
/*HEADER**********************************************************************
*
* Copyright 2008 Freescale Semiconductor, Inc.
* Copyright 2004-2008 Embedded Access Inc.
* Copyright 1989-2008 ARC International
*
* This software is owned or controlled by Freescale Semiconductor.
* Use of this software is governed by the Freescale MQX RTOS License
* distributed with this Material.
* See the MQX_RTOS_LICENSE file distributed for more details.
*
* Brief License Summary:
* This software is provided in source form for you to use free of charge,
* but it is not open source software. You are allowed to use this software
* but you cannot redistribute it or derivative works of it in source form.
* The software may be used only in connection with a product containing
* a Freescale microprocessor, microcontroller, or digital signal processor.
* See license agreement file for full license terms including other restrictions.
*****************************************************************************
*
* Comments:
*
*   This file contains functions of the IPC 
*   (Inter Processor Communication) component.
*
*
*END************************************************************************/

#include <stdarg.h>
#include <string.h>
#include "mqx_inc.h"
#if MQX_USE_IPC
#include "message.h"
#include "msg_prv.h"
#include "fio.h"
#include "io.h"
#include "io_pcb.h"
#include "name.h"
#include "name_prv.h"
#include "event.h"
#include "event_prv.h"
#include "ipc.h"
#include "ipc_prv.h"
#include "ipc_pcb.h"
#include "ipc_pcbv.h"
#include "ipcrtprv.h"

/* A list of current IPC PCB drivers that have been installed */
QUEUE_STRUCT _ipc_pcb_drivers =
{   NULL, NULL, 0, 0};

/* Dummy router table for the IPC. */
const IPC_ROUTING_STRUCT _ipc_routing_table[] =
{
    {   0, 0, 0}
};

/*
 * Definition of the field sizes in the IPC message data.
 * So that the ENDIAN-ness can be swapped by _msg_swap_endian_data
 */
static const unsigned char _ipc_msg_type_def[] =
{
    sizeof(_mqx_uint),
    0};

static const unsigned char _ipc_msg_def[] =
{
    sizeof(_mqx_uint), sizeof(_task_id),
    /* Parameters... number of these must equal IPC_MAX_PARAMETERS */
    sizeof(_mqx_uint), sizeof(_mqx_uint), sizeof(_mqx_uint), sizeof(_mqx_uint),
    sizeof(_mqx_uint), sizeof(_mqx_uint), sizeof(_mqx_uint), sizeof(_mqx_uint),
    sizeof(_mqx_uint), sizeof(_mqx_uint), sizeof(_mqx_uint), sizeof(_mqx_uint),
    sizeof(_mqx_uint), sizeof(_mqx_uint), sizeof(_mqx_uint), sizeof(_mqx_uint),
    0};

static const unsigned char _ipc_mqx_msg_def[] =
{
    sizeof(_mqx_uint), sizeof(_task_id), sizeof(_task_id), sizeof(_mqx_uint),
    sizeof(_mqx_uint), sizeof(void (_CODE_PTR_)(_mqx_uint)),
    sizeof(_mqx_uint), sizeof(_mqx_uint), sizeof(char *),
    sizeof(_mqx_uint), sizeof(_mqx_uint), sizeof(_mqx_uint),
    0};

const IPC_PROTOCOL_INIT_STRUCT _ipc_init_table[] =
{
    {   NULL, NULL, NULL, 0}
};

static const IPC_INIT_STRUCT _default_ipc_init =
{
    _ipc_routing_table,
    _ipc_init_table
};

/*!
 * \brief Adds an IPC handler for the MQX component.
 * 
 * The IPC task calls the function when an IPC message for the specified MQX 
 * component is received. The IPC task calls the function once for each component.
 * \n The parameter component can be one of:
 * \li KERNEL_EDS_SERIAL
 * \li KERNEL_EVENTS
 * \li KERNEL_IPC
 * \li KERNEL_IPC_MSG_ROUTING
 * \li KERNEL_LOG
 * \li KERNEL_LWLOG
 * \li KERNEL_MESSAGES
 * \li KERNEL_MUTEXES
 * \li KERNEL_NAME_MANAGEMENT
 * \li KERNEL_PARTITIONS
 * \li KERNEL_SEMAPHORES
 * \li KERNEL_TIMER
 * \li KERNEL_WATCHDOG
 * 
 * \param[in] handler   Pointer to the function that MQX calls when it receives 
 * an IPC request for the component.
 * \param[in] component MQX component that the handler is for (see Description).
 * 
 * \return MQX_OK
 * \return MQX_COMPONENT_DOES_NOT_EXIST (IPC component was not created.)
 * \return MQX_IPC_SERVICE_NOT_AVAILABLE (IPC server has not been started.)
 * 
 * \see _ipc_add_io_ipc_handler   
 */ 
_mqx_uint _ipc_add_ipc_handler
(
    IPC_HANDLER_FPTR handler,
    _mqx_uint        component
)
{ /* Body */
    KERNEL_DATA_STRUCT_PTR   kernel_data;
    IPC_COMPONENT_STRUCT_PTR ipc_component_ptr;

    _GET_KERNEL_DATA(kernel_data);

    ipc_component_ptr = kernel_data->IPC_COMPONENT_PTR;

#if MQX_CHECK_ERRORS
    if (!ipc_component_ptr)
    {
        return MQX_IPC_SERVICE_NOT_AVAILABLE;
    } /* Endif */

    if (component > MAX_KERNEL_COMPONENTS)
    {
        return MQX_COMPONENT_DOES_NOT_EXIST;
    } /* Endif */
#endif

    ipc_component_ptr->IPC_COMPONENT_HANDLER[component] = handler;

    return MQX_OK;

} /* Endif */

/*!
 * \brief Add an IPC handler for the I/O component.
 * 
 * The IPC task calls the function when an IPC message for the specified I/O 
 * component is received. The IPC task calls the function once for each component.
 * \n The parameter component can be one of:
 * \li IO_CAN_COMPONENT
 * \li IO_EDS_COMPONENT
 * \li IO_HDLC_COMPONENT
 * \li IO_LAPB_COMPONENT
 * \li IO_LAPD_COMPONENT
 * \li IO_MFS_COMPONENT
 * \li IO_PPP_COMPONENT
 * \li IO_RTCS_COMPONENT
 * \li IO_SDLC_COMPONENT
 * \li IO_SNMP_COMPONENT
 * \li IO_SUBSYSTEM_COMPONENT
 * 
 * \param[in] handler   Pointer to the function that MQX calls when it receives 
 * an IPC request for the component.
 * \param[in] component I/O component that the handler is for (see Description).
 * 
 * \return MQX_OK
 * \return MQX_IPC_SERVICE_NOT_AVAILABLE (IPC server has not been started.)
 * 
 * \see _ipc_add_ipc_handler 
 */ 
