input设备驱动开发

Posted 四季帆

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了input设备驱动开发相关的知识,希望对你有一定的参考价值。

1. 作为输入设备的驱动开发者,需要做以下几步:

        在驱动加载模块中,设置你的input设备支持的事件类型,类型参见表1设置;

        注册中断处理函数,例如键盘设备需要编写按键的抬起、放下,触摸屏设备需要编写按下、抬起、绝对移动,鼠标设备需要编写单击、抬起、相对移动,并且需要在必要的时候提交硬件数据(键值/坐标/状态等等);

        将输入设备注册到输入子系统中。

表1:事件类型

EV_SYN     0x00    同步事件
EV_KEY     0x01    按键事件
EV_REL     0x02    相对坐标(如:鼠标移动,报告相对最后一次位置的偏移)
EV_ABS     0x03    绝对坐标(如:触摸屏或操作杆,报告绝对的坐标位置)
EV_MSC     0x04    其它
EV_SW      0x05    开关
EV_LED     0x11    按键/设备灯
EV_SND     0x12    声音/警报
EV_REP     0x14    重复
EV_FF     0x15    力反馈
EV_PWR    0x16    电源
EV_MAX    0x1f    事件类型最大个数和提供位掩码支持
EV_FF_STATUS    0x17   力反馈状态

        Linux输入子系统提供了设备驱动层上报输入事件的函数,在include/linux/input.h中:

//上报按键事件
voidinput_report_key(struct input_dev *dev, unsigned int code, int value); 

//上报相对坐标事件
voidinput_report_rel(struct input_dev *dev, unsigned int code, int value);
 
//上报绝对坐标事件
voidinput_report_abs(struct input_dev *dev, unsigned int code, int value);

        当上报输入事件之后,需要调用下面的函数来通知输入子系统,以处理设备产生的完整事件:

void input_sync(struct input_dev *dev);

        输入子系统的一些位操作NBITS、BIT、LONG经常被用到:

#define NBITS(x) (((x)/BITS_PER_LONG)+1)       //通过位x获取数组的长度
#define BIT(x)   (1UL<<((x)%BITS_PER_LONG))    //返回位x在数组中的位域
#define LONG(x)  ((x)/BITS_PER_LONG)           //返回位x的索引

2. 输入设备驱动的简单案例

        在Linux内核文档的documentation/input下,有一个input-programming.txt文件,讲解了编写输入设备驱动程序的核心步骤。

        提供的案例代码描述了一个button设备,当有按下/释放发生时,BUTTON_IRQ被触发,产生的事件通过BUTTON_PORT引脚获取(即把BUTTON_PORT引脚的电平状态上报),以下是驱动的源代码:

#include <linux/input.h>                                                                                                        
#include <linux/module.h>
#include <linux/init.h>
#include <asm/irq.h>
#include <asm/io.h>

static struct input_dev *button_dev;

//由于案例代码只产生一个按键的值,因此input_sync()在这里不起关键作用。但如果是
//一个触摸屏,即有x坐标和y坐标,则需要通过input_sync()函数把x和y坐标完整地传递
//给输入子系统。
static void button_interrupt(int irq, void*dummy, struct pt_regs *fp)

        input_report_key(button_dev, BTN_1, inb(BUTTON_PORT) & 1);
        input_sync(button_dev);
      
 
static int __init button_init(void)

       int error;
       
       if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button",NULL)) 
                printk(KERN_ERR"button.c: Can't allocate irq %d\\n", button_irq);
                return -EBUSY;
             
        
       button_dev = input_allocate_device();
       if (!button_dev) 
               printk(KERN_ERR"button.c: Not enough memory\\n");
               error = -ENOMEM;
               goto err_free_irq;
       
 
       /* 上报事件类型定为按键 */
       button_dev->evbit[0] = BIT(EV_KEY);
       button_dev->keybit[LONG(BTN_0)] = BIT(BTN_0);

//把button_dev输入设备挂入输入设备链表中,并且通知事件处理层调用connect函数完成
//设备和事件处理(handler)的绑定,当用户打开设备时,便能够调用到相应的事件处理接口
//获得硬件上报的数据了 */
       error = input_register_device(button_dev);
       if (error) 
                printk(KERN_ERR"button.c: Failed to register device\\n");
                goto err_free_dev;
       
       return 0;

err_free_dev:
       input_free_device(button_dev);
err_free_irq:
       free_irq(BUTTON_IRQ, button_interrupt);
       return error;

 
static void __exit button_exit(void)

      input_unregister_device(button_dev);
      free_irq(BUTTON_IRQ, button_interrupt);

 
module_init(button_init);
module_exit(button_exit);

以上是关于input设备驱动开发的主要内容,如果未能解决你的问题,请参考以下文章

按键消抖基于FPGA的按键消抖模块开发

Linux编写一个Linux按键中断Demo

Linux编写一个Linux按键中断Demo

RTT设备与驱动之PWM

CAN总线的显性电平为什么能覆盖隐性电平?

LED驱动代码编写