input输入子系统
|字数总计:2.7k|阅读时长:10分钟|阅读量:
一、整体框架

软件层面(evdev.c/keyboard.c/mousedev.c)向核心层“ input.c”注册“ handler”(input_register_handler),这一边代表“软件”;另一边是“设备层面”,向核心层 “input.c”注册“device”(input_register_device),这一边代表“硬件”。

不管是先注册软件层面的“handler”还是硬件层面的“device”。最终都会成对的调用“input_attach_handler()”。此函数会判断二者是否有能够支持的对象,有的话会建立连接。换句话说就是:注册 input_dev 或 input_handler 时,会两两比较硬件层面的input_dev 和软件层面的 input_handler,根据 input_handler 的 id_table 判断这个 input_handler 能否支持这个 input_dev,如果能支持,则调用 input_handler 的 connect 函数建立”连接。
二、如何建立连接
看看源码“input_attach_handler()”:


如何建立连接,不同的 handler 都有自已不同的方式。实例分析“evdev.c”中的“connect”:
①首先分配了一个“evdev”结构变量,“evdev”成员中有一个“input_handle handle”结构(不是 input_handler ):
②再对这个“input_handle evdev”进行设置:
③最后注册这个 handle:

④总结如下:
三、建立连接后如何读数据,比如“按键值”
应用程序来读,最终会导致与之建立连接的那个“软件层面”的“handler”里面的“读函数”被调用。如:“evdev.c”中的“evdev_handler”结构里面的成员“.fops=&evdev_fops”,在“evdev_fops”结构中有一个“读”函数“evdev_read”。


如按下一个按键后,中断处理函数就会被调用。在中断处理函数里面先确定按键值。然后才来唤醒。搜索“evdev->wait”后找到在“evdev_event()”中有唤醒操作,函数是结构“evdev_handler”的“.event”事件成员。所以谁调用“evdev_event():


也即:硬件相关的代码来唤醒睡眠,input_dev那层调用的。比如读取按键值,在设备的中断服务程序里,确定事件是什么,然后调用相应的input_handler的event处理函数上报键值,唤醒睡眠。
①输入子系统的代码在drivers/input.c 这个 c 文件中。看一个驱动程序是从“入口函数”开始查看。
②这里注册了一个主设备号“INPUT_MAJOR”为 13 的字符设备,名字为“input”,它的
file_operations 结构是“input_fops”。这个结构中只有一个“open”函数。如这里想读按键,那么这个 open 函数中应该做了某些工作。 分析“int input_open_file(struct inode *inode, struct file *file)”:
③其中有一个“input_handler *” (“输入处理器”或“输入处理句柄” ),这里这个“输入处理句柄”结构指向一个“input_table[]”数组。从这个数组里面根据这个“次设备号 iminor(inode) >> 5”找到一项给他赋值。
④新的“ file_operations”结构”new_fops”等于上面的“ input_handler ”指针变量handler 的成员“ fops”(这是一个 file_operations 结构。 input_handler 结构中有这个file_operations 成员)。
⑤把这个新的 file_operations 结构赋给此函数“input_open_file”的形参“file”的 f_op。然后再调用这个新的 file_operations 结构“new_fops”的 open(inode,file)函数,如下:
⑥这样以后要来读按键时,用到的是“struct input_handler handler”中的“new_fops”,也就是“input_table[]中与之匹配的handler的file_operations,里面各种函数齐全” ,也即input.c中的open函数只是一个“中转”作用,并不是真的帮我们注册的设备只有一个open函数。
总结就是:我们的input的子系统,他的主设备号是13,我们知道一个设备,它对应的操作集,是由他的主设备号找到的,然而,我们的input的子系统下面所有的设备,他的主设备号都是13,那么不可能我主设备号是13,下面的所有次设备号都用一个操作集。那么我们怎么通过次设备号来区分不同的输入设备的操作集呢?答案就是把input子系统,也就是主设备号为13的那个操作集,只注册成一个open函数,以后我打开所有的input输入设备,首先都会通过主设备号13找到input注册的open函数,通过这个open函数,从一个事先注册好的input_table数组里面,通过次设备号取出对应的handler结构体,这个结构体里面就包含了真正这一类次设备号所需要的操作集了,里面read等函数齐全。简而言之,也就是说input里注册的file_operations那个唯一的open函数,只是一个中转站,用来更新成我们真正所需要用到的,根据次设备号配对的操作集file_operations。
input_table[]的handler是注册handler的时候放进去的,用evdev.c举例:
1 2 3 4 5
| 看“evdev.c”中的注册过程: evdev_init(void) -->return input_register_handler(&evdev_handler); -->input_table[handler->minor >> 5] = handler; 即:input_table[2] = &evdev_handler;
|
为什么用次设备号匹配?
同一类input设备用不同的次设备号区分,但是最终都是用到同一个file_operations。
input驱动程序的主设备号是13、次设备号的分布如下:
- joystick游戏杆: 0~16;
- mouse鼠标: 32~62;
- mice鼠标: 63;
- 事件设备: 64~95;
当我们设置好能产生哪一类input后,比如设置为事件设备,调用input_register_device注册一个新的事件设备的时候,就会从当前系统的事件设备的次设备号开始累加。比如第一个是系统自带的事件设备even0–>64,那么我们再次注册时候就会以65作为次设备号。

