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 *
mystrerror(int err)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
delete_disk_unit(led_dtls_t * dtls,int disk)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
update_picl(led_dtls_t * dtls,int disk)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
get_drv_info(di_node_t node,led_dtls_t * dtls)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
walk_disks(di_node_t node,led_dtls_t * dtls)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
chk_minors(led_dtls_t * dtls)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
is_led_test(led_dtls_t * dtls)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
set_clr_led(int diskNo,token_t led_tok,led_dtls_t * dtls,int set)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
set_led(int diskNo,token_t led_tok,led_dtls_t * dtls)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
clr_led(int diskNo,token_t led_tok,led_dtls_t * dtls)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
retry_led(led_dtls_t * dtls)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
start_led_test(led_dtls_t * dtls,int disk)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
end_led_test(led_dtls_t * dtls,int disk)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
wait_a_while(void)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 *
fcal_leds_thread(void * args)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