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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright (c) 2001 by Sun Microsystems, Inc. 24 * All rights reserved. 25 */ 26 27 #include <stdio.h> 28 #include <unistd.h> 29 #include <fcntl.h> 30 #include <string.h> 31 #include <stdlib.h> 32 #include <errno.h> 33 #include <stdarg.h> 34 #include <pthread.h> 35 #include <libintl.h> 36 #include <libdevinfo.h> 37 #include <syslog.h> 38 #include <sys/i2c/clients/i2c_client.h> 39 #include <poll.h> 40 #include "fcal_leds.h" 41 42 static char fcal_disk_unit[] = FCAL_PICL_DISK_UNIT; 43 44 static int update_picl(led_dtls_t *dtls, int disk); 45 static int get_drv_info(di_node_t node, led_dtls_t *dtls); 46 static int walk_disks(di_node_t node, led_dtls_t *dtls); 47 static int chk_minors(led_dtls_t *dtls); 48 static void set_led(int diskNo, token_t led_tok, led_dtls_t *dtls); 49 static int set_clr_led(int diskNo, token_t led_tok, led_dtls_t *dtls, int set); 50 void set_led(int diskNo, token_t led_tok, led_dtls_t *dtls); 51 void clr_led(int diskNo, token_t led_tok, led_dtls_t *dtls); 52 static void retry_led(led_dtls_t *dtls); 53 static void start_led_test(led_dtls_t *dtls, int disk); 54 static void end_led_test(led_dtls_t *dtls, int disk); 55 static int wait_a_while(void); 56 57 /* 58 * variant of strerror() which guards against negative errno and null strings 59 */ 60 char * 61 mystrerror(int err) 62 { 63 static char *unknown_errno = "unknown errno"; 64 char *ptr; 65 66 if ((err < 0) || ((ptr = strerror(err)) == NULL)) { 67 ptr = unknown_errno; 68 } 69 return (ptr); 70 } 71 72 void 73 delete_disk_unit(led_dtls_t *dtls, int disk) 74 { 75 int r; 76 picl_nodehdl_t slotndh; 77 picl_nodehdl_t diskndh; 78 79 r = find_disk_slot(dtls, disk, &slotndh); 80 if (r != PICL_SUCCESS) 81 return; 82 83 /* 84 * is there a disk-unit node here? 85 */ 86 r = ptree_find_node(slotndh, PICL_PROP_NAME, 87 PICL_PTYPE_CHARSTRING, fcal_disk_unit, 88 sizeof (fcal_disk_unit), &diskndh); 89 if (r != PICL_SUCCESS) 90 return; 91 92 /* 93 * remove disk-unit node and its properties 94 */ 95 r = ptree_delete_node(diskndh); 96 if (r != PICL_SUCCESS) 97 return; 98 (void) ptree_destroy_node(diskndh); 99 } 100 101 /* 102 * update_picl 103 * Called when disk goes off-line or goes to ready status. 104 * In the case of disk ready, locate platform tree node for the disk 105 * and add a target property (if missing). 106 * (The target address is fixed for a given disk slot and is used to 107 * tie the frutree disk-unit to the correct ssd node). 108 * Returns EAGAIN for a retriable failure, otherwise 0. 109 */ 110 static int 111 update_picl(led_dtls_t *dtls, int disk) 112 { 113 static char trailer[] = ",0"; 114 picl_nodehdl_t slotndh; 115 picl_nodehdl_t diskndh; 116 ptree_propinfo_t propinfo; 117 int r; 118 119 if (dtls->disk_detected[disk] != 0) { 120 picl_nodehdl_t fpndh; 121 picl_nodehdl_t ssdndh; 122 picl_prophdl_t tbl_h; 123 picl_prophdl_t tbl_prop_h; 124 picl_prophdl_t row_props_h[FCAL_DEVTABLE_NCOLS]; 125 char valbuf[80]; 126 char addr[MAXPATHLEN]; 127 char *ptrd; 128 const uchar_t *ptrs; 129 int len; 130 int addr_len; 131 132 for (;;) { 133 r = ptree_get_node_by_path(dtls->fcal_disk_parent, 134 &fpndh); 135 if (r != PICL_SUCCESS) { 136 return (0); 137 } 138 r = ptree_get_propval_by_name(fpndh, 139 PICL_PROP_CLASSNAME, (void *)valbuf, 140 sizeof (valbuf)); 141 if (r != PICL_SUCCESS) { 142 return (0); 143 } else if (strcmp(valbuf, "fp") == 0) { 144 /* 145 * The node with class fp (if present) is a 146 * holding node representing no actual hardware. 147 * Its presence results in two nodes with the 148 * same effective address. (The fp class node is 149 * UnitAddress 0,0 and the other fp node [class 150 * devctl] has bus-addr 0,0). Locating the 151 * required fp node for dynamic reconfiguration 152 * then goes wrong. So, just remove it. 153 */ 154 SYSLOG(LOG_WARNING, EM_SPURIOUS_FP); 155 r = ptree_delete_node(fpndh); 156 if (r == PICL_SUCCESS) { 157 (void) ptree_destroy_node(fpndh); 158 continue; 159 } 160 return (0); 161 } else { 162 break; 163 } 164 } 165 /* 166 * Got a good parent node. Look at its children for a node 167 * with this new port name. 168 * 169 * generate expected bus-addr property from the port-wwn 170 * Note: dtls->disk_port[disk] points to an array of uchar_t, 171 * the first character contains the length of the residue. 172 * The bus-addr property is formatted as follows: 173 * wabcdef0123456789,0 174 * where the 16 hex-digits represent 8 bytes from disk_port[]; 175 */ 176 ptrs = dtls->disk_port[disk]; 177 if (ptrs == NULL) 178 return (0); 179 len = *ptrs++; 180 ptrd = addr; 181 *ptrd++ = 'w'; 182 for (r = 0; r < len; r++, ptrd += 2) { 183 (void) snprintf(ptrd, MAXPATHLEN - (ptrd - addr), 184 "%.2x", *ptrs++); 185 } 186 addr_len = 1 + strlcat(addr, trailer, MAXPATHLEN); 187 if (addr_len > MAXPATHLEN) 188 return (0); 189 r = ptree_find_node(fpndh, FCAL_PICL_PROP_BUS_ADDR, 190 PICL_PTYPE_CHARSTRING, addr, addr_len, &ssdndh); 191 /* 192 * If the disk node corresponding to the newly inserted disk 193 * cannot be found in the platform tree, we have probably 194 * got in too early - probably before it's up to speed. In 195 * this case, the WWN gleaned from devinfo may also be wrong. 196 * This case is worth retrying in later polls when it may 197 * succeed, so return EAGAIN. All other failures are probably 198 * terminal, so log a failure and quit. 199 */ 200 if (r == PICL_NODENOTFOUND) 201 return (EAGAIN); 202 if (r != PICL_SUCCESS) { 203 SYSLOG(LOG_ERR, EM_NO_FP_NODE, disk); 204 return (0); 205 } 206 207 /* 208 * Found platform entry for disk, add target prop 209 */ 210 r = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, 211 PICL_PTYPE_INT, PICL_READ, sizeof (int), 212 FCAL_PICL_PROP_TARGET, NULL, NULL); 213 if (r != PICL_SUCCESS) 214 return (0); 215 (void) ptree_create_and_add_prop(ssdndh, &propinfo, &disk, 216 NULL); 217 218 /* 219 * Remove pre-existing disk-unit node and its 220 * properties - maybe its reference property is 221 * out-of-date. 222 */ 223 delete_disk_unit(dtls, disk); 224 225 /* 226 * Add a disk-unit node in frutree 227 */ 228 r = find_disk_slot(dtls, disk, &slotndh); 229 if (r != PICL_SUCCESS) 230 return (0); 231 r = ptree_create_and_add_node(slotndh, fcal_disk_unit, 232 PICL_CLASS_FRU, &diskndh); 233 if (r != PICL_SUCCESS) 234 return (0); 235 r = create_Device_table(&tbl_h, &tbl_prop_h); 236 if (r != PICL_SUCCESS) 237 return (0); 238 r = ptree_init_propinfo(&propinfo, 239 PTREE_PROPINFO_VERSION, PICL_PTYPE_CHARSTRING, 240 PICL_READ, sizeof (PICL_CLASS_BLOCK), PICL_PROP_CLASS, 241 NULL, NULL); 242 if (r != PICL_SUCCESS) 243 return (0); 244 r = ptree_create_prop(&propinfo, PICL_CLASS_BLOCK, 245 &row_props_h[0]); 246 if (r != PICL_SUCCESS) 247 return (0); 248 r = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, 249 PICL_PTYPE_REFERENCE, PICL_READ, sizeof (picl_prophdl_t), 250 FCAL_PICL_BLOCK_REF, NULL, NULL); 251 if (r != PICL_SUCCESS) 252 return (0); 253 r = ptree_create_prop(&propinfo, &ssdndh, &row_props_h[1]); 254 if (r != PICL_SUCCESS) 255 return (0); 256 r = ptree_add_row_to_table(tbl_h, FCAL_DEVTABLE_NCOLS, 257 row_props_h); 258 if (r != PICL_SUCCESS) 259 return (0); 260 (void) ptree_add_prop(diskndh, tbl_prop_h); 261 } else { 262 /* 263 * disk gone, remove disk_unit fru from frutree 264 */ 265 delete_disk_unit(dtls, disk); 266 } 267 return (0); 268 } 269 270 static int 271 get_drv_info(di_node_t node, led_dtls_t *dtls) 272 { 273 int *target_data; 274 uchar_t *port_data = NULL; 275 di_minor_t min_node; 276 int i, r; 277 int t = -1; 278 int *newStatus = malloc(dtls->n_disks * sizeof (int)); 279 if (newStatus == NULL) 280 return (0); 281 282 for (i = 0; i < dtls->n_disks; i++) { 283 newStatus[i] = MINORS_UNKNOWN; 284 } 285 r = di_prop_lookup_ints(DDI_DEV_T_ANY, node, HW_PROP_TARGET, 286 &target_data); 287 for (i = 0; i < r; i++) { 288 t = target_data[i]; 289 if ((t >= 0) && (t < dtls->n_disks)) { 290 /* set no minors until we know */ 291 newStatus[t] = NO_MINORS; 292 break; /* go with this node */ 293 } 294 } 295 if ((t >= 0) && (t < dtls->n_disks)) { 296 r = di_prop_lookup_bytes( 297 DDI_DEV_T_ANY, node, HW_PROP_PORT, &port_data); 298 /* 299 * The first byte of the array dtls->disk_port[t] contains 300 * the length of the residue. So 255 is the maximum length 301 * which can be handled. Limit the property data to this. 302 */ 303 if (r > 255) { 304 r = 0; 305 } 306 if ((r > 0) && (port_data != NULL)) { 307 if ((dtls->disk_port[t] != NULL) && 308 (*(dtls->disk_port[t]) != r)) { 309 /* 310 * existing data is of different length, 311 * free it and malloc a fresh array. 312 */ 313 free(dtls->disk_port[t]); 314 dtls->disk_port[t] = NULL; 315 } 316 if (dtls->disk_port[t] == NULL) { 317 dtls->disk_port[t] = malloc(r + 1); 318 } 319 if (dtls->disk_port[t] != NULL) { 320 *(dtls->disk_port[t]) = (uchar_t)r; 321 (void) memcpy(dtls->disk_port[t] + 1, 322 port_data, r); 323 } 324 } 325 min_node = di_minor_next(node, DI_MINOR_NIL); 326 if (min_node != DI_MINOR_NIL) { 327 /* 328 * device has minor device node(s) 329 */ 330 newStatus[t] = HAS_MINORS; /* got minor(s) */ 331 } 332 } 333 /* 334 * propagate attachment status and note changes 335 * don't propagate to absent disks, otherwise we may not detect a 336 * status change when they're replugged. 337 */ 338 r = 0; 339 for (i = 0; i < dtls->n_disks; i++) { 340 if ((newStatus[i] != MINORS_UNKNOWN) && 341 dtls->disk_detected[i] && 342 (dtls->disk_ready[i] != newStatus[i])) { 343 dtls->disk_ready[i] = newStatus[i]; 344 r = 1; 345 } 346 } 347 free(newStatus); 348 return (r); 349 } 350 351 /* 352 * Nodes belonging to the configured driver (dtls->fcal_driver) are 353 * located in the device tree. A check is applied that any node found has 354 * a physical address beginning with the configured search string 355 * (dtls->fcal_disk_parent). For each suitable node found, get_drv_info() 356 * is called to determine if a change of status has occurred. 357 * Returns 1 if any status has changed - else 0. 358 */ 359 static int 360 walk_disks(di_node_t node, led_dtls_t *dtls) 361 { 362 static char *sl_platform_sl = "/platform/"; 363 int r = 0; 364 int len; 365 /* find "/platform/" */ 366 char *ptr = strstr(dtls->fcal_disk_parent, sl_platform_sl); 367 368 if (ptr == NULL) 369 return (0); 370 /* skip over "/platform" */ 371 ptr += strlen(sl_platform_sl) - 1; 372 len = strlen(ptr); 373 374 for (node = di_drv_first_node(dtls->fcal_driver, node); 375 node != DI_NODE_NIL; 376 node = di_drv_next_node(node)) { 377 char *dev_path = di_devfs_path(node); 378 379 if (dev_path == NULL) { 380 /* no memory, just hope things get better */ 381 continue; 382 } 383 if (memcmp(dev_path, ptr, len) != 0) { 384 /* 385 * path name doesn't start right, skip this one 386 */ 387 free(dev_path); 388 continue; 389 } 390 free(dev_path); 391 if (get_drv_info(node, dtls) != 0) { 392 r = 1; /* change observed */ 393 } 394 } 395 396 return (r); 397 } 398 399 static int 400 chk_minors(led_dtls_t *dtls) 401 { 402 /* 403 * sets disk_ready flags for disks with minor devices (attached) 404 * returns 1 if any flags have changed else 0 405 */ 406 int err = 0; 407 int r = 0; 408 di_node_t tree = di_init("/", DINFOCPYALL); 409 if (tree == DI_NODE_NIL) { 410 err = errno; 411 SYSLOG(LOG_ERR, EM_DI_INIT_FAIL, mystrerror(err)); 412 } 413 if (err == 0) 414 r = walk_disks(tree, dtls); 415 if (tree != DI_NODE_NIL) 416 di_fini(tree); 417 return (r); 418 } 419 420 boolean_t 421 is_led_test(led_dtls_t *dtls) 422 { 423 int disk; 424 for (disk = 0; disk < dtls->n_disks; disk++) { 425 if (dtls->led_test_end[disk] != 0) 426 return (B_TRUE); 427 } 428 return (B_FALSE); 429 } 430 431 static int 432 set_clr_led(int diskNo, token_t led_tok, led_dtls_t *dtls, int set) 433 { 434 int err, led, disk, led_bit; 435 i2c_port_t port; 436 int mask = 0; 437 int fd = open(dtls->fcal_leds, O_RDWR); 438 if (fd < 0) 439 return (0); 440 /* 441 * generate a mask for all controlled LEDs 442 */ 443 for (led = 0; led < FCAL_LED_CNT; led++) { 444 for (disk = 0; disk < dtls->n_disks; disk++) { 445 mask |= dtls->led_addr[led][disk]; 446 } 447 } 448 port.value = 0; 449 port.direction = DIR_INPUT; 450 port.dir_mask = (uint8_t)mask; 451 /* read current light settings */ 452 err = ioctl(fd, I2C_GET_PORT, &port); 453 if (err < 0) { 454 (void) close(fd); 455 return (EAGAIN); 456 } 457 mask = port.value; 458 /* 459 * get bit setting for led to be changed 460 */ 461 led = led_tok - LED_PROPS_START - 1; 462 led_bit = dtls->led_addr[led][diskNo]; 463 if (dtls->assert_led_on == 0) { 464 if (set == 0) 465 mask |= led_bit; 466 else 467 mask &= ~led_bit; 468 } else { 469 if (set == 0) 470 mask &= ~led_bit; 471 else 472 mask |= led_bit; 473 } 474 475 /* 476 * re-write the leds 477 */ 478 port.value = (uint8_t)mask; 479 err = ioctl(fd, I2C_SET_PORT, &port); 480 (void) close(fd); 481 if (err == 0) 482 return (0); 483 return (EAGAIN); 484 } 485 486 static void 487 set_led(int diskNo, token_t led_tok, led_dtls_t *dtls) 488 { 489 if (set_clr_led(diskNo, led_tok, dtls, 1) != 0) 490 dtls->led_retry = B_TRUE; 491 dtls->led_state[led_tok - LED_PROPS_START - 1][diskNo] = 492 ((dtls->led_test_end[diskNo] != 0) ? 493 LED_STATE_TEST : LED_STATE_ON); 494 } 495 496 void 497 clr_led(int diskNo, token_t led_tok, led_dtls_t *dtls) 498 { 499 if (set_clr_led(diskNo, led_tok, dtls, 0) != 0) 500 dtls->led_retry = B_TRUE; 501 dtls->led_state[led_tok - LED_PROPS_START - 1][diskNo] = 502 LED_STATE_OFF; 503 } 504 505 /* 506 * have another go at getting the leds in step with required values 507 */ 508 static void 509 retry_led(led_dtls_t *dtls) 510 { 511 int r = 0; 512 int onFlag; 513 int diskNo; 514 int ledNo; 515 led_state_t state; 516 517 for (diskNo = 0; diskNo < dtls->n_disks; diskNo++) { 518 for (ledNo = 0; ledNo < FCAL_LED_CNT; ledNo++) { 519 state = dtls->led_state[ledNo][diskNo]; 520 if ((state == LED_STATE_ON) || 521 (state == LED_STATE_TEST)) 522 onFlag = 1; 523 else 524 onFlag = 0; 525 r |= set_clr_led(diskNo, LED_PROPS_START + 1 + ledNo, 526 dtls, onFlag); 527 } 528 } 529 530 dtls->led_retry = (r != 0); 531 } 532 533 static void 534 start_led_test(led_dtls_t *dtls, int disk) 535 { 536 int led_no; 537 538 /* 539 * if the poll thread has failed, can't do led test 540 */ 541 if (!dtls->polling) 542 return; 543 544 /* 545 * set test interval - doubles as flag for LED-test in progress 546 */ 547 dtls->led_test_end[disk] = dtls->led_test_time; 548 for (led_no = 1; led_no <= FCAL_LED_CNT; led_no++) { 549 set_led(disk, LED_PROPS_START + led_no, dtls); 550 } 551 } 552 553 static void 554 end_led_test(led_dtls_t *dtls, int disk) 555 { 556 /* 557 * There is a problem with a disk coming on-line. 558 * All its leds are lit for 10 seconds to meet the led-test 559 * requirement. The true state for the fault led can be determined 560 * immediately, but determination of whether to light blue or green 561 * requires a response from libdevinfo. Device reconfiguration logic 562 * (likely to be active at this time) holds a long term 563 * lock preventing devinfo calls from completing. Rather than 564 * leave a contradictory led indication showing during this 565 * period, it is better to anticipate the green led result 566 * and correct it when the facts are known. 567 */ 568 clr_led(disk, FCAL_REMOK_LED, dtls); 569 clr_led(disk, FCAL_FAULT_LED, dtls); 570 dtls->led_state[FCAL_READY_LED - LED_PROPS_START - 1][disk] = 571 LED_STATE_ON; 572 } 573 574 /* 575 * Evaluate required wait time and wait until that time or an event. 576 * Returns 0 for a time-out, otherwise the pending event(s). 577 * If the finish_now flag is becomes set, this routine acknowledges 578 * the request and waits for it to go away, 579 * i.e. no return while finish_now is set. 580 */ 581 static int 582 wait_a_while(void) 583 { 584 int r; 585 int events; 586 boolean_t acksent = B_FALSE; 587 588 do { 589 r = pthread_mutex_lock(&g_mutex); 590 if (r != 0) { 591 SYSLOG(LOG_ERR, EM_MUTEX_FAIL, mystrerror(r)); 592 return (0); 593 } 594 if (g_finish_now && !acksent) { 595 g_leds_thread_ack = B_TRUE; 596 (void) pthread_cond_signal(&g_cv_ack); 597 acksent = B_TRUE; 598 } 599 r = pthread_cond_wait(&g_cv, &g_mutex); 600 if (r != 0) { 601 SYSLOG(LOG_ERR, EM_CONDWAITFAIL, mystrerror(r)); 602 (void) pthread_mutex_unlock(&g_mutex); 603 return (0); 604 } 605 /* 606 * whilst under the mutex, take a local copy of the events 607 * and clear those that we handle 608 */ 609 events = g_event_flag & (FCAL_EV_POLL | FCAL_EV_CONFIG); 610 g_event_flag ^= events; 611 (void) pthread_mutex_unlock(&g_mutex); 612 } while (g_finish_now); 613 614 return (events); 615 } 616 617 /*ARGSUSED*/ 618 void * 619 fcal_leds_thread(void *args) 620 { 621 led_dtls_t *dtls = g_led_dtls; 622 int c, v; 623 int err = 0; 624 int events = 0; 625 int fd_bkplane; 626 i2c_port_t port; 627 int lastVal = I2C_IOCTL_INIT; 628 int ws; 629 int mask; 630 631 /* 632 * generate a mask for presence and fault status bits 633 */ 634 mask = 0; 635 for (c = 0; c < dtls->n_disks; c++) { 636 mask |= dtls->presence[c]; 637 mask |= dtls->faults[c]; 638 } 639 640 /* 641 * enter poll loop 642 */ 643 for (;;) { 644 /* 645 * see if a LED-test timer has expired 646 */ 647 for (c = 0; c < dtls->n_disks; c++) { 648 if (dtls->led_test_end[c] > 0) { 649 if (!dtls->polling) { 650 /* poll thread failure, end led-test */ 651 dtls->led_test_end[c] = 0; 652 } else if ((events & FCAL_EV_POLL) != 0) { 653 dtls->led_test_end[c]--; 654 } 655 if (dtls->led_test_end[c] == 0) { 656 /* 657 * clear blue and amber leds 658 */ 659 end_led_test(dtls, c); 660 /* treat any status as a change */ 661 lastVal = I2C_IOCTL_INIT; 662 } 663 } 664 } 665 fd_bkplane = open(dtls->fcal_status, O_RDONLY); 666 if (fd_bkplane < 0) { 667 SYSLOG(LOG_ERR, EM_CANT_OPEN, dtls->fcal_status); 668 err = errno; 669 break; 670 } 671 port.value = 0; 672 /* 673 * the direction and dir_mask fields are ignored, 674 * so one can only guess at their possible use 675 */ 676 port.direction = DIR_INPUT; 677 port.dir_mask = (uint8_t)mask; 678 c = ioctl(fd_bkplane, I2C_GET_PORT, &port); 679 if (c < 0) { 680 err = errno; 681 (void) close(fd_bkplane); 682 683 if (lastVal != I2C_IOCTL_FAIL) { 684 SYSLOG(LOG_ERR, EM_I2C_GET_PORT, 685 mystrerror(err)); 686 lastVal = I2C_IOCTL_FAIL; 687 events |= FCAL_EV_CONFIG; 688 } 689 } else { 690 (void) close(fd_bkplane); 691 ws = port.value & mask; 692 } 693 694 if ((c == 0) && (ws != lastVal)) { 695 events |= FCAL_EV_CONFIG; 696 lastVal = ws; 697 for (c = 0; c < dtls->n_disks; c++) { 698 /* 699 * first get the value of the relevant 700 * presence bit (as 0 or 1) 701 */ 702 v = ((lastVal & dtls->presence[c]) != 0); 703 704 /* hold previous presence value */ 705 ws = dtls->disk_detected[c]; 706 707 /* 708 * the disk is present if the backplane 709 * status bit for this disk is equal to the 710 * configured assert_presence value 711 */ 712 dtls->disk_detected[c] = 713 (v == dtls->assert_presence); 714 /* 715 * Don't add disk-unit node here for 716 * newly arrived disks. While the led 717 * test is running (and beyond) 718 * libdevinfo is locked out and we 719 * can't get port or target info. 720 */ 721 if ((!ws) && dtls->disk_detected[c]) { 722 /* 723 * disk has just come on-line 724 */ 725 start_led_test(dtls, c); 726 } 727 /* 728 * clear leds and ready status 729 * for disks which have been removed 730 */ 731 if (ws && (!dtls->disk_detected[c])) { 732 clr_led(c, FCAL_REMOK_LED, dtls); 733 clr_led(c, FCAL_FAULT_LED, dtls); 734 clr_led(c, FCAL_READY_LED, dtls); 735 dtls->disk_ready[c] = NO_MINORS; 736 dtls->disk_prev[c] = NO_MINORS; 737 v = update_picl(dtls, c); 738 /* 739 * set or clear retry flag 740 */ 741 dtls->picl_retry[c] = (v == EAGAIN); 742 } 743 /* 744 * for present disks which are not doing a 745 * led test, adjust fault LED 746 */ 747 if ((dtls->led_test_end[c] != 0) || 748 (!dtls->disk_detected[c])) 749 continue; 750 v = ((lastVal & dtls->faults[c]) != 0); 751 if (v == dtls->assert_fault) 752 set_led(c, FCAL_FAULT_LED, dtls); 753 else 754 clr_led(c, FCAL_FAULT_LED, dtls); 755 } 756 } 757 758 /* 759 * For detected disks whose status has changed, choose between 760 * ready and ok to remove. 761 * libdevinfo can be locked out for the entire duration of a 762 * disk spin-up. So it is best not to seek this info while 763 * a led-test is in progress. Otherwise the leds can be stuck 764 * on for about 40 seconds. 765 * Note that chk_minors() returns 0 unless a status change 766 * has occurred. 767 */ 768 if (!is_led_test(dtls) && chk_minors(dtls) != 0) { 769 events = FCAL_EV_CONFIG; 770 for (c = 0; c < dtls->n_disks; c++) { 771 if (!dtls->disk_detected[c]) 772 continue; 773 /* 774 * When disk_ready changes, disk_prev is set 775 * to its previous value. This allows the 776 * direction of the last transistion to be 777 * determined. 778 */ 779 if ((dtls->disk_prev[c] == HAS_MINORS) && 780 (dtls->disk_ready[c] == NO_MINORS)) { 781 clr_led(c, FCAL_READY_LED, dtls); 782 set_led(c, FCAL_REMOK_LED, dtls); 783 } else { 784 set_led(c, FCAL_READY_LED, dtls); 785 clr_led(c, FCAL_REMOK_LED, dtls); 786 } 787 } 788 } 789 /* 790 * Update PICL (disk-unit) for newly attached disks 791 * ** see note in header file for significance 792 * of disk_prev and disk_ready flags. 793 */ 794 for (c = 0; c < dtls->n_disks; c++) { 795 if ((dtls->disk_prev[c] == NO_MINORS) && 796 (dtls->disk_ready[c] == HAS_MINORS)) { 797 dtls->disk_prev[c] = HAS_MINORS; 798 v = update_picl(dtls, c); 799 /* 800 * set or clear retry flag 801 */ 802 dtls->picl_retry[c] = (v == EAGAIN); 803 } 804 } 805 if ((events & FCAL_EV_CONFIG) != 0) { 806 /* 807 * set fast polling 808 */ 809 dtls->fast_poll_end = dtls->relax_time_ticks; 810 } 811 /* 812 * if updating a led failed (e.g. I2C busy), try again 813 */ 814 if (dtls->led_retry) 815 retry_led(dtls); 816 817 events = wait_a_while(); 818 819 /* 820 * when picl is recycled, wait_a_while sleeps until the 821 * init routine has been called again. 822 * This is the moment when dtls may have become stale. 823 */ 824 if (dtls != g_led_dtls) { 825 dtls = g_led_dtls; 826 lastVal = I2C_IOCTL_INIT; 827 828 /* 829 * re-generate the presence and fault status mask 830 * in case the .conf file has changed 831 */ 832 mask = 0; 833 for (c = 0; c < dtls->n_disks; c++) { 834 mask |= dtls->presence[c]; 835 mask |= dtls->faults[c]; 836 } 837 } 838 839 /* 840 * count down relaxation time counter if a poll event 841 */ 842 if ((events & FCAL_EV_POLL) != 0) { 843 if (dtls->fast_poll_end > 0) 844 dtls->fast_poll_end--; 845 } 846 847 /* 848 * if updating PICL needs retrying, try it now 849 */ 850 for (c = 0; c < dtls->n_disks; c++) { 851 if (dtls->picl_retry[c]) { 852 v = update_picl(dtls, c); 853 dtls->picl_retry[c] = (v == EAGAIN); 854 } 855 } 856 } 857 858 return ((void *)err); 859 } 860