linux驱动子系统之输入子系统(2)

时间:2021-10-22 17:53:49

http://blog.csdn.net/shui1025701856/article/details/7577858?ticket=ST-59873-ehNd5KsBa9J7HUEosAU1-passport.csdn.net


输入设备(按键、键盘、触摸屏、鼠标)是典型的字符设备,工作机理是底层
在按键、触摸等动作发生时产生一个中断(或驱动timer定时查询),然后CPU
通过SPI、I2C或外部存储器总线读取键值,坐标等数据。
输入核心层提供了底层输入设备驱动程序所需的API,如分配/释放一个输入设备

struct input_dev*input_allocate_device(void)

voidinput_free_device(struct input_dev *dev)

input_allocate_device分配成功后,会返回一个Input_dev的结构体


下面进行具体分析:



内核提供的输入子系统是对分散的、多种不同类别的输入设备(键盘、鼠标、触摸屏、加速计、跟踪球、操纵杆等)进行统一处理的驱动程序。

 

输入子系统带来的好处:

l  抽象底层形态各异的硬件输入设备,为上层提供了统一的操作接口

l  提高了代码重用率和效率,减少了bug



输入子系统的三层结构:

l  事件驱动层

         负责和应用程序的接口

l  核心层

         提供事件驱动层和设备驱动层所需的函数接口

l  设备驱动层

         负责和底层输入设备通信

 

事件驱动层和核心层都是通用的,我们需要实现的是设备驱动层。输入设备驱动会把硬件产生的事件信息用统一的格式(struct input_event)上报给核心层,然后核心层进行分类后,再上报给相应的事件处理驱动程序,最后通过事件层传递到用户空间,应用程序可以通过设备节点来获取事件信息。比如底层报上来的是一个按键事件,核心层会报给evdev来处理;如果报的是一个鼠标事件,核心层会报给mousedev来处理

 

1.3 统一的格式

底层驱动可以通过struct input_event上报事件信息,上层应用也可以通过此结构体来获取事件信息

struct input_event {

         structtimeval time;   /* 事件产生时间 */

         __u16type;               /* 事件类型 */

         __u16code;              /* 事件代码 */

         __s32value;             /* 事件值 */

};

 

l  事件类型,定义在linux/input.h中

1.  #define EV_SYN          0x00    /*表示设备支持所有的事件*/  
2.  #define EV_KEY          0x01    /*键盘或者按键,表示一个键码*/  
3.  #define EV_REL          0x02    /*鼠标设备,表示一个相对的光标位置结果*/  
4.  #define EV_ABS          0x03    /*手写板产生的值,其是一个绝对整数值*/  
5.  #define EV_MSC          0x04    /*其他类型*/  
6.  #define EV_LED          0x11    /*LED灯设备*/  
7.  #define EV_SND          0x12    /*蜂鸣器,输入声音*/  
8.  #define EV_REP          0x14    /*允许重复按键类型*/  
9.  #define EV_PWR          0x16    /*电源管理事件*/ 

l  事件代码,不同事件类型包含了多种事件代码

EV_KEY                                KEY_1,KEY_2,KEY_3…

EV_REL                                REL_X,REL_Y, REL_Z…  

EV_ABS                                ABS_X,ABS_Y, ABS_Z…

And so on….

l  事件值,由硬件输入设备产生的,比如按键设备驱动会去获取GPIO引脚的状态(假设低电平有效),按下时是0,弹起时是1

 

1.4 输入设备驱动的实现

l  子系统中用input_dev结构体来描述一个输入设备

struct input_dev{

         /* 导出到用户空间的相关信息,在sys文件可以看到*/

         const char *name;

         const char *phys;

         const char *uniq;

         struct input_id id;

 

         unsigned longevbit[BITS_TO_LONGS(EV_CNT)];  /*输入设备的事件支持位图*/

         unsigned longkeybit[BITS_TO_LONGS(KEY_CNT)];/*按键设备支持的事件代码位图*/

         ……

}

Evbit的位图,设置了相应的位表示设备对此事件的支持

        

l  设置输入设备所支持的事件类型和事件代码,也就是输入设备对哪些事件具有处理能力,通常我们使用set_bit函数来设置,而不采用直接赋值

n  设置支持的事件类型(支持按键事件)

          set_bit(EV_KEY, input_dev->evbit);

 

n  设置支持的事件代码(支持按键1)

          set_bit(KEY_1, input_dev->keybit);

 

n  也可以直接调用input_set_capability来完成

input_set_capability(input_dev,EV_KEY, KEY_1);

 

l  内核为我们提供了一组函数接口,来实现输入设备驱动

n  分配/释放一个输入设备

struct input_dev*input_allocate_device(void)

voidinput_free_device(struct input_dev *dev)

input_allocate_device分配成功后,会返回一个Input_dev的结构体

 

n  注册和注销输入设备

          int input_register_device(struct input_dev*dev)

          void input_unregister_device(struct input_dev*dev)

注册函数用于将Input_dev注册进核心层中,注销则相反。参数dev是input_allocate_device的返回值

 

