Linux input系統數據上報流程

作爲雞生蛋系列文章,這裏主要關注Linux input系統,
主要爲觸摸事件上報流程.

讀該文章最好有對linux驅動的入門知識.
其實當你自己去分析了input系統後,再分析別的就相對很輕鬆了,
linux裏好多套路都差不多的.

本文例子以ft6236.c驅動爲例, 當然你也可以用goodix或者別的觸摸來分析.
但是分析基於的內核版本用4.19.6(我寫這篇文檔時最新穩定版)
(https://git.kernel.org/pub/sc...
文檔可參看
<<linux-4.19.6>>/Documentation/input/input.rst
<<linux-4.19.6>>/Documentation/input/input-programming.rst

觸屏設備驅動

eg:
(https://source.codeaurora.org...

static irqreturn_t ft6236_interrupt(int irq, void *dev_id)
{
......//5. 中斷處理中讀數據
    error = ft6236_read(ft6236->client, 0, sizeof(buf), &buf);
......
    for (i = 0; i < touches; i++) {
        struct ft6236_touchpoint *point = &buf.points[i];
        u16 x = ((point->xhi & 0xf) << 8) | buf.points[i].xlo;
        u16 y = ((point->yhi & 0xf) << 8) | buf.points[i].ylo;
......
        input_mt_slot(input, id);
        input_mt_report_slot_state(input, MT_TOOL_FINGER, act);
......//5. 上報數據, ABS即座標的絕對值
            input_report_abs(input, ABS_MT_POSITION_X, x);
            input_report_abs(input, ABS_MT_POSITION_Y, y);
......
    input_mt_sync_frame(input);
    input_sync(input);
......
}

//2. probe函數, 當設備與驅動匹配上時會執行該函數
static int ft6236_probe(struct i2c_client *client,
            const struct i2c_device_id *id)
{
......// 3. input設備申請
    input = devm_input_allocate_device(dev);
......
    ft6236->input = input;
    input->name = client->name;
    input->id.bustype = BUS_I2C;
......// 3. input設備參數/能力申明
        input_set_abs_params(input, ABS_MT_POSITION_X, 0,
                     ft6236->max_x, fuzz_x, 0);
        input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
                     ft6236->max_y, fuzz_y, 0);
......
    error = input_mt_init_slots(input, FT6236_MAX_TOUCH_POINTS,
                    INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
...... 5. 中斷來時回調到ft6236_interrupt
    error = devm_request_threaded_irq(dev, client->irq, NULL,
                      ft6236_interrupt, IRQF_ONESHOT,
                      client->name, ft6236);
......4. 註冊爲Input類設備
    error = input_register_device(input);
......
}
  //of table和id table在設備和驅動匹配時會用到
#ifdef CONFIG_OF
static const struct of_device_id ft6236_of_match[] = {
.....
MODULE_DEVICE_TABLE(of, ft6236_of_match);
#endif

static const struct i2c_device_id ft6236_id[] = {
.....
MODULE_DEVICE_TABLE(i2c, ft6236_id);

static struct i2c_driver ft6236_driver = {
    .driver = {
        .name = "ft6236",
        .of_match_table = of_match_ptr(ft6236_of_match),
    },
    .probe = ft6236_probe,
    .id_table = ft6236_id,
};
//1. 模塊init, 這是一個宏定義, 裏面包含了, module_init, i2c的添加驅動註冊,
//module_init可以理解爲對該文件的加載順序,其它的還有core_initcall late_initcall等
module_i2c_driver(ft6236_driver);

簡單說明下實現一個觸屏驅動包含以下內容

  1. 文件和模塊init
  2. 按照linux設備模型填充i2c驅動(設備一般在dts裏配置,這裏不提)
  3. 設備和驅動匹配上後,執行驅動的probe()函數, probe()裏申請input device, 能力填充, 再在裏面將設備註冊爲input類
  4. 當點擊屏後,中斷來了,回調中斷處理函數
  5. 中斷處理裏, 通過i2c的方法從硬件讀取數據,並進行上報.

注意, 觸屏上報有個多點觸摸協議,可參看文檔
<<linux-4.19.6>>/Documentation/input/multi-touch-protocol.rst

上報--input_report_abs()

我們的重點是想知道數據上報流程, 所以自然要分析input_report_abs()

include/linux/input.h
static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)
{
    input_event(dev, EV_ABS, code, value);
}

可以看到其爲內聯函數, 爲input_event(,EV_ABS, ...)的二次封裝;

input_report_key()       -+                  +- EV_KEY
input_report_rel()       -|                  |- EV_REL
input_report_abs()       -|                  |- EV_ABS
input_report_ff_status() -|--input_event() --|- EV_FF_STATUS
input_report_switch()    -|                  |- EV_SW
input_sync()             -|                  |- EV_SYN, SYN_REPORT
input_mt_sync()          -+                  +- EV_SYN, SYN_MT_REPORT

對於我們的根據來說,即
input_event(dev, EV_ABS, ABS_MT_POSITION_X, 座標值)

drivers/input/input.c
void input_event(struct input_dev *dev,
         unsigned int type, unsigned int code, int value)
{
....//event是否支持, 這個和驅動裏probe()時填充能力,設置參數有關,略過
    if (is_event_supported(type, dev->evbit, EV_MAX)) {
....
        input_handle_event(dev, type, code, value);
...
}

static void input_handle_event(struct input_dev *dev,
                   unsigned int type, unsigned int code, int value)
{
    int disposition = input_get_disposition(dev, type, code, &value); //得到disposition
......
    if (disposition & INPUT_FLUSH) {
        if (dev->num_vals >= 2)
            input_pass_values(dev, dev->vals, dev->num_vals);
        dev->num_vals = 0;
    } else if (dev->num_vals >= dev->max_vals - 2) {
        dev->vals[dev->num_vals++] = input_value_sync;
        input_pass_values(dev, dev->vals, dev->num_vals);  //**<--> 重點,
        dev->num_vals = 0;
    }

}

還記得在驅動中斷回調函數ft6236_interrupt()裏,上報值時,我們調用了這些函數,

            input_report_abs(input, ABS_MT_POSITION_X, x);
            input_report_abs(input, ABS_MT_POSITION_Y, y);
......
    input_mt_sync_frame(input);
    input_sync(input);

這些值到input_event()對應着

input_report_abs()       -|                  |- EV_ABS
input_sync()             -|--input_event() --|- EV_SYN, SYN_REPORT
input_mt_sync()          -+                  +- EV_SYN, SYN_MT_REPORT

所以我們可以簡單看下input_handle_event() --> input_get_disposition()
EV_SYN事件和EV_ABS的返回值

static int input_get_disposition(struct input_dev *dev,
              unsigned int type, unsigned int code, int *pval)
{
    int disposition = INPUT_IGNORE_EVENT;
......
    switch (type) {

    case EV_SYN:
        switch (code) {
        case SYN_CONFIG:
            disposition = INPUT_PASS_TO_ALL;
            break;

        case SYN_REPORT:
            disposition = INPUT_PASS_TO_HANDLERS | INPUT_FLUSH;
            break;
        case SYN_MT_REPORT:
            disposition = INPUT_PASS_TO_HANDLERS;
            break;
        }
        break;
......
    case EV_ABS:
        if (is_event_supported(code, dev->absbit, ABS_MAX))
            disposition = input_handle_abs_event(dev, code, &value);//這個可以看看,他會對相同值進行過濾,返回INPUT_IGNORE_EVENT

        break;
......
    return disposition;
}

讓我們回到input_handle_event() --> input_pass_values()

static void input_pass_values(struct input_dev *dev,
                  struct input_value *vals, unsigned int count)
{
......
    if (handle) {
        count = input_to_handler(handle, vals, count);
    } else {
        list_for_each_entry_rcu(handle, &dev->h_list, d_node)
            if (handle->open) {
                count = input_to_handler(handle, vals, count);
                if (!count)
                    break;
            }
    }
......
}

其重點函數爲input_to_handler()

static unsigned int input_to_handler(struct input_handle *handle,
            struct input_value *vals, unsigned int count)
{
    struct input_handler *handler = handle->handler;
......
    if (handler->filter) {
        for (v = vals; v != vals + count; v++) {
            if (handler->filter(handle, v->type, v->code, v->value))
                continue;
.......
    }
......
    if (handler->events)
        handler->events(handle, vals, count); //<--handler的events.
    else if (handler->event)
        for (v = vals; v != vals + count; v++)
            handler->event(handle, v->type, v->code, v->value);

    return count;
}

分析到這個函數的時候, 似乎有些斷了,
我們看到有三個handler->filter(), handler->events(), handler->event()函數調用,
哪這三個函數又調用到哪兒去了呢?這時又該如何繼續分析呢?

handler (input_register_device() --> handler)

對此,

  1. 我們可以搜索下哪兒在給這三個函數賦值,但情況不太樂觀;
  2. 我們回想下在驅動probe裏,我們與input相關的有如下,
static int ft6236_probe(struct i2c_client *client,
            const struct i2c_device_id *id)
{
......// 3. input設備申請
    input = devm_input_allocate_device(dev);
......// 3. input設備參數/能力申明
        input_set_abs_params(input, ABS_MT_POSITION_X, 0,
                     ft6236->max_x, fuzz_x, 0);
        input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
                     ft6236->max_y, fuzz_y, 0);
......
    error = input_mt_init_slots(input, FT6236_MAX_TOUCH_POINTS,
                    INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
......4. 註冊爲Input類設備
    error = input_register_device(input);

所以有很大概率是在申請設備, 設備能力, slots設置,註冊input類這幾個函數裏面實現的.

我們這裏就直接看答案

int input_register_device(struct input_dev *dev)
{
......//前面有些默認能力參數等的設置,略過
    error = device_add(&dev->dev);
......//將設備節點加入到input_dev_list
    list_add_tail(&dev->node, &input_dev_list);
    //遍歷input_handler_list, 然後調用input_attach_handler,看匹配的handler
    list_for_each_entry(handler, &input_handler_list, node)
        input_attach_handler(dev, handler);
.......
}

input_dev_list 和 input_handler_list, 是定義的兩個list,
static LIST_HEAD(input_dev_list);
static LIST_HEAD(input_handler_list);

我們可以猜測,所有的input dev和handler都會掛在這兩個list裏,
然後調用上面的input_attach_handler()進行兩者的匹配,
對於dev list我們不關注,有興趣的同學可自己看下,
重點想要知道的是handler相關的,
那我們的問題自然又轉爲
哪些會掛到input_handler_list上?
搞明白這個問題,然後進一步的分析input_attach_handler()匹配.

通過對drivers/input/input.c搜索, 覺得input_register_handler()這個的可能性最大,
因爲list嘛,肯定有對他進行add的地方, 別的地方代碼都沒有add

int input_register_handler(struct input_handler *handler)
{
......//初始化h_list
    INIT_LIST_HEAD(&handler->h_list);
    //將node加到list尾部
    list_add_tail(&handler->node, &input_handler_list);
    //在註冊handler的時候也對已有設備調用一次attach()
    list_for_each_entry(dev, &input_dev_list, node)
        input_attach_handler(dev, handler);
......
}

先看下input_handler定義,裏面就有我們想找的event() filter()函數

include/linux/input.h
struct input_handler {

    void *private;

    void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
    void (*events)(struct input_handle *handle,
               const struct input_value *vals, unsigned int count);
    bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
    bool (*match)(struct input_handler *handler, struct input_dev *dev);
    int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
    void (*disconnect)(struct input_handle *handle);
    void (*start)(struct input_handle *handle);
......
    const char *name;

    const struct input_device_id *id_table;

    struct list_head    h_list;
    struct list_head    node;
};

然後再進一步,我們就想要知道誰在調用input_register_handler()註冊handler了.
通過搜索代碼,我這裏列舉下

File handler名 在哪個函數裏註冊的
drivers/input/apm-power.c apmpower_handler apmpower_init()
drivers/input/evbug.c evbug_handler evbug_init()
drivers/input/input-leds.c input_leds_handler input_leds_init()
drivers/input/joydev.c joydev_handler joydev_init()
drivers/input/mousedev.c mousedev_handler mousedev_init()
drivers/input/evdev.c evdev_handler evdev_init()
drivers/tty/serial/ kgdboc.c kgdboc_reset_handler kgdboc_restore_input_helper()
drivers/macintosh/mac_hid.c mac_hid_emumouse_handler mac_hid_start_emulation()
net/rfkill/input.c rfkill_handler rfkill_handler_init()
drivers/tty/sysrq.c sysrq_handler sysrq_register_handler()
drivers/tty/vt/keyboard.c kbd_handler kbd_init()

由上我們知道,在各個模塊的init裏,註冊了所支持的handler,
用來處理幾類常見的事件,如鼠標、鍵盤、搖桿等(其中最爲基礎的是evdev_handler,
它能夠接收任意類型的事件,任意id的設備都可以和它匹配連接)
也就是說,最終的handler的調用函數是上面的handler中的一個。

哪我們的觸屏究竟用的哪一個handler呢?這就得接下來看attach裏的匹配過程了

input_attach_handler() --> input_match_device() --> input_match_device_id()
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
    const struct input_device_id *id;
    int error;

    id = input_match_device(handler, dev); //-->匹配
    if (!id)
        return -ENODEV;

    error = handler->connect(handler, dev, id); //-->連接
......
    return error;
}

bool input_match_device_id(const struct input_dev *dev,
               const struct input_device_id *id)
{
    if (id->flags & INPUT_DEVICE_ID_MATCH_BUS) //Bus總線的匹配
......
    if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR) //Vendor匹配
......
    if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT) //Product匹配
......
    if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
......
    if (!bitmap_subset(id->evbit, dev->evbit, EV_MAX) || //匹配id的evbit和input_dev中evbit的各個位
        !bitmap_subset(id->keybit, dev->keybit, KEY_MAX) ||
.....) {
        return false;
    }

    return true;
}

static const struct input_device_id *input_match_device(struct input_handler *handler,
                            struct input_dev *dev)
{
    const struct input_device_id *id;

    for (id = handler->id_table; id->flags || id->driver_info; id++) {
        //進行id的一個匹配, 如果有match爲空或者match成功, 返回id
        if (input_match_device_id(dev, id) &&
            (!handler->match || handler->match(handler, dev))) {
            return id;
        }
    }

    return NULL;
}

device id的定義如下,

struct input_device_id {

    kernel_ulong_t flags;

    __u16 bustype;
    __u16 vendor;
    __u16 product;
    __u16 version;

    kernel_ulong_t evbit[INPUT_DEVICE_ID_EV_MAX / BITS_PER_LONG + 1];
    kernel_ulong_t keybit[INPUT_DEVICE_ID_KEY_MAX / BITS_PER_LONG + 1];
    kernel_ulong_t relbit[INPUT_DEVICE_ID_REL_MAX / BITS_PER_LONG + 1];
    kernel_ulong_t absbit[INPUT_DEVICE_ID_ABS_MAX / BITS_PER_LONG + 1];
......
    kernel_ulong_t driver_info;
};

基實整個匹配也就是進行,總線,廠商,能力(evbit, keybit), id_table的匹配,
我們的觸屏也是匹配到的evdev_handler,
我們可以再看一下evdev_handler的定義

drivers/input/evdev.c
static const struct input_device_id evdev_ids[] = {
    { .driver_info = 1 },    /* Matches all devices */
    { },            /* Terminating zero entry */
};

MODULE_DEVICE_TABLE(input, evdev_ids);

static struct input_handler evdev_handler = {
    .event        = evdev_event,
    .events        = evdev_events,
    .connect    = evdev_connect,
......
    .minor        = EVDEV_MINOR_BASE,
    .name        = "evdev",
    .id_table    = evdev_ids,   //<--id_table
};

回到input_register_handler() --> input_attach_handler() --> handler->connect()
我們以handler drivers/input/evdev.c爲例分析
其connect()裏做的事情

  • 主要爲name,dev,handle,等信息填充,
  • 註冊handle, 將device和handler連接起來,
  • 字符設備添加
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
             const struct input_device_id *id)
{
......//從這個定義我們可知,input的從設備號從64開始,可爲32個, 所以從設備號爲64~95
    minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);
......
    evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
......
    INIT_LIST_HEAD(&evdev->client_list);
......
    dev_no = minor;
......
    dev_set_name(&evdev->dev, "event%d", dev_no); //<--名字爲eventN

    evdev->handle.dev = input_get_device(dev); //<--handle.dev
    evdev->handle.name = dev_name(&evdev->dev);
    evdev->handle.handler = handler; //<--注意一個是handle,一個是handler
    evdev->handle.private = evdev;

    evdev->dev.devt = MKDEV(INPUT_MAJOR, minor); <--//主設備號INPUT_MAJOR爲13,include/uapi/linux/major.h
    evdev->dev.class = &input_class; //<--- 類別爲input_class, 即/sys/class/input/
    evdev->dev.parent = &dev->dev;
    evdev->dev.release = evdev_free;
    device_initialize(&evdev->dev);

    error = input_register_handle(&evdev->handle); //註冊handle, 注意我們之前分析的是handler,表示鍵盤,搖桿等的可處理.
......
    cdev_init(&evdev->cdev, &evdev_fops); //<--注意把 file_operations 和cdev->ops關聯起來了.

    error = cdev_device_add(&evdev->cdev, &evdev->dev); //<--cdev添加, 這個時候就可以在/dev/input/看到了
......
}

input_register_handle()所做的就是將handle句柄掛到dev和handler的list裏,
當有事件來時就知道咋處理,至此也表示一個handle和dev匹配成功.

/**
.....//可以看看這個註釋
 * This function puts a new input handle onto device's
 * and handler's lists so that events can flow through
 * it once it is opened using input_open_device().
......
 */
int input_register_handle(struct input_handle *handle)
{
......
    if (handler->filter)
        list_add_rcu(&handle->d_node, &dev->h_list);
    else
        list_add_tail_rcu(&handle->d_node, &dev->h_list);
......
    list_add_tail_rcu(&handle->h_node, &handler->h_list);
......
}

report和handler小結

所以到目前爲至,我們知道了

當各個handler init時 --> input_register_handler() --> input_attach_handler() -->   handler->connect()
或者驅動 --> probe() --> input_register_device() --> input_attach_handler --> handler->connect()

                                             +--> input_register_handle() dev和handler關聯
handler->connect()-->  eg:evdev.c events() --+
                                             +-->cdev_device_add() 註冊字符設備

對於input_report_abs()上報我這也列舉整個流程, 代碼不再詳細看了

input_report_abs() --> input_event(, EV_ABS, , ) --> input_handle_event() --> input_pass_values() --> input_to_handler() -->
handler->events()/event() --> eg:evdev.c events() --> evdev_pass_values() --> 數據填充 --> __pass_event() --> client->buffer[]
static void evdev_events(struct input_handle *handle,
             const struct input_value *vals, unsigned int count)
{
......
    if (client)
        evdev_pass_values(client, vals, count, ev_time);
    else
        list_for_each_entry_rcu(client, &evdev->client_list, node)
            evdev_pass_values(client, vals, count, ev_time);
......
}

static void evdev_pass_values(struct evdev_client *client,
            const struct input_value *vals, unsigned int count,
            ktime_t *ev_time)
{
    struct evdev *evdev = client->evdev;
    const struct input_value *v;
    struct input_event event;
    struct timespec64 ts;
......//時間
    event.input_event_sec = ts.tv_sec;
    event.input_event_usec = ts.tv_nsec / NSEC_PER_USEC;
......
    for (v = vals; v != vals + count; v++) {
......//事件數據填充
        event.type = v->type;
        event.code = v->code;
        event.value = v->value;
        __pass_event(client, &event); //<--放到client->buffer裏
    }
......
}

__pass_event()將event放到client->buffer[]裏
static void __pass_event(struct evdev_client *client,
             const struct input_event *event)
{
    client->buffer[client->head++] = *event;
    client->head &= client->bufsize - 1;

    if (unlikely(client->head == client->tail)) {
        /*
         * This effectively "drops" all unconsumed events, leaving
         * EV_SYN/SYN_DROPPED plus the newest event in the queue.
         */
        client->tail = (client->head - 2) & (client->bufsize - 1);

        client->buffer[client->tail].input_event_sec =
                        event->input_event_sec;
        client->buffer[client->tail].input_event_usec =
                        event->input_event_usec;
        client->buffer[client->tail].type = EV_SYN;
        client->buffer[client->tail].code = SYN_DROPPED;
        client->buffer[client->tail].value = 0;

        client->packet_head = client->tail;
    }

    if (event->type == EV_SYN && event->code == SYN_REPORT) {
        client->packet_head = client->head;
        kill_fasync(&client->fasync, SIGIO, POLL_IN);
    }
}

數據是如何讀取的?

我們從上面分析,看到數據已經放到了client->buffer[], 那讀取也肯定也是從這裏讀,
具體分析就不講了,我這裏只列下
還記得evdev_connect()時將file_operations和dev關聯起來了

cdev_init(&evdev->cdev, &evdev_fops);

evdev的file_operations定義如下:

static const struct file_operations evdev_fops = {
    .owner        = THIS_MODULE,
    .read        = evdev_read,
    .write        = evdev_write,
    .poll        = evdev_poll,
    .open        = evdev_open,
    .release    = evdev_release,
    .unlocked_ioctl    = evdev_ioctl,
#ifdef CONFIG_COMPAT
    .compat_ioctl    = evdev_ioctl_compat,
......
}

(evdev_open分析略過)

所以我們很容易想到讀數據其實就是調用evdev_read(),

static ssize_t evdev_read(struct file *file, char __user *buffer,
              size_t count, loff_t *ppos)
{
    struct evdev_client *client = file->private_data;
    struct evdev *evdev = client->evdev;
......
    for (;;) {
......//循環讀取下一個事件, 並通過input_event_to_user() --> copy_to_user()給用戶空間, 這樣上面就讀到數據了.
        while (read + input_event_size() <= count &&
               evdev_fetch_next_event(client, &event)) {

            if (input_event_to_user(buffer + read, &event))
......
    return read;
}

static int evdev_fetch_next_event(struct evdev_client *client,
                  struct input_event *event)
{
    int have_event;

    spin_lock_irq(&client->buffer_lock);

    have_event = client->packet_head != client->tail;
    if (have_event) {
        *event = client->buffer[client->tail++];
        client->tail &= client->bufsize - 1;
    }

    spin_unlock_irq(&client->buffer_lock);

    return have_event;
}

read數據小結

read時候 evdev_read--> 從client->buffer[]循環獲取事件 evdev_fetch_next_event() --> input_event_to_user() --> copy_to_user()

涉及到的一些數據結構

struct evdev {
    int open;
    struct input_handle handle; -->
    wait_queue_head_t wait;
    struct evdev_client __rcu *grab;
    struct list_head client_list;
    spinlock_t client_lock; /* protects client_list */
    struct mutex mutex;
    struct device dev;
    struct cdev cdev;
    bool exist;
};

struct evdev_client {
    unsigned int head;
    unsigned int tail;
......
    struct evdev *evdev;
    struct list_head node;
    unsigned int clk_type;
    bool revoked;
    unsigned long *evmasks[EV_CNT];
    unsigned int bufsize;
    struct input_event buffer[];
};

struct input_handler {

    void *private;

    void (*event)(....);
    void (*events)(....);
......
    int (*connect)(......);
    void (*disconnect)(struct input_handle *handle);
    void (*start)(struct input_handle *handle);
......
    int minor;
    const char *name;

    const struct input_device_id *id_table;

    struct list_head    h_list;
    struct list_head    node; --> input_handler_list
};

struct input_handle {
......
    int open;
    const char *name;

    struct input_dev *dev;
    struct input_handler *handler;

    struct list_head    d_node;
    struct list_head    h_node;
};

相關數據結構

他們可簡單用如下圖表示, 即有兩個列表, input_handler_list和input_dev_list
分別是所有可用的handler和input dev,
他們之間靠input_handle連在一起.

input_handler_list[hander1|hander2|...]           input_dev_list[dev1|dev2|...]
                    ^         ^                                   ^     ^
                    |         |                                   |     |
                    |         |                                   |     |
      [handle1{handler|dev}]--| ----------------------------------+     |
                 [handle2{handler|dev}]---------------------------------+
                    [handle..{handler|dev}]略...

調試相關

對於android可用命令

sendevent/getevent

發送或獲取event事件

也可查看一些節點獲得信息

/proc/bus/input/
/sys/class/input/
/dev/input/

總結

所以總的來說, 內容有如下

  1. 按照linux設備架構,驅動模型實現driver,
  2. 當各個handler init或者驅動註冊input device時,會進行handler的匹配,
    匹配成功後調用handler的connect()通過handle進行device handler的關聯,
    並註冊字符設備

    當各個handler init時 --> input_register_handler() --> input_attach_handler() -->  handler->connect()
    或者驅動 --> probe() --> input_register_device() --> input_attach_handler -->  handler->connect()
    
                                                 +--> input_register_handle() dev和handler關聯
    handler->connect()-->  eg:evdev.c events() --+
                                                 +-->cdev_device_add() 註冊字符設備
  3. 當點擊觸屏後, 進到中斷處理,然後讀取數據,再report,並存到client的buffer[]裏

    input_report_abs() --> input_event(, EV_ABS, , ) --> input_handle_event() --> input_pass_values() --> input_to_handler() -->
    handler->events()/event() --> eg:evdev.c events() --> evdev_pass_values() --> 數據填充 --> __pass_event() --> client->buffer[]
  4. 上層用戶空調read時(open我們略過了), 只要有數據,不斷從client->buffer[]讀取並通過copy_to_user()拷到用戶空間, 所以上層就拿到數據了.

    read時候...--> evdev_read--> 從client->buffer[]循環獲取事件 evdev_fetch_next_event() --> 
    input_event_to_user() --> copy_to_user()
  5. 大體流向

    userpace open()/read() /dev/input/event*
    ----------------------------------------
    kernel
               ↑
       input handler evdev.c
               ↑
       input core input.c
               ↑
       device driver
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章