
/*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 Partititon component.
*
*
*END************************************************************************/

#include "mqx_inc.h"
#if MQX_USE_PARTITIONS
#include "partition.h"
#include "partition_prv.h"

/*!
 * \brief Allocates a private partition block from the partition.
 * 
 * The functions allocate a fixed-size memory block, which the task owns.
 * 
 * \param[in] partition Partition from which to allocate the partition block.
 * 
 * \return Pointer to the partition block (success).
 * \return NULL (failure)
 * 
 * \warning On failure, calls _task_set_error() to set one of the following task 
 * error codes:
 * \li PARTITION_INVALID (Partition does not represent a valid partition.)
 * \li PARTITION_OUT_OF_BLOCKS (All the partition blocks in the partition are 
 * allocated (for static partitions only).)
 * \li PARTITION_BLOCK_INVALID_CHECKSUM (MQX found an incorrect checksum in the 
 * partition block header.)
 * \li Task error code set by _mem_alloc_system() MQX cannot allocate memory for 
 * the partition block (for dynamic partitions only).     
 * 
 * \see _partition_alloc_zero
 * \see _partition_alloc_system
 * \see _partition_alloc_system_zero
 * \see _partition_create
 * \see _task_set_error
 * \see _mem_alloc_system
 */ 
void *_partition_alloc
(
    _partition_id partition
)
{ /* Body */
    KERNEL_DATA_STRUCT_PTR kernel_data;
    void                  *result;

    _GET_KERNEL_DATA(kernel_data);

    _KLOGE2(KLOG_partition_alloc, partition);

    result = _partition_alloc_internal((PARTPOOL_STRUCT_PTR) partition, kernel_data->ACTIVE_PTR);

    _KLOGX2(KLOG_partition_alloc, result);

    return (result);

} /* Endbody */

/*!
 * \private
 * 
 * \brief Allocates a private partition block from the partition.
 * 
 * \param[in] partpool_ptr The partition to obtain the memory block from.
 * \param[in] td_ptr       The task to own the partition block.
 * 
 * \return Pointer to the partition block (success).
 * \return NULL (failure)
 * 
 * \warning On failure, calls _task_set_error() to set one of the following task 
 * error codes:
 * \li PARTITION_INVALID (Partition does not represent a valid partition.)
 * \li PARTITION_OUT_OF_BLOCKS (All the partition blocks in the partition are 
 * allocated (for static partitions only).)
 * \li PARTITION_BLOCK_INVALID_CHECKSUM (MQX found an incorrect checksum in the 
 * partition block header.)
 * \li Task error code set by _mem_alloc_system() MQX cannot allocate memory for 
 * the partition block (for dynamic partitions only). 
 */ 
void *_partition_alloc_internal
(
    PARTPOOL_STRUCT_PTR partpool_ptr,
    TD_STRUCT_PTR       td_ptr
)
{ /* Body */
    KERNEL_DATA_STRUCT_PTR              kernel_data = NULL;
    (void)kernel_data;
    INTERNAL_PARTITION_BLOCK_STRUCT_PTR block_ptr;
    PARTPOOL_BLOCK_STRUCT_PTR           partpool_block_ptr;
    _mqx_uint                           number_of_blocks;

#if MQX_CHECK_ERRORS
    if (partpool_ptr == NULL)
    {
        _task_set_error(PARTITION_INVALID);
        return NULL;
    } /* Endif */
#endif /* MQX_CHECK_ERRORS */

#if MQX_CHECK_VALIDITY
    if (partpool_ptr->POOL.VALID != PARTITION_VALID)
    {
        _task_set_error(PARTITION_INVALID);
        return NULL;
    } /* Endif */
#endif /* MQX_CHECK_VALIDITY */

    _GET_KERNEL_DATA(kernel_data);

    _INT_DISABLE(); // uses kernel data - based on DEFINE combination

    if (partpool_ptr->AVAILABLE == 0)
    {
        if ((partpool_ptr->PARTITION_TYPE == PARTITION_DYNAMIC) && partpool_ptr->GROW_BLOCKS
                        && ((partpool_ptr->MAXIMUM_BLOCKS == 0) || (partpool_ptr->TOTAL_BLOCKS
                                        < partpool_ptr->MAXIMUM_BLOCKS)))
        {
            /* We can attempt to grow the partition */
            number_of_blocks = partpool_ptr->GROW_BLOCKS;
            if ((partpool_ptr->MAXIMUM_BLOCKS != 0) && (number_of_blocks > (partpool_ptr->MAXIMUM_BLOCKS
                            - partpool_ptr->TOTAL_BLOCKS)))
            {
                number_of_blocks = partpool_ptr->MAXIMUM_BLOCKS - partpool_ptr->TOTAL_BLOCKS;
            } /* Endif */

            /* 
             * Temporarily boost up TOTAL BLOCKS to allow us to
             * enable interrupts during memory allocation
             */
            partpool_ptr->TOTAL_BLOCKS += number_of_blocks;
            _int_enable();

#if PSP_MEMORY_ALIGNMENT
            partpool_block_ptr = (PARTPOOL_BLOCK_STRUCT_PTR) _mem_alloc_system(
                            (_mem_size) (sizeof(PARTPOOL_BLOCK_STRUCT) + PSP_MEMORY_ALIGNMENT
                                            + (partpool_ptr->BLOCK_SIZE * number_of_blocks)));
#else
            partpool_block_ptr = _mem_alloc_system((_mem_size)
                            (sizeof(PARTPOOL_BLOCK_STRUCT) +
                                            (partpool_ptr->BLOCK_SIZE * number_of_blocks)));
#endif /* PSP_MEMORY_ALIGNMENT */
            if (partpool_block_ptr != NULL)
            {
                _mem_set_type(partpool_block_ptr, MEM_TYPE_PARTITION_BLOCKS);

                _partition_extend_internal(partpool_ptr, partpool_block_ptr, number_of_blocks);
            }
            else
            {
                partpool_ptr->TOTAL_BLOCKS -= number_of_blocks;
            } /* Endif */

            _int_disable();

        } /* Endif */

        if (((volatile PARTPOOL_STRUCT *) partpool_ptr)->AVAILABLE == 0)
        {
            _int_enable();
            _task_set_error(PARTITION_OUT_OF_BLOCKS);
            return NULL;
        } /* Endif */
    } /* Endif */

    block_ptr = partpool_ptr->FREE_LIST_PTR;

#if MQX_CHECK_VALIDITY
    if (!VALID_PARTITION_CHECKSUM(block_ptr))
    {
        _int_enable();
        _task_set_error(PARTITION_BLOCK_INVALID_CHECKSUM);
        return NULL;
    } /* Endif */
#endif /* MQX_CHECK_VALIDITY */

    partpool_ptr->FREE_LIST_PTR = block_ptr->LINK.NEXT_BLOCK_PTR;
    --partpool_ptr->AVAILABLE;

    if ((partpool_ptr->TOTAL_BLOCKS - partpool_ptr->AVAILABLE) > partpool_ptr->MAX_BLOCKS_USED)
    {
        partpool_ptr->MAX_BLOCKS_USED = partpool_ptr->TOTAL_BLOCKS - partpool_ptr->AVAILABLE;
    }/* Endif */
    block_ptr->TASK_ID = td_ptr->TASK_ID;
    block_ptr->LINK.PARTITION_PTR = partpool_ptr;

    CALC_PARTITION_CHECKSUM(block_ptr);

    /* Reset partition test walker */
    partpool_ptr->TEST_FREE_PTR = partpool_ptr->FREE_LIST_PTR;

    _INT_ENABLE();

    return (void *) ((unsigned char *) block_ptr + sizeof(INTERNAL_PARTITION_BLOCK_STRUCT));

} /* Endbody */

/*!
 * \brief Gets the number of free partition blocks in the partition.
 * 
 * \param[in] partition Partition about which to get info.
 * 
 * \return Number of free partition blocks (success).
 * \return MAX_MQX_UINT (failure)
 * 
 * \warning On failure, calls _task_set_error() to set the following task error 
 * code:
 * \li PARTITION_INVALID (Partition does not represent a valid partition.)
 * 
 * \see _partition_get_block_size
 * \see _partition_get_max_used_blocks
 * \see _partition_get_total_blocks
 * \see _partition_get_total_size
 * \see _task_set_error 
 */ 
