解析gpio-keys(kernel-4.7)

kernel中的gpio-keys,在input子系统一个重要的功用,可以对gpio口进行input 按键操作,虽然简单,却是驱动中常做的配置,需要做详细记录。
platform设备注册:

static int __init gpio_keys_init(void)
{
    return platform_driver_register(&gpio_keys_device_driver);
}

注册结构体gpio_keys_device_driver

    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 = of_match_ptr(gpio_keys_of_match),
        }
    };  

其中注册了driver,driver在platform_driver中为device_driver结构,查看platform设备驱动 :

    struct platform_driver {
        int (*probe)(struct platform_device *); // 关联到gpio_keys_probe
        int (*remove)(struct platform_device *); // 关联到gpio_keys_remove
        void (*shutdown)(struct platform_device *);
        int (*suspend)(struct platform_device *, pm_message_t state);
        int (*resume)(struct platform_device *);
        struct device_driver driver;
        const struct platform_device_id *id_table;
        bool prevent_deferred_probe;
    }; 

查看device_driver :

    struct device_driver {
        const char      *name;      // 驱动名"gpio-keys"
        struct bus_type     *bus;
        struct module       *owner;
        const char      *mod_name;  /* used for built-in modules */

        bool suppress_bind_attrs;   /* disables bind/unbind via sysfs */
        enum probe_type probe_type;

        const struct of_device_id   *of_match_table;  //of_match_ptr(gpio_keys_of_match)
        const struct acpi_device_id *acpi_match_table;

        int (*probe) (struct device *dev);
        int (*remove) (struct device *dev);
        void (*shutdown) (struct device *dev);
        int (*suspend) (struct device *dev, pm_message_t state);
        int (*resume) (struct device *dev);

        const struct attribute_group **groups;
        const struct dev_pm_ops *pm;    //  &gpio_keys_pm_ops
        struct driver_private *p;
    };

device_driver中的pm涉及到了dev_pm_ops结构,并未结构体赋值gpio_keys_pm_ops
gpio_keys_pm_ops的定义在:

static SIMPLE_DEV_PM_OPS(gpio_keys_pm_ops, gpio_keys_suspend, gpio_keys_resume);  

查看dev_pm_ops结构体和 SIMPLE_DEV_PM_OPS宏的定义:


/**
 * struct dev_pm_ops - device PM callbacks
 */
struct dev_pm_ops {
    int (*prepare)(struct device *dev);
    void (*complete)(struct device *dev);
    int (*suspend)(struct device *dev);
    int (*resume)(struct device *dev);
    int (*freeze)(struct device *dev);
    int (*thaw)(struct device *dev);
    int (*poweroff)(struct device *dev);
    int (*restore)(struct device *dev);
    int (*suspend_late)(struct device *dev);
    int (*resume_early)(struct device *dev);
    int (*freeze_late)(struct device *dev);
    int (*thaw_early)(struct device *dev);
    int (*poweroff_late)(struct device *dev);
    int (*restore_early)(struct device *dev);
    int (*suspend_noirq)(struct device *dev);
    int (*resume_noirq)(struct device *dev);
    int (*freeze_noirq)(struct device *dev);
    int (*thaw_noirq)(struct device *dev);
    int (*poweroff_noirq)(struct device *dev);
    int (*restore_noirq)(struct device *dev);
    int (*runtime_suspend)(struct device *dev);
    int (*runtime_resume)(struct device *dev);
    int (*runtime_idle)(struct device *dev);
};



/*
 * Use this if you want to use the same suspend and resume callbacks for suspend
 * to RAM and hibernation.
 */
#define SIMPLE_DEV_PM_OPS(name, suspend_fn, resume_fn) \
const struct dev_pm_ops name = { \
    SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \
}

可以看到SIMPLE_DEV_PM_OPS宏的作用就是装载dev_pm_ops结构体,关联gpio_keys_suspend, gpio_keys_resume 函数 。

继续查看of_match_ptr,其定义为:

#define of_match_ptr(_ptr)  (_ptr) 

关联了gpio_keys_of_match

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

其中 of_device_id 的定义:

struct of_device_id {
    char    name[32];
    char    type[32];
    char    compatible[128];
    const void *data;
};

MODULE_DEVICE_TABLE 定义:

#define MODULE_DEVICE_TABLE(type, name)                 \
extern const typeof(name) __mod_##type##__##name##_device_table     \
  __attribute__ ((unused, alias(__stringify(name))))

