前言:学习驱动有一段时间了,之前所学习的驱动都是驱动最原始的写法,也移植过很多驱动,然而并没有对内核自带的驱动进行深入的了解,于是出于好奇,就认真的学习了一下内核自带的驱动,我发现之前所学习的驱动都是将file_operations结构体直接定义在驱动定义的C代码里的,当然还有它里面的一些函数操作,可是我看内核自带的驱动代码的时候我发现里面是用另外一种机制来实现这些的,那便是子系统!初次接触到子系统的我可谓一脸懵逼啊!脑海里浮现一大堆问号,比如:子系统是个什么鬼?,它是用来干嘛的?以及它在内核中它是怎么定义的?出于这些问题,为了搞明白它是怎么一回事儿,我并决定一探究竟!于是选择内核自带的按键驱动进行了具体的分析与学习!
1.什么是子系统?
内核是操作系统的核心。Linux内核提供很多基本功能,如虚拟内存、多任务、共享库、需求加载、共享写时拷贝(Copy-On-Write)以及网络功能等。增加各种不同功能导致内核代码不断增加。
Linux内核把不同功能分成不同的子系统的方法,通过一种整体的结构把各种功能集合在一起,提高了工作效率。同时还提供动态加载模块的方式,为动态修改内核功能提供了灵活性。
2.linux 驱动子系统
linux内核中自带了很多的驱动子系统,其中比较典型的有:input子系统,led子系统,framebuffer子系统(LCD),I2c子系统等,这些子系统它是通过一层一层的函数传递与封装,它实现了设备驱动的注册,以及定义了file-operations结构体里面的各种函数操作等,不需要在单独的设备驱动代码中进行注册,定义,直接调用相应的的子系统即可,
3.linux input子系统
以前学了单独的按键设备驱动以及led驱动,实际上,在linux中实现这些设备驱动,有一种更为推荐的方法,就是input输入子系统。平常我们的按键,触摸屏,鼠标等输入型设备都可以利用input接口(这个接口是什么呢?可以看我对按键驱动分析就一目了然)来简化驱动程序并实现设备驱动。Input子系统处理输入事务,任何输入设备的驱动程序都可以通过Input输入子系统提供的接口注册到内核,利用子系统提供的功能来与用户空间交互.
我对input 子系统的理解是:其实input 子系统是对linux输入设备驱动进行了高度抽象最终分成了三层:包括input设备驱动层,核心层,事件处理层,也就是说我们之前移植的按键,usb,触摸屏驱动代码也只是子系统的一部分,起初我们自己编写的那些驱动代码都是分散的,按键是按键,led是led,都没有对这些不同类别的输入设备驱动统一起来,也就是说input 子系统这种机制的出现,最大的优点我觉得就是为内核大大的简化了驱动程序!!!这个input子系统最神秘之处就是它的核心层部分,这个核心层做了什么呢?它在内核中是怎么定义的?对于这两个问题只有深入理解了input.c这个代码之后,并豁然开朗!!!当然我有分析,继续看下文...
下面我们来好好学习一下这神秘的input子系统吧!
4.input子系统原理
linux输入子系统的体系结构可以分为三个层面,分别为:硬件驱动层、子系统核心层、事件处理层 ,意思就是每个层次只是负责单独的一个功能,无需参与其他的功能,有点类似函数的封装, 三个层面具体的功能如下:
硬件驱动层:其中硬件驱动层负责操作具体的硬件设备,这层的代码是针对具体的驱动程序的,需要驱动程序的作者来编写。
子系统核心层:子系统核心层是链接其他两个层之间的纽带与桥梁,向下提供驱动层的接口,向上提供事件处理层的接口。
事件处理层:事件处理层负责与用户程序打交道,将硬件驱动层传来的事件报告给用户程序。
关于linux输入子系统的框架结构如下图所示:
****************************************************************************************************************************************************************************************
分析:由上图所展现的内容就是linux输入子系统的分层结构。
/dev/input目录下显示的是已经注册在内核中的设备编程接口,用户通过open这些设备文件来打开不同的输入设备进行硬件操作。
事件处理层为不同硬件类型提供了用户访问及处理接口。例如当我们打开设备/dev/input/mice时,会调用到事件处理层的Mouse Handler来处理输入事件,这也使得设备驱动层无需关心设备文件的操作,因为Mouse Handler已经有了对应事件处理的方法。
输入子系统由内核代码drivers/input/input.c构成,它的存在屏蔽了用户到设备驱动的交互细节,为设备驱动层和事件处理层提供了相互通信的统一界面。
*****************************************************************************************************************************************************************************************
linux输入子系统的事件处理机制:
******************************************************************************************************************************************************************************
分析:由上图可知输入子系统核心层提供的支持以及如何上报事件到input event drivers。
作为输入设备的驱动开发者,需要做以下几步:
1. 在驱动加载模块中,设置你的input设备支持的事件类型。(事件类型有:EV_SYN 0x00 同步事件;EV_KEY 0x01 按键事件;EV_LED 0x11 按键/设备灯等)
2. 注册中断处理函数,例如键盘设备需要编写按键的抬起、放下,触摸屏设备需要编写按下、抬起、绝对移动,鼠标设备需要编写单击、抬起、相对移动,并且需要在必要的时候提交硬件数据(键值/坐标/状态等等)
3.将输入设备注册到输入子系统中
*****************************************************************************************************************************************************************************************
5.内核自带的按键驱动分析
****************************************************************************************************************************************************************************
其实在linux内核中实现按键功能,主要的涉及到两方面的内容:linux的中断处理和输入子系统
Linux中断分为两个部分:前半部和后半部。前半部是实际响应中断的函数,需要用request_irq注册;后半部是由前半部调度,并在一个更安全的时间来执行的函数,该函数就是具体负责执行中断的内容。前半部不允许被其他中断干扰,因此它的内容短小,而执行后半部时可以响应其他中断。这种机制的好处是可以迅速的响应中断。
输入子系统有三个核心结构体:input_dev,input_handle和input_handler。input_dev表示一个输入设备,包含输入设备的一些相关信息;input_handler表示对输入事件的具体处理,它为输入设备的功能实现了一个接口;input_handle是用来连接输入设备和输入事件。输入子系统主要的任务就是把这三个结构体连接在一起。
参考网上很多人总结的博客,大部分人分析linux input子系统都是从硬件驱动层,核心层,事件处理层这三个层面来分析的,
感觉他们分析的蛮好,那么我也按这个套路来学习一下按键驱动子系统!!!我将从底层向用户层的方向来分析这个input 子系统!
*****************************************************************************************************************************************************************************
1.设备驱动层
在驱动层,Linux提供了一个通用的基于GPIO的按键驱动程序文件——gpio_keys.c(它是在drivers/input/keyboard目录下),
我们的按键驱动也是调用该文件的。
[zoulei@CentOS linux-3.0]$ vim drivers/input/keyboard/gpio_keys.c
static int __init gpio_keys_init(void)/*按键的初始化*/ { return platform_driver_register(&gpio_keys_device_driver);//注册驱动 }
/*gpio_keys_device_driver的内容为*/ static struct platform_drivergpio_keys_device_driver = { .probe = gpio_keys_probe, .remove = __devexit_p(gpio_keys_remove), .driver = { .name = "gpio-keys", //与设备名一致,即与mach-smdk.c文件中的平台设备中.name一致 .owner = THIS_MODULE, .pm = &gpio_keys_pm_ops, .of_match_table= gpio_keys_of_match, } };
在这个gpio_keys.c文件中我觉得最为重要且最值得注意的就是里面的probe函数,它与我们之前自己编写的代码最大的不同之处就是里面出现了input结构体,话不多说请看源代码!
448 static int __devinit gpio_keys_probe(struct platform_device *pdev) 449 { 450 struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; 451 struct gpio_keys_drvdata *ddata; 452 struct device *dev = &pdev->dev; 453 struct input_dev *input; 454 int i, error; 455 int wakeup = 0; 456 457 ddata = kzalloc(sizeof(struct gpio_keys_drvdata) + //kzalloc为驱动数据ddata开辟一段内存空间,并清零,它实现了kmalloc与memset两个函数的功能 458 pdata->nbuttons * sizeof(struct gpio_button_data), 459 GFP_KERNEL); 460 input = input_allocate_device();//为输入设备input开辟一段内存空间 461 if (!ddata || !input) { 462 dev_err(dev, "failed to allocate state\n"); 463 error = -ENOMEM; 464 goto fail1; 465 } 466 467 ddata->input = input; 468 ddata->n_buttons = pdata->nbuttons; 469 ddata->enable = pdata->enable; 470 ddata->disable = pdata->disable; 471 mutex_init(&ddata->disable_lock); 472 473 platform_set_drvdata(pdev, ddata); //把驱动数据ddata以私有数据形式存放在平台总线设备pdev中,以备日后使用 474 input_set_drvdata(input, ddata); //把驱动数据ddata以私有数据形式存放在输入设备input中,以备日后使用 475 476 input->name = pdata->name ? : pdev->name; //为输入设备input幅值 477 input->phys = "gpio-keys/input0"; 478 input->dev.parent = &pdev->dev; 479 input->open = gpio_keys_open; 480 input->close = gpio_keys_close; 481 482 input->id.bustype = BUS_HOST; 483 input->id.vendor = 0x0001; 484 input->id.product = 0x0001; 485 input->id.version = 0x0100; 486 487 /* Enable auto repeat feature of Linux input subsystem */ 488 if (pdata->rep) 489 __set_bit(EV_REP, input->evbit); //为输入设备input赋予自动重复特性,因为按键可以反复地按 490 491 for (i = 0; i < pdata->nbuttons; i++) { //为每个按键进行初始化,及设置属性 492 struct gpio_keys_button *button = &pdata->buttons[i]; 493 struct gpio_button_data *bdata = &ddata->data[i]; 494 unsigned int type = button->type ?: EV_KEY; 495 496 bdata->input = input; 497 bdata->button = button; 498 499 error = gpio_keys_setup_key(pdev, bdata, button);// gpio_keys_setup_key为关键函数,它的作用是初始化按键,即按键去抖,以及 500 if (error) //按键中断函数的定义都是在这个函数中 501 goto fail2; 502 503 if (button->wakeup) 504 wakeup = 1; 505 506 input_set_capability(input, type, button->code); 507 } 508 /* 在指定目录下生成sys属性文件,即在执行完下面语句后,会在/sys/devices/platform/gpio-keys/目录下生成四个文件: disabled_keys、disabled_switches、switches、keys,前两个是可读可写的,后两个为只读 */ 509 error = sysfs_create_group(&pdev->dev.kobj, &gpio_keys_attr_group); 510 if (error) { 511 dev_err(dev, "Unable to export keys/switches, error: %d\n", 512 error); 513 goto fail2; 514 } 515 516 error = input_register_device(input);//注册输入设备input,把输入设备加到输入设备链表中,并寻找合适的handler与input_handler配对 517 if (error) { 518 dev_err(dev, "Unable to register input device, error: %d\n", 519 error); 520 goto fail3; 521 } 522 523 /* get current state of buttons */ //把当前的按键状态上报给系统,这个是在系统初始化,即上电的时候报告给系统的 524 for (i = 0; i < pdata->nbuttons; i++) 525 gpio_keys_report_event(&ddata->data[i]); 526 input_sync(input); //用于事件的同步,即告知系统设备已传递完一组完整的数据 527 528 device_init_wakeup(&pdev->dev, wakeup); 529 530 return 0; 531 532 fail3: 533 sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group); 534 fail2: 535 while (--i >= 0) { 536 free_irq(gpio_to_irq(pdata->buttons[i].gpio), &ddata->data[i]); 537 if (ddata->data[i].timer_debounce) 538 del_timer_sync(&ddata->data[i].timer); 539 cancel_work_sync(&ddata->data[i].work); 540 gpio_free(pdata->buttons[i].gpio); 541 } 542 543 platform_set_drvdata(pdev, NULL); 544 fail1: 545 input_free_device(input); 546 kfree(ddata); 547 548 return error; 549 }/*小节
这个probe函数它是通过主要函数gpio_keys_setup_key,实现了按键去抖,按键中断功能,并没有想当初我们自己编写的按键驱动代码那样,直接将按键去抖函数,和按键中断函数定义在按键驱动代码别处而是封装在了gpio_keys_setup_key函数中,简化了函数代码!另外probe函数中调用了 input_set_capability函数,它标识输入设备对哪些事件感兴趣,因为是按键,所以感兴趣的事件一定是EV_KEY,这个函数就是子系统处理机制中,我们事先要进行设置input设备支持的事件类型,它是定义在input.c文件中的。
这个中断函数要注意,该中断函数即为中断机制的前半部,它主要思想是在按键去抖间隔时间内不执行中断后半部分内容,只有在经过去抖间隔时间达到以后,才执行工作队列中的内容&bdata->work,即执行gpio_keys_gpio_work_func函数。中断后半部函数gpio_keys_gpio_work_func调用的是gpio_keys_gpio_report_event函数,而gpio_keys_gpio_report_event函数调用的是input_event函数,该函数的功能是把输入事件上报给系统。input_event函数经过一次次传递和封装,最终到达用户空间而被用户所用。 */
2.input 核心层
之前说过,这个核心层我觉得是input子系统最重要,也是最神秘的一层,接下来让我们来揭开其神秘的面纱吧!
[zoulei@CentOS linux-3.0]$ vim drivers/input/input.c
2133 static const struct file_operations input_fops = { 2134 .owner = THIS_MODULE, 2135 .open = input_open_file, 2136 .llseek = noop_llseek, 2137 }; static int __init input_init(void) 2140 { 2141 int err; 2142 2143 err = class_register(&input_class);//注册input类,这里就是生成了sys/class/input目录,在该目录下会看到按键驱动移植链接文件event0 2144 if (err) { 2145 pr_err("unable to register input_dev class\n"); 2146 return err; 2147 } 2148 2149 err = input_proc_init();//在proc目录下建立input子系统目录及交互文件,即/proc/bus/input目录下的devices文件和handlers文件 2150 if (err) 2151 goto fail1; 2152 2153 err = register_chrdev(INPUT_MAJOR, "input", &input_fops); 2154 if (err) { //注册一个主设备号为INPUT_MAJOR(13),次设备号为0~255,文件操作符为input_fops的字符设备 2155 pr_err("unable to register char major %d", INPUT_MAJOR); 2156 goto fail2; 2157 } 2158 2159 return 0; 2160 2161 fail2: input_proc_exit(); 2162 fail1: class_unregister(&input_class); 2163 return err; 2164 }这个初始化函数中,有一个注册字符设备函数register_chrdev,这个函数有一个参数input_fops值得注意,所有主设备号13的字符设备的操作最终都会转入到input_fops中。例如event0的主设备号为13,对其的操作会落在input_fops中,这个结构体中,源代码中就只有两个函数操作成员,分别是open与llseek,这个open对应的是源代码中的 input_open_file,通过对input_open_file函数的学习,我发现input_fops只是输入子系统通用的file_operations,对于不同的输入子系统(如键盘、鼠标、触摸屏等),通过input_open_file函数会调用各自不同的file_operations。对于按键来说,它调用的是drivers/input目录下Evdev.c文件中的evdev_fops。
struct input_dev *input_allocate_device(void) 1648 { 1649 struct input_dev *dev; 1650 1651 dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL); 1652 if (dev) { 1653 dev->dev.type = &input_dev_type;//初始化设备的类型 1654 dev->dev.class = &input_class; 1655 device_initialize(&dev->dev); 1656 mutex_init(&dev->mutex); //初始化互斥锁 1657 spin_lock_init(&dev->event_lock); //初始化自旋锁 1658 INIT_LIST_HEAD(&dev->h_list); //初始化链表 1659 INIT_LIST_HEAD(&dev->node); 1660 1661 __module_get(THIS_MODULE); 1662 } 1663 1664 return dev; 1665 }
该函数返回一个指向 input_dev 类型的指针,该结构体是一个输入设备结构体,包含了输入设备的一些相关信息,如
设备支持的按键码、设备的名称、设备支持的事件等
static void input_handle_event(struct input_dev *dev, 217 unsigned int type, unsigned int code, int value) 218 { 219 int disposition = INPUT_IGNORE_EVENT; 221 switch (type) { 223 case EV_SYN: 224 switch (code) { 225 case SYN_CONFIG: 226 disposition = INPUT_PASS_TO_ALL; 227 break; 228 229 case SYN_REPORT: 230 if (!dev->sync) { 231 dev->sync = true; 232 disposition = INPUT_PASS_TO_HANDLERS; 233 } 234 break; 235 case SYN_MT_REPORT: 236 dev->sync = false; 237 disposition = INPUT_PASS_TO_HANDLERS; 238 break; 239 } ...... }
347 void input_event(struct input_dev *dev, 348 unsigned int type, unsigned int code, int value) 349 { 350 unsigned long flags; 352 if (is_event_supported(type, dev->evbit, EV_MAX)) { 354 spin_lock_irqsave(&dev->event_lock, flags); 355 add_input_randomness(type, code, value); 356 input_handle_event(dev, type, code, value); 357 spin_unlock_irqrestore(&dev->event_lock, flags); 358 } 359 }上面我们已经分析过input_event函数的功能是上报事件给子系统,但是分析这个函数起着主要作用的是里面的input_handle_event函数,而 input_handle_event
中有一个event()函数与input_pass_event()函数。有些事件是发送给设备,而不是发送给 handler 处理的。event()函数用来向输入子系统报告一个将要发送给设备的事件,例如让 LED 灯点亮事件、蜂鸣器鸣叫事件等。当事件报告给输入子系统后,就要求设备处理这个事件。这里就体现了函数的一次次传递与封装!
*****************************************************************************************************************************************************************************************
/*小节其实这个核心层,事件处理层,还有设备驱动层三者之间的关系就好像一个单独驱动中的device与driver的关系一样,设备驱动层定义了设备注册的接口函数input_register_handle,因为设备驱动层input device与事件处理层hander 有一个匹配的过程,匹配成功就会调用事件处理层中的connect函数,connect函数会调用input_register_handle,当然就好比device与driver匹配成功之后会调用probe函数一样,
所有的input device都挂在
input_dev_list
链上
所有的handler都挂在input_handler_list上,
所以事件层要与设备驱动层进行匹配就必须的提前注册。
事件层与设备驱动层之间的匹配调用的是input_attach_handler-> input_match_device函数。
总的来说,这个核心层所扮演的角色就好比一家人,一对夫妻上要照顾自己的爸妈,下要照顾自己的子女,而照顾所需要的钱就是函数接口,所以说子系统核心层是链接其他两个层之间的纽带与桥梁,向下提供驱动层的接口(input_register_handle),向上提供事件处理层的接口(connect)。
3.事件处理层
事件处理层文件主要是用来支持输入设备并与用户空间交互,这部分代码一般不需要我们自己去编写,因为Linux内核已经自带有一些事件处理器,可以支持大部分输入设备,比如Evdev.c、mousedev.c、joydev.c等。对按键来说,用到的是Evdev.c文件(它在drivers/input目录下)。
[zoulei@CentOS linux-3.0]$ vim drivers/input/evdev.c
998 static int __init evdev_init(void)
999 {
1000 return input_register_handler(&evdev_handler);//注册evdev_handler
1001 }
evdev_handler类型就是前面介绍过的三个重要结构体中的一个——input_handler,
input_handler 是输入子系统的主要数据结构,一般将其称为 handler 处理器,表示对输入事件的具体处理。
input_handler 为输入设备的功能实现了一个接口,输入事件最终传递到handler 处理器,handler 处理器根据一定的规则,
然后对事件进行处理,具体的规则将在下面详细介绍
它的定义为:
struct input_handler { void *private; void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value); 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; //定义了一个 name, 表示 handler 的名字,显示在/proc/bus/input/handlers 目录 //中。 const struct input_device_id *blacklist; //指向一个 input_device_id 表,这个表包含 handler 应该忽略的设备 struct list_head h_list; struct list_head node; };
988 static struct input_handler evdev_handler = { 989 .event = evdev_event,//传递信息是调用,在input_pass_event 990 .connect = evdev_connect,//device与handler匹配时调用 991 .disconnect = evdev_disconnect, 992 .fops = &evdev_fops, 993 .minor = EVDEV_MINOR_BASE, 994 .name = "evdev", 995 .id_table = evdev_ids, 996 };其中evdev_connect主要用来连接input_dev和input_handler;evdev_event是把input事件发给所有的client。另外在该结构体内还定义了一个fops集合,它被赋值为evdev_fops的指针。evdev_fops就是在介绍核心层input.c文件时,提到的当处理按键输入子系统时,file_operations所对应的就是evdev_fops
input_handler注册
1923 int input_register_handler(struct input_handler *handler)
1924 {
1925 struct input_dev *dev;
1926 int retval;
1927
1928 retval = mutex_lock_interruptible(&input_mutex);
1929 if (retval)
1930 return retval;
1931
1932 INIT_LIST_HEAD(&handler->h_list);
1933//其中的 handler->minor 表示对应 input 设备结点的次设备号。 handler->minor以右移 5 位作为索引值插入到 //input_table[ ]
1934 if (handler->fops != NULL) {
1935 if (input_table[handler->minor >> 5]) {
1936 retval = -EBUSY;
1937 goto out;
1938 }
1939 input_table[handler->minor >> 5] = handler;
1940 }
1941
1942 list_add_tail(&handler->node, &input_handler_list);
1943
1944 list_for_each_entry(dev, &input_dev_list, node)
1945 input_attach_handler(dev, handler);
//input_attach_handler()函数的作用是匹配 input_dev_list 链表中的 input_dev 与 handler。如果成功会将 input_dev
//与 handler 联系起来。也就是说在注册handler和dev时都会去调用该函数。
1947 input_wakeup_procfs_readers(); 1948 1949 out: 1950 mutex_unlock(&input_mutex); 1951 return retval; 1952 }evdev_open函数分析
275 static int evdev_open(struct inode *inode, struct file *file) 276 { 277 struct evdev *evdev; 278 struct evdev_client *client; 279 int i = iminor(inode) - EVDEV_MINOR_BASE;//得到设备列表中的序号 280 unsigned int bufsize; 281 int error; 283 if (i >= EVDEV_MINORS) 284 return -ENODEV; 286 error = mutex_lock_interruptible(&evdev_table_mutex); 287 if (error) 288 return error; 289 evdev = evdev_table[i];//得到设备列表中的序号 290 if (evdev) 291 get_device(&evdev->dev); //增加计数 292 mutex_unlock(&evdev_table_mutex); 294 if (!evdev) 295 return -ENODEV; 297 bufsize = evdev_compute_buffer_size(evdev->handle.dev); 299 client = kzalloc(sizeof(struct evdev_client) + //分配并初始化client结构体 300 bufsize * sizeof(struct input_event), 301 GFP_KERNEL); 302 if (!client) { 303 error = -ENOMEM; 304 goto err_put_evdev; 305 } 307 client->bufsize = bufsize; 308 spin_lock_init(&client->buffer_lock); 309 client->evdev = evdev; 310 evdev_attach_client(evdev, client); 312 error = evdev_open_device(evdev); //evdev_open_device函数的作用是打开设备,当设备是第一次被打开时,则调用input_open_device函数 313 if (error) 314 goto err_free_client; 316 file->private_data = client; 317 nonseekable_open(inode, file); 318
evdev_open函数负责打开一个输入设备,但是真正起到打开设备的是evdev_open_device
383 static ssize_t evdev_read(struct file *file, char __user *buffer,//evdev_read函数负责读取设备 384 size_t count, loff_t *ppos) 385 { 386 struct evdev_client *client = file->private_data; 387 struct evdev *evdev = client->evdev; 388 struct input_event event; 389 int retval; 390 391 if (count < input_event_size()) //对读取的数据大小进行判断 392 return -EINVAL; 393 394 if (client->packet_head == client->tail && evdev->exist && //如果缓存中没有数据可读而设备又存在,并且被设置为O_NONBLOCK方式, 395 (file->f_flags & O_NONBLOCK)) //则退出 396 return -EAGAIN; 397 398 retval = wait_event_interruptible(evdev->wait, //等待缓存中是否有数据可读,或设备是否被移走 399 client->packet_head != client->tail || !evdev->exist); 400 if (retval) 401 return retval; 402 403 if (!evdev->exist)//如果设备不存在,则退出 404 return -ENODEV; 405 406 while (retval + input_event_size() <= count && 407 evdev_fetch_next_event(client, &event)) { 408 409 if (input_event_to_user(buffer + retval, &event)) 410 return -EFAULT; 411 412 retval += input_event_size(); 413 } 414 415 return retval; 416 }
evdev_connect分析:
908 static int evdev_connect(struct input_handler *handler, struct input_dev *dev, 909 const struct input_device_id *id) 910 { 911 struct evdev *evdev; 912 int minor; 913 int error; 914 915 for (minor = 0; minor < EVDEV_MINORS; minor++) 916 if (!evdev_table[minor]) 917 break; 918 919 if (minor == EVDEV_MINORS) { 920 pr_err("no more free evdev devices\n"); 921 return -ENFILE; 922 } 923 924 evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); 925 if (!evdev) 926 return -ENOMEM; 927 928 INIT_LIST_HEAD(&evdev->client_list); 929 spin_lock_init(&evdev->client_lock); 930 mutex_init(&evdev->mutex); 931 init_waitqueue_head(&evdev->wait); 932 933 dev_set_name(&evdev->dev, "event%d", minor); 934 evdev->exist = true; 935 evdev->minor = minor; 937 evdev->handle.dev = input_get_device(dev); 938 evdev->handle.name = dev_name(&evdev->dev); 939 evdev->handle.handler = handler; 940 evdev->handle.private = evdev; 942 evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor); 943 evdev->dev.class = &input_class; 944 evdev->dev.parent = &dev->dev; 945 evdev->dev.release = evdev_free; 946 device_initialize(&evdev->dev);
//在这段代码里主要完成 evdev封装的 device的初始化 .注意在这里 ,使它所属的类指向 input_class.这样在 /sysfs中创
//建的设备目录就会在 /sys/class/input/下面显示
948 error = input_register_handle(&evdev->handle); ...... }将handle挂到所对应input device的h_list链表上.还将handle挂到对应的handler的hlist链表上.如果handler定
义了start函数,将调用之. 到这里,我们已经看到了input device, handler和handle是怎么关联起来的了
到这里input子系统按键驱动就分析完了。。。