分类: LINUX
2012-08-24 11:21:56
Intput子系统内核源码分析
在linux-2.6.32.2/drivers/input/input.c中
使用register_chrdev(INPUT_MAJOR, "input", &input_fops)函数注册字符设备驱动
定义的file_operations的结构为:
static const struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
};
在该file_operations结构中只有open函数,没有其他的操作函数,进入input_open_file函数内部可以分析可知
/* 以次设备号在input_table结构体数组中找到一项input_handler */
struct input_handler *handler = input_table[iminor(inode) >> 5];
input_handler 结构为:
struct input_handler {
void *private;
// 建立连接
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
// 事件处理函数
int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
void (*disconnect)(struct input_handle *handle);
void (*start)(struct input_handle *handle);
const struct file_operations *fops;
int minor;
const char *name;
const struct input_device_id *id_table;
const struct input_device_id *blacklist;
struct list_head h_list;
struct list_head node;
};
/* 在input_handler结构中获得fops*/
new_fops = fops_get(handler->fops);
/* 将获得的fops作为字符设置新的操作函数集file_operations */
file->f_op = new_fops;、
/* 调用新的file_operations中的open函数 */
new_fops->open(inode, file);
app: read > ... > file->f_op->read
问:input_table数组由谁构造?
答:input_register_handler函数实现。
分析input_register_handler函数:
int input_register_handler(struct input_handler *handler)
/* 将 input_handler 结构体添加到input_table数组中 */
input_table[handler->minor >> 5] = handler;
/* 将input_handler结构放入链表 */
list_add_tail(&handler->node, &input_handler_list);
/* 对于每个input_dev,调用input_attach_handler */
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);
注册输入设备:
int input_register_device(struct input_dev *dev)
/* 将input_dev结构放入链表 */
list_add_tail(&dev->node, &input_dev_list);
/* 对于每个input_handler,调用input_attach_handler */
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
input_attach_handler函数:
int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
/* 判断该驱动是否支持该输入设备, */
input_match_device(handler->id_table, dev);
/* 如果支持,则调用connect函数建立连接 */
handler->connect(handler, dev, id);
注册input_dev或input_handler时,会两两比较左边的input_dev和右边的input_handler,
根据input_handler的id_table判断这个input_handler能否支持这个input_dev,
如果能支持,则调用input_handler的connect函数建立"连接"
问:怎么建立连接?
答:1. 分配一个input_handle结构体
2.
input_handle.dev = input_dev; // 指向左边的input_dev
input_handle.handler = input_handler; // 指向右边的input_handler
3. 注册:
input_handler->h_list = &input_handle;
input_dev->h_list = &input_handle;
以evdev.c为例:
evdev_connect
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); // 分配一个input_handle
// 设置
evdev->handle.dev = dev; // 指向左边的input_dev
evdev->handle.name = evdev->name;
evdev->handle.handler = handler; // 指向右边的input_handler
evdev->handle.private = evdev;
// 注册
error = input_register_handle(&evdev->handle);
怎么读按键?
app: read
--------------------------
.......
evdev_read
// 无数据并且是非阻塞方式打开,则立刻返回
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);
谁来唤醒?
evdev_event
wake_up_interruptible(&evdev->wait);
evdev_event被谁调用?
猜:应该是硬件相关的代码,input_dev那层调用的
在设备的中断服务程序里,确定事件是什么,然后调用相应的input_handler的event处理函数
// 上报事件
input_event(input, type, button->code, !!state);
input_sync(input);
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
input_handle_event(dev, type, code, value);
input_pass_event(dev, type, code, value);
handle = rcu_dereference(dev->grab);
if (handle)
handle->handler->event(handle, type, code, value);
else
list_for_each_entry_rcu(handle, &dev->h_list, d_node)
if (handle->open)
handle->handler->event(handle,
type, code, value);
以上内容全是本人凭个人理解写出的:如果错误请多包涵,若能指出,本人将万分感激。