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