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