_mqx_uint _ipc_add_io_ipc_handler
(
    IPC_HANDLER_FPTR handler,
    _mqx_uint        component
)
{ /* Body */
    KERNEL_DATA_STRUCT_PTR   kernel_data;
    IPC_COMPONENT_STRUCT_PTR ipc_component_ptr;

    _GET_KERNEL_DATA(kernel_data);

    ipc_component_ptr = kernel_data->IPC_COMPONENT_PTR;

#if MQX_CHECK_ERRORS
    if (!ipc_component_ptr)
    {
        return MQX_IPC_SERVICE_NOT_AVAILABLE;
    } /* Endif */
#endif

    ipc_component_ptr->IPC_IO_COMPONENT_HANDLER[component] = handler;

    return MQX_OK;

} /* Endbody */

/*!
 * \brief Handles multi-processor event requests.
 * 
 * \param[in] imsg_ptr The incoming message.
 * 
 * \return MQX_OK  
 */ 
_mqx_uint _event_ipc_handler
(
    void   *imsg_ptr
)
{ /* Body */
    IPC_MESSAGE_STRUCT_PTR ipc_msg_ptr = imsg_ptr;
    void                  *event_ptr;
    _mqx_uint              error_code;
    _mqx_uint              result = 0;

    switch (IPC_GET_TYPE(ipc_msg_ptr->MESSAGE_TYPE))
    {
        case IPC_EVENT_OPEN:
        error_code = _event_open((char *)&ipc_msg_ptr->PARAMETERS[0],
                        &event_ptr);
        result = (_mqx_uint)event_ptr;
        break;
        case IPC_EVENT_SET:
        event_ptr = (void *)ipc_msg_ptr->PARAMETERS[0];
        error_code = _event_set(event_ptr, ipc_msg_ptr->PARAMETERS[1]);
        break;
        default:
        error_code = MQX_IPC_INVALID_MESSAGE;
        break;
    } /* Endswitch */

    _ipc_send_internal(FALSE,
                    PROC_NUMBER_FROM_QID(ipc_msg_ptr->HEADER.SOURCE_QID),
                    KERNEL_MESSAGES, IPC_ACTIVATE,
                    3, result, ipc_msg_ptr->REQUESTOR_ID,
                    error_code);
    _msg_free(ipc_msg_ptr);

    return MQX_OK;

} /* Endbody */

/*!
 * \brief Installs the handler for IPC events.
 */ 
void _event_install_ipc_handler(void)
{ /* Body */

    _ipc_add_ipc_handler( _event_ipc_handler, KERNEL_EVENTS);

} /* Endbody */

/*!
 * \brief Initializes an IPC for a PCB driver.
 * 
 * The function is used in structure of type IPC_PROTOCOL_STRUCT to initialize the 
 * Inter-Processor Communications over a serial port using the standard 
 * ASYNC_SERIAL driver. 
 * \n The IPC_PROTOCOL_INIT_DATA field in IPC_PROTOCOL_INIT_STRUCT must point to 
 * a structure of type IPC_PCB_INIT_STRUCT.
 * 
 * \param[in] init_ptr Pointer to an IPC protocol initialization structure
 * (IPC_PROTOCOL_INIT_STRUCT).
 * \param[in] data_ptr Pointer to an IPC protocol information structure 
 * (IPC_PROTOCOL_INFO_STRUCT).
 * 
 * \return MQX_OK
 * \return IPC_PCB_OUTPUT_PCB_POOL_CREATE_FAILED
 * \return IPC_PCB_INVALID_QUEUE
 * \return IPC_PCB_PACKET_POOL_CREATE_FAILED
 * \return IPC_PCB_DEVICE_OPEN_FAILED
 * \return IPC_PCB_INPUT_PCB_POOL_CREATE_FAILED
 * \return        
 */ 
