前言:
本篇blog就针对platform总线驱动的调用机制,就直接抠出代码进行分析了。
Evdev.c (drivers\input)
module_init(evdev_init);
module_exit(evdev_exit);
static int __init evdev_init(void)
{
return input_register_handler(&evdev_handler);
}
input.c层(核心层)
input_register_handler(struct input_handler *handler)
// 初始化handler链表
INIT_LIST_HEAD(&handler->h_list);
//现将handler保存到input_table数组中,minor一般都是32的倍数
// 注意,这个在input.c核心代码中有用到
input_table[handler->minor >> 5] = handler;//并将handler->node加入到input_handler_list链表中
list_add_tail(&handler->node, &input_handler_list);// 通过handler找到对应的输入设备
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);//更新//proc中的数据
input_wakeup_procfs_readers();
input_attach_handler(dev, handler);
//比对设备id列表
list_for_each_entry(dev, &input_dev_list, node)
id = input_match_device(handler, dev);
// 如果比对成功,那么执行handler的connect函数
handler->connect(handler, dev, id);
static const struct input_device_id evdev_ids[] = {
{ .driver_info = 1 }, /* Matches all devices */
{ }, /* Terminating zero entry */
};
input_match_device(handler, dev);
//id表示的是hander中的id_table,那这里面一般有什么呢
// 在evdev_ids中兼容所有的设备
for (id = handler->id_table; id->flags || id->driver_info; id++)
/*以下有几种比对方式:
1, 比较id->flags: 前提就是设置了flags,如果没有设置,就不比较
总线INPUT_DEVICE_ID_MATCH_BUS,id->bustype != dev->id.bustype
厂商:INPUT_DEVICE_ID_MATCH_VENDOR,id->vendor != dev->id.vendor
产品id:INPUT_DEVICE_ID_MATCH_PRODUCT,id->product != dev->id.product
版本:INPUT_DEVICE_ID_MATCH_VERSION,id->version != dev->id.version
2,比较设置产生的事件类型:比如有evbit,keybit,absbit
//就是将dev->id和handler->id中evbit/keybit/absbit数组进行位与预算
MATCH_BIT(evbit, EV_MAX);
3,如果hander有match函数,那么就执行match函数,以下这种写法很神奇
if (!handler->match || handler->match(handler, dev))
return id; // 代码能执行到这里就表示找到了
总结:1,将构建的handler加入到input_handler_list中
2,并匹配input_dev_list中的device,device的类型为struct input_dev
3, 匹配的依据是各自对象中的id结构体,匹配成功后会执行handler中的connect函数
4,对于input设备对象的驱动代码中,需要设置evbit/keybit/absbit等数组中的位,用于进行比对
------------------------------------------------------------------------------------------------------------
hander对象中的connect函数:
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,
};
//参数1,handler对象本身 2,匹配成功后的input设备对象 3,id列表
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,const struct input_device_id *id)
// 在evdev_table数组找到一个没有使用的位置
for (minor = 0; minor < EVDEV_MINORS; minor++)if (!evdev_table[minor]);
//分配一个evdev 对象
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);// 初始化列表,用于记录????
INIT_LIST_HEAD(&evdev->client_list);
//初始化等待队列
init_waitqueue_head(&evdev->wait);
//设置evdev的名字,用于用户空间的设备节点
dev_set_name(&evdev->dev, "event%d", minor);
evdev->exist = true;
evdev->minor = minor;
// 此时出现了struct input_handle
// struct input_handle会记录input设备是哪个
evdev->handle.dev = input_get_device(dev);
evdev->handle.name = dev_name(&evdev->dev);
//同时handle也会记录handler是哪个
evdev->handle.handler = handler;
evdev->handle.private = evdev;
//设置input设备的设备号,次设备号从64开始
evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
evdev->dev.class = &input_class;
evdev->dev.parent = &dev->dev;
evdev->dev.release = evdev_free;device_initialize(&evdev->dev);
// 注册handle,实际就是将input设备对象加入到handle中的handle->d_node链表中
// 将handler加入到handle中的handle->h_node链表中
input_register_handle(&evdev->handle);
list_add_tail_rcu(&handle->d_node, &dev->h_list);
list_add_tail_rcu(&handle->h_node, &handler->h_list);// 如果handler中有start,那么就执行start
if (handler->start)
handler->start(handle);
// 将evdev存放到evdev_table数组中,下标为次设备号evdev_install_chrdev(evdev);
//注册字符设备,创建相应的设备节点device_add(&evdev->dev);
总结:
1, struct evdev是用于与应用程序进行交互的媒介,用于创建字符设备
2, struct evdev是在handler和input设备匹配成功后产生的
3, struct evdev中包含了struct input_handle,该handle中记录了handler和input设备
4, 每一个input设备对应一个struct evdev,一个handler可以对应多个struct evdev和input设备
5, evdev中的input_handle两个链表:handle->d_node记录对应的input设备对象的链表
handle->d_node记录对应的handler对象
-----------------------------------------------------------------------------------
input核心层代码分析:
Input.c (drivers\input)
subsys_initcall(input_init);
module_exit(input_exit);
.name = "input",
.devnode = input_devnode,
};
input_init(void)
class_register(&input_class);
// 创建/proc/bus/input/: devices handlers,你可以理解成是总线
err = input_proc_init();
//注册字符设备
register_chrdev(INPUT_MAJOR, "input", &input_fops);
static const struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
.llseek = noop_llseek,
};
应用open()
sys_open
static int input_open_file(struct inode *inode, struct file *file)
struct input_handler *handler;
//根据设备此设备号召到对应的input_table
// 该数组在input_register_handler函数中初始化了handler = input_table[iminor(inode) >> 5];
if (handler)
//如果有当前次设备对应的handler,那么就获取hander对应的fops
new_fops = fops_get(handler->fops);
// 保存用户空间的f_opold_fops = file->f_op;
// 将handler的f_op赋值给用户空间的f_opfile->f_op = new_fops;
// 执行handler的open函数
err = new_fops->open(inode, file);
此时要切换到evdev.c中代码中驱动
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 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,
};static int evdev_open(struct inode *inode, struct file *file)
int i = iminor(inode) - EVDEV_MINOR_BASE;
// 在evdev_table数组中根据次设备号找到对应的struct evdev
// 该对象是在evdev_connect()中实现的
evdev = evdev_table[i];bufsize = evdev_compute_buffer_size(evdev->handle.dev);
//产生一个struct evdev_client对象,该对象用于承载用户空间和内核空间的缓冲区
//里面包含一个struct input_event buffer[];
client = kzalloc(sizeof(struct evdev_client) +
bufsize * sizeof(struct input_event),
GFP_KERNEL);
client->bufsize = bufsize;
client->evdev = evdev;
// 将evdev_client放入到evdev->client_list链表中
evdev_attach_client(evdev, client);
error = evdev_open_device(evdev);
//好像没做什么事情,主要是判断input设备对象是否有open方法,有就打开
//并且对应handle的使用进行计数
input_open_device(&evdev->handle);
//将evdev_client记录到应用空间的file中的私有数据中
file->private_data = client;
用户open-->input_handler-->evdev(handle)<----input_dev
evdev_client==============================================================================================
应用read()
---------------------------------------------------------------------
sys_read
-------------------------------------------------------------------
input.c file->f_op = new_fops;
evdev.c: evdev_handler->evdev_read
static ssize_t evdev_read(struct file *file, char __user *buffer,size_t count, loff_t *ppos)
struct evdev *evdev = client->evdev;
// 等待数据
wait_event_interruptible(evdev->wait,client->packet_head != client->tail || !evdev->exist);//如果数据到来后,将数据上传给用户
input_event_to_user(buffer + retval, &event)
copy_to_user(buffer, event, sizeof(struct input_event)
总结: 1,read中数据的等待需要有等待队列来完成
2,在read中的等待队列是谁唤醒的呢:接下来我们来看看input设备驱动层中数据提交:
input_report_key(button_dev, KEY_LEFT, 0);
input_start_autorepeat(dev, code);
input_pass_event(dev, type, code, value);
struct input_handler *handler;
struct input_handle *handle;
// 在input设备对象中的dev->h_list找到handle对象
list_for_each_entry_rcu(handle, &dev->h_list, d_node)
//再找到handlerhandler = handle->handler;
//执行handler的event函数,并传递type, code, valuehandler->event(handle, type, code, value);
所以此时又切换到evdev.c中的evdev_handler:
static void evdev_event(struct input_handle *handle,
unsigned int type, unsigned int code, int value)|
//获取通过handle中找到evdev
struct evdev *evdev = handle->private;//封装input_event
event.type = type;event.code = code;
event.value = value;//通过evdev->client_list找到一个evdev_client对象
list_for_each_entry_rcu(client, &evdev->client_list, node)// 将input设备层传递过来的数据放入到client对象中
evdev_pass_event(client, &event);//如果type为EV_SYN的话,那么就将唤醒等待队列
if (type == EV_SYN && code == SYN_REPORT)
wake_up_interruptible(&evdev->wait);总结:
evdev.c
用户open-->input_handler-->evdev(handle)<----input_dev
file->private_data<----evdev_client
用户read-->input_handler--evdev_client input_dev
填充evdev_client<----------------------input_report_key
<----input_evnt----------evdev->waitqueue<---wake_up_interruptible<----input_sync