Linux/Android——input_handler之evdev

來源:互聯網
上載者:User

標籤:input   handler   input_dev   handle   evdev   

    在前文Linux/Android——input子系統核心 中概括了總體的結構,以及介紹了input核心的職責,其中有說道註冊input裝置時會去匹配已有的事件處理器handler,

而這個handler也是存放在一個鏈表裡面的,這裡介紹下input子系統中的事件處理input_handler機制.


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


evdev:

  /kernel/drivers/input下眾多事件處理器handler其中的一個,可以看下源碼/kernel/drivers/input/evdev.c中的模組init:

static int __init evdev_init(void){return input_register_handler(&evdev_handler);}

這個初始化就是往input核心中註冊一個input_handler類型的evdev_handler,調用的是input.c提供的介面,input_handler結構前文有介紹,看下evdev_handler的賦值:

static struct input_handler evdev_handler = {.event= evdev_event,.connect= evdev_connect,.disconnect= evdev_disconnect,.fops= &evdev_fops,.minor= EVDEV_MINOR_BASE,.name= "evdev",.id_table= evdev_ids,};

賦值各個函數指標!



input_register_handler:

 可以看到上面的evdev handler 就是調用這個介面註冊到input核心中的,同樣evdev.c同目錄下也還有其它的handler,有興趣可以看看它們的init函數,都是會調用到這個介面去註冊的.

