Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src Add a new ioctl, GPIOPULSE to gpio(4) to allow for pulsing a...



details:   https://anonhg.NetBSD.org/src/rev/bb38c0047fb5
branches:  trunk
changeset: 768863:bb38c0047fb5
user:      mbalmer <mbalmer%NetBSD.org@localhost>
date:      Sun Aug 28 07:48:50 2011 +0000

description:
Add a new ioctl, GPIOPULSE to gpio(4) to allow for pulsing a pin.
If a pin can pulse in hardware, that will be used, else it will
be pulsed in software.  There is no way yet to set the pulse frequency
for pins that pulse in hardware.  While here, make the code mpsafe and
allow more than one thread in the driver (access to ioctl is serialized).

diffstat:

 share/man/man4/gpio.4      |   29 +++++-
 sys/dev/gpio/gpio.c        |  231 ++++++++++++++++++++++++++++++++++----------
 sys/dev/gpio/gpiovar.h     |   32 +++--
 sys/sys/gpio.h             |   40 +++++--
 usr.sbin/gpioctl/gpioctl.8 |   29 ++++-
 usr.sbin/gpioctl/gpioctl.c |   95 ++++++++++++++++--
 6 files changed, 356 insertions(+), 100 deletions(-)

diffs (truncated from 917 to 300 lines):

diff -r 49a3e00d954d -r bb38c0047fb5 share/man/man4/gpio.4
--- a/share/man/man4/gpio.4     Sun Aug 28 07:48:18 2011 +0000
+++ b/share/man/man4/gpio.4     Sun Aug 28 07:48:50 2011 +0000
@@ -1,4 +1,4 @@
-.\" $NetBSD: gpio.4,v 1.18 2010/03/22 18:58:31 joerg Exp $
+.\" $NetBSD: gpio.4,v 1.19 2011/08/28 07:48:50 mbalmer Exp $
 .\"    $OpenBSD: gpio.4,v 1.5 2004/11/23 09:39:29 reyk Exp $
 .\"
 .\" Copyright (c) 2004 Alexander Yurchenko <grange%openbsd.org@localhost>
@@ -15,7 +15,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd September 27, 2009
+.Dd August 21, 2011
 .Dt GPIO 4
 .Os
 .Sh NAME
@@ -123,6 +123,31 @@
 .Fa gp_value
 field is ignored and on return contains the old pin state.
 .Pp
+.It Dv GPIOPULSE (struct gpio_pulse)
+Starts pulsing the pin:
+.Bd -literal
+/* GPIO pulse request */
+struct gpio_pulse {
+       char            gp_name[GPIOMAXNAME];   /* pin name */
+       int             gp_pin;                 /* pin number */
+       struct timeval  gp_pulse_on;            /* "on" period */
+       struct timeval  gp_pulse_off;           /* "off" period */
+};
+.Ed
+.Pp
+The
+.Fa gp_name
+or
+.Fa gp_pin
+field must be set before calling.
+If
+.Fa gp_pulse_on
+or
+.Fa gp_pulse_off
+is set to zero, a default of
+.Xr hz 9 / 2
+is assumed for both periods.
+.Pp
 .It Dv GPIOSET (struct gpio_set)
 Changes pin configuration flags with the new ones provided in the
 .Fa gpio_set
diff -r 49a3e00d954d -r bb38c0047fb5 sys/dev/gpio/gpio.c
--- a/sys/dev/gpio/gpio.c       Sun Aug 28 07:48:18 2011 +0000
+++ b/sys/dev/gpio/gpio.c       Sun Aug 28 07:48:50 2011 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: gpio.c,v 1.35 2011/08/12 08:00:52 mbalmer Exp $ */
+/* $NetBSD: gpio.c,v 1.36 2011/08/28 07:48:50 mbalmer Exp $ */
 /*     $OpenBSD: gpio.c,v 1.6 2006/01/14 12:33:49 grange Exp $ */
 
 /*
@@ -19,21 +19,25 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: gpio.c,v 1.35 2011/08/12 08:00:52 mbalmer Exp $");
+__KERNEL_RCSID(0, "$NetBSD: gpio.c,v 1.36 2011/08/28 07:48:50 mbalmer Exp $");
 
 /*
  * General Purpose Input/Output framework.
  */
 
 #include <sys/param.h>
