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
opl_get_scf_logical_disk(const apid_t * apidp,char ** errstring,scfiocgetdiskled_t * scf_disk)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
opl_disk_led_control(apid_t * apidp,char ** errstring,struct cfga_msg * msgp,int request,scfiocgetdiskled_t * scf_disk)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
opl_print_locator(apid_t * apidp,struct cfga_msg * msgp,unsigned char led)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
opl_print_led(apid_t * apidp,struct cfga_msg * msgp,unsigned char led)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
opl_setlocator(const char * mode,apid_t * apidp,char ** errstring,struct cfga_msg * msgp)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
opl_getled(int print_switch,apid_t * apidp,char ** errstring,struct cfga_msg * msgp)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
opl_setled(const char * mode,apid_t * apidp,char ** errstring,struct cfga_msg * msgp)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
plat_dev_led(const char * func,scfga_cmd_t cmd,apid_t * apidp,prompt_t * argsp,cfga_flags_t flags,char ** errstring)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