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