嵌入式内核及驱动开发之学习笔记(十四) 输入子系统框架使用

举报
王建峰 发表于 2021/11/19 02:57:56 2021/11/19
【摘要】 简单回顾一下之前学习的驱动知识。基于之前的学习,首先实现了字符驱动框架的编写,实现模块化的驱动,简单的加载/卸载去动态执行驱动程序。对于应用层<——>驱动层,编写file_operations 使驱动对上层应用层提供接口,实现功能机制让用户可以通过阻塞、非阻塞、多路复用或者异步通信的方式从驱动读设备;对于驱动层&l...

简单回顾一下之前学习的驱动知识。基于之前的学习,首先实现了字符驱动框架的编写,实现模块化的驱动,简单的加载/卸载去动态执行驱动程序。对于应用层<——>驱动层,编写file_operations 使驱动对上层应用层提供接口,实现功能机制让用户可以通过阻塞、非阻塞、多路复用或者异步通信的方式从驱动读设备;对于驱动层<——>硬件层,通过地址映射机制读写硬件的寄存器,实现对硬件的控制。利用中断实现驱动程序对中断事件的异步处理,引入“中断下半部”的概念来解决中断处理耗时大的问题;最后,通过设备驱动模型,实现了设备、驱动分离的思想,而平台总线模型就是这样的一个机制。

 

言归正传

 

设备驱动模型,将对硬件的描述从原来的驱动代码中抽离出去,构造出一个设备对象。可以让驱动代码去兼容更多的硬件,这样提高了代码的重用率。

目的是用少量的代码,去兼容大量的硬件。

 

模型开发

虽然抛去设备对象,但回顾字符驱动开发流程,驱动代码中仍然有很多相似的代码。精明的程序员前辈们总结出了一种模型框架,这种模型屏蔽了硬件的差异化(差异部分的代码我们自己来实现),对上提供统一的接口,以兼容更多不同的硬件。输入子系统这种模型之一。

 

输入子系统

输入设备

像按键、鼠标、触摸屏、游戏杆这类都是输入设备。如果为多个输入设备设计驱动,为每一个设备定制一个,就意味着你要写很多份驱动代码;如果设计成输入子系统,我们只要实现有部分差异性的代码即可

驱动分层

按照框架模型,再将驱动代码上中下分成三层:


<上>input handler层:数据处理者
<中>input 核心层:管理层         
<下>input device设备层:       

input handler层和input核心层有系统来实现,而编程主要在input device层。

(有待补充!!!!!!!!!!!!!!!!!!!!!!)

 

 

 

主要流程

分配一个input device对象

struct input_dev *input_allocate_device(void)
 

 

初始化input  device对象

进入input_dev对象,可以查看到该对象的很多属性。表示的是一个具体的输入设备,描述设备能够产生什么数据。对输入设备对象的初始化

添加设备信息

在用户层被识别到/sys/class/input/目录下,主要是用来给用户层展示的信息


  
  1. inputdev->name = "simple input key";
  2. inputdev->phys = "key/input/input0";
  3. inputdev->uniq = "simple key0 for 4412";
  4. inputdev->id.bustype = BUS_HOST;
  5. inputdev->id.vendor =0x1234 ;
  6. inputdev->id.product = 0x8888;
  7. inputdev->id.version = 0x0001;

设置位表

inputdev->evbit表示设备产生的数据类型,inputdev->keybit表示要设置按键的键值


  
  1. //当前设备能够产生按键数据--将某个bit置1
  2. __set_bit(EV_KEY, inputdev->evbit);
  3. //表示当前设备能够产生power按键
  4. //__set_bit(KEY_POWER, inputdev->keybit);
  5. //另外一种设置bit的方式
  6. inputdev->keybit[BIT_WORD(KEY_POWER)] |= BIT_MASK(KEY_POWER); // 116%32

 

注册input device对象

int input_register_device(struct input_dev *dev)
 

 

上报数据

将设备产生的数据上报给输入子系统,由系统把数据交给用户。


  
  1. void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
  2. static inline void input_sync(struct input_dev *dev)

 

用户层读取

用户空间读到的数据:统一的数据包

