This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

nrf9160 bsd socket recv() not blocking

My simple BSD socket example does little more than send/recv a message to a server over LTE (it's adapted from Rallare's tcp sample). It works, however the recv() call does not block. I'm currently using the v0.3.0 release (and associated tags per the release notes). I found a note in the nrfxlib/bsdlib/CHANGELOG.rst that a fix is available in bsdlib 0.2.4. I have tried various combinations of using post-0.3.0 code and so far every attempt has failed to build for one reason or another. The samples don't build either, so I don't think it's my code.

So my question is what do I need to do to use the newer bsdlib in nrfxlib?

Parents Reply Children
  • I was so hopeful the patch would work, but no such luck. The patch didn't apply (I don't have west.yml). So I grabbed bsd_os.c directly and bsd_os_timedwait() was declared differently (and it's in nrfxlib). That was easy to change but then I ran into DT_NORDIC_NRF_UARTE_1_{TX,RX,CTX,RTS}_PIN undeclared errors.

    I've got a work around that gets me over this hurdle (looping+sleeping) so on to the next thing for now...

    ... which is calling bind() [which I think is ultimately calling nrf_bind()] to bind a UDP socket to a port does not work. Try it with the sequence:
    create socket(AF_INET, SOCK_DGRAM, 0)
    setup sockaddr_in appropriately (using IPv4, btw)
    call bind()
    call sendto()
    on the receiving side, the source port is not what I called with bind()

  • Yes, you are right. Sorry for this. The bsd_os_timedwait() has changed to take in a pointer, which was done early in april when updating the bsdlib:

    https://github.com/NordicPlayground/nrfxlib/commit/dfcef2f5c7f4aec0b7265b87c09ae136c5d5e7f3

    You will need to have nrfxlib at 1st of april (or later), and for the UARTE1 tracing, you can omit those for testing (delete code related to define CONFIG_BSD_LIBRARY_TRACE_ENABLED)

     

    I tested at_client (and a ntp example, without the calls to "blocking_*" functions) with the following repos:

    nrf -> git checkout v0.3.0

    nrfxlib -> git checkout master

    zephyr -> git checkout v1.13.99-ncs2

     

    And this is my modified bsd_os.c:

     

    /*
     * Copyright (c) 2018 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic
     */
    
    #include <string.h>
    #include <bsd_os.h>
    #include <bsd.h>
    #include <bsd_limits.h>
    #include <bsd_platform.h>
    #include <nrf.h>
    
    #include <nrf_errno.h>
    
    #include <init.h>
    #include <zephyr.h>
    #include <zephyr/types.h>
    #include <errno.h>
    
    #ifdef CONFIG_BSD_LIBRARY_TRACE_ENABLED
    #include <nrfx_uarte.h>
    #endif
    
    #ifndef ENOKEY
    #define ENOKEY 2001
    #endif
    
    #ifndef EKEYEXPIRED
    #define EKEYEXPIRED 2002
    #endif
    
    #ifndef EKEYREVOKED
    #define EKEYREVOKED 2003
    #endif
    
    #ifndef EKEYREJECTED
    #define EKEYREJECTED 2004
    #endif
    
    #define UNUSED_FLAGS 0
    
    /* Handle modem traces from IRQ context with lower priority. */
    #define TRACE_IRQ EGU2_IRQn
    #define TRACE_IRQ_PRIORITY 6
    
    void IPC_IRQHandler(void);
    
    #define THREAD_MONITOR_ENTRIES 10
    
    struct sleeping_thread {
    	sys_snode_t node;
    	struct k_sem sem;
    };
    
    /* An array of thread ID and RPC counter pairs, used to avoid race conditions.
     * It allows to identify whether it is safe to put the thread to sleep or not.
     */
    static struct thread_monitor_entry {
    	k_tid_t id; /* Thread ID. */
    	int cnt; /* Last RPC event count. */
    } thread_event_monitor[THREAD_MONITOR_ENTRIES];
    
    /* A list of threads that are sleeping and should be woken up on next event. */
    static sys_slist_t sleeping_threads;
    
    /* RPC event counter, incremented on each RPC event. */
    static atomic_t rpc_event_cnt;
    
    /* Get thread monitor structure assigned to a specific thread id, with a RPC
     * counter value at which bsdlib last checked the 'readiness' of a thread
     */
    static struct thread_monitor_entry *thread_monitor_entry_get(k_tid_t id)
    {
    	struct thread_monitor_entry *entry = thread_event_monitor;
    	struct thread_monitor_entry *new_entry = thread_event_monitor;
    	int entry_age, oldest_entry_age = 0;
    
    	for ( ; PART_OF_ARRAY(thread_event_monitor, entry); entry++) {
    		if (entry->id == id) {
    			return entry;
    		} else if (entry->id == 0) {
    			/* Uninitialized field. */
    			new_entry = entry;
    			break;
    		}
    
    		/* Identify oldest entry. */
    		entry_age = rpc_event_cnt - entry->cnt;
    		if (entry_age > oldest_entry_age) {
    			oldest_entry_age = entry_age;
    			new_entry = entry;
    		}
    	}
    
    	new_entry->id = id;
    	new_entry->cnt = rpc_event_cnt - 1;
    
    	return new_entry;
    }
    
    /* Update thread monitor entry RPC counter. */
    static void thread_monitor_entry_update(struct thread_monitor_entry *entry)
    {
    	entry->cnt = rpc_event_cnt;
    }
    
    /* Verify that thread can be put into sleep (no RPC event occured in a
     * meantime), or whether we should return to bsdlib to re-verify if a sleep is
     * needed.
     */
    static bool can_thread_sleep(struct thread_monitor_entry *entry)
    {
    	bool allow_to_sleep = true;
    
    	if (rpc_event_cnt != entry->cnt) {
    		thread_monitor_entry_update(entry);
    		allow_to_sleep = false;
    	}
    
    	return allow_to_sleep;
    }
    
    /* Initialize sleeping thread structure. */
    static void sleeping_thread_init(struct sleeping_thread *thread)
    {
    	k_sem_init(&thread->sem, 0, 1);
    }
    
    /* Add thread to the sleeping threads list. Will return information whether
     * the thread was allowed to sleep or not.
     */
    static bool sleeping_thread_add(struct sleeping_thread *thread)
    {
    	bool allow_to_sleep = false;
    	struct thread_monitor_entry *entry;
    
    	u32_t key = irq_lock();
    
    	entry = thread_monitor_entry_get(k_current_get());
    
    	if (can_thread_sleep(entry)) {
    		allow_to_sleep = true;
    		sys_slist_append(&sleeping_threads, &thread->node);
    	}
    
    	irq_unlock(key);
    
    	return allow_to_sleep;
    }
    
    /* Remove a thread form the sleeping threads list. */
    static void sleeping_thread_remove(struct sleeping_thread *thread)
    {
    	struct thread_monitor_entry *entry;
    
    	u32_t key = irq_lock();
    
    	sys_slist_find_and_remove(&sleeping_threads, &thread->node);
    
    	entry = thread_monitor_entry_get(k_current_get());
    	thread_monitor_entry_update(entry);
    
    	irq_unlock(key);
    }
    
    int32_t bsd_os_timedwait(uint32_t context, int32_t *timeout)
    {
    	struct sleeping_thread thread;
    	s64_t start, remaining;
    
    	start = k_uptime_get();
    
    	if (*timeout == 0) {
    		k_yield();
    		return NRF_ETIMEDOUT;
    	}
    
    	if (*timeout < 0) {
    		*timeout = K_FOREVER;
    	}
    
    	sleeping_thread_init(&thread);
    
    	if (!sleeping_thread_add(&thread)) {
    		return 0;
    	}
    
    	(void)k_sem_take(&thread.sem, *timeout);
    
    	sleeping_thread_remove(&thread);
    
    	if (*timeout == K_FOREVER) {
    		return 0;
    	}
    
    	/* Calculate how much time is left until timeout. */
    	remaining = *timeout - (k_uptime_get() - start);
    	*timeout = remaining > 0 ? remaining : 0;
    
    	if (*timeout == 0) {
    		return NRF_ETIMEDOUT;
    	}
    
    	return 0;
    }
    
    void bsd_os_errno_set(int err_code)
    {
    	switch (err_code) {
    	case NRF_EPERM:
    		errno = EPERM;
    		break;
    	case NRF_ENOENT:
    		errno = ENOENT;
    		break;
    	case NRF_EIO:
    		errno = EIO;
    		break;
    	case NRF_EBADF:
    		errno = EBADF;
    		break;
    	case NRF_ENOMEM:
    		errno = ENOMEM;
    		break;
    	case NRF_EACCES:
    		errno = EACCES;
    		break;
    	case NRF_EFAULT:
    		errno = EFAULT;
    		break;
    	case NRF_EINVAL:
    		errno = EINVAL;
    		break;
    	case NRF_EMFILE:
    		errno = EMFILE;
    		break;
    	case NRF_EAGAIN:
    		errno = EAGAIN;
    		break;
    	case NRF_EPROTOTYPE:
    		errno = EPROTOTYPE;
    		break;
    	case NRF_ENOPROTOOPT:
    		errno = ENOPROTOOPT;
    		break;
    	case NRF_EPROTONOSUPPORT:
    		errno = EPROTONOSUPPORT;
    		break;
    	case NRF_ESOCKTNOSUPPORT:
    		errno = ESOCKTNOSUPPORT;
    		break;
    	case NRF_EOPNOTSUPP:
    		errno = EOPNOTSUPP;
    		break;
    	case NRF_EAFNOSUPPORT:
    		errno = EAFNOSUPPORT;
    		break;
    	case NRF_EADDRINUSE:
    		errno = EADDRINUSE;
    		break;
    	case NRF_ENETDOWN:
    		errno = ENETDOWN;
    		break;
    	case NRF_ENETUNREACH:
    		errno = ENETUNREACH;
    		break;
    	case NRF_ECONNRESET:
    		errno = ECONNRESET;
    		break;
    	case NRF_EISCONN:
    		errno = EISCONN;
    		break;
    	case NRF_ENOTCONN:
    		errno = ENOTCONN;
    		break;
    	case NRF_ETIMEDOUT:
    		errno = ETIMEDOUT;
    		break;
    	case NRF_ENOBUFS:
    		errno = ENOBUFS;
    		break;
    	case NRF_EHOSTDOWN:
    		errno = EHOSTDOWN;
    		break;
    	case NRF_EINPROGRESS:
    		errno = EINPROGRESS;
    		break;
    	case NRF_ECANCELED:
    		errno = ECANCELED;
    		break;
    	case NRF_ENOKEY:
    		errno = ENOKEY;
    		break;
    	case NRF_EKEYEXPIRED:
    		errno = EKEYEXPIRED;
    		break;
    	case NRF_EKEYREVOKED:
    		errno = EKEYREVOKED;
    		break;
    	case NRF_EKEYREJECTED:
    		errno = EKEYREJECTED;
    		break;
    	default:
    		errno = EINVAL;
    		break;
    	}
    }
    
    void bsd_os_application_irq_set(void)
    {
    	NVIC_SetPendingIRQ(BSD_APPLICATION_IRQ);
    }
    
    void bsd_os_application_irq_clear(void)
    {
    	NVIC_ClearPendingIRQ(BSD_APPLICATION_IRQ);
    }
    
    void bsd_os_trace_irq_set(void)
    {
    	NVIC_SetPendingIRQ(TRACE_IRQ);
    }
    
    void bsd_os_trace_irq_clear(void)
    {
    	NVIC_ClearPendingIRQ(TRACE_IRQ);
    }
    
    ISR_DIRECT_DECLARE(ipc_proxy_irq_handler)
    {
    	IPC_IRQHandler();
    	ISR_DIRECT_PM(); /* PM done after servicing interrupt for best latency
    			  */
    
    	return 1; /* We should check if scheduling decision should be made */
    }
    
    ISR_DIRECT_DECLARE(rpc_proxy_irq_handler)
    {
    	atomic_inc(&rpc_event_cnt);
    
    	bsd_os_application_irq_handler();
    
    	struct sleeping_thread *thread;
    
    	/* Wake up all sleeping threads. */
    	SYS_SLIST_FOR_EACH_CONTAINER(&sleeping_threads, thread, node) {
    		k_sem_give(&thread->sem);
    	}
    
    	ISR_DIRECT_PM(); /* PM done after servicing interrupt for best latency
    			  */
    	return 1; /* We should check if scheduling decision should be made */
    }
    
    ISR_DIRECT_DECLARE(trace_proxy_irq_handler)
    {
    	/*
    	 * Process traces.
    	 * The function has to be called even if UART traces are disabled.
    	 */
    	bsd_os_trace_irq_handler();
    	ISR_DIRECT_PM(); /* PM done after servicing interrupt for best latency
    			  */
    	return 1; /* We should check if scheduling decision should be made */
    }
    
    void trace_task_create(void)
    {
    	IRQ_DIRECT_CONNECT(TRACE_IRQ, TRACE_IRQ_PRIORITY,
    			   trace_proxy_irq_handler, UNUSED_FLAGS);
    	irq_enable(TRACE_IRQ);
    }
    
    void read_task_create(void)
    {
    	IRQ_DIRECT_CONNECT(BSD_APPLICATION_IRQ, BSD_APPLICATION_IRQ_PRIORITY,
    			   rpc_proxy_irq_handler, UNUSED_FLAGS);
    	irq_enable(BSD_APPLICATION_IRQ);
    }
    
    void trace_uart_init(void)
    {
    }
    
    /* This function is called by bsd_init and must not be called explicitly. */
    void bsd_os_init(void)
    {
    	sys_slist_init(&sleeping_threads);
    	atomic_clear(&rpc_event_cnt);
    
    	read_task_create();
    
    	/* Configure and enable modem tracing over UART. */
    	trace_uart_init();
    	trace_task_create();
    }
    
    int32_t bsd_os_trace_put(const uint8_t * const data, uint32_t len)
    {
    
    	return 0;
    }
    
    static int _bsd_driver_init(struct device *unused)
    {
    	/* Setup the two IRQs used by the BSD library.
    	 * Note: No enable irq_enable here. This is done through bsd_init.
    	 */
    	IRQ_DIRECT_CONNECT(BSD_NETWORK_IRQ, BSD_NETWORK_IRQ_PRIORITY,
    			   ipc_proxy_irq_handler, UNUSED_FLAGS);
    	bsd_init();
    
    	return 0;
    }
    
    SYS_INIT(_bsd_driver_init, POST_KERNEL, 0);

     

    Could you try this and see if it behaves better?

     

    Kind regards,

    Håkon

  • Success! I copied your bsd_os.c, and used nrfxlib master branch and it worked!

    Thank you for your assistance!

    Also, knowing the precise versions of the projects is a really big help. I've struggled with that often when trying out new features.

Related