输入子系统(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 <linux/module.h>
#include <linux/version.h>

#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>

#include <asm/gpio.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>

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

发布了329 篇原创文章 · 获赞 66 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/Ciellee/article/details/104897526