内核版本:3.9.5

本节将以even handler来分析设备的注册和打开的过程,分析之前不妨回顾一下上节介绍的数据结构.

结合前两节分析可知,input子系统分为3层,最上一层是event handler,中间层是input core,底层是input driver.input driver把event report到input core层,input core对event进行分发,传到 event handler,相应的event handler层把event 放到event buffer中,等待应用程序读取!这就是input的基本思想.

那么我我们来看,Linux模块机制告诉我们在设备注册之前必须先初始化INPUT子系统,这个工作由input_init()函数来完成.在drivers/input.c中:

 static int __init input_init(void)
{
int err; err = class_register(&input_class);//注册input类
if (err) {
pr_err("unable to register input_dev class\n");
return err;
} err = input_proc_init();//创建/proc中的项
if (err)
goto fail1; err = register_chrdev_region(MKDEV(INPUT_MAJOR, ),
INPUT_MAX_CHAR_DEVICES, "input");/*注册设备,主设备号为INPUT_MAJOR,就是13,后面注册的输入设备都使用该主设备号*/
if (err) {
pr_err("unable to register char major %d", INPUT_MAJOR);
goto fail2;
} return ; fail2: input_proc_exit();
fail1: class_unregister(&input_class);
return err;
} static void __exit input_exit(void)
{
input_proc_exit();
unregister_chrdev_region(MKDEV(INPUT_MAJOR, ),
INPUT_MAX_CHAR_DEVICES);
class_unregister(&input_class);
} subsys_initcall(input_init);
module_exit(input_exit);

subsys_initcall和module_exit宏相信大家都很熟悉,这俩函数也很简单,没什么看的.

在入口函数里面创建了一个input_class类,其实就在/sys/class下创建了一个目录input.当然对于一个新设备,可以注册进一个class也可以不注册进去,如果存在对应class的话注册进去更好.另外在/proc创建了入口项,这样就可以/proc目录看到input的信息,然后就注册设备,可以看出输入子系统的主设备号是13,在这里并没有生成设备文件.只是在/dev/目录下创建了input目录,以后所有注册进系统的输入设备文件都放在这个目录下.

那么接下来看看怎么注册input设备的.我们需要在设备驱动层中完成输入设备的注册,通过调用input_register_device()函数来完成,该函数的一个重要任务就是完成设备与事件驱动的匹配.

 int input_register_device(struct input_dev *dev)
{
static atomic_t input_no = ATOMIC_INIT();
struct input_devres *devres = NULL;
struct input_handler *handler;
unsigned int packet_size;
const char *path;
int error; if (dev->devres_managed) {
devres = devres_alloc(devm_input_device_unregister,
sizeof(struct input_devres), GFP_KERNEL);
if (!devres)
return -ENOMEM; devres->input = dev;
} /* 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); packet_size = input_estimate_events_per_packet(dev);
if (dev->hint_events_per_packet < packet_size)
dev->hint_events_per_packet = packet_size; dev->max_vals = max(dev->hint_events_per_packet, packet_size) + ;
dev->vals = kcalloc(dev->max_vals, sizeof(*dev->vals), GFP_KERNEL);
if (!dev->vals) {
error = -ENOMEM;
goto err_devres_free;
} /*
* 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] = ;
dev->rep[REP_PERIOD] = ;
} if (!dev->getkeycode)/*没有定义设备的getkeycode函数,则使用默认的获取键值函数*/
dev->getkeycode = input_default_getkeycode; if (!dev->setkeycode)/*没有定义设备的setkeycode函数,则使用默认的设定键值函数*/
dev->setkeycode = input_default_setkeycode; dev_set_name(&dev->dev, "input%ld",
(unsigned long) atomic_inc_return(&input_no) - );/*设定dev的名字*/ error = device_add(&dev->dev);/*添加设备*/
if (error)
goto err_free_vals; 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)
goto err_device_del; list_add_tail(&dev->node, &input_dev_list);/*将设备添加到input_dev_list设备链表*/ list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);/*遍历input_handler_list,试图与每一个handler进行匹配*/ input_wakeup_procfs_readers(); mutex_unlock(&input_mutex); if (dev->devres_managed) {
dev_dbg(dev->dev.parent, "%s: registering %s with devres.\n",
__func__, dev_name(&dev->dev));
devres_add(dev->dev.parent, devres);
}
return ; err_device_del:
device_del(&dev->dev);
err_free_vals:
kfree(dev->vals);
dev->vals = NULL;
err_devres_free:
devres_free(devres);
return error;
}

