#include "tusb_option.h"
#if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_NUC120)
#include "device/dcd.h"
#include "NUC100Series.h"
#define PERIPH_SETUP_BUF_BASE 0
#define PERIPH_SETUP_BUF_LEN 8
#define PERIPH_EP0_BUF_BASE (PERIPH_SETUP_BUF_BASE + PERIPH_SETUP_BUF_LEN)
#define PERIPH_EP0_BUF_LEN CFG_TUD_ENDPOINT0_SIZE
#define PERIPH_EP1_BUF_BASE (PERIPH_EP0_BUF_BASE + PERIPH_EP0_BUF_LEN)
#define PERIPH_EP1_BUF_LEN CFG_TUD_ENDPOINT0_SIZE
#define PERIPH_EP2_BUF_BASE (PERIPH_EP1_BUF_BASE + PERIPH_EP1_BUF_LEN)
#define USBD_BUF_SIZE 512
enum ep_enum
{
PERIPH_EP0 = 0,
PERIPH_EP1 = 1,
PERIPH_EP2 = 2,
PERIPH_EP3 = 3,
PERIPH_EP4 = 4,
PERIPH_EP5 = 5,
PERIPH_MAX_EP,
};
static volatile uint8_t assigned_address;
static uint32_t bufseg_addr;
static bool active_ep0_xfer;
static struct xfer_ctl_t
{
uint8_t *data_ptr;
union {
uint16_t in_remaining_bytes;
uint16_t out_bytes_so_far;
};
uint16_t max_packet_size;
uint16_t total_bytes;
} xfer_table[PERIPH_MAX_EP];
static void usb_attach(void)
{
USBD->DRVSE0 &= ~USBD_DRVSE0_DRVSE0_Msk;
}
static void usb_detach(void)
{
USBD->DRVSE0 |= USBD_DRVSE0_DRVSE0_Msk;
}
static inline void usb_memcpy(uint8_t *dest, uint8_t *src, uint16_t size)
{
while(size--) *dest++ = *src++;
}
static void usb_control_send_zlp(void)
{
USBD->EP[PERIPH_EP0].CFG |= USBD_CFG_DSQ_SYNC_Msk;
USBD->EP[PERIPH_EP0].MXPLD = 0;
}
static uint8_t decode_ep_addr(USBD_EP_T *ep)
{
uint8_t ep_addr = ep->CFG & USBD_CFG_EP_NUM_Msk;
if ( USBD_CFG_EPMODE_IN == (ep->CFG & USBD_CFG_STATE_Msk) )
ep_addr |= TUSB_DIR_IN_MASK;
return ep_addr;
}
static USBD_EP_T *ep_entry(uint8_t ep_addr, bool add)
{
USBD_EP_T *ep;
enum ep_enum ep_index;
for (ep_index = PERIPH_EP0, ep = USBD->EP; ep_index < PERIPH_MAX_EP; ep_index++, ep++)
{
if (add)
{
if (0 == (ep->CFG & USBD_CFG_STATE_Msk)) return ep;
}
else
{
uint8_t candidate_ep_addr = decode_ep_addr(ep);
if (candidate_ep_addr == ep_addr) return ep;
}
}
return NULL;
}
static void dcd_in_xfer(struct xfer_ctl_t *xfer, USBD_EP_T *ep)
{
uint16_t bytes_now = tu_min16(xfer->in_remaining_bytes, xfer->max_packet_size);
#if 0#endif
{
usb_memcpy((uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), xfer->data_ptr, bytes_now);
}
ep->MXPLD = bytes_now;
}
static void bus_reset(void)
{
USBD->STBUFSEG = PERIPH_SETUP_BUF_BASE;
for (enum ep_enum ep_index = PERIPH_EP0; ep_index < PERIPH_MAX_EP; ep_index++)
{
USBD->EP[ep_index].CFG = 0;
USBD->EP[ep_index].CFGP = 0;
}
USBD->EP[PERIPH_EP0].CFG = USBD_CFG_CSTALL_Msk | USBD_CFG_EPMODE_IN;
USBD->EP[PERIPH_EP0].BUFSEG = PERIPH_EP0_BUF_BASE;
xfer_table[PERIPH_EP0].max_packet_size = PERIPH_EP0_BUF_LEN;
USBD->EP[PERIPH_EP1].CFG = USBD_CFG_CSTALL_Msk | USBD_CFG_EPMODE_OUT;
USBD->EP[PERIPH_EP1].BUFSEG = PERIPH_EP1_BUF_BASE;
xfer_table[PERIPH_EP1].max_packet_size = PERIPH_EP1_BUF_LEN;
bufseg_addr = PERIPH_EP2_BUF_BASE;
USBD->FADDR = 0;
active_ep0_xfer = false;
}
static const uint32_t enabled_irqs = USBD_INTSTS_FLDET_STS_Msk | USBD_INTSTS_BUS_STS_Msk | USBD_INTSTS_SETUP_Msk | USBD_INTSTS_USB_STS_Msk;
void dcd_init(uint8_t rhport)
{
(void) rhport;
USBD->ATTR = 0x7D0;
usb_detach();
bus_reset();
usb_attach();
USBD->INTSTS = enabled_irqs;
USBD->INTEN = enabled_irqs;
}
void dcd_int_enable(uint8_t rhport)
{
(void) rhport;
NVIC_EnableIRQ(USBD_IRQn);
}
void dcd_int_disable(uint8_t rhport)
{
(void) rhport;
NVIC_DisableIRQ(USBD_IRQn);
}
void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
{
(void) rhport;
usb_control_send_zlp();
assigned_address = dev_addr;
}
void dcd_remote_wakeup(uint8_t rhport)
{
(void) rhport;
USBD->ATTR = USBD_ATTR_RWAKEUP_Msk;
}
bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
{
(void) rhport;
USBD_EP_T *ep = ep_entry(p_endpoint_desc->bEndpointAddress, true);
TU_ASSERT(ep);
int const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress);
int const size = tu_edpt_packet_size(p_endpoint_desc);
tusb_xfer_type_t const type = (tusb_xfer_type_t) p_endpoint_desc->bmAttributes.xfer;
struct xfer_ctl_t *xfer = &xfer_table[ep - USBD->EP];
ep->BUFSEG = bufseg_addr;
bufseg_addr += size;
TU_ASSERT(bufseg_addr <= USBD_BUF_SIZE);
uint32_t cfg = tu_edpt_number(p_endpoint_desc->bEndpointAddress);
cfg |= (TUSB_DIR_IN == dir) ? USBD_CFG_EPMODE_IN : USBD_CFG_EPMODE_OUT;
if (TUSB_XFER_ISOCHRONOUS == type)
cfg |= USBD_CFG_TYPE_ISO;
ep->CFG = cfg;
xfer->max_packet_size = size;
return true;
}
void dcd_edpt_close_all (uint8_t rhport)
{
(void) rhport;
}
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes)
{
(void) rhport;
tusb_dir_t dir = tu_edpt_dir(ep_addr);
USBD_EP_T *ep = ep_entry(ep_addr, false);
struct xfer_ctl_t *xfer = &xfer_table[ep - USBD->EP];
xfer->data_ptr = buffer;
xfer->in_remaining_bytes = total_bytes;
xfer->total_bytes = total_bytes;
if ( (0x80 == ep_addr) && !active_ep0_xfer ) ep->CFG |= USBD_CFG_DSQ_SYNC_Msk;
if (TUSB_DIR_IN == dir)
{
dcd_in_xfer(xfer, ep);
}
else
{
xfer->out_bytes_so_far = 0;
ep->MXPLD = xfer->max_packet_size;
}
return true;
}
#if 0#endif
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
USBD_EP_T *ep = ep_entry(ep_addr, false);
ep->CFGP |= USBD_CFGP_SSTALL_Msk;
}
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
USBD_EP_T *ep = ep_entry(ep_addr, false);
ep->CFG |= USBD_CFG_CSTALL_Msk;
}
void dcd_int_handler(uint8_t rhport)
{
(void) rhport;
uint32_t status = USBD->INTSTS;
uint32_t state = USBD->ATTR & 0xf;
if(status & USBD_INTSTS_FLDET_STS_Msk)
{
if(USBD->FLDET & USBD_FLDET_FLDET_Msk)
{
USBD->ATTR |= USBD_ATTR_USB_EN_Msk | USBD_ATTR_PHY_EN_Msk;
}
else
{
USBD->ATTR &= ~USBD_ATTR_USB_EN_Msk;
}
}
if(status & USBD_INTSTS_BUS_STS_Msk)
{
if(state & USBD_STATE_USBRST)
{
USBD->ATTR |= USBD_ATTR_USB_EN_Msk | USBD_ATTR_PHY_EN_Msk;
bus_reset();
dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
}
if(state & USBD_STATE_SUSPEND)
{
USBD->ATTR &= ~USBD_ATTR_PHY_EN_Msk;
dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
}
if(state & USBD_STATE_RESUME)
{
USBD->ATTR |= USBD_ATTR_USB_EN_Msk | USBD_ATTR_PHY_EN_Msk;
dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
}
}
if(status & USBD_INTSTS_SETUP_Msk)
{
USBD->EP[PERIPH_EP0].CFGP |= USBD_CFGP_CLRRDY_Msk;
USBD->EP[PERIPH_EP1].CFGP |= USBD_CFGP_CLRRDY_Msk;
dcd_event_setup_received(0, (uint8_t *)USBD_BUF_BASE, true);
}
if(status & USBD_INTSTS_USB_STS_Msk)
{
if (status & (1UL << USBD_INTSTS_EPEVT_Pos))
{
if((USBD->FADDR != assigned_address) && (USBD->FADDR == 0)) USBD->FADDR = assigned_address;
uint16_t const available_bytes = USBD->EP[PERIPH_EP0].MXPLD;
active_ep0_xfer = (available_bytes == xfer_table[PERIPH_EP0].max_packet_size);
dcd_event_xfer_complete(0, 0x80, available_bytes, XFER_RESULT_SUCCESS, true);
}
enum ep_enum ep_index;
uint32_t mask;
struct xfer_ctl_t *xfer;
USBD_EP_T *ep;
for (ep_index = PERIPH_EP1, mask = (2UL << USBD_INTSTS_EPEVT_Pos), xfer = &xfer_table[PERIPH_EP1], ep = &USBD->EP[PERIPH_EP1]; ep_index < PERIPH_MAX_EP; ep_index++, mask <<= 1, xfer++, ep++)
{
if(status & mask)
{
USBD->INTSTS = mask;
uint16_t const available_bytes = ep->MXPLD;
uint8_t const ep_addr = decode_ep_addr(ep);
bool const out_ep = !(ep_addr & TUSB_DIR_IN_MASK);
if (out_ep)
{
#if 0#endif
{
usb_memcpy(xfer->data_ptr, (uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), available_bytes);
xfer->data_ptr += available_bytes;
}
xfer->out_bytes_so_far += available_bytes;
if ( (xfer->total_bytes == xfer->out_bytes_so_far) || (available_bytes < xfer->max_packet_size) )
dcd_event_xfer_complete(0, ep_addr, xfer->out_bytes_so_far, XFER_RESULT_SUCCESS, true);
else
ep->MXPLD = xfer->max_packet_size;
}
else
{
xfer->in_remaining_bytes -= available_bytes;
xfer->data_ptr += available_bytes;
if (xfer->in_remaining_bytes)
dcd_in_xfer(xfer, ep);
else
dcd_event_xfer_complete(0, ep_addr, xfer->total_bytes, XFER_RESULT_SUCCESS, true);
}
}
}
}
USBD->INTSTS = status & enabled_irqs;
}
void dcd_disconnect(uint8_t rhport)
{
(void) rhport;
usb_detach();
}
void dcd_connect(uint8_t rhport)
{
(void) rhport;
usb_attach();
}
#endif