1@@linux内核input子系统解析2
Input子系统架构包括内核层与框架层详解

第1章Android Input子系统架构1.1Input服务的启动在Android的开机过程中,系统中的服务很多都是由SystemServer中启动的。
SystemServer的代码中有这么一句话。
Framework/base/services/java/com/android/server/SystemServer.javaSlog.i(TAG, "Window Manager");wm = WindowManagerService.main(context, power,factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL,!firstBoot);在这里new了一个WindowManagerService的类,我们找到这个类的构造函数。
Framework/base/services/java/com/android/server/wm/WindowManagerServer.javaprivate WindowManagerService(Context context, PowerManagerService pm,boolean haveInputMethods, boolean showBootMsgs) {……mInputManager = new InputManager(context, this);……mInputManager.start();……}在WindowManagerService的构造函数中又new了一个InputManager类。
InputManager类是整个android的input的上层代码最重要的类,就是通过这个类繁衍出了整个复杂的Android 的input子系统。
作用就好像Zygote的孕育着Android的各个服务,而InputManager就是负责将整个android的Input子系统。
Linux内核Input子系统初始化驱动架构

Input子系统初始化驱动架构目标:分析input子系统初始化驱动架构本文要点:1.input子系统重要数据结构及关系图2.input子系统初始化驱动架构input子系统结构图:Input子系统构建过程结构关系图注1:开发人员在初始化设备时,需初始化相应的输入设备类型evbit[],按键值keybit[],相对位移relbit[]等。
如:evbit[0] = BIT(EV_KEY),表示此设备支持按键;keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0),表示支持的上报值BTN_0;relbit[0] = BIT(REL_X) | BIT(REL_Y),用于表示相对坐标值,如鼠标的位移。
主要数据结构间的关系:struct input_id {__u16 bustype; //总线类型,如#define BUS_HOST 0x19__u16 vendor; //制造商__u16 product; //产品id__u16 version; //版本};struct timer_list {……void (*function)(unsigned long);unsigned long data;};#define EV_MAX 0x1f#define EV_CNT (EV_MAX+1)#define KEY_MAX 0x2ff#define KEY_CNT (KEY_MAX+1)#define ABS_MAX 0x3f#define REP_MAX 0x01#define BITS_PER_BYTE 8#define DIV_ROUND_UP(x,y) (((x) + ((y) - 1)) / (y))#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))struct input_dev { //input子系统中唯一一个需要驱动开发人员填充的数据结构……struct input_id id;//和具体的handler匹配时,会用到unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; //需要开发人员填充unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; //需要开发人员填充//timer.data = (long) dev//timer.function = input_repeat_key;struct timer_list timer;//用于处理按键按下的时候重复上报键值int abs[ABS_MAX + 1];int absmin[ABS_MAX + 1];//#define REP_DELAY 0x00 ;rep[REP_DELAY] = 250int rep[REP_MAX + 1];// #define REP_PERIOD 0x01;rep[REP_PERIOD] = 33;struct list_head h_list;//list_add_tail_rcu(&handle->d_node, & dev->h_list);struct list_head node;//把自己链入到input_dev_list};static struct input_handler *input_table[8];struct input_device_id {kernel_ulong_t flags; //标志信息__u16 bustype;__u16 vendor;__u16 product;__u16 version;kernel_ulong_t evbit[INPUT_DEVICE_ID_EV_MAX / BITS_PER_LONG + 1];kernel_ulong_t keybit[INPUT_DEVICE_ID_KEY_MAX / BITS_PER_LONG + 1];kernel_ulong_t relbit[INPUT_DEVICE_ID_REL_MAX / BITS_PER_LONG + 1];kernel_ulong_t absbit[INPUT_DEVICE_ID_ABS_MAX / BITS_PER_LONG + 1];kernel_ulong_t mscbit[INPUT_DEVICE_ID_MSC_MAX / BITS_PER_LONG + 1];kernel_ulong_t ledbit[INPUT_DEVICE_ID_LED_MAX / BITS_PER_LONG + 1];kernel_ulong_t sndbit[INPUT_DEVICE_ID_SND_MAX / BITS_PER_LONG + 1];kernel_ulong_t ffbit[INPUT_DEVICE_ID_FF_MAX / BITS_PER_LONG + 1];kernel_ulong_t swbit[INPUT_DEVICE_ID_SW_MAX / BITS_PER_LONG + 1];kernel_ulong_t driver_info;};struct input_handler {……const struct file_operations *fops;int minor;//input_table[handler->minor >> 5] = handler;//用于判断此handler可以匹配哪些input_devconst struct input_device_id *id_table;const struct input_device_id *blacklist;//黑名单struct list_head h_list;// 指向和input_dev匹配成功后生成的对应的handle struct list_head node;// 把自己链入到input_handler_list};#define EVDEV_MINORS 32static struct evdev *evdev_table[EVDEV_MINORS];static char *input_devnode(struct device *dev, mode_t *mode) {return kasprintf(GFP_KERNEL, "input/%s", dev_name(dev)); }struct class input_class = {.name = "input",.devnode = input_devnode,};struct evdev {……int minor;struct input_handle handle;//非常重要的结构体,详见后续分析struct list_head client_list;//指向open设备时,生成的client(client在//上报按键值时,会用到)struct device dev;};struct input_handle {……struct input_dev *dev;struct input_handler *handler;//指向自己的handlerstruct list_head d_node;//把自己加入到匹配成功的input_dev-> h_liststruct list_head h_node;// 把自己加入到匹配成功的input_handler-> h_liststruct input_event {struct timeval time;//时间戳__u16 type; //上报事件的类型,将和dev->evbit里的相应位比较,//若相同,则表示支持这类事件__u16 code; //上报事件的值__s32 value; //说明按键时按下还是释放,0表示释放,非0表示按下};struct evdev_client {//上报的按键值、类型等,最终将封装成input_event,赋给buffer[]中的某//一项.#define EVDEV_BUFFER_SIZE 64struct input_event buffer[EVDEV_BUFFER_SIZE];struct evdev *evdev;//指向自己的evdevstruct list_head node;//加入到对应的evdev->client_list……};input子系统总体设计思想:一、input子系统初始化:static char *input_devnode(struct device *dev, mode_t *mode){return kasprintf(GFP_KERNEL, "input/%s", dev_name(dev));}struct class input_class = {.name = "input",.devnode = input_devnode,};static const struct file_operations input_fops = {.owner = THIS_MODULE,.open = input_open_file,};static int input_open_file(struct inode *inode, struct file *file) {……struct input_handler *handler;handler = input_table[iminor(inode) >> 5];new_fops = fops_get(handler->fops);// evdev_handler->fopsfile->f_op = new_fops;new_fops->open(inode, file); //即int evdev_open(struct inode *inode, struct file *file) }-------------------------------------------------------------------------------------------- static unsigned int input_abs_bypass_init_data[]__initdata = {ABS_MT_TOUCH_MAJOR, //接触面的长轴ABS_MT_TOUCH_MINOR, //接触面的短轴,如果是圆形接触面,这个参数可以省略。
关于input子系统的详细介绍嵌入式硬件工程师必看

关于input子系统的详细介绍嵌入式硬件工程师必看Input子系统是Linu某内核中的一个子系统,它用于处理与输入设备相关的硬件驱动程序和事件处理器。
在嵌入式设备中,Input子系统主要负责处理与触摸屏幕、键盘、鼠标等有关的输入设备。
Input子系统可以接收从输入设备产生的事件,例如一个触摸屏幕的触摸手势,通过Device Tree机制将其传递给用户空间,从而使应用程序能够响应这些事件。
一个完整的Input子系统通常包含三部分:输入设备驱动程序、事件处理器和设备文件。
其中,输入设备驱动程序负责与硬件进行交互,从硬件中读取输入数据;事件处理器负责解析输入数据,并产生和分发输入事件;设备文件是用户空间程序通过读写接口来与Input子系统进行通信。
在输入设备驱动程序中,一般需要完成以下功能:1.设备初始化:根据硬件特性来初始化模块。
在触摸屏幕中,可能需要配置硬件的灵敏度、分辨率、采样方式等。
2. 注册设备:与Kernel中的Input子系统进行通信,并在其中注册自己的设备。
这个步骤通常用来将硬件设备与Input子系统的驱动程序进行匹配。
3. 数据收集:在输入设备驱动程序中需要进行数据的收集和转换,将设备产生的原始数据转换成Linu某中统一的格式。
例如,在触摸屏幕的驱动程序中可能需要进行坐标的转换。
事件处理器即是处理硬件输入事件的机制,对于用户空间而言,它主要提供了两个接口:poll和读文件。
我们通过文件描述符打开设备文件,并使用poll或读文件的方式来读取输入事件,让应用程序能够响应事件。
设备文件一般是通过sysfs file system进行构建的。
在这种方式下,设备驱动程序将其硬件设备与输入子系统相关联时,会在sysfs file system中创建对应的节点,从而使输入设备在用户空间能够被访问到。
总的来说,Input子系统在嵌入式设备中是一个非常重要但又容易被忽视的子系统。
它将硬件设备和用户空间进行了紧密的连接,使得用户能够通过相对简单的操作响应输入事件。
input子系统2

input子系统输入设备(如按键,键盘,触摸屏,鼠标等)是典型的字符设备,其一般的工作机制是低层在按键,触摸等动作发生时产生一个中断(或驱动通过timer定时查询),然后cpu通过SPI,I2C或者外部存储器总线读取键值,坐标等数据,放一个缓冲区,字符设备驱动管理该缓冲区,而驱动的read()接口让用户可以读取键值,坐标等数据。
在Linux中,输入子系统是由输入子系统设备驱动层、输入子系统核心层(Input Core)和输入子系统事件处理层(Event Handler)组成。
其中设备驱动层提供对硬件各寄存器的读写访问和将底层硬件对用户输入访问的响应转换为标准的输入事件,再通过核心层提交给事件处理层;而核心层对下提供了设备驱动层的编程接口,对上又提供了事件处理层的编程接口;而事件处理层就为我们用户空间的应用程序提供了统一访问设备的接口和驱动层提交来的事件处理。
所以这使得我们输入设备的驱动部分不在用关心对设备文件的操作,而是要关心对各硬件寄存器的操作和提交的输入事件。
input子系统结构图input子系统设备驱动实现Input设备用input_dev结构体描述,定义在input.h中1 在驱动模块加载函数中设置Input设备支持input子系统的哪些事件;2 将Input设备注册到input子系统中;3 在Input设备发生输入操作时(如:键盘被按下/抬起、触摸屏被触摸/抬起/移动、鼠标被移动/单击/抬起时等),提交所发生的事件及对应的键值/坐标等状态。
相关的API函数所有的输入设备都是用input_dev类型来表示,和最早的cdev字符类型不同input设备的初始化函数,返回一个struct input_dev类型的指针struct input_dev *input_allocate_device()input_dev中有两个重要的成员evbit keybitevbit它表示响应的事件类型,而具体到这个设备所能产生或接受的的事件类型在input.h中定义EV_RST 0x00 ResetEV_KEY 0x01 按键(键盘或按钮)EV_REL 0x02 相对坐标(鼠标)EV_ABS 0x03 绝对坐标(操作杆或书写板)EV_MSC 0x04 其它EV_LED 0x11 LED或其他指示设备EV_SND 0x12 声音(如蜂鸣器)EV_REP 0x14 RepeatEV_FF 0x15 力反馈keybit 表示支持的按键类型,总结来说,evbit表示支持什么设备,如键盘,鼠标,或者触摸屏,keybit表示支持那个按键,如key1 key2等,设置者两个成员是用set_bit函数如set_bit(EV_KEY,button_dev->evbit)set_bit(KEY_1, button_dev >keybit);注册输入设备函数,input_register_device(struct input_dev *dev)以上为输入型设备的初始化工作.当接收到按键或者其他输入硬件的变化,我们应该向input core报告.void input_report_key(struct input_dev *dev,unsigned int code,int value);//报告键值参数1 input_dev结构体参数2 那个按键KEY_1 参数value 0或者非0,一般来说非0表示按下0表示释放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);//报告绝对坐标在触摸屏驱动设计中,一次坐标及按下状态的整个报告过程如下: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);//同步该函数表示发送一条完整的报告.如上面的例子,三条是一个完整的报告.一般对触摸屏或者是鼠标是非常重要的.最后释放input_devvoid input_free_device(struct input_dev *dev);void input_unregister_device(struct input_dev *);通过上面的介绍,可以完成input类型驱动的编写,驱动程序编写后,下面解决测试程序问题,在input子系统中,最终所有的输入信息都会汇总到/dev/input/eventX这个文件中,X是大于0的数所以我们只需读取驱动所对应的文件就可以获取相应的input信息,如果确定该读取那一个event?读取/proc/bus/input/device可以查看设备的详细信息已获得驱动所对应的event文件如下代码,可以读取event文件,读取的长度是一个input_enent类型结构体长度key_fd = open("/dev/event0", O_RDWR)read(key_fd,&event_buf,sizeof(struct input_event));//该结构体,对应在驱动层报告的信息struct input_event {struct timeval time; //时间__u16 type;__u16 code;__s32 value;};。
linux内核-input子系统解析

linux内核input子系统解析华清远见刘洪涛Android、X windows、qt等众多应用对于linux系统中键盘、鼠标、触摸屏等输入设备的支持都通过、或越来越倾向于标准的input输入子系统。
因为input子系统已经完成了字符驱动的文件操作接口,所以编写驱动的核心工作是完成input系统留出的接口,工作量不大。
但如果你想更灵活的应用它,就需要好好的分析下input子系统了。
一、input输入子系统框架下图是input输入子系统框架,输入子系统由输入子系统核心层(Input Core ),驱动层和事件处理层(Event Handler)三部份组成。
一个输入事件,如鼠标移动,键盘按键按下,joystick的移动等等通过input driver -> Input core -> Event handler -> userspace 到达用户空间传给应用程序。
注意:keyboard.c不会在/dev/input下产生节点,而是作为ttyn终端(不包括串口终端)的输入。
二、Input driver编写要点1、分配、注册、注销input设备struct input_dev *input_allocate_device(void)int input_register_device(struct input_dev *dev)void input_unregister_device(struct input_dev *dev)2、设置input设备支持的事件类型、事件码、事件值的范围、input_id等信息参见usb键盘驱动:usbkbd.cusb_to_input_id(dev, &input_dev->id);//设置bustype、vendo、product等input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP);//支持的事件类型input_dev->ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL) | BIT(LED_COMPOSE) | BIT(LED_KANA);// EV_LED事件支持的事件码for (i = 0; i < 255; i++)set_bit(usb_kbd_keycode[i], input_dev->keybit); //EV_KEY事件支持的事件码include/linux/input.h中定义了支持的类型(下面列出的是2.6.22内核的情况)#define EV_SYN 0x00#define EV_KEY 0x01#define EV_REL 0x02#define EV_ABS 0x03#define EV_MSC 0x04#define EV_SW 0x05#define EV_LED 0x11#define EV_SND 0x12#define EV_REP 0x14#define EV_FF 0x15#define EV_PWR 0x16#define EV_FF_STATUS 0x17#define EV_MAX 0x1f一个设备可以支持一个或多个事件类型。
关于Linux设备驱动中input子系统的介绍

关于Linux设备驱动中input子系统的介绍
对于输入类设备如键盘、鼠标、触摸屏之类的Linux驱动,内核提供input子系统,使得这类设备的处理变得非常便捷。
总体上来讲,input子系统由三部分组成:事件驱动《》input核心《》设备驱动。
其中事件驱动负责与用户程序打交道,诸如设备节点/dev之类的,都由他负责,我们在写驱动时就不用实现这个了;设备驱动负责与硬件设备打交道,这里的交互很简单,只需要读取相关硬件的数据,然后抛给input核心就可以了;
举个例子,以按键key为例,定义了设备设备号、按键值,配置管脚和中断方式,然后申请中断。
在中断服务函数中,读取对应管脚值,用input_report函数发送给input核心,并用input_sync通知发送结束即可。
另外,在模块初始化时,定义一个input_dev的结构体,这个input_dev是input 子系统设备驱动端的核心数据结构,由于输入设备多种多样,就是通过这个结构体告诉核心你的输入设备类型。
其中的两个重要成员,这些宏具体在linux/input.h中定义。
linux系统的内核子系统之间的关系

linux系统的内核子系统之间的关系Linux系统的内核子系统之间的关系Linux操作系统的内核是其最核心的组成部分,它负责管理和控制整个系统的运行。
内核由多个子系统组成,每个子系统负责不同的功能模块,它们之间相互配合,共同完成系统的各项任务。
本文将介绍几个常见的内核子系统及其之间的关系。
1. 文件系统子系统文件系统子系统负责管理文件和目录的存储和访问。
它提供了对文件系统的抽象,使用户和应用程序可以通过文件路径来访问文件和目录。
文件系统子系统由虚拟文件系统层、各种具体的文件系统类型和存储设备驱动程序组成。
虚拟文件系统层提供了一个统一的接口,使不同的文件系统可以以相同的方式进行访问。
具体的文件系统类型如ext4、NTFS等负责实现不同的文件系统格式,而存储设备驱动程序则负责控制硬盘、闪存等存储设备的读写。
2. 进程管理子系统进程管理子系统负责管理系统中的进程。
它负责创建、终止和调度进程,并提供进程间通信和同步的机制。
进程管理子系统包括进程调度器、进程控制块、进程间通信和同步机制等。
进程调度器决定了系统中运行哪些进程以及它们的优先级和时间片分配。
进程控制块保存了进程的状态信息,包括程序计数器、寄存器和运行时堆栈等。
进程间通信和同步机制如管道、信号量、消息队列等,使不同进程之间可以进行数据交换和协调工作。
3. 设备驱动子系统设备驱动子系统负责管理和控制硬件设备的访问。
它提供了对设备的抽象接口,使应用程序可以通过统一的方式访问不同类型的设备。
设备驱动子系统包括字符设备驱动和块设备驱动。
字符设备驱动用于管理字符设备,如串口、键盘等,它提供了以字节为单位的读写接口。
块设备驱动用于管理块设备,如硬盘、闪存等,它提供了以块为单位的读写接口。
设备驱动子系统还包括中断处理、DMA控制等功能,用于处理设备的中断请求和数据传输。
4. 网络子系统网络子系统负责管理和控制系统的网络功能。
它提供了网络协议栈、网络接口和网络设备驱动等功能。
Linuxinput子系统(2)

Linuxinput子系统(2)尽管原文写于2003,仍有参考价值。
<!-- @page { size: 8.5in 11in; margin: 0.79in } P { margin-bottom: 0.08in } H4 { margin-bottom: 0.08in } TD P { margin-bottom: 0in } -->Using the Input Subsystempart 2Linux input子系统一个很重要的特性是它提供了 event interface。
它通过字符设备节点对用户空间导出了原生 event,允许用户程序操作任何 event,不会遗失任何信息。
查找 event interface版本使用 EVIOCGVERSION ioctl function。
参数是 32位 int类型,代表 major version (two high bytes), minor version (third byte), patch level (low byte)。
Listing 1显示了使用EVIOCGVERSION的例子:第 1个参数是 event device node的打开文件描述符。
你需要传递一个指向 int数据的一个指针作为第 3个参数。
Listing 1. Sample EVIOCGVERSION Function/* ioctl() accesses the underlying driver */if (ioctl(fd, EVIOCGVERSION, &version)) {perror("evdev ioctl");}/* the EVIOCGVERSION ioctl() returns an int *//* so we unpack it and display it */printf("evdev driver version is %d.%d.%d/n",version >> 16, (version >> 8) & 0xff,version & 0xff);查找设备身份信息event interface支持获取设备的身份信息,使用 EVIOCGID ioctl function。
Linuxinput子系统实例分析(一)

Linuxinput⼦系统实例分析(⼀)这是⼀个简单的输⼊设备驱动实例。
这个输⼊设备只有⼀个按键,按键被连接到⼀条中断线上,当按键被按下时,将产⽣⼀个中断,内核将检测到这个中断,并对其进⾏处理。
该实例的代码如下:1: #include <linux/module.h>2: #include <linux/init.h>3: #include <linux/fs.h>4: #include <linux/interrupt.h>5: #include <linux/irq.h>6: #include <linux/sched.h>7: #include <linux/spinlock.h>8: #include <linux/pm.h>9: #include <linux/slab.h>10: #include <linux/sysctl.h>11: #include <linux/proc_fs.h>12: #include <linux/delay.h>13: #include <linux/platform_device.h>14: #include <linux/input.h>15: #include <linux/workqueue.h>16: #include <linux/gpio.h>17:18:19: #define gpio_key 32*4+30 //PD(30) 即将使⽤的gpio20: #define DEV_NAME "gpio_key"21:22: int g_irq = -1; //中断号23: static struct input_dev *button_dev; //输⼊⼦系统设备结构24:25:26: //中断处理函数27: static irqreturn_t button_interrupt(int irq, void *p)28: {29: /*get pin value <down 0, up 1> */30:31: int val = gpio_get_value(gpio_key);32:33: input_report_key(button_dev, KEY_1, val);34:35: input_sync(button_dev);36:37: return IRQ_RETVAL(IRQ_HANDLED);38: }39:40:41:42: static int __init button_init(void)44: int irq = -1, err = -1;45: unsigned long irqflags;46: //申请gpio47: err = gpio_request(gpio_key, "test_key");48: if(err < 0){49: printk("request gpio[%d] failed...\n", gpio_key);50: goto end1;51: }52:53: //gpio输⼊54: err = gpio_direction_input(gpio_key);55: if (err < 0) {56: //dev_err(dev, "failed to configure"57: // " direction for GPIO %d, error %d\n",58: // gpio_key, error);59: goto end2;60: }61: //申请gpio中断号62: g_irq = (irq = gpio_to_irq(gpio_key));63: if (irq < 0) {64: err = irq;65: //dev_err(dev, "Unable to get irq number for GPIO %d, error %d\n", 66: // gpio_key, irq);67: goto end2;68: }69: //中断类型70: irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;71: /* 申请中断 */72: if (request_irq(irq, button_interrupt, irqflags, DEV_NAME, NULL)) {73:74: printk(KERN_ERR"cannotallocate irq");75: err= -EBUSY;76: goto end2;77: }78:79: /*分配input_dev */80: button_dev = input_allocate_device();81: if (button_dev == NULL) {82: printk(KERN_ERR "notenough memory\n");83: err= - ENOMEM;84: goto end3;85:86: }87: /*设置输⼊设备⽀持的事件类型和事件代码 */88: button_dev->name = "key_gpio";89: set_bit(EV_KEY, button_dev->evbit);90: set_bit(KEY_1, button_dev->keybit);92: /*把输⼊设备注册进核⼼层 */93: err = input_register_device(button_dev);94: if(err) {95: printk(KERN_ERR "failedto register device\n"); 96: goto end4;97: }98:99: printk("initialized\n");100: return 0;101:102: end4:103: input_free_device(button_dev);104: end3:105: free_irq(irq, NULL);106: end2:107: gpio_free(gpio_key);108: end1:109: return err;110:111: }112:113:114:115: static void __exit button_exit(void)116: {117: input_unregister_device(button_dev);118: input_free_device(button_dev);119:120: gpio_free(gpio_key);121: free_irq(g_irq, NULL);122: }123:124:125:126: module_init(button_init);127: module_exit(button_exit);128:129: MODULE_LICENSE("GPL");130: MODULE_AUTHOR("xuyonghong@>"); 131:132:133:134:135:136:当编译进内核烧写板⼦后可以看到相应的设备⽂件:root@CarRadio:/sys/devices# ls virtual/input/input2/ capabilities id name power subsystem uniq event2 modalias phys properties ueventroot@CarRadio:/sys/devices# cat virtual/input/input2/name key_gpio#这样就可以监控event2来捕捉按键root@CarRadio:/# ls dev/input/event2dev/input/event2root@CarRadio:/#驱动分析:1.申请gpiogpio_request(gpio_key, "test_key");2.设置为gpio输⼊模式gpio_direction_input(gpio_key);3.申请gpio中断号,注册中断//申请gpio中断号 g_irq = (irq = gpio_to_irq(gpio_key));/* 申请中断 */request_irq(irq, button_interrupt, irqflags, DEV_NAME, NULL);4.分配input_dev设备/*分配input_dev */button_dev = input_allocate_device();5.把输⼊设备注册进核⼼层input_register_device(button_dev);。
kernel下的uinput机制_示例及概述说明

kernel下的uinput机制示例及概述说明1. 引言1.1 概述在计算机领域中,uinput(user input)机制是一种通过模拟输入设备来实现对系统的控制和操作的方法。
它在kernel中被广泛应用,可以创建虚拟的输入设备,并向系统注入用户自定义的输入事件。
通过uinput机制,我们可以模拟鼠标、键盘、触摸屏等各种输入设备的行为,使得用户或开发者能够以编程的方式进行各种操作和测试。
1.2 文章结构本文将就kernel下的uinput机制进行详细介绍与示例演示。
首先,在"2. uinput机制的原理与用途"部分中,我们将解释什么是uinput机制以及它在系统中起到的作用,进而探讨其背后的实现原理和所涉及到的应用领域。
接着,在"3. 示例:通过uinput模拟输入设备的创建和操作"部分中,我们将会给出具体示例,演示如何使用uinput机制来创建虚拟输入设备,并对其进行模拟操作。
随后,在"4. kernel中uinput机制的相关配置和API接口介绍"部分中,我们将详细介绍如何配置kernel以支持uinput机制,并提供相关API接口的介绍和使用示例。
最后,在"5. 结论与展望"部分中,我们将对uinput机制进行总结回顾,并探讨目前存在的问题以及未来的发展趋势。
1.3 目的本文旨在全面介绍kernel下的uinput机制,帮助读者了解其原理和用途,并提供具体示例和配置指南,使得读者能够深入理解并应用该机制,从而满足各种自动化测试、模拟操作等需求。
同时,对于开发者来说,该文章也提供了一个参考和指导,以便他们在实际项目中更好地利用uinput机制来实现相关功能。
2. uinput机制的原理与用途2.1 什么是uinput机制uinput(User Input)机制是Linux内核中的一个子系统,用于模拟和生成输入事件。
详细了解Linux设备模型中的input子系统

详细了解Linux设备模型中的input子系统
本节从整体上讲解了输入子系统的框架结构。
有助于读者从整体上认识linux 的输入子系统。
在陷入代码分析的过程中,通过本节的知识能够找准方向,明白原理。
本节重点:
输入子系统的框架结构
各层对应内核中的文件位置
输入子系统的事件处理机制
输入子系统的驱动层基本操作流程
输入子系统的驱动层常用函数
本节难点:
输入子系统的事件处理机制
输入子系统的驱动工作流程
1 初识linux输入子系统
linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(EventHandler)、输入子系统核心层(InputCore)和输入子系统设备驱动层。
对于输入子系统设备驱动层而言,主要实现对硬件设备的读写访问,中断设置,并把硬件产生的事件转换为核心层定义的规范提交给事件处理层。
对于核心层而言,为设备驱动层提供了规范和接口。
设备驱动层只要关心如何驱动硬件并获得硬件数据(例如按下的按键数据),然后调用核心层提供的接口,核心层会自动把数据提交给事件处理层。
对于事件处理层而言,则是用户编程的接口(设备节点),并处理驱动层提交的数据处理。
对于linux输入子系统的框架结构如下图1所示:
图1 linux输入子系统框架结构
由上图所展现的内容就是linux输入子系统的分层结构。
Input子系统详解

Input子系统详解一.Input子系统架构Linux系统提供了input子系统,按键、触摸屏、键盘、鼠标等输入都可以利用input接口函数来实现设备驱动,下面是Input子系统架构:Input子系统架构二.Input系统的组成输入子系统由驱动层(Drivers),输入子系统核心层(Input Core )和事件处理层(Event Handler)三部份组成。
一个输入事件,如鼠标移动,键盘按键按下等都是通过Driver -> InputCore -> Eventhandler -> userspace 的顺序到达用户空间传给应用程序。
下面介绍各部分的功能:(1)驱动层功能:负责和底层的硬件设备打交道,将底层硬件设备对用户输入的响应转换为标准的输入事件以后再向上发送给输入子系统核心层(Input Core)。
(2)Input系统核心层:Input Core即Input Layer,由driver/input/input.c及相关头文件实现,它对下提供了设备驱动层的接口,对上提供了事件处理层(Event Handler)的编程接口。
(3)事件处理层将硬件设备上报的事件分发到用户空间和内核。
三.Input设备驱动编写在Linux内核中,input设备用input_dev结构体描述,使用input子系统实现输入设备驱动的时候,驱动的核心工作是向系统报告按键、触摸屏、键盘、鼠标等输入事件(event,通过input_event结构体描述),不再需要关心文件操作接口,因为input子系统已经完成了文件操作接口。
驱动报告的事件经过InputCore和Eventhandler最终到达用户空间。
下面给出一个使用input子系统的例子,通过这个例子来解析input子系统的方方面面。
(1)键盘驱动static void button_interrupt(int irq, void *dummy, struct pt_regs *fp){input_report_key(&button_dev, BTN_1, inb(BUTTON_PORT) & 1);input_sync(&button_dev);}static int __init button_init(void){if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL)) {printk(KERN_ERR "button.c: Can''t allocate irq %d\n", button_irq);return -EBUSY;}button_dev.evbit[0] = BIT(EV_KEY);button_dev.keybit[LONG(BTN_0)] = BIT(BTN_0);input_register_device(&button_dev);}static void __exit button_exit(void){input_unregister_device(&button_dev);free_irq(BUTTON_IRQ, button_interrupt);}module_init(button_init);module_exit(button_exit);这是个最简单使用input子系统的例子,权且引出这input子系统,这个驱动中主要涉及input子系统的函数下面一一列出,后面会有详细的介绍:1)set_bit(EV_KEY, button_dev.evbit);set_bit(BTN_0, button_dev.keybit);分别用来设置设备所产生的事件以及上报的按键值。
Linux设备模型之input子系统详解2

