xref: /openbsd-src/sys/net/ppp_tty.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: ppp_tty.c,v 1.27 2014/04/19 12:18:35 henning Exp $	*/
2 /*	$NetBSD: ppp_tty.c,v 1.12 1997/03/24 21:23:10 christos Exp $	*/
3 
4 /*
5  * ppp_tty.c - Point-to-Point Protocol (PPP) driver for asynchronous
6  * 	       tty devices.
7  *
8  * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  *
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  *
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in
19  *    the documentation and/or other materials provided with the
20  *    distribution.
21  *
22  * 3. The name "Carnegie Mellon University" must not be used to
23  *    endorse or promote products derived from this software without
24  *    prior written permission. For permission or any legal
25  *    details, please contact
26  *      Office of Technology Transfer
27  *      Carnegie Mellon University
28  *      5000 Forbes Avenue
29  *      Pittsburgh, PA  15213-3890
30  *      (412) 268-4387, fax: (412) 268-7395
31  *      tech-transfer@andrew.cmu.edu
32  *
33  * 4. Redistributions of any form whatsoever must retain the following
34  *    acknowledgment:
35  *    "This product includes software developed by Computing Services
36  *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
37  *
38  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
39  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
40  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
41  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
42  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
43  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
44  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
45  *
46  * Based on:
47  *	@(#)if_sl.c	7.6.1.2 (Berkeley) 2/15/89
48  *
49  * Copyright (c) 1987 Regents of the University of California.
50  * All rights reserved.
51  *
52  * Redistribution and use in source and binary forms are permitted
53  * provided that the above copyright notice and this paragraph are
54  * duplicated in all such forms and that any documentation,
55  * advertising materials, and other materials related to such
56  * distribution and use acknowledge that the software was developed
57  * by the University of California, Berkeley.  The name of the
58  * University may not be used to endorse or promote products derived
59  * from this software without specific prior written permission.
60  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
61  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
62  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
63  *
64  * Serial Line interface
65  *
66  * Rick Adams
67  * Center for Seismic Studies
68  * 1300 N 17th Street, Suite 1450
69  * Arlington, Virginia 22209
70  * (703)276-7900
71  * rick@seismo.ARPA
72  * seismo!rick
73  *
74  * Pounded on heavily by Chris Torek (chris@mimsy.umd.edu, umcp-cs!chris).
75  * Converted to 4.3BSD Beta by Chris Torek.
76  * Other changes made at Berkeley, based in part on code by Kirk Smith.
77  *
78  * Converted to 4.3BSD+ 386BSD by Brad Parker (brad@cayman.com)
79  * Added VJ tcp header compression; more unified ioctls
80  *
81  * Extensively modified by Paul Mackerras (paulus@cs.anu.edu.au).
82  * Cleaned up a lot of the mbuf-related code to fix bugs that
83  * caused system crashes and packet corruption.  Changed pppstart
84  * so that it doesn't just give up with a collision if the whole
85  * packet doesn't fit in the output ring buffer.
86  *
87  * Added priority queueing for interactive IP packets, following
88  * the model of if_sl.c, plus hooks for bpf.
89  * Paul Mackerras (paulus@cs.anu.edu.au).
90  */
91 
92 /* from if_sl.c,v 1.11 84/10/04 12:54:47 rick Exp */
93 /* from NetBSD: if_ppp.c,v 1.15.2.2 1994/07/28 05:17:58 cgd Exp */
94 
95 #include "ppp.h"
96 #if NPPP > 0
97 
98 #define VJC
99 #define PPP_COMPRESS
100 
101 #include <sys/param.h>
102 #include <sys/proc.h>
103 #include <sys/mbuf.h>
104 #include <sys/dkstat.h>
105 #include <sys/socket.h>
106 #include <sys/timeout.h>
107 #include <sys/ioctl.h>
108 #include <sys/file.h>
109 #include <sys/tty.h>
110 #include <sys/kernel.h>
111 #include <sys/conf.h>
112 #include <sys/vnode.h>
113 #include <sys/systm.h>
114 
115 #include <net/if.h>
116 #include <net/if_types.h>
117 
118 #ifdef VJC
119 #include <netinet/in.h>
120 #include <netinet/in_systm.h>
121 #include <netinet/ip.h>
122 #include <net/slcompress.h>
123 #endif
124 
125 #include <net/bpf.h>
126 #include <net/ppp_defs.h>
127 #include <net/if_ppp.h>
128 #include <net/if_pppvar.h>
129 
130 int	pppopen(dev_t dev, struct tty *tp);
131 int	pppclose(struct tty *tp, int flag);
132 int	pppread(struct tty *tp, struct uio *uio, int flag);
133 int	pppwrite(struct tty *tp, struct uio *uio, int flag);
134 int	ppptioctl(struct tty *tp, u_long cmd, caddr_t data, int flag,
135 		       struct proc *);
136 int	pppinput(int c, struct tty *tp);
137 int	pppstart(struct tty *tp, int);
138 
139 u_int16_t pppfcs(u_int16_t fcs, u_char *cp, int len);
140 void	pppasyncstart(struct ppp_softc *);
141 void	pppasyncctlp(struct ppp_softc *);
142 void	pppasyncrelinq(struct ppp_softc *);
143 void	ppp_timeout(void *);
144 void	pppgetm(struct ppp_softc *sc);
145 void	pppdumpb(u_char *b, int l);
146 void	ppplogchar(struct ppp_softc *, int);
147 
148 /*
149  * Some useful mbuf macros not in mbuf.h.
150  */
151 #define M_IS_CLUSTER(m)	((m)->m_flags & M_EXT)
152 
153 #define M_DATASTART(m)	\
154 	(M_IS_CLUSTER(m) ? (m)->m_ext.ext_buf : \
155 	    (m)->m_flags & M_PKTHDR ? (m)->m_pktdat : (m)->m_dat)
156 
157 #define M_DATASIZE(m)	\
158 	(M_IS_CLUSTER(m) ? (m)->m_ext.ext_size : \
159 	    (m)->m_flags & M_PKTHDR ? MHLEN: MLEN)
160 
161 /*
162  * Does c need to be escaped?
163  */
164 #define ESCAPE_P(c)	(sc->sc_asyncmap[(c) >> 5] & (1 << ((c) & 0x1F)))
165 
166 /*
167  * Procedures for using an async tty interface for PPP.
168  */
169 
170 /* This is a NetBSD-1.0 or later kernel. */
171 #define CCOUNT(q)	((q)->c_cc)
172 
173 #define PPP_LOWAT	100	/* Process more output when < LOWAT on queue */
174 #define	PPP_HIWAT	400	/* Don't start a new packet if HIWAT on queue */
175 
176 /*
177  * Line specific open routine for async tty devices.
178  * Attach the given tty to the first available ppp unit.
179  * Called from device open routine or ttioctl.
180  */
181 /* ARGSUSED */
182 int
183 pppopen(dev_t dev, struct tty *tp)
184 {
185     struct proc *p = curproc;		/* XXX */
186     struct ppp_softc *sc;
187     int error, s;
188 
189     if ((error = suser(p, 0)) != 0)
190 	return (error);
191 
192     s = spltty();
193 
194     if (tp->t_line == PPPDISC) {
195 	sc = (struct ppp_softc *) tp->t_sc;
196 	if (sc != NULL && sc->sc_devp == (void *) tp) {
197 	    splx(s);
198 	    return (0);
199 	}
200     }
201 
202     if ((sc = pppalloc(p->p_p->ps_pid)) == NULL) {
203 	splx(s);
204 	return ENXIO;
205     }
206 
207     if (sc->sc_relinq)
208 	(*sc->sc_relinq)(sc);	/* get previous owner to relinquish the unit */
209 
210     timeout_set(&sc->sc_timo, ppp_timeout, sc);
211     sc->sc_ilen = 0;
212     sc->sc_m = NULL;
213     bzero(sc->sc_asyncmap, sizeof(sc->sc_asyncmap));
214     sc->sc_asyncmap[0] = 0xffffffff;
215     sc->sc_asyncmap[3] = 0x60000000;
216     sc->sc_rasyncmap = 0;
217     sc->sc_devp = (void *) tp;
218     sc->sc_start = pppasyncstart;
219     sc->sc_ctlp = pppasyncctlp;
220     sc->sc_relinq = pppasyncrelinq;
221     sc->sc_outm = NULL;
222     pppgetm(sc);
223     sc->sc_if.if_flags |= IFF_RUNNING;
224     sc->sc_if.if_baudrate = tp->t_ospeed;
225 
226     tp->t_sc = (caddr_t) sc;
227     ttyflush(tp, FREAD | FWRITE);
228 
229     splx(s);
230     return (0);
231 }
232 
233 /*
234  * Line specific close routine, called from device close routine
235  * and from ttioctl.
236  * Detach the tty from the ppp unit.
237  * Mimics part of ttyclose().
238  */
239 int
240 pppclose(struct tty *tp, int flag)
241 {
242     struct ppp_softc *sc;
243     int s;
244 
245     s = spltty();
246     ttyflush(tp, FREAD|FWRITE);
247     tp->t_line = 0;
248     sc = (struct ppp_softc *) tp->t_sc;
249     if (sc != NULL) {
250 	tp->t_sc = NULL;
251 	if (tp == (struct tty *) sc->sc_devp) {
252 	    pppasyncrelinq(sc);
253 	    pppdealloc(sc);
254 	}
255     }
256     splx(s);
257     return 0;
258 }
259 
260 /*
261  * Relinquish the interface unit to another device.
262  */
263 void
264 pppasyncrelinq(struct ppp_softc *sc)
265 {
266     int s;
267 
268     s = spltty();
269     if (sc->sc_outm) {
270 	m_freem(sc->sc_outm);
271 	sc->sc_outm = NULL;
272     }
273     if (sc->sc_m) {
274 	m_freem(sc->sc_m);
275 	sc->sc_m = NULL;
276     }
277     if (sc->sc_flags & SC_TIMEOUT) {
278 	timeout_del(&sc->sc_timo);
279 	sc->sc_flags &= ~SC_TIMEOUT;
280     }
281     splx(s);
282 }
283 
284 /*
285  * Line specific (tty) read routine.
286  */
287 int
288 pppread(struct tty *tp, struct uio *uio, int flag)
289 {
290     struct ppp_softc *sc = (struct ppp_softc *)tp->t_sc;
291     struct mbuf *m, *m0;
292     int s;
293     int error = 0;
294 
295     if (sc == NULL)
296 	return 0;
297     /*
298      * Loop waiting for input, checking that nothing disasterous
299      * happens in the meantime.
300      */
301     s = spltty();
302     for (;;) {
303 	if (tp != (struct tty *) sc->sc_devp || tp->t_line != PPPDISC) {
304 	    splx(s);
305 	    return 0;
306 	}
307 	if (!IF_IS_EMPTY(&sc->sc_inq))
308 	    break;
309 	if ((tp->t_state & TS_CARR_ON) == 0 && (tp->t_cflag & CLOCAL) == 0
310 	    && (tp->t_state & TS_ISOPEN)) {
311 	    splx(s);
312 	    return 0;		/* end of file */
313 	}
314 	if (tp->t_state & TS_ASYNC || flag & IO_NDELAY) {
315 	    splx(s);
316 	    return (EWOULDBLOCK);
317 	}
318 	error = ttysleep(tp, (caddr_t)&tp->t_rawq, TTIPRI|PCATCH, ttyin, 0);
319 	if (error) {
320 	    splx(s);
321 	    return error;
322 	}
323     }
324 
325     /* Pull place-holder byte out of canonical queue */
326     getc(&tp->t_canq);
327 
328     /* Get the packet from the input queue */
329     IF_DEQUEUE(&sc->sc_inq, m0);
330     splx(s);
331 
332     for (m = m0; m && uio->uio_resid; m = m->m_next)
333 	if ((error = uiomove(mtod(m, u_char *), m->m_len, uio)) != 0)
334 	    break;
335     m_freem(m0);
336     return (error);
337 }
338 
339 /*
340  * Line specific (tty) write routine.
341  */
342 int
343 pppwrite(struct tty *tp, struct uio *uio, int flag)
344 {
345     struct ppp_softc *sc = (struct ppp_softc *)tp->t_sc;
346     struct mbuf *m, *m0, **mp;
347     struct sockaddr dst;
348     int len, error;
349 
350     if ((tp->t_state & TS_CARR_ON) == 0 && (tp->t_cflag & CLOCAL) == 0)
351 	return 0;		/* wrote 0 bytes */
352     if (tp->t_line != PPPDISC)
353 	return (EINVAL);
354     if (sc == NULL || tp != (struct tty *) sc->sc_devp)
355 	return EIO;
356     if (uio->uio_resid > sc->sc_if.if_mtu + PPP_HDRLEN ||
357 	uio->uio_resid < PPP_HDRLEN)
358 	return (EMSGSIZE);
359     for (mp = &m0; uio->uio_resid; mp = &m->m_next) {
360 	if (mp == &m0) {
361 	    MGETHDR(m, M_WAIT, MT_DATA);
362 	    m->m_pkthdr.len = uio->uio_resid - PPP_HDRLEN;
363 	    m->m_pkthdr.rcvif = NULL;
364 	} else
365 	    MGET(m, M_WAIT, MT_DATA);
366 	*mp = m;
367 	m->m_len = 0;
368 	if (uio->uio_resid >= MCLBYTES / 2)
369 	    MCLGET(m, M_DONTWAIT);
370 	len = M_TRAILINGSPACE(m);
371 	if (len > uio->uio_resid)
372 	    len = uio->uio_resid;
373 	if ((error = uiomove(mtod(m, u_char *), len, uio)) != 0) {
374 	    m_freem(m0);
375 	    return (error);
376 	}
377 	m->m_len = len;
378     }
379     dst.sa_family = AF_UNSPEC;
380     bcopy(mtod(m0, u_char *), dst.sa_data, PPP_HDRLEN);
381     m0->m_data += PPP_HDRLEN;
382     m0->m_len -= PPP_HDRLEN;
383     return ((*sc->sc_if.if_output)(&sc->sc_if, m0, &dst, (struct rtentry *)0));
384 }
385 
386 /*
387  * Line specific (tty) ioctl routine.
388  * This discipline requires that tty device drivers call
389  * the line specific l_ioctl routine from their ioctl routines.
390  */
391 /* ARGSUSED */
392 int
393 ppptioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, struct proc *p)
394 {
395     struct ppp_softc *sc = (struct ppp_softc *) tp->t_sc;
396     int error, s;
397 
398     if (sc == NULL || tp != (struct tty *) sc->sc_devp)
399 	return -1;
400 
401     error = 0;
402     switch (cmd) {
403     case PPPIOCSASYNCMAP:
404 	if ((error = suser(p, 0)) != 0)
405 	    break;
406 	sc->sc_asyncmap[0] = *(u_int *)data;
407 	break;
408 
409     case PPPIOCGASYNCMAP:
410 	*(u_int *)data = sc->sc_asyncmap[0];
411 	break;
412 
413     case PPPIOCSRASYNCMAP:
414 	if ((error = suser(p, 0)) != 0)
415 	    break;
416 	sc->sc_rasyncmap = *(u_int *)data;
417 	break;
418 
419     case PPPIOCGRASYNCMAP:
420 	*(u_int *)data = sc->sc_rasyncmap;
421 	break;
422 
423     case PPPIOCSXASYNCMAP:
424 	if ((error = suser(p, 0)) != 0)
425 	    break;
426 	s = spltty();
427 	bcopy(data, sc->sc_asyncmap, sizeof(sc->sc_asyncmap));
428 	sc->sc_asyncmap[1] = 0;		    /* mustn't escape 0x20 - 0x3f */
429 	sc->sc_asyncmap[2] &= ~0x40000000;  /* mustn't escape 0x5e */
430 	sc->sc_asyncmap[3] |= 0x60000000;   /* must escape 0x7d, 0x7e */
431 	splx(s);
432 	break;
433 
434     case PPPIOCGXASYNCMAP:
435 	bcopy(sc->sc_asyncmap, data, sizeof(sc->sc_asyncmap));
436 	break;
437 
438     default:
439 	error = pppioctl(sc, cmd, data, flag, p);
440 	if (error == 0 && cmd == PPPIOCSMRU)
441 	    pppgetm(sc);
442     }
443 
444     return error;
445 }
446 
447 /*
448  * FCS lookup table as calculated by genfcstab.
449  */
450 static u_int16_t fcstab[256] = {
451 	0x0000,	0x1189,	0x2312,	0x329b,	0x4624,	0x57ad,	0x6536,	0x74bf,
452 	0x8c48,	0x9dc1,	0xaf5a,	0xbed3,	0xca6c,	0xdbe5,	0xe97e,	0xf8f7,
453 	0x1081,	0x0108,	0x3393,	0x221a,	0x56a5,	0x472c,	0x75b7,	0x643e,
454 	0x9cc9,	0x8d40,	0xbfdb,	0xae52,	0xdaed,	0xcb64,	0xf9ff,	0xe876,
455 	0x2102,	0x308b,	0x0210,	0x1399,	0x6726,	0x76af,	0x4434,	0x55bd,
456 	0xad4a,	0xbcc3,	0x8e58,	0x9fd1,	0xeb6e,	0xfae7,	0xc87c,	0xd9f5,
457 	0x3183,	0x200a,	0x1291,	0x0318,	0x77a7,	0x662e,	0x54b5,	0x453c,
458 	0xbdcb,	0xac42,	0x9ed9,	0x8f50,	0xfbef,	0xea66,	0xd8fd,	0xc974,
459 	0x4204,	0x538d,	0x6116,	0x709f,	0x0420,	0x15a9,	0x2732,	0x36bb,
460 	0xce4c,	0xdfc5,	0xed5e,	0xfcd7,	0x8868,	0x99e1,	0xab7a,	0xbaf3,
461 	0x5285,	0x430c,	0x7197,	0x601e,	0x14a1,	0x0528,	0x37b3,	0x263a,
462 	0xdecd,	0xcf44,	0xfddf,	0xec56,	0x98e9,	0x8960,	0xbbfb,	0xaa72,
463 	0x6306,	0x728f,	0x4014,	0x519d,	0x2522,	0x34ab,	0x0630,	0x17b9,
464 	0xef4e,	0xfec7,	0xcc5c,	0xddd5,	0xa96a,	0xb8e3,	0x8a78,	0x9bf1,
465 	0x7387,	0x620e,	0x5095,	0x411c,	0x35a3,	0x242a,	0x16b1,	0x0738,
466 	0xffcf,	0xee46,	0xdcdd,	0xcd54,	0xb9eb,	0xa862,	0x9af9,	0x8b70,
467 	0x8408,	0x9581,	0xa71a,	0xb693,	0xc22c,	0xd3a5,	0xe13e,	0xf0b7,
468 	0x0840,	0x19c9,	0x2b52,	0x3adb,	0x4e64,	0x5fed,	0x6d76,	0x7cff,
469 	0x9489,	0x8500,	0xb79b,	0xa612,	0xd2ad,	0xc324,	0xf1bf,	0xe036,
470 	0x18c1,	0x0948,	0x3bd3,	0x2a5a,	0x5ee5,	0x4f6c,	0x7df7,	0x6c7e,
471 	0xa50a,	0xb483,	0x8618,	0x9791,	0xe32e,	0xf2a7,	0xc03c,	0xd1b5,
472 	0x2942,	0x38cb,	0x0a50,	0x1bd9,	0x6f66,	0x7eef,	0x4c74,	0x5dfd,
473 	0xb58b,	0xa402,	0x9699,	0x8710,	0xf3af,	0xe226,	0xd0bd,	0xc134,
474 	0x39c3,	0x284a,	0x1ad1,	0x0b58,	0x7fe7,	0x6e6e,	0x5cf5,	0x4d7c,
475 	0xc60c,	0xd785,	0xe51e,	0xf497,	0x8028,	0x91a1,	0xa33a,	0xb2b3,
476 	0x4a44,	0x5bcd,	0x6956,	0x78df,	0x0c60,	0x1de9,	0x2f72,	0x3efb,
477 	0xd68d,	0xc704,	0xf59f,	0xe416,	0x90a9,	0x8120,	0xb3bb,	0xa232,
478 	0x5ac5,	0x4b4c,	0x79d7,	0x685e,	0x1ce1,	0x0d68,	0x3ff3,	0x2e7a,
479 	0xe70e,	0xf687,	0xc41c,	0xd595,	0xa12a,	0xb0a3,	0x8238,	0x93b1,
480 	0x6b46,	0x7acf,	0x4854,	0x59dd,	0x2d62,	0x3ceb,	0x0e70,	0x1ff9,
481 	0xf78f,	0xe606,	0xd49d,	0xc514,	0xb1ab,	0xa022,	0x92b9,	0x8330,
482 	0x7bc7,	0x6a4e,	0x58d5,	0x495c,	0x3de3,	0x2c6a,	0x1ef1,	0x0f78
483 };
484 
485 /*
486  * Calculate a new FCS given the current FCS and the new data.
487  */
488 u_int16_t
489 pppfcs(u_int16_t fcs, u_char *cp, int len)
490 {
491     while (len--)
492 	fcs = PPP_FCS(fcs, *cp++);
493     return (fcs);
494 }
495 
496 /*
497  * This gets called from pppoutput when a new packet is
498  * put on a queue, at splsoftnet.
499  */
500 void
501 pppasyncstart(struct ppp_softc *sc)
502 {
503     struct tty *tp = (struct tty *) sc->sc_devp;
504     struct mbuf *m;
505     int len;
506     u_char *start, *stop, *cp;
507     int n, ndone, done, idle;
508     struct mbuf *m2;
509     int s;
510 
511     idle = 0;
512     while (CCOUNT(&tp->t_outq) < PPP_HIWAT) {
513 	/*
514 	 * See if we have an existing packet partly sent.
515 	 * If not, get a new packet and start sending it.
516 	 */
517 	m = sc->sc_outm;
518 	if (m == NULL) {
519 	    /*
520 	     * Get another packet to be sent.
521 	     */
522 	    m = ppp_dequeue(sc);
523 	    if (m == NULL) {
524 		idle = 1;
525 		break;
526 	    }
527 
528 	    /*
529 	     * The extra PPP_FLAG will start up a new packet, and thus
530 	     * will flush any accumulated garbage.  We do this whenever
531 	     * the line may have been idle for some time.
532 	     */
533 	    if (CCOUNT(&tp->t_outq) == 0) {
534 		++sc->sc_stats.ppp_obytes;
535 		(void) putc(PPP_FLAG, &tp->t_outq);
536 	    }
537 
538 	    /* Calculate the FCS for the first mbuf's worth. */
539 	    sc->sc_outfcs = pppfcs(PPP_INITFCS, mtod(m, u_char *), m->m_len);
540 	}
541 
542 	for (;;) {
543 	    start = mtod(m, u_char *);
544 	    len = m->m_len;
545 	    stop = start + len;
546 	    while (len > 0) {
547 		/*
548 		 * Find out how many bytes in the string we can
549 		 * handle without doing something special.
550 		 */
551 		for (cp = start; cp < stop; cp++)
552 		    if (ESCAPE_P(*cp))
553 			break;
554 		n = cp - start;
555 		if (n) {
556 		    /* NetBSD (0.9 or later), 4.3-Reno or similar. */
557 		    ndone = n - b_to_q(start, n, &tp->t_outq);
558 		    len -= ndone;
559 		    start += ndone;
560 		    sc->sc_stats.ppp_obytes += ndone;
561 
562 		    if (ndone < n)
563 			break;	/* packet doesn't fit */
564 		}
565 		/*
566 		 * If there are characters left in the mbuf,
567 		 * the first one must be special.
568 		 * Put it out in a different form.
569 		 */
570 		if (len) {
571 		    s = spltty();
572 		    if (putc(PPP_ESCAPE, &tp->t_outq)) {
573 			splx(s);
574 			break;
575 		    }
576 		    if (putc(*start ^ PPP_TRANS, &tp->t_outq)) {
577 			(void) unputc(&tp->t_outq);
578 			splx(s);
579 			break;
580 		    }
581 		    splx(s);
582 		    sc->sc_stats.ppp_obytes += 2;
583 		    start++;
584 		    len--;
585 		}
586 	    }
587 
588 	    /*
589 	     * If we didn't empty this mbuf, remember where we're up to.
590 	     * If we emptied the last mbuf, try to add the FCS and closing
591 	     * flag, and if we can't, leave sc_outm pointing to m, but with
592 	     * m->m_len == 0, to remind us to output the FCS and flag later.
593 	     */
594 	    done = len == 0;
595 	    if (done && m->m_next == NULL) {
596 		u_char *p, *q;
597 		int c;
598 		u_char endseq[8];
599 
600 		/*
601 		 * We may have to escape the bytes in the FCS.
602 		 */
603 		p = endseq;
604 		c = ~sc->sc_outfcs & 0xFF;
605 		if (ESCAPE_P(c)) {
606 		    *p++ = PPP_ESCAPE;
607 		    *p++ = c ^ PPP_TRANS;
608 		} else
609 		    *p++ = c;
610 		c = (~sc->sc_outfcs >> 8) & 0xFF;
611 		if (ESCAPE_P(c)) {
612 		    *p++ = PPP_ESCAPE;
613 		    *p++ = c ^ PPP_TRANS;
614 		} else
615 		    *p++ = c;
616 		*p++ = PPP_FLAG;
617 
618 		/*
619 		 * Try to output the FCS and flag.  If the bytes
620 		 * don't all fit, back out.
621 		 */
622 		s = spltty();
623 		for (q = endseq; q < p; ++q)
624 		    if (putc(*q, &tp->t_outq)) {
625 			done = 0;
626 			for (; q > endseq; --q)
627 			    unputc(&tp->t_outq);
628 			break;
629 		    }
630 		splx(s);
631 		if (done)
632 		    sc->sc_stats.ppp_obytes += q - endseq;
633 	    }
634 
635 	    if (!done) {
636 		/* remember where we got to */
637 		m->m_data = start;
638 		m->m_len = len;
639 		break;
640 	    }
641 
642 	    /* Finished with this mbuf; free it and move on. */
643 	    MFREE(m, m2);
644 	    m = m2;
645 	    if (m == NULL) {
646 		/* Finished a packet */
647 		break;
648 	    }
649 	    sc->sc_outfcs = pppfcs(sc->sc_outfcs, mtod(m, u_char *), m->m_len);
650 	}
651 
652 	/*
653 	 * If m == NULL, we have finished a packet.
654 	 * If m != NULL, we've either done as much work this time
655 	 * as we need to, or else we've filled up the output queue.
656 	 */
657 	sc->sc_outm = m;
658 	if (m)
659 	    break;
660     }
661 
662     /* Call pppstart to start output again if necessary. */
663     s = spltty();
664     pppstart(tp, 0);
665 
666     /*
667      * This timeout is needed for operation on a pseudo-tty,
668      * because the pty code doesn't call pppstart after it has
669      * drained the t_outq.
670      */
671     if (!idle && (sc->sc_flags & SC_TIMEOUT) == 0) {
672 	timeout_add(&sc->sc_timo, 1);
673 	sc->sc_flags |= SC_TIMEOUT;
674     }
675 
676     splx(s);
677 }
678 
679 /*
680  * This gets called when a received packet is placed on
681  * the inq, at splsoftnet.
682  */
683 void
684 pppasyncctlp(struct ppp_softc *sc)
685 {
686     struct tty *tp;
687     int s;
688 
689     /* Put a placeholder byte in canq for ttpoll()/ttnread(). */
690     s = spltty();
691     tp = (struct tty *) sc->sc_devp;
692     putc(0, &tp->t_canq);
693     ttwakeup(tp);
694     splx(s);
695 }
696 
697 /*
698  * Start output on async tty interface.  If the transmit queue
699  * has drained sufficiently, arrange for pppasyncstart to be
700  * called later at splsoftnet.
701  */
702 int
703 pppstart(struct tty *tp, int force)
704 {
705     struct ppp_softc *sc = (struct ppp_softc *) tp->t_sc;
706 
707     /*
708      * If there is stuff in the output queue, send it now.
709      * We are being called in lieu of ttstart and must do what it would.
710      */
711     if (tp->t_oproc != NULL)
712 	(*tp->t_oproc)(tp);
713 
714     /*
715      * If the transmit queue has drained and the tty has not hung up
716      * or been disconnected from the ppp unit, then tell if_ppp.c that
717      * we need more output.
718      */
719     if ((CCOUNT(&tp->t_outq) < PPP_LOWAT || force)
720 	&& !((tp->t_state & TS_CARR_ON) == 0 && (tp->t_cflag & CLOCAL) == 0)
721 	&& sc != NULL && tp == (struct tty *) sc->sc_devp) {
722 	ppp_restart(sc);
723     }
724 
725     return 0;
726 }
727 
728 /*
729  * Timeout routine - try to start some more output.
730  */
731 void
732 ppp_timeout(void *x)
733 {
734     struct ppp_softc *sc = (struct ppp_softc *) x;
735     struct tty *tp = (struct tty *) sc->sc_devp;
736     int s;
737 
738     s = spltty();
739     sc->sc_flags &= ~SC_TIMEOUT;
740     pppstart(tp, 1);
741     splx(s);
742 }
743 
744 /*
745  * Allocate enough mbuf to handle current MRU.
746  */
747 void
748 pppgetm(struct ppp_softc *sc)
749 {
750     struct mbuf *m, **mp;
751     int len;
752     int s;
753 
754     s = spltty();
755     mp = &sc->sc_m;
756     for (len = sc->sc_mru + PPP_HDRLEN + PPP_FCSLEN; len > 0; ){
757 	if ((m = *mp) == NULL) {
758 	    MGETHDR(m, M_DONTWAIT, MT_DATA);
759 	    if (m == NULL)
760 		break;
761 	    *mp = m;
762 	    MCLGET(m, M_DONTWAIT);
763 	}
764 	len -= M_DATASIZE(m);
765 	mp = &m->m_next;
766     }
767     splx(s);
768 }
769 
770 /*
771  * tty interface receiver interrupt.
772  */
773 static unsigned int paritytab[8] = {
774     0x96696996, 0x69969669, 0x69969669, 0x96696996,
775     0x69969669, 0x96696996, 0x96696996, 0x69969669
776 };
777 
778 int
779 pppinput(int c, struct tty *tp)
780 {
781     struct ppp_softc *sc;
782     struct mbuf *m;
783     int ilen, s;
784 
785     sc = (struct ppp_softc *) tp->t_sc;
786     if (sc == NULL || tp != (struct tty *) sc->sc_devp)
787 	return 0;
788 
789     ++tk_nin;
790     ++sc->sc_stats.ppp_ibytes;
791 
792     if (c & TTY_FE) {
793 	/* framing error or overrun on this char - abort packet */
794 	if (sc->sc_flags & SC_DEBUG)
795 	    printf("%s: bad char %x\n", sc->sc_if.if_xname, c);
796 	goto flush;
797     }
798 
799     c &= 0xff;
800 
801     /*
802      * Handle software flow control of output.
803      */
804     if (tp->t_iflag & IXON) {
805 	if (c == tp->t_cc[VSTOP] && tp->t_cc[VSTOP] != _POSIX_VDISABLE) {
806 	    if ((tp->t_state & TS_TTSTOP) == 0) {
807 		tp->t_state |= TS_TTSTOP;
808 		(*cdevsw[major(tp->t_dev)].d_stop)(tp, 0);
809 	    }
810 	    return 0;
811 	}
812 	if (c == tp->t_cc[VSTART] && tp->t_cc[VSTART] != _POSIX_VDISABLE) {
813 	    tp->t_state &= ~TS_TTSTOP;
814 	    if (tp->t_oproc != NULL)
815 		(*tp->t_oproc)(tp);
816 	    return 0;
817 	}
818     }
819 
820     s = spltty();
821     if (c & 0x80)
822 	sc->sc_flags |= SC_RCV_B7_1;
823     else
824 	sc->sc_flags |= SC_RCV_B7_0;
825     if (paritytab[c >> 5] & (1 << (c & 0x1F)))
826 	sc->sc_flags |= SC_RCV_ODDP;
827     else
828 	sc->sc_flags |= SC_RCV_EVNP;
829     splx(s);
830 
831     if (sc->sc_flags & SC_LOG_RAWIN)
832 	ppplogchar(sc, c);
833 
834     if (c == PPP_FLAG) {
835 	ilen = sc->sc_ilen;
836 	sc->sc_ilen = 0;
837 
838 	if (sc->sc_rawin_count > 0)
839 	    ppplogchar(sc, -1);
840 
841 	/*
842 	 * If SC_ESCAPED is set, then we've seen the packet
843 	 * abort sequence "}~".
844 	 */
845 	if (sc->sc_flags & (SC_FLUSH | SC_ESCAPED)
846 	    || (ilen > 0 && sc->sc_fcs != PPP_GOODFCS)) {
847 	    s = spltty();
848 	    sc->sc_flags |= SC_PKTLOST;	/* note the dropped packet */
849 	    if ((sc->sc_flags & (SC_FLUSH | SC_ESCAPED)) == 0){
850 		if (sc->sc_flags & SC_DEBUG)
851 		    printf("%s: bad fcs %x\n", sc->sc_if.if_xname,
852 			sc->sc_fcs);
853 		sc->sc_if.if_ierrors++;
854 		sc->sc_stats.ppp_ierrors++;
855 	    } else
856 		sc->sc_flags &= ~(SC_FLUSH | SC_ESCAPED);
857 	    splx(s);
858 	    return 0;
859 	}
860 
861 	if (ilen < PPP_HDRLEN + PPP_FCSLEN) {
862 	    if (ilen) {
863 		if (sc->sc_flags & SC_DEBUG)
864 		    printf("%s: too short (%d)\n", sc->sc_if.if_xname, ilen);
865 		s = spltty();
866 		sc->sc_if.if_ierrors++;
867 		sc->sc_stats.ppp_ierrors++;
868 		sc->sc_flags |= SC_PKTLOST;
869 		splx(s);
870 	    }
871 	    return 0;
872 	}
873 
874 	/*
875 	 * Remove FCS trailer.  Somewhat painful...
876 	 */
877 	ilen -= 2;
878 	if (--sc->sc_mc->m_len == 0) {
879 	    for (m = sc->sc_m; m->m_next != sc->sc_mc; m = m->m_next)
880 		;
881 	    sc->sc_mc = m;
882 	}
883 	sc->sc_mc->m_len--;
884 
885 	/* excise this mbuf chain */
886 	m = sc->sc_m;
887 	sc->sc_m = sc->sc_mc->m_next;
888 	sc->sc_mc->m_next = NULL;
889 
890 	ppppktin(sc, m, sc->sc_flags & SC_PKTLOST);
891 	if (sc->sc_flags & SC_PKTLOST) {
892 	    s = spltty();
893 	    sc->sc_flags &= ~SC_PKTLOST;
894 	    splx(s);
895 	}
896 
897 	pppgetm(sc);
898 	return 0;
899     }
900 
901     if (sc->sc_flags & SC_FLUSH) {
902 	if (sc->sc_flags & SC_LOG_FLUSH)
903 	    ppplogchar(sc, c);
904 	return 0;
905     }
906 
907     if (c < 0x20 && (sc->sc_rasyncmap & (1 << c)))
908 	return 0;
909 
910     s = spltty();
911     if (sc->sc_flags & SC_ESCAPED) {
912 	sc->sc_flags &= ~SC_ESCAPED;
913 	c ^= PPP_TRANS;
914     } else if (c == PPP_ESCAPE) {
915 	sc->sc_flags |= SC_ESCAPED;
916 	splx(s);
917 	return 0;
918     }
919     splx(s);
920 
921     /*
922      * Initialize buffer on first octet received.
923      * First octet could be address or protocol (when compressing
924      * address/control).
925      * Second octet is control.
926      * Third octet is first or second (when compressing protocol)
927      * octet of protocol.
928      * Fourth octet is second octet of protocol.
929      */
930     if (sc->sc_ilen == 0) {
931 	/* reset the first input mbuf */
932 	if (sc->sc_m == NULL) {
933 	    pppgetm(sc);
934 	    if (sc->sc_m == NULL) {
935 		if (sc->sc_flags & SC_DEBUG)
936 		    printf("%s: no input mbufs!\n", sc->sc_if.if_xname);
937 		goto flush;
938 	    }
939 	}
940 	m = sc->sc_m;
941 	m->m_len = 0;
942 	m->m_data = M_DATASTART(sc->sc_m);
943 	sc->sc_mc = m;
944 	sc->sc_mp = mtod(m, char *);
945 	sc->sc_fcs = PPP_INITFCS;
946 	if (c != PPP_ALLSTATIONS) {
947 	    if (sc->sc_flags & SC_REJ_COMP_AC) {
948 		if (sc->sc_flags & SC_DEBUG)
949 		    printf("%s: garbage received: 0x%x (need 0xFF)\n",
950 			sc->sc_if.if_xname, c);
951 		goto flush;
952 	    }
953 	    *sc->sc_mp++ = PPP_ALLSTATIONS;
954 	    *sc->sc_mp++ = PPP_UI;
955 	    sc->sc_ilen += 2;
956 	    m->m_len += 2;
957 	}
958     }
959     if (sc->sc_ilen == 1 && c != PPP_UI) {
960 	if (sc->sc_flags & SC_DEBUG)
961 	    printf("%s: missing UI (0x3), got 0x%x\n",
962 		sc->sc_if.if_xname, c);
963 	goto flush;
964     }
965     if (sc->sc_ilen == 2 && (c & 1) == 1) {
966 	/* a compressed protocol */
967 	*sc->sc_mp++ = 0;
968 	sc->sc_ilen++;
969 	sc->sc_mc->m_len++;
970     }
971     if (sc->sc_ilen == 3 && (c & 1) == 0) {
972 	if (sc->sc_flags & SC_DEBUG)
973 	    printf("%s: bad protocol %x\n", sc->sc_if.if_xname,
974 		(sc->sc_mp[-1] << 8) + c);
975 	goto flush;
976     }
977 
978     /* packet beyond configured mru? */
979     if (++sc->sc_ilen > sc->sc_mru + PPP_HDRLEN + PPP_FCSLEN) {
980 	if (sc->sc_flags & SC_DEBUG)
981 	    printf("%s: packet too big\n", sc->sc_if.if_xname);
982 	goto flush;
983     }
984 
985     /* is this mbuf full? */
986     m = sc->sc_mc;
987     if (M_TRAILINGSPACE(m) <= 0) {
988 	if (m->m_next == NULL) {
989 	    pppgetm(sc);
990 	    if (m->m_next == NULL) {
991 		if (sc->sc_flags & SC_DEBUG)
992 		    printf("%s: too few input mbufs!\n", sc->sc_if.if_xname);
993 		goto flush;
994 	    }
995 	}
996 	sc->sc_mc = m = m->m_next;
997 	m->m_len = 0;
998 	m->m_data = M_DATASTART(m);
999 	sc->sc_mp = mtod(m, char *);
1000     }
1001 
1002     ++m->m_len;
1003     *sc->sc_mp++ = c;
1004     sc->sc_fcs = PPP_FCS(sc->sc_fcs, c);
1005     return 0;
1006 
1007  flush:
1008     if (!(sc->sc_flags & SC_FLUSH)) {
1009 	s = spltty();
1010 	sc->sc_if.if_ierrors++;
1011 	sc->sc_stats.ppp_ierrors++;
1012 	sc->sc_flags |= SC_FLUSH;
1013 	splx(s);
1014 	if (sc->sc_flags & SC_LOG_FLUSH)
1015 	    ppplogchar(sc, c);
1016     }
1017     return 0;
1018 }
1019 
1020 #define MAX_DUMP_BYTES	128
1021 
1022 void
1023 ppplogchar(struct ppp_softc *sc, int c)
1024 {
1025     if (c >= 0)
1026 	sc->sc_rawin[sc->sc_rawin_count++] = c;
1027     if (sc->sc_rawin_count >= sizeof(sc->sc_rawin)
1028 	|| (c < 0 && sc->sc_rawin_count > 0)) {
1029 	printf("%s input: ", sc->sc_if.if_xname);
1030 	pppdumpb(sc->sc_rawin, sc->sc_rawin_count);
1031 	sc->sc_rawin_count = 0;
1032     }
1033 }
1034 
1035 void
1036 pppdumpb(u_char *b, int l)
1037 {
1038     char buf[3*MAX_DUMP_BYTES+4];
1039     char *bp = buf;
1040     static char digits[] = "0123456789abcdef";
1041 
1042     while (l--) {
1043 	if (bp >= buf + sizeof(buf) - 3) {
1044 	    *bp++ = '>';
1045 	    break;
1046 	}
1047 	*bp++ = digits[*b >> 4]; /* convert byte to ascii hex */
1048 	*bp++ = digits[*b++ & 0xf];
1049 	*bp++ = ' ';
1050     }
1051 
1052     *bp = 0;
1053     printf("%s\n", buf);
1054 }
1055 
1056 #endif	/* NPPP > 0 */
1057