static char rcsid[] = "$Header: in_cksum.c,v 820.1 86/12/04 19:53:26 root Exp $";
static char sccsid[]="%W% %Y% %Q% %G%";

/************************************************************************
 *									*
 *				Copyright 1984, 1985			*
 *			VALID LOGIC SYSTEMS INCORPORATED		*
 *									*
 *	This listing contains confidential proprietary information	*
 *	which is not to be disclosed to unauthorized persons without	*
 *	written consent of an officer of Valid Logic Systems 		*
 *	Incoroporated.							*
 *									*
 *	The copyright notice appearing above is included to provide	*
 *	statutory protection in the event of unauthorized or 		*
 *	unintentional public disclosure.				*
 *									*
 ************************************************************************/

/*	in_cksum.c	1.18	85/08/07	*/

#ifdef s32
#include "../h/types.h"
#include "../h/mbuf.h"

u_short fastEvenChksum ();
u_short fastOddChksum ();

/*
 * Checksum routine for Internet Protocol family (s32/68010 version).
 *
 * The length to sum must be less than 64k.
 * These are reasonable assumptions since the packet sizes in the Internet
 * protocols agree with them.
 *
 * The running checksum maintained in the long "sum" cell is only 16bits.
 * The upper 16bits is used for maintaining the "carry" from the 16bit
 * additions.  Since the length of the byte stream to checksum is
 * assumed to be less than 65kbytes (32kwords) carry into the 31st (sign)
 * bit is not a problem.  Thus, the 16bit add carry can be ignored until
 * all sums are done; then it is added into the sum.
 */

u_short
in_cksum(m, len)
    register struct mbuf *m;
    register int len;
{
    register u_char *bptr;	/* Pointer into mbuf data area */
    register u_long sum = 0;	/* Running checksum */
    register int mlen = 0;	/* Number of bytes in current mbuf */

    while(len)
	{

    /* Find next non-empty mbuf */

	while(1)
	    {
	    if(m==0)
		{
		printf("cksum: out of data\n");
		goto done;
		}
	    if(m->m_len)
		break;
	    m = m->m_next;
	    }

    /* Point into data part of mbuf */
	
	bptr = mtod(m, u_char *);

    /* Add in low half of word split over mbuf boundaries */

	if(mlen == -1)
	    {
	    sum += *bptr++;
	    mlen = m->m_len - 1;
	    len--;
	    }
	else
	    mlen = m->m_len;
    
    /* Step to next buffer in chain (for next iteration) */

	m = m->m_next;

    /*
     * Calculate amount of current mbuf to include in ckecksum.
     * Adjust total length left.
     */

	if(len < mlen)
	    mlen = len;
	len -= mlen;

    /* 
     * First add whole word (16bit) data from mbuf into sum.
     * Then add first half of word split over mbuf boundaries
     *  (may also happen due to an odd number of bytes to sum).
     */

	if((u_long)bptr & 0x01)
	    sum += fastOddChksum(bptr, mlen&~0x1);
	else
	    sum += fastEvenChksum(bptr, mlen&~0x1);
	if(mlen & 0x01)
	    {
	    sum += *(bptr + mlen - 1) << 8;
	    mlen = -1;
	    }
	}

/*
 * Add carry from previous additions into total sum.
 * If this addition carries, adjust it again.
 * Return then total sum.
 */

done:
    sum = (sum >> 16) + (sum & 0xFFFF);
    if(sum & 0x10000)
	sum -= 0xFFFF;
    return(~sum);
}

/* Checksum a stream of 16bit values started at an even address */

u_short
fastEvenChksum(m, len)
    register u_short *m;	/* a5, pointer to byte stream */
    register u_short len;	/* d7, number of bytes to checksum */
{
    register u_long sum = 0;	/* d6, running checksum */
    register u_short llen;	/* d5, number of longwords */

    llen = len >> 2;		/* Convert to number of longwords */

asm("	clrl	d1		");	/* Create a zero constant; clr (X) */
asm("	jra	ecksum_tstloop	");	/* Goto end of loop to test limits */

asm("ecksum_loop:		");
asm("	addl	a5@+,d6 	");	/* Add next word, 4 bytes */
asm("	addxl	d1,d6 		");	/* Add in carry(X) */
asm("ecksum_tstloop:		");
asm("	dbf	d5,ecksum_loop	");

asm("	movl	d6,d0		");	/* Add two halves of 32bit value */
asm("	swap	d0		");	/*  into a single 16bit value */
asm("	addw	d0,d6		");

asm("	btst	#1,d7		");	/* Check for a final 16 bit word */
asm("	beq	ecksum_done	");
asm("	movw	a5@+,d0		");	/* Yes, add it in also */
asm("	addxw	d0,d6		");

asm("ecksum_done:");
asm("	addxw	d1,d6		");	/* Add in last carry */

    return(sum);
}

/* Checksum a stream of 16bit values started at an odd address */

