tech-kern archive

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

Re: Adding pulse support to gpio(4), gpioctl(8)



Am 24.08.11 00:00, schrieb Mindaugas Rasiukevicius:
> Marc Balmer <mbalmer%NetBSD.org@localhost> wrote:
>> Here comes the third iteration of the gpio(4) patch.  In addition to
>> whjat I already described, u_int_XXX types have been replaced by
>> uint_XXX and gpio(4) is made MPSAFE.  Comments?
>>
>> Furthermore I am thinking if it would be useful if more than one process
>> could open a gpio device, as a long as they use different pins, e.g. one
>> process controlls a stepper motor using some of the pins while another
>> process drives some LEDs.
> 
> There is nothing what prevents from multiple threads calling gpioioctl(),
> which is obviously not MP-safe.  As soon as you will start fixing this, it
> will bring you back to the point I have already stated - the design needs
> to be MP-safe in general.

That is done now.  I agree with the design remark, but when gpio(4) was
originally designed, I guess mpsafeness was not one of the primary goals...

> 
>> +    mutex_enter(&sc->sc_mtx);
>> +    if (sc->sc_opened)
>> +            return EBUSY;
> 
> This leaks the lock.

Ooops... But it is not needed here anymore, see below...

> 
>>      int                      sc_opened;
>> +    kmutex_t                 sc_mtx;
> 
> Preferred suffix is _lock, rather than _mtx, so please use sc_lock.

I have seen both forms in the tree.

> 
>> +            sc->sc_pins[pin].pin_ticks_on = tvtohz(&pulse->gp_pulse_on);
>> +            sc->sc_pins[pin].pin_ticks_off = tvtohz(&pulse->gp_pulse_off);
>> +            if (sc->sc_pins[pin].pin_ticks_on == 0
>> +                || sc->sc_pins[pin].pin_ticks_off == 0) {
>> +                    sc->sc_pins[pin].pin_ticks_on = hz / 2;
>> +                    sc->sc_pins[pin].pin_ticks_off = hz / 2;
>> +            }
> 
> Use gpio_pin_t *gpin = &sc->sc_pins[pin]; and gpin variable instead of
> sc->sc_pins[pin] each time.  Apart from better readability, GCC will most
> likely generate a better code.

I reworked the patch again.  This version changes the behaviour as well:
 It is now possible that multiple thready/processes have a gpio device
open, but only one thread can be in ioctl().  Two new functions are
added, gpio_lock() and gpio_nolock() which can be used by child drivers
as well.

Since there is more than one way to attach a child device (via
GPIOATTACH, modload, drvctl), I use a flag in the gpio_attach_args,
sc_nolock, to signal to a child device to not lock the gpio when it is
attached via ioctl().

In the end, the GPIOATTACH and GPIODETACH should be removed from the
gpio(4) driver and the functionality be provided by drvctl(8).


Index: sys/sys/gpio.h
===================================================================
RCS file: /cvsroot/src/sys/sys/gpio.h,v
retrieving revision 1.8
diff -u -p -r1.8 gpio.h
--- sys/sys/gpio.h      23 Jun 2011 00:46:37 -0000      1.8
+++ sys/sys/gpio.h      26 Aug 2011 08:58:57 -0000
@@ -20,9 +20,12 @@
 #ifndef _SYS_GPIO_H_
 #define _SYS_GPIO_H_
 
+#include <sys/time.h>
+
 /* GPIO pin states */
 #define GPIO_PIN_LOW           0x00    /* low level (logical 0) */
 #define GPIO_PIN_HIGH          0x01    /* high level (logical 1) */
+#define GPIO_PIN_PULSE         0x02    /* pulsing, or-ed with state */
 
 /* Max name length of a pin */
 #define GPIOMAXNAME            64
