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.

RTOS/SW-EK-TM4C1294XL: Change Static IP at Runtime

Part Number: SW-EK-TM4C1294XL

Tool/software: TI-RTOS

How can I change the static IP?

I gathered this code from other posts:

HANDLE hCfg;
CI_IPNET newIP;
newIP.NetType = 0;
newIP.IPAddr = inet_addr("192.168.0.100");
newIP.IPMask = inet_addr("255.255.255.0");
newIP.hBind = 0;

CfgGetEntry(0, CFGTAG_IPNET, 1, 1, &hCfg);
CfgRemoveEntry(0, hCfg);
int resp = CfgAddEntry(0, CFGTAG_IPNET, 1, 0, sizeof(CI_IPNET), &newIP, 0);

This works, but I seem to be deleting a configuration handle that had many settings applied to it in the generated code from XGCONF:

ti_ndk_config_ip_init(hCfg);

 /* add the Tcp module configuration settings. */
ti_ndk_config_tcp_init(hCfg);

/* add the configuration settings for NDK low priority tasks stack size. */
rc = 1280;
CfgAddEntry(hCfg, CFGTAG_OS, CFGITEM_OS_TASKSTKLOW,
             CFG_ADDMODE_UNIQUE, sizeof(uint), (UINT8 *)&rc, 0 );

This generated initialization code is setting TCP and priority settings by using the same handle used to set the IP. Am I deleting all these settings in the IP change code?

