/*********************************************************************
 *
 *	User Datagram Protocol (UDP) Communications Layer
 *  Module for Microchip TCP/IP Stack
 *	 -Provides unreliable, minimum latency transport of application 
 *    datagram (packet) oriented data
 *	 -Reference: RFC 768
 *
 *********************************************************************
 * FileName:        UDP.c
 * Dependencies:    StackTsk.h
 *                  MAC.h
 * Processor:       PIC18, PIC24F, PIC24H, dsPIC30F, dsPIC33F
 * Complier:        Microchip C18 v3.02 or higher
 *					Microchip C30 v2.01 or higher
 * Company:         Microchip Technology, Inc.
 *
 * Software License Agreement
 *
 * Copyright © 2002-2007 Microchip Technology Inc.  All rights 
 * reserved.
 *
 * Microchip licenses to you the right to use, modify, copy, and 
 * distribute: 
 * (i)  the Software when embedded on a Microchip microcontroller or 
 *      digital signal controller product (“Device”) which is 
 *      integrated into Licensee’s product; or
 * (ii) ONLY the Software driver source files ENC28J60.c and 
 *      ENC28J60.h ported to a non-Microchip device used in 
 *      conjunction with a Microchip ethernet controller for the 
 *      sole purpose of interfacing with the ethernet controller. 
 *
 * You should refer to the license agreement accompanying this 
 * Software for additional information regarding your rights and 
 * obligations.
 *
 * THE SOFTWARE AND DOCUMENTATION ARE PROVIDED “AS IS” WITHOUT 
 * WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT 
 * LIMITATION, ANY WARRANTY OF MERCHANTABILITY, FITNESS FOR A 
 * PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL 
 * MICROCHIP BE LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR 
 * CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF 
 * PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY OR SERVICES, ANY CLAIMS 
 * BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE 
 * THEREOF), ANY CLAIMS FOR INDEMNITY OR CONTRIBUTION, OR OTHER 
 * SIMILAR COSTS, WHETHER ASSERTED ON THE BASIS OF CONTRACT, TORT 
 * (INCLUDING NEGLIGENCE), BREACH OF WARRANTY, OR OTHERWISE.
 *
 *
 * Author               Date    Comment
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * Nilesh Rajbharti     3/19/01  Original        (Rev 1.0)
 * Nilesh Rajbharti     2/26/03  Fixed UDPGet and UDPProcess bugs
 *                               as discovered and fixed by John Owen
 *                               of Powerwave.
 *                               1. UDPGet would return FALSE on last good byte
 *                               2. UDPProcess was incorrectly calculating length.
 * Nilesh Rajbharti     5/19/03  Added bFirstRead flag similar to TCP
 *                               to detect very first UDPGet and
 *                               reset MAC Rx pointer to begining of
 *                               UDP data area.  This would ensure that
 *                               if UDP socket has pending Rx data and
 *                               another module resets MAC Rx pointer,
 *                               next UDP socket Get would get correct
 *                               data.
 * Robert Sloan (RSS)    5/29/03 Improved FindMatchingSocket()
 * Nilesh Rajbharti     12/2/03  Added UDPChecksum logic in UDPProcess()
 * Nilesh Rajbharti     12/5/03  Modified UDPProcess() and FindMatchingSocket()
 *                               to include localIP as new parameter.
 *                               This corrects pseudo header checksum
 *                               logic in UDPProcess().
 *                               It also corrects broadcast packet
 *                               matching correct in FindMatchingSocket().
 * Howard Schlunder		1/16/06	 Fixed an imporbable RX checksum bug 
 *								 when using a Microchip Ethernet controller)
 * Howard Schlunder		6/02/06	 Fixed a bug where all RXed UDP packets 
 *								 without a checksum (0x0000) were thrown
 *								 away.  No checksum is legal in UDP.
 * Howard Schlunder		8/10/06	 Fixed a bug where UDP sockets would 
 *								 unintentionally keep the remote MAC 
 *								 address cached, even after calling 
 *								 UDPInit(), UDPClose(), or reseting 
 *								 the part without clearing all the 
 *								 PICmicro memory.
 ********************************************************************/
