• linux系统下标准GPIO按键驱动


    前言:

      linux下platform平台驱动是为了方便隔离bsp与driver,利于移植。体现好代码的高内聚,低耦合。Linux设备驱动模型中,关心总线,设备和驱动三个实体。总线将设备和驱动绑定。在系统每注册一个设备的时候,都会寻找与之相匹配的驱动,相反的,每加载一个驱动的时候,也会寻找与之匹配的设备。匹配由总线完成。linux发明了一种虚拟的总线,称之为platform总线,相应的设备称之为platform_device,驱动为platform_driver。

      基于这个模型,又根据面向对象的思想,同一类的事物定义为一个基类。因此在驱动中,将同一基类的驱动,再抽象一个核心层。因此又分为了input设备,I2C设备,SPI设备等驱动。我们今天说的标准按键驱动,就是基于input输入设备。

    正题:

      在内核中,按键的驱动已经完成!!!不需要我们自己写。driver/input/keyboard/gpio_keys.c 就是驱动文件。刚才说了,有驱动,还要有设备啊~只有这两个匹配了,只有这样我们才能在应用层操作这个设备。这个设备我们在哪注册呢?一般的,在板级的初始化c文件里面。比如:board_max6q_sabresd.c。这里面我们怎么搞呢?看下面::::::

     1 #if defined(CONFIG_KEYBOARD_GPIO) || defined(CONFIG_KEYBOARD_GPIO_MODULE)
     2 #define GPIO_BUTTON(gpio_num, ev_code, act_low, descr, wake, debounce)    
     3 {                                
     4     .gpio        = gpio_num,                
     5     .type        = EV_KEY,                
     6     .code        = ev_code,                
     7     .active_low    = act_low,                
     8     .desc        = "btn " descr,                
     9     .wakeup        = wake,                    
    10     .debounce_interval = debounce,                
    11 }
    12 
    13 static struct gpio_keys_button imx6q_buttons[] = {
    14     GPIO_BUTTON(SABRESD_KEY_USER1, KEY_VOLUMEUP, 1, "user-key-1", 0, 1),
    15     GPIO_BUTTON(SABRESD_KEY_USER2, KEY_VOLUMEDOWN, 1, "user-key-2", 0, 1),
    16     GPIO_BUTTON(SABRESD_KEY_WHIBUSB, KEY_F1, 1, "whibusb", 0, 1),
    17     GPIO_BUTTON(SABRESD_KEY_WHIBUSL, KEY_F2, 1, "whibusl", 0, 1),
    18     GPIO_BUTTON(SABRESD_KEY_WHIBUSR, KEY_F3, 1, "whibusr", 0, 1),
    19 };
    20 
    21 static struct gpio_keys_platform_data imx6q_button_data = {
    22     .buttons    = imx6q_buttons,
    23     .nbuttons    = ARRAY_SIZE(imx6q_buttons),
    24 };
    25 
    26 static struct platform_device imx6q_button_device = {
    27     .name        = "gpio-keys",
    28     .id        = -1,
    29     .num_resources  = 0,
    30     .dev        = {
    31         .platform_data = &imx6q_button_data,
    32     }
    33 };
    34 
    35 static void __init imx6q_add_device_buttons(void)
    36 {
    37     platform_device_register(&imx6q_button_device);
    38 }
    39 #else
    40 static void __init imx6q_add_device_buttons(void) {}
    41 #endif

    上面注册了5个按键设备。然后在board_init()初始化函数里面,添加imx6q_add_device_buttons()。我们就可以通过应用层操作了。比如:按下某个按键的时候,在read()函数中获取哪个键被按下。下面的链接360无死角的gpio按键应用。注意的是/dev/input/eventX不太一样,操作的时候可以cat /proc/bus/input/devices查看一下咱们的按键是哪个event。

      看看上面的代码,依葫芦画瓢就可以完成GPIO按键的设备添加。接下来我们分析下驱动,能用了,最好还是明白下原理。

      跟网上其他的差不多,我们着重分析两个函数:

      

      1 static int __devinit gpio_keys_probe(struct platform_device *pdev)
      2 {
      3     struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;    //相关的结构体以及宏定义在本c文件和include/linux/input.h include/linux/gpio_keys.h里面找。
      4     struct gpio_keys_drvdata *ddata;
      5     struct device *dev = &pdev->dev;
      6     struct input_dev *input;
      7     int i, error;
      8     int wakeup = 0;
      9   
    10 ddata = kzalloc(sizeof(struct gpio_keys_drvdata) +    //分配且清空数据空间 11 pdata->nbuttons * sizeof(struct gpio_button_data), 12 GFP_KERNEL); 13 input = input_allocate_device();    //分配一个input设备 14 if (!ddata || !input) { 15 dev_err(dev, "failed to allocate state "); 16 error = -ENOMEM; 17 goto fail1; 18 } 19     //设置input设备属性
    20
    ddata->input = input;    
    21 ddata->n_buttons = pdata->nbuttons; 22 ddata->enable = pdata->enable; 23 ddata->disable = pdata->disable; 24 mutex_init(&ddata->disable_lock); 25 26 platform_set_drvdata(pdev, ddata); 27 input_set_drvdata(input, ddata); 28 29 input->name = pdata->name ? : pdev->name; 30 input->phys = "gpio-keys/input0"; 31 input->dev.parent = &pdev->dev; 32 input->open = gpio_keys_open; 33 input->close = gpio_keys_close; 34 35 input->id.bustype = BUS_HOST; 36 input->id.vendor = 0x0001; 37 input->id.product = 0x0001; 38 input->id.version = 0x0100; 39 40 /* Enable auto repeat feature of Linux input subsystem */ 41 if (pdata->rep) 42 __set_bit(EV_REP, input->evbit); 43 44 for (i = 0; i < pdata->nbuttons; i++) {    //对注册的每个gpio进行设置 45 struct gpio_keys_button *button = &pdata->buttons[i]; 46 struct gpio_button_data *bdata = &ddata->data[i]; 47 unsigned int type = button->type ?: EV_KEY; 48 49 bdata->input = input; 50 bdata->button = button; 51 52 error = gpio_keys_setup_key(pdev, bdata, button);    //这个是具体实现,下面分析 53 if (error) 54 goto fail2; 55 56 if (button->wakeup) 57 wakeup = 1; 58      /*设置设备对事件的支持,比如设置对键1和键2的支持*/ 59 input_set_capability(input, type, button->code); 60 } 61   //创建文件系统的节点,可以网上搜搜看,好像我之前的博文也有写到这块 62 error = sysfs_create_group(&pdev->dev.kobj, &gpio_keys_attr_group); 63 if (error) { 64 dev_err(dev, "Unable to export keys/switches, error: %d ", 65 error); 66 goto fail2; 67 } 68   //注册一个input设备 69 error = input_register_device(input); 70 if (error) { 71 dev_err(dev, "Unable to register input device, error: %d ", 72 error); 73 goto fail3; 74 } 75 76 /* get current state of buttons */ 77 for (i = 0; i < pdata->nbuttons; i++) 78 gpio_keys_report_event(&ddata->data[i]); 79 input_sync(input); 80 81 device_init_wakeup(&pdev->dev, wakeup); 82 83 return 0; 84 85 fail3: 86 sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group); 87 fail2: 88 while (--i >= 0) { 89 free_irq(gpio_to_irq(pdata->buttons[i].gpio), &ddata->data[i]); 90 if (ddata->data[i].timer_debounce) 91 del_timer_sync(&ddata->data[i].timer); 92 cancel_work_sync(&ddata->data[i].work); 93 gpio_free(pdata->buttons[i].gpio); 94 } 95 96 platform_set_drvdata(pdev, NULL); 97 fail1: 98 input_free_device(input); 99 kfree(ddata); 100 101 return error; 102 }

    上面是probe函数的一些简要说明,probe顾名思义,就是探测到设备注册时,驱动完成的工作。

    下面的函数是probe里面重要的gpio_keys_setup_key()函数

     1 static int __devinit gpio_keys_setup_key(struct platform_device *pdev,
     2                      struct gpio_button_data *bdata,
     3                      struct gpio_keys_button *button)
     4 {
     5     const char *desc = button->desc ? button->desc : "gpio_keys";
     6     struct device *dev = &pdev->dev;
     7     unsigned long irqflags;
     8     int irq, error;
     9 
    10     setup_timer(&bdata->timer, gpio_keys_timer, (unsigned long)bdata);
    11     INIT_WORK(&bdata->work, gpio_keys_work_func);
    12 
    13     error = gpio_request(button->gpio, desc);
    14     if (error < 0) {
    15         dev_err(dev, "failed to request GPIO %d, error %d
    ",
    16             button->gpio, error);
    17         goto fail2;
    18     }
    19 
    20     error = gpio_direction_input(button->gpio);
    21     if (error < 0) {
    22         dev_err(dev, "failed to configure"
    23             " direction for GPIO %d, error %d
    ",
    24             button->gpio, error);
    25         goto fail3;
    26     }
    27 
    28     if (button->debounce_interval) {
    29         error = gpio_set_debounce(button->gpio,
    30                       button->debounce_interval * 1000);
    31         /* use timer if gpiolib doesn't provide debounce */
    32         if (error < 0)
    33             bdata->timer_debounce = button->debounce_interval;
    34     }
    35 
    36     irq = gpio_to_irq(button->gpio);
    37     if (irq < 0) {
    38         error = irq;
    39         dev_err(dev, "Unable to get irq number for GPIO %d, error %d
    ",
    40             button->gpio, error);
    41         goto fail3;
    42     }
    43 
    44     irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
    45     /*
    46      * If platform has specified that the button can be disabled,
    47      * we don't want it to share the interrupt line.
    48      */
    49     if (!button->can_disable)
    50         irqflags |= IRQF_SHARED;
    51     /*
    52      * If platform has specified that the button can wake up the system,
    53      * for example, the power key which usually use to wake up the system
    54      * from suspend, we add the IRQF_EARLY_RESUME flag to this irq, so
    55      * that the power key press can be handled and reported as early as
    56      * possible. Some platform like Android need to get the power key
    57      * event early to reume some devcies like framebuffer and etc.
    58      */
    59     if (button->wakeup)
    60         irqflags |= IRQF_EARLY_RESUME;
    61 
    62     error = request_any_context_irq(irq, gpio_keys_isr, irqflags, desc, bdata);
    63     if (error < 0) {
    64         dev_err(dev, "Unable to claim irq %d; error %d
    ",
    65             irq, error);
    66         goto fail3;
    67     }
    68 
    69     return 0;
    70 
    71 fail3:
    72     gpio_free(button->gpio);
    73 fail2:
    74     return error;
    75 }

    主要是gpio的中断,定时器,工作队列等设置。没什么大问题。具体函数不懂,碰到一个查一个。

    代分析码相对简单困了回去睡觉了。

    参考:

      GPIO按键的应用参考:http://gofayao.blog.163.com/blog/static/147305254201491631157686/

      驱动代码分析:http://www.linuxidc.com/Linux/2011-11/47650p3.htm

    谢谢!

  • 相关阅读:
    ApplicationContext
    消息队列种类
    Zookeeper的功能以及工作原理
    java多线程(1)模式 不可变对象
    dubbo容错,负载均衡,协议
    dubbo的底层原理
    消息事务处理
    多线程循环打印 abc
    Spring 事务管理
    文件上传和下载
  • 原文地址:https://www.cnblogs.com/cyc2009/p/4127496.html
Copyright © 2020-2023  润新知