input子系统(二)

二.内核代码

2.1输入子系统设备驱动层

我们先从设备驱动层进行讲解

首先设备驱动层调用input_allocate_device进行申请input_dev结构体,接着对该结构体进行赋值,然后调用input_register_device进行注册设备。同时我们在open函数里定义中断函数,中断函数里实现input_report_key的操作,向核心层报告按键消息。

通过上面分析我们主要关注input_allocate_device、input_register_device和input_report_key的实现吧。

struct input_dev *input_allocate_device(void)

{

       struct input_dev *dev;

       dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);

       if (dev) {

              dev->dev.type = &input_dev_type;   //设备属性设置

              dev->dev.class = &input_class;     //设备类设置

              device_initialize(&dev->dev);

              mutex_init(&dev->mutex);                  //初始化锁

              spin_lock_init(&dev->event_lock);    //初始化锁

              INIT_LIST_HEAD(&dev->h_list);    //初始化队列

              INIT_LIST_HEAD(&dev->node);    //初始化队列

              __module_get(THIS_MODULE);

       }

       return dev;

}

在这个input_allocate_device里,我想顺便讲讲设备属性相关的知识。由这个函数中的input_dev_type,我们对此进行跟踪。

static struct device_type input_dev_type = {

       .groups          = input_dev_attr_groups,  //待跟踪

       .release    = input_dev_release,

       .uevent           = input_dev_uevent,

#ifdef CONFIG_PM

       .pm         = &input_dev_pm_ops,

#endif

};

static const struct attribute_group *input_dev_attr_groups[] = {

       &input_dev_attr_group,    //待跟踪

       &input_dev_id_attr_group,

       &input_dev_caps_attr_group,

       NULL

};

static struct attribute_group input_dev_attr_group = {

       .attrs       = input_dev_attrs,     //待跟踪

};

static struct attribute *input_dev_attrs[] = {

       &dev_attr_name.attr,

       &dev_attr_phys.attr,

       &dev_attr_uniq.attr,

       &dev_attr_modalias.attr,     //待跟踪

       NULL

};

static DEVICE_ATTR(modalias, S_IRUGO, input_dev_show_modalias, NULL);

static ssize_t input_dev_show_modalias(struct device *dev,

                                   struct device_attribute *attr,

                                   char *buf)

{

       struct input_dev *id = to_input_dev(dev);

       ssize_t len;

       len = input_print_modalias(buf, PAGE_SIZE, id, 1);

       return min_t(int, len, PAGE_SIZE);

}

好了,我们把重点之一放在input_register_device和input_report_key的实现上吧。

int input_register_device(struct input_dev *dev)

{

       static atomic_t input_no = ATOMIC_INIT(0);

       struct input_handler *handler;

       const char *path;

       int error;

       __set_bit(EV_SYN, dev->evbit);  //设置input_dev支持所有的事件

       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; //设置键值

       dev_set_name(&dev->dev, "input%ld",

                   (unsigned long) atomic_inc_return(&input_no) - 1); //设置input_dev中设备名

       error = device_add(&dev->dev); //将input_dev中的device注册到设备模型中

       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_dev加入input_dev_list链表中

       list_for_each_entry(handler, &input_handler_list, node) //在input_handler_list中找handler

              input_attach_handler(dev, handler); //将input_dev与handler尝试匹配

       input_wakeup_procfs_readers();

       mutex_unlock(&input_mutex);

       return 0;

}

对于input_register_device中的input_attach_handler函数主要完成将input_dev与handler尝试匹配,只有匹配成功才能进行下一步操作,我们看看input_attach_handler。

static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)

{

       const struct input_device_id *id;

       int error;

       //如果定义了handler的黑名单,并且该input_dev在此黑名单中,则退出

       if (handler->blacklist && input_match_device(handler->blacklist, dev))

              return -ENODEV;

       //将该input_dev与所有的handler的白名单相匹配

       id = input_match_device(handler->id_table, dev);

       if (!id)  //如果匹配不成功的情况

              return -ENODEV;

       //如果匹配成功,调用handler的connet函数将input_dev与该handler相连

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

       if (error && error != -ENODEV)

              printk(KERN_ERR

                     "input: failed to attach handler %s to device %s, "

                     "error: %d\n",

                     handler->name, kobject_name(&dev->dev.kobj), error);

       return error;

}

