oplmsu.c revision 7656:2621e50fdf4a
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
51extern 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
59struct module_info oplmsu_mod_info = {
60	MOD_ID,
61	MOD_NAME,
62	0,
63	16384,
64	14336,
65	2048
66};
67
68struct qinit oplmsu_urinit = {
69	NULL,
70	oplmsu_ursrv,
71	oplmsu_open,
72	oplmsu_close,
73	NULL,
74	&oplmsu_mod_info,
75	NULL
76};
77
78struct 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
88struct 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
98struct qinit oplmsu_lwinit = {
99	NULL,
100	oplmsu_lwsrv,
101	oplmsu_open,
102	oplmsu_close,
103	NULL,
104	&oplmsu_mod_info,
105	NULL
106};
107
108struct streamtab oplmsu_info = {
109	&oplmsu_urinit,
110	&oplmsu_uwinit,
111	&oplmsu_lrinit,
112	&oplmsu_lwinit
113};
114
115static 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
133static 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
148struct modldrv modldrv = {
149	&mod_driverops,
150	"OPL serial mux driver",
151	&oplmsu_ops
152};
153
154struct modlinkage modlinkage = {
155	MODREV_1,
156	(void *)&modldrv,
157	NULL
158};
159
160uinst_t		oplmsu_uinst_local;	/* upper_instance_table structure */
161uinst_t		*oplmsu_uinst = &oplmsu_uinst_local;
162int		oplmsu_queue_flag;	/* Enable/disable queueing flag */
163int		oplmsu_check_su;	/* Check super-user flag */
164
165#ifdef DEBUG
166int		oplmsu_debug_mode = 0;	/* Enable/disable debug mode */
167int		oplmsu_trace_on;	/* Enable/disable trace */
168uint_t		oplmsu_ltrc_size;	/* Trace buffer size */
169msu_trc_t	*oplmsu_ltrc_top;	/* Top of trace data area */
170msu_trc_t	*oplmsu_ltrc_tail;	/* Tail of trace data area */
171msu_trc_t	*oplmsu_ltrc_cur;	/* Current pointer of trace data area */
172ulong_t		oplmsu_ltrc_ccnt;	/* Current counter */
173kmutex_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
181static kmutex_t		oplmsu_bthrd_excl;
182static kthread_id_t	oplmsu_bthrd_id = NULL;
183static int		oplmsu_conf_st = MSU_UNCONFIGURED;
184static 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
218int
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
253int
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
270int
271_info(struct modinfo *modinfop)
272{
273	return (mod_info(&modlinkage, modinfop));
274}
275
276/* ARGSUSED */
277int
278oplmsu_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
305int
306oplmsu_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
393int
394oplmsu_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 */
458int
459oplmsu_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 */
584int
585oplmsu_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 */
696int
697oplmsu_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 */
736int
737oplmsu_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 */
820int
821oplmsu_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 */
864int
865oplmsu_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 */
906int
907oplmsu_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 */
1015int
1016oplmsu_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
1083int
1084oplmsu_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
1109int
1110oplmsu_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
1163int
1164oplmsu_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
1191int
1192oplmsu_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
1303int
1304oplmsu_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 */
1443dev_info_t *
1444oplmsu_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 */
1471int
1472oplmsu_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 */
1546void
1547oplmsu_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
1666void
1667oplmsu_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
1683void
1684oplmsu_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
1708int
1709oplmsu_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 */
1762int
1763oplmsu_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 */
1861int
1862oplmsu_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 */
1908int
1909oplmsu_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 */
2002int
2003oplmsu_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 */
2269int
2270oplmsu_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 */
2415int
2416oplmsu_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