Linux驱动之输入子系统简析

2024-08-20  

输入子系统由驱动层、输入子系统核心、事件处理层三部分组成。一个输入事件,如鼠标移动、键盘按下等通过Driver->Inputcore->Event handler->userspace的顺序到达用户控件的应用程序。

系统框图

假设打开一个字符设备驱动程序/dev/event0,event代表的是输入子系统的设备文件,当应用程序调用C库的open函数后,open函数会进入系统调用,最后定位到driversinputinput.c文件下(这个文件就是核心层)的。这个函数的功能主要是根据设备的次设备号找到新的fops结构,然后切换到新的fops结构,然后调用它的打开函数。输入子系统的主设备号恒为#define INPUT_MAJOR 13,定义在includelinuxmajor.h中。


static int input_open_file(struct inode *inode, struct file *file)

{

    struct input_handler *handler = input_table[iminor(inode) >> 5];//根据次设备号找到在input_table表中找到handler结构体

    const struct file_operations *old_fops, *new_fops = NULL;

    int err;


    /* No load-on-demand here? */

    if (!handler || !(new_fops = fops_get(handler->fops)))//判断handler结构体是否存在,存在的话将里面的fops变量赋给new_fops

        return -ENODEV;


    /*

     * That's _really_ odd. Usually NULL ->open means 'nothing special',

     * 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;//切换f_op变量,以后调用诸如read、write等系统调用时会进入到new_fops的read、write函数


    err = new_fops->open(inode, file);//调用new_fops的open函数


    if (err) {

        fops_put(file->f_op);

        file->f_op = fops_get(old_fops);

    }

    fops_put(old_fops);//释放掉老的fops结构

    return err;

}


接着先来看到input_table表的建立,可以看到它是一个静态变量,在本文件(driversinputinput.c)中搜索它,可以看到它位于input_register_handler函数,这是一个全局的函数,可以供外部的文件调用,这个函数的主要功能是注册一个handler结构体,这个结构体中存在minor这个设备的次设备号,这个结构所在的函数对应的其实就是上述的事件层。


int input_register_handler(struct input_handler *handler)

{

    struct input_dev *dev;


    INIT_LIST_HEAD(&handler->h_list);//初始化handler的h_list结构体,这是一个双向链表


    if (handler->fops != NULL) {

        if (input_table[handler->minor >> 5])//检查是否已经存在这个次设备号的handler结构

            return -EBUSY;


        input_table[handler->minor >> 5] = handler;//将handler结构次设备号放入input_table表

    }


    list_add_tail(&handler->node, &input_handler_list);//将handler结构根据node成员放入input_handler_list链表


    list_for_each_entry(dev, &input_dev_list, node)//根据node这个成员在input_dev_list链表中循环查找dev结构

        input_attach_handler(dev, handler);//对于每一个dev结构调用input_attach_handler函数


    input_wakeup_procfs_readers();//将这个设备信息写入proc文件系统

    return 0;

}


接着搜索input_register_handler,抽取driversinputevdev.c这个文件,可以看到在这个模块的入口函数调用了注册函数


static int __init evdev_init(void)

{

    return input_register_handler(&evdev_handler);

}

接着看到evdev_handler这个结构体,在这个结构体里面找到了evdev_fops这个结构


static struct input_handler evdev_handler = {

    .event =    evdev_event,

    .connect =    evdev_connect,

    .disconnect =    evdev_disconnect,

    .fops =        &evdev_fops,

    .minor =    EVDEV_MINOR_BASE,

    .name =        'evdev',

    .id_table =    evdev_ids,

};


接着看到evdev_fops结构体,可以看到应用层调用的read、write等函数在这里被定义


static const struct file_operations evdev_fops = {

    .owner =    THIS_MODULE,

    .read =        evdev_read,

    .write =    evdev_write,

    .poll =        evdev_poll,

    .open =        evdev_open,

    .release =    evdev_release,

    .unlocked_ioctl = evdev_ioctl,

#ifdef CONFIG_COMPAT

    .compat_ioctl =    evdev_ioctl_compat,

#endif

    .fasync =    evdev_fasync,

    .flush =    evdev_flush

};


知道了事件层对应的位置,那么设备驱动层在哪里呢?接着往下看,回到input_register_handler函数,在里面看到如下语句,这句语句的作用是将事件层与驱动层联系起来。


list_for_each_entry(dev, &input_dev_list, node)//根据node这个成员在input_dev_list链表中循环查找dev结构

        input_attach_handler(dev, handler);//对于每一个dev结构调用input_attach_handler函数

这里可以看到一个新的结构体dev,先看一下dev结构体,它的原型为input_dev,跟抽取driversinputevdev.c这个文件一样,搜索input_dev这个结构体,先列出input_dev这个结构体


struct input_dev {


    void *private;


    const char *name;

    const char *phys;

    const char *uniq;

    struct input_id id;


    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;


    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;

};


接着看到driversinputtabletkbtab.c这个文件,这个文件代表的就是设备驱动层,简单分析一下,可以看到它也是一个内核的模块,可以动态加载,一旦加载后,它会调用kbtab_init函数,最终会调用到kbtab_probe这个函数,可以看到最终又定位到了input_register_device这个注册设备的函数,它位于核心层,即driversinputinput.c文件下。


static int kbtab_probe(struct usb_interface *intf, const struct usb_device_id *id)

{

    ...

    ...

    input_dev = input_allocate_device();//分配一个input_dev 结构体

    if (!kbtab || !input_dev)

        goto fail1;


    ...

    ...


    input_dev->name = 'KB Gear Tablet';//初始化input_dev 结构体

    input_dev->phys = kbtab->phys;

    usb_to_input_id(dev, &input_dev->id);

    input_dev->dev.parent = &intf->dev;


    input_set_drvdata(input_dev, kbtab);


    input_dev->open = kbtab_open;

    input_dev->close = kbtab_close;


    input_dev->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_MSC);

    input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);

    input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOUCH);

    input_dev->mscbit[0] |= BIT(MSC_SERIAL);

    input_set_abs_params(input_dev, ABS_X, 0, 0x2000, 4, 0);

    input_set_abs_params(input_dev, ABS_Y, 0, 0x1750, 4, 0);

    input_set_abs_params(input_dev, ABS_PRESSURE, 0, 0xff, 0, 0);


    ...

    ...


    error = input_register_device(kbtab->dev);//注册input_dev结构体

    ...

    ...

}


接着看到input_register_device这个函数,它根input_register_handler相对应,前一个注册设备驱动层,后一个注册事件层。列出input_register_device函数,它同样位于driversinputinput.c文件中。


int input_register_device(struct input_dev *dev)

{

    static atomic_t input_no = ATOMIC_INIT(0);

    struct input_handler *handler;

    const char *path;

    int error;


    set_bit(EV_SYN, dev->evbit);//设置同步事件


    /*

     * If delay and period are pre-set by the driver, then autorepeating

     * is handled by the driver itself and we don't do it in input.c.

     */


    init_timer(&dev->timer);//初始化一个定时器

    if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {//按键是否需要重复,如果需要设置重复函数与重复时间

        dev->timer.data = (long) dev;

        dev->timer.function = input_repeat_key;

        dev->rep[REP_DELAY] = 250;

        dev->rep[REP_PERIOD] = 33;

    }


    if (!dev->getkeycode)

        dev->getkeycode = input_default_getkeycode;//获得按键值默认函数


    if (!dev->setkeycode)

        dev->setkeycode = input_default_setkeycode;//设置按键值默认函数


    list_add_tail(&dev->node, &input_dev_list);//将dev->node放入input_dev_list链表


    snprintf(dev->cdev.class_id, sizeof(dev->cdev.class_id),

         'input%ld', (unsigned long) atomic_inc_return(&input_no) - 1);


    if (!dev->cdev.dev)

        dev->cdev.dev = dev->dev.parent;


    error = class_device_add(&dev->cdev);

    if (error)

        return error;


    path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL);

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。