1*57ef84d0Smbalmer /* $NetBSD: gpioctl.c,v 1.9 2011/08/12 08:02:33 mbalmer Exp $ */ 232eeaba5Smbalmer 3825ff184Sjmcneill /* 4*57ef84d0Smbalmer * Copyright (c) 2008, 2010 Marc Balmer <mbalmer@openbsd.org> 5825ff184Sjmcneill * Copyright (c) 2004 Alexander Yurchenko <grange@openbsd.org> 6825ff184Sjmcneill * 7825ff184Sjmcneill * Permission to use, copy, modify, and distribute this software for any 8825ff184Sjmcneill * purpose with or without fee is hereby granted, provided that the above 9825ff184Sjmcneill * copyright notice and this permission notice appear in all copies. 10825ff184Sjmcneill * 11825ff184Sjmcneill * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12825ff184Sjmcneill * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13825ff184Sjmcneill * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14825ff184Sjmcneill * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15825ff184Sjmcneill * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16825ff184Sjmcneill * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17825ff184Sjmcneill * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18825ff184Sjmcneill */ 19825ff184Sjmcneill 20825ff184Sjmcneill /* 21825ff184Sjmcneill * Program to control GPIO devices. 22825ff184Sjmcneill */ 23825ff184Sjmcneill 24825ff184Sjmcneill #include <sys/types.h> 25825ff184Sjmcneill #include <sys/gpio.h> 26825ff184Sjmcneill #include <sys/ioctl.h> 27825ff184Sjmcneill 28825ff184Sjmcneill #include <err.h> 2932eeaba5Smbalmer #include <errno.h> 30825ff184Sjmcneill #include <fcntl.h> 3132eeaba5Smbalmer #include <limits.h> 3232eeaba5Smbalmer #include <paths.h> 33825ff184Sjmcneill #include <stdio.h> 34825ff184Sjmcneill #include <stdlib.h> 35825ff184Sjmcneill #include <string.h> 36825ff184Sjmcneill #include <unistd.h> 37825ff184Sjmcneill 38825ff184Sjmcneill 3932eeaba5Smbalmer char *dev; 4032eeaba5Smbalmer int devfd = -1; 4132eeaba5Smbalmer int quiet = 0; 42825ff184Sjmcneill 43*57ef84d0Smbalmer static void getinfo(void); 44*57ef84d0Smbalmer static void gpioread(int, char *); 45*57ef84d0Smbalmer static void gpiowrite(int, char *, int); 46*57ef84d0Smbalmer static void gpioset(int pin, char *name, int flags, char *alias); 47*57ef84d0Smbalmer static void gpiounset(int pin, char *name); 48*57ef84d0Smbalmer static void devattach(char *, int, u_int32_t); 49*57ef84d0Smbalmer static void devdetach(char *); 50*57ef84d0Smbalmer static void usage(void); 5132eeaba5Smbalmer 5232eeaba5Smbalmer extern long long strtonum(const char *numstr, long long minval, 5332eeaba5Smbalmer long long maxval, const char **errstrp); 5432eeaba5Smbalmer 5532eeaba5Smbalmer const struct bitstr { 56825ff184Sjmcneill unsigned int mask; 57825ff184Sjmcneill const char *string; 58825ff184Sjmcneill } pinflags[] = { 59825ff184Sjmcneill { GPIO_PIN_INPUT, "in" }, 60825ff184Sjmcneill { GPIO_PIN_OUTPUT, "out" }, 61825ff184Sjmcneill { GPIO_PIN_INOUT, "inout" }, 62825ff184Sjmcneill { GPIO_PIN_OPENDRAIN, "od" }, 63825ff184Sjmcneill { GPIO_PIN_PUSHPULL, "pp" }, 64825ff184Sjmcneill { GPIO_PIN_TRISTATE, "tri" }, 65825ff184Sjmcneill { GPIO_PIN_PULLUP, "pu" }, 66cfebb94aSxtraeme { GPIO_PIN_PULLDOWN, "pd" }, 67cfebb94aSxtraeme { GPIO_PIN_INVIN, "iin" }, 6832eeaba5Smbalmer { GPIO_PIN_INVOUT, "iout" }, 6998c90767Smbalmer { GPIO_PIN_PULSATE, "pulsate" }, 70825ff184Sjmcneill { 0, NULL }, 71825ff184Sjmcneill }; 72825ff184Sjmcneill 73825ff184Sjmcneill int 74825ff184Sjmcneill main(int argc, char *argv[]) 75825ff184Sjmcneill { 7632eeaba5Smbalmer const struct bitstr *bs; 7732eeaba5Smbalmer int pin, ch, n, fl = 0, value = 0; 7832eeaba5Smbalmer const char *errstr; 79825ff184Sjmcneill char *ep; 8032eeaba5Smbalmer int ga_offset = -1; 8132eeaba5Smbalmer u_int32_t ga_mask = 0; 8232eeaba5Smbalmer long lval; 8332eeaba5Smbalmer char *nam = NULL; 8432eeaba5Smbalmer char devn[32]; 85825ff184Sjmcneill 8632eeaba5Smbalmer while ((ch = getopt(argc, argv, "q")) != -1) 87825ff184Sjmcneill switch (ch) { 88825ff184Sjmcneill case 'q': 89825ff184Sjmcneill quiet = 1; 90825ff184Sjmcneill break; 91825ff184Sjmcneill default: 92825ff184Sjmcneill usage(); 93825ff184Sjmcneill /* NOTREACHED */ 94825ff184Sjmcneill } 95825ff184Sjmcneill argc -= optind; 96825ff184Sjmcneill argv += optind; 97825ff184Sjmcneill 9832eeaba5Smbalmer if (argc < 1) 99825ff184Sjmcneill usage(); 10032eeaba5Smbalmer dev = argv[0]; 10132eeaba5Smbalmer 10232eeaba5Smbalmer if (strncmp(_PATH_DEV, dev, sizeof(_PATH_DEV) - 1)) { 10332eeaba5Smbalmer (void)snprintf(devn, sizeof(devn), "%s%s", _PATH_DEV, dev); 10432eeaba5Smbalmer dev = devn; 10532eeaba5Smbalmer } 10632eeaba5Smbalmer 10732eeaba5Smbalmer if ((devfd = open(dev, O_RDWR)) == -1) 10832eeaba5Smbalmer err(EXIT_FAILURE, "%s", dev); 10932eeaba5Smbalmer 11032eeaba5Smbalmer if (argc == 1) { 11132eeaba5Smbalmer getinfo(); 11232eeaba5Smbalmer return EXIT_SUCCESS; 11332eeaba5Smbalmer } 11432eeaba5Smbalmer 11532eeaba5Smbalmer if (!strcmp(argv[1], "attach")) { 11632eeaba5Smbalmer char *driver, *offset, *mask; 11732eeaba5Smbalmer 11832eeaba5Smbalmer if (argc != 5) 11932eeaba5Smbalmer usage(); 12032eeaba5Smbalmer 12132eeaba5Smbalmer driver = argv[2]; 12232eeaba5Smbalmer offset = argv[3]; 12332eeaba5Smbalmer mask = argv[4]; 12432eeaba5Smbalmer 12532eeaba5Smbalmer ga_offset = strtonum(offset, 0, INT_MAX, &errstr); 12632eeaba5Smbalmer if (errstr) 12732eeaba5Smbalmer errx(EXIT_FAILURE, "offset is %s: %s", errstr, offset); 12832eeaba5Smbalmer lval = strtol(mask, &ep, 0); 12932eeaba5Smbalmer if (*mask == '\0' || *ep != '\0') 13032eeaba5Smbalmer errx(EXIT_FAILURE, "invalid mask (not a number)"); 13132eeaba5Smbalmer if ((errno == ERANGE && (lval == LONG_MAX 13232eeaba5Smbalmer || lval == LONG_MIN)) || (unsigned long)lval > UINT_MAX) 13332eeaba5Smbalmer errx(EXIT_FAILURE, "mask out of range"); 13432eeaba5Smbalmer ga_mask = lval; 13532eeaba5Smbalmer devattach(driver, ga_offset, ga_mask); 13632eeaba5Smbalmer return EXIT_SUCCESS; 13732eeaba5Smbalmer } else if (!strcmp(argv[1], "detach")) { 13832eeaba5Smbalmer if (argc != 3) 13932eeaba5Smbalmer usage(); 14032eeaba5Smbalmer devdetach(argv[2]); 14132eeaba5Smbalmer } else { 14232eeaba5Smbalmer char *nm = NULL; 14332eeaba5Smbalmer 14432eeaba5Smbalmer /* expecting a pin number or name */ 14532eeaba5Smbalmer pin = strtonum(argv[1], 0, INT_MAX, &errstr); 14632eeaba5Smbalmer if (errstr) 14732eeaba5Smbalmer nm = argv[1]; /* try named pin */ 14832eeaba5Smbalmer if (argc > 2) { 14932eeaba5Smbalmer if (!strcmp(argv[2], "set")) { 15032eeaba5Smbalmer for (n = 3; n < argc; n++) { 15132eeaba5Smbalmer for (bs = pinflags; bs->string != NULL; 15232eeaba5Smbalmer bs++) { 15332eeaba5Smbalmer if (!strcmp(argv[n], 15432eeaba5Smbalmer bs->string)) { 15532eeaba5Smbalmer fl |= bs->mask; 15632eeaba5Smbalmer break; 15732eeaba5Smbalmer } 15832eeaba5Smbalmer } 15932eeaba5Smbalmer if (bs->string == NULL) 16032eeaba5Smbalmer nam = argv[n]; 16132eeaba5Smbalmer } 16232eeaba5Smbalmer gpioset(pin, nm, fl, nam); 16332eeaba5Smbalmer } else if (!strcmp(argv[2], "unset")) { 16432eeaba5Smbalmer gpiounset(pin, nm); 16532eeaba5Smbalmer } else { 16632eeaba5Smbalmer value = strtonum(argv[2], INT_MIN, INT_MAX, 16732eeaba5Smbalmer &errstr); 16832eeaba5Smbalmer if (errstr) { 16932eeaba5Smbalmer if (!strcmp(argv[2], "on")) 17032eeaba5Smbalmer value = 1; 17132eeaba5Smbalmer else if (!strcmp(argv[2], "off")) 17232eeaba5Smbalmer value = 0; 17332eeaba5Smbalmer else if (!strcmp(argv[2], "toggle")) 17432eeaba5Smbalmer value = 2; 17532eeaba5Smbalmer else 17632eeaba5Smbalmer errx(EXIT_FAILURE, 17732eeaba5Smbalmer "%s: invalid value", 17832eeaba5Smbalmer argv[2]); 17932eeaba5Smbalmer } 18032eeaba5Smbalmer gpiowrite(pin, nm, value); 18132eeaba5Smbalmer } 18232eeaba5Smbalmer } else 18332eeaba5Smbalmer gpioread(pin, nm); 184825ff184Sjmcneill } 185825ff184Sjmcneill 1869b5c4f42Sxtraeme return EXIT_SUCCESS; 187825ff184Sjmcneill } 188825ff184Sjmcneill 189*57ef84d0Smbalmer static void 190825ff184Sjmcneill getinfo(void) 191825ff184Sjmcneill { 192825ff184Sjmcneill struct gpio_info info; 193825ff184Sjmcneill 194825ff184Sjmcneill if (ioctl(devfd, GPIOINFO, &info) == -1) 1959b5c4f42Sxtraeme err(EXIT_FAILURE, "GPIOINFO"); 196825ff184Sjmcneill 197825ff184Sjmcneill if (quiet) 198825ff184Sjmcneill return; 199825ff184Sjmcneill 20032eeaba5Smbalmer printf("%s: %d pins\n", dev, info.gpio_npins); 201825ff184Sjmcneill } 202825ff184Sjmcneill 203*57ef84d0Smbalmer static void 20432eeaba5Smbalmer gpioread(int pin, char *gp_name) 205825ff184Sjmcneill { 20632eeaba5Smbalmer struct gpio_req req; 207825ff184Sjmcneill 20832eeaba5Smbalmer memset(&req, 0, sizeof(req)); 20932eeaba5Smbalmer if (gp_name != NULL) 21032eeaba5Smbalmer strlcpy(req.gp_name, gp_name, sizeof(req.gp_name)); 21132eeaba5Smbalmer else 21232eeaba5Smbalmer req.gp_pin = pin; 21332eeaba5Smbalmer 21432eeaba5Smbalmer if (ioctl(devfd, GPIOREAD, &req) == -1) 21532eeaba5Smbalmer err(EXIT_FAILURE, "GPIOREAD"); 216825ff184Sjmcneill 217825ff184Sjmcneill if (quiet) 218825ff184Sjmcneill return; 219825ff184Sjmcneill 22032eeaba5Smbalmer if (gp_name) 22132eeaba5Smbalmer printf("pin %s: state %d\n", gp_name, req.gp_value); 22232eeaba5Smbalmer else 22332eeaba5Smbalmer printf("pin %d: state %d\n", pin, req.gp_value); 224825ff184Sjmcneill } 225825ff184Sjmcneill 226*57ef84d0Smbalmer static void 22732eeaba5Smbalmer gpiowrite(int pin, char *gp_name, int value) 228825ff184Sjmcneill { 22932eeaba5Smbalmer struct gpio_req req; 230825ff184Sjmcneill 231825ff184Sjmcneill if (value < 0 || value > 2) 2329b5c4f42Sxtraeme errx(EXIT_FAILURE, "%d: invalid value", value); 233825ff184Sjmcneill 23432eeaba5Smbalmer memset(&req, 0, sizeof(req)); 23532eeaba5Smbalmer if (gp_name != NULL) 23632eeaba5Smbalmer strlcpy(req.gp_name, gp_name, sizeof(req.gp_name)); 23732eeaba5Smbalmer else 23832eeaba5Smbalmer req.gp_pin = pin; 23932eeaba5Smbalmer req.gp_value = (value == 0 ? GPIO_PIN_LOW : GPIO_PIN_HIGH); 240825ff184Sjmcneill if (value < 2) { 24132eeaba5Smbalmer if (ioctl(devfd, GPIOWRITE, &req) == -1) 24232eeaba5Smbalmer err(EXIT_FAILURE, "GPIOWRITE"); 243825ff184Sjmcneill } else { 24432eeaba5Smbalmer if (ioctl(devfd, GPIOTOGGLE, &req) == -1) 24532eeaba5Smbalmer err(EXIT_FAILURE, "GPIOTOGGLE"); 246825ff184Sjmcneill } 247825ff184Sjmcneill 248825ff184Sjmcneill if (quiet) 249825ff184Sjmcneill return; 250825ff184Sjmcneill 25132eeaba5Smbalmer if (gp_name) 25232eeaba5Smbalmer printf("pin %s: state %d -> %d\n", gp_name, req.gp_value, 25332eeaba5Smbalmer (value < 2 ? value : 1 - req.gp_value)); 25432eeaba5Smbalmer else 25532eeaba5Smbalmer printf("pin %d: state %d -> %d\n", pin, req.gp_value, 25632eeaba5Smbalmer (value < 2 ? value : 1 - req.gp_value)); 257825ff184Sjmcneill } 258825ff184Sjmcneill 259*57ef84d0Smbalmer static void 26032eeaba5Smbalmer gpioset(int pin, char *name, int fl, char *alias) 261825ff184Sjmcneill { 26232eeaba5Smbalmer struct gpio_set set; 263825ff184Sjmcneill const struct bitstr *bs; 264825ff184Sjmcneill 26532eeaba5Smbalmer memset(&set, 0, sizeof(set)); 26632eeaba5Smbalmer if (name != NULL) 26732eeaba5Smbalmer strlcpy(set.gp_name, name, sizeof(set.gp_name)); 26832eeaba5Smbalmer else 26932eeaba5Smbalmer set.gp_pin = pin; 27032eeaba5Smbalmer set.gp_flags = fl; 27132eeaba5Smbalmer 27232eeaba5Smbalmer if (alias != NULL) 27332eeaba5Smbalmer strlcpy(set.gp_name2, alias, sizeof(set.gp_name2)); 27432eeaba5Smbalmer 27532eeaba5Smbalmer if (ioctl(devfd, GPIOSET, &set) == -1) 27632eeaba5Smbalmer err(EXIT_FAILURE, "GPIOSET"); 277825ff184Sjmcneill 278825ff184Sjmcneill if (quiet) 279825ff184Sjmcneill return; 280825ff184Sjmcneill 28132eeaba5Smbalmer if (name != NULL) 28232eeaba5Smbalmer printf("pin %s: caps:", name); 28332eeaba5Smbalmer else 284825ff184Sjmcneill printf("pin %d: caps:", pin); 285825ff184Sjmcneill for (bs = pinflags; bs->string != NULL; bs++) 28632eeaba5Smbalmer if (set.gp_caps & bs->mask) 287825ff184Sjmcneill printf(" %s", bs->string); 288825ff184Sjmcneill printf(", flags:"); 289825ff184Sjmcneill for (bs = pinflags; bs->string != NULL; bs++) 29032eeaba5Smbalmer if (set.gp_flags & bs->mask) 291825ff184Sjmcneill printf(" %s", bs->string); 292825ff184Sjmcneill if (fl > 0) { 293825ff184Sjmcneill printf(" ->"); 294825ff184Sjmcneill for (bs = pinflags; bs->string != NULL; bs++) 295825ff184Sjmcneill if (fl & bs->mask) 296825ff184Sjmcneill printf(" %s", bs->string); 297825ff184Sjmcneill } 298825ff184Sjmcneill printf("\n"); 299825ff184Sjmcneill } 300825ff184Sjmcneill 301*57ef84d0Smbalmer static void 30232eeaba5Smbalmer gpiounset(int pin, char *name) 30332eeaba5Smbalmer { 30432eeaba5Smbalmer struct gpio_set set; 30532eeaba5Smbalmer 30632eeaba5Smbalmer memset(&set, 0, sizeof(set)); 30732eeaba5Smbalmer if (name != NULL) 30832eeaba5Smbalmer strlcpy(set.gp_name, name, sizeof(set.gp_name)); 30932eeaba5Smbalmer else 31032eeaba5Smbalmer set.gp_pin = pin; 31132eeaba5Smbalmer 31232eeaba5Smbalmer if (ioctl(devfd, GPIOUNSET, &set) == -1) 31332eeaba5Smbalmer err(EXIT_FAILURE, "GPIOUNSET"); 31432eeaba5Smbalmer } 31532eeaba5Smbalmer 316*57ef84d0Smbalmer static void 31732eeaba5Smbalmer devattach(char *dvname, int offset, u_int32_t mask) 31832eeaba5Smbalmer { 31932eeaba5Smbalmer struct gpio_attach attach; 32032eeaba5Smbalmer 32132eeaba5Smbalmer memset(&attach, 0, sizeof(attach)); 32232eeaba5Smbalmer strlcpy(attach.ga_dvname, dvname, sizeof(attach.ga_dvname)); 32332eeaba5Smbalmer attach.ga_offset = offset; 32432eeaba5Smbalmer attach.ga_mask = mask; 32532eeaba5Smbalmer if (ioctl(devfd, GPIOATTACH, &attach) == -1) 32632eeaba5Smbalmer err(EXIT_FAILURE, "GPIOATTACH"); 32732eeaba5Smbalmer } 32832eeaba5Smbalmer 329*57ef84d0Smbalmer static void 33032eeaba5Smbalmer devdetach(char *dvname) 33132eeaba5Smbalmer { 33232eeaba5Smbalmer struct gpio_attach attach; 33332eeaba5Smbalmer 33432eeaba5Smbalmer memset(&attach, 0, sizeof(attach)); 33532eeaba5Smbalmer strlcpy(attach.ga_dvname, dvname, sizeof(attach.ga_dvname)); 33632eeaba5Smbalmer if (ioctl(devfd, GPIODETACH, &attach) == -1) 33732eeaba5Smbalmer err(EXIT_FAILURE, "GPIODETACH"); 33832eeaba5Smbalmer } 33932eeaba5Smbalmer 340*57ef84d0Smbalmer static void 341825ff184Sjmcneill usage(void) 342825ff184Sjmcneill { 343*57ef84d0Smbalmer const char *progname; 34432eeaba5Smbalmer 345*57ef84d0Smbalmer progname = getprogname(); 34632eeaba5Smbalmer fprintf(stderr, "usage: %s [-q] device [pin] [0 | 1 | 2 | " 347*57ef84d0Smbalmer "on | off | toggle]\n", progname); 34832eeaba5Smbalmer fprintf(stderr, " %s [-q] device pin set [flags] [name]\n", 349*57ef84d0Smbalmer progname); 350*57ef84d0Smbalmer fprintf(stderr, " %s [-q] device pin unset\n", progname); 35132eeaba5Smbalmer fprintf(stderr, " %s [-q] device attach device offset mask\n", 352*57ef84d0Smbalmer progname); 353*57ef84d0Smbalmer fprintf(stderr, " %s [-q] device detach device\n", progname); 354825ff184Sjmcneill 3559b5c4f42Sxtraeme exit(EXIT_FAILURE); 356825ff184Sjmcneill } 357