在input_attach_handler中,我们主要工作是将该input_dev与所有的handler的白名单相匹配函数input_match_device,以及如果匹配成功,调用handler的connet函数将input_dev与该handler相连的函数handler->connect。

首先我们看看input_match_device

static const struct input_device_id *input_match_device(const struct input_device_id *id,

                                                 struct input_dev *dev)

{

       int i;

       for (; id->flags || id->driver_info; id++) {

              if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)

                     if (id->bustype != dev->id.bustype)  //总线是否匹配

                            continue;

              if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)

                     if (id->vendor != dev->id.vendor)   //设备厂商是否匹配

                            continue;

              if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)

                     if (id->product != dev->id.product)    //设备号是否匹配

                            continue;

              if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)

                     if (id->version != dev->id.version)   //版本号是否匹配

                            continue;

        //只有id->flags没定义或者定义的类型匹配成功才会进入MATCH_BIT的匹配项

              MATCH_BIT(evbit,  EV_MAX);

              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);

              return id;

       }

       return NULL;

}

#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;

从MATCH_BIT宏的定义可以看到,只有当input_dev和input_handler的id成员在evbit、keybit……swbit项相同才会匹配成功。而且匹配的顺序是从evbit、keybit到swbit。只要有一项不同,就会循环到id中的下一项进行比较。

总结下,注册input device的过程就是为input device设置默认值,并将其挂以input_dev_list。与挂载在input_handler_list中的handler相匹配。如果匹配成功,就会调用handler的connect函数。

好了,我们来看下Evdev.c下的evdev_connect函数吧,该函数主要用来连接input_dev和input_handler,这样事件的流通链才能建立。流通链建立后,事件才知道被谁处理,或者处理后向谁返回结果。

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;

       //寻找evdev_table中为空的一项

       for (minor = 0; minor < EVDEV_MINORS; minor++)

              if (!evdev_table[minor])

                     break;

       // EVDEV_MINORS为32,表示evdev_handler支持32个设备文件

       if (minor == EVDEV_MINORS) {

              printk(KERN_ERR "evdev: no more free evdev devices\n");

              return -ENFILE;

       }

       //分配struct evdev,里面包含一个handle,它是handler和input device的集合体

       evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);

       if (!evdev)

              return -ENOMEM;

       INIT_LIST_HEAD(&evdev->client_list);  //初始化链表

       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 = 1; 

       evdev->minor = minor;

       evdev->handle.dev = input_get_device(dev);        //给handle的input_dev赋值

       evdev->handle.name = dev_name(&evdev->dev);

       evdev->handle.handler = handler;               //给handle的input_handler赋值

       evdev->handle.private = evdev;

       evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);

       //初始化evdev->dev这个设备驱动模型

       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);  //注册input_handle到内核

       if (error)

              goto err_free_evdev;

       error = evdev_install_chrdev(evdev);    //将evdev_table的minor项指向evdev

       if (error)

              goto err_unregister_handle;

       error = device_add(&evdev->dev);       //将evdev->dev注册到sysfs文件系统中

       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_connect函数中注册input_handle的函数input_register_handle的实现

int input_register_handle(struct input_handle *handle)

{

       struct input_handler *handler = handle->handler;

       struct input_dev *dev = handle->dev;

       int error;

       error = mutex_lock_interruptible(&dev->mutex);

       if (error)

              return error;

       list_add_tail_rcu(&handle->d_node, &dev->h_list);  //将handle加入dev->h_list中

       mutex_unlock(&dev->mutex);

       list_add_tail(&handle->h_node, &handler->h_list);  //将handle加入handler->h_list中

       if (handler->start)

              handler->start(handle);

       return 0;

}

好了,我们跟着input_register_device一直分析到了input_dev,input_dev与input_handler的匹配,input_handle的注册。

------------------------------------------------------------------------------分割线--------------------------------------------------------------------------------

我们知道在设备驱动层的open函数中会注册一个中断,然后中断处理函数中会调用input_report_key来报告一次事件给输入子系统,那么我们就看看这个函数吧。