_mqx_uint _partition_get_free_blocks
(
    _partition_id partition
)
{ /* Body */
    PARTPOOL_STRUCT_PTR partpool_ptr = (PARTPOOL_STRUCT_PTR) partition;

#if MQX_CHECK_VALIDITY
    if (partpool_ptr->POOL.VALID != PARTITION_VALID)
    {
        _task_set_error(PARTITION_INVALID);
        return (MAX_MQX_UINT);
    } /* Endif */
#endif /* MQX_CHECK_VALIDITY */

    return (partpool_ptr->AVAILABLE);

} /* Endbody */

/*!
 * \brief Creates the partition kernel component.
 * 
 * \return MQX_OK
 * \return MQX_CANNOT_CALL_FUNCTION_FROM_ISR (Function cannot be called from an ISR.)
 * \return MQX_OUT_OF_MEMORY (MQX is out of memory.)
 * 
 * \warning Cannot be called from an ISR.
 * \warning Might block the calling task.
 * 
 * \see _partition_create
 * \see _partition_destroy   
 */ 
_mqx_uint _partition_create_component(void)
{ /* Body */
    KERNEL_DATA_STRUCT_PTR         kernel_data;
    PARTITION_COMPONENT_STRUCT_PTR part_component_ptr;

    _GET_KERNEL_DATA(kernel_data);

    _KLOGE1(KLOG_partition_create_component);

#if MQX_CHECK_ERRORS
    if (kernel_data->IN_ISR)
    {
        _KLOGX2(KLOG_partition_create_component, MQX_CANNOT_CALL_FUNCTION_FROM_ISR);
        return (MQX_CANNOT_CALL_FUNCTION_FROM_ISR);
    } /* Endif */
#endif /* MQX_CHECK_ERRORS */

    _lwsem_wait((LWSEM_STRUCT_PTR) &kernel_data->COMPONENT_CREATE_LWSEM);

#if MQX_CHECK_ERRORS
    if (kernel_data->KERNEL_COMPONENTS[KERNEL_PARTITIONS] != NULL)
    {
        _lwsem_post((LWSEM_STRUCT_PTR) &kernel_data->COMPONENT_CREATE_LWSEM);
        _KLOGX2(KLOG_partition_create_component, MQX_OK);
        return (MQX_OK);
    } /* Endif */
#endif /* MQX_CHECK_ERRORS */

    part_component_ptr = _mem_alloc_system_zero((_mem_size) sizeof(PARTITION_COMPONENT_STRUCT));
#if MQX_CHECK_MEMORY_ALLOCATION_ERRORS
    if (part_component_ptr == NULL)
    {
        _lwsem_post((LWSEM_STRUCT_PTR) &kernel_data->COMPONENT_CREATE_LWSEM);
        _KLOGX2(KLOG_partition_create_component, MQX_OUT_OF_MEMORY);
        return (MQX_OUT_OF_MEMORY);
    } /* Endif */
#endif /* MQX_CHECK_MEMORY_ALLOCATION_ERRORS */
    _mem_set_type(part_component_ptr, MEM_TYPE_PARTITION_COMPONENT);

    kernel_data->KERNEL_COMPONENTS[KERNEL_PARTITIONS] = part_component_ptr;
    part_component_ptr->VALID = PARTITION_VALID;

    _QUEUE_INIT(&part_component_ptr->PARTITIONS, 0);

#if MQX_COMPONENT_DESTRUCTION
    kernel_data->COMPONENT_CLEANUP[KERNEL_PARTITIONS] = _partition_cleanup;
#endif /* MQX_COMPONENT_DESTRUCTION */

    _lwsem_post((LWSEM_STRUCT_PTR) &kernel_data->COMPONENT_CREATE_LWSEM);

    _KLOGX2(KLOG_partition_create_component, MQX_OK);

    return (MQX_OK);

} /* Endbody */

/*!
 * \brief Creates the partition at the specific location outside the default 
 * memory pool (a static partition).
 * 
 * \param[in] partition_location Pointer to the start of the partition.
 * \param[in] partition_size     The size of the partition.
 * \param[in] block_size         Number of single-addressable units in each 
 * partition block in the partition.
 * 
 * \return Partition ID (success).
 * \return PARTITION_NULL_ID (failure)
 * 
 * \warning Creates the partition component if it were not previously created.
 * \warning On failure, calls _task_set_error() to set the following task error 
 * code:
 * \li MAX_INVALID_PARAMETER (One of the following: block_size is 0 or 
 * partition_size is too small.)  
 * 
 * \see _partition_alloc
 * \see _partition_alloc_zero
 * \see _partition_alloc_system
 * \see _partition_alloc_system_zero
 * \see _partition_calculate_size
 * \see _partition_create
 * \see _partition_extend
 * \see _task_set_error
 */ 
_partition_id _partition_create_at
(
    void     *partition_location,
    _mem_size partition_size,
    _mem_size block_size
)
{ /* Body */
    _KLOGM(KERNEL_DATA_STRUCT_PTR kernel_data);
    PARTPOOL_STRUCT_PTR partpool_ptr;
    unsigned char       *block_area_ptr;
    _mem_size           block_area_size;
    _mqx_uint           number_of_blocks;
    _mqx_uint           result;

    _KLOGM(_GET_KERNEL_DATA(kernel_data));

    _KLOGE4(KLOG_partition_create_at, partition_location, partition_size, block_size);

#if MQX_CHECK_ERRORS
    if (block_size == 0)
    {
        _task_set_error(MQX_INVALID_PARAMETER);
        _KLOGX2(KLOG_partition_create_at, PARTITION_NULL_ID);
        return (PARTITION_NULL_ID);
    } /* Endif */
#endif /* MQX_CHECK_ERRORS */

    partpool_ptr = (PARTPOOL_STRUCT_PTR)_ALIGN_ADDR_TO_HIGHER_MEM(partition_location);
    block_area_ptr = (unsigned char *) partpool_ptr + sizeof(PARTPOOL_STRUCT);
    block_area_ptr = (unsigned char *) _ALIGN_ADDR_TO_HIGHER_MEM(block_area_ptr);

#if MQX_CHECK_ERRORS
    if (partition_size < (_mem_size) block_area_ptr - (_mem_size) partition_location)
    {
        _task_set_error(MQX_INVALID_PARAMETER);
        _KLOGX2(KLOG_partition_create_at, PARTITION_NULL_ID);
        return (PARTITION_NULL_ID);
    } /* Endif */
#endif /* MQX_CHECK_ERRORS */
    block_area_size = partition_size - ((_mem_size) block_area_ptr - (_mem_size) partition_location);
    block_size += (_mem_size) sizeof(INTERNAL_PARTITION_BLOCK_STRUCT);
    _MEMORY_ALIGN_VAL_LARGER(block_size);

    number_of_blocks = block_area_size / block_size;
#if MQX_CHECK_ERRORS
    if (number_of_blocks == 0)
    {
        _task_set_error(MQX_INVALID_PARAMETER);
        _KLOGX2(KLOG_partition_create_at, PARTITION_NULL_ID);
        return (PARTITION_NULL_ID);
    } /* Endif */
#endif /* MQX_CHECK_ERRORS */

    result = _partition_create_internal(partpool_ptr, block_size, number_of_blocks);
#if MQX_CHECK_ERRORS
    if (result != MQX_OK)
    {
        _task_set_error(MQX_INVALID_PARAMETER);
        _KLOGX2(KLOG_partition_create_at, result);
        return (PARTITION_NULL_ID);
    } /* Endif */
#endif /* MQX_CHECK_ERRORS */

    partpool_ptr->PARTITION_TYPE = PARTITION_STATIC;

    _KLOGX5(KLOG_partition_create_at, partpool_ptr, partition_size, block_size, MQX_OK);
    return ((_partition_id) partpool_ptr);

} /* Endbody */

