CifX API Configuration

The cifX Toolkit configuration can use newlib and FreeRTOS functions/ objects. The changed cifX Toolkit OS Abstraction is:

CifXToolkit_OS_FreeRTOS.c
/**************************************************************************************
 Exclusion of Liability for this demo software:
 The following software is intended for and must only be used for reference and in an
 evaluation laboratory environment. It is provided without charge and is subject to
 alterations. There is no warranty for the software, to the extent permitted by
 applicable law. Except when otherwise stated in writing the copyright holders and/or
 other parties provide the software "as is" without warranty of any kind, either
 expressed or implied.
 Please refer to the Agreement in README_DISCLAIMER.txt, provided together with this file!
 By installing or otherwise using the software, you accept the terms of this Agreement.
 If you do not agree to the terms of this Agreement, then do not install or use the
 Software!
 **************************************************************************************/

/**************************************************************************************

Copyright (c) Hilscher Gesellschaft fuer Systemautomation mbH. All Rights Reserved.

***************************************************************************************

  $Id: cifXToolkit_OS-freeRTOS.c 14146 2021-07-27 13:25:12Z LContreras $:

  Description:
    OS abstraction function implementation for FreeRTOS on netX90 APP

  Changes:
    Date        Description
    -----------------------------------------------------------------------------------
    2021-06-18  initial port for FreeRTOS 10.2.0 running netX90 APP CPU

**************************************************************************************/

#include <stdlib.h>
#include <stdio.h>

#include "OS_Includes.h"

#include <string.h>
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>


#include "cifXToolkit.h"
#include "cifXErrors.h"
//#include "OS_Dependent.h"

/* netX90 APP CPU */
#include "netx90_app.h" /* NVIC_* helpers, idpm_com_host_IRQn */

/* OS kernel includes */
#include "FreeRTOS.h"
#include "task.h"     /* Sleep, kernel tick, critical section */
#include "semphr.h"   /* Mutex, binary semaphore for Event */

static int s_fOSInitDone = 0; /*<! Status initialisation flag of this module */


/* Interrupt members (static variables sufficient as only one DPM is handled) */
static DEVICEINSTANCE* s_ptIrqDevInst = NULL; /*<! Interrupt context */
static TaskHandle_t    s_xDsrTask     = NULL; /*<! DSR task handle, notified by DPM IRQ */
static bool            s_fDSRActive   = false;


/*
* NOTE: The type of BaseType_t is set by the port, hence it may differ.
* This module only uses this type internally and thus should be compatible with different types.
*  The netX90 APP CPI port defines it as 'long' (see portmacro.h of FreeRTOS).
*/

/*****************************************************************************/
/*! O/S Specific initialization
*     \return CIFX_NO_ERROR on success                                       */
/*****************************************************************************/
long OS_Init(void)
{
  s_fOSInitDone = 1;

  return CIFX_NO_ERROR;
}

/*****************************************************************************/
/*! O/S Specific de-initialization (de-initializes libpciaccess)             */
/*****************************************************************************/
void OS_Deinit(void)
{
  s_fOSInitDone = 0;
}


/*****************************************************************************/
/*****************************************************************************/
/*!<                          Memory functions                               */
/*****************************************************************************/
/*****************************************************************************/

/*
*  Although FreeRTOS provides dedicated functions for heap memory handling such
*  as vPortMalloc() and vPortFree(), there are different memory allocation schemes
*  available to choose from (not to mention custom implementations) each with
*  varying degree of properties and functionality.
*
*  Some ports do not supply a realloc() or memmove() function, which are expected
*  by cifX Toolkit. These are used in the scope of handling multiple devices
*  which this port does not support (1x iDPM only).
*
*  As such, the heap memory functions by the C library are used here.
*/
/*****************************************************************************/
/*! Memory allocation wrapper (standard malloc)
*     \param ulSize Size of block to allocate
*     \return NULL on failure                                                */
/*****************************************************************************/
void* OS_Memalloc(uint32_t ulSize)
{
  return malloc(ulSize);
}

/*****************************************************************************/
/*! Memory de-allocation wrapper (standard free)
*     \param pvMem  Block to free                                            */
/*****************************************************************************/
void OS_Memfree(void* pvMem)
{
  free(pvMem);
}