static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)

{

       input_event(dev, EV_KEY, code, !!value);

}

该函数的第一个参数是产生事件的输入设备,第二个参数是产生的事件,第三个参数是事件的值。另外,第二个参数可以取BTN_0、 BTN_1、BTN_LEFT、
BTN_RIGTH等值。当第二个参数为按键时,第三个参数表示按键的状态,value值为0表示按键释放,非0表示按键按下。

我们可以很清晰的发现,input_report_key函数中真正起作用的是input_event,该函数是用来向输入子系统报告输入设备产生的事件。

void input_event(struct input_dev *dev,

               unsigned int type, unsigned int code, int value)

{

       unsigned long flags;

       if (is_event_supported(type, dev->evbit, EV_MAX)) { //检查输入设备是否支持该事件

              spin_lock_irqsave(&dev->event_lock, flags);  //对事件锁锁定

              add_input_randomness(type, code, value);  //没有多大的作用

              input_handle_event(dev, type, code, value);  //继续输入子系统的相关模块发送数据

              spin_unlock_irqrestore(&dev->event_lock, flags);

       }

}

上面input_event函数调用的input_handle_event函数是向输入子系统传送事件信息。第一个参数是输入设备input_dev,第二个参数是事件的类型,第三个参数是键码,第四个参数是键值。好,让我们看看这个input_handle_event函数吧

static void input_handle_event(struct input_dev *dev,

                            unsigned int type, unsigned int code, int value)

{

       int disposition = INPUT_IGNORE_EVENT;  //表示使用什么样的方式处理事件

       switch (type) {

       case EV_SYN:

              switch (code) {

              case SYN_CONFIG:

                     disposition = INPUT_PASS_TO_ALL;

                     break;

              case SYN_REPORT:

                     if (!dev->sync) {

                            dev->sync = 1;

                            disposition = INPUT_PASS_TO_HANDLERS;

                     }

                     break;

              case SYN_MT_REPORT:

                     dev->sync = 0;

                     disposition = INPUT_PASS_TO_HANDLERS;

                     break;

              }

              break;

       case EV_KEY:

              //判断是否支持按键以及测试按键状态是否改变

              if (is_event_supported(code, dev->keybit, KEY_MAX) &&

                  !!test_bit(code, dev->key) != value) {

                     if (value != 2) {

                            __change_bit(code, dev->key);  //改变键的状态

                            if (value)

                                   input_start_autorepeat(dev, code);  //处理重复按键

                            else

                                   input_stop_autorepeat(dev);

                     }

                     disposition = INPUT_PASS_TO_HANDLERS;  //把事件交给handler处理

              }

              break;

       case EV_SW:

              if (is_event_supported(code, dev->swbit, SW_MAX) &&

                  !!test_bit(code, dev->sw) != value) {

                     __change_bit(code, dev->sw);

                     disposition = INPUT_PASS_TO_HANDLERS;

              }

              break;

       case EV_ABS:

              if (is_event_supported(code, dev->absbit, ABS_MAX)) {

                     if (test_bit(code, input_abs_bypass)) {

                            disposition = INPUT_PASS_TO_HANDLERS;

                            break;

                     }

                     value = input_defuzz_abs_event(value,

                                   dev->abs[code], dev->absfuzz[code]);

                     if (dev->abs[code] != value) {

                            dev->abs[code] = value;

                            disposition = INPUT_PASS_TO_HANDLERS;

                     }

              }

              break;

       case EV_REL:

              if (is_event_supported(code, dev->relbit, REL_MAX) && value)

                     disposition = INPUT_PASS_TO_HANDLERS;

              break;

       case EV_MSC:

              if (is_event_supported(code, dev->mscbit, MSC_MAX))

                     disposition = INPUT_PASS_TO_ALL;

              break;

       case EV_LED:

              if (is_event_supported(code, dev->ledbit, LED_MAX) &&

                  !!test_bit(code, dev->led) != value) {

                     __change_bit(code, dev->led);

                     disposition = INPUT_PASS_TO_ALL;

              }

              break;

       case EV_SND:

              if (is_event_supported(code, dev->sndbit, SND_MAX)) {

                     if (!!test_bit(code, dev->snd) != !!value)

                            __change_bit(code, dev->snd);

                     disposition = INPUT_PASS_TO_ALL;

              }

              break;

       case EV_REP:

              if (code <= REP_MAX && value >= 0 && dev->rep[code] != value) {

                     dev->rep[code] = value;

                     disposition = INPUT_PASS_TO_ALL;

              }

              break;

       case EV_FF:

              if (value >= 0)

                     disposition = INPUT_PASS_TO_ALL;

              break;

       case EV_PWR:

              disposition = INPUT_PASS_TO_ALL;

              break;

       }

       if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)

              dev->sync = 0;

       if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)

              dev->event(dev, type, code, value);  //事件交给input_dev处理

       if (disposition & INPUT_PASS_TO_HANDLERS)

              input_pass_event(dev, type, code, value);  //事件交给input_handler处理

}

