/*
 * Copyright (c) 2021 Shanghai Panchip Microelectronics Co.,Ltd.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include "dongle_usb.h"
#include "musbfsfc.h"
#include "descript.h"
#include "endpoint.h"
#include "endpoint0.h"
#include "pan271x.h"
#include "usb.h"
#include "common.h"

static uint8_t ep_index_now;
enum usb_stat_t usb_stat;
#define TEST_USB 0
#if !TEST_USB
void usb_report(void)
{
	if (usb_stat == usb_suspending) {
		mini_printf("usb_suspending\n");
		SYS_delay_10nop(5000000);// 1.5s
		mini_printf("usb_suspended\n");
		usb_stat = usb_suspended;
		ring_buf_reset(&ringbuf_raw);
		return;
	} else if (usb_stat == usb_suspended) {
		if (!ring_buf_is_empty(&ringbuf_raw)) {
			usb_stat = usb_resuming;
			mini_printf("resuming\n");
			USB->POWER |= 0x4;
			SYS_delay_10nop(100000); // 30ms;
			USB->POWER &= ~0x4;
			mini_printf("resuming done\n");
		}
		return;
	}
	uint8_t index_before;

	index_before = USB->INDEX;

	WRITE_REG(USB->INDEX, 1);

	if (((USB->CSR0_INCSR1 & 0X2) == 0) && (!(ring_buf_is_empty(&ringbuf_raw)))) {
		uint8_t mouse_data[MOUSE_REPORT_SIZE] = { 0 };
		uint8_t mouse_report_data[USB_MOUSE_REPORT_SIZE] = { 0 };
		ring_buf_get(&ringbuf_raw, (uint8_t *)&mouse_data, MOUSE_REPORT_SIZE);

		/* report id */
		mouse_report_data[0] = MOUSE_REPORT_ID;
		/* key data */
		mouse_report_data[0] = mouse_data[3];
		/* x data */
		mouse_report_data[1] = mouse_data[4];
		mouse_report_data[2] = mouse_data[5];
		/* y data */
		mouse_report_data[3] = mouse_data[6];
		mouse_report_data[4] = mouse_data[7];
		/* roll data */
		mouse_report_data[5] = mouse_data[8];
		mouse_report_data[6] = 0;

		USB_Write((uint32_t)1, (uint32_t)7, (void *)mouse_report_data);
		WRITE_REG(USB->CSR0_INCSR1, M_INCSR_IPR);
	}

	WRITE_REG(USB->INDEX, COMPOSITE_ENDPOINT);

	if (((USB->CSR0_INCSR1 & 0X2) == 0) && (!(ring_buf_is_empty(&ringbuf_raw2)))) {
		uint8_t mouse_data[MOUSE_REPORT_SIZE] = { 0 };
		uint8_t mouse_report_data[USB_MOUSE_REPORT_SIZE] = { 0 };
		ring_buf_get(&ringbuf_raw2, (uint8_t *)&mouse_data, MOUSE_REPORT_SIZE);

		/* report id */
		mouse_report_data[0] = MOUSE_REPORT_ID;
		/* key data */
		mouse_report_data[1] = mouse_data[3];
		/* x data */
		mouse_report_data[2] = mouse_data[4];
		mouse_report_data[3] = mouse_data[5];
		/* y data */
		mouse_report_data[4] = mouse_data[6];
		mouse_report_data[5] = mouse_data[7];
		/* roll data */
		mouse_report_data[6] = mouse_data[8];
		mouse_report_data[7] = 0;

		USB_Write((uint32_t)COMPOSITE_ENDPOINT, (uint32_t)USB_MOUSE_REPORT_SIZE, (void *)mouse_report_data);
		WRITE_REG(USB->CSR0_INCSR1, M_INCSR_IPR);
	}

	WRITE_REG(USB->INDEX, index_before);
}
#else
void auto_circle(int16_t *x_value, int16_t *y_value)
{
	static volatile uint16_t usb_cycle_cnt;

	#define CIRCLE_SIZE 250

	if (usb_cycle_cnt < CIRCLE_SIZE) {
		*x_value = 0;
		*y_value = 2;
		usb_cycle_cnt++;
	} else if (usb_cycle_cnt < 2 * CIRCLE_SIZE) {
		*x_value = 2;
		*y_value = 0;
		usb_cycle_cnt++;
	} else if (usb_cycle_cnt < 3 * CIRCLE_SIZE) {
		*x_value = 0;
		*y_value = -2;
		usb_cycle_cnt++;
	} else if (usb_cycle_cnt < 4 * CIRCLE_SIZE) {
		*x_value = -2;
		*y_value = 0;
		usb_cycle_cnt++;
	} else {
		*x_value = 0;
		*y_value = 2;
		usb_cycle_cnt = 1;
	}
}

