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