文章目录
应用程序读取多个按键和驱动代码的结构分析
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h>
int main(void)
{
int fd;
int ret;
struct input_event event;
fd = open("/dev/event1", O_RDWR);
if(fd < 0)
{
perror("open");
exit(1);
}
while(1)
{
ret = read(fd, &event, sizeof(struct input_event));
if(ret < 0)
{
perror("read");
exit(1);
}
if(event.type == EV_KEY){
switch(event.code){
case KEY_POWER:
if(event.value){ //按下
printf("__APP_USER__ : power pressed\n");
}else{
printf("__APP_USER__ : power up\n");
}
break;
case KEY_VOLUMEDOWN:
if(event.value){ //按下
printf("__APP_USER__ : KEY_VOLUMEDOWN pressed\n");
}else{
printf("__APP_USER__ : KEY_VOLUMEDOWN up\n");
}
break;
case KEY_VOLUMEUP:
if(event.value){ //按下
printf("__APP_USER__ : KEY_VOLUMEUP pressed\n");
}else{
printf("__APP_USER__ : KEY_VOLUMEUP up\n");
}
break;
}
}
}
close(fd);
return 0;
}
这样,我们只需要改设备数量和添加设备树节点就能不修改其它代码实现驱动
输入子系统工作逻辑初识
5, 输入子系统的工作原理和代码分析
目的:
a,学会如何分析内核中子系统的代码,从而可以举一反三
b,整体把握框架思想,理解分层中各层的配合方式
c,掌握子系统,增强排错能力
代码跟读方法:
1,带着问题去读
2,做好笔记和画图
3,驱动联系应用,应用是调用的,驱动是实现的
input三层的分析
分层分析:
input handler层:evdev.c
module_init(evdev_init);
module_exit(evdev_exit);
static struct input_handler evdev_handler = {
.event = evdev_event,
.events = evdev_events,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.legacy_minors = true,
.minor = EVDEV_MINOR_BASE,
.name = “evdev”,
.id_table = evdev_ids,
};
evdev_init(void)
|
input_register_handler(&evdev_handler);
|
//初始化h_list
INIT_LIST_HEAD(&handler->h_list);
//将当前的handler加入到一个input_handler_list
list_add_tail(&handler->node, &input_handler_list);
//遍历链表input_dev_list
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);
|
// 将当前的hanler和input dev进行匹配, event handler能够匹配所有的input dev
id = input_match_device(handler, dev);
//匹配成功,之后要调用handler中connect方法
// 实际就是event handler,实际调用了evdev_connect
error = handler->connect(handler, dev, id);
//将当前的handler加入到/proc/bus/input/handlers文件中
input_wakeup_procfs_readers();
总结:
1,注册了evdev_handler
2, 遍历input dev list,并行匹配,恒匹配成功,自动会
调用handler中connect方法--- evdev_connect
input核心层:input.c
subsys_initcall(input_init);
module_exit(input_exit);
input_init()
|
//注册类,类似于class_create();
err = class_register(&input_class);
//在/proc创建 bus/input/devices handlers
err = input_proc_init();
//申请设备号
err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),
INPUT_MAX_CHAR_DEVICES, "input");
总结:
1,注册了主设备号
2,注册input class
simple_input.c
input_register_device(inputdev);
|
//将input dev加入到链表input_dev_list
list_add_tail(&dev->node, &input_dev_list);
//遍历input handler链表,进行匹配
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
|
//匹配成功,之后要调用handler中connect方法
// 实际就是event handler,实际调用了evdev_connect
error = handler->connect(handler, dev, id);
分析: evdev.c中evdev_connect()— 属于input handler层
evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
|
//找到一个没有被使用的次设备号, 从64开始, 65,66
minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);
// 实例化 一个evdev对象
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
//初始化evdev对象
INIT_LIST_HEAD(&evdev->client_list);
spin_lock_init(&evdev->client_lock);
mutex_init(&evdev->mutex);
//等待队列是完成阻塞
init_waitqueue_head(&evdev->wait);
evdev->exist = true;
dev_no = minor;
dev_no -= EVDEV_MINOR_BASE; //减去了64
// 创建设备文件/dev/event0,1,2
dev_set_name(&evdev->dev, "event%d", dev_no);
evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);// 12, 64
evdev->dev.class = &input_class;
evdev->dev.parent = &dev->dev;
evdev->dev.release = evdev_free;
device_initialize(&evdev->dev)
device_add(&evdev->dev);
//以上代码和device_create是一样
//利用handle记录input device和input handler
evdev->handle.dev = input_get_device(dev);
evdev->handle.name = dev_name(&evdev->dev);
evdev->handle.handler = handler;
//你中有有我,我中有你
evdev->handle.private = evdev;
//将儿子关联到父亲(input handler)和母亲(input dev)
error = 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);
//初始化了cdev,完成了fops, 为用户提供文件io
cdev_init(&evdev->cdev, &evdev_fops);
evdev->cdev.kobj.parent = &evdev->dev.kobj;
error = cdev_add(&evdev->cdev, evdev->dev.devt, 1);
总结:
1,分配evdev,并初始化,记录input device和handler的关系
2,创建设备节点/dev/event0
3, 注册cdev,并实现fops
4,关系:
多个input device可以对应一个event handler
一个input device对应一个 evdev,对应于一个设备节点/dev/event0,1,2
5, 所有的设备节点调用open,read,write文件io的时候
实际是调用cdev中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,
.llseek = no_llseek,
};
device_create(struct class * class, struct device * parent, dev_t devt, void * drvdata, const char * fmt, …)
|
dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
|
device_create_groups_vargs(class, parent, devt, drvdata, NULL, fmt, args);
|
struct device *dev = NULL;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
device_initialize(dev);
dev->devt = devt;
dev->class = class;
dev->parent = parent;
dev->groups = groups;
dev->release = device_create_release;
dev_set_drvdata(dev, drvdata);
kobject_set_name_vargs(&dev->kobj, fmt, args);//设置名字
device_add(dev);//注册到系统
应用是如何调用到输入子系统中去的
应用程序中调用了输入子系统的代码,数据是如何传递给用户层的?
open("/dev/event1", O_RDWR);
vfs
sys_open();
struct file file->f_ops = cdev->ops;
file->f_ops->open();
设备驱动层:输入子系统
input handler 层:evdev.c
cdev;
xxx_ops = {
.open = xxx_open,
.write = xxx_write,
}
static const struct file_operations evdev_fops = {
.owner = THIS_MODULE,
.read = evdev_read,
.write = evdev_write,
.poll = evdev_poll,
.open = evdev_open,
}
实际最终调用了evdev_open();