Linux设备模型之input子系统详解 2 Linux设备模型之input子系统详解2如果input device被强制指定了handler,则调用该handler的event函数.结合handle注册的分析.我们知道.会将handle挂到input device的h_list链表上.如果没有为input device强制指定handler.就会遍历input device-h_list上的handle成员.如果该handle被打开,则调用与输入设备对应的handler的event()函数.注意,只有在handle被打开的情况下才会接收到事件.另外,输入设备的handler强制设置一般是用带EVIOCGRAB标志的ioctl如下是发图的方示总结evnet的处理过程:我们已经分析了input 来完成的.device,handler和handle的注册过程以及事件的上报和处理.下面以evdev为实例做分析.来贯穿理解一下整个过程.七:evdev概述Evdev对应的设备节点一般位于/dev/input/event0~/dev/input/event4.理论上可以对应32个设备节点.分别代表被handler匹配的32个input device.可以用cat/dev/input/event0.然后移动鼠标或者键盘按键就会有数据输出(两者之间只能选一.因为一个设备文件只能关能一个输入设备).还可以往这个文件里写数据,使其产生特定的事件.这个过程我们之后再详细分析.为了分析这一过程,必须从input子系统的初始化说起.八:input子系统的初始化Input子系统的初始化函数为input_init().代码如下:static int __init input_init(void){interr;err=class_register(&input_class);if(err){printk(KERN_ERR"input:unabl e to register input_dev class\n");returnerr;}err=input_proc_init();if(err)goto fail1;err=register_chrdev(INPUT_MAJOR,"input",&input_fops);if(err){printk(KERN_ERR"input:unable to register charmajor%d",INPUT_MAJOR);goto fail2;}return 0;fail2:input_proc_exit();fail1:class_unregister(&input_class);return err;}在这个初始化函数里,先注册了一个名为"input"的类.所有input device都属于这个类.在sysfs中表现就是.所有input device所代表的目录都位于/dev/class/input下面.然后调用input_proc_init()在/proc下面建立相关的交互文件.再后调用register_chrdev()注册了主设备号为INPUT_MAJOR(13).次设备号为0~255的字符设备.它的操作指针为input_fops.在这里,我们看到.所有主设备号13的字符设备的操作最终都会转入到input_fops中.在前面分析的/dev/input/event0~/dev/input/event4的主设备号为13.操作也不例外的落在了input_fops中.Input_fops定义如下:static const struct file_operationsinput_fops={.owner=THIS_MODULE,.open=input_open_file,};打开文件所对应的操作函数为input_open_file.代码如下示:static intinput_open_file(struct inode*inode,struct file*file){structinput_handler*handler=input_table[iminor(inode)5];const structfile_operations*old_fops,*new_fops=NULL;int err;/*No load-on-demand here?*/if(~handler||~(new_fops=fops_get(handler-fops)))return- iminor(inode)为打开文件所对应的次设备号.input_table是一个ENODEV;struct input_handler全局数组.在这里.它先设备结点的次设备号右移5位做为索引值到input_table中取对应项.从这里我们也可以看到.一个handle代表1 5个设备节点(因为在input_table中取值是以次备号右移5位为索引的.即低5位相同的次备号对应的是同一个索引).在这里,终于看到了input_talbe大显身手的地方了.input_talbe[]中取值和input_talbe[]的赋值,这两个过程是相对应的.在input_table中找到对应的handler之后,就会检验这个handle是否存,是否带有fops文件操作集.如果没有.则返回一个设备不存在的错误./**That's _really_ ually NULL-open means"nothingspecial",*not"no device".Oh,well.*/if(~new_fops-open){fops_put(new_fops);return-ENODEV;}old_fops=file-f_op;file-f_op=new_fops;err=new_fops-open(inode,file);if(err){fops_put(file-f_op);file-f_op=fops_get(old_fops);}fops_put(old_fops);return err;}然后将handler中的fops替换掉当前的fops.如果新的fops中有open()函数,则调用它.九:evdev的初始化Evdev的模块初始化函数为evdev_init().代码如下:static int __init evdev_init(void){returninput_register_handler(&evdev_handler);}它调用了input_register_handler注册了一个handler.注意到,在这里evdev_handler中定义的minor为EVDEV_MINOR_BASE(64).也就是说evdev_handler所表示的设备文件范围为(13,64)à(13,64+32).从之前的分析我们知道.匹配成功的关键在于handler中的blacklist和id_talbe.Evdev_handler的id_table定义如下:static const struct input_device_idevdev_ids={{.driver_info=1},/*Matches all devices*/{},/*Terminating zero entry*/};它没有定义flags.也没有定义匹配属性值.这个handler是匹配所有input device的.从前面的分析我们知道.匹配成功之后会调用handler-connect函数.在Evdev_handler中,该成员函数如下所示:static int evdev_connect(struct input_handler*handler,structinput_dev*dev,const struct input_device_id*id){structevdev*evdev;int minor;int error;for(minor=0;minorEVDEV_MINORS;minor++)if(~evdev_table[minor])break;if(minor==EVDEV_MINORS){printk(KERN_ERR"evdev:no more free evdev devices\n");return-ENFILE;}EVDEV_MINORS定义为32.表示evdev_handler所表示的32个设备文件.evdev_talbe是一个struct evdev类型的数在接下来的代码中我们可以看到这个组.struct evdev是模块使用的封装结构.结构的使用.这一段代码的在evdev_talbe找到为空的那一项.minor就是数组中第一项为空的序号.evdev=kzalloc(sizeof(struct evdev),GFP_KERNEL);if(~evdev)return-ENOMEM;INIT_LIST_HEAD(&evdev-client_list);spin_lock_init(&evdev-client_lock);mutex_init(&evdev-mutex);init_waitqueue_head(&evdev-wait);snprintf(evdev-name,sizeof(evdev-name),"event%d",minor);evdev-exist=1;evdev-minor=minor;evdev-handle.dev=input_get_device(dev);=evdev-name;evdev-handle.handler=handler;evdev-handle.private=evdev;接下来,分配了一个evdev结构,并对这个结构进行初始化.在这里我们可以看到,这个结构封装了一个handle结构,这结构与我们之前所讨论的handler是不相同的.注意有一个字母的差别哦.我们可以把handle看成是handler和input device的信息集合体.在这个结构里集合了匹配成功的handler和input device strlcpy(evdev-dev.bus_id,evdev-name,sizeof(evdev-dev.bus_id));evdev-dev.devt=MKDEV(INPUT_MAJOR,EVDEV_MINOR_BASE+minor);evdev-dev.class=&input_class;evdev-dev.parent=&dev-dev;evdev-dev.release=evdev_free;device_initialize(&evdev-dev);在这段代码里主要完成evdev封装的device的初始化.注意在这里,使它所属的类指向input_class.这样在/sysfs中创建的设备目录就会在/sys/class/input/下面显示.error=input_register_handle(&evdev-handle);if(error)goto err_free_evdev;error=evdev_install_chrdev(evdev);if(error)gotoerr_unregister_handle;error=device_add(&evdev-dev);if(error)gotoerr_cleanup_evdev;return 0;err_cleanup_evdev:evdev_cleanup(evdev);;err_unregister_handle:input_unregister_handle(&evdev-handle)err_free_evdev:put_device(&evdev-dev);return error;}注册handle,如果是成功的,那么调用evdev_install_chrdev将evdev_table的minor项指向evdev.然后将evdev-device注册到sysfs.如果失败,将进行相关的错误处理.万事俱备了,但是要接收事件,还得要等"东风".这个"东风"就是要打开相应的handle.这个打开过程是在文件的open()中完成的.十:evdev设备结点的open()操作我们知道.对主设备号为INPUT_MAJOR的设备节点进行操作,会将操作集转换成handler的操作集.在evdev中,这个操作集就是evdev_fops.对应的open函数如下示:static int evdev_open(struct inode*inode,struct struct evdev_client*client;int file*file){struct evdev*evdev;i=iminor(inode)-EVDEV_MINOR_BASE;int error;if(i=EVDEV_MINORS)return-ENODEV;error=mutex_lock_interruptible(&evdev_table_mutex);if(error)return error;evdev=evdev_table[i];if(evdev)get_device(&evdev-dev);mutex_unlock(&evdev_table_mutex);if(~evdev)return-ENODEV;client=kzalloc(sizeof(struct evdev_client),GFP_KERNEL);if(~client){error=-ENOMEM;goto err_put_evdev;}spin_lock_init(&client-buffer_lock);client-evdev=evdev;evdev_attach_client(evdev,client);error=evdev_open_device(ev dev);if(error)goto err_free_client;file-private_data=client;return0;err_free_client:evdev_detach_client(evdev,client);kfree(client);err_put_evdev:put_de vice(&evdev-dev);return error;}iminor(inode)-EVDEV_MINOR_BASE 就得到了在evdev_table[]中的序号.然后将数组中对应的evdev取出.递增devdev中device的引用计数.分配并初始化一个client.并将它和evdev关联起来:client-evdev指向它所表示的evdev.将client挂到evdev-client_list上.将client赋为file的私有区.对应handle的打开是在此evdev_open_device()中完成的.代码如下:static intevdev_open_device(struct evdev*evdev){int retval;retval=mutex_lock_interruptible(&evdev-mutex);if(retval)returnretval;if(~evdev-exist)retval=-ENODEV;else if(~evdev-open++){retval=input_open_device(&evdev-handle);if(retval)evdev-open--;}mutex_unlock(&evdev-mutex);return retval;}如果evdev是第一次打开,就会调用input_open_device()打开evdev对应的handle.跟踪一下这个函数:int input_open_device(struct input_handle*handle){structinput_dev*dev=handle-dev;int retval;retval=mutex_lock_interruptible(&dev-mutex);if(retval)returnretval;if(dev-going_away){retval=-ENODEV;goto out;}handle-open++;if(~dev-users++&&dev-open)retval=dev-open(dev);if(retval){dev-users--;if(~--handle-open){/**Make sure we are not delivering any moreevents*through this handle*/synchronize_rcu();}}out:mutex);return retval;}在这个函数中,我们看到.递增mutex_unlock(&dev- handle的打开计数.如果是第一次打开.则调用input device的open()函数.十一:evdev的事件处理经过上面的分析.每当input device上报一个事件时,会将其交给和它匹配的handler的event函数处理.在evdev中.这个event函数对应的代码为:static void evdev_event(structinput_handle*handle,unsigned int type,unsigned int code,int value){struct evdev*evdev=handle-private;structevdev_client*client;struct input_eventevent;do_gettimeofday(&event.time);event.type=type;event.code=code;event.value=value;rcu_read_lock();cl ient=rcu_dereference(evdev-grab);if(client)evdev_pass_event(client,&event);elselist_for_each_entry_rcu(client,&evdev-client_list,node)evdev_pass_event(client,&event);rcu_read_unlock();wake_ up_interruptible(&evdev-wait);}首先构造一个struct input_event结构.并设备它的type.code,value为处理事件的相关属性.如果该设备被强制设置了handle.则调用如之对应的client.我们在open的时候分析到.会初始化clinet并将其链入到evdev-client_list.这样,就可以通过evdev- client_list找到这个client了.对于找到的第一个client都会调用evdev_pass_event().代码如下:static void evdev_pass_event(struct evdev_client*client,struct input_event*event){/**Interrupts are disabled,just acquire the lock*/spin_lock(&client-buffer_lock);client-buffer[client-head++]=*event;client-head&=EVDEV_BUFFER_SIZE-1;spin_unlock(&client-buffer_lock);kill_fasync(&client-fasync,SIGIO,POLL_IN);}这里的操作很简单.就是将event保存到client- buffer中.而client-head就是当前的数据位置.注意这里是一个环形缓存区.写数据是从client-head写.而读数据则是从client-tail中读.十二:设备节点的read处理对于evdev设备节点的read操作都会由evdev_read()完成.它的代码如下:static ssize_t evdev_read(struct file*file,char__user*buffer,size_t count,loff_t*ppos){struct evdev_client*client=file-private_data;struct evdev*evdev=client-evdev;struct input_eventevent;int retval;if(countevdev_event_size())return-EINVAL;if(client-head==client-tail&&evdev- f_flags&O_NONBLOCK))return-EAGAIN;exist&&(file-retval=wait_event_interruptible(evdev-wait,client-head~=client-tail||~evdev-exist);if(retval)return retval;if(~evdev-exist)return-ENODEV;while(retval+evdev_event_size()=count&&evdev_fetch_next_event(client, &event)){if(evdev_event_to_user(buffer+retval,&event))return-EFAULT;retval+=evdev_event_size();}return retval;}首先,它判断缓存区大小是否足够.在读取数据的情况下,可能当前缓存区内没有数据可读.在这里先睡眠等待缓存区中有数据.如果在睡眠的时候,.条件满足.是不会进行睡眠状态而直接返回的.然后根据read()提够的缓存区大小.将client中的数据写入到用户空间的缓存区中.十三:设备节点的写操作同样.对设备节点的写操作是由evdev_write()完成的.代码如下:static ssize_t evdev_write(structfile*file,const char __user*buffer,size_t count,loff_t*ppos){structevdev_client*client=file-private_data;struct evdev*evdev=client-evdev;struct input_event event;int retval;retval=mutex_lock_interruptible(&evdev-mutex);if(retval)return retval;if(~evdev-exist){retval=-ENODEV;goto out;}while(retvalcount){if(evdev_event_from_user(buffer+retval,&event)){retval=-EFAULT;goto out;}input_inject_event(&evdev-handle,event.type,event.code,event.value);retval+=evdev_event_size();}out:mutex_unlock(&evdev-mutex);return retval;}首先取得操作设备文件所对应的evdev.实际上,这里写入设备文件的是一个event结构的数组.我们在之前分析过,这个结构里包含了事件的type.code 和event.将写入设备的event数组取出.然后对每一项调用event_inject_event().这个函数的操作和input_event()差不多.就是将第一个参数handle转换为输入设备结构.然后这个设备再产生一个事件.代码如下:voidinput_inject_event(struct input_handle*handle,unsigned int type,unsigned int code,int value){struct input_dev*dev=handle-dev;struct input_handle*grab;unsigned long flags;if(is_event_supported(type,dev-evbit,EV_MAX)){spin_lock_irqsave(&dev-event_lock,flags);rcu_read_lock();grab=rcu_dereference(dev-grab);if(~grab||grab==handle)input_handle_event(dev,type,code,value);spin_unlock_irqrestore(&dev-event_lock,flags);}}rcu_read_unlock();我们在这里也可以跟input_event()对比一下,这里设备可以产生任意事件,而不需要和设备所支持的事件类型相匹配.由此可见.对于写操作而言.就是让与设备文件相关的输入设备产生一个特定的事件.将上述设备文件的操作过程以图的方式表示如下:十四:小结在这一节点,分析了整个input子系统的架构,各个环节的流程.最后还以evdev为例.将各个流程贯穿在一起.以加深对input子系统的理解.由此也可以看出.linux设备驱动采用了分层的模式.从最下层的设备模型到设备,驱动,总线再到input子系统最后到input device.这样的分层结构使得最上层的驱动不必关心下层是怎么实现的.而下层驱动又为多种型号同样功能的驱动提供了一个统一的接口.第一个图分析错了,应该是handler-connect-input_register_handle()想请问下,这里的read过程.它是睡眠等待的,如果两个进程同时等一个设备.那唤醒时根据社么来区别数据给哪个进程呢?."两个进程等一个设备".不可能出现这样的情况哈.因为每一次open都会对应evdev_table[]中一项.也就是说多次open不会对应到evdev_table的相同项.如果evdev_table[]占满了,evdev_handler和其它的input_device关联就会失败.不好意思,上面那段话我说错了.i`m so sorry~如果多个进程打开同一个evdev文件,那么都会找到相同的evdev.然后每个进程都会生成evdev_client.存放在evdev-client_list.在handle接收到一个事件的时候,都会把事件copy到挂在evdev-clinet_list 上的evdev_client的buffer中.这样,所有打开同一个设备的进程都会收到这个消息而唤醒.你可以在你pc上测试一下.另外,当过程调用EVIOCGRAB的ioctl,就会本进程的evdev_clinet和evdev"绑定"起来.这样,当handle有事件的时候,只有这个绑定进程才会被唤醒,其它过程一直睡眠.我的意思是这样:所有打开同一个设备的进程都会收到这个消息而唤醒.一开始想这样可能会出问题.比如等来的是别的进程需要的数据,而不是自己需要的.后来一想这些都给调用者自己判断不就得了.上午有点犯傻了博主有联系方式不.欢迎交流,Email:这里还有个问题不明白哈:"这里的操作很简单.就是将event保存到client-buffer中.而client-head 就是当前的数据位置.注意这里是一个环形缓存区.写数据是从client-head写.而读数据则是从client-tail中读."这个缓冲区满了的处理还不是很明确,看代码没明白则么样个处理法.比如open 了,但却一直不去read.缓冲区肯定会满的当iput device和input handler的id成员在evbit,keybit,…swbit项相同才会匹配成功。
linux input用法

linux input用法摘要:一、Linux input 用法简介1.Linux input 命令的作用2.input 命令的常用选项和参数二、Linux input 命令的具体用法1.查询输入设备信息2.设置输入设备属性3.读取输入设备事件4.控制输入设备行为三、Linux input 命令的实际应用1.键盘事件监听2.鼠标事件监听3.触摸屏事件监听四、Linux input 命令的高级应用1.输入设备的高级属性设置2.输入设备的高级事件读取3.输入设备的自动化控制正文:Linux input 命令是Linux 系统中用于查询和控制输入设备的命令,它可以获取输入设备的信息,设置输入设备的属性,读取输入设备的事件,以及控制输入设备的行为。
通过使用input 命令,用户可以更加灵活地管理和控制输入设备,以满足各种不同的应用需求。
一、Linux input 用法简介Linux input 命令主要用于查询和控制输入设备,包括键盘、鼠标、触摸屏等。
用户可以通过input 命令的选项和参数,对输入设备进行各种操作。
二、Linux input 命令的具体用法1.查询输入设备信息使用input 命令的-d 选项,可以查询系统中所有的输入设备,包括设备名称、类型、驱动等信息。
例如:```input -d```1.设置输入设备属性使用input 命令的-s 选项,可以设置输入设备的属性,例如:```input -s <设备名称> event=<事件类型>```1.读取输入设备事件使用input 命令的-e 选项,可以读取输入设备的事件,例如:```input -e <设备名称>```1.控制输入设备行为使用input 命令的-c 选项,可以控制输入设备的行为,例如:```input -c <设备名称> <命令>```三、Linux input 命令的实际应用1.键盘事件监听使用input 命令可以监听键盘事件,例如:```input -l```可以实时显示按键事件。
Linux input 子系统范例和基本函数解析

Linux input 子系统范例和基本函数解析输入子系统是为了将输入设备的功能呈现给应用程序。
它支持鼠标、键盘、蜂鸣器、触摸屏、传感器等需要不断上报数据的设备。
简单的例子这个例子中的设备只有一个按键key,当key按下时,将产生中断,内核检测到中断并对其进行处理。
#include #include static struct input_dev *button_dev; /*输入设备结构体*/ /*中断处理函数*/ staTIc irqreturn_t button_interrupt(int irq, void *dummy) { /*向输入子系统报告产生按键事件*/ input_report_key(button_dev, BTN_0, inb(BUTTON_PORT) /*通知接收者,一个报告发送完毕*/ input_sync(button_dev); return IRQ_HANDLED; //?} /*加载函数*/ staTIc int __init button_init(void){ int error; /*申请中断处理函数*/ //返回0表示成功,返回-INV AL表示无效if(request_irq(BUTTON_IRQ,button_interrupt,0,"button",NULL)) { /*申请失败,则打印出错信息*/ printk(KERN_ERR "button.c: Cant allocate irq %d\n", button_irq); return -EBUSY; } /*分配一个设备结构体*/ //将在sys/class/input/input-n 下面创建设备属性文件button_dev = input_allocate_device(); if (!button_dev) /*判断分配是否成功*/ { printk(KERN_ERR "button.c: Not enough memory\n"); error = -ENOMEM; goto err_free_irq; } button_dev->evbit[0] = BIT_MASK(EV_KEY); /*设置按键信息*/ button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0); error = input_register_device(button_dev); /*注册一个输入设备*/ if (error) { printk(KERN_ERR "button.c: Failed to register device\n"); goto err_free_dev; } return 0; err_free_dev: /*以下是错误处理*/ input_free_device(button_dev); err_free_irq: free_irq(BUTTON_IRQ,。
详细解读Linux的输入子系统

详细解读Linux的输入子系统
1. 简介
这时一组驱动的集合,它们可以用于所有的基于linux的输入设备,虽然目前它只是用于USB输入设备,将来(2.5、2.6版本)它们将会被扩展并替换掉现存的多数输入系统,这就是为什么它们被放在drivers/input/目录下,而不是drivers/usb/。
输入设备驱动的核心是input模块,它需要在其他输入模块之前被加载--它是输入系统其它两个模块之间通讯的桥梁:
1.1 设备驱动(Device drivers)
这些模块负责和实际的硬件打交道(例如通过USB),给input模块提供相应的事件(按键,鼠标移动)
1.2 事件处理模块(Event handlers)
这些模块从input模块获得事件信息,并根据需要通过不同的接口传递这些事件--往kernel 传递按键事件,或者通过模拟的PS/2接口给GPM和X传递鼠标移动事件,等等。
2. 一个简单的例子
通常,对于大多数配置来说,系统有一个USB鼠标和一个USB键盘,你需要加载以下几个模块(或者把它们编译到内核中):
input
mousedev
keybdev
usbcore
uhci_hcdor ohci_hcd or ehci_hcd
usbhid
在这之后,USB键盘直接就可以工作了,USB鼠标会作为一个字符设备,主次设备号分别为13和63:
crw-r--r-- 1 root root 13, 63 Mar 28 22:45 mice
你需要主动创建该鼠标设备节点,创建的命令如下:
cd/dev。
Linux中Input输入子系统分析

Linux中Input输入子系统分析
王闯
【期刊名称】《电脑开发与应用》
【年(卷),期】2012(025)007
【摘要】详细分析了linux系统中input输人子系统的实现代码,从input driver 层、input core层、event handler层3个方面介绍了input输入子系统的实现框架及接口类型,对主要数据结构struct input_ dev、struct input_handle、struct input _ handler、struct input_event、input_register_handle和重要函数input_allocate device、input_register_device、
input_register_handle.input_register handler以及sys文件接口实现进行详细说明.
【总页数】3页(P70-72)
【作者】王闯
【作者单位】华为技术有限公司,广东深圳518102
【正文语种】中文
【中图分类】TP311
【相关文献】
1.Linux输入子系统的分析与应用 [J], 刘超;左登超;刘建华;左登峰
2.Linux输入子系统在触摸屏驱动上的实现 [J], 於琪建;张海峰
3.基于输入子系统的Linux触摸屏驱动的实现 [J], 王鑫;孔勇
4.基于Linux内核输入子系统的驱动研究 [J], 刘少平
5.基于触摸屏驱动的Linux内核输入子系统研究 [J], 华明;徐造林
因版权原因,仅展示原文概要,查看原文内容请购买。
Linux 下的 Input 子系统

Linux 下的 Input 子系统朱银瑞;吴庆洪;吴华玲【期刊名称】《计算机系统应用》【年(卷),期】2013(000)012【摘要】In Linux system the Input sub-system is character driven system. Now android, X-windows, Qt, and many other applications in Linux system for input devices such as keyboard, mouse, touch screen support all through, or more and more inclined to standard Input subsystem. Based on current conditions, this article first from Input_dev layer, Input Core layer and the Event Handler layer introduced the implementation of Input sub-system framework, and then achieve it through the Input sub-system for 4x4 matrix keypad and the user in the application layer use the interface function provided by Input sub-system to operate the buttons in order to view the specific states and values of keys. The results shows that the keypad driver in Input sub-system are more stable and have a high efficiency and so it has a good common use.%Input 子系统属于 Linux 系统下字符类驱动系统,现在 Android、X-windows、Qt 等众多应用于 Linux 系统中键盘、鼠标、触摸屏等输入设备的支持都通过、或越来越多倾向于标准的 Input 子系统。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
input_handler 2、input_handler 结构体 以 evdev.c 中的 evdev_handler 为例: static struct input_handler evdev_handler = {
.event = evdev_event, //向系统报告 input 事件,系统通过 read 方法读取 .connect = evdev_connect, //和 input_dev 匹配后调用 connect 构建 .disconnect = evdev_disconnect, .fops = &evdev_fops, //event 设备文件的操作方法 .minor = EVDEV_MINOR_BASE, //次设备号基准值 .name = "evdev", .id_table = evdev_ids, //匹配规则 }; input 3、input 字符设备注册过程 drivers/input/input.c 中: static int __init input_init(void) { int err; err = class_register(&input_class); …… err = register_chrdev(INPUT_MAJOR, "input", &input_fops); …… } input_fops 定义: static const struct file_operations input_fops = { .owner = THIS_MODULE, .open = input_open_file, }; Input_dev 和 input_handler 匹配后调用 input_handler 的 connect。以 evdev_handler 为例: static int evdev_connect(struct input_handler *handler, struct input_dev *dev,const struct input_device_id *id) { struct evdev *evdev; struct class_device *cdev; dev_t devt; int minor; int error; for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++); if (minor == EVDEV_MINORS) { printk(KERN_ERR "evdev: no more free evdev devices\n"); return -ENFILE; } evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);//为每个匹配 evdev_handler 的设备
注意:keyboard.c 不会在/dev/input 下产生节点,而是作为 ttyn 终端(不包括串口终端)的输入。 Input 二、Input driver 编写要点 1、分配、注册、注销 input 设备 struct input_dev *input_allocate_device(void) int input_register_device(struct input_dev *dev) void input_unregister_device(struct input_dev *dev) input_id 2、设置 input 设备支持的事件类型、事件码、事件值的范围、input_id 等信息 参见 usb 键盘驱动:usbkbd.c usb_to_input_id(dev, &input_dev->id);//设置 bustype、vendo、product 等 input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP);//支持的事件类型 input_dev->ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL) | BIT(LED_COMPOSE) | BIT(LED_KANA);// EV_LED 事件支持的事件码 for (i = 0; i < 255; i++) set_bit(usb_kbd_keycode[i], input_dev->keybit); //EV_KEY 事件支持的事件码
linux 内核 input 子系统解析
作者:刘洪涛,华清远见嵌入式学院讲师。 Android、X windows、qt 等众多应用对于 linux 系统中键盘、鼠标、触摸屏等输入设备的支持都通过、 或越来越倾向于标准的 input 输入子系统。 因为 input 子系统已经完成了字符驱动的文件操作接口,所以编写驱动的核心工作是完成 input 系统 留出的接口,工作量不大。但如果你想更灵活的应用它,就需要好好的分析下 input 子系统了。 input 一、input 输入子系统框架 下图是 input 输入子系统框架,输入子系统由输入子系统核心层( Input Core ) ,驱动层和事件处理 层( Event Handler 三部份组成。 ) 一个输入事件, 如鼠标移动, 键盘按键按下, joystick 的移动等等通过 input driver -> Input core -> Event handler -> userspace 到达用户空间传给应用程序。
一个设备可以支持一个或多个事件类型。每个事件类型下面还需要设置具体的触发事件码。比如: EV_KEY 事件,需要定义其支持哪些按键事件码。 3、如果需要,设置 input 设备的打开、关闭、写入数据时的处理方法 参见 usb 键盘驱动:usbkbd.c input_dev->open = usb_kbd_open; input_dev->close = usb_kbd_close; input_dev->event = usb_kbd_event; 4、在发生输入事件时,向子系统报告事件 用于报告 EV_KEY、EV_REL、EV_ABS 等事件的函数有: 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) 如果你觉得麻烦,你也可以只记住1个函数(因为上述函数都是通过它实现的) void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) Event 三、Event Handler 层解析 Input 1、Input 输入子系统数据结构关系图
include/linux/input.h 中定义了支持的类型(下面列出的是2.6.22内核的情况) #define EV_SYN #define EV_KEY #define EV_REL #define EV_ABS #define EV_MSC #define EV_SW #define EV_LED #define EV_SND #define EV_REP #define EV_FF #define EV_PWR #define EV_FF_STATUS #define EV_MAX 0x1f 0x00 0x01 0x02 0x03 0x04 0x05 0x11 0x12 0x1e->f_op = fops_get(old_fops); } fops_put(old_fops); return err; } input 5、input 字符设备的其它操作 由于在 open 阶段已经把设备文件的操作操作方法重定位了到了具体的 input_handler, 所以其它接口 操作(read、write、ioctl 等) ,由各个 input_handler 的 fops 方法决定。如 evdev.c 中的:evdev_fops。
创建一个 evdev。 if (!evdev) return -ENOMEM; INIT_LIST_HEAD(&evdev->client_list); init_waitqueue_head(&evdev->wait); evdev->exist = 1; evdev->minor = minor; evdev->handle.dev = dev; evdev-> = evdev->name; evdev->handle.handler = handler; evdev->handle.private = evdev; sprintf(evdev->name, "event%d", minor); evdev_table[minor] = evdev;//记录 evdev 的位置, 字符设备/dev/input/evnetx 访问时根据次 设备号及 EVDEV_MINOR_BASE 最终在 evdev_open 中找到对应的 evdev devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor), cdev = class_device_create(&input_class, &dev->cdev, devt,dev->cdev.dev, evdev->name);// evdev->name);//创建了 event 字符设备节点 …… } input 4、input 字符设备的打开过程 static int input_open_file(struct inode *inode, struct file *file) { struct input_handler *handler = input_table[iminor(inode) >> 5]; //得到对应的 input_handler const struct file_operations *old_fops, *new_fops = NULL; int err; if (!handler || !(new_fops = fops_get(handler->fops))) //取出对应 input_handler 的 file_operations return -ENODEV; if (!new_fops->open) { fops_put(new_fops); return -ENODEV; } old_fops = file->f_op; file->f_op = new_fops;//重定位打开的设备文件的操作方法 err = new_fops->open(inode, file); if (err) { fops_put(file->f_op);