一个驱动设备对应多个驱动程序
dev层负责写与硬件相关的驱动程序,中断函数,事件上报等;handler层负责构造file_operations结构体,创建handle。
Input层是中转层,接口层,dev层需要向input层注册input_dev,handler层需要向input层注册input_handler和input_handle,input层有很多功能函数,把dev层和handler层链接起来。
subsys_initcall(input_init); // input作为模块,安装该模块时会执行input_init
static int __init input_init(void)
err = class_register(&input_class); // 类创建—类名input
err = input_proc_init(); // 在/proc下面建立相关的文件
err = register_chrdev(INPUT_MAJOR, "input", &input_fops); // 注册字符设备proc/devices,主设备号13
// 该字符设备的file_operations 结构
static const struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file, }; // 当我们挂载完一个新的input驱动,则内核便会调用该.open函数
// input_init(void) 执行结束,完成了类创建;一个名为input的字符设备创建,主设备号为13、次设备号64
module_init(evdev_init); // subsys_initcall比module_init优先级高,所以先执行input_init,再执行evdev_init
static int __init evdev_init(void)
input_register_handler(&evdev_handler); // 向input层注册一个input_handler结构体
// .id_table是固定的,其他成员函数的参数需要注册input_dev的时候才会传进来。
static struct input_handler evdev_handler = {
.event = evdev_event, // 当事件发生时会调用evdev_event()函数 -- 事件处理函数
.connect = evdev_connect, // 当input_dev和evdev_handler匹配成功时会调用evdev_connect()函数
.disconnect = evdev_disconnect,
.fops = &evdev_fops, // 驱动的file_operations放在了handler层
.minor = EVDEV_MINOR_BASE, // 次设备号最小值:64
.name = "evdev", // 设备名称evdev – 后面实际注册设备时给设备命名为evdev 0/evdev1
.id_table = evdev_ids, // input_dev和id_table比较,匹配则说明evdev_handler能支持这个设备
};
// evdev_init结束,完成了向input层注册一个input_handler结构体
evdev_init进入→input_register_handler(还没有注册设备前)
int input_register_handler(struct input_handler *handler)
INIT_LIST_HEAD(&handler->h_list);
// 初始化evdev_handler结构体中的h_list链表,prev和next指针都指向链表头。
// 该链表后续将被用于存放handle->h_node
input_table[handler->minor >> 5] = handler; //将evdev_handler存入到input_table[2]中.
list_add_tail(&handler->node, &input_handler_list); //放入到全局链表
// static LIST_HEAD(input_handler_list); //全局链表input_handler_list在前面创建好了
// 在input_handler_list->priv和input_handler_list两个节点之间插入新节点evdev_handler->node
// 左-右:头节点input_handler_list 新节点evdev_handler->node 尾节点input_handler_list->priv
list_for_each_entry(dev, &input_dev_list, node) == for (;;;)
input_attach_handler(dev, handler); input_attach_handler(dev, handler);
// struct input_dev *dev; 执行list_for_each_entry前只是定义了dev,还没有指向具体的input_dev结构
体,执行list_for_each_entry后dev就指向了一个input_dev结构体,然后将得到的dev和传进来的
evdev_handler进行匹配,for循环会把存在input_dev_list链表里的所有input_dev都会遍历一遍,得到所
有的input_dev结构体的入口地址也就得到了该结构体,然后和evdev_handler进行比较,匹配两者id。
// input_register_handler结束,完成了将evdev_handler存入到input_table[2]中;将evdev_handler->node插入input_handler_list链表,根据input_dev_list得到所有input_dev的地址并将input_dev和evdev_handler进行匹配。
input_attach_handler()函数
从input_register_handler进入
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
if (handler->blacklist && input_match_device(handler->blacklist, dev))
return -ENODEV; // blacklist标记的黑名单,evdev_handler-> blacklist为空=0
id = input_match_device(handler->id_table, dev); //匹配两者id
error = handler->connect(handler, dev, id); // 匹配成功则调用evdev_handler->connect函数
// input_attach_handler()结束,完成了对传进来的input_dev和evdev_handler的id_table进行匹配,匹配成功则调用handler->connect()函数,否则接着对下一个input_dev和evdev_handler的id_table进行匹配。
evdev_handler->connect
从input_attach_handler()函数进来
handler->connect(handler, dev, id);
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,const struct input_device_id *id)
for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++);
// 寻找一个还没有用过的minor,如果minor <32且evdev_table[minor]!=0,说明该minor被占用,继续minor++
// 当继续往下找到一个minor <32且evdev_table[minor]=0,说明该minor还没有被使用,找到后退出for循环;
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
// struct evdev *evdev; 前面已经创建了一个evdev结构体,现在给这个结构体分配内存空间;
// 空间分配好后,就要对evdev进行设置;
INIT_LIST_HEAD(&evdev->client_list); // 初始化链表client_list,该链表将在evdev_open()函数中使用到 ;
init_waitqueue_head(&evdev->wait); // 初始化等待队列头,该队列头在read函数中休眠,在event函数中唤醒;
evdev->exist = 1; // 存在标志;
evdev->handle.dev = dev; // 让evdev->handle.dev指向传进来的input_dev;
evdev->handle.handler = handler; // 让evdev->handle.handler指向传进来的evdev_handler;
evdev->handle.name = evdev->name; // 让evdev->handle.name指针指向evdev->name数组首地址;
evdev->handle.private = evdev; // 让指针evdev->handle.private指向evdev结构体/即其首地址;
sprintf(evdev->name, "event%d", minor); // 保存驱动设备名字, event%d 到evdev->name;
evdev_table[minor] = evdev; // 将evdev结构体注册到evdev_table[minor];
devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
//将一个已知的主次设备号转化为一个无符号32位整形;
// 主设备号:13;次设备号64+minor;
cdev = class_device_create(&input_class, &dev->cdev, devt,dev->cdev.dev, evdev->name);
// 在class/input类下创建类驱动设备;
// 当我们挂载一个新的input驱动,则内核便会调用input_fops->open函数;
error = sysfs_create_link(&input_class.subsys.kobj,&cdev->kobj, evdev->name);
// 创建内核连接;
error = input_register_handle(&evdev->handle);
// 注册这个input_handle结构体到input层;
evdev_connect()函数结束,完成创建了一个evdev结构体并填充,并将新创建的evdev存入到evdev_table[minor];
在input类下创建了类设备/主设备号13次设备号64+minor; 最后向input层注册evdev->handle结构体;
input_register_handle()函数
从evdev_connect进入
input_register_handle(&evdev->handle);
list_add_tail(&handle->d_node, &handle->dev->h_list);
// handle->dev->h_list = input_dev->h_lish,将handle->d_node插入input_dev->h_lish链表;
list_add_tail(&handle->h_node, &handler->h_list);
// 将handle->h_node插入evdev->h_lish链表;
input_register_handle()函数结束,完成将handle->d_nod/h_node分别插入到input_dev->h_lish和evdev->h_lish链表.
结束input_register_handle()函数后返回到list_for_each_entry()这个for循环,继续下一个dev的匹配
list_for_each_entry(dev, &input_dev_list, node) == for (;;;)
input_attach_handler(dev, handler); input_attach_handler(dev, handler);
input_match_device()函数
从input_attach_handler进入:input_device_id是evdev_handler里的(input_device是一个结构体)
input_match_device (const struct input_device_id *id, struct input_dev *dev){
for (; id->flags || id->driver_info; id++){
return id;
}
Return NULL;
// id++ 一定程度后,id->driver_info=0,
直接看:id-> evbit[i] & dev-> evbit[i]) != id-> evbit[i]
!=优先级大于&,由于evdev_handler-> id_table->evbit没有设置,所以为0,0&任何东西都是0
input_table[]数组每一位
static struct input_handler *input_table[8];
mousedev_handler->minor = 32; handler->minor >> 5 = 1
evdev_handler->minor = 64; handler->minor >> 5 = 2
tsdev_handler->minor = 128; handler->minor >> 5 = 3
调用int input_register_handler(struct input_handler *handler)
input_table[handler->minor >> 5]
宏定义list_for_each_entry()—实际是一个for循环
list_for_each_entry(dev, &input_dev_list, node)
#define list_for_each_entry(pos, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member); \
prefetch(pos->member.next), &pos->member != (head); \
pos = list_entry(pos->member.next, typeof(*pos), member))
for循环初始化:pos = list_entry((head)->next, typeof(*pos), member); 得到放在结构体地址;
for循环条件:prefetch(pos->member.next), &pos->member != (head); // prefetch()用于预取以提高遍历速度;
for循环没执行一次:pos = list_entry(pos->member.next, typeof(*pos), member))
/* list_entry - get the struct for this entry
* @ptr: the &struct list_head pointer. &input_dev_list == head (head)->next == ptr
* @type: the type of the struct this is embedded in. input_dev == dev == pos == typeof(*pos) == type
* @member: the name of the list_struct within the struct. */ node ==
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
// 讲讲container_of:作用:根据一个结构体变量中的一个域成员变量的指针来获取指向整个结构体变量的指针。
container_of的作用 -- 获得结构体的地址。那么我们需要给他提供三个参数,分别是:ptr:member成员的指针 type:结构体类型 member:成员member的名字。
分析gpio_keys.c驱动程序
platform_driver_register()函数
module_init(gpio_keys_init);
static int __init gpio_keys_init(void)
platform_driver_register(&gpio_keys_device_driver); //注册设备驱动
struct platform_driver gpio_keys_device_driver = {
.probe = gpio_keys_probe,
.remove = __devexit_p(gpio_keys_remove),
.driver = {
.name = "gpio-keys",
}
};
platform_driver_register()函数结束,完成了将platform_driver设备驱动注册.
gpio_keys_probe()函数
从.probe进入
static int __devinit gpio_keys_probe(struct platform_device *pdev)
struct input_dev *input; //创建一个input_dev结构体指针;
input = input_allocate_device(); // 分配内存;
// input_allocate_device()函数会返回一个准备好的input_dev结构体;
input->evbit[0] = BIT(EV_KEY); // 设置事件类型为按键类型;
input->name = pdev->name; // 设置input_dev->name;
request_irq() // 注册中断;
error = input_register_device(input); // 向input层注册input_dev;
return 0; // 注册成功后退出gpio_keys_probe()函数;
input_register_device()函数
从gpio_keys_probe()进入
int input_register_device(struct input_dev *dev)
set_bit(EV_SYN, dev->evbit); // 设置为同步事件
init_timer(&dev->timer); // 初始化定时器—防抖动吗?
list_add_tail(&dev->node, &input_dev_list); // 将input_dev->node放入链表
error = class_device_add(&dev->cdev);
class_device_get(class_dev);
//class_device对象的计数加1,返回class_device对象的指针
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
// 这两句构成了一个for循环,每从input_handler_list链表取得一个
// handler结构体的地址,将当前的input_dev和该handler进行匹配,
// 匹配成功则建立连接,创建类设备并上传一个handle到input层.
input_register_device()结束,完成了将当前的input_dev->node存入input_dev_list链表;从input_handler_list链表获得所有的handler结构体入口地址,并和该input_dev匹配。
类设备和相应的驱动准备好后,驱动层就可以发送事件给input层,经过input层中转后最终调用evdev_handler->event函数,在中断处理函数中调用input_event()函数。
中断处理函数
static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
gpio_get_value(); // 进入中断处理函数后,获得I/O电平
input_event(input, type, button->code, !!state); // 事件发送
input_sync(input); //发送同步信号,表示事件发送完毕
//////
* input_event() - report new input event
* @dev: device that generated the event
* @type: type of the event
* @code: event code
* @value: value of the event
input_event()函数
从中断处理函数进入
input_event(input, type, button->code, !!state);
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
switch (type) … // 事件判断
if (dev->grab)
dev->grab->handler->event(dev->grab, type, code, value);
//判断有没有声明自定义的处理函数(初始化过程中显然没有定义)
list_for_each_entry(handle, &dev->h_list, d_node)
if (handle->open)
handle->handler->event(handle, type, code, value);
// 上面三句话构成了一个双层for循环
// 首先从input_dev->h_list链表找到与之关联的第一个input_handle的入口地址.
// 如果evdev_handler->open=1,表示设备已经打开数据可以传递.
input_event()函数结束,完成事件判断,从链表中获取handler入口地址,判断是该驱动对应的handler后开始传递数据.
evdev_event()函数
从input_event()进入
static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
struct evdev_client *client;
// 环形缓冲区;
wake_up_interruptible(&evdev->wait);
// 唤醒,唤醒后就可以执行读操作;
kill_fasync(&client->fasync, SIGIO, POLL_IN);
// 利用kill指令发送数据给进程,进程接收到数据后就会跳转到read函数;
evdev_read()函数
从哪进入:回顾kill指令内容
static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
// 数据会放到buffer缓冲区;
struct evdev_client *client = file->private_data; // 通过file结构体找到evdev_client;
struct evdev *evdev = client->evdev; // 紧接着获得evdev结构体;
retval=wait_event_interruptible(evdev->wait,client->head!=client->tail || !evdev->exist);
// 由于在evdev->event中已经被唤醒,即在进入evdev->read前已经被唤醒.
while (client->head != client->tail && retval + evdev_event_size() <= count)
// 数据发送
input_open_file()函数
当挂载了一个新的input_dev时(热拔插机制使得挂载后能够自动创建类设备),会调用input_open_file()函数
static int input_open_file(struct inode *inode, struct file *file)
struct input_handler *handler = input_table[iminor(inode) >> 5];
// iminor(inode)获得input_dev的次设备号,并右移5位,即除32
// 假如input_dev对应的handler是evdev_handler,则iminor(inode) >> 5 = 2
// *handler = input_table[2]; 获得input_handler
old_fops = file->f_op;
file->f_op = new_fops;
// 将新的file_operations *new_fops赋到file-> file_operations *f_op里
// 此时input子系统的file_operations就等于新挂载的input驱动的file_operations
// 结构体,实现一个偷天换日的效果.
err = new_fops->open(inode, file); // 调用evdev_handler->open成员函数
来源:CSDN
作者:九十度转角9
链接:https://blog.csdn.net/qq_42264877/article/details/104631853