gpio - gpio framework, initial commit
authorAlex Hornung <ahornung@gmail.com>
Sun, 4 Oct 2009 12:09:33 +0000 (13:09 +0100)
committerAlex Hornung <ahornung@gmail.com>
Sun, 4 Oct 2009 13:01:59 +0000 (14:01 +0100)
* Initial commit of the new gpio framework,including a sample gpio
  consumer driver, gpio_led.
  While the framework is not directly based on OpenBSD's, some
  structures and other similarities have been taken over to ease porting
  of gpio drivers from OpenBSD.

Partially-Obtained-from: OpenBSD

sys/dev/misc/gpio/gpio.c [new file with mode: 0644]
sys/dev/misc/gpio/gpio.h [new file with mode: 0644]
sys/dev/misc/gpio/gpio_led.c [new file with mode: 0644]

diff --git a/sys/dev/misc/gpio/gpio.c b/sys/dev/misc/gpio/gpio.c
new file mode 100644 (file)
index 0000000..bb90915
--- /dev/null
@@ -0,0 +1,603 @@
+/*
+ * Copyright (c) 2009 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Alex Hornung <ahornung@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright (c) 2008 Marc Balmer <mbalmer@openbsd.org>
+ * Copyright (c) 2004, 2006 Alexander Yurchenko <grange@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ * XXX: consumer_detach stuff.
+ * XXX: userland stuff.
+ */
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/limits.h>
+#include <sys/thread.h>
+#include <sys/thread2.h>
+#include <sys/malloc.h>
+#include <sys/ctype.h>
+#include <sys/sbuf.h>
+#include <sys/queue.h>
+#include <sys/uio.h>
+#include <sys/lock.h>
+#include <sys/ioccom.h>
+#include <dev/misc/gpio/gpio.h>
+#include <sys/devfs.h>
+
+struct gpio_driver {
+       char    *name;
+       struct devfs_bitmap     unit_bitmap;
+       LIST_ENTRY(gpio_driver) link;
+};
+
+static LIST_HEAD(, gpio_consumer) gpio_conslist = LIST_HEAD_INITIALIZER(&gpio_conslist);
+static LIST_HEAD(, gpio_driver) gpio_driverlist = LIST_HEAD_INITIALIZER(&gpio_driverlist);
+DEVFS_DECLARE_CLONE_BITMAP(gpio);
+static struct lock gpio_lock;
+
+void
+gpio_consumer_register(struct gpio_consumer *gcp)
+{
+       lockmgr(&gpio_lock, LK_EXCLUSIVE);
+       LIST_INSERT_HEAD(&gpio_conslist, gcp, link);
+       lockmgr(&gpio_lock, LK_RELEASE);
+}
+
+void
+gpio_consumer_unregister(struct gpio_consumer *gcp)
+{
+       lockmgr(&gpio_lock, LK_EXCLUSIVE);
+       LIST_REMOVE(gcp, link);
+       lockmgr(&gpio_lock, LK_RELEASE);
+}
+
+int
+gpio_consumer_attach(const char *consumer, void *arg, struct gpio *gp,
+           int pin, u_int32_t mask)
+{
+       struct gpio_consumer *gcp;
+       int error = -1;
+       int locked = 0;
+
+       /* Check if it is locked already. if not, we acquire the lock */
+       if (!(lockstatus(&gpio_lock, curthread)) == LK_EXCLUSIVE) {
+               lockmgr(&gpio_lock, LK_EXCLUSIVE);
+               locked = 1;
+       }
+
+       LIST_FOREACH(gcp, &gpio_conslist, link) {
+               if (strcmp(gcp->consumer_name, consumer) != 0)
+                       continue;
+
+               if (gcp->consumer_attach)
+                       error = gcp->consumer_attach(gp, arg, pin, mask);
+               if (error) {
+                       kprintf("gpio: Attach of consumer %s to gpio %s%d pin %d failed "
+                           "(consumer error %d)\n", consumer, gp->driver_name,
+                           gp->driver_unit, pin, error);
+                       goto end;
+               }
+
+               kprintf("gpio: Attached consumer %s to gpio %s%d pin %d\n",
+                   consumer, gp->driver_name, gp->driver_unit, pin);
+               goto end;
+       }
+
+       kprintf("gpio: Attach of consumer %s to gpio %s%d pin %d failed "
+           "(unknown consumer)\n", consumer, gp->driver_name, gp->driver_unit, pin);
+
+end:
+       /* If we acquired the lock, we also get rid of it */
+       if (locked)
+               lockmgr(&gpio_lock, LK_RELEASE);
+       return error;
+}
+
+int
+gpio_consumer_detach(const char *consumer, struct gpio *gp,
+               int pin)
+{
+       struct gpio_consumer *gcp;
+       int error = -1;
+       int locked = 0;
+
+       /* Check if it is locked already. if not, we acquire the lock */
+       if (!(lockstatus(&gpio_lock, curthread)) == LK_EXCLUSIVE) {
+               lockmgr(&gpio_lock, LK_EXCLUSIVE);
+               locked = 1;
+       }
+
+       LIST_FOREACH(gcp, &gpio_conslist, link) {
+               if (strcmp(gcp->consumer_name, consumer) != 0)
+                       continue;
+
+               if (gcp->consumer_detach)
+                       error = gcp->consumer_detach(gp, NULL, pin);
+               if (error) {
+                       kprintf("gpio: Detach of consumer %s from gpio %s%d pin %d failed "
+                           "(consumer error %d)\n", consumer, gp->driver_name,
+                           gp->driver_unit, pin, error);
+                       goto end;
+               }
+
+               kprintf("gpio: Detached consumer %s from gpio %s%d pin %d\n",
+                   consumer, gp->driver_name, gp->driver_unit, pin);
+               goto end;
+       }
+
+       kprintf("gpio: Detach of consumer %s from gpio %s%d pin %d failed "
+           "(unknown consumer)\n", consumer, gp->driver_name, gp->driver_unit, pin);
+
+end:
+       /* If we acquired the lock, we also get rid of it */
+       if (locked)
+               lockmgr(&gpio_lock, LK_RELEASE);
+       return error;
+}
+
+struct gpio_mapping *
+gpio_map(struct gpio *gp, int *map, int offset, u_int32_t mask)
+{
+       struct gpio_mapping *gmp;
+       int npins, pin, i;
+       int locked = 0;
+
+       npins = gpio_npins(mask);
+       if (npins > gp->npins)
+               return NULL;
+       if (npins == 0)
+               return NULL;
+
+       /* Check if it is locked already. if not, we acquire the lock */
+       if (!(lockstatus(&gpio_lock, curthread)) == LK_EXCLUSIVE) {
+               lockmgr(&gpio_lock, LK_EXCLUSIVE);
+               locked = 1;
+       }
+
+       gmp = kmalloc(sizeof(struct gpio_mapping), M_TEMP, M_WAITOK);
+       gmp->gp = gp;
+       if (map) {
+               gmp->map = map;
+               gmp->map_alloced = 0;
+       } else {
+               gmp->map = kmalloc(sizeof(int) * npins, M_TEMP, M_WAITOK);
+               gmp->map_alloced = 1;
+       }
+
+       for (npins = 0, i = 0; i < 32; i++)
+               if (mask & (1 << i)) {
+                       pin = offset + i;
+                       if (pin < 0 || pin >= gp->npins ||
+                               gp->pins[pin].pin_mapped || gp->pins[pin].pin_opened) {
+                               if (map == NULL)
+                                       kfree(gmp->map, M_TEMP);
+                               kfree(gmp, M_TEMP);
+                               /* If we acquired the lock, we also get rid of it */
+                               if (locked)
+                                       lockmgr(&gpio_lock, LK_RELEASE);
+                               return NULL;
+                       }
+                       gp->pins[pin].pin_mapped = 1;
+                       gmp->map[npins++] = pin;
+               }
+       gmp->size = npins;
+
+       /* If we acquired the lock, we also get rid of it */
+       if (locked)
+               lockmgr(&gpio_lock, LK_RELEASE);
+
+       return gmp;
+}
+
+void
+gpio_unmap(struct gpio_mapping *gmp)
+{
+       int pin, i;
+       int locked = 0;
+
+       /* Check if it is locked already. if not, we acquire the lock */
+       if (!(lockstatus(&gpio_lock, curthread)) == LK_EXCLUSIVE) {
+               lockmgr(&gpio_lock, LK_EXCLUSIVE);
+               locked = 1;
+       }
+
+       for (i = 0; i < gmp->size; i++) {
+               pin = gmp->map[i];
+               gmp->gp->pins[pin].pin_mapped = 0;
+       }
+
+       if (gmp->map_alloced)
+               kfree(gmp->map, M_TEMP);
+       kfree(gmp, M_TEMP);
+
+       /* If we acquired the lock, we also get rid of it */
+       if (locked)
+               lockmgr(&gpio_lock, LK_RELEASE);
+}
+
+int
+gpio_npins(u_int32_t mask)
+{
+       int npins, i;
+
+       for (npins = 0, i = 0; i < 32; i++)
+               if (mask & (1 << i))
+                       npins++;
+
+       return (npins);
+}
+
+int
+gpio_pin_read(struct gpio *gp, struct gpio_mapping *map, int pin)
+{
+       return gp->pin_read(gp->arg, map->map[pin]);
+}
+
+void
+gpio_pin_write(struct gpio *gp, struct gpio_mapping *map, int pin, int data)
+{
+       return gp->pin_write(gp->arg, map->map[pin], data);
+}
+
+void
+gpio_pin_ctl(struct gpio *gp, struct gpio_mapping *map, int pin, int flags)
+{
+       return gp->pin_ctl(gp->arg, map->map[pin], flags);
+}
+
+int
+gpio_pin_caps(struct gpio *gp, struct gpio_mapping *map, int pin)
+{
+       return (gp->pins[map->map[pin]].pin_caps);
+}
+
+static int
+gpio_open(struct dev_open_args *ap)
+{
+       struct gpio     *gp;
+       gpio_pin_t      *pin;
+       cdev_t  dev;
+
+       dev = ap->a_head.a_dev;
+       gp = dev->si_drv1;
+       pin = dev->si_drv2;
+
+       if (pin->pin_opened || pin->pin_mapped)
+               return EBUSY;
+
+       pin->pin_opened = 1;
+
+       return 0;
+}
+
+static int
+gpio_close(struct dev_close_args *ap)
+{
+       struct gpio     *gp;
+       gpio_pin_t      *pin;
+       cdev_t  dev;
+
+       dev = ap->a_head.a_dev;
+       gp = dev->si_drv1;
+       pin = dev->si_drv2;
+
+       if (pin->pin_opened)
+               pin->pin_opened = 0;
+
+       return 0;
+}
+
+static int
+gpio_write(struct dev_write_args *ap)
+{
+       struct gpio     *gp;
+       gpio_pin_t      *pin;
+       cdev_t          dev;
+       int             error;
+       int             data = 0;
+
+       dev = ap->a_head.a_dev;
+       gp = dev->si_drv1;
+       pin = dev->si_drv2;
+
+       if (ap->a_uio->uio_resid > sizeof(int))
+               return EINVAL;
+
+       error = uiomove((void *)&data, ap->a_uio->uio_resid, ap->a_uio);
+       if (error)
+               return error;
+
+       if (data != GPIO_PIN_LOW && data != GPIO_PIN_HIGH)
+               return EINVAL;
+
+       gp->pin_write(gp->arg, pin->pin_num, data);
+       pin->pin_state = data;
+
+       return 0;
+}
+
+static int
+gpio_read(struct dev_read_args *ap)
+{
+       struct gpio     *gp;
+       gpio_pin_t      *pin;
+       cdev_t          dev;
+       int             error;
+       int             data = 0;
+
+       dev = ap->a_head.a_dev;
+       gp = dev->si_drv1;
+       pin = dev->si_drv2;
+
+       if (ap->a_uio->uio_resid < sizeof(char))
+               return EINVAL;
+
+       data = gp->pin_read(gp->arg, pin->pin_num);
+
+       error = uiomove((void *)&data,
+           (ap->a_uio->uio_resid > sizeof(int))?(sizeof(int)):(ap->a_uio->uio_resid),
+               ap->a_uio);
+
+       return error;
+}
+
+static int
+gpio_ioctl(struct dev_ioctl_args *ap)
+{
+       struct gpio_pin_set_args *gpsa;
+       struct gpio     *gp;
+       gpio_pin_t      *pin;
+       cdev_t          dev;
+       int             error = 0;
+
+       dev = ap->a_head.a_dev;
+       gp = dev->si_drv1;
+       pin = dev->si_drv2;
+
+       switch(ap->a_cmd) {
+       case GPIOPINSET:
+               gpsa = (struct gpio_pin_set_args *)ap->a_data;
+               if (pin->pin_opened || pin->pin_mapped)
+                       return EBUSY;
+
+               gpsa->caps = pin->pin_caps;
+               gpsa->flags = pin->pin_flags;
+
+               if ((gpsa->flags & pin->pin_caps) != gpsa->flags)
+                       return ENODEV;
+
+               if (gpsa->flags > 0) {
+                       gp->pin_ctl(gp->arg, pin->pin_num, gpsa->flags);
+                       pin->pin_flags = gpsa->flags | GPIO_PIN_SET;
+               }
+               break;
+
+       case GPIOPINUNSET:
+               gpsa = (struct gpio_pin_set_args *)ap->a_data;
+               error = EINVAL;
+               break;
+
+       default:
+               return EINVAL;
+       }
+       return 0;
+}
+
+static int
+gpio_master_ioctl(struct dev_ioctl_args *ap)
+{
+       struct gpio_pin_set_args *gpsa;
+       struct gpio_info        *gpi;
+       struct gpio_attach_args *gpaa;
+       struct gpio     *gp;
+       cdev_t          dev;
+       gpio_pin_t      *pin;
+       int             error = 0;
+
+       dev = ap->a_head.a_dev;
+       gp = dev->si_drv1;
+
+       switch(ap->a_cmd) {
+       case GPIOINFO:
+               gpi = (struct gpio_info *)ap->a_data;
+               gpi->npins = gp->npins;
+               if (gpi->pins != NULL) {
+                       error = copyout(gp->pins, gpi->pins,
+                           sizeof(struct gpio_pin)*gp->npins);
+               }
+               break;
+
+       case GPIOATTACH:
+               gpaa = (struct gpio_attach_args *)ap->a_data;
+               error = gpio_consumer_attach(gpaa->consumer_name,
+                   (gpaa->arg_type == GPIO_TYPE_INT)?
+                   ((void *)gpaa->consumer_arg.lint):
+                   (gpaa->consumer_arg.string),
+                   gp, gpaa->pin_offset, gpaa->pin_mask);
+               break;
+
+       case GPIODETACH:
+               gpaa = (struct gpio_attach_args *)ap->a_data;
+               error = gpio_consumer_detach(gpaa->consumer_name, gp,
+                   gpaa->pin_offset);
+               break;
+
+       case GPIOPINSET:
+               gpsa = (struct gpio_pin_set_args *)ap->a_data;
+               if (gpsa->pin < 0 || gpsa->pin >= gp->npins)
+                       return EINVAL;
+
+               pin = &gp->pins[gpsa->pin];
+
+               if (pin->pin_opened || pin->pin_mapped)
+                       return EBUSY;
+
+               gpsa->caps = pin->pin_caps;
+               gpsa->flags = pin->pin_flags;
+
+               if ((gpsa->flags & pin->pin_caps) != gpsa->flags)
+                       return ENODEV;
+
+               if (gpsa->flags > 0) {
+                       gp->pin_ctl(gp->arg, gpsa->pin, gpsa->flags);
+                       pin->pin_flags = gpsa->flags | GPIO_PIN_SET;
+               }
+               break;
+
+       case GPIOPINUNSET:
+               gpsa = (struct gpio_pin_set_args *)ap->a_data;
+               error = EINVAL;
+               break;
+
+       default:
+               return EINVAL;
+       }
+
+       return error;
+}
+
+static struct dev_ops gpio_ops = {
+       { "gpio", 0, 0 },
+       .d_open  =      gpio_open,
+       .d_close =      gpio_close,
+       .d_write =      gpio_write,
+       .d_read  =      gpio_read,
+       .d_ioctl =      gpio_ioctl,
+};
+
+static struct dev_ops gpio_master_ops = {
+       { "gpio", 0, 0 },
+       .d_ioctl =      gpio_master_ioctl,
+};
+
+void
+gpio_register(struct gpio *gp)
+{
+       struct gpio_driver *gpd;
+       int i, unit, master_unit = -1;
+
+       KKASSERT(gp->npins > 0);
+       KKASSERT(gp->pins);
+
+       lockmgr(&gpio_lock, LK_EXCLUSIVE);
+       LIST_FOREACH(gpd, &gpio_driverlist, link) {
+               if (strcmp(gpd->name, gp->driver_name) != 0)
+                       continue;
+
+               master_unit = devfs_clone_bitmap_get(&gpd->unit_bitmap, 0);
+               break;
+       }
+       if (master_unit == -1) {
+               gpd = kmalloc(sizeof(struct gpio_driver),
+                   M_TEMP, M_WAITOK | M_ZERO);
+               gpd->name = kstrdup(gp->driver_name, M_TEMP);
+               devfs_clone_bitmap_init(&gpd->unit_bitmap);
+               master_unit = devfs_clone_bitmap_get(&gpd->unit_bitmap, 0);
+               LIST_INSERT_HEAD(&gpio_driverlist, gpd, link);
+       }
+       lockmgr(&gpio_lock, LK_RELEASE);
+
+       gp->driver_unit = master_unit;
+       kprintf("gpio: GPIO driver %s%d registered, npins = %d\n",
+           gp->driver_name, master_unit, gp->npins);
+
+       unit = devfs_clone_bitmap_get(&DEVFS_CLONE_BITMAP(gpio), 0);
+       gp->master_dev = make_dev(&gpio_master_ops, unit, UID_ROOT, GID_WHEEL, 0600,
+           "gpio/%s%d/master", gp->driver_name, master_unit);
+       gp->master_dev->si_drv1 = gp;
+
+       for (i = 0; i < gp->npins; i++) {
+               unit = devfs_clone_bitmap_get(&DEVFS_CLONE_BITMAP(gpio), 0);
+               gp->pins[i].dev = make_dev(&gpio_ops, unit, UID_ROOT, GID_WHEEL, 0600,
+                   "gpio/%s%d/%d", gp->driver_name, master_unit, gp->pins[i].pin_num);
+               gp->pins[i].dev->si_drv1 = gp;
+               gp->pins[i].dev->si_drv2 = &gp->pins[i];
+       }
+}
+
+void
+gpio_unregister(struct gpio *gp)
+{
+       struct gpio_driver *gpd;
+       int i;
+
+       KKASSERT(gp->npins > 0);
+       KKASSERT(gp->pins);
+
+       for (i = 0; i < gp->npins; i++) {
+               devfs_clone_bitmap_get(&DEVFS_CLONE_BITMAP(gpio),
+                   minor(gp->pins[i].dev));
+               destroy_dev(gp->pins[i].dev);
+       }
+
+       destroy_dev(gp->master_dev);
+
+       lockmgr(&gpio_lock, LK_EXCLUSIVE);
+       LIST_FOREACH(gpd, &gpio_driverlist, link) {
+               if (strcmp(gpd->name, gp->driver_name) != 0)
+                       continue;
+
+               devfs_clone_bitmap_put(&gpd->unit_bitmap, gp->driver_unit);
+               LIST_REMOVE(gpd, link);
+               break;
+       }
+       lockmgr(&gpio_lock, LK_RELEASE);
+
+       kprintf("gpio: GPIO driver %s%d unregistered\n",
+           gp->driver_name, gp->driver_unit);
+}
+
+static void
+gpio_drvinit(void *unused)
+{
+       lockinit(&gpio_lock, "gpio_lock", 0, 0);
+       devfs_clone_bitmap_init(&DEVFS_CLONE_BITMAP(gpio));
+}
+
+SYSINIT(gpio, SI_SUB_PRE_DRIVERS, SI_ORDER_FIRST, gpio_drvinit, NULL);
diff --git a/sys/dev/misc/gpio/gpio.h b/sys/dev/misc/gpio/gpio.h
new file mode 100644 (file)
index 0000000..b211a8f
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2009 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Alex Hornung <ahornung@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright (c) 2008 Marc Balmer <mbalmer@openbsd.org>
+ * Copyright (c) 2004, 2006 Alexander Yurchenko <grange@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* GPIO pin states */
+#define GPIO_PIN_LOW           0x00    /* low level (logical 0) */
+#define GPIO_PIN_HIGH          0x01    /* high level (logical 1) */
+
+/* Max name length of a pin */
+#define GPIOPINMAXNAME         64
+
+/* GPIO pin configuration flags */
+#define GPIO_PIN_INPUT         0x0001  /* input direction */
+#define GPIO_PIN_OUTPUT                0x0002  /* output direction */
+#define GPIO_PIN_INOUT         0x0004  /* bi-directional */
+#define GPIO_PIN_OPENDRAIN     0x0008  /* open-drain output */
+#define GPIO_PIN_PUSHPULL      0x0010  /* push-pull output */
+#define GPIO_PIN_TRISTATE      0x0020  /* output disabled */
+#define GPIO_PIN_PULLUP                0x0040  /* internal pull-up enabled */
+#define GPIO_PIN_PULLDOWN      0x0080  /* internal pull-down enabled */
+#define GPIO_PIN_INVIN         0x0100  /* invert input */
+#define GPIO_PIN_INVOUT                0x0200  /* invert output */
+#define GPIO_PIN_USER          0x0400  /* user != 0 can access */
+#define GPIO_PIN_SET           0x8000  /* set for securelevel access */
+
+/* Consumer attach arg types */
+#define        GPIO_TYPE_INT           0x01
+
+typedef struct gpio_pin {
+       int     pin_num;                /* number */
+       int     pin_caps;               /* capabilities */
+       int     pin_flags;              /* current configuration */
+       int     pin_state;              /* current state */
+       int     pin_mapped;             /* is mapped */
+       int     pin_opened;             /* is opened */
+       cdev_t  dev;
+} gpio_pin_t;
+
+struct gpio {
+       const char      *driver_name;
+       void            *arg;
+       int             (*pin_read)(void *, int);
+       void            (*pin_write)(void *, int, int);
+       void            (*pin_ctl)(void *, int, int);
+       gpio_pin_t      *pins;
+       int             npins;
+
+       /* Private members */
+       int             driver_unit;
+       cdev_t          master_dev;
+};
+
+struct gpio_consumer {
+       const char      *consumer_name;
+       int             (*consumer_attach)(struct gpio *, void *, int, u_int32_t);
+       int             (*consumer_detach)(struct gpio *, void *, int);
+       LIST_ENTRY(gpio_consumer)       link;
+};
+
+struct gpio_info {
+       int             npins;
+       gpio_pin_t      *pins;
+};
+
+struct gpio_attach_args {
+       char            consumer_name[16];
+       int             pin_offset;
+       u_int32_t       pin_mask;
+       int             arg_type;
+       union {
+               char    string[32];
+               long    lint;
+       } consumer_arg;
+};
+
+struct gpio_pin_set_args {
+       int     pin;
+       int     caps;
+       int     flags;
+};
+
+struct gpio_mapping {
+       struct gpio     *gp;
+       int             *map;
+       int             size;
+
+       int             map_alloced;
+};
+
+void   gpio_consumer_register(struct gpio_consumer *gcp);
+void   gpio_consumer_unregister(struct gpio_consumer *gcp);
+int    gpio_consumer_attach(const char *consumer, void *arg, struct gpio *gp,
+           int pin, u_int32_t mask);
+int    gpio_consumer_detach(const char *consumer, struct gpio *gp, int pin);
+struct gpio_mapping *gpio_map(struct gpio *gp, int *map, int offset, u_int32_t mask);
+void   gpio_unmap(struct gpio_mapping *gmp);
+
+int    gpio_npins(u_int32_t mask);
+int    gpio_pin_read(struct gpio *gp, struct gpio_mapping *map, int pin);
+void   gpio_pin_write(struct gpio  *gp, struct gpio_mapping *map, int pin, int data);
+void   gpio_pin_ctl(struct gpio  *gp, struct gpio_mapping *map, int pin, int flags);
+int    gpio_pin_caps(struct gpio  *gp, struct gpio_mapping *map, int pin);
+void   gpio_register(struct gpio *gp);
+void   gpio_unregister(struct gpio *gp);
+
+void   led_switch(const char *name, int on_off);
+
+#define GPIOINFO               _IOWR('G', 0, struct gpio_info)
+#define GPIOPINSET             _IOWR('G', 4, struct gpio_pin_set_args)
+#define GPIOPINUNSET           _IOWR('G', 5, struct gpio_pin_set_args)
+#define GPIOATTACH             _IOWR('G', 6, struct gpio_attach_args)
+#define GPIODETACH             _IOWR('G', 7, struct gpio_attach_args)
diff --git a/sys/dev/misc/gpio/gpio_led.c b/sys/dev/misc/gpio/gpio_led.c
new file mode 100644 (file)
index 0000000..ad0bbc9
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 2009 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Alex Hornung <ahornung@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/limits.h>
+#include <sys/malloc.h>
+#include <sys/ctype.h>
+#include <sys/sbuf.h>
+#include <sys/queue.h>
+#include <dev/misc/gpio/gpio.h>
+#include <sys/uio.h>
+#include <sys/lock.h>
+#include <sys/devfs.h>
+
+struct ledsc {
+       LIST_ENTRY(ledsc)       list;
+       struct gpio *gp;
+       int             pin;
+       cdev_t  dev;
+       int             unit;
+       int             opened;
+       char    *name;
+       struct gpio_mapping *gp_map;
+};
+
+DEVFS_DECLARE_CLONE_BITMAP(nled);
+static struct lock led_lock;
+static LIST_HEAD(, ledsc) led_list = LIST_HEAD_INITIALIZER(&led_list);
+static MALLOC_DEFINE(M_LED, "LED", "LED driver");
+
+
+static int
+led_open(struct dev_open_args *ap)
+{
+       struct ledsc *sc;
+       cdev_t  dev;
+
+       dev = ap->a_head.a_dev;
+       sc = dev->si_drv1;
+
+       if (sc->opened)
+               return EBUSY;
+
+       sc->opened = 1;
+
+       return 0;
+}
+
+static int
+led_close(struct dev_close_args *ap)
+{
+       struct ledsc *sc;
+       cdev_t  dev;
+
+       dev = ap->a_head.a_dev;
+       sc = dev->si_drv1;
+
+       if (sc->opened)
+               sc->opened = 0;
+
+       return 0;
+}
+
+static int
+led_write(struct dev_write_args *ap)
+{
+       struct ledsc *sc;
+       cdev_t  dev;
+       int             error;
+       int             data = 0;
+       int             len;
+
+       dev = ap->a_head.a_dev;
+       sc = dev->si_drv1;
+
+       if (ap->a_uio->uio_resid > sizeof(int))
+               return EINVAL;
+
+       len = ap->a_uio->uio_resid;
+
+       error = uiomove((void *)&data, ap->a_uio->uio_resid, ap->a_uio);
+       if (error)
+               return error;
+
+       if (len > 1)
+               data = ((char *)&data)[0];
+
+       if (data >= '0')
+               data -= '0';
+
+       gpio_pin_write(sc->gp, sc->gp_map, 0, data);
+
+       return 0;
+}
+
+static int
+led_read(struct dev_read_args *ap)
+{
+       struct ledsc *sc;
+       cdev_t  dev;
+       int             error;
+       int             data = 0;
+
+       dev = ap->a_head.a_dev;
+       sc = dev->si_drv1;
+
+       if (ap->a_uio->uio_resid < sizeof(int))
+               return EINVAL;
+
+       data = gpio_pin_read(sc->gp, sc->gp_map, 0);
+
+       error = uiomove((void *)&data,
+           (ap->a_uio->uio_resid > sizeof(int))?(sizeof(int)):(ap->a_uio->uio_resid),
+               ap->a_uio);
+
+       return error;
+}
+
+static int
+led_ioctl(struct dev_ioctl_args *ap)
+{
+       /* XXX: set a name */
+       return 0;
+}
+
+static struct dev_ops nled_ops = {
+       { "gpio", 0, 0 },
+       .d_open  =      led_open,
+       .d_close =      led_close,
+       .d_write =      led_write,
+       .d_read  =      led_read,
+       .d_ioctl =      led_ioctl,
+};
+
+
+static int
+led_attach(struct gpio *gp, void *arg, int pin, u_int32_t mask)
+{
+       struct ledsc *sc;
+
+       if (arg == NULL)
+               return 1;
+
+       lockmgr(&led_lock, LK_EXCLUSIVE);
+       sc = kmalloc(sizeof(struct ledsc), M_LED, M_WAITOK);
+
+       /* XXX: check for name collisions */
+       sc->name = kstrdup((char *)arg, M_LED);
+       sc->pin = pin;
+       sc->gp = gp;
+
+       sc->gp_map = gpio_map(gp, NULL, pin, 1);
+       if (sc->gp_map == NULL) {
+               return 2;
+       }
+
+       sc->unit = devfs_clone_bitmap_get(&DEVFS_CLONE_BITMAP(nled), 0);
+
+       LIST_INSERT_HEAD(&led_list, sc, list);
+       sc->dev = make_dev(&nled_ops, sc->unit,
+           UID_ROOT, GID_WHEEL, 0600, "led/%s", sc->name);
+       sc->dev->si_drv1 = sc;
+       lockmgr(&led_lock, LK_RELEASE);
+
+       kprintf("gpio_led: Attached led '%s' to gpio %s, pin %d\n",
+           sc->name, sc->gp->driver_name, pin);
+
+       return 0;
+}
+
+static void
+led_detach(struct gpio *gp, void *arg, int pin)
+{
+       /* XXX: implement */
+}
+
+void
+led_switch(const char *name, int on_off)
+{
+       struct ledsc *sc;
+
+       if (name == NULL)
+               return;
+
+       lockmgr(&led_lock, LK_EXCLUSIVE);
+       LIST_FOREACH(sc, &led_list, list) {
+               if (strcmp(name, sc->name) != 0)
+                       continue;
+
+               gpio_pin_write(sc->gp, sc->gp_map, 0, on_off);
+               break;
+       }
+
+       lockmgr(&led_lock, LK_RELEASE);
+}
+
+struct gpio_consumer led_gpio_cons = {
+       .consumer_name = "led",
+       .consumer_attach = led_attach,
+       .consumer_detach = led_detach,
+};
+
+static void
+led_drvinit(void *unused)
+{
+       lockinit(&led_lock, "led_lock", 0, 0);
+       devfs_clone_bitmap_init(&DEVFS_CLONE_BITMAP(nled));
+       gpio_consumer_register(&led_gpio_cons);
+}
+
+SYSINIT(leddev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, led_drvinit, NULL);