/**************************************************************************/
/*                                                                        */
/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
/*                                                                        */
/*       This software is licensed under the Microsoft Software License   */
/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
/*       and in the root directory of this software.                      */
/*                                                                        */
/**************************************************************************/


/**************************************************************************/
/**************************************************************************/
/**                                                                       */
/** ThreadX Component                                                     */
/**                                                                       */
/**   IAR Multithreaded Library Support                                   */
/**                                                                       */
/**************************************************************************/
/**************************************************************************/

#define TX_SOURCE_CODE


/* Define IAR library for tools prior to version 8.  */

#if (__VER__ < 8000000)


/* IAR version 7 and below.  */

/* Include necessary system files.  */

#include "tx_api.h"
#include "tx_initialize.h"
#include "tx_thread.h"
#include "tx_mutex.h"


/* This implementation requires that the following macros are defined in the 
   tx_port.h file and <yvals.h> is included with the following code segments:
   
#ifdef  TX_ENABLE_IAR_LIBRARY_SUPPORT
#include <yvals.h>
#endif

#ifdef  TX_ENABLE_IAR_LIBRARY_SUPPORT
#define TX_THREAD_EXTENSION_2           VOID    *tx_thread_iar_tls_pointer;          
#else
#define TX_THREAD_EXTENSION_2          
#endif

#ifdef  TX_ENABLE_IAR_LIBRARY_SUPPORT
#define TX_THREAD_CREATE_EXTENSION(thread_ptr)                      thread_ptr -> tx_thread_iar_tls_pointer =  __iar_dlib_perthread_allocate();                                  
#define TX_THREAD_DELETE_EXTENSION(thread_ptr)                      __iar_dlib_perthread_deallocate(thread_ptr -> tx_thread_iar_tls_pointer); \
                                                                    thread_ptr -> tx_thread_iar_tls_pointer =  TX_NULL;            
#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION               __iar_dlib_perthread_access(0);
#else
#define TX_THREAD_CREATE_EXTENSION(thread_ptr)                                  
#define TX_THREAD_DELETE_EXTENSION(thread_ptr)                                  
#endif

    This should be done automatically if TX_ENABLE_IAR_LIBRARY_SUPPORT is defined while building the ThreadX library and the 
    application.  

    Finally, the project options General Options -> Library Configuration should have the "Enable thread support in library" box selected.
*/

#ifdef TX_ENABLE_IAR_LIBRARY_SUPPORT

#include <yvals.h>


#if _MULTI_THREAD

TX_MUTEX    __tx_iar_system_lock_mutexes[_MAX_LOCK];
UINT        __tx_iar_system_lock_next_free_mutex =  0;


/* Define error counters, just for debug purposes.  */

UINT        __tx_iar_system_lock_no_mutexes;
UINT        __tx_iar_system_lock_internal_errors;
UINT        __tx_iar_system_lock_isr_caller;


/* Define the TLS access function for the IAR library.  */

void _DLIB_TLS_MEMORY *__iar_dlib_perthread_access(void _DLIB_TLS_MEMORY *symbp)
{

char _DLIB_TLS_MEMORY   *p = 0;
    
    /* Is there a current thread?  */
    if (_tx_thread_current_ptr)
      p = (char _DLIB_TLS_MEMORY *) _tx_thread_current_ptr -> tx_thread_iar_tls_pointer;
    else
      p = (void _DLIB_TLS_MEMORY *) __segment_begin("__DLIB_PERTHREAD");
    p += __IAR_DLIB_PERTHREAD_SYMBOL_OFFSET(symbp);
    return (void _DLIB_TLS_MEMORY *) p;
}


/* Define mutexes for IAR library.  */

