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