#define __UDP_C

#include "TCPIP Stack/TCPIP.h"

#if defined(STACK_USE_UDP)

#define LOCAL_UDP_PORT_START_NUMBER (50000u)
#define LOCAL_UDP_PORT_END_NUMBER   (51999u)


UDP_SOCKET_INFO		UDPSocketInfo[MAX_UDP_SOCKETS];
UDP_SOCKET			activeUDPSocket;
static UDP_SOCKET	LastPutSocket = INVALID_UDP_SOCKET;

static UDP_SOCKET FindMatchingSocket(UDP_HEADER *h, NODE_INFO *remoteNode,
                                    IP_ADDR *localIP);

/*********************************************************************
 * Function:        void UDPInit(void)
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        Initializes internal variables.
 *
 * Note:
 ********************************************************************/
void UDPInit(void)
{
    UDP_SOCKET s;

    for ( s = 0; s < MAX_UDP_SOCKETS; s++ )
    {
		UDPClose(s);
    }
}

/*********************************************************************
 * Function:        UDP_SOCKET UDPOpen(UDP_PORT localPort,
 *                                     NODE_INFO *remoteNode,
 *                                     UDP_PORT remotePort)
 *
 * PreCondition:    UDPInit() is already called
 *
 * Input:           remoteNode - Remote Node info such as MAC and IP
 *                               address
 *                               If NULL, broadcast node address is set.
 *                  remotePort - Remote Port to which to talk to
 *                               If INVALID_UDP_SOCKET, localPort is
 *                               opened for Listen.
 *                  localPort  - A valid port number or 0x0000 if 
 *								 dynamic assignment is desired.
 *
 * Output:          On success: A UDP socket handle that can be used 
 *					for subsequent UDP API calls.
 *					On failure: INVALID_UDP_SOCKET
 *
 * Side Effects:    None
 *
 * Overview:        A UDP socket is allocated and parameters are stored.
 *
 * Note:            If the localPort is not specified, a unique port 
 *					number is automatically assigned.
 ********************************************************************/
UDP_SOCKET UDPOpen(UDP_PORT localPort,
                   NODE_INFO *remoteNode,
                   UDP_PORT remotePort)
{
    UDP_SOCKET s;
    UDP_SOCKET_INFO *p;

	// Local temp port numbers.
	static WORD NextPort __attribute__((persistent));


    p = UDPSocketInfo;
    for ( s = 0; s < MAX_UDP_SOCKETS; s++ )
    {
        if(p->localPort == INVALID_UDP_PORT)
        {
			p->localPort = localPort;	

			if(localPort == 0x0000u)
			{
				if(NextPort > LOCAL_UDP_PORT_END_NUMBER || NextPort < LOCAL_UDP_PORT_START_NUMBER)
					NextPort = LOCAL_UDP_PORT_START_NUMBER;
	
	            p->localPort    = NextPort++;
			}

            // If remoteNode is supplied, remember it.
            if(remoteNode)
            {
                memcpy((void*)&p->remoteNode,
                        (const void*)remoteNode,
                        sizeof(p->remoteNode));
            }
            else
			{
				// else Set broadcast address
				memset((void*)&p->remoteNode, 0xFF, sizeof(p->remoteNode));
			}

            p->remotePort   = remotePort;
            p->TxCount      = 0;
            p->RxCount      = 0;

            // Mark this socket as active.
            // Once an active socket is set, subsequent operation can be
            // done without explicitely supply socket identifier.
            activeUDPSocket = s;
            return s;
        }
        p++;
    }

    return (UDP_SOCKET)INVALID_UDP_SOCKET;
}




/*********************************************************************
 * Function:        void UDPClose(UDP_SOCKET s)
 *
 * PreCondition:    UDPOpen() is already called
 *
 * Input:           s       - Socket that is to be closed.
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        Given socket is marked as available for future
 *                  new communcations.
 *
 * Note:            This function does not affect previous
 *                  active UDP socket designation.
  ********************************************************************/
