1. 程式人生 > >16 核心裡gpio-keys裝置驅動的裝置樹描述

16 核心裡gpio-keys裝置驅動的裝置樹描述

此裝置驅動在核心裡配置:

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