/*!
 * \private
 * 
 * \brief Create a partition at a location with a specified number of blocks.
 * 
 * \param[in] partpool_ptr   The start of the partition.
 * \param[in] actual_size    The total size of each block with overheads.
 * \param[in] initial_blocks The initial number of blocks in the partition.
 * 
 * \return MQX_OK
 * \return MQX_CANNOT_CALL_FUNCTION_FROM_ISR (Function cannot be called from an ISR.)
 * \return MQX_OUT_OF_MEMORY (MQX is out of memory.)   
 */ 
_mqx_uint _partition_create_internal
(
    PARTPOOL_STRUCT_PTR partpool_ptr,
    _mem_size           actual_size,
    _mqx_uint           initial_blocks
)
{ /* Body */
    KERNEL_DATA_STRUCT_PTR              kernel_data;
    INTERNAL_PARTITION_BLOCK_STRUCT_PTR block_ptr;
    PARTITION_COMPONENT_STRUCT_PTR      part_component_ptr;
    _mqx_uint                           result;

    _GET_KERNEL_DATA(kernel_data);

    part_component_ptr = (PARTITION_COMPONENT_STRUCT_PTR) kernel_data->KERNEL_COMPONENTS[KERNEL_PARTITIONS];
    if (part_component_ptr == NULL)
    {
        result = _partition_create_component();
        part_component_ptr = (PARTITION_COMPONENT_STRUCT_PTR) kernel_data->KERNEL_COMPONENTS[KERNEL_PARTITIONS];
#if MQX_CHECK_MEMORY_ALLOCATION_ERRORS
        if (part_component_ptr == NULL)
        {
            return (result);
        } /* Endif */
#endif /* MQX_CHECK_MEMORY_ALLOCATION_ERRORS */
    } /* Endif */

    _mem_zero(partpool_ptr, (_mem_size) sizeof(PARTPOOL_STRUCT));

    partpool_ptr->BLOCK_SIZE = actual_size;
    partpool_ptr->POOL.VALID = PARTITION_VALID;
    partpool_ptr->POOL.NUMBER_OF_BLOCKS = initial_blocks;
    partpool_ptr->AVAILABLE = initial_blocks;
    partpool_ptr->TOTAL_BLOCKS = initial_blocks;

    block_ptr = (INTERNAL_PARTITION_BLOCK_STRUCT_PTR)((unsigned char *) partpool_ptr + sizeof(PARTPOOL_STRUCT));

    block_ptr = (INTERNAL_PARTITION_BLOCK_STRUCT_PTR)
    _ALIGN_ADDR_TO_HIGHER_MEM(block_ptr);

    partpool_ptr->POOL.FIRST_IBLOCK_PTR = block_ptr;

    while (initial_blocks--)
    {
        block_ptr->LINK.NEXT_BLOCK_PTR = partpool_ptr->FREE_LIST_PTR;
        CALC_PARTITION_CHECKSUM(block_ptr);
        partpool_ptr->FREE_LIST_PTR = block_ptr;
        block_ptr = (INTERNAL_PARTITION_BLOCK_STRUCT_PTR)((unsigned char *) block_ptr + actual_size);
    } /* Endwhile */

    _int_disable();
    _QUEUE_ENQUEUE(&part_component_ptr->PARTITIONS, partpool_ptr);
    _int_enable();

    return (MQX_OK);

} /* Endbody */

/*!
 * \brief Creates the partition in the default memory pool (a dynamic partition).
 * 
 * The function creates a partition of fixed-size partition blocks in the default 
 * memory pool.
 * 
 * \param[in] block_size     Number of single-addressable units in each partition 
 * block.
 * \param[in] initial_blocks The initial number of blocks in the partition.
 * \param[in] grow_blocks    Number of blocks by which to grow the partition if 
 * all the partition blocks are allocated.
 * \param[in] maximum_blocks If grow_blocks is not 0, one of:
 * \li Maximum number of blocks in the partition.
 * \li 0 (Unlimited growth.)
 * 
 * \return Partition ID (success).
 * \return PARTITION_NULL_ID (failure)
 * 
 * \warning Creates the partition component if it were not previously created.
 * \warning On failure, calls _task_set_error() to set one of the following task 
 * error codes:
 * \li MQX_INVALID_PARAMETER (Block_size is 0.)
 * \li Task error codes returned by _mem_alloc_system()
 * 
 * \see _partition_alloc
 * \see _partition_alloc_zero
 * \see _partition_alloc_system
 * \see _partition_alloc_system_zero
 * \see _partition_calculate_size
 * \see _partition_create_at
 * \see _partition_destroy
 * \see _task_set_error
 * \see _mem_alloc_system 
 */ 
_partition_id _partition_create
(
    _mem_size block_size,
    _mqx_uint initial_blocks,
    _mqx_uint grow_blocks,
    _mqx_uint maximum_blocks
)
{ /* Body */
    KERNEL_DATA_STRUCT_PTR kernel_data;
    PARTPOOL_STRUCT_PTR    partpool_ptr;
    _mem_size              actual_size;
    _mqx_uint              result;

    _GET_KERNEL_DATA(kernel_data);

    _KLOGE5(KLOG_partition_create, block_size, initial_blocks, grow_blocks,
                    maximum_blocks);

#if MQX_CHECK_ERRORS
    if (kernel_data->IN_ISR)
    {
        _task_set_error(MQX_CANNOT_CALL_FUNCTION_FROM_ISR);
        _KLOGX2(KLOG_partition_create, PARTITION_NULL_ID);
        return (PARTITION_NULL_ID);
    } /* Endif */

    if (block_size == 0)
    {
        _task_set_error(MQX_INVALID_PARAMETER);
        _KLOGX2(KLOG_partition_create, PARTITION_NULL_ID);
        return (PARTITION_NULL_ID);
    } /* Endif */
#endif /* MQX_CHECK_ERRORS */

    actual_size = (_mem_size) sizeof(INTERNAL_PARTITION_BLOCK_STRUCT) + block_size;
    _MEMORY_ALIGN_VAL_LARGER(actual_size);

#if PSP_MEMORY_ALIGNMENT
    partpool_ptr = (PARTPOOL_STRUCT_PTR) _mem_alloc_system((_mem_size) (sizeof(PARTPOOL_STRUCT) + PSP_MEMORY_ALIGNMENT
                    + (actual_size * initial_blocks)));
#else
    partpool_ptr = ((PARTPOOL_STRUCT_PTR))_mem_alloc_system(
                    (_mem_size)(sizeof(PARTPOOL_STRUCT) + (actual_size * initial_blocks)));
#endif /* PSP_MEMORY_ALIGNMENT */
#if MQX_CHECK_MEMORY_ALLOCATION_ERRORS
    if (partpool_ptr == NULL)
    {
        _KLOGX2(KLOG_partition_create, PARTITION_NULL_ID);
        return (PARTITION_NULL_ID);
    }/* Endif */
#endif /* MQX_CHECK_MEMORY_ALLOCATION_ERRORS */
    _mem_set_type(partpool_ptr, MEM_TYPE_PARTITION);

    result = _partition_create_internal(partpool_ptr, actual_size, initial_blocks);
#if MQX_CHECK_MEMORY_ALLOCATION_ERRORS
    if (result != MQX_OK)
    {
        _KLOGX2(KLOG_partition_create, PARTITION_NULL_ID);
        _mem_free(partpool_ptr);
        return (PARTITION_NULL_ID);
    } /* Endif */
#endif /* MQX_CHECK_MEMORY_ALLOCATION_ERRORS */

    partpool_ptr->PARTITION_TYPE = PARTITION_DYNAMIC;
    partpool_ptr->GROW_BLOCKS = grow_blocks;
    partpool_ptr->MAXIMUM_BLOCKS = maximum_blocks;

    _KLOGX3(KLOG_partition_create, partpool_ptr, MQX_OK);
    return ((_partition_id) partpool_ptr);

} /* Endbody */

