当前位置:   article > 正文

lv15 input子系统框架、外设驱动开发 5

lv15 input子系统框架、外设驱动开发 5

一、input子系统基本框架

 在我们日常的Linux系统中,存在大量的输入设备,例如按键、鼠标、键盘、触摸屏、摇杆等,他们本身就是字符设备,linux内核将这些字符设备的共同性抽象出来,简化驱动开发建立了一个input子系统。

Linux内核为了两个目的:

  1. 简化纯输入类外设(如:键盘、鼠标、游戏杆、轨迹球、触摸屏。。。等等)的驱动开发

  2. 统一输入类外设产生的数据格式(struct input_event),更加方便应用层编程

设计了输入子系统框架

Linux 内核驱动可以都是遵循一个逐层抽象的架构: 最上层的抽象层便于系统软件的访问,中间层的实现硬件协议细节,同时提供上下两层连接的接口,对于最下层的 driver 来说就是要定义底层驱动要实现的接口和实际的设备控制,由于 Linux 内核各类驱动的框架支持,driver 可以更加关注设备本身的特性。
Linux输入子系统(linux input subsystem)也不例外,从上到下可以分为三层实现,分别为:输入子系统事件处理层(EventHandler)、输入子系统核心层(InputCore)和输入子系统设备驱动层。

事件处理层:接收来自核心层上报的事件,并选择对应的handler(事件处理器 struct input_handler)去处理。内核维护着多个事件处理器对象,每个input_handler对象专门处理一类事件,所有产生同类事件的设备驱动共用同一个handler。

设备驱动层:主要实现获取硬件设备的数据信息(包括触摸屏被按下、按下位置、鼠标移动、键盘按下等等),并转换为核心层定义的规范事件后提交给核心层,该层每个设备对应一个struct input_dev对象,

核心层:负责连接设备驱动层和事件处理层,为设备驱动层提供输入设备驱动的接口(struct input_dev)以及输入设备驱动的注册函数(input_register_device),为事件处理层提供输入事件驱动的接口;通知事件处理层对事件进行处理。

二、驱动开发步骤

  1. /*init或probe函数中:
  2. 1. 创建struct input_dev对象input_allocate_device
  3. 2. 设置事件类型以及相关参数set_bit
  4. 3. 注册struct input_dev对象input_register_device
  5. */
  6. /*exit或remove函数中:
  7. 1. 注销struct input_dev对象input_unregister_device
  8. 2. 销毁struct input_dev对象input_free_device
  9. */
  10. /*上报事件
  11. 两种事件上报方式:
  12. 1. 对有中断支持的输入设备:在其中断处理函数(上半部或下半部)中上报事件
  13. 2. 对无中断支持的输入设备:使用workqueue循环定时上报(struct delayed_work)
  14. 主要函数:
  15. input_event //通用上报,上报的是分量,如x轴分量
  16. input_report_abs //上报绝对坐标
  17. input_sync //完整数据检查统一上报在这边处理,如x、y、z轴数据统一上报
  18. */

相关接口:

  1. /*_init*/
  2. struct input_dev *input_allocate_device(void)//创建对象
  3. void set_bit(struct input_dev *dev,unsigned long whichbits)//设置事件类型
  4. void input_set_abs_params(struct input_dev *dev,unsigned int axis,int min,int max,int fuzz,int flat)
  5. int input_register_device(struct input_dev *dev)//注册input设备到内核
  6. /*_exit*/
  7. void input_unregister_device(struct input_dev *dev)
  8. void input_free_device(struct input_dev *dev)
  9. /*上报事件*/
  10. void input_event(struct input_dev *,unsigned int t,unsigned int c,int v)
  11. void input_report_key(struct input_dev *,unsigned int c,int v) //上报按键事件
  12. void input_report_abs(struct input_dev *,unsigned int c,int v)//上报绝对坐标事件
  13.    
  14. void input_sync(struct input_dev *)//上报完成后需要调用这些函数来通知系统处理完整事件
  15. /*应用层数据类型*/
  16. struct input_event {
  17.    struct timeval time;       // 时间戳
  18.    __u16 type;             // 事件类型
  19.    __u16 code;             // 哪个分值
  20.    __s32 value;            // 具体值      
  21. };

