一、前言
前面我们分析了android的input子系统的android部分的代码,下面我们继续来分析kernel部分的,对于这个系统kernel部分和标准linux差别不大,
google在原linux基础上增加了一些代码以使其更适合手持设备,比如支持多点触控设备,支持android特有的4个按键等等。我们会一步一步的分析
内核代码,来分析出input系统的工作原理。
二、input设备类的注册
在input系统android部分我们知道了,在android层的eventhub中,建立了一个epoll,来监控/dev/input目录下有效的input设备所有事件,如果有
事件输入的话就会读出当前事件,然后交给android上层处理。所使用的方法也是 open(),read(),close()的标准方法。因此我们要分析这个input
系统的话,要从/dev/input下的设备文件分的实现析其,他的实现代码在android\kernel\drivers\input\input.c文件中,input也是内核的一个模块
我们从模块的初始化看起subsys_initcall(input_init);
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; |
} V
/****************************************************************
struct class input_class = {
.name = "input",
.devnode = input_devnode,
};
********************************************************************/
err = input_proc_init(); //建立input类proc文件系统 见下面
if (err)
goto fail1;
err = register_chrdev(INPUT_MAJOR, "input", &input_fops);//注册字符设备驱动,此中包含了设备的具体操作函数 见下面
if (err) {
pr_err("unable to register char major %d", INPUT_MAJOR);
goto fail2;
}
return 0;
fail2: input_proc_exit();
fail1: class_unregister(&input_class);
return err;
}
1.proc文件系统建立
我们首先对proc文件系统做一个介绍:/proc 文件系统是一个特殊的软件创建的文件系统, 内核用来输出消息到外界./proc 下的每个
文件都绑到一个内核函数上, 当文件被读的时候即时产生文件内容. 我们已经见到一些这样的文件起作用; 例如, /proc/modules, 常
常返回当前已加载的模块列表./proc 在 Linux 系统中非常多地应用. 很多现代 Linux 发布中的工具, 例如ps, top, 以及 uptime,
从 /proc 中获取它们的信息. 一些设备驱动也通过/proc 输出信息, 你的也可以这样做. /proc 文件系统是动态的, 因此你的模块
可以在任何时候添加或去除条目.-------Linux 设备驱动 第三版
input_proc_init()代码在android\kernel\drivers\input\input.c中
static int __init input_proc_init(void)
{
struct proc_dir_entry *entry;
proc_bus_input_dir = proc_mkdir("bus/input", NULL);//为input类创建/proc/bus/input目录
if (!proc_bus_input_dir) |
return -ENOMEM; V
/*********************************************************************************
proc_mkdir()函数定义在android\kernel_imx\fs\proc\generic.c中,这个函数调用
调用proc_mkdir_mode(name, S_IRUGO | S_IXUGO, parent);
这个函数定义也在这个文件中
struct proc_dir_entry *proc_mkdir_mode(const char *name, mode_t mode,
struct proc_dir_entry *parent)
{
struct proc_dir_entry *ent;
ent = __proc_create(&parent, name, S_IFDIR | mode, 2);
if (ent) {
if (proc_register(parent, ent) < 0) {
kfree(ent);
ent = NULL;
}
}
return ent;
}
函数调用__proc_create()定义在本文件中
static struct proc_dir_entry *__proc_create(struct proc_dir_entry **parent,
const char *name,
mode_t mode,
nlink_t nlink)
{
..................
if (xlate_proc_name(name, parent, &fn) != 0)//将name整理成/proc/name
goto out;
...................
//分配内存
ent = kmalloc(sizeof(struct proc_dir_entry) + len + 1, GFP_KERNEL);
if (!ent) goto out;
//初始化这个这个proc子系统
memset(ent, 0, sizeof(struct proc_dir_entry));
memcpy(((char *) ent) + sizeof(struct proc_dir_entry), fn, len + 1);
ent->name = ((char *) ent) + sizeof(*ent);
ent->namelen = len;
ent->mode = mode;
ent->nlink = nlink;
atomic_set(&ent->count, 1);
ent->pde_users = 0;
spin_lock_init(&ent->pde_unload_lock);
ent->pde_unload_completion = NULL;
INIT_LIST_HEAD(&ent->pde_openers);//得到队列回首地址
out:
return ent;
}
到了这里我们就创建好了这个proc文件系统的子目录,下面继续创建设备文件
**************************************************************************************/
entry = proc_create("devices", 0, proc_bus_input_dir, //创建devices这个设备文件
&input_devices_fileops);
if (!entry)
goto fail1;
entry = proc_create("handlers", 0, proc_bus_input_dir,//创建handlers这个设备文件
&input_handlers_fileops);
if (!entry)
goto fail2;
return 0;
fail2: remove_proc_entry("devices", proc_bus_input_dir);
fail1: remove_proc_entry("bus/input", NULL);
return -ENOMEM;
}
这里创建了两个proc文件
a) 我们先看entry = proc_create("devices", 0, proc_bus_input_dir,&input_devices_fileops);
proc_create()是在proc_bus_input_dir创建名字为"devices"的proc文件,这个文件支持的操作由
input_devices_fileops来定义。当我们对这个文件读、写、查找时就是调用了他里面相应的函数,因
此我们重点来看这个参数
static const struct file_operations input_devices_fileops = {
.owner = THIS_MODULE,
.open = input_proc_devices_open,
.poll = input_proc_devices_poll,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
从这里看出,这个文件支持open poll read seek release这5个操作,这里还涉及到seq_file这个内核文件接口
我们再来介绍一下它:由于特殊性, 在/proc 下的大文件的实现有点麻烦.一直以来, /proc 方法因为当输出数量
变大时的错误实现变得声名狼藉. 作为一种清理 /proc 代码以及使内核开发者活得轻松些的方法, 添加了 seq_file
接口. 这个接口提供了简单的一套函数来实现大内核虚拟文件.set_file 接口假定你在创建一个虚拟文件, 它涉及
一系列的必须返回给用户空间的项. 为使用 seq_file, 你必须创建一个简单的 "iterator" 对象, 它能在
序列里建立一个位置, 向前进, 并且输出序列里的一个项.---------摘自LDD3
总的来讲seq_file是小的的proc文件以链表的形式合成一个大的文件,而方便用户使用。
下面我们一一来分析这几个函数
.open = input_proc_devices_open 打开文件
static int input_proc_devices_open(struct inode *inode, struct file *file)
{
return seq_open(file, &input_devices_seq_ops);//根据input_devices_seq_ops 初始化一个seq_file
}
input_devices_seq_ops的定义
static const struct seq_operations input_devices_seq_ops = {
.start = input_devices_seq_start, //起始位输入设备链表input_dev_list的首地址. input_dev_list是系统维护的所有输入设备的一个链表,
//通过这个链表可以访问系统所有的input设备
.next = input_devices_seq_next, //根据input_dev_list链表查找下个设备
.stop = input_seq_stop,
.show = input_devices_seq_show, //从设备连表里提取出哪些内容到seq_file中
}; |
V
static int input_devices_seq_show(struct seq_file *seq, void *v)
{
。。。。。。
seq_printf(seq, "I: Bus=%04x Vendor=%04x Product=%04x Version=%04x\n", //首先会是设备厂家ID ,产品ID,驱动版本
dev->id.bustype, dev->id.vendor, dev->id.product, dev->id.version);
seq_printf(seq, "N: Name=\"%s\"\n", dev->name ? dev->name : ""); //设备名字
seq_printf(seq, "P: Phys=%s\n", dev->phys ? dev->phys : ""); //设备物理设备文件路径
seq_printf(seq, "S: Sysfs=%s\n", path ? path : ""); //设备sysfs中的路径
seq_printf(seq, "U: Uniq=%s\n", dev->uniq ? dev->uniq : ""); //啥东西不认识
seq_printf(seq, "H: Handlers="); // input链表中的路径
list_for_each_entry(handle, &dev->h_list, d_node)
seq_printf(seq, "%s ", handle->name);
seq_putc(seq, '\n');
input_seq_print_bitmap(seq, "PROP", dev->propbit, INPUT_PROP_MAX); //设备的各种信息
input_seq_print_bitmap(seq, "EV", dev->evbit, EV_MAX);
if (test_bit(EV_KEY, dev->evbit))
input_seq_print_bitmap(seq, "KEY", dev->keybit, KEY_MAX);
if (test_bit(EV_REL, dev->evbit))
input_seq_print_bitmap(seq, "REL", dev->relbit, REL_MAX);
if (test_bit(EV_ABS, dev->evbit))
input_seq_print_bitmap(seq, "ABS", dev->absbit, ABS_MAX);
if (test_bit(EV_MSC, dev->evbit))
input_seq_print_bitmap(seq, "MSC", dev->mscbit, MSC_MAX);
if (test_bit(EV_LED, dev->evbit))
input_seq_print_bitmap(seq, "LED", dev->ledbit, LED_MAX);
if (test_bit(EV_SND, dev->evbit))
input_seq_print_bitmap(seq, "SND", dev->sndbit, SND_MAX);
if (test_bit(EV_FF, dev->evbit))
input_seq_print_bitmap(seq, "FF", dev->ffbit, FF_MAX);
if (test_bit(EV_SW, dev->evbit))
input_seq_print_bitmap(seq, "SW", dev->swbit, SW_MAX);
seq_putc(seq, '\n');
kfree(path);
return 0;
}
也就是说当我们打开devices这个文件的时候,应该可以读到所有设备的上面这些配置信息。
.poll = input_proc_devices_poll,//poll查询等待
static unsigned int input_proc_devices_poll(struct file *file, poll_table *wait)
{
poll_wait(file, &input_devices_poll_wait, wait);
if (file->f_version != input_devices_state) {
file->f_version = input_devices_state;
return POLLIN | POLLRDNORM;
}
return 0;
}
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
这三个都是seq_file的标准操作函数,实现文件的读取查找和关闭,不多做说明。
总结来看,这个device这个文件就是input设备的描述文件,他从input_dev_list中读取设备信息,然后放到seq_file中
供使用者方便的查看设备。
b) entry = proc_create("handlers", 0, proc_bus_input_dir,&input_handlers_fileops);
这个proc文件还是看参数input_handlers_fileops
static const struct file_operations input_handlers_fileops = {
.owner = THIS_MODULE,
.open = input_proc_handlers_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
同样的
.read
= seq_read,
.llseek
= seq_lseek,
.release
= seq_release
这三个都是seq_file的标准操作函数不做说明,重点来看
.open
= input_proc_handlers_open,
static int input_proc_handlers_open(struct inode *inode, struct file *file)
{
return seq_open(file, &input_handlers_seq_ops);
}
参数input_handlers_seq_ops的定义
static const struct seq_operations input_handlers_seq_ops = {
.start = input_handlers_seq_start, //返回input_handler_list的首地址。每个input设备的输入事件,都可以附加它的处理程序,这个处理程序可以通过input_handler连接到设备上
//同一时间可以有不同的程序连接到不同的输入设备上,这个链表就是维护不同设备和其处理事件的连接结构体的链表
.next = input_handlers_seq_next, //链表下一个节点
.stop = input_seq_stop,
.show = input_handlers_seq_show, //打开这个文件所显示的信息
}; |
V
static int input_handlers_seq_show(struct seq_file *seq, void *v)
{
struct input_handler *handler = container_of(v, struct input_handler, node);
union input_seq_state *state = (union input_seq_state *)&seq->private;
seq_printf(seq, "N: Number=%u Name=%s", state->pos, handler->name);//编号 和 handler的名字
if (handler->filter)
seq_puts(seq, " (filter)"); //有没有filter
if (handler->fops)
seq_printf(seq, " Minor=%d", handler->minor);//表示 handler 实现的文件操作集,这里不是很重要。
seq_putc(seq, '\n');
return 0;
}
到了这里input设备类的proc文件系统已经建立完成了,总结一下,这个文件系统,提供了设备查询,设备事件处理程序关联方法,就是为了方便用户使用的一个接口。
2.注册字符设备
err = register_chrdev(INPUT_MAJOR, "input", &input_fops);注册一个字符设备,其主设备号为INPUT_MAJOR input设备,此设备号在0~256之间,由系统自动指定
这个字符设备的操作函数集input_fops:
static const struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
.llseek = noop_llseek,
};
input设备支持open 和 seek两种操作。
到了这里整个input设备类的注册已经完成了。总结一下,input设备类注册过程,首先注册一个类,然后注册proc文件系统,最后注册input的字符设备。
二、input设备注册
一个输入设备要加载到系统中,需要调用int input_register_device(struct input_dev *dev)
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;
/* Every input device generates EV_SYN/SYN_REPORT events. */
__set_bit(EV_SYN, dev->evbit); //强制加入 EV_SYN 事件
/* KEY_RESERVED is not supposed to be transmitted to userspace. */
__clear_bit(KEY_RESERVED, dev->keybit); //用户空间不支持KEY_RESERVED
/* 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", //input设备的名字叫 input0 input1 input2.。。。。
(unsigned long) atomic_inc_return(&input_no) - 1);
error = device_add(&dev->dev); //使用device_add()函数将input_dev包含的device结构注册到Linux设备模型中,并可以在sysfs文件系统中表现出来。
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;
}
list_add_tail(&dev->node, &input_dev_list); //将设备加入到device_add中
list_for_each_entry(handler, &input_handler_list, node) //将设备和handler连接起来
input_attach_handler(dev, handler);
input_wakeup_procfs_readers();
mutex_unlock(&input_mutex);
return 0;
}
到了这里input设备的注册已经完成了。设备注册就是生成设备文件,建立好各个目录中的设备连接,然后将handler 与之相连接
三、事件处理
在驱动中我们报告事件调用的是input_event()函数
void input_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
unsigned long flags;
if (is_event_supported(type, dev->evbit, EV_MAX)) {
spin_lock_irqsave(&dev->event_lock, flags);
add_input_randomness(type, code, value);//函数对事件发送没有一点用处,只是用来对随机数熵池增加一些贡献,因为按键输入是一种随机事件,
//所以对熵池是有贡献的。熵池是内核产生随机数用的一个框架,它是收集各种随机事件,然后尽量保证返回数据随机性。
input_handle_event(dev, type, code, value);
spin_unlock_irqrestore(&dev->event_lock, flags);
}
}
主要来看input_handle_event这个函数是,上报事件的进一步传递
static void input_handle_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
int disposition = INPUT_IGNORE_EVENT;
switch (type) {
case EV_SYN:
switch (code) {
case SYN_CONFIG:
disposition = INPUT_PASS_TO_ALL;
break;
case SYN_REPORT:
if (!dev->sync) {
dev->sync = true;
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
case SYN_MT_REPORT:
dev->sync = false;
disposition = INPUT_PASS_TO_HANDLERS;
break;
}
break;
case EV_KEY:
if (is_event_supported(code, dev->keybit, KEY_MAX) &&
!!test_bit(code, dev->key) != value) {
if (value != 2) {
__change_bit(code, dev->key);
if (value)
input_start_autorepeat(dev, code);//重复按键处理
else
input_stop_autorepeat(dev);
}
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
case EV_SW:
if (is_event_supported(code, dev->swbit, SW_MAX) &&
!!test_bit(code, dev->sw) != value) {
__change_bit(code, dev->sw);
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
case EV_ABS:
if (is_event_supported(code, dev->absbit, ABS_MAX))
disposition = input_handle_abs_event(dev, code, &value);
break;
case EV_REL:
if (is_event_supported(code, dev->relbit, REL_MAX) && value)
disposition = INPUT_PASS_TO_HANDLERS;
break;
case EV_MSC:
if (is_event_supported(code, dev->mscbit, MSC_MAX))
disposition = INPUT_PASS_TO_ALL;
break;
case EV_LED:
if (is_event_supported(code, dev->ledbit, LED_MAX) &&
!!test_bit(code, dev->led) != value) {
__change_bit(code, dev->led);
disposition = INPUT_PASS_TO_ALL;
}
break;
case EV_SND:
if (is_event_supported(code, dev->sndbit, SND_MAX)) {
if (!!test_bit(code, dev->snd) != !!value)
__change_bit(code, dev->snd);
disposition = INPUT_PASS_TO_ALL;
}
break;
case EV_REP:
if (code <= REP_MAX && value >= 0 && dev->rep[code] != value) {
dev->rep[code] = value;
disposition = INPUT_PASS_TO_ALL;
}
break;
case EV_FF:
if (value >= 0)
disposition = INPUT_PASS_TO_ALL;
break;
case EV_PWR:
disposition = INPUT_PASS_TO_ALL;
break;
}
if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
dev->sync = false;
//上面是检测上报事件的有效性
if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)//如果需要设备处理参与的话
dev->event(dev, type, code, value); //设备的事件处理函数是用户自定义的一个处理事件的函数。比如摁下caps lock键 键盘上会有一个灯打开和关闭 用的就是这个原理
if (disposition & INPUT_PASS_TO_HANDLERS)//如果需要handler参与
input_pass_event(dev, type, code, value); //进行下一步的传递
}
我们看input_pass_event
static void input_pass_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
struct input_handler *handler;
struct input_handle *handle;
rcu_read_lock();
handle = rcu_dereference(dev->grab);
if (handle)
handle->handler->event(handle, type, code, value);
else {
bool filtered = false;
list_for_each_entry_rcu(handle, &dev->h_list, d_node) {
if (!handle->open)
continue;
handler = handle->handler;
if (!handler->filter) {
if (filtered)
break;
handler->event(handle, type, code, value);
} else if (handler->filter(handle, type, code, value))
filtered = true;
}
}
rcu_read_unlock();
}
input_pass_event这个函数如果dev指定了handler的话就执行这个handler->event 如果没有的话就遍历所有handler_list 来查找合适handler->event
从这里可以看出如果我们要处理input_event的话,必须制定一个handler->event,事实上,我们从/dev/input目录下的设备文件读取事件也是这么做的,
我们接下来继续看。
首先我们必须找到设备 handler注册地方
我们知道读取事件都是在/dev/input/event0 ...设备上读去的,我们找到event设备的 创建函数在
android\kernel_imx\drivers\input\evdev.c中
static int __init evdev_init(void)
{
return input_register_handler(&evdev_handler);
}
我们看到这里调用input_register_handler注册了handler,重点来看这个参数
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,
};
这里event=evdev_event,这就是处理事件的函数具体的
static void evdev_event(struct input_handle *handle,
unsigned int type, unsigned int code, int value)
{
struct evdev *evdev = handle->private;
struct evdev_client *client;
struct input_event event;
struct timespec ts;
ktime_get_ts(&ts);
event.time.tv_sec = ts.tv_sec;
event.time.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
event.type = type;
event.code = code;
event.value = value;
rcu_read_lock();
client = rcu_dereference(evdev->grab); //得到一个client
if (client)
evdev_pass_event(client, &event); //把事件传递过去
else
list_for_each_entry_rcu(client, &evdev->client_list, node)
evdev_pass_event(client, &event);
rcu_read_unlock();
if (type == EV_SYN && code == SYN_REPORT)
wake_up_interruptible(&evdev->wait);
}
我们继续看evdev_pass_event
static void evdev_pass_event(struct evdev_client *client,
struct input_event *event)
{
/* Interrupts are disabled, just acquire the lock. */
spin_lock(&client->buffer_lock);
client->buffer[client->head++] = *event;
client->head &= client->bufsize - 1;
if (unlikely(client->head == client->tail)) {
/*
* This effectively "drops" all unconsumed events, leaving
* EV_SYN/SYN_DROPPED plus the newest event in the queue.
*/
client->tail = (client->head - 2) & (client->bufsize - 1);
client->buffer[client->tail].time = event->time;
client->buffer[client->tail].type = EV_SYN;
client->buffer[client->tail].code = SYN_DROPPED;
client->buffer[client->tail].value = 0;
client->packet_head = client->tail;
if (client->use_wake_lock)
wake_unlock(&client->wake_lock);
}
if (event->type == EV_SYN && event->code == SYN_REPORT) {
client->packet_head = client->head;
if (client->use_wake_lock)
wake_lock(&client->wake_lock);
kill_fasync(&client->fasync, SIGIO, POLL_IN);
}
spin_unlock(&client->buffer_lock);
}
这个函数就是把相应的input_event放入到 client->buffer 的队列中。到了这里,我们看到输入设备的事件处理,被放到一个队列里,那么然后呢?
我从event中读事件,是调用的 evdev的read函数,我们来看
evdev的操作集.fops
= &evdev_fops,
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,
};
可以看到读取函数为evdev_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,
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))//把事件拷贝到 usr的buff中
return -EFAULT;
retval += input_event_size();
}
if (retval == 0 && file->f_flags & O_NONBLOCK)
retval = -EAGAIN;
return retval;
}
到了这里所有的事件处理已经完成了。总结一下,事件读取设备文件evdev 是独立于input_device的一个内核模块,两者通过input_handler联系起来
当input_device收到驱动发送的event以后,会回调evdev的处理函数,把event复制到一个缓存中,当我们从evdev读取数据时,相应的处理函数会把
event复制到buff中,因此我们就读取到了事件。android读取到事件以后,就会进行android层面的input_event处理