input_subsys 输入子系统框架分析

在linux内核中 已做好各类驱动的框架,驱动程序也属于内核的一部分,我们可以在原有的驱动上修改,来匹配我们自已的硬件,也可以自已编写符合内核驱动框架的驱动程序。出于学习的目的,便于更好的理解各类驱动的框架和编程思想,先分析内核自带的驱动框架和流程,再自已编写符合内核框架的驱动程序。下面开始,从输入子系统开始学习分析,后面一步一步涉及各类驱动。

一、输入子系统

  从 drivers/input/input.c 这个文件开始分析,分析驱动程序的时候,先从其入口函数开始,因为每当加载一个驱动程序的时候就是调用这个驱动的入口函数,后面我们就假设对一个驱动的读写操作,一步一步展开深入,分析其流程,从而得到框架,当我们自已写驱动时就可以参照这个框架一步步编写程序,实现功能。

 1 /* 定义一个 file_operations 结构体 并设置 */
 2 static const struct file_operations input_fops = {
 3     .owner = THIS_MODULE,
 4     .open = input_open_file,
 5     .llseek = noop_llseek,
 6 };
 7
 8 static int __init input_init(void)
 9 {
10     int err;
11     /* 注册一个 input_class 类 */
12     err = class_register(&input_class);
13     if (err) {
14         pr_err("unable to register input_dev class\n");
15         return err;
16     }
17
18     err = input_proc_init();
19     if (err)
20         goto fail1;
21     /* 注册一个字符设备 参数:INPUT_MAJOR-主设备号 "input"-名字 input_fops-file_operations 结构体 */
22     err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
23     if (err) {
24         pr_err("unable to register char major %d", INPUT_MAJOR);
25         goto fail2;
26     }
27
28     return 0;
29
30  fail2:    input_proc_exit();
31  fail1:    class_unregister(&input_class);
32     return err;
33 }

  A:假设我们的应用程序打开这个设备,那么就会调用这个设备的open函数,上面注册设备时,传入了一个 file_operations 结构体,他里面有一个 open = input_open_file 进入这个函数来分析

static int input_open_file(struct inode *inode, struct file *file)
{
    /* 定义一个 input_handler 结构体指针 */
    struct input_handler *handler;
    /* 定义两个file_operations 结构体指针 */
    const struct file_operations *old_fops, *new_fops = NULL;
    int err;
    /* 互斥锁 上锁 */
    err = mutex_lock_interruptible(&input_mutex);
    if (err)
        return err;
    /* 设置 handler 指向 以打开设备的次设备号右移5位的值为下标
     * 取出 input_table【下标】 数组的一项 赋给 handler
     */
    /* No load-on-demand here? */
    handler = input_table[iminor(inode) >> 5];
    /* 判断这项是否存在 1-存在 0-不存在 */
    if (handler)
    /* 把 handler 里的 file_operations 结构体 赋给 new_fops */
        new_fops = fops_get(handler->fops);
    /* 互斥锁 解锁 */
    mutex_unlock(&input_mutex);

    /*
     * That‘s _really_ odd. Usually NULL ->open means "nothing special",
     * not "no device". Oh, well...
     */
    if (!new_fops || !new_fops->open) {
        fops_put(new_fops);
        err = -ENODEV;
        goto out;
    }
    /* 保存原来的 file_operations 并把新的new_fops 赋给file->f_op */
    old_fops = file->f_op;
    file->f_op = new_fops;
    /* 调用新的fops里的open函数
     * 即执行的是 handler->fops->open 函数
     */
    err = new_fops->open(inode, file);
    if (err) {
        fops_put(file->f_op);
        file->f_op = fops_get(old_fops);
    }
    fops_put(old_fops);
out:
    return err;
}

到这里,我们就知道了,APP执行open时,最终会调用到 handler->fops->open 函数,那么,handler->fops->open 函数是谁呢?这个函数从那里来的?谁创建设置这个函数?

