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