Linux输入(input)子系统

###############################################################################################
早前曾研究了一下输入子系统的原理,给人的感觉是输入子系统很复杂.但其实内核开发者在这方面已经做得很完善了,
输入子系统虽然错综复杂,但是只要我们领会了输入子系统的一些设计思想后,我们要使用它并非难事.

以下以内核自带的gpio_keys驱动为例,介绍输入子系统的使用.
主要的原因是gpio_keys驱动比较简单易懂,另外不是没个人都有触摸屏,但键盘的话相信每一块开发板上都配有吧^_^

按照以前的习惯,先从下到上的研究底层驱动是如何提交输入事件的:
###############################################################################################

drivers/input/keyboard/gpio_keys.c:

static int __devinit  gpio_keys_probe(struct platform_device*pdev)
{
      structgpio_keys_platform_data *pdata =pdev->dev.platform_data;
      structinput_dev *input;
      int i,error;

      input =input_allocate_device(); //申请input_dev结构
      if(!input)
            return-ENOMEM;

      platform_set_drvdata(pdev, input); //把input_dev结构放好(以后方便调用)

      input->evbit[0] = BIT(EV_KEY); //目前event的类型不操作32,所以你会看到对于evbit数组的操作都是对evbit[0]中的位来进行操作.

      input->name = pdev->name;
      input->phys = "gpio-keys/input0";
      input->dev.parent =&pdev->dev;

      input->id.bustype = BUS_HOST;
      input->id.vendor = 0x0001;
      input->id.product = 0x0001;
      input->id.version = 0x0100;

      for (i = 0;i < pdata->nbuttons; i++) {
            structgpio_keys_button *button =&pdata->buttons[i];
            int irq =gpio_to_irq(button->gpio);
            unsigned inttype = button->type ?: EV_KEY;

            set_irq_type(irq, IRQ_TYPE_EDGE_BOTH);

             
            error =request_irq(irq, gpio_keys_isr, IRQF_SAMPLE_RANDOM,
                                button->desc ? button->desc :"gpio_keys",
                                pdev);
            if (error){
                  printk(KERN_ERR "gpio-keys: unable to claim irq %d; error%d\n",
                        irq,error);
                  gotofail;
            }

            input_set_capability(input, type,button->code);
      }

      error =input_register_device(input); //注册输入设备,并和对应的handler处理函数挂钩
      if (error){
            printk(KERN_ERR "Unable to register gpio-keys inputdevice\n");
            gotofail;
      }

      return0;

  fail:
      for (i = i -1; i >= 0; i--)
            free_irq(gpio_to_irq(pdata->buttons[i].gpio),pdev);

      input_free_device(input);

      returnerror;
}


提到input_dev结构,以下谈一下我对于它的理解:
struct  input_dev  {

      void*private;

      const char*name;
      const char*phys;
      const char*uniq;
      structinput_id id;

     

      unsignedlong evbit[NBITS(EV_MAX)];
      unsignedlong keybit[NBITS(KEY_MAX)];
      unsignedlong relbit[NBITS(REL_MAX)];
      unsignedlong absbit[NBITS(ABS_MAX)];
      unsignedlong mscbit[NBITS(MSC_MAX)];
      unsignedlong ledbit[NBITS(LED_MAX)];
      unsignedlong sndbit[NBITS(SND_MAX)];
      unsignedlong ffbit[NBITS(FF_MAX)];
      unsignedlong swbit[NBITS(SW_MAX)];

      .........................................
};




void  input_set_capability(struct input_dev *dev,unsigned int type, unsigned int code)
{
      switch(type) {
      caseEV_KEY:
            __set_bit(code, dev->keybit); //比如按键,应该对哪些键值的按键进行处理(对于其它按键不予理睬)
            break;

      caseEV_REL:
            __set_bit(code, dev->relbit);
            break;

      caseEV_ABS:
            __set_bit(code, dev->absbit);
            break;

      caseEV_MSC:
            __set_bit(code, dev->mscbit);
            break;

      caseEV_SW:
            __set_bit(code, dev->swbit);
            break;

      caseEV_LED:
            __set_bit(code, dev->ledbit);
            break;

      caseEV_SND:
            __set_bit(code, dev->sndbit);
            break;

      caseEV_FF:
            __set_bit(code, dev->ffbit);
            break;

      default:
            printk(KERN_ERR
                  "input_set_capability: unknown type %u (code %u)\n",
                  type,code);
            dump_stack();
            return;
      }

      __set_bit(type, dev->evbit); //感觉和前面重复了(前面一经配置过一次了)
}
EXPORT_SYMBOL(input_set_capability);


