xref: /illumos-gate/usr/src/cmd/picl/plugins/sun4u/mpxu/frudr/piclfrudr.c (revision 98677c366f39bc9e671513615d9b1a2c6f15621d)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <stdio.h>
30 #include <stddef.h>
31 #include <syslog.h>
32 #include <strings.h>
33 #include <unistd.h>
34 #include <libintl.h>
35 #include <stdlib.h>
36 #include <ctype.h>
37 #include <picl.h>
38 #include <picltree.h>
39 #include <picld_pluginutil.h>
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <fcntl.h>
43 #include <dirent.h>
44 #include <sys/sysevent/dr.h>
45 #include <pthread.h>
46 #include <libdevinfo.h>
47 #include <limits.h>
48 #include <sys/systeminfo.h>
49 #include <sys/envmon.h>
50 #include <i2c_gpio.h>
51 #include "libdevice.h"
52 #include "picldefs.h"
53 #include <sys/raidioctl.h>
54 #include <sys/param.h>
55 #include <sys/epic.h>
56 
57 /*
58  * Plugin registration entry points
59  */
60 static void	piclfrudr_register(void);
61 static void	piclfrudr_init(void);
62 static void	piclfrudr_fini(void);
63 static void	rmc_state_event(void);
64 static void	seattle_setleds(void);
65 static void	boston_set_frontleds(const char *, int);
66 static void	boston_set_rearleds(const char *, int);
67 
68 #pragma	init(piclfrudr_register)
69 
70 static picld_plugin_reg_t  my_reg_info = {
71 	PICLD_PLUGIN_VERSION_1,
72 	PICLD_PLUGIN_CRITICAL,
73 	"SUNW_MPXU_frudr",
74 	piclfrudr_init,
75 	piclfrudr_fini,
76 };
77 
78 /*
79  * Log message texts
80  */
81 #define	EM_THREAD_CREATE_FAILED gettext("piclfrudr: pthread_create failed: %s")
82 #define	DELETE_PROP_FAIL gettext("ptree_delete_prop failed: %d")
83 #define	EM_DI_INIT_FAIL	gettext("piclfrudr: di_init failed: %s")
84 #define	PROPINFO_FAIL gettext("ptree_init_propinfo %s failed: %d")
85 #define	ADD_NODE_FAIL gettext("ptree_create_and_add_node %s failed: %d")
86 #define	ADD_TBL_ENTRY_FAIL gettext("piclfrudr: cannot add entry to table")
87 #define	EM_POLL_FAIL gettext("piclfrudr: poll() failed: %s")
88 #define	ADD_PROP_FAIL gettext("ptree_create_and_add_prop %s failed: %d")
89 #define	EM_MUTEX_FAIL gettext("piclfrudr: pthread_mutex_lock returned: %s")
90 #define	EM_UNK_FRU gettext("piclfrudr: Fru removed event for unknown node")
91 #define	PARSE_CONF_FAIL gettext("parse config file %s failed")
92 #define	EM_NO_SC_DEV gettext("piclfrudr: failed to locate SC device node")
93 #define	EM_NO_SYSINFO gettext("piclfrudr: failed to get SC sysinfo: %s")
94 
95 /*
96  * PICL property values
97  */
98 #define	PICL_PROPVAL_ON		"ON"
99 #define	PICL_PROPVAL_OFF	"OFF"
100 
101 /*
102  * Local defines
103  */
104 #define	SEEPROM_DRIVER_NAME	"seeprom"
105 #define	FRUTREE_PATH		"/frutree"
106 #define	CHASSIS_LOC_PATH	"/frutree/chassis/%s"
107 #define	SYS_BOARD_PATH		"/frutree/chassis/MB/system-board/%s"
108 #define	SEATTLE1U_HDDBP_PATH	\
109 	"/frutree/chassis/MB/system-board/HDDBP/disk-backplane-1/%s"
110 #define	SEATTLE2U_HDDBP_PATH	\
111 	"/frutree/chassis/MB/system-board/HDDBP/disk-backplane-3/%s"
112 #define	BOSTON_HDDBP_PATH	\
113 	"/frutree/chassis/MB/system-board/HDDCNTRL/disk-controller/HDDBP" \
114 	"/disk-backplane-8/%s"
115 
116 #define	CONFFILE_PREFIX		"fru_"
117 #define	CONFFILE_SUFFIX		".conf"
118 #define	CONFFILE_FRUTREE	"piclfrutree.conf"
119 #define	PS_NAME			"PS"
120 #define	PS_NAME_LEN		2
121 #define	PS_FRU_NAME		"power-supply"
122 #define	PS_PLATFORM_NAME	"power-supply-fru-prom"
123 #define	DISK_NAME		"HDD"
124 #define	DISK_NAME_LEN		3
125 #define	DISK_FRU_NAME		"disk"
126 #define	SCC_NAME		"SCC"
127 #define	SCC_NAME_LEN		3
128 #define	SCC_FRU_NAME		"scc"
129 #define	RMC_NAME		"SC"
130 #define	RMC_NAME_LEN		2
131 #define	RMC_FRU_NAME		"sc"
132 #define	DEV_PREFIX		"/devices"
133 #define	ENXS_FRONT_SRVC_LED	0x20
134 #define	ENXS_FRONT_ACT_LED	0x10
135 #define	ENXS_REAR_SRVC_LED	0x20
136 #define	ENXS_REAR_ACT_LED	0x10
137 #define	ENTS_SRVC_LED		0x20
138 #define	ENTS_ACT_LED		0x10
139 #define	V440_SRVC_LED		0x2
140 #define	V440_ACT_LED		0x1
141 #define	BOSTON_FRONT_SRVC_LED	0x2
142 #define	BOSTON_FRONT_ACT_LED	0x4
143 #define	BOSTON_FRONT_CLEAR_DIR	0x0
144 #define	BOSTON_FRONT_CLEAR_POL	0x0
145 #define	BOSTON_FRONT_LED_MASK	0xffffffff
146 #define	BOSTON_REAR_SRVC_LED	0x8000
147 #define	BOSTON_REAR_ACT_LED	0x2000
148 #define	BOSTON_REAR_CLEAR_POL	0x0000
149 #define	BOSTON_REAR_LED_MASK	0xe000
150 
151 /*
152  * PSU defines
153  */
154 #define	PSU_I2C_BUS_DEV "/devices/pci@1e,600000/isa@7/i2c@0,320:devctl"
155 #define	PSU_DEV	\
156 	"/devices/pci@1e,600000/isa@7/i2c@0,320/power-supply-fru-prom@0,%x"
157 #define	PSU_PLATFORM	"/platform/pci@1e,600000/isa@7/i2c@0,320"
158 #define	PS0_ADDR ((sys_platform == PLAT_CHALUPA19) ? 0xc0 : 0xb0)
159 #define	PS1_ADDR ((sys_platform == PLAT_CHALUPA19) ? 0xc2 : 0xa4)
160 #define	PS2_ADDR 0x70
161 #define	PS3_ADDR 0x72
162 #define	PS0_UNITADDR	((sys_platform == PLAT_CHALUPA19) ? "0,c0" : "0,b0")
163 #define	PS1_UNITADDR	((sys_platform == PLAT_CHALUPA19) ? "0,c2" : "0,a4")
164 #define	PS2_UNITADDR	"0,70"
165 #define	PS3_UNITADDR	"0,72"
166 #define	PS0_NAME "PS0"
167 #define	PS1_NAME "PS1"
168 #define	PS2_NAME "PS2"
169 #define	PS3_NAME "PS3"
170 #define	PSU0_NAME "PSU0"
171 #define	PSU1_NAME "PSU1"
172 #define	PSU2_NAME "PSU2"
173 #define	PSU3_NAME "PSU3"
174 #define	PS_DEVICE_NAME "power-supply-fru-prom"
175 #define	PSU_COMPATIBLE	"i2c-at24c64"
176 
177 /*
178  * Seattle/Boston PSU defines
179  */
180 #define	SEATTLE_PSU_I2C_BUS_DEV "/devices/i2c@1f,530000:devctl"
181 #define	SEATTLE_PSU_DEV	\
182 	"/devices/i2c@1f,530000/power-supply-fru-prom@0,%x"
183 #define	SEATTLE_PSU_PLATFORM	"/platform/i2c@1f,530000"
184 #define	SEATTLE_PS0_ADDR	0x6c
185 #define	SEATTLE_PS1_ADDR	0x6e
186 #define	SEATTLE_PS0_UNITADDR	"0,6c"
187 #define	SEATTLE_PS1_UNITADDR	"0,6e"
188 #define	SEATTLE_PSU_COMPATIBLE	"i2c-at34c02"
189 #define	BOSTON_PSU_I2C_BUS_DEV	"/devices/i2c@1f,520000:devctl"
190 #define	BOSTON_PSU_DEV	\
191 	"/devices/i2c@1f,520000/power-supply-fru-prom@0,%x"
192 #define	BOSTON_PSU_PLATFORM	"/platform/i2c@1f,520000"
193 #define	BOSTON_PS0_ADDR		0x24
194 #define	BOSTON_PS1_ADDR		0x32
195 #define	BOSTON_PS2_ADDR		0x52
196 #define	BOSTON_PS3_ADDR		0x72
197 #define	BOSTON_PS0_UNITADDR	"0,24"
198 #define	BOSTON_PS1_UNITADDR	"0,32"
199 #define	BOSTON_PS2_UNITADDR	"0,52"
200 #define	BOSTON_PS3_UNITADDR	"0,72"
201 #define	BOSTON_PSU_COMPATIBLE	"i2c-at34c02"
202 
203 /*
204  * disk defines
205  */
206 #define	REMOK_LED "OK2RM"
207 #define	FAULT_LED "SERVICE"
208 #define	PLATFORMLEN 9
209 #define	N_DISKS 8
210 #define	N_CHALUPA_DISKS 4
211 #define	N_ENTS_DISKS 8
212 #define	N_MPXU_DISKS 4
213 #define	N_EN19_DISKS 2
214 #define	DISK_POLL_TIME	5000
215 /* For V440 RAID policy */
216 #define	V440_DISK_DEVCTL "/devices/pci@1f,700000/scsi@2:devctl"
217 
218 /*
219  * Seattle/Boston disk defines
220  */
221 #define	N_SEATTLE1U_DISKS	2
222 #define	N_SEATTLE2U_DISKS	4
223 #define	N_BOSTON_DISKS		8
224 #define	SEATTLE_DISK_DEVCTL \
225 	"/devices/pci@1e,600000/pci@0/pci@a/pci@0/pci@8/scsi@1:devctl"
226 #define	BOSTON_DISK_DEVCTL_1068X \
227 	"/devices/pci@1f,700000/pci@0/pci@2/pci@0/pci@8/LSILogic,sas@1:devctl"
228 #define	BOSTON_DISK_DEVCTL_1068E \
229 	"/devices/pci@1e,600000/pci@0/pci@2/scsi@0:devctl"
230 
231 /*
232  * led defines
233  */
234 #define	ENXS_LED_DIR	"/devices/pci@1e,600000/isa@7/i2c@0,320/"
235 #define	ENXS_FRONT_LEDS	"gpio@0,70:"
236 #define	ENXS_REAR_LEDS	ENXS_LED_DIR "gpio@0,44:port_1"
237 
238 #define	ENTS_LED_DIR	"/devices/pci@1e,600000/isa@7/i2c@0,320/"
239 #define	ENTS_LEDS	"gpio@0,70:"
240 
241 #define	V440_LED_DIR	"/devices/pci@1e,600000/isa@7/i2c@0,320/"
242 #define	V440_LED_PATH	V440_LED_DIR "gpio@0,48:port_0"
243 
244 /*
245  * Seattle/Boston led defines
246  */
247 #define	SEATTLE_LED_DEV	"/devices/ebus@1f,464000/env-monitor@3,0:env-monitor0"
248 #define	BOSTON_LED_DIR	"/devices/i2c@1f,520000/"
249 #define	BOSTON_FRONT_LED_PATH	BOSTON_LED_DIR "gpio@0,3a:port_0"
250 #define	BOSTON_REAR_LED_PATH	BOSTON_LED_DIR "hardware-monitor@0,5c:adm1026"
251 
252 /*
253  * Seattle/Boston USB defines
254  */
255 #define	MAX_USB_PORTS		4
256 #define	USB_CONF_FILE_NAME	"usb-a-"
257 
258 typedef struct id_props {
259 	envmon_handle_t	envhandle;
260 	picl_prophdl_t	volprop;
261 } id_props_t;
262 
263 typedef struct idp_lkup {
264 	int		maxnum;		/* entries in array */
265 	int		num;		/* entries in use */
266 	id_props_t	idp[1];
267 } idp_lkup_t;
268 
269 /*
270  * table for mapping RMC handles to volatile property handles
271  */
272 static idp_lkup_t	*idprop = NULL;
273 
274 /*
275  * path names to system-controller device and fault led gpio
276  */
277 static char		*sc_device_name = NULL;
278 static char		*bezel_leds = NULL;
279 
280 /*
281  * disk data
282  */
283 static int disk_ready[N_DISKS];
284 static char *disk_name[N_DISKS] = { "HDD0", "HDD1", "HDD2", "HDD3",
285 					"HDD4", "HDD5", "HDD6", "HDD7" };
286 static volatile boolean_t	disk_leds_thread_ack = B_FALSE;
287 static volatile	boolean_t	disk_leds_thread_running = B_FALSE;
288 static pthread_t		ledsthr_tid;
289 static pthread_attr_t		ledsthr_attr;
290 static boolean_t		ledsthr_created = B_FALSE;
291 static boolean_t		g_mutex_init = B_FALSE;
292 static pthread_cond_t		g_cv;
293 static pthread_cond_t		g_cv_ack;
294 static pthread_mutex_t		g_mutex;
295 static volatile boolean_t	g_finish_now = B_FALSE;
296 
297 /*
298  * Boston platform-specific flag which tells us if we are using
299  * a LSI 1068X disk controller (0) or a LSI 1068E (1).
300  */
301 static int boston_1068e_flag = 0;
302 
303 /*
304  * static strings
305  */
306 static const char		str_devfs_path[] = "devfs-path";
307 
308 /*
309  * OperationalStatus property values
310  */
311 static const char		str_opst_present[] = "present";
312 static const char		str_opst_ok[] = "okay";
313 static const char		str_opst_faulty[] = "faulty";
314 static const char		str_opst_download[] = "download";
315 static const char		str_opst_unknown[] = "unknown";
316 static size_t			max_opst_len = sizeof (str_opst_download);
317 
318 /*
319  * forward reference
320  */
321 static void opst_init(void);
322 static void add_op_status_by_name(const char *name, const char *child_name,
323     picl_prophdl_t *prophdl_p);
324 static void add_op_status_to_node(picl_nodehdl_t nodeh,
325     picl_prophdl_t *prophdl_p);
326 static int read_vol_data(ptree_rarg_t *r_arg, void *buf);
327 static int find_picl_handle(picl_prophdl_t proph);
328 static void disk_leds_init(void);
329 static void disk_leds_fini(void);
330 static void *disk_leds_thread(void *args);
331 static picl_nodehdl_t find_child_by_name(picl_nodehdl_t parh, char *name);
332 static void post_frudr_event(char *ename, picl_nodehdl_t parenth,
333     picl_nodehdl_t fruh);
334 static int add_prop_ref(picl_nodehdl_t nodeh, picl_nodehdl_t value, char *name);
335 static void remove_fru_parents(picl_nodehdl_t fruh);
336 static int get_node_by_class(picl_nodehdl_t nodeh, const char *classname,
337     picl_nodehdl_t *foundnodeh);
338 static int get_sys_controller_node(picl_nodehdl_t *nodeh);
339 static char *create_sys_controller_pathname(picl_nodehdl_t sysconh);
340 static char *create_bezel_leds_pathname(const char *dirpath,
341     const char *devname);
342 static void frudr_evhandler(const char *ename, const void *earg,
343     size_t size, void *cookie);
344 static void fru_add_handler(const char *ename, const void *earg,
345     size_t size, void *cookie);
346 static void frutree_evhandler(const char *ename, const void *earg,
347 	size_t size, void *cookie);
348 static int create_table(picl_nodehdl_t fruhdl, picl_prophdl_t *tblhdlp,
349     char *tbl_name);
350 static int create_table_entry(picl_prophdl_t tblhdl,
351     picl_nodehdl_t refhdl, char *class);
352 static int create_i2c_node(char *ap_id);
353 static void delete_i2c_node(char *ap_id);
354 static int set_led(char *name, char *ptr, char *value);
355 static int ps_name_to_addr(char *name);
356 static char *ps_name_to_unitaddr(char *name);
357 static char *ps_apid_to_nodename(char *apid);
358 static void add_op_status(envmon_hpu_t *hpu, int *index);
359 
360 #define	sprintf_buf2(buf, a1, a2) (void) snprintf(buf, sizeof (buf), a1, a2)
361 
362 /*
363  * Because this plugin is shared across different platforms, we need to
364  * distinguish for certain functionality
365  */
366 #define	PLAT_UNKNOWN	(-1)
367 #define	PLAT_ENXS	0
368 #define	PLAT_ENTS	1
369 #define	PLAT_CHALUPA	2
370 #define	PLAT_EN19	3
371 #define	PLAT_CHALUPA19	4
372 #define	PLAT_SALSA19	5
373 #define	PLAT_SEATTLE1U	6
374 #define	PLAT_SEATTLE2U	7
375 #define	PLAT_BOSTON	8
376 
377 static int sys_platform;
378 
379 static void
380 get_platform()
381 {
382 	char	platform[64];
383 	(void) sysinfo(SI_PLATFORM, platform, sizeof (platform));
384 	if (strcmp(platform, "SUNW,Sun-Fire-V250") == 0)
385 		sys_platform = PLAT_ENTS;
386 	else if (strcmp(platform, "SUNW,Sun-Fire-V440") == 0)
387 		sys_platform = PLAT_CHALUPA;
388 	else if (strcmp(platform, "SUNW,Sun-Fire-V210") == 0)
389 		sys_platform = PLAT_ENXS;
390 	else if (strcmp(platform, "SUNW,Sun-Fire-V240") == 0)
391 		sys_platform = PLAT_ENXS;
392 	else if (strcmp(platform, "SUNW,Netra-240") == 0)
393 		sys_platform = PLAT_EN19;
394 	else if (strcmp(platform, "SUNW,Netra-210") == 0)
395 		sys_platform = PLAT_SALSA19;
396 	else if (strcmp(platform, "SUNW,Netra-440") == 0)
397 		sys_platform = PLAT_CHALUPA19;
398 	else if (strcmp(platform, "SUNW,Sun-Fire-V215") == 0)
399 		sys_platform = PLAT_SEATTLE1U;
400 	else if (strcmp(platform, "SUNW,Sun-Fire-V245") == 0)
401 		sys_platform = PLAT_SEATTLE2U;
402 	else if (strcmp(platform, "SUNW,Sun-Fire-V445") == 0)
403 		sys_platform = PLAT_BOSTON;
404 	else
405 		sys_platform = PLAT_UNKNOWN;
406 }
407 
408 /*
409  * This function is executed as part of .init when the plugin is
410  * dlopen()ed
411  */
412 static void
413 piclfrudr_register(void)
414 {
415 	(void) picld_plugin_register(&my_reg_info);
416 }
417 
418 /*
419  * This function is the init entry point of the plugin.
420  * It initializes the /frutree tree
421  */
422 static void
423 piclfrudr_init(void)
424 {
425 	picl_nodehdl_t	sc_nodeh;
426 	picl_nodehdl_t	locationh;
427 	picl_nodehdl_t	childh;
428 	char namebuf[PATH_MAX];
429 
430 	get_platform();
431 
432 	if (sc_device_name != NULL) {
433 		free(sc_device_name);	/* must have reen restarted */
434 		sc_device_name = NULL;
435 	}
436 
437 	if ((get_sys_controller_node(&sc_nodeh) != PICL_SUCCESS) ||
438 	    ((sc_device_name = create_sys_controller_pathname(sc_nodeh)) ==
439 	    NULL))
440 		syslog(LOG_ERR, EM_NO_SC_DEV);
441 
442 	opst_init();
443 	disk_leds_init();
444 
445 	(void) ptree_register_handler(PICLEVENT_DR_AP_STATE_CHANGE,
446 	    frudr_evhandler, NULL);
447 	(void) ptree_register_handler(PICL_FRU_ADDED, fru_add_handler, NULL);
448 	(void) ptree_register_handler(PICLEVENT_SYSEVENT_DEVICE_ADDED,
449 	    frutree_evhandler, NULL);
450 
451 	/*
452 	 * There is a window of opportunity for the RMC to deliver an event
453 	 * indicating a newly operable state just before we are listening for
454 	 * it. In this case, envmon will have missed setting up /platform
455 	 * and won't get a signal from frudr. So send it a PICL_FRU_ADDED just
456 	 * in case.
457 	 */
458 
459 	if ((sys_platform == PLAT_CHALUPA) ||
460 		(sys_platform == PLAT_CHALUPA19)) {
461 		sprintf_buf2(namebuf, CHASSIS_LOC_PATH, RMC_NAME);
462 	} else {
463 		sprintf_buf2(namebuf, SYS_BOARD_PATH, RMC_NAME);
464 	}
465 
466 	if (ptree_get_node_by_path(namebuf, &locationh) != PICL_SUCCESS)
467 		return;
468 	if (ptree_get_propval_by_name(locationh, PICL_PROP_CHILD,
469 	    &childh, sizeof (picl_nodehdl_t)) != PICL_SUCCESS)
470 		return;
471 	post_frudr_event(PICL_FRU_ADDED, locationh, childh);
472 }
473 
474 static void
475 add_op_status_by_name(const char *name, const char *child_name,
476     picl_prophdl_t *prophdl_p)
477 {
478 	picl_nodehdl_t		nodeh;
479 
480 	if (ptree_get_node_by_path(name, &nodeh) != PICL_SUCCESS) {
481 		return;
482 	}
483 
484 	if (ptree_get_propval_by_name(nodeh, PICL_PROP_CHILD,
485 	    &nodeh, sizeof (picl_nodehdl_t)) != PICL_SUCCESS) {
486 
487 		if (child_name == NULL)
488 			return;
489 		/*
490 		 * create fru node of supplied name
491 		 */
492 		if (ptree_create_and_add_node(nodeh, child_name,
493 		    PICL_CLASS_FRU, &nodeh) != PICL_SUCCESS)
494 			return;
495 	}
496 
497 	add_op_status_to_node(nodeh, prophdl_p);
498 }
499 
500 /*
501  * function to add a volatile property to a specified node
502  */
503 static void
504 add_op_status_to_node(picl_nodehdl_t nodeh, picl_prophdl_t *prophdl_p)
505 {
506 	int			err;
507 	ptree_propinfo_t	info;
508 	picl_prophdl_t		proph;
509 
510 	err = ptree_init_propinfo(&info, PTREE_PROPINFO_VERSION,
511 	    PICL_PTYPE_CHARSTRING, PICL_VOLATILE | PICL_READ, max_opst_len,
512 	    PICL_PROP_OPERATIONAL_STATUS, read_vol_data, NULL);
513 
514 	if (err == PICL_SUCCESS) {
515 		if (ptree_get_prop_by_name(nodeh, PICL_PROP_OPERATIONAL_STATUS,
516 		    &proph) == PICL_SUCCESS) {
517 			if (ptree_delete_prop(proph) == PICL_SUCCESS)
518 				err = ptree_destroy_prop(proph);
519 		}
520 	}
521 
522 	if ((err != PICL_SUCCESS) || ((err = ptree_create_and_add_prop(nodeh,
523 	    &info, NULL, prophdl_p)) != PICL_SUCCESS)) {
524 		syslog(LOG_ERR, ADD_PROP_FAIL, PICL_PROP_OPERATIONAL_STATUS,
525 		    err);
526 		return;
527 	}
528 }
529 
530 /*
531  * Deliver volatile property value.
532  * prtpicl gets very upset if we fail this command, so if the property
533  * cannot be retrieved, return a status of unknown.
534  */
535 static int
536 read_vol_data(ptree_rarg_t *r_arg, void *buf)
537 {
538 	picl_prophdl_t	proph;
539 	int		index;
540 	int		envmon_fd;
541 	int		err;
542 	envmon_hpu_t	data;
543 
544 	proph = r_arg->proph;
545 	index = find_picl_handle(proph);
546 
547 	if (index < 0) {
548 		/*
549 		 * We drop memory of PSU op status handles in opst_init()
550 		 * when we get an RMC faulty event. We cannot access the
551 		 * status info in this circumstance, so returning "unknown"
552 		 * is appropriate.
553 		 */
554 		(void) strlcpy(buf, str_opst_unknown, max_opst_len);
555 		return (PICL_SUCCESS);
556 	}
557 
558 	envmon_fd = open(sc_device_name, O_RDONLY);
559 
560 	if (envmon_fd < 0) {
561 		/*
562 		 * To get this far we must have succeeded with an earlier
563 		 * open, so this is an unlikely failure. It would be more
564 		 * helpful to indicate the nature of the failure, but we
565 		 * don't have the space to say much. Just return "unknown".
566 		 */
567 		(void) strlcpy(buf, str_opst_unknown, max_opst_len);
568 		return (PICL_SUCCESS);
569 	}
570 
571 	data.id = idprop->idp[index].envhandle;
572 	err = ioctl(envmon_fd, ENVMONIOCHPU, &data);
573 
574 	if (err < 0) {
575 		/*
576 		 * If we can't read the stats, "unknown" is a reasonable
577 		 * status to return. This one really shouldn't happen.
578 		 */
579 		(void) strlcpy(buf, str_opst_unknown, max_opst_len);
580 		(void) close(envmon_fd);
581 		return (PICL_SUCCESS);
582 	}
583 
584 	(void) close(envmon_fd);
585 
586 	if (strncmp(data.id.name, DISK_NAME, DISK_NAME_LEN) == 0 &&
587 	    data.fru_status == ENVMON_FRU_PRESENT) {
588 		(void) strlcpy(buf, str_opst_present, max_opst_len);
589 		return (PICL_SUCCESS);
590 	}
591 
592 	if (data.sensor_status != ENVMON_SENSOR_OK) {
593 		(void) strlcpy(buf, str_opst_unknown, max_opst_len);
594 		return (PICL_SUCCESS);
595 	}
596 
597 	(void) strlcpy(buf,
598 	    data.fru_status == ENVMON_FRU_PRESENT ? str_opst_ok :
599 	    data.fru_status == ENVMON_FRU_DOWNLOAD ? str_opst_download :
600 	    data.fru_status == ENVMON_FRU_FAULT ? str_opst_faulty :
601 	    str_opst_unknown, max_opst_len);
602 
603 	return (PICL_SUCCESS);
604 }
605 
606 /*
607  * Function for explicitly turning on system leds
608  * for a failed/degraded RMC (SC).
609  */
610 static void
611 solaris_setleds(const char *led_path, int leds)
612 {
613 	i2c_gpio_t	gpio;
614 	int		fd = open(led_path, O_RDWR);
615 
616 	if (fd < 0)
617 		return;
618 
619 	gpio.reg_val = (leds ^ 0xff);
620 	gpio.reg_mask = 0xffffffff;
621 	if (ioctl(fd, GPIO_SET_CONFIG, &gpio) == 0) {
622 		gpio.reg_val = (leds ^ 0xff);
623 		gpio.reg_mask = 0xffffffff;
624 		(void) ioctl(fd, GPIO_SET_OUTPUT, &gpio);
625 	}
626 	(void) close(fd);
627 }
628 
629 /*
630  * Function for explicitly turning on system leds
631  * for a failed/degraded RMC (SC) on Seattle
632  */
633 static void
634 seattle_setleds(void)
635 {
636 	int fd;
637 
638 	fd = open(SEATTLE_LED_DEV, O_RDWR);
639 
640 	if (fd < 0)
641 		return;
642 
643 	ioctl(fd, EPIC_SET_POWER_LED, (char *)0);
644 	ioctl(fd, EPIC_SET_ALERT_LED, (char *)0);
645 	(void) close(fd);
646 }
647 
648 /*
649  * Function for explicitly turning on the front system leds
650  * for a failed/degraded RMC (SC) on Boston
651  */
652 static void
653 boston_set_frontleds(const char *led_path, int leds)
654 {
655 	i2c_gpio_t	gpio;
656 	int		fd = open(led_path, O_RDWR);
657 
658 	if (fd < 0) {
659 		return;
660 	}
661 
662 	/* first clear the polarity */
663 	gpio.reg_val  = BOSTON_FRONT_CLEAR_POL;
664 	gpio.reg_mask = BOSTON_FRONT_LED_MASK;
665 	if (ioctl(fd, GPIO_SET_POLARITY, &gpio) < 0)	{
666 		(void) close(fd);
667 		return;
668 	}
669 
670 	/* now clear the direction */
671 	gpio.reg_val  = BOSTON_FRONT_CLEAR_DIR;
672 	gpio.reg_mask = BOSTON_FRONT_LED_MASK;
673 	if (ioctl(fd, GPIO_SET_CONFIG, &gpio) < 0)	{
674 		(void) close(fd);
675 		return;
676 	}
677 
678 	/* and light the leds */
679 	gpio.reg_val = leds;
680 	gpio.reg_mask = BOSTON_FRONT_LED_MASK;
681 	ioctl(fd, GPIO_SET_OUTPUT, &gpio);
682 	(void) close(fd);
683 }
684 
685 /*
686  * Function for explicitly turning on the rear system leds
687  * for a failed/degraded RMC (SC) on Boston
688  */
689 static void
690 boston_set_rearleds(const char *led_path, int leds)
691 {
692 	i2c_gpio_t	gpio;
693 	int		fd = open(led_path, O_RDWR);
694 
695 	if (fd < 0) {
696 		return;
697 	}
698 
699 	/* first clear the polarity */
700 	gpio.reg_val  = BOSTON_REAR_CLEAR_POL;
701 	gpio.reg_mask = BOSTON_REAR_LED_MASK;
702 	if (ioctl(fd, GPIO_SET_POLARITY, &gpio) < 0)	{
703 		(void) close(fd);
704 		return;
705 	}
706 
707 	/* now set the direction */
708 	gpio.reg_val  = BOSTON_REAR_LED_MASK;
709 	gpio.reg_mask = BOSTON_REAR_LED_MASK;
710 	if (ioctl(fd, GPIO_SET_CONFIG, &gpio) < 0)	{
711 		(void) close(fd);
712 		return;
713 	}
714 
715 	/* and light the leds */
716 	gpio.reg_val = leds;
717 	gpio.reg_mask = BOSTON_REAR_LED_MASK;
718 	ioctl(fd, GPIO_SET_OUTPUT, &gpio);
719 	(void) close(fd);
720 }
721 
722 static void
723 rmc_state_event(void)
724 {
725 	envmon_hpu_t	hpu;
726 	int		res;
727 	int		fd = open(sc_device_name, O_RDONLY);
728 
729 	if (fd < 0)
730 		return;
731 
732 	(void) strlcpy(hpu.id.name, RMC_NAME, sizeof (hpu.id.name));
733 	res = ioctl(fd, ENVMONIOCHPU, &hpu);
734 	(void) close(fd);
735 
736 	if ((res == 0) && (hpu.sensor_status == ENVMON_SENSOR_OK) &&
737 	    ((hpu.fru_status & ENVMON_FRU_FAULT) != 0)) {
738 		/*
739 		 * SC failed event - light the service led
740 		 * note that as Solaris is still running,
741 		 * the Solaris active led should be lit too.
742 		 */
743 		switch (sys_platform) {
744 		case PLAT_ENXS:
745 		case PLAT_SALSA19:
746 		case PLAT_EN19:
747 			solaris_setleds(ENXS_REAR_LEDS,
748 			    ENXS_REAR_SRVC_LED | ENXS_REAR_ACT_LED);
749 			/*
750 			 * the device name for the bezel leds GPIO device
751 			 * tends to vary from Unix to Unix. Search for it.
752 			 */
753 			if (bezel_leds  == NULL) {
754 				bezel_leds =
755 				    create_bezel_leds_pathname(ENXS_LED_DIR,
756 				    ENXS_FRONT_LEDS);
757 			}
758 			if (bezel_leds == NULL)
759 				return;
760 			solaris_setleds(bezel_leds,
761 			    ENXS_FRONT_SRVC_LED | ENXS_FRONT_ACT_LED);
762 			break;
763 		case PLAT_ENTS:
764 			/*
765 			 * the device name for the system leds gpio can vary
766 			 * as there are several similar gpio devices. Search
767 			 * for one with the desired address.
768 			 */
769 			if (bezel_leds  == NULL) {
770 				bezel_leds =
771 				    create_bezel_leds_pathname(ENTS_LED_DIR,
772 				    ENTS_LEDS);
773 			}
774 			if (bezel_leds == NULL)
775 				return;
776 			solaris_setleds(bezel_leds,
777 			    ENTS_SRVC_LED | ENTS_ACT_LED);
778 			break;
779 		case PLAT_CHALUPA:
780 		case PLAT_CHALUPA19:
781 			solaris_setleds(V440_LED_PATH,
782 			    V440_SRVC_LED | V440_ACT_LED);
783 			break;
784 		case PLAT_BOSTON:
785 			/* set front leds */
786 			boston_set_frontleds(BOSTON_FRONT_LED_PATH,
787 			    BOSTON_FRONT_SRVC_LED | BOSTON_FRONT_ACT_LED);
788 			/* and then the rear leds */
789 			boston_set_rearleds(BOSTON_REAR_LED_PATH,
790 			    BOSTON_REAR_SRVC_LED | BOSTON_REAR_ACT_LED);
791 			break;
792 		case PLAT_SEATTLE1U:
793 		case PLAT_SEATTLE2U:
794 			seattle_setleds();
795 			break;
796 		default:
797 			break;
798 		}
799 	}
800 }
801 
802 static int
803 find_picl_handle(picl_prophdl_t proph)
804 {
805 	int index;
806 
807 	if (idprop == NULL)
808 		return (-1);
809 
810 	for (index = 0; index < idprop->num; index++) {
811 		if (idprop->idp[index].volprop == proph)
812 			return (index);
813 	}
814 
815 	return (-1);
816 }
817 
818 static int
819 find_vol_prop_by_name(const char *name)
820 {
821 	int index;
822 
823 	if (idprop == NULL)
824 		return (-1);
825 
826 	for (index = 0; index < idprop->num; index++) {
827 		if (strcmp(idprop->idp[index].envhandle.name, name) == 0)
828 			return (index);
829 	}
830 
831 	return (-1);
832 }
833 
834 /*
835  * This function is the fini entry point of the plugin.
836  */
837 static void
838 piclfrudr_fini(void)
839 {
840 	(void) ptree_unregister_handler(PICLEVENT_DR_AP_STATE_CHANGE,
841 	    frudr_evhandler, NULL);
842 	(void) ptree_unregister_handler(PICL_FRU_ADDED, fru_add_handler,
843 	    NULL);
844 	disk_leds_fini();
845 	if (idprop != NULL) {
846 		free(idprop);
847 		idprop = NULL;
848 	}
849 	if (sc_device_name != NULL) {
850 		free(sc_device_name);
851 		sc_device_name = NULL;
852 	}
853 }
854 
855 /*
856  * subroutine for various functions. Finds immediate child of parh with
857  * requested name if present. Otherwise returns NULL.
858  */
859 static picl_nodehdl_t
860 find_child_by_name(picl_nodehdl_t parh, char *name)
861 {
862 	picl_nodehdl_t nodeh;
863 	int err;
864 	char	nodename[PICL_PROPNAMELEN_MAX];
865 
866 	err = ptree_get_propval_by_name(parh, PICL_PROP_CHILD,
867 	    &nodeh, sizeof (picl_nodehdl_t));
868 	if (err != PICL_SUCCESS)
869 		return (NULL);
870 	for (;;) {
871 		err = ptree_get_propval_by_name(nodeh, PICL_PROP_NAME, nodename,
872 		    sizeof (nodename));
873 		if (err != PICL_SUCCESS)
874 			return (NULL);
875 		if (strcmp(name, nodename) == 0) {
876 			return (nodeh);
877 		}
878 		err = ptree_get_propval_by_name(nodeh, PICL_PROP_PEER,
879 		    &nodeh, sizeof (picl_nodehdl_t));
880 		if (err != PICL_SUCCESS)
881 			return (NULL);
882 	}
883 }
884 
885 /* Creates a reference property for a given PICL node */
886 static int
887 add_prop_ref(picl_nodehdl_t nodeh, picl_nodehdl_t value, char *name)
888 {
889 	picl_prophdl_t proph;
890 	ptree_propinfo_t propinfo;
891 	int err;
892 
893 	err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
894 	    PICL_PTYPE_REFERENCE, PICL_READ, sizeof (picl_nodehdl_t), name,
895 	    NULL, NULL);
896 	if (err != PICL_SUCCESS) {
897 		syslog(LOG_ERR, PROPINFO_FAIL, name, err);
898 		return (err);
899 	}
900 	err = ptree_create_and_add_prop(nodeh, &propinfo, &value, &proph);
901 	if (err != PICL_SUCCESS) {
902 		syslog(LOG_ERR, ADD_PROP_FAIL, name, err);
903 		return (err);
904 	}
905 	return (PICL_SUCCESS);
906 }
907 
908 /* create an entry in the specified table */
909 static int
910 create_table_entry(picl_prophdl_t tblhdl, picl_nodehdl_t refhdl, char *class)
911 {
912 	int			err;
913 	ptree_propinfo_t	prop;
914 	picl_prophdl_t		prophdl[2];
915 
916 	/* first column is class */
917 	prop.version = PTREE_PROPINFO_VERSION;
918 	prop.piclinfo.type =  PICL_PTYPE_CHARSTRING;
919 	prop.piclinfo.accessmode = PICL_READ;
920 	prop.piclinfo.size = PICL_CLASSNAMELEN_MAX;
921 	prop.read = NULL;
922 	prop.write = NULL;
923 	(void) strlcpy(prop.piclinfo.name, PICL_PROP_CLASS,
924 	    sizeof (prop.piclinfo.name));
925 	err = ptree_create_prop(&prop, class, &prophdl[0]);
926 	if (err != PICL_SUCCESS) {
927 		syslog(LOG_ERR, ADD_TBL_ENTRY_FAIL, err);
928 		return (err);
929 	}
930 
931 	/* second column is reference property */
932 	prop.version = PTREE_PROPINFO_VERSION;
933 	prop.piclinfo.type =  PICL_PTYPE_REFERENCE;
934 	prop.piclinfo.accessmode = PICL_READ;
935 	prop.piclinfo.size = sizeof (picl_nodehdl_t);
936 	prop.read = NULL;
937 	prop.write = NULL;
938 	sprintf_buf2(prop.piclinfo.name, "_%s_", class);
939 	err = ptree_create_prop(&prop, &refhdl, &prophdl[1]);
940 	if (err != PICL_SUCCESS) {
941 		syslog(LOG_ERR, ADD_TBL_ENTRY_FAIL, err);
942 		return (err);
943 	}
944 
945 	/* add row to table */
946 	err = ptree_add_row_to_table(tblhdl, 2, prophdl);
947 	if (err != PICL_SUCCESS)
948 		syslog(LOG_ERR, ADD_TBL_ENTRY_FAIL, err);
949 	return (err);
950 }
951 
952 /* create an empty table property */
953 static int
954 create_table(picl_nodehdl_t fruhdl, picl_prophdl_t *tblhdlp, char *tbl_name)
955 {
956 	int			err;
957 	ptree_propinfo_t	prop;
958 	picl_prophdl_t		tblprophdl;
959 
960 	err = ptree_create_table(tblhdlp);
961 	if (err != PICL_SUCCESS) {
962 		syslog(LOG_ERR, ADD_PROP_FAIL, tbl_name, err);
963 		return (err);
964 	}
965 	prop.version = PTREE_PROPINFO_VERSION;
966 	prop.piclinfo.type =  PICL_PTYPE_TABLE;
967 	prop.piclinfo.accessmode = PICL_READ;
968 	prop.piclinfo.size = sizeof (picl_prophdl_t);
969 	prop.read = NULL;
970 	prop.write = NULL;
971 	(void) strlcpy(prop.piclinfo.name, tbl_name,
972 	    sizeof (prop.piclinfo.name));
973 	err = ptree_create_and_add_prop(fruhdl, &prop, tblhdlp, &tblprophdl);
974 	if (err != PICL_SUCCESS)
975 		syslog(LOG_ERR, ADD_PROP_FAIL, tbl_name, err);
976 	return (err);
977 }
978 
979 /*
980  * The size of outfilename must be PATH_MAX
981  */
982 static int
983 get_config_file(char *outfilename, char *fru)
984 {
985 	char		nmbuf[SYS_NMLN];
986 	char		pname[PATH_MAX];
987 	int		dir;
988 
989 	for (dir = 0; dir < 2; dir++) {
990 		if (sysinfo(dir == 0 ? SI_PLATFORM : SI_MACHINE,
991 		    nmbuf, sizeof (nmbuf)) == -1) {
992 			continue;
993 		}
994 
995 		(void) snprintf(pname, PATH_MAX, PICLD_PLAT_PLUGIN_DIRF, nmbuf);
996 		(void) strlcat(pname, CONFFILE_PREFIX, PATH_MAX);
997 		(void) strlcat(pname, fru, PATH_MAX);
998 		(void) strlcat(pname, CONFFILE_SUFFIX, PATH_MAX);
999 
1000 		if (access(pname, R_OK) == 0) {
1001 			(void) strlcpy(outfilename, pname, PATH_MAX);
1002 			return (0);
1003 		}
1004 	}
1005 
1006 	(void) snprintf(pname, PATH_MAX, "%s/%s%s%s",
1007 	    PICLD_COMMON_PLUGIN_DIR, CONFFILE_PREFIX, fru,
1008 	    CONFFILE_SUFFIX);
1009 
1010 	if (access(pname, R_OK) == 0) {
1011 		(void) strlcpy(outfilename, pname, PATH_MAX);
1012 		return (0);
1013 	}
1014 
1015 	return (-1);
1016 }
1017 
1018 static void
1019 remove_fru_parents(picl_nodehdl_t fruh)
1020 {
1021 	char			name[MAXPATHLEN];
1022 	int			retval;
1023 	picl_nodehdl_t		nodeh;
1024 	picl_prophdl_t		tableh;
1025 	picl_prophdl_t		tblh;
1026 	picl_prophdl_t		fruph;
1027 
1028 	retval = ptree_get_propval_by_name(fruh, PICL_PROP_NAME, name,
1029 	    sizeof (name));
1030 	if (retval != PICL_SUCCESS) {
1031 		syslog(LOG_ERR, EM_UNK_FRU);
1032 		return;
1033 	}
1034 	retval = ptree_get_prop_by_name(fruh, PICL_PROP_DEVICES,
1035 	    &tableh);
1036 
1037 	if (retval != PICL_SUCCESS) {
1038 		/* no Devices table, nothing to do */
1039 		return;
1040 	}
1041 
1042 	/*
1043 	 * follow all reference properties in the second
1044 	 * column of the table and delete any _fru_parent node
1045 	 * at the referenced node.
1046 	 */
1047 	retval = ptree_get_propval(tableh, &tblh, sizeof (tblh));
1048 	if (retval != PICL_SUCCESS) {
1049 		/* can't get value of table property */
1050 		return;
1051 	}
1052 	/* get first col, first row */
1053 	retval = ptree_get_next_by_col(tblh, &tblh);
1054 	if (retval != PICL_SUCCESS) {
1055 		/* no rows? */
1056 		return;
1057 	}
1058 	/*
1059 	 * starting at next col, get every entry in the column
1060 	 */
1061 	for (retval = ptree_get_next_by_row(tblh, &tblh);
1062 	    retval == PICL_SUCCESS;
1063 	    retval = ptree_get_next_by_col(tblh, &tblh)) {
1064 		/*
1065 		 * should be a ref prop in our hands,
1066 		 * get the target node handle
1067 		 */
1068 		retval = ptree_get_propval(tblh, &nodeh,
1069 		    sizeof (nodeh));
1070 		if (retval != PICL_SUCCESS) {
1071 			continue;
1072 		}
1073 		/*
1074 		 * got the referenced node, has it got a
1075 		 * _fru_parent property?
1076 		 */
1077 		retval = ptree_get_prop_by_name(nodeh,
1078 		    PICL_REFPROP_FRU_PARENT, &fruph);
1079 		if (retval != PICL_SUCCESS) {
1080 			continue;
1081 		}
1082 		/*
1083 		 * got a _fru_parent node reference delete it
1084 		 */
1085 		retval = ptree_delete_prop(fruph);
1086 		if (retval != PICL_SUCCESS) {
1087 			continue;
1088 		}
1089 		retval = ptree_destroy_prop(fruph);
1090 		if (retval != PICL_SUCCESS) {
1091 			continue;
1092 		}
1093 	}
1094 }
1095 
1096 static void
1097 remove_tables(picl_nodehdl_t rootnd)
1098 {
1099 	picl_nodehdl_t	tableh;
1100 	int		retval;
1101 
1102 	retval = ptree_get_prop_by_name(rootnd, PICL_PROP_DEVICES, &tableh);
1103 
1104 	if (retval == PICL_SUCCESS) {
1105 		/*
1106 		 * found a Devices property, delete it
1107 		 */
1108 		if ((retval = ptree_delete_prop(tableh)) == PICL_SUCCESS) {
1109 			(void) ptree_destroy_prop(tableh);
1110 		}
1111 	}
1112 
1113 	/*
1114 	 * is there a child node?
1115 	 */
1116 	retval = ptree_get_propval_by_name(rootnd, PICL_PROP_CHILD, &rootnd,
1117 	    sizeof (rootnd));
1118 
1119 	while (retval == PICL_SUCCESS) {
1120 
1121 		remove_tables(rootnd);
1122 
1123 		/*
1124 		 * any siblings?
1125 		 */
1126 		retval = ptree_get_propval_by_name(rootnd, PICL_PROP_PEER,
1127 		    &rootnd, sizeof (rootnd));
1128 	}
1129 }
1130 
1131 /* event completion handler for PICL_FRU_ADDED/PICL_FRU_REMOVED events */
1132 static void
1133 frudr_completion_handler(char *ename, void *earg, size_t size)
1134 {
1135 	picl_nodehdl_t	fruh;
1136 	picl_nodehdl_t	childh;
1137 	char	nodename[PICL_PROPNAMELEN_MAX];
1138 	int err;
1139 
1140 	if (strcmp(ename, PICL_FRU_REMOVED) == 0) {
1141 		/*
1142 		 * now frudata has been notified that the node is to be
1143 		 * removed, we can actually remove it
1144 		 */
1145 		fruh = NULL;
1146 		(void) nvlist_lookup_uint64(earg,
1147 		    PICLEVENTARG_FRUHANDLE, &fruh);
1148 		if (fruh != NULL) {
1149 			/*
1150 			 * first find name of the fru
1151 			 */
1152 			err = ptree_get_propval_by_name(fruh, PICL_PROP_PARENT,
1153 			    &childh, sizeof (childh));
1154 			if (err == PICL_SUCCESS) {
1155 				err = ptree_get_propval_by_name(childh,
1156 				    PICL_PROP_NAME, nodename,
1157 				    sizeof (nodename));
1158 			}
1159 			if (err == PICL_SUCCESS) {
1160 				/*
1161 				 * if it was a power supply, delete i2c node
1162 				 */
1163 				if (strncmp(nodename, PS_NAME,
1164 				    PS_NAME_LEN) == 0) {
1165 					(void) delete_i2c_node(nodename);
1166 				}
1167 
1168 				/*
1169 				 * is disk node, make thread re-evaluate led
1170 				 * state
1171 				 */
1172 				if (strncmp(nodename, DISK_NAME,
1173 				    DISK_NAME_LEN) == 0) {
1174 					disk_ready[nodename[DISK_NAME_LEN] -
1175 					    '0'] = -1;
1176 				}
1177 			}
1178 
1179 			remove_fru_parents(fruh);
1180 
1181 			/*
1182 			 * now we can delete the node
1183 			 */
1184 			err = ptree_delete_node(fruh);
1185 			if (err == PICL_SUCCESS) {
1186 				(void) ptree_destroy_node(fruh);
1187 			} else {
1188 				syslog(LOG_ERR, DELETE_PROP_FAIL, err);
1189 			}
1190 		}
1191 	}
1192 	nvlist_free(earg);
1193 	free(earg);
1194 	free(ename);
1195 }
1196 
1197 /*
1198  * Post the PICL_FRU_ADDED/PICL_FRU_REMOVED event
1199  */
1200 static void
1201 post_frudr_event(char *ename, picl_nodehdl_t parenth, picl_nodehdl_t fruh)
1202 {
1203 	nvlist_t	*nvl;
1204 	char		*ev_name;
1205 
1206 	ev_name = strdup(ename);
1207 	if (ev_name == NULL)
1208 		return;
1209 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME_TYPE, NULL)) {
1210 		free(ev_name);
1211 		return;
1212 	}
1213 	if (parenth != 0L &&
1214 	    nvlist_add_uint64(nvl, PICLEVENTARG_PARENTHANDLE, parenth)) {
1215 		free(ev_name);
1216 		nvlist_free(nvl);
1217 		return;
1218 	}
1219 	if (fruh != 0L &&
1220 	    nvlist_add_uint64(nvl, PICLEVENTARG_FRUHANDLE, fruh)) {
1221 		free(ev_name);
1222 		nvlist_free(nvl);
1223 		return;
1224 	}
1225 	if (ptree_post_event(ev_name, nvl, sizeof (nvl),
1226 	    frudr_completion_handler) != 0) {
1227 		free(ev_name);
1228 		nvlist_free(nvl);
1229 	}
1230 }
1231 
1232 static void
1233 add_ps_to_platform(char *unit)
1234 {
1235 	picl_nodehdl_t		parent_hdl;
1236 	picl_nodehdl_t		child_hdl;
1237 	ptree_propinfo_t	info;
1238 	int			unit_size = 1 + strlen(unit);
1239 	int			res;
1240 	char			unit_addr[PICL_UNITADDR_LEN_MAX];
1241 
1242 	switch (sys_platform) {
1243 	case PLAT_SEATTLE1U:
1244 	case PLAT_SEATTLE2U:
1245 		res = ptree_get_node_by_path(SEATTLE_PSU_PLATFORM, &parent_hdl);
1246 		break;
1247 	case PLAT_BOSTON:
1248 		res = ptree_get_node_by_path(BOSTON_PSU_PLATFORM, &parent_hdl);
1249 		break;
1250 	default:
1251 		res = ptree_get_node_by_path(PSU_PLATFORM, &parent_hdl);
1252 		break;
1253 	}
1254 
1255 	if (res != PICL_SUCCESS)
1256 		return;
1257 	/*
1258 	 * seeprom nodes sit below this node,
1259 	 * is there one with the supplied unit address?
1260 	 */
1261 	res = ptree_get_propval_by_name(parent_hdl, PICL_PROP_CHILD,
1262 	    &child_hdl, sizeof (picl_nodehdl_t));
1263 
1264 	while (res == PICL_SUCCESS) {
1265 		res = ptree_get_propval_by_name(child_hdl, PICL_PROP_PEER,
1266 		    &child_hdl, sizeof (picl_nodehdl_t));
1267 		if ((res == PICL_SUCCESS) &&
1268 			ptree_get_propval_by_name(child_hdl,
1269 			PICL_PROP_UNIT_ADDRESS, unit_addr,
1270 			sizeof (unit_addr)) == PICL_SUCCESS) {
1271 			unit_addr[sizeof (unit_addr) - 1] = '\0';
1272 			if (strcmp(unit_addr, unit) == 0)
1273 				return;	/* unit address exists already */
1274 		}
1275 	}
1276 
1277 	/*
1278 	 * found platform location for PS seeprom node, create it
1279 	 */
1280 	if (ptree_create_and_add_node(parent_hdl, PS_PLATFORM_NAME,
1281 	    PICL_CLASS_SEEPROM, &child_hdl) != PICL_SUCCESS)
1282 		return;
1283 	if (ptree_init_propinfo(&info, PTREE_PROPINFO_VERSION,
1284 	    PICL_PTYPE_CHARSTRING, PICL_READ, unit_size,
1285 	    PICL_PROP_UNIT_ADDRESS, NULL, NULL) != PICL_SUCCESS)
1286 		return;
1287 	(void) ptree_create_and_add_prop(child_hdl, &info, unit, NULL);
1288 }
1289 
1290 /*
1291  * handle EC_DR picl events
1292  */
1293 /*ARGSUSED*/
1294 static void
1295 frudr_evhandler(const char *ename, const void *earg, size_t size, void *cookie)
1296 {
1297 	nvlist_t		*nvlp;
1298 	char			*dtype;
1299 	char			*ap_id;
1300 	char			*hint;
1301 	char			path[MAXPATHLEN];
1302 	picl_nodehdl_t		fruh;
1303 	picl_nodehdl_t		locnodeh;
1304 	int			err;
1305 	int			index;
1306 	picl_nodehdl_t		childh;
1307 	char			*fru_name;
1308 	boolean_t		rmc_flag = B_FALSE;
1309 
1310 	if (strcmp(ename, PICLEVENT_DR_AP_STATE_CHANGE) != 0) {
1311 		return;
1312 	}
1313 
1314 	if (nvlist_unpack((char *)earg, size, &nvlp, NULL)) {
1315 		return;
1316 	}
1317 
1318 	if (nvlist_lookup_string(nvlp, PICLEVENTARG_DATA_TYPE, &dtype)) {
1319 		nvlist_free(nvlp);
1320 		return;
1321 	}
1322 
1323 	if (strcmp(dtype, PICLEVENTARG_PICLEVENT_DATA) != 0) {
1324 		nvlist_free(nvlp);
1325 		return;
1326 	}
1327 
1328 	if (nvlist_lookup_string(nvlp, PICLEVENTARG_AP_ID, &ap_id)) {
1329 		nvlist_free(nvlp);
1330 		return;
1331 	}
1332 
1333 	/*
1334 	 * check ap_id really is a hot-plug device
1335 	 */
1336 	if (strncmp(ap_id, PS_NAME, PS_NAME_LEN) == 0) {
1337 		fru_name = PS_FRU_NAME;
1338 	} else if (strncmp(ap_id, DISK_NAME, DISK_NAME_LEN) == 0) {
1339 		fru_name = DISK_FRU_NAME;
1340 	} else if (strncmp(ap_id, SCC_NAME, SCC_NAME_LEN) == 0) {
1341 		fru_name = SCC_FRU_NAME;
1342 	} else if (strncmp(ap_id, RMC_NAME, RMC_NAME_LEN) == 0) {
1343 		fru_name = RMC_FRU_NAME;
1344 		rmc_flag = B_TRUE;
1345 	} else {
1346 		nvlist_free(nvlp);
1347 		return;
1348 	}
1349 
1350 	if (nvlist_lookup_string(nvlp, PICLEVENTARG_HINT, &hint)) {
1351 		nvlist_free(nvlp);
1352 		return;
1353 	}
1354 
1355 	/*
1356 	 * OK - so this is an EC_DR event - let's handle it.
1357 	 */
1358 	if (rmc_flag && (sys_platform != PLAT_CHALUPA) &&
1359 		(sys_platform != PLAT_CHALUPA19))
1360 		sprintf_buf2(path, SYS_BOARD_PATH, ap_id);
1361 	else {
1362 		if ((sys_platform == PLAT_CHALUPA19) &&
1363 			(strncmp(ap_id, PS_NAME, PS_NAME_LEN) == 0)) {
1364 			sprintf_buf2(path, CHASSIS_LOC_PATH,
1365 				ps_apid_to_nodename(ap_id));
1366 		} else	if (strncmp(ap_id, DISK_NAME, DISK_NAME_LEN) == 0) {
1367 			switch (sys_platform)	{
1368 			case PLAT_SEATTLE1U:
1369 				sprintf_buf2(path, SEATTLE1U_HDDBP_PATH, ap_id);
1370 				break;
1371 			case PLAT_SEATTLE2U:
1372 				sprintf_buf2(path, SEATTLE2U_HDDBP_PATH, ap_id);
1373 				break;
1374 			case PLAT_BOSTON:
1375 				sprintf_buf2(path, BOSTON_HDDBP_PATH, ap_id);
1376 				break;
1377 			default:
1378 				sprintf_buf2(path, CHASSIS_LOC_PATH, ap_id);
1379 				break;
1380 			}
1381 		} else	{
1382 			sprintf_buf2(path, CHASSIS_LOC_PATH, ap_id);
1383 
1384 		}
1385 	}
1386 
1387 	if (ptree_get_node_by_path(path, &locnodeh) != PICL_SUCCESS) {
1388 		nvlist_free(nvlp);
1389 		return;
1390 	}
1391 
1392 	/*
1393 	 * now either add or delete the fru node as appropriate. If no
1394 	 * hint, treat as insert and update the tree if necessary.
1395 	 */
1396 	if (strcmp(hint, DR_HINT_REMOVE) == 0) {
1397 		if (ptree_get_propval_by_name(locnodeh, PICL_PROP_CHILD,
1398 		    &fruh, sizeof (picl_nodehdl_t)) == PICL_SUCCESS) {
1399 			/*
1400 			 * fru was there - but has gone away
1401 			 */
1402 			post_frudr_event(PICL_FRU_REMOVED, NULL, fruh);
1403 		}
1404 	} else if (rmc_flag) {
1405 		/*
1406 		 * An event on the RMC location, just pass it on
1407 		 * it's not really a PICL_FRU_ADDED event, so offer
1408 		 * the child handle as well (if it exists).
1409 		 */
1410 		if (ptree_get_propval_by_name(locnodeh, PICL_PROP_CHILD,
1411 		    &fruh, sizeof (picl_nodehdl_t)) != PICL_SUCCESS) {
1412 			fruh = NULL;
1413 		}
1414 		post_frudr_event(PICL_FRU_ADDED, locnodeh, fruh);
1415 	} else {
1416 		/*
1417 		 * fru has been inserted (or may need to update)
1418 		 * if node already there, then just return
1419 		 */
1420 		childh = find_child_by_name(locnodeh, fru_name);
1421 		if (childh != NULL) {
1422 			nvlist_free(nvlp);
1423 			return;
1424 		}
1425 
1426 		/*
1427 		 * create requested fru node
1428 		 */
1429 		err = ptree_create_and_add_node(locnodeh, fru_name,
1430 		    PICL_CLASS_FRU, &childh);
1431 		if (err != PICL_SUCCESS) {
1432 			syslog(LOG_ERR, ADD_NODE_FAIL, ap_id, err);
1433 			nvlist_free(nvlp);
1434 			return;
1435 		}
1436 
1437 		/*
1438 		 * power supplies have operational status and fruid -
1439 		 * add OperationalStatus property and create i2c device node
1440 		 * before posting fru_added event
1441 		 */
1442 		if (strncmp(ap_id, PS_NAME, PS_NAME_LEN) == 0) {
1443 			index = find_vol_prop_by_name(
1444 					ps_apid_to_nodename(ap_id));
1445 			if (index >= 0)
1446 				add_op_status_to_node(childh,
1447 				    &idprop->idp[index].volprop);
1448 			(void) create_i2c_node(ap_id);
1449 			add_ps_to_platform(ps_name_to_unitaddr(ap_id));
1450 		}
1451 
1452 		/*
1453 		 * now post event
1454 		 */
1455 		post_frudr_event(PICL_FRU_ADDED, locnodeh, NULL);
1456 	}
1457 	nvlist_free(nvlp);
1458 }
1459 
1460 /*
1461  * Handle PICL_FRU_ADDED events.
1462  * These events are posted by the frudr_evhandler of this plugin in response to
1463  * PICLEVENT_DR_AP_STATE_CHANGE events. The sequence is as follows:
1464  *	1) frudr_evhandler catches PICLEVENT_DR_AP_STATE_CHANGE and creates a
1465  *	child node below the relevant location.
1466  *	2) frudr_evhandler posts a PICL_FRU_ADDED event.
1467  *	3) envmon catches PICL_FRU_ADDED event, gropes the RMC configuration
1468  *	and creates platform tree nodes (primarily for PSUs). (If the event
1469  *	is for the RMC itself, envmon deletes existing platform nodes and
1470  *	rebuilds from scratch.)
1471  *	4) this plugin catches PICL_FRU_ADDED event, looks for a related
1472  *	configuration file and parses it. This adds Fru data properties (etc.).
1473  *	5) frudata catches the event and updates its FRUID data cache.
1474  */
1475 /*ARGSUSED*/
1476 static void
1477 fru_add_handler(const char *ename, const void *earg, size_t size, void *cookie)
1478 {
1479 	int			retval;
1480 	picl_nodehdl_t		locnodeh;
1481 	picl_nodehdl_t		rooth;
1482 	char			path[MAXPATHLEN];
1483 	char			*fru_name;
1484 
1485 	if (strcmp(ename, PICL_FRU_ADDED) != 0)
1486 		return;
1487 
1488 	retval = nvlist_lookup_uint64((nvlist_t *)earg,
1489 	    PICLEVENTARG_PARENTHANDLE, &locnodeh);
1490 
1491 	if (retval != PICL_SUCCESS)
1492 		return;
1493 
1494 	retval = ptree_get_propval_by_name(locnodeh, PICL_PROP_NAME,
1495 	    path, sizeof (path));
1496 
1497 	if (retval != PICL_SUCCESS)
1498 		return;
1499 
1500 	fru_name = strdup(path);
1501 
1502 	if (fru_name == NULL)
1503 		return;
1504 
1505 	/*
1506 	 * We're about to parse a fru-specific .conf file to populate
1507 	 * picl nodes relating to the dynamically added component. In the
1508 	 * case of the RMC, there is a problem: all of its /platform tree
1509 	 * nodes have just been replaced by envmon. It is now necessary to
1510 	 * repopulate Devices tables in /frutree.
1511 	 * picld_pluginutil_parse_config_file doesn't handle repopulating
1512 	 * existing tables, so as a work round, delete all tables found
1513 	 * under /frutree. This works on Enchilada Server as the tables
1514 	 * are all created from parsing a .conf file, and we're about to
1515 	 * redo that action.
1516 	 */
1517 	if (strcmp(fru_name, RMC_NAME) == 0) {
1518 		rmc_state_event();
1519 		retval = ptree_get_node_by_path(FRUTREE_PATH, &rooth);
1520 		if (retval == PICL_SUCCESS) {
1521 			remove_tables(rooth);
1522 		}
1523 	}
1524 
1525 	/*
1526 	 * Re-establish the HPU(FRU) volatile properties.
1527 	 * This needs to be done before the .conf file is parsed because
1528 	 * it has a side effect of re-creating any missing power-supply
1529 	 * fru node. The .conf file can then hang properties beneath.
1530 	 */
1531 	opst_init();
1532 
1533 	/*
1534 	 * see if there's a .conf file for this fru
1535 	 */
1536 	if (get_config_file(path, fru_name) == 0) {
1537 		if ((ptree_get_root(&rooth) != PICL_SUCCESS) ||
1538 		    (picld_pluginutil_parse_config_file(rooth, path) !=
1539 		    PICL_SUCCESS)) {
1540 			syslog(LOG_ERR, PARSE_CONF_FAIL, path);
1541 		}
1542 	}
1543 
1544 	free(fru_name);
1545 }
1546 
1547 /*
1548  * Handle PICLEVENT_SYSEVENT_DEVICE_ADDED events.
1549  */
1550 /*ARGSUSED*/
1551 static void
1552 frutree_evhandler(const char *ename, const void *earg, size_t size,
1553     void *cookie)
1554 {
1555 	nvlist_t		*nvlp;
1556 	picl_nodehdl_t		rooth;
1557 	char			path[MAXPATHLEN];
1558 	char			*fru_name;
1559 	char			*dtype;
1560 	char			*dpath;
1561 	char			*ptr;
1562 	char			*ptr2;
1563 	int			done = B_FALSE;
1564 	int			i;
1565 
1566 	if (strcmp(ename, PICLEVENT_SYSEVENT_DEVICE_ADDED) != 0)
1567 		return;
1568 
1569 	if (nvlist_unpack((char *)earg, size, &nvlp, NULL))
1570 		return;
1571 
1572 	if (nvlist_lookup_string(nvlp, PICLEVENTARG_DATA_TYPE, &dtype)) {
1573 		nvlist_free(nvlp);
1574 		return;
1575 	}
1576 
1577 	if (strcmp(dtype, PICLEVENTARG_PICLEVENT_DATA) != 0) {
1578 		nvlist_free(nvlp);
1579 		return;
1580 	}
1581 
1582 	if (nvlist_lookup_string(nvlp, PICLEVENTARG_DEVFS_PATH, &dpath)) {
1583 		nvlist_free(nvlp);
1584 		return;
1585 	}
1586 
1587 	fru_name = strdup(dpath);
1588 
1589 	if (fru_name == NULL) {
1590 		nvlist_free(nvlp);
1591 		return;
1592 	}
1593 
1594 	nvlist_free(nvlp);
1595 
1596 	/*
1597 	 * fru_name is of the form
1598 	 *	"/pci@1e,600000/usb@a/mouse@2"
1599 	 * or
1600 	 *	"/pci@1e,600000/usb@a/device@2/mouse@0"
1601 	 * reduce it to "usb-a-2"
1602 	 */
1603 	if ((sys_platform == PLAT_SEATTLE1U) ||
1604 	    (sys_platform == PLAT_SEATTLE2U) ||
1605 	    (sys_platform == PLAT_BOSTON)) {
1606 		for (i = 0; i < MAX_USB_PORTS; i++) {
1607 			sprintf(fru_name, "%s%d", USB_CONF_FILE_NAME, i+1);
1608 			if (get_config_file(path, fru_name) == 0) {
1609 				if ((ptree_get_root(&rooth) != PICL_SUCCESS) ||
1610 				    (picld_pluginutil_parse_config_file(rooth,
1611 				    path) != PICL_SUCCESS)) {
1612 					syslog(LOG_ERR, PARSE_CONF_FAIL, path);
1613 				}
1614 			}
1615 		}
1616 	} else {
1617 		ptr = fru_name;
1618 		if (*ptr == '/') {
1619 			ptr++;
1620 			ptr = strchr(ptr, '/');
1621 			if (ptr != NULL) {
1622 				ptr++;
1623 				(void) memmove(fru_name, ptr, strlen(ptr) + 1);
1624 				ptr = strchr(fru_name, '@');
1625 				if (ptr != NULL) {
1626 					*ptr = '-';
1627 					ptr++;
1628 					ptr = strchr(ptr, '/');
1629 					if (ptr != NULL) {
1630 						*ptr = '-';
1631 						ptr++;
1632 						ptr2 = ptr;
1633 						ptr = strchr(ptr, '@');
1634 						if (ptr != NULL) {
1635 							ptr++;
1636 							(void) memmove(ptr2,
1637 							    ptr, strlen(ptr)+1);
1638 							ptr2 = strchr(ptr2,
1639 							    '/');
1640 							if (ptr2 != NULL) {
1641 								*ptr2 = '\0';
1642 							}
1643 							done = B_TRUE;
1644 						}
1645 					}
1646 				}
1647 			}
1648 		}
1649 		if (done == B_FALSE) {
1650 			free(fru_name);
1651 			return;
1652 		}
1653 
1654 		/*
1655 		 * see if there's a .conf file for this fru
1656 		 */
1657 
1658 		if (get_config_file(path, fru_name) == 0) {
1659 			if ((ptree_get_root(&rooth) != PICL_SUCCESS) ||
1660 			    (picld_pluginutil_parse_config_file(rooth, path) !=
1661 			    PICL_SUCCESS)) {
1662 				syslog(LOG_ERR, PARSE_CONF_FAIL, path);
1663 			}
1664 		}
1665 	}
1666 
1667 	free(fru_name);
1668 }
1669 
1670 static int
1671 set_led(char *name, char *ptr, char *value)
1672 {
1673 	char			path[MAXPATHLEN];
1674 	picl_prophdl_t		proph;
1675 	ptree_propinfo_t	propinfo;
1676 	picl_prophdl_t		tableh;
1677 	picl_nodehdl_t		locnodeh;
1678 	picl_nodehdl_t		nodeh;
1679 	picl_prophdl_t		tblh;
1680 	int			retval;
1681 	char			*value_ptr;
1682 	char			label[PICL_PROPNAMELEN_MAX];
1683 	char			class[PICL_PROPNAMELEN_MAX];
1684 
1685 	/* find the location node */
1686 	switch (sys_platform)	{
1687 	case PLAT_CHALUPA:
1688 	case PLAT_CHALUPA19:
1689 		sprintf_buf2(path, CHASSIS_LOC_PATH, name);
1690 		break;
1691 	case PLAT_SEATTLE1U:
1692 		sprintf_buf2(path, SEATTLE1U_HDDBP_PATH, name);
1693 		break;
1694 	case PLAT_SEATTLE2U:
1695 		sprintf_buf2(path, SEATTLE2U_HDDBP_PATH, name);
1696 		break;
1697 	case PLAT_BOSTON:
1698 		sprintf_buf2(path, BOSTON_HDDBP_PATH, name);
1699 		break;
1700 	default:
1701 		sprintf_buf2(path, CHASSIS_LOC_PATH, name);
1702 		break;
1703 	}
1704 
1705 	if (ptree_get_node_by_path(path, &locnodeh) != PICL_SUCCESS)	{
1706 		return (PICL_FAILURE);
1707 	}
1708 
1709 	/*
1710 	 * if no fru node, then turn led off
1711 	 */
1712 	if (find_child_by_name(locnodeh, DISK_FRU_NAME) != NULL)
1713 		value_ptr = value;
1714 	else
1715 		value_ptr = PICL_PROPVAL_OFF;
1716 
1717 	/* get its Devices table */
1718 	if (ptree_get_prop_by_name(locnodeh, PICL_PROP_DEVICES, &tableh) !=
1719 	    PICL_SUCCESS)
1720 		return (PICL_FAILURE);
1721 	if (ptree_get_propval(tableh, &tblh, sizeof (tblh)) != PICL_SUCCESS)
1722 		return (PICL_FAILURE);
1723 
1724 	/* get first col, first row */
1725 	if (ptree_get_next_by_col(tblh, &tblh) != PICL_SUCCESS)
1726 		return (PICL_FAILURE);
1727 
1728 	/*
1729 	 * starting at next col, get every entry in the column
1730 	 */
1731 	for (retval = ptree_get_next_by_row(tblh, &tblh);
1732 	    retval == PICL_SUCCESS;
1733 	    retval = ptree_get_next_by_col(tblh, &tblh)) {
1734 		/*
1735 		 * get the target node handle
1736 		 */
1737 		if (ptree_get_propval(tblh, &nodeh, sizeof (nodeh))
1738 		    != PICL_SUCCESS)
1739 			continue;
1740 
1741 		/*
1742 		 * check it's a led
1743 		 */
1744 		if (ptree_get_propval_by_name(nodeh, PICL_PROP_CLASSNAME,
1745 		    class, sizeof (class)) != PICL_SUCCESS)
1746 			continue;
1747 		if (strcmp(class, "led") != 0)
1748 			continue;
1749 
1750 		/*
1751 		 * check its the right led
1752 		 */
1753 		if (ptree_get_propval_by_name(nodeh, PICL_PROP_LABEL,
1754 		    label, sizeof (label)) != PICL_SUCCESS)
1755 			continue;
1756 		if (strcmp(label, ptr) == 0) {
1757 			/*
1758 			 * set it
1759 			 */
1760 			if (ptree_get_prop_by_name(nodeh, PICL_PROP_STATE,
1761 			    &proph) != PICL_SUCCESS)
1762 				continue;
1763 			if (ptree_get_propinfo(proph, &propinfo) !=
1764 			    PICL_SUCCESS)
1765 				continue;
1766 			retval =  ptree_update_propval_by_name(nodeh,
1767 			    PICL_PROP_STATE, value_ptr, propinfo.piclinfo.size);
1768 			return (retval);
1769 		}
1770 	}
1771 	return (PICL_FAILURE);
1772 }
1773 
1774 /*
1775  * function to find first node of specified class beneath supplied node
1776  */
1777 static int
1778 get_node_by_class(picl_nodehdl_t nodeh, const char *classname,
1779     picl_nodehdl_t *foundnodeh)
1780 {
1781 	int		err;
1782 	char		clname[PICL_CLASSNAMELEN_MAX+1];
1783 	picl_nodehdl_t	childh;
1784 
1785 	/*
1786 	 * go through the children
1787 	 */
1788 	err = ptree_get_propval_by_name(nodeh, PICL_PROP_CHILD, &childh,
1789 	    sizeof (picl_nodehdl_t));
1790 
1791 	while (err == PICL_SUCCESS) {
1792 		err = ptree_get_propval_by_name(childh, PICL_PROP_CLASSNAME,
1793 		    clname, sizeof (clname));
1794 
1795 		if ((err == PICL_SUCCESS) && (strcmp(clname, classname) == 0)) {
1796 			*foundnodeh = childh;
1797 			return (PICL_SUCCESS);
1798 		}
1799 
1800 		err = get_node_by_class(childh, classname, foundnodeh);
1801 		if (err == PICL_SUCCESS)
1802 			return (PICL_SUCCESS);
1803 
1804 		err = ptree_get_propval_by_name(childh, PICL_PROP_PEER,
1805 		    &childh, sizeof (picl_nodehdl_t));
1806 	}
1807 
1808 	return (PICL_NODENOTFOUND);
1809 }
1810 
1811 /*
1812  * get system-controller node
1813  */
1814 static int
1815 get_sys_controller_node(picl_nodehdl_t *nodeh)
1816 {
1817 	int		err;
1818 
1819 	/* get platform node */
1820 	err = ptree_get_node_by_path(PICL_NODE_ROOT PICL_NODE_PLATFORM, nodeh);
1821 	if (err != PICL_SUCCESS)
1822 		return (err);
1823 	err = get_node_by_class(*nodeh, PICL_CLASS_SERVICE_PROCESSOR, nodeh);
1824 	return (err);
1825 }
1826 
1827 /*
1828  * create pathname string for system-controller device
1829  */
1830 static char *
1831 create_sys_controller_pathname(picl_nodehdl_t sysconh)
1832 {
1833 	char		*ptr;
1834 	char		namebuf[PATH_MAX];
1835 	size_t		len;
1836 	DIR		*dirp;
1837 	struct dirent	*dp;
1838 	struct stat	statbuf;
1839 
1840 	/*
1841 	 * prefix devfs-path name with /devices
1842 	 */
1843 	(void) strlcpy(namebuf, DEV_PREFIX, PATH_MAX);
1844 
1845 	/*
1846 	 * append devfs-path property
1847 	 */
1848 	len = strlen(namebuf);
1849 	if (ptree_get_propval_by_name(sysconh, str_devfs_path, namebuf + len,
1850 	    sizeof (namebuf) - len) != PICL_SUCCESS) {
1851 		return (NULL);
1852 	}
1853 
1854 	/*
1855 	 * locate final component of name
1856 	 */
1857 	ptr = strrchr(namebuf, '/');
1858 	if (ptr == NULL)
1859 		return (NULL);
1860 	*ptr = '\0';		/* terminate at end of directory path */
1861 	len = strlen(ptr + 1);	/* length of terminal name */
1862 	dirp = opendir(namebuf);
1863 	if (dirp == NULL) {
1864 		return (NULL);
1865 	}
1866 	*ptr++ = '/';		/* restore '/' and advance to final name */
1867 
1868 	while ((dp = readdir(dirp)) != NULL) {
1869 		/*
1870 		 * look for a name which starts with the string at *ptr
1871 		 */
1872 		if (strlen(dp->d_name) < len)
1873 			continue;	/* skip short names */
1874 		if (strncmp(dp->d_name, ptr, len) == 0) {
1875 			/*
1876 			 * Got a match, restore full pathname and stat the
1877 			 * entry. Reject if not a char device
1878 			 */
1879 			(void) strlcpy(ptr, dp->d_name,
1880 			    sizeof (namebuf) - (ptr - namebuf));
1881 			if (stat(namebuf, &statbuf) < 0)
1882 				continue;	/* reject if can't stat it */
1883 			if (!S_ISCHR(statbuf.st_mode))
1884 				continue;	/* not a character device */
1885 			/*
1886 			 * go with this entry
1887 			 */
1888 			(void) closedir(dirp);
1889 			return (strdup(namebuf));
1890 		}
1891 	}
1892 	(void) closedir(dirp);
1893 	return (NULL);
1894 }
1895 
1896 /*
1897  * create pathname string for bezel leds device
1898  */
1899 static char *
1900 create_bezel_leds_pathname(const char *dirpath, const char *devname)
1901 {
1902 	char		namebuf[PATH_MAX];
1903 	size_t		lendirpath;
1904 	size_t		len;
1905 	DIR		*dirp;
1906 	struct dirent	*dp;
1907 	struct stat	statbuf;
1908 
1909 	/*
1910 	 * start with directory name
1911 	 */
1912 	(void) strlcpy(namebuf, dirpath, PATH_MAX);
1913 
1914 	/*
1915 	 * append devfs-path property
1916 	 */
1917 	lendirpath = strlen(namebuf);
1918 	dirp = opendir(namebuf);
1919 	if (dirp == NULL) {
1920 		return (NULL);
1921 	}
1922 
1923 	len = strlen(devname);
1924 
1925 	while ((dp = readdir(dirp)) != NULL) {
1926 		/*
1927 		 * look for a name which starts with the gpio string
1928 		 */
1929 		if (strlen(dp->d_name) < len)
1930 			continue;	/* skip short names */
1931 		if (strncmp(dp->d_name, devname, len) == 0) {
1932 			/*
1933 			 * Got a match, restore full pathname and stat the
1934 			 * entry. Reject if not a char device
1935 			 */
1936 			(void) strlcpy(namebuf + lendirpath, dp->d_name,
1937 			    sizeof (namebuf) - lendirpath);
1938 			if (stat(namebuf, &statbuf) < 0)
1939 				continue;	/* reject if can't stat it */
1940 			if (!S_ISCHR(statbuf.st_mode))
1941 				continue;	/* not a character device */
1942 			/*
1943 			 * go with this entry
1944 			 */
1945 			(void) closedir(dirp);
1946 			return (strdup(namebuf));
1947 		}
1948 	}
1949 	(void) closedir(dirp);
1950 	return (NULL);
1951 }
1952 
1953 /*
1954  * initialise structure associated with nodes requiring OperationalStatus
1955  */
1956 static void
1957 opst_init(void)
1958 {
1959 	int			res;
1960 	int			index = 0;
1961 	int			fd;
1962 	int			entries = 0;
1963 	int			err = 0;
1964 	boolean_t		rmc_flag;
1965 	boolean_t		ps_flag;
1966 	boolean_t		disk_flag;
1967 	size_t			len;
1968 	envmon_sysinfo_t	sysinfo;
1969 	envmon_hpu_t		hpu;
1970 
1971 	if (idprop != NULL) {
1972 		/*
1973 		 * This must be a restart, clean up earlier allocation
1974 		 */
1975 		free(idprop);
1976 		idprop = NULL;
1977 	}
1978 
1979 	if (sc_device_name == NULL)
1980 		err = 1;
1981 	else {
1982 		fd = open(sc_device_name, O_RDONLY);
1983 
1984 		if (fd < 0) {
1985 			syslog(LOG_ERR, EM_NO_SC_DEV);
1986 			err = 1;
1987 		}
1988 	}
1989 
1990 	if (err == 0) {
1991 		res = ioctl(fd, ENVMONIOCSYSINFO, &sysinfo);
1992 
1993 		if (res < 0) {
1994 			syslog(LOG_ERR, EM_NO_SYSINFO, strerror(errno));
1995 			(void) close(fd);
1996 			err = 1;
1997 		}
1998 	}
1999 
2000 	if (err == 0) {
2001 		entries = sysinfo.maxHPU;
2002 		len = offsetof(idp_lkup_t, idp) + entries * sizeof (id_props_t);
2003 		idprop = calloc(len, 1);
2004 		if (idprop == NULL) {
2005 			(void) close(fd);
2006 			err = 1;
2007 		}
2008 	}
2009 
2010 	if (err == 0) {
2011 		idprop->maxnum = entries;
2012 		hpu.id.name[0] = '\0';	/* request for first name */
2013 		res = ioctl(fd, ENVMONIOCHPU, &hpu);
2014 
2015 		/*
2016 		 * The HPU node for the RMC is a special case. Its handle is
2017 		 * generated by the rmclomv driver. Rather than building
2018 		 * knowledge of its frutree hierarchic name into the driver, we
2019 		 * put that knowledge here.
2020 		 */
2021 		while ((res == 0) && (index < entries) &&
2022 		    (hpu.next_id.name[0] != '\0')) {
2023 			hpu.id = hpu.next_id;
2024 			res = ioctl(fd, ENVMONIOCHPU, &hpu);
2025 			if ((res == 0) &&
2026 			    ((hpu.sensor_status & ENVMON_NOT_PRESENT) == 0)) {
2027 				add_op_status(&hpu, &index);
2028 			}
2029 		}
2030 
2031 		idprop->num = index;
2032 		(void) close(fd);
2033 	}
2034 }
2035 
2036 static void
2037 disk_leds_init(void)
2038 {
2039 	int err = 0, i;
2040 
2041 	if (!g_mutex_init) {
2042 		if ((pthread_cond_init(&g_cv, NULL) == 0) &&
2043 		    (pthread_cond_init(&g_cv_ack, NULL) == 0) &&
2044 		    (pthread_mutex_init(&g_mutex, NULL) == 0)) {
2045 			g_mutex_init = B_TRUE;
2046 		} else {
2047 			return;
2048 		}
2049 	}
2050 
2051 	/*
2052 	 * Initialise to -1 so the led thread will set correctly.
2053 	 * Do this before creating the disk_leds thread,
2054 	 * so there's no race.
2055 	 */
2056 	for (i = 0; i < N_DISKS; i++)
2057 		disk_ready[i] = -1;
2058 
2059 	if (ledsthr_created) {
2060 		/*
2061 		 * this is a restart, wake up sleeping threads
2062 		 */
2063 		err = pthread_mutex_lock(&g_mutex);
2064 		if (err != 0) {
2065 			syslog(LOG_ERR, EM_MUTEX_FAIL, strerror(errno));
2066 			return;
2067 		}
2068 		g_finish_now = B_FALSE;
2069 		(void) pthread_cond_broadcast(&g_cv);
2070 		(void) pthread_mutex_unlock(&g_mutex);
2071 	} else {
2072 		if ((pthread_attr_init(&ledsthr_attr) != 0) ||
2073 		    (pthread_attr_setscope(&ledsthr_attr,
2074 		    PTHREAD_SCOPE_SYSTEM) != 0))
2075 			return;
2076 		if ((err = pthread_create(&ledsthr_tid, &ledsthr_attr,
2077 		    disk_leds_thread, NULL)) != 0) {
2078 			syslog(LOG_ERR, EM_THREAD_CREATE_FAILED,
2079 			    strerror(errno));
2080 			return;
2081 		}
2082 		ledsthr_created = B_TRUE;
2083 	}
2084 }
2085 
2086 static void
2087 disk_leds_fini(void)
2088 {
2089 	int	err, i;
2090 
2091 	/*
2092 	 * turn the leds off as we'll no longer be monitoring them
2093 	 */
2094 	for (i = 0; i < N_DISKS; i++)
2095 		(void) set_led(disk_name[i], REMOK_LED, PICL_PROPVAL_OFF);
2096 
2097 	/*
2098 	 * disk_leds_thread() never started or an error occured so
2099 	 * that it's not running
2100 	 */
2101 	if (!disk_leds_thread_running)
2102 		return;
2103 
2104 	/*
2105 	 * tell led thread to pause
2106 	 */
2107 	if (!ledsthr_created)
2108 		return;
2109 	err = pthread_mutex_lock(&g_mutex);
2110 	if (err != 0) {
2111 		syslog(LOG_ERR, EM_MUTEX_FAIL, strerror(errno));
2112 		return;
2113 	}
2114 	g_finish_now = B_TRUE;
2115 	disk_leds_thread_ack = B_FALSE;
2116 	(void) pthread_cond_broadcast(&g_cv);
2117 
2118 	/*
2119 	 * and wait for them to acknowledge
2120 	 */
2121 	while (!disk_leds_thread_ack) {
2122 		(void) pthread_cond_wait(&g_cv_ack, &g_mutex);
2123 	}
2124 	(void) pthread_mutex_unlock(&g_mutex);
2125 }
2126 
2127 static void
2128 update_disk_node(char *fruname, char *devpath)
2129 {
2130 	picl_nodehdl_t slotndh;
2131 	picl_nodehdl_t diskndh;
2132 	picl_nodehdl_t devhdl;
2133 	picl_prophdl_t tblhdl;
2134 	picl_prophdl_t tblhdl2;
2135 	picl_prophdl_t tblproph;
2136 	int err;
2137 	char path[MAXPATHLEN];
2138 
2139 	switch (sys_platform)	{
2140 	case PLAT_CHALUPA:
2141 	case PLAT_CHALUPA19:
2142 		sprintf_buf2(path, CHASSIS_LOC_PATH, fruname);
2143 		break;
2144 	case PLAT_SEATTLE1U:
2145 		sprintf_buf2(path, SEATTLE1U_HDDBP_PATH, fruname);
2146 		break;
2147 	case PLAT_SEATTLE2U:
2148 		sprintf_buf2(path, SEATTLE2U_HDDBP_PATH, fruname);
2149 		break;
2150 	case PLAT_BOSTON:
2151 		sprintf_buf2(path, BOSTON_HDDBP_PATH, fruname);
2152 		break;
2153 	default:
2154 		sprintf_buf2(path, CHASSIS_LOC_PATH, fruname);
2155 		break;
2156 	}
2157 
2158 	if (ptree_get_node_by_path(path, &slotndh) != PICL_SUCCESS) {
2159 		return;
2160 	}
2161 	diskndh = find_child_by_name(slotndh, DISK_FRU_NAME);
2162 	if (diskndh == NULL) {
2163 		return;
2164 	}
2165 	err = ptree_get_node_by_path(devpath, &devhdl);
2166 	if (err == PICL_SUCCESS) {
2167 		err = ptree_get_propval_by_name(diskndh,
2168 		    PICL_PROP_DEVICES, &tblhdl, sizeof (tblhdl));
2169 		if (err != PICL_SUCCESS)
2170 			return;
2171 		err = ptree_get_next_by_col(tblhdl, &tblhdl2);
2172 		if (err != PICL_SUCCESS) {
2173 			err = create_table_entry(tblhdl, devhdl,
2174 			    PICL_CLASS_BLOCK);
2175 			if (err != PICL_SUCCESS)
2176 				return;
2177 			err = add_prop_ref(devhdl, diskndh,
2178 			    PICL_REFPROP_FRU_PARENT);
2179 			if (err != PICL_SUCCESS)
2180 				return;
2181 		}
2182 	} else {
2183 		/*
2184 		 * no mechanism for deleting row - so delete
2185 		 * whole table and start again
2186 		 */
2187 		err = ptree_get_prop_by_name(diskndh, PICL_PROP_DEVICES,
2188 		    &tblproph);
2189 		if (err != PICL_SUCCESS)
2190 			return;
2191 		err = ptree_delete_prop(tblproph);
2192 		if (err != PICL_SUCCESS)
2193 			return;
2194 		(void) ptree_destroy_prop(tblproph);
2195 		err = create_table(diskndh, &tblhdl, PICL_PROP_DEVICES);
2196 		if (err != PICL_SUCCESS)
2197 			return;
2198 	}
2199 }
2200 
2201 /*
2202  * We will light the OK2REMOVE LED for disks configured
2203  * into a raid if (and only if) the driver reports
2204  * that the disk has failed.
2205  */
2206 static int
2207 raid_ok2rem_policy(raid_config_t config, int target)
2208 {
2209 	int i;
2210 
2211 	for (i = 0; i < config.ndisks; i++) {
2212 		int d = config.disk[i];
2213 		int dstatus = config.diskstatus[i];
2214 
2215 		if (d  == target)	{
2216 			switch (dstatus) {
2217 			case RAID_DISKSTATUS_MISSING:
2218 				/* If LED is on, turn it off */
2219 				if (disk_ready[d] == B_FALSE) {
2220 					if (set_led(disk_name[d], REMOK_LED,
2221 					    PICL_PROPVAL_OFF) == PICL_SUCCESS) {
2222 						disk_ready[d] = B_TRUE;
2223 					}
2224 				}
2225 			break;
2226 			case RAID_DISKSTATUS_GOOD:
2227 				if (disk_ready[d] != B_TRUE) {
2228 					if (set_led(disk_name[d], REMOK_LED,
2229 					    PICL_PROPVAL_OFF) == PICL_SUCCESS) {
2230 						disk_ready[d] = B_TRUE;
2231 					}
2232 				}
2233 			break;
2234 			case RAID_DISKSTATUS_FAILED:
2235 				if (disk_ready[d] != B_FALSE) {
2236 					if (set_led(disk_name[d], REMOK_LED,
2237 					PICL_PROPVAL_ON) == PICL_SUCCESS) {
2238 						disk_ready[d] = B_FALSE;
2239 					}
2240 				}
2241 			break;
2242 			default:
2243 			break;
2244 			}
2245 			return (1);
2246 		}
2247 	}
2248 	return (0);
2249 }
2250 
2251 static int
2252 check_raid(int target)
2253 {
2254 	raid_config_t	config;
2255 	int	fd;
2256 	int	numvols;
2257 	int	i, j;
2258 
2259 	switch (sys_platform) {
2260 	case PLAT_CHALUPA:
2261 	case PLAT_CHALUPA19:
2262 		fd = open(V440_DISK_DEVCTL, O_RDONLY);
2263 		break;
2264 	case PLAT_SEATTLE1U:
2265 	case PLAT_SEATTLE2U:
2266 		fd = open(SEATTLE_DISK_DEVCTL, O_RDONLY);
2267 		break;
2268 	case PLAT_BOSTON:
2269 		if (boston_1068e_flag) {
2270 		    fd = open(BOSTON_DISK_DEVCTL_1068E, O_RDONLY);
2271 		} else {
2272 		    fd = open(BOSTON_DISK_DEVCTL_1068X, O_RDONLY);
2273 		}
2274 		break;
2275 	default:
2276 		fd = -1;
2277 		break;
2278 	}
2279 
2280 	if (fd == -1) {
2281 		return (0);
2282 	}
2283 
2284 	if (ioctl(fd, RAID_NUMVOLUMES, &numvols)) {
2285 		(void) close(fd);
2286 		return (0);
2287 	}
2288 
2289 	for (i = 0; i < numvols; i++)	{
2290 		config.unitid = i;
2291 		if (ioctl(fd, RAID_GETCONFIG, &config)) {
2292 			(void) close(fd);
2293 			return (0);
2294 		}
2295 		if (raid_ok2rem_policy(config, target))	{
2296 			(void) close(fd);
2297 			return (1);
2298 		}
2299 	}
2300 
2301 	(void) close(fd);
2302 	return (0);
2303 }
2304 
2305 /*ARGSUSED*/
2306 static void *
2307 disk_leds_thread(void *args)
2308 {
2309 	int 	c;
2310 	int 	i;
2311 	char	**disk_dev;
2312 	int fd;
2313 
2314 	devctl_hdl_t dhdl;
2315 
2316 	int 	n_disks = 0,
2317 		do_raid = 0,
2318 		err 	= 0;
2319 	uint_t	statep	= 0;
2320 
2321 	static char *mpxu_devs[] = {
2322 		"/pci@1c,600000/scsi@2/sd@0,0",
2323 		"/pci@1c,600000/scsi@2/sd@1,0",
2324 		"/pci@1c,600000/scsi@2/sd@2,0",
2325 		"/pci@1c,600000/scsi@2/sd@3,0"
2326 	};
2327 
2328 	static char *ents_devs[] = {
2329 		"/pci@1d,700000/scsi@4/sd@0,0",
2330 		"/pci@1d,700000/scsi@4/sd@1,0",
2331 		"/pci@1d,700000/scsi@4/sd@2,0",
2332 		"/pci@1d,700000/scsi@4/sd@3,0",
2333 		"/pci@1d,700000/scsi@4/sd@8,0",
2334 		"/pci@1d,700000/scsi@4/sd@9,0",
2335 		"/pci@1d,700000/scsi@4/sd@a,0",
2336 		"/pci@1d,700000/scsi@4/sd@b,0"
2337 	};
2338 
2339 	static char *v440_devs[] = {
2340 		"/pci@1f,700000/scsi@2/sd@0,0",
2341 		"/pci@1f,700000/scsi@2/sd@1,0",
2342 		"/pci@1f,700000/scsi@2/sd@2,0",
2343 		"/pci@1f,700000/scsi@2/sd@3,0"
2344 	};
2345 
2346 	static char *n210_devs[] = {
2347 		"/pci@1c,600000/LSILogic,sas@1/sd@0,0",
2348 		"/pci@1c,600000/LSILogic,sas@1/sd@1,0"
2349 	};
2350 
2351 	static char *seattle_devs[] = {
2352 		"/pci@1e,600000/pci@0/pci@a/pci@0/pci@8/scsi@1/sd@0,0",
2353 		"/pci@1e,600000/pci@0/pci@a/pci@0/pci@8/scsi@1/sd@1,0",
2354 		"/pci@1e,600000/pci@0/pci@a/pci@0/pci@8/scsi@1/sd@2,0",
2355 		"/pci@1e,600000/pci@0/pci@a/pci@0/pci@8/scsi@1/sd@3,0"
2356 	};
2357 
2358 	static char *boston_devs_1068e[] = {
2359 		"/pci@1e,600000/pci@0/pci@2/scsi@0/sd@0,0",
2360 		"/pci@1e,600000/pci@0/pci@2/scsi@0/sd@1,0",
2361 		"/pci@1e,600000/pci@0/pci@2/scsi@0/sd@2,0",
2362 		"/pci@1e,600000/pci@0/pci@2/scsi@0/sd@3,0",
2363 		"/pci@1e,600000/pci@0/pci@2/scsi@0/sd@4,0",
2364 		"/pci@1e,600000/pci@0/pci@2/scsi@0/sd@5,0",
2365 		"/pci@1e,600000/pci@0/pci@2/scsi@0/sd@6,0",
2366 		"/pci@1e,600000/pci@0/pci@2/scsi@0/sd@7,0"
2367 	};
2368 	static char *boston_devs_1068x[] = {
2369 		"/pci@1f,700000/pci@0/pci@2/pci@0/pci@8/LSILogic,sas@1/sd@0,0",
2370 		"/pci@1f,700000/pci@0/pci@2/pci@0/pci@8/LSILogic,sas@1/sd@1,0",
2371 		"/pci@1f,700000/pci@0/pci@2/pci@0/pci@8/LSILogic,sas@1/sd@2,0",
2372 		"/pci@1f,700000/pci@0/pci@2/pci@0/pci@8/LSILogic,sas@1/sd@3,0",
2373 		"/pci@1f,700000/pci@0/pci@2/pci@0/pci@8/LSILogic,sas@1/sd@4,0",
2374 		"/pci@1f,700000/pci@0/pci@2/pci@0/pci@8/LSILogic,sas@1/sd@5,0",
2375 		"/pci@1f,700000/pci@0/pci@2/pci@0/pci@8/LSILogic,sas@1/sd@6,0",
2376 		"/pci@1f,700000/pci@0/pci@2/pci@0/pci@8/LSILogic,sas@1/sd@7,0"
2377 	};
2378 
2379 	char	*ddev[N_DISKS];		/* "/devices"  */
2380 	char	*pdev[N_DISKS];		/* "/platform" */
2381 
2382 	switch (sys_platform) {
2383 
2384 	case PLAT_ENTS:
2385 		disk_dev = ents_devs;
2386 		n_disks = N_ENTS_DISKS;
2387 		break;
2388 
2389 	case PLAT_CHALUPA:
2390 	case PLAT_CHALUPA19:
2391 		do_raid = 1;
2392 		disk_dev = v440_devs;
2393 		n_disks = N_CHALUPA_DISKS;
2394 		break;
2395 
2396 	case PLAT_SALSA19:
2397 		disk_dev = n210_devs;
2398 		n_disks = N_EN19_DISKS;
2399 		break;
2400 
2401 	case PLAT_SEATTLE1U:
2402 	case PLAT_SEATTLE2U:
2403 		do_raid = 1;
2404 		disk_dev = seattle_devs;
2405 		n_disks = (sys_platform == PLAT_SEATTLE1U) ?
2406 			N_SEATTLE1U_DISKS : N_SEATTLE2U_DISKS;
2407 		break;
2408 
2409 	case PLAT_BOSTON:
2410 		/*
2411 		 * If we can open the devctl path for the built-in 1068E
2412 		 * controller then assume we're a 1068E-equipped Boston
2413 		 * and make all the paths appropriate for that hardware.
2414 		 * Otherwise assume we are a 1068X-equipped Boston and
2415 		 * make all of the paths appropriate for a 1068X PCI-X
2416 		 * controller in PCI slot 4.
2417 		 *
2418 		 * The flag is also used in the check_raid() function.
2419 		 */
2420 		if ((fd = open(BOSTON_DISK_DEVCTL_1068E, O_RDONLY)) != -1) {
2421 			boston_1068e_flag = 1;
2422 			disk_dev = boston_devs_1068e;
2423 		} else {
2424 			boston_1068e_flag = 0;
2425 			disk_dev = boston_devs_1068x;
2426 		}
2427 		(void) close(fd);
2428 		do_raid = 1;
2429 		n_disks = N_BOSTON_DISKS;
2430 		break;
2431 
2432 	default: /* PLAT_ENXS/PLAT_EN19 */
2433 		disk_dev = mpxu_devs;
2434 		n_disks = (sys_platform == PLAT_EN19) ?
2435 			N_EN19_DISKS : N_MPXU_DISKS;
2436 	}
2437 
2438 	/*
2439 	 * make up disk names
2440 	 */
2441 
2442 	for (i = 0; i < n_disks; i++) {
2443 		char buffer[MAXPATHLEN];
2444 		sprintf(buffer, "/devices%s", disk_dev[i]);
2445 		ddev[i] = strdup(buffer);
2446 		sprintf(buffer, "/platform%s", disk_dev[i]);
2447 		pdev[i] = strdup(buffer);
2448 	}
2449 
2450 	disk_leds_thread_running = B_TRUE;
2451 
2452 	for (;;) {
2453 		for (i = 0; i < n_disks; i++) {
2454 			/*
2455 			 * If it's one of the RAID disks, we have already
2456 			 * applied the ok2remove policy.
2457 			 */
2458 			if (do_raid && check_raid(i))	{
2459 				continue;
2460 			}
2461 
2462 			dhdl = devctl_device_acquire(ddev[i], 0);
2463 			devctl_device_getstate(dhdl, &statep);
2464 			devctl_release(dhdl);
2465 
2466 			if (statep & DEVICE_OFFLINE) {
2467 				if (disk_ready[i] != B_FALSE) {
2468 					update_disk_node(disk_name[i], pdev[i]);
2469 					if (set_led(disk_name[i], REMOK_LED,
2470 					    PICL_PROPVAL_ON) == PICL_SUCCESS)
2471 						disk_ready[i] = B_FALSE;
2472 				}
2473 			} else if (statep & DEVICE_ONLINE) {
2474 				if (disk_ready[i] != B_TRUE) {
2475 					update_disk_node(disk_name[i], pdev[i]);
2476 					if (set_led(disk_name[i], REMOK_LED,
2477 					    PICL_PROPVAL_OFF) == PICL_SUCCESS)
2478 						disk_ready[i] = B_TRUE;
2479 				}
2480 			}
2481 		}
2482 
2483 		/*
2484 		 * wait a bit until we check again
2485 		 */
2486 
2487 		(void) poll(NULL, 0, DISK_POLL_TIME);
2488 
2489 		/*
2490 		 * are we to stop?
2491 		 */
2492 
2493 		(void) pthread_mutex_lock(&g_mutex);
2494 
2495 		while (g_finish_now) {
2496 			/*
2497 			 * notify _fini routine that we've paused
2498 			 */
2499 			disk_leds_thread_ack = B_TRUE;
2500 			(void) pthread_cond_signal(&g_cv_ack);
2501 
2502 			/*
2503 			 * and go to sleep in case we get restarted
2504 			 */
2505 			(void) pthread_cond_wait(&g_cv, &g_mutex);
2506 		}
2507 		(void) pthread_mutex_unlock(&g_mutex);
2508 	}
2509 
2510 	return ((void *)err);
2511 }
2512 
2513 /*
2514  * Given the powersupply name, convert to addr
2515  */
2516 static int
2517 ps_name_to_addr(char *name)
2518 {
2519 	int ps_addr = 0;
2520 	if ((strcmp(name, PS0_NAME) == 0) ||
2521 		(strcmp(name, PSU0_NAME) == 0))	{
2522 		switch (sys_platform) {
2523 		case PLAT_SEATTLE1U:
2524 		case PLAT_SEATTLE2U:
2525 			ps_addr = SEATTLE_PS0_ADDR;
2526 			break;
2527 		case PLAT_BOSTON:
2528 			ps_addr = BOSTON_PS0_ADDR;
2529 			break;
2530 		default:
2531 			ps_addr = PS0_ADDR;
2532 			break;
2533 		}
2534 	} else if ((strcmp(name, PS1_NAME) == 0) ||
2535 		(strcmp(name, PSU1_NAME) == 0))	{
2536 		switch (sys_platform) {
2537 		case PLAT_SEATTLE1U:
2538 		case PLAT_SEATTLE2U:
2539 			ps_addr = SEATTLE_PS1_ADDR;
2540 			break;
2541 		case PLAT_BOSTON:
2542 			ps_addr = BOSTON_PS1_ADDR;
2543 			break;
2544 		default:
2545 			ps_addr = PS1_ADDR;
2546 			break;
2547 		}
2548 	} else if ((strcmp(name, PS2_NAME) == 0) ||
2549 		(strcmp(name, PSU2_NAME) == 0))	{
2550 		switch (sys_platform) {
2551 		case PLAT_BOSTON:
2552 			ps_addr = BOSTON_PS2_ADDR;
2553 			break;
2554 		default:
2555 			ps_addr = PS2_ADDR;
2556 			break;
2557 		}
2558 	} else if ((strcmp(name, PS3_NAME) == 0) ||
2559 		(strcmp(name, PSU3_NAME) == 0))	{
2560 		switch (sys_platform) {
2561 		case PLAT_BOSTON:
2562 			ps_addr = BOSTON_PS3_ADDR;
2563 			break;
2564 		default:
2565 			ps_addr = PS3_ADDR;
2566 			break;
2567 		}
2568 	}
2569 
2570 	return (ps_addr);
2571 }
2572 
2573 /*
2574  * Given powersupply name, convert to unit addr
2575  */
2576 static char *
2577 ps_name_to_unitaddr(char *name)
2578 {
2579 	char *unitaddr;
2580 
2581 	if (strcmp(name, PS0_NAME) == 0)	{
2582 		switch (sys_platform) {
2583 		case PLAT_SEATTLE1U:
2584 		case PLAT_SEATTLE2U:
2585 			unitaddr = SEATTLE_PS0_UNITADDR;
2586 			break;
2587 		case PLAT_BOSTON:
2588 			unitaddr = BOSTON_PS0_UNITADDR;
2589 			break;
2590 		default:
2591 			unitaddr = PS0_UNITADDR;
2592 			break;
2593 		}
2594 	} else if (strcmp(name, PS1_NAME) == 0)	{
2595 		switch (sys_platform) {
2596 		case PLAT_SEATTLE1U:
2597 		case PLAT_SEATTLE2U:
2598 			unitaddr = SEATTLE_PS1_UNITADDR;
2599 			break;
2600 		case PLAT_BOSTON:
2601 			unitaddr = BOSTON_PS1_UNITADDR;
2602 			break;
2603 		default:
2604 			unitaddr = PS1_UNITADDR;
2605 			break;
2606 		}
2607 	} else if (strcmp(name, PS2_NAME) == 0)	{
2608 		switch (sys_platform) {
2609 		case PLAT_BOSTON:
2610 			unitaddr = BOSTON_PS2_UNITADDR;
2611 			break;
2612 		default:
2613 			unitaddr = PS2_UNITADDR;
2614 			break;
2615 		}
2616 	} else if (strcmp(name, PS3_NAME) == 0)	{
2617 		switch (sys_platform) {
2618 		case PLAT_BOSTON:
2619 			unitaddr = BOSTON_PS3_UNITADDR;
2620 			break;
2621 		default:
2622 			unitaddr = PS3_UNITADDR;
2623 			break;
2624 		}
2625 	}
2626 	else
2627 		unitaddr = NULL;
2628 
2629 	return (unitaddr);
2630 }
2631 
2632 /*
2633  * converts apid to real FRU name in PICL tree. The
2634  * name of powersupply devices on chalupa19 are
2635  * PSU instead of PS
2636  */
2637 static char *
2638 ps_apid_to_nodename(char *apid)
2639 {
2640 	char *nodename;
2641 
2642 	if (sys_platform != PLAT_CHALUPA19)
2643 		return (apid);
2644 
2645 	if (strcmp(apid, PS0_NAME) == 0)
2646 		nodename = PSU0_NAME;
2647 	else if (strcmp(apid, PS1_NAME) == 0)
2648 		nodename = PSU1_NAME;
2649 	else if (strcmp(apid, PS2_NAME) == 0)
2650 		nodename = PSU2_NAME;
2651 	else if (strcmp(apid, PS3_NAME) == 0)
2652 		nodename = PSU3_NAME;
2653 	else
2654 		nodename = apid;
2655 
2656 	return (nodename);
2657 }
2658 
2659 /*
2660  * Create SEEPROM node at insertion time.
2661  */
2662 static int
2663 create_i2c_node(char *ap_id)
2664 {
2665 	int	nd_reg[2];
2666 	devctl_ddef_t	ddef_hdl;
2667 	devctl_hdl_t	bus_hdl;
2668 	devctl_hdl_t	dev_hdl;
2669 	char		dev_path[MAXPATHLEN];
2670 	char		*compatible;
2671 
2672 	/* create seeprom node */
2673 	nd_reg[0] = 0;
2674 	nd_reg[1] = ps_name_to_addr(ap_id);
2675 
2676 	switch (sys_platform) {
2677 	case PLAT_SEATTLE1U:
2678 	case PLAT_SEATTLE2U:
2679 		bus_hdl = devctl_bus_acquire(SEATTLE_PSU_I2C_BUS_DEV, 0);
2680 		compatible = SEATTLE_PSU_COMPATIBLE;
2681 		break;
2682 	case PLAT_BOSTON:
2683 		bus_hdl = devctl_bus_acquire(BOSTON_PSU_I2C_BUS_DEV, 0);
2684 		compatible = BOSTON_PSU_COMPATIBLE;
2685 		break;
2686 	default:
2687 		bus_hdl = devctl_bus_acquire(PSU_I2C_BUS_DEV, 0);
2688 		compatible = PSU_COMPATIBLE;
2689 		break;
2690 	}
2691 
2692 	if (bus_hdl == NULL)
2693 		return (DDI_FAILURE);
2694 
2695 	/* device definition properties */
2696 	ddef_hdl = devctl_ddef_alloc(PS_DEVICE_NAME, 0);
2697 	(void) devctl_ddef_string(ddef_hdl, "compatible", compatible);
2698 	(void) devctl_ddef_string(ddef_hdl, "device_type", "seeprom");
2699 	(void) devctl_ddef_int_array(ddef_hdl, "reg", 2, nd_reg);
2700 
2701 	/* create the device node */
2702 	if (devctl_bus_dev_create(bus_hdl, ddef_hdl, 0, &dev_hdl))
2703 		return (DDI_FAILURE);
2704 
2705 	if (devctl_get_pathname(dev_hdl, dev_path, MAXPATHLEN) == NULL)
2706 		return (DDI_FAILURE);
2707 
2708 	devctl_release(dev_hdl);
2709 	devctl_ddef_free(ddef_hdl);
2710 	devctl_release(bus_hdl);
2711 	return (DDI_SUCCESS);
2712 }
2713 
2714 /*
2715  * Delete SEEPROM node at insertion time.
2716  */
2717 static void
2718 delete_i2c_node(char *ap_id)
2719 {
2720 	devctl_hdl_t	dev_hdl;
2721 	char	buf[MAXPATHLEN];
2722 
2723 	switch (sys_platform) {
2724 	case PLAT_SEATTLE1U:
2725 	case PLAT_SEATTLE2U:
2726 		sprintf_buf2(buf, SEATTLE_PSU_DEV, ps_name_to_addr(ap_id));
2727 		break;
2728 	case PLAT_BOSTON:
2729 		sprintf_buf2(buf, BOSTON_PSU_DEV, ps_name_to_addr(ap_id));
2730 		break;
2731 	default:
2732 		sprintf_buf2(buf, PSU_DEV, ps_name_to_addr(ap_id));
2733 		break;
2734 	}
2735 
2736 	dev_hdl = devctl_device_acquire(buf, 0);
2737 	if (dev_hdl == NULL) {
2738 		return;
2739 	}
2740 
2741 	/*
2742 	 * If the seeprom driver is not loaded, calls to
2743 	 * devctl_device_remove fails for seeprom devices
2744 	 */
2745 	if (devctl_device_remove(dev_hdl)) {
2746 		di_init_driver(SEEPROM_DRIVER_NAME, 0);
2747 		devctl_device_remove(dev_hdl);
2748 	}
2749 	devctl_release(dev_hdl);
2750 }
2751 
2752 static void
2753 add_op_status(envmon_hpu_t *hpu, int *index)
2754 {
2755 	boolean_t		rmc_flag;
2756 	boolean_t		ps_flag;
2757 	boolean_t		disk_flag;
2758 	char			node_name[MAXPATHLEN];
2759 	boolean_t		flag;
2760 
2761 	rmc_flag = (strcmp(hpu->id.name, RMC_NAME) == 0);
2762 	ps_flag = (strncmp(hpu->id.name, PS_NAME,
2763 		PS_NAME_LEN) == 0);
2764 	disk_flag = (strncmp(hpu->id.name, DISK_NAME,
2765 		DISK_NAME_LEN) == 0);
2766 	if (rmc_flag || ps_flag) {
2767 		idprop->idp[*index].envhandle = hpu->id;
2768 		flag = rmc_flag && ((sys_platform != PLAT_CHALUPA) &&
2769 			(sys_platform != PLAT_CHALUPA19));
2770 		sprintf_buf2(node_name,
2771 			flag ? SYS_BOARD_PATH : CHASSIS_LOC_PATH, ps_flag ?
2772 			ps_apid_to_nodename(hpu->id.name) : hpu->id.name);
2773 
2774 		add_op_status_by_name(node_name, ps_flag ? PS_FRU_NAME : NULL,
2775 			&idprop->idp[(*index)++].volprop);
2776 	} else if (disk_flag)	{
2777 		idprop->idp[*index].envhandle = hpu->id;
2778 		switch (sys_platform)	{
2779 		case PLAT_CHALUPA:
2780 		case PLAT_CHALUPA19:
2781 			sprintf_buf2(node_name, CHASSIS_LOC_PATH, hpu->id.name);
2782 			break;
2783 		case PLAT_SEATTLE1U:
2784 			sprintf_buf2(node_name, SEATTLE1U_HDDBP_PATH, \
2785 				hpu->id.name);
2786 			break;
2787 		case PLAT_SEATTLE2U:
2788 			sprintf_buf2(node_name, SEATTLE2U_HDDBP_PATH, \
2789 				hpu->id.name);
2790 			break;
2791 		case PLAT_BOSTON:
2792 			sprintf_buf2(node_name, BOSTON_HDDBP_PATH, \
2793 				hpu->id.name);
2794 			break;
2795 		default:
2796 			sprintf_buf2(node_name, SYS_BOARD_PATH, hpu->id.name);
2797 			break;
2798 		}
2799 		add_op_status_by_name(node_name, DISK_FRU_NAME,
2800 			&idprop->idp[(*index)++].volprop);
2801 	}
2802 }
2803