在上面的代码里有一条 handler = input_table[iminor(inode) >> 5]; 从 input_table 数组中得到,那这个数组又是谁构造和填充设置的呢?这个数组是一个静态数组,所以在本文件中搜索一下应该可以知道结果。

static struct input_handler *input_table[8];

int input_register_handler(struct input_handler *handler)
{
    /* 定义一个 input_dev 结构体指针 */
    struct input_dev *dev;
    int retval;
    /* 互斥锁 上锁 */
    retval = mutex_lock_interruptible(&input_mutex);
    if (retval)
        return retval;
    /* 初始化链表头 */
    INIT_LIST_HEAD(&handler->h_list);
    /* 判断 handler->fops 是否存在 判断 input_table[handler->minor >> 5]
     * 数组里这项是否存在 存在就返回忙错误 跳到 out处
     */
    if (handler->fops != NULL) {
        if (input_table[handler->minor >> 5]) {
            retval = -EBUSY;
            goto out;
        }
        /* 把这个函数的参数 handler 赋给这一项 即设置数组这一项,open 里要用到 */
        input_table[handler->minor >> 5] = handler;
    }
    /* 把这个结构体加入 input_handler_list 链表 */
    list_add_tail(&handler->node, &input_handler_list);
    /* 遍历 input_dev_list 链表 对这个链表的每一个节点都执行 input_attach_handler(dev, handler); */
    list_for_each_entry(dev, &input_dev_list, node)
    /* 这个函数是比较dev和handler里的id 是否匹配 */
        input_attach_handler(dev, handler);

    input_wakeup_procfs_readers();

 out:
    mutex_unlock(&input_mutex);
    return retval;
}

搜索得到在 int input_register_handler(struct input_handler *handler) 函数里构造设置了,这里传入的参数 struct input_handler *handler 我们不清楚是那来的,再搜索那里调用了 input_register_handler 把参数搞清楚

得到很多文件调用了这个函数,我们选一个简单的来分析 就以 Evdev.c 为例来分析一下,了解一个 传入参数是什么内容。进入 evdev.c 分析。

Apm-power.c (drivers\input): return input_register_handler(&apmpower_handler);
Evbug.c (drivers\input): return input_register_handler(&evbug_handler);
Evdev.c (drivers\input): return input_register_handler(&evdev_handler);
Input.c (drivers\input): * input_register_handler - register a new input handler
Input.c (drivers\input):int input_register_handler(struct input_handler *handler)
Input.c (drivers\input):EXPORT_SYMBOL(input_register_handler);
Input.c (net\rfkill): return input_register_handler(&rfkill_handler);
Input.h (include\linux):int __must_check input_register_handler(struct input_handler *);
Joydev.c (drivers\input): return input_register_handler(&joydev_handler);
Keyboard.c (drivers\tty\vt): error = input_register_handler(&kbd_handler);
Kgdboc.c (drivers\tty\serial): if (input_register_handler(&kgdboc_reset_handler) == 0)
Mac_hid.c (drivers\macintosh): err = input_register_handler(&mac_hid_emumouse_handler);
Mousedev.c (drivers\input): error = input_register_handler(&mousedev_handler);
Sysrq.c (drivers\tty): error = input_register_handler(&sysrq_handler);

static struct input_handler evdev_handler = {
    .event        = evdev_event,            /* 事件处理函数 */
    .connect    = evdev_connect,        /* 连接函数 设备层和 软件处理层连接 */
    .disconnect    = evdev_disconnect,    /* 清除连接 */
    .fops        = &evdev_fops,            /* file_operations 结构体 */
    .minor        = EVDEV_MINOR_BASE,        /* 次设备号 */
    .name        = "evdev",                /* 名字 */
    .id_table    = evdev_ids,            /* ID 用于和设备层匹配 */
};

static int __init evdev_init(void)
{
    /* 注册一个 input_register_handler */
    return input_register_handler(&evdev_handler);
}

我们可以看到,是在入口函数里调用了 input_register_handler 这个函数,这个参数就是 input_handler 结构体,可以看到这个结构体里很多函数,file_operations 结构体 等等,从这里我们基本就可以确定在前的所需要用到的一些值和函数了。下面简单列出一些相对重要的参数。

