#include <u.h>
#include <libc.h>
#include <ip.h>
#include <auth.h>
#include "ppp.h"

static  ushort  endian  = 1;
static  uchar*  aendian = (uchar*)&endian;
#define LITTLE  *aendian

ushort
ptclbsum(uchar *addr, int len)
{
       ulong losum, hisum, mdsum, x;
       ulong t1, t2;

       losum = 0;
       hisum = 0;
       mdsum = 0;

       x = 0;
       if((uintptr)addr & 1) {
               if(len) {
                       hisum += addr[0];
                       len--;
                       addr++;
               }
               x = 1;
       }
       while(len >= 16) {
               t1 = *(ushort*)(addr+0);
               t2 = *(ushort*)(addr+2);        mdsum += t1;
               t1 = *(ushort*)(addr+4);        mdsum += t2;
               t2 = *(ushort*)(addr+6);        mdsum += t1;
               t1 = *(ushort*)(addr+8);        mdsum += t2;
               t2 = *(ushort*)(addr+10);       mdsum += t1;
               t1 = *(ushort*)(addr+12);       mdsum += t2;
               t2 = *(ushort*)(addr+14);       mdsum += t1;
               mdsum += t2;
               len -= 16;
               addr += 16;
       }
       while(len >= 2) {
               mdsum += *(ushort*)addr;
               len -= 2;
               addr += 2;
       }
       if(x) {
               if(len)
                       losum += addr[0];
               if(LITTLE)
                       losum += mdsum;
               else
                       hisum += mdsum;
       } else {
               if(len)
                       hisum += addr[0];
               if(LITTLE)
                       hisum += mdsum;
               else
                       losum += mdsum;
       }

       losum += hisum >> 8;
       losum += (hisum & 0xff) << 8;
       while(hisum = losum>>16)
               losum = hisum + (losum & 0xffff);

       return losum & 0xffff;
}

ushort
ptclcsum(Block *bp, int offset, int len)
{
       uchar *addr;
       ulong losum, hisum;
       ushort csum;
       int odd, blen, x;

       /* Correct to front of data area */
       while(bp != nil && offset && offset >= BLEN(bp)) {
               offset -= BLEN(bp);
               bp = bp->next;
       }
       if(bp == nil)
               return 0;

       addr = bp->rptr + offset;
       blen = BLEN(bp) - offset;

       if(bp->next == nil) {
               if(blen < len)
                       len = blen;
               return ~ptclbsum(addr, len) & 0xffff;
       }

       losum = 0;
       hisum = 0;

       odd = 0;
       while(len) {
               x = blen;
               if(len < x)
                       x = len;

               csum = ptclbsum(addr, x);
               if(odd)
                       hisum += csum;
               else
                       losum += csum;
               odd = (odd+x) & 1;
               len -= x;

               bp = bp->next;
               if(bp == nil)
                       break;
               blen = BLEN(bp);
               addr = bp->rptr;
       }

       losum += hisum>>8;
       losum += (hisum&0xff)<<8;
       while((csum = losum>>16) != 0)
               losum = csum + (losum & 0xffff);

       return ~losum & 0xffff;
}

ushort
ipcsum(uchar *addr)
{
       int len;
       ulong sum;

       sum = 0;
       len = (addr[0]&0xf)<<2;

       while(len > 0) {
               sum += (addr[0]<<8) | addr[1] ;
               len -= 2;
               addr += 2;
       }

       sum = (sum & 0xffff) + (sum >> 16);
       sum = (sum & 0xffff) + (sum >> 16);

       return (sum^0xffff);
}