/*++

Copyright (c) 1993  Microsoft Corporation

Module Name:

    vjslip.c

Abstract:

Author:

    Thomas J. Dimitri  (TommyD)

Environment:

Revision History:

--*/

#include "wan.h"

#define __FILE_SIG__    VJ_FILESIG

#if 0
NPAGED_LOOKASIDE_LIST   VJCtxList; // List of free vj context descs
#endif

#define INCR(counter) ++comp->counter;

//   A.2  Compression
//
//   This routine looks daunting but isn't really.  The code splits into four
//   approximately equal sized sections:  The first quarter manages a
//   circularly linked, least-recently-used list of `active' TCP
//   connections./47/  The second figures out the sequence/ack/window/urg
//   changes and builds the bulk of the compressed packet.  The third handles
//   the special-case encodings.  The last quarter does packet ID and
//   connection ID encoding and replaces the original packet header with the
//   compressed header.
//
//   The arguments to this routine are a pointer to a packet to be
//   compressed, a pointer to the compression state data for the serial line,
//   and a flag which enables or disables connection id (C bit) compression.
//
//   Compression is done `in-place' so, if a compressed packet is created,
//   both the start address and length of the incoming packet (the off and
//   len fields of m) will be updated to reflect the removal of the original
//   header and its replacement by the compressed header.  If either a
//   compressed or uncompressed packet is created, the compression state is
//   updated.  This routines returns the packet type for the transmit framer
//   (TYPE_IP, TYPE_UNCOMPRESSED_TCP or TYPE_COMPRESSED_TCP).
//
//   Because 16 and 32 bit arithmetic is done on various header fields, the
//   incoming IP packet must be aligned appropriately (e.g., on a SPARC, the
//   IP header is aligned on a 32-bit boundary).  Substantial changes would
//   have to be made to the code below if this were not true (and it would
//   probably be cheaper to byte copy the incoming header to somewhere
//   correctly aligned than to make those changes).
//
//   Note that the outgoing packet will be aligned arbitrarily (e.g., it
//   could easily start on an odd-byte boundary).
//

