/* Copyright 1987 by the Massachusetts Institute of Technology */
/* See permission and disclaimer notice in file notice.h */
#include	<notice.h>

/* SLIP network driver.  This file contains the routines which do the SLIP
 * protocol framing and data transparency (i.e. byte stuffing).
 */

/*
 *------------------------------------------------------------------
 *
 * $Source: /mit/cgw/src/gw/nets/RCS/slip.c,v $
 * $Revision: 1.1 $
 * $Date: 88/06/05 22:40:02 $
 * $State: Exp $
 * $Author: jon $
 * $Locker: jon $
 *
 * $Log:	slip.c,v $
 * Revision 1.1  88/06/05  22:40:02  jon
 * Initial revision
 * 
 *------------------------------------------------------------------
 */

#ifndef lint
static char *rcsid_slip_c = "$Header: slip.c,v 1.1 88/06/05 22:40:02 jon Locked $";
#endif	lint

#include	<types.h>
#include	<sys.h>
#include	"../src/const.h"
#include	"../src/param.h"
#include	"../src/defs.h"
#include	"../src/macs.h"
#include	"../src/net.h"
#include	"../src/ext.h"
#include	"../in/in.h"
#include	"../dev.vax/dhv.h"

#include	"slip.h"

/* Protocol initialization.  SLIP doesn't have a type field so it can
 * only handle IP packets.  Also sets the interrupt level protocol
 * handling routine on the input device.
 */
slip_prinit(netp, prot, nm)
net *netp;
unsw prot;
word nm;
{
    void slip_ibyte(), slip_obytes();

    if (netp->n_type != ((unsb)T_SLIP))
      bughalt("slip_prinit called but not SLIP");

    if (prot != P_IN)
      bughalt("Protocol for SLIP was not IP");

    netp->n_idevp->d_rc = (unsw)slip_ibyte;
    netp->n_odevp->d_rc = (unsw)slip_obytes;
}

/* This routine is called when a packet is recieved.  It just passes
 * it up to the IP forwarder.  The byte unstuffing has already been
 * taken care of by slip_ibyte().  I suppose the net input routine
 * could just be made inaddq, but that seems wrong somehow.
 */
slip_in(iob)
iorb *iob;
{
    inaddq(iob);
}

/* This is called at interrupt level as each byte is received.  The
 * framing and byte unstuffing are handled here.  The format of the
 * character is identical to that returned in the received SILO of a
 * DHV-11 (a consequence of that being the first and so far only
 * async. serial line device).
 */
#define iob_putc(c) *devp->d_rfnt++ = (unsb)c;
#define I_ESC I_USR2

slip_ibyte(devp, c)
reg dct *devp;
reg unss c;
{
    reg iorb *iob;

    if ((iob = devp->d_qhd) == NULL)
      return;

    if (c & (PARITY|FRAME|OVERRUN)) {
	if (devp->d_addr != devp->d_rfnt) {
	    iob->i_stat |= I_ERR;
	    iocmr(devp);
	}
	return;
    }

    c &= 0xff;
    if (c == FEND) {
	/* If havn't received any bytes yet, ignore */
	if (devp->d_addr != devp->d_rfnt) {
	    iob->i_stat |= I_DONE;
	    iob->i_bxfr = devp->d_rfnt - devp->d_addr;
	    iocmr(devp);
	}
	return;
    }

    /* check for overflow */
    if (devp->d_rfnt == devp->d_rend) {
	iob->i_stat |= I_ERR;
	iocmr(devp);
	return;
    }

    if (iob->i_stat & I_ESC) {
	iob->i_stat &= ~I_ESC;
	switch (c) {
	case TFEND:
	    iob_putc(FEND); break;
	case TFESC:
	    iob_putc(FESC); break;
	}
    }
    else if (c == FESC)
      iob->i_stat |= I_ESC;
    else
      iob_putc(c);
}

/* Routine to send a SLIP packet.  Just passes the packet on to the
 * normal network handling code.  The SLIP protocol stuff (framing and
 * byte stuffing) is handled by slip_obytes which is called from the
 * device driver. */
slip_out(iob, netp, prot, nm)
iorb *iob;
net *netp;
unsw prot;
word nm;
{
    if (prot != P_IN)
      bughalt("Bad protocol in slip_out");

    return (netsend(iob, netp));
}

#if old_byte_stuffing
    reg unsb *cp, *end;

    /* Byte stuff */
    for (cp = iob->i_addr, end = iob->i_addr + iob->i_breq; cp < end; cp++) {
	if ((*cp == FEND) || (*cp == FESC)) {
	    copy(cp, cp+1, end - cp);
	    iob->i_breq++; end++;
	    *cp++ = FESC;
	    if (*cp == FEND)
	      *cp = TFEND;
	    else
	      *cp = TFESC;
	}
    }

    /* Put framing character around packet */
    iob->i_addr--;
    iob->i_breq++;
    *iob->i_addr = FEND;
    iob->i_addr[iob->i_breq] = FEND;
    iob->i_breq++;
#endif

/* Called by the device driver to get the bytes to send.  The framing
 * and byte stuffing is done here.  The data to send is pointed to by
 * devp->d_rfnt.  The number of bytes to send is returned (zero if end
 * of packet).  */
slip_obytes(devp)
dct *devp;
{
    reg iorb *iob;
    reg byte *cp;
    unss cnt;
    unss breq;

    /* End of packet check */
    if (devp->d_breq == 0)
      return 0;

    if ((iob = devp->d_qhd) == NULL)
      return 0;

    /* Figure out what character to put on the front. */
    devp->d_rfnt = cp = devp->d_addr - 1;
    breq = devp->d_breq;
    if ((iob->i_stat & I_ESC) == 0) { /* First time */
	iob->i_stat |= I_ESC;
	*cp++ = FEND;	/* Packet frame */
    }
    else {
	breq--;
	if (iob->i_usr1 == FEND) /* Stuffing an FEND */
	  *cp++ = TFEND;
	else			/* Stuffing an FESC */
	  *cp++ = TFESC;
    }
    cnt = 1;

    /* Look ahead for a character that needs to be stuffed */
    for (; breq; breq--, cp++, cnt++) {
	if ((*cp == FEND) || (*cp == FESC)) {
	    iob->i_usr1 = *cp;
	    *cp++ = FESC;
	    cnt++;
	    devp->d_addr = cp;
	    devp->d_breq = breq;
	    return cnt;
	}
    }
    devp->d_breq = 0;

    /* If end up here then no more bytes to stuff; put on trailing
     * framing character */
    *cp = FEND;
    return cnt+1;
}