void UDPClose(UDP_SOCKET s)
{
    UDPSocketInfo[s].localPort = INVALID_UDP_PORT;
	UDPSocketInfo[s].remoteNode.IPAddr.Val = 0x00000000;
    UDPSocketInfo[s].Flags.bFirstRead = FALSE;
}


/*********************************************************************
 * Function:        BOOL UDPIsPutReady(UDP_SOCKET s)
 *
 * PreCondition:
 *
 * Input:           s       - Socket that is to be loaded and made
 *                            an active UDP socket.
 *
 * Output:          WORD: Number of bytes that can be put, 0 if none
 *
 * Side Effects:    None
 *
 * Overview:        None
 *
 * Note:            This call sets given socket as an active UDP socket.
 ********************************************************************/
WORD UDPIsPutReady(UDP_SOCKET s)
{
	if(LastPutSocket != s)
	{
		LastPutSocket = s;
		MACSetWritePtr(BASE_TX_ADDR + sizeof(ETHER_HEADER) + sizeof(IP_HEADER) + sizeof(UDP_HEADER));
		UDPSocketInfo[s].TxCount = 0;
	}

	activeUDPSocket = s;
	if(!MACIsTxReady())
		return 0;

	return MAC_TX_BUFFER_SIZE - sizeof(IP_HEADER) - sizeof(UDP_HEADER) - UDPSocketInfo[activeUDPSocket].TxCount;
}

/*********************************************************************
 * Function:        BOOL UDPPut(BYTE v)
 *
 * PreCondition:    UDPIsPutReady() > 0 with desired UDP socket
 *                  that is to be loaded.
 *
 * Input:           v       - Data byte to loaded into transmit buffer
 *
 * Output:          TRUE if put successful
 *                  FALSE if transmit buffer is full and put failed
 *
 * Side Effects:    None
 *
 * Overview:        Given data byte is put into UDP transmit buffer
 *                  and active UDP socket buffer length is incremented
 *                  by one.
 *                  If buffer has become full, FALSE is returned.
 *                  Or else TRUE is returned.
 *
 * Note:            This function loads data into an active UDP socket
 *                  as determined by previous call to UDPIsPutReady()
 ********************************************************************/
BOOL UDPPut(BYTE v)
{
	// See if we are out of transmit space.
	if(UDPSocketInfo[activeUDPSocket].TxCount >= (MAC_TX_BUFFER_SIZE - sizeof(IP_HEADER) - sizeof(UDP_HEADER)))
	{
		return FALSE;
	}

    // Load application data byte
    MACPut(v);
	UDPSocketInfo[activeUDPSocket].TxCount++;

    return TRUE;
}

/*********************************************************************
 * Function:        WORD UDPPutArray(BYTE *cData, WORD wDataLen)
 *
 * PreCondition:    UDPIsPutReady() > 0 with desired UDP socket
 *                  that is to be loaded.
 *
 * Input:           *cData - Pointer to data to write
 *					wDataLen - Length of data @ *cData to write
 *
 * Output:          WORD: Number of bytes successfully placed in the 
 *						  UDP transmit buffer
 *
 * Side Effects:    None
 *
 * Overview:        None
 *
 * Note:            None
 ********************************************************************/
WORD UDPPutArray(BYTE *cData, WORD wDataLen)
{
	WORD wTemp;

	wTemp = (MAC_TX_BUFFER_SIZE - sizeof(IP_HEADER) - sizeof(UDP_HEADER)) - UDPSocketInfo[activeUDPSocket].TxCount;
	if(wTemp < wDataLen)
		wDataLen = wTemp;

	UDPSocketInfo[activeUDPSocket].TxCount += wDataLen; 

    // Load application data bytes
    MACPutArray(cData, wDataLen);

    return wDataLen;
}