UCHAR
sl_compress_tcp(
    PUUCHAR UNALIGNED   *m_off,         // Frame start (points to IP header)
    ULONG               *m_len,         // Length of entire frame
    ULONG               *precomph_len,  // Length of TCP/IP header pre-comp
    ULONG               *postcomph_len, // Length of TCP/IP header post-comp
    struct slcompress   *comp,          // Compression struct for this link
    ULONG compress_cid) {               // Compress connection id boolean

    struct cstate *cs = comp->last_cs->cs_next;
    struct ip_v4 UNALIGNED *ip = (struct ip_v4 UNALIGNED *)*m_off;
    struct ip_v4 UNALIGNED *csip;
    ULONG hlen = ip->ip_hl & 0x0F;      // last 4 bits are the length
    struct tcphdr UNALIGNED *oth;       /* last TCP header */
    struct tcphdr UNALIGNED *th;        /* current TCP header */

//   ----------------------------
//    47. The two most common operations on the connection list are a `find'
//   that terminates at the first entry (a new packet for the most recently
//   used connection) and moving the last entry on the list to the head of
//   the list (the first packet from a new connection).  A circular list
//   efficiently handles these two operations.

    ULONG deltaS, deltaA;     /* general purpose temporaries */
    ULONG changes = 0;        /* change mask */
    UCHAR new_seq[16];       /* changes from last to current */
    UCHAR UNALIGNED *cp = new_seq;
    USHORT ip_len;

    /*
     * Bail if this is an IP fragment or if the TCP packet isn't
     * `compressible' (i.e., ACK isn't set or some other control bit is
     * set).  Or if it does not contain the TCP protocol.
     */
    if ((ip->ip_off & 0xff3f) || *m_len < 40 || ip->ip_p != IPPROTO_TCP)
         return (TYPE_IP);

    th = (struct tcphdr UNALIGNED *) & ((ULONG UNALIGNED *) ip)[hlen];
    if ((th->th_flags & (TH_SYN | TH_FIN | TH_RST | TH_ACK)) != TH_ACK)
         return (TYPE_IP);

    //
    // The TCP/IP stack is propagating the padding bytes that it
    // is receiving off of the LAN.  This shows up here as a
    // packet that has a length that is greater than the IP datagram
    // length.  We will add this work around for now.
    //
    if (*m_len > ntohs(ip->ip_len)) {
        *m_len = ntohs(ip->ip_len);
    }

    /*
     * Packet is compressible -- we're going to send either a
     * COMPRESSED_TCP or UNCOMPRESSED_TCP packet.  Either way we need to
     * locate (or create) the connection state.  Special case the most
     * recently used connection since it's most likely to be used again &
     * we don't have to do any reordering if it's used.
     */

    //
    // Keep stats here
    //
    INCR(OutPackets);

    csip = (struct ip_v4 UNALIGNED*)&cs->cs_ip;

    if (ip->ip_src.s_addr != csip->ip_src.s_addr ||
        ip->ip_dst.s_addr != csip->ip_dst.s_addr ||
        *(ULONG UNALIGNED *) th != ((ULONG UNALIGNED *) csip)[csip->ip_hl & 0x0F]) {

         /*
          * Wasn't the first -- search for it.
          *
          * States are kept in a circularly linked list with last_cs
          * pointing to the end of the list.  The list is kept in lru
          * order by moving a state to the head of the list whenever
          * it is referenced.  Since the list is short and,
          * empirically, the connection we want is almost always near
          * the front, we locate states via linear search.  If we
          * don't find a state for the datagram, the oldest state is
          * (re-)used.
          */
         struct cstate *lcs;
         struct cstate *lastcs = comp->last_cs;

         do {
              lcs = cs;
              cs = cs->cs_next;
              INCR(OutSearches);

              csip = (struct ip_v4 UNALIGNED*)&cs->cs_ip;

              if (ip->ip_src.s_addr == csip->ip_src.s_addr &&
                  ip->ip_dst.s_addr == csip->ip_dst.s_addr &&
                  *(ULONG UNALIGNED *) th == ((ULONG UNALIGNED *) csip)[cs->cs_ip.ip_hl & 0x0F])

                   goto found;

         } while (cs != lastcs);

         /*
          * Didn't find it -- re-use oldest cstate.  Send an
          * uncompressed packet that tells the other side what
          * connection number we're using for this conversation. Note
          * that since the state list is circular, the oldest state
          * points to the newest and we only need to set last_cs to
          * update the lru linkage.
          */

         INCR(OutMisses);

         //
         // A miss!
         //
         comp->last_cs = lcs;
         hlen += (th->th_off >> 4);
         hlen <<= 2;

         if (hlen > *m_len) {
             return(TYPE_IP);
         }

         goto uncompressed;

found:
         /* Found it -- move to the front on the connection list. */
         if (cs == lastcs)
              comp->last_cs = lcs;
         else {
              lcs->cs_next = cs->cs_next;
              cs->cs_next = lastcs->cs_next;
              lastcs->cs_next = cs;
         }
    }

    /*
     * Make sure that only what we expect to change changed. The first
     * line of the `if' checks the IP protocol version, header length &
     * type of service.  The 2nd line checks the "Don't fragment" bit.
     * The 3rd line checks the time-to-live and protocol (the protocol
     * check is unnecessary but costless).  The 4th line checks the TCP
     * header length.  The 5th line checks IP options, if any.  The 6th
     * line checks TCP options, if any.  If any of these things are
     * different between the previous & current datagram, we send the
     * current datagram `uncompressed'.
     */
    oth = (struct tcphdr UNALIGNED *) & ((ULONG UNALIGNED *) csip)[hlen];
    deltaS = hlen;
    hlen += (th->th_off >> 4);
    hlen <<= 2;

    //
    // Bug fix?  It's in cslip.tar.Z
    //
    if (hlen > *m_len) {
        NdisWanDbgOut(DBG_FAILURE, DBG_VJ,("Bad TCP packet length"));
        return(TYPE_IP);
    }

    if (((USHORT UNALIGNED *) ip)[0] != ((USHORT UNALIGNED *) csip)[0] ||
        ((USHORT UNALIGNED *) ip)[3] != ((USHORT UNALIGNED *) csip)[3] ||
        ((USHORT UNALIGNED *) ip)[4] != ((USHORT UNALIGNED *) csip)[4] ||
        (th->th_off >> 4) != (oth->th_off >> 4) ||
        (deltaS > 5 &&
         memcmp((UCHAR UNALIGNED *)(ip + 1), (UCHAR UNALIGNED *)(csip + 1), (deltaS - 5) << 2)) ||
        ((th->th_off >> 4) > 5 &&
         memcmp((UCHAR UNALIGNED *)(th + 1), (UCHAR UNALIGNED *)(oth + 1), ((th->th_off >> 4) - 5) << 2))) {

        goto uncompressed;
    }

    /*
     * Figure out which of the changing fields changed.  The receiver
     * expects changes in the order: urgent, window, ack, seq.
     */
    if (th->th_flags & TH_URG) {
         deltaS = ntohs(th->th_urp);
         ENCODEZ(deltaS);
         changes |= NEW_U;
    } else if (th->th_urp != oth->th_urp) {
    
         /*
          * argh! URG not set but urp changed -- a sensible
          * implementation should never do this but RFC793 doesn't
          * prohibit the change so we have to deal with it.
          */
         goto uncompressed;
    }

    if (deltaS = (USHORT) (ntohs(th->th_win) - ntohs(oth->th_win))) {
         ENCODE(deltaS);
         changes |= NEW_W;
    }
    if (deltaA = ntohl(th->th_ack) - ntohl(oth->th_ack)) {
        if (deltaA > 0xffff) {
            goto uncompressed;
        }

         ENCODE(deltaA);
         changes |= NEW_A;
    }
    if (deltaS = ntohl(th->th_seq) - ntohl(oth->th_seq)) {
        if (deltaS > 0xffff) {
            goto uncompressed;
        }

         ENCODE(deltaS);
         changes |= NEW_S;
    }

    ip_len = ntohs(csip->ip_len);

    /*
     * Look for the special-case encodings.
     */
    switch (changes) {

    case 0:
         /*
          * Nothing changed. If this packet contains data and the last
          * one didn't, this is probably a data packet following an
          * ack (normal on an interactive connection) and we send it
          * compressed.  Otherwise it's probably a retransmit,
          * retransmitted ack or window probe.  Send it uncompressed
          * in case the other side missed the compressed version.
          */
         if (ip->ip_len != csip->ip_len &&
             ip_len == hlen)

              break;

         /* (fall through) */

    case SPECIAL_I:
    case SPECIAL_D:
         /*
          * Actual changes match one of our special case encodings --
          * send packet uncompressed.
          */
         goto uncompressed;

    case NEW_S | NEW_A:
         if (deltaS == deltaA &&
             deltaS == (ip_len - hlen)) {
              /* special case for echoed terminal traffic */
              changes = SPECIAL_I;
              cp = new_seq;
         }
         break;

    case NEW_S:
         if (deltaS == (ip_len - hlen)) {
              /* special case for data xfer */
              changes = SPECIAL_D;
              cp = new_seq;
         }
         break;
    }

    deltaS = ntohs(ip->ip_id) - ntohs(csip->ip_id);

    if (deltaS != 1) {
         ENCODEZ(deltaS);
         changes |= NEW_I;
    }

    if (th->th_flags & TH_PUSH)
         changes |= TCP_PUSH_BIT;
    /*
     * Grab the cksum before we overwrite it below.  Then update our
     * state with this packet's header.
     */
    deltaA = (th->th_sumhi << 8) + th->th_sumlo;

    NdisMoveMemory((UCHAR UNALIGNED *)csip,
                   (UCHAR UNALIGNED *)ip,
                   hlen);

    /*
     * We want to use the original packet as our compressed packet. (cp -
     * new_seq) is the number of bytes we need for compressed sequence
     * numbers.  In addition we need one byte for the change mask, one
     * for the connection id and two for the tcp checksum. So, (cp -
     * new_seq) + 4 bytes of header are needed.  hlen is how many bytes
     * of the original packet to toss so subtract the two to get the new
     * packet size.
     */
    deltaS = (ULONG)(cp - new_seq);
    cp = (UCHAR UNALIGNED *) ip;
    *precomph_len = hlen;

    if (compress_cid == 0 || comp->last_xmit != cs->cs_id) {
         comp->last_xmit = cs->cs_id;
         hlen -= deltaS + 4;
         *postcomph_len = deltaS + 4;
         cp += hlen;
         *cp++ = (UCHAR)(changes | NEW_C);
         *cp++ = cs->cs_id;
    } else {
         hlen -= deltaS + 3;
         *postcomph_len = deltaS + 3;
         cp += hlen;
         *cp++ = (UCHAR)changes;
    }

    *m_len -= hlen;
    *m_off += hlen;
    *cp++ = (UCHAR)(deltaA >> 8);
    *cp++ = (UCHAR)(deltaA);

    NdisMoveMemory((UCHAR UNALIGNED *)cp,
                   (UCHAR UNALIGNED *)new_seq,
                   deltaS);

    INCR(OutCompressed);
    return (TYPE_COMPRESSED_TCP);

uncompressed:
    /*
     * Update connection state cs & send uncompressed packet
     * ('uncompressed' means a regular ip/tcp packet but with the
     * 'conversation id' we hope to use on future compressed packets in
     * the protocol field).
     */

    NdisMoveMemory((UCHAR UNALIGNED *)csip,
                   (UCHAR UNALIGNED *)ip,
                   hlen);

    ip->ip_p = cs->cs_id;
    comp->last_xmit = cs->cs_id;
    return (TYPE_UNCOMPRESSED_TCP);
}





