xref: /illumos-gate/usr/src/uts/sun4u/opl/io/oplmsu/oplmsu_ioctl_lrp.c (revision 25cf1a301a396c38e8adf52c15f537b80d2483f7)
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 ((lpath->status == MSU_LINK_NU) ||
498 	    (lpath->status == MSU_SETID_NU) ||
499 	    (upath->traditional_status == MSU_WSTR_ACK) ||
500 	    (upath->traditional_status == MSU_WTCS_ACK) ||
501 	    (upath->traditional_status == MSU_WTMS_ACK) ||
502 	    (upath->traditional_status == MSU_WPPS_ACK) ||
503 	    (upath->traditional_status == MSU_WWSZ_ACK) ||
504 	    (upath->traditional_status == MSU_WCAR_ACK) ||
505 	    (upath->traditional_status == MSU_WSTP_ACK) ||
506 	    (upath->traditional_status == MSU_WPTH_CHG)) {
507 		mutex_exit(&oplmsu_uinst->l_lock);
508 		mutex_exit(&oplmsu_uinst->u_lock);
509 		rw_exit(&oplmsu_uinst->lock);
510 		freemsg(mp);
511 	} else if ((upath->traditional_status == MSU_MAKE_INST) ||
512 	    (upath->traditional_status == MSU_STOP) ||
513 	    (upath->traditional_status == MSU_STANDBY) ||
514 	    (upath->traditional_status == MSU_SETID) ||
515 	    (upath->traditional_status == MSU_LINK)) {
516 		oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_FAIL, upath->status,
517 		    MSU_FAIL);
518 		mutex_exit(&oplmsu_uinst->l_lock);
519 		mutex_exit(&oplmsu_uinst->u_lock);
520 		rw_exit(&oplmsu_uinst->lock);
521 		freemsg(mp);
522 	} else if (upath->traditional_status == MSU_FAIL) {
523 		mutex_exit(&oplmsu_uinst->l_lock);
524 		mutex_exit(&oplmsu_uinst->u_lock);
525 		rw_exit(&oplmsu_uinst->lock);
526 		freemsg(mp);
527 	} else if (upath->traditional_status == MSU_ACTIVE) {
528 		altn_upath = oplmsu_search_standby();
529 		if (altn_upath == NULL) {
530 			oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_FAIL,
531 			    upath->status, MSU_FAIL);
532 
533 			oplmsu_clear_ioctl_path(lpath);
534 			lpath->src_upath = NULL;
535 			lpath->status = MSU_EXT_NOTUSED;
536 			lpath->uinst = NULL;
537 			mutex_exit(&oplmsu_uinst->l_lock);
538 			mutex_exit(&oplmsu_uinst->u_lock);
539 
540 			OPLMSU_RWLOCK_UPGRADE();
541 			oplmsu_uinst->lower_queue = NULL;
542 			rw_exit(&oplmsu_uinst->lock);
543 			freemsg(mp);
544 			return (SUCCESS);
545 		}
546 
547 		mutex_exit(&oplmsu_uinst->l_lock);
548 		if (oplmsu_cmn_allocmb(lrq, mp, &fmp, sizeof (char),
549 		    MSU_READ_SIDE) == FAILURE) {
550 			mutex_exit(&oplmsu_uinst->u_lock);
551 			rw_exit(&oplmsu_uinst->lock);
552 			return (FAILURE);
553 		}
554 
555 		if (oplmsu_cmn_prechg(lrq, mp, MSU_READ_SIDE, &nmp, &term_ioctl,
556 		    &term_stat) == FAILURE) {
557 			mutex_exit(&oplmsu_uinst->u_lock);
558 			rw_exit(&oplmsu_uinst->lock);
559 			freeb(fmp);
560 			return (FAILURE);
561 		}
562 
563 		oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_FAIL,
564 		    upath->status, MSU_FAIL);
565 
566 		mutex_enter(&oplmsu_uinst->l_lock);
567 		lpath->uinst = NULL;
568 
569 		altn_upath->traditional_status = term_stat;
570 		altn_lpath = altn_upath->lpath;
571 
572 		altn_lpath->hndl_mp = NULL;
573 		altn_lpath->hndl_uqueue = NULL;
574 		altn_lpath->src_upath = upath;
575 		altn_lpath->status = MSU_EXT_VOID;
576 		dst_queue = RD(altn_lpath->lower_queue);
577 		mutex_exit(&oplmsu_uinst->l_lock);
578 		mutex_exit(&oplmsu_uinst->u_lock);
579 
580 		OPLMSU_RWLOCK_UPGRADE();
581 		oplmsu_uinst->lower_queue = NULL;
582 		oplmsu_cmn_set_mflush(fmp);
583 
584 		if (ctrl != NULL) {
585 			mutex_enter(&oplmsu_uinst->c_lock);
586 			noenable(WR(ctrl->queue));
587 			mutex_exit(&oplmsu_uinst->c_lock);
588 
589 			oplmsu_queue_flag = 1;
590 		}
591 
592 		rw_exit(&oplmsu_uinst->lock);
593 		freemsg(mp);
594 
595 		OPLMSU_TRACE(dst_queue, fmp, MSU_TRC_LO);
596 		qreply(dst_queue, fmp);
597 		OPLMSU_TRACE(dst_queue, nmp, MSU_TRC_LO);
598 		qreply(dst_queue, nmp);
599 	}
600 	return (SUCCESS);
601 }
602 
603 /* M_DATA[xoff/xon] was received from serial port */
604 int
605 oplmsu_lrdata_xoffxon(queue_t *lrq, mblk_t *mp)
606 {
607 	upath_t	*upath, *stp_upath = NULL;
608 	lpath_t	*lpath, *stp_lpath = NULL;
609 	mblk_t	*nmp = NULL, *fmp = NULL;
610 	ctrl_t	*ctrl;
611 	int	term_stat, term_ioctl;
612 
613 	rw_enter(&oplmsu_uinst->lock, RW_READER);
614 	mutex_enter(&oplmsu_uinst->u_lock);
615 	mutex_enter(&oplmsu_uinst->l_lock);
616 
617 	if (oplmsu_uinst->lower_queue != NULL) {
618 		/* Get lower path of active status */
619 		stp_lpath = (lpath_t *)oplmsu_uinst->lower_queue->q_ptr;
620 		if (stp_lpath != NULL) {
621 			stp_upath =
622 			    oplmsu_search_upath_info(stp_lpath->path_no);
623 		}
624 	}
625 
626 	lpath = (lpath_t *)lrq->q_ptr;
627 	upath = oplmsu_search_upath_info(lpath->path_no);
628 
629 	if ((stp_upath != NULL) && (stp_upath != upath)) {
630 		if ((stp_upath->status != MSU_PSTAT_ACTIVE) ||
631 		    (stp_upath->traditional_status != MSU_ACTIVE)) {
632 			mutex_exit(&oplmsu_uinst->l_lock);
633 			mutex_exit(&oplmsu_uinst->u_lock);
634 			rw_exit(&oplmsu_uinst->lock);
635 			putbq(lrq, mp);
636 			return (FAILURE);
637 		}
638 	}
639 
640 	if ((upath->status == MSU_PSTAT_ACTIVE) &&
641 	    ((upath->traditional_status == MSU_ACTIVE) ||
642 	    (upath->traditional_status == MSU_WTCS_ACK) ||
643 	    (upath->traditional_status == MSU_WTMS_ACK) ||
644 	    (upath->traditional_status == MSU_WPPS_ACK) ||
645 	    (upath->traditional_status == MSU_WWSZ_ACK) ||
646 	    (upath->traditional_status == MSU_WCAR_ACK))) {
647 		mutex_exit(&oplmsu_uinst->l_lock);
648 		mutex_exit(&oplmsu_uinst->u_lock);
649 		oplmsu_rcmn_through_hndl(lrq, mp, MSU_NORM);
650 		rw_exit(&oplmsu_uinst->lock);
651 		return (SUCCESS);
652 	} else if ((upath->status != MSU_PSTAT_STANDBY) ||
653 	    (upath->traditional_status != MSU_STANDBY)) {
654 		mutex_exit(&oplmsu_uinst->l_lock);
655 		mutex_exit(&oplmsu_uinst->u_lock);
656 		rw_exit(&oplmsu_uinst->lock);
657 		freemsg(mp);
658 		cmn_err(CE_WARN, "oplmsu: lr-xoffxon: "
659 		    "Can't change to specified path");
660 		return (SUCCESS);
661 	}
662 	mutex_exit(&oplmsu_uinst->l_lock);
663 
664 	if (oplmsu_cmn_allocmb(lrq, mp, &fmp, sizeof (char), MSU_READ_SIDE) ==
665 	    FAILURE) {
666 		mutex_exit(&oplmsu_uinst->u_lock);
667 		rw_exit(&oplmsu_uinst->lock);
668 		return (FAILURE);
669 	}
670 
671 	if (oplmsu_cmn_prechg(lrq, mp, MSU_READ_SIDE, &nmp, &term_ioctl,
672 	    &term_stat) == FAILURE) {
673 		mutex_exit(&oplmsu_uinst->u_lock);
674 		rw_exit(&oplmsu_uinst->lock);
675 		freeb(fmp);
676 		return (FAILURE);
677 	}
678 
679 	oplmsu_cmn_set_mflush(fmp);
680 	upath->traditional_status = term_stat;
681 
682 	mutex_enter(&oplmsu_uinst->l_lock);
683 	lpath->hndl_mp = mp;
684 	lpath->hndl_uqueue = NULL;
685 	lpath->src_upath = stp_upath;
686 	lpath->status = MSU_EXT_VOID;
687 
688 	mutex_enter(&oplmsu_uinst->c_lock);
689 	ctrl = oplmsu_uinst->user_ctrl;
690 	if (term_stat != MSU_WPTH_CHG) {
691 		/*
692 		 * Send termios to new active path and wait response
693 		 */
694 		if (ctrl != NULL) {
695 			noenable(WR(ctrl->queue));
696 		}
697 		mutex_exit(&oplmsu_uinst->c_lock);
698 		mutex_exit(&oplmsu_uinst->l_lock);
699 		mutex_exit(&oplmsu_uinst->u_lock);
700 		rw_exit(&oplmsu_uinst->lock);
701 
702 		OPLMSU_TRACE(RD(lrq), fmp, MSU_TRC_LO);
703 		qreply(RD(lrq), fmp);
704 		OPLMSU_TRACE(RD(lrq), nmp, MSU_TRC_LO);
705 		qreply(RD(lrq), nmp);
706 	} else {
707 		/*
708 		 * No termios messages are received. Change active path.
709 		 */
710 
711 		oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_ACTIVE, upath->status,
712 		    MSU_ACTIVE);
713 
714 		lpath->uinst = oplmsu_uinst;
715 		lpath->src_upath = NULL;
716 		lpath->status = MSU_EXT_NOTUSED;
717 
718 		/* Notify of the active path changing */
719 		prom_opl_switch_console(upath->ser_devcb.lsb);
720 
721 		putq(WR(lrq), fmp);
722 
723 		/* Send XON to notify active path */
724 		(void) oplmsu_cmn_put_xoffxon(WR(lrq), MSU_XON_4);
725 
726 		if (lpath->hndl_mp != NULL) {
727 			/* Put a message(M_DATA) on a queue */
728 			if (ctrl != NULL) {
729 				putq(RD(ctrl->queue), lpath->hndl_mp);
730 			}
731 		}
732 
733 		oplmsu_clear_ioctl_path(lpath);
734 
735 		if (ctrl != NULL) {
736 			noenable(WR(ctrl->queue));
737 		}
738 
739 		if ((stp_upath != NULL) && (stp_lpath != NULL)) {
740 			/* Change the status of stop path */
741 			oplmsu_cmn_set_upath_sts(stp_upath, MSU_PSTAT_STANDBY,
742 			    stp_upath->status, MSU_STANDBY);
743 
744 			oplmsu_clear_ioctl_path(stp_lpath);
745 			stp_lpath->uinst = NULL;
746 			stp_lpath->src_upath = NULL;
747 			stp_lpath->status = MSU_EXT_NOTUSED;
748 		}
749 #ifdef DEBUG
750 		oplmsu_cmn_prt_pathname(upath->ser_devcb.dip);
751 #endif
752 		/* Send XOFF to notify all standby paths */
753 		oplmsu_cmn_putxoff_standby();
754 		mutex_exit(&oplmsu_uinst->c_lock);
755 		mutex_exit(&oplmsu_uinst->l_lock);
756 		mutex_exit(&oplmsu_uinst->u_lock);
757 
758 		OPLMSU_RWLOCK_UPGRADE();
759 		mutex_enter(&oplmsu_uinst->u_lock);
760 		oplmsu_uinst->lower_queue = lrq;
761 		oplmsu_uinst->inst_status = oplmsu_get_inst_status();
762 		mutex_exit(&oplmsu_uinst->u_lock);
763 
764 		if (ctrl != NULL) {
765 			queue_t *temp_queue;
766 
767 			mutex_enter(&oplmsu_uinst->c_lock);
768 			temp_queue = WR(ctrl->queue);
769 			mutex_exit(&oplmsu_uinst->c_lock);
770 
771 			/* Reschedule a queue for service */
772 			enableok(temp_queue);
773 
774 			oplmsu_queue_flag = 0;
775 			oplmsu_wcmn_high_qenable(temp_queue, RW_WRITER);
776 		}
777 		rw_exit(&oplmsu_uinst->lock);
778 	}
779 	return (SUCCESS);
780 }
781