input子系统——linux驱动学习笔记(二)

为了更深入的了解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子系统完整的画出来,需要继续往后看后续的文章。






    





  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值