三、key2-input版代码解析

借用直接按键驱动的代码,按键中所有根字符设备相关的input子系统已经帮我们实现好了,都可以不需要。

  1. #include <linux/module.h>
  2. #include <linux/kernel.h>
  3. #include <linux/fs.h>
  4. #include <linux/gpio.h>
  5. #include <linux/interrupt.h>
  6. #include <linux/of_gpio.h>
  7. #include <linux/of_irq.h>
  8. #include <linux/cdev.h>
  9. #include <linux/wait.h>
  10. #include <linux/sched.h>
  11. #include <linux/poll.h>
  12. #include <linux/mm.h>
  13. #include <linux/input.h> //<------------
  14. #include <linux/delay.h>
  15. #include <linux/slab.h>
  16. #include <asm/uaccess.h>
  17. struct fs4412key2_dev
  18. {
  19. struct input_dev *pdev;
  20. int gpio;
  21. int irqno;
  22. };
  23. struct fs4412key2_dev *pgmydev = NULL;
  24. irqreturn_t key2_irq_handle(int no,void *arg)
  25. {
  26. struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)arg;
  27. int status1 = 0;
  28. int status2 = 0;
  29. status1 = gpio_get_value(pmydev->gpio);
  30. mdelay(1);
  31. status2 = gpio_get_value(pmydev->gpio);
  32. if(status1 != status2)
  33. {
  34. return IRQ_NONE;
  35. }
  36. if(status1)
  37. {
  38. input_event(pmydev->pdev,EV_KEY,KEY_2,0); //<--------------------上报事件,0按下
  39. input_sync(pmydev->pdev); //往核心层上报
  40. }
  41. else
  42. {
  43. input_event(pmydev->pdev,EV_KEY,KEY_2,1); //<--------------------上报事件,1抬起
  44. input_sync(pmydev->pdev); //往核心层上报
  45. }
  46. return IRQ_HANDLED;
  47. }
  48. int __init fs4412key2_init(void)
  49. {
  50. int ret = 0;
  51. struct device_node *pnode = NULL;
  52. pnode = of_find_node_by_path("/fs4412-key2");
  53. if(NULL == pnode)
  54. {
  55. printk("find node failed\n");
  56. return -1;
  57. }
  58. pgmydev = (struct fs4412key2_dev *)kmalloc(sizeof(struct fs4412key2_dev),GFP_KERNEL);
  59. if(NULL == pgmydev)
  60. {
  61. printk("kmallc for struct fs4412key2_dev failed\n");
  62. return -1;
  63. }
  64. pgmydev->gpio = of_get_named_gpio(pnode,"key2-gpio",0);
  65. pgmydev->irqno = irq_of_parse_and_map(pnode,0);
  66. //<---------------------
  67. pgmydev->pdev = input_allocate_device(); //分配pdev空间
  68. set_bit(EV_KEY,pgmydev->pdev->evbit); //设置事件
  69. set_bit(KEY_2,pgmydev->pdev->keybit); //上报的哪个按键
  70. ret = input_register_device(pgmydev->pdev);//注册到系统
  71. ret = request_irq(pgmydev->irqno,key2_irq_handle,IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,"fs4412key2",pgmydev);
  72. if(ret)
  73. {
  74. printk("request_irq failed\n");
  75. input_unregister_device(pgmydev->pdev); //反操作
  76. input_free_device(pgmydev->pdev); //反操作
  77. kfree(pgmydev);
  78. pgmydev = NULL;
  79. return -1;
  80. }
  81. return 0;
  82. }
  83. void __exit fs4412key2_exit(void)
  84. {
  85. free_irq(pgmydev->irqno,pgmydev);
  86. input_unregister_device(pgmydev->pdev); //反操作
  87. input_free_device(pgmydev->pdev); //反操作
  88. kfree(pgmydev);
  89. pgmydev = NULL;
  90. }
  91. MODULE_LICENSE("GPL");
  92. module_init(fs4412key2_init);
  93. module_exit(fs4412key2_exit);

