// Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// in_chk.cpp - IPv6/IPv4 checksum module
//

#include <in_sock.h> // IPv6 enhanced in_sock.h
#include "in_chk.h"


EXPORT_C void TChecksum::Add(RMBufChain &aPacket, TInt aOffset)
	{
	TMBufIter m(aPacket);
	const RMBuf *mbuf;
    TUint32 count = 0;

	while ((mbuf = m++) != NULL)
		{
	    const TUint16 *p, *end;
		const TUint8 *ptr = mbuf->Ptr(), *boundary;
		TInt len = mbuf->Length();
	    union { TUint16 i16; TUint8 i8[2]; } u;

		if (len <= 0)
			continue;
		if (aOffset > 0)
			{
			aOffset -= len;
			if (aOffset >= 0)
				continue;
			//
			// Setup for computing the checksum, starting
			// from middle of the buffer. aOffset is left
			// with negative value.
			//
			ptr += (aOffset + len);
			len = -aOffset;
	        }

		//
	    // Collect any solitary leading and trailing octets
	    // into a single half word and add that first.
	    //
	    u.i16 = 0;

	    // Leading octet
	    if ((TUint32)ptr & 1)
	    	{
		    u.i8[1] = *ptr++;
            count++;
            len--;
	        }

		// Trailing octet
	    boundary = ptr + len;
	    if (len & 1)
			u.i8[0] = *--boundary;
	    end = (TUint16*)boundary;

        //
        // Explicit loop unrolling for checksum calculation.
            //
        // Duff's device, while not pretty, is perfectly legal
        // C++ as well as being the most efficient way to do it.
        // The switch statement is used to enter the do-while
        // loop at the correct offset. As a result, we get one
        // partial iteration of the loop followed by zero or
        // more full iterations. Exacly four iterations are
        // required to process a completely filled RMBuf.
        //
        // The gcc used in the current SDK tool chain doesnt't
        // seem to be very good at optimizing ARM code, so we
        // try to make things simple for it.
        //
	    register TUint32 sum = u.i16;
        const TInt loops = (len + 30) >> 5;
        if (loops)
        	{
            p = end - (loops << 4);
		    switch ((len >> 1) & 15)
		    	{
			default:
				do {			//lint -fallthrough
		                case  0:		sum += p[ 0];	//lint -fallthrough
		                case 15:		sum += p[ 1];	//lint -fallthrough
		                case 14:		sum += p[ 2];	//lint -fallthrough
		                case 13:		sum += p[ 3];	//lint -fallthrough
		                case 12:		sum += p[ 4];	//lint -fallthrough
		                case 11:		sum += p[ 5];	//lint -fallthrough
		                case 10:		sum += p[ 6];	//lint -fallthrough
		                case  9:		sum += p[ 7];	//lint -fallthrough
		                case  8:		sum += p[ 8];	//lint -fallthrough
		                case  7:		sum += p[ 9];	//lint -fallthrough
		                case  6:		sum += p[10];	//lint -fallthrough
		                case  5:		sum += p[11];	//lint -fallthrough
		                case  4:		sum += p[12];	//lint -fallthrough
		                case  3:		sum += p[13];	//lint -fallthrough
		                case  2:		sum += p[14];	//lint -fallthrough
		                case  1:		sum += p[15];
					} while ((p += 16) < end);
		        }
			}

        //
        // Swap byte order if there is an alignment mismatch
        // between this buffer and the previous ones. We fold
        // the partial sum into the lower half word in order
        // to avoid the possibility of overflow.
        //
        if (count & 1)
        	{
	        iSum += (sum >> 24);          // add octet  3
	        iSum += (sum >> 8) & 0xffff,  // add octets 2-1
	        iSum += (sum << 8) & 0xff00;  // add octet  0
        	}
        else
        	{
	        iSum += sum;
        	}

        // Update number of bytes summed
        count += len;
    	}
	}

EXPORT_C void TChecksum::Add(RMBufChain &aPacket, const RMBufPktInfo *aInfo, TInt aOffset)
	{
	if (aInfo)
		{
		//
		// Compute the pseudo header part
		//
		// It is assumed that the aInfo->iLength is the length
		// of the full IPv6 packet, and the aOffset gives the
		// beginning of the upper layer header. For the pseudo
		// header computation, the upper layer length must be
		// used and thus the aOffset is subtracted!
		//
        AddH((TUint32)aInfo->iLength - aOffset);
        AddH((TUint16)aInfo->iProtocol);
		Add((TUint16 *)&TInetAddr::Cast(aInfo->iSrcAddr).Ip6Address(), 16);
		Add((TUint16 *)&TInetAddr::Cast(aInfo->iDstAddr).Ip6Address(), 16);
        }
	//
	// Add the payload part
	//
	Add(aPacket, aOffset);
	}

EXPORT_C TUint32 TChecksum::Calculate(const TUint16 *aPtr, TInt aLen)
	{
	register TUint32 sum = 0;

    if (aLen >>= 1)
    	{
        //
        // Explicit loop unrolling for checksum calculation.
            //
        // Duff's device, while not pretty, is perfectly legal
        // C++ as well as being the most efficient way to do it.
        // The switch statement is used to enter the do-while
        // loop at the correct offset. As a result, we get one
        // partial iteration of the loop followed by zero or
        // more full iterations. Exacly four iterations are
        // required to process a completely filled RMBuf.
        //
        // The gcc used in the current SDK tool chains doesnt't
        // seem to be very good at optimizing ARM code, so we
        // try to make things simple for it.
        //
    	const TUint16 *end = aPtr + aLen;
        const TInt loops = (aLen + 15) >> 4;
        register const TUint16 *p = end - (loops << 4);
		switch (aLen & 15)
			{
		default:
			do {			//lint -fallthrough
		        case  0:		sum += p[ 0];	//lint -fallthrough
		        case 15:		sum += p[ 1];	//lint -fallthrough
		        case 14:		sum += p[ 2];	//lint -fallthrough
		        case 13:		sum += p[ 3];	//lint -fallthrough
		        case 12:		sum += p[ 4];	//lint -fallthrough
		        case 11:		sum += p[ 5];	//lint -fallthrough
		        case 10:		sum += p[ 6];	//lint -fallthrough
		        case  9:		sum += p[ 7];	//lint -fallthrough
		        case  8:		sum += p[ 8];	//lint -fallthrough
		        case  7:		sum += p[ 9];	//lint -fallthrough
		        case  6:		sum += p[10];	//lint -fallthrough
		        case  5:		sum += p[11];	//lint -fallthrough
		        case  4:		sum += p[12];	//lint -fallthrough
		        case  3:		sum += p[13];	//lint -fallthrough
		        case  2:		sum += p[14];	//lint -fallthrough
		        case  1:		sum += p[15];
				} while ((p += 16) < end);
			}
        }

	return sum;
	}