static irqreturn_t  gpio_keys_isr(int irq, void *dev_id)
{
              int i;
              struct platform_device *pdev = dev_id;
              struct gpio_keys_platform_data *pdata =pdev->dev.platform_data;
              struct input_dev *input = platform_get_drvdata(pdev);

              for (i = 0; i < pdata->nbuttons; i++){
                              struct gpio_keys_button *button =&pdata->buttons[i];
                              int gpio = button->gpio;

                              if (irq == gpio_to_irq(gpio)) { //判断哪个键被按了?
                                              unsigned int type = button->type ?: EV_KEY;
                                              int state = (gpio_get_value(gpio) ? 1 : 0) ^button->active_low; //记录按键状态

                                              input_event(input, type, button->code,!!state); //汇报输入事件
                                              input_sync(input); //等待输入事件处理完成
                              }
              }

              return IRQ_HANDLED;
}



void  input_event(struct input_dev *dev, unsigned inttype, unsigned int code, int value)
{
      structinput_handle *handle;

      if (type> EV_MAX || !test_bit(type,dev->evbit)) //首先判断该事件类型是否有效且为该设备所接受
            return;

      add_input_randomness(type, code, value);

      switch(type) {

            caseEV_SYN:
                  switch(code) {
                        caseSYN_CONFIG:
                              if(dev->event)
                                    dev->event(dev, type, code, value);
                              break;

                        caseSYN_REPORT:
                              if(dev->sync)
                                    return;
                              dev->sync = 1;
                              break;
                  }
                  break;

            caseEV_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);
}
EXPORT_SYMBOL(input_event);


#########################################################################
好了,下面再来研究一下event层对于input层报告的这个键盘输入事件是如何来处理的.
#########################################################################

drivers/input/evdev.c:

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,
};

static void  evdev_event(struct input_handle *handle,unsigned int type, unsigned int code, int value)
{
              struct evdev *evdev = handle->private;
              struct evdev_client *client;

              if (evdev->grab) {
                              client = evdev->grab;

                              do_gettimeofday(&client->buffer[client->head].time);
                              client->buffer[client->head].type =type;
                              client->buffer[client->head].code =code;
                              client->buffer[client->head].value =value;
                              client->head = (client->head + 1)& (EVDEV_BUFFER_SIZE - 1);

                              kill_fasync(&client->fasync, SIGIO,POLL_IN);
              } else
                             
               list_for_each_entry(client,&evdev->client_list, node) {
                      
                       do_gettimeofday(&client->buffer[client->head].time);
                       client->buffer[client->head].type =type;
                       client->buffer[client->head].code =code;
                       client->buffer[client->head].value =value;
                           
                       client->head = (client->head + 1)& (EVDEV_BUFFER_SIZE - 1);

                       kill_fasync(&client->fasync, SIGIO,POLL_IN);//通知调用input_sync的进程:输入事件经已处理完毕(通知底层).
               }

       wake_up_interruptible(&evdev->wait);//唤醒睡眠在evdev->wait等待队列等待输入信息的进程(通知上层).
}

###################################################################################
好了,至此一个按键的输入事件处理完毕,现在再来从上到上的来看看用户是如何获取这个输入事件的.
###################################################################################


static const struct file_operations evdev_fops = {
       .owner=       THIS_MODULE,
       .read=        evdev_read,
       .write=       evdev_write,
       .poll=        evdev_poll,
       .open=        evdev_open,
       .release=     evdev_release,
       .unlocked_ioctl = evdev_ioctl,
#ifdef CONFIG_COMPAT
       .compat_ioctl = evdev_ioctl_compat,
#endif
       .fasync=      evdev_fasync,
       .flush=       evdev_flush
};


