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