n  报告输入事件

报告按键值

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)

用于事件同步 ,防止数据混乱

voidinput_sync(struct input_dev *dev)

以上都是对Input_event的封装

voidinput_event(struct input_dev *dev,unsigned int type, unsigned int code, intvalue)

and so on…




input通用数据结构
1. input_dev是input设备基本的设备结构,每个input驱动程序必须分配初始化这样一个结构
include/linux/input.h
struct input_dev {
const char *name; //设备名称
const char *phys;//设备在系统的物理路径
const char *uniq;//统一的ID
struct input_id id;//设备ID
unsigned long evbit[BITS_TO_LONGS(EV_CNT)];//事件
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];//按键
unsigned long relbit[BITS_TO_LONGS(REL_CNT)];//相对设备
unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];//绝对设备
unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];//杂项设备
unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];//LED
unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];//声音设备
unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];//强制反馈设备
unsigned long swbit[BITS_TO_LONGS(SW_CNT)];//开关设备
unsigned int keycodemax;//按键码的最大值
unsigned int keycodesize;//按键码的大小
void *keycode;//按键码
int (*setkeycode)(struct input_dev *dev,
 unsigned int scancode, unsigned int keycode);
int (*getkeycode)(struct input_dev *dev,
 unsigned int scancode, unsigned int *keycode);
struct ff_device *ff;
unsigned int repeat_key;
struct timer_list timer;
int sync;
int abs[ABS_CNT];
int rep[REP_MAX + 1];
unsigned long key[BITS_TO_LONGS(KEY_CNT)];
unsigned long led[BITS_TO_LONGS(LED_CNT)];
unsigned long snd[BITS_TO_LONGS(SND_CNT)];
unsigned long sw[BITS_TO_LONGS(SW_CNT)];
int absmax[ABS_CNT];
int absmin[ABS_CNT];
int absfuzz[ABS_CNT];
int absflat[ABS_CNT];
int absres[ABS_CNT];
int (*open)(struct input_dev *dev);
void (*close)(struct input_dev *dev);
int (*flush)(struct input_dev *dev, struct file *file);
int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
struct input_handle *grab;
spinlock_t event_lock;
struct mutex mutex;
unsigned int users;
bool going_away;
struct device dev;
struct list_head h_list;
struct list_head node;
};


2. input_event 驱动层向input子系统核心报告事件的函数。
void input_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value);
 
不同设备内容的报告均是通过input_event函数来完成的,选择使用了不同参数而已
@type:类型(EV_KEY键盘,EV_REL相对,EV_ABS绝对)
@code:编码(事件代码,如键盘代码)
@value:值(EV_KEY当按键按下时值为1,松开时值为0,如果事件为EV_REL,value的正
数值和负数值分别代表两个不同的方向)

键盘keyboard属于按键设备EV_KEY
轨迹属于相对设备EV_REL
触模屏属于绝对设备EV_ABS


3. 分配并初始化input_dev
input_allocate_device()
4. 向内核注册一个input设备
input_register_device()


5. input驱动流程
1)定义input设备结构
struct input_dev *input;
2)分配并初始化input_dev接构
input = input_allocate_device();
input->name = "gpio-keys";
input->phys = "gpio-keys/input0";
input->id.bustype = BUS_HOST;
input->id.vendor  = 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0100;
3) 记录本设备对于哪些事件感兴趣(对其进行处理)input_set_capability();
定义/drivers/input/input.c
void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)
input_set_capability(input, EV_KEY, 101);


4)向内核注册一个input设备
input_register_device(input);


5)记录按键状态
int state = (gpio_get_value((S3C64XX_GPN(0)) ? 1 : 0) ^ 1 ;


6)汇报输入事件
input_event(input, EV_KEY, 101, !!state);


7)等待输入事件处理完成
input_sync(input);


8)注销一个input设备
input_unregister_device(input);


