/****************************************************************************
 * @file     main.c
 * @version  V1.00
 * $Revision: 1 $
  * $Date: 16/02/ 10:37 $
 * @brief    Transmit and receive data from PC terminal through RS232 interface.
 *
 * @note
 * Copyright (C) 2016 Shanghai Panchip Microelectronics Co., Ltd.   All rights reserved.
 *
 ******************************************************************************/

#include "pan271x.h"

#include "musbfsfc.h"
#include "descript.h"
#include "endpoint.h"
#include "pan_usb.h"

extern uint8_t suspend_evt;
uint8_t stop_flag = FALSE, wake_flag = false;

void Clock_Init(void)
{
    PMU->SYS_CLK_CTRL |= SYS_CLK_CTRL_OTP_READ_MODE_SEL_Msk;
    PMU->SYS_CLK_CTRL &= ~(SYS_CLK_CTRL_OTP_CLK_HIGH_CYCLE_Msk | SYS_CLK_CTRL_OTP_CLK_LOW_CYCLE_Msk);
    PMU->SYS_CLK_CTRL |= ((0x1 << SYS_CLK_CTRL_OTP_CLK_HIGH_CYCLE_Pos) | (0x0 << SYS_CLK_CTRL_OTP_CLK_LOW_CYCLE_Pos));
    PMU->SYS_CLK_CTRL |= SYS_CLK_CTRL_OTP_SW_UP_CYCLE_EN_Msk;

    PMU->ANA_ANALDO |= ANA_ANALDO_EN_LDO_FSYN_AON_Msk;
    //MCU
    CLK_XthStartupConfig();
    PMU->XTH_CTRL |= XTH_CTRL_FSYNXO_BUFEN_AON_Msk;
    PMU->XTH_CTRL |= XTH_CTRL_FSYNXO_EN_AON_Msk;
    CLK_WaitClockReady(CLK_SYS_SRCSEL_XTH);

    CLK_HCLKConfig(0);

    CLK_SYSCLKConfig(CLK_DPLL_REF_CLKSEL_XTH,CLK_DPLL_OUT_48M);
    CLK_RefClkSrcConfig(CLK_SYS_SRCSEL_DPLL_1);

    CLK_AHBPeriphClockCmd(CLK_AHBPeriph_All,ENABLE);
    CLK_APB1PeriphClockCmd(CLK_APB1Periph_All,ENABLE);
}


void Sys_Init(void)
{
    /* Unlock protected registers */
    SYS_UnlockReg();

    /* Init I/O Multi-function  */
    SYS_SET_MFP(P0, 6, UART0_RX);
    SYS_SET_MFP(P0, 5, UART0_TX);
    GPIO_EnableDigitalPath(P0, BIT6);

    UART_Init(UART0, 115200);

    SYS_LockReg();
}

void platform_init()
{
    Clock_Init();
    Sys_Init();
}

void USB_IRQHandler(void)
{
     musbfsfc_intr();
}

extern uint8_t bulkin_ep_need_data;

uint8_t report[46] = {
    0x05, 0x01,  // Usage Page (Generic Desktop)
    0x09, 0x02,  // Usage (Mouse)
    0xA1, 0x01,  // Collection (Application)
//    0x85, 0x01,  // Report Id (1)
    0x09, 0x01,  //   Usage (Pointer)
    0xA1, 0x00,  //   Collection (Physical)
    0x05, 0x09,  //     Usage Page (Buttons)
    0x19, 0x01,  //     Usage Minimum (01) - Button 1
    0x29, 0x03,  //     Usage Maximum (03) - Button 3
    0x15, 0x00,  //     Logical Minimum (0)
    0x25, 0x01,  //     Logical Maximum (1)
    0x95, 0x08,  //     Report Count (8)
    0x75, 0x01,  //     Report Size (1)
    0x81, 0x02,  //     Input (Data, Variable, Absolute) - Button states
//    0x75, 0x05,  //     Report Size (5)
//    0x95, 0x01,  //     Report Count (1)
//    0x81, 0x01,  //     Input (Constant) - Padding or Reserved bits
    0x05, 0x01,  //     Usage Page (Generic Desktop)
    0x09, 0x30,  //     Usage (X)
    0x09, 0x31,  //     Usage (Y)
    0x09, 0x38,  //     Usage (Wheel)
    0x15, 0x81,  //     Logical Minimum (-127)
    0x25, 0x7F,  //     Logical Maximum (127)
    0x75, 0x08,  //     Report Size (8)
    0x95, 0x03,  //     Report Count (3)
    0x81, 0x06,  //     Input (Data, Variable, Relative) - X & Y coordinate
    0xC0,        //   End Collection
    0xC0,        // End Collection
};

