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