/*
 * Copyright (c) 2011,2013,2015-2016,2019 The Linux Foundation. All rights reserved.
 *
 * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
 *
 *
 * Permission to use, copy, modify, and/or distribute this software for
 * any purpose with or without fee is hereby granted, provided that the
 * above copyright notice and this permission notice appear in all
 * copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * This file was originally distributed by Qualcomm Atheros, Inc.
 * under proprietary terms before Copyright ownership was assigned
 * to the Linux Foundation.
 */

/**
 * @ingroup adf_os_public
 * @file adf_os_mem.h
 * This file abstracts memory operations.
 */

#ifndef _ADF_OS_MEM_H
#define _ADF_OS_MEM_H

#include <adf_os_types.h>
#include <adf_os_mem_pvt.h>

#include "vos_cnss.h"

#ifdef CONFIG_WCNSS_MEM_PRE_ALLOC
#include <net/cnss_prealloc.h>
#endif

#include <i_vos_types.h>
#ifdef CONFIG_VOS_MEM_PRE_ALLOC
#include <vos_memory.h>
#endif

/**
 * struct adf_os_mem_dma_page_t - Allocated dmaable page
 * @page_v_addr_start: Page start virtual address
 * @page_v_addr_end: Page end virtual address
 * @page_p_addr: Page start physical address
 */
struct adf_os_mem_dma_page_t {
	char *page_v_addr_start;
	char *page_v_addr_end;
	adf_os_dma_addr_t page_p_addr;
};

/**
 * struct adf_os_mem_multi_page_t - multiple page allocation information storage
 * @num_element_per_page: Number of element in single page
 * @num_pages: Number of allocation needed pages
 * @dma_pages: page information storage in case of coherent memory
 * @cacheable_pages: page information storage in case of cacheable memory
 */
struct adf_os_mem_multi_page_t {
	uint16_t num_element_per_page;
	uint16_t num_pages;
	struct adf_os_mem_dma_page_t *dma_pages;
	void **cacheable_pages;
};

#ifdef MEMORY_DEBUG
#define adf_os_mem_alloc(_osdev, _size) adf_os_mem_alloc_debug(_osdev,\
			_size, __FILE__, __LINE__)

void *
adf_os_mem_alloc_debug(adf_os_device_t osdev, adf_os_size_t size,
			const char *fileName, a_uint32_t lineNum);


#define adf_os_mem_free(_buf)  adf_os_mem_free_debug(_buf)

void
adf_os_mem_free_debug(void *buf);

#else
/**
 * @brief Allocate a memory buffer. Note this call can block.
 *
 * @param[in] size    buffer size
 *
 * @return Buffer pointer or NULL if there's not enough memory.
 */
static inline void *
adf_os_mem_alloc(adf_os_device_t osdev, adf_os_size_t size)
{
#if defined(CONFIG_WCNSS_MEM_PRE_ALLOC) || defined(CONFIG_VOS_MEM_PRE_ALLOC)
    void *p_mem;

   if (size > WCNSS_PRE_ALLOC_GET_THRESHOLD)
   {
       p_mem = wcnss_prealloc_get(size);
       if (NULL != p_mem)
           return p_mem;
   }
#endif

    return __adf_os_mem_alloc(osdev, size);
}

/**
 * @brief Free malloc'ed buffer
 *
 * @param[in] buf     buffer pointer allocated by @ref adf_os_mem_alloc
 */
static inline void
adf_os_mem_free(void *buf)
{
#if defined(CONFIG_WCNSS_MEM_PRE_ALLOC) || defined(CONFIG_VOS_MEM_PRE_ALLOC)
    if (wcnss_prealloc_put(buf))
    {
        return;
    }
#endif

    __adf_os_mem_free(buf);
}

#endif

void *
adf_os_mem_alloc_outline(adf_os_device_t osdev, adf_os_size_t size);

void
adf_os_mem_free_outline(void *buf);

static inline  void *
adf_os_mem_alloc_consistent(
    adf_os_device_t osdev, adf_os_size_t size, adf_os_dma_addr_t *paddr, adf_os_dma_context_t mctx)
{
    return __adf_os_mem_alloc_consistent(osdev, size, paddr, mctx);
}

static inline void
adf_os_mem_free_consistent(
    adf_os_device_t osdev,
    adf_os_size_t size,
    void *vaddr,
    adf_os_dma_addr_t paddr,
    adf_os_dma_context_t memctx)
{
    __adf_os_mem_free_consistent(osdev, size, vaddr, paddr, memctx);
}

/**
 * @brief Move a memory buffer. Overlapping regions are not allowed.
 *
 * @param[in] dst     destination address
 * @param[in] src     source address
 * @param[in] size    buffer size
 */
static inline __ahdecl void
adf_os_mem_copy(void *dst, const void *src, adf_os_size_t size)
{
    __adf_os_mem_copy(dst, src, size);
}

/**
 * @brief Does a non-destructive copy of memory buffer
 *
 * @param[in] dst     destination address
 * @param[in] src     source address
 * @param[in] size    buffer size
 */
static inline void
adf_os_mem_move(void *dst, void *src, adf_os_size_t size)
{
	__adf_os_mem_move(dst,src,size);
}


/**
 * @brief Fill a memory buffer
 *
 * @param[in] buf   buffer to be filled
 * @param[in] b     byte to fill
 * @param[in] size  buffer size
 */
static inline void
adf_os_mem_set(void *buf, a_uint8_t b, adf_os_size_t size)
{
    __adf_os_mem_set(buf, b, size);
}


/**
 * @brief Zero a memory buffer
 *
 * @param[in] buf   buffer to be zeroed
 * @param[in] size  buffer size
 */
static inline __ahdecl void
adf_os_mem_zero(void *buf, adf_os_size_t size)
{
    __adf_os_mem_zero(buf, size);
}

void
adf_os_mem_zero_outline(void *buf, adf_os_size_t size);

/**
 * @brief Compare two memory buffers
 *
 * @param[in] buf1  first buffer
 * @param[in] buf2  second buffer
 * @param[in] size  buffer size
 *
 * @retval    0     equal
 * @retval    1     not equal
 */
static inline int
adf_os_mem_cmp(const void *buf1, const void *buf2, adf_os_size_t size)
{
    return __adf_os_mem_cmp(buf1, buf2, size);
}

/**
 * @brief Compare two strings
 *
 * @param[in] str1  First string
 * @param[in] str2  Second string
 *
 * @retval    0     equal
 * @retval    >0    not equal, if  str1  sorts lexicographically after str2
 * @retval    <0    not equal, if  str1  sorts lexicographically before str2
 */
static inline a_int32_t
adf_os_str_cmp(const char *str1, const char *str2)
{
    return __adf_os_str_cmp(str1, str2);
}

/**
 * @brief Returns the length of a string
 *
 * @param[in] str   input string
 *
 * @return    length of string
 */
static inline a_int32_t
adf_os_str_len(const char *str)
{
    return (a_int32_t)__adf_os_str_len(str);
}

void adf_os_mem_multi_pages_alloc(adf_os_device_t osdev,
		struct adf_os_mem_multi_page_t *pages,
		size_t element_size,
		uint16_t element_num,
		adf_os_dma_context_t memctxt,
		bool cacheable);

void adf_os_mem_multi_pages_free(adf_os_device_t osdev,
		struct adf_os_mem_multi_page_t *pages,
		adf_os_dma_context_t memctxt,
		bool cacheable);

#endif