void __iar_system_Mtxinit(__iar_Rmtx *m)
{

UINT        i;
UINT        status;
TX_MUTEX    *mutex_ptr;


    /* First, find a free mutex in the list.  */
    for (i = 0; i < _MAX_LOCK; i++)
    {

        /* Setup a pointer to the start of the next free mutex.  */
        mutex_ptr =  &__tx_iar_system_lock_mutexes[__tx_iar_system_lock_next_free_mutex++];
    
        /* Check for wrap-around on the next free mutex.  */
        if (__tx_iar_system_lock_next_free_mutex >= _MAX_LOCK)
        {
        
            /* Yes, set the free index back to 0.  */
            __tx_iar_system_lock_next_free_mutex =  0;
        }
    
        /* Is this mutex free?  */
        if (mutex_ptr -> tx_mutex_id != TX_MUTEX_ID)
        {
        
            /* Yes, this mutex is free, get out of the loop!  */
            break;
        }
    }

    /* Determine if a free mutex was found.   */
    if (i >= _MAX_LOCK)
    {
    
        /* Error!  No more free mutexes!  */
        
        /* Increment the no mutexes error counter.  */
        __tx_iar_system_lock_no_mutexes++;
        
        /* Set return pointer to NULL.  */
        *m =  TX_NULL;
        
        /* Return.  */
        return;
    }
    
    /* Now create the ThreadX mutex for the IAR library.  */
    status =  _tx_mutex_create(mutex_ptr, "IAR System Library Lock", TX_NO_INHERIT);
    
    /* Determine if the creation was successful.  */
    if (status == TX_SUCCESS)
    {
    
        /* Yes, successful creation, return mutex pointer.  */
        *m =  (VOID *) mutex_ptr;
    }
    else
    {
    
        /* Increment the internal error counter.  */
        __tx_iar_system_lock_internal_errors++;
    
        /* Return a NULL pointer to indicate an error.  */
        *m =  TX_NULL;
    }
}

void __iar_system_Mtxdst(__iar_Rmtx *m)
{

    /* Simply delete the mutex.  */
    _tx_mutex_delete((TX_MUTEX *) *m);
}

void __iar_system_Mtxlock(__iar_Rmtx *m)
{

UINT    status;


    /* Determine the caller's context. Mutex locks are only available from initialization and 
       threads.  */
    if ((_tx_thread_system_state == 0) || (_tx_thread_system_state >= TX_INITIALIZE_IN_PROGRESS))
    {
    
        /* Get the mutex.  */
        status =  _tx_mutex_get((TX_MUTEX *) *m, TX_WAIT_FOREVER);

        /* Check the status of the mutex release.  */
        if (status)
        {
        
            /* Internal error, increment the counter.  */
            __tx_iar_system_lock_internal_errors++;
        }
    }
    else
    {
        
        /* Increment the ISR caller error.  */
        __tx_iar_system_lock_isr_caller++;
    }
}

void __iar_system_Mtxunlock(__iar_Rmtx *m)
{

UINT    status;


    /* Determine the caller's context. Mutex unlocks are only available from initialization and 
       threads.  */
    if ((_tx_thread_system_state == 0) || (_tx_thread_system_state >= TX_INITIALIZE_IN_PROGRESS))
    {
    
        /* Release the mutex.  */
        status =  _tx_mutex_put((TX_MUTEX *) *m);
        
        /* Check the status of the mutex release.  */
        if (status)
        {
        
            /* Internal error, increment the counter.  */
            __tx_iar_system_lock_internal_errors++;
        }
    }
    else
    {
        
        /* Increment the ISR caller error.  */
        __tx_iar_system_lock_isr_caller++;
    }
}


#if _DLIB_FILE_DESCRIPTOR

TX_MUTEX    __tx_iar_file_lock_mutexes[_MAX_FLOCK];
UINT        __tx_iar_file_lock_next_free_mutex =  0;


/* Define error counters, just for debug purposes.  */

UINT        __tx_iar_file_lock_no_mutexes;
UINT        __tx_iar_file_lock_internal_errors;
UINT        __tx_iar_file_lock_isr_caller;


