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 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 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 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 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 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 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 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 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