@@ -51,25 +54,33 @@ struct gpio_info {
 
 /* GPIO pin request (read/write/toggle) */
 struct gpio_req {
-       char gp_name[GPIOMAXNAME];      /* pin name */
-       int gp_pin;                     /* pin number */
-       int gp_value;                   /* value */
+       char            gp_name[GPIOMAXNAME];   /* pin name */
+       int             gp_pin;                 /* pin number */
+       int             gp_value;               /* value */
+};
+
+/* 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 */
 };
 
 /* GPIO pin configuration */
 struct gpio_set {
-       char gp_name[GPIOMAXNAME];
-       int gp_pin;
-       int gp_caps;
-       int gp_flags;
-       char gp_name2[GPIOMAXNAME];     /* new name */
+       char    gp_name[GPIOMAXNAME];
+       int     gp_pin;
+       int     gp_caps;
+       int     gp_flags;
+       char    gp_name2[GPIOMAXNAME];  /* new name */
 };
 
 /* Attach/detach device drivers that use GPIO pins */
 struct gpio_attach {
-       char ga_dvname[16];     /* device name */
-       int ga_offset;          /* pin number */
-       u_int32_t ga_mask;      /* binary mask */
+       char            ga_dvname[16];  /* device name */
+       int             ga_offset;      /* pin number */
+       uint32_t        ga_mask;        /* binary mask */
 };
 
 /* GPIO pin control (old API) */
