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#include <sys/errno.h>
27#include <sys/modctl.h>
28#include <sys/stat.h>
29#include <sys/kmem.h>
30#include <sys/ksynch.h>
31#include <sys/stream.h>
32#include <sys/stropts.h>
33#include <sys/termio.h>
34#include <sys/ddi.h>
35#include <sys/file.h>
36#include <sys/disp.h>
37#include <sys/sunddi.h>
38#include <sys/sunldi.h>
39#include <sys/sunndi.h>
40#include <sys/prom_plat.h>
41#include <sys/oplmsu/oplmsu.h>
42#include <sys/oplmsu/oplmsu_proto.h>
43
44/*
45 *	LOWER READ SERVICE PROCEDURE
46 */
47
48/* termios ioctl response received */
49int
50oplmsu_lrioctl_termios(queue_t *lrq, mblk_t *mp)
51{
52	upath_t		*upath, *altn_upath = NULL, *stp_upath = NULL;
53	lpath_t		*lpath, *altn_lpath = NULL, *stp_lpath = NULL;
54	struct iocblk	*iocp, *temp_iocp = NULL;
55	mblk_t		*hndl_mp, *nmp = NULL, *fmp = NULL;
56	queue_t		*dst_queue;
57	int		term_ioctl, term_stat, sts;
58	int		ack_flag, termio_flag, chkflag;
59	ulong_t		trad_sts;
60
61	rw_enter(&oplmsu_uinst->lock, RW_READER);
62	iocp = (struct iocblk *)mp->b_rptr;
63
64	mutex_enter(&oplmsu_uinst->u_lock);
65	mutex_enter(&oplmsu_uinst->l_lock);
66	lpath = (lpath_t *)lrq->q_ptr;
67	hndl_mp = lpath->hndl_mp;
68
69	upath = oplmsu_search_upath_info(lpath->path_no);
70	trad_sts = upath->traditional_status;
71	mutex_exit(&oplmsu_uinst->l_lock);
72	mutex_exit(&oplmsu_uinst->u_lock);
73
74	if (((iocp->ioc_cmd == TCSETS) && (trad_sts == MSU_WTCS_ACK)) ||
75	    ((iocp->ioc_cmd == TCSETSW) && (trad_sts == MSU_WTCS_ACK)) ||
76	    ((iocp->ioc_cmd == TCSETSF) && (trad_sts == MSU_WTCS_ACK)) ||
77	    ((iocp->ioc_cmd == TIOCMSET) && (trad_sts == MSU_WTMS_ACK)) ||
78	    ((iocp->ioc_cmd == TIOCSPPS) && (trad_sts == MSU_WPPS_ACK)) ||
79	    ((iocp->ioc_cmd == TIOCSWINSZ) && (trad_sts == MSU_WWSZ_ACK)) ||
80	    ((iocp->ioc_cmd == TIOCSSOFTCAR) && (trad_sts == MSU_WCAR_ACK))) {
81		if (mp->b_datap->db_type == M_IOCACK) {
82			ack_flag = ACK_RES;
83		} else {
84			ack_flag = NAK_RES;
85		}
86	} else {
87		rw_exit(&oplmsu_uinst->lock);
88		freemsg(mp);
89		cmn_err(CE_WARN, "oplmsu: lr-termios: "
90		    "Status of path is improper");
91		return (SUCCESS);
92	}
93
94	switch (trad_sts) {
95	case MSU_WTCS_ACK :
96		termio_flag = MSU_TIOS_TCSETS;
97		break;
98
99	case MSU_WTMS_ACK :
100		termio_flag = MSU_TIOS_MSET;
101		break;
102
103	case MSU_WPPS_ACK :
104		termio_flag = MSU_TIOS_PPS;
105		break;
106
107	case MSU_WWSZ_ACK :
108		termio_flag = MSU_TIOS_WINSZP;
109		break;
110
111	case MSU_WCAR_ACK :
112		termio_flag = MSU_TIOS_SOFTCAR;
113		break;
114
115	default :
116		termio_flag = MSU_TIOS_END;
117		break;
118	}
119
120	if (hndl_mp == NULL) {
121		switch (trad_sts) {
122		case MSU_WTCS_ACK :	/* FALLTHRU */
123		case MSU_WTMS_ACK :	/* FALLTHRU */
124		case MSU_WPPS_ACK :	/* FALLTHRU */
125		case MSU_WWSZ_ACK :	/* FALLTHRU */
126		case MSU_WCAR_ACK :
127			chkflag = MSU_CMD_STOP;
128			break;
129
130		default :
131			chkflag = FAILURE;
132			break;
133		}
134	} else {
135		/* xoff/xon received */
136		if (hndl_mp->b_datap->db_type == M_DATA) {
137			chkflag = MSU_CMD_ACTIVE;
138		} else { /* Normal termios */
139			temp_iocp = (struct iocblk *)hndl_mp->b_rptr;
140			chkflag = temp_iocp->ioc_cmd;
141		}
142	}
143
144	if ((chkflag == MSU_CMD_ACTIVE) || (chkflag == MSU_CMD_STOP)) {
145		if (ack_flag == ACK_RES) { /* M_IOCACK received */
146			ctrl_t	*ctrl;
147
148			if (oplmsu_cmn_prechg_termio(lrq, mp, MSU_READ_SIDE,
149			    termio_flag, &nmp, &term_stat) == FAILURE) {
150				rw_exit(&oplmsu_uinst->lock);
151				return (FAILURE);
152			}
153
154			OPLMSU_RWLOCK_UPGRADE();
155			mutex_enter(&oplmsu_uinst->u_lock);
156			if (term_stat != MSU_WPTH_CHG) {
157				upath->traditional_status = term_stat;
158				mutex_exit(&oplmsu_uinst->u_lock);
159				rw_exit(&oplmsu_uinst->lock);
160				freemsg(mp);
161
162				OPLMSU_TRACE(RD(lrq), nmp, MSU_TRC_LO);
163
164				/* Continue sending termios ioctls */
165				qreply(RD(lrq), nmp);
166				return (SUCCESS);
167			}
168			freemsg(mp);
169
170			/* Change status of new active path */
171			oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_ACTIVE,
172			    upath->status, MSU_ACTIVE);
173
174			mutex_enter(&oplmsu_uinst->l_lock);
175			lpath->uinst = oplmsu_uinst;
176			dst_queue = lpath->hndl_uqueue;
177
178			ctrl = oplmsu_uinst->user_ctrl;
179			if ((chkflag == MSU_CMD_ACTIVE) && (hndl_mp != NULL)) {
180				/* Put a message(M_DATA) on a queue */
181				if (ctrl != NULL) {
182					mutex_enter(&oplmsu_uinst->c_lock);
183					(void) putq(RD(ctrl->queue), hndl_mp);
184					mutex_exit(&oplmsu_uinst->c_lock);
185				}
186			}
187
188			oplmsu_clear_ioctl_path(lpath);
189			stp_upath = lpath->src_upath;
190			lpath->src_upath = NULL;
191			lpath->status = MSU_EXT_NOTUSED;
192
193			/* Notify of the active path changing */
194			(void) prom_opl_switch_console(upath->ser_devcb.lsb);
195
196			/* Send XON to notify active path */
197			(void) oplmsu_cmn_put_xoffxon(WR(lrq), MSU_XON_4);
198
199			stp_lpath = stp_upath->lpath;
200			stp_lpath->uinst = NULL;
201			oplmsu_clear_ioctl_path(stp_lpath);
202			stp_lpath->src_upath = NULL;
203			stp_lpath->status = MSU_EXT_NOTUSED;
204
205			/* Change status of stopped or old-active path */
206			if (chkflag == MSU_CMD_STOP) {
207				sts = MSU_PSTAT_STOP;
208				trad_sts = MSU_STOP;
209			} else { /* == MSU_CMD_ACTIVE */
210				sts = MSU_PSTAT_STANDBY;
211				trad_sts = MSU_STANDBY;
212			}
213			oplmsu_cmn_set_upath_sts(stp_upath, sts,
214			    stp_upath->status, trad_sts);
215
216			/* Send XOFF to notify all standby paths */
217			oplmsu_cmn_putxoff_standby();
218			oplmsu_uinst->lower_queue = lrq;
219			oplmsu_uinst->inst_status = oplmsu_get_inst_status();
220			mutex_exit(&oplmsu_uinst->l_lock);
221			mutex_exit(&oplmsu_uinst->u_lock);
222
223			/* Change active path of user node */
224			if (ctrl != NULL) {
225				queue_t	*temp_queue;
226
227				mutex_enter(&oplmsu_uinst->c_lock);
228				temp_queue = WR(ctrl->queue);
229				mutex_exit(&oplmsu_uinst->c_lock);
230
231				/* Reschedule a queue for service */
232				enableok(temp_queue);
233
234				oplmsu_queue_flag = 0;
235				oplmsu_wcmn_high_qenable(temp_queue, RW_WRITER);
236			}
237			rw_exit(&oplmsu_uinst->lock);
238
239			if (nmp != NULL) {
240				freemsg(nmp);
241			}
242
243			/* Wake up oplmsu_config_stop */
244			mutex_enter(&oplmsu_uinst->l_lock);
245			if (stp_lpath->sw_flag) {
246				stp_lpath->sw_flag = 0;
247				cv_signal(&stp_lpath->sw_cv);
248			}
249			mutex_exit(&oplmsu_uinst->l_lock);
250			return (SUCCESS);
251		} else { /* M_IOCNAK received */
252			mutex_enter(&oplmsu_uinst->u_lock);
253			mutex_enter(&oplmsu_uinst->l_lock);
254			if ((chkflag == MSU_CMD_ACTIVE) &&
255			    (lpath->hndl_uqueue == NULL)) {
256				oplmsu_clear_ioctl_path(lpath);
257				stp_upath = lpath->src_upath;
258				lpath->src_upath = NULL;
259				lpath->status = MSU_EXT_NOTUSED;
260				mutex_exit(&oplmsu_uinst->l_lock);
261
262				oplmsu_cmn_set_upath_sts(upath,
263				    MSU_PSTAT_STANDBY, upath->status,
264				    MSU_STANDBY);
265				mutex_exit(&oplmsu_uinst->u_lock);
266
267				if (hndl_mp != NULL) {
268					freemsg(hndl_mp);
269				}
270
271				OPLMSU_RWLOCK_UPGRADE();
272				mutex_enter(&oplmsu_uinst->u_lock);
273				oplmsu_uinst->inst_status =
274				    oplmsu_get_inst_status();
275				mutex_exit(&oplmsu_uinst->u_lock);
276				rw_exit(&oplmsu_uinst->lock);
277				return (SUCCESS);
278			} else if ((chkflag == MSU_CMD_STOP) &&
279			    (lpath->src_upath != NULL) &&
280			    (lpath->src_upath->lpath->sw_flag)) {
281			/* MSU_CMD_STOP for active path */
282
283				dst_queue = RD(lpath->hndl_uqueue);
284				stp_upath = lpath->src_upath;
285
286				/* Search alternate path from standby paths */
287				altn_upath = oplmsu_search_standby();
288				if (altn_upath == NULL) {
289					altn_upath = upath;
290				}
291
292				mutex_exit(&oplmsu_uinst->l_lock);
293				if (oplmsu_cmn_allocmb(lrq, mp, &fmp,
294				    sizeof (char), MSU_READ_SIDE) == FAILURE) {
295					mutex_exit(&oplmsu_uinst->u_lock);
296					rw_exit(&oplmsu_uinst->lock);
297					return (FAILURE);
298				}
299
300				if (oplmsu_cmn_prechg(lrq, mp, MSU_READ_SIDE,
301				    &nmp, &term_ioctl, &term_stat) == FAILURE) {
302					mutex_exit(&oplmsu_uinst->u_lock);
303					rw_exit(&oplmsu_uinst->lock);
304					freeb(fmp);
305					return (FAILURE);
306				}
307
308				altn_upath->traditional_status = term_stat;
309				altn_lpath = altn_upath->lpath;
310
311				mutex_enter(&oplmsu_uinst->l_lock);
312				altn_lpath->hndl_mp = hndl_mp;
313				altn_lpath->hndl_uqueue = dst_queue;
314				altn_lpath->src_upath = stp_upath;
315				altn_lpath->status = MSU_EXT_VOID;
316				dst_queue = RD(altn_lpath->lower_queue);
317
318				oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_FAIL,
319				    upath->status, MSU_FAIL);
320
321				oplmsu_clear_ioctl_path(lpath);
322				lpath->src_upath = NULL;
323				lpath->status = MSU_EXT_NOTUSED;
324				mutex_exit(&oplmsu_uinst->l_lock);
325				mutex_exit(&oplmsu_uinst->u_lock);
326
327				OPLMSU_RWLOCK_UPGRADE();
328				mutex_enter(&oplmsu_uinst->u_lock);
329				oplmsu_uinst->inst_status =
330				    oplmsu_get_inst_status();
331				mutex_exit(&oplmsu_uinst->u_lock);
332				rw_exit(&oplmsu_uinst->lock);
333				freemsg(mp);
334				oplmsu_cmn_set_mflush(fmp);
335
336				OPLMSU_TRACE(dst_queue, fmp, MSU_TRC_LO);
337				qreply(dst_queue, fmp);
338
339				OPLMSU_TRACE(dst_queue, nmp, MSU_TRC_LO);
340				qreply(dst_queue, nmp);
341				return (SUCCESS);
342			}
343		}
344	} else if ((chkflag == TCSETS) || (chkflag == TCSETSW) ||
345	    (chkflag == TCSETSF) || (chkflag == TIOCMSET) ||
346	    (chkflag == TIOCSPPS) || (chkflag == TIOCSWINSZ) ||
347	    (chkflag == TIOCSSOFTCAR)) {
348		mutex_enter(&oplmsu_uinst->u_lock);
349		mutex_enter(&oplmsu_uinst->l_lock);
350
351		if ((ack_flag == ACK_RES) &&
352		    (lpath->hndl_uqueue != NULL)) { /* M_IOCACK received */
353			mutex_exit(&oplmsu_uinst->l_lock);
354			mutex_exit(&oplmsu_uinst->u_lock);
355			if (oplmsu_cmn_copymb(lrq, mp, &nmp, hndl_mp,
356			    MSU_READ_SIDE) == FAILURE) {
357				rw_exit(&oplmsu_uinst->lock);
358				return (FAILURE);
359			}
360
361			OPLMSU_RWLOCK_UPGRADE();
362			switch (chkflag) {
363			case TCSETS :	/* FALLTHRU */
364			case TCSETSW :	/* FALLTHRU */
365			case TCSETSF :
366				if (oplmsu_uinst->tcsets_p != NULL) {
367					freemsg(oplmsu_uinst->tcsets_p);
368				}
369				oplmsu_uinst->tcsets_p = nmp;
370				break;
371
372			case TIOCMSET :
373				if (oplmsu_uinst->tiocmset_p != NULL) {
374					freemsg(oplmsu_uinst->tiocmset_p);
375				}
376				oplmsu_uinst->tiocmset_p = nmp;
377				break;
378
379			case TIOCSPPS :
380				if (oplmsu_uinst->tiocspps_p != NULL) {
381					freemsg(oplmsu_uinst->tiocspps_p);
382				}
383				oplmsu_uinst->tiocspps_p = nmp;
384				break;
385
386			case TIOCSWINSZ :
387				if (oplmsu_uinst->tiocswinsz_p != NULL) {
388					freemsg(oplmsu_uinst->tiocswinsz_p);
389				}
390				oplmsu_uinst->tiocswinsz_p = nmp;
391				break;
392
393			case TIOCSSOFTCAR :
394				if (oplmsu_uinst->tiocssoftcar_p != NULL) {
395					freemsg(oplmsu_uinst->tiocssoftcar_p);
396				}
397				oplmsu_uinst->tiocssoftcar_p = nmp;
398				break;
399			}
400
401			mutex_enter(&oplmsu_uinst->u_lock);
402			mutex_enter(&oplmsu_uinst->l_lock);
403			upath->traditional_status = lpath->status;
404			nmp = lpath->hndl_mp;
405			nmp->b_datap->db_type = M_IOCACK;
406			dst_queue = RD(lpath->hndl_uqueue);
407			bcopy(mp->b_rptr, nmp->b_rptr, sizeof (struct iocblk));
408
409			oplmsu_clear_ioctl_path(lpath);
410			lpath->src_upath = NULL;
411			lpath->status = MSU_EXT_NOTUSED;
412			mutex_exit(&oplmsu_uinst->l_lock);
413			mutex_exit(&oplmsu_uinst->u_lock);
414			freemsg(mp);
415			(void) putq(dst_queue, nmp);
416
417			/* Check sleep flag and wake up thread */
418			oplmsu_cmn_wakeup(dst_queue);
419			rw_exit(&oplmsu_uinst->lock);
420			return (SUCCESS);
421		} else if ((ack_flag == NAK_RES) &&
422		    (lpath->hndl_uqueue != NULL)) { /* M_IOCNAK received */
423			upath->traditional_status = lpath->status;
424
425			nmp = lpath->hndl_mp;
426			nmp->b_datap->db_type = M_IOCNAK;
427			dst_queue = RD(lpath->hndl_uqueue);
428
429			oplmsu_clear_ioctl_path(lpath);
430			lpath->src_upath = NULL;
431			lpath->status = MSU_EXT_NOTUSED;
432			mutex_exit(&oplmsu_uinst->l_lock);
433			mutex_exit(&oplmsu_uinst->u_lock);
434			freemsg(mp);
435			(void) putq(dst_queue, nmp);
436
437			/* Check sleep flag and wake up thread */
438			oplmsu_cmn_wakeup(dst_queue);
439			rw_exit(&oplmsu_uinst->lock);
440			return (SUCCESS);
441		}
442	}
443
444	mutex_enter(&oplmsu_uinst->u_lock);
445	switch (upath->status) {
446	case MSU_PSTAT_FAIL :
447		upath->traditional_status = MSU_FAIL;
448		break;
449
450	case MSU_PSTAT_STOP :
451		upath->traditional_status = MSU_STOP;
452		break;
453
454	case MSU_PSTAT_STANDBY :
455		upath->traditional_status = MSU_STANDBY;
456		break;
457
458	case MSU_PSTAT_ACTIVE :
459		upath->traditional_status = MSU_ACTIVE;
460		break;
461	}
462
463	mutex_enter(&oplmsu_uinst->l_lock);
464	oplmsu_clear_ioctl_path(lpath);
465	mutex_exit(&oplmsu_uinst->l_lock);
466	mutex_exit(&oplmsu_uinst->u_lock);
467	rw_exit(&oplmsu_uinst->lock);
468	freemsg(mp);
469	return (SUCCESS);
470}
471
472/* M_ERROR or M_HANGUP response received */
473int
474oplmsu_lrmsg_error(queue_t *lrq, mblk_t *mp)
475{
476	upath_t	*upath, *altn_upath = NULL;
477	lpath_t	*lpath, *altn_lpath = NULL;
478	mblk_t	*nmp = NULL, *fmp = NULL;
479	queue_t	*dst_queue = NULL;
480	ctrl_t	*ctrl;
481	int	term_stat, term_ioctl;
482
483	rw_enter(&oplmsu_uinst->lock, RW_READER);
484	mutex_enter(&oplmsu_uinst->c_lock);
485	ctrl = oplmsu_uinst->user_ctrl;
486	if (ctrl != NULL) {
487		dst_queue = RD(ctrl->queue);
488	}
489	mutex_exit(&oplmsu_uinst->c_lock);
490
491	mutex_enter(&oplmsu_uinst->u_lock);
492	mutex_enter(&oplmsu_uinst->l_lock);
493	lpath = (lpath_t *)lrq->q_ptr;
494	upath = oplmsu_search_upath_info(lpath->path_no);
495
496	if (upath == NULL) {
497		mutex_exit(&oplmsu_uinst->l_lock);
498		mutex_exit(&oplmsu_uinst->u_lock);
499		rw_exit(&oplmsu_uinst->lock);
500		freemsg(mp);
501		return (SUCCESS);
502	}
503
504	if ((lpath->status == MSU_LINK_NU) ||
505	    (lpath->status == MSU_SETID_NU) ||
506	    (upath->traditional_status == MSU_WSTR_ACK) ||
507	    (upath->traditional_status == MSU_WTCS_ACK) ||
508	    (upath->traditional_status == MSU_WTMS_ACK) ||
509	    (upath->traditional_status == MSU_WPPS_ACK) ||
510	    (upath->traditional_status == MSU_WWSZ_ACK) ||
511	    (upath->traditional_status == MSU_WCAR_ACK) ||
512	    (upath->traditional_status == MSU_WSTP_ACK) ||
513	    (upath->traditional_status == MSU_WPTH_CHG)) {
514		mutex_exit(&oplmsu_uinst->l_lock);
515		mutex_exit(&oplmsu_uinst->u_lock);
516		rw_exit(&oplmsu_uinst->lock);
517		freemsg(mp);
518	} else if ((upath->traditional_status == MSU_MAKE_INST) ||
519	    (upath->traditional_status == MSU_STOP) ||
520	    (upath->traditional_status == MSU_STANDBY) ||
521	    (upath->traditional_status == MSU_SETID) ||
522	    (upath->traditional_status == MSU_LINK)) {
523		oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_FAIL, upath->status,
524		    MSU_FAIL);
525		mutex_exit(&oplmsu_uinst->l_lock);
526		mutex_exit(&oplmsu_uinst->u_lock);
527		rw_exit(&oplmsu_uinst->lock);
528		freemsg(mp);
529	} else if (upath->traditional_status == MSU_FAIL) {
530		mutex_exit(&oplmsu_uinst->l_lock);
531		mutex_exit(&oplmsu_uinst->u_lock);
532		rw_exit(&oplmsu_uinst->lock);
533		freemsg(mp);
534	} else if (upath->traditional_status == MSU_ACTIVE) {
535		altn_upath = oplmsu_search_standby();
536		if (altn_upath == NULL) {
537			oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_FAIL,
538			    upath->status, MSU_FAIL);
539
540			oplmsu_clear_ioctl_path(lpath);
541			lpath->src_upath = NULL;
542			lpath->status = MSU_EXT_NOTUSED;
543			lpath->uinst = NULL;
544			mutex_exit(&oplmsu_uinst->l_lock);
545			mutex_exit(&oplmsu_uinst->u_lock);
546
547			OPLMSU_RWLOCK_UPGRADE();
548			oplmsu_uinst->lower_queue = NULL;
549			rw_exit(&oplmsu_uinst->lock);
550			freemsg(mp);
551			return (SUCCESS);
552		}
553
554		mutex_exit(&oplmsu_uinst->l_lock);
555		if (oplmsu_cmn_allocmb(lrq, mp, &fmp, sizeof (char),
556		    MSU_READ_SIDE) == FAILURE) {
557			mutex_exit(&oplmsu_uinst->u_lock);
558			rw_exit(&oplmsu_uinst->lock);
559			return (FAILURE);
560		}
561
562		if (oplmsu_cmn_prechg(lrq, mp, MSU_READ_SIDE, &nmp, &term_ioctl,
563		    &term_stat) == FAILURE) {
564			mutex_exit(&oplmsu_uinst->u_lock);
565			rw_exit(&oplmsu_uinst->lock);
566			freeb(fmp);
567			return (FAILURE);
568		}
569
570		oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_FAIL,
571		    upath->status, MSU_FAIL);
572
573		mutex_enter(&oplmsu_uinst->l_lock);
574		lpath->uinst = NULL;
575
576		altn_upath->traditional_status = term_stat;
577		altn_lpath = altn_upath->lpath;
578
579		altn_lpath->hndl_mp = NULL;
580		altn_lpath->hndl_uqueue = NULL;
581		altn_lpath->src_upath = upath;
582		altn_lpath->status = MSU_EXT_VOID;
583		dst_queue = RD(altn_lpath->lower_queue);
584		mutex_exit(&oplmsu_uinst->l_lock);
585		mutex_exit(&oplmsu_uinst->u_lock);
586
587		OPLMSU_RWLOCK_UPGRADE();
588		oplmsu_uinst->lower_queue = NULL;
589		oplmsu_cmn_set_mflush(fmp);
590
591		if (ctrl != NULL) {
592			mutex_enter(&oplmsu_uinst->c_lock);
593			noenable(WR(ctrl->queue));
594			mutex_exit(&oplmsu_uinst->c_lock);
595
596			oplmsu_queue_flag = 1;
597		}
598
599		rw_exit(&oplmsu_uinst->lock);
600		freemsg(mp);
601
602		OPLMSU_TRACE(dst_queue, fmp, MSU_TRC_LO);
603		qreply(dst_queue, fmp);
604		OPLMSU_TRACE(dst_queue, nmp, MSU_TRC_LO);
605		qreply(dst_queue, nmp);
606	}
607	return (SUCCESS);
608}
609
610/* M_DATA[xoff/xon] was received from serial port */
611int
612oplmsu_lrdata_xoffxon(queue_t *lrq, mblk_t *mp)
613{
614	upath_t	*upath, *stp_upath = NULL;
615	lpath_t	*lpath, *stp_lpath = NULL;
616	mblk_t	*nmp = NULL, *fmp = NULL;
617	ctrl_t	*ctrl;
618	int	term_stat, term_ioctl;
619
620	rw_enter(&oplmsu_uinst->lock, RW_READER);
621	mutex_enter(&oplmsu_uinst->u_lock);
622	mutex_enter(&oplmsu_uinst->l_lock);
623
624	if (oplmsu_uinst->lower_queue != NULL) {
625		/* Get lower path of active status */
626		stp_lpath = (lpath_t *)oplmsu_uinst->lower_queue->q_ptr;
627		if (stp_lpath != NULL) {
628			stp_upath =
629			    oplmsu_search_upath_info(stp_lpath->path_no);
630		}
631	}
632
633	lpath = (lpath_t *)lrq->q_ptr;
634	upath = oplmsu_search_upath_info(lpath->path_no);
635
636	if (upath == NULL) {
637		mutex_exit(&oplmsu_uinst->l_lock);
638		mutex_exit(&oplmsu_uinst->u_lock);
639		rw_exit(&oplmsu_uinst->lock);
640		freemsg(mp);
641		return (SUCCESS);
642	}
643
644	if ((stp_upath != NULL) && (stp_upath != upath)) {
645		if ((stp_upath->status != MSU_PSTAT_ACTIVE) ||
646		    (stp_upath->traditional_status != MSU_ACTIVE)) {
647			mutex_exit(&oplmsu_uinst->l_lock);
648			mutex_exit(&oplmsu_uinst->u_lock);
649			rw_exit(&oplmsu_uinst->lock);
650			(void) putbq(lrq, mp);
651			return (FAILURE);
652		}
653	}
654
655	if ((upath->status == MSU_PSTAT_ACTIVE) &&
656	    ((upath->traditional_status == MSU_ACTIVE) ||
657	    (upath->traditional_status == MSU_WTCS_ACK) ||
658	    (upath->traditional_status == MSU_WTMS_ACK) ||
659	    (upath->traditional_status == MSU_WPPS_ACK) ||
660	    (upath->traditional_status == MSU_WWSZ_ACK) ||
661	    (upath->traditional_status == MSU_WCAR_ACK))) {
662		mutex_exit(&oplmsu_uinst->l_lock);
663		mutex_exit(&oplmsu_uinst->u_lock);
664		(void) oplmsu_rcmn_through_hndl(lrq, mp, MSU_NORM);
665		rw_exit(&oplmsu_uinst->lock);
666		return (SUCCESS);
667	} else if ((upath->status != MSU_PSTAT_STANDBY) ||
668	    (upath->traditional_status != MSU_STANDBY)) {
669		mutex_exit(&oplmsu_uinst->l_lock);
670		mutex_exit(&oplmsu_uinst->u_lock);
671		rw_exit(&oplmsu_uinst->lock);
672		freemsg(mp);
673		cmn_err(CE_WARN, "oplmsu: lr-xoffxon: "
674		    "Can't change to specified path");
675		return (SUCCESS);
676	}
677	mutex_exit(&oplmsu_uinst->l_lock);
678
679	if (oplmsu_cmn_allocmb(lrq, mp, &fmp, sizeof (char), MSU_READ_SIDE) ==
680	    FAILURE) {
681		mutex_exit(&oplmsu_uinst->u_lock);
682		rw_exit(&oplmsu_uinst->lock);
683		return (FAILURE);
684	}
685
686	if (oplmsu_cmn_prechg(lrq, mp, MSU_READ_SIDE, &nmp, &term_ioctl,
687	    &term_stat) == FAILURE) {
688		mutex_exit(&oplmsu_uinst->u_lock);
689		rw_exit(&oplmsu_uinst->lock);
690		freeb(fmp);
691		return (FAILURE);
692	}
693
694	oplmsu_cmn_set_mflush(fmp);
695	upath->traditional_status = term_stat;
696
697	mutex_enter(&oplmsu_uinst->l_lock);
698	lpath->hndl_mp = mp;
699	lpath->hndl_uqueue = NULL;
700	lpath->src_upath = stp_upath;
701	lpath->status = MSU_EXT_VOID;
702
703	mutex_enter(&oplmsu_uinst->c_lock);
704	ctrl = oplmsu_uinst->user_ctrl;
705	if (term_stat != MSU_WPTH_CHG) {
706		/*
707		 * Send termios to new active path and wait response
708		 */
709		if (ctrl != NULL) {
710			noenable(WR(ctrl->queue));
711		}
712		mutex_exit(&oplmsu_uinst->c_lock);
713		mutex_exit(&oplmsu_uinst->l_lock);
714		mutex_exit(&oplmsu_uinst->u_lock);
715		rw_exit(&oplmsu_uinst->lock);
716
717		OPLMSU_TRACE(RD(lrq), fmp, MSU_TRC_LO);
718		qreply(RD(lrq), fmp);
719		OPLMSU_TRACE(RD(lrq), nmp, MSU_TRC_LO);
720		qreply(RD(lrq), nmp);
721	} else {
722		/*
723		 * No termios messages are received. Change active path.
724		 */
725
726		oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_ACTIVE, upath->status,
727		    MSU_ACTIVE);
728
729		lpath->uinst = oplmsu_uinst;
730		lpath->src_upath = NULL;
731		lpath->status = MSU_EXT_NOTUSED;
732
733		/* Notify of the active path changing */
734		(void) prom_opl_switch_console(upath->ser_devcb.lsb);
735
736		(void) putq(WR(lrq), fmp);
737
738		/* Send XON to notify active path */
739		(void) oplmsu_cmn_put_xoffxon(WR(lrq), MSU_XON_4);
740
741		if (lpath->hndl_mp != NULL) {
742			/* Put a message(M_DATA) on a queue */
743			if (ctrl != NULL) {
744				(void) putq(RD(ctrl->queue), lpath->hndl_mp);
745			}
746		}
747
748		oplmsu_clear_ioctl_path(lpath);
749
750		if (ctrl != NULL) {
751			noenable(WR(ctrl->queue));
752		}
753
754		if ((stp_upath != NULL) && (stp_lpath != NULL)) {
755			/* Change the status of stop path */
756			oplmsu_cmn_set_upath_sts(stp_upath, MSU_PSTAT_STANDBY,
757			    stp_upath->status, MSU_STANDBY);
758
759			oplmsu_clear_ioctl_path(stp_lpath);
760			stp_lpath->uinst = NULL;
761			stp_lpath->src_upath = NULL;
762			stp_lpath->status = MSU_EXT_NOTUSED;
763		}
764#ifdef DEBUG
765		oplmsu_cmn_prt_pathname(upath->ser_devcb.dip);
766#endif
767		/* Send XOFF to notify all standby paths */
768		oplmsu_cmn_putxoff_standby();
769		mutex_exit(&oplmsu_uinst->c_lock);
770		mutex_exit(&oplmsu_uinst->l_lock);
771		mutex_exit(&oplmsu_uinst->u_lock);
772
773		OPLMSU_RWLOCK_UPGRADE();
774		mutex_enter(&oplmsu_uinst->u_lock);
775		oplmsu_uinst->lower_queue = lrq;
776		oplmsu_uinst->inst_status = oplmsu_get_inst_status();
777		mutex_exit(&oplmsu_uinst->u_lock);
778
779		if (ctrl != NULL) {
780			queue_t *temp_queue;
781
782			mutex_enter(&oplmsu_uinst->c_lock);
783			temp_queue = WR(ctrl->queue);
784			mutex_exit(&oplmsu_uinst->c_lock);
785
786			/* Reschedule a queue for service */
787			enableok(temp_queue);
788
789			oplmsu_queue_flag = 0;
790			oplmsu_wcmn_high_qenable(temp_queue, RW_WRITER);
791		}
792		rw_exit(&oplmsu_uinst->lock);
793	}
794	return (SUCCESS);
795}
796