gpio_keys_probe 函数用来识别驱动设备,一下为具体操作:

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 gpio_keys_drvdata *ddata;
    struct input_dev *input;
    size_t size;
    int i, error;
    int wakeup = 0;

    if (!pdata) {   /*这里从dts 设备树中获取gpio的数据*/
        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;
    }

    input = devm_input_allocate_device(dev); //分配input设备
    if (!input) {
        dev_err(dev, "failed to allocate input device\n");
        return -ENOMEM;
    }
    /* 将私有数组加载到input设备中 */
    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 = &pdev->dev;
    input->open = gpio_keys_open;   // input打开操作
    input->close = gpio_keys_close;  // input关闭操作

    input->id.bustype = BUS_HOST;
    input->id.vendor = 0x0001;
    input->id.product = 0x0001;
    input->id.version = 0x0100;

    /* 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];
        struct gpio_button_data *bdata = &ddata->data[i];

        error = gpio_keys_setup_key(pdev, input, bdata, button);
        if (error)
            return error;

        if (button->wakeup)
            wakeup = 1;
    }

    error = sysfs_create_group(&pdev->dev.kobj, &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);
        goto err_remove_group;
    }

    device_init_wakeup(&pdev->dev, wakeup);

    return 0;

err_remove_group:
    sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group);
    return error;
}

其中定义的重要结构体gpio_keys_platform_data , 这个结构用来定义gpio的按键功能:

struct gpio_keys_platform_data {
    struct gpio_keys_button *buttons;
    int nbuttons;
    unsigned int poll_interval;
    unsigned int rep:1;
    int (*enable)(struct device *dev);
    void (*disable)(struct device *dev);
    const char *name;
};

gpio_keys_drvdata 结构体,这个用来关联input系统:

struct gpio_keys_drvdata {
    const struct gpio_keys_platform_data *pdata;
    struct input_dev *input;
    struct mutex disable_lock;
    struct gpio_button_data data[0];
};

gpio_keys_get_devtree_pdata 函数获取设备树中的数据的具体操作:

/*
 * Translate OpenFirmware node properties into platform_data
 */
static struct gpio_keys_platform_data *
gpio_keys_get_devtree_pdata(struct device *dev)
{
    struct device_node *node, *pp;
    struct gpio_keys_platform_data *pdata;
    struct gpio_keys_button *button;
    int error;
    int nbuttons;
    int i;

    node = dev->of_node;
    if (!node)
        return ERR_PTR(-ENODEV);
     /* Find the next available child node*/ 
     /* 找到下一个可用的子节点*/
    nbuttons = of_get_available_child_count(node);
    if (nbuttons == 0)
        return ERR_PTR(-ENODEV);

    pdata = devm_kzalloc(dev,
                 sizeof(*pdata) + nbuttons * sizeof(*button),
                 GFP_KERNEL);
    if (!pdata)
        return ERR_PTR(-ENOMEM);

    pdata->buttons = (struct gpio_keys_button *)(pdata + 1);
    pdata->nbuttons = nbuttons;

    pdata->rep = !!of_get_property(node, "autorepeat", NULL);

    of_property_read_string(node, "label", &pdata->name);

    i = 0;
    for_each_available_child_of_node(node, pp) {
        enum of_gpio_flags flags;

        button = &pdata->buttons[i++];

        button->gpio = of_get_gpio_flags(pp, 0, &flags);
        if (button->gpio < 0) {
            error = button->gpio;
            if (error != -ENOENT) {
                if (error != -EPROBE_DEFER)
                    dev_err(dev,
                        "Failed to get gpio flags, error: %d\n",
                        error);
                return ERR_PTR(error);
            }
        } else {
            button->active_low = flags & OF_GPIO_ACTIVE_LOW;
        }

        button->irq = irq_of_parse_and_map(pp, 0);

        if (!gpio_is_valid(button->gpio) && !button->irq) {
            dev_err(dev, "Found button without gpios or irqs\n");
            return ERR_PTR(-EINVAL);
        }

        if (of_property_read_u32(pp, "linux,code", &button->code)) {
            dev_err(dev, "Button without keycode: 0x%x\n",
                button->gpio);
            return ERR_PTR(-EINVAL);
        }

        button->desc = of_get_property(pp, "label", NULL);

        if (of_property_read_u32(pp, "linux,input-type", &button->type))
            button->type = EV_KEY;

        button->wakeup = of_property_read_bool(pp, "wakeup-source") ||
                 /* legacy name */
                 of_property_read_bool(pp, "gpio-key,wakeup");

        button->can_disable = !!of_get_property(pp, "linux,can-disable", NULL);

        if (of_property_read_u32(pp, "debounce-interval",
                     &button->debounce_interval))
            button->debounce_interval = 5;
    }

    if (pdata->nbuttons == 0)
        return ERR_PTR(-EINVAL);

    return pdata;
} 

gpio_keys_get_devtree_pdata 函数使用了gpio_keys_button 的结构体定义button:

/**
 * struct gpio_keys_button - configuration parameters
 */
struct gpio_keys_button {
    unsigned int code;
    int gpio;
    int active_low;
    const char *desc;
    unsigned int type;
    int wakeup;
    int debounce_interval;
    bool can_disable;
    int value;
    unsigned int irq;
    struct gpio_desc *gpiod;
};

gpio_keys_button 中重要的结构体gpio_desc,定义如下:


struct gpio_desc {
    struct gpio_device  *gdev;
    unsigned long       flags;
/* flag symbols are bit numbers */
#define FLAG_REQUESTED  0
#define FLAG_IS_OUT 1
#define FLAG_EXPORT 2   /* protected by sysfs_lock */
#define FLAG_SYSFS  3   /* exported via /sys/class/gpio/control */
#define FLAG_ACTIVE_LOW 6   /* value has active low */
#define FLAG_OPEN_DRAIN 7   /* Gpio is open drain type */
#define FLAG_OPEN_SOURCE 8  /* Gpio is open source type */
#define FLAG_USED_AS_IRQ 9  /* GPIO is connected to an IRQ */
#define FLAG_IS_HOGGED  11  /* GPIO is hogged */

    /* Connection label */
    const char      *label;
    /* Name of the GPIO */
    const char      *name;
};

你可能感兴趣的:(linux设备驱动)