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.

NDK IPAdd Hook and http related function

Hi ,

I am using TI-RTOS 2.14.10 and NDK 2.24.03.35 with CCS 6.1.1 and TM4C1294XL board.

I am having a problem while using the ethernet and the NDK stack.


What I want to do is launch a task and test (inside the task) if ethernet is having access to internet. According to this I want to do some action if the ethernet is online or not.

If I launch this task within the hook (netIPAddrHook) that triggers when the board gets an IP from DHCP, the HTTP functions (like the ones used in the HTTP get examples) works and do the job.

But when I create and launch the task from outside the hook (like in the main function, at the beginning of the program), the functions fail to connect (HTTPCli_connect) with error -101 and errno = 0 and other function like EMAC_IsLinkUp fail too.


I can't manage to find a way to create a task at the launch of the system, launch it and then do something like this inside :

if ethernet is online then

do http stuff (get, etc.)

else

do other stuff with uart

endif


My problem is that I want the network task to be launched even if the ethernet link is not online at the beginning of the system, because I want to be able to use it later. 

What I understand is that a network task need to be the daughter of a netIpAddrHook function to be able to call network related functions.

Is there a way to do that ? All the post that I saw talk about launching network related task in the hook.

Thank you fr your help.


Regards,

Yannick

Please find attached simplified code that show what I want to do (Not working) and what is working but isn't best for me.

//WHAT I WANT TO DO 

int main(void)
{
   InitStuff();

   Task_Handle nettaskHandle;
   Task_Params nettaskParams;
   Error_Block neteb;

   //SPI Task Launching
   Task_Params_init(&nettaskParams);
   nettaskParams.stackSize = 2048;
   //define the stack size for the task
   nettaskParams.priority = 6;

   nettaskParams.instance->name = "Task_network_daemon";
   Error_init(&neteb);
   nettaskHandle = Task_create((Task_FuncPtr)TASK_NETWORK_DAEMON, &nettaskParams, &neteb);
   if (nettaskHandle == NULL) 
   {
   	System_printf("Task_network_daemon() creation failed!\n");
	BIOS_exit(0);
   }
}


// Hook called when ethernet link is on and have IP from dhcp server
void netIPAddrHook(unsigned int IPAddr, unsigned int IfIdx, unsigned int fAdd)
{
    // Create a Database task when the IP address is added
    if (fAdd)
    {
    	System_printf("FIRST IP OK\n");
    	flagIPOK = true;
    }
    else
    {
    	flagIPOK = false;
    }

}


void TASK_NETWORK_DAEMON()
{
   if(EMAC_isLinkUp(0) && flagIPOK)
   {
      // Ethernet OK
      doHttpStuff(); // http get, etc -> HTTP FAIL HERE
   }
   else
   {
      // Access to internet via uart wifi module
   }
}


// WHAT I HAVE AND IS WORKING

     void netIPAddrHook(unsigned int IPAddr, unsigned int IfIdx, unsigned int fAdd)
{
	static Task_Handle ethernetTaskHandle;
	Task_Params ethernetTaskParams;


    // Create a Database task when the IP address is added
    if (fAdd && !ethernetTaskHandle)
    {
    	System_printf("IP OK");

    	Error_Block ethernetTaskEb;
        Error_init(&ethernetTaskEb);

        Task_Params_init(&ethernetTaskParams);
        ethernetTaskParams.instance->name = "ETH_Task";
        // If there is errors, increase the size of the stack size
        ethernetTaskParams.stackSize = HTTPTASKSTACKSIZE;
        ethernetTaskParams.priority = 5;
        ethernetTaskHandle = Task_create((Task_FuncPtr)TASK_NETWORK_DAEMON_ETH, &ethernetTaskParams, &ethernetTaskEb);
        if (ethernetTaskHandle == NULL) {
            System_printf("netIPAddrHook: Failed to create HTTP Task\n", -1);

           // BIOS_exit(0);
        }

    }
}


void TASK_NETWORK_DAEMON_ETH()
{
   if(EMAC_isLinkUp(0))
   {
      DoHttpStuff(); // htt get ,etc EVERYTHING IS OK HERE
   }
}    

