Reworked gpioctl(8) command to reflect recent gpio(4) changes.

This commit is contained in:
mbalmer 2009-07-25 16:18:09 +00:00
parent 8964d40bc5
commit 32eeaba5d3
4 changed files with 402 additions and 187 deletions

View File

@ -1,7 +1,7 @@
# $NetBSD: Makefile,v 1.3 2009/04/22 15:23:03 lukem Exp $
# $NetBSD: Makefile,v 1.4 2009/07/25 16:18:09 mbalmer Exp $
PROG= gpioctl
SRCS= gpioctl.c
SRCS= gpioctl.c strtonum.c
MAN= gpioctl.8

View File

@ -1,6 +1,6 @@
.\" $NetBSD: gpioctl.8,v 1.4 2008/01/09 15:56:27 xtraeme Exp $
.\" $OpenBSD: gpioctl.8,v 1.5 2004/12/02 05:11:40 grange Exp $
.\" $NetBSD: gpioctl.8,v 1.5 2009/07/25 16:18:09 mbalmer Exp $
.\"
.\" Copyright (c) 2009 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,66 +15,71 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd January 9, 2008
.Dd July 15, 2009
.Dt GPIOCTL 8
.Os
.Sh NAME
.Nm gpioctl
.Nd control GPIO devices
.Sh SYNOPSIS
.Nm
.Op Fl hq
.Op Fl d Ar device
.Op Ar pin
.Op Ar 0 | 1 | 2
.Nm
.Op Fl hq
.Op Fl d Ar device
.Fl c
.Nm gpioctl
.Op Fl q
.Ar device
attach
.Ar device
.Ar offset
.Ar mask
.Nm gpioctl
.Op Fl q
.Ar device
detach
.Ar device
.Nm gpioctl
.Op Fl q
.Ar device
.Ar pin
.Op Ar 0 | 1 | 2
.Nm gpioctl
.Op Fl q
.Ar device
.Ar pin
.Op Ar on | off | toggle
.Nm gpioctl
.Op Fl q
.Ar device
.Ar pin
set
.Op Ar flags
.Op Ar name
.Nm gpioctl
.Op Fl q
.Ar device
.Ar pin
unset
.Sh DESCRIPTION
The
.Nm
program allows manipulation of
.Tn GPIO
program allows manipulation of GPIO
(General Purpose Input/Output) device pins.
Such devices can be either part of the chipset or embedded
.Tn CPU ,
Such devices can be either part of the chipset or embedded CPU,
or a separate chip.
The usual way of using
.Tn GPIO
is to connect some simple devices such as LEDs, 1-wire thermal sensors,
etc., to its pins.
The usual way of using GPIO
is to connect some simple devices such as LEDs and 1-wire thermal sensors
to its pins.
.Pp
Each
.Tn GPIO
device has an associated device file in the
Each GPIO device has an associated device file in the
.Pa /dev
directory.
By default
.Nm
uses
.Pa /dev/gpio0 ,
which corresponds to the first found
.Tn GPIO
device in the system.
If more than one
.Tn GPIO
device is present, an alternative device file can be specified with the
.Fl d
option in order to access a particular
.Tn GPIO
device.
.Ar device
can be specified with or without the
.Pa /dev
prefix.
For example,
.Pa /dev/gpio0
or
.Pa gpio0 .
.Pp
When executed without any arguments,
.Nm
reads information about the
.Tn GPIO
device and displays it.
.Pp
.Tn GPIO
pins can be either
GPIO pins can be either
.Dq read
or
.Dq written
@ -82,9 +87,7 @@ with the values of logical 0 or 1.
If only a
.Ar pin
number is specified on the command line, the pin state will be read
from the
.Tn GPIO
controller and displayed.
from the GPIO controller and displayed.
To write to a pin, a value must be specified after the
.Ar pin
number.
@ -92,15 +95,28 @@ Values can be either 0 or 1.
A value of 2 has a special meaning: it
.Dq toggles
the pin, i.e. changes its state to the opposite.
Instead of the numerical values, the word
.Ar on ,
.Ar off ,
or
.Ar toggle
can be used.
.Pp
Each pin can be configured with different flags with the
.Fl c
option.
The following configuration flags are supported by the
.Tn GPIO
framework:
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.
Besides using individual pins, device drivers that use GPIO pins can be
attached to a
.Xr gpio 4
device using the
.Nm
command.
.Pp
.Bl -tag -width XXXXXXX -offset indent -compact
The following configuration
.Ar flags
are supported by the GPIO framework:
.Pp
.Bl -tag -width Ds -offset indent -compact
.It in
input direction
.It out
@ -123,31 +139,23 @@ invert input
invert output
.El
.Pp
Note that not all the flags can be supported by the particular
.Tn GPIO
controller.
The list of supported flags is always displayed when executing
.Nm
with the
.Fl c
option.
If only a
.Ar pin
number is specified on the command line, the current pin flags will be
displayed.
To change pin flags, a new flags set separated by spaces must be
specified after the
.Ar pin
number.
Note that not all the flags may be supported by the particular GPIO controller.
.Pp
The
.Fl q
option causes
When executed with only the
.Xr gpio 4
device name as argument,
.Nm
to operate quietly i.e. nothing is printed to stdout.
The
.Fl h
option displays a usage summary.
reads information about the
.Tn GPIO
device and displays it.
At securelevel 0 the number of physically available pins is displayed,
at higher securelevels the number of configured (set) pins is displayed.
.Pp
The options are as follows:
.Bl -tag -width Ds
.It Fl q
Operate quietly i.e. nothing is printed to stdout.
.El
.Sh FILES
.Bl -tag -width "/dev/gpiou" -compact
.It /dev/gpio Ns Ar u
@ -158,17 +166,33 @@ file.
.Sh EXAMPLES
Configure pin 20 to have push-pull output:
.Pp
.Dl # gpioctl -c 20 out pp
.Dl # gpioctl gpio0 20 set out pp
.Pp
Write logical 1 to pin 20:
.Pp
.Dl # gpioctl 20 1
.Dl # gpioctl gpio0 20 1
.Pp
Attach a
.Xr onewire 4
bus on a
.Xr gpioow 4
device on pin 4:
.Pp
.Dl # gpioctl gpio0 attach gpioow 4 0x01
.Pp
Detach the gpioow0 device:
.Pp
.Dl # gpioctl gpio0 detach gpioow0
.Pp
Configure pin 5 as output and name it error_led:
.Pp
.Dl # gpioctl gpio0 5 set out error_led
.Pp
Toggle the error_led:
.Pp
.Dl # gpioctl gpio0 error_led 2
.Sh SEE ALSO
.Xr elansc 4 ,
.Xr gcscpcib 4 ,
.Xr gpio 4 ,
.Xr gscpcib 4 ,
.Xr nsclpcsio 4
.Xr gpio 4
.Sh HISTORY
The
.Nm
@ -181,3 +205,5 @@ The
.Nm
program was written by
.An Alexander Yurchenko Aq grange@openbsd.org .
Device attachment was added by
.An Marc Balmer Aq marc@msys.ch .

