Merge branch 'master' into ch32v307
This commit is contained in:
@@ -239,12 +239,12 @@ static void _dcd_ft90x_attach(void)
|
||||
|
||||
CRITICAL_SECTION_BEGIN
|
||||
// Turn off the device enable bit.
|
||||
#if BOARD_DEVICE_RHPORT_SPEED == OPT_MODE_HIGH_SPEED
|
||||
#if BOARD_TUD_MAX_SPEED == OPT_MODE_HIGH_SPEED
|
||||
USBD_REG(fctrl) = 0;
|
||||
#else // BOARD_DEVICE_RHPORT_SPEED == OPT_MODE_FULL_SPEED
|
||||
#else // BOARD_TUD_MAX_SPEED == OPT_MODE_FULL_SPEED
|
||||
//Set the full speed only bit if required.
|
||||
USBD_REG(fctrl) = MASK_USBD_FCTRL_MODE_FS_ONLY;
|
||||
#endif // BOARD_DEVICE_RHPORT_SPEED
|
||||
#endif // BOARD_TUD_MAX_SPEED
|
||||
|
||||
// Clear first reset and suspend interrupts.
|
||||
do
|
||||
@@ -291,7 +291,7 @@ static void _dcd_ft90x_detach(void)
|
||||
delayms(1);
|
||||
|
||||
// Disable USB PHY
|
||||
dcd_disconnect(BOARD_DEVICE_RHPORT_NUM);
|
||||
dcd_disconnect(BOARD_TUD_RHPORT);
|
||||
delayms(1);
|
||||
|
||||
// Disable Chip USB device clock/PM configuration.
|
||||
@@ -312,7 +312,7 @@ static void _dcd_ft90x_detach(void)
|
||||
|
||||
// Determine the speed of the USB to which we are connected.
|
||||
// Set the speed of the PHY accordingly.
|
||||
// High speed can be disabled through CFG_TUSB_RHPORT0_MODE settings.
|
||||
// High speed can be disabled through CFG_TUSB_RHPORT0_MODE or CFG_TUD_MAX_SPEED settings.
|
||||
static void _ft90x_usb_speed(void)
|
||||
{
|
||||
uint8_t fctrl_val;
|
||||
@@ -323,7 +323,7 @@ static void _ft90x_usb_speed(void)
|
||||
delayus(200);
|
||||
}
|
||||
|
||||
#if BOARD_DEVICE_RHPORT_SPEED == OPT_MODE_HIGH_SPEED
|
||||
#if BOARD_TUD_MAX_SPEED == OPT_MODE_HIGH_SPEED
|
||||
|
||||
/* Detect high or full speed */
|
||||
fctrl_val = MASK_USBD_FCTRL_USB_DEV_EN;
|
||||
@@ -347,11 +347,11 @@ static void _ft90x_usb_speed(void)
|
||||
delayus(125 + 5);
|
||||
_speed = (USBD_REG(cmif) & MASK_USBD_CMIF_SOFIRQ) ?
|
||||
TUSB_SPEED_HIGH : TUSB_SPEED_FULL;
|
||||
dcd_event_bus_reset(BOARD_DEVICE_RHPORT_NUM, _speed, true);
|
||||
dcd_event_bus_reset(BOARD_TUD_RHPORT, _speed, true);
|
||||
|
||||
#endif /* !__FT930__ */
|
||||
|
||||
#else // BOARD_DEVICE_RHPORT_SPEED == OPT_MODE_FULL_SPEED
|
||||
#else // BOARD_TUD_MAX_SPEED == OPT_MODE_FULL_SPEED
|
||||
|
||||
/* User force set to full speed */
|
||||
_speed = TUSB_SPEED_FULL;
|
||||
@@ -364,10 +364,10 @@ static void _ft90x_usb_speed(void)
|
||||
}
|
||||
#endif
|
||||
USBD_REG(fctrl) = fctrl_val;
|
||||
dcd_event_bus_reset(BOARD_DEVICE_RHPORT_NUM, _speed, true);
|
||||
dcd_event_bus_reset(BOARD_TUD_RHPORT, _speed, true);
|
||||
return;
|
||||
|
||||
#endif // BOARD_DEVICE_RHPORT_SPEED
|
||||
#endif // BOARD_TUD_MAX_SPEED
|
||||
}
|
||||
|
||||
// Send a buffer to the USB IN FIFO.
|
||||
@@ -587,7 +587,7 @@ void dcd_remote_wakeup(uint8_t rhport)
|
||||
|
||||
SYS->MSC0CFG = SYS->MSC0CFG | MASK_SYS_MSC0CFG_DEV_RMWAKEUP;
|
||||
|
||||
// Atleast 2 ms of delay needed for RESUME Data K state.
|
||||
// At least 2 ms of delay needed for RESUME Data K state.
|
||||
delayms(2);
|
||||
|
||||
SYS->MSC0CFG &= ~MASK_SYS_MSC0CFG_DEV_RMWAKEUP;
|
||||
@@ -649,6 +649,13 @@ void dcd_disconnect(uint8_t rhport)
|
||||
_ft90x_phy_enable(false);
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Endpoint API
|
||||
@@ -713,7 +720,7 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *ep_desc)
|
||||
else
|
||||
total_ram = USBD_RAMTOTAL_OUT;
|
||||
// Work out how much has been allocated to existing endpoints.
|
||||
// The total RAM allocated shoudl alsyes be a positive number as this
|
||||
// The total RAM allocated should always be a positive number as this
|
||||
// algorithm should not let it go below zero.
|
||||
for (int i = 1; i < USBD_MAX_ENDPOINT_COUNT; i++)
|
||||
{
|
||||
@@ -892,7 +899,7 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
|
||||
|
||||
void _ft90x_usbd_ISR(void)
|
||||
{
|
||||
tud_int_handler(BOARD_DEVICE_RHPORT_NUM); // Resolves to dcd_int_handler().
|
||||
tud_int_handler(BOARD_TUD_RHPORT); // Resolves to dcd_int_handler().
|
||||
}
|
||||
|
||||
void dcd_int_handler(uint8_t rhport)
|
||||
@@ -930,19 +937,19 @@ void dcd_int_handler(uint8_t rhport)
|
||||
{
|
||||
// Reset endpoints to default state.
|
||||
_ft90x_reset_edpts();
|
||||
dcd_event_bus_reset(BOARD_DEVICE_RHPORT_NUM, _speed, true);
|
||||
dcd_event_bus_reset(BOARD_TUD_RHPORT, _speed, true);
|
||||
}
|
||||
if (cmif & MASK_USBD_CMIF_SUSIRQ) //Handle Suspend interrupt
|
||||
{
|
||||
dcd_event_bus_signal(BOARD_DEVICE_RHPORT_NUM, DCD_EVENT_SUSPEND, true);
|
||||
dcd_event_bus_signal(BOARD_TUD_RHPORT, DCD_EVENT_SUSPEND, true);
|
||||
}
|
||||
if (cmif & MASK_USBD_CMIF_RESIRQ) //Handle Resume interrupt
|
||||
{
|
||||
dcd_event_bus_signal(BOARD_DEVICE_RHPORT_NUM, DCD_EVENT_RESUME, true);
|
||||
dcd_event_bus_signal(BOARD_TUD_RHPORT, DCD_EVENT_RESUME, true);
|
||||
}
|
||||
if (cmif & MASK_USBD_CMIF_SOFIRQ) //Handle SOF interrupt
|
||||
{
|
||||
dcd_event_bus_signal(BOARD_DEVICE_RHPORT_NUM, DCD_EVENT_SOF, true);
|
||||
dcd_event_bus_signal(BOARD_TUD_RHPORT, DCD_EVENT_SOF, true);
|
||||
}
|
||||
}
|
||||
// Handle endpoint interrupts.
|
||||
@@ -969,11 +976,11 @@ void dcd_int_handler(uint8_t rhport)
|
||||
USBD_EP_SR_REG(USBD_EP_0) = MASK_USBD_EP0SR_STALL;
|
||||
}
|
||||
|
||||
// Host has sent a SETUP packet. Recieve this into the setup packet store.
|
||||
// Host has sent a SETUP packet. Receive this into the setup packet store.
|
||||
_ft90x_dusb_out(USBD_EP_0, (uint8_t *)_ft90x_setup_packet, sizeof(USB_device_request));
|
||||
|
||||
// Send the packet to tinyusb.
|
||||
dcd_event_setup_received(BOARD_DEVICE_RHPORT_NUM, _ft90x_setup_packet, true);
|
||||
dcd_event_setup_received(BOARD_TUD_RHPORT, _ft90x_setup_packet, true);
|
||||
|
||||
// Clear the interrupt that signals a SETUP packet is received.
|
||||
USBD_EP_SR_REG(USBD_EP_0) = (MASK_USBD_EP0SR_SETUP);
|
||||
@@ -995,7 +1002,7 @@ void dcd_int_handler(uint8_t rhport)
|
||||
xfer_bytes = _ft90x_edpt_xfer_out(USBD_EP_0, (uint8_t *)ep_xfer[USBD_EP_0].buff_ptr, xfer_bytes);
|
||||
}
|
||||
// Now signal completion of data packet.
|
||||
dcd_event_xfer_complete(BOARD_DEVICE_RHPORT_NUM, (ep_xfer[USBD_EP_0].dir ? TUSB_DIR_IN_MASK : 0), xfer_bytes, XFER_RESULT_SUCCESS, true);
|
||||
dcd_event_xfer_complete(BOARD_TUD_RHPORT, (ep_xfer[USBD_EP_0].dir ? TUSB_DIR_IN_MASK : 0), xfer_bytes, XFER_RESULT_SUCCESS, true);
|
||||
|
||||
// Allow new transfers on the control endpoint.
|
||||
ep_xfer[USBD_EP_0].valid = 0;
|
||||
@@ -1052,7 +1059,7 @@ void dcd_int_handler(uint8_t rhport)
|
||||
if (ep_xfer[ep_number].remain_size == 0)
|
||||
{
|
||||
// Signal tinyUSB.
|
||||
dcd_event_xfer_complete(BOARD_DEVICE_RHPORT_NUM, ep_number | ep_dirmask, ep_xfer[ep_number].total_size, XFER_RESULT_SUCCESS, true);
|
||||
dcd_event_xfer_complete(BOARD_TUD_RHPORT, ep_number | ep_dirmask, ep_xfer[ep_number].total_size, XFER_RESULT_SUCCESS, true);
|
||||
|
||||
// Allow new transfers on this endpoint.
|
||||
ep_xfer[ep_number].valid = 0;
|
||||
@@ -1077,21 +1084,21 @@ void ft90x_usbd_pm_ISR(void)
|
||||
{
|
||||
// Signal connection interrupt
|
||||
SYS->PMCFG_H = MASK_SYS_PMCFG_PM_GPIO_IRQ_PEND;
|
||||
dcd_event_bus_signal(BOARD_DEVICE_RHPORT_NUM, DCD_EVENT_RESUME, true);
|
||||
dcd_event_bus_signal(BOARD_TUD_RHPORT, DCD_EVENT_RESUME, true);
|
||||
}
|
||||
|
||||
if (pmcfg & MASK_SYS_PMCFG_DEV_DIS_DEV)
|
||||
{
|
||||
// Signal disconnection interrupt
|
||||
SYS->PMCFG_H = MASK_SYS_PMCFG_PM_GPIO_IRQ_PEND;
|
||||
dcd_event_bus_signal(BOARD_DEVICE_RHPORT_NUM, DCD_EVENT_UNPLUGGED, true);
|
||||
dcd_event_bus_signal(BOARD_TUD_RHPORT, DCD_EVENT_UNPLUGGED, true);
|
||||
}
|
||||
|
||||
if (pmcfg & MASK_SYS_PMCFG_HOST_RST_DEV)
|
||||
{
|
||||
// Signal Host Reset interrupt
|
||||
SYS->PMCFG_H = MASK_SYS_PMCFG_PM_GPIO_IRQ_PEND;
|
||||
dcd_event_bus_signal(BOARD_DEVICE_RHPORT_NUM, DCD_EVENT_BUS_RESET, true);
|
||||
dcd_event_bus_signal(BOARD_TUD_RHPORT, DCD_EVENT_BUS_RESET, true);
|
||||
}
|
||||
|
||||
if (pmcfg & MASK_SYS_PMCFG_HOST_RESUME_DEV)
|
||||
@@ -1102,7 +1109,7 @@ void ft90x_usbd_pm_ISR(void)
|
||||
{
|
||||
// If we are driving K-state on Device USB port;
|
||||
// We must maintain the 1ms requirement before resuming the phy
|
||||
dcd_event_bus_signal(BOARD_DEVICE_RHPORT_NUM, DCD_EVENT_RESUME, true);
|
||||
dcd_event_bus_signal(BOARD_TUD_RHPORT, DCD_EVENT_RESUME, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -29,6 +29,14 @@
|
||||
|
||||
#include "fsl_device_registers.h"
|
||||
|
||||
#if !defined(USB1_BASE) && defined(USB_OTG1_BASE)
|
||||
#define USB1_BASE USB_OTG1_BASE
|
||||
#endif
|
||||
|
||||
#if !defined(USB2_BASE) && defined(USB_OTG2_BASE)
|
||||
#define USB2_BASE USB_OTG2_BASE
|
||||
#endif
|
||||
|
||||
static const ci_hs_controller_t _ci_controller[] =
|
||||
{
|
||||
// RT1010 and RT1020 only has 1 USB controller
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
#include "device/dcd.h"
|
||||
#include "ci_hs_type.h"
|
||||
|
||||
#if CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX
|
||||
#if CFG_TUSB_MCU == OPT_MCU_MIMXRT
|
||||
#include "ci_hs_imxrt.h"
|
||||
#elif TU_CHECK_MCU(OPT_MCU_LPC18XX, OPT_MCU_LPC43XX)
|
||||
#include "ci_hs_lpc18_43.h"
|
||||
@@ -266,6 +266,14 @@ void dcd_disconnect(uint8_t rhport)
|
||||
dcd_reg->USBCMD &= ~USBCMD_RUN_STOP;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// HELPER
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
// Chipidea Highspeed USB IP implement EHCI for host functionality
|
||||
|
||||
#if CFG_TUH_ENABLED && \
|
||||
(CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX)
|
||||
(CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INCLUDE
|
||||
@@ -39,7 +39,7 @@
|
||||
#include "portable/ehci/ehci_api.h"
|
||||
#include "ci_hs_type.h"
|
||||
|
||||
#if CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX
|
||||
#if CFG_TUSB_MCU == OPT_MCU_MIMXRT
|
||||
#include "ci_hs_imxrt.h"
|
||||
#elif TU_CHECK_MCU(OPT_MCU_LPC18XX, OPT_MCU_LPC43XX)
|
||||
#include "ci_hs_lpc18_43.h"
|
||||
|
||||
@@ -243,7 +243,7 @@ static struct
|
||||
|
||||
// Converts xfer pointer to epnum (0,1,2,3) regardless of xfer direction
|
||||
#define XFER_EPNUM(xfer) ((xfer - &_dcd.xfer_status[0][0]) >> 1)
|
||||
// Converts xfer pinter to EPx_REGS pointer (returns same pointer for IN and OUT with same endpoint number)
|
||||
// Converts xfer pointer to EPx_REGS pointer (returns same pointer for IN and OUT with same endpoint number)
|
||||
#define XFER_REGS(xfer) ep_regs[XFER_EPNUM(xfer)]
|
||||
// Converts epnum (0,1,2,3) to EPx_REGS pointer
|
||||
#define EPNUM_REGS(epnum) ep_regs[epnum]
|
||||
@@ -882,6 +882,14 @@ void dcd_disconnect(uint8_t rhport)
|
||||
REG_CLR_BIT(USB_MCTRL_REG, USB_NAT);
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool is_in_isr(void)
|
||||
{
|
||||
return (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) != 0;
|
||||
|
||||
@@ -188,7 +188,7 @@ tusb_speed_t hcd_port_speed_get(uint8_t rhport)
|
||||
static void list_remove_qhd_by_addr(ehci_link_t* list_head, uint8_t dev_addr)
|
||||
{
|
||||
for(ehci_link_t* prev = list_head;
|
||||
!prev->terminate && (tu_align32(prev->address) != (uint32_t) list_head);
|
||||
!prev->terminate && (tu_align32(prev->address) != (uint32_t) list_head) && prev != NULL;
|
||||
prev = list_next(prev) )
|
||||
{
|
||||
// TODO check type for ISO iTD and siTD
|
||||
@@ -199,7 +199,7 @@ static void list_remove_qhd_by_addr(ehci_link_t* list_head, uint8_t dev_addr)
|
||||
#pragma GCC diagnostic pop
|
||||
if ( qhd->dev_addr == dev_addr )
|
||||
{
|
||||
// TODO deactive all TD, wait for QHD to inactive before removal
|
||||
// TODO deactivate all TD, wait for QHD to inactive before removal
|
||||
prev->address = qhd->next.address;
|
||||
|
||||
// EHCI 4.8.2 link the removed qhd to async head (which always reachable by Host Controller)
|
||||
@@ -839,7 +839,7 @@ static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t c
|
||||
if (TUSB_SPEED_HIGH == p_qhd->ep_speed)
|
||||
{
|
||||
TU_ASSERT( interval <= 16, );
|
||||
if ( interval < 4) // sub milisecond interval
|
||||
if ( interval < 4) // sub millisecond interval
|
||||
{
|
||||
p_qhd->interval_ms = 0;
|
||||
p_qhd->int_smask = (interval == 1) ? TU_BIN8(11111111) :
|
||||
|
||||
@@ -114,7 +114,7 @@ typedef struct
|
||||
volatile uint32_t current_page : 3 ; ///< Index into the qTD buffer pointer list
|
||||
uint32_t int_on_complete : 1 ; ///< Interrupt on complete
|
||||
volatile uint32_t total_bytes : 15 ; ///< Transfer bytes, decreased during transaction
|
||||
volatile uint32_t data_toggle : 1 ; ///< Data Toogle bit
|
||||
volatile uint32_t data_toggle : 1 ; ///< Data Toggle bit
|
||||
|
||||
|
||||
/// Buffer Page Pointer List, Each element in the list is a 4K page aligned, physical memory address. The lower 12 bits in each pointer are reserved (except for the first one) as each memory pointer must reference the start of a 4K page
|
||||
@@ -160,7 +160,7 @@ typedef struct TU_ATTR_ALIGNED(32)
|
||||
uint8_t used;
|
||||
uint8_t removing; // removed from asyn list, waiting for async advance
|
||||
uint8_t pid;
|
||||
uint8_t interval_ms; // polling interval in frames (or milisecond)
|
||||
uint8_t interval_ms; // polling interval in frames (or millisecond)
|
||||
|
||||
uint16_t total_xferred_bytes; // number of bytes xferred until a qtd with ioc bit set
|
||||
uint8_t reserved2[2];
|
||||
@@ -225,7 +225,7 @@ typedef struct TU_ATTR_ALIGNED(32)
|
||||
uint16_t reserved ; ///< reserved
|
||||
|
||||
// Word 3: siTD Transfer Status and Control
|
||||
// Status [7:0] TODO indentical to qTD Token'status --> refractor later
|
||||
// Status [7:0] TODO identical to qTD Token'status --> refactor later
|
||||
volatile uint32_t : 1 ; // reserved
|
||||
volatile uint32_t split_state : 1 ;
|
||||
volatile uint32_t missed_uframe : 1 ;
|
||||
@@ -350,8 +350,8 @@ typedef volatile struct
|
||||
uint32_t periodic_status : 1 ; ///< Periodic schedule status
|
||||
uint32_t async_status : 1 ; ///< Async schedule status
|
||||
uint32_t : 2 ;
|
||||
uint32_t nxp_int_async : 1 ; ///< NXP customized: This bit is set by the Host Controller when the cause of an interrupt is a completion of a USB transaction where the Transfer Descriptor (TD) has an interrupt on complete (IOC) bit set andthe TD was from the asynchronous schedule. This bit is also set by the Host when a short packet is detected andthe packet is on the asynchronous schedule.
|
||||
uint32_t nxp_int_period : 1 ; ///< NXP customized: This bit is set by the Host Controller when the cause of an interrupt is a completion of a USB transaction where the Transfer Descriptor (TD) has an interrupt on complete (IOC) bit set andthe TD was from the periodic schedule.
|
||||
uint32_t nxp_int_async : 1 ; ///< NXP customized: This bit is set by the Host Controller when the cause of an interrupt is a completion of a USB transaction where the Transfer Descriptor (TD) has an interrupt on complete (IOC) bit set and the TD was from the asynchronous schedule. This bit is also set by the Host when a short packet is detected and the packet is on the asynchronous schedule.
|
||||
uint32_t nxp_int_period : 1 ; ///< NXP customized: This bit is set by the Host Controller when the cause of an interrupt is a completion of a USB transaction where the Transfer Descriptor (TD) has an interrupt on complete (IOC) bit set and the TD was from the periodic schedule.
|
||||
uint32_t : 12 ;
|
||||
}status_bm;
|
||||
};
|
||||
|
||||
@@ -59,6 +59,7 @@ typedef struct {
|
||||
uint16_t queued_len;
|
||||
uint16_t max_size;
|
||||
bool short_packet;
|
||||
uint8_t interval;
|
||||
} xfer_ctl_t;
|
||||
|
||||
static const char *TAG = "TUSB:DCD";
|
||||
@@ -240,6 +241,14 @@ void dcd_disconnect(uint8_t rhport)
|
||||
USB0.dctl |= USB_SFTDISCON_M;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* DCD Endpoint port
|
||||
*------------------------------------------------------------------*/
|
||||
@@ -259,6 +268,7 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_edpt)
|
||||
|
||||
xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, dir);
|
||||
xfer->max_size = tu_edpt_packet_size(desc_edpt);
|
||||
xfer->interval = desc_edpt->bInterval;
|
||||
|
||||
if (dir == TUSB_DIR_OUT) {
|
||||
out_ep[epnum].doepctl |= USB_USBACTEP1_M |
|
||||
@@ -371,6 +381,13 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t to
|
||||
USB0.in_ep_reg[epnum].dieptsiz = (num_packets << USB_D_PKTCNT0_S) | total_bytes;
|
||||
USB0.in_ep_reg[epnum].diepctl |= USB_D_EPENA1_M | USB_D_CNAK1_M; // Enable | CNAK
|
||||
|
||||
// For ISO endpoint with interval=1 set correct DATA0/DATA1 bit for next frame
|
||||
if ((USB0.in_ep_reg[epnum].diepctl & USB_D_EPTYPE0_M) == (1 << USB_D_EPTYPE1_S) && xfer->interval == 1) {
|
||||
// Take odd/even bit from frame counter.
|
||||
uint32_t const odd_frame_now = (USB0.dsts & (1u << USB_SOFFN_S));
|
||||
USB0.in_ep_reg[epnum].diepctl |= (odd_frame_now ? USB_DI_SETD0PID1 : USB_DI_SETD1PID1);
|
||||
}
|
||||
|
||||
// Enable fifo empty interrupt only if there are something to put in the fifo.
|
||||
if(total_bytes != 0) {
|
||||
USB0.dtknqr4_fifoemptymsk |= (1 << epnum);
|
||||
@@ -379,6 +396,13 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t to
|
||||
// Each complete packet for OUT xfers triggers XFRC.
|
||||
USB0.out_ep_reg[epnum].doeptsiz |= USB_PKTCNT0_M | ((xfer->max_size & USB_XFERSIZE0_V) << USB_XFERSIZE0_S);
|
||||
USB0.out_ep_reg[epnum].doepctl |= USB_EPENA0_M | USB_CNAK0_M;
|
||||
|
||||
// For ISO endpoint with interval=1 set correct DATA0/DATA1 bit for next frame
|
||||
if ((USB0.out_ep_reg[epnum].doepctl & USB_D_EPTYPE0_M) == (1 << USB_D_EPTYPE1_S) && xfer->interval == 1) {
|
||||
// Take odd/even bit from frame counter.
|
||||
uint32_t const odd_frame_now = (USB0.dsts & (1u << USB_SOFFN_S));
|
||||
USB0.out_ep_reg[epnum].doepctl |= (odd_frame_now ? USB_DO_SETD0PID1 : USB_DO_SETD1PID1);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -717,7 +741,7 @@ static void handle_epin_ints(void)
|
||||
|
||||
// XFER Timeout
|
||||
if (USB0.in_ep_reg[n].diepint & USB_D_TIMEOUT0_M) {
|
||||
// Clear interrupt or enpoint will hang.
|
||||
// Clear interrupt or endpoint will hang.
|
||||
USB0.in_ep_reg[n].diepint = USB_D_TIMEOUT0_M;
|
||||
// Maybe retry?
|
||||
}
|
||||
|
||||
@@ -648,6 +648,14 @@ void dcd_disconnect(uint8_t rhport)
|
||||
USB0->POWER &= ~USB_POWER_SOFTCONN;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
786
src/portable/microchip/pic/dcd_pic.c
Normal file
786
src/portable/microchip/pic/dcd_pic.c
Normal file
@@ -0,0 +1,786 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Koji Kitayama
|
||||
* Copyright (c) 2022 Reimu NotMoe <reimu@sudomaker.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#include "tusb_option.h"
|
||||
|
||||
#if CFG_TUD_ENABLED && \
|
||||
(CFG_TUSB_MCU == OPT_MCU_PIC32MX || CFG_TUSB_MCU == OPT_MCU_PIC32MM || \
|
||||
CFG_TUSB_MCU == OPT_MCU_PIC32MK || CFG_TUSB_MCU == OPT_MCU_PIC24 || \
|
||||
CFG_TUSB_MCU == OPT_MCU_DSPIC33)
|
||||
|
||||
#include <xc.h>
|
||||
|
||||
#include "device/dcd.h"
|
||||
|
||||
|
||||
#if (CFG_TUSB_MCU == OPT_MCU_PIC32MX || CFG_TUSB_MCU == OPT_MCU_PIC32MM || CFG_TUSB_MCU == OPT_MCU_PIC32MK)
|
||||
|
||||
#define TU_PIC_INT_SIZE 4
|
||||
|
||||
#elif (CFG_TUSB_MCU == OPT_MCU_PIC24 || CFG_TUSB_MCU == OPT_MCU_DSPIC33)
|
||||
|
||||
#define TU_PIC_INT_SIZE 2
|
||||
|
||||
#else
|
||||
|
||||
#error Unsupportd PIC MCU
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if TU_PIC_INT_SIZE == 4
|
||||
|
||||
#ifndef KVA_TO_PA
|
||||
#define KVA_TO_PA(kva) ((uint32_t)(kva) & 0x1fffffff)
|
||||
#endif
|
||||
|
||||
#ifndef PA_TO_KVA1
|
||||
#define PA_TO_KVA1(pa) ((uint32_t)(pa) | 0xA0000000)
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#ifndef KVA_TO_PA
|
||||
#define KVA_TO_PA(kva) (kva)
|
||||
#endif
|
||||
|
||||
#ifndef PA_TO_KVA1
|
||||
#define PA_TO_KVA1(pa) (pa)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
enum {
|
||||
TOK_PID_OUT = 0x1u,
|
||||
TOK_PID_IN = 0x9u,
|
||||
TOK_PID_SETUP = 0xDu,
|
||||
};
|
||||
|
||||
// The BDT is 8 bytes on 32bit PICs and 4 bytes on 8/16bit PICs
|
||||
#if TU_PIC_INT_SIZE == 4
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
union {
|
||||
uint32_t head;
|
||||
struct {
|
||||
union {
|
||||
struct {
|
||||
uint16_t : 2;
|
||||
uint16_t tok_pid : 4;
|
||||
uint16_t data : 1;
|
||||
uint16_t own : 1;
|
||||
uint16_t : 8;
|
||||
};
|
||||
struct {
|
||||
uint16_t : 2;
|
||||
uint16_t bdt_stall : 1;
|
||||
uint16_t dts : 1;
|
||||
uint16_t ninc : 1;
|
||||
uint16_t keep : 1;
|
||||
uint16_t : 10;
|
||||
};
|
||||
};
|
||||
uint16_t bc : 10;
|
||||
uint16_t : 6;
|
||||
};
|
||||
};
|
||||
uint8_t *addr;
|
||||
} buffer_descriptor_t;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(buffer_descriptor_t) == 8, "size is not correct" );
|
||||
#else
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
union {
|
||||
uint16_t head;
|
||||
|
||||
struct {
|
||||
uint16_t : 10;
|
||||
uint16_t tok_pid : 4;
|
||||
uint16_t data : 1;
|
||||
uint16_t own : 1;
|
||||
};
|
||||
struct {
|
||||
uint16_t : 10;
|
||||
uint16_t bdt_stall : 1;
|
||||
uint16_t dts : 1;
|
||||
uint16_t ninc : 1;
|
||||
uint16_t keep : 1;
|
||||
};
|
||||
|
||||
struct {
|
||||
uint16_t bc : 10;
|
||||
uint16_t : 6;
|
||||
};
|
||||
};
|
||||
uint8_t *addr;
|
||||
} buffer_descriptor_t;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(buffer_descriptor_t) == 4, "size is not correct" );
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
union {
|
||||
uint32_t state;
|
||||
struct {
|
||||
uint32_t max_packet_size :11;
|
||||
uint32_t : 5;
|
||||
uint32_t odd : 1;
|
||||
uint32_t :15;
|
||||
};
|
||||
};
|
||||
uint16_t length;
|
||||
uint16_t remaining;
|
||||
} endpoint_state_t;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(endpoint_state_t) == 8, "size is not correct" );
|
||||
|
||||
typedef struct
|
||||
{
|
||||
union {
|
||||
/* [#EP][OUT,IN][EVEN,ODD] */
|
||||
buffer_descriptor_t bdt[16][2][2];
|
||||
#if TU_PIC_INT_SIZE == 4
|
||||
uint16_t bda[256];
|
||||
#else
|
||||
uint8_t bda[256];
|
||||
#endif
|
||||
};
|
||||
TU_ATTR_ALIGNED(4) union {
|
||||
endpoint_state_t endpoint[16][2];
|
||||
endpoint_state_t endpoint_unified[16 * 2];
|
||||
};
|
||||
uint8_t setup_packet[8];
|
||||
uint8_t addr;
|
||||
} dcd_data_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INTERNAL OBJECT & FUNCTION DECLARATION
|
||||
//--------------------------------------------------------------------+
|
||||
// BDT(Buffer Descriptor Table) must be 256-byte aligned
|
||||
CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(512) volatile static dcd_data_t _dcd;
|
||||
|
||||
#if TU_PIC_INT_SIZE == 4
|
||||
TU_VERIFY_STATIC( sizeof(_dcd.bdt) == 512, "size is not correct" );
|
||||
#else
|
||||
TU_VERIFY_STATIC( sizeof(_dcd.bdt) == 256, "size is not correct" );
|
||||
#endif
|
||||
|
||||
#if TU_PIC_INT_SIZE == 4
|
||||
typedef uint32_t ep_reg_t;
|
||||
#elif TU_PIC_INT_SIZE == 2
|
||||
typedef uint16_t ep_reg_t;
|
||||
#endif
|
||||
|
||||
static inline volatile void *ep_addr(uint8_t rhport, uint8_t ep_num) {
|
||||
#if CFG_TUSB_MCU == OPT_MCU_PIC32MK
|
||||
volatile void *ep_reg_base = rhport ? (&U2EP0) : (&U1EP0);
|
||||
#else
|
||||
volatile void *ep_reg_base = &U1EP0;
|
||||
#endif
|
||||
#if TU_PIC_INT_SIZE == 4
|
||||
const size_t offset = 0x10;
|
||||
#else
|
||||
const size_t offset = 0x2;
|
||||
#endif
|
||||
return ep_reg_base + offset * ep_num;
|
||||
}
|
||||
|
||||
static inline ep_reg_t ep_read(uint8_t rhport, uint8_t ep_num) {
|
||||
volatile ep_reg_t *ep = ep_addr(rhport, ep_num);
|
||||
return *ep;
|
||||
}
|
||||
|
||||
static inline void ep_write(uint8_t rhport, uint8_t ep_num, ep_reg_t val) {
|
||||
volatile ep_reg_t *ep = ep_addr(rhport, ep_num);
|
||||
*ep = val;
|
||||
}
|
||||
|
||||
static inline void ep_clear(uint8_t rhport, uint8_t ep_num, ep_reg_t val) {
|
||||
#if TU_PIC_INT_SIZE == 4
|
||||
volatile ep_reg_t *ep_clr = (ep_addr(rhport, ep_num) + 0x4);
|
||||
*ep_clr = val;
|
||||
#else
|
||||
ep_reg_t v = ep_read(rhport, ep_num);
|
||||
v &= ~val;
|
||||
ep_write(rhport, ep_num, v);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void ep_set(uint8_t rhport, uint8_t ep_num, ep_reg_t val) {
|
||||
#if TU_PIC_INT_SIZE == 4
|
||||
volatile ep_reg_t *ep_s = (ep_addr(rhport, ep_num) + 0x8);
|
||||
*ep_s = val;
|
||||
#else
|
||||
ep_reg_t v = ep_read(rhport, ep_num);
|
||||
v |= val;
|
||||
ep_write(rhport, ep_num, v);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void intr_enable(uint8_t rhport) {
|
||||
#if CFG_TUSB_MCU == OPT_MCU_PIC32MM
|
||||
IEC0SET = _IEC0_USBIE_MASK;
|
||||
#elif CFG_TUSB_MCU == OPT_MCU_PIC32MX
|
||||
IEC1SET = _IEC1_USBIE_MASK;
|
||||
#elif CFG_TUSB_MCU == OPT_MCU_PIC32MK
|
||||
if (rhport == 0)
|
||||
IEC1SET = _IEC1_USB1IE_MASK;
|
||||
else
|
||||
IEC7SET = _IEC7_USB2IE_MASK;
|
||||
#elif (CFG_TUSB_MCU == OPT_MCU_PIC24) || (CFG_TUSB_MCU == OPT_MCU_DSPIC33)
|
||||
IEC5bits.USB1IE = 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void intr_disable(uint8_t rhport) {
|
||||
#if CFG_TUSB_MCU == OPT_MCU_PIC32MM
|
||||
IEC0CLR = _IEC0_USBIE_MASK;
|
||||
#elif CFG_TUSB_MCU == OPT_MCU_PIC32MX
|
||||
IEC1CLR = _IEC1_USBIE_MASK;
|
||||
#elif CFG_TUSB_MCU == OPT_MCU_PIC32MK
|
||||
if (rhport == 0)
|
||||
IEC1CLR = _IEC1_USB1IE_MASK;
|
||||
else
|
||||
IEC7CLR = _IEC7_USB2IE_MASK;
|
||||
#elif (CFG_TUSB_MCU == OPT_MCU_PIC24) || (CFG_TUSB_MCU == OPT_MCU_DSPIC33)
|
||||
IEC5bits.USB1IE = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int intr_is_enabled(uint8_t rhport) {
|
||||
#if CFG_TUSB_MCU == OPT_MCU_PIC32MM
|
||||
return IEC0bits.USBIE;
|
||||
#elif CFG_TUSB_MCU == OPT_MCU_PIC32MX
|
||||
return IEC1bits.USBIE;
|
||||
#elif CFG_TUSB_MCU == OPT_MCU_PIC32MK
|
||||
if (rhport == 0)
|
||||
return IEC1bits.USB1IE;
|
||||
else
|
||||
return IEC7bits.USB2IE;
|
||||
#elif (CFG_TUSB_MCU == OPT_MCU_PIC24) || (CFG_TUSB_MCU == OPT_MCU_DSPIC33)
|
||||
return IEC5bits.USB1IE;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void intr_clear(uint8_t rhport) {
|
||||
#if CFG_TUSB_MCU == OPT_MCU_PIC32MM
|
||||
IFS0CLR = _IFS0_USBIF_MASK;
|
||||
#elif CFG_TUSB_MCU == OPT_MCU_PIC32MX
|
||||
IFS1CLR = _IFS1_USBIF_MASK;
|
||||
#elif CFG_TUSB_MCU == OPT_MCU_PIC32MK
|
||||
if (rhport == 0)
|
||||
IFS1CLR = _IFS1_USB1IF_MASK;
|
||||
else
|
||||
IFS7CLR = _IFS7_USB2IF_MASK;
|
||||
#elif (CFG_TUSB_MCU == OPT_MCU_PIC24) || (CFG_TUSB_MCU == OPT_MCU_DSPIC33)
|
||||
IFS5bits.USB1IF = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void prepare_next_setup_packet(uint8_t rhport)
|
||||
{
|
||||
const unsigned out_odd = _dcd.endpoint[0][0].odd;
|
||||
const unsigned in_odd = _dcd.endpoint[0][1].odd;
|
||||
TU_ASSERT(0 == _dcd.bdt[0][0][out_odd].own, );
|
||||
|
||||
_dcd.bdt[0][0][out_odd].data = 0;
|
||||
_dcd.bdt[0][0][out_odd ^ 1].data = 1;
|
||||
_dcd.bdt[0][1][in_odd].data = 1;
|
||||
_dcd.bdt[0][1][in_odd ^ 1].data = 0;
|
||||
dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_OUT),
|
||||
_dcd.setup_packet, sizeof(_dcd.setup_packet));
|
||||
}
|
||||
|
||||
static void process_stall(uint8_t rhport)
|
||||
{
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
unsigned const endpt = ep_read(rhport, i);
|
||||
|
||||
if (endpt & _U1EP0_EPSTALL_MASK) {
|
||||
// prepare next setup if endpoint0
|
||||
if ( i == 0 ) prepare_next_setup_packet(rhport);
|
||||
|
||||
// clear stall bit
|
||||
ep_clear(rhport, i, _U1EP0_EPSTALL_MASK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void process_tokdne(uint8_t rhport)
|
||||
{
|
||||
ep_reg_t s = U1STAT;
|
||||
|
||||
U1IR = _U1IR_TRNIF_MASK;
|
||||
|
||||
uint8_t epnum = (s >> _U1STAT_ENDPT0_POSITION);
|
||||
uint8_t dir = (s & _U1STAT_DIR_MASK) >> _U1STAT_DIR_POSITION;
|
||||
unsigned odd = (s & _U1STAT_PPBI_MASK) ? 1 : 0;
|
||||
|
||||
buffer_descriptor_t *bd = (buffer_descriptor_t *)&_dcd.bda[s];
|
||||
endpoint_state_t *ep = &_dcd.endpoint_unified[s >> 3];
|
||||
|
||||
/* fetch pid before discarded by the next steps */
|
||||
const unsigned pid = bd->tok_pid;
|
||||
|
||||
/* reset values for a next transfer */
|
||||
bd->bdt_stall = 0;
|
||||
bd->dts = 1;
|
||||
bd->ninc = 0;
|
||||
bd->keep = 0;
|
||||
/* update the odd variable to prepare for the next transfer */
|
||||
ep->odd = odd ^ 1;
|
||||
if (pid == TOK_PID_SETUP) {
|
||||
dcd_event_setup_received(rhport, (uint8_t *)PA_TO_KVA1(bd->addr), true);
|
||||
#if TU_PIC_INT_SIZE == 4
|
||||
U1CONCLR = _U1CON_PKTDIS_TOKBUSY_MASK;
|
||||
#else
|
||||
U1CONbits.PKTDIS = 0;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
const unsigned bc = bd->bc;
|
||||
const unsigned remaining = ep->remaining - bc;
|
||||
if (remaining && bc == ep->max_packet_size) {
|
||||
/* continue the transferring consecutive data */
|
||||
ep->remaining = remaining;
|
||||
const int next_remaining = remaining - ep->max_packet_size;
|
||||
if (next_remaining > 0) {
|
||||
/* prepare to the after next transfer */
|
||||
bd->addr += ep->max_packet_size * 2;
|
||||
bd->bc = next_remaining > ep->max_packet_size ? ep->max_packet_size: next_remaining;
|
||||
bd->own = 1; /* the own bit must set after addr */
|
||||
}
|
||||
return;
|
||||
}
|
||||
const unsigned length = ep->length;
|
||||
dcd_event_xfer_complete(rhport,
|
||||
tu_edpt_addr(epnum, dir),
|
||||
length - remaining, XFER_RESULT_SUCCESS, true);
|
||||
if (0 == epnum && 0 == length) {
|
||||
/* After completion a ZLP of control transfer,
|
||||
* it prepares for the next steup transfer. */
|
||||
if (_dcd.addr) {
|
||||
/* When the transfer was the SetAddress,
|
||||
* the device address should be updated here. */
|
||||
U1ADDR = _dcd.addr;
|
||||
_dcd.addr = 0;
|
||||
}
|
||||
prepare_next_setup_packet(rhport);
|
||||
}
|
||||
}
|
||||
|
||||
static void process_bus_reset(uint8_t rhport)
|
||||
{
|
||||
#if TU_PIC_INT_SIZE == 4
|
||||
U1PWRCCLR = _U1PWRC_USUSPEND_MASK;
|
||||
U1CONSET = _U1CON_PPBRST_MASK;
|
||||
#else
|
||||
U1PWRCbits.USUSPND = 0;
|
||||
U1CONbits.PPBRST = 1;
|
||||
#endif
|
||||
U1ADDR = 0;
|
||||
|
||||
U1IE = _U1IE_URSTIE_MASK | _U1IE_TRNIE_MASK | _U1IE_IDLEIE_MASK |
|
||||
_U1IE_UERRIE_MASK | _U1IE_STALLIE_MASK;
|
||||
|
||||
U1EP0 = _U1EP0_EPHSHK_MASK | _U1EP0_EPRXEN_MASK | _U1EP0_EPTXEN_MASK;
|
||||
|
||||
for (unsigned i = 1; i < 16; ++i) {
|
||||
ep_write(rhport, i, 0);
|
||||
}
|
||||
|
||||
buffer_descriptor_t *bd = _dcd.bdt[0][0];
|
||||
for (unsigned i = 0; i < sizeof(_dcd.bdt)/sizeof(*bd); ++i, ++bd) {
|
||||
bd->head = 0;
|
||||
}
|
||||
const endpoint_state_t ep0 = {
|
||||
.max_packet_size = CFG_TUD_ENDPOINT0_SIZE,
|
||||
.odd = 0,
|
||||
.length = 0,
|
||||
.remaining = 0,
|
||||
};
|
||||
_dcd.endpoint[0][0] = ep0;
|
||||
_dcd.endpoint[0][1] = ep0;
|
||||
tu_memclr(_dcd.endpoint[1], sizeof(_dcd.endpoint) - sizeof(_dcd.endpoint[0]));
|
||||
_dcd.addr = 0;
|
||||
prepare_next_setup_packet(rhport);
|
||||
#if TU_PIC_INT_SIZE == 4
|
||||
U1CONCLR = _U1CON_PPBRST_MASK;
|
||||
#else
|
||||
U1CONbits.PPBRST = 0;
|
||||
#endif
|
||||
dcd_event_bus_reset(rhport, TUSB_SPEED_FULL, true);
|
||||
}
|
||||
|
||||
static void process_bus_sleep(uint8_t rhport)
|
||||
{
|
||||
// Enable resume & disable suspend interrupt
|
||||
dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
|
||||
}
|
||||
|
||||
static void process_bus_resume(uint8_t rhport)
|
||||
{
|
||||
// Enable suspend & disable resume interrupt
|
||||
#if TU_PIC_INT_SIZE == 4
|
||||
U1PWRCCLR = _U1PWRC_USUSPEND_MASK;
|
||||
U1IECLR = _U1IE_RESUMEIE_MASK;
|
||||
U1IESET = _U1IE_IDLEIE_MASK;
|
||||
#else
|
||||
U1PWRCbits.USUSPND = 0;
|
||||
U1IEbits.RESUMEIE = 0;
|
||||
U1IEbits.IDLEIE = 1;
|
||||
#endif
|
||||
|
||||
dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* Device API
|
||||
*------------------------------------------------------------------*/
|
||||
void dcd_init(uint8_t rhport)
|
||||
{
|
||||
intr_disable(rhport);
|
||||
intr_clear(rhport);
|
||||
|
||||
#if CFG_TUSB_MCU == OPT_MCU_PIC32MM
|
||||
TRISBbits.TRISB6 = 1;
|
||||
#endif
|
||||
|
||||
tu_memclr(&_dcd, sizeof(_dcd));
|
||||
|
||||
#if TU_PIC_INT_SIZE == 4
|
||||
U1PWRCSET = _U1PWRC_USBPWR_MASK;
|
||||
#else
|
||||
U1PWRCbits.USBPWR = 1;
|
||||
#endif
|
||||
|
||||
#if TU_PIC_INT_SIZE == 4
|
||||
uint32_t bdt_phys = KVA_TO_PA((uintptr_t)_dcd.bdt);
|
||||
|
||||
U1BDTP1 = (uint8_t)(bdt_phys >> 8);
|
||||
U1BDTP2 = (uint8_t)(bdt_phys >> 16);
|
||||
U1BDTP3 = (uint8_t)(bdt_phys >> 24);
|
||||
#else
|
||||
U1BDTP1 = (uint8_t)((uint16_t)(void *)_dcd.bdt >> 8);
|
||||
|
||||
U1CNFG1bits.PPB = 2;
|
||||
#endif
|
||||
|
||||
U1IE = _U1IE_URSTIE_MASK;
|
||||
|
||||
dcd_connect(rhport);
|
||||
}
|
||||
|
||||
void dcd_int_enable(uint8_t rhport)
|
||||
{
|
||||
intr_enable(rhport);
|
||||
}
|
||||
|
||||
void dcd_int_disable(uint8_t rhport)
|
||||
{
|
||||
intr_disable(rhport);
|
||||
}
|
||||
|
||||
void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
|
||||
{
|
||||
_dcd.addr = dev_addr & 0x7F;
|
||||
/* Response with status first before changing device address */
|
||||
dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
|
||||
}
|
||||
|
||||
void dcd_remote_wakeup(uint8_t rhport)
|
||||
{
|
||||
#if TU_PIC_INT_SIZE == 4
|
||||
U1CONSET = _U1CON_RESUME_MASK;
|
||||
#else
|
||||
U1CONbits.RESUME = 1;
|
||||
#endif
|
||||
unsigned cnt = 25000000 / 1000;
|
||||
while (cnt--) asm volatile("nop");
|
||||
|
||||
#if TU_PIC_INT_SIZE == 4
|
||||
U1CONCLR = _U1CON_RESUME_MASK;
|
||||
#else
|
||||
U1CONbits.RESUME = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void dcd_connect(uint8_t rhport)
|
||||
{
|
||||
while (!U1CONbits.USBEN) {
|
||||
#if TU_PIC_INT_SIZE == 4
|
||||
U1CONSET = _U1CON_USBEN_SOFEN_MASK;
|
||||
#else
|
||||
U1CONbits.USBEN = 1;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void dcd_disconnect(uint8_t rhport)
|
||||
{
|
||||
U1CON = 0;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
|
||||
{
|
||||
const unsigned ep_addr = ep_desc->bEndpointAddress;
|
||||
const unsigned epn = tu_edpt_number(ep_addr);
|
||||
const unsigned dir = tu_edpt_dir(ep_addr);
|
||||
const unsigned xfer = ep_desc->bmAttributes.xfer;
|
||||
endpoint_state_t *ep = &_dcd.endpoint[epn][dir];
|
||||
const unsigned odd = ep->odd;
|
||||
buffer_descriptor_t *bd = _dcd.bdt[epn][dir];
|
||||
|
||||
/* No support for control transfer */
|
||||
TU_ASSERT(epn && (xfer != TUSB_XFER_CONTROL));
|
||||
|
||||
ep->max_packet_size = tu_edpt_packet_size(ep_desc);
|
||||
|
||||
|
||||
unsigned val = _U1EP0_EPCONDIS_MASK;
|
||||
val |= (xfer != TUSB_XFER_ISOCHRONOUS) ? _U1EP0_EPHSHK_MASK : 0;
|
||||
val |= dir ? _U1EP0_EPTXEN_MASK : _U1EP0_EPRXEN_MASK;
|
||||
|
||||
ep_reg_t tmp = ep_read(rhport, epn);
|
||||
tmp |= val;
|
||||
ep_write(rhport, epn, tmp);
|
||||
|
||||
if (xfer != TUSB_XFER_ISOCHRONOUS) {
|
||||
bd[odd].dts = 1;
|
||||
bd[odd].data = 0;
|
||||
bd[odd ^ 1].dts = 1;
|
||||
bd[odd ^ 1].data = 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void dcd_edpt_close_all(uint8_t rhport)
|
||||
{
|
||||
const unsigned ie = intr_is_enabled(rhport);
|
||||
intr_disable(rhport);
|
||||
|
||||
for (unsigned i = 1; i < 16; ++i) {
|
||||
ep_write(rhport, i, 0);
|
||||
}
|
||||
|
||||
if (ie) intr_enable(rhport);
|
||||
|
||||
buffer_descriptor_t *bd = _dcd.bdt[1][0];
|
||||
for (unsigned i = 2; i < sizeof(_dcd.bdt)/sizeof(*bd); ++i, ++bd) {
|
||||
bd->head = 0;
|
||||
}
|
||||
endpoint_state_t *ep = &_dcd.endpoint[1][0];
|
||||
for (unsigned i = 2; i < sizeof(_dcd.endpoint)/sizeof(*ep); ++i, ++ep) {
|
||||
/* Clear except the odd */
|
||||
ep->max_packet_size = 0;
|
||||
ep->length = 0;
|
||||
ep->remaining = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
|
||||
{
|
||||
const unsigned epn = tu_edpt_number(ep_addr);
|
||||
const unsigned dir = tu_edpt_dir(ep_addr);
|
||||
endpoint_state_t *ep = &_dcd.endpoint[epn][dir];
|
||||
buffer_descriptor_t *bd = _dcd.bdt[epn][dir];
|
||||
const unsigned msk = dir ? _U1EP0_EPTXEN_MASK : _U1EP0_EPRXEN_MASK;
|
||||
const unsigned ie = intr_is_enabled(rhport);
|
||||
|
||||
intr_disable(rhport);
|
||||
|
||||
ep_clear(rhport, epn, msk);
|
||||
|
||||
ep->max_packet_size = 0;
|
||||
ep->length = 0;
|
||||
ep->remaining = 0;
|
||||
bd[0].head = 0;
|
||||
bd[1].head = 0;
|
||||
|
||||
if (ie) intr_enable(rhport);
|
||||
}
|
||||
|
||||
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes)
|
||||
{
|
||||
|
||||
const unsigned epn = tu_edpt_number(ep_addr);
|
||||
const unsigned dir = tu_edpt_dir(ep_addr);
|
||||
endpoint_state_t *ep = &_dcd.endpoint[epn][dir];
|
||||
buffer_descriptor_t *bd = &_dcd.bdt[epn][dir][ep->odd];
|
||||
TU_ASSERT(0 == bd->own);
|
||||
|
||||
const unsigned ie = intr_is_enabled(rhport);
|
||||
|
||||
intr_disable(rhport);
|
||||
|
||||
ep->length = total_bytes;
|
||||
ep->remaining = total_bytes;
|
||||
|
||||
const unsigned mps = ep->max_packet_size;
|
||||
if (total_bytes > mps) {
|
||||
buffer_descriptor_t *next = ep->odd ? bd - 1: bd + 1;
|
||||
/* When total_bytes is greater than the max packet size,
|
||||
* it prepares to the next transfer to avoid NAK in advance. */
|
||||
next->bc = total_bytes >= 2 * mps ? mps: total_bytes - mps;
|
||||
next->addr = (uint8_t *)KVA_TO_PA(buffer + mps);
|
||||
next->own = 1;
|
||||
}
|
||||
bd->bc = total_bytes >= mps ? mps: total_bytes;
|
||||
bd->addr = (uint8_t *)KVA_TO_PA(buffer);
|
||||
bd->own = 1; /* This bit must be set last */
|
||||
|
||||
if (ie) intr_enable(rhport);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
|
||||
{
|
||||
(void) rhport;
|
||||
const unsigned epn = tu_edpt_number(ep_addr);
|
||||
|
||||
if (0 == epn) {
|
||||
ep_set(rhport, epn, _U1EP0_EPSTALL_MASK);
|
||||
} else {
|
||||
const unsigned dir = tu_edpt_dir(ep_addr);
|
||||
const unsigned odd = _dcd.endpoint[epn][dir].odd;
|
||||
buffer_descriptor_t *bd = &_dcd.bdt[epn][dir][odd];
|
||||
TU_ASSERT(0 == bd->own,);
|
||||
|
||||
const unsigned ie = intr_is_enabled(rhport);
|
||||
|
||||
intr_disable(rhport);
|
||||
|
||||
bd->bdt_stall = 1;
|
||||
bd->own = 1; /* This bit must be set last */
|
||||
|
||||
if (ie) intr_enable(rhport);
|
||||
}
|
||||
}
|
||||
|
||||
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
|
||||
{
|
||||
const unsigned epn = tu_edpt_number(ep_addr);
|
||||
TU_VERIFY(epn,);
|
||||
const unsigned dir = tu_edpt_dir(ep_addr);
|
||||
const unsigned odd = _dcd.endpoint[epn][dir].odd;
|
||||
buffer_descriptor_t *bd = _dcd.bdt[epn][dir];
|
||||
TU_VERIFY(bd[odd].own,);
|
||||
|
||||
const unsigned ie = intr_is_enabled(rhport);
|
||||
|
||||
intr_disable(rhport);
|
||||
|
||||
bd[odd].own = 0;
|
||||
|
||||
// clear stall
|
||||
bd[odd].bdt_stall = 0;
|
||||
|
||||
// Reset data toggle
|
||||
bd[odd ].data = 0;
|
||||
bd[odd ^ 1].data = 1;
|
||||
|
||||
// We already cleared this in ISR, but just clear it here to be safe
|
||||
const unsigned endpt = ep_read(rhport, epn);
|
||||
if (endpt & _U1EP0_EPSTALL_MASK) {
|
||||
ep_clear(rhport, endpt, _U1EP0_EPSTALL_MASK);
|
||||
}
|
||||
|
||||
if (ie) intr_enable(rhport);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// ISR
|
||||
//--------------------------------------------------------------------+
|
||||
void dcd_int_handler(uint8_t rhport)
|
||||
{
|
||||
uint32_t is = U1IR;
|
||||
uint32_t msk = U1IE;
|
||||
|
||||
U1IR = is & ~msk;
|
||||
is &= msk;
|
||||
|
||||
if (is & _U1IR_UERRIF_MASK) {
|
||||
uint32_t es = U1EIR;
|
||||
U1EIR = es;
|
||||
U1IR = is; /* discard any pending events */
|
||||
}
|
||||
|
||||
if (is & _U1IR_URSTIF_MASK) {
|
||||
U1IR = is; /* discard any pending events */
|
||||
process_bus_reset(rhport);
|
||||
}
|
||||
|
||||
if (is & _U1IR_IDLEIF_MASK) {
|
||||
// Note Host usually has extra delay after bus reset (without SOF), which could falsely
|
||||
// detected as Sleep event. Though usbd has debouncing logic so we are good
|
||||
U1IR = _U1IR_IDLEIF_MASK;
|
||||
process_bus_sleep(rhport);
|
||||
}
|
||||
|
||||
if (is & _U1IR_RESUMEIF_MASK) {
|
||||
U1IR = _U1IR_RESUMEIF_MASK;
|
||||
process_bus_resume(rhport);
|
||||
}
|
||||
|
||||
if (is & _U1IR_SOFIF_MASK) {
|
||||
U1IR = _U1IR_SOFIF_MASK;
|
||||
dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
|
||||
}
|
||||
|
||||
if (is & _U1IR_STALLIF_MASK) {
|
||||
U1IR = _U1IR_STALLIF_MASK;
|
||||
process_stall(rhport);
|
||||
}
|
||||
|
||||
if (is & _U1IR_TRNIF_MASK) {
|
||||
process_tokdne(rhport);
|
||||
}
|
||||
|
||||
intr_clear(rhport);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -186,6 +186,14 @@ void dcd_disconnect(uint8_t rhport)
|
||||
USB_REGS->POWERbits.SOFTCONN = 1;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool is_in_isr(void)
|
||||
{
|
||||
return (_CP0_GET_STATUS() & (_CP0_STATUS_EXL_MASK | _CP0_STATUS_IPL_MASK)) != 0;
|
||||
@@ -530,6 +538,7 @@ static void ep0_handle_rx(void)
|
||||
|
||||
transferred = rx_fifo_read(0, xfer->buffer + xfer->transferred);
|
||||
xfer->transferred += transferred;
|
||||
TU_ASSERT(xfer->transferred <= xfer->total_len,);
|
||||
if (transferred < xfer->max_packet_size || xfer->transferred == xfer->total_len)
|
||||
{
|
||||
ep0_set_stage(EP0_STAGE_DATA_OUT_COMPLETE);
|
||||
@@ -560,8 +569,10 @@ static void epn_handle_rx_int(uint8_t epnum)
|
||||
transferred = rx_fifo_read(epnum, xfer->buffer + xfer->transferred);
|
||||
USB_REGS->EPCSR[epnum].RXCSRL_HOSTbits.RXPKTRDY = 0;
|
||||
xfer->transferred += transferred;
|
||||
TU_ASSERT(xfer->transferred <= xfer->total_len,);
|
||||
if (transferred < xfer->max_packet_size || xfer->transferred == xfer->total_len)
|
||||
{
|
||||
USB_REGS->INTRRXEbits.w &= ~(1u << epnum);
|
||||
xfer_complete(xfer, XFER_RESULT_SUCCESS, true);
|
||||
}
|
||||
}
|
||||
@@ -579,6 +590,7 @@ static void epn_handle_tx_int(uint8_t epnum)
|
||||
else
|
||||
{
|
||||
xfer->transferred += xfer->last_packet_size;
|
||||
TU_ASSERT(xfer->transferred <= xfer->total_len,);
|
||||
if (xfer->last_packet_size < xfer->max_packet_size || xfer->transferred == xfer->total_len)
|
||||
{
|
||||
xfer->last_packet_size = 0;
|
||||
@@ -689,7 +701,7 @@ void dcd_int_handler(uint8_t rhport)
|
||||
int i;
|
||||
uint8_t mask;
|
||||
__USBCSR2bits_t csr2_bits;
|
||||
uint16_t rxints = USB_REGS->INTRRX;
|
||||
uint16_t rxints = USB_REGS->INTRRX & USB_REGS->INTRRXEbits.w;
|
||||
uint16_t txints = USB_REGS->INTRTX;
|
||||
csr2_bits = USBCSR2bits;
|
||||
(void) rhport;
|
||||
|
||||
@@ -21,16 +21,16 @@
|
||||
* THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
|
||||
*******************************************************************************/
|
||||
/*******************************************************************************
|
||||
USBHS Peripheral Library Register Defintions
|
||||
USBHS Peripheral Library Register Definitions
|
||||
|
||||
File Name:
|
||||
usbhs_registers.h
|
||||
|
||||
Summary:
|
||||
USBHS PLIB Register Defintions
|
||||
USBHS PLIB Register Definitions
|
||||
|
||||
Description:
|
||||
This file contains the constants and defintions which are required by the
|
||||
This file contains the constants and definitions which are required by the
|
||||
the USBHS library.
|
||||
*******************************************************************************/
|
||||
|
||||
|
||||
@@ -180,6 +180,14 @@ void dcd_connect(uint8_t rhport)
|
||||
USB->DEVICE.CTRLB.reg &= ~USB_DEVICE_CTRLB_DETACH;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* DCD Endpoint port
|
||||
*------------------------------------------------------------------*/
|
||||
@@ -214,14 +222,14 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
|
||||
UsbDeviceDescBank* bank = &sram_registers[epnum][dir];
|
||||
uint32_t size_value = 0;
|
||||
while (size_value < 7) {
|
||||
if (1 << (size_value + 3) == tu_edpt_packet_size(desc_edpt)) {
|
||||
if (1 << (size_value + 3) >= tu_edpt_packet_size(desc_edpt)) {
|
||||
break;
|
||||
}
|
||||
size_value++;
|
||||
}
|
||||
|
||||
// unsupported endpoint size
|
||||
if ( size_value == 7 && tu_edpt_packet_size(desc_edpt) != 1023 ) return false;
|
||||
if ( size_value == 7 && tu_edpt_packet_size(desc_edpt) > 1023 ) return false;
|
||||
|
||||
bank->PCKSIZE.bit.SIZE = size_value;
|
||||
|
||||
@@ -242,6 +250,13 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
|
||||
return true;
|
||||
}
|
||||
|
||||
void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr) {
|
||||
(void) rhport;
|
||||
(void) ep_addr;
|
||||
|
||||
// TODO: implement if necessary?
|
||||
}
|
||||
|
||||
void dcd_edpt_close_all (uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
@@ -271,14 +286,14 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
|
||||
{
|
||||
bank->PCKSIZE.bit.MULTI_PACKET_SIZE = total_bytes;
|
||||
bank->PCKSIZE.bit.BYTE_COUNT = 0;
|
||||
ep->EPSTATUSCLR.reg |= USB_DEVICE_EPSTATUSCLR_BK0RDY;
|
||||
ep->EPINTFLAG.reg |= USB_DEVICE_EPINTFLAG_TRFAIL0;
|
||||
ep->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_BK0RDY;
|
||||
ep->EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRFAIL0;
|
||||
} else
|
||||
{
|
||||
bank->PCKSIZE.bit.MULTI_PACKET_SIZE = 0;
|
||||
bank->PCKSIZE.bit.BYTE_COUNT = total_bytes;
|
||||
ep->EPSTATUSSET.reg |= USB_DEVICE_EPSTATUSSET_BK1RDY;
|
||||
ep->EPINTFLAG.reg |= USB_DEVICE_EPINTFLAG_TRFAIL1;
|
||||
ep->EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_BK1RDY;
|
||||
ep->EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRFAIL1;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -210,6 +210,14 @@ void dcd_disconnect(uint8_t rhport)
|
||||
UDP->UDP_TXVC = UDP_TXVC_TXVDIS_Msk;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
# define USE_SOF 0
|
||||
#endif
|
||||
|
||||
// Dual bank can imporve performance, but need 2 times bigger packet buffer
|
||||
// Dual bank can improve performance, but need 2 times bigger packet buffer
|
||||
// As SAM7x has only 4KB packet buffer, use with caution !
|
||||
// Enable in FS mode as packets are smaller
|
||||
#ifndef USE_DUAL_BANK
|
||||
@@ -191,6 +191,14 @@ void dcd_disconnect(uint8_t rhport)
|
||||
USB_REG->DEVCTRL &=~(DEVCTRL_ADDEN | DEVCTRL_UADD);
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
static tusb_speed_t get_speed(void)
|
||||
{
|
||||
switch (USB_REG->SR & SR_SPEED) {
|
||||
@@ -636,7 +644,7 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
|
||||
}
|
||||
__set_PRIMASK(irq_state);
|
||||
|
||||
// Here a ZLP has been recieved
|
||||
// Here a ZLP has been received
|
||||
// and the DMA transfer must be not started.
|
||||
// It is the end of transfer
|
||||
return false;
|
||||
@@ -726,7 +734,7 @@ bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16
|
||||
}
|
||||
__set_PRIMASK(irq_state);
|
||||
|
||||
// Here a ZLP has been recieved
|
||||
// Here a ZLP has been received
|
||||
// and the DMA transfer must be not started.
|
||||
// It is the end of transfer
|
||||
return false;
|
||||
|
||||
@@ -305,6 +305,14 @@ void dcd_disconnect(uint8_t rhport)
|
||||
USB_OTG_FS->CTL = 0;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
#if CFG_TUD_ENABLED && CFG_TUSB_MCU == OPT_MCU_NRF5X
|
||||
|
||||
#include <stdatomic.h>
|
||||
#include "nrf.h"
|
||||
#include "nrf_clock.h"
|
||||
#include "nrf_power.h"
|
||||
@@ -72,6 +73,7 @@ typedef struct
|
||||
// nRF will auto accept OUT packet after DMA is done
|
||||
// indicate packet is already ACK
|
||||
volatile bool data_received;
|
||||
volatile bool started;
|
||||
|
||||
// Set to true when data was transferred from RAM to ISO IN output buffer.
|
||||
// New data can be put in ISO IN output buffer after SOF.
|
||||
@@ -79,9 +81,6 @@ typedef struct
|
||||
|
||||
} xfer_td_t;
|
||||
|
||||
static osal_mutex_def_t dcd_mutex_def;
|
||||
static osal_mutex_t dcd_mutex;
|
||||
|
||||
// Data for managing dcd
|
||||
static struct
|
||||
{
|
||||
@@ -90,7 +89,7 @@ static struct
|
||||
xfer_td_t xfer[EP_CBI_COUNT + 1][2];
|
||||
|
||||
// nRF can only carry one DMA at a time, this is used to guard the access to EasyDMA
|
||||
volatile bool dma_running;
|
||||
atomic_bool dma_running;
|
||||
}_dcd;
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
@@ -121,8 +120,6 @@ TU_ATTR_ALWAYS_INLINE static inline bool is_in_isr(void)
|
||||
// helper to start DMA
|
||||
static void start_dma(volatile uint32_t* reg_startep)
|
||||
{
|
||||
_dcd.dma_running = true;
|
||||
|
||||
(*reg_startep) = 1;
|
||||
__ISB(); __DSB();
|
||||
|
||||
@@ -131,50 +128,18 @@ static void start_dma(volatile uint32_t* reg_startep)
|
||||
// Therefore dma_pending is corrected right away
|
||||
if ( (reg_startep == &NRF_USBD->TASKS_EP0STATUS) || (reg_startep == &NRF_USBD->TASKS_EP0RCVOUT) )
|
||||
{
|
||||
_dcd.dma_running = false;
|
||||
atomic_flag_clear(&_dcd.dma_running);
|
||||
}
|
||||
}
|
||||
|
||||
// only 1 EasyDMA can be active at any time
|
||||
// TODO use Cortex M4 LDREX and STREX command (atomic) to have better mutex access to EasyDMA
|
||||
// since current implementation does not 100% guarded against race condition
|
||||
static void edpt_dma_start(volatile uint32_t* reg_startep)
|
||||
{
|
||||
// Called in critical section i.e within USB ISR, or USB/Global interrupt disabled
|
||||
if ( is_in_isr() || __get_PRIMASK() || !NVIC_GetEnableIRQ(USBD_IRQn) )
|
||||
if ( atomic_flag_test_and_set(&_dcd.dma_running) )
|
||||
{
|
||||
if (_dcd.dma_running)
|
||||
{
|
||||
//use usbd task to defer later
|
||||
usbd_defer_func((osal_task_func_t) edpt_dma_start, (void*) (uintptr_t) reg_startep, true);
|
||||
}else
|
||||
{
|
||||
start_dma(reg_startep);
|
||||
}
|
||||
usbd_defer_func((osal_task_func_t) edpt_dma_start, (void*) (uintptr_t) reg_startep, true);
|
||||
}else
|
||||
{
|
||||
// Called in non-critical thread-mode, should be 99% of the time.
|
||||
// Should be safe to blocking wait until previous DMA transfer complete
|
||||
uint8_t const rhport = 0;
|
||||
bool started = false;
|
||||
osal_mutex_lock(dcd_mutex, OSAL_TIMEOUT_WAIT_FOREVER);
|
||||
while(!started)
|
||||
{
|
||||
// LDREX/STREX may be needed in form of std atomic (required C11) or
|
||||
// use osal mutex to guard against multiple core MCUs such as nRF53
|
||||
dcd_int_disable(rhport);
|
||||
|
||||
if ( !_dcd.dma_running )
|
||||
{
|
||||
start_dma(reg_startep);
|
||||
started = true;
|
||||
}
|
||||
|
||||
dcd_int_enable(rhport);
|
||||
|
||||
// osal_yield();
|
||||
}
|
||||
osal_mutex_unlock(dcd_mutex);
|
||||
start_dma(reg_startep);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,7 +147,7 @@ static void edpt_dma_start(volatile uint32_t* reg_startep)
|
||||
static void edpt_dma_end(void)
|
||||
{
|
||||
TU_ASSERT(_dcd.dma_running, );
|
||||
_dcd.dma_running = false;
|
||||
atomic_flag_clear(&_dcd.dma_running);
|
||||
}
|
||||
|
||||
// helper getting td
|
||||
@@ -191,12 +156,26 @@ static inline xfer_td_t* get_td(uint8_t epnum, uint8_t dir)
|
||||
return &_dcd.xfer[epnum][dir];
|
||||
}
|
||||
|
||||
static void xact_out_dma(uint8_t epnum);
|
||||
// Function wraps xact_out_dma which wants uint8_t while usbd_defer_func wants void (*)(void *)
|
||||
static void xact_out_dma_wrapper(void *epnum)
|
||||
{
|
||||
xact_out_dma((uint8_t)((uintptr_t)epnum));
|
||||
}
|
||||
|
||||
// Start DMA to move data from Endpoint -> RAM
|
||||
static void xact_out_dma(uint8_t epnum)
|
||||
{
|
||||
xfer_td_t* xfer = get_td(epnum, TUSB_DIR_OUT);
|
||||
uint32_t xact_len;
|
||||
|
||||
// DMA can't be active during read of SIZE.EPOUT or SIZE.ISOOUT, so try to lock,
|
||||
// If already running defer call regardless if it was called from ISR or task,
|
||||
if ( atomic_flag_test_and_set(&_dcd.dma_running) )
|
||||
{
|
||||
usbd_defer_func((osal_task_func_t)xact_out_dma_wrapper, (void *)(uint32_t)epnum, is_in_isr());
|
||||
return;
|
||||
}
|
||||
if (epnum == EP_ISO_NUM)
|
||||
{
|
||||
xact_len = NRF_USBD->SIZE.ISOOUT;
|
||||
@@ -204,6 +183,7 @@ static void xact_out_dma(uint8_t epnum)
|
||||
if (xact_len & USBD_SIZE_ISOOUT_ZERO_Msk)
|
||||
{
|
||||
xact_len = 0;
|
||||
atomic_flag_clear(&_dcd.dma_running);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -211,7 +191,7 @@ static void xact_out_dma(uint8_t epnum)
|
||||
NRF_USBD->ISOOUT.PTR = (uint32_t) xfer->buffer;
|
||||
NRF_USBD->ISOOUT.MAXCNT = xact_len;
|
||||
|
||||
edpt_dma_start(&NRF_USBD->TASKS_STARTISOOUT);
|
||||
start_dma(&NRF_USBD->TASKS_STARTISOOUT);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -223,7 +203,7 @@ static void xact_out_dma(uint8_t epnum)
|
||||
NRF_USBD->EPOUT[epnum].PTR = (uint32_t) xfer->buffer;
|
||||
NRF_USBD->EPOUT[epnum].MAXCNT = xact_len;
|
||||
|
||||
edpt_dma_start(&NRF_USBD->TASKS_STARTEPOUT[epnum]);
|
||||
start_dma(&NRF_USBD->TASKS_STARTEPOUT[epnum]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,7 +228,6 @@ static void xact_in_dma(uint8_t epnum)
|
||||
void dcd_init (uint8_t rhport)
|
||||
{
|
||||
TU_LOG1("dcd init\r\n");
|
||||
dcd_mutex = osal_mutex_create(&dcd_mutex_def);
|
||||
(void) rhport;
|
||||
}
|
||||
|
||||
@@ -307,6 +286,14 @@ void dcd_connect(uint8_t rhport)
|
||||
NRF_USBD->USBPULLUP = 1;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
@@ -451,6 +438,7 @@ void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr)
|
||||
// When both ISO endpoint are close there is no need for SOF any more.
|
||||
if (_dcd.xfer[EP_ISO_NUM][TUSB_DIR_IN].mps + _dcd.xfer[EP_ISO_NUM][TUSB_DIR_OUT].mps == 0) NRF_USBD->INTENCLR = USBD_INTENCLR_SOF_Msk;
|
||||
}
|
||||
_dcd.xfer[epnum][dir].started = false;
|
||||
__ISB(); __DSB();
|
||||
}
|
||||
|
||||
@@ -463,17 +451,10 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
|
||||
|
||||
xfer_td_t* xfer = get_td(epnum, dir);
|
||||
|
||||
if (!is_in_isr()) {
|
||||
osal_mutex_lock(dcd_mutex, OSAL_TIMEOUT_WAIT_FOREVER);
|
||||
dcd_int_disable(rhport);
|
||||
}
|
||||
TU_ASSERT(!xfer->started);
|
||||
xfer->buffer = buffer;
|
||||
xfer->total_len = total_bytes;
|
||||
xfer->actual_len = 0;
|
||||
if (!is_in_isr()) {
|
||||
dcd_int_enable(rhport);
|
||||
osal_mutex_unlock(dcd_mutex);
|
||||
}
|
||||
|
||||
// Control endpoint with zero-length packet and opposite direction to 1st request byte --> status stage
|
||||
bool const control_status = (epnum == 0 && total_bytes == 0 && dir != tu_edpt_dir(NRF_USBD->BMREQUESTTYPE));
|
||||
@@ -488,13 +469,20 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
|
||||
}
|
||||
else if ( dir == TUSB_DIR_OUT )
|
||||
{
|
||||
xfer->started = true;
|
||||
if ( epnum == 0 )
|
||||
{
|
||||
// Accept next Control Out packet. TASKS_EP0RCVOUT also require EasyDMA
|
||||
edpt_dma_start(&NRF_USBD->TASKS_EP0RCVOUT);
|
||||
}else
|
||||
{
|
||||
if ( xfer->data_received && xfer->total_len > xfer->actual_len)
|
||||
// started just set, it could start DMA transfer if interrupt was trigger after this line
|
||||
// code only needs to start transfer (from Endpoint to RAM) when data_received was set
|
||||
// before started was set. If started is NOT set but data_received is, it means that
|
||||
// current transfer was already finished and next data is already present in endpoint and
|
||||
// can be consumed by future transfer
|
||||
__ISB(); __DSB();
|
||||
if ( xfer->data_received && xfer->started )
|
||||
{
|
||||
// Data is already received previously
|
||||
// start DMA to copy to SRAM
|
||||
@@ -777,7 +765,7 @@ void dcd_int_handler(uint8_t rhport)
|
||||
if ( tu_bit_test(int_status, USBD_INTEN_ENDEPOUT0_Pos+epnum))
|
||||
{
|
||||
xfer_td_t* xfer = get_td(epnum, TUSB_DIR_OUT);
|
||||
uint8_t const xact_len = NRF_USBD->EPOUT[epnum].AMOUNT;
|
||||
uint16_t const xact_len = NRF_USBD->EPOUT[epnum].AMOUNT;
|
||||
|
||||
xfer->buffer += xact_len;
|
||||
xfer->actual_len += xact_len;
|
||||
@@ -796,7 +784,9 @@ void dcd_int_handler(uint8_t rhport)
|
||||
}
|
||||
}else
|
||||
{
|
||||
TU_ASSERT(xfer->started,);
|
||||
xfer->total_len = xfer->actual_len;
|
||||
xfer->started = false;
|
||||
|
||||
// CBI OUT complete
|
||||
dcd_event_xfer_complete(0, epnum, xfer->actual_len, XFER_RESULT_SUCCESS, true);
|
||||
@@ -849,7 +839,7 @@ void dcd_int_handler(uint8_t rhport)
|
||||
{
|
||||
xfer_td_t* xfer = get_td(epnum, TUSB_DIR_OUT);
|
||||
|
||||
if (xfer->actual_len < xfer->total_len)
|
||||
if ( xfer->started && xfer->actual_len < xfer->total_len )
|
||||
{
|
||||
xact_out_dma(epnum);
|
||||
}else
|
||||
|
||||
@@ -497,4 +497,12 @@ void dcd_connect(uint8_t rhport)
|
||||
usb_attach();
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -551,4 +551,12 @@ void dcd_connect(uint8_t rhport)
|
||||
usb_attach();
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -181,7 +181,7 @@ static void dcd_userEP_in_xfer(struct xfer_ctl_t *xfer, USBD_EP_T *ep)
|
||||
ep->EPINTEN = USBD_EPINTEN_TXPKIEN_Msk;
|
||||
}
|
||||
|
||||
/* provided buffers are thankfully 32-bit aligned, allowing most data to be transfered as 32-bit */
|
||||
/* provided buffers are thankfully 32-bit aligned, allowing most data to be transferred as 32-bit */
|
||||
#if 0 // TODO support dcd_edpt_xfer_fifo API
|
||||
if (xfer->ff)
|
||||
{
|
||||
@@ -720,4 +720,12 @@ void dcd_connect(uint8_t rhport)
|
||||
usb_attach();
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -328,6 +328,14 @@ void dcd_disconnect(uint8_t rhport)
|
||||
KHCI->CONTROL &= ~USB_CONTROL_DPPULLUPNONOTG_MASK;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
@@ -228,6 +228,14 @@ void dcd_disconnect(uint8_t rhport)
|
||||
sie_write(SIE_CMDCODE_DEVICE_STATUS, 1, 0);
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// CONTROL HELPER
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
@@ -78,8 +78,10 @@ typedef struct {
|
||||
|
||||
// Max nbytes for each control/bulk/interrupt transfer
|
||||
enum {
|
||||
NBYTES_CBI_FULLSPEED_MAX = 64,
|
||||
NBYTES_CBI_HIGHSPEED_MAX = 32767 // can be up to all 15-bit, but only tested with 4096
|
||||
NBYTES_ISO_FS_MAX = 1023, // FS ISO
|
||||
NBYTES_ISO_HS_MAX = 1024, // HS ISO
|
||||
NBYTES_CBI_FS_MAX = 64, // FS control/bulk/interrupt
|
||||
NBYTES_CBI_HS_MAX = 32767 // can be up to all 15-bit, but only tested with 4096
|
||||
};
|
||||
|
||||
enum {
|
||||
@@ -112,6 +114,8 @@ enum {
|
||||
typedef union TU_ATTR_PACKED
|
||||
{
|
||||
// Full and High speed has different bit layout for buffer_offset and nbytes
|
||||
// TODO FS/HS layout depends on the max speed of controller e.g
|
||||
// lpc55s69 PORT0 is only FS but actually has the same layout as HS on port1
|
||||
|
||||
// Buffer (aligned 64) = DATABUFSTART [31:22] | buffer_offset [21:6]
|
||||
volatile struct {
|
||||
@@ -175,6 +179,9 @@ typedef struct
|
||||
// Use CFG_TUSB_MEM_SECTION to place it accordingly.
|
||||
CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(256) static dcd_data_t _dcd;
|
||||
|
||||
// Dummy buffer to fix ZLPs overwriting the buffer (probably an USB/DMA controller bug)
|
||||
CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(64) static uint8_t dummy[8];
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Multiple Controllers
|
||||
//--------------------------------------------------------------------+
|
||||
@@ -225,8 +232,36 @@ static inline uint8_t ep_addr2id(uint8_t ep_addr)
|
||||
//--------------------------------------------------------------------+
|
||||
// CONTROLLER API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
static void prepare_setup_packet(uint8_t rhport)
|
||||
{
|
||||
if (_dcd_controller[rhport].max_speed == TUSB_SPEED_FULL )
|
||||
{
|
||||
_dcd.ep[0][1].buffer_fs.offset = get_buf_offset(_dcd.setup_packet);
|
||||
}else
|
||||
{
|
||||
_dcd.ep[0][1].buffer_hs.offset = get_buf_offset(_dcd.setup_packet);
|
||||
}
|
||||
}
|
||||
|
||||
static void edpt_reset(uint8_t rhport, uint8_t ep_id)
|
||||
{
|
||||
(void) rhport;
|
||||
tu_memclr(&_dcd.ep[ep_id], sizeof(_dcd.ep[ep_id]));
|
||||
}
|
||||
|
||||
static void edpt_reset_all(uint8_t rhport)
|
||||
{
|
||||
for (uint8_t ep_id = 0; ep_id < 2*_dcd_controller[rhport].ep_pairs; ++ep_id)
|
||||
{
|
||||
edpt_reset(rhport, ep_id);
|
||||
}
|
||||
prepare_setup_packet(rhport);
|
||||
}
|
||||
void dcd_init(uint8_t rhport)
|
||||
{
|
||||
edpt_reset_all(rhport);
|
||||
|
||||
dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
|
||||
|
||||
dcd_reg->EPLISTSTART = (uint32_t) _dcd.ep;
|
||||
@@ -277,6 +312,14 @@ void dcd_disconnect(uint8_t rhport)
|
||||
dcd_reg->DEVCMDSTAT &= ~CMDSTAT_DEVICE_CONNECT_MASK;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// DCD Endpoint Port
|
||||
//--------------------------------------------------------------------+
|
||||
@@ -302,18 +345,13 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
|
||||
|
||||
bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
|
||||
{
|
||||
(void) rhport;
|
||||
|
||||
// TODO not support ISO yet
|
||||
TU_VERIFY(p_endpoint_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS);
|
||||
|
||||
//------------- Prepare Queue Head -------------//
|
||||
uint8_t ep_id = ep_addr2id(p_endpoint_desc->bEndpointAddress);
|
||||
|
||||
// Check if endpoint is available
|
||||
TU_ASSERT( _dcd.ep[ep_id][0].disable && _dcd.ep[ep_id][1].disable );
|
||||
|
||||
tu_memclr(_dcd.ep[ep_id], 2*sizeof(ep_cmd_sts_t));
|
||||
edpt_reset(rhport, ep_id);
|
||||
_dcd.ep[ep_id][0].is_iso = (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS);
|
||||
|
||||
// Enable EP interrupt
|
||||
@@ -325,19 +363,20 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
|
||||
|
||||
void dcd_edpt_close_all (uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
// TODO implement dcd_edpt_close_all()
|
||||
for (uint8_t ep_id = 0; ep_id < 2*_dcd_controller[rhport].ep_pairs; ++ep_id)
|
||||
{
|
||||
_dcd.ep[ep_id][0].active = _dcd.ep[ep_id][0].active = 0; // TODO proper way is to EPSKIP then wait ep[][].active then write ep[][].disable (see table 778 in LPC55S69 Use Manual)
|
||||
_dcd.ep[ep_id][0].disable = _dcd.ep[ep_id][1].disable = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void prepare_setup_packet(uint8_t rhport)
|
||||
void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
|
||||
{
|
||||
if (_dcd_controller[rhport].max_speed == TUSB_SPEED_FULL )
|
||||
{
|
||||
_dcd.ep[0][1].buffer_fs.offset = get_buf_offset(_dcd.setup_packet);;
|
||||
}else
|
||||
{
|
||||
_dcd.ep[0][1].buffer_hs.offset = get_buf_offset(_dcd.setup_packet);;
|
||||
}
|
||||
(void) rhport;
|
||||
|
||||
uint8_t ep_id = ep_addr2id(ep_addr);
|
||||
_dcd.ep[ep_id][0].active = _dcd.ep[ep_id][0].active = 0; // TODO proper way is to EPSKIP then wait ep[][].active then write ep[][].disable (see table 778 in LPC55S69 Use Manual)
|
||||
_dcd.ep[ep_id][0].disable = _dcd.ep[ep_id][1].disable = 1;
|
||||
}
|
||||
|
||||
static void prepare_ep_xfer(uint8_t rhport, uint8_t ep_id, uint16_t buf_offset, uint16_t total_bytes)
|
||||
@@ -346,13 +385,12 @@ static void prepare_ep_xfer(uint8_t rhport, uint8_t ep_id, uint16_t buf_offset,
|
||||
|
||||
if (_dcd_controller[rhport].max_speed == TUSB_SPEED_FULL )
|
||||
{
|
||||
// TODO ISO FullSpeed can have up to 1023 bytes
|
||||
nbytes = tu_min16(total_bytes, NBYTES_CBI_FULLSPEED_MAX);
|
||||
nbytes = tu_min16(total_bytes, _dcd.ep[ep_id][0].is_iso ? NBYTES_ISO_FS_MAX : NBYTES_CBI_FS_MAX);
|
||||
_dcd.ep[ep_id][0].buffer_fs.offset = buf_offset;
|
||||
_dcd.ep[ep_id][0].buffer_fs.nbytes = nbytes;
|
||||
}else
|
||||
{
|
||||
nbytes = tu_min16(total_bytes, NBYTES_CBI_HIGHSPEED_MAX);
|
||||
nbytes = tu_min16(total_bytes, NBYTES_CBI_HS_MAX);
|
||||
_dcd.ep[ep_id][0].buffer_hs.offset = buf_offset;
|
||||
_dcd.ep[ep_id][0].buffer_hs.nbytes = nbytes;
|
||||
}
|
||||
@@ -364,13 +402,19 @@ static void prepare_ep_xfer(uint8_t rhport, uint8_t ep_id, uint16_t buf_offset,
|
||||
|
||||
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes)
|
||||
{
|
||||
(void) rhport;
|
||||
|
||||
uint8_t const ep_id = ep_addr2id(ep_addr);
|
||||
|
||||
tu_memclr(&_dcd.dma[ep_id], sizeof(xfer_dma_t));
|
||||
_dcd.dma[ep_id].total_bytes = total_bytes;
|
||||
|
||||
if (!buffer)
|
||||
{
|
||||
// Although having no data, ZLPs can cause buffer overwritten to zeroes.
|
||||
// Probably due to USB/DMA controller side effect/bug.
|
||||
// Assigned buffer offset to (valid) dummy to prevent overwriting to DATABUFSTART
|
||||
buffer = (uint8_t*)(uint32_t)dummy;
|
||||
}
|
||||
|
||||
prepare_ep_xfer(rhport, ep_id, get_buf_offset(buffer), total_bytes);
|
||||
|
||||
return true;
|
||||
@@ -382,15 +426,14 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t to
|
||||
static void bus_reset(uint8_t rhport)
|
||||
{
|
||||
tu_memclr(&_dcd, sizeof(dcd_data_t));
|
||||
edpt_reset_all(rhport);
|
||||
|
||||
// disable all non-control endpoints on bus reset
|
||||
for(uint8_t ep_id = 2; ep_id < 2*MAX_EP_PAIRS; ep_id++)
|
||||
// disable all endpoints as specified by LPC55S69 UM Table 778
|
||||
for(uint8_t ep_id = 0; ep_id < 2*MAX_EP_PAIRS; ep_id++)
|
||||
{
|
||||
_dcd.ep[ep_id][0].disable = _dcd.ep[ep_id][1].disable = 1;
|
||||
}
|
||||
|
||||
prepare_setup_packet(rhport);
|
||||
|
||||
dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
|
||||
|
||||
dcd_reg->EPINUSE = 0;
|
||||
@@ -498,23 +541,15 @@ void dcd_int_handler(uint8_t rhport)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO support suspend & resume
|
||||
if (cmd_stat & CMDSTAT_SUSPEND_CHANGE_MASK)
|
||||
{
|
||||
if (cmd_stat & CMDSTAT_DEVICE_SUSPEND_MASK)
|
||||
{ // suspend signal, bus idle for more than 3ms
|
||||
// Note: Host may delay more than 3 ms before and/or after bus reset before doing enumeration.
|
||||
if (cmd_stat & CMDSTAT_DEVICE_ADDR_MASK)
|
||||
{
|
||||
dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
|
||||
}
|
||||
// suspend signal, bus idle for more than 3ms
|
||||
// Note: Host may delay more than 3 ms before and/or after bus reset before doing enumeration.
|
||||
if (cmd_stat & CMDSTAT_DEVICE_ADDR_MASK)
|
||||
{
|
||||
dcd_event_bus_signal(rhport, (cmd_stat & CMDSTAT_DEVICE_SUSPEND_MASK) ? DCD_EVENT_SUSPEND : DCD_EVENT_RESUME, true);
|
||||
}
|
||||
}
|
||||
// else
|
||||
// { // resume signal
|
||||
// dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
// Setup Receive
|
||||
|
||||
@@ -27,14 +27,14 @@
|
||||
#include "tusb_option.h"
|
||||
|
||||
#if CFG_TUD_ENABLED && \
|
||||
(CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX)
|
||||
(CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT)
|
||||
|
||||
#warning "transdimenion is renamed to chipidea (portable/chipidea/ci_hs) to match other opensource naming convention such as linux. This file will be removed in the future, please update your makefile accordingly"
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INCLUDE
|
||||
//--------------------------------------------------------------------+
|
||||
#if CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX
|
||||
#if CFG_TUSB_MCU == OPT_MCU_MIMXRT
|
||||
#include "fsl_device_registers.h"
|
||||
#define INCLUDE_FSL_DEVICE_REGISTERS
|
||||
#else
|
||||
@@ -153,7 +153,7 @@ typedef struct
|
||||
const uint8_t ep_count; // Max bi-directional Endpoints
|
||||
}dcd_controller_t;
|
||||
|
||||
#if CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX
|
||||
#if CFG_TUSB_MCU == OPT_MCU_MIMXRT
|
||||
static const dcd_controller_t _dcd_controller[] =
|
||||
{
|
||||
// RT1010 and RT1020 only has 1 USB controller
|
||||
@@ -294,6 +294,14 @@ void dcd_disconnect(uint8_t rhport)
|
||||
dcd_reg->USBCMD &= ~USBCMD_RUN_STOP;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// HELPER
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
@@ -29,14 +29,14 @@
|
||||
// NXP Trans-Dimension USB IP implement EHCI for host functionality
|
||||
|
||||
#if CFG_TUH_ENABLED && \
|
||||
(CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX)
|
||||
(CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT)
|
||||
|
||||
#warning "transdimenion is renamed to chipidea (portable/chipidea/ci_hs) to match other opensource naming convention such as linux. This file will be removed in the future, please update your makefile accordingly"
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INCLUDE
|
||||
//--------------------------------------------------------------------+
|
||||
#if CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX
|
||||
#if CFG_TUSB_MCU == OPT_MCU_MIMXRT
|
||||
#include "fsl_device_registers.h"
|
||||
#else
|
||||
// LPCOpen for 18xx & 43xx
|
||||
@@ -58,7 +58,7 @@ typedef struct
|
||||
const IRQn_Type irqnum; // IRQ number
|
||||
}hcd_controller_t;
|
||||
|
||||
#if CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX
|
||||
#if CFG_TUSB_MCU == OPT_MCU_MIMXRT
|
||||
static const hcd_controller_t _hcd_controller[] =
|
||||
{
|
||||
// RT1010 and RT1020 only has 1 USB controller
|
||||
|
||||
@@ -165,7 +165,7 @@ bool hcd_init(uint8_t rhport)
|
||||
//------------- Data Structure init -------------//
|
||||
tu_memclr(&ohci_data, sizeof(ohci_data_t));
|
||||
for(uint8_t i=0; i<32; i++)
|
||||
{ // assign all interrupt pointes to period head ed
|
||||
{ // assign all interrupt pointers to period head ed
|
||||
ohci_data.hcca.interrupt_table[i] = (uint32_t) &ohci_data.period_head_ed;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
//--------------------------------------------------------------------+
|
||||
// OHCI CONFIGURATION & CONSTANTS
|
||||
//--------------------------------------------------------------------+
|
||||
#define HOST_HCD_XFER_INTERRUPT // TODO interrupt is used widely, should always be enalbed
|
||||
#define HOST_HCD_XFER_INTERRUPT // TODO interrupt is used widely, should always be enabled
|
||||
#define OHCI_PERIODIC_LIST (defined HOST_HCD_XFER_INTERRUPT || defined HOST_HCD_XFER_ISOCHRONOUS)
|
||||
|
||||
// TODO merge OHCI with EHCI
|
||||
|
||||
@@ -43,17 +43,25 @@
|
||||
#define RHPORT_OFFSET 1
|
||||
#define RHPORT_PIO(_x) ((_x)-RHPORT_OFFSET)
|
||||
|
||||
static pio_usb_configuration_t pio_host_config = PIO_USB_DEFAULT_CONFIG;
|
||||
static pio_usb_configuration_t pio_host_cfg = PIO_USB_DEFAULT_CONFIG;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// HCD API
|
||||
//--------------------------------------------------------------------+
|
||||
bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param)
|
||||
{
|
||||
(void) rhport;
|
||||
TU_VERIFY(cfg_id == TUH_CFGID_RPI_PIO_USB_CONFIGURATION);
|
||||
memcpy(&pio_host_cfg, cfg_param, sizeof(pio_usb_configuration_t));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hcd_init(uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
|
||||
// To run USB SOF interrupt in core1, call this init in core1
|
||||
pio_usb_host_init(&pio_host_config);
|
||||
pio_usb_host_init(&pio_host_cfg);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -55,7 +55,10 @@ static uint8_t *next_buffer_ptr;
|
||||
// USB_MAX_ENDPOINTS Endpoints, direction TUSB_DIR_OUT for out and TUSB_DIR_IN for in.
|
||||
static struct hw_endpoint hw_endpoints[USB_MAX_ENDPOINTS][2];
|
||||
|
||||
static inline struct hw_endpoint *hw_endpoint_get_by_num(uint8_t num, tusb_dir_t dir)
|
||||
// SOF may be used by remote wakeup as RESUME, this indicate whether SOF is actually used by usbd
|
||||
static bool _sof_enable = false;
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline struct hw_endpoint *hw_endpoint_get_by_num(uint8_t num, tusb_dir_t dir)
|
||||
{
|
||||
return &hw_endpoints[num][dir];
|
||||
}
|
||||
@@ -85,7 +88,7 @@ static void _hw_endpoint_alloc(struct hw_endpoint *ep, uint8_t transfer_type)
|
||||
uint dpram_offset = hw_data_offset(ep->hw_data_buf);
|
||||
hard_assert(hw_data_offset(next_buffer_ptr) <= USB_DPRAM_MAX);
|
||||
|
||||
pico_info(" Alloced %d bytes at offset 0x%x (0x%p)\r\n", size, dpram_offset, ep->hw_data_buf);
|
||||
pico_info(" Allocated %d bytes at offset 0x%x (0x%p)\r\n", size, dpram_offset, ep->hw_data_buf);
|
||||
|
||||
// Fill in endpoint control register with buffer offset
|
||||
uint32_t const reg = EP_CTRL_ENABLE_BITS | ((uint)transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | dpram_offset;
|
||||
@@ -185,7 +188,7 @@ static void hw_endpoint_xfer(uint8_t ep_addr, uint8_t *buffer, uint16_t total_by
|
||||
hw_endpoint_xfer_start(ep, buffer, total_bytes);
|
||||
}
|
||||
|
||||
static void hw_handle_buff_status(void)
|
||||
static void __tusb_irq_path_func(hw_handle_buff_status)(void)
|
||||
{
|
||||
uint32_t remaining_buffers = usb_hw->buf_status;
|
||||
pico_trace("buf_status = 0x%08x\n", remaining_buffers);
|
||||
@@ -214,7 +217,7 @@ static void hw_handle_buff_status(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void reset_ep0_pid(void)
|
||||
TU_ATTR_ALWAYS_INLINE static inline void reset_ep0_pid(void)
|
||||
{
|
||||
// If we have finished this transfer on EP0 set pid back to 1 for next
|
||||
// setup transfer. Also clear a stall in case
|
||||
@@ -226,7 +229,7 @@ static void reset_ep0_pid(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void reset_non_control_endpoints(void)
|
||||
static void __tusb_irq_path_func(reset_non_control_endpoints)(void)
|
||||
{
|
||||
// Disable all non-control
|
||||
for ( uint8_t i = 0; i < USB_MAX_ENDPOINTS-1; i++ )
|
||||
@@ -242,11 +245,21 @@ static void reset_non_control_endpoints(void)
|
||||
next_buffer_ptr = &usb_dpram->epx_data[0];
|
||||
}
|
||||
|
||||
static void dcd_rp2040_irq(void)
|
||||
static void __tusb_irq_path_func(dcd_rp2040_irq)(void)
|
||||
{
|
||||
uint32_t const status = usb_hw->ints;
|
||||
uint32_t handled = 0;
|
||||
|
||||
if (status & USB_INTF_DEV_SOF_BITS)
|
||||
{
|
||||
handled |= USB_INTF_DEV_SOF_BITS;
|
||||
|
||||
// disable SOF interrupt if it is used for RESUME in remote wakeup
|
||||
if (!_sof_enable) usb_hw_clear->inte = USB_INTS_DEV_SOF_BITS;
|
||||
|
||||
dcd_event_sof(0, usb_hw->sof_rd & USB_SOF_RD_BITS, true);
|
||||
}
|
||||
|
||||
// xfer events are handled before setup req. So if a transfer completes immediately
|
||||
// before closing the EP, the events will be delivered in same order.
|
||||
if (status & USB_INTS_BUFF_STATUS_BITS)
|
||||
@@ -357,7 +370,7 @@ void dcd_init (uint8_t rhport)
|
||||
usb_hw->pwr = USB_USB_PWR_VBUS_DETECT_BITS | USB_USB_PWR_VBUS_DETECT_OVERRIDE_EN_BITS;
|
||||
#endif
|
||||
|
||||
irq_set_exclusive_handler(USBCTRL_IRQ, dcd_rp2040_irq);
|
||||
irq_add_shared_handler(USBCTRL_IRQ, dcd_rp2040_irq, PICO_SHARED_IRQ_HANDLER_HIGHEST_ORDER_PRIORITY);
|
||||
|
||||
// Init control endpoints
|
||||
tu_memclr(hw_endpoints[0], 2*sizeof(hw_endpoint_t));
|
||||
@@ -405,9 +418,13 @@ void dcd_set_address (__unused uint8_t rhport, __unused uint8_t dev_addr)
|
||||
|
||||
void dcd_remote_wakeup(__unused uint8_t rhport)
|
||||
{
|
||||
pico_info("dcd_remote_wakeup %d\n", rhport);
|
||||
assert(rhport == 0);
|
||||
usb_hw_set->sie_ctrl = USB_SIE_CTRL_RESUME_BITS;
|
||||
pico_info("dcd_remote_wakeup %d\n", rhport);
|
||||
assert(rhport == 0);
|
||||
|
||||
// since RESUME interrupt is not triggered if we are the one initiate
|
||||
// briefly enable SOF to notify usbd when bus is ready
|
||||
usb_hw_set->inte = USB_INTS_DEV_SOF_BITS;
|
||||
usb_hw_set->sie_ctrl = USB_SIE_CTRL_RESUME_BITS;
|
||||
}
|
||||
|
||||
// disconnect by disabling internal pull-up resistor on D+/D-
|
||||
@@ -424,6 +441,21 @@ void dcd_connect(__unused uint8_t rhport)
|
||||
usb_hw_set->sie_ctrl = USB_SIE_CTRL_PULLUP_EN_BITS;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
|
||||
_sof_enable = en;
|
||||
|
||||
if (en)
|
||||
{
|
||||
usb_hw_set->inte = USB_INTS_DEV_SOF_BITS;
|
||||
}else
|
||||
{
|
||||
usb_hw_clear->inte = USB_INTS_DEV_SOF_BITS;
|
||||
}
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* DCD Endpoint port
|
||||
*------------------------------------------------------------------*/
|
||||
@@ -501,7 +533,7 @@ void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr)
|
||||
hw_endpoint_close(ep_addr);
|
||||
}
|
||||
|
||||
void dcd_int_handler(uint8_t rhport)
|
||||
void __tusb_irq_path_func(dcd_int_handler)(uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
dcd_rp2040_irq();
|
||||
|
||||
@@ -79,93 +79,103 @@ static struct hw_endpoint *get_dev_ep(uint8_t dev_addr, uint8_t ep_addr)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline uint8_t dev_speed(void)
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t dev_speed(void)
|
||||
{
|
||||
return (usb_hw->sie_status & USB_SIE_STATUS_SPEED_BITS) >> USB_SIE_STATUS_SPEED_LSB;
|
||||
return (usb_hw->sie_status & USB_SIE_STATUS_SPEED_BITS) >> USB_SIE_STATUS_SPEED_LSB;
|
||||
}
|
||||
|
||||
static bool need_pre(uint8_t dev_addr)
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool need_pre(uint8_t dev_addr)
|
||||
{
|
||||
// If this device is different to the speed of the root device
|
||||
// (i.e. is a low speed device on a full speed hub) then need pre
|
||||
return hcd_port_speed_get(0) != tuh_speed_get(dev_addr);
|
||||
// If this device is different to the speed of the root device
|
||||
// (i.e. is a low speed device on a full speed hub) then need pre
|
||||
return hcd_port_speed_get(0) != tuh_speed_get(dev_addr);
|
||||
}
|
||||
|
||||
static void hw_xfer_complete(struct hw_endpoint *ep, xfer_result_t xfer_result)
|
||||
static void __tusb_irq_path_func(hw_xfer_complete)(struct hw_endpoint *ep, xfer_result_t xfer_result)
|
||||
{
|
||||
// Mark transfer as done before we tell the tinyusb stack
|
||||
uint8_t dev_addr = ep->dev_addr;
|
||||
uint8_t ep_addr = ep->ep_addr;
|
||||
uint xferred_len = ep->xferred_len;
|
||||
hw_endpoint_reset_transfer(ep);
|
||||
hcd_event_xfer_complete(dev_addr, ep_addr, xferred_len, xfer_result, true);
|
||||
// Mark transfer as done before we tell the tinyusb stack
|
||||
uint8_t dev_addr = ep->dev_addr;
|
||||
uint8_t ep_addr = ep->ep_addr;
|
||||
uint xferred_len = ep->xferred_len;
|
||||
hw_endpoint_reset_transfer(ep);
|
||||
hcd_event_xfer_complete(dev_addr, ep_addr, xferred_len, xfer_result, true);
|
||||
}
|
||||
|
||||
static void _handle_buff_status_bit(uint bit, struct hw_endpoint *ep)
|
||||
static void __tusb_irq_path_func(_handle_buff_status_bit)(uint bit, struct hw_endpoint *ep)
|
||||
{
|
||||
usb_hw_clear->buf_status = bit;
|
||||
bool done = hw_endpoint_xfer_continue(ep);
|
||||
if (done)
|
||||
usb_hw_clear->buf_status = bit;
|
||||
// EP may have been stalled?
|
||||
assert(ep->active);
|
||||
bool done = hw_endpoint_xfer_continue(ep);
|
||||
if ( done )
|
||||
{
|
||||
hw_xfer_complete(ep, XFER_RESULT_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
static void __tusb_irq_path_func(hw_handle_buff_status)(void)
|
||||
{
|
||||
uint32_t remaining_buffers = usb_hw->buf_status;
|
||||
pico_trace("buf_status 0x%08x\n", remaining_buffers);
|
||||
|
||||
// Check EPX first
|
||||
uint bit = 0b1;
|
||||
if ( remaining_buffers & bit )
|
||||
{
|
||||
remaining_buffers &= ~bit;
|
||||
struct hw_endpoint * ep = &epx;
|
||||
|
||||
uint32_t ep_ctrl = *ep->endpoint_control;
|
||||
if ( ep_ctrl & EP_CTRL_DOUBLE_BUFFERED_BITS )
|
||||
{
|
||||
hw_xfer_complete(ep, XFER_RESULT_SUCCESS);
|
||||
TU_LOG(3, "Double Buffered: ");
|
||||
}
|
||||
}
|
||||
|
||||
static void hw_handle_buff_status(void)
|
||||
{
|
||||
uint32_t remaining_buffers = usb_hw->buf_status;
|
||||
pico_trace("buf_status 0x%08x\n", remaining_buffers);
|
||||
|
||||
// Check EPX first
|
||||
uint bit = 0b1;
|
||||
if (remaining_buffers & bit)
|
||||
else
|
||||
{
|
||||
TU_LOG(3, "Single Buffered: ");
|
||||
}
|
||||
TU_LOG_HEX(3, ep_ctrl);
|
||||
|
||||
_handle_buff_status_bit(bit, ep);
|
||||
}
|
||||
|
||||
// Check "interrupt" (asynchronous) endpoints for both IN and OUT
|
||||
for ( uint i = 1; i <= USB_HOST_INTERRUPT_ENDPOINTS && remaining_buffers; i++ )
|
||||
{
|
||||
// EPX is bit 0 & 1
|
||||
// IEP1 IN is bit 2
|
||||
// IEP1 OUT is bit 3
|
||||
// IEP2 IN is bit 4
|
||||
// IEP2 OUT is bit 5
|
||||
// IEP3 IN is bit 6
|
||||
// IEP3 OUT is bit 7
|
||||
// etc
|
||||
for ( uint j = 0; j < 2; j++ )
|
||||
{
|
||||
bit = 1 << (i * 2 + j);
|
||||
if ( remaining_buffers & bit )
|
||||
{
|
||||
remaining_buffers &= ~bit;
|
||||
struct hw_endpoint *ep = &epx;
|
||||
|
||||
uint32_t ep_ctrl = *ep->endpoint_control;
|
||||
if (ep_ctrl & EP_CTRL_DOUBLE_BUFFERED_BITS)
|
||||
{
|
||||
TU_LOG(3, "Double Buffered: ");
|
||||
}else
|
||||
{
|
||||
TU_LOG(3, "Single Buffered: ");
|
||||
}
|
||||
TU_LOG_HEX(3, ep_ctrl);
|
||||
|
||||
_handle_buff_status_bit(bit, ep);
|
||||
_handle_buff_status_bit(bit, &ep_pool[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check interrupt endpoints
|
||||
for (uint i = 1; i <= USB_HOST_INTERRUPT_ENDPOINTS && remaining_buffers; i++)
|
||||
{
|
||||
// EPX is bit 0
|
||||
// IEP1 is bit 2
|
||||
// IEP2 is bit 4
|
||||
// IEP3 is bit 6
|
||||
// etc
|
||||
bit = 1 << (i*2);
|
||||
|
||||
if (remaining_buffers & bit)
|
||||
{
|
||||
remaining_buffers &= ~bit;
|
||||
_handle_buff_status_bit(bit, &ep_pool[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (remaining_buffers)
|
||||
{
|
||||
panic("Unhandled buffer %d\n", remaining_buffers);
|
||||
}
|
||||
if ( remaining_buffers )
|
||||
{
|
||||
panic("Unhandled buffer %d\n", remaining_buffers);
|
||||
}
|
||||
}
|
||||
|
||||
static void hw_trans_complete(void)
|
||||
static void __tusb_irq_path_func(hw_trans_complete)(void)
|
||||
{
|
||||
if (usb_hw->sie_ctrl & USB_SIE_CTRL_SEND_SETUP_BITS)
|
||||
{
|
||||
pico_trace("Sent setup packet\n");
|
||||
struct hw_endpoint *ep = &epx;
|
||||
assert(ep->active);
|
||||
// Set transferred length to 8 for a setup packet
|
||||
ep->xferred_len = 8;
|
||||
hw_xfer_complete(ep, XFER_RESULT_SUCCESS);
|
||||
}
|
||||
else
|
||||
@@ -175,178 +185,196 @@ static void hw_trans_complete(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void hcd_rp2040_irq(void)
|
||||
static void __tusb_irq_path_func(hcd_rp2040_irq)(void)
|
||||
{
|
||||
uint32_t status = usb_hw->ints;
|
||||
uint32_t handled = 0;
|
||||
uint32_t status = usb_hw->ints;
|
||||
uint32_t handled = 0;
|
||||
|
||||
if (status & USB_INTS_HOST_CONN_DIS_BITS)
|
||||
if ( status & USB_INTS_HOST_CONN_DIS_BITS )
|
||||
{
|
||||
handled |= USB_INTS_HOST_CONN_DIS_BITS;
|
||||
|
||||
if ( dev_speed() )
|
||||
{
|
||||
handled |= USB_INTS_HOST_CONN_DIS_BITS;
|
||||
|
||||
if (dev_speed())
|
||||
{
|
||||
hcd_event_device_attach(RHPORT_NATIVE, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
hcd_event_device_remove(RHPORT_NATIVE, true);
|
||||
}
|
||||
|
||||
// Clear speed change interrupt
|
||||
usb_hw_clear->sie_status = USB_SIE_STATUS_SPEED_BITS;
|
||||
hcd_event_device_attach(RHPORT_NATIVE, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
hcd_event_device_remove(RHPORT_NATIVE, true);
|
||||
}
|
||||
|
||||
if (status & USB_INTS_BUFF_STATUS_BITS)
|
||||
{
|
||||
handled |= USB_INTS_BUFF_STATUS_BITS;
|
||||
TU_LOG(2, "Buffer complete\n");
|
||||
hw_handle_buff_status();
|
||||
}
|
||||
// Clear speed change interrupt
|
||||
usb_hw_clear->sie_status = USB_SIE_STATUS_SPEED_BITS;
|
||||
}
|
||||
|
||||
if (status & USB_INTS_TRANS_COMPLETE_BITS)
|
||||
{
|
||||
handled |= USB_INTS_TRANS_COMPLETE_BITS;
|
||||
usb_hw_clear->sie_status = USB_SIE_STATUS_TRANS_COMPLETE_BITS;
|
||||
TU_LOG(2, "Transfer complete\n");
|
||||
hw_trans_complete();
|
||||
}
|
||||
if ( status & USB_INTS_STALL_BITS )
|
||||
{
|
||||
// We have rx'd a stall from the device
|
||||
// NOTE THIS SHOULD HAVE PRIORITY OVER BUFF_STATUS
|
||||
// AND TRANS_COMPLETE as the stall is an alternative response
|
||||
// to one of those events
|
||||
pico_trace("Stall REC\n");
|
||||
handled |= USB_INTS_STALL_BITS;
|
||||
usb_hw_clear->sie_status = USB_SIE_STATUS_STALL_REC_BITS;
|
||||
hw_xfer_complete(&epx, XFER_RESULT_STALLED);
|
||||
}
|
||||
|
||||
if (status & USB_INTS_STALL_BITS)
|
||||
{
|
||||
// We have rx'd a stall from the device
|
||||
pico_trace("Stall REC\n");
|
||||
handled |= USB_INTS_STALL_BITS;
|
||||
usb_hw_clear->sie_status = USB_SIE_STATUS_STALL_REC_BITS;
|
||||
hw_xfer_complete(&epx, XFER_RESULT_STALLED);
|
||||
}
|
||||
if ( status & USB_INTS_BUFF_STATUS_BITS )
|
||||
{
|
||||
handled |= USB_INTS_BUFF_STATUS_BITS;
|
||||
TU_LOG(2, "Buffer complete\n");
|
||||
hw_handle_buff_status();
|
||||
}
|
||||
|
||||
if (status & USB_INTS_ERROR_RX_TIMEOUT_BITS)
|
||||
{
|
||||
handled |= USB_INTS_ERROR_RX_TIMEOUT_BITS;
|
||||
usb_hw_clear->sie_status = USB_SIE_STATUS_RX_TIMEOUT_BITS;
|
||||
}
|
||||
if ( status & USB_INTS_TRANS_COMPLETE_BITS )
|
||||
{
|
||||
handled |= USB_INTS_TRANS_COMPLETE_BITS;
|
||||
usb_hw_clear->sie_status = USB_SIE_STATUS_TRANS_COMPLETE_BITS;
|
||||
TU_LOG(2, "Transfer complete\n");
|
||||
hw_trans_complete();
|
||||
}
|
||||
|
||||
if (status & USB_INTS_ERROR_DATA_SEQ_BITS)
|
||||
{
|
||||
usb_hw_clear->sie_status = USB_SIE_STATUS_DATA_SEQ_ERROR_BITS;
|
||||
TU_LOG(3, " Seq Error: [0] = 0x%04u [1] = 0x%04x\r\n", tu_u32_low16(*epx.buffer_control), tu_u32_high16(*epx.buffer_control));
|
||||
panic("Data Seq Error \n");
|
||||
}
|
||||
if ( status & USB_INTS_ERROR_RX_TIMEOUT_BITS )
|
||||
{
|
||||
handled |= USB_INTS_ERROR_RX_TIMEOUT_BITS;
|
||||
usb_hw_clear->sie_status = USB_SIE_STATUS_RX_TIMEOUT_BITS;
|
||||
}
|
||||
|
||||
if (status ^ handled)
|
||||
{
|
||||
panic("Unhandled IRQ 0x%x\n", (uint) (status ^ handled));
|
||||
}
|
||||
if ( status & USB_INTS_ERROR_DATA_SEQ_BITS )
|
||||
{
|
||||
usb_hw_clear->sie_status = USB_SIE_STATUS_DATA_SEQ_ERROR_BITS;
|
||||
TU_LOG(3, " Seq Error: [0] = 0x%04u [1] = 0x%04x\r\n",
|
||||
tu_u32_low16(*epx.buffer_control),
|
||||
tu_u32_high16(*epx.buffer_control));
|
||||
panic("Data Seq Error \n");
|
||||
}
|
||||
|
||||
if ( status ^ handled )
|
||||
{
|
||||
panic("Unhandled IRQ 0x%x\n", (uint) (status ^ handled));
|
||||
}
|
||||
}
|
||||
|
||||
void __tusb_irq_path_func(hcd_int_handler)(uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
hcd_rp2040_irq();
|
||||
}
|
||||
|
||||
static struct hw_endpoint *_next_free_interrupt_ep(void)
|
||||
{
|
||||
struct hw_endpoint *ep = NULL;
|
||||
for (uint i = 1; i < TU_ARRAY_SIZE(ep_pool); i++)
|
||||
struct hw_endpoint * ep = NULL;
|
||||
for ( uint i = 1; i < TU_ARRAY_SIZE(ep_pool); i++ )
|
||||
{
|
||||
ep = &ep_pool[i];
|
||||
if ( !ep->configured )
|
||||
{
|
||||
ep = &ep_pool[i];
|
||||
if (!ep->configured)
|
||||
{
|
||||
// Will be configured by _hw_endpoint_init / _hw_endpoint_allocate
|
||||
ep->interrupt_num = i - 1;
|
||||
return ep;
|
||||
}
|
||||
// Will be configured by _hw_endpoint_init / _hw_endpoint_allocate
|
||||
ep->interrupt_num = (uint8_t) (i - 1);
|
||||
return ep;
|
||||
}
|
||||
return ep;
|
||||
}
|
||||
return ep;
|
||||
}
|
||||
|
||||
static struct hw_endpoint *_hw_endpoint_allocate(uint8_t transfer_type)
|
||||
{
|
||||
struct hw_endpoint *ep = NULL;
|
||||
struct hw_endpoint * ep = NULL;
|
||||
|
||||
if (transfer_type == TUSB_XFER_INTERRUPT)
|
||||
{
|
||||
ep = _next_free_interrupt_ep();
|
||||
pico_info("Allocate interrupt ep %d\n", ep->interrupt_num);
|
||||
assert(ep);
|
||||
ep->buffer_control = &usbh_dpram->int_ep_buffer_ctrl[ep->interrupt_num].ctrl;
|
||||
ep->endpoint_control = &usbh_dpram->int_ep_ctrl[ep->interrupt_num].ctrl;
|
||||
// 0 for epx (double buffered): TODO increase to 1024 for ISO
|
||||
// 2x64 for intep0
|
||||
// 3x64 for intep1
|
||||
// etc
|
||||
ep->hw_data_buf = &usbh_dpram->epx_data[64 * (ep->interrupt_num + 2)];
|
||||
}
|
||||
else
|
||||
{
|
||||
ep = &epx;
|
||||
ep->buffer_control = &usbh_dpram->epx_buf_ctrl;
|
||||
ep->endpoint_control = &usbh_dpram->epx_ctrl;
|
||||
ep->hw_data_buf = &usbh_dpram->epx_data[0];
|
||||
}
|
||||
if ( transfer_type != TUSB_XFER_CONTROL )
|
||||
{
|
||||
// Note: even though datasheet name these "Interrupt" endpoints. These are actually
|
||||
// "Asynchronous" endpoints and can be used for other type such as: Bulk (ISO need confirmation)
|
||||
ep = _next_free_interrupt_ep();
|
||||
pico_info("Allocate %s ep %d\n", tu_edpt_type_str(transfer_type), ep->interrupt_num);
|
||||
assert(ep);
|
||||
ep->buffer_control = &usbh_dpram->int_ep_buffer_ctrl[ep->interrupt_num].ctrl;
|
||||
ep->endpoint_control = &usbh_dpram->int_ep_ctrl[ep->interrupt_num].ctrl;
|
||||
// 0 for epx (double buffered): TODO increase to 1024 for ISO
|
||||
// 2x64 for intep0
|
||||
// 3x64 for intep1
|
||||
// etc
|
||||
ep->hw_data_buf = &usbh_dpram->epx_data[64 * (ep->interrupt_num + 2)];
|
||||
}
|
||||
else
|
||||
{
|
||||
ep = &epx;
|
||||
ep->buffer_control = &usbh_dpram->epx_buf_ctrl;
|
||||
ep->endpoint_control = &usbh_dpram->epx_ctrl;
|
||||
ep->hw_data_buf = &usbh_dpram->epx_data[0];
|
||||
}
|
||||
|
||||
return ep;
|
||||
return ep;
|
||||
}
|
||||
|
||||
static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t ep_addr, uint wMaxPacketSize, uint8_t transfer_type, uint8_t bmInterval)
|
||||
static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type, uint8_t bmInterval)
|
||||
{
|
||||
// Already has data buffer, endpoint control, and buffer control allocated at this point
|
||||
assert(ep->endpoint_control);
|
||||
assert(ep->buffer_control);
|
||||
assert(ep->hw_data_buf);
|
||||
// Already has data buffer, endpoint control, and buffer control allocated at this point
|
||||
assert(ep->endpoint_control);
|
||||
assert(ep->buffer_control);
|
||||
assert(ep->hw_data_buf);
|
||||
|
||||
uint8_t const num = tu_edpt_number(ep_addr);
|
||||
tusb_dir_t const dir = tu_edpt_dir(ep_addr);
|
||||
uint8_t const num = tu_edpt_number(ep_addr);
|
||||
tusb_dir_t const dir = tu_edpt_dir(ep_addr);
|
||||
|
||||
ep->ep_addr = ep_addr;
|
||||
ep->dev_addr = dev_addr;
|
||||
ep->ep_addr = ep_addr;
|
||||
ep->dev_addr = dev_addr;
|
||||
|
||||
// For host, IN to host == RX, anything else rx == false
|
||||
ep->rx = (dir == TUSB_DIR_IN);
|
||||
// For host, IN to host == RX, anything else rx == false
|
||||
ep->rx = (dir == TUSB_DIR_IN);
|
||||
|
||||
// Response to a setup packet on EP0 starts with pid of 1
|
||||
ep->next_pid = (num == 0 ? 1u : 0u);
|
||||
ep->wMaxPacketSize = wMaxPacketSize;
|
||||
ep->transfer_type = transfer_type;
|
||||
// Response to a setup packet on EP0 starts with pid of 1
|
||||
ep->next_pid = (num == 0 ? 1u : 0u);
|
||||
ep->wMaxPacketSize = wMaxPacketSize;
|
||||
ep->transfer_type = transfer_type;
|
||||
|
||||
pico_trace("hw_endpoint_init dev %d ep %d %s xfer %d\n", ep->dev_addr, tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)], ep->transfer_type);
|
||||
pico_trace("dev %d ep %d %s setup buffer @ 0x%p\n", ep->dev_addr, tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)], ep->hw_data_buf);
|
||||
uint dpram_offset = hw_data_offset(ep->hw_data_buf);
|
||||
// Bits 0-5 should be 0
|
||||
assert(!(dpram_offset & 0b111111));
|
||||
pico_trace("hw_endpoint_init dev %d ep %d %s xfer %d\n", ep->dev_addr, tu_edpt_number(ep->ep_addr),
|
||||
ep_dir_string[tu_edpt_dir(ep->ep_addr)], ep->transfer_type);
|
||||
pico_trace("dev %d ep %d %s setup buffer @ 0x%p\n", ep->dev_addr, tu_edpt_number(ep->ep_addr),
|
||||
ep_dir_string[tu_edpt_dir(ep->ep_addr)], ep->hw_data_buf);
|
||||
uint dpram_offset = hw_data_offset(ep->hw_data_buf);
|
||||
// Bits 0-5 should be 0
|
||||
assert(!(dpram_offset & 0b111111));
|
||||
|
||||
// Fill in endpoint control register with buffer offset
|
||||
uint32_t ep_reg = EP_CTRL_ENABLE_BITS
|
||||
| EP_CTRL_INTERRUPT_PER_BUFFER
|
||||
| (ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB)
|
||||
| dpram_offset;
|
||||
ep_reg |= bmInterval ? (bmInterval - 1) << EP_CTRL_HOST_INTERRUPT_INTERVAL_LSB : 0;
|
||||
*ep->endpoint_control = ep_reg;
|
||||
pico_trace("endpoint control (0x%p) <- 0x%x\n", ep->endpoint_control, ep_reg);
|
||||
ep->configured = true;
|
||||
// Fill in endpoint control register with buffer offset
|
||||
uint32_t ep_reg = EP_CTRL_ENABLE_BITS
|
||||
| EP_CTRL_INTERRUPT_PER_BUFFER
|
||||
| (ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB)
|
||||
| dpram_offset;
|
||||
if ( bmInterval )
|
||||
{
|
||||
ep_reg |= (uint32_t) ((bmInterval - 1) << EP_CTRL_HOST_INTERRUPT_INTERVAL_LSB);
|
||||
}
|
||||
*ep->endpoint_control = ep_reg;
|
||||
pico_trace("endpoint control (0x%p) <- 0x%x\n", ep->endpoint_control, ep_reg);
|
||||
ep->configured = true;
|
||||
|
||||
if (bmInterval)
|
||||
if ( ep != &epx )
|
||||
{
|
||||
// Endpoint has its own addr_endp and interrupt bits to be setup!
|
||||
// This is an interrupt/async endpoint. so need to set up ADDR_ENDP register with:
|
||||
// - device address
|
||||
// - endpoint number / direction
|
||||
// - preamble
|
||||
uint32_t reg = (uint32_t) (dev_addr | (num << USB_ADDR_ENDP1_ENDPOINT_LSB));
|
||||
|
||||
if ( dir == TUSB_DIR_OUT )
|
||||
{
|
||||
// This is an interrupt endpoint
|
||||
// so need to set up interrupt endpoint address control register with:
|
||||
// device address
|
||||
// endpoint number / direction
|
||||
// preamble
|
||||
uint32_t reg = dev_addr | (num << USB_ADDR_ENDP1_ENDPOINT_LSB);
|
||||
|
||||
if (dir == TUSB_DIR_OUT)
|
||||
{
|
||||
reg |= USB_ADDR_ENDP1_INTEP_DIR_BITS;
|
||||
}
|
||||
|
||||
if (need_pre(dev_addr))
|
||||
{
|
||||
reg |= USB_ADDR_ENDP1_INTEP_PREAMBLE_BITS;
|
||||
}
|
||||
usb_hw->int_ep_addr_ctrl[ep->interrupt_num] = reg;
|
||||
|
||||
// Finally, enable interrupt that endpoint
|
||||
usb_hw_set->int_ep_ctrl = 1 << (ep->interrupt_num + 1);
|
||||
|
||||
// If it's an interrupt endpoint we need to set up the buffer control
|
||||
// register
|
||||
reg |= USB_ADDR_ENDP1_INTEP_DIR_BITS;
|
||||
}
|
||||
|
||||
if ( need_pre(dev_addr) )
|
||||
{
|
||||
reg |= USB_ADDR_ENDP1_INTEP_PREAMBLE_BITS;
|
||||
}
|
||||
usb_hw->int_ep_addr_ctrl[ep->interrupt_num] = reg;
|
||||
|
||||
// Finally, enable interrupt that endpoint
|
||||
usb_hw_set->int_ep_ctrl = 1 << (ep->interrupt_num + 1);
|
||||
|
||||
// If it's an interrupt endpoint we need to set up the buffer control
|
||||
// register
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
@@ -354,39 +382,44 @@ static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t
|
||||
//--------------------------------------------------------------------+
|
||||
bool hcd_init(uint8_t rhport)
|
||||
{
|
||||
pico_trace("hcd_init %d\n", rhport);
|
||||
assert(rhport == 0);
|
||||
(void) rhport;
|
||||
pico_trace("hcd_init %d\n", rhport);
|
||||
assert(rhport == 0);
|
||||
|
||||
// Reset any previous state
|
||||
rp2040_usb_init();
|
||||
// Reset any previous state
|
||||
rp2040_usb_init();
|
||||
|
||||
// Force VBUS detect to always present, for now we assume vbus is always provided (without using VBUS En)
|
||||
usb_hw->pwr = USB_USB_PWR_VBUS_DETECT_BITS | USB_USB_PWR_VBUS_DETECT_OVERRIDE_EN_BITS;
|
||||
// Force VBUS detect to always present, for now we assume vbus is always provided (without using VBUS En)
|
||||
usb_hw->pwr = USB_USB_PWR_VBUS_DETECT_BITS | USB_USB_PWR_VBUS_DETECT_OVERRIDE_EN_BITS;
|
||||
|
||||
irq_set_exclusive_handler(USBCTRL_IRQ, hcd_rp2040_irq);
|
||||
// Remove shared irq if it was previously added so as not to fill up shared irq slots
|
||||
irq_remove_handler(USBCTRL_IRQ, hcd_rp2040_irq);
|
||||
|
||||
// clear epx and interrupt eps
|
||||
memset(&ep_pool, 0, sizeof(ep_pool));
|
||||
irq_add_shared_handler(USBCTRL_IRQ, hcd_rp2040_irq, PICO_SHARED_IRQ_HANDLER_HIGHEST_ORDER_PRIORITY);
|
||||
|
||||
// Enable in host mode with SOF / Keep alive on
|
||||
usb_hw->main_ctrl = USB_MAIN_CTRL_CONTROLLER_EN_BITS | USB_MAIN_CTRL_HOST_NDEVICE_BITS;
|
||||
usb_hw->sie_ctrl = SIE_CTRL_BASE;
|
||||
usb_hw->inte = USB_INTE_BUFF_STATUS_BITS |
|
||||
USB_INTE_HOST_CONN_DIS_BITS |
|
||||
USB_INTE_HOST_RESUME_BITS |
|
||||
USB_INTE_STALL_BITS |
|
||||
USB_INTE_TRANS_COMPLETE_BITS |
|
||||
USB_INTE_ERROR_RX_TIMEOUT_BITS |
|
||||
USB_INTE_ERROR_DATA_SEQ_BITS ;
|
||||
// clear epx and interrupt eps
|
||||
memset(&ep_pool, 0, sizeof(ep_pool));
|
||||
|
||||
return true;
|
||||
// Enable in host mode with SOF / Keep alive on
|
||||
usb_hw->main_ctrl = USB_MAIN_CTRL_CONTROLLER_EN_BITS | USB_MAIN_CTRL_HOST_NDEVICE_BITS;
|
||||
usb_hw->sie_ctrl = SIE_CTRL_BASE;
|
||||
usb_hw->inte = USB_INTE_BUFF_STATUS_BITS |
|
||||
USB_INTE_HOST_CONN_DIS_BITS |
|
||||
USB_INTE_HOST_RESUME_BITS |
|
||||
USB_INTE_STALL_BITS |
|
||||
USB_INTE_TRANS_COMPLETE_BITS |
|
||||
USB_INTE_ERROR_RX_TIMEOUT_BITS |
|
||||
USB_INTE_ERROR_DATA_SEQ_BITS ;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void hcd_port_reset(uint8_t rhport)
|
||||
{
|
||||
pico_trace("hcd_port_reset\n");
|
||||
assert(rhport == 0);
|
||||
// TODO: Nothing to do here yet. Perhaps need to reset some state?
|
||||
(void) rhport;
|
||||
pico_trace("hcd_port_reset\n");
|
||||
assert(rhport == 0);
|
||||
// TODO: Nothing to do here yet. Perhaps need to reset some state?
|
||||
}
|
||||
|
||||
void hcd_port_reset_end(uint8_t rhport)
|
||||
@@ -396,25 +429,28 @@ void hcd_port_reset_end(uint8_t rhport)
|
||||
|
||||
bool hcd_port_connect_status(uint8_t rhport)
|
||||
{
|
||||
pico_trace("hcd_port_connect_status\n");
|
||||
assert(rhport == 0);
|
||||
return usb_hw->sie_status & USB_SIE_STATUS_SPEED_BITS;
|
||||
(void) rhport;
|
||||
pico_trace("hcd_port_connect_status\n");
|
||||
assert(rhport == 0);
|
||||
return usb_hw->sie_status & USB_SIE_STATUS_SPEED_BITS;
|
||||
}
|
||||
|
||||
tusb_speed_t hcd_port_speed_get(uint8_t rhport)
|
||||
{
|
||||
assert(rhport == 0);
|
||||
// TODO: Should enumval this register
|
||||
switch (dev_speed())
|
||||
{
|
||||
case 1:
|
||||
return TUSB_SPEED_LOW;
|
||||
case 2:
|
||||
return TUSB_SPEED_FULL;
|
||||
default:
|
||||
panic("Invalid speed\n");
|
||||
return TUSB_SPEED_INVALID;
|
||||
}
|
||||
(void) rhport;
|
||||
assert(rhport == 0);
|
||||
|
||||
// TODO: Should enumval this register
|
||||
switch ( dev_speed() )
|
||||
{
|
||||
case 1:
|
||||
return TUSB_SPEED_LOW;
|
||||
case 2:
|
||||
return TUSB_SPEED_FULL;
|
||||
default:
|
||||
panic("Invalid speed\n");
|
||||
return TUSB_SPEED_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
// Close all opened endpoint belong to this device
|
||||
@@ -446,21 +482,23 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr)
|
||||
|
||||
uint32_t hcd_frame_number(uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
return usb_hw->sof_rd;
|
||||
(void) rhport;
|
||||
return usb_hw->sof_rd;
|
||||
}
|
||||
|
||||
void hcd_int_enable(uint8_t rhport)
|
||||
{
|
||||
assert(rhport == 0);
|
||||
irq_set_enabled(USBCTRL_IRQ, true);
|
||||
(void) rhport;
|
||||
assert(rhport == 0);
|
||||
irq_set_enabled(USBCTRL_IRQ, true);
|
||||
}
|
||||
|
||||
void hcd_int_disable(uint8_t rhport)
|
||||
{
|
||||
// todo we should check this is disabling from the correct core; note currently this is never called
|
||||
assert(rhport == 0);
|
||||
irq_set_enabled(USBCTRL_IRQ, false);
|
||||
(void) rhport;
|
||||
// todo we should check this is disabling from the correct core; note currently this is never called
|
||||
assert(rhport == 0);
|
||||
irq_set_enabled(USBCTRL_IRQ, false);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
@@ -469,120 +507,116 @@ void hcd_int_disable(uint8_t rhport)
|
||||
|
||||
bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) rhport;
|
||||
|
||||
pico_trace("hcd_edpt_open dev_addr %d, ep_addr %d\n", dev_addr, ep_desc->bEndpointAddress);
|
||||
pico_trace("hcd_edpt_open dev_addr %d, ep_addr %d\n", dev_addr, ep_desc->bEndpointAddress);
|
||||
|
||||
// Allocated differently based on if it's an interrupt endpoint or not
|
||||
struct hw_endpoint *ep = _hw_endpoint_allocate(ep_desc->bmAttributes.xfer);
|
||||
// Allocated differently based on if it's an interrupt endpoint or not
|
||||
struct hw_endpoint *ep = _hw_endpoint_allocate(ep_desc->bmAttributes.xfer);
|
||||
TU_ASSERT(ep);
|
||||
|
||||
_hw_endpoint_init(ep,
|
||||
dev_addr,
|
||||
ep_desc->bEndpointAddress,
|
||||
tu_edpt_packet_size(ep_desc),
|
||||
ep_desc->bmAttributes.xfer,
|
||||
ep_desc->bInterval);
|
||||
_hw_endpoint_init(ep,
|
||||
dev_addr,
|
||||
ep_desc->bEndpointAddress,
|
||||
tu_edpt_packet_size(ep_desc),
|
||||
ep_desc->bmAttributes.xfer,
|
||||
ep_desc->bInterval);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) rhport;
|
||||
|
||||
pico_trace("hcd_edpt_xfer dev_addr %d, ep_addr 0x%x, len %d\n", dev_addr, ep_addr, buflen);
|
||||
|
||||
uint8_t const ep_num = tu_edpt_number(ep_addr);
|
||||
tusb_dir_t const ep_dir = tu_edpt_dir(ep_addr);
|
||||
pico_trace("hcd_edpt_xfer dev_addr %d, ep_addr 0x%x, len %d\n", dev_addr, ep_addr, buflen);
|
||||
|
||||
// Get appropriate ep. Either EPX or interrupt endpoint
|
||||
struct hw_endpoint *ep = get_dev_ep(dev_addr, ep_addr);
|
||||
assert(ep);
|
||||
uint8_t const ep_num = tu_edpt_number(ep_addr);
|
||||
tusb_dir_t const ep_dir = tu_edpt_dir(ep_addr);
|
||||
|
||||
// Control endpoint can change direction 0x00 <-> 0x80
|
||||
if ( ep_addr != ep->ep_addr )
|
||||
{
|
||||
assert(ep_num == 0);
|
||||
// Get appropriate ep. Either EPX or interrupt endpoint
|
||||
struct hw_endpoint *ep = get_dev_ep(dev_addr, ep_addr);
|
||||
|
||||
// Direction has flipped on endpoint control so re init it but with same properties
|
||||
_hw_endpoint_init(ep, dev_addr, ep_addr, ep->wMaxPacketSize, ep->transfer_type, 0);
|
||||
}
|
||||
TU_ASSERT(ep);
|
||||
|
||||
// If a normal transfer (non-interrupt) then initiate using
|
||||
// sie ctrl registers. Otherwise interrupt ep registers should
|
||||
// already be configured
|
||||
if (ep == &epx) {
|
||||
hw_endpoint_xfer_start(ep, buffer, buflen);
|
||||
// EP should be inactive
|
||||
assert(!ep->active);
|
||||
|
||||
// That has set up buffer control, endpoint control etc
|
||||
// for host we have to initiate the transfer
|
||||
usb_hw->dev_addr_ctrl = dev_addr | (ep_num << USB_ADDR_ENDP_ENDPOINT_LSB);
|
||||
// Control endpoint can change direction 0x00 <-> 0x80
|
||||
if ( ep_addr != ep->ep_addr )
|
||||
{
|
||||
assert(ep_num == 0);
|
||||
|
||||
uint32_t flags = USB_SIE_CTRL_START_TRANS_BITS | SIE_CTRL_BASE |
|
||||
(ep_dir ? USB_SIE_CTRL_RECEIVE_DATA_BITS : USB_SIE_CTRL_SEND_DATA_BITS);
|
||||
// Set pre if we are a low speed device on full speed hub
|
||||
flags |= need_pre(dev_addr) ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0;
|
||||
// Direction has flipped on endpoint control so re init it but with same properties
|
||||
_hw_endpoint_init(ep, dev_addr, ep_addr, ep->wMaxPacketSize, ep->transfer_type, 0);
|
||||
}
|
||||
|
||||
usb_hw->sie_ctrl = flags;
|
||||
}else
|
||||
{
|
||||
hw_endpoint_xfer_start(ep, buffer, buflen);
|
||||
}
|
||||
// If a normal transfer (non-interrupt) then initiate using
|
||||
// sie ctrl registers. Otherwise interrupt ep registers should
|
||||
// already be configured
|
||||
if ( ep == &epx )
|
||||
{
|
||||
hw_endpoint_xfer_start(ep, buffer, buflen);
|
||||
|
||||
return true;
|
||||
// That has set up buffer control, endpoint control etc
|
||||
// for host we have to initiate the transfer
|
||||
usb_hw->dev_addr_ctrl = (uint32_t) (dev_addr | (ep_num << USB_ADDR_ENDP_ENDPOINT_LSB));
|
||||
|
||||
uint32_t flags = USB_SIE_CTRL_START_TRANS_BITS | SIE_CTRL_BASE |
|
||||
(ep_dir ? USB_SIE_CTRL_RECEIVE_DATA_BITS : USB_SIE_CTRL_SEND_DATA_BITS) |
|
||||
(need_pre(dev_addr) ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0);
|
||||
usb_hw->sie_ctrl = flags;
|
||||
}else
|
||||
{
|
||||
hw_endpoint_xfer_start(ep, buffer, buflen);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8])
|
||||
{
|
||||
(void) rhport;
|
||||
(void) rhport;
|
||||
|
||||
// Copy data into setup packet buffer
|
||||
memcpy((void*)&usbh_dpram->setup_packet[0], setup_packet, 8);
|
||||
// Copy data into setup packet buffer
|
||||
for ( uint8_t i = 0; i < 8; i++ )
|
||||
{
|
||||
usbh_dpram->setup_packet[i] = setup_packet[i];
|
||||
}
|
||||
|
||||
// Configure EP0 struct with setup info for the trans complete
|
||||
struct hw_endpoint *ep = _hw_endpoint_allocate(0);
|
||||
// Configure EP0 struct with setup info for the trans complete
|
||||
struct hw_endpoint * ep = _hw_endpoint_allocate(0);
|
||||
TU_ASSERT(ep);
|
||||
|
||||
// EP0 out
|
||||
_hw_endpoint_init(ep, dev_addr, 0x00, ep->wMaxPacketSize, 0, 0);
|
||||
assert(ep->configured);
|
||||
// EPX should be inactive
|
||||
assert(!ep->active);
|
||||
|
||||
ep->remaining_len = 8;
|
||||
ep->active = true;
|
||||
// EP0 out
|
||||
_hw_endpoint_init(ep, dev_addr, 0x00, ep->wMaxPacketSize, 0, 0);
|
||||
assert(ep->configured);
|
||||
|
||||
// Set device address
|
||||
usb_hw->dev_addr_ctrl = dev_addr;
|
||||
ep->remaining_len = 8;
|
||||
ep->active = true;
|
||||
|
||||
// Set pre if we are a low speed device on full speed hub
|
||||
uint32_t const flags = SIE_CTRL_BASE | USB_SIE_CTRL_SEND_SETUP_BITS | USB_SIE_CTRL_START_TRANS_BITS |
|
||||
(need_pre(dev_addr) ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0);
|
||||
// Set device address
|
||||
usb_hw->dev_addr_ctrl = dev_addr;
|
||||
|
||||
usb_hw->sie_ctrl = flags;
|
||||
// Set pre if we are a low speed device on full speed hub
|
||||
uint32_t const flags = SIE_CTRL_BASE | USB_SIE_CTRL_SEND_SETUP_BITS | USB_SIE_CTRL_START_TRANS_BITS |
|
||||
(need_pre(dev_addr) ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0);
|
||||
|
||||
return true;
|
||||
usb_hw->sie_ctrl = flags;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//bool hcd_edpt_busy(uint8_t dev_addr, uint8_t ep_addr)
|
||||
//{
|
||||
// // EPX is shared, so multiple device addresses and endpoint addresses share that
|
||||
// // so if any transfer is active on epx, we are busy. Interrupt endpoints have their own
|
||||
// // EPX so ep->active will only be busy if there is a pending transfer on that interrupt endpoint
|
||||
// // on that device
|
||||
// pico_trace("hcd_edpt_busy dev addr %d ep_addr 0x%x\n", dev_addr, ep_addr);
|
||||
// struct hw_endpoint *ep = get_dev_ep(dev_addr, ep_addr);
|
||||
// assert(ep);
|
||||
// bool busy = ep->active;
|
||||
// pico_trace("busy == %d\n", busy);
|
||||
// return busy;
|
||||
//}
|
||||
|
||||
bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr)
|
||||
{
|
||||
(void) dev_addr;
|
||||
(void) ep_addr;
|
||||
(void) dev_addr;
|
||||
(void) ep_addr;
|
||||
|
||||
panic("hcd_clear_stall");
|
||||
return true;
|
||||
panic("hcd_clear_stall");
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -38,7 +38,7 @@ const char *ep_dir_string[] = {
|
||||
"in",
|
||||
};
|
||||
|
||||
static inline void _hw_endpoint_lock_update(__unused struct hw_endpoint * ep, __unused int delta) {
|
||||
TU_ATTR_ALWAYS_INLINE static inline void _hw_endpoint_lock_update(__unused struct hw_endpoint * ep, __unused int delta) {
|
||||
// todo add critsec as necessary to prevent issues between worker and IRQ...
|
||||
// note that this is perhaps as simple as disabling IRQs because it would make
|
||||
// sense to have worker and IRQ on same core, however I think using critsec is about equivalent.
|
||||
@@ -47,6 +47,12 @@ static inline void _hw_endpoint_lock_update(__unused struct hw_endpoint * ep, __
|
||||
static void _hw_endpoint_xfer_sync(struct hw_endpoint *ep);
|
||||
static void _hw_endpoint_start_next_buffer(struct hw_endpoint *ep);
|
||||
|
||||
// if usb hardware is in host mode
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool is_host_mode(void)
|
||||
{
|
||||
return (usb_hw->main_ctrl & USB_MAIN_CTRL_HOST_NDEVICE_BITS) ? true : false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
//
|
||||
//--------------------------------------------------------------------+
|
||||
@@ -58,18 +64,22 @@ void rp2040_usb_init(void)
|
||||
unreset_block_wait(RESETS_RESET_USBCTRL_BITS);
|
||||
|
||||
// Clear any previous state just in case
|
||||
// TODO Suppress warning array-bounds with gcc11
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Warray-bounds"
|
||||
#if __GNUC__ > 6
|
||||
#pragma GCC diagnostic ignored "-Wstringop-overflow"
|
||||
#endif
|
||||
memset(usb_hw, 0, sizeof(*usb_hw));
|
||||
memset(usb_dpram, 0, sizeof(*usb_dpram));
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
// Mux the controller to the onboard usb phy
|
||||
usb_hw->muxing = USB_USB_MUXING_TO_PHY_BITS | USB_USB_MUXING_SOFTCON_BITS;
|
||||
|
||||
TU_LOG2_INT(sizeof(hw_endpoint_t));
|
||||
}
|
||||
|
||||
void hw_endpoint_reset_transfer(struct hw_endpoint *ep)
|
||||
void __tusb_irq_path_func(hw_endpoint_reset_transfer)(struct hw_endpoint *ep)
|
||||
{
|
||||
ep->active = false;
|
||||
ep->remaining_len = 0;
|
||||
@@ -77,20 +87,24 @@ void hw_endpoint_reset_transfer(struct hw_endpoint *ep)
|
||||
ep->user_buf = 0;
|
||||
}
|
||||
|
||||
void _hw_endpoint_buffer_control_update32(struct hw_endpoint *ep, uint32_t and_mask, uint32_t or_mask) {
|
||||
uint32_t value = 0;
|
||||
if (and_mask) {
|
||||
value = *ep->buffer_control & and_mask;
|
||||
}
|
||||
if (or_mask) {
|
||||
value |= or_mask;
|
||||
if (or_mask & USB_BUF_CTRL_AVAIL) {
|
||||
if (*ep->buffer_control & USB_BUF_CTRL_AVAIL) {
|
||||
panic("ep %d %s was already available", tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]);
|
||||
}
|
||||
*ep->buffer_control = value & ~USB_BUF_CTRL_AVAIL;
|
||||
// 12 cycle delay.. (should be good for 48*12Mhz = 576Mhz)
|
||||
// Don't need delay in host mode as host is in charge
|
||||
void __tusb_irq_path_func(_hw_endpoint_buffer_control_update32)(struct hw_endpoint *ep, uint32_t and_mask, uint32_t or_mask) {
|
||||
uint32_t value = 0;
|
||||
if ( and_mask )
|
||||
{
|
||||
value = *ep->buffer_control & and_mask;
|
||||
}
|
||||
if ( or_mask )
|
||||
{
|
||||
value |= or_mask;
|
||||
if ( or_mask & USB_BUF_CTRL_AVAIL )
|
||||
{
|
||||
if ( *ep->buffer_control & USB_BUF_CTRL_AVAIL )
|
||||
{
|
||||
panic("ep %d %s was already available", tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]);
|
||||
}
|
||||
*ep->buffer_control = value & ~USB_BUF_CTRL_AVAIL;
|
||||
// 12 cycle delay.. (should be good for 48*12Mhz = 576Mhz)
|
||||
// Don't need delay in host mode as host is in charge
|
||||
#if !CFG_TUH_ENABLED
|
||||
__asm volatile (
|
||||
"b 1f\n"
|
||||
@@ -102,13 +116,13 @@ void _hw_endpoint_buffer_control_update32(struct hw_endpoint *ep, uint32_t and_m
|
||||
"1:\n"
|
||||
: : : "memory");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
*ep->buffer_control = value;
|
||||
}
|
||||
*ep->buffer_control = value;
|
||||
}
|
||||
|
||||
// prepare buffer, return buffer control
|
||||
static uint32_t prepare_ep_buffer(struct hw_endpoint *ep, uint8_t buf_id)
|
||||
static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint *ep, uint8_t buf_id)
|
||||
{
|
||||
uint16_t const buflen = tu_min16(ep->remaining_len, ep->wMaxPacketSize);
|
||||
ep->remaining_len = (uint16_t)(ep->remaining_len - buflen);
|
||||
@@ -143,17 +157,21 @@ static uint32_t prepare_ep_buffer(struct hw_endpoint *ep, uint8_t buf_id)
|
||||
}
|
||||
|
||||
// Prepare buffer control register value
|
||||
static void _hw_endpoint_start_next_buffer(struct hw_endpoint *ep)
|
||||
static void __tusb_irq_path_func(_hw_endpoint_start_next_buffer)(struct hw_endpoint *ep)
|
||||
{
|
||||
uint32_t ep_ctrl = *ep->endpoint_control;
|
||||
|
||||
// always compute and start with buffer 0
|
||||
uint32_t buf_ctrl = prepare_ep_buffer(ep, 0) | USB_BUF_CTRL_SEL;
|
||||
|
||||
// For now: skip double buffered for Device mode, OUT endpoint since
|
||||
// For now: skip double buffered for OUT endpoint in Device mode, since
|
||||
// host could send < 64 bytes and cause short packet on buffer0
|
||||
// NOTE this could happen to Host mode IN endpoint
|
||||
bool const force_single = !(usb_hw->main_ctrl & USB_MAIN_CTRL_HOST_NDEVICE_BITS) && !tu_edpt_dir(ep->ep_addr);
|
||||
// NOTE: this could happen to Host mode IN endpoint
|
||||
// Also, Host mode "interrupt" endpoint hardware is only single buffered,
|
||||
// NOTE2: Currently Host bulk is implemented using "interrupt" endpoint
|
||||
bool const is_host = is_host_mode();
|
||||
bool const force_single = (!is_host && !tu_edpt_dir(ep->ep_addr)) ||
|
||||
(is_host && tu_edpt_number(ep->ep_addr) != 0);
|
||||
|
||||
if(ep->remaining_len && !force_single)
|
||||
{
|
||||
@@ -174,7 +192,7 @@ static void _hw_endpoint_start_next_buffer(struct hw_endpoint *ep)
|
||||
|
||||
*ep->endpoint_control = ep_ctrl;
|
||||
|
||||
TU_LOG(3, " Prepare BufCtrl: [0] = 0x%04u [1] = 0x%04x\r\n", tu_u32_low16(buf_ctrl), tu_u32_high16(buf_ctrl));
|
||||
TU_LOG(3, " Prepare BufCtrl: [0] = 0x%04x [1] = 0x%04x\r\n", tu_u32_low16(buf_ctrl), tu_u32_high16(buf_ctrl));
|
||||
|
||||
// Finally, write to buffer_control which will trigger the transfer
|
||||
// the next time the controller polls this dpram address
|
||||
@@ -205,7 +223,7 @@ void hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, uint16_t to
|
||||
}
|
||||
|
||||
// sync endpoint buffer and return transferred bytes
|
||||
static uint16_t sync_ep_buffer(struct hw_endpoint *ep, uint8_t buf_id)
|
||||
static uint16_t __tusb_irq_path_func(sync_ep_buffer)(struct hw_endpoint *ep, uint8_t buf_id)
|
||||
{
|
||||
uint32_t buf_ctrl = _hw_endpoint_buffer_control_get_value32(ep);
|
||||
if (buf_id) buf_ctrl = buf_ctrl >> 16;
|
||||
@@ -241,13 +259,13 @@ static uint16_t sync_ep_buffer(struct hw_endpoint *ep, uint8_t buf_id)
|
||||
return xferred_bytes;
|
||||
}
|
||||
|
||||
static void _hw_endpoint_xfer_sync (struct hw_endpoint *ep)
|
||||
static void __tusb_irq_path_func(_hw_endpoint_xfer_sync) (struct hw_endpoint *ep)
|
||||
{
|
||||
// Update hw endpoint struct with info from hardware
|
||||
// after a buff status interrupt
|
||||
|
||||
uint32_t __unused buf_ctrl = _hw_endpoint_buffer_control_get_value32(ep);
|
||||
TU_LOG(3, " Sync BufCtrl: [0] = 0x%04u [1] = 0x%04x\r\n", tu_u32_low16(buf_ctrl), tu_u32_high16(buf_ctrl));
|
||||
TU_LOG(3, " Sync BufCtrl: [0] = 0x%04x [1] = 0x%04x\r\n", tu_u32_low16(buf_ctrl), tu_u32_high16(buf_ctrl));
|
||||
|
||||
// always sync buffer 0
|
||||
uint16_t buf0_bytes = sync_ep_buffer(ep, 0);
|
||||
@@ -285,14 +303,14 @@ static void _hw_endpoint_xfer_sync (struct hw_endpoint *ep)
|
||||
usb_hw->abort &= ~TU_BIT(ep_id);
|
||||
|
||||
TU_LOG(3, "----SHORT PACKET buffer0 on EP %02X:\r\n", ep->ep_addr);
|
||||
TU_LOG(3, " BufCtrl: [0] = 0x%04u [1] = 0x%04x\r\n", tu_u32_low16(buf_ctrl), tu_u32_high16(buf_ctrl));
|
||||
TU_LOG(3, " BufCtrl: [0] = 0x%04x [1] = 0x%04x\r\n", tu_u32_low16(buf_ctrl), tu_u32_high16(buf_ctrl));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if transfer is complete
|
||||
bool hw_endpoint_xfer_continue(struct hw_endpoint *ep)
|
||||
bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint *ep)
|
||||
{
|
||||
_hw_endpoint_lock_update(ep, 1);
|
||||
// Part way through a transfer
|
||||
|
||||
@@ -16,6 +16,15 @@
|
||||
#define TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX PICO_RP2040_USB_DEVICE_ENUMERATION_FIX
|
||||
#endif
|
||||
|
||||
#ifndef PICO_RP2040_USB_FAST_IRQ
|
||||
#define PICO_RP2040_USB_FAST_IRQ 0
|
||||
#endif
|
||||
|
||||
#if PICO_RP2040_USB_FAST_IRQ
|
||||
#define __tusb_irq_path_func(x) __no_inline_not_in_flash_func(x)
|
||||
#else
|
||||
#define __tusb_irq_path_func(x) x
|
||||
#endif
|
||||
|
||||
#define pico_info(...) TU_LOG(2, __VA_ARGS__)
|
||||
#define pico_trace(...) TU_LOG(3, __VA_ARGS__)
|
||||
@@ -72,23 +81,31 @@ bool hw_endpoint_xfer_continue(struct hw_endpoint *ep);
|
||||
void hw_endpoint_reset_transfer(struct hw_endpoint *ep);
|
||||
|
||||
void _hw_endpoint_buffer_control_update32(struct hw_endpoint *ep, uint32_t and_mask, uint32_t or_mask);
|
||||
static inline uint32_t _hw_endpoint_buffer_control_get_value32(struct hw_endpoint *ep) {
|
||||
return *ep->buffer_control;
|
||||
}
|
||||
static inline void _hw_endpoint_buffer_control_set_value32(struct hw_endpoint *ep, uint32_t value) {
|
||||
return _hw_endpoint_buffer_control_update32(ep, 0, value);
|
||||
}
|
||||
static inline void _hw_endpoint_buffer_control_set_mask32(struct hw_endpoint *ep, uint32_t value) {
|
||||
return _hw_endpoint_buffer_control_update32(ep, ~value, value);
|
||||
}
|
||||
static inline void _hw_endpoint_buffer_control_clear_mask32(struct hw_endpoint *ep, uint32_t value) {
|
||||
return _hw_endpoint_buffer_control_update32(ep, ~value, 0);
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t _hw_endpoint_buffer_control_get_value32 (struct hw_endpoint *ep)
|
||||
{
|
||||
return *ep->buffer_control;
|
||||
}
|
||||
|
||||
static inline uintptr_t hw_data_offset(uint8_t *buf)
|
||||
TU_ATTR_ALWAYS_INLINE static inline void _hw_endpoint_buffer_control_set_value32 (struct hw_endpoint *ep, uint32_t value)
|
||||
{
|
||||
// Remove usb base from buffer pointer
|
||||
return (uintptr_t)buf ^ (uintptr_t)usb_dpram;
|
||||
return _hw_endpoint_buffer_control_update32(ep, 0, value);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void _hw_endpoint_buffer_control_set_mask32 (struct hw_endpoint *ep, uint32_t value)
|
||||
{
|
||||
return _hw_endpoint_buffer_control_update32(ep, ~value, value);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void _hw_endpoint_buffer_control_clear_mask32 (struct hw_endpoint *ep, uint32_t value)
|
||||
{
|
||||
return _hw_endpoint_buffer_control_update32(ep, ~value, 0);
|
||||
}
|
||||
|
||||
static inline uintptr_t hw_data_offset (uint8_t *buf)
|
||||
{
|
||||
// Remove usb base from buffer pointer
|
||||
return (uintptr_t) buf ^ (uintptr_t) usb_dpram;
|
||||
}
|
||||
|
||||
extern const char *ep_dir_string[];
|
||||
|
||||
@@ -687,6 +687,14 @@ void dcd_disconnect(uint8_t rhport)
|
||||
USB0.SYSCFG.BIT.DPRPU = 0;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
@@ -600,7 +600,7 @@ uint32_t hcd_frame_number(uint8_t rhport)
|
||||
bool hcd_port_connect_status(uint8_t rhport)
|
||||
{
|
||||
(void)rhport;
|
||||
return USB0.INTSTS1.BIT.ATTCH ? true: false;
|
||||
return USB0.INTSTS1.BIT.ATTCH ? true : false;
|
||||
}
|
||||
|
||||
void hcd_port_reset(uint8_t rhport)
|
||||
|
||||
@@ -247,6 +247,14 @@ void dcd_disconnect(uint8_t rhport)
|
||||
DEV_DISCONNECT(usbdev);
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
@@ -109,12 +109,20 @@
|
||||
#define STM32F1_FSDEV
|
||||
#endif
|
||||
|
||||
#if defined(STM32L412xx) || defined(STM32L422xx) || \
|
||||
defined(STM32L432xx) || defined(STM32L433xx) || \
|
||||
defined(STM32L442xx) || defined(STM32L443xx) || \
|
||||
defined(STM32L452xx) || defined(STM32L462xx)
|
||||
#define STM32L4_FSDEV
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_ENABLED && \
|
||||
( TU_CHECK_MCU(OPT_MCU_STM32F0, OPT_MCU_STM32F3, OPT_MCU_STM32L0, OPT_MCU_STM32L1, OPT_MCU_STM32G4, OPT_MCU_STM32WB) || \
|
||||
(TU_CHECK_MCU(OPT_MCU_STM32F1) && defined(STM32F1_FSDEV)) \
|
||||
(TU_CHECK_MCU(OPT_MCU_STM32F1) && defined(STM32F1_FSDEV)) || \
|
||||
(TU_CHECK_MCU(OPT_MCU_STM32L4) && defined(STM32L4_FSDEV)) \
|
||||
)
|
||||
|
||||
// In order to reduce the dependance on HAL, we undefine this.
|
||||
// In order to reduce the dependence on HAL, we undefine this.
|
||||
// Some definitions are copied to our private include file.
|
||||
#undef USE_HAL_DRIVER
|
||||
|
||||
@@ -287,6 +295,14 @@ void dcd_connect(uint8_t rhport)
|
||||
}
|
||||
#endif
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
// Enable device interrupt
|
||||
void dcd_int_enable (uint8_t rhport)
|
||||
{
|
||||
@@ -294,7 +310,8 @@ void dcd_int_enable (uint8_t rhport)
|
||||
// Member here forces write to RAM before allowing ISR to execute
|
||||
__DSB();
|
||||
__ISB();
|
||||
#if CFG_TUSB_MCU == OPT_MCU_STM32F0 || CFG_TUSB_MCU == OPT_MCU_STM32L0
|
||||
#if CFG_TUSB_MCU == OPT_MCU_STM32F0 || CFG_TUSB_MCU == OPT_MCU_STM32L0 || \
|
||||
CFG_TUSB_MCU == OPT_MCU_STM32L4
|
||||
NVIC_EnableIRQ(USB_IRQn);
|
||||
|
||||
#elif CFG_TUSB_MCU == OPT_MCU_STM32L1
|
||||
@@ -342,7 +359,8 @@ void dcd_int_disable(uint8_t rhport)
|
||||
{
|
||||
(void)rhport;
|
||||
|
||||
#if CFG_TUSB_MCU == OPT_MCU_STM32F0 || CFG_TUSB_MCU == OPT_MCU_STM32L0
|
||||
#if CFG_TUSB_MCU == OPT_MCU_STM32F0 || CFG_TUSB_MCU == OPT_MCU_STM32L0 || \
|
||||
CFG_TUSB_MCU == OPT_MCU_STM32L4
|
||||
NVIC_DisableIRQ(USB_IRQn);
|
||||
#elif CFG_TUSB_MCU == OPT_MCU_STM32L1
|
||||
NVIC_DisableIRQ(USB_LP_IRQn);
|
||||
|
||||
@@ -98,6 +98,10 @@
|
||||
#undef USB_PMAADDR
|
||||
#define USB_PMAADDR USB1_PMAADDR
|
||||
|
||||
#elif CFG_TUSB_MCU == OPT_MCU_STM32L4
|
||||
#include "stm32l4xx.h"
|
||||
#define PMA_LENGTH (1024u)
|
||||
|
||||
#else
|
||||
#error You are using an untested or unimplemented STM32 variant. Please update the driver.
|
||||
// This includes L1x0, L1x1, L1x2, L4x2 and L4x3, G1x1, G1x3, and G1x4
|
||||
|
||||
@@ -602,6 +602,13 @@ void dcd_disconnect(uint8_t rhport)
|
||||
dev->DCTL |= USB_OTG_DCTL_SDIS;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* DCD Endpoint port
|
||||
|
||||
@@ -243,7 +243,7 @@ static void USBC_Dev_SetAddress(u8 address)
|
||||
|
||||
static void __USBC_Dev_Tx_SendStall(void)
|
||||
{
|
||||
//send stall, and fifo is flushed automaticly
|
||||
//send stall, and fifo is flushed automatically
|
||||
USBC_REG_set_bit_w(USBC_BP_TXCSR_D_SEND_STALL, USBC_REG_TXCSR(USBC0_BASE));
|
||||
}
|
||||
static u32 __USBC_Dev_Tx_IsEpStall(void)
|
||||
@@ -909,6 +909,14 @@ void dcd_disconnect(uint8_t rhport)
|
||||
USBC_REG_clear_bit_b(USBC_BP_POWER_D_SOFT_CONNECT, USBC_REG_PCTL(USBC0_BASE));
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
void dcd_int_enable(uint8_t rhport)
|
||||
{
|
||||
(void)rhport;
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
#define USBC1_BASE 0x01c14000
|
||||
#define USBC2_BASE 0x01c1E000
|
||||
|
||||
//Some reg whithin musb
|
||||
//Some reg within musb
|
||||
#define USBPHY_CLK_REG 0x01c200CC
|
||||
#define USBPHY_CLK_RST_BIT 0
|
||||
#define USBPHY_CLK_GAT_BIT 1
|
||||
|
||||
@@ -34,6 +34,13 @@
|
||||
#include "device/dcd.h"
|
||||
#include "dwc2_type.h"
|
||||
|
||||
// Following symbols must be defined by port header
|
||||
// - _dwc2_controller[]: array of controllers
|
||||
// - DWC2_EP_MAX: largest EP counts of all controllers
|
||||
// - dwc2_phy_init/dwc2_phy_update: phy init called before and after core reset
|
||||
// - dwc2_dcd_int_enable/dwc2_dcd_int_disable
|
||||
// - dwc2_remote_wakeup_delay
|
||||
|
||||
#if defined(TUP_USBIP_DWC2_STM32)
|
||||
#include "dwc2_stm32.h"
|
||||
#elif TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
|
||||
@@ -55,7 +62,7 @@
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// DWC2 registers
|
||||
#define DWC2_REG(_port) ((dwc2_regs_t*) DWC2_REG_BASE)
|
||||
#define DWC2_REG(_port) ((dwc2_regs_t*) _dwc2_controller[_port].reg_base)
|
||||
|
||||
// Debug level for DWC2
|
||||
#define DWC2_DEBUG 2
|
||||
@@ -72,7 +79,6 @@
|
||||
#define dcache_clean_invalidate(_addr, _size)
|
||||
#endif
|
||||
|
||||
|
||||
static TU_ATTR_ALIGNED(4) uint32_t _setup_packet[2];
|
||||
|
||||
typedef struct {
|
||||
@@ -93,44 +99,47 @@ static uint16_t ep0_pending[2]; // Index determines direction
|
||||
static uint16_t _allocated_fifo_words_tx; // TX FIFO size in words (IN EPs)
|
||||
static bool _out_ep_closed; // Flag to check if RX FIFO size needs an update (reduce its size)
|
||||
|
||||
// SOF enabling flag - required for SOF to not get disabled in ISR when SOF was enabled by
|
||||
static bool _sof_en;
|
||||
|
||||
// Calculate the RX FIFO size according to recommendations from reference manual
|
||||
static inline uint16_t calc_rx_ff_size(uint16_t ep_size)
|
||||
static inline uint16_t calc_grxfsiz(uint16_t max_ep_size, uint8_t ep_count)
|
||||
{
|
||||
return 15 + 2*(ep_size/4) + 2*DWC2_EP_MAX;
|
||||
return 15 + 2*(max_ep_size/4) + 2*ep_count;
|
||||
}
|
||||
|
||||
static void update_grxfsiz(uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
|
||||
dwc2_regs_t * dwc2 = DWC2_REG(rhport);
|
||||
dwc2_regs_t * dwc2 = DWC2_REG(rhport);
|
||||
uint8_t const ep_count = _dwc2_controller[rhport].ep_count;
|
||||
|
||||
// Determine largest EP size for RX FIFO
|
||||
uint16_t max_epsize = 0;
|
||||
for (uint8_t epnum = 0; epnum < DWC2_EP_MAX; epnum++)
|
||||
for (uint8_t epnum = 0; epnum < ep_count; epnum++)
|
||||
{
|
||||
max_epsize = tu_max16(max_epsize, xfer_status[epnum][TUSB_DIR_OUT].max_size);
|
||||
}
|
||||
|
||||
// Update size of RX FIFO
|
||||
dwc2->grxfsiz = calc_rx_ff_size(max_epsize);
|
||||
dwc2->grxfsiz = calc_grxfsiz(max_epsize, ep_count);
|
||||
}
|
||||
|
||||
// Setup the control endpoint 0.
|
||||
// Start of Bus Reset
|
||||
static void bus_reset(uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
|
||||
dwc2_regs_t * dwc2 = DWC2_REG(rhport);
|
||||
dwc2_regs_t * dwc2 = DWC2_REG(rhport);
|
||||
uint8_t const ep_count = _dwc2_controller[rhport].ep_count;
|
||||
|
||||
tu_memclr(xfer_status, sizeof(xfer_status));
|
||||
_out_ep_closed = false;
|
||||
|
||||
_sof_en = false;
|
||||
|
||||
// clear device address
|
||||
dwc2->dcfg &= ~DCFG_DAD_Msk;
|
||||
|
||||
// 1. NAK for all OUT endpoints
|
||||
for ( uint8_t n = 0; n < DWC2_EP_MAX; n++ )
|
||||
for ( uint8_t n = 0; n < ep_count; n++ )
|
||||
{
|
||||
dwc2->epout[n].doepctl |= DOEPCTL_SNAK;
|
||||
}
|
||||
@@ -180,22 +189,24 @@ static void bus_reset(uint8_t rhport)
|
||||
// - 2 for each used OUT endpoint
|
||||
//
|
||||
// Therefore GRXFSIZ = 13 + 1 + 1 + 2 x (Largest-EPsize/4) + 2 x EPOUTnum
|
||||
// - FullSpeed (64 Bytes ): GRXFSIZ = 15 + 2 x 16 + 2 x DWC2_EP_MAX = 47 + 2 x DWC2_EP_MAX
|
||||
// - Highspeed (512 bytes): GRXFSIZ = 15 + 2 x 128 + 2 x DWC2_EP_MAX = 271 + 2 x DWC2_EP_MAX
|
||||
// - FullSpeed (64 Bytes ): GRXFSIZ = 15 + 2 x 16 + 2 x ep_count = 47 + 2 x ep_count
|
||||
// - Highspeed (512 bytes): GRXFSIZ = 15 + 2 x 128 + 2 x ep_count = 271 + 2 x ep_count
|
||||
//
|
||||
// NOTE: Largest-EPsize & EPOUTnum is actual used endpoints in configuration. Since DCD has no knowledge
|
||||
// of the overall picture yet. We will use the worst scenario: largest possible + DWC2_EP_MAX
|
||||
// of the overall picture yet. We will use the worst scenario: largest possible + ep_count
|
||||
//
|
||||
// For Isochronous, largest EP size can be 1023/1024 for FS/HS respectively. In addition if multiple ISO
|
||||
// are enabled at least "2 x (Largest-EPsize/4) + 1" are recommended. Maybe provide a macro for application to
|
||||
// overwrite this.
|
||||
|
||||
dwc2->grxfsiz = calc_rx_ff_size(TUD_OPT_HIGH_SPEED ? 512 : 64);
|
||||
// EP0 out max is 64
|
||||
dwc2->grxfsiz = calc_grxfsiz(64, ep_count);
|
||||
|
||||
// Setup the control endpoint 0
|
||||
_allocated_fifo_words_tx = 16;
|
||||
|
||||
// Control IN uses FIFO 0 with 64 bytes ( 16 32-bit word )
|
||||
dwc2->dieptxf0 = (16 << DIEPTXF0_TX0FD_Pos) | (DWC2_EP_FIFO_SIZE/4 - _allocated_fifo_words_tx);
|
||||
dwc2->dieptxf0 = (16 << DIEPTXF0_TX0FD_Pos) | (_dwc2_controller[rhport].ep_fifo_size/4 - _allocated_fifo_words_tx);
|
||||
|
||||
// Fixed control EP0 size to 64 bytes
|
||||
dwc2->epin[0].diepctl &= ~(0x03 << DIEPCTL_MPSIZ_Pos);
|
||||
@@ -358,7 +369,11 @@ static void reset_core(dwc2_regs_t * dwc2)
|
||||
static bool phy_hs_supported(dwc2_regs_t * dwc2)
|
||||
{
|
||||
// note: esp32 incorrect report its hs_phy_type as utmi
|
||||
#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
|
||||
return false;
|
||||
#else
|
||||
return TUD_OPT_HIGH_SPEED && dwc2->ghwcfg2_bm.hs_phy_type != HS_PHY_TYPE_NONE;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void phy_fs_init(dwc2_regs_t * dwc2)
|
||||
@@ -457,6 +472,7 @@ static bool check_dwc2(dwc2_regs_t * dwc2)
|
||||
#endif
|
||||
|
||||
// For some reasons: GD32VF103 snpsid and all hwcfg register are always zero (skip it)
|
||||
(void) dwc2;
|
||||
#if !TU_CHECK_MCU(OPT_MCU_GD32VF103)
|
||||
uint32_t const gsnpsid = dwc2->gsnpsid & GSNPSID_ID_MASK;
|
||||
TU_ASSERT(gsnpsid == DWC2_OTG_ID || gsnpsid == DWC2_FS_IOT_ID || gsnpsid == DWC2_HS_IOT_ID);
|
||||
@@ -588,6 +604,24 @@ void dcd_disconnect(uint8_t rhport)
|
||||
dwc2->dctl |= DCTL_SDIS;
|
||||
}
|
||||
|
||||
// Be advised: audio, video and possibly other iso-ep classes use dcd_sof_enable() to enable/disable its corresponding ISR on purpose!
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
dwc2_regs_t * dwc2 = DWC2_REG(rhport);
|
||||
|
||||
_sof_en = en;
|
||||
|
||||
if (en)
|
||||
{
|
||||
dwc2->gintsts = GINTSTS_SOF;
|
||||
dwc2->gintmsk |= GINTMSK_SOFM;
|
||||
}
|
||||
else
|
||||
{
|
||||
dwc2->gintmsk &= ~GINTMSK_SOFM;
|
||||
}
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* DCD Endpoint port
|
||||
@@ -597,12 +631,13 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
|
||||
{
|
||||
(void) rhport;
|
||||
|
||||
dwc2_regs_t * dwc2 = DWC2_REG(rhport);
|
||||
dwc2_regs_t * dwc2 = DWC2_REG(rhport);
|
||||
uint8_t const ep_count = _dwc2_controller[rhport].ep_count;
|
||||
|
||||
uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress);
|
||||
uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress);
|
||||
|
||||
TU_ASSERT(epnum < DWC2_EP_MAX);
|
||||
TU_ASSERT(epnum < ep_count);
|
||||
|
||||
xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
|
||||
xfer->max_size = tu_edpt_packet_size(desc_edpt);
|
||||
@@ -613,12 +648,12 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
|
||||
if(dir == TUSB_DIR_OUT)
|
||||
{
|
||||
// Calculate required size of RX FIFO
|
||||
uint16_t const sz = calc_rx_ff_size(4*fifo_size);
|
||||
uint16_t const sz = calc_grxfsiz(4*fifo_size, ep_count);
|
||||
|
||||
// If size_rx needs to be extended check if possible and if so enlarge it
|
||||
if (dwc2->grxfsiz < sz)
|
||||
{
|
||||
TU_ASSERT(sz + _allocated_fifo_words_tx <= DWC2_EP_FIFO_SIZE/4);
|
||||
TU_ASSERT(sz + _allocated_fifo_words_tx <= _dwc2_controller[rhport].ep_fifo_size/4);
|
||||
|
||||
// Enlarge RX FIFO
|
||||
dwc2->grxfsiz = sz;
|
||||
@@ -655,15 +690,15 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
|
||||
// - IN EP 1 gets FIFO 1, IN EP "n" gets FIFO "n".
|
||||
|
||||
// Check if free space is available
|
||||
TU_ASSERT(_allocated_fifo_words_tx + fifo_size + dwc2->grxfsiz <= DWC2_EP_FIFO_SIZE/4);
|
||||
TU_ASSERT(_allocated_fifo_words_tx + fifo_size + dwc2->grxfsiz <= _dwc2_controller[rhport].ep_fifo_size/4);
|
||||
|
||||
_allocated_fifo_words_tx += fifo_size;
|
||||
|
||||
TU_LOG(DWC2_DEBUG, " Allocated %u bytes at offset %u", fifo_size*4, DWC2_EP_FIFO_SIZE-_allocated_fifo_words_tx*4);
|
||||
TU_LOG(DWC2_DEBUG, " Allocated %u bytes at offset %lu", fifo_size*4, _dwc2_controller[rhport].ep_fifo_size-_allocated_fifo_words_tx*4);
|
||||
|
||||
// DIEPTXF starts at FIFO #1.
|
||||
// Both TXFD and TXSA are in unit of 32-bit words.
|
||||
dwc2->dieptxf[epnum - 1] = (fifo_size << DIEPTXF_INEPTXFD_Pos) | (DWC2_EP_FIFO_SIZE/4 - _allocated_fifo_words_tx);
|
||||
dwc2->dieptxf[epnum - 1] = (fifo_size << DIEPTXF_INEPTXFD_Pos) | (_dwc2_controller[rhport].ep_fifo_size/4 - _allocated_fifo_words_tx);
|
||||
|
||||
dwc2->epin[epnum].diepctl |= (1 << DIEPCTL_USBAEP_Pos) |
|
||||
(epnum << DIEPCTL_TXFNUM_Pos) |
|
||||
@@ -680,14 +715,13 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
|
||||
// Close all non-control endpoints, cancel all pending transfers if any.
|
||||
void dcd_edpt_close_all (uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
|
||||
dwc2_regs_t * dwc2 = DWC2_REG(rhport);
|
||||
dwc2_regs_t * dwc2 = DWC2_REG(rhport);
|
||||
uint8_t const ep_count = _dwc2_controller[rhport].ep_count;
|
||||
|
||||
// Disable non-control interrupt
|
||||
dwc2->daintmsk = (1 << DAINTMSK_OEPM_Pos) | (1 << DAINTMSK_IEPM_Pos);
|
||||
|
||||
for(uint8_t n = 1; n < DWC2_EP_MAX; n++)
|
||||
for(uint8_t n = 1; n < ep_count; n++)
|
||||
{
|
||||
// disable OUT endpoint
|
||||
dwc2->epout[n].doepctl = 0;
|
||||
@@ -796,8 +830,7 @@ static void dcd_edpt_disable (uint8_t rhport, uint8_t ep_addr, bool stall)
|
||||
}
|
||||
|
||||
// Flush the FIFO, and wait until we have confirmed it cleared.
|
||||
dwc2->grstctl |= (epnum << GRSTCTL_TXFNUM_Pos);
|
||||
dwc2->grstctl |= GRSTCTL_TXFFLSH;
|
||||
dwc2->grstctl = ((epnum << GRSTCTL_TXFNUM_Pos) | GRSTCTL_TXFFLSH);
|
||||
while ( (dwc2->grstctl & GRSTCTL_TXFFLSH_Msk) != 0 ) {}
|
||||
}
|
||||
else
|
||||
@@ -849,8 +882,9 @@ void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr)
|
||||
{
|
||||
uint16_t const fifo_size = (dwc2->dieptxf[epnum - 1] & DIEPTXF_INEPTXFD_Msk) >> DIEPTXF_INEPTXFD_Pos;
|
||||
uint16_t const fifo_start = (dwc2->dieptxf[epnum - 1] & DIEPTXF_INEPTXSA_Msk) >> DIEPTXF_INEPTXSA_Pos;
|
||||
|
||||
// For now only the last opened endpoint can be closed without fuss.
|
||||
TU_ASSERT(fifo_start == DWC2_EP_FIFO_SIZE/4 - _allocated_fifo_words_tx,);
|
||||
TU_ASSERT(fifo_start == _dwc2_controller[rhport].ep_fifo_size/4 - _allocated_fifo_words_tx,);
|
||||
_allocated_fifo_words_tx -= fifo_size;
|
||||
}
|
||||
else
|
||||
@@ -969,7 +1003,7 @@ static void handle_rxflvl_irq(uint8_t rhport)
|
||||
|
||||
switch ( pktsts )
|
||||
{
|
||||
// Global OUT NAK: do nothign
|
||||
// Global OUT NAK: do nothing
|
||||
case GRXSTS_PKTSTS_GLOBALOUTNAK: break;
|
||||
|
||||
case GRXSTS_PKTSTS_SETUPRX:
|
||||
@@ -1053,11 +1087,12 @@ static void handle_rxflvl_irq(uint8_t rhport)
|
||||
|
||||
static void handle_epout_irq (uint8_t rhport)
|
||||
{
|
||||
dwc2_regs_t *dwc2 = DWC2_REG(rhport);
|
||||
dwc2_regs_t * dwc2 = DWC2_REG(rhport);
|
||||
uint8_t const ep_count = _dwc2_controller[rhport].ep_count;
|
||||
|
||||
// DAINT for a given EP clears when DOEPINTx is cleared.
|
||||
// OEPINT will be cleared when DAINT's out bits are cleared.
|
||||
for ( uint8_t n = 0; n < DWC2_EP_MAX; n++ )
|
||||
for ( uint8_t n = 0; n < ep_count; n++ )
|
||||
{
|
||||
if ( dwc2->daint & TU_BIT(DAINT_OEPINT_Pos + n) )
|
||||
{
|
||||
@@ -1104,12 +1139,13 @@ static void handle_epout_irq (uint8_t rhport)
|
||||
|
||||
static void handle_epin_irq (uint8_t rhport)
|
||||
{
|
||||
dwc2_regs_t *dwc2 = DWC2_REG(rhport);
|
||||
dwc2_epin_t* epin = dwc2->epin;
|
||||
dwc2_regs_t * dwc2 = DWC2_REG(rhport);
|
||||
uint8_t const ep_count = _dwc2_controller[rhport].ep_count;
|
||||
dwc2_epin_t* epin = dwc2->epin;
|
||||
|
||||
// DAINT for a given EP clears when DIEPINTx is cleared.
|
||||
// IEPINT will be cleared when DAINT's out bits are cleared.
|
||||
for ( uint8_t n = 0; n < DWC2_EP_MAX; n++ )
|
||||
for ( uint8_t n = 0; n < ep_count; n++ )
|
||||
{
|
||||
if ( dwc2->daint & TU_BIT(DAINT_IEPINT_Pos + n) )
|
||||
{
|
||||
@@ -1251,8 +1287,16 @@ void dcd_int_handler(uint8_t rhport)
|
||||
{
|
||||
dwc2->gotgint = GINTSTS_SOF;
|
||||
|
||||
// Disable SOF interrupt since currently only used for remote wakeup detection
|
||||
dwc2->gintmsk &= ~GINTMSK_SOFM;
|
||||
if (_sof_en)
|
||||
{
|
||||
uint32_t frame = (dwc2->dsts & (DSTS_FNSOF)) >> 8;
|
||||
dcd_event_sof(rhport, frame, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Disable SOF interrupt if SOF was not explicitly enabled. SOF was used for remote wakeup detection
|
||||
dwc2->gintmsk &= ~GINTMSK_SOFM;
|
||||
}
|
||||
|
||||
dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
|
||||
}
|
||||
|
||||
@@ -35,9 +35,12 @@
|
||||
#include "broadcom/interrupts.h"
|
||||
#include "broadcom/caches.h"
|
||||
|
||||
#define DWC2_REG_BASE USB_OTG_GLOBAL_BASE
|
||||
#define DWC2_EP_MAX 8
|
||||
#define DWC2_EP_FIFO_SIZE 4096
|
||||
|
||||
static const dwc2_controller_t _dwc2_controller[] =
|
||||
{
|
||||
{ .reg_base = USB_OTG_GLOBAL_BASE, .irqnum = USB_IRQn, .ep_count = DWC2_EP_MAX, .ep_fifo_size = 4096 }
|
||||
};
|
||||
|
||||
#define dcache_clean(_addr, _size) data_clean(_addr, _size)
|
||||
#define dcache_invalidate(_addr, _size) data_invalidate(_addr, _size)
|
||||
@@ -46,15 +49,13 @@
|
||||
TU_ATTR_ALWAYS_INLINE
|
||||
static inline void dwc2_dcd_int_enable(uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
BP_EnableIRQ(USB_IRQn);
|
||||
BP_EnableIRQ(_dwc2_controller[rhport].irqnum);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE
|
||||
static inline void dwc2_dcd_int_disable (uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
BP_DisableIRQ(USB_IRQn);
|
||||
BP_DisableIRQ(_dwc2_controller[rhport].irqnum);
|
||||
}
|
||||
|
||||
static inline void dwc2_remote_wakeup_delay(void)
|
||||
|
||||
@@ -37,20 +37,22 @@
|
||||
// EFM32 has custom control register before DWC registers
|
||||
#define DWC2_REG_BASE (USB_BASE + offsetof(USB_TypeDef, GOTGCTL))
|
||||
#define DWC2_EP_MAX 7
|
||||
#define DWC2_EP_FIFO_SIZE 2048
|
||||
|
||||
static const dwc2_controller_t _dwc2_controller[] =
|
||||
{
|
||||
{ .reg_base = DWC2_REG_BASE, .irqnum = USB_IRQn, .ep_count = DWC2_EP_MAX, .ep_fifo_size = 2048 }
|
||||
};
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE
|
||||
static inline void dwc2_dcd_int_enable(uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
NVIC_EnableIRQ(USB_IRQn);
|
||||
NVIC_EnableIRQ(_dwc2_controller[rhport].irqnum);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE
|
||||
static inline void dwc2_dcd_int_disable (uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
NVIC_DisableIRQ(USB_IRQn);
|
||||
NVIC_DisableIRQ(_dwc2_controller[rhport].irqnum);
|
||||
}
|
||||
|
||||
static inline void dwc2_remote_wakeup_delay(void)
|
||||
|
||||
@@ -37,10 +37,12 @@
|
||||
//#include "soc/usb_periph.h"
|
||||
|
||||
#define DWC2_REG_BASE 0x60080000UL
|
||||
#define DWC2_EP_MAX 5 // USB_OUT_EP_NUM
|
||||
#define DWC2_EP_FIFO_SIZE 1024
|
||||
#define DWC2_EP_MAX 6 // USB_OUT_EP_NUM. TODO ESP32Sx only has 5 tx fifo (5 endpoint IN)
|
||||
|
||||
// #define EP_FIFO_NUM 5
|
||||
static const dwc2_controller_t _dwc2_controller[] =
|
||||
{
|
||||
{ .reg_base = DWC2_REG_BASE, .irqnum = 0, .ep_count = DWC2_EP_MAX, .ep_fifo_size = 1024 }
|
||||
};
|
||||
|
||||
static intr_handle_t usb_ih;
|
||||
|
||||
|
||||
@@ -34,8 +34,11 @@
|
||||
|
||||
#define DWC2_REG_BASE 0x50000000UL
|
||||
#define DWC2_EP_MAX 4
|
||||
#define DWC2_EP_FIFO_SIZE 1280
|
||||
#define RHPORT_IRQn 86
|
||||
|
||||
static const dwc2_controller_t _dwc2_controller[] =
|
||||
{
|
||||
{ .reg_base = DWC2_REG_BASE, .irqnum = 86, .ep_count = DWC2_EP_MAX, .ep_fifo_size = 1280 }
|
||||
};
|
||||
|
||||
extern uint32_t SystemCoreClock;
|
||||
|
||||
@@ -57,15 +60,13 @@ static inline void __eclic_disable_interrupt (uint32_t irq){
|
||||
TU_ATTR_ALWAYS_INLINE
|
||||
static inline void dwc2_dcd_int_enable(uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
__eclic_enable_interrupt(RHPORT_IRQn);
|
||||
__eclic_enable_interrupt(_dwc2_controller[rhport].irqnum);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE
|
||||
static inline void dwc2_dcd_int_disable (uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
__eclic_disable_interrupt(RHPORT_IRQn);
|
||||
__eclic_disable_interrupt(_dwc2_controller[rhport].irqnum);
|
||||
}
|
||||
|
||||
static inline void dwc2_remote_wakeup_delay(void)
|
||||
|
||||
@@ -43,10 +43,14 @@
|
||||
#define EP_MAX_FS USB_OTG_FS_MAX_IN_ENDPOINTS
|
||||
#define EP_FIFO_SIZE_FS USB_OTG_FS_TOTAL_FIFO_SIZE
|
||||
|
||||
#define EP_MAX_HS USB_OTG_HS_MAX_IN_ENDPOINTS
|
||||
#define EP_FIFO_SIZE_HS USB_OTG_HS_TOTAL_FIFO_SIZE
|
||||
|
||||
#elif CFG_TUSB_MCU == OPT_MCU_STM32F4
|
||||
#include "stm32f4xx.h"
|
||||
#define EP_MAX_FS USB_OTG_FS_MAX_IN_ENDPOINTS
|
||||
#define EP_FIFO_SIZE_FS USB_OTG_FS_TOTAL_FIFO_SIZE
|
||||
|
||||
#define EP_MAX_HS USB_OTG_HS_MAX_IN_ENDPOINTS
|
||||
#define EP_FIFO_SIZE_HS USB_OTG_HS_TOTAL_FIFO_SIZE
|
||||
|
||||
@@ -54,13 +58,22 @@
|
||||
#include "stm32h7xx.h"
|
||||
#define EP_MAX_FS 9
|
||||
#define EP_FIFO_SIZE_FS 4096
|
||||
|
||||
#define EP_MAX_HS 9
|
||||
#define EP_FIFO_SIZE_HS 4096
|
||||
|
||||
// NOTE: H7 with only 1 USB port: H72x / H73x / H7Ax / H7Bx
|
||||
// USB_OTG_FS_PERIPH_BASE and OTG_FS_IRQn not defined
|
||||
#if (! defined USB2_OTG_FS)
|
||||
#define USB_OTG_FS_PERIPH_BASE USB1_OTG_HS_PERIPH_BASE
|
||||
#define OTG_FS_IRQn OTG_HS_IRQn
|
||||
#endif
|
||||
|
||||
#elif CFG_TUSB_MCU == OPT_MCU_STM32F7
|
||||
#include "stm32f7xx.h"
|
||||
#define EP_MAX_FS 6
|
||||
#define EP_FIFO_SIZE_FS 1280
|
||||
|
||||
#define EP_MAX_HS 9
|
||||
#define EP_FIFO_SIZE_HS 4096
|
||||
|
||||
@@ -69,39 +82,53 @@
|
||||
#define EP_MAX_FS 6
|
||||
#define EP_FIFO_SIZE_FS 1280
|
||||
|
||||
#elif CFG_TUSB_MCU == OPT_MCU_STM32U5
|
||||
#include "stm32u5xx.h"
|
||||
#define USB_OTG_FS_PERIPH_BASE USB_OTG_FS_BASE
|
||||
#define EP_MAX_FS 6
|
||||
#define EP_FIFO_SIZE_FS 1280
|
||||
|
||||
#else
|
||||
#error "Unsupported MCUs"
|
||||
#endif
|
||||
|
||||
// On STM32 we associate Port0 to OTG_FS, and Port1 to OTG_HS
|
||||
#if TUD_OPT_RHPORT == 0
|
||||
#define DWC2_REG_BASE USB_OTG_FS_PERIPH_BASE
|
||||
#define DWC2_EP_MAX EP_MAX_FS
|
||||
#define DWC2_EP_FIFO_SIZE EP_FIFO_SIZE_FS
|
||||
#define RHPORT_IRQn OTG_FS_IRQn
|
||||
|
||||
// OTG HS always has higher number of endpoints than FS
|
||||
#ifdef USB_OTG_HS_PERIPH_BASE
|
||||
#define DWC2_EP_MAX EP_MAX_HS
|
||||
#else
|
||||
#define DWC2_REG_BASE USB_OTG_HS_PERIPH_BASE
|
||||
#define DWC2_EP_MAX EP_MAX_HS
|
||||
#define DWC2_EP_FIFO_SIZE EP_FIFO_SIZE_HS
|
||||
#define RHPORT_IRQn OTG_HS_IRQn
|
||||
|
||||
#define DWC2_EP_MAX EP_MAX_FS
|
||||
#endif
|
||||
|
||||
extern uint32_t SystemCoreClock;
|
||||
// On STM32 for consistency we associate
|
||||
// - Port0 to OTG_FS, and Port1 to OTG_HS
|
||||
static const dwc2_controller_t _dwc2_controller[] =
|
||||
{
|
||||
#ifdef USB_OTG_FS_PERIPH_BASE
|
||||
{ .reg_base = USB_OTG_FS_PERIPH_BASE, .irqnum = OTG_FS_IRQn, .ep_count = EP_MAX_FS, .ep_fifo_size = EP_FIFO_SIZE_FS },
|
||||
#endif
|
||||
|
||||
#ifdef USB_OTG_HS_PERIPH_BASE
|
||||
{ .reg_base = USB_OTG_HS_PERIPH_BASE, .irqnum = OTG_HS_IRQn, .ep_count = EP_MAX_HS, .ep_fifo_size = EP_FIFO_SIZE_HS },
|
||||
#endif
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
//
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// SystemCoreClock is already included by family header
|
||||
// extern uint32_t SystemCoreClock;
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE
|
||||
static inline void dwc2_dcd_int_enable(uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
NVIC_EnableIRQ(RHPORT_IRQn);
|
||||
NVIC_EnableIRQ(_dwc2_controller[rhport].irqnum);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE
|
||||
static inline void dwc2_dcd_int_disable (uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
NVIC_DisableIRQ(RHPORT_IRQn);
|
||||
NVIC_DisableIRQ(_dwc2_controller[rhport].irqnum);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE
|
||||
|
||||
@@ -19,6 +19,19 @@
|
||||
|
||||
#include "stdint.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Controller
|
||||
typedef struct
|
||||
{
|
||||
uintptr_t reg_base;
|
||||
uint32_t irqnum;
|
||||
uint8_t ep_count;
|
||||
uint32_t ep_fifo_size;
|
||||
}dwc2_controller_t;
|
||||
|
||||
/* DWC OTG HW Release versions */
|
||||
#define DWC2_CORE_REV_2_71a 0x4f54271a
|
||||
#define DWC2_CORE_REV_2_72a 0x4f54272a
|
||||
@@ -40,10 +53,6 @@
|
||||
#define DWC2_FS_IOT_ID 0x55310000
|
||||
#define DWC2_HS_IOT_ID 0x55320000
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
// HS PHY
|
||||
typedef struct
|
||||
|
||||
@@ -34,23 +34,24 @@
|
||||
|
||||
#include "xmc_device.h"
|
||||
|
||||
// XMC has custom control register before DWC registers
|
||||
#define DWC2_REG_BASE USB0_BASE
|
||||
#define DWC2_EP_MAX 7
|
||||
#define DWC2_EP_FIFO_SIZE 2048
|
||||
|
||||
static const dwc2_controller_t _dwc2_controller[] =
|
||||
{
|
||||
// Note: XMC has some custom control registers before DWC registers
|
||||
{ .reg_base = USB0_BASE, .irqnum = USB0_0_IRQn, .ep_count = DWC2_EP_MAX, .ep_fifo_size = 2048 }
|
||||
};
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE
|
||||
static inline void dwc2_dcd_int_enable(uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
NVIC_EnableIRQ(USB0_0_IRQn);
|
||||
NVIC_EnableIRQ(_dwc2_controller[rhport].irqnum);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE
|
||||
static inline void dwc2_dcd_int_disable (uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
NVIC_DisableIRQ(USB0_0_IRQn);
|
||||
NVIC_DisableIRQ(_dwc2_controller[rhport].irqnum);
|
||||
}
|
||||
|
||||
static inline void dwc2_remote_wakeup_delay(void)
|
||||
|
||||
@@ -82,6 +82,14 @@ void dcd_disconnect(uint8_t rhport)
|
||||
(void) rhport;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
@@ -222,6 +222,14 @@ void dcd_disconnect(uint8_t rhport)
|
||||
dcd_int_enable(rhport);
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* DCD Endpoint port
|
||||
*------------------------------------------------------------------*/
|
||||
@@ -623,7 +631,18 @@ void dcd_int_handler(uint8_t rhport)
|
||||
handle_setup_packet();
|
||||
}
|
||||
|
||||
uint16_t curr_vector = USBVECINT;
|
||||
// Workaround possible bug in MSP430 GCC 9.3.0 where volatile variable
|
||||
// USBVECINT is read from twice when only once is intended. The second
|
||||
// (garbage) read seems to be triggered by certain switch statement
|
||||
// configurations.
|
||||
uint16_t curr_vector;
|
||||
#if __GNUC__ > 9 || (__GNUC__ == 9 && __GNUC_MINOR__ > 2)
|
||||
asm volatile ("mov %1, %0"
|
||||
: "=r" (curr_vector)
|
||||
: "m" (USBVECINT));
|
||||
#else
|
||||
curr_vector = USBVECINT;
|
||||
#endif
|
||||
|
||||
switch(curr_vector)
|
||||
{
|
||||
|
||||
@@ -401,6 +401,13 @@ void dcd_disconnect(uint8_t rhport)
|
||||
usb_pullup_out_write(0);
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// DCD Endpoint Port
|
||||
@@ -458,7 +465,7 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
|
||||
enable = 1;
|
||||
usb_out_ctrl_write((0 << CSR_USB_OUT_CTRL_STALL_OFFSET) | (enable << CSR_USB_OUT_CTRL_ENABLE_OFFSET) | tu_edpt_number(ep_addr));
|
||||
}
|
||||
// IN endpoints will get unstalled when more data is written.
|
||||
// IN endpoints will get un-stalled when more data is written.
|
||||
}
|
||||
|
||||
bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes)
|
||||
|
||||
Reference in New Issue
Block a user