void __iar_file_Mtxinit(__iar_Rmtx *m)
{

UINT        i;
UINT        status;
TX_MUTEX    *mutex_ptr;


    /* First, find a free mutex in the list.  */
    for (i = 0; i < _MAX_FLOCK; i++)
    {

        /* Setup a pointer to the start of the next free mutex.  */
        mutex_ptr =  &__tx_iar_file_lock_mutexes[__tx_iar_file_lock_next_free_mutex++];
    
        /* Check for wrap-around on the next free mutex.  */
        if (__tx_iar_file_lock_next_free_mutex >= _MAX_LOCK)
        {
        
            /* Yes, set the free index back to 0.  */
            __tx_iar_file_lock_next_free_mutex =  0;
        }
    
        /* Is this mutex free?  */
        if (mutex_ptr -> tx_mutex_id != TX_MUTEX_ID)
        {
        
            /* Yes, this mutex is free, get out of the loop!  */
            break;
        }
    }

    /* Determine if a free mutex was found.   */
    if (i >= _MAX_LOCK)
    {
    
        /* Error!  No more free mutexes!  */
        
        /* Increment the no mutexes error counter.  */
        __tx_iar_file_lock_no_mutexes++;
        
        /* Set return pointer to NULL.  */
        *m =  TX_NULL;
        
        /* Return.  */
        return;
    }
    
    /* Now create the ThreadX mutex for the IAR library.  */
    status =  _tx_mutex_create(mutex_ptr, "IAR File Library Lock", TX_NO_INHERIT);
    
    /* Determine if the creation was successful.  */
    if (status == TX_SUCCESS)
    {
    
        /* Yes, successful creation, return mutex pointer.  */
        *m =  (VOID *) mutex_ptr;
    }
    else
    {
    
        /* Increment the internal error counter.  */
        __tx_iar_file_lock_internal_errors++;
    
        /* Return a NULL pointer to indicate an error.  */
        *m =  TX_NULL;
    }
}

void __iar_file_Mtxdst(__iar_Rmtx *m)
{

    /* Simply delete the mutex.  */
    _tx_mutex_delete((TX_MUTEX *) *m);
}

void __iar_file_Mtxlock(__iar_Rmtx *m)
{

UINT    status;


    /* Determine the caller's context. Mutex locks are only available from initialization and 
       threads.  */
    if ((_tx_thread_system_state == 0) || (_tx_thread_system_state >= TX_INITIALIZE_IN_PROGRESS))
    {
    
        /* Get the mutex.  */
        status =  _tx_mutex_get((TX_MUTEX *) *m, TX_WAIT_FOREVER);

        /* Check the status of the mutex release.  */
        if (status)
        {
        
            /* Internal error, increment the counter.  */
            __tx_iar_file_lock_internal_errors++;
        }
    }
    else
    {
        
        /* Increment the ISR caller error.  */
        __tx_iar_file_lock_isr_caller++;
    }
}

void __iar_file_Mtxunlock(__iar_Rmtx *m)
{

UINT    status;


    /* Determine the caller's context. Mutex unlocks are only available from initialization and 
       threads.  */
    if ((_tx_thread_system_state == 0) || (_tx_thread_system_state >= TX_INITIALIZE_IN_PROGRESS))
    {
    
        /* Release the mutex.  */
        status =  _tx_mutex_put((TX_MUTEX *) *m);
        
        /* Check the status of the mutex release.  */
        if (status)
        {
        
            /* Internal error, increment the counter.  */
            __tx_iar_file_lock_internal_errors++;
        }
    }
    else
    {
        
        /* Increment the ISR caller error.  */
        __tx_iar_file_lock_isr_caller++;
    }
}
#endif  /* _DLIB_FILE_DESCRIPTOR */

#endif  /* _MULTI_THREAD  */

#endif  /* TX_ENABLE_IAR_LIBRARY_SUPPORT  */

#else   /* IAR version 8 and above.  */


/* Include necessary system files.  */

#include "tx_api.h"
#include "tx_initialize.h"
#include "tx_thread.h"
#include "tx_mutex.h"

