xref: /illumos-gate/usr/src/uts/sun4u/opl/io/oplmsu/oplmsu.c (revision 3fe80ca4)
125cf1a30Sjl /*
225cf1a30Sjl  * CDDL HEADER START
325cf1a30Sjl  *
425cf1a30Sjl  * The contents of this file are subject to the terms of the
525cf1a30Sjl  * Common Development and Distribution License (the "License").
625cf1a30Sjl  * You may not use this file except in compliance with the License.
725cf1a30Sjl  *
825cf1a30Sjl  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
925cf1a30Sjl  * or http://www.opensolaris.org/os/licensing.
1025cf1a30Sjl  * See the License for the specific language governing permissions
1125cf1a30Sjl  * and limitations under the License.
1225cf1a30Sjl  *
1325cf1a30Sjl  * When distributing Covered Code, include this CDDL HEADER in each
1425cf1a30Sjl  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1525cf1a30Sjl  * If applicable, add the following below this CDDL HEADER, with the
1625cf1a30Sjl  * fields enclosed by brackets "[]" replaced with your own identifying
1725cf1a30Sjl  * information: Portions Copyright [yyyy] [name of copyright owner]
1825cf1a30Sjl  *
1925cf1a30Sjl  * CDDL HEADER END
2025cf1a30Sjl  */
2119397407SSherry Moore 
2225cf1a30Sjl /*
2325cf1a30Sjl  * All Rights Reserved, Copyright (c) FUJITSU LIMITED 2006
2425cf1a30Sjl  */
2525cf1a30Sjl 
2619397407SSherry Moore /*
2707d06da5SSurya Prakki  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
2819397407SSherry Moore  * Use is subject to license terms.
2919397407SSherry Moore  */
3019397407SSherry Moore 
31*3fe80ca4SDan Cross /*
32*3fe80ca4SDan Cross  * Copyright 2023 Oxide Computer Company
33*3fe80ca4SDan Cross  */
3425cf1a30Sjl 
3525cf1a30Sjl #include <sys/errno.h>
3625cf1a30Sjl #include <sys/modctl.h>
3725cf1a30Sjl #include <sys/stat.h>
3825cf1a30Sjl #include <sys/kmem.h>
3925cf1a30Sjl #include <sys/ksynch.h>
4025cf1a30Sjl #include <sys/stream.h>
4125cf1a30Sjl #include <sys/stropts.h>
4225cf1a30Sjl #include <sys/termio.h>
4325cf1a30Sjl #include <sys/ddi.h>
4425cf1a30Sjl #include <sys/file.h>
4525cf1a30Sjl #include <sys/disp.h>
4625cf1a30Sjl #include <sys/sunddi.h>
4725cf1a30Sjl #include <sys/sunldi.h>
4825cf1a30Sjl #include <sys/sunndi.h>
4925cf1a30Sjl #include <sys/kbio.h>
5025cf1a30Sjl #include <sys/prom_plat.h>
5125cf1a30Sjl #include <sys/oplmsu/oplmsu.h>
5225cf1a30Sjl #include <sys/oplmsu/oplmsu_proto.h>
5325cf1a30Sjl 
5425cf1a30Sjl extern int ddi_create_internal_pathname(dev_info_t *, char *, int, minor_t);
5525cf1a30Sjl 
5625cf1a30Sjl #define	MOD_ID		0xe145
5725cf1a30Sjl #define	MOD_NAME	"oplmsu"
5825cf1a30Sjl 
5925cf1a30Sjl #define	META_NAME	"oplmsu"
6025cf1a30Sjl #define	USER_NAME	"a"
6125cf1a30Sjl 
6225cf1a30Sjl struct module_info oplmsu_mod_info = {
6325cf1a30Sjl 	MOD_ID,
6425cf1a30Sjl 	MOD_NAME,
6525cf1a30Sjl 	0,
6625cf1a30Sjl 	16384,
6725cf1a30Sjl 	14336,
6825cf1a30Sjl 	2048
6925cf1a30Sjl };
7025cf1a30Sjl 
7125cf1a30Sjl struct qinit oplmsu_urinit = {
7225cf1a30Sjl 	NULL,
7325cf1a30Sjl 	oplmsu_ursrv,
7425cf1a30Sjl 	oplmsu_open,
7525cf1a30Sjl 	oplmsu_close,
7625cf1a30Sjl 	NULL,
7725cf1a30Sjl 	&oplmsu_mod_info,
7825cf1a30Sjl 	NULL
7925cf1a30Sjl };
8025cf1a30Sjl 
8125cf1a30Sjl struct qinit oplmsu_uwinit = {
8225cf1a30Sjl 	oplmsu_uwput,
8325cf1a30Sjl 	oplmsu_uwsrv,
8425cf1a30Sjl 	oplmsu_open,
8525cf1a30Sjl 	oplmsu_close,
8625cf1a30Sjl 	NULL,
8725cf1a30Sjl 	&oplmsu_mod_info,
8825cf1a30Sjl 	NULL
8925cf1a30Sjl };
9025cf1a30Sjl 
9125cf1a30Sjl struct qinit oplmsu_lrinit = {
9225cf1a30Sjl 	oplmsu_lrput,
9325cf1a30Sjl 	oplmsu_lrsrv,
9425cf1a30Sjl 	oplmsu_open,
9525cf1a30Sjl 	oplmsu_close,
9625cf1a30Sjl 	NULL,
9725cf1a30Sjl 	&oplmsu_mod_info,
9825cf1a30Sjl 	NULL
9925cf1a30Sjl };
10025cf1a30Sjl 
10125cf1a30Sjl struct qinit oplmsu_lwinit = {
10225cf1a30Sjl 	NULL,
10325cf1a30Sjl 	oplmsu_lwsrv,
10425cf1a30Sjl 	oplmsu_open,
10525cf1a30Sjl 	oplmsu_close,
10625cf1a30Sjl 	NULL,
10725cf1a30Sjl 	&oplmsu_mod_info,
10825cf1a30Sjl 	NULL
10925cf1a30Sjl };
11025cf1a30Sjl 
11125cf1a30Sjl struct streamtab oplmsu_info = {
11225cf1a30Sjl 	&oplmsu_urinit,
11325cf1a30Sjl 	&oplmsu_uwinit,
11425cf1a30Sjl 	&oplmsu_lrinit,
11525cf1a30Sjl 	&oplmsu_lwinit
11625cf1a30Sjl };
11725cf1a30Sjl 
11825cf1a30Sjl static struct cb_ops cb_oplmsu_ops = {
11925cf1a30Sjl 	nulldev,			/* cb_open */
12025cf1a30Sjl 	nulldev,			/* cb_close */
12125cf1a30Sjl 	nodev,				/* cb_strategy */
12225cf1a30Sjl 	nodev,				/* cb_print */
12325cf1a30Sjl 	nodev,				/* cb_dump */
12425cf1a30Sjl 	nodev,				/* cb_read */
12525cf1a30Sjl 	nodev,				/* cb_write */
12625cf1a30Sjl 	nodev,				/* cb_ioctl */
12725cf1a30Sjl 	nodev,				/* cb_devmap */
12825cf1a30Sjl 	nodev,				/* cb_mmap */
12925cf1a30Sjl 	nodev,				/* cb_segmap */
13025cf1a30Sjl 	nochpoll,			/* cb_chpoll */
13125cf1a30Sjl 	ddi_prop_op,			/* cb_prop_op */
13225cf1a30Sjl 	(&oplmsu_info),			/* cb_stream */
13325cf1a30Sjl 	(int)(D_NEW|D_MP|D_HOTPLUG)	/* cb_flag */
13425cf1a30Sjl };
13525cf1a30Sjl 
13625cf1a30Sjl static struct dev_ops oplmsu_ops = {
13725cf1a30Sjl 	DEVO_REV,			/* devo_rev */
13825cf1a30Sjl 	0,				/* devo_refcnt */
13925cf1a30Sjl 	(oplmsu_getinfo),		/* devo_getinfo */
14025cf1a30Sjl 	(nulldev),			/* devo_identify */
14125cf1a30Sjl 	(nulldev),			/* devo_probe */
14225cf1a30Sjl 	(oplmsu_attach),		/* devo_attach */
14325cf1a30Sjl 	(oplmsu_detach),		/* devo_detach */
14425cf1a30Sjl 	(nodev),			/* devo_reset */
14525cf1a30Sjl 	&(cb_oplmsu_ops),		/* devo_cb_ops */
14625cf1a30Sjl 	(struct bus_ops *)NULL,		/* devo_bus_ops */
14719397407SSherry Moore 	NULL,				/* devo_power */
14819397407SSherry Moore 	ddi_quiesce_not_needed,			/* dev_quiesce */
14925cf1a30Sjl };
15025cf1a30Sjl 
15125cf1a30Sjl struct modldrv modldrv = {
15225cf1a30Sjl 	&mod_driverops,
15319397407SSherry Moore 	"OPL serial mux driver",
15425cf1a30Sjl 	&oplmsu_ops
15525cf1a30Sjl };
15625cf1a30Sjl 
15725cf1a30Sjl struct modlinkage modlinkage = {
15825cf1a30Sjl 	MODREV_1,
15925cf1a30Sjl 	(void *)&modldrv,
16025cf1a30Sjl 	NULL
16125cf1a30Sjl };
16225cf1a30Sjl 
16325cf1a30Sjl uinst_t		oplmsu_uinst_local;	/* upper_instance_table structure */
16425cf1a30Sjl uinst_t		*oplmsu_uinst = &oplmsu_uinst_local;
16525cf1a30Sjl int		oplmsu_queue_flag;	/* Enable/disable queueing flag */
16625cf1a30Sjl int		oplmsu_check_su;	/* Check super-user flag */
16725cf1a30Sjl 
16825cf1a30Sjl #ifdef DEBUG
16925cf1a30Sjl int		oplmsu_debug_mode = 0;	/* Enable/disable debug mode */
17025cf1a30Sjl int		oplmsu_trace_on;	/* Enable/disable trace */
17125cf1a30Sjl uint_t		oplmsu_ltrc_size;	/* Trace buffer size */
17225cf1a30Sjl msu_trc_t	*oplmsu_ltrc_top;	/* Top of trace data area */
17325cf1a30Sjl msu_trc_t	*oplmsu_ltrc_tail;	/* Tail of trace data area */
17425cf1a30Sjl msu_trc_t	*oplmsu_ltrc_cur;	/* Current pointer of trace data area */
17525cf1a30Sjl ulong_t		oplmsu_ltrc_ccnt;	/* Current counter */
17625cf1a30Sjl kmutex_t	oplmsu_ltrc_lock;	/* Lock table for trace mode */
17725cf1a30Sjl #endif
17825cf1a30Sjl 
17925cf1a30Sjl /* oplmsu_conf_st */
18025cf1a30Sjl #define	MSU_CONFIGURED		2
18125cf1a30Sjl #define	MSU_CONFIGURING		1
18225cf1a30Sjl #define	MSU_UNCONFIGURED	0
18325cf1a30Sjl 
18425cf1a30Sjl static kmutex_t		oplmsu_bthrd_excl;
18525cf1a30Sjl static kthread_id_t	oplmsu_bthrd_id = NULL;
18625cf1a30Sjl static int		oplmsu_conf_st = MSU_UNCONFIGURED;
18725cf1a30Sjl static kcondvar_t	oplmsu_conf_cv;
18825cf1a30Sjl 
18925cf1a30Sjl 
19025cf1a30Sjl /*
19125cf1a30Sjl  * Locking hierarcy of oplmsu driver. This driver have 5 locks in uinst_t.
19225cf1a30Sjl  *
19325cf1a30Sjl  * Each mutex guards as follows.
19425cf1a30Sjl  *
19525cf1a30Sjl  *  uinst_t->lock: This mutex is read/write mutex.
19625cf1a30Sjl  *     read lock : acquired if the member of uinst_t is refered only.
19725cf1a30Sjl  *     write lock: acquired if the member of uinst_t is changed.
19825cf1a30Sjl  *
19925cf1a30Sjl  *  uinst_t->u_lock: This mutex is normal mutex.
20025cf1a30Sjl  *   This mutex is acquired at reading/changing the member of all upath_t.
20125cf1a30Sjl  *
20225cf1a30Sjl  *  uinst_t->l_lock: This mutex is normal mutex.
20325cf1a30Sjl  *   This mutex is acquired at reading/changing the member of all lpath_t.
20425cf1a30Sjl  *
20525cf1a30Sjl  *  uinst_t->c_lock: This mutex is normal mutex.
20625cf1a30Sjl  *   This mutex is acquired at reading/changing the member of the ctrl_t.
20725cf1a30Sjl  *
20825cf1a30Sjl  *  oplmsu_bthrd_excl: This mutex is normal mutex.
20925cf1a30Sjl  *   This mutex is used only to start/stop the configuring thread of the
21025cf1a30Sjl  *   multiplexed STREAMS.
21125cf1a30Sjl  *   This mutex is exclusively acquired with the above-mentioned 4 mutexes.
21225cf1a30Sjl  *
21325cf1a30Sjl  * To guard of the deadlock by cross locking, the base locking hierarcy
21425cf1a30Sjl  * is as follows:
21525cf1a30Sjl  *
21625cf1a30Sjl  *     uisnt->lock ==> uinst->u_lock ==> uinst->l_lock ==> uinst->c_lock
21725cf1a30Sjl  *
21825cf1a30Sjl  */
21925cf1a30Sjl 
22025cf1a30Sjl 
22125cf1a30Sjl int
_init(void)22225cf1a30Sjl _init(void)
22325cf1a30Sjl {
22425cf1a30Sjl 	int	rval;
22525cf1a30Sjl 
22625cf1a30Sjl 	/* Initialize R/W lock for uinst_t */
22725cf1a30Sjl 	rw_init(&oplmsu_uinst->lock, "uinst rwlock", RW_DRIVER, NULL);
22825cf1a30Sjl 
22925cf1a30Sjl 	/* Initialize mutex for upath_t */
23025cf1a30Sjl 	mutex_init(&oplmsu_uinst->u_lock, "upath lock", MUTEX_DRIVER, NULL);
23125cf1a30Sjl 
23225cf1a30Sjl 	/* Initialize mutex for lpath_t */
23325cf1a30Sjl 	mutex_init(&oplmsu_uinst->l_lock, "lpath lock", MUTEX_DRIVER, NULL);
23425cf1a30Sjl 
23525cf1a30Sjl 	/* Initialize mutex for ctrl_t */
23625cf1a30Sjl 	mutex_init(&oplmsu_uinst->c_lock, "ctrl lock", MUTEX_DRIVER, NULL);
23725cf1a30Sjl 
23825cf1a30Sjl 	/* Initialize mutex for protecting background thread */
23925cf1a30Sjl 	mutex_init(&oplmsu_bthrd_excl, NULL, MUTEX_DRIVER, NULL);
24025cf1a30Sjl 
24125cf1a30Sjl 	/* Initialize condition variable */
24225cf1a30Sjl 	cv_init(&oplmsu_conf_cv, NULL, CV_DRIVER, NULL);
24325cf1a30Sjl 
24425cf1a30Sjl 	rval = mod_install(&modlinkage);
24525cf1a30Sjl 	if (rval != DDI_SUCCESS) {
24625cf1a30Sjl 		cv_destroy(&oplmsu_conf_cv);
24725cf1a30Sjl 		mutex_destroy(&oplmsu_bthrd_excl);
24825cf1a30Sjl 		mutex_destroy(&oplmsu_uinst->c_lock);
24925cf1a30Sjl 		mutex_destroy(&oplmsu_uinst->l_lock);
25025cf1a30Sjl 		mutex_destroy(&oplmsu_uinst->u_lock);
25125cf1a30Sjl 		rw_destroy(&oplmsu_uinst->lock);
25225cf1a30Sjl 	}
25325cf1a30Sjl 	return (rval);
25425cf1a30Sjl }
25525cf1a30Sjl 
25625cf1a30Sjl int
_fini(void)25725cf1a30Sjl _fini(void)
25825cf1a30Sjl {
25925cf1a30Sjl 	int	rval;
26025cf1a30Sjl 
26125cf1a30Sjl 	rval = mod_remove(&modlinkage);
26225cf1a30Sjl 	if (rval == DDI_SUCCESS) {
26325cf1a30Sjl 		cv_destroy(&oplmsu_conf_cv);
26425cf1a30Sjl 		mutex_destroy(&oplmsu_bthrd_excl);
26525cf1a30Sjl 		mutex_destroy(&oplmsu_uinst->c_lock);
26625cf1a30Sjl 		mutex_destroy(&oplmsu_uinst->l_lock);
26725cf1a30Sjl 		mutex_destroy(&oplmsu_uinst->u_lock);
26825cf1a30Sjl 		rw_destroy(&oplmsu_uinst->lock);
26925cf1a30Sjl 	}
27025cf1a30Sjl 	return (rval);
27125cf1a30Sjl }
27225cf1a30Sjl 
27325cf1a30Sjl int
_info(struct modinfo * modinfop)27425cf1a30Sjl _info(struct modinfo *modinfop)
27525cf1a30Sjl {
27625cf1a30Sjl 	return (mod_info(&modlinkage, modinfop));
27725cf1a30Sjl }
27825cf1a30Sjl 
27925cf1a30Sjl /* ARGSUSED */
28025cf1a30Sjl int
oplmsu_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** resultp)28125cf1a30Sjl oplmsu_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
28225cf1a30Sjl {
28325cf1a30Sjl 	dev_t	dev = (dev_t)arg;
28425cf1a30Sjl 	minor_t	inst;
28525cf1a30Sjl 	int	rval = DDI_SUCCESS;
28625cf1a30Sjl 
28725cf1a30Sjl 	switch (cmd) {
28825cf1a30Sjl 	case DDI_INFO_DEVT2DEVINFO  :
28925cf1a30Sjl 		if (oplmsu_uinst->msu_dip == NULL) {
29025cf1a30Sjl 			rval = DDI_FAILURE;
29125cf1a30Sjl 		} else {
29225cf1a30Sjl 			*resultp = oplmsu_uinst->msu_dip;
29325cf1a30Sjl 		}
29425cf1a30Sjl 		break;
29525cf1a30Sjl 
29625cf1a30Sjl 	case DDI_INFO_DEVT2INSTANCE :
29725cf1a30Sjl 		inst = getminor(dev) & ~(META_NODE_MASK|USER_NODE_MASK);
29825cf1a30Sjl 		*resultp = (void *)(uintptr_t)inst;
29925cf1a30Sjl 		break;
30025cf1a30Sjl 
30125cf1a30Sjl 	default :
30225cf1a30Sjl 		rval = DDI_FAILURE;
30325cf1a30Sjl 		break;
30425cf1a30Sjl 	}
30525cf1a30Sjl 	return (rval);
30625cf1a30Sjl }
30725cf1a30Sjl 
30825cf1a30Sjl int
oplmsu_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)30925cf1a30Sjl oplmsu_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
31025cf1a30Sjl {
31125cf1a30Sjl 	minor_t	meta_minor, user_minor;
31225cf1a30Sjl 	int	rval = 0;
31325cf1a30Sjl 	int	instance;
31425cf1a30Sjl #define	CNTRL(c) ((c) & 037)
31525cf1a30Sjl 	char	abt_ch_seq[3] = { '\r', '~', CNTRL('b') };
31625cf1a30Sjl 
31725cf1a30Sjl 	if (cmd == DDI_RESUME) {
31825cf1a30Sjl 		return (DDI_SUCCESS);
31925cf1a30Sjl 	}
32025cf1a30Sjl 
32125cf1a30Sjl 	if (cmd != DDI_ATTACH) {
32225cf1a30Sjl 		return (DDI_FAILURE);
32325cf1a30Sjl 	}
32425cf1a30Sjl 
32525cf1a30Sjl 	instance = ddi_get_instance(dip);
32625cf1a30Sjl 	if (instance != 0) {
32725cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: attach: "
32825cf1a30Sjl 		    "Invaild instance => %d", instance);
32925cf1a30Sjl 		return (DDI_FAILURE);
33025cf1a30Sjl 	}
33125cf1a30Sjl 
33225cf1a30Sjl 	/* Create minor number for meta control node */
33325cf1a30Sjl 	meta_minor = instance | META_NODE_MASK;
33425cf1a30Sjl 	/* Create minor number for user access node */
33525cf1a30Sjl 	user_minor = instance | USER_NODE_MASK;
33625cf1a30Sjl 
33725cf1a30Sjl 	/* Create minor node for user access */
33825cf1a30Sjl 	rval = ddi_create_minor_node(dip, USER_NAME, S_IFCHR, user_minor,
33925cf1a30Sjl 	    DDI_NT_SERIAL, 0);
34025cf1a30Sjl 	if (rval != DDI_SUCCESS) {
34125cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: attach: "
34225cf1a30Sjl 		    "ddi_create_minor_node failed. errno = %d", rval);
34325cf1a30Sjl 		ddi_remove_minor_node(dip, NULL);
34425cf1a30Sjl 		return (rval);
34525cf1a30Sjl 	}
34625cf1a30Sjl 
34725cf1a30Sjl 	/* Create minor node for meta control */
34825cf1a30Sjl 	rval = ddi_create_internal_pathname(dip, META_NAME, S_IFCHR,
34925cf1a30Sjl 	    meta_minor);
35025cf1a30Sjl 	if (rval != DDI_SUCCESS) {
35125cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: attach: "
35225cf1a30Sjl 		    "ddi_create_internal_pathname failed. errno = %d", rval);
35325cf1a30Sjl 		ddi_remove_minor_node(dip, NULL);
35425cf1a30Sjl 		return (rval);
35525cf1a30Sjl 	}
35625cf1a30Sjl 
35725cf1a30Sjl 	rw_enter(&oplmsu_uinst->lock, RW_WRITER);
35825cf1a30Sjl 
35925cf1a30Sjl 	/* Get each properties */
36025cf1a30Sjl 	oplmsu_check_su = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
36125cf1a30Sjl 	    (DDI_PROP_DONTPASS|DDI_PROP_NOTPROM), "check-superuser", 1);
36225cf1a30Sjl 
36325cf1a30Sjl 	/*
36425cf1a30Sjl 	 * Initialize members of uinst_t
36525cf1a30Sjl 	 */
36625cf1a30Sjl 
36725cf1a30Sjl 	oplmsu_uinst->inst_status = INST_STAT_UNCONFIGURED;
36825cf1a30Sjl 	oplmsu_uinst->path_num = UNDEFINED;
36925cf1a30Sjl 	oplmsu_uinst->msu_dip = dip;
37025cf1a30Sjl 	(void) strcpy(oplmsu_uinst->abts, abt_ch_seq);
37125cf1a30Sjl 
37225cf1a30Sjl #ifdef DEBUG
37325cf1a30Sjl 	oplmsu_trace_on = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
37425cf1a30Sjl 	    (DDI_PROP_DONTPASS|DDI_PROP_NOTPROM), "trace-mode", 1);
37525cf1a30Sjl 	oplmsu_ltrc_size = (uint_t)ddi_prop_get_int(DDI_DEV_T_ANY, dip,
37625cf1a30Sjl 	    (DDI_PROP_DONTPASS|DDI_PROP_NOTPROM), "trace-bufsize", 128);
37725cf1a30Sjl 
37825cf1a30Sjl 	if (oplmsu_trace_on == MSU_TRACE_ON) {
37925cf1a30Sjl 		/* Initialize mutex for msu_trc_t */
38025cf1a30Sjl 		mutex_init(&oplmsu_ltrc_lock, "trc lock", MUTEX_DRIVER, NULL);
38125cf1a30Sjl 
38225cf1a30Sjl 		mutex_enter(&oplmsu_ltrc_lock);
38325cf1a30Sjl 		oplmsu_ltrc_top = (msu_trc_t *)kmem_zalloc(
38425cf1a30Sjl 		    (sizeof (msu_trc_t) * oplmsu_ltrc_size), KM_SLEEP);
38525cf1a30Sjl 		oplmsu_ltrc_cur = (msu_trc_t *)(oplmsu_ltrc_top - 1);
38625cf1a30Sjl 		oplmsu_ltrc_tail =
38725cf1a30Sjl 		    (msu_trc_t *)(oplmsu_ltrc_top + (oplmsu_ltrc_size - 1));
38825cf1a30Sjl 		mutex_exit(&oplmsu_ltrc_lock);
38925cf1a30Sjl 	}
39025cf1a30Sjl #endif
39125cf1a30Sjl 	rw_exit(&oplmsu_uinst->lock);
39225cf1a30Sjl 	ddi_report_dev(dip);
39325cf1a30Sjl 	return (rval);
39425cf1a30Sjl }
39525cf1a30Sjl 
39625cf1a30Sjl int
oplmsu_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)39725cf1a30Sjl oplmsu_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
39825cf1a30Sjl {
39925cf1a30Sjl 	lpath_t	*lpath, *next_lpath;
40025cf1a30Sjl 
40125cf1a30Sjl 	if (cmd == DDI_SUSPEND) {
40225cf1a30Sjl 		return (DDI_SUCCESS);
40325cf1a30Sjl 	}
40425cf1a30Sjl 
40525cf1a30Sjl 	if (cmd != DDI_DETACH) {
40625cf1a30Sjl 		return (DDI_FAILURE);
40725cf1a30Sjl 	}
40825cf1a30Sjl 
40925cf1a30Sjl 	rw_enter(&oplmsu_uinst->lock, RW_WRITER);
41025cf1a30Sjl 
41125cf1a30Sjl 	/* Delete all upath_t */
41225cf1a30Sjl 	oplmsu_delete_upath_info();
41325cf1a30Sjl 
41425cf1a30Sjl 	/* Delete all lpath_t */
41525cf1a30Sjl 	mutex_enter(&oplmsu_uinst->l_lock);
41625cf1a30Sjl 	lpath = oplmsu_uinst->first_lpath;
41725cf1a30Sjl 	oplmsu_uinst->first_lpath = NULL;
41825cf1a30Sjl 	oplmsu_uinst->last_lpath = NULL;
41925cf1a30Sjl 	mutex_exit(&oplmsu_uinst->l_lock);
42025cf1a30Sjl 
42125cf1a30Sjl #ifdef DEBUG
42225cf1a30Sjl 	if (oplmsu_trace_on == MSU_TRACE_ON) {
42325cf1a30Sjl 		mutex_enter(&oplmsu_ltrc_lock);
42425cf1a30Sjl 		if (oplmsu_ltrc_top != NULL) {
42525cf1a30Sjl 			kmem_free(oplmsu_ltrc_top,
42625cf1a30Sjl 			    (sizeof (msu_trc_t) * oplmsu_ltrc_size));
42725cf1a30Sjl 		}
42825cf1a30Sjl 		oplmsu_ltrc_top = NULL;
42925cf1a30Sjl 		oplmsu_ltrc_cur = NULL;
43025cf1a30Sjl 		oplmsu_ltrc_tail = NULL;
43125cf1a30Sjl 		mutex_exit(&oplmsu_ltrc_lock);
43225cf1a30Sjl 
43325cf1a30Sjl 		mutex_destroy(&oplmsu_ltrc_lock);
43425cf1a30Sjl 	}
43525cf1a30Sjl #endif
43625cf1a30Sjl 	rw_exit(&oplmsu_uinst->lock);
43725cf1a30Sjl 
43825cf1a30Sjl 	while (lpath) {
43925cf1a30Sjl 		if (lpath->rbuf_id) {
44025cf1a30Sjl 			unbufcall(lpath->rbuf_id);
44125cf1a30Sjl 		}
44225cf1a30Sjl 
44325cf1a30Sjl 		if (lpath->rtout_id) {
44407d06da5SSurya Prakki 			(void) untimeout(lpath->rtout_id);
44525cf1a30Sjl 		}
44625cf1a30Sjl 
44725cf1a30Sjl 		if (lpath->rbuftbl) {
44825cf1a30Sjl 			kmem_free(lpath->rbuftbl, sizeof (struct buf_tbl));
44925cf1a30Sjl 		}
45025cf1a30Sjl 
45125cf1a30Sjl 		cv_destroy(&lpath->sw_cv);
45225cf1a30Sjl 		next_lpath = lpath->l_next;
45325cf1a30Sjl 		kmem_free(lpath, sizeof (lpath_t));
45425cf1a30Sjl 		lpath = next_lpath;
45525cf1a30Sjl 	}
45625cf1a30Sjl 	ddi_remove_minor_node(dip, NULL);
45725cf1a30Sjl 	return (DDI_SUCCESS);
45825cf1a30Sjl }
45925cf1a30Sjl 
46025cf1a30Sjl /* ARGSUSED */
46125cf1a30Sjl int
oplmsu_open(queue_t * urq,dev_t * dev,int oflag,int sflag,cred_t * cred_p)46225cf1a30Sjl oplmsu_open(queue_t *urq, dev_t *dev, int oflag, int sflag, cred_t *cred_p)
46325cf1a30Sjl {
46425cf1a30Sjl 	ctrl_t	*ctrl;
46525cf1a30Sjl 	minor_t	mindev = 0;
46625cf1a30Sjl 	minor_t	qmindev = 0;
46725cf1a30Sjl 	major_t	majdev;
46825cf1a30Sjl 	ulong_t	node_flag;
46925cf1a30Sjl 
47025cf1a30Sjl 	DBG_PRINT((CE_NOTE, "oplmsu: open: "
47125cf1a30Sjl 	    "devt = 0x%lx, sflag = 0x%x", *dev, sflag));
47225cf1a30Sjl 
47325cf1a30Sjl 	if (sflag == CLONEOPEN) {
47425cf1a30Sjl 		return (EINVAL);
47525cf1a30Sjl 	}
47625cf1a30Sjl 
47725cf1a30Sjl 	/* Get minor device number */
47825cf1a30Sjl 	qmindev = (minor_t)getminor(*dev);
47925cf1a30Sjl 	/* Get node type */
48025cf1a30Sjl 	node_flag = MSU_NODE_TYPE(qmindev);
48125cf1a30Sjl 	if ((node_flag != MSU_NODE_USER) && (node_flag != MSU_NODE_META)) {
48225cf1a30Sjl 		return (EINVAL);
48325cf1a30Sjl 	}
48425cf1a30Sjl 
48525cf1a30Sjl 	mutex_enter(&oplmsu_bthrd_excl);
48625cf1a30Sjl 	if ((node_flag == MSU_NODE_USER) &&
48725cf1a30Sjl 	    (oplmsu_conf_st != MSU_CONFIGURED)) { /* User access & First open */
48825cf1a30Sjl 		int	cv_rval;
48925cf1a30Sjl 
49025cf1a30Sjl 		DBG_PRINT((CE_NOTE, "oplmsu: open: "
49125cf1a30Sjl 		    "oplmsu_conf_st = %x", oplmsu_conf_st));
49225cf1a30Sjl 
49325cf1a30Sjl 		if (oplmsu_conf_st == MSU_UNCONFIGURED) {
49425cf1a30Sjl 			oplmsu_conf_st = MSU_CONFIGURING;
49525cf1a30Sjl 
49625cf1a30Sjl 			/* Start up background thread */
49725cf1a30Sjl 			oplmsu_bthrd_id = thread_create(NULL, 2 * DEFAULTSTKSZ,
49825cf1a30Sjl 			    oplmsu_setup, (void *)oplmsu_uinst, 0, &p0, TS_RUN,
49925cf1a30Sjl 			    minclsyspri);
50025cf1a30Sjl 		}
50125cf1a30Sjl 
50225cf1a30Sjl 		/*
50325cf1a30Sjl 		 * Wait with cv_wait_sig() until background thread is
50425cf1a30Sjl 		 * completed.
50525cf1a30Sjl 		 */
50625cf1a30Sjl 		while (oplmsu_conf_st == MSU_CONFIGURING) {
50725cf1a30Sjl 			cv_rval =
50825cf1a30Sjl 			    cv_wait_sig(&oplmsu_conf_cv, &oplmsu_bthrd_excl);
50925cf1a30Sjl 			if (cv_rval == 0) {
51025cf1a30Sjl 				mutex_exit(&oplmsu_bthrd_excl);
51125cf1a30Sjl 				return (EINTR);
51225cf1a30Sjl 			}
51325cf1a30Sjl 		}
51425cf1a30Sjl 	}
51525cf1a30Sjl 	mutex_exit(&oplmsu_bthrd_excl);
51625cf1a30Sjl 
51725cf1a30Sjl 	rw_enter(&oplmsu_uinst->lock, RW_WRITER);
51825cf1a30Sjl 
51925cf1a30Sjl 	/*
52025cf1a30Sjl 	 *  If the node which will open is meta-control-node or
52125cf1a30Sjl 	 * user-access-node, and q_ptr, this is queue_t queue
52225cf1a30Sjl 	 * table member, is not NULL, then oplmsu returns
52325cf1a30Sjl 	 * SUCCESS immidiately.
52425cf1a30Sjl 	 *  This process is used to protect dual open.
52525cf1a30Sjl 	 */
52625cf1a30Sjl 
52725cf1a30Sjl 	if ((urq != NULL) && (urq->q_ptr != NULL)) {
52825cf1a30Sjl 		rw_exit(&oplmsu_uinst->lock);
52925cf1a30Sjl 		return (SUCCESS);
53025cf1a30Sjl 	}
53125cf1a30Sjl 
53225cf1a30Sjl 	/*
53325cf1a30Sjl 	 *  If the node which will open is User-Access-Node, and instance
53425cf1a30Sjl 	 * status of oplmsu is no ONLINE, then oplmsu_open process fails
53525cf1a30Sjl 	 * with return value 'EIO'.
53625cf1a30Sjl 	 */
53725cf1a30Sjl 
53825cf1a30Sjl 	if ((node_flag == MSU_NODE_USER) &&
53925cf1a30Sjl 	    (oplmsu_uinst->inst_status != INST_STAT_ONLINE)) {
54025cf1a30Sjl 		rw_exit(&oplmsu_uinst->lock);
54125cf1a30Sjl 		return (EIO);
54225cf1a30Sjl 	}
54325cf1a30Sjl 
54425cf1a30Sjl 	mindev |= qmindev;			/* Create minor device number */
54525cf1a30Sjl 	majdev = getmajor(*dev);		/* Get major device number */
54625cf1a30Sjl 	*dev = makedevice(majdev, mindev);	/* Make device number */
54725cf1a30Sjl 
54825cf1a30Sjl 	/* Allocate kernel memory for ctrl_t */
54925cf1a30Sjl 	ctrl = (ctrl_t *)kmem_zalloc(sizeof (ctrl_t), KM_SLEEP);
55025cf1a30Sjl 
55125cf1a30Sjl 	/*
55225cf1a30Sjl 	 * Initialize members of ctrl_t
55325cf1a30Sjl 	 */
55425cf1a30Sjl 	ctrl->minor = (minor_t)mindev;
55525cf1a30Sjl 	ctrl->queue = urq;
55625cf1a30Sjl 	ctrl->sleep_flag = CV_WAKEUP;
55725cf1a30Sjl 	ctrl->node_type = node_flag;
55825cf1a30Sjl 	ctrl->wbuftbl =
55925cf1a30Sjl 	    (struct buf_tbl *)kmem_zalloc(sizeof (struct buf_tbl), KM_SLEEP);
56025cf1a30Sjl 	cv_init(&ctrl->cvp, "oplmsu ctrl_tbl condvar", CV_DRIVER, NULL);
56125cf1a30Sjl 
56225cf1a30Sjl 	mutex_enter(&oplmsu_uinst->c_lock);
56325cf1a30Sjl 
56425cf1a30Sjl 	if (node_flag == MSU_NODE_USER) {	/* User access node */
56525cf1a30Sjl 
56625cf1a30Sjl 		oplmsu_uinst->user_ctrl = ctrl;
56725cf1a30Sjl 		oplmsu_queue_flag = 0;
56825cf1a30Sjl 
56925cf1a30Sjl 	} else {	/* Meta control node */
57025cf1a30Sjl 
57125cf1a30Sjl 		oplmsu_uinst->meta_ctrl = ctrl;
57225cf1a30Sjl 	}
57325cf1a30Sjl 
57425cf1a30Sjl 	RD(urq)->q_ptr = ctrl;
57525cf1a30Sjl 	WR(urq)->q_ptr = ctrl;
57625cf1a30Sjl 
57725cf1a30Sjl 	mutex_exit(&oplmsu_uinst->c_lock);
57825cf1a30Sjl 	rw_exit(&oplmsu_uinst->lock);
57925cf1a30Sjl 
58025cf1a30Sjl 	OPLMSU_TRACE(urq, (mblk_t *)node_flag, MSU_TRC_OPN);
58125cf1a30Sjl 
58225cf1a30Sjl 	qprocson(urq);	/* Enable put and service routine */
58325cf1a30Sjl 	return (SUCCESS);
58425cf1a30Sjl }
58525cf1a30Sjl 
58625cf1a30Sjl /* ARGSUSED */
58725cf1a30Sjl int
oplmsu_close(queue_t * urq,int flag,cred_t * cred_p)58825cf1a30Sjl oplmsu_close(queue_t *urq, int flag, cred_t *cred_p)
58925cf1a30Sjl {
59025cf1a30Sjl 	ctrl_t		*ctrl;
59125cf1a30Sjl 	minor_t		qmindev = 0;
59225cf1a30Sjl 	lpath_t		*lpath;
59325cf1a30Sjl 	ulong_t		node_flag;
59425cf1a30Sjl 	bufcall_id_t	wbuf_id;
59525cf1a30Sjl 	timeout_id_t	wtout_id;
59625cf1a30Sjl 
59725cf1a30Sjl 	rw_enter(&oplmsu_uinst->lock, RW_READER);
59825cf1a30Sjl 	mutex_enter(&oplmsu_uinst->l_lock);
59925cf1a30Sjl 	mutex_enter(&oplmsu_uinst->c_lock);
60025cf1a30Sjl 	if ((ctrl = urq->q_ptr) == NULL) {
60125cf1a30Sjl 		mutex_exit(&oplmsu_uinst->c_lock);
60225cf1a30Sjl 		mutex_exit(&oplmsu_uinst->l_lock);
60325cf1a30Sjl 		rw_exit(&oplmsu_uinst->lock);
60425cf1a30Sjl 
60525cf1a30Sjl 		DBG_PRINT((CE_NOTE, "oplmsu: close: "
60625cf1a30Sjl 		    "close has already been completed"));
60725cf1a30Sjl 		return (FAILURE);
60825cf1a30Sjl 	}
60925cf1a30Sjl 	qmindev = ctrl->minor;
61025cf1a30Sjl 
61125cf1a30Sjl 	DBG_PRINT((CE_NOTE, "oplmsu: close: ctrl->minor = 0x%x", qmindev));
61225cf1a30Sjl 
61325cf1a30Sjl 	node_flag = MSU_NODE_TYPE(qmindev);
61425cf1a30Sjl 	if (node_flag > MSU_NODE_META) {
61525cf1a30Sjl 		mutex_exit(&oplmsu_uinst->c_lock);
61625cf1a30Sjl 		mutex_exit(&oplmsu_uinst->l_lock);
61725cf1a30Sjl 		rw_exit(&oplmsu_uinst->lock);
61825cf1a30Sjl 		return (EINVAL);
61925cf1a30Sjl 	}
62025cf1a30Sjl 
62125cf1a30Sjl 	/*
62225cf1a30Sjl 	 *  Check that queue which is waiting for response from lower stream
62325cf1a30Sjl 	 * exist. If queue exists, oplmsu sets CV_SLEEP to sleep_flag.
62425cf1a30Sjl 	 */
62525cf1a30Sjl 
62625cf1a30Sjl 	for (lpath = oplmsu_uinst->first_lpath; lpath; ) {
62725cf1a30Sjl 		if (((RD(urq) == lpath->hndl_uqueue) ||
62825cf1a30Sjl 		    (WR(urq) == lpath->hndl_uqueue)) &&
62925cf1a30Sjl 		    (lpath->hndl_mp != NULL)) {
63025cf1a30Sjl 			ctrl->sleep_flag = CV_SLEEP;
63125cf1a30Sjl 			break;
63225cf1a30Sjl 		}
63325cf1a30Sjl 
63425cf1a30Sjl 		lpath = lpath->l_next;
63525cf1a30Sjl 	}
63625cf1a30Sjl 	mutex_exit(&oplmsu_uinst->l_lock);
63725cf1a30Sjl 	rw_exit(&oplmsu_uinst->lock);
63825cf1a30Sjl 
63925cf1a30Sjl 	/* If sleep_flag is not CV_SLEEP, oplmsu calls cv_wait. */
64025cf1a30Sjl 	if (lpath) {
64125cf1a30Sjl 		while (ctrl->sleep_flag != CV_WAKEUP) {
64225cf1a30Sjl 			cv_wait(&ctrl->cvp, &oplmsu_uinst->c_lock);
64325cf1a30Sjl 		}
64425cf1a30Sjl 	}
64525cf1a30Sjl 
64625cf1a30Sjl 	flushq(RD(urq), FLUSHALL);
64725cf1a30Sjl 	flushq(WR(urq), FLUSHALL);
64825cf1a30Sjl 	mutex_exit(&oplmsu_uinst->c_lock);
64925cf1a30Sjl 	qprocsoff(urq);		/* Disable queuing of queue */
65025cf1a30Sjl 
65125cf1a30Sjl 	rw_enter(&oplmsu_uinst->lock, RW_WRITER);
65225cf1a30Sjl 	switch (node_flag) {
65325cf1a30Sjl 	case MSU_NODE_USER :	/* User access node */
65425cf1a30Sjl 		oplmsu_uinst->user_ctrl = NULL;
65525cf1a30Sjl 		oplmsu_queue_flag = 0;
65625cf1a30Sjl 		break;
65725cf1a30Sjl 
65825cf1a30Sjl 	case MSU_NODE_META :	/* Meta control node */
65925cf1a30Sjl 		oplmsu_uinst->meta_ctrl = NULL;
66025cf1a30Sjl 		break;
66125cf1a30Sjl 
66225cf1a30Sjl 	default :
66325cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: close: node_flag = 0x%lx", node_flag);
66425cf1a30Sjl 	}
66525cf1a30Sjl 
666bbe1232eSToomas Soome 	ctrl->minor = 0;
66725cf1a30Sjl 	ctrl->queue = NULL;
66825cf1a30Sjl 	wbuf_id = ctrl->wbuf_id;
66925cf1a30Sjl 	wtout_id = ctrl->wtout_id;
67025cf1a30Sjl 	ctrl->wbuf_id = 0;
67125cf1a30Sjl 	ctrl->wtout_id = 0;
67225cf1a30Sjl 
67325cf1a30Sjl 	cv_destroy(&ctrl->cvp);
67425cf1a30Sjl 	kmem_free(ctrl->wbuftbl, sizeof (struct buf_tbl));
67525cf1a30Sjl 	ctrl->wbuftbl = NULL;
67625cf1a30Sjl 
67725cf1a30Sjl 	RD(urq)->q_ptr = NULL;
67825cf1a30Sjl 	WR(urq)->q_ptr = NULL;
67925cf1a30Sjl 	rw_exit(&oplmsu_uinst->lock);
68025cf1a30Sjl 
68125cf1a30Sjl 	if (wbuf_id != 0) {
68225cf1a30Sjl 		unbufcall(wbuf_id);
68325cf1a30Sjl 	}
68425cf1a30Sjl 
68525cf1a30Sjl 	if (wtout_id != 0) {
68607d06da5SSurya Prakki 		(void) untimeout(wtout_id);
68725cf1a30Sjl 	}
68825cf1a30Sjl 
68925cf1a30Sjl 	/* Free kernel memory for ctrl_t */
69025cf1a30Sjl 	kmem_free(ctrl, sizeof (ctrl_t));
69125cf1a30Sjl 
69225cf1a30Sjl 	OPLMSU_TRACE(urq, (mblk_t *)node_flag, MSU_TRC_CLS);
69325cf1a30Sjl 	return (SUCCESS);
69425cf1a30Sjl }
69525cf1a30Sjl 
69625cf1a30Sjl /*
69725cf1a30Sjl  * Upper write put procedure
69825cf1a30Sjl  */
69925cf1a30Sjl int
oplmsu_uwput(queue_t * uwq,mblk_t * mp)70025cf1a30Sjl oplmsu_uwput(queue_t *uwq, mblk_t *mp)
70125cf1a30Sjl {
70225cf1a30Sjl 
70325cf1a30Sjl 	if (mp == NULL) {
70425cf1a30Sjl 		return (SUCCESS);
70525cf1a30Sjl 	}
70625cf1a30Sjl 
70725cf1a30Sjl 	if ((uwq == NULL) || (uwq->q_ptr == NULL)) {
70825cf1a30Sjl 		freemsg(mp);
70925cf1a30Sjl 		return (SUCCESS);
71025cf1a30Sjl 	}
71125cf1a30Sjl 
71225cf1a30Sjl 	OPLMSU_TRACE(uwq, mp, MSU_TRC_UI);
71325cf1a30Sjl 
71425cf1a30Sjl 	rw_enter(&oplmsu_uinst->lock, RW_READER);
71525cf1a30Sjl 	if (mp->b_datap->db_type == M_FLUSH) {
71625cf1a30Sjl 		oplmsu_wcmn_flush_hndl(uwq, mp, RW_READER);
71725cf1a30Sjl 	} else if (mp->b_datap->db_type >= QPCTL) {
71825cf1a30Sjl 		ctrl_t	*ctrl;
71925cf1a30Sjl 
72025cf1a30Sjl 		mutex_enter(&oplmsu_uinst->c_lock);
72125cf1a30Sjl 		ctrl = (ctrl_t *)uwq->q_ptr;
72225cf1a30Sjl 
72325cf1a30Sjl 		/* Link high priority message to local queue */
72425cf1a30Sjl 		oplmsu_link_high_primsg(&ctrl->first_upri_hi,
72525cf1a30Sjl 		    &ctrl->last_upri_hi, mp);
72625cf1a30Sjl 
72725cf1a30Sjl 		mutex_exit(&oplmsu_uinst->c_lock);
72825cf1a30Sjl 		oplmsu_wcmn_high_qenable(WR(uwq), RW_READER);
72925cf1a30Sjl 	} else {
73007d06da5SSurya Prakki 		(void) putq(WR(uwq), mp);
73125cf1a30Sjl 	}
73225cf1a30Sjl 	rw_exit(&oplmsu_uinst->lock);
73325cf1a30Sjl 	return (SUCCESS);
73425cf1a30Sjl }
73525cf1a30Sjl 
73625cf1a30Sjl /*
73725cf1a30Sjl  * Upper write service procedure
73825cf1a30Sjl  */
73925cf1a30Sjl int
oplmsu_uwsrv(queue_t * uwq)74025cf1a30Sjl oplmsu_uwsrv(queue_t *uwq)
74125cf1a30Sjl {
74225cf1a30Sjl 	struct iocblk	*iocp = NULL;
74325cf1a30Sjl 	mblk_t		*mp = NULL;
74425cf1a30Sjl 	int		rval;
74525cf1a30Sjl 
74625cf1a30Sjl 	if ((uwq == NULL) || (uwq->q_ptr == NULL)) {
74725cf1a30Sjl 		return (FAILURE);
74825cf1a30Sjl 	}
74925cf1a30Sjl 
75025cf1a30Sjl 	rw_enter(&oplmsu_uinst->lock, RW_READER);
75125cf1a30Sjl 
75225cf1a30Sjl 	/* Handle high priority message */
75325cf1a30Sjl 	while (mp = oplmsu_wcmn_high_getq(uwq)) {
75425cf1a30Sjl 		if (mp->b_datap->db_type == M_FLUSH) {
75525cf1a30Sjl 			oplmsu_wcmn_flush_hndl(uwq, mp, RW_READER);
75625cf1a30Sjl 			continue;
75725cf1a30Sjl 		}
75825cf1a30Sjl 
75925cf1a30Sjl 		if (oplmsu_wcmn_through_hndl(uwq, mp, MSU_HIGH, RW_READER) ==
76025cf1a30Sjl 		    FAILURE) {
76125cf1a30Sjl 			rw_exit(&oplmsu_uinst->lock);
76225cf1a30Sjl 			return (SUCCESS);
76325cf1a30Sjl 		}
76425cf1a30Sjl 	}
76525cf1a30Sjl 	rw_exit(&oplmsu_uinst->lock);
76625cf1a30Sjl 
76725cf1a30Sjl 	/* Handle normal priority message */
76825cf1a30Sjl 	while (mp = getq(uwq)) {
76925cf1a30Sjl 		rval = SUCCESS;
77025cf1a30Sjl 		switch (mp->b_datap->db_type) {
77125cf1a30Sjl 		case M_IOCTL :
77225cf1a30Sjl 			iocp = (struct iocblk *)mp->b_rptr;
77325cf1a30Sjl 			switch (iocp->ioc_cmd) {
77425cf1a30Sjl 			case I_PLINK :
77525cf1a30Sjl 				if (oplmsu_cmn_pullup_msg(uwq, mp) != FAILURE) {
77625cf1a30Sjl 					rval = oplmsu_uwioctl_iplink(uwq, mp);
77725cf1a30Sjl 				}
77825cf1a30Sjl 				break;
77925cf1a30Sjl 
78025cf1a30Sjl 			case I_PUNLINK :
78125cf1a30Sjl 				if (oplmsu_cmn_pullup_msg(uwq, mp) != FAILURE) {
78225cf1a30Sjl 					rval = oplmsu_uwioctl_ipunlink(uwq, mp);
78325cf1a30Sjl 				}
78425cf1a30Sjl 				break;
78525cf1a30Sjl 
78625cf1a30Sjl 			case TCSETS :		/* FALLTHRU */
78725cf1a30Sjl 			case TCSETSW :		/* FALLTHRU */
78825cf1a30Sjl 			case TCSETSF :		/* FALLTHRU */
78925cf1a30Sjl 			case TIOCMSET :		/* FALLTHRU */
79025cf1a30Sjl 			case TIOCSPPS :		/* FALLTHRU */
79125cf1a30Sjl 			case TIOCSWINSZ :	/* FALLTHRU */
79225cf1a30Sjl 			case TIOCSSOFTCAR :
79325cf1a30Sjl 				rval = oplmsu_uwioctl_termios(uwq, mp);
79425cf1a30Sjl 				break;
79525cf1a30Sjl 
79625cf1a30Sjl 			default :
79725cf1a30Sjl 				rw_enter(&oplmsu_uinst->lock, RW_READER);
79825cf1a30Sjl 				rval = oplmsu_wcmn_through_hndl(uwq, mp,
79925cf1a30Sjl 				    MSU_NORM, RW_READER);
80025cf1a30Sjl 				rw_exit(&oplmsu_uinst->lock);
80125cf1a30Sjl 				break;
80225cf1a30Sjl 			}
80325cf1a30Sjl 			break;
80425cf1a30Sjl 
80525cf1a30Sjl 		default :
80625cf1a30Sjl 			rw_enter(&oplmsu_uinst->lock, RW_READER);
80725cf1a30Sjl 			rval = oplmsu_wcmn_through_hndl(uwq, mp, MSU_NORM,
80825cf1a30Sjl 			    RW_READER);
80925cf1a30Sjl 			rw_exit(&oplmsu_uinst->lock);
81025cf1a30Sjl 			break;
81125cf1a30Sjl 		}
81225cf1a30Sjl 
81325cf1a30Sjl 		if (rval == FAILURE) {
81425cf1a30Sjl 			break;
81525cf1a30Sjl 		}
81625cf1a30Sjl 	}
81725cf1a30Sjl 	return (SUCCESS);
81825cf1a30Sjl }
81925cf1a30Sjl 
82025cf1a30Sjl /*
82125cf1a30Sjl  * Lower write service procedure
82225cf1a30Sjl  */
82325cf1a30Sjl int
oplmsu_lwsrv(queue_t * lwq)82425cf1a30Sjl oplmsu_lwsrv(queue_t *lwq)
82525cf1a30Sjl {
82625cf1a30Sjl 	mblk_t		*mp;
82725cf1a30Sjl 	queue_t		*dst_queue;
82825cf1a30Sjl 	lpath_t		*lpath;
82925cf1a30Sjl 
83025cf1a30Sjl 	rw_enter(&oplmsu_uinst->lock, RW_READER);
83125cf1a30Sjl 	while (mp = getq(lwq)) {
83225cf1a30Sjl 		if (mp->b_datap->db_type >= QPCTL) {
83325cf1a30Sjl 			rw_exit(&oplmsu_uinst->lock);
83425cf1a30Sjl 			OPLMSU_TRACE(WR(lwq), mp, MSU_TRC_LO);
83525cf1a30Sjl 			putnext(WR(lwq), mp);
83625cf1a30Sjl 			rw_enter(&oplmsu_uinst->lock, RW_READER);
83725cf1a30Sjl 			continue;
83825cf1a30Sjl 		}
83925cf1a30Sjl 
84025cf1a30Sjl 		dst_queue = WR(lwq);
84125cf1a30Sjl 		if (canputnext(dst_queue)) {
84225cf1a30Sjl 			rw_exit(&oplmsu_uinst->lock);
84325cf1a30Sjl 			OPLMSU_TRACE(dst_queue, mp, MSU_TRC_LO);
84425cf1a30Sjl 			putnext(dst_queue, mp);
84525cf1a30Sjl 			rw_enter(&oplmsu_uinst->lock, RW_READER);
84625cf1a30Sjl 		} else {
84707d06da5SSurya Prakki 			(void) putbq(WR(lwq), mp);
84825cf1a30Sjl 			break;
84925cf1a30Sjl 		}
85025cf1a30Sjl 	}
85125cf1a30Sjl 
85225cf1a30Sjl 	mutex_enter(&oplmsu_uinst->l_lock);
85325cf1a30Sjl 	lpath = (lpath_t *)lwq->q_ptr;
85425cf1a30Sjl 	if (lpath->uwq_flag != 0) {
85525cf1a30Sjl 		qenable(WR(lpath->uwq_queue));
85625cf1a30Sjl 		lpath->uwq_flag = 0;
85725cf1a30Sjl 		lpath->uwq_queue = NULL;
85825cf1a30Sjl 	}
85925cf1a30Sjl 	mutex_exit(&oplmsu_uinst->l_lock);
86025cf1a30Sjl 	rw_exit(&oplmsu_uinst->lock);
86125cf1a30Sjl 	return (SUCCESS);
86225cf1a30Sjl }
86325cf1a30Sjl 
86425cf1a30Sjl /*
86525cf1a30Sjl  * Lower read put procedure
86625cf1a30Sjl  */
86725cf1a30Sjl int
oplmsu_lrput(queue_t * lrq,mblk_t * mp)86825cf1a30Sjl oplmsu_lrput(queue_t *lrq, mblk_t *mp)
86925cf1a30Sjl {
87025cf1a30Sjl 
87125cf1a30Sjl 	if (mp == NULL) {
87225cf1a30Sjl 		return (SUCCESS);
87325cf1a30Sjl 	}
87425cf1a30Sjl 
87525cf1a30Sjl 	if ((lrq == NULL) || (lrq->q_ptr == NULL)) {
87625cf1a30Sjl 		freemsg(mp);
87725cf1a30Sjl 		return (SUCCESS);
87825cf1a30Sjl 	}
87925cf1a30Sjl 
88025cf1a30Sjl 	OPLMSU_TRACE(lrq, mp, MSU_TRC_LI);
88125cf1a30Sjl 
88225cf1a30Sjl 	if (mp->b_datap->db_type == M_FLUSH) {
88325cf1a30Sjl 		rw_enter(&oplmsu_uinst->lock, RW_READER);
88425cf1a30Sjl 		oplmsu_rcmn_flush_hndl(lrq, mp);
88525cf1a30Sjl 		rw_exit(&oplmsu_uinst->lock);
88625cf1a30Sjl 	} else if (mp->b_datap->db_type >= QPCTL) {
88725cf1a30Sjl 		lpath_t	*lpath;
88825cf1a30Sjl 
88925cf1a30Sjl 		rw_enter(&oplmsu_uinst->lock, RW_READER);
89025cf1a30Sjl 		mutex_enter(&oplmsu_uinst->l_lock);
89125cf1a30Sjl 		lpath = lrq->q_ptr;
89225cf1a30Sjl 
89325cf1a30Sjl 		/* Link high priority message to local queue */
89425cf1a30Sjl 		oplmsu_link_high_primsg(&lpath->first_lpri_hi,
89525cf1a30Sjl 		    &lpath->last_lpri_hi, mp);
89625cf1a30Sjl 
89725cf1a30Sjl 		mutex_exit(&oplmsu_uinst->l_lock);
89825cf1a30Sjl 		rw_exit(&oplmsu_uinst->lock);
89925cf1a30Sjl 		oplmsu_rcmn_high_qenable(lrq);
90025cf1a30Sjl 	} else {
90107d06da5SSurya Prakki 		(void) putq(lrq, mp);
90225cf1a30Sjl 	}
90325cf1a30Sjl 	return (SUCCESS);
90425cf1a30Sjl }
90525cf1a30Sjl 
90625cf1a30Sjl /*
90725cf1a30Sjl  * Lower read service procedure
90825cf1a30Sjl  */
90925cf1a30Sjl int
oplmsu_lrsrv(queue_t * lrq)91025cf1a30Sjl oplmsu_lrsrv(queue_t *lrq)
91125cf1a30Sjl {
91225cf1a30Sjl 	mblk_t		*mp;
91325cf1a30Sjl 	boolean_t	aborted;
91425cf1a30Sjl 	int		rval;
91525cf1a30Sjl 
91625cf1a30Sjl 	if ((lrq == NULL) || (lrq->q_ptr == NULL)) {
91725cf1a30Sjl 		return (FAILURE);
91825cf1a30Sjl 	}
91925cf1a30Sjl 
92025cf1a30Sjl 	/* Handle normal priority message */
92125cf1a30Sjl 	while (mp = getq(lrq)) {
92225cf1a30Sjl 		if (mp->b_datap->db_type >= QPCTL) {
92325cf1a30Sjl 			cmn_err(CE_WARN, "oplmsu: lr-srv: "
92425cf1a30Sjl 			    "Invalid db_type => %x", mp->b_datap->db_type);
92525cf1a30Sjl 		}
92625cf1a30Sjl 
92725cf1a30Sjl 		switch (mp->b_datap->db_type) {
92825cf1a30Sjl 		case M_DATA :
92925cf1a30Sjl 			aborted = B_FALSE;
93025cf1a30Sjl 			rw_enter(&oplmsu_uinst->lock, RW_READER);
93125cf1a30Sjl 			if ((abort_enable == KIOCABORTALTERNATE) &&
93225cf1a30Sjl 			    (RD(oplmsu_uinst->lower_queue) == lrq)) {
93325cf1a30Sjl 				uchar_t	*rx_char = mp->b_rptr;
93425cf1a30Sjl 				lpath_t	*lpath;
93525cf1a30Sjl 
93625cf1a30Sjl 				mutex_enter(&oplmsu_uinst->l_lock);
93725cf1a30Sjl 				lpath = lrq->q_ptr;
93825cf1a30Sjl 				while (rx_char != mp->b_wptr) {
93919397407SSherry Moore 					if (*rx_char == *lpath->abt_char) {
94025cf1a30Sjl 					lpath->abt_char++;
94125cf1a30Sjl 					if (*lpath->abt_char == '\0') {
94219397407SSherry Moore 						abort_sequence_enter((char *)
94319397407SSherry Moore 						    NULL);
94419397407SSherry Moore 						lpath->abt_char
94519397407SSherry Moore 						    = oplmsu_uinst->abts;
94619397407SSherry Moore 						aborted = B_TRUE;
94719397407SSherry Moore 						break;
94825cf1a30Sjl 					}
94919397407SSherry Moore 					} else {
95025cf1a30Sjl 					lpath->abt_char = (*rx_char ==
95125cf1a30Sjl 					    *oplmsu_uinst->abts) ?
95219397407SSherry Moore 					    oplmsu_uinst->abts + 1 :
95319397407SSherry Moore 					    oplmsu_uinst->abts;
95419397407SSherry Moore 					}
95519397407SSherry Moore 					rx_char++;
95625cf1a30Sjl 				}
95725cf1a30Sjl 				mutex_exit(&oplmsu_uinst->l_lock);
95825cf1a30Sjl 			}
95925cf1a30Sjl 			rw_exit(&oplmsu_uinst->lock);
96025cf1a30Sjl 
96125cf1a30Sjl 			if (aborted) {
96225cf1a30Sjl 				freemsg(mp);
96325cf1a30Sjl 				continue;
96425cf1a30Sjl 			}
96525cf1a30Sjl 
96625cf1a30Sjl 			/*
96725cf1a30Sjl 			 * When 1st byte of the received M_DATA is XON or,
96825cf1a30Sjl 			 * 1st byte is XOFF and 2nd byte is XON.
96925cf1a30Sjl 			 */
97025cf1a30Sjl 
97125cf1a30Sjl 			if ((*(mp->b_rptr) == MSU_XON) ||
97225cf1a30Sjl 			    (((mp->b_wptr - mp->b_rptr) == 2) &&
97325cf1a30Sjl 			    ((*(mp->b_rptr) == MSU_XOFF) &&
97425cf1a30Sjl 			    (*(mp->b_rptr + 1) == MSU_XON)))) {
97525cf1a30Sjl 				/* Path switching by XOFF/XON */
97625cf1a30Sjl 				if (oplmsu_lrdata_xoffxon(lrq, mp) == FAILURE) {
97725cf1a30Sjl 					return (SUCCESS);
97825cf1a30Sjl 				}
97925cf1a30Sjl 			} else {
98025cf1a30Sjl 				rw_enter(&oplmsu_uinst->lock, RW_READER);
98125cf1a30Sjl 				rval =
98225cf1a30Sjl 				    oplmsu_rcmn_through_hndl(lrq, mp, MSU_NORM);
98325cf1a30Sjl 				rw_exit(&oplmsu_uinst->lock);
98425cf1a30Sjl 
98525cf1a30Sjl 				if (rval == FAILURE) {
98625cf1a30Sjl 					return (SUCCESS);
98725cf1a30Sjl 				}
98825cf1a30Sjl 			}
98925cf1a30Sjl 			break;
99025cf1a30Sjl 
99125cf1a30Sjl 		case M_BREAK :
99225cf1a30Sjl 			if ((mp->b_wptr - mp->b_rptr) == 0 && msgdsize(mp)
99325cf1a30Sjl 			    == 0) {
99425cf1a30Sjl 				rw_enter(&oplmsu_uinst->lock, RW_READER);
99525cf1a30Sjl 				if ((abort_enable != KIOCABORTALTERNATE) &&
99625cf1a30Sjl 				    (RD(oplmsu_uinst->lower_queue) == lrq)) {
99725cf1a30Sjl 					abort_sequence_enter((char *)NULL);
99825cf1a30Sjl 				}
99925cf1a30Sjl 				rw_exit(&oplmsu_uinst->lock);
100025cf1a30Sjl 				freemsg(mp);
100125cf1a30Sjl 				break;
100225cf1a30Sjl 			}
100325cf1a30Sjl 			/* FALLTHRU */
100425cf1a30Sjl 
100525cf1a30Sjl 		default :
100625cf1a30Sjl 			rw_enter(&oplmsu_uinst->lock, RW_READER);
100725cf1a30Sjl 			(void) oplmsu_rcmn_through_hndl(lrq, mp, MSU_NORM);
100825cf1a30Sjl 			rw_exit(&oplmsu_uinst->lock);
100925cf1a30Sjl 			break;
101025cf1a30Sjl 		}
101125cf1a30Sjl 	}
101225cf1a30Sjl 	return (SUCCESS);
101325cf1a30Sjl }
101425cf1a30Sjl 
101525cf1a30Sjl /*
101625cf1a30Sjl  * Upper read service procedure
101725cf1a30Sjl  */
101825cf1a30Sjl int
oplmsu_ursrv(queue_t * urq)101925cf1a30Sjl oplmsu_ursrv(queue_t *urq)
102025cf1a30Sjl {
102125cf1a30Sjl 	mblk_t	*mp;
102225cf1a30Sjl 	queue_t	*dst_queue;
102325cf1a30Sjl 	lpath_t	*lpath;
102425cf1a30Sjl 	ctrl_t	*ctrl;
102525cf1a30Sjl 	int	res_chk = 0;
102625cf1a30Sjl 
102725cf1a30Sjl 	rw_enter(&oplmsu_uinst->lock, RW_READER);
102825cf1a30Sjl 	while (mp = getq(urq)) {
102925cf1a30Sjl 		if (mp->b_datap->db_type >= QPCTL) {
103025cf1a30Sjl 			if ((mp->b_datap->db_type == M_IOCACK) ||
103125cf1a30Sjl 			    (mp->b_datap->db_type == M_IOCNAK)) {
103225cf1a30Sjl 				res_chk = 1;
103325cf1a30Sjl 			}
103425cf1a30Sjl 			rw_exit(&oplmsu_uinst->lock);
103525cf1a30Sjl 			OPLMSU_TRACE(RD(urq), mp, MSU_TRC_UO);
103625cf1a30Sjl 			putnext(RD(urq), mp);
103725cf1a30Sjl 
103825cf1a30Sjl 			rw_enter(&oplmsu_uinst->lock, RW_READER);
103925cf1a30Sjl 			mutex_enter(&oplmsu_uinst->l_lock);
104025cf1a30Sjl 			lpath = oplmsu_uinst->first_lpath;
104125cf1a30Sjl 			while (lpath) {
104225cf1a30Sjl 				qenable(RD(lpath->lower_queue));
104325cf1a30Sjl 				lpath = lpath->l_next;
104425cf1a30Sjl 			}
104525cf1a30Sjl 			mutex_exit(&oplmsu_uinst->l_lock);
104625cf1a30Sjl 
104725cf1a30Sjl 			if (res_chk == 1) {
104825cf1a30Sjl 				mutex_enter(&oplmsu_uinst->c_lock);
104925cf1a30Sjl 				ctrl = (ctrl_t *)urq->q_ptr;
105025cf1a30Sjl 				if (ctrl != NULL) {
105125cf1a30Sjl 					if (ctrl->wait_queue != NULL) {
105225cf1a30Sjl 						qenable(WR(ctrl->wait_queue));
105325cf1a30Sjl 						ctrl->wait_queue = NULL;
105425cf1a30Sjl 					}
105525cf1a30Sjl 				}
105625cf1a30Sjl 				mutex_exit(&oplmsu_uinst->c_lock);
105725cf1a30Sjl 				res_chk = 0;
105825cf1a30Sjl 			}
105925cf1a30Sjl 			continue;
106025cf1a30Sjl 		}
106125cf1a30Sjl 
106225cf1a30Sjl 		dst_queue = RD(urq);
106325cf1a30Sjl 		if (canputnext(dst_queue)) {
106425cf1a30Sjl 			rw_exit(&oplmsu_uinst->lock);
106525cf1a30Sjl 			OPLMSU_TRACE(dst_queue, mp, MSU_TRC_UO);
106625cf1a30Sjl 			putnext(dst_queue, mp);
106725cf1a30Sjl 			rw_enter(&oplmsu_uinst->lock, RW_READER);
106825cf1a30Sjl 		} else {
106907d06da5SSurya Prakki 			(void) putbq(urq, mp);
107025cf1a30Sjl 			break;
107125cf1a30Sjl 		}
107225cf1a30Sjl 	}
107325cf1a30Sjl 
107425cf1a30Sjl 	mutex_enter(&oplmsu_uinst->c_lock);
107525cf1a30Sjl 	ctrl = urq->q_ptr;
107625cf1a30Sjl 	if (ctrl->lrq_flag != 0) {
107725cf1a30Sjl 		qenable(ctrl->lrq_queue);
107825cf1a30Sjl 		ctrl->lrq_flag = 0;
107925cf1a30Sjl 		ctrl->lrq_queue = NULL;
108025cf1a30Sjl 	}
108125cf1a30Sjl 	mutex_exit(&oplmsu_uinst->c_lock);
108225cf1a30Sjl 	rw_exit(&oplmsu_uinst->lock);
108325cf1a30Sjl 	return (SUCCESS);
108425cf1a30Sjl }
108525cf1a30Sjl 
108625cf1a30Sjl int
oplmsu_open_msu(dev_info_t * dip,ldi_ident_t * lip,ldi_handle_t * lhp)108725cf1a30Sjl oplmsu_open_msu(dev_info_t *dip, ldi_ident_t *lip, ldi_handle_t *lhp)
108825cf1a30Sjl {
108925cf1a30Sjl 	dev_t	devt;
109025cf1a30Sjl 	int	rval;
109125cf1a30Sjl 
109225cf1a30Sjl 	/* Allocate LDI identifier */
109325cf1a30Sjl 	rval = ldi_ident_from_dip(dip, lip);
109425cf1a30Sjl 	if (rval != 0) {
109525cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: open-msu: "
109625cf1a30Sjl 		    "ldi_ident_from_dip failed. errno = %d", rval);
109725cf1a30Sjl 		return (rval);
109825cf1a30Sjl 	}
109925cf1a30Sjl 
110025cf1a30Sjl 	/* Open oplmsu(meta ctrl node) */
110125cf1a30Sjl 	devt = makedevice(ddi_driver_major(dip), META_NODE_MASK);
110225cf1a30Sjl 	rval =
110325cf1a30Sjl 	    ldi_open_by_dev(&devt, OTYP_CHR, (FREAD|FWRITE), kcred, lhp, *lip);
110425cf1a30Sjl 	if (rval != 0) {
110525cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: open-msu: "
110625cf1a30Sjl 		    "ldi_open_by_dev failed. errno = %d", rval);
110725cf1a30Sjl 		ldi_ident_release(*lip);
110825cf1a30Sjl 	}
110925cf1a30Sjl 	return (rval);
111025cf1a30Sjl }
111125cf1a30Sjl 
111225cf1a30Sjl int
oplmsu_plink_serial(dev_info_t * dip,ldi_handle_t msu_lh,int * id)111325cf1a30Sjl oplmsu_plink_serial(dev_info_t *dip, ldi_handle_t msu_lh, int *id)
111425cf1a30Sjl {
111525cf1a30Sjl 	ldi_ident_t	li = NULL;
111625cf1a30Sjl 	ldi_handle_t	lh = NULL;
111725cf1a30Sjl 	int		param;
111825cf1a30Sjl 	int		rval;
111925cf1a30Sjl 	char		pathname[MSU_PATHNAME_SIZE];
112025cf1a30Sjl 	char		wrkbuf[MSU_PATHNAME_SIZE];
112125cf1a30Sjl 
112225cf1a30Sjl 	/* Create physical path-name for serial */
112307d06da5SSurya Prakki 	(void) ddi_pathname(dip, wrkbuf);
112425cf1a30Sjl 	*(wrkbuf + strlen(wrkbuf)) = '\0';
112507d06da5SSurya Prakki 	(void) sprintf(pathname, "/devices%s:%c", wrkbuf,
112607d06da5SSurya Prakki 	    'a'+ ddi_get_instance(dip));
112725cf1a30Sjl 
112825cf1a30Sjl 	/* Allocate LDI identifier */
112925cf1a30Sjl 	rval = ldi_ident_from_dip(dip, &li);
113025cf1a30Sjl 	if (rval != 0) {
113125cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: plink-serial: "
113225cf1a30Sjl 		    "%s ldi_ident_from_dip failed. errno = %d", pathname, rval);
113325cf1a30Sjl 		return (rval);
113425cf1a30Sjl 	}
113525cf1a30Sjl 
113625cf1a30Sjl 	/* Open serial */
113725cf1a30Sjl 	rval = ldi_open_by_name(pathname, (FREAD|FWRITE|FEXCL), kcred, &lh, li);
113825cf1a30Sjl 	if (rval != 0) {
113925cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: plink-serial: "
114025cf1a30Sjl 		    "%s open failed. errno = %d", pathname, rval);
114125cf1a30Sjl 		ldi_ident_release(li);
114225cf1a30Sjl 		return (rval);
114325cf1a30Sjl 	}
114425cf1a30Sjl 
114525cf1a30Sjl 	/* Try to remove the top module from the stream */
114625cf1a30Sjl 	param = 0;
114725cf1a30Sjl 	while ((ldi_ioctl(lh, I_POP, (intptr_t)0, FKIOCTL, kcred, &param))
114825cf1a30Sjl 	    == 0) {
114925cf1a30Sjl 		continue;
115025cf1a30Sjl 	}
115125cf1a30Sjl 
115225cf1a30Sjl 	/* Issue ioctl(I_PLINK) */
115325cf1a30Sjl 	param = 0;
115425cf1a30Sjl 	rval = ldi_ioctl(msu_lh, I_PLINK, (intptr_t)lh, FKIOCTL, kcred, &param);
115525cf1a30Sjl 	if (rval != 0) {
115625cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: plink-serial: "
115725cf1a30Sjl 		    "%s ioctl(I_PLINK) failed. errno = %d", pathname, rval);
115825cf1a30Sjl 	}
115925cf1a30Sjl 
116025cf1a30Sjl 	(void) ldi_close(lh, (FREAD|FWRITE|FEXCL), kcred);
116125cf1a30Sjl 	ldi_ident_release(li);
116225cf1a30Sjl 
116325cf1a30Sjl 	*id = param;	/* Save link-id */
116425cf1a30Sjl 	return (rval);
116525cf1a30Sjl }
116625cf1a30Sjl 
116725cf1a30Sjl int
oplmsu_set_lpathnum(int lnk_id,int instance)116825cf1a30Sjl oplmsu_set_lpathnum(int lnk_id, int instance)
116925cf1a30Sjl {
117025cf1a30Sjl 	lpath_t	*lpath;
117125cf1a30Sjl 	int	rval = SUCCESS;
117225cf1a30Sjl 
117325cf1a30Sjl 	rw_enter(&oplmsu_uinst->lock, RW_READER);
117425cf1a30Sjl 	mutex_enter(&oplmsu_uinst->l_lock);
117525cf1a30Sjl 	lpath = oplmsu_uinst->first_lpath;
117625cf1a30Sjl 	while (lpath) {
117725cf1a30Sjl 		if ((lpath->path_no == UNDEFINED) &&
117825cf1a30Sjl 		    (lpath->link_id == lnk_id)) {
117925cf1a30Sjl 			lpath->path_no = instance; /* Set instance number */
118025cf1a30Sjl 			lpath->src_upath = NULL;
118125cf1a30Sjl 			lpath->status = MSU_SETID_NU;
118225cf1a30Sjl 			break;
118325cf1a30Sjl 		}
118425cf1a30Sjl 		lpath = lpath->l_next;
118525cf1a30Sjl 	}
118625cf1a30Sjl 	mutex_exit(&oplmsu_uinst->l_lock);
118725cf1a30Sjl 	rw_exit(&oplmsu_uinst->lock);
118825cf1a30Sjl 
118925cf1a30Sjl 	if (lpath == NULL) {
119025cf1a30Sjl 		rval = EINVAL;
119125cf1a30Sjl 	}
119225cf1a30Sjl 	return (rval);
119325cf1a30Sjl }
119425cf1a30Sjl 
119525cf1a30Sjl int
oplmsu_dr_attach(dev_info_t * dip)119625cf1a30Sjl oplmsu_dr_attach(dev_info_t *dip)
119725cf1a30Sjl {
119825cf1a30Sjl 	ldi_ident_t	msu_li = NULL;
119925cf1a30Sjl 	ldi_handle_t	msu_lh = NULL;
120025cf1a30Sjl 	upath_t		*upath;
120125cf1a30Sjl 	int		len;
120225cf1a30Sjl 	int		instance;
120325cf1a30Sjl 	int		lnk_id = 0;
120425cf1a30Sjl 	int		param = 0;
120525cf1a30Sjl 	int		rval;
120625cf1a30Sjl 
120725cf1a30Sjl 	/* Get instance for serial */
120825cf1a30Sjl 	instance = ddi_get_instance(dip);
120925cf1a30Sjl 
121025cf1a30Sjl 	rw_enter(&oplmsu_uinst->lock, RW_WRITER);
121125cf1a30Sjl 	mutex_enter(&oplmsu_uinst->u_lock);
121225cf1a30Sjl 
121325cf1a30Sjl 	/* Get current number of paths */
121425cf1a30Sjl 	oplmsu_uinst->path_num = oplmsu_get_pathnum();
121525cf1a30Sjl 
121625cf1a30Sjl 	/* Check specified upath_t */
121725cf1a30Sjl 	upath = oplmsu_uinst->first_upath;
121825cf1a30Sjl 	while (upath) {
121925cf1a30Sjl 		if (instance == upath->path_no) {
122025cf1a30Sjl 			break;
122125cf1a30Sjl 		}
122225cf1a30Sjl 		upath = upath->u_next;
122325cf1a30Sjl 	}
122425cf1a30Sjl 	mutex_exit(&oplmsu_uinst->u_lock);
122525cf1a30Sjl 	rw_exit(&oplmsu_uinst->lock);
122625cf1a30Sjl 
122725cf1a30Sjl 	if (upath != NULL) {
122825cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: attach(dr): "
122925cf1a30Sjl 		    "Instance %d already exist", instance);
123025cf1a30Sjl 		return (EINVAL);
123125cf1a30Sjl 	}
123225cf1a30Sjl 
123325cf1a30Sjl 	/* Open oplmsu */
123425cf1a30Sjl 	rval = oplmsu_open_msu(oplmsu_uinst->msu_dip, &msu_li, &msu_lh);
123525cf1a30Sjl 	if (rval != 0) {
123625cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: attach(dr): "
123725cf1a30Sjl 		    "msu open failed. errno = %d", rval);
123825cf1a30Sjl 		return (rval);
123925cf1a30Sjl 	}
124025cf1a30Sjl 
124125cf1a30Sjl 	/* Connect two streams */
124225cf1a30Sjl 	rval = oplmsu_plink_serial(dip, msu_lh, &lnk_id);
124325cf1a30Sjl 	if (rval != 0) {
124425cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: attach(dr): "
124525cf1a30Sjl 		    "i_plink failed. errno = %d", rval);
124625cf1a30Sjl 		(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
124725cf1a30Sjl 		ldi_ident_release(msu_li);
124825cf1a30Sjl 		return (rval);
124925cf1a30Sjl 	}
125025cf1a30Sjl 
125125cf1a30Sjl 	rval = oplmsu_set_lpathnum(lnk_id, instance);
125225cf1a30Sjl 	if (rval != 0) {
125325cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: attach(dr): "
125425cf1a30Sjl 		    "Link id %d is not found", lnk_id);
125525cf1a30Sjl 		/* Issue ioctl(I_PUNLINK) */
125625cf1a30Sjl 		(void) ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)lnk_id, FKIOCTL,
125725cf1a30Sjl 		    kcred, &param);
125825cf1a30Sjl 		(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
125925cf1a30Sjl 		ldi_ident_release(msu_li);
126025cf1a30Sjl 		return (rval);
126125cf1a30Sjl 	}
126225cf1a30Sjl 
126325cf1a30Sjl 	/* Add the path */
126425cf1a30Sjl 	rval = oplmsu_config_add(dip);
126525cf1a30Sjl 	if (rval != 0) {
126625cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: attach(dr): "
126725cf1a30Sjl 		    "Failed to add the path. errno = %d", rval);
126825cf1a30Sjl 		/* Issue ioctl(I_PUNLINK) */
126925cf1a30Sjl 		(void) ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)lnk_id, FKIOCTL,
127025cf1a30Sjl 		    kcred, &param);
127125cf1a30Sjl 
127225cf1a30Sjl 		(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
127325cf1a30Sjl 		ldi_ident_release(msu_li);
127425cf1a30Sjl 		return (rval);
127525cf1a30Sjl 	}
127625cf1a30Sjl 
127725cf1a30Sjl 	/* Start to use the path */
127825cf1a30Sjl 	rval = oplmsu_config_start(instance);
127925cf1a30Sjl 	if (rval != 0) {
128025cf1a30Sjl 		struct msu_path	*mpath;
128125cf1a30Sjl 		struct msu_dev	*mdev;
128225cf1a30Sjl 
128325cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: attach(dr): "
128425cf1a30Sjl 		    "Failed to start the path. errno = %d", rval);
128525cf1a30Sjl 
128625cf1a30Sjl 		len = sizeof (struct msu_path) + sizeof (struct msu_dev);
128725cf1a30Sjl 		mpath = (struct msu_path *)kmem_zalloc((size_t)len, KM_SLEEP);
128825cf1a30Sjl 		mpath->num = 1;
128925cf1a30Sjl 		mdev = (struct msu_dev *)(mpath + 1);
129025cf1a30Sjl 		mdev->dip = dip;
129125cf1a30Sjl 
129225cf1a30Sjl 		/* Delete the path */
129325cf1a30Sjl 		if ((oplmsu_config_del(mpath)) == 0) {
129425cf1a30Sjl 			/* Issue ioctl(I_PUNLINK) */
129525cf1a30Sjl 			(void) ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)lnk_id,
129625cf1a30Sjl 			    FKIOCTL, kcred, &param);
129725cf1a30Sjl 		}
129825cf1a30Sjl 		kmem_free(mpath, (size_t)len);
129925cf1a30Sjl 	}
130025cf1a30Sjl 
130125cf1a30Sjl 	/* Close oplmsu */
130225cf1a30Sjl 	(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
130325cf1a30Sjl 	ldi_ident_release(msu_li);
130425cf1a30Sjl 	return (rval);
130525cf1a30Sjl }
130625cf1a30Sjl 
130725cf1a30Sjl int
oplmsu_dr_detach(dev_info_t * dip)130825cf1a30Sjl oplmsu_dr_detach(dev_info_t *dip)
130925cf1a30Sjl {
131025cf1a30Sjl 	ldi_ident_t	msu_li = NULL;
131125cf1a30Sjl 	ldi_handle_t	msu_lh = NULL;
131225cf1a30Sjl 	struct msu_path	*mpath;
131325cf1a30Sjl 	struct msu_dev	*mdev;
131425cf1a30Sjl 	upath_t		*upath;
131525cf1a30Sjl 	lpath_t		*lpath;
131625cf1a30Sjl 	int		len;
131725cf1a30Sjl 	int		instance;
131825cf1a30Sjl 	int		count = 0;
131925cf1a30Sjl 	int		param = 0;
132025cf1a30Sjl 	int		status;
132125cf1a30Sjl 	int		rval;
132225cf1a30Sjl 
132325cf1a30Sjl 	/* Get instance for serial */
132425cf1a30Sjl 	instance = ddi_get_instance(dip);
132525cf1a30Sjl 
132625cf1a30Sjl 	rw_enter(&oplmsu_uinst->lock, RW_WRITER);
132725cf1a30Sjl 	mutex_enter(&oplmsu_uinst->u_lock);
132825cf1a30Sjl 
132925cf1a30Sjl 	/* Get current number of paths */
133025cf1a30Sjl 	oplmsu_uinst->path_num = oplmsu_get_pathnum();
133125cf1a30Sjl 
133225cf1a30Sjl 	rval = FAILURE;
133325cf1a30Sjl 
133425cf1a30Sjl 	/* Check specified upath_t */
133525cf1a30Sjl 	upath = oplmsu_uinst->first_upath;
133625cf1a30Sjl 	while (upath) {
133725cf1a30Sjl 		if (instance == upath->path_no) {
133825cf1a30Sjl 			/* Save status of specified path */
133925cf1a30Sjl 			status = upath->status;
134025cf1a30Sjl 			rval = SUCCESS;
134125cf1a30Sjl 		}
134225cf1a30Sjl 		upath = upath->u_next;
134325cf1a30Sjl 		count += 1;
134425cf1a30Sjl 	}
134525cf1a30Sjl 	mutex_exit(&oplmsu_uinst->u_lock);
134625cf1a30Sjl 	rw_exit(&oplmsu_uinst->lock);
134725cf1a30Sjl 
134825cf1a30Sjl 	if (rval == FAILURE) {
134925cf1a30Sjl 		if (count <= 1) {
135025cf1a30Sjl 			cmn_err(CE_WARN, "oplmsu: detach(dr): "
135125cf1a30Sjl 			    "Instance %d is last path", instance);
135225cf1a30Sjl 		} else {
135325cf1a30Sjl 			cmn_err(CE_WARN, "oplmsu: detach(dr): "
135425cf1a30Sjl 			    "Instance %d doesn't find", instance);
135525cf1a30Sjl 		}
135625cf1a30Sjl 		return (EINVAL);
135725cf1a30Sjl 	}
135825cf1a30Sjl 
135925cf1a30Sjl 	/* Check status of specified path */
136025cf1a30Sjl 	if ((status == MSU_PSTAT_ACTIVE) || (status == MSU_PSTAT_STANDBY)) {
136125cf1a30Sjl 		/* Stop to use the path */
136225cf1a30Sjl 		rval = oplmsu_config_stop(instance);
136325cf1a30Sjl 		if (rval != 0) {
136425cf1a30Sjl 			cmn_err(CE_WARN, "oplmsu: detach(dr): "
136525cf1a30Sjl 			    "Failed to stop the path. errno = %d", rval);
136625cf1a30Sjl 			return (rval);
136725cf1a30Sjl 		}
136825cf1a30Sjl 	}
136925cf1a30Sjl 
137025cf1a30Sjl 	/* Prepare to unlink the path */
137125cf1a30Sjl 	rval = oplmsu_config_disc(instance);
137225cf1a30Sjl 	if (rval != 0) {
137325cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: detach(dr): "
137425cf1a30Sjl 		    "Failed to disconnect the path. errno = %d", rval);
137525cf1a30Sjl 		return (rval);
137625cf1a30Sjl 	}
137725cf1a30Sjl 
137825cf1a30Sjl 	rw_enter(&oplmsu_uinst->lock, RW_READER);
137925cf1a30Sjl 	mutex_enter(&oplmsu_uinst->l_lock);
138025cf1a30Sjl 	lpath = oplmsu_uinst->first_lpath;
138125cf1a30Sjl 	while (lpath) {
138225cf1a30Sjl 		if (lpath->path_no == instance) { /* Get link ID */
138325cf1a30Sjl 			break;
138425cf1a30Sjl 		}
138525cf1a30Sjl 		lpath = lpath->l_next;
138625cf1a30Sjl 	}
138725cf1a30Sjl 	mutex_exit(&oplmsu_uinst->l_lock);
138825cf1a30Sjl 	rw_exit(&oplmsu_uinst->lock);
138925cf1a30Sjl 
139025cf1a30Sjl 	if (lpath == NULL) {
139125cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: detach(dr): Can not find link ID");
139225cf1a30Sjl 		return (EINVAL);
139325cf1a30Sjl 	}
139425cf1a30Sjl 
139525cf1a30Sjl 	/* Open oplmsu */
139625cf1a30Sjl 	rval = oplmsu_open_msu(oplmsu_uinst->msu_dip, &msu_li, &msu_lh);
139725cf1a30Sjl 	if (rval != 0) {
139825cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: detach(dr): "
139925cf1a30Sjl 		    "msu open failed. errno = %d", rval);
140025cf1a30Sjl 		return (rval);
140125cf1a30Sjl 	}
140225cf1a30Sjl 
140325cf1a30Sjl 	/* Issue ioctl(I_PUNLINK) */
140425cf1a30Sjl 	rval = ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)lpath->link_id, FKIOCTL,
140525cf1a30Sjl 	    kcred, &param);
140625cf1a30Sjl 	if (rval != 0) {
140725cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: detach(dr): "
140825cf1a30Sjl 		    "ioctl(I_PUNLINK) failed. errno = %d", rval);
140925cf1a30Sjl 		(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
141025cf1a30Sjl 		ldi_ident_release(msu_li);
141125cf1a30Sjl 		return (rval);
141225cf1a30Sjl 	}
141325cf1a30Sjl 
141425cf1a30Sjl 	/* Close oplmsu(meta node) */
141525cf1a30Sjl 	(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
141625cf1a30Sjl 	ldi_ident_release(msu_li);
141725cf1a30Sjl 
141825cf1a30Sjl 	len = sizeof (struct msu_path) + sizeof (struct msu_dev);
141925cf1a30Sjl 	mpath = (struct msu_path *)kmem_zalloc((size_t)len, KM_SLEEP);
142025cf1a30Sjl 	mpath->num = 1;
142125cf1a30Sjl 	mdev = (struct msu_dev *)(mpath + 1);
142225cf1a30Sjl 	mdev->dip = dip;
142325cf1a30Sjl 
142425cf1a30Sjl 	/* Delete the path */
142525cf1a30Sjl 	rval = oplmsu_config_del(mpath);
142625cf1a30Sjl 	if (rval != 0) {
142725cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: detach(dr): "
142825cf1a30Sjl 		    "Failed to delete the path. errno = %d", rval);
142925cf1a30Sjl 	}
143025cf1a30Sjl 
143125cf1a30Sjl 	kmem_free(mpath, (size_t)len);
143225cf1a30Sjl 	return (rval);
143325cf1a30Sjl }
143425cf1a30Sjl 
143525cf1a30Sjl /*
143625cf1a30Sjl  * The ebus and the serial device path under a given CMU_CH chip
143725cf1a30Sjl  * is expected to be always at the same address. So, it is safe
143825cf1a30Sjl  * to hard-code the pathnames as below.
143925cf1a30Sjl  */
144025cf1a30Sjl #define	EBUS_PATH		"ebus@1"
144125cf1a30Sjl #define	SERIAL_PATH		"serial@14,400000"
144225cf1a30Sjl #define	EBUS_SERIAL_PATH	("/" EBUS_PATH "/" SERIAL_PATH)
144325cf1a30Sjl 
144425cf1a30Sjl /*
144525cf1a30Sjl  * Given the CMU_CH dip, find the serial device dip.
144625cf1a30Sjl  */
144725cf1a30Sjl dev_info_t *
oplmsu_find_ser_dip(dev_info_t * cmuch_dip)144825cf1a30Sjl oplmsu_find_ser_dip(dev_info_t *cmuch_dip)
144925cf1a30Sjl {
145025cf1a30Sjl 	dev_info_t	*ebus_dip;
145125cf1a30Sjl 	dev_info_t	*ser_dip = NULL;
145225cf1a30Sjl 
1453*3fe80ca4SDan Cross 	ndi_devi_enter(cmuch_dip);
145425cf1a30Sjl 	ebus_dip = ndi_devi_findchild(cmuch_dip, EBUS_PATH);
145525cf1a30Sjl 
145625cf1a30Sjl 	DBG_PRINT((CE_NOTE, "oplmsu: find-serial-dip: "
145707d06da5SSurya Prakki 	    "ebus_dip = %p", (void *)ebus_dip));
145825cf1a30Sjl 
145925cf1a30Sjl 	if (ebus_dip != NULL) {
1460*3fe80ca4SDan Cross 		ndi_devi_enter(ebus_dip);
146125cf1a30Sjl 		ser_dip = ndi_devi_findchild(ebus_dip, SERIAL_PATH);
146225cf1a30Sjl 
146325cf1a30Sjl 		DBG_PRINT((CE_NOTE, "oplmsu: find-serial-dip: "
146407d06da5SSurya Prakki 		    "ser_dip = %p", (void *)ser_dip));
1465*3fe80ca4SDan Cross 		ndi_devi_exit(ebus_dip);
146625cf1a30Sjl 	}
1467*3fe80ca4SDan Cross 	ndi_devi_exit(cmuch_dip);
146825cf1a30Sjl 	return (ser_dip);
146925cf1a30Sjl }
147025cf1a30Sjl 
147125cf1a30Sjl /*
147225cf1a30Sjl  * Find all console related serial devices.
147325cf1a30Sjl  */
147425cf1a30Sjl int
oplmsu_find_serial(ser_devl_t ** ser_dl)147525cf1a30Sjl oplmsu_find_serial(ser_devl_t **ser_dl)
147625cf1a30Sjl {
147725cf1a30Sjl 	dev_info_t	*root_dip;
147825cf1a30Sjl 	dev_info_t	*cmuch_dip;
147925cf1a30Sjl 	dev_info_t	*dip;
148025cf1a30Sjl 	ser_devl_t	*wrk_ser_dl;
148125cf1a30Sjl 	int		count = 0;
148225cf1a30Sjl 	char		pathname[MSU_PATHNAME_SIZE];
148325cf1a30Sjl 	dev_t		devt;
148425cf1a30Sjl 	char		*namep;
148525cf1a30Sjl 
148625cf1a30Sjl 	root_dip = ddi_root_node();
1487*3fe80ca4SDan Cross 	ndi_devi_enter(root_dip);
148825cf1a30Sjl 	cmuch_dip = ddi_get_child(root_dip);
148925cf1a30Sjl 
149025cf1a30Sjl 	while (cmuch_dip != NULL) {
149125cf1a30Sjl 		namep = ddi_binding_name(cmuch_dip);	/* Get binding name */
149225cf1a30Sjl 		if (namep == NULL) {
149325cf1a30Sjl 			cmuch_dip = ddi_get_next_sibling(cmuch_dip);
149425cf1a30Sjl 			continue;
149525cf1a30Sjl 		}
149625cf1a30Sjl 
149725cf1a30Sjl 		DBG_PRINT((CE_NOTE, "oplmsu: find-serial: name => %s", namep));
149825cf1a30Sjl 
149925cf1a30Sjl 		if ((strcmp(namep, MSU_CMUCH_FF) != 0) &&
150025cf1a30Sjl 		    (strcmp(namep, MSU_CMUCH_DC) != 0)) {
150125cf1a30Sjl #ifdef DEBUG
150225cf1a30Sjl 			if (strcmp(namep, MSU_CMUCH_DBG) != 0) {
150325cf1a30Sjl 				cmuch_dip = ddi_get_next_sibling(cmuch_dip);
150425cf1a30Sjl 				continue;
150525cf1a30Sjl 			}
150625cf1a30Sjl #else
150725cf1a30Sjl 			cmuch_dip = ddi_get_next_sibling(cmuch_dip);
150825cf1a30Sjl 			continue;
150925cf1a30Sjl #endif
151025cf1a30Sjl 		}
151125cf1a30Sjl 
15125b20806aSraghuram 		/*
15135b20806aSraghuram 		 * Online the cmuch_dip so that its in the right state
15145b20806aSraghuram 		 * to get the complete path, that is both name and address.
15155b20806aSraghuram 		 */
15165b20806aSraghuram 		(void) ndi_devi_online(cmuch_dip, 0);
151725cf1a30Sjl 		(void) ddi_pathname(cmuch_dip, pathname);
151825cf1a30Sjl 		DBG_PRINT((CE_NOTE,
151925cf1a30Sjl 		    "oplmsu: find-serial: cmu-ch path => %s", pathname));
152025cf1a30Sjl 		(void) strcat(pathname, EBUS_SERIAL_PATH);
152125cf1a30Sjl 
152225cf1a30Sjl 		/*
152325cf1a30Sjl 		 * Call ddi_pathname_to_dev_t to forceload and attach
152425cf1a30Sjl 		 * the required drivers.
152525cf1a30Sjl 		 */
152625cf1a30Sjl 		devt = ddi_pathname_to_dev_t(pathname);
152725cf1a30Sjl 		DBG_PRINT((CE_NOTE, "oplmsu: find-serial: serial device "
152825cf1a30Sjl 		    "dev_t = %lx", devt));
152925cf1a30Sjl 		if ((devt != NODEV) &&
153025cf1a30Sjl 		    ((dip = oplmsu_find_ser_dip(cmuch_dip)) != NULL)) {
153125cf1a30Sjl 			wrk_ser_dl = (ser_devl_t *)
153225cf1a30Sjl 			    kmem_zalloc(sizeof (ser_devl_t), KM_SLEEP);
153325cf1a30Sjl 			wrk_ser_dl->dip = dip;
153425cf1a30Sjl 			count += 1;
153525cf1a30Sjl 
153625cf1a30Sjl 			if (*ser_dl != NULL) {
153725cf1a30Sjl 				wrk_ser_dl->next = *ser_dl;
153825cf1a30Sjl 			}
153925cf1a30Sjl 			*ser_dl = wrk_ser_dl;
154025cf1a30Sjl 		}
154125cf1a30Sjl 		cmuch_dip = ddi_get_next_sibling(cmuch_dip);
154225cf1a30Sjl 	}
1543*3fe80ca4SDan Cross 	ndi_devi_exit(root_dip);
154425cf1a30Sjl 	return (count);
154525cf1a30Sjl }
154625cf1a30Sjl 
154725cf1a30Sjl /* Configure STREAM */
154825cf1a30Sjl void
oplmsu_conf_stream(uinst_t * msu_uinst)154925cf1a30Sjl oplmsu_conf_stream(uinst_t *msu_uinst)
155025cf1a30Sjl {
155125cf1a30Sjl 	ldi_ident_t	msu_li = NULL;
155225cf1a30Sjl 	ldi_handle_t	msu_lh = NULL;
155325cf1a30Sjl 	struct msu_path	*mpath;
155425cf1a30Sjl 	struct msu_dev	*mdev;
155525cf1a30Sjl 	ser_devl_t	*ser_dl = NULL, *next_ser_dl;
155625cf1a30Sjl 	int		*plink_id;
155725cf1a30Sjl 	int		size;
155825cf1a30Sjl 	int		i;
155925cf1a30Sjl 	int		param;
156025cf1a30Sjl 	int		connected = 0;
156125cf1a30Sjl 	int		devcnt = 0;
156225cf1a30Sjl 	int		rval;
156325cf1a30Sjl 
156425cf1a30Sjl 	DBG_PRINT((CE_NOTE,
156525cf1a30Sjl 	    "oplmsu: conf-stream: stream configuration start!"));
156625cf1a30Sjl 
156725cf1a30Sjl 	/* Find serial devices */
156825cf1a30Sjl 	devcnt = oplmsu_find_serial(&ser_dl);
156925cf1a30Sjl 	if ((devcnt == 0) || (ser_dl == NULL)) {
157025cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: conf-stream: "
157125cf1a30Sjl 		    "Discovered serial device = %d", devcnt);
157225cf1a30Sjl 		return;
157325cf1a30Sjl 	}
157425cf1a30Sjl 
157525cf1a30Sjl 	/* Open oplmsu */
157625cf1a30Sjl 	rval = oplmsu_open_msu(msu_uinst->msu_dip, &msu_li, &msu_lh);
157725cf1a30Sjl 	if (rval != 0) {
157825cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: conf-stream: "
157925cf1a30Sjl 		    "msu open failed. errno = %d", rval);
158025cf1a30Sjl 		return;
158125cf1a30Sjl 	}
158225cf1a30Sjl 
158325cf1a30Sjl 	size = (sizeof (struct msu_path) + (sizeof (struct msu_dev) * devcnt));
158425cf1a30Sjl 	mpath = (struct msu_path *)kmem_zalloc((size_t)size, KM_SLEEP);
158525cf1a30Sjl 	plink_id = (int *)kmem_zalloc((sizeof (int) * devcnt), KM_SLEEP);
158625cf1a30Sjl 
158725cf1a30Sjl 	mdev = (struct msu_dev *)(mpath + 1);
158825cf1a30Sjl 	for (i = 0; i < devcnt; i++) {
158925cf1a30Sjl 		/* Connect two streams */
159025cf1a30Sjl 		rval = oplmsu_plink_serial(ser_dl->dip, msu_lh, &plink_id[i]);
159125cf1a30Sjl 		if (rval != 0) {
159225cf1a30Sjl 			cmn_err(CE_WARN, "oplmsu: conf-stream: "
159325cf1a30Sjl 			    "i_plink failed. errno = %d", rval);
159425cf1a30Sjl 			next_ser_dl = ser_dl->next;
159525cf1a30Sjl 			kmem_free(ser_dl, sizeof (ser_devl_t));
159625cf1a30Sjl 			ser_dl = next_ser_dl;
159725cf1a30Sjl 			continue;
159825cf1a30Sjl 		}
159925cf1a30Sjl 
160025cf1a30Sjl 		rval = oplmsu_set_lpathnum(plink_id[i],
160125cf1a30Sjl 		    ddi_get_instance(ser_dl->dip));
160225cf1a30Sjl 		if (rval != 0) {
160325cf1a30Sjl 			cmn_err(CE_WARN, "oplmsu: conf-stream: "
160425cf1a30Sjl 			    "Link id %d is not found", plink_id[i]);
160525cf1a30Sjl 			/* Issue ioctl(I_PUNLINK) */
160625cf1a30Sjl 			(void) ldi_ioctl(msu_lh, I_PUNLINK,
160725cf1a30Sjl 			    (intptr_t)plink_id[i], FKIOCTL, kcred, &param);
160825cf1a30Sjl 			next_ser_dl = ser_dl->next;
160925cf1a30Sjl 			kmem_free(ser_dl, sizeof (ser_devl_t));
161025cf1a30Sjl 			ser_dl = next_ser_dl;
161125cf1a30Sjl 			continue;
161225cf1a30Sjl 		}
161325cf1a30Sjl 
161425cf1a30Sjl 		mdev->dip = ser_dl->dip;
161525cf1a30Sjl 		next_ser_dl = ser_dl->next;
161625cf1a30Sjl 		kmem_free(ser_dl, sizeof (ser_devl_t));
161725cf1a30Sjl 		ser_dl = next_ser_dl;
161825cf1a30Sjl 
161925cf1a30Sjl 		mdev++;
162025cf1a30Sjl 		connected++;
162125cf1a30Sjl 	}
162225cf1a30Sjl 
162325cf1a30Sjl 	if (connected == 0) {
162425cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: conf-stream: "
162525cf1a30Sjl 		    "Connected paths = %d", connected);
162625cf1a30Sjl 		(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
162725cf1a30Sjl 		ldi_ident_release(msu_li);
162825cf1a30Sjl 		kmem_free(plink_id, (sizeof (int) * devcnt));
162925cf1a30Sjl 		kmem_free(mpath, size);
163025cf1a30Sjl 		return;
163125cf1a30Sjl 	}
163225cf1a30Sjl 
163325cf1a30Sjl 	/* Setup all structure */
163425cf1a30Sjl 	mpath->num = connected;
163525cf1a30Sjl 	rval = oplmsu_config_new(mpath);
163625cf1a30Sjl 	if (rval != 0) {
163725cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: conf-stream: "
163825cf1a30Sjl 		    "Failed to create all paths. errno = %d", rval);
163925cf1a30Sjl 		oplmsu_unlinks(msu_lh, plink_id, devcnt);
164025cf1a30Sjl 		(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
164125cf1a30Sjl 		ldi_ident_release(msu_li);
164225cf1a30Sjl 		kmem_free(plink_id, (sizeof (int) * devcnt));
164325cf1a30Sjl 		kmem_free(mpath, size);
164425cf1a30Sjl 		return;
164525cf1a30Sjl 	}
164625cf1a30Sjl 
164725cf1a30Sjl 	/* Start to use all paths */
164825cf1a30Sjl 	rval = oplmsu_config_start(MSU_PATH_ALL);
164925cf1a30Sjl 	if (rval != 0) {
165025cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: conf-stream: "
165125cf1a30Sjl 		    "Failed to start all paths. errno = %d", rval);
165225cf1a30Sjl 
165325cf1a30Sjl 		/* Delete the path */
165425cf1a30Sjl 		rval = oplmsu_config_del(mpath);
165525cf1a30Sjl 		if (rval == 0) {
165625cf1a30Sjl 			oplmsu_unlinks(msu_lh, plink_id, devcnt);
165725cf1a30Sjl 		}
165825cf1a30Sjl 	}
165925cf1a30Sjl 
166025cf1a30Sjl 	(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
166125cf1a30Sjl 	ldi_ident_release(msu_li);
166225cf1a30Sjl 	kmem_free(plink_id, (sizeof (int) * devcnt));
166325cf1a30Sjl 	kmem_free(mpath, size);
166425cf1a30Sjl 
166525cf1a30Sjl 	DBG_PRINT((CE_NOTE, "oplmsu: conf-stream: stream configuration end!"));
166625cf1a30Sjl }
166725cf1a30Sjl 
166825cf1a30Sjl void
oplmsu_unlinks(ldi_handle_t msu_lh,int * plink_id,int devcnt)166925cf1a30Sjl oplmsu_unlinks(ldi_handle_t msu_lh, int *plink_id, int devcnt)
167025cf1a30Sjl {
167125cf1a30Sjl 	int	i;
167225cf1a30Sjl 	int	param = 0;
167325cf1a30Sjl 
167425cf1a30Sjl 	for (i = 0; i < devcnt; i++) {
167525cf1a30Sjl 		if (plink_id[i] == 0) {
167625cf1a30Sjl 			continue;
167725cf1a30Sjl 		}
167825cf1a30Sjl 
167925cf1a30Sjl 		/* Issue ioctl(I_PUNLINK) */
168025cf1a30Sjl 		(void) ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)plink_id[i],
168125cf1a30Sjl 		    FKIOCTL, kcred, &param);
168225cf1a30Sjl 	}
168325cf1a30Sjl }
168425cf1a30Sjl 
168525cf1a30Sjl void
oplmsu_setup(uinst_t * msu_uinst)168625cf1a30Sjl oplmsu_setup(uinst_t *msu_uinst)
168725cf1a30Sjl {
168825cf1a30Sjl 
168925cf1a30Sjl 	DBG_PRINT((CE_NOTE, "oplmsu: setup: Background thread start!"));
169025cf1a30Sjl 
169125cf1a30Sjl 	mutex_enter(&oplmsu_bthrd_excl);
169225cf1a30Sjl 	if (oplmsu_conf_st == MSU_CONFIGURING) {
169325cf1a30Sjl 		mutex_exit(&oplmsu_bthrd_excl);
169425cf1a30Sjl 		oplmsu_conf_stream(msu_uinst);	/* Configure stream */
169525cf1a30Sjl 		mutex_enter(&oplmsu_bthrd_excl);
169625cf1a30Sjl 		oplmsu_conf_st = MSU_CONFIGURED;
169725cf1a30Sjl 		cv_broadcast(&oplmsu_conf_cv);	/* Wake up from cv_wait_sig() */
169825cf1a30Sjl 	}
169925cf1a30Sjl 
170025cf1a30Sjl 	if (oplmsu_bthrd_id != NULL) {
170125cf1a30Sjl 		oplmsu_bthrd_id = NULL;
170225cf1a30Sjl 	}
170325cf1a30Sjl 	mutex_exit(&oplmsu_bthrd_excl);
170425cf1a30Sjl 
170525cf1a30Sjl 	DBG_PRINT((CE_NOTE, "oplmsu: setup: Background thread end!"));
170625cf1a30Sjl 
170725cf1a30Sjl 	thread_exit();
170825cf1a30Sjl }
170925cf1a30Sjl 
171025cf1a30Sjl int
oplmsu_create_upath(dev_info_t * dip)171125cf1a30Sjl oplmsu_create_upath(dev_info_t *dip)
171225cf1a30Sjl {
171325cf1a30Sjl 	upath_t		*upath;
171425cf1a30Sjl 	lpath_t		*lpath;
171525cf1a30Sjl 	dev_info_t	*cmuch_dip;
171625cf1a30Sjl 	int		instance;
171725cf1a30Sjl 	int		lsb;
171825cf1a30Sjl 
171925cf1a30Sjl 	cmuch_dip = ddi_get_parent(ddi_get_parent(dip));
172025cf1a30Sjl 	lsb = ddi_prop_get_int(DDI_DEV_T_ANY, cmuch_dip, 0, MSU_BOARD_PROP,
172125cf1a30Sjl 	    FAILURE);
172225cf1a30Sjl 	if (lsb == FAILURE) {
172325cf1a30Sjl 		return (lsb);
172425cf1a30Sjl 	}
172525cf1a30Sjl 
172625cf1a30Sjl 	instance = ddi_get_instance(dip);
172725cf1a30Sjl 
172825cf1a30Sjl 	mutex_enter(&oplmsu_uinst->l_lock);
172925cf1a30Sjl 	lpath = oplmsu_uinst->first_lpath;
173025cf1a30Sjl 	while (lpath) {
173125cf1a30Sjl 		if (lpath->path_no == instance) {
173225cf1a30Sjl 			break;
173325cf1a30Sjl 		}
173425cf1a30Sjl 		lpath = lpath->l_next;
173525cf1a30Sjl 	}
173625cf1a30Sjl 
173725cf1a30Sjl 	if (lpath == NULL) {
173825cf1a30Sjl 		mutex_exit(&oplmsu_uinst->l_lock);
173925cf1a30Sjl 		return (ENODEV);
174025cf1a30Sjl 	}
174125cf1a30Sjl 
174225cf1a30Sjl 	upath = (upath_t *)kmem_zalloc(sizeof (upath_t), KM_SLEEP);
174325cf1a30Sjl 
174425cf1a30Sjl 	/*
174525cf1a30Sjl 	 * Initialize members of upath_t
174625cf1a30Sjl 	 */
174725cf1a30Sjl 
174825cf1a30Sjl 	upath->path_no = instance;
174925cf1a30Sjl 	upath->lpath = lpath;
175025cf1a30Sjl 	upath->ser_devcb.dip = dip;
175125cf1a30Sjl 	upath->ser_devcb.lsb = lsb;
175225cf1a30Sjl 	oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STOP, MSU_PSTAT_EMPTY,
175325cf1a30Sjl 	    MSU_STOP);
175425cf1a30Sjl 
175525cf1a30Sjl 	lpath->src_upath = NULL;
175625cf1a30Sjl 	lpath->status = MSU_EXT_NOTUSED;
175725cf1a30Sjl 	mutex_exit(&oplmsu_uinst->l_lock);
175825cf1a30Sjl 
175925cf1a30Sjl 	oplmsu_link_upath(upath);
176025cf1a30Sjl 	return (SUCCESS);
176125cf1a30Sjl }
176225cf1a30Sjl 
176325cf1a30Sjl /* Setup new upper instance structure */
176425cf1a30Sjl int
oplmsu_config_new(struct msu_path * mpath)176525cf1a30Sjl oplmsu_config_new(struct msu_path *mpath)
176625cf1a30Sjl {
176725cf1a30Sjl 	struct msu_dev	*mdev;
176825cf1a30Sjl 	int		i;
176925cf1a30Sjl 	int		rval = SUCCESS;
177025cf1a30Sjl 
177125cf1a30Sjl 	DBG_PRINT((CE_NOTE, "oplmsu: conf-new: config_new() called"));
177225cf1a30Sjl 	ASSERT(mpath);
177325cf1a30Sjl 
177425cf1a30Sjl 	if (mpath->num == 0) {
177525cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: conf-new: "
177625cf1a30Sjl 		    "Number of paths = %d", mpath->num);
177725cf1a30Sjl 		return (EINVAL);
177825cf1a30Sjl 	}
177925cf1a30Sjl 
178025cf1a30Sjl 	rw_enter(&oplmsu_uinst->lock, RW_WRITER);
178125cf1a30Sjl 
178225cf1a30Sjl 	mutex_enter(&oplmsu_uinst->l_lock);
178325cf1a30Sjl 	rval = oplmsu_check_lpath_usable();
178425cf1a30Sjl 	mutex_exit(&oplmsu_uinst->l_lock);
178525cf1a30Sjl 
178625cf1a30Sjl 	if (rval == BUSY) { /* Check whether Lower path is usable */
178725cf1a30Sjl 		rw_exit(&oplmsu_uinst->lock);
178825cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: conf-new: "
178925cf1a30Sjl 		    "Other processing is using this device");
179025cf1a30Sjl 		return (EBUSY);
179125cf1a30Sjl 	}
179225cf1a30Sjl 
179325cf1a30Sjl 	/*
179425cf1a30Sjl 	 * Because the OPLMSU instance already exists when the upper path
179525cf1a30Sjl 	 * table exists, the configure_new processing cannot be done.
179625cf1a30Sjl 	 */
179725cf1a30Sjl 
179825cf1a30Sjl 	mutex_enter(&oplmsu_uinst->u_lock);
179925cf1a30Sjl 
180025cf1a30Sjl 	if ((oplmsu_uinst->first_upath != NULL) ||
180125cf1a30Sjl 	    (oplmsu_uinst->last_upath != NULL)) {
180225cf1a30Sjl 		mutex_exit(&oplmsu_uinst->u_lock);
180325cf1a30Sjl 		rw_exit(&oplmsu_uinst->lock);
180425cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: conf-new: upath_t already exist");
180525cf1a30Sjl 		return (EINVAL);
180625cf1a30Sjl 	}
180725cf1a30Sjl 
180825cf1a30Sjl 	/*
180925cf1a30Sjl 	 * Because the config_new processing has already been done
181025cf1a30Sjl 	 * if oplmsu_uinst->path_num isn't -1, this processing cannot be
181125cf1a30Sjl 	 * continued.
181225cf1a30Sjl 	 */
181325cf1a30Sjl 
181425cf1a30Sjl 	if (oplmsu_uinst->path_num != UNDEFINED) {
181525cf1a30Sjl 		mutex_exit(&oplmsu_uinst->u_lock);
181625cf1a30Sjl 		rw_exit(&oplmsu_uinst->lock);
181725cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: conf-new: "
181825cf1a30Sjl 		    "conf-new processing has already been completed");
181925cf1a30Sjl 		return (EINVAL);
182025cf1a30Sjl 	}
182125cf1a30Sjl 
182225cf1a30Sjl 	/*
182325cf1a30Sjl 	 * Only the number of specified paths makes the upper path
182425cf1a30Sjl 	 * information tables.
182525cf1a30Sjl 	 */
182625cf1a30Sjl 
182725cf1a30Sjl 	mdev = (struct msu_dev *)(mpath + 1);
182825cf1a30Sjl 	for (i = 0; i < mpath->num; i++) {
182925cf1a30Sjl 		/*
183025cf1a30Sjl 		 * Associate upper path information table with lower path
183125cf1a30Sjl 		 * information table.
183225cf1a30Sjl 		 *
183325cf1a30Sjl 		 * If the upper path information table and the lower path
183425cf1a30Sjl 		 * information table cannot be associated, the link list of
183525cf1a30Sjl 		 * the upper path information table is released.
183625cf1a30Sjl 		 */
183725cf1a30Sjl 		rval = oplmsu_create_upath(mdev->dip);
183825cf1a30Sjl 		if (rval != SUCCESS) {
183925cf1a30Sjl 			oplmsu_delete_upath_info();
184025cf1a30Sjl 			mutex_exit(&oplmsu_uinst->u_lock);
184125cf1a30Sjl 			rw_exit(&oplmsu_uinst->lock);
184225cf1a30Sjl 			cmn_err(CE_WARN, "oplmsu: conf-new: "
184325cf1a30Sjl 			    "Failed to create upath %d", rval);
184425cf1a30Sjl 			return (rval);
184525cf1a30Sjl 		}
184625cf1a30Sjl 
184725cf1a30Sjl 		mdev++;
184825cf1a30Sjl 	}
184925cf1a30Sjl 
185025cf1a30Sjl 	/*
185125cf1a30Sjl 	 * Setup members of uinst_t
185225cf1a30Sjl 	 */
185325cf1a30Sjl 
185425cf1a30Sjl 	oplmsu_uinst->inst_status = oplmsu_get_inst_status();
185525cf1a30Sjl 	oplmsu_uinst->path_num = mpath->num;
185625cf1a30Sjl 	oplmsu_uinst->lower_queue = NULL;
185725cf1a30Sjl 	mutex_exit(&oplmsu_uinst->u_lock);
185825cf1a30Sjl 	rw_exit(&oplmsu_uinst->lock);
185925cf1a30Sjl 	return (SUCCESS);
186025cf1a30Sjl }
186125cf1a30Sjl 
186225cf1a30Sjl /* Add path information */
186325cf1a30Sjl int
oplmsu_config_add(dev_info_t * dip)186425cf1a30Sjl oplmsu_config_add(dev_info_t *dip)
186525cf1a30Sjl {
186625cf1a30Sjl 	upath_t	*upath;
186725cf1a30Sjl 	int	instance;
186825cf1a30Sjl 	int	rval = SUCCESS;
186925cf1a30Sjl 
187025cf1a30Sjl 	DBG_PRINT((CE_NOTE, "oplmsu: conf-add: config_add() called"));
187125cf1a30Sjl 	ASSERT(dip);
187225cf1a30Sjl 
187325cf1a30Sjl 	instance = ddi_get_instance(dip);
187425cf1a30Sjl 	rw_enter(&oplmsu_uinst->lock, RW_WRITER);
187525cf1a30Sjl 
187625cf1a30Sjl 	if (oplmsu_uinst->path_num == UNDEFINED) {
187725cf1a30Sjl 		rw_exit(&oplmsu_uinst->lock);
187825cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: conf-add: "
187925cf1a30Sjl 		    "conf-new processing has not been completed yet");
188025cf1a30Sjl 		return (EINVAL);
188125cf1a30Sjl 	}
188225cf1a30Sjl 
188325cf1a30Sjl 	mutex_enter(&oplmsu_uinst->u_lock);
188425cf1a30Sjl 	upath = oplmsu_search_upath_info(instance);
188525cf1a30Sjl 	if (upath != NULL) {
188625cf1a30Sjl 		mutex_exit(&oplmsu_uinst->u_lock);
188725cf1a30Sjl 		rw_exit(&oplmsu_uinst->lock);
188825cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: conf-add: "
188925cf1a30Sjl 		    "Proper upath_t doesn't find");
189025cf1a30Sjl 		return (EINVAL);
189125cf1a30Sjl 	}
189225cf1a30Sjl 
189325cf1a30Sjl 	rval = oplmsu_create_upath(dip);
189425cf1a30Sjl 	if (rval != SUCCESS) {
189525cf1a30Sjl 		mutex_exit(&oplmsu_uinst->u_lock);
189625cf1a30Sjl 		rw_exit(&oplmsu_uinst->lock);
189725cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: conf-add: "
189825cf1a30Sjl 		    "Failed to create upath %d", rval);
189925cf1a30Sjl 		return (rval);
190025cf1a30Sjl 	}
190125cf1a30Sjl 
190225cf1a30Sjl 	oplmsu_uinst->inst_status = oplmsu_get_inst_status();
190325cf1a30Sjl 	oplmsu_uinst->path_num = oplmsu_get_pathnum();
190425cf1a30Sjl 	mutex_exit(&oplmsu_uinst->u_lock);
190525cf1a30Sjl 	rw_exit(&oplmsu_uinst->lock);
190625cf1a30Sjl 	return (SUCCESS);
190725cf1a30Sjl }
190825cf1a30Sjl 
190925cf1a30Sjl /* Delete each path information */
191025cf1a30Sjl int
oplmsu_config_del(struct msu_path * mpath)191125cf1a30Sjl oplmsu_config_del(struct msu_path *mpath)
191225cf1a30Sjl {
191325cf1a30Sjl 	struct msu_dev	*mdev;
191425cf1a30Sjl 	upath_t		*upath;
191525cf1a30Sjl 	lpath_t		*lpath;
191625cf1a30Sjl 	int		rval = SUCCESS;
191725cf1a30Sjl 	int		use_flag;
191825cf1a30Sjl 	int		i;
191925cf1a30Sjl 
192025cf1a30Sjl 	DBG_PRINT((CE_NOTE, "oplmsu: conf-del: config_del() called"));
192125cf1a30Sjl 	ASSERT(mpath);
192225cf1a30Sjl 
192325cf1a30Sjl 	mdev = (struct msu_dev *)(mpath + 1);
192425cf1a30Sjl 
192525cf1a30Sjl 	rw_enter(&oplmsu_uinst->lock, RW_WRITER);
192625cf1a30Sjl 	mutex_enter(&oplmsu_uinst->u_lock);
192725cf1a30Sjl 	for (i = 0; i < mpath->num; i++) {
192825cf1a30Sjl 		upath = oplmsu_search_upath_info(ddi_get_instance(mdev->dip));
192925cf1a30Sjl 		if (upath == NULL) {
193025cf1a30Sjl 			cmn_err(CE_WARN, "oplmsu: conf-del: "
193125cf1a30Sjl 			    "Proper upath_t doesn't find");
193225cf1a30Sjl 			rval = ENODEV;
193325cf1a30Sjl 			mdev++;
193425cf1a30Sjl 			continue;
193525cf1a30Sjl 		}
193625cf1a30Sjl 
193725cf1a30Sjl 		lpath = upath->lpath;
193825cf1a30Sjl 		if (lpath == NULL) {
193925cf1a30Sjl 			if ((upath->traditional_status == MSU_WSTP_ACK) ||
194025cf1a30Sjl 			    (upath->traditional_status == MSU_WSTR_ACK) ||
194125cf1a30Sjl 			    (upath->traditional_status == MSU_WPTH_CHG) ||
194225cf1a30Sjl 			    (upath->traditional_status == MSU_WTCS_ACK) ||
194325cf1a30Sjl 			    (upath->traditional_status == MSU_WTMS_ACK) ||
194425cf1a30Sjl 			    (upath->traditional_status == MSU_WPPS_ACK) ||
194525cf1a30Sjl 			    (upath->traditional_status == MSU_WWSZ_ACK) ||
194625cf1a30Sjl 			    (upath->traditional_status == MSU_WCAR_ACK)) {
194725cf1a30Sjl 				cmn_err(CE_WARN, "oplmsu: conf-del: "
194825cf1a30Sjl 				    "Other processing is using this device");
194925cf1a30Sjl 				rval = EBUSY;
195025cf1a30Sjl 				mdev++;
195125cf1a30Sjl 				continue;
195225cf1a30Sjl 			}
195325cf1a30Sjl 
195425cf1a30Sjl 			if ((upath->status != MSU_PSTAT_DISCON) ||
195525cf1a30Sjl 			    (upath->traditional_status != MSU_DISCON)) {
195625cf1a30Sjl 				cmn_err(CE_WARN, "oplmsu: conf-del: "
195725cf1a30Sjl 				    "Status of path is improper");
195825cf1a30Sjl 				rval = EINVAL;
195925cf1a30Sjl 				mdev++;
196025cf1a30Sjl 				continue;
196125cf1a30Sjl 			}
196225cf1a30Sjl 		} else {
196325cf1a30Sjl 			mutex_enter(&oplmsu_uinst->l_lock);
196425cf1a30Sjl 			use_flag = oplmsu_set_ioctl_path(lpath, NULL, NULL);
196525cf1a30Sjl 			if (use_flag == BUSY) {
196625cf1a30Sjl 				mutex_exit(&oplmsu_uinst->l_lock);
196725cf1a30Sjl 				cmn_err(CE_WARN, "oplmsu: conf-del: "
196825cf1a30Sjl 				    "Other processing is using lower path");
196925cf1a30Sjl 				rval = EBUSY;
197025cf1a30Sjl 				mdev++;
197125cf1a30Sjl 				continue;
197225cf1a30Sjl 			}
197325cf1a30Sjl 
197425cf1a30Sjl 			if (((upath->status != MSU_PSTAT_STOP) ||
197525cf1a30Sjl 			    (upath->traditional_status != MSU_STOP)) &&
197625cf1a30Sjl 			    ((upath->status != MSU_PSTAT_FAIL) ||
197725cf1a30Sjl 			    (upath->traditional_status != MSU_FAIL))) {
197825cf1a30Sjl 				oplmsu_clear_ioctl_path(lpath);
197925cf1a30Sjl 				mutex_exit(&oplmsu_uinst->l_lock);
198025cf1a30Sjl 				cmn_err(CE_WARN, "oplmsu: conf-del: "
198125cf1a30Sjl 				    "Status of path isn't 'Offline:stop/fail'");
198225cf1a30Sjl 				rval = EINVAL;
198325cf1a30Sjl 				mdev++;
198425cf1a30Sjl 				continue;
198525cf1a30Sjl 			}
198625cf1a30Sjl 			lpath->src_upath = NULL;
198725cf1a30Sjl 			lpath->status = MSU_SETID_NU;
198825cf1a30Sjl 			oplmsu_clear_ioctl_path(lpath);
198925cf1a30Sjl 			mutex_exit(&oplmsu_uinst->l_lock);
199025cf1a30Sjl 		}
199125cf1a30Sjl 		oplmsu_unlink_upath(upath);	/* Unlink upath_t */
199225cf1a30Sjl 		kmem_free(upath, sizeof (upath_t));
199325cf1a30Sjl 		mdev++;
199425cf1a30Sjl 	}
199525cf1a30Sjl 
199625cf1a30Sjl 	oplmsu_uinst->inst_status = oplmsu_get_inst_status();
199725cf1a30Sjl 	oplmsu_uinst->path_num = oplmsu_get_pathnum();
199825cf1a30Sjl 	mutex_exit(&oplmsu_uinst->u_lock);
199925cf1a30Sjl 	rw_exit(&oplmsu_uinst->lock);
200025cf1a30Sjl 	return (rval);
200125cf1a30Sjl }
200225cf1a30Sjl 
200325cf1a30Sjl /* Stop to use the path */
200425cf1a30Sjl int
oplmsu_config_stop(int pathnum)200525cf1a30Sjl oplmsu_config_stop(int pathnum)
200625cf1a30Sjl {
200725cf1a30Sjl 	upath_t	*upath, *altn_upath;
200825cf1a30Sjl 	lpath_t	*lpath, *altn_lpath;
200925cf1a30Sjl 	queue_t	*stp_queue = NULL;
201025cf1a30Sjl 	queue_t	*dst_queue = NULL;
201125cf1a30Sjl 	mblk_t	*nmp = NULL, *fmp = NULL;
201225cf1a30Sjl 	ctrl_t	*ctrl;
201325cf1a30Sjl 	int	term_ioctl, term_stat;
201425cf1a30Sjl 	int	use_flag;
201525cf1a30Sjl 
201625cf1a30Sjl 	DBG_PRINT((CE_NOTE,
201725cf1a30Sjl 	    "oplmsu: conf-stop: config_stop(%d) called", pathnum));
201825cf1a30Sjl 
201925cf1a30Sjl 	if (pathnum == MSU_PATH_ALL) {
202025cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: conf-stop: "
202125cf1a30Sjl 		    "All path can't be transferred to the status of "
202225cf1a30Sjl 		    "'Offline:stop'");
202325cf1a30Sjl 		return (EINVAL);
202425cf1a30Sjl 	}
202525cf1a30Sjl 
202625cf1a30Sjl 	rw_enter(&oplmsu_uinst->lock, RW_WRITER);
202725cf1a30Sjl 	mutex_enter(&oplmsu_uinst->u_lock);
202825cf1a30Sjl 
202925cf1a30Sjl 	upath = oplmsu_search_upath_info(pathnum);	/* Search upath_t */
203025cf1a30Sjl 	if (upath == NULL) {
203125cf1a30Sjl 		mutex_exit(&oplmsu_uinst->u_lock);
203225cf1a30Sjl 		rw_exit(&oplmsu_uinst->lock);
203325cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: conf-stop: "
203425cf1a30Sjl 		    "Proper upath_t doesn't find");
203525cf1a30Sjl 		return (ENODEV);
203625cf1a30Sjl 	}
203725cf1a30Sjl 
203825cf1a30Sjl 	lpath = upath->lpath;
203925cf1a30Sjl 	if (lpath == NULL) {
204025cf1a30Sjl 		mutex_exit(&oplmsu_uinst->u_lock);
204125cf1a30Sjl 		rw_exit(&oplmsu_uinst->lock);
204225cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: conf-stop: "
204325cf1a30Sjl 		    "Proper lpath_t doesn't exist");
204425cf1a30Sjl 		return (ENODEV);
204525cf1a30Sjl 	}
204625cf1a30Sjl 
204725cf1a30Sjl 	mutex_enter(&oplmsu_uinst->l_lock);
204825cf1a30Sjl 
204925cf1a30Sjl 	/* Check status of lpath_t */
205025cf1a30Sjl 	use_flag = oplmsu_set_ioctl_path(lpath, NULL, NULL);
205125cf1a30Sjl 	if (use_flag == BUSY) {
205225cf1a30Sjl 		mutex_exit(&oplmsu_uinst->l_lock);
205325cf1a30Sjl 		mutex_exit(&oplmsu_uinst->u_lock);
205425cf1a30Sjl 		rw_exit(&oplmsu_uinst->lock);
205525cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: conf-stop: "
205625cf1a30Sjl 		    "Other processing is using lower path");
205725cf1a30Sjl 		return (EBUSY);
205825cf1a30Sjl 	}
205925cf1a30Sjl 
206025cf1a30Sjl 	if (upath->status == MSU_PSTAT_FAIL) {
206125cf1a30Sjl 		oplmsu_clear_ioctl_path(lpath);
206225cf1a30Sjl 		mutex_exit(&oplmsu_uinst->l_lock);
206325cf1a30Sjl 		mutex_exit(&oplmsu_uinst->u_lock);
206425cf1a30Sjl 		rw_exit(&oplmsu_uinst->lock);
206525cf1a30Sjl 		return (EIO);
206625cf1a30Sjl 	} else if ((upath->status == MSU_PSTAT_STOP) &&
206725cf1a30Sjl 	    (upath->traditional_status == MSU_STOP)) {
206825cf1a30Sjl 		oplmsu_clear_ioctl_path(lpath);
206925cf1a30Sjl 		mutex_exit(&oplmsu_uinst->l_lock);
207025cf1a30Sjl 		mutex_exit(&oplmsu_uinst->u_lock);
207125cf1a30Sjl 		rw_exit(&oplmsu_uinst->lock);
207225cf1a30Sjl 		return (SUCCESS);
207325cf1a30Sjl 	} else if ((upath->status == MSU_PSTAT_STANDBY) &&
207425cf1a30Sjl 	    (upath->traditional_status == MSU_STANDBY)) {
207525cf1a30Sjl 		oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STOP,
207625cf1a30Sjl 		    upath->status, MSU_STOP);
207725cf1a30Sjl 		oplmsu_clear_ioctl_path(lpath);
207825cf1a30Sjl 		lpath->src_upath = NULL;
207925cf1a30Sjl 		lpath->status = MSU_EXT_NOTUSED;
208025cf1a30Sjl 
208125cf1a30Sjl 		oplmsu_uinst->inst_status = oplmsu_get_inst_status();
208225cf1a30Sjl 		mutex_exit(&oplmsu_uinst->l_lock);
208325cf1a30Sjl 		mutex_exit(&oplmsu_uinst->u_lock);
208425cf1a30Sjl 		rw_exit(&oplmsu_uinst->lock);
208525cf1a30Sjl 		return (SUCCESS);
208625cf1a30Sjl 	} else if ((upath->status == MSU_PSTAT_ACTIVE) &&
208725cf1a30Sjl 	    (upath->traditional_status == MSU_ACTIVE)) {
208825cf1a30Sjl 		altn_upath = oplmsu_search_standby();
208925cf1a30Sjl 		if (altn_upath == NULL) { /* Alternate path doesn't exist */
209025cf1a30Sjl 			DBG_PRINT((CE_NOTE, "oplmsu: conf-stop: "
209125cf1a30Sjl 			    "Alternate upper path doesn't find"));
209225cf1a30Sjl 			oplmsu_clear_ioctl_path(lpath);
209325cf1a30Sjl 			mutex_exit(&oplmsu_uinst->l_lock);
209425cf1a30Sjl 			mutex_exit(&oplmsu_uinst->u_lock);
209525cf1a30Sjl 			rw_exit(&oplmsu_uinst->lock);
209625cf1a30Sjl 			return (EINVAL);
209725cf1a30Sjl 		}
209825cf1a30Sjl 
209925cf1a30Sjl 		if ((fmp = allocb(sizeof (char), BPRI_LO)) == NULL) {
210025cf1a30Sjl 			oplmsu_clear_ioctl_path(lpath);
210125cf1a30Sjl 			mutex_exit(&oplmsu_uinst->l_lock);
210225cf1a30Sjl 			mutex_exit(&oplmsu_uinst->u_lock);
210325cf1a30Sjl 			rw_exit(&oplmsu_uinst->lock);
210425cf1a30Sjl 			return (ENOSR);
210525cf1a30Sjl 		}
210625cf1a30Sjl 
210725cf1a30Sjl 		if (oplmsu_stop_prechg(&nmp, &term_ioctl, &term_stat) !=
210825cf1a30Sjl 		    SUCCESS) {
210925cf1a30Sjl 			oplmsu_clear_ioctl_path(lpath);
211025cf1a30Sjl 			mutex_exit(&oplmsu_uinst->l_lock);
211125cf1a30Sjl 			mutex_exit(&oplmsu_uinst->u_lock);
211225cf1a30Sjl 			rw_exit(&oplmsu_uinst->lock);
211325cf1a30Sjl 			freeb(fmp);
211425cf1a30Sjl 			return (ENOSR);
211525cf1a30Sjl 		}
211625cf1a30Sjl 
211725cf1a30Sjl 		altn_lpath = altn_upath->lpath;
211825cf1a30Sjl 		use_flag = oplmsu_set_ioctl_path(altn_lpath, NULL, NULL);
211925cf1a30Sjl 		if (use_flag == BUSY) {
212025cf1a30Sjl 			oplmsu_clear_ioctl_path(lpath);
212125cf1a30Sjl 			mutex_exit(&oplmsu_uinst->l_lock);
212225cf1a30Sjl 			mutex_exit(&oplmsu_uinst->u_lock);
212325cf1a30Sjl 			rw_exit(&oplmsu_uinst->lock);
212425cf1a30Sjl 
212525cf1a30Sjl 			cmn_err(CE_WARN, "oplmsu: conf-stop: "
212625cf1a30Sjl 			    "Other processing is using alternate lower path");
212725cf1a30Sjl 			freeb(fmp);
212825cf1a30Sjl 			freemsg(nmp);
212925cf1a30Sjl 			return (EBUSY);
213025cf1a30Sjl 		}
213125cf1a30Sjl 
213225cf1a30Sjl 		dst_queue = WR(altn_lpath->lower_queue);
213325cf1a30Sjl 
213425cf1a30Sjl 		/* termios is not held. Change alternate path to MSU_ACTIVE */
213525cf1a30Sjl 		if (nmp == NULL) {
213625cf1a30Sjl 			altn_upath->traditional_status = term_stat;
213725cf1a30Sjl 			altn_lpath->src_upath = upath;
213825cf1a30Sjl 			altn_lpath->status = MSU_EXT_VOID;
213925cf1a30Sjl 
214025cf1a30Sjl 			oplmsu_uinst->lower_queue = NULL;
214125cf1a30Sjl 
214225cf1a30Sjl 			ctrl = oplmsu_uinst->user_ctrl;
214325cf1a30Sjl 			if (ctrl != NULL) {
214425cf1a30Sjl 				mutex_enter(&oplmsu_uinst->c_lock);
214525cf1a30Sjl 				stp_queue = WR(ctrl->queue);
214625cf1a30Sjl 				mutex_exit(&oplmsu_uinst->c_lock);
214725cf1a30Sjl 				noenable(stp_queue);
214825cf1a30Sjl 				oplmsu_queue_flag = 1;
214925cf1a30Sjl 			}
215025cf1a30Sjl 
215125cf1a30Sjl 			/* Make M_FLUSH and send to alternate path */
215225cf1a30Sjl 			oplmsu_cmn_set_mflush(fmp);
215307d06da5SSurya Prakki 			(void) putq(dst_queue, fmp);
215425cf1a30Sjl 
215525cf1a30Sjl 			/* Change status of alternate path */
215625cf1a30Sjl 			oplmsu_cmn_set_upath_sts(altn_upath, MSU_PSTAT_ACTIVE,
215725cf1a30Sjl 			    altn_upath->status, MSU_ACTIVE);
215825cf1a30Sjl 
215925cf1a30Sjl 			oplmsu_clear_ioctl_path(altn_lpath);
216025cf1a30Sjl 			altn_lpath->uinst = oplmsu_uinst;
216125cf1a30Sjl 			altn_lpath->src_upath = NULL;
216225cf1a30Sjl 			altn_lpath->status = MSU_EXT_NOTUSED;
216325cf1a30Sjl 
216425cf1a30Sjl 			/* Notify of the active path changing */
216507d06da5SSurya Prakki 			(void) prom_opl_switch_console(
216607d06da5SSurya Prakki 			    altn_upath->ser_devcb.lsb);
216725cf1a30Sjl 
216825cf1a30Sjl 			/* Send XON to notify active path */
216925cf1a30Sjl 			(void) oplmsu_cmn_put_xoffxon(dst_queue, MSU_XON_4);
217025cf1a30Sjl 
217125cf1a30Sjl 			/* Send XOFF to notify all standby paths */
217225cf1a30Sjl 			oplmsu_cmn_putxoff_standby();
217325cf1a30Sjl 
217425cf1a30Sjl 			oplmsu_uinst->lower_queue = RD(dst_queue);
217525cf1a30Sjl 			ctrl = oplmsu_uinst->user_ctrl;
217625cf1a30Sjl 
217725cf1a30Sjl 			/* Switch active path of oplmsu */
217825cf1a30Sjl 			if (ctrl != NULL) {
217925cf1a30Sjl 				queue_t	*altn_queue;
218025cf1a30Sjl 
218125cf1a30Sjl 				mutex_enter(&oplmsu_uinst->c_lock);
218225cf1a30Sjl 				altn_queue = WR(ctrl->queue);
218325cf1a30Sjl 				mutex_exit(&oplmsu_uinst->c_lock);
218425cf1a30Sjl 
218525cf1a30Sjl 				/* Restart queuing of user access node */
218625cf1a30Sjl 				enableok(altn_queue);
218725cf1a30Sjl 
218825cf1a30Sjl 				oplmsu_queue_flag = 0;
218925cf1a30Sjl 				mutex_exit(&oplmsu_uinst->l_lock);
219025cf1a30Sjl 				mutex_exit(&oplmsu_uinst->u_lock);
219125cf1a30Sjl 				oplmsu_wcmn_high_qenable(altn_queue, RW_WRITER);
219225cf1a30Sjl 				mutex_enter(&oplmsu_uinst->u_lock);
219325cf1a30Sjl 				mutex_enter(&oplmsu_uinst->l_lock);
219425cf1a30Sjl 			}
219525cf1a30Sjl 
219625cf1a30Sjl 			/* Stop previous active path */
219725cf1a30Sjl 			oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STOP,
219825cf1a30Sjl 			    upath->status, MSU_STOP);
219925cf1a30Sjl 
220025cf1a30Sjl 			lpath->uinst = NULL;
220125cf1a30Sjl 			lpath->src_upath = NULL;
220225cf1a30Sjl 			lpath->status = MSU_EXT_NOTUSED;
220325cf1a30Sjl 			oplmsu_clear_ioctl_path(lpath);
220425cf1a30Sjl 
220525cf1a30Sjl 			oplmsu_uinst->inst_status = oplmsu_get_inst_status();
220625cf1a30Sjl 			mutex_exit(&oplmsu_uinst->l_lock);
220725cf1a30Sjl 			mutex_exit(&oplmsu_uinst->u_lock);
220825cf1a30Sjl 			rw_exit(&oplmsu_uinst->lock);
220925cf1a30Sjl 			return (SUCCESS);
221025cf1a30Sjl 		}
221125cf1a30Sjl 
221225cf1a30Sjl 		/* Send termios information to alternate path */
221325cf1a30Sjl 		if (canput(dst_queue)) {
221425cf1a30Sjl 			altn_upath->traditional_status = term_stat;
221525cf1a30Sjl 			altn_lpath->src_upath = upath;
221625cf1a30Sjl 			altn_lpath->status = MSU_EXT_VOID;
221725cf1a30Sjl 
221825cf1a30Sjl 			upath->traditional_status = MSU_WSTP_ACK;
221925cf1a30Sjl 			lpath->uinst = NULL;
222025cf1a30Sjl 
222125cf1a30Sjl 			oplmsu_uinst->lower_queue = NULL;
222225cf1a30Sjl 
222325cf1a30Sjl 			ctrl = oplmsu_uinst->user_ctrl;
222425cf1a30Sjl 			if (ctrl != NULL) {
222525cf1a30Sjl 				mutex_enter(&oplmsu_uinst->c_lock);
222625cf1a30Sjl 				stp_queue = WR(ctrl->queue);
222725cf1a30Sjl 				mutex_exit(&oplmsu_uinst->c_lock);
222825cf1a30Sjl 				noenable(stp_queue);
222925cf1a30Sjl 				oplmsu_queue_flag = 1;
223025cf1a30Sjl 			}
223125cf1a30Sjl 
223225cf1a30Sjl 			mutex_exit(&oplmsu_uinst->l_lock);
223325cf1a30Sjl 			mutex_exit(&oplmsu_uinst->u_lock);
223425cf1a30Sjl 			rw_exit(&oplmsu_uinst->lock);
223525cf1a30Sjl 			oplmsu_cmn_set_mflush(fmp);
223607d06da5SSurya Prakki 			(void) putq(dst_queue, fmp);
223707d06da5SSurya Prakki 			(void) putq(dst_queue, nmp);
223825cf1a30Sjl 
223925cf1a30Sjl 			mutex_enter(&oplmsu_uinst->l_lock);
224025cf1a30Sjl 			lpath->sw_flag = 1;
224125cf1a30Sjl 			while (lpath->sw_flag != 0) {
224225cf1a30Sjl 				/* Wait for the completion of path switching */
224325cf1a30Sjl 				cv_wait(&lpath->sw_cv, &oplmsu_uinst->l_lock);
224425cf1a30Sjl 			}
224525cf1a30Sjl 			mutex_exit(&oplmsu_uinst->l_lock);
224625cf1a30Sjl 			return (SUCCESS);
224725cf1a30Sjl 		} else {
224825cf1a30Sjl 			oplmsu_clear_ioctl_path(altn_lpath);
224925cf1a30Sjl 			oplmsu_clear_ioctl_path(lpath);
225025cf1a30Sjl 			mutex_exit(&oplmsu_uinst->l_lock);
225125cf1a30Sjl 			mutex_exit(&oplmsu_uinst->u_lock);
225225cf1a30Sjl 			rw_exit(&oplmsu_uinst->lock);
225325cf1a30Sjl 			freeb(fmp);
225425cf1a30Sjl 			freemsg(nmp);
225525cf1a30Sjl 			return (FAILURE);
225625cf1a30Sjl 		}
225725cf1a30Sjl 		/* NOTREACHED */
225825cf1a30Sjl 	} else {
225925cf1a30Sjl 		oplmsu_clear_ioctl_path(lpath);
226025cf1a30Sjl 		mutex_exit(&oplmsu_uinst->l_lock);
226125cf1a30Sjl 		mutex_exit(&oplmsu_uinst->u_lock);
226225cf1a30Sjl 		rw_exit(&oplmsu_uinst->lock);
226325cf1a30Sjl 
226425cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: conf-stop: "
226525cf1a30Sjl 		    "Status of path is improper");
226625cf1a30Sjl 		return (EINVAL);
226725cf1a30Sjl 	}
226825cf1a30Sjl 	/* NOTREACHED */
226925cf1a30Sjl }
227025cf1a30Sjl 
227125cf1a30Sjl /* Start to use path */
227225cf1a30Sjl int
oplmsu_config_start(int pathnum)227325cf1a30Sjl oplmsu_config_start(int pathnum)
227425cf1a30Sjl {
227525cf1a30Sjl 	upath_t	*upath = NULL;
227625cf1a30Sjl 	lpath_t	*lpath = NULL;
227725cf1a30Sjl 	queue_t	*dst_queue, *main_rq = NULL;
227825cf1a30Sjl 	int	msu_tty_port;
227925cf1a30Sjl 
228025cf1a30Sjl 	DBG_PRINT((CE_NOTE,
228125cf1a30Sjl 	    "oplmsu: conf-start: config_start(%d) called", pathnum));
228225cf1a30Sjl 
228325cf1a30Sjl 	rw_enter(&oplmsu_uinst->lock, RW_WRITER);
228425cf1a30Sjl 	mutex_enter(&oplmsu_uinst->u_lock);
228525cf1a30Sjl 
228625cf1a30Sjl 	if (oplmsu_get_inst_status() == INST_STAT_BUSY) {
228725cf1a30Sjl 		mutex_exit(&oplmsu_uinst->u_lock);
228825cf1a30Sjl 		rw_exit(&oplmsu_uinst->lock);
228925cf1a30Sjl 		return (EBUSY);
229025cf1a30Sjl 	}
229125cf1a30Sjl 
229225cf1a30Sjl 	if (pathnum == MSU_PATH_ALL) {
229325cf1a30Sjl 		(void) oplmsu_search_min_stop_path();
229425cf1a30Sjl 	}
229525cf1a30Sjl 
229625cf1a30Sjl 	for (upath = oplmsu_uinst->first_upath; upath; ) {
229725cf1a30Sjl 		if ((pathnum != MSU_PATH_ALL) && (upath->path_no != pathnum)) {
229825cf1a30Sjl 			upath = upath->u_next;
229925cf1a30Sjl 			continue;
230025cf1a30Sjl 		}
230125cf1a30Sjl 
230225cf1a30Sjl 		if (upath->path_no == pathnum) {
230325cf1a30Sjl 			lpath = upath->lpath;
230425cf1a30Sjl 			if (lpath == NULL) {
230525cf1a30Sjl 				mutex_exit(&oplmsu_uinst->u_lock);
230625cf1a30Sjl 				rw_exit(&oplmsu_uinst->lock);
230725cf1a30Sjl 				cmn_err(CE_WARN, "oplmsu: conf-start: "
230825cf1a30Sjl 				    "Proper lpath_t doesn't exist");
230925cf1a30Sjl 				return (EINVAL);
231025cf1a30Sjl 			}
231125cf1a30Sjl 
231225cf1a30Sjl 			oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STANDBY,
231325cf1a30Sjl 			    upath->status, MSU_STANDBY);
231425cf1a30Sjl 
231525cf1a30Sjl 			mutex_enter(&oplmsu_uinst->l_lock);
231625cf1a30Sjl 			lpath->src_upath = NULL;
231725cf1a30Sjl 			lpath->status = MSU_EXT_NOTUSED;
231825cf1a30Sjl 			mutex_exit(&oplmsu_uinst->l_lock);
231925cf1a30Sjl 			mutex_exit(&oplmsu_uinst->u_lock);
232025cf1a30Sjl 			rw_exit(&oplmsu_uinst->lock);
232125cf1a30Sjl 			return (SUCCESS);
232225cf1a30Sjl 		}
232325cf1a30Sjl 
232425cf1a30Sjl 		/*
232525cf1a30Sjl 		 * with PATH_ALL
232625cf1a30Sjl 		 */
232725cf1a30Sjl 		lpath = upath->lpath;
232825cf1a30Sjl 		if (lpath == NULL) {
232925cf1a30Sjl 			upath = upath->u_next;
233025cf1a30Sjl 
233125cf1a30Sjl 			DBG_PRINT((CE_WARN, "oplmsu: conf-start: "
233225cf1a30Sjl 			    "Proper lpath_t doesn't exist"));
233325cf1a30Sjl 			continue;
233425cf1a30Sjl 		}
233525cf1a30Sjl 
233625cf1a30Sjl 		msu_tty_port = ddi_prop_get_int(DDI_DEV_T_ANY,
233725cf1a30Sjl 		    oplmsu_uinst->msu_dip, 0, MSU_TTY_PORT_PROP, -1);
233825cf1a30Sjl 
233925cf1a30Sjl 		if (upath->ser_devcb.lsb == msu_tty_port) {
234025cf1a30Sjl 			/* Notify of the active path changing */
234107d06da5SSurya Prakki 			(void) prom_opl_switch_console(upath->ser_devcb.lsb);
234225cf1a30Sjl 
234325cf1a30Sjl 			oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_ACTIVE,
234425cf1a30Sjl 			    upath->status, MSU_ACTIVE);
234525cf1a30Sjl 
234625cf1a30Sjl 			mutex_enter(&oplmsu_uinst->l_lock);
234725cf1a30Sjl 			main_rq = RD(lpath->lower_queue);
234825cf1a30Sjl 			dst_queue = WR(lpath->lower_queue);
234925cf1a30Sjl 			lpath->src_upath = NULL;
235025cf1a30Sjl 			lpath->status = MSU_EXT_NOTUSED;
235125cf1a30Sjl 			lpath->uinst = oplmsu_uinst;
235225cf1a30Sjl 			mutex_exit(&oplmsu_uinst->l_lock);
235325cf1a30Sjl 
235425cf1a30Sjl 			/* Send XON to notify active path */
235525cf1a30Sjl 			(void) oplmsu_cmn_put_xoffxon(dst_queue, MSU_XON_4);
235625cf1a30Sjl 		} else {
235725cf1a30Sjl 			oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STANDBY,
235825cf1a30Sjl 			    upath->status, MSU_STANDBY);
235925cf1a30Sjl 
236025cf1a30Sjl 			mutex_enter(&oplmsu_uinst->l_lock);
236125cf1a30Sjl 			lpath->src_upath = NULL;
236225cf1a30Sjl 			lpath->status = MSU_EXT_NOTUSED;
236325cf1a30Sjl 			mutex_exit(&oplmsu_uinst->l_lock);
236425cf1a30Sjl 		}
236525cf1a30Sjl 		upath = upath->u_next;
236625cf1a30Sjl 	}
236725cf1a30Sjl 
236825cf1a30Sjl 	if (main_rq == NULL) {
236925cf1a30Sjl 		upath_t	*altn_upath;
237025cf1a30Sjl 		lpath_t	*altn_lpath;
237125cf1a30Sjl 
237225cf1a30Sjl 		altn_upath = oplmsu_search_standby();
237325cf1a30Sjl 		if (altn_upath) {
237425cf1a30Sjl 			oplmsu_cmn_set_upath_sts(altn_upath, MSU_PSTAT_ACTIVE,
237525cf1a30Sjl 			    altn_upath->status, MSU_ACTIVE);
237625cf1a30Sjl 
237725cf1a30Sjl 			/* Notify of the active path changing */
237807d06da5SSurya Prakki 			(void) prom_opl_switch_console(
237907d06da5SSurya Prakki 			    altn_upath->ser_devcb.lsb);
238025cf1a30Sjl 
238125cf1a30Sjl 			altn_lpath = altn_upath->lpath;
238225cf1a30Sjl 			if (altn_lpath) {
238325cf1a30Sjl 				mutex_enter(&oplmsu_uinst->l_lock);
238425cf1a30Sjl 				main_rq = RD(altn_lpath->lower_queue);
238525cf1a30Sjl 				dst_queue = WR(altn_lpath->lower_queue);
238625cf1a30Sjl 				altn_lpath->src_upath = NULL;
238725cf1a30Sjl 				altn_lpath->status = MSU_EXT_NOTUSED;
238825cf1a30Sjl 				altn_lpath->uinst = oplmsu_uinst;
238925cf1a30Sjl 				mutex_exit(&oplmsu_uinst->l_lock);
239025cf1a30Sjl 
239125cf1a30Sjl 				/* Send XON to notify active path */
239225cf1a30Sjl 				(void) oplmsu_cmn_put_xoffxon(dst_queue,
239325cf1a30Sjl 				    MSU_XON_4);
239425cf1a30Sjl 			} else {
239525cf1a30Sjl 				cmn_err(CE_WARN, "oplmsu: conf-start: "
239625cf1a30Sjl 				    "Proper alternate lpath_t doesn't exist");
239725cf1a30Sjl 			}
239825cf1a30Sjl 		} else {
239925cf1a30Sjl 			cmn_err(CE_WARN, "oplmsu: conf-start: "
240025cf1a30Sjl 			    "Proper alternate upath_t doesn't exist");
240125cf1a30Sjl 		}
240225cf1a30Sjl 	}
240325cf1a30Sjl 
240425cf1a30Sjl 	mutex_enter(&oplmsu_uinst->l_lock);
240525cf1a30Sjl 
240625cf1a30Sjl 	/* Send XOFF to notify all standby paths */
240725cf1a30Sjl 	oplmsu_cmn_putxoff_standby();
240825cf1a30Sjl 
240925cf1a30Sjl 	/* Change active path of oplmsu */
241025cf1a30Sjl 	oplmsu_uinst->lower_queue = main_rq;
241125cf1a30Sjl 	oplmsu_uinst->inst_status = oplmsu_get_inst_status();
241225cf1a30Sjl 	mutex_exit(&oplmsu_uinst->l_lock);
241325cf1a30Sjl 	mutex_exit(&oplmsu_uinst->u_lock);
241425cf1a30Sjl 	rw_exit(&oplmsu_uinst->lock);
241525cf1a30Sjl 	return (SUCCESS);
241625cf1a30Sjl }
241725cf1a30Sjl 
241825cf1a30Sjl /* Prepare of unlink path */
241925cf1a30Sjl int
oplmsu_config_disc(int pathnum)242025cf1a30Sjl oplmsu_config_disc(int pathnum)
242125cf1a30Sjl {
242225cf1a30Sjl 	upath_t	*upath;
242325cf1a30Sjl 	lpath_t	*lpath;
242425cf1a30Sjl 	int	use_flag;
242525cf1a30Sjl 
242625cf1a30Sjl 	DBG_PRINT((CE_NOTE,
242725cf1a30Sjl 	    "oplmsu: conf-disc: config_disc(%d) called", pathnum));
242825cf1a30Sjl 
242925cf1a30Sjl 	rw_enter(&oplmsu_uinst->lock, RW_READER);
243025cf1a30Sjl 	mutex_enter(&oplmsu_uinst->u_lock);
243125cf1a30Sjl 
243225cf1a30Sjl 	upath = oplmsu_search_upath_info(pathnum);
243325cf1a30Sjl 	if (upath == NULL) {
243425cf1a30Sjl 		mutex_exit(&oplmsu_uinst->u_lock);
243525cf1a30Sjl 		rw_exit(&oplmsu_uinst->lock);
243625cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: conf-disc: "
243725cf1a30Sjl 		    "Proper upath_t doesn't find");
243825cf1a30Sjl 		return (EINVAL);
243925cf1a30Sjl 	}
244025cf1a30Sjl 
244125cf1a30Sjl 	if ((upath->status == MSU_PSTAT_DISCON) ||
244225cf1a30Sjl 	    (upath->traditional_status == MSU_DISCON)) {
244325cf1a30Sjl 		mutex_exit(&oplmsu_uinst->u_lock);
244425cf1a30Sjl 		rw_exit(&oplmsu_uinst->lock);
244525cf1a30Sjl 		return (SUCCESS);
244625cf1a30Sjl 	} else if (((upath->status != MSU_PSTAT_STOP) ||
244725cf1a30Sjl 	    (upath->traditional_status != MSU_STOP)) &&
244825cf1a30Sjl 	    ((upath->status != MSU_PSTAT_FAIL) ||
244925cf1a30Sjl 	    (upath->traditional_status != MSU_FAIL))) {
245025cf1a30Sjl 		mutex_exit(&oplmsu_uinst->u_lock);
245125cf1a30Sjl 		rw_exit(&oplmsu_uinst->lock);
245225cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: conf-disc: "
245325cf1a30Sjl 		    "Status of path is improper");
245425cf1a30Sjl 		return (EINVAL);
245525cf1a30Sjl 	}
245625cf1a30Sjl 
245725cf1a30Sjl 	lpath = upath->lpath;
245825cf1a30Sjl 	if (lpath == NULL) {
245925cf1a30Sjl 		mutex_exit(&oplmsu_uinst->u_lock);
246025cf1a30Sjl 		rw_exit(&oplmsu_uinst->lock);
246125cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: conf-disc: "
246225cf1a30Sjl 		    "Proper lpath_t doesn't exist");
246325cf1a30Sjl 		return (ENODEV);
246425cf1a30Sjl 	}
246525cf1a30Sjl 
246625cf1a30Sjl 	mutex_enter(&oplmsu_uinst->l_lock);
246725cf1a30Sjl 
246825cf1a30Sjl 	/* Check lower path status */
246925cf1a30Sjl 	use_flag = oplmsu_set_ioctl_path(lpath, NULL, NULL);
247025cf1a30Sjl 	if (use_flag == BUSY) {
247125cf1a30Sjl 		mutex_exit(&oplmsu_uinst->l_lock);
247225cf1a30Sjl 		mutex_exit(&oplmsu_uinst->u_lock);
247325cf1a30Sjl 		rw_exit(&oplmsu_uinst->lock);
247425cf1a30Sjl 		cmn_err(CE_WARN, "oplmsu: conf-disc: "
247525cf1a30Sjl 		    "Other processing is using lower path");
247625cf1a30Sjl 		return (EBUSY);
247725cf1a30Sjl 	}
247825cf1a30Sjl 
247925cf1a30Sjl 	upath->status = MSU_PSTAT_STOP;
248025cf1a30Sjl 	upath->traditional_status = MSU_SETID;
248125cf1a30Sjl 
248225cf1a30Sjl 	oplmsu_clear_ioctl_path(lpath);
248325cf1a30Sjl 	mutex_exit(&oplmsu_uinst->l_lock);
248425cf1a30Sjl 	mutex_exit(&oplmsu_uinst->u_lock);
248525cf1a30Sjl 	rw_exit(&oplmsu_uinst->lock);
248625cf1a30Sjl 	return (SUCCESS);
248725cf1a30Sjl }
2488