Merge branch 'master' into ch32v307

This commit is contained in:
Ha Thach
2023-01-07 23:46:46 +07:00
committed by GitHub
690 changed files with 46009 additions and 19690 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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
//--------------------------------------------------------------------+

View File

@@ -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"

View File

@@ -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;

View File

@@ -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) :

View File

@@ -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;
};

View File

@@ -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?
}

View File

@@ -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
//--------------------------------------------------------------------+

View 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

View File

@@ -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;

View File

@@ -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.
*******************************************************************************/

View File

@@ -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;

View File

@@ -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
//--------------------------------------------------------------------+

View File

@@ -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;

View File

@@ -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
//--------------------------------------------------------------------+

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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
//--------------------------------------------------------------------+

View File

@@ -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
//--------------------------------------------------------------------+

View File

@@ -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

View File

@@ -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
//--------------------------------------------------------------------+

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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();

View File

@@ -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

View File

@@ -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

View File

@@ -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[];

View File

@@ -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
//--------------------------------------------------------------------+

View File

@@ -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)

View File

@@ -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
//--------------------------------------------------------------------+

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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;

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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
//--------------------------------------------------------------------+

View File

@@ -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)
{

View File

@@ -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)