summaryrefslogtreecommitdiff
path: root/usr/src/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd')
-rw-r--r--usr/src/cmd/Makefile3
-rw-r--r--usr/src/cmd/ahciem/Makefile39
-rw-r--r--usr/src/cmd/ahciem/ahciem.c302
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);
+}