/*****************************************************************************/
/*! Memory resize wrapper (standard realloc)
*     \param pvMem      Block to resize
*     \param ulNewSize  New size of the block
*     \return NULL on error                                                  */
/*****************************************************************************/
void* OS_Memrealloc(void* pvMem, uint32_t ulNewSize)
{
  return realloc(pvMem, ulNewSize);
}

/*****************************************************************************/
/*! Memset wrapper
*     \param pvMem   Memory to set
*     \param bFill   Fill byte
*     \param ulSize  Size of the fill block                                  */
/*****************************************************************************/
void OS_Memset(void* pvMem, uint8_t bFill, uint32_t ulSize)
{
  memset(pvMem, bFill, ulSize);
}

/*****************************************************************************/
/*! Memcopy wrapper
*     \param pvDest  Destination pointer
*     \param pvSrc   Source pointer
*     \param ulSize  Size to copy                                            */
/*****************************************************************************/
void OS_Memcpy(void* pvDest, void* pvSrc, uint32_t ulSize)
{
  memcpy(pvDest, pvSrc, ulSize);
}

/*****************************************************************************/
/*! Memcompare wrapper
*     \param pvBuf1  First compare buffer
*     \param pvBuf2  Second compare buffer
*     \param ulSize  Size to compare
*     \return 0 if blocks are equal                                          */
/*****************************************************************************/
int OS_Memcmp(void* pvBuf1, void* pvBuf2, uint32_t ulSize)
{
  return memcmp(pvBuf1, pvBuf2, ulSize);
}

/*****************************************************************************/
/*! Memmove wrapper (Overlapping memory copy)
*     \param pvDest  Destination buffer
*     \param pvSrc   Source buffer
*     \param ulSize  Size to move                                            */
/*****************************************************************************/
void OS_Memmove(void* pvDest, void* pvSrc, uint32_t ulSize)
{
  memmove(pvDest, pvSrc, ulSize);
}


/*****************************************************************************/
/*****************************************************************************/
/*!<                         Timing functions                                */
/*****************************************************************************/
/*****************************************************************************/

/*
*
* cifX Toolkit API is working with millisecond increments and thus requires a timer resolution of at least 1kHz.
*
* This implementation excepts the kernel tick to be set appropriately, otherwise
* custom implementation is needed facilitating a hardware clock or similar.
*
* If FreeRTOS' configuration configTICK_RATE_HZ is set to 1000, FreeRTOS ticks may be used.
* In case this value exceeds 1000Hz, limitation of macro pdMS_TO_TICKS() need
* to be considered and the calculation to convert timer ticks to ms needs updating.
*
* vTaskDelay( requires INCLUDE_vTaskDelay to be set.
*
* xTaskGetTickCount()
* As the toolkit operates on 32bit timeout values, 16bit timer resolutions
* should be avoided in the first place.
* In case configUSE_16_BIT_TICKS is set, casting and overflow handling needs to added
* to guarantee expected handling.
*
*/

/*****************************************************************************/
/*! Get Millisecond counter value (used for timeout handling)
*     \return Counter value with a resolution of 1ms                         */
/*****************************************************************************/
uint32_t OS_GetMilliSecCounter(void)
{

#if ((1000 == configTICK_RATE_HZ) && !configUSE_16_BIT_TICKS)
  TickType_t tTicks = xTaskGetTickCount();
  return (uint32_t)tTicks;
#else
  #error OS_Sleep(): Custom timer needed
#endif /* configTICK_RATE_HZ, !configUSE_16_BIT_TICKS */

}

/*****************************************************************************/
/*! Sleep for the given time
*     \param ulSleepTimeMs Time in ms to sleep (0 will sleep for 50us)       */
/*****************************************************************************/
void OS_Sleep(uint32_t ulSleepTimeMs)
{
#if ((1000 == configTICK_RATE_HZ) && !configUSE_16_BIT_TICKS)
  vTaskDelay(pdMS_TO_TICKS(ulSleepTimeMs));
#else
  #error OS_Sleep(): Custom timer needed
#endif /* configTICK_RATE_HZ, !configUSE_16_BIT_TICKS */
}

/*****************************************************************************/
/*****************************************************************************/
/*!<                         Interrupt handling                              */
/*****************************************************************************/
/*****************************************************************************/

