2 * ppp_ahdlc.c - STREAMS module for doing PPP asynchronous HDLC.
4 * Copyright (c) 1994 The Australian National University.
7 * Permission to use, copy, modify, and distribute this software and its
8 * documentation is hereby granted, provided that the above copyright
9 * notice appears in all copies. This software is provided without any
10 * warranty, express or implied. The Australian National University
11 * makes no representations about the suitability of this software for
14 * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
15 * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
16 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
17 * THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY
20 * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
21 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
22 * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
23 * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
24 * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
27 * $Id: ppp_ahdlc.c,v 1.11 1999/09/15 23:49:05 masputra Exp $
31 * This file is used under Solaris 2, SVR4, SunOS 4, and Digital UNIX.
33 #include <sys/types.h>
34 #include <sys/param.h>
35 #include <sys/stream.h>
36 #include <sys/errno.h>
41 #include <sys/cmn_err.h>
46 #include <sys/cmn_err.h>
50 #include <net/ppp_defs.h>
51 #include <net/pppio.h>
54 #define IFRAME_BSIZE 512 /* Block size to allocate for input */
55 #define OFRAME_BSIZE 4096 /* Don't allocb more than this for output */
57 MOD_OPEN_DECL(ahdlc_open);
58 MOD_CLOSE_DECL(ahdlc_close);
59 static int ahdlc_wput __P((queue_t *, mblk_t *));
60 static int ahdlc_rput __P((queue_t *, mblk_t *));
61 static void stuff_frame __P((queue_t *, mblk_t *));
62 static void unstuff_chars __P((queue_t *, mblk_t *));
63 static int msg_byte __P((mblk_t *, unsigned int));
65 /* Extract byte i of message mp. */
66 #define MSG_BYTE(mp, i) ((i) < (mp)->b_wptr - (mp)->b_rptr? (mp)->b_rptr[i]: \
69 /* Is this LCP packet one we have to transmit using LCP defaults? */
70 #define LCP_USE_DFLT(mp) (1 <= (code = MSG_BYTE((mp), 4)) && code <= 7)
72 #define PPP_AHDL_ID 0x7d23
73 static struct module_info minfo = {
75 PPP_AHDL_ID, "ppp_ahdl", 0, INFPSZ, 640, 512
77 PPP_AHDL_ID, "ppp_ahdl", 0, INFPSZ, 4096, 128
81 static struct qinit rinit = {
82 ahdlc_rput, NULL, ahdlc_open, ahdlc_close, NULL, &minfo, NULL
85 static struct qinit winit = {
86 ahdlc_wput, NULL, NULL, NULL, NULL, &minfo, NULL
89 #if defined(SVR4) && !defined(SOL2)
91 #define ppp_ahdlcinfo phdlinfo
93 struct streamtab ppp_ahdlcinfo = {
94 &rinit, &winit, NULL, NULL
99 typedef struct ahdlc_state {
110 struct pppstat stats;
113 /* Values for flags */
114 #define ESCAPED 0x100 /* last saw escape char on input */
115 #define IFLUSH 0x200 /* flushing input due to error */
117 /* RCV_B7_1, etc., defined in net/pppio.h, are stored in flags also. */
118 #define RCV_FLAGS (RCV_B7_1|RCV_B7_0|RCV_ODDP|RCV_EVNP)
121 * FCS lookup table as calculated by genfcstab.
123 static u_short fcstab[256] = {
124 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
125 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
126 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
127 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
128 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
129 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
130 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
131 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
132 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
133 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
134 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
135 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
136 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
137 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
138 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
139 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
140 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
141 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
142 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
143 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
144 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
145 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
146 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
147 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
148 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
149 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
150 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
151 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
152 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
153 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
154 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
155 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
159 * STREAMS module entry points.
166 sp = (ahdlc_state_t *) ALLOC_SLEEP(sizeof(ahdlc_state_t));
169 bzero((caddr_t) sp, sizeof(ahdlc_state_t));
170 q->q_ptr = (caddr_t) sp;
171 WR(q)->q_ptr = (caddr_t) sp;
173 sp->xaccm[3] = 0x60000000;
181 MOD_CLOSE(ahdlc_close)
183 ahdlc_state_t *state;
187 state = (ahdlc_state_t *) q->q_ptr;
188 if (state->cur_frame != 0) {
189 freemsg(state->cur_frame);
190 state->cur_frame = 0;
192 FREE(q->q_ptr, sizeof(ahdlc_state_t));
194 OTHERQ(q)->q_ptr = NULL;
205 ahdlc_state_t *state;
209 struct ppp_stats *psp;
211 state = (ahdlc_state_t *) q->q_ptr;
213 DPRINT("state == 0 in ahdlc_wput\n");
218 switch (mp->b_datap->db_type) {
221 * A data packet - do character-stuffing and FCS, and
229 iop = (struct iocblk *) mp->b_rptr;
231 switch (iop->ioc_cmd) {
233 if (iop->ioc_count < sizeof(u_int32_t)
234 || iop->ioc_count > sizeof(ext_accm))
236 if (mp->b_cont == 0) {
237 DPRINT1("ahdlc_wput/%d: PPPIO_XACCM b_cont = 0!\n", state->unit);
240 bcopy((caddr_t)mp->b_cont->b_rptr, (caddr_t)state->xaccm,
242 state->xaccm[2] &= ~0x40000000; /* don't escape 0x5e */
243 state->xaccm[3] |= 0x60000000; /* do escape 0x7d, 0x7e */
249 if (iop->ioc_count != sizeof(u_int32_t))
251 if (mp->b_cont == 0) {
252 DPRINT1("ahdlc_wput/%d: PPPIO_RACCM b_cont = 0!\n", state->unit);
255 bcopy((caddr_t)mp->b_cont->b_rptr, (caddr_t)&state->raccm,
262 np = allocb(sizeof(int), BPRI_HI);
270 *(int *)np->b_wptr = state->flags & RCV_FLAGS;
271 np->b_wptr += sizeof(int);
272 iop->ioc_count = sizeof(int);
277 np = allocb(sizeof(struct ppp_stats), BPRI_HI);
285 psp = (struct ppp_stats *) np->b_wptr;
286 np->b_wptr += sizeof(struct ppp_stats);
287 bzero((caddr_t)psp, sizeof(struct ppp_stats));
288 psp->p = state->stats;
289 iop->ioc_count = sizeof(struct ppp_stats);
294 /* we knew this anyway */
305 else if (error == 0) {
306 mp->b_datap->db_type = M_IOCACK;
309 mp->b_datap->db_type = M_IOCNAK;
311 iop->ioc_error = error;
317 switch (*mp->b_rptr) {
319 state->mtu = ((unsigned short *)mp->b_rptr)[1];
323 state->mru = ((unsigned short *)mp->b_rptr)[1];
327 state->unit = mp->b_rptr[1];
346 ahdlc_state_t *state;
348 state = (ahdlc_state_t *) q->q_ptr;
350 DPRINT("state == 0 in ahdlc_rput\n");
355 switch (mp->b_datap->db_type) {
357 unstuff_chars(q, mp);
362 if (state->cur_frame != 0) {
363 /* XXX would like to send this up for debugging */
364 freemsg(state->cur_frame);
365 state->cur_frame = 0;
369 state->flags = IFLUSH;
379 /* Extract bit c from map m, to determine if c needs to be escaped. */
380 #define ESCAPE(c, m) ((m)[(c) >> 5] & (1 << ((c) & 0x1f)))
387 ahdlc_state_t *state;
388 int ilen, olen, c, extra, code;
389 mblk_t *omsg, *op, *np;
390 uchar_t *sp, *sp0, *dp, *dp0, *spend;
392 u_int32_t *xaccm, lcp_xaccm[8];
394 state = (ahdlc_state_t *) q->q_ptr;
398 * We estimate the length of the output packet as
399 * 1.25 * input length + 16 (for initial flag, FCS, final flag, slop).
401 olen = ilen + (ilen >> 2) + 16;
402 if (olen > OFRAME_BSIZE)
404 omsg = op = allocb(olen, BPRI_MED);
409 * Put in an initial flag, unless the serial driver currently has
410 * packets still to be transmitted in its queue.
413 if (qsize(q->q_next) == 0) {
419 * For LCP packets with code values between 1 and 7 (Conf-Req
420 * to Code-Rej), we must escape all control characters.
422 xaccm = state->xaccm;
423 if (MSG_BYTE(mp, 0) == PPP_ALLSTATIONS
424 && MSG_BYTE(mp, 1) == PPP_UI
425 && MSG_BYTE(mp, 2) == (PPP_LCP >> 8)
426 && MSG_BYTE(mp, 3) == (PPP_LCP & 0xFF)
427 && LCP_USE_DFLT(mp)) {
428 bcopy((caddr_t) state->xaccm, (caddr_t) lcp_xaccm, sizeof(lcp_xaccm));
437 extra = sp + olen - spend;
443 * We can safely process the input up to `spend'
444 * without overrunning the output, provided we don't
445 * hit more than `extra' characters which need to be escaped.
451 if (ESCAPE(c, xaccm)) {
454 else if (sp < spend - 1)
458 fcs = PPP_FCS(fcs, c);
462 fcs = PPP_FCS(fcs, c);
470 * At this point, we have emptied an input block
471 * and/or filled an output block.
473 if (sp >= mp->b_wptr) {
475 * We've emptied an input block. Advance to the next.
479 break; /* all done */
484 * The output block is full. Allocate a new one.
488 if (olen > OFRAME_BSIZE)
490 np = allocb(olen, BPRI_MED);
500 * Append the FCS and closing flag.
501 * This could require up to 5 characters.
504 /* Sigh. Need another block. */
506 np = allocb(5, BPRI_MED);
514 if (ESCAPE(c, xaccm)) {
519 c = (~fcs >> 8) & 0xff;
520 if (ESCAPE(c, xaccm)) {
531 state->stats.ppp_obytes += msgdsize(omsg);
532 state->stats.ppp_opackets++;
543 state->stats.ppp_oerrors++;
544 putctl1(RD(q)->q_next, M_CTL, PPPCTL_OERROR);
547 #define UPDATE_FLAGS(c) { \
549 state->flags |= RCV_B7_1; \
551 state->flags |= RCV_B7_0; \
552 if (0x6996 & (1 << ((((c) >> 4) ^ (c)) & 0xf))) \
553 state->flags |= RCV_ODDP; \
555 state->flags |= RCV_EVNP; \
559 * Process received characters.
566 ahdlc_state_t *state;
568 uchar_t *cp, *cpend, *dp, *dp0;
572 state = (ahdlc_state_t *) q->q_ptr;
573 state->stats.ppp_ibytes += msgdsize(mp);
577 * Advance to next input block if necessary.
579 if (cp >= mp->b_wptr) {
587 if ((state->flags & (IFLUSH|ESCAPED)) == 0
588 && state->inlen > 0 && (om = state->cur_blk) != 0) {
590 * Process bulk chars as quickly as possible.
593 len = om->b_datap->db_lim - dp; /* max # output bytes */
594 extra = (mp->b_wptr - cp) - len;/* #input chars - #output bytes */
596 len += extra; /* we'll run out of input first */
608 if (c == PPP_ESCAPE) {
613 if (cp >= cpend || (c = *cp) == PPP_FLAG) {
614 state->flags |= ESCAPED;
622 fcs = PPP_FCS(fcs, c);
624 state->inlen += dp - dp0;
627 if (cp >= mp->b_wptr)
628 continue; /* advance to the next mblk */
636 * If the ESCAPE flag is set, the frame ended with
637 * the frame abort sequence "}~".
639 om = state->cur_frame;
641 state->cur_frame = 0;
643 if (len == 0 && (state->flags & IFLUSH) == 0)
645 state->stats.ppp_ipackets++;
646 if (om != 0 && (state->flags & (IFLUSH|ESCAPED)) == 0
647 && len > PPP_FCSLEN) {
648 if (state->infcs == PPP_GOODFCS) {
649 adjmsg(om, -PPP_FCSLEN); /* chop off fcs */
650 putnext(q, om); /* bombs away! */
653 DPRINT2("ppp%d: bad fcs (len=%d)\n", state->unit, len);
657 state->flags &= ~(IFLUSH|ESCAPED);
658 state->stats.ppp_ierrors++;
659 putctl1(q->q_next, M_CTL, PPPCTL_IERROR);
663 if (state->flags & IFLUSH)
665 if (state->flags & ESCAPED) {
667 state->flags &= ~ESCAPED;
668 } else if (c == PPP_ESCAPE) {
669 state->flags |= ESCAPED;
672 if (state->inlen == 0) {
674 * First byte of the frame: allocate the first message block.
676 om = allocb(IFRAME_BSIZE, BPRI_MED);
678 state->flags |= IFLUSH;
681 state->cur_frame = om;
683 state->infcs = PPP_INITFCS;
686 if (om->b_wptr >= om->b_datap->db_lim) {
688 * Current message block is full. Allocate another one,
689 * unless we have run out of MRU.
691 if (state->inlen >= state->mru + PPP_HDRLEN + PPP_FCSLEN) {
692 state->flags |= IFLUSH;
693 DPRINT2("ppp%d: frame too long (%d)\n",
694 state->unit, state->inlen);
697 om = allocb(IFRAME_BSIZE, BPRI_MED);
699 state->flags |= IFLUSH;
702 state->cur_blk->b_cont = om;
707 if (state->inlen == 0) {
709 * We don't do address/control & protocol decompression here,
710 * but we try to put the first byte at an offset such that
711 * the info field starts on a word boundary. The code here
712 * will do this except for packets with protocol compression
713 * but not address/control compression.
715 if (c != PPP_ALLSTATIONS) {
719 om->b_rptr = om->b_wptr;
725 state->infcs = PPP_FCS(state->infcs, c);
734 while (mp != 0 && i >= mp->b_wptr - mp->b_rptr)
738 return mp->b_rptr[i];