#ifndef lint static char sccsid[] = "@(#)tty.c 1.1 94/10/31 SMI"; #endif /* * Copyright (c) 1990 by Sun Microsystems, Inc. */ #include #include #include #include #include #include #include #include #include void ttycommon_close(tc) register tty_common_t *tc; { tc->t_flags &= ~TS_XCLUDE; tc->t_readq = NULL; tc->t_writeq = NULL; if (tc->t_iocpending != NULL) { /* * We were holding an "ioctl" response pending the * availability of an "mblk" to hold data to be passed up; * another "ioctl" came through, which means that "ioctl" * must have timed out or been aborted. */ freemsg(tc->t_iocpending); tc->t_iocpending = NULL; } } /* * A "line discipline" module's queue is full. * Check whether IMAXBEL is set; if so, output a ^G, otherwise send an M_FLUSH * upstream flushing all the read queues. */ void ttycommon_qfull(tc, q) register tty_common_t *tc; queue_t *q; { register mblk_t *mp; if (tc->t_iflag & IMAXBEL) { if (canput(WR(q)) && (mp = allocb(1, BPRI_HI)) != NULL) { *mp->b_wptr++ = _CTRL(g); (*WR(q)->q_qinfo->qi_putp)(WR(q), mp); } } else { flushq(q, FLUSHDATA); (void) putctl1(q->q_next, M_FLUSH, FLUSHR); } } /* * Process an "ioctl" message sent down to us, and return a reply message, * even if we don't understand the "ioctl". Our client may want to use * that reply message for its own purposes if we don't understand it but * they do, and may want to modify it if we both understand it but they * understand it better than we do. * If the "ioctl" reply requires additional data to be passed up to the * caller, and we cannot allocate an mblk to hold the data, we return the * amount of data to be sent, so that our caller can do a "bufcall" and try * again later; otherwise, we return 0. */ unsigned ttycommon_ioctl(tc, q, mp, errorp) register tty_common_t *tc; queue_t *q; register mblk_t *mp; int *errorp; { register struct iocblk *iocp; int ioctlrespsize; int s; *errorp = 0; /* no error detected yet */ iocp = (struct iocblk *)mp->b_rptr; switch (iocp->ioc_cmd) { case TCSETSF: /* * Flush the driver's queue, and send an M_FLUSH upstream * to flush everybody above us. */ s = spltty(); /* XXX - splzs? */ flushq(RD(q), FLUSHDATA); (void) putctl1(RD(q)->q_next, M_FLUSH, FLUSHR); (void) splx(s); case TCSETSW: case TCSETS: { register struct termios *cb = (struct termios *)mp->b_cont->b_rptr; /* * The only information we look at are the iflag word, * the cflag word, and the start and stop characters. */ tc->t_iflag = cb->c_iflag; tc->t_cflag = cb->c_cflag; tc->t_stopc = cb->c_cc[VSTOP]; tc->t_startc = cb->c_cc[VSTART]; break; } case TCSETAF: s = spltty(); /* XXX - splzs? */ /* * Flush the driver's queue, and send an M_FLUSH upstream * to flush everybody above us. */ flushq(RD(q), FLUSHDATA); (void) putctl1(RD(q)->q_next, M_FLUSH, FLUSHR); (void) splx(s); case TCSETAW: case TCSETA: { register struct termio *cb = (struct termio *)mp->b_cont->b_rptr; /* * The only information we look at are the iflag word * and the cflag word. * Don't clear out the unset portions, leave them as * they are. */ tc->t_iflag = (tc->t_iflag & 0xffff0000 | cb->c_iflag); tc->t_cflag = (tc->t_cflag & 0xffff0000 | cb->c_cflag); break; } case TIOCSWINSZ: { register struct winsize *ws = (struct winsize *)mp->b_cont->b_rptr; /* * If the window size changed, send a SIGWINCH. */ if (bcmp((caddr_t)&tc->t_size, (caddr_t)ws, sizeof (struct winsize))) { tc->t_size = *ws; (void) putctl1(RD(q)->q_next, M_PCSIG, SIGWINCH); } break; } case TIOCSSIZE: /* * Set the window size, but don't send a SIGWINCH. */ tc->t_size.ws_row = ((struct ttysize *)mp->b_cont->b_rptr)->ts_lines; tc->t_size.ws_col = ((struct ttysize *)mp->b_cont->b_rptr)->ts_cols; tc->t_size.ws_xpixel = 0; tc->t_size.ws_ypixel = 0; break; /* * Prevent more opens. */ case TIOCEXCL: tc->t_flags |= TS_XCLUDE; break; /* * Permit more opens. */ case TIOCNXCL: tc->t_flags &= ~TS_XCLUDE; break; /* * Set or clear the "soft carrier" flag. */ case TIOCSSOFTCAR: if (*(int *)mp->b_cont->b_rptr) tc->t_flags |= TS_SOFTCAR; else tc->t_flags &= ~TS_SOFTCAR; break; /* * The permission checking has already been done at the stream * head, since it has to be done in the context of the process * doing the call. */ case TIOCSTI: { register mblk_t *bp; /* * Simulate typing of a character at the terminal. */ if ((bp = allocb(1, BPRI_MED)) != NULL) { if (!canput(tc->t_readq->q_next)) freemsg(bp); else { *bp->b_wptr++ = *mp->b_cont->b_rptr; putnext(tc->t_readq, bp); } } break; } } /* * Turn the ioctl message into an ioctl ACK message. */ iocp->ioc_count = 0; /* no data returned unless we say so */ mp->b_datap->db_type = M_IOCACK; switch (iocp->ioc_cmd) { case TCSETSF: case TCSETSW: case TCSETS: case TCSETAF: case TCSETAW: case TCSETA: case TIOCSWINSZ: case TIOCSSIZE: case TIOCEXCL: case TIOCNXCL: case TIOCSSOFTCAR: case TIOCSTI: /* * We've done all the important work on these already; * just reply with an ACK. */ break; case TCGETS: { register struct termios *cb; register mblk_t *datap; if ((datap = allocb(sizeof (struct termios), BPRI_HI)) == NULL) { ioctlrespsize = sizeof (struct termios); goto allocfailure; } cb = (struct termios *)datap->b_wptr; /* * The only information we supply is the cflag word. * Our copy of the iflag word is just that, a copy. */ bzero((caddr_t) cb, sizeof (struct termios)); cb->c_cflag = tc->t_cflag; datap->b_wptr += (sizeof (*cb)) / (sizeof (*datap->b_wptr)); iocp->ioc_count = sizeof (struct termios); if (mp->b_cont != NULL) freemsg(mp->b_cont); mp->b_cont = datap; break; } case TCGETA: { register struct termio *cb; register mblk_t *datap; if ((datap = allocb(sizeof (struct termio), BPRI_HI)) == NULL) { ioctlrespsize = sizeof (struct termio); goto allocfailure; } cb = (struct termio *)datap->b_wptr; /* * The only information we supply is the cflag word. * Our copy of the iflag word is just that, a copy. */ bzero((caddr_t) cb, sizeof (struct termio)); cb->c_cflag = tc->t_cflag; datap->b_wptr += (sizeof (*cb)) / (sizeof (*datap->b_wptr)); iocp->ioc_count = sizeof (struct termio); if (mp->b_cont != NULL) freemsg(mp->b_cont); mp->b_cont = datap; break; } /* * Get the "soft carrier" flag. */ case TIOCGSOFTCAR: { register mblk_t *datap; if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL) { ioctlrespsize = sizeof (int); goto allocfailure; } if (tc->t_flags & TS_SOFTCAR) *(int *)datap->b_wptr = 1; else *(int *)datap->b_wptr = 0; datap->b_wptr += (sizeof (int)) / (sizeof (*datap->b_wptr)); iocp->ioc_count = sizeof (int); if (mp->b_cont != NULL) freemsg(mp->b_cont); mp->b_cont = datap; break; } case TIOCGWINSZ: { register mblk_t *datap; if ((datap = allocb(sizeof (struct winsize), BPRI_HI)) == NULL) { ioctlrespsize = sizeof (struct winsize); goto allocfailure; } /* * Return the current size. */ *(struct winsize *)datap->b_wptr = tc->t_size; datap->b_wptr += (sizeof (struct winsize)) / (sizeof (*datap->b_wptr)); iocp->ioc_count = sizeof (struct winsize); if (mp->b_cont != NULL) freemsg(mp->b_cont); mp->b_cont = datap; break; } case TIOCISPACE: { register mblk_t *datap; if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL) { ioctlrespsize = sizeof (int); goto allocfailure; } /* * Return the current space in the input queue. */ *(int *)datap->b_wptr = qspace(RD(q)); datap->b_wptr += (sizeof (int)) / (sizeof (*datap->b_wptr)); iocp->ioc_count = sizeof (int); if (mp->b_cont != NULL) freemsg(mp->b_cont); mp->b_cont = datap; break; } case TIOCISIZE: { register mblk_t *datap; if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL) { ioctlrespsize = sizeof (int); goto allocfailure; } /* * Return the size of the input queue. */ *(int *)datap->b_wptr = qhiwat(RD(q)); datap->b_wptr += (sizeof (int)) / (sizeof (*datap->b_wptr)); iocp->ioc_count = sizeof (int); if (mp->b_cont != NULL) freemsg(mp->b_cont); mp->b_cont = datap; break; } case TIOCMGET: { register mblk_t *datap; if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL) { ioctlrespsize = sizeof (int); goto allocfailure; } *(int *)datap->b_wptr = 0; /* reserve space */ datap->b_wptr += (sizeof (int)/sizeof (*datap->b_wptr)); iocp->ioc_count = sizeof (int); if (mp->b_cont != NULL) freemsg(mp->b_cont); mp->b_cont = datap; *errorp = -1; /* we can't do it all */ break; } case TIOCOUTQ: { register mblk_t *datap; register mblk_t *bp; int count = 0; /* * XXX - doesn't handle messages not yet being DMAed * if they've been removed from the queue. Also doesn't * handle some queue above us being full, but that's * life (that shouldn't happen in normal operation, * since none of the standard tty modules have service * procedures, and if you have a non-standard tty * module you shouldn't be using this "ioctl" anyway). */ if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL) { ioctlrespsize = sizeof (int); goto allocfailure; } s=spltty(); for (bp = q->q_first; bp != NULL; bp = bp->b_next) count += msgdsize(bp); (void)splx(s); *(int *)datap->b_wptr = count; datap->b_wptr += (sizeof (int)) / (sizeof (*datap->b_wptr)); iocp->ioc_count = sizeof (int); if (mp->b_cont != NULL) freemsg(mp->b_cont); mp->b_cont = datap; break; } default: *errorp = -1; /* we don't understand it, maybe they do */ break; } return (0); allocfailure: /* * We needed to allocate something to handle this "ioctl", but * couldn't; save this "ioctl" and arrange to get called back when * it's more likely that we can get what we need. * If there's already one being saved, throw it out, since it * must have timed out. */ s = splstr(); if (tc->t_iocpending != NULL) freemsg(tc->t_iocpending); tc->t_iocpending = mp; /* hold this ioctl */ (void) splx(s); return (ioctlrespsize); }