_mqx_uint _ipc_pcb_init(
    const IPC_PROTOCOL_INIT_STRUCT * init_ptr,
    void                            *data_ptr)
{
    IPC_PROTOCOL_INFO_STRUCT_PTR info_ptr;
    IPC_PCB_INFO_STRUCT_PTR      pcb_info_ptr;
    IPC_PCB_INIT_STRUCT_PTR      pcb_init_ptr;
    _io_pcb_pool_id              pcb_pool;
    _queue_id                    qid;
    _pool_id                     msg_pool;
    _mqx_uint                    result = MQX_OK;
    _psp_code_addr               _ipc_pcb_function_location = (_psp_code_addr) _ipc_pcb_input_notification;

    info_ptr = (IPC_PROTOCOL_INFO_STRUCT_PTR) data_ptr;
    pcb_info_ptr = (IPC_PCB_INFO_STRUCT_PTR) _mem_alloc_system_zero((_mem_size) sizeof(IPC_PCB_INFO_STRUCT));
#if MQX_CHECK_MEMORY_ALLOCATION_ERRORS
    if (pcb_info_ptr == NULL)
    {
        return (_task_get_error());
    }
#endif
    info_ptr->IPC_PROTOCOL_INFO_PTR = pcb_info_ptr;

    pcb_init_ptr = (IPC_PCB_INIT_STRUCT_PTR) init_ptr->IPC_PROTOCOL_INIT_DATA;

    info_ptr->IPC_TYPE = IPC_PCB_PROTOCOL;
    info_ptr->IPC_INIT_PTR = init_ptr;

    /* CREATE OUTPUT PCB POOL */
    pcb_pool = _io_pcb_create_pool(1, 0, pcb_init_ptr->OUT_PCBS_INITIAL, pcb_init_ptr->OUT_PCBS_TO_GROW,
                    pcb_init_ptr->OUT_PCBS_MAX, 0, NULL, 0, NULL);
#if MQX_CHECK_ERRORS
    if (pcb_pool == IO_PCB_NULL_POOL_ID)
    {
        result = IPC_PCB_OUTPUT_PCB_POOL_CREATE_FAILED;
    }
    else
    {
#endif
        pcb_info_ptr->PCB_OUTPUT_POOL = pcb_pool;
#if MQX_CHECK_ERRORS
    }
#endif

    /* Create output message queue */
    if (result == MQX_OK)
    {
        qid = _msgq_open_system((_queue_number) info_ptr->IPC_INIT_PTR->IPC_OUT_QUEUE, 0, _ipc_pcb_output_notification,
                        info_ptr);
#if MQX_CHECK_ERRORS
        if (qid == MSGQ_NULL_QUEUE_ID)
        {
            result = IPC_PCB_INVALID_QUEUE;
        }
        else
        {
#endif
            pcb_info_ptr->OUT_MSG_QID = qid;
#if MQX_CHECK_ERRORS
        }
#endif
    }

    if (result == MQX_OK)
    {
        /* Create input message pool */
        msg_pool = _msgpool_create(pcb_init_ptr->IN_MESSAGES_MAX_SIZE, pcb_init_ptr->IN_MESSAGES_TO_ALLOCATE,
                        pcb_init_ptr->IN_MESSAGES_TO_GROW, pcb_init_ptr->IN_MESSAGES_MAX_ALLOCATE);
#if MQX_CHECK_ERRORS
        if (msg_pool == MSGPOOL_NULL_POOL_ID)
        {
            result = (IPC_PCB_PACKET_POOL_CREATE_FAILED);
        }
        else
        {
#endif
            pcb_info_ptr->MSG_INPUT_POOL = msg_pool;
#if MQX_CHECK_ERRORS
        }
#endif
    }

    if (result == MQX_OK)
    {
        if (pcb_init_ptr->DEVICE_INSTALL != NULL)
        {
            result = (*pcb_init_ptr->DEVICE_INSTALL)(pcb_init_ptr->IO_PCB_DEVICE_NAME,
                            pcb_init_ptr->DEVICE_INSTALL_PARAMETER);
        }
    }

    if (result == MQX_OK)
    {
        /* Open the IO PCB device */
        pcb_info_ptr->FD = fopen(pcb_init_ptr->IO_PCB_DEVICE_NAME, (char *) 0);
#if MQX_CHECK_ERRORS
        if (pcb_info_ptr->FD == NULL)
        {
            result = IPC_PCB_DEVICE_OPEN_FAILED;
        }
#endif
    }

    if (result == MQX_OK)
    {
        pcb_pool = _io_pcb_create_pool(1, 0, pcb_init_ptr->IN_MESSAGES_TO_ALLOCATE, pcb_init_ptr->IN_MESSAGES_TO_GROW,
                        pcb_init_ptr->IN_MESSAGES_MAX_ALLOCATE, _ipc_pcb_alloc, info_ptr, 0, NULL);
#if MQX_CHECK_ERRORS
        if (pcb_pool == IO_PCB_NULL_POOL_ID)
        {
            result = IPC_PCB_INPUT_PCB_POOL_CREATE_FAILED;
        }
        else
        {
#endif
            pcb_info_ptr->PCB_INPUT_POOL = pcb_pool;
#if MQX_CHECK_ERRORS
        }
#endif
    }

    if (result == MQX_OK)
    {
        result = ioctl(pcb_info_ptr->FD, IO_PCB_IOCTL_READ_CALLBACK_SET, &_ipc_pcb_function_location);
    }

    if (result == MQX_OK)
    {
        result = ioctl(pcb_info_ptr->FD, IO_PCB_IOCTL_SET_INPUT_POOL, (void *) &pcb_info_ptr->PCB_INPUT_POOL);
    }

    if (result == MQX_OK)
    {
        result = ioctl(pcb_info_ptr->FD, IO_PCB_IOCTL_START, NULL);
    }

#if MQX_CHECK_ERRORS
    if (result != MQX_OK)
    {
        if (pcb_info_ptr->FD)
        {
            fclose(pcb_info_ptr->FD);
        }
        if (pcb_info_ptr->PCB_INPUT_POOL)
        {
            _io_pcb_destroy_pool(pcb_info_ptr->PCB_INPUT_POOL);
        }
        if (pcb_info_ptr->MSG_INPUT_POOL)
        {
            _msgpool_destroy(pcb_info_ptr->MSG_INPUT_POOL);
        }
        if (pcb_info_ptr->OUT_MSG_QID)
        {
            _msgq_close(pcb_info_ptr->OUT_MSG_QID);
        }
        if (pcb_info_ptr->PCB_OUTPUT_POOL)
        {
            _io_pcb_destroy_pool(pcb_info_ptr->PCB_OUTPUT_POOL);
        }
        _mem_free(pcb_info_ptr);
    }
    else
    {
#endif
        if (_ipc_pcb_drivers.NEXT == NULL)
        {
            _queue_init(&_ipc_pcb_drivers, 0);
        }
        _queue_enqueue(&_ipc_pcb_drivers, &pcb_info_ptr->QUEUE);
#if MQX_CHECK_ERRORS
    }
#endif

    return (result);
}

/*!
 * \brief This function is called whenever a message is to be sent offboard.
 * 
 * It packs the message into a PCB and sends it off to an IO PCB device driver.   
 * The function is called by _msg_send (it is an activation function).
 * Note that the PCB free function is set to _ipc_pcb_free.
 * 
 * \param[in] data_ptr Pointer to an IPC protocol information structure 
 * (IPC_PROTOCOL_INFO_STRUCT). 
 */                     
void _ipc_pcb_output_notification
(
    void   *data_ptr
)
{ /* Body */
    IPC_PROTOCOL_INFO_STRUCT_PTR info_ptr;
    IPC_PCB_INFO_STRUCT_PTR      pcb_info_ptr;
    IO_PCB_STRUCT_PTR            pcb_ptr;
    IPC_MESSAGE_STRUCT_PTR       msg_ptr;
#if PSP_MEMORY_ADDRESSING_CAPABILITY > 8
    bool                      must_unpack;
    bool                      success;
    uint32_t                      frag_size;
    uint32_t                      result;
#endif

    info_ptr = (IPC_PROTOCOL_INFO_STRUCT_PTR) data_ptr;
    pcb_info_ptr = (IPC_PCB_INFO_STRUCT_PTR) info_ptr->IPC_PROTOCOL_INFO_PTR;

#if PSP_MEMORY_ADDRESSING_CAPABILITY > 8
    /* Determine if we must unpack the messages */
    result = ioctl(pcb_info_ptr->FD, IO_PCB_IOCTL_UNPACKED_ONLY, &must_unpack);
    if (result != MQX_OK)
    {
        must_unpack = FALSE;
    }
#endif

    while (_msgq_get_count(info_ptr->IPC_OUT_QID))
    {
        pcb_ptr = _io_pcb_alloc(pcb_info_ptr->PCB_OUTPUT_POOL, FALSE);
        if (pcb_ptr == NULL)
        {
            return;
        }
        msg_ptr = (IPC_MESSAGE_STRUCT_PTR) _msgq_poll(info_ptr->IPC_OUT_QID);

#if PSP_MEMORY_ADDRESSING_CAPABILITY > 8
        if (must_unpack)
        {
            success = _ipc_pcb_unpack_message(msg_ptr, pcb_ptr);
            _msg_free(msg_ptr);
#if MQX_CHECK_MEMORY_ALLOCATION_ERRORS
            if (!success)
            {
                /* Toss the packet */
                _io_pcb_free_internal(pcb_ptr);
                return;
            }
#endif
        }
        else
        {
            pcb_ptr->FRAGMENTS[0].LENGTH = (uint32_t)msg_ptr->HEADER.SIZE;
            pcb_ptr->FRAGMENTS[0].FRAGMENT = (unsigned char *)msg_ptr;
            pcb_ptr->OWNER_PRIVATE_FLAGS = IPC_PCB_FRAG_IS_MSG;
        }
#else
        pcb_ptr->FRAGMENTS[0].LENGTH = (uint32_t) msg_ptr->HEADER.SIZE;
        pcb_ptr->FRAGMENTS[0].FRAGMENT = (unsigned char *) msg_ptr;
#endif
        pcb_ptr->FREE_PCB_FUNCTION_PTR = _ipc_pcb_free;

        _io_pcb_write(pcb_info_ptr->FD, pcb_ptr);

        pcb_info_ptr->OUTPUT_MESSAGE_COUNT++;

    }
}

