Module Name:    src
Committed By:   mbalmer
Date:           Sun Aug 28 07:48:51 UTC 2011

Modified Files:
        src/share/man/man4: gpio.4
        src/sys/dev/gpio: gpio.c gpiovar.h
        src/sys/sys: gpio.h
        src/usr.sbin/gpioctl: gpioctl.8 gpioctl.c

Log Message:
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).


To generate a diff of this commit:
cvs rdiff -u -r1.18 -r1.19 src/share/man/man4/gpio.4
cvs rdiff -u -r1.35 -r1.36 src/sys/dev/gpio/gpio.c
cvs rdiff -u -r1.11 -r1.12 src/sys/dev/gpio/gpiovar.h
cvs rdiff -u -r1.8 -r1.9 src/sys/sys/gpio.h
cvs rdiff -u -r1.8 -r1.9 src/usr.sbin/gpioctl/gpioctl.8
cvs rdiff -u -r1.10 -r1.11 src/usr.sbin/gpioctl/gpioctl.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/share/man/man4/gpio.4
diff -u src/share/man/man4/gpio.4:1.18 src/share/man/man4/gpio.4:1.19
--- src/share/man/man4/gpio.4:1.18	Mon Mar 22 18:58:31 2010
+++ src/share/man/man4/gpio.4	Sun Aug 28 07:48:50 2011
@@ -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 <gra...@openbsd.org>
@@ -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

Index: src/sys/dev/gpio/gpio.c
diff -u src/sys/dev/gpio/gpio.c:1.35 src/sys/dev/gpio/gpio.c:1.36
--- src/sys/dev/gpio/gpio.c:1.35	Fri Aug 12 08:00:52 2011
+++ src/sys/dev/gpio/gpio.c	Sun Aug 28 07:48:50 2011
@@ -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)
 {
+	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 +508,9 @@
 	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 +530,9 @@
 
 		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 +550,68 @@
 		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 +648,10 @@
 		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 +669,7 @@
 				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 +689,6 @@
 			}
 		}
 		return EINVAL;
-		break;
 	case GPIOSET:
 		if (kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET,
 		    NULL, NULL, NULL, NULL))
@@ -574,12 +696,11 @@
 
 		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 +750,9 @@
 			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: src/sys/dev/gpio/gpiovar.h
diff -u src/sys/dev/gpio/gpiovar.h:1.11 src/sys/dev/gpio/gpiovar.h:1.12
--- src/sys/dev/gpio/gpiovar.h:1.11	Fri Aug 12 08:00:52 2011
+++ src/sys/dev/gpio/gpiovar.h	Sun Aug 28 07:48:50 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: gpiovar.h,v 1.11 2011/08/12 08:00:52 mbalmer Exp $ */
+/* $NetBSD: gpiovar.h,v 1.12 2011/08/28 07:48:50 mbalmer Exp $ */
 /*	$OpenBSD: gpiovar.h,v 1.3 2006/01/14 12:33:49 grange Exp $	*/
 
 /*
@@ -35,18 +35,22 @@
 
 /* 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 @@
 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 @@
 	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: src/sys/sys/gpio.h
diff -u src/sys/sys/gpio.h:1.8 src/sys/sys/gpio.h:1.9
--- src/sys/sys/gpio.h:1.8	Thu Jun 23 00:46:37 2011
+++ src/sys/sys/gpio.h	Sun Aug 28 07:48:50 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: gpio.h,v 1.8 2011/06/23 00:46:37 matt Exp $ */
+/* $NetBSD: gpio.h,v 1.9 2011/08/28 07:48:50 mbalmer Exp $ */
 /*	$OpenBSD: gpio.h,v 1.7 2008/11/26 14:51:20 mbalmer Exp $	*/
 /*
  * Copyright (c) 2009 Marc Balmer <m...@msys.ch>
@@ -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 @@
 
 /* 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 @@
 
 /* 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 @@
 #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: src/usr.sbin/gpioctl/gpioctl.8
diff -u src/usr.sbin/gpioctl/gpioctl.8:1.8 src/usr.sbin/gpioctl/gpioctl.8:1.9
--- src/usr.sbin/gpioctl/gpioctl.8:1.8	Fri Aug 12 08:02:33 2011
+++ src/usr.sbin/gpioctl/gpioctl.8	Sun Aug 28 07:48:50 2011
@@ -1,6 +1,6 @@
-.\" $NetBSD: gpioctl.8,v 1.8 2011/08/12 08:02:33 mbalmer Exp $
+.\" $NetBSD: gpioctl.8,v 1.9 2011/08/28 07:48:50 mbalmer Exp $
 .\"
-.\" Copyright (c) 2009, 2010 Marc Balmer <m...@msys.ch>
+.\" Copyright (c) 2009, 2010, 2011 Marc Balmer <m...@msys.ch>
 .\" Copyright (c) 2004 Alexander Yurchenko <gra...@openbsd.org>
 .\"
 .\" 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 @@
 .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 @@
 .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 @@
 .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 @@
 .Nm
 program was written by
 .An Alexander Yurchenko Aq gra...@openbsd.org .
-Device attachment was added by
+Device attachment and pulsing was added by
 .An Marc Balmer Aq m...@msys.ch .

Index: src/usr.sbin/gpioctl/gpioctl.c
diff -u src/usr.sbin/gpioctl/gpioctl.c:1.10 src/usr.sbin/gpioctl/gpioctl.c:1.11
--- src/usr.sbin/gpioctl/gpioctl.c:1.10	Fri Aug 12 08:06:23 2011
+++ src/usr.sbin/gpioctl/gpioctl.c	Sun Aug 28 07:48:50 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: gpioctl.c,v 1.10 2011/08/12 08:06:23 mbalmer Exp $ */
+/* $NetBSD: gpioctl.c,v 1.11 2011/08/28 07:48:50 mbalmer Exp $ */
 
 /*
  * Copyright (c) 2008, 2010 Marc Balmer <mbal...@netbsd.org>
@@ -32,10 +32,10 @@
 #include <paths.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <time.h>
 #include <string.h>
 #include <unistd.h>
 
-
 char *dev;
 int devfd = -1;
 int quiet = 0;
@@ -43,9 +43,10 @@
 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 +75,12 @@
 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 +101,8 @@
 		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 +144,12 @@
 		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 +166,24 @@
 				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 +250,9 @@
 		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 +272,62 @@
 }
 
 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 +385,7 @@
 }
 
 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 +416,8 @@
 	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);

Reply via email to