xref: /titanic_44/usr/src/uts/sun4u/starcat/io/mboxsc.c (revision ad9a7bd3532cf0ef931ded51a5ffe5d0496aad88)
103831d35Sstevel /*
203831d35Sstevel  * CDDL HEADER START
303831d35Sstevel  *
403831d35Sstevel  * The contents of this file are subject to the terms of the
503831d35Sstevel  * Common Development and Distribution License (the "License").
603831d35Sstevel  * You may not use this file except in compliance with the License.
703831d35Sstevel  *
803831d35Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
903831d35Sstevel  * or http://www.opensolaris.org/os/licensing.
1003831d35Sstevel  * See the License for the specific language governing permissions
1103831d35Sstevel  * and limitations under the License.
1203831d35Sstevel  *
1303831d35Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
1403831d35Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1503831d35Sstevel  * If applicable, add the following below this CDDL HEADER, with the
1603831d35Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
1703831d35Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
1803831d35Sstevel  *
1903831d35Sstevel  * CDDL HEADER END
2003831d35Sstevel  */
2103831d35Sstevel 
2203831d35Sstevel /*
2307d06da5SSurya Prakki  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
2403831d35Sstevel  * Use is subject to license terms.
2503831d35Sstevel  */
2603831d35Sstevel 
2703831d35Sstevel /*
2803831d35Sstevel  * This file contains the implementation of the mboxsc module, a mailbox layer
2903831d35Sstevel  * built upon the Starcat IOSRAM driver.
3003831d35Sstevel  */
3103831d35Sstevel 
3203831d35Sstevel #include <sys/types.h>
3303831d35Sstevel #include <sys/systm.h>
3403831d35Sstevel #include <sys/modctl.h>
3503831d35Sstevel #include <sys/errno.h>
3603831d35Sstevel #include <sys/ksynch.h>
3703831d35Sstevel #include <sys/kmem.h>
3803831d35Sstevel #include <sys/varargs.h>
3903831d35Sstevel #include <sys/ddi.h>
4003831d35Sstevel #include <sys/sunddi.h>
4103831d35Sstevel #include <sys/cmn_err.h>
4203831d35Sstevel #include <sys/debug.h>
4303831d35Sstevel #include <sys/sysmacros.h>
4403831d35Sstevel 
4503831d35Sstevel #include <sys/iosramreg.h>
4603831d35Sstevel #include <sys/iosramio.h>
4703831d35Sstevel #include <sys/mboxsc.h>
4803831d35Sstevel #include <sys/mboxsc_impl.h>
4903831d35Sstevel 
5003831d35Sstevel /*
5103831d35Sstevel  * Debugging facility
5203831d35Sstevel  */
5303831d35Sstevel #define	DBGACT_NONE	(0x00000000)
5403831d35Sstevel #define	DBGACT_BREAK	(0x00000001)
5503831d35Sstevel #define	DBGACT_SHOWPOS	(0x00000002)
5603831d35Sstevel #define	DBGACT_DEFAULT	DBGACT_NONE
5703831d35Sstevel 
5803831d35Sstevel #define	DBG_DEV		(0x00000001)
5903831d35Sstevel #define	DBG_CALLS	(0x00000002)
6003831d35Sstevel #define	DBG_RETS	(0x00000004)
6103831d35Sstevel #define	DBG_ARGS	(0x00000008)
6203831d35Sstevel #define	DBG_KMEM	(0x00000010)
6303831d35Sstevel #define	DBG_ALL		(0xFFFFFFFF)
6403831d35Sstevel 
6503831d35Sstevel #ifdef DEBUG
6603831d35Sstevel static uint32_t	mboxsc_debug_mask = 0x00000000;
6703831d35Sstevel #define	DPRINTF0(class, action, fmt) \
6803831d35Sstevel 	mboxsc_dprintf(__FILE__, __LINE__, (class), (action), (fmt))
6903831d35Sstevel #define	DPRINTF1(class, action, fmt, arg1) \
7003831d35Sstevel 	mboxsc_dprintf(__FILE__, __LINE__, (class), (action), (fmt),\
7103831d35Sstevel 	    (arg1))
7203831d35Sstevel #define	DPRINTF2(class, action, fmt, arg1, arg2) \
7303831d35Sstevel 	mboxsc_dprintf(__FILE__, __LINE__, (class), (action), (fmt),\
7403831d35Sstevel 	    (arg1), (arg2))
7503831d35Sstevel #define	DPRINTF3(class, action, fmt, arg1, arg2, arg3) \
7603831d35Sstevel 	mboxsc_dprintf(__FILE__, __LINE__, (class), (action), (fmt),\
7703831d35Sstevel 	    (arg1), (arg2), (arg3))
7803831d35Sstevel #define	DPRINTF4(class, action, fmt, arg1, arg2, arg3, arg4) \
7903831d35Sstevel 	mboxsc_dprintf(__FILE__, __LINE__, (class), (action), (fmt),\
8003831d35Sstevel 	    (arg1), (arg2), (arg3), (arg4))
8103831d35Sstevel #define	DPRINTF5(class, action, fmt, arg1, arg2, arg3, arg4, arg5) \
8203831d35Sstevel 	mboxsc_dprintf(__FILE__, __LINE__, (class), (action), (fmt),\
8303831d35Sstevel 	    (arg1), (arg2), (arg3), (arg4), (arg5))
8403831d35Sstevel #else	/* DEBUG */
8503831d35Sstevel #define	DPRINTF0(class, action, fmt)
8603831d35Sstevel #define	DPRINTF1(class, action, fmt, arg1)
8703831d35Sstevel #define	DPRINTF2(class, action, fmt, arg1, arg2)
8803831d35Sstevel #define	DPRINTF3(class, action, fmt, arg1, arg2, arg3)
8903831d35Sstevel #define	DPRINTF4(class, action, fmt, arg1, arg2, arg3, arg4)
9003831d35Sstevel #define	DPRINTF5(class, action, fmt, arg1, arg2, arg3, arg4, arg5)
9103831d35Sstevel #endif	/* DEBUG */
9203831d35Sstevel 
9303831d35Sstevel /*
9403831d35Sstevel  * Basic constants
9503831d35Sstevel  */
9603831d35Sstevel #ifndef TRUE
9703831d35Sstevel #define	TRUE	(1)
9803831d35Sstevel #endif	/* TRUE */
9903831d35Sstevel #ifndef FALSE
10003831d35Sstevel #define	FALSE	(0)
10103831d35Sstevel #endif	/* FALSE */
10203831d35Sstevel 
10303831d35Sstevel 
10403831d35Sstevel /*
10503831d35Sstevel  * Whenever mboxsc_init is called to create a new mailbox, an instance of
10603831d35Sstevel  * mboxsc_mbox_t is created and inserted into a hash table to maintain
10703831d35Sstevel  * various information about the mailbox.  The mbox_state, mbox_refcount, and
10803831d35Sstevel  * mbox_wait fields are all protected by the global mboxsc_lock mutex.
10903831d35Sstevel  * If lock contention between mailboxes becomes an issue, each mailbox will
11003831d35Sstevel  * need to be given its own mutex to protect the mbox_wait, mbox_state,
11103831d35Sstevel  * and mbox_update_wait fields.  The mbox_refcount field will probably need to
11203831d35Sstevel  * remain under global protection, however, since it is used to keep track of
11303831d35Sstevel  * the number of threads sleeping inside the mailbox's various synchronization
11403831d35Sstevel  * mechanisms and would consequently be difficult to protect using those same
11503831d35Sstevel  * mechanisms.
11603831d35Sstevel  */
11703831d35Sstevel typedef struct mboxsc_mbox {
11803831d35Sstevel 	uint32_t		mbox_key;
11903831d35Sstevel 	int			mbox_direction;
12003831d35Sstevel 	void			(*mbox_callback)(void);
12103831d35Sstevel 	uint32_t		mbox_length;
12203831d35Sstevel 	uint16_t		mbox_refcount;
12303831d35Sstevel 	uint16_t		mbox_state;
12403831d35Sstevel 	kcondvar_t		mbox_wait;
12503831d35Sstevel 	mboxsc_msghdr_t		mbox_header;
12603831d35Sstevel 	struct mboxsc_mbox	*mbox_hash_next;
12703831d35Sstevel } mboxsc_mbox_t;
12803831d35Sstevel 
12903831d35Sstevel /*
13003831d35Sstevel  * Various state flags that can be set on a mailbox.  Multiple states may
13103831d35Sstevel  * be active at the same time.
13203831d35Sstevel  */
13303831d35Sstevel #define	STATE_IDLE	(0x0000)
13403831d35Sstevel #define	STATE_WRITING	(0x0001)
13503831d35Sstevel #define	STATE_READING	(0x0002)
13603831d35Sstevel #define	STATE_HDRVALID	(0x0004)
13703831d35Sstevel 
13803831d35Sstevel /*
13903831d35Sstevel  * Timeout periods for mboxsc_putmsg and mboxsc_getmsg, converted to ticks
14003831d35Sstevel  * from the microsecond values found in mboxsc_impl.h.
14103831d35Sstevel  */
14203831d35Sstevel #define	EAGAIN_POLL		(drv_usectohz(MBOXSC_EAGAIN_POLL_USECS))
14303831d35Sstevel #define	PUTMSG_POLL		(drv_usectohz(MBOXSC_PUTMSG_POLL_USECS))
14403831d35Sstevel #define	HWLOCK_POLL		(drv_usectohz(MBOXSC_HWLOCK_POLL_USECS))
14503831d35Sstevel #define	LOOP_WARN_INTERVAL	(drv_usectohz(MBOXSC_USECS_PER_SECOND * 15))
14603831d35Sstevel 
14703831d35Sstevel /*
14803831d35Sstevel  * Various tests that are performed on message header fields.
14903831d35Sstevel  */
15003831d35Sstevel #define	IS_UNSOLICITED_TYPE(type)	((type) != MBOXSC_MSG_REPLY)
15103831d35Sstevel #define	MSG_TYPE_MATCHES(type, msgp)	\
15203831d35Sstevel 	(((type) == 0) || ((type) & (msgp)->msg_type))
15303831d35Sstevel #define	MSG_CMD_MATCHES(cmd, msgp)	\
15403831d35Sstevel 	(((cmd) == 0) || ((cmd) == (msgp)->msg_cmd))
15503831d35Sstevel #define	MSG_TRANSID_MATCHES(tid, msgp)	\
15603831d35Sstevel 	(((tid) == 0) || ((tid) == (msgp)->msg_transid))
15703831d35Sstevel 
15803831d35Sstevel /*
159*ad9a7bd3SRichard Lowe  * This macro can be used to determine the size of any field in the message
160*ad9a7bd3SRichard Lowe  * header (or any other struct, for that matter).
16103831d35Sstevel  */
16203831d35Sstevel #define	FIELD_SIZE(type, field)		(sizeof (((type *)0)->field))
16303831d35Sstevel 
16403831d35Sstevel /*
16503831d35Sstevel  * Mask used when generating unique transaction ID values.
16603831d35Sstevel  * This arbitrarily chosen value will be OR'd together with
16703831d35Sstevel  * a counter for each successive internally-generated transaction ID.
16803831d35Sstevel  */
16903831d35Sstevel #define	TRANSID_GEN_MASK	(0xFFC0000000000000)
17003831d35Sstevel 
17103831d35Sstevel /*
17203831d35Sstevel  * All existing mailboxes are stored in a hash table with HASHTBL_SIZE
17303831d35Sstevel  * entries so they can be rapidly accessed by their key values.
17403831d35Sstevel  */
17503831d35Sstevel #define	HASHTBL_SIZE	(32)
17603831d35Sstevel #define	HASH_KEY(key)	((((key) >> 24) ^ ((key) >> 16) ^ ((key) >> 9) ^\
17703831d35Sstevel 			    (key)) & (HASHTBL_SIZE - 1));
17803831d35Sstevel 
17903831d35Sstevel /*
18003831d35Sstevel  * Unfortunately, it is necessary to calculate checksums on data split up
18103831d35Sstevel  * amongst different buffers in some cases.  Consequently, mboxsc_checksum
18203831d35Sstevel  * accepts a "seed" value as one of its parameters.  When first starting a
18303831d35Sstevel  * checksum calculation, the seed should be 0.
18403831d35Sstevel  */
18503831d35Sstevel #define	CHKSUM_INIT	(0)
18603831d35Sstevel 
18703831d35Sstevel /*
18803831d35Sstevel  * local variables
18903831d35Sstevel  */
19003831d35Sstevel static kmutex_t		mboxsc_lock;
19103831d35Sstevel static mboxsc_mbox_t	*mboxsc_hash_table[HASHTBL_SIZE];
19203831d35Sstevel static uint32_t		mboxsc_flaglock_count;
19303831d35Sstevel static uint32_t		mboxsc_active_version = MBOXSC_PROTOCOL_VERSION;
19403831d35Sstevel static kcondvar_t	mboxsc_dereference_cv;
19503831d35Sstevel 
19603831d35Sstevel /*
19703831d35Sstevel  * Structures from modctl.h used for loadable module support.
19803831d35Sstevel  * The mboxsc API is a "miscellaneous" module.
19903831d35Sstevel  */
20003831d35Sstevel extern struct mod_ops mod_miscops;
20103831d35Sstevel 
20203831d35Sstevel static struct modlmisc modlmisc = {
20303831d35Sstevel 	&mod_miscops,
204f500b196SRichard Bean 	"IOSRAM Mailbox API 'mboxsc'",
20503831d35Sstevel };
20603831d35Sstevel 
20703831d35Sstevel static struct modlinkage modlinkage = {
20803831d35Sstevel 	MODREV_1,
20903831d35Sstevel 	(void *)&modlmisc,
21003831d35Sstevel 	NULL
21103831d35Sstevel };
21203831d35Sstevel 
21303831d35Sstevel /*
21403831d35Sstevel  * Prototypes for local functions
21503831d35Sstevel  */
21603831d35Sstevel static void		mboxsc_iosram_callback(void *arg);
21703831d35Sstevel static void		mboxsc_hdrchange_callback(void);
21803831d35Sstevel static int		mboxsc_add_mailbox(mboxsc_mbox_t *mailboxp);
21903831d35Sstevel static void		mboxsc_close_mailbox(mboxsc_mbox_t *mailboxp);
22003831d35Sstevel static void		mboxsc_hashinsert_mailbox(mboxsc_mbox_t *mailboxp);
22103831d35Sstevel static mboxsc_mbox_t	*mboxsc_hashfind_mailbox_by_key(uint32_t key);
22203831d35Sstevel static mboxsc_mbox_t	*mboxsc_hashremove_mailbox_by_key(uint32_t key);
22303831d35Sstevel static mboxsc_chksum_t	mboxsc_checksum(mboxsc_chksum_t seed, uint8_t *buf,
22403831d35Sstevel 	uint32_t length);
22503831d35Sstevel static int		mboxsc_lock_flags(uint8_t mandatory, clock_t deadline);
22603831d35Sstevel static int		mboxsc_unlock_flags(uint8_t mandatory);
22703831d35Sstevel static int		mboxsc_timed_read(clock_t deadline, uint32_t key,
22803831d35Sstevel 	uint32_t off, uint32_t len, caddr_t dptr);
22903831d35Sstevel static int		mboxsc_timed_write(clock_t deadline, uint32_t key,
23003831d35Sstevel 	uint32_t off, uint32_t len, caddr_t dptr);
23103831d35Sstevel static int		mboxsc_timed_get_flag(clock_t deadline, uint32_t key,
23203831d35Sstevel 	uint8_t *data_validp, uint8_t *int_pendingp);
23303831d35Sstevel static int		mboxsc_timed_set_flag(clock_t deadline, uint32_t key,
23403831d35Sstevel 	uint8_t data_valid, uint8_t int_pending);
23503831d35Sstevel static int		mboxsc_timed_send_intr(clock_t deadline);
23603831d35Sstevel static int		mboxsc_expire_message(uint32_t key, int *resultp);
23703831d35Sstevel static uint64_t		mboxsc_generate_transid(uint64_t prev_transid);
23803831d35Sstevel static void		mboxsc_reference_mailbox(mboxsc_mbox_t *mailboxp);
23903831d35Sstevel static void		mboxsc_dereference_mailbox(mboxsc_mbox_t *mailboxp);
24003831d35Sstevel #ifdef DEBUG
24103831d35Sstevel /*PRINTFLIKE5*/
24203831d35Sstevel static void		mboxsc_dprintf(const char *file, int line,
24303831d35Sstevel 	uint32_t class, uint32_t action, const char *fmt, ...);
24403831d35Sstevel int			mboxsc_debug(int cmd, void *arg);
24503831d35Sstevel #endif /* DEBUG */
24603831d35Sstevel 
24703831d35Sstevel 
24803831d35Sstevel /*
24903831d35Sstevel  * _init
25003831d35Sstevel  *
25103831d35Sstevel  * Loadable module support routine.  Initializes global lock and hash table.
25203831d35Sstevel  */
25303831d35Sstevel int
_init(void)25403831d35Sstevel _init(void)
25503831d35Sstevel {
25603831d35Sstevel 	int		i;
25703831d35Sstevel 	uint32_t	sms_version;
25803831d35Sstevel 	int		error = 0;
25903831d35Sstevel 
26003831d35Sstevel 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "_init called\n");
26103831d35Sstevel 
26203831d35Sstevel 	/*
26303831d35Sstevel 	 * Initialize all module resources.
26403831d35Sstevel 	 */
26503831d35Sstevel 	mutex_init(&mboxsc_lock, NULL, MUTEX_DRIVER, NULL);
26603831d35Sstevel 	cv_init(&mboxsc_dereference_cv, NULL, CV_DRIVER, NULL);
26703831d35Sstevel 
26803831d35Sstevel 	for (i = 0; i < HASHTBL_SIZE; i++) {
26903831d35Sstevel 		mboxsc_hash_table[i] = NULL;
27003831d35Sstevel 	}
27103831d35Sstevel 	mboxsc_flaglock_count = 0;
27203831d35Sstevel 
27303831d35Sstevel 	if (mod_install(&modlinkage) != 0) {
27403831d35Sstevel 		goto failed;
27503831d35Sstevel 	}
27603831d35Sstevel 
27703831d35Sstevel 	/*
27803831d35Sstevel 	 * Set the os_mbox_version field in the IOSRAM header to indicate the
27903831d35Sstevel 	 * highest Mailbox Protocol version we support
28003831d35Sstevel 	 */
28103831d35Sstevel 	error = iosram_hdr_ctrl(IOSRAM_HDRCMD_SET_OS_MBOX_VER,
28203831d35Sstevel 	    (void *)MBOXSC_PROTOCOL_VERSION);
28303831d35Sstevel 	if (error != 0) {
28403831d35Sstevel 		goto failed;
28503831d35Sstevel 	}
28603831d35Sstevel 
28703831d35Sstevel 	/*
28803831d35Sstevel 	 * Read the sms_mbox_version field in the IOSRAM header to determine
28903831d35Sstevel 	 * what the greatest commonly supported version is.
29003831d35Sstevel 	 */
29103831d35Sstevel 	error = iosram_hdr_ctrl(IOSRAM_HDRCMD_GET_SMS_MBOX_VER,
29203831d35Sstevel 	    (void *)&sms_version);
29303831d35Sstevel 	if (error != 0) {
29403831d35Sstevel 		goto failed;
29503831d35Sstevel 	}
29603831d35Sstevel 	mboxsc_active_version = MIN(MBOXSC_PROTOCOL_VERSION, sms_version);
29703831d35Sstevel 	DPRINTF2(DBG_DEV, DBGACT_DEFAULT,
29803831d35Sstevel 	    "sms version: %d, active version: %d\n", sms_version,
29903831d35Sstevel 	    mboxsc_active_version);
30003831d35Sstevel 
30103831d35Sstevel 	/*
30203831d35Sstevel 	 * Register a callback with the IOSRAM driver to receive notification of
30303831d35Sstevel 	 * changes to the IOSRAM header, in case the sms_mbox_version field
30403831d35Sstevel 	 * changes.
30503831d35Sstevel 	 */
30603831d35Sstevel 	error = iosram_hdr_ctrl(IOSRAM_HDRCMD_REG_CALLBACK,
30703831d35Sstevel 	    (void *)mboxsc_hdrchange_callback);
30803831d35Sstevel 	if (error != 0) {
30903831d35Sstevel 		goto failed;
31003831d35Sstevel 	}
31103831d35Sstevel 
31203831d35Sstevel 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "_init ret: 0x%08x\n", error);
31303831d35Sstevel 	return (0);
31403831d35Sstevel 
31503831d35Sstevel 	/*
31603831d35Sstevel 	 * If initialization fails, uninitialize resources.
31703831d35Sstevel 	 */
31803831d35Sstevel failed:
31903831d35Sstevel 	mutex_destroy(&mboxsc_lock);
32003831d35Sstevel 	cv_destroy(&mboxsc_dereference_cv);
32103831d35Sstevel 
32203831d35Sstevel 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "_init ret: 0x%08x\n", error);
32303831d35Sstevel 	return (error);
32403831d35Sstevel }
32503831d35Sstevel 
32603831d35Sstevel /*
32703831d35Sstevel  * _fini
32803831d35Sstevel  *
32903831d35Sstevel  * Loadable module support routine. Closes all mailboxes and releases all
33003831d35Sstevel  * resources.
33103831d35Sstevel  */
33203831d35Sstevel int
_fini(void)33303831d35Sstevel _fini(void)
33403831d35Sstevel {
33503831d35Sstevel 	int		i;
33603831d35Sstevel 	int		error = 0;
33703831d35Sstevel 	mboxsc_mbox_t	*mailboxp;
33803831d35Sstevel 
33903831d35Sstevel 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "_fini called\n");
34003831d35Sstevel 
34103831d35Sstevel 	/*
34203831d35Sstevel 	 * Attempt to remove the module.  If successful, close all mailboxes
34303831d35Sstevel 	 * and deallocate the global lock.
34403831d35Sstevel 	 */
34503831d35Sstevel 	error = mod_remove(&modlinkage);
34603831d35Sstevel 	if (error == 0) {
34703831d35Sstevel 		mutex_enter(&mboxsc_lock);
34803831d35Sstevel 
34907d06da5SSurya Prakki 		(void) iosram_hdr_ctrl(IOSRAM_HDRCMD_REG_CALLBACK, NULL);
35003831d35Sstevel 
35103831d35Sstevel 		for (i = 0; i < HASHTBL_SIZE; i++) {
35203831d35Sstevel 			while (mboxsc_hash_table[i] != NULL) {
35303831d35Sstevel 				mailboxp = mboxsc_hash_table[i];
35403831d35Sstevel 				mboxsc_close_mailbox(mailboxp);
35503831d35Sstevel 			}
35603831d35Sstevel 		}
35703831d35Sstevel 		mutex_exit(&mboxsc_lock);
35803831d35Sstevel 		mutex_destroy(&mboxsc_lock);
35903831d35Sstevel 		cv_destroy(&mboxsc_dereference_cv);
36003831d35Sstevel 	}
36103831d35Sstevel 
36203831d35Sstevel 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "_fini ret: 0x%08x\n", error);
36303831d35Sstevel 	return (error);
36403831d35Sstevel }
36503831d35Sstevel 
36603831d35Sstevel /*
36703831d35Sstevel  * _info
36803831d35Sstevel  *
36903831d35Sstevel  * Loadable module support routine.
37003831d35Sstevel  */
37103831d35Sstevel int
_info(struct modinfo * modinfop)37203831d35Sstevel _info(struct modinfo *modinfop)
37303831d35Sstevel {
37403831d35Sstevel 	int		error = 0;
37503831d35Sstevel 
37603831d35Sstevel 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "_info called\n");
37703831d35Sstevel 
37803831d35Sstevel 	error = mod_info(&modlinkage, modinfop);
37903831d35Sstevel 
38003831d35Sstevel 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "_info ret: 0x%08x\n", error);
38103831d35Sstevel 
38203831d35Sstevel 	return (error);
38303831d35Sstevel }
38403831d35Sstevel 
38503831d35Sstevel /*
38603831d35Sstevel  * mboxsc_init
38703831d35Sstevel  *
38803831d35Sstevel  * Attempts to create a new mailbox.
38903831d35Sstevel  */
39003831d35Sstevel int
mboxsc_init(uint32_t key,int direction,void (* event_handler)(void))39103831d35Sstevel mboxsc_init(uint32_t key, int direction, void (*event_handler)(void))
39203831d35Sstevel {
39303831d35Sstevel 	int		error = 0;
39403831d35Sstevel 	mboxsc_mbox_t	*mailboxp;
39503831d35Sstevel 
39603831d35Sstevel 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_init called\n");
39703831d35Sstevel 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key);
39803831d35Sstevel 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "direction = %d\n", direction);
39903831d35Sstevel 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "event_handlerp = %p\n",
40007d06da5SSurya Prakki 	    (void *)event_handler);
40103831d35Sstevel 
40203831d35Sstevel 	/*
40303831d35Sstevel 	 * Check for valid direction and callback specification.
40403831d35Sstevel 	 */
40503831d35Sstevel 	if (((direction != MBOXSC_MBOX_IN) && (direction != MBOXSC_MBOX_OUT)) ||
40603831d35Sstevel 	    ((event_handler != NULL) && (direction != MBOXSC_MBOX_IN))) {
40703831d35Sstevel 		DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_init ret: 0x%08x\n",
40803831d35Sstevel 		    EINVAL);
40903831d35Sstevel 		return (EINVAL);
41003831d35Sstevel 	}
41103831d35Sstevel 
41203831d35Sstevel 	/*
41303831d35Sstevel 	 * Allocate memory for the mailbox structure and initialize all
41403831d35Sstevel 	 * caller-provided fields.
41503831d35Sstevel 	 */
41603831d35Sstevel 	mailboxp = (mboxsc_mbox_t *)kmem_zalloc(sizeof (mboxsc_mbox_t),
41703831d35Sstevel 	    KM_SLEEP);
41807d06da5SSurya Prakki 	DPRINTF2(DBG_KMEM, DBGACT_DEFAULT, "kmem_zalloc(%lu) = %p\n",
41907d06da5SSurya Prakki 	    sizeof (mboxsc_mbox_t), (void *)mailboxp);
42003831d35Sstevel 	mailboxp->mbox_key = key;
42103831d35Sstevel 	mailboxp->mbox_direction = direction;
42203831d35Sstevel 	mailboxp->mbox_callback = event_handler;
42303831d35Sstevel 
42403831d35Sstevel 	/*
42503831d35Sstevel 	 * Attempt to add the mailbox.  If unsuccessful, free the allocated
42603831d35Sstevel 	 * memory.
42703831d35Sstevel 	 */
42803831d35Sstevel 	mutex_enter(&mboxsc_lock);
42903831d35Sstevel 	error = mboxsc_add_mailbox(mailboxp);
43003831d35Sstevel 	mutex_exit(&mboxsc_lock);
43103831d35Sstevel 
43203831d35Sstevel 	if (error != 0) {
43307d06da5SSurya Prakki 		DPRINTF2(DBG_KMEM, DBGACT_DEFAULT, "kmem_free(%p, %lu)\n",
43407d06da5SSurya Prakki 		    (void *)mailboxp, sizeof (mboxsc_mbox_t));
43503831d35Sstevel 		kmem_free(mailboxp, sizeof (mboxsc_mbox_t));
43603831d35Sstevel 	}
43703831d35Sstevel 
43803831d35Sstevel 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_init ret: 0x%08x\n", error);
43903831d35Sstevel 	return (error);
44003831d35Sstevel }
44103831d35Sstevel 
44203831d35Sstevel /*
44303831d35Sstevel  * mboxsc_fini
44403831d35Sstevel  *
44503831d35Sstevel  * Closes the mailbox with the indicated key, if it exists.
44603831d35Sstevel  */
44703831d35Sstevel int
mboxsc_fini(uint32_t key)44803831d35Sstevel mboxsc_fini(uint32_t key)
44903831d35Sstevel {
45003831d35Sstevel 	int		error = 0;
45103831d35Sstevel 	mboxsc_mbox_t	*mailboxp;
45203831d35Sstevel 
45303831d35Sstevel 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_fini called\n");
45403831d35Sstevel 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key);
45503831d35Sstevel 
45603831d35Sstevel 	/*
45703831d35Sstevel 	 * Attempt to close the mailbox.
45803831d35Sstevel 	 */
45903831d35Sstevel 	mutex_enter(&mboxsc_lock);
46003831d35Sstevel 	mailboxp = mboxsc_hashfind_mailbox_by_key(key);
46103831d35Sstevel 	if (mailboxp == NULL) {
46203831d35Sstevel 		error = EBADF;
46303831d35Sstevel 	} else {
46403831d35Sstevel 		while (mailboxp->mbox_refcount != 0) {
46503831d35Sstevel 			cv_wait(&mboxsc_dereference_cv, &mboxsc_lock);
46603831d35Sstevel 		}
46703831d35Sstevel 		mboxsc_close_mailbox(mailboxp);
46803831d35Sstevel 	}
46903831d35Sstevel 	mutex_exit(&mboxsc_lock);
47003831d35Sstevel 
47103831d35Sstevel 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_fini ret: 0x%08x\n", error);
47203831d35Sstevel 	return (error);
47303831d35Sstevel }
47403831d35Sstevel 
47503831d35Sstevel /*
47603831d35Sstevel  * mboxsc_putmsg
47703831d35Sstevel  *
47803831d35Sstevel  * Attempt to place a message into an outbound mailbox and signal the
47903831d35Sstevel  * recipient.  A successful return (0) indicates that the message was
48003831d35Sstevel  * successfully delivered.
48103831d35Sstevel  */
48203831d35Sstevel int
mboxsc_putmsg(uint32_t key,uint32_t type,uint32_t cmd,uint64_t * transidp,uint32_t length,void * datap,clock_t timeout)48303831d35Sstevel mboxsc_putmsg(uint32_t key, uint32_t type, uint32_t cmd, uint64_t *transidp,
48403831d35Sstevel 		uint32_t length, void *datap, clock_t timeout)
48503831d35Sstevel {
48603831d35Sstevel 	int		i;
48703831d35Sstevel 	int		error = 0;
48803831d35Sstevel 	int		result;
48903831d35Sstevel 	int		lock_held = 0;
49003831d35Sstevel 	int		unlock_err;
49103831d35Sstevel 	uint8_t		data_valid;
49203831d35Sstevel 	clock_t		deadline;
49303831d35Sstevel 	clock_t		remainder;
49403831d35Sstevel 	mboxsc_chksum_t	checksum;
49503831d35Sstevel 	mboxsc_mbox_t	*mailboxp;
49603831d35Sstevel 	mboxsc_msghdr_t	header;
49703831d35Sstevel 
49803831d35Sstevel #ifdef DEBUG /* because lint whines about if stmts without consequents */
49903831d35Sstevel 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_putmsg called\n");
50003831d35Sstevel 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key);
50103831d35Sstevel 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "type = 0x%x\n", type);
50203831d35Sstevel 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "cmd = 0x%x\n", cmd);
50307d06da5SSurya Prakki 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "transidp = %p\n", (void *)transidp);
50403831d35Sstevel 	if (transidp != NULL) {
50507d06da5SSurya Prakki 		DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "*transidp = 0x%016lx\n",
50603831d35Sstevel 		    *transidp);
50703831d35Sstevel 	}
50803831d35Sstevel 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "length = 0x%x\n", length);
50903831d35Sstevel 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "datap = %p\n", datap);
51007d06da5SSurya Prakki 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "timeout = %ld\n", timeout);
51103831d35Sstevel #endif /* DEBUG */
51203831d35Sstevel 
51303831d35Sstevel 	/*
51403831d35Sstevel 	 * Perform some basic sanity checks on the message.
51503831d35Sstevel 	 */
51603831d35Sstevel 	for (i = 0; i < MBOXSC_NUM_MSG_TYPES; i++) {
51703831d35Sstevel 		if (type == (1 << i)) {
51803831d35Sstevel 			break;
51903831d35Sstevel 		}
52003831d35Sstevel 	}
52103831d35Sstevel 	if ((i == MBOXSC_NUM_MSG_TYPES) || (cmd == 0) ||
52203831d35Sstevel 	    ((datap == NULL) && (length != 0)) ||
52303831d35Sstevel 	    (timeout < MBOXSC_PUTMSG_MIN_TIMEOUT_MSECS) ||
52403831d35Sstevel 	    (timeout > MBOXSC_PUTMSG_MAX_TIMEOUT_MSECS)) {
52503831d35Sstevel 		DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
52603831d35Sstevel 		    "mboxsc_putmsg ret: 0x%08x\n", EINVAL);
52703831d35Sstevel 		return (EINVAL);
52803831d35Sstevel 	}
52903831d35Sstevel 
53003831d35Sstevel 	/*
53103831d35Sstevel 	 * Initialize the header structure with values provided by the caller.
53203831d35Sstevel 	 */
53303831d35Sstevel 	header.msg_version = mboxsc_active_version;
53403831d35Sstevel 	header.msg_type = type;
53503831d35Sstevel 	header.msg_cmd = cmd;
53603831d35Sstevel 	header.msg_length = MBOXSC_MSGHDR_SIZE + length;
53703831d35Sstevel 	if (transidp != NULL) {
53803831d35Sstevel 		header.msg_transid = *transidp;
53903831d35Sstevel 	} else {
54003831d35Sstevel 		header.msg_transid = 0;
54103831d35Sstevel 	}
54203831d35Sstevel 
54303831d35Sstevel 	/*
54403831d35Sstevel 	 * Perform additional sanity checks on the mailbox and message.
54503831d35Sstevel 	 * Make sure that the specified mailbox really exists, that the
54603831d35Sstevel 	 * given message will fit in it, and that the current message's
54703831d35Sstevel 	 * transaction ID isn't the same as the last message's transaction
54803831d35Sstevel 	 * ID unless both messages are replies (it's okay, necessary even,
54903831d35Sstevel 	 * to reuse a transaction ID when resending a failed reply message,
55003831d35Sstevel 	 * but that is the only case in which it is permissible).
55103831d35Sstevel 	 */
55203831d35Sstevel 	mutex_enter(&mboxsc_lock);
55303831d35Sstevel 	mailboxp = mboxsc_hashfind_mailbox_by_key(key);
55403831d35Sstevel 
55503831d35Sstevel 	if (mailboxp == NULL) {
55603831d35Sstevel 		error = EBADF;
55703831d35Sstevel 	} else if ((mailboxp->mbox_direction != MBOXSC_MBOX_OUT) ||
55803831d35Sstevel 	    (length + MBOXSC_PROTOCOL_SIZE > mailboxp->mbox_length) ||
55903831d35Sstevel 	    ((header.msg_transid == mailboxp->mbox_header.msg_transid) &&
56003831d35Sstevel 	    ((type & mailboxp->mbox_header.msg_type) != MBOXSC_MSG_REPLY) &&
56103831d35Sstevel 	    (header.msg_transid != 0))) {
56203831d35Sstevel 		error = EINVAL;
56303831d35Sstevel 	}
56403831d35Sstevel 
56503831d35Sstevel 	if (error != 0) {
56603831d35Sstevel 		mutex_exit(&mboxsc_lock);
56703831d35Sstevel 		DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
56803831d35Sstevel 		    "mboxsc_putmsg ret: 0x%08x\n", error);
56903831d35Sstevel 		return (error);
57003831d35Sstevel 	}
57103831d35Sstevel 
57203831d35Sstevel 	/*
57303831d35Sstevel 	 * If the message's transaction ID is set to 0, generate a unique
57403831d35Sstevel 	 * transaction ID and copy it into the message header.  If the message
57503831d35Sstevel 	 * is successfully delivered and transidp != NULL, we'll copy this new
57603831d35Sstevel 	 * transid into *transidp later.
57703831d35Sstevel 	 */
57803831d35Sstevel 	if (header.msg_transid == 0) {
57903831d35Sstevel 		header.msg_transid =
58003831d35Sstevel 		    mboxsc_generate_transid(mailboxp->mbox_header.msg_transid);
58103831d35Sstevel 	}
58203831d35Sstevel 
58303831d35Sstevel 	/*
58403831d35Sstevel 	 * Don't allow mboxsc_putmsg to attempt to place a message for
58503831d35Sstevel 	 * longer than the caller's timeout.
58603831d35Sstevel 	 */
58703831d35Sstevel 	deadline = ddi_get_lbolt() +
58803831d35Sstevel 	    drv_usectohz(timeout * MBOXSC_USECS_PER_MSEC);
58903831d35Sstevel 
59003831d35Sstevel 	/*
59103831d35Sstevel 	 * Increment the reference count on the mailbox to keep it from being
59203831d35Sstevel 	 * closed, and wait for it to become available.
59303831d35Sstevel 	 */
59403831d35Sstevel 	mboxsc_reference_mailbox(mailboxp);
59503831d35Sstevel 	remainder = 1;
59603831d35Sstevel 	while ((mailboxp->mbox_state & STATE_WRITING) &&
59703831d35Sstevel 	    (remainder > 0)) {
59803831d35Sstevel 		remainder = cv_timedwait_sig(&(mailboxp->mbox_wait),
59903831d35Sstevel 		    &mboxsc_lock, deadline);
60003831d35Sstevel 	}
60103831d35Sstevel 
60203831d35Sstevel 	/*
60303831d35Sstevel 	 * Check to see whether or not the mailbox became available.  If it
60403831d35Sstevel 	 * did not, decrement its reference count and return an error to the
60503831d35Sstevel 	 * caller.
60603831d35Sstevel 	 */
60703831d35Sstevel 	if (remainder == -1) {
60803831d35Sstevel 		error = ENOSPC;
60903831d35Sstevel 	} else if (remainder == 0) {
61003831d35Sstevel 		error = EINTR;
61103831d35Sstevel 	}
61203831d35Sstevel 
61303831d35Sstevel 	if (error != 0) {
61403831d35Sstevel 		mboxsc_dereference_mailbox(mailboxp);
61503831d35Sstevel 		mutex_exit(&mboxsc_lock);
61603831d35Sstevel 		DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
61703831d35Sstevel 		    "mboxsc_putmsg ret: 0x%08x\n", error);
61803831d35Sstevel 		return (error);
61903831d35Sstevel 	}
62003831d35Sstevel 
62103831d35Sstevel 	/*
62203831d35Sstevel 	 * Since the message is valid and we're going to try to write it to
62303831d35Sstevel 	 * IOSRAM, record its header for future reference (e.g. to make sure the
62403831d35Sstevel 	 * next message doesn't incorrectly use the same transID).
62503831d35Sstevel 	 */
62603831d35Sstevel 	bcopy(&header, &(mailboxp->mbox_header), MBOXSC_MSGHDR_SIZE);
62703831d35Sstevel 
62803831d35Sstevel 	/*
62903831d35Sstevel 	 * Flag the mailbox as being in use and release the global lock.
63003831d35Sstevel 	 */
63103831d35Sstevel 	mailboxp->mbox_state |= STATE_WRITING;
63203831d35Sstevel 	mutex_exit(&mboxsc_lock);
63303831d35Sstevel 
63403831d35Sstevel 	/*
63503831d35Sstevel 	 * Calculate the message checksum using the header and the data.
63603831d35Sstevel 	 */
63703831d35Sstevel 	checksum = mboxsc_checksum(CHKSUM_INIT, (uint8_t *)&header,
63803831d35Sstevel 	    MBOXSC_MSGHDR_SIZE);
63903831d35Sstevel 	checksum = mboxsc_checksum(checksum, (uint8_t *)datap, length);
64003831d35Sstevel 
64103831d35Sstevel 	/*
64203831d35Sstevel 	 * Attempt to write the message and checksum to IOSRAM until successful,
64303831d35Sstevel 	 * or as long as time remains and no errors other than EAGAIN are
64403831d35Sstevel 	 * returned from any call to the IOSRAM driver in case there is a tunnel
64503831d35Sstevel 	 * switch in progress.
64603831d35Sstevel 	 */
64703831d35Sstevel 	error = mboxsc_timed_write(deadline, key, MBOXSC_MSGHDR_OFFSET,
64803831d35Sstevel 	    MBOXSC_MSGHDR_SIZE, (caddr_t)&header);
64903831d35Sstevel 
65003831d35Sstevel 	if (error == 0) {
65103831d35Sstevel 		error = mboxsc_timed_write(deadline, key, MBOXSC_DATA_OFFSET,
65203831d35Sstevel 		    length, (caddr_t)datap);
65303831d35Sstevel 	}
65403831d35Sstevel 
65503831d35Sstevel 	if (error == 0) {
65603831d35Sstevel 		error = mboxsc_timed_write(deadline, key, header.msg_length,
65703831d35Sstevel 		    MBOXSC_CHKSUM_SIZE, (caddr_t)&checksum);
65803831d35Sstevel 	}
65903831d35Sstevel 
66003831d35Sstevel 	/*
66103831d35Sstevel 	 * Lock the flags before setting data_valid.  This isn't strictly
66203831d35Sstevel 	 * necessary for correct protocol operation, but it gives us a chance to
66303831d35Sstevel 	 * verify that the flags lock is functional before we commit to sending
66403831d35Sstevel 	 * the message.
66503831d35Sstevel 	 */
66603831d35Sstevel 	if (error == 0) {
66703831d35Sstevel 		error = mboxsc_lock_flags(FALSE, deadline);
66803831d35Sstevel 		if (error == 0) {
66903831d35Sstevel 			lock_held = 1;
67003831d35Sstevel 		} else if (error == EBUSY) {
67103831d35Sstevel 			error = EAGAIN;
67203831d35Sstevel 		}
67303831d35Sstevel 	}
67403831d35Sstevel 
67503831d35Sstevel 	if (error == 0) {
67603831d35Sstevel 		error = mboxsc_timed_set_flag(deadline, key, IOSRAM_DATA_VALID,
67703831d35Sstevel 		    IOSRAM_INT_TO_SSC);
67803831d35Sstevel 	}
67903831d35Sstevel 
68003831d35Sstevel 	/*
68103831d35Sstevel 	 * Unlock the flags.  If an error is encountered, only return it if
68203831d35Sstevel 	 * another error hasn't been encountered previously.
68303831d35Sstevel 	 */
68403831d35Sstevel 	if (lock_held) {
68503831d35Sstevel 		unlock_err = mboxsc_unlock_flags(TRUE);
68603831d35Sstevel 		if ((unlock_err != 0) && ((error == 0) || (error == EAGAIN))) {
68703831d35Sstevel 			error = unlock_err;
68803831d35Sstevel 		}
68903831d35Sstevel 	}
69003831d35Sstevel 
69103831d35Sstevel 	/*
69203831d35Sstevel 	 * If time ran out or an IOSRAM call failed, notify other callers that
69303831d35Sstevel 	 * the mailbox is available, decrement its reference count, and return
69403831d35Sstevel 	 * an error.
69503831d35Sstevel 	 */
69603831d35Sstevel 	if (error != 0) {
69703831d35Sstevel 		ASSERT((error != EINVAL) && (error != EMSGSIZE));
69803831d35Sstevel 		mutex_enter(&mboxsc_lock);
69903831d35Sstevel 		mailboxp->mbox_state &= ~STATE_WRITING;
70003831d35Sstevel 		cv_broadcast(&(mailboxp->mbox_wait));
70103831d35Sstevel 		mboxsc_dereference_mailbox(mailboxp);
70203831d35Sstevel 		mutex_exit(&mboxsc_lock);
70303831d35Sstevel 		DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
70403831d35Sstevel 		    "mboxsc_putmsg ret: 0x%08x\n", error);
70503831d35Sstevel 		return (error);
70603831d35Sstevel 	}
70703831d35Sstevel 
70803831d35Sstevel 	/*
70903831d35Sstevel 	 * Send an interrupt to the remote mailbox interface to announce the
71003831d35Sstevel 	 * presence of a new, valid message.
71103831d35Sstevel 	 */
71203831d35Sstevel 	error = mboxsc_timed_send_intr(deadline);
71303831d35Sstevel 
71403831d35Sstevel 	/*
71503831d35Sstevel 	 * Wait until either the data_valid flag is set INVALID by the
71603831d35Sstevel 	 * remote client or time runs out.  Since we're calling delay as
71703831d35Sstevel 	 * a part of polling the flag anyway, we don't really need to do
71803831d35Sstevel 	 * the usual continuous retry if iosram_get_flag returns EAGAIN.
71903831d35Sstevel 	 */
72003831d35Sstevel 	data_valid = IOSRAM_DATA_VALID;
72103831d35Sstevel 	if (error == DDI_SUCCESS) {
72203831d35Sstevel 		do {
72303831d35Sstevel 			delay(MIN(PUTMSG_POLL, deadline - ddi_get_lbolt()));
72403831d35Sstevel 			error = iosram_get_flag(key, &data_valid, NULL);
72503831d35Sstevel 		} while ((data_valid == IOSRAM_DATA_VALID) &&
72603831d35Sstevel 		    ((error == EAGAIN) || (error == 0)) &&
72703831d35Sstevel 		    (deadline - ddi_get_lbolt() >= 0));
72803831d35Sstevel 	}
72903831d35Sstevel 
73003831d35Sstevel 	/*
73103831d35Sstevel 	 * If the data_valid flag was set to INVALID by the other side, the
73203831d35Sstevel 	 * message was successfully transmitted.  If it wasn't, but there
73303831d35Sstevel 	 * weren't any IOSRAM errors, the operation timed out.  If there was a
73403831d35Sstevel 	 * problem with the IOSRAM, pass that info back to the caller.
73503831d35Sstevel 	 */
73603831d35Sstevel 	if (data_valid == IOSRAM_DATA_INVALID) {
73703831d35Sstevel 		result = 0;
73803831d35Sstevel 	} else if ((error == 0) || (error == DDI_FAILURE)) {
73903831d35Sstevel 		result = ETIMEDOUT;
74003831d35Sstevel 	} else {
74103831d35Sstevel 		ASSERT(error != EINVAL);
74203831d35Sstevel 		result = error;
74303831d35Sstevel 	}
74403831d35Sstevel 
74503831d35Sstevel 	/*
74603831d35Sstevel 	 * If the message has not been picked up, expire it. Note that this may
74703831d35Sstevel 	 * actually result in detecting successful message delivery if the SC
74803831d35Sstevel 	 * picks it up at the last moment.  If expiration fails due to an error,
74903831d35Sstevel 	 * return an error to the user even if the message appears to have
75003831d35Sstevel 	 * been successfully delivered.
75103831d35Sstevel 	 */
75203831d35Sstevel 	if (data_valid == IOSRAM_DATA_VALID) {
75303831d35Sstevel 		error = mboxsc_expire_message(key, &result);
75403831d35Sstevel 		if ((error != 0) && ((result == 0) || (result == ETIMEDOUT))) {
75503831d35Sstevel 			result = error;
75603831d35Sstevel 		}
75703831d35Sstevel 	}
75803831d35Sstevel 
75903831d35Sstevel 	/*
76003831d35Sstevel 	 * If the message was successfully delivered, and we generated a
76103831d35Sstevel 	 * transaction ID for the caller, and the caller wants to know what it
76203831d35Sstevel 	 * was, give it to them.
76303831d35Sstevel 	 */
76403831d35Sstevel 	if ((result == 0) && (transidp != NULL) && (*transidp == 0)) {
76503831d35Sstevel 		*transidp = header.msg_transid;
76603831d35Sstevel 	}
76703831d35Sstevel 
76803831d35Sstevel 	/*
76903831d35Sstevel 	 * Regardless of whether the message was successfully transmitted or
77003831d35Sstevel 	 * not, notify other callers that the mailbox is available and decrement
77103831d35Sstevel 	 * its reference count.
77203831d35Sstevel 	 */
77303831d35Sstevel 	mutex_enter(&mboxsc_lock);
77403831d35Sstevel 	mailboxp->mbox_state &= ~STATE_WRITING;
77503831d35Sstevel 	cv_broadcast(&(mailboxp->mbox_wait));
77603831d35Sstevel 	mboxsc_dereference_mailbox(mailboxp);
77703831d35Sstevel 	mutex_exit(&mboxsc_lock);
77803831d35Sstevel 
77903831d35Sstevel 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_putmsg ret: 0x%08x\n",
78003831d35Sstevel 	    result);
78103831d35Sstevel 	return (result);
78203831d35Sstevel }
78303831d35Sstevel 
78403831d35Sstevel /*
78503831d35Sstevel  * mboxsc_getmsg
78603831d35Sstevel  *
78703831d35Sstevel  * Attempt to retrieve a message from the mailbox with the given key that
78803831d35Sstevel  * matches values provided in msgp.  A successful return (0) indicates that
78903831d35Sstevel  * a message matching the caller's request was successfully received within
79003831d35Sstevel  * timeout milliseconds.  If a message matching the caller's request is
79103831d35Sstevel  * detected, but can't be successfully read, an error will be returned even
79203831d35Sstevel  * if the caller's timeout hasn't expired.
79303831d35Sstevel  */
79403831d35Sstevel int
mboxsc_getmsg(uint32_t key,uint32_t * typep,uint32_t * cmdp,uint64_t * transidp,uint32_t * lengthp,void * datap,clock_t timeout)79503831d35Sstevel mboxsc_getmsg(uint32_t key, uint32_t *typep, uint32_t *cmdp, uint64_t *transidp,
79603831d35Sstevel 		uint32_t *lengthp, void *datap, clock_t timeout)
79703831d35Sstevel {
79803831d35Sstevel 	int		error = 0;
79903831d35Sstevel 	uint32_t	datalen;
80003831d35Sstevel 	uint8_t		data_valid;
80103831d35Sstevel 	uint8_t		lock_held;
80203831d35Sstevel 	mboxsc_chksum_t	read_checksum;
80303831d35Sstevel 	mboxsc_chksum_t	calc_checksum;
80403831d35Sstevel 	uint64_t	read_transid;
80503831d35Sstevel 	clock_t		deadline;
80603831d35Sstevel 	clock_t		remainder;
80703831d35Sstevel 	mboxsc_mbox_t	*mailboxp;
80803831d35Sstevel 	mboxsc_msghdr_t	header;
80903831d35Sstevel 
81003831d35Sstevel #ifdef DEBUG /* because lint whines about if stmts without consequents */
81103831d35Sstevel 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_getmsg called\n");
81203831d35Sstevel 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key);
81307d06da5SSurya Prakki 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "typep = %p\n", (void *)typep);
81403831d35Sstevel 	if (typep != NULL) {
81503831d35Sstevel 		DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "*typep = 0x%x\n", *typep);
81603831d35Sstevel 	}
81707d06da5SSurya Prakki 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "cmdp = %p\n", (void *)cmdp);
81803831d35Sstevel 	if (cmdp != NULL) {
81903831d35Sstevel 		DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "*cmdp = 0x%x\n", *cmdp);
82003831d35Sstevel 	}
82107d06da5SSurya Prakki 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "transidp = %p\n", (void *)transidp);
82203831d35Sstevel 	if (transidp != NULL) {
82307d06da5SSurya Prakki 		DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "*transidp = 0x%lx\n",
82403831d35Sstevel 		    *transidp);
82503831d35Sstevel 	}
82607d06da5SSurya Prakki 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "lengthp = %p\n", (void *)lengthp);
82703831d35Sstevel 	if (lengthp != NULL) {
82803831d35Sstevel 		DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "*lengthp = 0x%x\n",
82903831d35Sstevel 		    *lengthp);
83003831d35Sstevel 	}
83103831d35Sstevel 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "datap = %p\n", datap);
83203831d35Sstevel 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "timeout = %ld\n", timeout);
83303831d35Sstevel #endif /* DEBUG */
83403831d35Sstevel 
83503831d35Sstevel 	/*
83603831d35Sstevel 	 * Perform basic sanity checks on the caller's request.
83703831d35Sstevel 	 */
83803831d35Sstevel 	if ((typep == NULL) || (*typep >= (1 << MBOXSC_NUM_MSG_TYPES)) ||
83903831d35Sstevel 	    (cmdp == NULL) || (transidp == NULL) || (lengthp == NULL) ||
84003831d35Sstevel 	    ((datap == NULL) && (*lengthp != 0)) ||
84103831d35Sstevel 	    (timeout < MBOXSC_GETMSG_MIN_TIMEOUT_MSECS) ||
84203831d35Sstevel 	    (timeout > MBOXSC_GETMSG_MAX_TIMEOUT_MSECS)) {
84303831d35Sstevel 		DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
84403831d35Sstevel 		    "mboxsc_getmsg ret: 0x%08x\n", EINVAL);
84503831d35Sstevel 		return (EINVAL);
84603831d35Sstevel 	}
84703831d35Sstevel 
84803831d35Sstevel 	/*
84903831d35Sstevel 	 * Don't allow mboxsc_getmsg to attempt to receive a message for
85003831d35Sstevel 	 * longer than the caller's timeout.
85103831d35Sstevel 	 */
85203831d35Sstevel 	deadline = ddi_get_lbolt() +
85303831d35Sstevel 	    drv_usectohz(timeout * MBOXSC_USECS_PER_MSEC);
85403831d35Sstevel 
85503831d35Sstevel 	/*
85603831d35Sstevel 	 * Perform additional sanity checks on the client's request and the
85703831d35Sstevel 	 * associated mailbox.
85803831d35Sstevel 	 */
85903831d35Sstevel 	mutex_enter(&mboxsc_lock);
86003831d35Sstevel 	mailboxp = mboxsc_hashfind_mailbox_by_key(key);
86103831d35Sstevel 	if (mailboxp == NULL) {
86203831d35Sstevel 		error = EBADF;
86303831d35Sstevel 	} else if (mailboxp->mbox_direction != MBOXSC_MBOX_IN) {
86403831d35Sstevel 		error = EINVAL;
86503831d35Sstevel 	}
86603831d35Sstevel 
86703831d35Sstevel 	if (error != 0) {
86803831d35Sstevel 		mutex_exit(&mboxsc_lock);
86903831d35Sstevel 		DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
87003831d35Sstevel 		    "mboxsc_getmsg ret: 0x%08x\n", error);
87103831d35Sstevel 		return (error);
87203831d35Sstevel 	}
87303831d35Sstevel 
87403831d35Sstevel 	/*
87503831d35Sstevel 	 * The request is okay, so reference the mailbox (to keep it from being
87603831d35Sstevel 	 * closed), and proceed with the real work.
87703831d35Sstevel 	 */
87803831d35Sstevel 	mboxsc_reference_mailbox(mailboxp);
87903831d35Sstevel 
88003831d35Sstevel 	/*
88103831d35Sstevel 	 * Certain failures that may occur late in the process of getting a
88203831d35Sstevel 	 * message (e.g. checksum error, cancellation by the sender) are
88303831d35Sstevel 	 * supposed to leave the recipient waiting for the next message to
88403831d35Sstevel 	 * arrive rather than returning an error.  To facilitate restarting
88503831d35Sstevel 	 * the message acquisition process, the following label is provided
88603831d35Sstevel 	 * as a target for a very few judiciously-placed "goto"s.
88703831d35Sstevel 	 *
88803831d35Sstevel 	 * The mboxsc_lock mutex MUST be held when jumping to this point.
88903831d35Sstevel 	 */
89003831d35Sstevel mboxsc_getmsg_retry:
89103831d35Sstevel 	;
89203831d35Sstevel 
89303831d35Sstevel 	/*
89403831d35Sstevel 	 * If there is a valid message in the mailbox right now, check to
89503831d35Sstevel 	 * see if it matches the caller's request.  If not, or if another
89603831d35Sstevel 	 * caller is already reading it, wait for either the arrival of the
89703831d35Sstevel 	 * next message or the expiration of the caller's specified timeout.
89803831d35Sstevel 	 */
89903831d35Sstevel 	error = 0;
90003831d35Sstevel 	while (!(mailboxp->mbox_state & STATE_HDRVALID) ||
90103831d35Sstevel 	    (mailboxp->mbox_state & STATE_READING) ||
90203831d35Sstevel 	    !MSG_TYPE_MATCHES(*typep, &(mailboxp->mbox_header)) ||
90303831d35Sstevel 	    !MSG_CMD_MATCHES(*cmdp, &(mailboxp->mbox_header)) ||
90403831d35Sstevel 	    !MSG_TRANSID_MATCHES(*transidp, &(mailboxp->mbox_header))) {
90503831d35Sstevel 		remainder = cv_timedwait_sig(&(mailboxp->mbox_wait),
90603831d35Sstevel 		    &mboxsc_lock, deadline);
90703831d35Sstevel 		if (remainder == -1) {
90803831d35Sstevel 			error = ETIMEDOUT;
90903831d35Sstevel 		} else if (remainder == 0) {
91003831d35Sstevel 			error = EINTR;
91103831d35Sstevel 		}
91203831d35Sstevel 
91303831d35Sstevel 		if (error != 0) {
91403831d35Sstevel 			mboxsc_dereference_mailbox(mailboxp);
91503831d35Sstevel 			mutex_exit(&mboxsc_lock);
91603831d35Sstevel 			DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
91703831d35Sstevel 			    "mboxsc_getmsg ret: 0x%08x\n", error);
91803831d35Sstevel 			return (error);
91903831d35Sstevel 		}
92003831d35Sstevel 	}
92103831d35Sstevel 
92203831d35Sstevel 	/*
92303831d35Sstevel 	 * If somebody sends us a message using a Mailbox Protocol version
92403831d35Sstevel 	 * greater than the highest one we understand, invalidate the message,
92503831d35Sstevel 	 * because we can't safely interpret anything beyond the version field.
92603831d35Sstevel 	 */
92703831d35Sstevel 	if (mailboxp->mbox_header.msg_version > MBOXSC_PROTOCOL_VERSION) {
92803831d35Sstevel 		DPRINTF1(DBG_DEV, DBGACT_DEFAULT,
92903831d35Sstevel 		    "incoming message with unsupported version %d\n",
93003831d35Sstevel 		    mailboxp->mbox_header.msg_version);
93103831d35Sstevel 		mailboxp->mbox_state &= ~STATE_HDRVALID;
93203831d35Sstevel 		goto mboxsc_getmsg_retry;
93303831d35Sstevel 	}
93403831d35Sstevel 
93503831d35Sstevel 	/*
93603831d35Sstevel 	 * At this point, there is a stored message header that matches the
93703831d35Sstevel 	 * caller's request, but the actual message may no longer be valid
93803831d35Sstevel 	 * in IOSRAM.  Check the data_valid flag to see whether or not
93903831d35Sstevel 	 * this is the case.  If the message has expired, go start over.
94003831d35Sstevel 	 *
94103831d35Sstevel 	 * The global mutex is held while reading flag data from IOSRAM to
94203831d35Sstevel 	 * avoid certain race conditions.  One race condition is still
94303831d35Sstevel 	 * possible (i.e. SC-side has just set the data_valid flag for a
94403831d35Sstevel 	 * new message, but the stored message header hasn't been updated
94503831d35Sstevel 	 * yet), but it won't cause incorrect behavior (just some wasted work).
94603831d35Sstevel 	 */
94703831d35Sstevel 	error = iosram_get_flag(key, &data_valid, NULL);
94803831d35Sstevel 
94903831d35Sstevel 	ASSERT(error != EINVAL);
95003831d35Sstevel 	if (error == 0) {
95103831d35Sstevel 		if (data_valid != IOSRAM_DATA_VALID) {
95203831d35Sstevel 			mailboxp->mbox_state &= ~STATE_HDRVALID;
95303831d35Sstevel 			goto mboxsc_getmsg_retry;
95403831d35Sstevel 		}
95503831d35Sstevel 	} else if ((error == EAGAIN) && (deadline - ddi_get_lbolt() >= 0)) {
95603831d35Sstevel 		mutex_exit(&mboxsc_lock);
95703831d35Sstevel 		delay(MIN(EAGAIN_POLL, deadline - ddi_get_lbolt()));
95803831d35Sstevel 		mutex_enter(&mboxsc_lock);
95903831d35Sstevel 		goto mboxsc_getmsg_retry;
96003831d35Sstevel 	}
96103831d35Sstevel 
96203831d35Sstevel 	/*
96303831d35Sstevel 	 * If the message is larger than the caller's buffer, provide the caller
96403831d35Sstevel 	 * with the length of the message and return an error.
96503831d35Sstevel 	 */
96603831d35Sstevel 	datalen = mailboxp->mbox_header.msg_length - MBOXSC_MSGHDR_SIZE;
96703831d35Sstevel 	if ((error == 0) && (datalen > *lengthp)) {
96803831d35Sstevel 		*lengthp = datalen;
96903831d35Sstevel 		error = EMSGSIZE;
97003831d35Sstevel 	}
97103831d35Sstevel 
97203831d35Sstevel 	/*
97303831d35Sstevel 	 * Note that there's no need to check STATE_HDRVALID before broadcasting
97403831d35Sstevel 	 * here because the header is guaranteed to be valid at this point.
97503831d35Sstevel 	 */
97603831d35Sstevel 	if (error != 0) {
97703831d35Sstevel 		cv_broadcast(&(mailboxp->mbox_wait));
97803831d35Sstevel 		mboxsc_dereference_mailbox(mailboxp);
97903831d35Sstevel 		mutex_exit(&mboxsc_lock);
98003831d35Sstevel 		DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
98103831d35Sstevel 		    "mboxsc_getmsg ret: 0x%08x\n", error);
98203831d35Sstevel 		return (error);
98303831d35Sstevel 	}
98403831d35Sstevel 
98503831d35Sstevel 	/*
98603831d35Sstevel 	 * Store a copy of the current message header, flag the mailbox to
98703831d35Sstevel 	 * indicate that it is being read and attempt to read the message data
98803831d35Sstevel 	 * and checksum.
98903831d35Sstevel 	 */
99003831d35Sstevel 	bcopy(&(mailboxp->mbox_header), &header, MBOXSC_MSGHDR_SIZE);
99103831d35Sstevel 	mailboxp->mbox_state |= STATE_READING;
99203831d35Sstevel 	mutex_exit(&mboxsc_lock);
99303831d35Sstevel 
99403831d35Sstevel 	if (datalen > 0) {
99503831d35Sstevel 		error = mboxsc_timed_read(deadline, key, MBOXSC_DATA_OFFSET,
99603831d35Sstevel 		    datalen, (caddr_t)datap);
99703831d35Sstevel 	}
99803831d35Sstevel 
99903831d35Sstevel 	if (error == 0) {
100003831d35Sstevel 		error = mboxsc_timed_read(deadline, key, header.msg_length,
100103831d35Sstevel 		    MBOXSC_CHKSUM_SIZE, (caddr_t)&read_checksum);
100203831d35Sstevel 	}
100303831d35Sstevel 
100403831d35Sstevel 	/*
100503831d35Sstevel 	 * Check for errors that may have occurred while accessing IOSRAM.
100603831d35Sstevel 	 */
100703831d35Sstevel 	if (error != 0) {
100803831d35Sstevel 		ASSERT((error != EINVAL) && (error != EMSGSIZE));
100903831d35Sstevel 		mutex_enter(&mboxsc_lock);
101003831d35Sstevel 		mailboxp->mbox_state &= ~STATE_READING;
101103831d35Sstevel 		if (mailboxp->mbox_state & STATE_HDRVALID) {
101203831d35Sstevel 			cv_broadcast(&(mailboxp->mbox_wait));
101303831d35Sstevel 		}
101403831d35Sstevel 		mboxsc_dereference_mailbox(mailboxp);
101503831d35Sstevel 		mutex_exit(&mboxsc_lock);
101603831d35Sstevel 		DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
101703831d35Sstevel 		    "mboxsc_getmsg ret: 0x%08x\n", error);
101803831d35Sstevel 		return (error);
101903831d35Sstevel 	}
102003831d35Sstevel 
102103831d35Sstevel 	/*
102203831d35Sstevel 	 * Calculate the checksum for the header and data that was read from
102303831d35Sstevel 	 * IOSRAM.
102403831d35Sstevel 	 */
102503831d35Sstevel 	calc_checksum = mboxsc_checksum(CHKSUM_INIT, (uint8_t *)&header,
102603831d35Sstevel 	    MBOXSC_MSGHDR_SIZE);
102703831d35Sstevel 	calc_checksum = mboxsc_checksum(calc_checksum, (uint8_t *)datap,
102803831d35Sstevel 	    datalen);
102903831d35Sstevel 
103003831d35Sstevel 	/*
103103831d35Sstevel 	 * If the message header has been invalidated, note the change.
103203831d35Sstevel 	 * If a the checksum verification fails, invalidate the message
103303831d35Sstevel 	 * header.  In either case, go back to the beginning and wait
103403831d35Sstevel 	 * for a new message.
103503831d35Sstevel 	 */
103603831d35Sstevel 	mutex_enter(&mboxsc_lock);
103703831d35Sstevel 	if (!(mailboxp->mbox_state & STATE_HDRVALID)) {
103803831d35Sstevel 		error = -1;
103903831d35Sstevel 		DPRINTF0(DBG_DEV, DBGACT_DEFAULT,
104003831d35Sstevel 		    "mboxsc_getmsg - message invalidated while reading\n");
104103831d35Sstevel 	} else if (read_checksum != calc_checksum) {
104203831d35Sstevel 		error = -1;
104303831d35Sstevel 		mailboxp->mbox_state &= ~STATE_HDRVALID;
104403831d35Sstevel 		DPRINTF0(DBG_DEV, DBGACT_DEFAULT,
104503831d35Sstevel 		    "mboxsc_getmsg - message failed checksum\n");
104603831d35Sstevel 		cmn_err(CE_NOTE,
104703831d35Sstevel 		    "mboxsc_getmsg - message failed checksum\n");
104803831d35Sstevel 	}
104903831d35Sstevel 
105003831d35Sstevel 	if (error == -1) {
105103831d35Sstevel 		mailboxp->mbox_state &= ~STATE_READING;
105203831d35Sstevel 		goto mboxsc_getmsg_retry;
105303831d35Sstevel 	}
105403831d35Sstevel 
105503831d35Sstevel 	/*
105603831d35Sstevel 	 * Acquire the hardware lock used for synchronization of data_valid flag
105703831d35Sstevel 	 * access to avoid race conditions.  If it is acquired, try to check the
105803831d35Sstevel 	 * current data_valid flag and transaction ID to verify that the message
105903831d35Sstevel 	 * is still valid.
106003831d35Sstevel 	 */
106103831d35Sstevel 	mutex_exit(&mboxsc_lock);
106203831d35Sstevel 
106303831d35Sstevel 	if ((error = mboxsc_lock_flags(FALSE, deadline)) != 0) {
106403831d35Sstevel 		lock_held = FALSE;
106503831d35Sstevel 		/*
106603831d35Sstevel 		 * We don't "do" EBUSY here, so treat it as EAGAIN.
106703831d35Sstevel 		 */
106803831d35Sstevel 		if (error == EBUSY) {
106903831d35Sstevel 			error = EAGAIN;
107003831d35Sstevel 		}
107103831d35Sstevel 	} else {
107203831d35Sstevel 		lock_held = TRUE;
107303831d35Sstevel 	}
107403831d35Sstevel 
107503831d35Sstevel 	if (error == 0) {
107603831d35Sstevel 		error = mboxsc_timed_get_flag(deadline, key, &data_valid, NULL);
107703831d35Sstevel 	}
107803831d35Sstevel 
107903831d35Sstevel 	if ((error == 0) && (data_valid == IOSRAM_DATA_VALID)) {
108003831d35Sstevel 		error = mboxsc_timed_read(deadline, key,
1081*ad9a7bd3SRichard Lowe 		    offsetof(mboxsc_msghdr_t, msg_transid),
108203831d35Sstevel 		    FIELD_SIZE(mboxsc_msghdr_t, msg_transid),
108303831d35Sstevel 		    (caddr_t)&read_transid);
108403831d35Sstevel 	}
108503831d35Sstevel 
108603831d35Sstevel 	/*
108703831d35Sstevel 	 * If something failed along the way, either the error is unrecoverable
108803831d35Sstevel 	 * or we're just plain out of time, so unlock the flags if they were
108903831d35Sstevel 	 * locked, release the mailbox, wake up other potential readers if
109003831d35Sstevel 	 * there's still a message around, and return.
109103831d35Sstevel 	 */
109203831d35Sstevel 	if (error != 0) {
109303831d35Sstevel 		ASSERT((error != EINVAL) && (error != EMSGSIZE));
109403831d35Sstevel 		if (lock_held) {
109507d06da5SSurya Prakki 			(void) mboxsc_unlock_flags(TRUE);
109603831d35Sstevel 		}
109703831d35Sstevel 		mutex_enter(&mboxsc_lock);
109803831d35Sstevel 		mailboxp->mbox_state &= ~STATE_READING;
109903831d35Sstevel 		if (mailboxp->mbox_state & STATE_HDRVALID) {
110003831d35Sstevel 			cv_broadcast(&(mailboxp->mbox_wait));
110103831d35Sstevel 		}
110203831d35Sstevel 		mboxsc_dereference_mailbox(mailboxp);
110303831d35Sstevel 		mutex_exit(&mboxsc_lock);
110403831d35Sstevel 		DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
110503831d35Sstevel 		    "mboxsc_getmsg ret: 0x%08x\n", error);
110603831d35Sstevel 		return (error);
110703831d35Sstevel 	}
110803831d35Sstevel 
110903831d35Sstevel 	/*
111003831d35Sstevel 	 * If the data_valid flag isn't set to IOSRAM_DATA_VALID, or the
111103831d35Sstevel 	 * message transaction ID in IOSRAM has changed, the message being
111203831d35Sstevel 	 * read was timed out by its sender.  Since the data_valid flag can't
111303831d35Sstevel 	 * change as long as we have the flags locked, we can safely mark the
111403831d35Sstevel 	 * stored message header invalid if either the data_valid flag isn't set
111503831d35Sstevel 	 * or the stored transaction ID doesn't match the one we read.  (If
111603831d35Sstevel 	 * data_valid is set, the transaction ID shouldn't be changing
111703831d35Sstevel 	 * underneath us.)  On the other hand, if there may still be a valid
111803831d35Sstevel 	 * message, wake up any pending readers.
111903831d35Sstevel 	 */
112003831d35Sstevel 	if ((data_valid != IOSRAM_DATA_VALID) ||
112103831d35Sstevel 	    (read_transid != header.msg_transid)) {
112203831d35Sstevel 		mutex_enter(&mboxsc_lock);
112303831d35Sstevel 		mailboxp->mbox_state &= ~STATE_READING;
112403831d35Sstevel 		if ((data_valid != IOSRAM_DATA_VALID) ||
112503831d35Sstevel 		    (mailboxp->mbox_header.msg_transid != read_transid)) {
112603831d35Sstevel 			mailboxp->mbox_state &= ~STATE_HDRVALID;
112703831d35Sstevel 		} else if (mailboxp->mbox_state & STATE_HDRVALID) {
112803831d35Sstevel 			cv_broadcast(&(mailboxp->mbox_wait));
112903831d35Sstevel 		}
113003831d35Sstevel 
113103831d35Sstevel 		/*
113203831d35Sstevel 		 * Unfortunately, we can't be holding mboxsc_lock when we unlock
113303831d35Sstevel 		 * the flags.  However, we have to hold the flags until here to
113403831d35Sstevel 		 * make sure the SC doesn't change the message's state while
113503831d35Sstevel 		 * we're checking to see if we should invalidate our stored
113603831d35Sstevel 		 * header.
113703831d35Sstevel 		 */
113803831d35Sstevel 		mutex_exit(&mboxsc_lock);
113903831d35Sstevel 		error = mboxsc_unlock_flags(TRUE);
114003831d35Sstevel 		mutex_enter(&mboxsc_lock);
114103831d35Sstevel 
114203831d35Sstevel 		DPRINTF0(DBG_DEV, DBGACT_DEFAULT,
114303831d35Sstevel 		    "mboxsc_getmsg() - message invalidated by sender\n");
114403831d35Sstevel 		goto mboxsc_getmsg_retry;
114503831d35Sstevel 	}
114603831d35Sstevel 
114703831d35Sstevel 	/*
114803831d35Sstevel 	 * If everything has worked up to this point, all that remains is
114903831d35Sstevel 	 * to set the data_valid flag to IOSRAM_DATA_INVALID, tidy up, and
115003831d35Sstevel 	 * return the message.  If the flag can't be set, the message can't
115103831d35Sstevel 	 * be received, so keep trying as long as there is time.
115203831d35Sstevel 	 */
115303831d35Sstevel 	error = mboxsc_timed_set_flag(deadline, key, IOSRAM_DATA_INVALID,
115403831d35Sstevel 	    IOSRAM_INT_NONE);
115503831d35Sstevel 
115607d06da5SSurya Prakki 	(void) mboxsc_unlock_flags(TRUE);
115703831d35Sstevel 	mutex_enter(&mboxsc_lock);
115803831d35Sstevel 
115903831d35Sstevel 	if (error != 0) {
116003831d35Sstevel 		ASSERT(error != EINVAL);
116103831d35Sstevel 		mboxsc_dereference_mailbox(mailboxp);
116203831d35Sstevel 		mailboxp->mbox_state &= ~STATE_READING;
116303831d35Sstevel 		if (mailboxp->mbox_state & STATE_HDRVALID) {
116403831d35Sstevel 			cv_broadcast(&(mailboxp->mbox_wait));
116503831d35Sstevel 		}
116603831d35Sstevel 		mutex_exit(&mboxsc_lock);
116703831d35Sstevel 		DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
116803831d35Sstevel 		    "mboxsc_getmsg ret: 0x%08x\n", error);
116903831d35Sstevel 		return (error);
117003831d35Sstevel 	}
117103831d35Sstevel 
117203831d35Sstevel 	/*
117303831d35Sstevel 	 * If the message was read 100% successfully and the stored message
117403831d35Sstevel 	 * header for the mailbox still matches the message that was read,
117503831d35Sstevel 	 * invalidate it to prevent other readers from trying to read it.
117603831d35Sstevel 	 */
117703831d35Sstevel 	if (bcmp(&(mailboxp->mbox_header), &header, MBOXSC_MSGHDR_SIZE) == 0) {
117803831d35Sstevel 		mailboxp->mbox_state &= ~STATE_HDRVALID;
117903831d35Sstevel 	} else if (mailboxp->mbox_state & STATE_HDRVALID) {
118003831d35Sstevel 		cv_broadcast(&(mailboxp->mbox_wait));
118103831d35Sstevel 	}
118203831d35Sstevel 
118303831d35Sstevel 	mboxsc_dereference_mailbox(mailboxp);
118403831d35Sstevel 	mailboxp->mbox_state &= ~STATE_READING;
118503831d35Sstevel 	mutex_exit(&mboxsc_lock);
118603831d35Sstevel 
118703831d35Sstevel 	/*
118803831d35Sstevel 	 * Since we're successfully returning a message, we need to provide the
118903831d35Sstevel 	 * caller with all of the interesting header information.
119003831d35Sstevel 	 */
119103831d35Sstevel 	*typep = header.msg_type;
119203831d35Sstevel 	*cmdp = header.msg_cmd;
119303831d35Sstevel 	*transidp = header.msg_transid;
119403831d35Sstevel 	*lengthp = datalen;
119503831d35Sstevel 
119603831d35Sstevel 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_getmsg ret: 0x%08x\n", 0);
119703831d35Sstevel 	return (0);
119803831d35Sstevel }
119903831d35Sstevel 
120003831d35Sstevel /*
120103831d35Sstevel  * mboxsc_ctrl
120203831d35Sstevel  *
120303831d35Sstevel  * This routine provides access to a variety of services not available through
120403831d35Sstevel  * the basic API.
120503831d35Sstevel  */
120603831d35Sstevel int
mboxsc_ctrl(uint32_t key,uint32_t cmd,void * arg)120703831d35Sstevel mboxsc_ctrl(uint32_t key, uint32_t cmd, void *arg)
120803831d35Sstevel {
120903831d35Sstevel 	int		error = 0;
121003831d35Sstevel 	mboxsc_mbox_t	*mailboxp;
121103831d35Sstevel 
121203831d35Sstevel 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_ctrl called\n");
121303831d35Sstevel 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key);
121403831d35Sstevel 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "cmd = 0x%x\n", cmd);
121503831d35Sstevel 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "arg = %p\n", arg);
121603831d35Sstevel 
121703831d35Sstevel 	mutex_enter(&mboxsc_lock);
121803831d35Sstevel 	mailboxp = mboxsc_hashfind_mailbox_by_key(key);
121903831d35Sstevel 	if (mailboxp == NULL) {
122003831d35Sstevel 		mutex_exit(&mboxsc_lock);
122103831d35Sstevel 		DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_ctrl ret: 0x%08x\n",
122203831d35Sstevel 		    EBADF);
122303831d35Sstevel 		return (EBADF);
122403831d35Sstevel 	}
122503831d35Sstevel 
122603831d35Sstevel 	switch (cmd) {
122703831d35Sstevel 		case MBOXSC_CMD_VERSION:
122803831d35Sstevel 			/*
122903831d35Sstevel 			 * Return the Protocol version currently in use.  Since
123003831d35Sstevel 			 * there is only one version that exists right now, we
123103831d35Sstevel 			 * can't be using anything else.
123203831d35Sstevel 			 */
123303831d35Sstevel 			if (arg == NULL) {
123403831d35Sstevel 				error = EINVAL;
123503831d35Sstevel 				break;
123603831d35Sstevel 			}
123703831d35Sstevel 
123803831d35Sstevel 			*(uint32_t *)arg = MBOXSC_PROTOCOL_VERSION;
123903831d35Sstevel 			break;
124003831d35Sstevel 
124103831d35Sstevel 		case MBOXSC_CMD_MAXVERSION:
124203831d35Sstevel 			/*
124303831d35Sstevel 			 * Return the highest Protocol version that we support.
124403831d35Sstevel 			 */
124503831d35Sstevel 			if (arg == NULL) {
124603831d35Sstevel 				error = EINVAL;
124703831d35Sstevel 				break;
124803831d35Sstevel 			}
124903831d35Sstevel 
125003831d35Sstevel 			*(uint32_t *)arg = MBOXSC_PROTOCOL_VERSION;
125103831d35Sstevel 			break;
125203831d35Sstevel 
125303831d35Sstevel 		case MBOXSC_CMD_MAXDATALEN:
125403831d35Sstevel 			/*
125503831d35Sstevel 			 * Return the amount of space available for client data
125603831d35Sstevel 			 * in the indicated mailbox.
125703831d35Sstevel 			 */
125803831d35Sstevel 			if (arg == NULL) {
125903831d35Sstevel 				error = EINVAL;
126003831d35Sstevel 				break;
126103831d35Sstevel 			}
126203831d35Sstevel 
126303831d35Sstevel 			*(uint32_t *)arg = mailboxp->mbox_length -
126403831d35Sstevel 			    MBOXSC_PROTOCOL_SIZE;
126503831d35Sstevel 			break;
126603831d35Sstevel 
126703831d35Sstevel 		case MBOXSC_CMD_PUTMSG_TIMEOUT_RANGE:
126803831d35Sstevel 		{
126903831d35Sstevel 			mboxsc_timeout_range_t *rangep;
127003831d35Sstevel 
127103831d35Sstevel 			/*
127203831d35Sstevel 			 * Return the range of acceptable timeout values for
127303831d35Sstevel 			 * mboxsc_putmsg, expressed in milliseconds.
127403831d35Sstevel 			 */
127503831d35Sstevel 			if (arg == NULL) {
127603831d35Sstevel 				error = EINVAL;
127703831d35Sstevel 				break;
127803831d35Sstevel 			}
127903831d35Sstevel 
128003831d35Sstevel 			rangep = (mboxsc_timeout_range_t *)arg;
128103831d35Sstevel 			rangep->min_timeout = MBOXSC_PUTMSG_MIN_TIMEOUT_MSECS;
128203831d35Sstevel 			rangep->max_timeout = MBOXSC_PUTMSG_MAX_TIMEOUT_MSECS;
128303831d35Sstevel 			break;
128403831d35Sstevel 		}
128503831d35Sstevel 
128603831d35Sstevel 		case MBOXSC_CMD_GETMSG_TIMEOUT_RANGE:
128703831d35Sstevel 		{
128803831d35Sstevel 			mboxsc_timeout_range_t *rangep;
128903831d35Sstevel 
129003831d35Sstevel 			/*
129103831d35Sstevel 			 * Return the range of acceptable timeout values for
129203831d35Sstevel 			 * mboxsc_getmsg, expressed in milliseconds.
129303831d35Sstevel 			 */
129403831d35Sstevel 			if (arg == NULL) {
129503831d35Sstevel 				error = EINVAL;
129603831d35Sstevel 				break;
129703831d35Sstevel 			}
129803831d35Sstevel 
129903831d35Sstevel 			rangep = (mboxsc_timeout_range_t *)arg;
130003831d35Sstevel 			rangep->min_timeout = MBOXSC_GETMSG_MIN_TIMEOUT_MSECS;
130103831d35Sstevel 			rangep->max_timeout = MBOXSC_GETMSG_MAX_TIMEOUT_MSECS;
130203831d35Sstevel 			break;
130303831d35Sstevel 		}
130403831d35Sstevel 
130503831d35Sstevel 		default:
130603831d35Sstevel 			error = ENOTSUP;
130703831d35Sstevel 			break;
130803831d35Sstevel 	}
130903831d35Sstevel 
131003831d35Sstevel 	mutex_exit(&mboxsc_lock);
131103831d35Sstevel 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_ctrl ret: 0x%08x\n", error);
131203831d35Sstevel 	return (error);
131303831d35Sstevel }
131403831d35Sstevel 
131503831d35Sstevel /*
131603831d35Sstevel  * mboxsc_putmsg_def_timeout
131703831d35Sstevel  *
131803831d35Sstevel  * This routine returns the default mboxsc_putmsg timeout provided for the
131903831d35Sstevel  * convenience of clients.
132003831d35Sstevel  */
132103831d35Sstevel clock_t
mboxsc_putmsg_def_timeout(void)132203831d35Sstevel mboxsc_putmsg_def_timeout(void)
132303831d35Sstevel {
132403831d35Sstevel 	return (MBOXSC_PUTMSG_DEF_TIMEOUT_MSECS);
132503831d35Sstevel }
132603831d35Sstevel 
132703831d35Sstevel /*
132803831d35Sstevel  * mboxsc_iosram_callback
132903831d35Sstevel  *
133003831d35Sstevel  * This routine is registered with the IOSRAM driver for all inbound mailboxes,
133103831d35Sstevel  * and performs preliminary processing of all new messages.
133203831d35Sstevel  */
133303831d35Sstevel static void
mboxsc_iosram_callback(void * arg)133403831d35Sstevel mboxsc_iosram_callback(void *arg)
133503831d35Sstevel {
133603831d35Sstevel 	int		error = 0;
133703831d35Sstevel 	uint8_t		data_valid;
133803831d35Sstevel 	uint32_t	key = (uint32_t)(uintptr_t)arg;
133903831d35Sstevel 	mboxsc_mbox_t	*mailboxp;
134003831d35Sstevel 
134103831d35Sstevel 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_iosram_callback called\n");
134203831d35Sstevel 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "arg = 0x%x\n", key);
134303831d35Sstevel 
134403831d35Sstevel 	mutex_enter(&mboxsc_lock);
134503831d35Sstevel 	mailboxp = mboxsc_hashfind_mailbox_by_key(key);
134603831d35Sstevel 
134703831d35Sstevel 	/*
134803831d35Sstevel 	 * We shouldn't ever receive a callback for a mailbox that doesn't
134903831d35Sstevel 	 * exist or for an output mailbox.
135003831d35Sstevel 	 */
135103831d35Sstevel 	ASSERT(mailboxp != NULL);
135203831d35Sstevel 	ASSERT(mailboxp->mbox_direction == MBOXSC_MBOX_IN);
135303831d35Sstevel 
135403831d35Sstevel 	/*
135503831d35Sstevel 	 * Attempt to read the header of the mailbox.  If the IOSRAM returns
135603831d35Sstevel 	 * EAGAIN, indicating a tunnel switch is in progress, do not retry
135703831d35Sstevel 	 * the operation.
135803831d35Sstevel 	 */
135903831d35Sstevel 	mailboxp->mbox_state &= ~STATE_HDRVALID;
136003831d35Sstevel 	error = iosram_rd(key, MBOXSC_MSGHDR_OFFSET, MBOXSC_MSGHDR_SIZE,
136103831d35Sstevel 	    (caddr_t)&(mailboxp->mbox_header));
136203831d35Sstevel 
136303831d35Sstevel 	/*
136403831d35Sstevel 	 * If somebody sends us a message using a Mailbox Protocol version
136503831d35Sstevel 	 * greater than the highest one we understand, ignore the message,
136603831d35Sstevel 	 * because we can't safely interpret anything beyond the version field.
136703831d35Sstevel 	 */
136803831d35Sstevel 	if (mailboxp->mbox_header.msg_version > MBOXSC_PROTOCOL_VERSION) {
136903831d35Sstevel 		error = -1;
137003831d35Sstevel 		DPRINTF1(DBG_DEV, DBGACT_DEFAULT,
137103831d35Sstevel 		    "incoming message with unsupported version %d\n",
137203831d35Sstevel 		    mailboxp->mbox_header.msg_version);
137303831d35Sstevel 	}
137403831d35Sstevel 
137503831d35Sstevel 	/*
137603831d35Sstevel 	 * If this message is a repeat of a previous message (which should
137703831d35Sstevel 	 * only happen with reply messages), it is conceivable that a client
137803831d35Sstevel 	 * already executing in mboxsc_getmsg for the previous message could
137903831d35Sstevel 	 * end up receiving the new message before this callback gets a chance
138003831d35Sstevel 	 * to execute.  If that happens, the data_valid flag will already have
138103831d35Sstevel 	 * been cleared.  Call iosram_get_flag to see if that is the case, and
138203831d35Sstevel 	 * do not process the message if it is.
138303831d35Sstevel 	 */
138403831d35Sstevel 	if (error == 0) {
138503831d35Sstevel 		error = iosram_get_flag(key, &data_valid, NULL);
138603831d35Sstevel 		if ((error == 0) && (data_valid != IOSRAM_DATA_VALID)) {
138703831d35Sstevel 			error = -1;
138803831d35Sstevel 		}
138903831d35Sstevel 	}
139003831d35Sstevel 
139103831d35Sstevel 	/*
139203831d35Sstevel 	 * If the iosram_rd call failed, return.
139303831d35Sstevel 	 */
139403831d35Sstevel 	if (error != 0) {
139503831d35Sstevel 		mutex_exit(&mboxsc_lock);
139603831d35Sstevel 		DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
139703831d35Sstevel 		    "mboxsc_iosram_callback ret (0x%08x)\n", error);
139803831d35Sstevel 		return;
139903831d35Sstevel 	}
140003831d35Sstevel 
140103831d35Sstevel 	/*
140203831d35Sstevel 	 * If the message read from IOSRAM was unsolicited, invoke
140303831d35Sstevel 	 * its callback.  Otherwise, wake all threads that are waiting
140403831d35Sstevel 	 * in mboxsc_getmsg.
140503831d35Sstevel 	 */
140603831d35Sstevel 	mailboxp->mbox_state |= STATE_HDRVALID;
140703831d35Sstevel 	if (IS_UNSOLICITED_TYPE(mailboxp->mbox_header.msg_type) &&
140803831d35Sstevel 	    (mailboxp->mbox_callback != NULL)) {
140903831d35Sstevel 		mboxsc_reference_mailbox(mailboxp);
141003831d35Sstevel 		mutex_exit(&mboxsc_lock);
141103831d35Sstevel 		(*(mailboxp->mbox_callback))();
141203831d35Sstevel 		mutex_enter(&mboxsc_lock);
141303831d35Sstevel 		mboxsc_dereference_mailbox(mailboxp);
141403831d35Sstevel 	} else {
141503831d35Sstevel 		cv_broadcast(&(mailboxp->mbox_wait));
141603831d35Sstevel 	}
141703831d35Sstevel 
141803831d35Sstevel 	mutex_exit(&mboxsc_lock);
141903831d35Sstevel 
142003831d35Sstevel 	DPRINTF0(DBG_RETS, DBGACT_DEFAULT, "mboxsc_iosram_callback ret\n");
142103831d35Sstevel }
142203831d35Sstevel 
142303831d35Sstevel /*
142403831d35Sstevel  * mboxsc_hdrchange_callback
142503831d35Sstevel  *
142603831d35Sstevel  * This routine is registered with the IOSRAM driver to react to any changes SMS
142703831d35Sstevel  * makes to the IOSRAM header.
142803831d35Sstevel  */
142903831d35Sstevel static void
mboxsc_hdrchange_callback(void)143003831d35Sstevel mboxsc_hdrchange_callback(void)
143103831d35Sstevel {
143203831d35Sstevel 	int		error;
143303831d35Sstevel 	uint32_t	sms_version;
143403831d35Sstevel 
143503831d35Sstevel 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT,
143603831d35Sstevel 	    "mboxsc_hdrchange_callback called\n");
143703831d35Sstevel 
143803831d35Sstevel 	error = iosram_hdr_ctrl(IOSRAM_HDRCMD_GET_SMS_MBOX_VER,
143903831d35Sstevel 	    (void *)&sms_version);
144003831d35Sstevel 	if (error == 0) {
144103831d35Sstevel 		DPRINTF1(DBG_DEV, DBGACT_DEFAULT,
144203831d35Sstevel 		    "sms mailbox version = %d\n", sms_version);
144303831d35Sstevel 		mboxsc_active_version = MIN(MBOXSC_PROTOCOL_VERSION,
144403831d35Sstevel 		    sms_version);
144503831d35Sstevel 	}
144603831d35Sstevel 
144703831d35Sstevel 	DPRINTF0(DBG_RETS, DBGACT_DEFAULT, "mboxsc_hdrchange_callback ret\n");
144803831d35Sstevel }
144903831d35Sstevel 
145003831d35Sstevel 
145103831d35Sstevel /*
145203831d35Sstevel  * mboxsc_add_mailbox
145303831d35Sstevel  *
145403831d35Sstevel  * If no other mailbox exists with the same key as this mailbox, attempt to
145503831d35Sstevel  * retrieve its length from the IOSRAM driver and register the mboxsc callback
145603831d35Sstevel  * for the associated IOSRAM chunk.  If successful, initialize the
145703831d35Sstevel  * non-client-supplied mailbox fields and insert it into the hash table.
145803831d35Sstevel  * NOTE: The caller MUST hold mboxsc_lock to avoid corrupting the hash table.
145903831d35Sstevel  */
146003831d35Sstevel static int
mboxsc_add_mailbox(mboxsc_mbox_t * mailboxp)146103831d35Sstevel mboxsc_add_mailbox(mboxsc_mbox_t *mailboxp)
146203831d35Sstevel {
146303831d35Sstevel 	int		error = 0;
146403831d35Sstevel 	uint32_t	key = mailboxp->mbox_key;
146503831d35Sstevel 
146603831d35Sstevel 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_add_mailbox called\n");
146707d06da5SSurya Prakki 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "mailboxp = %p\n", (void *)mailboxp);
146803831d35Sstevel 
146903831d35Sstevel 	/*
147003831d35Sstevel 	 * The global lock must be held by the caller.
147103831d35Sstevel 	 */
147203831d35Sstevel 	ASSERT(mutex_owned(&mboxsc_lock));
147303831d35Sstevel 
147403831d35Sstevel 	/*
147503831d35Sstevel 	 * Don't create the mailbox if it already exists.
147603831d35Sstevel 	 */
147703831d35Sstevel 	if (mboxsc_hashfind_mailbox_by_key(key) != NULL) {
147803831d35Sstevel 		DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
147903831d35Sstevel 		    "mboxsc_add_mailbox ret: 0x%08x\n", EEXIST);
148003831d35Sstevel 		return (EEXIST);
148103831d35Sstevel 	}
148203831d35Sstevel 
148303831d35Sstevel 	/*
148403831d35Sstevel 	 * Obtain the mailbox length and register the mboxsc callback with the
148503831d35Sstevel 	 * IOSRAM driver.  If either call to the IOSRAM driver fails, or the
148603831d35Sstevel 	 * chunk is too small to be used as a mailbox, return an error to the
148703831d35Sstevel 	 * caller.
148803831d35Sstevel 	 */
148903831d35Sstevel 	error = iosram_ctrl(key, IOSRAM_CMD_CHUNKLEN, &(mailboxp->mbox_length));
149003831d35Sstevel 
149103831d35Sstevel 	if ((error == 0) && (mailboxp->mbox_length < MBOXSC_PROTOCOL_SIZE)) {
149203831d35Sstevel 		error = EFAULT;
149303831d35Sstevel 	}
149403831d35Sstevel 
149503831d35Sstevel 	if ((error == 0) && (mailboxp->mbox_direction == MBOXSC_MBOX_IN)) {
149603831d35Sstevel 		error = iosram_register(key, mboxsc_iosram_callback,
149703831d35Sstevel 		    (void *)(uintptr_t)(key));
149803831d35Sstevel 		if (error == EBUSY) {
149903831d35Sstevel 			error = EFAULT;
150003831d35Sstevel 		}
150103831d35Sstevel 	}
150203831d35Sstevel 
150303831d35Sstevel 	if (error != 0) {
150403831d35Sstevel 		DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
150503831d35Sstevel 		    "mboxsc_add_mailbox ret: 0x%08x\n", error);
150603831d35Sstevel 		return (error);
150703831d35Sstevel 	}
150803831d35Sstevel 
150903831d35Sstevel 	/*
151003831d35Sstevel 	 * Initialize remaining mailbox fields and insert mailbox into
151103831d35Sstevel 	 * hash table.
151203831d35Sstevel 	 */
151303831d35Sstevel 	mailboxp->mbox_state = STATE_IDLE;
151403831d35Sstevel 	mailboxp->mbox_refcount = 0;
151503831d35Sstevel 	cv_init(&(mailboxp->mbox_wait), NULL, CV_DRIVER, NULL);
151603831d35Sstevel 	mboxsc_hashinsert_mailbox(mailboxp);
151703831d35Sstevel 
151803831d35Sstevel 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_add_mailbox ret: 0x%08x\n",
151903831d35Sstevel 	    0);
152003831d35Sstevel 	return (0);
152103831d35Sstevel }
152203831d35Sstevel 
152303831d35Sstevel /*
152403831d35Sstevel  * mboxsc_close_mailbox
152503831d35Sstevel  *
152603831d35Sstevel  * Remove a mailbox from the hash table, unregister its IOSRAM callback, and
152703831d35Sstevel  * deallocate its resources.
152803831d35Sstevel  * NOTE: The caller MUST hold mboxsc_lock to avoid corrupting the hash table.
152903831d35Sstevel  */
153003831d35Sstevel static void
mboxsc_close_mailbox(mboxsc_mbox_t * mailboxp)153103831d35Sstevel mboxsc_close_mailbox(mboxsc_mbox_t *mailboxp)
153203831d35Sstevel {
153303831d35Sstevel 	int		error = 0;
153403831d35Sstevel 	uint32_t	key = mailboxp->mbox_key;
153503831d35Sstevel 
153603831d35Sstevel 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_close_mailbox called\n");
153707d06da5SSurya Prakki 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "mailboxp = %p\n", (void *)mailboxp);
153803831d35Sstevel 
153903831d35Sstevel 	/*
154003831d35Sstevel 	 * The global lock must be held by the caller.
154103831d35Sstevel 	 */
154203831d35Sstevel 	ASSERT(mutex_owned(&mboxsc_lock));
154303831d35Sstevel 
154403831d35Sstevel 	/*
154503831d35Sstevel 	 * Unregister the mboxsc callback for this particular mailbox.
154603831d35Sstevel 	 */
154703831d35Sstevel 	if (mailboxp->mbox_direction == MBOXSC_MBOX_IN) {
154803831d35Sstevel 		error = iosram_unregister(key);
154903831d35Sstevel 		if (error == EINVAL) {
155003831d35Sstevel 			DPRINTF1(DBG_DEV, DBGACT_DEFAULT, "invalid key (0x%08x)"
155103831d35Sstevel 			    " reported in mboxsc_close_mailbox.\n", key);
155203831d35Sstevel 			error = 0;
155303831d35Sstevel 		}
155403831d35Sstevel 	}
155503831d35Sstevel 
155603831d35Sstevel 	/*
155703831d35Sstevel 	 * Remove the mailbox from the hash table and deallocate its resources.
155803831d35Sstevel 	 */
155907d06da5SSurya Prakki 	(void) mboxsc_hashremove_mailbox_by_key(key);
156003831d35Sstevel 	cv_destroy(&(mailboxp->mbox_wait));
156107d06da5SSurya Prakki 	DPRINTF2(DBG_KMEM, DBGACT_DEFAULT, "kmem_free(%p, %lu)\n",
156207d06da5SSurya Prakki 	    (void *)mailboxp, sizeof (mboxsc_mbox_t));
156303831d35Sstevel 	kmem_free(mailboxp, sizeof (mboxsc_mbox_t));
156403831d35Sstevel 
156503831d35Sstevel 	DPRINTF0(DBG_RETS, DBGACT_DEFAULT, "mboxsc_close_mailbox ret\n");
156603831d35Sstevel }
156703831d35Sstevel 
156803831d35Sstevel /*
156903831d35Sstevel  * mboxsc_hashinsert_mailbox
157003831d35Sstevel  *
157103831d35Sstevel  * Insert a fully initialized mailbox into the hash table.  No duplicate
157203831d35Sstevel  * checking is performed at this point, so the caller is responsible for
157303831d35Sstevel  * duplicate prevention if it is desired.
157403831d35Sstevel  * NOTE: The caller MUST hold mboxsc_lock to avoid corrupting the hash table.
157503831d35Sstevel  */
157603831d35Sstevel static void
mboxsc_hashinsert_mailbox(mboxsc_mbox_t * mailboxp)157703831d35Sstevel mboxsc_hashinsert_mailbox(mboxsc_mbox_t *mailboxp)
157803831d35Sstevel {
157903831d35Sstevel 	uint32_t	hash;
158003831d35Sstevel 
158103831d35Sstevel 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT,
158203831d35Sstevel 	    "mboxsc_hashinsert_mailbox called\n");
158307d06da5SSurya Prakki 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "mailboxp = %p\n", (void *)mailboxp);
158403831d35Sstevel 
158503831d35Sstevel 	/*
158603831d35Sstevel 	 * The global lock must be held by the caller.
158703831d35Sstevel 	 */
158803831d35Sstevel 	ASSERT(mutex_owned(&mboxsc_lock));
158903831d35Sstevel 
159003831d35Sstevel 	hash = HASH_KEY(mailboxp->mbox_key);
159103831d35Sstevel 	mailboxp->mbox_hash_next = mboxsc_hash_table[hash];
159203831d35Sstevel 	mboxsc_hash_table[hash] = mailboxp;
159303831d35Sstevel 
159403831d35Sstevel 	DPRINTF0(DBG_RETS, DBGACT_DEFAULT,
159503831d35Sstevel 	    "mboxsc_hashinsert_mailbox ret\n");
159603831d35Sstevel }
159703831d35Sstevel 
159803831d35Sstevel /*
159903831d35Sstevel  * mboxsc_hashfind_mailbox_by_key
160003831d35Sstevel  *
160103831d35Sstevel  * Locate a mailbox with the given key in the hash table.  Return a pointer
160203831d35Sstevel  * to the mailbox if it exists, or NULL if no matching mailbox is found.
160303831d35Sstevel  * NOTE: The caller MUST hold mboxsc_lock to avoid corrupting the hash table.
160403831d35Sstevel  */
160503831d35Sstevel static mboxsc_mbox_t *
mboxsc_hashfind_mailbox_by_key(uint32_t key)160603831d35Sstevel mboxsc_hashfind_mailbox_by_key(uint32_t key)
160703831d35Sstevel {
160803831d35Sstevel 	uint32_t	hash;
160903831d35Sstevel 	mboxsc_mbox_t	*mailboxp;
161003831d35Sstevel 
161103831d35Sstevel 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT,
161203831d35Sstevel 	    "mboxsc_hashfind_mailbox_by_key called\n");
161303831d35Sstevel 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key);
161403831d35Sstevel 
161503831d35Sstevel 	/*
161603831d35Sstevel 	 * The global lock must be held by the caller.
161703831d35Sstevel 	 */
161803831d35Sstevel 	ASSERT(mutex_owned(&mboxsc_lock));
161903831d35Sstevel 
162003831d35Sstevel 	hash = HASH_KEY(key);
162103831d35Sstevel 	mailboxp = mboxsc_hash_table[hash];
162203831d35Sstevel 	while (mailboxp != NULL) {
162303831d35Sstevel 		if (mailboxp->mbox_key == key) {
162403831d35Sstevel 			break;
162503831d35Sstevel 		}
162603831d35Sstevel 		mailboxp = mailboxp->mbox_hash_next;
162703831d35Sstevel 	}
162803831d35Sstevel 
162903831d35Sstevel 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
163007d06da5SSurya Prakki 	    "mboxsc_hashfind_mailbox_by_key ret: %p\n", (void *)mailboxp);
163103831d35Sstevel 	return (mailboxp);
163203831d35Sstevel }
163303831d35Sstevel 
163403831d35Sstevel /*
163503831d35Sstevel  * mboxsc_hashremove_mailbox_by_key
163603831d35Sstevel  *
163703831d35Sstevel  * Locate a mailbox with the given key in the hash table.  If it exists,
163803831d35Sstevel  * remove it from the hash table and return a pointer to it.  Otherwise,
163903831d35Sstevel  * return NULL.
164003831d35Sstevel  * NOTE: The caller MUST hold mboxsc_lock to avoid corrupting the hash table.
164103831d35Sstevel  */
164203831d35Sstevel static mboxsc_mbox_t *
mboxsc_hashremove_mailbox_by_key(uint32_t key)164303831d35Sstevel mboxsc_hashremove_mailbox_by_key(uint32_t key)
164403831d35Sstevel {
164503831d35Sstevel 	uint32_t	hash;
164603831d35Sstevel 	mboxsc_mbox_t	*mailboxp;
164703831d35Sstevel 	mboxsc_mbox_t	*last;
164803831d35Sstevel 
164903831d35Sstevel 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT,
165003831d35Sstevel 	    "mboxsc_hashremove_mailbox_by_key called\n");
165103831d35Sstevel 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key);
165203831d35Sstevel 
165303831d35Sstevel 	/*
165403831d35Sstevel 	 * The global lock must be held by the caller.
165503831d35Sstevel 	 */
165603831d35Sstevel 	ASSERT(mutex_owned(&mboxsc_lock));
165703831d35Sstevel 
165803831d35Sstevel 	hash = HASH_KEY(key);
165903831d35Sstevel 	mailboxp = mboxsc_hash_table[hash];
166003831d35Sstevel 	last = NULL;
166103831d35Sstevel 	while (mailboxp != NULL) {
166203831d35Sstevel 		if (mailboxp->mbox_key == key) {
166303831d35Sstevel 			break;
166403831d35Sstevel 		}
166503831d35Sstevel 		last = mailboxp;
166603831d35Sstevel 		mailboxp = mailboxp->mbox_hash_next;
166703831d35Sstevel 	}
166803831d35Sstevel 
166903831d35Sstevel 	/*
167003831d35Sstevel 	 * If a mailbox was found, remove it from the hash table.
167103831d35Sstevel 	 */
167203831d35Sstevel 	if (mailboxp != NULL) {
167303831d35Sstevel 		if (last == NULL) {
167403831d35Sstevel 			mboxsc_hash_table[hash] = mailboxp->mbox_hash_next;
167503831d35Sstevel 		} else {
167603831d35Sstevel 			last->mbox_hash_next = mailboxp->mbox_hash_next;
167703831d35Sstevel 		}
167803831d35Sstevel 
167903831d35Sstevel 		mailboxp->mbox_hash_next = NULL;
168003831d35Sstevel 	}
168103831d35Sstevel 
168203831d35Sstevel 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
168307d06da5SSurya Prakki 	    "mboxsc_hashremove_mailbox_by_key ret: %p\n", (void *)mailboxp);
168403831d35Sstevel 	return (mailboxp);
168503831d35Sstevel }
168603831d35Sstevel 
168703831d35Sstevel /*
168803831d35Sstevel  * mboxsc_checksum
168903831d35Sstevel  *
169003831d35Sstevel  * Given a pointer to a data buffer and its length, calculate the checksum of
169103831d35Sstevel  * the data contained therein.
169203831d35Sstevel  */
169303831d35Sstevel static mboxsc_chksum_t
mboxsc_checksum(mboxsc_chksum_t seed,uint8_t * buf,uint32_t length)169403831d35Sstevel mboxsc_checksum(mboxsc_chksum_t seed, uint8_t *buf, uint32_t length)
169503831d35Sstevel {
169603831d35Sstevel 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_checksum called\n");
169703831d35Sstevel 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "seed = 0x%x\n", seed);
169807d06da5SSurya Prakki 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "buf = %p\n", (void *)buf);
169903831d35Sstevel 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "length = 0x%x\n", length);
170003831d35Sstevel 
170103831d35Sstevel 	while (length-- > 0) {
170203831d35Sstevel 		seed += *(buf++);
170303831d35Sstevel 	}
170403831d35Sstevel 
170503831d35Sstevel 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_checksum ret: 0x%08x\n",
170603831d35Sstevel 	    seed);
170703831d35Sstevel 	return (seed);
170803831d35Sstevel }
170903831d35Sstevel 
171003831d35Sstevel /*
171103831d35Sstevel  * mboxsc_lock_flags
171203831d35Sstevel  *
171303831d35Sstevel  * Acquire the hardware lock used for data_valid flag synchronization.  If the
171403831d35Sstevel  * lock is currently held by SMS and acquisition is mandatory, just keep on
171503831d35Sstevel  * trying until it is acquired.  If acquisition is not mandatory, keep trying
171603831d35Sstevel  * until the given deadline has been reached.  To avoid loading the system
171703831d35Sstevel  * unreasonably on EBUSY or EAGAIN, sleep for an appropriate amount of time
171803831d35Sstevel  * before retrying.  If a hardware error is encountered return it to the caller.
171903831d35Sstevel  *
172003831d35Sstevel  * If the lock is held, but not by SMS, clear it and acquire it.  Nobody
172103831d35Sstevel  * else should be grabbing that lock.
172203831d35Sstevel  */
172303831d35Sstevel static int
mboxsc_lock_flags(uint8_t mandatory,clock_t deadline)172403831d35Sstevel mboxsc_lock_flags(uint8_t mandatory, clock_t deadline)
172503831d35Sstevel {
172603831d35Sstevel 	int		error;
172703831d35Sstevel 	int		warned = 0;
172803831d35Sstevel 	uint32_t	sema;
172903831d35Sstevel 	clock_t		pause;
173003831d35Sstevel 	clock_t		warning_time = ddi_get_lbolt() + LOOP_WARN_INTERVAL;
173103831d35Sstevel 
173203831d35Sstevel 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_lock_flags called\n");
173303831d35Sstevel 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "mandatory = 0x%x\n", mandatory);
173407d06da5SSurya Prakki 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "deadline = 0x%lx\n", deadline);
173503831d35Sstevel 
173603831d35Sstevel 	/*
173703831d35Sstevel 	 * Keep trying to acquire the lock until successful or (if acquisition
173803831d35Sstevel 	 * is not mandatory) time runs out.  If EBUSY (lock is already held) or
173903831d35Sstevel 	 * EAGAIN (tunnel switch in progress) is encountered, sleep for an
174003831d35Sstevel 	 * appropriate amount of time before retrying.  Any other error is
174103831d35Sstevel 	 * unrecoverable.
174203831d35Sstevel 	 */
174303831d35Sstevel 	do {
174403831d35Sstevel 		pause = 0;
174503831d35Sstevel 
174603831d35Sstevel 		/*
174703831d35Sstevel 		 * Since multiple threads could conceivably want the flag lock
174803831d35Sstevel 		 * at the same time, we place the lock under a mutex and keep a
174903831d35Sstevel 		 * counter indicating how many threads have the flags locked at
175003831d35Sstevel 		 * the moment.
175103831d35Sstevel 		 */
175203831d35Sstevel 		mutex_enter(&mboxsc_lock);
175303831d35Sstevel 		if ((mboxsc_flaglock_count > 0) ||
175403831d35Sstevel 		    ((error = iosram_sema_acquire(&sema)) == 0)) {
175503831d35Sstevel 			mboxsc_flaglock_count++;
175603831d35Sstevel 			mutex_exit(&mboxsc_lock);
175703831d35Sstevel 
175803831d35Sstevel 			if (warned) {
175903831d35Sstevel 				cmn_err(CE_WARN, "Flags locked");
176003831d35Sstevel 			}
176103831d35Sstevel 			DPRINTF0(DBG_RETS, DBGACT_DEFAULT,
176203831d35Sstevel 			    "mboxsc_lock_flags ret: 0\n");
176303831d35Sstevel 			return (0);
176403831d35Sstevel 		}
176503831d35Sstevel 
176603831d35Sstevel 		/*
176703831d35Sstevel 		 * If iosram_sema_acquire returned EBUSY (lock already held),
176803831d35Sstevel 		 * make sure the lock is held by SMS, since nobody else should
176903831d35Sstevel 		 * ever be holding it.  If EBUSY or EAGAIN (tunnel switch in
177003831d35Sstevel 		 * progress) was returned, determine the appropriate amount of
177103831d35Sstevel 		 * time to sleep before trying again.
177203831d35Sstevel 		 */
177303831d35Sstevel 		if (error == EBUSY) {
177403831d35Sstevel 			if (IOSRAM_SEMA_GET_IDX(sema) != IOSRAM_SEMA_SMS_IDX) {
177507d06da5SSurya Prakki 				(void) iosram_sema_release();
177603831d35Sstevel 				cmn_err(CE_WARN,
177703831d35Sstevel 				    "Incorrect flag lock value read (0x%08x)",
177803831d35Sstevel 				    sema);
177903831d35Sstevel 			} else {
178003831d35Sstevel 				pause = (mandatory ? HWLOCK_POLL :
178103831d35Sstevel 				    MIN(HWLOCK_POLL, deadline -
178203831d35Sstevel 				    ddi_get_lbolt()));
178303831d35Sstevel 			}
178403831d35Sstevel 		} else if (error == EAGAIN) {
178503831d35Sstevel 			pause = (mandatory ? EAGAIN_POLL : MIN(EAGAIN_POLL,
178603831d35Sstevel 			    deadline - ddi_get_lbolt()));
178703831d35Sstevel 		}
178803831d35Sstevel 
178903831d35Sstevel 		/*
179003831d35Sstevel 		 * We had to hold the lock until now to protect the potential
179103831d35Sstevel 		 * iosram_sema_release call above.
179203831d35Sstevel 		 */
179303831d35Sstevel 		mutex_exit(&mboxsc_lock);
179403831d35Sstevel 
179503831d35Sstevel 		/*
179603831d35Sstevel 		 * If EAGAIN or EBUSY was encountered, we're looping.
179703831d35Sstevel 		 */
179803831d35Sstevel 		if ((error == EAGAIN) || (error == EBUSY)) {
179903831d35Sstevel 			/*
180003831d35Sstevel 			 * If we've been looping here for a while, something is
180103831d35Sstevel 			 * probably wrong, so we should generated a warning.
180203831d35Sstevel 			 */
180303831d35Sstevel 			if (warning_time - ddi_get_lbolt() <= 0) {
180403831d35Sstevel 				if (!warned) {
180503831d35Sstevel 					warned = 1;
180603831d35Sstevel 					cmn_err(CE_WARN,
180703831d35Sstevel 					    "Unable to lock flags (0x%08x)",
180803831d35Sstevel 					    error);
180903831d35Sstevel 				} else {
181003831d35Sstevel 					cmn_err(CE_WARN,
181103831d35Sstevel 					    "Still unable to lock flags");
181203831d35Sstevel 				}
181303831d35Sstevel 				warning_time = ddi_get_lbolt() +
181403831d35Sstevel 				    LOOP_WARN_INTERVAL;
181503831d35Sstevel 			}
181603831d35Sstevel 
181703831d35Sstevel 			/*
181803831d35Sstevel 			 * Sleep a while before trying again.
181903831d35Sstevel 			 */
182003831d35Sstevel 			delay(pause);
182103831d35Sstevel 		}
182203831d35Sstevel 	} while (((error == EAGAIN) || (error == EBUSY)) &&
182303831d35Sstevel 	    (mandatory || (deadline - ddi_get_lbolt() >= 0)));
182403831d35Sstevel 
182503831d35Sstevel 	/*
182603831d35Sstevel 	 * If something really bad has happened, generate a warning.
182703831d35Sstevel 	 */
182803831d35Sstevel 	if ((error != EAGAIN) && (error != EBUSY)) {
182903831d35Sstevel 		cmn_err(CE_WARN, "Flag locking failed! (%d)", error);
183003831d35Sstevel 	}
183103831d35Sstevel 
183203831d35Sstevel 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_lock_flags ret: 0x%08x\n",
183303831d35Sstevel 	    error);
183403831d35Sstevel 	return (error);
183503831d35Sstevel }
183603831d35Sstevel 
183703831d35Sstevel /*
183803831d35Sstevel  * mboxsc_unlock_flags
183903831d35Sstevel  *
184003831d35Sstevel  * Release the hardware lock used for data_valid flag synchronization.
184103831d35Sstevel  * If a hardware error is encountered, return it to the caller.  If the
184203831d35Sstevel  * mandatory flag is set, loop and retry if EAGAIN is encountered.
184303831d35Sstevel  */
184403831d35Sstevel static int
mboxsc_unlock_flags(uint8_t mandatory)184503831d35Sstevel mboxsc_unlock_flags(uint8_t mandatory)
184603831d35Sstevel {
184703831d35Sstevel 	int	error;
184803831d35Sstevel 	int	warned = 0;
184903831d35Sstevel 	clock_t	warning_time = ddi_get_lbolt() + LOOP_WARN_INTERVAL;
185003831d35Sstevel 
185103831d35Sstevel 	ASSERT(mboxsc_flaglock_count != 0);
185203831d35Sstevel 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_unlock_flags called\n");
185303831d35Sstevel 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "mandatory = 0x%x\n", mandatory);
185403831d35Sstevel 
185503831d35Sstevel 	do {
185603831d35Sstevel 		/*
185703831d35Sstevel 		 * Since multiple threads could conceivably want the flag lock
185803831d35Sstevel 		 * at the same time, we place the lock under a mutex and keep a
185903831d35Sstevel 		 * counter indicating how many threads have the flags locked at
186003831d35Sstevel 		 * the moment.
186103831d35Sstevel 		 */
186203831d35Sstevel 		mutex_enter(&mboxsc_lock);
186303831d35Sstevel 		if ((mboxsc_flaglock_count > 1) ||
186403831d35Sstevel 		    ((error = iosram_sema_release()) == 0)) {
186503831d35Sstevel 			mboxsc_flaglock_count--;
186603831d35Sstevel 			mutex_exit(&mboxsc_lock);
186703831d35Sstevel 
186803831d35Sstevel 			if (warned) {
186903831d35Sstevel 				cmn_err(CE_WARN, "Flags unlocked");
187003831d35Sstevel 			}
187103831d35Sstevel 			DPRINTF0(DBG_RETS, DBGACT_DEFAULT,
187203831d35Sstevel 			    "mboxsc_unlock_flags ret: 0\n");
187303831d35Sstevel 			return (0);
187403831d35Sstevel 		}
187503831d35Sstevel 		mutex_exit(&mboxsc_lock);
187603831d35Sstevel 
187703831d35Sstevel 		/*
187803831d35Sstevel 		 * If iosram_sema_release returned EAGAIN (tunnel switch in
187903831d35Sstevel 		 * progress) and unlocking the flags is mandatory, sleep before
188003831d35Sstevel 		 * trying again.  If we've been trying for a while, display a
188103831d35Sstevel 		 * warning message too.
188203831d35Sstevel 		 */
188303831d35Sstevel 		if ((error == EAGAIN) && mandatory) {
188403831d35Sstevel 			if (warning_time - ddi_get_lbolt() <= 0) {
188503831d35Sstevel 				if (!warned) {
188603831d35Sstevel 					warned = 1;
188703831d35Sstevel 					cmn_err(CE_WARN, "Unable to unlock "
188803831d35Sstevel 					    "flags (iosram EAGAIN)");
188903831d35Sstevel 				} else {
189003831d35Sstevel 					cmn_err(CE_WARN,
189103831d35Sstevel 					    "Still unable to unlock flags");
189203831d35Sstevel 				}
189303831d35Sstevel 				warning_time = ddi_get_lbolt() +
189403831d35Sstevel 				    LOOP_WARN_INTERVAL;
189503831d35Sstevel 			}
189603831d35Sstevel 
189703831d35Sstevel 			delay(EAGAIN_POLL);
189803831d35Sstevel 		}
189903831d35Sstevel 	} while ((error == EAGAIN) && mandatory);
190003831d35Sstevel 
190103831d35Sstevel 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_unlock_flags ret: 0x%08x\n",
190203831d35Sstevel 	    error);
190303831d35Sstevel 	return (error);
190403831d35Sstevel }
190503831d35Sstevel 
190603831d35Sstevel /*
190703831d35Sstevel  * mboxsc_timed_read
190803831d35Sstevel  *
190903831d35Sstevel  * This function is just a wrapper around iosram_rd that will keep sleeping
191003831d35Sstevel  * and retrying, up to a given deadline, if iosram_rd returns EAGAIN
191103831d35Sstevel  * (presumably due to a tunnel switch).
191203831d35Sstevel  */
191303831d35Sstevel static int
mboxsc_timed_read(clock_t deadline,uint32_t key,uint32_t off,uint32_t len,caddr_t dptr)191403831d35Sstevel mboxsc_timed_read(clock_t deadline, uint32_t key, uint32_t off, uint32_t len,
191503831d35Sstevel 	caddr_t dptr)
191603831d35Sstevel {
191703831d35Sstevel 	int error;
191803831d35Sstevel 
191903831d35Sstevel 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_timed_read called\n");
192007d06da5SSurya Prakki 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "deadline = 0x%lx\n", deadline);
192103831d35Sstevel 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key);
192203831d35Sstevel 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "off = 0x%x\n", off);
192303831d35Sstevel 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "len = 0x%x\n", len);
192407d06da5SSurya Prakki 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "dptr = %p\n", (void *)dptr);
192503831d35Sstevel 
192603831d35Sstevel 	do {
192703831d35Sstevel 		error = iosram_rd(key, off, len, dptr);
192803831d35Sstevel 		if (error == EAGAIN) {
192903831d35Sstevel 			delay(MIN(EAGAIN_POLL, deadline - ddi_get_lbolt()));
193003831d35Sstevel 		}
193103831d35Sstevel 	} while ((error == EAGAIN) && (deadline - ddi_get_lbolt() >= 0));
193203831d35Sstevel 
193303831d35Sstevel 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
193403831d35Sstevel 	    "mboxsc_timed_read ret: 0x%08x\n", error);
193503831d35Sstevel 	return (error);
193603831d35Sstevel }
193703831d35Sstevel 
193803831d35Sstevel /*
193903831d35Sstevel  * mboxsc_timed_write
194003831d35Sstevel  *
194103831d35Sstevel  * This function is just a wrapper around iosram_wr that will keep sleeping
194203831d35Sstevel  * and retrying, up to a given deadline, if iosram_wr returns EAGAIN
194303831d35Sstevel  * (presumably due to a tunnel switch).
194403831d35Sstevel  */
194503831d35Sstevel static int
mboxsc_timed_write(clock_t deadline,uint32_t key,uint32_t off,uint32_t len,caddr_t dptr)194603831d35Sstevel mboxsc_timed_write(clock_t deadline, uint32_t key, uint32_t off, uint32_t len,
194703831d35Sstevel 	caddr_t dptr)
194803831d35Sstevel {
194903831d35Sstevel 	int error;
195003831d35Sstevel 
195103831d35Sstevel 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_timed_write called\n");
195207d06da5SSurya Prakki 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "deadline = 0x%lx\n", deadline);
195303831d35Sstevel 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key);
195403831d35Sstevel 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "off = 0x%x\n", off);
195503831d35Sstevel 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "len = 0x%x\n", len);
195607d06da5SSurya Prakki 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "dptr = %p\n", (void *)dptr);
195703831d35Sstevel 
195803831d35Sstevel 	do {
195903831d35Sstevel 		error = iosram_wr(key, off, len, dptr);
196003831d35Sstevel 		if (error == EAGAIN) {
196103831d35Sstevel 			delay(MIN(EAGAIN_POLL, deadline - ddi_get_lbolt()));
196203831d35Sstevel 		}
196303831d35Sstevel 	} while ((error == EAGAIN) && (deadline - ddi_get_lbolt() >= 0));
196403831d35Sstevel 
196503831d35Sstevel 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
196603831d35Sstevel 	    "mboxsc_timed_write ret: 0x%08x\n", error);
196703831d35Sstevel 	return (error);
196803831d35Sstevel }
196903831d35Sstevel 
197003831d35Sstevel /*
197103831d35Sstevel  * mboxsc_timed_get_flag
197203831d35Sstevel  *
197303831d35Sstevel  * This function is just a wrapper around iosram_get_flag that will keep
197403831d35Sstevel  * sleeping and retrying, up to a given deadline, if iosram_get_flag returns
197503831d35Sstevel  * EAGAIN (presumably due to a tunnel switch).
197603831d35Sstevel  */
197703831d35Sstevel static int
mboxsc_timed_get_flag(clock_t deadline,uint32_t key,uint8_t * data_validp,uint8_t * int_pendingp)197803831d35Sstevel mboxsc_timed_get_flag(clock_t deadline, uint32_t key, uint8_t *data_validp,
197903831d35Sstevel 	uint8_t *int_pendingp)
198003831d35Sstevel {
198103831d35Sstevel 	int error;
198203831d35Sstevel 
198303831d35Sstevel 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_timed_get_flag called\n");
198407d06da5SSurya Prakki 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "deadline = 0x%lx\n", deadline);
198503831d35Sstevel 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key);
198607d06da5SSurya Prakki 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "data_validp = %p\n",
198707d06da5SSurya Prakki 	    (void *)data_validp);
198807d06da5SSurya Prakki 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "int_pendingp = %p\n",
198907d06da5SSurya Prakki 	    (void *)int_pendingp);
199003831d35Sstevel 
199103831d35Sstevel 	do {
199203831d35Sstevel 		error = iosram_get_flag(key, data_validp, int_pendingp);
199303831d35Sstevel 		if (error == EAGAIN) {
199403831d35Sstevel 			delay(MIN(EAGAIN_POLL, deadline - ddi_get_lbolt()));
199503831d35Sstevel 		}
199603831d35Sstevel 	} while ((error == EAGAIN) && (deadline - ddi_get_lbolt() >= 0));
199703831d35Sstevel 
199803831d35Sstevel 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
199903831d35Sstevel 	    "mboxsc_timed_get_flag ret: 0x%08x\n", error);
200003831d35Sstevel 	return (error);
200103831d35Sstevel }
200203831d35Sstevel 
200303831d35Sstevel /*
200403831d35Sstevel  * mboxsc_timed_set_flag
200503831d35Sstevel  *
200603831d35Sstevel  * This function is just a wrapper around iosram_set_flag that will keep
200703831d35Sstevel  * sleeping and retrying, up to a given deadline, if iosram_set_flag returns
200803831d35Sstevel  * EAGAIN (presumably due to a tunnel switch).
200903831d35Sstevel  */
201003831d35Sstevel static int
mboxsc_timed_set_flag(clock_t deadline,uint32_t key,uint8_t data_valid,uint8_t int_pending)201103831d35Sstevel mboxsc_timed_set_flag(clock_t deadline, uint32_t key, uint8_t data_valid,
201203831d35Sstevel 	uint8_t int_pending)
201303831d35Sstevel {
201403831d35Sstevel 	int error;
201503831d35Sstevel 
201603831d35Sstevel 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_timed_set_flag called\n");
201707d06da5SSurya Prakki 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "deadline = 0x%lx\n", deadline);
201803831d35Sstevel 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key);
201903831d35Sstevel 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "data_valid = %d\n", data_valid);
202003831d35Sstevel 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "int_pending = %d\n", int_pending);
202103831d35Sstevel 
202203831d35Sstevel 	do {
202303831d35Sstevel 		error = iosram_set_flag(key, data_valid, int_pending);
202403831d35Sstevel 		if (error == EAGAIN) {
202503831d35Sstevel 			delay(MIN(EAGAIN_POLL, deadline - ddi_get_lbolt()));
202603831d35Sstevel 		}
202703831d35Sstevel 	} while ((error == EAGAIN) && (deadline - ddi_get_lbolt() >= 0));
202803831d35Sstevel 
202903831d35Sstevel 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
203003831d35Sstevel 	    "mboxsc_timed_set_flag ret: 0x%08x\n", error);
203103831d35Sstevel 	return (error);
203203831d35Sstevel }
203303831d35Sstevel 
203403831d35Sstevel /*
203503831d35Sstevel  * mboxsc_timed_send_intr
203603831d35Sstevel  *
203703831d35Sstevel  * This function is just a wrapper around iosram_send_intr that will keep
203803831d35Sstevel  * sleeping and retrying, up to a given deadline, if iosram_send_intr returns
203903831d35Sstevel  * EAGAIN (presumably due to a tunnel switch).
204003831d35Sstevel  */
204103831d35Sstevel static int
mboxsc_timed_send_intr(clock_t deadline)204203831d35Sstevel mboxsc_timed_send_intr(clock_t deadline)
204303831d35Sstevel {
204403831d35Sstevel 	int error;
204503831d35Sstevel 
204603831d35Sstevel 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_timed_send_intr called\n");
204707d06da5SSurya Prakki 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "deadline = 0x%lx\n", deadline);
204803831d35Sstevel 
204903831d35Sstevel 	do {
205003831d35Sstevel 		error = iosram_send_intr();
205103831d35Sstevel 		if (error == DDI_FAILURE) {
205203831d35Sstevel 			delay(MIN(EAGAIN_POLL, deadline - ddi_get_lbolt()));
205303831d35Sstevel 		}
205403831d35Sstevel 	} while ((error == DDI_FAILURE) && (deadline - ddi_get_lbolt() >= 0));
205503831d35Sstevel 
205603831d35Sstevel 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
205703831d35Sstevel 	    "mboxsc_timed_send_intr ret: 0x%08x\n", error);
205803831d35Sstevel 	return (error);
205903831d35Sstevel }
206003831d35Sstevel 
206103831d35Sstevel /*
206203831d35Sstevel  * mboxsc_expire_message
206303831d35Sstevel  *
206403831d35Sstevel  * This function is called by mboxsc_putmsg to handle expiration of messages
206503831d35Sstevel  * that weren't picked up before they timed out.  It will not return until the
206603831d35Sstevel  * message has been picked up (which isn't expected), the message has been
206703831d35Sstevel  * successfully expired, or a serious error has been encountered.  If the
206803831d35Sstevel  * message is finally picked up, it will set the value pointed to by "resultp"
206903831d35Sstevel  * to 0.  Unlike other sections of code, this function will never time out on
207003831d35Sstevel  * EAGAIN from the iosram driver, since it is important that both sides of the
207103831d35Sstevel  * IOSRAM agree on whether or not a message was delivered successfully.
207203831d35Sstevel  */
207303831d35Sstevel static int
mboxsc_expire_message(uint32_t key,int * resultp)207403831d35Sstevel mboxsc_expire_message(uint32_t key, int *resultp)
207503831d35Sstevel {
207603831d35Sstevel 	int	error = 0;
207703831d35Sstevel 	int	lock_held = 0;
207803831d35Sstevel 	int	warned = 0;
207903831d35Sstevel 	uint8_t	data_valid;
208003831d35Sstevel 	clock_t	warning_time = ddi_get_lbolt() + LOOP_WARN_INTERVAL;
208103831d35Sstevel 
208203831d35Sstevel 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_expire_message called\n");
208303831d35Sstevel 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key);
208407d06da5SSurya Prakki 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "resultp = %p\n", (void *)resultp);
208503831d35Sstevel 
208603831d35Sstevel 	do {
208703831d35Sstevel 		error = 0;
208803831d35Sstevel 
208903831d35Sstevel 		/*
209003831d35Sstevel 		 * Lock the flags if they aren't locked already.
209103831d35Sstevel 		 */
209203831d35Sstevel 		if (!lock_held) {
209303831d35Sstevel 			error = mboxsc_lock_flags(TRUE, 0);
209403831d35Sstevel 			if (error == 0) {
209503831d35Sstevel 				lock_held = 1;
209603831d35Sstevel 			}
209703831d35Sstevel 		}
209803831d35Sstevel 
209903831d35Sstevel 		/*
210003831d35Sstevel 		 * If the flags were locked successfully, reread the data-valid
210103831d35Sstevel 		 * flag.
210203831d35Sstevel 		 */
210303831d35Sstevel 		if (error == 0) {
210403831d35Sstevel 			error = iosram_get_flag(key, &data_valid, NULL);
210503831d35Sstevel 		}
210603831d35Sstevel 
210703831d35Sstevel 		/*
210803831d35Sstevel 		 * If the data-valid flag was read successfully, see if it has
210903831d35Sstevel 		 * been cleared or not, as the other side may have finally read
211003831d35Sstevel 		 * the message.
211103831d35Sstevel 		 */
211203831d35Sstevel 		if (error == 0) {
211303831d35Sstevel 			if (data_valid == IOSRAM_DATA_INVALID) {
211403831d35Sstevel 				/*
211503831d35Sstevel 				 * Surprise!  The SC finally picked up the
211603831d35Sstevel 				 * message, so delivery succeeded after all.
211703831d35Sstevel 				 */
211803831d35Sstevel 				if (*resultp == ETIMEDOUT) {
211903831d35Sstevel 					*resultp = 0;
212003831d35Sstevel 				}
212103831d35Sstevel 			} else {
212203831d35Sstevel 				/*
212303831d35Sstevel 				 * The message still hasn't been read, so try to
212403831d35Sstevel 				 * clear the data-valid flag.
212503831d35Sstevel 				 */
212603831d35Sstevel 				error = iosram_set_flag(key,
212703831d35Sstevel 				    IOSRAM_DATA_INVALID, IOSRAM_INT_NONE);
212803831d35Sstevel 			}
212903831d35Sstevel 		}
213003831d35Sstevel 
213103831d35Sstevel 		/*
213203831d35Sstevel 		 * If the flags were locked, unlock them, no matter what else
213303831d35Sstevel 		 * has or has not succeeded.  Don't overwrite the existing value
213403831d35Sstevel 		 * of "error" unless no errors other than EAGAIN have been
213503831d35Sstevel 		 * encountered previously.  If we hit EAGAIN at some point,
213603831d35Sstevel 		 * unlocking the flags here is optional.  In all other cases, it
213703831d35Sstevel 		 * is mandatory.
213803831d35Sstevel 		 */
213903831d35Sstevel 		if (lock_held) {
214003831d35Sstevel 			int unlock_err;
214103831d35Sstevel 
214203831d35Sstevel 			if (error == EAGAIN) {
214303831d35Sstevel 				unlock_err = mboxsc_unlock_flags(FALSE);
214403831d35Sstevel 			} else {
214503831d35Sstevel 				unlock_err = mboxsc_unlock_flags(TRUE);
214603831d35Sstevel 			}
214703831d35Sstevel 
214803831d35Sstevel 			if (unlock_err == 0) {
214903831d35Sstevel 				lock_held = 0;
215003831d35Sstevel 			} else if ((error == 0) || (error == EAGAIN)) {
215103831d35Sstevel 				error = unlock_err;
215203831d35Sstevel 			}
215303831d35Sstevel 		}
215403831d35Sstevel 
215503831d35Sstevel 		/*
215603831d35Sstevel 		 * Did we hit a tunnel switch? (iosram driver returns EAGAIN)
215703831d35Sstevel 		 * If so, sleep for a while before trying the whole process
215803831d35Sstevel 		 * again.
215903831d35Sstevel 		 */
216003831d35Sstevel 		if (error == EAGAIN) {
216103831d35Sstevel 			/*
216203831d35Sstevel 			 * If we've been stuck in this loop for a while,
216303831d35Sstevel 			 * something is probably wrong, and we should display a
216403831d35Sstevel 			 * warning.
216503831d35Sstevel 			 */
216603831d35Sstevel 			if (warning_time - ddi_get_lbolt() <= 0) {
216703831d35Sstevel 				if (!warned) {
216803831d35Sstevel 					warned = 1;
216903831d35Sstevel 					cmn_err(CE_WARN, "Unable to clear flag "
217003831d35Sstevel 					    "(iosram EAGAIN)");
217103831d35Sstevel 				} else {
217203831d35Sstevel 					cmn_err(CE_WARN,
217303831d35Sstevel 					    "Still unable to clear flag");
217403831d35Sstevel 				}
217503831d35Sstevel 				warning_time = ddi_get_lbolt() +
217603831d35Sstevel 				    LOOP_WARN_INTERVAL;
217703831d35Sstevel 			}
217803831d35Sstevel 
217903831d35Sstevel 			delay(EAGAIN_POLL);
218003831d35Sstevel 		}
218103831d35Sstevel 	} while (error == EAGAIN);
218203831d35Sstevel 
218303831d35Sstevel 	/*
218403831d35Sstevel 	 * If the data-valid flag was not successfully cleared due to some sort
218503831d35Sstevel 	 * of problem, report it.  Otherwise, if we looped for a while on EAGAIN
218603831d35Sstevel 	 * and generated a warning about it, indicate that everything is okay
218703831d35Sstevel 	 * now.
218803831d35Sstevel 	 */
218903831d35Sstevel 	if (error != 0) {
219003831d35Sstevel 		cmn_err(CE_WARN, "Message expiration failure! (%d)", error);
219103831d35Sstevel 	} else if (warned) {
219203831d35Sstevel 		cmn_err(CE_WARN, "Flag cleared");
219303831d35Sstevel 	}
219403831d35Sstevel 
219503831d35Sstevel 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
219603831d35Sstevel 	    "mboxsc_expire_message ret: 0x%08x\n", error);
219703831d35Sstevel 	return (error);
219803831d35Sstevel }
219903831d35Sstevel 
220003831d35Sstevel 
220103831d35Sstevel /*
220203831d35Sstevel  * mboxsc_generate_transid
220303831d35Sstevel  *
220403831d35Sstevel  * This function generates unique transaction IDs using an incrementing counter.
220503831d35Sstevel  * The value generated is guaranteed not to be the same as the prev_transid
220603831d35Sstevel  * value passed in by the caller.
220703831d35Sstevel  */
220803831d35Sstevel static uint64_t
mboxsc_generate_transid(uint64_t prev_transid)220903831d35Sstevel mboxsc_generate_transid(uint64_t prev_transid)
221003831d35Sstevel {
221103831d35Sstevel 	uint64_t	new_transid;
221203831d35Sstevel 	static uint64_t	transid_counter = 0;
221303831d35Sstevel 
221403831d35Sstevel 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_generate_transid called");
221507d06da5SSurya Prakki 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "prev_transid = 0x%016lx\n",
221603831d35Sstevel 	    prev_transid);
221703831d35Sstevel 
221803831d35Sstevel 	do {
221903831d35Sstevel 		new_transid = TRANSID_GEN_MASK | transid_counter++;
222003831d35Sstevel 		if (transid_counter & TRANSID_GEN_MASK) {
222103831d35Sstevel 			transid_counter = 0;
222203831d35Sstevel 		}
222303831d35Sstevel 	} while (new_transid == prev_transid);
222403831d35Sstevel 
222503831d35Sstevel 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
222607d06da5SSurya Prakki 	    "mboxsc_generate_transid ret: 0x%016lx", new_transid);
222703831d35Sstevel 	return (new_transid);
222803831d35Sstevel }
222903831d35Sstevel 
223003831d35Sstevel 
223103831d35Sstevel /*
223203831d35Sstevel  * mboxsc_reference_mailbox
223303831d35Sstevel  *
223403831d35Sstevel  * Increment the mailbox's reference count to prevent it from being closed.
223503831d35Sstevel  * This really doesn't deserve to be a function, but since a dereference
223603831d35Sstevel  * function is needed, having a corresponding reference function makes the code
223703831d35Sstevel  * clearer.
223803831d35Sstevel  */
223903831d35Sstevel static void
mboxsc_reference_mailbox(mboxsc_mbox_t * mailboxp)224003831d35Sstevel mboxsc_reference_mailbox(mboxsc_mbox_t *mailboxp)
224103831d35Sstevel {
224203831d35Sstevel 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_reference_mailbox called");
224307d06da5SSurya Prakki 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "mailboxp = 0x%p\n",
224407d06da5SSurya Prakki 	    (void *)mailboxp);
224503831d35Sstevel 
224603831d35Sstevel 	ASSERT(mutex_owned(&mboxsc_lock));
224703831d35Sstevel 
224803831d35Sstevel 	mailboxp->mbox_refcount++;
224903831d35Sstevel 
225003831d35Sstevel 	DPRINTF0(DBG_RETS, DBGACT_DEFAULT, "mboxsc_reference_mailbox ret");
225103831d35Sstevel }
225203831d35Sstevel 
225303831d35Sstevel 
225403831d35Sstevel /*
225503831d35Sstevel  * mboxsc_dereference_mailbox
225603831d35Sstevel  *
225703831d35Sstevel  * Decrement the mailbox's reference count, and if the count has gone to zero,
225803831d35Sstevel  * signal any threads waiting for mailboxes to be completely dereferenced.
225903831d35Sstevel  */
226003831d35Sstevel static void
mboxsc_dereference_mailbox(mboxsc_mbox_t * mailboxp)226103831d35Sstevel mboxsc_dereference_mailbox(mboxsc_mbox_t *mailboxp)
226203831d35Sstevel {
226303831d35Sstevel 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT,
226403831d35Sstevel 	    "mboxsc_dereference_mailbox called");
226507d06da5SSurya Prakki 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "mailboxp = 0x%p\n",
226607d06da5SSurya Prakki 	    (void *)mailboxp);
226703831d35Sstevel 
226803831d35Sstevel 	ASSERT(mutex_owned(&mboxsc_lock));
226903831d35Sstevel 
227003831d35Sstevel 	mailboxp->mbox_refcount--;
227103831d35Sstevel 	if (mailboxp->mbox_refcount == 0) {
227203831d35Sstevel 		cv_broadcast(&mboxsc_dereference_cv);
227303831d35Sstevel 	}
227403831d35Sstevel 
227503831d35Sstevel 	DPRINTF0(DBG_RETS, DBGACT_DEFAULT, "mboxsc_dereference_mailbox ret");
227603831d35Sstevel }
227703831d35Sstevel 
227803831d35Sstevel 
227903831d35Sstevel #ifndef DEBUG
228003831d35Sstevel /* ARGSUSED */
228103831d35Sstevel int
mboxsc_debug(int cmd,void * arg)228203831d35Sstevel mboxsc_debug(int cmd, void *arg)
228303831d35Sstevel {
228403831d35Sstevel 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_debug called");
228503831d35Sstevel 	DPRINTF0(DBG_RETS, DBGACT_DEFAULT, "mboxsc_debug ret");
228603831d35Sstevel 	return (ENOTSUP);
228703831d35Sstevel }
228803831d35Sstevel #else	/* DEBUG */
228903831d35Sstevel 
229003831d35Sstevel static void	print_hash_table(void);
229103831d35Sstevel static int	print_mailbox_by_key(uint32_t key);
229203831d35Sstevel static void	print_mailbox(mboxsc_mbox_t *mailboxp);
229303831d35Sstevel 
229403831d35Sstevel int
mboxsc_debug(int cmd,void * arg)229503831d35Sstevel mboxsc_debug(int cmd, void *arg)
229603831d35Sstevel {
229703831d35Sstevel 	int		error = 0;
229803831d35Sstevel 
229903831d35Sstevel 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_debug called\n");
230003831d35Sstevel 
230103831d35Sstevel 	switch (cmd) {
230203831d35Sstevel 		case MBOXSC_PRNMBOX:
230303831d35Sstevel 			error = print_mailbox_by_key((uint32_t)(uintptr_t)arg);
230403831d35Sstevel 			break;
230503831d35Sstevel 
230603831d35Sstevel 		case MBOXSC_PRNHASHTBL:
230703831d35Sstevel 			print_hash_table();
230803831d35Sstevel 			break;
230903831d35Sstevel 
231003831d35Sstevel 		case MBOXSC_SETDBGMASK:
231103831d35Sstevel 			mboxsc_debug_mask = (uint32_t)(uintptr_t)arg;
231203831d35Sstevel 			break;
231303831d35Sstevel 
231403831d35Sstevel 		default:
231503831d35Sstevel 			DPRINTF1(DBG_DEV, DBGACT_DEFAULT,
231603831d35Sstevel 			    "Error: unknown mboxsc debug cmd (%d)\n", cmd);
231703831d35Sstevel 			error = ENOTTY;
231803831d35Sstevel 			break;
231903831d35Sstevel 	}
232003831d35Sstevel 
232103831d35Sstevel 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_debug ret: 0x%08x\n", error);
232203831d35Sstevel 
232303831d35Sstevel 	return (error);
232403831d35Sstevel }
232503831d35Sstevel 
232603831d35Sstevel /*PRINTFLIKE5*/
232703831d35Sstevel static void
mboxsc_dprintf(const char * file,int line,uint32_t class,uint32_t action,const char * fmt,...)232803831d35Sstevel mboxsc_dprintf(
232903831d35Sstevel 	const char	*file,
233003831d35Sstevel 	int		line,
233103831d35Sstevel 	uint32_t	class,
233203831d35Sstevel 	uint32_t	action,
233303831d35Sstevel 	const char	*fmt,
233403831d35Sstevel 	...)
233503831d35Sstevel {
233603831d35Sstevel 	int		i;
233703831d35Sstevel 	char		indent_buf[64];
233803831d35Sstevel 	char		msg_buf[256];
233903831d35Sstevel 	va_list		adx;
234003831d35Sstevel 	static uint32_t	indent = 0;
234103831d35Sstevel 
234203831d35Sstevel 	if (action & DBGACT_SHOWPOS) {
234303831d35Sstevel 		cmn_err(CE_CONT, "%s at line %d:\n", file, line);
234403831d35Sstevel 	}
234503831d35Sstevel 
234603831d35Sstevel 	if (class & DBG_RETS) {
234703831d35Sstevel 		indent--;
234803831d35Sstevel 	}
234903831d35Sstevel 
235003831d35Sstevel 	if (class & mboxsc_debug_mask) {
235103831d35Sstevel 		indent_buf[0] = '\0';
235203831d35Sstevel 		for (i = 0; i < indent; i++) {
235307d06da5SSurya Prakki 			(void) strcat(indent_buf, "  ");
235403831d35Sstevel 		}
235503831d35Sstevel 
235603831d35Sstevel 		va_start(adx, fmt);
235707d06da5SSurya Prakki 		(void) vsprintf(msg_buf, fmt, adx);
235803831d35Sstevel 		va_end(adx);
235903831d35Sstevel 
236003831d35Sstevel 		cmn_err(CE_CONT, "%s%s", indent_buf, msg_buf);
236103831d35Sstevel 	}
236203831d35Sstevel 
236303831d35Sstevel 	if (class & DBG_CALLS) {
236403831d35Sstevel 		indent++;
236503831d35Sstevel 	}
236603831d35Sstevel 
236703831d35Sstevel 	if (action & DBGACT_BREAK) {
236803831d35Sstevel 		debug_enter("");
236903831d35Sstevel 	}
237003831d35Sstevel }
237103831d35Sstevel 
237203831d35Sstevel static void
print_hash_table(void)237303831d35Sstevel print_hash_table(void)
237403831d35Sstevel {
237503831d35Sstevel 	int		i;
237603831d35Sstevel 	mboxsc_mbox_t	*mailboxp;
237703831d35Sstevel 
237803831d35Sstevel 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "print_hash_table called\n");
237903831d35Sstevel 
238003831d35Sstevel 	mutex_enter(&mboxsc_lock);
238103831d35Sstevel 
238203831d35Sstevel 	for (i = 0; i < HASHTBL_SIZE; i++) {
238303831d35Sstevel 		DPRINTF1(DBG_DEV, DBGACT_DEFAULT, "hash[%02d]:\n", i);
238403831d35Sstevel 
238503831d35Sstevel 		for (mailboxp = mboxsc_hash_table[i]; mailboxp != NULL;
238603831d35Sstevel 		    mailboxp = mailboxp->mbox_hash_next) {
238703831d35Sstevel 			DPRINTF2(DBG_DEV, DBGACT_DEFAULT,
238803831d35Sstevel 			    "    key: 0x%08x, dir: %d\n", mailboxp->mbox_key,
238903831d35Sstevel 			    mailboxp->mbox_direction);
239003831d35Sstevel 		}
239103831d35Sstevel 	}
239203831d35Sstevel 
239303831d35Sstevel 	mutex_exit(&mboxsc_lock);
239403831d35Sstevel 
239503831d35Sstevel 	DPRINTF0(DBG_RETS, DBGACT_DEFAULT, "print_hash_table ret\n");
239603831d35Sstevel }
239703831d35Sstevel 
239803831d35Sstevel static int
print_mailbox_by_key(uint32_t key)239903831d35Sstevel print_mailbox_by_key(uint32_t key)
240003831d35Sstevel {
240103831d35Sstevel 	int		error = 0;
240203831d35Sstevel 	mboxsc_mbox_t	*mailboxp;
240303831d35Sstevel 
240403831d35Sstevel 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "print_mailbox_by_key called\n");
240503831d35Sstevel 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%08x\n", key);
240603831d35Sstevel 
240703831d35Sstevel 	mutex_enter(&mboxsc_lock);
240803831d35Sstevel 
240903831d35Sstevel 	mailboxp = mboxsc_hashfind_mailbox_by_key(key);
241003831d35Sstevel 	if (mailboxp != NULL) {
241103831d35Sstevel 		print_mailbox(mailboxp);
241203831d35Sstevel 		error = 0;
241303831d35Sstevel 	} else {
241403831d35Sstevel 		DPRINTF1(DBG_DEV, DBGACT_DEFAULT,
241503831d35Sstevel 		    "print_mailbox_by_key: no such mbox 0x%08x\n", key);
241603831d35Sstevel 		error = EBADF;
241703831d35Sstevel 	}
241803831d35Sstevel 
241903831d35Sstevel 	mutex_exit(&mboxsc_lock);
242003831d35Sstevel 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
242103831d35Sstevel 	    "print_mailbox_by_key ret: 0x%08x\n", error);
242203831d35Sstevel 
242303831d35Sstevel 	return (error);
242403831d35Sstevel }
242503831d35Sstevel 
242603831d35Sstevel /* ARGSUSED */
242703831d35Sstevel static void
print_mailbox(mboxsc_mbox_t * mailboxp)242803831d35Sstevel print_mailbox(mboxsc_mbox_t *mailboxp)
242903831d35Sstevel {
243003831d35Sstevel 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "print_mailbox called\n");
243107d06da5SSurya Prakki 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "mailboxp = %p\n",
243207d06da5SSurya Prakki 	    (void *)mailboxp);
243303831d35Sstevel 	if (mailboxp->mbox_direction == MBOXSC_MBOX_IN) {
243403831d35Sstevel 		DPRINTF3(DBG_DEV, DBGACT_DEFAULT,
243503831d35Sstevel 		    "key = 0x%08x, dir = %d, callback = %p\n",
243603831d35Sstevel 		    mailboxp->mbox_key, mailboxp->mbox_direction,
243707d06da5SSurya Prakki 		    (void *)mailboxp->mbox_callback);
243803831d35Sstevel 	} else {
243903831d35Sstevel 		DPRINTF2(DBG_DEV, DBGACT_DEFAULT, "key = 0x%08x, dir = %d\n",
244007d06da5SSurya Prakki 		    (int)mailboxp->mbox_key, mailboxp->mbox_direction);
244103831d35Sstevel 	}
244203831d35Sstevel 	DPRINTF3(DBG_DEV, DBGACT_DEFAULT,
244303831d35Sstevel 	    "length = %d, refcount = %d, state = %d\n",
244403831d35Sstevel 	    mailboxp->mbox_length, mailboxp->mbox_refcount,
244503831d35Sstevel 	    mailboxp->mbox_state);
244607d06da5SSurya Prakki 	/* LINTED E_BAD_FORMAT_ARG_TYPE2 */
244707d06da5SSurya Prakki 	DPRINTF2(DBG_DEV, DBGACT_DEFAULT, "waitcv = %p, hashnext = %p\n",
244807d06da5SSurya Prakki 	    (void *)&mailboxp->mbox_wait, (void *)mailboxp->mbox_hash_next);
244903831d35Sstevel 	if (mailboxp->mbox_direction == MBOXSC_MBOX_IN) {
245003831d35Sstevel 		DPRINTF3(DBG_DEV, DBGACT_DEFAULT,
245103831d35Sstevel 		    "hdr.type = 0x%x, hdr.cmd = 0x%x, hdr.len = 0x%x\n",
245203831d35Sstevel 		    mailboxp->mbox_header.msg_type,
245303831d35Sstevel 		    mailboxp->mbox_header.msg_cmd,
245403831d35Sstevel 		    mailboxp->mbox_header.msg_length);
245507d06da5SSurya Prakki 		DPRINTF1(DBG_DEV, DBGACT_DEFAULT, "hdr.tid = 0x%016lx\n",
245603831d35Sstevel 		    mailboxp->mbox_header.msg_transid);
245703831d35Sstevel 	}
245803831d35Sstevel 	DPRINTF0(DBG_RETS, DBGACT_DEFAULT, "print_mailbox ret\n");
245903831d35Sstevel }
246003831d35Sstevel #endif	/* DEBUG */
2461