/* This implementation requires that the following macros are defined in the 
   tx_port.h file and <yvals.h> is included with the following code segments:
   
#ifdef  TX_ENABLE_IAR_LIBRARY_SUPPORT
#include <yvals.h>
#endif

#ifdef  TX_ENABLE_IAR_LIBRARY_SUPPORT
#define TX_THREAD_EXTENSION_2           VOID    *tx_thread_iar_tls_pointer;          
#else
#define TX_THREAD_EXTENSION_2          
#endif

#ifdef  TX_ENABLE_IAR_LIBRARY_SUPPORT
void    *_tx_iar_create_per_thread_tls_area(void);
void    _tx_iar_destroy_per_thread_tls_area(void *tls_ptr);
void    __iar_Initlocks(void);

#define TX_THREAD_CREATE_EXTENSION(thread_ptr)                      thread_ptr -> tx_thread_iar_tls_pointer =  __iar_dlib_perthread_allocate();                                  
#define TX_THREAD_DELETE_EXTENSION(thread_ptr)                      do {__iar_dlib_perthread_deallocate(thread_ptr -> tx_thread_iar_tls_pointer); \
                                                                        thread_ptr -> tx_thread_iar_tls_pointer =  TX_NULL; } while(0);
#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION               do {__iar_Initlocks();} while(0);
#else
#define TX_THREAD_CREATE_EXTENSION(thread_ptr)                                  
#define TX_THREAD_DELETE_EXTENSION(thread_ptr)                                  
#endif

    This should be done automatically if TX_ENABLE_IAR_LIBRARY_SUPPORT is defined while building the ThreadX library and the 
    application.  

    Finally, the project options General Options -> Library Configuration should have the "Enable thread support in library" box selected.
*/

#ifdef TX_ENABLE_IAR_LIBRARY_SUPPORT

#include <DLib_threads.h>


void * __aeabi_read_tp();

void* _tx_iar_create_per_thread_tls_area();
void _tx_iar_destroy_per_thread_tls_area(void *tls_ptr);

#pragma section="__iar_tls$$DATA"

/* Define the TLS access function for the IAR library.  */
void * __aeabi_read_tp(void)
{
  void *p = 0;
  TX_THREAD *thread_ptr = _tx_thread_current_ptr;
  if (thread_ptr)
  {
    p = thread_ptr->tx_thread_iar_tls_pointer;      
  }
  else
  {
    p = __section_begin("__iar_tls$$DATA");
  }
  return p;
}

/* Define the TLS creation and destruction to use malloc/free.  */

void* _tx_iar_create_per_thread_tls_area()
{
  UINT tls_size = __iar_tls_size();  
  
  /* Get memory for TLS.  */  
  void *p = malloc(tls_size);

  /* Initialize TLS-area and run constructors for objects in TLS */
  __iar_tls_init(p);
  return p;
}

void _tx_iar_destroy_per_thread_tls_area(void *tls_ptr)
{
  /* Destroy objects living in TLS */
  __call_thread_dtors();
  free(tls_ptr);  
}

#ifndef _MAX_LOCK
#define _MAX_LOCK 4
#endif

static TX_MUTEX    __tx_iar_system_lock_mutexes[_MAX_LOCK];
static UINT        __tx_iar_system_lock_next_free_mutex =  0;


/* Define error counters, just for debug purposes.  */

UINT        __tx_iar_system_lock_no_mutexes;
UINT        __tx_iar_system_lock_internal_errors;
UINT        __tx_iar_system_lock_isr_caller;


/* Define mutexes for IAR library.  */

