符合input子系统的设备驱动之按键驱动(三)

发布时间 2023-07-12 18:07:58作者: Bright-Ho~蜗牛~

作者: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 = 0i < 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)设置该结构体,让handledev指向传进来的dev,handlehandler指向传进来的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自己的链表中,这样devhandler就真正的建立了联系; input_dev可以通过从自己的链表h_list找到handle,根据handle.handler找到对该设备的处理事件handler;反过来, input_handler可以通过从自己的链表h_list找到handle,根据handle.dev找到事件处理对应的设备input_dev;


input子系统devhandler匹配连接图:





这里再着重的分析一下,硬件相关操作的定时器和按键中断处理;


定时器和中断处理,定时器主要就是按键防抖动;中断处理就是获取按键值;


定时器两要素:

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 = 0i < 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);

按下和松开都会上报数据;其原因是中断注册时,按键是双边沿触发触发中断;

所谓上报数据,就是数据准备好了,通知等待的进程去读数据了;上报函数具体做了哪些事,之前也分析过了;