/*****************************************************************************/
/*! DSR task evaluating DPM changes.
*   Function should be instantiated as a high priority task
*   (all cifX API calls should be lower that its priority).
*     \param pvParameters   User parameter (unused)                          */
/*****************************************************************************/
void CIFX_DSRHandler(void* pvParameters)
{
  while(s_ptIrqDevInst)
  {
    (void)ulTaskNotifyTake(pdTRUE, /* Clear the notification value on exit */
                           portMAX_DELAY);/* Block indefinitely */
    if(s_fDSRActive)
    {
      cifXTKitDSRHandler(s_ptIrqDevInst);
    }
    else
      break;
  }

  vTaskDelete(s_xDsrTask);
  s_xDsrTask = NULL;
}

/*****************************************************************************/
/*! IRQ handler for DPM interrupt.
*   Fixed location in NVIC (idpm_com_host_IRQn).                             */
/*****************************************************************************/
void IDPM_IRQHandler(void)
{
  /* Prohibit any call if toolkit is not fully configured */
  if(!s_fOSInitDone || !s_ptIrqDevInst || !s_xDsrTask)
  {
    /* Spurious IRQ, disable if detected */
    NVIC_DisableIRQ(idpm_com_host_IRQn);
  }
  else
  {
    int iResult = 0;
    iResult = cifXTKitISRHandler(s_ptIrqDevInst, 1); /* Ignore register block as we are netX90 APP */

    /* "The DSR is expected to be interruptible and will process the interrupt events in non-interrupt mode."
       -> Signal to DSR thread */
    if(CIFX_TKIT_IRQ_DSR_REQUESTED == iResult)
    {
      BaseType_t xHigherPriorityTaskWoken = pdFALSE;

      /* Unblock the handling task so the task can perform any processing necessitated
      by the interrupt. s_xDsrTask is the task's handle, which was obtained
      when the task was created. */
      vTaskNotifyGiveFromISR(s_xDsrTask, &xHigherPriorityTaskWoken);

      /* Force a context switch if xHigherPriorityTaskWoken is now set to pdTRUE. */
      portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
    }
  }
}

/*****************************************************************************/
/*! Enable interrupts on the given device.
*   Achieved by creating an DSR service task which is called by ISR handler
*   (the latter is installed in NVIC).
*     \param pvOSDependent Pointer to internal device structure              */
/*****************************************************************************/
void OS_EnableInterrupts(void* pvOSDependent)
{
  s_fDSRActive = true;

  (void)xTaskCreate(CIFX_DSRHandler,
                    "CIFXDSR",
                    configMINIMAL_STACK_SIZE, /* Depending on the application usage, more Stack may be needed */
                    (void*) NULL,
                    CIFX_INTERRUPT_DSR_TASK_PRIORITY,
                    &s_xDsrTask);

  /* Store context locally of ISR/DSR */
  s_ptIrqDevInst = (DEVICEINSTANCE*)pvOSDependent;

  /* Only used the shared interrupt */
  NVIC_SetPriority(idpm_com_host_IRQn, NETX90_IDPM_IRQ_PRIORITY);
  NVIC_EnableIRQ(idpm_com_host_IRQn);
}

/*****************************************************************************/
/*! Disable interrupts on the given device
*     \param pvOSDependent Pointer to internal device structure              */
/*****************************************************************************/
void OS_DisableInterrupts(void* pvOSDependent)
{
  UNREFERENCED_PARAMETER(pvOSDependent);

  /* Disable physical interrupt of DPM */
  NVIC_DisableIRQ(idpm_com_host_IRQn);
  NVIC_SetPriority(idpm_com_host_IRQn, 0); /* Reset priority to default */

  /* Trigger deinit of DSR task */
  s_fDSRActive = false;
  (void)xTaskNotifyGive(s_xDsrTask);

  taskYIELD();

  /* Task handle is deleted in DSR epilogue */

  s_ptIrqDevInst = NULL;
}

/*****************************************************************************/
/*****************************************************************************/
/*!<                   Mutex functions (channel access)                      */
/*****************************************************************************/
/*****************************************************************************/

/*****************************************************************************/
/*! Create mutex
*     \return Handle to new created mutex                                    */
/*****************************************************************************/
void* OS_CreateMutex(void)
{
  return (void*) xSemaphoreCreateMutex();
}

