赞
踩
在我们日常的Linux系统中,存在大量的输入设备,例如按键、鼠标、键盘、触摸屏、摇杆等,他们本身就是字符设备,linux内核将这些字符设备的共同性抽象出来,简化驱动开发建立了一个input子系统。
Linux内核为了两个目的:
简化纯输入类外设(如:键盘、鼠标、游戏杆、轨迹球、触摸屏。。。等等)的驱动开发
统一输入类外设产生的数据格式(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),为事件处理层提供输入事件驱动的接口;通知事件处理层对事件进行处理。
- /*init或probe函数中:
- 1. 创建struct input_dev对象input_allocate_device
- 2. 设置事件类型以及相关参数set_bit
- 3. 注册struct input_dev对象input_register_device
- */
-
- /*exit或remove函数中:
- 1. 注销struct input_dev对象input_unregister_device
- 2. 销毁struct input_dev对象input_free_device
- */
-
- /*上报事件
- 两种事件上报方式:
- 1. 对有中断支持的输入设备:在其中断处理函数(上半部或下半部)中上报事件
- 2. 对无中断支持的输入设备:使用workqueue循环定时上报(struct delayed_work)
- 主要函数:
- input_event //通用上报,上报的是分量,如x轴分量
- input_report_abs //上报绝对坐标
- input_sync //完整数据检查统一上报在这边处理,如x、y、z轴数据统一上报
- */
-
相关接口:
- /*_init*/
- struct input_dev *input_allocate_device(void)//创建对象
-
- void set_bit(struct input_dev *dev,unsigned long whichbits)//设置事件类型
-
- void input_set_abs_params(struct input_dev *dev,unsigned int axis,int min,int max,int fuzz,int flat)
-
- int input_register_device(struct input_dev *dev)//注册input设备到内核
-
- /*_exit*/
- void input_unregister_device(struct input_dev *dev)
- void input_free_device(struct input_dev *dev)
-
- /*上报事件*/
- void input_event(struct input_dev *,unsigned int t,unsigned int c,int v)
-
- void input_report_key(struct input_dev *,unsigned int c,int v) //上报按键事件
- void input_report_abs(struct input_dev *,unsigned int c,int v)//上报绝对坐标事件
-
- void input_sync(struct input_dev *)//上报完成后需要调用这些函数来通知系统处理完整事件
-
- /*应用层数据类型*/
- struct input_event {
- struct timeval time; // 时间戳
- __u16 type; // 事件类型
- __u16 code; // 哪个分值
- __s32 value; // 具体值
- };
借用直接按键驱动的代码,按键中所有根字符设备相关的input子系统已经帮我们实现好了,都可以不需要。
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/fs.h>
- #include <linux/gpio.h>
- #include <linux/interrupt.h>
- #include <linux/of_gpio.h>
- #include <linux/of_irq.h>
- #include <linux/cdev.h>
- #include <linux/wait.h>
- #include <linux/sched.h>
- #include <linux/poll.h>
- #include <linux/mm.h>
- #include <linux/input.h> //<------------
- #include <linux/delay.h>
- #include <linux/slab.h>
- #include <asm/uaccess.h>
-
-
- struct fs4412key2_dev
- {
- struct input_dev *pdev;
-
- int gpio;
- int irqno;
- };
-
- struct fs4412key2_dev *pgmydev = NULL;
-
- irqreturn_t key2_irq_handle(int no,void *arg)
- {
- struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)arg;
- int status1 = 0;
- int status2 = 0;
-
- status1 = gpio_get_value(pmydev->gpio);
- mdelay(1);
- status2 = gpio_get_value(pmydev->gpio);
-
- if(status1 != status2)
- {
- return IRQ_NONE;
- }
-
- if(status1)
- {
- input_event(pmydev->pdev,EV_KEY,KEY_2,0); //<--------------------上报事件,0按下
- input_sync(pmydev->pdev); //往核心层上报
- }
- else
- {
- input_event(pmydev->pdev,EV_KEY,KEY_2,1); //<--------------------上报事件,1抬起
- input_sync(pmydev->pdev); //往核心层上报
- }
-
- return IRQ_HANDLED;
- }
-
- int __init fs4412key2_init(void)
- {
- int ret = 0;
-
- struct device_node *pnode = NULL;
-
- pnode = of_find_node_by_path("/fs4412-key2");
- if(NULL == pnode)
- {
- printk("find node failed\n");
- return -1;
- }
-
-
- pgmydev = (struct fs4412key2_dev *)kmalloc(sizeof(struct fs4412key2_dev),GFP_KERNEL);
- if(NULL == pgmydev)
- {
- printk("kmallc for struct fs4412key2_dev failed\n");
- return -1;
- }
-
- pgmydev->gpio = of_get_named_gpio(pnode,"key2-gpio",0);
-
- pgmydev->irqno = irq_of_parse_and_map(pnode,0);
- //<---------------------
- pgmydev->pdev = input_allocate_device(); //分配pdev空间
-
- set_bit(EV_KEY,pgmydev->pdev->evbit); //设置事件
- set_bit(KEY_2,pgmydev->pdev->keybit); //上报的哪个按键
-
- ret = input_register_device(pgmydev->pdev);//注册到系统
-
- ret = request_irq(pgmydev->irqno,key2_irq_handle,IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,"fs4412key2",pgmydev);
- if(ret)
- {
- printk("request_irq failed\n");
- input_unregister_device(pgmydev->pdev); //反操作
- input_free_device(pgmydev->pdev); //反操作
- kfree(pgmydev);
- pgmydev = NULL;
- return -1;
- }
- return 0;
- }
-
- void __exit fs4412key2_exit(void)
- {
-
- free_irq(pgmydev->irqno,pgmydev);
-
- input_unregister_device(pgmydev->pdev); //反操作
- input_free_device(pgmydev->pdev); //反操作
-
- kfree(pgmydev);
- pgmydev = NULL;
- }
-
-
- MODULE_LICENSE("GPL");
-
- module_init(fs4412key2_init);
- module_exit(fs4412key2_exit);
测试
testkey.2
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <linux/input.h>
- #include <fcntl.h>
- #include <unistd.h>
-
- #include <stdio.h>
-
- int main(int argc,char *argv[])
- {
- int fd = -1;
- struct input_event evt;
- if(argc < 2)
- {
- printf("Argument is too few\n");
- return 1;
- }
-
- /*open*/
- fd = open(argv[1],O_RDONLY);
- if(fd < 0)
- {
- printf("open %s failed\n",argv[1]);
- return 2;
- }
-
- /*init mpu6050*/
-
- while(1)
- {
- read(fd,&evt,sizeof(evt));
- if(evt.type == EV_KEY && evt.code == KEY_2)
- {
- if(evt.value)
- {
- printf("KEY2 DOWN\n");
- }
- else
- {
- printf("KEY2 UP\n");
- }
- }
- }
-
-
- /*close*/
- close(fd);
- fd = -1;
- return 0;
- }
Makefile
- ifeq ($(KERNELRELEASE),)
-
- ifeq ($(ARCH),arm)
- KERNELDIR ?= /home/linux/Linux_4412/kernel/linux-3.14
- ROOTFS ?= /opt/4412/rootfs
- else
- KERNELDIR ?= /lib/modules/$(shell uname -r)/build
- endif
- PWD := $(shell pwd)
-
-
- modules:
- $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
-
- modules_install:
- $(MAKE) -C $(KERNELDIR) M=$(PWD) modules INSTALL_MOD_PATH=$(ROOTFS) modules_install
-
- clean:
- rm -rf *.o *.ko .*.cmd *.mod.* modules.order Module.symvers .tmp_versions
-
- else
-
- CONFIG_MODULE_SIG=n
-
- obj-m += fs4412_key2.o
-
- endif
编译拷贝到跟文件系统测试
mpu6050_drv_input.c
注:绝对坐标的上报事件,如角速度加速度无变化核心层会屏蔽掉,除非有变化
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/fs.h>
- #include <linux/i2c.h>
- #include <linux/cdev.h>
- #include <linux/wait.h>
- #include <linux/sched.h>
- #include <linux/poll.h>
- #include <linux/slab.h>
- #include <linux/mm.h>
- #include <linux/input.h>
- #include <linux/io.h>
- #include <asm/uaccess.h>
- #include <asm/atomic.h>
-
- #define SMPLRT_DIV 0x19
- #define CONFIG 0x1A
- #define GYRO_CONFIG 0x1B
- #define ACCEL_CONFIG 0x1C
-
- #define ACCEL_XOUT_H 0x3B
- #define ACCEL_XOUT_L 0x3C
- #define ACCEL_YOUT_H 0x3D
- #define ACCEL_YOUT_L 0x3E
- #define ACCEL_ZOUT_H 0x3F
- #define ACCEL_ZOUT_L 0x40
- #define TEMP_OUT_H 0x41
- #define TEMP_OUT_L 0x42
- #define GYRO_XOUT_H 0x43
- #define GYRO_XOUT_L 0x44
- #define GYRO_YOUT_H 0x45
- #define GYRO_YOUT_L 0x46
- #define GYRO_ZOUT_H 0x47
- #define GYRO_ZOUT_L 0x48
-
- #define PWR_MGMT_1 0x6B
-
-
- struct mpu6050_dev
- {
- struct input_dev * pinput; //<---------------------
-
- struct i2c_client *pclient;
-
- struct delayed_work work; //可设置时间,到时间work回调函数会被调用
- };
-
- struct mpu6050_dev *pgmydev = NULL;
-
- int mpu6050_read_byte(struct i2c_client *pclt,unsigned char reg)
- {
- int ret = 0;
- char txbuf[1] = {reg};
- char rxbuf[1] = {0};
-
- struct i2c_msg msg[2] =
- {
- {pclt->addr,0,1,txbuf},
- {pclt->addr,I2C_M_RD,1,rxbuf}
- };
-
- ret = i2c_transfer(pclt->adapter,msg,ARRAY_SIZE(msg));
- if(ret < 0)
- {
- printk("ret = %d,in mpu6050_read_byte\n",ret);
- return ret;
- }
-
- return rxbuf[0];
- }
-
-
- int mpu6050_write_byte(struct i2c_client *pclt,unsigned char reg,unsigned char val)
- {
- int ret = 0;
- char txbuf[2] = {reg,val};
-
- struct i2c_msg msg[1] =
- {
- {pclt->addr,0,2,txbuf},
- };
-
- ret = i2c_transfer(pclt->adapter,msg,ARRAY_SIZE(msg));
- if(ret < 0)
- {
- printk("ret = %d,in mpu6050_write_byte\n",ret);
- return ret;
- }
-
- return 0;
- }
-
- void mpu6050_work_func(struct work_struct *pwk)
- {
- struct mpu6050_dev *pmydev = container_of((struct delayed_work *)pwk,struct mpu6050_dev,work);
- unsigned short ax = 0;
- unsigned short ay = 0;
- unsigned short az = 0;
- unsigned short gx = 0;
- unsigned short gy = 0;
- unsigned short gz = 0;
- unsigned short temp = 0;
-
- ax = mpu6050_read_byte(pmydev->pclient,ACCEL_XOUT_L);
- ax = mpu6050_read_byte(pmydev->pclient,ACCEL_XOUT_H) << 8;
- input_report_abs(pmydev->pinput,ABS_X,ax); //<-----上报分量
-
- ay = mpu6050_read_byte(pmydev->pclient,ACCEL_YOUT_L);
- ay = mpu6050_read_byte(pmydev->pclient,ACCEL_YOUT_H) << 8;
- input_report_abs(pmydev->pinput,ABS_Y,ay);
-
- az = mpu6050_read_byte(pmydev->pclient,ACCEL_ZOUT_L);
- az = mpu6050_read_byte(pmydev->pclient,ACCEL_ZOUT_H) << 8;
- input_report_abs(pmydev->pinput,ABS_Z,az);
-
- gx = mpu6050_read_byte(pmydev->pclient,GYRO_XOUT_L);
- gx = mpu6050_read_byte(pmydev->pclient,GYRO_XOUT_H) << 8;
- input_report_abs(pmydev->pinput,ABS_RX,gx);
-
- gy = mpu6050_read_byte(pmydev->pclient,GYRO_YOUT_L);
- gy = mpu6050_read_byte(pmydev->pclient,GYRO_YOUT_H) << 8;
- input_report_abs(pmydev->pinput,ABS_RY,gy);
-
- gz = mpu6050_read_byte(pmydev->pclient,GYRO_ZOUT_L);
- gz = mpu6050_read_byte(pmydev->pclient,GYRO_ZOUT_H) << 8;
- input_report_abs(pmydev->pinput,ABS_RZ,gz);
-
- temp = mpu6050_read_byte(pmydev->pclient,TEMP_OUT_L);
- temp = mpu6050_read_byte(pmydev->pclient,TEMP_OUT_H) << 8;
- input_report_abs(pmydev->pinput,ABS_MISC,temp);
-
- input_sync(pmydev->pinput); //<----------绝对坐标真正上报,如角速度加速度无变化核心层会屏蔽掉,除非有变化
- schedule_delayed_work(&pgmydev->work,msecs_to_jiffies(1000)); //<---------延时1秒调用
- }
-
- void init_mpu6050(struct i2c_client *pclt)
- {
- mpu6050_write_byte(pclt,PWR_MGMT_1,0x00);
- mpu6050_write_byte(pclt,SMPLRT_DIV,0x07);
- mpu6050_write_byte(pclt,CONFIG,0x06);
- mpu6050_write_byte(pclt,GYRO_CONFIG,0xF8);
- mpu6050_write_byte(pclt,ACCEL_CONFIG,0x19);
- }
-
- static int mpu6050_probe(struct i2c_client *pclt,const struct i2c_device_id *pid)
- {
- int ret = 0;
-
- pgmydev = (struct mpu6050_dev *)kmalloc(sizeof(struct mpu6050_dev),GFP_KERNEL);
- if(NULL == pgmydev)
- {
- printk("kmalloc failed\n");
- return -1;
- }
- memset(pgmydev,0,sizeof(struct mpu6050_dev));
-
- pgmydev->pclient = pclt;
-
- init_mpu6050(pgmydev->pclient);
-
- pgmydev->pinput = input_allocate_device(); //<-----------------
-
- set_bit(EV_ABS,pgmydev->pinput->evbit); //<-----------------设置绝对坐标类事件
- input_set_abs_params(pgmydev->pinput,ABS_X,-32768,32767,0,0); //<--------32768,32767代表取值范围,0误差范围,0代表不使用平滑参数值
- input_set_abs_params(pgmydev->pinput,ABS_Y,-32768,32767,0,0);
- input_set_abs_params(pgmydev->pinput,ABS_Z,-32768,32767,0,0);
- input_set_abs_params(pgmydev->pinput,ABS_RX,-32768,32767,0,0);
- input_set_abs_params(pgmydev->pinput,ABS_RY,-32768,32767,0,0);
- input_set_abs_params(pgmydev->pinput,ABS_RZ,-32768,32767,0,0);
- input_set_abs_params(pgmydev->pinput,ABS_MISC,-32768,32767,0,0); //温度
-
- ret = input_register_device(pgmydev->pinput); //<-----------------
-
- if(ret)
- {
- printk("input_register_device failed\n");
-
- input_free_device(pgmydev->pinput);
- pgmydev->pinput = NULL;
-
- kfree(pgmydev);
- pgmydev = NULL;
- return -1;
- }
-
- INIT_DELAYED_WORK(&pgmydev->work,mpu6050_work_func); //<-----------------
-
-
- schedule_delayed_work(&pgmydev->work,msecs_to_jiffies(1000)); //<-----------------1s后函数会被调用
- return 0;
- }
-
- static int mpu6050_remove(struct i2c_client *pclt)
- {
- cancel_delayed_work(&pgmydev->work); //<-----------------取消每隔1秒的函数
-
- input_unregister_device(pgmydev->pinput); //<-----------------
-
- input_free_device(pgmydev->pinput);
- pgmydev->pinput = NULL;
-
- kfree(pgmydev);
- pgmydev = NULL;
-
- return 0;
- }
-
- struct of_device_id mpu6050_dt[] =
- {
- {.compatible = "invensense,mpu6050"},
- {}
- };
-
-
- struct i2c_device_id mpu6050_ids[] =
- {
- {"mpu6050",0},
- {}
- };
-
-
- struct i2c_driver mpu6050_driver =
- {
- .driver = {
- .name = "mpu6050",
- .owner = THIS_MODULE,
- .of_match_table = mpu6050_dt,
- },
- .probe = mpu6050_probe,
- .remove = mpu6050_remove,
- .id_table = mpu6050_ids,
- };
-
- #if 0
- int __init mpu6050_driver_init(void)
- {
- i2c_add_driver(&mpu6050_driver);
- }
-
- void __exit mpu6050_driver_exit(void)
- {
- i2c_del_driver(&mpu6050_driver);
- }
- module_init(mpu6050_driver_init);
- module_exit(mpu6050_driver_exit);
- #else
- module_i2c_driver(mpu6050_driver);
- #endif
-
- MODULE_LICENSE("GPL");
testmpu6050_input.c
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <linux/input.h>
- #include <fcntl.h>
- #include <unistd.h>
-
- #include <stdio.h>
-
- int main(int argc,char *argv[])
- {
- int fd = -1;
- struct input_event evt;
- if(argc < 2)
- {
- printf("Argument is too few\n");
- return 1;
- }
-
- /*open*/
- fd = open(argv[1],O_RDONLY);
- if(fd < 0)
- {
- printf("open %s failed\n",argv[1]);
- return 2;
- }
-
- /*init mpu6050*/
-
- while(1)
- {
- read(fd,&evt,sizeof(evt));
- if(evt.type == EV_ABS)
- {
- switch(evt.code)
- {
- case ABS_X:
- printf("Accel-x:%d\n",evt.value);
- break;
- case ABS_Y:
- printf("Accel-y:%d\n",evt.value);
- break;
- case ABS_Z:
- printf("Accel-z:%d\n",evt.value);
- break;
- case ABS_RX:
- printf("Gyro-x:%d\n",evt.value);
- break;
- case ABS_RY:
- printf("Gyro-y:%d\n",evt.value);
- break;
- case ABS_RZ:
- printf("Gyro-z:%d\n",evt.value);
- break;
- case ABS_MISC:
- printf("Temp:%d\n",evt.value);
- break;
- }
- }
- }
-
-
- /*close*/
- close(fd);
- fd = -1;
- return 0;
- }
Makefile
- ifeq ($(KERNELRELEASE),)
-
- ifeq ($(ARCH),arm)
- KERNELDIR ?= /home/linux/Linux_4412/kernel/linux-3.14
- ROOTFS ?= /opt/4412/rootfs
- else
- KERNELDIR ?= /lib/modules/$(shell uname -r)/build
- endif
- PWD := $(shell pwd)
-
-
- modules:
- $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
-
- modules_install:
- $(MAKE) -C $(KERNELDIR) M=$(PWD) modules INSTALL_MOD_PATH=$(ROOTFS) modules_install
-
- clean:
- rm -rf *.o *.ko .*.cmd *.mod.* modules.order Module.symvers .tmp_versions
-
- else
-
- CONFIG_MODULE_SIG=n
-
- obj-m += mpu6050_drv_input.o
-
- endif
编译拷贝到rootfs
验证
有数值变化才会上传值
拓展:
网络设备、块设备 的开发套路设计思想也类似。
网络设备面向的是协议栈
块设备面向的是文件系统
字符设备面向的是应用层
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。