/*!
 * \brief Destroys a partition that is in the default memory pool (a dynamic 
 * partition).
 * 
 * If all the partition blocks in a dynamic partition are first freed, any task 
 * can destroy the partition.
 * 
 * \param[in] partition Partition ID of the partition to destroy.
 * 
 * \return MQX_OK 
 * \return MQX_COMPONENT_DOES_NOT_EXIST (Partition component is not created.)
 * \return MQX_INVALID_PARAMETER (Partition_id is invalid.)
 * \return PARTITION_INVALID_TYPE (Partition is not a dynamic partition.)
 * \return PARTITION_ALL_BLOCKS_NOT_FREE (There are allocated partition blocks in 
 * the partition.)
 * \return Errors from _mem_free() 
 * 
 * \see _mem_free
 * \see _partition_create
 * \see _partition_free      
 */ 
_mqx_uint _partition_destroy
(
    _partition_id partition
)
{ /* Body */
    KERNEL_DATA_STRUCT_PTR         kernel_data;
    PARTPOOL_STRUCT_PTR            partpool_ptr;
    PARTPOOL_BLOCK_STRUCT_PTR      current_ptr;
    PARTPOOL_BLOCK_STRUCT_PTR      next_ptr;
    _mqx_uint                      result;
    PARTITION_COMPONENT_STRUCT_PTR part_component_ptr;

    _GET_KERNEL_DATA(kernel_data);

    _KLOGE2(KLOG_partition_destroy, partition);

    part_component_ptr = (PARTITION_COMPONENT_STRUCT_PTR) kernel_data->KERNEL_COMPONENTS[KERNEL_PARTITIONS];

    partpool_ptr = (PARTPOOL_STRUCT_PTR) partition;

#if MQX_CHECK_ERRORS
    if (part_component_ptr == NULL)
    {
        _KLOGX2(KLOG_partition_destroy, MQX_COMPONENT_DOES_NOT_EXIST);
        return MQX_COMPONENT_DOES_NOT_EXIST;
    } /* Endif */

    if (partpool_ptr->POOL.VALID != PARTITION_VALID)
    {
        _KLOGX2(KLOG_partition_destroy, MQX_INVALID_PARAMETER);
        return MQX_INVALID_PARAMETER;
    } /* Endif */

    if (partpool_ptr->PARTITION_TYPE != PARTITION_DYNAMIC)
    {
        _KLOGX2(KLOG_partition_destroy, PARTITION_INVALID_TYPE);
        return PARTITION_INVALID_TYPE;
    } /* Endif */
#endif /* MQX_CHECK_ERRORS */

    if (partpool_ptr->TOTAL_BLOCKS != partpool_ptr->AVAILABLE)
    {
        _KLOGX2(KLOG_partition_destroy, PARTITION_ALL_BLOCKS_NOT_FREE);
        return PARTITION_ALL_BLOCKS_NOT_FREE;
    } /* Endif */

    /* Remove the partition from the list of partitions maintained by MQX */
    _int_disable();
    _QUEUE_REMOVE(&part_component_ptr->PARTITIONS, partpool_ptr);
    _int_enable();

    partpool_ptr->POOL.VALID = 0;

    /* Free any extensions */
    current_ptr = partpool_ptr->POOL.NEXT_POOL_PTR;
    while (current_ptr)
    {
        next_ptr = current_ptr->NEXT_POOL_PTR;
        _mem_free(current_ptr);
        current_ptr = next_ptr;
    } /* Endwhile */

    result = _mem_free(partpool_ptr);

    _KLOGX2(KLOG_partition_destroy, result);

    return result;

} /* Endbody */

/*!
 * \brief Adds more blocks of memory to the static partition.
 * 
 * The function extends a partition that was created with _partition_create_at(). 
 * Based on the size of the partition's partition blocks, the function divides 
 * the additional memory into partition blocks and adds them to the partition.
 * 
 * \param[in] partition          Static partition to extend.
 * \param[in] partition_location Where the additional blocks are to start from.
 * \param[in] partition_size     Number of single-addressable units to add.
 * 
 * \return MQX_OK
 * \return PARTITION_INVALID (Partition does not represent a valid partition.)
 * \return MQX_INVALID_PARAMETER (One of the following:
 * \li Partition_size is 0.
 * \li Partition_id does not represent a static partition.)
 * 
 * \see _partition_create_at
 * \see _partition_alloc
 * \see _partition_alloc_zero
 */ 
_mqx_uint _partition_extend
(
    _partition_id partition,
    void         *partition_location,
    _mem_size     partition_size
)
{ /* Body */
    _KLOGM(KERNEL_DATA_STRUCT_PTR kernel_data);
    PARTPOOL_STRUCT_PTR                 partpool_ptr = (PARTPOOL_STRUCT_PTR) partition;
    PARTPOOL_BLOCK_STRUCT_PTR           partpool_block_ptr;
    _mem_size                           block_area_size;
    unsigned char                       *block_ptr;
    _mqx_uint                           number_of_blocks;

    _KLOGM(_GET_KERNEL_DATA(kernel_data));

    _KLOGE4(KLOG_partition_extend, partition, partition_location, partition_size);

#if MQX_CHECK_VALIDITY
    if (partpool_ptr->POOL.VALID != PARTITION_VALID)
    {
        _KLOGX2(KLOG_partition_extend, PARTITION_INVALID);
        return (PARTITION_INVALID);
    } /* Endif */
#endif /* MQX_CHECK_VALIDITY */

#if MQX_CHECK_ERRORS
    if (partpool_ptr->PARTITION_TYPE == PARTITION_DYNAMIC)
    {
        _KLOGX2(KLOG_partition_extend, MQX_INVALID_PARAMETER);
        return (MQX_INVALID_PARAMETER);
    } /* Endif */
#endif /* MQX_CHECK_ERRORS */

    partpool_block_ptr = (PARTPOOL_BLOCK_STRUCT_PTR)_ALIGN_ADDR_TO_HIGHER_MEM(partition_location);
    block_ptr = (unsigned char *) partpool_block_ptr + sizeof(PARTPOOL_BLOCK_STRUCT);
    block_ptr = (unsigned char *) _ALIGN_ADDR_TO_HIGHER_MEM(block_ptr);

#if MQX_CHECK_ERRORS
    if (partition_size < (_mem_size) block_ptr - (_mem_size) partition_location)
    {
        _task_set_error(MQX_INVALID_PARAMETER);
        _KLOGX2(KLOG_partition_extend, MQX_INVALID_PARAMETER);
        return (MQX_INVALID_PARAMETER);
    } /* Endif */
#endif /* MQX_CHECK_ERRORS */
    block_area_size = partition_size - ((_mem_size) block_ptr - (_mem_size) partition_location);
    number_of_blocks = block_area_size / partpool_ptr->BLOCK_SIZE;
#if MQX_CHECK_ERRORS
    if (number_of_blocks == 0)
    {
        _task_set_error(MQX_INVALID_PARAMETER);
        _KLOGX2(KLOG_partition_extend, MQX_INVALID_PARAMETER);
        return (MQX_INVALID_PARAMETER);
    } /* Endif */
#endif /* MQX_CHECK_ERRORS */

    partpool_ptr->TOTAL_BLOCKS += number_of_blocks;

    _partition_extend_internal(partpool_ptr, partpool_block_ptr, number_of_blocks);

    _KLOGX5(KLOG_partition_extend, partition, partpool_ptr, partition_size, MQX_OK);

    return MQX_OK;

} /* Endbody */

/*!
 * \private
 * 
 * \brief Adds more blocks of memory into the partition.
 * 
 * \param[in] partpool_ptr       The partition to add to.
 * \param[in] partpool_block_ptr The new block to add.
 * \param[in] number_of_blocks   The number of blocks to add.
 */ 