/* input_table[handler->minor >> 5] = handler;
 * handler->minor >> 5 = 64 >> 5 = 2
 * handler = evdev_handler
 * input_table[2] = evdev_handler
 */

 /* err = new_fops->open(inode, file);
  * new_fops->open = evdev_fops->evdev_open(inode, file);
  */

 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,
    .llseek        = no_llseek,
};

由此得知,当app应用层调用操作时,最终会调用到 input_handler 结构体里的操作函数 事实上如果跟着分析下去还有很多东西,如果现在跟得太深,不太好理解,所以暂时对于handler层 先分析到这里,后面的内容和另一个层 设备层是一起的,所以下面转过头先分析设备层,等到设备层分析到和handler层相接的地方,在往下分析,由此可以便于加深理解,为什么我知道有个设备层呢?“我也是跟别人学的” 但是有一点可以看出来,上面分析的过程中,出现过一个东西在 input_register_handler 这个函里有一段处理

    /* 把这个结构体加入 input_handler_list 链表 */
    list_add_tail(&handler->node, &input_handler_list);
    /* 遍历 input_dev_list 链表 对这个链表的每一个节点都执行 input_attach_handler(dev, handler); */
    list_for_each_entry(dev, &input_dev_list, node)
    /* 这个函数是比较dev和handler里的id 是否匹配 */
        input_attach_handler(dev, handler);

这里提到一个链表 input_dev_list 这就是设备层的链表,我们搜索一下,看在那里可以找到相关的信息,搜索到一些内容,我们依次进去看一下,看在那里有这个链表的处理 最终我搜索到一条这样的处理

list_add_tail(&dev->node, &input_dev_list); 这条处理就把dev结构体加入input_dev_list这个链表,在 int input_register_device(struct input_dev *dev) 函数里调用,看名字就是注册一个输入设备,参数是input_dev 结构体

和我们前面看到的注册类同,因此我们再搜索一下谁调用了这个函数 结果是 一大堆设备调用了这个函数,由此我猜测,调用这个函数的就是设备层关于硬件相关的驱动,你问我怎么知道,因为我是天才!!!开个玩笑,我要是天才就不用学了。。。当然我也是跟别人学的。所谓的别人就是“韦东山大牛” 这不是广告,纯属个人觉得,他的东西确实好,好像跑偏了。回到正题。刚说到搜索到很多调用,我们选一个简单点的来分析一下,设备层这边的框架,现在分析的东西,就是我们自已写驱动所需要编写的内容,了解了这层的框架和逻辑思维后,按照框架就可以写出符合内核的驱动程序了,同上面一样,找一个简单的例子分析,我里我们选择 gpio_key.c 这只是一个例子,他没有具体的硬件操作。下面开始

在 static int __devinit gpio_keys_probe(struct platform_device *pdev) 这个函数中调用了 error = input_register_device(input); 和上面一样,注册一个input_dev 设备,关于 gpio_keys_probe 是和平台设备相关,我们先不管他我们写驱动时也可以不管他,直接注册设备即可,在这里了解一下 注册前需要做那些事,看代码