/*****************************************************************************/
/*! Try to acquire mutex with timeout
*     \param pvMutex   Handle to mutex
*     \param ulTimeout Timeout in ms to wait for mutex
*     \return !=0 if mutex was acquired                                      */
/*****************************************************************************/
int OS_WaitMutex(void* pvMutex, uint32_t ulTimeout)
{
  BaseType_t tRes;

#if ((1000 == configTICK_RATE_HZ) && !configUSE_16_BIT_TICKS)
  tRes = xSemaphoreTake((SemaphoreHandle_t)pvMutex, (TickType_t)pdMS_TO_TICKS(ulTimeout));
#else
  #error OS_WaitMutex(): Custom timer needed
#endif /* configTICK_RATE_HZ, !configUSE_16_BIT_TICKS */

  /* Map return value for cifX Toolkit */
  if(pdPASS == tRes)
    return 1;
  else
    return 0;

}

/*****************************************************************************/
/*! Release previously acquired mutex
*     \param pvMutex   Handle to mutex                                       */
/*****************************************************************************/
void OS_ReleaseMutex(void* pvMutex)
{
  (void)xSemaphoreGive((SemaphoreHandle_t)pvMutex);
}

/*****************************************************************************/
/*! Delete mutex
*     \param pvMutex   Handle to mutex                                       */
/*****************************************************************************/
void OS_DeleteMutex(void* pvMutex)
{
  vSemaphoreDelete((SemaphoreHandle_t)pvMutex);
}

/*****************************************************************************/
/*****************************************************************************/
/*!<                        Lock functions                                   */
/*****************************************************************************/
/*****************************************************************************/

/*****************************************************************************/
/*! Create Lock (Usually same as mutex, but does not support timed waiting)
*     \return Handle to created lock                                         */
/*****************************************************************************/
void* OS_CreateLock(void)
{
  /* Ideally, any task which calls cifX API functions requiring a lock (ultimately changes to HSK cells)
   * should be running in task mode.
   * While changing the implementation of OS_*Lock() functions to work in interrupt mode could be done,
   * it should be seen as application specific.
   * Special care needs to be taken to be fully IRQ safe (beware of e.g. configMAX_SYSCALL_INTERRUPT_PRIORITY
   * and other FreeRTOS/IRQ setting!).
   */

  /* For critical sections, no initialisation is needed, so return a valid placeholder. */
  return (void*)1;
}

/*****************************************************************************/
/*! Acquire a lock
*     \param pvLock Handle to lock                                           */
/*****************************************************************************/
void OS_EnterLock(void* pvLock)
{
  /* Taken from FreeRTOS 10.2. documentation (taskENTER_CRITICAL()):
   * "Preemptive context switches only occur inside an interrupt, so will not occur when interrupts are disabled.
   * Therefore, the task that called taskENTER_CRITICAL() is guaranteed to remain in the Running state until
   * the critical section is exited, unless the task explicitly attempts to block or yield
   * (which it should not do from inside a critical section)."
  */

  taskENTER_CRITICAL();
}

/*****************************************************************************/
/*! Release a lock
*     \param pvLock Handle to lock                                           */
/*****************************************************************************/
void OS_LeaveLock(void* pvLock)
{
  taskEXIT_CRITICAL();
}

/*****************************************************************************/
/*! Delete a lock
*     \param pvLock Handle to lock                                           */
/*****************************************************************************/
void OS_DeleteLock(void* pvLock)
{
  UNREFERENCED_PARAMETER(pvLock);
}


/*****************************************************************************/
/*****************************************************************************/
/*!<             Event functions (signalled from DSR)                        */
/*****************************************************************************/
/*****************************************************************************/

/*****************************************************************************/
/*! Create event
*     \return Handle to created event                                        */
/*****************************************************************************/
void* OS_CreateEvent(void)
{
  SemaphoreHandle_t tSem = xSemaphoreCreateBinary();
  return (void*)tSem;
}

/*****************************************************************************/
/*! Signal event
*     \param pvEvent Handle to event                                         */
/*****************************************************************************/
void OS_SetEvent(void* pvEvent)
{
  (void)xSemaphoreGive((SemaphoreHandle_t)pvEvent);
}

