20150301 IMX257 输入子系统
2015-03-01 李海沿
一、输入子系统
1.输入子系统结构体定义
struct input_dev{ const char *name; 设备名 const char *phys; 设备在系统中路径 const char *uniq; struct input_id id; 用于匹配input hander参数 unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)]; unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; //设备所支持事件类型,主要有EV_SYNC,EV_KEY,EV_REL,EV_ABS等 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)]; 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 hint_events_per_packet; unsigned int keycodemax; unsigned int keycodesize; void *keycode; int (*setkeycode)(struct input_dev *dev, const struct input_keymap_entry *ke, unsigned int *old_keycode); int (*getkeycode)(struct input_dev *dev, struct input_keymap_entry *ke); struct ff_device *ff; unsigned int repeat_key; struct timer_list timer; int rep[REP_CNT]; struct input_mt_slot *mt; int mtsize; int slot; int trkid; struct input_absinfo *absinfo; unsigned long key[BITS_TO_LONGS(KEY_CNT)]; //按键对应的键值 unsigned long led[BITS_TO_LONGS(LED_CNT)]; //LED对应的指示灯状态 unsigned long snd[BITS_TO_LONGS(SND_CNT)]; unsigned long sw[BITS_TO_LONGS(SW_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); //事件处理函数,主要是接收用户下发的命令,如点亮led; struct input_handle __rcu *grab; spinlock_t event_lock; struct mutex mutex; unsigned int users; bool going_away; bool sync; struct device dev; struct list_headh_list; //设备所支持的input handle; struct list_headnode; }; |
输入设备信息:
输入设备信息,匹配input hander时主要用下面参数 struct input_id { __u16 bustype; 总线类型 __u16 vendor; 产家编号 __u16 product; 产品编号 __u16 version; 版本信息 }; |
2.输入设备事件处理结构
用于 输入设备事件处理 的数据结构: struct input_handler { void *private; void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value); bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value); bool (*match)(struct input_handler *handler, struct input_dev *dev); int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id); //当输入设备和input handler相匹配时调用该函数; void (*disconnect)(struct input_handle *handle); void (*start)(struct input_handle *handle); const struct file_operations *fops; //所支持的file operation操作; int minor; const char *name; const struct input_device_id *id_table; //所有能够支持的输入设备; struct list_headh_list; struct list_headnode; }; |
3. 链接input_dev 和input_handler 的结构体
连接input-dev 和input handler的数据结构: struct input_handle { void *private; int open; const char *name; struct input_dev *dev; input dev struct input_handler *handler; input handler struct list_headd_node; struct list_headh_node; }; |
4. 注册和注销函数
int input_register_device(struct input_dev *dev) int input_unregister_device(struct input_dev *dev) |
5. 驱动事件支持
设备驱动通过set_bit()告诉input子系统它支持哪些事件,哪些按键。例如:
set_bit(EV_KEY,button_dev.evbit) struct input_dev中的两个成员: evbit--事件类型 keybit--按键类型 事件类型: EV_RST--reset EV_REL--相对坐标 EV_MSC--其它 EV_SND--声音 EV_FF--力反馈 EV_KEY--按键 EV_ABS--绝对坐标 EV_LED--led EV_REP--repeat 当事件类型为EV_KEY时,还需指明按键类型: BTN_LEFT--鼠标左键 BTN_RIGHT--鼠标右键 BTN_MIDDLE--鼠标中键 BTN_0--数字0键 BTN_1--数字1键 |
6. 事件报告
Void input_event(struct input_dev *dev,unsigned int type,unsigned int code,int value); //报告指定type,code的输入事件 Void input_report_key(struct input_dev *dev,unsigned int code,int value); //报告键值 Void input_report_rel(struct input_dev *dev,unsigned int code,int value); //报告相对坐标 Void input_report_abs(struct input_dev *dev,unsigned int code,int value); //报告绝对坐标 Void input_sync(struct input_dev *dev); //报告同步事件 在触摸屏驱动设计中,一次坐标及按下状态的整个报告过程如下: Input_report_abs(input_dev,ABS_X,x); //X坐标 Input_report_abs(input_dev,ABS_Y,y); //Y坐标 Input_report_abs(input_dev,ABS_PRESSURE,pres); //压力 input_sync(struct input_dev *dev); //同步 structinput_event { struct timeval time; __u16 type; __u16 code; __s32 value; }; |
① code
事件的代码。如果事件类型是EV_KEY,该代码则为设备的键盘代码。例如:键盘上的按键代码值为0--127,鼠标按键代码为0x110---0x116,其中0x110(BTN_LEFT)为鼠标左键,0x111(BTN_RIGHT)为鼠标左键,其它代码含义参考include/linux/input.h文件。
②value
事件的值。若事件的类型是EV_KEY,当按键按下时值为1,松开值为0
报告完毕之后,input_sync()通知系统接受,告诉input core:此次报告已结束。
二、程序分析
1.定义输入子系统结构体
2.设置GPIO引脚模式及初始化GPIO中断
3.输入子系统相关设置
如图所示:
80行 分配一个input_dev结构体
82行 设置输入子系统支持按键操作
83行 设置支持按键的类型为BTN_0
84行 设置名字和初始化名字
86行 注册输入子系统
4.初始化定时器,用于防抖动
5.发生按键操作时,进入中断中断,开启定时器
如图所示,全局保存发生发生中断的引脚,然后开启定时器,在定时器中断函数中处理。
6.定时器中断函数
如图所示,在定时器中断函数中,获取发生按键的键值,然后使用input_report_key函数向系统报告事件,接着就是使用input_sync函数通知接收者,报告完毕。
7. 在exit函数中注销
如图所示,在exit函数中,将我们前面申请的GPIO端口和GPIO中断都释放,然后删除定时器,
接着就是注销我们的输入子系统,释放输入子系统结构体。
8.测试: 在应用程序中打开设备
如图所示,在/dev/input/下面有event1。
9.读取输入子系统
如图所示,
我们使用read函数来读取输入子系统的数据,注意此处我们得到的是一个input_event的结构体。
接着如果我们的信息是按键按下,则打印按键一些信息,如果是通知信息,则打印syn event。
10.编译测试
附驱动程序:
1 /****************************** 2 linux key_inputSystem 3 *****************************/ 4 #include <linux/module.h> 5 #include <linux/init.h> 6 #include <linux/kernel.h> 7 #include <linux/delay.h> 8 #include <linux/types.h> 9 #include <linux/ioctl.h> 10 #include <linux/gpio.h> 11 #include <linux/fs.h> 12 #include <linux/device.h> 13 #include <linux/uaccess.h> 14 #include <linux/irq.h> 15 #include <linux/wait.h> 16 #include <linux/sched.h>//error: ‘TASK_INTERRUPTIBLE‘ undeclared 17 #include <linux/interrupt.h> 18 #include <linux/input.h> 19 #include <asm/irq.h> 20 #include <asm/io.h> 21 22 #include "mx257_gpio.h" 23 #include "mx25_pins.h" 24 #include "iomux.h" 25 26 #define Driver_NAME "key_input" 27 #define GPIO2_10 MX25_PIN_A24 28 //定义各个按键按下的键值 29 struct pin_desc{ 30 unsigned int pin; 31 unsigned int key_val; 32 }; 33 //当按键按下时,键值分别为 以下值 34 struct pin_desc pins_desc[1] = { 35 {GPIO2_10, 0x05}, 36 }; 37 38 static struct input_dev *key_input_dev;//输入子系统设备结构体 39 static struct timer_list key_timer; //定时器结构体 40 static struct pin_desc *pin_desc_irq; //保存发生中断的引脚信息 41 42 //定时器到时函数 43 static int key_timer_function(unsigned long data){ 44 struct pin_desc *pin_desc_tmp = pin_desc_irq; //发生中断的引脚信息 45 unsigned int pinval_tmp; //按键键值缓冲 46 if(!pin_desc_tmp) 47 return 0; 48 pinval_tmp = gpio_get_value(IOMUX_TO_GPIO(pin_desc_tmp->pin));//获取键值 49 if(!pinval_tmp){ 50 input_report_key(key_input_dev, BTN_0,0); 51 input_sync(key_input_dev); //报告完毕,通知接收者 52 }else{ 53 input_report_key(key_input_dev, BTN_0, 1); 54 input_sync(key_input_dev); //报告完毕,通知接收者 55 } 56 return 0; 57 } 58 /* 中断程序key_irq */ 59 static irqreturn_t key_irq_function(int irq, void *dev_id) 60 { 61 pin_desc_irq = (struct pin_desc *)dev_id; //获取中断引脚的信息 62 mod_timer(&key_timer,jiffies+HZ/50); //开启定时器,时间20ms 63 64 return IRQ_RETVAL(IRQ_HANDLED); 65 } 66 //初始化函数 67 static int __init key_input_init(void) 68 { 69 printk("<0>\nHello,this is %s module!\n\n",Driver_NAME); 70 71 mxc_request_iomux(GPIO2_10, MUX_CONFIG_ALT5);//设备引脚为GPIO模式 72 gpio_request(IOMUX_TO_GPIO(GPIO2_10), "GPIO2_10");//申请IO端口使用 73 gpio_direction_input(IOMUX_TO_GPIO(GPIO2_10));//设备引脚为输入 74 //设备引脚为上拉输入 75 mxc_iomux_set_pad(GPIO2_10, PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL | PAD_CTL_100K_PU); 76 if(request_irq(IOMUX_TO_IRQ(GPIO2_10), key_irq_function, IRQF_TRIGGER_FALLING, "key_GPIO2_10", &pins_desc[0])) 77 return -1; 78 79 //input输入子系统设置 80 key_input_dev = input_allocate_device();//分配一个input_dev结构体 81 //设置能产生哪些事件 82 key_input_dev->evbit[0] = BIT_MASK(EV_KEY); //设置按键信息 83 key_input_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0); 84 key_input_dev->name = "key_input_name"; 85 key_input_dev->dev.init_name = "key_input_init_name"; 86 input_register_device(key_input_dev); 87 88 init_timer(&key_timer); //初始化定时器 89 key_timer.function = &key_timer_function; //设置定时器处理函数 90 add_timer(&key_timer); //将该定时器加入内核 91 92 printk("<0>have setting all pins to gpio interrupt mod by IRQF_TRIGGER_FALLING !\n"); 93 printk("Input system initialize successfu!\n\n"); 94 return 0; 95 } 96 //exit 97 static void __exit key_input_exit(void) 98 { 99 printk("<0>\nGoodbye,%s!\n\n",Driver_NAME); 100 /* free gpios */ 101 free_irq(IOMUX_TO_IRQ(GPIO2_10), &pins_desc[0]); 102 mxc_free_iomux(GPIO2_10, MUX_CONFIG_ALT5); 103 gpio_free(IOMUX_TO_GPIO(GPIO2_10)); 104 105 del_timer(&key_timer); //删除定时器 106 input_unregister_device(key_input_dev); //注销驱动 107 input_free_device(key_input_dev); //释放结构体内存 108 printk("Input system unregister successfu!\n\n"); 109 } 110 111 /* 这两行指定驱动程序的初始化函数和卸载函数 */ 112 module_init(key_input_init); 113 module_exit(key_input_exit); 114 115 /* 描述驱动程序的一些信息,不是必须的 */ 116 MODULE_AUTHOR("Lover雪"); 117 MODULE_VERSION("0.1.0"); 118 MODULE_DESCRIPTION("IMX257 key Driver"); 119 MODULE_LICENSE("GPL");
附应用程序:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <unistd.h> 4 #include <sys/types.h> 5 #include <sys/stat.h> 6 #include <fcntl.h> 7 #include <termios.h> 8 #include <errno.h> 9 #include <limits.h> 10 #include <time.h> 11 #include <linux/input.h> 12 #include "mx257_gpio.h" 13 14 15 int main(int argc, char **argv) 16 { 17 int fd; 18 int key_value,i=0,count; 19 struct input_event ev_key; 20 21 fd = open("/dev/input/event1",O_RDWR); 22 if(fd < 0){ 23 printf("can‘t open !!!\n"); 24 exit(1); 25 } 26 printf("open successful!\n"); 27 while(1){ 28 count = read(fd,&ev_key,sizeof(struct input_event)); 29 for(i = 0; i<(int)count/sizeof(struct input_event); i++){ 30 if(EV_KEY == ev_key.type) 31 printf("type: %d ,code: %d ,value: %d \n",ev_key.type, ev_key.code,ev_key.value); 32 if(EV_SYN == ev_key.type) 33 printf("syn event\n\n"); 34 } 35 } 36 close(fd); 37 return 0; 38 }