WORD UDPPutROMArray(ROM BYTE *cData, WORD wDataLen)
{
	WORD wTemp;

	wTemp = (MAC_TX_BUFFER_SIZE - sizeof(IP_HEADER) - sizeof(UDP_HEADER)) - UDPSocketInfo[activeUDPSocket].TxCount;
	if(wTemp < wDataLen)
		wDataLen = wTemp;

	UDPSocketInfo[activeUDPSocket].TxCount += wDataLen; 

    // Load application data bytes
    MACPutROMArray(cData, wDataLen);

    return wDataLen;
}

BYTE* UDPPutString(BYTE *strData)
{
	return strData + UDPPutArray(strData, strlen((char*)strData));
}

ROM BYTE* UDPPutROMString(ROM BYTE *strData)
{
	return strData + UDPPutROMArray(strData, strlenpgm((ROM char*)strData));
}



/*********************************************************************
 * Function:        BOOL UDPFlush(void)
 *
 * PreCondition:    UDPPut() is already called and desired UDP socket
 *                  is set as an active socket by calling
 *                  UDPIsPutReady().
 *
 * Input:           None
 *
 * Output:          All and any data associated with active UDP socket
 *                  buffer is marked as ready for transmission.
 *
 * Side Effects:    None
 *
 * Overview:        None
 *
 * Note:            This function transmit all data from
 *                  an active UDP socket.
 ********************************************************************/
void UDPFlush(void)
{
    UDP_HEADER      h;
    UDP_SOCKET_INFO *p;

    // Wait for TX hardware to become available (finish transmitting 
    // any previous packet)
    while(!IPIsTxReady());

    p = &UDPSocketInfo[activeUDPSocket];

	
    h.SourcePort        = swaps(p->localPort);
    h.DestinationPort   = swaps(p->remotePort);
    h.Length            = (WORD)((WORD)p->TxCount + (WORD)sizeof(UDP_HEADER));
    // Do not swap h.Length yet.  It is needed in IPPutHeader.
    h.Checksum          = 0x0000;

    MACSetWritePtr(BASE_TX_ADDR + sizeof(ETHER_HEADER));

    // Load IP header.
    IPPutHeader( &p->remoteNode,
                 IP_PROT_UDP,
                 h.Length );


    // Now swap h.Length.
    h.Length            = swaps(h.Length);

    // Now load UDP header.
    IPPutArray((BYTE*)&h, sizeof(h));

    // Update checksum.
    // TO BE IMPLEMENTED

    MACFlush();

    p->TxCount          = 0;
	LastPutSocket 		= INVALID_UDP_SOCKET;
}



/*********************************************************************
 * Function:        WORD UDPIsGetReady(UDP_SOCKET s)
 *
 * PreCondition:    UDPInit() is already called.
 *
 * Input:           A valid UDP socket that is already "Listen"ed on
 *                  or opened.
 *
 * Output:          WORD: Number of available bytes that can be 
 *					retrieved.  0 if no data received on this socket.
 *
 * Side Effects:    Given socket is set as an active UDP Socket.
 *
 * Overview:        None
 *
 * Note:            This function automatically sets supplied socket
 *                  as an active socket.  Caller need not call
 *                  explicit function UDPSetActiveSocket().  All
 *                  subsequent calls will us this socket as an
 *                  active socket.
 ********************************************************************/
WORD UDPIsGetReady(UDP_SOCKET s)
{
    activeUDPSocket = s;
    return UDPSocketInfo[activeUDPSocket].RxCount;
}

/*********************************************************************
 * Function:        BOOL UDPGet(BYTE *v)
 *
 * PreCondition:    UDPInit() is already called     AND
 *                  UDPIsGetReady(s) > 0
 *
 * Input:           v       - Buffer to receive UDP data byte
 *
 * Output:          TRUE    if a data byte was read
 *                  FALSE   if no data byte was read or available
 *
 * Side Effects:    None
 *
 * Overview:        None
 *
 * Note:            This function fetches data from an active UDP
 *                  socket as set by UDPIsGetReady() call.
 ********************************************************************/
