本帖最后由 nello 于 2015-12-18 17:17 编辑
所用内核是 linux-2.6.22.6
Ⅰ ---->对于硬件驱动层
设备驱动程序首先分配一个input_dev结构体
1 struct input_dev *input =input_allocate_device();
2 设置这个结构体
3 注册这个结构体
注册函数为:
input_register_device(input)
list_add_tail(&dev->node, &input_dev_list); //将注册的这个输入设备挂接到input_dev_list链表上
list_for_each_entry(handler, &input_handler_list, node)//遍历整个input_handler_list链表
input_attach_handler(dev, handler);
input_match_device(handler->id_table, dev)
handler->connect(handler, dev, id)//如果匹配成功那么就调用 handler里面注册的connect函数
Ⅱ----->对于input核心层
input核心层 input.c
input_init
① class_register(&input_class);//注册一个class
② register_chrdev(INPUT_MAJOR, "input", &input_fops); //注册一个字符型设备 但是还缺少一个步骤 那就是 class_device_create
Ⅲ----->对于事件处理层(以event事件处理机制为例)
1 首先分配设置注册一个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,
};
input_register_handler(&evdev_handler);
input_table[handler->minor >> 5] = evdev_handler;//将分配设置的evdev_handler结构体放入 input_table数组中 这个数组是以minor为索引的;这里向右移动5为的原因是 每一个事件处理器能够支持 32中输入设备 这些输入设备以次设备好来索引
list_add_tail(&handler->node, &input_handler_list);//将注册的这个事件处理器 挂接到input_handler_list链表上
list_for_each_entry(dev, &input_dev_list, node)//遍历整个input_dev_list链表 寻找匹配项
input_attach_handler(dev, handler); //寻找匹配
handler->connect(handler, dev, id)//如果匹配成功那么就调用 handler里面注册的connect函数
鉴于input_register_device input_register_handler最终都会调用到 handler->connect函数 下面分析一下 connect函数都做了什么
evdev_connect(handler, dev, id)
for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++);//这个for循环就是为了找到一个空的evdev_table 并且 minor < EVDEV_MINORS
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);//分配一个evdev结构体
init_waitqueue_head(&evdev->wait);//初始化 wait
evdev->exist = 1; //设置这个evdev结构体 并且注意到 evdev里面有一个handle 将已经匹配成功的 input_dev跟input_handler链接在了一起
evdev->minor = minor;
evdev->handle.dev = dev;
evdev->handle.name = evdev->name;
evdev->handle.handler = handler;
evdev->handle.private = evdev;
evdev_table[minor] = evdev; //将 这个evdev放入 evdev_table
devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
③class_device_create(&input_class, &dev->cdev, devt, dev->cdev.dev, evdev->name); //说明这个event(0~n)是供应用程序调用的
input_register_handle(&evdev->handle)//////这个函数也是挺重要的
根据①②③ 就完成了 一个输入子系统字符设备驱动程序的设置过程
由于内核中事先已经加载好了 input_init 函数那么①②率先完成
evdev_handler这个结构体内核中也已经加载好了,那么当驱动程序被加载好以后 执行input_register_device(input)就会调用evdev_connect 函数完成第三步③ 至此整个字符设备就已经注册好了
接下来看一下input_init函数的file_operaions 结构体
static const struct file_operations input_fops = { //当应用程序打开event0时就会调用这个input_open_file
.owner = THIS_MODULE,
.open = input_open_file,
};
根据以上的分析 当应用程序执行open("dev/input/event0",O_RDWR);;;;那么就会调用
这里的input_open_file
new_fops = fops_get(handler->fops) //根据注册的那个handler获得一个新的file_operations结构体 也就是handler里面的结构体 即evdev_fops
然后调用 new_fops->open(inode, file);//
观察 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
};
对于evdev_open
i = iminor(inode) - EVDEV_MINOR_BASE;
evdev = evdev_table; //根据次设备号来获得evdev结构体----这个结构体能够索引到能够匹配的input_dev跟input_handler
client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);//分配一个客户端结构
client->evdev = evdev;//将这个endev放入客户端结构体中
list_add_tail(&client->node, &evdev->client_list);
input_open_device(&evdev->handle);
dev->open(dev);
当底层有数据产生的时候就会就会将数据传递给事件处理器
利用函数
input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)//上报事件
list_for_each_entry(handle, &dev->h_list, d_node)//遍历input_dev上因input_dev与input_handler配对成功而生成的handler
if (handle->open) //首先判断这个设备是不是已经被打开了
handle->handler->event(handle, type, code, value); 如果被打开了那么就调用相应handler的event函数
evdev_event
struct evdev *evdev = handle->private; //根据connect函数中设置的私有数据 得到evdev
list_for_each_entry(client, &evdev->client_list, node)
-
-
-
填充client->buffer
kill_fasync(&client->fasync, SIGIO, POLL_IN);上报事件
wake_up_interruptible(&evdev->wait);//唤醒进程
事件上报完成或者进程被唤醒 那么就要读取数据了
当应用程序调用read函数时就会调用
evdev_read
retval = wait_event_interruptible(evdev->wait,//(这个wait在connect函数被调用的时候就已经被初始化了)
client->head != client->tail || !evdev->exist); // 没有事件产生的时候进程就会睡在endev的等待队列上了,,等待条件是有事件产生 或者设备不存在了
evdev_event_to_user-----至此就将事件上报给了应用程序中
|