| /**************************************************************************** |
| * arch/arm/src/nrf91/nrf91_modem_os.c |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| |
| #include <arch/irq.h> |
| |
| #include <nuttx/mutex.h> |
| #include <nuttx/semaphore.h> |
| #include <nuttx/signal.h> |
| |
| #include <nuttx/mm/mm.h> |
| |
| #include <debug.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <semaphore.h> |
| |
| #include "nrf_modem.h" |
| #include "nrf_modem_os.h" |
| |
| #include "nrf91_modem.h" |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| #define NRF_ESHUTDOWN 110 |
| |
| #define NRF91_MODEM_WAITING_PREALLOC (32) |
| |
| /**************************************************************************** |
| * Private Types |
| ****************************************************************************/ |
| |
| struct nrf91_modem_os_waiting_s |
| { |
| sem_t sem; |
| uint32_t context; |
| bool waiting; |
| struct timespec ts_start; |
| }; |
| |
| struct nrf91_modem_os_s |
| { |
| sem_t sem[NRF_MODEM_OS_NUM_SEM_REQUIRED]; |
| uint8_t sem_cntr; |
| mutex_t waiting_lock; |
| struct nrf91_modem_os_waiting_s waiting[NRF91_MODEM_WAITING_PREALLOC]; |
| }; |
| |
| /**************************************************************************** |
| * Private Function Prototypes |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| static struct nrf91_modem_os_s g_nrf91_modem_os; |
| static struct mm_heap_s *g_shmtxheap = NULL; |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: nrf_waiting_get |
| ****************************************************************************/ |
| |
| struct nrf91_modem_os_waiting_s *nrf_waiting_get(void) |
| { |
| struct nrf91_modem_os_waiting_s *ret = NULL; |
| int i = 0; |
| |
| nxmutex_lock(&g_nrf91_modem_os.waiting_lock); |
| |
| for (i = 0; i < NRF91_MODEM_WAITING_PREALLOC; i++) |
| { |
| if (g_nrf91_modem_os.waiting[i].waiting == false) |
| { |
| ret = &g_nrf91_modem_os.waiting[i]; |
| nxsem_init(&ret->sem, 0, 0); |
| break; |
| } |
| } |
| |
| nxmutex_unlock(&g_nrf91_modem_os.waiting_lock); |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: nrf_waiting_free |
| ****************************************************************************/ |
| |
| void nrf_waiting_free(struct nrf91_modem_os_waiting_s *w) |
| { |
| nxmutex_lock(&g_nrf91_modem_os.waiting_lock); |
| |
| nxsem_destroy(&w->sem); |
| w->context = 0; |
| w->waiting = false; |
| |
| nxmutex_unlock(&g_nrf91_modem_os.waiting_lock); |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: nrf_modem_os_init |
| * |
| * Description: |
| * Initialize the glue layer. |
| * |
| ****************************************************************************/ |
| |
| void nrf_modem_os_init(void) |
| { |
| memset(&g_nrf91_modem_os, 0, sizeof(struct nrf91_modem_os_s)); |
| |
| /* Initialize shared memory TX heap */ |
| |
| g_shmtxheap = mm_initialize("shmtx", |
| (void *) NRF91_SHMEM_TX_BASE, |
| NRF91_SHMEM_TX_SIZE); |
| |
| nxmutex_init(&g_nrf91_modem_os.waiting_lock); |
| } |
| |
| /**************************************************************************** |
| * Name: nrf_modem_os_shutdown |
| * |
| * Description: |
| * Deinitialize the glue layer. |
| * |
| * When shutdown is called, all pending calls to @c nrf_modem_os_timedwait |
| * shall exit and return -NRF_ESHUTDOWN. |
| * |
| ****************************************************************************/ |
| |
| void nrf_modem_os_shutdown(void) |
| { |
| struct nrf91_modem_os_waiting_s *waiting = NULL; |
| int i = 0; |
| |
| /* TODO: send AT+CFUN=0 */ |
| |
| /* Wake up all waiting semaphores */ |
| |
| for (i = 0; i < NRF91_MODEM_WAITING_PREALLOC; i++) |
| { |
| waiting = &g_nrf91_modem_os.waiting[i]; |
| if (waiting->waiting == true) |
| { |
| nxsem_post(&waiting->sem); |
| } |
| } |
| |
| /* Uninitialize shared memory TX heap */ |
| |
| mm_uninitialize(g_shmtxheap); |
| } |
| |
| /**************************************************************************** |
| * Name: nrf_modem_os_shm_tx_alloc |
| * |
| * Description: |
| * Allocate a buffer on the TX area of shared memory. |
| * |
| ****************************************************************************/ |
| |
| void *nrf_modem_os_shm_tx_alloc(size_t bytes) |
| { |
| return mm_malloc(g_shmtxheap, bytes); |
| } |
| |
| /**************************************************************************** |
| * Name: nrf_modem_os_shm_tx_free |
| * |
| * Description: |
| * Free a shared memory buffer in the TX area. |
| * |
| ****************************************************************************/ |
| |
| void nrf_modem_os_shm_tx_free(void *mem) |
| { |
| mm_free(g_shmtxheap, mem); |
| } |
| |
| /**************************************************************************** |
| * Name: nrf_modem_os_alloc |
| * |
| * Description: |
| * Allocate a buffer on the library heap. |
| * |
| ****************************************************************************/ |
| |
| void *nrf_modem_os_alloc(size_t bytes) |
| { |
| return malloc(bytes); |
| } |
| |
| /**************************************************************************** |
| * Name: nrf_modem_os_free |
| * |
| * Description: |
| * Free a memory buffer in the library heap. |
| * |
| ****************************************************************************/ |
| |
| void nrf_modem_os_free(void *mem) |
| { |
| free(mem); |
| } |
| |
| /**************************************************************************** |
| * Name: nrf_modem_os_busywait |
| * |
| * Description: |
| * Busy wait. |
| * |
| ****************************************************************************/ |
| |
| void nrf_modem_os_busywait(int32_t usec) |
| { |
| up_udelay(usec); |
| } |
| |
| /**************************************************************************** |
| * Name: nrf_modem_os_timedwait |
| * |
| * Description: |
| * Put a thread to sleep for a specific time or until an event occurs. |
| * |
| ****************************************************************************/ |
| |
| int32_t nrf_modem_os_timedwait(uint32_t context, int32_t *timeout) |
| { |
| struct nrf91_modem_os_waiting_s *waiting = NULL; |
| struct timespec ts_now; |
| struct timespec abstime; |
| int32_t remaining = 0; |
| int32_t diff = 0; |
| int ret = -EAGAIN; |
| |
| /* Modem is not initialized or was shut down */ |
| |
| if (!nrf_modem_is_initialized()) |
| { |
| ret = -NRF_ESHUTDOWN; |
| goto errout; |
| } |
| |
| if (*timeout == 0) |
| { |
| ret = -EAGAIN; |
| goto errout; |
| } |
| |
| /* Get free waiting slot */ |
| |
| waiting = nrf_waiting_get(); |
| if (waiting == NULL) |
| { |
| ret = -ENOMEM; |
| goto errout; |
| } |
| |
| waiting->context = context; |
| waiting->waiting = true; |
| clock_systime_timespec(&waiting->ts_start); |
| |
| if (*timeout < 0) |
| { |
| /* Wait for event */ |
| |
| nxsem_wait(&waiting->sem); |
| ret = OK; |
| } |
| else |
| { |
| /* Wait for event or timeout */ |
| |
| abstime.tv_sec = *timeout / 1000; |
| abstime.tv_nsec = (*timeout % 1000) * 1000000; |
| |
| nxsem_timedwait(&waiting->sem, &abstime); |
| } |
| |
| /* Free a waiting slot */ |
| |
| nrf_waiting_free(waiting); |
| |
| /* Modem is not initialized or was shut down */ |
| |
| if (!nrf_modem_is_initialized()) |
| { |
| ret = -NRF_ESHUTDOWN; |
| goto errout; |
| } |
| |
| /* Handle timeout */ |
| |
| if (ret < 0) |
| { |
| clock_systime_timespec(&ts_now); |
| |
| diff = ((ts_now.tv_sec - waiting->ts_start.tv_sec) * 1000 + |
| (ts_now.tv_nsec - waiting->ts_start.tv_nsec) / 1000000); |
| |
| remaining = *timeout - diff; |
| |
| /* Return remaining timeout */ |
| |
| *timeout = (remaining > 0) ? remaining : 0; |
| |
| if (*timeout == 0) |
| { |
| ret = -EAGAIN; |
| } |
| } |
| |
| errout: |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: nrf_modem_os_event_notify |
| * |
| * Description: |
| * Notify the application that an event has occurred. |
| * |
| ****************************************************************************/ |
| |
| void nrf_modem_os_event_notify(uint32_t context) |
| { |
| struct nrf91_modem_os_waiting_s *waiting = NULL; |
| int i = 0; |
| |
| for (i = 0; i < NRF91_MODEM_WAITING_PREALLOC; i++) |
| { |
| waiting = &g_nrf91_modem_os.waiting[i]; |
| if (waiting->waiting == true) |
| { |
| if (waiting->context == context || context == 0) |
| { |
| nxsem_post(&waiting->sem); |
| } |
| } |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: nrf_modem_os_sleep |
| * |
| * Description: |
| * Put a thread to sleep for a specific amount of time. |
| * |
| ****************************************************************************/ |
| |
| int nrf_modem_os_sleep(uint32_t timeout) |
| { |
| /* Timeout in ms */ |
| |
| nxsig_usleep(timeout * 1000); |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: nrf_modem_os_errno_set |
| * |
| * Description: |
| * Set errno. |
| * |
| ****************************************************************************/ |
| |
| void nrf_modem_os_errno_set(int errno_val) |
| { |
| set_errno(errno_val); |
| } |
| |
| /**************************************************************************** |
| * Name: nrf_modem_os_is_in_isr |
| * |
| * Description: |
| * Check if executing in interrupt context. |
| * |
| ****************************************************************************/ |
| |
| bool nrf_modem_os_is_in_isr(void) |
| { |
| return up_interrupt_context(); |
| } |
| |
| /**************************************************************************** |
| * Name: nrf_modem_os_sem_init |
| * |
| * Description: |
| * Initialize a semaphore. |
| * |
| ****************************************************************************/ |
| |
| int nrf_modem_os_sem_init(void **sem, unsigned int initial_count, |
| unsigned int limit) |
| { |
| sem_t *modemsem = &g_nrf91_modem_os.sem[g_nrf91_modem_os.sem_cntr]; |
| int ret = OK; |
| |
| UNUSED(limit); |
| |
| DEBUGASSERT(g_nrf91_modem_os.sem_cntr < NRF_MODEM_OS_NUM_SEM_REQUIRED); |
| ret = nxsem_init(modemsem, 0, initial_count); |
| g_nrf91_modem_os.sem_cntr++; |
| |
| *sem = (void *)modemsem; |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: nrf_modem_os_sem_give |
| * |
| * Description: |
| * Give a semaphore. |
| * |
| ****************************************************************************/ |
| |
| void nrf_modem_os_sem_give(void *sem) |
| { |
| nxsem_post((sem_t *)sem); |
| } |
| |
| /**************************************************************************** |
| * Name: nrf_modem_os_sem_take |
| * |
| * Description: |
| * Take a semaphore. |
| * |
| ****************************************************************************/ |
| |
| int nrf_modem_os_sem_take(void *sem, int timeout) |
| { |
| int ret = OK; |
| sem_t *s = sem; |
| |
| if (timeout == -1) |
| { |
| ret = nxsem_wait(s); |
| } |
| else |
| { |
| struct timespec abstime; |
| |
| abstime.tv_sec = timeout / 1000; |
| abstime.tv_nsec = (timeout % 1000) * 1000000; |
| |
| ret = nxsem_timedwait(s, &abstime); |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: nrf_modem_os_sem_count_get |
| * |
| * Description: |
| * Get a semaphore's count. |
| * |
| ****************************************************************************/ |
| |
| unsigned int nrf_modem_os_sem_count_get(void *sem) |
| { |
| int sval = 0; |
| |
| nxsem_get_value((sem_t *)sem, &sval); |
| return sval; |
| } |
| |
| /**************************************************************************** |
| * Name: nrf_modem_os_log |
| * |
| * Description: |
| * Generic logging procedure. |
| * |
| ****************************************************************************/ |
| |
| void nrf_modem_os_log(int level, const char *fmt, ...) |
| { |
| va_list ap; |
| |
| va_start(ap, fmt); |
| vsyslog(level, fmt, ap); |
| va_end(ap); |
| } |
| |
| /**************************************************************************** |
| * Name: nrf_modem_os_logdump |
| * |
| * Description: |
| * Logging procedure for dumping hex representation of object. |
| * |
| ****************************************************************************/ |
| |
| void nrf_modem_os_logdump(int level, const char *str, const void *data, |
| size_t len) |
| { |
| syslog(level, "nrf_modem_os_logdump"); |
| lib_dumpbuffer(str, data, len); |
| } |