输入子系统:
为何引入:
以前我们写一些输入设备(键盘鼠标等)的驱动都是采用字符设备、混杂设备处理的。
为了实现对分散的、不同类别的输入设备进行统一驱动,出现了输入子系统
自己写驱动程序时需要: 1 major
2 file_operation
3 register_chrdev
4 入口函数
5 出口函数
输入子系统: 别人已经帮你做好上述
我们需要弄清楚把自己代码融合进去
输入子系统框架
总体框架:
//用户编程的接口(设备节点),并处理驱动层提交的数据
输入子系统事件处理层 *evdev.c //输入事件驱动
//为设备驱动层提供了规范和接口。设备驱动层只要关心如何驱动硬件并获得硬件数据,然后调用核心层提供的接口,核心层会自动把数据提交给事件处理层
//它的存在屏蔽了用户到设备驱动的交互细节,为设备驱动层和事件处理层提供了相互通信的统一界面
输入子系统核心层 input.c
//主要完成对硬件设备的读写访问,中断设置,并把硬件产生的事件转换成核心层定义的规范交给事件处理层
输入子系统设备驱动层 // 输入设备驱动
input_table[] 全局 input_handler类型的数组
input.c 核心层: 中转作用
入口函数:
input_init: //创建设备类并注册设备,对照自己写的驱动,为了可以自动挂接,还没有class_device_create,
//应该是在我们input_register_device之后,调用handler的connect函数建立连接时在类下创建设备
err = class_register(&input_class);
err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
static const struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
};
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;
};
问:只有一个open函数如何读
input_open_file:
struct input_handler *handler = input_table[iminor(inode) >> 5];
new_fops = fops_get(handler->fops)
file->f_op = new_fops; // ->&evdev_fops
err = new_fops->open(inode, file);
app: read>...>file->f_op->read
input_table数组由谁构造:
input_register_handler:input_table[handler->minor >> 5] = handler; 纯软件构造
evdev.c keyboard.c mousedev.c -> input_register_handler向核心层注册,设备子系统已经向核心层注册,我们不用管
设备: input_register_device->input.c 硬件
建立handler与device联系:
input_device结构 与input_handler结构中的.id_table比较
可以的话调用input_handler.connect函数建立连接
input_register_device: //设备驱动注册为设备委托内核做的事情
list_add_tail(&dev->node, &input_dev_list); //将输入设备挂接到输入设备链表input_dev_list中
//查找并匹配输入设备对应的事件处理层,通过input_handler_list链表
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler); //根据input_handler的id_table判断能否支持这个input_device
input_attach_handler:
id = input_match_device(handler->id_table, dev);
input_device结构 与input_handler结构中的.id_table比较
可以的话调用input_handler.connect函数建立连接
error = handler->connect(handler, dev, id);
handler->connect(handler, dev, id);:如何具体的连接:
通过evdev->handle,handle结构有两个变量分别是dev,input_handler
输入设备驱动最终的目的就是能够与事件处理层的事件驱动相互匹配,但是在drivers/input目录下有evdev.c事件驱动、mousedev.c事件驱动、joydev.c事件驱动
等等,我们的输入设备产生的事件应该最终上报给谁,然后让事件驱动再去处理呢?
其实evdev.c mousedev.c等genuine硬件输入设备的处理方式的不同抽象除了不同
的事件处理接口帮助上层去调用而我们写的设备驱动程序只不过是完成了硬件寄存器中数
据的读写,但提交给用户的事件必须是经过事件处理层的封装和同步才能够完成的,事件处
理层提供给用户一个统一的界面来操作。
整个关联过程:
input_register_device->input_attach_handler->input_match_device->handler->connect
->input_register_handle(evdev.c)->input_dev,handler->input_dev_list,input_handler_list
核心层与事件处理层建立联系:
err = class_register(&input_class); //创建类
err = register_chrdev(INPUT_MAJOR, "input", &input_fops); //注册input设备
input_register_handler:
//放入数组
input_table[handler->minor >> 5] = handler;
//放入链表
list_add_tail(&handler->node, &input_handler_list);
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);
怎么建立连接:
1.分配一个input_handle结构体
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); //分配一个input_handle结构
2.设置input_handle结构体
input_handle.dev = input_dev;
input_handle_handler = input_handler
3.注册
input_handler->dev = &input_handle
input_dev->h_list = &input_handle
//设置
evdev->handle.dev = dev;
evdev->handle.name = evdev->name;
evdev->handle.handler = handler;
evdev->handle.private = evdev;
sprintf(evdev->name, "event%d", minor);
//注册
error = input_register_handle(&evdev->handle);
input_device 与 input_handler中均有.h_list ,两者通过.h_list找到input_handle,
然后分别通过input_handle的.handler与.dev找到相应的handler与.dev
怎么读按键?
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处理函数
gpio_keys_isr
//上报事件
input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value):
struct input_handle *handle;
list_for_each_entry(handle, &dev->h_list, d_node)
if (handle->open)
handle->handler->event(handle, type, code, value);
怎么写符合输入子系统框架的驱动:
1.分配一个input_dev结构体
2.设置
3.注册
4.硬件相关的代码,比如在中断服务程序里上报事件
LINUX输入子系统详解
最新推荐文章于 2024-08-29 21:09:14 发布