符合input子系统的设备驱动之按键驱动(三)
作者:Bright-Ho
联系方式:836665637@qq.com
符合input子系统的设备驱动之按键驱动(三)
前两节我们回顾了按键实现的硬件原理,这一节我们就实现input系统的设备硬件层的内容;
(1)首先看入口函数做了哪些事情?
46 static struct input_dev *buttons_dev;
47 static struct pin_desc *irq_pd;
48 static struct timer_list buttons_timer; /*定时器*/
85 static int buttons_init(void){
86
87 int i;
89 /*1. 分配一个input_dev结构体*/
90 buttons_dev = input_allocate_device();
92 /*2. 设置*/
93 /*2.1 能产生按键类事件*/
94 set_bit(EV_KEY,buttons_dev->evbit);
95
96 /*2.2 能产生按键类事件里面的哪些按键,L S ENTER SHIFT*/
97 set_bit(KEY_L,buttons_dev->keybit);
98 set_bit(KEY_S,buttons_dev->keybit);
99 set_bit(KEY_ENTER,buttons_dev->keybit);
100 set_bit(KEY_LEFTSHIFT,buttons_dev->keybit);
101
102 /*3. 注册,会把buttons_dev放入设备链表*/
103 input_register_device(buttons_dev);
104 /*4. 硬件相关的操作*/
105
106 /*4.1 初始化定时器*/
107 init_timer(&buttons_timer);
108 buttons_timer.function = buttons_timer_function;
109 add_timer(&buttons_timer);
110
111 for(i = 0; i < 4, i++){
112 /*注册按键中断*/
113 request_irq(pins_desc[i].irq, buttons_irq_func,IRQT_BOTHEDGE,pins_desc[i].name, &pins_desc[i])
114 }
115
116 return 0;
117 }
input_dev结构体原型如下:
927 struct input_dev {
928
929 void *private;
930
931 const char *name;
932 const char *phys;
933 const char *uniq;
934 struct input_id id;
935
936 unsigned long evbit[NBITS(EV_MAX)]; /*表示能产生哪类事件,同步类,按键类,相对位移类,绝对位移*/
937 unsigned long keybit[NBITS(KEY_MAX)]; 按键事件
938 unsigned long relbit[NBITS(REL_MAX)]; 相对位移事件
939 unsigned long absbit[NBITS(ABS_MAX)]; 绝对位移事件
940 unsigned long mscbit[NBITS(MSC_MAX)]; 鼠标事件
941 unsigned long ledbit[NBITS(LED_MAX)]; led
942 unsigned long sndbit[NBITS(SND_MAX)]; 声音
943 unsigned long ffbit[NBITS(FF_MAX)];
944 unsigned long swbit[NBITS(SW_MAX)];
945 。。。。
};
设备层就是:
(1)通过核心层提供的input_allocate_device()函数,分配一个input_dev结构体;
(2)分配好了之后,就开始设置产生什么事件,比如按键类事件,按键类事件的哪些按键; (3)通过核心岑提供的input_register_device(buttons_dev)注册函数,把初始化好input_dev结构,放入设备链表;
(4)硬件相关的操作,初始化定时器,注册按键中断;这部分由于硬件设备不同,相关操作就不一样。这里分析的是按键相关的操作;
总结:
input_dev结构是对硬件设备抽象出来的一个结构;当分配,设置,注册好input_dev结构后,就把该结构加入设备链表;之后就会一一从handler链表中取出每一个input_handler处理事件,使用“核心层”提供的input_attach_handler()绑定函数和input_match_device(handler->id_table, dev)匹配函数,与input_dev匹配,它们两者之间通过id_table来匹配;匹配成功后,就直接调用“事件处理层”中input_handler里面的connect函数来建立连接;
connect(handler,dev,id)函数做哪些事情?
(1)分配一个evdev结构体
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
(2)设置该结构体,让handle的dev指向传进来的dev,handle的handler指向传进来的handler;
646 evdev->exist = 1;
647 evdev->minor = minor;
648 evdev->handle.dev = dev;
649 evdev->handle.name = evdev->name;
650 evdev->handle.handler = handler;
651 evdev->handle.private = evdev;
(3)通过MDEV机制,在dev/下生成设备接口,以便应用层调用;为什么在这里生成接口,之前有分析过;
devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
class_device_create(&input_class, &dev->cdev, devt, dev->cdev.dev, evdev->name);
(4)调用核心层提供的input_register_handle(&evdev->handle)函数;
1227 list_add_tail(&handle->d_node, &handle->dev->h_list);
1228 list_add_tail(&handle->h_node, &handler->h_list);
把handle分别放入input_dev自己的链表和input_handler自己的链表中,这样dev和handler就真正的建立了联系; input_dev可以通过从自己的链表h_list找到handle,根据handle.handler找到对该设备的处理事件handler;反过来, input_handler可以通过从自己的链表h_list找到handle,根据handle.dev找到事件处理对应的设备input_dev;
input子系统dev与handler匹配连接图:
这里再着重的分析一下,硬件相关操作的定时器和按键中断处理;
定时器和中断处理,定时器主要就是按键防抖动;中断处理就是获取按键值;
定时器两要素:
(1)定时器超时时间;
(2)定时器的处理函数;
106 /*4.1 初始化定时器*/
107 init_timer(&buttons_timer);
108 buttons_timer.function = buttons_timer_function;
109 add_timer(&buttons_timer);
110
111 for(i = 0; i < 4, i++){
112 /*注册按键中断*/
113 request_irq(pins_desc[i].irq, buttons_irq_func,IRQT_BOTHEDGE,pins_desc[i].name, &pins_desc[i])
114 }
115
中断处理函数: buttons_irq_func;
50 /*中断处理函数*/
51 static irqreturn_t buttons_irq_func(int irq, void *dev_id){
52
53 /*10ms后启动定时器*/
54 irq_pd = (struct pin_desc *)dev_id;
55 mod_timer(&buttons_timer, jiffies+HZ/100);
56 return IRQ_RETVAL(IRQ_HANDLED);
57 }
中断处理函数里面主要就是来设置定时器的超时时间,定时器超时时间一到,就会调用定时器处理函数;设置超时时间就是防止抖动,等待按键按下或者松开时,让按键值趋于稳定后,在去获取按键值;
定时器处理函数: buttons_timer_function;
59 /*定时器处理函数*/
60 static void buttons_timer_function(unsigned long data){
61
62 struct pin_desc *pindesc = irq_pd;
63 unsigned int pinval;
64
65 if(!pindesc)
66 return;
67
68 pinval = s3c2410_gpio_getpin(pindesc->pin);
69
70 if(pinval){
71
72 /*松开:最后一个参数:0-松开, 1-按下*/
73 input_event(buttons_dev,EV_KEY,pindesc->key_val,0);
74 input_sync(buttons_dev);
75 }
76 else{
77
78 /*按下:最后一个参数:0-松开, 1-按下*/
79 input_event(buttons_dev,EV_KEY,pindesc->key_val,1);
80 input_sync(buttons_dev);
81 }
82
83 }
定时器处理函数做的事:
(1)通过内核gpio函数.获取按键值
pinval = s3c2410_gpio_getpin(pindesc->pin);
(2)获取到按键值后,上报数据
input_event(buttons_dev,EV_KEY,pindesc->key_val,1);
按下和松开都会上报数据;其原因是中断注册时,按键是双边沿触发触发中断;
所谓上报数据,就是数据准备好了,通知等待的进程去读数据了;上报函数具体做了哪些事,之前也分析过了;