+#include <sys/callout.h>
 #include <sys/systm.h>
 #include <sys/conf.h>
 #include <sys/device.h>
 #include <sys/fcntl.h>
 #include <sys/ioctl.h>
 #include <sys/gpio.h>
+#include <sys/kernel.h>
 #include <sys/vnode.h>
 #include <sys/kmem.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
 #include <sys/queue.h>
 #include <sys/kauth.h>
 #ifdef _MODULE
@@ -58,7 +62,9 @@
        gpio_pin_t              *sc_pins;       /* pins array */
        int                      sc_npins;      /* number of pins */
 
-       int                      sc_opened;
+       kmutex_t                 sc_mtx;
+       kcondvar_t               sc_ioctl;      /* ioctl in progress */
+       int                      sc_ioctl_busy; /* ioctl is busy */
        LIST_HEAD(, gpio_dev)    sc_devs;       /* devices */
        LIST_HEAD(, gpio_name)   sc_names;      /* named pins */
 };
@@ -73,6 +79,9 @@
 static int     gpio_search(device_t, cfdata_t, const int *, void *);
 static int     gpio_print(void *, const char *);
 static int     gpio_pinbyname(struct gpio_softc *, char *);
+static void    gpio_pulse(void *);
+static int     gpio_ioctl(struct gpio_softc *, u_long, void *, int,
+    struct lwp *);
 
 /* Old API */
 static int     gpio_ioctl_oapi(struct gpio_softc *, u_long, void *, int,
@@ -85,10 +94,11 @@
 dev_type_open(gpioopen);
 dev_type_close(gpioclose);
 dev_type_ioctl(gpioioctl);
+dev_type_ioctl(gpioioctl_locked);
 
 const struct cdevsw gpio_cdevsw = {
        gpioopen, gpioclose, noread, nowrite, gpioioctl,
-       nostop, notty, nopoll, nommap, nokqfilter, D_OTHER,
+       nostop, notty, nopoll, nommap, nokqfilter, D_OTHER | D_MPSAFE
 };
 
 extern struct cfdriver gpio_cd;
@@ -144,7 +154,7 @@
 {
        struct gpio_softc *sc = device_private(self);
        struct gpiobus_attach_args *gba = aux;
-
+       int pin;
        sc->sc_dev = self;
        sc->sc_gc = gba->gba_gc;
        sc->sc_pins = gba->gba_pins;
@@ -152,8 +162,15 @@
 
        printf(": %d pins\n", sc->sc_npins);
 
+       for (pin = 0; pin < sc->sc_npins; pin++) {
+               callout_init(&sc->sc_pins[pin].pin_pulse, CALLOUT_MPSAFE);
+               callout_setfunc(&sc->sc_pins[pin].pin_pulse, gpio_pulse,
+                    &sc->sc_pins[pin]);
+       }
        if (!pmf_device_register(self, NULL, gpio_resume))
                aprint_error_dev(self, "couldn't establish power handler\n");
+       mutex_init(&sc->sc_mtx, MUTEX_DEFAULT, IPL_VM);
+       cv_init(&sc->sc_ioctl, "gpioctl");
 
        /*
         * Attach all devices that can be connected to the GPIO pins
@@ -165,11 +182,23 @@
 static int
 gpio_detach(device_t self, int flags)
 {
-       int rc;
+       struct gpio_softc *sc;
+       int pin, rc;
+
+       sc = device_private(self);
+
+       for (pin = 0; pin < sc->sc_npins; pin++) {
+               if (sc->sc_pins[pin].pin_state & GPIO_PIN_PULSE) {
+                       callout_halt(&sc->sc_pins[pin].pin_pulse, NULL);
+                       callout_destroy(&sc->sc_pins[pin].pin_pulse);
+                       sc->sc_pins[pin].pin_state &= ~GPIO_PIN_PULSE;
+               }
+       }
 
        if ((rc = config_detach_children(self, flags)) != 0)
                return rc;
-
+       mutex_destroy(&sc->sc_mtx);
+       cv_destroy(&sc->sc_ioctl);
 #if 0
        int maj, mn;
 
@@ -228,7 +257,7 @@
 
 /* return 1 if all pins can be mapped, 0 if not */
 int
-gpio_pin_can_map(void *gpio, int offset, u_int32_t mask)
+gpio_pin_can_map(void *gpio, int offset, uint32_t mask)
 {
        struct gpio_softc *sc = gpio;
        int npins, pin, i;
@@ -250,7 +279,7 @@
 }
 
 int
-gpio_pin_map(void *gpio, int offset, u_int32_t mask, struct gpio_pinmap *map)
+gpio_pin_map(void *gpio, int offset, uint32_t mask, struct gpio_pinmap *map)
 {
        struct gpio_softc *sc = gpio;
        int npins, pin, i;
@@ -320,7 +349,7 @@
 }
 
 int
-gpio_npins(u_int32_t mask)
+gpio_npins(uint32_t mask)
 {
        int npins, i;
 
@@ -332,30 +361,47 @@
 }
 
 int
+gpio_lock(void *data)
+{
+       struct gpio_softc *sc;
+       int error;
+
+       error = 0;
+       sc = (struct gpio_softc *)data;
+       mutex_enter(&sc->sc_mtx);
+       while (sc->sc_ioctl_busy) {
+               error = cv_wait_sig(&sc->sc_ioctl, &sc->sc_mtx);
+               if (error)
+                       break;
+       }
+       if (!error)
+               sc->sc_ioctl_busy = 1;
+       mutex_exit(&sc->sc_mtx);
+       return error;
+}
+
+void
+gpio_unlock(void *data)
+{
+       struct gpio_softc *sc;
+
+       sc = (struct gpio_softc *)data;
+       mutex_enter(&sc->sc_mtx);
+       sc->sc_ioctl_busy = 0;
+       cv_signal(&sc->sc_ioctl);
+       mutex_exit(&sc->sc_mtx);
+}
+
+int
 gpioopen(dev_t dev, int flag, int mode, struct lwp *l)
 {
        struct gpio_softc *sc;
-       int ret;
 
        sc = device_lookup_private(&gpio_cd, minor(dev));
        if (sc == NULL)
                return ENXIO;
-       DPRINTF(("%s: opening\n", device_xname(sc->sc_dev)));
-       if (sc->sc_opened) {
-               DPRINTF(("%s: already opened\n", device_xname(sc->sc_dev)));
-               return EBUSY;
-       }
 
-       if ((ret = gpiobus_open(sc->sc_gc, sc->sc_dev))) {
-               DPRINTF(("%s: gpiobus_open returned %d\n",
-                   device_xname(sc->sc_dev),
-                   ret));
-               return ret;
-       }
-
-       sc->sc_opened = 1;
-
-       return 0;
+       return gpiobus_open(sc->sc_gc, sc->sc_dev);
 }
 
 int
@@ -364,10 +410,7 @@
        struct gpio_softc *sc;
 
        sc = device_lookup_private(&gpio_cd, minor(dev));
-       DPRINTF(("%s: closing\n", device_xname(sc->sc_dev)));
-       (void)gpiobus_close(sc->sc_gc, sc->sc_dev);
-       sc->sc_opened = 0;
-
+       gpiobus_close(sc->sc_gc, sc->sc_dev);
        return 0;
 }
 
@@ -382,25 +425,63 @@
         return -1;
 }
 
+static void
+gpio_pulse(void *arg)
+{
+       struct gpio_pin *pin;
+
+       pin = (struct gpio_pin *)arg;
+       if ((pin->pin_state & GPIO_PIN_PULSE) == 0)
+               return;
+
+       if (pin->pin_state & GPIO_PIN_HIGH) {
+               gpiobus_pin_write(pin->pin_gc, pin->pin_num, GPIO_PIN_LOW);
+               pin->pin_state &= ~GPIO_PIN_HIGH;
+               callout_schedule(&pin->pin_pulse, pin->pin_ticks_off);
+       } else {
+               gpiobus_pin_write(pin->pin_gc, pin->pin_num, GPIO_PIN_HIGH);
+               pin->pin_state |= GPIO_PIN_HIGH;
+               callout_schedule(&pin->pin_pulse, pin->pin_ticks_on);
+       }
+}
+
 int
 gpioioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)



Home | Main Index | Thread Index | Old Index