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