#include "arm_atsam_protocol.h"
#include <string.h>
Usb2422       USB2422_shadow;
unsigned char i2c0_buf[34];
const uint16_t MFRNAME[] = {'M', 'a', 's', 's', 'd', 'r', 'o', 'p', ' ', 'I', 'n', 'c', '.'};  const uint16_t PRDNAME[] = {'M', 'a', 's', 's', 'd', 'r', 'o', 'p', ' ', 'H', 'u', 'b'};       #ifndef MD_BOOTLOADER
const uint16_t SERNAME[] = {'U', 'n', 'a', 'v', 'a', 'i', 'l', 'a', 'b', 'l', 'e'};  #else
__attribute__((__aligned__(4))) const uint16_t SERNAME[BOOTLOADER_SERIAL_MAX_SIZE] = {'M', 'D', 'H', 'U', 'B', 'B', 'O', 'O', 'T', 'L', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'};
#endif  
uint8_t usb_host_port;
#ifndef MD_BOOTLOADER
uint8_t usb_extra_state;
uint8_t usb_extra_manual;
uint8_t usb_gcr_auto;
#endif  
uint16_t adc_extra;
void USB_write2422_block(void) {
    unsigned char *dest = i2c0_buf;
    unsigned char *src;
    unsigned char *base = (unsigned char *)&USB2422_shadow;
    DBGC(DC_USB_WRITE2422_BLOCK_BEGIN);
    for (src = base; src < base + 256; src += 32) {
        dest[0] = src - base;
        dest[1] = 32;
        memcpy(&dest[2], src, 32);
        i2c0_transmit(USB2422_ADDR, dest, 34, 50000);
        SERCOM0->I2CM.CTRLB.bit.CMD = 0x03;
        while (SERCOM0->I2CM.SYNCBUSY.bit.SYSOP) {
            DBGC(DC_USB_WRITE2422_BLOCK_SYNC_SYSOP);
        }
        wait_us(100);
    }
    DBGC(DC_USB_WRITE2422_BLOCK_COMPLETE);
}
void USB2422_init(void) {
    Gclk *   pgclk = GCLK;
    Mclk *   pmclk = MCLK;
    Port *   pport = PORT;
    Oscctrl *posc  = OSCCTRL;
    Usb *    pusb  = USB;
    DBGC(DC_USB2422_INIT_BEGIN);
    while ((v_5v = adc_get(ADC_5V)) < ADC_5V_START_LEVEL) {
        DBGC(DC_USB2422_INIT_WAIT_5V_LOW);
    }
        pgclk->PCHCTRL[10].bit.GEN  = 0;
    pgclk->PCHCTRL[10].bit.CHEN = 1;
    pmclk->AHBMASK.bit.USB_     = 1;
    pmclk->APBBMASK.bit.USB_    = 1;
        pport->Group[0].PMUX[12].reg          = 0x77;      pport->Group[0].PINCFG[24].bit.PMUXEN = 1;
    pport->Group[0].PINCFG[25].bit.PMUXEN = 1;
    pport->Group[1].PMUX[11].bit.PMUXE    = 7;      pport->Group[1].PINCFG[22].bit.PMUXEN = 1;
        posc->DFLLCTRLA.bit.ENABLE = 0;
    while (posc->DFLLSYNC.bit.ENABLE) {
        DBGC(DC_USB2422_INIT_OSC_SYNC_DISABLING);
    }
    while (posc->DFLLSYNC.bit.DFLLCTRLB) {
        DBGC(DC_USB2422_INIT_OSC_SYNC_DFLLCTRLB_1);
    }
    posc->DFLLCTRLB.bit.USBCRM = 1;
    while (posc->DFLLSYNC.bit.DFLLCTRLB) {
        DBGC(DC_USB2422_INIT_OSC_SYNC_DFLLCTRLB_2);
    }
    posc->DFLLCTRLB.bit.MODE = 1;
    while (posc->DFLLSYNC.bit.DFLLCTRLB) {
        DBGC(DC_USB2422_INIT_OSC_SYNC_DFLLCTRLB_3);
    }
    posc->DFLLCTRLB.bit.QLDIS = 0;
    while (posc->DFLLSYNC.bit.DFLLCTRLB) {
        DBGC(DC_USB2422_INIT_OSC_SYNC_DFLLCTRLB_4);
    }
    posc->DFLLCTRLB.bit.CCDIS = 1;
    posc->DFLLMUL.bit.MUL     = 0xBB80;      while (posc->DFLLSYNC.bit.DFLLMUL) {
        DBGC(DC_USB2422_INIT_OSC_SYNC_DFLLMUL);
    }
    posc->DFLLCTRLA.bit.ENABLE = 1;
    while (posc->DFLLSYNC.bit.ENABLE) {
        DBGC(DC_USB2422_INIT_OSC_SYNC_ENABLING);
    }
    pusb->DEVICE.CTRLA.bit.SWRST = 1;
    while (pusb->DEVICE.SYNCBUSY.bit.SWRST) {
        DBGC(DC_USB2422_INIT_USB_SYNC_SWRST);
    }
    while (pusb->DEVICE.CTRLA.bit.SWRST) {
        DBGC(DC_USB2422_INIT_USB_WAIT_SWRST);
    }
        pusb->DEVICE.PADCAL.bit.TRANSN = (USB_FUSES_TRANSN_ADDR >> USB_FUSES_TRANSN_Pos) & USB_FUSES_TRANSN_Msk;
    pusb->DEVICE.PADCAL.bit.TRANSP = (USB_FUSES_TRANSP_ADDR >> USB_FUSES_TRANSP_Pos) & USB_FUSES_TRANSP_Msk;
    pusb->DEVICE.PADCAL.bit.TRIM   = (USB_FUSES_TRIM_ADDR >> USB_FUSES_TRIM_Pos) & USB_FUSES_TRIM_Msk;
        pusb->DEVICE.CTRLB.bit.SPDCONF = 0;      pusb->DEVICE.CTRLA.bit.MODE    = 0;
    pusb->DEVICE.CTRLA.bit.ENABLE  = 1;
    while (pusb->DEVICE.SYNCBUSY.bit.ENABLE) {
        DBGC(DC_USB2422_INIT_USB_SYNC_ENABLING);
    }
    pusb->DEVICE.QOSCTRL.bit.DQOS = 2;
    pusb->DEVICE.QOSCTRL.bit.CQOS = 2;
    pport->Group[USB2422_HUB_ACTIVE_GROUP].PINCFG[USB2422_HUB_ACTIVE_PIN].bit.INEN = 1;
    i2c0_init();  
    sr_exp_data.bit.HUB_CONNECT = 1;      sr_exp_data.bit.HUB_RESET_N = 1;      SR_EXP_WriteData();
    wait_us(100);
#ifndef MD_BOOTLOADER
    usb_extra_manual = 0;
    usb_gcr_auto     = 1;
#endif  
    DBGC(DC_USB2422_INIT_COMPLETE);
}
void USB_reset(void) {
    DBGC(DC_USB_RESET_BEGIN);
        sr_exp_data.bit.HUB_RESET_N = 0;      SR_EXP_WriteData();
    wait_us(2);
    sr_exp_data.bit.HUB_RESET_N = 1;      SR_EXP_WriteData();
    DBGC(DC_USB_RESET_COMPLETE);
}
void USB_configure(void) {
    Usb2422 *pusb2422 = &USB2422_shadow;
    memset(pusb2422, 0, sizeof(Usb2422));
    uint16_t *serial_use    = (uint16_t *)SERNAME;                     uint8_t   serial_length = sizeof(SERNAME) / sizeof(uint16_t);  #ifndef MD_BOOTLOADER
    uint32_t serial_ptrloc = (uint32_t)&_srom - 4;
#else                                                      
    uint32_t serial_ptrloc = (uint32_t)&_erom - 4;
#endif                                                         uint32_t serial_address = *(uint32_t *)serial_ptrloc;  
    DBGC(DC_USB_CONFIGURE_BEGIN);
    if (serial_address != 0xFFFFFFFF && serial_address < serial_ptrloc)      {
        if ((serial_address & 0xFF) % 4 == 0)          {
            serial_use    = (uint16_t *)(serial_address);
            serial_length = 0;
            while ((*(serial_use + serial_length) > 32 && *(serial_use + serial_length) < 127) && serial_length < BOOTLOADER_SERIAL_MAX_SIZE) {
                serial_length++;
                DBGC(DC_USB_CONFIGURE_GET_SERIAL);
            }
        }
    }
        pusb2422->VID.reg               = 0x04D8;      pusb2422->PID.reg               = 0xEEC5;      pusb2422->DID.reg               = 0x0101;      pusb2422->CFG1.bit.SELF_BUS_PWR = 1;           pusb2422->CFG1.bit.HS_DISABLE   = 1;               pusb2422->CFG3.bit.STRING_EN = 1;          pusb2422->MAXPB.reg = 20;      pusb2422->HCMCB.reg = 20;      pusb2422->MFRSL.reg = sizeof(MFRNAME) / sizeof(uint16_t);
    pusb2422->PRDSL.reg = sizeof(PRDNAME) / sizeof(uint16_t);
    pusb2422->SERSL.reg = serial_length;
    memcpy(pusb2422->MFRSTR, MFRNAME, sizeof(MFRNAME));
    memcpy(pusb2422->PRDSTR, PRDNAME, sizeof(PRDNAME));
    memcpy(pusb2422->SERSTR, serial_use, serial_length * sizeof(uint16_t));
                pusb2422->STCD.bit.USB_ATTACH = 1;
    USB_write2422_block();
    adc_extra = 0;
    DBGC(DC_USB_CONFIGURE_COMPLETE);
}
uint16_t USB_active(void) { return (PORT->Group[USB2422_HUB_ACTIVE_GROUP].IN.reg & (1 << USB2422_HUB_ACTIVE_PIN)) != 0; }
void USB_set_host_by_voltage(void) {
            
    DBGC(DC_USB_SET_HOST_BY_VOLTAGE_BEGIN);
    usb_host_port = USB_HOST_PORT_UNKNOWN;
#ifndef MD_BOOTLOADER
    usb_extra_state = USB_EXTRA_STATE_UNKNOWN;
#endif                                 sr_exp_data.bit.SRC_1    = 1;      sr_exp_data.bit.SRC_2    = 1;      sr_exp_data.bit.E_UP_N   = 1;      sr_exp_data.bit.E_DN1_N  = 1;      sr_exp_data.bit.E_VBUS_1 = 0;      sr_exp_data.bit.E_VBUS_2 = 0;  
    SR_EXP_WriteData();
    wait_ms(250);
    while ((v_5v = adc_get(ADC_5V)) < ADC_5V_START_LEVEL) {
        DBGC(DC_USB_SET_HOST_5V_LOW_WAITING);
    }
    v_con_1 = adc_get(ADC_CON1);
    v_con_2 = adc_get(ADC_CON2);
    v_con_1_boot = v_con_1;
    v_con_2_boot = v_con_2;
    if (v_con_1 > v_con_2) {
        sr_exp_data.bit.S_UP  = 0;          sr_exp_data.bit.S_DN1 = 1;          sr_exp_data.bit.SRC_1 = 1;          sr_exp_data.bit.SRC_2 = 0;  
        sr_exp_data.bit.E_VBUS_1 = 1;          sr_exp_data.bit.E_VBUS_2 = 0;  
        SR_EXP_WriteData();
        sr_exp_data.bit.E_UP_N = 0;  
        SR_EXP_WriteData();
        usb_host_port = USB_HOST_PORT_1;
    } else {
        sr_exp_data.bit.S_UP  = 1;          sr_exp_data.bit.S_DN1 = 0;          sr_exp_data.bit.SRC_1 = 0;          sr_exp_data.bit.SRC_2 = 1;  
        sr_exp_data.bit.E_VBUS_1 = 0;          sr_exp_data.bit.E_VBUS_2 = 1;  
        SR_EXP_WriteData();
        sr_exp_data.bit.E_UP_N = 0;  
        SR_EXP_WriteData();
        usb_host_port = USB_HOST_PORT_2;
    }
#ifndef MD_BOOTLOADER
    usb_extra_state = USB_EXTRA_STATE_DISABLED;
#endif  
    USB_reset();
    USB_configure();
    DBGC(DC_USB_SET_HOST_BY_VOLTAGE_COMPLETE);
}
uint8_t USB2422_Port_Detect_Init(void) {
    uint32_t port_detect_retry_ms;
    uint32_t tmod;
    DBGC(DC_PORT_DETECT_INIT_BEGIN);
    USB_set_host_by_voltage();
    port_detect_retry_ms = timer_read64() + PORT_DETECT_RETRY_INTERVAL;
    while (!USB_active()) {
        tmod = timer_read64() % PORT_DETECT_RETRY_INTERVAL;
        if (v_con_1 > v_con_2)          {
                        if (tmod > 500 && tmod < 600) {
                DBG_LED_ON;
            } else {
                DBG_LED_OFF;
            }
        } else if (v_con_2 > v_con_1)          {
                        if (tmod > 500 && tmod < 600) {
                DBG_LED_ON;
            } else if (tmod > 700 && tmod < 800) {
                DBG_LED_ON;
            } else {
                DBG_LED_OFF;
            }
        }
        if (timer_read64() > port_detect_retry_ms) {
            DBGC(DC_PORT_DETECT_INIT_FAILED);
            return 0;
        }
    }
    DBGC(DC_PORT_DETECT_INIT_COMPLETE);
    return 1;
}
#ifndef MD_BOOTLOADER
void USB_ExtraSetState(uint8_t state) {
    uint8_t state_save = state;
    if (state == USB_EXTRA_STATE_DISABLED_UNTIL_REPLUG) state = USB_EXTRA_STATE_DISABLED;
    if (usb_host_port == USB_HOST_PORT_1)
        sr_exp_data.bit.E_VBUS_2 = state;
    else if (usb_host_port == USB_HOST_PORT_2)
        sr_exp_data.bit.E_VBUS_1 = state;
    else
        return;
    sr_exp_data.bit.E_DN1_N = !state;
    SR_EXP_WriteData();
    usb_extra_state = state_save;
    if (usb_extra_state == USB_EXTRA_STATE_ENABLED)
        CDC_print("USB: Extra enabled\r\n");
    else if (usb_extra_state == USB_EXTRA_STATE_DISABLED) {
        CDC_print("USB: Extra disabled\r\n");
#    ifdef USE_MASSDROP_CONFIGURATOR
        if (led_animation_breathing) gcr_breathe = gcr_desired;
#    endif
    } else if (usb_extra_state == USB_EXTRA_STATE_DISABLED_UNTIL_REPLUG)
        CDC_print("USB: Extra disabled until replug\r\n");
    else
        CDC_print("USB: Extra state unknown\r\n");
}
void USB_HandleExtraDevice(void) {
    uint16_t adcval;
    if (usb_host_port == USB_HOST_PORT_1)
        adcval = adc_get(ADC_CON2);
    else if (usb_host_port == USB_HOST_PORT_2)
        adcval = adc_get(ADC_CON1);
    else
        return;
    adc_extra = adc_extra * 0.9 + adcval * 0.1;
        if (usb_extra_state == USB_EXTRA_STATE_DISABLED_UNTIL_REPLUG) {
                if (adc_extra > USB_EXTRA_ADC_THRESHOLD) usb_extra_state = USB_EXTRA_STATE_DISABLED;
        return;      }
    if (usb_extra_manual) {
        if (usb_extra_state == USB_EXTRA_STATE_DISABLED) USB_ExtraSetState(USB_EXTRA_STATE_ENABLED);
        return;
    }
        if (usb_extra_state == USB_EXTRA_STATE_DISABLED && adc_extra < USB_EXTRA_ADC_THRESHOLD)
        USB_ExtraSetState(USB_EXTRA_STATE_ENABLED);
    else if (usb_extra_state == USB_EXTRA_STATE_ENABLED && adc_extra > USB_EXTRA_ADC_THRESHOLD)
        USB_ExtraSetState(USB_EXTRA_STATE_DISABLED);
}
#endif