/*
 * i2c.c
 *
 * Copyright (C) 2012-2020, Samsung Electronics Co., Ltd.
 *
 * I2C interface, common implementation
 */

#include <macros.h>
#include <tee_internal_api.h>
#include <time.h>
#include <driver/i2c/i2c.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <core/driver.h>
#include <errno.h>
#include "tuiHal.h"
#include "bsp_common.h"
#include "board.h"
#include "dbg.h"
#include "device.h"
#include "i2c.h"

enum i2cState_t {
    STATE_START,
    STATE_READ,
    STATE_WRITE,
    STATE_STOP
};

static int fd_i2c;
static unsigned long buff_size;

static TEE_Result i2c_buff_config(unsigned long size)
{
    if (size > buff_size) {
        if (ioctl(fd_i2c, I2C_SET_TRANSAC_LEN, size) < 0) {
            return E_TUI_HAL_IO;
        }
        buff_size = size;
    }
    return TEE_SUCCESS;
}

TEE_Result i2c_init(deviceInfo_t *i2cDev)
{
    TEE_Result ret = TEE_SUCCESS;

    dbgPrintf(">> %s\n", __func__);
    if (i2cDev->state == DEV_CONFIGURED) {
        return E_TUI_HAL_BUSY;
    }

    fd_i2c = open(TOUCH_I2C_DRVNAME, O_RDONLY, 0);
    if (fd_i2c < 0) {
        ret = E_TUI_HAL_IO;
        errPrintf("Failed to open: %s errno %d\n", TOUCH_I2C_DRVNAME, errno);
        goto do_clean;
    }

    i2cDev->state |= DEV_CONFIGURED;
    buff_size = 0;
    dbgPrintf("<< %s\n", __func__);
    return TEE_SUCCESS;

do_clean:
    i2c_release(i2cDev);
    return ret;
}

TEE_Result i2c_release(deviceInfo_t *i2cDev)
{
    TEE_Result ret = TEE_SUCCESS;

    dbgPrintf(">> %s\n", __func__);
    if (i2cDev->state & DEV_CONFIGURED) {
        if (fd_i2c >= 0) {
            ALWAYS_ZERO(close(fd_i2c));
            fd_i2c = -1;
            buff_size = 0;
        }
        i2cDev->state &= ~DEV_CONFIGURED;
    }
    dbgPrintf("<< %s\n", __func__);
    return ret;
}

TEE_Result i2c_receive(deviceInfo_t *i2cDev,
                       uint32_t slaveAddr,
                       uint8_t *buf,
                       uint32_t num)
{
    TEE_Result ret;
    (void)i2cDev;
    char temp_char;
    struct i2c_msg msg = {};
    int res;

    msg.addr = slaveAddr;
    msg.rx_buf = (char *)buf;
    msg.rx_len = num;
    msg.tx_buf = &temp_char;
    msg.tx_len = 0;

    ret = i2c_buff_config(num);
    if (ret != TEE_SUCCESS) {
        return ret;
    }

    res = ioctl(fd_i2c, I2C_READ, (unsigned long)&msg);
    if (res < 0) {
        errPrintf("I2C_READ ret=%d, errno=%d\n", res, errno);
        ret = E_TUI_HAL_IO;
    }
    return ret;
}

TEE_Result i2c_send(deviceInfo_t *i2cDev,
                    uint32_t slaveAddr,
                    uint8_t *buf,
                    uint32_t num)
{
    TEE_Result ret;
    (void)i2cDev;
    char temp_char;
    struct i2c_msg msg = {};
    int res;

    msg.addr = slaveAddr;
    msg.tx_buf = (char *)buf;
    msg.tx_len = num;
    msg.rx_buf = &temp_char;
    msg.rx_len = 0;

    ret = i2c_buff_config(num);
    if (ret != TEE_SUCCESS) {
        return ret;
    }

    res = ioctl(fd_i2c, I2C_WRITE, (unsigned long)&msg);
    if (res < 0) {
        errPrintf("I2C_WRITE ret=%d, errno=%d\n", res, errno);
        ret = E_TUI_HAL_IO;
    }
    return ret;
}

TEE_Result i2c_read_write(deviceInfo_t *i2cDev,
                          uint32_t slaveAddr,
                          const uint8_t *wr_buff,
                          uint32_t wr_size,
                          uint8_t *rd_buff,
                          uint32_t rd_size)
{
    TEE_Result ret;
    (void)i2cDev;
    struct i2c_msg msg = {};
    int res;

    msg.addr = slaveAddr;
    msg.tx_buf = (char *)wr_buff;
    msg.tx_len = wr_size;
    msg.rx_buf = (char *)rd_buff;
    msg.rx_len = rd_size;

    ret = i2c_buff_config((wr_size > rd_size) ? wr_size : rd_size);
    if (ret != TEE_SUCCESS) {
        return ret;
    }

    res = ioctl(fd_i2c, I2C_TRANSFER, (unsigned long)&msg);
    if (res < 0) {
        errPrintf("I2C_TRANSFER ret=%d, errno=%d\n", res, errno);
        ret = E_TUI_HAL_IO;
    }
    return ret;
}
