输入服务子系统框架代码分析(韦东山的视频总结及针对linux-2.6.30.4)

来源:互联网 发布:淘宝宝贝摄影技巧 编辑:程序博客网 时间:2024/04/29 21:57

自己的总结有错误请评论,我们共同进步。


static struct input_handler *input_table[8];

/*入口函数*/

static int __init input_init(void){

    /*注册

     *#define  INPUT_MAJOR  13

     *主设备号为13

     */

     err = register_chrdev(INPUT_MAJOR, "input", &input_fops); 

}                                                          


static const struct file_operations input_fops = {
       .owner = THIS_MODULE,
       .open = input_open_file,

};

注册的file_operation怎么没有读写等的定义呢?

当读写等操作时,是怎么读写的呢?

下面来分析:

设备的主设备号都是13,就要靠次设备号来访问设备了,当打开设备文件时先访问的是input_open_file函数,

static int input_open_file(struct inode *inode, struct file *file)
{
     struct input_handler *handler;
     const struct file_operations *old_fops, *new_fops = NULL;
     int err;

     /*handler等于以次设备号右移5位为下标input_table[]的值 */             

      handler = input_table[iminor(inode) >> 5];    

      if (!handler || !(new_fops = fops_get(handler->fops))) 

                    /*new_fops等于了 handler->fops当调用读写时就调用handler->fops的读写*/

             {     

                               err = -ENODEV;                            

         goto out;                                          
       }

     old_fops = file->f_op;
     file->f_op = new_fops;

    /*file->f_op等于new_fops,

     *而new_fops等于handler- >fops结构,

     *因此file_f_op 就等于handler->fops结构,

     *读写就调用handler->fops的读 *写函数

     */
}


接下来又有问题那就是input_table中的数组项又有谁来定义,用source insight搜索可得到。

int input_register_handler(struct input_handler *handler)
{
     struct input_dev *dev;
     INIT_LIST_HEAD(&handler->h_list);
     if (handler->fops != NULL) {
          if (input_table[handler->minor >> 5]) {
              retval = -EBUSY;
              goto out;
          }
          input_table[handler->minor >> 5] = handler;
     }
      list_add_tail(&handler->node, &input_handler_list);
      list_for_each_entry(dev, &input_dev_list, node);
      input_attach_handler(dev, handler); 
}


问题又来了,这个只是函数定义,并未真正的调用呀,还是不知道input_table的数组项的值,要想知道input_table的数组项的值,就必须看谁调用了input_register_handler,用source insight继续搜索可得到:

apm-power.c :return input_register_handler(&apmpower_handler);
evbug.c : return input_register_handler(&evbug_handler);
evdev.c : return input_register_handler(&evdev_handler);
joydev.c : return input_register_handler(&joydev_handler);
keyboard.c : error = input_register_handler(&kbd_handler);
mousedev.c : error = input_register_handler(&mousedev_handler);
rfkill-input.c: return input_register_handler(&&rfkill_handler);

这些设置了input_table数组项,但是它们设置了input_table哪一项就要看&*_hadler了


static struct input_handler evdev_handler = {
     .event = evdev_event,
     .connect = evdev_connect,
     .disconnect= evdev_disconnect,
     .fops = &evdev_fops,
     .minor = EVDEV_MINOR_BASE,
     .name = "evdev",
     .id_table= evdev_ids,
};

evdev_handler.fops != NULL;

#define  EVDEV_MINOR_BASE64

input_table[(&evdev_handler)->minor >> 5] = input_table[2] = &evdev_handler;


static struct input_handler mousedev_handler = {
    .event =mousedev_event,
    .connect =mousedev_connect,
    .disconnect =mousedev_disconnect,
    .fops =&mousedev_fops,
    .minor =MOUSEDEV_MINOR_BASE,
    .name ="mousedev",
    .id_table =mousedev_ids,
};

mousedev_handler !=NULL;

#define MOUSEDEV_MINOR_BASE32

input_table[(&mousedev_handler)->minor >> 5] = input_table[1] = &mousedev_handler;


static struct input_handler joydev_handler = {
    .event= joydev_event,
    .connect= joydev_connect,
    .disconnect= joydev_disconnect,
    .fops= &joydev_fops,
    .minor= JOYDEV_MINOR_BASE,
    .name= "joydev",
    .id_table= joydev_ids,
    .blacklist= joydev_blacklist,
};

joydev_handler.fops != NULL;

#define JOYDEV_MINOR_BASE0

input_table[(&joydev_handler)->minor >> 5] = input_table[0] = &joydev_handler;



static struct input_handler apmpower_handler = {
    .event =apmpower_event,
    .connect =apmpower_connect,
    .disconnect =apmpower_disconnect,
    .name ="apm-power",
    .id_table =apmpower_ids,
};

apmpower_handler.fops=NULL;因此不在input_table中定义

static struct input_handler evbug_handler = {
    .event =evbug_event,
    .connect =evbug_connect,
    .disconnect =evbug_disconnect,
    .name ="evbug",
    .id_table =evbug_ids,
};

