/* * (C) Copyright 2014 Leo C. * * SPDX-License-Identifier: GPL-2.0+ */ #include "common.h" #include #include #include #include "command.h" #include "print-utils.h" #include "getopt-min.h" #include "env.h" #include "gpio.h" //#include "debug.h" static const int namestr = GPIO_MAX; static char *pin_names[GPIO_MAX+1]; static uint_least8_t pin_names_width; static void pinnames_get(void) { static const FLASH char delim1[] = {":= "}; static const FLASH char delim2[] = {", "}; char *lp; char *ptr; uint_fast8_t i; if (pin_names[namestr] != NULL) free(pin_names[namestr]); memset(pin_names, 0, sizeof(pin_names)); pin_names_width = 0; /* TODO: enters endless loop on wrong parameters */ if ((lp = getenv_str(PSTR(ENV_PINALIAS))) != NULL) { pin_names[namestr] = strdup(lp); ptr = strtok_P(pin_names[namestr], delim1); while (ptr != NULL) { if (((i = strtoul(ptr, &lp, 10)) < GPIO_MAX) && lp != ptr && (ptr = strtok_P(NULL, delim2)) != NULL ) { pin_names[i] = ptr; ptr = strtok_P(NULL, delim1); } } for (i = 0; i < GPIO_MAX; i++) if (strlen(pin_names[i]) > pin_names_width) pin_names_width = strlen(pin_names[i]); } } static size_t xstrlen(char *s) { if (s == NULL) return 0; else return strlen(s); } static const FLASH char * const FLASH pinconf_str[] = { FSTR("?"), FSTR("Input"), FSTR("Pullup"), FSTR("Output"), FSTR("Clock"), }; static const FLASH char * const FLASH pinlevel_str[] = { FSTR("Low"), FSTR("High"), FSTR(""), }; static int print_pin(int pin, int multi) { int pinconf; const FLASH char *levelp; long div; pinconf = gpio_config_get(pin); if (pinconf == OUTPUT_TIMER) { div = gpio_clockdiv_get(pin); levelp = pinlevel_str[2]; } else levelp = pinlevel_str[gpio_read(pin)]; if (multi) { printf_P(PSTR("%3d "), pin); if (pin_names_width) { printf_P(PSTR("%s "), pin_names[pin]); print_blanks(pin_names_width - xstrlen(pin_names[pin])); } my_puts_P(pinconf_str[pinconf]); print_blanks(7 - strlen_P(pinconf_str[pinconf])); my_puts_P(levelp); print_blanks(5 - strlen_P(levelp)); if (pinconf == OUTPUT_TIMER) printf_P(PSTR("%8ld %8ld"), div, F_CPU/div); } else { printf_P(PSTR("%d: \"%s\", "), pin, pin_names[pin] ? pin_names[pin] : 0); my_puts_P(pinconf_str[pinconf]); printf_P(PSTR(", ")); my_puts_P(levelp); if (pinconf == OUTPUT_TIMER) printf_P(PSTR("divide by %ld (%ldHz)"), div, F_CPU/div); } printf_P(PSTR("\n")); return 0; } static int_fast8_t pinarg_insert(int pin, uint_fast8_t count, uint_fast8_t pinarg[]) { uint_fast8_t pos; if (pin < 0 || pin >= GPIO_MAX) return -1; for (pos = 0; pos < count; pos++) { if (pin == pinarg[pos]) return 0; if (pin < pinarg[pos]) break; } for (uint_fast8_t i = count-1; i == pos ; i--) pinarg[i+1] = pinarg[i]; pinarg[pos] = pin; return 1; } static uint_fast8_t pinarg_get(char * arg, uint_fast8_t pinarg[]) { char *endp; uint_fast8_t pin1; int_fast8_t rc; uint_fast8_t count = 0; while (1) { pin1 = strtoul(arg, &endp, 10); if (endp != arg && *endp == '-') { arg = endp+1; uint_fast8_t pin2 = (int) strtoul(arg, &endp, 10); if (pin1 < pin2) for (; pin1 < pin2; pin1++) if ((rc = pinarg_insert(pin1, count, pinarg)) >= 0) count += rc; else return 0; else return 0; } if (endp != arg) { if ((*endp == ',' || *endp == '\0') && (rc = pinarg_insert(pin1, count, pinarg)) >= 0) { count += rc; if (*endp == '\0') return count; } else return 0; } else return 0; arg = endp+1; } } command_ret_t do_gpio(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) { char printheader = 1; uint_fast8_t pinarg[GPIO_MAX]; uint_fast8_t pinargc; (void) cmdtp; (void) flag; /* reset getopt() */ optind = 0; int opt; while ((opt = getopt(argc, argv, PSTR("s"))) != -1) { switch (opt) { case 's': printheader = 0; break; default: /* '?' */ return CMD_RET_USAGE; } } /* remaining arguments */ argc -= optind; pinnames_get(); if (argc == 0) { /* print cofig of all pins */ for (pinargc = 0; pinargc < GPIO_MAX; pinargc++) pinarg[pinargc] = pinargc; } else { /* get first arg */ pinargc = pinarg_get(argv[optind++], pinarg); if (pinargc == 0) return CMD_RET_USAGE; else argc--; } if (argc == 0) { /* no more args, print config */ if (pinargc == 1) print_pin(pinarg[0], 0); else { if (printheader) { if (pin_names_width > 0) { if ( strlen("Name") > pin_names_width) pin_names_width = strlen("Name"); char s[pin_names_width+1]; memset(s, ' ', pin_names_width); s[pin_names_width] = '\0'; strncpy_P(s, PSTR("Name"), 4); printf_P(PSTR("Pin %s Config Level Divider Frequency/Hz\n"),s); memset(s, '-', pin_names_width); printf_P(PSTR("----%s-----------------------------------\n"), s); } else printf_P(PSTR("Pin Config Level Divider Frequency/Hz\n" "--------------------------------------\n")); } for (uint_fast8_t i = 0; i < pinargc; i++) print_pin(pinarg[i], 1); } return CMD_RET_SUCCESS; } /* arguments must be in pairs: pins conf */ if (argc % 2 != 1) return CMD_RET_USAGE; while (argc > 0) { char *endp; gpiomode_t mode = NONE; int level = 0; unsigned long value = 0; uint8_t hz_flag = 0; switch (toupper(argv[optind][0])) { case 'H': level = 1; case 'L': mode = OUTPUT; break; case 'P': mode = INPUT_PULLUP; break; case 'I': case 'T': mode = INPUT; break; default: value = strtoul(argv[optind], &endp, 10); switch (*endp) { case 'M': value *= 1000; case 'K': value *= 1000; endp++; } if (*endp && strcmp_P(endp, PSTR("Hz")) == 0) { hz_flag = 1; endp += 2; } if (*endp != '\0') { printf_P(PSTR("invalid parameter: '%s'\n"), argv[optind]); return CMD_RET_USAGE; } if (value == 0) { printf_P(PSTR("invalid value: %lu \n")); return CMD_RET_USAGE; } if (hz_flag) { if (value > F_CPU / 2) { printf_P(PSTR("Max frequency is: %luHz\n"), F_CPU/2); return CMD_RET_USAGE; } value = F_CPU/value; } mode = OUTPUT_TIMER; } if (mode == NONE) return CMD_RET_USAGE; for (uint_fast8_t i = 0; i < pinargc; i++) { switch (mode) { case OUTPUT: gpio_write(pinarg[i], level); /* fall thru */ case INPUT: case INPUT_PULLUP: gpio_config(pinarg[i], mode); break; case OUTPUT_TIMER: if (gpio_clockdiv_set(pinarg[i], value) < 0) { printf_P(PSTR("Setting pin %d to %lu failed.\n"), pinarg[i], value); } break; default: break; } } optind++; pinargc = pinarg_get(argv[optind++], pinarg); argc -= 2; } return CMD_RET_SUCCESS; }