/* Double Buffering is not yet supported              */

#include "usb.h"
#include "usbcfg.h"
#include "usbreg.h"
#include "usbhw.h"
#include "usbuser.h"
#include "stm32f10x_nvic.h"


#define EP_BUF_ADDR     (sizeof(EP_BUF_DSCR)*USB_EP_NUM) /* Endpoint Buffer Start Address */

/* Pointer to Endpoint Buffer Descriptors */
EP_BUF_DSCR *pBUF_DSCR = (EP_BUF_DSCR *)USB_PMA_ADDR;

/* Endpoint Free Buffer Address */
WORD FreeBufAddr;

void EP_Reset (DWORD EPNum) {
  DWORD num, val;

  num = EPNum & 0x0F;
  val = EPxREG(num);
  if (EPNum & 0x80) {                       /* IN Endpoint */
    EPxREG(num) = val & (EP_MASK | EP_DTOG_TX);
  } else {                                  /* OUT Endpoint */
    EPxREG(num) = val & (EP_MASK | EP_DTOG_RX);
  }
}

void EP_Status (DWORD EPNum, DWORD stat) {
  DWORD num, val;

  num = EPNum & 0x0F;
  val = EPxREG(num);
  if (EPNum & 0x80) {                       /* IN Endpoint */
    EPxREG(num) = (val ^ (stat & EP_STAT_TX)) & (EP_MASK | EP_STAT_TX);
  } else {                                  /* OUT Endpoint */
    EPxREG(num) = (val ^ (stat & EP_STAT_RX)) & (EP_MASK | EP_STAT_RX);
  }
}

void USB_Init (void) {
  GPIO_InitTypeDef GPIO_InitStructure;
  RCC->APB1ENR |= (1 << 23);                /* enable clock for USB */

  /* Enable USB Interrupts */
  NVIC->IPR [5] |= 0x00000010;              /* set priority lower than SVC */
  NVIC->ISER[0] |= (1 << (USB_LP_CAN_RX0_IRQChannel & 0x1F));

  /* Control USB connecting via SW */
  GPIO_InitStructure.GPIO_Pin = BOARD_USB_ENABLE_PIN;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(BOARD_USB_ENABLE_PORT, &GPIO_InitStructure);
  BOARD_USB_ENABLE_PORT->BRR =  BOARD_USB_ENABLE_PIN;
}

void USB_Connect (BOOL con) {

  CNTR = CNTR_FRES;                         /* Force USB Reset */
  ISTR = 0;                                 /* Clear Interrupt Status */
  if (con) {
    CNTR = CNTR_RESETM;                     /* USB Reset Interrupt Mask */
  } else {
    CNTR = CNTR_FRES | CNTR_PDWN;           /* Switch Off USB Device */
  }
}

void USB_Reset (void) {
/* Double Buffering is not yet supported              */
  ISTR = 0;                                 /* Clear Interrupt Status */
  CNTR = CNTR_CTRM | CNTR_RESETM |
         (USB_SUSPEND_EVENT ? CNTR_SUSPM   : 0) |
         (USB_WAKEUP_EVENT  ? CNTR_WKUPM   : 0) |
         (USB_ERROR_EVENT   ? CNTR_ERRM    : 0) |
         (USB_ERROR_EVENT   ? CNTR_PMAOVRM : 0) |
         (USB_SOF_EVENT     ? CNTR_SOFM    : 0) |
         (USB_SOF_EVENT     ? CNTR_ESOFM   : 0);

  FreeBufAddr = EP_BUF_ADDR;

  BTABLE = 0x00;                            /* set BTABLE Address */

  /* Setup Control Endpoint 0 */
  
  pBUF_DSCR->ADDR_TX = FreeBufAddr;
  FreeBufAddr += USB_MAX_PACKET0;
  pBUF_DSCR->ADDR_RX = FreeBufAddr;
  FreeBufAddr += USB_MAX_PACKET0;
  if (USB_MAX_PACKET0 > 62) {
    pBUF_DSCR->COUNT_RX = ((USB_MAX_PACKET0 << 5) - 1) | 0x8000;
  } else {
    pBUF_DSCR->COUNT_RX =   USB_MAX_PACKET0 << 9;
  }
  EPxREG(0) = EP_CONTROL | EP_RX_VALID;

  DADDR = DADDR_EF | 0;                     /* Enable USB Default Address */
}

void USB_Suspend (void) {
  CNTR |= CNTR_FSUSP;                       /* Force Suspend */
  CNTR |= CNTR_LPMODE;                      /* Low Power Mode */
}

void USB_Resume (void) { }

void USB_WakeUp (void) {
  CNTR &= ~CNTR_FSUSP;                      /* Clear Suspend */
}

void USB_WakeUpCfg (BOOL cfg) { }

void USB_SetAddress (DWORD adr) {
  DADDR = DADDR_EF | adr;
}

void USB_Configure (BOOL cfg) {
  cfg = cfg;
}

