input_subsys 输入子系统框架分析
Posted x2i0e19linux
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 "); 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 (driversinput): return input_register_handler(&apmpower_handler);
Evbug.c (driversinput): return input_register_handler(&evbug_handler);
Evdev.c (driversinput): return input_register_handler(&evdev_handler);
Input.c (driversinput): * input_register_handler - register a new input handler
Input.c (driversinput):int input_register_handler(struct input_handler *handler)
Input.c (driversinput):EXPORT_SYMBOL(input_register_handler);
Input.c (net
fkill): return input_register_handler(&rfkill_handler);
Input.h (includelinux):int __must_check input_register_handler(struct input_handler *);
Joydev.c (driversinput): return input_register_handler(&joydev_handler);
Keyboard.c (drivers tyvt): error = input_register_handler(&kbd_handler);
Kgdboc.c (drivers tyserial): if (input_register_handler(&kgdboc_reset_handler) == 0)
Mac_hid.c (driversmacintosh): err = input_register_handler(&mac_hid_emumouse_handler);
Mousedev.c (driversinput): error = input_register_handler(&mousedev_handler);
Sysrq.c (drivers ty): 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 "); 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 ", error); goto fail2; } /* 注册 input_dev */ error = input_register_device(input); if (error) { dev_err(dev, "Unable to register input device, error: %d ", 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 ", 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 ", 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 "); 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:硬件机关的操作 (例:当按键按下中,产生中断,在中断服务函数里上报事件即可)
以上是关于input_subsys 输入子系统框架分析的主要内容,如果未能解决你的问题,请参考以下文章
linux内核分析作业:操作系统是如何工作的进行:完成一个简单的时间片轮转多道程序内核代码
Linux内核分析:完成一个简单的时间片轮转多道程序内核代码