/**************************************************************************/
/*                                                                        */
/*       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                                                     */
/**                                                                       */
/**   Module Manager                                                      */
/**                                                                       */
/**************************************************************************/
/**************************************************************************/

#define TX_SOURCE_CODE

#include "tx_api.h"
#include "tx_initialize.h"
#include "tx_thread.h"
#include "tx_timer.h"
#include "tx_queue.h"
#include "tx_event_flags.h"
#include "tx_semaphore.h"
#include "tx_mutex.h"
#include "tx_block_pool.h"
#include "tx_byte_pool.h"
#include "txm_module.h"
#include "txm_module_manager_util.h"

#ifdef TX_ENABLE_EVENT_TRACE
#include "tx_trace.h"
#endif

#ifdef TXM_MODULE_ENABLE_FILEX
extern UINT  _txm_module_manager_filex_stop(TXM_MODULE_INSTANCE *module_instance);
#endif

#ifdef TXM_MODULE_ENABLE_NETX
extern UINT  _txm_module_manager_netx_stop(TXM_MODULE_INSTANCE *module_instance);
#endif

#ifdef TXM_MODULE_ENABLE_NETXDUO
extern UINT  _txm_module_manager_netxduo_stop(TXM_MODULE_INSTANCE *module_instance);
#endif

#ifdef TXM_MODULE_ENABLE_GUIX
extern UINT  _txm_module_manager_guix_stop(TXM_MODULE_INSTANCE *module_instance);
#endif

#ifdef TXM_MODULE_ENABLE_USBX
extern UINT  _txm_module_manager_usbx_stop(TXM_MODULE_INSTANCE *module_instance);
#endif

