xref: /illumos-gate/usr/src/uts/sun4u/opl/io/oplmsu/oplmsu.c (revision 257873cfc1dd3337766407f80397db60a56f2f5a)
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 2008 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 			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 		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 		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 			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 		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 			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 	ddi_pathname(dip, wrkbuf);
1121 	*(wrkbuf + strlen(wrkbuf)) = '\0';
1122 	sprintf(pathname, "/devices%s:%c", wrkbuf, 'a'+ ddi_get_instance(dip));
1123 
1124 	/* Allocate LDI identifier */
1125 	rval = ldi_ident_from_dip(dip, &li);
1126 	if (rval != 0) {
1127 		cmn_err(CE_WARN, "oplmsu: plink-serial: "
1128 		    "%s ldi_ident_from_dip failed. errno = %d", pathname, rval);
1129 		return (rval);
1130 	}
1131 
1132 	/* Open serial */
1133 	rval = ldi_open_by_name(pathname, (FREAD|FWRITE|FEXCL), kcred, &lh, li);
1134 	if (rval != 0) {
1135 		cmn_err(CE_WARN, "oplmsu: plink-serial: "
1136 		    "%s open failed. errno = %d", pathname, rval);
1137 		ldi_ident_release(li);
1138 		return (rval);
1139 	}
1140 
1141 	/* Try to remove the top module from the stream */
1142 	param = 0;
1143 	while ((ldi_ioctl(lh, I_POP, (intptr_t)0, FKIOCTL, kcred, &param))
1144 	    == 0) {
1145 		continue;
1146 	}
1147 
1148 	/* Issue ioctl(I_PLINK) */
1149 	param = 0;
1150 	rval = ldi_ioctl(msu_lh, I_PLINK, (intptr_t)lh, FKIOCTL, kcred, &param);
1151 	if (rval != 0) {
1152 		cmn_err(CE_WARN, "oplmsu: plink-serial: "
1153 		    "%s ioctl(I_PLINK) failed. errno = %d", pathname, rval);
1154 	}
1155 
1156 	(void) ldi_close(lh, (FREAD|FWRITE|FEXCL), kcred);
1157 	ldi_ident_release(li);
1158 
1159 	*id = param;	/* Save link-id */
1160 	return (rval);
1161 }
1162 
1163 int
1164 oplmsu_set_lpathnum(int lnk_id, int instance)
1165 {
1166 	lpath_t	*lpath;
1167 	int	rval = SUCCESS;
1168 
1169 	rw_enter(&oplmsu_uinst->lock, RW_READER);
1170 	mutex_enter(&oplmsu_uinst->l_lock);
1171 	lpath = oplmsu_uinst->first_lpath;
1172 	while (lpath) {
1173 		if ((lpath->path_no == UNDEFINED) &&
1174 		    (lpath->link_id == lnk_id)) {
1175 			lpath->path_no = instance; /* Set instance number */
1176 			lpath->src_upath = NULL;
1177 			lpath->status = MSU_SETID_NU;
1178 			break;
1179 		}
1180 		lpath = lpath->l_next;
1181 	}
1182 	mutex_exit(&oplmsu_uinst->l_lock);
1183 	rw_exit(&oplmsu_uinst->lock);
1184 
1185 	if (lpath == NULL) {
1186 		rval = EINVAL;
1187 	}
1188 	return (rval);
1189 }
1190 
1191 int
1192 oplmsu_dr_attach(dev_info_t *dip)
1193 {
1194 	ldi_ident_t	msu_li = NULL;
1195 	ldi_handle_t	msu_lh = NULL;
1196 	upath_t		*upath;
1197 	int		len;
1198 	int		instance;
1199 	int		lnk_id = 0;
1200 	int		param = 0;
1201 	int		rval;
1202 
1203 	/* Get instance for serial */
1204 	instance = ddi_get_instance(dip);
1205 
1206 	rw_enter(&oplmsu_uinst->lock, RW_WRITER);
1207 	mutex_enter(&oplmsu_uinst->u_lock);
1208 
1209 	/* Get current number of paths */
1210 	oplmsu_uinst->path_num = oplmsu_get_pathnum();
1211 
1212 	/* Check specified upath_t */
1213 	upath = oplmsu_uinst->first_upath;
1214 	while (upath) {
1215 		if (instance == upath->path_no) {
1216 			break;
1217 		}
1218 		upath = upath->u_next;
1219 	}
1220 	mutex_exit(&oplmsu_uinst->u_lock);
1221 	rw_exit(&oplmsu_uinst->lock);
1222 
1223 	if (upath != NULL) {
1224 		cmn_err(CE_WARN, "oplmsu: attach(dr): "
1225 		    "Instance %d already exist", instance);
1226 		return (EINVAL);
1227 	}
1228 
1229 	/* Open oplmsu */
1230 	rval = oplmsu_open_msu(oplmsu_uinst->msu_dip, &msu_li, &msu_lh);
1231 	if (rval != 0) {
1232 		cmn_err(CE_WARN, "oplmsu: attach(dr): "
1233 		    "msu open failed. errno = %d", rval);
1234 		return (rval);
1235 	}
1236 
1237 	/* Connect two streams */
1238 	rval = oplmsu_plink_serial(dip, msu_lh, &lnk_id);
1239 	if (rval != 0) {
1240 		cmn_err(CE_WARN, "oplmsu: attach(dr): "
1241 		    "i_plink failed. errno = %d", rval);
1242 		(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
1243 		ldi_ident_release(msu_li);
1244 		return (rval);
1245 	}
1246 
1247 	rval = oplmsu_set_lpathnum(lnk_id, instance);
1248 	if (rval != 0) {
1249 		cmn_err(CE_WARN, "oplmsu: attach(dr): "
1250 		    "Link id %d is not found", lnk_id);
1251 		/* Issue ioctl(I_PUNLINK) */
1252 		(void) ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)lnk_id, FKIOCTL,
1253 		    kcred, &param);
1254 		(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
1255 		ldi_ident_release(msu_li);
1256 		return (rval);
1257 	}
1258 
1259 	/* Add the path */
1260 	rval = oplmsu_config_add(dip);
1261 	if (rval != 0) {
1262 		cmn_err(CE_WARN, "oplmsu: attach(dr): "
1263 		    "Failed to add the path. errno = %d", rval);
1264 		/* Issue ioctl(I_PUNLINK) */
1265 		(void) ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)lnk_id, FKIOCTL,
1266 		    kcred, &param);
1267 
1268 		(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
1269 		ldi_ident_release(msu_li);
1270 		return (rval);
1271 	}
1272 
1273 	/* Start to use the path */
1274 	rval = oplmsu_config_start(instance);
1275 	if (rval != 0) {
1276 		struct msu_path	*mpath;
1277 		struct msu_dev	*mdev;
1278 
1279 		cmn_err(CE_WARN, "oplmsu: attach(dr): "
1280 		    "Failed to start the path. errno = %d", rval);
1281 
1282 		len = sizeof (struct msu_path) + sizeof (struct msu_dev);
1283 		mpath = (struct msu_path *)kmem_zalloc((size_t)len, KM_SLEEP);
1284 		mpath->num = 1;
1285 		mdev = (struct msu_dev *)(mpath + 1);
1286 		mdev->dip = dip;
1287 
1288 		/* Delete the path */
1289 		if ((oplmsu_config_del(mpath)) == 0) {
1290 			/* Issue ioctl(I_PUNLINK) */
1291 			(void) ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)lnk_id,
1292 			    FKIOCTL, kcred, &param);
1293 		}
1294 		kmem_free(mpath, (size_t)len);
1295 	}
1296 
1297 	/* Close oplmsu */
1298 	(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
1299 	ldi_ident_release(msu_li);
1300 	return (rval);
1301 }
1302 
1303 int
1304 oplmsu_dr_detach(dev_info_t *dip)
1305 {
1306 	ldi_ident_t	msu_li = NULL;
1307 	ldi_handle_t	msu_lh = NULL;
1308 	struct msu_path	*mpath;
1309 	struct msu_dev	*mdev;
1310 	upath_t		*upath;
1311 	lpath_t		*lpath;
1312 	int		len;
1313 	int		instance;
1314 	int		count = 0;
1315 	int		param = 0;
1316 	int		status;
1317 	int		rval;
1318 
1319 	/* Get instance for serial */
1320 	instance = ddi_get_instance(dip);
1321 
1322 	rw_enter(&oplmsu_uinst->lock, RW_WRITER);
1323 	mutex_enter(&oplmsu_uinst->u_lock);
1324 
1325 	/* Get current number of paths */
1326 	oplmsu_uinst->path_num = oplmsu_get_pathnum();
1327 
1328 	rval = FAILURE;
1329 
1330 	/* Check specified upath_t */
1331 	upath = oplmsu_uinst->first_upath;
1332 	while (upath) {
1333 		if (instance == upath->path_no) {
1334 			/* Save status of specified path */
1335 			status = upath->status;
1336 			rval = SUCCESS;
1337 		}
1338 		upath = upath->u_next;
1339 		count += 1;
1340 	}
1341 	mutex_exit(&oplmsu_uinst->u_lock);
1342 	rw_exit(&oplmsu_uinst->lock);
1343 
1344 	if (rval == FAILURE) {
1345 		if (count <= 1) {
1346 			cmn_err(CE_WARN, "oplmsu: detach(dr): "
1347 			    "Instance %d is last path", instance);
1348 		} else {
1349 			cmn_err(CE_WARN, "oplmsu: detach(dr): "
1350 			    "Instance %d doesn't find", instance);
1351 		}
1352 		return (EINVAL);
1353 	}
1354 
1355 	/* Check status of specified path */
1356 	if ((status == MSU_PSTAT_ACTIVE) || (status == MSU_PSTAT_STANDBY)) {
1357 		/* Stop to use the path */
1358 		rval = oplmsu_config_stop(instance);
1359 		if (rval != 0) {
1360 			cmn_err(CE_WARN, "oplmsu: detach(dr): "
1361 			    "Failed to stop the path. errno = %d", rval);
1362 			return (rval);
1363 		}
1364 	}
1365 
1366 	/* Prepare to unlink the path */
1367 	rval = oplmsu_config_disc(instance);
1368 	if (rval != 0) {
1369 		cmn_err(CE_WARN, "oplmsu: detach(dr): "
1370 		    "Failed to disconnect the path. errno = %d", rval);
1371 		return (rval);
1372 	}
1373 
1374 	rw_enter(&oplmsu_uinst->lock, RW_READER);
1375 	mutex_enter(&oplmsu_uinst->l_lock);
1376 	lpath = oplmsu_uinst->first_lpath;
1377 	while (lpath) {
1378 		if (lpath->path_no == instance) { /* Get link ID */
1379 			break;
1380 		}
1381 		lpath = lpath->l_next;
1382 	}
1383 	mutex_exit(&oplmsu_uinst->l_lock);
1384 	rw_exit(&oplmsu_uinst->lock);
1385 
1386 	if (lpath == NULL) {
1387 		cmn_err(CE_WARN, "oplmsu: detach(dr): Can not find link ID");
1388 		return (EINVAL);
1389 	}
1390 
1391 	/* Open oplmsu */
1392 	rval = oplmsu_open_msu(oplmsu_uinst->msu_dip, &msu_li, &msu_lh);
1393 	if (rval != 0) {
1394 		cmn_err(CE_WARN, "oplmsu: detach(dr): "
1395 		    "msu open failed. errno = %d", rval);
1396 		return (rval);
1397 	}
1398 
1399 	/* Issue ioctl(I_PUNLINK) */
1400 	rval = ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)lpath->link_id, FKIOCTL,
1401 	    kcred, &param);
1402 	if (rval != 0) {
1403 		cmn_err(CE_WARN, "oplmsu: detach(dr): "
1404 		    "ioctl(I_PUNLINK) failed. errno = %d", rval);
1405 		(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
1406 		ldi_ident_release(msu_li);
1407 		return (rval);
1408 	}
1409 
1410 	/* Close oplmsu(meta node) */
1411 	(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
1412 	ldi_ident_release(msu_li);
1413 
1414 	len = sizeof (struct msu_path) + sizeof (struct msu_dev);
1415 	mpath = (struct msu_path *)kmem_zalloc((size_t)len, KM_SLEEP);
1416 	mpath->num = 1;
1417 	mdev = (struct msu_dev *)(mpath + 1);
1418 	mdev->dip = dip;
1419 
1420 	/* Delete the path */
1421 	rval = oplmsu_config_del(mpath);
1422 	if (rval != 0) {
1423 		cmn_err(CE_WARN, "oplmsu: detach(dr): "
1424 		    "Failed to delete the path. errno = %d", rval);
1425 	}
1426 
1427 	kmem_free(mpath, (size_t)len);
1428 	return (rval);
1429 }
1430 
1431 /*
1432  * The ebus and the serial device path under a given CMU_CH chip
1433  * is expected to be always at the same address. So, it is safe
1434  * to hard-code the pathnames as below.
1435  */
1436 #define	EBUS_PATH		"ebus@1"
1437 #define	SERIAL_PATH		"serial@14,400000"
1438 #define	EBUS_SERIAL_PATH	("/" EBUS_PATH "/" SERIAL_PATH)
1439 
1440 /*
1441  * Given the CMU_CH dip, find the serial device dip.
1442  */
1443 dev_info_t *
1444 oplmsu_find_ser_dip(dev_info_t *cmuch_dip)
1445 {
1446 	int		circ1, circ2;
1447 	dev_info_t	*ebus_dip;
1448 	dev_info_t	*ser_dip = NULL;
1449 
1450 	ndi_devi_enter(cmuch_dip, &circ1);
1451 	ebus_dip = ndi_devi_findchild(cmuch_dip, EBUS_PATH);
1452 
1453 	DBG_PRINT((CE_NOTE, "oplmsu: find-serial-dip: "
1454 	    "ebus_dip = %p", ebus_dip));
1455 
1456 	if (ebus_dip != NULL) {
1457 		ndi_devi_enter(ebus_dip, &circ2);
1458 		ser_dip = ndi_devi_findchild(ebus_dip, SERIAL_PATH);
1459 
1460 		DBG_PRINT((CE_NOTE, "oplmsu: find-serial-dip: "
1461 		    "ser_dip = %p", ser_dip));
1462 		ndi_devi_exit(ebus_dip, circ2);
1463 	}
1464 	ndi_devi_exit(cmuch_dip, circ1);
1465 	return (ser_dip);
1466 }
1467 
1468 /*
1469  * Find all console related serial devices.
1470  */
1471 int
1472 oplmsu_find_serial(ser_devl_t **ser_dl)
1473 {
1474 	dev_info_t	*root_dip;
1475 	dev_info_t	*cmuch_dip;
1476 	dev_info_t	*dip;
1477 	ser_devl_t	*wrk_ser_dl;
1478 	int		circ;
1479 	int		count = 0;
1480 	char		pathname[MSU_PATHNAME_SIZE];
1481 	dev_t		devt;
1482 	char		*namep;
1483 
1484 	root_dip = ddi_root_node();
1485 	ndi_devi_enter(root_dip, &circ);
1486 	cmuch_dip = ddi_get_child(root_dip);
1487 
1488 	while (cmuch_dip != NULL) {
1489 		namep = ddi_binding_name(cmuch_dip);	/* Get binding name */
1490 		if (namep == NULL) {
1491 			cmuch_dip = ddi_get_next_sibling(cmuch_dip);
1492 			continue;
1493 		}
1494 
1495 		DBG_PRINT((CE_NOTE, "oplmsu: find-serial: name => %s", namep));
1496 
1497 		if ((strcmp(namep, MSU_CMUCH_FF) != 0) &&
1498 		    (strcmp(namep, MSU_CMUCH_DC) != 0)) {
1499 #ifdef DEBUG
1500 			if (strcmp(namep, MSU_CMUCH_DBG) != 0) {
1501 				cmuch_dip = ddi_get_next_sibling(cmuch_dip);
1502 				continue;
1503 			}
1504 #else
1505 			cmuch_dip = ddi_get_next_sibling(cmuch_dip);
1506 			continue;
1507 #endif
1508 		}
1509 
1510 		/*
1511 		 * Online the cmuch_dip so that its in the right state
1512 		 * to get the complete path, that is both name and address.
1513 		 */
1514 		(void) ndi_devi_online(cmuch_dip, 0);
1515 		(void) ddi_pathname(cmuch_dip, pathname);
1516 		DBG_PRINT((CE_NOTE,
1517 		    "oplmsu: find-serial: cmu-ch path => %s", pathname));
1518 		(void) strcat(pathname, EBUS_SERIAL_PATH);
1519 
1520 		/*
1521 		 * Call ddi_pathname_to_dev_t to forceload and attach
1522 		 * the required drivers.
1523 		 */
1524 		devt = ddi_pathname_to_dev_t(pathname);
1525 		DBG_PRINT((CE_NOTE, "oplmsu: find-serial: serial device "
1526 		    "dev_t = %lx", devt));
1527 		if ((devt != NODEV) &&
1528 		    ((dip = oplmsu_find_ser_dip(cmuch_dip)) != NULL)) {
1529 			wrk_ser_dl = (ser_devl_t *)
1530 			    kmem_zalloc(sizeof (ser_devl_t), KM_SLEEP);
1531 			wrk_ser_dl->dip = dip;
1532 			count += 1;
1533 
1534 			if (*ser_dl != NULL) {
1535 				wrk_ser_dl->next = *ser_dl;
1536 			}
1537 			*ser_dl = wrk_ser_dl;
1538 		}
1539 		cmuch_dip = ddi_get_next_sibling(cmuch_dip);
1540 	}
1541 	ndi_devi_exit(root_dip, circ);
1542 	return (count);
1543 }
1544 
1545 /* Configure STREAM */
1546 void
1547 oplmsu_conf_stream(uinst_t *msu_uinst)
1548 {
1549 	ldi_ident_t	msu_li = NULL;
1550 	ldi_handle_t	msu_lh = NULL;
1551 	struct msu_path	*mpath;
1552 	struct msu_dev	*mdev;
1553 	ser_devl_t	*ser_dl = NULL, *next_ser_dl;
1554 	int		*plink_id;
1555 	int		size;
1556 	int		i;
1557 	int		param;
1558 	int		connected = 0;
1559 	int		devcnt = 0;
1560 	int		rval;
1561 
1562 	DBG_PRINT((CE_NOTE,
1563 	    "oplmsu: conf-stream: stream configuration start!"));
1564 
1565 	/* Find serial devices */
1566 	devcnt = oplmsu_find_serial(&ser_dl);
1567 	if ((devcnt == 0) || (ser_dl == NULL)) {
1568 		cmn_err(CE_WARN, "oplmsu: conf-stream: "
1569 		    "Discovered serial device = %d", devcnt);
1570 		return;
1571 	}
1572 
1573 	/* Open oplmsu */
1574 	rval = oplmsu_open_msu(msu_uinst->msu_dip, &msu_li, &msu_lh);
1575 	if (rval != 0) {
1576 		cmn_err(CE_WARN, "oplmsu: conf-stream: "
1577 		    "msu open failed. errno = %d", rval);
1578 		return;
1579 	}
1580 
1581 	size = (sizeof (struct msu_path) + (sizeof (struct msu_dev) * devcnt));
1582 	mpath = (struct msu_path *)kmem_zalloc((size_t)size, KM_SLEEP);
1583 	plink_id = (int *)kmem_zalloc((sizeof (int) * devcnt), KM_SLEEP);
1584 
1585 	mdev = (struct msu_dev *)(mpath + 1);
1586 	for (i = 0; i < devcnt; i++) {
1587 		/* Connect two streams */
1588 		rval = oplmsu_plink_serial(ser_dl->dip, msu_lh, &plink_id[i]);
1589 		if (rval != 0) {
1590 			cmn_err(CE_WARN, "oplmsu: conf-stream: "
1591 			    "i_plink failed. errno = %d", rval);
1592 			next_ser_dl = ser_dl->next;
1593 			kmem_free(ser_dl, sizeof (ser_devl_t));
1594 			ser_dl = next_ser_dl;
1595 			continue;
1596 		}
1597 
1598 		rval = oplmsu_set_lpathnum(plink_id[i],
1599 		    ddi_get_instance(ser_dl->dip));
1600 		if (rval != 0) {
1601 			cmn_err(CE_WARN, "oplmsu: conf-stream: "
1602 			    "Link id %d is not found", plink_id[i]);
1603 			/* Issue ioctl(I_PUNLINK) */
1604 			(void) ldi_ioctl(msu_lh, I_PUNLINK,
1605 			    (intptr_t)plink_id[i], FKIOCTL, kcred, &param);
1606 			next_ser_dl = ser_dl->next;
1607 			kmem_free(ser_dl, sizeof (ser_devl_t));
1608 			ser_dl = next_ser_dl;
1609 			continue;
1610 		}
1611 
1612 		mdev->dip = ser_dl->dip;
1613 		next_ser_dl = ser_dl->next;
1614 		kmem_free(ser_dl, sizeof (ser_devl_t));
1615 		ser_dl = next_ser_dl;
1616 
1617 		mdev++;
1618 		connected++;
1619 	}
1620 
1621 	if (connected == 0) {
1622 		cmn_err(CE_WARN, "oplmsu: conf-stream: "
1623 		    "Connected paths = %d", connected);
1624 		(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
1625 		ldi_ident_release(msu_li);
1626 		kmem_free(plink_id, (sizeof (int) * devcnt));
1627 		kmem_free(mpath, size);
1628 		return;
1629 	}
1630 
1631 	/* Setup all structure */
1632 	mpath->num = connected;
1633 	rval = oplmsu_config_new(mpath);
1634 	if (rval != 0) {
1635 		cmn_err(CE_WARN, "oplmsu: conf-stream: "
1636 		    "Failed to create all paths. errno = %d", rval);
1637 		oplmsu_unlinks(msu_lh, plink_id, devcnt);
1638 		(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
1639 		ldi_ident_release(msu_li);
1640 		kmem_free(plink_id, (sizeof (int) * devcnt));
1641 		kmem_free(mpath, size);
1642 		return;
1643 	}
1644 
1645 	/* Start to use all paths */
1646 	rval = oplmsu_config_start(MSU_PATH_ALL);
1647 	if (rval != 0) {
1648 		cmn_err(CE_WARN, "oplmsu: conf-stream: "
1649 		    "Failed to start all paths. errno = %d", rval);
1650 
1651 		/* Delete the path */
1652 		rval = oplmsu_config_del(mpath);
1653 		if (rval == 0) {
1654 			oplmsu_unlinks(msu_lh, plink_id, devcnt);
1655 		}
1656 	}
1657 
1658 	(void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
1659 	ldi_ident_release(msu_li);
1660 	kmem_free(plink_id, (sizeof (int) * devcnt));
1661 	kmem_free(mpath, size);
1662 
1663 	DBG_PRINT((CE_NOTE, "oplmsu: conf-stream: stream configuration end!"));
1664 }
1665 
1666 void
1667 oplmsu_unlinks(ldi_handle_t msu_lh, int *plink_id, int devcnt)
1668 {
1669 	int	i;
1670 	int	param = 0;
1671 
1672 	for (i = 0; i < devcnt; i++) {
1673 		if (plink_id[i] == 0) {
1674 			continue;
1675 		}
1676 
1677 		/* Issue ioctl(I_PUNLINK) */
1678 		(void) ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)plink_id[i],
1679 		    FKIOCTL, kcred, &param);
1680 	}
1681 }
1682 
1683 void
1684 oplmsu_setup(uinst_t *msu_uinst)
1685 {
1686 
1687 	DBG_PRINT((CE_NOTE, "oplmsu: setup: Background thread start!"));
1688 
1689 	mutex_enter(&oplmsu_bthrd_excl);
1690 	if (oplmsu_conf_st == MSU_CONFIGURING) {
1691 		mutex_exit(&oplmsu_bthrd_excl);
1692 		oplmsu_conf_stream(msu_uinst);	/* Configure stream */
1693 		mutex_enter(&oplmsu_bthrd_excl);
1694 		oplmsu_conf_st = MSU_CONFIGURED;
1695 		cv_broadcast(&oplmsu_conf_cv);	/* Wake up from cv_wait_sig() */
1696 	}
1697 
1698 	if (oplmsu_bthrd_id != NULL) {
1699 		oplmsu_bthrd_id = NULL;
1700 	}
1701 	mutex_exit(&oplmsu_bthrd_excl);
1702 
1703 	DBG_PRINT((CE_NOTE, "oplmsu: setup: Background thread end!"));
1704 
1705 	thread_exit();
1706 }
1707 
1708 int
1709 oplmsu_create_upath(dev_info_t *dip)
1710 {
1711 	upath_t		*upath;
1712 	lpath_t		*lpath;
1713 	dev_info_t	*cmuch_dip;
1714 	int		instance;
1715 	int		lsb;
1716 
1717 	cmuch_dip = ddi_get_parent(ddi_get_parent(dip));
1718 	lsb = ddi_prop_get_int(DDI_DEV_T_ANY, cmuch_dip, 0, MSU_BOARD_PROP,
1719 	    FAILURE);
1720 	if (lsb == FAILURE) {
1721 		return (lsb);
1722 	}
1723 
1724 	instance = ddi_get_instance(dip);
1725 
1726 	mutex_enter(&oplmsu_uinst->l_lock);
1727 	lpath = oplmsu_uinst->first_lpath;
1728 	while (lpath) {
1729 		if (lpath->path_no == instance) {
1730 			break;
1731 		}
1732 		lpath = lpath->l_next;
1733 	}
1734 
1735 	if (lpath == NULL) {
1736 		mutex_exit(&oplmsu_uinst->l_lock);
1737 		return (ENODEV);
1738 	}
1739 
1740 	upath = (upath_t *)kmem_zalloc(sizeof (upath_t), KM_SLEEP);
1741 
1742 	/*
1743 	 * Initialize members of upath_t
1744 	 */
1745 
1746 	upath->path_no = instance;
1747 	upath->lpath = lpath;
1748 	upath->ser_devcb.dip = dip;
1749 	upath->ser_devcb.lsb = lsb;
1750 	oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STOP, MSU_PSTAT_EMPTY,
1751 	    MSU_STOP);
1752 
1753 	lpath->src_upath = NULL;
1754 	lpath->status = MSU_EXT_NOTUSED;
1755 	mutex_exit(&oplmsu_uinst->l_lock);
1756 
1757 	oplmsu_link_upath(upath);
1758 	return (SUCCESS);
1759 }
1760 
1761 /* Setup new upper instance structure */
1762 int
1763 oplmsu_config_new(struct msu_path *mpath)
1764 {
1765 	struct msu_dev	*mdev;
1766 	int		i;
1767 	int		rval = SUCCESS;
1768 
1769 	DBG_PRINT((CE_NOTE, "oplmsu: conf-new: config_new() called"));
1770 	ASSERT(mpath);
1771 
1772 	if (mpath->num == 0) {
1773 		cmn_err(CE_WARN, "oplmsu: conf-new: "
1774 		    "Number of paths = %d", mpath->num);
1775 		return (EINVAL);
1776 	}
1777 
1778 	rw_enter(&oplmsu_uinst->lock, RW_WRITER);
1779 
1780 	mutex_enter(&oplmsu_uinst->l_lock);
1781 	rval = oplmsu_check_lpath_usable();
1782 	mutex_exit(&oplmsu_uinst->l_lock);
1783 
1784 	if (rval == BUSY) { /* Check whether Lower path is usable */
1785 		rw_exit(&oplmsu_uinst->lock);
1786 		cmn_err(CE_WARN, "oplmsu: conf-new: "
1787 		    "Other processing is using this device");
1788 		return (EBUSY);
1789 	}
1790 
1791 	/*
1792 	 * Because the OPLMSU instance already exists when the upper path
1793 	 * table exists, the configure_new processing cannot be done.
1794 	 */
1795 
1796 	mutex_enter(&oplmsu_uinst->u_lock);
1797 
1798 	if ((oplmsu_uinst->first_upath != NULL) ||
1799 	    (oplmsu_uinst->last_upath != NULL)) {
1800 		mutex_exit(&oplmsu_uinst->u_lock);
1801 		rw_exit(&oplmsu_uinst->lock);
1802 		cmn_err(CE_WARN, "oplmsu: conf-new: upath_t already exist");
1803 		return (EINVAL);
1804 	}
1805 
1806 	/*
1807 	 * Because the config_new processing has already been done
1808 	 * if oplmsu_uinst->path_num isn't -1, this processing cannot be
1809 	 * continued.
1810 	 */
1811 
1812 	if (oplmsu_uinst->path_num != UNDEFINED) {
1813 		mutex_exit(&oplmsu_uinst->u_lock);
1814 		rw_exit(&oplmsu_uinst->lock);
1815 		cmn_err(CE_WARN, "oplmsu: conf-new: "
1816 		    "conf-new processing has already been completed");
1817 		return (EINVAL);
1818 	}
1819 
1820 	/*
1821 	 * Only the number of specified paths makes the upper path
1822 	 * information tables.
1823 	 */
1824 
1825 	mdev = (struct msu_dev *)(mpath + 1);
1826 	for (i = 0; i < mpath->num; i++) {
1827 		/*
1828 		 * Associate upper path information table with lower path
1829 		 * information table.
1830 		 *
1831 		 * If the upper path information table and the lower path
1832 		 * information table cannot be associated, the link list of
1833 		 * the upper path information table is released.
1834 		 */
1835 		rval = oplmsu_create_upath(mdev->dip);
1836 		if (rval != SUCCESS) {
1837 			oplmsu_delete_upath_info();
1838 			mutex_exit(&oplmsu_uinst->u_lock);
1839 			rw_exit(&oplmsu_uinst->lock);
1840 			cmn_err(CE_WARN, "oplmsu: conf-new: "
1841 			    "Failed to create upath %d", rval);
1842 			return (rval);
1843 		}
1844 
1845 		mdev++;
1846 	}
1847 
1848 	/*
1849 	 * Setup members of uinst_t
1850 	 */
1851 
1852 	oplmsu_uinst->inst_status = oplmsu_get_inst_status();
1853 	oplmsu_uinst->path_num = mpath->num;
1854 	oplmsu_uinst->lower_queue = NULL;
1855 	mutex_exit(&oplmsu_uinst->u_lock);
1856 	rw_exit(&oplmsu_uinst->lock);
1857 	return (SUCCESS);
1858 }
1859 
1860 /* Add path information */
1861 int
1862 oplmsu_config_add(dev_info_t *dip)
1863 {
1864 	upath_t	*upath;
1865 	int	instance;
1866 	int	rval = SUCCESS;
1867 
1868 	DBG_PRINT((CE_NOTE, "oplmsu: conf-add: config_add() called"));
1869 	ASSERT(dip);
1870 
1871 	instance = ddi_get_instance(dip);
1872 	rw_enter(&oplmsu_uinst->lock, RW_WRITER);
1873 
1874 	if (oplmsu_uinst->path_num == UNDEFINED) {
1875 		rw_exit(&oplmsu_uinst->lock);
1876 		cmn_err(CE_WARN, "oplmsu: conf-add: "
1877 		    "conf-new processing has not been completed yet");
1878 		return (EINVAL);
1879 	}
1880 
1881 	mutex_enter(&oplmsu_uinst->u_lock);
1882 	upath = oplmsu_search_upath_info(instance);
1883 	if (upath != NULL) {
1884 		mutex_exit(&oplmsu_uinst->u_lock);
1885 		rw_exit(&oplmsu_uinst->lock);
1886 		cmn_err(CE_WARN, "oplmsu: conf-add: "
1887 		    "Proper upath_t doesn't find");
1888 		return (EINVAL);
1889 	}
1890 
1891 	rval = oplmsu_create_upath(dip);
1892 	if (rval != SUCCESS) {
1893 		mutex_exit(&oplmsu_uinst->u_lock);
1894 		rw_exit(&oplmsu_uinst->lock);
1895 		cmn_err(CE_WARN, "oplmsu: conf-add: "
1896 		    "Failed to create upath %d", rval);
1897 		return (rval);
1898 	}
1899 
1900 	oplmsu_uinst->inst_status = oplmsu_get_inst_status();
1901 	oplmsu_uinst->path_num = oplmsu_get_pathnum();
1902 	mutex_exit(&oplmsu_uinst->u_lock);
1903 	rw_exit(&oplmsu_uinst->lock);
1904 	return (SUCCESS);
1905 }
1906 
1907 /* Delete each path information */
1908 int
1909 oplmsu_config_del(struct msu_path *mpath)
1910 {
1911 	struct msu_dev	*mdev;
1912 	upath_t		*upath;
1913 	lpath_t		*lpath;
1914 	int		rval = SUCCESS;
1915 	int		use_flag;
1916 	int		i;
1917 
1918 	DBG_PRINT((CE_NOTE, "oplmsu: conf-del: config_del() called"));
1919 	ASSERT(mpath);
1920 
1921 	mdev = (struct msu_dev *)(mpath + 1);
1922 
1923 	rw_enter(&oplmsu_uinst->lock, RW_WRITER);
1924 	mutex_enter(&oplmsu_uinst->u_lock);
1925 	for (i = 0; i < mpath->num; i++) {
1926 		upath = oplmsu_search_upath_info(ddi_get_instance(mdev->dip));
1927 		if (upath == NULL) {
1928 			cmn_err(CE_WARN, "oplmsu: conf-del: "
1929 			    "Proper upath_t doesn't find");
1930 			rval = ENODEV;
1931 			mdev++;
1932 			continue;
1933 		}
1934 
1935 		lpath = upath->lpath;
1936 		if (lpath == NULL) {
1937 			if ((upath->traditional_status == MSU_WSTP_ACK) ||
1938 			    (upath->traditional_status == MSU_WSTR_ACK) ||
1939 			    (upath->traditional_status == MSU_WPTH_CHG) ||
1940 			    (upath->traditional_status == MSU_WTCS_ACK) ||
1941 			    (upath->traditional_status == MSU_WTMS_ACK) ||
1942 			    (upath->traditional_status == MSU_WPPS_ACK) ||
1943 			    (upath->traditional_status == MSU_WWSZ_ACK) ||
1944 			    (upath->traditional_status == MSU_WCAR_ACK)) {
1945 				cmn_err(CE_WARN, "oplmsu: conf-del: "
1946 				    "Other processing is using this device");
1947 				rval = EBUSY;
1948 				mdev++;
1949 				continue;
1950 			}
1951 
1952 			if ((upath->status != MSU_PSTAT_DISCON) ||
1953 			    (upath->traditional_status != MSU_DISCON)) {
1954 				cmn_err(CE_WARN, "oplmsu: conf-del: "
1955 				    "Status of path is improper");
1956 				rval = EINVAL;
1957 				mdev++;
1958 				continue;
1959 			}
1960 		} else {
1961 			mutex_enter(&oplmsu_uinst->l_lock);
1962 			use_flag = oplmsu_set_ioctl_path(lpath, NULL, NULL);
1963 			if (use_flag == BUSY) {
1964 				mutex_exit(&oplmsu_uinst->l_lock);
1965 				cmn_err(CE_WARN, "oplmsu: conf-del: "
1966 				    "Other processing is using lower path");
1967 				rval = EBUSY;
1968 				mdev++;
1969 				continue;
1970 			}
1971 
1972 			if (((upath->status != MSU_PSTAT_STOP) ||
1973 			    (upath->traditional_status != MSU_STOP)) &&
1974 			    ((upath->status != MSU_PSTAT_FAIL) ||
1975 			    (upath->traditional_status != MSU_FAIL))) {
1976 				oplmsu_clear_ioctl_path(lpath);
1977 				mutex_exit(&oplmsu_uinst->l_lock);
1978 				cmn_err(CE_WARN, "oplmsu: conf-del: "
1979 				    "Status of path isn't 'Offline:stop/fail'");
1980 				rval = EINVAL;
1981 				mdev++;
1982 				continue;
1983 			}
1984 			lpath->src_upath = NULL;
1985 			lpath->status = MSU_SETID_NU;
1986 			oplmsu_clear_ioctl_path(lpath);
1987 			mutex_exit(&oplmsu_uinst->l_lock);
1988 		}
1989 		oplmsu_unlink_upath(upath);	/* Unlink upath_t */
1990 		kmem_free(upath, sizeof (upath_t));
1991 		mdev++;
1992 	}
1993 
1994 	oplmsu_uinst->inst_status = oplmsu_get_inst_status();
1995 	oplmsu_uinst->path_num = oplmsu_get_pathnum();
1996 	mutex_exit(&oplmsu_uinst->u_lock);
1997 	rw_exit(&oplmsu_uinst->lock);
1998 	return (rval);
1999 }
2000 
2001 /* Stop to use the path */
2002 int
2003 oplmsu_config_stop(int pathnum)
2004 {
2005 	upath_t	*upath, *altn_upath;
2006 	lpath_t	*lpath, *altn_lpath;
2007 	queue_t	*stp_queue = NULL;
2008 	queue_t	*dst_queue = NULL;
2009 	mblk_t	*nmp = NULL, *fmp = NULL;
2010 	ctrl_t	*ctrl;
2011 	int	term_ioctl, term_stat;
2012 	int	use_flag;
2013 
2014 	DBG_PRINT((CE_NOTE,
2015 	    "oplmsu: conf-stop: config_stop(%d) called", pathnum));
2016 
2017 	if (pathnum == MSU_PATH_ALL) {
2018 		cmn_err(CE_WARN, "oplmsu: conf-stop: "
2019 		    "All path can't be transferred to the status of "
2020 		    "'Offline:stop'");
2021 		return (EINVAL);
2022 	}
2023 
2024 	rw_enter(&oplmsu_uinst->lock, RW_WRITER);
2025 	mutex_enter(&oplmsu_uinst->u_lock);
2026 
2027 	upath = oplmsu_search_upath_info(pathnum);	/* Search upath_t */
2028 	if (upath == NULL) {
2029 		mutex_exit(&oplmsu_uinst->u_lock);
2030 		rw_exit(&oplmsu_uinst->lock);
2031 		cmn_err(CE_WARN, "oplmsu: conf-stop: "
2032 		    "Proper upath_t doesn't find");
2033 		return (ENODEV);
2034 	}
2035 
2036 	lpath = upath->lpath;
2037 	if (lpath == NULL) {
2038 		mutex_exit(&oplmsu_uinst->u_lock);
2039 		rw_exit(&oplmsu_uinst->lock);
2040 		cmn_err(CE_WARN, "oplmsu: conf-stop: "
2041 		    "Proper lpath_t doesn't exist");
2042 		return (ENODEV);
2043 	}
2044 
2045 	mutex_enter(&oplmsu_uinst->l_lock);
2046 
2047 	/* Check status of lpath_t */
2048 	use_flag = oplmsu_set_ioctl_path(lpath, NULL, NULL);
2049 	if (use_flag == BUSY) {
2050 		mutex_exit(&oplmsu_uinst->l_lock);
2051 		mutex_exit(&oplmsu_uinst->u_lock);
2052 		rw_exit(&oplmsu_uinst->lock);
2053 		cmn_err(CE_WARN, "oplmsu: conf-stop: "
2054 		    "Other processing is using lower path");
2055 		return (EBUSY);
2056 	}
2057 
2058 	if (upath->status == MSU_PSTAT_FAIL) {
2059 		oplmsu_clear_ioctl_path(lpath);
2060 		mutex_exit(&oplmsu_uinst->l_lock);
2061 		mutex_exit(&oplmsu_uinst->u_lock);
2062 		rw_exit(&oplmsu_uinst->lock);
2063 		return (EIO);
2064 	} else if ((upath->status == MSU_PSTAT_STOP) &&
2065 	    (upath->traditional_status == MSU_STOP)) {
2066 		oplmsu_clear_ioctl_path(lpath);
2067 		mutex_exit(&oplmsu_uinst->l_lock);
2068 		mutex_exit(&oplmsu_uinst->u_lock);
2069 		rw_exit(&oplmsu_uinst->lock);
2070 		return (SUCCESS);
2071 	} else if ((upath->status == MSU_PSTAT_STANDBY) &&
2072 	    (upath->traditional_status == MSU_STANDBY)) {
2073 		oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STOP,
2074 		    upath->status, MSU_STOP);
2075 		oplmsu_clear_ioctl_path(lpath);
2076 		lpath->src_upath = NULL;
2077 		lpath->status = MSU_EXT_NOTUSED;
2078 
2079 		oplmsu_uinst->inst_status = oplmsu_get_inst_status();
2080 		mutex_exit(&oplmsu_uinst->l_lock);
2081 		mutex_exit(&oplmsu_uinst->u_lock);
2082 		rw_exit(&oplmsu_uinst->lock);
2083 		return (SUCCESS);
2084 	} else if ((upath->status == MSU_PSTAT_ACTIVE) &&
2085 	    (upath->traditional_status == MSU_ACTIVE)) {
2086 		altn_upath = oplmsu_search_standby();
2087 		if (altn_upath == NULL) { /* Alternate path doesn't exist */
2088 			DBG_PRINT((CE_NOTE, "oplmsu: conf-stop: "
2089 			    "Alternate upper path doesn't find"));
2090 			oplmsu_clear_ioctl_path(lpath);
2091 			mutex_exit(&oplmsu_uinst->l_lock);
2092 			mutex_exit(&oplmsu_uinst->u_lock);
2093 			rw_exit(&oplmsu_uinst->lock);
2094 			return (EINVAL);
2095 		}
2096 
2097 		if ((fmp = allocb(sizeof (char), BPRI_LO)) == NULL) {
2098 			oplmsu_clear_ioctl_path(lpath);
2099 			mutex_exit(&oplmsu_uinst->l_lock);
2100 			mutex_exit(&oplmsu_uinst->u_lock);
2101 			rw_exit(&oplmsu_uinst->lock);
2102 			return (ENOSR);
2103 		}
2104 
2105 		if (oplmsu_stop_prechg(&nmp, &term_ioctl, &term_stat) !=
2106 		    SUCCESS) {
2107 			oplmsu_clear_ioctl_path(lpath);
2108 			mutex_exit(&oplmsu_uinst->l_lock);
2109 			mutex_exit(&oplmsu_uinst->u_lock);
2110 			rw_exit(&oplmsu_uinst->lock);
2111 			freeb(fmp);
2112 			return (ENOSR);
2113 		}
2114 
2115 		altn_lpath = altn_upath->lpath;
2116 		use_flag = oplmsu_set_ioctl_path(altn_lpath, NULL, NULL);
2117 		if (use_flag == BUSY) {
2118 			oplmsu_clear_ioctl_path(lpath);
2119 			mutex_exit(&oplmsu_uinst->l_lock);
2120 			mutex_exit(&oplmsu_uinst->u_lock);
2121 			rw_exit(&oplmsu_uinst->lock);
2122 
2123 			cmn_err(CE_WARN, "oplmsu: conf-stop: "
2124 			    "Other processing is using alternate lower path");
2125 			freeb(fmp);
2126 			freemsg(nmp);
2127 			return (EBUSY);
2128 		}
2129 
2130 		dst_queue = WR(altn_lpath->lower_queue);
2131 
2132 		/* termios is not held. Change alternate path to MSU_ACTIVE */
2133 		if (nmp == NULL) {
2134 			altn_upath->traditional_status = term_stat;
2135 			altn_lpath->src_upath = upath;
2136 			altn_lpath->status = MSU_EXT_VOID;
2137 
2138 			oplmsu_uinst->lower_queue = NULL;
2139 
2140 			ctrl = oplmsu_uinst->user_ctrl;
2141 			if (ctrl != NULL) {
2142 				mutex_enter(&oplmsu_uinst->c_lock);
2143 				stp_queue = WR(ctrl->queue);
2144 				mutex_exit(&oplmsu_uinst->c_lock);
2145 				noenable(stp_queue);
2146 				oplmsu_queue_flag = 1;
2147 			}
2148 
2149 			/* Make M_FLUSH and send to alternate path */
2150 			oplmsu_cmn_set_mflush(fmp);
2151 			putq(dst_queue, fmp);
2152 
2153 			/* Change status of alternate path */
2154 			oplmsu_cmn_set_upath_sts(altn_upath, MSU_PSTAT_ACTIVE,
2155 			    altn_upath->status, MSU_ACTIVE);
2156 
2157 			oplmsu_clear_ioctl_path(altn_lpath);
2158 			altn_lpath->uinst = oplmsu_uinst;
2159 			altn_lpath->src_upath = NULL;
2160 			altn_lpath->status = MSU_EXT_NOTUSED;
2161 
2162 			/* Notify of the active path changing */
2163 			prom_opl_switch_console(altn_upath->ser_devcb.lsb);
2164 
2165 			/* Send XON to notify active path */
2166 			(void) oplmsu_cmn_put_xoffxon(dst_queue, MSU_XON_4);
2167 
2168 			/* Send XOFF to notify all standby paths */
2169 			oplmsu_cmn_putxoff_standby();
2170 
2171 			oplmsu_uinst->lower_queue = RD(dst_queue);
2172 			ctrl = oplmsu_uinst->user_ctrl;
2173 
2174 			/* Switch active path of oplmsu */
2175 			if (ctrl != NULL) {
2176 				queue_t	*altn_queue;
2177 
2178 				mutex_enter(&oplmsu_uinst->c_lock);
2179 				altn_queue = WR(ctrl->queue);
2180 				mutex_exit(&oplmsu_uinst->c_lock);
2181 
2182 				/* Restart queuing of user access node */
2183 				enableok(altn_queue);
2184 
2185 				oplmsu_queue_flag = 0;
2186 				mutex_exit(&oplmsu_uinst->l_lock);
2187 				mutex_exit(&oplmsu_uinst->u_lock);
2188 				oplmsu_wcmn_high_qenable(altn_queue, RW_WRITER);
2189 				mutex_enter(&oplmsu_uinst->u_lock);
2190 				mutex_enter(&oplmsu_uinst->l_lock);
2191 			}
2192 
2193 			/* Stop previous active path */
2194 			oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STOP,
2195 			    upath->status, MSU_STOP);
2196 
2197 			lpath->uinst = NULL;
2198 			lpath->src_upath = NULL;
2199 			lpath->status = MSU_EXT_NOTUSED;
2200 			oplmsu_clear_ioctl_path(lpath);
2201 
2202 			oplmsu_uinst->inst_status = oplmsu_get_inst_status();
2203 			mutex_exit(&oplmsu_uinst->l_lock);
2204 			mutex_exit(&oplmsu_uinst->u_lock);
2205 			rw_exit(&oplmsu_uinst->lock);
2206 			return (SUCCESS);
2207 		}
2208 
2209 		/* Send termios information to alternate path */
2210 		if (canput(dst_queue)) {
2211 			altn_upath->traditional_status = term_stat;
2212 			altn_lpath->src_upath = upath;
2213 			altn_lpath->status = MSU_EXT_VOID;
2214 
2215 			upath->traditional_status = MSU_WSTP_ACK;
2216 			lpath->uinst = NULL;
2217 
2218 			oplmsu_uinst->lower_queue = NULL;
2219 
2220 			ctrl = oplmsu_uinst->user_ctrl;
2221 			if (ctrl != NULL) {
2222 				mutex_enter(&oplmsu_uinst->c_lock);
2223 				stp_queue = WR(ctrl->queue);
2224 				mutex_exit(&oplmsu_uinst->c_lock);
2225 				noenable(stp_queue);
2226 				oplmsu_queue_flag = 1;
2227 			}
2228 
2229 			mutex_exit(&oplmsu_uinst->l_lock);
2230 			mutex_exit(&oplmsu_uinst->u_lock);
2231 			rw_exit(&oplmsu_uinst->lock);
2232 			oplmsu_cmn_set_mflush(fmp);
2233 			putq(dst_queue, fmp);
2234 			putq(dst_queue, nmp);
2235 
2236 			mutex_enter(&oplmsu_uinst->l_lock);
2237 			lpath->sw_flag = 1;
2238 			while (lpath->sw_flag != 0) {
2239 				/* Wait for the completion of path switching */
2240 				cv_wait(&lpath->sw_cv, &oplmsu_uinst->l_lock);
2241 			}
2242 			mutex_exit(&oplmsu_uinst->l_lock);
2243 			return (SUCCESS);
2244 		} else {
2245 			oplmsu_clear_ioctl_path(altn_lpath);
2246 			oplmsu_clear_ioctl_path(lpath);
2247 			mutex_exit(&oplmsu_uinst->l_lock);
2248 			mutex_exit(&oplmsu_uinst->u_lock);
2249 			rw_exit(&oplmsu_uinst->lock);
2250 			freeb(fmp);
2251 			freemsg(nmp);
2252 			return (FAILURE);
2253 		}
2254 		/* NOTREACHED */
2255 	} else {
2256 		oplmsu_clear_ioctl_path(lpath);
2257 		mutex_exit(&oplmsu_uinst->l_lock);
2258 		mutex_exit(&oplmsu_uinst->u_lock);
2259 		rw_exit(&oplmsu_uinst->lock);
2260 
2261 		cmn_err(CE_WARN, "oplmsu: conf-stop: "
2262 		    "Status of path is improper");
2263 		return (EINVAL);
2264 	}
2265 	/* NOTREACHED */
2266 }
2267 
2268 /* Start to use path */
2269 int
2270 oplmsu_config_start(int pathnum)
2271 {
2272 	upath_t	*upath = NULL;
2273 	lpath_t	*lpath = NULL;
2274 	queue_t	*dst_queue, *main_rq = NULL;
2275 	int	msu_tty_port;
2276 
2277 	DBG_PRINT((CE_NOTE,
2278 	    "oplmsu: conf-start: config_start(%d) called", pathnum));
2279 
2280 	rw_enter(&oplmsu_uinst->lock, RW_WRITER);
2281 	mutex_enter(&oplmsu_uinst->u_lock);
2282 
2283 	if (oplmsu_get_inst_status() == INST_STAT_BUSY) {
2284 		mutex_exit(&oplmsu_uinst->u_lock);
2285 		rw_exit(&oplmsu_uinst->lock);
2286 		return (EBUSY);
2287 	}
2288 
2289 	if (pathnum == MSU_PATH_ALL) {
2290 		(void) oplmsu_search_min_stop_path();
2291 	}
2292 
2293 	for (upath = oplmsu_uinst->first_upath; upath; ) {
2294 		if ((pathnum != MSU_PATH_ALL) && (upath->path_no != pathnum)) {
2295 			upath = upath->u_next;
2296 			continue;
2297 		}
2298 
2299 		if (upath->path_no == pathnum) {
2300 			lpath = upath->lpath;
2301 			if (lpath == NULL) {
2302 				mutex_exit(&oplmsu_uinst->u_lock);
2303 				rw_exit(&oplmsu_uinst->lock);
2304 				cmn_err(CE_WARN, "oplmsu: conf-start: "
2305 				    "Proper lpath_t doesn't exist");
2306 				return (EINVAL);
2307 			}
2308 
2309 			oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STANDBY,
2310 			    upath->status, MSU_STANDBY);
2311 
2312 			mutex_enter(&oplmsu_uinst->l_lock);
2313 			lpath->src_upath = NULL;
2314 			lpath->status = MSU_EXT_NOTUSED;
2315 			mutex_exit(&oplmsu_uinst->l_lock);
2316 			mutex_exit(&oplmsu_uinst->u_lock);
2317 			rw_exit(&oplmsu_uinst->lock);
2318 			return (SUCCESS);
2319 		}
2320 
2321 		/*
2322 		 * with PATH_ALL
2323 		 */
2324 		lpath = upath->lpath;
2325 		if (lpath == NULL) {
2326 			upath = upath->u_next;
2327 
2328 			DBG_PRINT((CE_WARN, "oplmsu: conf-start: "
2329 			    "Proper lpath_t doesn't exist"));
2330 			continue;
2331 		}
2332 
2333 		msu_tty_port = ddi_prop_get_int(DDI_DEV_T_ANY,
2334 		    oplmsu_uinst->msu_dip, 0, MSU_TTY_PORT_PROP, -1);
2335 
2336 		if (upath->ser_devcb.lsb == msu_tty_port) {
2337 			/* Notify of the active path changing */
2338 			prom_opl_switch_console(upath->ser_devcb.lsb);
2339 
2340 			oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_ACTIVE,
2341 			    upath->status, MSU_ACTIVE);
2342 
2343 			mutex_enter(&oplmsu_uinst->l_lock);
2344 			main_rq = RD(lpath->lower_queue);
2345 			dst_queue = WR(lpath->lower_queue);
2346 			lpath->src_upath = NULL;
2347 			lpath->status = MSU_EXT_NOTUSED;
2348 			lpath->uinst = oplmsu_uinst;
2349 			mutex_exit(&oplmsu_uinst->l_lock);
2350 
2351 			/* Send XON to notify active path */
2352 			(void) oplmsu_cmn_put_xoffxon(dst_queue, MSU_XON_4);
2353 		} else {
2354 			oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STANDBY,
2355 			    upath->status, MSU_STANDBY);
2356 
2357 			mutex_enter(&oplmsu_uinst->l_lock);
2358 			lpath->src_upath = NULL;
2359 			lpath->status = MSU_EXT_NOTUSED;
2360 			mutex_exit(&oplmsu_uinst->l_lock);
2361 		}
2362 		upath = upath->u_next;
2363 	}
2364 
2365 	if (main_rq == NULL) {
2366 		upath_t	*altn_upath;
2367 		lpath_t	*altn_lpath;
2368 
2369 		altn_upath = oplmsu_search_standby();
2370 		if (altn_upath) {
2371 			oplmsu_cmn_set_upath_sts(altn_upath, MSU_PSTAT_ACTIVE,
2372 			    altn_upath->status, MSU_ACTIVE);
2373 
2374 			/* Notify of the active path changing */
2375 			prom_opl_switch_console(altn_upath->ser_devcb.lsb);
2376 
2377 			altn_lpath = altn_upath->lpath;
2378 			if (altn_lpath) {
2379 				mutex_enter(&oplmsu_uinst->l_lock);
2380 				main_rq = RD(altn_lpath->lower_queue);
2381 				dst_queue = WR(altn_lpath->lower_queue);
2382 				altn_lpath->src_upath = NULL;
2383 				altn_lpath->status = MSU_EXT_NOTUSED;
2384 				altn_lpath->uinst = oplmsu_uinst;
2385 				mutex_exit(&oplmsu_uinst->l_lock);
2386 
2387 				/* Send XON to notify active path */
2388 				(void) oplmsu_cmn_put_xoffxon(dst_queue,
2389 				    MSU_XON_4);
2390 			} else {
2391 				cmn_err(CE_WARN, "oplmsu: conf-start: "
2392 				    "Proper alternate lpath_t doesn't exist");
2393 			}
2394 		} else {
2395 			cmn_err(CE_WARN, "oplmsu: conf-start: "
2396 			    "Proper alternate upath_t doesn't exist");
2397 		}
2398 	}
2399 
2400 	mutex_enter(&oplmsu_uinst->l_lock);
2401 
2402 	/* Send XOFF to notify all standby paths */
2403 	oplmsu_cmn_putxoff_standby();
2404 
2405 	/* Change active path of oplmsu */
2406 	oplmsu_uinst->lower_queue = main_rq;
2407 	oplmsu_uinst->inst_status = oplmsu_get_inst_status();
2408 	mutex_exit(&oplmsu_uinst->l_lock);
2409 	mutex_exit(&oplmsu_uinst->u_lock);
2410 	rw_exit(&oplmsu_uinst->lock);
2411 	return (SUCCESS);
2412 }
2413 
2414 /* Prepare of unlink path */
2415 int
2416 oplmsu_config_disc(int pathnum)
2417 {
2418 	upath_t	*upath;
2419 	lpath_t	*lpath;
2420 	int	use_flag;
2421 
2422 	DBG_PRINT((CE_NOTE,
2423 	    "oplmsu: conf-disc: config_disc(%d) called", pathnum));
2424 
2425 	rw_enter(&oplmsu_uinst->lock, RW_READER);
2426 	mutex_enter(&oplmsu_uinst->u_lock);
2427 
2428 	upath = oplmsu_search_upath_info(pathnum);
2429 	if (upath == NULL) {
2430 		mutex_exit(&oplmsu_uinst->u_lock);
2431 		rw_exit(&oplmsu_uinst->lock);
2432 		cmn_err(CE_WARN, "oplmsu: conf-disc: "
2433 		    "Proper upath_t doesn't find");
2434 		return (EINVAL);
2435 	}
2436 
2437 	if ((upath->status == MSU_PSTAT_DISCON) ||
2438 	    (upath->traditional_status == MSU_DISCON)) {
2439 		mutex_exit(&oplmsu_uinst->u_lock);
2440 		rw_exit(&oplmsu_uinst->lock);
2441 		return (SUCCESS);
2442 	} else if (((upath->status != MSU_PSTAT_STOP) ||
2443 	    (upath->traditional_status != MSU_STOP)) &&
2444 	    ((upath->status != MSU_PSTAT_FAIL) ||
2445 	    (upath->traditional_status != MSU_FAIL))) {
2446 		mutex_exit(&oplmsu_uinst->u_lock);
2447 		rw_exit(&oplmsu_uinst->lock);
2448 		cmn_err(CE_WARN, "oplmsu: conf-disc: "
2449 		    "Status of path is improper");
2450 		return (EINVAL);
2451 	}
2452 
2453 	lpath = upath->lpath;
2454 	if (lpath == NULL) {
2455 		mutex_exit(&oplmsu_uinst->u_lock);
2456 		rw_exit(&oplmsu_uinst->lock);
2457 		cmn_err(CE_WARN, "oplmsu: conf-disc: "
2458 		    "Proper lpath_t doesn't exist");
2459 		return (ENODEV);
2460 	}
2461 
2462 	mutex_enter(&oplmsu_uinst->l_lock);
2463 
2464 	/* Check lower path status */
2465 	use_flag = oplmsu_set_ioctl_path(lpath, NULL, NULL);
2466 	if (use_flag == BUSY) {
2467 		mutex_exit(&oplmsu_uinst->l_lock);
2468 		mutex_exit(&oplmsu_uinst->u_lock);
2469 		rw_exit(&oplmsu_uinst->lock);
2470 		cmn_err(CE_WARN, "oplmsu: conf-disc: "
2471 		    "Other processing is using lower path");
2472 		return (EBUSY);
2473 	}
2474 
2475 	upath->status = MSU_PSTAT_STOP;
2476 	upath->traditional_status = MSU_SETID;
2477 
2478 	oplmsu_clear_ioctl_path(lpath);
2479 	mutex_exit(&oplmsu_uinst->l_lock);
2480 	mutex_exit(&oplmsu_uinst->u_lock);
2481 	rw_exit(&oplmsu_uinst->lock);
2482 	return (SUCCESS);
2483 }
2484