xref: /illumos-gate/usr/src/cmd/ahciem/ahciem.c (revision fd6d41c5)
1*fd6d41c5SRobert Mustacchi /*
2*fd6d41c5SRobert Mustacchi  * This file and its contents are supplied under the terms of the
3*fd6d41c5SRobert Mustacchi  * Common Development and Distribution License ("CDDL"), version 1.0.
4*fd6d41c5SRobert Mustacchi  * You may only use this file in accordance with the terms of version
5*fd6d41c5SRobert Mustacchi  * 1.0 of the CDDL.
6*fd6d41c5SRobert Mustacchi  *
7*fd6d41c5SRobert Mustacchi  * A full copy of the text of the CDDL should have accompanied this
8*fd6d41c5SRobert Mustacchi  * source.  A copy of the CDDL is also available via the Internet at
9*fd6d41c5SRobert Mustacchi  * http://www.illumos.org/license/CDDL.
10*fd6d41c5SRobert Mustacchi  */
11*fd6d41c5SRobert Mustacchi 
12*fd6d41c5SRobert Mustacchi /*
13*fd6d41c5SRobert Mustacchi  * Copyright (c) 2018 Joyent, Inc.
14*fd6d41c5SRobert Mustacchi  */
15*fd6d41c5SRobert Mustacchi 
16*fd6d41c5SRobert Mustacchi #include <stdio.h>
17*fd6d41c5SRobert Mustacchi #include <sys/types.h>
18*fd6d41c5SRobert Mustacchi #include <sys/stat.h>
19*fd6d41c5SRobert Mustacchi #include <fcntl.h>
20*fd6d41c5SRobert Mustacchi #include <errno.h>
21*fd6d41c5SRobert Mustacchi #include <string.h>
22*fd6d41c5SRobert Mustacchi #include <strings.h>
23*fd6d41c5SRobert Mustacchi #include <unistd.h>
24*fd6d41c5SRobert Mustacchi #include <stdlib.h>
25*fd6d41c5SRobert Mustacchi #include <err.h>
26*fd6d41c5SRobert Mustacchi #include <libgen.h>
27*fd6d41c5SRobert Mustacchi #include <libdevinfo.h>
28*fd6d41c5SRobert Mustacchi 
29*fd6d41c5SRobert Mustacchi #include <sys/sata/adapters/ahci/ahciem.h>
30*fd6d41c5SRobert Mustacchi 
31*fd6d41c5SRobert Mustacchi #define	AHCIEM_IDENT	"ident"
32*fd6d41c5SRobert Mustacchi #define	AHCIEM_FAULT	"fault"
33*fd6d41c5SRobert Mustacchi #define	AHCIEM_NOACTIVITY	"noactivity"
34*fd6d41c5SRobert Mustacchi #define	AHCIEM_DEFAULT	"default"
35*fd6d41c5SRobert Mustacchi #define	AHCIEM_UNKNOWN	"unknown"
36*fd6d41c5SRobert Mustacchi 
37*fd6d41c5SRobert Mustacchi #define	EXIT_USAGE	2
38*fd6d41c5SRobert Mustacchi 
39*fd6d41c5SRobert Mustacchi static const char *ahciem_progname;
40*fd6d41c5SRobert Mustacchi 
41*fd6d41c5SRobert Mustacchi typedef struct {
42*fd6d41c5SRobert Mustacchi 	boolean_t		ahci_set;
43*fd6d41c5SRobert Mustacchi 	ahci_em_led_state_t	ahci_led;
44*fd6d41c5SRobert Mustacchi 	int			ahci_argc;
45*fd6d41c5SRobert Mustacchi 	char			**ahci_argv;
46*fd6d41c5SRobert Mustacchi 	boolean_t		*ahci_found;
47*fd6d41c5SRobert Mustacchi 	int			ahci_err;
48*fd6d41c5SRobert Mustacchi } ahciem_t;
49*fd6d41c5SRobert Mustacchi 
50*fd6d41c5SRobert Mustacchi static void
ahciem_usage(const char * fmt,...)51*fd6d41c5SRobert Mustacchi ahciem_usage(const char *fmt, ...)
52*fd6d41c5SRobert Mustacchi {
53*fd6d41c5SRobert Mustacchi 	if (fmt != NULL) {
54*fd6d41c5SRobert Mustacchi 		va_list ap;
55*fd6d41c5SRobert Mustacchi 
56*fd6d41c5SRobert Mustacchi 		va_start(ap, fmt);
57*fd6d41c5SRobert Mustacchi 		vwarnx(fmt, ap);
58*fd6d41c5SRobert Mustacchi 		va_end(ap);
59*fd6d41c5SRobert Mustacchi 	}
60*fd6d41c5SRobert Mustacchi 
61*fd6d41c5SRobert Mustacchi 	(void) fprintf(stderr, "Usage: %s [-s mode] [port]\n"
62*fd6d41c5SRobert Mustacchi 	    "\n"
63*fd6d41c5SRobert Mustacchi 	    "\t-s mode\t\tset LED to mode\n",
64*fd6d41c5SRobert Mustacchi 	    ahciem_progname);
65*fd6d41c5SRobert Mustacchi }
66*fd6d41c5SRobert Mustacchi 
67*fd6d41c5SRobert Mustacchi static const char *
ahciem_led_to_string(uint_t led)68*fd6d41c5SRobert Mustacchi ahciem_led_to_string(uint_t led)
69*fd6d41c5SRobert Mustacchi {
70*fd6d41c5SRobert Mustacchi 	switch (led) {
71*fd6d41c5SRobert Mustacchi 	case AHCI_EM_LED_IDENT_ENABLE:
72*fd6d41c5SRobert Mustacchi 		return (AHCIEM_IDENT);
73*fd6d41c5SRobert Mustacchi 	case AHCI_EM_LED_FAULT_ENABLE:
74*fd6d41c5SRobert Mustacchi 		return (AHCIEM_FAULT);
75*fd6d41c5SRobert Mustacchi 	case AHCI_EM_LED_ACTIVITY_DISABLE:
76*fd6d41c5SRobert Mustacchi 		return (AHCIEM_NOACTIVITY);
77*fd6d41c5SRobert Mustacchi 	case (AHCI_EM_LED_IDENT_ENABLE | AHCI_EM_LED_FAULT_ENABLE):
78*fd6d41c5SRobert Mustacchi 		return (AHCIEM_IDENT "," AHCIEM_FAULT);
79*fd6d41c5SRobert Mustacchi 	case (AHCI_EM_LED_IDENT_ENABLE | AHCI_EM_LED_ACTIVITY_DISABLE):
80*fd6d41c5SRobert Mustacchi 		return (AHCIEM_IDENT "," AHCIEM_NOACTIVITY);
81*fd6d41c5SRobert Mustacchi 	case (AHCI_EM_LED_FAULT_ENABLE | AHCI_EM_LED_ACTIVITY_DISABLE):
82*fd6d41c5SRobert Mustacchi 		return (AHCIEM_FAULT "," AHCIEM_NOACTIVITY);
83*fd6d41c5SRobert Mustacchi 	/* BEGIN CSTYLED */
84*fd6d41c5SRobert Mustacchi 	case (AHCI_EM_LED_IDENT_ENABLE | AHCI_EM_LED_FAULT_ENABLE |
85*fd6d41c5SRobert Mustacchi 	    AHCI_EM_LED_ACTIVITY_DISABLE):
86*fd6d41c5SRobert Mustacchi 		return (AHCIEM_IDENT "," AHCIEM_FAULT "," AHCIEM_NOACTIVITY);
87*fd6d41c5SRobert Mustacchi 	/* END CSTYLED */
88*fd6d41c5SRobert Mustacchi 	case 0:
89*fd6d41c5SRobert Mustacchi 		return (AHCIEM_DEFAULT);
90*fd6d41c5SRobert Mustacchi 	default:
91*fd6d41c5SRobert Mustacchi 		return (AHCIEM_UNKNOWN);
92*fd6d41c5SRobert Mustacchi 	}
93*fd6d41c5SRobert Mustacchi }
94*fd6d41c5SRobert Mustacchi 
95*fd6d41c5SRobert Mustacchi static boolean_t
ahciem_match(ahciem_t * ahci,const char * port)96*fd6d41c5SRobert Mustacchi ahciem_match(ahciem_t *ahci, const char *port)
97*fd6d41c5SRobert Mustacchi {
98*fd6d41c5SRobert Mustacchi 	int i;
99*fd6d41c5SRobert Mustacchi 
100*fd6d41c5SRobert Mustacchi 	if (ahci->ahci_argc == 0)
101*fd6d41c5SRobert Mustacchi 		return (B_TRUE);
102*fd6d41c5SRobert Mustacchi 
103*fd6d41c5SRobert Mustacchi 	for (i = 0; i < ahci->ahci_argc; i++) {
104*fd6d41c5SRobert Mustacchi 		size_t len = strlen(ahci->ahci_argv[i]);
105*fd6d41c5SRobert Mustacchi 
106*fd6d41c5SRobert Mustacchi 		/*
107*fd6d41c5SRobert Mustacchi 		 * Perform a partial match on the base name. This allows us to
108*fd6d41c5SRobert Mustacchi 		 * match all of a controller by using a string like "ahci0".
109*fd6d41c5SRobert Mustacchi 		 */
110*fd6d41c5SRobert Mustacchi 		if (strncmp(ahci->ahci_argv[i], port, len) == 0) {
111*fd6d41c5SRobert Mustacchi 			ahci->ahci_found[i] = B_TRUE;
112*fd6d41c5SRobert Mustacchi 			return (B_TRUE);
113*fd6d41c5SRobert Mustacchi 		}
114*fd6d41c5SRobert Mustacchi 
115*fd6d41c5SRobert Mustacchi 	}
116*fd6d41c5SRobert Mustacchi 
117*fd6d41c5SRobert Mustacchi 	return (B_FALSE);
118*fd6d41c5SRobert Mustacchi }
119*fd6d41c5SRobert Mustacchi 
120*fd6d41c5SRobert Mustacchi static ahci_em_led_state_t
ahciem_parse(const char * arg)121*fd6d41c5SRobert Mustacchi ahciem_parse(const char *arg)
122*fd6d41c5SRobert Mustacchi {
123*fd6d41c5SRobert Mustacchi 	if (strcmp(arg, AHCIEM_IDENT) == 0) {
124*fd6d41c5SRobert Mustacchi 		return (AHCI_EM_LED_IDENT_ENABLE);
125*fd6d41c5SRobert Mustacchi 	} else if (strcmp(arg, AHCIEM_FAULT) == 0) {
126*fd6d41c5SRobert Mustacchi 		return (AHCI_EM_LED_FAULT_ENABLE);
127*fd6d41c5SRobert Mustacchi 	} else if (strcmp(arg, AHCIEM_NOACTIVITY) == 0) {
128*fd6d41c5SRobert Mustacchi 		return (AHCI_EM_LED_ACTIVITY_DISABLE);
129*fd6d41c5SRobert Mustacchi 	} else if (strcmp(arg, AHCIEM_DEFAULT) == 0) {
130*fd6d41c5SRobert Mustacchi 		return (0);
131*fd6d41c5SRobert Mustacchi 	}
132*fd6d41c5SRobert Mustacchi 
133*fd6d41c5SRobert Mustacchi 	errx(EXIT_USAGE, "invalid LED mode with -s: %s", arg);
134*fd6d41c5SRobert Mustacchi }
135*fd6d41c5SRobert Mustacchi 
136*fd6d41c5SRobert Mustacchi static void
ahciem_set(ahciem_t * ahci,const char * portstr,int fd,int port)137*fd6d41c5SRobert Mustacchi ahciem_set(ahciem_t *ahci, const char *portstr, int fd, int port)
138*fd6d41c5SRobert Mustacchi {
139*fd6d41c5SRobert Mustacchi 	ahci_ioc_em_set_t set;
140*fd6d41c5SRobert Mustacchi 
141*fd6d41c5SRobert Mustacchi 	bzero(&set, sizeof (set));
142*fd6d41c5SRobert Mustacchi 
143*fd6d41c5SRobert Mustacchi 	set.aiems_port = port;
144*fd6d41c5SRobert Mustacchi 	set.aiems_op = AHCI_EM_IOC_SET_OP_SET;
145*fd6d41c5SRobert Mustacchi 	set.aiems_leds = ahci->ahci_led;
146*fd6d41c5SRobert Mustacchi 
147*fd6d41c5SRobert Mustacchi 	if (ioctl(fd, AHCI_EM_IOC_SET, &set) != 0) {
148*fd6d41c5SRobert Mustacchi 		warn("failed to set LEDs on %s", portstr);
149*fd6d41c5SRobert Mustacchi 		ahci->ahci_err = 1;
150*fd6d41c5SRobert Mustacchi 	}
151*fd6d41c5SRobert Mustacchi }
152*fd6d41c5SRobert Mustacchi 
153*fd6d41c5SRobert Mustacchi static int
ahciem_devinfo(di_node_t node,void * arg)154*fd6d41c5SRobert Mustacchi ahciem_devinfo(di_node_t node, void *arg)
155*fd6d41c5SRobert Mustacchi {
156*fd6d41c5SRobert Mustacchi 	char *driver, *mpath, *fullpath;
157*fd6d41c5SRobert Mustacchi 	const char *sup;
158*fd6d41c5SRobert Mustacchi 	int inst, fd;
159*fd6d41c5SRobert Mustacchi 	uint_t i;
160*fd6d41c5SRobert Mustacchi 	ahciem_t *ahci = arg;
161*fd6d41c5SRobert Mustacchi 	di_minor_t m;
162*fd6d41c5SRobert Mustacchi 	ahci_ioc_em_get_t get;
163*fd6d41c5SRobert Mustacchi 
164*fd6d41c5SRobert Mustacchi 	if ((driver = di_driver_name(node)) == NULL)
165*fd6d41c5SRobert Mustacchi 		return (DI_WALK_CONTINUE);
166*fd6d41c5SRobert Mustacchi 	if (strcmp(driver, "ahci") != 0)
167*fd6d41c5SRobert Mustacchi 		return (DI_WALK_CONTINUE);
168*fd6d41c5SRobert Mustacchi 	inst = di_instance(node);
169*fd6d41c5SRobert Mustacchi 
170*fd6d41c5SRobert Mustacchi 	m = DI_MINOR_NIL;
171*fd6d41c5SRobert Mustacchi 	while ((m = di_minor_next(node, m)) != DI_MINOR_NIL) {
172*fd6d41c5SRobert Mustacchi 		char *mname = di_minor_name(m);
173*fd6d41c5SRobert Mustacchi 
174*fd6d41c5SRobert Mustacchi 		if (mname != NULL && strcmp("devctl", mname) == 0)
175*fd6d41c5SRobert Mustacchi 			break;
176*fd6d41c5SRobert Mustacchi 	}
177*fd6d41c5SRobert Mustacchi 
178*fd6d41c5SRobert Mustacchi 	if (m == DI_MINOR_NIL) {
179*fd6d41c5SRobert Mustacchi 		warnx("encountered ahci%d without devctl node", inst);
180*fd6d41c5SRobert Mustacchi 		return (DI_WALK_PRUNECHILD);
181*fd6d41c5SRobert Mustacchi 	}
182*fd6d41c5SRobert Mustacchi 
183*fd6d41c5SRobert Mustacchi 	if ((mpath = di_devfs_minor_path(m)) == NULL) {
184*fd6d41c5SRobert Mustacchi 		warnx("failed to get path for ahci%d devctl minor", inst);
185*fd6d41c5SRobert Mustacchi 		return (DI_WALK_PRUNECHILD);
186*fd6d41c5SRobert Mustacchi 	}
187*fd6d41c5SRobert Mustacchi 
188*fd6d41c5SRobert Mustacchi 	if (asprintf(&fullpath, "/devices/%s", mpath) == -1) {
189*fd6d41c5SRobert Mustacchi 		warn("failed to construct /devices path from %s", mpath);
190*fd6d41c5SRobert Mustacchi 		return (DI_WALK_PRUNECHILD);
191*fd6d41c5SRobert Mustacchi 	}
192*fd6d41c5SRobert Mustacchi 
193*fd6d41c5SRobert Mustacchi 	if ((fd = open(fullpath, O_RDWR)) < 0) {
194*fd6d41c5SRobert Mustacchi 		warn("failed to open ahci%d devctl path %s", inst, fullpath);
195*fd6d41c5SRobert Mustacchi 		goto out;
196*fd6d41c5SRobert Mustacchi 	}
197*fd6d41c5SRobert Mustacchi 
198*fd6d41c5SRobert Mustacchi 	bzero(&get, sizeof (get));
199*fd6d41c5SRobert Mustacchi 	if (ioctl(fd, AHCI_EM_IOC_GET, &get) != 0) {
200*fd6d41c5SRobert Mustacchi 		warn("failed to get AHCI enclosure information for ahci%d",
201*fd6d41c5SRobert Mustacchi 		    inst);
202*fd6d41c5SRobert Mustacchi 		ahci->ahci_err = 1;
203*fd6d41c5SRobert Mustacchi 		goto out;
204*fd6d41c5SRobert Mustacchi 	}
205*fd6d41c5SRobert Mustacchi 
206*fd6d41c5SRobert Mustacchi 	if ((get.aiemg_flags & AHCI_EM_FLAG_CONTROL_ACTIVITY) != 0) {
207*fd6d41c5SRobert Mustacchi 		sup = ahciem_led_to_string(AHCI_EM_LED_IDENT_ENABLE |
208*fd6d41c5SRobert Mustacchi 		    AHCI_EM_LED_FAULT_ENABLE | AHCI_EM_LED_ACTIVITY_DISABLE);
209*fd6d41c5SRobert Mustacchi 	} else {
210*fd6d41c5SRobert Mustacchi 		sup = ahciem_led_to_string(AHCI_EM_LED_IDENT_ENABLE |
211*fd6d41c5SRobert Mustacchi 		    AHCI_EM_LED_FAULT_ENABLE);
212*fd6d41c5SRobert Mustacchi 	}
213*fd6d41c5SRobert Mustacchi 
214*fd6d41c5SRobert Mustacchi 	for (i = 0; i < AHCI_EM_IOC_MAX_PORTS; i++) {
215*fd6d41c5SRobert Mustacchi 		char port[64];
216*fd6d41c5SRobert Mustacchi 		const char *state;
217*fd6d41c5SRobert Mustacchi 
218*fd6d41c5SRobert Mustacchi 		if (((1 << i) & get.aiemg_nports) == 0)
219*fd6d41c5SRobert Mustacchi 			continue;
220*fd6d41c5SRobert Mustacchi 
221*fd6d41c5SRobert Mustacchi 		(void) snprintf(port, sizeof (port), "ahci%d/%u", inst, i);
222*fd6d41c5SRobert Mustacchi 		if (!ahciem_match(ahci, port))
223*fd6d41c5SRobert Mustacchi 			continue;
224*fd6d41c5SRobert Mustacchi 
225*fd6d41c5SRobert Mustacchi 		if (ahci->ahci_set) {
226*fd6d41c5SRobert Mustacchi 			ahciem_set(ahci, port, fd, i);
227*fd6d41c5SRobert Mustacchi 			continue;
228*fd6d41c5SRobert Mustacchi 		}
229*fd6d41c5SRobert Mustacchi 
230*fd6d41c5SRobert Mustacchi 		state = ahciem_led_to_string(get.aiemg_status[i]);
231*fd6d41c5SRobert Mustacchi 		(void) printf("%-20s %-12s %s,default\n", port, state, sup);
232*fd6d41c5SRobert Mustacchi 	}
233*fd6d41c5SRobert Mustacchi 
234*fd6d41c5SRobert Mustacchi out:
235*fd6d41c5SRobert Mustacchi 	free(fullpath);
236*fd6d41c5SRobert Mustacchi 	return (DI_WALK_PRUNECHILD);
237*fd6d41c5SRobert Mustacchi }
238*fd6d41c5SRobert Mustacchi 
239*fd6d41c5SRobert Mustacchi int
main(int argc,char * argv[])240*fd6d41c5SRobert Mustacchi main(int argc, char *argv[])
241*fd6d41c5SRobert Mustacchi {
242*fd6d41c5SRobert Mustacchi 	int c, i, ret;
243*fd6d41c5SRobert Mustacchi 	di_node_t root;
244*fd6d41c5SRobert Mustacchi 	ahciem_t ahci;
245*fd6d41c5SRobert Mustacchi 
246*fd6d41c5SRobert Mustacchi 	ahciem_progname = basename(argv[0]);
247*fd6d41c5SRobert Mustacchi 
248*fd6d41c5SRobert Mustacchi 	bzero(&ahci, sizeof (ahciem_t));
249*fd6d41c5SRobert Mustacchi 	while ((c = getopt(argc, argv, ":s:")) != -1) {
250*fd6d41c5SRobert Mustacchi 		switch (c) {
251*fd6d41c5SRobert Mustacchi 		case 's':
252*fd6d41c5SRobert Mustacchi 			ahci.ahci_set = B_TRUE;
253*fd6d41c5SRobert Mustacchi 			ahci.ahci_led = ahciem_parse(optarg);
254*fd6d41c5SRobert Mustacchi 			break;
255*fd6d41c5SRobert Mustacchi 		case ':':
256*fd6d41c5SRobert Mustacchi 			ahciem_usage("option -%c requires an operand\n",
257*fd6d41c5SRobert Mustacchi 			    optopt);
258*fd6d41c5SRobert Mustacchi 			return (EXIT_USAGE);
259*fd6d41c5SRobert Mustacchi 		case '?':
260*fd6d41c5SRobert Mustacchi 		default:
261*fd6d41c5SRobert Mustacchi 			ahciem_usage("unknown option: -%c\n", optopt);
262*fd6d41c5SRobert Mustacchi 			return (EXIT_USAGE);
263*fd6d41c5SRobert Mustacchi 		}
264*fd6d41c5SRobert Mustacchi 	}
265*fd6d41c5SRobert Mustacchi 
266*fd6d41c5SRobert Mustacchi 	argc -= optind;
267*fd6d41c5SRobert Mustacchi 	argv += optind;
268*fd6d41c5SRobert Mustacchi 	ahci.ahci_argc = argc;
269*fd6d41c5SRobert Mustacchi 	ahci.ahci_argv = argv;
270*fd6d41c5SRobert Mustacchi 	if (argc > 0) {
271*fd6d41c5SRobert Mustacchi 		ahci.ahci_found = calloc(argc, sizeof (boolean_t));
272*fd6d41c5SRobert Mustacchi 		if (ahci.ahci_found == NULL) {
273*fd6d41c5SRobert Mustacchi 			err(EXIT_FAILURE, "failed to alloc memory for %d "
274*fd6d41c5SRobert Mustacchi 			    "booleans", argc);
275*fd6d41c5SRobert Mustacchi 		}
276*fd6d41c5SRobert Mustacchi 	}
277*fd6d41c5SRobert Mustacchi 
278*fd6d41c5SRobert Mustacchi 	if ((root = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) {
279*fd6d41c5SRobert Mustacchi 		err(EXIT_FAILURE, "failed to open devinfo tree");
280*fd6d41c5SRobert Mustacchi 	}
281*fd6d41c5SRobert Mustacchi 
282*fd6d41c5SRobert Mustacchi 	if (!ahci.ahci_set) {
283*fd6d41c5SRobert Mustacchi 		(void) printf("%-20s %-12s %s\n", "PORT", "ACTIVE",
284*fd6d41c5SRobert Mustacchi 		    "SUPPORTED");
285*fd6d41c5SRobert Mustacchi 	}
286*fd6d41c5SRobert Mustacchi 
287*fd6d41c5SRobert Mustacchi 	if (di_walk_node(root, DI_WALK_CLDFIRST, &ahci,
288*fd6d41c5SRobert Mustacchi 	    ahciem_devinfo) != 0) {
289*fd6d41c5SRobert Mustacchi 		err(EXIT_FAILURE, "failed to walk devinfo tree");
290*fd6d41c5SRobert Mustacchi 	}
291*fd6d41c5SRobert Mustacchi 
292*fd6d41c5SRobert Mustacchi 	ret = ahci.ahci_err;
293*fd6d41c5SRobert Mustacchi 	for (i = 0; i < argc; i++) {
294*fd6d41c5SRobert Mustacchi 		if (ahci.ahci_found[i])
295*fd6d41c5SRobert Mustacchi 			continue;
296*fd6d41c5SRobert Mustacchi 		warnx("failed to find ahci enclosure port \"%s\"",
297*fd6d41c5SRobert Mustacchi 		    ahci.ahci_argv[i]);
298*fd6d41c5SRobert Mustacchi 		ret = 1;
299*fd6d41c5SRobert Mustacchi 	}
300*fd6d41c5SRobert Mustacchi 
301*fd6d41c5SRobert Mustacchi 	return (ret);
302*fd6d41c5SRobert Mustacchi }
303