@@ -81,8 +92,8 @@ struct gpio_pin_ctl {
 
 /* GPIO pin operation (read/write/toggle) (old API) */
 struct gpio_pin_op {
-       int gp_pin;                     /* pin number */
-       int gp_value;                   /* value */
+       int gp_pin;             /* pin number */
+       int gp_value;           /* value */
 };
 
 #define GPIOINFO               _IOR('G', 0, struct gpio_info)
@@ -101,5 +112,6 @@ struct gpio_pin_op {
 #define GPIOTOGGLE             _IOWR('G', 9, struct gpio_req)
 #define GPIOATTACH             _IOWR('G', 10, struct gpio_attach)
 #define GPIODETACH             _IOWR('G', 11, struct gpio_attach)
+#define GPIOPULSE              _IOWR('G', 12, struct gpio_pulse)
 
 #endif /* !_SYS_GPIO_H_ */
Index: sys/dev/gpio/gpiovar.h
===================================================================
RCS file: /cvsroot/src/sys/dev/gpio/gpiovar.h,v
retrieving revision 1.11
diff -u -p -r1.11 gpiovar.h
--- sys/dev/gpio/gpiovar.h      12 Aug 2011 08:00:52 -0000      1.11
+++ sys/dev/gpio/gpiovar.h      26 Aug 2011 08:58:57 -0000
@@ -35,18 +35,22 @@ typedef struct gpio_chipset_tag {
 
 /* GPIO pin description */
 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_num;        /* number */
+       int                     pin_caps;       /* capabilities */
+       int                     pin_flags;      /* current configuration */
+       int                     pin_state;      /* current state */
+       int                     pin_mapped;     /* is mapped */
+       callout_t               pin_pulse;      /* for pulsing */
+       int                     pin_ticks_on;   /* "on" period */
+       int                     pin_ticks_off;  /* "off" period */
+       gpio_chipset_tag_t      pin_gc;         /* reference the controller */
 } gpio_pin_t;
 
 /* Attach GPIO framework to the controller */
 struct gpiobus_attach_args {
-       gpio_chipset_tag_t      gba_gc;         /* underlying controller */
+       gpio_chipset_tag_t       gba_gc;        /* underlying controller */
        gpio_pin_t              *gba_pins;      /* pins array */
-       int                     gba_npins;      /* total number of pins */
+       int                      gba_npins;     /* total number of pins */
 };
 
 int gpiobus_print(void *, const char *);
@@ -67,8 +71,9 @@ int gpiobus_print(void *, const char *);
 struct gpio_attach_args {
        void            *ga_gpio;
        int              ga_offset;
-       u_int32_t        ga_mask;
+       uint32_t         ga_mask;
        char            *ga_dvname;
+       int              ga_nolock;
 };
 
 /* GPIO pin map */
@@ -88,13 +93,16 @@ struct gpio_name {
        LIST_ENTRY(gpio_name)   gp_next;
 };
 
-int    gpio_pin_can_map(void *, int, u_int32_t);
-int    gpio_pin_map(void *, int, u_int32_t, struct gpio_pinmap *);
+int    gpio_pin_can_map(void *, int, uint32_t);
+int    gpio_pin_map(void *, int, uint32_t, struct gpio_pinmap *);
 void   gpio_pin_unmap(void *, struct gpio_pinmap *);
 int    gpio_pin_read(void *, struct gpio_pinmap *, int);
 void   gpio_pin_write(void *, struct gpio_pinmap *, int, int);
 void   gpio_pin_ctl(void *, struct gpio_pinmap *, int, int);
 int    gpio_pin_caps(void *, struct gpio_pinmap *, int);
-int    gpio_npins(u_int32_t);
+int    gpio_npins(uint32_t);
+
+int    gpio_lock(void *);
+void   gpio_unlock(void *);
 
 #endif /* !_DEV_GPIO_GPIOVAR_H_ */
Index: sys/dev/gpio/gpio.c
===================================================================
RCS file: /cvsroot/src/sys/dev/gpio/gpio.c,v
retrieving revision 1.35
diff -u -p -r1.35 gpio.c
--- sys/dev/gpio/gpio.c 12 Aug 2011 08:00:52 -0000      1.35
+++ sys/dev/gpio/gpio.c 26 Aug 2011 08:58:57 -0000
@@ -26,14 +26,18 @@ __KERNEL_RCSID(0, "$NetBSD: gpio.c,v 1.3
  */
 
 #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 @@ struct gpio_softc {
        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_detach(device_t, int);
 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 @@ CFATTACH_DECL3_NEW(gpio, sizeof(struct g
 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 @@ gpio_attach(device_t parent, device_t se
 {
        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 @@ gpio_attach(device_t parent, device_t se
 
        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 @@ gpio_attach(device_t parent, device_t se
 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 @@ gpiobus_print(void *aux, const char *pnp
 
 /* 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 @@ gpio_pin_can_map(void *gpio, int offset,
 }
 
 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 @@ gpio_pin_caps(void *gpio, struct gpio_pi
 }
 
 int
-gpio_npins(u_int32_t mask)
+gpio_npins(uint32_t mask)
 {
        int npins, i;
 
@@ -332,30 +361,46 @@ gpio_npins(u_int32_t mask)
 }
 
 int
+gpio_lock(void *data)
+{
+       struct gpio_softc *sc;
+       int error;
+
+       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 +409,7 @@ gpioclose(dev_t dev, int flag, int mode,
        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 +424,63 @@ gpio_pinbyname(struct gpio_softc *sc, ch
         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)
 {
+       int error;
        struct gpio_softc *sc;
+
+       sc = device_lookup_private(&gpio_cd, minor(dev));
+
+       error = gpio_lock(sc);
+       if (error)
+               return error;
+
+       error = gpio_ioctl(sc, cmd, data, flag, l);
+       gpio_unlock(sc);
+       return error;
+}
+
+static int
+gpio_ioctl(struct gpio_softc *sc, u_long cmd, void *data, int flag,
+    struct lwp *l)
+{
        gpio_chipset_tag_t gc;
        struct gpio_info *info;
        struct gpio_attach *attach;
        struct gpio_attach_args ga;
        struct gpio_dev *gdev;
        struct gpio_req *req;
+       struct gpio_pulse *pulse;
        struct gpio_name *nm;
        struct gpio_set *set;
+       struct gpio_pin *gpin;
        device_t dv;
        cfdata_t cf;
        kauth_cred_t cred;
        int locs[GPIOCF_NLOCS];
        int pin, value, flags, npins;
 
-       sc = device_lookup_private(&gpio_cd, minor(dev));
        gc = sc->sc_gc;
 
        if (cmd != GPIOINFO && !device_is_active(sc->sc_dev)) {
@@ -427,11 +507,9 @@ gpioioctl(dev_t dev, u_long cmd, void *d
        case GPIOREAD:
                req = (struct gpio_req *)data;
 
-               if (req->gp_name[0] != '\0') {
+               if (req->gp_name[0] != '\0')
                        pin = gpio_pinbyname(sc, req->gp_name);
-                       if (pin == -1)
-                               return EINVAL;
-               } else
+               else
                        pin = req->gp_pin;
 
                if (pin < 0 || pin >= sc->sc_npins)
@@ -451,11 +529,9 @@ gpioioctl(dev_t dev, u_long cmd, void *d
 
                req = (struct gpio_req *)data;
 
-               if (req->gp_name[0] != '\0') {
+               if (req->gp_name[0] != '\0')
                        pin = gpio_pinbyname(sc, req->gp_name);
-                       if (pin == -1)
-                               return EINVAL;
-               } else
+               else
                        pin = req->gp_pin;
 
                if (pin < 0 || pin >= sc->sc_npins)
@@ -473,23 +549,68 @@ gpioioctl(dev_t dev, u_long cmd, void *d
                if (value != GPIO_PIN_LOW && value != GPIO_PIN_HIGH)
                        return EINVAL;
 
+               if (sc->sc_pins[pin].pin_state & GPIO_PIN_PULSE) {
+                       callout_halt(&sc->sc_pins[pin].pin_pulse, NULL);
+                       sc->sc_pins[pin].pin_state &= ~GPIO_PIN_PULSE;
+               }
                gpiobus_pin_write(gc, pin, value);
                /* return old value */
                req->gp_value = sc->sc_pins[pin].pin_state;
                /* update current value */
                sc->sc_pins[pin].pin_state = value;
                break;
+       case GPIOPULSE:
+               if ((flag & FWRITE) == 0)
+                       return EBADF;
+
+               pulse = (struct gpio_pulse *)data;
+               if (pulse->gp_name[0] != '\0')
+                       pin = gpio_pinbyname(sc, pulse->gp_name);
+               else
+                       pin = pulse->gp_pin;
+
+               if (pin < 0 || pin >= sc->sc_npins)
+                       return EINVAL;
+
+               gpin = &sc->sc_pins[pin];
+               if (gpin->pin_mapped)
+                       return EBUSY;
+
+               if (!(gpin->pin_flags & GPIO_PIN_SET) &&
+                   kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET,
+                   NULL, NULL, NULL, NULL))
+                       return EPERM;
+
+               if (gpin->pin_flags & GPIO_PIN_PULSATE) {
+                       gpiobus_pin_write(gc, pin, GPIO_PIN_HIGH);
+                       gpin->pin_state = GPIO_PIN_PULSE;
+                       return 0;
+               }
+
+               if (gpin->pin_state & GPIO_PIN_PULSE)
+                       callout_halt(&gpin->pin_pulse, NULL);
+
+               gpin->pin_gc = gc;
+
+               gpin->pin_ticks_on = tvtohz(&pulse->gp_pulse_on);
+               gpin->pin_ticks_off = tvtohz(&pulse->gp_pulse_off);
+               if (gpin->pin_ticks_on == 0 || gpin->pin_ticks_off == 0) {
+                       gpin->pin_ticks_on = hz / 2;
+                       gpin->pin_ticks_off = hz / 2;
+               }
+               gpiobus_pin_write(gc, pin, GPIO_PIN_HIGH);
+               gpin->pin_state = GPIO_PIN_HIGH | GPIO_PIN_PULSE;
+               callout_schedule(&gpin->pin_pulse, gpin->pin_ticks_on);
+               break;
        case GPIOTOGGLE:
                if ((flag & FWRITE) == 0)
                        return EBADF;
 
                req = (struct gpio_req *)data;
 
-               if (req->gp_name[0] != '\0') {
+               if (req->gp_name[0] != '\0')
                        pin = gpio_pinbyname(sc, req->gp_name);
-                       if (pin == -1)
-                               return EINVAL;
-               } else
+               else
                        pin = req->gp_pin;
 
                if (pin < 0 || pin >= sc->sc_npins)
@@ -526,9 +647,10 @@ gpioioctl(dev_t dev, u_long cmd, void *d
                ga.ga_dvname = attach->ga_dvname;
                ga.ga_offset = attach->ga_offset;
                ga.ga_mask = attach->ga_mask;
-               DPRINTF(("%s: attach %s with offset %d and mask 0x%02x\n",
-                   device_xname(sc->sc_dev), ga.ga_dvname, ga.ga_offset,
-                   ga.ga_mask));
+               ga.ga_nolock = 1;
+               DPRINTF(("%s: attach %s with offset %d and mask "
+                   "0x%02x\n", device_xname(sc->sc_dev), ga.ga_dvname,
+                   ga.ga_offset, ga.ga_mask));
 
                locs[GPIOCF_OFFSET] = ga.ga_offset;
                locs[GPIOCF_MASK] = ga.ga_mask;
@@ -546,7 +668,7 @@ gpioioctl(dev_t dev, u_long cmd, void *d
                                return EINVAL;
                } else
                        return EINVAL;
-               break;
+               return 0;
        case GPIODETACH:
                if (kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET,
                    NULL, NULL, NULL, NULL))
@@ -566,7 +688,6 @@ gpioioctl(dev_t dev, u_long cmd, void *d
                        }
                }
                return EINVAL;
-               break;
        case GPIOSET:
                if (kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET,
                    NULL, NULL, NULL, NULL))
@@ -574,12 +695,11 @@ gpioioctl(dev_t dev, u_long cmd, void *d
 
                set = (struct gpio_set *)data;
 
-               if (set->gp_name[0] != '\0') {
+               if (set->gp_name[0] != '\0')
                        pin = gpio_pinbyname(sc, set->gp_name);
-                       if (pin == -1)
-                               return EINVAL;
-               } else
+               else
                        pin = set->gp_pin;
+
                if (pin < 0 || pin >= sc->sc_npins)
                        return EINVAL;
                flags = set->gp_flags;
@@ -629,11 +749,9 @@ gpioioctl(dev_t dev, u_long cmd, void *d
                        return EPERM;
 
                set = (struct gpio_set *)data;
-               if (set->gp_name[0] != '\0') {
+               if (set->gp_name[0] != '\0')
                        pin = gpio_pinbyname(sc, set->gp_name);
-                       if (pin == -1)
-                               return EINVAL;
-               } else
+               else
                        pin = set->gp_pin;
 
                if (pin < 0 || pin >= sc->sc_npins)
Index: share/man/man4/gpio.4
===================================================================
RCS file: /cvsroot/src/share/man/man4/gpio.4,v
retrieving revision 1.18
diff -u -p -r1.18 gpio.4
--- share/man/man4/gpio.4       22 Mar 2010 18:58:31 -0000      1.18
+++ share/man/man4/gpio.4       26 Aug 2011 08:58:57 -0000
@@ -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 @@ Toggles the pin output value, i.e. chang
 .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
Index: usr.sbin/gpioctl/gpioctl.8
===================================================================
RCS file: /cvsroot/src/usr.sbin/gpioctl/gpioctl.8,v
retrieving revision 1.8
diff -u -p -r1.8 gpioctl.8
--- usr.sbin/gpioctl/gpioctl.8  12 Aug 2011 08:02:33 -0000      1.8
+++ usr.sbin/gpioctl/gpioctl.8  26 Aug 2011 08:58:57 -0000
@@ -1,6 +1,6 @@
 .\" $NetBSD: gpioctl.8,v 1.8 2011/08/12 08:02:33 mbalmer Exp $
 .\"
-.\" Copyright (c) 2009, 2010 Marc Balmer <marc%msys.ch@localhost>
+.\" Copyright (c) 2009, 2010, 2011 Marc Balmer <marc%msys.ch@localhost>
 .\" Copyright (c) 2004 Alexander Yurchenko <grange%openbsd.org@localhost>
 .\"
 .\" Permission to use, copy, modify, and distribute this software for any
@@ -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 25, 2009
+.Dd August 21, 2011
 .Dt GPIOCTL 8
 .Os
 .Sh NAME
@@ -48,6 +48,12 @@ detach
 .Op Fl q
 .Ar device
 .Ar pin
+pulse
+.Op Ar frequency Op Ar duty cycle
+.Nm gpioctl
+.Op Fl q
+.Ar device
+.Ar pin
 set
 .Op Ar flags
 .Op Ar name
@@ -92,7 +98,7 @@ To write to a pin, a value must be speci
 .Ar pin
 number.
 Values can be either 0 or 1.
-A value of 2 has a special meaning: it
+A value of 2
 .Dq toggles
 the pin, i.e. changes its state to the opposite.
 Instead of the numerical values, the word
@@ -102,6 +108,19 @@ or
 .Ar toggle
 can be used.
 .Pp
+To
+.Dq pulse
+a pin, use the pulse command line option with an optional frequency value
+in hertz and an optional duty cycle in percent.
+If no frequency is specified, 1 Hz is assumed.
+If no duty cycle is specified, 50% are assumed.
+If the underlying hardware is not capable of pulsing in hardware,
+pulsing is done in software using the
+.Xr callout 9
+facility.
+The frequency and duty cycle arguments are ignored for pins that are able to
+pulse in hardware.
+.Pp
 Only pins that have been configured at securelevel 0, typically during system
 startup, are accessible once the securelevel has been raised.
 Pins can be given symbolic names for easier use.
@@ -207,5 +226,5 @@ The
 .Nm
 program was written by
 .An Alexander Yurchenko Aq grange%openbsd.org@localhost .
-Device attachment was added by
+Device attachment and pulsing was added by
 .An Marc Balmer Aq marc%msys.ch@localhost .
Index: usr.sbin/gpioctl/gpioctl.c
===================================================================
RCS file: /cvsroot/src/usr.sbin/gpioctl/gpioctl.c,v
retrieving revision 1.10
diff -u -p -r1.10 gpioctl.c
--- usr.sbin/gpioctl/gpioctl.c  12 Aug 2011 08:06:23 -0000      1.10
+++ usr.sbin/gpioctl/gpioctl.c  26 Aug 2011 08:58:58 -0000
@@ -32,6 +32,7 @@
 #include <paths.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <time.h>
 #include <string.h>
 #include <unistd.h>
 
@@ -43,9 +44,10 @@ int quiet = 0;
 static void getinfo(void);
 static void gpioread(int, char *);
 static void gpiowrite(int, char *, int);
+static void gpiopulse(int, char *, double, double);
 static void gpioset(int pin, char *name, int flags, char *alias);
 static void gpiounset(int pin, char *name);
-static void devattach(char *, int, u_int32_t);
+static void devattach(char *, int, uint32_t);
 static void devdetach(char *);
 static void usage(void);
 
@@ -74,11 +76,12 @@ int
 main(int argc, char *argv[])
 {
        const struct bitstr *bs;
+       double freq, dc;
        int pin, ch, n, fl = 0, value = 0;
        const char *errstr;
        char *ep;
        int ga_offset = -1;
-       u_int32_t ga_mask = 0;
+       uint32_t ga_mask = 0;
        long lval;
        char *nam = NULL;
        char devn[32];
@@ -99,6 +102,8 @@ main(int argc, char *argv[])
                usage();
        dev = argv[0];
 
+       freq = dc = 0.0;
+
        if (strncmp(_PATH_DEV, dev, sizeof(_PATH_DEV) - 1)) {
                (void)snprintf(devn, sizeof(devn), "%s%s", _PATH_DEV, dev);
                dev = devn;
@@ -140,12 +145,12 @@ main(int argc, char *argv[])
                devdetach(argv[2]);
        } else {
                char *nm = NULL;
-       
+
                /* expecting a pin number or name */
                pin = strtonum(argv[1], 0, INT_MAX, &errstr);
                if (errstr)
                        nm = argv[1];   /* try named pin */
-               if (argc > 2) { 
+               if (argc > 2) {
                        if (!strcmp(argv[2], "set")) {
                                for (n = 3; n < argc; n++) {
                                        for (bs = pinflags; bs->string != NULL;
@@ -162,14 +167,24 @@ main(int argc, char *argv[])
                                gpioset(pin, nm, fl, nam);
                        } else if (!strcmp(argv[2], "unset")) {
                                gpiounset(pin, nm);
+                       } else if (!strcmp(argv[2], "pulse")) {
+                               if (argc == 4) {
+                                       freq = atof(argv[3]);
+                                       dc = 50.0;
+                               } else if (argc == 5) {
+                                       freq = atof(argv[3]);
+                                       dc = atof(argv[4]);
+                               } else
+                                       freq = dc = 0.0;
+                               gpiopulse(pin, nm, freq, dc);
                        } else {
                                value = strtonum(argv[2], INT_MIN, INT_MAX,
                                   &errstr);
                                if (errstr) {
                                        if (!strcmp(argv[2], "on"))
-                                               value = 1;
+                                               value = GPIO_PIN_HIGH;
                                        else if (!strcmp(argv[2], "off"))
-                                               value = 0;
+                                               value = GPIO_PIN_LOW;
                                        else if (!strcmp(argv[2], "toggle"))
                                                value = 2;
                                        else
@@ -236,8 +251,9 @@ gpiowrite(int pin, char *gp_name, int va
                strlcpy(req.gp_name, gp_name, sizeof(req.gp_name));
        else
                req.gp_pin = pin;
-       req.gp_value = (value == 0 ? GPIO_PIN_LOW : GPIO_PIN_HIGH);
-       if (value < 2) {
+
+       if (value == GPIO_PIN_HIGH || value == GPIO_PIN_LOW) {
+               req.gp_value = value;
                if (ioctl(devfd, GPIOWRITE, &req) == -1)
                        err(EXIT_FAILURE, "GPIOWRITE");
        } else {
@@ -257,6 +273,62 @@ gpiowrite(int pin, char *gp_name, int va
 }
 
 static void
+gpiopulse(int pin, char *gp_name, double freq, double dc)
+{
+       struct gpio_pulse pulse;
+       suseconds_t period, on, off;
+
+       if (freq < 0.0 || (dc < 0.0 || dc >= 100.0))
+               errx(EXIT_FAILURE, "%.f Hz, %.f%% duty cycle: invalid value",
+                   freq, dc);
+
+       memset(&pulse, 0, sizeof(pulse));
+       if (gp_name != NULL)
+               strlcpy(pulse.gp_name, gp_name, sizeof(pulse.gp_name));
+       else
+               pulse.gp_pin = pin;
+
+       if (freq > 0.0 && dc > 0.0) {
+               period = 1000000 / freq;
+               on = period * dc / 100;
+               off = period - on;
+
+               if (on >= 1000000) {
+                       pulse.gp_pulse_on.tv_sec = on / 1000000;
+                       on -= pulse.gp_pulse_on.tv_sec * 1000000;
+                       pulse.gp_pulse_on.tv_usec = on;
+               } else {
+                       pulse.gp_pulse_on.tv_sec = 0;
+                       pulse.gp_pulse_on.tv_usec = on;
+               }
+               if (off >= 1000000) {
+                       pulse.gp_pulse_off.tv_sec = off / 1000000;
+                       off -= pulse.gp_pulse_off.tv_sec * 1000000;
+                       pulse.gp_pulse_off.tv_usec = off;
+               } else {
+                       pulse.gp_pulse_off.tv_sec = 0;
+                       pulse.gp_pulse_off.tv_usec = off;
+               }
+       } else {        /* gpio(4) defaults */
+               freq = 1.0;
+               dc = 50.0;
+       }
+
+       if (ioctl(devfd, GPIOPULSE, &pulse) == -1)
+               err(EXIT_FAILURE, "GPIOPULSE");
+
+       if (quiet)
+               return;
+
+       if (gp_name)
+               printf("pin %s: pulse at %.f Hz with a %.f%% duty cylce\n",
+                   gp_name, freq, dc);
+       else
+               printf("pin %d: pulse at %.f Hz with a %.f%% duty cylce\n",
+                   pin, freq, dc);
+}
+
+static void
 gpioset(int pin, char *name, int fl, char *alias)
 {
        struct gpio_set set;
@@ -314,7 +386,7 @@ gpiounset(int pin, char *name)
 }
 
 static void
-devattach(char *dvname, int offset, u_int32_t mask)
+devattach(char *dvname, int offset, uint32_t mask)
 {
        struct gpio_attach attach;
 
@@ -345,6 +417,8 @@ usage(void)
        progname = getprogname();
        fprintf(stderr, "usage: %s [-q] device [pin] [0 | 1 | 2 | "
            "on | off | toggle]\n", progname);
+       fprintf(stderr, "usage: %s [-q] device pin pulse [frequency "
+           "[duty cycle]]\n", progname);
        fprintf(stderr, "       %s [-q] device pin set [flags] [name]\n",
            progname);
        fprintf(stderr, "       %s [-q] device pin unset\n", progname);


Home | Main Index | Thread Index | Old Index