树莓派 -- 输入设备驱动 (key)

输入设备(如按键,键盘,触摸屏等)是典型的字符设备,其一般工作原理是底层在按键或触摸等动作发生时产生一个中断,然后CPU通过SPI,I2C总线读取键值。
在这些工作中之后中断和读键值是与设备相关的,而输入事件的缓冲区管理,字符设备驱动的file_operations接口则对输入设备是通用的。因此内核设计了输入子系统,由核心层处理公共的工作。

先看gpio-keys platform_driver驱动代码, 源码
drivers/input/keyboard/gpio_keys.c

static int gpio_keys_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    const struct gpio_keys_platform_data *pdata = dev_get_platdata(dev);
    struct fwnode_handle *child = NULL;
    struct gpio_keys_drvdata *ddata;
    struct input_dev *input;
    size_t size;
    int i, error;
    int wakeup = 0;
    if (!pdata) {
        pdata = gpio_keys_get_devtree_pdata(dev);
        if (IS_ERR(pdata))
            return PTR_ERR(pdata);
    }
    size = sizeof(struct gpio_keys_drvdata) +
            pdata->nbuttons * sizeof(struct gpio_button_data);
    ddata = devm_kzalloc(dev, size, GFP_KERNEL);
    if (!ddata) {
        dev_err(dev, "failed to allocate state\n");
        return -ENOMEM;
    }
    ddata->keymap = devm_kcalloc(dev,
                     pdata->nbuttons, sizeof(ddata->keymap[0]),
                     GFP_KERNEL);
    if (!ddata->keymap)
        return -ENOMEM;
    //分配输入设备
    input = devm_input_allocate_device(dev);
    if (!input) {
        dev_err(dev, "failed to allocate input device\n");
        return -ENOMEM;
    }
    ddata->pdata = pdata;
    ddata->input = input;
    mutex_init(&ddata->disable_lock);
    platform_set_drvdata(pdev, ddata);
    input_set_drvdata(input, ddata);
    input->name = pdata->name ? : pdev->name;
    input->phys = "gpio-keys/input0";
    input->dev.parent = dev;
    input->open = gpio_keys_open;
    input->close = gpio_keys_close;
    input->id.bustype = BUS_HOST;
    input->id.vendor = 0x0001;
    input->id.product = 0x0001;
    input->id.version = 0x0100;
    input->keycode = ddata->keymap;
    input->keycodesize = sizeof(ddata->keymap[0]);
    input->keycodemax = pdata->nbuttons;
    /* Enable auto repeat feature of Linux input subsystem */
    if (pdata->rep)
        __set_bit(EV_REP, input->evbit);
    for (i = 0; i < pdata->nbuttons; i++) {
        const struct gpio_keys_button *button = &pdata->buttons[i];
        if (!dev_get_platdata(dev)) {
            child = device_get_next_child_node(dev, child);
            if (!child) {
                dev_err(dev,
                    "missing child device node for entry %d\n",
                    i);
                return -EINVAL;
            }
        }
        error = gpio_keys_setup_key(pdev, input, ddata,
                        button, i, child);
        if (error) {
            fwnode_handle_put(child);
            return error;
        }
        if (button->wakeup)
            wakeup = 1;
    }
    fwnode_handle_put(child);
    error = devm_device_add_group(dev, &gpio_keys_attr_group);
    if (error) {
        dev_err(dev, "Unable to export keys/switches, error: %d\n",
            error);
        return error;
    }
    //注册输入设备
    error = input_register_device(input);
    if (error) {
        dev_err(dev, "Unable to register input device, error: %d\n",
            error);
        return error;
    }
    device_init_wakeup(dev, wakeup);
    return 0;
}

key的硬件配置信息由设备树传入。
我们再看看设备树input的描述
Documentation\devicetree\bindings\input\gpio-keys.txt中给出的例子

Subnode properties:

    - gpios: OF device-tree gpio specification.
    - interrupts: the interrupt line for that input.
    - label: Descriptive name of the key.
    - linux,code: Keycode to emit.

Example nodes:

    gpio-keys {
            compatible = "gpio-keys";
            autorepeat;

            up {
                label = "GPIO Key UP";
                linux,code = <103>;
                gpios = <&gpio1 0 1>;
            };

            down {
                label = "GPIO Key DOWN";
                linux,code = <108>;
                interrupts = <1 IRQ_TYPE_LEVEL_HIGH 7>;
            };
            ...

打算使用Interrupts的方式。
首先看一下RaspberryPi 3-b的中断在设备树中的描述
arch\arm\boot\dts\bcm283x.dtsi

interrupt-parent = <&intc>;
    soc {
        compatible = "simple-bus";
        //...
        intc: interrupt-controller@7e00b200 {
            compatible = "brcm,bcm2835-armctrl-ic";
            reg = <0x7e00b200 0x200>;
            interrupt-controller;
            #interrupt-cells = <2>;
        };

        //...
        gpio: gpio@7e200000 {
            compatible = "brcm,bcm2835-gpio";
            reg = <0x7e200000 0xb4>;
            interrupts = <2 17>, <2 18>, <2 19>, <2 20>;

            gpio-controller;
            #gpio-cells = <2>;

            interrupt-controller;
            #interrupt-cells = <2>;

            //...

arch\arm\boot\dts\bcm2837.dtsi

    soc {
        //...
        local_intc: local_intc@40000000 {
            compatible = "brcm,bcm2836-l1-intc";
            reg = <0x40000000 0x100>;
            interrupt-controller;
            #interrupt-cells = <1>;
            interrupt-parent = <&local_intc>;
        };
    };
/* Make the BCM2835-style global interrupt controller be a child of the
 * CPU-local interrupt controller.
 */
&intc {
    compatible = "brcm,bcm2836-armctrl-ic";
    reg = <0x7e00b200 0x200>;
    interrupt-parent = <&local_intc>;
    interrupts = <8>;
};

arch\arm\boot\dts\bcm270x.dtsi

    soc: soc {
        //...

        gpio@7e200000 { /* gpio */
            interrupts = <2 17>, <2 18>;
        };

在arch\arm\boot\dts\bcm2708-rpi.dtsi中添加gpio_keys nodes.

    gpio_keys: gpio_keys {
        compatible = "gpio-keys";
    };

在arch\arm\boot\dts\bcm2710-rpi-3-b.dts中添加gpio_keys nodes.

&gpio_keys {
    pinctrl-names = "default";

    key1_key: key1 {
      interrupt-parent = <&gpio>;
      interrupts = <2 20>;
      gpios = <&gpio 20 GPIO_ACTIVE_LOW>;
      linux,code = <100>;
      label = "key1";
      debounce-interval = <10>;
      wakeup-source;
    };
};

重新编译设备树

$ make bcm2709_defconfig
$ make dtbs

并将新的设备树copy到SD卡boot下, 启动raspberryPi
查看platform devices

pi@raspberrypi:/sys/devices/platform $ cd gpio_keys/
pi@raspberrypi:/sys/devices/platform/gpio_keys $ ls
disabled_keys      driver           input  modalias  power      switches
disabled_switches  driver_override  keys   of_node   subsystem  uevent
pi@raspberrypi:/sys/devices/platform/gpio_keys $ cd driver
pi@raspberrypi:/sys/devices/platform/gpio_keys/driver $ ls
bind  gpio_keys  module  uevent  unbind
pi@raspberrypi:/sys/devices/platform/gpio_keys/driver $ 
posted @ 2018-07-01 22:14  feiwatson  阅读(569)  评论(0编辑  收藏  举报