/*-------------------------------------------------------------------------*\
|* File name:	NEW_SLIP.C						Revision date:	1996.08.17 *|
\*-------------------------------------------------------------------------*/

#define URANTEST 1

/*  slip.c  (c) Steve Adam 1995         steve@netinfo.com.au
 *
 *          Module contains functions for input/output
 *          of slip packets.  Slip conversions are performed
 *          here.  Note that both of these functions are
 *          interruptable.  That is, they do what work they
 *          can then return.  If called again later, they
 *          continue at the same point.
 *
 *          The output queue functions are also here.
 *
 *          Based on RFC1055
 */

#include <stdio.h>      /* Included for NULL.  Might change that later...   */
#include <tos.h>
#include <string.h>
#include "device.h"
#include <stdlib.h>

#include "globdefs.h"
#include "globdecl.h"

extern DEV_LIST *bdev;


/*extern void  *memcpy (void *, const void *, unsigned long);*/
extern int     memcmp (const void *, const void *, unsigned long);
extern void    *memset( void *ptr, int val, size_t len );
/*extern void  *bzero (void *, unsigned long);*/


/* SLIP special character codes
 */
#define END             0300    /* indicates end of packet */
#define ESC             0333    /* indicates byte stuffing */
#define ESC_END         0334    /* ESC ESC_END means END data byte */
#define ESC_ESC         0335    /* ESC ESC_ESC means ESC data byte */

/* Bits in first octet of compressed packet */
#define NEW_C  0x40 /* flag bits for what changed in a packet */
#define NEW_I  0x20
#define NEW_S  0x08
#define NEW_A  0x04
#define NEW_W  0x02
#define NEW_U  0x01

#define TCP_PUSH_BIT 0x10

/* reserved, special-case values of above */
#define SPECIAL_I   (NEW_S|NEW_W|NEW_U)   /* echoed interactive traffic */
#define SPECIAL_D   (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */
#define SPECIALS_MASK    (NEW_S|NEW_A|NEW_W|NEW_U)


#define BCMP(p1,p2,n)         memcmp((p2), (p1), (uint32)(n))
#define BCOPY(p1,p2,n)        memcpy((p2), (p1), (uint32)(n))
#define ovbcopy(p1,p2,n) memcpy((p2), (p1), (uint32)(n))

extern struct slip *myslip;
extern struct slcompress *comp;
extern struct cstate mytstate[MAX_STATES];
extern struct cstate myrstate[MAX_STATES];


/*  slip_in()
 *
 *      Receives packet data from the serial port and adds it to
 *  the data buffer in the packet structure.  If no packet is
 *  currently allocated, then allocate one.  If no data is
 *  available from the serial port, then return, but REMEMBER
 *  where we're up to so we can continue later.  When a packet
 *  has been completely received, pass it to ip_in() for
 *  further processing.  We don't care what's in the packets,
 *  but if the length is less than an IP header, throw it away.
 *  Do SLIP special character processing in an interruptable way.
 *  (ie: We can return from this function in the middle of things..)
 */

