| File: | src/usr.sbin/gpioctl/gpioctl.c |
| Warning: | line 106, column 3 Address of stack memory associated with local variable 'devn' is still referred to by the global variable 'dev' upon returning to the caller. This will be a dangling reference |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* $OpenBSD: gpioctl.c,v 1.17 2015/12/26 20:52:03 mmcc Exp $ */ | |||
| 2 | /* | |||
| 3 | * Copyright (c) 2008 Marc Balmer <mbalmer@openbsd.org> | |||
| 4 | * Copyright (c) 2004 Alexander Yurchenko <grange@openbsd.org> | |||
| 5 | * | |||
| 6 | * Permission to use, copy, modify, and distribute this software for any | |||
| 7 | * purpose with or without fee is hereby granted, provided that the above | |||
| 8 | * copyright notice and this permission notice appear in all copies. | |||
| 9 | * | |||
| 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| 17 | */ | |||
| 18 | ||||
| 19 | /* | |||
| 20 | * Program to control GPIO devices. | |||
| 21 | */ | |||
| 22 | ||||
| 23 | #include <sys/types.h> | |||
| 24 | #include <sys/gpio.h> | |||
| 25 | #include <sys/ioctl.h> | |||
| 26 | #include <sys/limits.h> | |||
| 27 | ||||
| 28 | #include <err.h> | |||
| 29 | #include <errno(*__errno()).h> | |||
| 30 | #include <fcntl.h> | |||
| 31 | #include <paths.h> | |||
| 32 | #include <stdio.h> | |||
| 33 | #include <stdlib.h> | |||
| 34 | #include <string.h> | |||
| 35 | #include <unistd.h> | |||
| 36 | ||||
| 37 | ||||
| 38 | char *dev; | |||
| 39 | int devfd = -1; | |||
| 40 | int quiet = 0; | |||
| 41 | ||||
| 42 | void getinfo(void); | |||
| 43 | void pinread(int, char *); | |||
| 44 | void pinwrite(int, char *, int); | |||
| 45 | void pinset(int pin, char *name, int flags, char *alias); | |||
| 46 | void unset(int pin, char *name); | |||
| 47 | void devattach(char *, int, u_int32_t, u_int32_t); | |||
| 48 | void devdetach(char *); | |||
| 49 | ||||
| 50 | __dead__attribute__((__noreturn__)) void usage(void); | |||
| 51 | ||||
| 52 | const struct bitstr { | |||
| 53 | unsigned int mask; | |||
| 54 | const char *string; | |||
| 55 | } pinflags[] = { | |||
| 56 | { GPIO_PIN_INPUT0x0001, "in" }, | |||
| 57 | { GPIO_PIN_OUTPUT0x0002, "out" }, | |||
| 58 | { GPIO_PIN_INOUT0x0004, "inout" }, | |||
| 59 | { GPIO_PIN_OPENDRAIN0x0008, "od" }, | |||
| 60 | { GPIO_PIN_PUSHPULL0x0010, "pp" }, | |||
| 61 | { GPIO_PIN_TRISTATE0x0020, "tri" }, | |||
| 62 | { GPIO_PIN_PULLUP0x0040, "pu" }, | |||
| 63 | { GPIO_PIN_PULLDOWN0x0080, "pd" }, | |||
| 64 | { GPIO_PIN_INVIN0x0100, "iin" }, | |||
| 65 | { GPIO_PIN_INVOUT0x0200, "iout" }, | |||
| 66 | { 0, NULL((void *)0) }, | |||
| 67 | }; | |||
| 68 | ||||
| 69 | int | |||
| 70 | main(int argc, char *argv[]) | |||
| 71 | { | |||
| 72 | const struct bitstr *bs; | |||
| 73 | long lval; | |||
| 74 | u_int32_t ga_mask = 0, ga_flags = 0; | |||
| 75 | int pin, ch, ga_offset = -1, n, fl = 0, value = 0; | |||
| 76 | const char *errstr; | |||
| 77 | char *ep, *flags, *nam = NULL((void *)0); | |||
| 78 | char devn[32]; | |||
| 79 | ||||
| 80 | while ((ch = getopt(argc, argv, "q")) != -1) | |||
| ||||
| 81 | switch (ch) { | |||
| 82 | case 'q': | |||
| 83 | quiet = 1; | |||
| 84 | break; | |||
| 85 | default: | |||
| 86 | usage(); | |||
| 87 | /* NOTREACHED */ | |||
| 88 | } | |||
| 89 | argc -= optind; | |||
| 90 | argv += optind; | |||
| 91 | ||||
| 92 | if (argc < 1) | |||
| 93 | usage(); | |||
| 94 | dev = argv[0]; | |||
| 95 | ||||
| 96 | if (strncmp(_PATH_DEV"/dev/", dev, sizeof(_PATH_DEV"/dev/") - 1)) { | |||
| 97 | (void)snprintf(devn, sizeof(devn), "%s%s", _PATH_DEV"/dev/", dev); | |||
| 98 | dev = devn; | |||
| 99 | } | |||
| 100 | ||||
| 101 | if ((devfd = open(dev, O_RDWR0x0002)) == -1) | |||
| 102 | err(1, "%s", dev); | |||
| 103 | ||||
| 104 | if (argc == 1) { | |||
| 105 | getinfo(); | |||
| 106 | return 0; | |||
| ||||
| 107 | } | |||
| 108 | ||||
| 109 | if (!strcmp(argv[1], "attach")) { | |||
| 110 | char *driver, *offset, *mask; | |||
| 111 | ||||
| 112 | if (argc != 5 && argc != 6) | |||
| 113 | usage(); | |||
| 114 | ||||
| 115 | driver = argv[2]; | |||
| 116 | offset = argv[3]; | |||
| 117 | mask = argv[4]; | |||
| 118 | flags = argc == 6 ? argv[5] : NULL((void *)0); | |||
| 119 | ||||
| 120 | ga_offset = strtonum(offset, 0, INT_MAX0x7fffffff, &errstr); | |||
| 121 | if (errstr) | |||
| 122 | errx(1, "offset is %s: %s", errstr, offset); | |||
| 123 | ||||
| 124 | lval = strtol(mask, &ep, 0); | |||
| 125 | if (*mask == '\0' || *ep != '\0') | |||
| 126 | errx(1, "invalid mask (not a number)"); | |||
| 127 | if ((errno(*__errno()) == ERANGE34 && (lval == LONG_MAX0x7fffffffffffffffL | |||
| 128 | || lval == LONG_MIN(-0x7fffffffffffffffL-1))) || lval > UINT_MAX0xffffffffU) | |||
| 129 | errx(1, "mask out of range"); | |||
| 130 | ga_mask = lval; | |||
| 131 | if (flags != NULL((void *)0)) { | |||
| 132 | lval = strtonum(flags, 0, UINT_MAX0xffffffffU, &errstr); | |||
| 133 | if (errstr) | |||
| 134 | errx(1, "flags is %s: %s", errstr, flags); | |||
| 135 | ga_flags = lval; | |||
| 136 | } | |||
| 137 | devattach(driver, ga_offset, ga_mask, ga_flags); | |||
| 138 | return 0; | |||
| 139 | } else if (!strcmp(argv[1], "detach")) { | |||
| 140 | if (argc != 3) | |||
| 141 | usage(); | |||
| 142 | devdetach(argv[2]); | |||
| 143 | } else { | |||
| 144 | char *nm = NULL((void *)0); | |||
| 145 | ||||
| 146 | /* expecting a pin number or name */ | |||
| 147 | pin = strtonum(argv[1], 0, INT_MAX0x7fffffff, &errstr); | |||
| 148 | if (errstr) | |||
| 149 | nm = argv[1]; /* try named pin */ | |||
| 150 | if (argc > 2) { | |||
| 151 | if (!strcmp(argv[2], "set")) { | |||
| 152 | for (n = 3; n < argc; n++) { | |||
| 153 | for (bs = pinflags; bs->string != NULL((void *)0); | |||
| 154 | bs++) { | |||
| 155 | if (!strcmp(argv[n], | |||
| 156 | bs->string)) { | |||
| 157 | fl |= bs->mask; | |||
| 158 | break; | |||
| 159 | } | |||
| 160 | } | |||
| 161 | if (bs->string == NULL((void *)0)) | |||
| 162 | nam = argv[n]; | |||
| 163 | } | |||
| 164 | pinset(pin, nm, fl, nam); | |||
| 165 | } else if (!strcmp(argv[2], "unset")) { | |||
| 166 | unset(pin, nm); | |||
| 167 | } else { | |||
| 168 | value = strtonum(argv[2], INT_MIN(-0x7fffffff-1), INT_MAX0x7fffffff, | |||
| 169 | &errstr); | |||
| 170 | if (errstr) { | |||
| 171 | if (!strcmp(argv[2], "on")) | |||
| 172 | value = 1; | |||
| 173 | else if (!strcmp(argv[2], "off")) | |||
| 174 | value = 0; | |||
| 175 | else if (!strcmp(argv[2], "toggle")) | |||
| 176 | value = 2; | |||
| 177 | else | |||
| 178 | errx(1, "%s: invalid value", | |||
| 179 | argv[2]); | |||
| 180 | } | |||
| 181 | pinwrite(pin, nm, value); | |||
| 182 | } | |||
| 183 | } else | |||
| 184 | pinread(pin, nm); | |||
| 185 | } | |||
| 186 | ||||
| 187 | return (0); | |||
| 188 | } | |||
| 189 | ||||
| 190 | void | |||
| 191 | getinfo(void) | |||
| 192 | { | |||
| 193 | struct gpio_info info; | |||
| 194 | ||||
| 195 | memset(&info, 0, sizeof(info)); | |||
| 196 | if (ioctl(devfd, GPIOINFO((unsigned long)0x40000000 | ((sizeof(struct gpio_info) & 0x1fff) << 16) | ((('G')) << 8) | ((0))), &info) == -1) | |||
| 197 | err(1, "GPIOINFO"); | |||
| 198 | ||||
| 199 | if (quiet) | |||
| 200 | return; | |||
| 201 | ||||
| 202 | printf("%s: %d pins\n", dev, info.gpio_npins); | |||
| 203 | } | |||
| 204 | ||||
| 205 | void | |||
| 206 | pinread(int pin, char *gp_name) | |||
| 207 | { | |||
| 208 | struct gpio_pin_op op; | |||
| 209 | ||||
| 210 | memset(&op, 0, sizeof(op)); | |||
| 211 | if (gp_name != NULL((void *)0)) | |||
| 212 | strlcpy(op.gp_name, gp_name, sizeof(op.gp_name)); | |||
| 213 | else | |||
| 214 | op.gp_pin = pin; | |||
| 215 | ||||
| 216 | if (ioctl(devfd, GPIOPINREAD(((unsigned long)0x80000000|(unsigned long)0x40000000) | ((sizeof (struct gpio_pin_op) & 0x1fff) << 16) | ((('G')) << 8) | ((1))), &op) == -1) | |||
| 217 | err(1, "GPIOPINREAD"); | |||
| 218 | ||||
| 219 | if (quiet) | |||
| 220 | return; | |||
| 221 | ||||
| 222 | if (gp_name) | |||
| 223 | printf("pin %s: state %d\n", gp_name, op.gp_value); | |||
| 224 | else | |||
| 225 | printf("pin %d: state %d\n", pin, op.gp_value); | |||
| 226 | } | |||
| 227 | ||||
| 228 | void | |||
| 229 | pinwrite(int pin, char *gp_name, int value) | |||
| 230 | { | |||
| 231 | struct gpio_pin_op op; | |||
| 232 | ||||
| 233 | if (value < 0 || value > 2) | |||
| 234 | errx(1, "%d: invalid value", value); | |||
| 235 | ||||
| 236 | memset(&op, 0, sizeof(op)); | |||
| 237 | if (gp_name != NULL((void *)0)) | |||
| 238 | strlcpy(op.gp_name, gp_name, sizeof(op.gp_name)); | |||
| 239 | else | |||
| 240 | op.gp_pin = pin; | |||
| 241 | op.gp_value = (value == 0 ? GPIO_PIN_LOW0x00 : GPIO_PIN_HIGH0x01); | |||
| 242 | if (value < 2) { | |||
| 243 | if (ioctl(devfd, GPIOPINWRITE(((unsigned long)0x80000000|(unsigned long)0x40000000) | ((sizeof (struct gpio_pin_op) & 0x1fff) << 16) | ((('G')) << 8) | ((2))), &op) == -1) | |||
| 244 | err(1, "GPIOPINWRITE"); | |||
| 245 | } else { | |||
| 246 | if (ioctl(devfd, GPIOPINTOGGLE(((unsigned long)0x80000000|(unsigned long)0x40000000) | ((sizeof (struct gpio_pin_op) & 0x1fff) << 16) | ((('G')) << 8) | ((3))), &op) == -1) | |||
| 247 | err(1, "GPIOPINTOGGLE"); | |||
| 248 | } | |||
| 249 | ||||
| 250 | if (quiet) | |||
| 251 | return; | |||
| 252 | ||||
| 253 | if (gp_name) | |||
| 254 | printf("pin %s: state %d -> %d\n", gp_name, op.gp_value, | |||
| 255 | (value < 2 ? value : 1 - op.gp_value)); | |||
| 256 | else | |||
| 257 | printf("pin %d: state %d -> %d\n", pin, op.gp_value, | |||
| 258 | (value < 2 ? value : 1 - op.gp_value)); | |||
| 259 | } | |||
| 260 | ||||
| 261 | void | |||
| 262 | pinset(int pin, char *name, int fl, char *alias) | |||
| 263 | { | |||
| 264 | struct gpio_pin_set set; | |||
| 265 | const struct bitstr *bs; | |||
| 266 | ||||
| 267 | memset(&set, 0, sizeof(set)); | |||
| 268 | if (name != NULL((void *)0)) | |||
| 269 | strlcpy(set.gp_name, name, sizeof(set.gp_name)); | |||
| 270 | else | |||
| 271 | set.gp_pin = pin; | |||
| 272 | set.gp_flags = fl; | |||
| 273 | ||||
| 274 | if (alias != NULL((void *)0)) | |||
| 275 | strlcpy(set.gp_name2, alias, sizeof(set.gp_name2)); | |||
| 276 | ||||
| 277 | if (ioctl(devfd, GPIOPINSET(((unsigned long)0x80000000|(unsigned long)0x40000000) | ((sizeof (struct gpio_pin_set) & 0x1fff) << 16) | ((('G')) << 8) | ((4))), &set) == -1) | |||
| 278 | err(1, "GPIOPINSET"); | |||
| 279 | ||||
| 280 | if (quiet) | |||
| 281 | return; | |||
| 282 | ||||
| 283 | if (name != NULL((void *)0)) | |||
| 284 | printf("pin %s: caps:", name); | |||
| 285 | else | |||
| 286 | printf("pin %d: caps:", pin); | |||
| 287 | for (bs = pinflags; bs->string != NULL((void *)0); bs++) | |||
| 288 | if (set.gp_caps & bs->mask) | |||
| 289 | printf(" %s", bs->string); | |||
| 290 | printf(", flags:"); | |||
| 291 | for (bs = pinflags; bs->string != NULL((void *)0); bs++) | |||
| 292 | if (set.gp_flags & bs->mask) | |||
| 293 | printf(" %s", bs->string); | |||
| 294 | if (fl > 0) { | |||
| 295 | printf(" ->"); | |||
| 296 | for (bs = pinflags; bs->string != NULL((void *)0); bs++) | |||
| 297 | if (fl & bs->mask) | |||
| 298 | printf(" %s", bs->string); | |||
| 299 | } | |||
| 300 | printf("\n"); | |||
| 301 | } | |||
| 302 | ||||
| 303 | void | |||
| 304 | unset(int pin, char *name) | |||
| 305 | { | |||
| 306 | struct gpio_pin_set set; | |||
| 307 | ||||
| 308 | memset(&set, 0, sizeof(set)); | |||
| 309 | if (name != NULL((void *)0)) | |||
| 310 | strlcpy(set.gp_name, name, sizeof(set.gp_name)); | |||
| 311 | else | |||
| 312 | set.gp_pin = pin; | |||
| 313 | ||||
| 314 | if (ioctl(devfd, GPIOPINUNSET(((unsigned long)0x80000000|(unsigned long)0x40000000) | ((sizeof (struct gpio_pin_set) & 0x1fff) << 16) | ((('G')) << 8) | ((5))), &set) == -1) | |||
| 315 | err(1, "GPIOPINUNSET"); | |||
| 316 | } | |||
| 317 | ||||
| 318 | void | |||
| 319 | devattach(char *dvname, int offset, u_int32_t mask, u_int32_t flags) | |||
| 320 | { | |||
| 321 | struct gpio_attach attach; | |||
| 322 | ||||
| 323 | memset(&attach, 0, sizeof(attach)); | |||
| 324 | strlcpy(attach.ga_dvname, dvname, sizeof(attach.ga_dvname)); | |||
| 325 | attach.ga_offset = offset; | |||
| 326 | attach.ga_mask = mask; | |||
| 327 | attach.ga_flags = flags; | |||
| 328 | if (ioctl(devfd, GPIOATTACH(((unsigned long)0x80000000|(unsigned long)0x40000000) | ((sizeof (struct gpio_attach) & 0x1fff) << 16) | ((('G')) << 8) | ((6))), &attach) == -1) | |||
| 329 | err(1, "GPIOATTACH"); | |||
| 330 | } | |||
| 331 | ||||
| 332 | void | |||
| 333 | devdetach(char *dvname) | |||
| 334 | { | |||
| 335 | struct gpio_attach attach; | |||
| 336 | ||||
| 337 | memset(&attach, 0, sizeof(attach)); | |||
| 338 | strlcpy(attach.ga_dvname, dvname, sizeof(attach.ga_dvname)); | |||
| 339 | if (ioctl(devfd, GPIODETACH(((unsigned long)0x80000000|(unsigned long)0x40000000) | ((sizeof (struct gpio_attach) & 0x1fff) << 16) | ((('G')) << 8) | ((7))), &attach) == -1) | |||
| 340 | err(1, "GPIODETACH"); | |||
| 341 | } | |||
| 342 | void | |||
| 343 | usage(void) | |||
| 344 | { | |||
| 345 | extern char *__progname; | |||
| 346 | ||||
| 347 | fprintf(stderr(&__sF[2]), "usage: %s [-q] device pin [0 | 1 | 2 | " | |||
| 348 | "on | off | toggle]\n", __progname); | |||
| 349 | fprintf(stderr(&__sF[2]), " %s [-q] device pin set [flags] [name]\n", | |||
| 350 | __progname); | |||
| 351 | fprintf(stderr(&__sF[2]), " %s [-q] device pin unset\n", __progname); | |||
| 352 | fprintf(stderr(&__sF[2]), " %s [-q] device attach device offset mask " | |||
| 353 | "[flag]\n", __progname); | |||
| 354 | fprintf(stderr(&__sF[2]), " %s [-q] device detach device\n", __progname); | |||
| 355 | ||||
| 356 | exit(1); | |||
| 357 | } |