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
45char *opl_opts[] = {
46	"locator",
47	"led",
48	"mode",
49	NULL
50};
51
52
53static scfga_ret_t
54opl_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 */
92static scfga_ret_t
93opl_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 */
151static void
152opl_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 */
183static void
184opl_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
212static scfga_ret_t
213opl_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
238static scfga_ret_t
239opl_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
265static scfga_ret_t
266opl_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*/
303scfga_ret_t
304plat_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