主要内容:
1,输入子系统的作用和框架
2,输入子系统的编程方式--学会最简单的输入子系统的开发方式
3,输入子系统和平台总线的结合编程方式---驱动所有按键
4,输入子系统的工作原理和代码分析---学习内核的设计思想
什么是输入设备: 1,按键/keyboard 2, 鼠标 mouse 3, 触摸屏 touchscreen :gt811, ft56xx 4, 游戏杆 joystick 有多个输入设备需要驱动的时候,假如不考虑输入子系统 a, gt811 设备号,创建文件,硬件初始化,实现fop,阻塞 硬件初始化 b, ft56xx 设备号,创建文件,硬件初始化,实现fop,阻塞 硬件初始化 多个输入设备有共同点: 获取到数据(操作硬件),上报给用户(xxx_read, copy_to_user, 阻塞) 差异化 通用 多个输入设备,有部分差异,也有部分通用 内核就会考虑,将通用代码编写好,将差异化的代码留给驱动工程师 设计成输入子系统:使得应用编程人员和驱动编程人员编程的时候变得简单统一 1, 兼容所有的输入设备 2, 统一的编程驱动方法(实现差异化硬件操作) 3, 统一的应用操作接口:/dev/input/event0,event1 open("/dev/input/event0"), read(fd, struct input_event): struct input_event buff可以认为是一个统一的数据包
应用层 --------------------------------- input handler层:数据处理者 完成fop:实现xxx_read(), xxx_open 将数据交给用户:数据从input device层 不知道具体数据是什么,只知道把数据给用户 ---------------------------------------------------------- input 核心层:管理层 ---------------------------------------------------------- input device设备层: 抽象出一个对象,描述输入设备信息 初始化输入设备硬件,获取到数据 知道具体的数据是什么,但是不知道数据如何给用户 --------------------------------- 硬件层:mouse ts, keybaord,joystick 编程: 主要在input device层
前提: input 核心层代码和 input handler 层需要在内核中必须有: drivers/input/evdev.c // event input handler driver/input/input.c // 核心层 linux@ubuntu:~/Level10/day5/linux-5.4.79/drivers/input$ ls apm-power.c input.c input-poller.c Makefile serio evbug.c input-compat.c input-poller.h matrix-keymap.c sparse-keymap.c evdev.c input-compat.h joydev.c misc tablet ff-core.c input-leds.c joystick mouse touchscreen ff-memless.c input-mt.c Kconfig mousedev.c gameport input-polldev.c keyboard rmi4 如果没有,可以使用 make menuconfig 在内核中进行配置 Device Drivers ---> Input device support ---> -*- Generic input layer (needed for keyboard, mouse, ...) // input.c <*> Event interface //input handler层--evdev.c
编写步骤: 1. 分配一个input device对象 2. 初始化input device对象 3. 注册input device对象 上报数据: void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) 参数1:当前input device上报数据 参数2:上报的是哪种数据类型 EV_KEY,EV_ABS 参数3:具体数据是什么 KEY_POWER 参数4:值是什么 用户空间读到的数据: 统一的数据包 struct input_event{ struct timeval time;时间戳 __u16 type; //数据类型 __u16 code;//具体数据是什么 __s32 value;//值是是什么 }
/* 设备树文件: key_int_node{ compatible = "test_key"; interrupt-parent = <&gpx1>; interrupts = <2 0>; }; */ #include <linux/init.h> #include <linux/module.h> #include <linux/input.h> #include <linux/interrupt.h> #include <linux/of.h> #include <linux/of_irq.h> #include <asm/io.h> #define GPX1CON_ADDR 0x11000c20 #define GPX1_SIZE 8 struct input_dev *pInputdev; int irqno; void* reg_virt_base; int get_irqno_from_node(void) { //获取到设备树中的节点 struct device_node *np = of_find_node_by_path("/key_int_node"); if(np){ printk("find node ok\n"); } else{ printk("find node failed\n"); return 0; } //通过节点去获取到中断号码 irqno = irq_of_parse_and_map(np,0); printk("irqno = %d\n",irqno); return irqno; } irqreturn_t input_key_irq_handler(int irqno, void *devid) { unsigned int GPX1DAT; printk("--------------%s------------------\n",__FUNCTION__); GPX1DAT = readl(reg_virt_base + 4); printk("----------GPX1DAT = %d-----------\n",GPX1DAT); if( GPX1DAT &= 0x1<<2 ){//抬起 printk("__KERN__:Enter up\n"); input_event(pInputdev,EV_KEY,KEY_POWER,0); //上报数据 input_sync(pInputdev); //上报数据结束 } else{//按下 printk("__KERN__:Enter down\n"); input_event(pInputdev,EV_KEY,KEY_POWER,1); //上报数据 input_sync(pInputdev); //上报数据结束 } return IRQ_HANDLED; } static int __init simple_input_dev_init(void) { /* 1,分配一个input device对象 2,初始化input device对象 3,注册input device对象 */ int ret ; pInputdev = input_allocate_device(); if(pInputdev == NULL) { printk(KERN_ERR "input_allocate_device error"); return -ENOMEM; } //当前设备能够产生“按键”类型数据 __set_bit(EV_KEY,pInputdev->evbit); //当前设备能够产生power按键值 __set_bit(KEY_POWER,pInputdev->keybit); pInputdev->name = "button_dev";//随便取个名字 不然会报错 ret = input_register_device(pInputdev); if(ret != 0) { printk(KERN_ERR "input_register_device error"); goto err0; } irqno = get_irqno_from_node(); ret = request_irq(irqno, input_key_irq_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "IRQ_EINT10", NULL); if(ret != 0) { printk("request_irq ERROR!\n"); goto err1; } reg_virt_base= ioremap(GPX1CON_ADDR, GPX1_SIZE); return 0; err1: input_unregister_device(pInputdev); err0: input_free_device(pInputdev); return ret; } static void __exit simple_input_dev_exit(void) { free_irq(irqno,NULL); input_unregister_device(pInputdev); input_free_device(pInputdev); } module_init(simple_input_dev_init); module_exit(simple_input_dev_exit); MODULE_LICENSE("GPL");
[root@farsight ]# ls /dev/input/ event0 [root@farsight ]# insmod simple_input_dev.ko [ 51.361454] simple_input_dev: loading out-of-tree module taints kernel. [ 51.367989] input: button_dev as /devices/virtual/input/input1 [root@farsight ]# ls /dev/input/ event0 event1 [root@farsight ]# [ 24.691143] ----------GPX1DAT = 0----------- [ 24.695394] __KERN__:Enter down [ 24.850114] --------------input_key_irq_handler------------------ [ 24.854734] ----------GPX1DAT = 4----------- [ 24.858985] __KERN__:Enter up
相匹配的APP程序代码
#include <stdio.h> #include <stdlib.h> #include <linux/input.h> #include <fcntl.h> struct input_event *pEvent; int main(int argc, char *argv[]) { int fd,ret; fd = open("/dev/input/event1",O_RDWR); // 地址以及设备文件名需要按实际情况修改 if(fd < 0) { perror("open"); exit(1); } while(1){ ret = read(fd, (void*)pEvent, sizeof(struct input_event)); if(pEvent->type == EV_KEY) //如果产生的是“按键”类型数据 if(pEvent->code == KEY_POWER) //如果按键值是POWER if(pEvent->value) //如果传输的数据大于0 { printf("____APP____:Key up\n"); } else { printf("____APP____:Key down\n"); } } close(fd); return 0; }
效果展示:
[ 5238.782829] --------------input_key_irq_handler------------------ [ 5238.787451] ----------GPX1DAT = 0----------- [ 5238.791703] __KERN__:Enter down [ 5239.004857] --------------input_key_irq_handler------------------ [ 5239.009476] ----------GPX1DAT = 4----------- [ 5239.013728] __KERN__:Enter up [ 5242.390972] --------------input_key_irq_handler------------------ [ 5242.395588] ----------GPX1DAT = 0----------- [ 5242.399839] __KERN__:Enter down [ 5242.609469] --------------input_key_irq_handler------------------ [ 5242.614089] ----------GPX1DAT = 4----------- [ 5242.618341] __KERN__:Enter up