void __iar_system_Mtxinit(__iar_Rmtx *m)
{

UINT        i;
UINT        status;
TX_MUTEX    *mutex_ptr;


    /* First, find a free mutex in the list.  */
    for (i = 0; i < _MAX_LOCK; i++)
    {

        /* Setup a pointer to the start of the next free mutex.  */
        mutex_ptr =  &__tx_iar_system_lock_mutexes[__tx_iar_system_lock_next_free_mutex++];
    
        /* Check for wrap-around on the next free mutex.  */
        if (__tx_iar_system_lock_next_free_mutex >= _MAX_LOCK)
        {
        
            /* Yes, set the free index back to 0.  */
            __tx_iar_system_lock_next_free_mutex =  0;
        }
    
        /* Is this mutex free?  */
        if (mutex_ptr -> tx_mutex_id != TX_MUTEX_ID)
        {
        
            /* Yes, this mutex is free, get out of the loop!  */
            break;
        }
    }

    /* Determine if a free mutex was found.   */
    if (i >= _MAX_LOCK)
    {
    
        /* Error!  No more free mutexes!  */
        
        /* Increment the no mutexes error counter.  */
        __tx_iar_system_lock_no_mutexes++;
        
        /* Set return pointer to NULL.  */
        *m =  TX_NULL;
        
        /* Return.  */
        return;
    }
    
    /* Now create the ThreadX mutex for the IAR library.  */
    status =  _tx_mutex_create(mutex_ptr, "IAR System Library Lock", TX_NO_INHERIT);
    
    /* Determine if the creation was successful.  */
    if (status == TX_SUCCESS)
    {
    
        /* Yes, successful creation, return mutex pointer.  */
        *m =  (VOID *) mutex_ptr;
    }
    else
    {
    
        /* Increment the internal error counter.  */
        __tx_iar_system_lock_internal_errors++;
    
        /* Return a NULL pointer to indicate an error.  */
        *m =  TX_NULL;
    }
}

void __iar_system_Mtxdst(__iar_Rmtx *m)
{

    /* Simply delete the mutex.  */
    _tx_mutex_delete((TX_MUTEX *) *m);
}

void __iar_system_Mtxlock(__iar_Rmtx *m)
{
  if (*m)
  {  
    UINT    status;

    /* Determine the caller's context. Mutex locks are only available from initialization and 
       threads.  */
    if ((_tx_thread_system_state == 0) || (_tx_thread_system_state >= TX_INITIALIZE_IN_PROGRESS))
    {
    
        /* Get the mutex.  */
        status =  _tx_mutex_get((TX_MUTEX *) *m, TX_WAIT_FOREVER);

        /* Check the status of the mutex release.  */
        if (status)
        {
        
            /* Internal error, increment the counter.  */
            __tx_iar_system_lock_internal_errors++;
        }
    }
    else
    {
        
        /* Increment the ISR caller error.  */
        __tx_iar_system_lock_isr_caller++;
    }
  }
}

void __iar_system_Mtxunlock(__iar_Rmtx *m)
{
  if (*m)
  {
    UINT    status;

    /* Determine the caller's context. Mutex unlocks are only available from initialization and 
       threads.  */
    if ((_tx_thread_system_state == 0) || (_tx_thread_system_state >= TX_INITIALIZE_IN_PROGRESS))
    {
    
        /* Release the mutex.  */
        status =  _tx_mutex_put((TX_MUTEX *) *m);
        
        /* Check the status of the mutex release.  */
        if (status)
        {
        
            /* Internal error, increment the counter.  */
            __tx_iar_system_lock_internal_errors++;
        }
    }
    else
    {
        
        /* Increment the ISR caller error.  */
        __tx_iar_system_lock_isr_caller++;
    }
  }
}


#if _DLIB_FILE_DESCRIPTOR

#include <stdio.h>                        /* Added to get access to FOPEN_MAX */
#ifndef _MAX_FLOCK
#define _MAX_FLOCK FOPEN_MAX              /* Define _MAX_FLOCK as the maximum number of open files */
#endif


TX_MUTEX    __tx_iar_file_lock_mutexes[_MAX_FLOCK];
UINT        __tx_iar_file_lock_next_free_mutex =  0;


/* Define error counters, just for debug purposes.  */

UINT        __tx_iar_file_lock_no_mutexes;
UINT        __tx_iar_file_lock_internal_errors;
UINT        __tx_iar_file_lock_isr_caller;


