/*
 *  Copyright (c) 1996-2007 Digi International Inc., All Rights Reserved
 *
 *  This software contains proprietary and confidential information of Digi
 *  International Inc.  By accepting transfer of this copy, Recipient agrees
 *  to retain this software in confidence, to prevent disclosure to others,
 *  and to make no use of this software other than that for which it was
 *  delivered.  This is an unpublished copyrighted work of Digi International
 *  Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly
 *  prohibited.
 *
 *  Restricted Rights Legend
 *
 *  Use, duplication, or disclosure by the Government is subject to
 *  restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
 *  Technical Data and Computer Software clause at DFARS 252.227-7031 or
 *  subparagraphs (c)(1) and (2) of the Commercial Computer Software -
 *  Restricted Rights at 48 CFR 52.227-19, as applicable.
 *
 *  Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
 *
 */

/* #define USE_NONCACHE */

#include <stdio.h>
#include <Npttypes.h>
#include <noncache.h>

#ifdef USE__NONCACHE

#include <mmu.h>

typedef struct
{
    BOOLEAN inuse;
    int     len;
    int     size;
    char    *buffer;
} NA_NONCACHED_TYPE;

#define NA_MAX_NONCACHED    4096

NA_NONCACHED_TYPE   nonCachedTab[NA_MAX_NONCACHED];

BOOLEAN nonCachedInitialize = FALSE;


static int  nonCachedEntryCount;
static char *nonCachedHeadPtr;
static char *nonCachedTailPtr;

#endif

/* Initialize nonCache area and reserved memory block */
int nonCacheInit (void)
{

#ifdef USE_NONCACHE
    int          i;

    #ifndef NETOS_GNU_TOOLS
        char *non_cache_start=(char *)__ghsbegin_non_cache;          
        char *non_cache_end=(char *)__ghsend_non_cache;       
    #endif 
    WORD32 pageAddress = (WORD32) non_cache_start;
    WORD32 pastLastPageAddress = (WORD32) non_cache_end;
    int ccode = CACHE_SUCCESS;
    
    while ((pageAddress < pastLastPageAddress) && (ccode == CACHE_SUCCESS))
    {
        ccode = NASetCacheAccessModes(pageAddress, MMU_BUFFERED, MMU_CLIENT_RW, &pageAddress);
    }
    

    for (i = 0; i < NA_MAX_NONCACHED; i++)
    {
        nonCachedTab[i].inuse = FALSE;
    }

    nonCachedInitialize = TRUE;
    nonCachedEntryCount = 0;
    nonCachedHeadPtr    = non_cache_start;
    nonCachedTailPtr    = non_cache_end;
    nonCachedInitialize = TRUE;
#endif

    return (0);
}

/*ad*
 * Allocate a buffer from the non-cached memory region
 *
 * This function allocates a buffer from the non-cached memory
 * region, as opposed to malloc(), which allocates from the
 * cached memory region.  There are two reasons why you may
 * want non-cached buffers.
 *
 * <list type="ordered">
 *	<item>Non-cached buffers can be used for DMA transfers
 *        without being invalidated or flushed.
 *  </item>
 *	<item>Using non-cached buffers for some operations may
 *        improve an application's overall performance by 
 *        allowing data in another buffer to stay in cache.
 *        </item>
 *  </list>
 *
 * @param len The number of bytes to allocate.
 *
 * @return Pointer to the allocated memory.
 *
 * @external
 * @category MMU
 * @since 6.1 
 *
 * @see @link nonCachedFree
*ad*/
void * nonCachedMalloc(int len)
{
#ifdef USE_NONCACHE
    int i;
    int blockSize = 4;

    if (nonCachedInitialize == FALSE)
    {
        nonCacheInit ();
    }

    /* Calculate block size (power of 2) */
    for (;;)
    {
        if (len <= blockSize)
        {
            break;
        }

        /* Increment size by long */
        blockSize *= 2;
    }

    for (i = 0; i < NA_MAX_NONCACHED; i++)
    {
        /* Skip memory inuse or free memory wrong size */
        if (nonCachedTab[i].inuse == TRUE)
        {
            continue;
        }

        /* Check if old buffer exist; if not then allocate a new buffer from block */
        if (nonCachedTab[i].buffer == NULL)
        {
            if (nonCachedHeadPtr + blockSize > nonCachedTailPtr)
            {
                /* Not enough memory */
                goto FAILURE;
            }

            nonCachedTab[i].size = blockSize;
            nonCachedTab[i].buffer = nonCachedHeadPtr;
            nonCachedHeadPtr += blockSize;
        }
        else
        {
            /* Check if size of old buffer is big enough */
            if (len > nonCachedTab[i].size)
            {
                continue;
            }
        }


        /* Set ownership to memory */
        nonCachedTab[i].inuse = TRUE;
        nonCachedTab[i].len = len;

        nonCachedEntryCount++;
        return (nonCachedTab[i].buffer);
    }

FAILURE:
    return (NULL);

#else
    void   *buffer;

    buffer = (void *)malloc (len);

    return (buffer);
#endif

}

/*ad*
 * Free a non-cached buffer
 *
 * This function frees a buffer previously allocated by
 * nonCachedMalloc().
 *
 * @external
 * @category MMU
 * @since 6.1 
 *
 * @param buffer Pointer to buffer to free.
 *
 * @return SUCCESS Buffer freed OK
 * @return FAIL Invalid buffer pointer
 * @see @link nonCachedMalloc
*ad*/
int nonCachedFree(void * buffer)
{
#ifdef USE_NONCACHE
    int i= 0;
    int count = 0;

    for (i = 0; i < NA_MAX_NONCACHED; i++)
    {
        if (count > nonCachedEntryCount)
        {
            goto FAILURE;
        }

        if (nonCachedTab[i].inuse == FALSE)
        {
            continue;
        }

        if ((char *)buffer < nonCachedTab[i].buffer)
        {
            goto FAILURE;
        }

        if (nonCachedTab[i].buffer == (char *)buffer)
        {
            nonCachedTab[i].inuse = FALSE;
            goto COMPLETE;
        }
        
        count++;
    }
        
    FAILURE:
        /* Buffer does not exist */
/*		printf("nonCachedFree: Invalid buffer\n"); */
        return -1;

    COMPLETE:
        /* Buffer Released */
        nonCachedEntryCount--;
#else
    if (buffer == NULL)
    {
        return (-1);
    }

    free (buffer);
#endif

    return (0);
}