第76,77行对于每个input_dev,遍历input_handler_list,调用input_attach_handler,根据input_handler的id_table判断能否支持这个input_dev.

 static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
const struct input_device_id *id;
int error; id = input_match_device(handler, dev);
if (!id)
return -ENODEV; error = handler->connect(handler, dev, id);/*执行handler的connect,建立handler与设备之间的联系*/
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; /*遍历handler的id_table与device进行匹配*/
for (id = handler->id_table; id->flags || id->driver_info; id++) { /*根据flags的标志位,按需要匹配相应的字段*/
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; if (!bitmap_subset(id->evbit, dev->evbit, EV_MAX))//匹配所有的事件类型
continue; /*匹配所有事件的子事件*/
if (!bitmap_subset(id->keybit, dev->keybit, KEY_MAX))
continue; if (!bitmap_subset(id->relbit, dev->relbit, REL_MAX))
continue; if (!bitmap_subset(id->absbit, dev->absbit, ABS_MAX))
continue; if (!bitmap_subset(id->mscbit, dev->mscbit, MSC_MAX))
continue; if (!bitmap_subset(id->ledbit, dev->ledbit, LED_MAX))
continue; if (!bitmap_subset(id->sndbit, dev->sndbit, SND_MAX))
continue; if (!bitmap_subset(id->ffbit, dev->ffbit, FF_MAX))
continue; if (!bitmap_subset(id->swbit, dev->swbit, SW_MAX))
continue; if (!handler->match || handler->match(handler, dev))
return id;//匹配成功,返回id
} return NULL;
}

这里接下来就到input_handler的connect了,看来注册input_handler是一道绕不过去的坎儿.那么我们来看具体的event handler的注册,在drivers/input/evdev.c中:

 static const struct input_device_id evdev_ids[] = {
{ .driver_info = }, /* Matches all devices */
{ }, /* Terminating zero entry */
}; MODULE_DEVICE_TABLE(input, evdev_ids); static struct input_handler evdev_handler = {
.event = evdev_event,
.events = evdev_events,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.legacy_minors = true,
.minor = EVDEV_MINOR_BASE,
.name = "evdev",
.id_table = evdev_ids,
}; static int __init evdev_init(void)
{
return input_register_handler(&evdev_handler);
} static void __exit evdev_exit(void)
{
input_unregister_handler(&evdev_handler);
} module_init(evdev_init);
module_exit(evdev_exit);

这里一些相关的代码我也顺便贴出来了,再来一个:

 int input_register_handler(struct input_handler *handler)
{
struct input_dev *dev;
int error; error = mutex_lock_interruptible(&input_mutex);
if (error)
return error; INIT_LIST_HEAD(&handler->h_list); list_add_tail(&handler->node, &input_handler_list); list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler); input_wakeup_procfs_readers(); mutex_unlock(&input_mutex);
return ;
}

这个函数不用多说了,注册event handler这个input_haner的实例,并且在注册之后迅速遍历了一下input_dev_list链表,查找所有的input_dev设备,看这个input_handler是否支持它.那么回到我们上面的遗留问题看看input_handler的connect函数.对于event handler来说,这个函数就是evdev_connect.在drivers/input/evdev.c中:

 static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
