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