@@ -31,6 +31,8 @@ struct gpio_button_data {
struct input_dev *input;
struct timer_list timer;
struct work_struct work;
+ struct device *dev; /* device used to export button to sysfs */
+ bool disable; /* is this gpio button disabled */
};
struct gpio_keys_drvdata {
@@ -38,6 +40,159 @@ struct gpio_keys_drvdata {
struct gpio_button_data data[0];
};
+/*
+ * gpio-keys sysfs interface
+ *
+ * Following interface is export to userspace through
+ * sysfs. This interface can be used to enable/disable
+ * single GPIO lines from generating events.
+ *
+ * /sys/class/input/gpio-keys/input/input0/gpio-key.N/
+ * /code ... input event code (ro)
+ * /type ... input event type (ro)
+ * /desc ... description of the button (ro)
+ * /disable ... enable/disable gpio line from
+ * generating events (rw)
+ *
+ * Userspace program can enumerate these keys and based
+ * on {code,type,desc} tuple disable or enable the line
+ * depending on the system state.
+ */
+
+static ssize_t gpio_keys_code_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const struct gpio_button_data *bdata = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", bdata->button->code);
+}
+
+static DEVICE_ATTR(code, S_IRUGO, gpio_keys_code_show, NULL);
+
+static ssize_t gpio_keys_type_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const struct gpio_button_data *bdata = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", bdata->button->type);
+}
+
+static DEVICE_ATTR(type, S_IRUGO, gpio_keys_type_show, NULL);
+
+static ssize_t gpio_keys_desc_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const struct gpio_button_data *bdata = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", bdata->button->desc);
+}
+
+static DEVICE_ATTR(desc, S_IRUGO, gpio_keys_desc_show, NULL);
+
+static ssize_t gpio_keys_disable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const struct gpio_button_data *bdata = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", (int)bdata->disable);
+}
+
+static ssize_t gpio_keys_disable_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct gpio_button_data *bdata = dev_get_drvdata(dev);
+ int val;
+
+ if (sscanf(buf, "%d", &val) != 1)
+ return -EINVAL;
+
+ val = !!val;
+ if (bdata->disable == val)
+ return size;
+
+ if (val) {
+ disable_irq(gpio_to_irq(bdata->button->gpio));
+ } else {
+ enable_irq(gpio_to_irq(bdata->button->gpio));
+ }
+
+ bdata->disable = val;
+ return size;
+}
+
+static DEVICE_ATTR(disable, S_IWUSR | S_IRUGO,
+ gpio_keys_disable_show, gpio_keys_disable_store);
+
+static struct attribute *gpio_keys_attrs[] = {
+ &dev_attr_code.attr,
+ &dev_attr_type.attr,
+ &dev_attr_desc.attr,
+ &dev_attr_disable.attr,
+ NULL,
+};
+
+static const struct attribute_group gpio_keys_attr_group = {
+ .attrs = gpio_keys_attrs,
+};
+
+static void gpio_keys_unexport(struct platform_device *pdev)
+{
+ struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
+ struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < pdata->nbuttons; i++) {
+ struct gpio_button_data *bdata = &ddata->data[i];
+
+ if (bdata->dev != NULL) {
+ sysfs_remove_group(&bdata->dev->kobj,
+ &gpio_keys_attr_group);
+ device_unregister(bdata->dev);
+ bdata->dev = NULL;
+ }
+ }
+}
+
+static int gpio_keys_export(struct platform_device *pdev)
+{
+ struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
+ struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev);
+ struct input_dev *input = ddata->input;
+ int i, error = 0;
+
+ for (i = 0; i < pdata->nbuttons; i++) {
+ struct gpio_button_data *bdata = &ddata->data[i];
+ struct device *dev;
+
+ /*
+ * We create one "device" per gpio line that is used
+ * as input gpio key device.
+ */
+ dev = device_create(input->dev.class, &input->dev,
+ MKDEV(0, 0), bdata, "gpio-key.%d", i);
+ if (dev == NULL) {
+ error = -ENOMEM;
+ break;
+ }
+
+ error = sysfs_create_group(&dev->kobj, &gpio_keys_attr_group);
+ if (error != 0) {
+ device_unregister(dev);
+ break;
+ }
+
+ bdata->disable = false;
+ bdata->dev = dev;
+ }
+
+ if (error != 0) {
+ /* something failed, clean up all entries */
+ gpio_keys_unexport(pdev);
+ }
+
+ return error;
+}
+
static void gpio_keys_report_event(struct work_struct *work)
{
struct gpio_button_data *bdata =
@@ -170,6 +325,12 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
goto fail2;
}
+ error = gpio_keys_export(pdev);
+ if (error) {
+ pr_warning("gpio-keys: Unable to export gpio-keys to sysfs, "
+ " error %d", error);
+ }
+
device_init_wakeup(&pdev->dev, wakeup);
return 0;
@@ -199,6 +360,7 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev)
int i;
device_init_wakeup(&pdev->dev, 0);
+ gpio_keys_unexport(pdev);
for (i = 0; i < pdata->nbuttons; i++) {
int irq = gpio_to_irq(pdata->buttons[i].gpio);