Don't take in account the above code as running, it's just samples from my code to show my problem.

 

  • Hi Yannick,

    You don't have to create the Task within that hook function. But, it's the easiest way to do it so that's why it's recommended that it be created there.

    Another way you could get this to work is by using a semaphore. Your task should call Semaphore_pend() at the very beginning, and wait forever until the semaphore is posted.

    Then, in the IP address hook, you could post the semaphore instead of creating the task there.

    This will cause your task to wait until the stack is ready for sockets code to run (the errors you see are because the task is running the sockets code too early).

    Steve
  • Thank you for your reply,

    I tried with the semaphore but it doesn't work. As you said I put the post in the hook and the pend into the task but the same errors appear the connect fails.

    The semaphore solution doesn't fit my needs because it blocks the task. All I want to do i have a task that can check if the ethernet is online (accessing the ethernet) with a if condition. And continue execution of the task knowing that the ethernet is online or not.

    Thanks for your help,

    Regards,

    Yannick

  • Yannick Riou said:
    I tried with the semaphore but it doesn't work. As you said I put the post in the hook and the pend into the task but the same errors appear the connect fails.

    Hmm.  This method is done a lot here internally, and should be effectively the same as creating the Task in the IP address hook.

    Yannick Riou said:
    The semaphore solution doesn't fit my needs because it blocks the task

    In both cases, the Task does not run until the IP address has been added (and hence the stack is up, ready and running).  In the original case, it won't run because it hasn't been created yet.  In the semaphore case, it won't run in the same fashion, because the semaphore won't post until the hook runs.  Isn't that what you want?  No matter which scenario, your task can't run any sockets code until the network stack has been brought up, and both of these methods ensure that this is the case.

    Or it could be that I'm misunderstanding what you're trying to do.  Maybe you don't care about the IP address being obtained or not, but just want to know when the Ethernet link is connected?

    But if so, you will still find that the sockets code fails.  Even if the link is up, the stack won't be ready, and you will again hit the failure cases.

    Yannick Riou said:
    he functions fail to connect (HTTPCli_connect) with error -101 and errno = 0 and other function like EMAC_IsLinkUp fail too

    that -101 error is "HTTPCli_ESOCKETFAIL" and means that the call to socket() in the HTTP client is failing.  Perphaps you are running out of memory?

    Actually, first let's check the value of the following in your *.cfg file:

    Global.autoOpenCloseFD

    Can you print that value out in your *.cfg file?  Is it set to true or false?

    If that is set to true, then it might be worthwhile to take a step back.  Can you try your same Task create scenarios, but do it with some basic socket operations?

    You could just make a new Task function that creates a socket with socket().  If it fails, try printing out errno.

    Steve

  • Hi Steve,


    Thank you for your answer. I was also surprised that the semaphore solution doesn't work, but to make sure I am doing it right, please find a project linked so you can check if it's ok. I am just getting IP from wtfismyip.com.

    Or it could be that I'm misunderstanding what you're trying to do.  Maybe you don't care about the IP address being obtained or not, but just want to know when the Ethernet link is connected?

    I want in a task to test if the ethernet link is having access to internet. According to this, I will either acess internet via physical link or anything else (wifi). That's why I didn't want the task to block. Because if the ethernet is not online, I want to do something else (continuing the execution of the task and access via wifi).


    It seems the semaphore can be a solution to my problem : I launch task in the main, then at the beginning of the task I wait for the semaphore (posted in the ip hook) for at least 10 seconds (a flag will be set along with the semaphore post in the IP hook), and then test the flag to know if the ethernet link is having access to ethernet. See the below code :

    #include <string.h>
    
    #include <xdc/runtime/Error.h>
    #include <xdc/runtime/System.h>
    #include <xdc/runtime/IHeap.h>
    #include <xdc/runtime/Memory.h>
    
     #include <errno.h>
    
    /* TI-RTOS Header files */
    #include <ti/drivers/GPIO.h>
    #include <ti/drivers/UART.h>
    #include <ti/net/http/httpcli.h>
    #include <ti/sysbios/BIOS.h>
    #include <ti/sysbios/knl/Task.h>
    #include <ti/sysbios/knl/Semaphore.h>
    
    #include <inc/hw_memmap.h>
    #include <inc/hw_types.h>
    #include <inc/hw_flash.h>
    
    #include <errno.h>
    
    #include <sys/socket.h>
    
    #include <string.h>
    
    /* Example/Board Header file */
    #include "Board.h"
    
    #define IP   "69.30.217.90"
    #define PORT  80
    
    #define HOSTNAME "wtfismyip.com"
    #define REQUEST_URI "/text"
    
    #define HTTPTASKSTACKSIZE 8192
    
    /*
     *  ======== httpTask ========
     *  Makes an HTTP GET request
     */
    
    int flag = 0;
    
    Semaphore_Handle sem;
    Semaphore_Params sem_params;
    Error_Block ebSem;
    
    Task_Handle taskHandle;
    Task_Params taskParams;
    
    Error_Block eb;
    
    Void httpTask(UArg arg0, UArg arg1)
    {
    	Semaphore_pend(sem,10000000); // wait 10 s for the stack to initialize and get ip
    
    	// Semaphore has been posted or timeout triggered
    	if(flag)
    	{
    		// Ethernet is only, we gotIP
    	}
    	else
    	{
    		// Ethernet is offline, the pend got timeout
    	}
    
    	int i = 0;
        bool moreFlag = false;
        char db_write_ok[20]={0x00,0x00,0x00,0x00,0x00,0x00,'\0'};
        int ret;
        char header[100]={};
        struct sockaddr_in addr;
    
        HTTPCli_Struct cli;
        HTTPCli_Field fields[2] = {
            { HTTPStd_FIELD_NAME_HOST, HOSTNAME },
            { NULL, NULL }
        };
    
        // Set port and IP address of the database
        	addr.sin_family = AF_INET;
        	addr.sin_port = htons(PORT);
        	inet_pton(AF_INET, IP, &addr.sin_addr);
        	while(1)
        	{
    
        	HTTPCli_construct(&cli);
    
        	HTTPCli_setRequestFields(&cli, fields);
    
        	ret = HTTPCli_connect(&cli, (struct sockaddr *)&addr, 0, NULL);
        	if (ret < 0)
        	{
        		System_printf("httpTask: connect failed : %d\n", ret);System_flush();
    
    
        		HTTPCli_disconnect(&cli);
        		HTTPCli_destruct(&cli);
        		continue;
        	}
    
        	ret = HTTPCli_sendRequest(&cli, HTTPStd_GET, REQUEST_URI, false);
        	if (ret < 0)
        	{
        		System_printf("httpTask: send failed : %d\n", ret);System_flush();
    
        		HTTPCli_disconnect(&cli);
        		HTTPCli_destruct(&cli);
        		continue;
        	}
    
        	ret = HTTPCli_getResponseStatus(&cli);
        	if (ret != HTTPStd_OK)
        	{
        		System_printf("httpTask: cannot get status : %d\n", ret);System_flush();
    
        		HTTPCli_disconnect(&cli);
        		HTTPCli_destruct(&cli);
        		continue;
        	}
    
        	ret = HTTPCli_getResponseField(&cli, header, sizeof(header), &moreFlag);
        	if (ret != HTTPCli_FIELD_ID_END)
        	{
        		System_printf("httpTask: response field processing failed : %d\n", ret);System_flush();
    
        		HTTPCli_disconnect(&cli);
        		HTTPCli_destruct(&cli);
        		continue;
        	}
    
        	ret = HTTPCli_readResponseBody(&cli, db_write_ok, sizeof(db_write_ok), &moreFlag);
        	if (ret < 0)
        	{
        		System_printf("httpTask: response body processing failed : %d\n", ret);System_flush();
    
        		HTTPCli_disconnect(&cli);
        		HTTPCli_destruct(&cli);
        		continue;
        	}
    
        	System_printf("Rcv : %s\n",db_write_ok);System_flush();
    
         	HTTPCli_disconnect(&cli);
            HTTPCli_destruct(&cli);
    
            Task_sleep(100);
        	}
    
    }
    
    /*
     *  ======== netIPAddrHook ========
     *  This function is called when IP Addr is added/deleted
     */
    void netIPAddrHook(unsigned int IPAddr, unsigned int IfIdx, unsigned int fAdd)
    {
        /* Create a HTTP task when the IP address is added */
        if (fAdd) {
        	System_printf("IP ADD\r\n");System_flush();
            flag = 1;
        	Semaphore_post(sem);
        	
        }
        else
        {
        	System_printf("IP Del\r\n");System_flush();
        	flag = 0;
        }
    }
    
    
    /*
     *  ======== main ========
     */
    int main(void)
    {
    	Semaphore_Params_init(&sem_params);
    	sem_params.mode = Semaphore_Mode_BINARY;
    	// Create seamphore as blocked when created
    	sem = Semaphore_create(0, &sem_params, &ebSem);
    
          /* Call board init functions */
        Board_initGeneral();
        Board_initGPIO();
        Board_initUART();
        Board_initEMAC();
    
        // Turn on user LED
        GPIO_write(Board_LED0, Board_LED_ON);
    
        System_printf("Starting the HTTP GET example\nSystem provider is set to SysMin. Halt the target to view any SysMin contents in ROV.\n");
        //SysMin will only print to the console when you call flush or exit
        System_flush();
    
       Error_init(&eb);
    
       Task_Params_init(&taskParams);
    
       // If there is errors, increase the size of the stack size
       taskParams.stackSize = HTTPTASKSTACKSIZE;
       taskParams.priority = 6;
       taskHandle = Task_create((Task_FuncPtr)httpTask, &taskParams, &eb);
       if (taskHandle == NULL) {
    	System_printf("httpTask: Failed to create HTTP Task\n", -1);
    
    	BIOS_exit(0);
       }
    
        // Start BIOS
        BIOS_start();
    
        return (0);
    }

    But this solution will only work if the semaphore solution is working.

    I checked and the Global.autoOpenCloseFD flag is set to true.

    About the semaphore solution not working, I am wondering if it can be a bad priority set problem for stack and the other task.

    2337.testHttp_get.rar

  • Yannick,

    Is your board ever getting an IP address? Do you see one being printed out on the console?

    Also, what's your network topology? Are you hooked up to a router?

    Can you try plugging into a router (a home router, that would give you a local 192.168.1.* IP address)? Do you get an IP address there?

    In Wireshark, do you see DHCP discover packets going out on the wire?

    Sorry, I didn't have time to dive deeper into this today. I will do that tomorrow. In the meantime, providing the above further info will help me.

    Steve
  • Steven,


    Yes the board is getting an ip address but ry to access socket before DHCP is on status 0017 even with the semaphore solution.

    Yes the board is hooked up to a router with a DHCP, so no problem to get an ip address. I already tried to plug it in a router and get the sae result, I get an ipaddress but socket failed even wth two solution above.

    Regards,

    Yannick

  • Yannick,

    I was able to build your app and have a look.  The semaphore solution is working as expected - the http task is blocked until the semaphore posts in the IP address hook.  So, we know that the NDK stack is up and running by the time the socket code runs.

    The reason you are getting that failure is due to creating the Task in main(), in combination with setting Global.autoOpenCloseFD = true.

    From the NDK API guide:

    "Note that the Global.autoOpenCloseFD parameter is only supported for dynamically-created Tasks

    created from within a Task context (that is, from within another running Task function). Tasks created

    statically in the configuration or dynamically in main() or a Hwi or Swi thread do not have support this

    feature."

    If you really want to create your task in main, here is an easy workaround for your problem - just add the calls to open/close the file descriptor table into the beginning and end of your task function.

    For example:

    Void httpTask(UArg arg0, UArg arg1)
    {

        Semaphore_pend(sem, 10000); // wait 10 s for the stack to initialize and get ip

        fdOpenSession(Task_self());

        <the rest of your code ...>

        fdCloseSession(Task_self());
    }

    I tried this and was able to get past the -101 error.

    Steve

  • Hi Steve,

    Thank you for your answer, the solution is working like a charm.

    But I want to understand the reason why I need to do this. What does the Global.autoOpenCloseFD variable is for ? why do I need to add fdOpenSession ?

    My understanding is that if I create (as you said) the network task in main, the socket is not automatically opened, so I need to open it manually wit fdOpenSession function, is that right ?


    Thank you again for your time !


    Regards,

    Yannick

  • Hi Yannick,

    The NDK sockets require a file descriptor environment to be set up.  In result, it is necessary to call fdOpenSession() prior to creating a socket and running any sockets code.  (As well, one must call fdCloseSession() once socket operations are complete).  These calls are typically done at the very beginning of a Task function's code, and at the very end, respectively.

    Since having those calls to fdOpen/CloseSession is not standard from a BSD sockets perspective, I made it a feature to have those functions called automatically for you - that's what the Global.autoOpenCloseFD = true configuration setting does.  However, it's not without its limitations, as you've discovered.

    So, while you don't NEED to use Global.autoOpenCloseFD = true, if you don't, then you must have the fdOpen/CloseSession calls in your sockets code.  The advantage of the latter method is that you can create the Task however you want.

    You can find more details on these topics in the following places:

    1. NDK User's Guide (spru523*.pdf):
      1. 3.3.1 Initializing the File Descriptor Table
      2. 3.4 Example Code
      3. 3.5.1 Troubleshooting Common Problems

    2. NDK API Guide (spru524*.pdf):
      1. 3.1.2.2 Auto-Initializing the File Descriptor Environment
      2. 3.1 File Descriptor Environment
      3. 3.1.2.2 Auto-Initializing the File Descriptor Environment (in particular regarding Global.autoOpenCloseFD)

    Steve