Adapted to Lora Semtech + Nucleo

Dependencies:   DebugLib

Dependents:   LoRaWAN-lmic-app LoRaWAN-lmic-app LoRaWAN-test-10secs LoRaPersonalizedDeviceForEverynet ... more

Fork of lwip_ppp_ethernet by Donatien Garnier

arch/sys_arch.cpp

Committer:
donatien
Date:
2012-05-24
Revision:
0:8e01dca41002

File content as of revision 0:8e01dca41002:

/* sysarch.cpp */
/*
Copyright (C) 2012 ARM Limited.

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#define __DEBUG__ 0
#ifndef __MODULE__
#define __MODULE__ "sys_arch.cpp"
#endif

extern "C"
{
#include "lwip/opt.h"
#include "lwip/def.h"
#include "lwip/sys.h"
#include "lwip/arch.h"
#include "cc.h"
#include "sys_arch.h"
}

#include "core/fwk.h"
#include "rtos.h"
#include "mbed.h" //From mbed library

/*
From http://lwip.wikia.com/wiki/Porting_for_an_OS:

The sys_arch provides semaphores and mailboxes to lwIP. For the full
lwIP functionality, multiple threads support can be implemented in the
sys_arch, but this is not required for the basic lwIP functionality.
*/

extern "C" 
/*
The file is .cpp to be able to use the C++ abstraction of Mutexes, Semaphores, etc.
however it should be linked in a C-fashion
*/
{

#if SYS_LIGHTWEIGHT_PROT == 0
#error "SYS_LIGHTWEIGHT_PROT must be set"
#endif

#if SYS_LIGHTWEIGHT_PROT
//Critical regions protection
static Mutex sys_arch_protect_mtx;

sys_prot_t sys_arch_protect(void)
{

  sys_arch_protect_mtx.lock();

  return 0;
}

void sys_arch_unprotect(sys_prot_t pval)
{

  LWIP_UNUSED_ARG(pval);
  sys_arch_protect_mtx.unlock();

}
#endif

//Mutexes

/** Create a new mutex
 * @param mutex pointer to the mutex to create
 * @return a new mutex */
err_t sys_mutex_new(sys_mutex_t *pMtx)
{

  SYS_ARCH_DECL_PROTECT(lev);
  DBG("Trying to create new mutex");

  SYS_ARCH_PROTECT(lev); //Protect this section
  pMtx->pMtx = new Mutex();
  SYS_ARCH_UNPROTECT(lev);

  if(pMtx->pMtx == NULL)
  {
    DBG("No mem");

    return ERR_MEM;
  }

  pMtx->valid = true;

  DBG("mutex created OK");

  return ERR_OK; //Return the semaphore
}

/** Lock a mutex
 * @param mutex the mutex to lock */
void sys_mutex_lock(sys_mutex_t *pMtx)
{

  ((Mutex*)(pMtx->pMtx))->lock();

}

/** Unlock a mutex
 * @param mutex the mutex to unlock */
void sys_mutex_unlock(sys_mutex_t *pMtx)
{

  ((Mutex*)(pMtx->pMtx))->unlock();

}

/** Delete a mutex
 * @param mutex the mutex to delete */
void sys_mutex_free(sys_mutex_t *pMtx)
{

  SYS_ARCH_DECL_PROTECT(lev);
  SYS_ARCH_PROTECT(lev); //Protect this section
  delete ((Mutex*)(pMtx->pMtx));
  SYS_ARCH_UNPROTECT(lev);

}

/** Check if a mutex is valid/allocated: return 1 for valid, 0 for invalid */
int sys_mutex_valid(sys_mutex_t *pMtx)
{

  if (pMtx->pMtx != NULL)
  {

    return pMtx->valid;
  }

  return false;
}

/** Set a mutex invalid so that sys_mutex_valid returns 0 */
void sys_mutex_set_invalid(sys_mutex_t *pMtx)
{

  pMtx->valid = false;

}

//Semaphores

/*
The implementation reserves a pool of semaphores that can be used by
LwIP in order to only use static allocation
*/

//static sys_sem sys_sem_pool[LWIP_SEMAPHORES_COUNT] = { 0 };

/*
Creates and returns a new semaphore. The count argument specifies the
initial state of the semaphore. Returns the semaphore, or SYS_SEM_NULL
on error.
*/
err_t sys_sem_new_mul(sys_sem_t *pSem, u8_t size, u8_t count)
{

  SYS_ARCH_DECL_PROTECT(lev);
  DBG("Trying to create new semaphore of size=%d", size);

  SYS_ARCH_PROTECT(lev); //Protect this section
  pSem->pSphre = new Semaphore(size);
  SYS_ARCH_UNPROTECT(lev);
  if(pSem->pSphre == NULL)
  {
    ERR("Failed!");
    return ERR_MEM;
  }

  while(count < size)
  {
    ((Semaphore*)(pSem->pSphre))->wait();
    count++;
  }
  pSem->valid = true;


  return ERR_OK; //Return the semaphore
}


/*
Creates and returns a new semaphore. The count argument specifies the
initial state of the semaphore. Returns the semaphore, or SYS_SEM_NULL
on error.
*/
err_t sys_sem_new(sys_sem_t *pSem, u8_t count)
{

  SYS_ARCH_DECL_PROTECT(lev);
  DBG("Trying to create new semaphore of count=%d", count);

  SYS_ARCH_PROTECT(lev); //Protect this section
  pSem->pSphre = new Semaphore(1);
  SYS_ARCH_UNPROTECT(lev);
  if(pSem->pSphre == NULL)
  {
    ERR("Failed!");
    return ERR_MEM;
  }

  if(!count)
  {
    ((Semaphore*)(pSem->pSphre))->wait();
  }
  pSem->valid = true;


  return ERR_OK; //Return the semaphore
}

/*
Frees a semaphore created by sys_sem_new. Since these two functions
provide the entry and exit point for all semaphores used by lwIP, you
have great flexibility in how these are allocated and deallocated (for
example, from the heap, a memory pool, a semaphore pool, etc).
*/
void sys_sem_free(sys_sem_t *pSem)
{
  SYS_ARCH_DECL_PROTECT(lev);
  DBG("Deleting semaphore");
  SYS_ARCH_PROTECT(lev); //Protect this section
  delete ((Semaphore*)(pSem->pSphre));
  SYS_ARCH_UNPROTECT(lev);

}

/*
Signals (or releases) a semaphore.
*/
void sys_sem_signal(sys_sem_t* pSem)
{

  ((Semaphore*)(pSem->pSphre))->release(); //Produce (i.e. release) a resource

}

/*
Blocks the thread while waiting for the semaphore to be signaled. The
timeout parameter specifies how many milliseconds the function should
block before returning; if the function times out, it should return
SYS_ARCH_TIMEOUT. If timeout=0, then the function should block
indefinitely. If the function acquires the semaphore, it should return
how many milliseconds expired while waiting for the semaphore. The
function may return 0 if the semaphore was immediately available.
*/
u32_t sys_arch_sem_wait(sys_sem_t* pSem, u32_t timeout)
{

  int ret;  
  Timer t;
  uint32_t timeout_sphre;
  int time_spent;
  
  timeout_sphre = (timeout != 0) ? timeout : osWaitForever /*block indefinitely*/;
  
  t.start();
  ret = ((Semaphore*)(pSem->pSphre))->wait(timeout_sphre);
  if(ret == 0)
  {

    return SYS_ARCH_TIMEOUT;
  }
  time_spent = t.read_ms();

  return time_spent; 
}

/** Check if a sempahore is valid/allocated: return 1 for valid, 0 for invalid */
int sys_sem_valid(sys_sem_t *pSem)
{

  if (pSem->pSphre != NULL)
  {

    return (pSem->valid);
  }

  return false;
}

/** Set a semaphore invalid so that sys_sem_valid returns 0 */
void sys_sem_set_invalid(sys_sem_t *pSem)
{

  pSem->valid = false;

}

//Mailboxes

/*
The implementation reserves a pool of mailboxes of generic (void*) type that can be used by
LwIP in order to only use static allocation
*/

#define QUEUE_SIZE 8
#define SYS_MBOX_SIZE QUEUE_SIZE
#if 0
/*
Returns a new mailbox, or SYS_MBOX_NULL on error.
*/
err_t sys_mbox_new(sys_mbox_t *pMbox, int size)
{

  SYS_ARCH_DECL_PROTECT(lev);
  ERR("Trying to create new queue of size %d", size);

  SYS_ARCH_PROTECT(lev); //Protect this section
  if(size > QUEUE_SIZE)
  {
    ERR("Queue size > QUEUE_SIZE");
  }
  pMbox->pQueue = new Queue<void,QUEUE_SIZE>();
  SYS_ARCH_UNPROTECT(lev);
  if(pMbox->pQueue == NULL)
  {

    return ERR_MEM;
  }
  pMbox->valid = true;


  return ERR_OK; //Return the mailbox
}

/*
Deallocates a mailbox. If there are messages still present in the
mailbox when the mailbox is deallocated, it is an indication of a
programming error in lwIP and the developer should be notified.
*/
void sys_mbox_free(sys_mbox_t* pMbox)
{
  ERR("WHY??");

  SYS_ARCH_DECL_PROTECT(lev);
  SYS_ARCH_PROTECT(lev); //Protect this section
  delete ((Queue<void,QUEUE_SIZE>*)(pMbox->pQueue));
  SYS_ARCH_UNPROTECT(lev);

}

/*
Posts the "msg" to the mailbox.
*/
void sys_mbox_post(sys_mbox_t* pMbox, void *msg)
{

  if(msg == NULL)
  {
    ERR("NULL");
    Thread::wait(100);
  }

  ((Queue<void,QUEUE_SIZE>*)(pMbox->pQueue))->put(msg, osWaitForever);

}

/*
Blocks the thread until a message arrives in the mailbox, but does not
block the thread longer than timeout milliseconds (similar to the
sys_arch_sem_wait() function). The msg argument is a pointer to the
message in the mailbox and may be NULL to indicate that the message
should be dropped. This should return either SYS_ARCH_TIMEOUT or the
number of milliseconds elapsed waiting for a message.
*/
u32_t sys_arch_mbox_fetch(sys_mbox_t* pMbox, void **msg, u32_t timeout)
{
  Timer t;
  uint32_t timeout_queue;
  int time_spent;
  osEvent evt;
  
  timeout_queue = (timeout != 0) ? timeout : osWaitForever /*block indefinitely*/;
  
  t.start();
  evt = ((Queue<void,QUEUE_SIZE>*)(pMbox->pQueue))->get(timeout_queue);
  if(evt.status != osEventMessage)
  {
    return SYS_ARCH_TIMEOUT;
  }
  time_spent = t.read_ms();
  if(msg!=NULL)
  {
    *msg = evt.value.p;
  }
  else
  {
    ERR("Dropped");
    Thread::wait(100);
  }
  return time_spent; 
}

/*
This is similar to sys_arch_mbox_fetch, however if a message is not
present in the mailbox, it immediately returns with the code
SYS_MBOX_EMPTY. On success 0 is returned with msg pointing to the
message retrieved from the mailbox.
*/
u32_t sys_arch_mbox_tryfetch(sys_mbox_t* pMbox, void **msg)
{

  osEvent evt;

  evt = ((Queue<void,QUEUE_SIZE>*)(pMbox->pQueue))->get(0);
  if(evt.status != osEventMessage)
  {
    return SYS_MBOX_EMPTY;
  }
  if(msg!=NULL)
  {
    *msg = evt.value.p;
  }
  else
  {
    ERR("Dropped");
    Thread::wait(100);
  }

  return ERR_OK; 
}

/*
Tries to post a message to mbox by polling (no timeout).
*/
#define X() do{(*((volatile uint32_t *)0x40024048))=__LINE__;}while(0)
#define D(d) do{(*((volatile uint32_t *)0x4002404C))=d;}while(0)
err_t sys_mbox_trypost(sys_mbox_t* pMbox, void *msg)
{
  int ret;

  if(msg == NULL)
  {
    ERR("NULL");
    Thread::wait(100);
  }

  ret = ((Queue<void,QUEUE_SIZE>*)(pMbox->pQueue))->put(msg,0);
  if(ret != osOK)
  {
    ERR("FULL");
    return ERR_MEM; 
  }

  return ERR_OK;
}

/** Check if an mbox is valid/allocated: return 1 for valid, 0 for invalid */
int sys_mbox_valid(sys_mbox_t *pMbox)
{

  if (pMbox->pQueue != NULL)
  {

    return (pMbox->valid);
  }

  return false;
}

/** Set an mbox invalid so that sys_mbox_valid returns 0 */
void sys_mbox_set_invalid(sys_mbox_t *pMbox)
{

  pMbox->valid = false;

}
#else

/** Check if an mbox is valid/allocated: return 1 for valid, 0 for invalid */
int sys_mbox_valid(sys_mbox_t *pMbox)
{
  return (pMbox->valid);
}

/** Set an mbox invalid so that sys_mbox_valid returns 0 */
void sys_mbox_set_invalid(sys_mbox_t *pMbox)
{
  pMbox->valid = false;
}

/*-----------------------------------------------------------------------------------*/
err_t
sys_mbox_new(sys_mbox_t* mb, int size)
{
  LWIP_UNUSED_ARG(size);

  mb->first = mb->last = 0;
  sys_sem_new(&mb->not_empty, 0);
  sys_sem_new(&mb->not_full, 0);
  sys_sem_new(&mb->mutex, 1);
  mb->wait_send = 0;
  mb->valid=true;

  return ERR_OK;
}
/*-----------------------------------------------------------------------------------*/
void
sys_mbox_free(sys_mbox_t* mb)
{
  if ((mb != NULL)) {
    sys_arch_sem_wait(&mb->mutex, 0);

    sys_sem_free(&mb->not_empty);
    sys_sem_free(&mb->not_full);
    sys_sem_free(&mb->mutex);
    //mb->not_empty = mb->not_full = mb->mutex = NULL;
    mb->valid=false;
    /*  LWIP_DEBUGF("sys_mbox_free: mbox 0x%lx\n", mbox); */
  }
}
/*-----------------------------------------------------------------------------------*/
err_t
sys_mbox_trypost(sys_mbox_t* mb, void *msg)
{
  u8_t first;
  LWIP_ASSERT("invalid mbox", (mb != NULL) );

  sys_arch_sem_wait(&mb->mutex, 0);

  LWIP_DEBUGF(SYS_DEBUG, ("sys_mbox_trypost: mbox %p msg %p\n",
                          (void *)mb, (void *)msg));

  if ((mb->last + 1) >= (mb->first + SYS_MBOX_SIZE)) {
    sys_sem_signal(&mb->mutex);
    return ERR_MEM;
  }

  mb->msgs[mb->last % SYS_MBOX_SIZE] = msg;

  if (mb->last == mb->first) {
    first = 1;
  } else {
    first = 0;
  }

  mb->last++;

  if (first) {
    sys_sem_signal(&mb->not_empty);
  }

  sys_sem_signal(&mb->mutex);

  return ERR_OK;
}
/*-----------------------------------------------------------------------------------*/
void
sys_mbox_post(sys_mbox_t* mb, void *msg)
{
  u8_t first;
  LWIP_ASSERT("invalid mbox", (mb != NULL));

  sys_arch_sem_wait(&mb->mutex, 0);

  LWIP_DEBUGF(SYS_DEBUG, ("sys_mbox_post: mbox %p msg %p\n", (void *)mb, (void *)msg));

  while ((mb->last + 1) >= (mb->first + SYS_MBOX_SIZE)) {
    mb->wait_send++;
    sys_sem_signal(&mb->mutex);
    sys_arch_sem_wait(&mb->not_full, 0);
    sys_arch_sem_wait(&mb->mutex, 0);
    mb->wait_send--;
  }

  mb->msgs[mb->last % SYS_MBOX_SIZE] = msg;

  if (mb->last == mb->first) {
    first = 1;
  } else {
    first = 0;
  }

  mb->last++;

  if (first) {
    sys_sem_signal(&mb->not_empty);
  }

  sys_sem_signal(&mb->mutex);
}
/*-----------------------------------------------------------------------------------*/
u32_t
sys_arch_mbox_tryfetch(sys_mbox_t* mb, void **msg)
{
  LWIP_ASSERT("invalid mbox", (mb != NULL));

  sys_arch_sem_wait(&mb->mutex, 0);

  if (mb->first == mb->last) {
    sys_sem_signal(&mb->mutex);
    return SYS_MBOX_EMPTY;
  }

  if (msg != NULL) {
    LWIP_DEBUGF(SYS_DEBUG, ("sys_mbox_tryfetch: mbox %p msg %p\n", (void *)mb, *msg));
    *msg = mb->msgs[mb->first % SYS_MBOX_SIZE];
  }
  else{
    LWIP_DEBUGF(SYS_DEBUG, ("sys_mbox_tryfetch: mbox %p, null msg\n", (void *)mb));
  }

  mb->first++;

  if (mb->wait_send) {
    sys_sem_signal(&mb->not_full);
  }

  sys_sem_signal(&mb->mutex);

  return 0;
}
/*-----------------------------------------------------------------------------------*/
u32_t
sys_arch_mbox_fetch(sys_mbox_t* mb, void **msg, u32_t timeout)
{
  u32_t time_needed = 0;
  LWIP_ASSERT("invalid mbox", (mb != NULL));

  /* The mutex lock is quick so we don't bother with the timeout
     stuff here. */
  sys_arch_sem_wait(&mb->mutex, 0);

  while (mb->first == mb->last) {
    sys_sem_signal(&mb->mutex);

    /* We block while waiting for a mail to arrive in the mailbox. We
       must be prepared to timeout. */
    if (timeout != 0) {
      time_needed = sys_arch_sem_wait(&mb->not_empty, timeout);

      if (time_needed == SYS_ARCH_TIMEOUT) {
        return SYS_ARCH_TIMEOUT;
      }
    } else {
      sys_arch_sem_wait(&mb->not_empty, 0);
    }
    sys_arch_sem_wait(&mb->mutex, 0);
  }

  if (msg != NULL) {
    LWIP_DEBUGF(SYS_DEBUG, ("sys_mbox_fetch: mbox %p msg %p\n", (void *)mb, *msg));
    *msg = mb->msgs[mb->first % SYS_MBOX_SIZE];
  }
  else{
    LWIP_DEBUGF(SYS_DEBUG, ("sys_mbox_fetch: mbox %p, null msg\n", (void *)mb));
  }

  mb->first++;

  if (mb->wait_send) {
    sys_sem_signal(&mb->not_full);
  }

  sys_sem_signal(&mb->mutex);

  return time_needed;
}

#endif

//Threads and timeout lists

/*
The implementation reserves a pool of threads that can be used by
LwIP in order to only use static allocation
*/

//sys_thread_t* lwip_system_threads = NULL; //Linked list of active threads
//struct sys_timeo* lwip_system_timeouts = NULL; // Default timeouts list for lwIP

/*
Instanciates a thread for LwIP:
name is the thread name. thread(arg) is the call made as the thread's
entry point. stacksize is the recommended stack size for this thread.
prio is the priority that lwIP asks for. Stack size(s) and priority(ies)
are defined in lwipopts.h.
*/

struct sys_thread
{
  Thread* pTask;
  //struct sys_timeo* timeouts;
  struct sys_thread* next;
};
struct sys_thread* lwip_system_threads = NULL; //Linked list of active threads

sys_thread_t sys_thread_new(const char *name, lwip_thread_fn fn, void *arg, int stacksize, int prio)
{
  SYS_ARCH_DECL_PROTECT(lev);
  SYS_ARCH_PROTECT(lev); //Protect this section
  struct sys_thread* pT = new struct sys_thread;
  SYS_ARCH_UNPROTECT(lev);
  if(pT==NULL)
  {
    ERR("No mem");
    return;
  }

  SYS_ARCH_PROTECT(lev); //Protect this section

  //Link in list
  pT->next = lwip_system_threads;
  lwip_system_threads = pT;


  SYS_ARCH_UNPROTECT(lev); //Protect this section

  DBG("Trying to create new thread of stacksize %d and prio %d", stacksize, prio);

  //Must be done at the very end because the task will start immediately
  SYS_ARCH_PROTECT(lev);
  pT->pTask = new Thread((void (*)(void const *argument))fn, arg, (osPriority)prio /*FIXME*/, stacksize);
  SYS_ARCH_UNPROTECT(lev);

  DBG("pT->pTask=%p", pT->pTask);
  if(pT->pTask == NULL)
  {
    SYS_ARCH_PROTECT(lev);

    //Unlink
    if(pT == lwip_system_threads)
    {
      lwip_system_threads = pT->next;
    }
    else
    {
      struct sys_thread* pLT = lwip_system_threads;
      while(pLT->next != pT)
      {
        pLT = pLT->next;
      }
      pLT->next = pT->next;
    }
    SYS_ARCH_UNPROTECT(lev); //Release protection
    SYS_ARCH_PROTECT(lev);
    delete pT;
    SYS_ARCH_UNPROTECT(lev);
    ERR("No mem");
    return;
  }

  DBG("Thread OK");
  return; //Return the thread
}
#if 0
struct sys_timeouts *sys_arch_timeouts(void) {
  struct sys_timeo* timeouts;
  
  SYS_ARCH_DECL_PROTECT(lev);
  
  timeouts = &lwip_system_timeouts; //If there is no match, just return the system-wide default version
  
  SYS_ARCH_PROTECT(lev); //Protect this section
  
  sys_thread_t pT = lwip_system_threads;

  // Search the threads list for the thread that is currently running
  for ( ; pT!=NULL; pT=pT->next)
  {
     if ( Task::isCurrent(pT->pTask) )
     {
      timeouts = pT->timeouts;
     }
  }
  
  SYS_ARCH_UNPROTECT(lev); //Release protection

  return timeouts;
}
#endif
/*
Architecture-specific initialization.
*/
static Timer sys_jiffies_timer;

void sys_init(void)
{
  sys_jiffies_timer.start();
}

/*
Used by PPP as a timestamp-ish value.
*/
u32_t sys_jiffies(void)
{
  static u32_t jiffies = 0;
  jiffies += 1 + sys_jiffies_timer.read_ms()/10;
  sys_jiffies_timer.reset();

  return jiffies;
}

/**
 * Sleep for some ms. Timeouts are NOT processed while sleeping.
 *
 * @param ms number of milliseconds to sleep
 */
//Change DG, arch specific
void
sys_msleep(u32_t ms)
{
  Thread::wait(ms);
}

} //extern "C"