xref: /titanic_44/usr/src/uts/sun4u/io/rmclomv.c (revision 4bff34e37def8a90f9194d81bc345c52ba20086a)
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 2007 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 <sys/types.h>
30 #include <sys/stat.h>
31 #include <sys/conf.h>
32 #include <sys/modctl.h>
33 #include <sys/callb.h>
34 #include <sys/strlog.h>
35 #include <sys/cyclic.h>
36 #include <sys/rmc_comm_dp.h>
37 #include <sys/rmc_comm_dp_boot.h>
38 #include <sys/rmc_comm_drvintf.h>
39 #include <sys/rmc_comm.h>
40 #include <sys/machsystm.h>
41 #include <sys/sysevent.h>
42 #include <sys/sysevent/dr.h>
43 #include <sys/sysevent/env.h>
44 #include <sys/sysevent/eventdefs.h>
45 #include <sys/file.h>
46 #include <sys/disp.h>
47 #include <sys/reboot.h>
48 #include <sys/envmon.h>
49 #include <sys/rmclomv_impl.h>
50 #include <sys/cpu_sgnblk_defs.h>
51 #include <sys/utsname.h>
52 #include <sys/systeminfo.h>
53 #include <sys/ddi.h>
54 #include <sys/time.h>
55 #include <sys/promif.h>
56 
57 #define	offsetof(s, m)	(size_t)(&(((s *)0)->m))
58 #define	RMCRESBUFLEN	1024
59 #define	DATE_TIME_MSG_SIZE	78
60 #define	RMCLOMV_WATCHDOG_MODE	"rmclomv-watchdog-mode"
61 #define	DELAY_TIME	5000000	 /* 5 seconds, in microseconds */
62 #define	CPU_SIGNATURE_DELAY_TIME	5000000	 /* 5 secs, in microsecs */
63 
64 extern void	pmugpio_watchdog_pat();
65 static clock_t	timesync_interval;
66 
67 extern int	watchdog_activated;
68 static int	last_watchdog_msg = 1;
69 extern int	watchdog_enable;
70 extern int	boothowto;
71 
72 int		rmclomv_watchdog_mode;
73 
74 /*
75  * functions local to this driver.
76  */
77 static int	rmclomv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg,
78     void **resultp);
79 static int	rmclomv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
80 static int	rmclomv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
81 static uint_t	rmclomv_break_intr(caddr_t arg);
82 static int	rmclomv_add_intr_handlers(void);
83 static int	rmclomv_remove_intr_handlers(void);
84 static uint_t	rmclomv_event_data_handler(char *);
85 static void	rmclomv_dr_data_handler(const char *, int);
86 static int	rmclomv_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p);
87 static int	rmclomv_close(dev_t dev, int flag, int otyp, cred_t *cred_p);
88 static int	rmclomv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
89     cred_t *cred_p, int *rval_p);
90 static void	rmclomv_checkrmc_start(void);
91 static void	rmclomv_checkrmc_destroy(void);
92 static void	rmclomv_checkrmc_wakeup(void *);
93 static void	rmclomv_refresh_start(void);
94 static void	rmclomv_refresh_destroy(void);
95 static void	rmclomv_refresh_wakeup(void);
96 static void	rmclomv_reset_cache(rmclomv_cache_section_t *new_chain,
97     rmclomv_cache_section_t *new_subchain, dp_get_sysinfo_r_t *sysinfo);
98 static rmclomv_cache_section_t *rmclomv_find_section(
99     rmclomv_cache_section_t *start, uint16_t sensor);
100 static rmclomv_cache_section_t *create_cache_section(int sensor_type, int num);
101 static int	get_sensor_by_name(const rmclomv_cache_section_t *section,
102     const char *name, int *index);
103 static int	validate_section_entry(rmclomv_cache_section_t *section,
104     int index);
105 static int	add_names_to_section(rmclomv_cache_section_t *section);
106 static void	free_section(rmclomv_cache_section_t *section);
107 static void	add_section(rmclomv_cache_section_t **head,
108     rmclomv_cache_section_t *section);
109 static int	rmclomv_do_cmd(int req_cmd, int resp_cmd, int resp_len,
110     intptr_t arg_req, intptr_t arg_res);
111 static void	refresh_name_cache(int force_fail);
112 static void	set_val_unav(envmon_sensor_t *sensor);
113 static void	set_fan_unav(envmon_fan_t *fan);
114 static int	do_psu_cmd(intptr_t arg, int mode, envmon_indicator_t *env_ind,
115     dp_get_psu_status_t *rmc_psu, dp_get_psu_status_r_t *rmc_psu_r,
116     int detector_type);
117 static uint_t rmc_set_watchdog_timer(uint_t timeoutval);
118 static uint_t rmc_clear_watchdog_timer(void);
119 static void send_watchdog_msg(int msg);
120 static void plat_timesync(void *arg);
121 
122 /*
123  * Driver entry points
124  */
125 static struct cb_ops rmclomv_cb_ops = {
126 	rmclomv_open,	/* open */
127 	rmclomv_close,	/* close */
128 	nodev,		/* strategy() */
129 	nodev,		/* print() */
130 	nodev,		/* dump() */
131 	nodev,		/* read() */
132 	nodev,		/* write() */
133 	rmclomv_ioctl,	/* ioctl() */
134 	nodev,		/* devmap() */
135 	nodev,		/* mmap() */
136 	ddi_segmap,	/* segmap() */
137 	nochpoll,	/* poll() */
138 	ddi_prop_op,    /* prop_op() */
139 	NULL,		/* cb_str */
140 	D_NEW | D_MP	/* cb_flag */
141 };
142 
143 
144 static struct dev_ops rmclomv_ops = {
145 	DEVO_REV,
146 	0,			/* ref count */
147 	rmclomv_getinfo,	/* getinfo() */
148 	nulldev,		/* identify() */
149 	nulldev,		/* probe() */
150 	rmclomv_attach,		/* attach() */
151 	rmclomv_detach,		/* detach */
152 	nodev,			/* reset */
153 	&rmclomv_cb_ops,		/* pointer to cb_ops structure */
154 	(struct bus_ops *)NULL,
155 	nulldev			/* power() */
156 };
157 
158 /*
159  * Loadable module support.
160  */
161 extern struct mod_ops mod_driverops;
162 
163 static struct modldrv modldrv = {
164 	&mod_driverops,			/* Type of module. This is a driver */
165 	"rmclomv control driver v%I%",	/* Name of the module */
166 	&rmclomv_ops			/* pointer to the dev_ops structure */
167 };
168 
169 static struct modlinkage modlinkage = {
170 	MODREV_1,
171 	&modldrv,
172 	NULL
173 };
174 
175 /*
176  * Device info
177  */
178 static dev_info_t		*rmclomv_dip = NULL;
179 static int			rmclomv_break_requested = B_FALSE;
180 static ddi_softintr_t		rmclomv_softintr_id;
181 static ddi_iblock_cookie_t	rmclomv_soft_iblock_cookie;
182 
183 extern void (*abort_seq_handler)();
184 /* key_position is effective key-position. Set to locked if unknown */
185 static rsci8 key_position = RMC_KEYSWITCH_POS_LOCKED;
186 /* real_key_position starts off as unknown and records value actually seen */
187 static rsci8 real_key_position = RMC_KEYSWITCH_POS_UNKNOWN;
188 static void rmclomv_abort_seq_handler(char *msg);
189 
190 /*
191  * mutexes which protect the interrupt handlers.
192  */
193 static kmutex_t		rmclomv_event_hdlr_lock;
194 static kmutex_t		rmclomv_refresh_lock;
195 static kcondvar_t	rmclomv_refresh_sig_cv;
196 static kmutex_t		rmclomv_checkrmc_lock;
197 static kcondvar_t	rmclomv_checkrmc_sig_cv;
198 
199 /*
200  * mutex to protect the handle_name cache
201  */
202 static kmutex_t		rmclomv_cache_lock;
203 
204 /*
205  * mutex to protect the RMC state
206  */
207 static kmutex_t		rmclomv_state_lock;
208 
209 /*
210  * Payloads of the event handlers.
211  */
212 static dp_event_notification_t	rmclomv_event_payload;
213 static rmc_comm_msg_t	rmclomv_event_payload_msg;
214 
215 /*
216  * Checkrmc commands..
217  */
218 #define	RMCLOMV_CHECKRMC_EXITNOW	(-1)
219 #define	RMCLOMV_CHECKRMC_WAIT		0
220 #define	RMCLOMV_CHECKRMC_PROCESSNOW	1
221 
222 /*
223  * Checkrmc thread state
224  */
225 static int rmclomv_checkrmc_sig = RMCLOMV_CHECKRMC_WAIT;
226 static kt_did_t rmclomv_checkrmc_tid = 0;
227 
228 /*
229  * RMC state data
230  */
231 #define	RMCLOMV_RMCSTATE_UNKNOWN	0
232 #define	RMCLOMV_RMCSTATE_OK		1
233 #define	RMCLOMV_RMCSTATE_FAILED		2
234 #define	RMCLOMV_RMCSTATE_DOWNLOAD	3
235 
236 /*
237  * RMC error indicator values (status from last RMC command)
238  */
239 #define	RMCLOMV_RMCERROR_NONE		0
240 
241 /* fail RMC after 5 minutes without a good response */
242 #define	RMCLOMV_RMCFAILTHRESHOLD	5
243 
244 /*
245  * rmclomv_rmc_state is the state reported in OperationalStatus.
246  * rmclomv_rmc_error reflects the result of the last RMC interaction.
247  * rmclomv_rmcfailcount is used by the rmclomv_checkrmc thread to count
248  * failures in its regular status polls. Once RMCLOMV_RMCFAILTHRESHOLD
249  * is reached, rmclomv_rmc_state is marked as RMCLOMV_RMCSTATE_FAILED.
250  */
251 static int	rmclomv_rmc_state = RMCLOMV_RMCSTATE_UNKNOWN;
252 static int	rmclomv_rmc_error = RMCLOMV_RMCERROR_NONE;
253 static int	rmclomv_rmcfailcount;
254 
255 /*
256  * Refresh commands..
257  */
258 #define	RMCLOMV_REFRESH_EXITNOW		(-1)
259 #define	RMCLOMV_REFRESH_WAIT		0
260 #define	RMCLOMV_REFRESH_PROCESSNOW	1
261 
262 /*
263  * Refresh thread state
264  */
265 static int rmclomv_refresh_sig = RMCLOMV_REFRESH_WAIT;
266 static kt_did_t rmclomv_refresh_tid = 0;
267 
268 /*
269  * timeout id
270  */
271 static timeout_id_t	timer_id;
272 
273 /*
274  * Handle-name cache
275  */
276 #define	LOCK_CACHE		mutex_enter(&rmclomv_cache_lock);
277 #define	RELEASE_CACHE		mutex_exit(&rmclomv_cache_lock);
278 static rmclomv_cache_section_t	*rmclomv_cache;		/* main handle-names */
279 static rmclomv_cache_section_t	*rmclomv_subcache;	/* derived names */
280 static dp_get_sysinfo_r_t	rmclomv_sysinfo_data;
281 static boolean_t		rmclomv_sysinfo_valid;
282 static int			rmclomv_cache_valid;
283 
284 extern pri_t maxclsyspri;
285 
286 /*
287  * static strings
288  */
289 static const char	str_percent[]		= "%";
290 static const char	str_rpm[]		= " rpm";
291 static const char	str_ip_volts_ind[]	= "P_PWR";
292 static const char	str_ip2_volts_ind[]	= "P_PWR2";
293 static const char	str_ff_pok_ind[]	= "FF_POK";
294 static const char	str_vlo_volts_ind[]	= "FF_UV";
295 static const char	str_vhi_volts_ind[]	= "FF_OV";
296 static const char	str_chi_amps_ind[]	= "FF_OC";
297 static const char	str_chi_nr_ind[]	= "FF_NR";
298 static const char	str_ot_tmpr_ind[]	= "FF_OT";
299 static const char	str_fan_ind[]		= "FF_FAN";
300 static const char	str_pdct_fan_ind[]	= "FF_PDCT_FAN";
301 static const char	str_sc[]		= "SC";
302 
303 int
304 _init(void)
305 {
306 	int	error = 0;
307 
308 	mutex_init(&rmclomv_event_hdlr_lock, NULL, MUTEX_DEFAULT, NULL);
309 	mutex_init(&rmclomv_checkrmc_lock, NULL, MUTEX_DRIVER, NULL);
310 	mutex_init(&rmclomv_refresh_lock, NULL, MUTEX_DRIVER, NULL);
311 	mutex_init(&rmclomv_cache_lock, NULL, MUTEX_DRIVER, NULL);
312 	mutex_init(&rmclomv_state_lock, NULL, MUTEX_DRIVER, NULL);
313 	cv_init(&rmclomv_checkrmc_sig_cv, NULL, CV_DRIVER, NULL);
314 	cv_init(&rmclomv_refresh_sig_cv, NULL, CV_DRIVER, NULL);
315 
316 	error = mod_install(&modlinkage);
317 	if (error) {
318 		cv_destroy(&rmclomv_refresh_sig_cv);
319 		cv_destroy(&rmclomv_checkrmc_sig_cv);
320 		mutex_destroy(&rmclomv_state_lock);
321 		mutex_destroy(&rmclomv_cache_lock);
322 		mutex_destroy(&rmclomv_refresh_lock);
323 		mutex_destroy(&rmclomv_checkrmc_lock);
324 		mutex_destroy(&rmclomv_event_hdlr_lock);
325 	}
326 	return (error);
327 }
328 
329 
330 int
331 _info(struct modinfo *modinfop)
332 {
333 	return (mod_info(&modlinkage, modinfop));
334 }
335 
336 
337 int
338 _fini(void)
339 {
340 	int	error = 0;
341 
342 	error = mod_remove(&modlinkage);
343 	if (error)
344 		return (error);
345 	cv_destroy(&rmclomv_refresh_sig_cv);
346 	cv_destroy(&rmclomv_checkrmc_sig_cv);
347 	mutex_destroy(&rmclomv_state_lock);
348 	mutex_destroy(&rmclomv_cache_lock);
349 	mutex_destroy(&rmclomv_refresh_lock);
350 	mutex_destroy(&rmclomv_checkrmc_lock);
351 	mutex_destroy(&rmclomv_event_hdlr_lock);
352 	return (error);
353 }
354 
355 
356 /* ARGSUSED */
357 static int
358 rmclomv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
359 {
360 	minor_t m = getminor((dev_t)arg);
361 
362 	switch (cmd) {
363 	case DDI_INFO_DEVT2DEVINFO:
364 		if ((m != 0) || (rmclomv_dip == NULL)) {
365 			*resultp = NULL;
366 			return (DDI_FAILURE);
367 		}
368 		*resultp = rmclomv_dip;
369 		return (DDI_SUCCESS);
370 	case DDI_INFO_DEVT2INSTANCE:
371 		*resultp = (void *)(uintptr_t)m;
372 		return (DDI_SUCCESS);
373 	default:
374 		return (DDI_FAILURE);
375 	}
376 }
377 
378 
379 static int
380 rmclomv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
381 {
382 	int			instance;
383 	int			err;
384 	char			*wdog_state;
385 	int			attaching = 1;
386 
387 	switch (cmd) {
388 	case DDI_ATTACH:
389 		/*
390 		 * only allow one instance
391 		 */
392 		instance = ddi_get_instance(dip);
393 		if (instance != 0)
394 			return (DDI_FAILURE);
395 
396 		err = ddi_create_minor_node(dip, "rmclomv", S_IFCHR,
397 		    instance, DDI_PSEUDO, NULL);
398 		if (err != DDI_SUCCESS)
399 			return (DDI_FAILURE);
400 
401 		/*
402 		 * Register with rmc_comm to prevent it being detached
403 		 * (in the unlikely event that its attach succeeded on a
404 		 * platform whose platmod doesn't lock it down).
405 		 */
406 		err = rmc_comm_register();
407 		if (err != DDI_SUCCESS) {
408 			ddi_remove_minor_node(dip, NULL);
409 			return (DDI_FAILURE);
410 		}
411 
412 		/* Remember the dev info */
413 		rmclomv_dip = dip;
414 
415 		/*
416 		 * Add the handlers which watch for unsolicited messages
417 		 * and post event to Sysevent Framework.
418 		 */
419 		err = rmclomv_add_intr_handlers();
420 		if (err != DDI_SUCCESS) {
421 			rmc_comm_unregister();
422 			ddi_remove_minor_node(dip, NULL);
423 			rmclomv_dip = NULL;
424 			return (DDI_FAILURE);
425 		}
426 
427 		rmclomv_checkrmc_start();
428 		rmclomv_refresh_start();
429 
430 		abort_seq_handler = rmclomv_abort_seq_handler;
431 		ddi_report_dev(dip);
432 
433 		/*
434 		 * Check whether we have an application watchdog
435 		 */
436 		if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip,
437 		    DDI_PROP_DONTPASS, RMCLOMV_WATCHDOG_MODE,
438 		    &wdog_state) == DDI_PROP_SUCCESS) {
439 			if (strcmp(wdog_state, "app") == 0) {
440 				rmclomv_watchdog_mode = 1;
441 				watchdog_enable = 0;
442 			}
443 			else
444 				rmclomv_watchdog_mode = 0;
445 			ddi_prop_free(wdog_state);
446 		}
447 
448 		tod_ops.tod_set_watchdog_timer = rmc_set_watchdog_timer;
449 		tod_ops.tod_clear_watchdog_timer = rmc_clear_watchdog_timer;
450 
451 		/*
452 		 * Now is a good time to activate hardware watchdog
453 		 * (if one exists).
454 		 */
455 		mutex_enter(&tod_lock);
456 		if (watchdog_enable && tod_ops.tod_set_watchdog_timer != NULL)
457 			err = tod_ops.tod_set_watchdog_timer(0);
458 		mutex_exit(&tod_lock);
459 		if (err != 0)
460 			printf("Hardware watchdog enabled\n");
461 
462 		/*
463 		 * Set time interval and start timesync routine.
464 		 * Also just this once set the Solaris clock
465 		 * to the RMC clock.
466 		 */
467 		timesync_interval = drv_usectohz(5*60 * MICROSEC);
468 		plat_timesync((void *) &attaching);
469 
470 		return (DDI_SUCCESS);
471 	case DDI_RESUME:
472 		return (DDI_SUCCESS);
473 	default:
474 		return (DDI_FAILURE);
475 	}
476 }
477 
478 
479 static int
480 rmclomv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
481 {
482 	int	instance;
483 	int	err;
484 
485 	switch (cmd) {
486 	case DDI_DETACH:
487 		instance = ddi_get_instance(dip);
488 		if (instance != 0)
489 			return (DDI_FAILURE);
490 
491 		/*
492 		 * Remove the handlers which watch for unsolicited messages
493 		 * and post event to Sysevent Framework.
494 		 */
495 		err = rmclomv_remove_intr_handlers();
496 		if (err != DDI_SUCCESS) {
497 			cmn_err(CE_WARN, "Failed to remove event handlers");
498 			return (DDI_FAILURE);
499 		}
500 		rmclomv_checkrmc_destroy();
501 		rmclomv_refresh_destroy();
502 		rmclomv_reset_cache(NULL, NULL, NULL);
503 		ddi_remove_minor_node(dip, NULL);
504 
505 		/* Forget the dev info */
506 		rmclomv_dip = NULL;
507 		rmc_comm_unregister();
508 		return (DDI_SUCCESS);
509 	case DDI_SUSPEND:
510 		return (DDI_SUCCESS);
511 	default:
512 		return (DDI_FAILURE);
513 	}
514 }
515 
516 static int
517 rmclomv_add_intr_handlers()
518 {
519 	int	err;
520 
521 	if (ddi_get_soft_iblock_cookie(rmclomv_dip, DDI_SOFTINT_HIGH,
522 	    &rmclomv_soft_iblock_cookie) != DDI_SUCCESS) {
523 		return (DDI_FAILURE);
524 	}
525 	err = ddi_add_softintr(rmclomv_dip, DDI_SOFTINT_HIGH,
526 	    &rmclomv_softintr_id, &rmclomv_soft_iblock_cookie, NULL,
527 	    rmclomv_break_intr, NULL);
528 	if (err != DDI_SUCCESS)
529 		return (DDI_FAILURE);
530 	rmclomv_event_payload_msg.msg_buf = (caddr_t)&rmclomv_event_payload;
531 	rmclomv_event_payload_msg.msg_len = sizeof (rmclomv_event_payload);
532 	err = rmc_comm_reg_intr(DP_RMC_EVENTS, rmclomv_event_data_handler,
533 	    &rmclomv_event_payload_msg, NULL, &rmclomv_event_hdlr_lock);
534 	if (err != 0) {
535 		ddi_remove_softintr(rmclomv_softintr_id);
536 		return (DDI_FAILURE);
537 	}
538 	return (DDI_SUCCESS);
539 }
540 
541 static int
542 rmclomv_remove_intr_handlers(void)
543 {
544 	int err = rmc_comm_unreg_intr(DP_RMC_EVENTS,
545 	    rmclomv_event_data_handler);
546 	if (err != 0) {
547 		cmn_err(CE_WARN, "Failed to unregister DP_RMC_EVENTS "
548 		    "handler. Err=%d", err);
549 		return (DDI_FAILURE);
550 	}
551 	ddi_remove_softintr(rmclomv_softintr_id);
552 	return (DDI_SUCCESS);
553 }
554 
555 static void
556 rmclomv_abort_seq_handler(char *msg)
557 {
558 	if (key_position == RMC_KEYSWITCH_POS_LOCKED)
559 		cmn_err(CE_CONT, "KEY in LOCKED position, "
560 		    "ignoring debug enter sequence");
561 	else  {
562 		rmclomv_break_requested = B_TRUE;
563 		if (msg != NULL)
564 			prom_printf("%s\n", msg);
565 
566 		ddi_trigger_softintr(rmclomv_softintr_id);
567 	}
568 }
569 
570 /* ARGSUSED */
571 static uint_t
572 rmclomv_break_intr(caddr_t arg)
573 {
574 	if (rmclomv_break_requested) {
575 		rmclomv_break_requested = B_FALSE;
576 		debug_enter(NULL);
577 		return (DDI_INTR_CLAIMED);
578 	}
579 
580 	return (DDI_INTR_UNCLAIMED);
581 }
582 
583 /*
584  * Create a cache section structure
585  */
586 static rmclomv_cache_section_t *
587 create_cache_section(int sensor_type, int num)
588 {
589 	size_t len = offsetof(rmclomv_cache_section_t, entry[0]) +
590 	    num * sizeof (rmclomv_cache_entry_t);
591 	rmclomv_cache_section_t *ptr = kmem_zalloc(len, KM_SLEEP);
592 	ptr->next_section = NULL;
593 	ptr->sensor_type = sensor_type;
594 	ptr->num_entries = num;
595 	ptr->section_len = len;
596 	return (ptr);
597 }
598 
599 /*
600  * Free a cache_section.
601  */
602 static void
603 free_section(rmclomv_cache_section_t *section)
604 {
605 	size_t len = section->section_len;
606 	kmem_free(section, len);
607 }
608 
609 /*
610  * adds supplied section to end of cache chain
611  * must be called with cache locked
612  */
613 static void
614 add_section(rmclomv_cache_section_t **head, rmclomv_cache_section_t *section)
615 {
616 	section->next_section = *head;
617 	*head = section;
618 }
619 
620 /*
621  * This function releases all cache sections and exchanges the two
622  * chain heads for new values.
623  */
624 static void
625 rmclomv_reset_cache(rmclomv_cache_section_t *new_chain,
626     rmclomv_cache_section_t *new_subchain, dp_get_sysinfo_r_t *sysinfo)
627 {
628 	rmclomv_cache_section_t	*first;
629 	rmclomv_cache_section_t	*sub_first;
630 	rmclomv_cache_section_t	*next;
631 
632 	LOCK_CACHE
633 
634 	rmclomv_cache_valid = (new_chain != NULL);
635 	first = rmclomv_cache;
636 	rmclomv_cache = new_chain;
637 	sub_first = rmclomv_subcache;
638 	rmclomv_subcache = new_subchain;
639 
640 	if (sysinfo == NULL)
641 		bzero(&rmclomv_sysinfo_data, sizeof (rmclomv_sysinfo_data));
642 	else
643 		bcopy(sysinfo, &rmclomv_sysinfo_data,
644 		    sizeof (rmclomv_sysinfo_data));
645 
646 	rmclomv_sysinfo_valid = (sysinfo != NULL);
647 
648 	RELEASE_CACHE
649 
650 	while (first != NULL) {
651 		next = first->next_section;
652 		free_section(first);
653 		first = next;
654 	}
655 
656 	while (sub_first != NULL) {
657 		next = sub_first->next_section;
658 		free_section(sub_first);
659 		sub_first = next;
660 	}
661 }
662 
663 /*
664  * cache must be locked before calling rmclomv_find_section
665  */
666 static rmclomv_cache_section_t *
667 rmclomv_find_section(rmclomv_cache_section_t *start, uint16_t sensor)
668 {
669 	rmclomv_cache_section_t	*next = start;
670 
671 	while ((next != NULL) && (next->sensor_type != sensor))
672 		next = next->next_section;
673 
674 	return (next);
675 }
676 
677 /*
678  * Return a string presenting the keyswitch position
679  * For unknown values returns "Unknown"
680  */
681 static char *
682 rmclomv_key_position(enum rmc_keyswitch_pos pos)
683 {
684 	switch (pos) {
685 
686 	case RMC_KEYSWITCH_POS_NORMAL:
687 		return ("NORMAL");
688 	case RMC_KEYSWITCH_POS_DIAG:
689 		return ("DIAG");
690 	case RMC_KEYSWITCH_POS_LOCKED:
691 		return ("LOCKED");
692 	case RMC_KEYSWITCH_POS_OFF:
693 		return ("STBY");
694 	default:
695 		return ("UNKNOWN");
696 	}
697 }
698 
699 /*
700  * The sensor id name is sought in the supplied section and if found
701  * its index within the section is written to *index.
702  * Return value is zero for success, otherwise -1.
703  * The cache must be locked before calling get_sensor_by_name
704  */
705 static int
706 get_sensor_by_name(const rmclomv_cache_section_t *section,
707     const char *name, int *index)
708 {
709 	int i;
710 
711 	for (i = 0; i < section->num_entries; i++) {
712 		if (strcmp(name, section->entry[i].handle_name.name) == 0) {
713 			*index = i;
714 			return (0);
715 		}
716 	}
717 
718 	*index = 0;
719 	return (-1);
720 }
721 
722 /*
723  * fills in the envmon_handle name
724  * if it is unknown (not cached), the dp_handle_t is returned as a hex-digit
725  * string
726  */
727 static void
728 rmclomv_hdl_to_envhdl(dp_handle_t hdl, envmon_handle_t *envhdl)
729 {
730 	rmclomv_cache_section_t *next;
731 	int			i;
732 
733 	LOCK_CACHE
734 
735 	for (next = rmclomv_cache; next != NULL; next = next->next_section) {
736 		for (i = 0; i < next->num_entries; i++) {
737 			if (next->entry[i].handle == hdl) {
738 				*envhdl = next->entry[i].handle_name;
739 					RELEASE_CACHE
740 					return;
741 			}
742 		}
743 	}
744 
745 	/*
746 	 * Sought handle not currently cached.
747 	 */
748 	RELEASE_CACHE
749 
750 	(void) snprintf(envhdl->name, sizeof (envhdl->name),
751 	    "Unknown SC node 0x%x", hdl);
752 }
753 
754 static void
755 rmclomv_dr_data_handler(const char *fru_name, int hint)
756 {
757 	int				err = 0;
758 	nvlist_t			*attr_list;
759 	char				attach_pnt[MAXPATHLEN];
760 
761 	(void) snprintf(attach_pnt, sizeof (attach_pnt), "%s", fru_name);
762 
763 	err = nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE, KM_NOSLEEP);
764 	if (err != 0) {
765 		cmn_err(CE_WARN,
766 		    "Failed to allocate name-value list for %s event", EC_DR);
767 		return;
768 	}
769 
770 	err = nvlist_add_string(attr_list, DR_AP_ID, attach_pnt);
771 	if (err != 0) {
772 		cmn_err(CE_WARN, "Failed to add attr [%s] for %s event",
773 		    DR_AP_ID, EC_DR);
774 		nvlist_free(attr_list);
775 		return;
776 	}
777 
778 	/*
779 	 * Add the hint
780 	 */
781 	err = nvlist_add_string(attr_list, DR_HINT, SE_HINT2STR(hint));
782 	if (err != 0) {
783 		cmn_err(CE_WARN, "Failed to add attr [%s] for %s event",
784 		    DR_HINT, EC_DR);
785 		nvlist_free(attr_list);
786 		return;
787 	}
788 
789 	err = ddi_log_sysevent(rmclomv_dip, DDI_VENDOR_SUNW, EC_DR,
790 	    ESC_DR_AP_STATE_CHANGE, attr_list, NULL, DDI_NOSLEEP);
791 	if (err != 0) {
792 		cmn_err(CE_WARN, "Failed to log %s/%s event",
793 		    DR_AP_ID, EC_DR);
794 	}
795 
796 	nvlist_free(attr_list);
797 }
798 
799 static void
800 fan_sysevent(char *fru_name, char *sensor_name, int sub_event)
801 {
802 	nvlist_t		*attr_list;
803 	char			fan_str[MAXNAMELEN];
804 	int			err;
805 
806 	err = nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE, KM_NOSLEEP);
807 	if (err != 0) {
808 		cmn_err(CE_WARN,
809 		    "Failed to allocate name-value list for %s/%s event",
810 		    EC_ENV, ESC_ENV_FAN);
811 		return;
812 	}
813 
814 	err = nvlist_add_string(attr_list, ENV_FRU_ID, fru_name);
815 	if (err != 0) {
816 		cmn_err(CE_WARN, "Failed to add attr [%s] for %s/%s event",
817 		    ENV_FRU_ID, EC_ENV, ESC_ENV_FAN);
818 		nvlist_free(attr_list);
819 		return;
820 	}
821 
822 	err = nvlist_add_string(attr_list, ENV_FRU_RESOURCE_ID, sensor_name);
823 	if (err != 0) {
824 		cmn_err(CE_WARN, "Failed to add attr [%s] for %s/%s event",
825 		    ENV_FRU_RESOURCE_ID, EC_ENV, ESC_ENV_FAN);
826 		nvlist_free(attr_list);
827 		return;
828 	}
829 
830 	err = nvlist_add_string(attr_list, ENV_FRU_DEVICE, ENV_RESERVED_ATTR);
831 	if (err != 0) {
832 		cmn_err(CE_WARN, "Failed to add attr [%s] for %s/%s event",
833 		    ENV_FRU_DEVICE, EC_ENV, ESC_ENV_FAN);
834 		nvlist_free(attr_list);
835 		return;
836 	}
837 
838 	err = nvlist_add_int32(attr_list, ENV_FRU_STATE,
839 	    (sub_event == RMC_ENV_FAULT_EVENT) ? ENV_FAILED : ENV_OK);
840 	if (err != 0) {
841 		cmn_err(CE_WARN, "Failed to add attr [%s] for %s/%s event",
842 		    ENV_FRU_STATE, EC_ENV, ESC_ENV_FAN);
843 		nvlist_free(attr_list);
844 		return;
845 	}
846 
847 	if (sub_event == RMC_ENV_FAULT_EVENT) {
848 		(void) snprintf(fan_str, sizeof (fan_str),
849 		    "fan %s/%s is now failed", fru_name, sensor_name);
850 	} else {
851 		(void) snprintf(fan_str, sizeof (fan_str),
852 		    "fan %s/%s is now ok", fru_name, sensor_name);
853 	}
854 	err = nvlist_add_string(attr_list, ENV_MSG, fan_str);
855 	if (err != 0) {
856 		cmn_err(CE_WARN, "Failed to add attr [%s] for %s/%s event",
857 		    ENV_MSG, EC_ENV, ESC_ENV_FAN);
858 		nvlist_free(attr_list);
859 		return;
860 	}
861 
862 	err = ddi_log_sysevent(rmclomv_dip, DDI_VENDOR_SUNW, EC_ENV,
863 	    ESC_ENV_FAN, attr_list, NULL, DDI_NOSLEEP);
864 	if (err != 0) {
865 		cmn_err(CE_WARN, "Failed to log %s/%s event",
866 		    EC_ENV, ESC_ENV_FAN);
867 	}
868 
869 	cmn_err(CE_NOTE, "%s", fan_str);
870 	nvlist_free(attr_list);
871 }
872 
873 static void
874 threshold_sysevent(char *fru_name, char *sensor_name, int sub_event,
875 	char event_type)
876 {
877 	nvlist_t		*attr_list;
878 	int			err;
879 	char			*subclass;
880 	char			sensor_str[MAXNAMELEN];
881 
882 	subclass = (event_type == 'T') ? ESC_ENV_TEMP : ESC_ENV_POWER;
883 
884 	err = nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE, KM_NOSLEEP);
885 	if (err != 0) {
886 		cmn_err(CE_WARN,
887 		    "Failed to allocate name-value list for %s/%s event",
888 		    EC_ENV, subclass);
889 		return;
890 	}
891 
892 	err = nvlist_add_string(attr_list, ENV_FRU_ID, fru_name);
893 	if (err != 0) {
894 		cmn_err(CE_WARN, "Failed to add attr [%s] for %s/%s event",
895 		    ENV_FRU_ID, EC_ENV, subclass);
896 		nvlist_free(attr_list);
897 		return;
898 	}
899 
900 	err = nvlist_add_string(attr_list, ENV_FRU_RESOURCE_ID, sensor_name);
901 	if (err != 0) {
902 		cmn_err(CE_WARN, "Failed to add attr [%s] for %s/%s event",
903 		    ENV_FRU_RESOURCE_ID, EC_ENV, subclass);
904 		nvlist_free(attr_list);
905 		return;
906 	}
907 
908 	err = nvlist_add_string(attr_list, ENV_FRU_DEVICE, ENV_RESERVED_ATTR);
909 	if (err != 0) {
910 		cmn_err(CE_WARN, "Failed to add attr [%s] for %s/%s event",
911 		    ENV_FRU_DEVICE, EC_ENV, subclass);
912 		nvlist_free(attr_list);
913 		return;
914 	}
915 
916 	switch (sub_event) {
917 	case RMC_ENV_OK_EVENT:
918 		err = nvlist_add_int32(attr_list, ENV_FRU_STATE, ENV_OK);
919 		break;
920 	case RMC_ENV_WARNING_THRESHOLD_EVENT:
921 		err = nvlist_add_int32(attr_list, ENV_FRU_STATE, ENV_WARNING);
922 		break;
923 	case RMC_ENV_SHUTDOWN_THRESHOLD_EVENT:
924 		err = nvlist_add_int32(attr_list, ENV_FRU_STATE, ENV_FAILED);
925 		break;
926 	}
927 	if (err != 0) {
928 		cmn_err(CE_WARN, "Failed to add attr [%s] for %s/%s event",
929 		    ENV_FRU_STATE, EC_ENV, subclass);
930 		nvlist_free(attr_list);
931 		return;
932 	}
933 
934 	switch (sub_event) {
935 	case RMC_ENV_OK_EVENT:
936 		(void) snprintf(sensor_str, sizeof (sensor_str),
937 		    "sensor %s/%s is now ok", fru_name,
938 		    sensor_name);
939 		break;
940 	case RMC_ENV_WARNING_THRESHOLD_EVENT:
941 		(void) snprintf(sensor_str, sizeof (sensor_str),
942 		    "sensor %s/%s is now outside warning thresholds", fru_name,
943 		    sensor_name);
944 		break;
945 	case RMC_ENV_SHUTDOWN_THRESHOLD_EVENT:
946 		(void) snprintf(sensor_str, sizeof (sensor_str),
947 		    "sensor %s/%s is now outside shutdown thresholds", fru_name,
948 		    sensor_name);
949 		break;
950 	}
951 	err = nvlist_add_string(attr_list, ENV_MSG, sensor_str);
952 	if (err != 0) {
953 		cmn_err(CE_WARN, "Failed to add attr [%s] for %s/%s event",
954 		    ENV_MSG, EC_ENV, subclass);
955 		nvlist_free(attr_list);
956 		return;
957 	}
958 
959 	err = ddi_log_sysevent(rmclomv_dip, DDI_VENDOR_SUNW, EC_ENV,
960 	    subclass, attr_list, NULL, DDI_NOSLEEP);
961 	if (err != 0) {
962 		cmn_err(CE_WARN, "Failed to log %s/%s event",
963 		    EC_ENV, subclass);
964 	}
965 
966 	cmn_err(CE_NOTE, "%s", sensor_str);
967 	nvlist_free(attr_list);
968 }
969 
970 static uint_t
971 rmclomv_event_data_handler(char *arg)
972 {
973 	dp_event_notification_t	*payload;
974 	rmc_comm_msg_t	*msg;
975 	envmon_handle_t envhdl;
976 	int hint;
977 	char *ptr, *save_ptr;
978 
979 	if (arg == NULL) {
980 		return (DDI_INTR_CLAIMED);
981 	}
982 
983 	msg = (rmc_comm_msg_t *)arg;
984 	if (msg->msg_buf == NULL) {
985 		return (DDI_INTR_CLAIMED);
986 	}
987 
988 	payload = (dp_event_notification_t *)msg->msg_buf;
989 	switch (payload->event) {
990 
991 	case RMC_KEYSWITCH_EVENT:
992 		real_key_position = payload->event_info.ev_keysw.key_position;
993 		cmn_err(CE_NOTE, "keyswitch change event - state = %s",
994 		    rmclomv_key_position(real_key_position));
995 		if ((real_key_position != RMC_KEYSWITCH_POS_UNKNOWN) &&
996 		    (real_key_position <= RMC_KEYSWITCH_POS_OFF)) {
997 			key_position = real_key_position;
998 		} else {
999 			/* treat unknown key position as locked */
1000 			key_position = RMC_KEYSWITCH_POS_LOCKED;
1001 		}
1002 		break;
1003 
1004 	case RMC_HPU_EVENT:
1005 		/*
1006 		 * send appropriate sysevent
1007 		 */
1008 		switch (payload->event_info.ev_hpunot.sub_event) {
1009 		case RMC_HPU_REMOVE_EVENT:
1010 			hint = SE_HINT_REMOVE;
1011 			break;
1012 		case RMC_HPU_INSERT_EVENT:
1013 			hint = SE_HINT_INSERT;
1014 			break;
1015 		default:
1016 			hint = SE_NO_HINT;
1017 			break;
1018 		}
1019 		rmclomv_hdl_to_envhdl(payload->event_info.ev_hpunot.hpu_hdl,
1020 		    &envhdl);
1021 		rmclomv_dr_data_handler(envhdl.name, hint);
1022 		break;
1023 
1024 	case RMC_INIT_EVENT:
1025 		/*
1026 		 * Wake up the refresh thread.
1027 		 */
1028 		rmclomv_refresh_wakeup();
1029 
1030 		/*
1031 		 * Wake up the checkrmc thread for an early indication to PICL
1032 		 */
1033 		rmclomv_checkrmc_wakeup(NULL);
1034 		break;
1035 
1036 	case RMC_ENV_EVENT:
1037 		rmclomv_hdl_to_envhdl(payload->event_info.ev_envnot.env_hdl,
1038 		    &envhdl);
1039 
1040 		/* split name into fru name and sensor name */
1041 		ptr = strchr(envhdl.name, '.');
1042 
1043 		/* must have at least one '.' */
1044 		if (ptr == NULL)
1045 			break;
1046 
1047 		/* find last '.' - convert the others to '/' */
1048 		for (;;) {
1049 			save_ptr = ptr;
1050 			ptr = strchr(ptr, '.');
1051 			if (ptr == NULL) {
1052 				ptr = save_ptr;
1053 				break;
1054 			}
1055 			*save_ptr = '/';
1056 		}
1057 		*ptr = '\0';
1058 		ptr++;
1059 		/* is it a voltage or temperature sensor? */
1060 		if ((*ptr == 'V' || *ptr == 'T') && *(ptr + 1) == '_') {
1061 			switch (payload->event_info.ev_envnot.sub_event) {
1062 			case RMC_ENV_WARNING_THRESHOLD_EVENT:
1063 			case RMC_ENV_SHUTDOWN_THRESHOLD_EVENT:
1064 			case RMC_ENV_OK_EVENT:
1065 				threshold_sysevent(envhdl.name, ptr,
1066 				    payload->event_info.ev_envnot.sub_event,
1067 				    *ptr);
1068 				break;
1069 			default:
1070 				break;
1071 			}
1072 		}
1073 
1074 		/*
1075 		 * is it a fan sensor?
1076 		 * Fan sensor names end either in RS, F0 or F1
1077 		 */
1078 		if ((*ptr == 'R' && *(ptr + 1) == 'S' && *(ptr + 2) == '\0') ||
1079 		    (*ptr == 'F' && *(ptr + 1) == '0' && *(ptr + 2) == '\0') ||
1080 		    (*ptr == 'F' && *(ptr + 1) == '1' && *(ptr + 2) == '\0')) {
1081 			switch (payload->event_info.ev_envnot.sub_event) {
1082 			case RMC_ENV_FAULT_EVENT:
1083 			case RMC_ENV_OK_EVENT:
1084 				fan_sysevent(envhdl.name, ptr,
1085 				    payload->event_info.ev_envnot.sub_event);
1086 				break;
1087 			default:
1088 				break;
1089 			}
1090 		}
1091 		break;
1092 
1093 	case RMC_LOG_EVENT:
1094 	{
1095 		int level = 10;
1096 		int flags = SL_NOTE | SL_CONSOLE;
1097 		char *message =
1098 		    (char *)payload->event_info.ev_rmclog.log_record;
1099 
1100 		message[ payload->event_info.ev_rmclog.log_record_size] = '\0';
1101 
1102 		/*
1103 		 * Logs have a 10 character prefix - specifying the severity of
1104 		 * the event being logged. Thus all the magic number 10s down
1105 		 * here
1106 		 */
1107 		if (0 == strncmp("CRITICAL: ", message, 10)) {
1108 			message += 10;
1109 			level = 0;
1110 			flags = SL_FATAL | SL_ERROR | SL_CONSOLE;
1111 		} else if (0 == strncmp("MAJOR:    ", message, 10)) {
1112 			message += 10;
1113 			level = 5;
1114 			flags = SL_WARN | SL_ERROR | SL_CONSOLE;
1115 		} else if (0 == strncmp("MINOR:    ", message, 10)) {
1116 			message += 10;
1117 			level = 10;
1118 			flags = SL_NOTE | SL_CONSOLE;
1119 		}
1120 
1121 		(void) strlog(0, 0, level, flags, message);
1122 		break;
1123 	}
1124 
1125 	default:
1126 		return (DDI_INTR_CLAIMED);
1127 	}
1128 
1129 	return (DDI_INTR_CLAIMED);
1130 }
1131 
1132 /*ARGSUSED*/
1133 static int
1134 rmclomv_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p)
1135 {
1136 	int error = 0;
1137 	int instance = getminor(*dev_p);
1138 
1139 	if (instance != 0)
1140 		return (ENXIO);
1141 
1142 	if ((flag & FWRITE) != 0 && (error = drv_priv(cred_p)) != 0)
1143 		return (error);
1144 
1145 	return (0);
1146 }
1147 
1148 /*ARGSUSED*/
1149 static int
1150 rmclomv_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
1151 {
1152 	return (DDI_SUCCESS);
1153 }
1154 
1155 static int
1156 rmclomv_do_cmd(int req_cmd, int resp_cmd, int resp_len, intptr_t arg_req,
1157     intptr_t arg_res)
1158 {
1159 	rmc_comm_msg_t request, *reqp = &request;
1160 	rmc_comm_msg_t response, *resp = &response;
1161 	int rv = 0;
1162 
1163 	bzero((caddr_t)&request, sizeof (request));
1164 	reqp->msg_type = req_cmd;
1165 	reqp->msg_buf = (caddr_t)arg_req;
1166 	bzero((caddr_t)&response, sizeof (response));
1167 	resp->msg_type = resp_cmd;
1168 	resp->msg_buf = (caddr_t)arg_res;
1169 	resp->msg_len = resp_len;
1170 
1171 	switch (req_cmd) {
1172 	case DP_GET_SYSINFO:
1173 		resp->msg_len = sizeof (dp_get_sysinfo_r_t);
1174 		break;
1175 	case DP_GET_EVENT_LOG:
1176 		resp->msg_len = sizeof (dp_get_event_log_r_t);
1177 		break;
1178 	case DP_GET_VOLTS:
1179 		reqp->msg_len = sizeof (dp_get_volts_t);
1180 		break;
1181 	case DP_GET_TEMPERATURES:
1182 		reqp->msg_len = sizeof (dp_get_temperatures_t);
1183 		break;
1184 	case DP_GET_CIRCUIT_BRKS:
1185 		reqp->msg_len = sizeof (dp_get_circuit_brks_t);
1186 		break;
1187 	case DP_GET_FAN_STATUS:
1188 		reqp->msg_len = sizeof (dp_get_fan_status_t);
1189 		break;
1190 	case DP_GET_PSU_STATUS:
1191 		reqp->msg_len = sizeof (dp_get_psu_status_t);
1192 		break;
1193 	case DP_GET_LED_STATE:
1194 		reqp->msg_len = sizeof (dp_get_led_state_t);
1195 		break;
1196 	case DP_SET_LED_STATE:
1197 		reqp->msg_len = sizeof (dp_set_led_state_t);
1198 		break;
1199 	case DP_GET_FRU_STATUS:
1200 		reqp->msg_len = sizeof (dp_get_fru_status_t);
1201 		break;
1202 	case DP_GET_HANDLE_NAME:
1203 		reqp->msg_len = sizeof (dp_get_handle_name_t);
1204 		break;
1205 	case DP_GET_ALARM_STATE:
1206 		reqp->msg_len = sizeof (dp_get_alarm_state_t);
1207 		break;
1208 	case DP_SET_ALARM_STATE:
1209 		reqp->msg_len = sizeof (dp_set_alarm_state_t);
1210 		break;
1211 	case DP_GET_SDP_VERSION:
1212 		resp->msg_len = sizeof (dp_get_sdp_version_r_t);
1213 		break;
1214 	case DP_GET_CHASSIS_SERIALNUM:
1215 		reqp->msg_len = 0;
1216 		break;
1217 	case DP_GET_DATE_TIME:
1218 		reqp->msg_len = 0;
1219 		break;
1220 	default:
1221 		return (EINVAL);
1222 	}
1223 
1224 	rv = rmc_comm_request_response(reqp, resp,
1225 	    RMCLOMV_DEFAULT_MAX_MBOX_WAIT_TIME);
1226 
1227 	if (rv != RCNOERR) {
1228 		/*
1229 		 * RMC returned an error or failed to respond.
1230 		 * Where the RMC itself is implicated, rmclomv_rmc_error
1231 		 * is set non-zero. It is cleared after an error free exchange.
1232 		 * Two failure cases are distinguished:
1233 		 * RMCLOMV_RMCSTATE_FAILED and RMCLOMV_RMCSTATE_DOWNLOAD.
1234 		 */
1235 		switch (rv) {
1236 		case RCENOSOFTSTATE:
1237 			/* invalid/NULL soft state structure */
1238 			return (EIO);
1239 		case RCENODATALINK:
1240 			/*
1241 			 * firmware download in progress,
1242 			 * can you come back later?
1243 			 */
1244 			rmclomv_rmc_error = RMCLOMV_RMCSTATE_DOWNLOAD;
1245 			rmclomv_rmc_state = RMCLOMV_RMCSTATE_DOWNLOAD;
1246 			return (EAGAIN);
1247 		case RCENOMEM:
1248 			/* memory problems */
1249 			return (ENOMEM);
1250 		case RCECANTRESEND:
1251 			/* resend failed */
1252 			rmclomv_rmc_error = RMCLOMV_RMCSTATE_FAILED;
1253 			return (EIO);
1254 		case RCEMAXRETRIES:
1255 			/* reply not received - retries exceeded */
1256 			rmclomv_rmc_error = RMCLOMV_RMCSTATE_FAILED;
1257 			return (EINTR);
1258 		case RCETIMEOUT:
1259 			/* reply not received - command has timed out */
1260 			rmclomv_rmc_error = RMCLOMV_RMCSTATE_FAILED;
1261 			return (EINTR);
1262 		case RCEINVCMD:
1263 			/* data protocol cmd not supported */
1264 			return (ENOTSUP);
1265 		case RCEINVARG:
1266 			/* invalid argument(s) */
1267 			return (ENOTSUP);
1268 		case RCEGENERIC:
1269 			/* generic error */
1270 			rmclomv_rmc_error = RMCLOMV_RMCSTATE_FAILED;
1271 			return (EIO);
1272 		default:
1273 			rmclomv_rmc_error = RMCLOMV_RMCSTATE_FAILED;
1274 			return (EIO);
1275 		}
1276 	}
1277 
1278 	rmclomv_rmc_error = RMCLOMV_RMCERROR_NONE;
1279 	return (0);
1280 }
1281 
1282 /*
1283  * validate_section_entry checks that the entry at the specified index
1284  * is valid and not duplicated by an entry above. If these tests fail
1285  * the entry is removed and B_FALSE returned. Otherwise returns B_TRUE.
1286  */
1287 static int
1288 validate_section_entry(rmclomv_cache_section_t *section, int index)
1289 {
1290 	int			i;
1291 	rmclomv_cache_entry_t	*entry;
1292 
1293 	for (i = index; i < section->num_entries; i++) {
1294 		entry = &section->entry[i];
1295 		if (entry->handle_name.name[0] == '\0') {
1296 			cmn_err(CE_WARN,
1297 			    "rmclomv: empty handle_name, handle 0x%x type %x",
1298 			    entry->handle, section->sensor_type);
1299 		} else if (entry->ind_mask != 0) {
1300 			continue;	/* skip special entries */
1301 		} else if (entry->handle == DP_NULL_HANDLE) {
1302 			cmn_err(CE_WARN,
1303 			    "rmclomv: null handle id for \"%s\" type %x",
1304 			    entry->handle_name.name, section->sensor_type);
1305 		} else if (i == index) {
1306 			continue;
1307 		} else if (section->entry[index].handle == entry->handle) {
1308 			cmn_err(CE_WARN,
1309 			    "rmclomv: duplicate handle 0x%x type %x",
1310 			    entry->handle, section->sensor_type);
1311 		} else if (strcmp(entry->handle_name.name,
1312 		    section->entry[index].handle_name.name) == 0) {
1313 			cmn_err(CE_WARN,
1314 			    "rmclomv: duplicate handle_name \"%s\", "
1315 			    "handle 0x%x type %x", entry->handle_name.name,
1316 			    entry->handle, section->sensor_type);
1317 		} else
1318 			continue;
1319 
1320 		/*
1321 		 * need to remove the entry at index
1322 		 */
1323 		section->num_entries--;
1324 
1325 		for (i = index; i < section->num_entries; i++) {
1326 			section->entry[i] = section->entry[i + 1];
1327 		}
1328 
1329 		return (B_FALSE);
1330 	}
1331 
1332 	return (B_TRUE);
1333 }
1334 
1335 /*
1336  * Populate a section containing handles with corresponding names
1337  * The supplied section structure must not be publically visible and the
1338  * name cache must not be locked either (because RMC i/o is required).
1339  *
1340  * This is the place where a sanity check is applied. Entries containing
1341  * duplicate handles, duplicate names or empty names are removed and the
1342  * structure is compacted. As a result num_entries may be reduced.
1343  */
1344 static int
1345 add_names_to_section(rmclomv_cache_section_t *section)
1346 {
1347 	int			retval = 0;
1348 	int			ditched = B_FALSE;
1349 	int			index;
1350 	dp_get_handle_name_r_t	handle_name_r;
1351 	rmclomv_cache_entry_t	*entry;
1352 
1353 	for (index = 0; index < section->num_entries; index++) {
1354 		entry = &section->entry[index];
1355 		if (entry->ind_mask != 0)
1356 			continue;	/* skip special entries */
1357 		handle_name_r.handle = entry->handle;
1358 		retval = rmclomv_do_cmd(DP_GET_HANDLE_NAME,
1359 		    DP_GET_HANDLE_NAME_R, sizeof (handle_name_r),
1360 		    (intptr_t)&handle_name_r, (intptr_t)&handle_name_r);
1361 		if (retval == 0)
1362 			bcopy(handle_name_r.name,
1363 			    entry->handle_name.name, DP_MAX_HANDLE_NAME);
1364 	}
1365 
1366 	/*
1367 	 * now ditch invalid and duplicate entries
1368 	 */
1369 	for (index = 0; index < section->num_entries; index++) {
1370 		while (validate_section_entry(section, index) == B_FALSE)
1371 			ditched = B_TRUE;
1372 	}
1373 
1374 	if (ditched)
1375 		cmn_err(CE_WARN, "Retaining %d nodes of type %d",
1376 		    section->num_entries, section->sensor_type);
1377 
1378 	return (retval);
1379 }
1380 
1381 /*
1382  * The supplied (PSU) cache section is traversed and entries are created
1383  * for the individual indicators belonging to a PSU. These entries are
1384  * placed in a private chain. The caller, subsequently acquires the
1385  * cache lock and copies the chain head to make it public.
1386  * The handle-names for PSU indicators are derived from the parent PSU
1387  * handle-name.
1388  * NOTE: add_names_to_section() may have reduced psu_section->num_entries
1389  *       so DON'T USE psu_resp->num_psus
1390  */
1391 static void
1392 make_psu_subsections(rmclomv_cache_section_t *psu_section,
1393     rmclomv_cache_section_t **chain_head, dp_get_psu_status_r_t *psu_resp)
1394 {
1395 	int			index;
1396 	int			subindex = 0;
1397 	rmclomv_cache_section_t	*subsection;
1398 	rmclomv_cache_entry_t	*src_entry;
1399 	rmclomv_cache_entry_t	*dst_entry;
1400 
1401 	subsection = create_cache_section(RMCLOMV_VOLT_IND,
1402 	    RMCLOMV_MAX_VI_PER_PSU * psu_section->num_entries);
1403 	for (index = 0; index < psu_section->num_entries; index++) {
1404 		src_entry = &psu_section->entry[index];
1405 		if ((psu_resp->psu_status[index].mask &
1406 		    DP_PSU_INPUT_STATUS) != 0) {
1407 			dst_entry = &subsection->entry[subindex++];
1408 			dst_entry->handle = src_entry->handle;
1409 			dst_entry->ind_mask = DP_PSU_INPUT_STATUS;
1410 			(void) snprintf(dst_entry->handle_name.name,
1411 			    ENVMON_MAXNAMELEN, "%s.%s",
1412 			    src_entry->handle_name.name,
1413 			    str_ip_volts_ind);
1414 		}
1415 
1416 		if ((psu_resp->psu_status[index].mask &
1417 		    DP_PSU_SEC_INPUT_STATUS) != 0) {
1418 			dst_entry = &subsection->entry[subindex++];
1419 			dst_entry->handle = src_entry->handle;
1420 			dst_entry->ind_mask = DP_PSU_SEC_INPUT_STATUS;
1421 			(void) snprintf(dst_entry->handle_name.name,
1422 			    ENVMON_MAXNAMELEN, "%s.%s",
1423 			    src_entry->handle_name.name,
1424 			    str_ip2_volts_ind);
1425 		}
1426 
1427 		if ((psu_resp->psu_status[index].mask &
1428 		    DP_PSU_OUTPUT_STATUS) != 0) {
1429 			dst_entry = &subsection->entry[subindex++];
1430 			dst_entry->handle = src_entry->handle;
1431 			dst_entry->ind_mask = DP_PSU_OUTPUT_STATUS;
1432 			(void) snprintf(dst_entry->handle_name.name,
1433 			    ENVMON_MAXNAMELEN, "%s.%s",
1434 			    src_entry->handle_name.name,
1435 			    str_ff_pok_ind);
1436 		}
1437 
1438 		if ((psu_resp->psu_status[index].mask &
1439 		    DP_PSU_OUTPUT_VLO_STATUS) != 0) {
1440 			dst_entry = &subsection->entry[subindex++];
1441 			dst_entry->handle = src_entry->handle;
1442 			dst_entry->ind_mask = DP_PSU_OUTPUT_VLO_STATUS;
1443 			(void) snprintf(dst_entry->handle_name.name,
1444 			    ENVMON_MAXNAMELEN, "%s.%s",
1445 			    src_entry->handle_name.name,
1446 			    str_vlo_volts_ind);
1447 		}
1448 
1449 		if ((psu_resp->psu_status[index].mask &
1450 		    DP_PSU_OUTPUT_VHI_STATUS) != 0) {
1451 			dst_entry = &subsection->entry[subindex++];
1452 			dst_entry->handle = src_entry->handle;
1453 			dst_entry->ind_mask = DP_PSU_OUTPUT_VHI_STATUS;
1454 			(void) snprintf(dst_entry->handle_name.name,
1455 			    ENVMON_MAXNAMELEN, "%s.%s",
1456 			    src_entry->handle_name.name,
1457 			    str_vhi_volts_ind);
1458 		}
1459 	}
1460 	/*
1461 	 * Adjust number of entries value in cache section
1462 	 * to match the facts.
1463 	 */
1464 	subsection->num_entries = subindex;
1465 	add_section(chain_head, subsection);
1466 
1467 	subsection = create_cache_section(RMCLOMV_AMP_IND,
1468 	    RMCLOMV_MAX_CI_PER_PSU * psu_section->num_entries);
1469 	subindex = 0;
1470 	for (index = 0; index < psu_section->num_entries; index++) {
1471 		int mask = psu_resp->psu_status[index].mask;
1472 		src_entry = &psu_section->entry[index];
1473 		if ((mask & DP_PSU_OUTPUT_AHI_STATUS) != 0) {
1474 			dst_entry = &subsection->entry[subindex++];
1475 			dst_entry->handle = src_entry->handle;
1476 			dst_entry->ind_mask = DP_PSU_OUTPUT_AHI_STATUS;
1477 			(void) snprintf(dst_entry->handle_name.name,
1478 			    ENVMON_MAXNAMELEN, "%s.%s",
1479 			    src_entry->handle_name.name,
1480 			    str_chi_amps_ind);
1481 		}
1482 		if ((mask & DP_PSU_NR_WARNING) != 0) {
1483 			dst_entry = &subsection->entry[subindex++];
1484 			dst_entry->handle = src_entry->handle;
1485 			dst_entry->ind_mask = DP_PSU_NR_WARNING;
1486 			(void) snprintf(dst_entry->handle_name.name,
1487 			    ENVMON_MAXNAMELEN, "%s.%s",
1488 			    src_entry->handle_name.name,
1489 			    str_chi_nr_ind);
1490 		}
1491 	}
1492 	subsection->num_entries = subindex;
1493 	add_section(chain_head, subsection);
1494 
1495 	subsection = create_cache_section(RMCLOMV_TEMP_IND,
1496 	    psu_section->num_entries);
1497 	subindex = 0;
1498 	for (index = 0; index < psu_section->num_entries; index++) {
1499 		if ((psu_resp->psu_status[index].mask &
1500 		    DP_PSU_OVERTEMP_FAULT) != 0) {
1501 			src_entry = &psu_section->entry[index];
1502 			dst_entry = &subsection->entry[subindex++];
1503 			dst_entry->handle = src_entry->handle;
1504 			dst_entry->ind_mask = DP_PSU_OVERTEMP_FAULT;
1505 			(void) snprintf(dst_entry->handle_name.name,
1506 			    ENVMON_MAXNAMELEN, "%s.%s",
1507 			    src_entry->handle_name.name,
1508 			    str_ot_tmpr_ind);
1509 		}
1510 	}
1511 	subsection->num_entries = subindex;
1512 	add_section(chain_head, subsection);
1513 
1514 	subsection = create_cache_section(RMCLOMV_FAN_IND,
1515 	    RMCLOMV_MAX_FI_PER_PSU * psu_section->num_entries);
1516 	subindex = 0;
1517 	for (index = 0; index < psu_section->num_entries; index++) {
1518 		int mask = psu_resp->psu_status[index].mask;
1519 		src_entry = &psu_section->entry[index];
1520 		if ((mask & DP_PSU_FAN_FAULT) != 0) {
1521 			dst_entry = &subsection->entry[subindex++];
1522 			dst_entry->handle = src_entry->handle;
1523 			dst_entry->ind_mask = DP_PSU_FAN_FAULT;
1524 			(void) snprintf(dst_entry->handle_name.name,
1525 			    ENVMON_MAXNAMELEN, "%s.%s",
1526 			    src_entry->handle_name.name, str_fan_ind);
1527 		}
1528 		if ((mask & DP_PSU_PDCT_FAN) != 0) {
1529 			dst_entry = &subsection->entry[subindex++];
1530 			dst_entry->handle = src_entry->handle;
1531 			dst_entry->ind_mask = DP_PSU_PDCT_FAN;
1532 			(void) snprintf(dst_entry->handle_name.name,
1533 			    ENVMON_MAXNAMELEN, "%s.%s",
1534 			    src_entry->handle_name.name, str_pdct_fan_ind);
1535 		}
1536 	}
1537 	subsection->num_entries = subindex;
1538 	add_section(chain_head, subsection);
1539 }
1540 
1541 static void
1542 refresh_name_cache(int force_fail)
1543 {
1544 	union {
1545 		dp_get_volts_t		u_volts_cmd;
1546 		dp_get_temperatures_t	u_temp_cmd;
1547 		dp_get_circuit_brks_t	u_ampi_cmd;
1548 		dp_get_fan_status_t	u_fan_cmd;
1549 		dp_get_psu_status_t	u_psu_cmd;
1550 		dp_get_fru_status_t	u_fru_cmd;
1551 		dp_get_led_state_t	u_led_cmd;
1552 		dp_set_led_state_t	u_setled_cmd;
1553 		dp_get_alarm_state_t	u_alarm_cmd;
1554 		dp_set_alarm_state_t	u_setalarm_cmd;
1555 	} rmc_cmdbuf;
1556 
1557 /* defines for accessing union fields */
1558 #define	volts_cmd	rmc_cmdbuf.u_volts_cmd
1559 #define	temp_cmd	rmc_cmdbuf.u_temp_cmd
1560 #define	ampi_cmd	rmc_cmdbuf.u_ampi_cmd
1561 #define	fan_cmd		rmc_cmdbuf.u_fan_cmd
1562 #define	psu_cmd		rmc_cmdbuf.u_psu_cmd
1563 #define	fru_cmd		rmc_cmdbuf.u_fru_cmd
1564 #define	led_cmd		rmc_cmdbuf.u_led_cmd
1565 #define	setled_cmd	rmc_cmdbuf.u_setled_cmd
1566 #define	alarm_cmd	rmc_cmdbuf.u_alarm_cmd
1567 #define	setalarm_cmd	rmc_cmdbuf.u_setalarm_cmd
1568 
1569 	/*
1570 	 * Data area to read sensor data into
1571 	 */
1572 	static union {
1573 		char			reservation[RMCRESBUFLEN];
1574 		dp_get_volts_r_t	u_volts_r;
1575 		dp_get_temperatures_r_t	u_temp_r;
1576 		dp_get_circuit_brks_r_t	u_ampi_r;
1577 		dp_get_fan_status_r_t	u_fan_r;
1578 		dp_get_psu_status_r_t	u_psu_r;
1579 		dp_get_fru_status_r_t	u_fru_r;
1580 		dp_get_led_state_r_t	u_led_r;
1581 		dp_set_led_state_r_t	u_setled_r;
1582 		dp_get_alarm_state_r_t	u_alarm_r;
1583 		dp_set_alarm_state_r_t	u_setalarm_r;
1584 	} rmc_sensbuf;
1585 
1586 /* defines for accessing union fields */
1587 #define	volts_r		rmc_sensbuf.u_volts_r
1588 #define	temp_r		rmc_sensbuf.u_temp_r
1589 #define	ampi_r		rmc_sensbuf.u_ampi_r
1590 #define	fan_r		rmc_sensbuf.u_fan_r
1591 #define	psu_r		rmc_sensbuf.u_psu_r
1592 #define	fru_r		rmc_sensbuf.u_fru_r
1593 #define	led_r		rmc_sensbuf.u_led_r
1594 #define	setled_r	rmc_sensbuf.u_setled_r
1595 #define	alarm_r		rmc_sensbuf.u_alarm_r
1596 #define	setalarm_r	rmc_sensbuf.u_setalarm_r
1597 
1598 	int			retval = force_fail;
1599 	int			retval1 = retval;
1600 	int			index;
1601 	rmclomv_cache_section_t	*my_chain = NULL;
1602 	rmclomv_cache_section_t	*derived_chain = NULL;
1603 	rmclomv_cache_section_t	*section;
1604 	rmclomv_cache_section_t	*psu_section;
1605 	rmclomv_cache_section_t	*fru_section;
1606 	dp_get_sysinfo_r_t	sysinfo;
1607 	rmclomv_cache_entry_t	*entry;
1608 
1609 	if (retval == 0) {
1610 		retval = rmclomv_do_cmd(DP_GET_SYSINFO, DP_GET_SYSINFO_R,
1611 		    sizeof (sysinfo), NULL, (intptr_t)&sysinfo);
1612 	}
1613 	if (retval == 0) {
1614 		fru_cmd.handle = DP_NULL_HANDLE;
1615 		retval = rmclomv_do_cmd(DP_GET_FRU_STATUS, DP_GET_FRU_STATUS_R,
1616 		    RMCRESBUFLEN, (intptr_t)&fru_cmd, (intptr_t)&fru_r);
1617 	}
1618 	if (retval != 0)
1619 		fru_r.num_frus = 0;
1620 
1621 	/*
1622 	 * Reserve space for special additional entries in the FRU section
1623 	 */
1624 	fru_section = create_cache_section(RMCLOMV_HPU_IND,
1625 	    RMCLOMV_NUM_SPECIAL_FRUS + fru_r.num_frus);
1626 
1627 	/*
1628 	 * add special entry for RMC itself
1629 	 */
1630 	entry = &fru_section->entry[0];
1631 	(void) snprintf(entry->handle_name.name, sizeof (envmon_handle_t),
1632 	    "SC");
1633 	entry->handle = 0;
1634 	entry->ind_mask = 1;	/* flag as a special entry */
1635 
1636 	/*
1637 	 * populate any other FRU entries
1638 	 */
1639 	for (index = 0; index < fru_r.num_frus; index++) {
1640 		fru_section->entry[RMCLOMV_NUM_SPECIAL_FRUS + index].handle =
1641 		    fru_r.fru_status[index].handle;
1642 		fru_section->entry[RMCLOMV_NUM_SPECIAL_FRUS + index].ind_mask =
1643 		    0;
1644 	}
1645 
1646 	my_chain = fru_section;
1647 
1648 	if (retval == 0) {
1649 		volts_cmd.handle = DP_NULL_HANDLE;
1650 		retval = rmclomv_do_cmd(DP_GET_VOLTS, DP_GET_VOLTS_R,
1651 		    RMCRESBUFLEN, (intptr_t)&volts_cmd, (intptr_t)&volts_r);
1652 	}
1653 	if (retval == 0) {
1654 		section = create_cache_section(RMCLOMV_VOLT_SENS,
1655 		    volts_r.num_volts);
1656 		for (index = 0; index < volts_r.num_volts; index++) {
1657 			section->entry[index].handle =
1658 			    volts_r.volt_status[index].handle;
1659 		}
1660 		add_section(&my_chain, section);
1661 	}
1662 	if (retval == 0) {
1663 		temp_cmd.handle = DP_NULL_HANDLE;
1664 		retval = rmclomv_do_cmd(DP_GET_TEMPERATURES,
1665 		    DP_GET_TEMPERATURES_R, RMCRESBUFLEN,
1666 		    (intptr_t)&temp_cmd, (intptr_t)&temp_r);
1667 	}
1668 	if (retval == 0) {
1669 		section = create_cache_section(RMCLOMV_TEMP_SENS,
1670 		    temp_r.num_temps);
1671 		for (index = 0; index < temp_r.num_temps; index++) {
1672 			section->entry[index].handle =
1673 			    temp_r.temp_status[index].handle;
1674 		}
1675 		add_section(&my_chain, section);
1676 	}
1677 	if (retval == 0) {
1678 		fan_cmd.handle = DP_NULL_HANDLE;
1679 		retval = rmclomv_do_cmd(DP_GET_FAN_STATUS, DP_GET_FAN_STATUS_R,
1680 		    RMCRESBUFLEN, (intptr_t)&fan_cmd, (intptr_t)&fan_r);
1681 	}
1682 	if (retval == 0) {
1683 		section = create_cache_section(RMCLOMV_FAN_SENS,
1684 		    fan_r.num_fans);
1685 		for (index = 0; index < fan_r.num_fans; index++) {
1686 			section->entry[index].handle =
1687 			    fan_r.fan_status[index].handle;
1688 		}
1689 		add_section(&my_chain, section);
1690 	}
1691 	if (retval == 0) {
1692 		ampi_cmd.handle = DP_NULL_HANDLE;
1693 		retval = rmclomv_do_cmd(DP_GET_CIRCUIT_BRKS,
1694 		    DP_GET_CIRCUIT_BRKS_R, RMCRESBUFLEN,
1695 		    (intptr_t)&ampi_cmd, (intptr_t)&ampi_r);
1696 	}
1697 	if (retval == 0) {
1698 		section = create_cache_section(RMCLOMV_AMP_IND,
1699 		    ampi_r.num_circuit_brks);
1700 		for (index = 0; index < ampi_r.num_circuit_brks; index++) {
1701 			section->entry[index].handle =
1702 			    ampi_r.circuit_brk_status[index].handle;
1703 		}
1704 		add_section(&my_chain, section);
1705 	}
1706 	if (retval == 0) {
1707 		led_cmd.handle = DP_NULL_HANDLE;
1708 		retval = rmclomv_do_cmd(DP_GET_LED_STATE, DP_GET_LED_STATE_R,
1709 		    RMCRESBUFLEN, (intptr_t)&led_cmd, (intptr_t)&led_r);
1710 	}
1711 	if (retval == 0) {
1712 		section = create_cache_section(RMCLOMV_LED_IND,
1713 		    led_r.num_leds);
1714 		for (index = 0; index < led_r.num_leds; index++) {
1715 			section->entry[index].handle =
1716 			    led_r.led_state[index].handle;
1717 		}
1718 		add_section(&my_chain, section);
1719 	}
1720 	/*
1721 	 * The command DP_GET_ALARM_STATE may not be valid on
1722 	 * some RMC versions, so we ignore the return value
1723 	 * and proceed
1724 	 */
1725 	if (retval == 0) {
1726 		alarm_cmd.handle = DP_NULL_HANDLE;
1727 		retval1 = rmclomv_do_cmd(DP_GET_ALARM_STATE,
1728 		    DP_GET_ALARM_STATE_R, RMCRESBUFLEN,
1729 		    (intptr_t)&alarm_cmd, (intptr_t)&alarm_r);
1730 		if ((retval1 == 0) && alarm_r.num_alarms) {
1731 			section = create_cache_section(RMCLOMV_ALARM_IND,
1732 			    alarm_r.num_alarms);
1733 			for (index = 0; index < alarm_r.num_alarms; index++) {
1734 				section->entry[index].handle =
1735 				    alarm_r.alarm_state[index].handle;
1736 			}
1737 			add_section(&my_chain, section);
1738 		}
1739 	}
1740 	if (retval == 0) {
1741 		psu_cmd.handle = DP_NULL_HANDLE;
1742 		retval = rmclomv_do_cmd(DP_GET_PSU_STATUS, DP_GET_PSU_STATUS_R,
1743 		    RMCRESBUFLEN, (intptr_t)&psu_cmd, (intptr_t)&psu_r);
1744 	}
1745 	if (retval == 0) {
1746 		/*
1747 		 * WARNING:
1748 		 * =======
1749 		 * The PSUs must be probed last so that the response data
1750 		 * (psu_r) is available for make_psu_subsections() below.
1751 		 * Note that all the responses share the same data area
1752 		 * which is declared as a union.
1753 		 */
1754 		psu_section = create_cache_section(RMCLOMV_PSU_IND,
1755 		    psu_r.num_psus);
1756 		for (index = 0; index < psu_r.num_psus; index++) {
1757 			psu_section->entry[index].handle =
1758 			    psu_r.psu_status[index].handle;
1759 		}
1760 		add_section(&my_chain, psu_section);
1761 	}
1762 	if (retval == 0) {
1763 		for (section = my_chain;
1764 		    section != NULL;
1765 		    section = section->next_section) {
1766 			retval = add_names_to_section(section);
1767 			if (retval != 0) {
1768 				break;
1769 			}
1770 		}
1771 	}
1772 
1773 	/*
1774 	 * now add nodes derived from PSUs
1775 	 */
1776 	if (retval == 0) {
1777 		make_psu_subsections(psu_section, &derived_chain, &psu_r);
1778 		/*
1779 		 * name cache sections all set, exchange new for old
1780 		 */
1781 		rmclomv_reset_cache(my_chain, derived_chain, &sysinfo);
1782 	} else {
1783 		/*
1784 		 * RMC is not responding, ditch any existing cache
1785 		 * and just leave the special SC FRU node
1786 		 */
1787 		rmclomv_reset_cache(my_chain, NULL, NULL);
1788 	}
1789 }
1790 
1791 static void
1792 set_val_unav(envmon_sensor_t *sensor)
1793 {
1794 	sensor->value = ENVMON_VAL_UNAVAILABLE;
1795 	sensor->lowthresholds.warning = ENVMON_VAL_UNAVAILABLE;
1796 	sensor->lowthresholds.shutdown = ENVMON_VAL_UNAVAILABLE;
1797 	sensor->lowthresholds.poweroff = ENVMON_VAL_UNAVAILABLE;
1798 	sensor->highthresholds.warning = ENVMON_VAL_UNAVAILABLE;
1799 	sensor->highthresholds.shutdown = ENVMON_VAL_UNAVAILABLE;
1800 	sensor->highthresholds.poweroff = ENVMON_VAL_UNAVAILABLE;
1801 }
1802 
1803 static void
1804 set_fan_unav(envmon_fan_t *fan)
1805 {
1806 	fan->speed = ENVMON_VAL_UNAVAILABLE;
1807 	fan->units[0] = '\0';
1808 	fan->lowthresholds.warning = ENVMON_VAL_UNAVAILABLE;
1809 	fan->lowthresholds.shutdown = ENVMON_VAL_UNAVAILABLE;
1810 	fan->lowthresholds.poweroff = ENVMON_VAL_UNAVAILABLE;
1811 }
1812 
1813 static int
1814 do_psu_cmd(intptr_t arg, int mode, envmon_indicator_t *env_ind,
1815     dp_get_psu_status_t *rmc_psu, dp_get_psu_status_r_t *rmc_psu_r,
1816     int detector_type)
1817 {
1818 	int			index;
1819 	uint16_t		sensor_status;
1820 	rmclomv_cache_section_t	*section;
1821 	uint16_t		indicator_mask;
1822 
1823 	if (ddi_copyin((caddr_t)arg, (caddr_t)env_ind,
1824 	    sizeof (envmon_indicator_t), mode) != 0)
1825 		return (EFAULT);
1826 
1827 	/* ensure we've got PSU handles cached */
1828 	LOCK_CACHE
1829 
1830 	sensor_status = ENVMON_SENSOR_OK;
1831 	section = rmclomv_find_section(rmclomv_subcache, detector_type);
1832 	if (env_ind->id.name[0] == '\0') {
1833 		/* request for first handle */
1834 		if ((section == NULL) || (section->num_entries == 0))
1835 			env_ind->next_id.name[0] = '\0';
1836 		else
1837 			env_ind->next_id = section->entry[0].handle_name;
1838 		sensor_status = ENVMON_NOT_PRESENT;
1839 	} else {
1840 		/* ensure name is properly terminated */
1841 		env_ind->id.name[ENVMON_MAXNAMELEN - 1] = '\0';
1842 		if ((section == NULL) || (get_sensor_by_name(section,
1843 		    env_ind->id.name, &index)) != 0) {
1844 			env_ind->next_id.name[0] = '\0';
1845 			sensor_status = ENVMON_NOT_PRESENT;
1846 		} else if (index + 1 < section->num_entries)
1847 			env_ind->next_id =
1848 			    section->entry[index + 1].handle_name;
1849 		else
1850 			env_ind->next_id.name[0] = '\0';
1851 	}
1852 	if (sensor_status == ENVMON_SENSOR_OK) {
1853 		/*
1854 		 * user correctly identified a sensor, note its
1855 		 * handle value and request the indicator status
1856 		 */
1857 		rmc_psu->handle = section->entry[index].handle;
1858 		indicator_mask = section->entry[index].ind_mask;
1859 	}
1860 
1861 	RELEASE_CACHE
1862 
1863 	if ((sensor_status == ENVMON_SENSOR_OK) && (rmclomv_rmc_error ||
1864 	    rmclomv_do_cmd(DP_GET_PSU_STATUS, DP_GET_PSU_STATUS_R,
1865 	    sizeof (dp_get_psu_status_r_t), (intptr_t)rmc_psu,
1866 	    (intptr_t)rmc_psu_r) != 0)) {
1867 		sensor_status = ENVMON_INACCESSIBLE;
1868 	}
1869 	if ((env_ind->sensor_status = sensor_status) == ENVMON_SENSOR_OK) {
1870 		/*
1871 		 * copy results into buffer for user
1872 		 */
1873 		if ((rmc_psu_r->psu_status[0].flag & DP_PSU_PRESENCE) == 0)
1874 			env_ind->sensor_status |= ENVMON_NOT_PRESENT;
1875 		if (rmc_psu_r->psu_status[0].sensor_status !=
1876 		    DP_SENSOR_DATA_AVAILABLE)
1877 			env_ind->sensor_status |= ENVMON_INACCESSIBLE;
1878 		env_ind->condition =
1879 		    (rmc_psu_r->psu_status[0].flag & indicator_mask) == 0 ?
1880 		    0 : 1;
1881 	}
1882 
1883 	if (rmclomv_rmc_error != RMCLOMV_RMCERROR_NONE)
1884 		env_ind->sensor_status = ENVMON_INACCESSIBLE;
1885 
1886 	if (ddi_copyout((caddr_t)env_ind, (caddr_t)arg,
1887 	    sizeof (envmon_indicator_t), mode) != 0)
1888 		return (EFAULT);
1889 
1890 	return (0);
1891 }
1892 
1893 /*ARGSUSED*/
1894 static int
1895 rmclomv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred_p,
1896     int *rval_p)
1897 {
1898 	int instance = getminor(dev);
1899 	envmon_sysinfo_t lomv_sysinfo;
1900 	union {
1901 		envmon_sensor_t		u_env_sensor;
1902 		envmon_indicator_t	u_env_ind;
1903 		envmon_fan_t		u_env_fan;
1904 		envmon_led_info_t	u_env_ledinfo;
1905 		envmon_led_ctl_t	u_env_ledctl;
1906 		envmon_hpu_t		u_env_hpu;
1907 		envmon_alarm_info_t	u_env_alarminfo;
1908 		envmon_alarm_ctl_t	u_env_alarmctl;
1909 	} env_buf;
1910 #define	env_sensor	env_buf.u_env_sensor
1911 #define	env_ind		env_buf.u_env_ind
1912 #define	env_fan		env_buf.u_env_fan
1913 #define	env_ledinfo	env_buf.u_env_ledinfo
1914 #define	env_ledctl	env_buf.u_env_ledctl
1915 #define	env_hpu		env_buf.u_env_hpu
1916 #define	env_alarminfo	env_buf.u_env_alarminfo
1917 #define	env_alarmctl	env_buf.u_env_alarmctl
1918 
1919 	union {
1920 		dp_get_volts_t		u_rmc_volts;
1921 		dp_get_temperatures_t	u_rmc_temp;
1922 		dp_get_circuit_brks_t	u_rmc_ampi;
1923 		dp_get_fan_status_t	u_rmc_fan;
1924 		dp_get_psu_status_t	u_rmc_psu;
1925 		dp_get_fru_status_t	u_rmc_fru;
1926 		dp_get_led_state_t	u_rmc_led;
1927 		dp_set_led_state_t	u_rmc_setled;
1928 		dp_get_alarm_state_t	u_rmc_alarm;
1929 		dp_set_alarm_state_t	u_rmc_setalarm;
1930 	} rmc_reqbuf;
1931 #define	rmc_volts	rmc_reqbuf.u_rmc_volts
1932 #define	rmc_temp	rmc_reqbuf.u_rmc_temp
1933 #define	rmc_ampi	rmc_reqbuf.u_rmc_ampi
1934 #define	rmc_fan		rmc_reqbuf.u_rmc_fan
1935 #define	rmc_psu		rmc_reqbuf.u_rmc_psu
1936 #define	rmc_fru		rmc_reqbuf.u_rmc_fru
1937 #define	rmc_led		rmc_reqbuf.u_rmc_led
1938 #define	rmc_setled	rmc_reqbuf.u_rmc_setled
1939 #define	rmc_alarm	rmc_reqbuf.u_rmc_alarm
1940 #define	rmc_setalarm	rmc_reqbuf.u_rmc_setalarm
1941 
1942 	union {
1943 		dp_get_volts_r_t	u_rmc_volts_r;
1944 		dp_get_temperatures_r_t	u_rmc_temp_r;
1945 		dp_get_circuit_brks_r_t	u_rmc_ampi_r;
1946 		dp_get_fan_status_r_t	u_rmc_fan_r;
1947 		dp_get_psu_status_r_t	u_rmc_psu_r;
1948 		dp_get_fru_status_r_t	u_rmc_fru_r;
1949 		dp_get_led_state_r_t	u_rmc_led_r;
1950 		dp_set_led_state_r_t	u_rmc_setled_r;
1951 		dp_get_alarm_state_r_t	u_rmc_alarm_r;
1952 		dp_set_alarm_state_r_t	u_rmc_setalarm_r;
1953 		dp_get_sdp_version_r_t	u_rmc_sdpversion_r;
1954 		dp_get_serialnum_r_t	u_rmc_serialnum_r;
1955 	} rmc_resbuf;
1956 #define	rmc_volts_r	rmc_resbuf.u_rmc_volts_r
1957 #define	rmc_temp_r	rmc_resbuf.u_rmc_temp_r
1958 #define	rmc_ampi_r	rmc_resbuf.u_rmc_ampi_r
1959 #define	rmc_fan_r	rmc_resbuf.u_rmc_fan_r
1960 #define	rmc_psu_r	rmc_resbuf.u_rmc_psu_r
1961 #define	rmc_fru_r	rmc_resbuf.u_rmc_fru_r
1962 #define	rmc_led_r	rmc_resbuf.u_rmc_led_r
1963 #define	rmc_setled_r	rmc_resbuf.u_rmc_setled_r
1964 #define	rmc_alarm_r	rmc_resbuf.u_rmc_alarm_r
1965 #define	rmc_setalarm_r	rmc_resbuf.u_rmc_setalarm_r
1966 #define	rmc_sdpver_r	rmc_resbuf.u_rmc_sdpversion_r
1967 #define	rmc_serialnum_r	rmc_resbuf.u_rmc_serialnum_r
1968 
1969 	int			retval = 0;
1970 	int			special = 0;
1971 	int			index;
1972 	uint16_t		sensor_status;
1973 	rmclomv_cache_section_t	*section;
1974 	envmon_chassis_t chassis;
1975 
1976 	if (instance != 0)
1977 		return (ENXIO);
1978 
1979 	switch (cmd) {
1980 	case ENVMONIOCSYSINFO:
1981 
1982 		LOCK_CACHE
1983 
1984 		/*
1985 		 * A number of OK/not_OK indicators are supported by PSUs
1986 		 * (voltage, current, fan, temperature). So the maximum
1987 		 * number of such indicators relates to the maximum number
1988 		 * of power-supplies.
1989 		 */
1990 		if (rmclomv_sysinfo_valid) {
1991 			lomv_sysinfo.maxVoltSens = rmclomv_sysinfo_data.maxVolt;
1992 			lomv_sysinfo.maxVoltInd =
1993 			    RMCLOMV_MAX_VI_PER_PSU *
1994 			    rmclomv_sysinfo_data.maxPSU;
1995 			/*
1996 			 * the ALOM-Solaris interface does not include
1997 			 * amp sensors, so we can hard code this value
1998 			 */
1999 			lomv_sysinfo.maxAmpSens = 0;
2000 			lomv_sysinfo.maxAmpInd =
2001 			    rmclomv_sysinfo_data.maxCircuitBrks +
2002 			    (RMCLOMV_MAX_CI_PER_PSU *
2003 			    rmclomv_sysinfo_data.maxPSU);
2004 			lomv_sysinfo.maxTempSens = rmclomv_sysinfo_data.maxTemp;
2005 			lomv_sysinfo.maxTempInd =
2006 			    (RMCLOMV_MAX_TI_PER_PSU *
2007 			    rmclomv_sysinfo_data.maxPSU);
2008 			lomv_sysinfo.maxFanSens = rmclomv_sysinfo_data.maxFan;
2009 			lomv_sysinfo.maxFanInd =
2010 			    RMCLOMV_MAX_FI_PER_PSU *
2011 			    rmclomv_sysinfo_data.maxPSU;
2012 			lomv_sysinfo.maxLED = rmclomv_sysinfo_data.maxLED;
2013 			lomv_sysinfo.maxHPU = RMCLOMV_NUM_SPECIAL_FRUS +
2014 			    rmclomv_sysinfo_data.maxFRU;
2015 		} else {
2016 			bzero(&lomv_sysinfo, sizeof (lomv_sysinfo));
2017 			lomv_sysinfo.maxHPU = 1;	/* just the SC node */
2018 		}
2019 
2020 		RELEASE_CACHE
2021 
2022 		if (ddi_copyout((caddr_t)&lomv_sysinfo, (caddr_t)arg,
2023 		    sizeof (lomv_sysinfo), mode) != 0)
2024 			return (EFAULT);
2025 		break;
2026 
2027 	case ENVMONIOCVOLTSENSOR:
2028 		if (ddi_copyin((caddr_t)arg, (caddr_t)&env_sensor,
2029 		    sizeof (envmon_sensor_t), mode) != 0)
2030 			return (EFAULT);
2031 
2032 		/* see if we've got volts handles cached */
2033 		LOCK_CACHE
2034 		sensor_status = ENVMON_SENSOR_OK;
2035 
2036 		if ((rmclomv_cache_valid == B_FALSE) ||
2037 		    ((section = rmclomv_find_section(rmclomv_cache,
2038 		    RMCLOMV_VOLT_SENS)) == NULL)) {
2039 			env_sensor.next_id.name[0] = '\0';
2040 			sensor_status = ENVMON_NOT_PRESENT;
2041 		} else if (env_sensor.id.name[0] == '\0') {
2042 			/* request for first handle */
2043 			if (section->num_entries == 0)
2044 				env_sensor.next_id.name[0] = '\0';
2045 			else
2046 				env_sensor.next_id =
2047 				    section->entry[0].handle_name;
2048 			sensor_status = ENVMON_NOT_PRESENT;
2049 		} else {
2050 			/* ensure name is properly terminated */
2051 			env_sensor.id.name[ENVMON_MAXNAMELEN - 1] = '\0';
2052 			if (get_sensor_by_name(section, env_sensor.id.name,
2053 			    &index) != 0) {
2054 				env_sensor.next_id.name[0] = '\0';
2055 				sensor_status = ENVMON_NOT_PRESENT;
2056 			} else if (index + 1 < section->num_entries)
2057 				env_sensor.next_id =
2058 				    section->entry[index + 1].handle_name;
2059 			else
2060 				env_sensor.next_id.name[0] = '\0';
2061 		}
2062 		if (sensor_status == ENVMON_SENSOR_OK) {
2063 			/*
2064 			 * user correctly identified a sensor, note its
2065 			 * handle value and request the sensor value
2066 			 */
2067 			rmc_volts.handle = section->entry[index].handle;
2068 		}
2069 		RELEASE_CACHE
2070 		if ((sensor_status == ENVMON_SENSOR_OK) && (rmclomv_rmc_error ||
2071 		    rmclomv_do_cmd(DP_GET_VOLTS, DP_GET_VOLTS_R,
2072 		    sizeof (rmc_volts_r), (intptr_t)&rmc_volts,
2073 		    (intptr_t)&rmc_volts_r) != 0)) {
2074 			sensor_status = ENVMON_INACCESSIBLE;
2075 		}
2076 		if ((sensor_status == ENVMON_SENSOR_OK) &&
2077 		    (rmc_volts_r.volt_status[0].sensor_status ==
2078 		    DP_SENSOR_NOT_PRESENT)) {
2079 			sensor_status = ENVMON_NOT_PRESENT;
2080 		}
2081 		if ((env_sensor.sensor_status = sensor_status) ==
2082 		    ENVMON_SENSOR_OK) {
2083 			/*
2084 			 * copy results into buffer for user
2085 			 */
2086 			if (rmc_volts_r.volt_status[0].sensor_status !=
2087 			    DP_SENSOR_DATA_AVAILABLE)
2088 				env_sensor.sensor_status = ENVMON_INACCESSIBLE;
2089 			env_sensor.value =
2090 			    rmc_volts_r.volt_status[0].reading;
2091 			env_sensor.lowthresholds.warning =
2092 			    rmc_volts_r.volt_status[0].low_warning;
2093 			env_sensor.lowthresholds.shutdown =
2094 			    rmc_volts_r.volt_status[0].low_soft_shutdown;
2095 			env_sensor.lowthresholds.poweroff =
2096 			    rmc_volts_r.volt_status[0].low_hard_shutdown;
2097 			env_sensor.highthresholds.warning =
2098 			    rmc_volts_r.volt_status[0].high_warning;
2099 			env_sensor.highthresholds.shutdown =
2100 			    rmc_volts_r.volt_status[0].high_soft_shutdown;
2101 			env_sensor.highthresholds.poweroff =
2102 			    rmc_volts_r.volt_status[0].high_hard_shutdown;
2103 		}
2104 		if (env_sensor.sensor_status != ENVMON_SENSOR_OK ||
2105 		    rmclomv_rmc_error != RMCLOMV_RMCERROR_NONE)
2106 			set_val_unav(&env_sensor);
2107 
2108 		if (ddi_copyout((caddr_t)&env_sensor, (caddr_t)arg,
2109 		    sizeof (envmon_sensor_t), mode) != 0)
2110 			return (EFAULT);
2111 		break;
2112 
2113 	case ENVMONIOCVOLTIND:
2114 		return (do_psu_cmd(arg, mode, &env_ind, &rmc_psu, &rmc_psu_r,
2115 		    RMCLOMV_VOLT_IND));
2116 
2117 	case ENVMONIOCTEMPIND:
2118 		return (do_psu_cmd(arg, mode, &env_ind, &rmc_psu, &rmc_psu_r,
2119 		    RMCLOMV_TEMP_IND));
2120 
2121 	case ENVMONIOCFANIND:
2122 		return (do_psu_cmd(arg, mode, &env_ind, &rmc_psu, &rmc_psu_r,
2123 		    RMCLOMV_FAN_IND));
2124 
2125 	case ENVMONIOCAMPSENSOR:
2126 		if (ddi_copyin((caddr_t)arg, (caddr_t)&env_sensor,
2127 		    sizeof (envmon_sensor_t), mode) != 0)
2128 			return (EFAULT);
2129 
2130 		env_sensor.sensor_status = ENVMON_NOT_PRESENT;
2131 		env_sensor.next_id.name[0] = '\0';
2132 
2133 		if (ddi_copyout((caddr_t)&env_sensor, (caddr_t)arg,
2134 		    sizeof (envmon_sensor_t), mode) != 0)
2135 			return (EFAULT);
2136 		break;
2137 
2138 	case ENVMONIOCTEMPSENSOR:
2139 		if (ddi_copyin((caddr_t)arg, (caddr_t)&env_sensor,
2140 		    sizeof (envmon_sensor_t), mode) != 0)
2141 			return (EFAULT);
2142 
2143 		/* see if we've got temperature handles cached */
2144 		LOCK_CACHE
2145 		sensor_status = ENVMON_SENSOR_OK;
2146 
2147 		if ((rmclomv_cache_valid == B_FALSE) ||
2148 		    ((section = rmclomv_find_section(rmclomv_cache,
2149 		    RMCLOMV_TEMP_SENS)) == NULL)) {
2150 			env_sensor.next_id.name[0] = '\0';
2151 			sensor_status = ENVMON_NOT_PRESENT;
2152 		} else if (env_sensor.id.name[0] == '\0') {
2153 			/* request for first handle */
2154 			if (section->num_entries == 0)
2155 				env_sensor.next_id.name[0] = '\0';
2156 			else
2157 				env_sensor.next_id =
2158 				    section->entry[0].handle_name;
2159 			sensor_status = ENVMON_NOT_PRESENT;
2160 		} else {
2161 			/* ensure name is properly terminated */
2162 			env_sensor.id.name[ENVMON_MAXNAMELEN - 1] = '\0';
2163 			if (get_sensor_by_name(section, env_sensor.id.name,
2164 			    &index) != 0) {
2165 				env_sensor.next_id.name[0] = '\0';
2166 				sensor_status = ENVMON_NOT_PRESENT;
2167 			} else if (index + 1 < section->num_entries)
2168 				env_sensor.next_id =
2169 				    section->entry[index + 1].handle_name;
2170 			else
2171 				env_sensor.next_id.name[0] = '\0';
2172 		}
2173 		if (sensor_status == ENVMON_SENSOR_OK) {
2174 			/*
2175 			 * user correctly identified a sensor, note its
2176 			 * handle value and request the sensor value
2177 			 */
2178 			rmc_temp.handle = section->entry[index].handle;
2179 		}
2180 		RELEASE_CACHE
2181 		if ((sensor_status == ENVMON_SENSOR_OK) && (rmclomv_rmc_error ||
2182 		    rmclomv_do_cmd(DP_GET_TEMPERATURES, DP_GET_TEMPERATURES_R,
2183 		    sizeof (rmc_temp_r), (intptr_t)&rmc_temp,
2184 		    (intptr_t)&rmc_temp_r) != 0)) {
2185 			sensor_status = ENVMON_INACCESSIBLE;
2186 		}
2187 		if ((sensor_status == ENVMON_SENSOR_OK) &&
2188 		    (rmc_temp_r.temp_status[0].sensor_status ==
2189 		    DP_SENSOR_NOT_PRESENT)) {
2190 			sensor_status = ENVMON_NOT_PRESENT;
2191 		}
2192 		if ((env_sensor.sensor_status = sensor_status) ==
2193 		    ENVMON_SENSOR_OK) {
2194 			/*
2195 			 * copy results into buffer for user
2196 			 */
2197 			if (rmc_temp_r.temp_status[0].sensor_status !=
2198 			    DP_SENSOR_DATA_AVAILABLE)
2199 				env_sensor.sensor_status = ENVMON_INACCESSIBLE;
2200 			env_sensor.value =
2201 			    rmc_temp_r.temp_status[0].value;
2202 			env_sensor.lowthresholds.warning =
2203 			    rmc_temp_r.temp_status[0].low_warning;
2204 			env_sensor.lowthresholds.shutdown =
2205 			    rmc_temp_r.temp_status[0].low_soft_shutdown;
2206 			env_sensor.lowthresholds.poweroff =
2207 			    rmc_temp_r.temp_status[0].low_hard_shutdown;
2208 			env_sensor.highthresholds.warning =
2209 			    rmc_temp_r.temp_status[0].high_warning;
2210 			env_sensor.highthresholds.shutdown =
2211 			    rmc_temp_r.temp_status[0].high_soft_shutdown;
2212 			env_sensor.highthresholds.poweroff =
2213 			    rmc_temp_r.temp_status[0].high_hard_shutdown;
2214 		}
2215 		if (env_sensor.sensor_status != ENVMON_SENSOR_OK ||
2216 		    rmclomv_rmc_error != RMCLOMV_RMCERROR_NONE)
2217 			set_val_unav(&env_sensor);
2218 
2219 		if (ddi_copyout((caddr_t)&env_sensor, (caddr_t)arg,
2220 		    sizeof (envmon_sensor_t), mode) != 0)
2221 			return (EFAULT);
2222 		break;
2223 
2224 
2225 	case ENVMONIOCFAN:
2226 		if (ddi_copyin((caddr_t)arg, (caddr_t)&env_fan,
2227 		    sizeof (envmon_fan_t), mode) != 0)
2228 			return (EFAULT);
2229 
2230 		/* see if we've got fan handles cached */
2231 		LOCK_CACHE
2232 		sensor_status = ENVMON_SENSOR_OK;
2233 
2234 		if ((rmclomv_cache_valid == B_FALSE) ||
2235 		    ((section = rmclomv_find_section(rmclomv_cache,
2236 		    RMCLOMV_FAN_SENS)) == NULL)) {
2237 			env_fan.next_id.name[0] = '\0';
2238 			sensor_status = ENVMON_NOT_PRESENT;
2239 		} else if (env_fan.id.name[0] == '\0') {
2240 			/* request for first handle */
2241 			if (section->num_entries == 0)
2242 				env_fan.next_id.name[0] = '\0';
2243 			else
2244 				env_fan.next_id =
2245 				    section->entry[0].handle_name;
2246 			sensor_status = ENVMON_NOT_PRESENT;
2247 		} else {
2248 			/* ensure name is properly terminated */
2249 			env_fan.id.name[ENVMON_MAXNAMELEN - 1] = '\0';
2250 			if (get_sensor_by_name(section, env_fan.id.name,
2251 			    &index) != 0) {
2252 				env_fan.next_id.name[0] = '\0';
2253 				sensor_status = ENVMON_NOT_PRESENT;
2254 			} else if (index + 1 < section->num_entries)
2255 				env_fan.next_id =
2256 				    section->entry[index + 1].handle_name;
2257 			else
2258 				env_fan.next_id.name[0] = '\0';
2259 		}
2260 		if (sensor_status == ENVMON_SENSOR_OK) {
2261 			/*
2262 			 * user correctly identified a sensor, note its
2263 			 * handle value and request the sensor value
2264 			 */
2265 			rmc_fan.handle = section->entry[index].handle;
2266 		}
2267 		RELEASE_CACHE
2268 		if ((sensor_status == ENVMON_SENSOR_OK) && (rmclomv_rmc_error ||
2269 		    rmclomv_do_cmd(DP_GET_FAN_STATUS, DP_GET_FAN_STATUS_R,
2270 		    sizeof (rmc_fan_r), (intptr_t)&rmc_fan,
2271 		    (intptr_t)&rmc_fan_r) != 0)) {
2272 			sensor_status = ENVMON_INACCESSIBLE;
2273 		}
2274 		if ((sensor_status == ENVMON_SENSOR_OK) &&
2275 		    (rmc_fan_r.fan_status[0].sensor_status ==
2276 		    DP_SENSOR_NOT_PRESENT)) {
2277 			sensor_status = ENVMON_NOT_PRESENT;
2278 		}
2279 		if ((env_fan.sensor_status = sensor_status) ==
2280 		    ENVMON_SENSOR_OK) {
2281 			if ((rmc_fan_r.fan_status[0].flag &
2282 			    DP_FAN_PRESENCE) == 0)
2283 				env_fan.sensor_status = ENVMON_NOT_PRESENT;
2284 			if (rmc_fan_r.fan_status[0].sensor_status !=
2285 			    DP_SENSOR_DATA_AVAILABLE)
2286 				env_fan.sensor_status |= ENVMON_INACCESSIBLE;
2287 			if (env_fan.sensor_status == ENVMON_SENSOR_OK) {
2288 				/*
2289 				 * copy results into buffer for user
2290 				 */
2291 				env_fan.speed =
2292 				    rmc_fan_r.fan_status[0].speed;
2293 				env_fan.lowthresholds.warning =
2294 				    rmc_fan_r.fan_status[0].minspeed;
2295 				env_fan.lowthresholds.shutdown =
2296 				    ENVMON_VAL_UNAVAILABLE;
2297 				env_fan.lowthresholds.poweroff =
2298 				    ENVMON_VAL_UNAVAILABLE;
2299 				if ((rmc_fan_r.fan_status[0].flag &
2300 				    DP_FAN_SPEED_VAL_UNIT) == 0)
2301 					bcopy(str_rpm, env_fan.units,
2302 					    sizeof (str_rpm));
2303 				else
2304 					bcopy(str_percent, env_fan.units,
2305 					    sizeof (str_percent));
2306 			}
2307 		}
2308 		if (env_fan.sensor_status != ENVMON_SENSOR_OK ||
2309 		    rmclomv_rmc_error != RMCLOMV_RMCERROR_NONE)
2310 			set_fan_unav(&env_fan);
2311 
2312 		if (ddi_copyout((caddr_t)&env_fan, (caddr_t)arg,
2313 		    sizeof (envmon_fan_t), mode) != 0)
2314 			return (EFAULT);
2315 		break;
2316 
2317 	case ENVMONIOCAMPIND:
2318 		if (ddi_copyin((caddr_t)arg, (caddr_t)&env_ind,
2319 		    sizeof (envmon_indicator_t), mode) != 0)
2320 			return (EFAULT);
2321 
2322 		/* see if we've got amp indicator handles cached */
2323 		LOCK_CACHE
2324 		sensor_status = ENVMON_SENSOR_OK;
2325 
2326 		if ((rmclomv_cache_valid == B_FALSE) ||
2327 		    ((section = rmclomv_find_section(rmclomv_cache,
2328 		    RMCLOMV_AMP_IND)) == NULL)) {
2329 			RELEASE_CACHE
2330 			return (do_psu_cmd(arg, mode, &env_ind, &rmc_psu,
2331 			    &rmc_psu_r, RMCLOMV_AMP_IND));
2332 		} else if (env_ind.id.name[0] == '\0') {
2333 			/* request for first handle */
2334 			if (section->num_entries == 0) {
2335 				RELEASE_CACHE
2336 				return (do_psu_cmd(arg, mode, &env_ind,
2337 				    &rmc_psu, &rmc_psu_r, RMCLOMV_AMP_IND));
2338 			}
2339 			env_ind.next_id = section->entry[0].handle_name;
2340 			sensor_status = ENVMON_NOT_PRESENT;
2341 		} else {
2342 			/* ensure name is properly terminated */
2343 			env_ind.id.name[ENVMON_MAXNAMELEN - 1] = '\0';
2344 			if (get_sensor_by_name(section, env_ind.id.name,
2345 			    &index) != 0) {
2346 				RELEASE_CACHE
2347 				return (do_psu_cmd(arg, mode, &env_ind,
2348 				    &rmc_psu, &rmc_psu_r, RMCLOMV_AMP_IND));
2349 			}
2350 			if (index + 1 < section->num_entries) {
2351 				env_ind.next_id =
2352 				    section->entry[index + 1].handle_name;
2353 			} else {
2354 				rmclomv_cache_section_t	*sub_section =
2355 				    rmclomv_find_section(rmclomv_subcache,
2356 				    RMCLOMV_AMP_IND);
2357 				if ((sub_section == NULL) ||
2358 				    (sub_section->num_entries == 0))
2359 					env_ind.next_id.name[0] = '\0';
2360 				else
2361 					env_ind.next_id =
2362 					    sub_section->entry[0].handle_name;
2363 			}
2364 		}
2365 		if (sensor_status == ENVMON_SENSOR_OK) {
2366 			/*
2367 			 * user correctly identified an indicator, note its
2368 			 * handle value and request the indicator status
2369 			 */
2370 			rmc_ampi.handle = section->entry[index].handle;
2371 		}
2372 		RELEASE_CACHE
2373 		if ((sensor_status == ENVMON_SENSOR_OK) && (rmclomv_rmc_error ||
2374 		    rmclomv_do_cmd(DP_GET_CIRCUIT_BRKS, DP_GET_CIRCUIT_BRKS_R,
2375 		    sizeof (rmc_ampi_r), (intptr_t)&rmc_ampi,
2376 		    (intptr_t)&rmc_ampi_r) != 0)) {
2377 			sensor_status = ENVMON_INACCESSIBLE;
2378 		}
2379 		if ((sensor_status == ENVMON_SENSOR_OK) &&
2380 		    (rmc_ampi_r.circuit_brk_status[0].sensor_status ==
2381 		    DP_SENSOR_NOT_PRESENT)) {
2382 			sensor_status = ENVMON_NOT_PRESENT;
2383 		}
2384 		if ((env_ind.sensor_status = sensor_status) ==
2385 		    ENVMON_SENSOR_OK) {
2386 			/*
2387 			 * copy results into buffer for user
2388 			 */
2389 			if (rmc_ampi_r.circuit_brk_status[0].sensor_status !=
2390 			    DP_SENSOR_DATA_AVAILABLE)
2391 				env_ind.sensor_status = ENVMON_INACCESSIBLE;
2392 			env_ind.condition =
2393 			    rmc_ampi_r.circuit_brk_status[0].status;
2394 		}
2395 
2396 		/*
2397 		 * If rmclomv_rmc_error is set there is no way
2398 		 * that we read information from RSC. Just copy
2399 		 * out an inaccessible evironmental.
2400 		 */
2401 		if (rmclomv_rmc_error != RMCLOMV_RMCERROR_NONE) {
2402 			env_ind.sensor_status = ENVMON_INACCESSIBLE;
2403 			env_ind.condition = ENVMON_INACCESSIBLE;
2404 		}
2405 
2406 		if (ddi_copyout((caddr_t)&env_ind, (caddr_t)arg,
2407 		    sizeof (envmon_indicator_t), mode) != 0)
2408 			return (EFAULT);
2409 		break;
2410 
2411 	case ENVMONIOCHPU:
2412 		if (ddi_copyin((caddr_t)arg, (caddr_t)&env_hpu,
2413 		    sizeof (envmon_hpu_t), mode) != 0)
2414 			return (EFAULT);
2415 
2416 		/* see if we've got hpu handles cached */
2417 		LOCK_CACHE
2418 
2419 		if ((rmclomv_cache_valid == B_FALSE) ||
2420 		    ((section = rmclomv_find_section(rmclomv_cache,
2421 		    RMCLOMV_HPU_IND)) == NULL)) {
2422 			RELEASE_CACHE
2423 			return (EAGAIN);
2424 		}
2425 
2426 		/*
2427 		 * At this point the cache is locked and section points to
2428 		 * the section relating to hpus.
2429 		 */
2430 		sensor_status = ENVMON_SENSOR_OK;
2431 		if (env_hpu.id.name[0] == '\0') {
2432 			/* request for first handle */
2433 			if (section->num_entries == 0)
2434 				env_hpu.next_id.name[0] = '\0';
2435 			else
2436 				env_hpu.next_id =
2437 				    section->entry[0].handle_name;
2438 			sensor_status = ENVMON_NOT_PRESENT;
2439 		} else {
2440 			/* ensure name is properly terminated */
2441 			env_hpu.id.name[ENVMON_MAXNAMELEN - 1] = '\0';
2442 			if (get_sensor_by_name(section, env_hpu.id.name,
2443 			    &index) != 0) {
2444 				env_hpu.next_id.name[0] = '\0';
2445 				sensor_status = ENVMON_NOT_PRESENT;
2446 			} else if (index + 1 < section->num_entries)
2447 				env_hpu.next_id =
2448 				    section->entry[index + 1].handle_name;
2449 			else
2450 				env_hpu.next_id.name[0] = '\0';
2451 		}
2452 		if (sensor_status == ENVMON_SENSOR_OK) {
2453 			/*
2454 			 * user correctly identified an hpu, note its
2455 			 * handle value and request the hpu status
2456 			 */
2457 			rmc_fru.handle = section->entry[index].handle;
2458 			special = section->entry[index].ind_mask;
2459 		}
2460 		RELEASE_CACHE
2461 		if ((env_hpu.sensor_status = sensor_status) ==
2462 		    ENVMON_SENSOR_OK) {
2463 			env_hpu.fru_status = ENVMON_FRU_PRESENT;
2464 
2465 			if (special != 0) {
2466 				/* this is the pseudo SC node */
2467 				mutex_enter(&rmclomv_state_lock);
2468 				switch (rmclomv_rmc_state) {
2469 				case RMCLOMV_RMCSTATE_OK:
2470 					break;
2471 				case RMCLOMV_RMCSTATE_FAILED:
2472 					env_hpu.fru_status = ENVMON_FRU_FAULT;
2473 					break;
2474 				case RMCLOMV_RMCSTATE_DOWNLOAD:
2475 					env_hpu.fru_status =
2476 					    ENVMON_FRU_DOWNLOAD;
2477 					break;
2478 				default:
2479 					env_hpu.sensor_status =
2480 					    ENVMON_INACCESSIBLE;
2481 					break;
2482 				}
2483 				mutex_exit(&rmclomv_state_lock);
2484 			} else if (rmclomv_rmc_error ||
2485 			    rmclomv_do_cmd(DP_GET_FRU_STATUS,
2486 			    DP_GET_FRU_STATUS_R, sizeof (rmc_fru_r),
2487 			    (intptr_t)&rmc_fru, (intptr_t)&rmc_fru_r) != 0) {
2488 				env_hpu.sensor_status = ENVMON_INACCESSIBLE;
2489 			} else {
2490 				/*
2491 				 * copy results into buffer for user
2492 				 */
2493 				if (rmc_fru_r.fru_status[0].presence == 0) {
2494 					env_hpu.sensor_status =
2495 					    ENVMON_NOT_PRESENT;
2496 					env_hpu.fru_status =
2497 					    ENVMON_FRU_NOT_PRESENT;
2498 				} else if (rmc_fru_r.fru_status[0].sensor_status
2499 				    != DP_SENSOR_DATA_AVAILABLE) {
2500 					env_hpu.sensor_status =
2501 					    ENVMON_INACCESSIBLE;
2502 				} else {
2503 					uint8_t status =
2504 					    rmc_fru_r.fru_status[0].status;
2505 					if (status == DP_FRU_STATUS_UNKNOWN) {
2506 						env_hpu.sensor_status =
2507 						    ENVMON_INACCESSIBLE;
2508 					} else if (status != DP_FRU_STATUS_OK) {
2509 						env_hpu.fru_status =
2510 						    ENVMON_FRU_FAULT;
2511 					}
2512 				}
2513 			}
2514 		}
2515 
2516 		/*
2517 		 * If rmclomv_rmc_error is set there is no way
2518 		 * that we read information from RSC. Just copy
2519 		 * out an inaccessible environmental.
2520 		 */
2521 		if (rmclomv_rmc_error != RMCLOMV_RMCERROR_NONE) {
2522 			env_hpu.sensor_status = ENVMON_INACCESSIBLE;
2523 			env_hpu.fru_status = ENVMON_INACCESSIBLE;
2524 		}
2525 
2526 		if (ddi_copyout((caddr_t)&env_hpu, (caddr_t)arg,
2527 		    sizeof (envmon_hpu_t), mode) != 0)
2528 			return (EFAULT);
2529 		break;
2530 
2531 	case ENVMONIOCGETLED:
2532 		if (ddi_copyin((caddr_t)arg, (caddr_t)&env_ledinfo,
2533 		    sizeof (envmon_led_info_t), mode) != 0)
2534 			return (EFAULT);
2535 
2536 		/* see if we've got LED handles cached */
2537 		LOCK_CACHE
2538 		sensor_status = ENVMON_SENSOR_OK;
2539 
2540 		if ((rmclomv_cache_valid == B_FALSE) ||
2541 		    ((section = rmclomv_find_section(rmclomv_cache,
2542 		    RMCLOMV_LED_IND)) == NULL)) {
2543 			env_ledinfo.next_id.name[0] = '\0';
2544 			sensor_status = ENVMON_NOT_PRESENT;
2545 		} else if (env_ledinfo.id.name[0] == '\0') {
2546 			/* request for first handle */
2547 			if (section->num_entries == 0)
2548 				env_ledinfo.next_id.name[0] = '\0';
2549 			else
2550 				env_ledinfo.next_id =
2551 				    section->entry[0].handle_name;
2552 			sensor_status = ENVMON_NOT_PRESENT;
2553 		} else {
2554 			/* ensure name is properly terminated */
2555 			env_ledinfo.id.name[ENVMON_MAXNAMELEN - 1] = '\0';
2556 			if (get_sensor_by_name(section, env_ledinfo.id.name,
2557 			    &index) != 0) {
2558 				env_ledinfo.next_id.name[0] = '\0';
2559 				sensor_status = ENVMON_NOT_PRESENT;
2560 			} else if (index + 1 < section->num_entries)
2561 				env_ledinfo.next_id =
2562 				    section->entry[index + 1].handle_name;
2563 			else
2564 				env_ledinfo.next_id.name[0] = '\0';
2565 		}
2566 		if (sensor_status == ENVMON_SENSOR_OK) {
2567 			/*
2568 			 * user correctly identified a LED, note its
2569 			 * handle value and request the LED status
2570 			 */
2571 			rmc_led.handle = section->entry[index].handle;
2572 		}
2573 		RELEASE_CACHE
2574 		if ((sensor_status == ENVMON_SENSOR_OK) && (rmclomv_rmc_error ||
2575 		    rmclomv_do_cmd(DP_GET_LED_STATE, DP_GET_LED_STATE_R,
2576 		    sizeof (rmc_led_r), (intptr_t)&rmc_led,
2577 		    (intptr_t)&rmc_led_r) != 0)) {
2578 			sensor_status = ENVMON_INACCESSIBLE;
2579 		}
2580 		if ((sensor_status == ENVMON_SENSOR_OK) &&
2581 		    (rmc_led_r.led_state[0].sensor_status ==
2582 		    DP_SENSOR_NOT_PRESENT)) {
2583 			sensor_status = ENVMON_NOT_PRESENT;
2584 		}
2585 		if ((env_ledinfo.sensor_status = sensor_status) ==
2586 		    ENVMON_SENSOR_OK) {
2587 			/*
2588 			 * copy results into buffer for user
2589 			 * start with some defaults then override
2590 			 */
2591 			env_ledinfo.sensor_status = ENVMON_SENSOR_OK;
2592 			env_ledinfo.led_state = ENVMON_LED_OFF;
2593 			env_ledinfo.led_color = ENVMON_LED_CLR_NONE;
2594 
2595 			if (rmc_led_r.led_state[0].sensor_status !=
2596 			    DP_SENSOR_DATA_AVAILABLE)
2597 				env_ledinfo.sensor_status = ENVMON_INACCESSIBLE;
2598 			else {
2599 				dp_led_state_t ledState;
2600 				ledState = rmc_led_r.led_state[0];
2601 				env_ledinfo.led_color = (int8_t)ledState.colour;
2602 
2603 				switch (ledState.state) {
2604 				case (rsci8)DP_LED_OFF:
2605 					break;
2606 				case (rsci8)DP_LED_ON:
2607 					env_ledinfo.led_state = ENVMON_LED_ON;
2608 					break;
2609 				case (rsci8)DP_LED_BLINKING:
2610 					env_ledinfo.led_state =
2611 					    ENVMON_LED_BLINKING;
2612 					break;
2613 				case (rsci8)DP_LED_FLASHING:
2614 					env_ledinfo.led_state =
2615 					    ENVMON_LED_FLASHING;
2616 					break;
2617 				default:
2618 					break;
2619 				}
2620 			}
2621 		}
2622 
2623 		/*
2624 		 * If rmclomv_rmc_error is set there is no way
2625 		 * that we read information from RSC. Just copy
2626 		 * out an inaccessible environmental.
2627 		 */
2628 		if (rmclomv_rmc_error != RMCLOMV_RMCERROR_NONE) {
2629 			env_ledinfo.sensor_status = ENVMON_INACCESSIBLE;
2630 			env_ledinfo.led_state = ENVMON_INACCESSIBLE;
2631 		}
2632 
2633 		if (ddi_copyout((caddr_t)&env_ledinfo, (caddr_t)arg,
2634 		    sizeof (envmon_led_info_t), mode) != 0)
2635 			return (EFAULT);
2636 		break;
2637 
2638 	case ENVMONIOCSETLED:
2639 		if ((mode & FWRITE) == 0)
2640 			return (EACCES);
2641 		if (drv_priv(cred_p) != 0)
2642 			return (EPERM);
2643 		if (ddi_copyin((caddr_t)arg, (caddr_t)&env_ledctl,
2644 		    sizeof (envmon_led_ctl_t), mode) != 0)
2645 			return (EFAULT);
2646 		if (env_ledctl.led_state < RMCLOMV_MIN_LED_STATE ||
2647 		    env_ledctl.led_state > RMCLOMV_MAX_LED_STATE)
2648 			return (EINVAL);
2649 		/*
2650 		 * Ensure name is properly terminated.
2651 		 */
2652 		env_ledctl.id.name[ENVMON_MAXNAMELEN - 1] = '\0';
2653 
2654 		/* see if we've got LED handles cached */
2655 		LOCK_CACHE
2656 
2657 		if ((rmclomv_cache_valid == B_FALSE) ||
2658 		    ((section = rmclomv_find_section(rmclomv_cache,
2659 		    RMCLOMV_LED_IND)) == NULL) ||
2660 		    (get_sensor_by_name(section, env_ledctl.id.name,
2661 		    &index) != 0)) {
2662 			RELEASE_CACHE
2663 			return (EINVAL);	/* no such LED */
2664 		}
2665 		/*
2666 		 * user correctly identified a LED, note its handle value
2667 		 */
2668 		rmc_setled.handle = section->entry[index].handle;
2669 		RELEASE_CACHE
2670 		switch (env_ledctl.led_state) {
2671 		case ENVMON_LED_ON:
2672 			rmc_setled.state = DP_LED_ON;
2673 			break;
2674 		case ENVMON_LED_BLINKING:
2675 			rmc_setled.state = DP_LED_BLINKING;
2676 			break;
2677 		case ENVMON_LED_FLASHING:
2678 			rmc_setled.state = DP_LED_FLASHING;
2679 			break;
2680 		default:
2681 			rmc_setled.state = DP_LED_OFF;
2682 			break;
2683 		}
2684 		retval = rmclomv_do_cmd(DP_SET_LED_STATE, DP_SET_LED_STATE_R,
2685 		    sizeof (rmc_setled_r), (intptr_t)&rmc_setled,
2686 		    (intptr_t)&rmc_setled_r);
2687 
2688 		if (retval != 0) {
2689 			break;
2690 		}
2691 
2692 		if (rmc_setled_r.status != 0) {
2693 			cmn_err(CE_WARN, "ENVMONIOCSETLED: \"%s\" status: 0x%x",
2694 			    env_ledctl.id.name, rmc_setled_r.status);
2695 			return (EIO);
2696 		}
2697 		break;
2698 
2699 	case ENVMONIOCGETKEYSW:
2700 	{
2701 		enum rmc_keyswitch_pos	rmc_pos = real_key_position;
2702 		envmon_keysw_pos_t	envmon_pos;
2703 
2704 		/*
2705 		 * Yes, I know this is ugly, but the V210 has no keyswitch,
2706 		 * even though the ALOM returns a value for it
2707 		 */
2708 		if (strcmp(platform, "SUNW,Sun-Fire-V210") == 0) {
2709 			return (ENOTSUP);
2710 		}
2711 
2712 		switch (rmc_pos) {
2713 
2714 		case RMC_KEYSWITCH_POS_NORMAL:
2715 			envmon_pos = ENVMON_KEYSW_POS_NORMAL;
2716 			break;
2717 		case RMC_KEYSWITCH_POS_DIAG:
2718 			envmon_pos = ENVMON_KEYSW_POS_DIAG;
2719 			break;
2720 		case RMC_KEYSWITCH_POS_LOCKED:
2721 			envmon_pos = ENVMON_KEYSW_POS_LOCKED;
2722 			break;
2723 		case RMC_KEYSWITCH_POS_OFF:
2724 			envmon_pos = ENVMON_KEYSW_POS_OFF;
2725 			break;
2726 		default:
2727 			envmon_pos = ENVMON_KEYSW_POS_UNKNOWN;
2728 			break;
2729 		}
2730 
2731 		if (ddi_copyout((caddr_t)&envmon_pos, (caddr_t)arg,
2732 		    sizeof (envmon_pos), mode) != 0)
2733 			return (EFAULT);
2734 		break;
2735 	}
2736 
2737 	case ENVMONIOCGETALARM:
2738 		if (ddi_copyin((caddr_t)arg, (caddr_t)&env_alarminfo,
2739 		    sizeof (envmon_alarm_info_t), mode) != 0)
2740 			return (EFAULT);
2741 
2742 		/* see if we've got ALARM handles cached */
2743 		LOCK_CACHE
2744 		sensor_status = ENVMON_SENSOR_OK;
2745 
2746 		if ((rmclomv_cache_valid == B_FALSE) ||
2747 		    ((section = rmclomv_find_section(rmclomv_cache,
2748 		    RMCLOMV_ALARM_IND)) == NULL)) {
2749 			env_alarminfo.next_id.name[0] = '\0';
2750 			sensor_status = ENVMON_NOT_PRESENT;
2751 		} else if (env_alarminfo.id.name[0] == '\0') {
2752 			/* request for first handle */
2753 			if (section->num_entries == 0)
2754 				env_alarminfo.next_id.name[0] = '\0';
2755 			else
2756 				env_alarminfo.next_id =
2757 				    section->entry[0].handle_name;
2758 			sensor_status = ENVMON_NOT_PRESENT;
2759 		} else {
2760 			/* ensure name is properly terminated */
2761 			env_alarminfo.id.name[ENVMON_MAXNAMELEN - 1] = '\0';
2762 			if (get_sensor_by_name(section, env_alarminfo.id.name,
2763 			    &index) != 0) {
2764 				env_alarminfo.next_id.name[0] = '\0';
2765 				sensor_status = ENVMON_NOT_PRESENT;
2766 			} else if (index + 1 < section->num_entries)
2767 				env_alarminfo.next_id =
2768 				    section->entry[index + 1].handle_name;
2769 			else
2770 				env_alarminfo.next_id.name[0] = '\0';
2771 		}
2772 		if (sensor_status == ENVMON_SENSOR_OK) {
2773 			/*
2774 			 * user correctly identified a ALARM, note its
2775 			 * handle value and request the ALARM status
2776 			 */
2777 			rmc_alarm.handle = section->entry[index].handle;
2778 		}
2779 		RELEASE_CACHE
2780 		if ((sensor_status == ENVMON_SENSOR_OK) &&
2781 		    (rmclomv_rmc_error ||
2782 		    rmclomv_do_cmd(DP_GET_ALARM_STATE, DP_GET_ALARM_STATE_R,
2783 		    sizeof (rmc_alarm_r), (intptr_t)&rmc_alarm,
2784 		    (intptr_t)&rmc_alarm_r) != 0)) {
2785 			sensor_status = ENVMON_INACCESSIBLE;
2786 		}
2787 		if ((env_alarminfo.sensor_status = sensor_status) ==
2788 		    ENVMON_SENSOR_OK) {
2789 			/*
2790 			 * copy results into buffer for user
2791 			 * start with some defaults then override
2792 			 */
2793 			env_alarminfo.sensor_status = ENVMON_SENSOR_OK;
2794 			env_alarminfo.alarm_state = ENVMON_ALARM_OFF;
2795 
2796 			if (rmc_alarm_r.alarm_state[0].sensor_status !=
2797 			    DP_SENSOR_DATA_AVAILABLE)
2798 				env_alarminfo.sensor_status =
2799 				    ENVMON_INACCESSIBLE;
2800 			else {
2801 				dp_alarm_state_t alarmState;
2802 				alarmState = rmc_alarm_r.alarm_state[0];
2803 
2804 				switch (alarmState.state) {
2805 				case DP_ALARM_OFF:
2806 					break;
2807 				case DP_ALARM_ON:
2808 					env_alarminfo.alarm_state =
2809 					    ENVMON_ALARM_ON;
2810 					break;
2811 				default:
2812 					break;
2813 				}
2814 			}
2815 		}
2816 
2817 		/*
2818 		 * If rmclomv_rmc_error is set there is no way
2819 		 * that we read information from RSC. Just copy
2820 		 * out an inaccessible environmental.
2821 		 */
2822 		if (rmclomv_rmc_error != RMCLOMV_RMCERROR_NONE) {
2823 			env_alarminfo.sensor_status = ENVMON_INACCESSIBLE;
2824 			env_alarminfo.alarm_state = ENVMON_INACCESSIBLE;
2825 		}
2826 
2827 		if (ddi_copyout((caddr_t)&env_alarminfo, (caddr_t)arg,
2828 		    sizeof (envmon_alarm_info_t), mode) != 0)
2829 			return (EFAULT);
2830 		break;
2831 
2832 	case ENVMONIOCSETALARM:
2833 		if ((mode & FWRITE) == 0)
2834 			return (EACCES);
2835 		if (drv_priv(cred_p) != 0)
2836 			return (EPERM);
2837 		if (ddi_copyin((caddr_t)arg, (caddr_t)&env_alarmctl,
2838 		    sizeof (envmon_alarm_ctl_t), mode) != 0)
2839 			return (EFAULT);
2840 		if (env_alarmctl.alarm_state < RMCLOMV_MIN_ALARM_STATE ||
2841 		    env_alarmctl.alarm_state > RMCLOMV_MAX_ALARM_STATE)
2842 			return (EINVAL);
2843 		/*
2844 		 * Ensure name is properly terminated.
2845 		 */
2846 		env_alarmctl.id.name[ENVMON_MAXNAMELEN - 1] = '\0';
2847 
2848 		/* see if we've got ALARM handles cached */
2849 		LOCK_CACHE
2850 
2851 		if ((rmclomv_cache_valid == B_FALSE) ||
2852 		    ((section = rmclomv_find_section(rmclomv_cache,
2853 		    RMCLOMV_ALARM_IND)) == NULL) ||
2854 		    (get_sensor_by_name(section, env_alarmctl.id.name,
2855 		    &index) != 0)) {
2856 			RELEASE_CACHE
2857 			return (EINVAL);	/* no such ALARM */
2858 		}
2859 		/*
2860 		 * user correctly identified a ALARM, note its handle value
2861 		 */
2862 		rmc_setalarm.handle = section->entry[index].handle;
2863 		RELEASE_CACHE
2864 		rmc_setalarm.state = (rsci8)env_alarmctl.alarm_state;
2865 		retval = rmclomv_do_cmd(DP_SET_ALARM_STATE,
2866 		    DP_SET_ALARM_STATE_R,
2867 		    sizeof (rmc_setalarm_r),
2868 		    (intptr_t)&rmc_setalarm,
2869 		    (intptr_t)&rmc_setalarm_r);
2870 
2871 		if (retval != 0) {
2872 			break;
2873 		}
2874 
2875 		if (rmc_setalarm_r.status != 0) {
2876 			cmn_err(CE_WARN, "ENVMONIOCSETALARM: \"%s\" status: "
2877 			    "0x%x", env_alarmctl.id.name,
2878 			    rmc_setalarm_r.status);
2879 			return (EIO);
2880 		}
2881 		break;
2882 
2883 	case ENVMONIOCCHASSISSERIALNUM:
2884 		retval = rmclomv_do_cmd(DP_GET_SDP_VERSION,
2885 		    DP_GET_SDP_VERSION_R, sizeof (rmc_sdpver_r),
2886 		    NULL, (intptr_t)&rmc_sdpver_r);
2887 
2888 		if (retval != 0) {
2889 			cmn_err(CE_WARN, "DP_GET_SDP_VERSION failed, ret=%d\n",
2890 			    retval);
2891 			break;
2892 		} else if (rmc_sdpver_r.version < SDP_RESPONDS_TO_ALL_CMDS) {
2893 			retval = ENOTSUP;
2894 			break;
2895 		}
2896 		retval = rmclomv_do_cmd(DP_GET_CHASSIS_SERIALNUM,
2897 		    DP_GET_CHASSIS_SERIALNUM_R, sizeof (rmc_serialnum_r),
2898 		    NULL, (intptr_t)&rmc_serialnum_r);
2899 
2900 		if (retval != 0) {
2901 			break;
2902 		}
2903 		bcopy(rmc_serialnum_r.chassis_serial_number,
2904 		    chassis.serial_number,
2905 		    sizeof (rmc_serialnum_r.chassis_serial_number));
2906 
2907 		if (ddi_copyout((caddr_t)&chassis, (caddr_t)arg,
2908 		    sizeof (chassis), mode) != 0) {
2909 			return (EFAULT);
2910 		}
2911 		sensor_status = ENVMON_SENSOR_OK;
2912 		break;
2913 
2914 	default:
2915 		retval = ENOTSUP;
2916 		break;
2917 	}
2918 
2919 	return (retval);
2920 }
2921 
2922 /* ARGSUSED */
2923 static void
2924 rmclomv_checkrmc(caddr_t arg)
2925 {
2926 	callb_cpr_t		cprinfo;
2927 	int			err;
2928 	int			retries;
2929 	int			state;
2930 	dp_get_sysinfo_r_t 	sysinfo;
2931 
2932 	CALLB_CPR_INIT(&cprinfo, &rmclomv_checkrmc_lock, callb_generic_cpr,
2933 	    "rmclomv_checkrmc");
2934 
2935 	mutex_enter(&rmclomv_checkrmc_lock);
2936 	for (;;) {
2937 		/*
2938 		 * Initial entry to this for loop is made with
2939 		 * rmclomv_checkrmc_sig set to RMCLOMV_PROCESS_NOW. So the
2940 		 * following while loop drops through the first time. A
2941 		 * timeout call is made just before polling the RMC. Its
2942 		 * interrupt routine sustains this loop by injecting additional
2943 		 * state changes and cv events.
2944 		 */
2945 		/*
2946 		 * Wait for someone to tell me to continue.
2947 		 */
2948 		while (rmclomv_checkrmc_sig == RMCLOMV_CHECKRMC_WAIT) {
2949 			CALLB_CPR_SAFE_BEGIN(&cprinfo);
2950 			cv_wait(&rmclomv_checkrmc_sig_cv,
2951 			    &rmclomv_checkrmc_lock);
2952 			CALLB_CPR_SAFE_END(&cprinfo, &rmclomv_checkrmc_lock);
2953 		}
2954 
2955 		mutex_exit(&rmclomv_checkrmc_lock);
2956 		/*
2957 		 * mustn't hold same lock as timeout called with
2958 		 * when cancelling timer
2959 		 */
2960 		if (timer_id != 0) {
2961 			(void) untimeout(timer_id);
2962 			timer_id = 0;
2963 		}
2964 		mutex_enter(&rmclomv_checkrmc_lock);
2965 
2966 		/* RMCLOMV_CHECKRMC_EXITNOW implies signal by _detach(). */
2967 		if (rmclomv_checkrmc_sig == RMCLOMV_CHECKRMC_EXITNOW) {
2968 			rmclomv_checkrmc_sig = RMCLOMV_CHECKRMC_WAIT;
2969 
2970 			/* rmclomv_checkrmc_lock is held at this point! */
2971 			CALLB_CPR_EXIT(&cprinfo);
2972 
2973 			thread_exit();
2974 			/* NOTREACHED */
2975 		}
2976 
2977 		rmclomv_checkrmc_sig = RMCLOMV_CHECKRMC_WAIT;
2978 
2979 		/*
2980 		 * If the RMC is not responding, rmclomv_do_cmd() takes a
2981 		 * long time and eventually times out. We conclude that the
2982 		 * RMC is broken if it doesn't respond to a number of polls
2983 		 * made 60 secs apart. So that the rmclomv_do_cmd() time-out
2984 		 * period isn't added to our 60 second timer, make the
2985 		 * timeout() call before calling rmclomv_do_cmd().
2986 		 */
2987 		if (timer_id == 0) {
2988 			timer_id = timeout(rmclomv_checkrmc_wakeup, NULL,
2989 			    60 * drv_usectohz(1000000));
2990 		}
2991 
2992 		mutex_exit(&rmclomv_checkrmc_lock);
2993 
2994 		err = rmclomv_do_cmd(DP_GET_SYSINFO, DP_GET_SYSINFO_R,
2995 		    sizeof (sysinfo), NULL, (intptr_t)&sysinfo);
2996 		if (err == 0) {
2997 			mutex_enter(&rmclomv_state_lock);
2998 			state = rmclomv_rmc_state;
2999 			/* successful poll, reset fail count */
3000 			rmclomv_rmcfailcount = 0;
3001 			mutex_exit(&rmclomv_state_lock);
3002 
3003 			if (state != RMCLOMV_RMCSTATE_OK) {
3004 				rmclomv_refresh_wakeup();
3005 			}
3006 		}
3007 		if ((err != 0) &&
3008 		    (rmclomv_rmc_error != RMCLOMV_RMCSTATE_DOWNLOAD)) {
3009 			/*
3010 			 * Failed response or no response from RMC.
3011 			 * Count the failure.
3012 			 * If threshold exceeded, send a DR event.
3013 			 */
3014 			mutex_enter(&rmclomv_state_lock);
3015 			retries = rmclomv_rmcfailcount;
3016 			state = rmclomv_rmc_state;
3017 			if (retries == RMCLOMV_RMCFAILTHRESHOLD)
3018 				rmclomv_rmc_state = RMCLOMV_RMCSTATE_FAILED;
3019 			if (rmclomv_rmcfailcount <= RMCLOMV_RMCFAILTHRESHOLD)
3020 				rmclomv_rmcfailcount++;
3021 			mutex_exit(&rmclomv_state_lock);
3022 
3023 			if (retries == RMCLOMV_RMCFAILTHRESHOLD) {
3024 				cmn_err(CE_WARN, "SC %s responding",
3025 				    state == RMCLOMV_RMCSTATE_OK ?
3026 				    "has stopped" : "is not");
3027 				refresh_name_cache(B_TRUE);
3028 				rmclomv_dr_data_handler(str_sc, SE_NO_HINT);
3029 			}
3030 		}
3031 
3032 		/*
3033 		 * Re-enter the lock to prepare for another iteration.
3034 		 * We must have the lock here to protect rmclomv_checkrmc_sig.
3035 		 */
3036 		mutex_enter(&rmclomv_checkrmc_lock);
3037 	}
3038 }
3039 
3040 static void
3041 rmclomv_checkrmc_start(void)
3042 {
3043 	kthread_t *tp;
3044 
3045 	mutex_enter(&rmclomv_checkrmc_lock);
3046 
3047 	if (rmclomv_checkrmc_tid == 0) {
3048 		rmclomv_checkrmc_sig = RMCLOMV_CHECKRMC_PROCESSNOW;
3049 
3050 		tp = thread_create(NULL, 0, rmclomv_checkrmc, NULL, 0,
3051 		    &p0, TS_RUN, maxclsyspri);
3052 		rmclomv_checkrmc_tid = tp->t_did;
3053 	}
3054 
3055 	mutex_exit(&rmclomv_checkrmc_lock);
3056 }
3057 
3058 static void
3059 rmclomv_checkrmc_destroy(void)
3060 {
3061 	kt_did_t tid;
3062 
3063 	mutex_enter(&rmclomv_checkrmc_lock);
3064 	tid = rmclomv_checkrmc_tid;
3065 	if (tid != 0) {
3066 		rmclomv_checkrmc_sig = RMCLOMV_CHECKRMC_EXITNOW;
3067 		cv_signal(&rmclomv_checkrmc_sig_cv);
3068 		rmclomv_checkrmc_tid = 0;
3069 	}
3070 	mutex_exit(&rmclomv_checkrmc_lock);
3071 
3072 	/*
3073 	 * Wait for rmclomv_checkrmc() to finish
3074 	 */
3075 	if (tid != 0)
3076 		thread_join(tid);
3077 }
3078 
3079 /*ARGSUSED*/
3080 static void
3081 rmclomv_checkrmc_wakeup(void *arg)
3082 {
3083 	mutex_enter(&rmclomv_checkrmc_lock);
3084 
3085 	if (rmclomv_checkrmc_sig != RMCLOMV_CHECKRMC_EXITNOW)
3086 		rmclomv_checkrmc_sig = RMCLOMV_CHECKRMC_PROCESSNOW;
3087 	cv_signal(&rmclomv_checkrmc_sig_cv);
3088 
3089 	mutex_exit(&rmclomv_checkrmc_lock);
3090 }
3091 
3092 /* ARGSUSED */
3093 static void
3094 rmclomv_refresh(caddr_t arg)
3095 {
3096 	void			(*plat_nodename_set_fun)(void);
3097 	sig_state_t		*current_sgn_p;
3098 	callb_cpr_t		cprinfo;
3099 	int			state;
3100 	int			tmp_checkrmc_sig;
3101 
3102 	CALLB_CPR_INIT(&cprinfo, &rmclomv_refresh_lock, callb_generic_cpr,
3103 	    "rmclomv_refresh");
3104 
3105 	/*
3106 	 * Wait until the rmclomv_checkrmc() thread has had a chance to
3107 	 * run its main loop.  This is done so that rmclomv_refresh will
3108 	 * only run its main loop once at start of day; otherwise, it may
3109 	 * run twice and generate warning messages when redundantly populating
3110 	 * its internal cache.
3111 	 */
3112 	do {
3113 		delay(drv_usectohz(DELAY_TIME));
3114 		mutex_enter(&rmclomv_checkrmc_lock);
3115 		tmp_checkrmc_sig = rmclomv_checkrmc_sig;
3116 		mutex_exit(&rmclomv_checkrmc_lock);
3117 	} while (tmp_checkrmc_sig != RMCLOMV_CHECKRMC_WAIT);
3118 
3119 	mutex_enter(&rmclomv_refresh_lock);
3120 	for (;;) {
3121 
3122 		/*
3123 		 * Wait for someone to tell me to continue.
3124 		 */
3125 		while (rmclomv_refresh_sig == RMCLOMV_REFRESH_WAIT) {
3126 			CALLB_CPR_SAFE_BEGIN(&cprinfo);
3127 			cv_wait(&rmclomv_refresh_sig_cv, &rmclomv_refresh_lock);
3128 			CALLB_CPR_SAFE_END(&cprinfo, &rmclomv_refresh_lock);
3129 		}
3130 
3131 		/* RMCLOMV_REFRESH_EXITNOW implies signal by _detach(). */
3132 		if (rmclomv_refresh_sig == RMCLOMV_REFRESH_EXITNOW) {
3133 			rmclomv_refresh_sig = RMCLOMV_REFRESH_WAIT;
3134 
3135 			/* rmclomv_refresh_lock is held at this point! */
3136 			CALLB_CPR_EXIT(&cprinfo);
3137 
3138 			thread_exit();
3139 			/* NOTREACHED */
3140 		}
3141 
3142 		ASSERT(rmclomv_refresh_sig == RMCLOMV_REFRESH_PROCESSNOW);
3143 		rmclomv_refresh_sig = RMCLOMV_REFRESH_WAIT;
3144 
3145 		mutex_exit(&rmclomv_refresh_lock);
3146 
3147 		refresh_name_cache(B_FALSE);
3148 
3149 		/*
3150 		 * We're not going to access rmclomv_sysinfo_data here,
3151 		 * so there's no point in locking it before reading
3152 		 * rmclomv_sysinfo_valid. Also this avoids holding two
3153 		 * locks at once and the concommitant worry about deadlocks.
3154 		 */
3155 		if (rmclomv_sysinfo_valid) {
3156 			/*
3157 			 * We've just successfully read the RMC sysinfo
3158 			 * so the RMC must be operational. Update its
3159 			 * state and if it was previously not OK, refresh
3160 			 * nodename, CPU signatures and watchdog settings.
3161 			 */
3162 			mutex_enter(&rmclomv_state_lock);
3163 			rmclomv_rmcfailcount = 0;
3164 			state = rmclomv_rmc_state;
3165 			rmclomv_rmc_state = RMCLOMV_RMCSTATE_OK;
3166 			mutex_exit(&rmclomv_state_lock);
3167 
3168 			if (state != RMCLOMV_RMCSTATE_OK) {
3169 				rmclomv_dr_data_handler(str_sc, SE_NO_HINT);
3170 				if (state == RMCLOMV_RMCSTATE_FAILED) {
3171 					cmn_err(CE_NOTE, "SC recovered");
3172 				}
3173 			}
3174 
3175 			if (utsname.nodename[0] != 0) {
3176 				plat_nodename_set_fun =
3177 				    (void (*)(void))modgetsymvalue(
3178 				    "plat_nodename_set", 0);
3179 				if (plat_nodename_set_fun != NULL)
3180 					plat_nodename_set_fun();
3181 			}
3182 
3183 			current_sgn_p = (sig_state_t *)modgetsymvalue(
3184 			    "current_sgn", 0);
3185 
3186 			/*
3187 			 * Delay before calling CPU_SIGNATURE, to allow
3188 			 * any pending asynchronous communications (i.e.
3189 			 * plat_timesync()) to complete.  This helps to
3190 			 * prevent the situation where the message associated
3191 			 * with the CPU_SIGNATURE state cannot be sent to the
3192 			 * system controller.
3193 			 */
3194 			if ((current_sgn_p != NULL) &&
3195 			    (current_sgn_p->state_t.sig != 0)) {
3196 				delay(drv_usectohz(CPU_SIGNATURE_DELAY_TIME));
3197 				CPU_SIGNATURE(current_sgn_p->state_t.sig,
3198 				    current_sgn_p->state_t.state,
3199 				    current_sgn_p->state_t.sub_state, -1);
3200 
3201 				if (!(boothowto & RB_DEBUG)) {
3202 					/*
3203 					 * Delay before calling
3204 					 * send_watchdog_msg, to allow
3205 					 * CPU_SIGNATURE() time to
3206 					 * complete; this increases the
3207 					 * chances of successfully sending
3208 					 * the watchdog message to the
3209 					 * system controller.
3210 					 */
3211 					delay(drv_usectohz(
3212 					    CPU_SIGNATURE_DELAY_TIME));
3213 					send_watchdog_msg(last_watchdog_msg);
3214 				}
3215 			}
3216 		}
3217 
3218 		/*
3219 		 * update keyswitch value in case it changed while the
3220 		 * RMC was out of action
3221 		 */
3222 		LOCK_CACHE
3223 		if (rmclomv_sysinfo_valid) {
3224 			real_key_position = rmclomv_sysinfo_data.keyswitch;
3225 			if ((real_key_position != RMC_KEYSWITCH_POS_UNKNOWN) &&
3226 			    (real_key_position <= RMC_KEYSWITCH_POS_OFF)) {
3227 				key_position = real_key_position;
3228 			} else {
3229 				/* treat unknown key position as locked */
3230 				key_position = RMC_KEYSWITCH_POS_LOCKED;
3231 			}
3232 		} else {
3233 			/* treat unreadable key position as locked */
3234 			key_position = RMC_KEYSWITCH_POS_LOCKED;
3235 			real_key_position = RMC_KEYSWITCH_POS_UNKNOWN;
3236 		}
3237 		RELEASE_CACHE
3238 
3239 		/*
3240 		 * Re-enter the lock to prepare for another iteration.
3241 		 * We must have the lock here to protect rmclomv_refresh_sig.
3242 		 */
3243 		mutex_enter(&rmclomv_refresh_lock);
3244 	}
3245 }
3246 
3247 static void
3248 rmclomv_refresh_start(void)
3249 {
3250 	kthread_t *tp;
3251 
3252 	mutex_enter(&rmclomv_refresh_lock);
3253 
3254 	if (rmclomv_refresh_tid == 0) {
3255 		rmclomv_refresh_sig = RMCLOMV_REFRESH_PROCESSNOW;
3256 
3257 		tp = thread_create(NULL, 0, rmclomv_refresh, NULL, 0,
3258 		    &p0, TS_RUN, maxclsyspri);
3259 		rmclomv_refresh_tid = tp->t_did;
3260 	}
3261 
3262 	mutex_exit(&rmclomv_refresh_lock);
3263 }
3264 
3265 static void
3266 rmclomv_refresh_destroy(void)
3267 {
3268 	kt_did_t tid;
3269 
3270 	mutex_enter(&rmclomv_refresh_lock);
3271 	tid = rmclomv_refresh_tid;
3272 	if (tid != 0) {
3273 		rmclomv_refresh_sig = RMCLOMV_REFRESH_EXITNOW;
3274 		cv_signal(&rmclomv_refresh_sig_cv);
3275 		rmclomv_refresh_tid = 0;
3276 	}
3277 	mutex_exit(&rmclomv_refresh_lock);
3278 
3279 	/*
3280 	 * Wait for rmclomv_refresh() to finish
3281 	 */
3282 	if (tid != 0)
3283 		thread_join(tid);
3284 }
3285 
3286 static void
3287 rmclomv_refresh_wakeup(void)
3288 {
3289 	mutex_enter(&rmclomv_refresh_lock);
3290 
3291 	if (rmclomv_refresh_sig != RMCLOMV_REFRESH_EXITNOW)
3292 		rmclomv_refresh_sig = RMCLOMV_REFRESH_PROCESSNOW;
3293 	cv_signal(&rmclomv_refresh_sig_cv);
3294 
3295 	mutex_exit(&rmclomv_refresh_lock);
3296 }
3297 
3298 static void
3299 send_watchdog_msg(int msg)
3300 {
3301 	rmc_comm_msg_t request;
3302 	dp_set_host_watchdog_t watchdog_msg;
3303 
3304 	if (rmclomv_watchdog_mode)
3305 		return;
3306 
3307 	watchdog_msg.enable = msg;
3308 	request.msg_type = DP_SET_HOST_WATCHDOG;
3309 	request.msg_len = sizeof (watchdog_msg);
3310 	request.msg_buf = (caddr_t)&watchdog_msg;
3311 	(void) rmc_comm_request_nowait(&request, (msg == 1) ?
3312 	    RMC_COMM_DREQ_URGENT : 0);
3313 }
3314 
3315 /*ARGSUSED*/
3316 static uint_t
3317 rmc_set_watchdog_timer(uint_t timeoutval)
3318 {
3319 	ASSERT(MUTEX_HELD(&tod_lock));
3320 
3321 	if ((watchdog_enable == 0) || (watchdog_available == 0)) {
3322 		return (0);
3323 	}
3324 
3325 	/*
3326 	 * If boothowto has RB_DEBUG set we never want to set the watchdog
3327 	 * support on.
3328 	 */
3329 	if (boothowto & RB_DEBUG) {
3330 		return (0);
3331 	}
3332 
3333 	/*
3334 	 * When the watchdog is shut off last_watchdog_msg goes from a
3335 	 * 0 to a 1. So we must test to see that last_watchdog_msg is
3336 	 * set to 1 indicating that watchdog was shut off and
3337 	 * After which we set last_watchdog_msg back to 0 so that we do not
3338 	 * run this code
3339 	 * again.
3340 	 */
3341 	if (last_watchdog_msg == 1) {
3342 		send_watchdog_msg(0);
3343 		last_watchdog_msg = 0;
3344 	}
3345 
3346 	pmugpio_watchdog_pat();
3347 
3348 	watchdog_activated = 1;
3349 
3350 	return (1);
3351 }
3352 
3353 static uint_t
3354 rmc_clear_watchdog_timer(void)
3355 {
3356 	ASSERT(MUTEX_HELD(&tod_lock));
3357 	if ((watchdog_activated == 0) || (boothowto & RB_DEBUG))
3358 		return (0);
3359 
3360 	send_watchdog_msg(1);
3361 	last_watchdog_msg = 1;
3362 	watchdog_activated = 0;
3363 
3364 	return (0);
3365 }
3366 
3367 static void
3368 plat_timesync(void *arg)
3369 {
3370 	timestruc_t now;
3371 	todinfo_t tod;
3372 	rmc_comm_msg_t request;
3373 	dp_set_date_time_t set_time_msg;
3374 	int retval;
3375 	timestruc_t ts;
3376 	dp_get_date_time_r_t *date_and_time_info;
3377 	int buffer[DATE_TIME_MSG_SIZE];
3378 
3379 	/* Is the system coming up? */
3380 	if (arg != NULL) {
3381 		/* Request the time from the RMC clock. */
3382 		retval = rmclomv_do_cmd(DP_GET_DATE_TIME, DP_GET_DATE_TIME_R,
3383 		    DATE_TIME_MSG_SIZE, NULL, (intptr_t)&buffer);
3384 
3385 		/*
3386 		 * If we were able to get the time lets set the local clock.
3387 		 * The time returned from RMC is in Unix time format.
3388 		 *
3389 		 * If we couldn't get the time we'll accept the drift so as not
3390 		 * to cause congestion on the I2C bus or cause boot
3391 		 * performance regressions.
3392 		 */
3393 		if (retval == RCNOERR) {
3394 			date_and_time_info = (dp_get_date_time_r_t *)buffer;
3395 			ts.tv_sec = date_and_time_info->current_datetime;
3396 			ts.tv_nsec = 0;
3397 			mutex_enter(&tod_lock);
3398 			tod_set(ts);
3399 			set_hrestime(&ts);
3400 			mutex_exit(&tod_lock);
3401 		}
3402 	}
3403 
3404 	gethrestime(&now);
3405 	mutex_enter(&tod_lock);
3406 	tod = utc_to_tod(now.tv_sec);
3407 	mutex_exit(&tod_lock);
3408 
3409 	set_time_msg.year	= tod.tod_year;
3410 	set_time_msg.month	= tod.tod_month - 1;
3411 	set_time_msg.day	= tod.tod_day;
3412 	set_time_msg.hour	= tod.tod_hour;
3413 	set_time_msg.minute	= tod.tod_min;
3414 	set_time_msg.second	= tod.tod_sec;
3415 
3416 	request.msg_type = DP_SET_DATE_TIME;
3417 	request.msg_len = sizeof (set_time_msg);
3418 	request.msg_buf = (caddr_t)&set_time_msg;
3419 
3420 	(void) rmc_comm_request_nowait(&request, 0);
3421 
3422 	(void) timeout(plat_timesync, NULL, timesync_interval);
3423 }
3424 
3425 /*
3426  * Interfaces to get/set alarm relays from outside
3427  */
3428 int
3429 rmclomv_alarm_get(int alarm_type, int *alarm_state)
3430 {
3431 	rmclomv_cache_section_t	*section;
3432 	int			index;
3433 	uint16_t		sensor_status;
3434 	dp_get_alarm_state_t	u_rmc_alarm;
3435 	dp_get_alarm_state_r_t	u_rmc_alarm_r;
3436 
3437 	/* see if we've got ALARM handles cached */
3438 	LOCK_CACHE
3439 	sensor_status = ENVMON_SENSOR_OK;
3440 
3441 	if ((rmclomv_cache_valid == B_FALSE) ||
3442 	    ((section = rmclomv_find_section(rmclomv_cache,
3443 	    RMCLOMV_ALARM_IND)) == NULL)) {
3444 		sensor_status = ENVMON_NOT_PRESENT;
3445 	}
3446 	if (sensor_status == ENVMON_SENSOR_OK) {
3447 		/*
3448 		 * user correctly identified a ALARM, note its
3449 		 * handle value and request the ALARM status
3450 		 */
3451 		index = alarm_type;
3452 		if (index >= section->num_entries)
3453 			sensor_status = ENVMON_INACCESSIBLE;
3454 		else
3455 			u_rmc_alarm.handle = section->entry[index].handle;
3456 	}
3457 	RELEASE_CACHE
3458 	if ((sensor_status == ENVMON_SENSOR_OK) && (rmclomv_rmc_error ||
3459 	    rmclomv_do_cmd(DP_GET_ALARM_STATE, DP_GET_ALARM_STATE_R,
3460 	    sizeof (u_rmc_alarm_r), (intptr_t)&u_rmc_alarm,
3461 	    (intptr_t)&u_rmc_alarm_r) != 0)) {
3462 		sensor_status = ENVMON_INACCESSIBLE;
3463 	}
3464 	if (sensor_status == ENVMON_SENSOR_OK) {
3465 		/*
3466 		 * copy results into buffer for user
3467 		 * start with some defaults then override
3468 		 */
3469 		*alarm_state = 0;
3470 
3471 		if (u_rmc_alarm_r.alarm_state[0].sensor_status !=
3472 		    DP_SENSOR_DATA_AVAILABLE)
3473 			return (ENXIO);
3474 		else {
3475 			dp_alarm_state_t alarmState;
3476 			alarmState = u_rmc_alarm_r.alarm_state[0];
3477 
3478 			switch (alarmState.state) {
3479 			case DP_ALARM_OFF:
3480 				break;
3481 			case DP_ALARM_ON:
3482 				*alarm_state = 1;
3483 				break;
3484 			default:
3485 				break;
3486 			}
3487 		}
3488 	} else
3489 		return (ENXIO);
3490 
3491 	return (0);
3492 }
3493 
3494 int
3495 rmclomv_alarm_set(int alarm_type, int new_state)
3496 {
3497 	rmclomv_cache_section_t	*section;
3498 	int			index;
3499 	uint16_t		sensor_status;
3500 	dp_set_alarm_state_t	u_rmc_setalarm;
3501 	dp_set_alarm_state_r_t	u_rmc_setalarm_r;
3502 
3503 	/* see if we've got ALARM handles cached */
3504 	LOCK_CACHE
3505 	sensor_status = ENVMON_SENSOR_OK;
3506 
3507 	if ((rmclomv_cache_valid == B_FALSE) ||
3508 	    ((section = rmclomv_find_section(rmclomv_cache,
3509 	    RMCLOMV_ALARM_IND)) == NULL)) {
3510 		sensor_status = ENVMON_NOT_PRESENT;
3511 	}
3512 	if (sensor_status == ENVMON_SENSOR_OK) {
3513 		/*
3514 		 * user correctly identified a ALARM, note its
3515 		 * handle value and request the ALARM status
3516 		 */
3517 		index = alarm_type;
3518 		if (index >= section->num_entries)
3519 			sensor_status = ENVMON_INACCESSIBLE;
3520 		else {
3521 			u_rmc_setalarm.handle = section->entry[index].handle;
3522 			u_rmc_setalarm.state = new_state;
3523 		}
3524 	}
3525 	RELEASE_CACHE
3526 	if ((sensor_status == ENVMON_SENSOR_OK) &&
3527 	    (rmclomv_rmc_error ||
3528 	    rmclomv_do_cmd(DP_SET_ALARM_STATE, DP_SET_ALARM_STATE_R,
3529 	    sizeof (u_rmc_setalarm_r), (intptr_t)&u_rmc_setalarm,
3530 	    (intptr_t)&u_rmc_setalarm_r) != 0)) {
3531 		sensor_status = ENVMON_INACCESSIBLE;
3532 	}
3533 
3534 	if (u_rmc_setalarm_r.status != DP_SET_ALARM_OK) {
3535 		return (EIO);
3536 	}
3537 
3538 	if (sensor_status != ENVMON_SENSOR_OK) {
3539 		return (ENXIO);
3540 	}
3541 
3542 	return (0);
3543 }
3544