免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2485 | 回复: 0
打印 上一主题 下一主题

Linux驱动之input子系统浅析 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-12-14 18:26 |只看该作者 |倒序浏览

一,前言:
1, 近日要写一个很特殊的键盘驱动,故对linux的input子系统分析了一番,写下分析笔记,以防不日即忘。
2, 使用input子系统的一般流程为:input_allocate_device()申请一个input_dev设备——>初始化该input_dev——>input_register_device()向子系统注册该设备——>中断时input_event()向子系统报告事件。此流程一目了然,即使不看input子系统,我们按照它给出的一般流程也可以把自己写的IO驱动加入到input子系统里去,但input_event()提交完数据后,我们的数据去哪了呢,怎么处理呢,总觉得云里雾里的,总想一探究竟。
3,此分析基于linux内核:2.6.19.2
二:下面给出一个简单的驱动,用定时器来模拟中断来提交键盘的事件。
#include
#include
#include
#include
#include
#include
#include
#include

/*! Input device structure. */
static struct input_dev *serkbd_dev = NULL;
static struct timer_list report_timer;

static unsigned short t_interval = 2*HZ;

#define  KEYCODES 8

#define press_left_code     30
#define press_right_code    29
#define press_up_code       28
#define press_down_code     27

#define rel_left_code       158
#define rel_right_code      157
#define rel_up_code         156
#define rel_down_code       155

static u16 serkpd_keycodes[KEYCODES] = {
       press_left_code, press_right_code,
       press_up_code, press_down_code,
       rel_left_code, rel_right_code,
       rel_up_code, rel_down_code
};
static void ser_kpp_handle_timer(unsigned long data)
{
       static int i=0;
      
          input_event(serkbd_dev, EV_KEY,
                                                     serkpd_keycodes
                                                     [i/2], ((i%2)==0)?1:0);
       if(++i >=2*KEYCODES)
              i=0;

       report_timer.expires = jiffies + t_interval;
       add_timer(&report_timer);
}
static int ser_kpp_open(struct input_dev *dev)
{
       return 0;
}
static void ser_kpp_close(struct input_dev *dev)
{
}
static int init_ser_keyb()
{
       int i, irq;
       int retval;
       unsigned int reg_val;

       serkbd_dev = input_allocate_device();
       if (!serkbd_dev) {
              printk(KERN_ERR
                     "serkbd_dev: not enough memory for input device\n");
              return -ENOMEM;
       }

       serkbd_dev->keycode = &serkpd_keycodes;
       serkbd_dev->keycodesize = sizeof(unsigned char);
       serkbd_dev->keycodemax = KEYCODES;
       serkbd_dev->name = "serkpd";
       serkbd_dev->id.bustype = BUS_HOST;
       serkbd_dev->open = ser_kpp_open;
       serkbd_dev->close = ser_kpp_close;


       __set_bit(EV_KEY, serkbd_dev->evbit);
//设定其支持的事件码
       for (i = 0; i
              __set_bit(serkpd_keycodes, serkbd_dev->keybit);

       input_register_device(serkbd_dev);
      
       /* Initialize the polling timer */
       init_timer(&report_timer);
       report_timer.expires = jiffies + t_interval;
       report_timer.function = ser_kpp_handle_timer;
       add_timer(&report_timer);
       return 0;
}


static int __init ser_kpp_init(void)
{
      
       if(init_ser_keyb()){
              printk(KERN_INFO "Cannot insmod ser_kpp\n");
              return 1;
       }else{
              printk(KERN_INFO "ser-keypad init\n");
              return 0;
       }
}

static void __exit ser_kpp_cleanup(void)
{
       del_timer(&report_timer);
       input_unregister_device(serkbd_dev);

       if (serkbd_dev)
              input_free_device(serkbd_dev);
}

module_init(ser_kpp_init);
module_exit(ser_kpp_cleanup);

MODULE_AUTHOR("sparkle-cliz@sohu.com");
MODULE_DESCRIPTION("input test Driver");
MODULE_LICENSE("GPL");

现在就利用上面这个驱动来分析一下:
1,  input_allocate_device()定义在\drivers\input\input.c下:
struct input_dev *input_allocate_device(void)
{
       struct input_dev *dev;

       dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
       if (dev) {
              dev->cdev.class = &input_class;
              class_device_initialize(&dev->cdev);
              mutex_init(&dev->mutex);
              INIT_LIST_HEAD(&dev->h_list);
              INIT_LIST_HEAD(&dev->node);

              __module_get(THIS_MODULE);
       }

       return dev;
}
这个函数很简单,主要是申请一个input_dev,再对其作些简单的初始化。
2,然后驱动再对返回的input_dev作进一步的初始化,并用__set_bit(EV_KEY, serkbd_dev->evbit);目的是设定其支持的事件。
3,  input_register_device(serkbd_dev),把设备注册进input子系统,定义如下:
int input_register_device(struct input_dev *dev)
{
       static atomic_t input_no = ATOMIC_INIT(0);
       struct input_handle *handle;
       struct input_handler *handler;
       const struct input_device_id *id;
       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);
       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;
       }
