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