input 输入子系统分析

Posted liushuhe1990

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了input 输入子系统分析相关的知识,希望对你有一定的参考价值。

//input 输入子系统分析
//刘术河
2016.08.22

linux-2.6.39-at91-2016.08.11-lshdriversinputInput.c
该文件下有input_register_device 和 input_register_handler 函数接口
0.先分析input.c的核心层架构
input_init(void)
class_register(&input_class); //注册一个类
register_chrdev(INPUT_MAJOR, "input", &input_fops); //注册一个字符设备,注册一个fops结构体
0.1 fops结构体
static const struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
.llseek = noop_llseek,
};
0.2可见fops只提供一个open函数input_open_file
//该open函数,是在应用程序open设备节点的时候,才会调用到这个input_open_file
input_open_file
struct input_handler *handler;
handler = input_table[iminor(inode) >> 5];
new_fops = fops_get(handler->fops);
new_fops->open(inode, file); //这里调用open函数
//注:new_fops是从input_table列表里查找出来的,搜索它是在哪里被赋值的
//发现是在input_register_handler里
// input_table[handler->minor >> 5] = handler;


1.分析input_register_handler
有9个驱动程序调用
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);
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);
1.1这里只分析Evdev.c,他相对简单,Keyboard.c涉及到tty太复杂不去分析
//这是handler的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,
};
//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,
};
static const struct input_device_id evdev_ids[] = {
{ .driver_info = 1 }, /* Matches all devices */
{ }, /* Terminating zero entry */
};
//Evdev.c初始化函数
module_init(evdev_init);
evdev_init
input_register_handler(&evdev_handler);
input_table[handler->minor >> 5] = handler; //假设minor是64则64>>5=64/32=2,表示input_table[2]=&evdev_handler
list_add_tail(&handler->node, &input_handler_list); //将evdev_handler加入input_handler_list列表,注意这个handler->node是添加到列表
//尾部,所以handler->node的next、prev会自动的被赋值故可以不初始化它
__list_add(new, head->prev, head);
__list_add(struct list_head *new,struct list_head *prev,struct list_head *next)
//注:new=handler->node,prev=链表头的前驱,next=链表头
next->prev = new; //next->prev:指向尾节点,即将链表头前驱指向新的节点
new->next = next; //新节点的后驱指向链表头
new->prev = prev; //新节点的前驱,指向之前的尾节点
prev->next = new; //之前尾节点的后驱,指向新添加的节点
//这就形成一个新的双向循环链表
list_for_each_entry(dev, &input_dev_list, node) //从input_dev_list开始,循环遍历dev中的node子项,返回一个dev的指针
input_attach_handler(dev, handler); //如果在设备链表input_dev_list找到对应的dev设备,就调用input_attach_handler(dev, handler);
const struct input_device_id *id;
input_match_device(handler, dev); //这里是看dev和handler能否匹配
if (!handler->match || handler->match(handler, dev))
return id;
//这里如果handler->match不存在,或者handler->match(handler, dev)返回值非0,就返回ID
//必须能匹配,否则这里不会执行
handler->connect(handler, dev, id); //这里相当于 .connect = evdev_connect,
evdev_connect
struct evdev *evdev;
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
init_waitqueue_head(&evdev->wait); //初始化,一个等待队列
dev_set_name(&evdev->dev, "event%d", minor); //设置输入事件名字
evdev->handle.dev = input_get_device(dev); //获取input_dev结构体
evdev->handle.handler = handler; //这里evdev->handle.handler=evdev_handler
evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor); //分配设备号
//重点
input_register_handle(&evdev->handle);
struct input_handler *handler = handle->handler;
struct input_dev *dev = handle->dev;
list_add_tail_rcu(&handle->d_node, &dev->h_list); //将handle里的d_node设备放进input_dev的设备链表尾部
list_add_tail_rcu(&handle->h_node, &handler->h_list); //将handle里的h_node:handler处理者,放进handler的设备链表尾部
evdev_install_chrdev(evdev);
evdev_table[evdev->minor] = evdev; //将evdev放在evdev_table数组里
device_add(&evdev->dev);
//注:这里又涉及到另一条总线 bus---device---driver
//先不深入分析这条总线
bus_add_device(dev); //将设备添加到总线
bus_probe_device(dev); //在总线上探测设备


