1. 程式人生 > >Linux驅動開發10:【裝置樹】nanopi的按鍵驅動

Linux驅動開發10:【裝置樹】nanopi的按鍵驅動

介紹

這一節在nanopi上實現按鍵驅動,和LED驅動一樣,通用的按鍵驅動在linux核心中已經實現好,我們只需要按照要求寫好裝置樹即可,不用我們自己實現按鍵驅動。這一節中首先修改裝置樹並測試按鍵驅動,然後分析drivers/input/keyboard/gpio_keys.c檔案,看按鍵驅動是如何實現的。

新增裝置樹節點

sun8i-h3-neo-nanopi-air.dtsi檔案中加入以下內容

/ {
    mygpio-key {
        compatible = "gpio-keys";
        input-name = "key-test";
        pinctrl-names = "default"
; pinctrl-0 = <&test_pin1>; [email protected]0 { lable = "key-test"; linux,code = <BTN_0>; gpios = <&pio 6 8 GPIO_ACTIVE_LOW>; }; }; ... }; &pio { ... test_pin1: [email protected]1 { pins = "PG8"
; function = "gpio_in"; }; };

和LED的裝置樹節點相似,按鍵的裝置樹節點也由兩部分組成,一部分是mygpio-key,另一部分是[email protected],其中[email protected]也是pio的子節點,表明了GPIO的編號和模式,在mygpio-key的pinctrl-0屬性中對其進行了引用。

mygpio-key的compatible屬性用來與驅動相匹配,input-name是輸入裝置的名稱,因為在linux核心的按鍵驅動中使用的是input子系統。接下來是[email protected]

子節點,lable表明了其名稱,linux,code是註冊的事件型別是BTN_0事件,最後gpios表明了該按鍵對應的GPIO控制器。

測試

由於nanopi中預設是不支援通用的gpio-key驅動的,所以需要對核心進行重新配置和編譯。在核心根目錄執行

make menuconfig

然後進入Device Drivers --- Input device support --- Keyborads,選中GPIO_Buttons,如下圖所示
這裡寫圖片描述
儲存退出後重新編譯核心和裝置樹,然後啟動nanopi,進入/sys/class/input/event2可以看到如下內容,其中name就是我們定義的key的名字
這裡寫圖片描述
可見event2就是我們的按鍵輸入事件,使用以下程式讀取/dev/input/event2

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h>

int main(int argc, char const *argv[])
{
    int fd = 0;
    struct input_event event[2] = {0};
    int ret = 0;

    if (argc != 2) {
        printf("./iread <file>\n");
        return -1;
    }

    fd = open(argv[1],O_RDONLY);
    if (fd < 0) {
        perror("open");
        return -1;
    }

    while(1){
        ret = read(fd, &event, sizeof(event));
        if(ret < 0) {
            perror("read");
            return -1;
        }
        printf("ret:%d, val0:%d, val1:%d\n", ret,
        event[0].value, event[1].value);
        sleep(1);
    }
    return 0;
}

將該檔案編譯為iread,然後執行sudo ./iread /dev/input/event2,程式會阻塞,此時如果在PG8引腳接一個按鍵到3.3V,按下該按鍵會讀取該引腳的狀態,如下圖所示
這裡寫圖片描述

按鍵驅動實現的分析

按鍵驅動的實現在drivers/input/keyborad/gpio_keys.c檔案中,這是一個platform驅動,platform_driver的定義如下:

static const struct of_device_id gpio_keys_of_match[] = {
    { .compatible = "gpio-keys", },
    { },
};

static struct platform_driver gpio_keys_device_driver = {
    .probe      = gpio_keys_probe,
    .remove     = gpio_keys_remove,
    .driver     = {
        .name   = "gpio-keys",
        .pm = &gpio_keys_pm_ops,
        .of_match_table = gpio_keys_of_match,
    }
};

這裡的compatible屬性和我們定義的裝置樹節點相同,gpio_keys_probe函式執行。

 drivers/input/keyborad/gpio_keys.c --- gpio_keys_probe(                --- struct gpio_keys_platform_data *pdata
                                     |    struct platform_device *pdev)  |- struct gpio_keys_drvdata *ddata
                                     |                                   |- struct input_dev *input
                                     |                                   |- pdata = gpio_keys_get_devtree_pdata(dev)
                                     |                                   |- size = sizeof(struct gpio_keys_drvdata) +
                                     |                                   |         pdata->nbuttons * 
                                     |                                   |         sizeof(struct gpio_button_data);
                                     |                                   |- ddata = devm_kzalloc(dev, size, GFP_KERNEL);
                                     |                                   |- input = devm_input_allocate_device(dev)
                                     |                                   |- input->open = gpio_keys_open
                                     |                                   |- input->close = gpio_keys_close
                                     |                                   |- input->keycodemax = pdata->nbuttons
                                     |                                   |- for (i = 0; i < pdata->nbuttons; i++) {
                                     |                                   |      struct gpio_keys_button *button 
                                     |                                   |              = &pdata->buttons[i];
                                     |                                   |      gpio_keys_setup_key(pdev, input, 
                                     |                                   |              ddata, button, i, child);
                                     |                                   |  }
                                     |                                   |- input_register_device(input)
                                     |- gpio_keys_get_devtree_pdata( --- int nbuttons;
                                     |    struct device *dev)         |- struct gpio_keys_platform_data *pdata
                                     |                                |- struct gpio_keys_button *button
                                     |                                |- nbuttons = device_get_child_node_count(dev)
                                     |                                |- pdata = devm_kzalloc(dev,
                                     |                                |    sizeof(*pdata)+nbuttons*sizeof(*button),
                                     |                                |    GFP_KERNEL);
                                     |                                |- button = (struct gpio_keys_button *)
                                     |                                |    (pdata + 1);
                                     |                                |- pdata->buttons = button
                                     |                                |- pdata->nbuttons = nbuttons
                                     |                                |- device_property_read_string(
                                     |                                |    dev,"label",&pdata->name);
                                     |                                |- device_for_each_child_node(dev, child) {
                                     |                                |      fwnode_property_read_string(child, 
                                     |                                |        "label", &button->desc);
                                     |                                |      fwnode_property_read_u32(child, 
                                     |                                |        "linux,input-type", &button->type));
                                     |                                |  }
                                     |- gpio_keys_setup_key(                    --- struct gpio_button_data *bdata
                                          struct platform_device *pdev,          |- bdata = &ddata->data[idx]
                                          struct input_dev *input,               |- bdata->input = input
                                          struct gpio_keys_drvdata *ddata,       |- bdata->gpiod = 
                                          const struct gpio_keys_button *button, |    devm_fwnode_get_gpiod_from_child(
                                          int idx,                               |    dev, NULL,child,GPIOD_IN,desc)
                                          struct fwnode_handle *child)           |- irq = gpiod_to_irq(bdata->gpiod)
                                                                                 |- bdata->irq = irq
                                                                                 |- isr = gpio_keys_irq_isr
                                                                                 |- bdata->code = &ddata->keymap[idx]
                                                                                 |- *bdata->code = button->code
                                                                                 |- input_set_capability(
                                                                                 |    input, button->type ?: EV_KEY, *bdata->code)
                                                                                 |- devm_request_any_context_irq(
                                                                                 |    dev, bdata->irq, isr, irqflags,
                                                                                 |    desc, bdata);

該過程和LED驅動一致,先是解析裝置樹節點的屬性填充私有資料,然後註冊中斷,對input進行填充,最後註冊input子系統