/*!
 * \brief Frees a pcb sent via the ipc_pcb output task.
 *   
 * The message that is in the PCB must be freed.
 * 
 * \param[in] pcb_ptr The pcb that has finished. 
 */ 
_mqx_uint _ipc_pcb_free
(
    IO_PCB_STRUCT_PTR pcb_ptr
)
{
    _msg_free(pcb_ptr->FRAGMENTS[0].FRAGMENT);
    return (_io_pcb_free_internal(pcb_ptr));
}

/*!
 * \brief Obtains PCB from input PCB pool.
 * 
 * This function is called whenever a PCB is being allocated from the input PCB 
 * pool, by the input task. For example, when the input task is preparing to set 
 * up receive data, it would obtain a PCB from the input pool. At that point, 
 * this function will be called by the pcb alloc function. This function obtains 
 * a message and sets up the PCB to point to it.
 *  
 * \param[out] pcb_ptr  Pointer to the new PCB.
 * \param[in]  data_ptr Pointer to an IPC protocol information structure. 
 * (IPC_PROTOCOL_INFO_STRUCT).
 * 
 * \return Pointer to the PCB (or new PCB).
 * \return NULL (failure) 
 */ 

IO_PCB_STRUCT_PTR _ipc_pcb_alloc
(
    IO_PCB_STRUCT_PTR pcb_ptr,
    void             *data_ptr
)
{
    IPC_PROTOCOL_INFO_STRUCT_PTR info_ptr;
    IPC_PCB_INFO_STRUCT_PTR      pcb_info_ptr;
    MESSAGE_HEADER_STRUCT_PTR    msg_ptr;

    info_ptr = (IPC_PROTOCOL_INFO_STRUCT_PTR) data_ptr;
    pcb_info_ptr = (IPC_PCB_INFO_STRUCT_PTR) info_ptr->IPC_PROTOCOL_INFO_PTR;

    msg_ptr = _msg_alloc(pcb_info_ptr->MSG_INPUT_POOL);
#if MQX_CHECK_ERRORS
    if (msg_ptr == NULL)
    {
        return (NULL);
    }
#endif
    pcb_ptr->FRAGMENTS[0].LENGTH = 0;
    pcb_ptr->FRAGMENTS[0].FRAGMENT = (unsigned char *) msg_ptr;
    pcb_ptr->INSTANTIATOR_PRIVATE = info_ptr;

    return (pcb_ptr);
}

/*!
 * \brief This function is called by the underlying IO PCB device driver whenever 
 * an input message has been received.
 * 
 * \param[in] fd_ptr  The file descriptor used to set the notification function.
 * \param[in] pcb_ptr The pcb that has arrived. 
 */ 
void _ipc_pcb_input_notification
(
    MQX_FILE_PTR      fd_ptr,
    IO_PCB_STRUCT_PTR pcb_ptr
)
{
    KERNEL_DATA_STRUCT_PTR       kernel_data;
    IPC_PROTOCOL_INFO_STRUCT_PTR info_ptr;
    IPC_PCB_INFO_STRUCT_PTR      pcb_info_ptr;
    MESSAGE_HEADER_STRUCT_PTR    msg_ptr;

    _GET_KERNEL_DATA(kernel_data);

    info_ptr = (IPC_PROTOCOL_INFO_STRUCT_PTR) pcb_ptr->INSTANTIATOR_PRIVATE;
    pcb_info_ptr = (IPC_PCB_INFO_STRUCT_PTR) info_ptr->IPC_PROTOCOL_INFO_PTR;

    msg_ptr = (MESSAGE_HEADER_STRUCT_PTR) pcb_ptr->FRAGMENTS[0].FRAGMENT;
    if (MSG_MUST_CONVERT_HDR_ENDIAN(msg_ptr->CONTROL))
    {
        _msg_swap_endian_header(msg_ptr);
    }
    if (PROC_NUMBER_FROM_QID(msg_ptr->TARGET_QID) == 0)
    {
        /* This is a special message for this CPU */
        msg_ptr->TARGET_QID = BUILD_QID(kernel_data->INIT.PROCESSOR_NUMBER, QUEUE_FROM_QID(msg_ptr->TARGET_QID));
    }
    _msgq_send(msg_ptr);

    _io_pcb_free(pcb_ptr);

    pcb_info_ptr->INPUT_MESSAGE_COUNT++;
}

/*!
 * \brief Adds a route to the message routing table.
 * 
 * The IPC component must first be created.
 * 
 * \param[in] min_proc_number Minimum processor number in the range.
 * \param[in] max_proc_number Maximum processor number in the range.
 * \param[in] queue           Queue number of the IPC to use for processor numbers 
 * in the range.
 * 
 * \return MQX_OK
 * \return MQX_COMPONENT_DOES_NOT_EXIST (IPC component was not created.)
 * \return MQX_INVALID_PROCESSOR_NUMBER
 * \return MSGQ_INVALID_QUEUE_ID
 * \return IPC_ROUTE_EXISTS
 * \return MQX_OUT_OF_MEMORY
 * 
 * \see _ipc_msg_route_remove
 * \see _ipc_msg_processor_route_exists
 * \see IPC_ROUTING_STRUCT      
 */ 
_mqx_uint _ipc_msg_route_add
(
    _processor_number min_proc_number,
    _processor_number max_proc_number,
    _queue_number     queue
)
{
    KERNEL_DATA_STRUCT_PTR               kernel_data;
    IPC_MSG_ROUTING_COMPONENT_STRUCT_PTR component_ptr;
    IPC_MSG_ROUTING_STRUCT_PTR           routing_ptr;
#if MQX_CHECK_ERRORS
    _processor_number                    i;
#endif

    _GET_KERNEL_DATA(kernel_data);
    component_ptr = (IPC_MSG_ROUTING_COMPONENT_STRUCT_PTR) kernel_data->KERNEL_COMPONENTS[KERNEL_IPC_MSG_ROUTING];
#if MQX_CHECK_ERRORS
    if (component_ptr == NULL)
    {
        return (MQX_COMPONENT_DOES_NOT_EXIST);
    }
    if ((min_proc_number < 1) || (max_proc_number > MQX_MAX_PROCESSOR_NUMBER) || (min_proc_number > max_proc_number))
    {
        return (MQX_INVALID_PROCESSOR_NUMBER);
    }
    if ((queue == 0) || (queue >= MAX_QNUMBERS))
    {
        return (MSGQ_INVALID_QUEUE_ID);
    }
    for (i = min_proc_number; i <= max_proc_number; ++i)
    {
        if (_ipc_msg_processor_route_exists(i))
        {
            return (IPC_ROUTE_EXISTS);
        }
    }
#endif
    routing_ptr = (IPC_MSG_ROUTING_STRUCT_PTR) _mem_alloc_system(sizeof(IPC_MSG_ROUTING_STRUCT));
#if MQX_CHECK_MEMORY_ALLOCATION_ERRORS
    if (routing_ptr == NULL)
    {
        return (MQX_OUT_OF_MEMORY);
    }
#endif
    routing_ptr->MIN_PROC_NUMBER = min_proc_number;
    routing_ptr->MAX_PROC_NUMBER = max_proc_number;
    routing_ptr->QUEUE = queue;

    _QUEUE_ENQUEUE(&component_ptr->ROUTING_LIST, &routing_ptr->LINK);

    return (MQX_OK);
}

