xref: /illumos-gate/usr/src/uts/common/io/tty_common.c (revision 9a2c4685)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
37c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
47c478bd9Sstevel@tonic-gate  */
57c478bd9Sstevel@tonic-gate 
67c478bd9Sstevel@tonic-gate /*
77c478bd9Sstevel@tonic-gate  * Copyright (c) 1983 Regents of the University of California.
87c478bd9Sstevel@tonic-gate  * All rights reserved. The Berkeley software License Agreement
97c478bd9Sstevel@tonic-gate  * specifies the terms and conditions for redistribution.
107c478bd9Sstevel@tonic-gate  */
117c478bd9Sstevel@tonic-gate 
127c478bd9Sstevel@tonic-gate #include <sys/types.h>
137c478bd9Sstevel@tonic-gate #include <sys/param.h>
147c478bd9Sstevel@tonic-gate #include <sys/signal.h>
157c478bd9Sstevel@tonic-gate #include <sys/systm.h>
167c478bd9Sstevel@tonic-gate #include <sys/termio.h>
177c478bd9Sstevel@tonic-gate #include <sys/ttold.h>
187c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
197c478bd9Sstevel@tonic-gate #include <sys/stream.h>
207c478bd9Sstevel@tonic-gate #include <sys/strsubr.h>
217c478bd9Sstevel@tonic-gate #include <sys/strsun.h>
227c478bd9Sstevel@tonic-gate #include <sys/tty.h>
237c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
247c478bd9Sstevel@tonic-gate #include <sys/errno.h>
257c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
267c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
277c478bd9Sstevel@tonic-gate #include <sys/esunddi.h>
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate /*
307c478bd9Sstevel@tonic-gate  * The default (sane) set of termios values, unless
317c478bd9Sstevel@tonic-gate  * otherwise set by the user.
327c478bd9Sstevel@tonic-gate  */
337c478bd9Sstevel@tonic-gate static struct termios default_termios = {
347c478bd9Sstevel@tonic-gate 	BRKINT|ICRNL|IXON|IMAXBEL,		/* c_iflag */
357c478bd9Sstevel@tonic-gate 	OPOST|ONLCR|TAB3,			/* c_oflag */
367c478bd9Sstevel@tonic-gate 	B9600|CS8|CREAD,			/* c_cflag */
377c478bd9Sstevel@tonic-gate 	ISIG|ICANON|IEXTEN|ECHO|ECHOK|ECHOE|ECHOKE|ECHOCTL, /* c_lflag */
387c478bd9Sstevel@tonic-gate 	{
397c478bd9Sstevel@tonic-gate 		CINTR,
407c478bd9Sstevel@tonic-gate 		CQUIT,
417c478bd9Sstevel@tonic-gate 		CERASE,
427c478bd9Sstevel@tonic-gate 		CKILL,
437c478bd9Sstevel@tonic-gate 		CEOF,
447c478bd9Sstevel@tonic-gate 		CEOL,
457c478bd9Sstevel@tonic-gate 		CEOL2,
467c478bd9Sstevel@tonic-gate 		CNSWTCH,
477c478bd9Sstevel@tonic-gate 		CSTART,
487c478bd9Sstevel@tonic-gate 		CSTOP,
497c478bd9Sstevel@tonic-gate 		CSUSP,
507c478bd9Sstevel@tonic-gate 		CDSUSP,
517c478bd9Sstevel@tonic-gate 		CRPRNT,
527c478bd9Sstevel@tonic-gate 		CFLUSH,
537c478bd9Sstevel@tonic-gate 		CWERASE,
547c478bd9Sstevel@tonic-gate 		CLNEXT,
55*9a2c4685SToomas Soome 		CSTATUS,
56*9a2c4685SToomas Soome 		CERASE2
577c478bd9Sstevel@tonic-gate 	}
587c478bd9Sstevel@tonic-gate };
597c478bd9Sstevel@tonic-gate 
607c478bd9Sstevel@tonic-gate 
617c478bd9Sstevel@tonic-gate static int termioval(char **, uint_t *, char *);
627c478bd9Sstevel@tonic-gate 
637c478bd9Sstevel@tonic-gate void
ttycommon_close(tty_common_t * tc)647c478bd9Sstevel@tonic-gate ttycommon_close(tty_common_t *tc)
657c478bd9Sstevel@tonic-gate {
667c478bd9Sstevel@tonic-gate 	mutex_enter(&tc->t_excl);
677c478bd9Sstevel@tonic-gate 	tc->t_flags &= ~TS_XCLUDE;
687c478bd9Sstevel@tonic-gate 	tc->t_readq = NULL;
697c478bd9Sstevel@tonic-gate 	tc->t_writeq = NULL;
707c478bd9Sstevel@tonic-gate 	if (tc->t_iocpending != NULL) {
717c478bd9Sstevel@tonic-gate 		mblk_t *mp;
727c478bd9Sstevel@tonic-gate 
737c478bd9Sstevel@tonic-gate 		mp = tc->t_iocpending;
747c478bd9Sstevel@tonic-gate 		tc->t_iocpending = NULL;
757c478bd9Sstevel@tonic-gate 		mutex_exit(&tc->t_excl);
767c478bd9Sstevel@tonic-gate 		/*
777c478bd9Sstevel@tonic-gate 		 * We were holding an "ioctl" response pending the
787c478bd9Sstevel@tonic-gate 		 * availability of an "mblk" to hold data to be passed up;
797c478bd9Sstevel@tonic-gate 		 * another "ioctl" came through, which means that "ioctl"
807c478bd9Sstevel@tonic-gate 		 * must have timed out or been aborted.
817c478bd9Sstevel@tonic-gate 		 */
827c478bd9Sstevel@tonic-gate 		freemsg(mp);
837c478bd9Sstevel@tonic-gate 	} else
847c478bd9Sstevel@tonic-gate 		mutex_exit(&tc->t_excl);
857c478bd9Sstevel@tonic-gate }
867c478bd9Sstevel@tonic-gate 
877c478bd9Sstevel@tonic-gate /*
887c478bd9Sstevel@tonic-gate  * A "line discipline" module's queue is full.
897c478bd9Sstevel@tonic-gate  * Check whether IMAXBEL is set; if so, output a ^G, otherwise send an M_FLUSH
907c478bd9Sstevel@tonic-gate  * upstream flushing all the read queues.
917c478bd9Sstevel@tonic-gate  */
927c478bd9Sstevel@tonic-gate void
ttycommon_qfull(tty_common_t * tc,queue_t * q)937c478bd9Sstevel@tonic-gate ttycommon_qfull(tty_common_t *tc, queue_t *q)
947c478bd9Sstevel@tonic-gate {
957c478bd9Sstevel@tonic-gate 	mblk_t *mp;
967c478bd9Sstevel@tonic-gate 
977c478bd9Sstevel@tonic-gate 	if (tc->t_iflag & IMAXBEL) {
987c478bd9Sstevel@tonic-gate 		if (canput(WR(q))) {
997c478bd9Sstevel@tonic-gate 			if ((mp = allocb(1, BPRI_HI)) != NULL) {
1007c478bd9Sstevel@tonic-gate 				*mp->b_wptr++ = CTRL('g');
1017c478bd9Sstevel@tonic-gate 				(void) putq(WR(q), mp);
1027c478bd9Sstevel@tonic-gate 			}
1037c478bd9Sstevel@tonic-gate 		}
1047c478bd9Sstevel@tonic-gate 	} else {
1057c478bd9Sstevel@tonic-gate 		flushq(q, FLUSHDATA);
1067c478bd9Sstevel@tonic-gate 		(void) putnextctl1(q, M_FLUSH, FLUSHR);
1077c478bd9Sstevel@tonic-gate 	}
1087c478bd9Sstevel@tonic-gate }
1097c478bd9Sstevel@tonic-gate 
1107c478bd9Sstevel@tonic-gate /*
1117c478bd9Sstevel@tonic-gate  * Process an "ioctl" message sent down to us, and return a reply message,
1127c478bd9Sstevel@tonic-gate  * even if we don't understand the "ioctl".  Our client may want to use
1137c478bd9Sstevel@tonic-gate  * that reply message for its own purposes if we don't understand it but
1147c478bd9Sstevel@tonic-gate  * they do, and may want to modify it if we both understand it but they
1157c478bd9Sstevel@tonic-gate  * understand it better than we do.
1167c478bd9Sstevel@tonic-gate  * If the "ioctl" reply requires additional data to be passed up to the
1177c478bd9Sstevel@tonic-gate  * caller, and we cannot allocate an mblk to hold the data, we return the
1187c478bd9Sstevel@tonic-gate  * amount of data to be sent, so that our caller can do a "bufcall" and try
1197c478bd9Sstevel@tonic-gate  * again later; otherwise, we return 0.
1207c478bd9Sstevel@tonic-gate  */
1217c478bd9Sstevel@tonic-gate size_t
ttycommon_ioctl(tty_common_t * tc,queue_t * q,mblk_t * mp,int * errorp)1227c478bd9Sstevel@tonic-gate ttycommon_ioctl(tty_common_t *tc, queue_t *q, mblk_t *mp, int *errorp)
1237c478bd9Sstevel@tonic-gate {
1247c478bd9Sstevel@tonic-gate 	struct iocblk *iocp;
1257c478bd9Sstevel@tonic-gate 	size_t ioctlrespsize;
1267c478bd9Sstevel@tonic-gate 	mblk_t *tmp;
1277c478bd9Sstevel@tonic-gate 
1287c478bd9Sstevel@tonic-gate 	*errorp = 0;	/* no error detected yet */
1297c478bd9Sstevel@tonic-gate 
1307c478bd9Sstevel@tonic-gate 	iocp = (struct iocblk *)mp->b_rptr;
1317c478bd9Sstevel@tonic-gate 
1327c478bd9Sstevel@tonic-gate 	if (iocp->ioc_count == TRANSPARENT) {
1337c478bd9Sstevel@tonic-gate 		*errorp = -1;	/* we don't understand it, maybe they do */
1347c478bd9Sstevel@tonic-gate 		return (0);
1357c478bd9Sstevel@tonic-gate 	}
1367c478bd9Sstevel@tonic-gate 
1377c478bd9Sstevel@tonic-gate 	switch (iocp->ioc_cmd) {
1387c478bd9Sstevel@tonic-gate 
1397c478bd9Sstevel@tonic-gate 	case TCSETSF:
1407c478bd9Sstevel@tonic-gate 		/*
1417c478bd9Sstevel@tonic-gate 		 * Flush the driver's queue, and send an M_FLUSH upstream
1427c478bd9Sstevel@tonic-gate 		 * to flush everybody above us.
1437c478bd9Sstevel@tonic-gate 		 */
1447c478bd9Sstevel@tonic-gate 		flushq(RD(q), FLUSHDATA);
1457c478bd9Sstevel@tonic-gate 		(void) putnextctl1(RD(q), M_FLUSH, FLUSHR);
1467c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
1477c478bd9Sstevel@tonic-gate 
1487c478bd9Sstevel@tonic-gate 	case TCSETSW:
1497c478bd9Sstevel@tonic-gate 	case TCSETS: {
1507c478bd9Sstevel@tonic-gate 		struct termios *cb;
1517c478bd9Sstevel@tonic-gate 
1527c478bd9Sstevel@tonic-gate 		if (miocpullup(mp, sizeof (struct termios)) != 0) {
1537c478bd9Sstevel@tonic-gate 			*errorp = -1;
1547c478bd9Sstevel@tonic-gate 			break;
1557c478bd9Sstevel@tonic-gate 		}
1567c478bd9Sstevel@tonic-gate 
1577c478bd9Sstevel@tonic-gate 		/*
1587c478bd9Sstevel@tonic-gate 		 * The only information we look at are the iflag word,
1597c478bd9Sstevel@tonic-gate 		 * the cflag word, and the start and stop characters.
1607c478bd9Sstevel@tonic-gate 		 */
1617c478bd9Sstevel@tonic-gate 		cb = (struct termios *)mp->b_cont->b_rptr;
1627c478bd9Sstevel@tonic-gate 		mutex_enter(&tc->t_excl);
1637c478bd9Sstevel@tonic-gate 		tc->t_iflag = cb->c_iflag;
1647c478bd9Sstevel@tonic-gate 		tc->t_cflag = cb->c_cflag;
1657c478bd9Sstevel@tonic-gate 		tc->t_stopc = cb->c_cc[VSTOP];
1667c478bd9Sstevel@tonic-gate 		tc->t_startc = cb->c_cc[VSTART];
1677c478bd9Sstevel@tonic-gate 		mutex_exit(&tc->t_excl);
1687c478bd9Sstevel@tonic-gate 		break;
1697c478bd9Sstevel@tonic-gate 	}
1707c478bd9Sstevel@tonic-gate 
1717c478bd9Sstevel@tonic-gate 	case TCSETAF:
1727c478bd9Sstevel@tonic-gate 		/*
1737c478bd9Sstevel@tonic-gate 		 * Flush the driver's queue, and send an M_FLUSH upstream
1747c478bd9Sstevel@tonic-gate 		 * to flush everybody above us.
1757c478bd9Sstevel@tonic-gate 		 */
1767c478bd9Sstevel@tonic-gate 		flushq(RD(q), FLUSHDATA);
1777c478bd9Sstevel@tonic-gate 		(void) putnextctl1(RD(q), M_FLUSH, FLUSHR);
1787c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
1797c478bd9Sstevel@tonic-gate 
1807c478bd9Sstevel@tonic-gate 	case TCSETAW:
1817c478bd9Sstevel@tonic-gate 	case TCSETA: {
1827c478bd9Sstevel@tonic-gate 		struct termio *cb;
1837c478bd9Sstevel@tonic-gate 
1847c478bd9Sstevel@tonic-gate 		if (miocpullup(mp, sizeof (struct termio)) != 0) {
1857c478bd9Sstevel@tonic-gate 			*errorp = -1;
1867c478bd9Sstevel@tonic-gate 			break;
1877c478bd9Sstevel@tonic-gate 		}
1887c478bd9Sstevel@tonic-gate 
1897c478bd9Sstevel@tonic-gate 		/*
1907c478bd9Sstevel@tonic-gate 		 * The only information we look at are the iflag word
1917c478bd9Sstevel@tonic-gate 		 * and the cflag word.  Don't touch the unset portions.
1927c478bd9Sstevel@tonic-gate 		 */
1937c478bd9Sstevel@tonic-gate 		cb = (struct termio *)mp->b_cont->b_rptr;
1947c478bd9Sstevel@tonic-gate 		mutex_enter(&tc->t_excl);
1957c478bd9Sstevel@tonic-gate 		tc->t_iflag = (tc->t_iflag & 0xffff0000 | cb->c_iflag);
1967c478bd9Sstevel@tonic-gate 		tc->t_cflag = (tc->t_cflag & 0xffff0000 | cb->c_cflag);
1977c478bd9Sstevel@tonic-gate 		mutex_exit(&tc->t_excl);
1987c478bd9Sstevel@tonic-gate 		break;
1997c478bd9Sstevel@tonic-gate 	}
2007c478bd9Sstevel@tonic-gate 
2017c478bd9Sstevel@tonic-gate 	case TIOCSWINSZ: {
2027c478bd9Sstevel@tonic-gate 		struct winsize *ws;
2037c478bd9Sstevel@tonic-gate 
2047c478bd9Sstevel@tonic-gate 		if (miocpullup(mp, sizeof (struct winsize)) != 0) {
2057c478bd9Sstevel@tonic-gate 			*errorp = -1;
2067c478bd9Sstevel@tonic-gate 			break;
2077c478bd9Sstevel@tonic-gate 		}
2087c478bd9Sstevel@tonic-gate 
2097c478bd9Sstevel@tonic-gate 		/*
2107c478bd9Sstevel@tonic-gate 		 * If the window size changed, send a SIGWINCH.
2117c478bd9Sstevel@tonic-gate 		 */
2127c478bd9Sstevel@tonic-gate 		ws = (struct winsize *)mp->b_cont->b_rptr;
2137c478bd9Sstevel@tonic-gate 		mutex_enter(&tc->t_excl);
2147c478bd9Sstevel@tonic-gate 		if (bcmp(&tc->t_size, ws, sizeof (struct winsize)) != 0) {
2157c478bd9Sstevel@tonic-gate 			tc->t_size = *ws;
2167c478bd9Sstevel@tonic-gate 			mutex_exit(&tc->t_excl);
2177c478bd9Sstevel@tonic-gate 			(void) putnextctl1(RD(q), M_PCSIG, SIGWINCH);
2187c478bd9Sstevel@tonic-gate 		} else
2197c478bd9Sstevel@tonic-gate 			mutex_exit(&tc->t_excl);
2207c478bd9Sstevel@tonic-gate 		break;
2217c478bd9Sstevel@tonic-gate 	}
2227c478bd9Sstevel@tonic-gate 
2237c478bd9Sstevel@tonic-gate 	/*
2247c478bd9Sstevel@tonic-gate 	 * Prevent more opens.
2257c478bd9Sstevel@tonic-gate 	 */
2267c478bd9Sstevel@tonic-gate 	case TIOCEXCL:
2277c478bd9Sstevel@tonic-gate 		mutex_enter(&tc->t_excl);
2287c478bd9Sstevel@tonic-gate 		tc->t_flags |= TS_XCLUDE;
2297c478bd9Sstevel@tonic-gate 		mutex_exit(&tc->t_excl);
2307c478bd9Sstevel@tonic-gate 		break;
2317c478bd9Sstevel@tonic-gate 
2327c478bd9Sstevel@tonic-gate 	/*
2337c478bd9Sstevel@tonic-gate 	 * Permit more opens.
2347c478bd9Sstevel@tonic-gate 	 */
2357c478bd9Sstevel@tonic-gate 	case TIOCNXCL:
2367c478bd9Sstevel@tonic-gate 		mutex_enter(&tc->t_excl);
2377c478bd9Sstevel@tonic-gate 		tc->t_flags &= ~TS_XCLUDE;
2387c478bd9Sstevel@tonic-gate 		mutex_exit(&tc->t_excl);
2397c478bd9Sstevel@tonic-gate 		break;
2407c478bd9Sstevel@tonic-gate 
2417c478bd9Sstevel@tonic-gate 	/*
2427c478bd9Sstevel@tonic-gate 	 * Set or clear the "soft carrier" flag.
2437c478bd9Sstevel@tonic-gate 	 */
2447c478bd9Sstevel@tonic-gate 	case TIOCSSOFTCAR:
2457c478bd9Sstevel@tonic-gate 		if (miocpullup(mp, sizeof (int)) != 0) {
2467c478bd9Sstevel@tonic-gate 			*errorp = -1;
2477c478bd9Sstevel@tonic-gate 			break;
2487c478bd9Sstevel@tonic-gate 		}
2497c478bd9Sstevel@tonic-gate 
2507c478bd9Sstevel@tonic-gate 		mutex_enter(&tc->t_excl);
2517c478bd9Sstevel@tonic-gate 		if (*(int *)mp->b_cont->b_rptr)
2527c478bd9Sstevel@tonic-gate 			tc->t_flags |= TS_SOFTCAR;
2537c478bd9Sstevel@tonic-gate 		else
2547c478bd9Sstevel@tonic-gate 			tc->t_flags &= ~TS_SOFTCAR;
2557c478bd9Sstevel@tonic-gate 		mutex_exit(&tc->t_excl);
2567c478bd9Sstevel@tonic-gate 		break;
2577c478bd9Sstevel@tonic-gate 
2587c478bd9Sstevel@tonic-gate 	/*
2597c478bd9Sstevel@tonic-gate 	 * The permission checking has already been done at the stream
2607c478bd9Sstevel@tonic-gate 	 * head, since it has to be done in the context of the process
2617c478bd9Sstevel@tonic-gate 	 * doing the call.
2627c478bd9Sstevel@tonic-gate 	 */
2637c478bd9Sstevel@tonic-gate 	case TIOCSTI: {
2647c478bd9Sstevel@tonic-gate 		mblk_t *bp;
2657c478bd9Sstevel@tonic-gate 
2667c478bd9Sstevel@tonic-gate 		if (miocpullup(mp, sizeof (char)) != 0) {
2677c478bd9Sstevel@tonic-gate 			*errorp = -1;
2687c478bd9Sstevel@tonic-gate 			break;
2697c478bd9Sstevel@tonic-gate 		}
2707c478bd9Sstevel@tonic-gate 
2717c478bd9Sstevel@tonic-gate 		/*
2727c478bd9Sstevel@tonic-gate 		 * Simulate typing of a character at the terminal.
2737c478bd9Sstevel@tonic-gate 		 */
2747c478bd9Sstevel@tonic-gate 		if ((bp = allocb(1, BPRI_MED)) != NULL) {
2757c478bd9Sstevel@tonic-gate 			if (!canput(tc->t_readq->q_next))
2767c478bd9Sstevel@tonic-gate 				freemsg(bp);
2777c478bd9Sstevel@tonic-gate 			else {
2787c478bd9Sstevel@tonic-gate 				*bp->b_wptr++ = *mp->b_cont->b_rptr;
2797c478bd9Sstevel@tonic-gate 				putnext(tc->t_readq, bp);
2807c478bd9Sstevel@tonic-gate 			}
2817c478bd9Sstevel@tonic-gate 		}
2827c478bd9Sstevel@tonic-gate 		break;
2837c478bd9Sstevel@tonic-gate 	}
2847c478bd9Sstevel@tonic-gate 	}
2857c478bd9Sstevel@tonic-gate 
2867c478bd9Sstevel@tonic-gate 	/*
2877c478bd9Sstevel@tonic-gate 	 * Turn the ioctl message into an ioctl ACK message.
2887c478bd9Sstevel@tonic-gate 	 */
2897c478bd9Sstevel@tonic-gate 	iocp->ioc_count = 0;	/* no data returned unless we say so */
2907c478bd9Sstevel@tonic-gate 	mp->b_datap->db_type = M_IOCACK;
2917c478bd9Sstevel@tonic-gate 
2927c478bd9Sstevel@tonic-gate 	switch (iocp->ioc_cmd) {
2937c478bd9Sstevel@tonic-gate 
2947c478bd9Sstevel@tonic-gate 	case TCSETSF:
2957c478bd9Sstevel@tonic-gate 	case TCSETSW:
2967c478bd9Sstevel@tonic-gate 	case TCSETS:
2977c478bd9Sstevel@tonic-gate 	case TCSETAF:
2987c478bd9Sstevel@tonic-gate 	case TCSETAW:
2997c478bd9Sstevel@tonic-gate 	case TCSETA:
3007c478bd9Sstevel@tonic-gate 	case TIOCSWINSZ:
3017c478bd9Sstevel@tonic-gate 	case TIOCEXCL:
3027c478bd9Sstevel@tonic-gate 	case TIOCNXCL:
3037c478bd9Sstevel@tonic-gate 	case TIOCSSOFTCAR:
3047c478bd9Sstevel@tonic-gate 	case TIOCSTI:
3057c478bd9Sstevel@tonic-gate 		/*
3067c478bd9Sstevel@tonic-gate 		 * We've done all the important work on these already;
3077c478bd9Sstevel@tonic-gate 		 * just reply with an ACK.
3087c478bd9Sstevel@tonic-gate 		 */
3097c478bd9Sstevel@tonic-gate 		break;
3107c478bd9Sstevel@tonic-gate 
3117c478bd9Sstevel@tonic-gate 	case TCGETS: {
3127c478bd9Sstevel@tonic-gate 		struct termios *cb;
3137c478bd9Sstevel@tonic-gate 		mblk_t *datap;
3147c478bd9Sstevel@tonic-gate 
3157c478bd9Sstevel@tonic-gate 		if ((datap = allocb(sizeof (struct termios),
3167c478bd9Sstevel@tonic-gate 		    BPRI_HI)) == NULL) {
3177c478bd9Sstevel@tonic-gate 			ioctlrespsize = sizeof (struct termios);
3187c478bd9Sstevel@tonic-gate 			goto allocfailure;
3197c478bd9Sstevel@tonic-gate 		}
3207c478bd9Sstevel@tonic-gate 		cb = (struct termios *)datap->b_wptr;
3217c478bd9Sstevel@tonic-gate 		/*
3227c478bd9Sstevel@tonic-gate 		 * The only information we supply is the cflag word.
3237c478bd9Sstevel@tonic-gate 		 * Our copy of the iflag word is just that, a copy.
3247c478bd9Sstevel@tonic-gate 		 */
3257c478bd9Sstevel@tonic-gate 		bzero(cb, sizeof (struct termios));
3267c478bd9Sstevel@tonic-gate 		cb->c_cflag = tc->t_cflag;
3277c478bd9Sstevel@tonic-gate 		datap->b_wptr += sizeof (struct termios);
3287c478bd9Sstevel@tonic-gate 		iocp->ioc_count = sizeof (struct termios);
3297c478bd9Sstevel@tonic-gate 		if (mp->b_cont != NULL)
3307c478bd9Sstevel@tonic-gate 			freemsg(mp->b_cont);
3317c478bd9Sstevel@tonic-gate 		mp->b_cont = datap;
3327c478bd9Sstevel@tonic-gate 		break;
3337c478bd9Sstevel@tonic-gate 	}
3347c478bd9Sstevel@tonic-gate 
3357c478bd9Sstevel@tonic-gate 	case TCGETA: {
3367c478bd9Sstevel@tonic-gate 		struct termio *cb;
3377c478bd9Sstevel@tonic-gate 		mblk_t *datap;
3387c478bd9Sstevel@tonic-gate 
3397c478bd9Sstevel@tonic-gate 		if ((datap = allocb(sizeof (struct termio), BPRI_HI)) == NULL) {
3407c478bd9Sstevel@tonic-gate 			ioctlrespsize = sizeof (struct termio);
3417c478bd9Sstevel@tonic-gate 			goto allocfailure;
3427c478bd9Sstevel@tonic-gate 		}
3437c478bd9Sstevel@tonic-gate 
3447c478bd9Sstevel@tonic-gate 		cb = (struct termio *)datap->b_wptr;
3457c478bd9Sstevel@tonic-gate 		/*
3467c478bd9Sstevel@tonic-gate 		 * The only information we supply is the cflag word.
3477c478bd9Sstevel@tonic-gate 		 * Our copy of the iflag word is just that, a copy.
3487c478bd9Sstevel@tonic-gate 		 */
3497c478bd9Sstevel@tonic-gate 		bzero(cb, sizeof (struct termio));
3507c478bd9Sstevel@tonic-gate 		cb->c_cflag = tc->t_cflag;
3517c478bd9Sstevel@tonic-gate 		datap->b_wptr += sizeof (struct termio);
3527c478bd9Sstevel@tonic-gate 		iocp->ioc_count = sizeof (struct termio);
3537c478bd9Sstevel@tonic-gate 		if (mp->b_cont != NULL)
3547c478bd9Sstevel@tonic-gate 			freemsg(mp->b_cont);
3557c478bd9Sstevel@tonic-gate 		mp->b_cont = datap;
3567c478bd9Sstevel@tonic-gate 		break;
3577c478bd9Sstevel@tonic-gate 	}
3587c478bd9Sstevel@tonic-gate 
3597c478bd9Sstevel@tonic-gate 	/*
3607c478bd9Sstevel@tonic-gate 	 * Get the "soft carrier" flag.
3617c478bd9Sstevel@tonic-gate 	 */
3627c478bd9Sstevel@tonic-gate 	case TIOCGSOFTCAR: {
3637c478bd9Sstevel@tonic-gate 		mblk_t *datap;
3647c478bd9Sstevel@tonic-gate 
3657c478bd9Sstevel@tonic-gate 		if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL) {
3667c478bd9Sstevel@tonic-gate 			ioctlrespsize = sizeof (int);
3677c478bd9Sstevel@tonic-gate 			goto allocfailure;
3687c478bd9Sstevel@tonic-gate 		}
3697c478bd9Sstevel@tonic-gate 		if (tc->t_flags & TS_SOFTCAR)
3707c478bd9Sstevel@tonic-gate 			*(int *)datap->b_wptr = 1;
3717c478bd9Sstevel@tonic-gate 		else
3727c478bd9Sstevel@tonic-gate 			*(int *)datap->b_wptr = 0;
3737c478bd9Sstevel@tonic-gate 		datap->b_wptr += sizeof (int);
3747c478bd9Sstevel@tonic-gate 		iocp->ioc_count = sizeof (int);
3757c478bd9Sstevel@tonic-gate 		if (mp->b_cont != NULL)
3767c478bd9Sstevel@tonic-gate 			freemsg(mp->b_cont);
3777c478bd9Sstevel@tonic-gate 		mp->b_cont = datap;
3787c478bd9Sstevel@tonic-gate 		break;
3797c478bd9Sstevel@tonic-gate 	}
3807c478bd9Sstevel@tonic-gate 
3817c478bd9Sstevel@tonic-gate 	case TIOCGWINSZ: {
3827c478bd9Sstevel@tonic-gate 		mblk_t *datap;
3837c478bd9Sstevel@tonic-gate 
3847c478bd9Sstevel@tonic-gate 		if ((datap = allocb(sizeof (struct winsize),
3857c478bd9Sstevel@tonic-gate 		    BPRI_HI)) == NULL) {
3867c478bd9Sstevel@tonic-gate 			ioctlrespsize = sizeof (struct winsize);
3877c478bd9Sstevel@tonic-gate 			goto allocfailure;
3887c478bd9Sstevel@tonic-gate 		}
3897c478bd9Sstevel@tonic-gate 		/*
3907c478bd9Sstevel@tonic-gate 		 * Return the current size.
3917c478bd9Sstevel@tonic-gate 		 */
3927c478bd9Sstevel@tonic-gate 		*(struct winsize *)datap->b_wptr = tc->t_size;
3937c478bd9Sstevel@tonic-gate 		datap->b_wptr += sizeof (struct winsize);
3947c478bd9Sstevel@tonic-gate 		iocp->ioc_count = sizeof (struct winsize);
3957c478bd9Sstevel@tonic-gate 		if (mp->b_cont != NULL)
3967c478bd9Sstevel@tonic-gate 			freemsg(mp->b_cont);
3977c478bd9Sstevel@tonic-gate 		mp->b_cont = datap;
3987c478bd9Sstevel@tonic-gate 		break;
3997c478bd9Sstevel@tonic-gate 	}
4007c478bd9Sstevel@tonic-gate 
4017c478bd9Sstevel@tonic-gate 	default:
4027c478bd9Sstevel@tonic-gate 		*errorp = -1;	/* we don't understand it, maybe they do */
4037c478bd9Sstevel@tonic-gate 		break;
4047c478bd9Sstevel@tonic-gate 	}
4057c478bd9Sstevel@tonic-gate 	return (0);
4067c478bd9Sstevel@tonic-gate 
4077c478bd9Sstevel@tonic-gate allocfailure:
4087c478bd9Sstevel@tonic-gate 
4097c478bd9Sstevel@tonic-gate 	mutex_enter(&tc->t_excl);
4107c478bd9Sstevel@tonic-gate 	tmp = tc->t_iocpending;
4117c478bd9Sstevel@tonic-gate 	tc->t_iocpending = mp;	/* hold this ioctl */
4127c478bd9Sstevel@tonic-gate 	mutex_exit(&tc->t_excl);
4137c478bd9Sstevel@tonic-gate 	/*
4147c478bd9Sstevel@tonic-gate 	 * We needed to allocate something to handle this "ioctl", but
4157c478bd9Sstevel@tonic-gate 	 * couldn't; save this "ioctl" and arrange to get called back when
4167c478bd9Sstevel@tonic-gate 	 * it's more likely that we can get what we need.
4177c478bd9Sstevel@tonic-gate 	 * If there's already one being saved, throw it out, since it
4187c478bd9Sstevel@tonic-gate 	 * must have timed out.
4197c478bd9Sstevel@tonic-gate 	 */
4207c478bd9Sstevel@tonic-gate 	if (tmp != NULL)
4217c478bd9Sstevel@tonic-gate 		freemsg(tmp);
4227c478bd9Sstevel@tonic-gate 	return (ioctlrespsize);
4237c478bd9Sstevel@tonic-gate }
4247c478bd9Sstevel@tonic-gate 
425*9a2c4685SToomas Soome #define	NFIELDS	22	/* 18 control characters + 4 sets of modes */
4267c478bd9Sstevel@tonic-gate 
4277c478bd9Sstevel@tonic-gate /*
4287c478bd9Sstevel@tonic-gate  * Init routine run from main at boot time.
4297c478bd9Sstevel@tonic-gate  * Creates a property in the "options" node that is
4307c478bd9Sstevel@tonic-gate  * the default set of termios modes upon driver open.
4317c478bd9Sstevel@tonic-gate  * If the property already existed, then it was
4327c478bd9Sstevel@tonic-gate  * defined in the options.conf file.  In this case we
4337c478bd9Sstevel@tonic-gate  * need to convert this string (stty -g style) to an
4347c478bd9Sstevel@tonic-gate  * actual termios structure and store the new property
4357c478bd9Sstevel@tonic-gate  * value.
4367c478bd9Sstevel@tonic-gate  */
4377c478bd9Sstevel@tonic-gate 
4387c478bd9Sstevel@tonic-gate void
ttyinit()4397c478bd9Sstevel@tonic-gate ttyinit()
4407c478bd9Sstevel@tonic-gate {
4417c478bd9Sstevel@tonic-gate 	dev_info_t *dip;
4427c478bd9Sstevel@tonic-gate 	struct termios new_termios;
4437c478bd9Sstevel@tonic-gate 	struct termios *tp;
4447c478bd9Sstevel@tonic-gate 	char *property = "ttymodes";
4457c478bd9Sstevel@tonic-gate 	char **modesp, *cp;
4467c478bd9Sstevel@tonic-gate 	int i;
4477c478bd9Sstevel@tonic-gate 	uint_t val;
4487c478bd9Sstevel@tonic-gate 	uint_t len;
4497c478bd9Sstevel@tonic-gate 
4507c478bd9Sstevel@tonic-gate 
4517c478bd9Sstevel@tonic-gate 	/*
4527c478bd9Sstevel@tonic-gate 	 * If the termios defaults were NOT set up by the
4537c478bd9Sstevel@tonic-gate 	 * user via the options.conf file, create it using the
4547c478bd9Sstevel@tonic-gate 	 * "sane" set of termios modes.
4557c478bd9Sstevel@tonic-gate 	 * Note that if the property had been created via the
4567c478bd9Sstevel@tonic-gate 	 * options.conf file, it would have been created as
4577c478bd9Sstevel@tonic-gate 	 * a string property.  Since we would like to store
4587c478bd9Sstevel@tonic-gate 	 * a structure (termios) in this property, we need
4597c478bd9Sstevel@tonic-gate 	 * to change the property type to byte array.
4607c478bd9Sstevel@tonic-gate 	 */
4617c478bd9Sstevel@tonic-gate 	if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, ddi_root_node(), 0,
4627c478bd9Sstevel@tonic-gate 	    property, (char ***)&modesp, &len) != DDI_PROP_SUCCESS) {
4637c478bd9Sstevel@tonic-gate 
4647c478bd9Sstevel@tonic-gate 		if ((dip = ddi_find_devinfo("options", -1, 0)) == NULL) {
4657c478bd9Sstevel@tonic-gate 			cmn_err(CE_PANIC,
4667c478bd9Sstevel@tonic-gate 			    "ttyinit: Can't find options node!\n");
4677c478bd9Sstevel@tonic-gate 		}
4687c478bd9Sstevel@tonic-gate 		/*
4697c478bd9Sstevel@tonic-gate 		 * Create the property.
4707c478bd9Sstevel@tonic-gate 		 */
4717c478bd9Sstevel@tonic-gate 		if (ddi_prop_update_byte_array(DDI_DEV_T_NONE, dip,
4727c478bd9Sstevel@tonic-gate 		    property, (uchar_t *)&default_termios,
4737c478bd9Sstevel@tonic-gate 		    sizeof (struct termios)) != DDI_PROP_SUCCESS) {
4747c478bd9Sstevel@tonic-gate 			cmn_err(CE_PANIC, "ttyinit: can't create %s property\n",
4757c478bd9Sstevel@tonic-gate 			    property);
4767c478bd9Sstevel@tonic-gate 		}
4777c478bd9Sstevel@tonic-gate 		return;
4787c478bd9Sstevel@tonic-gate 	}
4797c478bd9Sstevel@tonic-gate 
4807c478bd9Sstevel@tonic-gate 	/*
4817c478bd9Sstevel@tonic-gate 	 * This property was already set in the options.conf
4827c478bd9Sstevel@tonic-gate 	 * file.  We must convert it from a "stty -g" string
4837c478bd9Sstevel@tonic-gate 	 * to an actual termios structure.
4847c478bd9Sstevel@tonic-gate 	 */
4857c478bd9Sstevel@tonic-gate 	bzero(&new_termios, sizeof (struct termios));
4867c478bd9Sstevel@tonic-gate 	tp = &new_termios;
4877c478bd9Sstevel@tonic-gate 	cp = *modesp;
4887c478bd9Sstevel@tonic-gate 	for (i = 0; i < NFIELDS; i++) {
4897c478bd9Sstevel@tonic-gate 		/*
4907c478bd9Sstevel@tonic-gate 		 * Check for bad field/string.
4917c478bd9Sstevel@tonic-gate 		 */
4927c478bd9Sstevel@tonic-gate 		if (termioval(&cp, &val, *modesp+strlen(*modesp)) == -1) {
4937c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN,
4947c478bd9Sstevel@tonic-gate 			    "ttyinit: property '%s' %s\n", property,
4957c478bd9Sstevel@tonic-gate 			    "set incorrectly, using sane value");
4967c478bd9Sstevel@tonic-gate 			tp = &default_termios;
4977c478bd9Sstevel@tonic-gate 			break;
4987c478bd9Sstevel@tonic-gate 		}
4997c478bd9Sstevel@tonic-gate 		switch (i) {
5007c478bd9Sstevel@tonic-gate 		case 0:
5017c478bd9Sstevel@tonic-gate 			new_termios.c_iflag = (tcflag_t)val;
5027c478bd9Sstevel@tonic-gate 			break;
5037c478bd9Sstevel@tonic-gate 		case 1:
5047c478bd9Sstevel@tonic-gate 			new_termios.c_oflag = (tcflag_t)val;
5057c478bd9Sstevel@tonic-gate 			break;
5067c478bd9Sstevel@tonic-gate 		case 2:
5077c478bd9Sstevel@tonic-gate 			new_termios.c_cflag = (tcflag_t)val;
5087c478bd9Sstevel@tonic-gate 			break;
5097c478bd9Sstevel@tonic-gate 		case 3:
5107c478bd9Sstevel@tonic-gate 			new_termios.c_lflag = (tcflag_t)val;
5117c478bd9Sstevel@tonic-gate 			break;
5127c478bd9Sstevel@tonic-gate 		default:
5137c478bd9Sstevel@tonic-gate 			new_termios.c_cc[i - 4] = (cc_t)val;
5147c478bd9Sstevel@tonic-gate 		}
5157c478bd9Sstevel@tonic-gate 	}
5167c478bd9Sstevel@tonic-gate 	if ((dip = ddi_find_devinfo("options", -1, 0)) == NULL) {
5177c478bd9Sstevel@tonic-gate 		cmn_err(CE_PANIC, "ttyinit: Can't find options node!\n");
5187c478bd9Sstevel@tonic-gate 	}
5197c478bd9Sstevel@tonic-gate 
5207c478bd9Sstevel@tonic-gate 	/*
5217c478bd9Sstevel@tonic-gate 	 * We need to create ttymode property as a byte array
5227c478bd9Sstevel@tonic-gate 	 * since it will be interpreted as a termios struct.
5237c478bd9Sstevel@tonic-gate 	 * The property was created as a string by default.
5247c478bd9Sstevel@tonic-gate 	 * So remove the old property and add the new one -
5257c478bd9Sstevel@tonic-gate 	 * otherwise we end up with two ttymodes properties.
5267c478bd9Sstevel@tonic-gate 	 */
5277c478bd9Sstevel@tonic-gate 	if (e_ddi_prop_remove(DDI_DEV_T_NONE, dip, property)
5287c478bd9Sstevel@tonic-gate 	    != DDI_PROP_SUCCESS) {
5297c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "ttyinit: cannot remove '%s' property\n",
5307c478bd9Sstevel@tonic-gate 		    property);
5317c478bd9Sstevel@tonic-gate 	}
5327c478bd9Sstevel@tonic-gate 	/*
5337c478bd9Sstevel@tonic-gate 	 * Store the new defaults.  Since, this property was
5347c478bd9Sstevel@tonic-gate 	 * autoconfig'ed, we must use e_ddi_prop_update_byte_array().
5357c478bd9Sstevel@tonic-gate 	 */
5367c478bd9Sstevel@tonic-gate 	if (e_ddi_prop_update_byte_array(DDI_DEV_T_NONE, dip, property,
5377c478bd9Sstevel@tonic-gate 	    (uchar_t *)tp, sizeof (struct termios)) != DDI_PROP_SUCCESS) {
5387c478bd9Sstevel@tonic-gate 		cmn_err(CE_PANIC, "ttyinit: cannot modify '%s' property\n",
5397c478bd9Sstevel@tonic-gate 		    property);
5407c478bd9Sstevel@tonic-gate 	}
5417c478bd9Sstevel@tonic-gate 	ddi_prop_free(modesp);
5427c478bd9Sstevel@tonic-gate }
5437c478bd9Sstevel@tonic-gate 
5447c478bd9Sstevel@tonic-gate /*
5457c478bd9Sstevel@tonic-gate  * Convert hex string representation of termios field
5467c478bd9Sstevel@tonic-gate  * to a uint_t.  Increments string pointer to the next
5477c478bd9Sstevel@tonic-gate  * field, and assigns value. Returns -1 if no more fields
5487c478bd9Sstevel@tonic-gate  * or an error.
5497c478bd9Sstevel@tonic-gate  */
5507c478bd9Sstevel@tonic-gate 
5517c478bd9Sstevel@tonic-gate static int
termioval(char ** sp,uint_t * valp,char * ep)5527c478bd9Sstevel@tonic-gate termioval(char **sp, uint_t *valp, char *ep)
5537c478bd9Sstevel@tonic-gate {
5547c478bd9Sstevel@tonic-gate 	char *s = *sp;
5557c478bd9Sstevel@tonic-gate 	uint_t digit;
5567c478bd9Sstevel@tonic-gate 
5577c478bd9Sstevel@tonic-gate 	if (s == 0)
5587c478bd9Sstevel@tonic-gate 		return (-1);
5597c478bd9Sstevel@tonic-gate 	*valp = 0;
5607c478bd9Sstevel@tonic-gate 	while (s < ep) {
5617c478bd9Sstevel@tonic-gate 		if (*s >= '0' && *s <= '9')
5627c478bd9Sstevel@tonic-gate 			digit = *s++ - '0';
5637c478bd9Sstevel@tonic-gate 		else if (*s >= 'a' && *s <= 'f')
5647c478bd9Sstevel@tonic-gate 			digit = *s++ - 'a' + 10;
5657c478bd9Sstevel@tonic-gate 		else if (*s >= 'A' && *s <= 'F')
5667c478bd9Sstevel@tonic-gate 			digit = *s++ - 'A' + 10;
5677c478bd9Sstevel@tonic-gate 		else if (*s == ':' || *s == '\0')
5687c478bd9Sstevel@tonic-gate 			break;
5697c478bd9Sstevel@tonic-gate 		else
5707c478bd9Sstevel@tonic-gate 			return (-1);
5717c478bd9Sstevel@tonic-gate 		*valp = (*valp * 16) + digit;
5727c478bd9Sstevel@tonic-gate 	}
5737c478bd9Sstevel@tonic-gate 	/*
5747c478bd9Sstevel@tonic-gate 	 * Null string or empty field.
5757c478bd9Sstevel@tonic-gate 	 */
5767c478bd9Sstevel@tonic-gate 	if (s == *sp)
5777c478bd9Sstevel@tonic-gate 		return (-1);
5787c478bd9Sstevel@tonic-gate 
5797c478bd9Sstevel@tonic-gate 	if (s < ep && *s == ':')
5807c478bd9Sstevel@tonic-gate 		s++;
5817c478bd9Sstevel@tonic-gate 
5827c478bd9Sstevel@tonic-gate 	*sp = s;
5837c478bd9Sstevel@tonic-gate 	return (0);
5847c478bd9Sstevel@tonic-gate }
585