This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

RM57L843: Ethernet - LWIP Integration

Part Number: RM57L843
Other Parts Discussed in Thread: DP83848I, DP83640

Hi!

We are using RM57 microcontroller in one of our projects, when we tried to integrate LWIP 1.4.1 downloaded from TI to our code we are facing some issues.

Below are the details:

PHY used is DP83848I, connection is MII.

Application : we are sending a UDP Packet to PC, we are unable to see anything on wireshark (not even ARP packet), we didn't see any error while executing the UDP sequence.

UDP Sequence we followed is :

1.lwIPInit()

2.pbuf_alloc()

3.udp_new()

4.udp_bind()

5.udp_connect()

6.udp_send()

7. Enabled Tx and Rx Interrupts.

The references that we followed are from the following links:

https://e2e.ti.com/support/archive/launchyourdesign/m/boosterpackcontest/666058

https://e2e.ti.com/support/microcontrollers/hercules/f/hercules-safety-microcontrollers-forum/412821/udp-communication-with-lwip-library

To add to this, we have seen the Manchester encoded signals on either side of PHY whenever we were continuously sending PING command from command prompt which indicates the fact that the circuitry should be fine (mostly).

Can someone please guide us to resolve the above issue i.e., any extra care to be taken while LWIP integration or any additions to above sequence.

Regards,

