输入子系统的意义
为驱动定义一个标准的编程格式,对于驱动,我们只需要关注input_register_device这个函数去注册设备,以及input_dev这个结构体去决定我们需要提交的事件type,code,value。
为用户空间获取的数据格式是统一的(input_event的结构体),在app层,只需要判断type,code,从而获取value就可以知道输入设备的状态。
针对输入设备: button, keyboard, mouse, ts, gsensor, joystick
输入子系统整体框架
应用层
------------------------------------
input handler 数据处理层: drivers/input/evdev.c
1, 和用户进行交互,实现fops
2, 不知道数据是如何得到,但是知道如何将数据交给用户
-------------------------------------
input core层--drivers/input/input.c
1,维护了两个链表
2, 为上下两层提供接口
---------------------------------------
input device层: 输入设备硬件层--自己去写
自带的input device代码:
drivers/input/touchscreen/
1, 初始化硬件,获取硬件中数据
2, 知道得到数据,但是不知道如何将数据给用
-------------------------------------------
使用输入子系统示例
先来看一下,如果我们要是用输入子系统,大概是一个怎么样的框架吧,以鼠标为例。
//初始化阶段
struct input_dev* minput_dev;
minput_dev = input_allocate_device();
minput_dev->name = "sc usb";
set_bit(EV_KEY, minput_dev->evbit);
set_bit(EV_REL, minput_dev->evbit);
set_bit(BTN_LEFT, minput_dev->keybit);
set_bit(BTN_RIGHT, minput_dev->keybit);
set_bit(BTN_MIDDLE, minput_dev->keybit);
set_bit(REL_X, minput_dev->relbit);
set_bit(REL_Y, minput_dev->relbit);
set_bit(REL_WHEEL, minput_dev->relbit);
input_register_device(myusb.minput_dev);
**//上报数据阶段,按下为1,松开为0**
input_report_key(dev, BTN_LEFT, data[1] & 0x01);
input_report_key(dev, BTN_RIGHT, data[1] & 0x02);
input_report_key(dev, BTN_MIDDLE, data[1] & 0x04);
input_report_rel(dev, REL_X, data[2]);
input_report_rel(dev, REL_Y, data[3]);
input_report_rel(dev, REL_WHEEL, data[5]);
input_sync(dev);
//下面是input_dev 结构体,我只列出了比较有用的。
struct input_dev {
const char *name;
unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
struct device dev;
struct list_head h_list;
struct list_head node;
};
#define EV_SYN 0x00
#define EV_KEY 0x01
#define EV_REL 0x02
#define EV_ABS 0x03
#define EV_MSC 0x04
#define EV_SW 0x05
#define EV_LED 0x11
#define EV_SND 0x12
#define EV_REP 0x14
#define EV_FF 0x15
#define EV_PWR 0x16
#define EV_FF_STATUS 0x17
#define EV_MAX 0x1f
#define EV_CNT (EV_MAX+1)
//下面代码指,使能哪一个功能,我们这里使能了按键,使能了相对位移
set_bit(EV_KEY, minput_dev->evbit);
set_bit(EV_REL, minput_dev->evbit);
//说明使能哪些按键(鼠标左键、右键、中建),使能哪个方向的相对位移
set_bit(BTN_LEFT, minput_dev->keybit);
set_bit(BTN_RIGHT, minput_dev->keybit);
set_bit(BTN_MIDDLE, minput_dev->keybit);
set_bit(REL_X, minput_dev->relbit);
set_bit(REL_Y, minput_dev->relbit);
set_bit(REL_WHEEL, minput_dev->relbit);
//上报数据函数原型,value对应:按下为1,松开为0,需要我们自己赋值
input_report_key(struct input_dev *dev, unsigned int code, int value)
//下面函数代表一次完整的上报结束
input_sync(dev);
这些就是input的全部代码了,还是比较简单的,接下来我们进行源码分析。
代码跟读—drivers/input/input.c
1.drivers/input/input.c
static int __init input_init(void)
|
class_register(&input_class) //注册input class类
input_proc_init() //在proc目录下,生成各种文件
|
proc_mkdir("bus/input", NULL);
proc_create("devices", 0, proc_bus_input_dir, &input_devices_fileops);
proc_create("handlers", 0, proc_bus_input_dir,&input_handlers_fileops);
//申请设备号13,生成input_fops,
register_chrdev(INPUT_MAJOR, "input", &input_fops);
代码跟读—drivers/input/evdev.c
首先是几个结构体和宏定义需要我们掌握
#define EVDEV_MINOR_BASE 64
#define EVDEV_MINORS 32
static struct input_handler evdev_handler = {
.event = evdev_event,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.fops = &evdev_fops,
.minor = EVDEV_MINOR_BASE, 64
.name = "evdev",
.id_table = evdev_ids,
};
struct evdev {
int minor;
struct input_handle handle;
struct evdev_client __rcu *grab;
struct list_head client_list;
struct device dev;
};
struct input_handle {
void *private;
int open;
const char *name;
struct input_dev *dev;
struct input_handler *handler;
struct list_head d_node;
struct list_head h_node;
};
下面分析注册handler
```input_register_handler(&evdev_handler)
|
INIT_LIST_HEAD(&handler->h_list); //初始化handler链表
//64 = 2,在input_table[2]存放evdev_handler
if (input_table[handler->minor >> 5]) {
retval = -EBUSY;
goto out;
}
input_table[handler->minor >> 5] = handler;
list_add_tail(&handler->node, &input_handler_list); //将node节点放入input_handler_list中
//在input_dev_list链表中遍历dev节点,将handler和dev进行匹配
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);
|
//如果匹配成功,调用handler中的connect方法
handler->connect(handler, dev, id);
调用的connect方法:
.connect = evdev_connect,
evdev_connect(struct input_handler *handler, struct input_dev *dev,const struct input_device_id *id)
|
//从0到32,在evdev_table[]中找一个空的数组
for (minor = 0; minor < EVDEV_MINORS; minor++)
if (!evdev_table[minor])
break;
//初始化client_list
INIT_LIST_HEAD(&evdev->client_list);
//初始化等待队列头
init_waitqueue_head(&evdev->wait);
//对evdev进行初始化,生成设备节点,所以我们会看到设备节点 event0 1 2 3
dev_set_name(&evdev->dev, "event%d", minor);
evdev->exist = true;
evdev->minor = minor;
evdev->handle.dev = input_get_device(dev);
evdev->handle.name = dev_name(&evdev->dev);
evdev->handle.handler = handler;
evdev->handle.private = evdev;
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);
device_add(&evdev->dev)
//inputdev, input handler, input handle建立双向关系
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);
//把当前的evdev放入evdev_table[]
evdev_install_chrdev(evdev);
|
evdev_table[evdev->minor] = evdev;
## dev和handler怎么匹配
匹配机制是,全部匹配,因为他的条件是.driver_info = 1,所有dev都满足这个条件,所以全部匹配
static const struct input_device_id evdev_ids[] = {
{ .driver_info = 1 }, /* Matches all devices */
{ }, /* Terminating zero entry */
};
## 初始化部分的代码到这里就结束了,下一章是应用和驱动是如何交互