//把这个新设备加入input_dev_list链表,以后有用(比如遍历这个链表)
       list_add_tail(&dev->node, &input_dev_list);

       snprintf(dev->cdev.class_id, sizeof(dev->cdev.class_id),
               "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);
//往sys系统里添加相应的信息,在/sys/class/input/里添加一个目录项。
       error = class_device_add(&dev->cdev);
       if (error)
              return error;

       error = sysfs_create_group(&dev->cdev.kobj, &input_dev_attr_group);
       if (error)
              goto fail1;

       error = sysfs_create_group(&dev->cdev.kobj, &input_dev_id_attr_group);
       if (error)
              goto fail2;

       error = sysfs_create_group(&dev->cdev.kobj, &input_dev_caps_attr_group);
       if (error)
              goto fail3;

       path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL);
       printk(KERN_INFO "input: %s as %s\n",
              dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
       kfree(path);
//加入一个新设备的时候会遍历一个input_handler_list链表,以找到目前有哪些handler可以处理这个设备,input_match_device(handler->id_table, dev)是一个匹配函数,下面会作分析。如果匹配成功就调用handler->connect。
       list_for_each_entry(handler, &input_handler_list, node)
              if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
                     if ((id = input_match_device(handler->id_table, dev)))
                            if ((handle = handler->connect(handler, dev, id))) {
                                   input_link_handle(handle);
                                   if (handler->start)
                                          handler->start(handle);
                            }

       input_wakeup_procfs_readers();

       return 0;

fail3:      sysfs_remove_group(&dev->cdev.kobj, &input_dev_id_attr_group);
fail2:      sysfs_remove_group(&dev->cdev.kobj, &input_dev_attr_group);
fail1:      class_device_del(&dev->cdev);
       return error;
}
3.1,input_match_device定下如下:
static const struct input_device_id *input_match_device(const struct input_device_id *id,
                                                 struct input_dev *dev)
{
       int i;

       for (; 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);

