#include "fingerprint.h"
#include "gw9558x_common.h"

#ifndef ENABLE_SENSORS_FPRINT_SECURE
void gw9558_spi_setup_conf(struct gf_device *gf_dev, u32 bits)
{
	gf_dev->spi->bits_per_word = 8 * bits;
	if (gf_dev->prev_bits_per_word != gf_dev->spi->bits_per_word) {
		if (spi_setup(gf_dev->spi))
			pr_err("failed to setup spi conf\n");
		pr_info("prev-bpw:%d, bpw:%d\n",
				gf_dev->prev_bits_per_word, gf_dev->spi->bits_per_word);
		gf_dev->prev_bits_per_word = gf_dev->spi->bits_per_word;
	}
}

int gw9558_spi_read_bytes(struct gf_device *gf_dev, u16 addr,
		u32 data_len, u8 *rx_buf)
{
	struct spi_message msg;
	struct spi_transfer *xfer = NULL;
	u8 *tmp_buf = NULL;

	xfer = kzalloc(sizeof(*xfer) * 2, GFP_KERNEL);
	if (xfer == NULL)
		return -ENOMEM;

	tmp_buf = gf_dev->spi_buffer;

	spi_message_init(&msg);
	*tmp_buf = 0xF0;
	*(tmp_buf + 1) = (u8)((addr >> 8) & 0xFF);
	*(tmp_buf + 2) = (u8)(addr & 0xFF);
	xfer[0].tx_buf = tmp_buf;
	xfer[0].len = 3;
	set_delay_in_spi_transfer(&xfer[0], SPI_TRANSFER_DELAY);
	spi_message_add_tail(&xfer[0], &msg);
	spi_sync(gf_dev->spi, &msg);

	spi_message_init(&msg);
	/* memset((tmp_buf + 4), 0x00, data_len + 1); */
	/* 4 bytes align */
	*(tmp_buf + 4) = 0xF1;
	xfer[1].tx_buf = tmp_buf + 4;
	xfer[1].rx_buf = tmp_buf + 4;
	xfer[1].len = data_len + 1;
	set_delay_in_spi_transfer(&xfer[1], SPI_TRANSFER_DELAY);
	spi_message_add_tail(&xfer[1], &msg);
	spi_sync(gf_dev->spi, &msg);

	memcpy(rx_buf, (tmp_buf + 5), data_len);

	kfree(xfer);
	if (xfer != NULL)
		xfer = NULL;

	return 0;
}

int gw9558_spi_write_bytes(struct gf_device *gf_dev, u16 addr,
		u32 data_len, u8 *tx_buf)
{
	struct spi_message msg;
	struct spi_transfer *xfer = NULL;
	u8 *tmp_buf = NULL;

	xfer = kzalloc(sizeof(*xfer), GFP_KERNEL);
	if (xfer == NULL)
		return -ENOMEM;

	tmp_buf = gf_dev->spi_buffer;

	spi_message_init(&msg);
	*tmp_buf = 0xF0;
	*(tmp_buf + 1) = (u8)((addr >> 8) & 0xFF);
	*(tmp_buf + 2) = (u8)(addr & 0xFF);
	memcpy(tmp_buf + 3, tx_buf, data_len);
	xfer[0].len = data_len + 3;
	xfer[0].tx_buf = tmp_buf;
	set_delay_in_spi_transfer(&xfer[0], SPI_TRANSFER_DELAY);
	spi_message_add_tail(&xfer[0], &msg);
	spi_sync(gf_dev->spi, &msg);

	kfree(xfer);
	if (xfer != NULL)
		xfer = NULL;

	return 0;
}

int gw9558_spi_read_byte(struct gf_device *gf_dev, u16 addr, u8 *value)
{
	struct spi_message msg;
	struct spi_transfer *xfer = NULL;

	xfer = kzalloc(sizeof(*xfer) * 2, GFP_KERNEL);
	if (xfer == NULL)
		return -ENOMEM;

	spi_message_init(&msg);
	*gf_dev->spi_buffer = 0xF0;
	*(gf_dev->spi_buffer + 1) = (u8)((addr >> 8) & 0xFF);
	*(gf_dev->spi_buffer + 2) = (u8)(addr & 0xFF);

	xfer[0].tx_buf = gf_dev->spi_buffer;
	xfer[0].len = 3;
	set_delay_in_spi_transfer(&xfer[0], SPI_TRANSFER_DELAY);
	spi_message_add_tail(&xfer[0], &msg);
	spi_sync(gf_dev->spi, &msg);

	spi_message_init(&msg);
	/* 4 bytes align */
	*(gf_dev->spi_buffer + 4) = 0xF1;
	xfer[1].tx_buf = gf_dev->spi_buffer + 4;
	xfer[1].rx_buf = gf_dev->spi_buffer + 4;
	xfer[1].len = 2;
	set_delay_in_spi_transfer(&xfer[1], SPI_TRANSFER_DELAY);
	spi_message_add_tail(&xfer[1], &msg);
	spi_sync(gf_dev->spi, &msg);

	*value = *(gf_dev->spi_buffer + 5);

	kfree(xfer);
	if (xfer != NULL)
		xfer = NULL;

	return 0;
}