用户调取read函数,以这种数据形式读取数据。


  
  1. struct input_event {
  2.     struct timeval time; //时间戳
  3.     __u16 type; //数据类型
  4.     __u16 code;//具体数据是什么
  5.     __s32 value;//值是是什么
  6. };

 

 

驱动代码实现

当模块加载时,分配一个输入设备对象,然后添加一个按键类型并指定键值为'KEY_POWER'。同时申请一个中断资源,当有按键key2按下时,会产生一个中断信号。在驱动-中断处理中,进行上报数据的动作。


  
  1. //simple_input_drv.c
  2. #include <linux/init.h>
  3. #include <linux/module.h>
  4. #include <linux/input.h>
  5. #include <linux/interrupt.h>
  6. #include <linux/of.h>
  7. #include <linux/of_irq.h>
  8. #include <asm/io.h>
  9. #define GPXCON_REG 0x11000C20
  10. irqreturn_t input_key_irq_handler(int irqno, void *devid); //中断入口
  11. int get_irqno_from_node(void);
  12. struct input_dev *inputdev;
  13. int irqno;
  14. void *reg_base;
  15. static int __init input_drv_init(void)
  16. {
  17. printk("-------%s-------------\n", __FUNCTION__);
  18. int ret;
  19. //分配 input device 对象
  20. inputdev = input_allocate_device();
  21. if(NULL == inputdev)
  22. {
  23. printk(KERN_ERR "input_allocate_device error\n");
  24. return -ENOMEM;
  25. }
  26. //初始化 input device 对象
  27. __set_bit(EV_KEY, inputdev->evbit);
  28. __set_bit(KEY_POWER, inputdev->keybit);
  29. //注册 input device 对象
  30. ret = input_register_device(inputdev);
  31. if(0 != ret)
  32. {
  33. printk(KERN_ERR "input_allocate_device error\n");
  34. goto err_0;
  35. }
  36. //从设备树种拿中断号 -> 申请中断资源
  37. irqno = get_irqno_from_node();
  38. ret = request_irq(irqno, input_key_irq_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
  39. "key3_eint10", NULL);
  40. if(ret != 0)
  41. {
  42. printk("request_irq error\n");
  43. goto err_1;
  44. }
  45. //寄存器地址映射
  46. reg_base = ioremap(GPXCON_REG, 8);
  47. return 0;
  48. err_1:
  49. input_unregister_device(inputdev);
  50. err_0:
  51. input_free_device(inputdev);
  52. return ret;
  53. }
  54. static void __exit input_drv_exit(void)
  55. {
  56. printk("-------%s-------------\n", __FUNCTION__);
  57. iounmap(reg_base); //取消地址映射
  58. free_irq(irqno, NULL); //释放中断资源
  59. input_unregister_device(inputdev); //将input device 对象注销
  60. input_free_device(inputdev); //释放input device 对象
  61. }
  62. irqreturn_t input_key_irq_handler(int irqno, void *devid)
  63. {
  64. int value = 0;
  65. printk("-------%s-------------\n", __FUNCTION__);
  66. //读取数据寄存器
  67. value = readl(reg_base + 4) & (1 << 2);
  68. if(value) {//抬起
  69. //input_event(inputdev, EV_KEY, KEY_POWER, 0);
  70. input_report_key(inputdev, KEY_POWER, 1);
  71. input_sync(inputdev);//上报数据结束
  72. } else {
  73. input_event(inputdev, EV_KEY, KEY_POWER, 1);
  74. input_sync(inputdev);//上报数据结束
  75. }
  76. return IRQ_HANDLED;
  77. }
  78. int get_irqno_from_node(void)
  79. {
  80. //从设备树路径,查找节点
  81. struct device_node *np = of_find_node_by_path("/key_int_node");
  82. if(np){
  83. printk("find node ok\n");
  84. }else{
  85. printk("find node failed\n");
  86. }
  87. int irqno = irq_of_parse_and_map(np, 0);
  88. printk("irqno = %d\n", irqno);
  89. return irqno;
  90. }
  91. module_init(input_drv_init);
  92. module_exit(input_drv_exit);
  93. MODULE_LICENSE("GPL");

 

文章来源: blog.csdn.net,作者:hinzer,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/feit2417/article/details/84437276

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。