1、input子系统架构

 Linux input输入子系统笔记_第1张图片

下面对每层进行分析:

2、核心层input.c

input_init-> register_chrdev(INPUT_MAJOR, "input", &input_fops);
static struct file_operations input_fops = {
    .owner = THIS_MODULE,
    .open = input_open_file,
};
static int input_open_file(struct inode *inode, struct file *file)
{
    ...
    struct input_handler *handler = input_table[iminor(inode) >> 5];
    struct file_operations *old_fops, *new_fops = NULL;
    if (!handler || !(new_fops = fops_get(handler->fops)))
    return -ENODEV;
    int err;
    old_fops = file->f_op;
    file->f_op = new_fops;
    err = new_fops->open(inode, file);
    …
}

核心层主要创建了一个字符设备,并且只初始化了open函数。当打开设备时,input_open_file函数被调用,这个函数将从input_table数组中找到设备对应的handler,然后将设备的真正的fops结构赋值,最终掉用的就是对应设备的open函数。关于input_table数组在那里赋值,当然就是注册handler时。在handler层说明。

3、设备层

通过input_register_device注册设备。

void input_register_device(struct input_dev *dev)
{
    …
    INIT_LIST_HEAD(&dev->h_list);
     //加入list
    list_add_tail(&dev->node, &input_dev_list);
    // 对于每一个input_handler,都调用input_attach_handler
    list_for_each_entry(handler, &input_handler_list, node)
    if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
    if ((id = input_match_device(handler->id_table, dev)))//根据id->flags进行对应匹配
    //如果匹配成功则调用handler的connect函数
    if ((handle = handler->connect(handler, dev, id)))
        input_link_handle(handle);//handle加入list
}

4、handler层

通过input_register_handler注册handler

void input_register_handler(struct input_handler *handler)
{
    ...
    INIT_LIST_HEAD(&handler->h_list);
    if (handler->fops != NULL)
    input_table[handler->minor >> 5] = handler; //赋值input_table
    list_add_tail(&handler->node, &input_handler_list);
    // 对于每个input_dev,调用input_attach_handler
    list_for_each_entry(dev, &input_dev_list, node)
    if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
    if ((id = input_match_device(handler->id_table, dev))) //根据id->flags进行对应匹配
    //如果匹配成功则调用handler的connect函数
    if ((handle = handler->connect(handler, dev, id)))
        input_link_handle(handle); //handle加入list
      ...
}

注册input_dev或input_handler时,会两两比较左边的input_dev和右边的input_handler,根据input_handler的id_table判断这个input_handler能否支持这个input_dev,如果能支持,则调用input_handler的connect函数建立"连接"

***特殊的handler:evdev_handler//用于匹配所有的devices

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,
    };
static struct input_device_id evdev_ids[] = {
{ .driver_info = 1 },/* Matches all devices */ //匹配所有的设备0
{ },/* Terminating zero entry */
};

5、怎么建立连接

当注册一个设备的时候,evdev_handler->evdev_connect被调用。

static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)
{
    ...
    if (!(evdev = kmalloc(sizeof(struct evdev), GFP_KERNEL)))
    INIT_LIST_HEAD(&evdev->list);
    init_waitqueue_head(&evdev->wait);
    evdev->exist = 1;
    evdev->minor = minor;
    evdev->handle.dev = dev;
    evdev->handle.name = evdev->name;
    evdev->handle.handler = handler;
    evdev->handle.private = evdev;
    return &evdev->handle;
    ...
}

创建handle,最主要的就是将handle.handler = handler,这样就建立了联系。通过handle将设备和hanler联系起来。

6、键盘,鼠标,怎么read

evdev_fops. evdev_read

static ssize_t evdev_read(struct file * file, char __user * buffer, size_t count, loff_t *ppos)
{
    ....
    // 无数据并且是非阻塞方式打开,则立刻返回
    if (list->head == list->tail && list->evdev->exist && (file->f_flags & O_NONBLOCK))
    return -EAGAIN;
    // 否则休眠,等待被唤醒。
    retval = wait_event_interruptible(list->evdev->wait,list->head != list->tail || (!list->evdev->exist));
    //拷贝数据到用户空间
    if (copy_to_user(buffer + retval, list->buffer + list->tail,sizeof(struct input_event))) 
    ...
}

何时可以读,当然是硬件将其唤醒。input_dev将其唤醒。

比如在gpio_keys.c中,通过中断来上报事件

中断到来:

  input_event	
    list_for_each_entry(handle, &dev->h_list, d_node)        
    if(handle->open)
    handle->handler->event(handle, type, code, value);//调用的就是evdev_event,
  input_sync //表示发送完毕

7、事件类型

struct input_dev {
    unsigned long evbit[NBITS(EV_MAX)];   // 表示能产生哪类事件
    unsigned long keybit[NBITS(KEY_MAX)]; // 表示能产生哪些按键
    unsigned long relbit[NBITS(REL_MAX)]; // 表示能产生哪些相对位移事件, x,y,滚轮
    unsigned long absbit[NBITS(ABS_MAX)]; // 表示能产生哪些绝对位移事件, x,y
    unsigned long mscbit[NBITS(MSC_MAX)];// 表示其他功能事件
    unsigned long ledbit[NBITS(LED_MAX)]; //表示指示灯事件
    unsigned long sndbit[NBITS(SND_MAX)];// 表示声音或警报事件
    unsigned long ffbit[NBITS(FF_MAX)];// 表示作用力事件
    unsigned long swbit[NBITS(SW_MAX)];// 表示开关事件

8、2.6和4.9内核input核心层的区别

   主要的流程都是一样的,4.9的内核去除了在初始化创建register_chrdev(INPUT_MAJOR, "input", &input_fops);而是在connet函数中创建对应的字符设备。也就去除了input_table数组。对2.6进行了优化,去除了查找fops的过程,直接通过handle找到对应的fops。