为了更深入的了解input子系统,光把一个按键驱动写好是不够的,我们下面来看input子系统跟我们写的程序是怎么联系起来的。
我们首先来看在前面的按键输入驱动中的 struct input_dev * button_dev = input_allocate_device();
struct input_dev *input_allocate_device(void)
{
struct input_dev *dev;
dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
if (dev) {
dev->dev.type = &input_dev_type;
dev->dev.class = &input_class;
device_initialize(&dev->dev);
mutex_init(&dev->mutex);
spin_lock_init(&dev->event_lock);
INIT_LIST_HEAD(&dev->h_list);
INIT_LIST_HEAD(&dev->node);
__module_get(THIS_MODULE);
}
return dev;
}
这个程序没什么难度,主要就是分配内存,并做一些通用的初始化。
struct input_dev {
/* private: */
void *private;/* do not use */
/* public: */
const char *name;
const char *phys;
const char *uniq;
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 mscbit[BITS_TO_LONGS(MSC_CNT)];
unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
unsigned long swbit[BITS_TO_LONGS(SW_CNT)];
unsigned int keycodemax;
unsigned int keycodesize;
void *keycode;
int (*setkeycode)(struct input_dev *dev, int scancode, int keycode);
int (*getkeycode)(struct input_dev *dev, int scancode, int *keycode);
struct ff_device *ff;
unsigned int repeat_key;
struct timer_list timer;
int sync;
int abs[ABS_MAX + 1];
int rep[REP_MAX + 1];
unsigned long key[BITS_TO_LONGS(KEY_CNT)];
unsigned long led[BITS_TO_LONGS(LED_CNT)];
unsigned long snd[BITS_TO_LONGS(SND_CNT)];
unsigned long sw[BITS_TO_LONGS(SW_CNT)];
int absmax[ABS_MAX + 1];
int absmin[ABS_MAX + 1];
int absfuzz[ABS_MAX + 1];
int absflat[ABS_MAX + 1];
int (*open)(struct input_dev *dev);
void (*close)(struct input_dev *dev);
int (*flush)(struct input_dev *dev, struct file *file);
int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
struct input_handle *grab;
spinlock_t event_lock;
struct mutex mutex;
unsigned int users;
int going_away;
struct device dev;
union { /* temporarily so while we switching to struct device */
struct device *dev;
} cdev;
struct list_headh_list;
struct list_headnode;
};
接下来,在按键驱动程序中要做的就是指定好evbit、keybit 这两个成员变量,接着就是
input_register_device(button_dev);
int input_register_device(struct input_dev *dev)
{
static atomic_t input_no = ATOMIC_INIT(0);
struct input_handler *handler;
const char *path;
int error;
__set_bit(EV_SYN, dev->evbit);
/*
* If delay and period are pre-set by the driver, then autorepeating
* is handled by the driver itself and we don't do it in input.c.
*/
init_timer(&dev->timer); //内核的动态定时器:http://blog.csdn.net/daisy_chenting/article/details/6949964
//用来处理重复击键。
if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) { //如果处理重复击键的位都没有设,就赋上默认值。
dev->timer.data = (long) dev;
dev->timer.function = input_repeat_key;
dev->rep[REP_DELAY] = 250;
dev->rep[REP_PERIOD] = 33;
}
if (!dev->getkeycode)
dev->getkeycode = input_default_getkeycode;
if (!dev->setkeycode)
dev->setkeycode = input_default_setkeycode;
snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id),
"input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);
if (dev->cdev.dev)
dev->dev.parent = dev->cdev.dev;
error = device_add(&dev->dev);
if (error)
return error;
path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
printk(KERN_INFO "input: %s as %s\n",
dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
kfree(path);
error = mutex_lock_interruptible(&input_mutex);
if (error) {
device_del(&dev->dev);
return error;
}
list_add_tail(&dev->node, &input_dev_list);
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
input_wakeup_procfs_readers();
mutex_unlock(&input_mutex);
return 0;
}
里面最重要的就是这两句:
list_add_tail(&dev->node, &input_dev_list); //这个变量在static LIST_HEAD(input_handler_list)处定义
把自己的设备节点加入到input_dev_list中去。
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
且
/**
* list_for_each_entry-iterate over list of given type
* @pos:the type * to use as a loop cursor.
* @head:the head for your list.
* @member:the name of the list_struct within the struct.
*/
#define list_for_each_entry(pos, head, member)\
for (pos = list_entry((head)->next, typeof(*pos), member);\
prefetch(pos->member.next), &pos->member != (head);\
pos = list_entry(pos->member.next, typeof(*pos), member))
也就是说,
for (handler = list_entry((&input_dev_list)->next, typeof(*handler), node);\
prefetch(handler->node.next), &handler->node != (&input_dev_list); \
handler = list_entryhandler->node.next, typeof(*handler),node))
{
input_attach_handler(dev, handler);
}
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
const struct input_device_id *id; //这个结构体主要包含设备的标志信息,如产品制造商、bustype、产品ID、位的设置等
int error;
if (handler->blacklist && input_match_device(handler->blacklist, dev))
return -ENODEV;
id = input_match_device(handler->id_table, dev);
if (!id)
return -ENODEV;
error = handler->connect(handler, dev, id);
if (error && error != -ENODEV)
printk(KERN_ERR
"input: failed to attach handler %s to device %s, "
"error: %d\n",
handler->name, kobject_name(&dev->dev.kobj), error);
return error;
}
说明:
1、handler->blacklist就是黑名单的意思,也就是说,即使dev匹配handler里面的id_table里的一员,也不能匹配。
2、如果dev不在黑名单之内,就匹配id_table这个链表,如果链表里有一项匹配成功,就调用handler->connect(handler, dev, id)
对这一篇文章做个总结:
在按键驱动程序的input_allocate_device --> 设置相应的位 --> input_register_device 这个过程中,
其实就是为自己的struct input_dev 分配内存并初始化一些通用的位 -->
设置一些与自己输入设备相关的特殊位-->
在全局的input_handler_list中,找到相匹配的handler,并调用这个handler的connect函数。
在我们的按键输入驱动中,当按键按下时,在中断处理程序中,主要就是向input子系统报告输入事件:
input_report_key(button_dev, BTN_0, 1);
input_sync(button_dev);
我们不需要自己考虑按键重复按下的问题,因为input_report_key会自动检查这个问题。
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_KEY, code, !!value);
}
void input_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
unsigned long flags;
if (is_event_supported(type, dev->evbit, EV_MAX)) {
spin_lock_irqsave(&dev->event_lock, flags);
add_input_randomness(type, code, value); //这一句会我们学习输入事件的报告是没有用处的。
input_handle_event(dev, type, code, value);
spin_unlock_irqrestore(&dev->event_lock, flags);
}
}
说到dev->evbit,相当于用一个unsigned long类型就足够了,每一位表明了设备支持的输入事件,1表示支持。
对这些位的设置,我们可以参考include/linux/input.h这个文件,比如:
/*
* Event types
*/
#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)
我们的按键就简单的支持EV_KEY。
我们继续看上文的input_handle_event(dev, type, code, value);这一句:
我们传进去的参数type是EV_KEY、code是BTN_0、value是1 .
static void input_handle_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
int disposition = INPUT_IGNORE_EVENT;
switch (type) {
case EV_SYN:
switch (code) {
case SYN_CONFIG:
disposition = INPUT_PASS_TO_ALL;
break;
case SYN_REPORT:
if (!dev->sync) {
dev->sync = 1;
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
}
break;
case EV_KEY:
if (is_event_supported(code, dev->keybit, KEY_MAX) &&
!!test_bit(code, dev->key) != value) {
if (value != 2) {
__change_bit(code, dev->key);
if (value)
input_start_autorepeat(dev, code);
}
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
case EV_SW:
if (is_event_supported(code, dev->swbit, SW_MAX) &&
!!test_bit(code, dev->sw) != value) {
__change_bit(code, dev->sw);
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
case EV_ABS:
if (is_event_supported(code, dev->absbit, ABS_MAX)) {
value = input_defuzz_abs_event(value,
dev->abs[code], dev->absfuzz[code]);
if (dev->abs[code] != value) {
dev->abs[code] = value;
disposition = INPUT_PASS_TO_HANDLERS;
}
}
break;
case EV_REL:
if (is_event_supported(code, dev->relbit, REL_MAX) && value)
disposition = INPUT_PASS_TO_HANDLERS;
break;
case EV_MSC:
if (is_event_supported(code, dev->mscbit, MSC_MAX))
disposition = INPUT_PASS_TO_ALL;
break;
case EV_LED:
if (is_event_supported(code, dev->ledbit, LED_MAX) &&
!!test_bit(code, dev->led) != value) {
__change_bit(code, dev->led);
disposition = INPUT_PASS_TO_ALL;
}
break;
case EV_SND:
if (is_event_supported(code, dev->sndbit, SND_MAX)) {
if (!!test_bit(code, dev->snd) != !!value)
__change_bit(code, dev->snd);
disposition = INPUT_PASS_TO_ALL;
}
break;
case EV_REP:
if (code <= REP_MAX && value >= 0 && dev->rep[code] != value) {
dev->rep[code] = value;
disposition = INPUT_PASS_TO_ALL;
}
break;
case EV_FF:
if (value >= 0)
disposition = INPUT_PASS_TO_ALL;
break;
case EV_PWR:
disposition = INPUT_PASS_TO_ALL;
break;
}
if (type != EV_SYN)
dev->sync = 0;
if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
dev->event(dev, type, code, value);
if (disposition & INPUT_PASS_TO_HANDLERS)
input_pass_event(dev, type, code, value);
}
程序说明:
我们主要看EV_KEY相关的代码,函数的disposition表示使用什么方式处理事件,被初始化为忽略事件,如果没有被改变设置为其他的处理方式,最终会被忽略。
test_bit(code, dev->key)是用来测试按键状态是否改变的,__change_bit(code, dev->key);用来改变按键状态,二而我们传进去的value值是1,所以执行input_start_autorepeat(dev, code)来处理重复按键的情况。
若diaposition 的值为INPUT_PASS_TO_HANDLERS说明事件要会传给handler处理;
而INPUT_PASS_TO_DEVICE说明应该由设备自行处理,如果dev->even函数非空,就调用该函数进行处理。(这种情况通常发生在让LED点亮、蜂鸣器鸣叫等)
接下来最重要的就是:input_pass_event(dev, type, code, value)这一句了,我们来看这个函数:
static void input_pass_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
struct input_handle *handle;
rcu_read_lock();
handle = rcu_dereference(dev->grab);
if (handle)
handle->handler->event(handle, type, code, value);
else
list_for_each_entry_rcu(handle, &dev->h_list, d_node)
if (handle->open)
handle->handler->event(handle,
type, code, value);
rcu_read_unlock();
}
程序说明:
1、dev的grap就是强制为input_device的handle,如果它不为空,就调用它的even函数来处理,也就是说,dev与该handler已经处于绑定的状态。
2、未绑定,就遍历dev->h_list里的handle节点,如果handle被打开,表示该设备已经被一个用户进程使用,就会调用与输入设备对应的handler的even函数。(只有handle被打开,才需要向用户空间导出信息)。
那么handle->handler->even函数是什么时候定义的呢?又如何向用户空间导出信息?我们现在还不能非常清晰将整个input子系统完整的画出来,需要继续往后看后续的文章。