static int __devinit gpio_keys_probe(struct platform_device *pdev)
{
    const struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
    struct gpio_keys_drvdata *ddata;
    struct device *dev = &pdev->dev;
    struct gpio_keys_platform_data alt_pdata;
    struct input_dev *input;
    int i, error;
    int wakeup = 0;

    if (!pdata) {
        error = gpio_keys_get_devtree_pdata(dev, &alt_pdata);
        if (error)
            return error;
        pdata = &alt_pdata;
    }

    ddata = kzalloc(sizeof(struct gpio_keys_drvdata) +
            pdata->nbuttons * sizeof(struct gpio_button_data),
            GFP_KERNEL);
    /* 分配一个 inpu_dev 结构体 前面有定义的 struct input_dev *input; */
    input = input_allocate_device();
    if (!ddata || !input) {
        dev_err(dev, "failed to allocate state\n");
        error = -ENOMEM;
        goto fail1;
    }

    ddata->input = input;
    ddata->n_buttons = pdata->nbuttons;
    ddata->enable = pdata->enable;
    ddata->disable = pdata->disable;
    mutex_init(&ddata->disable_lock);

    platform_set_drvdata(pdev, ddata);
    input_set_drvdata(input, ddata);
    /* 这里有一堆设置 input_dev 结构体的 我们自已编写的时候用到什么函数就实现什么函数这里是例子 */
    input->name = pdata->name ? : pdev->name;
    input->phys = "gpio-keys/input0";
    input->dev.parent = &pdev->dev;
    input->open = gpio_keys_open;    /* open 函数 */
    input->close = gpio_keys_close;

    input->id.bustype = BUS_HOST;
    input->id.vendor = 0x0001;
    input->id.product = 0x0001;
    input->id.version = 0x0100;

    /* Enable auto repeat feature of Linux input subsystem */
    if (pdata->rep)
        __set_bit(EV_REP, input->evbit);

    for (i = 0; i < pdata->nbuttons; i++) {
        const struct gpio_keys_button *button = &pdata->buttons[i];
        struct gpio_button_data *bdata = &ddata->data[i];

        error = gpio_keys_setup_key(pdev, input, bdata, button);
        if (error)
            goto fail2;

        if (button->wakeup)
            wakeup = 1;
    }

    error = sysfs_create_group(&pdev->dev.kobj, &gpio_keys_attr_group);
    if (error) {
        dev_err(dev, "Unable to export keys/switches, error: %d\n",
            error);
        goto fail2;
    }
    /* 注册 input_dev */
    error = input_register_device(input);
    if (error) {
        dev_err(dev, "Unable to register input device, error: %d\n",
            error);
        goto fail3;
    }

    /* get current state of buttons that are connected to GPIOs */
    for (i = 0; i < pdata->nbuttons; i++) {
        struct gpio_button_data *bdata = &ddata->data[i];
        if (gpio_is_valid(bdata->button->gpio))
            gpio_keys_gpio_report_event(bdata);
    }
    input_sync(input);

    device_init_wakeup(&pdev->dev, wakeup);

    return 0;

 fail3:
    sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group);
 fail2:
    while (--i >= 0)
        gpio_remove_key(&ddata->data[i]);

    platform_set_drvdata(pdev, NULL);
 fail1:
    input_free_device(input);
    kfree(ddata);
    /* If we have no platform_data, we allocated buttons dynamically. */
    if (!pdev->dev.platform_data)
        kfree(pdata->buttons);

    return error;
}

我们看到,在注册前,分配了一个input_dev 结构体 并设置他。然后调用注册函数,我们到注册函数里看一下做了些什么鬼事情?

int input_register_device(struct input_dev *dev)
{
    /* 这是什么鬼,不认识 不管他 */
    static atomic_t input_no = ATOMIC_INIT(0);
    /* 定义一个 input_handler 结构体 */
    struct input_handler *handler;
    const char *path;
    int error;

    /* Every input device generates EV_SYN/SYN_REPORT events. */
    __set_bit(EV_SYN, dev->evbit);

    /* KEY_RESERVED is not supposed to be transmitted to userspace. */
    __clear_bit(KEY_RESERVED, dev->keybit);

    /* Make sure that bitmasks not mentioned in dev->evbit are clean. */
    input_cleanse_bitmasks(dev);

    if (!dev->hint_events_per_packet)
        dev->hint_events_per_packet =
                input_estimate_events_per_packet(dev);

    /*
     * 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;

    dev_set_name(&dev->dev, "input%ld",
             (unsigned long) atomic_inc_return(&input_no) - 1);

    error = device_add(&dev->dev);
    if (error)
        return error;

    path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
    pr_info("%s as %s\n",
        dev->name ? dev->name : "Unspecified device",
        path ? path : "N/A");
    kfree(path);
    /* 互斥锁 上锁 老是有这东东,怎么互斥怎么锁,没空理它以后再说 */
    error = mutex_lock_interruptible(&input_mutex);
    if (error) {
        device_del(&dev->dev);
        return error;
    }
    /* 把 input_dev 加入 input_dev_list 链表 */
    list_add_tail(&dev->node, &input_dev_list);
    /* 遍历 input_handler_list 链表 对每一项都执行input_attach_handler 函数 */
    list_for_each_entry(handler, &input_handler_list, node)
        input_attach_handler(dev, handler);

    input_wakeup_procfs_readers();

    mutex_unlock(&input_mutex);

    return 0;
}