void _partition_extend_internal
(
    PARTPOOL_STRUCT_PTR       partpool_ptr,
    PARTPOOL_BLOCK_STRUCT_PTR partpool_block_ptr,
    _mqx_uint                 number_of_blocks
)
{ /* Body */
    INTERNAL_PARTITION_BLOCK_STRUCT_PTR block_ptr;

    partpool_block_ptr->VALID = PARTITION_VALID;
    partpool_block_ptr->NUMBER_OF_BLOCKS = number_of_blocks;

    block_ptr = (INTERNAL_PARTITION_BLOCK_STRUCT_PTR)((unsigned char *) partpool_block_ptr + sizeof(PARTPOOL_BLOCK_STRUCT));
    block_ptr = (INTERNAL_PARTITION_BLOCK_STRUCT_PTR)_ALIGN_ADDR_TO_HIGHER_MEM(block_ptr);

    partpool_block_ptr->FIRST_IBLOCK_PTR = block_ptr;

    while (number_of_blocks--)
    {
        _int_disable();
        block_ptr->LINK.NEXT_BLOCK_PTR = partpool_ptr->FREE_LIST_PTR;
        CALC_PARTITION_CHECKSUM(block_ptr);
        partpool_ptr->FREE_LIST_PTR = block_ptr;
        partpool_ptr->AVAILABLE++;
        _int_enable();
        block_ptr = (INTERNAL_PARTITION_BLOCK_STRUCT_PTR)((unsigned char *) block_ptr + partpool_ptr->BLOCK_SIZE);
    } /* Endwhile */

    _int_disable();
    partpool_block_ptr->NEXT_POOL_PTR = partpool_ptr->POOL.NEXT_POOL_PTR;
    partpool_ptr->POOL.NEXT_POOL_PTR = partpool_block_ptr;
    _int_enable();

} /* Endbody */

/*!
 * \brief Frees the selected partition block and returns it to the partition.
 * 
 * Partition blocks created by _partition_alloc() or _partition_alloc_zero() can 
 * be freed only by task, that allocated them. However when partition block was 
 * created by _partition_alloc_system() or _partition_alloc_system_zero(), then 
 * it can be freed by any task.
 * 
 * \param[in] mem_ptr Pointer to the partition block to free.
 * 
 * \return MQX_OK
 * \return PARTITION_BLOCK_INVALID_CHECKSUM (Checksum in the partition block 
 * header is not correct; the integrity of the partition is in question.)
 * \return MQX_NOT_RESOURCE_OWNER (Task is not the one that owns the partition block.)
 * \return PARTITION_INVALID (Mem_ptr is part of a partition that is not valid.)
 * 
 * \see _partition_alloc
 * \see _partition_alloc_zero
 * \see _partition_alloc_system
 * \see _partition_alloc_system_zero
 * \see _partition_create    
 */ 
_mqx_uint _partition_free
(
    void   *mem_ptr
)
{ /* Body */
    KERNEL_DATA_STRUCT_PTR              kernel_data;
    PARTPOOL_STRUCT_PTR                 partpool_ptr;
    INTERNAL_PARTITION_BLOCK_STRUCT_PTR block_ptr;

    _GET_KERNEL_DATA(kernel_data);

    _KLOGE2(KLOG_partition_free, mem_ptr);

    block_ptr = (INTERNAL_PARTITION_BLOCK_STRUCT_PTR)((unsigned char *) mem_ptr - sizeof(INTERNAL_PARTITION_BLOCK_STRUCT));

#if MQX_CHECK_VALIDITY
    if (!VALID_PARTITION_CHECKSUM(block_ptr))
    {
        _KLOGX2(KLOG_partition_free, PARTITION_BLOCK_INVALID_CHECKSUM);
        return (PARTITION_BLOCK_INVALID_CHECKSUM);
    } /* Endif */
#endif /* MQX_CHECK_VALIDITY */

#if MQX_CHECK_ERRORS
    if (block_ptr->TASK_ID != SYSTEM_TD_PTR(kernel_data)->TASK_ID)
    {
        /* Let system blocks be freed by anyone */
        if (block_ptr->TASK_ID != kernel_data->ACTIVE_PTR->TASK_ID)
        {
            _KLOGX2(KLOG_partition_free, MQX_NOT_RESOURCE_OWNER);
            return (MQX_NOT_RESOURCE_OWNER);
        } /* Endif */
    } /* Endif */
#endif /* MQX_CHECK_ERRORS */

    partpool_ptr = block_ptr->LINK.PARTITION_PTR;
    _INT_DISABLE();

#if MQX_CHECK_VALIDITY
    if (partpool_ptr->POOL.VALID != PARTITION_VALID)
    {
        _int_enable();
        _KLOGX2(KLOG_partition_free, PARTITION_INVALID);
        return (PARTITION_INVALID);
    } /* Endif */
#endif /* MQX_CHECK_VALIDITY */

    block_ptr->TASK_ID = 0;
    block_ptr->LINK.NEXT_BLOCK_PTR = partpool_ptr->FREE_LIST_PTR;
    partpool_ptr->FREE_LIST_PTR = block_ptr;

    ++partpool_ptr->AVAILABLE;
    CALC_PARTITION_CHECKSUM(block_ptr);

    _INT_ENABLE();
    _KLOGX2(KLOG_partition_free, MQX_OK);
    return (MQX_OK);

} /* Endbody */

/*!
 * \brief Allocates a system partition block from the partition.
 * 
 * The functions allocate a fixed-size block of memory that is not owned by any 
 * task.
 * 
 * \param[in] partition Partition from which to allocate the partition block.
 * 
 * \return Pointer to the partition block (success).
 * \return NULL (failure)
 *   
 * \warning On failure, calls _task_set_error() to set one of the task error codes 
 * described for _partition_alloc().
 *   
 * \see _partition_alloc_system_zero
 * \see _partition_alloc
 * \see _partition_alloc_zero
 * \see _partition_create
 * \see _task_set_error
 */ 
void *_partition_alloc_system
(
    _partition_id partition
)
{ /* Body */
    KERNEL_DATA_STRUCT_PTR kernel_data;
    void                  *result;

    _GET_KERNEL_DATA(kernel_data);

    _KLOGE2(KLOG_partition_alloc_system, partition);

    result = _partition_alloc_internal((PARTPOOL_STRUCT_PTR) partition, SYSTEM_TD_PTR(kernel_data));

    _KLOGX2(KLOG_partition_alloc_system, result);
    return (result);

} /* Endbody */

/*!
 * \brief Allocates a zero-filled system partition block from the partition.
 * 
 * The functions allocate a fixed-size block of memory that is not owned by any 
 * task.
 * 
 * \param[in] partition Partition from which to allocate the partition block.
 * 
 * \return Pointer to the partition block (success).
 * \return NULL (failure)
 *   
 * \warning On failure, calls _task_set_error() to set one of the task error codes 
 * described for _partition_alloc(). 
 * 
 * \see _partition_alloc_system
 * \see _partition_alloc
 * \see _partition_alloc_zero
 * \see _partition_create
 * \see _task_set_error
 */ 
void *_partition_alloc_system_zero
(
    _partition_id partition
)
{ /* Body */
    KERNEL_DATA_STRUCT_PTR kernel_data;
    PARTPOOL_STRUCT_PTR    partpool_ptr = (PARTPOOL_STRUCT_PTR) partition;
    void                  *result;

    _GET_KERNEL_DATA(kernel_data);

    _KLOGE2(KLOG_partition_alloc_system_zero, partition);

    result = _partition_alloc_internal(partpool_ptr, SYSTEM_TD_PTR(kernel_data));
#if MQX_CHECK_MEMORY_ALLOCATION_ERRORS
    if (result == NULL)
    {
        _KLOGX2(KLOG_partition_alloc_system_zero, result);
        return (result);
    } /* Endif */
#endif /* MQX_CHECK_MEMORY_ALLOCATION_ERRORS */

    _mem_zero(result, (_mem_size) (partpool_ptr->BLOCK_SIZE - sizeof(INTERNAL_PARTITION_BLOCK_STRUCT)));

    _KLOGX2(KLOG_partition_alloc_system_zero, result);
    return (result);

} /* Endbody */

/*!
 * \brief Returns all partition blocks owned by the task to the pool.
 * 
 * \param[in] td_ptr The task being destroyed.
 */ 
