/* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright (c) 2018 Joyent, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define AHCIEM_IDENT "ident" #define AHCIEM_FAULT "fault" #define AHCIEM_NOACTIVITY "noactivity" #define AHCIEM_DEFAULT "default" #define AHCIEM_UNKNOWN "unknown" #define EXIT_USAGE 2 static const char *ahciem_progname; typedef struct { boolean_t ahci_set; ahci_em_led_state_t ahci_led; int ahci_argc; char **ahci_argv; boolean_t *ahci_found; int ahci_err; } ahciem_t; static void ahciem_usage(const char *fmt, ...) { if (fmt != NULL) { va_list ap; va_start(ap, fmt); vwarnx(fmt, ap); va_end(ap); } (void) fprintf(stderr, "Usage: %s [-s mode] [port]\n" "\n" "\t-s mode\t\tset LED to mode\n", ahciem_progname); } static const char * ahciem_led_to_string(uint_t led) { switch (led) { case AHCI_EM_LED_IDENT_ENABLE: return (AHCIEM_IDENT); case AHCI_EM_LED_FAULT_ENABLE: return (AHCIEM_FAULT); case AHCI_EM_LED_ACTIVITY_DISABLE: return (AHCIEM_NOACTIVITY); case (AHCI_EM_LED_IDENT_ENABLE | AHCI_EM_LED_FAULT_ENABLE): return (AHCIEM_IDENT "," AHCIEM_FAULT); case (AHCI_EM_LED_IDENT_ENABLE | AHCI_EM_LED_ACTIVITY_DISABLE): return (AHCIEM_IDENT "," AHCIEM_NOACTIVITY); case (AHCI_EM_LED_FAULT_ENABLE | AHCI_EM_LED_ACTIVITY_DISABLE): return (AHCIEM_FAULT "," AHCIEM_NOACTIVITY); /* BEGIN CSTYLED */ case (AHCI_EM_LED_IDENT_ENABLE | AHCI_EM_LED_FAULT_ENABLE | AHCI_EM_LED_ACTIVITY_DISABLE): return (AHCIEM_IDENT "," AHCIEM_FAULT "," AHCIEM_NOACTIVITY); /* END CSTYLED */ case 0: return (AHCIEM_DEFAULT); default: return (AHCIEM_UNKNOWN); } } static boolean_t ahciem_match(ahciem_t *ahci, const char *port) { int i; if (ahci->ahci_argc == 0) return (B_TRUE); for (i = 0; i < ahci->ahci_argc; i++) { size_t len = strlen(ahci->ahci_argv[i]); /* * Perform a partial match on the base name. This allows us to * match all of a controller by using a string like "ahci0". */ if (strncmp(ahci->ahci_argv[i], port, len) == 0) { ahci->ahci_found[i] = B_TRUE; return (B_TRUE); } } return (B_FALSE); } static ahci_em_led_state_t ahciem_parse(const char *arg) { if (strcmp(arg, AHCIEM_IDENT) == 0) { return (AHCI_EM_LED_IDENT_ENABLE); } else if (strcmp(arg, AHCIEM_FAULT) == 0) { return (AHCI_EM_LED_FAULT_ENABLE); } else if (strcmp(arg, AHCIEM_NOACTIVITY) == 0) { return (AHCI_EM_LED_ACTIVITY_DISABLE); } else if (strcmp(arg, AHCIEM_DEFAULT) == 0) { return (0); } errx(EXIT_USAGE, "invalid LED mode with -s: %s", arg); } static void ahciem_set(ahciem_t *ahci, const char *portstr, int fd, int port) { ahci_ioc_em_set_t set; bzero(&set, sizeof (set)); set.aiems_port = port; set.aiems_op = AHCI_EM_IOC_SET_OP_SET; set.aiems_leds = ahci->ahci_led; if (ioctl(fd, AHCI_EM_IOC_SET, &set) != 0) { warn("failed to set LEDs on %s", portstr); ahci->ahci_err = 1; } } static int ahciem_devinfo(di_node_t node, void *arg) { char *driver, *mpath, *fullpath; const char *sup; int inst, fd; uint_t i; ahciem_t *ahci = arg; di_minor_t m; ahci_ioc_em_get_t get; if ((driver = di_driver_name(node)) == NULL) return (DI_WALK_CONTINUE); if (strcmp(driver, "ahci") != 0) return (DI_WALK_CONTINUE); inst = di_instance(node); m = DI_MINOR_NIL; while ((m = di_minor_next(node, m)) != DI_MINOR_NIL) { char *mname = di_minor_name(m); if (mname != NULL && strcmp("devctl", mname) == 0) break; } if (m == DI_MINOR_NIL) { warnx("encountered ahci%d without devctl node", inst); return (DI_WALK_PRUNECHILD); } if ((mpath = di_devfs_minor_path(m)) == NULL) { warnx("failed to get path for ahci%d devctl minor", inst); return (DI_WALK_PRUNECHILD); } if (asprintf(&fullpath, "/devices/%s", mpath) == -1) { warn("failed to construct /devices path from %s", mpath); return (DI_WALK_PRUNECHILD); } if ((fd = open(fullpath, O_RDWR)) < 0) { warn("failed to open ahci%d devctl path %s", inst, fullpath); goto out; } bzero(&get, sizeof (get)); if (ioctl(fd, AHCI_EM_IOC_GET, &get) != 0) { warn("failed to get AHCI enclosure information for ahci%d", inst); ahci->ahci_err = 1; goto out; } if ((get.aiemg_flags & AHCI_EM_FLAG_CONTROL_ACTIVITY) != 0) { sup = ahciem_led_to_string(AHCI_EM_LED_IDENT_ENABLE | AHCI_EM_LED_FAULT_ENABLE | AHCI_EM_LED_ACTIVITY_DISABLE); } else { sup = ahciem_led_to_string(AHCI_EM_LED_IDENT_ENABLE | AHCI_EM_LED_FAULT_ENABLE); } for (i = 0; i < AHCI_EM_IOC_MAX_PORTS; i++) { char port[64]; const char *state; if (((1 << i) & get.aiemg_nports) == 0) continue; (void) snprintf(port, sizeof (port), "ahci%d/%u", inst, i); if (!ahciem_match(ahci, port)) continue; if (ahci->ahci_set) { ahciem_set(ahci, port, fd, i); continue; } state = ahciem_led_to_string(get.aiemg_status[i]); (void) printf("%-20s %-12s %s,default\n", port, state, sup); } out: free(fullpath); return (DI_WALK_PRUNECHILD); } int main(int argc, char *argv[]) { int c, i, ret; di_node_t root; ahciem_t ahci; ahciem_progname = basename(argv[0]); bzero(&ahci, sizeof (ahciem_t)); while ((c = getopt(argc, argv, ":s:")) != -1) { switch (c) { case 's': ahci.ahci_set = B_TRUE; ahci.ahci_led = ahciem_parse(optarg); break; case ':': ahciem_usage("option -%c requires an operand\n", optopt); return (EXIT_USAGE); case '?': default: ahciem_usage("unknown option: -%c\n", optopt); return (EXIT_USAGE); } } argc -= optind; argv += optind; ahci.ahci_argc = argc; ahci.ahci_argv = argv; if (argc > 0) { ahci.ahci_found = calloc(argc, sizeof (boolean_t)); if (ahci.ahci_found == NULL) { err(EXIT_FAILURE, "failed to alloc memory for %d " "booleans", argc); } } if ((root = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) { err(EXIT_FAILURE, "failed to open devinfo tree"); } if (!ahci.ahci_set) { (void) printf("%-20s %-12s %s\n", "PORT", "ACTIVE", "SUPPORTED"); } if (di_walk_node(root, DI_WALK_CLDFIRST, &ahci, ahciem_devinfo) != 0) { err(EXIT_FAILURE, "failed to walk devinfo tree"); } ret = ahci.ahci_err; for (i = 0; i < argc; i++) { if (ahci.ahci_found[i]) continue; warnx("failed to find ahci enclosure port \"%s\"", ahci.ahci_argv[i]); ret = 1; } return (ret); }