//   A.3  Decompression
//
//   This routine decompresses a received packet.  It is called with a
//   pointer to the packet, the packet length and type, and a pointer to the
//   compression state structure for the incoming serial line.  It returns a
//   pointer to the resulting packet or zero if there were errors in the
//   incoming packet.  If the packet is COMPRESSED_TCP or UNCOMPRESSED_TCP,
//   the compression state will be updated.
//
//   The new packet will be constructed in-place.  That means that there must
//   be 128 bytes of free space in front of bufp to allow room for the
//   reconstructed IP and TCP headers.  The reconstructed packet will be
//   aligned on a 32-bit boundary.
//

//LONG
//sl_uncompress_tcp(
//    PUUCHAR UNALIGNED  *bufp,
//    LONG len,
//    UCHAR type,
//    struct slcompress  *comp) {
LONG
sl_uncompress_tcp(
    PUUCHAR UNALIGNED *InBuffer,
    PLONG   InLength,
    UCHAR   UNALIGNED *OutBuffer,
    PLONG   OutLength,
    UCHAR   type,
    struct slcompress *comp
    )
{
    UCHAR UNALIGNED *cp;
    LONG inlen;
    LONG hlen, changes;
    struct tcphdr UNALIGNED *th;
    struct cstate *cs;
    struct ip_v4 UNALIGNED *ip;

    inlen = *InLength;

    switch (type) {

    case TYPE_ERROR:
    default:
        NdisWanDbgOut(DBG_FAILURE, DBG_VJ, ("Packet transmission error type 0x%.2x",type));
         goto bad;

    case TYPE_IP:
         break;

    case TYPE_UNCOMPRESSED_TCP:
         /*
          * Locate the saved state for this connection.  If the state
          * index is legal, clear the 'discard' flag.
          */
         ip = (struct ip_v4 UNALIGNED *) *InBuffer;
         if (ip->ip_p >= comp->MaxStates) {
            NdisWanDbgOut(DBG_FAILURE, DBG_VJ, ("Max state exceeded %u", ip->ip_p));
            goto bad;
         }

         cs = &comp->rstate[comp->last_recv = ip->ip_p];
         comp->flags &= ~SLF_TOSS;

         /*
          * Restore the IP protocol field then save a copy of this
          * packet header.  (The checksum is zeroed in the copy so we
          * don't have to zero it each time we process a compressed
          * packet.
          */
         hlen = ip->ip_hl & 0x0F;
         hlen += ((struct tcphdr UNALIGNED *) & ((ULONG UNALIGNED *) ip)[hlen])->th_off >> 4;
         hlen <<= 2;

         if (hlen > inlen) {
             NdisWanDbgOut(DBG_FAILURE, DBG_VJ, ("recv'd runt uncompressed packet %d %d", hlen, inlen));
             goto bad;
         }

         NdisMoveMemory((PUCHAR)&cs->cs_ip,
                        (PUCHAR)ip,
                        hlen);

         cs->cs_ip.ip_p = IPPROTO_TCP;

         NdisMoveMemory((PUCHAR)OutBuffer,
                        (PUCHAR)&cs->cs_ip,
                        hlen);

         cs->cs_ip.ip_sum = 0;
         cs->cs_hlen = (USHORT)hlen;

         *InBuffer = (PUCHAR)ip + hlen;
         *InLength = inlen - hlen;
         *OutLength = hlen;

         INCR(InUncompressed);
         return (inlen);

    case TYPE_COMPRESSED_TCP:
         break;
    }

    /* We've got a compressed packet. */
    INCR(InCompressed);
    cp = *InBuffer;
    changes = *cp++;

    if (changes & NEW_C) {
         /*
          * Make sure the state index is in range, then grab the
          * state. If we have a good state index, clear the 'discard'
          * flag.
          */
         if (*cp >= comp->MaxStates) {
            NdisWanDbgOut(DBG_FAILURE, DBG_VJ, ("MaxState of %u too big", *cp));                
            goto bad;
         }

         comp->flags &= ~SLF_TOSS;
         comp->last_recv = *cp++;
    } else {
         /*
          * This packet has an implicit state index.  If we've had a
          * line error since the last time we got an explicit state
          * index, we have to toss the packet.
          */
         if (comp->flags & SLF_TOSS) {
            NdisWanDbgOut(DBG_FAILURE, DBG_VJ,("Packet has state index, have to toss it"));
            INCR(InTossed);
            return (0);
        }
    }

    /*
     * Find the state then fill in the TCP checksum and PUSH bit.
     */

    cs = &comp->rstate[comp->last_recv];

    //
    // If there was a line error and we did not get notified we could
    // miss a TYPE_UNCOMPRESSED_TCP which would leave us with an
    // un-init'd cs!
    //
    if (cs->cs_hlen == 0) {
        NdisWanDbgOut(DBG_FAILURE, DBG_VJ,("Un-Init'd state!"));
        goto bad;
    }

    hlen = (cs->cs_ip.ip_hl & 0x0F) << 2;
    th = (struct tcphdr UNALIGNED  *) & ((UCHAR UNALIGNED  *) &cs->cs_ip)[hlen];

    th->th_sumhi = cp[0];
    th->th_sumlo = cp[1];

    cp += 2;
    if (changes & TCP_PUSH_BIT)
         th->th_flags |= TH_PUSH;
    else
         th->th_flags &= ~TH_PUSH;

    /*
     * Fix up the state's ack, seq, urg and win fields based on the
     * changemask.
     */
    switch (changes & SPECIALS_MASK) {
    case SPECIAL_I:
         {
            UCHAR UNALIGNED *   piplen=(UCHAR UNALIGNED *)&(cs->cs_ip.ip_len);
            UCHAR UNALIGNED *   ptcplen;
            ULONG   tcplen;
            ULONG   i;

            i = ((piplen[0] << 8) + piplen[1]) - cs->cs_hlen;

//          th->th_ack = htonl(ntohl(th->th_ack) + i);

            ptcplen=(UCHAR UNALIGNED *)&(th->th_ack);
            tcplen=(ptcplen[0] << 24) + (ptcplen[1] << 16) +
                    (ptcplen[2] << 8) + ptcplen[3] + i;
            ptcplen[3]=(UCHAR)(tcplen);
            ptcplen[2]=(UCHAR)(tcplen >> 8);
            ptcplen[1]=(UCHAR)(tcplen >> 16);
            ptcplen[0]=(UCHAR)(tcplen >> 24);


//          th->th_seq = htonl(ntohl(th->th_seq) + i);

            ptcplen=(UCHAR UNALIGNED *)&(th->th_seq);
            tcplen=(ptcplen[0] << 24) + (ptcplen[1] << 16) +
                    (ptcplen[2] << 8) + ptcplen[3] + i;
            ptcplen[3]=(UCHAR)(tcplen);
            ptcplen[2]=(UCHAR)(tcplen >> 8);
            ptcplen[1]=(UCHAR)(tcplen >> 16);
            ptcplen[0]=(UCHAR)(tcplen >> 24);

         }
         break;

    case SPECIAL_D:
         {
//          th->th_seq = htonl(ntohl(th->th_seq) + ntohs(cs->cs_ip.ip_len)
//                      - cs->cs_hlen);

            UCHAR   UNALIGNED *piplen=(UCHAR UNALIGNED *)&(cs->cs_ip.ip_len);
            UCHAR   UNALIGNED *ptcplen;
            ULONG   tcplen;
            ULONG   i;

            i = ((piplen[0] << 8) + piplen[1]) - cs->cs_hlen;

            ptcplen=(UCHAR UNALIGNED *)&(th->th_seq);
            tcplen=(ptcplen[0] << 24) + (ptcplen[1] << 16) +
                    (ptcplen[2] << 8) + ptcplen[3] + i;

            ptcplen[3]=(UCHAR)(tcplen);
            ptcplen[2]=(UCHAR)(tcplen >> 8);
            ptcplen[1]=(UCHAR)(tcplen >> 16);
            ptcplen[0]=(UCHAR)(tcplen >> 24);


         }

         break;

    default:
         if (changes & NEW_U) {
              th->th_flags |= TH_URG;
              DECODEU(th->th_urp)
         } else
              th->th_flags &= ~TH_URG;

         if (changes & NEW_W)
            DECODES(th->th_win);
         if (changes & NEW_A)
            DECODEL(th->th_ack)
         if (changes & NEW_S)
            DECODEL(th->th_seq)

         break;
    }
    /* Update the IP ID */
    if (changes & NEW_I) {
    
         DECODES(cs->cs_ip.ip_id)

    } else {

        USHORT id;
        UCHAR UNALIGNED *pid = (UCHAR UNALIGNED *)&(cs->cs_ip.ip_id);

//        cs->cs_ip.ip_id = htons(ntohs(cs->cs_ip.ip_id) + 1);
        id=(pid[0] << 8) + pid[1] + 1;
        pid[0]=(UCHAR)(id >> 8);
        pid[1]=(UCHAR)(id);
    }


    /*
     * At this point, cp points to the first byte of data in the packet.
     * If we're not aligned on a 4-byte boundary, copy the data down so
     * the IP & TCP headers will be aligned.  Then back up cp by the
     * TCP/IP header length to make room for the reconstructed header (we
     * assume the packet we were handed has enough space to prepend 128
     * bytes of header).  Adjust the lenth to account for the new header
     * & fill in the IP total length.
     */
//    len -= (cp - *bufp);
    inlen -= (ULONG)(cp - *InBuffer);

    if (inlen < 0) {
    
         /*
          * we must have dropped some characters (crc should detect
          * this but the old slip framing won't)
          */
        NdisWanDbgOut(DBG_FAILURE, DBG_VJ,("len has dropped below 0!"));
         goto bad;
    }
//
//  Who Cares about 4 byte alignement!  It's just a useless big copy!
//
//    if ((ULONG) cp & 3) {
//         if (len > 0)
//          //
//          // BUG BUG we want OVBCOPY..
//          //
//            NdisMoveMemory(
//              (PUCHAR)((ULONG) cp & ~3),
//              cp,
//              len);
//         cp = (PUCHAR) ((ULONG) cp & ~3);
//    }

//    cp -= cs->cs_hlen;
//    len += cs->cs_hlen;

//    cs->cs_ip.ip_len = htons(len);
    cs->cs_ip.ip_len = htons(inlen + cs->cs_hlen);

//  NdisMoveMemory(
//      (PUCHAR)cp,
//      (PUCHAR)&cs->cs_ip,
//      cs->cs_hlen);

  NdisMoveMemory((PUCHAR)OutBuffer,
                 (PUCHAR)&cs->cs_ip,
                 cs->cs_hlen);

//  *bufp = cp;
    *InBuffer = cp;
    *InLength = inlen;
    *OutLength = cs->cs_hlen;

    /* recompute the ip header checksum */
    {
//         USHORT UNALIGNED * bp = (USHORT UNALIGNED *) cp;
         USHORT UNALIGNED * bp = (USHORT UNALIGNED *) OutBuffer;

         for (changes = 0; hlen > 0; hlen -= 2)
              changes += *bp++;

         changes = (changes & 0xffff) + (changes >> 16);
         changes = (changes & 0xffff) + (changes >> 16);
//         ((struct ip_v4 UNALIGNED *) cp)->ip_sum = (USHORT)~changes;
         ((struct ip_v4 UNALIGNED *) OutBuffer)->ip_sum = (USHORT)~changes;
    }

    return (inlen + cs->cs_hlen);

bad:
    comp->flags |= SLF_TOSS;
    INCR(InErrors);
    return (0);
}




