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