/*!
 * \brief Removes a route from the message routing table.
 * 
 * The IPC component must first be installed.
 * 
 * \param[in] min_proc_number Minimum processor number in the range.
 * \param[in] max_proc_number Maximum processor number in the range.
 * \param[in] queue           Queue number of the IPC to remove.
 * 
 * \return MQX_OK
 * \return MQX_COMPONENT_DOES_NOT_EXIST (IPC component was not created.)
 * \return MQX_INVALID_PROCESSOR_NUMBER  
 * 
 * \see _ipc_msg_route_add     
 * \see _ipc_msg_processor_route_exists  
 */ 
_mqx_uint _ipc_msg_route_remove
(
    _processor_number min_proc_number,
    _processor_number max_proc_number,
    _queue_number     queue
)
{
    KERNEL_DATA_STRUCT_PTR               kernel_data;
    IPC_MSG_ROUTING_COMPONENT_STRUCT_PTR component_ptr;
    IPC_MSG_ROUTING_STRUCT_PTR           routing_ptr;
    IPC_MSG_ROUTING_STRUCT_PTR           next_ptr;
    uint16_t                              i;

    _GET_KERNEL_DATA(kernel_data);
    component_ptr = (IPC_MSG_ROUTING_COMPONENT_STRUCT_PTR)(
                    (void *) kernel_data->KERNEL_COMPONENTS[KERNEL_IPC_MSG_ROUTING]);
#if MQX_CHECK_ERRORS
    if (component_ptr == NULL)
    {
        return (MQX_COMPONENT_DOES_NOT_EXIST);
    }
    if ((min_proc_number < 1) || (max_proc_number > MQX_MAX_PROCESSOR_NUMBER) || (min_proc_number > max_proc_number))
    {
        return (MQX_INVALID_PROCESSOR_NUMBER);
    }
#endif

    routing_ptr = (IPC_MSG_ROUTING_STRUCT_PTR)((void *) component_ptr->ROUTING_LIST.NEXT);

    /* Scan list for specified processors */
    for (i = 0; i < component_ptr->ROUTING_LIST.SIZE; i++)
    {
        next_ptr = (IPC_MSG_ROUTING_STRUCT_PTR)((void *) routing_ptr->LINK.NEXT);
        if ((routing_ptr->MIN_PROC_NUMBER == min_proc_number) && (routing_ptr->MAX_PROC_NUMBER == max_proc_number))
        {
#if MQX_CHECK_ERRORS
            if (routing_ptr->QUEUE == queue)
            {
#endif
                _QUEUE_REMOVE(&component_ptr->ROUTING_LIST, &routing_ptr->LINK);
                _mem_free(routing_ptr);
#if MQX_CHECK_ERRORS
            }
#endif
        }
        routing_ptr = next_ptr;
    }

    return (MQX_OK);
}

/*!
 * \brief Checks whether a route to the specified processor exists.
 * 
 * \param[in] proc_number Processor number to check for a route.
 * 
 * \return Pointer to the route (a route exists).
 * \return NULL (Route does not exist.)
 * 
 * \see _ipc_msg_route_add
 * \see _ipc_msg_route_remove  
 */ 
void *_ipc_msg_processor_route_exists
(
    _processor_number proc_number
)
{
    KERNEL_DATA_STRUCT_PTR               kernel_data;
    IPC_MSG_ROUTING_COMPONENT_STRUCT_PTR component_ptr;
    IPC_MSG_ROUTING_STRUCT_PTR           routing_ptr;
    uint16_t                              i;

    _GET_KERNEL_DATA(kernel_data);
    component_ptr = (IPC_MSG_ROUTING_COMPONENT_STRUCT_PTR) kernel_data->KERNEL_COMPONENTS[KERNEL_IPC_MSG_ROUTING];
#if MQX_CHECK_ERRORS
    if (component_ptr == NULL)
    {
        return (NULL);
    }
#endif

    routing_ptr = (IPC_MSG_ROUTING_STRUCT_PTR)((void *) component_ptr->ROUTING_LIST.NEXT);
    for (i = 0; i < component_ptr->ROUTING_LIST.SIZE; i++)
    {
        if ((proc_number >= routing_ptr->MIN_PROC_NUMBER) && (proc_number <= routing_ptr->MAX_PROC_NUMBER))
        {
            return ((void *) routing_ptr);
        }
        routing_ptr = (IPC_MSG_ROUTING_STRUCT_PTR)((void *) routing_ptr->LINK.NEXT);
    }

    /* Route not found */
    return (NULL);
}

/*!
 * \private
 * 
 * \brief This function is called from the _ipc_init to initialize the message
 * router.
 * 
 * \param[in] route_ptr Pointer to IPC routing structure (IPC_MSG_ROUTING_STRUCT_PTR).
 * 
 * \return MQX_OK
 * \return MQX_COMPONENT_DOES_NOT_EXIST (IPC component was not created.)
 * \return MQX_INVALID_PROCESSOR_NUMBER
 * \return MSGQ_INVALID_QUEUE_ID
 * \return IPC_ROUTE_EXISTS
 * \return MQX_OUT_OF_MEMORY 
 */ 
_mqx_uint _ipc_msg_route_init_internal
(
    const IPC_ROUTING_STRUCT * route_ptr
)
{
    KERNEL_DATA_STRUCT_PTR               kernel_data;
    IPC_MSG_ROUTING_COMPONENT_STRUCT_PTR component_ptr;

    _mqx_uint result;

    _GET_KERNEL_DATA(kernel_data);

    component_ptr = (IPC_MSG_ROUTING_COMPONENT_STRUCT_PTR) _mem_alloc_system_zero(
                    (_mem_size) sizeof(IPC_MSG_ROUTING_COMPONENT_STRUCT));
#if MQX_CHECK_MEMORY_ALLOCATION_ERRORS
    if (component_ptr == NULL)
    {
        return (MQX_OUT_OF_MEMORY);
    }
#endif

    _QUEUE_INIT(&component_ptr->ROUTING_LIST, 0);

    /* Install the message routing handlers */
    kernel_data->KERNEL_COMPONENTS[KERNEL_IPC_MSG_ROUTING] = (void *) component_ptr;

    while (route_ptr->QUEUE != 0)
    {
        result = _ipc_msg_route_add(route_ptr->MIN_PROC_NUMBER, route_ptr->MAX_PROC_NUMBER, route_ptr->QUEUE);
#if MQX_CHECK_ERRORS
        if (result != MQX_OK)
        {
            return (result);
        }
#endif
        route_ptr++;
    }

    component_ptr->MSG_ROUTER = _ipc_msg_route_internal;

    return (MQX_OK);
}

