Index: head/sys/kern/tty.c =================================================================== --- head/sys/kern/tty.c (revision 131129) +++ head/sys/kern/tty.c (revision 131130) @@ -1,2926 +1,2935 @@ /*- * Copyright (c) 1982, 1986, 1990, 1991, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Copyright (c) 2002 Networks Associates Technologies, Inc. * All rights reserved. * * Portions of this software were developed for the FreeBSD Project by * ThinkSec AS and NAI Labs, the Security Research Division of Network * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 * ("CBOSS"), as part of the DARPA CHATS research program. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)tty.c 8.8 (Berkeley) 1/21/94 */ /*- * TODO: * o Fix races for sending the start char in ttyflush(). * o Handle inter-byte timeout for "MIN > 0, TIME > 0" in ttyselect(). * With luck, there will be MIN chars before select() returns(). * o Handle CLOCAL consistently for ptys. Perhaps disallow setting it. * o Don't allow input in TS_ZOMBIE case. It would be visible through * FIONREAD. * o Do the new sio locking stuff here and use it to avoid special * case for EXTPROC? * o Lock PENDIN too? * o Move EXTPROC and/or PENDIN to t_state? * o Wrap most of ttioctl in spltty/splx. * o Implement TIOCNOTTY or remove it from . * o Send STOP if IXOFF is toggled off while TS_TBLOCK is set. * o Don't allow certain termios flags to affect disciplines other * than TTYDISC. Cancel their effects before switch disciplines * and ignore them if they are set while we are in another * discipline. * o Now that historical speed conversions are handled here, don't * do them in drivers. * o Check for TS_CARR_ON being set while everything is closed and not * waiting for carrier. TS_CARR_ON isn't cleared if nothing is open, * so it would live until the next open even if carrier drops. * o Restore TS_WOPEN since it is useful in pstat. It must be cleared * only when _all_ openers leave open(). */ #include __FBSDID("$FreeBSD$"); #include "opt_compat.h" #include "opt_tty.h" #include #include #include #include #include #include #include #ifndef BURN_BRIDGES #if defined(COMPAT_43) #include #endif #endif #include #define TTYDEFCHARS #include #undef TTYDEFCHARS #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MALLOC_DEFINE(M_TTYS, "ttys", "tty data structures"); long tk_cancc; long tk_nin; long tk_nout; long tk_rawcc; static int proc_compare(struct proc *p1, struct proc *p2); static int ttnread(struct tty *tp); static void ttyecho(int c, struct tty *tp); static int ttyoutput(int c, struct tty *tp); static void ttypend(struct tty *tp); static void ttyretype(struct tty *tp); static void ttyrub(int c, struct tty *tp); static void ttyrubo(struct tty *tp, int cnt); static void ttyunblock(struct tty *tp); static int ttywflush(struct tty *tp); static int filt_ttyread(struct knote *kn, long hint); static void filt_ttyrdetach(struct knote *kn); static int filt_ttywrite(struct knote *kn, long hint); static void filt_ttywdetach(struct knote *kn); /* * Table with character classes and parity. The 8th bit indicates parity, * the 7th bit indicates the character is an alphameric or underscore (for * ALTWERASE), and the low 6 bits indicate delay type. If the low 6 bits * are 0 then the character needs no special processing on output; classes * other than 0 might be translated or (not currently) require delays. */ #define E 0x00 /* Even parity. */ #define O 0x80 /* Odd parity. */ #define PARITY(c) (char_type[c] & O) #define ALPHA 0x40 /* Alpha or underscore. */ #define ISALPHA(c) (char_type[(c) & TTY_CHARMASK] & ALPHA) #define CCLASSMASK 0x3f #define CCLASS(c) (char_type[c] & CCLASSMASK) #define BS BACKSPACE #define CC CONTROL #define CR RETURN #define NA ORDINARY | ALPHA #define NL NEWLINE #define NO ORDINARY #define TB TAB #define VT VTAB static u_char const char_type[] = { E|CC, O|CC, O|CC, E|CC, O|CC, E|CC, E|CC, O|CC, /* nul - bel */ O|BS, E|TB, E|NL, O|CC, E|VT, O|CR, O|CC, E|CC, /* bs - si */ O|CC, E|CC, E|CC, O|CC, E|CC, O|CC, O|CC, E|CC, /* dle - etb */ E|CC, O|CC, O|CC, E|CC, O|CC, E|CC, E|CC, O|CC, /* can - us */ O|NO, E|NO, E|NO, O|NO, E|NO, O|NO, O|NO, E|NO, /* sp - ' */ E|NO, O|NO, O|NO, E|NO, O|NO, E|NO, E|NO, O|NO, /* ( - / */ E|NA, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* 0 - 7 */ O|NA, E|NA, E|NO, O|NO, E|NO, O|NO, O|NO, E|NO, /* 8 - ? */ O|NO, E|NA, E|NA, O|NA, E|NA, O|NA, O|NA, E|NA, /* @ - G */ E|NA, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* H - O */ E|NA, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* P - W */ O|NA, E|NA, E|NA, O|NO, E|NO, O|NO, O|NO, O|NA, /* X - _ */ E|NO, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* ` - g */ O|NA, E|NA, E|NA, O|NA, E|NA, O|NA, O|NA, E|NA, /* h - o */ O|NA, E|NA, E|NA, O|NA, E|NA, O|NA, O|NA, E|NA, /* p - w */ E|NA, O|NA, O|NA, E|NO, O|NO, E|NO, E|NO, O|CC, /* x - del */ /* * Meta chars; should be settable per character set; * for now, treat them all as normal characters. */ NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, }; #undef BS #undef CC #undef CR #undef NA #undef NL #undef NO #undef TB #undef VT /* Macros to clear/set/test flags. */ #define SET(t, f) (t) |= (f) #define CLR(t, f) (t) &= ~(f) #define ISSET(t, f) ((t) & (f)) #undef MAX_INPUT /* XXX wrong in */ #define MAX_INPUT TTYHOG /* XXX limit is usually larger for !ICANON */ /* * list of struct tty where pstat(8) can pick it up with sysctl * * The lock order is to grab the list mutex before the tty mutex. * Together with additions going on the tail of the list, this allows * the sysctl to avoid doing retries. */ static TAILQ_HEAD(, tty) tty_list = TAILQ_HEAD_INITIALIZER(tty_list); static struct mtx tty_list_mutex; static int drainwait = 5*60; SYSCTL_INT(_kern, OID_AUTO, drainwait, CTLFLAG_RW, &drainwait, 0, "Output drain timeout in seconds"); /* * Initial open of tty, or (re)entry to standard tty line discipline. */ int ttyopen(struct cdev *device, struct tty *tp) { int s; s = spltty(); tp->t_dev = device; + tp->t_hotchar = 0; if (!ISSET(tp->t_state, TS_ISOPEN)) { ttyref(tp); SET(tp->t_state, TS_ISOPEN); if (ISSET(tp->t_cflag, CLOCAL)) SET(tp->t_state, TS_CONNECTED); bzero(&tp->t_winsize, sizeof(tp->t_winsize)); } /* XXX don't hang forever on output */ if (tp->t_timeout < 0) tp->t_timeout = drainwait*hz; ttsetwater(tp); splx(s); return (0); } /* * Handle close() on a tty line: flush and set to initial state, * bumping generation number so that pending read/write calls * can detect recycling of the tty. * XXX our caller should have done `spltty(); l_close(); ttyclose();' * and l_close() should have flushed, but we repeat the spltty() and * the flush in case there are buggy callers. */ int ttyclose(struct tty *tp) { int s; funsetown(&tp->t_sigio); s = spltty(); if (constty == tp) constty_clear(); ttyflush(tp, FREAD | FWRITE); clist_free_cblocks(&tp->t_canq); clist_free_cblocks(&tp->t_outq); clist_free_cblocks(&tp->t_rawq); tp->t_gen++; tp->t_line = TTYDISC; + tp->t_hotchar = 0; tp->t_pgrp = NULL; tp->t_session = NULL; tp->t_state = 0; ttyrel(tp); splx(s); return (0); } #define FLUSHQ(q) { \ if ((q)->c_cc) \ ndflush(q, (q)->c_cc); \ } /* Is 'c' a line delimiter ("break" character)? */ #define TTBREAKC(c, lflag) \ ((c) == '\n' || (((c) == cc[VEOF] || \ (c) == cc[VEOL] || ((c) == cc[VEOL2] && lflag & IEXTEN)) && \ (c) != _POSIX_VDISABLE)) /* * Process input of a single character received on a tty. */ int ttyinput(int c, struct tty *tp) { tcflag_t iflag, lflag; cc_t *cc; int i, err; /* * If input is pending take it first. */ lflag = tp->t_lflag; if (ISSET(lflag, PENDIN)) ttypend(tp); /* * Gather stats. */ if (ISSET(lflag, ICANON)) { ++tk_cancc; ++tp->t_cancc; } else { ++tk_rawcc; ++tp->t_rawcc; } ++tk_nin; /* * Block further input iff: * current input > threshold AND input is available to user program * AND input flow control is enabled and not yet invoked. * The 3 is slop for PARMRK. */ iflag = tp->t_iflag; if (tp->t_rawq.c_cc + tp->t_canq.c_cc > tp->t_ihiwat - 3 && (!ISSET(lflag, ICANON) || tp->t_canq.c_cc != 0) && (ISSET(tp->t_cflag, CRTS_IFLOW) || ISSET(iflag, IXOFF)) && !ISSET(tp->t_state, TS_TBLOCK)) ttyblock(tp); /* Handle exceptional conditions (break, parity, framing). */ cc = tp->t_cc; err = (ISSET(c, TTY_ERRORMASK)); if (err) { CLR(c, TTY_ERRORMASK); if (ISSET(err, TTY_BI)) { if (ISSET(iflag, IGNBRK)) return (0); if (ISSET(iflag, BRKINT)) { ttyflush(tp, FREAD | FWRITE); if (tp->t_pgrp != NULL) { PGRP_LOCK(tp->t_pgrp); pgsignal(tp->t_pgrp, SIGINT, 1); PGRP_UNLOCK(tp->t_pgrp); } goto endcase; } if (ISSET(iflag, PARMRK)) goto parmrk; } else if ((ISSET(err, TTY_PE) && ISSET(iflag, INPCK)) || ISSET(err, TTY_FE)) { if (ISSET(iflag, IGNPAR)) return (0); else if (ISSET(iflag, PARMRK)) { parmrk: if (tp->t_rawq.c_cc + tp->t_canq.c_cc > MAX_INPUT - 3) goto input_overflow; (void)putc(0377 | TTY_QUOTE, &tp->t_rawq); (void)putc(0 | TTY_QUOTE, &tp->t_rawq); (void)putc(c | TTY_QUOTE, &tp->t_rawq); goto endcase; } else c = 0; } } if (!ISSET(tp->t_state, TS_TYPEN) && ISSET(iflag, ISTRIP)) CLR(c, 0x80); if (!ISSET(lflag, EXTPROC)) { /* * Check for literal nexting very first */ if (ISSET(tp->t_state, TS_LNCH)) { SET(c, TTY_QUOTE); CLR(tp->t_state, TS_LNCH); } /* * Scan for special characters. This code * is really just a big case statement with * non-constant cases. The bottom of the * case statement is labeled ``endcase'', so goto * it after a case match, or similar. */ /* * Control chars which aren't controlled * by ICANON, ISIG, or IXON. */ if (ISSET(lflag, IEXTEN)) { if (CCEQ(cc[VLNEXT], c)) { if (ISSET(lflag, ECHO)) { if (ISSET(lflag, ECHOE)) { (void)ttyoutput('^', tp); (void)ttyoutput('\b', tp); } else ttyecho(c, tp); } SET(tp->t_state, TS_LNCH); goto endcase; } if (CCEQ(cc[VDISCARD], c)) { if (ISSET(lflag, FLUSHO)) CLR(tp->t_lflag, FLUSHO); else { ttyflush(tp, FWRITE); ttyecho(c, tp); if (tp->t_rawq.c_cc + tp->t_canq.c_cc) ttyretype(tp); SET(tp->t_lflag, FLUSHO); } goto startoutput; } } /* * Signals. */ if (ISSET(lflag, ISIG)) { if (CCEQ(cc[VINTR], c) || CCEQ(cc[VQUIT], c)) { if (!ISSET(lflag, NOFLSH)) ttyflush(tp, FREAD | FWRITE); ttyecho(c, tp); if (tp->t_pgrp != NULL) { PGRP_LOCK(tp->t_pgrp); pgsignal(tp->t_pgrp, CCEQ(cc[VINTR], c) ? SIGINT : SIGQUIT, 1); PGRP_UNLOCK(tp->t_pgrp); } goto endcase; } if (CCEQ(cc[VSUSP], c)) { if (!ISSET(lflag, NOFLSH)) ttyflush(tp, FREAD); ttyecho(c, tp); if (tp->t_pgrp != NULL) { PGRP_LOCK(tp->t_pgrp); pgsignal(tp->t_pgrp, SIGTSTP, 1); PGRP_UNLOCK(tp->t_pgrp); } goto endcase; } } /* * Handle start/stop characters. */ if (ISSET(iflag, IXON)) { if (CCEQ(cc[VSTOP], c)) { if (!ISSET(tp->t_state, TS_TTSTOP)) { SET(tp->t_state, TS_TTSTOP); (*tp->t_stop)(tp, 0); return (0); } if (!CCEQ(cc[VSTART], c)) return (0); /* * if VSTART == VSTOP then toggle */ goto endcase; } if (CCEQ(cc[VSTART], c)) goto restartoutput; } /* * IGNCR, ICRNL, & INLCR */ if (c == '\r') { if (ISSET(iflag, IGNCR)) return (0); else if (ISSET(iflag, ICRNL)) c = '\n'; } else if (c == '\n' && ISSET(iflag, INLCR)) c = '\r'; } if (!ISSET(tp->t_lflag, EXTPROC) && ISSET(lflag, ICANON)) { /* * From here on down canonical mode character * processing takes place. */ /* * erase or erase2 (^H / ^?) */ if (CCEQ(cc[VERASE], c) || CCEQ(cc[VERASE2], c) ) { if (tp->t_rawq.c_cc) ttyrub(unputc(&tp->t_rawq), tp); goto endcase; } /* * kill (^U) */ if (CCEQ(cc[VKILL], c)) { if (ISSET(lflag, ECHOKE) && tp->t_rawq.c_cc == tp->t_rocount && !ISSET(lflag, ECHOPRT)) while (tp->t_rawq.c_cc) ttyrub(unputc(&tp->t_rawq), tp); else { ttyecho(c, tp); if (ISSET(lflag, ECHOK) || ISSET(lflag, ECHOKE)) ttyecho('\n', tp); FLUSHQ(&tp->t_rawq); tp->t_rocount = 0; } CLR(tp->t_state, TS_LOCAL); goto endcase; } /* * word erase (^W) */ if (CCEQ(cc[VWERASE], c) && ISSET(lflag, IEXTEN)) { int ctype; /* * erase whitespace */ while ((c = unputc(&tp->t_rawq)) == ' ' || c == '\t') ttyrub(c, tp); if (c == -1) goto endcase; /* * erase last char of word and remember the * next chars type (for ALTWERASE) */ ttyrub(c, tp); c = unputc(&tp->t_rawq); if (c == -1) goto endcase; if (c == ' ' || c == '\t') { (void)putc(c, &tp->t_rawq); goto endcase; } ctype = ISALPHA(c); /* * erase rest of word */ do { ttyrub(c, tp); c = unputc(&tp->t_rawq); if (c == -1) goto endcase; } while (c != ' ' && c != '\t' && (!ISSET(lflag, ALTWERASE) || ISALPHA(c) == ctype)); (void)putc(c, &tp->t_rawq); goto endcase; } /* * reprint line (^R) */ if (CCEQ(cc[VREPRINT], c) && ISSET(lflag, IEXTEN)) { ttyretype(tp); goto endcase; } /* * ^T - kernel info and generate SIGINFO */ if (CCEQ(cc[VSTATUS], c) && ISSET(lflag, IEXTEN)) { if (ISSET(lflag, ISIG) && tp->t_pgrp != NULL) { PGRP_LOCK(tp->t_pgrp); pgsignal(tp->t_pgrp, SIGINFO, 1); PGRP_UNLOCK(tp->t_pgrp); } if (!ISSET(lflag, NOKERNINFO)) ttyinfo(tp); goto endcase; } } /* * Check for input buffer overflow */ if (tp->t_rawq.c_cc + tp->t_canq.c_cc >= MAX_INPUT) { input_overflow: if (ISSET(iflag, IMAXBEL)) { if (tp->t_outq.c_cc < tp->t_ohiwat) (void)ttyoutput(CTRL('g'), tp); } goto endcase; } if ( c == 0377 && ISSET(iflag, PARMRK) && !ISSET(iflag, ISTRIP) && ISSET(iflag, IGNBRK|IGNPAR) != (IGNBRK|IGNPAR)) (void)putc(0377 | TTY_QUOTE, &tp->t_rawq); /* * Put data char in q for user and * wakeup on seeing a line delimiter. */ if (putc(c, &tp->t_rawq) >= 0) { if (!ISSET(lflag, ICANON)) { ttwakeup(tp); ttyecho(c, tp); goto endcase; } if (TTBREAKC(c, lflag)) { tp->t_rocount = 0; catq(&tp->t_rawq, &tp->t_canq); ttwakeup(tp); } else if (tp->t_rocount++ == 0) tp->t_rocol = tp->t_column; if (ISSET(tp->t_state, TS_ERASE)) { /* * end of prterase \.../ */ CLR(tp->t_state, TS_ERASE); (void)ttyoutput('/', tp); } i = tp->t_column; ttyecho(c, tp); if (CCEQ(cc[VEOF], c) && ISSET(lflag, ECHO)) { /* * Place the cursor over the '^' of the ^D. */ i = imin(2, tp->t_column - i); while (i > 0) { (void)ttyoutput('\b', tp); i--; } } } endcase: /* * IXANY means allow any character to restart output. */ if (ISSET(tp->t_state, TS_TTSTOP) && !ISSET(iflag, IXANY) && cc[VSTART] != cc[VSTOP]) return (0); restartoutput: CLR(tp->t_lflag, FLUSHO); CLR(tp->t_state, TS_TTSTOP); startoutput: return (ttstart(tp)); } /* * Output a single character on a tty, doing output processing * as needed (expanding tabs, newline processing, etc.). * Returns < 0 if succeeds, otherwise returns char to resend. * Must be recursive. */ static int ttyoutput(int c, struct tty *tp) { tcflag_t oflag; int col, s; oflag = tp->t_oflag; if (!ISSET(oflag, OPOST)) { if (ISSET(tp->t_lflag, FLUSHO)) return (-1); if (putc(c, &tp->t_outq)) return (c); tk_nout++; tp->t_outcc++; return (-1); } /* * Do tab expansion if OXTABS is set. Special case if we external * processing, we don't do the tab expansion because we'll probably * get it wrong. If tab expansion needs to be done, let it happen * externally. */ CLR(c, ~TTY_CHARMASK); if (c == '\t' && ISSET(oflag, OXTABS) && !ISSET(tp->t_lflag, EXTPROC)) { c = 8 - (tp->t_column & 7); if (!ISSET(tp->t_lflag, FLUSHO)) { s = spltty(); /* Don't interrupt tabs. */ c -= b_to_q(" ", c, &tp->t_outq); tk_nout += c; tp->t_outcc += c; splx(s); } tp->t_column += c; return (c ? -1 : '\t'); } if (c == CEOT && ISSET(oflag, ONOEOT)) return (-1); /* * Newline translation: if ONLCR is set, * translate newline into "\r\n". */ if (c == '\n' && ISSET(tp->t_oflag, ONLCR)) { tk_nout++; tp->t_outcc++; if (!ISSET(tp->t_lflag, FLUSHO) && putc('\r', &tp->t_outq)) return (c); } /* If OCRNL is set, translate "\r" into "\n". */ else if (c == '\r' && ISSET(tp->t_oflag, OCRNL)) c = '\n'; /* If ONOCR is set, don't transmit CRs when on column 0. */ else if (c == '\r' && ISSET(tp->t_oflag, ONOCR) && tp->t_column == 0) return (-1); tk_nout++; tp->t_outcc++; if (!ISSET(tp->t_lflag, FLUSHO) && putc(c, &tp->t_outq)) return (c); col = tp->t_column; switch (CCLASS(c)) { case BACKSPACE: if (col > 0) --col; break; case CONTROL: break; case NEWLINE: if (ISSET(tp->t_oflag, ONLCR | ONLRET)) col = 0; break; case RETURN: col = 0; break; case ORDINARY: ++col; break; case TAB: col = (col + 8) & ~7; break; } tp->t_column = col; return (-1); } /* * Ioctls for all tty devices. Called after line-discipline specific ioctl * has been called to do discipline-specific functions and/or reject any * of these ioctl commands. */ /* ARGSUSED */ int ttioctl(struct tty *tp, u_long cmd, void *data, int flag) { struct proc *p; struct thread *td; struct pgrp *pgrp; int s, error, bits, sig, sig2; td = curthread; /* XXX */ p = td->td_proc; /* If the ioctl involves modification, hang if in the background. */ switch (cmd) { case TIOCCBRK: case TIOCCONS: case TIOCDRAIN: case TIOCEXCL: case TIOCFLUSH: #ifdef TIOCHPCL case TIOCHPCL: #endif case TIOCNXCL: case TIOCSBRK: case TIOCSCTTY: case TIOCSDRAINWAIT: case TIOCSETA: case TIOCSETAF: case TIOCSETAW: case TIOCSETD: case TIOCSPGRP: case TIOCSTART: case TIOCSTAT: case TIOCSTI: case TIOCSTOP: case TIOCSWINSZ: #ifndef BURN_BRIDGES #if defined(COMPAT_43) case TIOCLBIC: case TIOCLBIS: case TIOCLSET: case TIOCSETC: case OTIOCSETD: case TIOCSETN: case TIOCSETP: case TIOCSLTC: #endif #endif sx_slock(&proctree_lock); PROC_LOCK(p); while (isbackground(p, tp) && !(p->p_flag & P_PPWAIT) && !SIGISMEMBER(p->p_sigacts->ps_sigignore, SIGTTOU) && !SIGISMEMBER(td->td_sigmask, SIGTTOU)) { pgrp = p->p_pgrp; PROC_UNLOCK(p); if (pgrp->pg_jobc == 0) { sx_sunlock(&proctree_lock); return (EIO); } PGRP_LOCK(pgrp); sx_sunlock(&proctree_lock); pgsignal(pgrp, SIGTTOU, 1); PGRP_UNLOCK(pgrp); error = ttysleep(tp, &lbolt, TTOPRI | PCATCH, "ttybg1", 0); if (error) return (error); sx_slock(&proctree_lock); PROC_LOCK(p); } PROC_UNLOCK(p); sx_sunlock(&proctree_lock); break; } if (tp->t_break != NULL) { switch (cmd) { case TIOCSBRK: tp->t_break(tp, 1); return (0); case TIOCCBRK: tp->t_break(tp, 0); return (0); default: break; } } if (tp->t_modem != NULL) { switch (cmd) { case TIOCSDTR: tp->t_modem(tp, SER_DTR, 0); return (0); case TIOCCDTR: tp->t_modem(tp, 0, SER_DTR); return (0); case TIOCMSET: bits = *(int *)data; sig = (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1; sig2 = ((~bits) & (TIOCM_DTR | TIOCM_RTS)) >> 1; tp->t_modem(tp, sig, sig2); return (0); case TIOCMBIS: bits = *(int *)data; sig = (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1; tp->t_modem(tp, sig, 0); return (0); case TIOCMBIC: bits = *(int *)data; sig = (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1; tp->t_modem(tp, 0, sig); return (0); case TIOCMGET: sig = tp->t_modem(tp, 0, 0); /* See t_state, TS_ASYNC); else CLR(tp->t_state, TS_ASYNC); splx(s); break; case FIONBIO: /* set/clear non-blocking i/o */ break; /* XXX: delete. */ case FIONREAD: /* get # bytes to read */ s = spltty(); *(int *)data = ttnread(tp); splx(s); break; case FIOSETOWN: /* * Policy -- Don't allow FIOSETOWN on someone else's * controlling tty */ if (tp->t_session != NULL && !isctty(p, tp)) return (ENOTTY); error = fsetown(*(int *)data, &tp->t_sigio); if (error) return (error); break; case FIOGETOWN: if (tp->t_session != NULL && !isctty(p, tp)) return (ENOTTY); *(int *)data = fgetown(&tp->t_sigio); break; case TIOCEXCL: /* set exclusive use of tty */ s = spltty(); SET(tp->t_state, TS_XCLUDE); splx(s); break; case TIOCFLUSH: { /* flush buffers */ int flags = *(int *)data; if (flags == 0) flags = FREAD | FWRITE; else flags &= FREAD | FWRITE; ttyflush(tp, flags); break; } case TIOCCONS: /* become virtual console */ if (*(int *)data) { struct nameidata nid; if (constty && constty != tp && ISSET(constty->t_state, TS_CONNECTED)) return (EBUSY); /* Ensure user can open the real console. */ NDINIT(&nid, LOOKUP, LOCKLEAF | FOLLOW, UIO_SYSSPACE, "/dev/console", td); if ((error = namei(&nid)) != 0) return (error); NDFREE(&nid, NDF_ONLY_PNBUF); error = VOP_ACCESS(nid.ni_vp, VREAD, td->td_ucred, td); vput(nid.ni_vp); if (error) return (error); constty_set(tp); } else if (tp == constty) constty_clear(); break; case TIOCDRAIN: /* wait till output drained */ error = ttywait(tp); if (error) return (error); break; case TIOCGETA: { /* get termios struct */ struct termios *t = (struct termios *)data; bcopy(&tp->t_termios, t, sizeof(struct termios)); break; } case TIOCGETD: /* get line discipline */ *(int *)data = tp->t_line; break; case TIOCGWINSZ: /* get window size */ *(struct winsize *)data = tp->t_winsize; break; case TIOCGPGRP: /* get pgrp of tty */ if (!isctty(p, tp)) return (ENOTTY); *(int *)data = tp->t_pgrp ? tp->t_pgrp->pg_id : NO_PID; break; #ifdef TIOCHPCL case TIOCHPCL: /* hang up on last close */ s = spltty(); SET(tp->t_cflag, HUPCL); splx(s); break; #endif case TIOCNXCL: /* reset exclusive use of tty */ s = spltty(); CLR(tp->t_state, TS_XCLUDE); splx(s); break; case TIOCOUTQ: /* output queue size */ *(int *)data = tp->t_outq.c_cc; break; case TIOCSETA: /* set termios struct */ case TIOCSETAW: /* drain output, set */ case TIOCSETAF: { /* drn out, fls in, set */ struct termios *t = (struct termios *)data; if (t->c_ispeed == 0) t->c_ispeed = t->c_ospeed; if (t->c_ispeed == 0) t->c_ispeed = tp->t_ospeed; if (t->c_ispeed == 0) return (EINVAL); s = spltty(); if (cmd == TIOCSETAW || cmd == TIOCSETAF) { error = ttywait(tp); if (error) { splx(s); return (error); } if (cmd == TIOCSETAF) ttyflush(tp, FREAD); } if (!ISSET(t->c_cflag, CIGNORE)) { /* * Set device hardware. */ if (tp->t_param && (error = (*tp->t_param)(tp, t))) { splx(s); return (error); } if (ISSET(t->c_cflag, CLOCAL) && !ISSET(tp->t_cflag, CLOCAL)) { /* * XXX disconnections would be too hard to * get rid of without this kludge. The only * way to get rid of controlling terminals * is to exit from the session leader. */ CLR(tp->t_state, TS_ZOMBIE); wakeup(TSA_CARR_ON(tp)); ttwakeup(tp); ttwwakeup(tp); } if ((ISSET(tp->t_state, TS_CARR_ON) || ISSET(t->c_cflag, CLOCAL)) && !ISSET(tp->t_state, TS_ZOMBIE)) SET(tp->t_state, TS_CONNECTED); else CLR(tp->t_state, TS_CONNECTED); tp->t_cflag = t->c_cflag; tp->t_ispeed = t->c_ispeed; if (t->c_ospeed != 0) tp->t_ospeed = t->c_ospeed; ttsetwater(tp); } if (ISSET(t->c_lflag, ICANON) != ISSET(tp->t_lflag, ICANON) && cmd != TIOCSETAF) { if (ISSET(t->c_lflag, ICANON)) SET(tp->t_lflag, PENDIN); else { /* * XXX we really shouldn't allow toggling * ICANON while we're in a non-termios line * discipline. Now we have to worry about * panicing for a null queue. */ if (tp->t_canq.c_cbreserved > 0 && tp->t_rawq.c_cbreserved > 0) { catq(&tp->t_rawq, &tp->t_canq); /* * XXX the queue limits may be * different, so the old queue * swapping method no longer works. */ catq(&tp->t_canq, &tp->t_rawq); } CLR(tp->t_lflag, PENDIN); } ttwakeup(tp); } tp->t_iflag = t->c_iflag; tp->t_oflag = t->c_oflag; /* * Make the EXTPROC bit read only. */ if (ISSET(tp->t_lflag, EXTPROC)) SET(t->c_lflag, EXTPROC); else CLR(t->c_lflag, EXTPROC); tp->t_lflag = t->c_lflag | ISSET(tp->t_lflag, PENDIN); if (t->c_cc[VMIN] != tp->t_cc[VMIN] || t->c_cc[VTIME] != tp->t_cc[VTIME]) ttwakeup(tp); bcopy(t->c_cc, tp->t_cc, sizeof(t->c_cc)); splx(s); break; } case TIOCSETD: { /* set line discipline */ int t = *(int *)data; struct cdev *device = tp->t_dev; if ((u_int)t >= nlinesw) return (ENXIO); - if (t != tp->t_line) { - s = spltty(); - ttyld_close(tp, flag); - error = (*linesw[t]->l_open)(device, tp); - if (error) { - (void)ttyld_open(tp, device); - splx(s); - return (error); - } - tp->t_line = t; - splx(s); + if (t == tp->t_line) + return (0); + s = spltty(); + ttyld_close(tp, flag); + tp->t_line = t; + error = ttyld_open(tp, device); + if (error) { + /* + * If we fail to switch line discipline we cannot + * fall back to the previous, because we can not + * trust that ldisc to open successfully either. + * Fall back to the default ldisc which we know + * will allways succeed. + */ + tp->t_line = TTYDISC; + (void)ttyld_open(tp, device); } + splx(s); + return (error); break; } case TIOCSTART: /* start output, like ^Q */ s = spltty(); if (ISSET(tp->t_state, TS_TTSTOP) || ISSET(tp->t_lflag, FLUSHO)) { CLR(tp->t_lflag, FLUSHO); CLR(tp->t_state, TS_TTSTOP); ttstart(tp); } splx(s); break; case TIOCSTI: /* simulate terminal input */ if ((flag & FREAD) == 0 && suser(td)) return (EPERM); if (!isctty(p, tp) && suser(td)) return (EACCES); s = spltty(); ttyld_rint(tp, *(u_char *)data); splx(s); break; case TIOCSTOP: /* stop output, like ^S */ s = spltty(); if (!ISSET(tp->t_state, TS_TTSTOP)) { SET(tp->t_state, TS_TTSTOP); (*tp->t_stop)(tp, 0); } splx(s); break; case TIOCSCTTY: /* become controlling tty */ /* Session ctty vnode pointer set in vnode layer. */ sx_slock(&proctree_lock); if (!SESS_LEADER(p) || ((p->p_session->s_ttyvp || tp->t_session) && (tp->t_session != p->p_session))) { sx_sunlock(&proctree_lock); return (EPERM); } tp->t_session = p->p_session; tp->t_pgrp = p->p_pgrp; SESS_LOCK(p->p_session); ttyref(tp); /* ttyrel(): kern_proc.c:pgdelete() */ p->p_session->s_ttyp = tp; SESS_UNLOCK(p->p_session); PROC_LOCK(p); p->p_flag |= P_CONTROLT; PROC_UNLOCK(p); sx_sunlock(&proctree_lock); break; case TIOCSPGRP: { /* set pgrp of tty */ sx_slock(&proctree_lock); pgrp = pgfind(*(int *)data); if (!isctty(p, tp)) { if (pgrp != NULL) PGRP_UNLOCK(pgrp); sx_sunlock(&proctree_lock); return (ENOTTY); } if (pgrp == NULL) { sx_sunlock(&proctree_lock); return (EPERM); } PGRP_UNLOCK(pgrp); if (pgrp->pg_session != p->p_session) { sx_sunlock(&proctree_lock); return (EPERM); } sx_sunlock(&proctree_lock); tp->t_pgrp = pgrp; break; } case TIOCSTAT: /* simulate control-T */ s = spltty(); ttyinfo(tp); splx(s); break; case TIOCSWINSZ: /* set window size */ if (bcmp((caddr_t)&tp->t_winsize, data, sizeof (struct winsize))) { tp->t_winsize = *(struct winsize *)data; if (tp->t_pgrp != NULL) { PGRP_LOCK(tp->t_pgrp); pgsignal(tp->t_pgrp, SIGWINCH, 1); PGRP_UNLOCK(tp->t_pgrp); } } break; case TIOCSDRAINWAIT: error = suser(td); if (error) return (error); tp->t_timeout = *(int *)data * hz; wakeup(TSA_OCOMPLETE(tp)); wakeup(TSA_OLOWAT(tp)); break; case TIOCGDRAINWAIT: *(int *)data = tp->t_timeout / hz; break; default: #if defined(COMPAT_43) #ifndef BURN_BRIDGES return (ttcompat(tp, cmd, data, flag)); #else return (ENOIOCTL); #endif #else return (ENOIOCTL); #endif } return (0); } int ttypoll(struct cdev *dev, int events, struct thread *td) { int s; int revents = 0; struct tty *tp; KASSERT(devsw(dev)->d_flags & D_TTY, ("ttypoll() called on non D_TTY device (%s)", devtoname(dev))); tp = dev->si_tty; KASSERT(tp != NULL, ("ttypoll(): no tty pointer on device (%s)", devtoname(dev))); if (tp == NULL) /* XXX used to return ENXIO, but that means true! */ return ((events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)) | POLLHUP); s = spltty(); if (events & (POLLIN | POLLRDNORM)) { if (ttnread(tp) > 0 || ISSET(tp->t_state, TS_ZOMBIE)) revents |= events & (POLLIN | POLLRDNORM); else selrecord(td, &tp->t_rsel); } if (events & (POLLOUT | POLLWRNORM)) { if ((tp->t_outq.c_cc <= tp->t_olowat && ISSET(tp->t_state, TS_CONNECTED)) || ISSET(tp->t_state, TS_ZOMBIE)) revents |= events & (POLLOUT | POLLWRNORM); else selrecord(td, &tp->t_wsel); } splx(s); return (revents); } static struct filterops ttyread_filtops = { 1, NULL, filt_ttyrdetach, filt_ttyread }; static struct filterops ttywrite_filtops = { 1, NULL, filt_ttywdetach, filt_ttywrite }; int ttykqfilter(struct cdev *dev, struct knote *kn) { struct tty *tp; struct klist *klist; int s; KASSERT(devsw(dev)->d_flags & D_TTY, ("ttykqfilter() called on non D_TTY device (%s)", devtoname(dev))); tp = dev->si_tty; KASSERT(tp != NULL, ("ttykqfilter(): no tty pointer on device (%s)", devtoname(dev))); switch (kn->kn_filter) { case EVFILT_READ: klist = &tp->t_rsel.si_note; kn->kn_fop = &ttyread_filtops; break; case EVFILT_WRITE: klist = &tp->t_wsel.si_note; kn->kn_fop = &ttywrite_filtops; break; default: return (1); } kn->kn_hook = (caddr_t)dev; s = spltty(); SLIST_INSERT_HEAD(klist, kn, kn_selnext); splx(s); return (0); } static void filt_ttyrdetach(struct knote *kn) { struct tty *tp = ((struct cdev *)kn->kn_hook)->si_tty; int s = spltty(); SLIST_REMOVE(&tp->t_rsel.si_note, kn, knote, kn_selnext); splx(s); } static int filt_ttyread(struct knote *kn, long hint) { struct tty *tp = ((struct cdev *)kn->kn_hook)->si_tty; kn->kn_data = ttnread(tp); if (ISSET(tp->t_state, TS_ZOMBIE)) { kn->kn_flags |= EV_EOF; return (1); } return (kn->kn_data > 0); } static void filt_ttywdetach(struct knote *kn) { struct tty *tp = ((struct cdev *)kn->kn_hook)->si_tty; int s = spltty(); SLIST_REMOVE(&tp->t_wsel.si_note, kn, knote, kn_selnext); splx(s); } static int filt_ttywrite(struct knote *kn, long hint) { struct tty *tp = ((struct cdev *)kn->kn_hook)->si_tty; kn->kn_data = tp->t_outq.c_cc; if (ISSET(tp->t_state, TS_ZOMBIE)) return (1); return (kn->kn_data <= tp->t_olowat && ISSET(tp->t_state, TS_CONNECTED)); } /* * Must be called at spltty(). */ static int ttnread(struct tty *tp) { int nread; if (ISSET(tp->t_lflag, PENDIN)) ttypend(tp); nread = tp->t_canq.c_cc; if (!ISSET(tp->t_lflag, ICANON)) { nread += tp->t_rawq.c_cc; if (nread < tp->t_cc[VMIN] && tp->t_cc[VTIME] == 0) nread = 0; } return (nread); } /* * Wait for output to drain. */ int ttywait(struct tty *tp) { int error, s; error = 0; s = spltty(); while ((tp->t_outq.c_cc || ISSET(tp->t_state, TS_BUSY)) && ISSET(tp->t_state, TS_CONNECTED) && tp->t_oproc) { (*tp->t_oproc)(tp); if ((tp->t_outq.c_cc || ISSET(tp->t_state, TS_BUSY)) && ISSET(tp->t_state, TS_CONNECTED)) { SET(tp->t_state, TS_SO_OCOMPLETE); error = ttysleep(tp, TSA_OCOMPLETE(tp), TTOPRI | PCATCH, "ttywai", tp->t_timeout); if (error) { if (error == EWOULDBLOCK) error = EIO; break; } } else break; } if (!error && (tp->t_outq.c_cc || ISSET(tp->t_state, TS_BUSY))) error = EIO; splx(s); return (error); } /* * Flush if successfully wait. */ static int ttywflush(struct tty *tp) { int error; if ((error = ttywait(tp)) == 0) ttyflush(tp, FREAD); return (error); } /* * Flush tty read and/or write queues, notifying anyone waiting. */ void ttyflush(struct tty *tp, int rw) { int s; s = spltty(); #if 0 again: #endif if (rw & FWRITE) { FLUSHQ(&tp->t_outq); CLR(tp->t_state, TS_TTSTOP); } (*tp->t_stop)(tp, rw); if (rw & FREAD) { FLUSHQ(&tp->t_canq); FLUSHQ(&tp->t_rawq); CLR(tp->t_lflag, PENDIN); tp->t_rocount = 0; tp->t_rocol = 0; CLR(tp->t_state, TS_LOCAL); ttwakeup(tp); if (ISSET(tp->t_state, TS_TBLOCK)) { if (rw & FWRITE) FLUSHQ(&tp->t_outq); ttyunblock(tp); /* * Don't let leave any state that might clobber the * next line discipline (although we should do more * to send the START char). Not clearing the state * may have caused the "putc to a clist with no * reserved cblocks" panic/printf. */ CLR(tp->t_state, TS_TBLOCK); #if 0 /* forget it, sleeping isn't always safe and we don't know when it is */ if (ISSET(tp->t_iflag, IXOFF)) { /* * XXX wait a bit in the hope that the stop * character (if any) will go out. Waiting * isn't good since it allows races. This * will be fixed when the stop character is * put in a special queue. Don't bother with * the checks in ttywait() since the timeout * will save us. */ SET(tp->t_state, TS_SO_OCOMPLETE); ttysleep(tp, TSA_OCOMPLETE(tp), TTOPRI, "ttyfls", hz / 10); /* * Don't try sending the stop character again. */ CLR(tp->t_state, TS_TBLOCK); goto again; } #endif } } if (rw & FWRITE) { FLUSHQ(&tp->t_outq); ttwwakeup(tp); } splx(s); } /* * Copy in the default termios characters. */ void termioschars(struct termios *t) { bcopy(ttydefchars, t->c_cc, sizeof t->c_cc); } /* * Old interface. */ void ttychars(struct tty *tp) { termioschars(&tp->t_termios); } /* * Handle input high water. Send stop character for the IXOFF case. Turn * on our input flow control bit and propagate the changes to the driver. * XXX the stop character should be put in a special high priority queue. */ void ttyblock(struct tty *tp) { SET(tp->t_state, TS_TBLOCK); if (ISSET(tp->t_iflag, IXOFF) && tp->t_cc[VSTOP] != _POSIX_VDISABLE && putc(tp->t_cc[VSTOP], &tp->t_outq) != 0) CLR(tp->t_state, TS_TBLOCK); /* try again later */ ttstart(tp); } /* * Handle input low water. Send start character for the IXOFF case. Turn * off our input flow control bit and propagate the changes to the driver. * XXX the start character should be put in a special high priority queue. */ static void ttyunblock(struct tty *tp) { CLR(tp->t_state, TS_TBLOCK); if (ISSET(tp->t_iflag, IXOFF) && tp->t_cc[VSTART] != _POSIX_VDISABLE && putc(tp->t_cc[VSTART], &tp->t_outq) != 0) SET(tp->t_state, TS_TBLOCK); /* try again later */ ttstart(tp); } #ifdef notyet /* Not used by any current (i386) drivers. */ /* * Restart after an inter-char delay. */ void ttrstrt(void *tp_arg) { struct tty *tp; int s; KASSERT(tp_arg != NULL, ("ttrstrt")); tp = tp_arg; s = spltty(); CLR(tp->t_state, TS_TIMEOUT); ttstart(tp); splx(s); } #endif int ttstart(struct tty *tp) { if (tp->t_oproc != NULL) /* XXX: Kludge for pty. */ (*tp->t_oproc)(tp); return (0); } /* * "close" a line discipline */ int ttylclose(struct tty *tp, int flag) { if (flag & FNONBLOCK || ttywflush(tp)) ttyflush(tp, FREAD | FWRITE); return (0); } /* * Handle modem control transition on a tty. * Flag indicates new state of carrier. * Returns 0 if the line should be turned off, otherwise 1. */ int ttymodem(struct tty *tp, int flag) { if (ISSET(tp->t_state, TS_CARR_ON) && ISSET(tp->t_cflag, MDMBUF)) { /* * MDMBUF: do flow control according to carrier flag * XXX TS_CAR_OFLOW doesn't do anything yet. TS_TTSTOP * works if IXON and IXANY are clear. */ if (flag) { CLR(tp->t_state, TS_CAR_OFLOW); CLR(tp->t_state, TS_TTSTOP); ttstart(tp); } else if (!ISSET(tp->t_state, TS_CAR_OFLOW)) { SET(tp->t_state, TS_CAR_OFLOW); SET(tp->t_state, TS_TTSTOP); (*tp->t_stop)(tp, 0); } } else if (flag == 0) { /* * Lost carrier. */ CLR(tp->t_state, TS_CARR_ON); if (ISSET(tp->t_state, TS_ISOPEN) && !ISSET(tp->t_cflag, CLOCAL)) { SET(tp->t_state, TS_ZOMBIE); CLR(tp->t_state, TS_CONNECTED); if (tp->t_session) { sx_slock(&proctree_lock); if (tp->t_session->s_leader) { struct proc *p; p = tp->t_session->s_leader; PROC_LOCK(p); psignal(p, SIGHUP); PROC_UNLOCK(p); } sx_sunlock(&proctree_lock); } ttyflush(tp, FREAD | FWRITE); return (0); } } else { /* * Carrier now on. */ SET(tp->t_state, TS_CARR_ON); if (!ISSET(tp->t_state, TS_ZOMBIE)) SET(tp->t_state, TS_CONNECTED); wakeup(TSA_CARR_ON(tp)); ttwakeup(tp); ttwwakeup(tp); } return (1); } /* * Reinput pending characters after state switch * call at spltty(). */ static void ttypend(struct tty *tp) { struct clist tq; int c; CLR(tp->t_lflag, PENDIN); SET(tp->t_state, TS_TYPEN); /* * XXX this assumes too much about clist internals. It may even * fail if the cblock slush pool is empty. We can't allocate more * cblocks here because we are called from an interrupt handler * and clist_alloc_cblocks() can wait. */ tq = tp->t_rawq; bzero(&tp->t_rawq, sizeof tp->t_rawq); tp->t_rawq.c_cbmax = tq.c_cbmax; tp->t_rawq.c_cbreserved = tq.c_cbreserved; while ((c = getc(&tq)) >= 0) ttyinput(c, tp); CLR(tp->t_state, TS_TYPEN); } /* * Process a read call on a tty device. */ int ttread(struct tty *tp, struct uio *uio, int flag) { struct clist *qp; int c; tcflag_t lflag; cc_t *cc = tp->t_cc; struct thread *td; struct proc *p; int s, first, error = 0; int has_stime = 0, last_cc = 0; long slp = 0; /* XXX this should be renamed `timo'. */ struct timeval stime; struct pgrp *pg; td = curthread; p = td->td_proc; loop: s = spltty(); lflag = tp->t_lflag; /* * take pending input first */ if (ISSET(lflag, PENDIN)) { ttypend(tp); splx(s); /* reduce latency */ s = spltty(); lflag = tp->t_lflag; /* XXX ttypend() clobbers it */ } /* * Hang process if it's in the background. */ if (isbackground(p, tp)) { splx(s); sx_slock(&proctree_lock); PROC_LOCK(p); if (SIGISMEMBER(p->p_sigacts->ps_sigignore, SIGTTIN) || SIGISMEMBER(td->td_sigmask, SIGTTIN) || (p->p_flag & P_PPWAIT) || p->p_pgrp->pg_jobc == 0) { PROC_UNLOCK(p); sx_sunlock(&proctree_lock); return (EIO); } pg = p->p_pgrp; PROC_UNLOCK(p); PGRP_LOCK(pg); sx_sunlock(&proctree_lock); pgsignal(pg, SIGTTIN, 1); PGRP_UNLOCK(pg); error = ttysleep(tp, &lbolt, TTIPRI | PCATCH, "ttybg2", 0); if (error) return (error); goto loop; } if (ISSET(tp->t_state, TS_ZOMBIE)) { splx(s); return (0); /* EOF */ } /* * If canonical, use the canonical queue, * else use the raw queue. * * (should get rid of clists...) */ qp = ISSET(lflag, ICANON) ? &tp->t_canq : &tp->t_rawq; if (flag & IO_NDELAY) { if (qp->c_cc > 0) goto read; if (!ISSET(lflag, ICANON) && cc[VMIN] == 0) { splx(s); return (0); } splx(s); return (EWOULDBLOCK); } if (!ISSET(lflag, ICANON)) { int m = cc[VMIN]; long t = cc[VTIME]; struct timeval timecopy; /* * Check each of the four combinations. * (m > 0 && t == 0) is the normal read case. * It should be fairly efficient, so we check that and its * companion case (m == 0 && t == 0) first. * For the other two cases, we compute the target sleep time * into slp. */ if (t == 0) { if (qp->c_cc < m) goto sleep; if (qp->c_cc > 0) goto read; /* m, t and qp->c_cc are all 0. 0 is enough input. */ splx(s); return (0); } t *= 100000; /* time in us */ #define diff(t1, t2) (((t1).tv_sec - (t2).tv_sec) * 1000000 + \ ((t1).tv_usec - (t2).tv_usec)) if (m > 0) { if (qp->c_cc <= 0) goto sleep; if (qp->c_cc >= m) goto read; getmicrotime(&timecopy); if (!has_stime) { /* first character, start timer */ has_stime = 1; stime = timecopy; slp = t; } else if (qp->c_cc > last_cc) { /* got a character, restart timer */ stime = timecopy; slp = t; } else { /* nothing, check expiration */ slp = t - diff(timecopy, stime); if (slp <= 0) goto read; } last_cc = qp->c_cc; } else { /* m == 0 */ if (qp->c_cc > 0) goto read; getmicrotime(&timecopy); if (!has_stime) { has_stime = 1; stime = timecopy; slp = t; } else { slp = t - diff(timecopy, stime); if (slp <= 0) { /* Timed out, but 0 is enough input. */ splx(s); return (0); } } } #undef diff /* * Rounding down may make us wake up just short * of the target, so we round up. * The formula is ceiling(slp * hz/1000000). * 32-bit arithmetic is enough for hz < 169. * XXX see tvtohz() for how to avoid overflow if hz * is large (divide by `tick' and/or arrange to * use tvtohz() if hz is large). */ slp = (long) (((u_long)slp * hz) + 999999) / 1000000; goto sleep; } if (qp->c_cc <= 0) { sleep: /* * There is no input, or not enough input and we can block. */ error = ttysleep(tp, TSA_HUP_OR_INPUT(tp), TTIPRI | PCATCH, ISSET(tp->t_state, TS_CONNECTED) ? "ttyin" : "ttyhup", (int)slp); splx(s); if (error == EWOULDBLOCK) error = 0; else if (error) return (error); /* * XXX what happens if another process eats some input * while we are asleep (not just here)? It would be * safest to detect changes and reset our state variables * (has_stime and last_cc). */ slp = 0; goto loop; } read: splx(s); /* * Input present, check for input mapping and processing. */ first = 1; if (ISSET(lflag, ICANON | ISIG)) goto slowcase; for (;;) { char ibuf[IBUFSIZ]; int icc; icc = imin(uio->uio_resid, IBUFSIZ); icc = q_to_b(qp, ibuf, icc); if (icc <= 0) { if (first) goto loop; break; } error = uiomove(ibuf, icc, uio); /* * XXX if there was an error then we should ungetc() the * unmoved chars and reduce icc here. */ if (error) break; if (uio->uio_resid == 0) break; first = 0; } goto out; slowcase: for (;;) { c = getc(qp); if (c < 0) { if (first) goto loop; break; } /* * delayed suspend (^Y) */ if (CCEQ(cc[VDSUSP], c) && ISSET(lflag, IEXTEN | ISIG) == (IEXTEN | ISIG)) { if (tp->t_pgrp != NULL) { PGRP_LOCK(tp->t_pgrp); pgsignal(tp->t_pgrp, SIGTSTP, 1); PGRP_UNLOCK(tp->t_pgrp); } if (first) { error = ttysleep(tp, &lbolt, TTIPRI | PCATCH, "ttybg3", 0); if (error) break; goto loop; } break; } /* * Interpret EOF only in canonical mode. */ if (CCEQ(cc[VEOF], c) && ISSET(lflag, ICANON)) break; /* * Give user character. */ error = ureadc(c, uio); if (error) /* XXX should ungetc(c, qp). */ break; if (uio->uio_resid == 0) break; /* * In canonical mode check for a "break character" * marking the end of a "line of input". */ if (ISSET(lflag, ICANON) && TTBREAKC(c, lflag)) break; first = 0; } out: /* * Look to unblock input now that (presumably) * the input queue has gone down. */ s = spltty(); if (ISSET(tp->t_state, TS_TBLOCK) && tp->t_rawq.c_cc + tp->t_canq.c_cc <= tp->t_ilowat) ttyunblock(tp); splx(s); return (error); } /* * Check the output queue on tp for space for a kernel message (from uprintf * or tprintf). Allow some space over the normal hiwater mark so we don't * lose messages due to normal flow control, but don't let the tty run amok. * Sleeps here are not interruptible, but we return prematurely if new signals * arrive. */ int ttycheckoutq(struct tty *tp, int wait) { int hiwat, s; sigset_t oldmask; struct thread *td; struct proc *p; td = curthread; p = td->td_proc; hiwat = tp->t_ohiwat; SIGEMPTYSET(oldmask); s = spltty(); if (wait) { PROC_LOCK(p); oldmask = td->td_siglist; PROC_UNLOCK(p); } if (tp->t_outq.c_cc > hiwat + OBUFSIZ + 100) while (tp->t_outq.c_cc > hiwat) { ttstart(tp); if (tp->t_outq.c_cc <= hiwat) break; if (!wait) { splx(s); return (0); } PROC_LOCK(p); if (!SIGSETEQ(td->td_siglist, oldmask)) { PROC_UNLOCK(p); splx(s); return (0); } PROC_UNLOCK(p); SET(tp->t_state, TS_SO_OLOWAT); tsleep(TSA_OLOWAT(tp), PZERO - 1, "ttoutq", hz); } splx(s); return (1); } /* * Process a write call on a tty device. */ int ttwrite(struct tty *tp, struct uio *uio, int flag) { char *cp = NULL; int cc, ce; struct thread *td; struct proc *p; int i, hiwat, cnt, error, s; char obuf[OBUFSIZ]; hiwat = tp->t_ohiwat; cnt = uio->uio_resid; error = 0; cc = 0; td = curthread; p = td->td_proc; loop: s = spltty(); if (ISSET(tp->t_state, TS_ZOMBIE)) { splx(s); if (uio->uio_resid == cnt) error = EIO; goto out; } if (!ISSET(tp->t_state, TS_CONNECTED)) { if (flag & IO_NDELAY) { splx(s); error = EWOULDBLOCK; goto out; } error = ttysleep(tp, TSA_CARR_ON(tp), TTIPRI | PCATCH, "ttydcd", 0); splx(s); if (error) goto out; goto loop; } splx(s); /* * Hang the process if it's in the background. */ sx_slock(&proctree_lock); PROC_LOCK(p); if (isbackground(p, tp) && ISSET(tp->t_lflag, TOSTOP) && !(p->p_flag & P_PPWAIT) && !SIGISMEMBER(p->p_sigacts->ps_sigignore, SIGTTOU) && !SIGISMEMBER(td->td_sigmask, SIGTTOU)) { if (p->p_pgrp->pg_jobc == 0) { PROC_UNLOCK(p); sx_sunlock(&proctree_lock); error = EIO; goto out; } PROC_UNLOCK(p); PGRP_LOCK(p->p_pgrp); sx_sunlock(&proctree_lock); pgsignal(p->p_pgrp, SIGTTOU, 1); PGRP_UNLOCK(p->p_pgrp); error = ttysleep(tp, &lbolt, TTIPRI | PCATCH, "ttybg4", 0); if (error) goto out; goto loop; } else { PROC_UNLOCK(p); sx_sunlock(&proctree_lock); } /* * Process the user's data in at most OBUFSIZ chunks. Perform any * output translation. Keep track of high water mark, sleep on * overflow awaiting device aid in acquiring new space. */ while (uio->uio_resid > 0 || cc > 0) { if (ISSET(tp->t_lflag, FLUSHO)) { uio->uio_resid = 0; return (0); } if (tp->t_outq.c_cc > hiwat) goto ovhiwat; /* * Grab a hunk of data from the user, unless we have some * leftover from last time. */ if (cc == 0) { cc = imin(uio->uio_resid, OBUFSIZ); cp = obuf; error = uiomove(cp, cc, uio); if (error) { cc = 0; break; } } /* * If nothing fancy need be done, grab those characters we * can handle without any of ttyoutput's processing and * just transfer them to the output q. For those chars * which require special processing (as indicated by the * bits in char_type), call ttyoutput. After processing * a hunk of data, look for FLUSHO so ^O's will take effect * immediately. */ while (cc > 0) { if (!ISSET(tp->t_oflag, OPOST)) ce = cc; else { ce = cc - scanc((u_int)cc, (u_char *)cp, char_type, CCLASSMASK); /* * If ce is zero, then we're processing * a special character through ttyoutput. */ if (ce == 0) { tp->t_rocount = 0; if (ttyoutput(*cp, tp) >= 0) { /* No Clists, wait a bit. */ ttstart(tp); if (flag & IO_NDELAY) { error = EWOULDBLOCK; goto out; } error = ttysleep(tp, &lbolt, TTOPRI|PCATCH, "ttybf1", 0); if (error) goto out; goto loop; } cp++; cc--; if (ISSET(tp->t_lflag, FLUSHO) || tp->t_outq.c_cc > hiwat) goto ovhiwat; continue; } } /* * A bunch of normal characters have been found. * Transfer them en masse to the output queue and * continue processing at the top of the loop. * If there are any further characters in this * <= OBUFSIZ chunk, the first should be a character * requiring special handling by ttyoutput. */ tp->t_rocount = 0; i = b_to_q(cp, ce, &tp->t_outq); ce -= i; tp->t_column += ce; cp += ce, cc -= ce, tk_nout += ce; tp->t_outcc += ce; if (i > 0) { /* No Clists, wait a bit. */ ttstart(tp); if (flag & IO_NDELAY) { error = EWOULDBLOCK; goto out; } error = ttysleep(tp, &lbolt, TTOPRI | PCATCH, "ttybf2", 0); if (error) goto out; goto loop; } if (ISSET(tp->t_lflag, FLUSHO) || tp->t_outq.c_cc > hiwat) break; } ttstart(tp); } out: /* * If cc is nonzero, we leave the uio structure inconsistent, as the * offset and iov pointers have moved forward, but it doesn't matter * (the call will either return short or restart with a new uio). */ uio->uio_resid += cc; return (error); ovhiwat: ttstart(tp); s = spltty(); /* * This can only occur if FLUSHO is set in t_lflag, * or if ttstart/oproc is synchronous (or very fast). */ if (tp->t_outq.c_cc <= hiwat) { splx(s); goto loop; } if (flag & IO_NDELAY) { splx(s); uio->uio_resid += cc; return (uio->uio_resid == cnt ? EWOULDBLOCK : 0); } SET(tp->t_state, TS_SO_OLOWAT); error = ttysleep(tp, TSA_OLOWAT(tp), TTOPRI | PCATCH, "ttywri", tp->t_timeout); splx(s); if (error == EWOULDBLOCK) error = EIO; if (error) goto out; goto loop; } /* * Rubout one character from the rawq of tp * as cleanly as possible. */ static void ttyrub(int c, struct tty *tp) { char *cp; int savecol; int tabc, s; if (!ISSET(tp->t_lflag, ECHO) || ISSET(tp->t_lflag, EXTPROC)) return; CLR(tp->t_lflag, FLUSHO); if (ISSET(tp->t_lflag, ECHOE)) { if (tp->t_rocount == 0) { /* * Screwed by ttwrite; retype */ ttyretype(tp); return; } if (c == ('\t' | TTY_QUOTE) || c == ('\n' | TTY_QUOTE)) ttyrubo(tp, 2); else { CLR(c, ~TTY_CHARMASK); switch (CCLASS(c)) { case ORDINARY: ttyrubo(tp, 1); break; case BACKSPACE: case CONTROL: case NEWLINE: case RETURN: case VTAB: if (ISSET(tp->t_lflag, ECHOCTL)) ttyrubo(tp, 2); break; case TAB: if (tp->t_rocount < tp->t_rawq.c_cc) { ttyretype(tp); return; } s = spltty(); savecol = tp->t_column; SET(tp->t_state, TS_CNTTB); SET(tp->t_lflag, FLUSHO); tp->t_column = tp->t_rocol; cp = tp->t_rawq.c_cf; if (cp) tabc = *cp; /* XXX FIX NEXTC */ for (; cp; cp = nextc(&tp->t_rawq, cp, &tabc)) ttyecho(tabc, tp); CLR(tp->t_lflag, FLUSHO); CLR(tp->t_state, TS_CNTTB); splx(s); /* savecol will now be length of the tab. */ savecol -= tp->t_column; tp->t_column += savecol; if (savecol > 8) savecol = 8; /* overflow screw */ while (--savecol >= 0) (void)ttyoutput('\b', tp); break; default: /* XXX */ #define PANICSTR "ttyrub: would panic c = %d, val = %d\n" (void)printf(PANICSTR, c, CCLASS(c)); #ifdef notdef panic(PANICSTR, c, CCLASS(c)); #endif } } } else if (ISSET(tp->t_lflag, ECHOPRT)) { if (!ISSET(tp->t_state, TS_ERASE)) { SET(tp->t_state, TS_ERASE); (void)ttyoutput('\\', tp); } ttyecho(c, tp); } else { ttyecho(tp->t_cc[VERASE], tp); /* * This code may be executed not only when an ERASE key * is pressed, but also when ^U (KILL) or ^W (WERASE) are. * So, I didn't think it was worthwhile to pass the extra * information (which would need an extra parameter, * changing every call) needed to distinguish the ERASE2 * case from the ERASE. */ } --tp->t_rocount; } /* * Back over cnt characters, erasing them. */ static void ttyrubo(struct tty *tp, int cnt) { while (cnt-- > 0) { (void)ttyoutput('\b', tp); (void)ttyoutput(' ', tp); (void)ttyoutput('\b', tp); } } /* * ttyretype -- * Reprint the rawq line. Note, it is assumed that c_cc has already * been checked. */ static void ttyretype(struct tty *tp) { char *cp; int s, c; /* Echo the reprint character. */ if (tp->t_cc[VREPRINT] != _POSIX_VDISABLE) ttyecho(tp->t_cc[VREPRINT], tp); (void)ttyoutput('\n', tp); /* * XXX * FIX: NEXTC IS BROKEN - DOESN'T CHECK QUOTE * BIT OF FIRST CHAR. */ s = spltty(); for (cp = tp->t_canq.c_cf, c = (cp != NULL ? *cp : 0); cp != NULL; cp = nextc(&tp->t_canq, cp, &c)) ttyecho(c, tp); for (cp = tp->t_rawq.c_cf, c = (cp != NULL ? *cp : 0); cp != NULL; cp = nextc(&tp->t_rawq, cp, &c)) ttyecho(c, tp); CLR(tp->t_state, TS_ERASE); splx(s); tp->t_rocount = tp->t_rawq.c_cc; tp->t_rocol = 0; } /* * Echo a typed character to the terminal. */ static void ttyecho(int c, struct tty *tp) { if (!ISSET(tp->t_state, TS_CNTTB)) CLR(tp->t_lflag, FLUSHO); if ((!ISSET(tp->t_lflag, ECHO) && (c != '\n' || !ISSET(tp->t_lflag, ECHONL))) || ISSET(tp->t_lflag, EXTPROC)) return; if (ISSET(tp->t_lflag, ECHOCTL) && ((ISSET(c, TTY_CHARMASK) <= 037 && c != '\t' && c != '\n') || ISSET(c, TTY_CHARMASK) == 0177)) { (void)ttyoutput('^', tp); CLR(c, ~TTY_CHARMASK); if (c == 0177) c = '?'; else c += 'A' - 1; } (void)ttyoutput(c, tp); } /* * Wake up any readers on a tty. */ void ttwakeup(struct tty *tp) { if (SEL_WAITING(&tp->t_rsel)) selwakeuppri(&tp->t_rsel, TTIPRI); if (ISSET(tp->t_state, TS_ASYNC) && tp->t_sigio != NULL) pgsigio(&tp->t_sigio, SIGIO, (tp->t_session != NULL)); wakeup(TSA_HUP_OR_INPUT(tp)); KNOTE(&tp->t_rsel.si_note, 0); } /* * Wake up any writers on a tty. */ void ttwwakeup(struct tty *tp) { if (SEL_WAITING(&tp->t_wsel) && tp->t_outq.c_cc <= tp->t_olowat) selwakeuppri(&tp->t_wsel, TTOPRI); if (ISSET(tp->t_state, TS_ASYNC) && tp->t_sigio != NULL) pgsigio(&tp->t_sigio, SIGIO, (tp->t_session != NULL)); if (ISSET(tp->t_state, TS_BUSY | TS_SO_OCOMPLETE) == TS_SO_OCOMPLETE && tp->t_outq.c_cc == 0) { CLR(tp->t_state, TS_SO_OCOMPLETE); wakeup(TSA_OCOMPLETE(tp)); } if (ISSET(tp->t_state, TS_SO_OLOWAT) && tp->t_outq.c_cc <= tp->t_olowat) { CLR(tp->t_state, TS_SO_OLOWAT); wakeup(TSA_OLOWAT(tp)); } KNOTE(&tp->t_wsel.si_note, 0); } /* * Look up a code for a specified speed in a conversion table; * used by drivers to map software speed values to hardware parameters. */ int ttspeedtab(int speed, struct speedtab *table) { for ( ; table->sp_speed != -1; table++) if (table->sp_speed == speed) return (table->sp_code); return (-1); } /* * Set input and output watermarks and buffer sizes. For input, the * high watermark is about one second's worth of input above empty, the * low watermark is slightly below high water, and the buffer size is a * driver-dependent amount above high water. For output, the watermarks * are near the ends of the buffer, with about 1 second's worth of input * between them. All this only applies to the standard line discipline. */ void ttsetwater(struct tty *tp) { int cps, ttmaxhiwat, x; /* Input. */ clist_alloc_cblocks(&tp->t_canq, TTYHOG, 512); switch (tp->t_ispeedwat) { case (speed_t)-1: cps = tp->t_ispeed / 10; break; case 0: /* * This case is for old drivers that don't know about * t_ispeedwat. Arrange for them to get the old buffer * sizes and watermarks. */ cps = TTYHOG - 2 * 256; tp->t_ififosize = 2 * 256; break; default: cps = tp->t_ispeedwat / 10; break; } tp->t_ihiwat = cps; tp->t_ilowat = 7 * cps / 8; x = cps + tp->t_ififosize; clist_alloc_cblocks(&tp->t_rawq, x, x); /* Output. */ switch (tp->t_ospeedwat) { case (speed_t)-1: cps = tp->t_ospeed / 10; ttmaxhiwat = 2 * TTMAXHIWAT; break; case 0: cps = tp->t_ospeed / 10; ttmaxhiwat = TTMAXHIWAT; break; default: cps = tp->t_ospeedwat / 10; ttmaxhiwat = 8 * TTMAXHIWAT; break; } #define CLAMP(x, h, l) ((x) > h ? h : ((x) < l) ? l : (x)) tp->t_olowat = x = CLAMP(cps / 2, TTMAXLOWAT, TTMINLOWAT); x += cps; x = CLAMP(x, ttmaxhiwat, TTMINHIWAT); /* XXX clamps are too magic */ tp->t_ohiwat = roundup(x, CBSIZE); /* XXX for compat */ x = imax(tp->t_ohiwat, TTMAXHIWAT); /* XXX for compat/safety */ x += OBUFSIZ + 100; clist_alloc_cblocks(&tp->t_outq, x, x); #undef CLAMP } /* * Report on state of foreground process group. */ void ttyinfo(struct tty *tp) { struct timeval utime, stime; struct proc *p, *pick; struct thread *td; const char *stateprefix, *state; long rss; int load, pctcpu; if (ttycheckoutq(tp,0) == 0) return; /* Print load average. */ load = (averunnable.ldavg[0] * 100 + FSCALE / 2) >> FSHIFT; ttyprintf(tp, "load: %d.%02d ", load / 100, load % 100); /* * On return following a ttyprintf(), we set tp->t_rocount to 0 so * that pending input will be retyped on BS. */ if (tp->t_session == NULL) { ttyprintf(tp, "not a controlling terminal\n"); tp->t_rocount = 0; return; } if (tp->t_pgrp == NULL) { ttyprintf(tp, "no foreground process group\n"); tp->t_rocount = 0; return; } PGRP_LOCK(tp->t_pgrp); if ((p = LIST_FIRST(&tp->t_pgrp->pg_members)) == 0) { PGRP_UNLOCK(tp->t_pgrp); ttyprintf(tp, "empty foreground process group\n"); tp->t_rocount = 0; return; } /* * Pick the most interesting process and copy some of its * state for printing later. sched_lock must be held for * most parts of this. Holding it throughout is simplest * and prevents even unimportant inconsistencies in the * copy of the state, but may increase interrupt latency * too much. */ mtx_lock_spin(&sched_lock); for (pick = NULL; p != 0; p = LIST_NEXT(p, p_pglist)) if (proc_compare(pick, p)) pick = p; PGRP_UNLOCK(tp->t_pgrp); td = FIRST_THREAD_IN_PROC(pick); /* XXXKSE */ #if 0 KASSERT(td != NULL, ("ttyinfo: no thread")); #else if (td == NULL) { mtx_unlock_spin(&sched_lock); ttyprintf(tp, "foreground process without thread\n"); tp->t_rocount = 0; return; } #endif stateprefix = ""; if (TD_IS_RUNNING(td)) state = "running"; else if (TD_ON_RUNQ(td) || TD_CAN_RUN(td)) state = "runnable"; else if (TD_IS_SLEEPING(td)) { /* XXX: If we're sleeping, are we ever not in a queue? */ if (TD_ON_SLEEPQ(td)) state = td->td_wmesg; else state = "sleeping without queue"; } else if (TD_ON_LOCK(td)) { state = td->td_lockname; stateprefix = "*"; } else if (TD_IS_SUSPENDED(td)) state = "suspended"; else if (TD_AWAITING_INTR(td)) state = "intrwait"; else state = "unknown"; calcru(pick, &utime, &stime, NULL); pctcpu = (sched_pctcpu(td) * 10000 + FSCALE / 2) >> FSHIFT; if (pick->p_state == PRS_NEW || pick->p_state == PRS_ZOMBIE) rss = 0; else rss = pgtok(vmspace_resident_count(pick->p_vmspace)); mtx_unlock_spin(&sched_lock); /* Print command, pid, state, utime, stime, %cpu, and rss. */ ttyprintf(tp, " cmd: %s %d [%s%s] %ld.%02ldu %ld.%02lds %d%% %ldk\n", pick->p_comm, pick->p_pid, stateprefix, state, (long)utime.tv_sec, utime.tv_usec / 10000, (long)stime.tv_sec, stime.tv_usec / 10000, pctcpu / 100, rss); tp->t_rocount = 0; } /* * Returns 1 if p2 is "better" than p1 * * The algorithm for picking the "interesting" process is thus: * * 1) Only foreground processes are eligible - implied. * 2) Runnable processes are favored over anything else. The runner * with the highest cpu utilization is picked (p_estcpu). Ties are * broken by picking the highest pid. * 3) The sleeper with the shortest sleep time is next. With ties, * we pick out just "short-term" sleepers (P_SINTR == 0). * 4) Further ties are broken by picking the highest pid. */ #define ISRUN(p, val) \ do { \ struct thread *td; \ val = 0; \ FOREACH_THREAD_IN_PROC(p, td) { \ if (TD_ON_RUNQ(td) || \ TD_IS_RUNNING(td)) { \ val = 1; \ break; \ } \ } \ } while (0) #define TESTAB(a, b) ((a)<<1 | (b)) #define ONLYA 2 #define ONLYB 1 #define BOTH 3 static int proc_compare(struct proc *p1, struct proc *p2) { int esta, estb; struct ksegrp *kg; mtx_assert(&sched_lock, MA_OWNED); if (p1 == NULL) return (1); ISRUN(p1, esta); ISRUN(p2, estb); /* * see if at least one of them is runnable */ switch (TESTAB(esta, estb)) { case ONLYA: return (0); case ONLYB: return (1); case BOTH: /* * tie - favor one with highest recent cpu utilization */ esta = estb = 0; FOREACH_KSEGRP_IN_PROC(p1,kg) { esta += kg->kg_estcpu; } FOREACH_KSEGRP_IN_PROC(p2,kg) { estb += kg->kg_estcpu; } if (estb > esta) return (1); if (esta > estb) return (0); return (p2->p_pid > p1->p_pid); /* tie - return highest pid */ } /* * weed out zombies */ switch (TESTAB(p1->p_state == PRS_ZOMBIE, p2->p_state == PRS_ZOMBIE)) { case ONLYA: return (1); case ONLYB: return (0); case BOTH: return (p2->p_pid > p1->p_pid); /* tie - return highest pid */ } #if 0 /* XXXKSE */ /* * pick the one with the smallest sleep time */ if (p2->p_slptime > p1->p_slptime) return (0); if (p1->p_slptime > p2->p_slptime) return (1); /* * favor one sleeping in a non-interruptible sleep */ if (p1->p_sflag & PS_SINTR && (p2->p_sflag & PS_SINTR) == 0) return (1); if (p2->p_sflag & PS_SINTR && (p1->p_sflag & PS_SINTR) == 0) return (0); #endif return (p2->p_pid > p1->p_pid); /* tie - return highest pid */ } /* * Output char to tty; console putchar style. */ int tputchar(int c, struct tty *tp) { int s; s = spltty(); if (!ISSET(tp->t_state, TS_CONNECTED)) { splx(s); return (-1); } if (c == '\n') (void)ttyoutput('\r', tp); (void)ttyoutput(c, tp); ttstart(tp); splx(s); return (0); } /* * Sleep on chan, returning ERESTART if tty changed while we napped and * returning any errors (e.g. EINTR/EWOULDBLOCK) reported by tsleep. If * the tty is revoked, restarting a pending call will redo validation done * at the start of the call. */ int ttysleep(struct tty *tp, void *chan, int pri, char *wmesg, int timo) { int error; int gen; gen = tp->t_gen; error = tsleep(chan, pri, wmesg, timo); if (error) return (error); return (tp->t_gen == gen ? 0 : ERESTART); } /* * Gain a reference to a TTY */ int ttyref(struct tty *tp) { int i; mtx_lock(&tp->t_mtx); KASSERT(tp->t_refcnt > 0, ("ttyref(): tty refcnt is %d (%s)", tp->t_refcnt, tp->t_dev != NULL ? devtoname(tp->t_dev) : "??")); i = ++tp->t_refcnt; mtx_unlock(&tp->t_mtx); return (i); } /* * Drop a reference to a TTY. * When reference count drops to zero, we free it. */ int ttyrel(struct tty *tp) { int i; mtx_lock(&tty_list_mutex); mtx_lock(&tp->t_mtx); KASSERT(tp->t_refcnt > 0, ("ttyrel(): tty refcnt is %d (%s)", tp->t_refcnt, tp->t_dev != NULL ? devtoname(tp->t_dev) : "??")); i = --tp->t_refcnt; if (i != 0) { mtx_unlock(&tp->t_mtx); mtx_unlock(&tty_list_mutex); return (i); } TAILQ_REMOVE(&tty_list, tp, t_list); mtx_unlock(&tp->t_mtx); mtx_unlock(&tty_list_mutex); mtx_destroy(&tp->t_mtx); free(tp, M_TTYS); return (i); } /* * Allocate a tty struct. Clists in the struct will be allocated by * ttyopen(). */ struct tty * ttymalloc(struct tty *tp) { static int once; if (!once) { mtx_init(&tty_list_mutex, "ttylist", NULL, MTX_DEF); once++; } if (tp) { /* * XXX: Either this argument should go away, or we should * XXX: require it and do a ttyrel(tp) here and allocate * XXX: a new tty. For now do nothing. */ return(tp); } tp = malloc(sizeof *tp, M_TTYS, M_WAITOK | M_ZERO); tp->t_timeout = -1; mtx_init(&tp->t_mtx, "tty", NULL, MTX_DEF); tp->t_refcnt = 1; mtx_lock(&tty_list_mutex); TAILQ_INSERT_TAIL(&tty_list, tp, t_list); mtx_unlock(&tty_list_mutex); return (tp); } static int sysctl_kern_ttys(SYSCTL_HANDLER_ARGS) { struct tty *tp, *tp2; struct xtty xt; int error; error = 0; mtx_lock(&tty_list_mutex); tp = TAILQ_FIRST(&tty_list); if (tp != NULL) ttyref(tp); mtx_unlock(&tty_list_mutex); while (tp != NULL) { bzero(&xt, sizeof xt); xt.xt_size = sizeof xt; #define XT_COPY(field) xt.xt_##field = tp->t_##field xt.xt_rawcc = tp->t_rawq.c_cc; xt.xt_cancc = tp->t_canq.c_cc; xt.xt_outcc = tp->t_outq.c_cc; XT_COPY(line); if (tp->t_dev != NULL) xt.xt_dev = dev2udev(tp->t_dev); XT_COPY(state); XT_COPY(flags); XT_COPY(timeout); if (tp->t_pgrp != NULL) xt.xt_pgid = tp->t_pgrp->pg_id; if (tp->t_session != NULL) xt.xt_sid = tp->t_session->s_sid; XT_COPY(termios); XT_COPY(winsize); XT_COPY(column); XT_COPY(rocount); XT_COPY(rocol); XT_COPY(ififosize); XT_COPY(ihiwat); XT_COPY(ilowat); XT_COPY(ispeedwat); XT_COPY(ohiwat); XT_COPY(olowat); XT_COPY(ospeedwat); #undef XT_COPY error = SYSCTL_OUT(req, &xt, sizeof xt); if (error != 0) { ttyrel(tp); return (error); } mtx_lock(&tty_list_mutex); tp2 = TAILQ_NEXT(tp, t_list); if (tp2 != NULL) ttyref(tp2); mtx_unlock(&tty_list_mutex); ttyrel(tp); tp = tp2; } return (0); } SYSCTL_PROC(_kern, OID_AUTO, ttys, CTLTYPE_OPAQUE|CTLFLAG_RD, 0, 0, sysctl_kern_ttys, "S,xtty", "All ttys"); SYSCTL_LONG(_kern, OID_AUTO, tty_nin, CTLFLAG_RD, &tk_nin, 0, "Total TTY in characters"); SYSCTL_LONG(_kern, OID_AUTO, tty_nout, CTLFLAG_RD, &tk_nout, 0, "Total TTY out characters"); void nottystop(struct tty *tp, int rw) { return; } int ttyread(struct cdev *dev, struct uio *uio, int flag) { struct tty *tp; KASSERT(devsw(dev)->d_flags & D_TTY, ("ttyread() called on non D_TTY device (%s)", devtoname(dev))); tp = dev->si_tty; KASSERT(tp != NULL, ("ttyread(): no tty pointer on device (%s)", devtoname(dev))); if (tp == NULL) return (ENODEV); return (ttyld_read(tp, uio, flag)); } int ttywrite(struct cdev *dev, struct uio *uio, int flag) { struct tty *tp; KASSERT(devsw(dev)->d_flags & D_TTY, ("ttywrite() called on non D_TTY device (%s)", devtoname(dev))); tp = dev->si_tty; KASSERT(tp != NULL, ("ttywrite(): no tty pointer on device (%s)", devtoname(dev))); if (tp == NULL) return (ENODEV); return (ttyld_write(tp, uio, flag)); } int ttyioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) { struct tty *tp; int error; tp = dev->si_tty; error = ttyld_ioctl(tp, cmd, data, flag, td); if (error == ENOIOCTL) error = ttioctl(tp, cmd, data, flag); if (error != ENOIOCTL) return (error); return (ENOTTY); } int ttyldoptim(struct tty *tp) { struct termios *t; t = &tp->t_termios; if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON)) && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK)) && (!(t->c_iflag & PARMRK) || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK)) && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) && linesw[tp->t_line]->l_rint == ttyinput) tp->t_state |= TS_CAN_BYPASS_L_RINT; else tp->t_state &= ~TS_CAN_BYPASS_L_RINT; - return (linesw[tp->t_line]->l_hotchar); + return (tp->t_hotchar); } /* * Record the relationship between the serial ports notion of modem control * signals and the one used in certain ioctls in a way the compiler can enforce * XXX: We should define TIOCM_* in terms of SER_ if we can limit the * XXX: consequences of the #include work that would take. */ CTASSERT(SER_DTR == TIOCM_DTR / 2); CTASSERT(SER_RTS == TIOCM_RTS / 2); CTASSERT(SER_STX == TIOCM_ST / 2); CTASSERT(SER_SRX == TIOCM_SR / 2); CTASSERT(SER_CTS == TIOCM_CTS / 2); CTASSERT(SER_DCD == TIOCM_DCD / 2); CTASSERT(SER_RI == TIOCM_RI / 2); CTASSERT(SER_DSR == TIOCM_DSR / 2); Index: head/sys/net/if_sl.c =================================================================== --- head/sys/net/if_sl.c (revision 131129) +++ head/sys/net/if_sl.c (revision 131130) @@ -1,1115 +1,1110 @@ /* * Copyright (c) 1987, 1989, 1992, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)if_sl.c 8.6 (Berkeley) 2/1/94 * $FreeBSD$ */ /* * Serial Line interface * * Rick Adams * Center for Seismic Studies * 1300 N 17th Street, Suite 1450 * Arlington, Virginia 22209 * (703)276-7900 * rick@seismo.ARPA * seismo!rick * * Pounded on heavily by Chris Torek (chris@mimsy.umd.edu, umcp-cs!chris). * N.B.: this belongs in netinet, not net, the way it stands now. * Should have a link-layer type designation, but wouldn't be * backwards-compatible. * * Converted to 4.3BSD Beta by Chris Torek. * Other changes made at Berkeley, based in part on code by Kirk Smith. * W. Jolitz added slip abort. * * Hacked almost beyond recognition by Van Jacobson (van@helios.ee.lbl.gov). * Added priority queuing for "interactive" traffic; hooks for TCP * header compression; ICMP filtering (at 2400 baud, some cretin * pinging you can use up all your bandwidth). Made low clist behavior * more robust and slightly less likely to hang serial line. * Sped up a bunch of things. * * Note that splimp() is used throughout to block both (tty) input * interrupts and network activity; thus, splimp must be >= spltty. */ #include "opt_inet.h" #include "opt_slip.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if INET #include #include #include #include #else #error "Huh? Slip without inet?" #endif #include #include #include #include static MALLOC_DEFINE(M_SL, "sl", "SLIP Interface"); /* * SLRMAX is a hard limit on input packet size. To simplify the code * and improve performance, we require that packets fit in an mbuf * cluster, and if we get a compressed packet, there's enough extra * room to expand the header into a max length tcp/ip header (128 * bytes). So, SLRMAX can be at most * MCLBYTES - 128 * * SLMTU is the default transmit MTU. The transmit MTU should be kept * small enough so that interactive use doesn't suffer, but large * enough to provide good performance. 552 is a good choice for SLMTU * because it is high enough to not fragment TCP packets being routed * through this host. Packet fragmentation is bad with SLIP because * fragment headers aren't compressed. The previous assumptions about * the best MTU value don't really hold when using modern modems with * BTLZ data compression because the modem buffers play a much larger * role in interactive performance than the MTU. The MTU can be changed * at any time to suit the specific environment with ifconfig(8), and * its maximum value is defined as SLTMAX. SLTMAX must not be so large * that it would overflow the stack if BPF is configured (XXX; if_ppp.c * handles this better). * * SLIP_HIWAT is the amount of data that will be queued 'downstream' * of us (i.e., in clists waiting to be picked up by the tty output * interrupt). If we queue a lot of data downstream, it's immune to * our t.o.s. queuing. * E.g., if SLIP_HIWAT is 1024, the interactive traffic in mixed * telnet/ftp will see a 1 sec wait, independent of the mtu (the * wait is dependent on the ftp window size but that's typically * 1k - 4k). So, we want SLIP_HIWAT just big enough to amortize * the cost (in idle time on the wire) of the tty driver running * off the end of its clists & having to call back slstart for a * new packet. For a tty interface with any buffering at all, this * cost will be zero. Even with a totally brain dead interface (like * the one on a typical workstation), the cost will be <= 1 character * time. So, setting SLIP_HIWAT to ~100 guarantees that we'll lose * at most 1% while maintaining good interactive response. */ #define BUFOFFSET (128+sizeof(struct ifnet **)+SLIP_HDRLEN) #define SLRMAX (MCLBYTES - BUFOFFSET) #define SLBUFSIZE (SLRMAX + BUFOFFSET) #ifndef SLMTU #define SLMTU 552 /* default MTU */ #endif #define SLTMAX 1500 /* maximum MTU */ #define SLIP_HIWAT roundup(50,CBSIZE) #define CLISTRESERVE 1024 /* Can't let clists get too low */ /* * SLIP ABORT ESCAPE MECHANISM: * (inspired by HAYES modem escape arrangement) * 1sec escape 1sec escape 1sec escape { 1sec escape 1sec escape } * within window time signals a "soft" exit from slip mode by remote end * if the IFF_DEBUG flag is on. */ #define ABT_ESC '\033' /* can't be t_intr - distant host must know it*/ #define ABT_IDLE 1 /* in seconds - idle before an escape */ #define ABT_COUNT 3 /* count of escapes for abort */ #define ABT_WINDOW (ABT_COUNT*2+2) /* in seconds - time to count */ static LIST_HEAD(sl_list, sl_softc) sl_list; #define FRAME_END 0xc0 /* Frame End */ #define FRAME_ESCAPE 0xdb /* Frame Esc */ #define TRANS_FRAME_END 0xdc /* transposed frame end */ #define TRANS_FRAME_ESCAPE 0xdd /* transposed frame esc */ static int slisstatic(int); static void slmarkstatic(int); static struct sl_softc *slcreate(void); static void sldestroy(struct sl_softc *sc); static struct mbuf *sl_btom(struct sl_softc *, int); static timeout_t sl_keepalive; static timeout_t sl_outfill; static l_close_t slclose; static l_rint_t slinput; static l_ioctl_t sltioctl; static int slioctl(struct ifnet *, u_long, caddr_t); static int slopen(struct cdev *, struct tty *); static int sloutput(struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *); static int slstart(struct tty *); static struct linesw slipdisc = { slopen, slclose, l_noread, l_nowrite, - sltioctl, slinput, slstart, ttymodem, - FRAME_END + sltioctl, slinput, slstart, ttymodem }; /* * Called from boot code to establish sl interfaces. */ static int sl_modevent(module_t mod, int type, void *data) { switch (type) { case MOD_LOAD: ldisc_register(SLIPDISC, &slipdisc); LIST_INIT(&sl_list); break; case MOD_UNLOAD: ldisc_deregister(SLIPDISC); printf("if_sl module unload - not possible for this module type\n"); return EINVAL; } return 0; } static moduledata_t sl_mod = { "if_sl", sl_modevent, 0 }; DECLARE_MODULE(if_sl, sl_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); static int *st_unit_list; static size_t st_unit_max = 0; static int slisunitfree(int unit) { struct sl_softc *nc; LIST_FOREACH(nc, &sl_list, sl_next) { if (nc->sc_if.if_dunit == unit) return (0); } return (1); } static int slisstatic(unit) int unit; { size_t i; for (i = 0; i < st_unit_max; i++) if (st_unit_list[i] == unit) return 1; return 0; } static void slmarkstatic(unit) int unit; { int *t; if (slisstatic(unit)) return; MALLOC(t, int *, sizeof(int) * (st_unit_max+1), M_SL, M_NOWAIT); if (t == NULL) return; if (st_unit_list) { bcopy(st_unit_list, t, sizeof(int) * st_unit_max); free(st_unit_list, M_SL); } st_unit_list = t; st_unit_list[st_unit_max] = unit; st_unit_max++; } static struct sl_softc * slcreate() { struct sl_softc *sc; int unit; struct mbuf *m; MALLOC(sc, struct sl_softc *, sizeof(*sc), M_SL, M_WAITOK | M_ZERO); m = m_gethdr(M_TRYWAIT, MT_DATA); if (m != NULL) { MCLGET(m, M_TRYWAIT); if ((m->m_flags & M_EXT) == 0) { m_free(m); m = NULL; } } if (m == NULL) { printf("sl: can't allocate buffer\n"); free(sc, M_SL); return (NULL); } sc->sc_ep = mtod(m, u_char *) + SLBUFSIZE; sc->sc_mbuf = m; sc->sc_buf = sc->sc_ep - SLRMAX; sc->sc_mp = sc->sc_buf; sl_compress_init(&sc->sc_comp, -1); sc->sc_if.if_softc = sc; sc->sc_if.if_mtu = SLMTU; sc->sc_if.if_flags = #ifdef SLIP_IFF_OPTS SLIP_IFF_OPTS; #else IFF_POINTOPOINT | SC_AUTOCOMP | IFF_MULTICAST; #endif sc->sc_if.if_type = IFT_SLIP; sc->sc_if.if_ioctl = slioctl; sc->sc_if.if_output = sloutput; sc->sc_if.if_snd.ifq_maxlen = 50; sc->sc_fastq.ifq_maxlen = 32; sc->sc_if.if_linkmib = sc; sc->sc_if.if_linkmiblen = sizeof *sc; mtx_init(&sc->sc_fastq.ifq_mtx, "sl_fastq", NULL, MTX_DEF); /* * Find a suitable unit number. */ for (unit=0; ; unit++) { if (slisstatic(unit)) continue; if (!slisunitfree(unit)) continue; break; } if_initname(&sc->sc_if, "sl", unit); LIST_INSERT_HEAD(&sl_list, sc, sl_next); if_attach(&sc->sc_if); bpfattach(&sc->sc_if, DLT_SLIP, SLIP_HDRLEN); return sc; } /* * Line specific open routine. * Attach the given tty to the first available sl unit. */ /* ARGSUSED */ static int slopen(dev, tp) struct cdev *dev; register struct tty *tp; { register struct sl_softc *sc; int s, error; error = suser(curthread); if (error) return (error); - if (tp->t_line == SLIPDISC) - return (0); - if ((sc = slcreate()) == NULL) return (ENOBUFS); + tp->t_hotchar = FRAME_END; tp->t_sc = (caddr_t)sc; sc->sc_ttyp = tp; sc->sc_if.if_baudrate = tp->t_ospeed; ttyflush(tp, FREAD | FWRITE); - tp->t_line = SLIPDISC; /* * We don't use t_canq or t_rawq, so reduce their * cblock resources to 0. Reserve enough cblocks * for t_outq to guarantee that we can fit a full * packet if the SLIP_HIWAT check allows slstart() * to loop. Use the same value for the cblock * limit since the reserved blocks should always * be enough. Reserving cblocks probably makes * the CLISTRESERVE check unnecessary and wasteful. */ clist_alloc_cblocks(&tp->t_canq, 0, 0); clist_alloc_cblocks(&tp->t_outq, SLIP_HIWAT + 2 * sc->sc_if.if_mtu + 1, SLIP_HIWAT + 2 * sc->sc_if.if_mtu + 1); clist_alloc_cblocks(&tp->t_rawq, 0, 0); s = splnet(); if_up(&sc->sc_if); splx(s); return (0); } static void sldestroy(struct sl_softc *sc) { bpfdetach(&sc->sc_if); if_detach(&sc->sc_if); LIST_REMOVE(sc, sl_next); m_free(sc->sc_mbuf); mtx_destroy(&sc->sc_fastq.ifq_mtx); if (sc->bpfbuf) free(sc->bpfbuf, M_SL); free(sc, M_SL); } /* * Line specific close routine. * Detach the tty from the sl unit. */ static int slclose(tp,flag) struct tty *tp; int flag; { register struct sl_softc *sc; int s; ttyflush(tp, FREAD | FWRITE); /* * XXX the placement of the following spl is misleading. tty * interrupts must be blocked across line discipline switches * and throughout closes to avoid races. */ s = splimp(); /* actually, max(spltty, splnet) */ clist_free_cblocks(&tp->t_outq); - tp->t_line = 0; sc = (struct sl_softc *)tp->t_sc; if (sc != NULL) { if (sc->sc_outfill) { sc->sc_outfill = 0; untimeout(sl_outfill, sc, sc->sc_ofhandle); } if (sc->sc_keepalive) { sc->sc_keepalive = 0; untimeout(sl_keepalive, sc, sc->sc_kahandle); } if_down(&sc->sc_if); sc->sc_ttyp = NULL; tp->t_sc = NULL; sldestroy(sc); } splx(s); return 0; } /* * Line specific (tty) ioctl routine. * Provide a way to get the sl unit number. */ /* ARGSUSED */ static int sltioctl(tp, cmd, data, flag, td) struct tty *tp; u_long cmd; caddr_t data; int flag; struct thread *td; { struct sl_softc *sc = (struct sl_softc *)tp->t_sc; int s, unit, wasup; s = splimp(); switch (cmd) { case SLIOCGUNIT: *(int *)data = sc->sc_if.if_dunit; break; case SLIOCSUNIT: unit = *(u_int *)data; if (unit < 0) { splx(s); return (ENXIO); } if (sc->sc_if.if_dunit != unit) { if (!slisunitfree(unit)) { splx(s); return (ENXIO); } wasup = sc->sc_if.if_flags & IFF_UP; bpfdetach(&sc->sc_if); if_detach(&sc->sc_if); LIST_REMOVE(sc, sl_next); if_initname(&sc->sc_if, "sl", unit); LIST_INSERT_HEAD(&sl_list, sc, sl_next); if_attach(&sc->sc_if); bpfattach(&sc->sc_if, DLT_SLIP, SLIP_HDRLEN); if (wasup) if_up(&sc->sc_if); else if_down(&sc->sc_if); clist_alloc_cblocks(&tp->t_outq, SLIP_HIWAT + 2 * sc->sc_if.if_mtu + 1, SLIP_HIWAT + 2 * sc->sc_if.if_mtu + 1); } slmarkstatic(unit); break; case SLIOCSKEEPAL: sc->sc_keepalive = *(u_int *)data * hz; if (sc->sc_keepalive) { sc->sc_flags |= SC_KEEPALIVE; sc->sc_kahandle = timeout(sl_keepalive, sc, sc->sc_keepalive); } else { if ((sc->sc_flags & SC_KEEPALIVE) != 0) { untimeout(sl_keepalive, sc, sc->sc_kahandle); sc->sc_flags &= ~SC_KEEPALIVE; } } break; case SLIOCGKEEPAL: *(int *)data = sc->sc_keepalive / hz; break; case SLIOCSOUTFILL: sc->sc_outfill = *(u_int *)data * hz; if (sc->sc_outfill) { sc->sc_flags |= SC_OUTWAIT; sc->sc_ofhandle = timeout(sl_outfill, sc, sc->sc_outfill); } else { if ((sc->sc_flags & SC_OUTWAIT) != 0) { untimeout(sl_outfill, sc, sc->sc_ofhandle); sc->sc_flags &= ~SC_OUTWAIT; } } break; case SLIOCGOUTFILL: *(int *)data = sc->sc_outfill / hz; break; default: splx(s); return (ENOIOCTL); } splx(s); return (0); } /* * Queue a packet. Start transmission if not active. * Compression happens in slstart; if we do it here, IP TOS * will cause us to not compress "background" packets, because * ordering gets trashed. It can be done for all packets in slstart. */ static int sloutput(ifp, m, dst, rtp) struct ifnet *ifp; register struct mbuf *m; struct sockaddr *dst; struct rtentry *rtp; { register struct sl_softc *sc = ifp->if_softc; register struct ip *ip; int s, error; /* * `Cannot happen' (see slioctl). Someday we will extend * the line protocol to support other address families. */ if (dst->sa_family != AF_INET) { if_printf(ifp, "af%d not supported\n", dst->sa_family); m_freem(m); sc->sc_if.if_noproto++; return (EAFNOSUPPORT); } if (sc->sc_ttyp == NULL || !(ifp->if_flags & IFF_UP)) { m_freem(m); return (ENETDOWN); } if ((sc->sc_ttyp->t_state & TS_CONNECTED) == 0) { m_freem(m); return (EHOSTUNREACH); } ip = mtod(m, struct ip *); if (sc->sc_if.if_flags & SC_NOICMP && ip->ip_p == IPPROTO_ICMP) { m_freem(m); return (ENETRESET); /* XXX ? */ } if (ip->ip_tos & IPTOS_LOWDELAY && !ALTQ_IS_ENABLED(&sc->sc_if.if_snd)) error = !(IF_HANDOFF(&sc->sc_fastq, m, NULL)); else IFQ_HANDOFF(&sc->sc_if, m, error); if (error) { sc->sc_if.if_oerrors++; return (ENOBUFS); } s = splimp(); if (sc->sc_ttyp->t_outq.c_cc == 0) slstart(sc->sc_ttyp); splx(s); return (0); } /* * Start output on interface. Get another datagram * to send from the interface queue and map it to * the interface before starting output. */ static int slstart(tp) register struct tty *tp; { register struct sl_softc *sc = (struct sl_softc *)tp->t_sc; register struct mbuf *m; register u_char *cp; register struct ip *ip; int s; register int len = 0; for (;;) { /* * Call output process whether or not there is more in the * output queue. We are being called in lieu of ttstart * and must do what it would. */ (*tp->t_oproc)(tp); if (tp->t_outq.c_cc != 0) { if (sc != NULL) sc->sc_flags &= ~SC_OUTWAIT; if (tp->t_outq.c_cc > SLIP_HIWAT) return 0; } /* * This happens briefly when the line shuts down. */ if (sc == NULL) return 0; /* * Get a packet and send it to the interface. */ s = splimp(); IF_DEQUEUE(&sc->sc_fastq, m); if (m) sc->sc_if.if_omcasts++; /* XXX */ else IF_DEQUEUE(&sc->sc_if.if_snd, m); splx(s); if (m == NULL) return 0; /* * We do the header compression here rather than in sloutput * because the packets will be out of order if we are using TOS * queueing, and the connection id compression will get * munged when this happens. */ if (sc->sc_if.if_bpf) { /* * We need to save the TCP/IP header before it's * compressed. To avoid complicated code, we just * copy the entire packet into a stack buffer (since * this is a serial line, packets should be short * and/or the copy should be negligible cost compared * to the packet transmission time). */ register struct mbuf *m1 = m; register u_char *cp; if (sc->bpfbuf == NULL) MALLOC(sc->bpfbuf, u_char *, SLTMAX + SLIP_HDRLEN, M_SL, M_NOWAIT); if (sc->bpfbuf) { cp = sc->bpfbuf + SLIP_HDRLEN; len = 0; do { register int mlen = m1->m_len; bcopy(mtod(m1, caddr_t), cp, mlen); cp += mlen; len += mlen; } while ((m1 = m1->m_next) != NULL); } } ip = mtod(m, struct ip *); if (ip->ip_v == IPVERSION && ip->ip_p == IPPROTO_TCP) { if (sc->sc_if.if_flags & SC_COMPRESS) *mtod(m, u_char *) |= sl_compress_tcp(m, ip, &sc->sc_comp, 1); } if (sc->sc_if.if_bpf && sc->bpfbuf) { /* * Put the SLIP pseudo-"link header" in place. The * compressed header is now at the beginning of the * mbuf. */ sc->bpfbuf[SLX_DIR] = SLIPDIR_OUT; bcopy(mtod(m, caddr_t), &sc->bpfbuf[SLX_CHDR], CHDR_LEN); BPF_TAP(&sc->sc_if, sc->bpfbuf, len + SLIP_HDRLEN); } /* * If system is getting low on clists, just flush our * output queue (if the stuff was important, it'll get * retransmitted). Note that SLTMAX is used instead of * the current if_mtu setting because connections that * have already been established still use the original * (possibly larger) mss. */ if (cfreecount < CLISTRESERVE + SLTMAX) { m_freem(m); sc->sc_if.if_collisions++; continue; } sc->sc_flags &= ~SC_OUTWAIT; /* * The extra FRAME_END will start up a new packet, and thus * will flush any accumulated garbage. We do this whenever * the line may have been idle for some time. */ if (tp->t_outq.c_cc == 0) { ++sc->sc_if.if_obytes; (void) putc(FRAME_END, &tp->t_outq); } while (m) { register u_char *ep; cp = mtod(m, u_char *); ep = cp + m->m_len; while (cp < ep) { /* * Find out how many bytes in the string we can * handle without doing something special. */ register u_char *bp = cp; while (cp < ep) { switch (*cp++) { case FRAME_ESCAPE: case FRAME_END: --cp; goto out; } } out: if (cp > bp) { /* * Put n characters at once * into the tty output queue. */ if (b_to_q((char *)bp, cp - bp, &tp->t_outq)) break; sc->sc_if.if_obytes += cp - bp; } /* * If there are characters left in the mbuf, * the first one must be special.. * Put it out in a different form. */ if (cp < ep) { if (putc(FRAME_ESCAPE, &tp->t_outq)) break; if (putc(*cp++ == FRAME_ESCAPE ? TRANS_FRAME_ESCAPE : TRANS_FRAME_END, &tp->t_outq)) { (void) unputc(&tp->t_outq); break; } sc->sc_if.if_obytes += 2; } } m = m_free(m); } if (putc(FRAME_END, &tp->t_outq)) { /* * Not enough room. Remove a char to make room * and end the packet normally. * If you get many collisions (more than one or two * a day) you probably do not have enough clists * and you should increase "nclist" in param.c. */ (void) unputc(&tp->t_outq); (void) putc(FRAME_END, &tp->t_outq); sc->sc_if.if_collisions++; } else { ++sc->sc_if.if_obytes; sc->sc_if.if_opackets++; } } return 0; } /* * Copy data buffer to mbuf chain; add ifnet pointer. */ static struct mbuf * sl_btom(sc, len) register struct sl_softc *sc; register int len; { struct mbuf *m, *newm; MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == NULL) return (NULL); /* * If we have more than MHLEN bytes, it's cheaper to * queue the cluster we just filled & allocate a new one * for the input buffer. Otherwise, fill the mbuf we * allocated above. Note that code in the input routine * guarantees that packet will fit in a cluster. */ if (len >= MHLEN) { MCLGET(m, M_DONTWAIT); if ((m->m_flags & M_EXT) == 0) { /* * we couldn't get a cluster - if memory's this * low, it's time to start dropping packets. */ (void) m_free(m); return (NULL); } /* Swap the new and old clusters */ newm = m; m = sc->sc_mbuf; sc->sc_mbuf = newm; sc->sc_ep = mtod(newm, u_char *) + SLBUFSIZE; m->m_data = (caddr_t)sc->sc_buf; } else bcopy((caddr_t)sc->sc_buf, mtod(m, caddr_t), len); m->m_len = len; m->m_pkthdr.len = len; m->m_pkthdr.rcvif = &sc->sc_if; return (m); } /* * tty interface receiver interrupt. */ static int slinput(c, tp) register int c; register struct tty *tp; { register struct sl_softc *sc; register struct mbuf *m; register int len; u_char chdr[CHDR_LEN]; tk_nin++; sc = (struct sl_softc *)tp->t_sc; if (sc == NULL) return 0; if (c & TTY_ERRORMASK || (tp->t_state & TS_CONNECTED) == 0) { sc->sc_flags |= SC_ERROR; return 0; } c &= TTY_CHARMASK; ++sc->sc_if.if_ibytes; if (sc->sc_if.if_flags & IFF_DEBUG) { if (c == ABT_ESC) { /* * If we have a previous abort, see whether * this one is within the time limit. */ if (sc->sc_abortcount && time_second >= sc->sc_starttime + ABT_WINDOW) sc->sc_abortcount = 0; /* * If we see an abort after "idle" time, count it; * record when the first abort escape arrived. */ if (time_second >= sc->sc_lasttime + ABT_IDLE) { if (++sc->sc_abortcount == 1) sc->sc_starttime = time_second; if (sc->sc_abortcount >= ABT_COUNT) { slclose(tp,0); return 0; } } } else sc->sc_abortcount = 0; sc->sc_lasttime = time_second; } switch (c) { case TRANS_FRAME_ESCAPE: if (sc->sc_escape) c = FRAME_ESCAPE; break; case TRANS_FRAME_END: if (sc->sc_escape) c = FRAME_END; break; case FRAME_ESCAPE: sc->sc_escape = 1; return 0; case FRAME_END: sc->sc_flags &= ~SC_KEEPALIVE; if(sc->sc_flags & SC_ERROR) { sc->sc_flags &= ~SC_ERROR; goto newpack; } len = sc->sc_mp - sc->sc_buf; if (len < 3) /* less than min length packet - ignore */ goto newpack; if (sc->sc_if.if_bpf) { /* * Save the compressed header, so we * can tack it on later. Note that we * will end up copying garbage in some * cases but this is okay. We remember * where the buffer started so we can * compute the new header length. */ bcopy(sc->sc_buf, chdr, CHDR_LEN); } if ((c = (*sc->sc_buf & 0xf0)) != (IPVERSION << 4)) { if (c & 0x80) c = TYPE_COMPRESSED_TCP; else if (c == TYPE_UNCOMPRESSED_TCP) *sc->sc_buf &= 0x4f; /* XXX */ /* * We've got something that's not an IP packet. * If compression is enabled, try to decompress it. * Otherwise, if `auto-enable' compression is on and * it's a reasonable packet, decompress it and then * enable compression. Otherwise, drop it. */ if (sc->sc_if.if_flags & SC_COMPRESS) { len = sl_uncompress_tcp(&sc->sc_buf, len, (u_int)c, &sc->sc_comp); if (len <= 0) goto error; } else if ((sc->sc_if.if_flags & SC_AUTOCOMP) && c == TYPE_UNCOMPRESSED_TCP && len >= 40) { len = sl_uncompress_tcp(&sc->sc_buf, len, (u_int)c, &sc->sc_comp); if (len <= 0) goto error; sc->sc_if.if_flags |= SC_COMPRESS; } else goto error; } if (sc->sc_if.if_bpf) { /* * Put the SLIP pseudo-"link header" in place. * We couldn't do this any earlier since * decompression probably moved the buffer * pointer. Then, invoke BPF. */ register u_char *hp = sc->sc_buf - SLIP_HDRLEN; hp[SLX_DIR] = SLIPDIR_IN; bcopy(chdr, &hp[SLX_CHDR], CHDR_LEN); BPF_TAP(&sc->sc_if, hp, len + SLIP_HDRLEN); } m = sl_btom(sc, len); if (m == NULL) goto error; sc->sc_if.if_ipackets++; if ((sc->sc_if.if_flags & IFF_UP) == 0) { m_freem(m); goto newpack; } if (! netisr_queue(NETISR_IP, m)) { sc->sc_if.if_ierrors++; sc->sc_if.if_iqdrops++; } goto newpack; } if (sc->sc_mp < sc->sc_ep) { *sc->sc_mp++ = c; sc->sc_escape = 0; return 0; } /* can't put lower; would miss an extra frame */ sc->sc_flags |= SC_ERROR; error: sc->sc_if.if_ierrors++; newpack: sc->sc_mp = sc->sc_buf = sc->sc_ep - SLRMAX; sc->sc_escape = 0; return 0; } /* * Process an ioctl request. */ static int slioctl(ifp, cmd, data) register struct ifnet *ifp; u_long cmd; caddr_t data; { register struct ifaddr *ifa = (struct ifaddr *)data; register struct ifreq *ifr = (struct ifreq *)data; register int s, error = 0; struct sl_softc *sc = ifp->if_softc; s = splimp(); switch (cmd) { case SIOCSIFFLAGS: /* * if.c will set the interface up even if we * don't want it to. */ if (sc->sc_ttyp == NULL) { ifp->if_flags &= ~IFF_UP; } break; case SIOCSIFADDR: /* * This is "historical" - set the interface up when * setting the address. */ if (ifa->ifa_addr->sa_family == AF_INET) { if (sc->sc_ttyp != NULL) ifp->if_flags |= IFF_UP; } else { error = EAFNOSUPPORT; } break; case SIOCSIFDSTADDR: if (ifa->ifa_addr->sa_family != AF_INET) error = EAFNOSUPPORT; break; case SIOCADDMULTI: case SIOCDELMULTI: break; case SIOCSIFMTU: /* * Set the interface MTU. */ if (ifr->ifr_mtu > SLTMAX) error = EINVAL; else { struct tty *tp; ifp->if_mtu = ifr->ifr_mtu; tp = sc->sc_ttyp; if (tp != NULL) clist_alloc_cblocks(&tp->t_outq, SLIP_HIWAT + 2 * ifp->if_mtu + 1, SLIP_HIWAT + 2 * ifp->if_mtu + 1); } break; default: error = EINVAL; } splx(s); return (error); } static void sl_keepalive(chan) void *chan; { struct sl_softc *sc = chan; if (sc->sc_keepalive) { if (sc->sc_flags & SC_KEEPALIVE) { if (sc->sc_ttyp->t_pgrp != NULL) { PGRP_LOCK(sc->sc_ttyp->t_pgrp); pgsignal (sc->sc_ttyp->t_pgrp, SIGURG, 1); PGRP_UNLOCK(sc->sc_ttyp->t_pgrp); } } else sc->sc_flags |= SC_KEEPALIVE; sc->sc_kahandle = timeout(sl_keepalive, sc, sc->sc_keepalive); } else { sc->sc_flags &= ~SC_KEEPALIVE; } } static void sl_outfill(chan) void *chan; { struct sl_softc *sc = chan; register struct tty *tp = sc->sc_ttyp; int s; if (sc->sc_outfill && tp != NULL) { if (sc->sc_flags & SC_OUTWAIT) { s = splimp (); ++sc->sc_if.if_obytes; (void) putc(FRAME_END, &tp->t_outq); (*tp->t_oproc)(tp); splx (s); } else sc->sc_flags |= SC_OUTWAIT; sc->sc_ofhandle = timeout(sl_outfill, sc, sc->sc_outfill); } else { sc->sc_flags &= ~SC_OUTWAIT; } } Index: head/sys/net/ppp_tty.c =================================================================== --- head/sys/net/ppp_tty.c (revision 131129) +++ head/sys/net/ppp_tty.c (revision 131130) @@ -1,1099 +1,1091 @@ /* * ppp_tty.c - Point-to-Point Protocol (PPP) driver for asynchronous * tty devices. * * Copyright (c) 1989 Carnegie Mellon University. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by Carnegie Mellon University. The name of the * University may not be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * Drew D. Perkins * Carnegie Mellon University * 4910 Forbes Ave. * Pittsburgh, PA 15213 * (412) 268-8576 * ddp@andrew.cmu.edu * * Based on: * @(#)if_sl.c 7.6.1.2 (Berkeley) 2/15/89 * * Copyright (c) 1987 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by the University of California, Berkeley. The name of the * University may not be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * Serial Line interface * * Rick Adams * Center for Seismic Studies * 1300 N 17th Street, Suite 1450 * Arlington, Virginia 22209 * (703)276-7900 * rick@seismo.ARPA * seismo!rick * * Pounded on heavily by Chris Torek (chris@mimsy.umd.edu, umcp-cs!chris). * Converted to 4.3BSD Beta by Chris Torek. * Other changes made at Berkeley, based in part on code by Kirk Smith. * * Converted to 4.3BSD+ 386BSD by Brad Parker (brad@cayman.com) * Added VJ tcp header compression; more unified ioctls * * Extensively modified by Paul Mackerras (paulus@cs.anu.edu.au). * Cleaned up a lot of the mbuf-related code to fix bugs that * caused system crashes and packet corruption. Changed pppstart * so that it doesn't just give up with a "collision" if the whole * packet doesn't fit in the output ring buffer. * * Added priority queueing for interactive IP packets, following * the model of if_sl.c, plus hooks for bpf. * Paul Mackerras (paulus@cs.anu.edu.au). */ /* $FreeBSD$ */ #include "opt_ppp.h" /* XXX for ppp_defs.h */ #define VJC /* XXX for ppp_defs.h */ #include #include #include #include #include #include #include #include #include #include #ifdef PPP_FILTER #include #endif #include #include static int pppopen(struct cdev *dev, struct tty *tp); static int pppclose(struct tty *tp, int flag); static int pppread(struct tty *tp, struct uio *uio, int flag); static int pppwrite(struct tty *tp, struct uio *uio, int flag); static int ppptioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, struct thread *td); static int pppinput(int c, struct tty *tp); static int pppstart(struct tty *tp); static u_short pppfcs(u_short fcs, u_char *cp, int len); static void pppasyncstart(struct ppp_softc *); static void pppasyncctlp(struct ppp_softc *); static void pppasyncrelinq(struct ppp_softc *); static void pppasyncsetmtu(struct ppp_softc *); static void ppp_timeout(void *); static void pppgetm(struct ppp_softc *sc); static void ppplogchar(struct ppp_softc *, int); /* XXX called from if_ppp.c - layering violation */ void pppasyncattach(void *); void pppasyncdetach(void); /* * Some useful mbuf macros not in mbuf.h. */ #define M_IS_CLUSTER(m) ((m)->m_flags & M_EXT) #define M_DATASTART(m) \ (M_IS_CLUSTER(m) ? (m)->m_ext.ext_buf : \ (m)->m_flags & M_PKTHDR ? (m)->m_pktdat : (m)->m_dat) #define M_DATASIZE(m) \ (M_IS_CLUSTER(m) ? (m)->m_ext.ext_size : \ (m)->m_flags & M_PKTHDR ? MHLEN: MLEN) /* * Does c need to be escaped? */ #define ESCAPE_P(c) (sc->sc_asyncmap[(c) >> 5] & (1 << ((c) & 0x1F))) /* * Procedures for using an async tty interface for PPP. */ /* This is a FreeBSD-2.X kernel. */ #define CCOUNT(q) ((q)->c_cc) #define PPP_LOWAT 100 /* Process more output when < LOWAT on queue */ #define PPP_HIWAT 400 /* Don't start a new packet if HIWAT on que */ /* * Define the PPP line discipline. */ static struct linesw pppdisc = { pppopen, pppclose, pppread, pppwrite, - ppptioctl, pppinput, pppstart, ttymodem, - PPP_FLAG + ppptioctl, pppinput, pppstart, ttymodem }; void pppasyncattach(dummy) void *dummy; { ldisc_register(PPPDISC, &pppdisc); } void pppasyncdetach() { ldisc_deregister(PPPDISC); } /* * Line specific open routine for async tty devices. * Attach the given tty to the first available ppp unit. * Called from device open routine or ttioctl() at >= splsofttty() */ /* ARGSUSED */ static int pppopen(dev, tp) struct cdev *dev; register struct tty *tp; { struct thread *td = curthread; /* XXX */ register struct ppp_softc *sc; int error, s; if ((error = suser(td)) != 0) return (error); s = spltty(); - if (tp->t_line == PPPDISC) { - sc = (struct ppp_softc *) tp->t_sc; - if (sc != NULL && sc->sc_devp == (void *) tp) { - splx(s); - return (0); - } - } + tp->t_hotchar = PPP_FLAG; if ((sc = pppalloc(td->td_proc->p_pid)) == NULL) { splx(s); return ENXIO; } if (sc->sc_relinq) (*sc->sc_relinq)(sc); /* get previous owner to relinquish the unit */ sc->sc_ilen = 0; sc->sc_m = NULL; bzero(sc->sc_asyncmap, sizeof(sc->sc_asyncmap)); sc->sc_asyncmap[0] = 0xffffffff; sc->sc_asyncmap[3] = 0x60000000; sc->sc_rasyncmap = 0; sc->sc_devp = (void *) tp; sc->sc_start = pppasyncstart; sc->sc_ctlp = pppasyncctlp; sc->sc_relinq = pppasyncrelinq; sc->sc_setmtu = pppasyncsetmtu; sc->sc_outm = NULL; pppgetm(sc); sc->sc_if.if_flags |= IFF_RUNNING; getmicrotime(&sc->sc_if.if_lastchange); sc->sc_if.if_baudrate = tp->t_ospeed; tp->t_sc = (caddr_t) sc; ttyflush(tp, FREAD | FWRITE); /* * Pre-allocate cblocks to the "just right" amount. The 1 byte t_canq * allocation helps avoid the need for select and/or FIONREAD. * We also pass 1 byte tokens through t_canq... */ clist_alloc_cblocks(&tp->t_canq, 1, 1); clist_alloc_cblocks(&tp->t_outq, sc->sc_if.if_mtu + PPP_HIWAT, sc->sc_if.if_mtu + PPP_HIWAT); clist_alloc_cblocks(&tp->t_rawq, 0, 0); splx(s); return (0); } /* * Line specific close routine, called from device close routine * and from ttioctl at >= splsofttty(). * Detach the tty from the ppp unit. * Mimics part of ttyclose(). */ static int pppclose(tp, flag) struct tty *tp; int flag; { register struct ppp_softc *sc; int s; s = spltty(); ttyflush(tp, FREAD | FWRITE); clist_free_cblocks(&tp->t_canq); clist_free_cblocks(&tp->t_outq); - tp->t_line = 0; sc = (struct ppp_softc *) tp->t_sc; if (sc != NULL) { tp->t_sc = NULL; if (tp == (struct tty *) sc->sc_devp) { pppasyncrelinq(sc); pppdealloc(sc); } } splx(s); return 0; } /* * Relinquish the interface unit to another device. */ static void pppasyncrelinq(sc) struct ppp_softc *sc; { int s; s = spltty(); if (sc->sc_outm) { m_freem(sc->sc_outm); sc->sc_outm = NULL; } if (sc->sc_m) { m_freem(sc->sc_m); sc->sc_m = NULL; } if (sc->sc_flags & SC_TIMEOUT) { untimeout(ppp_timeout, (void *) sc, sc->sc_ch); sc->sc_flags &= ~SC_TIMEOUT; } splx(s); } /* * This gets called from the upper layer to notify a mtu change */ static void pppasyncsetmtu(sc) register struct ppp_softc *sc; { register struct tty *tp = (struct tty *) sc->sc_devp; int s; s = spltty(); if (tp != NULL) clist_alloc_cblocks(&tp->t_outq, sc->sc_if.if_mtu + PPP_HIWAT, sc->sc_if.if_mtu + PPP_HIWAT); splx(s); } /* * Line specific (tty) read routine. * called at zero spl from the device driver in the response to user-level * reads on the tty file descriptor (ie: pppd). */ static int pppread(tp, uio, flag) register struct tty *tp; struct uio *uio; int flag; { register struct ppp_softc *sc = (struct ppp_softc *)tp->t_sc; struct mbuf *m, *m0; register int s; int error = 0; if (sc == NULL) return 0; /* * Loop waiting for input, checking that nothing disasterous * happens in the meantime. */ s = spltty(); for (;;) { if (tp != (struct tty *) sc->sc_devp || tp->t_line != PPPDISC) { splx(s); return 0; } if (sc->sc_inq.ifq_head != NULL) break; if ((tp->t_state & TS_CONNECTED) == 0) { splx(s); return 0; /* end of file */ } if (tp->t_state & TS_ASYNC || flag & IO_NDELAY) { splx(s); return (EWOULDBLOCK); } error = ttysleep(tp, TSA_HUP_OR_INPUT(tp), TTIPRI | PCATCH, "pppin", 0); if (error) { splx(s); return error; } } /* Pull place-holder byte out of canonical queue */ getc(&tp->t_canq); /* Get the packet from the input queue */ IF_DEQUEUE(&sc->sc_inq, m0); splx(s); for (m = m0; m && uio->uio_resid; m = m->m_next) if ((error = uiomove(mtod(m, u_char *), m->m_len, uio)) != 0) break; m_freem(m0); return (error); } /* * Line specific (tty) write routine. * called at zero spl from the device driver in the response to user-level * writes on the tty file descriptor (ie: pppd). */ static int pppwrite(tp, uio, flag) register struct tty *tp; struct uio *uio; int flag; { register struct ppp_softc *sc = (struct ppp_softc *)tp->t_sc; struct mbuf *m, *m0, **mp; struct sockaddr dst; int len, error, s; if ((tp->t_state & TS_CONNECTED) == 0) return 0; /* wrote 0 bytes */ if (tp->t_line != PPPDISC) return (EINVAL); if (sc == NULL || tp != (struct tty *) sc->sc_devp) return EIO; if (uio->uio_resid > sc->sc_if.if_mtu + PPP_HDRLEN || uio->uio_resid < PPP_HDRLEN) return (EMSGSIZE); s = spltty(); for (mp = &m0; uio->uio_resid; mp = &m->m_next) { MGET(m, M_TRYWAIT, MT_DATA); if ((*mp = m) == NULL) { m_freem(m0); splx(s); return (ENOBUFS); } m->m_len = 0; if (uio->uio_resid >= MCLBYTES / 2) MCLGET(m, M_DONTWAIT); len = M_TRAILINGSPACE(m); if (len > uio->uio_resid) len = uio->uio_resid; if ((error = uiomove(mtod(m, u_char *), len, uio)) != 0) { m_freem(m0); splx(s); return (error); } m->m_len = len; } dst.sa_family = AF_UNSPEC; bcopy(mtod(m0, u_char *), dst.sa_data, PPP_HDRLEN); m0->m_data += PPP_HDRLEN; m0->m_len -= PPP_HDRLEN; /* call the upper layer to "transmit" it... */ error = pppoutput(&sc->sc_if, m0, &dst, (struct rtentry *)0); splx(s); return (error); } /* * Line specific (tty) ioctl routine. * This discipline requires that tty device drivers call * the line specific l_ioctl routine from their ioctl routines. */ /* ARGSUSED */ static int ppptioctl(tp, cmd, data, flag, td) struct tty *tp; u_long cmd; caddr_t data; int flag; struct thread *td; { struct ppp_softc *sc = (struct ppp_softc *) tp->t_sc; int error, s; if (sc == NULL || tp != (struct tty *) sc->sc_devp) return (ENOIOCTL); error = 0; switch (cmd) { case PPPIOCSASYNCMAP: if ((error = suser(td)) != 0) break; sc->sc_asyncmap[0] = *(u_int *)data; break; case PPPIOCGASYNCMAP: *(u_int *)data = sc->sc_asyncmap[0]; break; case PPPIOCSRASYNCMAP: if ((error = suser(td)) != 0) break; sc->sc_rasyncmap = *(u_int *)data; break; case PPPIOCGRASYNCMAP: *(u_int *)data = sc->sc_rasyncmap; break; case PPPIOCSXASYNCMAP: if ((error = suser(td)) != 0) break; s = spltty(); bcopy(data, sc->sc_asyncmap, sizeof(sc->sc_asyncmap)); sc->sc_asyncmap[1] = 0; /* mustn't escape 0x20 - 0x3f */ sc->sc_asyncmap[2] &= ~0x40000000; /* mustn't escape 0x5e */ sc->sc_asyncmap[3] |= 0x60000000; /* must escape 0x7d, 0x7e */ splx(s); break; case PPPIOCGXASYNCMAP: bcopy(sc->sc_asyncmap, data, sizeof(sc->sc_asyncmap)); break; default: error = pppioctl(sc, cmd, data, flag, td); if (error == 0 && cmd == PPPIOCSMRU) pppgetm(sc); } return error; } /* * FCS lookup table as calculated by genfcstab. */ static u_short fcstab[256] = { 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 }; /* * Calculate a new FCS given the current FCS and the new data. */ static u_short pppfcs(u_short fcs, u_char *cp, int len) { while (len--) fcs = PPP_FCS(fcs, *cp++); return (fcs); } /* * This gets called at splsoftnet from if_ppp.c at various times * when there is data ready to be sent. */ static void pppasyncstart(sc) register struct ppp_softc *sc; { register struct tty *tp = (struct tty *) sc->sc_devp; register struct mbuf *m; register int len; register u_char *start, *stop, *cp; int n, ndone, done, idle; int s; idle = 0; /* XXX assumes atomic access to *tp although we're not at spltty(). */ while (CCOUNT(&tp->t_outq) < PPP_HIWAT) { /* * See if we have an existing packet partly sent. * If not, get a new packet and start sending it. */ m = sc->sc_outm; if (m == NULL) { /* * Get another packet to be sent. */ m = ppp_dequeue(sc); if (m == NULL) { idle = 1; break; } /* * The extra PPP_FLAG will start up a new packet, and thus * will flush any accumulated garbage. We do this whenever * the line may have been idle for some time. */ /* XXX as above. */ if (CCOUNT(&tp->t_outq) == 0) { ++sc->sc_stats.ppp_obytes; (void) putc(PPP_FLAG, &tp->t_outq); } /* Calculate the FCS for the first mbuf's worth. */ sc->sc_outfcs = pppfcs(PPP_INITFCS, mtod(m, u_char *), m->m_len); getmicrotime(&sc->sc_if.if_lastchange); } for (;;) { start = mtod(m, u_char *); len = m->m_len; stop = start + len; while (len > 0) { /* * Find out how many bytes in the string we can * handle without doing something special. */ for (cp = start; cp < stop; cp++) if (ESCAPE_P(*cp)) break; n = cp - start; if (n) { /* NetBSD (0.9 or later), 4.3-Reno or similar. */ ndone = n - b_to_q(start, n, &tp->t_outq); len -= ndone; start += ndone; sc->sc_stats.ppp_obytes += ndone; if (ndone < n) break; /* packet doesn't fit */ } /* * If there are characters left in the mbuf, * the first one must be special. * Put it out in a different form. */ if (len) { s = spltty(); if (putc(PPP_ESCAPE, &tp->t_outq)) { splx(s); break; } if (putc(*start ^ PPP_TRANS, &tp->t_outq)) { (void) unputc(&tp->t_outq); splx(s); break; } splx(s); sc->sc_stats.ppp_obytes += 2; start++; len--; } } /* * If we didn't empty this mbuf, remember where we're up to. * If we emptied the last mbuf, try to add the FCS and closing * flag, and if we can't, leave sc_outm pointing to m, but with * m->m_len == 0, to remind us to output the FCS and flag later. */ done = len == 0; if (done && m->m_next == NULL) { u_char *p, *q; int c; u_char endseq[8]; /* * We may have to escape the bytes in the FCS. */ p = endseq; c = ~sc->sc_outfcs & 0xFF; if (ESCAPE_P(c)) { *p++ = PPP_ESCAPE; *p++ = c ^ PPP_TRANS; } else *p++ = c; c = (~sc->sc_outfcs >> 8) & 0xFF; if (ESCAPE_P(c)) { *p++ = PPP_ESCAPE; *p++ = c ^ PPP_TRANS; } else *p++ = c; *p++ = PPP_FLAG; /* * Try to output the FCS and flag. If the bytes * don't all fit, back out. */ s = spltty(); for (q = endseq; q < p; ++q) if (putc(*q, &tp->t_outq)) { done = 0; for (; q > endseq; --q) unputc(&tp->t_outq); break; } splx(s); if (done) sc->sc_stats.ppp_obytes += q - endseq; } if (!done) { /* remember where we got to */ m->m_data = start; m->m_len = len; break; } /* Finished with this mbuf; free it and move on. */ m = m_free(m); if (m == NULL) { /* Finished a packet */ break; } sc->sc_outfcs = pppfcs(sc->sc_outfcs, mtod(m, u_char *), m->m_len); } /* * If m == NULL, we have finished a packet. * If m != NULL, we've either done as much work this time * as we need to, or else we've filled up the output queue. */ sc->sc_outm = m; if (m) break; } /* Call pppstart to start output again if necessary. */ s = spltty(); pppstart(tp); /* * This timeout is needed for operation on a pseudo-tty, * because the pty code doesn't call pppstart after it has * drained the t_outq. */ if (!idle && (sc->sc_flags & SC_TIMEOUT) == 0) { sc->sc_ch = timeout(ppp_timeout, (void *) sc, 1); sc->sc_flags |= SC_TIMEOUT; } splx(s); } /* * This gets called when a received packet is placed on * the inq, at splsoftnet. The pppd daemon is to be woken up to do a read(). */ static void pppasyncctlp(sc) struct ppp_softc *sc; { struct tty *tp; int s; /* Put a placeholder byte in canq for ttselect()/ttnread(). */ s = spltty(); tp = (struct tty *) sc->sc_devp; putc(0, &tp->t_canq); ttwakeup(tp); splx(s); } /* * Start output on async tty interface. If the transmit queue * has drained sufficiently, arrange for pppasyncstart to be * called later at splsoftnet. * Called at spltty or higher. */ static int pppstart(tp) register struct tty *tp; { register struct ppp_softc *sc = (struct ppp_softc *) tp->t_sc; /* * Call output process whether or not there is any output. * We are being called in lieu of ttstart and must do what it would. */ if (tp->t_oproc != NULL) (*tp->t_oproc)(tp); /* * If the transmit queue has drained and the tty has not hung up * or been disconnected from the ppp unit, then tell if_ppp.c that * we need more output. */ if (CCOUNT(&tp->t_outq) < PPP_LOWAT && !((tp->t_state & TS_CONNECTED) == 0) && sc != NULL && tp == (struct tty *) sc->sc_devp) { ppp_restart(sc); } return 0; } /* * Timeout routine - try to start some more output. */ static void ppp_timeout(x) void *x; { struct ppp_softc *sc = (struct ppp_softc *) x; struct tty *tp = (struct tty *) sc->sc_devp; int s; s = spltty(); sc->sc_flags &= ~SC_TIMEOUT; pppstart(tp); splx(s); } /* * Allocate enough mbuf to handle current MRU. */ static void pppgetm(sc) register struct ppp_softc *sc; { struct mbuf *m, **mp; int len; mp = &sc->sc_m; for (len = sc->sc_mru + PPP_HDRLEN + PPP_FCSLEN; len > 0; ){ if ((m = *mp) == NULL) { MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == NULL) break; *mp = m; MCLGET(m, M_DONTWAIT); } len -= M_DATASIZE(m); mp = &m->m_next; } } /* * tty interface receiver interrupt. */ static unsigned paritytab[8] = { 0x96696996, 0x69969669, 0x69969669, 0x96696996, 0x69969669, 0x96696996, 0x96696996, 0x69969669 }; /* * Called when character is available from device driver. * Only guaranteed to be at splsofttty() or spltty() * This is safe to be called while the upper half's netisr is preempted. */ static int pppinput(c, tp) int c; register struct tty *tp; { register struct ppp_softc *sc; struct mbuf *m; int ilen, s; sc = (struct ppp_softc *) tp->t_sc; if (sc == NULL || tp != (struct tty *) sc->sc_devp) return 0; ++tk_nin; ++sc->sc_stats.ppp_ibytes; if ((tp->t_state & TS_CONNECTED) == 0) { if (sc->sc_flags & SC_DEBUG) if_printf(&sc->sc_if, "no carrier\n"); goto flush; } if (c & TTY_ERRORMASK) { /* framing error or overrun on this char - abort packet */ if (sc->sc_flags & SC_DEBUG) if_printf(&sc->sc_if, "line error %x\n", c & TTY_ERRORMASK); goto flush; } c &= TTY_CHARMASK; /* * Handle software flow control of output. */ if (tp->t_iflag & IXON) { if (c == tp->t_cc[VSTOP] && tp->t_cc[VSTOP] != _POSIX_VDISABLE) { if ((tp->t_state & TS_TTSTOP) == 0) { tp->t_state |= TS_TTSTOP; tp->t_stop(tp, 0); } return 0; } if (c == tp->t_cc[VSTART] && tp->t_cc[VSTART] != _POSIX_VDISABLE) { tp->t_state &= ~TS_TTSTOP; if (tp->t_oproc != NULL) (*tp->t_oproc)(tp); return 0; } } s = spltty(); if (c & 0x80) sc->sc_flags |= SC_RCV_B7_1; else sc->sc_flags |= SC_RCV_B7_0; if (paritytab[c >> 5] & (1 << (c & 0x1F))) sc->sc_flags |= SC_RCV_ODDP; else sc->sc_flags |= SC_RCV_EVNP; splx(s); if (sc->sc_flags & SC_LOG_RAWIN) ppplogchar(sc, c); if (c == PPP_FLAG) { ilen = sc->sc_ilen; sc->sc_ilen = 0; if (sc->sc_rawin_count > 0) ppplogchar(sc, -1); /* * If SC_ESCAPED is set, then we've seen the packet * abort sequence "}~". */ if (sc->sc_flags & (SC_FLUSH | SC_ESCAPED) || (ilen > 0 && sc->sc_fcs != PPP_GOODFCS)) { s = spltty(); sc->sc_flags |= SC_PKTLOST; /* note the dropped packet */ if ((sc->sc_flags & (SC_FLUSH | SC_ESCAPED)) == 0){ if (sc->sc_flags & SC_DEBUG) if_printf(&sc->sc_if, "bad fcs %x, pkt len %d\n", sc->sc_fcs, ilen); sc->sc_if.if_ierrors++; sc->sc_stats.ppp_ierrors++; } else sc->sc_flags &= ~(SC_FLUSH | SC_ESCAPED); splx(s); return 0; } if (ilen < PPP_HDRLEN + PPP_FCSLEN) { if (ilen) { if (sc->sc_flags & SC_DEBUG) if_printf(&sc->sc_if, "too short (%d)\n", ilen); s = spltty(); sc->sc_if.if_ierrors++; sc->sc_stats.ppp_ierrors++; sc->sc_flags |= SC_PKTLOST; splx(s); } return 0; } /* * Remove FCS trailer. Somewhat painful... */ ilen -= 2; if (--sc->sc_mc->m_len == 0) { for (m = sc->sc_m; m->m_next != sc->sc_mc; m = m->m_next) ; sc->sc_mc = m; } sc->sc_mc->m_len--; /* excise this mbuf chain */ m = sc->sc_m; sc->sc_m = sc->sc_mc->m_next; sc->sc_mc->m_next = NULL; ppppktin(sc, m, sc->sc_flags & SC_PKTLOST); if (sc->sc_flags & SC_PKTLOST) { s = spltty(); sc->sc_flags &= ~SC_PKTLOST; splx(s); } pppgetm(sc); return 0; } if (sc->sc_flags & SC_FLUSH) { if (sc->sc_flags & SC_LOG_FLUSH) ppplogchar(sc, c); return 0; } if (c < 0x20 && (sc->sc_rasyncmap & (1 << c))) return 0; s = spltty(); if (sc->sc_flags & SC_ESCAPED) { sc->sc_flags &= ~SC_ESCAPED; c ^= PPP_TRANS; } else if (c == PPP_ESCAPE) { sc->sc_flags |= SC_ESCAPED; splx(s); return 0; } splx(s); /* * Initialize buffer on first octet received. * First octet could be address or protocol (when compressing * address/control). * Second octet is control. * Third octet is first or second (when compressing protocol) * octet of protocol. * Fourth octet is second octet of protocol. */ if (sc->sc_ilen == 0) { /* reset the first input mbuf */ if (sc->sc_m == NULL) { pppgetm(sc); if (sc->sc_m == NULL) { if (sc->sc_flags & SC_DEBUG) if_printf(&sc->sc_if, "no input mbufs!\n"); goto flush; } } m = sc->sc_m; m->m_len = 0; m->m_data = M_DATASTART(sc->sc_m); sc->sc_mc = m; sc->sc_mp = mtod(m, char *); sc->sc_fcs = PPP_INITFCS; if (c != PPP_ALLSTATIONS) { if (sc->sc_flags & SC_REJ_COMP_AC) { if (sc->sc_flags & SC_DEBUG) if_printf(&sc->sc_if, "garbage received: 0x%x (need 0xFF)\n", c); goto flush; } *sc->sc_mp++ = PPP_ALLSTATIONS; *sc->sc_mp++ = PPP_UI; sc->sc_ilen += 2; m->m_len += 2; } } if (sc->sc_ilen == 1 && c != PPP_UI) { if (sc->sc_flags & SC_DEBUG) if_printf(&sc->sc_if, "missing UI (0x3), got 0x%x\n", c); goto flush; } if (sc->sc_ilen == 2 && (c & 1) == 1) { /* a compressed protocol */ *sc->sc_mp++ = 0; sc->sc_ilen++; sc->sc_mc->m_len++; } if (sc->sc_ilen == 3 && (c & 1) == 0) { if (sc->sc_flags & SC_DEBUG) if_printf(&sc->sc_if, "bad protocol %x\n", (sc->sc_mp[-1] << 8) + c); goto flush; } /* packet beyond configured mru? */ if (++sc->sc_ilen > sc->sc_mru + PPP_HDRLEN + PPP_FCSLEN) { if (sc->sc_flags & SC_DEBUG) if_printf(&sc->sc_if, "packet too big\n"); goto flush; } /* is this mbuf full? */ m = sc->sc_mc; if (M_TRAILINGSPACE(m) <= 0) { if (m->m_next == NULL) { pppgetm(sc); if (m->m_next == NULL) { if (sc->sc_flags & SC_DEBUG) if_printf(&sc->sc_if, "too few input mbufs!\n"); goto flush; } } sc->sc_mc = m = m->m_next; m->m_len = 0; m->m_data = M_DATASTART(m); sc->sc_mp = mtod(m, char *); } ++m->m_len; *sc->sc_mp++ = c; sc->sc_fcs = PPP_FCS(sc->sc_fcs, c); return 0; flush: if (!(sc->sc_flags & SC_FLUSH)) { s = spltty(); sc->sc_if.if_ierrors++; sc->sc_stats.ppp_ierrors++; sc->sc_flags |= SC_FLUSH; splx(s); if (sc->sc_flags & SC_LOG_FLUSH) ppplogchar(sc, c); } return 0; } #define MAX_DUMP_BYTES 128 static void ppplogchar(sc, c) struct ppp_softc *sc; int c; { if (c >= 0) sc->sc_rawin[sc->sc_rawin_count++] = c; if (sc->sc_rawin_count >= sizeof(sc->sc_rawin) || (c < 0 && sc->sc_rawin_count > 0)) { printf("%s input: %*D", sc->sc_if.if_xname, sc->sc_rawin_count, sc->sc_rawin, " "); sc->sc_rawin_count = 0; } } Index: head/sys/netgraph/bluetooth/drivers/h4/ng_h4.c =================================================================== --- head/sys/netgraph/bluetooth/drivers/h4/ng_h4.c (revision 131129) +++ head/sys/netgraph/bluetooth/drivers/h4/ng_h4.c (revision 131130) @@ -1,1058 +1,1049 @@ /* * ng_h4.c * * Copyright (c) 2001-2002 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: ng_h4.c,v 1.5 2003/05/10 05:51:25 max Exp $ * $FreeBSD$ * * Based on: * --------- * * FreeBSD: src/sys/netgraph/ng_tty.c * Author: Archie Cobbs * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /***************************************************************************** ***************************************************************************** ** This node implements a Bluetooth HCI UART transport layer as per chapter ** H4 of the Bluetooth Specification Book v1.1. It is a terminal line ** discipline that is also a netgraph node. Installing this line discipline ** on a terminal device instantiates a new netgraph node of this type, which ** allows access to the device via the "hook" hook of the node. ** ** Once the line discipline is installed, you can find out the name of the ** corresponding netgraph node via a NGIOCGINFO ioctl(). ***************************************************************************** *****************************************************************************/ /* MALLOC define */ #ifndef NG_SEPARATE_MALLOC MALLOC_DEFINE(M_NETGRAPH_H4, "netgraph_h4", "Netgraph Bluetooth H4 node"); #else #define M_NETGRAPH_H4 M_NETGRAPH #endif /* NG_SEPARATE_MALLOC */ /* Line discipline methods */ static int ng_h4_open (struct cdev *, struct tty *); static int ng_h4_close (struct tty *, int); static int ng_h4_read (struct tty *, struct uio *, int); static int ng_h4_write (struct tty *, struct uio *, int); static int ng_h4_input (int, struct tty *); static int ng_h4_start (struct tty *); static void ng_h4_start2 (node_p, hook_p, void *, int); static int ng_h4_ioctl (struct tty *, u_long, caddr_t, int, struct thread *); /* Line discipline descriptor */ static struct linesw ng_h4_disc = { ng_h4_open, /* open */ ng_h4_close, /* close */ ng_h4_read, /* read */ ng_h4_write, /* write */ ng_h4_ioctl, /* ioctl */ ng_h4_input, /* input */ ng_h4_start, /* start */ - ttymodem, /* modem */ - 0 /* hotchar (don't really care which one) */ + ttymodem /* modem */ }; /* Netgraph methods */ static ng_constructor_t ng_h4_constructor; static ng_rcvmsg_t ng_h4_rcvmsg; static ng_shutdown_t ng_h4_shutdown; static ng_newhook_t ng_h4_newhook; static ng_connect_t ng_h4_connect; static ng_rcvdata_t ng_h4_rcvdata; static ng_disconnect_t ng_h4_disconnect; /* Other stuff */ static void ng_h4_timeout (node_p); static void ng_h4_untimeout (node_p); static void ng_h4_queue_timeout (void *); static void ng_h4_process_timeout (node_p, hook_p, void *, int); static int ng_h4_mod_event (module_t, int, void *); /* Netgraph node type descriptor */ static struct ng_type typestruct = { .version = NG_ABI_VERSION, .name = NG_H4_NODE_TYPE, .mod_event = ng_h4_mod_event, .constructor = ng_h4_constructor, .rcvmsg = ng_h4_rcvmsg, .shutdown = ng_h4_shutdown, .newhook = ng_h4_newhook, .connect = ng_h4_connect, .rcvdata = ng_h4_rcvdata, .disconnect = ng_h4_disconnect, .cmdlist = ng_h4_cmdlist }; NETGRAPH_INIT(h4, &typestruct); MODULE_VERSION(ng_h4, NG_BLUETOOTH_VERSION); static int ng_h4_node = 0; /***************************************************************************** ***************************************************************************** ** Line discipline methods ***************************************************************************** *****************************************************************************/ /* * Set our line discipline on the tty. */ static int ng_h4_open(struct cdev *dev, struct tty *tp) { char name[NG_NODESIZ]; ng_h4_info_p sc = NULL; int s, error; /* Super-user only */ error = suser(curthread); /* XXX */ if (error != 0) return (error); s = splnet(); /* XXX */ spltty(); /* XXX */ - /* Already installed? */ - if (tp->t_line == H4DISC) { - sc = (ng_h4_info_p) tp->t_sc; - if (sc != NULL && sc->tp == tp) - goto out; - } - /* Initialize private struct */ MALLOC(sc, ng_h4_info_p, sizeof(*sc), M_NETGRAPH_H4, M_NOWAIT|M_ZERO); if (sc == NULL) { error = ENOMEM; goto out; } sc->tp = tp; sc->debug = NG_H4_WARN_LEVEL; sc->state = NG_H4_W4_PKT_IND; sc->want = 1; sc->got = 0; NG_BT_MBUFQ_INIT(&sc->outq, NG_H4_DEFAULTQLEN); callout_handle_init(&sc->timo); /* Setup netgraph node */ error = ng_make_node_common(&typestruct, &sc->node); if (error != 0) { bzero(sc, sizeof(*sc)); FREE(sc, M_NETGRAPH_H4); goto out; } /* Assign node its name */ snprintf(name, sizeof(name), "%s%d", typestruct.name, ng_h4_node ++); error = ng_name_node(sc->node, name); if (error != 0) { NG_H4_ALERT("%s: %s - node name exists?\n", __func__, name); NG_NODE_UNREF(sc->node); bzero(sc, sizeof(*sc)); FREE(sc, M_NETGRAPH_H4); goto out; } /* Set back pointers */ NG_NODE_SET_PRIVATE(sc->node, sc); tp->t_sc = (caddr_t) sc; /* The node has to be a WRITER because data can change node status */ NG_NODE_FORCE_WRITER(sc->node); /* * Pre-allocate cblocks to the an appropriate amount. * I'm not sure what is appropriate. */ ttyflush(tp, FREAD | FWRITE); clist_alloc_cblocks(&tp->t_canq, 0, 0); clist_alloc_cblocks(&tp->t_rawq, 0, 0); clist_alloc_cblocks(&tp->t_outq, MLEN + NG_H4_HIWATER, MLEN + NG_H4_HIWATER); out: splx(s); /* XXX */ return (error); } /* ng_h4_open */ /* * Line specific close routine, called from device close routine * and from ttioctl. This causes the node to be destroyed as well. */ static int ng_h4_close(struct tty *tp, int flag) { ng_h4_info_p sc = (ng_h4_info_p) tp->t_sc; int s; s = spltty(); /* XXX */ ttyflush(tp, FREAD | FWRITE); clist_free_cblocks(&tp->t_outq); - tp->t_line = 0; if (sc != NULL) { tp->t_sc = NULL; if (sc->node != NULL) { if (sc->flags & NG_H4_TIMEOUT) ng_h4_untimeout(sc->node); NG_NODE_SET_PRIVATE(sc->node, NULL); ng_rmnode_self(sc->node); sc->node = NULL; } NG_BT_MBUFQ_DESTROY(&sc->outq); bzero(sc, sizeof(*sc)); FREE(sc, M_NETGRAPH_H4); } splx(s); /* XXX */ return (0); } /* ng_h4_close */ /* * Once the device has been turned into a node, we don't allow reading. */ static int ng_h4_read(struct tty *tp, struct uio *uio, int flag) { return (EIO); } /* ng_h4_read */ /* * Once the device has been turned into a node, we don't allow writing. */ static int ng_h4_write(struct tty *tp, struct uio *uio, int flag) { return (EIO); } /* ng_h4_write */ /* * We implement the NGIOCGINFO ioctl() defined in ng_message.h. */ static int ng_h4_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, struct thread *td) { ng_h4_info_p sc = (ng_h4_info_p) tp->t_sc; int s, error = 0; s = spltty(); /* XXX */ switch (cmd) { case NGIOCGINFO: #undef NI #define NI(x) ((struct nodeinfo *)(x)) bzero(data, sizeof(*NI(data))); if (NG_NODE_HAS_NAME(sc->node)) strncpy(NI(data)->name, NG_NODE_NAME(sc->node), sizeof(NI(data)->name) - 1); strncpy(NI(data)->type, sc->node->nd_type->name, sizeof(NI(data)->type) - 1); NI(data)->id = (u_int32_t) ng_node2ID(sc->node); NI(data)->hooks = NG_NODE_NUMHOOKS(sc->node); break; default: error = ENOIOCTL; break; } splx(s); /* XXX */ return (error); } /* ng_h4_ioctl */ /* * Receive data coming from the device. We get one character at a time, which * is kindof silly. */ static int ng_h4_input(int c, struct tty *tp) { ng_h4_info_p sc = (ng_h4_info_p) tp->t_sc; if (sc == NULL || tp != sc->tp || sc->node == NULL || NG_NODE_NOT_VALID(sc->node)) return (0); /* Check for error conditions */ if ((sc->tp->t_state & TS_CONNECTED) == 0) { NG_H4_INFO("%s: %s - no carrier\n", __func__, NG_NODE_NAME(sc->node)); sc->state = NG_H4_W4_PKT_IND; sc->want = 1; sc->got = 0; return (0); /* XXX Loss of synchronization here! */ } /* Check for framing error or overrun on this char */ if (c & TTY_ERRORMASK) { NG_H4_ERR("%s: %s - line error %#x, c=%#x\n", __func__, NG_NODE_NAME(sc->node), c & TTY_ERRORMASK, c & TTY_CHARMASK); NG_H4_STAT_IERROR(sc->stat); sc->state = NG_H4_W4_PKT_IND; sc->want = 1; sc->got = 0; return (0); /* XXX Loss of synchronization here! */ } NG_H4_STAT_BYTES_RECV(sc->stat, 1); /* Append char to mbuf */ if (sc->got >= sizeof(sc->ibuf)) { NG_H4_ALERT("%s: %s - input buffer overflow, c=%#x, got=%d\n", __func__, NG_NODE_NAME(sc->node), c & TTY_CHARMASK, sc->got); NG_H4_STAT_IERROR(sc->stat); sc->state = NG_H4_W4_PKT_IND; sc->want = 1; sc->got = 0; return (0); /* XXX Loss of synchronization here! */ } sc->ibuf[sc->got ++] = (c & TTY_CHARMASK); NG_H4_INFO("%s: %s - got char %#x, want=%d, got=%d\n", __func__, NG_NODE_NAME(sc->node), c, sc->want, sc->got); if (sc->got < sc->want) return (0); /* Wait for more */ switch (sc->state) { /* Got packet indicator */ case NG_H4_W4_PKT_IND: NG_H4_INFO("%s: %s - got packet indicator %#x\n", __func__, NG_NODE_NAME(sc->node), sc->ibuf[0]); sc->state = NG_H4_W4_PKT_HDR; /* * Since packet indicator included in the packet header * just set sc->want to sizeof(packet header). */ switch (sc->ibuf[0]) { case NG_HCI_ACL_DATA_PKT: sc->want = sizeof(ng_hci_acldata_pkt_t); break; case NG_HCI_SCO_DATA_PKT: sc->want = sizeof(ng_hci_scodata_pkt_t); break; case NG_HCI_EVENT_PKT: sc->want = sizeof(ng_hci_event_pkt_t); break; default: NG_H4_WARN("%s: %s - ignoring unknown packet " \ "type=%#x\n", __func__, NG_NODE_NAME(sc->node), sc->ibuf[0]); NG_H4_STAT_IERROR(sc->stat); sc->state = NG_H4_W4_PKT_IND; sc->want = 1; sc->got = 0; break; } break; /* Got packet header */ case NG_H4_W4_PKT_HDR: sc->state = NG_H4_W4_PKT_DATA; switch (sc->ibuf[0]) { case NG_HCI_ACL_DATA_PKT: c = le16toh(((ng_hci_acldata_pkt_t *) (sc->ibuf))->length); break; case NG_HCI_SCO_DATA_PKT: c = ((ng_hci_scodata_pkt_t *)(sc->ibuf))->length; break; case NG_HCI_EVENT_PKT: c = ((ng_hci_event_pkt_t *)(sc->ibuf))->length; break; default: KASSERT((0), ("Invalid packet type=%#x\n", sc->ibuf[0])); break; } NG_H4_INFO("%s: %s - got packet header, packet type=%#x, " \ "packet size=%d, payload size=%d\n", __func__, NG_NODE_NAME(sc->node), sc->ibuf[0], sc->got, c); if (c > 0) { sc->want += c; /* * Try to prevent possible buffer overrun * * XXX I'm *really* confused here. It turns out * that Xircom card sends us packets with length * greater then 512 bytes! This is greater then * our old receive buffer (ibuf) size. In the same * time the card demands from us *not* to send * packets greater then 192 bytes. Weird! How the * hell i should know how big *receive* buffer * should be? For now increase receiving buffer * size to 1K and add the following check. */ if (sc->want >= sizeof(sc->ibuf)) { int b; NG_H4_ALERT("%s: %s - packet too big for " \ "buffer, type=%#x, got=%d, want=%d, " \ "length=%d\n", __func__, NG_NODE_NAME(sc->node), sc->ibuf[0], sc->got, sc->want, c); NG_H4_ALERT("Packet header:\n"); for (b = 0; b < sc->got; b++) NG_H4_ALERT("%#x ", sc->ibuf[b]); NG_H4_ALERT("\n"); /* Reset state */ NG_H4_STAT_IERROR(sc->stat); sc->state = NG_H4_W4_PKT_IND; sc->want = 1; sc->got = 0; } break; } /* else FALLTHROUGH and deliver frame */ /* XXX Is this true? Should we deliver empty frame? */ /* Got packet data */ case NG_H4_W4_PKT_DATA: NG_H4_INFO("%s: %s - got full packet, packet type=%#x, " \ "packet size=%d\n", __func__, NG_NODE_NAME(sc->node), sc->ibuf[0], sc->got); if (sc->hook != NULL && NG_HOOK_IS_VALID(sc->hook)) { struct mbuf *m = NULL; MGETHDR(m, M_DONTWAIT, MT_DATA); if (m != NULL) { m->m_pkthdr.len = 0; /* XXX m_copyback() is stupid */ m->m_len = min(MHLEN, sc->got); m_copyback(m, 0, sc->got, sc->ibuf); NG_SEND_DATA_ONLY(c, sc->hook, m); } else { NG_H4_ERR("%s: %s - could not get mbuf\n", __func__, NG_NODE_NAME(sc->node)); NG_H4_STAT_IERROR(sc->stat); } } sc->state = NG_H4_W4_PKT_IND; sc->want = 1; sc->got = 0; NG_H4_STAT_PCKTS_RECV(sc->stat); break; default: KASSERT((0), ("Invalid H4 node state=%d", sc->state)); break; } return (0); } /* ng_h4_input */ /* * This is called when the device driver is ready for more output. Called from * tty system. */ static int ng_h4_start(struct tty *tp) { ng_h4_info_p sc = (ng_h4_info_p) tp->t_sc; if (sc == NULL || tp != sc->tp || sc->node == NULL || NG_NODE_NOT_VALID(sc->node)) return (0); return (ng_send_fn(sc->node, NULL, ng_h4_start2, NULL, 0)); } /* ng_h4_start */ /* * Device driver is ready for more output. Part 2. Called (via ng_send_fn) * ng_h4_start() and from ng_h4_rcvdata() when a new mbuf is available for * output. */ static void ng_h4_start2(node_p node, hook_p hook, void *arg1, int arg2) { ng_h4_info_p sc = (ng_h4_info_p) NG_NODE_PRIVATE(node); struct mbuf *m = NULL; int s, size; s = spltty(); /* XXX */ #if 0 while (sc->tp->t_outq.c_cc < NG_H4_HIWATER) { /* XXX 2.2 specific ? */ #else while (1) { #endif /* Remove first mbuf from queue */ NG_BT_MBUFQ_DEQUEUE(&sc->outq, m); if (m == NULL) break; /* Send as much of it as possible */ while (m != NULL) { size = m->m_len - b_to_q(mtod(m, u_char *), m->m_len, &sc->tp->t_outq); NG_H4_STAT_BYTES_SENT(sc->stat, size); m->m_data += size; m->m_len -= size; if (m->m_len > 0) break; /* device can't take no more */ m = m_free(m); } /* Put remainder of mbuf chain (if any) back on queue */ if (m != NULL) { NG_BT_MBUFQ_PREPEND(&sc->outq, m); break; } /* Full packet has been sent */ NG_H4_STAT_PCKTS_SENT(sc->stat); } /* * Call output process whether or not there is any output. We are * being called in lieu of ttstart and must do what it would. */ if (sc->tp->t_oproc != NULL) (*sc->tp->t_oproc)(sc->tp); /* * This timeout is needed for operation on a pseudo-tty, because the * pty code doesn't call pppstart after it has drained the t_outq. */ if (NG_BT_MBUFQ_LEN(&sc->outq) > 0 && (sc->flags & NG_H4_TIMEOUT) == 0) ng_h4_timeout(node); splx(s); /* XXX */ } /* ng_h4_start2 */ /***************************************************************************** ***************************************************************************** ** Netgraph node methods ***************************************************************************** *****************************************************************************/ /* * Initialize a new node of this type. We only allow nodes to be created as * a result of setting the line discipline on a tty, so always return an error * if not. */ static int ng_h4_constructor(node_p node) { return (EOPNOTSUPP); } /* ng_h4_constructor */ /* * Add a new hook. There can only be one. */ static int ng_h4_newhook(node_p node, hook_p hook, const char *name) { ng_h4_info_p sc = (ng_h4_info_p) NG_NODE_PRIVATE(node); if (strcmp(name, NG_H4_HOOK) != 0) return (EINVAL); if (sc->hook != NULL) return (EISCONN); sc->hook = hook; return (0); } /* ng_h4_newhook */ /* * Connect hook. Just say yes. */ static int ng_h4_connect(hook_p hook) { ng_h4_info_p sc = (ng_h4_info_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); if (hook != sc->hook) { sc->hook = NULL; return (EINVAL); } NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); return (0); } /* ng_h4_connect */ /* * Disconnect the hook */ static int ng_h4_disconnect(hook_p hook) { ng_h4_info_p sc = (ng_h4_info_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); /* * We need to check for sc != NULL because we can be called from * ng_h4_clsoe() via ng_rmnode_self() */ if (sc != NULL) { if (hook != sc->hook) return (EINVAL); /* XXX do we have to untimeout and drain out queue? */ if (sc->flags & NG_H4_TIMEOUT) ng_h4_untimeout(NG_HOOK_NODE(hook)); NG_BT_MBUFQ_DRAIN(&sc->outq); sc->state = NG_H4_W4_PKT_IND; sc->want = 1; sc->got = 0; sc->hook = NULL; } return (0); } /* ng_h4_disconnect */ /* * Remove this node. The does the netgraph portion of the shutdown. * This should only be called indirectly from ng_h4_close(). */ static int ng_h4_shutdown(node_p node) { ng_h4_info_p sc = (ng_h4_info_p) NG_NODE_PRIVATE(node); char name[NG_NODESIZ]; /* Let old node go */ NG_NODE_SET_PRIVATE(node, NULL); NG_NODE_UNREF(node); /* Check if device was closed */ if (sc == NULL) goto out; /* Setup new netgraph node */ if (ng_make_node_common(&typestruct, &sc->node) != 0) { printf("%s: Unable to create new node!\n", __func__); sc->node = NULL; goto out; } /* Assign node its name */ snprintf(name, sizeof(name), "%s%d", typestruct.name, ng_h4_node ++); if (ng_name_node(sc->node, name) != 0) { printf("%s: %s - node name exists?\n", __func__, name); NG_NODE_UNREF(sc->node); sc->node = NULL; goto out; } /* The node has to be a WRITER because data can change node status */ NG_NODE_FORCE_WRITER(sc->node); NG_NODE_SET_PRIVATE(sc->node, sc); out: return (0); } /* ng_h4_shutdown */ /* * Receive incoming data from Netgraph system. Put it on our * output queue and start output if necessary. */ static int ng_h4_rcvdata(hook_p hook, item_p item) { ng_h4_info_p sc = (ng_h4_info_p)NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); int error = 0; struct mbuf *m = NULL; if (sc == NULL) { error = EHOSTDOWN; goto out; } if (hook != sc->hook) { error = EINVAL; goto out; } NGI_GET_M(item, m); if (NG_BT_MBUFQ_FULL(&sc->outq)) { NG_H4_ERR("%s: %s - dropping mbuf, len=%d\n", __func__, NG_NODE_NAME(sc->node), m->m_pkthdr.len); NG_BT_MBUFQ_DROP(&sc->outq); NG_H4_STAT_OERROR(sc->stat); NG_FREE_M(m); error = ENOBUFS; } else { NG_H4_INFO("%s: %s - queue mbuf, len=%d\n", __func__, NG_NODE_NAME(sc->node), m->m_pkthdr.len); NG_BT_MBUFQ_ENQUEUE(&sc->outq, m); /* * We have lock on the node, so we can call ng_h4_start2() * directly */ ng_h4_start2(sc->node, NULL, NULL, 0); } out: NG_FREE_ITEM(item); return (error); } /* ng_h4_rcvdata */ /* * Receive control message */ static int ng_h4_rcvmsg(node_p node, item_p item, hook_p lasthook) { ng_h4_info_p sc = (ng_h4_info_p) NG_NODE_PRIVATE(node); struct ng_mesg *msg = NULL, *resp = NULL; int error = 0; if (sc == NULL) { error = EHOSTDOWN; goto out; } NGI_GET_MSG(item, msg); switch (msg->header.typecookie) { case NGM_GENERIC_COOKIE: switch (msg->header.cmd) { case NGM_TEXT_STATUS: NG_MKRESPONSE(resp, msg, NG_TEXTRESPONSE, M_NOWAIT); if (resp == NULL) error = ENOMEM; else snprintf(resp->data, NG_TEXTRESPONSE, "Hook: %s\n" \ "Flags: %#x\n" \ "Debug: %d\n" \ "State: %d\n" \ "Queue: [have:%d,max:%d]\n" \ "Input: [got:%d,want:%d]", (sc->hook != NULL)? NG_H4_HOOK : "", sc->flags, sc->debug, sc->state, NG_BT_MBUFQ_LEN(&sc->outq), sc->outq.maxlen, sc->got, sc->want); break; default: error = EINVAL; break; } break; case NGM_H4_COOKIE: switch (msg->header.cmd) { case NGM_H4_NODE_RESET: NG_BT_MBUFQ_DRAIN(&sc->outq); sc->state = NG_H4_W4_PKT_IND; sc->want = 1; sc->got = 0; break; case NGM_H4_NODE_GET_STATE: NG_MKRESPONSE(resp, msg, sizeof(ng_h4_node_state_ep), M_NOWAIT); if (resp == NULL) error = ENOMEM; else *((ng_h4_node_state_ep *)(resp->data)) = sc->state; break; case NGM_H4_NODE_GET_DEBUG: NG_MKRESPONSE(resp, msg, sizeof(ng_h4_node_debug_ep), M_NOWAIT); if (resp == NULL) error = ENOMEM; else *((ng_h4_node_debug_ep *)(resp->data)) = sc->debug; break; case NGM_H4_NODE_SET_DEBUG: if (msg->header.arglen != sizeof(ng_h4_node_debug_ep)) error = EMSGSIZE; else sc->debug = *((ng_h4_node_debug_ep *)(msg->data)); break; case NGM_H4_NODE_GET_QLEN: NG_MKRESPONSE(resp, msg, sizeof(ng_h4_node_qlen_ep), M_NOWAIT); if (resp == NULL) error = ENOMEM; else *((ng_h4_node_qlen_ep *)(resp->data)) = sc->outq.maxlen; break; case NGM_H4_NODE_SET_QLEN: if (msg->header.arglen != sizeof(ng_h4_node_qlen_ep)) error = EMSGSIZE; else if (*((ng_h4_node_qlen_ep *)(msg->data)) <= 0) error = EINVAL; else sc->outq.maxlen = *((ng_h4_node_qlen_ep *)(msg->data)); break; case NGM_H4_NODE_GET_STAT: NG_MKRESPONSE(resp, msg, sizeof(ng_h4_node_stat_ep), M_NOWAIT); if (resp == NULL) error = ENOMEM; else bcopy(&sc->stat, resp->data, sizeof(ng_h4_node_stat_ep)); break; case NGM_H4_NODE_RESET_STAT: NG_H4_STAT_RESET(sc->stat); break; default: error = EINVAL; break; } break; default: error = EINVAL; break; } out: NG_RESPOND_MSG(error, node, item, resp); NG_FREE_MSG(msg); return (error); } /* ng_h4_rcvmsg */ /* * Set timeout */ static void ng_h4_timeout(node_p node) { ng_h4_info_p sc = (ng_h4_info_p) NG_NODE_PRIVATE(node); NG_NODE_REF(node); sc->timo = timeout(ng_h4_queue_timeout, node, 1); sc->flags |= NG_H4_TIMEOUT; } /* ng_h4_timeout */ /* * Unset timeout */ static void ng_h4_untimeout(node_p node) { ng_h4_info_p sc = (ng_h4_info_p) NG_NODE_PRIVATE(node); sc->flags &= ~NG_H4_TIMEOUT; untimeout(ng_h4_queue_timeout, node, sc->timo); NG_NODE_UNREF(node); } /* ng_h4_untimeout */ /* * OK, timeout has happend, so queue function to process it */ static void ng_h4_queue_timeout(void *context) { node_p node = (node_p) context; if (NG_NODE_IS_VALID(node)) ng_send_fn(node, NULL, &ng_h4_process_timeout, NULL, 0); NG_NODE_UNREF(node); } /* ng_h4_queue_timeout */ /* * Timeout processing function. * We still have data to output to the device, so try sending more. */ static void ng_h4_process_timeout(node_p node, hook_p hook, void *arg1, int arg2) { ng_h4_info_p sc = (ng_h4_info_p) NG_NODE_PRIVATE(node); sc->flags &= ~NG_H4_TIMEOUT; /* * We can call ng_h4_start2() directly here because we have lock * on the node. */ ng_h4_start2(node, NULL, NULL, 0); } /* ng_h4_process_timeout */ /* * Handle loading and unloading for this node type */ static int ng_h4_mod_event(module_t mod, int event, void *data) { static int ng_h4_ldisc; int s, error = 0; s = spltty(); /* XXX */ switch (event) { case MOD_LOAD: /* Register line discipline */ ng_h4_ldisc = ldisc_register(H4DISC, &ng_h4_disc); if (ng_h4_ldisc < 0) { printf("%s: can't register H4 line discipline\n", __func__); error = EIO; } break; case MOD_UNLOAD: /* Unregister line discipline */ ldisc_deregister(ng_h4_ldisc); break; default: error = EOPNOTSUPP; break; } splx(s); /* XXX */ return (error); } /* ng_h4_mod_event */ Index: head/sys/netgraph/ng_tty.c =================================================================== --- head/sys/netgraph/ng_tty.c (revision 131129) +++ head/sys/netgraph/ng_tty.c (revision 131130) @@ -1,685 +1,678 @@ /* * ng_tty.c * * Copyright (c) 1996-1999 Whistle Communications, Inc. * All rights reserved. * * Subject to the following obligations and disclaimer of warranty, use and * redistribution of this software, in source or object code forms, with or * without modifications are expressly permitted by Whistle Communications; * provided, however, that: * 1. Any and all reproductions of the source or object code must include the * copyright notice above and the following disclaimer of warranties; and * 2. No rights are granted, in any manner or form, to use Whistle * Communications, Inc. trademarks, including the mark "WHISTLE * COMMUNICATIONS" on advertising, endorsements, or otherwise except as * such appears in the above copyright notice or in the software. * * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * * Author: Archie Cobbs * * $FreeBSD$ * $Whistle: ng_tty.c,v 1.21 1999/11/01 09:24:52 julian Exp $ */ /* * This file implements a terminal line discipline that is also a * netgraph node. Installing this line discipline on a terminal device * instantiates a new netgraph node of this type, which allows access * to the device via the "hook" hook of the node. * * Once the line discipline is installed, you can find out the name * of the corresponding netgraph node via a NGIOCGINFO ioctl(). * * Incoming characters are delievered to the hook one at a time, each * in its own mbuf. You may optionally define a ``hotchar,'' which causes * incoming characters to be buffered up until either the hotchar is * seen or the mbuf is full (MHLEN bytes). Then all buffered characters * are immediately delivered. * * NOTE: This node operates at spltty(). */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Misc defs */ #define MAX_MBUFQ 3 /* Max number of queued mbufs */ #define NGT_HIWATER 400 /* High water mark on output */ /* Per-node private info */ struct ngt_sc { struct tty *tp; /* Terminal device */ node_p node; /* Netgraph node */ hook_p hook; /* Netgraph hook */ struct mbuf *m; /* Incoming data buffer */ struct mbuf *qhead, **qtail; /* Queue of outgoing mbuf's */ short qlen; /* Length of queue */ short hotchar; /* Hotchar, or -1 if none */ u_int flags; /* Flags */ struct callout_handle chand; /* See man timeout(9) */ }; typedef struct ngt_sc *sc_p; /* Flags */ #define FLG_TIMEOUT 0x0001 /* A timeout is pending */ #define FLG_DEBUG 0x0002 /* Debugging */ #ifdef INVARIANTS #define QUEUECHECK(sc) \ do { \ struct mbuf **mp; \ int k; \ \ for (k = 0, mp = &sc->qhead; \ k <= MAX_MBUFQ && *mp; \ k++, mp = &(*mp)->m_nextpkt); \ if (k != sc->qlen || k > MAX_MBUFQ || *mp || mp != sc->qtail) \ panic("%s: queue", __func__); \ } while (0) #else #define QUEUECHECK(sc) do {} while (0) #endif /* Line discipline methods */ static int ngt_open(struct cdev *dev, struct tty *tp); static int ngt_close(struct tty *tp, int flag); static int ngt_read(struct tty *tp, struct uio *uio, int flag); static int ngt_write(struct tty *tp, struct uio *uio, int flag); static int ngt_tioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, struct thread *); static int ngt_input(int c, struct tty *tp); static int ngt_start(struct tty *tp); /* Netgraph methods */ static ng_constructor_t ngt_constructor; static ng_rcvmsg_t ngt_rcvmsg; static ng_shutdown_t ngt_shutdown; static ng_newhook_t ngt_newhook; static ng_connect_t ngt_connect; static ng_rcvdata_t ngt_rcvdata; static ng_disconnect_t ngt_disconnect; static int ngt_mod_event(module_t mod, int event, void *data); /* Other stuff */ static void ngt_timeout(void *arg); #define ERROUT(x) do { error = (x); goto done; } while (0) /* Line discipline descriptor */ static struct linesw ngt_disc = { ngt_open, ngt_close, ngt_read, ngt_write, ngt_tioctl, ngt_input, ngt_start, - ttymodem, - NG_TTY_DFL_HOTCHAR /* XXX can't change this in serial driver */ + ttymodem }; /* Netgraph node type descriptor */ static struct ng_type typestruct = { .version = NG_ABI_VERSION, .name = NG_TTY_NODE_TYPE, .mod_event = ngt_mod_event, .constructor = ngt_constructor, .rcvmsg = ngt_rcvmsg, .shutdown = ngt_shutdown, .newhook = ngt_newhook, .connect = ngt_connect, .rcvdata = ngt_rcvdata, .disconnect = ngt_disconnect, }; NETGRAPH_INIT(tty, &typestruct); static int ngt_unit; static int ngt_nodeop_ok; /* OK to create/remove node */ static int ngt_ldisc; /****************************************************************** LINE DISCIPLINE METHODS ******************************************************************/ /* * Set our line discipline on the tty. * Called from device open routine or ttioctl() at >= splsofttty() */ static int ngt_open(struct cdev *dev, struct tty *tp) { struct thread *const td = curthread; /* XXX */ char name[sizeof(NG_TTY_NODE_TYPE) + 8]; sc_p sc; int s, error; /* Super-user only */ if ((error = suser(td))) return (error); s = splnet(); (void) spltty(); /* XXX is this necessary? */ - /* Already installed? */ - if (tp->t_line == NETGRAPHDISC) { - sc = (sc_p) tp->t_sc; - if (sc != NULL && sc->tp == tp) - goto done; - } + tp->t_hotchar = NG_TTY_DFL_HOTCHAR; /* Initialize private struct */ MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_WAITOK | M_ZERO); if (sc == NULL) { error = ENOMEM; goto done; } sc->tp = tp; sc->hotchar = NG_TTY_DFL_HOTCHAR; sc->qtail = &sc->qhead; QUEUECHECK(sc); callout_handle_init(&sc->chand); /* Setup netgraph node */ ngt_nodeop_ok = 1; error = ng_make_node_common(&typestruct, &sc->node); ngt_nodeop_ok = 0; if (error) { FREE(sc, M_NETGRAPH); goto done; } snprintf(name, sizeof(name), "%s%d", typestruct.name, ngt_unit++); /* Assign node its name */ if ((error = ng_name_node(sc->node, name))) { log(LOG_ERR, "%s: node name exists?\n", name); ngt_nodeop_ok = 1; NG_NODE_UNREF(sc->node); ngt_nodeop_ok = 0; goto done; } /* Set back pointers */ NG_NODE_SET_PRIVATE(sc->node, sc); tp->t_sc = (caddr_t) sc; /* * Pre-allocate cblocks to the an appropriate amount. * I'm not sure what is appropriate. */ ttyflush(tp, FREAD | FWRITE); clist_alloc_cblocks(&tp->t_canq, 0, 0); clist_alloc_cblocks(&tp->t_rawq, 0, 0); clist_alloc_cblocks(&tp->t_outq, MLEN + NGT_HIWATER, MLEN + NGT_HIWATER); done: /* Done */ splx(s); return (error); } /* * Line specific close routine, called from device close routine * and from ttioctl at >= splsofttty(). This causes the node to * be destroyed as well. */ static int ngt_close(struct tty *tp, int flag) { const sc_p sc = (sc_p) tp->t_sc; int s; s = spltty(); ttyflush(tp, FREAD | FWRITE); clist_free_cblocks(&tp->t_outq); - tp->t_line = 0; if (sc != NULL) { if (sc->flags & FLG_TIMEOUT) { untimeout(ngt_timeout, sc, sc->chand); sc->flags &= ~FLG_TIMEOUT; } ngt_nodeop_ok = 1; ng_rmnode_self(sc->node); ngt_nodeop_ok = 0; tp->t_sc = NULL; } splx(s); return (0); } /* * Once the device has been turned into a node, we don't allow reading. */ static int ngt_read(struct tty *tp, struct uio *uio, int flag) { return (EIO); } /* * Once the device has been turned into a node, we don't allow writing. */ static int ngt_write(struct tty *tp, struct uio *uio, int flag) { return (EIO); } /* * We implement the NGIOCGINFO ioctl() defined in ng_message.h. */ static int ngt_tioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, struct thread *td) { const sc_p sc = (sc_p) tp->t_sc; int s, error = 0; s = spltty(); switch (cmd) { case NGIOCGINFO: { struct nodeinfo *const ni = (struct nodeinfo *) data; const node_p node = sc->node; bzero(ni, sizeof(*ni)); if (NG_NODE_HAS_NAME(node)) strncpy(ni->name, NG_NODE_NAME(node), sizeof(ni->name) - 1); strncpy(ni->type, node->nd_type->name, sizeof(ni->type) - 1); ni->id = (u_int32_t) ng_node2ID(node); ni->hooks = NG_NODE_NUMHOOKS(node); break; } default: ERROUT(ENOIOCTL); } done: splx(s); return (error); } /* * Receive data coming from the device. We get one character at * a time, which is kindof silly. * Only guaranteed to be at splsofttty() or spltty(). */ static int ngt_input(int c, struct tty *tp) { const sc_p sc = (sc_p) tp->t_sc; const node_p node = sc->node; struct mbuf *m; int s, error = 0; if (!sc || tp != sc->tp) return (0); s = spltty(); if (!sc->hook) ERROUT(0); /* Check for error conditions */ if ((tp->t_state & TS_CONNECTED) == 0) { if (sc->flags & FLG_DEBUG) log(LOG_DEBUG, "%s: no carrier\n", NG_NODE_NAME(node)); ERROUT(0); } if (c & TTY_ERRORMASK) { /* framing error or overrun on this char */ if (sc->flags & FLG_DEBUG) log(LOG_DEBUG, "%s: line error %x\n", NG_NODE_NAME(node), c & TTY_ERRORMASK); ERROUT(0); } c &= TTY_CHARMASK; /* Get a new header mbuf if we need one */ if (!(m = sc->m)) { MGETHDR(m, M_DONTWAIT, MT_DATA); if (!m) { if (sc->flags & FLG_DEBUG) log(LOG_ERR, "%s: can't get mbuf\n", NG_NODE_NAME(node)); ERROUT(ENOBUFS); } m->m_len = m->m_pkthdr.len = 0; m->m_pkthdr.rcvif = NULL; sc->m = m; } /* Add char to mbuf */ *mtod(m, u_char *) = c; m->m_data++; m->m_len++; m->m_pkthdr.len++; /* Ship off mbuf if it's time */ if (sc->hotchar == -1 || c == sc->hotchar || m->m_len >= MHLEN) { m->m_data = m->m_pktdat; NG_SEND_DATA_ONLY(error, sc->hook, m); sc->m = NULL; } done: splx(s); return (error); } /* * This is called when the device driver is ready for more output. * Called from tty system at splsofttty() or spltty(). * Also call from ngt_rcv_data() when a new mbuf is available for output. */ static int ngt_start(struct tty *tp) { const sc_p sc = (sc_p) tp->t_sc; int s; s = spltty(); while (tp->t_outq.c_cc < NGT_HIWATER) { /* XXX 2.2 specific ? */ struct mbuf *m = sc->qhead; /* Remove first mbuf from queue */ if (!m) break; if ((sc->qhead = m->m_nextpkt) == NULL) sc->qtail = &sc->qhead; sc->qlen--; QUEUECHECK(sc); /* Send as much of it as possible */ while (m) { int sent; sent = m->m_len - b_to_q(mtod(m, u_char *), m->m_len, &tp->t_outq); m->m_data += sent; m->m_len -= sent; if (m->m_len > 0) break; /* device can't take no more */ m = m_free(m); } /* Put remainder of mbuf chain (if any) back on queue */ if (m) { m->m_nextpkt = sc->qhead; sc->qhead = m; if (sc->qtail == &sc->qhead) sc->qtail = &m->m_nextpkt; sc->qlen++; QUEUECHECK(sc); break; } } /* Call output process whether or not there is any output. We are * being called in lieu of ttstart and must do what it would. */ if (tp->t_oproc != NULL) (*tp->t_oproc) (tp); /* This timeout is needed for operation on a pseudo-tty, because the * pty code doesn't call pppstart after it has drained the t_outq. */ if (sc->qhead && (sc->flags & FLG_TIMEOUT) == 0) { sc->chand = timeout(ngt_timeout, sc, 1); sc->flags |= FLG_TIMEOUT; } splx(s); return (0); } /* * We still have data to output to the device, so try sending more. */ static void ngt_timeout(void *arg) { const sc_p sc = (sc_p) arg; int s; s = spltty(); sc->flags &= ~FLG_TIMEOUT; ngt_start(sc->tp); splx(s); } /****************************************************************** NETGRAPH NODE METHODS ******************************************************************/ /* * Initialize a new node of this type. * * We only allow nodes to be created as a result of setting * the line discipline on a tty, so always return an error if not. */ static int ngt_constructor(node_p node) { return (EOPNOTSUPP); } /* * Add a new hook. There can only be one. */ static int ngt_newhook(node_p node, hook_p hook, const char *name) { const sc_p sc = NG_NODE_PRIVATE(node); int s, error = 0; if (strcmp(name, NG_TTY_HOOK)) return (EINVAL); s = spltty(); if (sc->hook) ERROUT(EISCONN); sc->hook = hook; done: splx(s); return (error); } /* * Set the hooks into queueing mode (for outgoing packets) * Force single client at a time. */ static int ngt_connect(hook_p hook) { /*NG_HOOK_FORCE_WRITER(hook); NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));*/ return (0); } /* * Disconnect the hook */ static int ngt_disconnect(hook_p hook) { const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); int s; s = spltty(); if (hook != sc->hook) panic(__func__); sc->hook = NULL; m_freem(sc->m); sc->m = NULL; splx(s); return (0); } /* * Remove this node. The does the netgraph portion of the shutdown. * This should only be called indirectly from ngt_close(). */ static int ngt_shutdown(node_p node) { const sc_p sc = NG_NODE_PRIVATE(node); if (!ngt_nodeop_ok) return (EOPNOTSUPP); NG_NODE_SET_PRIVATE(node, NULL); NG_NODE_UNREF(sc->node); m_freem(sc->qhead); m_freem(sc->m); bzero(sc, sizeof(*sc)); FREE(sc, M_NETGRAPH); return (0); } /* * Receive incoming data from netgraph system. Put it on our * output queue and start output if necessary. */ static int ngt_rcvdata(hook_p hook, item_p item) { const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); int s, error = 0; struct mbuf *m; if (hook != sc->hook) panic(__func__); NGI_GET_M(item, m); NG_FREE_ITEM(item); s = spltty(); if (sc->qlen >= MAX_MBUFQ) ERROUT(ENOBUFS); m->m_nextpkt = NULL; *sc->qtail = m; sc->qtail = &m->m_nextpkt; sc->qlen++; QUEUECHECK(sc); m = NULL; if (sc->qlen == 1) ngt_start(sc->tp); done: splx(s); if (m) m_freem(m); return (error); } /* * Receive control message */ static int ngt_rcvmsg(node_p node, item_p item, hook_p lasthook) { const sc_p sc = NG_NODE_PRIVATE(node); struct ng_mesg *resp = NULL; int error = 0; struct ng_mesg *msg; NGI_GET_MSG(item, msg); switch (msg->header.typecookie) { case NGM_TTY_COOKIE: switch (msg->header.cmd) { case NGM_TTY_SET_HOTCHAR: { int hotchar; if (msg->header.arglen != sizeof(int)) ERROUT(EINVAL); hotchar = *((int *) msg->data); if (hotchar != (u_char) hotchar && hotchar != -1) ERROUT(EINVAL); sc->hotchar = hotchar; /* race condition is OK */ break; } case NGM_TTY_GET_HOTCHAR: NG_MKRESPONSE(resp, msg, sizeof(int), M_NOWAIT); if (!resp) ERROUT(ENOMEM); /* Race condition here is OK */ *((int *) resp->data) = sc->hotchar; break; default: ERROUT(EINVAL); } break; default: ERROUT(EINVAL); } done: NG_RESPOND_MSG(error, node, item, resp); NG_FREE_MSG(msg); return (error); } /****************************************************************** INITIALIZATION ******************************************************************/ /* * Handle loading and unloading for this node type */ static int ngt_mod_event(module_t mod, int event, void *data) { /* struct ng_type *const type = data;*/ int s, error = 0; switch (event) { case MOD_LOAD: /* Register line discipline */ s = spltty(); if ((ngt_ldisc = ldisc_register(NETGRAPHDISC, &ngt_disc)) < 0) { splx(s); log(LOG_ERR, "%s: can't register line discipline", __func__); return (EIO); } splx(s); break; case MOD_UNLOAD: /* Unregister line discipline */ s = spltty(); ldisc_deregister(ngt_ldisc); splx(s); break; default: error = EOPNOTSUPP; break; } return (error); } Index: head/sys/sys/linedisc.h =================================================================== --- head/sys/sys/linedisc.h (revision 131129) +++ head/sys/sys/linedisc.h (revision 131130) @@ -1,142 +1,141 @@ /*- * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * Copyright (c) 2004 * Poul-Henning Kamp. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)conf.h 8.5 (Berkeley) 1/9/95 * $FreeBSD$ */ #ifndef _SYS_LINEDISC_H_ #define _SYS_LINEDISC_H_ #ifdef _KERNEL struct tty; typedef int l_open_t(struct cdev *dev, struct tty *tp); typedef int l_close_t(struct tty *tp, int flag); typedef int l_read_t(struct tty *tp, struct uio *uio, int flag); typedef int l_write_t(struct tty *tp, struct uio *uio, int flag); typedef int l_ioctl_t(struct tty *tp, u_long cmd, caddr_t data, int flag, struct thread *td); typedef int l_rint_t(int c, struct tty *tp); typedef int l_start_t(struct tty *tp); typedef int l_modem_t(struct tty *tp, int flag); /* * Line discipline switch table */ struct linesw { l_open_t *l_open; l_close_t *l_close; l_read_t *l_read; l_write_t *l_write; l_ioctl_t *l_ioctl; l_rint_t *l_rint; l_start_t *l_start; l_modem_t *l_modem; - u_char l_hotchar; }; extern struct linesw *linesw[]; extern int nlinesw; int ldisc_register(int , struct linesw *); void ldisc_deregister(int); #define LDISC_LOAD -1 /* Loadable line discipline */ l_read_t l_noread; l_write_t l_nowrite; l_ioctl_t l_nullioctl; static __inline int ttyld_open(struct tty *tp, struct cdev *dev) { return ((*linesw[tp->t_line]->l_open)(dev, tp)); } static __inline int ttyld_close(struct tty *tp, int flag) { return ((*linesw[tp->t_line]->l_close)(tp, flag)); } static __inline int ttyld_read(struct tty *tp, struct uio *uio, int flag) { return ((*linesw[tp->t_line]->l_read)(tp, uio, flag)); } static __inline int ttyld_write(struct tty *tp, struct uio *uio, int flag) { return ((*linesw[tp->t_line]->l_write)(tp, uio, flag)); } static __inline int ttyld_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, struct thread *td) { return ((*linesw[tp->t_line]->l_ioctl)(tp, cmd, data, flag, td)); } static __inline int ttyld_rint(struct tty *tp, int c) { return ((*linesw[tp->t_line]->l_rint)(c, tp)); } static __inline int ttyld_start(struct tty *tp) { return ((*linesw[tp->t_line]->l_start)(tp)); } static __inline int ttyld_modem(struct tty *tp, int flag) { return ((*linesw[tp->t_line]->l_modem)(tp, flag)); } #endif /* _KERNEL */ #endif /* !_SYS_LINEDISC_H_ */ Index: head/sys/sys/tty.h =================================================================== --- head/sys/sys/tty.h (revision 131129) +++ head/sys/sys/tty.h (revision 131130) @@ -1,323 +1,324 @@ /*- * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Copyright (c) 2002 Networks Associates Technologies, Inc. * All rights reserved. * * Portions of this software were developed for the FreeBSD Project by * ThinkSec AS and NAI Labs, the Security Research Division of Network * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 * ("CBOSS"), as part of the DARPA CHATS research program. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)tty.h 8.6 (Berkeley) 1/21/94 * $FreeBSD$ */ #ifndef _SYS_TTY_H_ #define _SYS_TTY_H_ #include #include #include #include #include /* * Clists are character lists, which is a variable length linked list * of cblocks, with a count of the number of characters in the list. */ struct clist { int c_cc; /* Number of characters in the clist. */ int c_cbcount; /* Number of cblocks. */ int c_cbmax; /* Max # cblocks allowed for this clist. */ int c_cbreserved; /* # cblocks reserved for this clist. */ char *c_cf; /* Pointer to the first cblock. */ char *c_cl; /* Pointer to the last cblock. */ }; /* * Per-tty structure. * * Should be split in two, into device and tty drivers. * Glue could be masks of what to echo and circular buffer * (low, high, timeout). */ struct tty { struct clist t_rawq; /* Device raw input queue. */ long t_rawcc; /* Raw input queue statistics. */ struct clist t_canq; /* Device canonical queue. */ long t_cancc; /* Canonical queue statistics. */ struct clist t_outq; /* Device output queue. */ long t_outcc; /* Output queue statistics. */ int t_line; /* Interface to device drivers. */ struct cdev *t_dev; /* Device. */ int t_state; /* Device and driver (TS*) state. */ int t_flags; /* Tty flags. */ int t_timeout; /* Timeout for ttywait() */ struct pgrp *t_pgrp; /* Foreground process group. */ struct session *t_session; /* Enclosing session. */ struct sigio *t_sigio; /* Information for async I/O. */ struct selinfo t_rsel; /* Tty read/oob select. */ struct selinfo t_wsel; /* Tty write select. */ struct termios t_termios; /* Termios state. */ struct winsize t_winsize; /* Window size. */ /* Start output. */ void (*t_oproc)(struct tty *); /* Stop output. */ void (*t_stop)(struct tty *, int); /* Set hardware state. */ int (*t_param)(struct tty *, struct termios *); /* Set modem state */ int (*t_modem)(struct tty *, int, int); /* Set break state */ int (*t_break)(struct tty *, int); void *t_sc; /* XXX: net/if_sl.c:sl_softc. */ int t_column; /* Tty output column. */ int t_rocount, t_rocol; /* Tty. */ int t_ififosize; /* Total size of upstream fifos. */ int t_ihiwat; /* High water mark for input. */ int t_ilowat; /* Low water mark for input. */ speed_t t_ispeedwat; /* t_ispeed override for watermarks. */ int t_ohiwat; /* High water mark for output. */ int t_olowat; /* Low water mark for output. */ speed_t t_ospeedwat; /* t_ospeed override for watermarks. */ int t_gen; /* Generation number. */ TAILQ_ENTRY(tty) t_list; /* Global chain of ttys for pstat(8) */ struct mtx t_mtx; int t_refcnt; + int t_hotchar; /* linedisc preferred hot char */ }; #define t_cc t_termios.c_cc #define t_cflag t_termios.c_cflag #define t_iflag t_termios.c_iflag #define t_ispeed t_termios.c_ispeed #define t_lflag t_termios.c_lflag #define t_min t_termios.c_min #define t_oflag t_termios.c_oflag #define t_ospeed t_termios.c_ospeed #define t_time t_termios.c_time #define TTIPRI (PSOCK + 1) /* Sleep priority for tty reads. */ #define TTOPRI (PSOCK + 2) /* Sleep priority for tty writes. */ /* * Userland version of struct tty, for sysctl. */ struct xtty { size_t xt_size; /* Structure size. */ long xt_rawcc; /* Raw input queue statistics. */ long xt_cancc; /* Canonical queue statistics. */ long xt_outcc; /* Output queue statistics. */ int xt_line; /* Interface to device drivers. */ dev_t xt_dev; /* Userland (sysctl) instance. */ int xt_state; /* Device and driver (TS*) state. */ int xt_flags; /* Tty flags. */ int xt_timeout; /* Timeout for ttywait(). */ pid_t xt_pgid; /* Process group ID. */ pid_t xt_sid; /* Session ID. */ struct termios xt_termios; /* Termios state. */ struct winsize xt_winsize; /* Window size. */ int xt_column; /* Tty output column. */ int xt_rocount, xt_rocol; /* Tty. */ int xt_ififosize; /* Total size of upstream fifos. */ int xt_ihiwat; /* High water mark for input. */ int xt_ilowat; /* Low water mark for input. */ speed_t xt_ispeedwat; /* t_ispeed override for watermarks. */ int xt_ohiwat; /* High water mark for output. */ int xt_olowat; /* Low water mark for output. */ speed_t xt_ospeedwat; /* t_ospeed override for watermarks. */ }; /* * User data unfortunately has to be copied through buffers on the way to * and from clists. The buffers are on the stack so their sizes must be * fairly small. */ #define IBUFSIZ 384 /* Should be >= max value of MIN. */ #define OBUFSIZ 100 #ifndef TTYHOG #define TTYHOG 8192 #endif #ifdef _KERNEL #define TTMAXHIWAT roundup(2048, CBSIZE) #define TTMINHIWAT roundup(100, CBSIZE) #define TTMAXLOWAT 256 #define TTMINLOWAT 32 #endif /* These flags are kept in t_state. */ #define TS_SO_OLOWAT 0x00001 /* Wake up when output <= low water. */ #define TS_ASYNC 0x00002 /* Tty in async I/O mode. */ #define TS_BUSY 0x00004 /* Draining output. */ #define TS_CARR_ON 0x00008 /* Carrier is present. */ #define TS_FLUSH 0x00010 /* Outq has been flushed during DMA. */ #define TS_ISOPEN 0x00020 /* Open has completed. */ #define TS_TBLOCK 0x00040 /* Further input blocked. */ #define TS_TIMEOUT 0x00080 /* Wait for output char processing. */ #define TS_TTSTOP 0x00100 /* Output paused. */ #ifdef notyet #define TS_WOPEN 0x00200 /* Open in progress. */ #endif #define TS_XCLUDE 0x00400 /* Tty requires exclusivity. */ /* State for intra-line fancy editing work. */ #define TS_BKSL 0x00800 /* State for lowercase \ work. */ #define TS_CNTTB 0x01000 /* Counting tab width, ignore FLUSHO. */ #define TS_ERASE 0x02000 /* Within a \.../ for PRTRUB. */ #define TS_LNCH 0x04000 /* Next character is literal. */ #define TS_TYPEN 0x08000 /* Retyping suspended input (PENDIN). */ #define TS_LOCAL (TS_BKSL | TS_CNTTB | TS_ERASE | TS_LNCH | TS_TYPEN) /* Extras. */ #define TS_CAN_BYPASS_L_RINT 0x010000 /* Device in "raw" mode. */ #define TS_CONNECTED 0x020000 /* Connection open. */ #define TS_SNOOP 0x040000 /* Device is being snooped on. */ #define TS_SO_OCOMPLETE 0x080000 /* Wake up when output completes. */ #define TS_ZOMBIE 0x100000 /* Connection lost. */ /* Hardware flow-control-invoked bits. */ #define TS_CAR_OFLOW 0x200000 /* For MDMBUF (XXX handle in driver). */ #ifdef notyet #define TS_CTS_OFLOW 0x400000 /* For CCTS_OFLOW. */ #define TS_DSR_OFLOW 0x800000 /* For CDSR_OFLOW. */ #endif /* Character type information. */ #define ORDINARY 0 #define CONTROL 1 #define BACKSPACE 2 #define NEWLINE 3 #define TAB 4 #define VTAB 5 #define RETURN 6 struct speedtab { int sp_speed; /* Speed. */ int sp_code; /* Code. */ }; /* Modem control commands (driver). */ #define DMSET 0 #define DMBIS 1 #define DMBIC 2 #define DMGET 3 /* Flags on a character passed to ttyinput. */ #define TTY_CHARMASK 0x000000ff /* Character mask */ #define TTY_QUOTE 0x00000100 /* Character quoted */ #define TTY_ERRORMASK 0xff000000 /* Error mask */ #define TTY_FE 0x01000000 /* Framing error */ #define TTY_PE 0x02000000 /* Parity error */ #define TTY_OE 0x04000000 /* Overrun error */ #define TTY_BI 0x08000000 /* Break condition */ /* Is tp controlling terminal for p? */ #define isctty(p, tp) \ ((p)->p_session == (tp)->t_session && (p)->p_flag & P_CONTROLT) /* Is p in background of tp? */ #define isbackground(p, tp) \ (isctty((p), (tp)) && (p)->p_pgrp != (tp)->t_pgrp) /* Unique sleep addresses. */ #define TSA_CARR_ON(tp) ((void *)&(tp)->t_rawq) #define TSA_HUP_OR_INPUT(tp) ((void *)&(tp)->t_rawq.c_cf) #define TSA_OCOMPLETE(tp) ((void *)&(tp)->t_outq.c_cl) #define TSA_OLOWAT(tp) ((void *)&(tp)->t_outq) #ifdef _KERNEL #ifdef MALLOC_DECLARE MALLOC_DECLARE(M_TTYS); #endif extern struct msgbuf consmsgbuf; /* Message buffer for constty. */ extern struct tty *constty; /* Temporary virtual console. */ extern long tk_cancc; extern long tk_nin; extern long tk_nout; extern long tk_rawcc; int b_to_q(char *cp, int cc, struct clist *q); void catq(struct clist *from, struct clist *to); void clist_alloc_cblocks(struct clist *q, int ccmax, int ccres); void clist_free_cblocks(struct clist *q); void constty_set(struct tty *tp); void constty_clear(void); int getc(struct clist *q); void ndflush(struct clist *q, int cc); char *nextc(struct clist *q, char *cp, int *c); void nottystop(struct tty *tp, int rw); int putc(int c, struct clist *q); int q_to_b(struct clist *q, char *cp, int cc); void termioschars(struct termios *t); int tputchar(int c, struct tty *tp); int ttcompat(struct tty *tp, u_long com, caddr_t data, int flag); int ttioctl(struct tty *tp, u_long com, void *data, int flag); int ttread(struct tty *tp, struct uio *uio, int flag); void ttrstrt(void *tp); int ttsetcompat(struct tty *tp, u_long *com, caddr_t data, struct termios *term); void ttsetwater(struct tty *tp); int ttspeedtab(int speed, struct speedtab *table); int ttstart(struct tty *tp); void ttwakeup(struct tty *tp); int ttwrite(struct tty *tp, struct uio *uio, int flag); void ttwwakeup(struct tty *tp); void ttyblock(struct tty *tp); void ttychars(struct tty *tp); int ttycheckoutq(struct tty *tp, int wait); int ttyclose(struct tty *tp); void ttyflush(struct tty *tp, int rw); void ttyinfo(struct tty *tp); int ttyinput(int c, struct tty *tp); int ttylclose(struct tty *tp, int flag); int ttyldoptim(struct tty *tp); struct tty *ttymalloc(struct tty *tp); int ttymodem(struct tty *tp, int flag); int ttyopen(struct cdev *device, struct tty *tp); int ttyref(struct tty *tp); int ttyrel(struct tty *tp); int ttysleep(struct tty *tp, void *chan, int pri, char *wmesg, int timo); int ttywait(struct tty *tp); int unputc(struct clist *q); /* * XXX: temporary */ #include #endif /* _KERNEL */ #endif /* !_SYS_TTY_H_ */