All pastes #767233 Raw Edit

florian

public text v1 · immutable
·published 2007-11-09 16:14 UTC
rendered paste body
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 34f40ea..6353238 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -78,6 +78,8 @@ source "drivers/mmc/Kconfig"

 source "drivers/leds/Kconfig"

+source "drivers/buttons/Kconfig"
+
 source "drivers/infiniband/Kconfig"

 source "drivers/edac/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index cfe38ff..1faca07 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -79,6 +79,7 @@ obj-$(CONFIG_CPU_FREQ)                += cpufreq/
 obj-$(CONFIG_CPU_IDLE)         += cpuidle/
 obj-$(CONFIG_MMC)              += mmc/
 obj-$(CONFIG_NEW_LEDS)         += leds/
+obj-$(CONFIG_BUTTONS)          += buttons/
 obj-$(CONFIG_INFINIBAND)       += infiniband/
 obj-$(CONFIG_SGI_SN)           += sn/
 obj-y                          += firmware/
diff --git a/drivers/buttons/Kconfig b/drivers/buttons/Kconfig
new file mode 100644
index 0000000..75764ea
--- /dev/null
+++ b/drivers/buttons/Kconfig
@@ -0,0 +1,27 @@
+menuconfig BUTTONS
+       bool "Buttons Support"
+       depends on HAS_IOMEM
+       help
+         Say Y to enable Linux buttons support.  This allows control of supported
+         LEDs from both userspace and optionally, by kernel events (triggers).
+
+         This is not related to standard keyboard LEDs which are controlled
+         via the input system.
+
+if BUTTONS
+config BUTTONS_CLASS
+       tristate "Buttons Class Support"
+       help
+         This option enables the led sysfs class in /sys/class/leds.  You'll
+         need this to do anything useful with LEDs.  If unsure, say N.
+
+comment "Buttons drivers"
+
+config BUTTONS_GPIO
+       tristate "Buttons Support for GPIO connected buttons"
+       depends on BUTTONS_CLASS && GENERIC_GPIO
+       help
+         This option enables support for the buttonss connected to GPIO
+         outputs. To be useful the particular board must have LEDs
+         and they must be connected to the GPIO lines.
+endif # BUTTONS
diff --git a/drivers/buttons/Makefile b/drivers/buttons/Makefile
new file mode 100644
index 0000000..d606c9f
--- /dev/null
+++ b/drivers/buttons/Makefile
@@ -0,0 +1,6 @@
+# Buttons Core
+obj-$(CONFIG_BUTTONS)                  += btn-core.o
+obj-$(CONFIG_BUTTONS_CLASS)            += btn-class.o
+
+# Button Platform Drivers
+obj-$(CONFIG_BUTTONS_GPIO)             += btns-gpio.o
diff --git a/drivers/buttons/btn-class.c b/drivers/buttons/btn-class.c
new file mode 100644
index 0000000..3c94015
--- /dev/null
+++ b/drivers/buttons/btn-class.c
@@ -0,0 +1,134 @@
+/*
+ * Buttons Class Core
+ *
+ * Copyright (C) 2007 Florian Fainelli <florian@openwrt.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/device.h>
+#include <linux/sysdev.h>
+#include <linux/timer.h>
+#include <linux/err.h>
+#include <linux/ctype.h>
+#include <linux/buttons.h>
+#include "buttons.h"
+
+static struct class *buttons_class;
+
+static ssize_t button_state_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct button_classdev *button_cdev = dev_get_drvdata(dev);
+       ssize_t ret = 0;
+
+       /* no lock needed for this */
+       sprintf(buf, "%u\n", button_cdev->state);
+       ret = strlen(buf) + 1;
+
+       return ret;
+}
+
+static DEVICE_ATTR(state, 0644, button_state_show, NULL);
+
+/**
+ * button_classdev_suspend - suspend an button_classdev.
+ * @button_cdev: the button_classdev to suspend.
+ */
+void button_classdev_suspend(struct button_classdev *button_cdev)
+{
+        button_cdev->flags |= BUTTON_SUSPENDED;
+}
+EXPORT_SYMBOL_GPL(button_classdev_suspend);
+
+/**
+ * button_classdev_resume - resume an button_classdev.
+ * @button_cdev: the button_classdev to resume.
+ */
+void button_classdev_resume(struct button_classdev *button_cdev)
+{
+        button_cdev->flags &= ~BUTTON_SUSPENDED;
+}
+EXPORT_SYMBOL_GPL(button_classdev_resume);
+
+/**
+ * button_classdev_register - register a new object of button_classdev class.
+ * @dev: The device to register.
+ * @button_cdev: the button_classdev structure for this device.
+ */
+int button_classdev_register(struct device *parent, struct button_classdev *button_cdev)
+{
+       int rc;
+
+       button_cdev->dev = device_create(buttons_class, parent, 0, "%s",
+                                           button_cdev->name);
+       if (unlikely(IS_ERR(button_cdev->dev)))
+               return PTR_ERR(button_cdev->dev);
+
+       dev_set_drvdata(button_cdev->dev, button_cdev);
+
+       /* register the attributes */
+       rc = device_create_file(button_cdev->dev, &dev_attr_state);
+       if (rc)
+               goto err_out;
+
+       /* add to the list of buttons */
+       write_lock(&buttons_list_lock);
+       list_add_tail(&button_cdev->node, &buttons_list);
+       write_unlock(&buttons_list_lock);
+
+       printk(KERN_INFO "Registered button device: %s\n",
+                       button_cdev->name);
+
+       return 0;
+
+err_out:
+       device_unregister(button_cdev->dev);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(button_classdev_register);
+
+/**
+ * button_classdev_unregister - unregisters a object of button_properties class.
+ * @button_cdev: the button device to unregister
+ *
+ * Unregisters a previously registered via button_classdev_register object.
+ */
+void button_classdev_unregister(struct button_classdev *button_cdev)
+{
+       device_remove_file(button_cdev->dev, &dev_attr_state);
+
+       device_unregister(button_cdev->dev);
+
+       write_lock(&buttons_list_lock);
+       list_del(&button_cdev->node);
+       write_unlock(&buttons_list_lock);
+}
+EXPORT_SYMBOL_GPL(button_classdev_unregister);
+
+static int __init buttons_init(void)
+{
+       buttons_class = class_create(THIS_MODULE, "buttons");
+       if (IS_ERR(buttons_class))
+               return PTR_ERR(buttons_class);
+       return 0;
+}
+
+static void __exit buttons_exit(void)
+{
+       class_destroy(buttons_class);
+}
+
+subsys_initcall(buttons_init);
+module_exit(buttons_exit);
+
+MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Button Class Interface");
diff --git a/drivers/buttons/btn-core.c b/drivers/buttons/btn-core.c
new file mode 100644
index 0000000..e3759ef
--- /dev/null
+++ b/drivers/buttons/btn-core.c
@@ -0,0 +1,24 @@
+/*
+ * Buttons Class Core
+ *
+ * Copyright (C) 2007 Florian Fainelli <florian@openwrt.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/buttons.h>
+#include "buttons.h"
+
+DEFINE_RWLOCK(buttons_list_lock);
+LIST_HEAD(buttons_list);
+
+EXPORT_SYMBOL_GPL(buttons_list);
+EXPORT_SYMBOL_GPL(buttons_list_lock);
diff --git a/drivers/buttons/btns-gpio.c b/drivers/buttons/btns-gpio.c
new file mode 100644
index 0000000..3792146
--- /dev/null
+++ b/drivers/buttons/btns-gpio.c
@@ -0,0 +1,202 @@
+/*
+ * Buttons driver for GPIOs
+ *
+ * Copyright (C) 2007 Florian Fainelli <florian@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/buttons.h>
+#include <linux/workqueue.h>
+
+#include <asm/gpio.h>
+
+enum button_polarity {
+       NORMAL = 0,
+       REVERSE = 1,
+};
+
+struct gpio_button_data {
+       struct button_classdev cdev;
+       unsigned gpio;
+       struct work_struct work;
+       u8 can_sleep;
+       enum button_polarity polarity;
+};
+
+static void gpio_button_work(struct work_struct *work)
+{
+       struct gpio_button_data *button_dat =
+               container_of(work, struct gpio_button_data, work);
+
+       gpio_set_value_cansleep(button_dat->gpio, button_dat->new_level);
+}
+
+static void gpio_button_get(struct button_classdev *button_cdev,
+        enum button_state value)
+{
+       struct gpio_button_data *button_dat =
+               container_of(button_cdev, struct gpio_button_data, cdev);
+       unsigned int level;
+
+       if (value == BTN_RELEASED)
+               level = 0;
+       else
+               level = 1;
+
+       if (button_dat->polarity == REVERSE)
+               level = !level;
+
+       /* getting GPIOs with I2C/etc requires a preemptible task context */
+       if (button_dat->can_sleep) {
+               if (preempt_count())
+                       schedule_work(&button_dat->work);
+               else
+                       button_cdev->state = gpio_read_value_cansleep(button_dat->gpio);
+        } else
+               button_cdev->state = gpio_read_value(button_dat->gpio);
+}
+
+
+static int __init gpio_button_probe(struct platform_device *pdev)
+{
+       struct gpio_button_platform_data *pdata = pdev->dev.platform_data;
+       struct gpio_button *cur_button;
+       struct gpio_button_data *buttons_data, *button_dat;
+       int i, ret = 0;
+
+       if (!pdata)
+               return -EBUSY;
+
+       buttons_data = kzalloc(sizeof(struct gpio_button_data) * pdata->num_buttons,
+                               GFP_KERNEL);
+       if (!buttons_data)
+               return -ENOMEM;
+
+       for (i = 0; i < pdata->num_buttons; i++) {
+               cur_button = &pdata->buttons[i];
+               button_dat = &buttons_data[i];
+
+               button_dat->cdev.name = cur_button->name;
+               button_dat->cdev.default_trigger = cur_button->default_trigger;
+               button_dat->gpio = cur_button->gpio;
+               button_dat->can_sleep = gpio_cansleep(cur_button->gpio);
+               button_dat->cdev.get_state = gpio_button_get;
+               button_dat->active_low = cur_button->active_low;
+               button_dat->cdev.state = cur_button->polarity ? BTN_PRESSED : BTN_RELEASED;
+
+               ret = gpio_request(button_dat->gpio, button_dat->cdev.name);
+               if (ret < 0)
+                       goto err;
+
+               gpio_direction_output(button_dat->gpio, button_dat->active_low);
+
+               ret = button_classdev_register(&pdev->dev, &button_dat->cdev);
+               if (ret < 0) {
+                       gpio_free(button_dat->gpio);
+                       goto err;
+               }
+
+               INIT_WORK(&button_dat->work, gpio_button_work);
+       }
+
+       platform_set_drvdata(pdev, buttons_data);
+
+       return 0;
+
+err:
+       if (i > 0) {
+               for (i = i - 1; i >= 0; i--) {
+                       button_classdev_unregister(&buttons_data[i].cdev);
+                       gpio_free(buttons_data[i].gpio);
+               }
+       }
+
+       flush_schedubutton_work();
+       kfree(buttons_data);
+
+       return ret;
+}
+
+static int __exit gpio_button_remove(struct platform_device *pdev)
+{
+       int i;
+       struct gpio_button_platform_data *pdata = pdev->dev.platform_data;
+       struct gpio_button_data *buttons_data;
+
+       buttons_data = platform_get_drvdata(pdev);
+
+       for (i = 0; i < pdata->num_buttons; i++) {
+               button_classdev_unregister(&buttons_data[i].cdev);
+               gpio_free(buttons_data[i].gpio);
+       }
+
+       kfree(buttons_data);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int gpio_button_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct gpio_button_platform_data *pdata = pdev->dev.platform_data;
+       struct gpio_button_data *buttons_data;
+       int i;
+
+       buttons_data = platform_get_drvdata(pdev);
+
+       for (i = 0; i < pdata->num_buttons; i++)
+               button_classdev_suspend(&buttons_data[i].cdev);
+
+       return 0;
+}
+
+static int gpio_button_resume(struct platform_device *pdev)
+{
+       struct gpio_button_platform_data *pdata = pdev->dev.platform_data;
+       struct gpio_button_data *buttons_data;
+       int i;
+
+       buttons_data = platform_get_drvdata(pdev);
+
+       for (i = 0; i < pdata->num_buttons; i++)
+               button_classdev_resume(&buttons_data[i].cdev);
+
+       return 0;
+}
+#else
+#define gpio_button_suspend NULL
+#define gpio_button_resume NULL
+#endif
+
+static struct platform_driver gpio_button_driver = {
+       .remove         = __exit_p(gpio_button_remove),
+       .suspend        = gpio_button_suspend,
+       .resume         = gpio_button_resume,
+       .driver         = {
+               .name   = "buttons-gpio",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init gpio_button_init(void)
+{
+       return platform_driver_probe(&gpio_button_driver, gpio_button_probe);
+}
+
+static void __exit gpio_button_exit(void)
+{
+       platform_driver_unregister(&gpio_button_driver);
+}
+
+module_init(gpio_button_init);
+module_exit(gpio_button_exit);
+
+MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
+MODULE_DESCRIPTION("Button LED driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/buttons/buttons.h b/drivers/buttons/buttons.h
new file mode 100644
index 0000000..181dd37
--- /dev/null
+++ b/drivers/buttons/buttons.h
@@ -0,0 +1,20 @@
+/*
+ * Buttons Core
+ *
+ * Copyright (C) 2007 Florian Fainelli <florian@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#ifndef __BUTTONS_H_INCLUDED
+#define __BUTTONS_H_INCLUDED
+
+#include <linux/device.h>
+#include <linux/buttons.h>
+
+extern rwlock_t buttons_list_lock;
+extern struct list_head buttons_list;
+
+#endif /* __BUTTONS_H_INCLUDED */
diff --git a/include/linux/buttons.h b/include/linux/buttons.h
new file mode 100644
index 0000000..d0af4d2
--- /dev/null
+++ b/include/linux/buttons.h
@@ -0,0 +1,56 @@
+/*
+ * Driver model for buttons
+ *
+ * Copyright (C) 2007 Florian Fainelli <florian@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#ifndef __LINUX_BUTTONS_H_INCLUDED
+#define __LINUX_BUTTONS_H_INCLUDED
+
+#include <linux/list.h>
+#include <linux/spinlock.h>
+
+struct device;
+/*
+ * Button Core
+ */
+
+enum button_state {
+       BTN_RELEASED    = 0,
+       BTN_PRESSED     = 1,
+};
+
+struct button_classdev {
+       const char              *name;
+       bool                     state;
+       int                      flags;
+#define BUTTON_SUSPENDED           (1 << 0)
+       void            (*get_state)(struct button_classdev *button_cdev,
+                                                 enum button_state value);
+       struct device           *dev;
+       struct list_head         node;                  /* Button Device list */
+};
+
+extern int button_classdev_register(struct device *parent,
+                                struct button_classdev *button_cdev);
+extern void button_classdev_unregister(struct button_classdev *button_cdev);
+extern void button_classdev_suspend(struct button_classdev *button_cdev);
+extern void button_classdev_resume(struct button_classdev *button_cdev);
+
+/* For the buttons-gpio driver */
+struct gpio_button {
+       const char *name;
+       unsigned        gpio;
+       u8              polarity;
+};
+
+struct gpio_button_platform_data {
+       int             num_buttons;
+       struct gpio_button *buttons;
+};
+
+#endif         /* __LINUX_BUTTONS_H_INCLUDED */