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