static int evdev_open(struct inode *inode, struct file*file)
{
       struct evdev_client *client;
       struct evdev *evdev;
       int i = iminor(inode) - EVDEV_MINOR_BASE;
       int error;

       if (i >= EVDEV_MINORS)
               return -ENODEV;

       evdev = evdev_table[i];

       if (!evdev || !evdev->exist)
               return -ENODEV;

       client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);
       if (!client)
               return -ENOMEM;

       client->evdev = evdev;
         
       list_add_tail(&client->node,&evdev->client_list);

       if (!evdev->open++&& evdev->exist){
               error =input_open_device(&evdev->handle);
               if (error) {
                       list_del(&client->node);
                       kfree(client);
                       return error;
               }
       }

       file->private_data = client;//存放好evdev_client结构方便以后使用
       return 0;
}


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;
       int retval;

       if (count < evdev_event_size())//对于每次读取的数据大小是有一定的要求.
               return -EINVAL;

       if (client->head == client->tail&& evdev->exist&& (file->f_flags& O_NONBLOCK))//缓存中没有数据可读且设备是存在的,
                                          如果设置为NONBLOCK方式来读,立即返回.
               return -EAGAIN;

       retval = wait_event_interruptible(evdev->wait,
               client->head != client->tail ||!evdev->exist);//否则等待缓存有数据可读或设备不存在(被移去)
       if (retval)
               return retval;

       if (!evdev->exist)
               return -ENODEV;

       while (client->head != client->tail&& retval + evdev_event_size()<= count) {//下面开始读取数据

               struct input_event *event = (struct input_event *)client->buffer +client->tail;//获取缓存中的读指针

               if (evdev_event_to_user(buffer + retval, event))//返回数据给用户
                       return -EFAULT;

               client->tail = (client->tail + 1)& (EVDEV_BUFFER_SIZE - 1);//更新读指针
               retval += evdev_event_size();
       }

       return retval;
}

呵呵,看到了吧,应用程序就是这样获取输入事件的^_^

##########################################################################################################
本来对于gpio_keys这样的驱动程序,只要当发生按键事件的时候向上层应用程序汇报键值即可.
不过,对于一些带输出设备(例如led灯)的输入设备来说(例如键盘),上层应用程序同样可以利用event层来读取或改变其状态.
请看以下代码:
##########################################################################################################

static ssize_t evdev_write(struct file *file, const 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 = 0;

       if (!evdev->exist)
               return -ENODEV;

       while (retval < count) {

               if (evdev_event_from_user(buffer + retval,&event))//从用户处获取事件结构
                       return -EFAULT;
               input_inject_event(&evdev->handle,event.type, event.code, event.value);//往底层发送事件
               retval += evdev_event_size();
       }

       return retval;
}



void input_inject_event(struct input_handle *handle,unsigned int type, unsigned int code, int value)
{
       if (!handle->dev->grab ||handle->dev->grab == handle)
               input_event(handle->dev, type, code, value);
}
EXPORT_SYMBOL(input_inject_event);


void input_event(struct input_dev *dev, unsigned inttype, unsigned int code, int value)
{
    structinput_handle *handle;

    if (type> EV_MAX || !test_bit(type,dev->evbit))//首先判断该事件类型是否有效且为该设备所接受
      return;

   add_input_randomness(type, code, value);

    switch(type) {

       caseEV_SYN:
          switch(code) {
             caseSYN_CONFIG:
                if(dev->event)
                  dev->event(dev, type, code, value);
                break;

             caseSYN_REPORT:
                if(dev->sync)
                  return;
               dev->sync = 1;
                break;
          }
          break;

.............................................................
       caseEV_LED:

          if (code> LED_MAX || !test_bit(code,dev->ledbit) || !!test_bit(code,dev->led) == value)
            return;

         change_bit(code, dev->led);

          if(dev->event)
            dev->event(dev, type, code, value);

          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);
}
EXPORT_SYMBOL(input_event);

注:
   鉴于简单的gpio_keys驱动中没有注册自己的event接口,当然也没有对于LED灯的处理,而event层只是简单的向上层汇报输入事件(event层也不可能帮你处理你的led设备,对吧),所以这个通过输入子系统控制LED的部分暂时不去研究.
   (输出设备LED灯不属于这个输入设备gpio_key的一部分.当然,如果你想通过这个gpio_keys设备来控制led灯的话,可以修改这个gpio_keys驱动,详细可参考driver/input/keyboard目录下的驱动)



 

 原文地址 http://www.cnitblog.com/luofuchong/archive/2007/11/12/36157.html
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值