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)



>> The attached diff adds the ability to pulse a pin with a set frequency
>> and duty cycle.  A new gpio(4) ioctl, "GPIOPULSE", and a new gpioctl(8)
>> command line option "pulse" are added.
>>
>> Whereas the gpioctl(8) command line utility accepts the freqeuncy in
>> hertz and the duty cycle in percent, these values are converted to two
>> "struct timeval"s for the ioctl call.  gpio(4) accepts an "on" and "off"
>> period as "struct timeval" which are then converted to ticks using
>> tvtohz(9).
>>
>> The pulsing in software is realised using callout(9)s, if the underlying
>> hardware signals that it can pulse the pin in hardware, the pin is just
>> set to GPIO_PIN_HIGH and no callouts are used.  There is no way at the
>> moment to program the "on" and "off" periods for pins that pulse in
>> hardware (implementing that is left as an exercise to the reader...)
> 
> Find attached revised version that uses only one callout and clears
> pending callouts on device detach.  I also removed a few superflous
> tests, as noticed by rmind.
> 
> If you have any GPIO drivers as modules, e.g. gpiosim(4), make sure to
> rebuild them as internal data structures have changed.  I can tell from
> experience^Wpain that failing to do so leads to panics...

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.
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      23 Aug 2011 06:17:46 -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      23 Aug 2011 06:17:46 -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,7 +71,7 @@ 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;
 };
 
@@ -88,13 +92,13 @@ 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);
 
 #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 23 Aug 2011 06:17:46 -0000
@@ -26,14 +26,17 @@ __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/queue.h>
 #include <sys/kauth.h>
 #ifdef _MODULE
@@ -59,6 +62,7 @@ struct gpio_softc {
        int                      sc_npins;      /* number of pins */
 
        int                      sc_opened;
+       kmutex_t                 sc_mtx;
        LIST_HEAD(, gpio_dev)    sc_devs;       /* devices */
        LIST_HEAD(, gpio_name)   sc_names;      /* named pins */
 };
@@ -73,6 +77,7 @@ 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 *);
 
 /* Old API */
 static int     gpio_ioctl_oapi(struct gpio_softc *, u_long, void *, int,
@@ -88,7 +93,7 @@ dev_type_ioctl(gpioioctl);
 
 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 +149,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 +157,14 @@ 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_NONE);
 
        /*
         * Attach all devices that can be connected to the GPIO pins
@@ -165,11 +176,22 @@ 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);
 #if 0
        int maj, mn;
 
@@ -228,7 +250,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 +272,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 +342,7 @@ gpio_pin_caps(void *gpio, struct gpio_pi
 }
 
 int
-gpio_npins(u_int32_t mask)
+gpio_npins(uint32_t mask)
 {
        int npins, i;
 
@@ -340,22 +362,17 @@ gpioopen(dev_t dev, int flag, int mode, 
        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;
-       }
+       mutex_enter(&sc->sc_mtx);
+       if (sc->sc_opened)
+               return EBUSY;
 
-       sc->sc_opened = 1;
+       ret = gpiobus_open(sc->sc_gc, sc->sc_dev);
+       if (!ret)
+               sc->sc_opened = 1;
+       mutex_exit(&sc->sc_mtx);
 
-       return 0;
+       return ret;
 }
 
 int
@@ -364,7 +381,6 @@ 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;
 
@@ -382,6 +398,26 @@ 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)
 {
@@ -392,6 +428,7 @@ gpioioctl(dev_t dev, u_long cmd, void *d
        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;
        device_t dv;
@@ -427,11 +464,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 +486,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 +506,70 @@ 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;
+
+               if (sc->sc_pins[pin].pin_mapped)
+                       return EBUSY;
+
+               if (!(sc->sc_pins[pin].pin_flags & GPIO_PIN_SET) &&
+                   kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET,
+                   NULL, NULL, NULL, NULL))
+                       return EPERM;
+
+               if (sc->sc_pins[pin].pin_flags & GPIO_PIN_PULSATE) {
+                       gpiobus_pin_write(gc, pin, GPIO_PIN_HIGH);
+                       sc->sc_pins[pin].pin_state = GPIO_PIN_PULSE;
+                       return 0;
+               }
+
+               if (sc->sc_pins[pin].pin_state & GPIO_PIN_PULSE)
+                       callout_halt(&sc->sc_pins[pin].pin_pulse, NULL);
+
+               sc->sc_pins[pin].pin_gc = gc;
+
+               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;
+               }
+               gpiobus_pin_write(gc, pin, GPIO_PIN_HIGH);
+               sc->sc_pins[pin].pin_state = GPIO_PIN_HIGH | GPIO_PIN_PULSE;
+               callout_schedule(&sc->sc_pins[pin].pin_pulse,
+                   sc->sc_pins[pin].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)
@@ -574,12 +654,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 +708,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       23 Aug 2011 06:17:47 -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  23 Aug 2011 06:17:47 -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  23 Aug 2011 06:17:47 -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