测试

testkey.2

  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <linux/input.h>
  4. #include <fcntl.h>
  5. #include <unistd.h>
  6. #include <stdio.h>
  7. int main(int argc,char *argv[])
  8. {
  9. int fd = -1;
  10. struct input_event evt;
  11. if(argc < 2)
  12. {
  13. printf("Argument is too few\n");
  14. return 1;
  15. }
  16. /*open*/
  17. fd = open(argv[1],O_RDONLY);
  18. if(fd < 0)
  19. {
  20. printf("open %s failed\n",argv[1]);
  21. return 2;
  22. }
  23. /*init mpu6050*/
  24. while(1)
  25. {
  26. read(fd,&evt,sizeof(evt));
  27. if(evt.type == EV_KEY && evt.code == KEY_2)
  28. {
  29. if(evt.value)
  30. {
  31. printf("KEY2 DOWN\n");
  32. }
  33. else
  34. {
  35. printf("KEY2 UP\n");
  36. }
  37. }
  38. }
  39. /*close*/
  40. close(fd);
  41. fd = -1;
  42. return 0;
  43. }

Makefile 

  1. ifeq ($(KERNELRELEASE),)
  2. ifeq ($(ARCH),arm)
  3. KERNELDIR ?= /home/linux/Linux_4412/kernel/linux-3.14
  4. ROOTFS ?= /opt/4412/rootfs
  5. else
  6. KERNELDIR ?= /lib/modules/$(shell uname -r)/build
  7. endif
  8. PWD := $(shell pwd)
  9. modules:
  10. $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
  11. modules_install:
  12. $(MAKE) -C $(KERNELDIR) M=$(PWD) modules INSTALL_MOD_PATH=$(ROOTFS) modules_install
  13. clean:
  14. rm -rf *.o *.ko .*.cmd *.mod.* modules.order Module.symvers .tmp_versions
  15. else
  16. CONFIG_MODULE_SIG=n
  17. obj-m += fs4412_key2.o
  18. endif

 编译拷贝到跟文件系统测试

 

四、mpu6050-input版代码解析

mpu6050_drv_input.c