BOOL UDPGet(BYTE *v)
{
	// Make sure that there is data to return
    if ( UDPSocketInfo[activeUDPSocket].RxCount == 0u )
        return FALSE;

    // If if this very first read to packet, set MAC Rx Pointer to
    // beginig of UDP data area.
    if ( UDPSocketInfo[activeUDPSocket].Flags.bFirstRead )
    {
        UDPSocketInfo[activeUDPSocket].Flags.bFirstRead = FALSE;
        UDPSetRxBuffer(0);
    }

    *v = MACGet();

    UDPSocketInfo[activeUDPSocket].RxCount--;

    if ( UDPSocketInfo[activeUDPSocket].RxCount == 0u )
    {
        MACDiscardRx();
    }

    return TRUE;
}

/*********************************************************************
 * Function:        WORD UDPGetArray(BYTE *cData, WORD wDataLen)
 *
 * PreCondition:    UDPInit() is already called     AND
 *                  UDPIsGetReady(s) > 0
 *
 * Input:           *cData - Pointer to location to write retrieved bytes
 *					wDataLen - Number of bytes to retreive
 *				
 * Output:          WORD - Number of bytes written to cData
 *
 * Side Effects:    None
 *
 * Overview:        None
 *
 * Note:            This function fetches data from an active UDP
 *                  socket as set by UDPIsGetReady()
 ********************************************************************/
WORD UDPGetArray(BYTE *cData, WORD wDataLen)
{
    // If this is the very first read of the packet, set MAC Rx Pointer to
    // beginig of UDP data area.
    if(UDPSocketInfo[activeUDPSocket].Flags.bFirstRead)
    {
        UDPSocketInfo[activeUDPSocket].Flags.bFirstRead = FALSE;
        UDPSetRxBuffer(0);
    }

	// Make sure that there is data to return
	if(UDPSocketInfo[activeUDPSocket].RxCount < wDataLen)
		wDataLen = UDPSocketInfo[activeUDPSocket].RxCount;

	wDataLen = MACGetArray(cData, wDataLen);

    UDPSocketInfo[activeUDPSocket].RxCount -= wDataLen;

    if(UDPSocketInfo[activeUDPSocket].RxCount == 0u)
    {
        MACDiscardRx();
    }

    return wDataLen;
}

/*********************************************************************
 * Function:        void UDPDiscard(void)
 *
 * PreCondition:    UDPInit() is already called
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        None
 *
 * Note:            This function discards an active UDP socket content.
 ********************************************************************/
void UDPDiscard(void)
{
    if(UDPSocketInfo[activeUDPSocket].RxCount)
        MACDiscardRx();

    UDPSocketInfo[activeUDPSocket].RxCount = 0;
}



/*********************************************************************
 * Function:        BOOL UDPProcess(NODE_INFO* remoteNode,
 *                                  IP_ADDR *localIP,
 *                                  WORD len)
 *
 * PreCondition:    UDPInit() is already called     AND
 *                  UDP segment is ready in MAC buffer
 *
 * Input:           remoteNode      - Remote node info
 *                  len             - Total length of UDP semgent.
 *
 * Output:          TRUE if this function has completed its task
 *                  FALSE otherwise
 *
 * Side Effects:    None
 *
 * Overview:        None
 *
 * Note:            None
 ********************************************************************/
// Pseudo header as defined by rfc 793.
typedef struct _PSEUDO_HEADER
{
    IP_ADDR SourceAddress;
    IP_ADDR DestAddress;
    BYTE Zero;
    BYTE Protocol;
    WORD Length;
} PSEUDO_HEADER;

#define SwapPseudoHeader(h)  (h.Length = swaps(h.Length))

