1*98c90767Smbalmer /* $NetBSD: gpioctl.c,v 1.8 2009/09/25 20:27:50 mbalmer Exp $ */ 232eeaba5Smbalmer 3825ff184Sjmcneill /* 432eeaba5Smbalmer * Copyright (c) 2008 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 4332eeaba5Smbalmer void getinfo(void); 4432eeaba5Smbalmer void gpioread(int, char *); 4532eeaba5Smbalmer void gpiowrite(int, char *, int); 4632eeaba5Smbalmer void gpioset(int pin, char *name, int flags, char *alias); 4732eeaba5Smbalmer void gpiounset(int pin, char *name); 4832eeaba5Smbalmer void devattach(char *, int, u_int32_t); 4932eeaba5Smbalmer void devdetach(char *); 50825ff184Sjmcneill 5132eeaba5Smbalmer __dead void usage(void); 5232eeaba5Smbalmer 5332eeaba5Smbalmer extern long long strtonum(const char *numstr, long long minval, 5432eeaba5Smbalmer long long maxval, const char **errstrp); 5532eeaba5Smbalmer 5632eeaba5Smbalmer const struct bitstr { 57825ff184Sjmcneill unsigned int mask; 58825ff184Sjmcneill const char *string; 59825ff184Sjmcneill } pinflags[] = { 60825ff184Sjmcneill { GPIO_PIN_INPUT, "in" }, 61825ff184Sjmcneill { GPIO_PIN_OUTPUT, "out" }, 62825ff184Sjmcneill { GPIO_PIN_INOUT, "inout" }, 63825ff184Sjmcneill { GPIO_PIN_OPENDRAIN, "od" }, 64825ff184Sjmcneill { GPIO_PIN_PUSHPULL, "pp" }, 65825ff184Sjmcneill { GPIO_PIN_TRISTATE, "tri" }, 66825ff184Sjmcneill { GPIO_PIN_PULLUP, "pu" }, 67cfebb94aSxtraeme { GPIO_PIN_PULLDOWN, "pd" }, 68cfebb94aSxtraeme { GPIO_PIN_INVIN, "iin" }, 6932eeaba5Smbalmer { GPIO_PIN_INVOUT, "iout" }, 70*98c90767Smbalmer { GPIO_PIN_PULSATE, "pulsate" }, 71825ff184Sjmcneill { 0, NULL }, 72825ff184Sjmcneill }; 73825ff184Sjmcneill 74825ff184Sjmcneill int 75825ff184Sjmcneill main(int argc, char *argv[]) 76825ff184Sjmcneill { 7732eeaba5Smbalmer const struct bitstr *bs; 7832eeaba5Smbalmer int pin, ch, n, fl = 0, value = 0; 7932eeaba5Smbalmer const char *errstr; 80825ff184Sjmcneill char *ep; 8132eeaba5Smbalmer int ga_offset = -1; 8232eeaba5Smbalmer u_int32_t ga_mask = 0; 8332eeaba5Smbalmer long lval; 8432eeaba5Smbalmer char *nam = NULL; 8532eeaba5Smbalmer char devn[32]; 86825ff184Sjmcneill 8732eeaba5Smbalmer while ((ch = getopt(argc, argv, "q")) != -1) 88825ff184Sjmcneill switch (ch) { 89825ff184Sjmcneill case 'q': 90825ff184Sjmcneill quiet = 1; 91825ff184Sjmcneill break; 92825ff184Sjmcneill default: 93825ff184Sjmcneill usage(); 94825ff184Sjmcneill /* NOTREACHED */ 95825ff184Sjmcneill } 96825ff184Sjmcneill argc -= optind; 97825ff184Sjmcneill argv += optind; 98825ff184Sjmcneill 9932eeaba5Smbalmer if (argc < 1) 100825ff184Sjmcneill usage(); 10132eeaba5Smbalmer dev = argv[0]; 10232eeaba5Smbalmer 10332eeaba5Smbalmer if (strncmp(_PATH_DEV, dev, sizeof(_PATH_DEV) - 1)) { 10432eeaba5Smbalmer (void)snprintf(devn, sizeof(devn), "%s%s", _PATH_DEV, dev); 10532eeaba5Smbalmer dev = devn; 10632eeaba5Smbalmer } 10732eeaba5Smbalmer 10832eeaba5Smbalmer if ((devfd = open(dev, O_RDWR)) == -1) 10932eeaba5Smbalmer err(EXIT_FAILURE, "%s", dev); 11032eeaba5Smbalmer 11132eeaba5Smbalmer if (argc == 1) { 11232eeaba5Smbalmer getinfo(); 11332eeaba5Smbalmer return EXIT_SUCCESS; 11432eeaba5Smbalmer } 11532eeaba5Smbalmer 11632eeaba5Smbalmer if (!strcmp(argv[1], "attach")) { 11732eeaba5Smbalmer char *driver, *offset, *mask; 11832eeaba5Smbalmer 11932eeaba5Smbalmer if (argc != 5) 12032eeaba5Smbalmer usage(); 12132eeaba5Smbalmer 12232eeaba5Smbalmer driver = argv[2]; 12332eeaba5Smbalmer offset = argv[3]; 12432eeaba5Smbalmer mask = argv[4]; 12532eeaba5Smbalmer 12632eeaba5Smbalmer ga_offset = strtonum(offset, 0, INT_MAX, &errstr); 12732eeaba5Smbalmer if (errstr) 12832eeaba5Smbalmer errx(EXIT_FAILURE, "offset is %s: %s", errstr, offset); 12932eeaba5Smbalmer lval = strtol(mask, &ep, 0); 13032eeaba5Smbalmer if (*mask == '\0' || *ep != '\0') 13132eeaba5Smbalmer errx(EXIT_FAILURE, "invalid mask (not a number)"); 13232eeaba5Smbalmer if ((errno == ERANGE && (lval == LONG_MAX 13332eeaba5Smbalmer || lval == LONG_MIN)) || (unsigned long)lval > UINT_MAX) 13432eeaba5Smbalmer errx(EXIT_FAILURE, "mask out of range"); 13532eeaba5Smbalmer ga_mask = lval; 13632eeaba5Smbalmer devattach(driver, ga_offset, ga_mask); 13732eeaba5Smbalmer return EXIT_SUCCESS; 13832eeaba5Smbalmer } else if (!strcmp(argv[1], "detach")) { 13932eeaba5Smbalmer if (argc != 3) 14032eeaba5Smbalmer usage(); 14132eeaba5Smbalmer devdetach(argv[2]); 14232eeaba5Smbalmer } else { 14332eeaba5Smbalmer char *nm = NULL; 14432eeaba5Smbalmer 14532eeaba5Smbalmer /* expecting a pin number or name */ 14632eeaba5Smbalmer pin = strtonum(argv[1], 0, INT_MAX, &errstr); 14732eeaba5Smbalmer if (errstr) 14832eeaba5Smbalmer nm = argv[1]; /* try named pin */ 14932eeaba5Smbalmer if (argc > 2) { 15032eeaba5Smbalmer if (!strcmp(argv[2], "set")) { 15132eeaba5Smbalmer for (n = 3; n < argc; n++) { 15232eeaba5Smbalmer for (bs = pinflags; bs->string != NULL; 15332eeaba5Smbalmer bs++) { 15432eeaba5Smbalmer if (!strcmp(argv[n], 15532eeaba5Smbalmer bs->string)) { 15632eeaba5Smbalmer fl |= bs->mask; 15732eeaba5Smbalmer break; 15832eeaba5Smbalmer } 15932eeaba5Smbalmer } 16032eeaba5Smbalmer if (bs->string == NULL) 16132eeaba5Smbalmer nam = argv[n]; 16232eeaba5Smbalmer } 16332eeaba5Smbalmer gpioset(pin, nm, fl, nam); 16432eeaba5Smbalmer } else if (!strcmp(argv[2], "unset")) { 16532eeaba5Smbalmer gpiounset(pin, nm); 16632eeaba5Smbalmer } else { 16732eeaba5Smbalmer value = strtonum(argv[2], INT_MIN, INT_MAX, 16832eeaba5Smbalmer &errstr); 16932eeaba5Smbalmer if (errstr) { 17032eeaba5Smbalmer if (!strcmp(argv[2], "on")) 17132eeaba5Smbalmer value = 1; 17232eeaba5Smbalmer else if (!strcmp(argv[2], "off")) 17332eeaba5Smbalmer value = 0; 17432eeaba5Smbalmer else if (!strcmp(argv[2], "toggle")) 17532eeaba5Smbalmer value = 2; 17632eeaba5Smbalmer else 17732eeaba5Smbalmer errx(EXIT_FAILURE, 17832eeaba5Smbalmer "%s: invalid value", 17932eeaba5Smbalmer argv[2]); 18032eeaba5Smbalmer } 18132eeaba5Smbalmer gpiowrite(pin, nm, value); 18232eeaba5Smbalmer } 18332eeaba5Smbalmer } else 18432eeaba5Smbalmer gpioread(pin, nm); 185825ff184Sjmcneill } 186825ff184Sjmcneill 1879b5c4f42Sxtraeme return EXIT_SUCCESS; 188825ff184Sjmcneill } 189825ff184Sjmcneill 19032eeaba5Smbalmer void 191825ff184Sjmcneill getinfo(void) 192825ff184Sjmcneill { 193825ff184Sjmcneill struct gpio_info info; 194825ff184Sjmcneill 195825ff184Sjmcneill if (ioctl(devfd, GPIOINFO, &info) == -1) 1969b5c4f42Sxtraeme err(EXIT_FAILURE, "GPIOINFO"); 197825ff184Sjmcneill 198825ff184Sjmcneill if (quiet) 199825ff184Sjmcneill return; 200825ff184Sjmcneill 20132eeaba5Smbalmer printf("%s: %d pins\n", dev, info.gpio_npins); 202825ff184Sjmcneill } 203825ff184Sjmcneill 20432eeaba5Smbalmer void 20532eeaba5Smbalmer gpioread(int pin, char *gp_name) 206825ff184Sjmcneill { 20732eeaba5Smbalmer struct gpio_req req; 208825ff184Sjmcneill 20932eeaba5Smbalmer memset(&req, 0, sizeof(req)); 21032eeaba5Smbalmer if (gp_name != NULL) 21132eeaba5Smbalmer strlcpy(req.gp_name, gp_name, sizeof(req.gp_name)); 21232eeaba5Smbalmer else 21332eeaba5Smbalmer req.gp_pin = pin; 21432eeaba5Smbalmer 21532eeaba5Smbalmer if (ioctl(devfd, GPIOREAD, &req) == -1) 21632eeaba5Smbalmer err(EXIT_FAILURE, "GPIOREAD"); 217825ff184Sjmcneill 218825ff184Sjmcneill if (quiet) 219825ff184Sjmcneill return; 220825ff184Sjmcneill 22132eeaba5Smbalmer if (gp_name) 22232eeaba5Smbalmer printf("pin %s: state %d\n", gp_name, req.gp_value); 22332eeaba5Smbalmer else 22432eeaba5Smbalmer printf("pin %d: state %d\n", pin, req.gp_value); 225825ff184Sjmcneill } 226825ff184Sjmcneill 22732eeaba5Smbalmer void 22832eeaba5Smbalmer gpiowrite(int pin, char *gp_name, int value) 229825ff184Sjmcneill { 23032eeaba5Smbalmer struct gpio_req req; 231825ff184Sjmcneill 232825ff184Sjmcneill if (value < 0 || value > 2) 2339b5c4f42Sxtraeme errx(EXIT_FAILURE, "%d: invalid value", value); 234825ff184Sjmcneill 23532eeaba5Smbalmer memset(&req, 0, sizeof(req)); 23632eeaba5Smbalmer if (gp_name != NULL) 23732eeaba5Smbalmer strlcpy(req.gp_name, gp_name, sizeof(req.gp_name)); 23832eeaba5Smbalmer else 23932eeaba5Smbalmer req.gp_pin = pin; 24032eeaba5Smbalmer req.gp_value = (value == 0 ? GPIO_PIN_LOW : GPIO_PIN_HIGH); 241825ff184Sjmcneill if (value < 2) { 24232eeaba5Smbalmer if (ioctl(devfd, GPIOWRITE, &req) == -1) 24332eeaba5Smbalmer err(EXIT_FAILURE, "GPIOWRITE"); 244825ff184Sjmcneill } else { 24532eeaba5Smbalmer if (ioctl(devfd, GPIOTOGGLE, &req) == -1) 24632eeaba5Smbalmer err(EXIT_FAILURE, "GPIOTOGGLE"); 247825ff184Sjmcneill } 248825ff184Sjmcneill 249825ff184Sjmcneill if (quiet) 250825ff184Sjmcneill return; 251825ff184Sjmcneill 25232eeaba5Smbalmer if (gp_name) 25332eeaba5Smbalmer printf("pin %s: state %d -> %d\n", gp_name, req.gp_value, 25432eeaba5Smbalmer (value < 2 ? value : 1 - req.gp_value)); 25532eeaba5Smbalmer else 25632eeaba5Smbalmer printf("pin %d: state %d -> %d\n", pin, req.gp_value, 25732eeaba5Smbalmer (value < 2 ? value : 1 - req.gp_value)); 258825ff184Sjmcneill } 259825ff184Sjmcneill 26032eeaba5Smbalmer void 26132eeaba5Smbalmer gpioset(int pin, char *name, int fl, char *alias) 262825ff184Sjmcneill { 26332eeaba5Smbalmer struct gpio_set set; 264825ff184Sjmcneill const struct bitstr *bs; 265825ff184Sjmcneill 26632eeaba5Smbalmer memset(&set, 0, sizeof(set)); 26732eeaba5Smbalmer if (name != NULL) 26832eeaba5Smbalmer strlcpy(set.gp_name, name, sizeof(set.gp_name)); 26932eeaba5Smbalmer else 27032eeaba5Smbalmer set.gp_pin = pin; 27132eeaba5Smbalmer set.gp_flags = fl; 27232eeaba5Smbalmer 27332eeaba5Smbalmer if (alias != NULL) 27432eeaba5Smbalmer strlcpy(set.gp_name2, alias, sizeof(set.gp_name2)); 27532eeaba5Smbalmer 27632eeaba5Smbalmer if (ioctl(devfd, GPIOSET, &set) == -1) 27732eeaba5Smbalmer err(EXIT_FAILURE, "GPIOSET"); 278825ff184Sjmcneill 279825ff184Sjmcneill if (quiet) 280825ff184Sjmcneill return; 281825ff184Sjmcneill 28232eeaba5Smbalmer if (name != NULL) 28332eeaba5Smbalmer printf("pin %s: caps:", name); 28432eeaba5Smbalmer else 285825ff184Sjmcneill printf("pin %d: caps:", pin); 286825ff184Sjmcneill for (bs = pinflags; bs->string != NULL; bs++) 28732eeaba5Smbalmer if (set.gp_caps & bs->mask) 288825ff184Sjmcneill printf(" %s", bs->string); 289825ff184Sjmcneill printf(", flags:"); 290825ff184Sjmcneill for (bs = pinflags; bs->string != NULL; bs++) 29132eeaba5Smbalmer if (set.gp_flags & bs->mask) 292825ff184Sjmcneill printf(" %s", bs->string); 293825ff184Sjmcneill if (fl > 0) { 294825ff184Sjmcneill printf(" ->"); 295825ff184Sjmcneill for (bs = pinflags; bs->string != NULL; bs++) 296825ff184Sjmcneill if (fl & bs->mask) 297825ff184Sjmcneill printf(" %s", bs->string); 298825ff184Sjmcneill } 299825ff184Sjmcneill printf("\n"); 300825ff184Sjmcneill } 301825ff184Sjmcneill 30232eeaba5Smbalmer void 30332eeaba5Smbalmer gpiounset(int pin, char *name) 30432eeaba5Smbalmer { 30532eeaba5Smbalmer struct gpio_set set; 30632eeaba5Smbalmer 30732eeaba5Smbalmer memset(&set, 0, sizeof(set)); 30832eeaba5Smbalmer if (name != NULL) 30932eeaba5Smbalmer strlcpy(set.gp_name, name, sizeof(set.gp_name)); 31032eeaba5Smbalmer else 31132eeaba5Smbalmer set.gp_pin = pin; 31232eeaba5Smbalmer 31332eeaba5Smbalmer if (ioctl(devfd, GPIOUNSET, &set) == -1) 31432eeaba5Smbalmer err(EXIT_FAILURE, "GPIOUNSET"); 31532eeaba5Smbalmer } 31632eeaba5Smbalmer 31732eeaba5Smbalmer void 31832eeaba5Smbalmer devattach(char *dvname, int offset, u_int32_t mask) 31932eeaba5Smbalmer { 32032eeaba5Smbalmer struct gpio_attach attach; 32132eeaba5Smbalmer 32232eeaba5Smbalmer memset(&attach, 0, sizeof(attach)); 32332eeaba5Smbalmer strlcpy(attach.ga_dvname, dvname, sizeof(attach.ga_dvname)); 32432eeaba5Smbalmer attach.ga_offset = offset; 32532eeaba5Smbalmer attach.ga_mask = mask; 32632eeaba5Smbalmer if (ioctl(devfd, GPIOATTACH, &attach) == -1) 32732eeaba5Smbalmer err(EXIT_FAILURE, "GPIOATTACH"); 32832eeaba5Smbalmer } 32932eeaba5Smbalmer 33032eeaba5Smbalmer void 33132eeaba5Smbalmer devdetach(char *dvname) 33232eeaba5Smbalmer { 33332eeaba5Smbalmer struct gpio_attach attach; 33432eeaba5Smbalmer 33532eeaba5Smbalmer memset(&attach, 0, sizeof(attach)); 33632eeaba5Smbalmer strlcpy(attach.ga_dvname, dvname, sizeof(attach.ga_dvname)); 33732eeaba5Smbalmer if (ioctl(devfd, GPIODETACH, &attach) == -1) 33832eeaba5Smbalmer err(EXIT_FAILURE, "GPIODETACH"); 33932eeaba5Smbalmer } 34032eeaba5Smbalmer 34132eeaba5Smbalmer void 342825ff184Sjmcneill usage(void) 343825ff184Sjmcneill { 34432eeaba5Smbalmer extern char *__progname; 34532eeaba5Smbalmer 34632eeaba5Smbalmer fprintf(stderr, "usage: %s [-q] device [pin] [0 | 1 | 2 | " 34732eeaba5Smbalmer "on | off | toggle]\n", __progname); 34832eeaba5Smbalmer fprintf(stderr, " %s [-q] device pin set [flags] [name]\n", 34932eeaba5Smbalmer __progname); 35032eeaba5Smbalmer fprintf(stderr, " %s [-q] device pin unset\n", __progname); 35132eeaba5Smbalmer fprintf(stderr, " %s [-q] device attach device offset mask\n", 35232eeaba5Smbalmer __progname); 35332eeaba5Smbalmer fprintf(stderr, " %s [-q] device detach device\n", __progname); 354825ff184Sjmcneill 3559b5c4f42Sxtraeme exit(EXIT_FAILURE); 356825ff184Sjmcneill } 357