input 输入子系统分析

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

linux-2.6.39-at91-2016.08.11-lsh\drivers\input\Input.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 (drivers\input):	return input_register_handler(&apmpower_handler);
Evbug.c (drivers\input):	return input_register_handler(&evbug_handler);
Evdev.c (drivers\input):	return input_register_handler(&evdev_handler);
Joydev.c (drivers\input):	return input_register_handler(&joydev_handler);
Keyboard.c (drivers\tty\vt):	error = input_register_handler(&kbd_handler);
Kgdboc.c (drivers\tty\serial):	if (input_register_handler(&kgdboc_reset_handler) == 0)
Mac_hid.c (drivers\macintosh):	err = input_register_handler(&mac_hid_emumouse_handler);
Mousedev.c (drivers\input):	error = input_register_handler(&mousedev_handler);
Sysrq.c (drivers\tty):	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)

__list_add实现 ,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基本就分析完了,一路跟踪到最后,发现就是在connect里初始化一个时间等待队列

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注册时,填充的吗?注意看

nput_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结构体
 

lpc32xx_ts_probe
struct input_dev *input;

//设置能产生的事件类型

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输入子系统流程大致分析明白












































 

posted on 2018-09-08 13:38  紫枫术河  阅读(309)  评论(0)    收藏  举报

导航