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\n");
 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\n",
 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\n",
 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\n",
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\n",
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\n",
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\n",
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

谢谢!

时间: 2024-10-25 20:15:43

linux系统下标准GPIO按键驱动的相关文章

Vi (Unix及Linux系统下标准的编辑器)

Vi是Unix及Linux系统下标准的编辑器.学会它后,您将在Linux的世界里畅行无阻.基本上vi可以分为三种状态,分别是命令模式.插入模式,和底行模式. vi编辑器是所有Unix及Linux系统下标准的编辑器,它的强大不逊色于任何最新的文本编辑器,这里只是简单地介绍一下它的用法和一小部分指令.由于对Unix及Linux系统的任何版本,vi编辑器是完全相同的,因此您可以在其他任何介绍vi的地方进一步了解它.Vi也是Linux中最基本的文本编辑器,学会它后,您将在Linux的世界里畅行无阻. 基

使用 ipmitool 实现 Linux 系统下对服务器的 ipmi 管理

http://www.ibm.com/developerworks/cn/linux/l-ipmi/ 1.简介 IPMI(Intelligent Platform Management Interface)即智能平台管理接口是使硬件管理具备“智能化”的新一代通用接口标准.用户可以利用 IPMI 监视服务器的物理特征,如温度.电压.电扇工作状态.电源供应以及机箱入侵等.Ipmi 最大的优势在于它是独立于 CPU BIOS 和 OS 的,所以用户无论在开机还是关机的状态下,只要接通电源就可以实现对服

电源管理ACPI、及APM、GNU/Linux系统下的对应命令使用

/*********************************************************************  * Author  : Samson  * Date    : 05/19/2014  * Test platform:  *              Mint 15-3.8.13.13  *              GNU bash, version 4.2.45  * ***************************************

第九章 Linux系统下分区、格式化磁盘,学会挂载和卸载磁盘

1. 请查资料了解这些术语:/dev/hda, /dev/hdb, /dev/sda, /dev/sdb, 他们之间有什么区别?/dev/hda, /dev/hdb 是 IDE借口硬盘的块文件. /dev/hda  第一块IDE磁盘 : /dev/hdb 第二块IDE磁盘 /dev/sda, /dev/sdb 是SCSI接口硬盘的块文件. /dev/sda 第一块SCSI磁盘 :/dev/sdb 第二块SCSI磁盘 2. 为什么 du -b /etc/passwd  和 du -k  /etc/

linux系统下文件查找

在我们实际应用中,经常需要查找某个特定的文件,或者根据文件的某个特定属性进行查找,今天小菜就给大家分享一下,linux系统下文件查找的两大利器: 1,locate:非实时查找(基于预先生成的数据库查找):模糊匹配:速度快 2,find:实时查找(遍历目录中的所有文件完成查找):精确匹配,支持众多查找标准:速度慢 一.locate 查询系统上预先生成的文件索引数据库:/var/lib/mlocate/mlocate.db 依赖于事先构建的索引:索引的构建是在系统较为空闲时自动进行(周期性任务) 管

linux系统下安装Python环境

如何在Linux系统下搭建Python开发环境(http://www.maiziedu.com/course/python/)?Python在Linux系统中安装方法在Windows下是有很大的区别的,今天就具体记录一下关于 Python 环境软件包在Linux系统下的一些安装步骤 1.升级 Python 到 2.7.10( 默认 2.6.6 ) shell > yum -y install epel-release shell > yum -y install gcc wget readli

Linux系统下目录文件配置

刚刚接触Linux,对于Linux系统下的目录配置进行了一些研究,为了避免以后误操作这些目录,建议大家还是记忆一下相关的配置! 总结 Linux 根目录主要配置 目录 文件配置内容 /bin 单用户维护模式下还能被操作的命令 /boot 开机会使用到的文件,包括Linux内核文件以及开机菜单与开机所需配置文件 /dev 设备以及设备接口文件,访问该目录下文件相当于访问某设备 - /dev下的重要文件:/dev/null,/dev/zero,/dev/tty /etc 系统主要的配置文件,比如账号

Linux系统下程序后台运行nohup,&,screen等

"nohup" 在用户ssh终端断开或者网络端口时,终端没有HUP信号则会关闭所有子进程. nohup则会让进程忽略HUP信号,不受终端断开限制 一般在结尾加上"&"将命令同时放到中断后台运行 nohup标准输出和标准错误输出会被重定向到nohup.out文件中 [[email protected] ~]# nohup ping www.ibm.com & [1] 3059 nohup: appending output to `nohup.out'

linux系统下软件安装包类型及安装介绍

linux系统下软件安装包类型及安装介绍 一.解析Linux应用软件安装包,通常Linux应用软件的安装包有四种: 1)tar包,如software-1.2.3-1.tar.gz.他是使用UNIX系统的打包工具tar打包的. 2)rpm包,如software-1.2.3-1.i386.rpm.他是Redhat Linux提供的一种包封装格式.包的管理工具YUM 3)dpkg包,如software-1.2.3-1.deb.他是Debain Linux提供的一种包封装格式.包的管理工具apt-get