Should I be giving CfgAddEntry() a configuration handle? The API reference claims giving 0 for the handle changes the default configuration. Im not sure if that should be changed.

  • Hi Peter,

    An engineer has been assigned to this thread. You should get a response within 24 hours.

    Todd
  • Peter Borenstein said:
    Should I be giving CfgAddEntry() a configuration handle? The API reference claims giving 0 for the handle changes the default configuration. Im not sure if that should be changed.

    Passing zero should be fine. As you mention, this will use the default configuration. You may want to do something different if you're using multiple configurations (not the typical use case afaik).

    Peter Borenstein said:
    This works, but I seem to be deleting a configuration handle that had many settings applied to it in the generated code from XGCONF

    The following parts of your code in bold might be problematic:

    HANDLE hCfg;
    CI_IPNET newIP;
    newIP.NetType = 0;
    newIP.IPAddr = inet_addr("192.168.0.100");
    newIP.IPMask = inet_addr("255.255.255.0");
    newIP.hBind = 0;

    CfgGetEntry(0, CFGTAG_IPNET, 1, 1, &hCfg);
    CfgRemoveEntry(0, hCfg);
    int resp = CfgAddEntry(0, CFGTAG_IPNET, 1, 0, sizeof(CI_IPNET), &newIP, 0);

    I'm wondering if this is the exact code you have (verbatim) or if possibly you have something like the following?

    HANDLE hCfg = CfgGetDefault();

    The above code (and variable name) is how the default configuration handle is obtained. Since you're using that variable name (hCfg) that's typically used to store the handle to the configuration (not a configuration entry), I'm wondering if you're maybe accidentally passing the configuration handle in your call:

    CfgGetEntry(0, CFGTAG_IPNET, 1, 1, &hCfg);

    Which would overwrite the configuration. Is this possible?

    If not, I would recommend changing the variable you're using to be called something different than "hCfg", just to avoid confusion with other example code you're going to see in the forums.

    Peter Borenstein said:
    This generated initialization code is setting TCP and priority settings by using the same handle used to set the IP. Am I deleting all these settings in the IP change code?


    None of those settings should be getting deleted. You can add to the configuration during runtime. The existing configuration will be the same except for the setting you are adding or changing. The change will take effect immediately.

    Steve

  • Hi Steven,

    Here's a screen shot that includes a successful return value to prove the code "works":

    However, I still don't understand what is happening here. Why is 0 an acceptable handle to pass to CfgGetEntry() as the first argument?

    Edit: The default and the handle used during initialization are the same. Passing 0 is that same as passing the valid config handle that did the initialization.

  • I think I am mixing up config handles with config entry handles.

    Stepping through the init code relived that the handle used to initialize everything is  the same value returned by CfgGetDefault(); 0x20005C2C in the screenshot.

    "Passing zero should be fine. As you mention, this will use the default configuration."

    I found proof of your statement in NC_Start(). This function has the line CfgSetDefault( hCfg );. So we are guaranteed to have the auto-generated init code as default settings!

    But for a through understanding, how would you get the configuration handle assuming the handle was not set as default?

    I noticed this config handle is being passed to the network stack init hook, which could stash the handle somewhere safe. This seems to be a contradiction of documentation. NDK User Guide (SPRU523J) says "Hook functions must be defined using the following format: Void functionName(Void)". I think I would've learned your stack faster if this guide never existed...

    Todd already proved this was false for the IP address hook: e2e.ti.com/.../639403

    Slightly unrelated, I found a power point by Todd about how to create a HTTP server and it was super helpful. Kudos.

  • Hi Peter,

    Peter Borenstein said:
    But for a through understanding, how would you get the configuration handle assuming the handle was not set as default?

    You can save a configuration and load its settings again at a later time.

    Please find the attached example and below pseudo code. I tried to summarize the example in the pseudo code below:

    1. create a new configuration:
      1. hCfg = CfgNew()
    2. apply some settings:
      1. CfgAddEntry(hCfg, ...);
    3. Get the size needed to save the configuration's settings
      1.  int   MainConfigSize  = 0;
      2. CfgSave( hCfg, &MainConfigSize, 0 ); // passing 0 for pData will cause CfgSave to calculate the size needed
    4. Save the applied settings into MainConfig[]
      1. unsigned char MainConfig[512]; // used to store the config settings
      2. CfgSave( hCfg, &MainConfigSize, 0 );
    5. Optionally unset this config from default:
      1. CfgExecute(hCfg, 0)
    6. Free the configuration
      1.  CfgFree(hCfg);
    7. Create a second configuration
      1. hCfg2 - CfgNew()
    8. Load the settings from the previous configuration into the new one
      1. CfgLoad( hCfg2, MainConfigSize, MainConfig );
    9. Optionally set the new config to default:
      CfgExecute(hCfg2, 1)

    Steve

    Note: the attached source file is from a much older NDK and may not work "as is" (e.g. you probably wouldn't be able to just drop it into your existing app):

    /*
     *  Copyright 2007 by Texas Instruments Incorporated.
     *  All rights reserved. Property of Texas Instruments Incorporated.
     *  Restricted rights to use, duplicate or disclose this code are
     *  granted through contract.
     *
     *  @(#) TCP/IP_Network_Developers_Kit 1.93.00.09 08-16-2007 (ndk-c09)
     */
    //--------------------------------------------------------------------------
    // IP Stack Test Program
    //--------------------------------------------------------------------------
    // CfgDemo.c
    //
    // TCP/IP Client Configuration Demo
    //
    // Author: Michael A. Denio
    // Copyright 1999, 2000 by Texas Instruments Inc.
    //-------------------------------------------------------------------------
    #include <stdio.h>
    #include <netmain.h>
    #include <_stack.h>
    #include <common/console/console.h>
    #include <common/servers/servers.h>
    #include "cfgdemo.h"
    #include <c62.h> 
    
    //---------------------------------------------------------------------------
    // Version String
    //
    char *VerStr = "\nTCP/IP Stack Client Configuration Demo\n";
    
    // Simulate a non-volatile configuration
    //=====================================
    UINT8 MainConfig[512];
    int   MainConfigSize   = 0;
    int   MainConfigValid  = 0;
    //=====================================
    
    // Our simulated boot entry point
    static void   NetBoot();
    
    // Routine to get IP address from Ping packet
    static void GetIP( uint IfIdx );
    
    // NC Callback Functions
    static void   NetworkOpen();
    static void   NetworkClose();
    static void   NetworkIPAddr( IPN IPAddr, uint IfIdx, uint fAdd );
    
    // Fun reporting function
    void   ServiceReport( uint Item, uint Status, uint Report, HANDLE hCfgEntry );
    
    
    //---------------------------------------------------------------------
    // Main Entry Point
    //---------------------------------------------------------------------
    int main()
    {}
    
    // This callback function is called by the evaluation stack 5 min before the
    // evaluation period times out.
    void evalCallBack() 
    {
        printf("The Stack is going to shutdown in 5 min\n");
    }
    
    //
    // Main Thread
    //
    int StackTest()
    {
        HANDLE   hCfg;
        char     *hn = "tidsp";
        int      rc;
        uint     tmp;
    
        //
        // THIS MUST BE THE ABSOLUTE FIRST THING DONE IN AN APPLICATION!!
        //
        rc = NC_SystemOpen( NC_PRIORITY_LOW, NC_OPMODE_INTERRUPT );
        if( rc )
        {
            printf("NC_SystemOpen Failed (%d)\n",rc);
            for(;;);
        }
    
        // Print a banner
        printf(VerStr);
    
        // Enable the EDMA interrupt - since the EDMA interrupt
        // is configurable, this one line of code has no home.
        //
        // NOTE: This code is not required for DM642, nor any
        //       driver environment that does not use EMDA sharing.
        //       However, it doesn't hurt to turn it on.
        C62_enableIER( 1<<8 );
    
        //
        // We'll build a phony "default" configuration in this routine.
        // It will simulate having a default configuration stored in
        // non-volatile storage
        //
    
        // Create a new configuration
        hCfg = CfgNew();
        if( !hCfg )
        {
            printf("Unable to open configuration\n");
            goto main_exit;
        }
    
        // The evaluation version of TCP/IP Stack restricts usage of stack
        // to maximum of 24 Hours. If application wants to be notified 5 min
        // before the timeout, it can register a callback function by using   
        // the following configuration code section.
        {
            void (*pFxn)() = &evalCallBack;
            CfgAddEntry( hCfg, CFGTAG_SYSINFO, CFGITEM_SYSINFO_EVALCALLBACK, 0,
                        sizeof(void(*)()), (UINT8*) &pFxn, 0 );
        }
    
        // Add our global hostname (to be claimed in all connected domains)
        CfgAddEntry( hCfg, CFGTAG_SYSINFO, CFGITEM_DHCP_HOSTNAME, 0,
                     strlen(hn), (UINT8 *)hn, 0 );
    
        // Specify TELNET service
        {
            CI_SERVICE_TELNET telnet;
    
            bzero( &telnet, sizeof(telnet) );
            telnet.cisargs.IPAddr = INADDR_ANY;
            telnet.cisargs.pCbSrv = &ServiceReport;
            telnet.param.MaxCon   = 2;
            telnet.param.Callback = &ConsoleOpen;
            CfgAddEntry( hCfg, CFGTAG_SERVICE, CFGITEM_SERVICE_TELNET, 0,
                         sizeof(telnet), (UINT8 *)&telnet, 0 );
        }
    
        // Create RAM based WEB files for HTTP
        AddWebFiles();
    
        // Specify HTTP service
        {
            CI_SERVICE_HTTP http;
    
            bzero( &http, sizeof(http) );
            http.cisargs.IPAddr = INADDR_ANY;
            http.cisargs.pCbSrv = &ServiceReport;
            CfgAddEntry( hCfg, CFGTAG_SERVICE, CFGITEM_SERVICE_HTTP, 0,
                         sizeof(http), (UINT8 *)&http, 0 );
        }
    
        // We don't want to see debug messages less than WARNINGS
        tmp = DBG_WARN;
        CfgAddEntry( hCfg, CFGTAG_OS, CFGITEM_OS_DBGPRINTLEVEL,
                     CFG_ADDMODE_UNIQUE, sizeof(uint), (UINT8 *)&tmp, 0 );
    
        // Save the configuration to a linear buffer
        MainConfigSize = 0;
        CfgSave( hCfg, &MainConfigSize, 0 );
        printf("%d bytes required for save\n",MainConfigSize);
        if( MainConfigSize > sizeof( MainConfig ) )
            printf("FATAL: Config buffer too small\n");
        else
        {
            CfgSave( hCfg, &MainConfigSize, MainConfig );
            CfgFree( hCfg );
    
            // Now call what would really be the "boot" function
            NetBoot();
        }
    
        // Free the WEB files
        RemoveWebFiles();
    
        // Close the OS
    main_exit:
        NC_SystemClose();
        return(0);
    }
    
    
    //---------------------------------------------------------------------
    // Simulated Boot Entry Point
    //---------------------------------------------------------------------
    static void NetBoot()
    {
        HANDLE   hCfg;
        int      rc;
    
        //
        // Initialize the OS
        //
        // Note - we normally call NC_SystemOpen() here, but its already
        // been done in this simulation.
        //
    
        //
        // Boot the system using stored configuration
        //
        // We keep booting until the function returns 0. This allows
        // us to have a "reboot" command.
        //
        do
        {
            // Create a new configuration
            hCfg = CfgNew();
            if( !hCfg )
            {
                printf("Unable to open configuration\n");
                break;
            }
    
            // Load the configuration
            CfgLoad( hCfg, MainConfigSize, MainConfig );
    
            // Start the stack
            rc = NC_NetStart( hCfg, NetworkOpen, NetworkClose, NetworkIPAddr );
    
            // Delete Configuration
            CfgFree( hCfg );
        } while( rc > 0 );
    
        //
        // Close the OS
        //
        // Note - we normally call NC_SystemClose() here, but its done for
        // us in this simulation.
        //
    }
    
    //
    // System Task Code
    //
    static HANDLE hGetIP=0,hEcho=0,hData=0,hNull=0,hOob=0;
    
    #ifdef _INCLUDE_IPv6_CODE
    static HANDLE hEcho6=0;
    #endif
    
    //
    // NetworkOpen
    //
    // This function is called after the configuration has booted
    //
    static void NetworkOpen()
    {
        // If we don't have any kind of IP in our configuration, we
        // do a "config by ping" function. The calling parameter is the
        // interface index to configure.
        if( !MainConfigValid )
            hGetIP = TaskCreate( GetIP, "GetIP", OS_TASKPRINORM, 0x1000, 1, 0, 0 );
    
        // Create our local servers
        hEcho = TaskCreate( echosrv, "EchoSrv", OS_TASKPRINORM, 0x1400, 0, 0, 0 );
        hData = TaskCreate( datasrv, "DataSrv", OS_TASKPRINORM, 0x1400, 0, 0, 0 );
        hNull = TaskCreate( nullsrv, "NullSrv", OS_TASKPRINORM, 0x1400, 0, 0, 0 );
        hOob  = TaskCreate( oobsrv,  "OobSrv", OS_TASKPRINORM, 0x1000, 0, 0, 0 );
    
    #ifdef _INCLUDE_IPv6_CODE
        hEcho6 = TaskCreate( v6echosrv, "V6EchoSrv", OS_TASKPRINORM, 0x1400, 0, 0, 0 );
    #endif
    }
    
    //
    // NetworkClose
    //
    // This function is called when the network is shutting down,
    // or when it no longer has any IP addresses assigned to it.
    //
    static void NetworkClose()
    {
        // Kill the GetIP task only if it did not complete
        if( hGetIP )
            fdCloseSession( hGetIP );
        fdCloseSession( hOob );
        fdCloseSession( hNull );
        fdCloseSession( hData );
        fdCloseSession( hEcho );
    
    #ifdef _INCLUDE_IPv6_CODE
        fdCloseSession (hEcho6);
    #endif
    
        // Kill any active console
        ConsoleClose();
    
        // If we opened NETCTRL as NC_PRIORITY_HIGH, we can't
        // kill our task threads until we've given them the
        // opportunity to shutdown. We do this by manually
        // setting our task priority to NC_PRIORITY_LOW.
        TaskSetPri( TaskSelf(), NC_PRIORITY_LOW );
    
        if( hGetIP )
            TaskDestroy( hGetIP );
        TaskDestroy( hOob );
        TaskDestroy( hNull );
        TaskDestroy( hData );
        TaskDestroy( hEcho );
    }
    
    //
    // NetworkIPAddr
    //
    // This function is called whenever an IP address binding is
    // added or removed from the system.
    //
    static void NetworkIPAddr( IPN IPAddr, uint IfIdx, uint fAdd )
    {
        IPN IPTmp;
    
        if( fAdd )
            printf("Network Added: ");
        else
            printf("Network Removed: ");
    
        // Print a message
        IPTmp = ntohl( IPAddr );
        printf("If-%d:%d.%d.%d.%d\n", IfIdx,
                (UINT8)(IPTmp>>24)&0xFF, (UINT8)(IPTmp>>16)&0xFF,
                (UINT8)(IPTmp>>8)&0xFF, (UINT8)IPTmp&0xFF );
    }
    
    //
    // Service Status Reports
    //
    // Here's a quick example of using service status updates
    //
    static char *TaskName[]  = { "Telnet","HTTP","NAT","DHCPS","DHCPC","DNS" };
    static char *ReportStr[] = { "","Running","Updated","Complete","Fault" };
    static char *StatusStr[] = { "Disabled","Waiting","IPTerm","Failed","Enabled" };
    void ServiceReport( uint Item, uint Status, uint Report, HANDLE h )
    {
        printf( "Service Status: %-9s: %-9s: %-9s: %03d\n",
                TaskName[Item-1], StatusStr[Status],
                ReportStr[Report/256], Report&0xFF );
    }
    
    
    //
    // GetIP()
    //
    // Use ICMP ECHO request to get IP address
    //
    #define MAXPACKET 1000 // max packet size
    static void GetIP( uint IfIdx )
    {
        SOCKET   s;
        struct   sockaddr_in from;
        char     *pBuf = 0;
        int      cc, fromlen;
        IPN      IPMe;
        ICMPHDR  *pIcHdr;
        IPHDR    *pIpHdr;
        int      IPHdrLen;
        CI_IPNET NA;
    
        // Allocate FDT
        fdOpenSession( TaskSelf() );
    
        // Create the ICMP Socket
        s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
        if( s == INVALID_SOCKET )
            goto abort;
    
        // Initialize the "from" address
        bzero( &from, sizeof(struct sockaddr_in));
        from.sin_family     = AF_INET;
        from.sin_len        = sizeof( from );
    
        // Allocate a working buffer
        if( !(pBuf = mmBulkAlloc( MAXPACKET )) )
            goto abort;
    
        printf("GetIP Ready\n");
    
        while(1)
        {
            // Receive ICMP packet data
            fromlen = sizeof(from);
            cc = (int)recvfrom( s, pBuf, MAXPACKET, 0, (PSA)&from, &fromlen );
            if( cc < 0 )
                goto abort;
    
            // Get header pointers
            pIpHdr   = (IPHDR *)pBuf;
            IPHdrLen = (pIpHdr->VerLen & 0xF) * 4;
            pIcHdr   = (ICMPHDR *)(pBuf+IPHdrLen);
    
            // Verify the ICMP type is ECHO request
            if( pIcHdr->Type != ICMP_ECHO )
                continue;
    
            // Use the destination address as our IP address
            IPMe = RdNet32( &pIpHdr->IPDst );
    
            // Add the IP to the configuration
            // Assume the netmask is 255.255.254.0
            bzero( &NA, sizeof(NA) );
            NA.IPAddr = IPMe;
            NA.IPMask = inet_addr("255.255.254.0");
            strcpy( NA.Domain, "default.net" );
            if( CfgAddEntry( 0, CFGTAG_IPNET, IfIdx, 0,
                             sizeof(CI_IPNET), (UINT8 *)&NA, 0 ) >= 0 )
                break;
        }
    
    abort:
        hGetIP = 0;
    
        printf("GetIP Closing\n");
    
        if( pBuf )
            mmBulkFree( pBuf );
        if( s != INVALID_SOCKET )
            fdClose( s );
    
        fdCloseSession( TaskSelf() );
        TaskExit();
    }