/* ppp_async.c - Streams async functions Also does FCS
    Copyright (C) 1990  Brad K. Clements, All Rights Reserved
    fcstab and some ideas nicked from if_ppp.c from cmu. See copyright notice in if_ppp.h
     and Readme.streams
*/
#include <sys/types.h>

#include "ppp.h"
#if NPPP > 0
#define	STREAMS	1
#define	DEBUGS	1
#include <sys/param.h>
#include <sys/stream.h>
#include <sys/stropts.h>
#include <sys/dir.h>
#include <sys/signal.h>
#include <sys/user.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/if_ppp.h>
#include <sys/ppp_str.h>

#ifdef	DEBUGS
#include <sys/syslog.h>
#define	DLOG(s,a)	if(ppp_async_debug) log(LOG_INFO, s, a)

int	ppp_async_debug=0;
int	ppp_async_input_debug=0;
#define MAX_DUMP_BYTES 1504
#else
#define	DLOG(s)	{}
#endif

static	int	ppp_async_open(), ppp_async_close(), ppp_async_rput(), ppp_async_wput(),
		ppp_async_wsrv(), ppp_async_rsrv();

static 	struct	module_info	minfo ={
	0xabcd,"ppp_async",0, INFPSZ, 16384, 4096
};

static	struct	qinit	r_init = {
	ppp_async_rput, ppp_async_rsrv, ppp_async_open, ppp_async_close, NULL, &minfo, NULL
};
static	struct	qinit	w_init = {
	ppp_async_wput, ppp_async_wsrv, ppp_async_open, ppp_async_close, NULL, &minfo, NULL
};
struct	streamtab	ppp_asyncinfo = {
	&r_init, &w_init, NULL, NULL, NULL
};

/*
 * FCS lookup table as calculated by genfcstab.
 */