我们看到了在后面同样是加入了链表,同样也遍历链表,执行相同的函数,在dve层是遍历 input_headler_list链表 在handler层是遍历 input_dev_list链表 嗯,我主场我做主,我找你,你主场你做主,你找我,谁是你,谁是我,你高兴就好,爱谁谁。两个层都调用了相同的函数 input_attach_handler 所以我们很有必要去看一下他想干啥,

static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
    const struct input_device_id *id;
    int error;
    /* 找好基友函数 对比ID 是否相同,相同的话返回值 id=1  */
    id = input_match_device(handler, dev);
    if (!id)
        return -ENODEV;
    /* 上面必须返回1才会执行这条 即调用handr层的connect函数 */
    error = handler->connect(handler, dev, id);
    if (error && error != -ENODEV)
        pr_err("failed to attach handler %s to device %s, error: %d\n",
               handler->name, kobject_name(&dev->dev.kobj), error);

    return error;
}

里面还有一个函数我们再进去看看

static const struct input_device_id *input_match_device(struct input_handler *handler,
                            struct input_dev *dev)
{
    const struct input_device_id *id;
    int i;

    for (id = handler->id_table; id->flags || id->driver_info; id++) {

        if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
            if (id->bustype != dev->id.bustype)
                continue;

        if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
            if (id->vendor != dev->id.vendor)
                continue;

        if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
            if (id->product != dev->id.product)
                continue;

        if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
            if (id->version != dev->id.version)
                continue;

        MATCH_BIT(evbit,  EV_MAX);
        MATCH_BIT(keybit, KEY_MAX);
        MATCH_BIT(relbit, REL_MAX);
        MATCH_BIT(absbit, ABS_MAX);
        MATCH_BIT(mscbit, MSC_MAX);
        MATCH_BIT(ledbit, LED_MAX);
        MATCH_BIT(sndbit, SND_MAX);
        MATCH_BIT(ffbit,  FF_MAX);
        MATCH_BIT(swbit,  SW_MAX);

        if (!handler->match || handler->match(handler, dev))
            return id;
    }

    return NULL;
}

就是看一下 handler->id_tabe 支不支持 dev,支持就返回1 我们看到 handler->id_tabe  里说了,匹配所有设备,然后调用handler->connect,进去看看这个函数干啥?

static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
             const struct input_device_id *id)
{
    struct evdev *evdev;
    int minor;
    int error;

    for (minor = 0; minor < EVDEV_MINORS; minor++)
        if (!evdev_table[minor])
            break;

    if (minor == EVDEV_MINORS) {
        pr_err("no more free evdev devices\n");
        return -ENFILE;
    }

    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);

    dev_set_name(&evdev->dev, "event%d", minor);
    evdev->exist = true;
    evdev->minor = minor;

    evdev->handle.dev = input_get_device(dev);
    evdev->handle.name = dev_name(&evdev->dev);
    evdev->handle.handler = handler;
    evdev->handle.private = evdev;

    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);

    error = input_register_handle(&evdev->handle);
    if (error)
        goto err_free_evdev;

    error = evdev_install_chrdev(evdev);
    if (error)
        goto err_unregister_handle;

    error = device_add(&evdev->dev);
    if (error)
        goto err_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;
}

这里面又分配设置了一个input_headle 结构体 注意看名称,少了个 r