/** * input_register_handler - register a new input handler * @handler: handler to be registered * * This function registers a new input handler (interface) for input * devices in the system and attaches it to all input devices that * are compatible with the handler. */int input_register_handler(struct input_handler *handler){    struct input_dev *dev;    int retval;    retval = mutex_lock_interruptible(&input_mutex);    if (retval)        return retval;    INIT_LIST_HEAD(&handler->h_list);    if (handler->fops != NULL) {        if (input_table[handler->minor >> 5]) {            retval = -EBUSY;            goto out;        }        input_table[handler->minor >> 5] = handler; //給input.c定義的全域handler 數組賦值,evdev handler的次裝置號為64,這裡除以32,賦值在input_table[2]    }    list_add_tail(&handler->node, &input_handler_list);  //添加進handler 鏈表    list_for_each_entry(dev, &input_dev_list, node)   //同樣遍曆input_dev這個鏈表,依次調用下面的input_attach_handler去匹配input_dev,這個跟input_dev註冊的時候的情形類似        input_attach_handler(dev, handler);    input_wakeup_procfs_readers(); out:    mutex_unlock(&input_mutex);    return retval;}

input核心中儲存的handler數組:

static struct input_handler *input_table[8];

這是儲存註冊到input核心中的handler數組,因為在之前input註冊的時候註冊的字元裝置主裝置號為13.字元裝置的次裝置號為0~255,可以有256個裝置,

這裡後面會看到一個handler可以connect處理32個input裝置,所以input體系中,最多擁有8個handler


這個匹配過程和上一篇中的過程是一樣的,最後匹配上的話會調用匹配上的handler 中connect指標指向的函數.


另外可以注意的是evdev是匹配所有裝置的,因為:

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

如果沒有特定的handler添加進handler鏈表,那麼在匹配的時候,只要有這個evdev的handler,最後都會匹配到evdev,這個具體可以去看看上篇的匹配過程.

我這邊調試的是usb觸控螢幕,所以用的是evdev的handler,下面看下evdev的connect.


evdev_connect:

 註冊的evdev_handler中connect指向的函數為evdev_connect:

/* * Create new evdev device. Note that input core serializes calls * to connect and disconnect so we don't need to lock evdev_table here. */static int evdev_connect(struct input_handler *handler, struct input_dev *dev,             const struct input_device_id *id){    struct evdev *evdev;    int minor;    int error;    for (minor = 0; minor < EVDEV_MINORS; minor++)        if (!evdev_table[minor])            break;    if (minor == EVDEV_MINORS) {        pr_err("no more free evdev devices\n");        return -ENFILE;    }// 可以看到這裡evdev handler匹配串連好的裝置都以evdev 類型存在這個evdev_table數組的,這個數組大小為32個,這就是我上面說到的,為什麼只有8個handler//這裡是判斷evdev的32個位置中是否有空    evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); //為上面定義的*evdev分配記憶體空間    if (!evdev)        return -ENOMEM;    INIT_LIST_HEAD(&evdev->client_list); //以下都是對這個 evdev的初始化了    spin_lock_init(&evdev->client_lock);    mutex_init(&evdev->mutex);    init_waitqueue_head(&evdev->wait);    dev_set_name(&evdev->dev, "event%d", minor);  //給這個evdev命名    evdev->exist = true;    evdev->minor = minor;   // 以minor為索引賦值    evdev->handle.dev = input_get_device(dev);  //evdev中的handle變數的初始化 ,後面分析這個handle ,這裡面儲存的就是已經匹配成功的input_dev 和 handler    evdev->handle.name = dev_name(&evdev->dev);    evdev->handle.handler = handler;    evdev->handle.private = evdev;    evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);    evdev->dev.class = &input_class;    evdev->dev.parent = &dev->dev;    evdev->dev.release = evdev_free;    device_initialize(&evdev->dev);    error = input_register_handle(&evdev->handle); //把這個evdev中初始化好的handle 註冊到input核心中去,代表一個匹配成功的組合    if (error)        goto err_free_evdev;    error = evdev_install_chrdev(evdev);  //把這個初始化好的evdev添加到上面說到過的evdev_table數組,以minor索引序號    if (error)        goto err_unregister_handle;    error = device_add(&evdev->dev); //把這個device 添加到/sys/class/input/下面,所以我們可以看到/dev/input下面看到:event0~31 字樣字元裝置檔案,這就是在上面命名的    if (error)        goto err_cleanup_evdev;    return 0; err_cleanup_evdev:    evdev_cleanup(evdev); err_unregister_handle:    input_unregister_handle(&evdev->handle); err_free_evdev:    put_device(&evdev->dev);    return error;}


evdev:

這裡的evdev變數的結構如下:

struct evdev{int open; //開啟標誌int minor; //次裝置號struct input_handle handle; //包含的handlewait_queue_head_t wait; //等待隊列struct evdev_client __rcu *grab; //強制綁定的evdev_client結構struct list_head client_list; //evdev_client 鏈表,這說明一個evdev裝置可以處理多個evdev_client,可以有多個進程訪問evdev裝置spinlock_t client_lock; /* protects client_list */struct mutex mutex;struct device dev;bool exist;};


關於這個結構變數我的理解是抽象出來一個裝置,代表一個 input_dev與其匹配好的 handler的組合( handle),可以看作提供給事件處理層的一個封裝.


input_handle:

這個代表一個匹配成功的input dev和 handler組合,定義在input.h中,每個evdev中包含一個input_handle,並且註冊到input核心中:

/** * struct input_handle - links input device with an input handler * @private: handler-specific data * @open: counter showing whether the handle is 'open', i.e. should deliver *    events from its device * @name: name given to the handle by handler that created it * @dev: input device the handle is attached to * @handler: handler that works with the device through this handle * @d_node: used to put the handle on device's list of attached handles * @h_node: used to put the handle on handler's list of handles from which *    it gets events */struct input_handle {    void *private;  //指向上面封裝的evdev    int open;    const char *name;    struct input_dev *dev;   //input 裝置    struct input_handler *handler;  // 一個input的handler    struct list_head    d_node;  //鏈表結構    struct list_head    h_node;};

input_register_handle:

 看看這個handle的註冊,不要和handler搞混淆了,這不是一個概念~

/** * input_register_handle - register a new input handle * @handle: handle to register * * 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(). * * This function is supposed to be called from handler's * connect() method. */int input_register_handle(struct input_handle *handle){    struct input_handler *handler = handle->handler;    struct input_dev *dev = handle->dev;  //取出兩個成員...    /*     * Filters go to the head of the list, normal handlers     * to the tail.     */    if (handler->filter)        list_add_rcu(&handle->d_node, &dev->h_list);    else        list_add_tail_rcu(&handle->d_node, &dev->h_list);//把這個handle的d_node 加到對應input_dev的h_list鏈表裡面...    list_add_tail_rcu(&handle->h_node, &handler->h_list);//把這個handle的h_node 加到對應input_handler的h_list鏈表裡面...}

這個註冊是把handle 本身的鏈表加入到它自己的input_dev 以及 input_handler的h_list鏈表中,這樣以後就可以通過h_list遍曆到這個handle,

這樣就實現了三者的綁定聯絡.



另外在evdev中還有個結構:


struct evdev_client {    unsigned int head;  //buffer數組的索引頭    unsigned int tail;   //buffer數組的索引尾    unsigned int packet_head; /* [future] position of the first element of next packet */    spinlock_t buffer_lock; /* protects access to buffer, head and tail */    struct wake_lock wake_lock;    bool use_wake_lock;    char name[28];    struct fasync_struct *fasync;    //非同步通知函數    struct evdev *evdev;  //包含一個evdev變數    struct list_head node;  //鏈表    unsigned int bufsize;    struct input_event buffer[];   //input_event資料結構的數組,input_event代表一個事件,基本成員:類型(type),編碼(code),值(value)};

這個結構會在evdev被開啟的時候 建立,這裡關於evdev的初始以及在input系統中承接作用暫時介紹到這裡,

前文 Linux/Android——輸入子系統input_event傳遞 中有記錄從裝置驅動傳遞上來的event是怎麼到input核心,然後接著往上傳遞的,接下來就是用到evdev傳遞了.下篇介紹.












Linux/Android——input_handler之evdev

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.