1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <stdio.h>
28 #include <strings.h>
29 #include <libgen.h>
30 #include <cfga_scsi.h>
31 #include <sys/scfd/opcioif.h>
32 
33 
34 #define	SCF_DRV		"/devices/pseudo/scfd@200:rasctl"
35 #define	SCFRETRY	3
36 #define	SCFIOCWAIT	3
37 
38 
39 #define	OPL_LOCATOR_OPT	0
40 #define	OPL_LED_OPT	1
41 #define	OPL_MODE_OPT	2
42 char *opl_opts[] = {
43 	"locator",
44 	"led",
45 	"mode",
46 	NULL
47 };
48 
49 
50 static scfga_ret_t
opl_get_scf_logical_disk(const apid_t * apidp,char ** errstring,scfiocgetdiskled_t * scf_disk)51 opl_get_scf_logical_disk(const apid_t *apidp, char **errstring,
52 					scfiocgetdiskled_t *scf_disk)
53 {
54 	int len;
55 	char *phys_path;
56 	char *strptr;
57 
58 	phys_path  = strdup(apidp->path);
59 	if (phys_path == NULL) {
60 		cfga_err(errstring, ENOMEM, ERR_OP_FAILED, 0);
61 		return (SCFGA_ERR);
62 	}
63 	scf_disk->path[0] = '\0';
64 	if ((strptr = strstr(phys_path, ":")) != NULL) {
65 		strptr[0] = '\0';
66 		len = snprintf((char *)scf_disk->path, sizeof (scf_disk->path),
67 			"%s", (char *)(phys_path));
68 		if (len >= sizeof (scf_disk->path)) {
69 			free(phys_path);
70 			cfga_err(errstring, 0, ERR_OP_FAILED, 0);
71 			return (SCFGA_ERR);
72 		}
73 	} else {
74 		free(phys_path);
75 		cfga_err(errstring, 0, ERR_UNKNOWN, 0);
76 		return (SCFGA_ERR);
77 	}
78 	free(phys_path);
79 
80 	return (SCFGA_OK);
81 }
82 
83 
84 /*
85  * Open the SCF driver and use the ioctl interface to set or get the status.
86  *
87  * Returns 0 on success. Returns OP_FAILED on error.
88  */
89 static scfga_ret_t
opl_disk_led_control(apid_t * apidp,char ** errstring,struct cfga_msg * msgp,int request,scfiocgetdiskled_t * scf_disk)90 opl_disk_led_control(apid_t *apidp, char **errstring, struct cfga_msg *msgp,
91 	int request, scfiocgetdiskled_t *scf_disk)
92 {
93 	scfga_ret_t	retval;
94 	int		scf_fd = -1;
95 	int		retry = 0;
96 
97 	/* paranoid check */
98 	if ((apidp == NULL) || (msgp == NULL) || (scf_disk == NULL)) {
99 		cfga_err(errstring, 0, ERR_UNKNOWN, 0, 0);
100 		return (SCFGA_ERR);
101 	}
102 
103 	retval = opl_get_scf_logical_disk((const apid_t *)apidp, errstring,
104 					scf_disk);
105 	if (retval != SCFGA_OK) {
106 		/* errstring is set in opl_get_scf_logical_disk */
107 		return (retval);
108 	}
109 
110 	/* Open a file descriptor for the scf driver. */
111 	scf_fd = open(SCF_DRV, O_RDWR);
112 	if (scf_fd < 0) {
113 		cfga_err(errstring, errno, ERRARG_OPEN, SCF_DRV, 0);
114 		return (SCFGA_LIB_ERR);
115 	}
116 
117 	/*
118 	 * Use the ioctl interface with the SCF driver to get/set the
119 	 * hdd locator indicator.
120 	 */
121 	errno = 0;
122 	while (ioctl(scf_fd, request, scf_disk) < 0) {
123 		/* Check Retry Error Number */
124 		if (errno != EBUSY && errno != EIO) {
125 			break;
126 		}
127 
128 		/* Check Retry Times */
129 		if (++retry > SCFRETRY) {
130 			break;
131 		}
132 		errno = 0;
133 
134 		(void) sleep(SCFIOCWAIT);
135 	}
136 	(void) close(scf_fd);
137 
138 	if ((errno != 0) || (retry > SCFRETRY)) {
139 		cfga_err(errstring, errno, ERR_OP_FAILED, 0, 0);
140 		return (SCFGA_LIB_ERR);
141 	}
142 	return (SCFGA_OK);
143 }
144 
145 /*
146  * Print the value of the hard disk locator in a human friendly form.
147  */
148 static void
opl_print_locator(apid_t * apidp,struct cfga_msg * msgp,unsigned char led)149 opl_print_locator(apid_t *apidp, struct cfga_msg *msgp, unsigned char led)
150 {
151 	led_modeid_t mode = LED_MODE_UNK;
152 
153 	if ((msgp == NULL) || (msgp->message_routine == NULL)) {
154 		return;
155 	}
156 
157 	cfga_msg(msgp, MSG_LED_HDR, 0);
158 	switch ((int)led) {
159 	case SCF_DISK_LED_ON:
160 		mode = LED_MODE_FAULTED;
161 		break;
162 
163 	case SCF_DISK_LED_OFF:
164 		mode = LED_MODE_OFF;
165 		break;
166 
167 	case SCF_DISK_LED_BLINK:
168 		mode = LED_MODE_ON;
169 		break;
170 
171 	default:
172 		mode = LED_MODE_UNK;
173 	}
174 	cfga_led_msg(msgp, apidp, LED_STR_LOCATOR, mode);
175 }
176 
177 /*
178  * Print the value of the hard disk fault LED in a human friendly form.
179  */
180 static void
opl_print_led(apid_t * apidp,struct cfga_msg * msgp,unsigned char led)181 opl_print_led(apid_t *apidp, struct cfga_msg *msgp, unsigned char led)
182 {
183 	led_modeid_t mode = LED_MODE_UNK;
184 
185 	if ((msgp == NULL) || (msgp->message_routine == NULL)) {
186 		return;
187 	}
188 
189 	cfga_msg(msgp, MSG_LED_HDR, 0);
190 	switch ((int)led) {
191 	case SCF_DISK_LED_ON:
192 		mode = LED_MODE_ON;
193 		break;
194 
195 	case SCF_DISK_LED_OFF:
196 		mode = LED_MODE_OFF;
197 		break;
198 
199 	case SCF_DISK_LED_BLINK:
200 		mode = LED_MODE_BLINK;
201 		break;
202 
203 	default:
204 		mode = LED_MODE_UNK;
205 	}
206 	cfga_led_msg(msgp, apidp, LED_STR_FAULT, mode);
207 }
208 
209 static scfga_ret_t
opl_setlocator(const char * mode,apid_t * apidp,char ** errstring,struct cfga_msg * msgp)210 opl_setlocator(
211 	const char *mode,
212 	apid_t *apidp,
213 	char **errstring,
214 	struct cfga_msg *msgp)
215 {
216 	scfga_ret_t retval;
217 	scfiocgetdiskled_t scf_disk;
218 
219 	if (strcmp(mode, "on") == 0) {
220 		scf_disk.led = SCF_DISK_LED_BLINK;
221 	} else if (strcmp(mode, "off") == 0) {
222 		scf_disk.led = SCF_DISK_LED_OFF;
223 	} else {
224 		cfga_err(errstring, 0, ERRARG_OPT_INVAL, mode, 0);
225 		return (SCFGA_ERR);
226 	}
227 
228 	retval = opl_disk_led_control(apidp, errstring, msgp,
229 					SCFIOCSETDISKLED, &scf_disk);
230 
231 	return (retval);
232 }
233 
234 
235 static scfga_ret_t
opl_getled(int print_switch,apid_t * apidp,char ** errstring,struct cfga_msg * msgp)236 opl_getled(
237 	int print_switch,
238 	apid_t *apidp,
239 	char **errstring,
240 	struct cfga_msg *msgp)
241 {
242 	scfga_ret_t retval;
243 	scfiocgetdiskled_t scf_disk;
244 
245 	(void) memset((void *)&scf_disk, 0, sizeof (scf_disk));
246 
247 	retval = opl_disk_led_control(apidp, errstring, msgp,
248 				SCFIOCGETDISKLED, &scf_disk);
249 	if (retval != SCFGA_OK) {
250 		return (retval);
251 	}
252 	if (print_switch == OPL_LED_OPT) {
253 		opl_print_led(apidp, msgp, scf_disk.led);
254 	} else {
255 		opl_print_locator(apidp, msgp, scf_disk.led);
256 	}
257 
258 	return (SCFGA_OK);
259 }
260 
261 
262 static scfga_ret_t
opl_setled(const char * mode,apid_t * apidp,char ** errstring,struct cfga_msg * msgp)263 opl_setled(
264 	const char *mode,
265 	apid_t *apidp,
266 	char **errstring,
267 	struct cfga_msg *msgp)
268 {
269 	scfga_ret_t retval;
270 	scfiocgetdiskled_t scf_disk;
271 
272 	(void) memset((void *)&scf_disk, 0, sizeof (scf_disk));
273 
274 	if (strcmp(mode, "on") == 0) {
275 		scf_disk.led = SCF_DISK_LED_ON;
276 	} else if (strcmp(mode, "off") == 0) {
277 		scf_disk.led = SCF_DISK_LED_OFF;
278 	} else if (strcmp(mode, "blink") == 0) {
279 		scf_disk.led = SCF_DISK_LED_BLINK;
280 	} else {
281 		cfga_err(errstring, 0, ERRARG_OPT_INVAL, mode, 0);
282 		return (SCFGA_ERR);
283 	}
284 
285 	retval = opl_disk_led_control(apidp, errstring, msgp,
286 					SCFIOCSETDISKLED, &scf_disk);
287 	return (retval);
288 }
289 
290 /*
291  * The func argument is a string in one of the two following forms:
292  *	led=LED[,mode=MODE]
293  *	locator[=on|off]
294  * which can generically be thought of as:
295  *	name=value[,name=value]
296  * So first, split the function based on the comma into two name-value
297  * pairs.
298  */
299 /*ARGSUSED*/
300 scfga_ret_t
plat_dev_led(const char * func,scfga_cmd_t cmd,apid_t * apidp,prompt_t * argsp,cfga_flags_t flags,char ** errstring)301 plat_dev_led(
302 	const char *func,
303 	scfga_cmd_t cmd,
304 	apid_t *apidp,
305 	prompt_t *argsp,
306 	cfga_flags_t flags,
307 	char **errstring)
308 {
309 	scfga_ret_t retval = SCFGA_ERR;
310 	char *optptr = (char *)func;
311 	char *value = NULL;
312 
313 	int opt_locator = 0;
314 	int opt_led = 0;
315 	int opt_mode = 0;
316 	char *locator_value = NULL;
317 	char *led_value = NULL;
318 	char *mode_value = NULL;
319 
320 	if (func == NULL) {
321 		return (SCFGA_ERR);
322 	}
323 
324 	while (*optptr != '\0') {
325 		switch (getsubopt(&optptr, opl_opts, &value)) {
326 		case OPL_LOCATOR_OPT:
327 			opt_locator++;
328 			locator_value = value;
329 			break;
330 		case OPL_LED_OPT:
331 			opt_led++;
332 			led_value = value;
333 			break;
334 		case OPL_MODE_OPT:
335 			opt_mode++;
336 			mode_value = value;
337 			break;
338 		default:
339 			cfga_err(errstring, 0, ERR_CMD_INVAL, 0);
340 			return (SCFGA_OPNOTSUPP);
341 			break;
342 		}
343 	}
344 
345 	if (!opt_locator && !opt_led) {
346 		cfga_err(errstring, 0, ERR_CMD_INVAL, 0);
347 		return (SCFGA_ERR);
348 	}
349 
350 	if (opt_locator) {
351 		if ((opt_locator > 1) || opt_led || opt_mode ||
352 			(strncmp(func, "locator", strlen("locator")) != 0) ||
353 			(locator_value &&
354 			(strcmp(locator_value, "blink") == 0))) {
355 			cfga_err(errstring, 0, ERR_CMD_INVAL, 0);
356 			return (SCFGA_ERR);
357 		}
358 
359 		/* Options are sane so set or get the locator. */
360 		if (locator_value) {
361 			retval = opl_setlocator(locator_value, apidp,
362 				errstring, argsp->msgp);
363 		} else {
364 			retval = opl_getled(OPL_LOCATOR_OPT, apidp, errstring,
365 				argsp->msgp);
366 		}
367 	}
368 	if (opt_led) {
369 		if ((opt_led > 1) || (opt_mode > 1) || (opt_locator) ||
370 				(strncmp(func, "led", strlen("led")) != 0) ||
371 				(!led_value || strcmp(led_value, "fault")) ||
372 				(opt_mode && !mode_value)) {
373 
374 			cfga_err(errstring, 0, ERR_CMD_INVAL, 0);
375 			return (SCFGA_ERR);
376 		}
377 
378 		/* options are sane so go ahead and set or get the led */
379 		if (mode_value != NULL) {
380 			retval = opl_setled(mode_value, apidp, errstring,
381 				argsp->msgp);
382 		} else {
383 			retval = opl_getled(OPL_LED_OPT, apidp, errstring,
384 				argsp->msgp);
385 		}
386 	}
387 	return (retval);
388 
389 }
390