struct evdev *evdev;
int minor;
int dev_no;
int error; minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);
if (minor < ) {
error = minor;
pr_err("failed to reserve new minor: %d\n", error);
return error;
} evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
if (!evdev) {
error = -ENOMEM;
goto err_free_minor;
} INIT_LIST_HEAD(&evdev->client_list);
spin_lock_init(&evdev->client_lock);
mutex_init(&evdev->mutex);
init_waitqueue_head(&evdev->wait);
evdev->exist = true; dev_no = minor;
/* Normalize device number if it falls into legacy range */
if (dev_no < EVDEV_MINOR_BASE + EVDEV_MINORS)
dev_no -= EVDEV_MINOR_BASE;
dev_set_name(&evdev->dev, "event%d", dev_no); /*初始化handle,每个evdev中都有一个handle*/
evdev->handle.dev = input_get_device(dev);/*这里就将handle的dev指针指向了input_dev*/
evdev->handle.name = dev_name(&evdev->dev);
evdev->handle.handler = handler;/*这里将handle的handler指向了当前的input_handler.注意本函数evdev_connect,可能是在在输入设备注册的时候
在input_register_device函数中调用input_attach_handler的时候调用;也可能是在输入设备的处理方法input_handler时在input_register_handler
函数中也会用到input_attach_handler函数,就会调用本函数.这里就很明显了,本函数就将input_handler和input_dev都放在input_handle中统一管理*/
evdev->handle.private = evdev; /*初始化evdev中的内嵌device*/
evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);
evdev->dev.class = &input_class;
evdev->dev.parent = &dev->dev;
evdev->dev.release = evdev_free;
device_initialize(&evdev->dev); /*注册handle,主要将handle链接到input_dev和handler的h_list链表中去*/
error = input_register_handle(&evdev->handle);
if (error)
goto err_free_evdev; cdev_init(&evdev->cdev, &evdev_fops);
evdev->cdev.kobj.parent = &evdev->dev.kobj;
error = cdev_add(&evdev->cdev, evdev->dev.devt, );
if (error)
goto err_unregister_handle; error = device_add(&evdev->dev);
if (error)
goto err_cleanup_evdev; return ; err_cleanup_evdev:
evdev_cleanup(evdev);
err_unregister_handle:
input_unregister_handle(&evdev->handle);
err_free_evdev:
put_device(&evdev->dev);
err_free_minor:
input_free_minor(minor);
return error;
}

至此设备的注册完成!对应event handler,在/dev/input中将多出一个event(x)设备文件,对应一个evdev实例,应用程序打开它的话也就意味着通过event handler来和设备驱动层传递事件.

第54~60行这几行字符设备初始化,这里之后再将字符设备注册,然后我们打开event(x)设备文件的时候实际上就调用evdev_fops里定义的open回调函数.相应的操作函数也在evdev_fops中定义了.我们来看看evdev_fops.同样在drivers/input/evdev.c中:

 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,
};

首先来看打开event(x)设备文件,evdev_open函数.

 static int evdev_open(struct inode *inode, struct file *file)
{
struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev);
unsigned int bufsize = evdev_compute_buffer_size(evdev->handle.dev);
struct evdev_client *client;
int error; /*每当一个应用程序打开该文件都会生成一个client*/
client = kzalloc(sizeof(struct evdev_client) +
bufsize * sizeof(struct input_event),
GFP_KERNEL);
if (!client)
return -ENOMEM; client->bufsize = bufsize;
spin_lock_init(&client->buffer_lock);
client->evdev = evdev;
evdev_attach_client(evdev, client);/*将client链入evdev的client_list中*/ error = evdev_open_device(evdev);
if (error)
goto err_free_client; file->private_data = client;
nonseekable_open(inode, file); return ; err_free_client:
evdev_detach_client(evdev, client);
kfree(client);
return error;
}

第20行调用evdev_open_device来打开evdev(如果将inpit_dev抽象为一个父对象,这其实就是一个子对象,或者说这个结构封装了一个具体的实例化的input_dev).

 static int evdev_open_device(struct evdev *evdev)
{
int retval; retval = mutex_lock_interruptible(&evdev->mutex);
if (retval)
return retval; if (!evdev->exist)
retval = -ENODEV;
else if (!evdev->open++) {/*如果是第一次打开该设备,则要执行输入设备的open*/
retval = input_open_device(&evdev->handle);
if (retval)
evdev->open--;
} mutex_unlock(&evdev->mutex);
return retval;
}

这里终于看到了输入子系统核心层的接口input_open_device,此函数就是内核为我们做好的初始化input_dev的函数接口.

 int input_open_device(struct input_handle *handle)
{
struct input_dev *dev = handle->dev;
int retval; retval = mutex_lock_interruptible(&dev->mutex);
if (retval)
return retval; if (dev->going_away) {
retval = -ENODEV;
goto out;
} handle->open++; if (!dev->users++ && dev->open)/*如果是第一次打开此设备(当前input_dev的使用者数为0),并且input_dev中定义的open函数不为空*/
retval = dev->open(dev);//执行input_dev中定义的open,完成设备的初始化 if (retval) {
dev->users--;
if (!--handle->open) {
/*
* Make sure we are not delivering any more events
* through this handle
*/
synchronize_rcu();
}
} out:
mutex_unlock(&dev->mutex);
return retval;
}