int gw9558_spi_write_byte(struct gf_device *gf_dev, u16 addr, u8 value)
{
	struct spi_message msg;
	struct spi_transfer *xfer = NULL;

	xfer = kzalloc(sizeof(*xfer), GFP_KERNEL);
	if (xfer == NULL)
		return -ENOMEM;

	spi_message_init(&msg);
	*gf_dev->spi_buffer = 0xF0;
	*(gf_dev->spi_buffer + 1) = (u8)((addr >> 8) & 0xFF);
	*(gf_dev->spi_buffer + 2) = (u8)(addr & 0xFF);
	*(gf_dev->spi_buffer + 3) = value;

	xfer[0].tx_buf = gf_dev->spi_buffer;
	xfer[0].len = 3 + 1;
	set_delay_in_spi_transfer(&xfer[0], SPI_TRANSFER_DELAY);
	spi_message_add_tail(&xfer[0], &msg);
	spi_sync(gf_dev->spi, &msg);

	kfree(xfer);
	if (xfer != NULL)
		xfer = NULL;

	return 0;
}

static int gw9558_spi_transfer_raw(struct gf_device *gf_dev, u8 *tx_buf,
		u8 *rx_buf, u32 len)
{
	struct spi_message msg;
	struct spi_transfer xfer;

	spi_message_init(&msg);
	memset(&xfer, 0, sizeof(struct spi_transfer));

	xfer.tx_buf = tx_buf;
	xfer.rx_buf = rx_buf;
	xfer.len = len;
	spi_message_add_tail(&xfer, &msg);
	spi_sync(gf_dev->spi, &msg);

	return 0;
}

int gw9558_ioctl_transfer_raw_cmd(struct gf_device *gf_dev,
		unsigned long arg, unsigned int bufsiz)
{
	struct gf_ioc_transfer_raw ioc_xraw;
	int retval = 0;
	uint32_t len;

	do {
		if (copy_from_user(&ioc_xraw, (void __user *)arg,
				sizeof(struct gf_ioc_transfer_raw))) {
			pr_err("Failed to copy gf_ioc_transfer_raw from user to kernel\n");
			retval = -EFAULT;
			break;
		}

		if ((ioc_xraw.len > bufsiz) || (ioc_xraw.len == 0)) {
			pr_err("request transfer length larger than maximum buffer\n");
			retval = -EINVAL;
			break;
		}

		if (ioc_xraw.read_buf == NULL || ioc_xraw.write_buf == NULL) {
			pr_err("read buf and write buf can not equal to NULL simultaneously.\n");
			retval = -EINVAL;
			break;
		}

		/* change speed and set transfer mode */
		gw9558_spi_setup_conf(gf_dev, ioc_xraw.bits_per_word);

		len = ioc_xraw.len;

		if (copy_from_user(gf_dev->tx_buf, (void __user *)ioc_xraw.write_buf,
					ioc_xraw.len)) {
			pr_err("Failed to copy gf_ioc_transfer from user to kernel\n");
			retval = -EFAULT;
			break;
		}

		gw9558_spi_transfer_raw(gf_dev, gf_dev->tx_buf, gf_dev->rx_buf, len);

		if (copy_to_user((void __user *)ioc_xraw.read_buf,
					gf_dev->rx_buf, ioc_xraw.len)) {
			pr_err("Failed to copy gf_ioc_transfer_raw from kernel to user\n");
			retval = -EFAULT;
		}

	} while (0);

	return retval;
}

int gw9558_init_buffer(struct gf_device *gf_dev)
{
	int retval = 0;
	int len = TANSFER_MAX_LEN;

	gf_dev->spi_buffer = kzalloc(len, GFP_KERNEL);
	if (!gf_dev->spi_buffer) {
		pr_err("failed to allocate spi buffer\n");
		retval = -ENOMEM;
		goto alloc_failed;
	}

	gf_dev->tx_buf = kzalloc(len, GFP_KERNEL);
	if (!gf_dev->tx_buf) {
		pr_err("failed to allocate raw tx buffer\n");
		retval = -ENOMEM;
		goto alloc_failed;
	}

	gf_dev->rx_buf = kzalloc(len, GFP_KERNEL);
	if (!gf_dev->rx_buf) {
		kfree(gf_dev->tx_buf);
		pr_err("failed to allocate raw rx buffer\n");
		retval = -ENOMEM;
	}
alloc_failed:
	return retval;
}

int gw9558_free_buffer(struct gf_device *gf_dev)
{
	kfree(gf_dev->spi_buffer);
	kfree(gf_dev->tx_buf);
	kfree(gf_dev->rx_buf);
	return 0;
}
#endif