void _partition_cleanup
(
    TD_STRUCT_PTR td_ptr
)
{ /* Body */
    KERNEL_DATA_STRUCT_PTR              kernel_data;
    PARTPOOL_STRUCT_PTR                 partpool_ptr;
    PARTPOOL_BLOCK_STRUCT_PTR           partpool_block_ptr;
    PARTITION_COMPONENT_STRUCT_PTR      part_component_ptr;
    INTERNAL_PARTITION_BLOCK_STRUCT_PTR block_ptr;
    _mqx_uint                           i;

    _GET_KERNEL_DATA(kernel_data);

    part_component_ptr = (PARTITION_COMPONENT_STRUCT_PTR) kernel_data->KERNEL_COMPONENTS[KERNEL_PARTITIONS];

    if (part_component_ptr == NULL)
    {
        return; /* No work to do! */
    } /* Endif */

#if MQX_CHECK_VALIDITY
    if (part_component_ptr->VALID != PARTITION_VALID)
    {
        return;
    } /* Endif */
#endif /* MQX_CHECK_VALIDITY */

    partpool_ptr = (PARTPOOL_STRUCT_PTR)((void *) part_component_ptr->PARTITIONS.NEXT);
    while (partpool_ptr != (PARTPOOL_STRUCT_PTR)((void *) &part_component_ptr->PARTITIONS))
    {
        /* Check each partition */
        partpool_block_ptr = &partpool_ptr->POOL;
        while (partpool_block_ptr != NULL)
        {
            /* Check each poolblock in the partition */
#if MQX_CHECK_VALIDITY
            if (partpool_block_ptr->VALID != PARTITION_VALID)
            {
                break;
            }/* Endif */
#endif /* MQX_CHECK_VALIDITY */
            block_ptr = partpool_block_ptr->FIRST_IBLOCK_PTR;
            i = partpool_block_ptr->NUMBER_OF_BLOCKS + 1;
            while (--i)
            {
                if ((block_ptr->TASK_ID == td_ptr->TASK_ID) && (block_ptr->LINK.PARTITION_PTR == partpool_ptr))
                { /* An allocated Block */
                    _int_disable();
                    block_ptr->LINK.NEXT_BLOCK_PTR = partpool_ptr->FREE_LIST_PTR;
                    partpool_ptr->FREE_LIST_PTR = block_ptr;
                    ++partpool_ptr->AVAILABLE;
                    CALC_PARTITION_CHECKSUM(block_ptr);
                    _int_enable();
                } /* Endif */
                block_ptr = (INTERNAL_PARTITION_BLOCK_STRUCT_PTR)((unsigned char *) block_ptr + partpool_ptr->BLOCK_SIZE);
            } /* Endwhile */
            partpool_block_ptr = partpool_block_ptr->NEXT_POOL_PTR;
        } /* Endwhile */

        partpool_ptr = (PARTPOOL_STRUCT_PTR) partpool_ptr->NEXT;
    } /* Endwhile */

} /* Endbody */

/*!
 * \brief Tests all partition block checksums for validity.
 * 
 * \param[out] partpool_in_error       Pointer to the partition pool in error 
 * (initialized only if an error is found).
 * \param[out] partpool_block_in_error Pointer to the partition pool block in 
 * error (internal to MQX).
 * \param[out] block_in_error          Pointer to the partition block in error 
 * (initialized only if an error is found).
 * 
 * \return MQX_OK (No partitions have errors.)
 * \return MQX_INVALID_COMPONENT_BASE (Partition component data are not valid.)
 * \return PARTITION_INVALID (MQX found an invalid partition.)
 * \return PARTITION_BLOCK_INVALID_CHECKSUM (MQX found a partition block with an 
 * incorrect checksum.)
 * \return MQX_CORRUPT_QUEUE (An error is found.)
 * 
 * \warning Disables and enables interrupts.
 * 
 * \see _partition_alloc
 * \see _partition_alloc_zero
 * \see _partition_alloc_system
 * \see _partition_alloc_system_zero
 * \see _partition_create
 * \see _partition_free     
 */ 
_mqx_uint _partition_test
(
    _partition_id  *partpool_in_error,
    void          **partpool_block_in_error,
    void          **block_in_error
)
{ /* Body */
    KERNEL_DATA_STRUCT_PTR              kernel_data;
    PARTPOOL_STRUCT_PTR                 partpool_ptr;
    PARTPOOL_BLOCK_STRUCT_PTR           partpool_block_ptr;
    INTERNAL_PARTITION_BLOCK_STRUCT_PTR block_ptr;
    PARTITION_COMPONENT_STRUCT_PTR      part_component_ptr;
    _mqx_uint                           i;
    _mqx_uint                           result;

    _GET_KERNEL_DATA(kernel_data);
    _KLOGE1(KLOG_partition_test);

    part_component_ptr = (PARTITION_COMPONENT_STRUCT_PTR) kernel_data->KERNEL_COMPONENTS[KERNEL_PARTITIONS];
    if (part_component_ptr == NULL)
    {
        _KLOGX2(KLOG_partition_test, MQX_OK);
        return (MQX_OK);
    } /* Endif */

#if MQX_CHECK_VALIDITY
    if (part_component_ptr->VALID != PARTITION_VALID)
    {
        _KLOGX2(KLOG_partition_test, MQX_INVALID_COMPONENT_BASE);
        return (MQX_INVALID_COMPONENT_BASE);
    } /* Endif */
#endif /* MQX_CHECK_VALIDITY */

    _int_disable();

    /* Make sure that the queue of partitions is ok */
    result = _queue_test(&part_component_ptr->PARTITIONS, partpool_in_error);
    _int_enable();
    if (result != MQX_OK)
    {
        _KLOGX3(KLOG_partition_test, result, *partpool_in_error);
        return (result);
    } /* Endif */

    partpool_ptr = (PARTPOOL_STRUCT_PTR)((void *) part_component_ptr->PARTITIONS.NEXT);
    while (partpool_ptr != (PARTPOOL_STRUCT_PTR)((void *) &part_component_ptr->PARTITIONS))
    {
        /* Check all partitions */
        *partpool_in_error = partpool_ptr;

        partpool_block_ptr = &partpool_ptr->POOL;
        while (partpool_block_ptr != NULL)
        {
            /* Check all partition pool blocks contiguously */
            *partpool_block_in_error = partpool_block_ptr;

            block_ptr = partpool_block_ptr->FIRST_IBLOCK_PTR;
            i = partpool_block_ptr->NUMBER_OF_BLOCKS + 1;
            while (--i)
            {
#if MQX_CHECK_VALIDITY
                if (partpool_ptr->POOL.VALID != PARTITION_VALID)
                {
                    *block_in_error = NULL;
                    _KLOGX2(KLOG_partition_test, PARTITION_INVALID);
                    return (PARTITION_INVALID);
                } /* Endif */
#endif /* MQX_CHECK_VALIDITY */
                _int_disable();
                if (!VALID_PARTITION_CHECKSUM(block_ptr))
                {
                    _int_enable();
                    *block_in_error = (void *) ((unsigned char *) block_ptr + sizeof(INTERNAL_PARTITION_BLOCK_STRUCT));
                    _KLOGX2(KLOG_partition_test, PARTITION_BLOCK_INVALID_CHECKSUM);
                    return (PARTITION_BLOCK_INVALID_CHECKSUM);
                } /* Endif */
                _int_enable();
                block_ptr = (INTERNAL_PARTITION_BLOCK_STRUCT_PTR)((unsigned char *) block_ptr + partpool_ptr->BLOCK_SIZE);
            } /* Endwhile */

            partpool_block_ptr = partpool_block_ptr->NEXT_POOL_PTR;
        } /* Endwhile */

        /* Check partition free list */
        _int_disable();
        block_ptr = partpool_ptr->FREE_LIST_PTR;
        while (block_ptr != NULL)
        {
            partpool_ptr->TEST_FREE_PTR = block_ptr;
            _int_enable();
            _int_disable();
            block_ptr = (INTERNAL_PARTITION_BLOCK_STRUCT_PTR) partpool_ptr->TEST_FREE_PTR;
            if (!VALID_PARTITION_CHECKSUM(block_ptr))
            {
                _int_enable();
                *block_in_error = (void *) ((unsigned char *) block_ptr + sizeof(INTERNAL_PARTITION_BLOCK_STRUCT));
                _KLOGX2(KLOG_partition_test, PARTITION_BLOCK_INVALID_CHECKSUM);
                return (PARTITION_BLOCK_INVALID_CHECKSUM);
            } /* Endif */
            block_ptr = block_ptr->LINK.NEXT_BLOCK_PTR;
        } /* Endwhile */

        partpool_ptr = (PARTPOOL_STRUCT_PTR) partpool_ptr->NEXT;
        _int_enable();
    } /* Endwhile */

    _KLOGX2(KLOG_partition_test, MQX_OK);
    return MQX_OK;

} /* Endbody */

