summaryrefslogtreecommitdiff
path: root/usr.sbin/gpioctl
diff options
context:
space:
mode:
authormbalmer <mbalmer@NetBSD.org>2011-08-28 07:48:50 +0000
committermbalmer <mbalmer@NetBSD.org>2011-08-28 07:48:50 +0000
commitda668cdf91cdc35a69a5162bd24e7a5eca2bda4e (patch)
tree06c3388b9e3be3a02e3b03181b9f6ea020ecd9c0 /usr.sbin/gpioctl
parent1b3a256adc36e54133a189084ba985a7dca7fd3d (diff)
Add a new ioctl, GPIOPULSE to gpio(4) to allow for pulsing a pin.
If a pin can pulse in hardware, that will be used, else it will be pulsed in software. There is no way yet to set the pulse frequency for pins that pulse in hardware. While here, make the code mpsafe and allow more than one thread in the driver (access to ioctl is serialized).
Diffstat (limited to 'usr.sbin/gpioctl')
-rw-r--r--usr.sbin/gpioctl/gpioctl.829
-rw-r--r--usr.sbin/gpioctl/gpioctl.c95
2 files changed, 108 insertions, 16 deletions
diff --git a/usr.sbin/gpioctl/gpioctl.8 b/usr.sbin/gpioctl/gpioctl.8
index 61d4501e4bc..b7b05c0a3ff 100644
--- a/usr.sbin/gpioctl/gpioctl.8
+++ b/usr.sbin/gpioctl/gpioctl.8
@@ -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 <marc@msys.ch>
+.\" Copyright (c) 2009, 2010, 2011 Marc Balmer <marc@msys.ch>
.\" Copyright (c) 2004 Alexander Yurchenko <grange@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 @@ 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 specified after the
.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 .
-Device attachment was added by
+Device attachment and pulsing was added by
.An Marc Balmer Aq marc@msys.ch .
diff --git a/usr.sbin/gpioctl/gpioctl.c b/usr.sbin/gpioctl/gpioctl.c
index b7cd799b917..bf37d1da33a 100644
--- a/usr.sbin/gpioctl/gpioctl.c
+++ b/usr.sbin/gpioctl/gpioctl.c
@@ -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 <mbalmer@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 @@ 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 +75,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 +101,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 +144,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 +166,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 +250,9 @@ gpiowrite(int pin, char *gp_name, int value)
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 @@ gpiowrite(int pin, char *gp_name, int value)
}
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 @@ 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 +416,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);