uint16_t data_offset = 0,max_test_size = 0;
uint8_t mouse_report_pkt[4] = {0,};

void Test_MousePkt(void)
{
    mouse_report_pkt[0] = 0;
    if (data_offset < 100) {
        mouse_report_pkt[1] = 3; mouse_report_pkt[2] = 0; data_offset += 1;
    } else if (data_offset < 200) {
        mouse_report_pkt[1] = 0; mouse_report_pkt[2] = 0Xfd; data_offset += 1;
    } else if (data_offset < 300) {
        mouse_report_pkt[1] = 0XFd; mouse_report_pkt[2] = 0; data_offset += 1;
    } else if (data_offset < 400) {
        mouse_report_pkt[1] = 0; mouse_report_pkt[2] = 3; data_offset += 1;
        if (data_offset >= 400) data_offset = 0;
    }
    mouse_report_pkt[3] = 0;
}

void USB_TestExample(uint32_t IntrFlag)
{
    switch (IntrFlag) {
        //bulk out
        case USB_INT_BULKOUT_EP1:
            break;
        case USB_INT_BULKOUT_EP2:
            break;
        case USB_INT_BULKOUT_EP3:
            break;
        //bulk in
        case USB_INT_BULKIN_EP1:
            if ((USB->CSR0_INCSR1 & 0x2) == 0) {
                printf("data\n");
                stop_flag = FALSE;
                Test_MousePkt();
                WRITE_REG(USB->INDEX, 1);
                USB_Write((uint32_t)1, 4, (void*)mouse_report_pkt);
                WRITE_REG(USB->CSR0_INCSR1, M_INCSR_IPR);
            } else {
                stop_flag = TRUE;
                printf("no data\n");
            }
            if (wake_flag) {
                    wake_flag = false;
            }
            break;
        case USB_INT_BULKIN_EP2:
            break;
        case USB_INT_BULKIN_EP3:
            break;
        default:break;
    }
}

int main(void)
{
    uint8_t reg_tmp;

    platform_init();

    SYS_TEST("\nCPU @ %dHz\n", SystemCoreClock);

    // Configure USB
    SYS->SYS_CTRL |= (SYS_CTRL_USB_PU_Msk | SYS_CTRL_USB_PU2_Msk | SYS_CTRL_USB_EN_Msk);    // Enable USB and internal pullup path
    SYS->SYS_CTRL = ((SYS->SYS_CTRL & ~SYS_CTRL_VALID_REMOVAL_CYCLES_Msk) | 100);   // Configure USB debounce time

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

    NVIC_EnableIRQ(USB_IRQn);

    SYS_delay_10nop(1000000);

    while (1) {
        if (bulkin_ep_need_data == DATA_NEED) {
            printf("USB first start\n");
            Test_MousePkt();
            WRITE_REG(USB->CSR0_INCSR1, M_INCSR_CDT | M_INCSR_FF);
            WRITE_REG(USB->INDEX, 1);
            USB_Write((uint32_t)1, 4, (void*)mouse_report_pkt);
            WRITE_REG(USB->CSR0_INCSR1, M_INCSR_IPR);
            bulkin_ep_need_data = DATA_NONEED;
            SYS_delay_10nop(100000);
        }
    }
}
