1*e27bab72Smlelstv /* $NetBSD: gpioctl.c,v 1.22 2015/12/06 07:31:28 mlelstv Exp $ */ 232eeaba5Smbalmer 3825ff184Sjmcneill /* 41bdc60c3Smbalmer * Copyright (c) 2008, 2010, 2011, 2013 Marc Balmer <mbalmer@NetBSD.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> 35da668cdfSmbalmer #include <time.h> 36825ff184Sjmcneill #include <string.h> 37825ff184Sjmcneill #include <unistd.h> 38825ff184Sjmcneill 39c45de77dSjoerg static char *dev; 40c45de77dSjoerg static int devfd = -1; 41c45de77dSjoerg static int quiet = 0; 421bdc60c3Smbalmer static int state = 0; 43825ff184Sjmcneill 4457ef84d0Smbalmer static void getinfo(void); 4557ef84d0Smbalmer static void gpioread(int, char *); 4657ef84d0Smbalmer static void gpiowrite(int, char *, int); 4757ef84d0Smbalmer static void gpioset(int pin, char *name, int flags, char *alias); 4857ef84d0Smbalmer static void gpiounset(int pin, char *name); 49be149ed8Smbalmer static void devattach(char *, int, uint32_t, uint32_t); 50c45de77dSjoerg __dead static void usage(void); 5132eeaba5Smbalmer 52c45de77dSjoerg static const struct bitstr { 53825ff184Sjmcneill unsigned int mask; 54825ff184Sjmcneill const char *string; 55825ff184Sjmcneill } pinflags[] = { 56825ff184Sjmcneill { GPIO_PIN_INPUT, "in" }, 57825ff184Sjmcneill { GPIO_PIN_OUTPUT, "out" }, 58825ff184Sjmcneill { GPIO_PIN_INOUT, "inout" }, 59825ff184Sjmcneill { GPIO_PIN_OPENDRAIN, "od" }, 60825ff184Sjmcneill { GPIO_PIN_PUSHPULL, "pp" }, 61825ff184Sjmcneill { GPIO_PIN_TRISTATE, "tri" }, 62825ff184Sjmcneill { GPIO_PIN_PULLUP, "pu" }, 63cfebb94aSxtraeme { GPIO_PIN_PULLDOWN, "pd" }, 64cfebb94aSxtraeme { GPIO_PIN_INVIN, "iin" }, 6532eeaba5Smbalmer { GPIO_PIN_INVOUT, "iout" }, 6698c90767Smbalmer { GPIO_PIN_PULSATE, "pulsate" }, 67*e27bab72Smlelstv { GPIO_PIN_ALT0, "alt0" }, 68*e27bab72Smlelstv { GPIO_PIN_ALT1, "alt1" }, 69*e27bab72Smlelstv { GPIO_PIN_ALT2, "alt2" }, 70*e27bab72Smlelstv { GPIO_PIN_ALT3, "alt3" }, 71*e27bab72Smlelstv { GPIO_PIN_ALT4, "alt4" }, 72*e27bab72Smlelstv { GPIO_PIN_ALT5, "alt5" }, 73*e27bab72Smlelstv { GPIO_PIN_ALT6, "alt6" }, 74*e27bab72Smlelstv { GPIO_PIN_ALT7, "alt7" }, 75*e27bab72Smlelstv { GPIO_PIN_ALT7, "events" }, 76*e27bab72Smlelstv { GPIO_PIN_ALT7, "level" }, 77*e27bab72Smlelstv { GPIO_PIN_ALT7, "falling" }, 78*e27bab72Smlelstv { GPIO_PIN_USER, "user" }, 79825ff184Sjmcneill { 0, NULL }, 80825ff184Sjmcneill }; 81825ff184Sjmcneill 82825ff184Sjmcneill int 83825ff184Sjmcneill main(int argc, char *argv[]) 84825ff184Sjmcneill { 8532eeaba5Smbalmer const struct bitstr *bs; 8632eeaba5Smbalmer int pin, ch, n, fl = 0, value = 0; 8732eeaba5Smbalmer const char *errstr; 88825ff184Sjmcneill char *ep; 8932eeaba5Smbalmer int ga_offset = -1; 90da668cdfSmbalmer uint32_t ga_mask = 0; 91be149ed8Smbalmer uint32_t ga_flags = 0; 9232eeaba5Smbalmer long lval; 9332eeaba5Smbalmer char *nam = NULL; 94be149ed8Smbalmer char *flags; 9532eeaba5Smbalmer char devn[32]; 96825ff184Sjmcneill 971bdc60c3Smbalmer while ((ch = getopt(argc, argv, "qs")) != -1) 98825ff184Sjmcneill switch (ch) { 99825ff184Sjmcneill case 'q': 100825ff184Sjmcneill quiet = 1; 101825ff184Sjmcneill break; 1021bdc60c3Smbalmer case 's': 1031bdc60c3Smbalmer quiet = state = 1; 1041bdc60c3Smbalmer break; 105825ff184Sjmcneill default: 106825ff184Sjmcneill usage(); 107825ff184Sjmcneill /* NOTREACHED */ 108825ff184Sjmcneill } 109825ff184Sjmcneill argc -= optind; 110825ff184Sjmcneill argv += optind; 111825ff184Sjmcneill 11232eeaba5Smbalmer if (argc < 1) 113825ff184Sjmcneill usage(); 11432eeaba5Smbalmer dev = argv[0]; 11532eeaba5Smbalmer 11632eeaba5Smbalmer if (strncmp(_PATH_DEV, dev, sizeof(_PATH_DEV) - 1)) { 11732eeaba5Smbalmer (void)snprintf(devn, sizeof(devn), "%s%s", _PATH_DEV, dev); 11832eeaba5Smbalmer dev = devn; 11932eeaba5Smbalmer } 12032eeaba5Smbalmer 12132eeaba5Smbalmer if ((devfd = open(dev, O_RDWR)) == -1) 12232eeaba5Smbalmer err(EXIT_FAILURE, "%s", dev); 12332eeaba5Smbalmer 12432eeaba5Smbalmer if (argc == 1) { 12532eeaba5Smbalmer getinfo(); 12632eeaba5Smbalmer return EXIT_SUCCESS; 12732eeaba5Smbalmer } 12832eeaba5Smbalmer 12932eeaba5Smbalmer if (!strcmp(argv[1], "attach")) { 13032eeaba5Smbalmer char *driver, *offset, *mask; 13132eeaba5Smbalmer 132be149ed8Smbalmer if (argc != 5 && argc != 6) 13332eeaba5Smbalmer usage(); 13432eeaba5Smbalmer 13532eeaba5Smbalmer driver = argv[2]; 13632eeaba5Smbalmer offset = argv[3]; 13732eeaba5Smbalmer mask = argv[4]; 138be149ed8Smbalmer flags = argc == 6 ? argv[5] : NULL; 13932eeaba5Smbalmer 14032eeaba5Smbalmer ga_offset = strtonum(offset, 0, INT_MAX, &errstr); 14132eeaba5Smbalmer if (errstr) 14232eeaba5Smbalmer errx(EXIT_FAILURE, "offset is %s: %s", errstr, offset); 14332eeaba5Smbalmer lval = strtol(mask, &ep, 0); 14432eeaba5Smbalmer if (*mask == '\0' || *ep != '\0') 14532eeaba5Smbalmer errx(EXIT_FAILURE, "invalid mask (not a number)"); 14632eeaba5Smbalmer if ((errno == ERANGE && (lval == LONG_MAX 14732eeaba5Smbalmer || lval == LONG_MIN)) || (unsigned long)lval > UINT_MAX) 14832eeaba5Smbalmer errx(EXIT_FAILURE, "mask out of range"); 14932eeaba5Smbalmer ga_mask = lval; 150be149ed8Smbalmer if (flags != NULL) { 151be149ed8Smbalmer lval = strtol(flags, &ep, 0); 152be149ed8Smbalmer if (*flags == '\0' || *ep != '\0') 153be149ed8Smbalmer errx(EXIT_FAILURE, 1543b72c2b3Smbalmer "invalid flag locator (not a number)"); 155be149ed8Smbalmer if ((errno == ERANGE && (lval == LONG_MAX 156be149ed8Smbalmer || lval == LONG_MIN)) 157be149ed8Smbalmer || (unsigned long)lval > UINT_MAX) 1583b72c2b3Smbalmer errx(EXIT_FAILURE, "flag locator out of range"); 1593b72c2b3Smbalmer 160be149ed8Smbalmer ga_flags = lval; 161be149ed8Smbalmer } 162be149ed8Smbalmer devattach(driver, ga_offset, ga_mask, ga_flags); 16332eeaba5Smbalmer return EXIT_SUCCESS; 16432eeaba5Smbalmer } else { 16532eeaba5Smbalmer char *nm = NULL; 16632eeaba5Smbalmer 16732eeaba5Smbalmer /* expecting a pin number or name */ 16832eeaba5Smbalmer pin = strtonum(argv[1], 0, INT_MAX, &errstr); 16932eeaba5Smbalmer if (errstr) 17032eeaba5Smbalmer nm = argv[1]; /* try named pin */ 17132eeaba5Smbalmer if (argc > 2) { 17232eeaba5Smbalmer if (!strcmp(argv[2], "set")) { 17332eeaba5Smbalmer for (n = 3; n < argc; n++) { 17432eeaba5Smbalmer for (bs = pinflags; bs->string != NULL; 17532eeaba5Smbalmer bs++) { 17632eeaba5Smbalmer if (!strcmp(argv[n], 17732eeaba5Smbalmer bs->string)) { 17832eeaba5Smbalmer fl |= bs->mask; 17932eeaba5Smbalmer break; 18032eeaba5Smbalmer } 18132eeaba5Smbalmer } 18232eeaba5Smbalmer if (bs->string == NULL) 18332eeaba5Smbalmer nam = argv[n]; 18432eeaba5Smbalmer } 18532eeaba5Smbalmer gpioset(pin, nm, fl, nam); 186e680da6bSmbalmer } else if (!strcmp(argv[2], "unset")) 18732eeaba5Smbalmer gpiounset(pin, nm); 188e680da6bSmbalmer else { 18932eeaba5Smbalmer value = strtonum(argv[2], INT_MIN, INT_MAX, 19032eeaba5Smbalmer &errstr); 19132eeaba5Smbalmer if (errstr) { 19232eeaba5Smbalmer if (!strcmp(argv[2], "on")) 193da668cdfSmbalmer value = GPIO_PIN_HIGH; 19432eeaba5Smbalmer else if (!strcmp(argv[2], "off")) 195da668cdfSmbalmer value = GPIO_PIN_LOW; 19632eeaba5Smbalmer else if (!strcmp(argv[2], "toggle")) 19732eeaba5Smbalmer value = 2; 19832eeaba5Smbalmer else 19932eeaba5Smbalmer errx(EXIT_FAILURE, 20032eeaba5Smbalmer "%s: invalid value", 20132eeaba5Smbalmer argv[2]); 20232eeaba5Smbalmer } 20332eeaba5Smbalmer gpiowrite(pin, nm, value); 20432eeaba5Smbalmer } 20532eeaba5Smbalmer } else 20632eeaba5Smbalmer gpioread(pin, nm); 207825ff184Sjmcneill } 2089b5c4f42Sxtraeme return EXIT_SUCCESS; 209825ff184Sjmcneill } 210825ff184Sjmcneill 21157ef84d0Smbalmer static void 212825ff184Sjmcneill getinfo(void) 213825ff184Sjmcneill { 214825ff184Sjmcneill struct gpio_info info; 215825ff184Sjmcneill 216825ff184Sjmcneill if (ioctl(devfd, GPIOINFO, &info) == -1) 2179b5c4f42Sxtraeme err(EXIT_FAILURE, "GPIOINFO"); 218825ff184Sjmcneill 2191bdc60c3Smbalmer if (state) 2201bdc60c3Smbalmer printf("%d\n", info.gpio_npins); 2211bdc60c3Smbalmer 222825ff184Sjmcneill if (quiet) 223825ff184Sjmcneill return; 224825ff184Sjmcneill 22532eeaba5Smbalmer printf("%s: %d pins\n", dev, info.gpio_npins); 226825ff184Sjmcneill } 227825ff184Sjmcneill 22857ef84d0Smbalmer static void 22932eeaba5Smbalmer gpioread(int pin, char *gp_name) 230825ff184Sjmcneill { 23132eeaba5Smbalmer struct gpio_req req; 232825ff184Sjmcneill 23332eeaba5Smbalmer memset(&req, 0, sizeof(req)); 23432eeaba5Smbalmer if (gp_name != NULL) 23532eeaba5Smbalmer strlcpy(req.gp_name, gp_name, sizeof(req.gp_name)); 23632eeaba5Smbalmer else 23732eeaba5Smbalmer req.gp_pin = pin; 23832eeaba5Smbalmer 23932eeaba5Smbalmer if (ioctl(devfd, GPIOREAD, &req) == -1) 24032eeaba5Smbalmer err(EXIT_FAILURE, "GPIOREAD"); 241825ff184Sjmcneill 2421bdc60c3Smbalmer if (state) 2431bdc60c3Smbalmer printf("%d\n", req.gp_value); 2441bdc60c3Smbalmer 245825ff184Sjmcneill if (quiet) 246825ff184Sjmcneill return; 247825ff184Sjmcneill 24832eeaba5Smbalmer if (gp_name) 24932eeaba5Smbalmer printf("pin %s: state %d\n", gp_name, req.gp_value); 25032eeaba5Smbalmer else 25132eeaba5Smbalmer printf("pin %d: state %d\n", pin, req.gp_value); 252825ff184Sjmcneill } 253825ff184Sjmcneill 25457ef84d0Smbalmer static void 25532eeaba5Smbalmer gpiowrite(int pin, char *gp_name, int value) 256825ff184Sjmcneill { 25732eeaba5Smbalmer struct gpio_req req; 258825ff184Sjmcneill 259825ff184Sjmcneill if (value < 0 || value > 2) 2609b5c4f42Sxtraeme errx(EXIT_FAILURE, "%d: invalid value", value); 261825ff184Sjmcneill 26232eeaba5Smbalmer memset(&req, 0, sizeof(req)); 26332eeaba5Smbalmer if (gp_name != NULL) 26432eeaba5Smbalmer strlcpy(req.gp_name, gp_name, sizeof(req.gp_name)); 26532eeaba5Smbalmer else 26632eeaba5Smbalmer req.gp_pin = pin; 267da668cdfSmbalmer 268da668cdfSmbalmer if (value == GPIO_PIN_HIGH || value == GPIO_PIN_LOW) { 269da668cdfSmbalmer req.gp_value = value; 27032eeaba5Smbalmer if (ioctl(devfd, GPIOWRITE, &req) == -1) 27132eeaba5Smbalmer err(EXIT_FAILURE, "GPIOWRITE"); 272825ff184Sjmcneill } else { 27332eeaba5Smbalmer if (ioctl(devfd, GPIOTOGGLE, &req) == -1) 27432eeaba5Smbalmer err(EXIT_FAILURE, "GPIOTOGGLE"); 275825ff184Sjmcneill } 276825ff184Sjmcneill 2771bdc60c3Smbalmer if (state) 2781bdc60c3Smbalmer printf("%d\n", value < 2 ? value : 1 - req.gp_value); 2791bdc60c3Smbalmer 280825ff184Sjmcneill if (quiet) 281825ff184Sjmcneill return; 282825ff184Sjmcneill 28332eeaba5Smbalmer if (gp_name) 28432eeaba5Smbalmer printf("pin %s: state %d -> %d\n", gp_name, req.gp_value, 28532eeaba5Smbalmer (value < 2 ? value : 1 - req.gp_value)); 28632eeaba5Smbalmer else 28732eeaba5Smbalmer printf("pin %d: state %d -> %d\n", pin, req.gp_value, 28832eeaba5Smbalmer (value < 2 ? value : 1 - req.gp_value)); 289825ff184Sjmcneill } 290825ff184Sjmcneill 29157ef84d0Smbalmer static void 29232eeaba5Smbalmer gpioset(int pin, char *name, int fl, char *alias) 293825ff184Sjmcneill { 29432eeaba5Smbalmer struct gpio_set set; 295825ff184Sjmcneill const struct bitstr *bs; 296825ff184Sjmcneill 29732eeaba5Smbalmer memset(&set, 0, sizeof(set)); 29832eeaba5Smbalmer if (name != NULL) 29932eeaba5Smbalmer strlcpy(set.gp_name, name, sizeof(set.gp_name)); 30032eeaba5Smbalmer else 30132eeaba5Smbalmer set.gp_pin = pin; 30232eeaba5Smbalmer set.gp_flags = fl; 30332eeaba5Smbalmer 30432eeaba5Smbalmer if (alias != NULL) 30532eeaba5Smbalmer strlcpy(set.gp_name2, alias, sizeof(set.gp_name2)); 30632eeaba5Smbalmer 30732eeaba5Smbalmer if (ioctl(devfd, GPIOSET, &set) == -1) 30832eeaba5Smbalmer err(EXIT_FAILURE, "GPIOSET"); 309825ff184Sjmcneill 310825ff184Sjmcneill if (quiet) 311825ff184Sjmcneill return; 312825ff184Sjmcneill 31332eeaba5Smbalmer if (name != NULL) 31432eeaba5Smbalmer printf("pin %s: caps:", name); 31532eeaba5Smbalmer else 316825ff184Sjmcneill printf("pin %d: caps:", pin); 317825ff184Sjmcneill for (bs = pinflags; bs->string != NULL; bs++) 31832eeaba5Smbalmer if (set.gp_caps & bs->mask) 319825ff184Sjmcneill printf(" %s", bs->string); 320825ff184Sjmcneill printf(", flags:"); 321825ff184Sjmcneill for (bs = pinflags; bs->string != NULL; bs++) 32232eeaba5Smbalmer if (set.gp_flags & bs->mask) 323825ff184Sjmcneill printf(" %s", bs->string); 324825ff184Sjmcneill if (fl > 0) { 325825ff184Sjmcneill printf(" ->"); 326825ff184Sjmcneill for (bs = pinflags; bs->string != NULL; bs++) 327825ff184Sjmcneill if (fl & bs->mask) 328825ff184Sjmcneill printf(" %s", bs->string); 329825ff184Sjmcneill } 330825ff184Sjmcneill printf("\n"); 331825ff184Sjmcneill } 332825ff184Sjmcneill 33357ef84d0Smbalmer static void 33432eeaba5Smbalmer gpiounset(int pin, char *name) 33532eeaba5Smbalmer { 33632eeaba5Smbalmer struct gpio_set set; 33732eeaba5Smbalmer 33832eeaba5Smbalmer memset(&set, 0, sizeof(set)); 33932eeaba5Smbalmer if (name != NULL) 34032eeaba5Smbalmer strlcpy(set.gp_name, name, sizeof(set.gp_name)); 34132eeaba5Smbalmer else 34232eeaba5Smbalmer set.gp_pin = pin; 34332eeaba5Smbalmer 34432eeaba5Smbalmer if (ioctl(devfd, GPIOUNSET, &set) == -1) 34532eeaba5Smbalmer err(EXIT_FAILURE, "GPIOUNSET"); 34632eeaba5Smbalmer } 34732eeaba5Smbalmer 34857ef84d0Smbalmer static void 349be149ed8Smbalmer devattach(char *dvname, int offset, uint32_t mask, uint32_t flags) 35032eeaba5Smbalmer { 35132eeaba5Smbalmer struct gpio_attach attach; 35232eeaba5Smbalmer 35332eeaba5Smbalmer memset(&attach, 0, sizeof(attach)); 35432eeaba5Smbalmer strlcpy(attach.ga_dvname, dvname, sizeof(attach.ga_dvname)); 35532eeaba5Smbalmer attach.ga_offset = offset; 35632eeaba5Smbalmer attach.ga_mask = mask; 357be149ed8Smbalmer attach.ga_flags = flags; 35832eeaba5Smbalmer if (ioctl(devfd, GPIOATTACH, &attach) == -1) 35932eeaba5Smbalmer err(EXIT_FAILURE, "GPIOATTACH"); 36032eeaba5Smbalmer } 36132eeaba5Smbalmer 36257ef84d0Smbalmer static void 363825ff184Sjmcneill usage(void) 364825ff184Sjmcneill { 36557ef84d0Smbalmer const char *progname; 36632eeaba5Smbalmer 36757ef84d0Smbalmer progname = getprogname(); 3681bdc60c3Smbalmer fprintf(stderr, "usage: %s [-qs] device [pin] [0 | 1 | 2 | " 36957ef84d0Smbalmer "on | off | toggle]\n", progname); 37032eeaba5Smbalmer fprintf(stderr, " %s [-q] device pin set [flags] [name]\n", 37157ef84d0Smbalmer progname); 37257ef84d0Smbalmer fprintf(stderr, " %s [-q] device pin unset\n", progname); 373be149ed8Smbalmer fprintf(stderr, " %s [-q] device attach device offset mask " 3743b72c2b3Smbalmer "[flag]\n", 37557ef84d0Smbalmer progname); 376825ff184Sjmcneill 3779b5c4f42Sxtraeme exit(EXIT_FAILURE); 378825ff184Sjmcneill } 379