/*
* Copyright (c) 2011, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*    * Redistributions of source code must retain the above copyright
*      notice, this list of conditions and the following disclaimer.
*    * Redistributions in binary form must reproduce the above
*      copyright notice, this list of conditions and the following
*      disclaimer in the documentation and/or other materials provided
*      with the distribution.
*    * Neither the name of The Linux Foundation nor the names of its
*      contributors may be used to endorse or promote products derived
*      from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/


#ifndef OVERLAY_MEM_H
#define OVERLAY_MEM_H

#include <sys/mman.h>
#include <fcntl.h>
#include <alloc_controller.h>
#include <memalloc.h>

#include "gralloc_priv.h"
#include "overlayUtils.h"
#include "mdpWrapper.h"

#define SIZE_1M 0x00100000
#define SIZE_2M 0x00200000

namespace overlay {

/*
* Holds base address, offset and the fd
* */
class OvMem {
public:
    /* ctor init*/
    explicit OvMem();

    /* dtor DO NOT call close so it can be copied */
    ~OvMem();

    /* Use libgralloc to retrieve fd, base addr, alloc type */
    bool open(uint32_t numbufs,
            uint32_t bufSz, bool isSecure);

    /* close fd. assign base address to invalid*/
    bool close();

    /* return underlying fd */
    int getFD() const;

    /* return true if fd is valid and base address is valid */
    bool valid() const;

    /* dump the state of the object */
    void dump() const;

    /* return underlying address */
    void* addr() const;

    /* return underlying offset */
    uint32_t bufSz() const;

    /* return number of bufs */
    uint32_t numBufs() const ;

    /* Set / unset secure with MDP */
    bool setSecure(bool enable);

private:
    /* actual os fd */
    int mFd;

    /* points to base addr (mmap)*/
    void* mBaseAddr;

    /* allocated buffer type determined by gralloc (ashmem, ion, etc) */
    int mAllocType;

    /* holds buf size sent down by the client */
    uint32_t mBufSz;

    /* num of bufs */
    uint32_t mNumBuffers;

    /* gralloc alloc controller */
    gralloc::IAllocController* mAlloc;

    /*Holds the aligned buffer size used for actual allocation*/
    uint32_t mBufSzAligned;

    /* Flags if the buffer has been secured by MDP */
    bool mSecured;
};

//-------------------Inlines-----------------------------------

using gralloc::IMemAlloc;
using gralloc::alloc_data;

inline OvMem::OvMem() {
    mFd = -1;
    mBaseAddr = MAP_FAILED;
    mAllocType = 0;
    mBufSz = 0;
    mNumBuffers = 0;
    mSecured = false;
    mAlloc = gralloc::IAllocController::getInstance();
}

inline OvMem::~OvMem() { }

inline bool OvMem::open(uint32_t numbufs,
        uint32_t bufSz, bool isSecure)
{
    alloc_data data;
    int allocFlags = GRALLOC_USAGE_PRIVATE_IOMMU_HEAP;
    int err = 0;
    OVASSERT(numbufs && bufSz, "numbufs=%d bufSz=%d", numbufs, bufSz);
    mBufSz = bufSz;

    if(isSecure) {
        allocFlags = GRALLOC_USAGE_PRIVATE_MM_HEAP;
        allocFlags |= GRALLOC_USAGE_PROTECTED;
        mBufSzAligned = utils::align(bufSz, SIZE_2M);
        data.align = SIZE_2M;
    } else {
        mBufSzAligned = bufSz;
        data.align = getpagesize();
    }

    // Allocate uncached rotator buffers
    allocFlags |= GRALLOC_USAGE_PRIVATE_UNCACHED;

    mNumBuffers = numbufs;

    data.base = 0;
    data.fd = -1;
    data.offset = 0;
    data.size = mBufSzAligned * mNumBuffers;
    data.uncached = true;

    err = mAlloc->allocate(data, allocFlags);
    if (err != 0) {
        ALOGE("OvMem: Error allocating memory");
        return false;
    }

    mFd = data.fd;
    mBaseAddr = data.base;
    mAllocType = data.allocType;

    if(isSecure) {
        setSecure(true);
    }

    return true;
}

inline bool OvMem::close()
{
    int ret = 0;

    if(!valid()) {
        return true;
    }

    if(mSecured) {
        setSecure(false);
    }

    IMemAlloc* memalloc = mAlloc->getAllocator(mAllocType);
    ret = memalloc->free_buffer(mBaseAddr, mBufSzAligned * mNumBuffers, 0, mFd);
    if (ret != 0) {
        ALOGE("OvMem: error freeing buffer");
        return false;
    }

    mFd = -1;
    mBaseAddr = MAP_FAILED;
    mAllocType = 0;
    mBufSz = 0;
    mBufSzAligned = 0;
    mNumBuffers = 0;
    return true;
}

inline bool OvMem::setSecure(bool enable) {
    OvFD fbFd;
    if(!utils::openDev(fbFd, 0, Res::fbPath, O_RDWR)) {
        ALOGE("OvMem::%s failed to init fb0", __FUNCTION__);
        return false;
    }
    struct msmfb_secure_config config;
    utils::memset0(config);
    config.fd = mFd;
    config.enable = enable;
    if(!mdp_wrapper::setSecureBuffer(fbFd.getFD(), config)) {
        ALOGE("OvMem::%s failed enable=%d", __FUNCTION__, enable);
        fbFd.close();
        mSecured = false;
        return false;
    }
    fbFd.close();
    mSecured = enable;
    return true;
}

inline bool OvMem::valid() const
{
    return (mFd != -1) && (mBaseAddr != MAP_FAILED);
}

inline int OvMem::getFD() const
{
    return mFd;
}

inline void* OvMem::addr() const
{
    return mBaseAddr;
}

inline uint32_t OvMem::bufSz() const
{
    return mBufSz;
}

inline uint32_t OvMem::numBufs() const
{
    return mNumBuffers;
}

inline void OvMem::dump() const
{
    ALOGE("== Dump OvMem start ==");
    ALOGE("fd=%d addr=%p type=%d bufsz=%u AlignedBufSz=%u",
           mFd, mBaseAddr, mAllocType, mBufSz, mBufSzAligned);
    ALOGE("== Dump OvMem end ==");
}

} // overlay

#endif // OVERLAY_MEM_H