void USB_ConfigEP (USB_ENDPOINT_DESCRIPTOR *pEPD) {
/* Double Buffering is not yet supported              */
  DWORD num, val;

  num = pEPD->bEndpointAddress & 0x0F;

  val = pEPD->wMaxPacketSize;
  if (pEPD->bEndpointAddress & USB_ENDPOINT_DIRECTION_MASK) {
    (pBUF_DSCR + num)->ADDR_TX = FreeBufAddr;
    val = (val + 1) & ~1;
  } else {
    (pBUF_DSCR + num)->ADDR_RX = FreeBufAddr;
    if (val > 62) {
      val = (val + 31) & ~31;
      (pBUF_DSCR + num)->COUNT_RX = ((val << 5) - 1) | 0x8000;
    } else {
      val = (val + 1)  & ~1;
      (pBUF_DSCR + num)->COUNT_RX =   val << 9;
    }
  }
  FreeBufAddr += val;

  switch (pEPD->bmAttributes & USB_ENDPOINT_TYPE_MASK) {
    case USB_ENDPOINT_TYPE_CONTROL:
      val = EP_CONTROL;
      break;
    case USB_ENDPOINT_TYPE_ISOCHRONOUS:
      val = EP_ISOCHRONOUS;
      break;
    case USB_ENDPOINT_TYPE_BULK:
      val = EP_BULK;
      if (USB_DBL_BUF_EP & (1 << num)) {
        val |= EP_KIND;
      }
      break;
    case USB_ENDPOINT_TYPE_INTERRUPT:
      val = EP_INTERRUPT;
      break;
  }
  val |= num;
  EPxREG(num) = val;
}

void USB_DirCtrlEP (DWORD dir) { }

void USB_EnableEP (DWORD EPNum) {
  EP_Status(EPNum, EP_TX_VALID | EP_RX_VALID);
}

void USB_DisableEP (DWORD EPNum) {
  EP_Status(EPNum, EP_TX_DIS | EP_RX_DIS);
}

void USB_ResetEP (DWORD EPNum) {
  EP_Reset(EPNum);
}
void USB_SetStallEP (DWORD EPNum) {
  EP_Status(EPNum, EP_TX_STALL | EP_RX_STALL);
}

void USB_ClrStallEP (DWORD EPNum) {
  EP_Status(EPNum, EP_TX_VALID | EP_RX_VALID);
}


DWORD USB_ReadEP (DWORD EPNum, BYTE *pData) {
/* Double Buffering is not yet supported              */
  DWORD num, cnt, *pv, n;

  num = EPNum & 0x0F;

  pv  = (DWORD *)(USB_PMA_ADDR + 2*((pBUF_DSCR + num)->ADDR_RX));
  cnt = (pBUF_DSCR + num)->COUNT_RX & EP_COUNT_MASK;
  for (n = 0; n < (cnt + 1) / 2; n++) {
    *((__packed WORD *)pData) = *pv++;
    pData += 2;
  }
  EP_Status(EPNum, EP_RX_VALID);
  return (cnt);
}

DWORD USB_WriteEP (DWORD EPNum, BYTE *pData, DWORD cnt) {
/* Double Buffering is not yet supported              */
  DWORD num, *pv, n;

  num = EPNum & 0x0F;

  pv  = (DWORD *)(USB_PMA_ADDR + 2*((pBUF_DSCR + num)->ADDR_TX));
  for (n = 0; n < (cnt + 1) / 2; n++) {
    *pv++ = *((__packed WORD *)pData);
    pData += 2;
  }
  (pBUF_DSCR + num)->COUNT_TX = cnt;
  EP_Status(EPNum, EP_TX_VALID);

  return (cnt);
}

DWORD USB_GetFrame (void) {
  return (FNR & FNR_FN);
}
//IRQ #############################################################################
void USB_LP_CAN_RX0_IRQHandler (void) {
  DWORD istr, num, val;
  istr = ISTR;

  /* USB Reset Request */
  if (istr & ISTR_RESET) {
    USB_Reset();
#if USB_RESET_EVENT
    USB_Reset_Event();
#endif
    ISTR = ~ISTR_RESET;
  }

  /* USB Suspend Request */
  if (istr & ISTR_SUSP) {
    USB_Suspend();
#if USB_SUSPEND_EVENT
    USB_Suspend_Event();
#endif
    ISTR = ~ISTR_SUSP;
  }

  /* USB Wakeup */
  if (istr & ISTR_WKUP) {
    USB_WakeUp();
#if USB_WAKEUP_EVENT
    USB_WakeUp_Event();
#endif
    ISTR = ~ISTR_WKUP;
  }

  /* Start of Frame */
  if (istr & ISTR_SOF) {
#if USB_SOF_EVENT
    USB_SOF_Event();
#endif
    ISTR = ~ISTR_SOF;
  }

#if USB_ERROR_EVENT

  /* PMA Over/underrun */
  if (istr & ISTR_PMAOVR) {
    USB_Error_Event(1);
    ISTR = ~ISTR_PMAOVR;
  }

  /* Error: No Answer, CRC Error, Bit Stuff Error, Frame Format Error */
  if (istr & ISTR_ERR) {
    USB_Error_Event(0);
    ISTR = ~ISTR_ERR;
  }

#endif

  /* Endpoint Interrupts */
  while ((istr = ISTR) & ISTR_CTR) {
    ISTR = ~ISTR_CTR;

    num = istr & ISTR_EP_ID;

    val = EPxREG(num);
    if (val & EP_CTR_RX) {
      EPxREG(num) = val & ~EP_CTR_RX & EP_MASK;
      if (USB_P_EP[num]) {
        if (val & EP_SETUP) {
          USB_P_EP[num](USB_EVT_SETUP);
        } else {
          USB_P_EP[num](USB_EVT_OUT);
        }
      }
    }
    if (val & EP_CTR_TX) {
      EPxREG(num) = val & ~EP_CTR_TX & EP_MASK;
      if (USB_P_EP[num]) {
        USB_P_EP[num](USB_EVT_IN);
      }
    }
  }

}