/*!
 * \private
 * 
 * \brief This function is called from _msg_send to route an inter-processor message.
 * 
 * \param[in] pnum     The processor to send the message to.
 * \param[in] message  The message to be sent.
 * \param[in] blocking Is this a blocking message.
 * 
 * \return TRUE or FALSE
 * 
 * \warning On failure, calls _task_set_error() to set the following task error code:
 * \li MSGQ_INVALID_QUEUE_ID 
 */ 
bool _ipc_msg_route_internal
(
    _processor_number pnum,
    void             *message,
    bool           blocking
)
{
    KERNEL_DATA_STRUCT_PTR                  kernel_data;
    IPC_MSG_ROUTING_STRUCT_PTR              route_ptr;
    bool                                    result;
    _queue_number                           queue;

    _GET_KERNEL_DATA(kernel_data);

    route_ptr = (IPC_MSG_ROUTING_STRUCT_PTR) _ipc_msg_processor_route_exists(pnum);
    if (!route_ptr)
    {
        _task_set_error(MSGQ_INVALID_QUEUE_ID);
        return (FALSE);
    }
    queue = route_ptr->QUEUE;

    result = _msgq_send_queue(message, BUILD_QID(kernel_data->INIT.PROCESSOR_NUMBER, queue));
    if (result && blocking && (!kernel_data->IN_ISR))
    {
        _int_disable();
        kernel_data->ACTIVE_PTR->STATE = SEND_BLOCKED;
        _task_block();
        _int_enable();
    }

    return (result);
}

/*!
 * \brief Task that initializes IPCs and processes remote service requests.
 * 
 * For applications to use the IPC component, the task must be either specified 
 * in the task template list as an autostart task or explicitly created.
 * \n The task installs the IPCs that are listed in the IPC initialization 
 * structure. Pointer to this initialization structure (IPC_INIT_STRUCT_PTR) is 
 * provided as the creation parameter, otherwise default IPC_INIT_STRUCT is used 
 * (_default_ipc_init). When the initialization is finished it waits for service
 * requests from remote processors.
 *  
 * \param[in] parameter Pointer to the IPC_INIT_STRUCT (task creation parameter).
 * 
 * \see IPC_INIT_STRUCT
 */ 