Chetan.

  • Hi Chetan,

    Did you try the mentioned example on TI launchpad which Karthikeyan used for the example? Any difference between DP83848I used on your board and DP83640 on TI launchpad?

  • Hi Wang,

    Unfortunately, I don't have TI Launchpad. As far as the difference between DP83848I and DP83640 is concerned, the datasheet recommendations and basic circuitry remain the same.

    Infact, we have replaced DP83848I with DP83640 and checked. The issue remains the same. 

    Any other suggestions?

    Regards,

    Chetan.

  • Hi Chetan,

    I don't have a ready UDP example.

  • Hi Wang,

    Can you please escalate this to concerned team or if you are probably connected to this, please look into it. If it takes time to jot down certain key points and a snippet of working code, that is fine with me. Ethernet has become a bottleneck in my project and therefore any sort of help would be highly appreciated.

    Thanking you in anticipation of your kind and earliest response in this regard.

  • Hello Chetan,

    I just done an example of ethernet bootloader on RM57Lx device. The example uses tftp and UDP of LWIP1.4.1. I will post the related files for your reference.

    void
    TFTPInit(tTFTPRequest pfnRequest)
    {
    struct udp_pcb *UdpPcb;
    //
    // Remember the application's notification callback.
    //
    g_pfnRequest = pfnRequest;

    //
    // Start listening for incoming TFTP requests.
    //
    UdpPcb = udp_new();
    udp_bind(UdpPcb, IP_ADDR_ANY, TFTP_PORT);
    udp_recv(UdpPcb, TFTPRecv, NULL);
    while(!image_download);
    }

    bl_tftp.c
    #include "HL_sys_common.h"
    #include "tftp.h"
    #include "bl_flash.h"
    #include "bl_config.h"
    #include "uartstdio.h"
    #include <string.h>
    
    
    static uint8_t image_buf[512]={0};
    extern tBoolean last_packet;
    extern tBoolean image_download;
    
    extern uint32_t g_pulUpdateSuccess[8];
    extern uint32_t g_ulUpdateStatusAddr;
    extern uint32_t g_ulUpdateBufferSize;
    uint32_t FlashDestination = APP_START_ADDRESS;
    uint32_t erase_length = APPLICATION_IMAGE_SIZE;
    
    extern void TFTPInit(tTFTPRequest pfnRequest);
    
    //
    //TFTP write Image into Flash
    //
    static tTFTPError
    TFTP_WriteImage(tTFTPConnection *psTFTP)
    {
    	uint32_t oReturnCheck=0;
    
        memcpy(image_buf + psTFTP->ulDataRemaining, psTFTP->pucData, psTFTP->ulDataLength);
    
        if(last_packet)
        {
        	if((psTFTP->ulDataRemaining + psTFTP->ulDataLength) == TFTP_BLOCK_SIZE)
        	{
        		oReturnCheck = Fapi_BlockProgram(FlashDestination+(psTFTP->ulBlockNum - 1) * TFTP_BLOCK_SIZE,(uint32_t)(image_buf),TFTP_BLOCK_SIZE);
                // Return an error if an access violation occurred.
                if(oReturnCheck)
                {
                    UARTprintf("\r Program Flash failed:  ");
                }else{
                    UARTprintf("\r Block %d has been programmed!", psTFTP->ulBlockNum);
                }
    
        	}
        	else
        	{
                oReturnCheck = Fapi_BlockProgram(FlashDestination+(psTFTP->ulBlockNum - 1) * TFTP_BLOCK_SIZE, (uint32_t)(image_buf), (psTFTP->ulDataRemaining + psTFTP->ulDataLength) + 4 - (psTFTP->ulDataRemaining + psTFTP->ulDataLength)%4);
                if(oReturnCheck)
                {
                    UARTprintf("\r Program Flash failed:  ");
                }
                g_pulUpdateSuccess[2] = (psTFTP->ulBlockNum - 1) * TFTP_BLOCK_SIZE + (psTFTP->ulDataRemaining + psTFTP->ulDataLength) + 4 - (psTFTP->ulDataRemaining + psTFTP->ulDataLength)%4;
                oReturnCheck = Fapi_UpdateStatusProgram(g_ulUpdateStatusAddr, (uint32_t)&g_pulUpdateSuccess[0], g_ulUpdateBufferSize);
                if(oReturnCheck)
                {
                    UARTprintf("\r Program Flash failed:  ");
                }else{
                    UARTprintf("\r Application was loaded successful!  ");
        	    	image_download=1;
                }
        	}
        	memset(image_buf,0xFF,512);
        	last_packet=0;
        }
    
        //
        // Did all go as planned?
        //
    
        if(!oReturnCheck)
        {
            //
            // Everything is fine.
            //
            return(TFTP_OK);
        }
        else
        {
            //
            // Oops - we can't erase the block.  Report an error.
            //
            psTFTP->pcErrorString = "Flash write failure.";
            return(TFTP_ERR_NOT_DEFINED);
        }
    }
    
    //*****************************************************************************
    //
    // Read a block of data from the Flash
    //
    //*****************************************************************************
    static tTFTPError
    TFTP_ReadImage(tTFTPConnection *psTFTP)
    {
    	//Didn't develop the read function
    	return(TFTP_ERR_NOT_DEFINED);;
    }
    
    
    //*****************************************************************************
    //
    // Signals that the TFTP connection is being closed down.
    //
    //*****************************************************************************
    static void
    TFTP_Close(tTFTPConnection *psTFTP)
    {
        //
        // Nothing to do here currently.
        //
    }
    
    
    //*****************************************************************************
    //
    // Checks incoming TFTP request to determine if we want to handle it.
    //
    // \param psTFTP points to the TFTP connection instance structure for this
    // request.
    // \param bGet is \b true of the request is a GET (read) or \b false if it is
    // a PUT (write).
    // \param pucFileName points to a NULL terminated string containing the
    // requested filename.
    // \param tTFTPMode indicates the requested transfer mode, ASCII or binary.
    //
    // This function is called by the TFTP server whenever a new request is
    // received.  It checks the passed filename to determine whether it is valid.
    // If the filename is valid, the function sets fields \e ulDataRemaining, \e
    // pfnGetData and \e pfnClose in the \e psTFTP structure for a GET request or
    // fields \e pfnPutData, \e pfnClosebefore returning
    // \e TFTP_OK to continue processing the request.
    //
    // This implementation supports requests for "eeprom" which will read or write
    // the image stored in the serial flash device and "extflash" which will
    // access an image stored in the flash provided by the Flash/SRAM/LCD daughter
    // board if this is installed.
    //
    // \return Returns \e TFTP_OK if the request should be processed or any other
    // TFTP error code otherwise.  In cases where \e TFTP_OK is not returned, the
    // field psTFTP->pcErrorString may be set with an ASCII error string which
    // will be returned to the TFTP client.
    //
    //*****************************************************************************
    static tTFTPError
    TFTP_Request(tTFTPConnection *psTFTP, tBoolean bGet, char *pucFileName,
                    tTFTPMode eMode)
    {
        uint32_t oReturnCheck=0;
    #ifdef MSG_DEBUG
        UARTprintf("TFTP Request to Erase Flash... \n\r");
    #endif
    
        UARTprintf("Erasing flash ...... \n\r");
        oReturnCheck = Fapi_BlockErase(FlashDestination, erase_length);
        if(oReturnCheck)
        {
        	psTFTP->pcErrorString = "Flash erase failure.";
        	return(TFTP_ERR_NOT_DEFINED);
        }
        UARTprintf("Erase Done! \n\r");
    
        //
    	// Set the appropriate callback functions depending upon the
    	// type of request received.
    	//
    	psTFTP->pfnClose = TFTP_Close;
    
    	//
    	// Is this a GET or a PUT request?
    	//
    	if(bGet)
    	{
    		//
    		// GET request - fill in the image size and the data transfer
    		// function pointer.
    		//
    		psTFTP->pfnGetData = TFTP_ReadImage;
    	}
    	else
    	{
    		//
    		// PUT request - fill in the data transfer function pointer.
    		//
    		psTFTP->pfnPutData = TFTP_WriteImage;
    	}
    
        //
        // If we get here, all is well and the transfer can continue.
        //
        return(TFTP_OK);
    }
    
    //*****************************************************************************
    //
    // Initializes the TFTP server supporting the HDK board.
    // \return None.
    //
    //*****************************************************************************
    void TFTPQSInit(void)
    {
        //
        // Initialize the TFTP module and pass it our board-specific GET and PUT
        // request handler function pointer.
        //
        TFTPInit(TFTP_Request);
    }
    
    
    bl_tftp.htftp.h
    0456.tftp.c
    //*****************************************************************************
    //
    // tftp.c - A very simple lwIP TFTP server.
    //
    // Copyright (c) 2009-2020 Texas Instruments Incorporated.  All rights reserved.
    // Software License Agreement
    //
    // Texas Instruments (TI) is supplying this software for use solely and
    // exclusively on TI's microcontroller products. The software is owned by
    // TI and/or its suppliers, and is protected under applicable copyright
    // laws. You may not combine this software with "viral" open-source
    // software in order to form a larger program.
    //
    // THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
    // NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
    // NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    // A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
    // CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
    // DAMAGES, FOR ANY REASON WHATSOEVER.
    //
    //
    //*****************************************************************************
    
    #include "HL_sys_common.h"
    #include "lwiplib.h"
    #include "string.h"
    #include "ustdlib.h"
    #include "bl_config.h"
    
    #include "tftp.h"
    
    
    //*****************************************************************************
    //
    // The TFTP commands.
    //
    //*****************************************************************************
    #define TFTP_RRQ                1
    #define TFTP_WRQ                2
    #define TFTP_DATA               3
    #define TFTP_ACK                4
    #define TFTP_ERROR              5
    
    //*****************************************************************************
    //
    // The UDP port for the TFTP server.
    //
    //*****************************************************************************
    #define TFTP_PORT               69
    
    //*****************************************************************************
    //
    // Application connection notification callback.
    //
    //*****************************************************************************
    static tTFTPRequest g_pfnRequest;
    
    void TFTPInit(tTFTPRequest pfnRequest);
    
    //*****************************************************************************
    //
    // Image downloading flag
    //
    //*****************************************************************************
    tBoolean last_packet=0;
    tBoolean image_download=0;
    
    //*****************************************************************************
    //
    // Close the TFTP connection and free associated resources.
    //
    //*****************************************************************************
    static void
    TFTPClose(tTFTPConnection *psTFTP)
    {
    #ifdef MSG_DEBUG
        UARTprintf("TFTPClose in tftp.c... \n\r");
    #endif
        //
        // Tell the application we are closing the connection.
        //
        if(psTFTP->pfnClose)
        {
            psTFTP->pfnClose(psTFTP);
        }
    
        //
        // Close the underlying UDP connection.
        //
        udp_remove(psTFTP->pPCB);
    
        //
        // Free the instance data structure.
        //
        mem_free(psTFTP);
    }
    
    //*****************************************************************************
    //
    // Sends a TFTP error packet.
    //
    //*****************************************************************************
    static void
    TFTPErrorSend(tTFTPConnection *psTFTP, tTFTPError eError)
    {
        unsigned long ulLength;
        unsigned char *pucData;
        struct pbuf *p;
    
        //
        // How big is this packet going to be?
        //
        ulLength = 5 + strlen(psTFTP->pcErrorString);
    
        //
        // Allocate a pbuf for this data packet.
        //
        p = pbuf_alloc(PBUF_TRANSPORT, ulLength, PBUF_RAM);
        if(!p)
        {
            return;
        }
    
        //
        // Get a pointer to the data packet.
        //
        pucData = (unsigned char *)p->payload;
    
        //
        // Fill in the packet.
        //
        pucData[0] = (TFTP_ERROR >> 8) & 0xff;
        pucData[1] = TFTP_ERROR & 0xff;
        pucData[2] = ((unsigned long)eError >> 8) & 0xff;
        pucData[3] = (unsigned long)eError & 0xff;
        memcpy(&pucData[4], psTFTP->pcErrorString, ulLength - 5);
    
        //
        // Send the data packet.
        //
        udp_send(psTFTP->pPCB, p);
    
        //
        // Free the pbuf.
        //
        pbuf_free(p);
    }
    
    //*****************************************************************************
    //
    // Sends a TFTP data packet.
    //
    //*****************************************************************************
    static void
    TFTPDataSend(tTFTPConnection *psTFTP)
    {
        unsigned long ulLength;
        unsigned char *pucData;
        tTFTPError eError;
        struct pbuf *p;
    
        //
        // Determine the number of bytes to place into this packet.
        //
        if(psTFTP->ulDataRemaining < (psTFTP->ulBlockNum * TFTP_BLOCK_SIZE))
        {
            ulLength = psTFTP->ulDataRemaining & (TFTP_BLOCK_SIZE - 1);
        }
        else
        {
            ulLength = TFTP_BLOCK_SIZE;
        }
    
        //
        // Allocate a pbuf for this data packet.
        //
        p = pbuf_alloc(PBUF_TRANSPORT, ulLength + 4, PBUF_RAM);
        if(!p)
        {
            return;
        }
    
        //
        // Get a pointer to the data packet.
        //
        pucData = (unsigned char *)p->payload;
    
        //
        // Fill in the packet header.
        //
        pucData[0] = (TFTP_DATA >> 8) & 0xff;
        pucData[1] = TFTP_DATA & 0xff;
        pucData[2] = (psTFTP->ulBlockNum >> 8) & 0xff;
        pucData[3] = psTFTP->ulBlockNum & 0xff;
    
        //
        // Ask the application to provide the data we need.
        //
        psTFTP->pucData = pucData + 4;
        psTFTP->ulDataLength = ulLength;
        eError = psTFTP->pfnGetData(psTFTP);
    
        //
        // Send the data packet or, if an error was reported, send an error.
        //
        if(eError == TFTP_OK)
        {
            udp_send(psTFTP->pPCB, p);
        }
        else
        {
            TFTPErrorSend(psTFTP, eError);
            TFTPClose(psTFTP);
        }
    
        //
        // Free the pbuf.
        //
        pbuf_free(p);
    }
    
    //*****************************************************************************
    //
    // Send an ACK packet back to the TFTP client.
    //
    //*****************************************************************************
    static void
    TFTPDataAck(tTFTPConnection *psTFTP)
    {
        unsigned char *pucData;
        struct pbuf *p;
    
        //
        // Allocate a pbuf for this data packet.
        //
        p = pbuf_alloc(PBUF_TRANSPORT, 4, PBUF_RAM);
        if(!p)
        {
            return;
        }
    
        //
        // Get a pointer to the data packet.
        //
        pucData = (unsigned char *)p->payload;
    
        //
        // Fill in the packet header.
        //
        pucData[0] = (TFTP_ACK >> 8) & 0xff;
        pucData[1] = TFTP_ACK & 0xff;
        pucData[2] = (psTFTP->ulBlockNum >> 8) & 0xff;
        pucData[3] = psTFTP->ulBlockNum & 0xff;
    
        //
        // Send the data packet.
        //
        udp_send(psTFTP->pPCB, p);
    
        //
        // Free the pbuf.
        //
        pbuf_free(p);
    }
    
    //*****************************************************************************
    //
    // Handles datagrams received from the TFTP data connection.
    //
    //*****************************************************************************
    static void
    TFTPDataRecv(void *arg, struct udp_pcb *upcb, struct pbuf *p,
                 struct ip_addr *addr, u16_t port)
    {
        unsigned char *pucData;
        unsigned long ulBlock;
        struct pbuf *pBuf;
        tTFTPConnection *psTFTP;
        tTFTPError eRetcode;
    
        //
        // Initialize our return code.
        //
        eRetcode = TFTP_ERR_NOT_DEFINED;
    
        //
        // Get a pointer to the connection instance data.
        //
        psTFTP = (tTFTPConnection *)arg;
    
        //
        // Get a pointer to the TFTP packet.
        //
        pucData = (unsigned char *)(p->payload);
    
        //
        // If this is an ACK packet, send back the next block to satisfy an
        // ongoing GET (read) request.
        //
        if((pucData[0] == ((TFTP_ACK >> 8) & 0xff)) &&
           (pucData[1] == (TFTP_ACK & 0xff)))
        {
            //
            // Extract the block number from the acknowledge.
            //
            ulBlock = (pucData[2] << 8) + pucData[3];
    
            //
            // See if there is more data to be sent.  Note that we need the "<="
            // here to ensure that we send back a zero length packet in the case
            // that the file is a multiple of 512 bytes (i.e. the last packet
            // of valid data was a full packet).
            //
            if((ulBlock * TFTP_BLOCK_SIZE) <= psTFTP->ulDataRemaining)
            {
                //
                // Send the next block of the file.
                //
                psTFTP->ulBlockNum = ulBlock + 1;
                TFTPDataSend(psTFTP);
            }
            else
            {
                //
                // The transfer is complete, so close the data connection.
                //
                TFTPClose(psTFTP);
                psTFTP = NULL;
            }
        }
        else
        {
            //
            // If this is a DATA packet, get the payload and write it to the
            // appropriate location in the serial flash.
            //
            if((pucData[0] == ((TFTP_DATA >> 8) & 0xff)) &&
               (pucData[1] == (TFTP_DATA & 0xff)))
            {
                //
                // This is a data packet.  Extract the block number from the packet
                // and set the offset within the block (stored in ulDataRemaining)
                // to zero.
                //
                psTFTP->ulBlockNum = (pucData[2] << 8) + pucData[3];
                psTFTP->ulDataRemaining = 0;
                psTFTP->ulDataLength = p->len - 4;
    
                //
                // Pass the data back to the application for handling.  Remember
                // that the data may be stored across several pbufs in the chain.
                // We can't assume it is in a contiguous block.
                //
                psTFTP->pucData = pucData + 4;
                pBuf = p;
    
                //
                // Keep writing until we run out of data.
                //
                while(pBuf)
                {
                	if(pBuf->next==NULL)
                	{
                		last_packet=1;
                	}
                    //
                    // Pass this block to the application.
                    //
                    eRetcode = psTFTP->pfnPutData(psTFTP);
    
                    //
                    // Was the data written successfully?
                    //
                    if(eRetcode != TFTP_OK)
                    {
                        //
                        // No - drop out.
                        //
                        break;
                    }
    
                    //
                    // Update the offset so that it is correct for the next pbuf
                    // in the chain.
                    //
                    psTFTP->ulDataRemaining += psTFTP->ulDataLength;
    
                    //
                    // Move to the next pbuf in the chain
                    //
                    pBuf = pBuf->next;
                    if(pBuf)
                    {
                        psTFTP->pucData = pBuf->payload;
                        psTFTP->ulDataLength = pBuf->len;
                    }
                }
    
                //
                // If we get here and there was an error reported, pass the error
                // back to the TFTP client.
                //
                if(psTFTP && (eRetcode != TFTP_OK))
                {
                    //
                    // Send the error code to the client.
                    //
                    TFTPErrorSend(psTFTP, eRetcode);
    
                    //
                    // Close the connection.
                    //
                    TFTPClose(psTFTP);
                    psTFTP = NULL;
                }
                else
                {
                    //
                    // Acknowledge this block.
                    //
                    TFTPDataAck(psTFTP);
    
                    //
                    // Is the transfer finished?
                    //
                    if(p->tot_len < (TFTP_BLOCK_SIZE + 4))
                    {
                        //
                        // We got a short packet so the transfer is complete.  Close
                        // the connection.
                        //
                        TFTPClose(psTFTP);
                        psTFTP = NULL;
                    }
                }
            }
            else
            {
                //
                // Is the client reporting an error?
                //
    
                if((pucData[0] == ((TFTP_ERROR >> 8) & 0xff)) &&
                (pucData[1] == (TFTP_ERROR & 0xff)))
                {
                    //
                    // Yes - we got an error so close the connection.
                    //
                    TFTPClose(psTFTP);
                    psTFTP = NULL;
                }
            }
        }
    
        //
        // Free the pbuf.
        //
        pbuf_free(p);
    }
    
    //*****************************************************************************
    //
    // Parses the request string to determine the transfer mode, netascii, octet or
    // mail, for this request.
    //
    //*****************************************************************************
    static tTFTPMode
    TFTPModeGet(unsigned char *pucRequest, unsigned long ulLen)
    {
        unsigned long ulLoop, ulMax;
    
        //
        // Look for the first zero after the start of the filename string (skipping
        // the first two bytes of the request packet).
        //
        for(ulLoop = 2; ulLoop < ulLen; ulLoop++)
        {
            if(pucRequest[ulLoop] == (unsigned char)0)
            {
                break;
            }
        }
    
        //
        // Skip past the zero.
        //
        ulLoop++;
    
        //
        // Did we run off the end of the string?
        //
        if(ulLoop >= ulLen)
        {
            //
            // Yes - this appears to be an invalid request.
            //
            return(TFTP_MODE_INVALID);
        }
    
        //
        // How much data do we have left to look for the mode string?
        //
        ulMax = ulLen - ulLoop;
    
        //
        // Now determine which of the modes this request asks for.  Is it ASCII?
        //
        if(!ustrnicmp("netascii", (char *)&pucRequest[ulLoop], ulMax))
        {
            //
            // This is an ASCII file transfer.
            //
            return(TFTP_MODE_NETASCII);
        }
    
        //
        // Binary transfer?
        //
        if(!ustrnicmp("octet", (char *)&pucRequest[ulLoop], ulMax))
        {
            //
            // This is a binary file transfer.
            //
            return(TFTP_MODE_OCTET);
        }
    
        //
        // All other strings are invalid or obsolete ("mail" for example).
        //
        return(TFTP_MODE_INVALID);
    }
    
    //*****************************************************************************
    //
    // Handles datagrams received on the TFTP server port.
    //
    //*****************************************************************************
    static void
    TFTPRecv(void *arg, struct udp_pcb *upcb, struct pbuf *p, struct ip_addr *addr,
             u16_t port)
    {
        unsigned char *pucData;
        tBoolean bGetRequest;
        tTFTPMode eMode;
        tTFTPError eRetcode;
        tTFTPConnection *psTFTP;
    
        //
        // Get a pointer to the TFTP packet.
        //
        pucData = (unsigned char *)(p->payload);
    
        //
        // Is this a read (GET) request?
        //
        if((pucData[0] == ((TFTP_RRQ >> 8) & 0xff)) &&
           (pucData[1] == (TFTP_RRQ & 0xff)))
        {
            //
            // Yes - remember that this is a GET request.
            //
            bGetRequest = true;
        }
    
        //
        // Is this a write (PUT) request?
        //
        if((pucData[0] == ((TFTP_WRQ >> 8) & 0xff)) &&
           (pucData[1] == (TFTP_WRQ & 0xff)))
        {
            //
            // Yes - remember that this is a PUT request.
            //
            bGetRequest = false;
        }
        else
        {
            //
            // The request is neither GET nor PUT so just ignore it.
            //
            pbuf_free(p);
            return;
        }
    
        //
        // What is the mode for this request?
        //
        eMode = TFTPModeGet(pucData, p->len);
    
        //
        // Was the transfer mode valid?
        //
        if(eMode != TFTP_MODE_INVALID)
        {
            //
            // The transfer mode is valid so allocate a new connection instance
            // and pass this to the client to have it tell us how to proceed.
            //
            psTFTP = (tTFTPConnection *)mem_malloc(sizeof(tTFTPConnection));
    
            //
            // If we can't allocate the connection instance, all we can do is
            // ignore the datagram.
            //
            if(!psTFTP)
            {
                pbuf_free(p);
                return;
            }
    
            //
            // Clear out the structure and initialize a few fields.
            //
            memset(psTFTP, 0, sizeof(tTFTPConnection));
            psTFTP->pcErrorString = "Unknown error";
    
            //
            // Yes - create the new UDP connection and set things up to
            // handle this request.
            //
            psTFTP->pPCB = udp_new();
            udp_recv(psTFTP->pPCB, TFTPDataRecv, psTFTP);
            udp_connect(psTFTP->pPCB, addr, port);
    
            //
            // Ask the application if it wants to proceed with this request.
            //
            eRetcode = g_pfnRequest(psTFTP, bGetRequest, (char *)(pucData + 2),
                                    eMode);
    
            //
            // Does it want to go on?
            //
            if(eRetcode == TFTP_OK)
            {
                //
                // Yes - what kind of request is this?
                //
                if(bGetRequest)
                {
                    //
                    // For a GET request, we send back the first block of data.
                    //
                    psTFTP->ulBlockNum = 1;
                    TFTPDataSend(psTFTP);
                }
                else
                {
                    //
                    // For a PUT request, we acknowledge the transfer which tells
                    // the TFTP client that it can start sending us data.
                    //
                    psTFTP->ulBlockNum = 0;
                    TFTPDataAck(psTFTP);
                }
            }
            else
            {
                //
                // The application indicated that there was an error.  Send the
                // error report and close the connection.
                //
                TFTPErrorSend(psTFTP, eRetcode);
                TFTPClose(psTFTP);
                psTFTP = NULL;
            }
        }
    
        //
        // Free the pbuf.
        //
        pbuf_free(p);
    }
    
    //*****************************************************************************
    //
    //! Initializes the TFTP server module.
    //!
    //! \param pfnRequest - A pointer to the function which the server will call
    //! whenever a new incoming TFTP request is received.  This function must
    //! determine whether the request can be handled and return a value telling the
    //! server whether to continue processing the request or ignore it.
    //!
    //! This function initializes the lwIP TFTP server and starts listening for
    //! incoming requests from clients.  It must be called after the network stack
    //! is initialized using a call to lwIPInit().
    //!
    //! \return None.
    //
    //*****************************************************************************
    void
    TFTPInit(tTFTPRequest pfnRequest)
    {
    	struct udp_pcb *UdpPcb;
        //
        // Remember the application's notification callback.
        //
        g_pfnRequest = pfnRequest;
    
        //
        // Start listening for incoming TFTP requests.
        //
        UdpPcb = udp_new();
        udp_bind(UdpPcb, IP_ADDR_ANY, TFTP_PORT);
        udp_recv(UdpPcb, TFTPRecv, NULL);
        while(!image_download);
    }