Linux/Android——input子系統核心

來源:互聯網
上載者:User

標籤:input   input_register   input_handler   dev   list   

   之前的部落格有涉及到linux的input子系統,這裡學習記錄一下input模組.

input子系統,作為管理輸入裝置與系統進行互動的中樞,任何的輸入裝置驅動都要通過input向核心註冊其裝置,

常用的輸入裝置也就是滑鼠,鍵盤,觸控螢幕。

稍微細分一點整個輸入體系,就是 硬體驅動層input核心中轉層事件處理層.層次之間傳遞都以event事件的形式,這其中input串連上下層,分別提供介面.

之前有分析usbtouchscreen的驅動,也就是硬體驅動部分,這裡簡單記錄一下input核心中轉處理 input.c .


                                              撰寫不易,轉載需註明出處:http://blog.csdn.net/jscese/article/details/42123673

input_init:

 源碼位於/kernel/drivers/input/input.c ,模組初始調用口subsys_initcall(input_init),

由kernel啟動的時候由kernel_init——>do_basic_setup();——>do_initcalls調用到,這個啟動邏輯,後續有機會去學習一下,

這裡首先調用到初始函數:

static int __init input_init(void){    int err;    err = class_register(&input_class); //註冊input class,可在/sys/class下看到對應節點檔案    if (err) {        pr_err("unable to register input_dev class\n");        return err;    }    err = input_proc_init(); //proc fs的下的一些初始操作,函數原型在input.c,可查看/proc/bus/input    if (err)        goto fail1;    err = register_chrdev(INPUT_MAJOR, "input", &input_fops); // 註冊input字元裝置,主節點為INPUT_MAJOR==13,可以去input_fops裡看註冊函數,註冊到/dev/input    if (err) {        pr_err("unable to register char major %d", INPUT_MAJOR);        goto fail2;    }    return 0; fail2:    input_proc_exit(); fail1:    class_unregister(&input_class);    return err;}

這就是最開始的初始化過程了.


可以看下註冊方法函數:

static const struct file_operations input_fops = {    .owner = THIS_MODULE,    .open = input_open_file,    .llseek = noop_llseek,};

這裡面關注open file方法即可,後面分析。


input.c中還有很多其它的介面以及全域資料,後面陸續聯通,先從裝置驅動最先調用到的註冊 input_register_device


input_register_device:
/** * input_register_device - register device with input core * @dev: device to be registered * * This function registers device with input core. The device must be * allocated with input_allocate_device() and all it's capabilities * set up before registering. * If function fails the device must be freed with input_free_device(). * Once device has been successfully registered it can be unregistered * with input_unregister_device(); input_free_device() should not be * called in this case. */int input_register_device(struct input_dev *dev){    static atomic_t input_no = ATOMIC_INIT(0);          //這個原子變數,代表總共註冊的input裝置,每註冊一個加1,因為是靜態變數,所以每次調用都不會清零的    struct input_handler *handler;    const char *path;    int error;    __set_bit(EV_SYN, dev->evbit);  //EN_SYN 這個是裝置都要支援的事件類型,所以要設定    /*     * If delay and period are pre-set by the driver, then autorepeating     * is handled by the driver itself and we don't do it in input.c.     */        // 這個核心定時器是為了重複按鍵而設定的    init_timer(&dev->timer);    if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {        dev->timer.data = (long) dev;        dev->timer.function = input_repeat_key;        dev->rep[REP_DELAY] = 250;        dev->rep[REP_PERIOD] = 33;        //如果沒有定義有關重複按鍵的相關值,就用核心預設的    }    if (!dev->getkeycode)        dev->getkeycode = input_default_getkeycode;    if (!dev->setkeycode)        dev->setkeycode = input_default_setkeycode;        //以上設定的預設函數由input核心提供    dev_set_name(&dev->dev, "input%ld",             (unsigned long) atomic_inc_return(&input_no) - 1);        //設定input_dev中device的名字,這個名字會在/class/input中出現    error = device_add(&dev->dev);        //將device加入到linux裝置模型中去    if (error)        return error;    path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);    printk(KERN_INFO "input: %s as %s\n",        dev->name ? dev->name : "Unspecified device", path ? path : "N/A");    kfree(path);        //這個得到路徑名稱,並列印出來    error = mutex_lock_interruptible(&input_mutex);    if (error) {        device_del(&dev->dev);        return error;    }    list_add_tail(&dev->node, &input_dev_list);        // 將新分配的input裝置串連到input_dev_list鏈表上    list_for_each_entry(handler, &input_handler_list, node)        input_attach_handler(dev, handler);        //遍曆input_handler_list鏈表,配對 input_dev 和 input_handler        //input_attach_handler 這個函數是配對的關鍵,下面將詳細分析    input_wakeup_procfs_readers();        // 和proc檔案系統有關,暫時不考慮    mutex_unlock(&input_mutex);    return 0;   }


可以看到前面都是一些初始設定,加入到input.c 的全域input_dev 鏈表裡面,同時下面就行匹配對應handler的時候需要遍曆 handler 鏈表:

static LIST_HEAD(input_dev_list);static LIST_HEAD(input_handler_list);

可以看到用到了一個 list_for_each_entry, 剛開始看到還沒看懂,這是一個宏定義,原型是在/kernel/include/linux/list.h:

/** * list_for_each_entry    -    iterate over list of given type * @pos:    the type * to use as a loop cursor. * @head:    the head for your list. * @member:    the name of the list_struct within the struct. */#define list_for_each_entry(pos, head, member)                    for (pos = list_entry((head)->next, typeof(*pos), member);             &pos->member != (head);     \    //就是個for迴圈,跳出條件自然就是 遍曆找到相同的         pos = list_entry(pos->member.next, typeof(*pos), member))


input_attach_handler(dev, handler)則是匹配這個要註冊dev的handler:

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); //返回匹配的id,類型是struct input_device_id    if (!id)        return -ENODEV;    error = handler->connect(handler, dev, id); //<span><span class="comment">//配對成功調用handler的connect函數,這個函數在事件處理器中定義,主要產生一個input_handle結構,並初始化,還產生一個事件處理器相關的裝置結構</span></span>    if (error && error != -ENODEV)        pr_err("failed to attach handler %s to device %s, error: %d\n",               handler->name, kobject_name(&dev->dev.kobj), error);    return error;}


可以看下匹配 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 mscbit[INPUT_DEVICE_ID_MSC_MAX / BITS_PER_LONG + 1];kernel_ulong_t ledbit[INPUT_DEVICE_ID_LED_MAX / BITS_PER_LONG + 1];kernel_ulong_t sndbit[INPUT_DEVICE_ID_SND_MAX / BITS_PER_LONG + 1];kernel_ulong_t ffbit[INPUT_DEVICE_ID_FF_MAX / BITS_PER_LONG + 1];kernel_ulong_t swbit[INPUT_DEVICE_ID_SW_MAX / BITS_PER_LONG + 1];kernel_ulong_t driver_info;};


