76aed49a32973e752a07e4f456803bebeae3c9ab
[ppp.git] / NeXT / ppp_tty.c
1 /*      $ID: ppp_tty.c,v 1.4 1994/12/13 03:42:17 paulus Exp paulus $    */
2
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  * Rewritten for NextStep's funky kernel functions, I/O threads,
75  * and netbufs (instead of real mbufs).  Also, ifnets don't install
76  * into the kernel under NS as they do under BSD.  We have tried to
77  * make the code remain as similar to the NetBSD version without
78  * incurring too much hassle.  This code is the merge of 
79  * Philip Prindeville's <philipp@res.enst.fr>/Pete French's <pete@ohm.york.ac.uk>
80  * and Stephen Perkins'  <perkins@cps.msu.edu> independent ports.
81  *
82  */
83
84 /* from if_sl.c,v 1.11 84/10/04 12:54:47 rick Exp */
85 /* from NetBSD: if_ppp.c,v 1.15.2.2 1994/07/28 05:17:59 cgd Exp */
86
87 /* #include "ppp.h" */
88 #if NUM_PPP > 0
89
90 #define KERNEL 1
91 #define KERNEL_FEATURES 1
92 #define INET 1
93
94 #include <sys/param.h>
95 #include <sys/proc.h>
96 #include <sys/user.h>
97 #include "netbuf.h"
98 #include <sys/socket.h>
99 #include <sys/ioctl.h>
100 #include <sys/file.h>
101 #include <sys/tty.h>
102 #include <sys/conf.h>
103 #include <sys/dk.h>
104 #include <sys/uio.h>
105 #include <sys/errno.h>
106 #include <machine/param.h>
107
108
109 #include <kernserv/prototypes.h>
110 /* NeXT broke spl.h in 3.2/m68k. Thanks EPS! */
111
112 #if defined(m68k)
113 #import "spl.h"
114 #else
115 #include <driverkit/generalFuncs.h>
116 #import <kernserv/machine/spl.h>
117 #endif
118
119 #include <kernserv/kern_server_types.h>
120
121 #include <net/if.h>
122 #include <net/route.h>
123
124 #ifdef VJC
125 #include <netinet/in.h>
126 #include <netinet/in_systm.h>
127 #include <netinet/ip.h>
128 #endif
129
130 #include <net/ppp_defs.h>
131 #ifdef VJC
132 #include <net/vjcompress.h>
133 #endif
134 #include <net/if_ppp.h>
135 #include "if_pppvar.h"
136
137 #include "inlines.h"
138
139 int     pppopen __P((dev_t dev, struct tty *tp));
140 void    pppclose __P((struct tty *tp));
141 int     pppread __P((struct tty *tp, struct uio *uio));
142 int     pppwrite __P((struct tty *tp, struct uio *uio));
143 int     ppptioctl __P((struct tty *tp, int cmd, void *data, int flag));
144 void    pppinput __P((int c, struct tty *tp));
145 void    pppstart __P((struct tty *tp));
146
147 netbuf_t        pppgetbuf __P((netif_t));
148 int             pppoutput __P((netif_t ifp, netbuf_t m, void *arg));
149 void            pppintr __P((void *));
150
151 static u_int16_t pppfcs __P((u_int16_t fcs, u_char *cp, int len));
152 static void     pppasyncstart __P((struct ppp_softc *));
153 static void     pppasyncctlp __P((struct ppp_softc *));
154 static void     pppasyncrelinq __P((struct ppp_softc *));
155 static int      ppp_timeout __P((void *));
156 void            pppgetm __P((struct ppp_softc *sc));
157 static void     pppdumpb __P((u_char *b, int l));
158 void            ppplogchar __P((struct ppp_softc *, int));
159
160 extern kern_server_t instance;
161
162 #ifdef ADD_ERRORS
163
164 static int in_error_pkt_count = 0;   /* Number of packets received */
165 static int in_error_limit = -1;      /* Insert error at this limit */
166 static int in_error_max_sep  = 50;  /* Max range of random number */
167
168 static int out_error_pkt_count = 0;   /* Number of packets sent */
169 static int out_error_limit = -1;      /* Insert error at this limit */
170 static int out_error_max_sep  = 50;  /* Max range of random number */
171
172 #endif /* ADD_ERRORS */
173
174
175 /*
176  * Does c need to be escaped?
177  */
178 #define ESCAPE_P(c)     (sc->sc_asyncmap[(c) >> 5] & (1 << ((c) & 0x1F)))
179
180 #define CCOUNT(q)       ((q)->c_cc)
181
182 #define PPP_HIWAT       400     /* Don't start a new packet if HIWAT on que */
183
184 #include "linedisc.h"  
185
186
187 extern int ttymodem(struct tty*, int);
188 extern int ttselect(struct tty *tp, int rw);
189
190
191 static netbuf_t
192 pppgetinbuf(netif_t ifp)
193 {
194     register struct ppp_softc *sc = &ppp_softc[if_unit(ifp)];
195     netbuf_t nb;
196     int len = MAX(sc->sc_mru, PPP_MTU) + sizeof (struct ifnet *) +
197 #ifdef VJC
198               VJ_HDRLEN +
199 #endif
200               PPP_HDRLEN + PPP_FCSLEN;
201     nb =  ppp_nb_alloc(len);
202     if (nb != NULL)
203       {
204 #ifdef VJC
205         ppp_nb_shrink_top(nb, VJ_HDRLEN + PPP_HDRLEN);
206 #else
207         ppp_nb_shrink_top(nb, PPP_HDRLEN);
208 #endif
209       }
210
211     return nb;
212 }
213
214 /*
215  * I was a bit worried about reentrancy here.  +++SJP
216  */
217
218 void
219 pppfillfreeq(void *arg)
220 {
221     struct ppp_softc *sc = (struct ppp_softc *)arg;
222     netbuf_t nb;
223     volatile static int in = 0;
224
225     if (in)
226       return;
227     in = 1;
228
229     while(!nbq_high(&sc->sc_freeq)) {
230         nb = pppgetinbuf(sc->sc_if);
231         if (! nb) break;
232 #if 0
233         /*
234          * we no longer reset the length to 0 and then advance the
235          * packet length bit by bit; instead we write into it whenever
236          * we want, and at the end resize the packet before handing-off.
237          */
238         nb_shrink_bot(nb, nb_size(nb));
239 #endif
240         nbq_enqueue(&sc->sc_freeq, nb);
241     }
242
243     in = 0;
244 }
245
246 /*
247  * Line specific open routine for async tty devices.
248  * Attach the given tty to the first available ppp unit.
249  */
250 /* ARGSUSED */
251 int
252 pppopen(dev, tp)
253     dev_t dev;
254     register struct tty *tp;
255 {
256     struct proc *p = curproc;           /* XXX */
257     register struct ppp_softc *sc;
258     int s;
259
260     if (! suser())
261         return EPERM;
262
263     if (tp->t_line == PPPDISC) {
264         sc = (struct ppp_softc *) tp->t_sc;
265         if (sc != NULL && sc->sc_devp == (void *) tp)
266             return (0);
267     }
268
269     if ((sc = pppalloc(p->p_pid)) == NULL)
270         return ENXIO;
271
272     if (sc->sc_relinq)
273         (*sc->sc_relinq)(sc);   /* get previous owner to relinquish the unit */
274
275     pppfillfreeq((void *) sc);   /* fill the free queue - we may block */
276
277     s = splimp();
278     sc->sc_ilen = 0;
279     sc->sc_m = NULL;
280     bzero(sc->sc_asyncmap, sizeof(sc->sc_asyncmap));
281     sc->sc_asyncmap[0] = 0xffffffff;
282     sc->sc_asyncmap[3] = 0x60000000;
283     sc->sc_rasyncmap = 0;
284     sc->sc_devp = (void *) tp;
285     sc->sc_start = pppasyncstart;
286     sc->sc_ctlp = pppasyncctlp;
287     sc->sc_relinq = pppasyncrelinq;
288     sc->sc_outm = NULL;
289     pppgetm(sc);
290     if_flags_set(sc->sc_if, if_flags(sc->sc_if) | IFF_RUNNING);
291
292     tp->t_sc = (caddr_t) sc;
293     ttyflush(tp, FREAD | FWRITE);
294     splx(s);
295
296     return (0);
297 }
298
299 /*
300  * Line specific close routine.
301  * Detach the tty from the ppp unit.
302  * Mimics part of ttyclose().
303  */
304 void
305 pppclose(tp)
306     struct tty *tp;
307 {
308     register struct ppp_softc *sc;
309     int s;
310
311     ttywflush(tp);
312     s = splimp();               /* paranoid; splnet probably ok */
313     tp->t_line = 0;
314     sc = (struct ppp_softc *) tp->t_sc;
315     if (sc != NULL) {
316         tp->t_sc = NULL;
317         if (tp == (struct tty *) sc->sc_devp) {
318             pppasyncrelinq(sc);
319             pppdealloc(sc);
320         }
321     }
322     splx(s);
323     return;
324 }
325
326 /*
327  * Relinquish the interface unit to another device.
328  */
329 static void
330 pppasyncrelinq(sc)
331     struct ppp_softc *sc;
332 {
333     int s;
334
335     s = splimp();
336     if (sc->sc_outm) {
337         nb_free(sc->sc_outm);
338         sc->sc_outm = NULL;
339     }
340     if (sc->sc_m) {
341         nb_free(sc->sc_m);
342         sc->sc_m = NULL;
343     }
344     if (sc->sc_flags & SC_TIMEOUT) {
345         ns_untimeout(ppp_timeout, (void *) sc);
346         sc->sc_flags &= ~SC_TIMEOUT;
347     }
348     splx(s);
349 }
350
351 /*
352  * Line specific (tty) read routine.
353  */
354 int
355 pppread(tp, uio)
356     register struct tty *tp;
357     struct uio *uio;
358 {
359     register struct ppp_softc *sc = (struct ppp_softc *)tp->t_sc;
360     netbuf_t m;
361     register int s;
362     int error = 0;
363
364     if ((tp->t_state & TS_CARR_ON) == 0 && (tp->t_flags & CLOCAL) == 0)
365         return 0;               /* end of file */
366     if (sc == NULL || tp != (struct tty *) sc->sc_devp)
367         return 0;
368     s = splimp();
369     while (nbq_empty(&sc->sc_inq) && tp->t_line == PPPDISC) {
370         if (tp->t_state & (TS_ASYNC | TS_NBIO)) {
371             splx(s);
372             return (EWOULDBLOCK);
373         }
374 #if 0
375         /* NetBSD version... */
376         error = ttysleep(tp, (caddr_t)&tp->t_rawq, TTIPRI|PCATCH, ttyin, 0);
377         if (error) {
378             splx(s);
379             return error;
380         }
381 #else
382         sleep((caddr_t)&tp->t_rawq, TTIPRI);
383 #endif
384     }
385     if (tp->t_line != PPPDISC) {
386         splx(s);
387         return (-1);
388     }
389
390     /* Pull place-holder byte out of canonical queue */
391     getc(&tp->t_canq);
392
393     /* Get the packet from the input queue */
394     m = nbq_dequeue(&sc->sc_inq);
395     splx(s);
396     if (nbuf == NULL){
397       if (sc->sc_flags & SC_DEBUG)
398         IOLogDbg("Read didn't get a buffer at %s %d\n", __FILE__, __LINE__);
399       return -1;
400     }
401     error = uiomove(nb_map(m), nb_size(m), UIO_READ, uio);
402     nb_free(m);
403     return (error);
404 }
405
406 /*
407  * Line specific (tty) write routine.
408  */
409 int
410 pppwrite(tp, uio)
411     register struct tty *tp;
412     struct uio *uio;
413 {
414     register struct ppp_softc *sc = (struct ppp_softc *)tp->t_sc;
415     netbuf_t m;
416     struct sockaddr dst;
417     int len, error;
418
419     if ((tp->t_state & TS_CARR_ON) == 0 && (tp->t_flags & CLOCAL) == 0)
420         return 0;               /* wrote 0 bytes */
421     if (tp->t_line != PPPDISC)
422         return (EINVAL);
423     if (sc == NULL || tp != (struct tty *) sc->sc_devp)
424         return EIO;
425     if (uio->uio_resid > if_mtu(sc->sc_if) + PPP_HDRLEN ||
426       uio->uio_resid < PPP_HDRLEN)
427         return (EMSGSIZE);
428     m = pppgetbuf(sc->sc_if);
429     if (m == NULL){
430       if (sc->sc_flags & SC_DEBUG)
431         IOLogDbg("No buffers available for user level write()\n");
432       return(ENOBUFS);
433     }
434     ppp_nb_grow_top(m, PPP_HDRLEN);
435     len = uio->uio_resid;
436     if (error = uiomove(nb_map(m), nb_size(m), UIO_WRITE, uio)) {
437         nb_free(m);
438         return error;
439     }
440     nb_shrink_bot(m, nb_size(m) - len);
441     dst.sa_family = AF_UNSPEC;
442     bcopy(mtod(m, u_char *), dst.sa_data, PPP_HDRLEN);
443     ppp_nb_shrink_top(m, PPP_HDRLEN);
444     return (pppoutput(sc->sc_if, m, &dst));
445 }
446
447 /*
448  * Line specific (tty) ioctl routine.
449  * This discipline requires that tty device drivers call
450  * the line specific l_ioctl routine from their ioctl routines.
451  */
452 /* ARGSUSED */
453 int
454 ppptioctl(tp, cmd, data, flag)
455     struct tty *tp;
456     void *data;
457     int cmd, flag;
458 {
459     struct ppp_softc *sc = (struct ppp_softc *) tp->t_sc;
460     int error, s;
461
462     if (sc == NULL || tp != (struct tty *) sc->sc_devp)
463         return -1;
464
465     error = 0;
466     switch (cmd) {
467     case PPPIOCSASYNCMAP:
468         if (! suser())
469             return EPERM;
470 #if 0
471         IOLogDbg("ppp%d: set async map to 0x%08x\n", if_unit(sc->sc_if),
472                  *(u_long *)data);
473 #endif
474         sc->sc_asyncmap[0] = *(u_int *)data;
475         break;
476
477     case PPPIOCGASYNCMAP:
478         *(u_int *)data = sc->sc_asyncmap[0];
479 #if 0
480         IOLogDbg("ppp%d: asyncmap gets 0x%x\n", if_unit(sc->sc_if), *(u_int *)data);
481 #endif
482         break;
483
484     case PPPIOCSRASYNCMAP:
485         if (! suser())
486             return EPERM;
487         sc->sc_rasyncmap = *(u_int *)data;
488 #if 0
489         IOLogDbg("ppp%d: setting rasyncmap 0x%x\n", if_unit(sc->sc_if), sc->sc_rasyncmap);
490 #endif
491         break;
492
493     case PPPIOCGRASYNCMAP:
494         *(u_int *)data = sc->sc_rasyncmap;
495 #if 0
496         IOLogDbg("ppp%d: rasyncmap gets 0x%x\n", if_unit(sc->sc_if), *(u_int *)data);
497 #endif
498         break;
499
500     case PPPIOCSXASYNCMAP:
501         if (! suser())
502             return EPERM;
503         s = spltty();
504         bcopy(data, sc->sc_asyncmap, sizeof(sc->sc_asyncmap));
505         sc->sc_asyncmap[1] = 0;                 /* mustn't escape 0x20 - 0x3f */
506         sc->sc_asyncmap[2] &= ~0x40000000;      /* mustn't escape 0x5e */
507         sc->sc_asyncmap[3] |= 0x60000000;       /* must escape 0x7d, 0x7e */
508         splx(s);
509         break;
510
511     case PPPIOCGXASYNCMAP:
512         bcopy(sc->sc_asyncmap, data, sizeof(sc->sc_asyncmap));
513 #if 0
514         IOLogDbg("ppp%d: xasyncmap gets 0x%x/0x%x/0x%x/0x%x\n", if_unit(sc->sc_if), ((u_int *)data)[0], ((u_int *)data)[1], ((u_int *)data)[2], ((u_int *)data)[3]);
515 #endif
516         break;
517
518     default:
519         error = pppioctl(sc, cmd, data, flag);
520         if (error == 0 && cmd == PPPIOCSMRU)
521             pppgetm(sc);
522     }
523
524 #ifdef  i386
525     if (! error && (cmd & IOC_OUT)) {
526         struct uthread *_u = uthread_from_thread(current_thread());
527
528         /* third arg is destination in ioctl() call... */
529         copyout(data, (caddr_t) _u->uu_arg[2], (cmd >> 16) & IOCPARM_MASK);
530     }
531 #endif
532
533     return error;
534 }
535
536 /*
537  * FCS lookup table as calculated by genfcstab.
538  */
539 static const u_int16_t fcstab[256] = {
540         0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
541         0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
542         0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
543         0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
544         0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
545         0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
546         0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
547         0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
548         0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
549         0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
550         0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
551         0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
552         0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
553         0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
554         0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
555         0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
556         0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
557         0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
558         0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
559         0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
560         0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
561         0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
562         0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
563         0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
564         0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
565         0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
566         0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
567         0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
568         0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
569         0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
570         0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
571         0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
572 };
573
574 /*
575  * Calculate a new FCS given the current FCS and the new data.
576  */
577 static u_int16_t
578 pppfcs(fcs, cp, len)
579     register u_int16_t fcs;
580     register u_char *cp;
581     register int len;
582 {
583     while (len--)
584         fcs = PPP_FCS(fcs, *cp++);
585     return (fcs);
586 }
587
588 /*
589  * This gets called from pppoutput when a new packet is
590  * put on a queue.
591  */
592 static void
593 pppasyncstart(sc)
594     register struct ppp_softc *sc;
595 {
596     register struct tty *tp = (struct tty *) sc->sc_devp;
597     int s;
598
599     s = splimp();
600     pppstart(tp);
601     splx(s);
602 }
603
604 /*
605  * This gets called when a received packet is placed on
606  * the inq.
607  */
608 static void
609 pppasyncctlp(sc)
610     struct ppp_softc *sc;
611 {
612     struct tty *tp;
613
614     /* Put a placeholder byte in canq for ttselect()/ttnread(). */
615     tp = (struct tty *) sc->sc_devp;
616     putc(0, &tp->t_canq);
617     ttwakeup(tp);
618 }
619
620 /*
621  * Start output on async tty interface.  Get another datagram
622  * to send from the interface queue and start sending it.
623  */
624 void
625 pppstart(tp)
626     register struct tty *tp;
627 {
628     register struct ppp_softc *sc = (struct ppp_softc *) tp->t_sc;
629     register netbuf_t m;
630     register int len;
631     register u_char *start, *stop, *cp;
632     int n, ndone, done, idle;
633
634     if ((tp->t_state & TS_CARR_ON) == 0 && (tp->t_flags & CLOCAL) == 0
635         || sc == NULL || tp != (struct tty *) sc->sc_devp) {
636         if (tp->t_oproc != NULL)
637             (*tp->t_oproc)(tp);
638         return;
639     }
640
641     idle = 0;
642 #ifdef  OLD_MUX
643     while (CCOUNT(&tp->t_outq) == 0) {
644 #else
645     while (CCOUNT(&tp->t_outq) < PPP_HIWAT) {
646 #endif
647         /*
648          * See if we have an existing packet partly sent.
649          * If not, get a new packet and start sending it.
650          */
651         m = sc->sc_outm;
652         if (m == NULL) {
653             /*
654              * Get another packet to be sent.
655              */
656             m = ppp_dequeue(sc);
657             if (m == NULL) {
658                 idle = 1;
659                 break;
660             }
661
662             /*
663              * The extra PPP_FLAG will start up a new packet, and thus
664              * will flush any accumulated garbage.  We do this whenever
665              * the line may have been idle for some time.
666              */
667             if (CCOUNT(&tp->t_outq) == 0) {
668                 ++sc->sc_bytessent;
669                 (void) putc(PPP_FLAG, &tp->t_outq);
670             }
671
672             /* Calculate the FCS for the first netbuf's worth. */
673             sc->sc_outfcs = pppfcs(PPP_INITFCS, mtod(m, u_char *), nb_size(m));
674             sc->sc_outfcs ^= 0xffff;
675
676 #ifdef ADD_ERRORS
677       /*
678        * This section will adds random errors to 
679        * outgoing packets.
680        */
681
682       if (out_error_limit == -1)  /* Initial time through */
683         {
684           out_error_limit = rand() % out_error_max_sep;
685           IOLog("Introducing outgoing random error after %d more packets\n",
686                  out_error_limit);
687         }       
688
689       if (out_error_pkt_count >=  out_error_limit)
690         {
691           sc->sc_outfcs ^= 0x99;   /* Munge with some noise */
692           out_error_pkt_count = 0;
693           out_error_limit = rand() % out_error_max_sep;
694           IOLog("Introducing outgoing random error after %d more packets\n",
695                  out_error_limit);
696         }
697       else
698         ++out_error_pkt_count;
699       
700
701 #endif /* ADD_ERRORS */      
702
703             cp = mtod(m, u_char *) + nb_size(m);
704             nb_grow_bot(m, PPP_FCSLEN);
705             *cp++ = sc->sc_outfcs & 0xFF;
706             *cp++ = (sc->sc_outfcs >> 8) & 0xFF;
707         }
708
709         start = mtod(m, u_char *);
710         len = nb_size(m);
711         stop = start + len;
712         while (len > 0) {
713             /*
714              * Find out how many bytes in the string we can
715              * handle without doing something special.
716              */
717             for (cp = start; cp < stop; cp++)
718                 if (ESCAPE_P(*cp))
719                     break;
720             n = cp - start;
721             if (n) {
722                 /* NetBSD (0.9 or later), 4.3-Reno or similar. */
723                 ndone = n - b_to_q(start, n, &tp->t_outq);
724                 len -= ndone;
725                 start += ndone;
726                 sc->sc_bytessent += ndone;
727
728                 if (ndone < n)
729                     break;      /* packet doesn't fit */
730             }
731
732             /*
733              * If there are characters left in the netbuf,
734              * the first one must be special..
735              * Put it out in a different form.
736              */
737             if (len) {
738                 if (putc(PPP_ESCAPE, &tp->t_outq))
739                     break;
740                 if (putc(*start ^ PPP_TRANS, &tp->t_outq)) {
741                     (void) unputc(&tp->t_outq);
742                     break;
743                 }
744                 sc->sc_bytessent += 2;
745                 start++;
746                 len--;
747             }
748         }
749         /*
750          * If we didn't empty this netbuf, remember where we're up to.
751          */
752         done = len == 0;
753
754         if (!done) {
755             /* remember where we got to */
756             ppp_nb_shrink_top(m, start - mtod(m, u_char *));
757             break;      /* can't do any more at the moment */
758         }
759
760         /*
761          * Output trailing PPP flag and finish packet.
762          * We make the length zero in case the flag
763          * cannot be output immediately.
764          */
765         ppp_nb_shrink_top(m, nb_size(m));
766         if (putc(PPP_FLAG, &tp->t_outq))
767             break;
768         sc->sc_bytessent++;
769
770         /* Finished with this netbuf; free it and move on. */
771         nb_free(m);
772         m = NULL;
773         incr_cnt(sc->sc_if, if_opackets);
774
775         sc->sc_outm = m;
776     }
777
778     /*
779      * If there is stuff in the output queue, send it now.
780      * We are being called in lieu of ttstart and must do what it would.
781      */
782     if (tp->t_oproc != NULL)
783         (*tp->t_oproc)(tp);
784
785     /*
786      * This timeout is needed for operation on a pseudo-tty,
787      * because the pty code doesn't call pppstart after it has
788      * drained the t_outq.
789      */
790     if (!idle && (sc->sc_flags & SC_TIMEOUT) == 0) {
791         ns_timeout(ppp_timeout, (void *) sc, 1 * (1000000000L / HZ), CALLOUT_PRI_SOFTINT0);
792         sc->sc_flags |= SC_TIMEOUT;
793     }
794
795     return;
796 }
797
798 /*
799  * Timeout routine - try to start some more output.
800  */
801 static int
802 ppp_timeout(x)
803     void *x;
804 {
805     struct ppp_softc *sc = (struct ppp_softc *) x;
806     struct tty *tp = (struct tty *) sc->sc_devp;
807     int s;
808
809     s = splimp();
810     sc->sc_flags &= ~SC_TIMEOUT;
811     pppstart(tp);
812     splx(s);
813     return 0;
814 }
815
816 /*
817  * Allocate enough netbuf to handle current MRU.
818  *
819  * Warning Will Robinson:  pppgetm() can get called at interrupt-level!
820  */
821 void
822 pppgetm(sc)
823     register struct ppp_softc *sc;
824 {
825     int s;
826
827     s = splimp();
828     /*
829      * When the MRU is being changed, we could conceivably end up
830      * nuking a packet being received, but I doubt it, since the
831      * hand-shake is lock-step (ie. single packet).
832      */
833     if (sc->sc_m != NULL)
834         nb_free(sc->sc_m);
835     sc->sc_m = nbq_dequeue(&sc->sc_freeq);
836     splx(s);
837 }
838
839 /*
840  * 4.3 says this is an unused function.  However,
841  * it appears to be returning a NULL terminated string
842  * of several characters.  My guess is that the serial
843  * driver is doing a little buffering so that we don't
844  * get burdend with interrupts.
845  *
846  * This function gets called when you use the NeXT
847  * supplied serial drivers.  It does not get called
848  * with the MuX driver.  
849  *
850  * In order to expedite the work done here, we
851  * handle most things here that don't require
852  * processing of a PPP_FLAG.
853  *
854  */
855
856 void
857 ppprend(cp, n, tp)
858     unsigned char *cp;
859     int n;
860     struct tty *tp;
861 {
862
863 #ifndef OPTIMIZE_PPPREND        
864   while (n--) pppinput((u_char) *cp++, tp);
865 #else
866 #warning OPTIMIZE_PPPREND in effect
867
868   register struct ppp_softc *sc = (struct ppp_softc *)tp->t_sc;
869   register int ret;
870
871   if (sc == NULL || tp != (struct tty *) sc->sc_devp)
872     {
873       printf("Warning, bad softc structure at %s %d\n", __FILE__, __LINE__);
874       return;
875     }
876
877   /*
878    * We can handle FLUSHs, ESCAPES, and non PPP_FLAG characters
879    */
880   
881   while (n)
882     {
883       if (sc->sc_flags & SC_FLUSH)
884         {
885           do
886             {
887               if (*(cp++) == PPP_FLAG)
888                 {
889                   pppinput(PPP_FLAG, tp);
890                   --n;
891                   break;
892                 }
893               else if (sc->sc_flags & SC_LOG_FLUSH)
894                 ppplogchar(sc, *cp);
895             }
896           while(--n);
897         }
898       else if (sc->sc_ilen > 3 &&
899                        (nb_size(sc->sc_m) - sc->sc_ilen) > n &&
900                  *cp != PPP_FLAG &&
901                  *cp != PPP_ESCAPE)         /* Dont really handle escapes properly...should */
902         {
903             unsigned char* cp1 = cp;
904             if (sc->sc_flags & SC_ESCAPED)
905             {
906                 sc->sc_flags &= ~SC_ESCAPED;
907                 *cp ^= PPP_TRANS;
908               }
909             
910             do
911             {
912                 sc->sc_fcs = PPP_FCS(sc->sc_fcs, *(cp++));
913                 if (sc->sc_flags & SC_LOG_RAWIN)
914                   ppplogchar(sc, *cp);
915
916             } while(--n && *cp != PPP_FLAG && *cp != PPP_ESCAPE);
917             
918
919             bcopy(cp1, sc->sc_mp, (cp-cp1));
920
921             sc->sc_bytesrcvd += (cp - cp1);
922             sc->sc_ilen += (cp-cp1);
923             sc->sc_mp += (cp-cp1);
924         }
925         else
926         {
927             --n;
928             pppinput(*(cp++), tp);
929         }
930     }
931
932 #endif /* OPTIMIZE_PPPREND */
933 }
934
935 /*
936  * tty interface receiver interrupt.
937  */
938 static const unsigned paritytab[8] = {
939     0x96696996, 0x69969669, 0x69969669, 0x96696996,
940     0x69969669, 0x96696996, 0x96696996, 0x69969669
941 };
942
943 void
944 pppinput(c, tp)
945     int c;
946     register struct tty *tp;
947 {
948     register struct ppp_softc *sc;
949     netbuf_t m;
950     int ilen, s;
951
952     sc = (struct ppp_softc *) tp->t_sc;
953     if (sc == NULL || tp != (struct tty *) sc->sc_devp)
954         return;
955
956     ++tk_nin;
957     ++sc->sc_bytesrcvd;
958
959     if (c & TTY_FE) {
960         /* framing error or overrun on this char - abort packet */
961         IOLogDbg("ppp%d: bad char 0x%x\n", if_unit(sc->sc_if), c);
962         goto flush;
963     }
964
965     c &= 0xff;
966
967     if (c & 0x80)
968         sc->sc_flags |= SC_RCV_B7_1;
969     else
970         sc->sc_flags |= SC_RCV_B7_0;
971     if (paritytab[c >> 5] & (1 << (c & 0x1F)))
972         sc->sc_flags |= SC_RCV_ODDP;
973     else
974         sc->sc_flags |= SC_RCV_EVNP;
975
976     if (sc->sc_flags & SC_LOG_RAWIN)
977         ppplogchar(sc, c);
978
979     if (c == PPP_FLAG) {
980
981
982         ilen = sc->sc_ilen;
983         sc->sc_ilen = 0;
984
985         if (sc->sc_rawin_count > 0)
986             ppplogchar(sc, -1);
987
988 #ifdef ADD_ERRORS
989       /*
990        * This section will adds random packet
991        * errors if defined.
992        */
993
994       /*
995        * Initial time through
996        */
997       if (in_error_limit == -1) 
998         {
999           in_error_limit = rand() % in_error_max_sep;
1000           IOLog("Introducing incoming random error after %d more packets\n",
1001                  in_error_limit);
1002         }       
1003
1004       if ((in_error_pkt_count >= in_error_limit) && (ilen != 0))
1005         {
1006           sc->sc_fcs = !PPP_GOODFCS;
1007           in_error_pkt_count = 0;
1008           in_error_limit = rand() % in_error_max_sep;
1009           IOLog("Introducing incoming random error after %d more packets\n",
1010                  in_error_limit);
1011         }
1012
1013         if (ilen != 0)
1014           ++in_error_pkt_count;
1015       
1016
1017 #endif /* ADD_ERRORS */      
1018
1019         /*
1020          * From the RFC:
1021          *  Each Control Escape octet is also
1022          *  removed, and the following octet is exclusive-or'd with hexadecimal
1023          *  0x20, unless it is the Flag Sequence (which aborts a frame).
1024          *
1025          * So, if SC_ESCAPED is set, then we've seen the packet
1026          * abort sequence "}~".
1027          */
1028
1029         if (sc->sc_flags & (SC_FLUSH | SC_ESCAPED)
1030             || ilen > 0 && sc->sc_fcs != PPP_GOODFCS) {
1031             sc->sc_flags |= SC_PKTLOST; /* note the dropped packet */
1032             if ((sc->sc_flags & (SC_FLUSH | SC_ESCAPED)) == 0){
1033                 IOLogDbg("ppp%d: bad fcs 0x%04x\n", if_unit(sc->sc_if), sc->sc_fcs);
1034                 incr_cnt(sc->sc_if, if_ierrors);
1035             } else
1036                 sc->sc_flags &= ~(SC_FLUSH | SC_ESCAPED);
1037             return;
1038         }
1039
1040         if (ilen < PPP_HDRLEN + PPP_FCSLEN) {
1041             if (ilen) {
1042                 IOLogDbg("ppp%d: too short (%d)\n", if_unit(sc->sc_if), ilen);
1043                 incr_cnt(sc->sc_if, if_ierrors);
1044                 sc->sc_flags |= SC_PKTLOST;
1045             }
1046             return;
1047         }
1048
1049         /*
1050          * Remove FCS trailer.  Set packet length...
1051          */
1052         ilen -= PPP_FCSLEN;
1053         nb_shrink_bot(sc->sc_m, nb_size(sc->sc_m) - ilen);
1054
1055         /* excise this netbuf */
1056         m = sc->sc_m;
1057         sc->sc_m = NULL;
1058
1059         ppppktin(sc, m, sc->sc_flags & SC_PKTLOST);
1060         sc->sc_flags &= ~SC_PKTLOST;
1061
1062         pppgetm(sc);
1063         return;
1064     }
1065
1066     if (sc->sc_flags & SC_FLUSH) {
1067         if (sc->sc_flags & SC_LOG_FLUSH)
1068             ppplogchar(sc, c);
1069         return;
1070     }
1071
1072 /*
1073  * From the RFC:
1074  *  On reception, prior to FCS computation, each octet with value less
1075  *  than hexadecimal 0x20 is checked.  If it is flagged in the receiving
1076  *  ACCM, it is simply removed (it may have been inserted by intervening
1077  *  data communications equipment).  Each Control Escape octet is also
1078  *  removed, and the following octet is exclusive-or'd with hexadecimal
1079  *  0x20, unless it is the Flag Sequence (which aborts a frame).
1080  */
1081
1082     if (c < 0x20 && (sc->sc_rasyncmap & (1 << c))) {
1083         return;
1084     }
1085
1086     if (sc->sc_flags & SC_ESCAPED) {
1087         sc->sc_flags &= ~SC_ESCAPED;
1088         c ^= PPP_TRANS;
1089     } else if (c == PPP_ESCAPE) {
1090         sc->sc_flags |= SC_ESCAPED;
1091 /*      splx(s); */
1092         return;
1093     }
1094
1095     /*
1096      * Initialize buffer on first octet received.
1097      * First octet could be address or protocol (when compressing
1098      * address/control).
1099      * Second octet is control.
1100      * Third octet is first or second (when compressing protocol)
1101      * octet of protocol.
1102      * Fourth octet is second octet of protocol.
1103      */
1104     if (sc->sc_ilen == 0) {
1105         /* reset the input netbuf */
1106         if (sc->sc_m == NULL) {
1107             pppgetm(sc);
1108             if (sc->sc_m == NULL) {
1109                 /*
1110                  * We schedule a call here as pppindrain will
1111                  * not get scheduled and we need the free buffers
1112                  */
1113                 IOLog("ppp%d: no input netbufs!\n", if_unit(sc->sc_if));
1114                 (void)pppsched(pppfillfreeq, sc);
1115                 goto flush;
1116             }
1117         }
1118         m = sc->sc_m;
1119         sc->sc_mp = mtod(m, char *);
1120         sc->sc_fcs = PPP_INITFCS;
1121         if (c != PPP_ALLSTATIONS) {
1122             if (sc->sc_flags & SC_REJ_COMP_AC) {
1123                 IOLogDbg("ppp%d: garbage received: 0x%02x (need 0x%02x)\n",
1124                            if_unit(sc->sc_if), c, PPP_ALLSTATIONS);
1125                 goto flush;
1126             }
1127             *sc->sc_mp++ = PPP_ALLSTATIONS;
1128             *sc->sc_mp++ = PPP_UI;
1129             sc->sc_ilen += 2;
1130         }
1131     }
1132     if (sc->sc_ilen == 1 && c != PPP_UI) {
1133         IOLogDbg("ppp%d: missing UI (0x%02x), got 0x%02x\n",
1134                    if_unit(sc->sc_if), PPP_UI, c);
1135         goto flush;
1136     }
1137     if (sc->sc_ilen == 2 && (c & 1) == 1) {
1138         /* a compressed protocol */
1139         *sc->sc_mp++ = 0;
1140         sc->sc_ilen++;
1141     }
1142     if (sc->sc_ilen == 3 && (c & 1) == 0) {
1143         IOLogDbg("ppp%d: bad protocol %x\n", if_unit(sc->sc_if),
1144                    (sc->sc_mp[-1] << 8) + c);
1145         goto flush;
1146     }
1147
1148     /* packet beyond configured mru? */
1149     if (++sc->sc_ilen > sc->sc_mru + PPP_HDRLEN + PPP_FCSLEN) {
1150         IOLogDbg("ppp%d: packet too big (%d bytes)\n", if_unit(sc->sc_if),
1151                 sc->sc_ilen);
1152         goto flush;
1153     }
1154
1155     /* ilen was incremented above... */
1156     *sc->sc_mp++ = c;
1157     sc->sc_fcs = PPP_FCS(sc->sc_fcs, c);
1158     return;
1159
1160  flush:
1161     if (!(sc->sc_flags & SC_FLUSH)) {
1162         incr_cnt(sc->sc_if, if_ierrors);
1163         sc->sc_flags |= SC_FLUSH;
1164         if (sc->sc_flags & SC_LOG_FLUSH)
1165             ppplogchar(sc, c);
1166     }
1167     return;
1168 }
1169
1170 int
1171 install_ppp_ld(void)
1172 {
1173     return tty_ld_install(PPPDISC, NORMAL_LDISC, pppopen,
1174                           pppclose, pppread, pppwrite, ppptioctl,
1175                           pppinput, ppprend, pppstart, ttymodem,
1176                           ttselect);
1177 }
1178
1179 #define MAX_DUMP_BYTES  128
1180
1181 void
1182 ppplogchar(sc, c)
1183     struct ppp_softc *sc;
1184     int c;
1185 {
1186     if (c >= 0)
1187         sc->sc_rawin[sc->sc_rawin_count++] = c;
1188     if (sc->sc_rawin_count >= sizeof(sc->sc_rawin)
1189         || c < 0 && sc->sc_rawin_count > 0) {
1190         IOLog("ppp%d input:\n", if_unit(sc->sc_if));
1191         pppdumpb(sc->sc_rawin, sc->sc_rawin_count);
1192         sc->sc_rawin_count = 0;
1193     }
1194 }
1195
1196 static void
1197 pppdumpb(b, l)
1198     u_char *b;
1199     int l;
1200 {
1201     char buf[3*MAX_DUMP_BYTES+4];
1202     char *bp = buf;
1203     static char digits[] = "0123456789abcdef";
1204
1205     while (l--) {
1206         if (bp >= buf + sizeof(buf) - 3) {
1207             *bp++ = '>';
1208             break;
1209         }
1210         *bp++ = digits[*b >> 4]; /* convert byte to ascii hex */
1211         *bp++ = digits[*b++ & 0xf];
1212         *bp++ = ' ';
1213     }
1214
1215     *bp = 0;
1216     IOLog("%s\n", buf);
1217 }
1218 #endif  /* NUM_PPP > 0 */