输入服务子系统框架代码分析(韦东山的视频总结及针对linux-2.6.30.4)
来源:互联网 发布:淘宝宝贝摄影技巧 编辑:程序博客网 时间:2024/04/29 21:57
自己的总结有错误请评论,我们共同进步。
static struct input_handler *input_table[8];
/*入口函数*/static int __init input_init(void){
/*注册
*#define INPUT_MAJOR 13
*主设备号为13
*/
err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
}
static const struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
};
注册的file_operation怎么没有读写等的定义呢?
当读写等操作时,是怎么读写的呢?
下面来分析:
设备的主设备号都是13,就要靠次设备号来访问设备了,当打开设备文件时先访问的是input_open_file函数,
static int input_open_file(struct inode *inode, struct file *file)
{
struct input_handler *handler;
const struct file_operations *old_fops, *new_fops = NULL;
int err;
/*handler等于以次设备号右移5位为下标input_table[]的值 */
handler = input_table[iminor(inode) >> 5];
if (!handler || !(new_fops = fops_get(handler->fops)))
/*new_fops等于了 handler->fops当调用读写时就调用handler->fops的读写*/
{
err = -ENODEV;
goto out;
}
old_fops = file->f_op;
file->f_op = new_fops;
/*file->f_op等于new_fops,
*而new_fops等于handler- >fops结构,
*因此file_f_op 就等于handler->fops结构,
*读写就调用handler->fops的读 *写函数
*/
}
接下来又有问题那就是input_table中的数组项又有谁来定义,用source insight搜索可得到。
int input_register_handler(struct input_handler *handler)
{
struct input_dev *dev;
INIT_LIST_HEAD(&handler->h_list);
if (handler->fops != NULL) {
if (input_table[handler->minor >> 5]) {
retval = -EBUSY;
goto out;
}
input_table[handler->minor >> 5] = handler;
}
list_add_tail(&handler->node, &input_handler_list);
list_for_each_entry(dev, &input_dev_list, node);
input_attach_handler(dev, handler);
}
问题又来了,这个只是函数定义,并未真正的调用呀,还是不知道input_table的数组项的值,要想知道input_table的数组项的值,就必须看谁调用了input_register_handler,用source insight继续搜索可得到:
apm-power.c :return input_register_handler(&apmpower_handler);
evbug.c : return input_register_handler(&evbug_handler);
evdev.c : return input_register_handler(&evdev_handler);
joydev.c : return input_register_handler(&joydev_handler);
keyboard.c : error = input_register_handler(&kbd_handler);
mousedev.c : error = input_register_handler(&mousedev_handler);
rfkill-input.c: return input_register_handler(&&rfkill_handler);
这些设置了input_table数组项,但是它们设置了input_table哪一项就要看&*_hadler了
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,
};
evdev_handler.fops != NULL;
#define EVDEV_MINOR_BASE64
input_table[(&evdev_handler)->minor >> 5] = input_table[2] = &evdev_handler;
static struct input_handler mousedev_handler = {
.event =mousedev_event,
.connect =mousedev_connect,
.disconnect =mousedev_disconnect,
.fops =&mousedev_fops,
.minor =MOUSEDEV_MINOR_BASE,
.name ="mousedev",
.id_table =mousedev_ids,
};
mousedev_handler !=NULL;
#define MOUSEDEV_MINOR_BASE32
input_table[(&mousedev_handler)->minor >> 5] = input_table[1] = &mousedev_handler;
static struct input_handler joydev_handler = {
.event= joydev_event,
.connect= joydev_connect,
.disconnect= joydev_disconnect,
.fops= &joydev_fops,
.minor= JOYDEV_MINOR_BASE,
.name= "joydev",
.id_table= joydev_ids,
.blacklist= joydev_blacklist,
};
joydev_handler.fops != NULL;
#define JOYDEV_MINOR_BASE0
input_table[(&joydev_handler)->minor >> 5] = input_table[0] = &joydev_handler;
static struct input_handler apmpower_handler = {
.event =apmpower_event,
.connect =apmpower_connect,
.disconnect =apmpower_disconnect,
.name ="apm-power",
.id_table =apmpower_ids,
};
apmpower_handler.fops=NULL;因此不在input_table中定义
static struct input_handler evbug_handler = {
.event =evbug_event,
.connect =evbug_connect,
.disconnect =evbug_disconnect,
.name ="evbug",
.id_table =evbug_ids,
};
evbug_handler与apmpower_handler一样,
static struct input_handler kbd_handler = {
.event= kbd_event,
.connect= kbd_connect,
.disconnect= kbd_disconnect,
.start= kbd_start,
.name= "kbd",
.id_table= kbd_ids,
};
与apmpower_handler一样;
static struct input_handler rfkill_handler = {
.event =rfkill_event,
.connect =rfkill_connect,
.disconnect =rfkill_disconnect,
.start =rfkill_start,
.name ="rfkill",
.id_table =rfkill_ids,
};
与apmpower_handler一样;
又有问题了,那输入服务系统怎么知道按键设备文件?怎么就能读了呢?
那就是input_register_device函数注册。
int input_register_device(struct input_dev *dev)
{
list_add_tail(&dev->node, &input_dev_list); /*放入链表里去*/
/*********怎么放入链表的,放入后的结果是怎样的**************/
static LIST_HEAD(input_dev_list);
list.h中定义了
struct list_head {
struct list_head *next, *prev;
};
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
#define LIST_HEAD_INIT(name) { &(name), &(name) }
初始化时使得next,prev指向自身
static inline void list_add_tail(&dev->node, &input_dev_list)
{
__list_add(&dev->node,(&input_dev_list)->prev,&input_dev_list);
}
/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
void __list_add(struct list_head *new,struct list_head *prev,struct list_head *next)
{
(&dev->node)->prev =&dev->node;
(&dev->node)->next =&input_dev_list;
(&dev->node)->prev = prev;
prev->next =&dev->node;
}
EXPORT_SYMBOL(__list_add);
/*********************************************************/
/*对于每一个input_handler都调用input_attach_handler*/
/*
根据input_handler的id_table判断能否支
持
input_dev*/list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
}
/***********怎么判断是否支持,判断过还要做什么***********/
/*注册input_dev或input_handler时,会两两比较左边的input_dev和右边的input_hadler
*根据input_handler的id_table判断这个input_handler能否支持这个input_dev,
*如果能支持,就调用input_handler的connection函数与建立连接。
*/
input_attach_handler(struct input_dev *dev,struct input_handler *handler)
{
const struct input_device_id *id;
int error;
id = input_match_device(handler->id_table, dev);
if(!id)
return -ENODEV;
error = handler->connect(handler, dev, id);
/*匹配了,就调用*input_handler结构
*中的connection进行建立联系
*/
}
/******************************************************/
handler->connect中做了什么,就要看真正的input_handler中的connection。
上面有分析到只有
evdev_handler,
mousedev_handler,
joydev_handler在input_table[],
所以我们可以分析这三个中的一个。
下面以evdev_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,
};
/*
* Create new evdev device. Note that input core serializes calls
* to connect and disconnect so we don't need to lock evdev_table here.
*/
evdev_connect(struct input_handler *handler, struct input_dev *dev,const struct input_device_id *id)
{
struct evdev *evdev;
int minor;
int error;
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
/*分配一个struct evdev,此结构中有 一个input_handle结构*/
INIT_LIST_HEAD(&evdev->client_list);
/*设置*/
/*evdev->handle.dev 指向input_device*/
evdev->handle.dev = input_get_device(dev);
/*evdev->handle.handler指向input_handler*/
evdev->handle.handler = handler;
error = input_register_handle(&evdev->handle);/*注册*/
error = evdev_install_chrdev(evdev);
}
/***注册evdev->handle又做了什么呢***/
int input_register_handle(struct input_handle *handle)
{
struct input_handler *handler = handle->handler;
struct input_dev *dev = handle->dev;
int error;
list_add_tail_rcu(&handle->d_node, &dev->h_list);
/*结果为&handle->d_node的next指向了dev->h_list
*dev->h_list的prev指针指向了handle>d_node,
*以后就可以通过dev->h_list找的handle */
list_add_tail(&handle->h_node, &handler->h_list);
/*结果为&handle->h_node的next指向了
*handler->h_list , handler->h_list的prev指针指向了
*handle>d_node,以后就可以通过dev->h_list找的handle,
*并且可以通过handle找到dev. */
}
总结过程:
1.打开设备文件;
a.调用static const struct file_operations input_fops 中的open函数 即input_open_file
b.发生转换,file->f_op = input_table[iminor(inode) >> 5] 调用input_table[iminor(inode) >> 5]的open函数,(次设备号64-95,evdev.c,handler=&evdev_handler);
c.调用evdev_handler中的fops的open函数打开设备文件。
2.读设备文件:
调用(次设备号64-95,evdev.c,handler=&evdev_handler)evdev_handler中的fops的read函数读取设备文件。
当看evdev_handler中的fops的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;
/*非阻塞时*/
if (client->head == client->tail && evdev->exist
&&(file->f_flags & O_NONBLOCK))
return -EAGAIN;
/*阻塞时*/
retval = wait_event_interruptible(evdev->wait,
client->head != client->tail ||!evdev->exist);
if (retval)
return retval;
}
source insight搜索evdev->wait
进入得到
/*
* Pass incoming event to all connected clients.
*/
static void evdev_event(struct input_handle *handle,
unsigned int type, unsigned int code, int value)
{
wake_up_interruptible(&evdev->wait);
}
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,
};
不是中断处理函数调用的,而是在evdev_event被唤醒的,evdev_event是evdev_handler的event,那么中断处理函数就应该调用evdev_handler的event进行操作,接下来就看看
随便中一个驱动程序:
我们以\linux-2.6.30.4\drivers\input\keyboard\corgikbd.c为例。
中断处理函数:
static irqreturn_t corgikbd_interrupt(int irq, void *dev_id)
{
struct corgikbd *corgikbd_data = dev_id;
if (!timer_pending(&corgikbd_data->timer)) {
/** wait chattering delay **/
udelay(20);
corgikbd_scankeyboard(corgikbd_data);
}
return IRQ_HANDLED;
}
只调用了 corgikbd_scankeyboard函数,我们进去看看
static void corgikbd_scankeyboard(struct corgikbd *corgikbd_data)
{
unsigned int row, col, rowd;
unsigned long flags;
unsigned int num_pressed;
num_pressed = 0;
for (col = 0; col < KB_COLS; col++) {
corgikbd_discharge_all();
udelay(KB_DISCHARGE_DELAY);
corgikbd_activate_col(col);
udelay(KB_ACTIVATE_DELAY);
rowd = GET_ROWS_STATUS(col);
for (row = 0; row < KB_ROWS; row++) {
unsigned int scancode, pressed;
scancode = SCANCODE(row, col);
pressed = rowd & KB_ROWMASK(row);
input_report_key(corgikbd_data->input,
corgikbd_data->keycode[scancode], pressed);
}
corgikbd_reset_col(col);
}
corgikbd_activate_all();
input_sync(corgikbd_data->input);
}
去去判断有input_report_key函数很显眼
去看看
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_KEY, code, !!value);
}
进去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_event函数很相像*/
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_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;
}
if (disposition &INPUT_PASS_TO_HANDLERS)
input_pass_event(dev, type, code, value);
}
static void input_pass_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
struct input_handle *handle;
rcu_read_lock();
handle = rcu_dereference(dev->grab);
if (handle)
handle->handler->event(handle, type, code, value);
else
/*遍历所有的dev->h_list,如果已经调用handle->open
*调用 handle->handler->event进行处理,
*如果我们的按键跟evdev_handler
*匹配成功就调用evdev_handler的event,
*即我们前面所说的evdev_event*/
list_for_each_entry_rcu(handle, &dev->h_list, d_node)
if (handle->open)
handle->handler->event(handle,type, code, value);
rcu_read_unlock();
}
总结一下发生中断时,怎么处理的:
corgikbd_interrupt(int irq, void *dev_id) ->
corgikbd_scankeyboard(struct corgikbd *corgikbd_data) ->
/*上面两个是corgikbd.c独有的函数所以在其他驱动程序中,
*在中断处理函数中直接调用input.h相应函数上报事件,也可直接调用
*input_event函数上报事件*/
input_report_key(corgikbd_data->input, corgikbd_data->keycode[scancode], pressed);
-> void input_event(*dev,type,code,value) ; ->
input_handle_event(dev, type, code, value) ; ->
/*对于可以交给设备处理的事件,并且定义了dev->event,就调用dev->event进行处理,
*其他都是调用input_pass_event进行处理*/
if ((disposition &INPUT_PASS_TO_DEVICE) && dev->event)
dev->event(dev, type, code, value);
if (disposition & INPUT_PASS_TO_HANDLERS)
input_pass_event(dev, type, code, value); ->
调用handle->handler->event(handle,type, code, value)处理;
上面就是输入子服务系统的内容;
- 输入服务子系统框架代码分析(韦东山的视频总结及针对linux-2.6.30.4)
- 输入服务子系统实例分析(韦东山的视频总结及针对linux-2.6.30.4)
- POLL机制分析(韦东山的视频总结及针对linux-2.6.30.4)
- linux中断机制及中断注册1(韦东山的视频总结及针对linux-2.6.30.4)
- linux中断机制及中断注册2(韦东山的视频总结及针对linux-2.6.30.4)
- arm驱动程序——手动设备节点 (韦东山的视频总结及针对linux-2.6.30.4)
- arm驱动程序——自动创建设备节点 (韦东山的视频总结及针对linux-2.6.30)
- arm驱动程序——点亮led(韦东山的视频总结及针对linux-2.6.30)
- arm驱动程序——按键程序1(韦东山的视频总结及针对linux-2.6.30)
- arm驱动程序——按键程序2(韦东山的视频总结及针对linux-2.6.30)
- arm驱动程序——按键程序4_poll(韦东山的视频总结及针对linux-2.6.30)
- arm驱动程序——点亮led-利用次设备号(韦东山的视频总结及针对linux-2.6.30)
- arm驱动程序——按键程序3_定时器消抖(韦东山的视频总结及针对linux-2.6.30)
- arm驱动程序——按键程序5_异步通信(韦东山的视频总结及针对linux-2.6.30)
- arm驱动程序——按键程序6_互斥1—原子操作(韦东山的视频总结及针对linux-2.6.30)
- arm驱动程序——按键程序6_互斥—信号量(韦东山的视频总结及针对linux-2.6.30)
- Linux输入子系统框架分析及输入设备驱动编程
- 韦东山视频实验之Input子系统分析之一
- shell基础第十五篇-引号
- bluePen – 使用在线 CSS 编辑器美化你的网站
- JSON简单入门笔记
- 在web服务启动是就加载运行某个servlet --------load-on-startup
- 收藏的网站
- 输入服务子系统框架代码分析(韦东山的视频总结及针对linux-2.6.30.4)
- Fiddler真乃前端大杀器!!!
- 一位iOS教育类应用开发者是如何赚到60多万美元?
- Android升级ADT22后会报ClassNotFoundException的原因分析
- 语法分析分析c-minus的选择排序(1)
- -Java集合
- 35套用于扁平化设计的图标和网页元素《上集》
- 外观模式(Facade)
- * 7-3-f 菱形星号图