1213238Sgonzo/*- 2330449Seadler * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3330449Seadler * 4213238Sgonzo * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org> 5275395Srpaulo * Copyright (c) 2014, Rui Paulo <rpaulo@FreeBSD.org> 6296682Sgonzo * Copyright (c) 2015, Emmanuel Vadot <manu@bidouilliste.com> 7213238Sgonzo * All rights reserved. 8213238Sgonzo * 9213238Sgonzo * Redistribution and use in source and binary forms, with or without 10213238Sgonzo * modification, are permitted provided that the following conditions 11213238Sgonzo * are met: 12213238Sgonzo * 1. Redistributions of source code must retain the above copyright 13213238Sgonzo * notice unmodified, this list of conditions, and the following 14213238Sgonzo * disclaimer. 15213238Sgonzo * 2. Redistributions in binary form must reproduce the above copyright 16213238Sgonzo * notice, this list of conditions and the following disclaimer in the 17213238Sgonzo * documentation and/or other materials provided with the distribution. 18213238Sgonzo * 19213238Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20213238Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21213238Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22213238Sgonzo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23213238Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24213238Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25213238Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26213238Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27213238Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28213238Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29213238Sgonzo * SUCH DAMAGE. 30213238Sgonzo */ 31213238Sgonzo 32213238Sgonzo#include <sys/cdefs.h> 33213238Sgonzo__FBSDID("$FreeBSD: stable/11/usr.sbin/gpioctl/gpioctl.c 330449 2018-03-05 07:26:05Z eadler $"); 34213238Sgonzo 35213238Sgonzo#include <fcntl.h> 36213238Sgonzo#include <getopt.h> 37255629Ssbruno#include <paths.h> 38213238Sgonzo#include <stdio.h> 39213238Sgonzo#include <stdarg.h> 40213238Sgonzo#include <stdlib.h> 41213238Sgonzo#include <string.h> 42213238Sgonzo#include <unistd.h> 43213238Sgonzo 44275395Srpaulo#include <libgpio.h> 45213238Sgonzo 46296682Sgonzo#define PIN_TYPE_NUMBER 1 47296682Sgonzo#define PIN_TYPE_NAME 2 48296682Sgonzo 49213238Sgonzostruct flag_desc { 50213238Sgonzo const char *name; 51213238Sgonzo uint32_t flag; 52213238Sgonzo}; 53213238Sgonzo 54241737Sedstatic struct flag_desc gpio_flags[] = { 55213238Sgonzo { "IN", GPIO_PIN_INPUT }, 56213238Sgonzo { "OUT", GPIO_PIN_OUTPUT }, 57213238Sgonzo { "OD", GPIO_PIN_OPENDRAIN }, 58213238Sgonzo { "PP", GPIO_PIN_PUSHPULL }, 59213238Sgonzo { "TS", GPIO_PIN_TRISTATE }, 60213238Sgonzo { "PU", GPIO_PIN_PULLUP }, 61213238Sgonzo { "PD", GPIO_PIN_PULLDOWN }, 62213238Sgonzo { "II", GPIO_PIN_INVIN }, 63213238Sgonzo { "IO", GPIO_PIN_INVOUT }, 64213238Sgonzo { "PULSE", GPIO_PIN_PULSATE }, 65213238Sgonzo { NULL, 0 }, 66213238Sgonzo}; 67213238Sgonzo 68213238Sgonzoint str2cap(const char *str); 69213238Sgonzo 70213238Sgonzostatic void 71213238Sgonzousage(void) 72213238Sgonzo{ 73213238Sgonzo fprintf(stderr, "Usage:\n"); 74255629Ssbruno fprintf(stderr, "\tgpioctl [-f ctldev] -l [-v]\n"); 75296682Sgonzo fprintf(stderr, "\tgpioctl [-f ctldev] [-pN] -t pin\n"); 76296682Sgonzo fprintf(stderr, "\tgpioctl [-f ctldev] [-pN] -c pin flag ...\n"); 77296682Sgonzo fprintf(stderr, "\tgpioctl [-f ctldev] [-pN] -n pin pin-name\n"); 78296682Sgonzo fprintf(stderr, "\tgpioctl [-f ctldev] [-pN] pin [0|1]\n"); 79213238Sgonzo exit(1); 80213238Sgonzo} 81213238Sgonzo 82213238Sgonzostatic const char * 83213238Sgonzocap2str(uint32_t cap) 84213238Sgonzo{ 85213238Sgonzo struct flag_desc * pdesc = gpio_flags; 86213238Sgonzo while (pdesc->name) { 87213238Sgonzo if (pdesc->flag == cap) 88213238Sgonzo return pdesc->name; 89213238Sgonzo pdesc++; 90213238Sgonzo } 91213238Sgonzo 92213238Sgonzo return "UNKNOWN"; 93213238Sgonzo} 94213238Sgonzo 95213238Sgonzoint 96213238Sgonzostr2cap(const char *str) 97213238Sgonzo{ 98213238Sgonzo struct flag_desc * pdesc = gpio_flags; 99213238Sgonzo while (pdesc->name) { 100213238Sgonzo if (strcasecmp(str, pdesc->name) == 0) 101213238Sgonzo return pdesc->flag; 102213238Sgonzo pdesc++; 103213238Sgonzo } 104213238Sgonzo 105213238Sgonzo return (-1); 106213238Sgonzo} 107213238Sgonzo 108213238Sgonzo/* 109213238Sgonzo * Our handmade function for converting string to number 110213238Sgonzo */ 111275395Srpaulostatic int 112213238Sgonzostr2int(const char *s, int *ok) 113213238Sgonzo{ 114213238Sgonzo char *endptr; 115213238Sgonzo int res = strtod(s, &endptr); 116213238Sgonzo if (endptr != s + strlen(s) ) 117213238Sgonzo *ok = 0; 118213238Sgonzo else 119213238Sgonzo *ok = 1; 120213238Sgonzo 121213238Sgonzo return res; 122213238Sgonzo} 123213238Sgonzo 124213238Sgonzostatic void 125213238Sgonzoprint_caps(int caps) 126213238Sgonzo{ 127213238Sgonzo int i, need_coma; 128213238Sgonzo 129213238Sgonzo need_coma = 0; 130213238Sgonzo printf("<"); 131213238Sgonzo for (i = 0; i < 32; i++) { 132213238Sgonzo if (caps & (1 << i)) { 133213238Sgonzo if (need_coma) 134213238Sgonzo printf(","); 135213238Sgonzo printf("%s", cap2str(1 << i)); 136213238Sgonzo need_coma = 1; 137213238Sgonzo } 138213238Sgonzo } 139213238Sgonzo printf(">"); 140213238Sgonzo} 141213238Sgonzo 142213238Sgonzostatic void 143275395Srpaulodump_pins(gpio_handle_t handle, int verbose) 144213238Sgonzo{ 145275395Srpaulo int i, maxpin, pinv; 146275395Srpaulo gpio_config_t *cfgs; 147275395Srpaulo gpio_config_t *pin; 148213238Sgonzo 149275395Srpaulo maxpin = gpio_pin_list(handle, &cfgs); 150275395Srpaulo if (maxpin < 0) { 151275395Srpaulo perror("gpio_pin_list"); 152213238Sgonzo exit(1); 153213238Sgonzo } 154213238Sgonzo 155213238Sgonzo for (i = 0; i <= maxpin; i++) { 156275395Srpaulo pin = cfgs + i; 157275395Srpaulo pinv = gpio_pin_get(handle, pin->g_pin); 158275395Srpaulo printf("pin %02d:\t%d\t%s", pin->g_pin, pinv, 159275395Srpaulo pin->g_name); 160213238Sgonzo 161275395Srpaulo print_caps(pin->g_flags); 162213238Sgonzo 163213238Sgonzo if (verbose) { 164213238Sgonzo printf(", caps:"); 165275395Srpaulo print_caps(pin->g_caps); 166213238Sgonzo } 167213238Sgonzo printf("\n"); 168213238Sgonzo } 169276481Sloos free(cfgs); 170213238Sgonzo} 171213238Sgonzo 172296682Sgonzostatic int 173296682Sgonzoget_pinnum_by_name(gpio_handle_t handle, const char *name) { 174296682Sgonzo int i, maxpin, pinn; 175296682Sgonzo gpio_config_t *cfgs; 176296682Sgonzo gpio_config_t *pin; 177296682Sgonzo 178296682Sgonzo pinn = -1; 179296682Sgonzo maxpin = gpio_pin_list(handle, &cfgs); 180296682Sgonzo if (maxpin < 0) { 181296682Sgonzo perror("gpio_pin_list"); 182296682Sgonzo exit(1); 183296682Sgonzo } 184296682Sgonzo 185296682Sgonzo for (i = 0; i <= maxpin; i++) { 186296682Sgonzo pin = cfgs + i; 187296682Sgonzo gpio_pin_get(handle, pin->g_pin); 188296682Sgonzo if (!strcmp(name, pin->g_name)) { 189296682Sgonzo pinn = i; 190296682Sgonzo break; 191296682Sgonzo } 192296682Sgonzo } 193296682Sgonzo free(cfgs); 194296682Sgonzo 195296682Sgonzo return pinn; 196296682Sgonzo} 197296682Sgonzo 198275395Srpaulostatic void 199213238Sgonzofail(const char *fmt, ...) 200213238Sgonzo{ 201213238Sgonzo va_list ap; 202213238Sgonzo 203213238Sgonzo va_start(ap, fmt); 204213238Sgonzo vfprintf(stderr, fmt, ap); 205213238Sgonzo va_end(ap); 206213238Sgonzo exit(1); 207213238Sgonzo} 208213238Sgonzo 209275395Srpauloint 210213238Sgonzomain(int argc, char **argv) 211213238Sgonzo{ 212213238Sgonzo int i; 213275395Srpaulo gpio_config_t pin; 214275395Srpaulo gpio_handle_t handle; 215213238Sgonzo char *ctlfile = NULL; 216296682Sgonzo int pinn, pinv, pin_type, ch; 217213238Sgonzo int flags, flag, ok; 218279761Sloos int config, list, name, toggle, verbose; 219213238Sgonzo 220296682Sgonzo config = toggle = verbose = list = name = pin_type = 0; 221213238Sgonzo 222296682Sgonzo while ((ch = getopt(argc, argv, "cf:lntvNp")) != -1) { 223213238Sgonzo switch (ch) { 224213238Sgonzo case 'c': 225213238Sgonzo config = 1; 226213238Sgonzo break; 227213238Sgonzo case 'f': 228213238Sgonzo ctlfile = optarg; 229213238Sgonzo break; 230213238Sgonzo case 'l': 231213238Sgonzo list = 1; 232213238Sgonzo break; 233279761Sloos case 'n': 234279761Sloos name = 1; 235279761Sloos break; 236296682Sgonzo case 'N': 237296682Sgonzo pin_type = PIN_TYPE_NAME; 238296682Sgonzo break; 239296682Sgonzo case'p': 240296682Sgonzo pin_type = PIN_TYPE_NUMBER; 241296682Sgonzo break; 242213238Sgonzo case 't': 243213238Sgonzo toggle = 1; 244213238Sgonzo break; 245213238Sgonzo case 'v': 246213238Sgonzo verbose = 1; 247213238Sgonzo break; 248213238Sgonzo default: 249213238Sgonzo usage(); 250213238Sgonzo break; 251213238Sgonzo } 252213238Sgonzo } 253213238Sgonzo argv += optind; 254213238Sgonzo argc -= optind; 255213238Sgonzo if (ctlfile == NULL) 256275395Srpaulo handle = gpio_open(0); 257275395Srpaulo else 258275395Srpaulo handle = gpio_open_device(ctlfile); 259275395Srpaulo if (handle == GPIO_INVALID_HANDLE) { 260275395Srpaulo perror("gpio_open"); 261213238Sgonzo exit(1); 262213238Sgonzo } 263213238Sgonzo 264296682Sgonzo if (list) { 265296682Sgonzo dump_pins(handle, verbose); 266296682Sgonzo gpio_close(handle); 267296682Sgonzo exit(0); 268296682Sgonzo } 269296682Sgonzo 270296682Sgonzo if (argc == 0) 271296682Sgonzo usage(); 272296682Sgonzo 273296682Sgonzo /* Find the pin number by the name */ 274296682Sgonzo switch (pin_type) { 275296724Sdim default: 276296682Sgonzo /* First test if it is a pin number */ 277296682Sgonzo pinn = str2int(argv[0], &ok); 278296682Sgonzo if (ok) { 279296682Sgonzo /* Test if we have any pin named by this number and tell the user */ 280296682Sgonzo if (get_pinnum_by_name(handle, argv[0]) != -1) 281296682Sgonzo fail("%s is also a pin name, use -p or -N\n", argv[0]); 282296682Sgonzo } else { 283296682Sgonzo /* Test if it is a name */ 284296682Sgonzo if ((pinn = get_pinnum_by_name(handle, argv[0])) == -1) 285296682Sgonzo fail("Can't find pin named \"%s\"\n", argv[0]); 286296682Sgonzo } 287296682Sgonzo break; 288296682Sgonzo case PIN_TYPE_NUMBER: 289296682Sgonzo pinn = str2int(argv[0], &ok); 290296682Sgonzo if (!ok) 291296682Sgonzo fail("Invalid pin number: %s\n", argv[0]); 292296682Sgonzo break; 293296682Sgonzo case PIN_TYPE_NAME: 294296682Sgonzo if ((pinn = get_pinnum_by_name(handle, argv[0])) == -1) 295296682Sgonzo fail("Can't find pin named \"%s\"\n", argv[0]); 296296682Sgonzo break; 297296682Sgonzo } 298296682Sgonzo 299279761Sloos /* Set the pin name. */ 300279761Sloos if (name) { 301296682Sgonzo if (argc != 2) 302279761Sloos usage(); 303296682Sgonzo if (gpio_pin_set_name(handle, pinn, argv[1]) < 0) { 304279761Sloos perror("gpio_pin_set_name"); 305279761Sloos exit(1); 306279761Sloos } 307279761Sloos exit(0); 308279761Sloos } 309279761Sloos 310213238Sgonzo if (toggle) { 311213238Sgonzo /* 312296682Sgonzo * -t pin assumes no additional arguments 313296682Sgonzo */ 314296682Sgonzo if (argc > 1) 315213238Sgonzo usage(); 316275395Srpaulo if (gpio_pin_toggle(handle, pinn) < 0) { 317275395Srpaulo perror("gpio_pin_toggle"); 318213238Sgonzo exit(1); 319213238Sgonzo } 320275395Srpaulo gpio_close(handle); 321275395Srpaulo exit(0); 322213238Sgonzo } 323213238Sgonzo 324213238Sgonzo if (config) { 325213238Sgonzo flags = 0; 326296682Sgonzo for (i = 1; i < argc; i++) { 327213238Sgonzo flag = str2cap(argv[i]); 328213238Sgonzo if (flag < 0) 329213238Sgonzo fail("Invalid flag: %s\n", argv[i]); 330213238Sgonzo flags |= flag; 331213238Sgonzo } 332275395Srpaulo pin.g_pin = pinn; 333275395Srpaulo pin.g_flags = flags; 334275395Srpaulo if (gpio_pin_set_flags(handle, &pin) < 0) { 335275395Srpaulo perror("gpio_pin_set_flags"); 336213238Sgonzo exit(1); 337213238Sgonzo } 338213238Sgonzo exit(0); 339213238Sgonzo } 340213238Sgonzo 341213238Sgonzo /* 342213238Sgonzo * Last two cases - set value or print value 343213238Sgonzo */ 344296682Sgonzo if ((argc == 0) || (argc > 2)) 345213238Sgonzo usage(); 346213238Sgonzo 347213238Sgonzo /* 348213238Sgonzo * Read pin value 349213238Sgonzo */ 350213238Sgonzo if (argc == 1) { 351275395Srpaulo pinv = gpio_pin_get(handle, pinn); 352275395Srpaulo if (pinv < 0) { 353275395Srpaulo perror("gpio_pin_get"); 354213238Sgonzo exit(1); 355213238Sgonzo } 356275395Srpaulo printf("%d\n", pinv); 357275395Srpaulo exit(0); 358213238Sgonzo } 359213238Sgonzo 360213238Sgonzo /* Is it valid number (0 or 1) ? */ 361213238Sgonzo pinv = str2int(argv[1], &ok); 362296682Sgonzo if (ok == 0 || ((pinv != 0) && (pinv != 1))) 363213238Sgonzo fail("Invalid pin value: %s\n", argv[1]); 364213238Sgonzo 365213238Sgonzo /* 366213238Sgonzo * Set pin value 367213238Sgonzo */ 368275395Srpaulo if (gpio_pin_set(handle, pinn, pinv) < 0) { 369275395Srpaulo perror("gpio_pin_set"); 370213238Sgonzo exit(1); 371213238Sgonzo } 372213238Sgonzo 373275395Srpaulo gpio_close(handle); 374213238Sgonzo exit(0); 375213238Sgonzo} 376