static u_short fcstab[256] = {
	0x0000,	0x1189,	0x2312,	0x329b,	0x4624,	0x57ad,	0x6536,	0x74bf,
	0x8c48,	0x9dc1,	0xaf5a,	0xbed3,	0xca6c,	0xdbe5,	0xe97e,	0xf8f7,
	0x1081,	0x0108,	0x3393,	0x221a,	0x56a5,	0x472c,	0x75b7,	0x643e,
	0x9cc9,	0x8d40,	0xbfdb,	0xae52,	0xdaed,	0xcb64,	0xf9ff,	0xe876,
	0x2102,	0x308b,	0x0210,	0x1399,	0x6726,	0x76af,	0x4434,	0x55bd,
	0xad4a,	0xbcc3,	0x8e58,	0x9fd1,	0xeb6e,	0xfae7,	0xc87c,	0xd9f5,
	0x3183,	0x200a,	0x1291,	0x0318,	0x77a7,	0x662e,	0x54b5,	0x453c,
	0xbdcb,	0xac42,	0x9ed9,	0x8f50,	0xfbef,	0xea66,	0xd8fd,	0xc974,
	0x4204,	0x538d,	0x6116,	0x709f,	0x0420,	0x15a9,	0x2732,	0x36bb,
	0xce4c,	0xdfc5,	0xed5e,	0xfcd7,	0x8868,	0x99e1,	0xab7a,	0xbaf3,
	0x5285,	0x430c,	0x7197,	0x601e,	0x14a1,	0x0528,	0x37b3,	0x263a,
	0xdecd,	0xcf44,	0xfddf,	0xec56,	0x98e9,	0x8960,	0xbbfb,	0xaa72,
	0x6306,	0x728f,	0x4014,	0x519d,	0x2522,	0x34ab,	0x0630,	0x17b9,
	0xef4e,	0xfec7,	0xcc5c,	0xddd5,	0xa96a,	0xb8e3,	0x8a78,	0x9bf1,
	0x7387,	0x620e,	0x5095,	0x411c,	0x35a3,	0x242a,	0x16b1,	0x0738,
	0xffcf,	0xee46,	0xdcdd,	0xcd54,	0xb9eb,	0xa862,	0x9af9,	0x8b70,
	0x8408,	0x9581,	0xa71a,	0xb693,	0xc22c,	0xd3a5,	0xe13e,	0xf0b7,
	0x0840,	0x19c9,	0x2b52,	0x3adb,	0x4e64,	0x5fed,	0x6d76,	0x7cff,
	0x9489,	0x8500,	0xb79b,	0xa612,	0xd2ad,	0xc324,	0xf1bf,	0xe036,
	0x18c1,	0x0948,	0x3bd3,	0x2a5a,	0x5ee5,	0x4f6c,	0x7df7,	0x6c7e,
	0xa50a,	0xb483,	0x8618,	0x9791,	0xe32e,	0xf2a7,	0xc03c,	0xd1b5,
	0x2942,	0x38cb,	0x0a50,	0x1bd9,	0x6f66,	0x7eef,	0x4c74,	0x5dfd,
	0xb58b,	0xa402,	0x9699,	0x8710,	0xf3af,	0xe226,	0xd0bd,	0xc134,
	0x39c3,	0x284a,	0x1ad1,	0x0b58,	0x7fe7,	0x6e6e,	0x5cf5,	0x4d7c,
	0xc60c,	0xd785,	0xe51e,	0xf497,	0x8028,	0x91a1,	0xa33a,	0xb2b3,
	0x4a44,	0x5bcd,	0x6956,	0x78df,	0x0c60,	0x1de9,	0x2f72,	0x3efb,
	0xd68d,	0xc704,	0xf59f,	0xe416,	0x90a9,	0x8120,	0xb3bb,	0xa232,
	0x5ac5,	0x4b4c,	0x79d7,	0x685e,	0x1ce1,	0x0d68,	0x3ff3,	0x2e7a,
	0xe70e,	0xf687,	0xc41c,	0xd595,	0xa12a,	0xb0a3,	0x8238,	0x93b1,
	0x6b46,	0x7acf,	0x4854,	0x59dd,	0x2d62,	0x3ceb,	0x0e70,	0x1ff9,
	0xf78f,	0xe606,	0xd49d,	0xc514,	0xb1ab,	0xa022,	0x92b9,	0x8330,
	0x7bc7,	0x6a4e,	0x58d5,	0x495c,	0x3de3,	0x2c6a,	0x1ef1,	0x0f78
};


struct  ppp_async_info {
	u_int	pai_flags;
#define	PAI_FLAGS_INUSE		0x1
#define	PAI_FLAGS_FLUSH		0x2
#define	PAI_FLAGS_ESCAPED 	0x4
#define	PAI_FLAGS_COMPPROT	0x8
#define	PAI_FLAGS_COMPAC	0x10

	u_long	pai_asyncmap;			/* current outgoing asyncmap */
	int	pai_buffsize;			/* how big of an input buffer to alloc */
	int	pai_buffcount;			/* how many chars currently in the input buffer */
	u_short	pai_fcs;			/* the current fcs */
	mblk_t	*pai_buffer;			/* pointer to the current buffer list */
	mblk_t	*pai_bufftail;			/* pointer to the current input block */
};

typedef	struct ppp_async_info	PAI;

static PAI pai[NPPP];		/* our private cache of async control structures */



static int
  ppp_async_open(q, dev, flag, sflag)
queue_t	*q;
dev_t	dev;
int	flag,
        sflag;
/* an open function might fail if we don't have any more pai elements left free 

*/