我们知道,应用层使用设备的第一步,是open(“/dev/event1”),因此这里event1的主设备号成为关键,因为主设备号将表明你是什么设备,我们知道一个设备是根据主设备号来找到对应的file_operations,输入命令cat /proc/devices查看主设备为13的是input设备,因此当我们的应用层执行open函数打开event1设备的时候,实际上就是调用input_init中注册的那个唯一的open函数,也即input_open_file(),这个open函数就会如上面分析的那样根据次设备号来更新成真正的操作函数齐全的事件类设备的file_operations
1 2 3 4 5 6 7 8
| input.c--->input_open_file()中: { input_handler *handler = input_table[iminor(inode) >> 5] = &evdev_handler; --> new_fops = fops_get(handler->fops) --> new_fops = fops_get(&evdev_handler->fops); --> new_fops = fops_get(&evdev_handler->(fops=&evdev_fops)); ...... }
|
input_table[iminor(inode) >> 5] = &evdev_handler;(64~95)>> 5 都等于2。相当于事件设备都用了evdev_handler里面的evdev_fops操作集,里面open,read等函数齐全。假设我们注册了按键的事件设备,最终读取键值用到的就是evdev_handler里的read函数。理论上最多有32个事件设备。
六、驱动实例编写
怎么写符合一个输入子系统框架的驱动程序?
- 分配一个input_dev结构体。
- 设置这个结构体。如:能产生哪一类input、能产生这类input里的哪些东西……
- 注册此结构体。
- 硬件相关的代码,比如在中断服务程序里上报事件。
在全志H3中以PL3按键上报enter为例:
①首先在设备树中添加:
1 2 3 4
| input_dev_demo { compatible = "100ask,input_dev_demo"; gpios = <&r_pio 0 3 GPIO_ACTIVE_LOW>; };
|
②驱动代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
| #include <linux/module.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/sched.h> #include <linux/pm.h> #include <linux/slab.h> #include <linux/sysctl.h> #include <linux/proc_fs.h> #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/input.h> #include <linux/gpio_keys.h> #include <linux/workqueue.h> #include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/of.h> #include <linux/of_irq.h> #include <linux/of_gpio.h> #include <linux/spinlock.h>
#define Delay_TIME_MS 20
volatile unsigned long *PL_CON = NULL; volatile unsigned long *PL_DAT = NULL;
static struct input_dev *g_input_dev; struct timer_list ts_timer; static int g_irq; int enter_gpio; int pinval;
static void ts_irq_timer(unsigned long _data) {
#if 0 #else
volatile unsigned long PL_CON_temp;
PL_CON_temp = *PL_CON; *PL_CON &= ~(7<<12); pinval = ( (*PL_DAT) & (1<<3) ) ; *PL_CON = PL_CON_temp; #endif if (pinval) { input_event(g_input_dev, EV_KEY, KEY_ENTER, 1); input_sync(g_input_dev); } else { input_event(g_input_dev, EV_KEY, KEY_ENTER, 0); input_sync(g_input_dev); }
}
static irqreturn_t input_dev_demo_isr(int irq, void *dev_id) { mod_timer(&ts_timer, jiffies + msecs_to_jiffies(Delay_TIME_MS));
return IRQ_HANDLED; }
static int input_dev_demo_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; int error;
PL_CON = (volatile unsigned long *)ioremap(0x01F02C00, 16); PL_DAT = (volatile unsigned long *)ioremap(0x01F02C10, 16);
enter_gpio = of_get_gpio(pdev->dev.of_node, 0);
g_input_dev = devm_input_allocate_device(dev);
__set_bit(EV_KEY, g_input_dev->evbit);
__set_bit(KEY_ENTER, g_input_dev->keybit);
error = input_register_device(g_input_dev);
g_irq = gpio_to_irq(enter_gpio);
if(g_irq < 0){ printk("<kernel>: Failed to get irq number of pin !\n"); return -EINVAL; }
setup_timer(&ts_timer, ts_irq_timer, (unsigned long)NULL);
error = request_irq(g_irq, input_dev_demo_isr, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING ,"input_dev_demo_irq", NULL); if(error != 0){ printk("request_irq error!\n"); return -EBUSY; } return 0; }
static int input_dev_demo_remove(struct platform_device *pdev) { iounmap(PL_CON); iounmap(PL_DAT);
del_timer_sync(&ts_timer); free_irq(g_irq, NULL); input_unregister_device(g_input_dev); return 0; }
static const struct of_device_id input_dev_demo_of_match[] = { { .compatible = "100ask,input_dev_demo", }, { }, };
static struct platform_driver input_dev_demo_driver = { .probe = input_dev_demo_probe, .remove = input_dev_demo_remove, .driver = { .name = "input_dev_demo", .of_match_table = input_dev_demo_of_match, } };
static int __init input_dev_demo_init(void) { printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); return platform_driver_register(&input_dev_demo_driver); }
static void __exit input_dev_demo_exit(void) { platform_driver_unregister(&input_dev_demo_driver); }
module_init(input_dev_demo_init); module_exit(input_dev_demo_exit); MODULE_LICENSE("GPL");
|
③也可以使用内核自带的按键事件驱动程序,用系统自带的gpio_keys.c加PL3按键当成关机键:
1 2 3 4 5 6 7 8 9 10 11
| r_gpio_keys { compatible = "gpio-keys"; input-name = "k1"; pinctrl-names = "default"; pinctrl-0 = <&sw_r_npi>; 把这句话注释掉也能用,pinctrl子系统默认把引脚配置成了gpio k1 { label = "k1"; linux,code = <KEY_POWER>; gpios = <&r_pio 0 3 GPIO_ACTIVE_LOW>; }; };
|