input输入子系统
在使用Open的时候,就自己能够使用。如果需要将驱动程序制作成通用的。就需要使用到输入子系统,就是在现成的驱动中修改成自己板子需要的驱动。
input输入子系统
自己写的时候
1.确定主设备号major
2.构造一个file_operetion结构体
里面有Open
write
read
close等函数
3.使用register_chrdev告诉内核(注册这个驱动)
4.由入口函数来调用register_chrdev来挂载驱动
5.由出口函数来卸载掉驱动
input子系统
上面所述的1.2.3.4.5.都是有的,是系统已经做好了的。(现成的)
输入子系统框架:
input.c——->核心层
drivers/input/input.c >err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
使用了这样的一个结构体 input_fops
static const struct file_operations input_fops = { .owner = THIS_MODULE, .open = input_open_file, };
通过上面的结构体上可以看出 只有一个open函数,那么到底是怎么实现读写的了?
input_open_file >struct input_handler *handler = input_table[iminor(inode) >> 5];\ new_fops = fops_get(handler->fops) file->f_op = new_fops; err = new_fops->open(inode, file);
所以app,来read的时候最终就会调用到 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_handler 纯软件概念
注册input_handler:
input_register_handler
//放入数组
input_table[handler->minor >> 5] = handler;
//放入链表
list_add_tail(&handler->node, &input_handler_list); //同样的是将input_handler放入一个链表中去
//对于每一个input_dev调用 input_attach_handler
list_for_each_entry(dev, &input_dev_list, node) input_attach_handler(dev, handler);//根据input_handler的id_table判断能否能够支持这个input_dev
注册输入设备:
input_register_device
list_add_tail(&dev->node, &input_dev_list);//将输入设备放入一个链表中去 list_for_each_entry(handler, &input_handler_list, node) //对于每一个input_handler都调用input_attach_handler input_attach_handler(dev, handler);//根据input_handler的id_table判断能否能够支持这个input_dev
input_attach_handler:
id = input_match_device(handler->id_table, dev);//判断handler->id_table 是否和dev匹配 error = handler->connect(handler, dev, id);//若匹配则调用handler->connect()函数
小结:在注册input_dev或者input_handler时,会两个相互比较 左边的input_dev和右边的input_handler,
根据input_handler 的id_teble判断这个input_handler能否支持当前的input_dev,
如果能够支持就调用input_handler的connect()函数建立连接。
怎么建立连接了?
比如说evdev.c的input_handler结构如图
建立evdev的连接使用evdev_connect函数
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);//分配了一个input_handle结构
//设置input_handle
evdev->handle.dev = dev; //指向input_dev结构体 evdev->handle.name = evdev->name; evdev->handle.handler = handler; //指向右边的input_handler结构体 evdev->handle.private = evdev; error = input_register_handle(&evdev->handle);//注册这个input_handle
从上可以看出在链接的时候就构造一个input_handle结构体
evdev->handle.dev = dev; //指向input_dev结构体 evdev->handle.handler = handler; //指向右边的input_handler结构体
从下面的加入链表的动作中可以看出,两边都有一个h_lsit链表 ,这两个h_list都指向 input_handle,通过这个步骤就建立起了链接。
可以通过input_handler中的h_list找到input_dev .。同样反过来也可以input_dev,通过自身的h_list找到input_handler。这样就建立起了链接了
list_add_tail(&handle->d_node, &handle->dev->h_list);
list_add_tail(&handle->h_node, &handler->h_list);
小结:
1.分配一个input_handle结构体
2.设置这个input_handle
input_handle->handle.dev = input_dev; //指向input_dev结构体 input_handle->handle.handler = intp_handler; //指向右边的input_handler结构体
3.注册这个input_handle
error = input_register_handle(&evdev->handle);//注册这个input_handle input_handler->h_list = &input_handle; input_dev->h_list = &input_handle;
怎么读按键了?
app read
———————————-
…….
evdev_read
//无数据并且是非阻塞方式打开的话 就直接返回 -EAGAIN 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_read
evdev_event 被谁调用?
应该是硬件相关的代码来唤醒,也就是input_dev那层的东西来调用,evdev_event
在设备的中断服务程序里确定事件是什么,然后调用相应的input_handler的envet处理函数
//向操作系统上报事件
input_event(input, type, button->code, !!state);
input_sync(input); 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) //查找列表中的handle if (handle->open) //如果这个handle打开了 handle->handler->event(handle, type, code, value);//这一步就是调用右边的input_handler的event函数
怎么写一个符合输入子系统框架的驱动程序?
1.分配一个input_dev结构体
2.设置
3.注册
4.硬件相关代码,如中断服务程序上报事件。