void __iar_file_Mtxinit(__iar_Rmtx *m)
{

UINT        i;
UINT        status;
TX_MUTEX    *mutex_ptr;


    /* First, find a free mutex in the list.  */
    for (i = 0; i < _MAX_FLOCK; i++)
    {

        /* Setup a pointer to the start of the next free mutex.  */
        mutex_ptr =  &__tx_iar_file_lock_mutexes[__tx_iar_file_lock_next_free_mutex++];
    
        /* Check for wrap-around on the next free mutex.  */
        if (__tx_iar_file_lock_next_free_mutex >= _MAX_LOCK)
        {
        
            /* Yes, set the free index back to 0.  */
            __tx_iar_file_lock_next_free_mutex =  0;
        }
    
        /* Is this mutex free?  */
        if (mutex_ptr -> tx_mutex_id != TX_MUTEX_ID)
        {
        
            /* Yes, this mutex is free, get out of the loop!  */
            break;
        }
    }

    /* Determine if a free mutex was found.   */
    if (i >= _MAX_LOCK)
    {
    
        /* Error!  No more free mutexes!  */
        
        /* Increment the no mutexes error counter.  */
        __tx_iar_file_lock_no_mutexes++;
        
        /* Set return pointer to NULL.  */
        *m =  TX_NULL;
        
        /* Return.  */
        return;
    }
    
    /* Now create the ThreadX mutex for the IAR library.  */
    status =  _tx_mutex_create(mutex_ptr, "IAR File Library Lock", TX_NO_INHERIT);
    
    /* Determine if the creation was successful.  */
    if (status == TX_SUCCESS)
    {
    
        /* Yes, successful creation, return mutex pointer.  */
        *m =  (VOID *) mutex_ptr;
    }
    else
    {
    
        /* Increment the internal error counter.  */
        __tx_iar_file_lock_internal_errors++;
    
        /* Return a NULL pointer to indicate an error.  */
        *m =  TX_NULL;
    }
}

void __iar_file_Mtxdst(__iar_Rmtx *m)
{

    /* Simply delete the mutex.  */
    _tx_mutex_delete((TX_MUTEX *) *m);
}

void __iar_file_Mtxlock(__iar_Rmtx *m)
{

UINT    status;


    /* Determine the caller's context. Mutex locks are only available from initialization and 
       threads.  */
    if ((_tx_thread_system_state == 0) || (_tx_thread_system_state >= TX_INITIALIZE_IN_PROGRESS))
    {
    
        /* Get the mutex.  */
        status =  _tx_mutex_get((TX_MUTEX *) *m, TX_WAIT_FOREVER);

        /* Check the status of the mutex release.  */
        if (status)
        {
        
            /* Internal error, increment the counter.  */
            __tx_iar_file_lock_internal_errors++;
        }
    }
    else
    {
        
        /* Increment the ISR caller error.  */
        __tx_iar_file_lock_isr_caller++;
    }
}

void __iar_file_Mtxunlock(__iar_Rmtx *m)
{

UINT    status;


    /* Determine the caller's context. Mutex unlocks are only available from initialization and 
       threads.  */
    if ((_tx_thread_system_state == 0) || (_tx_thread_system_state >= TX_INITIALIZE_IN_PROGRESS))
    {
    
        /* Release the mutex.  */
        status =  _tx_mutex_put((TX_MUTEX *) *m);
        
        /* Check the status of the mutex release.  */
        if (status)
        {
        
            /* Internal error, increment the counter.  */
            __tx_iar_file_lock_internal_errors++;
        }
    }
    else
    {
        
        /* Increment the ISR caller error.  */
        __tx_iar_file_lock_isr_caller++;
    }
}
#endif  /* _DLIB_FILE_DESCRIPTOR */

#endif  /* TX_ENABLE_IAR_LIBRARY_SUPPORT  */

#endif /* IAR version 8 and above.  */
