文章目录
-
- 1 Linux自带按键驱动程序源码
- 2 Linux内核自带按键驱动程序的使用
1 Linux自带按键驱动程序源码 在Linux内核中也集成了按键的驱动程序,要使用的话,需要在内核中进行配置,按照下面路径找到相应的配置:
Device Drivers--->
Input device support--->
[*]Keyboards--->
<*>GPIO Buttons
选中“GPIO Buttons”选项,这样驱动程序就会编译进 Linux 内核中,如图 1.1所示:
文章图片
图 1.1 保存退出后,在.congfig文件下会出现“CONFIG_KEYBOARD_GPIO=y”。
Linux内核中自带的按键驱动程序路径drivers/input/keyboard/gpio_keys.c,按键驱动程序基于platform框架,使用input子系统实现功能。
gpio_keys.c文件部分代码如下:
673 static const struct of_device_id gpio_keys_of_match[] = {
674{ .compatible = "gpio-keys", },
675{ },
676 };
......
842 static struct platform_driver gpio_keys_device_driver = {
843.probe = gpio_keys_probe,
844.remove = gpio_keys_remove,
845.driver = {
846.name = "gpio-keys",
847.pm = &gpio_keys_pm_ops,
848.of_match_table = of_match_ptr(gpio_keys_of_match),
849}
850 };
851
852 static int __init gpio_keys_init(void)
853 {
854return platform_driver_register(&gpio_keys_device_driver);
855 }
856
857 static void __exit gpio_keys_exit(void)
858 {
859platform_driver_unregister(&gpio_keys_device_driver);
860 }
从上面代码可以看出,这是一个典型的platform框架结构,当和设备匹配成功后gpio_keys_probe函数就会执行,来看一下gpio_keys_probe函数中实现什么:
689 static int gpio_keys_probe(struct platform_device *pdev)
690 {
691struct device *dev = &pdev->dev;
692const struct gpio_keys_platform_data *pdata = https://www.it610.com/article/dev_get_platdata(dev);
693struct gpio_keys_drvdata *ddata;
694struct input_dev *input;
695size_t size;
696int i, error;
697int wakeup = 0;
698
699if (!pdata) {
700pdata = gpio_keys_get_devtree_pdata(dev);
701if (IS_ERR(pdata))
702return PTR_ERR(pdata);
703 }
......
713input = devm_input_allocate_device(dev);
714if (!input) {
715dev_err(dev,"failed to allocate input device\n");
716return -ENOMEM;
717}
718
719ddata->pdata = https://www.it610.com/article/pdata;
720ddata->input = input;
721mutex_init(&ddata->disable_lock);
722
723platform_set_drvdata(pdev, ddata);
724input_set_drvdata(input, ddata);
725
726input->name = pdata->name ? : pdev->name;
727input->phys = "gpio-keys/input0";
728input->dev.parent = &pdev->dev;
729input->open = gpio_keys_open;
730input->close = gpio_keys_close;
731
732input->id.bustype = BUS_HOST;
733input->id.vendor = 0x0001;
734input->id.product = 0x0001;
735input->id.version = 0x0100;
736
737/* Enable auto repeat feature of Linux input subsystem */
738if (pdata->rep)
739__set_bit(EV_REP, input->evbit);
740
741for (i = 0;
i < pdata->nbuttons;
i++) {
742const struct gpio_keys_button *button = &pdata->buttons[i];
743struct gpio_button_data *bdata = https://www.it610.com/article/&ddata->data[i];
744
745error = gpio_keys_setup_key(pdev, input, bdata, button);
746if (error)
747return error;
748
749if (button->wakeup)
750wakeup = 1;
751}
......
760error = input_register_device(input);
761if (error) {
762dev_err(dev, "Unable to register input device, error: %d\n",
763error);
764goto err_remove_group;
765}
......
774 }
第 700 行,调用 gpio_keys_get_devtree_pdata 函数从设备树中获取到 KEY 相关的设备节点信息。
第 713 行,使用 devm_input_allocate_device 函数申请 input_dev。
第 726~735,初始化 input_dev。
第 739 行,设置 input_dev 事件,这里设置了 EV_REP 事件。
第 745 行,调用 gpio_keys_setup_key 函数继续设置 KEY,此函数会设置 input_dev 的 EV_KEY 事件已经事件码(也就是 KEY 模拟为哪个按键)。
第 760 行,调用 input_register_device 函数向 Linux 系统注册 input_dev。
我们接下来再来看一下 gpio_keys_setup_key 函数的内容:
437 static int gpio_keys_setup_key(struct platform_device *pdev,
438struct input_dev *input,
439struct gpio_button_data *bdata,
440const struct gpio_keys_button *button)
441 {
442const char *desc = button->desc ? button->desc : "gpio_keys";
443struct device *dev = &pdev->dev;
444irq_handler_t isr;
445unsigned long irqflags;
446int irq;
447int error;
448
449bdata->input = input;
450bdata->button = button;
451spin_lock_init(&bdata->lock);
452
453if (gpio_is_valid(button->gpio)) {
454
455error = devm_gpio_request_one(&pdev->dev, button->gpio,
456GPIOF_IN, desc);
457if (error < 0) {
458dev_err(dev, "Failed to request GPIO %d, error %d\n",
459button->gpio, error);
460return error;
......
488isr = gpio_keys_gpio_isr;
489irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
490
491} else {
492if (!button->irq) {
493dev_err(dev, "No IRQ specified\n");
494return -EINVAL;
495}
496bdata->irq = button->irq;
......
506
507isr = gpio_keys_irq_isr;
508irqflags = 0;
509}
510
511input_set_capability(input, button->type ?: EV_KEY, button->code);
......
540return 0;
541 }
第 511 行,调用 input_set_capability 函数设置 EV_KEY 事件以及 KEY 的按键类型,也就是 KEY 作为哪个按键?我们会在设备树里面设置指定的 KEY 作为哪个按键。
一切都准备就绪以后剩下的就是等待按键按下,然后向 Linux 内核上报事件,事件上报是在 gpio_keys_irq_isr 函数中完成的,此函数内容如下:
392 static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id)
393 {
394struct gpio_button_data *bdata = https://www.it610.com/article/dev_id;
395const struct gpio_keys_button *button = bdata->button;
396struct input_dev *input = bdata->input;
397unsigned long flags;
398
399BUG_ON(irq != bdata->irq);
400
401spin_lock_irqsave(&bdata->lock, flags);
402
403if (!bdata->key_pressed) {
404if (bdata->button->wakeup)
405pm_wakeup_event(bdata->input->dev.parent, 0);
406
407input_event(input, EV_KEY, button->code, 1);
408input_sync(input);
409
410if (!bdata->release_delay) {
411input_event(input, EV_KEY, button->code, 0);
412input_sync(input);
413goto out;
414}
415
416bdata->key_pressed = true;
417}
418
419if (bdata->release_delay)
420mod_timer(&bdata->release_timer,
421jiffies + msecs_to_jiffies(bdata->release_delay));
422 out:
423spin_unlock_irqrestore(&bdata->lock, flags);
424return IRQ_HANDLED;
425 }
可以看出我们之前编写的key_input.c驱动文件和Linux内核中自带的驱动文件思路相同,都是申请和初始化input_dev结构体,设置事件,然后向Linux内核注册。最终在按键中断处理函数中上报事件和事件值。
2 Linux内核自带按键驱动程序的使用 要使用Linux内核中自带的按键驱动程序也很简单,只需要在设备树中添加指定的设备节点就可以了,具体可以参考Documentation/devicetree/bindings/input/gpio-keys.txt文件。
节点内容有已下几个要求:
① 节点名字为“gpio-keys”。
② gpio-keys 节点的 compatible 属性值一定要设置为“gpio-keys”。
③ 所有的 KEY 都是 gpio-keys 的子节点,每个子节点可以用如下属性描述自己:
gpios:KEY 所连接的 GPIO 信息。
interrupts:KEY 所使用 GPIO 中断信息,不是必须的,可以不写。
label:KEY 名字
linux,code:KEY 要模拟的按键,也就是示例代码 58.1.2.4 中的这些按键。
④ 如果按键要支持连按的话要加入 autorepeat。
打开topeet_emmc_4_3.dts文件,根据上面几点要求,创建按键的设备节点,内容如下:
1 gpio-keys {
2compatible = "gpio-keys";
3#address-cells = <1>;
4#size-cells = <0>;
5autorepeat;
6key0 {
7label = "GPIO Key Enter";
8linux,code = ;
9gpios = <&gpio1 18 GPIO_ACTIVE_LOW>;
10};
11 };
第2行,compatible属性值要和驱动中的compatible名称一样。
第5行,autorepeat表示支持按键连按。
第6~10行,设置按键信息,设置按键label名称。第8行,设置按键的功能,将按键设置为KEY_ENTER功能,也就是回车键。最后第9行设置按键所使用的GPIO引脚。
在设备树中添加完按键的设备节点后,重新编译设备树文件,然后重新下载设备树文件,启动Linux系统,查看/dev/input目录,如图 2.1所示:
文章图片
图 2.1 可以看到已经生成了event1这个文件,这个文件就是按键KEY0对应的设备文件,使用hexdump命令查看/dev/input/event1文件,命令如下:
hexdump /dev/input/event1
然后按下开发板上的按键KEY0,终端会出现下面的内容:
文章图片
图 2.2 终端输出这些内容表示Linux内核中的按键驱动正常工作,如果终端没有输出,按键按下没有反应,可以有下面几点原因:
① 是否使能 Linux 内核 KEY 驱动。
② 设备树中 gpio-keys 节点是否创建成功。
③ 在设备树中是否有其他外设也使用了 KEY 按键对应的 GPIO,但是我们并没有删除掉这些外设信息。
【i.MX6ULL终结者|i.MX6ULL终结者Linux INPUT子系统实验linux自带按键驱动程序】
文章图片
推荐阅读
- 嵌入式|陀螺仪数据处理(BMI088)
- 程序员|树莓派4B安装Ubuntu Mate20.04
- 嵌入式|CAN通讯
- STM32的使用|STM32F4软件IIC的使用
- Linux|7.【刷爆LeetCode】把字符串转换成整数(多方法、多思路)
- docker|k8s搭建EFK日志管理系统
- 运维|Linux的进程控制
- 服务器|Linux中进程间通信
- linux|【Linux】多线程的概念