#include "eclib.h"
#include <sys/mman.h>

static int fd;

#ifdef FT

#define CONTROL_NOTE "/dev/mem"
const off_t OFF_EC_PORT = 0x20000000;
uint64_t map_addr = MAP_FAILED;

bool port_dev_init(void)
{
    fd = open(CONTROL_NOTE, O_RDWR | O_SYNC);
    if (fd < 0) {
        return false;
    }
    map_addr = (uint64_t)mmap(NULL, 0x1000, PROT_WRITE|O_SYNC, MAP_SHARED, fd, OFF_EC_PORT);
    if (map_addr == MAP_FAILED) {
        return false;
    }
    return true;
}

void port_dev_exit(void)
{
    if (map_addr != MAP_FAILED) {
        munmap((void*)map_addr, 0x1000);
    }
    if (fd > 0)
        close(fd);
}

uint8_t ioread8(uint16_t port)
{
    uint8_t val = 0;
    val = *(uint8_t *)(map_addr + port);
    return val;
}

void iowrite8(uint16_t port, uint8_t data)
{
    *(uint8_t *)(map_addr + port) = data;
}

#else

#define CONTROL_NOTE "/dev/port"

bool port_dev_init(void)
{
    fd = open(CONTROL_NOTE, O_SYNC);
    if (fd < 0) {
        return false;
    }  
    return true;
}

void port_dev_exit(void)
{
    close(fd);
}

uint8_t ioread8(uint16_t port)
{
    uint8_t buf[2] = { 0 };

    lseek(fd, port, SEEK_SET);
    read(fd, buf, 1);
    return buf[0];
}

void iowrite8(uint16_t port, uint8_t data)
{
    uint8_t buf[2] = { 0 };
    printf("OFF_EC_D_PORT = 0x%X \n", port);
    lseek(fd, port, SEEK_SET);
    buf[0] = data;
    write(fd, buf, 1);
}

#endif

bool wait_kbc_ibe(uint16_t cmd_state)
{
    uint8_t  kbd_cmd_state = 0;
    uint64_t index;

    for (index = 0; index < EC_TIME_OUT; index ++) {
        kbd_cmd_state = (uint8_t)ioread8(cmd_state);
        if (!(kbd_cmd_state & EC_S_IBF)) {
            return true;
        }
        else {
            //delay
        }
    }
    printf("wait ibe time out\n");
    return false;
}

bool wait_kbc_obf (uint16_t cmd_state)
{
    uint8_t  kbd_cmd_state = 0;
    uint64_t index;

    for (index = 0; index < EC_TIME_OUT; index ++) {
        kbd_cmd_state = (uint8_t)ioread8(cmd_state);
        if (kbd_cmd_state & EC_S_OBF) {
            return true;
        }
        else {
            //delay
        }
    }
    printf("wait ibe time out\n");
    return false;
}

void send_data (uint8_t port, uint8_t data)
{
    wait_kbc_ibe (port);                   // Wait Input Buffer Empty
    iowrite8 (port - 4, data);
    wait_kbc_ibe (port);                   // Wait Input Buffer Empty
}


uint8_t get_data (uint8_t port, bool kb_ms_ctrl)
{
    uint8_t data;

    wait_kbc_ibe (port);                   // Wait Input Buffer Empty
    wait_kbc_obf (port);                   // Wait Output Buffer Full
    data = ioread8 (port - 4);

    return data;
}

void send_cmd(uint8_t port, uint8_t cmd, bool kb_ms_ctrl)
{
    wait_kbc_ibe(port);
    iowrite8(port, cmd);
    wait_kbc_ibe(port);
}

uint8_t read_ec_ram(uint8_t index)
{
    send_cmd (EC_C_PORT, EC_C_READ_MEM, false);
    /* send ECRAM offset to DATA port */
    send_data (EC_C_PORT, index);
    /* get DATA from EC */
    return get_data (EC_C_PORT, false);
}

void write_ec_ram(uint8_t index, uint8_t value)
{
    /* Write Command */
    send_cmd (EC_C_PORT, EC_C_WRITE_MEM, false);
    /* send ECRAM offset to DATA port */
    send_data (EC_C_PORT, index);
    /* Write DATA to EC ram */
    send_data (EC_C_PORT, value);
}

uint8_t read_ec(uint16_t offset)
{
    uint8_t data;

    iowrite8 ((uint16_t)(EC_IO_BASE + 1), (uint8_t)((offset & 0xFF00) >> 8));
    iowrite8 ((uint16_t)(EC_IO_BASE + 2), (uint8_t)(offset & 0x00FF));
    data =  ioread8 ((uint16_t)(EC_IO_BASE + 3));

    return data;
}

void write_ec(uint16_t offset, uint8_t data)
{
    iowrite8 ((uint16_t)(EC_IO_BASE + 1), (uint8_t)((offset & 0xFF00) >> 8));
    iowrite8 ((uint16_t)(EC_IO_BASE + 2), (uint8_t)(offset & 0x00FF));
    iowrite8 ((uint16_t)(EC_IO_BASE + 3), data);
}