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