{
  register PAI	*p;
  register int x;
  int	s;
  
  if(!suser()) {
    u.u_error = EPERM;
    return(OPENFAIL);	/* only let the superuser or setuid root ppl open
			   this module */
  }
  
  if(q->q_ptr) {
    u.u_error = EBUSY;
    return(OPENFAIL);
  }
  s = splstr();
  if(!q->q_ptr) {
    for(x=0; x < NPPP; x++) 	/* search for an empty PAI */
      if(!(pai[x].pai_flags & PAI_FLAGS_INUSE))
	break;
    if(x == NPPP)	{		/* all buffers in use */
      splx(s);		/* restore processor state */
      u.u_error = ENOBUFS;
      return (OPENFAIL);
    }
    p = &pai[x];
    DLOG("ppp_async%d: opening\n",x);
  }
  else {
    p = (PAI *) q->q_ptr;
    DLOG("ppp_async%d: reopen\n", (p-pai)/sizeof(PAI));
  }
  WR(q)->q_ptr = q->q_ptr =  (caddr_t) p;	/* point the write Q and this read Q to private data. */			
  p->pai_flags = PAI_FLAGS_INUSE;
  p->pai_asyncmap = 0xffffffff;		/* default async map */
  p->pai_buffsize = PPP_MTU + sizeof(struct ppp_header) + 
    sizeof(u_short);	/* how big of a buffer to alloc */
  p->pai_buffcount = 0;			/* number of chars currently in buffer */
  p->pai_buffer = NULL;
  splx(s);
  return(0);
}

static int
  ppp_async_close(q)
queue_t	*q;			/* queue info */
{
  int	s;
  register PAI 	*p;
  
  s = splstr();
  if(p = (PAI *) q->q_ptr) {
    p->pai_flags = 0;	/* clear all flags */
    if(p->pai_buffer) {	/* currently receiving some chars, discard the buffer */
      freemsg(p->pai_buffer);
      p->pai_buffer = NULL;
    }
    DLOG("ppp_async%d: closing\n",(p-pai)/sizeof(PAI));
  }
  splx(s);
  return(0);			
}


static int
  ppp_async_wput(q, mp)
queue_t  *q;
register mblk_t *mp;

/* M_IOCTL processing is performed at this level. There is some 
   weirdness here, but I couldn't think of an easier way to handle it.
   
   SIOCSIFASYNCMAP and SIOCGIFASYNCMAP is handled here.
   
   SIOCSIFCOMPAC and SIOCSIFCOMPPROT are both handled here, rather
   than jamming new flag bits into the if_ interface.  However the
   upper ppp_if.c module will set COMPAC and COMPPROT flags too, it
   just doesn't generate the IOACK. 
   
   SIOCSIFMRU and SIOCGIFMRU (Max Receive Unit) are both handled here.
   Rather than using the MTU to set the MRU, we have a seperate IOCTL for it.
   
   */
{
  
  register struct iocblk	*i;
  register PAI	*p;
  int	x;
  
  switch (mp->b_datap->db_type) {
    
  case 	M_FLUSH :
    if(*mp->b_rptr & FLUSHW)
      flushq(q, FLUSHDATA);
    putnext(q, mp);		/* send it along too */
    break;
    
  case	M_DATA :
    putq(q, mp);	/* queue it for my service routine */
    break;
    
  case	M_IOCTL :
    i = (struct iocblk *) mp->b_rptr;
    p =  (PAI *) q->q_ptr;
    switch (i->ioc_cmd) {
      
    case SIOCSIFCOMPAC :	/* enable or disable AC compression */
      if(i->ioc_count != sizeof(u_char)) {
	i->ioc_error = EINVAL;
	goto iocnak;
      }
      DLOG("ppp_async: SIFCOMPAC %d\n",*(u_char *) mp->b_cont->b_rptr);
      if( *(u_char *) mp->b_cont->b_rptr) 
	p->pai_flags |= PAI_FLAGS_COMPAC;
      else
	p->pai_flags &= ~PAI_FLAGS_COMPAC;
      i->ioc_count = 0;
      goto iocack;
    case SIOCSIFCOMPPROT:	/* enable or disable PROT  compression */
      if(i->ioc_count != sizeof(u_char)) {
	i->ioc_error = EINVAL;
	goto iocnak;
      }
      
      DLOG("ppp_async: SIFCOMPPROT %d\n",*(u_char *) mp->b_cont->b_rptr);
      if( *(u_char *) mp->b_cont->b_rptr) 
	p->pai_flags |= PAI_FLAGS_COMPPROT;
      else
	p->pai_flags &= ~PAI_FLAGS_COMPPROT;
      i->ioc_count = 0;
      goto iocack;
      
      
    case SIOCSIFMRU 	:
      if(i->ioc_count != sizeof(int)) {
	i->ioc_error = EINVAL;
	goto iocnak;
      }
      x = *(int *) mp->b_cont->b_rptr;
      if(x < PPP_MTU)
	x = PPP_MTU;
      x += sizeof(struct ppp_header) + sizeof(u_short);
      if(x > 4096) { /* couldn't allocb something this big */
	i->ioc_error = EINVAL;
	goto iocnak;
      }
      p->pai_buffsize = x;
      
      i->ioc_count  = 0;
      goto iocack;
    case SIOCGIFMRU :
      if(mp->b_cont = allocb(sizeof(int), BPRI_MED)) {
	*(int *) mp->b_cont->b_wptr = 
	  p->pai_buffsize - (sizeof(struct ppp_header) + 
			     sizeof(u_short));
	mp->b_cont->b_wptr += i->ioc_count  = sizeof(int);
	goto iocack;
      }
      i->ioc_error = ENOSR;
      goto iocnak;
      
    case SIOCGIFASYNCMAP :
      if(mp->b_cont = allocb(sizeof(u_long), BPRI_MED)) {
	*(u_long *) mp->b_cont->b_wptr = p->pai_asyncmap;
	mp->b_cont->b_wptr += i->ioc_count = sizeof(u_long);
	goto iocack;
      }
      i->ioc_error = ENOSR;
      goto iocnak;
      
    case SIOCSIFASYNCMAP :
      
      if(i->ioc_count != sizeof(u_long)) {
	i->ioc_error = EINVAL;
	goto iocnak;	/* ugh, goto */
      }
      DLOG("ppp_async: SIFASYNCMAP %lx\n",*(u_long *) mp->b_cont->b_rptr);
      
      p->pai_asyncmap = *(u_long *) mp->b_cont->b_rptr;
      i->ioc_count = 0;
    iocack:;
      mp->b_datap->db_type = M_IOCACK;
      qreply(q,mp);
      break;
    iocnak:;
      i->ioc_count = 0;
      mp->b_datap->db_type = M_IOCNAK;
      qreply(q, mp);
      break;
    default:		/* unknown IOCTL call */
      putnext(q,mp);	/* pass it along */
    }
    break;		
    default :
      putnext(q,mp);	/* don't know what to do with this, so send it along*/
  }
}

