xref: /illumos-gate/usr/src/uts/sun4u/opl/io/oplmsu/oplmsu.c (revision 5b20806a)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * All Rights Reserved, Copyright (c) FUJITSU LIMITED 2006
23  */
24 
25 #pragma ident	"%Z%%M%	%I%	%E% SMI"
26 
27 #include <sys/errno.h>
28 #include <sys/modctl.h>
29 #include <sys/stat.h>
30 #include <sys/kmem.h>
31 #include <sys/ksynch.h>
32 #include <sys/stream.h>
33 #include <sys/stropts.h>
34 #include <sys/termio.h>
35 #include <sys/ddi.h>
36 #include <sys/file.h>
37 #include <sys/disp.h>
38 #include <sys/sunddi.h>
39 #include <sys/sunldi.h>
40 #include <sys/sunndi.h>
41 #include <sys/kbio.h>
42 #include <sys/prom_plat.h>
43 #include <sys/oplmsu/oplmsu.h>
44 #include <sys/oplmsu/oplmsu_proto.h>
45 
46 extern int ddi_create_internal_pathname(dev_info_t *, char *, int, minor_t);
47 
48 #define	MOD_ID		0xe145
49 #define	MOD_NAME	"oplmsu"
50 
51 #define	META_NAME	"oplmsu"
52 #define	USER_NAME	"a"
53 
54 struct module_info oplmsu_mod_info = {
55 	MOD_ID,
56 	MOD_NAME,
57 	0,
58 	16384,
59 	14336,
60 	2048
61 };
62 
63 struct qinit oplmsu_urinit = {
64 	NULL,
65 	oplmsu_ursrv,
66 	oplmsu_open,
67 	oplmsu_close,
68 	NULL,
69 	&oplmsu_mod_info,
70 	NULL
71 };
72 
73 struct qinit oplmsu_uwinit = {
74 	oplmsu_uwput,
75 	oplmsu_uwsrv,
76 	oplmsu_open,
77 	oplmsu_close,
78 	NULL,
79 	&oplmsu_mod_info,
80 	NULL
81 };
82 
83 struct qinit oplmsu_lrinit = {
84 	oplmsu_lrput,
85 	oplmsu_lrsrv,
86 	oplmsu_open,
87 	oplmsu_close,
88 	NULL,
89 	&oplmsu_mod_info,
90 	NULL
91 };
92 
93 struct qinit oplmsu_lwinit = {
94 	NULL,
95 	oplmsu_lwsrv,
96 	oplmsu_open,
97 	oplmsu_close,
98 	NULL,
99 	&oplmsu_mod_info,
100 	NULL
101 };
102 
103 struct streamtab oplmsu_info = {
104 	&oplmsu_urinit,
105 	&oplmsu_uwinit,
106 	&oplmsu_lrinit,
107 	&oplmsu_lwinit
108 };
109 
110 static struct cb_ops cb_oplmsu_ops = {
111 	nulldev,			/* cb_open */
112 	nulldev,			/* cb_close */
113 	nodev,				/* cb_strategy */
114 	nodev,				/* cb_print */
115 	nodev,				/* cb_dump */
116 	nodev,				/* cb_read */
117 	nodev,				/* cb_write */
118 	nodev,				/* cb_ioctl */
119 	nodev,				/* cb_devmap */
120 	nodev,				/* cb_mmap */
121 	nodev,				/* cb_segmap */
122 	nochpoll,			/* cb_chpoll */
123 	ddi_prop_op,			/* cb_prop_op */
124 	(&oplmsu_info),			/* cb_stream */
125 	(int)(D_NEW|D_MP|D_HOTPLUG)	/* cb_flag */
126 };
127 
128 static struct dev_ops oplmsu_ops = {
129 	DEVO_REV,			/* devo_rev */
130 	0,				/* devo_refcnt */
131 	(oplmsu_getinfo),		/* devo_getinfo */
132 	(nulldev),			/* devo_identify */
133 	(nulldev),			/* devo_probe */
134 	(oplmsu_attach),		/* devo_attach */
135 	(oplmsu_detach),		/* devo_detach */
136 	(nodev),			/* devo_reset */
137 	&(cb_oplmsu_ops),		/* devo_cb_ops */
138 	(struct bus_ops *)NULL,		/* devo_bus_ops */
139 	NULL				/* devo_power */
140 };
141 
142 struct modldrv modldrv = {
143 	&mod_driverops,
144 	"OPL serial mux driver %I%",
145 	&oplmsu_ops
146 };
147 
148 struct modlinkage modlinkage = {
149 	MODREV_1,
150 	(void *)&modldrv,
151 	NULL
152 };
153 
154 uinst_t		oplmsu_uinst_local;	/* upper_instance_table structure */
155 uinst_t		*oplmsu_uinst = &oplmsu_uinst_local;
156 int		oplmsu_queue_flag;	/* Enable/disable queueing flag */
157 int		oplmsu_check_su;	/* Check super-user flag */
158 
159 #ifdef DEBUG
160 int		oplmsu_debug_mode = 0;	/* Enable/disable debug mode */
161 int		oplmsu_trace_on;	/* Enable/disable trace */
162 uint_t		oplmsu_ltrc_size;	/* Trace buffer size */
163 msu_trc_t	*oplmsu_ltrc_top;	/* Top of trace data area */
164 msu_trc_t	*oplmsu_ltrc_tail;	/* Tail of trace data area */
165 msu_trc_t	*oplmsu_ltrc_cur;	/* Current pointer of trace data area */
166 ulong_t		oplmsu_ltrc_ccnt;	/* Current counter */
167 kmutex_t	oplmsu_ltrc_lock;	/* Lock table for trace mode */
168 #endif
169 
170 /* oplmsu_conf_st */
171 #define	MSU_CONFIGURED		2
172 #define	MSU_CONFIGURING		1
173 #define	MSU_UNCONFIGURED	0
174 
175 static kmutex_t		oplmsu_bthrd_excl;
176 static kthread_id_t	oplmsu_bthrd_id = NULL;
177 static int		oplmsu_conf_st = MSU_UNCONFIGURED;
178 static kcondvar_t	oplmsu_conf_cv;
179 
180 
181 /*
182  * Locking hierarcy of oplmsu driver. This driver have 5 locks in uinst_t.
183  *
184  * Each mutex guards as follows.
185  *
186  *  uinst_t->lock: This mutex is read/write mutex.
187  *     read lock : acquired if the member of uinst_t is refered only.
188  *     write lock: acquired if the member of uinst_t is changed.
189  *
190  *  uinst_t->u_lock: This mutex is normal mutex.
191  *   This mutex is acquired at reading/changing the member of all upath_t.
192  *
193  *  uinst_t->l_lock: This mutex is normal mutex.
194  *   This mutex is acquired at reading/changing the member of all lpath_t.
195  *
196  *  uinst_t->c_lock: This mutex is normal mutex.
197  *   This mutex is acquired at reading/changing the member of the ctrl_t.
198  *
199  *  oplmsu_bthrd_excl: This mutex is normal mutex.
200  *   This mutex is used only to start/stop the configuring thread of the
201  *   multiplexed STREAMS.
202  *   This mutex is exclusively acquired with the above-mentioned 4 mutexes.
203  *
204  * To guard of the deadlock by cross locking, the base locking hierarcy
205  * is as follows:
206  *
207  *     uisnt->lock ==> uinst->u_lock ==> uinst->l_lock ==> uinst->c_lock
208  *
209  */
210 
211 
212 int
213 _init(void)
214 {
215 	int	rval;
216 
217 	/* Initialize R/W lock for uinst_t */
218 	rw_init(&oplmsu_uinst->lock, "uinst rwlock", RW_DRIVER, NULL);
219 
220 	/* Initialize mutex for upath_t */
221 	mutex_init(&oplmsu_uinst->u_lock, "upath lock", MUTEX_DRIVER, NULL);
222 
223 	/* Initialize mutex for lpath_t */
224 	mutex_init(&oplmsu_uinst->l_lock, "lpath lock", MUTEX_DRIVER, NULL);
225 
226 	/* Initialize mutex for ctrl_t */
227 	mutex_init(&oplmsu_uinst->c_lock, "ctrl lock", MUTEX_DRIVER, NULL);
228 
229 	/* Initialize mutex for protecting background thread */
230 	mutex_init(&oplmsu_bthrd_excl, NULL, MUTEX_DRIVER, NULL);
231 
232 	/* Initialize condition variable */
233 	cv_init(&oplmsu_conf_cv, NULL, CV_DRIVER, NULL);
234 
235 	rval = mod_install(&modlinkage);
236 	if (rval != DDI_SUCCESS) {
237 		cv_destroy(&oplmsu_conf_cv);
238 		mutex_destroy(&oplmsu_bthrd_excl);
239 		mutex_destroy(&oplmsu_uinst->c_lock);
240 		mutex_destroy(&oplmsu_uinst->l_lock);
241 		mutex_destroy(&oplmsu_uinst->u_lock);
242 		rw_destroy(&oplmsu_uinst->lock);
243 	}
244 	return (rval);
245 }
246 
247 int
248 _fini(void)
249 {
250 	int	rval;
251 
252 	rval = mod_remove(&modlinkage);
253 	if (rval == DDI_SUCCESS) {
254 		cv_destroy(&oplmsu_conf_cv);
255 		mutex_destroy(&oplmsu_bthrd_excl);
256 		mutex_destroy(&oplmsu_uinst->c_lock);
257 		mutex_destroy(&oplmsu_uinst->l_lock);
258 		mutex_destroy(&oplmsu_uinst->u_lock);
259 		rw_destroy(&oplmsu_uinst->lock);
260 	}
261 	return (rval);
262 }
263 
264 int
265 _info(struct modinfo *modinfop)
266 {
267 	return (mod_info(&modlinkage, modinfop));
268 }
269 
270 /* ARGSUSED */
271 int
272 oplmsu_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
273 {
274 	dev_t	dev = (dev_t)arg;
275 	minor_t	inst;
276 	int	rval = DDI_SUCCESS;
277 
278 	switch (cmd) {
279 	case DDI_INFO_DEVT2DEVINFO  :
280 		if (oplmsu_uinst->msu_dip == NULL) {
281 			rval = DDI_FAILURE;
282 		} else {
283 			*resultp = oplmsu_uinst->msu_dip;
284 		}
285 		break;
286 
287 	case DDI_INFO_DEVT2INSTANCE :
288 		inst = getminor(dev) & ~(META_NODE_MASK|USER_NODE_MASK);
289 		*resultp = (void *)(uintptr_t)inst;
290 		break;
291 
292 	default :
293 		rval = DDI_FAILURE;
294 		break;
295 	}
296 	return (rval);
297 }
298 
299 int
300 oplmsu_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
301 {
302 	minor_t	meta_minor, user_minor;
303 	int	rval = 0;
304 	int	instance;
305 #define	CNTRL(c) ((c) & 037)
306 	char	abt_ch_seq[3] = { '\r', '~', CNTRL('b') };
307 
308 	if (cmd == DDI_RESUME) {
309 		return (DDI_SUCCESS);
310 	}
311 
312 	if (cmd != DDI_ATTACH) {
313 		return (DDI_FAILURE);
314 	}
315 
316 	instance = ddi_get_instance(dip);
317 	if (instance != 0) {
318 		cmn_err(CE_WARN, "oplmsu: attach: "
319 		    "Invaild instance => %d", instance);
320 		return (DDI_FAILURE);
321 	}
322 
323 	/* Create minor number for meta control node */
324 	meta_minor = instance | META_NODE_MASK;
325 	/* Create minor number for user access node */
326 	user_minor = instance | USER_NODE_MASK;
327 
328 	/* Create minor node for user access */
329 	rval = ddi_create_minor_node(dip, USER_NAME, S_IFCHR, user_minor,
330 	    DDI_NT_SERIAL, 0);
331 	if (rval != DDI_SUCCESS) {
332 		cmn_err(CE_WARN, "oplmsu: attach: "
333 		    "ddi_create_minor_node failed. errno = %d", rval);
334 		ddi_remove_minor_node(dip, NULL);
335 		return (rval);
336 	}
337 
338 	/* Create minor node for meta control */
339 	rval = ddi_create_internal_pathname(dip, META_NAME, S_IFCHR,
340 	    meta_minor);
341 	if (rval != DDI_SUCCESS) {
342 		cmn_err(CE_WARN, "oplmsu: attach: "
343 		    "ddi_create_internal_pathname failed. errno = %d", rval);
344 		ddi_remove_minor_node(dip, NULL);
345 		return (rval);
346 	}
347 
348 	rw_enter(&oplmsu_uinst->lock, RW_WRITER);
349 
350 	/* Get each properties */
351 	oplmsu_check_su = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
352 	    (DDI_PROP_DONTPASS|DDI_PROP_NOTPROM), "check-superuser", 1);
353 
354 	/*
355 	 * Initialize members of uinst_t
356 	 */
357 
358 	oplmsu_uinst->inst_status = INST_STAT_UNCONFIGURED;
359 	oplmsu_uinst->path_num = UNDEFINED;
360 	oplmsu_uinst->msu_dip = dip;
361 	(void) strcpy(oplmsu_uinst->abts, abt_ch_seq);
362 
363 #ifdef DEBUG
364 	oplmsu_trace_on = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
365 	    (DDI_PROP_DONTPASS|DDI_PROP_NOTPROM), "trace-mode", 1);
366 	oplmsu_ltrc_size = (uint_t)ddi_prop_get_int(DDI_DEV_T_ANY, dip,
367 	    (DDI_PROP_DONTPASS|DDI_PROP_NOTPROM), "trace-bufsize", 128);
368 
369 	if (oplmsu_trace_on == MSU_TRACE_ON) {
370 		/* Initialize mutex for msu_trc_t */
371 		mutex_init(&oplmsu_ltrc_lock, "trc lock", MUTEX_DRIVER, NULL);
372 
373 		mutex_enter(&oplmsu_ltrc_lock);
374 		oplmsu_ltrc_top = (msu_trc_t *)kmem_zalloc(
375 		    (sizeof (msu_trc_t) * oplmsu_ltrc_size), KM_SLEEP);
376 		oplmsu_ltrc_cur = (msu_trc_t *)(oplmsu_ltrc_top - 1);
377 		oplmsu_ltrc_tail =
378 		    (msu_trc_t *)(oplmsu_ltrc_top + (oplmsu_ltrc_size - 1));
379 		mutex_exit(&oplmsu_ltrc_lock);
380 	}
381 #endif
382 	rw_exit(&oplmsu_uinst->lock);
383 	ddi_report_dev(dip);
384 	return (rval);
385 }
386 
387 int
388 oplmsu_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
389 {
390 	lpath_t	*lpath, *next_lpath;
391 
392 	if (cmd == DDI_SUSPEND) {
393 		return (DDI_SUCCESS);
394 	}
395 
396 	if (cmd != DDI_DETACH) {
397 		return (DDI_FAILURE);
398 	}
399 
400 	rw_enter(&oplmsu_uinst->lock, RW_WRITER);
401 
402 	/* Delete all upath_t */
403 	oplmsu_delete_upath_info();
404 
405 	/* Delete all lpath_t */
406 	mutex_enter(&oplmsu_uinst->l_lock);
407 	lpath = oplmsu_uinst->first_lpath;
408 	oplmsu_uinst->first_lpath = NULL;
409 	oplmsu_uinst->last_lpath = NULL;
410 	mutex_exit(&oplmsu_uinst->l_lock);
411 
412 #ifdef DEBUG
413 	if (oplmsu_trace_on == MSU_TRACE_ON) {
414 		mutex_enter(&oplmsu_ltrc_lock);
415 		if (oplmsu_ltrc_top != NULL) {
416 			kmem_free(oplmsu_ltrc_top,
417 			    (sizeof (msu_trc_t) * oplmsu_ltrc_size));
418 		}
419 		oplmsu_ltrc_top = NULL;
420 		oplmsu_ltrc_cur = NULL;
421 		oplmsu_ltrc_tail = NULL;
422 		mutex_exit(&oplmsu_ltrc_lock);
423 
424 		mutex_destroy(&oplmsu_ltrc_lock);
425 	}
426 #endif
427 	rw_exit(&oplmsu_uinst->lock);
428 
429 	while (lpath) {
430 		if (lpath->rbuf_id) {
431 			unbufcall(lpath->rbuf_id);
432 		}
433 
434 		if (lpath->rtout_id) {
435 			untimeout(lpath->rtout_id);
436 		}
437 
438 		if (lpath->rbuftbl) {
439 			kmem_free(lpath->rbuftbl, sizeof (struct buf_tbl));
440 		}
441 
442 		cv_destroy(&lpath->sw_cv);
443 		next_lpath = lpath->l_next;
444 		kmem_free(lpath, sizeof (lpath_t));
445 		lpath = next_lpath;
446 	}
447 	ddi_remove_minor_node(dip, NULL);
448 	return (DDI_SUCCESS);
449 }
450 
451 /* ARGSUSED */
452 int
453 oplmsu_open(queue_t *urq, dev_t *dev, int oflag, int sflag, cred_t *cred_p)
454 {
455 	ctrl_t	*ctrl;
456 	minor_t	mindev = 0;
457 	minor_t	qmindev = 0;
458 	major_t	majdev;
459 	ulong_t	node_flag;
460 
461 	DBG_PRINT((CE_NOTE, "oplmsu: open: "
462 	    "devt = 0x%lx, sflag = 0x%x", *dev, sflag));
463 
464 	if (sflag == CLONEOPEN) {
465 		return (EINVAL);
466 	}
467 
468 	/* Get minor device number */
469 	qmindev = (minor_t)getminor(*dev);
470 	/* Get node type */
471 	node_flag = MSU_NODE_TYPE(qmindev);
472 	if ((node_flag != MSU_NODE_USER) && (node_flag != MSU_NODE_META)) {
473 		return (EINVAL);
474 	}
475 
476 	mutex_enter(&oplmsu_bthrd_excl);
477 	if ((node_flag == MSU_NODE_USER) &&
478 	    (oplmsu_conf_st != MSU_CONFIGURED)) { /* User access & First open */
479 		int	cv_rval;
480 
481 		DBG_PRINT((CE_NOTE, "oplmsu: open: "
482 		    "oplmsu_conf_st = %x", oplmsu_conf_st));
483 
484 		if (oplmsu_conf_st == MSU_UNCONFIGURED) {
485 			oplmsu_conf_st = MSU_CONFIGURING;
486 
487 			/* Start up background thread */
488 			oplmsu_bthrd_id = thread_create(NULL, 2 * DEFAULTSTKSZ,
489 			    oplmsu_setup, (void *)oplmsu_uinst, 0, &p0, TS_RUN,
490 			    minclsyspri);
491 		}
492 
493 		/*
494 		 * Wait with cv_wait_sig() until background thread is
495 		 * completed.
496 		 */
497 		while (oplmsu_conf_st == MSU_CONFIGURING) {
498 			cv_rval =
499 			    cv_wait_sig(&oplmsu_conf_cv, &oplmsu_bthrd_excl);
500 			if (cv_rval == 0) {
501 				mutex_exit(&oplmsu_bthrd_excl);
502 				return (EINTR);
503 			}
504 		}
505 	}
506 	mutex_exit(&oplmsu_bthrd_excl);
507 
508 	rw_enter(&oplmsu_uinst->lock, RW_WRITER);
509 
510 	/*
511 	 *  If the node which will open is meta-control-node or
512 	 * user-access-node, and q_ptr, this is queue_t queue
513 	 * table member, is not NULL, then oplmsu returns
514 	 * SUCCESS immidiately.
515 	 *  This process is used to protect dual open.
516 	 */
517 
518 	if ((urq != NULL) && (urq->q_ptr != NULL)) {
519 		rw_exit(&oplmsu_uinst->lock);
520 		return (SUCCESS);
521 	}
522 
523 	/*
524 	 *  If the node which will open is User-Access-Node, and instance
525 	 * status of oplmsu is no ONLINE, then oplmsu_open process fails
526 	 * with return value 'EIO'.
527 	 */
528 
529 	if ((node_flag == MSU_NODE_USER) &&
530 	    (oplmsu_uinst->inst_status != INST_STAT_ONLINE)) {
531 		rw_exit(&oplmsu_uinst->lock);
532 		return (EIO);
533 	}
534 
535 	mindev |= qmindev;			/* Create minor device number */
536 	majdev = getmajor(*dev);		/* Get major device number */
537 	*dev = makedevice(majdev, mindev);	/* Make device number */
538 
539 	/* Allocate kernel memory for ctrl_t */
540 	ctrl = (ctrl_t *)kmem_zalloc(sizeof (ctrl_t), KM_SLEEP);
541 
542 	/*
543 	 * Initialize members of ctrl_t
544 	 */
545 	ctrl->minor = (minor_t)mindev;
546 	ctrl->queue = urq;
547 	ctrl->sleep_flag = CV_WAKEUP;
548 	ctrl->node_type = node_flag;
549 	ctrl->wbuftbl =
550 	    (struct buf_tbl *)kmem_zalloc(sizeof (struct buf_tbl), KM_SLEEP);
551 	cv_init(&ctrl->cvp, "oplmsu ctrl_tbl condvar", CV_DRIVER, NULL);
552 
553 	mutex_enter(&oplmsu_uinst->c_lock);
554 
555 	if (node_flag == MSU_NODE_USER) {	/* User access node */
556 
557 		oplmsu_uinst->user_ctrl = ctrl;
558 		oplmsu_queue_flag = 0;
559 
560 	} else {	/* Meta control node */
561 
562 		oplmsu_uinst->meta_ctrl = ctrl;
563 	}
564 
565 	RD(urq)->q_ptr = ctrl;
566 	WR(urq)->q_ptr = ctrl;
567 
568 	mutex_exit(&oplmsu_uinst->c_lock);
569 	rw_exit(&oplmsu_uinst->lock);
570 
571 	OPLMSU_TRACE(urq, (mblk_t *)node_flag, MSU_TRC_OPN);
572 
573 	qprocson(urq);	/* Enable put and service routine */
574 	return (SUCCESS);
575 }
576 
577 /* ARGSUSED */
578 int
579 oplmsu_close(queue_t *urq, int flag, cred_t *cred_p)
580 {
581 	ctrl_t		*ctrl;
582 	minor_t		qmindev = 0;
583 	lpath_t		*lpath;
584 	ulong_t		node_flag;
585 	bufcall_id_t	wbuf_id;
586 	timeout_id_t	wtout_id;
587 
588 	rw_enter(&oplmsu_uinst->lock, RW_READER);
589 	mutex_enter(&oplmsu_uinst->l_lock);
590 	mutex_enter(&oplmsu_uinst->c_lock);
591 	if ((ctrl = urq->q_ptr) == NULL) {
592 		mutex_exit(&oplmsu_uinst->c_lock);
593 		mutex_exit(&oplmsu_uinst->l_lock);
594 		rw_exit(&oplmsu_uinst->lock);
595 
596 		DBG_PRINT((CE_NOTE, "oplmsu: close: "
597 		    "close has already been completed"));
598 		return (FAILURE);
599 	}
600 	qmindev = ctrl->minor;
601 
602 	DBG_PRINT((CE_NOTE, "oplmsu: close: ctrl->minor = 0x%x", qmindev));
603 
604 	node_flag = MSU_NODE_TYPE(qmindev);
605 	if (node_flag > MSU_NODE_META) {
606 		mutex_exit(&oplmsu_uinst->c_lock);
607 		mutex_exit(&oplmsu_uinst->l_lock);
608 		rw_exit(&oplmsu_uinst->lock);
609 		return (EINVAL);
610 	}
611 
612 	/*
613 	 *  Check that queue which is waiting for response from lower stream
614 	 * exist. If queue exists, oplmsu sets CV_SLEEP to sleep_flag.
615 	 */
616 
617 	for (lpath = oplmsu_uinst->first_lpath; lpath; ) {
618 		if (((RD(urq) == lpath->hndl_uqueue) ||
619 		    (WR(urq) == lpath->hndl_uqueue)) &&
620 		    (lpath->hndl_mp != NULL)) {
621 			ctrl->sleep_flag = CV_SLEEP;
622 			break;
623 		}
624 
625 		lpath = lpath->l_next;
626 	}
627 	mutex_exit(&oplmsu_uinst->l_lock);
628 	rw_exit(&oplmsu_uinst->lock);
629 
630 	/* If sleep_flag is not CV_SLEEP, oplmsu calls cv_wait. */
631 	if (lpath) {
632 		while (ctrl->sleep_flag != CV_WAKEUP) {
633 			cv_wait(&ctrl->cvp, &oplmsu_uinst->c_lock);
634 		}
635 	}
636 
637 	flushq(RD(urq), FLUSHALL);
638 	flushq(WR(urq), FLUSHALL);
639 	mutex_exit(&oplmsu_uinst->c_lock);
640 	qprocsoff(urq);		/* Disable queuing of queue */
641 
642 	rw_enter(&oplmsu_uinst->lock, RW_WRITER);
643 	switch (node_flag) {
644 	case MSU_NODE_USER :	/* User access node */
645 		oplmsu_uinst->user_ctrl = NULL;
646 		oplmsu_queue_flag = 0;
647 		break;
648 
649 	case MSU_NODE_META :	/* Meta control node */
650 		oplmsu_uinst->meta_ctrl = NULL;
651 		break;
652 
653 	default :
654 		cmn_err(CE_WARN, "oplmsu: close: node_flag = 0x%lx", node_flag);
655 	}
656 
657 	ctrl->minor = NULL;
658 	ctrl->queue = NULL;
659 	wbuf_id = ctrl->wbuf_id;
660 	wtout_id = ctrl->wtout_id;
661 	ctrl->wbuf_id = 0;
662 	ctrl->wtout_id = 0;
663 
664 	cv_destroy(&ctrl->cvp);
665 	kmem_free(ctrl->wbuftbl, sizeof (struct buf_tbl));
666 	ctrl->wbuftbl = NULL;
667 
668 	RD(urq)->q_ptr = NULL;
669 	WR(urq)->q_ptr = NULL;
670 	rw_exit(&oplmsu_uinst->lock);
671 
672 	if (wbuf_id != 0) {
673 		unbufcall(wbuf_id);
674 	}
675 
676 	if (wtout_id != 0) {
677 		untimeout(wtout_id);
678 	}
679 
680 	/* Free kernel memory for ctrl_t */
681 	kmem_free(ctrl, sizeof (ctrl_t));
682 
683 	OPLMSU_TRACE(urq, (mblk_t *)node_flag, MSU_TRC_CLS);
684 	return (SUCCESS);
685 }
686 
687 /*
688  * Upper write put procedure
689  */
690 int
691 oplmsu_uwput(queue_t *uwq, mblk_t *mp)
692 {
693 
694 	if (mp == NULL) {
695 		return (SUCCESS);
696 	}
697 
698 	if ((uwq == NULL) || (uwq->q_ptr == NULL)) {
699 		freemsg(mp);
700 		return (SUCCESS);
701 	}
702 
703 	OPLMSU_TRACE(uwq, mp, MSU_TRC_UI);
704 
705 	rw_enter(&oplmsu_uinst->lock, RW_READER);
706 	if (mp->b_datap->db_type == M_FLUSH) {
707 		oplmsu_wcmn_flush_hndl(uwq, mp, RW_READER);
708 	} else if (mp->b_datap->db_type >= QPCTL) {
709 		ctrl_t	*ctrl;
710 
711 		mutex_enter(&oplmsu_uinst->c_lock);
712 		ctrl = (ctrl_t *)uwq->q_ptr;
713 
714 		/* Link high priority message to local queue */
715 		oplmsu_link_high_primsg(&ctrl->first_upri_hi,
716 		    &ctrl->last_upri_hi, mp);
717 
718 		mutex_exit(&oplmsu_uinst->c_lock);
719 		oplmsu_wcmn_high_qenable(WR(uwq), RW_READER);
720 	} else {
721 		putq(WR(uwq), mp);
722 	}
723 	rw_exit(&oplmsu_uinst->lock);
724 	return (SUCCESS);
725 }
726 
727 /*
728  * Upper write service procedure
729  */
730 int
731 oplmsu_uwsrv(queue_t *uwq)
732 {
733 	struct iocblk	*iocp = NULL;
734 	mblk_t		*mp = NULL;
735 	int		rval;
736 
737 	if ((uwq == NULL) || (uwq->q_ptr == NULL)) {
738 		return (FAILURE);
739 	}
740 
741 	rw_enter(&oplmsu_uinst->lock, RW_READER);
742 
743 	/* Handle high priority message */
744 	while (mp = oplmsu_wcmn_high_getq(uwq)) {
745 		if (mp->b_datap->db_type == M_FLUSH) {
746 			oplmsu_wcmn_flush_hndl(uwq, mp, RW_READER);
747 			continue;
748 		}
749 
750 		if (oplmsu_wcmn_through_hndl(uwq, mp, MSU_HIGH, RW_READER) ==
751 		    FAILURE) {
752 			rw_exit(&oplmsu_uinst->lock);
753 			return (SUCCESS);
754 		}
755 	}
756 	rw_exit(&oplmsu_uinst->lock);
757 
758 	/* Handle normal priority message */
759 	while (mp = getq(uwq)) {
760 		rval = SUCCESS;
761 		switch (mp->b_datap->db_type) {
762 		case M_IOCTL :
763 			iocp = (struct iocblk *)mp->b_rptr;
764 			switch (iocp->ioc_cmd) {
765 			case I_PLINK :
766 				if (oplmsu_cmn_pullup_msg(uwq, mp) != FAILURE) {
767 					rval = oplmsu_uwioctl_iplink(uwq, mp);
768 				}
769 				break;
770 
771 			case I_PUNLINK :
772 				if (oplmsu_cmn_pullup_msg(uwq, mp) != FAILURE) {
773 					rval = oplmsu_uwioctl_ipunlink(uwq, mp);
774 				}
775 				break;
776 
777 			case TCSETS :		/* FALLTHRU */
778 			case TCSETSW :		/* FALLTHRU */
779 			case TCSETSF :		/* FALLTHRU */
780 			case TIOCMSET :		/* FALLTHRU */
781 			case TIOCSPPS :		/* FALLTHRU */
782 			case TIOCSWINSZ :	/* FALLTHRU */
783 			case TIOCSSOFTCAR :
784 				rval = oplmsu_uwioctl_termios(uwq, mp);
785 				break;
786 
787 			default :
788 				rw_enter(&oplmsu_uinst->lock, RW_READER);
789 				rval = oplmsu_wcmn_through_hndl(uwq, mp,
790 				    MSU_NORM, RW_READER);
791 				rw_exit(&oplmsu_uinst->lock);
792 				break;
793 			}
794 			break;
795 
796 		default :
797 			rw_enter(&oplmsu_uinst->lock, RW_READER);
798 			rval = oplmsu_wcmn_through_hndl(uwq, mp, MSU_NORM,
799 			    RW_READER);
800 			rw_exit(&oplmsu_uinst->lock);
801 			break;
802 		}
803 
804 		if (rval == FAILURE) {
805 			break;
806 		}
807 	}
808 	return (SUCCESS);
809 }
810 
811 /*
812  * Lower write service procedure
813  */
814 int
815 oplmsu_lwsrv(queue_t *lwq)
816 {
817 	mblk_t		*mp;
818 	queue_t		*dst_queue;
819 	lpath_t		*lpath;
820 
821 	rw_enter(&oplmsu_uinst->lock, RW_READER);
822 	while (mp = getq(lwq)) {
823 		if (mp->b_datap->db_type >= QPCTL) {
824 			rw_exit(&oplmsu_uinst->lock);
825 			OPLMSU_TRACE(WR(lwq), mp, MSU_TRC_LO);
826 			putnext(WR(lwq), mp);
827 			rw_enter(&oplmsu_uinst->lock, RW_READER);
828 			continue;
829 		}
830 
831 		dst_queue = WR(lwq);
832 		if (canputnext(dst_queue)) {
833 			rw_exit(&oplmsu_uinst->lock);
834 			OPLMSU_TRACE(dst_queue, mp, MSU_TRC_LO);
835 			putnext(dst_queue, mp);
836 			rw_enter(&oplmsu_uinst->lock, RW_READER);
837 		} else {
838 			putbq(WR(lwq), mp);
839 			break;
840 		}
841 	}
842 
843 	mutex_enter(&oplmsu_uinst->l_lock);
844 	lpath = (lpath_t *)lwq->q_ptr;
845 	if (lpath->uwq_flag != 0) {
846 		qenable(WR(lpath->uwq_queue));
847 		lpath->uwq_flag = 0;
848 		lpath->uwq_queue = NULL;
849 	}
850 	mutex_exit(&oplmsu_uinst->l_lock);
851 	rw_exit(&oplmsu_uinst->lock);
852 	return (SUCCESS);
853 }
854 
855 /*
856  * Lower read put procedure
857  */
858 int
859 oplmsu_lrput(queue_t *lrq, mblk_t *mp)
860 {
861 
862 	if (mp == NULL) {
863 		return (SUCCESS);
864 	}
865 
866 	if ((lrq == NULL) || (lrq->q_ptr == NULL)) {
867 		freemsg(mp);
868 		return (SUCCESS);
869 	}
870 
871 	OPLMSU_TRACE(lrq, mp, MSU_TRC_LI);
872 
873 	if (mp->b_datap->db_type == M_FLUSH) {
874 		rw_enter(&oplmsu_uinst->lock, RW_READER);
875 		oplmsu_rcmn_flush_hndl(lrq, mp);
876 		rw_exit(&oplmsu_uinst->lock);
877 	} else if (mp->b_datap->db_type >= QPCTL) {
878 		lpath_t	*lpath;
879 
880 		rw_enter(&oplmsu_uinst->lock, RW_READER);
881 		mutex_enter(&oplmsu_uinst->l_lock);
882 		lpath = lrq->q_ptr;
883 
884 		/* Link high priority message to local queue */
885 		oplmsu_link_high_primsg(&lpath->first_lpri_hi,
886 		    &lpath->last_lpri_hi, mp);
887 
888 		mutex_exit(&oplmsu_uinst->l_lock);
889 		rw_exit(&oplmsu_uinst->lock);
890 		oplmsu_rcmn_high_qenable(lrq);
891 	} else {
892 		putq(lrq, mp);
893 	}
894 	return (SUCCESS);
895 }
896 
897 /*
898  * Lower read service procedure
899  */
900 int
901 oplmsu_lrsrv(queue_t *lrq)
902 {
903 	mblk_t		*mp;
904 	boolean_t	aborted;
905 	int		rval;
906 
907 	if ((lrq == NULL) || (lrq->q_ptr == NULL)) {
908 		return (FAILURE);
909 	}
910 
911 	/* Handle normal priority message */
912 	while (mp = getq(lrq)) {
913 		if (mp->b_datap->db_type >= QPCTL) {
914 			cmn_err(CE_WARN, "oplmsu: lr-srv: "
915 			    "Invalid db_type => %x", mp->b_datap->db_type);
916 		}
917 
918 		switch (mp->b_datap->db_type) {
919 		case M_DATA :
920 			aborted = B_FALSE;
921 			rw_enter(&oplmsu_uinst->lock, RW_READER);
922 			if ((abort_enable == KIOCABORTALTERNATE) &&
923 			    (RD(oplmsu_uinst->lower_queue) == lrq)) {
924 				uchar_t	*rx_char = mp->b_rptr;
925 				lpath_t	*lpath;
926 
927 				mutex_enter(&oplmsu_uinst->l_lock);
928 				lpath = lrq->q_ptr;
929 				while (rx_char != mp->b_wptr) {
930 				    if (*rx_char == *lpath->abt_char) {
931 					lpath->abt_char++;
932 					if (*lpath->abt_char == '\0') {
933 					    abort_sequence_enter((char *)NULL);
934 					    lpath->abt_char
935 						= oplmsu_uinst->abts;
936 					    aborted = B_TRUE;
937 					    break;
938 					}
939 				    } else {
940 					lpath->abt_char = (*rx_char ==
941 					    *oplmsu_uinst->abts) ?
942 						oplmsu_uinst->abts + 1 :
943 						oplmsu_uinst->abts;
944 				    }
945 				    rx_char++;
946 				}
947 				mutex_exit(&oplmsu_uinst->l_lock);
948 			}
949 			rw_exit(&oplmsu_uinst->lock);
950 
951 			if (aborted) {
952 				freemsg(mp);
953 				continue;
954 			}
955 
956 			/*
957 			 * When 1st byte of the received M_DATA is XON or,
958 			 * 1st byte is XOFF and 2nd byte is XON.
959 			 */
960 
961 			if ((*(mp->b_rptr) == MSU_XON) ||
962 			    (((mp->b_wptr - mp->b_rptr) == 2) &&
963 			    ((*(mp->b_rptr) == MSU_XOFF) &&
964 			    (*(mp->b_rptr + 1) == MSU_XON)))) {
965 				/* Path switching by XOFF/XON */
966 				if (oplmsu_lrdata_xoffxon(lrq, mp) == FAILURE) {
967 					return (SUCCESS);
968 				}
969 			} else {
970 				rw_enter(&oplmsu_uinst->lock, RW_READER);
971 				rval =
972 				    oplmsu_rcmn_through_hndl(lrq, mp, MSU_NORM);
973 				rw_exit(&oplmsu_uinst->lock);
974 
975 				if (rval == FAILURE) {
976 					return (SUCCESS);
977 				}
978 			}
979 			break;
980 
981 		case M_BREAK :
982 			if ((mp->b_wptr - mp->b_rptr) == 0 && msgdsize(mp)
983 			    == 0) {
984 				rw_enter(&oplmsu_uinst->lock, RW_READER);
985 				if ((abort_enable != KIOCABORTALTERNATE) &&
986 				    (RD(oplmsu_uinst->lower_queue) == lrq)) {
987 					abort_sequence_enter((char *)NULL);
988 				}
989 				rw_exit(&oplmsu_uinst->lock);
990 				freemsg(mp);
991 				break;
992 			}
993 			/* FALLTHRU */
994 
995 		default :
996 			rw_enter(&oplmsu_uinst->lock, RW_READER);
997 			(void) oplmsu_rcmn_through_hndl(lrq, mp, MSU_NORM);
998 			rw_exit(&oplmsu_uinst->lock);
999 			break;
1000 		}
1001 	}
1002 	return (SUCCESS);
1003 }
1004 
1005 /*
1006  * Upper read service procedure
1007  */
1008 int
1009 oplmsu_ursrv(queue_t *urq)
1010 {
1011 	mblk_t	*mp;
1012 	queue_t	*dst_queue;
1013 	lpath_t	*lpath;
1014 	ctrl_t	*ctrl;
1015 	int	res_chk = 0;
1016 
1017 	rw_enter(&oplmsu_uinst->lock, RW_READER);
1018 	while (mp = getq(urq)) {
1019 		if (mp->b_datap->db_type >= QPCTL) {
1020 			if ((mp->b_datap->db_type == M_IOCACK) ||
1021 			    (mp->b_datap->db_type == M_IOCNAK)) {
1022 				res_chk = 1;
1023 			}
1024 			rw_exit(&oplmsu_uinst->lock);
1025 			OPLMSU_TRACE(RD(urq), mp, MSU_TRC_UO);
1026 			putnext(RD(urq), mp);
1027 
1028 			rw_enter(&oplmsu_uinst->lock, RW_READER);
1029 			mutex_enter(&oplmsu_uinst->l_lock);
1030 			lpath = oplmsu_uinst->first_lpath;
1031 			while (lpath) {
1032 				qenable(RD(lpath->lower_queue));
1033 				lpath = lpath->l_next;
1034 			}
1035 			mutex_exit(&oplmsu_uinst->l_lock);
1036 
1037 			if (res_chk == 1) {
1038 				mutex_enter(&oplmsu_uinst->c_lock);
1039 				ctrl = (ctrl_t *)urq->q_ptr;
1040 				if (ctrl != NULL) {
1041 					if (ctrl->wait_queue != NULL) {
1042 						qenable(WR(ctrl->wait_queue));
1043 						ctrl->wait_queue = NULL;
1044 					}
1045 				}
1046 				mutex_exit(&oplmsu_uinst->c_lock);
1047 				res_chk = 0;
1048 			}
1049 			continue;
1050 		}
1051 
1052 		dst_queue = RD(urq);
1053 		if (canputnext(dst_queue)) {
1054 			rw_exit(&oplmsu_uinst->lock);
1055 			OPLMSU_TRACE(dst_queue, mp, MSU_TRC_UO);
1056 			putnext(dst_queue, mp);
1057 			rw_enter(&oplmsu_uinst->lock, RW_READER);
1058 		} else {
1059 			putbq(urq, mp);
1060 			break;
1061 		}
1062 	}
1063 
1064 	mutex_enter(&oplmsu_uinst->c_lock);
1065 	ctrl = urq->q_ptr;
1066 	if (ctrl->lrq_flag != 0) {
1067 		qenable(ctrl->lrq_queue);
1068 		ctrl->lrq_flag = 0;
1069 		ctrl->lrq_queue = NULL;
1070 	}
1071 	mutex_exit(&oplmsu_uinst->c_lock);
1072 	rw_exit(&oplmsu_uinst->lock);
1073 	return (SUCCESS);
1074 }
1075 
1076 int
1077 oplmsu_open_msu(dev_info_t *dip, ldi_ident_t *lip, ldi_handle_t *lhp)
1078 {
1079 	dev_t	devt;
1080 	int	rval;
1081 
1082 	/* Allocate LDI identifier */
1083 	rval = ldi_ident_from_dip(dip, lip);
1084 	if (rval != 0) {
1085 		cmn_err(CE_WARN, "oplmsu: open-msu: "
1086 		    "ldi_ident_from_dip failed. errno = %d", rval);
1087 		return (rval);
1088 	}
1089 
1090 	/* Open oplmsu(meta ctrl node) */
1091 	devt = makedevice(ddi_driver_major(dip), META_NODE_MASK);
1092 	rval =
1093 	    ldi_open_by_dev(&devt, OTYP_CHR, (FREAD|FWRITE), kcred, lhp, *lip);
1094 	if (rval != 0) {
1095 		cmn_err(CE_WARN, "oplmsu: open-msu: "
1096 		    "ldi_open_by_dev failed. errno = %d", rval);
1097 		ldi_ident_release(*lip);
1098 	}
1099 	return (rval);
1100 }
1101 
1102 int
1103 oplmsu_plink_serial(dev_info_t *dip, ldi_handle_t msu_lh, int *id)
1104 {
1105 	ldi_ident_t	li = NULL;
1106 	ldi_handle_t	lh = NULL;
1107 	int		param;
1108 	int		rval;
1109 	char		pathname[MSU_PATHNAME_SIZE];
1110 	char		wrkbuf[MSU_PATHNAME_SIZE];
1111 
1112 	/* Create physical path-name for serial */
1113 	ddi_pathname(dip, wrkbuf);
1114 	*(wrkbuf + strlen(wrkbuf)) = '\0';
1115 	sprintf(pathname, "/devices%s:%c", wrkbuf, 'a'+ ddi_get_instance(dip));
1116 
1117 	/* Allocate LDI identifier */
1118 	rval = ldi_ident_from_dip(dip, &li);
1119 	if (rval != 0) {
1120 		cmn_err(CE_WARN, "oplmsu: plink-serial: "
1121 		    "%s ldi_ident_from_dip failed. errno = %d", pathname, rval);
1122 		return (rval);
1123 	}
1124 
1125 	/* Open serial */
1126 	rval = ldi_open_by_name(pathname, (FREAD|FWRITE|FEXCL), kcred, &lh, li);
1127 	if (rval != 0) {
1128 		cmn_err(CE_WARN, "oplmsu: plink-serial: "
1129 		    "%s open failed. errno = %d", pathname, rval);
1130 		ldi_ident_release(li);
1131 		return (rval);
1132 	}
1133 
1134 	/* Try to remove the top module from the stream */
1135 	param = 0;
1136 	while ((ldi_ioctl(lh, I_POP, (intptr_t)0, FKIOCTL, kcred, &param))
1137 	    == 0) {
1138 		continue;
1139 	}
1140 
1141 	/* Issue ioctl(I_PLINK) */
1142 	param = 0;
1143 	rval = ldi_ioctl(msu_lh, I_PLINK, (intptr_t)lh, FKIOCTL, kcred, &param);
1144 	if (rval != 0) {
1145 		cmn_err(CE_WARN, "oplmsu: plink-serial: "
1146 		    "%s ioctl(I_PLINK) failed. errno = %d", pathname, rval);
1147 	}
1148 
1149 	(void) ldi_close(lh, (FREAD|FWRITE|FEXCL), kcred);
1150 	ldi_ident_release(li);
1151 
1152 	*id = param;	/* Save link-id */
1153 	return (rval);
1154 }
1155 
1156 int
1157 oplmsu_set_lpathnum(int lnk_id, int instance)
1158 {
1159 	lpath_t	*lpath;
1160 	int	rval = SUCCESS;
1161 
1162 	rw_enter(&oplmsu_uinst->lock, RW_READER);
1163 	mutex_enter(&oplmsu_uinst->l_lock);
1164 	lpath = oplmsu_uinst->first_lpath;
1165 	while (lpath) {
1166 		if ((lpath->path_no == UNDEFINED) &&
1167 		    (lpath->link_id == lnk_id)) {
1168 			lpath->path_no = instance; /* Set instance number */
1169 			lpath->src_upath = NULL;
1170 			lpath->status = MSU_SETID_NU;
1171 			break;
1172 		}
1173 		lpath = lpath->l_next;
1174 	}
1175 	mutex_exit(&oplmsu_uinst->l_lock);
1176 	rw_exit(&oplmsu_uinst->lock);
1177 
1178 	if (lpath == NULL) {
1179 		rval = EINVAL;
1180 	}
1181 	return (rval);
1182 }
1183 
1184 int
1185 oplmsu_dr_attach(dev_info_t *dip)
1186 {
1187 	ldi_ident_t	msu_li = NULL;
1188 	ldi_handle_t	msu_lh = NULL;
1189 	upath_t		*upath;
1190 	int		len;
1191 	int		instance;
1192 	int		lnk_id = 0;
1193 	int		param = 0;
1194 	int		rval;
1195 
1196 	/* Get instance for serial */
1197 	instance = ddi_get_instance(dip);
1198 
1199 	rw_enter(&oplmsu_uinst->lock, RW_WRITER);
1200 	mutex_enter(&oplmsu_uinst->u_lock);
1201 
1202 	/* Get current number of paths */
1203 	oplmsu_uinst->path_num = oplmsu_get_pathnum();
1204 
1205 	/* Check specified upath_t */
1206 	upath = oplmsu_uinst->first_upath;
1207 	while (upath) {
1208 		if (instance == upath->path_no) {
1209 			break;
1210 		}
1211 		upath = upath->u_next;
1212 	}
1213 	mutex_exit(&oplmsu_uinst->u_lock);
1214 	rw_exit(&oplmsu_uinst->lock);
1215 
1216 	if (upath != NULL) {
1217 		cmn_err(CE_WARN, "oplmsu: attach(dr): "
1218 		    "Instance %d already exist", instance);
1219 		return (EINVAL);
1220 	}
1221 
1222 	/* Open oplmsu */
1223 	rval = oplmsu_open_msu(oplmsu_uinst->msu_dip, &msu_li, &msu_lh);
1224 	if (rval != 0) {
1225 		cmn_err(CE_WARN, "oplmsu: attach(dr): "
1226 		    "msu open failed. errno = %d", rval);
1227 		return (rval);
1228 	}
1229 
1230 	/* Connect two streams */
1231 	rval = oplmsu_plink_serial(dip, msu_lh, &lnk_id);
1232 	if (rval != 0) {
1233 		cmn_err(CE_WARN, "oplmsu: attach(dr): "
1234 		    "i_plink failed. errno = %d", rval);
1235 		(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
1236 		ldi_ident_release(msu_li);
1237 		return (rval);
1238 	}
1239 
1240 	rval = oplmsu_set_lpathnum(lnk_id, instance);
1241 	if (rval != 0) {
1242 		cmn_err(CE_WARN, "oplmsu: attach(dr): "
1243 		    "Link id %d is not found", lnk_id);
1244 		/* Issue ioctl(I_PUNLINK) */
1245 		(void) ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)lnk_id, FKIOCTL,
1246 		    kcred, &param);
1247 		(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
1248 		ldi_ident_release(msu_li);
1249 		return (rval);
1250 	}
1251 
1252 	/* Add the path */
1253 	rval = oplmsu_config_add(dip);
1254 	if (rval != 0) {
1255 		cmn_err(CE_WARN, "oplmsu: attach(dr): "
1256 		    "Failed to add the path. errno = %d", rval);
1257 		/* Issue ioctl(I_PUNLINK) */
1258 		(void) ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)lnk_id, FKIOCTL,
1259 		    kcred, &param);
1260 
1261 		(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
1262 		ldi_ident_release(msu_li);
1263 		return (rval);
1264 	}
1265 
1266 	/* Start to use the path */
1267 	rval = oplmsu_config_start(instance);
1268 	if (rval != 0) {
1269 		struct msu_path	*mpath;
1270 		struct msu_dev	*mdev;
1271 
1272 		cmn_err(CE_WARN, "oplmsu: attach(dr): "
1273 		    "Failed to start the path. errno = %d", rval);
1274 
1275 		len = sizeof (struct msu_path) + sizeof (struct msu_dev);
1276 		mpath = (struct msu_path *)kmem_zalloc((size_t)len, KM_SLEEP);
1277 		mpath->num = 1;
1278 		mdev = (struct msu_dev *)(mpath + 1);
1279 		mdev->dip = dip;
1280 
1281 		/* Delete the path */
1282 		if ((oplmsu_config_del(mpath)) == 0) {
1283 			/* Issue ioctl(I_PUNLINK) */
1284 			(void) ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)lnk_id,
1285 			    FKIOCTL, kcred, &param);
1286 		}
1287 		kmem_free(mpath, (size_t)len);
1288 	}
1289 
1290 	/* Close oplmsu */
1291 	(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
1292 	ldi_ident_release(msu_li);
1293 	return (rval);
1294 }
1295 
1296 int
1297 oplmsu_dr_detach(dev_info_t *dip)
1298 {
1299 	ldi_ident_t	msu_li = NULL;
1300 	ldi_handle_t	msu_lh = NULL;
1301 	struct msu_path	*mpath;
1302 	struct msu_dev	*mdev;
1303 	upath_t		*upath;
1304 	lpath_t		*lpath;
1305 	int		len;
1306 	int		instance;
1307 	int		count = 0;
1308 	int		param = 0;
1309 	int		status;
1310 	int		rval;
1311 
1312 	/* Get instance for serial */
1313 	instance = ddi_get_instance(dip);
1314 
1315 	rw_enter(&oplmsu_uinst->lock, RW_WRITER);
1316 	mutex_enter(&oplmsu_uinst->u_lock);
1317 
1318 	/* Get current number of paths */
1319 	oplmsu_uinst->path_num = oplmsu_get_pathnum();
1320 
1321 	rval = FAILURE;
1322 
1323 	/* Check specified upath_t */
1324 	upath = oplmsu_uinst->first_upath;
1325 	while (upath) {
1326 		if (instance == upath->path_no) {
1327 			/* Save status of specified path */
1328 			status = upath->status;
1329 			rval = SUCCESS;
1330 		}
1331 		upath = upath->u_next;
1332 		count += 1;
1333 	}
1334 	mutex_exit(&oplmsu_uinst->u_lock);
1335 	rw_exit(&oplmsu_uinst->lock);
1336 
1337 	if (rval == FAILURE) {
1338 		if (count <= 1) {
1339 			cmn_err(CE_WARN, "oplmsu: detach(dr): "
1340 			    "Instance %d is last path", instance);
1341 		} else {
1342 			cmn_err(CE_WARN, "oplmsu: detach(dr): "
1343 			    "Instance %d doesn't find", instance);
1344 		}
1345 		return (EINVAL);
1346 	}
1347 
1348 	/* Check status of specified path */
1349 	if ((status == MSU_PSTAT_ACTIVE) || (status == MSU_PSTAT_STANDBY)) {
1350 		/* Stop to use the path */
1351 		rval = oplmsu_config_stop(instance);
1352 		if (rval != 0) {
1353 			cmn_err(CE_WARN, "oplmsu: detach(dr): "
1354 			    "Failed to stop the path. errno = %d", rval);
1355 			return (rval);
1356 		}
1357 	}
1358 
1359 	/* Prepare to unlink the path */
1360 	rval = oplmsu_config_disc(instance);
1361 	if (rval != 0) {
1362 		cmn_err(CE_WARN, "oplmsu: detach(dr): "
1363 		    "Failed to disconnect the path. errno = %d", rval);
1364 		return (rval);
1365 	}
1366 
1367 	rw_enter(&oplmsu_uinst->lock, RW_READER);
1368 	mutex_enter(&oplmsu_uinst->l_lock);
1369 	lpath = oplmsu_uinst->first_lpath;
1370 	while (lpath) {
1371 		if (lpath->path_no == instance) { /* Get link ID */
1372 			break;
1373 		}
1374 		lpath = lpath->l_next;
1375 	}
1376 	mutex_exit(&oplmsu_uinst->l_lock);
1377 	rw_exit(&oplmsu_uinst->lock);
1378 
1379 	if (lpath == NULL) {
1380 		cmn_err(CE_WARN, "oplmsu: detach(dr): Can not find link ID");
1381 		return (EINVAL);
1382 	}
1383 
1384 	/* Open oplmsu */
1385 	rval = oplmsu_open_msu(oplmsu_uinst->msu_dip, &msu_li, &msu_lh);
1386 	if (rval != 0) {
1387 		cmn_err(CE_WARN, "oplmsu: detach(dr): "
1388 		    "msu open failed. errno = %d", rval);
1389 		return (rval);
1390 	}
1391 
1392 	/* Issue ioctl(I_PUNLINK) */
1393 	rval = ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)lpath->link_id, FKIOCTL,
1394 	    kcred, &param);
1395 	if (rval != 0) {
1396 		cmn_err(CE_WARN, "oplmsu: detach(dr): "
1397 		    "ioctl(I_PUNLINK) failed. errno = %d", rval);
1398 		(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
1399 		ldi_ident_release(msu_li);
1400 		return (rval);
1401 	}
1402 
1403 	/* Close oplmsu(meta node) */
1404 	(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
1405 	ldi_ident_release(msu_li);
1406 
1407 	len = sizeof (struct msu_path) + sizeof (struct msu_dev);
1408 	mpath = (struct msu_path *)kmem_zalloc((size_t)len, KM_SLEEP);
1409 	mpath->num = 1;
1410 	mdev = (struct msu_dev *)(mpath + 1);
1411 	mdev->dip = dip;
1412 
1413 	/* Delete the path */
1414 	rval = oplmsu_config_del(mpath);
1415 	if (rval != 0) {
1416 		cmn_err(CE_WARN, "oplmsu: detach(dr): "
1417 		    "Failed to delete the path. errno = %d", rval);
1418 	}
1419 
1420 	kmem_free(mpath, (size_t)len);
1421 	return (rval);
1422 }
1423 
1424 /*
1425  * The ebus and the serial device path under a given CMU_CH chip
1426  * is expected to be always at the same address. So, it is safe
1427  * to hard-code the pathnames as below.
1428  */
1429 #define	EBUS_PATH		"ebus@1"
1430 #define	SERIAL_PATH		"serial@14,400000"
1431 #define	EBUS_SERIAL_PATH	("/" EBUS_PATH "/" SERIAL_PATH)
1432 
1433 /*
1434  * Given the CMU_CH dip, find the serial device dip.
1435  */
1436 dev_info_t *
1437 oplmsu_find_ser_dip(dev_info_t *cmuch_dip)
1438 {
1439 	int		circ1, circ2;
1440 	dev_info_t	*ebus_dip;
1441 	dev_info_t	*ser_dip = NULL;
1442 
1443 	ndi_devi_enter(cmuch_dip, &circ1);
1444 	ebus_dip = ndi_devi_findchild(cmuch_dip, EBUS_PATH);
1445 
1446 	DBG_PRINT((CE_NOTE, "oplmsu: find-serial-dip: "
1447 	    "ebus_dip = %p", ebus_dip));
1448 
1449 	if (ebus_dip != NULL) {
1450 		ndi_devi_enter(ebus_dip, &circ2);
1451 		ser_dip = ndi_devi_findchild(ebus_dip, SERIAL_PATH);
1452 
1453 		DBG_PRINT((CE_NOTE, "oplmsu: find-serial-dip: "
1454 		    "ser_dip = %p", ser_dip));
1455 		ndi_devi_exit(ebus_dip, circ2);
1456 	}
1457 	ndi_devi_exit(cmuch_dip, circ1);
1458 	return (ser_dip);
1459 }
1460 
1461 /*
1462  * Find all console related serial devices.
1463  */
1464 int
1465 oplmsu_find_serial(ser_devl_t **ser_dl)
1466 {
1467 	dev_info_t	*root_dip;
1468 	dev_info_t	*cmuch_dip;
1469 	dev_info_t	*dip;
1470 	ser_devl_t	*wrk_ser_dl;
1471 	int		circ;
1472 	int		count = 0;
1473 	char		pathname[MSU_PATHNAME_SIZE];
1474 	dev_t		devt;
1475 	char		*namep;
1476 
1477 	root_dip = ddi_root_node();
1478 	ndi_devi_enter(root_dip, &circ);
1479 	cmuch_dip = ddi_get_child(root_dip);
1480 
1481 	while (cmuch_dip != NULL) {
1482 		namep = ddi_binding_name(cmuch_dip);	/* Get binding name */
1483 		if (namep == NULL) {
1484 			cmuch_dip = ddi_get_next_sibling(cmuch_dip);
1485 			continue;
1486 		}
1487 
1488 		DBG_PRINT((CE_NOTE, "oplmsu: find-serial: name => %s", namep));
1489 
1490 		if ((strcmp(namep, MSU_CMUCH_FF) != 0) &&
1491 		    (strcmp(namep, MSU_CMUCH_DC) != 0)) {
1492 #ifdef DEBUG
1493 			if (strcmp(namep, MSU_CMUCH_DBG) != 0) {
1494 				cmuch_dip = ddi_get_next_sibling(cmuch_dip);
1495 				continue;
1496 			}
1497 #else
1498 			cmuch_dip = ddi_get_next_sibling(cmuch_dip);
1499 			continue;
1500 #endif
1501 		}
1502 
1503 		/*
1504 		 * Online the cmuch_dip so that its in the right state
1505 		 * to get the complete path, that is both name and address.
1506 		 */
1507 		(void) ndi_devi_online(cmuch_dip, 0);
1508 		(void) ddi_pathname(cmuch_dip, pathname);
1509 		DBG_PRINT((CE_NOTE,
1510 		    "oplmsu: find-serial: cmu-ch path => %s", pathname));
1511 		(void) strcat(pathname, EBUS_SERIAL_PATH);
1512 
1513 		/*
1514 		 * Call ddi_pathname_to_dev_t to forceload and attach
1515 		 * the required drivers.
1516 		 */
1517 		devt = ddi_pathname_to_dev_t(pathname);
1518 		DBG_PRINT((CE_NOTE, "oplmsu: find-serial: serial device "
1519 		    "dev_t = %lx", devt));
1520 		if ((devt != NODEV) &&
1521 		    ((dip = oplmsu_find_ser_dip(cmuch_dip)) != NULL)) {
1522 			wrk_ser_dl = (ser_devl_t *)
1523 			    kmem_zalloc(sizeof (ser_devl_t), KM_SLEEP);
1524 			wrk_ser_dl->dip = dip;
1525 			count += 1;
1526 
1527 			if (*ser_dl != NULL) {
1528 				wrk_ser_dl->next = *ser_dl;
1529 			}
1530 			*ser_dl = wrk_ser_dl;
1531 		}
1532 		cmuch_dip = ddi_get_next_sibling(cmuch_dip);
1533 	}
1534 	ndi_devi_exit(root_dip, circ);
1535 	return (count);
1536 }
1537 
1538 /* Configure STREAM */
1539 void
1540 oplmsu_conf_stream(uinst_t *msu_uinst)
1541 {
1542 	ldi_ident_t	msu_li = NULL;
1543 	ldi_handle_t	msu_lh = NULL;
1544 	struct msu_path	*mpath;
1545 	struct msu_dev	*mdev;
1546 	ser_devl_t	*ser_dl = NULL, *next_ser_dl;
1547 	int		*plink_id;
1548 	int		size;
1549 	int		i;
1550 	int		param;
1551 	int		connected = 0;
1552 	int		devcnt = 0;
1553 	int		rval;
1554 
1555 	DBG_PRINT((CE_NOTE,
1556 	    "oplmsu: conf-stream: stream configuration start!"));
1557 
1558 	/* Find serial devices */
1559 	devcnt = oplmsu_find_serial(&ser_dl);
1560 	if ((devcnt == 0) || (ser_dl == NULL)) {
1561 		cmn_err(CE_WARN, "oplmsu: conf-stream: "
1562 		    "Discovered serial device = %d", devcnt);
1563 		return;
1564 	}
1565 
1566 	/* Open oplmsu */
1567 	rval = oplmsu_open_msu(msu_uinst->msu_dip, &msu_li, &msu_lh);
1568 	if (rval != 0) {
1569 		cmn_err(CE_WARN, "oplmsu: conf-stream: "
1570 		    "msu open failed. errno = %d", rval);
1571 		return;
1572 	}
1573 
1574 	size = (sizeof (struct msu_path) + (sizeof (struct msu_dev) * devcnt));
1575 	mpath = (struct msu_path *)kmem_zalloc((size_t)size, KM_SLEEP);
1576 	plink_id = (int *)kmem_zalloc((sizeof (int) * devcnt), KM_SLEEP);
1577 
1578 	mdev = (struct msu_dev *)(mpath + 1);
1579 	for (i = 0; i < devcnt; i++) {
1580 		/* Connect two streams */
1581 		rval = oplmsu_plink_serial(ser_dl->dip, msu_lh, &plink_id[i]);
1582 		if (rval != 0) {
1583 			cmn_err(CE_WARN, "oplmsu: conf-stream: "
1584 			    "i_plink failed. errno = %d", rval);
1585 			next_ser_dl = ser_dl->next;
1586 			kmem_free(ser_dl, sizeof (ser_devl_t));
1587 			ser_dl = next_ser_dl;
1588 			continue;
1589 		}
1590 
1591 		rval = oplmsu_set_lpathnum(plink_id[i],
1592 		    ddi_get_instance(ser_dl->dip));
1593 		if (rval != 0) {
1594 			cmn_err(CE_WARN, "oplmsu: conf-stream: "
1595 			    "Link id %d is not found", plink_id[i]);
1596 			/* Issue ioctl(I_PUNLINK) */
1597 			(void) ldi_ioctl(msu_lh, I_PUNLINK,
1598 			    (intptr_t)plink_id[i], FKIOCTL, kcred, &param);
1599 			next_ser_dl = ser_dl->next;
1600 			kmem_free(ser_dl, sizeof (ser_devl_t));
1601 			ser_dl = next_ser_dl;
1602 			continue;
1603 		}
1604 
1605 		mdev->dip = ser_dl->dip;
1606 		next_ser_dl = ser_dl->next;
1607 		kmem_free(ser_dl, sizeof (ser_devl_t));
1608 		ser_dl = next_ser_dl;
1609 
1610 		mdev++;
1611 		connected++;
1612 	}
1613 
1614 	if (connected == 0) {
1615 		cmn_err(CE_WARN, "oplmsu: conf-stream: "
1616 		    "Connected paths = %d", connected);
1617 		(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
1618 		ldi_ident_release(msu_li);
1619 		kmem_free(plink_id, (sizeof (int) * devcnt));
1620 		kmem_free(mpath, size);
1621 		return;
1622 	}
1623 
1624 	/* Setup all structure */
1625 	mpath->num = connected;
1626 	rval = oplmsu_config_new(mpath);
1627 	if (rval != 0) {
1628 		cmn_err(CE_WARN, "oplmsu: conf-stream: "
1629 		    "Failed to create all paths. errno = %d", rval);
1630 		oplmsu_unlinks(msu_lh, plink_id, devcnt);
1631 		(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
1632 		ldi_ident_release(msu_li);
1633 		kmem_free(plink_id, (sizeof (int) * devcnt));
1634 		kmem_free(mpath, size);
1635 		return;
1636 	}
1637 
1638 	/* Start to use all paths */
1639 	rval = oplmsu_config_start(MSU_PATH_ALL);
1640 	if (rval != 0) {
1641 		cmn_err(CE_WARN, "oplmsu: conf-stream: "
1642 		    "Failed to start all paths. errno = %d", rval);
1643 
1644 		/* Delete the path */
1645 		rval = oplmsu_config_del(mpath);
1646 		if (rval == 0) {
1647 			oplmsu_unlinks(msu_lh, plink_id, devcnt);
1648 		}
1649 	}
1650 
1651 	(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
1652 	ldi_ident_release(msu_li);
1653 	kmem_free(plink_id, (sizeof (int) * devcnt));
1654 	kmem_free(mpath, size);
1655 
1656 	DBG_PRINT((CE_NOTE, "oplmsu: conf-stream: stream configuration end!"));
1657 }
1658 
1659 void
1660 oplmsu_unlinks(ldi_handle_t msu_lh, int *plink_id, int devcnt)
1661 {
1662 	int	i;
1663 	int	param = 0;
1664 
1665 	for (i = 0; i < devcnt; i++) {
1666 		if (plink_id[i] == 0) {
1667 			continue;
1668 		}
1669 
1670 		/* Issue ioctl(I_PUNLINK) */
1671 		(void) ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)plink_id[i],
1672 		    FKIOCTL, kcred, &param);
1673 	}
1674 }
1675 
1676 void
1677 oplmsu_setup(uinst_t *msu_uinst)
1678 {
1679 
1680 	DBG_PRINT((CE_NOTE, "oplmsu: setup: Background thread start!"));
1681 
1682 	mutex_enter(&oplmsu_bthrd_excl);
1683 	if (oplmsu_conf_st == MSU_CONFIGURING) {
1684 		mutex_exit(&oplmsu_bthrd_excl);
1685 		oplmsu_conf_stream(msu_uinst);	/* Configure stream */
1686 		mutex_enter(&oplmsu_bthrd_excl);
1687 		oplmsu_conf_st = MSU_CONFIGURED;
1688 		cv_broadcast(&oplmsu_conf_cv);	/* Wake up from cv_wait_sig() */
1689 	}
1690 
1691 	if (oplmsu_bthrd_id != NULL) {
1692 		oplmsu_bthrd_id = NULL;
1693 	}
1694 	mutex_exit(&oplmsu_bthrd_excl);
1695 
1696 	DBG_PRINT((CE_NOTE, "oplmsu: setup: Background thread end!"));
1697 
1698 	thread_exit();
1699 }
1700 
1701 int
1702 oplmsu_create_upath(dev_info_t *dip)
1703 {
1704 	upath_t		*upath;
1705 	lpath_t		*lpath;
1706 	dev_info_t	*cmuch_dip;
1707 	int		instance;
1708 	int		lsb;
1709 
1710 	cmuch_dip = ddi_get_parent(ddi_get_parent(dip));
1711 	lsb = ddi_prop_get_int(DDI_DEV_T_ANY, cmuch_dip, 0, MSU_BOARD_PROP,
1712 	    FAILURE);
1713 	if (lsb == FAILURE) {
1714 		return (lsb);
1715 	}
1716 
1717 	instance = ddi_get_instance(dip);
1718 
1719 	mutex_enter(&oplmsu_uinst->l_lock);
1720 	lpath = oplmsu_uinst->first_lpath;
1721 	while (lpath) {
1722 		if (lpath->path_no == instance) {
1723 			break;
1724 		}
1725 		lpath = lpath->l_next;
1726 	}
1727 
1728 	if (lpath == NULL) {
1729 		mutex_exit(&oplmsu_uinst->l_lock);
1730 		return (ENODEV);
1731 	}
1732 
1733 	upath = (upath_t *)kmem_zalloc(sizeof (upath_t), KM_SLEEP);
1734 
1735 	/*
1736 	 * Initialize members of upath_t
1737 	 */
1738 
1739 	upath->path_no = instance;
1740 	upath->lpath = lpath;
1741 	upath->ser_devcb.dip = dip;
1742 	upath->ser_devcb.lsb = lsb;
1743 	oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STOP, MSU_PSTAT_EMPTY,
1744 	    MSU_STOP);
1745 
1746 	lpath->src_upath = NULL;
1747 	lpath->status = MSU_EXT_NOTUSED;
1748 	mutex_exit(&oplmsu_uinst->l_lock);
1749 
1750 	oplmsu_link_upath(upath);
1751 	return (SUCCESS);
1752 }
1753 
1754 /* Setup new upper instance structure */
1755 int
1756 oplmsu_config_new(struct msu_path *mpath)
1757 {
1758 	struct msu_dev	*mdev;
1759 	int		i;
1760 	int		rval = SUCCESS;
1761 
1762 	DBG_PRINT((CE_NOTE, "oplmsu: conf-new: config_new() called"));
1763 	ASSERT(mpath);
1764 
1765 	if (mpath->num == 0) {
1766 		cmn_err(CE_WARN, "oplmsu: conf-new: "
1767 		    "Number of paths = %d", mpath->num);
1768 		return (EINVAL);
1769 	}
1770 
1771 	rw_enter(&oplmsu_uinst->lock, RW_WRITER);
1772 
1773 	mutex_enter(&oplmsu_uinst->l_lock);
1774 	rval = oplmsu_check_lpath_usable();
1775 	mutex_exit(&oplmsu_uinst->l_lock);
1776 
1777 	if (rval == BUSY) { /* Check whether Lower path is usable */
1778 		rw_exit(&oplmsu_uinst->lock);
1779 		cmn_err(CE_WARN, "oplmsu: conf-new: "
1780 		    "Other processing is using this device");
1781 		return (EBUSY);
1782 	}
1783 
1784 	/*
1785 	 * Because the OPLMSU instance already exists when the upper path
1786 	 * table exists, the configure_new processing cannot be done.
1787 	 */
1788 
1789 	mutex_enter(&oplmsu_uinst->u_lock);
1790 
1791 	if ((oplmsu_uinst->first_upath != NULL) ||
1792 	    (oplmsu_uinst->last_upath != NULL)) {
1793 		mutex_exit(&oplmsu_uinst->u_lock);
1794 		rw_exit(&oplmsu_uinst->lock);
1795 		cmn_err(CE_WARN, "oplmsu: conf-new: upath_t already exist");
1796 		return (EINVAL);
1797 	}
1798 
1799 	/*
1800 	 * Because the config_new processing has already been done
1801 	 * if oplmsu_uinst->path_num isn't -1, this processing cannot be
1802 	 * continued.
1803 	 */
1804 
1805 	if (oplmsu_uinst->path_num != UNDEFINED) {
1806 		mutex_exit(&oplmsu_uinst->u_lock);
1807 		rw_exit(&oplmsu_uinst->lock);
1808 		cmn_err(CE_WARN, "oplmsu: conf-new: "
1809 		    "conf-new processing has already been completed");
1810 		return (EINVAL);
1811 	}
1812 
1813 	/*
1814 	 * Only the number of specified paths makes the upper path
1815 	 * information tables.
1816 	 */
1817 
1818 	mdev = (struct msu_dev *)(mpath + 1);
1819 	for (i = 0; i < mpath->num; i++) {
1820 		/*
1821 		 * Associate upper path information table with lower path
1822 		 * information table.
1823 		 *
1824 		 * If the upper path information table and the lower path
1825 		 * information table cannot be associated, the link list of
1826 		 * the upper path information table is released.
1827 		 */
1828 		rval = oplmsu_create_upath(mdev->dip);
1829 		if (rval != SUCCESS) {
1830 			oplmsu_delete_upath_info();
1831 			mutex_exit(&oplmsu_uinst->u_lock);
1832 			rw_exit(&oplmsu_uinst->lock);
1833 			cmn_err(CE_WARN, "oplmsu: conf-new: "
1834 			    "Failed to create upath %d", rval);
1835 			return (rval);
1836 		}
1837 
1838 		mdev++;
1839 	}
1840 
1841 	/*
1842 	 * Setup members of uinst_t
1843 	 */
1844 
1845 	oplmsu_uinst->inst_status = oplmsu_get_inst_status();
1846 	oplmsu_uinst->path_num = mpath->num;
1847 	oplmsu_uinst->lower_queue = NULL;
1848 	mutex_exit(&oplmsu_uinst->u_lock);
1849 	rw_exit(&oplmsu_uinst->lock);
1850 	return (SUCCESS);
1851 }
1852 
1853 /* Add path information */
1854 int
1855 oplmsu_config_add(dev_info_t *dip)
1856 {
1857 	upath_t	*upath;
1858 	int	instance;
1859 	int	rval = SUCCESS;
1860 
1861 	DBG_PRINT((CE_NOTE, "oplmsu: conf-add: config_add() called"));
1862 	ASSERT(dip);
1863 
1864 	instance = ddi_get_instance(dip);
1865 	rw_enter(&oplmsu_uinst->lock, RW_WRITER);
1866 
1867 	if (oplmsu_uinst->path_num == UNDEFINED) {
1868 		rw_exit(&oplmsu_uinst->lock);
1869 		cmn_err(CE_WARN, "oplmsu: conf-add: "
1870 		    "conf-new processing has not been completed yet");
1871 		return (EINVAL);
1872 	}
1873 
1874 	mutex_enter(&oplmsu_uinst->u_lock);
1875 	upath = oplmsu_search_upath_info(instance);
1876 	if (upath != NULL) {
1877 		mutex_exit(&oplmsu_uinst->u_lock);
1878 		rw_exit(&oplmsu_uinst->lock);
1879 		cmn_err(CE_WARN, "oplmsu: conf-add: "
1880 		    "Proper upath_t doesn't find");
1881 		return (EINVAL);
1882 	}
1883 
1884 	rval = oplmsu_create_upath(dip);
1885 	if (rval != SUCCESS) {
1886 		mutex_exit(&oplmsu_uinst->u_lock);
1887 		rw_exit(&oplmsu_uinst->lock);
1888 		cmn_err(CE_WARN, "oplmsu: conf-add: "
1889 		    "Failed to create upath %d", rval);
1890 		return (rval);
1891 	}
1892 
1893 	oplmsu_uinst->inst_status = oplmsu_get_inst_status();
1894 	oplmsu_uinst->path_num = oplmsu_get_pathnum();
1895 	mutex_exit(&oplmsu_uinst->u_lock);
1896 	rw_exit(&oplmsu_uinst->lock);
1897 	return (SUCCESS);
1898 }
1899 
1900 /* Delete each path information */
1901 int
1902 oplmsu_config_del(struct msu_path *mpath)
1903 {
1904 	struct msu_dev	*mdev;
1905 	upath_t		*upath;
1906 	lpath_t		*lpath;
1907 	int		rval = SUCCESS;
1908 	int		use_flag;
1909 	int		i;
1910 
1911 	DBG_PRINT((CE_NOTE, "oplmsu: conf-del: config_del() called"));
1912 	ASSERT(mpath);
1913 
1914 	mdev = (struct msu_dev *)(mpath + 1);
1915 
1916 	rw_enter(&oplmsu_uinst->lock, RW_WRITER);
1917 	mutex_enter(&oplmsu_uinst->u_lock);
1918 	for (i = 0; i < mpath->num; i++) {
1919 		upath = oplmsu_search_upath_info(ddi_get_instance(mdev->dip));
1920 		if (upath == NULL) {
1921 			cmn_err(CE_WARN, "oplmsu: conf-del: "
1922 			    "Proper upath_t doesn't find");
1923 			rval = ENODEV;
1924 			mdev++;
1925 			continue;
1926 		}
1927 
1928 		lpath = upath->lpath;
1929 		if (lpath == NULL) {
1930 			if ((upath->traditional_status == MSU_WSTP_ACK) ||
1931 			    (upath->traditional_status == MSU_WSTR_ACK) ||
1932 			    (upath->traditional_status == MSU_WPTH_CHG) ||
1933 			    (upath->traditional_status == MSU_WTCS_ACK) ||
1934 			    (upath->traditional_status == MSU_WTMS_ACK) ||
1935 			    (upath->traditional_status == MSU_WPPS_ACK) ||
1936 			    (upath->traditional_status == MSU_WWSZ_ACK) ||
1937 			    (upath->traditional_status == MSU_WCAR_ACK)) {
1938 				cmn_err(CE_WARN, "oplmsu: conf-del: "
1939 				    "Other processing is using this device");
1940 				rval = EBUSY;
1941 				mdev++;
1942 				continue;
1943 			}
1944 
1945 			if ((upath->status != MSU_PSTAT_DISCON) ||
1946 			    (upath->traditional_status != MSU_DISCON)) {
1947 				cmn_err(CE_WARN, "oplmsu: conf-del: "
1948 				    "Status of path is improper");
1949 				rval = EINVAL;
1950 				mdev++;
1951 				continue;
1952 			}
1953 		} else {
1954 			mutex_enter(&oplmsu_uinst->l_lock);
1955 			use_flag = oplmsu_set_ioctl_path(lpath, NULL, NULL);
1956 			if (use_flag == BUSY) {
1957 				mutex_exit(&oplmsu_uinst->l_lock);
1958 				cmn_err(CE_WARN, "oplmsu: conf-del: "
1959 				    "Other processing is using lower path");
1960 				rval = EBUSY;
1961 				mdev++;
1962 				continue;
1963 			}
1964 
1965 			if (((upath->status != MSU_PSTAT_STOP) ||
1966 			    (upath->traditional_status != MSU_STOP)) &&
1967 			    ((upath->status != MSU_PSTAT_FAIL) ||
1968 			    (upath->traditional_status != MSU_FAIL))) {
1969 				oplmsu_clear_ioctl_path(lpath);
1970 				mutex_exit(&oplmsu_uinst->l_lock);
1971 				cmn_err(CE_WARN, "oplmsu: conf-del: "
1972 				    "Status of path isn't 'Offline:stop/fail'");
1973 				rval = EINVAL;
1974 				mdev++;
1975 				continue;
1976 			}
1977 			lpath->src_upath = NULL;
1978 			lpath->status = MSU_SETID_NU;
1979 			oplmsu_clear_ioctl_path(lpath);
1980 			mutex_exit(&oplmsu_uinst->l_lock);
1981 		}
1982 		oplmsu_unlink_upath(upath);	/* Unlink upath_t */
1983 		kmem_free(upath, sizeof (upath_t));
1984 		mdev++;
1985 	}
1986 
1987 	oplmsu_uinst->inst_status = oplmsu_get_inst_status();
1988 	oplmsu_uinst->path_num = oplmsu_get_pathnum();
1989 	mutex_exit(&oplmsu_uinst->u_lock);
1990 	rw_exit(&oplmsu_uinst->lock);
1991 	return (rval);
1992 }
1993 
1994 /* Stop to use the path */
1995 int
1996 oplmsu_config_stop(int pathnum)
1997 {
1998 	upath_t	*upath, *altn_upath;
1999 	lpath_t	*lpath, *altn_lpath;
2000 	queue_t	*stp_queue = NULL;
2001 	queue_t	*dst_queue = NULL;
2002 	mblk_t	*nmp = NULL, *fmp = NULL;
2003 	ctrl_t	*ctrl;
2004 	int	term_ioctl, term_stat;
2005 	int	use_flag;
2006 
2007 	DBG_PRINT((CE_NOTE,
2008 	    "oplmsu: conf-stop: config_stop(%d) called", pathnum));
2009 
2010 	if (pathnum == MSU_PATH_ALL) {
2011 		cmn_err(CE_WARN, "oplmsu: conf-stop: "
2012 		    "All path can't be transferred to the status of "
2013 		    "'Offline:stop'");
2014 		return (EINVAL);
2015 	}
2016 
2017 	rw_enter(&oplmsu_uinst->lock, RW_WRITER);
2018 	mutex_enter(&oplmsu_uinst->u_lock);
2019 
2020 	upath = oplmsu_search_upath_info(pathnum);	/* Search upath_t */
2021 	if (upath == NULL) {
2022 		mutex_exit(&oplmsu_uinst->u_lock);
2023 		rw_exit(&oplmsu_uinst->lock);
2024 		cmn_err(CE_WARN, "oplmsu: conf-stop: "
2025 		    "Proper upath_t doesn't find");
2026 		return (ENODEV);
2027 	}
2028 
2029 	lpath = upath->lpath;
2030 	if (lpath == NULL) {
2031 		mutex_exit(&oplmsu_uinst->u_lock);
2032 		rw_exit(&oplmsu_uinst->lock);
2033 		cmn_err(CE_WARN, "oplmsu: conf-stop: "
2034 		    "Proper lpath_t doesn't exist");
2035 		return (ENODEV);
2036 	}
2037 
2038 	mutex_enter(&oplmsu_uinst->l_lock);
2039 
2040 	/* Check status of lpath_t */
2041 	use_flag = oplmsu_set_ioctl_path(lpath, NULL, NULL);
2042 	if (use_flag == BUSY) {
2043 		mutex_exit(&oplmsu_uinst->l_lock);
2044 		mutex_exit(&oplmsu_uinst->u_lock);
2045 		rw_exit(&oplmsu_uinst->lock);
2046 		cmn_err(CE_WARN, "oplmsu: conf-stop: "
2047 		    "Other processing is using lower path");
2048 		return (EBUSY);
2049 	}
2050 
2051 	if (upath->status == MSU_PSTAT_FAIL) {
2052 		oplmsu_clear_ioctl_path(lpath);
2053 		mutex_exit(&oplmsu_uinst->l_lock);
2054 		mutex_exit(&oplmsu_uinst->u_lock);
2055 		rw_exit(&oplmsu_uinst->lock);
2056 		return (EIO);
2057 	} else if ((upath->status == MSU_PSTAT_STOP) &&
2058 	    (upath->traditional_status == MSU_STOP)) {
2059 		oplmsu_clear_ioctl_path(lpath);
2060 		mutex_exit(&oplmsu_uinst->l_lock);
2061 		mutex_exit(&oplmsu_uinst->u_lock);
2062 		rw_exit(&oplmsu_uinst->lock);
2063 		return (SUCCESS);
2064 	} else if ((upath->status == MSU_PSTAT_STANDBY) &&
2065 	    (upath->traditional_status == MSU_STANDBY)) {
2066 		oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STOP,
2067 		    upath->status, MSU_STOP);
2068 		oplmsu_clear_ioctl_path(lpath);
2069 		lpath->src_upath = NULL;
2070 		lpath->status = MSU_EXT_NOTUSED;
2071 
2072 		oplmsu_uinst->inst_status = oplmsu_get_inst_status();
2073 		mutex_exit(&oplmsu_uinst->l_lock);
2074 		mutex_exit(&oplmsu_uinst->u_lock);
2075 		rw_exit(&oplmsu_uinst->lock);
2076 		return (SUCCESS);
2077 	} else if ((upath->status == MSU_PSTAT_ACTIVE) &&
2078 	    (upath->traditional_status == MSU_ACTIVE)) {
2079 		altn_upath = oplmsu_search_standby();
2080 		if (altn_upath == NULL) { /* Alternate path doesn't exist */
2081 			DBG_PRINT((CE_NOTE, "oplmsu: conf-stop: "
2082 			    "Alternate upper path doesn't find"));
2083 			oplmsu_clear_ioctl_path(lpath);
2084 			mutex_exit(&oplmsu_uinst->l_lock);
2085 			mutex_exit(&oplmsu_uinst->u_lock);
2086 			rw_exit(&oplmsu_uinst->lock);
2087 			return (EINVAL);
2088 		}
2089 
2090 		if ((fmp = allocb(sizeof (char), BPRI_LO)) == NULL) {
2091 			oplmsu_clear_ioctl_path(lpath);
2092 			mutex_exit(&oplmsu_uinst->l_lock);
2093 			mutex_exit(&oplmsu_uinst->u_lock);
2094 			rw_exit(&oplmsu_uinst->lock);
2095 			return (ENOSR);
2096 		}
2097 
2098 		if (oplmsu_stop_prechg(&nmp, &term_ioctl, &term_stat) !=
2099 		    SUCCESS) {
2100 			oplmsu_clear_ioctl_path(lpath);
2101 			mutex_exit(&oplmsu_uinst->l_lock);
2102 			mutex_exit(&oplmsu_uinst->u_lock);
2103 			rw_exit(&oplmsu_uinst->lock);
2104 			freeb(fmp);
2105 			return (ENOSR);
2106 		}
2107 
2108 		altn_lpath = altn_upath->lpath;
2109 		use_flag = oplmsu_set_ioctl_path(altn_lpath, NULL, NULL);
2110 		if (use_flag == BUSY) {
2111 			oplmsu_clear_ioctl_path(lpath);
2112 			mutex_exit(&oplmsu_uinst->l_lock);
2113 			mutex_exit(&oplmsu_uinst->u_lock);
2114 			rw_exit(&oplmsu_uinst->lock);
2115 
2116 			cmn_err(CE_WARN, "oplmsu: conf-stop: "
2117 			    "Other processing is using alternate lower path");
2118 			freeb(fmp);
2119 			freemsg(nmp);
2120 			return (EBUSY);
2121 		}
2122 
2123 		dst_queue = WR(altn_lpath->lower_queue);
2124 
2125 		/* termios is not held. Change alternate path to MSU_ACTIVE */
2126 		if (nmp == NULL) {
2127 			altn_upath->traditional_status = term_stat;
2128 			altn_lpath->src_upath = upath;
2129 			altn_lpath->status = MSU_EXT_VOID;
2130 
2131 			oplmsu_uinst->lower_queue = NULL;
2132 
2133 			ctrl = oplmsu_uinst->user_ctrl;
2134 			if (ctrl != NULL) {
2135 				mutex_enter(&oplmsu_uinst->c_lock);
2136 				stp_queue = WR(ctrl->queue);
2137 				mutex_exit(&oplmsu_uinst->c_lock);
2138 				noenable(stp_queue);
2139 				oplmsu_queue_flag = 1;
2140 			}
2141 
2142 			/* Make M_FLUSH and send to alternate path */
2143 			oplmsu_cmn_set_mflush(fmp);
2144 			putq(dst_queue, fmp);
2145 
2146 			/* Change status of alternate path */
2147 			oplmsu_cmn_set_upath_sts(altn_upath, MSU_PSTAT_ACTIVE,
2148 			    altn_upath->status, MSU_ACTIVE);
2149 
2150 			oplmsu_clear_ioctl_path(altn_lpath);
2151 			altn_lpath->uinst = oplmsu_uinst;
2152 			altn_lpath->src_upath = NULL;
2153 			altn_lpath->status = MSU_EXT_NOTUSED;
2154 
2155 			/* Notify of the active path changing */
2156 			prom_opl_switch_console(altn_upath->ser_devcb.lsb);
2157 
2158 			/* Send XON to notify active path */
2159 			(void) oplmsu_cmn_put_xoffxon(dst_queue, MSU_XON_4);
2160 
2161 			/* Send XOFF to notify all standby paths */
2162 			oplmsu_cmn_putxoff_standby();
2163 
2164 			oplmsu_uinst->lower_queue = RD(dst_queue);
2165 			ctrl = oplmsu_uinst->user_ctrl;
2166 
2167 			/* Switch active path of oplmsu */
2168 			if (ctrl != NULL) {
2169 				queue_t	*altn_queue;
2170 
2171 				mutex_enter(&oplmsu_uinst->c_lock);
2172 				altn_queue = WR(ctrl->queue);
2173 				mutex_exit(&oplmsu_uinst->c_lock);
2174 
2175 				/* Restart queuing of user access node */
2176 				enableok(altn_queue);
2177 
2178 				oplmsu_queue_flag = 0;
2179 				mutex_exit(&oplmsu_uinst->l_lock);
2180 				mutex_exit(&oplmsu_uinst->u_lock);
2181 				oplmsu_wcmn_high_qenable(altn_queue, RW_WRITER);
2182 				mutex_enter(&oplmsu_uinst->u_lock);
2183 				mutex_enter(&oplmsu_uinst->l_lock);
2184 			}
2185 
2186 			/* Stop previous active path */
2187 			oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STOP,
2188 			    upath->status, MSU_STOP);
2189 
2190 			lpath->uinst = NULL;
2191 			lpath->src_upath = NULL;
2192 			lpath->status = MSU_EXT_NOTUSED;
2193 			oplmsu_clear_ioctl_path(lpath);
2194 
2195 			oplmsu_uinst->inst_status = oplmsu_get_inst_status();
2196 			mutex_exit(&oplmsu_uinst->l_lock);
2197 			mutex_exit(&oplmsu_uinst->u_lock);
2198 			rw_exit(&oplmsu_uinst->lock);
2199 			return (SUCCESS);
2200 		}
2201 
2202 		/* Send termios information to alternate path */
2203 		if (canput(dst_queue)) {
2204 			altn_upath->traditional_status = term_stat;
2205 			altn_lpath->src_upath = upath;
2206 			altn_lpath->status = MSU_EXT_VOID;
2207 
2208 			upath->traditional_status = MSU_WSTP_ACK;
2209 			lpath->uinst = NULL;
2210 
2211 			oplmsu_uinst->lower_queue = NULL;
2212 
2213 			ctrl = oplmsu_uinst->user_ctrl;
2214 			if (ctrl != NULL) {
2215 				mutex_enter(&oplmsu_uinst->c_lock);
2216 				stp_queue = WR(ctrl->queue);
2217 				mutex_exit(&oplmsu_uinst->c_lock);
2218 				noenable(stp_queue);
2219 				oplmsu_queue_flag = 1;
2220 			}
2221 
2222 			mutex_exit(&oplmsu_uinst->l_lock);
2223 			mutex_exit(&oplmsu_uinst->u_lock);
2224 			rw_exit(&oplmsu_uinst->lock);
2225 			oplmsu_cmn_set_mflush(fmp);
2226 			putq(dst_queue, fmp);
2227 			putq(dst_queue, nmp);
2228 
2229 			mutex_enter(&oplmsu_uinst->l_lock);
2230 			lpath->sw_flag = 1;
2231 			while (lpath->sw_flag != 0) {
2232 				/* Wait for the completion of path switching */
2233 				cv_wait(&lpath->sw_cv, &oplmsu_uinst->l_lock);
2234 			}
2235 			mutex_exit(&oplmsu_uinst->l_lock);
2236 			return (SUCCESS);
2237 		} else {
2238 			oplmsu_clear_ioctl_path(altn_lpath);
2239 			oplmsu_clear_ioctl_path(lpath);
2240 			mutex_exit(&oplmsu_uinst->l_lock);
2241 			mutex_exit(&oplmsu_uinst->u_lock);
2242 			rw_exit(&oplmsu_uinst->lock);
2243 			freeb(fmp);
2244 			freemsg(nmp);
2245 			return (FAILURE);
2246 		}
2247 		/* NOTREACHED */
2248 	} else {
2249 		oplmsu_clear_ioctl_path(lpath);
2250 		mutex_exit(&oplmsu_uinst->l_lock);
2251 		mutex_exit(&oplmsu_uinst->u_lock);
2252 		rw_exit(&oplmsu_uinst->lock);
2253 
2254 		cmn_err(CE_WARN, "oplmsu: conf-stop: "
2255 		    "Status of path is improper");
2256 		return (EINVAL);
2257 	}
2258 	/* NOTREACHED */
2259 }
2260 
2261 /* Start to use path */
2262 int
2263 oplmsu_config_start(int pathnum)
2264 {
2265 	upath_t	*upath = NULL;
2266 	lpath_t	*lpath = NULL;
2267 	queue_t	*dst_queue, *main_rq = NULL;
2268 	int	msu_tty_port;
2269 
2270 	DBG_PRINT((CE_NOTE,
2271 	    "oplmsu: conf-start: config_start(%d) called", pathnum));
2272 
2273 	rw_enter(&oplmsu_uinst->lock, RW_WRITER);
2274 	mutex_enter(&oplmsu_uinst->u_lock);
2275 
2276 	if (oplmsu_get_inst_status() == INST_STAT_BUSY) {
2277 		mutex_exit(&oplmsu_uinst->u_lock);
2278 		rw_exit(&oplmsu_uinst->lock);
2279 		return (EBUSY);
2280 	}
2281 
2282 	if (pathnum == MSU_PATH_ALL) {
2283 		(void) oplmsu_search_min_stop_path();
2284 	}
2285 
2286 	for (upath = oplmsu_uinst->first_upath; upath; ) {
2287 		if ((pathnum != MSU_PATH_ALL) && (upath->path_no != pathnum)) {
2288 			upath = upath->u_next;
2289 			continue;
2290 		}
2291 
2292 		if (upath->path_no == pathnum) {
2293 			lpath = upath->lpath;
2294 			if (lpath == NULL) {
2295 				mutex_exit(&oplmsu_uinst->u_lock);
2296 				rw_exit(&oplmsu_uinst->lock);
2297 				cmn_err(CE_WARN, "oplmsu: conf-start: "
2298 				    "Proper lpath_t doesn't exist");
2299 				return (EINVAL);
2300 			}
2301 
2302 			oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STANDBY,
2303 			    upath->status, MSU_STANDBY);
2304 
2305 			mutex_enter(&oplmsu_uinst->l_lock);
2306 			lpath->src_upath = NULL;
2307 			lpath->status = MSU_EXT_NOTUSED;
2308 			mutex_exit(&oplmsu_uinst->l_lock);
2309 			mutex_exit(&oplmsu_uinst->u_lock);
2310 			rw_exit(&oplmsu_uinst->lock);
2311 			return (SUCCESS);
2312 		}
2313 
2314 		/*
2315 		 * with PATH_ALL
2316 		 */
2317 		lpath = upath->lpath;
2318 		if (lpath == NULL) {
2319 			upath = upath->u_next;
2320 
2321 			DBG_PRINT((CE_WARN, "oplmsu: conf-start: "
2322 			    "Proper lpath_t doesn't exist"));
2323 			continue;
2324 		}
2325 
2326 		msu_tty_port = ddi_prop_get_int(DDI_DEV_T_ANY,
2327 		    oplmsu_uinst->msu_dip, 0, MSU_TTY_PORT_PROP, -1);
2328 
2329 		if (upath->ser_devcb.lsb == msu_tty_port) {
2330 			/* Notify of the active path changing */
2331 			prom_opl_switch_console(upath->ser_devcb.lsb);
2332 
2333 			oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_ACTIVE,
2334 			    upath->status, MSU_ACTIVE);
2335 
2336 			mutex_enter(&oplmsu_uinst->l_lock);
2337 			main_rq = RD(lpath->lower_queue);
2338 			dst_queue = WR(lpath->lower_queue);
2339 			lpath->src_upath = NULL;
2340 			lpath->status = MSU_EXT_NOTUSED;
2341 			lpath->uinst = oplmsu_uinst;
2342 			mutex_exit(&oplmsu_uinst->l_lock);
2343 
2344 			/* Send XON to notify active path */
2345 			(void) oplmsu_cmn_put_xoffxon(dst_queue, MSU_XON_4);
2346 		} else {
2347 			oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STANDBY,
2348 			    upath->status, MSU_STANDBY);
2349 
2350 			mutex_enter(&oplmsu_uinst->l_lock);
2351 			lpath->src_upath = NULL;
2352 			lpath->status = MSU_EXT_NOTUSED;
2353 			mutex_exit(&oplmsu_uinst->l_lock);
2354 		}
2355 		upath = upath->u_next;
2356 	}
2357 
2358 	if (main_rq == NULL) {
2359 		upath_t	*altn_upath;
2360 		lpath_t	*altn_lpath;
2361 
2362 		altn_upath = oplmsu_search_standby();
2363 		if (altn_upath) {
2364 			oplmsu_cmn_set_upath_sts(altn_upath, MSU_PSTAT_ACTIVE,
2365 			    altn_upath->status, MSU_ACTIVE);
2366 
2367 			/* Notify of the active path changing */
2368 			prom_opl_switch_console(altn_upath->ser_devcb.lsb);
2369 
2370 			altn_lpath = altn_upath->lpath;
2371 			if (altn_lpath) {
2372 				mutex_enter(&oplmsu_uinst->l_lock);
2373 				main_rq = RD(altn_lpath->lower_queue);
2374 				dst_queue = WR(altn_lpath->lower_queue);
2375 				altn_lpath->src_upath = NULL;
2376 				altn_lpath->status = MSU_EXT_NOTUSED;
2377 				altn_lpath->uinst = oplmsu_uinst;
2378 				mutex_exit(&oplmsu_uinst->l_lock);
2379 
2380 				/* Send XON to notify active path */
2381 				(void) oplmsu_cmn_put_xoffxon(dst_queue,
2382 				    MSU_XON_4);
2383 			} else {
2384 				cmn_err(CE_WARN, "oplmsu: conf-start: "
2385 				    "Proper alternate lpath_t doesn't exist");
2386 			}
2387 		} else {
2388 			cmn_err(CE_WARN, "oplmsu: conf-start: "
2389 			    "Proper alternate upath_t doesn't exist");
2390 		}
2391 	}
2392 
2393 	mutex_enter(&oplmsu_uinst->l_lock);
2394 
2395 	/* Send XOFF to notify all standby paths */
2396 	oplmsu_cmn_putxoff_standby();
2397 
2398 	/* Change active path of oplmsu */
2399 	oplmsu_uinst->lower_queue = main_rq;
2400 	oplmsu_uinst->inst_status = oplmsu_get_inst_status();
2401 	mutex_exit(&oplmsu_uinst->l_lock);
2402 	mutex_exit(&oplmsu_uinst->u_lock);
2403 	rw_exit(&oplmsu_uinst->lock);
2404 	return (SUCCESS);
2405 }
2406 
2407 /* Prepare of unlink path */
2408 int
2409 oplmsu_config_disc(int pathnum)
2410 {
2411 	upath_t	*upath;
2412 	lpath_t	*lpath;
2413 	int	use_flag;
2414 
2415 	DBG_PRINT((CE_NOTE,
2416 	    "oplmsu: conf-disc: config_disc(%d) called", pathnum));
2417 
2418 	rw_enter(&oplmsu_uinst->lock, RW_READER);
2419 	mutex_enter(&oplmsu_uinst->u_lock);
2420 
2421 	upath = oplmsu_search_upath_info(pathnum);
2422 	if (upath == NULL) {
2423 		mutex_exit(&oplmsu_uinst->u_lock);
2424 		rw_exit(&oplmsu_uinst->lock);
2425 		cmn_err(CE_WARN, "oplmsu: conf-disc: "
2426 		    "Proper upath_t doesn't find");
2427 		return (EINVAL);
2428 	}
2429 
2430 	if ((upath->status == MSU_PSTAT_DISCON) ||
2431 	    (upath->traditional_status == MSU_DISCON)) {
2432 		mutex_exit(&oplmsu_uinst->u_lock);
2433 		rw_exit(&oplmsu_uinst->lock);
2434 		return (SUCCESS);
2435 	} else if (((upath->status != MSU_PSTAT_STOP) ||
2436 	    (upath->traditional_status != MSU_STOP)) &&
2437 	    ((upath->status != MSU_PSTAT_FAIL) ||
2438 	    (upath->traditional_status != MSU_FAIL))) {
2439 		mutex_exit(&oplmsu_uinst->u_lock);
2440 		rw_exit(&oplmsu_uinst->lock);
2441 		cmn_err(CE_WARN, "oplmsu: conf-disc: "
2442 		    "Status of path is improper");
2443 		return (EINVAL);
2444 	}
2445 
2446 	lpath = upath->lpath;
2447 	if (lpath == NULL) {
2448 		mutex_exit(&oplmsu_uinst->u_lock);
2449 		rw_exit(&oplmsu_uinst->lock);
2450 		cmn_err(CE_WARN, "oplmsu: conf-disc: "
2451 		    "Proper lpath_t doesn't exist");
2452 		return (ENODEV);
2453 	}
2454 
2455 	mutex_enter(&oplmsu_uinst->l_lock);
2456 
2457 	/* Check lower path status */
2458 	use_flag = oplmsu_set_ioctl_path(lpath, NULL, NULL);
2459 	if (use_flag == BUSY) {
2460 		mutex_exit(&oplmsu_uinst->l_lock);
2461 		mutex_exit(&oplmsu_uinst->u_lock);
2462 		rw_exit(&oplmsu_uinst->lock);
2463 		cmn_err(CE_WARN, "oplmsu: conf-disc: "
2464 		    "Other processing is using lower path");
2465 		return (EBUSY);
2466 	}
2467 
2468 	upath->status = MSU_PSTAT_STOP;
2469 	upath->traditional_status = MSU_SETID;
2470 
2471 	oplmsu_clear_ioctl_path(lpath);
2472 	mutex_exit(&oplmsu_uinst->l_lock);
2473 	mutex_exit(&oplmsu_uinst->u_lock);
2474 	rw_exit(&oplmsu_uinst->lock);
2475 	return (SUCCESS);
2476 }
2477