/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright (c) 1992,1997 by Sun Microsystems, Inc.
 * All rights reserved.
 */
/* Copyright (c) 1990 Mentat Inc. */


#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <sys/types.h>
#include <sys/stream.h>
#include <sys/ddi.h>
#include <sys/isa_defs.h>
#include <inet/common.h>

#define	FOLD_SUM(sum) \
{ sum = (sum >> 16) + (sum & 0xFFFF); sum = (sum >> 16) + (sum & 0xFFFF); }
#define	U16AM(p, i, m)	((((uint16_t *)(p))[i]) & (uint32_t)(m))

/*
 * For maximum efficiency, these access macros should be redone for
 * machines that can access unaligned data.  NOTE: these assume
 * ability to fetch from a zero extended 'uint8_t' and 'uint16_t'.  Add explicit
 * masks in the U8_FETCH, U16_FETCH, PREV_TWO and NEXT_TWO as needed.
 */

#ifdef	_LITTLE_ENDIAN
#define	U8_FETCH_FIRST(p)	((p)[0])
#define	U8_FETCH_SECOND(p)	(((uint32_t)(p)[0]) << 8)
#define	PREV_ONE(p)		U16AM(p, -1, 0xFF00)
#define	NEXT_ONE(p)		U16AM(p, 0, 0xFF)
#else
#define	U8_FETCH_FIRST(p)	((uint32_t)((p)[0]) << 8)
#define	U8_FETCH_SECOND(p)	((p)[0])
#define	PREV_ONE(p)		U16AM(p, -1, 0xFF)
#define	NEXT_ONE(p)		U16AM(p, 0, 0xFF00)
#endif

#define	U16_FETCH(p)		U8_FETCH_FIRST(p) + U8_FETCH_SECOND(p+1)
#define	PREV_TWO(p)		((uint32_t)(((uint16_t *)(p))[-1]))
#define	NEXT_TWO(p)		((uint32_t)(((uint16_t *)(p))[0]))

/*
 * Return the ones complement checksum from the mblk chain at mp,
 * after skipping offset bytes, and adding in the supplied partial
 * sum.  Note that a final complement of the return value is needed
 * if no further contributions to the checksum are forthcoming.
 */
uint16_t
ip_csum(mp, offset, sum)
	mblk_t *mp;
	int	offset;
	uint32_t	sum;
{
	uint8_t	*startp = mp->b_rptr + offset;
	uint8_t	*endp = mp->b_wptr;
/* >= 0x2 means flipped for memory align, 0x1 means last count was odd */
	int	odd_total = 0;

#ifdef	TEST_COVERAGE
	mblk_t *safe_mp;
#define	INIT_COVERAGE()	(safe_mp = mp, safe_mp->b_next = NULL)
#define	MARK_COVERAGE(flag) (safe_mp->b_next = \
	(mblk_t *)((uint32_t)safe_mp->b_next | flag))