/*****************************************************************************/
/*! Reset event
*     \param pvEvent Handle to event                                         */
/*****************************************************************************/
void OS_ResetEvent(void* pvEvent)
{
  /* no equivalent available and not used by toolkit (currently) */
  UNREFERENCED_PARAMETER(pvEvent);
}

/*****************************************************************************/
/*! Delete event
*     \param pvEvent Handle to event                                         */
/*****************************************************************************/
void OS_DeleteEvent(void* pvEvent)
{
  vSemaphoreDelete((SemaphoreHandle_t)pvEvent);
}

/*****************************************************************************/
/*! Wait for event
*     \param pvEvent   Handle to event
*     \param ulTimeout Timeout in ms to wait for event
*     \return CIFX_EVENT_SIGNALLED if event was set, CIFX_EVENT_TIMEOUT otherwise */
/*****************************************************************************/
uint32_t OS_WaitEvent(void* pvEvent, uint32_t ulTimeout)
{
  BaseType_t tRes;

#if ((1000 == configTICK_RATE_HZ) && !configUSE_16_BIT_TICKS)
  tRes = xSemaphoreTake((SemaphoreHandle_t)pvEvent, (TickType_t)pdMS_TO_TICKS(ulTimeout));
#else
  #error OS_WaitEvent(): Custom timer needed
#endif /* configTICK_RATE_HZ, !configUSE_16_BIT_TICKS */

  /* Map return value for cifX Toolkit */
  if(pdPASS == tRes)
    return CIFX_EVENT_SIGNALLED;
  else
    return CIFX_EVENT_TIMEOUT;

}

/*****************************************************************************/
/*****************************************************************************/
/*!<                      1:1 memory mapping                                 */
/*****************************************************************************/
/*****************************************************************************/

/*****************************************************************************/
/*! Map driver pointer to user space
*     \param pvDriverMem   Pointer to driver memory
*     \param ulMemSize     Size of the memory to map
*     \param ppvMappedMem  Returned mapped pointer
*     \param pvOSDependent OS Dependent parameter in DEVICEINSTANCE
*     \return Handle to mapping, NULL on error                               */
/*****************************************************************************/
void* OS_MapUserPointer(void* pvDriverMem, uint32_t ulMemSize, void** ppvMappedMem, void* pvOSDependent)
{
  UNREFERENCED_PARAMETER(ulMemSize);
  UNREFERENCED_PARAMETER(pvOSDependent);

  /* No memory mapping needed (single address space) */
  *ppvMappedMem = pvDriverMem;

  return pvDriverMem;
}

/*****************************************************************************/
/*! Unmap previously mapped user space pointer
*     \param phMapping     Handle returned from OS_MapUserPointer
*     \param pvOSDependent OS Dependent parameter in DEVICEINSTANCE
*     \return 0 on error                                                     */
/*****************************************************************************/
int OS_UnmapUserPointer(void* phMapping, void* pvOSDependent)
{
  UNREFERENCED_PARAMETER(phMapping);
  UNREFERENCED_PARAMETER(pvOSDependent);

  /* No memory mapping needed (single address space) */
  return 1;
}

/*****************************************************************************/
/*****************************************************************************/
/*!<                String operations from libc                              */
/*****************************************************************************/
/*****************************************************************************/

/*****************************************************************************/
/*! Compare strings
*     \param pszBuf1  String buffer 1
*     \param pszBuf2  String buffer 2
*     \return 0 if strings are equal                                         */
/*****************************************************************************/
int OS_Strcmp(const char* pszBuf1, const char* pszBuf2)
{
  return strcmp(pszBuf1, pszBuf2);
}

/*****************************************************************************/
/*! Compare strings case insensitive
*     \param pszBuf1  String buffer 1
*     \param pszBuf2  String buffer 2
*     \param ulLen    Maximum length to compare
*     \return 0 if strings are equal                                         */
/*****************************************************************************/
int OS_Strnicmp(const char* pszBuf1, const char* pszBuf2, uint32_t ulLen)
{
  return strncasecmp(pszBuf1, pszBuf2, ulLen);
}

/*****************************************************************************/
/*! Get length of string
*     \param szText  Text buffer
*     \return Length of given string                                         */
/*****************************************************************************/
int OS_Strlen(const char* szText)
{
  return strlen(szText);
}

