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