至于具体的如何初始化input_dev,这个是具体的输入设备去实现的.我们这里不分析了.现在来看看,对于一个event(x)设备文件的.

 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;
size_t read = ;
int error; if (count != && count < input_event_size())
return -EINVAL; for (;;) {
if (!evdev->exist)/*evdev没有定义返回函数,直接返回吧,因为我们下面必须要用到这个函数*/
return -ENODEV; if (client->packet_head == client->tail &&
(file->f_flags & O_NONBLOCK))/*无数据并且是非阻塞状态,则直接返回,说明没什么要处理的*/
return -EAGAIN; /*
* count == 0 is special - no IO is done but we check
* for error conditions (see above).
*/
if (count == )
break; while (read + input_event_size() <= count &&
evdev_fetch_next_event(client, &event)) { if (input_event_to_user(buffer + read, &event))
return -EFAULT; read += input_event_size();
} if (read)
break; if (!(file->f_flags & O_NONBLOCK)) {/*如果是可阻塞状态的话,则等待在wait队列上.直到有数据要被处理,当前进程才被唤醒.这很好理解,既然是
输入设备,读的话比如读按键,那么必须要有硬件设备有按键按下才会返回按键值,这里还是处于事件处理层,应用程序在这里休眠,那么谁来唤醒?
当然是有按键按下才去唤醒,因此这个工作就交给了设备驱动层,那么找到这个唤醒呢,直接去找不好找,那么可以直接搜索evdev->wait,搜索结果
可知evdev->wait在evdev_event()函数中被唤醒*/
error = wait_event_interruptible(evdev->wait,
client->packet_head != client->tail ||
!evdev->exist);
if (error)
return error;
}
} return read;
}

注释中说的很清楚,evdev_event()会唤醒此处的读按键进程.那么evdev_event()又是被谁调用?显然是设备驱动层,现在看一个设备层例子,内核中有个按键的例子,gpiokey.c,这只是个例子不针对任何设备,在gpiokey.c终端处理函数里面.

 static void evdev_pass_values(struct evdev_client *client,
const struct input_value *vals, unsigned int count,
ktime_t mono, ktime_t real)
{
struct evdev *evdev = client->evdev;
const struct input_value *v;
struct input_event event;
bool wakeup = false; event.time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ?
mono : real); /* Interrupts are disabled, just acquire the lock. */
spin_lock(&client->buffer_lock); for (v = vals; v != vals + count; v++) {
event.type = v->type;
event.code = v->code;
event.value = v->value;
__pass_event(client, &event);
if (v->type == EV_SYN && v->code == SYN_REPORT)
wakeup = true;
} spin_unlock(&client->buffer_lock); if (wakeup)
wake_up_interruptible(&evdev->wait);
} /*
* Pass incoming events to all connected clients.
*/
static void evdev_events(struct input_handle *handle,
const struct input_value *vals, unsigned int count)
{
struct evdev *evdev = handle->private;
struct evdev_client *client;
ktime_t time_mono, time_real; time_mono = ktime_get();
time_real = ktime_sub(time_mono, ktime_get_monotonic_offset()); rcu_read_lock(); client = rcu_dereference(evdev->grab); if (client)
evdev_pass_values(client, vals, count, time_mono, time_real);
else
list_for_each_entry_rcu(client, &evdev->client_list, node)
evdev_pass_values(client, vals, count,
time_mono, time_real); rcu_read_unlock();
} /*
* Pass incoming event to all connected clients.
*/
static void evdev_event(struct input_handle *handle,
unsigned int type, unsigned int code, int value)
{
struct input_value vals[] = { { type, code, value } }; evdev_events(handle, vals, );
}

好了,就贴到这里,input子系统的大体脉络以及比较清楚了.

