xref: /illumos-gate/usr/src/uts/sun4u/opl/io/oplmsu/oplmsu.c (revision 25cf1a30)
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 		(void) ddi_pathname(cmuch_dip, pathname);
1504 		DBG_PRINT((CE_NOTE,
1505 		    "oplmsu: find-serial: cmu-ch path => %s", pathname));
1506 		(void) strcat(pathname, EBUS_SERIAL_PATH);
1507 
1508 		/*
1509 		 * Call ddi_pathname_to_dev_t to forceload and attach
1510 		 * the required drivers.
1511 		 */
1512 		devt = ddi_pathname_to_dev_t(pathname);
1513 		DBG_PRINT((CE_NOTE, "oplmsu: find-serial: serial device "
1514 		    "dev_t = %lx", devt));
1515 		if ((devt != NODEV) &&
1516 		    ((dip = oplmsu_find_ser_dip(cmuch_dip)) != NULL)) {
1517 			wrk_ser_dl = (ser_devl_t *)
1518 			    kmem_zalloc(sizeof (ser_devl_t), KM_SLEEP);
1519 			wrk_ser_dl->dip = dip;
1520 			count += 1;
1521 
1522 			if (*ser_dl != NULL) {
1523 				wrk_ser_dl->next = *ser_dl;
1524 			}
1525 			*ser_dl = wrk_ser_dl;
1526 		}
1527 		cmuch_dip = ddi_get_next_sibling(cmuch_dip);
1528 	}
1529 	ndi_devi_exit(root_dip, circ);
1530 	return (count);
1531 }
1532 
1533 /* Configure STREAM */
1534 void
1535 oplmsu_conf_stream(uinst_t *msu_uinst)
1536 {
1537 	ldi_ident_t	msu_li = NULL;
1538 	ldi_handle_t	msu_lh = NULL;
1539 	struct msu_path	*mpath;
1540 	struct msu_dev	*mdev;
1541 	ser_devl_t	*ser_dl = NULL, *next_ser_dl;
1542 	int		*plink_id;
1543 	int		size;
1544 	int		i;
1545 	int		param;
1546 	int		connected = 0;
1547 	int		devcnt = 0;
1548 	int		rval;
1549 
1550 	DBG_PRINT((CE_NOTE,
1551 	    "oplmsu: conf-stream: stream configuration start!"));
1552 
1553 	/* Find serial devices */
1554 	devcnt = oplmsu_find_serial(&ser_dl);
1555 	if ((devcnt == 0) || (ser_dl == NULL)) {
1556 		cmn_err(CE_WARN, "oplmsu: conf-stream: "
1557 		    "Discovered serial device = %d", devcnt);
1558 		return;
1559 	}
1560 
1561 	/* Open oplmsu */
1562 	rval = oplmsu_open_msu(msu_uinst->msu_dip, &msu_li, &msu_lh);
1563 	if (rval != 0) {
1564 		cmn_err(CE_WARN, "oplmsu: conf-stream: "
1565 		    "msu open failed. errno = %d", rval);
1566 		return;
1567 	}
1568 
1569 	size = (sizeof (struct msu_path) + (sizeof (struct msu_dev) * devcnt));
1570 	mpath = (struct msu_path *)kmem_zalloc((size_t)size, KM_SLEEP);
1571 	plink_id = (int *)kmem_zalloc((sizeof (int) * devcnt), KM_SLEEP);
1572 
1573 	mdev = (struct msu_dev *)(mpath + 1);
1574 	for (i = 0; i < devcnt; i++) {
1575 		/* Connect two streams */
1576 		rval = oplmsu_plink_serial(ser_dl->dip, msu_lh, &plink_id[i]);
1577 		if (rval != 0) {
1578 			cmn_err(CE_WARN, "oplmsu: conf-stream: "
1579 			    "i_plink failed. errno = %d", rval);
1580 			next_ser_dl = ser_dl->next;
1581 			kmem_free(ser_dl, sizeof (ser_devl_t));
1582 			ser_dl = next_ser_dl;
1583 			continue;
1584 		}
1585 
1586 		rval = oplmsu_set_lpathnum(plink_id[i],
1587 		    ddi_get_instance(ser_dl->dip));
1588 		if (rval != 0) {
1589 			cmn_err(CE_WARN, "oplmsu: conf-stream: "
1590 			    "Link id %d is not found", plink_id[i]);
1591 			/* Issue ioctl(I_PUNLINK) */
1592 			(void) ldi_ioctl(msu_lh, I_PUNLINK,
1593 			    (intptr_t)plink_id[i], FKIOCTL, kcred, &param);
1594 			next_ser_dl = ser_dl->next;
1595 			kmem_free(ser_dl, sizeof (ser_devl_t));
1596 			ser_dl = next_ser_dl;
1597 			continue;
1598 		}
1599 
1600 		mdev->dip = ser_dl->dip;
1601 		next_ser_dl = ser_dl->next;
1602 		kmem_free(ser_dl, sizeof (ser_devl_t));
1603 		ser_dl = next_ser_dl;
1604 
1605 		mdev++;
1606 		connected++;
1607 	}
1608 
1609 	if (connected == 0) {
1610 		cmn_err(CE_WARN, "oplmsu: conf-stream: "
1611 		    "Connected paths = %d", connected);
1612 		(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
1613 		ldi_ident_release(msu_li);
1614 		kmem_free(plink_id, (sizeof (int) * devcnt));
1615 		kmem_free(mpath, size);
1616 		return;
1617 	}
1618 
1619 	/* Setup all structure */
1620 	mpath->num = connected;
1621 	rval = oplmsu_config_new(mpath);
1622 	if (rval != 0) {
1623 		cmn_err(CE_WARN, "oplmsu: conf-stream: "
1624 		    "Failed to create all paths. errno = %d", rval);
1625 		oplmsu_unlinks(msu_lh, plink_id, devcnt);
1626 		(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
1627 		ldi_ident_release(msu_li);
1628 		kmem_free(plink_id, (sizeof (int) * devcnt));
1629 		kmem_free(mpath, size);
1630 		return;
1631 	}
1632 
1633 	/* Start to use all paths */
1634 	rval = oplmsu_config_start(MSU_PATH_ALL);
1635 	if (rval != 0) {
1636 		cmn_err(CE_WARN, "oplmsu: conf-stream: "
1637 		    "Failed to start all paths. errno = %d", rval);
1638 
1639 		/* Delete the path */
1640 		rval = oplmsu_config_del(mpath);
1641 		if (rval == 0) {
1642 			oplmsu_unlinks(msu_lh, plink_id, devcnt);
1643 		}
1644 	}
1645 
1646 	(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
1647 	ldi_ident_release(msu_li);
1648 	kmem_free(plink_id, (sizeof (int) * devcnt));
1649 	kmem_free(mpath, size);
1650 
1651 	DBG_PRINT((CE_NOTE, "oplmsu: conf-stream: stream configuration end!"));
1652 }
1653 
1654 void
1655 oplmsu_unlinks(ldi_handle_t msu_lh, int *plink_id, int devcnt)
1656 {
1657 	int	i;
1658 	int	param = 0;
1659 
1660 	for (i = 0; i < devcnt; i++) {
1661 		if (plink_id[i] == 0) {
1662 			continue;
1663 		}
1664 
1665 		/* Issue ioctl(I_PUNLINK) */
1666 		(void) ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)plink_id[i],
1667 		    FKIOCTL, kcred, &param);
1668 	}
1669 }
1670 
1671 void
1672 oplmsu_setup(uinst_t *msu_uinst)
1673 {
1674 
1675 	DBG_PRINT((CE_NOTE, "oplmsu: setup: Background thread start!"));
1676 
1677 	mutex_enter(&oplmsu_bthrd_excl);
1678 	if (oplmsu_conf_st == MSU_CONFIGURING) {
1679 		mutex_exit(&oplmsu_bthrd_excl);
1680 		oplmsu_conf_stream(msu_uinst);	/* Configure stream */
1681 		mutex_enter(&oplmsu_bthrd_excl);
1682 		oplmsu_conf_st = MSU_CONFIGURED;
1683 		cv_broadcast(&oplmsu_conf_cv);	/* Wake up from cv_wait_sig() */
1684 	}
1685 
1686 	if (oplmsu_bthrd_id != NULL) {
1687 		oplmsu_bthrd_id = NULL;
1688 	}
1689 	mutex_exit(&oplmsu_bthrd_excl);
1690 
1691 	DBG_PRINT((CE_NOTE, "oplmsu: setup: Background thread end!"));
1692 
1693 	thread_exit();
1694 }
1695 
1696 int
1697 oplmsu_create_upath(dev_info_t *dip)
1698 {
1699 	upath_t		*upath;
1700 	lpath_t		*lpath;
1701 	dev_info_t	*cmuch_dip;
1702 	int		instance;
1703 	int		lsb;
1704 
1705 	cmuch_dip = ddi_get_parent(ddi_get_parent(dip));
1706 	lsb = ddi_prop_get_int(DDI_DEV_T_ANY, cmuch_dip, 0, MSU_BOARD_PROP,
1707 	    FAILURE);
1708 	if (lsb == FAILURE) {
1709 		return (lsb);
1710 	}
1711 
1712 	instance = ddi_get_instance(dip);
1713 
1714 	mutex_enter(&oplmsu_uinst->l_lock);
1715 	lpath = oplmsu_uinst->first_lpath;
1716 	while (lpath) {
1717 		if (lpath->path_no == instance) {
1718 			break;
1719 		}
1720 		lpath = lpath->l_next;
1721 	}
1722 
1723 	if (lpath == NULL) {
1724 		mutex_exit(&oplmsu_uinst->l_lock);
1725 		return (ENODEV);
1726 	}
1727 
1728 	upath = (upath_t *)kmem_zalloc(sizeof (upath_t), KM_SLEEP);
1729 
1730 	/*
1731 	 * Initialize members of upath_t
1732 	 */
1733 
1734 	upath->path_no = instance;
1735 	upath->lpath = lpath;
1736 	upath->ser_devcb.dip = dip;
1737 	upath->ser_devcb.lsb = lsb;
1738 	oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STOP, MSU_PSTAT_EMPTY,
1739 	    MSU_STOP);
1740 
1741 	lpath->src_upath = NULL;
1742 	lpath->status = MSU_EXT_NOTUSED;
1743 	mutex_exit(&oplmsu_uinst->l_lock);
1744 
1745 	oplmsu_link_upath(upath);
1746 	return (SUCCESS);
1747 }
1748 
1749 /* Setup new upper instance structure */
1750 int
1751 oplmsu_config_new(struct msu_path *mpath)
1752 {
1753 	struct msu_dev	*mdev;
1754 	int		i;
1755 	int		rval = SUCCESS;
1756 
1757 	DBG_PRINT((CE_NOTE, "oplmsu: conf-new: config_new() called"));
1758 	ASSERT(mpath);
1759 
1760 	if (mpath->num == 0) {
1761 		cmn_err(CE_WARN, "oplmsu: conf-new: "
1762 		    "Number of paths = %d", mpath->num);
1763 		return (EINVAL);
1764 	}
1765 
1766 	rw_enter(&oplmsu_uinst->lock, RW_WRITER);
1767 
1768 	mutex_enter(&oplmsu_uinst->l_lock);
1769 	rval = oplmsu_check_lpath_usable();
1770 	mutex_exit(&oplmsu_uinst->l_lock);
1771 
1772 	if (rval == BUSY) { /* Check whether Lower path is usable */
1773 		rw_exit(&oplmsu_uinst->lock);
1774 		cmn_err(CE_WARN, "oplmsu: conf-new: "
1775 		    "Other processing is using this device");
1776 		return (EBUSY);
1777 	}
1778 
1779 	/*
1780 	 * Because the OPLMSU instance already exists when the upper path
1781 	 * table exists, the configure_new processing cannot be done.
1782 	 */
1783 
1784 	mutex_enter(&oplmsu_uinst->u_lock);
1785 
1786 	if ((oplmsu_uinst->first_upath != NULL) ||
1787 	    (oplmsu_uinst->last_upath != NULL)) {
1788 		mutex_exit(&oplmsu_uinst->u_lock);
1789 		rw_exit(&oplmsu_uinst->lock);
1790 		cmn_err(CE_WARN, "oplmsu: conf-new: upath_t already exist");
1791 		return (EINVAL);
1792 	}
1793 
1794 	/*
1795 	 * Because the config_new processing has already been done
1796 	 * if oplmsu_uinst->path_num isn't -1, this processing cannot be
1797 	 * continued.
1798 	 */
1799 
1800 	if (oplmsu_uinst->path_num != UNDEFINED) {
1801 		mutex_exit(&oplmsu_uinst->u_lock);
1802 		rw_exit(&oplmsu_uinst->lock);
1803 		cmn_err(CE_WARN, "oplmsu: conf-new: "
1804 		    "conf-new processing has already been completed");
1805 		return (EINVAL);
1806 	}
1807 
1808 	/*
1809 	 * Only the number of specified paths makes the upper path
1810 	 * information tables.
1811 	 */
1812 
1813 	mdev = (struct msu_dev *)(mpath + 1);
1814 	for (i = 0; i < mpath->num; i++) {
1815 		/*
1816 		 * Associate upper path information table with lower path
1817 		 * information table.
1818 		 *
1819 		 * If the upper path information table and the lower path
1820 		 * information table cannot be associated, the link list of
1821 		 * the upper path information table is released.
1822 		 */
1823 		rval = oplmsu_create_upath(mdev->dip);
1824 		if (rval != SUCCESS) {
1825 			oplmsu_delete_upath_info();
1826 			mutex_exit(&oplmsu_uinst->u_lock);
1827 			rw_exit(&oplmsu_uinst->lock);
1828 			cmn_err(CE_WARN, "oplmsu: conf-new: "
1829 			    "Failed to create upath %d", rval);
1830 			return (rval);
1831 		}
1832 
1833 		mdev++;
1834 	}
1835 
1836 	/*
1837 	 * Setup members of uinst_t
1838 	 */
1839 
1840 	oplmsu_uinst->inst_status = oplmsu_get_inst_status();
1841 	oplmsu_uinst->path_num = mpath->num;
1842 	oplmsu_uinst->lower_queue = NULL;
1843 	mutex_exit(&oplmsu_uinst->u_lock);
1844 	rw_exit(&oplmsu_uinst->lock);
1845 	return (SUCCESS);
1846 }
1847 
1848 /* Add path information */
1849 int
1850 oplmsu_config_add(dev_info_t *dip)
1851 {
1852 	upath_t	*upath;
1853 	int	instance;
1854 	int	rval = SUCCESS;
1855 
1856 	DBG_PRINT((CE_NOTE, "oplmsu: conf-add: config_add() called"));
1857 	ASSERT(dip);
1858 
1859 	instance = ddi_get_instance(dip);
1860 	rw_enter(&oplmsu_uinst->lock, RW_WRITER);
1861 
1862 	if (oplmsu_uinst->path_num == UNDEFINED) {
1863 		rw_exit(&oplmsu_uinst->lock);
1864 		cmn_err(CE_WARN, "oplmsu: conf-add: "
1865 		    "conf-new processing has not been completed yet");
1866 		return (EINVAL);
1867 	}
1868 
1869 	mutex_enter(&oplmsu_uinst->u_lock);
1870 	upath = oplmsu_search_upath_info(instance);
1871 	if (upath != NULL) {
1872 		mutex_exit(&oplmsu_uinst->u_lock);
1873 		rw_exit(&oplmsu_uinst->lock);
1874 		cmn_err(CE_WARN, "oplmsu: conf-add: "
1875 		    "Proper upath_t doesn't find");
1876 		return (EINVAL);
1877 	}
1878 
1879 	rval = oplmsu_create_upath(dip);
1880 	if (rval != SUCCESS) {
1881 		mutex_exit(&oplmsu_uinst->u_lock);
1882 		rw_exit(&oplmsu_uinst->lock);
1883 		cmn_err(CE_WARN, "oplmsu: conf-add: "
1884 		    "Failed to create upath %d", rval);
1885 		return (rval);
1886 	}
1887 
1888 	oplmsu_uinst->inst_status = oplmsu_get_inst_status();
1889 	oplmsu_uinst->path_num = oplmsu_get_pathnum();
1890 	mutex_exit(&oplmsu_uinst->u_lock);
1891 	rw_exit(&oplmsu_uinst->lock);
1892 	return (SUCCESS);
1893 }
1894 
1895 /* Delete each path information */
1896 int
1897 oplmsu_config_del(struct msu_path *mpath)
1898 {
1899 	struct msu_dev	*mdev;
1900 	upath_t		*upath;
1901 	lpath_t		*lpath;
1902 	int		rval = SUCCESS;
1903 	int		use_flag;
1904 	int		i;
1905 
1906 	DBG_PRINT((CE_NOTE, "oplmsu: conf-del: config_del() called"));
1907 	ASSERT(mpath);
1908 
1909 	mdev = (struct msu_dev *)(mpath + 1);
1910 
1911 	rw_enter(&oplmsu_uinst->lock, RW_WRITER);
1912 	mutex_enter(&oplmsu_uinst->u_lock);
1913 	for (i = 0; i < mpath->num; i++) {
1914 		upath = oplmsu_search_upath_info(ddi_get_instance(mdev->dip));
1915 		if (upath == NULL) {
1916 			cmn_err(CE_WARN, "oplmsu: conf-del: "
1917 			    "Proper upath_t doesn't find");
1918 			rval = ENODEV;
1919 			mdev++;
1920 			continue;
1921 		}
1922 
1923 		lpath = upath->lpath;
1924 		if (lpath == NULL) {
1925 			if ((upath->traditional_status == MSU_WSTP_ACK) ||
1926 			    (upath->traditional_status == MSU_WSTR_ACK) ||
1927 			    (upath->traditional_status == MSU_WPTH_CHG) ||
1928 			    (upath->traditional_status == MSU_WTCS_ACK) ||
1929 			    (upath->traditional_status == MSU_WTMS_ACK) ||
1930 			    (upath->traditional_status == MSU_WPPS_ACK) ||
1931 			    (upath->traditional_status == MSU_WWSZ_ACK) ||
1932 			    (upath->traditional_status == MSU_WCAR_ACK)) {
1933 				cmn_err(CE_WARN, "oplmsu: conf-del: "
1934 				    "Other processing is using this device");
1935 				rval = EBUSY;
1936 				mdev++;
1937 				continue;
1938 			}
1939 
1940 			if ((upath->status != MSU_PSTAT_DISCON) ||
1941 			    (upath->traditional_status != MSU_DISCON)) {
1942 				cmn_err(CE_WARN, "oplmsu: conf-del: "
1943 				    "Status of path is improper");
1944 				rval = EINVAL;
1945 				mdev++;
1946 				continue;
1947 			}
1948 		} else {
1949 			mutex_enter(&oplmsu_uinst->l_lock);
1950 			use_flag = oplmsu_set_ioctl_path(lpath, NULL, NULL);
1951 			if (use_flag == BUSY) {
1952 				mutex_exit(&oplmsu_uinst->l_lock);
1953 				cmn_err(CE_WARN, "oplmsu: conf-del: "
1954 				    "Other processing is using lower path");
1955 				rval = EBUSY;
1956 				mdev++;
1957 				continue;
1958 			}
1959 
1960 			if (((upath->status != MSU_PSTAT_STOP) ||
1961 			    (upath->traditional_status != MSU_STOP)) &&
1962 			    ((upath->status != MSU_PSTAT_FAIL) ||
1963 			    (upath->traditional_status != MSU_FAIL))) {
1964 				oplmsu_clear_ioctl_path(lpath);
1965 				mutex_exit(&oplmsu_uinst->l_lock);
1966 				cmn_err(CE_WARN, "oplmsu: conf-del: "
1967 				    "Status of path isn't 'Offline:stop/fail'");
1968 				rval = EINVAL;
1969 				mdev++;
1970 				continue;
1971 			}
1972 			lpath->src_upath = NULL;
1973 			lpath->status = MSU_SETID_NU;
1974 			oplmsu_clear_ioctl_path(lpath);
1975 			mutex_exit(&oplmsu_uinst->l_lock);
1976 		}
1977 		oplmsu_unlink_upath(upath);	/* Unlink upath_t */
1978 		kmem_free(upath, sizeof (upath_t));
1979 		mdev++;
1980 	}
1981 
1982 	oplmsu_uinst->inst_status = oplmsu_get_inst_status();
1983 	oplmsu_uinst->path_num = oplmsu_get_pathnum();
1984 	mutex_exit(&oplmsu_uinst->u_lock);
1985 	rw_exit(&oplmsu_uinst->lock);
1986 	return (rval);
1987 }
1988 
1989 /* Stop to use the path */
1990 int
1991 oplmsu_config_stop(int pathnum)
1992 {
1993 	upath_t	*upath, *altn_upath;
1994 	lpath_t	*lpath, *altn_lpath;
1995 	queue_t	*stp_queue = NULL;
1996 	queue_t	*dst_queue = NULL;
1997 	mblk_t	*nmp = NULL, *fmp = NULL;
1998 	ctrl_t	*ctrl;
1999 	int	term_ioctl, term_stat;
2000 	int	use_flag;
2001 
2002 	DBG_PRINT((CE_NOTE,
2003 	    "oplmsu: conf-stop: config_stop(%d) called", pathnum));
2004 
2005 	if (pathnum == MSU_PATH_ALL) {
2006 		cmn_err(CE_WARN, "oplmsu: conf-stop: "
2007 		    "All path can't be transferred to the status of "
2008 		    "'Offline:stop'");
2009 		return (EINVAL);
2010 	}
2011 
2012 	rw_enter(&oplmsu_uinst->lock, RW_WRITER);
2013 	mutex_enter(&oplmsu_uinst->u_lock);
2014 
2015 	upath = oplmsu_search_upath_info(pathnum);	/* Search upath_t */
2016 	if (upath == NULL) {
2017 		mutex_exit(&oplmsu_uinst->u_lock);
2018 		rw_exit(&oplmsu_uinst->lock);
2019 		cmn_err(CE_WARN, "oplmsu: conf-stop: "
2020 		    "Proper upath_t doesn't find");
2021 		return (ENODEV);
2022 	}
2023 
2024 	lpath = upath->lpath;
2025 	if (lpath == NULL) {
2026 		mutex_exit(&oplmsu_uinst->u_lock);
2027 		rw_exit(&oplmsu_uinst->lock);
2028 		cmn_err(CE_WARN, "oplmsu: conf-stop: "
2029 		    "Proper lpath_t doesn't exist");
2030 		return (ENODEV);
2031 	}
2032 
2033 	mutex_enter(&oplmsu_uinst->l_lock);
2034 
2035 	/* Check status of lpath_t */
2036 	use_flag = oplmsu_set_ioctl_path(lpath, NULL, NULL);
2037 	if (use_flag == BUSY) {
2038 		mutex_exit(&oplmsu_uinst->l_lock);
2039 		mutex_exit(&oplmsu_uinst->u_lock);
2040 		rw_exit(&oplmsu_uinst->lock);
2041 		cmn_err(CE_WARN, "oplmsu: conf-stop: "
2042 		    "Other processing is using lower path");
2043 		return (EBUSY);
2044 	}
2045 
2046 	if (upath->status == MSU_PSTAT_FAIL) {
2047 		oplmsu_clear_ioctl_path(lpath);
2048 		mutex_exit(&oplmsu_uinst->l_lock);
2049 		mutex_exit(&oplmsu_uinst->u_lock);
2050 		rw_exit(&oplmsu_uinst->lock);
2051 		return (EIO);
2052 	} else if ((upath->status == MSU_PSTAT_STOP) &&
2053 	    (upath->traditional_status == MSU_STOP)) {
2054 		oplmsu_clear_ioctl_path(lpath);
2055 		mutex_exit(&oplmsu_uinst->l_lock);
2056 		mutex_exit(&oplmsu_uinst->u_lock);
2057 		rw_exit(&oplmsu_uinst->lock);
2058 		return (SUCCESS);
2059 	} else if ((upath->status == MSU_PSTAT_STANDBY) &&
2060 	    (upath->traditional_status == MSU_STANDBY)) {
2061 		oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STOP,
2062 		    upath->status, MSU_STOP);
2063 		oplmsu_clear_ioctl_path(lpath);
2064 		lpath->src_upath = NULL;
2065 		lpath->status = MSU_EXT_NOTUSED;
2066 
2067 		oplmsu_uinst->inst_status = oplmsu_get_inst_status();
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_ACTIVE) &&
2073 	    (upath->traditional_status == MSU_ACTIVE)) {
2074 		altn_upath = oplmsu_search_standby();
2075 		if (altn_upath == NULL) { /* Alternate path doesn't exist */
2076 			DBG_PRINT((CE_NOTE, "oplmsu: conf-stop: "
2077 			    "Alternate upper path doesn't find"));
2078 			oplmsu_clear_ioctl_path(lpath);
2079 			mutex_exit(&oplmsu_uinst->l_lock);
2080 			mutex_exit(&oplmsu_uinst->u_lock);
2081 			rw_exit(&oplmsu_uinst->lock);
2082 			return (EINVAL);
2083 		}
2084 
2085 		if ((fmp = allocb(sizeof (char), BPRI_LO)) == NULL) {
2086 			oplmsu_clear_ioctl_path(lpath);
2087 			mutex_exit(&oplmsu_uinst->l_lock);
2088 			mutex_exit(&oplmsu_uinst->u_lock);
2089 			rw_exit(&oplmsu_uinst->lock);
2090 			return (ENOSR);
2091 		}
2092 
2093 		if (oplmsu_stop_prechg(&nmp, &term_ioctl, &term_stat) !=
2094 		    SUCCESS) {
2095 			oplmsu_clear_ioctl_path(lpath);
2096 			mutex_exit(&oplmsu_uinst->l_lock);
2097 			mutex_exit(&oplmsu_uinst->u_lock);
2098 			rw_exit(&oplmsu_uinst->lock);
2099 			freeb(fmp);
2100 			return (ENOSR);
2101 		}
2102 
2103 		altn_lpath = altn_upath->lpath;
2104 		use_flag = oplmsu_set_ioctl_path(altn_lpath, NULL, NULL);
2105 		if (use_flag == BUSY) {
2106 			oplmsu_clear_ioctl_path(lpath);
2107 			mutex_exit(&oplmsu_uinst->l_lock);
2108 			mutex_exit(&oplmsu_uinst->u_lock);
2109 			rw_exit(&oplmsu_uinst->lock);
2110 
2111 			cmn_err(CE_WARN, "oplmsu: conf-stop: "
2112 			    "Other processing is using alternate lower path");
2113 			freeb(fmp);
2114 			freemsg(nmp);
2115 			return (EBUSY);
2116 		}
2117 
2118 		dst_queue = WR(altn_lpath->lower_queue);
2119 
2120 		/* termios is not held. Change alternate path to MSU_ACTIVE */
2121 		if (nmp == NULL) {
2122 			altn_upath->traditional_status = term_stat;
2123 			altn_lpath->src_upath = upath;
2124 			altn_lpath->status = MSU_EXT_VOID;
2125 
2126 			oplmsu_uinst->lower_queue = NULL;
2127 
2128 			ctrl = oplmsu_uinst->user_ctrl;
2129 			if (ctrl != NULL) {
2130 				mutex_enter(&oplmsu_uinst->c_lock);
2131 				stp_queue = WR(ctrl->queue);
2132 				mutex_exit(&oplmsu_uinst->c_lock);
2133 				noenable(stp_queue);
2134 				oplmsu_queue_flag = 1;
2135 			}
2136 
2137 			/* Make M_FLUSH and send to alternate path */
2138 			oplmsu_cmn_set_mflush(fmp);
2139 			putq(dst_queue, fmp);
2140 
2141 			/* Change status of alternate path */
2142 			oplmsu_cmn_set_upath_sts(altn_upath, MSU_PSTAT_ACTIVE,
2143 			    altn_upath->status, MSU_ACTIVE);
2144 
2145 			oplmsu_clear_ioctl_path(altn_lpath);
2146 			altn_lpath->uinst = oplmsu_uinst;
2147 			altn_lpath->src_upath = NULL;
2148 			altn_lpath->status = MSU_EXT_NOTUSED;
2149 
2150 			/* Notify of the active path changing */
2151 			prom_opl_switch_console(altn_upath->ser_devcb.lsb);
2152 
2153 			/* Send XON to notify active path */
2154 			(void) oplmsu_cmn_put_xoffxon(dst_queue, MSU_XON_4);
2155 
2156 			/* Send XOFF to notify all standby paths */
2157 			oplmsu_cmn_putxoff_standby();
2158 
2159 			oplmsu_uinst->lower_queue = RD(dst_queue);
2160 			ctrl = oplmsu_uinst->user_ctrl;
2161 
2162 			/* Switch active path of oplmsu */
2163 			if (ctrl != NULL) {
2164 				queue_t	*altn_queue;
2165 
2166 				mutex_enter(&oplmsu_uinst->c_lock);
2167 				altn_queue = WR(ctrl->queue);
2168 				mutex_exit(&oplmsu_uinst->c_lock);
2169 
2170 				/* Restart queuing of user access node */
2171 				enableok(altn_queue);
2172 
2173 				oplmsu_queue_flag = 0;
2174 				mutex_exit(&oplmsu_uinst->l_lock);
2175 				mutex_exit(&oplmsu_uinst->u_lock);
2176 				oplmsu_wcmn_high_qenable(altn_queue, RW_WRITER);
2177 				mutex_enter(&oplmsu_uinst->u_lock);
2178 				mutex_enter(&oplmsu_uinst->l_lock);
2179 			}
2180 
2181 			/* Stop previous active path */
2182 			oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STOP,
2183 			    upath->status, MSU_STOP);
2184 
2185 			lpath->uinst = NULL;
2186 			lpath->src_upath = NULL;
2187 			lpath->status = MSU_EXT_NOTUSED;
2188 			oplmsu_clear_ioctl_path(lpath);
2189 
2190 			oplmsu_uinst->inst_status = oplmsu_get_inst_status();
2191 			mutex_exit(&oplmsu_uinst->l_lock);
2192 			mutex_exit(&oplmsu_uinst->u_lock);
2193 			rw_exit(&oplmsu_uinst->lock);
2194 			return (SUCCESS);
2195 		}
2196 
2197 		/* Send termios information to alternate path */
2198 		if (canput(dst_queue)) {
2199 			altn_upath->traditional_status = term_stat;
2200 			altn_lpath->src_upath = upath;
2201 			altn_lpath->status = MSU_EXT_VOID;
2202 
2203 			upath->traditional_status = MSU_WSTP_ACK;
2204 			lpath->uinst = NULL;
2205 
2206 			oplmsu_uinst->lower_queue = NULL;
2207 
2208 			ctrl = oplmsu_uinst->user_ctrl;
2209 			if (ctrl != NULL) {
2210 				mutex_enter(&oplmsu_uinst->c_lock);
2211 				stp_queue = WR(ctrl->queue);
2212 				mutex_exit(&oplmsu_uinst->c_lock);
2213 				noenable(stp_queue);
2214 				oplmsu_queue_flag = 1;
2215 			}
2216 
2217 			mutex_exit(&oplmsu_uinst->l_lock);
2218 			mutex_exit(&oplmsu_uinst->u_lock);
2219 			rw_exit(&oplmsu_uinst->lock);
2220 			oplmsu_cmn_set_mflush(fmp);
2221 			putq(dst_queue, fmp);
2222 			putq(dst_queue, nmp);
2223 
2224 			mutex_enter(&oplmsu_uinst->l_lock);
2225 			lpath->sw_flag = 1;
2226 			while (lpath->sw_flag != 0) {
2227 				/* Wait for the completion of path switching */
2228 				cv_wait(&lpath->sw_cv, &oplmsu_uinst->l_lock);
2229 			}
2230 			mutex_exit(&oplmsu_uinst->l_lock);
2231 			return (SUCCESS);
2232 		} else {
2233 			oplmsu_clear_ioctl_path(altn_lpath);
2234 			oplmsu_clear_ioctl_path(lpath);
2235 			mutex_exit(&oplmsu_uinst->l_lock);
2236 			mutex_exit(&oplmsu_uinst->u_lock);
2237 			rw_exit(&oplmsu_uinst->lock);
2238 			freeb(fmp);
2239 			freemsg(nmp);
2240 			return (FAILURE);
2241 		}
2242 		/* NOTREACHED */
2243 	} else {
2244 		oplmsu_clear_ioctl_path(lpath);
2245 		mutex_exit(&oplmsu_uinst->l_lock);
2246 		mutex_exit(&oplmsu_uinst->u_lock);
2247 		rw_exit(&oplmsu_uinst->lock);
2248 
2249 		cmn_err(CE_WARN, "oplmsu: conf-stop: "
2250 		    "Status of path is improper");
2251 		return (EINVAL);
2252 	}
2253 	/* NOTREACHED */
2254 }
2255 
2256 /* Start to use path */
2257 int
2258 oplmsu_config_start(int pathnum)
2259 {
2260 	upath_t	*upath = NULL;
2261 	lpath_t	*lpath = NULL;
2262 	queue_t	*dst_queue, *main_rq = NULL;
2263 	int	msu_tty_port;
2264 
2265 	DBG_PRINT((CE_NOTE,
2266 	    "oplmsu: conf-start: config_start(%d) called", pathnum));
2267 
2268 	rw_enter(&oplmsu_uinst->lock, RW_WRITER);
2269 	mutex_enter(&oplmsu_uinst->u_lock);
2270 
2271 	if (oplmsu_get_inst_status() == INST_STAT_BUSY) {
2272 		mutex_exit(&oplmsu_uinst->u_lock);
2273 		rw_exit(&oplmsu_uinst->lock);
2274 		return (EBUSY);
2275 	}
2276 
2277 	if (pathnum == MSU_PATH_ALL) {
2278 		(void) oplmsu_search_min_stop_path();
2279 	}
2280 
2281 	for (upath = oplmsu_uinst->first_upath; upath; ) {
2282 		if ((pathnum != MSU_PATH_ALL) && (upath->path_no != pathnum)) {
2283 			upath = upath->u_next;
2284 			continue;
2285 		}
2286 
2287 		if (upath->path_no == pathnum) {
2288 			lpath = upath->lpath;
2289 			if (lpath == NULL) {
2290 				mutex_exit(&oplmsu_uinst->u_lock);
2291 				rw_exit(&oplmsu_uinst->lock);
2292 				cmn_err(CE_WARN, "oplmsu: conf-start: "
2293 				    "Proper lpath_t doesn't exist");
2294 				return (EINVAL);
2295 			}
2296 
2297 			oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STANDBY,
2298 			    upath->status, MSU_STANDBY);
2299 
2300 			mutex_enter(&oplmsu_uinst->l_lock);
2301 			lpath->src_upath = NULL;
2302 			lpath->status = MSU_EXT_NOTUSED;
2303 			mutex_exit(&oplmsu_uinst->l_lock);
2304 			mutex_exit(&oplmsu_uinst->u_lock);
2305 			rw_exit(&oplmsu_uinst->lock);
2306 			return (SUCCESS);
2307 		}
2308 
2309 		/*
2310 		 * with PATH_ALL
2311 		 */
2312 		lpath = upath->lpath;
2313 		if (lpath == NULL) {
2314 			upath = upath->u_next;
2315 
2316 			DBG_PRINT((CE_WARN, "oplmsu: conf-start: "
2317 			    "Proper lpath_t doesn't exist"));
2318 			continue;
2319 		}
2320 
2321 		msu_tty_port = ddi_prop_get_int(DDI_DEV_T_ANY,
2322 		    oplmsu_uinst->msu_dip, 0, MSU_TTY_PORT_PROP, -1);
2323 
2324 		if (upath->ser_devcb.lsb == msu_tty_port) {
2325 			/* Notify of the active path changing */
2326 			prom_opl_switch_console(upath->ser_devcb.lsb);
2327 
2328 			oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_ACTIVE,
2329 			    upath->status, MSU_ACTIVE);
2330 
2331 			mutex_enter(&oplmsu_uinst->l_lock);
2332 			main_rq = RD(lpath->lower_queue);
2333 			dst_queue = WR(lpath->lower_queue);
2334 			lpath->src_upath = NULL;
2335 			lpath->status = MSU_EXT_NOTUSED;
2336 			lpath->uinst = oplmsu_uinst;
2337 			mutex_exit(&oplmsu_uinst->l_lock);
2338 
2339 			/* Send XON to notify active path */
2340 			(void) oplmsu_cmn_put_xoffxon(dst_queue, MSU_XON_4);
2341 		} else {
2342 			oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STANDBY,
2343 			    upath->status, MSU_STANDBY);
2344 
2345 			mutex_enter(&oplmsu_uinst->l_lock);
2346 			lpath->src_upath = NULL;
2347 			lpath->status = MSU_EXT_NOTUSED;
2348 			mutex_exit(&oplmsu_uinst->l_lock);
2349 		}
2350 		upath = upath->u_next;
2351 	}
2352 
2353 	if (main_rq == NULL) {
2354 		upath_t	*altn_upath;
2355 		lpath_t	*altn_lpath;
2356 
2357 		altn_upath = oplmsu_search_standby();
2358 		if (altn_upath) {
2359 			oplmsu_cmn_set_upath_sts(altn_upath, MSU_PSTAT_ACTIVE,
2360 			    altn_upath->status, MSU_ACTIVE);
2361 
2362 			/* Notify of the active path changing */
2363 			prom_opl_switch_console(altn_upath->ser_devcb.lsb);
2364 
2365 			altn_lpath = altn_upath->lpath;
2366 			if (altn_lpath) {
2367 				mutex_enter(&oplmsu_uinst->l_lock);
2368 				main_rq = RD(altn_lpath->lower_queue);
2369 				dst_queue = WR(altn_lpath->lower_queue);
2370 				altn_lpath->src_upath = NULL;
2371 				altn_lpath->status = MSU_EXT_NOTUSED;
2372 				altn_lpath->uinst = oplmsu_uinst;
2373 				mutex_exit(&oplmsu_uinst->l_lock);
2374 
2375 				/* Send XON to notify active path */
2376 				(void) oplmsu_cmn_put_xoffxon(dst_queue,
2377 				    MSU_XON_4);
2378 			} else {
2379 				cmn_err(CE_WARN, "oplmsu: conf-start: "
2380 				    "Proper alternate lpath_t doesn't exist");
2381 			}
2382 		} else {
2383 			cmn_err(CE_WARN, "oplmsu: conf-start: "
2384 			    "Proper alternate upath_t doesn't exist");
2385 		}
2386 	}
2387 
2388 	mutex_enter(&oplmsu_uinst->l_lock);
2389 
2390 	/* Send XOFF to notify all standby paths */
2391 	oplmsu_cmn_putxoff_standby();
2392 
2393 	/* Change active path of oplmsu */
2394 	oplmsu_uinst->lower_queue = main_rq;
2395 	oplmsu_uinst->inst_status = oplmsu_get_inst_status();
2396 	mutex_exit(&oplmsu_uinst->l_lock);
2397 	mutex_exit(&oplmsu_uinst->u_lock);
2398 	rw_exit(&oplmsu_uinst->lock);
2399 	return (SUCCESS);
2400 }
2401 
2402 /* Prepare of unlink path */
2403 int
2404 oplmsu_config_disc(int pathnum)
2405 {
2406 	upath_t	*upath;
2407 	lpath_t	*lpath;
2408 	int	use_flag;
2409 
2410 	DBG_PRINT((CE_NOTE,
2411 	    "oplmsu: conf-disc: config_disc(%d) called", pathnum));
2412 
2413 	rw_enter(&oplmsu_uinst->lock, RW_READER);
2414 	mutex_enter(&oplmsu_uinst->u_lock);
2415 
2416 	upath = oplmsu_search_upath_info(pathnum);
2417 	if (upath == NULL) {
2418 		mutex_exit(&oplmsu_uinst->u_lock);
2419 		rw_exit(&oplmsu_uinst->lock);
2420 		cmn_err(CE_WARN, "oplmsu: conf-disc: "
2421 		    "Proper upath_t doesn't find");
2422 		return (EINVAL);
2423 	}
2424 
2425 	if ((upath->status == MSU_PSTAT_DISCON) ||
2426 	    (upath->traditional_status == MSU_DISCON)) {
2427 		mutex_exit(&oplmsu_uinst->u_lock);
2428 		rw_exit(&oplmsu_uinst->lock);
2429 		return (SUCCESS);
2430 	} else if (((upath->status != MSU_PSTAT_STOP) ||
2431 	    (upath->traditional_status != MSU_STOP)) &&
2432 	    ((upath->status != MSU_PSTAT_FAIL) ||
2433 	    (upath->traditional_status != MSU_FAIL))) {
2434 		mutex_exit(&oplmsu_uinst->u_lock);
2435 		rw_exit(&oplmsu_uinst->lock);
2436 		cmn_err(CE_WARN, "oplmsu: conf-disc: "
2437 		    "Status of path is improper");
2438 		return (EINVAL);
2439 	}
2440 
2441 	lpath = upath->lpath;
2442 	if (lpath == NULL) {
2443 		mutex_exit(&oplmsu_uinst->u_lock);
2444 		rw_exit(&oplmsu_uinst->lock);
2445 		cmn_err(CE_WARN, "oplmsu: conf-disc: "
2446 		    "Proper lpath_t doesn't exist");
2447 		return (ENODEV);
2448 	}
2449 
2450 	mutex_enter(&oplmsu_uinst->l_lock);
2451 
2452 	/* Check lower path status */
2453 	use_flag = oplmsu_set_ioctl_path(lpath, NULL, NULL);
2454 	if (use_flag == BUSY) {
2455 		mutex_exit(&oplmsu_uinst->l_lock);
2456 		mutex_exit(&oplmsu_uinst->u_lock);
2457 		rw_exit(&oplmsu_uinst->lock);
2458 		cmn_err(CE_WARN, "oplmsu: conf-disc: "
2459 		    "Other processing is using lower path");
2460 		return (EBUSY);
2461 	}
2462 
2463 	upath->status = MSU_PSTAT_STOP;
2464 	upath->traditional_status = MSU_SETID;
2465 
2466 	oplmsu_clear_ioctl_path(lpath);
2467 	mutex_exit(&oplmsu_uinst->l_lock);
2468 	mutex_exit(&oplmsu_uinst->u_lock);
2469 	rw_exit(&oplmsu_uinst->lock);
2470 	return (SUCCESS);
2471 }
2472