void usb_report(void)
{
	uint8_t index_before;

	index_before = USB->INDEX;
	WRITE_REG(USB->INDEX, 1);

	if (((USB->CSR0_INCSR1 & 0X2) == 0)) {
		uint8_t mouse_report_data[USB_MOUSE_REPORT_SIZE] = { 0 };
		int16_t x_value, y_value;
		auto_circle(&x_value, &y_value);
		/* key data */
		mouse_report_data[0] = 0;
		/* x data */
		mouse_report_data[1] = x_value & 0xff;
		mouse_report_data[2] = x_value >> 8;
		/* y data */
		mouse_report_data[3] = y_value & 0xff;
		mouse_report_data[4] = y_value >> 8;
		/* roll data */
		mouse_report_data[5] = 0;
		mouse_report_data[6] = 0;

		USB_Write((uint32_t)1, (uint32_t)7, (void *)mouse_report_data);
		WRITE_REG(USB->CSR0_INCSR1, M_INCSR_IPR);
	}

	WRITE_REG(USB->INDEX, index_before);
}
#endif

void usb_vendor_ep_in(uint8_t len, uint8_t *data)
{
	uint8_t send_data[64];

	memset(send_data, 0xff, sizeof(send_data));
	memcpy(send_data, data, len);
	WRITE_REG(USB->INDEX, ep_index_now);
	len = 64;
	USB_Write((uint32_t)ep_index_now, len, (void *)send_data);
	WRITE_REG(USB->CSR0_INCSR1, M_INCSR_IPR);
}

static void get_own_mac_addr(void)
{
	uint8_t data[8] = { 0x0a, 0x35 };

	memcpy(&data[2], prf_state.own_addr, 6);
	mini_printf("get_mac_addr: ");
	data_printf(&data[2], 6);

	usb_vendor_ep_in(8, data);
}

static void get_pair_mac_addr(void)
{
	uint8_t data[8] = { 0x0a, 0x36, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };

	usb_vendor_ep_in(8, data);
}

static bool report_id_check(uint8_t data)
{
	if (data == 0x0a) {
		mini_printf("true report id 0x%x\n", data);
		return true;
	} else {
		mini_printf("not vendor report id 0x%x\n", data);
		return false;
	}
}

void usb_vendor_ep_out(uint8_t ep_index)
{
	uint8_t usb_data[64];
	uint8_t usb_data_len;
	uint8_t usb_reply_data[10] = { 0x0 };

	ep_index_now = ep_index;

	WRITE_REG(USB->INDEX, ep_index_now);
	usb_data_len = READ_REG(USB->OUT_COUNT1);
	mini_printf("usb_data_len %d\n", usb_data_len);
	USB_Read(ep_index_now, usb_data_len, usb_data);

	WRITE_REG(USB->INDEX, ep_index_now);

	WRITE_REG(USB->OUT_CSR1, 0);

	if (!report_id_check(usb_data[0])) {
		return;
	}

	mini_printf("cmd %x\n", usb_data[1]);

	switch (usb_data[1]) {

	case USB_VENDOR_DFU_GET_VERSION:
		mini_printf("USB_VENDOR_DFU_GET_VERSION\n");

		memcpy(usb_reply_data, usb_data, 3);
		usb_vendor_ep_in(11, usb_reply_data);
		break;

	case USB_VENDOR_PRF_TEST:
		usb_emi_prf_test(usb_data);
		break;

	case USB_GET_OWN_ADDR:
	{
		mini_printf("get own mac\n");

		get_own_mac_addr();

		break;
	}
	case USB_GET_PAIR_ADDR:
	{
		mini_printf("get pair mac\n");

		get_pair_mac_addr();

		mini_printf("emi test connect\n");
		break;
	}

	default:
		mini_printf("other opcode %x\n", usb_data[1]);
		break;
	}
}

