16 内核里gpio-keys设备驱动的设备树描述

时间:2022-12-12 17:54:47

此设备驱动适用于连接到一个具有中断功能的io口的按键驱动.
使用platform_device方法可参考:http://blog.csdn.net/jklinux/article/details/73828786

此设备驱动在内核里配置:

make menuconfig ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu-

Device Drivers --->
Input device support --->
[*] Keyboards --->
<*> GPIO Buttons

驱动源码在: drivers/input/keyboard/gpio_keys.c


898 static struct platform_driver gpio_keys_device_driver = {
899 .probe = gpio_keys_probe,
900 .remove = gpio_keys_remove,
901 .driver = {
902 .name = "gpio-keys",
903 .pm = &gpio_keys_pm_ops,
904 .of_match_table = gpio_keys_of_match,
905 }
906 };

722 static const struct of_device_id gpio_keys_of_match[] = {
723 { .compatible = "gpio-keys", },
724 { },
725 };
726 MODULE_DEVICE_TABLE(of, gpio_keys_of_match);

通过上面两部分内容可以得知,在设备树里设备节点的compatible属性值应为"gpio-keys".

当匹配上时, gpio_keys_probe函数就会被触发调用,获取设备提供的资源.
728 static int gpio_keys_probe(struct platform_device *pdev)
729 {
730 struct device *dev = &pdev->dev;
731 const struct gpio_keys_platform_data *pdata = dev_get_platdata(dev);
732 struct fwnode_handle *child = NULL;
733 struct gpio_keys_drvdata *ddata;
734 struct input_dev *input;
735 size_t size;
736 int i, error;
737 int wakeup = 0;
738
739 if (!pdata) {
740 pdata = gpio_keys_get_devtree_pdata(dev); //获取设备树里设备节点提供的资源
741 if (IS_ERR(pdata))
742 return PTR_ERR(pdata);
743 }
744
745 size = sizeof(struct gpio_keys_drvdata) +
746 pdata->nbuttons * sizeof(struct gpio_button_data);
747 ddata = devm_kzalloc(dev, size, GFP_KERNEL);
748 if (!ddata) {
749 dev_err(dev, "failed to allocate state\n");
750 return -ENOMEM;
751 }
752
753 ddata->keymap = devm_kcalloc(dev,
754 pdata->nbuttons, sizeof(ddata->keymap[0]),
755 GFP_KERNEL);
756 if (!ddata->keymap)
757 return -ENOMEM;
758
759 input = devm_input_allocate_device(dev);
760 if (!input) {
761 dev_err(dev, "failed to allocate input device\n");
762 return -ENOMEM;
763 }
764
... //初始化input_dev对象的成员
788 if (pdata->rep)
789 __set_bit(EV_REP, input->evbit);
790
791 for (i = 0; i < pdata->nbuttons; i++) {
792 const struct gpio_keys_button *button = &pdata->buttons[i];
793
794 if (!dev_get_platdata(dev)) {
795 child = device_get_next_child_node(dev, child); //获取设备节点里的子节点
...
803
804 error = gpio_keys_setup_key(pdev, input, ddata,
805 button, i, child); //根据子节点提供的属性值设置input_dev对象所支持的键码
...
813 }
...
824 error = input_register_device(input);
...

659 static struct gpio_keys_platform_data *
660 gpio_keys_get_devtree_pdata(struct device *dev)
661 {
662 struct gpio_keys_platform_data *pdata;
663 struct gpio_keys_button *button;
664 struct fwnode_handle *child;
665 int nbuttons;
666
667 nbuttons = device_get_child_node_count(dev);
668 if (nbuttons == 0)
669 return ERR_PTR(-ENODEV);//意味着需要通过设备节点的子节点来提供资源
...
681
682 pdata->rep = device_property_read_bool(dev, "autorepeat");
683 //表示设备节点可以有一个autorepeat属性,属性值为bool类型(0/1). 用于表示输入设备是否自动间隔地重复提交按键.
684 device_property_read_string(dev, "label", &pdata->name);
685 //表示设备节点可以有一个label属性, 属性性为string类型.用于指定输入设备的名字
686 device_for_each_child_node(dev, child) { //遍历子节点
687 if (is_of_node(child))
688 button->irq =
689 irq_of_parse_and_map(to_of_node(child), 0);
690
691 if (fwnode_property_read_u32(child, "linux,code",
692 &button->code)) {
693 dev_err(dev, "Button without keycode\n");
694 fwnode_handle_put(child);
695 return ERR_PTR(-EINVAL); //意味着每个子节点都必须有"linux,code"属性,属性值为u32类型。用于指定此子节点对应的按键的键码.
696 }
697
698 fwnode_property_read_string(child, "label", &button->desc);
699 //每个子节点还可以有一个label属性,属性值为string类型
700 if (fwnode_property_read_u32(child, "linux,input-type",
701 &button->type))
702 button->type = EV_KEY; //每个字节点还可以通过"linux,input-type"属性来指定输入设备所支持的事件类型. 如果不提供则设置为EV_KEY
703
704 button->wakeup =
705 fwnode_property_read_bool(child, "wakeup-source") ||
706 /* legacy name */
707 fwnode_property_read_bool(child, "gpio-key,wakeup");
708
709 button->can_disable =
710 fwnode_property_read_bool(child, "linux,can-disable");
711
712 if (fwnode_property_read_u32(child, "debounce-interval",
713 &button->debounce_interval))
714 button->debounce_interval = 5; //如是中断触发的按键,则"debounce-interval"属性无需设置,如是定时轮询方式的则需要设置此间隔时间.
715
716 button++;
717 }
718
719 return pdata;
720 }

当在probe函数里遍历每个子节点时,会调用gpio_keys_setup_key函数来设置并获取io口信息.
468 static int gpio_keys_setup_key(struct platform_device *pdev,
469 struct input_dev *input,
470 struct gpio_keys_drvdata *ddata,
471 const struct gpio_keys_button *button,
472 int idx,
473 struct fwnode_handle *child)
474 {
...
486
487 if (child) {
488 bdata->gpiod = devm_fwnode_get_gpiod_from_child(dev, NULL,
489 child,
490 GPIOD_IN,
491 desc);
// con_id为NULL, 则表示子节点里应用gpios属性来提供io口资源.

如板上有两个按键,一个按键接PA12(作键盘上的UP键), 另一个接PA11(作键盘上的ENTER键).
设备树里的设备节点:

mykeys {
compatible = "gpio-keys";
autorepeat = <1>;
label = "mykeys";

btn0 {
label = "btn0";
gpios = <&pio 0 12 GPIO_ACTIVE_HIGH>;
linux,code = <KEY_UP>;
};

btn1 {
label = "btn1";
gpios = <&pio 0 11 GPIO_ACTIVE_HIGH>;
linux,code = <KEY_ENTER>;
};
};

更新使用设备树,重启系统后,可以查看到:

^_^ / # cat /proc/bus/input/devices
I: Bus=0019 Vendor=0001 Product=0001 Version=0100
N: Name="mykeys"
P: Phys=gpio-keys/input0
S: Sysfs=/devices/platform/mykeys/input/input1
U: Uniq=
H: Handlers=kbd event1
B: PROP=0
B: EV=100003
B: KEY=8000000000 10000000

另如按键所接的IO口是没有中断功能的,则可以使用gpio_keys_polled.c设备驱动.

可参考内核源码里的: Documentation/devicetree/bindings/input/gpio-keys.txt
Documentation/devicetree/bindings/input/gpio-keys-polled.txt