BOOL UDPProcess(NODE_INFO *remoteNode, IP_ADDR *localIP, WORD len)
{
    UDP_HEADER		h;
    UDP_SOCKET		s;
    PSEUDO_HEADER	pseudoHeader;
    DWORD_VAL		checksums;


    // Retrieve UDP header.
    MACGetArray((BYTE*)&h, sizeof(h));

    h.SourcePort        = swaps(h.SourcePort);
    h.DestinationPort   = swaps(h.DestinationPort);
    h.Length            = swaps(h.Length) - sizeof(UDP_HEADER);

	// See if we need to validate the checksum field (0x0000 is disabled)
	if(h.Checksum)
	{
	    // Calculate IP pseudoheader checksum.
	    pseudoHeader.SourceAddress		= remoteNode->IPAddr;
	    pseudoHeader.DestAddress.v[0]	= localIP->v[0];
	    pseudoHeader.DestAddress.v[1]	= localIP->v[1];
	    pseudoHeader.DestAddress.v[2]	= localIP->v[2];
	    pseudoHeader.DestAddress.v[3]	= localIP->v[3];
	    pseudoHeader.Zero				= 0x0;
	    pseudoHeader.Protocol			= IP_PROT_UDP;
	    pseudoHeader.Length				= len;

	    SwapPseudoHeader(pseudoHeader);
	
	    checksums.w[0] = ~CalcIPChecksum((BYTE*)&pseudoHeader,
	                                    sizeof(pseudoHeader));
	
	
	    // Now calculate UDP packet checksum in NIC RAM -- should match pseudoHeader
	    IPSetRxBuffer(0);
	    checksums.w[1] = CalcIPBufferChecksum(len);
	
	    if(checksums.w[0] != checksums.w[1])
	    {
	        MACDiscardRx();
	        return TRUE;
	    }
	}

    s = FindMatchingSocket(&h, remoteNode, localIP);
    if ( s == INVALID_UDP_SOCKET )
    {
         // If there is no matching socket, There is no one to handle
         // this data.  Discard it.
        MACDiscardRx();
    }
    else
    {
        UDPSocketInfo[s].RxCount = h.Length;
        UDPSocketInfo[s].Flags.bFirstRead = TRUE;
    }


    return TRUE;
}


/*********************************************************************
 * Function:        UDP_SOCKET FindMatchingSocket(UDP_HEADER *h,
 *                                NODE_INFO *remoteNode,
 *                                IP_ADDR *localIP)
 *
 * PreCondition:    UDP Segment header has been retrieved from buffer
 *                  The IP header has also been retrieved
 *
 * Input:           remoteNode      - Remote node info from IP header
 *                  h               - header of UDP semgent.
 *
 * Output:          matching UDP socket or INVALID_UDP_SOCKET
 *
 * Side Effects:    None
 *
 * Overview:        None
 *
 * Note:            None
 ********************************************************************/
#define BROADCAST_ADDRESS   (0xfffffffful)
static UDP_SOCKET FindMatchingSocket(UDP_HEADER *h,
                                     NODE_INFO *remoteNode,
                                     IP_ADDR *localIP)
{
    UDP_SOCKET s;
    UDP_SOCKET partialMatch;
    UDP_SOCKET_INFO *p;

    partialMatch = INVALID_UDP_SOCKET;

    p = UDPSocketInfo;
    for ( s = 0; s < MAX_UDP_SOCKETS; s++ )
    {
        // This packet is said to be matching with current socket
        // 1. If its destination port matches with our local port.
        // 2. This socket does not have any data pending.
        // 3. Packet source IP address matches with socket remote IP address.
        //    OR this socket had transmitted packet with destination address as broadcast.
        if ( p->localPort == h->DestinationPort )
        {
            if ( (p->remotePort == h->SourcePort) && (p->RxCount == 0ul) )
            {
                if ( (p->remoteNode.IPAddr.Val == remoteNode->IPAddr.Val) ||
                     (localIP->Val == BROADCAST_ADDRESS) )
                {
                    return s;
                }
            }

            partialMatch = s;
        }
        p++;
    }

    if ( partialMatch != INVALID_UDP_SOCKET )
    {
        p = &UDPSocketInfo[partialMatch];

        memcpy((void*)&p->remoteNode,
                (const void*)remoteNode, sizeof(p->remoteNode) );

        p->remotePort = h->SourcePort;
    }
    return partialMatch;
}


#endif //#if defined(STACK_USE_UDP)