void _ipc_task
(
    uint32_t parameter
)
{
    KERNEL_DATA_STRUCT_PTR         kernel_data;
    IPC_COMPONENT_STRUCT_PTR       ipc_component_ptr;
    const IPC_INIT_STRUCT          * ipc_init_ptr = (IPC_INIT_STRUCT_PTR) parameter;
    IPC_MESSAGE_STRUCT_PTR         msg_ptr;
    IPC_MQX_MESSAGE_STRUCT_PTR     ipc_msg_ptr;
    const IPC_PROTOCOL_INIT_STRUCT * protocol_init_ptr;
    IPC_PROTOCOL_INFO_STRUCT_PTR   info_ptr;
    TD_STRUCT_PTR                  td_ptr;
    IPC_HANDLER_FPTR               handler;
    _queue_id                      queue;
    _mqx_uint                      result;
    _mqx_uint                      component;

    _GET_KERNEL_DATA(kernel_data);

    if (ipc_init_ptr == NULL)
    {
        ipc_init_ptr = &_default_ipc_init;
    }

    /* Install the message routing handlers */
    result = _ipc_msg_route_init_internal(ipc_init_ptr->ROUTING_LIST_PTR);
#if MQX_CHECK_ERRORS
    if (result != MQX_OK)
    {
        _task_set_error(result);
        _task_block();
    }
#endif

    kernel_data->MY_IPC_TD_PTR = kernel_data->ACTIVE_PTR;
    kernel_data->MY_IPC_ID = kernel_data->ACTIVE_PTR->TASK_ID;

    /* Create the IPC component structure */
    ipc_component_ptr = (IPC_COMPONENT_STRUCT_PTR) _mem_alloc_zero((_mem_size) sizeof(IPC_COMPONENT_STRUCT));
#if MQX_CHECK_MEMORY_ALLOCATION_ERRORS
    if (ipc_component_ptr == NULL)
    {
        _task_block();
    }
#endif
    kernel_data->IPC_COMPONENT_PTR = ipc_component_ptr;

    /* Create a pool of messages for use by the ipc task. */
    kernel_data->IPC_NAMED_POOL = _msgpool_create(sizeof(IPC_MESSAGE_STRUCT), IPC_NUM_MESSAGES, IPC_GROW_MESSAGES,
                    IPC_LIMIT_MESSAGES);
#if MQX_CHECK_ERRORS || MQX_CHECK_MEMORY_ALLOCATION_ERRORS
    if (kernel_data->IPC_NAMED_POOL == MSGPOOL_NULL_POOL_ID)
    {
        _task_block();
    }
#endif

    /* Open the ipc message queue */
    queue = _msgq_open(IPC_MESSAGE_QUEUE_NUMBER, 0);
#if MQX_CHECK_ERRORS
    if (queue == MSGQ_NULL_QUEUE_ID)
    {
        _task_block();
    }
#endif

    kernel_data->IPC = _ipc_send_internal;

    /* Initialize inter-processor communications protocols */
    protocol_init_ptr = ipc_init_ptr->PROTOCOL_LIST_PTR;
    while (protocol_init_ptr->IPC_PROTOCOL_INIT != NULL)
    {
#if MQX_CHECK_ERRORS
        if (((_queue_number) protocol_init_ptr->IPC_OUT_QUEUE) == 0
                        || ((_queue_number) protocol_init_ptr->IPC_OUT_QUEUE > MQX_MAX_PROCESSOR_NUMBER))
        {
            _task_block();
        }
#endif
        info_ptr = (IPC_PROTOCOL_INFO_STRUCT_PTR) _mem_alloc_zero((_mem_size) sizeof(IPC_PROTOCOL_INFO_STRUCT));
#if MQX_CHECK_MEMORY_ALLOCATION_ERRORS
        if (info_ptr == NULL)
        {
            _task_block();
        }
#endif
        info_ptr->IPC_OUT_QUEUE = protocol_init_ptr->IPC_OUT_QUEUE;
        info_ptr->IPC_NAME = protocol_init_ptr->IPC_NAME;
        info_ptr->IPC_OUT_QID = _msgq_get_id(0, (_queue_number) protocol_init_ptr->IPC_OUT_QUEUE);
        info_ptr->IPC_INIT_PTR = protocol_init_ptr;

        (*protocol_init_ptr->IPC_PROTOCOL_INIT)(protocol_init_ptr, info_ptr);

        protocol_init_ptr++;
    }

    /*
     * The IPC Initialization is complete. Wait for messages to come in
     * and service them.
     */
    while (TRUE)
    {

        _task_set_error(MQX_OK);
        msg_ptr = (IPC_MESSAGE_STRUCT_PTR) _msgq_receive(MSGQ_ANY_QUEUE, 0L);
        if (MSG_MUST_CONVERT_DATA_ENDIAN(msg_ptr->HEADER.CONTROL))
        {
            _msg_swap_endian_data((unsigned char *) ((void *) _ipc_msg_type_def), (MESSAGE_HEADER_STRUCT_PTR)(
                            (void *) msg_ptr));
            if (msg_ptr->MESSAGE_TYPE & IPC_MQX_TYPE_MASK)
            {
                _mem_swap_endian((unsigned char *) _ipc_mqx_msg_def, &msg_ptr->NUMBER_OF_PARAMETERS);
            }
            else
            {
                _mem_swap_endian_len((unsigned char *) _ipc_msg_def, &msg_ptr->NUMBER_OF_PARAMETERS,
                                msg_ptr->NUMBER_OF_PARAMETERS + 2);
            }
        }

        if (msg_ptr->MESSAGE_TYPE & IPC_MQX_TYPE_MASK)
        {

            ipc_msg_ptr = (IPC_MQX_MESSAGE_STRUCT_PTR)((void *) msg_ptr);
            switch (ipc_msg_ptr->MESSAGE_TYPE & IPC_MQX_TYPE_MASK)
            {

                case IPC_MQX_CREATE:
                    _KLOGE4(KLOG_task_create, 0, ipc_msg_ptr->TEMPLATE_INDEX,ipc_msg_ptr->CREATE_PARAMETER)
                    ;
                    if (ipc_msg_ptr->TEMPLATE_INDEX == 0)
                    {
                        td_ptr = _task_build_internal(0, (uint32_t) &ipc_msg_ptr->TEMPLATE, NULL, 0, FALSE);
                    }
                    else
                    {
                        td_ptr = _task_build_internal(ipc_msg_ptr->TEMPLATE_INDEX,
                                        (uint32_t) ipc_msg_ptr->CREATE_PARAMETER, NULL, 0, FALSE);
                    }

                    _KLOGX3(KLOG_task_create, MQX_NULL_TASK_ID, MQX_INVALID_PROCESSOR_NUMBER)
                    ;
                    if (td_ptr != NULL)
                    {
                        td_ptr->PARENT = ipc_msg_ptr->REQUESTOR_ID;
                        _task_ready(td_ptr);
                        _KLOGX3(KLOG_task_create, td_ptr->TASK_ID, MQX_OK);
                    }
                    else
                    {
                        _KLOGX3(KLOG_task_create, MQX_NULL_TASK_ID, _task_get_error());
                    }
                    result = (_mqx_uint) td_ptr->TASK_ID;
                    _ipc_send_internal(FALSE, PROC_NUMBER_FROM_TASKID(msg_ptr->REQUESTOR_ID), KERNEL_MESSAGES,
                                    IPC_ACTIVATE, (_mqx_uint) 3, result, msg_ptr->REQUESTOR_ID,
                                    kernel_data->ACTIVE_PTR->TASK_ERROR_CODE);
                    break;

                case IPC_MQX_ABORT:
                    result = (_mqx_uint) _task_abort(ipc_msg_ptr->VICTIM_ID);
                    break;
                case IPC_MQX_RESTART:
                    result = (_mqx_uint) _task_restart(ipc_msg_ptr->VICTIM_ID, NULL, TRUE);
                    break;
                case IPC_MQX_DESTROY:
                    result = (_mqx_uint) _task_destroy(ipc_msg_ptr->VICTIM_ID);
                    break;

                case IPC_MQX_ACTIVATE:
                    /* Start the specified task, setting the INFO
                     * to the returned result.
                     */
                    td_ptr = (TD_STRUCT_PTR) _task_get_td(ipc_msg_ptr->REQUESTOR_ID);
                    if (td_ptr != NULL)
                    {
                        if (td_ptr->STATE == SEND_BLOCKED)
                        {
                            td_ptr->INFO = ipc_msg_ptr->VICTIM_ID;
                            _task_set_error_td_internal(td_ptr, ipc_msg_ptr->CREATE_PARAMETER);
                            _task_ready(td_ptr);
                        }
                    }
                    result = MQX_OK;
                    break;

            }

            _msg_free((void *) msg_ptr);

        }
        else
        {

            component = IPC_GET_COMPONENT(msg_ptr->MESSAGE_TYPE);

            if (component < MAX_KERNEL_COMPONENTS)
            {
                handler = ipc_component_ptr->IPC_COMPONENT_HANDLER[component];
#if MQX_CHECK_ERRORS
                if (handler)
                {
#endif
                    _task_set_error(MQX_OK);
                    result = (*handler)((void *) msg_ptr);
#if MQX_CHECK_ERRORS
                }
                else
                {
                    result = MQX_IPC_SERVICE_NOT_AVAILABLE;
                }
#endif
            }
            else if (component < (MAX_KERNEL_COMPONENTS + MAX_IO_COMPONENTS))
            {
                handler = ipc_component_ptr->IPC_IO_COMPONENT_HANDLER[component - MAX_KERNEL_COMPONENTS];
#if MQX_CHECK_ERRORS
                if (handler)
                {
#endif
                    _task_set_error(MQX_OK);
                    result = (*handler)((void *) msg_ptr);
#if MQX_CHECK_ERRORS
                }
                else
                {
                    result = MQX_IPC_SERVICE_NOT_AVAILABLE;
                    _msg_free((void *) msg_ptr);
                }
#endif
            }
            else
            {
                result = MQX_IPC_INVALID_MESSAGE;
                _msg_free((void *) msg_ptr);
            }
        }
    }
}

/*!
 * \private
 * 
 * \brief Creates an IPC message and sends it.
 * 
 * \param[in] blocking         Specifies whether or not to block current task.
 * \param[in] processor_number The processor to send to.
 * \param[in] component        The component to receive the message.
 * \param[in] type             The type of component message.
 * \param[in] num_parameters   The number of parameters in the message.
 * 
 * \return MQX_OK
 * \return Task error code from ....  
 */ 
