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