有兩個函數input_match_device 以及 下面的 connect需要瞭解:


input_match_device:

static const struct input_device_id *input_match_device(struct input_handler *handler,                            struct input_dev *dev){    const struct input_device_id *id;    int i;    for (id = handler->id_table; id->flags || id->driver_info; id++) {        if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)  //匹配匯流排id            if (id->bustype != dev->id.bustype)                continue;        if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)  //匹配生產商id            if (id->vendor != dev->id.vendor)                continue;        if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT) //匹配產品id            if (id->product != dev->id.product)                continue;        if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION) //匹配版本            if (id->version != dev->id.version)                continue;        MATCH_BIT(evbit,  EV_MAX);  //匹配id的evbit和input_dev中evbit的各個位,如果不匹配則continue,數組中下一個裝置        MATCH_BIT(keybit, KEY_MAX);        MATCH_BIT(relbit, REL_MAX);        MATCH_BIT(absbit, ABS_MAX);        MATCH_BIT(mscbit, MSC_MAX);        MATCH_BIT(ledbit, LED_MAX);        MATCH_BIT(sndbit, SND_MAX);        MATCH_BIT(ffbit,  FF_MAX);        MATCH_BIT(swbit,  SW_MAX);        if (!handler->match || handler->match(handler, dev))            return id;    }    return NULL;}

MATCH_bit 原型:

#define MATCH_BIT(bit, max) for (i = 0; i < BITS_TO_LONGS(max); i++) if ((id->bit[i] & dev->bit[i]) != id->bit[i]) break; if (i != BITS_TO_LONGS(max)) continue;

可以看到這麼多步的目的除了初始以及添加input_dev到鏈表,就是為了去匹配 input_handler_list 中對應的handler

匹配的最終是需要比對handler以及input_dev中的 id,其中input_dev 中的id類型為 input_id

struct input_id {__u16 bustype;__u16 vendor;__u16 product;__u16 version;};

這跟上面 input_handler 結構裡面的 input_device_id 匹配id 變數,來確認 handler!

在最開始的時候就有提到,整個input輸入體系,分三個層次,現在的input核心層做的事就是:

在硬體驅動層調用 input_register_device時 ,往核心註冊驅動的同時,根據硬體的相關id去匹配 適用的事件處理層(input_handler)!

這裡匹配上之後就會調用對應 input_handler 的connect 函數。


input_handler:

input_dev 變數代表的是硬體裝置,前文Linux/Android——輸入子系統input_event傳遞中有介紹

input_handler 變數代表的是事件處理器

同樣在input.h 中定義:

/** * struct input_handler - implements one of interfaces for input devices * @private: driver-specific data * @event: event handler. This method is being called by input core with *interrupts disabled and dev->event_lock spinlock held and so *it may not sleep * @filter: similar to @event; separates normal event handlers from *"filters". * @match: called after comparing device's id with handler's id_table *to perform fine-grained matching between device and handler * @connect: called when attaching a handler to an input device * @disconnect: disconnects a handler from input device * @start: starts handler for given handle. This function is called by *input core right after connect() method and also when a process *that "grabbed" a device releases it * @fops: file operations this driver implements * @minor: beginning of range of 32 minors for devices this driver *can provide * @name: name of the handler, to be shown in /proc/bus/input/handlers * @id_table: pointer to a table of input_device_ids this driver can *handle * @h_list: list of input handles associated with the handler * @node: for placing the driver onto input_handler_list * * Input handlers attach to input devices and create input handles. There * are likely several handlers attached to any given input device at the * same time. All of them will get their copy of input event generated by * the device. * * The very same structure is used to implement input filters. Input core * allows filters to run first and will not pass event to regular handlers * if any of the filters indicate that the event should be filtered (by * returning %true from their filter() method). * * Note that input core serializes calls to connect() and disconnect() * methods. */struct input_handler {void *private;void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);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 struct file_operations *fops;int minor;const char *name;const struct input_device_id *id_table; //這個就是上面說到的 會跟input_dev中的input_id 比對 id項的struct list_headh_list;struct list_headnode;};

這個結構詳細的含義,注釋有。

這個結構裡面暫時只需要理解的:

註冊input_dev ,在事件處理資料鏈表裡面匹配上 input_handler ,就會調用其 *connect 函數指標 進行串連,

將input_dev 跟 input_handler 進行綁定, 後續的運作事件的handler處理將會走這個input_handler的 *event !

在上篇input_event 傳遞中最後調用到event階段.


這裡簡單記錄到這裡,下篇介紹input_handler 的處理機制~




Linux/Android——input子系統核心

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.