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