在上面这个input_handle_event函数中,因为我们主要讨论的是键盘事件,所以在此只关注EV_KEY事件,其他事件与之类似。在这个函数的最后几行代码,我想加以补充说明。有些事件是发给设备的,而不是发给handler处理的。event函数用来向输入子系统报告一个将要发送给设备的事件,例如让LED灯点亮事件、蜂鸣器鸣叫事件等。当事件报告给输入子系统后,就要求这个设备处理这个事件。

在我们这个按键事件中,最终是把这个事件交给handler处理的,我们跟踪input_pass_event看看,它主要是将事件传递到合适的函数。

static void input_pass_event(struct input_dev *dev,

                          unsigned int type, unsigned int code, int value)

{

       struct input_handle *handle;

       rcu_read_lock();

       handle = rcu_dereference(dev->grab);  //grab是强制为input_dev的handle

       if (handle)

              handle->handler->event(handle, type, code, value);  //有grab时候

       else

       //遍历input_dev上的handle链表

              list_for_each_entry_rcu(handle, &dev->h_list, d_node) 

                     if (handle->open)  //handle的open打开表示有handler与这个input_dev匹配

                            handle->handler->event(handle,

                                                 type, code, value);  //调用事件驱动层的evdev_event函数

       rcu_read_unlock();

} 

既然如此,我们就看看事件驱动层的evdev_event函数吧

static void evdev_event(struct input_handle *handle,

                     unsigned int type, unsigned int code, int value)

{

       struct evdev *evdev = handle->private;

       struct evdev_client *client;

       struct input_event event;

       do_gettimeofday(&event.time);

       //给input_event的type、code和value赋值

       event.type = type;

       event.code = code;

       event.value = value;

       rcu_read_lock();

       client = rcu_dereference(evdev->grab);

       if (client)

              evdev_pass_event(client, &event);

       else

              list_for_each_entry_rcu(client, &evdev->client_list, node)

                     evdev_pass_event(client, &event);

       rcu_read_unlock();

       wake_up_interruptible(&evdev->wait);  //唤醒事件驱动层read中的一个等待队列

}

继续跟踪 evdev_pass_event(client, &event);

static void evdev_pass_event(struct evdev_client *client,
			     struct input_event *event)
{
	/*
	 * Interrupts are disabled, just acquire the lock
	 */
	spin_lock(&client->buffer_lock);
	wake_lock_timeout(&client->wake_lock, 5 * HZ);
	client->buffer[client->head++] = *event;//保存到client buffer中
	client->head &= EVDEV_BUFFER_SIZE - 1;
	spin_unlock(&client->buffer_lock);

	kill_fasync(&client->fasync, SIGIO, POLL_IN);
}

好了,到此为止,我们已经把中断处理函数中向输入子系统报告事件的整个流程分析完毕了。

2.2输入子系统核心层

我们现在来看看Input子系统的核心层input.c

输入子系统的核心层向上为用户层提供接口函数,向下为驱动程序提供统一的接口函数。这样就能够使输入设备的事件通过输入子系统发送给用户层应用程序,用户层应用程序也可以通过输入子系统通知驱动程序完成某项功能。输入子系统的核心层作为一个模块存在,必然有一个初始化函数,该函数为input_init。