2.分析到这里,input_register_handler基本就分析完了,一路跟踪到最后,发现
就是在handler->connect(handler, dev, id); 里初始化一个init_waitqueue_head(&evdev->wait); 事件等待队列
那么设备和驱动如何调用起来的?
2.1应该是硬件会唤醒这个事件等待队列 evdev->wait
2.2分析 evdev->wait 被调用过程
搜索evdev->wait,发现 在 evdev_event、evdev_hangup 里有wake_up_interruptible(&evdev->wait);
在 evdev_read、有wait_event_interruptible(evdev->wait,client->head != client->tail || !evdev->exist);
在 evdev_poll、有poll_wait(file, &evdev->wait, wait);
在evdev.c的 static struct input_handler evdev_handler = {.event = evdev_event,};有evdev_event这个函数,所以应该是在
evdev_event
wake_up_interruptible(&evdev->wait); //唤醒等待队列
那么在哪里发生休眠的?
分析在static const struct file_operations evdev_fops = {.read = evdev_read, .poll = evdev_poll,}
应该在evdev_read->wait_event_interruptible(evdev->waitclient->head != client->tail || !evdev->exist);
或者在evdev_poll->poll_wait(file, &evdev->wait, wait);这两个情况发生的休眠

3.分析到这里,基本弄清楚 input_register_handler 干的工作,
3.1 填充 input_table[handler->minor >> 5] = handler;
3.2 注册handler到input_handler_list链表,并且遍历input_dev_list链表,遍历input_dev_list链表会调用input_attach_handler函数,
这个函数就是看handler和dev是否能匹配,能匹配做4件事
3.2.1. init_waitqueue_head(&evdev->wait); //初始化一个等待队列
3.2.2. input_register_handle(&evdev->handle); //注册一个handle结构,注意不是handler
3.2.3. evdev_install_chrdev(evdev); //填充evdev_table[]
3.2.4. device_add(&evdev->dev); //真正把输入设备注册进系统的bus上

4.分析input_register_handler填充的 input_table 用途
4.1 搜索发现在 input_open_file里用到input_table,而input_open_file在input_init()初始化里
static const struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
.llseek = noop_llseek,
};
input_init
class_register(&input_class);
register_chrdev(INPUT_MAJOR, "input", &input_fops);
//当有设备打开此设备会调用到input_open_file函数
input_open_file(struct inode *inode, struct file *file)
struct input_handler *handler;
struct file_operations *new_fops = NULL;
handler = input_table[iminor(inode) >> 5]; //这里发现是将次设备号inode>>5=inode/32 = 2,即从input_table[2]取出一个handler
//这不就是input_register_handler注册时,填充的吗?注意看
//input_table[handler->minor >> 5] = handler; 太秒了!!!
new_fops = fops_get(handler->fops); //从handler得到handler的fops结构体即evdev_fops
new_fops->open(inode, file); //调用fops的open函数,即evdev_fops.open
evdev_open //
struct evdev *evdev;
struct evdev_client *client;
evdev = evdev_table[i]; //这个evdev_table[]就是在evdev_install_chrdev函数里填充的
//evdev_table[evdev->minor] = evdev;这里面有handle.handler
client->evdev = evdev; //注意:这个evdev.handle.handler=evdev_handler,巧妙
evdev_attach_client(evdev, client); //将client->node添加到evdev->client_list链表尾部
evdev_open_device(evdev);
{
if (!evdev->exist)
retval = -ENODEV;
else if (!evdev->open++) { //evdev_connect初始化的时候没有设置evdev.open所以这里evdev.open=0
retval = input_open_device(&evdev->handle); //就调用evdev->handle
if (retval)
evdev->open--;
}
}
//这里接着分析input_open_device(&evdev->handle);
input_open_device(&evdev->handle);
struct input_dev *dev = handle->dev; //这里handle->dev = 就是evdev->handle.dev = input_get_device(dev);
{
if (!dev->users++ && dev->open) //这就是具体设备的硬件open函数,这里是判断input_register_device注册
//的设备有没有open成员,有就调用
//所以就要分析具体的设备,这里举个例子Lpc32xx_ts.c
//发现在lpc32xx_ts_probe有这2句话,具体分析等我写input_register_device
//input = input_allocate_device();
//input->open = lpc32xx_ts_open;
retval = dev->open(dev); //所以这里调用lpc32xx_ts_open
lpc32xx_ts_open
lpc32xx_setup_tsc(tsc);
//都是这种寄存器
}

