/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef GRALLOC_QSD8K_PMEMALLOC_H
#define GRALLOC_QSD8K_PMEMALLOC_H

#include <limits.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>


/**
 * An interface to the PMEM allocators.
 */
class PmemAllocator {

 public:

    virtual ~PmemAllocator();

    // Only valid after init_pmem_area() has completed successfully.
    virtual void* get_base_address() = 0;

    virtual int alloc_pmem_buffer(size_t size, int usage, void** pBase,
            int* pOffset, int* pFd) = 0;
    virtual int free_pmem_buffer(size_t size, void* base, int offset, int fd) = 0;
};


/**
 * A PMEM allocator that allocates the entire pmem memory from the kernel and
 * then uses a user-space allocator to suballocate from that.  This requires
 * that the PMEM device driver have kernel allocation disabled.
 */
class PmemUserspaceAllocator: public PmemAllocator {

 public:

    class Deps {
     public:

        class Allocator {
         public:
            virtual ~Allocator();
            virtual ssize_t setSize(size_t size) = 0;
            virtual size_t  size() const = 0;
            virtual ssize_t allocate(size_t size, uint32_t flags = 0) = 0;
            virtual ssize_t deallocate(size_t offset) = 0;
        };

        virtual ~Deps();

        // pmem
        virtual size_t getPmemTotalSize(int fd, size_t* size) = 0;
        virtual int connectPmem(int fd, int master_fd) = 0;
        virtual int mapPmem(int fd, int offset, size_t size) = 0;
        virtual int unmapPmem(int fd, int offset, size_t size) = 0;

        // C99
        virtual int getErrno() = 0;

        // POSIX
        virtual void* mmap(void* start, size_t length, int prot, int flags, int fd,
                off_t offset) = 0;
        virtual int open(const char* pathname, int flags, int mode) = 0;
        virtual int close(int fd) = 0;
    };

    PmemUserspaceAllocator(Deps& deps, Deps::Allocator& allocator, const char* pmemdev);
    virtual ~PmemUserspaceAllocator();

    // Only valid after init_pmem_area() has completed successfully.
    virtual void* get_base_address();

    virtual int init_pmem_area_locked();
    virtual int init_pmem_area();
    virtual int alloc_pmem_buffer(size_t size, int usage, void** pBase,
            int* pOffset, int* pFd);
    virtual int free_pmem_buffer(size_t size, void* base, int offset, int fd);

#ifndef ANDROID_OS
    // DO NOT USE: For testing purposes only.
    void set_master_values(int fd, void* base) {
        master_fd = fd;
        master_base = base;
    }
#endif // ANDROID_OS

 private:

    enum {
        MASTER_FD_INIT = -1,
    };

    Deps& deps;
    Deps::Allocator& allocator;

    pthread_mutex_t lock;
    const char* pmemdev;
    int master_fd;
    void* master_base;
};


/**
 * A PMEM allocator that allocates each individual allocation from the kernel
 * (using the kernel's allocator).  This requires the kernel driver for the
 * particular PMEM device being allocated from to support kernel allocation.
 */
class PmemKernelAllocator: public PmemAllocator {

 public:

    class Deps {
     public:

        virtual ~Deps();

        // C99
        virtual int getErrno() = 0;

        // POSIX
        virtual void* mmap(void* start, size_t length, int prot, int flags, int fd,
                off_t offset) = 0;
        virtual int munmap(void* start, size_t length) = 0;
        virtual int open(const char* pathname, int flags, int mode) = 0;
        virtual int close(int fd) = 0;
    };

    PmemKernelAllocator(Deps& deps, const char* pmemdev);
    virtual ~PmemKernelAllocator();

    // Only valid after init_pmem_area() has completed successfully.
    virtual void* get_base_address();

    virtual int alloc_pmem_buffer(size_t size, int usage, void** pBase,
            int* pOffset, int* pFd);
    virtual int free_pmem_buffer(size_t size, void* base, int offset, int fd);

 private:

    Deps& deps;

    const char* pmemdev;
};

#endif  // GRALLOC_QSD8K_PMEMALLOC_H