/*!
 * \brief Transfers the ownership of the partition block.
 * 
 * Any task can transfer the ownership of a private partition block or a system 
 * partition block.
 * \n If new_owner_id is the System Task ID, the partition block becomes a system 
 * partition block.
 * \n If the ownership of a system partition block is transferred to a task, the 
 * partition block becomes a resource of the task.
 * 
 * \param[in] mem_ptr      Pointer to the partition block to transfer.
 * \param[in] new_owner_id Task ID of the new owner.
 * 
 * \return MQX_OK 
 * \return PARTITION_BLOCK_INVALID_CHECKSUM (Checksum of the partition block 
 * header is not correct, which indicates that mem_ptr might not point to a valid 
 * partition block.)
 * \return PARTITION_INVALID_TASK_ID (Task_id is not valid.)
 * 
 * \see _partition_alloc
 * \see _partition_alloc_zero
 * \see _partition_alloc_system
 * \see _partition_alloc_system_zero   
 */ 
_mqx_uint _partition_transfer
(
    void    *mem_ptr,
    _task_id new_owner_id
)
{ /* Body */
    _KLOGM(KERNEL_DATA_STRUCT_PTR       kernel_data);
    TD_STRUCT_PTR                       new_td_ptr;
    INTERNAL_PARTITION_BLOCK_STRUCT_PTR block_ptr;

    _KLOGM(_GET_KERNEL_DATA(kernel_data));

    _KLOGE3(KLOG_partition_transfer, mem_ptr, new_owner_id);

    block_ptr = (INTERNAL_PARTITION_BLOCK_STRUCT_PTR)((unsigned char *) mem_ptr - sizeof(INTERNAL_PARTITION_BLOCK_STRUCT));

#if MQX_CHECK_VALIDITY
    if (!VALID_PARTITION_CHECKSUM(block_ptr))
    {
        _KLOGX2(KLOG_partition_transfer, PARTITION_BLOCK_INVALID_CHECKSUM);
        return (PARTITION_BLOCK_INVALID_CHECKSUM);
    } /* Endif */
#endif /* MQX_CHECK_VALIDITY */

    new_td_ptr = (TD_STRUCT_PTR) _task_get_td(new_owner_id);
#if MQX_CHECK_ERRORS
    if (new_td_ptr == NULL)
    {
        _KLOGX2(KLOG_partition_transfer, PARTITION_INVALID_TASK_ID);
        return (PARTITION_INVALID_TASK_ID);
    } /* Endif */
#endif /* MQX_CHECK_ERRORS */

    _INT_DISABLE();
    block_ptr->TASK_ID = new_owner_id;
    CALC_PARTITION_CHECKSUM(block_ptr);
    _INT_ENABLE();

    _KLOGX2(KLOG_partition_transfer, MQX_OK);
    return (MQX_OK);

} /* Endbody */

/*!
 * \brief Gets the number of allocated partition blocks in the partition.
 * 
 * \param[in] partition The partition to obtain information about.
 * 
 * \return Number of allocated partition blocks (success).
 * \return 0 (failure)
 * 
 * \warning On failure, calls _task_set_error() to set the following task error 
 * code:
 * \li PARTITION_INVALID (Partition does not represent a valid partition.)
 * 
 * \see _partition_get_block_size
 * \see _partition_get_free_blocks
 * \see _partition_get_total_blocks
 * \see _partition_get_total_size
 * \see _task_set_error  
 */ 
_mqx_uint _partition_get_max_used_blocks
(
    _partition_id partition
)
{ /* Body */
    PARTPOOL_STRUCT_PTR partpool_ptr = (PARTPOOL_STRUCT_PTR) partition;

#if MQX_CHECK_VALIDITY
    if (partpool_ptr->POOL.VALID != PARTITION_VALID)
    {
        _task_set_error(PARTITION_INVALID);
        return 0;
    } /* Endif */
#endif /* MQX_CHECK_VALIDITY */

    return (partpool_ptr->MAX_BLOCKS_USED);

} /* Endbody */

/*!
 * \brief Gets the total number of partition blocks in the partition.
 * 
 * The function returns the sum of the number of free partition blocks and the 
 * number of allocated partition blocks in the partition.
 * 
 * \param[in] partition The partition to obtain information about.
 * 
 * \return Total number of partition blocks in the partition (success).
 * \return 0 (failure)
 * 
 * \warning On failure, calls _task_set_error() to set the following task error 
 * code:
 * \li PARTITION_INVALID (Partition does not represent a valid partition.)
 * 
 * \see _partition_get_block_size
 * \see _partition_get_free_blocks
 * \see _partition_get_max_used_blocks
 * \see _partition_get_total_size
 * \see _task_set_error  
 */ 
_mqx_uint _partition_get_total_blocks
(
    _partition_id partition
)
{ /* Body */
    PARTPOOL_STRUCT_PTR partpool_ptr = (PARTPOOL_STRUCT_PTR) partition;

#if MQX_CHECK_VALIDITY
    if (partpool_ptr->POOL.VALID != PARTITION_VALID)
    {
        _task_set_error(PARTITION_INVALID);
        return 0;
    } /* Endif */
#endif /* MQX_CHECK_VALIDITY */

    return (partpool_ptr->TOTAL_BLOCKS);

} /* Endbody */

/*!
 * \brief Gets the size of the partition blocks in the partition.
 * 
 * If the processor supports memory alignment, the function might return a value 
 * that is larger that what was specified when the partition was created.
 * 
 * \param[in] partition The partition to obtain information about.
 * 
 * \return Number of single-addressable units in a partition block (success).
 * \return 0 (failure) 
 * 
 * \warning On failure, calls _task_set_error() to set the following task error 
 * code:
 * \li PARTITION_INVALID (Partition does not represent a valid partition.)
 * 
 * \see _partition_get_free_blocks
 * \see _partition_get_max_used_blocks
 * \see _partition_get_total_blocks
 * \see _partition_get_total_size
 * \see _partition_create_at
 * \see _task_set_error 
 */ 
_mem_size _partition_get_block_size
(
    _partition_id partition
)
{ /* Body */
    PARTPOOL_STRUCT_PTR partpool_ptr = (PARTPOOL_STRUCT_PTR) partition;

#if MQX_CHECK_VALIDITY
    if (partpool_ptr->POOL.VALID != PARTITION_VALID)
    {
        _task_set_error(PARTITION_INVALID);
        return 0;
    } /* Endif */
#endif /* MQX_CHECK_VALIDITY */

    return (partpool_ptr->BLOCK_SIZE - (_mem_size) sizeof(INTERNAL_PARTITION_BLOCK_STRUCT));

} /* Endbody */

/*!
 * \brief Gets the size of the partition.
 * 
 * The size of the partition includes extensions and internal overhead.
 * 
 * \param[in] partition The partition to obtain information about.
 * 
 * \return Number of single-addressable units in the partition (success).
 * \return 0 (failure)
 *    
 * \warning On failure, calls _task_set_error() to set the following task error 
 * code:
 * \li PARTITION_INVALID (Partition does not represent a valid partition.)
 * 
 * \see _partition_get_block_size
 * \see _partition_get_free_blocks
 * \see _partition_get_max_used_blocks
 * \see _partition_get_total_blocks
 * \see _partition_extend
 * \see _task_set_error
 */ 
