Linux驱动子系统之输入子系统

来源:互联网 发布:手机短信群发软件免费 编辑:程序博客网 时间:2024/05/17 23:06

[概述]

Gpio-keys是基于input架构实现的一个通用GPIO按键驱动。该驱动基于platform_driver架构,实现了驱动和设备分离,符合Linux设备驱动模型的思想。工程中的按键驱动我们一般都会基于gpio-keys来写,所以我们有必要对gpio_keys进行分析。 

[gpio-keys驱动分析]

Gpio-keys的代码在drivers/input/keyboard/gpio_keys.c中,具体分析一下probe函数,probe函数会在platformdriver和platform device匹配上后被调用。

  1. static int __devinit gpio_keys_probe(structplatform_device *pdev)  
  2. {  
  3.          structgpio_keys_platform_data *pdata = pdev->dev.platform_data;  
  4.          structgpio_keys_drvdata *ddata;  
  5.          structinput_dev *input;  
  6.          inti, error;  
  7.          intwakeup = 0;  
  8.    
  9.          /*kzalloc 对kmalloc的封装,会清0分配的空间*/  
  10.          ddata= kzalloc(sizeof(struct gpio_keys_drvdata) +  
  11.                             pdata->nbuttons* sizeof(struct gpio_button_data),  
  12.                             GFP_KERNEL);  
  13.    
  14.          /*分配一个input设备*/  
  15.          input= input_allocate_device();  
  16.          if(!ddata || !input) {  
  17.                    error= -ENOMEM;  
  18.                    gotofail1;  
  19.          }  
  20.    
  21.          platform_set_drvdata(pdev,ddata);  
  22.           
  23. /* 设置input设备属性 */  
  24.          input->name= pdev->name;  
  25.          input->phys= "gpio-keys/input0";  
  26.          input->dev.parent= &pdev->dev;  
  27.    
  28.          input->id.bustype= BUS_HOST;  
  29.          input->id.vendor= 0x0001;  
  30.          input->id.product= 0x0001;  
  31.          input->id.version= 0x0100;  
  32.    
  33.          /*Enable auto repeat feature of Linux input subsystem */  
  34.          if(pdata->rep)  
  35.                    __set_bit(EV_REP,input->evbit);  
  36.    
  37.          ddata->input= input;  
  38.    
  39.          for(i = 0; i < pdata->nbuttons; i++) {  
  40.                    structgpio_keys_button *button = &pdata->buttons[i];  
  41.                    structgpio_button_data *bdata = &ddata->data[i];  
  42.                    intirq;  
  43.                    unsignedint type = button->type ?: EV_KEY;  
  44.    
  45.                    bdata->input= input;  
  46.                    bdata->button= button;  
  47.                    setup_timer(&bdata->timer,  
  48.                                 gpio_keys_timer, (unsigned long)bdata);  
  49.                    /*初始化工作队列 */  
  50.                    INIT_WORK(&bdata->work,gpio_keys_report_event);  
  51.    
  52.                    /*申请GPIO口*/  
  53. error = gpio_request(button->gpio, button->desc ?:"gpio_keys");  
  54.                    if(error < 0) {  
  55.                             pr_err("gpio-keys:failed to request GPIO %d,"  
  56.                                      "error %d\n", button->gpio, error);  
  57.                             gotofail2;  
  58.                    }  
  59.                 /* 把GPIO设为输入 */  
  60.                    error= gpio_direction_input(button->gpio);  
  61.                    if(error < 0) {  
  62.                             pr_err("gpio-keys:failed to configure input"  
  63.                                      "direction for GPIO %d, error %d\n",  
  64.                                      button->gpio,error);  
  65.                             gpio_free(button->gpio);  
  66.                             gotofail2;  
  67.                    }  
  68.                    /*获取GPIO对应的中断*/  
  69.                    irq= gpio_to_irq(button->gpio);  
  70.                    if(irq < 0) {  
  71.                             error= irq;  
  72.                             pr_err("gpio-keys:Unable to get irq number"  
  73.                                      "for GPIO %d, error %d\n",  
  74.                                      button->gpio,error);  
  75.                             gpio_free(button->gpio);  
  76.                             gotofail2;  
  77.                    }  
  78.                    /*注册中断 */  
  79.                    error= request_irq(irq, gpio_keys_isr,  
  80.                                          IRQF_SHARED |  
  81.                                          IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,  
  82.                                          button->desc ? button->desc :"gpio_keys",  
  83.                                          bdata);  
  84.                    if(error) {  
  85.                             pr_err("gpio-keys:Unable to claim irq %d; error %d\n",  
  86.                                      irq,error);  
  87.                             gpio_free(button->gpio);  
  88.                             gotofail2;  
  89.                    }  
  90.    
  91.                    if(button->wakeup)  
  92.                             wakeup= 1;  
  93.    
  94.                    /*设置设备对事件的支持,比如设置对键1和键2的支持*/  
  95.                    input_set_capability(input,type, button->code);  
  96.          }  
  97.           
  98.          /*注册input设备*/  
  99.          error= input_register_device(input);  
  100.          if(error) {  
  101.                    pr_err("gpio-keys:Unable to register input device, "  
  102.                             "error:%d\n", error);  
  103.                    gotofail2;  
  104.          }  
  105.    
  106.          device_init_wakeup(&pdev->dev,wakeup);  
  107.    
  108.          return0;  
  109.          ……  
  110.          returnerror;  
  111. }  