void slip_in(void)
{
    static GPKT *inpkt = (GPKT *)NULL;  /* Packet being read            */
    static char *nextin = (char *)NULL; /* Pointer to next buffer octet */
    /*static int esc_read = FALSE;        Was ESC the last byte read?  */
    static uint16 buf_avail;            /* Max number of chars to read  */
    static uint16 bufsize = 0;          /* Current buffer size          */

    int newch;  /* New character read from serial port              */
    uint16 len; /* Length of packet received.  (Calculated later)   */
    char *mblk;

/*new_packet:*/

    if (inpkt == (GPKT *)NULL) {    /* No current packet, allocate one  */
        bufsize = buf_avail = cfg.mtu;
        inpkt = allocate_packet(bufsize + 68);
        if (inpkt == (GPKT *)NULL)  /* What, no memory? LOG_IT          */
            return;                 /* Try again later                  */

        nextin = inpkt->pip;        /* Init pointer to start of buffer  */
        /*esc_read = FALSE;            Init ESC state flag              */
    }
    else {      /* Check to see if we've about to overflow our buffer   */
                /* If we are, then realloc() to a larger buffer.  This  */
                /* *will* force a copy, but "Be liberal etc."           */
                /* Hopefully this will never happen. (Yeah, right!)     */
        if (buf_avail == 0) {
            buf_avail = cfg.mtu;            /* reset buf_avail      */
            len = bufsize;                  /* Save old size        */
            bufsize += buf_avail;           /* increase bufsize     */
            mblk = KRrealloc(inpkt->pip, (long)bufsize + 128); /* was 68 */
            if (mblk == (char *)NULL)
                ;   /* Ooops, out of memory. LOG_IT and worry later...  */
            else
                inpkt->fp = inpkt->pip = mblk;

            nextin = &inpkt->pip[len];  /* Reinit data pointer          */
        }
    }

    while (TRUE) {      /* Loop till no more data or packet complete    */
        if (CharAvailable(bdev) == FALSE)    /* ATARI: If no serial char waiting */
               return;             /* then return.  Try again later        */

        newch = PortGetByte(bdev) & 0xff;/*  ATARI: get serial byte.  The */
                                        /* 0xff mask is just in case.   */
                                        
             switch(newch) 
                 {
                 case ESC_END:
                         *nextin++ = (cfg.slp->flags & SLF_ESC) ? END : newch;
                         cfg.slp->flags &= ~SLF_ESC;
                     --buf_avail;
                     continue;

                 case ESC_ESC:
                         *nextin++ = (cfg.slp->flags & SLF_ESC) ? ESC : newch;
                         cfg.slp->flags &= ~SLF_ESC;
                     --buf_avail;
                     continue;

                 /* If we fall through to default: then an inappropriate character
                  * followed the ESC character.  This an error condition. LOG_IT
                  * RFC1055 suggested copying the next character as is.  I don't
                  * have any better ideas so...  (Should we discard packet?)
                  */

                       case END:                       /* End of current packet            */
                        len = (uint16)(nextin - inpkt->pip);  /* pkt length.                      */

                              if (len)
                                   {
                                     inpkt->ip_len = len;                /* Set packet length    */
     
                                 /* realloc should return same block if newsize < oldsize    */
                                      /* so this operation should *NOT* force a memory copy       */
                                     /* The extra 68 bytes is to allow a ICMP error packet to be */
                                 /* constructed from this pkt if necessary.  68 bytes is the */
                                 /* most such a packet would need (max ip header + 8 bytes)  */

                                  inpkt->pip=inpkt->fp = KRrealloc(inpkt->pip, (long)len + 128); /*was 68 */

                                        /*printf("Before uncompress\r\n");*/

                                        uncompre(inpkt, cfg.slp); /*removed
extra ss*/
                                        
                                        /*printf("After uncompress\r\n");*/
                                             
                                        if (cfg.slp->flags & SLF_TOSS)
                                             {
                                                  printf("deleting packet\r\n");
                                                delete_packet(inpkt, EI_SUM);  /* This is how we `ignore' packets  */
                                                return;
                                             }

                                        /*printf("back from uncompress and delete test\r\n");*/

                                        ip_in(inpkt);                   /* Pass the packet to IP    */
     
                                        /*printf("back from ip_in\r\n");*/
     
                                     inpkt = (GPKT *)NULL;           /* Clear our `state'        */

                                      bufsize = buf_avail = cfg.mtu;
                                      inpkt = allocate_packet(bufsize + 68);
                                      if (inpkt == (GPKT *)NULL)   /*What, no memory? LOG_IT          */
                                      return;                  /*Try again later                  */
                                      nextin = inpkt->pip;         /*Init pointer to start of buffer  */

                                        /*printf("before goto new packet \r\n");*/
                                   
                                        goto new_packet;
                                        /*return;                     And give everyone else a go  */
                                   }
                               break;

                       case ESC:       /* An escape character means skip to the next char  */
                              cfg.slp->flags |= SLF_ESC;    /* But set ESC state TRUE first...  */
                           break;

                    default:                /* Default case, just copy the char.        */
                              cfg.slp->flags &= ~SLF_ESC;
                        *nextin++ = (char)newch;
                        --buf_avail;
                              break;
                     }
        
        
new_packet:
;
    }
     /*return;*/
    /* This function should never exit here */
}


/*  output_packet()
 *
 *      This function adds a completely processed IP packet to
 *  an output queue.  The queue is just a chain of GPKT's.  They
 *  are chained using the `.pnout' pointer of GPKT.  NULL means
 *  end of list.  New packets are always added to the end of the
 *  list and and later dequeued (by get_outpacket()) from the front.
 *
 *      This function also calls slip_out().  This should always
 *  happen when a packet goes on the queue anyway.  slip_out()
 *  can still be called seperately from elsewhere, though.
 */

static GPKT *outqueue = (GPKT *)NULL;   /* Initial value must be NULL       */

void output_packet(GPKT *opkt)
{
    GPKT *endqueue = outqueue;

/*   extern int pktfd;
    if (pktfd <= 0)
          pktfd = Fcreate("pkt.log", 0);

    Fwrite(pktfd, 2L, &opkt->ip_len);
    Fwrite(pktfd, (long)opkt->ip_len, opkt->pip);
*/
    opkt->next = (GPKT *)NULL; /* It should have been NULL anyway but....  */

    if (outqueue == (GPKT *)NULL) {  /* Queue was empty                      */
        outqueue = opkt;
    }
    else {
        while (endqueue->next != (GPKT *)NULL) /* advance to end of chain   */
            endqueue = endqueue->next;

        endqueue->next = opkt;
    }

     /*printf("in output packet right before slip_out\r\n");*/

    slip_out();
    
    /*printf("Got back from slip_out\r\n");*/
}