evdev->handle.dev = input_get_device(dev);
evdev->handle.name = dev_name(&evdev->dev);
evdev->handle.handler = handler;
evdev->handle.private = evdev;

一个指向 handler 一个指向dev 所以无论从那边进入都可以找到对方,到此双方就建立“基友”的连接关系了。

  B:假设app读操作,通过上面的分析我们知道最终会调用到对应的handler里的fops里的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_event event;
    int retval = 0;

    if (count < input_event_size())
        return -EINVAL;

    if (!(file->f_flags & O_NONBLOCK)) {
        retval = wait_event_interruptible(evdev->wait,/* 休眠 谁来唤醒 搜索evdev->wait */
                client->packet_head != client->tail ||
                !evdev->exist);
        if (retval)
            return retval;
    }

    if (!evdev->exist)
        return -ENODEV;

    while (retval + input_event_size() <= count &&
           evdev_fetch_next_event(client, &event)) {

        if (input_event_to_user(buffer + retval, &event))
            return -EFAULT;

        retval += input_event_size();
    }

    if (retval == 0 && (file->f_flags & O_NONBLOCK))
        return -EAGAIN;

    return retval;
}

在读函数里休眠,那谁来唤醒他呢,他在那个队列里休眠,我们就搜索这个队列,看在那里有唤醒操作 在static void evdev_event(struct input_handle *handle)函数里调用 wake_up_interruptible(&evdev->wait); 来唤醒,谁又调用了evdev_event 函数呢 别人猜得很准,说是从设备层那边会调用到这个函数,用例子来说明,当有按键按下时,进入设备层中断处理函数 在里面会调用inpu_event,分析这个函数可以知道如何操作

static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id)
{
    struct gpio_button_data *bdata = dev_id;
    const struct gpio_keys_button *button = bdata->button;
    struct input_dev *input = bdata->input;
    unsigned long flags;

    BUG_ON(irq != bdata->irq);

    spin_lock_irqsave(&bdata->lock, flags);

    if (!bdata->key_pressed) {
        input_event(input, EV_KEY, button->code, 1);
        input_sync(input);

        if (!bdata->timer_debounce) {
            input_event(input, EV_KEY, button->code, 0);
            input_sync(input);
            goto out;
        }

        bdata->key_pressed = true;
    }

    if (bdata->timer_debounce)
        mod_timer(&bdata->timer,
            jiffies + msecs_to_jiffies(bdata->timer_debounce));
out:
    spin_unlock_irqrestore(&bdata->lock, flags);
    return IRQ_HANDLED;
}

这里就简单列出调用关系,就不做进一步细化分析了

inpu_event

  input_handle_event

    input_pass_event

      handler->event(handle, type, code, value);  //这里最终就是调用 evdev_event 唤醒 evdev_read函数唤醒后调用 input_event_to_user 把数据给app

到这里框架就分析完毕了,那么 我们自已如何编写一个符合内核这套input 输入子系统的驱动呢,我们主要关心设备层

1:分配一个input_dev 结构体

2:设置

3:注册

4:硬件机关的操作 (例:当按键按下中,产生中断,在中断服务函数里上报事件即可)

原文地址:https://www.cnblogs.com/x2i0e19linux/p/11730488.html

时间: 2024-11-04 02:04:14

input_subsys 输入子系统框架分析的相关文章

Linux输入子系统框架分析(1)

在Linux下的输入设备键盘.触摸屏.鼠标等都可以用输入子系统来实现驱动.输入子系统分为三层,核心层和设备驱动层,事件层.核心层和事件层由Linux输入子系统本身实现,设备驱动层由我们实现.我们在设备驱动层将输入事件上报给核心层input.c,核心层找到匹配的事件层,将事件交给事件层处理,事件层处理完后传递到用户空间. 我们最终要搞清楚的是在用户空间调用open和read最终在内核中是怎样处理的,向内核上报的事件又是谁处理的,处理完后是怎样传递到用户空间的? 上面两个图是输入子系统的框架. 下面

input输入子系统框架分析

