tech-kern archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
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...)
Comments?
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 21 Aug 2011 11:38:27 -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 */
/* 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 */
+ u_int32_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 21 Aug 2011 11:38:27 -0000
@@ -35,18 +35,23 @@ 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_on; /* on period while pulsing */
+ callout_t pin_pulse_off; /* off period while pulsing */
+ int pin_ticks_on;
+ int pin_ticks_off;
+ 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 *);
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 21 Aug 2011 11:38:28 -0000
@@ -26,12 +26,14 @@ __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/queue.h>
@@ -73,6 +75,8 @@ 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_on(void *);
+static void gpio_pulse_off(void *);
/* Old API */
static int gpio_ioctl_oapi(struct gpio_softc *, u_long, void *, int,
@@ -382,6 +386,26 @@ gpio_pinbyname(struct gpio_softc *sc, ch
return -1;
}
+static void
+gpio_pulse_off(void *arg)
+{
+ struct gpio_pin *pin;
+
+ pin = (struct gpio_pin *)arg;
+ gpiobus_pin_write(pin->pin_gc, pin->pin_num, GPIO_PIN_LOW);
+ callout_schedule(&pin->pin_pulse_off, pin->pin_ticks_off);
+}
+
+static void
+gpio_pulse_on(void *arg)
+{
+ struct gpio_pin *pin;
+
+ pin = (struct gpio_pin *)arg;
+ gpiobus_pin_write(pin->pin_gc, pin->pin_num, GPIO_PIN_HIGH);
+ callout_schedule(&pin->pin_pulse_on, pin->pin_ticks_on);
+}
+
int
gpioioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
{
@@ -392,6 +416,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;
@@ -479,6 +504,62 @@ gpioioctl(dev_t dev, u_long cmd, void *d
/* 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);
+ if (pin == -1)
+ return EINVAL;
+ } 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_stop(&sc->sc_pins[pin].pin_pulse_on);
+ callout_stop(&sc->sc_pins[pin].pin_pulse_off);
+ callout_destroy(&sc->sc_pins[pin].pin_pulse_on);
+ callout_destroy(&sc->sc_pins[pin].pin_pulse_off);
+ }
+ sc->sc_pins[pin].pin_gc = gc;
+ callout_init(&sc->sc_pins[pin].pin_pulse_on, 0);
+ callout_init(&sc->sc_pins[pin].pin_pulse_off, 0);
+
+ 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;
+ }
+ callout_setfunc(&sc->sc_pins[pin].pin_pulse_on,
+ gpio_pulse_off, &sc->sc_pins[pin]);
+ callout_setfunc(&sc->sc_pins[pin].pin_pulse_off,
+ gpio_pulse_on, &sc->sc_pins[pin]);
+ gpiobus_pin_write(gc, pin, GPIO_PIN_HIGH);
+ callout_schedule(&sc->sc_pins[pin].pin_pulse_on,
+ sc->sc_pins[pin].pin_ticks_on);
+ sc->sc_pins[pin].pin_state = GPIO_PIN_PULSE;
+ break;
case GPIOTOGGLE:
if ((flag & FWRITE) == 0)
return EBADF;
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 21 Aug 2011 11:38:28 -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 21 Aug 2011 11:38:28 -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 21 Aug 2011 11:38:28 -0000
@@ -32,6 +32,7 @@
#include <paths.h>
#include <stdio.h>
#include <stdlib.h>
+#include <time.h>
#include <string.h>
#include <unistd.h>
@@ -43,6 +44,7 @@ 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);
@@ -74,6 +76,7 @@ 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;
@@ -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;
@@ -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