输入子系统(input subsystem)
发布日期:2021-06-29 14:51:07 浏览次数:3 分类:技术文章

本文共 8887 字,大约阅读时间需要 29 分钟。

输入子系统(input subsystem)

一、为什么引入输入子系统

之前我们学习了一些简单的字符设备驱动,我们在写按键驱动时,都采用字符设备、混杂设备处理的,使我们的驱动程序只能我们自己使用,别人很难使用。如果所有的输入设备都这样处理的话,那么大量的输入设备驱动将会非常的混乱分散。大神为了将不同类别的,分散的输入设备进行统一的驱动,出现了输入子系统。

二、输入子系统分层

输入子系统分为三层:驱动层(Input Driver)、核心层(Input Core)和 事件处理层(Event Handler)。

内核通过核心层 将 驱动层 和 事件处理层连接在一起,向上为应用层提供接口,向下为输入设备提供接口,这样在原本分散的,不同类别的输入设备的驱动在应用层就有了统一的接口。

在这里插入图片描述

从图中可以看出当我们写输入设备驱动时,只需要关注驱动层的事情,然后让核心层将驱动层和事件处理层连接起来。

三、输入子系统代码分析

现在我们根据源代码分析整个输入子系统的框架,输入子系统的核心层在driver/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_MAJOR。
再来看看它的file_operations结构:

static const struct file_operations input_fops = {
.owner = THIS_MODULE, .open = input_open_file,};

在它的file_operations结构中只提供了一个结构体,当我们打开这个设备是,就回调用这个open函数:

static int input_open_file(struct inode *inode, struct file *file){
struct input_handler *handler = input_table[iminor(inode) >> 5];//以次设备号从input_table中找到一个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; old_fops = file->f_op; file->f_op = new_fops;//使新的fops代替旧的fops err = new_fops->open(inode, file);//调用新的fops ......}

从上面知道open函数会从input_table数组中以次设备号得到一个结构,用它的fops替代打开文件的fops。

那么这个数组由谁来设置呢?通过查找会发现,它是在input_register_handler函数中被设置的。

int input_register_handler(struct input_handler *handler){
struct input_dev *dev; INIT_LIST_HEAD(&handler->h_list); if (handler->fops != NULL) {
if (input_table[handler->minor >> 5]) return -EBUSY; input_table[handler->minor >> 5] = handler; } list_add_tail(&handler->node, &input_handler_list);//把handler添加到input_handler_list量表中 list_for_each_entry(dev, &input_dev_list, node) input_attach_handler(dev, handler);//遍历input_dev_list中的每一项,让dev和handler建立联系。 input_wakeup_procfs_readers(); return 0;}

从函数中可以看到,它将一个input_handler结构的地址以次设备号放入数组中,并遍历input_dev_list中的每一项,让dev和handler建立联系。

从这个函数的名字我们可以猜测它是注册一个handler,它是被事件处理层调用的,并在input_dev_list链表中查找匹配的dev和它建立联系。那这个链表谁来设置的呢?它是由input_register_device函数设置的。

int input_register_device(struct input_dev *dev){
...... list_add_tail(&dev->node, &input_dev_list);//将一个input_dev结构加到input_dev_list链表 ...... list_for_each_entry(handler, &input_handler_list, node) input_attach_handler(dev, handler);//遍历input_handler_list中的每一项,让dev和handler建立联系。 input_wakeup_procfs_readers(); return 0;}

这个函数是被驱动层调用的,他注册一个input_dev结构,并遍历input_handler_list链表使这个input_dev结构和匹配的handler建立联系。

从中可以看出,不管是事件处理层先注册还是驱动层想注册,都将使它们之间建立联系。我们赢在驱动层注册一个input_dev结构,这样就能和事件处理层建立相应的联系了。

当建立联系后,我们在应用层调用read,write等函数时,将调用相应的事件处理层的read,write等函数。那这些read,write等函数函数是怎么做的呢?我们以evdev.c中的为例看一下:

static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos){
...... if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK)) return -EAGAIN; retval = wait_event_interruptible(evdev->wait, client->head != client->tail || !evdev->exist);//当client环形缓冲区为空,并且是非阻塞方式打开时,休眠 ......}

在read中,当client环形缓冲区为空,并且是非阻塞方式打开时,程序休眠。那么谁来唤醒它呢?

是在input_handler结构中的event函数中。当驱动程序有数据时上报一个事件,并发送一个事件信号。一般在中断服务函数中完成。

下面我们来总结一下输入子系统驱动的框架:

1、分配一个input_dev结构
2、设置input_dev结构
3、注册input_dev结构
4、硬件相关的操作,初始化硬件,注册中断……
5、在中断服务函数中上报事件,并发送事件信号

四、输入子系统的重要数据结构

  1. input_handler结构,由事件处理层注册,提供相应的read,write等函数,并提供连接函数。
struct input_handler {
void *private; void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);//事件处理函数 int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);//连接函数 void (*disconnect)(struct input_handle *handle); void (*start)(struct input_handle *handle); const struct file_operations *fops;//提供file_operations结构 int minor;//次设备号 const char *name; const struct input_device_id *id_table;//用于匹配input_dev结构 const struct input_device_id *blacklist; struct list_head h_list; struct list_head node;};
  1. input_dev结构,由驱动层分配,描述一些设备信息,设备类型等
struct input_dev {
void *private; const char *name; //设备名 const char *phys; const char *uniq; struct input_id id; //用于匹配事件处理层的input_handler结构 unsigned long evbit[NBITS(EV_MAX)]; //用于记录支持的事件类型 unsigned long keybit[NBITS(KEY_MAX)]; //记录支持的按键值 unsigned long relbit[NBITS(REL_MAX)]; //记录支持的相对坐标 unsigned long absbit[NBITS(ABS_MAX)]; //记录支持的绝对坐标 unsigned long mscbit[NBITS(MSC_MAX)]; unsigned long ledbit[NBITS(LED_MAX)]; unsigned long sndbit[NBITS(SND_MAX)]; unsigned long ffbit[NBITS(FF_MAX)]; unsigned long swbit[NBITS(SW_MAX)]; unsigned int keycodemax; //支持的按键值的个数 unsigned int keycodesize; //每个键值的字节数 void *keycode; //存储按键值的数组首地址 int (*setkeycode)(struct input_dev *dev, int scancode, int keycode); //修改键值的函数,可选 int (*getkeycode)(struct input_dev *dev, int scancode, int *keycode); //获取扫描码的键值,可选 struct ff_device *ff; unsigned int repeat_key; //最近一次按键值,用于连击 struct timer_list timer; //自动连击计时器 int state; int sync; //最后一次同步后没有新的事件置1 int abs[ABS_MAX + 1]; //当前各个坐标的值 int rep[REP_MAX + 1]; //自动连击的参数 unsigned long key[NBITS(KEY_MAX)]; unsigned long led[NBITS(LED_MAX)]; unsigned long snd[NBITS(SND_MAX)]; unsigned long sw[NBITS(SW_MAX)]; int absmax[ABS_MAX + 1]; int absmin[ABS_MAX + 1]; int absfuzz[ABS_MAX + 1]; int absflat[ABS_MAX + 1]; int (*open)(struct input_dev *dev); void (*close)(struct input_dev *dev); int (*flush)(struct input_dev *dev, struct file *file); int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value); struct input_handle *grab; struct mutex mutex; /* serializes open and close operations */ unsigned int users; struct class_device cdev; union {
/* temporarily so while we switching to struct device */ struct device *parent; } dev; struct list_head h_list; struct list_head node;};