_mqx_uint _ipc_send_internal
(
    bool           blocking,
    _processor_number processor_number,
    _mqx_uint         component,
    _mqx_uint         type,
    _mqx_uint         num_parameters,
    ...)
{
    KERNEL_DATA_STRUCT_PTR     kernel_data;
    IPC_MESSAGE_STRUCT_PTR     msg_ptr;
    IPC_MQX_MESSAGE_STRUCT_PTR ipc_msg_ptr;
    va_list                    ap;
    _mqx_uint                  i;

    _GET_KERNEL_DATA(kernel_data);

    kernel_data->ACTIVE_PTR->TASK_ERROR_CODE = MQX_OK;

    msg_ptr = (IPC_MESSAGE_STRUCT_PTR) _msg_alloc((_pool_id) kernel_data->IPC_NAMED_POOL);
    while (msg_ptr == NULL)
    {
        _time_delay(kernel_data->KERNEL_ALARM_RESOLUTION * 2);
        msg_ptr = (IPC_MESSAGE_STRUCT_PTR) _msg_alloc((_pool_id) kernel_data->IPC_NAMED_POOL);
    }

    msg_ptr->HEADER.TARGET_QID = BUILD_QID(processor_number, IPC_MESSAGE_QUEUE_NUMBER);
    msg_ptr->HEADER.SOURCE_QID = BUILD_QID((_processor_number) kernel_data->INIT.PROCESSOR_NUMBER,
                    IPC_MESSAGE_QUEUE_NUMBER);

    va_start(ap, num_parameters);

    if (component == KERNEL_MESSAGES)
    {
        ipc_msg_ptr = (IPC_MQX_MESSAGE_STRUCT_PTR)((void *) msg_ptr);
        ipc_msg_ptr->REQUESTOR_ID = kernel_data->ACTIVE_PTR->TASK_ID;
        switch (type)
        {
            case IPC_TASK_CREATE:
            case IPC_TASK_CREATE_WITH_TEMPLATE:
                ipc_msg_ptr->MESSAGE_TYPE = IPC_MQX_CREATE;
                if (type == IPC_TASK_CREATE_WITH_TEMPLATE)
                {
                    ipc_msg_ptr->TEMPLATE_INDEX = 0;
                    ipc_msg_ptr->TEMPLATE.TASK_TEMPLATE_INDEX = va_arg(ap, uint32_t);
                    ipc_msg_ptr->TEMPLATE.TASK_ADDRESS = (TASK_FPTR)va_arg(ap, _psp_code_addr);
                    ipc_msg_ptr->TEMPLATE.TASK_STACKSIZE = va_arg(ap, _mem_size);
                    ipc_msg_ptr->TEMPLATE.TASK_PRIORITY = va_arg(ap, _mqx_uint);
                    ipc_msg_ptr->TEMPLATE.TASK_NAME = va_arg(ap, char *);
                    ipc_msg_ptr->TEMPLATE.TASK_ATTRIBUTES = va_arg(ap, _mqx_uint);
                    ipc_msg_ptr->TEMPLATE.CREATION_PARAMETER = va_arg(ap, uint32_t);
                    ipc_msg_ptr->CREATE_PARAMETER = ipc_msg_ptr->TEMPLATE.CREATION_PARAMETER;
                    ipc_msg_ptr->TEMPLATE.DEFAULT_TIME_SLICE = va_arg(ap, uint32_t);
                    ipc_msg_ptr->HEADER.SIZE = sizeof(IPC_MQX_MESSAGE_STRUCT);
                }
                else
                {
                    i = va_arg(ap, _mqx_uint); /* Toss processor number */
                    ipc_msg_ptr->TEMPLATE_INDEX = va_arg(ap, _mqx_uint);
                    ipc_msg_ptr->CREATE_PARAMETER = (uint32_t) va_arg(ap, uint32_t);
                    ipc_msg_ptr->HEADER.SIZE = sizeof(IPC_MQX_MESSAGE_STRUCT) - sizeof(TASK_TEMPLATE_STRUCT);
                }
                break;
            case IPC_TASK_DESTROY:
                ipc_msg_ptr->MESSAGE_TYPE = IPC_MQX_DESTROY;
                ipc_msg_ptr->VICTIM_ID = va_arg(ap, _mqx_uint);
                ipc_msg_ptr->HEADER.SIZE = sizeof(IPC_MQX_MESSAGE_STRUCT) - sizeof(TASK_TEMPLATE_STRUCT);
                break;
            case IPC_TASK_ABORT:
                ipc_msg_ptr->MESSAGE_TYPE = IPC_MQX_ABORT;
                ipc_msg_ptr->VICTIM_ID = va_arg(ap, _mqx_uint);
                ipc_msg_ptr->HEADER.SIZE = sizeof(IPC_MQX_MESSAGE_STRUCT) - sizeof(TASK_TEMPLATE_STRUCT);
                break;
            case IPC_TASK_RESTART:
                ipc_msg_ptr->MESSAGE_TYPE = IPC_MQX_RESTART;
                ipc_msg_ptr->VICTIM_ID = va_arg(ap, _mqx_uint);
                ipc_msg_ptr->HEADER.SIZE = sizeof(IPC_MQX_MESSAGE_STRUCT) - sizeof(TASK_TEMPLATE_STRUCT);
                break;
            case IPC_ACTIVATE:
                ipc_msg_ptr->MESSAGE_TYPE = IPC_MQX_ACTIVATE;
                ipc_msg_ptr->VICTIM_ID = va_arg(ap, _mqx_uint);
                ipc_msg_ptr->REQUESTOR_ID = va_arg(ap, _mqx_uint);
                ipc_msg_ptr->CREATE_PARAMETER = (uint32_t) va_arg(ap, uint32_t);
                ipc_msg_ptr->HEADER.SIZE = sizeof(IPC_MQX_MESSAGE_STRUCT) - sizeof(TASK_TEMPLATE_STRUCT);
                break;
        }
    }
    else
    {
        msg_ptr->MESSAGE_TYPE = IPC_SET_COMPONENT(0, component);
        msg_ptr->MESSAGE_TYPE = IPC_SET_TYPE(msg_ptr->MESSAGE_TYPE, type);
        if (!blocking)
        {
            msg_ptr->MESSAGE_TYPE = IPC_SET_NON_BLOCKING(msg_ptr->MESSAGE_TYPE, 1);
        }

        msg_ptr->REQUESTOR_ID = kernel_data->ACTIVE_PTR->TASK_ID;

        if (num_parameters > IPC_MAX_PARAMETERS)
        {
            num_parameters = IPC_MAX_PARAMETERS;
        }

        if (type & IPC_STRING_PARAMETER)
        {
            --num_parameters;
            msg_ptr->HEADER.SIZE = (_msg_size) sizeof(IPC_MESSAGE_STRUCT);
        }
        else
        {
            msg_ptr->HEADER.SIZE = (_msg_size)(sizeof(IPC_MESSAGE_STRUCT) - (IPC_MAX_PARAMETERS * sizeof(uint32_t))
                            + (num_parameters * sizeof(uint32_t)));
        }

        msg_ptr->NUMBER_OF_PARAMETERS = (uint32_t) num_parameters;

        for (i = 0; (i < num_parameters) && (i < IPC_MAX_PARAMETERS); ++i)
        {
            msg_ptr->PARAMETERS[i] = (uint32_t) (va_arg(ap, uint32_t));
        }

        if (type & IPC_STRING_PARAMETER)
        {
            char *to_ptr = (char *) &msg_ptr->PARAMETERS[i];
            char *from_ptr = (char *) va_arg(ap, void *);
            uint32_t size = (IPC_MAX_PARAMETERS - i) * sizeof(uint32_t);
            strncpy((void *) to_ptr, (void *) from_ptr, size);
            to_ptr[size - 1] = '\0';
        }
    }

    va_end(ap);

    if (blocking)
    {
        if (_msgq_send_blocked_internal(msg_ptr))
        {
            return MQX_OK;
        }
    }
    else
    {
        if (_msgq_send(msg_ptr))
        {
            return MQX_OK;
        }
    }

    return kernel_data->ACTIVE_PTR->TASK_ERROR_CODE;
}
#endif /* MQX_USE_IPC */

/* EOF */