static int __init input_init(void)

{

       int err

       input_init_abs_bypass();

       err = class_register(&input_class);

       if (err) {

              printk(KERN_ERR "input: unable to register input_dev class\n");

              return err;

       }

       err = input_proc_init();    //在proc下建立相关交互文件

       if (err)

              goto fail1;

       err = register_chrdev(INPUT_MAJOR, "input", &input_fops); //注册cdev设备号为13

       if (err) {

              printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);

              goto fail2;

       }

       return 0;

 fail2:      input_proc_exit();

 fail1:      class_unregister(&input_class);

       return err;

}

我们跟踪下input_fops

static const struct file_operations input_fops = {

       .owner = THIS_MODULE,

       .open = input_open_file,

};

文件操作指针中定义了input_open_file函数,该函数将控制转到input_handler中定义的fopss文件指针的open函数,该函数在input_handler中实现,这样就使不同的handler处理器对应了不同的文件打开方式,为完成不同功能提供了方便。我们来看看input_open_file

static int input_open_file(struct inode *inode, struct file *file)

{

       struct input_handler *handler;

       const struct file_operations *old_fops, *new_fops = NULL;

       int err;

       lock_kernel();

       handler = input_table[iminor(inode) >> 5];  //由次设备号获取handler

       if (!handler || !(new_fops = fops_get(handler->fops))) { //获取handler的fops

              err = -ENODEV;

              goto out;

       }

       if (!new_fops->open) {           //handler的fops没有实现open,表示该设备不存在

              fops_put(new_fops);

              err = -ENODEV;

              goto out;

       }

       old_fops = file->f_op;              //保存老的fops

       file->f_op = new_fops;                       //设置新的fops

       err = new_fops->open(inode, file);    //打开handler中fops下的open

       if (err) {

              fops_put(file->f_op);

              file->f_op = fops_get(old_fops);

       }

       fops_put(old_fops);

out:

       unlock_kernel();

       return err;

}

既然这里打开的是handler下fops的open,那我们就跟踪看看那里的open函数evdev_open

static int evdev_open(struct inode *inode, struct file *file)

{

       struct evdev *evdev;

       struct evdev_client *client;

       int i = iminor(inode) - EVDEV_MINOR_BASE;   //得到evdev_table[]中的序号给i

       int error;

       if (i >= EVDEV_MINORS)

              return -ENODEV;     

       error = mutex_lock_interruptible(&evdev_table_mutex);

       if (error)

              return error;

       evdev = evdev_table[i];  //取出evdev_table[]中对应的evdev

       if (evdev)

              get_device(&evdev->dev);  //增加引用计数

       mutex_unlock(&evdev_table_mutex);

       if (!evdev)

              return -ENODEV;

       client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);    //分配evdev_client

       if (!client) {

              error = -ENOMEM;

              goto err_put_evdev;

       }

       spin_lock_init(&client->buffer_lock);

       client->evdev = evdev;    //将client->evdev指向它所表示的evdev

       evdev_attach_client(evdev, client);   //将client挂在evdev->client_list上

       error = evdev_open_device(evdev);  //打开输入设备

       if (error)

              goto err_free_client;

       file->private_data = client;

       return 0;

 err_free_client:

       evdev_detach_client(evdev, client);

       kfree(client);

 err_put_evdev:

       put_device(&evdev->dev);

       return error;

}

在evdev_open里,我们重点跟踪evdev_open_device,该函数用来打开相应的输入设备,使设备准备好接收或者发送数据。

static int evdev_open_device(struct evdev *evdev)

{

       int retval;

       retval = mutex_lock_interruptible(&evdev->mutex);  //获得锁

       if (retval)

              return retval;

       if (!evdev->exist)                 //检查设备是否存在

              retval = -ENODEV;

       else if (!evdev->open++) {  //检查设备是否已经打开

              retval = input_open_device(&evdev->handle);  //没打开则调用该函数打开设备

              if (retval)

                     evdev->open--;

       }

       mutex_unlock(&evdev->mutex);

       return retval;

}

我们跟踪上面evdev_open_device中的input_open_device吧

int input_open_device(struct input_handle *handle)

