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