evbug_handler与apmpower_handler一样,



static struct input_handler kbd_handler = {
   .event= kbd_event,
   .connect= kbd_connect,
   .disconnect= kbd_disconnect,
   .start= kbd_start,
   .name= "kbd",
   .id_table= kbd_ids,
};

 与apmpower_handler一样;


static struct input_handler rfkill_handler = {
   .event =rfkill_event,
   .connect =rfkill_connect,
   .disconnect =rfkill_disconnect,
   .start =rfkill_start,
   .name ="rfkill",
   .id_table =rfkill_ids,
};

与apmpower_handler一样;


又有问题了,那输入服务系统怎么知道按键设备文件?怎么就能读了呢?

那就是input_register_device函数注册。

int input_register_device(struct input_dev *dev)
{
      list_add_tail(&dev->node, &input_dev_list); /*放入链表里去*/

/*********怎么放入链表的,放入后的结果是怎样的**************/

static LIST_HEAD(input_dev_list);

list.h中定义了
struct list_head {
struct list_head *next, *prev;
};
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
#define LIST_HEAD_INIT(name) { &(name), &(name) }
初始化时使得next,prev指向自身

static inline void list_add_tail(&dev->node, &input_dev_list)
{
__list_add(&dev->node,(&input_dev_list)->prev,&input_dev_list);
}

/*
 
* Insert a new entry between two known consecutive entries.
 *
 * This is only for internal list manipulation where we know
 * the prev/next entries already!
 */

void __list_add(struct list_head *new,struct list_head *prev,struct list_head *next)
{
(&dev->node)->prev =&dev->node;
(&dev->node)->next =&input_dev_list;
(&dev->node)->prev = prev;
prev->next =&dev->node;
}
EXPORT_SYMBOL(__list_add);

结果就是:input_dev_list之前。

/*********************************************************/


   /*对于每一个input_handler都调用input_attach_handler*/      

      /*

据input_handler的id_table判断能否支

 

input_dev*/

       list_for_each_entry(handler, &input_handler_list, node)

            input_attach_handler(dev, handler);  

 }     

/***********怎么判断是否支持,判断过还要做什么***********/

/*注册input_dev或input_handler时,会两两比较左边的input_dev和右边的input_hadler
 *根据input_handler的id_table判断这个input_handler能否支持这个input_dev,
 *如果能支持,就调用input_handler的connection函数与建立连接。
 */

 input_attach_handler(struct input_dev *dev,struct input_handler *handler)
 {
        const struct input_device_id *id;

        int error;
        id = input_match_device(handler->id_table, dev);

if(!id)

 return -ENODEV;

         error = handler->connect(handler, dev, id);

         /*匹配了,就调用*input_handler结构     

          *中的connection进行建立联系

          */
}

/******************************************************/



handler->connect中做了什么就要看真正的input_handler中的connection。

上面有分析到只有

evdev_handler,

mousedev_handler,

joydev_handler在input_table[],

所以我们可以分析这三个中的一个。

下面以evdev_handler进行分析:

static struct input_handler evdev_handler = {
     .event= evdev_event,
     .connect= evdev_connect,
     .disconnect= evdev_disconnect,
     .fops= &evdev_fops,
     .minor= EVDEV_MINOR_BASE,
     .name= "evdev",
     .id_table= evdev_ids,
};

/*
 * Create new evdev device. Note that input core serializes calls
 * to connect and disconnect so we don't need to lock evdev_table here.
 */
 evdev_connect(struct input_handler *handler, struct input_dev *dev,const struct input_device_id *id)
{
         struct evdev *evdev;
         int minor;
         int error;
         evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);

         /*分配一个struct evdev,此结构中有 一个input_handle结构*/

         INIT_LIST_HEAD(&evdev->client_list);

         /*设置*/

        /*evdev->handle.dev 指向input_device*/
        evdev->handle.dev = input_get_device(dev); 

        /*evdev->handle.handler指向input_handler*/
        evdev->handle.handler = handler;
        error = input_register_handle(&evdev->handle);/*注册*/
        error = evdev_install_chrdev(evdev);

}


/***注册evdev->handle又做了什么呢***/

int input_register_handle(struct input_handle *handle)
{
         struct input_handler *handler = handle->handler;
         struct input_dev *dev = handle->dev;
         int error;
         list_add_tail_rcu(&handle->d_node, &dev->h_list); 

         /*结果为&handle->d_node的next指向了dev->h_list 

          *dev->h_list的prev指针指向了handle>d_node,

         *以后就可以通过dev->h_list找的handle  */

           list_add_tail(&handle->h_node, &handler->h_list); 

           /*结果为&handle->h_node的next指向了

            *handler->h_list , handler->h_list的prev指针指向了

            *handle>d_node,以后就可以通过dev->h_list找的handle,

            *并且可以通过handle找到dev.         */

}

 

总结过程:

1.打开设备文件;

  a.调用static const struct file_operations input_fops 中的open函数 即input_open_file

 b.发生转换,file->f_op = input_table[iminor(inode) >> 5] 调用input_table[iminor(inode) >> 5]的open函数,(次设备号64-95,evdev.c,handler=&evdev_handler);

 c.调用evdev_handler中的fops的open函数打开设备文件

2.读设备文件:

 调用(次设备号64-95,evdev.c,handler=&evdev_handler)evdev_handler中的fops的read函数读取设备文件。


当看evdev_handler中的fops的read函数时,发现问题了,没有按键时进入休眠,当有按键按下时调用中断处理函数进行唤醒,那输入子服务系统睡眠了有谁来唤醒呢。

static ssize_t evdev_read(struct file *file, char __user *buffer,size_t count, loff_t *ppos)
{

        struct evdev_client *client = file->private_data;
        struct evdev *evdev = client->evdev;
        struct input_event event;
        int retval;

       /*非阻塞时*/
       if (client->head == client->tail && evdev->exist 

          &&(file->f_flags & O_NONBLOCK))

            return -EAGAIN;

      /*阻塞时*/
      retval = wait_event_interruptible(evdev->wait,

      client->head != client->tail ||!evdev->exist);
      if (retval)
        return retval;

}

source insight搜索evdev->wait

进入得到

/*
 * Pass incoming event to all connected clients.
 */
static void evdev_event(struct input_handle *handle,
            unsigned int type, unsigned int code, int value)
{
      wake_up_interruptible(&evdev->wait);
}

static struct input_handler evdev_handler = {
     .event= evdev_event,
     .connect= evdev_connect,
     .disconnect= evdev_disconnect,
     .fops= &evdev_fops,
     .minor= EVDEV_MINOR_BASE,
     .name= "evdev",
     .id_table= evdev_ids,
};

不是中断处理函数调用的,而是在evdev_event被唤醒的,evdev_event是evdev_handler的event,那么中断处理函数就应该调用evdev_handler的event进行操作,接下来就看看

随便中一个驱动程序:

我们以\linux-2.6.30.4\drivers\input\keyboard\corgikbd.c为例。

中断处理函数:

static irqreturn_t corgikbd_interrupt(int irq, void *dev_id)
{
        struct corgikbd *corgikbd_data = dev_id;

        if (!timer_pending(&corgikbd_data->timer)) {
                /** wait chattering delay **/
                udelay(20);
                corgikbd_scankeyboard(corgikbd_data);
        }
        return IRQ_HANDLED;
}

只调用了 corgikbd_scankeyboard函数,我们进去看看

static void corgikbd_scankeyboard(struct corgikbd *corgikbd_data)
{
        unsigned int row, col, rowd;
        unsigned long flags;
        unsigned int num_pressed;

         num_pressed = 0;
        for (col = 0; col < KB_COLS; col++) {

              corgikbd_discharge_all();
              udelay(KB_DISCHARGE_DELAY);

              corgikbd_activate_col(col);

              udelay(KB_ACTIVATE_DELAY);


              rowd = GET_ROWS_STATUS(col);
              for (row = 0; row < KB_ROWS; row++) {
                 unsigned int scancode, pressed;
   
                 scancode = SCANCODE(row, col);
                 pressed = rowd & KB_ROWMASK(row);
                   input_report_key(corgikbd_data->input, 

                         corgikbd_data->keycode[scancode], pressed);


               }

               

        corgikbd_reset_col(col);
        }

        corgikbd_activate_all();

       input_sync(corgikbd_data->input);

}

去去判断有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);
                add_input_randomness(type, code, value);

                /*这个函数跟input_event函数很相像*/

                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_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;  

       }

       if (disposition &INPUT_PASS_TO_HANDLERS)
               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

                /*遍历所有的dev->h_list,如果已经调用handle->open

                 *调用 handle->handler->event进行处理,

                 *如果我们的按键跟evdev_handler

                 *匹配成功就调用evdev_handler的event,

                 *即我们前面所说的evdev_event*/
                list_for_each_entry_rcu(handle, &dev->h_list, d_node)
                     if (handle->open)
                           handle->handler->event(handle,type, code, value);
            rcu_read_unlock();
}

总结一下发生中断时,怎么处理的:

corgikbd_interrupt(int irq, void *dev_id)   ->  

corgikbd_scankeyboard(struct corgikbd *corgikbd_data) ->

/*上面两个是corgikbd.c独有的函数所以在其他驱动程序中,

 *在中断处理函数中直接调用input.h相应函数上报事件,也可直接调用

 *input_event函数上报事件*/

input_report_key(corgikbd_data->input, corgikbd_data->keycode[scancode], pressed);

  ->      void input_event(*dev,type,code,value) ;  ->

input_handle_event(dev, type, code, value) ;  ->

     /*对于可以交给设备处理的事件,并且定义了dev->event,就调用dev->event进行处理,

      *其他都是调用input_pass_event进行处理*/

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

     调用handle->handler->event(handle,type, code, value)处理;


上面就是输入子服务系统的内容;

 









原创粉丝点击