static int
  ppp_async_wsrv(q)
queue_t	*q;

/* this is an incredibly ugly routine. If you see a better way of doing this, feel free
   to improve it. I'm hoping that the buffer management routines are efficient, in terms
   of dup'ing and adjusting message blocks. Hopefully we won't run out of small message
   blocks. Perhaps some counters should be kept to record how efficient this routine
   is, and how much break up of messages is required. 
   
   I'm not doing anything funny with stuffing PPP_ESCAPES into the actual data buffers.
   If you get a lot of outgoing errors .. you might need to up your
   NBLK4 parameter by quite a bit.... (see param.c)
   */
{
  register u_char	*cp;
  register PAI	*p;
  register u_short	fcs;
  register mblk_t *mp;
  u_char		*start, *stop,c;
  mblk_t	*m1, *m0, *outgoing;
  
  while((mp = getq(q)) != NULL) {
    /* we can only get M_DATA types into our Queue, due to our Put function */
    if(!canput(q->q_next)) {
      putbq(q, mp);
      return;
    }
    if(msgdsize(mp) < (sizeof(u_char) + sizeof(u_short))) {	/* at least a protocol
								   and FCS required */
      freemsg(mp);	/* discard the message */
      putctl1(OTHERQ(q), M_CTL, IF_OUTPUT_ERROR);	/* indicate an output error */
      continue;
    }
    m0 = mp;	/* remember first message block */
    p = (PAI *) q->q_ptr;
    outgoing = NULL;
    fcs = PPP_INITFCS;
#define	SPECIAL(p,c)	((c) == PPP_FLAG) || ((c) == PPP_ESCAPE) ||  \
    ((c) < 0x20 && ((p)->pai_asyncmap & (1 << (c))))
      
      /* for each block in the message, scan between the start and stop
	 markers for escaped chars. dup the message block and chain in
	 the PPP_ESCAPE char and the PPP_TRANS'd character. Continue processing
	 for all of this data block, then for all remaining blocks
	 */
      while(mp) {
	start = mp->b_rptr;
	stop = mp->b_wptr;
	while(start < stop ) 	{
	  for(cp = start; cp < stop; cp++) {
	    if(SPECIAL(p,*cp))
	      break;
	    fcs = PPP_FCS(fcs, *cp);
	  }
	  
	  if(cp - start) {
	    /* dup the message block, up to len chars */
	    m1 = dupb(mp);
	    if(!m1)
	      goto nobuffs;
	    /* discard chars at front */
	    adjmsg(m1, start - mp->b_rptr);
	    
	    /* throw away remaining chars */
	    adjmsg(m1,  cp - stop);
	    if(outgoing)
	      linkb(outgoing, m1);
	    else
	      outgoing = m1;
	  } 
	  if(cp < stop) {	/* a special char must follow */
	    m1 = allocb(2 * sizeof(u_char),BPRI_LO);
	    if(!m1)
	      goto nobuffs;
	    *m1->b_wptr++ = PPP_ESCAPE;
	    *m1->b_wptr++ = *cp ^ PPP_TRANS;
	    fcs = PPP_FCS(fcs, *cp);
	    if(outgoing)
	      linkb(outgoing, m1);
	    else
	      outgoing = m1;
	  }
	  else
	    break;	/* no sense in doing another add and 
			   compare */
	  start = cp + 1;
	}  /* end while start < stop */
	mp = mp->b_cont;	/* look at the next block */
      } /* end while(mp) */
    m1 = allocb(sizeof(u_char) + 2 * sizeof(u_short), BPRI_LO);
    if(!m1)
      goto nobuffs;
    fcs ^= 0xffff;			/* XOR the resulting FCS */
    c = fcs & 0xff;
    if(SPECIAL(p,c)) {
      *m1->b_wptr++ = PPP_ESCAPE;
      *m1->b_wptr++ = c ^ PPP_TRANS;
    }
    else
      *m1->b_wptr++ = c;
    c = fcs >> 8;
    if(SPECIAL(p,c)) {
      *m1->b_wptr++ = PPP_ESCAPE;
      *m1->b_wptr++ = c ^ PPP_TRANS;
    }
    else
      *m1->b_wptr++  = c;
    
    *m1->b_wptr++ = PPP_FLAG;	/* add trailing PPP_FLAG */
    linkb(outgoing, m1);		/* gee, we better have an outgoing by now */
    /* now we check to see if the lower queue has entries, if so, we assume
       that we don't need a leading PPP_FLAG because these packets
       will be sent back to back */
    if(!qsize(q->q_next)) {	/* no entries in next queue, have to add a leading 
				   PPP_FLAG */
      m1 = allocb(sizeof(u_char), BPRI_LO);
      if(!m1)
	goto nobuffs;
      *m1->b_wptr++ = PPP_FLAG;
      linkb(m1, outgoing);
      outgoing = m1;
    }
    /* phew, ready to ship */
    putnext(q, outgoing);
    freemsg(m0);		/* discard original message block pointers */
    continue;
  nobuffs:;	/* well, we ran out of memory somewhere */
    if(outgoing)
      freemsg(outgoing);		/* throw away what we have already */
    putbq(q, m0);				/* put back the original message */
    putctl1(OTHERQ(q), M_CTL, IF_OUTPUT_ERROR);
    qenable(q);				/* reschedule ourselves for later */
    return;
  } /* end while(getq()) */
}	/* end function */					

