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