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