diff options
Diffstat (limited to 'usr/src/cmd')
-rw-r--r-- | usr/src/cmd/Makefile | 3 | ||||
-rw-r--r-- | usr/src/cmd/ahciem/Makefile | 39 | ||||
-rw-r--r-- | usr/src/cmd/ahciem/ahciem.c | 302 |
3 files changed, 343 insertions, 1 deletions
diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile index e69f940e11..9d42f388cd 100644 --- a/usr/src/cmd/Makefile +++ b/usr/src/cmd/Makefile @@ -21,7 +21,7 @@ # # Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. -# Copyright (c) 2017, Joyent, Inc. +# Copyright (c) 2018, Joyent, Inc. # Copyright (c) 2012 by Delphix. All rights reserved. # Copyright (c) 2013 DEY Storage Systems, Inc. All rights reserved. # Copyright 2014 Garrett D'Amore <garrett@damore.org> @@ -60,6 +60,7 @@ COMMON_SUBDIRS= \ adbgen \ acct \ acctadm \ + ahciem \ arch \ asa \ ast \ diff --git a/usr/src/cmd/ahciem/Makefile b/usr/src/cmd/ahciem/Makefile new file mode 100644 index 0000000000..7c9efa5f27 --- /dev/null +++ b/usr/src/cmd/ahciem/Makefile @@ -0,0 +1,39 @@ +# +# 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. +# + +PROG= ahciem +LINTPROGS= $(PROG:%=%.ln) + +include ../Makefile.cmd + +ROOTCMDDIR = $(ROOTLIB)/ahci +CPPFLAGS += -I$(SRC)/uts/common/ +CFLAGS += $(CCVERBOSE) +LDLIBS += -ldevinfo + +.KEEP_STATE: + +all: $(PROG) + +install: all $(ROOTCMD) + +clean: + +%.ln: %.c + $(LINT.c) $< $(LDLIBS) + +lint: $(LINTPROGS) + +include ../Makefile.targ diff --git a/usr/src/cmd/ahciem/ahciem.c b/usr/src/cmd/ahciem/ahciem.c new file mode 100644 index 0000000000..65ecf2a425 --- /dev/null +++ b/usr/src/cmd/ahciem/ahciem.c @@ -0,0 +1,302 @@ +/* + * 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 <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <strings.h> +#include <unistd.h> +#include <stdlib.h> +#include <err.h> +#include <libgen.h> +#include <libdevinfo.h> + +#include <sys/sata/adapters/ahci/ahciem.h> + +#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(ahci_em_led_state_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); +} |