_mem_size _partition_get_total_size
(
    _partition_id partition
)
{ /* Body */
    PARTPOOL_STRUCT_PTR       partpool_ptr = (PARTPOOL_STRUCT_PTR) partition;
    PARTPOOL_BLOCK_STRUCT_PTR partpool_block_ptr;
    register _mem_size        size;
    unsigned char                 *tmp_ptr;

#if MQX_CHECK_VALIDITY
    if (partpool_ptr->POOL.VALID != PARTITION_VALID)
    {
        _task_set_error(PARTITION_INVALID);
        return 0;
    } /* Endif */
#endif /* MQX_CHECK_VALIDITY */
    size = 0;
    partpool_block_ptr = partpool_ptr->POOL.NEXT_POOL_PTR;
    while (partpool_block_ptr != NULL)
    {
        tmp_ptr = (unsigned char *) partpool_block_ptr + sizeof(PARTPOOL_BLOCK_STRUCT);
        tmp_ptr = (unsigned char *) _ALIGN_ADDR_TO_HIGHER_MEM(tmp_ptr);
        size += (_mem_size) (tmp_ptr - (unsigned char *) partpool_block_ptr);
        partpool_block_ptr = partpool_block_ptr->NEXT_POOL_PTR;
    } /* Endif */

    tmp_ptr = (unsigned char *) partition + sizeof(PARTPOOL_STRUCT);
    tmp_ptr = (unsigned char *) _ALIGN_ADDR_TO_HIGHER_MEM(tmp_ptr);

    size += (_mem_size) (tmp_ptr - (unsigned char *) partition);

    return (size + partpool_ptr->BLOCK_SIZE * partpool_ptr->TOTAL_BLOCKS);

} /* Endbody */

/*!
 * \brief Calculates the number of single-addressable units in a partition.
 * 
 * If an application wants to use as much as possible of some memory that is 
 * outside the default memory pool, it can use the function to determine the 
 * maximum number of blocks that can be created.
 * \n For a dynamic partition, the application might want to limit (based on the 
 * results of the function) the amount of memory in the default memory pool that 
 * it uses to create the partition.
 * 
 * \param[in] number_of_blocks Number of partition blocks in the partition.
 * \param[in] block_size       Number of single-addressable units in one partition 
 * block in the partition.
 * 
 * \return Number of single-addressable units in the partition.
 * 
 * \see _partition_calculate_blocks
 * \see _partition_create
 * \see _partition_create_at 
 */ 
_mem_size _partition_calculate_size
(
    _mqx_uint number_of_blocks,
    _mem_size block_size
)
{ /* Body */
    _mem_size actual_block_size;
    _mem_size size;

    actual_block_size = sizeof(INTERNAL_PARTITION_BLOCK_STRUCT) + block_size;
    _MEMORY_ALIGN_VAL_LARGER(actual_block_size);

#if PSP_MEMORY_ALIGNMENT
    size = sizeof(PARTPOOL_STRUCT) + PSP_MEMORY_ALIGNMENT + actual_block_size * number_of_blocks;
#else
    size = sizeof(PARTPOOL_STRUCT) + actual_block_size * number_of_blocks;
#endif /* PSP_MEMORY_ALIGNMENT */

    return (size);
} /* Endbody */

/*!
 * /brief Calculates the number of partition blocks in a static partition.
 * 
 * When a task creates a static partition (_partition_create_at()), it specifies 
 * the size of the partition and the size of partition blocks. The function 
 * _partition_calculate_blocks() calculates how many blocks MQX actually created, 
 * taking into account internal headers.
 * 
 * \param[in] partition_size Number of single-addressable units that the partition 
 * can occupy.
 * \param[in] block_size     Number of single-addressable units in one partition 
 * block of the partition.
 * 
 * \return Number of partition blocks in the partition.
 * 
 * \see _partition_calculate_size
 * \see _partition_create_at 
 */ 
_mqx_uint _partition_calculate_blocks
(
    _mem_size partition_size,
    _mem_size block_size
)
{ /* Body */
    _KLOGM(KERNEL_DATA_STRUCT_PTR kernel_data);
    _mem_size actual_size;
    _mem_size size;
    _mqx_uint blocks;

    _KLOGM(_GET_KERNEL_DATA(kernel_data));
    _KLOGE3(KLOG_partition_calculate_blocks, partition_size, block_size);

    actual_size = sizeof(INTERNAL_PARTITION_BLOCK_STRUCT) + block_size;
    _MEMORY_ALIGN_VAL_LARGER(actual_size);

    size = (_mem_size) sizeof(PARTPOOL_STRUCT);
    _MEMORY_ALIGN_VAL_LARGER(size);

#if MQX_CHECK_ERRORS
    if (partition_size < size)
    {
        _task_set_error(MQX_INVALID_PARAMETER);
        _KLOGX2(KLOG_partition_calculate_blocks, PARTITION_NULL_ID);
        return (0);
    }
#endif /* MQX_CHECK_ERRORS */

    blocks = (partition_size - size) / actual_size;

    _KLOGX2(KLOG_partition_calculate_blocks, blocks);

    return blocks;

} /* Endbody */
/*!
 * \brief Gets size of partition.
 * 
 * \return Size of partition (Success).
 * \return 0 (Failure)   
 */ 
_mqx_uint _partition_get_number_of(void)
{
    uint32_t                        count = 0;
    KERNEL_DATA_STRUCT_PTR         kernel_data;
    PARTITION_COMPONENT_STRUCT_PTR part_component_ptr;

    _GET_KERNEL_DATA(kernel_data);

    part_component_ptr = (PARTITION_COMPONENT_STRUCT_PTR) kernel_data->KERNEL_COMPONENTS[KERNEL_PARTITIONS];

    if (part_component_ptr)
    {
        count = part_component_ptr->PARTITIONS.SIZE;
    }
    else
    {
        count = 0;
    } /* Endif */

    return count;
}
/*!
 * \brief Allocates a zero-filled private partition block from the partition.
 * 
 * The functions allocate a fixed-size memory block, which the task owns.
 * 
 * \param[in] partition Partition from which to allocate the partition block.
 * 
 * \return Pointer to the partition block (success).
 * \return NULL (failure)
 * 
 * \warning On failure, calls _task_set_error() to set one of the following task 
 * error codes:
 * \li PARTITION_INVALID (Partition does not represent a valid partition.)
 * \li PARTITION_OUT_OF_BLOCKS (All the partition blocks in the partition are 
 * allocated (for static partitions only).)
 * \li PARTITION_BLOCK_INVALID_CHECKSUM (MQX found an incorrect checksum in the 
 * partition block header.)
 * \li Task error code set by _mem_alloc_system() MQX cannot allocate memory for 
 * the partition block (for dynamic partitions only).     
 * 
 * \see _partition_alloc
 * \see _partition_alloc_system
 * \see _partition_alloc_system_zero
 * \see _partition_create
 * \see _task_set_error
 * \see _mem_alloc_system  
 */ 
void *_partition_alloc_zero
(
    _partition_id partition
)
{ /* Body */
    KERNEL_DATA_STRUCT_PTR kernel_data;
    PARTPOOL_STRUCT_PTR    partpool_ptr = (PARTPOOL_STRUCT_PTR) partition;
    void                  *result;

    _GET_KERNEL_DATA(kernel_data);

    _KLOGE2(KLOG_partition_alloc_zero, partition);

    result = _partition_alloc_internal(partpool_ptr, kernel_data->ACTIVE_PTR);
#if MQX_CHECK_MEMORY_ALLOCATION_ERRORS
    if (result == NULL)
    {
        _KLOGX2(KLOG_partition_alloc_zero, result);
        return (result);
    } /* Endif */
#endif /* MQX_CHECK_MEMORY_ALLOCATION_ERRORS */

    _mem_zero(result, (_mem_size) (partpool_ptr->BLOCK_SIZE - sizeof(INTERNAL_PARTITION_BLOCK_STRUCT)));

    _KLOGX2(KLOG_partition_alloc_zero, result);
    return (result);

} /* Endbody */

#endif /* MQX_USE_PARTITIONS */

/* EOF */