注:绝对坐标的上报事件,如角速度加速度无变化核心层会屏蔽掉,除非有变化

  1. #include <linux/module.h>
  2. #include <linux/kernel.h>
  3. #include <linux/fs.h>
  4. #include <linux/i2c.h>
  5. #include <linux/cdev.h>
  6. #include <linux/wait.h>
  7. #include <linux/sched.h>
  8. #include <linux/poll.h>
  9. #include <linux/slab.h>
  10. #include <linux/mm.h>
  11. #include <linux/input.h>
  12. #include <linux/io.h>
  13. #include <asm/uaccess.h>
  14. #include <asm/atomic.h>
  15. #define SMPLRT_DIV 0x19
  16. #define CONFIG 0x1A
  17. #define GYRO_CONFIG 0x1B
  18. #define ACCEL_CONFIG 0x1C
  19. #define ACCEL_XOUT_H 0x3B
  20. #define ACCEL_XOUT_L 0x3C
  21. #define ACCEL_YOUT_H 0x3D
  22. #define ACCEL_YOUT_L 0x3E
  23. #define ACCEL_ZOUT_H 0x3F
  24. #define ACCEL_ZOUT_L 0x40
  25. #define TEMP_OUT_H 0x41
  26. #define TEMP_OUT_L 0x42
  27. #define GYRO_XOUT_H 0x43
  28. #define GYRO_XOUT_L 0x44
  29. #define GYRO_YOUT_H 0x45
  30. #define GYRO_YOUT_L 0x46
  31. #define GYRO_ZOUT_H 0x47
  32. #define GYRO_ZOUT_L 0x48
  33. #define PWR_MGMT_1 0x6B
  34. struct mpu6050_dev
  35. {
  36. struct input_dev * pinput; //<---------------------
  37. struct i2c_client *pclient;
  38. struct delayed_work work; //可设置时间,到时间work回调函数会被调用
  39. };
  40. struct mpu6050_dev *pgmydev = NULL;
  41. int mpu6050_read_byte(struct i2c_client *pclt,unsigned char reg)
  42. {
  43. int ret = 0;
  44. char txbuf[1] = {reg};
  45. char rxbuf[1] = {0};
  46. struct i2c_msg msg[2] =
  47. {
  48. {pclt->addr,0,1,txbuf},
  49. {pclt->addr,I2C_M_RD,1,rxbuf}
  50. };
  51. ret = i2c_transfer(pclt->adapter,msg,ARRAY_SIZE(msg));
  52. if(ret < 0)
  53. {
  54. printk("ret = %d,in mpu6050_read_byte\n",ret);
  55. return ret;
  56. }
  57. return rxbuf[0];
  58. }
  59. int mpu6050_write_byte(struct i2c_client *pclt,unsigned char reg,unsigned char val)
  60. {
  61. int ret = 0;
  62. char txbuf[2] = {reg,val};
  63. struct i2c_msg msg[1] =
  64. {
  65. {pclt->addr,0,2,txbuf},
  66. };
  67. ret = i2c_transfer(pclt->adapter,msg,ARRAY_SIZE(msg));
  68. if(ret < 0)
  69. {
  70. printk("ret = %d,in mpu6050_write_byte\n",ret);
  71. return ret;
  72. }
  73. return 0;
  74. }
  75. void mpu6050_work_func(struct work_struct *pwk)
  76. {
  77. struct mpu6050_dev *pmydev = container_of((struct delayed_work *)pwk,struct mpu6050_dev,work);
  78. unsigned short ax = 0;
  79. unsigned short ay = 0;
  80. unsigned short az = 0;
  81. unsigned short gx = 0;
  82. unsigned short gy = 0;
  83. unsigned short gz = 0;
  84. unsigned short temp = 0;
  85. ax = mpu6050_read_byte(pmydev->pclient,ACCEL_XOUT_L);
  86. ax = mpu6050_read_byte(pmydev->pclient,ACCEL_XOUT_H) << 8;
  87. input_report_abs(pmydev->pinput,ABS_X,ax); //<-----上报分量
  88. ay = mpu6050_read_byte(pmydev->pclient,ACCEL_YOUT_L);
  89. ay = mpu6050_read_byte(pmydev->pclient,ACCEL_YOUT_H) << 8;
  90. input_report_abs(pmydev->pinput,ABS_Y,ay);
  91. az = mpu6050_read_byte(pmydev->pclient,ACCEL_ZOUT_L);
  92. az = mpu6050_read_byte(pmydev->pclient,ACCEL_ZOUT_H) << 8;
  93. input_report_abs(pmydev->pinput,ABS_Z,az);
  94. gx = mpu6050_read_byte(pmydev->pclient,GYRO_XOUT_L);
  95. gx = mpu6050_read_byte(pmydev->pclient,GYRO_XOUT_H) << 8;
  96. input_report_abs(pmydev->pinput,ABS_RX,gx);
  97. gy = mpu6050_read_byte(pmydev->pclient,GYRO_YOUT_L);
  98. gy = mpu6050_read_byte(pmydev->pclient,GYRO_YOUT_H) << 8;
  99. input_report_abs(pmydev->pinput,ABS_RY,gy);
  100. gz = mpu6050_read_byte(pmydev->pclient,GYRO_ZOUT_L);
  101. gz = mpu6050_read_byte(pmydev->pclient,GYRO_ZOUT_H) << 8;
  102. input_report_abs(pmydev->pinput,ABS_RZ,gz);
  103. temp = mpu6050_read_byte(pmydev->pclient,TEMP_OUT_L);
  104. temp = mpu6050_read_byte(pmydev->pclient,TEMP_OUT_H) << 8;
  105. input_report_abs(pmydev->pinput,ABS_MISC,temp);
  106. input_sync(pmydev->pinput); //<----------绝对坐标真正上报,如角速度加速度无变化核心层会屏蔽掉,除非有变化
  107. schedule_delayed_work(&pgmydev->work,msecs_to_jiffies(1000)); //<---------延时1秒调用
  108. }
  109. void init_mpu6050(struct i2c_client *pclt)
  110. {
  111. mpu6050_write_byte(pclt,PWR_MGMT_1,0x00);
  112. mpu6050_write_byte(pclt,SMPLRT_DIV,0x07);
  113. mpu6050_write_byte(pclt,CONFIG,0x06);
  114. mpu6050_write_byte(pclt,GYRO_CONFIG,0xF8);
  115. mpu6050_write_byte(pclt,ACCEL_CONFIG,0x19);
  116. }
  117. static int mpu6050_probe(struct i2c_client *pclt,const struct i2c_device_id *pid)
  118. {
  119. int ret = 0;
  120. pgmydev = (struct mpu6050_dev *)kmalloc(sizeof(struct mpu6050_dev),GFP_KERNEL);
  121. if(NULL == pgmydev)
  122. {
  123. printk("kmalloc failed\n");
  124. return -1;
  125. }
  126. memset(pgmydev,0,sizeof(struct mpu6050_dev));
  127. pgmydev->pclient = pclt;
  128. init_mpu6050(pgmydev->pclient);
  129. pgmydev->pinput = input_allocate_device(); //<-----------------
  130. set_bit(EV_ABS,pgmydev->pinput->evbit); //<-----------------设置绝对坐标类事件
  131. input_set_abs_params(pgmydev->pinput,ABS_X,-32768,32767,0,0); //<--------32768,32767代表取值范围,0误差范围,0代表不使用平滑参数值
  132. input_set_abs_params(pgmydev->pinput,ABS_Y,-32768,32767,0,0);
  133. input_set_abs_params(pgmydev->pinput,ABS_Z,-32768,32767,0,0);
  134. input_set_abs_params(pgmydev->pinput,ABS_RX,-32768,32767,0,0);
  135. input_set_abs_params(pgmydev->pinput,ABS_RY,-32768,32767,0,0);
  136. input_set_abs_params(pgmydev->pinput,ABS_RZ,-32768,32767,0,0);
  137. input_set_abs_params(pgmydev->pinput,ABS_MISC,-32768,32767,0,0); //温度
  138. ret = input_register_device(pgmydev->pinput); //<-----------------
  139. if(ret)
  140. {
  141. printk("input_register_device failed\n");
  142. input_free_device(pgmydev->pinput);
  143. pgmydev->pinput = NULL;
  144. kfree(pgmydev);
  145. pgmydev = NULL;
  146. return -1;
  147. }
  148. INIT_DELAYED_WORK(&pgmydev->work,mpu6050_work_func); //<-----------------
  149. schedule_delayed_work(&pgmydev->work,msecs_to_jiffies(1000)); //<-----------------1s后函数会被调用
  150. return 0;
  151. }
  152. static int mpu6050_remove(struct i2c_client *pclt)
  153. {
  154. cancel_delayed_work(&pgmydev->work); //<-----------------取消每隔1秒的函数
  155. input_unregister_device(pgmydev->pinput); //<-----------------
  156. input_free_device(pgmydev->pinput);
  157. pgmydev->pinput = NULL;
  158. kfree(pgmydev);
  159. pgmydev = NULL;
  160. return 0;
  161. }
  162. struct of_device_id mpu6050_dt[] =
  163. {
  164. {.compatible = "invensense,mpu6050"},
  165. {}
  166. };
  167. struct i2c_device_id mpu6050_ids[] =
  168. {
  169. {"mpu6050",0},
  170. {}
  171. };
  172. struct i2c_driver mpu6050_driver =
  173. {
  174. .driver = {
  175. .name = "mpu6050",
  176. .owner = THIS_MODULE,
  177. .of_match_table = mpu6050_dt,
  178. },
  179. .probe = mpu6050_probe,
  180. .remove = mpu6050_remove,
  181. .id_table = mpu6050_ids,
  182. };
  183. #if 0
  184. int __init mpu6050_driver_init(void)
  185. {
  186. i2c_add_driver(&mpu6050_driver);
  187. }
  188. void __exit mpu6050_driver_exit(void)
  189. {
  190. i2c_del_driver(&mpu6050_driver);
  191. }
  192. module_init(mpu6050_driver_init);
  193. module_exit(mpu6050_driver_exit);
  194. #else
  195. module_i2c_driver(mpu6050_driver);
  196. #endif
  197. MODULE_LICENSE("GPL");