/************************input gpio_keys 例子********************/
[csharp]  view plain  copy
  1. /** 
  2. *interrupt key kernel 2.6.35.7 
  3. */  
  4.  
  5. #include <linux/module.h>  
  6. #include <linux/init.h>  
  7. #include <linux/interrupt.h>  
  8. #include <linux/fs.h>  
  9. #include <mach/gpio.h>  
  10. #include <linux/input.h>  
  11. #include <linux/slab.h>  
  12. #include <linux/delay.h>  
  13. #include <linux/timer.h>  
  14. #define KEY_MAJOR 233  
  15. #define DEVICE_NAME "gpio_key"  
  16.   
  17. struct gpio_button_data {  
  18.     struct input_dev *input;  
  19.     struct timer_list timer;  
  20.     struct work_struct work;  
  21.       
  22. } *data;  
  23.   
  24.   
  25. static void gpio_keys_report_event(void)  
  26. {  
  27.       
  28.     struct input_dev *input = data->input;  
  29.     //记录按键状态  
  30.     int state = (gpio_get_value(S3C64XX_GPN(0)) ? 1 : 0) ^ 1 ;  
  31.     //汇报输入事件  
  32.     unsigned int type = EV_KEY;  
  33.     int code = 139;  
  34.     printk("type=%d, code=%d, state=%d\n", type, code, state);  
  35.     /**5. 汇报事件*/  
  36.     input_event(input, type, code, !!state);  
  37.     //等待输入事件完成  
  38.     input_sync(input);  
  39.       
  40. }  
  41. static void gpio_key_work_func(struct work_struct *work)  
  42. {  
  43.     //struct gpio_button_data *data =  
  44.             //  container_of(work, struct gpio_button_data, work);  
  45.       
  46.     gpio_keys_report_event();  
  47.       
  48. }  
  49. static void gpio_keys_timer(unsigned long _data)  
  50. {  
  51.     schedule_work(&data->work);  
  52. }  
  53.   
  54. static irqreturn_t gpio_key_isr(int irq, void *dev_id)  
  55. {  
  56.        
  57.     mod_timer(&data->timer, jiffies+msecs_to_jiffies(0));  
  58.     schedule_work(&data->work);  
  59.     return IRQ_HANDLED;  
  60. }  
  61.   
  62. static int keys_init(void)  
  63. {  
  64.     int ret = 0;  
  65.     int irq;//中断号  
  66.     unsigned long irqflags;  
  67.     setup_timer(&data->timer, gpio_keys_timer, (unsigned long)data);  
  68.     add_timer(&data->timer);  
  69.     //申请管脚  
  70.     gpio_request(S3C64XX_GPN(0), "HOME");  
  71.     //设置为输入  
  72.     gpio_direction_input(S3C64XX_GPN(0));  
  73.       
  74.     irq = gpio_to_irq(S3C64XX_GPN(0));  
  75.     printk("the gpio_key irq is [%d]\n ", irq);  
  76.       
  77.     irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;  
  78.       
  79.     ret = request_irq(irq, gpio_key_isr, irqflags, "HOME", NULL);  
  80.     if (ret) {  
  81.         printk("can't get gpio irq\n");  
  82.         return -1;  
  83.     }  
  84.       
  85.     return 0;  
  86. }  
  87.   
  88.   
  89. static int gpio_keys_probe(void)  
  90. {  
  91.     int ret = 0;  
  92.       
  93.     //struct gpio_button_data *data;  
  94.     /**1. 定义input_dev 结构*/  
  95.     struct input_dev *input;  
  96.     /**2. 分配并初始化结构*/  
  97.     input = input_allocate_device();  
  98.       
  99.     input->name = "gpio-key";  
  100.     input->phys = "gpio-key/input0";  
  101.     input->id.bustype = BUS_HOST;  
  102.     input->id.vendor  = 0x0001;  
  103.     input->id.product = 0x0001;  
  104.     input->id.version = 0x0100;  
  105.       
  106.     data = kzalloc(sizeof(struct gpio_button_data), GFP_KERNEL);  
  107.     data->input = input;  
  108.       
  109.     keys_init();  
  110.     /**3. 记录感兴趣的事件*/  
  111.     input_set_capability(input, EV_KEY, 139);  
  112.       
  113.     /**4. 向内核注册一个input设备*/  
  114.     ret = input_register_device(input);  
  115.     if(ret) {  
  116.         printk("unable to register input device\n");  
  117.         return -1;  
  118.     }  
  119.       
  120.     return 0;  
  121. }  
  122.   
  123.   
  124.   
  125. int key_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)  
  126. {  
  127.   
  128.       
  129.     return 0;  
  130. }  
  131.   
  132. static const struct file_operations key_ops = {  
  133.     .owner = THIS_MODULE,  
  134.     .ioctl = key_ioctl,  
  135. };  
  136.   
  137. static int __init gpio_keys_init(void)  
  138. {  
  139.     int ret = 0;  
  140.     ret = register_chrdev(KEY_MAJOR, DEVICE_NAME, &key_ops);  
  141.       
  142.     if (ret < 0) {  
  143.         printk("can't register gpio_keys_number\n");  
  144.         return -1;  
  145.     }  
  146.   
  147.     gpio_keys_probe();  
  148.     INIT_WORK(&data->work, gpio_key_work_func);  
  149.       
  150.       
  151.       
  152.     printk("gpio_keys init\n");  
  153.     return 0;  
  154. }  
  155.   
  156. static void __exit gpio_keys_exit(void)  
  157. {  
  158.     unregister_chrdev(KEY_MAJOR, DEVICE_NAME);  
  159. }  
  160.   
  161. module_init(gpio_keys_init);  
  162. module_exit(gpio_keys_exit);  
  163. MODULE_LICENSE("GPL");  



在串口测试

/ # getevent 

add device 2: /dev/input/event0
  name:     "gpio-key"
could not get driver version for /dev/input/mice, Not a typewriter
type=1, code=139, state=1
type=1, code=139, state=1
/dev/input/event0: 0001 008b 00000001
/dev/input/event0: 0000 0000 00000000

0001表示EV_KEY,008b是按键扫描码,1按下,0抬起