//   A.4  Initialization
//
//   This routine initializes the state structure for both the transmit and
//   receive halves of some serial line.  It must be called each time the
//   line is brought up.
//

VOID
WanInitVJ(
    VOID
)
{
#if 0
    NdisInitializeNPagedLookasideList(&VJCtxList,
                                      NULL,
                                      NULL,
                                      0,
                                      sizeof(slcompress),
                                      VJCTX_TAG,
                                      0);
#endif
}

VOID
WanDeleteVJ(
    VOID
    )
{
#if 0
    NdisDeleteNPagedLookasideList(&VJCtxList);
#endif
}

NDIS_STATUS
sl_compress_init(
    struct slcompress **retcomp,
    UCHAR  MaxStates
    )
{
    ULONG i;
    struct cstate *tstate; // = comp->tstate;
    struct slcompress *comp;

    comp = *retcomp;

    //
    // Do we need to allocate memory for this bundle
    //

    if (comp == NULL) {

        NdisWanAllocateMemory(&comp, sizeof(slcompress), VJCOMPRESS_TAG);

        //
        // If there was no memory to allocate
        //
        if (comp == NULL) {
    
            return(NDIS_STATUS_RESOURCES);
        }
    }

    tstate = comp->tstate;

    /*
     * Clean out any junk left from the last time line was used.
     */
    NdisZeroMemory(
        (PUCHAR) comp,
        sizeof(*comp));

    /*
     * Link the transmit states into a circular list.
     */
    for (i = MaxStates - 1; i > 0; --i) {
        tstate[i].cs_id = (UCHAR)i;
        tstate[i].cs_next = &tstate[i - 1];
    }

    tstate[0].cs_next = &tstate[MaxStates - 1];
    tstate[0].cs_id = 0;
    comp->last_cs = &tstate[0];

    /*
     * Make sure we don't accidentally do CID compression
     * (assumes MAX_VJ_STATES < 255).
     */
    comp->last_recv = 255;
    comp->last_xmit = 255;
    comp->flags = SLF_TOSS;
    comp->MaxStates=MaxStates;

    *retcomp = comp;

    return (NDIS_STATUS_SUCCESS);
}