{

       struct input_dev *dev = handle->dev;

       int retval;

       retval = mutex_lock_interruptible(&dev->mutex);

       if (retval)

              return retval;

       if (dev->going_away) {

              retval = -ENODEV;

              goto out;

       }

       handle->open++;  //递增handle的打开计数

       if (!dev->users++ && dev->open)  //如果第一次打开

              retval = dev->open(dev); //调用input_dev下的open函数

       if (retval) {

              dev->users--;

              if (!--handle->open) {

                     synchronize_rcu();

              }

       }

 out:

       mutex_unlock(&dev->mutex);

       return retval;

}

在input_open_device中,我们最终调用了input_dev下的open函数,而该函数就是设备驱动层的open,在按键驱动中,该open函数实现了一个申请中断的操作。

2.3输入子系统事件处理层

最后,我们来看看事件驱动层的Evdev.c,先看下该模块下的初始化函数evdev_init

static int __init evdev_init(void)

{
       return input_register_handler(&evdev_handler);

}

跟踪input_register_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]) { //是否该次设备号对应的input_table已满

                     retval = -EBUSY;

                     goto out;

              }

              input_table[handler->minor >> 5] = handler;  //将handler插入input_table

       }

       list_add_tail(&handler->node, &input_handler_list); //把handler挂到input_handler_list上

       list_for_each_entry(dev, &input_dev_list, node) //遍历input_dev_list上的input_dev

              input_attach_handler(dev, handler);  //查看input_dev与handler是否匹配

       input_wakeup_procfs_readers();

 out:

       mutex_unlock(&input_mutex);

       return retval;

}

我们知道通过input_register_handler注册了evdev_handler,我们看看evdev_handler是什么

static struct input_handler evdev_handler = {

       .event             = evdev_event,  //向输入子系统报告事件

       .connect  = evdev_connect,    //input_dev与input_handler相连

       .disconnect     = evdev_disconnect,

       .fops              = &evdev_fops,   

       .minor            = EVDEV_MINOR_BASE,

       .name             = "evdev",

       .id_table  = evdev_ids,

};

向输入子系统报告事件的函数evdev_even和将input_dev与input_handler相连的函数evdev_connect已经在前面分析过了,下面我们继续看看evdev_handler下的evdev_fops成员

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,

#endif

       .fasync           = evdev_fasync,

       .flush             = evdev_flush

};

上面这个file_operations结构体中的open我们已经分析过了,现在我们看看上面结构体中的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;

       struct input_event event;

       int retval;

       if (count < input_event_size())

              return -EINVAL;

       if (client->head == client->tail && evdev->exist &&

           (file->f_flags & O_NONBLOCK))

              return -EAGAIN;

       retval = wait_event_interruptible(evdev->wait,

              client->head != client->tail || !evdev->exist); //进入等待队列,等待有数据报告给系统

       if (retval)

              return retval;

       if (!evdev->exist)

              return -ENODEV;

       while (retval + input_event_size() <= count &&

              evdev_fetch_next_event(client, &event)) {

              if (input_event_to_user(buffer + retval, &event))   //拷贝内核数据给用户

                     return -EFAULT;

              retval += input_event_size();

       }

       return retval;

}

在这个read函数中有个等待队列,什么时候这个等待队列的条件才满足呢?我们知道在有按键,然后驱动层的中断处理函数会向输入子系统报告,最终调用了handler下的evdev_event函数,在那个函数中唤醒了这个等待队列。

好了,读分析好了,我们再讨论下写操作。其实写操作可以有两种方法,一种就是在这个handler的fops中的write里实现,但是这个不是2.6的内核常用的手段。写操作举个例子比如向LED中写0或者1,点灯。这个操作往往可以在上面的input_dev的event函数中实现。

时间: 2024-08-28 21:29:30

input子系统(二)的相关文章

20150303 IMX257 输入子系统(二)之键盘模拟

20150303 IMX257 输入子系统(二)之键盘模拟 2015-03-03 李海沿 前面我们已经详细介绍了基本的知识:地址http://www.cnblogs.com/lihaiyan/p/4309329.html 接下来,我们使用IMX257的IO引脚中断+Linux输入子系统实现一个模拟键盘按键.实现的效果是,我们使用IO模拟按键L.按键S和Enter键 这三个按键. 这次我们就不再多废话了,直接上程序,大家看代码: 实验效果图: 如图所示: 我们依次按下三个按键 第一个按键 显示的键

