You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
508 lines
14 KiB
508 lines
14 KiB
#include "gd32f10x.h"
|
|
#include "usbd_core.h"
|
|
#include "tusb.h"
|
|
#include "usbd.h"
|
|
|
|
static uint32_t g_interrupt_mask = 0U;
|
|
static const usbd_epkind_enum buf_kind = ENDP_SNG_BUF; /*!< single buffer endpoint type value */
|
|
|
|
/* device endpoints */
|
|
static usb_ep_struct in_ep[EP_COUNT]; /*!< the in direction endpoints */
|
|
static usb_ep_struct out_ep[EP_COUNT]; /*!< the out direction endpoints */
|
|
|
|
static void rcu_config(void)
|
|
{
|
|
/* enable USB pull-up pin clock */
|
|
rcu_periph_clock_enable(RCU_GPIO_PULLUP);
|
|
|
|
rcu_periph_clock_enable(RCU_PMU);
|
|
|
|
/* configure USB model clock from PLL clock */
|
|
rcu_usb_clock_config(RCU_CKUSB_CKPLL_DIV2);
|
|
|
|
/* enable USB APB1 clock */
|
|
rcu_periph_clock_enable(RCU_USBD);
|
|
}
|
|
|
|
static void gpio_config(void)
|
|
{
|
|
/* configure usb pull-up pin */
|
|
gpio_init(USB_PULLUP, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, USB_PULLUP_PIN);
|
|
}
|
|
|
|
void dcd_init(uint8_t rhport)
|
|
{
|
|
rcu_config();
|
|
gpio_config();
|
|
|
|
/* just reset the CLOSE bit */
|
|
USBD_REG_SET(USBD_CTL, CTL_SETRST);
|
|
|
|
/* may be need wait some time(tSTARTUP) ... */
|
|
|
|
/* clear SETRST bit in USBD_CTL register */
|
|
USBD_REG_SET(USBD_CTL, 0U);
|
|
|
|
/* clear all pending interrupts */
|
|
USBD_REG_SET(USBD_INTF, 0U);
|
|
|
|
/* set allocation buffer address */
|
|
USBD_REG_SET(USBD_BADDR, BUFFER_ADDRESS & 0xFFF8U);
|
|
|
|
g_interrupt_mask = IER_MASK;
|
|
|
|
#ifdef LPM_ENABLED
|
|
/* enable L1REQ interrupt */
|
|
USBD_REG_SET(USBD_LPMCS, LPMCS_LPMACK | LPMCS_LPMEN);
|
|
#endif /* LPM_ENABLED */
|
|
|
|
/* enable all interrupts mask bits */
|
|
USBD_REG_SET(USBD_CTL, g_interrupt_mask);
|
|
}
|
|
|
|
void dcd_int_enable(uint8_t rhport)
|
|
{
|
|
/* enable the USB low priority interrupt */
|
|
nvic_irq_enable(USBD_LP_CAN0_RX0_IRQn, 1, 0);
|
|
}
|
|
|
|
void dcd_int_disable(uint8_t rhport)
|
|
{
|
|
/* enable the USB low priority interrupt */
|
|
nvic_irq_disable(USBD_LP_CAN0_RX0_IRQn);
|
|
}
|
|
|
|
void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
|
|
{
|
|
USBD_REG_SET(USBD_DADDR, DADDR_USBEN | dev_addr);
|
|
}
|
|
|
|
void dcd_remote_wakeup(uint8_t rhport)
|
|
{
|
|
}
|
|
|
|
void dcd_connect(uint8_t rhport)
|
|
{
|
|
gpio_bit_set(USB_PULLUP, USB_PULLUP_PIN);
|
|
}
|
|
|
|
void dcd_disconnect(uint8_t rhport)
|
|
{
|
|
gpio_bit_reset(USB_PULLUP, USB_PULLUP_PIN);
|
|
}
|
|
|
|
bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_ep)
|
|
{
|
|
uint8_t ep_num = desc_ep->bEndpointAddress & 0x0FU;
|
|
uint32_t reg_value = 0;
|
|
|
|
/* set the endpoint type */
|
|
switch (desc_ep->bmAttributes.xfer) {
|
|
case TUSB_XFER_CONTROL:
|
|
reg_value = EP_CONTROL;
|
|
break;
|
|
case TUSB_XFER_BULK:
|
|
reg_value = EP_BULK;
|
|
break;
|
|
case TUSB_XFER_INTERRUPT:
|
|
reg_value = EP_INTERRUPT;
|
|
break;
|
|
case TUSB_XFER_ISOCHRONOUS:
|
|
reg_value = EP_ISO;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
USBD_REG_SET(USBD_EPxCS(ep_num), reg_value | ep_num);
|
|
|
|
reg_value = desc_ep->wMaxPacketSize.size;
|
|
|
|
if (desc_ep->bEndpointAddress >> 7U) {
|
|
usb_ep_struct *ep = &in_ep[ep_num];
|
|
|
|
ep->maxpacket = reg_value;
|
|
|
|
/* set the endpoint transmit buffer address */
|
|
(pbuf_reg + ep_num)->tx_addr = (uint16_t)g_free_buf_addr;
|
|
|
|
reg_value = (reg_value + 1U) & ~1U;
|
|
|
|
g_free_buf_addr += reg_value;
|
|
|
|
if (ENDP_DBL_BUF == buf_kind) {
|
|
USBD_ENDP_DOUBLE_BUF_SET(ep_num);
|
|
|
|
(pbuf_reg + ep_num)->rx_addr = (uint16_t)g_free_buf_addr;
|
|
|
|
g_free_buf_addr += reg_value;
|
|
|
|
USBD_ENDP_TX_STATUS_SET(ep_num, EPTX_VALID);
|
|
USBD_ENDP_RX_STATUS_SET(ep_num, EPRX_DISABLED);
|
|
} else {
|
|
/* configure the endpoint status as NAK status */
|
|
USBD_ENDP_TX_STATUS_SET(ep_num, EPTX_NAK);
|
|
}
|
|
} else {
|
|
usb_ep_struct *ep = &out_ep[ep_num];
|
|
|
|
ep->maxpacket = reg_value;
|
|
|
|
if (ENDP_DBL_BUF == buf_kind) {
|
|
USBD_ENDP_DOUBLE_BUF_SET(ep_num);
|
|
|
|
USBD_DTG_TX_TOGGLE(ep_num);
|
|
|
|
/* set the endpoint transmit buffer address */
|
|
(pbuf_reg + ep_num)->tx_addr = (uint16_t)g_free_buf_addr;
|
|
|
|
if (reg_value > 62U) {
|
|
reg_value = (reg_value + 31U) & ~31U;
|
|
(pbuf_reg + ep_num)->tx_count = (uint16_t)(((reg_value << 5U) - 1U) | 0x8000U);
|
|
} else {
|
|
reg_value = (reg_value + 1U) & ~1U;
|
|
(pbuf_reg + ep_num)->tx_count = (uint16_t)(reg_value << 9U);
|
|
}
|
|
|
|
g_free_buf_addr += reg_value;
|
|
}
|
|
|
|
reg_value = desc_ep->wMaxPacketSize.size;
|
|
|
|
/* set the endpoint receive buffer address */
|
|
(pbuf_reg + ep_num)->rx_addr = (uint16_t)g_free_buf_addr;
|
|
|
|
if (reg_value > 62U) {
|
|
reg_value = (reg_value + 31U) & ~31U;
|
|
(pbuf_reg + ep_num)->rx_count = (uint16_t)(((reg_value << 5U) - 1U) | 0x8000U);
|
|
} else {
|
|
reg_value = (reg_value + 1U) & ~1U;
|
|
(pbuf_reg + ep_num)->rx_count = (uint16_t)(reg_value << 9U);
|
|
}
|
|
|
|
if (ENDP_DBL_BUF == buf_kind) {
|
|
USBD_ENDP_RX_STATUS_SET(ep_num, EPRX_DISABLED);
|
|
USBD_ENDP_TX_STATUS_SET(ep_num, EPTX_NAK);
|
|
} else {
|
|
/* configure the endpoint status as NAK status */
|
|
USBD_ENDP_RX_STATUS_SET(ep_num, EPRX_NAK);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
static void __ep_rx (uint8_t ep_addr, uint8_t *pbuf, uint16_t buf_len)
|
|
{
|
|
usb_ep_struct *ep;
|
|
uint8_t ep_num = tu_edpt_number(ep_addr);
|
|
|
|
ep = &out_ep[ep_num];
|
|
|
|
/* configure the transaction level parameters */
|
|
ep->trs_buf = pbuf;
|
|
ep->trs_len = buf_len;
|
|
|
|
/* enable endpoint to receive */
|
|
USBD_ENDP_RX_STATUS_SET(ep_num, EPRX_VALID);
|
|
}
|
|
|
|
static void __ep_tx (uint8_t ep_addr, uint8_t *pbuf, uint16_t buf_len)
|
|
{
|
|
__IO uint32_t len = 0U;
|
|
uint8_t ep_num = tu_edpt_number(ep_addr);
|
|
usb_ep_struct *ep = &in_ep[ep_num];
|
|
|
|
/* configure the transaction level parameters */
|
|
ep->trs_buf = pbuf;
|
|
ep->trs_len = buf_len;
|
|
ep->trs_count = 0U;
|
|
|
|
/* transmit length is more than one packet */
|
|
if (ep->trs_len > ep->maxpacket) {
|
|
len = ep->maxpacket;
|
|
} else {
|
|
len = ep->trs_len;
|
|
}
|
|
|
|
usbd_ep_data_write(ep->trs_buf, (pbuf_reg + ep_num)->tx_addr, (uint16_t)len);
|
|
(pbuf_reg + ep_num)->tx_count = (uint16_t)len;
|
|
|
|
/* enable endpoint to transmit */
|
|
USBD_ENDP_TX_STATUS_SET(ep_num, EPTX_VALID);
|
|
}
|
|
|
|
bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
|
|
{
|
|
uint8_t const dir = tu_edpt_dir(ep_addr);
|
|
|
|
if (dir == TUSB_DIR_IN) {
|
|
__ep_tx(ep_addr, buffer, total_bytes);
|
|
} else {
|
|
__ep_rx(ep_addr, buffer, total_bytes);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
|
|
{
|
|
uint8_t ep_num = tu_edpt_number(ep_addr);
|
|
usb_ep_struct *ep;
|
|
|
|
if (ep_addr >> 7U) {
|
|
ep = &in_ep[ep_num];
|
|
|
|
USBD_ENDP_TX_STATUS_SET(ep_num, EPTX_STALL);
|
|
} else {
|
|
ep = &out_ep[ep_num];
|
|
|
|
USBD_ENDP_RX_STATUS_SET(ep_num, EPRX_STALL);
|
|
}
|
|
|
|
ep->stall = 1U;
|
|
|
|
if (0U == ep_num) {
|
|
/* control endpoint need to be stalled in two directions */
|
|
USBD_ENDP_RX_TX_STATUS_SET(ep_num, EPRX_STALL, EPTX_STALL);
|
|
}
|
|
}
|
|
|
|
void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
|
|
{
|
|
uint8_t ep_num = tu_edpt_number(ep_addr);
|
|
usb_ep_struct *ep;
|
|
|
|
if (ep_addr >> 7U) {
|
|
ep = &in_ep[ep_num];
|
|
|
|
/* clear endpoint data toggle bit */
|
|
USBD_DTG_TX_CLEAR(ep_num);
|
|
|
|
/* clear endpoint stall status */
|
|
USBD_ENDP_TX_STATUS_SET(ep_num, EPTX_VALID);
|
|
} else {
|
|
ep = &out_ep[ep_num];
|
|
|
|
/* clear endpoint data toggle bit */
|
|
USBD_DTG_RX_CLEAR(ep_num);
|
|
|
|
/* clear endpoint stall status */
|
|
USBD_ENDP_RX_STATUS_SET(ep_num, EPRX_VALID);
|
|
}
|
|
|
|
ep->stall = 0U;
|
|
}
|
|
|
|
static uint8_t __in_transaction (uint8_t ep_num)
|
|
{
|
|
usb_ep_struct *ep = &in_ep[ep_num];
|
|
|
|
if (0U == ep_num) {
|
|
if (0U != pudev->ctl_count) {
|
|
if (ep->trs_len > ep->maxpacket) {
|
|
/* one data packet has been transmited, update trs_len */
|
|
ep->trs_len -= ep->maxpacket;
|
|
|
|
/* continue to transmit remain data */
|
|
usbd_ep_tx (pudev, EP0_IN, ep->trs_buf, (uint16_t)ep->trs_len);
|
|
|
|
usbd_ep_rx (pudev, 0U, NULL, 0U);
|
|
} else {
|
|
#ifndef USB_DFU
|
|
/* transmit length is maxpacket multiple, so send zero length packet */
|
|
if ((0U == (ep->trs_len % ep->maxpacket)) && (ep->trs_len < pudev->ctl_count)) {
|
|
usbd_ep_tx (pudev, EP0_IN, NULL, 0U);
|
|
|
|
pudev->ctl_count = 0U;
|
|
|
|
usbd_ep_rx (pudev, 0U, NULL, 0U);
|
|
} else
|
|
#endif /* USB_DFU */
|
|
{
|
|
ep->trs_len = 0U;
|
|
|
|
if (USBD_CONFIGURED == pudev->status) {
|
|
pudev->class_data_handler(pudev, USBD_TX, EP0);
|
|
}
|
|
|
|
USBD_CONTRL_STATUS_RX();
|
|
|
|
pudev->ctl_count = 0U;
|
|
}
|
|
}
|
|
} else {
|
|
if (0U != g_device_address) {
|
|
USBD_REG_SET(USBD_DADDR, DADDR_USBEN | g_device_address);
|
|
g_device_address = 0U;
|
|
}
|
|
}
|
|
} else {
|
|
ep->trs_len -= ep->trs_count;
|
|
|
|
if (0U == ep->trs_len) {
|
|
if (USBD_CONFIGURED == pudev->status) {
|
|
pudev->class_data_handler(pudev, USBD_TX, ep_num);
|
|
}
|
|
} else {
|
|
usbd_ep_tx(pudev, ep_num, ep->trs_buf, (uint16_t)ep->trs_len);
|
|
}
|
|
}
|
|
|
|
return USBD_OK;
|
|
}
|
|
|
|
static uint8_t usbd_intf_lpst ()
|
|
{
|
|
uint8_t ep_num = 0U;
|
|
|
|
__IO uint16_t int_status = 0U;
|
|
__IO uint16_t ep_value = 0U;
|
|
|
|
usb_ep_struct *ep = NULL;
|
|
|
|
/* wait till interrupts are not pending */
|
|
while (0U != ((int_status = USBD_REG_GET(USBD_INTF)) & (uint16_t)INTF_STIF)) {
|
|
/* get endpoint number and the value of control and state register */
|
|
ep_num = (uint8_t)(int_status & INTF_EPNUM);
|
|
ep_value = USBD_REG_GET(USBD_EPxCS(ep_num));
|
|
|
|
if (0U == (int_status & INTF_DIR)) {
|
|
/* handle the in direction transaction */
|
|
|
|
ep = &in_ep[ep_num];
|
|
|
|
if (0U != (ep_value & EPxCS_TX_ST)) {
|
|
/* clear successful transmit interrupt flag */
|
|
USBD_ENDP_TX_STAT_CLEAR(ep_num);
|
|
|
|
/* just handle single buffer situation */
|
|
ep->trs_count = (pbuf_reg + ep_num)->tx_count & EPTCNT_CNT;
|
|
|
|
/* maybe mutiple packets */
|
|
ep->trs_buf += ep->trs_count;
|
|
|
|
__in_transaction(ep_num);
|
|
}
|
|
} else {
|
|
/* handle the out direction transaction */
|
|
|
|
uint16_t count = 0U;
|
|
|
|
ep = &(pudev->out_ep[ep_num]);
|
|
|
|
if (0U != (ep_value & EPxCS_RX_ST)) {
|
|
/* clear successful receive interrupt flag */
|
|
USBD_ENDP_RX_STAT_CLEAR(ep_num);
|
|
|
|
count = (pbuf_reg + ep_num)->rx_count & (uint16_t)EPRCNT_CNT;
|
|
|
|
if (0U != count) {
|
|
if (0U != (ep_value & EPxCS_SETUP)) {
|
|
/* handle setup packet */
|
|
usbd_ep_data_read(&(pudev->setup_packet[0]), pbuf_reg->rx_addr, count);
|
|
|
|
/* enter setup status */
|
|
usbd_setup_transaction(pudev);
|
|
|
|
return USBD_OK;
|
|
} else {
|
|
usbd_ep_data_read(ep->trs_buf, (pbuf_reg + ep_num)->rx_addr, count);
|
|
}
|
|
}
|
|
|
|
/* maybe mutiple packets */
|
|
ep->trs_count += count;
|
|
ep->trs_buf += count;
|
|
ep->trs_len -= count;
|
|
|
|
if ((0U == ep->trs_len) || (count < ep->maxpacket)) {
|
|
/* enter data OUT status */
|
|
usbd_out_transaction(pudev, ep_num);
|
|
|
|
ep->trs_count = 0U;
|
|
} else {
|
|
usbd_ep_rx(pudev, ep_num, ep->trs_buf, (uint16_t)ep->trs_len);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return USBD_OK;
|
|
}
|
|
|
|
void dcd_int_handler(uint8_t rhport)
|
|
{
|
|
__IO uint16_t interrupt_flag = 0U;
|
|
__IO uint16_t ctlr = 0U;
|
|
|
|
interrupt_flag = USBD_REG_GET(USBD_INTF);
|
|
|
|
if (g_interrupt_mask & INTF_STIF & interrupt_flag) {
|
|
/* the endpoint successful transfer interrupt service */
|
|
usbd_intf_lpst();
|
|
}
|
|
|
|
if (g_interrupt_mask & INTF_WKUPIF & interrupt_flag) {
|
|
/* clear wakeup interrupt flag in INTF */
|
|
USBD_REG_SET(USBD_INTF, (uint16_t)CLR_WKUPIF);
|
|
|
|
/* USB wakeup interrupt handle */
|
|
usbd_intf_wakeup(usb_device_dev);
|
|
|
|
#ifdef LPM_ENABLED
|
|
/* clear L1 remote wakeup flag */
|
|
L1_remote_wakeup = 0;
|
|
#endif /* LPM_ENABLED */
|
|
}
|
|
|
|
if (g_interrupt_mask & INTF_SPSIF & interrupt_flag) {
|
|
if(!(USBD_REG_GET(USBD_CTL) & CTL_RSREQ)) {
|
|
/* process library core layer suspend routine*/
|
|
usbd_intf_suspend(usb_device_dev);
|
|
|
|
/* clear of suspend interrupt flag bit must be done after setting of CTLR_SETSPS */
|
|
USBD_REG_SET(USBD_INTF, (uint16_t)CLR_SPSIF);
|
|
}
|
|
}
|
|
|
|
if (g_interrupt_mask & INTF_SOFIF & interrupt_flag) {
|
|
/* clear SOF interrupt flag in INTF */
|
|
USBD_REG_SET(USBD_INTF, (uint16_t)CLR_SOFIF);
|
|
|
|
/* USB SOF interrupt handle */
|
|
usbd_intf_sof(usb_device_dev);
|
|
}
|
|
|
|
if (g_interrupt_mask & INTF_ESOFIF & interrupt_flag) {
|
|
/* clear ESOF interrupt flag in INTF */
|
|
USBD_REG_SET(USBD_INTF, (uint16_t)CLR_ESOFIF);
|
|
|
|
/* USB ESOF interrupt handle */
|
|
usbd_intf_esof(usb_device_dev);
|
|
}
|
|
|
|
if (g_interrupt_mask & INTF_RSTIF & interrupt_flag) {
|
|
/* clear reset interrupt flag in INTF */
|
|
USBD_REG_SET(USBD_INTF, (uint16_t)CLR_RSTIF);
|
|
|
|
/* USB reset interrupt handle */
|
|
usbd_intf_reset(usb_device_dev);
|
|
}
|
|
|
|
#ifdef LPM_ENABLED
|
|
if (g_interrupt_mask & INTF_L1REQ & interrupt_flag) {
|
|
/* clear L1 ST bit in LPM INTF */
|
|
USBD_REG_SET(USBD_INTF, CLR_L1REQ);
|
|
|
|
/* read BESL field from subendpoint0 register which coressponds to HIRD parameter in LPM spec */
|
|
besl = (USBD_REG_GET(USBD_LPMCS) & LPMCS_BLSTAT) >> 4;
|
|
|
|
/* read BREMOTEWAKE bit from subendpoint0 register which corresponding to bRemoteWake bit in LPM request */
|
|
L1_remote_wakeup = (USBD_REG_GET(USBD_LPMCS) & LPMCS_REMWK) >> 8;
|
|
|
|
/* process USB device core layer suspend routine */
|
|
/* enter USB model in suspend and system in low power mode (DEEP_SLEEP mode) */
|
|
usbd_intf_suspend(usb_device_dev);
|
|
}
|
|
#endif /* LPM_ENABLED */
|
|
}
|
|
|