input subsystem簡(jiǎn)要分析

Input Subsystem 系統(tǒng)主要由 drivers/input/input.c input_register_device() 和 input_register_handler() 兩個(gè)函數(shù)實(shí)現(xiàn)。
Handler 組件通過(guò) input_register_handler 函數(shù)將邏輯設(shè)備操作結(jié)構(gòu)體注冊(cè)到 input_handler_list 鏈表中;
device 組件通過(guò) input_register_device 組件將直接對(duì)具體硬件的操作結(jié)構(gòu)體注冊(cè)到 input_dev_list 鏈表中

struct input_dev {
    ...
    struct device dev;
    ...
    struct list_head    h_list; //與 input_dev關(guān)聯(lián)的 input_headle 的鏈表頭
    struct list_head    node;   //用于將本設(shè)備連接到 input_dev_list
};

struct input_handler {
    ...
    const struct file_operations *fops;
    ...
    const struct input_device_id *id_table;//用戶和設(shè)備匹配, 這個(gè)是事件處理器所支持的input設(shè)備
    ...
    struct list_head    h_list; //所支持的 input_headle 結(jié)構(gòu)的鏈表的表頭
    struct list_head    node;   //鏈入全局鏈表input_handler_list, 它連接了所有注冊(cè)到內(nèi)核的handler 
};

struct input_handle {
    ...
    struct input_dev *dev;              //關(guān)聯(lián)的input_dev結(jié)構(gòu)
    struct input_handler *handler;      //關(guān)聯(lián)的input_headler結(jié)構(gòu)

    struct list_head    d_node;         //input_headle通過(guò)d_node連接到了input_dev的h_list上
    struct list_head    h_node;         //input_headle通過(guò)h_node連接到了input_headler的h_list上
};

input_dev / input_headler / input_headle 關(guān)系:
input_dev 是硬件驅(qū)動(dòng)層,代表一個(gè)input設(shè)備
input_headler 是事件處理層,代表一個(gè)事件處理器
input_headle 個(gè)人認(rèn)為屬于核心層, 代表一個(gè)配對(duì)的設(shè)備與事件處理器
那么input_dev 和input_handler是如何跟input子系統(tǒng)總線聯(lián)系起來(lái)的呢?

(linux)/drivers/input/input.c

input_init()
    class_register(&input_class);                       //創(chuàng)建類
    register_chrdev(INPUT_MAJOR, "input", &input_fops); //創(chuàng)建類下的設(shè)備, 并由此可知   file_operations結(jié)構(gòu)體變量為input_fops

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

//該結(jié)構(gòu)體只填充了 .open 方法, read / write / poll 等方法沒(méi)有填充, 那么肯定是由open做了些"工作"來(lái)間接實(shí)現(xiàn)了其他的功能, 那么就讓我們來(lái)看看 input_open_file的內(nèi)部

static int input_open_file(struct inode *inode, struct file *file)
{
    struct input_handler *handler;
    ...
    //以次設(shè)備號(hào)為下標(biāo),拿到一個(gè) input_handler, 在input_handler內(nèi)部有個(gè)file_operations成員
    handler = input_table[iminor(inode) >> 5];
    ...
    //將input_handler的file_operations成員拿出
    new_fops = fops_get(handler->fops);
    ...
    //將原來(lái)的fops保存起來(lái), 將新的fops給 file->f_op
    old_fops = file->f_op;
    file->f_op = new_fops;
    //從此file->f_op都是 新的fops
    err = new_fops->open(inode, file);
    ...
    return err;
}

以上程序的關(guān)鍵是 input_table[], 那么input_table是怎么來(lái)的呢?

insmod時(shí)候調(diào)用

//注冊(cè)input_handler

input_register_handler()
    input_table[handler->minor >> 5] = handler;     //那么是誰(shuí)調(diào)用了input_register_handler 了呢?
    //將handler加入 input_handler_list鏈表
    list_add_tail(&handler->node, &input_handler_list);
    //每個(gè) input_handler 都調(diào)用 input_attach_handler, 遍歷headler 查看是否支持dev
    list_for_each_entry(handler, &input_handler_list, node)
       //根據(jù)input_handler 的id_table 判斷能否支持該input_device
       input_attach_handler(dev, handler);          

插入設(shè)備調(diào)用

//注冊(cè)輸入設(shè)備

input_register_device()
    //將每個(gè) input_device 都放入鏈表
    list_add_tail(&dev->node, &input_dev_list);
    //每個(gè) input_handler 都調(diào)用input_attach_handler, 遍歷handler, 查看能否支持dev
    list_for_each_entry(handler, &input_handler_list, node)
       //根據(jù)input_handler 的id_table 判斷能夠支持該input_device
       input_attach_handler(dev, handler);          

以上兩個(gè)函數(shù)非常對(duì)稱, 不管先調(diào)用哪個(gè)函數(shù)都最后調(diào)用了 input_attach_handler, 那么input_attach_handler 做了什么工作呢?

static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
    id = input_match_device(handler->id_table, dev);//遍歷id_table, 看找有沒(méi)有與dev匹配的
    if (!id)
        return -ENODEV;

    error = handler->connect(handler, dev, id);

它先比較了handler層的 input_handler的id_table和device層的input_dev, 如果handler能夠支持input_dev 則調(diào)用handler的connect將device和handler"連接"起來(lái)

//那么他們是怎么建立"連接"的呢? 主要看 handler 里的 connect 函數(shù) (這里以evdev為例來(lái)說(shuō)明)

evdev_connect
1. 申請(qǐng)一個(gè)  handle 結(jié)構(gòu)體
2. 設(shè)置
    handle.dev = input_dev;
    handle.handler = input_handler;
3. input_dev->h_list = &handle;
    input_hanlder->h_list = &handle;

這樣一來(lái)
device層可以通過(guò) input_dev的h_list先找到handle 然后再通過(guò)handle的handler找到 input_handler
handler層可以通過(guò)input_handler的h_list先找到handle然后再通過(guò)handle的dev找到 input_dev
兩者就靠 handle連接了起來(lái)

從編寫代碼的過(guò)程再捋一遍思緒:
1. 申請(qǐng)input_dev結(jié)構(gòu)體 input_allocate_device

2. 設(shè)置 input_dev 
   1) 可以產(chǎn)生哪類事件
   2) 產(chǎn)生該類事件中的哪些事件

3. 將input_dev注冊(cè)到內(nèi)核

4. 實(shí)現(xiàn)業(yè)務(wù)邏輯代碼
    申請(qǐng)中斷等...

問(wèn)題:

  1. input_table 是什么?
  2. 應(yīng)用層如何獲取input_event上報(bào)的事件?
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 事件層 該函數(shù)返回一個(gè)指向 input_dev 類型的指針,該結(jié)構(gòu)體是一個(gè)輸入設(shè)備結(jié)構(gòu)體,包含了輸入設(shè)備的一些相關(guān)...
    季風(fēng)落地窗閱讀 741評(píng)論 0 3
  • 1:InputChannel提供函數(shù)創(chuàng)建底層的Pipe對(duì)象 2: 1)客戶端需要新建窗口 2)new ViewRo...
    自由人是工程師閱讀 5,717評(píng)論 0 18
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,644評(píng)論 19 139
  • make menuconfig過(guò)程解析作者 codercjg 在 28 九月 2015, 5:27 下午 make...
    codercjg閱讀 1,233評(píng)論 0 1
  • 心中有師,得師之光; 心中有月,得月之光; 心中有佛,得佛之光; 心中有道,得道之光。
    石竹閱讀 346評(píng)論 1 13

友情鏈接更多精彩內(nèi)容