五、输入子系统驱动层的编写

以下是一个简单的驱动的例子:

/* 参考drivers\input\keyboard\gpio_keys.c */#include 
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
struct buttons_desc{ int irq; char *name; unsigned int pin; unsigned int key_val;};static struct buttons_desc pins_desc[] = { { IRQ_EINT0, "S1", S3C2410_GPF0, KEY_L}, { IRQ_EINT2, "S2", S3C2410_GPF2, KEY_S}, { IRQ_EINT11, "S3", S3C2410_GPG3, KEY_ENTER}, { IRQ_EINT19, "S4", S3C2410_GPG11, KEY_LEFTSHIFT},};static struct input_dev *buttons_dev = NULL;static struct buttons_desc *irq_pd = NULL;static struct timer_list buttons_timer;static void buttons_timer_function(unsigned long data){ unsigned int pin_val; if(irq_pd == NULL){ return; } pin_val = s3c2410_gpio_getpin(irq_pd->pin); if(pin_val){ /*松开:输出参数:0*/ input_event(buttons_dev,EV_KEY,irq_pd->key_val,0); input_sync(buttons_dev); } else{ /*按下:输出参数:0*/ input_event(buttons_dev,EV_KEY,irq_pd->key_val,1); input_sync(buttons_dev); }}static irqreturn_t buttons_irq(int irq,void *dev_id){ irq_pd = (struct buttons_desc *)dev_id; mod_timer(&buttons_timer, jiffies + HZ/100); return IRQ_RETVAL(IRQ_HANDLED);}static int __init buttons_init(void){ int i,err; /*1、分配一个input_dev结构体*/ buttons_dev = input_allocate_device(); if(!buttons_dev){ return -ENOMEM; } /*2、设置*/ /*2.1、能产生哪类事件*/ set_bit(EV_KEY,buttons_dev->evbit); set_bit(EV_REP,buttons_dev->evbit); /*2.2、能产生这类操作里的哪些事件:L,S,ENTER,LEFTSHIFT*/ set_bit(KEY_L,buttons_dev->keybit); set_bit(KEY_S,buttons_dev->keybit); set_bit(KEY_ENTER,buttons_dev->keybit); set_bit(KEY_LEFTSHIFT,buttons_dev->keybit); /*3、注册*/ err = input_register_device(buttons_dev); if(err){ printk(KERN_ERR "buttons: unable to register buttons input device\n"); goto fail1; } /*4、硬件相关的操作*/ init_timer(&buttons_timer); buttons_timer.function = buttons_timer_function; add_timer(&buttons_timer); for(i = 0;i < 4;i += 1){ err = request_irq(pins_desc[i].irq,buttons_irq,IRQT_BOTHEDGE,pins_desc[i].name,&pins_desc[i]); if(err){ printk(KERN_ERR "buttons: unable to request irq\n"); goto fail2; } } return 0;fail2: for(i -= 1; i >= 0; i -= 1){ free_irq(pins_desc[i].irq, &pins_desc[i]); } del_timer(&buttons_timer);fail1: input_free_device(buttons_dev); return err;}static void __exit buttons_exit(void){ int i; for(i = 0;i < 4; i += 1){ free_irq(pins_desc[i].irq, &pins_desc[i]); } del_timer(&buttons_timer); input_free_device(buttons_dev);}module_init(buttons_init);module_exit(buttons_exit);MODULE_LICENSE("GPL");

————————————————

本文原文链接:https://blog.csdn.net/keil_chen/article/details/53001429

转载地址:https://ciellee.blog.csdn.net/article/details/104897526 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Input Subsystem 底层框架浅析
下一篇:【Android 输入子系统 01】- 前言

发表评论

最新留言

留言是一种美德,欢迎回访!
[***.207.175.100]2024年04月19日 15时16分49秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章