5.分析input_register_device调用
Lpc32xx_ts.c 就分析这个按键驱动程序
5.1 这个按键涉及到2个框架 平台设备、输入子系统
5.2 从入口函数进去
module_init(lpc32xx_ts_init);
platform_driver_register(&lpc32xx_ts_driver);
//这里就是注册一个平台设备,平台设备的分析我已经做过了,这里不深入进去分析
//主要分析输入子系统,该platform_driver_register会调用lpc32xx_ts_driver.probe探测函数
static struct platform_driver lpc32xx_ts_driver = {
.probe = lpc32xx_ts_probe,
.remove = __devexit_p(lpc32xx_ts_remove),
.driver = {
.name = MOD_NAME,
.owner = THIS_MODULE,
.pm = LPC32XX_TS_PM_OPS,
},
};

5.2 直接分析 lpc32xx_ts_driver.probe = lpc32xx_ts_probe,
lpc32xx_ts_probe
struct input_dev *input;
//分配一个input结构体
input = input_allocate_device();
input->name = MOD_NAME;
input->id.bustype = BUS_HOST;
input->open = lpc32xx_ts_open;
input->close = lpc32xx_ts_close;
//设置能产生的事件类型
input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
//设置能产生的事件
input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
//初始化input_dev结构体
input_set_abs_params(input, ABS_X, LPC32XX_TSC_MIN_XY_VAL,LPC32XX_TSC_MAX_XY_VAL, 0, 0);
input_set_abs_params(input, ABS_Y, LPC32XX_TSC_MIN_XY_VAL,LPC32XX_TSC_MAX_XY_VAL, 0, 0);
input_set_drvdata(input, tsc);
//注册触摸屏中断
request_irq(tsc->irq, lpc32xx_ts_interrupt,IRQF_DISABLED, pdev->name, tsc);
//注册输入设备
input_register_device(input);
struct input_handler *handler;
__set_bit(EV_SYN, dev->evbit);
__clear_bit(KEY_RESERVED, dev->keybit);
init_timer(&dev->timer);
dev->timer.function = input_repeat_key;
device_add(&dev->dev);
list_add_tail(&dev->node, &input_dev_list);
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
input_match_device(handler, dev);
handler->connect(handler, dev, id);

6.分析触摸屏中断函数
lpc32xx_ts_interrupt
struct input_dev *input = tsc->dev;
input_report_abs(input, ABS_X, (xs[1] + xs[2]) / 2);
input_report_abs(input, ABS_Y, (ys[1] + ys[2]) / 2);
input_report_key(input, BTN_TOUCH, 1);
//只分析一个上报事件,其他类似
input_report_key(input, BTN_TOUCH, 0);
input_event(dev, EV_KEY, code, !!value);
input_handle_event(dev, type, code, value);
input_pass_event(dev, type, code, value);
struct input_handler *handler;
struct input_handle *handle;
handler = handle->handler;
handler->event(handle, type, code, value); //其实就是evdev.c里的evdev_handler.event = evdev_event
input_sync(input);
6.1 分析evdev_event函数
evdev_event
struct evdev_client *client;
client = rcu_dereference(evdev->grab);
list_for_each_entry_rcu(client, &evdev->client_list, node)
evdev_pass_event(client, &event);
wake_up_interruptible(&evdev->wait); //注意这个等待队列,就是在注册input_register_handler初始化的
//init_waitqueue_head(&evdev->wait); //初始化一个等待队列

6.2 分析evdev->wait)是在哪里休眠的?
应该是应用程序读数据,没有数据时休眠的,直接分析evdev.c的 evdev_handler.evdev_fops.read = evdev_read
evdev_read
wait_event_interruptible(evdev->wait,client->head != client->tail || !evdev->exist) //里面就有这句休眠语句
6.3 到这里基本input输入子系统流程大致分析明白












































 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
























































































































































































































































































































以上是关于input 输入子系统分析的主要内容,如果未能解决你的问题,请参考以下文章

输入子系统框架分析

Linux input子系统编程分析与模板

input_subsys 输入子系统框架分析

linux input输入子系统应用分析

输入子系统分析

Linux输入子系统(Input Subsystem)