/*****************************************************************************/
/*! Copy string to destination buffer
*     \param szText   Destination string
*     \param szSource Source string
*     \param ulLen    Maximum length to copy
*     \return Pointer to szDest                                              */
/*****************************************************************************/
char* OS_Strncpy(char* szDest, const char* szSource, uint32_t ulLen)
{
  return strncpy(szDest, szSource, ulLen);
}

#ifdef CIFX_TOOLKIT_ENABLE_DSR_LOCK
/*****************************************************************************/
/*! This functions needs to provide a lock against the interrupt service
*   routine of the device. The easiest way is an IRQ lock but some operating
*   systems provide a way to lock against a specific interrupt
*     \param pvODDependent OS Dependent parameter in DEVICEINSTANCE          */
/*****************************************************************************/
void OS_IrqLock(void* pvOSDependent)
{
  UNREFERENCED_PARAMETER(pvOSDependent);

  /* Only mask DPM interrupt if it was configured */
  if(s_ptIrqDevInst)
    NVIC_DisableIRQ(idpm_com_host_IRQn);
}

/*****************************************************************************/
/*! This function re-enables the device's interrupt service routine.
*     \param pvODDependent OS Dependent parameter in DEVICEINSTANCE          */
/*****************************************************************************/
void OS_IrqUnlock(void* pvOSDependent)
{
  UNREFERENCED_PARAMETER(pvOSDependent);

  if(s_ptIrqDevInst)
    NVIC_EnableIRQ(idpm_com_host_IRQn);
}
#endif


/*****************************************************************************/
/*****************************************************************************/
/*****************************************************************************/
/*!<  Stubs needed to build, but not needed or available on netX90 APP CPU   */
/*****************************************************************************/
/*****************************************************************************/
/*****************************************************************************/

/*****************************************************************************/
/*! Read PCI configuration area of specified card
*     \param pvOSDependent OS Dependent parameter to identify card
*     \return Pointer to configuration data (passed to WritePCIConfig)       */
/*****************************************************************************/
void* OS_ReadPCIConfig(void* pvOSDependent)
{
  /* No PCI(e) hardware */
  UNREFERENCED_PARAMETER(pvOSDependent);
  return NULL;
}

/*****************************************************************************/
/*! Restore PCI configuration
*     \param pvOSDependent OS Dependent parameter to identify card
*     \param pvPCIConfig   Pointer returned from ReadPCIConfig               */
/*****************************************************************************/
void OS_WritePCIConfig(void* pvOSDependent, void* pvPCIConfig)
{
  /* No PCI(e) hardware */
  UNREFERENCED_PARAMETER(pvOSDependent);
  UNREFERENCED_PARAMETER(pvPCIConfig);
}


/*****************************************************************************/
/*! Open file for reading
*     \param szFile       File to open (including path)
*     \param pulFileSize  Returned size of the file in bytes
*     \return Handle to the file, NULL on failure                            */
/*****************************************************************************/
void* OS_FileOpen(char* szFile, uint32_t* pulFileLen)
{
  UNREFERENCED_PARAMETER(szFile);
  UNREFERENCED_PARAMETER(pulFileLen);
  return NULL;
}

/*****************************************************************************/
/*! Close open file
*     \param pvFile    Handle to the file (acquired by OS_FileOpen)          */
/*****************************************************************************/
void OS_FileClose(void* pvFile)
{
  UNREFERENCED_PARAMETER(pvFile);
}

/*****************************************************************************/
/*! Read data from file
*     \param pvFile    Handle to the file (acquired by OS_FileOpen)
*     \param ulOffset  Offset to read from
*     \param ulSize    Size to read
*     \param pvBuffer  Buffer to read data into
*     \return number of bytes read                                           */
/*****************************************************************************/
uint32_t OS_FileRead(void* pvFile, uint32_t ulOffset, uint32_t ulSize, void* pvBuffer)
{
  UNREFERENCED_PARAMETER(pvFile);
  UNREFERENCED_PARAMETER(ulOffset);
  UNREFERENCED_PARAMETER(ulSize);
  UNREFERENCED_PARAMETER(pvBuffer);
  return 0;
}

/*****************************************************************************/