static int
  ppp_async_rput(q, mp)
queue_t *q;
register mblk_t *mp;
{
  
  switch (mp->b_datap->db_type) {
    
  case 	M_FLUSH :
    if(*mp->b_rptr & FLUSHR)
      flushq(q, FLUSHDATA);
    putnext(q, mp);		/* send it along too */
    break;
    
  case	M_DATA :
    putq(q, mp);	/* queue it for my service routine */
    break;
    
    default :
      putnext(q,mp);	/* don't know what to do with this, so send it along*/
  }
}

static int
  ppp_async_rsrv(q)
queue_t	*q;
{
  register mblk_t *mp;
  register PAI	*p;
  register u_char	*cp,c;
  mblk_t	*m0;
  
  p = (PAI *) q->q_ptr;
#define	INPUT_ERROR(q)	putctl1(q, M_CTL, IF_INPUT_ERROR)
#define	STUFF_CHAR(p,c)	(*(p)->pai_bufftail->b_wptr++ = (c), (p)->pai_buffcount++)
#define	FLUSHEM(q,p)	(INPUT_ERROR(q), (p)->pai_flags |= PAI_FLAGS_FLUSH)
  
  while((mp = getq(q)) != NULL) {
    /* we can only get M_DATA types into our Queue, due to our Put function */
    if(!canput(q->q_next)) {
      putbq(q, mp);
      return;
    }
    m0 = mp;	/* remember first message block */
    for(; mp != NULL; mp = mp->b_cont) {	/* for each message block */
      cp = mp->b_rptr;
      while(cp < mp->b_wptr) {
	c = *cp++;
	if(c == PPP_FLAG) {
	  p->pai_flags &= ~PAI_FLAGS_FLUSH;	/* clear flush indicater */
	  
	  if(p->pai_buffcount > sizeof(u_short)) { /* discard FCS */
	    adjmsg(p->pai_buffer, -sizeof(u_short));
	    p->pai_buffcount -= sizeof(u_short);
	  }
	  if(p->pai_buffcount < sizeof(struct ppp_header)) {
	    if(p->pai_buffcount) {
	      INPUT_ERROR(q);
	      DLOG("ppp_async: short packet\n",0);
	    }
	    p->pai_buffcount = 0;
	    continue;
	  }
	  if(p->pai_buffer) {
	    if(p->pai_fcs == PPP_GOODFCS) {
#if DEBUGS
/*
 * here is where we will dump out a frame in hex using the log() 
 * function if ppp_async_input_debug is non-zero. As this function is
 * a pig, we only print up to the number of bytes specified by the value of
 * the ppp_async_input_debug variable so as to not cause too many
 * timeouts.   <gmc@quotron.com> 
 */

	      if (ppp_async_input_debug > 0) {
		register mblk_t *mptr = p->pai_buffer;
		register u_char *rptr;
		register u_int i, mlen;
		u_int frame_length = (unsigned) p->pai_buffcount;
		char buf[2*MAX_DUMP_BYTES+4];	/* tmp buffer */
		char *bp = buf;
		static char digits[] = "0123456789abcdef";

		log(LOG_INFO, "ppp_async: got input frame of %d bytes\n", frame_length); 
		rptr = mptr->b_rptr; /* get pointer to beginning  */
		mlen = mptr->b_wptr - rptr; /* get length of this dblock */

		/* only dump up to MAX_DUMP_BYTES */

		i = (frame_length < ppp_async_input_debug) ? frame_length :
		  ppp_async_input_debug;   

		while (i--) {	/* convert to ascii hex */
		  if (mlen-- == 0) {/* get next dblock */
		    mptr = mptr->b_cont;
		    if (mptr) { /* are we done? */
		      rptr = mptr->b_rptr; /* nope, get next dblock */
		      mlen = mptr->b_wptr - rptr;
		    }
		    else {	/* no more dblocks */
		      if (i != 0)
			log(LOG_ERR, "ppp_async: ran out of data! (this shouldn't happen\n");
		      break;
		    }
		  }
		  *bp++ = digits[*rptr >> 4]; /* convert byte to ascii hex */
		  *bp++ = digits[*rptr++ & 0xf];
		}
		  
		/* add a '>' to show that frame was truncated*/
		if (ppp_async_input_debug < frame_length)
		  *bp++ = '>';
		*bp = 0;
		log(LOG_INFO,"ppp_async: %s\n", buf); 
	      }
#endif /*DEBUGS*/
	      putnext(q, p->pai_buffer);
	    }
	    else {
	      INPUT_ERROR(q);
	      freemsg(p->pai_buffer);
	      DLOG("ppp_async: FCS Error\n",0);
	      /* we could be discarding this buffer because of data errors
		 in which case we could have kept it, however we might
		 also be discarding it because it was too small. so
		 we throw it away and the next allocation will be the
		 correct size
		 */
	    }
	    p->pai_buffer = NULL;
	    p->pai_buffcount  = 0;
	    continue;
	  }
	} 	/* c == PPP_FLAG */
	else if (p->pai_flags & PAI_FLAGS_FLUSH)
	  continue;			/* skipping chars */
	else if (c == PPP_ESCAPE) {
	  p->pai_flags |= PAI_FLAGS_ESCAPED;
	  continue;
	}
	
	if(p->pai_flags & PAI_FLAGS_ESCAPED) {
	  p->pai_flags &= ~PAI_FLAGS_ESCAPED; 	/* clear esc flag */
	  c ^= PPP_TRANS;		/* xor's are cool */
	}
	
	/* here we check to see if we have a buffer. If we don't, we assume
	   that this is the first char for the buffer, and we allocb one */
	
	if(!p->pai_buffer) {
	  /* we allocate buffer chains in blocks of ALLOCBSIZE */
	  
	  if(!(p->pai_buffer = allocb(ALLOCBSIZE, BPRI_MED))) {
	    FLUSHEM(q,p);
	    continue;
	    /* if we don't get a buffer, is there some way
	       to recover and requeue later? rather than flushing
	       the current packet... ? */
	  }
	  p->pai_bufftail = p->pai_buffer;
	}
	if(!p->pai_buffcount) {
	  p->pai_fcs = PPP_INITFCS;
	  if(c != PPP_ALLSTATIONS) {
	    if(p->pai_flags & PAI_FLAGS_COMPAC) {
	      STUFF_CHAR(p,PPP_ALLSTATIONS);
	      STUFF_CHAR(p,PPP_UI);
	    }
	    else {
	      DLOG("ppp_async: missed ALLSTATIONS\n",0);
	      FLUSHEM(q,p);
	      continue;
	    }
	  }
	} /* end if !p->pai_buffcount */
	if(p->pai_buffcount == 1 && c != PPP_UI) {
	  DLOG("ppp_async: missed UI\n",0);
	  FLUSHEM(q,p);
	  continue;
	}
	if(p->pai_buffcount == 2 && (c & 1) == 1) {
	  if(p->pai_flags & PAI_FLAGS_COMPPROT)
	    STUFF_CHAR(p, 0);
	  else {
	    DLOG("ppp_async: bad protocol high byte\n",0);
	    FLUSHEM(q,p);
	    continue;
	  }
	}
	if(p->pai_buffcount == 3 && (c & 1) == 0) {
	  DLOG("ppp_async: bad protocol low byte\n",0);
	  FLUSHEM(q,p);
	  continue;
	}
	if(p->pai_buffcount >= p->pai_buffsize)	{	/* overrun */
	  DLOG("ppp_async: too many chars in input buffer %d\n", p->pai_buffcount);
	  FLUSHEM(q,p);
	  continue;
	}
	/* determine if we have enough space in the buffer */
	if(p->pai_bufftail->b_wptr >= p->pai_bufftail->b_datap->db_lim) {
	  if(p->pai_bufftail = allocb(ALLOCBSIZE, BPRI_MED)) 
	    linkb(p->pai_buffer, p->pai_bufftail);
	  else {
	    DLOG("ppp_async: couldn't get buffer for tail\n",0);
	    FLUSHEM(q,p);	/* discard all of it */
	    continue;
	  }
	}
	STUFF_CHAR(p,c);
	p->pai_fcs = PPP_FCS(p->pai_fcs, c);
      } /* end while cp  < wptr */
    }	/* end for each block */
    /* discard this message now */
    freemsg(m0);
  }	/* end while  getq */
  
}

#endif /* NPPP > 0 */