#else
#define	INIT_COVERAGE()	/* */
#define	MARK_COVERAGE(flag)	/* */
#endif

	for (;;) {
		INIT_COVERAGE();
		if ((endp - startp) < 10) {
			MARK_COVERAGE(0x1);
			while ((endp - startp) >= 2) {
				MARK_COVERAGE(0x2);
				sum += U16_FETCH(startp);
				startp += 2;
			}
			if ((endp - startp) >= 1) {
				MARK_COVERAGE(0x4);
				odd_total = 1;
				sum += U8_FETCH_FIRST(startp);
			}
			MARK_COVERAGE(0x8);
			FOLD_SUM(sum);
			goto next_frag;
		}
		if ((uint32_t)startp & 0x1) {
			MARK_COVERAGE(0x10);
			odd_total = 3;
			startp++;
			sum = (sum << 8) + PREV_ONE(startp);
		}
		if ((uint32_t)startp & 0x2) {
			MARK_COVERAGE(0x20);
			startp += 2;
			sum += PREV_TWO(startp);
		}
		if ((uint32_t)endp & 0x1) {
			MARK_COVERAGE(0x40);
			odd_total ^= 0x1;
			endp--;
			sum += NEXT_ONE(endp);
		}
		if ((uint32_t)endp & 0x2) {
			MARK_COVERAGE(0x80);
			endp -= 2;
			sum += NEXT_TWO(endp);
		}

		{
#ifdef	NOT_ALL_PTRS_EQUAL
#define	INC_PTR(cnt)	ptr += cnt
#define	INC_ENDPTR(cnt)	endptr += cnt
			uint32_t	*ptr = (uint32_t *)startp;
			uint32_t	*endptr = (uint32_t *)endp;
#else
#define	INC_PTR(cnt)	startp += (cnt * sizeof (uint32_t))
#define	INC_ENDPTR(cnt)	endp += (cnt * sizeof (uint32_t))
#define	ptr		((uint32_t *)startp)
#define	endptr		((uint32_t *)endp)
#endif


#ifdef	USE_FETCH_AND_SHIFT
			uint32_t	u1, u2;
			uint32_t	mask = 0xFFFF;
#define	LOAD1(i)	u1 = ptr[i]
#define	LOAD2(i)	u2 = ptr[i]
#define	SUM1(i)		sum += (u1 & mask) + (u1 >> 16)
#define	SUM2(i)		sum += (u2 & mask) + (u2 >> 16)
#endif

#ifdef	USE_FETCH_AND_ADDC
			uint32_t	u1, u2;
#define	LOAD1(i)	u1 = ptr[i]
#define	LOAD2(i)	u2 = ptr[i]
#define	SUM1(i)		sum += u1
#define	SUM2(i)		sum += u2
#endif

#ifdef	USE_ADDC
#define	SUM1(i)		sum += ptr[i]
#endif

#ifdef	USE_POSTINC
#define	SUM1(i)		sum += *((uint16_t *)ptr)++; sum += *((uint16_t *)ptr)++
#undef	INC_PTR
#define	INC_PTR(i)	/* */
#endif

#ifndef	LOAD1
#define	LOAD1(i)	/* */
#endif

#ifndef	LOAD2
#define	LOAD2(i)	/* */
#endif

#ifndef	SUM2
#define	SUM2(i)		SUM1(i)
#endif

/* USE_INDEXING is the default */
#ifndef	SUM1
#define	SUM1(i)
	sum += ((uint16_t *)ptr)[i * 2]; sum += ((uint16_t *)ptr)[(i * 2) + 1]
#endif

		LOAD1(0);
		INC_ENDPTR(-8);
		if (ptr <= endptr) {
			MARK_COVERAGE(0x100);
			do {
				LOAD2(1); SUM1(0);
				LOAD1(2); SUM2(1);
				LOAD2(3); SUM1(2);
				LOAD1(4); SUM2(3);
				LOAD2(5); SUM1(4);
				LOAD1(6); SUM2(5);
				LOAD2(7); SUM1(6);
				LOAD1(8); SUM2(7);
				INC_PTR(8);
			} while (ptr <= endptr);
		}
#ifdef USE_TAIL_SWITCH
		switch ((endptr + 8) - ptr) {
		case 7:	LOAD2(6); SUM2(6);
		case 6:	LOAD2(5); SUM2(5);
		case 5:	LOAD2(4); SUM2(4);
		case 4:	LOAD2(3); SUM2(3);
		case 3:	LOAD2(2); SUM2(2);
		case 2:	LOAD2(1); SUM2(1);
		case 1:	SUM1(0);
		case 0:	break;
		}
#else
		INC_ENDPTR(4);
		if (ptr <= endptr) {
			MARK_COVERAGE(0x200);
			LOAD2(1); SUM1(0);
			LOAD1(2); SUM2(1);
			LOAD2(3); SUM1(2);
			LOAD1(4); SUM2(3);
			INC_PTR(4);
		}
		INC_ENDPTR(4);
		if (ptr < endptr) {
			MARK_COVERAGE(0x400);
			do {
				SUM1(0); LOAD1(1);
				INC_PTR(1);
			} while (ptr < endptr);
		}
#endif
		}

		FOLD_SUM(sum);
		if (odd_total > 1) {
			MARK_COVERAGE(0x800);
			sum = ((sum << 8) | (sum >> 8)) & 0xFFFF;
			odd_total -= 2;
		}
next_frag:
		mp = mp->b_cont;
		if (!mp) {
			MARK_COVERAGE(0x1000);
			{
			uint32_t	u1 = sum;
			return ((uint16_t)u1);
			}
		}
		MARK_COVERAGE(0x4000);
		startp = mp->b_rptr;
		endp = mp->b_wptr;
		if (odd_total && (endp > startp)) {
			MARK_COVERAGE(0x8000);
			odd_total = 0;
			sum += U8_FETCH_SECOND(startp);
			startp++;
		}
	}
}
#undef	endptr
#undef	INIT_COVERAGE
#undef	INC_PTR
#undef	INC_ENDPTR
#undef	LOAD1
#undef	LOAD2
#undef	MARK_COVERAGE
#undef	ptr
#undef	SUM1
#undef	SUM2



#undef	FOLD_SUM
#undef	NEXT_ONE
#undef	NEXT_TWO
#undef	PREV_ONE
#undef	PREV_TWO
#undef	U8_FETCH_FIRST
#undef	U8_FETCH_SECOND
#undef	U16AM
#undef	U16_FETCH