/*  get_outpacket()
 *
 *      Return the next packet from the output queue or NULL if none.
 *  If a packet is present, remove it from the queue before returning it.
 */

static GPKT *get_outpacket(void)
{
    GPKT *next_packet = outqueue;

    if (next_packet == (GPKT *)NULL)
        return (GPKT *)NULL;

    outqueue = outqueue->next;  /* Advance pointer (possibly to NULL)   */

    return next_packet;
}


/*  slip_out()
 *
 *      Sends a packet out the serial port, inserting SLIP
 *  special characters at the same time.  If no packet is
 *  currently being sent, get the next packet from the
 *  output queue.  If no packet is available, return.
 *  If the serial buffer can't accept any more characters,
 *  then return.  In this case, REMEMBER where we are up to
 *  so we can continue later.  When a packet has been
 *  completely sent, delete it.
 *
 *      When an ESC is sent this function *doesn't* assume that
 *  the serial buffer is available for just one more char.
 *  Therefore, the condition is noted for special action.
 */

static void ser_out(int c)
{
    PortSendByte(bdev, (char)c);
}


void slip_out(void)
{
    static GPKT *outpkt = (GPKT *)NULL; /* Packet being written             */
    static unsigned char *nextout = (unsigned char *)NULL;   /* next buffer octet    */
    static int esc_sent = FALSE;        /* Was ESC the last byte written    */
    static uint16 out_len;              /* number of chars to write         */
     struct ip_header *ip;

new_out_packet:

/*printf("in slip_out after new_out_packet\r\n");*/

    if (outpkt == (GPKT *)NULL) {       /* No packet being processed        */

          /*printf("attempting to get new packet\r\n");*/

        outpkt = get_outpacket();       /* So get one, if any               */
        if (outpkt == (GPKT *)NULL)
            return;                     /* If not, return;  Try later.      */
        out_len = outpkt->ip_len;       /* Number of chars to send          */
        nextout = (unsigned char *)outpkt->pip; /* Start of buffer to send          */
        esc_sent = FALSE;               /* Make it a *real* init            */

          /*printf("Have set variables for new packet\r\n");*/

          ip = (IP_HDR *)outpkt->pip;

          if (cfg.slp->flags & SLF_COMPRESS &&
                   ip->ptcl == IPPROTO_TCP)
               {
                    /*printf("Before slc compress!!#!#!#\r\n");*/
               
                    *outpkt->pip |= slc_comp(outpkt, cfg.slp->comp, cfg.slp->flags & SLF_COMPCID);/* removed ress */

                    /*printf("after slc_compress\r\n");*/

                    nextout = (unsigned char *)outpkt->pip;
                    out_len = outpkt->ip_len;
                    
                    /*printf("after the rest of the junk in slccompress test\r\n");*/
               }

                                                  /* Send an initial end, per RFC1055 */
        ser_out(END);                   /* But only if com buffer not full  */
    }


    while (TRUE) {          /* Send chars until buffer full, or all sent    */
          /* The test for output buffer full might not be supported
           *   by DEVICE.LIB  I'll have to check up on that...
           */

        if (out_len == 0) {             /* Packet is completely sent        */

               /*printf("Packet completely sent\r\n");*/

            ser_out(END);               /* ATARI:So send an END             */
            
            /*printf("END sent\r\n");*/
            
            delete_packet(outpkt, NS_SENT);
            
            /*printf("After delete packet\r\n");*/

            outpkt = (GPKT *)NULL;      /* All done..*/
            
            /*printf("Before goto new_out_packet\r\n");                       */

               goto new_out_packet;
            /* return;                      Like slip_in(), we return now    */
        }   /* Possibly better for slip_out() to continue if more queued!   */

        if (*nextout == ESC || *nextout == END) {   /* A special case char  */
            if (esc_sent) {             /* But we ESC'd it last time around */
                if (*nextout == ESC) {
                    ser_out(ESC_ESC);    /* ATARI: so send a ESC_ESC */
                }
                else {
                    ser_out(ESC_END);   /* ATARI: or send a ESC_END */
                }
                esc_sent = FALSE;
                ++nextout;
                --out_len;
            }
            else {
                ser_out(ESC);           /* ATARI: Send an ESC char  */
                esc_sent = TRUE;
            }
        }
        else {
            ser_out(*nextout);  /* ATARI: Otherwise just send the char  */
            ++nextout;
            --out_len;
        }
    }
    
    /* Like slip_in() this function never gets here.    */
}

#if	defined(URANTEST)
main()
{
return 0;
}
#endif

/*-------------------------------------------------------------------------*\
|* End of file:	NEW_SLIP.C												   *|
\*-------------------------------------------------------------------------*/