input子系统分析之三:驱动模块的更多相关文章

  1. SPI子系统分析之三:驱动模块

    内核版本:3.9.5 SPI核心层(平台无关) SPI子系统初始化的第一步就是将SPI总线注册进内核,并且在/sys下创建一个spi_master的类,以后注册的从设备都将挂接在该总线下. 下列函数位 ...

  2. Linux input子系统分析

    输入输出是用户和产品交互的手段,因此输入驱动开发在Linux驱动开发中很常见.同时,input子系统的分层架构思想在Linux驱动设计中极具代表性和先进性,因此对Linux input子系统进行深入分 ...

  3. linux kernel input 子系统分析

    Linux 内核为了处理各种不同类型的的输入设备 , 比如说鼠标 , 键盘 , 操纵杆 , 触摸屏 , 设计并实现了一个对上层应用统一的试图的抽象层 , 即是Linux 输入子系统 . 输入子系统的层 ...

  4. input子系统分析

    ------------------------------------------ 本文系本站原创,欢迎转载! 转载请注明出处:http://ericxiao.cublog.cn/ -------- ...

  5. input子系统分析之一:框架

    内核版本:3.9.5 输入设备总类繁杂,包括按键,键盘,触摸屏,鼠标,摇杆等等,它们本身都是字符设备,不过内核为了能将这些设备的共性抽象出来,简化驱动的开发,建立了一个Input子系统.Input子系 ...

  6. input子系统分析(转)

    转自:http://www.linuxidc.com/Linux/2011-09/43187.htm 作者:作者:YAOZHENGUO2006 Input子系统处理输入事务,任何输入设备的驱动程序都可 ...

  7. input子系统分析之二:数据结构

    内核版本:3.9.5 1. input_dev,用来标识输入设备 struct input_dev { const char *name; const char *phys; const char * ...

  8. 内核input子系统分析

    打开/driver/input/input.c 这就是input代码的核心 找到 static int __init input_init(void) { err = class_register(& ...

  9. SPI子系统分析之四:驱动模块

    内核版本:3.9.5 SPI控制器层(平台相关) 上一节讲了SPI核心层的注册和匹配函数,它是平台无关的.正是在核心层抽象了SPI控制器层的相同部分然后提供了统一的API给SPI设备层来使用.我们这一 ...

随机推荐

  1. 【spring源码学习】spring的aop目标对象中进行自我调用,且需要实施相应的事务定义的解决方案

    转载:http://www.iteye.com/topic/1122740 1.预备知识 aop概念请参考[http://www.iteye.com/topic/1122401]和[http://ji ...

  2. 申请apple开发人员账号的波折

    版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/xiebaochun/article/details/37578395 是的.po主要搞ios开发了, ...

  3. requireJS多页面应用实例

    本文是requireJS的一些知识点的总结,配上多页面应用中的实例分析. 本案例的目录结构如下: requireJS API的三个主要函数:define(创建模块),require(加载模块),con ...

  4. 一篇文章学LINQ(原创)

    本篇文章主要介绍linq的基本用法,采用sql和linq比较的方式由浅入深进行学习, 注意:此文章是根据真实表来进行案例说明,表结构如下:  表1:    Student(学生表)           ...

  5. SharePoint无法搜索解决

    重启服务器后,站点搜索时提示错误,无法进行搜索,进入管理中心搜索管理看到,"查询处理"出错. 解决方法: 停止搜索服务,重新启动,如下图所示 重启服务后,过了几分钟重新查询,查询正 ...

  6. Exception in thread "main" java.lang.UnsupportedClassVersionError: com/crack

    执行一个jar文件的时候抛异常了 Exception in thread "main" java.lang.UnsupportedClassVersionError: com/cr ...

  7. json.loads(s) returns error message like this: ValueError: Invalid control character at: line 1 column 33 (char 33)

    json.loads(s) returns error message like this: ValueError: Invalid control character at: line 1 colu ...

  8. 数据结构与算法JavaScript描述——栈的使用

    有一些问题特别适合用栈来解决.本节就介绍几个这样的例子.   1) 数制间的相互转换 可以利用栈将一个数字从一种数制转换成另一种数制.假设想将数字n 转换为以b 为基数的数字,实现转换的算法如下. 使 ...

  9. 简单的SOCKET例子

    定义实例socket.socket(),如果括号里不写参数,默认为IPV4+TCP 我们猜测客户端的完整代码如下: 同样我们猜测服务端的代码如下: 实际上运行客户端代码: 说明在py3里,网络编程发送 ...

  10. 浅谈使用 PHP 进行手机 APP 开发(API 接口开发)

    做过 API 的人应该了解,其实开发 API 比开发 WEB 更简洁,但可能逻辑更复杂,因为 API 其实就是数据输出,不用呈现页面,所以也就不存在 MVC(API 只有 M 和 C),那么我们来探讨 ...