input驱动在linux内核中经常会用到,比如按键类: power button,一般用gpio来做按键模拟,还有touch key等。触摸屏,键盘鼠标,gsensor,gameport等。本文结合一例子对input驱动模型做探讨。
下面是linux文档中的例子。
#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
#include <asm/irq.h>
#include <asm/io.h>
static struct input_dev *button_dev;//将该button设备定义为input_dev类型
static irqreturn_t button_interrupt(int irq, void *dummy)//中断处理函数
{
input_report_key(button_dev, BTN_0, inb(BUTTON_PORT) & 1);//向上层汇报键值//BTN_0,inb(BUTTON_PORT) & 1表示是通码还是断码
input_sync(button_dev);//等待结构体数据都填好后,即数据同步后发送给上层。
return IRQ_HANDLED;
}
static int __init button_init(void)//button设备的初始化
{
int error;
//中断申请,BUTTON_IRQ是中断源,button_interrupt是中断处理函数,0表示中//断类型,是上升沿,下降沿之类,在linux 2.6.35表示0是IRQF_TRIGGER_NONE //类型;”button”是设备名字;NULL表示设备的相关数据为空
if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL)) {
printk(KERN_ERR "button.c: Can't allocate irq %d\n", button_irq);
return -EBUSY;
}
//分配设备内存
button_dev = input_allocate_device();
if (!button_dev) {
printk(KERN_ERR "button.c: Not enough memory\n");
error = -ENOMEM;
goto err_free_irq;
}
//属性设置
button_dev->evbit[0] = BIT_MASK(EV_KEY);//表示该input设备是按键事件
button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);//设置按键的键值
error = input_register_device(button_dev);//注册input设备
if (error) {
printk(KERN_ERR "button.c: Failed to register device\n");
goto err_free_dev;
}
return 0;
err_free_dev:
input_free_device(button_dev);// 注册失败,释放已注册的部分
err_free_irq:
free_irq(BUTTON_IRQ, button_interrupt);//释放中断
return error;
}
static void __exit button_exit(void)
{
input_unregister_device(button_dev);// 注销注册
free_irq(BUTTON_IRQ, button_interrupt);
}
module_init(button_init); // button设备初始化部分,生成模块。Linux驱动特有的结构
module_exit(button_exit);//设备注销模块
上面是比较简单的注册 input 设备中断处理例子。
注意几个API函数:
1):
set_bit(EV_KEY, button_dev.evbit); set_bit(BTN_0, button_dev.keybit);
分别用来设置设备所产生的事件以及上报的按键值。Struct iput_dev 中有两个成员,一个是evbit.一个是keybit.分别用表示设备所支持的动作和按键类型。
2): input_register_device(&button_dev);
用来注册一个input device.
3): input_report_key()
用来上层上报一个按键动作
4): input_sync()
等待结构体数据都填好后,本次事件操作已完成,数据同步后发送给上层。
一.首先来分析input_allocate_device
struct input_dev *input_allocate_device(void)
{
struct input_dev *dev;
// 使用kzalloc函数分配内存
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;
}
内存分配成功后,向其成员变量赋值,主要包括input_dev_type(包括设备属性group,设备uevent事件),input_class(设备名以及设备节点),以及设备初始化(kobject是在里初始化的,为创建设备文件系统做准备),还有自旋锁的初始化等。
二、注册函数input_register_device
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;
/* Every input device generates EV_SYN/SYN_REPORT events. */
//所有设备都支持EV_SYN这种事件类型
__set_bit(EV_SYN, dev->evbit);
/* KEY_RESERVED is not supposed to be transmitted to userspace. */
__clear_bit(KEY_RESERVED, dev->keybit);
/* Make sure that bitmasks not mentioned in dev->evbit are clean. */
input_cleanse_bitmasks(dev);
/*
* 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);
//如果没有设置重复按键REP_DELAY和REP_PERIOD的值,下面会赋默认值
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;
dev_set_name(&dev->dev, "input%ld",
(unsigned long) atomic_inc_return(&input_no) - 1);
//添加到设备列表,总的设备增加一个
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;
}
//把设备节点添加到input设备的链表的尾部
list_add_tail(&dev->node, &input_dev_list);
/*下面是device和handler的匹配。将input device 挂到input_dev_list 链表上.然后,对每一个挂在input_handler_list 的handler 调用input_attach_handler().在这里的情况有好比设备模型中的device 和driver 的匹配。所有的input device 都挂在input_dev_list 链上。所有的handle 都挂在input_handler_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;
}
接下来来看input_attach_handler:
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
const struct input_device_id *id;
int error;
//具体的匹配在input_match_device中完成,得到一个id
id = input_match_device(handler, dev);
if (!id)
return -ENODEV;
//匹配成功,调用handler->connect
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;
}
分析input_match_device:
#define MATCH_BIT(bit, max) \
for (i = 0; i < BITS_TO_LONGS(max); i++) \
if ((id->bit[i] & dev->bit[i]) != id->bit[i]) \
break; \
if (i != BITS_TO_LONGS(max)) \
continue;
static const struct input_device_id *input_match_device(struct input_handler *handler,
struct input_dev *dev)
{
const struct input_device_id *id;
int i;
for (id = handler->id_table; id->flags || id->driver_info; id++) {
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(keybit, KEY_MAX);
MATCH_BIT(relbit, REL_MAX);
MATCH_BIT(absbit, ABS_MAX);
MATCH_BIT(mscbit, MSC_MAX);
MATCH_BIT(ledbit, LED_MAX);
MATCH_BIT(sndbit, SND_MAX);
MATCH_BIT(ffbit, FF_MAX);
MATCH_BIT(swbit, SW_MAX);
if (!handler->match || handler->match(handler, dev))
return id;
}
return NULL;
}
大概意思是,id是由handler->id_table赋值的,先通过id->flags依次匹配INPUT_DEVICE_ID_MATCH_BUS(总线类型),INPUT_DEVICE_ID_MATCH_VENDOR(设备厂商),INPUT_DEVICE_ID_MATCH_PRODUCT(设备),INPUT_DEVICE_ID_MATCH_VERSION(设备版本)。如果id->flags没有定义这些,则往下执行,通过MATCH_BIT宏依次匹配evbit,keybit这些位。
在这里,我们关注下handler和handle
三、注册handler input_register_handler
对input device而言,Handler是interface
int input_register_handler(struct input_handler *handler)
{
struct input_dev *dev;
int retval;
retval = mutex_lock_interruptible(&input_mutex);
if (retval)
return retval;
//初始化handler链表头
INIT_LIST_HEAD(&handler->h_list);
// handler->minor是设备的设备号,右移五位存放到input_table[]里
if (handler->fops != NULL) {
if (input_table[handler->minor >> 5]) {
retval = -EBUSY;
goto out;
}
input_table[handler->minor >> 5] = handler;
}
//在input_handler_lis尾部加入该handler节点
list_add_tail(&handler->node, &input_handler_list);
//对每一个挂在input_ dev _list 的device 调用input_attach_handler()
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);
input_wakeup_procfs_readers();
out:
mutex_unlock(&input_mutex);
return retval;
}
这和input_register_device函数有点相似。
四、关于handle
在代码里,handle类型是input_handle,它的数据结构如下:
struct input_handle {
void *private;
int open;
const char *name;//设备名
struct input_dev *dev;//input_dev
struct input_handler *handler;//handler
struct list_head d_node;// input设备节点链表结构
struct list_head h_node;// handler节点的链表结构
};
通过上述结构体,它包含了input设备和handler。Handle的注册如下:
//注意这段英文注释
/* This function is supposed to be called from handler's
* connect() method.
*/
int input_register_handle(struct input_handle *handle)
{
struct input_handler *handler = handle->handler;
struct input_dev *dev = handle->dev;
int error;
/*
* We take dev->mutex here to prevent race with
* input_release_device().
*/
error = mutex_lock_interruptible(&dev->mutex);
if (error)
return error;
/*
* Filters go to the head of the list, normal handlers
* to the tail.
*/
//将handle挂到input设备的h_list上
if (handler->filter)
list_add_rcu(&handle->d_node, &dev->h_list);
else
list_add_tail_rcu(&handle->d_node, &dev->h_list);
mutex_unlock(&dev->mutex);
/*下面这段英文注释,结合上面的英文注释,会有点奇怪,handle的注册和/*handler的connect方法有什么联系?前面handler和input设备匹配成功会调用handler的connect方法,连接的意思。Handle也是连接handler和input设备。Handler的connect 方 法是回调函数,具体函数内容没有明白,再加上handle的注册和handler的connect方法有什么联系,哪位朋友知道,请告之。*/
/*
* Since we are supposed to be called from ->connect()
* which is mutually exclusive with ->disconnect()
* we can't be racing with input_unregister_handle()
* and so separate lock is not needed here.
*/
//也将handle挂到handler的h_list上
list_add_tail_rcu(&handle->h_node, &handler->h_list);
//如果handler有定义start方法,就调用它,注意传进来的参数是handle,也//就是启动handle,这样完成注册
if (handler->start)
handler->start(handle);
return 0;
}
通过分析handle的注册函数,handle分别挂在input device和handler的链表上, 实现三者的关联。handler通过ID与input device匹配,关联由handle完成。
五、报告按键情况函数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);
}
它调用input_event函数
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);
//利用input device输入来产生随机数
add_input_randomness(type, code, value);
//对事件类型进行处理
input_handle_event(dev, type, code, value);
spin_unlock_irqrestore(&dev->event_lock, flags);
}
}
Input_handle_event函数:
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;
case SYN_MT_REPORT:
dev->sync = 0;
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);//重复按键
else
input_stop_autorepeat(dev);
}
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)) {
if (test_bit(code, input_abs_bypass)) {
disposition = INPUT_PASS_TO_HANDLERS;
break;
}
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 (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
dev->sync = 0;
if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
dev->event(dev, type, code, value);
//需不需要handler参与进来
if (disposition & INPUT_PASS_TO_HANDLERS)
input_pass_event(dev, type, code, value);//处理事件
}
input_pass_event函数:
static void input_pass_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
struct input_handler *handler;
struct input_handle *handle;
rcu_read_lock();
handle = rcu_dereference(dev->grab);
if (handle)
handle->handler->event(handle, type, code, value);
else {
bool filtered = false;
list_for_each_entry_rcu(handle, &dev->h_list, d_node) {
if (!handle->open)
continue;
handler = handle->handler;
if (!handler->filter) {
if (filtered)
break;
handler->event(handle, type, code, value);
} else if (handler->filter(handle, type, code, value))
filtered = true;
}
}
rcu_read_unlock();
}
函数功能:grab是input_handle类型指针,是input_dev*dev的成员变量,下面函数rcu_dereference意思是去取dev里的handle,如果指定了handle,则调用handle->handler->event函数处理事件;若input device没有指定handle,则遍历input dev的h_list上的handle,handle若打开,则将该打开的handle的成员handler重新赋给handler,随后调用这个handler的event方法。有事件产生时,input device的handle会被打开。处理事件交给与input device相对应的handle->handler->event函数处理。
结合linux文档例子,分析input驱动的API函数,对这些函数做了进一步探讨。