              return id;
       }

       return NULL;
}
Id->flags是包括要匹配的内容,然后是MATCH_BIT匹配每一位。
#define MATCH_BIT(bit, max) \
              for (i = 0; i
              //这里必须注意一下,bit不是id或dev的成员,而是一个宏替换。
                     if ((id->bit & dev->bit) != id->bit) \
                            break; \
              if (i != NBITS(max)) \
                     continue;
匹配过程中只要有任何不一样的,就直接进入下一个id的匹配。
3.2,这里留心一下input_link_handle(handle) 。
static void input_link_handle(struct input_handle *handle)
{
       list_add_tail(&handle->d_node, &handle->dev->h_list);
       list_add_tail(&handle->h_node, &handle->handler->h_list);
}
可以看到它把&handle->d_node加到了dev->h_list尾部,这个链表就包含了所有能处理这个设备的handler。可以说input_dev和input_handler就是通过input_handle关联起来的。
4,  定时器中断调用input_event()报告事件。该函数定义如下(片断):
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
       struct input_handle *handle;

       if (type > EV_MAX || !test_bit(type, dev->evbit))
              return;

       add_input_randomness(type, code, value);

       switch (type) {

     …
       …
              case EV_KEY:

                     if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value)
                            return;

                     if (value == 2)
                            break;

                     change_bit(code, dev->key);

                     if (test_bit(EV_REP, dev->evbit) && dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] && dev->timer.data && value) {
                            dev->repeat_key = code;
                            mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
                     }

                     break;

         …
         …
       }

       if (type != EV_SYN)
              dev->sync = 0;

       if (dev->grab)
              dev->grab->handler->event(dev->grab, type, code, value);
       else
              list_for_each_entry(handle, &dev->h_list, d_node)
                     if (handle->open)
                            handle->handler->event(handle, type, code, value);
}
这个函数先对type判断一下,看是否是支持的事件。然后在switch里面根据不同的type分别作处理。
看到dev->h_list我们应该感到有眼熟了,对了,我们就遍历这个链表,找到所有能处理这个input设备的handler,如果它是打开的就调用它本身的event()。这就是我们开篇时想知道的它的去向了。由此我们就知道处理一个设备的事件可能有不止一个的handler,也就是数据可能流向很多个地方。
三,下面以系统中最简单的一个handler来分析一下它的注册及上面提到的event处理过程。
该文件在drivers\input\evbug.c
1,首先看其入口:
static struct input_handler evbug_handler = {
       .event =   evbug_event,
       .connect =      evbug_connect,
       .disconnect =  evbug_disconnect,
       .name =          "evbug",
       .id_table =      evbug_ids,
};
static int __init evbug_init(void)
{
       return input_register_handler(&evbug_handler);
}
很简单干脆,直接向input子系统注册一个静态的input_handler结构体。
2,  input_register_handler函数如下:
int input_register_handler(struct input_handler *handler)
{
       struct input_dev *dev;
       struct input_handle *handle;
       const struct input_device_id *id;

       INIT_LIST_HEAD(&handler->h_list);
//在此例中,fops==NULL,所以不占用input_table。
       if (handler->fops != NULL) {
              if (input_table[handler->minor >> 5])
                     return -EBUSY;
              //把handler保存在input_table里,由minor >> 5可知,高三位相同,低五位不同的从设备号都由同一个handler处理。所以input_table数组成员只有8个。
              input_table[handler->minor >> 5] = handler;
       }

       list_add_tail(&handler->node, &input_handler_list);

       list_for_each_entry(dev, &input_dev_list, node)
              if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
                     if ((id = input_match_device(handler->id_table, dev)))
                            if ((handle = handler->connect(handler, dev, id))) {
                                   input_link_handle(handle);
                                   if (handler->start)
                                          handler->start(handle);
                            }

       input_wakeup_procfs_readers();
       return 0;
}
流程跟input_register_device差不多,把handler加入链表input_handler_list——>遍历input_dev_list——> input_match_device()找到匹配设备——>connect()——> input_link_handle()。
3,在此例中,要匹配的id定义为:
static const struct input_device_id evbug_ids[] = {
       { .driver_info = 1 },      /* Matches all devices */
       { },                /* Terminating zero entry */
};
如英文注解,可以匹配所有的input设备。故每匹配完一个设备就调用一次它本身的connect():
static struct input_handle *evbug_connect(struct input_handler *handler, struct input_dev *dev,
                                     const struct input_device_id *id)
{
       struct input_handle *handle;

       if (!(handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL)))
              return NULL;

       handle->dev = dev;
       handle->handler = handler;
       handle->name = evbug_name;

       input_open_device(handle);

       printk(KERN_DEBUG "evbug.c: Connected device: \"%s\", %s\n", dev->name, dev->phys);

       return handle;
}
这个函数很简单,它先申请一个input_handle结构体,然后把dev和handler赋给它,可以想像一下,handle有两只手,一只手牵着dev,另一只手牵着handler,这样无论是dev还是handler都可以通过handle找到对方。
4,input_open_device()函数定义如下:
int input_open_device(struct input_handle *handle)
{
       struct input_dev *dev = handle->dev;
       int err;

       err = mutex_lock_interruptible(&dev->mutex);
       if (err)
              return err;

       handle->open++;

       if (!dev->users++ && dev->open)
              err = dev->open(dev);
       if (err)
              handle->open--;
       mutex_unlock(&dev->mutex);

       return err;
}
函数很简单,增加open和users计数,如果第一次打开,就调用dev->open,记得我们的测试驱动中的open函数下定义是这样的:
static int ser_kpp_open(struct input_dev *dev)
{
       return 0;
}
什么都没做,直接返回0.
5,  最后我们看一下evbug.c里面的event函数。
static void evbug_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
{
       printk(KERN_DEBUG "evbug.c: Event. Dev: %s, Type: %d, Code: %d, Value: %d\n",
              handle->dev->phys, type, code, value);
}
就是input_event()报告事件的时候最终把数据传到这里来。这里只是简单地把这些信息输出来。
四,小结:input_dev在注册的时候会挂在input_dev_list链表上,然后通过遍历input_handler_list尝试匹配已有的每个handler,并把成功匹配的handle挂在dev->h_list链表上,当驱动input_event()报告事件时,就通过dev->h_list链表找到每一个已匹配的handler,并把事件传给它们处理。
       而与此对应handler注册时则会把自己挂在input_handler_list上,然后通过遍历input_dev_list链表,看看有没有匹配的input_dev, 有则把自己挂在匹配的dev->h_list上。当有事件到来时,input_handler结构体里面的event函数就可以对事件作处理了。