View File

@ -1,6 +1,7 @@
/* $NetBSD: gpioctl.c,v 1.5 2008/01/09 16:08:33 xtraeme Exp $ */
/* $OpenBSD: gpioctl.c,v 1.2 2004/08/08 00:05:09 deraadt Exp $ */
/* $NetBSD: gpioctl.c,v 1.6 2009/07/25 16:18:09 mbalmer Exp $ */
/*
* Copyright (c) 2008 Marc Balmer <mbalmer@openbsd.org>
* Copyright (c) 2004 Alexander Yurchenko <grange@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
@ -25,25 +26,34 @@
#include <sys/ioctl.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <paths.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define _PATH_DEV_GPIO "/dev/gpio0"
static const char *device = _PATH_DEV_GPIO;
static int devfd = -1;
static int quiet = 0;
char *dev;
int devfd = -1;
int quiet = 0;
static void getinfo(void);
static void pinread(int);
static void pinwrite(int, int);
static void pinctl(int, char *[], int);
static void usage(void);
void getinfo(void);
void gpioread(int, char *);
void gpiowrite(int, char *, int);
void gpioset(int pin, char *name, int flags, char *alias);
void gpiounset(int pin, char *name);
void devattach(char *, int, u_int32_t);
void devdetach(char *);
static const struct bitstr {
__dead void usage(void);
extern long long strtonum(const char *numstr, long long minval,
long long maxval, const char **errstrp);
const struct bitstr {
unsigned int mask;
const char *string;
} pinflags[] = {
@ -56,33 +66,28 @@ static const struct bitstr {
{ GPIO_PIN_PULLUP, "pu" },
{ GPIO_PIN_PULLDOWN, "pd" },
{ GPIO_PIN_INVIN, "iin" },
{ GPIO_PIN_INVOUT, "iiout" },
{ GPIO_PIN_INVOUT, "iout" },
{ 0, NULL },
};
int
main(int argc, char *argv[])
{
int ch;
const struct bitstr *bs;
int pin, ch, n, fl = 0, value = 0;
const char *errstr;
char *ep;
int do_ctl = 0;
int pin = 0, value = 0;
int ga_offset = -1;
u_int32_t ga_mask = 0;
long lval;
char *nam = NULL;
char devn[32];
setprogname(argv[0]);
while ((ch = getopt(argc, argv, "cd:hq")) != -1)
while ((ch = getopt(argc, argv, "q")) != -1)
switch (ch) {
case 'c':
do_ctl = 1;
break;
case 'd':
device = optarg;
break;
case 'q':
quiet = 1;
break;
case 'h':
case '?':
default:
usage();
/* NOTREACHED */
@ -90,129 +95,199 @@ main(int argc, char *argv[])
argc -= optind;
argv += optind;
if (argc > 0) {
pin = strtol(argv[0], &ep, 10);
if (*argv[0] == '\0' || *ep != '\0' || pin < 0)
errx(EXIT_FAILURE, "%s: invalid pin", argv[0]);
if (argc < 1)
usage();
dev = argv[0];
if (strncmp(_PATH_DEV, dev, sizeof(_PATH_DEV) - 1)) {
(void)snprintf(devn, sizeof(devn), "%s%s", _PATH_DEV, dev);
dev = devn;
}
if ((devfd = open(device, O_RDWR)) == -1)
err(EXIT_FAILURE, "%s", device);
if ((devfd = open(dev, O_RDWR)) == -1)
err(EXIT_FAILURE, "%s", dev);
if (argc == 0 && !do_ctl) {
if (argc == 1) {
getinfo();
} else if (argc == 1) {
if (do_ctl)
pinctl(pin, NULL, 0);
else
pinread(pin);
} else if (argc > 1) {
if (do_ctl) {
pinctl(pin, argv + 1, argc - 1);
} else {
value = strtol(argv[1], &ep, 10);
if (*argv[1] == '\0' || *ep != '\0')
errx(EXIT_FAILURE, "%s: invalid value",
argv[1]);
pinwrite(pin, value);
}
return EXIT_SUCCESS;
}
if (!strcmp(argv[1], "attach")) {
char *driver, *offset, *mask;
if (argc != 5)
usage();
driver = argv[2];
offset = argv[3];
mask = argv[4];
ga_offset = strtonum(offset, 0, INT_MAX, &errstr);
if (errstr)
errx(EXIT_FAILURE, "offset is %s: %s", errstr, offset);
lval = strtol(mask, &ep, 0);
if (*mask == '\0' || *ep != '\0')
errx(EXIT_FAILURE, "invalid mask (not a number)");
if ((errno == ERANGE && (lval == LONG_MAX
|| lval == LONG_MIN)) || (unsigned long)lval > UINT_MAX)
errx(EXIT_FAILURE, "mask out of range");
ga_mask = lval;
devattach(driver, ga_offset, ga_mask);
return EXIT_SUCCESS;
} else if (!strcmp(argv[1], "detach")) {
if (argc != 3)
usage();
devdetach(argv[2]);
} else {
usage();
/* NOTREACHED */
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 (!strcmp(argv[2], "set")) {
for (n = 3; n < argc; n++) {
for (bs = pinflags; bs->string != NULL;
bs++) {
if (!strcmp(argv[n],
bs->string)) {
fl |= bs->mask;
break;
}
}
if (bs->string == NULL)
nam = argv[n];
}
gpioset(pin, nm, fl, nam);
} else if (!strcmp(argv[2], "unset")) {
gpiounset(pin, nm);
} else {
value = strtonum(argv[2], INT_MIN, INT_MAX,
&errstr);
if (errstr) {
if (!strcmp(argv[2], "on"))
value = 1;
else if (!strcmp(argv[2], "off"))
value = 0;
else if (!strcmp(argv[2], "toggle"))
value = 2;
else
errx(EXIT_FAILURE,
"%s: invalid value",
argv[2]);
}
gpiowrite(pin, nm, value);
}
} else
gpioread(pin, nm);
}
return EXIT_SUCCESS;
}
static void
void
getinfo(void)
{
struct gpio_info info;
memset(&info, 0, sizeof(info));
if (ioctl(devfd, GPIOINFO, &info) == -1)
err(EXIT_FAILURE, "GPIOINFO");
if (quiet)
return;
printf("%s: %d pins\n", device, info.gpio_npins);
printf("%s: %d pins\n", dev, info.gpio_npins);
}
static void
pinread(int pin)
void
gpioread(int pin, char *gp_name)
{
struct gpio_pin_op op;
struct gpio_req req;
memset(&op, 0, sizeof(op));
op.gp_pin = pin;
if (ioctl(devfd, GPIOPINREAD, &op) == -1)
err(EXIT_FAILURE, "GPIOPINREAD");
memset(&req, 0, sizeof(req));
if (gp_name != NULL)
strlcpy(req.gp_name, gp_name, sizeof(req.gp_name));
else
req.gp_pin = pin;
if (ioctl(devfd, GPIOREAD, &req) == -1)
err(EXIT_FAILURE, "GPIOREAD");
if (quiet)
return;
printf("pin %d: state %d\n", pin, op.gp_value);
if (gp_name)
printf("pin %s: state %d\n", gp_name, req.gp_value);
else
printf("pin %d: state %d\n", pin, req.gp_value);
}
static void
pinwrite(int pin, int value)
void
gpiowrite(int pin, char *gp_name, int value)
{
struct gpio_pin_op op;
struct gpio_req req;
if (value < 0 || value > 2)
errx(EXIT_FAILURE, "%d: invalid value", value);
memset(&op, 0, sizeof(op));
op.gp_pin = pin;
op.gp_value = (value == 0 ? GPIO_PIN_LOW : GPIO_PIN_HIGH);
memset(&req, 0, sizeof(req));
if (gp_name != NULL)
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 (ioctl(devfd, GPIOPINWRITE, &op) == -1)
err(EXIT_FAILURE, "GPIOPINWRITE");
if (ioctl(devfd, GPIOWRITE, &req) == -1)
err(EXIT_FAILURE, "GPIOWRITE");
} else {
if (ioctl(devfd, GPIOPINTOGGLE, &op) == -1)
err(EXIT_FAILURE, "GPIOPINTOGGLE");
if (ioctl(devfd, GPIOTOGGLE, &req) == -1)
err(EXIT_FAILURE, "GPIOTOGGLE");
}
if (quiet)
return;
printf("pin %d: state %d -> %d\n", pin, op.gp_value,
(value < 2 ? value : 1 - op.gp_value));
if (gp_name)
printf("pin %s: state %d -> %d\n", gp_name, req.gp_value,
(value < 2 ? value : 1 - req.gp_value));
else
printf("pin %d: state %d -> %d\n", pin, req.gp_value,
(value < 2 ? value : 1 - req.gp_value));
}
static void
pinctl(int pin, char *flags[], int nflags)
void
gpioset(int pin, char *name, int fl, char *alias)
{
struct gpio_pin_ctl ctl;
int fl = 0;
struct gpio_set set;
const struct bitstr *bs;
int i;
memset(&ctl, 0, sizeof(ctl));
ctl.gp_pin = pin;
if (flags != NULL) {
for (i = 0; i < nflags; i++)
for (bs = pinflags; bs->string != NULL; bs++)
if (strcmp(flags[i], bs->string) == 0) {
fl |= bs->mask;
break;
}
}
ctl.gp_flags = fl;
if (ioctl(devfd, GPIOPINCTL, &ctl) == -1)
err(EXIT_FAILURE, "GPIOPINCTL");
memset(&set, 0, sizeof(set));
if (name != NULL)
strlcpy(set.gp_name, name, sizeof(set.gp_name));
else
set.gp_pin = pin;
set.gp_flags = fl;
if (alias != NULL)
strlcpy(set.gp_name2, alias, sizeof(set.gp_name2));
if (ioctl(devfd, GPIOSET, &set) == -1)
err(EXIT_FAILURE, "GPIOSET");
if (quiet)
return;
printf("pin %d: caps:", pin);
if (name != NULL)
printf("pin %s: caps:", name);
else
printf("pin %d: caps:", pin);
for (bs = pinflags; bs->string != NULL; bs++)
if (ctl.gp_caps & bs->mask)
if (set.gp_caps & bs->mask)
printf(" %s", bs->string);
printf(", flags:");
for (bs = pinflags; bs->string != NULL; bs++)
if (ctl.gp_flags & bs->mask)
if (set.gp_flags & bs->mask)
printf(" %s", bs->string);
if (fl > 0) {
printf(" ->");
@ -223,13 +298,58 @@ pinctl(int pin, char *flags[], int nflags)
printf("\n");
}
static void
void
gpiounset(int pin, char *name)
{
struct gpio_set set;
memset(&set, 0, sizeof(set));
if (name != NULL)
strlcpy(set.gp_name, name, sizeof(set.gp_name));
else
set.gp_pin = pin;
if (ioctl(devfd, GPIOUNSET, &set) == -1)
err(EXIT_FAILURE, "GPIOUNSET");
}
void
devattach(char *dvname, int offset, u_int32_t mask)
{
struct gpio_attach attach;
memset(&attach, 0, sizeof(attach));
strlcpy(attach.ga_dvname, dvname, sizeof(attach.ga_dvname));
attach.ga_offset = offset;
attach.ga_mask = mask;
if (ioctl(devfd, GPIOATTACH, &attach) == -1)
err(EXIT_FAILURE, "GPIOATTACH");
}
void
devdetach(char *dvname)
{
struct gpio_attach attach;
memset(&attach, 0, sizeof(attach));
strlcpy(attach.ga_dvname, dvname, sizeof(attach.ga_dvname));
if (ioctl(devfd, GPIODETACH, &attach) == -1)
err(EXIT_FAILURE, "GPIODETACH");
}
void
usage(void)
{
fprintf(stderr, "usage: %s [-hq] [-d device] [pin] [0 | 1 | 2]\n",
getprogname());
fprintf(stderr, " %s [-hq] [-d device] -c pin [flags]\n",
getprogname());
extern char *__progname;
fprintf(stderr, "usage: %s [-q] device [pin] [0 | 1 | 2 | "
"on | off | toggle]\n", __progname);
fprintf(stderr, " %s [-q] device pin set [flags] [name]\n",
__progname);
fprintf(stderr, " %s [-q] device pin unset\n", __progname);
fprintf(stderr, " %s [-q] device attach device offset mask\n",
__progname);
fprintf(stderr, " %s [-q] device detach device\n", __progname);
exit(EXIT_FAILURE);
}

View File

@ -0,0 +1,69 @@
/* $NetBSD: strtonum.c,v 1.1 2009/07/25 16:18:09 mbalmer Exp $ */
/* $OpenBSD: strtonum.c,v 1.6 2004/08/03 19:38:01 millert Exp $ */
/*
* Copyright (c) 2004 Ted Unangst and Todd Miller
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#define INVALID 1
#define TOOSMALL 2
#define TOOLARGE 3
long long strtonum(const char *numstr, long long minval, long long maxval,
const char **errstrp);
long long
strtonum(const char *numstr, long long minval, long long maxval,
const char **errstrp)
{
long long ll = 0;
char *ep;
int error = 0;
struct errval {
const char *errstr;
int err;
} ev[4] = {
{ NULL, 0 },
{ "invalid", EINVAL },
{ "too small", ERANGE },
{ "too large", ERANGE },
};
ev[0].err = errno;
errno = 0;
if (minval > maxval)
error = INVALID;
else {
ll = strtoll(numstr, &ep, 10);
if (numstr == ep || *ep != '\0')
error = INVALID;
else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval)
error = TOOSMALL;
else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval)
error = TOOLARGE;
}
if (errstrp != NULL)
*errstrp = ev[error].errstr;
errno = ev[error].err;
if (error)
ll = 0;
return (ll);
}