邮箱:zhuimengcanyang@163.com 痴爱嵌入式技术的蜗牛
分类: LINUX
2015-11-13 13:02:22
Linux输入子系统包括三个层次:事件处理层(Event Handler)、核心层(Input Core)和驱动层(Input Driver)。
1.事件层负责与用户程序打交道,将核心层传来的事件报告给用户程序。
2.核心层是链接其他两个层之间的纽带与桥梁,向下提供驱动层的接口,向上提供事件处理层的接口。
3.驱动层负责操作具体的硬件设备,这层的代码是针对具体的驱动程序的,键盘、鼠标、触摸屏等字符设备驱动功能的实现工作主要在这层。
在Linux输入子系统中,有几个重要的结构体,掌握这些结构体对理解整个输入子系统有很大的帮助:
input_dev结构体用来描述一个input设备,位于设备驱动层,一个设备能产生哪类以及该类事件中的哪些事件都是在该结构体中声明的。
struct input_dev {
const char
*name;
struct
input_id id;
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)];
unsigned
long key[BITS_TO_LONGS(KEY_CNT)];
int
(*open)(struct input_dev *dev);
void
(*close)(struct input_dev *dev);
struct
input_handle *grab;
struct device dev;
struct
list_head
h_list;
struct
list_head
node;
......
};
input_handler结构体用来描述一个事件处理器,结构体中的input_device_id *id_table成员用来和input_dev设备匹配,看是否支持该输入设备。
struct input_handler {
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; //用来匹配input_dev设备
const struct
input_device_id *blacklist;
struct
list_head
h_list;
struct
list_head
node;
};
struct input_device_id {
kernel_ulong_t flags;
__u16
bustype;
__u16
vendor;
__u16
product;
__u16
version;
kernel_ulong_t evbit[INPUT_DEVICE_ID_EV_MAX / BITS_PER_LONG +
1];
kernel_ulong_t keybit[INPUT_DEVICE_ID_KEY_MAX / BITS_PER_LONG +
1];
kernel_ulong_t relbit[INPUT_DEVICE_ID_REL_MAX / BITS_PER_LONG +
1];
kernel_ulong_t absbit[INPUT_DEVICE_ID_ABS_MAX / BITS_PER_LONG +
1];
......
kernel_ulong_t driver_info;
};
下面我们来跟踪一下内核,看看一个输入设备是如何被注册到系统中工作的。
1.申请一个输入设备
struct input_dev
*input_allocate_device(void)
@input.c
{
struct
input_dev *dev;
dev =
kzalloc(sizeof(struct input_dev), GFP_KERNEL);
device_initialize(&dev->dev);
//初始化内嵌的device结构体
INIT_LIST_HEAD(&dev->h_list);
INIT_LIST_HEAD(&dev->node);
return
dev;
}
2. 注册一个输入设备
int input_register_device(struct input_dev
*dev)
@input.c
{
struct
input_handler *handler;
error =
device_add(&dev->dev);
list_add_tail(&dev->node,
&input_dev_list);
//加入input_dev_list链表
// 对于每一个input_handler,都调用input_attach_handler
list_for_each_entry(handler, &input_handler_list,
node)
input_attach_handler(dev, handler);
}
当向系统注册一个输入设备时,系统会将该设备加入input_dev_list链表中,并从input_handler_list链表中取出每一个handler,调用input_attach_handler看看是否支持该设备,要知道当一个输入设备向系统注册时,可能会有好几个handler同时支持它。假设此时的input_handler是evdev_handler(@evdev.c)。
static int input_attach_handler(struct
input_dev *dev, struct input_handler
*handler)
@input.c
{
const struct
input_device_id *id;
id =
input_match_device(handler->id_table,
dev);
error =
handler->connect(handler, dev,
id);
}
evdev.c中input_device_id的设置:
static
const struct input_device_id evdev_ids[] = {
{
.driver_info = 1
},
{
},
};
static const struct input_device_id
*input_match_device(const struct input_device_id *id, struct
input_dev
*dev)
@input.c
{
int i;
//条件成立,进入该for循环
for (; id->flags ||
id->driver_info; id++) { #1
if (id->flags
& INPUT_DEVICE_ID_MATCH_BUS) //条件不成立,未执行
if
(id->bustype !=
dev->id.bustype)
continue;
if
(id->flags &
INPUT_DEVICE_ID_MATCH_VENDOR)
//条件不成立
if
(id->vendor != dev->id.vendor)
continue;
if (id->flags &
INPUT_DEVICE_ID_MATCH_PRODUCT)
//条件不成立
if
(id->product !=
dev->id.product)
continue;
if (id->flags
&
INPUT_DEVICE_ID_MATCH_VERSION)
//条件不成立
if
(id->version !=
dev->id.version)
continue;
MATCH_BIT(evbit,
EV_MAX);
//MATCH_BIT是一个宏可以用下面的语句替代
for (i = 0; i <
BITS_TO_LONGS(max); i++)
#2
if
((id->bit[i] &
dev->bit[i]) !=
id->bit[i])
break; //跳出 #2处的for循环
if (i !=
BITS_TO_LONGS(max))
continue; //跳出 #1处的for循环,即大循环
MATCH_BIT(keybit, KEY_MAX);
MATCH_BIT(relbit, REL_MAX);
......
return
id;
}
return
NULL;
}
到此这个handler就可以支持该输入设备,之后将会调用它的connect方法,对于evdev.c来说就是evdev_handler.connect,至于驱动层的事件如何通过核心层和事件层到达用户空间,在后续的evdev.c文件分析的时候会看到。
其实对于编写一个底层的输入设备驱动,并不需要关心这么多,因为Linux系统为我们做了很好的分层,我们只要专注核心层给我们提供的接口,使用这些接口向上注册驱动就可以了,因此编写一个输入设备驱动就变得很简单,下面是基本的框架。
1.分配input_dev结构体:input_allocate_device()
2.设置可以支持哪类事件,支持该类事件的那些事件
3.注册输入设备:input_register_device()
4.向系统报告事件:input_event()