u_short
fastOddChksum(m, len)
    register u_short *m;	/* a5, pointer to byte stream */
    register u_short len;	/* d7, number of bytes to checksum */
{
    register u_long sum = 0;	/* d6, running checksum */

    len >>= 1;			/* Convert to number of 16bit words */

asm("	clrl	d1		");	/* Create a zero constant */
asm("	jra	ocksum_tstloop	");	/* Goto end of loop to test limits */

asm("ocksum_loop:		");
asm("	clrl	d0		");	/* Clear cell for 16bit staging */
asm("	movb	a5@+,d0		");	/* Get high byte */
asm("	lslw	#8,d0		");	/* Put into upper 8 bits of 16 */
asm("	movb	a5@+,d0		");	/* Get low byte */
asm("	addl	d0,d6	 	");	/* Add 16bits (carry into high 16) */
asm("ocksum_tstloop:		");
asm("	dbf	d7,ocksum_loop	");

asm("	movl	d6,d0		");	/* Add two halves of 32bit value */
asm("	swap	d0		");	/*  into a single 16bit value */
asm("	addw	d0,d6		");

asm("	addxw	d1,d6		");	/* Add in last carry */

    return(sum);
}

#else s32
#include "../h/types.h"
#include "../h/mbuf.h"
#include "../netinet/in.h"
#include "../netinet/in_systm.h"

/*
 * Checksum routine for Internet Protocol family headers (VAX Version).
 *
 * This routine is very heavily used in the network
 * code and should be modified for each CPU to be as fast as possible.
 */

in_cksum(m, len)
	register struct mbuf *m;
	register int len;
{
	register u_short *w;		/* on vax, known to be r9 */
	register int sum = 0;		/* on vax, known to be r8 */
	register int mlen = 0;

	for (;;) {
		/*
		 * Each trip around loop adds in
		 * word from one mbuf segment.
		 */
		w = mtod(m, u_short *);
		if (mlen == -1) {
			/*
			 * There is a byte left from the last segment;
			 * add it into the checksum.  Don't have to worry
			 * about a carry-out here because we make sure
			 * that high part of (32 bit) sum is small below.
			 */

			sum += *(u_char *)w << 8;
			w = (u_short *)((char *)w + 1);
			mlen = m->m_len - 1;
			len--;
		} else
			mlen = m->m_len;
		m = m->m_next;
		if (len < mlen)
			mlen = len;
		len -= mlen;

		/*
		 * Force to long boundary so we do longword aligned
		 * memory operations.  It is too hard to do byte
		 * adjustment, do only word adjustment.
		 */
		if (((int)w&0x2) && mlen >= 2) {
			sum += *w++;
			mlen -= 2;
		}
		/*
		 * Do as much of the checksum as possible 32 bits at at time.
		 * In fact, this loop is unrolled to make overhead from
		 * branches &c small.
		 *
		 * We can do a 16 bit ones complement sum 32 bits at a time
		 * because the 32 bit register is acting as two 16 bit
		 * registers for adding, with carries from the low added
		 * into the high (by normal carry-chaining) and carries
		 * from the high carried into the low on the next word
		 * by use of the adwc instruction.  This lets us run
		 * this loop at almost memory speed.
		 *
		 * Here there is the danger of high order carry out, and
		 * we carefully use adwc.
		 */
		while ((mlen -= 32) >= 0) {
#undef ADD
			asm("clrl r0");		/* clears carry */
#define ADD		asm("adwc (r9)+,r8;");
			ADD; ADD; ADD; ADD; ADD; ADD; ADD; ADD;
			asm("adwc $0,r8");
		}
		mlen += 32;
		while ((mlen -= 8) >= 0) {
			asm("clrl r0");
			ADD; ADD;
			asm("adwc $0,r8");
		}
		mlen += 8;
		/*
		 * Now eliminate the possibility of carry-out's by
		 * folding back to a 16 bit number (adding high and
		 * low parts together.)  Then mop up trailing words
		 * and maybe an odd byte.
		 */
		{ asm("ashl $-16,r8,r0; addw2 r0,r8");
		  asm("adwc $0,r8; movzwl r8,r8"); }
		while ((mlen -= 2) >= 0) {
			asm("movzwl (r9)+,r0; addl2 r0,r8");
		}
		if (mlen == -1) {
			sum += *(u_char *)w;
		}

		if (len == 0)
			break;
		/*
		 * Locate the next block with some data.
		 * If there is a word split across a boundary we
		 * will wrap to the top with mlen == -1 and
		 * then add it in shifted appropriately.
		 */
		for (;;) {
			if (m == 0) {
				printf("cksum: out of data\n");
				goto done;
			}
			if (m->m_len)
				break;
			m = m->m_next;
		}
	}
done:
	/*
	 * Add together high and low parts of sum
	 * and carry to get cksum.
	 * Have to be careful to not drop the last
	 * carry here.
	 */
	{ asm("ashl $-16,r8,r0; addw2 r0,r8; adwc $0,r8");
	  asm("mcoml r8,r8; movzwl r8,r8"); }
	return (sum);
}
#endif s32