input子系统的搭建要点: 核心层为事件驱动层和设备驱动层的注册提供API的实现.核心层为设备驱动层上报事件提供API的实现 .事件驱动层为应用层提供API的实现 . (1)核心层:提供事件驱动层和设备驱动层所需的函数接口(为input dev和input handler建立联) drivers/input/input.c: ##主要接口函数一览:## ①为事件驱动层提供的: 注册API: int input_register_handler(struct input_handler *han

输入子系统 框架

目录 回顾引入 简介 框架小结 次设备号 框架结构图 数据管理结构 关键函数 框架分析 input_init input_open_file input_register_handler input_register_device input_attach_handler connect read 程序设计 无框架驱动 框架架构 测试 hexdump分析 tty读取分析 按键连发 title: 输入子系统 框架 tags: linux date: 2018-11-28 15:39:22 toc:

Linux驱动之输入子系统框架

    好记性不如烂笔头,整理一下笔记~ Linux驱动之输入子系统框架 输入子系统将该类驱动划分为3部分 1.核心层 input.c 2.设备层 Gpio_keys.c ... 3.事件处理层 Evdev.c 事件处理层为纯软件的东西,设备层涉及底层硬件,它们通过核心层建立联系,对外提供open write等接口. 1.我们首先来看,核心层 input.c如何向外界提供接口 在 input_init 中注册了字符设备驱动 register_chrdev(INPUT_MAJOR, "input&

linux input输入子系统应用分析

输入设备(如按键.键盘.触摸屏.鼠标等)是典型的字符设备,其一般的工作机理是底层在按键.触摸等动作发送时产生一个中断(或驱动通过timer定时查询),然后CPU通过SPI.I2 C或外部存储器总线读取键值.坐标等数据,放入1个缓冲区,字符设备驱动管理该缓冲区,而驱动的read()接口让用户可以读取键值.坐标等数据. 显然,在这些工作中,只是中断.读值是设备相关的,而输入事件的缓冲区管理以及字符设备驱动的file_operations接口则对输入设备是通用的.基于此,内核设计了输入子系统,由核心层

输入子系统之按键驱动

上一篇博文<input输入子系统框架分析>,尝试使用这种驱动模型来看一个按键驱动程序. 下面的程序是根据韦东山老师写的代码进行修改的,我的开发板是tq2440. button.c文件: #include <linux/module.h> #include <linux/version.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/interrupt.h>

输入子系统概念介绍

输入子系统在内核中的位置:/driver/input drivers/input/input.c: input_init ---> err = register_chrdev(INPUT_MAJOR, "input", &input_fops); static const struct file_operations input_fops = { .owner = THIS_MODULE, .open = input_open_file, }; 问:怎么读按键? inpu

Linux输入子系统

在Linux中,按键.触摸屏.鼠标等等输入设备都可以依靠输入子系统提供的接口函数来实现他们的设备驱动,在输入子系统中,系统已经完成了这些输入设备的共性,所以根据子系统提供的接口,只需要完成各自的独特性即可完成一个输入设备的设备驱动. Linux中,输入子系统由设备驱动层.核心层.事件处理层这三层组成.设备驱动层讲底层输入设备的响应转化为标准的输入事件,事件处理层就为应用程序提供统一的设备访问接口来跟底层交互数据,核心层则是连接驱动层和事件处理层的桥梁. 在输入子系统中重要的结构体就是input_

Linux驱动开发之输入子系统

2020-02-15 关键字: Linux 中输入设备大致可分以下几种: 1.按键/键盘(keyboard) 2.鼠标(mouse) 3.触摸屏(touchscreen) 4.游戏杆(joystick) 输入子系统的目的是为了屏蔽众多输入设备在硬件上的差异化,使得在开发输入设备的程序时能更简单统一.输入子系统屏蔽差异的方式就是为各种输入设备与上层应用提供统一的编程接口. Linux 输入子系统是一种编程框架,它可以自上而下分为以下几种层次: 1.应用层 2.input handler层:数据处理