input子系统——架构、驱动、应用程序

一.input子系统架构 input子系统由驱动层drivers,输入子系统核心层input core,事件处理层event handler组成. 一个输入事件,通过输入设备发给系统如鼠标移动,键盘按键按下等通过device driver->input core(handler->event函数)->event handler->user space的顺序到达用户空间传给应用程序. 一个输出事件,通过系统发给输入设备,通过user space->event handler-&

input子系统详解2

上一节大概了解了输入子系统的流程 这一节认真追踪一下代码 input.c: input_init(void)函数 1 static int __init input_init(void) 2 { 3 int err; 4 5 err = class_register(&input_class); 6 if (err) { 7 printk(KERN_ERR "input: unable to register input_dev class\n"); 8 return err;

总结INPUT子系统设计(重要)

INPUT子系统 一:什么是Input子系统? (应用场景,用途) 二:怎么设计Input子系统的程序? (分配一个输入设备——注册一个输入设备——上报输入事件——注销一个输入设备——释放一个输入设备) 三:Input子系统需要知道哪些? (涉及的重要数据结构(input_dev,input_handle,input_handler),中断的相关知识) 四:一个自带按键驱动分析 (linux-4.5/drivers/input/keyboard下的amikbd.c ) 五:模拟实现案例 (通过e

全网络对Linux input子系统最清晰、详尽的分析

Linux input分析之二:解构input_handler.input_core.input_device 输入输出是用户和产品交互的手段,因此输入驱动开发在Linux驱动开发中很常见.同时,input子系统的分层架构思想在Linux驱动设计中极具代表性和先进性,因此对Linux input子系统进行深入分析很有意义. 本文继续在<Linuxinput子系统分析之一:软件分层>的基础上继续深入研究Linux输入子系统的分层架构思想以及其实现.软件分层探讨的是输入消息从底层硬件到内核.应用层

Linux/Android——input子系统核心 (三)【转】

本文转载自:http://blog.csdn.net/jscese/article/details/42123673 之前的博客有涉及到linux的input子系统,这里学习记录一下input模块. input子系统,作为管理输入设备与系统进行交互的中枢,任何的输入设备驱动都要通过input向内核注册其设备, 常用的输入设备也就是鼠标,键盘,触摸屏. 稍微细分一点整个输入体系,就是 硬件驱动层,input核心中转层,事件处理层.层次之间传递都以event事件的形式,这其中input连接上下层,分

Android驱动之 Linux Input子系统之TP——A/B(Slot)协议

utm_source=tuicool&utm_medium=referral">点击打开链接 将A/B协议这部分单独拿出来说一方面是由于这部分内容是比較easy忽视的.周围大多数用到input子系统的开发者也不甚理解.还有一方面是由于这部分知识一旦扩展到TP(触摸屏Touch Panel)的多点触摸就要与Middleware/Framework一起结合起来看才干全然掌握,复杂性所在. 这里的Middleware/Framework是针对android来说的,本人从事android这

input子系统

以前,看过国嵌关于input子系统的视频课程,说实话,我看完后脑子里很乱,给我的印象好像是input子系统驱动是一个全新的驱动架构,疑惑相当多.前几天在网上,看到有很多人介绍韦东山老师的linux驱动课程很不错,于是,我就买了第二期的视频,看了韦老师讲解的input子系统视频课程后,我完全明白了整个input子系统的工作机制.为了方便以后查阅,对input子系统的整体框架总结如下: 典型的输入设备(如键盘.鼠标)的工作机制都是差不多的,都是在设备有动作时,向CPU产生一个中断,通知它读取相应的数

input子系统 KeyPad-Touch上报数据格式与机制

-----------------------------------------------------------------------本文系本站原创,欢迎转载!转载请注明出处:http://blog.csdn.net/android_huber交流邮箱:[email protected]----------------------------------------------------------------------- linux drive中input子系统上报信息,调用函数