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 */
49 int
oplmsu_lrioctl_termios(queue_t * lrq,mblk_t * mp)50 oplmsu_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 */
473 int
oplmsu_lrmsg_error(queue_t * lrq,mblk_t * mp)474 oplmsu_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 */
611 int
oplmsu_lrdata_xoffxon(queue_t * lrq,mblk_t * mp)612 oplmsu_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