/**************************************************************************/
/*                                                                        */
/*  FUNCTION                                               RELEASE        */
/*                                                                        */
/*    _txm_module_manager_stop                            PORTABLE C      */
/*                                                           6.2.1        */
/*  AUTHOR                                                                */
/*                                                                        */
/*    Scott Larson, Microsoft Corporation                                 */
/*                                                                        */
/*  DESCRIPTION                                                           */
/*                                                                        */
/*    This function stops execution of the specified module.              */
/*                                                                        */
/*  INPUT                                                                 */
/*                                                                        */
/*    module_instance                   Module instance pointer           */
/*                                                                        */
/*  OUTPUT                                                                */
/*                                                                        */
/*    status                            Completion status                 */
/*                                                                        */
/*  CALLS                                                                 */
/*                                                                        */
/*    _tx_block_pool_delete                 Block pool delete             */
/*    _tx_byte_pool_delete                  Byte pool delete              */
/*    _tx_event_flags_delete                Event flags delete            */
/*    _tx_mutex_delete                      Mutex delete                  */
/*    _tx_mutex_get                         Get protection mutex          */
/*    _tx_mutex_put                         Release protection mutex      */
/*    _tx_queue_delete                      Queue delete                  */
/*    _tx_semaphore_delete                  Semaphore delete              */
/*    _tx_thread_delete                     Thread delete                 */
/*    _tx_thread_sleep                      Thread sleep                  */
/*    _tx_thread_system_preempt_check       Check for preemption          */
/*    _tx_thread_terminate                  Thread terminate              */
/*    _tx_timer_delete                      Timer delete                  */
/*    _txm_module_manager_callback_deactivate                             */
/*                                          Deactivate callback           */
/*    _txm_module_manager_object_search     Search for object in module's */
/*                                            allocated object list       */
/*    _txm_module_manager_thread_create     Module thread create          */
/*    [_txm_module_manager_*_stop]          Optional external component   */
/*                                            stop functions              */
/*                                                                        */
/*  CALLED BY                                                             */
/*                                                                        */
/*    Application code                                                    */
/*                                                                        */
/*  RELEASE HISTORY                                                       */
/*                                                                        */
/*    DATE              NAME                      DESCRIPTION             */
/*                                                                        */
/*  09-30-2020      Scott Larson            Initial Version 6.1           */
/*  03-02-2021      Scott Larson            Modified comments, fix        */
/*                                            object delete underflow,    */
/*                                            resulting in version 6.1.5  */
/*  03-08-2023      Scott Larson            Added tx_trace.h include,     */
/*                                            resulting in version 6.2.1  */
/*                                                                        */
/**************************************************************************/
UINT  _txm_module_manager_stop(TXM_MODULE_INSTANCE *module_instance)
{

TX_INTERRUPT_SAVE_AREA

TX_THREAD                       *thread_ptr, *next_thread_ptr;
TX_TIMER                        *timer_ptr, *next_timer_ptr;
TX_QUEUE                        *queue_ptr, *next_queue_ptr;
TX_EVENT_FLAGS_GROUP            *events_ptr, *next_events_ptr;
TX_SEMAPHORE                    *semaphore_ptr, *next_semaphore_ptr;
TX_MUTEX                        *mutex_ptr, *next_mutex_ptr;
TX_BLOCK_POOL                   *block_pool_ptr, *next_block_pool_ptr;
TX_BYTE_POOL                    *byte_pool_ptr, *next_byte_pool_ptr;
UCHAR                           created_by_module;
ULONG                           i;
TXM_MODULE_ALLOCATED_OBJECT     *object_ptr;


    /* Pickup thread pointer.  */
    TX_THREAD_GET_CURRENT(thread_ptr)

    /* Determine if this is a legal request.  */

    /* Is there a current thread?  */
    if (thread_ptr == TX_NULL)
    {

        /* Illegal caller of this service.  */
        return(TX_CALLER_ERROR);
    }

    /* Is the caller an ISR or Initialization?  */
    if (TX_THREAD_GET_SYSTEM_STATE() != 0)
    {

        /* Illegal caller of this service.  */
        return(TX_CALLER_ERROR);
    }

#ifndef TX_TIMER_PROCESS_IN_ISR

    /* Check for invalid caller of this function.  First check for a calling thread.  */
    if (thread_ptr == &_tx_timer_thread)
    {

        /* Invalid caller of this function, return appropriate error code.  */
        return(TX_CALLER_ERROR);
    }
#endif

    /* Determine if the module manager has not been initialized yet.  */
    if (_txm_module_manager_ready != TX_TRUE)
    {

        /* Module manager has not been initialized.  */
        return(TX_NOT_AVAILABLE);
    }

    /* Determine if the module is valid.  */
    if (module_instance == TX_NULL)
    {

        /* Invalid module pointer.  */
        return(TX_PTR_ERROR);
    }

    /* Get module manager protection mutex.  */
    _tx_mutex_get(&_txm_module_manager_mutex, TX_WAIT_FOREVER);

    /* Determine if the module instance is valid.  */
    if (module_instance -> txm_module_instance_id != TXM_MODULE_ID)
    {

        /* Release the protection mutex.  */
        _tx_mutex_put(&_txm_module_manager_mutex);

        /* Invalid module pointer.  */
        return(TX_PTR_ERROR);
    }

    /* Determine if the module instance is in the loaded state.  */
    if (module_instance -> txm_module_instance_state != TXM_MODULE_STARTED)
    {

        /* Release the protection mutex.  */
        _tx_mutex_put(&_txm_module_manager_mutex);

        /* Return error if the module is not ready.  */
        return(TX_START_ERROR);
    }

    /* Set the module state to indicate the module is stopping.  */
    module_instance -> txm_module_instance_state =  TXM_MODULE_STOPPING;

    /* This thread was previously used as the start thread. So first, make sure it is terminated and deleted before doing anything else.  */
    _tx_thread_terminate(&(module_instance -> txm_module_instance_start_stop_thread));
    _tx_thread_delete(&(module_instance -> txm_module_instance_start_stop_thread));

    /* Determine if there is a module stop function.  */
    if (module_instance -> txm_module_instance_stop_thread_entry)
    {

        /* Yes, there is a stop function.  Build a thread for executing the module stop function.  */

        /* Create the module stop thread.  */
        _txm_module_manager_thread_create(&(module_instance -> txm_module_instance_start_stop_thread),
                                          "Module Stop Thread",
                                          module_instance -> txm_module_instance_shell_entry_function,
                                          module_instance -> txm_module_instance_stop_thread_entry,
                                          module_instance -> txm_module_instance_application_module_id,
                                          module_instance -> txm_module_instance_start_stop_stack_start_address,
                                          module_instance -> txm_module_instance_start_stop_stack_size,
                                          (UINT) module_instance -> txm_module_instance_start_stop_priority,
                                          (UINT) module_instance -> txm_module_instance_start_stop_priority,
                                          TXM_MODULE_TIME_SLICE,
                                          TX_AUTO_START,
                                          sizeof(TX_THREAD),
                                          module_instance);

        /* Wait for the stop thread to complete.  */
        i =  0;
        while ((i < TXM_MODULE_TIMEOUT) && (module_instance -> txm_module_instance_start_stop_thread.tx_thread_state != TX_COMPLETED))
        {

            /* Sleep to let the module stop thread run.  */
            _tx_thread_sleep(1);

            /* Increment the counter.  */
            i++;
        }

        /* At this point, we need to terminate and delete the stop thread.  */
        _tx_thread_terminate(&(module_instance -> txm_module_instance_start_stop_thread));
        _tx_thread_delete(&(module_instance -> txm_module_instance_start_stop_thread));
    }

    /* Delete the module's callback thread and queue for the callback thread.  */
    _tx_thread_terminate(&(module_instance -> txm_module_instance_callback_request_thread));
    _tx_thread_delete(&(module_instance -> txm_module_instance_callback_request_thread));
    _tx_queue_delete(&(module_instance -> txm_module_instance_callback_request_queue));

    /* Disable interrupts.  */
    TX_DISABLE

    /* Temporarily disable preemption.  This will keep other threads from creating and deleting threads.  */
    _tx_thread_preempt_disable++;

    /* Restore interrupts.  */
    TX_RESTORE

    /* Determine if any external component stop functions have been registered and
       if so, call them to cleanup any module objects in that component.  */

#ifdef TXM_MODULE_ENABLE_FILEX

    /* Call the FileX stop function.  */
    _txm_module_manager_filex_stop(module_instance);
#endif

#ifdef TXM_MODULE_ENABLE_NETX

    /* Call the NetX stop function.  */
    _txm_module_manager_netx_stop(module_instance);
#endif

#ifdef TXM_MODULE_ENABLE_NETXDUO

    /* Call the NetX Duo stop function.  */
    _txm_module_manager_netxduo_stop(module_instance);
#endif

#ifdef TXM_MODULE_ENABLE_GUIX

    /* Call the GUIX stop function.  */
    _txm_module_manager_guix_stop(module_instance);
#endif

#ifdef TXM_MODULE_ENABLE_USBX

    /* Call the USBX stop function.  */
    _txm_module_manager_usbx_stop(module_instance);
#endif

    /* Loop to delete any and all threads created by the module.  */
    i = _tx_thread_created_count;
    thread_ptr =  _tx_thread_created_ptr;
    while (i--)
    {

        /* Pickup the next thread pointer.  */
        next_thread_ptr =  thread_ptr -> tx_thread_created_next;

        /* Determine if the thread control block is inside the module.  */
        if ( (((CHAR *) thread_ptr) >= ((CHAR *) module_instance -> txm_module_instance_data_start)) &&
             (((CHAR *) thread_ptr) < ((CHAR *) module_instance -> txm_module_instance_data_end)))
        {

            /* Terminate and delete this thread, since it is part of this module.  */
            _tx_thread_terminate(thread_ptr);
            _tx_thread_delete(thread_ptr);
        }

        /* Is this thread part of the module?  */
        else if (thread_ptr -> tx_thread_module_instance_ptr == module_instance)
        {

            /* Terminate and delete this thread, since it is part of this module.  */
            _tx_thread_terminate(thread_ptr);
            _tx_thread_delete(thread_ptr);
        }

        /* Move to next thread.  */
        thread_ptr =  next_thread_ptr;
    }

    /* Loop to delete any and all timers created by the module.  */
    i = _tx_timer_created_count;
    timer_ptr =  _tx_timer_created_ptr;
    while (i--)
    {

        /* Pickup the next timer pointer.  */
        next_timer_ptr =  timer_ptr -> tx_timer_created_next;

        /* Check if this module created this timer.  */
        created_by_module =  _txm_module_manager_created_object_check(module_instance, (VOID *) timer_ptr);
        if (created_by_module == TX_TRUE)
        {

            /* Delete this timer, since it is part of this module.  */
            _tx_timer_delete(timer_ptr);
        }

        /* Move to next timer.  */
        timer_ptr =  next_timer_ptr;
    }

    /* Loop to delete any and all queues created by the module.  */
    i = _tx_queue_created_count;
    queue_ptr =  _tx_queue_created_ptr;
    while (i--)
    {

        /* Pickup the next queue pointer.  */
        next_queue_ptr =   queue_ptr -> tx_queue_created_next;

#ifndef TX_DISABLE_NOTIFY_CALLBACKS

        /* Determine if the queue callback function is associated with this module.  */
        if ((queue_ptr -> tx_queue_module_instance == module_instance) &&
            (queue_ptr -> tx_queue_send_notify == _txm_module_manager_queue_notify_trampoline))
        {

            /* Clear the callback notification for this queue since it is no longer valid.  */
            queue_ptr -> tx_queue_send_notify =  TX_NULL;
        }
#endif

        /* Check if this module created this queue.  */
        created_by_module =  _txm_module_manager_created_object_check(module_instance, (VOID *) queue_ptr);
        if (created_by_module == TX_TRUE)
        {

            /* Delete this queue, since it is part of this module.  */
            _tx_queue_delete(queue_ptr);
        }

        /* Move to next queue.  */
        queue_ptr =  next_queue_ptr;
    }

    /* Loop to delete any and all event flag groups created by the module.  */
    i = _tx_event_flags_created_count;
    events_ptr =  _tx_event_flags_created_ptr;
    while (i--)
    {

        /* Pickup the next event flags group pointer.  */
        next_events_ptr =   events_ptr -> tx_event_flags_group_created_next;

#ifndef TX_DISABLE_NOTIFY_CALLBACKS

        /* Determine if the event flags callback function is associated with this module.  */
        if ((events_ptr -> tx_event_flags_group_module_instance == module_instance) &&
            (events_ptr -> tx_event_flags_group_set_notify == _txm_module_manager_event_flags_notify_trampoline))
        {

            /* Clear the callback notification for this event flag group since it is no longer valid.  */
            events_ptr -> tx_event_flags_group_set_notify =  TX_NULL;
        }
#endif

        /* Check if this module created this event flags.  */
        created_by_module =  _txm_module_manager_created_object_check(module_instance, (VOID *) events_ptr);
        if (created_by_module == TX_TRUE)
        {

            /* Delete this event flags group, since it is part of this module.  */
            _tx_event_flags_delete(events_ptr);
        }

        /* Move to next event flags group.  */
        events_ptr =  next_events_ptr;
    }

    /* Loop to delete any and all semaphores created by the module.  */
    i = _tx_semaphore_created_count;
    semaphore_ptr =  _tx_semaphore_created_ptr;
    while (i--)
    {

        /* Pickup the next semaphore pointer.  */
        next_semaphore_ptr =   semaphore_ptr -> tx_semaphore_created_next;

#ifndef TX_DISABLE_NOTIFY_CALLBACKS

        /* Determine if the semaphore callback function is associated with this module.  */
        if ((semaphore_ptr -> tx_semaphore_module_instance == module_instance) &&
            (semaphore_ptr -> tx_semaphore_put_notify == _txm_module_manager_semaphore_notify_trampoline))
        {

            /* Clear the callback notification for this semaphore since it is no longer valid.  */
            semaphore_ptr -> tx_semaphore_put_notify =  TX_NULL;
        }
#endif

        /* Check if this module created this semaphore.  */
        created_by_module =  _txm_module_manager_created_object_check(module_instance, (VOID *) semaphore_ptr);
        if (created_by_module == TX_TRUE)
        {

            /* Delete this semaphore, since it is part of this module.  */
            _tx_semaphore_delete(semaphore_ptr);
        }

        /* Move to next semaphore.  */
        semaphore_ptr =  next_semaphore_ptr;
    }

    /* Loop to delete any and all mutexes created by the module.  */
    i = _tx_mutex_created_count;
    mutex_ptr =  _tx_mutex_created_ptr;
    while (i--)
    {

        /* Pickup the next mutex pointer.  */
        next_mutex_ptr =   mutex_ptr -> tx_mutex_created_next;

        /* Check if this module created this mutex.  */
        created_by_module =  _txm_module_manager_created_object_check(module_instance, (VOID *) mutex_ptr);
        if (created_by_module == TX_TRUE)
        {

            /* Delete this mutex, since it is part of this module.  */
            _tx_mutex_delete(mutex_ptr);
        }

        /* Move to next mutex.  */
        mutex_ptr =  next_mutex_ptr;
    }

    /* Loop to delete any and all block pools created by the module.  */
    i = _tx_block_pool_created_count;
    block_pool_ptr =  _tx_block_pool_created_ptr;
    while (i--)
    {

        /* Pickup the next block pool pointer.  */
        next_block_pool_ptr =   block_pool_ptr -> tx_block_pool_created_next;

        /* Check if this module created this block pool.  */
        created_by_module =  _txm_module_manager_created_object_check(module_instance, (VOID *) block_pool_ptr);
        if (created_by_module == TX_TRUE)
        {

            /* Delete this block pool, since it is part of this module.  */
            _tx_block_pool_delete(block_pool_ptr);
        }

        /* Move to next block pool.  */
        block_pool_ptr =  next_block_pool_ptr;
    }

    /* Loop to delete any and all byte pools created by the module.  */
    i = _tx_byte_pool_created_count;
    byte_pool_ptr =  _tx_byte_pool_created_ptr;
    while (i--)
    {

        /* Pickup the next byte pool pointer.  */
        next_byte_pool_ptr =   byte_pool_ptr -> tx_byte_pool_created_next;

        /* Check if this module created this byte pool.  */
        created_by_module =  _txm_module_manager_created_object_check(module_instance, (VOID *) byte_pool_ptr);
        if (created_by_module == TX_TRUE)
        {

            /* Delete this byte pool, since it is part of this module.  */
            _tx_byte_pool_delete(byte_pool_ptr);
        }

        /* Move to next byte pool.  */
        byte_pool_ptr =  next_byte_pool_ptr;
    }

#ifdef TX_ENABLE_EVENT_TRACE
    /* Has trace been enabled?  */
    if (_tx_trace_buffer_current_ptr != TX_NULL)
    {

        /* Is the trace buffer located inside the module?  */
        if ((ULONG) _tx_trace_header_ptr -> tx_trace_header_buffer_start_pointer >= (ULONG) module_instance -> txm_module_instance_data_start &&
            (ULONG) _tx_trace_header_ptr -> tx_trace_header_buffer_start_pointer < (ULONG) module_instance -> txm_module_instance_data_end)
        {
            _tx_trace_disable();
        }
    }
#endif

    /* Delete the allocated objects for this module.  */
    while (module_instance -> txm_module_instance_object_list_count != 0)
    {
        /* Pickup the current object pointer.  */
        object_ptr =  module_instance -> txm_module_instance_object_list_head;

        /* Move the head pointer forward.  */
        module_instance -> txm_module_instance_object_list_head =  object_ptr -> txm_module_allocated_object_next;

        /* Release the object.  */
        _tx_byte_release((VOID *) object_ptr);

        /* Decrement count.  */
        module_instance -> txm_module_instance_object_list_count--;
    }

    /* Set the allocated list head pointer to NULL.  */
    module_instance -> txm_module_instance_object_list_head =  TX_NULL;

    /* Disable interrupts.  */
    TX_DISABLE

    /* Enable preemption again.  */
    _tx_thread_preempt_disable--;

    /* Set the module state to indicate the module is stopped.  */
    module_instance -> txm_module_instance_state =  TXM_MODULE_STOPPED;

    /* Restore interrupts.  */
    TX_RESTORE

    /* Release the protection mutex.  */
    _tx_mutex_put(&_txm_module_manager_mutex);

    /* Check for preemption.  */
    _tx_thread_system_preempt_check();

    /* Return success.  */
    return(TX_SUCCESS);
}