testmpu6050_input.c

  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <linux/input.h>
  4. #include <fcntl.h>
  5. #include <unistd.h>
  6. #include <stdio.h>
  7. int main(int argc,char *argv[])
  8. {
  9. int fd = -1;
  10. struct input_event evt;
  11. if(argc < 2)
  12. {
  13. printf("Argument is too few\n");
  14. return 1;
  15. }
  16. /*open*/
  17. fd = open(argv[1],O_RDONLY);
  18. if(fd < 0)
  19. {
  20. printf("open %s failed\n",argv[1]);
  21. return 2;
  22. }
  23. /*init mpu6050*/
  24. while(1)
  25. {
  26. read(fd,&evt,sizeof(evt));
  27. if(evt.type == EV_ABS)
  28. {
  29. switch(evt.code)
  30. {
  31. case ABS_X:
  32. printf("Accel-x:%d\n",evt.value);
  33. break;
  34. case ABS_Y:
  35. printf("Accel-y:%d\n",evt.value);
  36. break;
  37. case ABS_Z:
  38. printf("Accel-z:%d\n",evt.value);
  39. break;
  40. case ABS_RX:
  41. printf("Gyro-x:%d\n",evt.value);
  42. break;
  43. case ABS_RY:
  44. printf("Gyro-y:%d\n",evt.value);
  45. break;
  46. case ABS_RZ:
  47. printf("Gyro-z:%d\n",evt.value);
  48. break;
  49. case ABS_MISC:
  50. printf("Temp:%d\n",evt.value);
  51. break;
  52. }
  53. }
  54. }
  55. /*close*/
  56. close(fd);
  57. fd = -1;
  58. return 0;
  59. }

Makefile

  1. ifeq ($(KERNELRELEASE),)
  2. ifeq ($(ARCH),arm)
  3. KERNELDIR ?= /home/linux/Linux_4412/kernel/linux-3.14
  4. ROOTFS ?= /opt/4412/rootfs
  5. else
  6. KERNELDIR ?= /lib/modules/$(shell uname -r)/build
  7. endif
  8. PWD := $(shell pwd)
  9. modules:
  10. $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
  11. modules_install:
  12. $(MAKE) -C $(KERNELDIR) M=$(PWD) modules INSTALL_MOD_PATH=$(ROOTFS) modules_install
  13. clean:
  14. rm -rf *.o *.ko .*.cmd *.mod.* modules.order Module.symvers .tmp_versions
  15. else
  16. CONFIG_MODULE_SIG=n
  17. obj-m += mpu6050_drv_input.o
  18. endif

编译拷贝到rootfs 

验证 

有数值变化才会上传值

拓展:

网络设备、块设备 的开发套路设计思想也类似。

网络设备面向的是协议栈

块设备面向的是文件系统

字符设备面向的是应用层

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/180150
推荐阅读
相关标签
  

闽ICP备14008679号