抓住了这样一个主线,整个input子系统的大概来龙去脉就基本清楚了。

下面顺便贴一下测试结果:
root@361-21com:/mnt/sd# insmod ser_keyb.ko
input: serkpd as /class/input/input0
ser-keypad init
root@361-21com:/mnt/sd# evbug.c: Event. Dev: , Type: 1, Code: 30, Value: 1
evbug.c: Event. Dev: , Type: 1, Code: 30, Value: 0
evbug.c: Event. Dev: , Type: 1, Code: 29, Value: 1
evbug.c: Event. Dev: , Type: 1, Code: 29, Value: 0
evbug.c: Event. Dev: , Type: 1, Code: 28, Value: 1
evbug.c: Event. Dev: , Type: 1, Code: 28, Value: 0
evbug.c: Event. Dev: , Type: 1, Code: 27, Value: 1
evbug.c: Event. Dev: , Type: 1, Code: 27, Value: 0
evbug.c: Event. Dev: , Type: 1, Code: 158, Value: 1
evbug.c: Event. Dev: , Type: 1, Code: 158, Value: 0
evbug.c: Event. Dev: , Type: 1, Code: 157, Value: 1
evbug.c: Event. Dev: , Type: 1, Code: 157, Value: 0
evbug.c: Event. Dev: , Type: 1, Code: 156, Value: 1
evbug.c: Event. Dev: , Type: 1, Code: 156, Value: 0
evbug.c: Event. Dev: , Type: 1, Code: 155, Value: 1
evbug.c: Event. Dev: , Type: 1, Code: 155, Value: 0
------------------------------------------
本文乃原创!
转载请注明出处:http://sparklecliz.cublog.cn/
------------------------------------------


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/107046/showart_2121132.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP