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