内核版本: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. 使用OPtional的orElse()问题

    使用OPtional的orElse()问题 项目中有这样一段代码: return Optional.ofNullable(service.A()).orElse(service.B()) 1 功能显而 ...

  2. CSU1612Destroy Tunnels(强连通)传递闭包

    Destroy Tunnels 原来早忘记了离散里含有这么一个叫传递闭包的东西 矩阵A的闭包B = A U A^2 U A^3 U ... 所以这里直接如果A[i][j]!= 0,建边i->j跑 ...

  3. 关于MySQL 通用查询日志和慢查询日志分析

    MySQL中的日志包括:错误日志.二进制日志.通用查询日志.慢查询日志等等.这里主要介绍下比较常用的两个功能:通用查询日志和慢查询日志. 1)通用查询日志:记录建立的客户端连接和执行的语句. 2)慢查 ...

  4. elixir 调用erlang 代码

    备注:    项目比较简单,主要是elixir 混合erlang 代码,elixir 调用erlang 模块方法   1. 初始化项目   mix new erlangelixirdemo 项目结构如 ...

  5. Protel 99 铺铜的一个坑 Pour Over Same

    Protel 99 铺铜的一个坑 Pour Over Same 好久没用 Protel 99 了,修改了一个旧的 PCB 文件. 需要修改线路,由于改了线路需要重新铺铜,得重新画铺铜的边框. 以下这个 ...

  6. python中高阶函数学习笔记

    什么是高阶函数 变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数 def fun(x, y, f): print f(x), f(y) fun ...

  7. 基于Oracle的EntityFramework的WEBAPI2的实现(二)——使用DbFirst

    之所以使用DbFirst而没有使用CodeFirst是因为考虑到现实的情况中,我们之所以会选择oracle而不是SQL SERVER,一方面是因为之前公司已经在使用Oracle,而且有好多我们需要用到 ...

  8. .NET基础复习一

    . ] ; 等号左边开辟了一个小的栈的空间.等号右边在堆空间开辟了5个空间,会将堆里开辟的第一个空间给地址赋值给栈里的空间 ,]; productList[,] ="1号"; pr ...

  9. cocos2d js 利用texture packer生成sprite

    cc.spriteFrameCache.addSpriteFrames(res.winLose_plist,res.winLose_png); var frame = cc.spriteFrameCa ...

  10. jquery json string 转换 合并

    Jquery 1.9.1 var BODY = { "recipients": { "values": [] }, "subject": ' ...