VOID
sl_compress_terminate(
    struct slcompress **comp
    )
{
    if (*comp != NULL) {
        NdisWanFreeMemory(*comp);
        *comp = NULL;
    }
}

//   A.5  Berkeley Unix dependencies
//
//   Note:  The following is of interest only if you are trying to bring the
//   sample code up on a system that is not derived from 4BSD (Berkeley
//   Unix).
//
//   The code uses the normal Berkeley Unix header files (from
//   /usr/include/netinet) for definitions of the structure of IP and TCP
//   headers.  The structure tags tend to follow the protocol RFCs closely
//   and should be obvious even if you do not have access to a 4BSD
//   system./48/
//
//   ----------------------------
//    48. In the event they are not obvious, the header files (and all the
//   Berkeley networking code) can be anonymous ftp'd from host
//
//
//   The macro BCOPY(src, dst, amt) is invoked to copy amt bytes from src to
//   dst.  In BSD, it translates into a call to BCOPY.  If you have the
//   misfortune to be running System-V Unix, it can be translated into a call
//   to memcpy.  The macro OVBCOPY(src, dst, amt) is used to copy when src
//   and dst overlap (i.e., when doing the 4-byte alignment copy).  In the
//   BSD kernel, it translates into a call to ovbcopy.  Since AT&T botched
//   the definition of memcpy, this should probably translate into a copy
//   loop under System-V.
//
//   The macro BCMP(src, dst, amt) is invoked to compare amt bytes of src and
//   dst for equality.  In BSD, it translates into a call to bcmp.  In
//   System-V, it can be translated into a call to memcmp or you can write a
//   routine to do the compare.  The routine should return zero if all bytes
//   of src and dst are equal and non-zero otherwise.
//
//   The routine ntohl(dat) converts (4 byte) long dat from network byte
//   order to host byte order.  On a reasonable cpu this can be the no-op
//   macro:
//                           #define ntohl(dat) (dat)
//
//   On a Vax or IBM PC (or anything with Intel byte order), you will have to
//   define a macro or routine to rearrange bytes.
//
//   The routine ntohs(dat) is like ntohl but converts (2 byte) shorts
//   instead of longs.  The routines htonl(dat) and htons(dat) do the inverse
//   transform (host to network byte order) for longs and shorts.
//
//   A struct mbuf is used in the call to sl_compress_tcp because that
//   routine needs to modify both the start address and length if the
//   incoming packet is compressed.  In BSD, an mbuf is the kernel's buffer
//   management structure.  If other systems, the following definition should
//   be sufficient:
//
//            struct mbuf {
//                    UCHAR  *m_off; /* pointer to start of data */
//                    int     m_len;  /* length of data */
//            };
//
//            #define mtod(m, t) ((t)(m->m_off))
//
//
//   B  Compatibility with past mistakes
//
//
//   When combined with the modern PPP serial line protocol[9], the use of
//   header compression is automatic and invisible to the user.
//   Unfortunately, many sites have existing users of the SLIP described in
//   [12] which doesn't allow for different protocol types to distinguish
//   header compressed packets from IP packets or for version numbers or an
//   option exchange that could be used to automatically negotiate header
//   compression.
//
//   The author has used the following tricks to allow header compressed SLIP
//   to interoperate with the existing servers and clients.  Note that these
//   are hacks for compatibility with past mistakes and should be offensive
//   to any right thinking person.  They are offered solely to ease the pain
//   of running SLIP while users wait patiently for vendors to release PPP.
//
//
//   B.1  Living without a framing `type' byte
//
//   The bizarre packet type numbers in sec. A.1 were chosen to allow a
//   `packet type' to be sent on lines where it is undesirable or impossible
//   to add an explicit type byte.  Note that the first byte of an IP packet
//   always contains `4' (the IP protocol version) in the top four bits.  And
//   that the most significant bit of the first byte of the compressed header
//   is ignored.  Using the packet types in sec. A.1, the type can be encoded
//   in the most significant bits of the outgoing packet using the code
//
//                    p->dat[0] |= sl_compress_tcp(p, comp);
//
//    and decoded on the receive side by
//
//                  if (p->dat[0] & 0x80)
//                          type = TYPE_COMPRESSED_TCP;
//                  else if (p->dat[0] >= 0x70) {
//                          type = TYPE_UNCOMPRESSED_TCP;
//                          p->dat[0] &=~ 0x30;
//                  } else
//                          type = TYPE_IP;
//                  status = sl_uncompress_tcp(p, type, comp);