[中断服务例程]

  1. static irqreturn_t gpio_keys_isr(int irq,void *dev_id)  
  2. {  
  3.          structgpio_button_data *bdata = dev_id;  
  4.          structgpio_keys_button *button = bdata->button;  
  5.    
  6.          BUG_ON(irq!= gpio_to_irq(button->gpio));  
  7.    
  8.          /*检测是否在platform device设置了去抖动www.linuxidc.com*/  
  9. if(button->debounce_interval)  
  10.                    mod_timer(&bdata->timer,  /* 延迟msecs_to_jiffies(button->debounce_interval)个jiffies后执行schedule_work()*/  
  11.                             jiffies+ msecs_to_jiffies(button->debounce_interval));  
  12.          else  
  13.                    schedule_work(&bdata->work);  
  14.    
  15.          returnIRQ_HANDLED;  
  16. }  

这里的中断被分成了上半部和下半部,上半部(中断处理例程)对中断进行了快速的相应,马上调度work上报事件信息:

  1. static void gpio_keys_report_event(structwork_struct *work)  
  2. {  
  3.          structgpio_button_data *bdata =  
  4.                    container_of(work,struct gpio_button_data, work);  
  5.          structgpio_keys_button *button = bdata->button;  
  6.          structinput_dev *input = bdata->input;  
  7.          unsignedint type = button->type ?: EV_KEY;  
  8.          intstate = (gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low;  
  9.    
  10.          input_event(input,type, button->code, !!state);  
  11.          input_sync(input);  
  12. }  

[两个结构体]

  1. struct gpio_keys_button {  
  2.          /*Configuration parameters */  
  3.          intcode;            /* 输入事件代码(KEY_*, SW_*) */  
  4.          intgpio;             /* gpio口 */  
  5.          intactive_low; /* 低电平有效*/  
  6.          char*desc;      /* 功能描述 */  
  7.          inttype;            /* 输入事件类型(EV_KEY, EV_SW) */  
  8.          intwakeup;               /* configure thebutton as a wake-up source */  
  9.          intdebounce_interval;    /* 去抖动间隔,单位微秒*/  
  10. };  
  11.    
  12. struct gpio_keys_platform_data {  
  13.          structgpio_keys_button *buttons;  
  14.          intnbuttons;  
  15.          unsignedint rep:1;           /* enable inputsubsystem auto repeat */  
  16. };  

到此基本上分析完了 

[工程中的实现]

上面只是分析了gpio-keys的作用,但是我们要如何使用呢?下面是具体的platform device的实现:

 
  1. static struct gpio_keys_button sc_buttons[]= {  
  2.     {  
  3.        .gpio   = S3C2410_GPG(0),   /* K1 */  
  4.        .code   = KEY_1,  
  5.        .desc   = "KEY1",  
  6.        .active_low = 1,  
  7.    },  
  8.     {  
  9.        .gpio   = S3C2410_GPG(3),   /* K2 */  
  10.        .code   = KEY_2,  
  11.        .desc   = "KEY2",  
  12.        .active_low = 1,  
  13.    },  
  14.     {  
  15.        .gpio   = S3C2410_GPG(5),   /* K3 */  
  16.        .code   = KEY_3,  
  17.         .desc  = "KEY3",  
  18.        .active_low = 1,  
  19.    },  
  20.     {  
  21.        .gpio   = S3C2410_GPG(6),   /* K4 */  
  22.        .code   = KEY_4,  
  23.        .desc   = "KEY4",  
  24.        .active_low = 1,  
  25.    },  
  26.     {  
  27.        .gpio   = S3C2410_GPG(7),   /* K5 */  
  28.        .code   = KEY_5,  
  29.        .desc   = "KEY5",  
  30.        .active_low = 1,  
  31.    },  
  32. };  
  33.    
  34. static struct gpio_keys_platform_datasc_button_data = {  
  35.    .buttons    = sc_buttons,  
  36.    .nbuttons   =ARRAY_SIZE(sc_buttons),  
  37. };  
  38.    
  39. static struct platform_device sc_button_device= {  
  40.    .name   = "gpio-keys",   /* 与platform driver的名字要相同 */  
  41.    .id = -1,  
  42.    .dev    = {  
  43.        .platform_data  =&sc_button_data,  
  44.     }  
  45. };  
  46.    
  47. static struct platform_device*mini2440_devices[] __initdata = {  
  48.          ……  
  49.    &sc_button_device,  
  50. };  

最后记得把内核中对应的配置选项选上就OK。

原创粉丝点击