void usb_intr(void)
{
	volatile uint8_t IntrUSB;
	volatile int16_t IntrIn;
	volatile int16_t IntrOut;

	/* Read interrupt registers */
	/* Mote if less than 8 IN endpoints are configured then */
	/* only M_REG_INTRIN1 need be read. */
	/* Similarly if less than 8 OUT endpoints are configured then */
	/* only M_REG_INTROUT1 need be read. */
	IntrUSB = READ_REG(USB->INT_USB);
	IntrIn = (uint16_t)READ_REG(USB->INT_IN2);
	IntrIn <<= 8;
	IntrIn |= (uint16_t)READ_REG(USB->INT_IN1);
	IntrOut = (uint16_t)READ_REG(USB->INT_OUT2);
	IntrOut <<= 8;
	IntrOut |= (uint16_t)READ_REG(USB->INT_OUT1);

	/* ep1 finish in trans isr */
	if (IntrIn & (1 << STAND_KB_ENDPOINT)) {
		if (usb_stat != usb_active) {
			mini_printf("usb_stat %d\n", usb_stat);
			usb_stat = usb_active;
			USB->POWER |= 0x1;
		}
	}

	/* ep2 finish in trans isr */
	if (IntrIn & (1 << COMPOSITE_ENDPOINT)) {
		if (usb_stat != usb_active) {
			mini_printf("usb_stat %d\n", usb_stat);
			usb_stat = usb_active;
			USB->POWER |= 0x1;
		}
	}

	if (IntrOut & (1 << 2)) {
		usb_vendor_ep_out(2);
	}

	/* Check for system interrupts */
	if (IntrUSB & M_INTR_PLUG) {
		if ((IntrUSB & M_INTR_PLUG_OUT) == M_INTR_PLUG_OUT) {
			mini_printf("plug out\n");
			WRITE_REG(USB->INT_USBE, READ_REG(USB->INT_USBE) & (~M_INTR_SOF_ENABLE));
		} else if (IntrUSB & M_INTR_PLUG_OUT) {
			mini_printf("plug in\n");
		}
	}

	if (IntrUSB & M_INTR_SUSPEND) {
		mini_printf("USB isr in: Suspend evt\n");
		usb_stat = usb_suspending;
	}

	if (IntrUSB & M_INTR_RESUME) {
		mini_printf("USB isr in: Resume evt\n");
		usb_stat = usb_active;
	}

	if (IntrUSB & M_INTR_RESET) {
		mini_printf("USB isr in: Reset evt\n");
		usb_stat = usb_active;
		WRITE_REG(USB->INT_USBE, READ_REG(USB->INT_USBE) | M_INTR_SOF_ENABLE);

		/* Panchip USB Reset Proc */
		USB_Reset();
	}

	/* Check for endpoint 0 interrupt */
	if (IntrIn & M_INTR_EP0) {
		uint8_t index_before;

		index_before = USB->INDEX;
		WRITE_REG(USB->INDEX, 0);
		/* Panchip USB EP0 Proc */
		Endpoint0(M_EP_NORMAL);
		WRITE_REG(USB->INDEX, index_before);
	}

	return;
}

void USB_IRQHandler(void)
{
	usb_intr();
}

void usb_init(void)
{
	uint32_t reg_tmp;

	SYS->SYS_CTRL |= (SYS_CTRL_USB_PU_Msk | SYS_CTRL_USB_PU2_Msk | SYS_CTRL_USB_EN_Msk);
	/*usb debounce time*/
	SYS->SYS_CTRL = ((SYS->SYS_CTRL & ~SYS_CTRL_VALID_REMOVAL_CYCLES_Msk) | 100);

	reg_tmp = READ_REG(USB->INT_USBE);
	reg_tmp |= 0x10;
	WRITE_REG(USB->INT_USBE,reg_tmp); 

//	NVIC_EnableIRQ(USB_IRQn);

	mini_printf("usb_init\n");
	NVIC_SetPriority(USB_IRQn, 1);
	NVIC_EnableIRQ(USB_IRQn);
}
