Linux输入子系统是一组驱动程序,它支持Linux系统上的所有输入设备,包括键盘、鼠标、触摸屏、手写板、游戏手柄等。输入子系统的核心是input模块,它负责在两类模块之间传递事件:
在本文中,我们将介绍Linux输入子系统的基本概念和结构,以及一些常用的命令和工具。我们将使用Ubuntu 20.04作为示例系统,但是这些内容也适用于其他Linux发行版。
驱动层
将底层的硬件输入转化为统一事件形式,想输入核心(Input Core)汇报。
输入子系统核心层
它承上启下为驱动层提供输入设备注册与操作接口,如:input_register_device;通知事件处理层对事件进行处理;在/Proc下产生相应的设备信息。
事件处理层
主要是和用户空间交互(Linux中在用户空间将所有的设备都当作文件来处理,由于在一般的驱动程序中都有提供fops接口,以及在/dev下生成相应的设备文件nod,这些操作在输入子系统中由事件处理层完成)。
设备描述
input_dev结构是实现设备驱动核心工作:向系统报告按键、触摸屏等输入事件(event,通过input_event结构描述),不再需要关心文件操作接口。驱动报告事件经过inputCore和Eventhandler到达用户空间。
注册输入设备函数:
int input_register_device(struct input_dev *dev)
注销输入设备函数:
void input_unregister_device(struct input_dev *dev)
驱动实现——初始化(事件支持)set_bit()告诉input输入子系统支持哪些事件,哪些按键。例如:
set_bit(EV_KEY,button_dev.evbit) (其中button_dev是struct input_dev类型)
struct input_dev****中有两个成员为:
**1)**evbit事件类型(包括EV_RST,EV_REL,EV_MSC,EV_KEY,EV_ABS,EV_REP等)。
**2)**keybit按键类型(当事件类型为EV_KEY时包括BTN_LEFT,BTN_0,BTN_1,BTN_MIDDLE等)。
驱动实现——报告事件用于报告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)
驱动实现——报告结束input_sync()同步用于告诉input core子系统报告结束,触摸屏设备驱动中,一次点击的整个报告过程如下:
input_reprot_abs(input_dev,ABS_X,x); //x坐标 input_reprot_abs(input_dev,ABS_Y,y); // y坐标 input_reprot_abs(input_dev,ABS_PRESSURE,1); input_sync(input_dev);//同步结束
实例分析(按键中断程序):
//按键初始化 static int __init button_init(void) {//申请中断 if(request_irq(BUTTON_IRQ,button_interrupt,0,”button”,NUll)) return –EBUSY; set_bit(EV_KEY,button_dev.evbit); //支持EV_KEY事件 set_bit(BTN_0,button_dev.keybit); //支持设备两个键 set_bit(BTN_1,button_dev.keybit); // input_register_device(&button_dev);//注册input设备 } /*在按键中断中报告事件*/ Static void button_interrupt(int irq,void *dummy,struct pt_regs *fp) { input_report_key(&button_dev,BTN_0,inb(BUTTON_PORT0));//读取寄存器BUTTON_PORT0的值 input_report_key(&button_dev,BTN_1,inb(BUTTON_PORT1)); input_sync(&button_dev); }
总结:input子系统仍然是字符设备驱动程序,但是代码量减少很多,****input子系统只需要完成两个工作:初始化和事件报告(这里在linux****中是通过中断来实现的)。
实例
#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct input_dev *button_dev; struct button_irq_desc { int irq; int pin; int pin_setting; int number; char *name; }; /*定义一个结构体数组*/ static struct button_irq_desc button_irqs [] = { {IRQ_EINT8 , S3C2410_GPG0 , S3C2410_GPG0_EINT8 , 0, "KEY0"}, {IRQ_EINT11, S3C2410_GPG3 , S3C2410_GPG3_EINT11 , 1, "KEY1"}, {IRQ_EINT13, S3C2410_GPG5 , S3C2410_GPG5_EINT13 , 2, "KEY2"}, {IRQ_EINT14, S3C2410_GPG6 , S3C2410_GPG6_EINT14 , 3, "KEY3"}, {IRQ_EINT15, S3C2410_GPG7 , S3C2410_GPG7_EINT15 , 4, "KEY4"}, {IRQ_EINT19, S3C2410_GPG11, S3C2410_GPG11_EINT19, 5, "KEY5"}, }; static int key_values = 0; static irqreturn_t buttons_interrupt(int irq, void *dev_id) { struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id; int down; udelay(0); /*获取按键值*/ down = !s3c2410_gpio_getpin(button_irqs->pin); //down: 1(按下),0(弹起) if (!down) { /*报告事件*/ key_values = button_irqs->number; //printk("====>rising key_values=%d\n",key_values); if(key_values==0) input_report_key(button_dev, KEY_1, 0); if(key_values==1) input_report_key(button_dev, KEY_2, 0); if(key_values==2) input_report_key(button_dev, KEY_3, 0); if(key_values==3) input_report_key(button_dev, KEY_4, 0); if(key_values==4) input_report_key(button_dev, KEY_5, 0); if(key_values==5) input_report_key(button_dev, KEY_6, 0); /*报告结束*/ input_sync(button_dev); } else { key_values = button_irqs->number; //printk("====>falling key_values=%d\n",key_values); if(key_values==0) input_report_key(button_dev, KEY_1, 1); if(key_values==1) input_report_key(button_dev, KEY_2, 1); if(key_values==2) input_report_key(button_dev, KEY_3, 1); if(key_values==3) input_report_key(button_dev, KEY_4, 1); if(key_values==4) input_report_key(button_dev, KEY_5, 1); if(key_values==5) input_report_key(button_dev, KEY_6, 1); input_sync(button_dev); } return IRQ_RETVAL(IRQ_HANDLED); } static int s3c24xx_request_irq(void) { int i; int err = 0; for (i = 0; i if (button_irqs[i].irq continue; } /* IRQ_TYPE_EDGE_FALLING,IRQ_TYPE_EDGE_RISING,IRQ_TYPE_EDGE_BOTH */ err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH, button_irqs[i].name, (void *)&button_irqs[i]); if (err) break; } /*错误处理*/ if (err) { i--; for (; i >= 0; i--) { if (button_irqs[i].irq continue; } disable_irq(button_irqs[i].irq); free_irq(button_irqs[i].irq, (void *)&button_irqs[i]); } return -EBUSY; } return 0; } static int __init dev_init(void) { /*request irq*/ s3c24xx_request_irq(); /* Initialise input stuff */ button_dev = input_allocate_device(); if (!button_dev) { printk(KERN_ERR "Unable to allocate the input device !!\n"); return -ENOMEM; } button_dev->name = "s3c2440_button"; button_dev->id.bustype = BUS_RS232; button_dev->id.vendor = 0xDEAD; button_dev->id.product = 0xBEEF; button_dev->id.version = 0x0100; button_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT(EV_SYN); //set_bit(EV_KEY, button_dev->evbit)//支持EV_KEY事件 /*设置支持哪些按键*/ set_bit(KEY_1, button_dev->keybit); set_bit(KEY_2, button_dev->keybit); set_bit(KEY_3, button_dev->keybit); set_bit(KEY_4, button_dev->keybit); set_bit(KEY_5, button_dev->keybit); set_bit(KEY_6, button_dev->keybit); //printk("KEY_RESERVED=%d ,KEY_1=%d",KEY_RESERVED,KEY_1); input_register_device(button_dev); //注册input设备 printk ("initialized\n"); return 0; } static void __exit dev_exit(void) { int i; for (i = 0; i if (button_irqs[i].irq continue; } free_irq(button_irqs[i].irq, (void *)&button_irqs[i]); } input_unregister_device(button_dev); } module_init(dev_init); module_exit(dev_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Xie");
在本文中,我们学习了Linux输入子系统的基本概念和结构,以及一些常用的命令和工具。我们了解了如何查看和控制输入设备的属性和状态,以及如何使用evtest和libinput工具来测试和调试输入设备。我们还学习了如何使用udev规则来自定义输入设备的行为和配置。
Linux输入子系统是一个强大而灵活的框架,它可以让你更好地管理和使用你的输入设备。通过使用Linux输入子系统,你可以提高你的工作效率和用户体验。我们建议你在使用Linux系统时,经常使用Linux输入子系统来优化你的输入设备。
以上是Linux输入子系统简介的详细内容。更多信息请关注PHP中文网其他相关文章!