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