Merge branch 'master' into zhangslice-master

This commit is contained in:
hathach
2021-09-18 13:35:26 +07:00
273 changed files with 17041 additions and 7025 deletions
+17 -29
View File
@@ -28,7 +28,7 @@
#if TUSB_OPT_DEVICE_ENABLED && CFG_TUSB_MCU == OPT_MCU_DA1469X
#include "DA1469xAB.h"
#include "mcu/mcu.h"
#include "device/dcd.h"
@@ -258,33 +258,6 @@ static const tusb_desc_endpoint_t ep0IN_desc =
#define XFER_CTL_BASE(_ep, _dir) &_dcd.xfer_status[_ep][_dir]
// Function could be called when VBUS change was detected.
void tusb_vbus_changed(bool present)
{
if (present != _dcd.vbus_present)
{
_dcd.vbus_present = present;
if (present)
{
USB->USB_MCTRL_REG = USB_USB_MCTRL_REG_USBEN_Msk;
USB->USB_NFSR_REG = 0;
USB->USB_FAR_REG = 0x80;
USB->USB_NFSR_REG = NFSR_NODE_RESET;
USB->USB_TXMSK_REG = 0;
USB->USB_RXMSK_REG = 0;
USB->USB_MAMSK_REG = USB_USB_MAMSK_REG_USB_M_INTR_Msk |
USB_USB_MAMSK_REG_USB_M_ALT_Msk |
USB_USB_MAMSK_REG_USB_M_WARN_Msk;
USB->USB_ALTMSK_REG = USB_USB_ALTMSK_REG_USB_M_RESET_Msk;
}
else
{
USB->USB_MCTRL_REG = 0;
}
}
}
static void fill_tx_fifo(xfer_ctl_t * xfer)
{
int left_to_send;
@@ -763,7 +736,16 @@ static void handle_ep0_nak(void)
void dcd_init(uint8_t rhport)
{
USB->USB_MCTRL_REG = USB_USB_MCTRL_REG_USBEN_Msk;
tusb_vbus_changed((CRG_TOP->ANA_STATUS_REG & CRG_TOP_ANA_STATUS_REG_VBUS_AVAILABLE_Msk) != 0);
USB->USB_NFSR_REG = 0;
USB->USB_FAR_REG = 0x80;
USB->USB_NFSR_REG = NFSR_NODE_RESET;
USB->USB_TXMSK_REG = 0;
USB->USB_RXMSK_REG = 0;
USB->USB_MAMSK_REG = USB_USB_MAMSK_REG_USB_M_INTR_Msk |
USB_USB_MAMSK_REG_USB_M_ALT_Msk |
USB_USB_MAMSK_REG_USB_M_WARN_Msk;
USB->USB_ALTMSK_REG = USB_USB_ALTMSK_REG_USB_M_RESET_Msk;
dcd_connect(rhport);
}
@@ -865,6 +847,12 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
return true;
}
void dcd_edpt_close_all (uint8_t rhport)
{
(void) rhport;
// TODO implement dcd_edpt_close_all()
}
void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
{
uint8_t const epnum = tu_edpt_number(ep_addr);
+64 -45
View File
@@ -24,11 +24,9 @@
* This file is part of the TinyUSB stack.
*/
#include "common/tusb_common.h"
#include "host/hcd_attr.h"
#if TUSB_OPT_HOST_ENABLED && \
(CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || \
CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX )
#if TUSB_OPT_HOST_ENABLED && defined(HCD_ATTR_EHCI_TRANSDIMENSION)
//--------------------------------------------------------------------+
// INCLUDE
@@ -36,27 +34,44 @@
#include "osal/osal.h"
#include "host/hcd.h"
#include "host/usbh_hcd.h"
#include "hcd_ehci.h"
#include "ehci_api.h"
#include "ehci.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
// Debug level of EHCI
#define EHCI_DBG 2
// Framelist size as small as possible to save SRAM
#ifdef HCD_ATTR_EHCI_TRANSDIMENSION
// NXP Transdimension: 8 elements
#define FRAMELIST_SIZE_BIT_VALUE 7u
#define FRAMELIST_SIZE_USBCMD_VALUE (((FRAMELIST_SIZE_BIT_VALUE & 3) << EHCI_USBCMD_POS_FRAMELIST_SIZE) | \
((FRAMELIST_SIZE_BIT_VALUE >> 2) << EHCI_USBCMD_POS_NXP_FRAMELIST_SIZE_MSB))
#else
// STD EHCI: 256 elements
#define FRAMELIST_SIZE_BIT_VALUE 2u
#define FRAMELIST_SIZE_USBCMD_VALUE ((FRAMELIST_SIZE_BIT_VALUE & 3) << EHCI_USBCMD_POS_FRAMELIST_SIZE)
#endif
#define FRAMELIST_SIZE (1024 >> FRAMELIST_SIZE_BIT_VALUE)
typedef struct
{
ehci_link_t period_framelist[EHCI_FRAMELIST_SIZE];
ehci_link_t period_framelist[FRAMELIST_SIZE];
// for NXP ECHI, only implement 1 ms & 2 ms & 4 ms, 8 ms (framelist)
// TODO only implement 1 ms & 2 ms & 4 ms, 8 ms (framelist)
// [0] : 1ms, [1] : 2ms, [2] : 4ms, [3] : 8 ms
// TODO better implementation without dummy head to save SRAM
ehci_qhd_t period_head_arr[4];
// Note control qhd of dev0 is used as head of async list
struct {
ehci_qhd_t qhd;
ehci_qtd_t qtd;
}control[CFG_TUSB_HOST_DEVICE_MAX+1];
}control[CFG_TUH_DEVICE_MAX+CFG_TUH_HUB+1];
ehci_qhd_t qhd_pool[HCD_MAX_ENDPOINT];
ehci_qtd_t qtd_pool[HCD_MAX_XFER] TU_ATTR_ALIGNED(32);
@@ -65,19 +80,17 @@ typedef struct
volatile uint32_t uframe_number;
}ehci_data_t;
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
// Periodic frame list must be 4K alignment
CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4096) static ehci_data_t ehci_data;
//--------------------------------------------------------------------+
// PROTOTYPE
//--------------------------------------------------------------------+
static inline ehci_link_t* get_period_head(uint8_t rhport, uint8_t interval_ms)
static inline ehci_link_t* get_period_head(uint8_t rhport, uint32_t interval_ms)
{
(void) rhport;
return (ehci_link_t*) &ehci_data.period_head_arr[ tu_log2( tu_min8(EHCI_FRAMELIST_SIZE, interval_ms) ) ];
return (ehci_link_t*) &ehci_data.period_head_arr[ tu_log2( tu_min32(FRAMELIST_SIZE, interval_ms) ) ];
}
static inline ehci_qhd_t* qhd_control(uint8_t dev_addr)
@@ -109,7 +122,7 @@ static inline bool qhd_has_xact_error (ehci_qhd_t * p_qhd)
//p_qhd->qtd_overlay.non_hs_period_missed_uframe || p_qhd->qtd_overlay.pingstate_err TODO split transaction error
}
static void qhd_init (ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc);
static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc);
static inline ehci_qtd_t* qtd_find_free (void);
static inline ehci_qtd_t* qtd_next (ehci_qtd_t const * p_qtd);
@@ -218,12 +231,13 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr)
ehci_data.regs->command_bm.async_adv_doorbell = 1;
}
// EHCI controller init
bool hcd_ehci_init(uint8_t rhport)
bool ehci_init(uint8_t rhport, uint32_t capability_reg, uint32_t operatial_reg)
{
(void) capability_reg; // not used yet
tu_memclr(&ehci_data, sizeof(ehci_data_t));
ehci_data.regs = (ehci_registers_t* ) hcd_ehci_register_addr(rhport);
ehci_data.regs = (ehci_registers_t* ) operatial_reg;
ehci_registers_t* regs = ehci_data.regs;
@@ -249,37 +263,38 @@ bool hcd_ehci_init(uint8_t rhport)
//------------- Periodic List -------------//
// Build the polling interval tree with 1 ms, 2 ms, 4 ms and 8 ms (framesize) only
for(uint32_t i=0; i<4; i++)
for ( uint32_t i = 0; i < TU_ARRAY_SIZE(ehci_data.period_head_arr); i++ )
{
ehci_data.period_head_arr[i].int_smask = 1; // queue head in period list must have smask non-zero
ehci_data.period_head_arr[i].int_smask = 1; // queue head in period list must have smask non-zero
ehci_data.period_head_arr[i].qtd_overlay.halted = 1; // dummy node, always inactive
}
ehci_link_t * const framelist = ehci_data.period_framelist;
ehci_link_t * const period_1ms = get_period_head(rhport, 1);
ehci_link_t * const period_1ms = get_period_head(rhport, 1u);
// all links --> period_head_arr[0] (1ms)
// 0, 2, 4, 6 etc --> period_head_arr[1] (2ms)
// 1, 5 --> period_head_arr[2] (4ms)
// 3 --> period_head_arr[3] (8ms)
// TODO EHCI_FRAMELIST_SIZE with other size than 8
for(uint32_t i=0; i<EHCI_FRAMELIST_SIZE; i++)
for(uint32_t i=0; i<FRAMELIST_SIZE; i++)
{
framelist[i].address = (uint32_t) period_1ms;
framelist[i].type = EHCI_QTYPE_QHD;
}
for(uint32_t i=0; i<EHCI_FRAMELIST_SIZE; i+=2)
for(uint32_t i=0; i<FRAMELIST_SIZE; i+=2)
{
list_insert(framelist + i, get_period_head(rhport, 2), EHCI_QTYPE_QHD);
list_insert(framelist + i, get_period_head(rhport, 2u), EHCI_QTYPE_QHD);
}
for(uint32_t i=1; i<EHCI_FRAMELIST_SIZE; i+=4)
for(uint32_t i=1; i<FRAMELIST_SIZE; i+=4)
{
list_insert(framelist + i, get_period_head(rhport, 4), EHCI_QTYPE_QHD);
list_insert(framelist + i, get_period_head(rhport, 4u), EHCI_QTYPE_QHD);
}
list_insert(framelist+3, get_period_head(rhport, 8), EHCI_QTYPE_QHD);
list_insert(framelist+3, get_period_head(rhport, 8u), EHCI_QTYPE_QHD);
period_1ms->terminate = 1;
@@ -289,10 +304,9 @@ bool hcd_ehci_init(uint8_t rhport)
regs->nxp_tt_control = 0;
//------------- USB CMD Register -------------//
regs->command |= TU_BIT(EHCI_USBCMD_POS_RUN_STOP) | TU_BIT(EHCI_USBCMD_POS_ASYNC_ENABLE)
| TU_BIT(EHCI_USBCMD_POS_PERIOD_ENABLE) // TODO enable period list only there is int/iso endpoint
| ((EHCI_CFG_FRAMELIST_SIZE_BITS & TU_BIN8(011)) << EHCI_USBCMD_POS_FRAMELIST_SZIE)
| ((EHCI_CFG_FRAMELIST_SIZE_BITS >> 2) << EHCI_USBCMD_POS_NXP_FRAMELIST_SIZE_MSB);
regs->command |= TU_BIT(EHCI_USBCMD_POS_RUN_STOP) | TU_BIT(EHCI_USBCMD_POS_ASYNC_ENABLE) |
TU_BIT(EHCI_USBCMD_POS_PERIOD_ENABLE) | // TODO enable period list only there is int/iso endpoint
FRAMELIST_SIZE_USBCMD_VALUE;
//------------- ConfigFlag Register (skip) -------------//
regs->portsc_bm.port_power = 1; // enable port power
@@ -470,16 +484,16 @@ static void async_advance_isr(uint8_t rhport)
}
}
static void port_connect_status_change_isr(uint8_t hostid)
static void port_connect_status_change_isr(uint8_t rhport)
{
// NOTE There is an sequence plug->unplug->…..-> plug if device is powering with pre-plugged device
if (ehci_data.regs->portsc_bm.current_connect_status)
{
hcd_port_reset(hostid);
hcd_event_device_attach(hostid, true);
hcd_port_reset(rhport);
hcd_event_device_attach(rhport, true);
}else // device unplugged
{
hcd_event_device_remove(hostid, true);
hcd_event_device_remove(rhport, true);
}
}
@@ -519,16 +533,16 @@ static void async_list_xfer_complete_isr(ehci_qhd_t * const async_head)
}while(p_qhd != async_head); // async list traversal, stop if loop around
}
static void period_list_xfer_complete_isr(uint8_t hostid, uint8_t interval_ms)
static void period_list_xfer_complete_isr(uint8_t hostid, uint32_t interval_ms)
{
uint16_t max_loop = 0;
uint32_t const period_1ms_addr = (uint32_t) get_period_head(hostid, 1);
uint32_t const period_1ms_addr = (uint32_t) get_period_head(hostid, 1u);
ehci_link_t next_item = * get_period_head(hostid, interval_ms);
// TODO abstract max loop guard for period
while( !next_item.terminate &&
!(interval_ms > 1 && period_1ms_addr == tu_align32(next_item.address)) &&
max_loop < (HCD_MAX_ENDPOINT + EHCI_MAX_ITD + EHCI_MAX_SITD)*CFG_TUSB_HOST_DEVICE_MAX)
max_loop < (HCD_MAX_ENDPOINT + EHCI_MAX_ITD + EHCI_MAX_SITD)*CFG_TUH_DEVICE_MAX)
{
switch ( next_item.type )
{
@@ -605,8 +619,8 @@ static void xfer_error_isr(uint8_t hostid)
}while(p_qhd != async_head); // async list traversal, stop if loop around
//------------- TODO refractor period list -------------//
uint32_t const period_1ms_addr = (uint32_t) get_period_head(hostid, 1);
for (uint8_t interval_ms=1; interval_ms <= EHCI_FRAMELIST_SIZE; interval_ms *= 2)
uint32_t const period_1ms_addr = (uint32_t) get_period_head(hostid, 1u);
for (uint32_t interval_ms=1; interval_ms <= FRAMELIST_SIZE; interval_ms *= 2)
{
ehci_link_t next_item = * get_period_head(hostid, interval_ms);
@@ -649,13 +663,15 @@ void hcd_int_handler(uint8_t rhport)
if (int_status & EHCI_INT_MASK_FRAMELIST_ROLLOVER)
{
ehci_data.uframe_number += (EHCI_FRAMELIST_SIZE << 3);
ehci_data.uframe_number += (FRAMELIST_SIZE << 3);
}
if (int_status & EHCI_INT_MASK_PORT_CHANGE)
{
uint32_t port_status = regs->portsc & EHCI_PORTSC_MASK_ALL;
TU_LOG_HEX(EHCI_DBG, regs->portsc);
if (regs->portsc_bm.connect_status_change)
{
port_connect_status_change_isr(rhport);
@@ -677,7 +693,7 @@ void hcd_int_handler(uint8_t rhport)
if (int_status & EHCI_INT_MASK_NXP_PERIODIC)
{
for (uint8_t i=1; i <= EHCI_FRAMELIST_SIZE; i *= 2)
for (uint32_t i=1; i <= FRAMELIST_SIZE; i *= 2)
{
period_list_xfer_complete_isr( rhport, i );
}
@@ -774,13 +790,16 @@ static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t c
tu_memclr(p_qhd, sizeof(ehci_qhd_t));
}
hcd_devtree_info_t devtree_info;
hcd_devtree_get_info(dev_addr, &devtree_info);
uint8_t const xfer_type = ep_desc->bmAttributes.xfer;
uint8_t const interval = ep_desc->bInterval;
p_qhd->dev_addr = dev_addr;
p_qhd->fl_inactive_next_xact = 0;
p_qhd->ep_number = tu_edpt_number(ep_desc->bEndpointAddress);
p_qhd->ep_speed = _usbh_devices[dev_addr].speed;
p_qhd->ep_speed = devtree_info.speed;
p_qhd->data_toggle_control= (xfer_type == TUSB_XFER_CONTROL) ? 1 : 0;
p_qhd->head_list_flag = (dev_addr == 0) ? 1 : 0; // addr0's endpoint is the static asyn list head
p_qhd->max_packet_size = ep_desc->wMaxPacketSize.size;
@@ -817,8 +836,8 @@ static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t c
p_qhd->int_smask = p_qhd->fl_int_cmask = 0;
}
p_qhd->fl_hub_addr = _usbh_devices[dev_addr].hub_addr;
p_qhd->fl_hub_port = _usbh_devices[dev_addr].hub_port;
p_qhd->fl_hub_addr = devtree_info.hub_addr;
p_qhd->fl_hub_port = devtree_info.hub_port;
p_qhd->mult = 1; // TODO not use high bandwidth/park mode yet
//------------- HCD Management Data -------------//
+2 -7
View File
@@ -46,8 +46,6 @@
//--------------------------------------------------------------------+
// EHCI CONFIGURATION & CONSTANTS
//--------------------------------------------------------------------+
#define EHCI_CFG_FRAMELIST_SIZE_BITS 7 /// Framelist Size (NXP specific) (0:1024) - (1:512) - (2:256) - (3:128) - (4:64) - (5:32) - (6:16) - (7:8)
#define EHCI_FRAMELIST_SIZE (1024 >> EHCI_CFG_FRAMELIST_SIZE_BITS)
// TODO merge OHCI with EHCI
enum {
@@ -55,9 +53,6 @@ enum {
EHCI_MAX_SITD = 16
};
//------------- Validation -------------//
TU_VERIFY_STATIC(EHCI_CFG_FRAMELIST_SIZE_BITS <= 7, "incorrect value");
//--------------------------------------------------------------------+
// EHCI Data Structure
//--------------------------------------------------------------------+
@@ -294,7 +289,7 @@ enum ehci_interrupt_mask_{
enum ehci_usbcmd_pos_ {
EHCI_USBCMD_POS_RUN_STOP = 0,
EHCI_USBCMD_POS_FRAMELIST_SZIE = 2,
EHCI_USBCMD_POS_FRAMELIST_SIZE = 2,
EHCI_USBCMD_POS_PERIOD_ENABLE = 4,
EHCI_USBCMD_POS_ASYNC_ENABLE = 5,
EHCI_USBCMD_POS_NXP_FRAMELIST_SIZE_MSB = 15,
@@ -411,7 +406,7 @@ typedef volatile struct
uint32_t wake_on_over_current_enable : 1; ///< Enables over-current conditions as wake-up events
uint32_t nxp_phy_clock_disable : 1; ///< NXP customized: the PHY can be put into Low Power Suspend Clock Disable when the downstream device has been put into suspend mode or when no downstream device is connected. Low power suspend is completely under the control of software. 0: enable PHY clock, 1: disable PHY clock
uint32_t nxp_port_force_fullspeed : 1; ///< NXP customized: Writing this bit to a 1 will force the port to only connect at Full Speed. It disables the chirp sequence that allowsthe port to identify itself as High Speed. This is useful for testing FS configurations with a HS host, hub or device.
uint32_t : 1;
uint32_t TU_RESERVED : 1;
uint32_t nxp_port_speed : 2; ///< NXP customized: This register field indicates the speed atwhich the port is operating. For HS mode operation in the host controllerand HS/FS operation in the device controller the port routing steers data to the Protocol engine. For FS and LS mode operation in the host controller, the port routing steers data to the Protocol Engine w/ Embedded Transaction Translator. 0x0: Fullspeed, 0x1: Lowspeed, 0x2: Highspeed
uint32_t TU_RESERVED : 4;
}portsc_bm;
@@ -24,27 +24,19 @@
* This file is part of the TinyUSB stack.
*/
#ifndef _TUSB_HCD_EHCI_H_
#define _TUSB_HCD_EHCI_H_
#ifndef _TUSB_EHCI_API_H_
#define _TUSB_EHCI_API_H_
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------+
// API Implemented by HCD
//--------------------------------------------------------------------+
// Get operational address i.e EHCI Command register
uint32_t hcd_ehci_register_addr(uint8_t rhport);
//--------------------------------------------------------------------+
// API Implemented by EHCI
//--------------------------------------------------------------------+
// Initialize EHCI driver
extern bool hcd_ehci_init (uint8_t rhport);
bool ehci_init(uint8_t rhport, uint32_t capability_reg, uint32_t operatial_reg);
#ifdef __cplusplus
}
+52 -64
View File
@@ -42,10 +42,6 @@
#include "device/dcd.h"
// Since TinyUSB doesn't use SOF for now, and this interrupt too often (1ms interval)
// We disable SOF for now until needed later on
#define USE_SOF 0
// Max number of bi-directional endpoints including EP0
// Note: ESP32S2 specs say there are only up to 5 IN active endpoints include EP0
// We should probably prohibit enabling Endpoint IN > 4 (not done yet)
@@ -92,11 +88,12 @@ static void bus_reset(void)
USB0.out_ep_reg[ep_num].doepctl |= USB_DO_SNAK0_M; // DOEPCTL0_SNAK
}
USB0.dcfg &= ~USB_DEVADDR_M; // reset address
// clear device address
USB0.dcfg &= ~USB_DEVADDR_M;
USB0.daintmsk |= USB_OUTEPMSK0_M | USB_INEPMSK0_M;
USB0.doepmsk |= USB_SETUPMSK_M | USB_XFERCOMPLMSK;
USB0.diepmsk |= USB_TIMEOUTMSK_M | USB_DI_XFERCOMPLMSK_M /*| USB_INTKNTXFEMPMSK_M*/;
USB0.daintmsk = USB_OUTEPMSK0_M | USB_INEPMSK0_M;
USB0.doepmsk = USB_SETUPMSK_M | USB_XFERCOMPLMSK;
USB0.diepmsk = USB_TIMEOUTMSK_M | USB_DI_XFERCOMPLMSK_M /*| USB_INTKNTXFEMPMSK_M*/;
// "USB Data FIFOs" section in reference manual
// Peripheral FIFO architecture
@@ -193,9 +190,6 @@ void dcd_init(uint8_t rhport)
USB0.gintsts = ~0U; //clear pending ints
USB0.gintmsk = USB_OTGINTMSK_M |
USB_MODEMISMSK_M |
#if USE_SOF
USB_SOFMSK_M |
#endif
USB_RXFLVIMSK_M |
USB_ERLYSUSPMSK_M |
USB_USBSUSPMSK_M |
@@ -220,8 +214,17 @@ void dcd_remote_wakeup(uint8_t rhport)
{
(void)rhport;
// TODO must manually clear this bit after 1-15 ms
// USB0.DCTL |= USB_RMTWKUPSIG_M;
// set remote wakeup
USB0.dctl |= USB_RMTWKUPSIG_M;
// enable SOF to detect bus resume
USB0.gintsts = USB_SOF_M;
USB0.gintmsk |= USB_SOFMSK_M;
// Per specs: remote wakeup signal bit must be clear within 1-15ms
vTaskDelay(pdMS_TO_TICKS(1));
USB0.dctl &= ~USB_RMTWKUPSIG_M;
}
// connect by enabling internal pull-up resistor on D+/D-
@@ -260,9 +263,10 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_edpt)
xfer->max_size = desc_edpt->wMaxPacketSize.size;
if (dir == TUSB_DIR_OUT) {
out_ep[epnum].doepctl |= USB_USBACTEP0_M |
desc_edpt->bmAttributes.xfer << USB_EPTYPE0_S |
desc_edpt->wMaxPacketSize.size << USB_MPS0_S;
out_ep[epnum].doepctl |= USB_USBACTEP1_M |
desc_edpt->bmAttributes.xfer << USB_EPTYPE1_S |
(desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? USB_DO_SETD0PID1_M : 0) |
desc_edpt->wMaxPacketSize.size << USB_MPS1_S;
USB0.daintmsk |= (1 << (16 + epnum));
} else {
// "USB Data FIFOs" section in reference manual
@@ -312,6 +316,30 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_edpt)
return true;
}
void dcd_edpt_close_all(uint8_t rhport)
{
(void) rhport;
usb_out_endpoint_t *out_ep = &(USB0.out_ep_reg[0]);
usb_in_endpoint_t *in_ep = &(USB0.in_ep_reg[0]);
// Disable non-control interrupt
USB0.daintmsk = USB_OUTEPMSK0_M | USB_INEPMSK0_M;
for(uint8_t n = 1; n < EP_MAX; n++)
{
// disable OUT endpoint
out_ep[n].doepctl = 0;
xfer_status[n][TUSB_DIR_OUT].max_size = 0;
// disable IN endpoint
in_ep[n].diepctl = 0;
xfer_status[n][TUSB_DIR_IN].max_size = 0;
}
_allocated_fifos = 1;
}
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes)
{
(void)rhport;
@@ -361,49 +389,6 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t to
bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
{
(void)rhport;
// USB buffers always work in bytes so to avoid unnecessary divisions we demand item_size = 1
TU_ASSERT(ff->item_size == 1);
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
xfer->buffer = NULL;
xfer->ff = ff;
xfer->total_len = total_bytes;
xfer->queued_len = 0;
xfer->short_packet = false;
uint16_t num_packets = (total_bytes / xfer->max_size);
uint8_t short_packet_size = total_bytes % xfer->max_size;
// Zero-size packet is special case.
if (short_packet_size > 0 || (total_bytes == 0)) {
num_packets++;
}
ESP_LOGV(TAG, "Transfer <-> EP%i, %s, pkgs: %i, bytes: %i",
epnum, ((dir == TUSB_DIR_IN) ? "USB0.HOST (in)" : "HOST->DEV (out)"),
num_packets, total_bytes);
// IN and OUT endpoint xfers are interrupt-driven, we just schedule them
// here.
if (dir == TUSB_DIR_IN) {
// A full IN transfer (multiple packets, possibly) triggers XFRC.
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
// Enable fifo empty interrupt only if there are something to put in the fifo.
if(total_bytes != 0) {
USB0.dtknqr4_fifoemptymsk |= (1 << epnum);
}
} else {
// 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;
}
return true;
}
#endif
@@ -748,8 +733,8 @@ static void _dcd_int_handler(void* arg)
(void) arg;
uint8_t const rhport = 0;
const uint32_t int_status = USB0.gintsts;
//const uint32_t int_msk = USB0.gintmsk;
const uint32_t int_msk = USB0.gintmsk;
const uint32_t int_status = USB0.gintsts & int_msk;
if (int_status & USB_USBRST_M) {
// start of reset
@@ -802,12 +787,15 @@ static void _dcd_int_handler(void* arg)
USB0.gotgint = otg_int;
}
#if USE_SOF
if (int_status & USB_SOF_M) {
USB0.gintsts = USB_SOF_M;
dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true); // do nothing actually
// Disable SOF interrupt since currently only used for remote wakeup detection
USB0.gintmsk &= ~USB_SOFMSK_M;
dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
}
#endif
if (int_status & USB_RXFLVI_M) {
// RXFLVL bit is read-only
+10 -2
View File
@@ -29,7 +29,7 @@
#if TUSB_OPT_DEVICE_ENABLED && \
(CFG_TUSB_MCU == OPT_MCU_SAMD11 || CFG_TUSB_MCU == OPT_MCU_SAMD21 || \
CFG_TUSB_MCU == OPT_MCU_SAMD51 || CFG_TUSB_MCU == OPT_MCU_SAME5X || \
CFG_TUSB_MCU == OPT_MCU_SAML22)
CFG_TUSB_MCU == OPT_MCU_SAML22 || CFG_TUSB_MCU == OPT_MCU_SAML21)
#include "sam.h"
#include "device/dcd.h"
@@ -125,7 +125,7 @@ void dcd_int_disable(uint8_t rhport)
}
#elif CFG_TUSB_MCU == OPT_MCU_SAMD11 || CFG_TUSB_MCU == OPT_MCU_SAMD21 || \
CFG_TUSB_MCU == OPT_MCU_SAML22
CFG_TUSB_MCU == OPT_MCU_SAML22 || CFG_TUSB_MCU == OPT_MCU_SAML21
void dcd_int_enable(uint8_t rhport)
{
@@ -230,16 +230,24 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
if ( dir == TUSB_DIR_OUT )
{
ep->EPCFG.bit.EPTYPE0 = desc_edpt->bmAttributes.xfer + 1;
ep->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ0 | USB_DEVICE_EPSTATUSCLR_DTGLOUT; // clear stall & dtoggle
ep->EPINTENSET.bit.TRCPT0 = true;
}else
{
ep->EPCFG.bit.EPTYPE1 = desc_edpt->bmAttributes.xfer + 1;
ep->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ1 | USB_DEVICE_EPSTATUSCLR_DTGLIN; // clear stall & dtoggle
ep->EPINTENSET.bit.TRCPT1 = true;
}
return true;
}
void dcd_edpt_close_all (uint8_t rhport)
{
(void) rhport;
// TODO implement dcd_edpt_close_all()
}
bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
{
(void) rhport;
+6
View File
@@ -269,6 +269,12 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
return true;
}
void dcd_edpt_close_all (uint8_t rhport)
{
(void) rhport;
// TODO implement dcd_edpt_close_all()
}
// Submit a transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack
bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
{
File diff suppressed because it is too large Load Diff
+768
View File
@@ -0,0 +1,768 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2018, hathach (tinyusb.org)
* Copyright (c) 2021, HiFiPhile
*
* 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 TUSB_OPT_DEVICE_ENABLED && CFG_TUSB_MCU == OPT_MCU_SAMX7X
#include "device/dcd.h"
#include "sam.h"
#include "common_usb_regs.h"
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
//--------------------------------------------------------------------+
// Since TinyUSB doesn't use SOF for now, and this interrupt too often (1ms interval)
// We disable SOF for now until needed later on
#ifndef USE_SOF
# define USE_SOF 0
#endif
// Dual bank can imporve 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
# if TUD_OPT_HIGH_SPEED
# define USE_DUAL_BANK 0
# else
# define USE_DUAL_BANK 1
# endif
#endif
#define EP_GET_FIFO_PTR(ep, scale) (((TU_XSTRCAT(TU_STRCAT(uint, scale),_t) (*)[0x8000 / ((scale) / 8)])FIFO_RAM_ADDR)[(ep)])
// DMA Channel Transfer Descriptor
typedef struct {
volatile uint32_t next_desc;
volatile uint32_t buff_addr;
volatile uint32_t chnl_ctrl;
uint32_t padding;
} dma_desc_t;
// Transfer control context
typedef struct {
uint8_t * buffer;
uint16_t total_len;
uint16_t queued_len;
uint16_t max_packet_size;
uint8_t interval;
tu_fifo_t * fifo;
} xfer_ctl_t;
static tusb_speed_t get_speed(void);
static void dcd_transmit_packet(xfer_ctl_t * xfer, uint8_t ep_ix);
// DMA descriptors shouldn't be placed in ITCM !
CFG_TUSB_MEM_SECTION static dma_desc_t dma_desc[6];
static xfer_ctl_t xfer_status[EP_MAX];
static const tusb_desc_endpoint_t ep0_desc =
{
.bEndpointAddress = 0x00,
.wMaxPacketSize = { .size = CFG_TUD_ENDPOINT0_SIZE },
};
TU_ATTR_ALWAYS_INLINE static inline void CleanInValidateCache(uint32_t *addr, int32_t size)
{
if (SCB->CCR & SCB_CCR_DC_Msk)
{
SCB_CleanInvalidateDCache_by_Addr(addr, size);
}
else
{
__DSB();
__ISB();
}
}
//------------------------------------------------------------------
// Device API
//------------------------------------------------------------------
// Initialize controller to device mode
void dcd_init (uint8_t rhport)
{
dcd_connect(rhport);
}
// Enable device interrupt
void dcd_int_enable (uint8_t rhport)
{
(void) rhport;
NVIC_EnableIRQ((IRQn_Type) ID_USBHS);
}
// Disable device interrupt
void dcd_int_disable (uint8_t rhport)
{
(void) rhport;
NVIC_DisableIRQ((IRQn_Type) ID_USBHS);
}
// Receive Set Address request, mcu port must also include status IN response
void dcd_set_address (uint8_t rhport, uint8_t dev_addr)
{
(void) dev_addr;
// DCD can only set address after status for this request is complete
// do it at dcd_edpt0_status_complete()
// Response with zlp status
dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
}
// Wake up host
void dcd_remote_wakeup (uint8_t rhport)
{
(void) rhport;
USB_REG->DEVCTRL |= DEVCTRL_RMWKUP;
}
// Connect by enabling internal pull-up resistor on D+/D-
void dcd_connect(uint8_t rhport)
{
(void) rhport;
dcd_int_disable(rhport);
// Enable the USB controller in device mode
USB_REG->CTRL = CTRL_UIMOD | CTRL_USBE;
while (!(USB_REG->SR & SR_CLKUSABLE));
#if TUD_OPT_HIGH_SPEED
USB_REG->DEVCTRL &= ~DEVCTRL_SPDCONF;
#else
USB_REG->DEVCTRL |= DEVCTRL_SPDCONF_LOW_POWER;
#endif
// Enable the End Of Reset, Suspend & Wakeup interrupts
USB_REG->DEVIER = (DEVIER_EORSTES | DEVIER_SUSPES | DEVIER_WAKEUPES);
#if USE_SOF
USB_REG->DEVIER = DEVIER_SOFES;
#endif
// Clear the End Of Reset, SOF & Wakeup interrupts
USB_REG->DEVICR = (DEVICR_EORSTC | DEVICR_SOFC | DEVICR_WAKEUPC);
// Manually set the Suspend Interrupt
USB_REG->DEVIFR |= DEVIFR_SUSPS;
// Ack the Wakeup Interrupt
USB_REG->DEVICR = DEVICR_WAKEUPC;
// Attach the device
USB_REG->DEVCTRL &= ~DEVCTRL_DETACH;
// Freeze USB clock
USB_REG->CTRL |= CTRL_FRZCLK;
}
// Disconnect by disabling internal pull-up resistor on D+/D-
void dcd_disconnect(uint8_t rhport)
{
(void) rhport;
dcd_int_disable(rhport);
// Disable all endpoints
USB_REG->DEVEPT &= ~(0x3FF << DEVEPT_EPEN0_Pos);
// Unfreeze USB clock
USB_REG->CTRL &= ~CTRL_FRZCLK;
while (!(USB_REG->SR & SR_CLKUSABLE));
// Clear all the pending interrupts
USB_REG->DEVICR = DEVICR_Msk;
// Disable all interrupts
USB_REG->DEVIDR = DEVIDR_Msk;
// Detach the device
USB_REG->DEVCTRL |= DEVCTRL_DETACH;
// Disable the device address
USB_REG->DEVCTRL &=~(DEVCTRL_ADDEN | DEVCTRL_UADD);
}
static tusb_speed_t get_speed(void)
{
switch (USB_REG->SR & SR_SPEED) {
case SR_SPEED_FULL_SPEED:
default:
return TUSB_SPEED_FULL;
case SR_SPEED_HIGH_SPEED:
return TUSB_SPEED_HIGH;
case SR_SPEED_LOW_SPEED:
return TUSB_SPEED_LOW;
}
}
static void dcd_ep_handler(uint8_t ep_ix)
{
uint32_t int_status = USB_REG->DEVEPTISR[ep_ix];
int_status &= USB_REG->DEVEPTIMR[ep_ix];
uint16_t count = (USB_REG->DEVEPTISR[ep_ix] &
DEVEPTISR_BYCT) >> DEVEPTISR_BYCT_Pos;
xfer_ctl_t *xfer = &xfer_status[ep_ix];
if (ep_ix == 0U)
{
static uint8_t ctrl_dir;
if (int_status & DEVEPTISR_CTRL_RXSTPI)
{
ctrl_dir = (USB_REG->DEVEPTISR[0] & DEVEPTISR_CTRL_CTRLDIR) >> DEVEPTISR_CTRL_CTRLDIR_Pos;
// Setup packet should always be 8 bytes. If not, ignore it, and try again.
if (count == 8)
{
uint8_t *ptr = EP_GET_FIFO_PTR(0,8);
dcd_event_setup_received(0, ptr, true);
}
// Ack and disable SETUP interrupt
USB_REG->DEVEPTICR[0] = DEVEPTICR_CTRL_RXSTPIC;
USB_REG->DEVEPTIDR[0] = DEVEPTIDR_CTRL_RXSTPEC;
}
if (int_status & DEVEPTISR_RXOUTI)
{
uint8_t *ptr = EP_GET_FIFO_PTR(0,8);
if (count && xfer->total_len)
{
uint16_t remain = xfer->total_len - xfer->queued_len;
if (count > remain)
{
count = remain;
}
if (xfer->buffer)
{
memcpy(xfer->buffer + xfer->queued_len, ptr, count);
} else
{
tu_fifo_write_n(xfer->fifo, ptr, count);
}
xfer->queued_len = (uint16_t)(xfer->queued_len + count);
}
// Acknowledge the interrupt
USB_REG->DEVEPTICR[0] = DEVEPTICR_RXOUTIC;
if ((count < xfer->max_packet_size) || (xfer->queued_len == xfer->total_len))
{
// RX COMPLETE
dcd_event_xfer_complete(0, 0, xfer->queued_len, XFER_RESULT_SUCCESS, true);
// Disable the interrupt
USB_REG->DEVEPTIDR[0] = DEVEPTIDR_RXOUTEC;
// Re-enable SETUP interrupt
if (ctrl_dir == 1)
{
USB_REG->DEVEPTIER[0] = DEVEPTIER_CTRL_RXSTPES;
}
}
}
if (int_status & DEVEPTISR_TXINI)
{
// Disable the interrupt
USB_REG->DEVEPTIDR[0] = DEVEPTIDR_TXINEC;
if ((xfer->total_len != xfer->queued_len))
{
// TX not complete
dcd_transmit_packet(xfer, 0);
} else
{
// TX complete
dcd_event_xfer_complete(0, 0x80 + 0, xfer->total_len, XFER_RESULT_SUCCESS, true);
// Re-enable SETUP interrupt
if (ctrl_dir == 0)
{
USB_REG->DEVEPTIER[0] = DEVEPTIER_CTRL_RXSTPES;
}
}
}
} else
{
if (int_status & DEVEPTISR_RXOUTI)
{
if (count && xfer->total_len)
{
uint16_t remain = xfer->total_len - xfer->queued_len;
if (count > remain)
{
count = remain;
}
uint8_t *ptr = EP_GET_FIFO_PTR(ep_ix,8);
if (xfer->buffer)
{
memcpy(xfer->buffer + xfer->queued_len, ptr, count);
} else {
tu_fifo_write_n(xfer->fifo, ptr, count);
}
xfer->queued_len = (uint16_t)(xfer->queued_len + count);
}
// Clear the FIFO control flag to receive more data.
USB_REG->DEVEPTIDR[ep_ix] = DEVEPTIDR_FIFOCONC;
// Acknowledge the interrupt
USB_REG->DEVEPTICR[ep_ix] = DEVEPTICR_RXOUTIC;
if ((count < xfer->max_packet_size) || (xfer->queued_len == xfer->total_len))
{
// RX COMPLETE
dcd_event_xfer_complete(0, ep_ix, xfer->queued_len, XFER_RESULT_SUCCESS, true);
// Disable the interrupt
USB_REG->DEVEPTIDR[ep_ix] = DEVEPTIDR_RXOUTEC;
// Though the host could still send, we don't know.
}
}
if (int_status & DEVEPTISR_TXINI)
{
// Acknowledge the interrupt
USB_REG->DEVEPTICR[ep_ix] = DEVEPTICR_TXINIC;
if ((xfer->total_len != xfer->queued_len))
{
// TX not complete
dcd_transmit_packet(xfer, ep_ix);
} else
{
// TX complete
dcd_event_xfer_complete(0, 0x80 + ep_ix, xfer->total_len, XFER_RESULT_SUCCESS, true);
// Disable the interrupt
USB_REG->DEVEPTIDR[ep_ix] = DEVEPTIDR_TXINEC;
}
}
}
}
static void dcd_dma_handler(uint8_t ep_ix)
{
uint32_t status = USB_REG->DEVDMA[ep_ix - 1].DEVDMASTATUS;
if (status & DEVDMASTATUS_CHANN_ENB)
{
return; // Ignore EOT_STA interrupt
}
// Disable DMA interrupt
USB_REG->DEVIDR = DEVIDR_DMA_1 << (ep_ix - 1);
xfer_ctl_t *xfer = &xfer_status[ep_ix];
uint16_t count = xfer->total_len - ((status & DEVDMASTATUS_BUFF_COUNT) >> DEVDMASTATUS_BUFF_COUNT_Pos);
if(USB_REG->DEVEPTCFG[ep_ix] & DEVEPTCFG_EPDIR)
{
dcd_event_xfer_complete(0, 0x80 + ep_ix, count, XFER_RESULT_SUCCESS, true);
} else
{
dcd_event_xfer_complete(0, ep_ix, count, XFER_RESULT_SUCCESS, true);
}
}
void dcd_int_handler(uint8_t rhport)
{
(void) rhport;
uint32_t int_status = USB_REG->DEVISR;
int_status &= USB_REG->DEVIMR;
// End of reset interrupt
if (int_status & DEVISR_EORST)
{
// Unfreeze USB clock
USB_REG->CTRL &= ~CTRL_FRZCLK;
while(!(USB_REG->SR & SR_CLKUSABLE));
// Reset all endpoints
for (int ep_ix = 1; ep_ix < EP_MAX; ep_ix++)
{
USB_REG->DEVEPT |= 1 << (DEVEPT_EPRST0_Pos + ep_ix);
USB_REG->DEVEPT &=~(1 << (DEVEPT_EPRST0_Pos + ep_ix));
}
dcd_edpt_open (0, &ep0_desc);
USB_REG->DEVICR = DEVICR_EORSTC;
USB_REG->DEVICR = DEVICR_WAKEUPC;
USB_REG->DEVICR = DEVICR_SUSPC;
USB_REG->DEVIER = DEVIER_SUSPES;
dcd_event_bus_reset(rhport, get_speed(), true);
}
// End of Wakeup interrupt
if (int_status & DEVISR_WAKEUP)
{
USB_REG->CTRL &= ~CTRL_FRZCLK;
while (!(USB_REG->SR & SR_CLKUSABLE));
USB_REG->DEVICR = DEVICR_WAKEUPC;
USB_REG->DEVIDR = DEVIDR_WAKEUPEC;
USB_REG->DEVIER = DEVIER_SUSPES;
dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
}
// Suspend interrupt
if (int_status & DEVISR_SUSP)
{
// Unfreeze USB clock
USB_REG->CTRL &= ~CTRL_FRZCLK;
while (!(USB_REG->SR & SR_CLKUSABLE));
USB_REG->DEVICR = DEVICR_SUSPC;
USB_REG->DEVIDR = DEVIDR_SUSPEC;
USB_REG->DEVIER = DEVIER_WAKEUPES;
USB_REG->CTRL |= CTRL_FRZCLK;
dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
}
#if USE_SOF
if(int_status & DEVISR_SOF)
{
USB_REG->DEVICR = DEVICR_SOFC;
dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
}
#endif
// Endpoints interrupt
for (int ep_ix = 0; ep_ix < EP_MAX; ep_ix++)
{
if (int_status & (DEVISR_PEP_0 << ep_ix))
{
dcd_ep_handler(ep_ix);
}
}
// Endpoints DMA interrupt
for (int ep_ix = 0; ep_ix < EP_MAX; ep_ix++)
{
if (EP_DMA_SUPPORT(ep_ix))
{
if (int_status & (DEVISR_DMA_1 << (ep_ix - 1)))
{
dcd_dma_handler(ep_ix);
}
}
}
}
//--------------------------------------------------------------------+
// Endpoint API
//--------------------------------------------------------------------+
// Invoked when a control transfer's status stage is complete.
// May help DCD to prepare for next control transfer, this API is optional.
void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request)
{
(void) rhport;
if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE &&
request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD &&
request->bRequest == TUSB_REQ_SET_ADDRESS )
{
uint8_t const dev_addr = (uint8_t) request->wValue;
USB_REG->DEVCTRL |= dev_addr | DEVCTRL_ADDEN;
}
}
// Configure endpoint's registers according to descriptor
bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
{
(void) rhport;
uint8_t const epnum = tu_edpt_number(ep_desc->bEndpointAddress);
uint8_t const dir = tu_edpt_dir(ep_desc->bEndpointAddress);
uint16_t const epMaxPktSize = ep_desc->wMaxPacketSize.size;
tusb_xfer_type_t const eptype = (tusb_xfer_type_t)ep_desc->bmAttributes.xfer;
uint8_t fifoSize = 0; // FIFO size
uint16_t defaultEndpointSize = 8; // Default size of Endpoint
// Find upper 2 power number of epMaxPktSize
if (epMaxPktSize)
{
while (defaultEndpointSize < epMaxPktSize)
{
fifoSize++;
defaultEndpointSize <<= 1;
}
}
xfer_status[epnum].max_packet_size = epMaxPktSize;
USB_REG->DEVEPT |= 1 << (DEVEPT_EPRST0_Pos + epnum);
USB_REG->DEVEPT &=~(1 << (DEVEPT_EPRST0_Pos + epnum));
if (epnum == 0)
{
// Enable the control endpoint - Endpoint 0
USB_REG->DEVEPT |= DEVEPT_EPEN0;
// Configure the Endpoint 0 configuration register
USB_REG->DEVEPTCFG[0] =
(
(fifoSize << DEVEPTCFG_EPSIZE_Pos) |
(TUSB_XFER_CONTROL << DEVEPTCFG_EPTYPE_Pos) |
(DEVEPTCFG_EPBK_1_BANK << DEVEPTCFG_EPBK_Pos) |
DEVEPTCFG_ALLOC
);
USB_REG->DEVEPTIER[0] = DEVEPTIER_RSTDTS;
USB_REG->DEVEPTIDR[0] = DEVEPTIDR_CTRL_STALLRQC;
if (DEVEPTISR_CFGOK == (USB_REG->DEVEPTISR[0] & DEVEPTISR_CFGOK))
{
// Endpoint configuration is successful
USB_REG->DEVEPTIER[0] = DEVEPTIER_CTRL_RXSTPES;
// Enable Endpoint 0 Interrupts
USB_REG->DEVIER = DEVIER_PEP_0;
return true;
} else
{
// Endpoint configuration is not successful
return false;
}
} else
{
// Enable the endpoint
USB_REG->DEVEPT |= ((0x01 << epnum) << DEVEPT_EPEN0_Pos);
// Set up the maxpacket size, fifo start address fifosize
// and enable the interrupt. CLear the data toggle.
// AUTOSW is needed for DMA ack !
USB_REG->DEVEPTCFG[epnum] =
(
(fifoSize << DEVEPTCFG_EPSIZE_Pos) |
(eptype << DEVEPTCFG_EPTYPE_Pos) |
(DEVEPTCFG_EPBK_1_BANK << DEVEPTCFG_EPBK_Pos) |
DEVEPTCFG_AUTOSW |
((dir & 0x01) << DEVEPTCFG_EPDIR_Pos)
);
if (eptype == TUSB_XFER_ISOCHRONOUS)
{
USB_REG->DEVEPTCFG[epnum] |= DEVEPTCFG_NBTRANS_1_TRANS;
}
#if USE_DUAL_BANK
if (eptype == TUSB_XFER_ISOCHRONOUS || eptype == TUSB_XFER_BULK)
{
USB_REG->DEVEPTCFG[epnum] |= DEVEPTCFG_EPBK_2_BANK;
}
#endif
USB_REG->DEVEPTCFG[epnum] |= DEVEPTCFG_ALLOC;
USB_REG->DEVEPTIER[epnum] = DEVEPTIER_RSTDTS;
USB_REG->DEVEPTIDR[epnum] = DEVEPTIDR_CTRL_STALLRQC;
if (DEVEPTISR_CFGOK == (USB_REG->DEVEPTISR[epnum] & DEVEPTISR_CFGOK))
{
USB_REG->DEVIER = ((0x01 << epnum) << DEVIER_PEP_0_Pos);
return true;
} else
{
// Endpoint configuration is not successful
return false;
}
}
}
void dcd_edpt_close_all (uint8_t rhport)
{
(void) rhport;
// TODO implement dcd_edpt_close_all()
}
void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
uint8_t const epnum = tu_edpt_number(ep_addr);
// Disable endpoint interrupt
USB_REG->DEVIDR = 1 << (DEVIDR_PEP_0_Pos + epnum);
// Disable EP
USB_REG->DEVEPT &=~(1 << (DEVEPT_EPEN0_Pos + epnum));
}
static void dcd_transmit_packet(xfer_ctl_t * xfer, uint8_t ep_ix)
{
uint16_t len = (uint16_t)(xfer->total_len - xfer->queued_len);
if (len)
{
if (len > xfer->max_packet_size)
{
len = xfer->max_packet_size;
}
uint8_t *ptr = EP_GET_FIFO_PTR(ep_ix,8);
if(xfer->buffer)
{
memcpy(ptr, xfer->buffer + xfer->queued_len, len);
}
else
{
tu_fifo_read_n(xfer->fifo, ptr, len);
}
__DSB();
__ISB();
xfer->queued_len = (uint16_t)(xfer->queued_len + len);
}
if (ep_ix == 0U)
{
// Control endpoint: clear the interrupt flag to send the data
USB_REG->DEVEPTICR[0] = DEVEPTICR_TXINIC;
} else
{
// Other endpoint types: clear the FIFO control flag to send the data
USB_REG->DEVEPTIDR[ep_ix] = DEVEPTIDR_FIFOCONC;
}
USB_REG->DEVEPTIER[ep_ix] = DEVEPTIER_TXINES;
}
// Submit a transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack
bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
{
(void) rhport;
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
xfer_ctl_t * xfer = &xfer_status[epnum];
xfer->buffer = buffer;
xfer->total_len = total_bytes;
xfer->queued_len = 0;
xfer->fifo = NULL;
if (EP_DMA_SUPPORT(epnum) && total_bytes != 0)
{
// Force the CPU to flush the buffer. We increase the size by 32 because the call aligns the
// address to 32-byte boundaries.
CleanInValidateCache((uint32_t*) tu_align((uint32_t) buffer, 4), total_bytes + 31);
uint32_t udd_dma_ctrl = total_bytes << DEVDMACONTROL_BUFF_LENGTH_Pos;
if (dir == TUSB_DIR_OUT)
{
udd_dma_ctrl |= DEVDMACONTROL_END_TR_IT | DEVDMACONTROL_END_TR_EN;
} else {
udd_dma_ctrl |= DEVDMACONTROL_END_B_EN;
}
USB_REG->DEVDMA[epnum - 1].DEVDMAADDRESS = (uint32_t)buffer;
udd_dma_ctrl |= DEVDMACONTROL_END_BUFFIT | DEVDMACONTROL_CHANN_ENB;
// Disable IRQs to have a short sequence
// between read of EOT_STA and DMA enable
uint32_t irq_state = __get_PRIMASK();
__disable_irq();
if (!(USB_REG->DEVDMA[epnum - 1].DEVDMASTATUS & DEVDMASTATUS_END_TR_ST))
{
USB_REG->DEVDMA[epnum - 1].DEVDMACONTROL = udd_dma_ctrl;
USB_REG->DEVIER = DEVIER_DMA_1 << (epnum - 1);
__set_PRIMASK(irq_state);
return true;
}
__set_PRIMASK(irq_state);
// Here a ZLP has been recieved
// and the DMA transfer must be not started.
// It is the end of transfer
return false;
} else
{
if (dir == TUSB_DIR_OUT)
{
USB_REG->DEVEPTIER[epnum] = DEVEPTIER_RXOUTES;
} else
{
dcd_transmit_packet(xfer,epnum);
}
}
return true;
}
// The number of bytes has to be given explicitly to allow more flexible control of how many
// bytes should be written and second to keep the return value free to give back a boolean
// success message. If total_bytes is too big, the FIFO will copy only what is available
// into the USB buffer!
bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
{
(void) rhport;
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
xfer_ctl_t * xfer = &xfer_status[epnum];
if(epnum == 0x80)
xfer = &xfer_status[EP_MAX];
xfer->buffer = NULL;
xfer->total_len = total_bytes;
xfer->queued_len = 0;
xfer->fifo = ff;
if (EP_DMA_SUPPORT(epnum) && total_bytes != 0)
{
tu_fifo_buffer_info_t info;
uint32_t udd_dma_ctrl_lin = DEVDMACONTROL_CHANN_ENB;
uint32_t udd_dma_ctrl_wrap = DEVDMACONTROL_CHANN_ENB | DEVDMACONTROL_END_BUFFIT;
if (dir == TUSB_DIR_OUT)
{
tu_fifo_get_write_info(ff, &info);
udd_dma_ctrl_lin |= DEVDMACONTROL_END_TR_IT | DEVDMACONTROL_END_TR_EN;
udd_dma_ctrl_wrap |= DEVDMACONTROL_END_TR_IT | DEVDMACONTROL_END_TR_EN;
} else {
tu_fifo_get_read_info(ff, &info);
if(info.len_wrap == 0)
{
udd_dma_ctrl_lin |= DEVDMACONTROL_END_B_EN;
}
udd_dma_ctrl_wrap |= DEVDMACONTROL_END_B_EN;
}
// Clean invalidate cache of linear part
CleanInValidateCache((uint32_t*) tu_align((uint32_t) info.ptr_lin, 4), info.len_lin + 31);
USB_REG->DEVDMA[epnum - 1].DEVDMAADDRESS = (uint32_t)info.ptr_lin;
if (info.len_wrap)
{
// Clean invalidate cache of wrapped part
CleanInValidateCache((uint32_t*) tu_align((uint32_t) info.ptr_wrap, 4), info.len_wrap + 31);
dma_desc[epnum - 1].next_desc = 0;
dma_desc[epnum - 1].buff_addr = (uint32_t)info.ptr_wrap;
dma_desc[epnum - 1].chnl_ctrl =
udd_dma_ctrl_wrap | (info.len_wrap << DEVDMACONTROL_BUFF_LENGTH_Pos);
// Clean cache of wrapped DMA descriptor
CleanInValidateCache((uint32_t*)&dma_desc[epnum - 1], sizeof(dma_desc_t));
udd_dma_ctrl_lin |= DEVDMASTATUS_DESC_LDST;
USB_REG->DEVDMA[epnum - 1].DEVDMANXTDSC = (uint32_t)&dma_desc[epnum - 1];
} else {
udd_dma_ctrl_lin |= DEVDMACONTROL_END_BUFFIT;
}
udd_dma_ctrl_lin |= (info.len_lin << DEVDMACONTROL_BUFF_LENGTH_Pos);
// Disable IRQs to have a short sequence
// between read of EOT_STA and DMA enable
uint32_t irq_state = __get_PRIMASK();
__disable_irq();
if (!(USB_REG->DEVDMA[epnum - 1].DEVDMASTATUS & DEVDMASTATUS_END_TR_ST))
{
USB_REG->DEVDMA[epnum - 1].DEVDMACONTROL = udd_dma_ctrl_lin;
USB_REG->DEVIER = DEVIER_DMA_1 << (epnum - 1);
__set_PRIMASK(irq_state);
return true;
}
__set_PRIMASK(irq_state);
// Here a ZLP has been recieved
// and the DMA transfer must be not started.
// It is the end of transfer
return false;
} else
{
if (dir == TUSB_DIR_OUT)
{
USB_REG->DEVEPTIER[epnum] = DEVEPTIER_RXOUTES;
} else
{
dcd_transmit_packet(xfer,epnum);
}
}
return true;
}
// Stall endpoint
void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
uint8_t const epnum = tu_edpt_number(ep_addr);
USB_REG->DEVEPTIER[epnum] = DEVEPTIER_CTRL_STALLRQS;
// Re-enable SETUP interrupt
if (epnum == 0)
{
USB_REG->DEVEPTIER[0] = DEVEPTIER_CTRL_RXSTPES;
}
}
// clear stall, data toggle is also reset to DATA0
void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
uint8_t const epnum = tu_edpt_number(ep_addr);
USB_REG->DEVEPTIDR[epnum] = DEVEPTIDR_CTRL_STALLRQC;
USB_REG->DEVEPTIER[epnum] = HSTPIPIER_RSTDTS;
}
#endif
@@ -339,6 +339,12 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
return true;
}
void dcd_edpt_close_all (uint8_t rhport)
{
(void) rhport;
// TODO implement dcd_edpt_close_all()
}
void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
+180 -59
View File
@@ -85,7 +85,7 @@ static struct
// Number of pending DMA that is started but not handled yet by dcd_int_handler().
// Since nRF can only carry one DMA can run at a time, this value is normally be either 0 or 1.
// However, in critical section with interrupt disabled, the DMA can be finished and added up
// until handled by dcd_init_handler() when exiting critical section.
// until handled by dcd_int_handler() when exiting critical section.
volatile uint8_t dma_pending;
}_dcd;
@@ -108,13 +108,21 @@ static inline uint32_t NVIC_GetEnableIRQ(IRQn_Type IRQn)
}
#endif
// check if we are in ISR
TU_ATTR_ALWAYS_INLINE static inline bool is_in_isr(void)
{
return (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) ? true : false;
}
// helper to start DMA
// 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)
{
// Only one dma can be active
if ( _dcd.dma_pending )
{
if (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk)
if (is_in_isr())
{
// Called within ISR, use usbd task to defer later
usbd_defer_func( (osal_task_func_t) edpt_dma_start, (void*) reg_startep, true );
@@ -159,6 +167,17 @@ static void edpt_dma_end(void)
_dcd.dma_pending = 0;
}
// helper to set TASKS_EP0STATUS / TASKS_EP0RCVOUT since they also need EasyDMA
// However TASKS_EP0STATUS doesn't trigger any DMA transfer and got ENDED event subsequently
// Therefore dma_running state will be corrected right away
void start_ep0_task(volatile uint32_t* reg_task)
{
edpt_dma_start(reg_task);
// correct the dma_running++ in dma start
if (_dcd.dma_pending) _dcd.dma_pending--;
}
// helper getting td
static inline xfer_td_t* get_td(uint8_t epnum, uint8_t dir)
{
@@ -187,7 +206,8 @@ static void xact_out_dma(uint8_t epnum)
}
else
{
xact_len = (uint8_t)NRF_USBD->SIZE.EPOUT[epnum];
// limit xact len to remaining length
xact_len = tu_min16((uint16_t) NRF_USBD->SIZE.EPOUT[epnum], xfer->total_len - xfer->actual_len);
// Trigger DMA move data from Endpoint -> SRAM
NRF_USBD->EPOUT[epnum].PTR = (uint32_t) xfer->buffer;
@@ -258,14 +278,8 @@ void dcd_remote_wakeup(uint8_t rhport)
(void) rhport;
// Bring controller out of low power mode
// will start wakeup when USBWUALLOWED is set
NRF_USBD->LOWPOWER = 0;
// Initiate RESUME signal
NRF_USBD->DPDMVALUE = USBD_DPDMVALUE_STATE_Resume;
NRF_USBD->TASKS_DPDMDRIVE = 1;
// TODO There is no USBEVENT Resume interrupt
// We may manually raise DCD_EVENT_RESUME event here
}
// disconnect by disabling internal pull-up resistor on D+/D-
@@ -293,8 +307,9 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
{
(void) rhport;
uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress);
uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress);
uint8_t const ep_addr = desc_edpt->bEndpointAddress;
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
_dcd.xfer[epnum][dir].mps = desc_edpt->wMaxPacketSize.size;
@@ -320,10 +335,13 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
{
// SPLIT ISO buffer when ISO IN endpoint is already opened.
if (_dcd.xfer[EP_ISO_NUM][TUSB_DIR_IN].mps) NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_HalfIN;
// Clear old events
NRF_USBD->EVENTS_ENDISOOUT = 0;
// Clear SOF event in case interrupt was not enabled yet.
if ((NRF_USBD->INTEN & USBD_INTEN_SOF_Msk) == 0) NRF_USBD->EVENTS_SOF = 0;
// Enable SOF and ISOOUT interrupts, and ISOOUT endpoint.
NRF_USBD->INTENSET = USBD_INTENSET_ENDISOOUT_Msk | USBD_INTENSET_SOF_Msk;
NRF_USBD->EPOUTEN |= USBD_EPOUTEN_ISOOUT_Msk;
@@ -331,20 +349,60 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
else
{
NRF_USBD->EVENTS_ENDISOIN = 0;
// SPLIT ISO buffer when ISO OUT endpoint is already opened.
if (_dcd.xfer[EP_ISO_NUM][TUSB_DIR_OUT].mps) NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_HalfIN;
// Clear SOF event in case interrupt was not enabled yet.
if ((NRF_USBD->INTEN & USBD_INTEN_SOF_Msk) == 0) NRF_USBD->EVENTS_SOF = 0;
// Enable SOF and ISOIN interrupts, and ISOIN endpoint.
NRF_USBD->INTENSET = USBD_INTENSET_ENDISOIN_Msk | USBD_INTENSET_SOF_Msk;
NRF_USBD->EPINEN |= USBD_EPINEN_ISOIN_Msk;
}
}
// clear stall and reset DataToggle
NRF_USBD->EPSTALL = (USBD_EPSTALL_STALL_UnStall << USBD_EPSTALL_STALL_Pos) | ep_addr;
NRF_USBD->DTOGGLE = (USBD_DTOGGLE_VALUE_Data0 << USBD_DTOGGLE_VALUE_Pos) | ep_addr;
__ISB(); __DSB();
return true;
}
void dcd_edpt_close_all (uint8_t rhport)
{
// disable interrupt to prevent race condition
dcd_int_disable(rhport);
// disable all non-control (bulk + interrupt) endpoints
for ( uint8_t ep = 1; ep < EP_CBI_COUNT; ep++ )
{
NRF_USBD->INTENCLR = TU_BIT(USBD_INTEN_ENDEPOUT0_Pos + ep) | TU_BIT(USBD_INTEN_ENDEPIN0_Pos + ep);
NRF_USBD->TASKS_STARTEPIN[ep] = 0;
NRF_USBD->TASKS_STARTEPOUT[ep] = 0;
tu_memclr(_dcd.xfer[ep], 2*sizeof(xfer_td_t));
}
// disable both ISO
NRF_USBD->INTENCLR = USBD_INTENCLR_SOF_Msk | USBD_INTENCLR_ENDISOOUT_Msk | USBD_INTENCLR_ENDISOIN_Msk;
NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_OneDir;
NRF_USBD->TASKS_STARTISOIN = 0;
NRF_USBD->TASKS_STARTISOOUT = 0;
tu_memclr(_dcd.xfer[EP_ISO_NUM], 2*sizeof(xfer_td_t));
// de-activate all non-control
NRF_USBD->EPOUTEN = 1UL;
NRF_USBD->EPINEN = 1UL;
dcd_int_enable(rhport);
}
void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
@@ -407,29 +465,25 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
if ( control_status )
{
// Status Phase also requires Easy DMA has to be available as well !!!!
// However TASKS_EP0STATUS doesn't trigger any DMA transfer and got ENDED event subsequently
// Therefore dma_running state will be corrected right away
edpt_dma_start(&NRF_USBD->TASKS_EP0STATUS);
if (_dcd.dma_pending) _dcd.dma_pending--; // correct the dma_running++ in dma start
// Status Phase also requires EasyDMA has to be available as well !!!!
start_ep0_task(&NRF_USBD->TASKS_EP0STATUS);
// The nRF doesn't interrupt on status transmit so we queue up a success response.
dcd_event_xfer_complete(0, ep_addr, 0, XFER_RESULT_SUCCESS, false);
dcd_event_xfer_complete(0, ep_addr, 0, XFER_RESULT_SUCCESS, is_in_isr());
}
else if ( dir == TUSB_DIR_OUT )
{
if ( epnum == 0 )
{
// Accept next Control Out packet
NRF_USBD->TASKS_EP0RCVOUT = 1;
// Accept next Control Out packet. TASKS_EP0RCVOUT also require EasyDMA
start_ep0_task(&NRF_USBD->TASKS_EP0RCVOUT);
}else
{
if ( xfer->data_received )
{
// Data may already be received previously
xfer->data_received = false;
// Data is already received previously
// start DMA to copy to SRAM
xfer->data_received = false;
xact_out_dma(epnum);
}
else
@@ -451,7 +505,11 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
xfer_td_t* xfer = get_td(epnum, dir);
if ( epnum == 0 )
{
@@ -459,6 +517,15 @@ void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
}else if (epnum != EP_ISO_NUM)
{
NRF_USBD->EPSTALL = (USBD_EPSTALL_STALL_Stall << USBD_EPSTALL_STALL_Pos) | ep_addr;
// Note: nRF can auto ACK packet OUT before get stalled.
// There maybe data in endpoint fifo already, we need to pull it out
if ( (dir == TUSB_DIR_OUT) && xfer->data_received )
{
TU_LOG_LOCATION();
xfer->data_received = false;
xact_out_dma(epnum);
}
}
__ISB(); __DSB();
@@ -472,14 +539,16 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
if ( epnum != 0 && epnum != EP_ISO_NUM )
{
// reset data toggle to DATA0
// First write this register with VALUE=Nop to select the endpoint, then either read it to get the status from
// VALUE, or write it again with VALUE=Data0 or Data1
NRF_USBD->DTOGGLE = ep_addr;
NRF_USBD->DTOGGLE = (USBD_DTOGGLE_VALUE_Data0 << USBD_DTOGGLE_VALUE_Pos) | ep_addr;
// clear stall
NRF_USBD->EPSTALL = (USBD_EPSTALL_STALL_UnStall << USBD_EPSTALL_STALL_Pos) | ep_addr;
// reset data toggle to DATA0
NRF_USBD->DTOGGLE = (USBD_DTOGGLE_VALUE_Data0 << USBD_DTOGGLE_VALUE_Pos) | ep_addr;
// Write any value to SIZE register will allow nRF to ACK/accept data
// Drop any pending data
if (dir == TUSB_DIR_OUT) NRF_USBD->SIZE.EPOUT[epnum] = 0;
__ISB(); __DSB();
@@ -491,6 +560,10 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
*------------------------------------------------------------------*/
void bus_reset(void)
{
// 6.35.6 USB controller automatically disabled all endpoints (except control)
NRF_USBD->EPOUTEN = 1UL;
NRF_USBD->EPINEN = 1UL;
for(int i=0; i<8; i++)
{
NRF_USBD->TASKS_STARTEPIN[i] = 0;
@@ -500,6 +573,15 @@ void bus_reset(void)
NRF_USBD->TASKS_STARTISOIN = 0;
NRF_USBD->TASKS_STARTISOOUT = 0;
// Clear USB Event Interrupt
NRF_USBD->EVENTS_USBEVENT = 0;
NRF_USBD->EVENTCAUSE |= NRF_USBD->EVENTCAUSE;
// Reset interrupt
NRF_USBD->INTENCLR = NRF_USBD->INTEN;
NRF_USBD->INTENSET = USBD_INTEN_USBRESET_Msk | USBD_INTEN_USBEVENT_Msk | USBD_INTEN_EPDATA_Msk |
USBD_INTEN_EP0SETUP_Msk | USBD_INTEN_EP0DATADONE_Msk | USBD_INTEN_ENDEPIN0_Msk | USBD_INTEN_ENDEPOUT0_Msk;
tu_varclr(&_dcd);
_dcd.xfer[0][TUSB_DIR_IN].mps = MAX_PACKET_SIZE;
_dcd.xfer[0][TUSB_DIR_OUT].mps = MAX_PACKET_SIZE;
@@ -545,48 +627,74 @@ void dcd_int_handler(uint8_t rhport)
if ( int_status & USBD_INTEN_SOF_Msk )
{
bool iso_enabled = false;
// ISOOUT: Transfer data gathered in previous frame from buffer to RAM
if (NRF_USBD->EPOUTEN & USBD_EPOUTEN_ISOOUT_Msk)
{
iso_enabled = true;
xact_out_dma(EP_ISO_NUM);
}
// ISOIN: Notify client that data was transferred
xfer_td_t* xfer = get_td(EP_ISO_NUM, TUSB_DIR_IN);
if ( xfer->iso_in_transfer_ready )
if (NRF_USBD->EPINEN & USBD_EPINEN_ISOIN_Msk)
{
xfer->iso_in_transfer_ready = false;
dcd_event_xfer_complete(0, EP_ISO_NUM | TUSB_DIR_IN_MASK, xfer->actual_len, XFER_RESULT_SUCCESS, true);
iso_enabled = true;
xfer_td_t* xfer = get_td(EP_ISO_NUM, TUSB_DIR_IN);
if ( xfer->iso_in_transfer_ready )
{
xfer->iso_in_transfer_ready = false;
dcd_event_xfer_complete(0, EP_ISO_NUM | TUSB_DIR_IN_MASK, xfer->actual_len, XFER_RESULT_SUCCESS, true);
}
}
if ( !iso_enabled )
{
// ISO endpoint is not used, SOF is only enabled one-time for remote wakeup
// so we disable it now
NRF_USBD->INTENCLR = USBD_INTENSET_SOF_Msk;
}
dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
}
if ( int_status & USBD_INTEN_USBEVENT_Msk )
{
uint32_t const evt_cause = NRF_USBD->EVENTCAUSE & (USBD_EVENTCAUSE_SUSPEND_Msk | USBD_EVENTCAUSE_RESUME_Msk);
TU_LOG(2, "EVENTCAUSE = 0x%04lX\r\n", NRF_USBD->EVENTCAUSE);
enum { EVT_CAUSE_MASK = USBD_EVENTCAUSE_SUSPEND_Msk | USBD_EVENTCAUSE_RESUME_Msk | USBD_EVENTCAUSE_USBWUALLOWED_Msk };
uint32_t const evt_cause = NRF_USBD->EVENTCAUSE & EVT_CAUSE_MASK;
NRF_USBD->EVENTCAUSE = evt_cause; // clear interrupt
if ( evt_cause & USBD_EVENTCAUSE_SUSPEND_Msk )
{
dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
// Put controller into low power mode
// Leave HFXO disable to application, since it may be used by other peripherals
NRF_USBD->LOWPOWER = 1;
// Leave HFXO disable to application, since it may be used by other
dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
}
if ( evt_cause & USBD_EVENTCAUSE_RESUME_Msk )
if ( evt_cause & USBD_EVENTCAUSE_USBWUALLOWED_Msk )
{
dcd_event_bus_signal(0, DCD_EVENT_RESUME , true);
// USB is out of low power mode, and wakeup is allowed
// Initiate RESUME signal
NRF_USBD->DPDMVALUE = USBD_DPDMVALUE_STATE_Resume;
NRF_USBD->TASKS_DPDMDRIVE = 1;
// There is no Resume interrupt for remote wakeup, enable SOF for to report bus ready state
// Clear SOF event in case interrupt was not enabled yet.
if ((NRF_USBD->INTEN & USBD_INTEN_SOF_Msk) == 0) NRF_USBD->EVENTS_SOF = 0;
NRF_USBD->INTENSET = USBD_INTENSET_SOF_Msk;
}
if ( evt_cause & USBD_EVENTCAUSE_RESUME_Msk )
{
dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
}
}
if ( int_status & EDPT_END_ALL_MASK )
{
// DMA complete move data from SRAM -> Endpoint
edpt_dma_end();
}
// Setup tokens are specific to the Control endpoint.
if ( int_status & USBD_INTEN_EP0SETUP_Msk )
{
@@ -607,6 +715,12 @@ void dcd_int_handler(uint8_t rhport)
}
}
if ( int_status & EDPT_END_ALL_MASK )
{
// DMA complete move data from SRAM -> Endpoint
edpt_dma_end();
}
//--------------------------------------------------------------------+
/* Control/Bulk/Interrupt (CBI) Transfer
*
@@ -655,8 +769,15 @@ void dcd_int_handler(uint8_t rhport)
{
if ( epnum == 0 )
{
// Accept next Control Out packet
NRF_USBD->TASKS_EP0RCVOUT = 1;
// Accept next Control Out packet. TASKS_EP0RCVOUT also require EasyDMA
if ( _dcd.dma_pending )
{
// use usbd task to defer later
usbd_defer_func( (osal_task_func_t) start_ep0_task, (void*) &NRF_USBD->TASKS_EP0RCVOUT, true );
}else
{
start_ep0_task(&NRF_USBD->TASKS_EP0RCVOUT);
}
}else
{
// nRF auto accept next Bulk/Interrupt OUT packet
@@ -764,7 +885,7 @@ static bool hfclk_running(void)
#ifdef SOFTDEVICE_PRESENT
if ( is_sd_enabled() )
{
uint32_t is_running;
uint32_t is_running = 0;
(void) sd_clock_hfclk_is_running(&is_running);
return (is_running ? true : false);
}
@@ -822,18 +943,22 @@ void tusb_hal_nrf_power_event (uint32_t event)
USB_EVT_READY = 2
};
#if CFG_TUSB_DEBUG >= 2
const char* const power_evt_str[] = { "Detected", "Removed", "Ready" };
TU_LOG(2, "Power USB event: %s\r\n", power_evt_str[event]);
#endif
switch ( event )
{
case USB_EVT_DETECTED:
TU_LOG2("Power USB Detect\r\n");
if ( !NRF_USBD->ENABLE )
{
/* Prepare for READY event receiving */
// Prepare for receiving READY event: disable interrupt since we will blocking wait
NRF_USBD->INTENCLR = USBD_INTEN_USBEVENT_Msk;
NRF_USBD->EVENTCAUSE = USBD_EVENTCAUSE_READY_Msk;
__ISB(); __DSB(); // for sync
#ifdef NRF52_SERIES
#ifdef NRF52_SERIES // NRF53 does not need this errata
// ERRATA 171, 187, 166
if ( nrfx_usbd_errata_187() )
{
@@ -868,7 +993,7 @@ void tusb_hal_nrf_power_event (uint32_t event)
}
#endif
/* Enable the peripheral */
// Enable the peripheral (will cause Ready event)
NRF_USBD->ENABLE = 1;
__ISB(); __DSB(); // for sync
@@ -878,13 +1003,11 @@ void tusb_hal_nrf_power_event (uint32_t event)
break;
case USB_EVT_READY:
TU_LOG2("Power USB Ready\r\n");
// Skip if pull-up is enabled and HCLK is already running.
// Application probably call this more than necessary.
if ( NRF_USBD->USBPULLUP && hfclk_running() ) break;
/* Waiting for USBD peripheral enabled */
// Waiting for USBD peripheral enabled
while ( !(USBD_EVENTCAUSE_READY_Msk & NRF_USBD->EVENTCAUSE) ) { }
NRF_USBD->EVENTCAUSE = USBD_EVENTCAUSE_READY_Msk;
@@ -936,9 +1059,8 @@ void tusb_hal_nrf_power_event (uint32_t event)
// ISO buffer Lower half for IN, upper half for OUT
NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_HalfIN;
// Enable interrupt
NRF_USBD->INTENSET = USBD_INTEN_USBRESET_Msk | USBD_INTEN_EPDATA_Msk |
USBD_INTEN_EP0SETUP_Msk | USBD_INTEN_EP0DATADONE_Msk | USBD_INTEN_ENDEPIN0_Msk | USBD_INTEN_ENDEPOUT0_Msk;
// Enable bus-reset interrupt
NRF_USBD->INTENSET = USBD_INTEN_USBRESET_Msk;
// Enable interrupt, priorities should be set by application
NVIC_ClearPendingIRQ(USBD_IRQn);
@@ -953,7 +1075,6 @@ void tusb_hal_nrf_power_event (uint32_t event)
break;
case USB_EVT_REMOVED:
TU_LOG2("Power USB Removed\r\n");
if ( NRF_USBD->ENABLE )
{
// Abort all transfers
@@ -973,7 +1094,7 @@ void tusb_hal_nrf_power_event (uint32_t event)
hfclk_disable();
dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) ? true : false);
dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, is_in_isr());
}
break;
+16 -3
View File
@@ -99,6 +99,11 @@ static void usb_detach(void)
USBD->DRVSE0 |= USBD_DRVSE0_DRVSE0_Msk;
}
static inline void usb_memcpy(uint8_t *dest, uint8_t *src, uint16_t size)
{
while(size--) *dest++ = *src++;
}
static void usb_control_send_zlp(void)
{
USBD->EP[PERIPH_EP0].CFG |= USBD_CFG_DSQ_SYNC_Msk;
@@ -151,7 +156,8 @@ static void dcd_in_xfer(struct xfer_ctl_t *xfer, USBD_EP_T *ep)
else
#endif
{
memcpy((uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), xfer->data_ptr, bytes_now);
// USB SRAM seems to only support byte access and memcpy could possibly do it by words
usb_memcpy((uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), xfer->data_ptr, bytes_now);
}
ep->MXPLD = bytes_now;
@@ -246,7 +252,7 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
/* mine the data for the information we need */
int const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress);
int const size = p_endpoint_desc->wMaxPacketSize.size;
tusb_xfer_type_t const type = p_endpoint_desc->bmAttributes.xfer;
tusb_xfer_type_t const type = (tusb_xfer_type_t) p_endpoint_desc->bmAttributes.xfer;
struct xfer_ctl_t *xfer = &xfer_table[ep - USBD->EP];
/* allocate buffer from USB RAM */
@@ -267,6 +273,12 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
return true;
}
void dcd_edpt_close_all (uint8_t rhport)
{
(void) rhport;
// TODO implement dcd_edpt_close_all()
}
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes)
{
(void) rhport;
@@ -439,7 +451,8 @@ void dcd_int_handler(uint8_t rhport)
else
#endif
{
memcpy(xfer->data_ptr, (uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), available_bytes);
// USB SRAM seems to only support byte access and memcpy could possibly do it by words
usb_memcpy(xfer->data_ptr, (uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), available_bytes);
xfer->data_ptr += available_bytes;
}
+68 -17
View File
@@ -40,6 +40,12 @@
#include "device/dcd.h"
#include "NuMicro.h"
// Since TinyUSB doesn't use SOF for now, and this interrupt too often (1ms interval)
// We disable SOF for now until needed later on
#ifndef USE_SOF
# define USE_SOF 0
#endif
/* allocation of USBD RAM for Setup, EP0_IN, and and EP_OUT */
#define PERIPH_SETUP_BUF_BASE 0
#define PERIPH_SETUP_BUF_LEN 8
@@ -65,9 +71,6 @@ enum ep_enum
PERIPH_MAX_EP,
};
/* set by dcd_set_address() */
static volatile uint8_t assigned_address;
/* reset by dcd_init(), this is used by dcd_edpt_open() to assign USBD peripheral buffer addresses */
static uint32_t bufseg_addr;
@@ -101,6 +104,11 @@ static void usb_detach(void)
USBD->SE0 |= USBD_SE0_SE0_Msk;
}
static inline void usb_memcpy(uint8_t *dest, uint8_t *src, uint16_t size)
{
while(size--) *dest++ = *src++;
}
static void usb_control_send_zlp(void)
{
USBD->EP[PERIPH_EP0].CFG |= USBD_CFG_DSQSYNC_Msk;
@@ -153,7 +161,8 @@ static void dcd_in_xfer(struct xfer_ctl_t *xfer, USBD_EP_T *ep)
else
#endif
{
memcpy((uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), xfer->data_ptr, bytes_now);
// USB SRAM seems to only support byte access and memcpy could possibly do it by words
usb_memcpy((uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), xfer->data_ptr, bytes_now);
}
ep->MXPLD = bytes_now;
@@ -191,7 +200,10 @@ static void bus_reset(void)
}
/* centralized location for USBD interrupt enable bit mask */
static const uint32_t enabled_irqs = USBD_INTSTS_VBDETIF_Msk | USBD_INTSTS_BUSIF_Msk | USBD_INTSTS_SETUP_Msk | USBD_INTSTS_USBIF_Msk | USBD_INTSTS_SOFIF_Msk;
enum {
ENABLED_IRQS = USBD_INTSTS_VBDETIF_Msk | USBD_INTSTS_BUSIF_Msk | USBD_INTSTS_SETUP_Msk |
USBD_INTSTS_USBIF_Msk | (USE_SOF ? USBD_INTSTS_SOFIF_Msk : 0)
};
/*
NUC121/NUC125/NUC126 TinyUSB API driver implementation
@@ -213,8 +225,8 @@ void dcd_init(uint8_t rhport)
usb_attach();
USBD->INTSTS = enabled_irqs;
USBD->INTEN = enabled_irqs;
USBD->INTSTS = ENABLED_IRQS;
USBD->INTEN = ENABLED_IRQS;
}
void dcd_int_enable(uint8_t rhport)
@@ -232,14 +244,30 @@ void dcd_int_disable(uint8_t rhport)
void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
{
(void) rhport;
(void) dev_addr;
usb_control_send_zlp(); /* SET_ADDRESS is the one exception where TinyUSB doesn't use dcd_edpt_xfer() to generate a ZLP */
assigned_address = dev_addr;
// DCD can only set address after status for this request is complete.
// do it at dcd_edpt0_status_complete()
}
static void remote_wakeup_delay(void)
{
// try to delay for 1 ms
uint32_t count = SystemCoreClock / 1000;
while(count--) __NOP();
}
void dcd_remote_wakeup(uint8_t rhport)
{
(void) rhport;
USBD->ATTR = USBD_ATTR_RWAKEUP_Msk;
// Enable PHY before sending Resume('K') state
USBD->ATTR |= USBD_ATTR_PHYEN_Msk;
USBD->ATTR |= USBD_ATTR_RWAKEUP_Msk;
// Per specs: remote wakeup signal bit must be clear within 1-15ms
remote_wakeup_delay();
USBD->ATTR &=~USBD_ATTR_RWAKEUP_Msk;
}
bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
@@ -252,7 +280,7 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
/* mine the data for the information we need */
int const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress);
int const size = p_endpoint_desc->wMaxPacketSize.size;
tusb_xfer_type_t const type = p_endpoint_desc->bmAttributes.xfer;
tusb_xfer_type_t const type = (tusb_xfer_type_t) p_endpoint_desc->bmAttributes.xfer;
struct xfer_ctl_t *xfer = &xfer_table[ep - USBD->EP];
/* allocate buffer from USB RAM */
@@ -273,6 +301,12 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
return true;
}
void dcd_edpt_close_all (uint8_t rhport)
{
(void) rhport;
// TODO implement dcd_edpt_close_all()
}
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes)
{
(void) rhport;
@@ -345,14 +379,16 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
USBD_EP_T *ep = ep_entry(ep_addr, false);
ep->CFG |= USBD_CFG_CSTALL_Msk;
ep->CFG = (ep->CFG & ~USBD_CFG_DSQSYNC_Msk) | USBD_CFG_CSTALL_Msk;
}
void dcd_int_handler(uint8_t rhport)
{
(void) rhport;
uint32_t status = USBD->INTSTS;
// Mask non-enabled irqs, ex. SOF
uint32_t status = USBD->INTSTS & (ENABLED_IRQS | 0xffffff00);
#ifdef SUPPORT_LPM
uint32_t state = USBD->ATTR & 0x300f;
#else
@@ -414,9 +450,6 @@ void dcd_int_handler(uint8_t rhport)
{
if (status & USBD_INTSTS_EPEVT0_Msk) /* PERIPH_EP0 (EP0_IN) event: this is treated separately from the rest */
{
/* given ACK from host has happened, we can now set the address (if not already done) */
if((USBD->FADDR != assigned_address) && (USBD->FADDR == 0)) USBD->FADDR = assigned_address;
uint16_t const available_bytes = USBD->EP[PERIPH_EP0].MXPLD;
active_ep0_xfer = (available_bytes == xfer_table[PERIPH_EP0].max_packet_size);
@@ -450,7 +483,8 @@ void dcd_int_handler(uint8_t rhport)
else
#endif
{
memcpy(xfer->data_ptr, (uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), available_bytes);
// USB SRAM seems to only support byte access and memcpy could possibly do it by words
usb_memcpy(xfer->data_ptr, (uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), available_bytes);
xfer->data_ptr += available_bytes;
}
@@ -485,7 +519,24 @@ void dcd_int_handler(uint8_t rhport)
}
/* acknowledge all interrupts */
USBD->INTSTS = status & enabled_irqs;
USBD->INTSTS = status & ENABLED_IRQS;
}
// Invoked when a control transfer's status stage is complete.
// May help DCD to prepare for next control transfer, this API is optional.
void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request)
{
(void) rhport;
if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE &&
request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD &&
request->bRequest == TUSB_REQ_SET_ADDRESS )
{
uint8_t const dev_addr = (uint8_t) request->wValue;
// Setting new address after the whole request is complete
USBD->FADDR = dev_addr;
}
}
void dcd_disconnect(uint8_t rhport)
+6
View File
@@ -353,6 +353,12 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
return true;
}
void dcd_edpt_close_all (uint8_t rhport)
{
(void) rhport;
// TODO implement dcd_edpt_close_all()
}
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes)
{
(void) rhport;
+162 -82
View File
@@ -26,7 +26,9 @@
#include "tusb_option.h"
#if TUSB_OPT_DEVICE_ENABLED && ( CFG_TUSB_MCU == OPT_MCU_MKL25ZXX )
#if TUSB_OPT_DEVICE_ENABLED && ( \
( CFG_TUSB_MCU == OPT_MCU_MKL25ZXX ) || ( CFG_TUSB_MCU == OPT_MCU_K32L2BXX ) \
)
#include "fsl_device_registers.h"
#define KHCI USB0
@@ -50,23 +52,23 @@ typedef struct TU_ATTR_PACKED
struct {
union {
struct {
uint16_t : 2;
uint16_t tok_pid : 4;
uint16_t data : 1;
uint16_t own : 1;
uint16_t : 8;
uint16_t : 2;
__IO uint16_t tok_pid : 4;
uint16_t data : 1;
__IO 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 : 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;
__IO uint16_t bc : 10;
uint16_t : 6;
};
};
uint8_t *addr;
@@ -118,10 +120,8 @@ 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;
if (_dcd.bdt[0][0][out_odd].own) {
TU_LOG1("DCD fail to prepare the next SETUP %d %d\r\n", out_odd, in_odd);
return;
}
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;
@@ -132,10 +132,16 @@ static void prepare_next_setup_packet(uint8_t rhport)
static void process_stall(uint8_t rhport)
{
if (KHCI->ENDPOINT[0].ENDPT & USB_ENDPT_EPSTALL_MASK) {
/* clear stall condition of the control pipe */
prepare_next_setup_packet(rhport);
KHCI->ENDPOINT[0].ENDPT &= ~USB_ENDPT_EPSTALL_MASK;
for (int i = 0; i < 16; ++i) {
unsigned const endpt = KHCI->ENDPOINT[i].ENDPT;
if (endpt & USB_ENDPT_EPSTALL_MASK) {
// prepare next setup if endpoint0
if ( i == 0 ) prepare_next_setup_packet(rhport);
// clear stall bit
KHCI->ENDPOINT[i].ENDPT = endpt & ~USB_ENDPT_EPSTALL_MASK;
}
}
}
@@ -143,12 +149,17 @@ static void process_tokdne(uint8_t rhport)
{
const unsigned s = KHCI->STAT;
KHCI->ISTAT = USB_ISTAT_TOKDNE_MASK; /* fetch the next token if received */
uint8_t const epnum = (s >> USB_STAT_ENDP_SHIFT);
uint8_t const dir = (s & USB_STAT_TX_MASK) >> USB_STAT_TX_SHIFT;
unsigned const odd = (s & USB_STAT_ODD_MASK) ? 1 : 0;
buffer_descriptor_t *bd = (buffer_descriptor_t *)&_dcd.bda[s];
endpoint_state_t *ep = &_dcd.endpoint_unified[s >> 3];
unsigned odd = (s & USB_STAT_ODD_MASK) ? 1 : 0;
/* 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;
@@ -161,9 +172,6 @@ static void process_tokdne(uint8_t rhport)
KHCI->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
return;
}
if (s >> 4) {
TU_LOG1("TKDNE %x\r\n", s);
}
const unsigned bc = bd->bc;
const unsigned remaining = ep->remaining - bc;
@@ -182,9 +190,9 @@ static void process_tokdne(uint8_t rhport)
}
const unsigned length = ep->length;
dcd_event_xfer_complete(rhport,
((s & USB_STAT_TX_MASK) << 4) | (s >> USB_STAT_ENDP_SHIFT),
tu_edpt_addr(epnum, dir),
length - remaining, XFER_RESULT_SUCCESS, true);
if (0 == (s & USB_STAT_ENDP_MASK) && 0 == length) {
if (0 == epnum && 0 == length) {
/* After completion a ZLP of control transfer,
* it prepares for the next steup transfer. */
if (_dcd.addr) {
@@ -202,7 +210,8 @@ static void process_bus_reset(uint8_t rhport)
KHCI->USBCTRL &= ~USB_USBCTRL_SUSP_MASK;
KHCI->CTL |= USB_CTL_ODDRST_MASK;
KHCI->ADDR = 0;
KHCI->INTEN = (KHCI->INTEN & ~USB_INTEN_RESUMEEN_MASK) | USB_INTEN_SLEEPEN_MASK;
KHCI->INTEN = USB_INTEN_USBRSTEN_MASK | USB_INTEN_TOKDNEEN_MASK | USB_INTEN_SLEEPEN_MASK |
USB_INTEN_ERROREN_MASK | USB_INTEN_STALLEN_MASK;
KHCI->ENDPOINT[0].ENDPT = USB_ENDPT_EPHSHK_MASK | USB_ENDPT_EPRXEN_MASK | USB_ENDPT_EPTXEN_MASK;
for (unsigned i = 1; i < 16; ++i) {
@@ -227,21 +236,27 @@ static void process_bus_reset(uint8_t rhport)
dcd_event_bus_reset(rhport, TUSB_SPEED_FULL, true);
}
static void process_bus_inactive(uint8_t rhport)
static void process_bus_sleep(uint8_t rhport)
{
(void) rhport;
// Enable resume & disable suspend interrupt
const unsigned inten = KHCI->INTEN;
KHCI->INTEN = (inten & ~USB_INTEN_SLEEPEN_MASK) | USB_INTEN_RESUMEEN_MASK;
KHCI->USBTRC0 |= USB_USBTRC0_USBRESMEN_MASK;
KHCI->USBCTRL |= USB_USBCTRL_SUSP_MASK;
dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
}
static void process_bus_active(uint8_t rhport)
static void process_bus_resume(uint8_t rhport)
{
(void) rhport;
KHCI->USBCTRL &= ~USB_USBCTRL_SUSP_MASK;
// Enable suspend & disable resume interrupt
const unsigned inten = KHCI->INTEN;
KHCI->USBCTRL &= ~USB_USBCTRL_SUSP_MASK; // will also clear USB_USBTRC0_USB_RESUME_INT_MASK
KHCI->USBTRC0 &= ~USB_USBTRC0_USBRESMEN_MASK;
KHCI->INTEN = (inten & ~USB_INTEN_RESUMEEN_MASK) | USB_INTEN_SLEEPEN_MASK;
dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
}
@@ -254,12 +269,15 @@ void dcd_init(uint8_t rhport)
KHCI->USBTRC0 |= USB_USBTRC0_USBRESET_MASK;
while (KHCI->USBTRC0 & USB_USBTRC0_USBRESET_MASK);
tu_memclr(&_dcd, sizeof(_dcd));
KHCI->USBTRC0 |= TU_BIT(6); /* software must set this bit to 1 */
KHCI->BDTPAGE1 = (uint8_t)((uintptr_t)_dcd.bdt >> 8);
KHCI->BDTPAGE2 = (uint8_t)((uintptr_t)_dcd.bdt >> 16);
KHCI->BDTPAGE3 = (uint8_t)((uintptr_t)_dcd.bdt >> 24);
KHCI->INTEN = USB_INTEN_USBRSTEN_MASK;
dcd_connect(rhport);
NVIC_ClearPendingIRQ(USB0_IRQn);
}
@@ -267,8 +285,6 @@ void dcd_init(uint8_t rhport)
void dcd_int_enable(uint8_t rhport)
{
(void) rhport;
KHCI->INTEN = USB_INTEN_USBRSTEN_MASK | USB_INTEN_TOKDNEEN_MASK |
USB_INTEN_SLEEPEN_MASK | USB_INTEN_ERROREN_MASK | USB_INTEN_STALLEN_MASK;
NVIC_EnableIRQ(USB0_IRQn);
}
@@ -276,13 +292,11 @@ void dcd_int_disable(uint8_t rhport)
{
(void) rhport;
NVIC_DisableIRQ(USB0_IRQn);
KHCI->INTEN = 0;
}
void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
{
(void) rhport;
_dcd.addr = dev_addr & 0x7F;
_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);
}
@@ -290,9 +304,12 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
void dcd_remote_wakeup(uint8_t rhport)
{
(void) rhport;
unsigned cnt = SystemCoreClock / 100;
KHCI->CTL |= USB_CTL_RESUME_MASK;
unsigned cnt = SystemCoreClock / 1000;
while (cnt--) __NOP();
KHCI->CTL &= ~USB_CTL_RESUME_MASK;
}
@@ -319,12 +336,12 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
(void) rhport;
const unsigned ep_addr = ep_desc->bEndpointAddress;
const unsigned epn = ep_addr & 0xFu;
const unsigned dir = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
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][0];
buffer_descriptor_t *bd = _dcd.bdt[epn][dir];
/* No support for control transfer */
TU_ASSERT(epn && (xfer != TUSB_XFER_CONTROL));
@@ -345,35 +362,60 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
return true;
}
void dcd_edpt_close_all(uint8_t rhport)
{
(void) rhport;
const unsigned ie = NVIC_GetEnableIRQ(USB0_IRQn);
NVIC_DisableIRQ(USB0_IRQn);
for (unsigned i = 1; i < 16; ++i) {
KHCI->ENDPOINT[i].ENDPT = 0;
}
if (ie) NVIC_EnableIRQ(USB0_IRQn);
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)
{
(void) rhport;
const unsigned epn = ep_addr & 0xFu;
const unsigned dir = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
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][0];
buffer_descriptor_t *bd = _dcd.bdt[epn][dir];
const unsigned msk = dir ? USB_ENDPT_EPTXEN_MASK : USB_ENDPT_EPRXEN_MASK;
const unsigned ie = NVIC_GetEnableIRQ(USB0_IRQn);
NVIC_DisableIRQ(USB0_IRQn);
KHCI->ENDPOINT[epn].ENDPT &= ~msk;
ep->max_packet_size = 0;
ep->length = 0;
ep->remaining = 0;
bd->head = 0;
bd[0].head = 0;
bd[1].head = 0;
if (ie) NVIC_EnableIRQ(USB0_IRQn);
}
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes)
{
(void) rhport;
NVIC_DisableIRQ(USB0_IRQn);
const unsigned epn = ep_addr & 0xFu;
const unsigned dir = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
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 = NVIC_GetEnableIRQ(USB0_IRQn);
NVIC_DisableIRQ(USB0_IRQn);
if (bd->own) {
TU_LOG1("DCD XFER fail %x %d %lx %lx\r\n", ep_addr, total_bytes, ep->state, bd->head);
return false; /* The last transfer has not completed */
}
ep->length = total_bytes;
ep->remaining = total_bytes;
@@ -386,42 +428,69 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t to
next->addr = buffer + mps;
next->own = 1;
}
bd->bc = total_bytes >= mps ? mps: total_bytes;
bd->addr = buffer;
bd->bc = total_bytes >= mps ? mps: total_bytes;
bd->addr = buffer;
__DSB();
bd->own = 1; /* the own bit must set after addr */
NVIC_EnableIRQ(USB0_IRQn);
bd->own = 1; /* This bit must be set last */
if (ie) NVIC_EnableIRQ(USB0_IRQn);
return true;
}
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
const unsigned epn = ep_addr & 0xFu;
const unsigned epn = tu_edpt_number(ep_addr);
if (0 == epn) {
KHCI->ENDPOINT[epn].ENDPT |= USB_ENDPT_EPSTALL_MASK;
} else {
const unsigned dir = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
buffer_descriptor_t *bd = _dcd.bdt[epn][dir];
bd[0].bdt_stall = 1;
bd[1].bdt_stall = 1;
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 = NVIC_GetEnableIRQ(USB0_IRQn);
NVIC_DisableIRQ(USB0_IRQn);
bd->bdt_stall = 1;
__DSB();
bd->own = 1; /* This bit must be set last */
if (ie) NVIC_EnableIRQ(USB0_IRQn);
}
}
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
const unsigned epn = ep_addr & 0xFu;
const unsigned dir = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
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,);
bd[odd ^ 1].own = 0;
bd[odd ^ 1].data = 1;
bd[odd ^ 1].bdt_stall = 0;
bd[odd].own = 0;
bd[odd].data = 0;
bd[odd].bdt_stall = 0;
const unsigned ie = NVIC_GetEnableIRQ(USB0_IRQn);
NVIC_DisableIRQ(USB0_IRQn);
bd[odd].own = 0;
__DSB();
// 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 = KHCI->ENDPOINT[epn].ENDPT;
if (endpt & USB_ENDPT_EPSTALL_MASK) {
KHCI->ENDPOINT[epn].ENDPT = endpt & ~USB_ENDPT_EPSTALL_MASK;
}
if (ie) NVIC_EnableIRQ(USB0_IRQn);
}
//--------------------------------------------------------------------+
@@ -429,48 +498,59 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
//--------------------------------------------------------------------+
void dcd_int_handler(uint8_t rhport)
{
(void) rhport;
uint32_t is = KHCI->ISTAT;
uint32_t msk = KHCI->INTEN;
// clear non-enabled interrupts
KHCI->ISTAT = is & ~msk;
is &= msk;
if (is & USB_ISTAT_ERROR_MASK) {
/* TODO: */
uint32_t es = KHCI->ERRSTAT;
KHCI->ERRSTAT = es;
KHCI->ISTAT = is; /* discard any pending events */
return;
}
if (is & USB_ISTAT_USBRST_MASK) {
KHCI->ISTAT = is; /* discard any pending events */
process_bus_reset(rhport);
return;
}
if (is & USB_ISTAT_SLEEP_MASK) {
// TU_LOG2("Suspend: "); TU_LOG2_HEX(is);
// 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
KHCI->ISTAT = USB_ISTAT_SLEEP_MASK;
process_bus_inactive(rhport);
return;
process_bus_sleep(rhport);
}
#if 0 // ISTAT_RESUME never trigger, probably for host mode ?
if (is & USB_ISTAT_RESUME_MASK) {
// TU_LOG2("ISTAT Resume: "); TU_LOG2_HEX(is);
KHCI->ISTAT = USB_ISTAT_RESUME_MASK;
process_bus_active(rhport);
return;
process_bus_resume(rhport);
}
#endif
if (KHCI->USBTRC0 & USB_USBTRC0_USB_RESUME_INT_MASK) {
// TU_LOG2("USBTRC0 Resume: "); TU_LOG2_HEX(is); TU_LOG2_HEX(KHCI->USBTRC0);
process_bus_resume(rhport);
}
if (is & USB_ISTAT_SOFTOK_MASK) {
KHCI->ISTAT = USB_ISTAT_SOFTOK_MASK;
dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
return;
}
if (is & USB_ISTAT_STALL_MASK) {
KHCI->ISTAT = USB_ISTAT_STALL_MASK;
process_stall(rhport);
return;
}
if (is & USB_ISTAT_TOKDNE_MASK) {
process_tokdne(rhport);
return;
}
}
+6
View File
@@ -326,6 +326,12 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
return true;
}
void dcd_edpt_close_all (uint8_t rhport)
{
(void) rhport;
// TODO implement dcd_edpt_close_all()
}
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
@@ -323,6 +323,12 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
return true;
}
void dcd_edpt_close_all (uint8_t rhport)
{
(void) rhport;
// TODO implement dcd_edpt_close_all()
}
static void prepare_setup_packet(uint8_t rhport)
{
if (_dcd_controller[rhport].max_speed == TUSB_SPEED_FULL )
@@ -127,7 +127,7 @@ typedef struct
__I uint32_t ENDPTSTAT; ///< Endpoint Status
__IO uint32_t ENDPTCOMPLETE; ///< Endpoint Complete
__IO uint32_t ENDPTCTRL[8]; ///< Endpoint Control 0 - 7
} dcd_registers_t;
} dcd_registers_t, hcd_registers_t;
#ifdef __cplusplus
}
@@ -57,11 +57,15 @@
// ENDPTCTRL
enum {
ENDPTCTRL_STALL = TU_BIT(0),
ENDPTCTRL_TOGGLE_INHIBIT = TU_BIT(5), ///< used for test only
ENDPTCTRL_TOGGLE_INHIBIT = TU_BIT(5), // used for test only
ENDPTCTRL_TOGGLE_RESET = TU_BIT(6),
ENDPTCTRL_ENABLE = TU_BIT(7)
};
enum {
ENDPTCTRL_TYPE_POS = 2, // Endpoint type is 2-bit field
};
// USBSTS, USBINTR
enum {
INTR_USB = TU_BIT(0),
@@ -91,12 +95,15 @@ typedef struct
uint32_t : 3 ;
uint32_t int_on_complete : 1 ;
volatile uint32_t total_bytes : 15 ;
uint32_t : 0 ;
uint32_t : 1 ;
// Word 2-6: 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
uint32_t buffer[5]; ///< buffer1 has frame_n for TODO Isochronous
//------------- DCD Area -------------//
//--------------------------------------------------------------------+
// TD is 32 bytes aligned but occupies only 28 bytes
// Therefore there are 4 bytes padding that we can use.
//--------------------------------------------------------------------+
uint16_t expected_bytes;
uint8_t reserved[2];
} dcd_qtd_t;
@@ -109,11 +116,10 @@ typedef struct
// Word 0: Capabilities and Characteristics
uint32_t : 15 ; ///< Number of packets executed per transaction descriptor 00 - Execute N transactions as demonstrated by the USB variable length protocol where N is computed using Max_packet_length and the Total_bytes field in the dTD. 01 - Execute one transaction 10 - Execute two transactions 11 - Execute three transactions Remark: Non-isochronous endpoints must set MULT = 00. Remark: Isochronous endpoints must set MULT = 01, 10, or 11 as needed.
uint32_t int_on_setup : 1 ; ///< Interrupt on setup This bit is used on control type endpoints to indicate if USBINT is set in response to a setup being received.
uint32_t max_package_size : 11 ; ///< This directly corresponds to the maximum packet size of the associated endpoint (wMaxPacketSize)
uint32_t max_packet_size : 11 ; ///< Endpoint's wMaxPacketSize
uint32_t : 2 ;
uint32_t zero_length_termination : 1 ; ///< This bit is used for non-isochronous endpoints to indicate when a zero-length packet is received to terminate transfers in case the total transfer length is “multiple”. 0 - Enable zero-length packet to terminate transfers equal to a multiple of Max_packet_length (default). 1 - Disable zero-length packet on transfers that are equal in length to a multiple Max_packet_length.
uint32_t iso_mult : 2 ; ///<
uint32_t : 0 ;
// Word 1: Current qTD Pointer
volatile uint32_t qtd_addr;
@@ -125,10 +131,11 @@ typedef struct
volatile tusb_control_request_t setup_request;
//--------------------------------------------------------------------+
/// Due to the fact QHD is 64 bytes aligned but occupies only 48 bytes
/// thus there are 16 bytes padding free that we can make use of.
// QHD is 64 bytes aligned but occupies only 48 bytes
// Therefore there are 16 bytes padding that we can use.
//--------------------------------------------------------------------+
uint8_t reserved[16];
tu_fifo_t * ff;
uint8_t reserved[12];
} dcd_qhd_t;
TU_VERIFY_STATIC( sizeof(dcd_qhd_t) == 64, "size is not correct");
@@ -145,10 +152,6 @@ typedef struct
}dcd_controller_t;
#if CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX
// Each endpoint with direction (IN/OUT) occupies a queue head
// Therefore QHD_MAX is 2 x max endpoint count
#define QHD_MAX (8*2)
static const dcd_controller_t _dcd_controller[] =
{
// RT1010 and RT1020 only has 1 USB controller
@@ -161,8 +164,6 @@ typedef struct
};
#else
#define QHD_MAX (6*2)
static const dcd_controller_t _dcd_controller[] =
{
{ .regs = (dcd_registers_t*) LPC_USB0_BASE, .irqnum = USB0_IRQn, .ep_count = 6 },
@@ -174,8 +175,10 @@ typedef struct
typedef struct {
// Must be at 2K alignment
dcd_qhd_t qhd[QHD_MAX] TU_ATTR_ALIGNED(64);
dcd_qtd_t qtd[QHD_MAX] TU_ATTR_ALIGNED(32); // for portability, TinyUSB only queue 1 TD for each Qhd
// Each endpoint with direction (IN/OUT) occupies a queue head
// for portability, TinyUSB only queue 1 TD for each Qhd
dcd_qhd_t qhd[DCD_ATTR_ENDPOINT_MAX][2] TU_ATTR_ALIGNED(64);
dcd_qtd_t qtd[DCD_ATTR_ENDPOINT_MAX][2] TU_ATTR_ALIGNED(32);
}dcd_data_t;
CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(2048)
@@ -195,9 +198,9 @@ static void bus_reset(uint8_t rhport)
// endpoint type of the unused direction must be changed from the control type to any other
// type (e.g. bulk). Leaving an un-configured endpoint control will cause undefined behavior
// for the data PID tracking on the active endpoint.
for( int i=1; i < _dcd_controller[rhport].ep_count; i++)
for( uint8_t i=1; i < _dcd_controller[rhport].ep_count; i++)
{
dcd_reg->ENDPTCTRL[i] = (TUSB_XFER_BULK << 2) | (TUSB_XFER_BULK << 18);
dcd_reg->ENDPTCTRL[i] = (TUSB_XFER_BULK << ENDPTCTRL_TYPE_POS) | (TUSB_XFER_BULK << (16+ENDPTCTRL_TYPE_POS));
}
//------------- Clear All Registers -------------//
@@ -217,11 +220,11 @@ static void bus_reset(uint8_t rhport)
tu_memclr(&_dcd_data, sizeof(dcd_data_t));
//------------- Set up Control Endpoints (0 OUT, 1 IN) -------------//
_dcd_data.qhd[0].zero_length_termination = _dcd_data.qhd[1].zero_length_termination = 1;
_dcd_data.qhd[0].max_package_size = _dcd_data.qhd[1].max_package_size = CFG_TUD_ENDPOINT0_SIZE;
_dcd_data.qhd[0].qtd_overlay.next = _dcd_data.qhd[1].qtd_overlay.next = QTD_NEXT_INVALID;
_dcd_data.qhd[0][0].zero_length_termination = _dcd_data.qhd[0][1].zero_length_termination = 1;
_dcd_data.qhd[0][0].max_packet_size = _dcd_data.qhd[0][1].max_packet_size = CFG_TUD_ENDPOINT0_SIZE;
_dcd_data.qhd[0][0].qtd_overlay.next = _dcd_data.qhd[0][1].qtd_overlay.next = QTD_NEXT_INVALID;
_dcd_data.qhd[0].int_on_setup = 1; // OUT only
_dcd_data.qhd[0][0].int_on_setup = 1; // OUT only
}
void dcd_init(uint8_t rhport)
@@ -238,14 +241,15 @@ void dcd_init(uint8_t rhport)
dcd_reg->USBMODE = USBMODE_CM_DEVICE;
dcd_reg->OTGSC = OTGSC_VBUS_DISCHARGE | OTGSC_OTG_TERMINATION;
// TODO Force fullspeed on non-highspeed port
// dcd_reg->PORTSC1 = PORTSC1_FORCE_FULL_SPEED;
#if !TUD_OPT_HIGH_SPEED
dcd_reg->PORTSC1 = PORTSC1_FORCE_FULL_SPEED;
#endif
CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t));
dcd_reg->ENDPTLISTADDR = (uint32_t) _dcd_data.qhd; // Endpoint List Address has to be 2K alignment
dcd_reg->USBSTS = dcd_reg->USBSTS;
dcd_reg->USBINTR = INTR_USB | INTR_ERROR | INTR_PORT_CHANGE | INTR_RESET | INTR_SUSPEND /*| INTR_SOF*/;
dcd_reg->USBINTR = INTR_USB | INTR_ERROR | INTR_PORT_CHANGE | INTR_SUSPEND;
dcd_reg->USBCMD &= ~0x00FF0000; // Interrupt Threshold Interval = 0
dcd_reg->USBCMD |= USBCMD_RUN_STOP; // Connect
@@ -272,7 +276,8 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
void dcd_remote_wakeup(uint8_t rhport)
{
(void) rhport;
dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
dcd_reg->PORTSC1 |= PORTSC1_FORCE_PORT_RESUME;
}
void dcd_connect(uint8_t rhport)
@@ -290,26 +295,33 @@ void dcd_disconnect(uint8_t rhport)
//--------------------------------------------------------------------+
// HELPER
//--------------------------------------------------------------------+
// index to bit position in register
static inline uint8_t ep_idx2bit(uint8_t ep_idx)
{
return ep_idx/2 + ( (ep_idx%2) ? 16 : 0);
}
static void qtd_init(dcd_qtd_t* p_qtd, void * data_ptr, uint16_t total_bytes)
{
// Force the CPU to flush the buffer. We increase the size by 31 because the call aligns the
// address to 32-byte boundaries. Buffer must be word aligned
CleanInvalidateDCache_by_Addr((uint32_t*) tu_align((uint32_t) data_ptr, 4), total_bytes + 31);
tu_memclr(p_qtd, sizeof(dcd_qtd_t));
p_qtd->next = QTD_NEXT_INVALID;
p_qtd->active = 1;
p_qtd->total_bytes = p_qtd->expected_bytes = total_bytes;
p_qtd->next = QTD_NEXT_INVALID;
p_qtd->active = 1;
p_qtd->total_bytes = p_qtd->expected_bytes = total_bytes;
p_qtd->int_on_complete = true;
if (data_ptr != NULL)
{
p_qtd->buffer[0] = (uint32_t) data_ptr;
p_qtd->buffer[0] = (uint32_t) data_ptr;
uint32_t const bufend = p_qtd->buffer[0] + total_bytes;
for(uint8_t i=1; i<5; i++)
{
p_qtd->buffer[i] |= tu_align4k( p_qtd->buffer[i-1] ) + 4096;
uint32_t const next_page = tu_align4k( p_qtd->buffer[i-1] ) + 4096;
if ( bufend <= next_page ) break;
p_qtd->buffer[i] = next_page;
// TODO page[1] FRAME_N for ISO transfer
}
}
}
@@ -324,12 +336,15 @@ void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
dcd_reg->ENDPTCTRL[epnum] |= ENDPTCTRL_STALL << (dir ? 16 : 0);
// flush to abort any primed buffer
dcd_reg->ENDPTFLUSH = TU_BIT(epnum + (dir ? 16 : 0));
}
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
{
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
// data toggle also need to be reset
dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
@@ -339,39 +354,87 @@ 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)
{
// TODO not support ISO yet
TU_VERIFY ( p_endpoint_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS);
uint8_t const epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress);
uint8_t const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress);
uint8_t const ep_idx = 2*epnum + dir;
uint8_t const epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress);
uint8_t const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress);
// Must not exceed max endpoint number
TU_ASSERT( epnum < _dcd_controller[rhport].ep_count );
//------------- Prepare Queue Head -------------//
dcd_qhd_t * p_qhd = &_dcd_data.qhd[ep_idx];
dcd_qhd_t * p_qhd = &_dcd_data.qhd[epnum][dir];
tu_memclr(p_qhd, sizeof(dcd_qhd_t));
p_qhd->zero_length_termination = 1;
p_qhd->max_package_size = p_endpoint_desc->wMaxPacketSize.size;
p_qhd->max_packet_size = p_endpoint_desc->wMaxPacketSize.size;
if (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS)
{
p_qhd->iso_mult = 1;
}
p_qhd->qtd_overlay.next = QTD_NEXT_INVALID;
CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t));
// Enable EP Control
dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
dcd_reg->ENDPTCTRL[epnum] |= ((p_endpoint_desc->bmAttributes.xfer << 2) | ENDPTCTRL_ENABLE | ENDPTCTRL_TOGGLE_RESET) << (dir ? 16 : 0);
uint32_t const epctrl = (p_endpoint_desc->bmAttributes.xfer << ENDPTCTRL_TYPE_POS) | ENDPTCTRL_ENABLE | ENDPTCTRL_TOGGLE_RESET;
if ( dir == TUSB_DIR_OUT )
{
dcd_reg->ENDPTCTRL[epnum] = (dcd_reg->ENDPTCTRL[epnum] & 0xFFFF0000u) | epctrl;
}else
{
dcd_reg->ENDPTCTRL[epnum] = (dcd_reg->ENDPTCTRL[epnum] & 0x0000FFFFu) | (epctrl << 16);
}
return true;
}
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
void dcd_edpt_close_all (uint8_t rhport)
{
dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
uint8_t const ep_idx = 2*epnum + dir;
// Disable all non-control endpoints
for( uint8_t epnum=1; epnum < _dcd_controller[rhport].ep_count; epnum++)
{
_dcd_data.qhd[epnum][TUSB_DIR_OUT].qtd_overlay.halted = 1;
_dcd_data.qhd[epnum][TUSB_DIR_IN ].qtd_overlay.halted = 1;
dcd_reg->ENDPTFLUSH = TU_BIT(epnum) | TU_BIT(epnum+16);
dcd_reg->ENDPTCTRL[epnum] = (TUSB_XFER_BULK << ENDPTCTRL_TYPE_POS) | (TUSB_XFER_BULK << (16+ENDPTCTRL_TYPE_POS));
}
}
void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
{
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
_dcd_data.qhd[epnum][dir].qtd_overlay.halted = 1;
// Flush EP
uint32_t const flush_mask = TU_BIT(epnum + (dir ? 16 : 0));
dcd_reg->ENDPTFLUSH = flush_mask;
while(dcd_reg->ENDPTFLUSH & flush_mask);
// Clear EP enable
dcd_reg->ENDPTCTRL[epnum] &=~(ENDPTCTRL_ENABLE << (dir ? 16 : 0));
}
static void qhd_start_xfer(uint8_t rhport, uint8_t epnum, uint8_t dir)
{
dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
dcd_qhd_t* p_qhd = &_dcd_data.qhd[epnum][dir];
dcd_qtd_t* p_qtd = &_dcd_data.qtd[epnum][dir];
p_qhd->qtd_overlay.halted = false; // clear any previous error
p_qhd->qtd_overlay.next = (uint32_t) p_qtd; // link qtd to qhd
// flush cache
CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t));
if ( epnum == 0 )
{
@@ -380,23 +443,87 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t t
while(dcd_reg->ENDPTSETUPSTAT & TU_BIT(0)) {}
}
dcd_qhd_t * p_qhd = &_dcd_data.qhd[ep_idx];
dcd_qtd_t * p_qtd = &_dcd_data.qtd[ep_idx];
// Force the CPU to flush the buffer. We increase the size by 32 because the call aligns the
// address to 32-byte boundaries.
// void* cast to suppress cast-align warning, buffer must be
CleanInvalidateDCache_by_Addr((uint32_t*) tu_align((uint32_t) buffer, 4), total_bytes + 31);
//------------- Prepare qtd -------------//
qtd_init(p_qtd, buffer, total_bytes);
p_qtd->int_on_complete = true;
p_qhd->qtd_overlay.next = (uint32_t) p_qtd; // link qtd to qhd
CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t));
// start transfer
dcd_reg->ENDPTPRIME = TU_BIT( ep_idx2bit(ep_idx) ) ;
dcd_reg->ENDPTPRIME = TU_BIT(epnum + (dir ? 16 : 0));
}
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
{
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
dcd_qhd_t* p_qhd = &_dcd_data.qhd[epnum][dir];
dcd_qtd_t* p_qtd = &_dcd_data.qtd[epnum][dir];
// Prepare qtd
qtd_init(p_qtd, buffer, total_bytes);
// Start qhd transfer
p_qhd->ff = NULL;
qhd_start_xfer(rhport, epnum, dir);
return true;
}
// fifo has to be aligned to 4k boundary
bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
{
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
dcd_qhd_t * p_qhd = &_dcd_data.qhd[epnum][dir];
dcd_qtd_t * p_qtd = &_dcd_data.qtd[epnum][dir];
tu_fifo_buffer_info_t fifo_info;
if (dir)
{
tu_fifo_get_read_info(ff, &fifo_info);
} else
{
tu_fifo_get_write_info(ff, &fifo_info);
}
if ( fifo_info.len_lin >= total_bytes )
{
// Linear length is enough for this transfer
qtd_init(p_qtd, fifo_info.ptr_lin, total_bytes);
}
else
{
// linear part is not enough
// prepare TD up to linear length
qtd_init(p_qtd, fifo_info.ptr_lin, fifo_info.len_lin);
if ( !tu_offset4k((uint32_t) fifo_info.ptr_wrap) && !tu_offset4k(tu_fifo_depth(ff)) )
{
// If buffer is aligned to 4K & buffer size is multiple of 4K
// We can make use of buffer page array to also combine the linear + wrapped length
p_qtd->total_bytes = p_qtd->expected_bytes = total_bytes;
for(uint8_t i = 1, page = 0; i < 5; i++)
{
// pick up buffer array where linear ends
if (p_qtd->buffer[i] == 0)
{
p_qtd->buffer[i] = (uint32_t) fifo_info.ptr_wrap + 4096 * page;
page++;
}
}
CleanInvalidateDCache_by_Addr((uint32_t*) tu_align((uint32_t) fifo_info.ptr_wrap, 4), total_bytes - fifo_info.len_wrap + 31);
}
else
{
// TODO we may need to carry the wrapped length after the linear part complete
// for now only transfer up to linear part
}
}
// Start qhd transfer
p_qhd->ff = ff;
qhd_start_xfer(rhport, epnum, dir);
return true;
}
@@ -404,9 +531,42 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t t
//--------------------------------------------------------------------+
// ISR
//--------------------------------------------------------------------+
static void process_edpt_complete_isr(uint8_t rhport, uint8_t epnum, uint8_t dir)
{
dcd_qhd_t * p_qhd = &_dcd_data.qhd[epnum][dir];
dcd_qtd_t * p_qtd = &_dcd_data.qtd[epnum][dir];
uint8_t result = p_qtd->halted ? XFER_RESULT_STALLED :
( p_qtd->xact_err || p_qtd->buffer_err ) ? XFER_RESULT_FAILED : XFER_RESULT_SUCCESS;
if ( result != XFER_RESULT_SUCCESS )
{
dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
// flush to abort error buffer
dcd_reg->ENDPTFLUSH = TU_BIT(epnum + (dir ? 16 : 0));
}
uint16_t const xferred_bytes = p_qtd->expected_bytes - p_qtd->total_bytes;
if (p_qhd->ff)
{
if (dir == TUSB_DIR_IN)
{
tu_fifo_advance_read_pointer(p_qhd->ff, xferred_bytes);
} else
{
tu_fifo_advance_write_pointer(p_qhd->ff, xferred_bytes);
}
}
// only number of bytes in the IOC qtd
dcd_event_xfer_complete(rhport, tu_edpt_addr(epnum, dir), xferred_bytes, result, true);
}
void dcd_int_handler(uint8_t rhport)
{
dcd_registers_t* const dcd_reg = _dcd_controller[rhport].regs;
dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
uint32_t const int_enable = dcd_reg->USBINTR;
uint32_t const int_status = dcd_reg->USBSTS & int_enable;
@@ -415,18 +575,46 @@ void dcd_int_handler(uint8_t rhport)
// disabled interrupt sources
if (int_status == 0) return;
if (int_status & INTR_RESET)
{
bus_reset(rhport);
uint32_t speed = (dcd_reg->PORTSC1 & PORTSC1_PORT_SPEED) >> PORTSC1_PORT_SPEED_POS;
dcd_event_bus_reset(rhport, (tusb_speed_t) speed, true);
}
// Set if the port controller enters the full or high-speed operational state.
// either from Bus Reset or Suspended state
if (int_status & INTR_PORT_CHANGE)
{
// TU_LOG2("PortChange %08lx\r\n", dcd_reg->PORTSC1);
// Reset interrupt is not enabled, we manually check if Port Change is due
// to connection / disconnection
if ( dcd_reg->USBSTS & INTR_RESET )
{
dcd_reg->USBSTS = INTR_RESET;
if (dcd_reg->PORTSC1 & PORTSC1_CURRENT_CONNECT_STATUS)
{
uint32_t const speed = (dcd_reg->PORTSC1 & PORTSC1_PORT_SPEED) >> PORTSC1_PORT_SPEED_POS;
bus_reset(rhport);
dcd_event_bus_reset(rhport, (tusb_speed_t) speed, true);
}else
{
dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true);
}
}
else
{
// Triggered by resuming from suspended state
if ( !(dcd_reg->PORTSC1 & PORTSC1_SUSPEND) )
{
dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
}
}
}
if (int_status & INTR_SUSPEND)
{
// TU_LOG2("Suspend %08lx\r\n", dcd_reg->PORTSC1);
if (dcd_reg->PORTSC1 & PORTSC1_SUSPEND)
{
// Note: Host may delay more than 3 ms before and/or after bus reset before doing enumeration.
// Skip suspend event if we are not addressed
if ((dcd_reg->DEVICEADDR >> 25) & 0x0f)
{
dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
@@ -434,21 +622,11 @@ void dcd_int_handler(uint8_t rhport)
}
}
// Make sure we read the latest version of _dcd_data.
CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t));
// TODO disconnection does not generate interrupt !!!!!!
// if (int_status & INTR_PORT_CHANGE)
// {
// if ( !(dcd_reg->PORTSC1 & PORTSC1_CURRENT_CONNECT_STATUS) )
// {
// dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_UNPLUGGED };
// dcd_event_handler(&event, true);
// }
// }
if (int_status & INTR_USB)
{
// Make sure we read the latest version of _dcd_data.
CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t));
uint32_t const edpt_complete = dcd_reg->ENDPTCOMPLETE;
dcd_reg->ENDPTCOMPLETE = edpt_complete; // acknowledge
@@ -456,26 +634,21 @@ void dcd_int_handler(uint8_t rhport)
{
//------------- Set up Received -------------//
// 23.10.10.2 Operational model for setup transfers
dcd_reg->ENDPTSETUPSTAT = dcd_reg->ENDPTSETUPSTAT;// acknowledge
dcd_reg->ENDPTSETUPSTAT = dcd_reg->ENDPTSETUPSTAT;
dcd_event_setup_received(rhport, (uint8_t*) &_dcd_data.qhd[0].setup_request, true);
dcd_event_setup_received(rhport, (uint8_t*) &_dcd_data.qhd[0][0].setup_request, true);
}
// 23.10.12.3 Failed QTD also get ENDPTCOMPLETE set
// nothing to do, we will submit xfer as error to usbd
// if (int_status & INTR_ERROR) { }
if ( edpt_complete )
{
for(uint8_t ep_idx = 0; ep_idx < QHD_MAX; ep_idx++)
for(uint8_t epnum = 0; epnum < DCD_ATTR_ENDPOINT_MAX; epnum++)
{
if ( tu_bit_test(edpt_complete, ep_idx2bit(ep_idx)) )
{
// 23.10.12.3 Failed QTD also get ENDPTCOMPLETE set
dcd_qtd_t * p_qtd = &_dcd_data.qtd[ep_idx];
uint8_t result = p_qtd->halted ? XFER_RESULT_STALLED :
( p_qtd->xact_err ||p_qtd->buffer_err ) ? XFER_RESULT_FAILED : XFER_RESULT_SUCCESS;
uint8_t const ep_addr = (ep_idx/2) | ( (ep_idx & 0x01) ? TUSB_DIR_IN_MASK : 0 );
dcd_event_xfer_complete(rhport, ep_addr, p_qtd->expected_bytes - p_qtd->total_bytes, result, true); // only number of bytes in the IOC qtd
}
if ( tu_bit_test(edpt_complete, epnum) ) process_edpt_complete_isr(rhport, epnum, TUSB_DIR_OUT);
if ( tu_bit_test(edpt_complete, epnum+16) ) process_edpt_complete_isr(rhport, epnum, TUSB_DIR_IN);
}
}
}
@@ -484,9 +657,6 @@ void dcd_int_handler(uint8_t rhport)
{
dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
}
if (int_status & INTR_NAK) {}
if (int_status & INTR_ERROR) TU_ASSERT(false, );
}
#endif
@@ -43,7 +43,7 @@
#include "common/tusb_common.h"
#include "common_transdimension.h"
#include "portable/ehci/hcd_ehci.h"
#include "portable/ehci/ehci_api.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
@@ -82,26 +82,26 @@ typedef struct
bool hcd_init(uint8_t rhport)
{
dcd_registers_t* dcd_reg = (dcd_registers_t*) _hcd_controller[rhport].regs_base;
hcd_registers_t* hcd_reg = (hcd_registers_t*) _hcd_controller[rhport].regs_base;
// Reset controller
dcd_reg->USBCMD |= USBCMD_RESET;
while( dcd_reg->USBCMD & USBCMD_RESET ) {}
hcd_reg->USBCMD |= USBCMD_RESET;
while( hcd_reg->USBCMD & USBCMD_RESET ) {}
// Set mode to device, must be set immediately after reset
#if CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX
// LPC18XX/43XX need to set VBUS Power Select to HIGH
// RHPORT1 is fullspeed only (need external PHY for Highspeed)
dcd_reg->USBMODE = USBMODE_CM_HOST | USBMODE_VBUS_POWER_SELECT;
if (rhport == 1) dcd_reg->PORTSC1 |= PORTSC1_FORCE_FULL_SPEED;
hcd_reg->USBMODE = USBMODE_CM_HOST | USBMODE_VBUS_POWER_SELECT;
if (rhport == 1) hcd_reg->PORTSC1 |= PORTSC1_FORCE_FULL_SPEED;
#else
dcd_reg->USBMODE = USBMODE_CM_HOST;
hcd_reg->USBMODE = USBMODE_CM_HOST;
#endif
// FIXME force full speed, still have issue with Highspeed enumeration
dcd_reg->PORTSC1 |= PORTSC1_FORCE_FULL_SPEED;
hcd_reg->PORTSC1 |= PORTSC1_FORCE_FULL_SPEED;
return hcd_ehci_init(rhport);
return ehci_init(rhport, (uint32_t) &hcd_reg->CAPLENGTH, (uint32_t) &hcd_reg->USBCMD);
}
void hcd_int_enable(uint8_t rhport)
@@ -114,12 +114,4 @@ void hcd_int_disable(uint8_t rhport)
NVIC_DisableIRQ(_hcd_controller[rhport].irqnum);
}
uint32_t hcd_ehci_register_addr(uint8_t rhport)
{
dcd_registers_t* hcd_reg = (dcd_registers_t*) _hcd_controller[rhport].regs_base;
// EHCI USBCMD has same address within dcd_register_t
return (uint32_t) &hcd_reg->USBCMD;
}
#endif
+6 -5
View File
@@ -24,10 +24,9 @@
* This file is part of the TinyUSB stack.
*/
#include <common/tusb_common.h>
#include "host/hcd_attr.h"
#if TUSB_OPT_HOST_ENABLED && \
(CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX)
#if TUSB_OPT_HOST_ENABLED && defined(HCD_ATTR_OHCI)
//--------------------------------------------------------------------+
// INCLUDE
@@ -35,7 +34,6 @@
#include "osal/osal.h"
#include "host/hcd.h"
#include "host/usbh_hcd.h"
#include "ohci.h"
// TODO remove
@@ -280,10 +278,13 @@ static void ed_init(ohci_ed_t *p_ed, uint8_t dev_addr, uint16_t ep_size, uint8_t
tu_memclr(p_ed, sizeof(ohci_ed_t));
}
hcd_devtree_info_t devtree_info;
hcd_devtree_get_info(dev_addr, &devtree_info);
p_ed->dev_addr = dev_addr;
p_ed->ep_number = ep_addr & 0x0F;
p_ed->pid = (xfer_type == TUSB_XFER_CONTROL) ? PID_FROM_TD : (tu_edpt_dir(ep_addr) ? PID_IN : PID_OUT);
p_ed->speed = _usbh_devices[dev_addr].speed;
p_ed->speed = devtree_info.speed;
p_ed->is_iso = (xfer_type == TUSB_XFER_ISOCHRONOUS) ? 1 : 0;
p_ed->max_packet_size = ep_size;
+1 -1
View File
@@ -159,7 +159,7 @@ typedef struct TU_ATTR_ALIGNED(256)
struct {
ohci_ed_t ed;
ohci_gtd_t gtd;
}control[CFG_TUSB_HOST_DEVICE_MAX+1];
}control[CFG_TUH_DEVICE_MAX+1];
// ochi_itd_t itd[OHCI_MAX_ITD]; // itd requires alignment of 32
ohci_ed_t ed_pool[HCD_MAX_ENDPOINT];
+194 -231
View File
@@ -37,6 +37,11 @@
#include "device/dcd.h"
// Current implementation force vbus detection as always present, causing device think it is always plugged into host.
// Therefore it cannot detect disconnect event, mistaken it as suspend.
// Note: won't work if change to 0 (for now)
#define FORCE_VBUS_DETECT 1
/*------------------------------------------------------------------*/
/* Low level controller
*------------------------------------------------------------------*/
@@ -48,129 +53,44 @@
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] = {0};
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)
{
return &hw_endpoints[num][dir];
return &hw_endpoints[num][dir];
}
static struct hw_endpoint *hw_endpoint_get_by_addr(uint8_t ep_addr)
{
uint8_t num = tu_edpt_number(ep_addr);
tusb_dir_t dir = tu_edpt_dir(ep_addr);
return hw_endpoint_get_by_num(num, dir);
uint8_t num = tu_edpt_number(ep_addr);
tusb_dir_t dir = tu_edpt_dir(ep_addr);
return hw_endpoint_get_by_num(num, dir);
}
static void _hw_endpoint_alloc(struct hw_endpoint *ep)
static void _hw_endpoint_alloc(struct hw_endpoint *ep, uint8_t transfer_type)
{
uint16_t size = tu_min16(64, ep->wMaxPacketSize);
// size must be multiple of 64
uint16_t size = tu_div_ceil(ep->wMaxPacketSize, 64) * 64u;
// Assumes single buffered for now
ep->hw_data_buf = next_buffer_ptr;
next_buffer_ptr += size;
// Bits 0-5 are ignored by the controller so make sure these are 0
if ((uintptr_t)next_buffer_ptr & 0b111111u)
{
// Round up to the next 64
uint32_t fixptr = (uintptr_t)next_buffer_ptr;
fixptr &= ~0b111111u;
fixptr += 64;
pico_info("Rounding non 64 byte boundary buffer up from %x to %x\n", (uintptr_t)next_buffer_ptr, fixptr);
next_buffer_ptr = (uint8_t*)fixptr;
}
assert(((uintptr_t)next_buffer_ptr & 0b111111u) == 0);
uint dpram_offset = hw_data_offset(ep->hw_data_buf);
assert(hw_data_offset(next_buffer_ptr) <= USB_DPRAM_MAX);
// double buffered Bulk endpoint
if ( transfer_type == TUSB_XFER_BULK )
{
size *= 2u;
}
pico_info("Alloced %d bytes at offset 0x%x (0x%p) for ep %d %s\n",
size,
dpram_offset,
ep->hw_data_buf,
ep->num,
ep_dir_string[ep->in]);
ep->hw_data_buf = next_buffer_ptr;
next_buffer_ptr += size;
// Fill in endpoint control register with buffer offset
uint32_t reg = EP_CTRL_ENABLE_BITS
| EP_CTRL_INTERRUPT_PER_BUFFER
| (ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB)
| dpram_offset;
assert(((uintptr_t )next_buffer_ptr & 0b111111u) == 0);
uint dpram_offset = hw_data_offset(ep->hw_data_buf);
assert(hw_data_offset(next_buffer_ptr) <= USB_DPRAM_MAX);
*ep->endpoint_control = reg;
}
pico_info(" Alloced %d bytes at offset 0x%x (0x%p)\r\n", size, dpram_offset, ep->hw_data_buf);
static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type)
{
const uint8_t num = tu_edpt_number(ep_addr);
const tusb_dir_t dir = tu_edpt_dir(ep_addr);
ep->ep_addr = ep_addr;
// For device, IN is a tx transfer and OUT is an rx transfer
ep->rx = (dir == TUSB_DIR_OUT);
// Response to a setup packet on EP0 starts with pid of 1
ep->next_pid = num == 0 ? 1u : 0u;
// Fill in endpoint control register with buffer offset
uint32_t const reg = EP_CTRL_ENABLE_BITS | (transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | dpram_offset;
// Add some checks around the max packet size
if (transfer_type == TUSB_XFER_ISOCHRONOUS)
{
if (wMaxPacketSize > USB_MAX_ISO_PACKET_SIZE)
{
panic("Isochronous wMaxPacketSize %d too large", wMaxPacketSize);
}
}
else
{
if (wMaxPacketSize > USB_MAX_PACKET_SIZE)
{
panic("Isochronous wMaxPacketSize %d too large", wMaxPacketSize);
}
}
ep->wMaxPacketSize = wMaxPacketSize;
ep->transfer_type = transfer_type;
// Every endpoint has a buffer control register in dpram
if (dir == TUSB_DIR_IN)
{
ep->buffer_control = &usb_dpram->ep_buf_ctrl[num].in;
}
else
{
ep->buffer_control = &usb_dpram->ep_buf_ctrl[num].out;
}
// Clear existing buffer control state
*ep->buffer_control = 0;
if (num == 0)
{
// EP0 has no endpoint control register because
// the buffer offsets are fixed
ep->endpoint_control = NULL;
// Buffer offset is fixed
ep->hw_data_buf = (uint8_t*)&usb_dpram->ep0_buf_a[0];
}
else
{
// Set the endpoint control register (starts at EP1, hence num-1)
if (dir == TUSB_DIR_IN)
{
ep->endpoint_control = &usb_dpram->ep_ctrl[num-1].in;
}
else
{
ep->endpoint_control = &usb_dpram->ep_ctrl[num-1].out;
}
// Now if it hasn't already been done
//alloc a buffer and fill in endpoint control register
if(!(ep->configured))
{
_hw_endpoint_alloc(ep);
}
}
ep->configured = true;
*ep->endpoint_control = reg;
}
#if 0 // todo unused
@@ -192,40 +112,87 @@ static void hw_endpoint_close(uint8_t ep_addr)
}
#endif
static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t bmAttributes)
static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type)
{
struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr);
_hw_endpoint_init(ep, ep_addr, wMaxPacketSize, bmAttributes);
struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr);
const uint8_t num = tu_edpt_number(ep_addr);
const tusb_dir_t dir = tu_edpt_dir(ep_addr);
ep->ep_addr = ep_addr;
// For device, IN is a tx transfer and OUT is an rx transfer
ep->rx = (dir == TUSB_DIR_OUT);
ep->next_pid = 0u;
ep->wMaxPacketSize = wMaxPacketSize;
ep->transfer_type = transfer_type;
// Every endpoint has a buffer control register in dpram
if ( dir == TUSB_DIR_IN )
{
ep->buffer_control = &usb_dpram->ep_buf_ctrl[num].in;
}
else
{
ep->buffer_control = &usb_dpram->ep_buf_ctrl[num].out;
}
// Clear existing buffer control state
*ep->buffer_control = 0;
if ( num == 0 )
{
// EP0 has no endpoint control register because the buffer offsets are fixed
ep->endpoint_control = NULL;
// Buffer offset is fixed (also double buffered)
ep->hw_data_buf = (uint8_t*) &usb_dpram->ep0_buf_a[0];
}
else
{
// Set the endpoint control register (starts at EP1, hence num-1)
if ( dir == TUSB_DIR_IN )
{
ep->endpoint_control = &usb_dpram->ep_ctrl[num - 1].in;
}
else
{
ep->endpoint_control = &usb_dpram->ep_ctrl[num - 1].out;
}
// alloc a buffer and fill in endpoint control register
_hw_endpoint_alloc(ep, transfer_type);
}
}
static void hw_endpoint_xfer(uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes, bool start)
static void hw_endpoint_xfer(uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes)
{
struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr);
_hw_endpoint_xfer(ep, buffer, total_bytes, start);
hw_endpoint_xfer_start(ep, buffer, total_bytes);
}
static void hw_handle_buff_status(void)
{
uint32_t remaining_buffers = usb_hw->buf_status;
pico_trace("buf_status 0x%08x\n", remaining_buffers);
pico_trace("buf_status = 0x%08x\n", remaining_buffers);
uint bit = 1u;
for (uint i = 0; remaining_buffers && i < USB_MAX_ENDPOINTS * 2; i++)
{
if (remaining_buffers & bit)
{
uint __unused which = (usb_hw->buf_cpu_should_handle & bit) ? 1 : 0;
// Should be single buffered
assert(which == 0);
// clear this in advance
usb_hw_clear->buf_status = bit;
// IN transfer for even i, OUT transfer for odd i
struct hw_endpoint *ep = hw_endpoint_get_by_num(i >> 1u, !(i & 1u));
// Continue xfer
bool done = _hw_endpoint_xfer_continue(ep);
bool done = hw_endpoint_xfer_continue(ep);
if (done)
{
// Notify
dcd_event_xfer_complete(0, ep->ep_addr, ep->len, XFER_RESULT_SUCCESS, true);
dcd_event_xfer_complete(0, ep->ep_addr, ep->xferred_len, XFER_RESULT_SUCCESS, true);
hw_endpoint_reset_transfer(ep);
}
remaining_buffers &= ~bit;
@@ -234,7 +201,7 @@ static void hw_handle_buff_status(void)
}
}
static void reset_ep0(void)
static 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
@@ -243,63 +210,36 @@ static void reset_ep0(void)
{
struct hw_endpoint *ep = hw_endpoint_get_by_addr(addrs[i]);
ep->next_pid = 1u;
ep->stalled = 0;
}
}
static void ep0_0len_status(void)
static void reset_non_control_endpoints(void)
{
// Send 0len complete response on EP0 IN
reset_ep0();
hw_endpoint_xfer(0x80, NULL, 0, true);
}
// Disable all non-control
for ( uint8_t i = 0; i < USB_MAX_ENDPOINTS-1; i++ )
{
usb_dpram->ep_ctrl[i].in = 0;
usb_dpram->ep_ctrl[i].out = 0;
}
static void _hw_endpoint_stall(struct hw_endpoint *ep)
{
assert(!ep->stalled);
if (tu_edpt_number(ep->ep_addr) == 0)
{
// A stall on EP0 has to be armed so it can be cleared on the next setup packet
usb_hw_set->ep_stall_arm = (tu_edpt_dir(ep->ep_addr) == TUSB_DIR_IN) ? USB_EP_STALL_ARM_EP0_IN_BITS : USB_EP_STALL_ARM_EP0_OUT_BITS;
}
_hw_endpoint_buffer_control_set_mask32(ep, USB_BUF_CTRL_STALL);
ep->stalled = true;
}
static void hw_endpoint_stall(uint8_t ep_addr)
{
struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr);
_hw_endpoint_stall(ep);
}
static void _hw_endpoint_clear_stall(struct hw_endpoint *ep)
{
if (tu_edpt_number(ep->ep_addr) == 0)
{
// Probably already been cleared but no harm
usb_hw_clear->ep_stall_arm = (tu_edpt_dir(ep->ep_addr) == TUSB_DIR_IN) ? USB_EP_STALL_ARM_EP0_IN_BITS : USB_EP_STALL_ARM_EP0_OUT_BITS;
}
_hw_endpoint_buffer_control_clear_mask32(ep, USB_BUF_CTRL_STALL);
ep->stalled = false;
}
static void hw_endpoint_clear_stall(uint8_t ep_addr)
{
struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr);
_hw_endpoint_clear_stall(ep);
// clear non-control hw endpoints
tu_memclr(hw_endpoints[1], sizeof(hw_endpoints) - 2*sizeof(hw_endpoint_t));
next_buffer_ptr = &usb_dpram->epx_data[0];
}
static void dcd_rp2040_irq(void)
{
uint32_t status = usb_hw->ints;
uint32_t const status = usb_hw->ints;
uint32_t handled = 0;
if (status & USB_INTS_SETUP_REQ_BITS)
{
handled |= USB_INTS_SETUP_REQ_BITS;
uint8_t const *setup = (uint8_t const *)&usb_dpram->setup_packet;
// Clear stall bits and reset pid
reset_ep0();
// reset pid to both 1 (data and ack)
reset_ep0_pid();
// Pass setup packet to tiny usb
dcd_event_setup_received(0, setup, true);
usb_hw_clear->sie_status = USB_SIE_STATUS_SETUP_REC_BITS;
@@ -311,7 +251,9 @@ static void dcd_rp2040_irq(void)
hw_handle_buff_status();
}
// SE0 for 2 us or more, usually together with Bus Reset
#if FORCE_VBUS_DETECT == 0
// Since we force VBUS detect On, device will always think it is connected and
// couldn't distinguish between disconnect and suspend
if (status & USB_INTS_DEV_CONN_DIS_BITS)
{
handled |= USB_INTS_DEV_CONN_DIS_BITS;
@@ -327,28 +269,26 @@ static void dcd_rp2040_irq(void)
usb_hw_clear->sie_status = USB_SIE_STATUS_CONNECTED_BITS;
}
#endif
// SE0 for 2.5 us or more
// SE0 for 2.5 us or more (will last at least 10ms)
if (status & USB_INTS_BUS_RESET_BITS)
{
pico_trace("BUS RESET\n");
usb_hw->dev_addr_ctrl = 0;
handled |= USB_INTS_BUS_RESET_BITS;
usb_hw->dev_addr_ctrl = 0;
reset_non_control_endpoints();
dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
usb_hw_clear->sie_status = USB_SIE_STATUS_BUS_RESET_BITS;
#if TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX
// Only run enumeration walk-around if pull up is enabled
if ( usb_hw->sie_ctrl & USB_SIE_CTRL_PULLUP_EN_BITS )
{
rp2040_usb_device_enumeration_fix();
}
if ( usb_hw->sie_ctrl & USB_SIE_CTRL_PULLUP_EN_BITS ) rp2040_usb_device_enumeration_fix();
#endif
}
#if 0
// TODO Enable SUSPEND & RESUME interrupt and test later on with/without VBUS detection
/* Note from pico datasheet 4.1.2.6.4 (v1.2)
* If you enable the suspend interrupt, it is likely you will see a suspend interrupt when
* the device is first connected but the bus is idle. The bus can be idle for a few ms before
@@ -370,7 +310,6 @@ static void dcd_rp2040_irq(void)
dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
usb_hw_clear->sie_status = USB_SIE_STATUS_RESUME_BITS;
}
#endif
if (status ^ handled)
{
@@ -388,36 +327,42 @@ static void dcd_rp2040_irq(void)
/*------------------------------------------------------------------*/
/* Controller API
*------------------------------------------------------------------*/
void dcd_init (uint8_t rhport)
{
pico_trace("dcd_init %d\n", rhport);
assert(rhport == 0);
assert(rhport == 0);
// Reset hardware to default state
rp2040_usb_init();
// Reset hardware to default state
rp2040_usb_init();
irq_set_exclusive_handler(USBCTRL_IRQ, dcd_rp2040_irq);
memset(hw_endpoints, 0, sizeof(hw_endpoints));
next_buffer_ptr = &usb_dpram->epx_data[0];
#if FORCE_VBUS_DETECT
// Force VBUS detect so the device thinks it is plugged into a host
usb_hw->pwr = USB_USB_PWR_VBUS_DETECT_BITS | USB_USB_PWR_VBUS_DETECT_OVERRIDE_EN_BITS;
#endif
// EP0 always exists so init it now
// EP0 OUT
hw_endpoint_init(0x0, 64, 0);
// EP0 IN
hw_endpoint_init(0x80, 64, 0);
irq_set_exclusive_handler(USBCTRL_IRQ, dcd_rp2040_irq);
// Initializes the USB peripheral for device mode and enables it.
// Don't need to enable the pull up here. Force VBUS
usb_hw->main_ctrl = USB_MAIN_CTRL_CONTROLLER_EN_BITS;
// Init control endpoints
tu_memclr(hw_endpoints[0], 2*sizeof(hw_endpoint_t));
hw_endpoint_init(0x0, 64, TUSB_XFER_CONTROL);
hw_endpoint_init(0x80, 64, TUSB_XFER_CONTROL);
// Enable individual controller IRQS here. Processor interrupt enable will be used
// for the global interrupt enable...
// TODO Enable SUSPEND & RESUME interrupt
usb_hw->sie_ctrl = USB_SIE_CTRL_EP0_INT_1BUF_BITS;
usb_hw->inte = USB_INTS_BUFF_STATUS_BITS | USB_INTS_BUS_RESET_BITS | USB_INTS_SETUP_REQ_BITS |
USB_INTS_DEV_CONN_DIS_BITS /* | USB_INTS_DEV_SUSPEND_BITS | USB_INTS_DEV_RESUME_FROM_HOST_BITS */ ;
// Init non-control endpoints
reset_non_control_endpoints();
dcd_connect(rhport);
// Initializes the USB peripheral for device mode and enables it.
// Don't need to enable the pull up here. Force VBUS
usb_hw->main_ctrl = USB_MAIN_CTRL_CONTROLLER_EN_BITS;
// Enable individual controller IRQS here. Processor interrupt enable will be used
// for the global interrupt enable...
// Note: Force VBUS detect cause disconnection not detectable
usb_hw->sie_ctrl = USB_SIE_CTRL_EP0_INT_1BUF_BITS;
usb_hw->inte = USB_INTS_BUFF_STATUS_BITS | USB_INTS_BUS_RESET_BITS | USB_INTS_SETUP_REQ_BITS |
USB_INTS_DEV_SUSPEND_BITS | USB_INTS_DEV_RESUME_FROM_HOST_BITS |
(FORCE_VBUS_DETECT ? 0 : USB_INTS_DEV_CONN_DIS_BITS);
dcd_connect(rhport);
}
void dcd_int_enable(uint8_t rhport)
@@ -434,11 +379,11 @@ void dcd_int_disable(uint8_t rhport)
void dcd_set_address (uint8_t rhport, uint8_t dev_addr)
{
pico_trace("dcd_set_address %d %d\n", rhport, dev_addr);
assert(rhport == 0);
assert(rhport == 0);
// Can't set device address in hardware until status xfer has complete
ep0_0len_status();
// Can't set device address in hardware until status xfer has complete
// Send 0len complete response on EP0 IN
hw_endpoint_xfer(0x80, NULL, 0);
}
void dcd_remote_wakeup(uint8_t rhport)
@@ -451,17 +396,15 @@ void dcd_remote_wakeup(uint8_t rhport)
// disconnect by disabling internal pull-up resistor on D+/D-
void dcd_disconnect(uint8_t rhport)
{
pico_info("dcd_disconnect %d\n", rhport);
assert(rhport == 0);
usb_hw_clear->sie_ctrl = USB_SIE_CTRL_PULLUP_EN_BITS;
(void) rhport;
usb_hw_clear->sie_ctrl = USB_SIE_CTRL_PULLUP_EN_BITS;
}
// connect by enabling internal pull-up resistor on D+/D-
void dcd_connect(uint8_t rhport)
{
pico_info("dcd_connect %d\n", rhport);
assert(rhport == 0);
usb_hw_set->sie_ctrl = USB_SIE_CTRL_PULLUP_EN_BITS;
(void) rhport;
usb_hw_set->sie_ctrl = USB_SIE_CTRL_PULLUP_EN_BITS;
}
/*------------------------------------------------------------------*/
@@ -470,62 +413,82 @@ void dcd_connect(uint8_t rhport)
void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request)
{
pico_trace("dcd_edpt0_status_complete %d\n", rhport);
assert(rhport == 0);
(void) rhport;
if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE &&
request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD &&
request->bRequest == TUSB_REQ_SET_ADDRESS)
{
pico_trace("Set HW address %d\n", assigned_address);
usb_hw->dev_addr_ctrl = (uint8_t) request->wValue;
}
reset_ep0();
if ( request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE &&
request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD &&
request->bRequest == TUSB_REQ_SET_ADDRESS )
{
usb_hw->dev_addr_ctrl = (uint8_t) request->wValue;
}
}
bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
{
pico_info("dcd_edpt_open %d %02x\n", rhport, desc_edpt->bEndpointAddress);
assert(rhport == 0);
hw_endpoint_init(desc_edpt->bEndpointAddress, desc_edpt->wMaxPacketSize.size, desc_edpt->bmAttributes.xfer);
return true;
}
void dcd_edpt_close_all (uint8_t rhport)
{
(void) rhport;
// may need to use EP Abort
reset_non_control_endpoints();
}
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
{
assert(rhport == 0);
// True means start new xfer
hw_endpoint_xfer(ep_addr, buffer, total_bytes, true);
hw_endpoint_xfer(ep_addr, buffer, total_bytes);
return true;
}
void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
{
pico_trace("dcd_edpt_stall %d %02x\n", rhport, ep_addr);
assert(rhport == 0);
hw_endpoint_stall(ep_addr);
(void) rhport;
if ( tu_edpt_number(ep_addr) == 0 )
{
// A stall on EP0 has to be armed so it can be cleared on the next setup packet
usb_hw_set->ep_stall_arm = (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) ? USB_EP_STALL_ARM_EP0_IN_BITS : USB_EP_STALL_ARM_EP0_OUT_BITS;
}
struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr);
// stall and clear current pending buffer
// may need to use EP_ABORT
_hw_endpoint_buffer_control_set_value32(ep, USB_BUF_CTRL_STALL);
}
void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
{
pico_trace("dcd_edpt_clear_stall %d %02x\n", rhport, ep_addr);
assert(rhport == 0);
hw_endpoint_clear_stall(ep_addr);
}
(void) rhport;
if (tu_edpt_number(ep_addr))
{
struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr);
// clear stall also reset toggle to DATA0, ready for next transfer
ep->next_pid = 0;
_hw_endpoint_buffer_control_clear_mask32(ep, USB_BUF_CTRL_STALL);
}
}
void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr)
{
// usbd.c says: In progress transfers on this EP may be delivered after this call
pico_trace("dcd_edpt_close %d %02x\n", rhport, ep_addr);
(void) rhport;
(void) ep_addr;
// usbd.c says: In progress transfers on this EP may be delivered after this call
pico_trace("dcd_edpt_close %02x\n", ep_addr);
}
void dcd_int_handler(uint8_t rhport)
{
(void) rhport;
dcd_rp2040_irq();
(void) rhport;
dcd_rp2040_irq();
}
#endif
+138 -113
View File
@@ -2,6 +2,7 @@
* The MIT License (MIT)
*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
* Copyright (c) 2021 Ha Thach (tinyusb.org) for Double Buffered
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -38,7 +39,6 @@
#include "host/hcd.h"
#include "host/usbh.h"
#include "host/usbh_hcd.h"
#define ROOT_PORT 0
@@ -52,46 +52,30 @@
static_assert(PICO_USB_HOST_INTERRUPT_ENDPOINTS <= USB_MAX_ENDPOINTS, "");
// Host mode uses one shared endpoint register for non-interrupt endpoint
struct hw_endpoint eps[1 + PICO_USB_HOST_INTERRUPT_ENDPOINTS];
#define epx (eps[0])
static struct hw_endpoint ep_pool[1 + PICO_USB_HOST_INTERRUPT_ENDPOINTS];
#define epx (ep_pool[0])
#define usb_hw_set hw_set_alias(usb_hw)
#define usb_hw_set hw_set_alias(usb_hw)
#define usb_hw_clear hw_clear_alias(usb_hw)
// Used for hcd pipe busy.
// todo still a bit wasteful
// top bit set if valid
uint8_t dev_ep_map[CFG_TUSB_HOST_DEVICE_MAX][1 + PICO_USB_HOST_INTERRUPT_ENDPOINTS][2];
// Flags we set by default in sie_ctrl (we add other bits on top)
static uint32_t sie_ctrl_base = USB_SIE_CTRL_SOF_EN_BITS |
USB_SIE_CTRL_KEEP_ALIVE_EN_BITS |
USB_SIE_CTRL_PULLDOWN_EN_BITS |
USB_SIE_CTRL_EP0_INT_1BUF_BITS;
enum {
SIE_CTRL_BASE = USB_SIE_CTRL_SOF_EN_BITS | USB_SIE_CTRL_KEEP_ALIVE_EN_BITS |
USB_SIE_CTRL_PULLDOWN_EN_BITS | USB_SIE_CTRL_EP0_INT_1BUF_BITS
};
static struct hw_endpoint *get_dev_ep(uint8_t dev_addr, uint8_t ep_addr)
{
uint8_t num = tu_edpt_number(ep_addr);
if (num == 0) {
return &epx;
}
uint8_t in = (ep_addr & TUSB_DIR_IN_MASK) ? 1 : 0;
uint mapping = dev_ep_map[dev_addr-1][num][in];
pico_trace("Get dev addr %d ep %d = %d\n", dev_addr, ep_addr, mapping);
return mapping >= 128 ? eps + (mapping & 0x7fu) : NULL;
}
uint8_t num = tu_edpt_number(ep_addr);
if ( num == 0 ) return &epx;
static void set_dev_ep(uint8_t dev_addr, uint8_t ep_addr, struct hw_endpoint *ep)
{
uint8_t num = tu_edpt_number(ep_addr);
uint8_t in = (ep_addr & TUSB_DIR_IN_MASK) ? 1 : 0;
uint32_t index = ep - eps;
hard_assert(index < TU_ARRAY_SIZE(eps));
// todo revisit why dev_addr can be 0 here
if (dev_addr) {
dev_ep_map[dev_addr-1][num][in] = 128u | index;
}
pico_trace("Set dev addr %d ep %d = %d\n", dev_addr, ep_addr, index);
for ( uint32_t i = 1; i < TU_ARRAY_SIZE(ep_pool); i++ )
{
struct hw_endpoint *ep = &ep_pool[i];
if ( ep->configured && (ep->dev_addr == dev_addr) && (ep->ep_addr == ep_addr) ) return ep;
}
return NULL;
}
static inline uint8_t dev_speed(void)
@@ -103,7 +87,7 @@ static 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_device_get_speed(dev_addr);
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)
@@ -111,15 +95,15 @@ static void 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 total_len = ep->total_len;
uint xferred_len = ep->xferred_len;
hw_endpoint_reset_transfer(ep);
hcd_event_xfer_complete(dev_addr, ep_addr, total_len, xfer_result, true);
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)
{
usb_hw_clear->buf_status = bit;
bool done = _hw_endpoint_xfer_continue(ep);
bool done = hw_endpoint_xfer_continue(ep);
if (done)
{
hw_xfer_complete(ep, XFER_RESULT_SUCCESS);
@@ -137,6 +121,17 @@ static void hw_handle_buff_status(void)
{
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);
}
@@ -153,7 +148,7 @@ static void hw_handle_buff_status(void)
if (remaining_buffers & bit)
{
remaining_buffers &= ~bit;
_handle_buff_status_bit(bit, &eps[i]);
_handle_buff_status_bit(bit, &ep_pool[i]);
}
}
@@ -165,19 +160,19 @@ static void hw_handle_buff_status(void)
static void hw_trans_complete(void)
{
struct hw_endpoint *ep = &epx;
assert(ep->active);
struct hw_endpoint *ep = &epx;
assert(ep->active);
if (ep->sent_setup)
{
pico_trace("Sent setup packet\n");
hw_xfer_complete(ep, XFER_RESULT_SUCCESS);
}
else
{
// Don't care. Will handle this in buff status
return;
}
if (usb_hw->sie_ctrl & USB_SIE_CTRL_SEND_SETUP_BITS)
{
pico_trace("Sent setup packet\n");
hw_xfer_complete(ep, XFER_RESULT_SUCCESS);
}
else
{
// Don't care. Will handle this in buff status
return;
}
}
static void hcd_rp2040_irq(void)
@@ -202,20 +197,21 @@ static void hcd_rp2040_irq(void)
usb_hw_clear->sie_status = USB_SIE_STATUS_SPEED_BITS;
}
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_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_BUFF_STATUS_BITS)
{
handled |= USB_INTS_BUFF_STATUS_BITS;
// print_bufctrl32(*epx.buffer_control);
hw_handle_buff_status();
}
if (status & USB_INTS_STALL_BITS)
{
// We have rx'd a stall from the device
@@ -234,7 +230,7 @@ static void hcd_rp2040_irq(void)
if (status & USB_INTS_ERROR_DATA_SEQ_BITS)
{
usb_hw_clear->sie_status = USB_SIE_STATUS_DATA_SEQ_ERROR_BITS;
// print_bufctrl32(*epx.buffer_control);
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");
}
@@ -247,9 +243,9 @@ static void hcd_rp2040_irq(void)
static struct hw_endpoint *_next_free_interrupt_ep(void)
{
struct hw_endpoint *ep = NULL;
for (uint i = 1; i < TU_ARRAY_SIZE(eps); i++)
for (uint i = 1; i < TU_ARRAY_SIZE(ep_pool); i++)
{
ep = &eps[i];
ep = &ep_pool[i];
if (!ep->configured)
{
// Will be configured by _hw_endpoint_init / _hw_endpoint_allocate
@@ -263,6 +259,7 @@ static struct hw_endpoint *_next_free_interrupt_ep(void)
static struct hw_endpoint *_hw_endpoint_allocate(uint8_t transfer_type)
{
struct hw_endpoint *ep = NULL;
if (transfer_type == TUSB_XFER_INTERRUPT)
{
ep = _next_free_interrupt_ep();
@@ -270,11 +267,11 @@ static struct hw_endpoint *_hw_endpoint_allocate(uint8_t transfer_type)
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;
// 0x180 for epx
// 0x1c0 for intep0
// 0x200 for intep1
// 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 + 1)];
ep->hw_data_buf = &usbh_dpram->epx_data[64 * (ep->interrupt_num + 2)];
}
else
{
@@ -283,6 +280,7 @@ static struct hw_endpoint *_hw_endpoint_allocate(uint8_t transfer_type)
ep->endpoint_control = &usbh_dpram->epx_ctrl;
ep->hw_data_buf = &usbh_dpram->epx_data[0];
}
return ep;
}
@@ -303,7 +301,7 @@ static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t
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->next_pid = (num == 0 ? 1u : 0u);
ep->wMaxPacketSize = wMaxPacketSize;
ep->transfer_type = transfer_type;
@@ -332,6 +330,7 @@ static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t
// preamble
uint32_t reg = dev_addr | (num << USB_ADDR_ENDP1_ENDPOINT_LSB);
// Assert the interrupt endpoint is IN_TO_HOST
// TODO Interrupt can also be OUT
assert(dir == TUSB_DIR_IN);
if (need_pre(dev_addr))
@@ -345,24 +344,9 @@ static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t
// If it's an interrupt endpoint we need to set up the buffer control
// register
}
}
static void hw_endpoint_init(uint8_t dev_addr, const tusb_desc_endpoint_t *ep_desc)
{
// Allocated differently based on if it's an interrupt endpoint or not
struct hw_endpoint *ep = _hw_endpoint_allocate(ep_desc->bmAttributes.xfer);
_hw_endpoint_init(ep,
dev_addr,
ep_desc->bEndpointAddress,
ep_desc->wMaxPacketSize.size,
ep_desc->bmAttributes.xfer,
ep_desc->bInterval);
// Map this struct to ep@device address
set_dev_ep(dev_addr, ep_desc->bEndpointAddress, ep);
}
//--------------------------------------------------------------------+
// HCD API
//--------------------------------------------------------------------+
@@ -374,14 +358,17 @@ bool hcd_init(uint8_t rhport)
// 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;
irq_set_exclusive_handler(USBCTRL_IRQ, hcd_rp2040_irq);
// clear epx and interrupt eps
memset(&eps, 0, sizeof(eps));
memset(&ep_pool, 0, sizeof(ep_pool));
// 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->sie_ctrl = SIE_CTRL_BASE;
usb_hw->inte = USB_INTE_BUFF_STATUS_BITS |
USB_INTE_HOST_CONN_DIS_BITS |
USB_INTE_HOST_RESUME_BITS |
@@ -409,7 +396,6 @@ bool hcd_port_connect_status(uint8_t rhport)
tusb_speed_t hcd_port_speed_get(uint8_t rhport)
{
pico_trace("hcd_port_speed_get\n");
assert(rhport == 0);
// TODO: Should enumval this register
switch (dev_speed())
@@ -420,15 +406,25 @@ tusb_speed_t hcd_port_speed_get(uint8_t rhport)
return TUSB_SPEED_FULL;
default:
panic("Invalid speed\n");
return TUSB_SPEED_INVALID;
}
}
// Close all opened endpoint belong to this device
void hcd_device_close(uint8_t rhport, uint8_t dev_addr)
{
(void) rhport;
(void) dev_addr;
pico_trace("hcd_device_close %d\n", dev_addr);
}
uint32_t hcd_frame_number(uint8_t rhport)
{
(void) rhport;
return usb_hw->sof_rd;
}
void hcd_int_enable(uint8_t rhport)
{
assert(rhport == 0);
@@ -442,36 +438,70 @@ void hcd_int_disable(uint8_t rhport)
irq_set_enabled(USBCTRL_IRQ, false);
}
//--------------------------------------------------------------------+
// Endpoint API
//--------------------------------------------------------------------+
bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc)
{
(void) rhport;
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);
_hw_endpoint_init(ep,
dev_addr,
ep_desc->bEndpointAddress,
ep_desc->wMaxPacketSize.size,
ep_desc->bmAttributes.xfer,
ep_desc->bInterval);
return true;
}
bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen)
{
pico_info("hcd_edpt_xfer dev_addr %d, ep_addr 0x%x, len %d\n", dev_addr, ep_addr, buflen);
(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);
// Get appropriate ep. Either EPX or interrupt endpoint
struct hw_endpoint *ep = get_dev_ep(dev_addr, ep_addr);
assert(ep);
if (ep_addr != ep->ep_addr)
// Control endpoint can change direction 0x00 <-> 0x80
if ( ep_addr != ep->ep_addr )
{
// Direction has flipped so re init it but with same properties
// TODO treat IN and OUT as invidual endpoints
_hw_endpoint_init(ep, dev_addr, ep_addr, ep->wMaxPacketSize, ep->transfer_type, 0);
}
assert(ep_num == 0);
// True indicates this is the start of the transfer
_hw_endpoint_xfer(ep, buffer, buflen, true);
// 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);
}
// 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);
// That has set up buffer control, endpoint control etc
// for host we have to initiate the transfer
usb_hw->dev_addr_ctrl = dev_addr | (tu_edpt_number(ep_addr) << USB_ADDR_ENDP_ENDPOINT_LSB);
uint32_t flags = USB_SIE_CTRL_START_TRANS_BITS | sie_ctrl_base;
flags |= ep->rx ? USB_SIE_CTRL_RECEIVE_DATA_BITS : USB_SIE_CTRL_SEND_DATA_BITS;
usb_hw->dev_addr_ctrl = 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);
// 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;
usb_hw->sie_ctrl = flags;
}else
{
hw_endpoint_xfer_start(ep, buffer, buflen);
}
return true;
@@ -479,41 +509,33 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8])
{
pico_info("hcd_setup_send dev_addr %d\n", dev_addr);
(void) rhport;
// Copy data into setup packet buffer
memcpy((void*)&usbh_dpram->setup_packet[0], setup_packet, 8);
// Configure EP0 struct with setup info for the trans complete
struct hw_endpoint *ep = _hw_endpoint_allocate(0);
// EP0 out
_hw_endpoint_init(ep, dev_addr, 0x00, ep->wMaxPacketSize, 0, 0);
assert(ep->configured);
ep->total_len = 8;
ep->transfer_size = 8;
ep->active = true;
ep->sent_setup = true;
ep->remaining_len = 8;
ep->active = true;
// Set device address
usb_hw->dev_addr_ctrl = dev_addr;
// Set pre if we are a low speed device on full speed hub
uint32_t flags = sie_ctrl_base | USB_SIE_CTRL_SEND_SETUP_BITS | USB_SIE_CTRL_START_TRANS_BITS;
flags |= need_pre(dev_addr) ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0;
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);
usb_hw->sie_ctrl = flags;
return true;
}
uint32_t hcd_frame_number(uint8_t rhport)
{
return usb_hw->sof_rd;
}
bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc)
{
pico_trace("hcd_edpt_open dev_addr %d, ep_addr %d\n", dev_addr, ep_desc->bEndpointAddress);
hw_endpoint_init(dev_addr, ep_desc);
return true;
}
//bool hcd_edpt_busy(uint8_t dev_addr, uint8_t ep_addr)
//{
@@ -531,6 +553,9 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const
bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr)
{
(void) dev_addr;
(void) ep_addr;
panic("hcd_clear_stall");
return true;
}
+218 -220
View File
@@ -2,6 +2,7 @@
* The MIT License (MIT)
*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
* Copyright (c) 2021 Ha Thach (tinyusb.org) for Double Buffered
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -43,42 +44,33 @@ static inline void _hw_endpoint_lock_update(struct hw_endpoint *ep, int delta) {
// sense to have worker and IRQ on same core, however I think using critsec is about equivalent.
}
#if TUSB_OPT_HOST_ENABLED
static inline void _hw_endpoint_update_last_buf(struct hw_endpoint *ep)
{
ep->last_buf = (ep->len + ep->transfer_size == ep->total_len);
}
#endif
static void _hw_endpoint_xfer_sync(struct hw_endpoint *ep);
static void _hw_endpoint_start_next_buffer(struct hw_endpoint *ep);
//--------------------------------------------------------------------+
//
//--------------------------------------------------------------------+
void rp2040_usb_init(void)
{
// Reset usb controller
reset_block(RESETS_RESET_USBCTRL_BITS);
unreset_block_wait(RESETS_RESET_USBCTRL_BITS);
// Reset usb controller
reset_block(RESETS_RESET_USBCTRL_BITS);
unreset_block_wait(RESETS_RESET_USBCTRL_BITS);
// Clear any previous state just in case
memset(usb_hw, 0, sizeof(*usb_hw));
memset(usb_dpram, 0, sizeof(*usb_dpram));
// Clear any previous state just in case
memset(usb_hw, 0, sizeof(*usb_hw));
memset(usb_dpram, 0, sizeof(*usb_dpram));
// Mux the controller to the onboard usb phy
usb_hw->muxing = USB_USB_MUXING_TO_PHY_BITS | USB_USB_MUXING_SOFTCON_BITS;
// Force VBUS detect so the device thinks it is plugged into a host
// TODO support VBUs detect
usb_hw->pwr = USB_USB_PWR_VBUS_DETECT_BITS | USB_USB_PWR_VBUS_DETECT_OVERRIDE_EN_BITS;
// Mux the controller to the onboard usb phy
usb_hw->muxing = USB_USB_MUXING_TO_PHY_BITS | USB_USB_MUXING_SOFTCON_BITS;
}
void hw_endpoint_reset_transfer(struct hw_endpoint *ep)
{
ep->stalled = false;
ep->active = false;
#if TUSB_OPT_HOST_ENABLED
ep->sent_setup = false;
#endif
ep->total_len = 0;
ep->len = 0;
ep->transfer_size = 0;
ep->user_buf = 0;
ep->active = false;
ep->remaining_len = 0;
ep->xferred_len = 0;
ep->user_buf = 0;
}
void _hw_endpoint_buffer_control_update32(struct hw_endpoint *ep, uint32_t and_mask, uint32_t or_mask) {
@@ -111,215 +103,221 @@ void _hw_endpoint_buffer_control_update32(struct hw_endpoint *ep, uint32_t and_m
*ep->buffer_control = value;
}
void _hw_endpoint_start_next_buffer(struct hw_endpoint *ep)
// prepare buffer, return buffer control
static uint32_t prepare_ep_buffer(struct hw_endpoint *ep, uint8_t buf_id)
{
// Prepare buffer control register value
uint32_t val = ep->transfer_size | USB_BUF_CTRL_AVAIL;
uint16_t const buflen = tu_min16(ep->remaining_len, ep->wMaxPacketSize);
ep->remaining_len -= buflen;
if (!ep->rx)
uint32_t buf_ctrl = buflen | USB_BUF_CTRL_AVAIL;
// PID
buf_ctrl |= ep->next_pid ? USB_BUF_CTRL_DATA1_PID : USB_BUF_CTRL_DATA0_PID;
ep->next_pid ^= 1u;
if ( !ep->rx )
{
// Copy data from user buffer to hw buffer
memcpy(ep->hw_data_buf + buf_id*64, ep->user_buf, buflen);
ep->user_buf += buflen;
// Mark as full
buf_ctrl |= USB_BUF_CTRL_FULL;
}
// Is this the last buffer? Only really matters for host mode. Will trigger
// the trans complete irq but also stop it polling. We only really care about
// trans complete for setup packets being sent
if (ep->remaining_len == 0)
{
buf_ctrl |= USB_BUF_CTRL_LAST;
}
if (buf_id) buf_ctrl = buf_ctrl << 16;
return buf_ctrl;
}
// Prepare buffer control register value
static void _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
// 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);
if(ep->remaining_len && !force_single)
{
// Use buffer 1 (double buffered) if there is still data
// TODO: Isochronous for buffer1 bit-field is different than CBI (control bulk, interrupt)
buf_ctrl |= prepare_ep_buffer(ep, 1);
// Set endpoint control double buffered bit if needed
ep_ctrl &= ~EP_CTRL_INTERRUPT_PER_BUFFER;
ep_ctrl |= EP_CTRL_DOUBLE_BUFFERED_BITS | EP_CTRL_INTERRUPT_PER_DOUBLE_BUFFER;
}else
{
// Single buffered since 1 is enough
ep_ctrl &= ~(EP_CTRL_DOUBLE_BUFFERED_BITS | EP_CTRL_INTERRUPT_PER_DOUBLE_BUFFER);
ep_ctrl |= EP_CTRL_INTERRUPT_PER_BUFFER;
}
*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));
// Finally, write to buffer_control which will trigger the transfer
// the next time the controller polls this dpram address
_hw_endpoint_buffer_control_set_value32(ep, buf_ctrl);
}
void hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, uint16_t total_len)
{
_hw_endpoint_lock_update(ep, 1);
if ( ep->active )
{
// TODO: Is this acceptable for interrupt packets?
TU_LOG(1, "WARN: starting new transfer on already active ep %d %s\n", tu_edpt_number(ep->ep_addr),
ep_dir_string[tu_edpt_dir(ep->ep_addr)]);
hw_endpoint_reset_transfer(ep);
}
// Fill in info now that we're kicking off the hw
ep->remaining_len = total_len;
ep->xferred_len = 0;
ep->active = true;
ep->user_buf = buffer;
_hw_endpoint_start_next_buffer(ep);
_hw_endpoint_lock_update(ep, -1);
}
// sync endpoint buffer and return transferred bytes
static uint16_t 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;
uint16_t xferred_bytes = buf_ctrl & USB_BUF_CTRL_LEN_MASK;
if ( !ep->rx )
{
// We are continuing a transfer here. If we are TX, we have successfully
// sent some data can increase the length we have sent
assert(!(buf_ctrl & USB_BUF_CTRL_FULL));
ep->xferred_len += xferred_bytes;
}else
{
// If we have received some data, so can increase the length
// we have received AFTER we have copied it to the user buffer at the appropriate offset
assert(buf_ctrl & USB_BUF_CTRL_FULL);
memcpy(ep->user_buf, ep->hw_data_buf + buf_id*64, xferred_bytes);
ep->xferred_len += xferred_bytes;
ep->user_buf += xferred_bytes;
}
// Short packet
if (xferred_bytes < ep->wMaxPacketSize)
{
pico_trace(" Short packet on buffer %d with %u bytes\n", buf_id, xferred_bytes);
// Reduce total length as this is last packet
ep->remaining_len = 0;
}
return xferred_bytes;
}
static void _hw_endpoint_xfer_sync (struct hw_endpoint *ep)
{
// Update hw endpoint struct with info from hardware
// after a buff status interrupt
uint32_t 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));
// always sync buffer 0
uint16_t buf0_bytes = sync_ep_buffer(ep, 0);
// sync buffer 1 if double buffered
if ( (*ep->endpoint_control) & EP_CTRL_DOUBLE_BUFFERED_BITS )
{
if (buf0_bytes == ep->wMaxPacketSize)
{
// Copy data from user buffer to hw buffer
memcpy(ep->hw_data_buf, &ep->user_buf[ep->len], ep->transfer_size);
// Mark as full
val |= USB_BUF_CTRL_FULL;
}
// PID
val |= ep->next_pid ? USB_BUF_CTRL_DATA1_PID : USB_BUF_CTRL_DATA0_PID;
#if TUSB_OPT_DEVICE_ENABLED
ep->next_pid ^= 1u;
#else
// For Host (also device but since we dictate the endpoint size, following scenario does not occur)
// Next PID depends on the number of packet in case wMaxPacketSize < 64 (e.g Interrupt Endpoint 8, or 12)
// Special case with control status stage where PID is always DATA1
if ( ep->transfer_size == 0 )
{
// ZLP also toggle data
ep->next_pid ^= 1u;
// sync buffer 1 if not short packet
sync_ep_buffer(ep, 1);
}else
{
uint32_t packet_count = 1 + ((ep->transfer_size - 1) / ep->wMaxPacketSize);
// short packet on buffer 0
// TODO couldn't figure out how to handle this case which happen with net_lwip_webserver example
// At this time (currently trigger per 2 buffer), the buffer1 is probably filled with data from
// the next transfer (not current one). For now we disable double buffered for device OUT
// NOTE this could happen to Host IN
#if 0
uint8_t const ep_num = tu_edpt_number(ep->ep_addr);
uint8_t const dir = (uint8_t) tu_edpt_dir(ep->ep_addr);
uint8_t const ep_id = 2*ep_num + (dir ? 0 : 1);
if ( packet_count & 0x01 )
{
ep->next_pid ^= 1u;
}
}
// abort queued transfer on buffer 1
usb_hw->abort |= TU_BIT(ep_id);
while ( !(usb_hw->abort_done & TU_BIT(ep_id)) ) {}
uint32_t ep_ctrl = *ep->endpoint_control;
ep_ctrl &= ~(EP_CTRL_DOUBLE_BUFFERED_BITS | EP_CTRL_INTERRUPT_PER_DOUBLE_BUFFER);
ep_ctrl |= EP_CTRL_INTERRUPT_PER_BUFFER;
_hw_endpoint_buffer_control_set_value32(ep, 0);
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));
#endif
#if TUSB_OPT_HOST_ENABLED
// Is this the last buffer? Only really matters for host mode. Will trigger
// the trans complete irq but also stop it polling. We only really care about
// trans complete for setup packets being sent
if (ep->last_buf)
{
pico_trace("Last buf (%d bytes left)\n", ep->transfer_size);
val |= USB_BUF_CTRL_LAST;
}
#endif
// Finally, write to buffer_control which will trigger the transfer
// the next time the controller polls this dpram address
_hw_endpoint_buffer_control_set_value32(ep, val);
pico_trace("buffer control (0x%p) <- 0x%x\n", ep->buffer_control, val);
//print_bufctrl16(val);
}
void _hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, uint16_t total_len)
{
_hw_endpoint_lock_update(ep, 1);
pico_trace("Start transfer of total len %d on ep %d %s\n", total_len, tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]);
if (ep->active)
{
// TODO: Is this acceptable for interrupt packets?
pico_warn("WARN: starting new transfer on already active ep %d %s\n", tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]);
hw_endpoint_reset_transfer(ep);
}
// Fill in info now that we're kicking off the hw
ep->total_len = total_len;
ep->len = 0;
// Limit by packet size but not less 64 (i.e low speed 8 bytes EP0)
ep->transfer_size = tu_min16(total_len, tu_max16(64, ep->wMaxPacketSize));
ep->active = true;
ep->user_buf = buffer;
#if TUSB_OPT_HOST_ENABLED
// Recalculate if this is the last buffer
_hw_endpoint_update_last_buf(ep);
ep->buf_sel = 0;
#endif
_hw_endpoint_start_next_buffer(ep);
_hw_endpoint_lock_update(ep, -1);
}
void _hw_endpoint_xfer_sync(struct hw_endpoint *ep)
{
// Update hw endpoint struct with info from hardware
// after a buff status interrupt
uint32_t buf_ctrl = _hw_endpoint_buffer_control_get_value32(ep);
#if TUSB_OPT_HOST_ENABLED
// RP2040-E4
// tag::host_buf_sel_fix[]
// TODO need changes to support double buffering
if (ep->buf_sel == 1)
{
// Host can erroneously write status to top half of buf_ctrl register
buf_ctrl = buf_ctrl >> 16;
// update buf1 -> buf0 to prevent panic with "already available"
*ep->buffer_control = buf_ctrl;
}
// Flip buf sel for host
ep->buf_sel ^= 1u;
// end::host_buf_sel_fix[]
#endif
// Get tranferred bytes after adjusted buf sel
uint16_t const transferred_bytes = buf_ctrl & USB_BUF_CTRL_LEN_MASK;
// We are continuing a transfer here. If we are TX, we have successfullly
// sent some data can increase the length we have sent
if (!ep->rx)
{
assert(!(buf_ctrl & USB_BUF_CTRL_FULL));
pico_trace("tx %d bytes (buf_ctrl 0x%08x)\n", transferred_bytes, buf_ctrl);
ep->len += transferred_bytes;
}
else
{
// If we are OUT we have recieved some data, so can increase the length
// we have recieved AFTER we have copied it to the user buffer at the appropriate
// offset
pico_trace("rx %d bytes (buf_ctrl 0x%08x)\n", transferred_bytes, buf_ctrl);
assert(buf_ctrl & USB_BUF_CTRL_FULL);
memcpy(&ep->user_buf[ep->len], ep->hw_data_buf, transferred_bytes);
ep->len += transferred_bytes;
}
// Sometimes the host will send less data than we expect...
// If this is a short out transfer update the total length of the transfer
// to be the current length
if ((ep->rx) && (transferred_bytes < ep->wMaxPacketSize))
{
pico_trace("Short rx transfer\n");
// Reduce total length as this is last packet
ep->total_len = ep->len;
}
}
}
// Returns true if transfer is complete
bool _hw_endpoint_xfer_continue(struct hw_endpoint *ep)
bool hw_endpoint_xfer_continue(struct hw_endpoint *ep)
{
_hw_endpoint_lock_update(ep, 1);
// Part way through a transfer
if (!ep->active)
{
panic("Can't continue xfer on inactive ep %d %s", tu_edpt_number(ep->ep_addr), ep_dir_string);
}
_hw_endpoint_lock_update(ep, 1);
// Part way through a transfer
if (!ep->active)
{
panic("Can't continue xfer on inactive ep %d %s", tu_edpt_number(ep->ep_addr), ep_dir_string);
}
// Update EP struct from hardware state
_hw_endpoint_xfer_sync(ep);
// Now we have synced our state with the hardware. Is there more data to transfer?
// Limit by packet size but not less 64 (i.e low speed 8 bytes EP0)
uint16_t remaining_bytes = ep->total_len - ep->len;
ep->transfer_size = tu_min16(remaining_bytes, tu_max16(64, ep->wMaxPacketSize));
#if TUSB_OPT_HOST_ENABLED
_hw_endpoint_update_last_buf(ep);
#endif
// Can happen because of programmer error so check for it
if (ep->len > ep->total_len)
{
panic("Transferred more data than expected");
}
// If we are done then notify tinyusb
if (ep->len == ep->total_len)
{
pico_trace("Completed transfer of %d bytes on ep %d %s\n",
ep->len, tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]);
// Notify caller we are done so it can notify the tinyusb stack
_hw_endpoint_lock_update(ep, -1);
return true;
}
else
{
_hw_endpoint_start_next_buffer(ep);
}
// Update EP struct from hardware state
_hw_endpoint_xfer_sync(ep);
// Now we have synced our state with the hardware. Is there more data to transfer?
// If we are done then notify tinyusb
if (ep->remaining_len == 0)
{
pico_trace("Completed transfer of %d bytes on ep %d %s\n",
ep->xferred_len, tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]);
// Notify caller we are done so it can notify the tinyusb stack
_hw_endpoint_lock_update(ep, -1);
// More work to do
return false;
}
return true;
}
else
{
_hw_endpoint_start_next_buffer(ep);
}
void _hw_endpoint_xfer(struct hw_endpoint *ep, uint8_t *buffer, uint16_t total_len, bool start)
{
// Trace
pico_trace("hw_endpoint_xfer ep %d %s", tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]);
pico_trace(" total_len %d, start=%d\n", total_len, start);
assert(ep->configured);
if (start)
{
_hw_endpoint_xfer_start(ep, buffer, total_len);
}
else
{
_hw_endpoint_xfer_continue(ep);
}
_hw_endpoint_lock_update(ep, -1);
// More work to do
return false;
}
#endif
+13 -77
View File
@@ -17,26 +17,11 @@
#endif
#if false && !defined(NDEBUG)
#define pico_trace(format,args...) printf(format, ## args)
#else
#define pico_trace(format,...) ((void)0)
#endif
#if false && !defined(NDEBUG)
#define pico_info(format,args...) printf(format, ## args)
#else
#define pico_info(format,...) ((void)0)
#endif
#if false && !defined(NDEBUG)
#define pico_warn(format,args...) printf(format, ## args)
#else
#define pico_warn(format,...) ((void)0)
#endif
#define pico_info(...) TU_LOG(2, __VA_ARGS__)
#define pico_trace(...) TU_LOG(3, __VA_ARGS__)
// Hardware information per endpoint
struct hw_endpoint
typedef struct hw_endpoint
{
// Is this a valid struct
bool configured;
@@ -50,51 +35,42 @@ struct hw_endpoint
// Endpoint control register
io_rw_32 *endpoint_control;
// Buffer control register
io_rw_32 *buffer_control;
// Buffer pointer in usb dpram
uint8_t *hw_data_buf;
// Have we been stalled
bool stalled;
// Current transfer information
bool active;
uint16_t total_len;
uint16_t len;
// Amount of data with the hardware
uint16_t transfer_size;
uint16_t remaining_len;
uint16_t xferred_len;
// User buffer in main memory
uint8_t *user_buf;
// Data needed from EP descriptor
uint16_t wMaxPacketSize;
// Interrupt, bulk, etc
uint8_t transfer_type;
#if TUSB_OPT_HOST_ENABLED
// Only needed for host mode
bool last_buf;
// RP2040-E4: HOST BUG. Host will incorrect write status to top half of buffer
// control register when doing transfers > 1 packet
uint8_t buf_sel;
// Only needed for host
uint8_t dev_addr;
bool sent_setup;
// If interrupt endpoint
uint8_t interrupt_num;
#endif
};
} hw_endpoint_t;
void rp2040_usb_init(void);
void hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, uint16_t total_len);
bool hw_endpoint_xfer_continue(struct hw_endpoint *ep);
void hw_endpoint_reset_transfer(struct hw_endpoint *ep);
void _hw_endpoint_xfer(struct hw_endpoint *ep, uint8_t *buffer, uint16_t total_len, bool start);
void _hw_endpoint_start_next_buffer(struct hw_endpoint *ep);
void _hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, uint16_t total_len);
void _hw_endpoint_xfer_sync(struct hw_endpoint *ep);
bool _hw_endpoint_xfer_continue(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;
@@ -117,44 +93,4 @@ static inline uintptr_t hw_data_offset(uint8_t *buf)
extern const char *ep_dir_string[];
typedef union TU_ATTR_PACKED
{
uint16_t u16;
struct TU_ATTR_PACKED
{
uint16_t xfer_len : 10;
uint16_t available : 1;
uint16_t stall : 1;
uint16_t reset_bufsel : 1;
uint16_t data_toggle : 1;
uint16_t last_buf : 1;
uint16_t full : 1;
};
} rp2040_buffer_control_t;
TU_VERIFY_STATIC(sizeof(rp2040_buffer_control_t) == 2, "size is not correct");
static inline void print_bufctrl16(uint32_t __unused u16)
{
rp2040_buffer_control_t __unused bufctrl = {
.u16 = u16
};
TU_LOG(2, "len = %u, available = %u, stall = %u, reset = %u, toggle = %u, last = %u, full = %u\r\n",
bufctrl.xfer_len, bufctrl.available, bufctrl.stall, bufctrl.reset_bufsel, bufctrl.data_toggle, bufctrl.last_buf, bufctrl.full);
}
static inline void print_bufctrl32(uint32_t u32)
{
uint16_t u16;
u16 = u32 >> 16;
TU_LOG(2, "Buffer Control 1 0x%x: ", u16);
print_bufctrl16(u16);
u16 = u32 & 0x0000ffff;
TU_LOG(2, "Buffer Control 0 0x%x: ", u16);
print_bufctrl16(u16);
}
#endif
+334 -167
View File
@@ -2,6 +2,7 @@
* The MIT License (MIT)
*
* Copyright (c) 2020 Koji Kitayama
* Portions copyrighted (c) 2021 Roland Winistoerfer
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -26,8 +27,13 @@
#include "tusb_option.h"
#if TUSB_OPT_DEVICE_ENABLED && ( CFG_TUSB_MCU == OPT_MCU_RX63X )
// Since TinyUSB doesn't use SOF for now, and this interrupt too often (1ms interval)
// We disable SOF for now until needed later on
#define USE_SOF 0
#if TUSB_OPT_DEVICE_ENABLED && ( CFG_TUSB_MCU == OPT_MCU_RX63X || \
CFG_TUSB_MCU == OPT_MCU_RX65X || \
CFG_TUSB_MCU == OPT_MCU_RX72N )
#include "device/dcd.h"
#include "iodefine.h"
@@ -38,6 +44,7 @@
#define SYSTEM_PRCR_PRKEY (0xA5u<<8)
#define USB_FIFOSEL_TX ((uint16_t)(1u<<5))
#define USB_FIFOSEL_BIGEND ((uint16_t)(1u<<8))
#define USB_FIFOSEL_MBW_8 ((uint16_t)(0u<<10))
#define USB_FIFOSEL_MBW_16 ((uint16_t)(1u<<10))
#define USB_IS0_CTSQ ((uint16_t)(7u))
@@ -63,7 +70,10 @@
#define USB_IS0_CTSQ_SETUP (1u)
#define USB_IS0_DVSQ_DEF (1u<<4)
#define USB_IS0_DVSQ_ADDR (2u<<4)
#define USB_IS0_DVSQ_SUSP (4u<<4)
#define USB_IS0_DVSQ_SUSP0 (4u<<4)
#define USB_IS0_DVSQ_SUSP1 (5u<<4)
#define USB_IS0_DVSQ_SUSP2 (6u<<4)
#define USB_IS0_DVSQ_SUSP3 (7u<<4)
#define USB_PIPECTR_PID_NAK (0u)
#define USB_PIPECTR_PID_BUF (1u)
@@ -89,6 +99,10 @@
#define FIFO_REQ_CLR (1u)
#define FIFO_COMPLETE (1u<<1)
// Start of definition of packed structs (used by the CCRX toolchain)
TU_ATTR_PACKED_BEGIN
TU_ATTR_BIT_FIELD_ORDER_BEGIN
typedef struct {
union {
struct {
@@ -112,37 +126,50 @@ typedef union {
typedef struct TU_ATTR_PACKED
{
uintptr_t addr; /* the start address of a transfer data buffer */
void *buf; /* the start address of a transfer data buffer */
uint16_t length; /* the number of bytes in the buffer */
uint16_t remaining; /* the number of bytes remaining in the buffer */
struct {
uint32_t ep : 8; /* an assigned endpoint address */
uint32_t ff : 1; /* `buf` is TU_FUFO or POD */
uint32_t : 0;
};
} pipe_state_t;
TU_ATTR_PACKED_END // End of definition of packed structs (used by the CCRX toolchain)
TU_ATTR_BIT_FIELD_ORDER_END
typedef struct
{
pipe_state_t pipe[9];
pipe_state_t pipe[10];
uint8_t ep[2][16]; /* a lookup table for a pipe index from an endpoint address */
} dcd_data_t;
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
CFG_TUSB_MEM_SECTION static dcd_data_t _dcd;
static dcd_data_t _dcd;
static uint32_t disable_interrupt(void)
{
uint32_t pswi;
#if defined(__CCRX__)
pswi = get_psw() & 0x010000;
clrpsw_i();
#else
pswi = __builtin_rx_mvfc(0) & 0x010000;
__builtin_rx_clrpsw('I');
#endif
return pswi;
}
static void enable_interrupt(uint32_t pswi)
{
#if defined(__CCRX__)
set_psw(get_psw() | pswi);
#else
__builtin_rx_mvtc(0, __builtin_rx_mvfc(0) | pswi);
#endif
}
static unsigned find_pipe(unsigned xfer)
@@ -213,40 +240,27 @@ static volatile uint16_t* ep_addr_to_pipectr(uint8_t rhport, unsigned ep_addr)
return ctr;
}
static unsigned wait_for_pipe_ready(void)
static unsigned edpt0_max_packet_size(void)
{
unsigned ctr;
do {
ctr = USB0.D0FIFOCTR.WORD;
} while (!(ctr & USB_FIFOCTR_FRDY));
return ctr;
return USB0.DCPMAXP.BIT.MXPS;
}
static unsigned select_pipe(unsigned num, unsigned attr)
static unsigned edpt_max_packet_size(unsigned num)
{
USB0.PIPESEL.WORD = num;
USB0.D0FIFOSEL.WORD = num | attr;
while (!(USB0.D0FIFOSEL.BIT.CURPIPE != num)) ;
return wait_for_pipe_ready();
USB0.PIPESEL.WORD = num;
return USB0.PIPEMAXP.WORD;
}
/* 1 less than mps bytes were written to FIFO
* 2 no bytes were written to FIFO
* 0 mps bytes were written to FIFO */
static int fifo_write(volatile void *fifo, pipe_state_t* pipe, unsigned mps)
static inline void pipe_wait_for_ready(unsigned num)
{
unsigned rem = pipe->remaining;
if (!rem) return 2;
unsigned len = TU_MIN(rem, mps);
while (USB0.D0FIFOSEL.BIT.CURPIPE != num) ;
while (!USB0.D0FIFOCTR.BIT.FRDY) ;
}
static void pipe_write_packet(void *buf, volatile void *fifo, unsigned len)
{
hw_fifo_t *reg = (hw_fifo_t*)fifo;
uintptr_t addr = pipe->addr + pipe->length - rem;
if (addr & 1u) {
/* addr is not 2-byte aligned */
reg->u8 = *(const uint8_t *)addr;
++addr;
--len;
}
uintptr_t addr = (uintptr_t)buf;
while (len >= 2) {
reg->u16 = *(const uint16_t *)addr;
addr += 2;
@@ -256,31 +270,147 @@ static int fifo_write(volatile void *fifo, pipe_state_t* pipe, unsigned mps)
reg->u8 = *(const uint8_t *)addr;
++addr;
}
if (rem < mps) return 1;
return 0;
}
/* 1 less than mps bytes were read from FIFO
* 2 the end of the buffer reached.
* 0 mps bytes were read from FIFO */
static int fifo_read(volatile void *fifo, pipe_state_t* pipe, unsigned mps, size_t len)
static void pipe_read_packet(void *buf, volatile void *fifo, unsigned len)
{
unsigned rem = pipe->remaining;
if (!rem) return 2;
if (rem < len) len = rem;
pipe->remaining = rem - len;
uint8_t *p = (uint8_t*)buf;
uint8_t *reg = (uint8_t*)fifo; /* byte access is always at base register address */
while (len--) *p++ = *reg;
}
hw_fifo_t *reg = (hw_fifo_t*)fifo;
uintptr_t addr = pipe->addr;
unsigned loop = len;
while (loop--) {
*(uint8_t *)addr = reg->u8;
++addr;
static void pipe_read_write_packet_ff(tu_fifo_t *f, volatile void *fifo, unsigned len, unsigned dir)
{
static const struct {
void (*tu_fifo_get_info)(tu_fifo_t *f, tu_fifo_buffer_info_t *info);
void (*tu_fifo_advance)(tu_fifo_t *f, uint16_t n);
void (*pipe_read_write)(void *buf, volatile void *fifo, unsigned len);
} ops[] = {
/* OUT */ {tu_fifo_get_write_info,tu_fifo_advance_write_pointer,pipe_read_packet},
/* IN */ {tu_fifo_get_read_info, tu_fifo_advance_read_pointer, pipe_write_packet},
};
tu_fifo_buffer_info_t info;
ops[dir].tu_fifo_get_info(f, &info);
unsigned total_len = len;
len = TU_MIN(total_len, info.len_lin);
ops[dir].pipe_read_write(info.ptr_lin, fifo, len);
unsigned rem = total_len - len;
if (rem) {
len = TU_MIN(rem, info.len_wrap);
ops[dir].pipe_read_write(info.ptr_wrap, fifo, len);
rem -= len;
}
pipe->addr = addr;
if (rem < mps) return 1;
if (rem == len) return 2;
return 0;
ops[dir].tu_fifo_advance(f, total_len - rem);
}
static bool pipe0_xfer_in(void)
{
pipe_state_t *pipe = &_dcd.pipe[0];
const unsigned rem = pipe->remaining;
if (!rem) {
pipe->buf = NULL;
return true;
}
const unsigned mps = edpt0_max_packet_size();
const unsigned len = TU_MIN(mps, rem);
void *buf = pipe->buf;
if (len) {
if (pipe->ff) {
pipe_read_write_packet_ff((tu_fifo_t*)buf, (volatile void*)&USB0.CFIFO.WORD, len, TUSB_DIR_IN);
} else {
pipe_write_packet(buf, (volatile void*)&USB0.CFIFO.WORD, len);
pipe->buf = (uint8_t*)buf + len;
}
}
if (len < mps) USB0.CFIFOCTR.WORD = USB_FIFOCTR_BVAL;
pipe->remaining = rem - len;
return false;
}
static bool pipe0_xfer_out(void)
{
pipe_state_t *pipe = &_dcd.pipe[0];
const unsigned rem = pipe->remaining;
const unsigned mps = edpt0_max_packet_size();
const unsigned vld = USB0.CFIFOCTR.BIT.DTLN;
const unsigned len = TU_MIN(TU_MIN(rem, mps), vld);
void *buf = pipe->buf;
if (len) {
if (pipe->ff) {
pipe_read_write_packet_ff((tu_fifo_t*)buf, (volatile void*)&USB0.CFIFO.WORD, len, TUSB_DIR_OUT);
} else {
pipe_read_packet(buf, (volatile void*)&USB0.CFIFO.WORD, len);
pipe->buf = (uint8_t*)buf + len;
}
}
if (len < mps) USB0.CFIFOCTR.WORD = USB_FIFOCTR_BCLR;
pipe->remaining = rem - len;
if ((len < mps) || (rem == len)) {
pipe->buf = NULL;
return true;
}
return false;
}
static bool pipe_xfer_in(unsigned num)
{
pipe_state_t *pipe = &_dcd.pipe[num];
const unsigned rem = pipe->remaining;
if (!rem) {
pipe->buf = NULL;
return true;
}
USB0.D0FIFOSEL.WORD = num | USB_FIFOSEL_MBW_16 | (TU_BYTE_ORDER == TU_BIG_ENDIAN ? USB_FIFOSEL_BIGEND : 0);
const unsigned mps = edpt_max_packet_size(num);
pipe_wait_for_ready(num);
const unsigned len = TU_MIN(rem, mps);
void *buf = pipe->buf;
if (len) {
if (pipe->ff) {
pipe_read_write_packet_ff((tu_fifo_t*)buf, (volatile void*)&USB0.D0FIFO.WORD, len, TUSB_DIR_IN);
} else {
pipe_write_packet(buf, (volatile void*)&USB0.D0FIFO.WORD, len);
pipe->buf = (uint8_t*)buf + len;
}
}
if (len < mps) USB0.D0FIFOCTR.WORD = USB_FIFOCTR_BVAL;
USB0.D0FIFOSEL.WORD = 0;
while (USB0.D0FIFOSEL.BIT.CURPIPE) ; /* if CURPIPE bits changes, check written value */
pipe->remaining = rem - len;
return false;
}
static bool pipe_xfer_out(unsigned num)
{
pipe_state_t *pipe = &_dcd.pipe[num];
const unsigned rem = pipe->remaining;
USB0.D0FIFOSEL.WORD = num | USB_FIFOSEL_MBW_8;
const unsigned mps = edpt_max_packet_size(num);
pipe_wait_for_ready(num);
const unsigned vld = USB0.D0FIFOCTR.BIT.DTLN;
const unsigned len = TU_MIN(TU_MIN(rem, mps), vld);
void *buf = pipe->buf;
if (len) {
if (pipe->ff) {
pipe_read_write_packet_ff((tu_fifo_t*)buf, (volatile void*)&USB0.D0FIFO.WORD, len, TUSB_DIR_OUT);
} else {
pipe_read_packet(buf, (volatile void*)&USB0.D0FIFO.WORD, len);
pipe->buf = (uint8_t*)buf + len;
}
}
if (len < mps) USB0.D0FIFOCTR.WORD = USB_FIFOCTR_BCLR;
USB0.D0FIFOSEL.WORD = 0;
while (USB0.D0FIFOSEL.BIT.CURPIPE) ; /* if CURPIPE bits changes, check written value */
pipe->remaining = rem - len;
if ((len < mps) || (rem == len)) {
pipe->buf = NULL;
return NULL != buf;
}
return false;
}
static void process_setup_packet(uint8_t rhport)
@@ -288,7 +418,7 @@ static void process_setup_packet(uint8_t rhport)
uint16_t setup_packet[4];
if (0 == (USB0.INTSTS0.WORD & USB_IS0_VALID)) return;
USB0.CFIFOCTR.WORD = USB_FIFOCTR_BCLR;
setup_packet[0] = USB0.USBREQ.WORD;
setup_packet[0] = tu_le16toh(USB0.USBREQ.WORD);
setup_packet[1] = USB0.USBVAL;
setup_packet[2] = USB0.USBINDX;
setup_packet[3] = USB0.USBLENG;
@@ -310,96 +440,63 @@ static void process_status_completion(uint8_t rhport)
dcd_event_xfer_complete(rhport, ep_addr, 0, XFER_RESULT_SUCCESS, true);
}
static bool process_edpt0_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes)
static bool process_pipe0_xfer(int buffer_type, uint8_t ep_addr, void* buffer, uint16_t total_bytes)
{
(void)rhport;
pipe_state_t *pipe = &_dcd.pipe[0];
/* configure fifo direction and access unit settings */
if (ep_addr) { /* IN, 2 bytes */
USB0.CFIFOSEL.WORD = USB_FIFOSEL_TX | USB_FIFOSEL_MBW_16;
USB0.CFIFOSEL.WORD = USB_FIFOSEL_TX | USB_FIFOSEL_MBW_16 | (TU_BYTE_ORDER == TU_BIG_ENDIAN ? USB_FIFOSEL_BIGEND : 0);
while (!(USB0.CFIFOSEL.WORD & USB_FIFOSEL_TX)) ;
} else { /* OUT, a byte */
USB0.CFIFOSEL.WORD = USB_FIFOSEL_MBW_8;
while (USB0.CFIFOSEL.WORD & USB_FIFOSEL_TX) ;
}
pipe_state_t *pipe = &_dcd.pipe[0];
pipe->ff = buffer_type;
pipe->length = total_bytes;
pipe->remaining = total_bytes;
if (total_bytes) {
pipe->addr = (uintptr_t)buffer;
pipe->length = total_bytes;
pipe->remaining = total_bytes;
pipe->buf = buffer;
if (ep_addr) { /* IN */
TU_ASSERT(USB0.DCPCTR.BIT.BSTS && (USB0.USBREQ.WORD & 0x80));
if (fifo_write(&USB0.CFIFO.WORD, pipe, 64)) {
USB0.CFIFOCTR.WORD = USB_FIFOCTR_BVAL;
}
pipe0_xfer_in();
}
USB0.DCPCTR.WORD = USB_PIPECTR_PID_BUF;
} else {
/* ZLP */
pipe->addr = 0;
pipe->length = 0;
pipe->remaining = 0;
pipe->buf = NULL;
USB0.DCPCTR.WORD = USB_PIPECTR_CCPL | USB_PIPECTR_PID_BUF;
}
return true;
}
static void process_edpt0_bemp(uint8_t rhport)
static bool process_pipe_xfer(int buffer_type, uint8_t ep_addr, void* buffer, uint16_t total_bytes)
{
pipe_state_t *pipe = &_dcd.pipe[0];
const unsigned rem = pipe->remaining;
if (rem > 64) {
pipe->remaining = rem - 64;
int r = fifo_write(&USB0.CFIFO.WORD, &_dcd.pipe[0], 64);
if (r) USB0.CFIFOCTR.WORD = USB_FIFOCTR_BVAL;
return;
}
pipe->addr = 0;
pipe->remaining = 0;
dcd_event_xfer_complete(rhport, tu_edpt_addr(0, TUSB_DIR_IN),
pipe->length, XFER_RESULT_SUCCESS, true);
}
static void process_edpt0_brdy(uint8_t rhport)
{
size_t len = USB0.CFIFOCTR.BIT.DTLN;
int cplt = fifo_read(&USB0.CFIFO.WORD, &_dcd.pipe[0], 64, len);
if (cplt || (len < 64)) {
if (2 != cplt) {
USB0.CFIFOCTR.WORD = USB_FIFOCTR_BCLR;
}
dcd_event_xfer_complete(rhport, tu_edpt_addr(0, TUSB_DIR_OUT),
_dcd.pipe[0].length - _dcd.pipe[0].remaining,
XFER_RESULT_SUCCESS, true);
}
}
static bool process_pipe_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes)
{
(void)rhport;
const unsigned epn = tu_edpt_number(ep_addr);
const unsigned dir = tu_edpt_dir(ep_addr);
const unsigned num = _dcd.ep[dir][epn];
TU_ASSERT(num);
pipe_state_t *pipe = &_dcd.pipe[num];
pipe->addr = (uintptr_t)buffer;
pipe_state_t *pipe = &_dcd.pipe[num];
pipe->ff = buffer_type;
pipe->buf = buffer;
pipe->length = total_bytes;
pipe->remaining = total_bytes;
USB0.PIPESEL.WORD = num;
const unsigned mps = USB0.PIPEMAXP.WORD;
if (dir) { /* IN */
USB0.D0FIFOSEL.WORD = num | USB_FIFOSEL_MBW_16;
while (!(USB0.D0FIFOSEL.BIT.CURPIPE != num)) ;
int r = fifo_write(&USB0.D0FIFO.WORD, pipe, mps);
if (r) USB0.D0FIFOCTR.WORD = USB_FIFOCTR_BVAL;
USB0.D0FIFOSEL.WORD = 0;
if (total_bytes) {
pipe_xfer_in(num);
} else { /* ZLP */
USB0.D0FIFOSEL.WORD = num;
pipe_wait_for_ready(num);
USB0.D0FIFOCTR.WORD = USB_FIFOCTR_BVAL;
USB0.D0FIFOSEL.WORD = 0;
while (USB0.D0FIFOSEL.BIT.CURPIPE) ; /* if CURPIPE bits changes, check written value */
}
} else {
volatile reg_pipetre_t *pt = get_pipetre(num);
if (pt) {
const unsigned mps = edpt_max_packet_size(num);
volatile uint16_t *ctr = get_pipectr(num);
if (*ctr & 0x3) *ctr = USB_PIPECTR_PID_NAK;
pt->TRE = TU_BIT(8);
@@ -408,47 +505,50 @@ static bool process_pipe_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer,
*ctr = USB_PIPECTR_PID_BUF;
}
}
// TU_LOG1("X %x %d\r\n", ep_addr, total_bytes);
// TU_LOG1("X %x %d %d\r\n", ep_addr, total_bytes, buffer_type);
return true;
}
static bool process_edpt_xfer(int buffer_type, uint8_t ep_addr, void* buffer, uint16_t total_bytes)
{
const unsigned epn = tu_edpt_number(ep_addr);
if (0 == epn) {
return process_pipe0_xfer(buffer_type, ep_addr, buffer, total_bytes);
} else {
return process_pipe_xfer(buffer_type, ep_addr, buffer, total_bytes);
}
}
static void process_pipe0_bemp(uint8_t rhport)
{
bool completed = pipe0_xfer_in();
if (completed) {
pipe_state_t *pipe = &_dcd.pipe[0];
dcd_event_xfer_complete(rhport, tu_edpt_addr(0, TUSB_DIR_IN),
pipe->length, XFER_RESULT_SUCCESS, true);
}
}
static void process_pipe_brdy(uint8_t rhport, unsigned num)
{
pipe_state_t *pipe = &_dcd.pipe[num];
if (tu_edpt_dir(pipe->ep)) { /* IN */
select_pipe(num, USB_FIFOSEL_MBW_16);
const unsigned mps = USB0.PIPEMAXP.WORD;
unsigned rem = pipe->remaining;
rem -= TU_MIN(rem, mps);
pipe->remaining = rem;
if (rem) {
int r = 0;
r = fifo_write(&USB0.D0FIFO.WORD, pipe, mps);
if (r) USB0.D0FIFOCTR.WORD = USB_FIFOCTR_BVAL;
USB0.D0FIFOSEL.WORD = 0;
return;
}
USB0.D0FIFOSEL.WORD = 0;
pipe->addr = 0;
pipe->remaining = 0;
dcd_event_xfer_complete(rhport, pipe->ep, pipe->length,
XFER_RESULT_SUCCESS, true);
pipe_state_t *pipe = &_dcd.pipe[num];
const unsigned dir = tu_edpt_dir(pipe->ep);
bool completed;
if (dir) { /* IN */
completed = pipe_xfer_in(num);
} else {
const unsigned ctr = select_pipe(num, USB_FIFOSEL_MBW_8);
const unsigned len = ctr & USB_FIFOCTR_DTLN;
const unsigned mps = USB0.PIPEMAXP.WORD;
int cplt = fifo_read(&USB0.D0FIFO.WORD, pipe, mps, len);
if (cplt || (len < mps)) {
if (2 != cplt) {
USB0.D0FIFO.WORD = USB_FIFOCTR_BCLR;
}
USB0.D0FIFOSEL.WORD = 0;
dcd_event_xfer_complete(rhport, pipe->ep,
pipe->length - pipe->remaining,
XFER_RESULT_SUCCESS, true);
return;
if (num) {
completed = pipe_xfer_out(num);
} else {
completed = pipe0_xfer_out();
}
USB0.D0FIFOSEL.WORD = 0;
}
if (completed) {
dcd_event_xfer_complete(rhport, pipe->ep,
pipe->length - pipe->remaining,
XFER_RESULT_SUCCESS, true);
// TU_LOG1("C %d %d\r\n", num, pipe->length - pipe->remaining);
}
}
@@ -458,7 +558,9 @@ static void process_bus_reset(uint8_t rhport)
USB0.BRDYENB.WORD = 1;
USB0.CFIFOCTR.WORD = USB_FIFOCTR_BCLR;
USB0.D0FIFOSEL.WORD = 0;
while (USB0.D0FIFOSEL.BIT.CURPIPE) ; /* if CURPIPE bits changes, check written value */
USB0.D1FIFOSEL.WORD = 0;
while (USB0.D1FIFOSEL.BIT.CURPIPE) ; /* if CURPIPE bits changes, check written value */
volatile uint16_t *ctr = (volatile uint16_t*)((uintptr_t)(&USB0.PIPE1CTR.WORD));
volatile uint16_t *tre = (volatile uint16_t*)((uintptr_t)(&USB0.PIPE1TRE.WORD));
for (int i = 1; i <= 5; ++i) {
@@ -486,12 +588,16 @@ static void process_set_address(uint8_t rhport)
const uint32_t addr = USB0.USBADDR.BIT.USBADDR;
if (!addr) return;
const tusb_control_request_t setup_packet = {
.bmRequestType = 0,
.bRequest = 5,
.wValue = addr,
.wIndex = 0,
.wLength = 0,
};
#if defined(__CCRX__)
.bmRequestType = { 0 }, /* Note: CCRX needs the braces over this struct member */
#else
.bmRequestType = 0,
#endif
.bRequest = TUSB_REQ_SET_ADDRESS,
.wValue = addr,
.wIndex = 0,
.wLength = 0,
};
dcd_event_setup_received(rhport, (const uint8_t*)&setup_packet, true);
}
@@ -513,11 +619,18 @@ void dcd_init(uint8_t rhport)
USB0.SYSCFG.BIT.DCFM = 0;
USB0.SYSCFG.BIT.USBE = 1;
USB.DPUSR0R.BIT.FIXPHY0 = 0u; /* USB0 Transceiver Output fixed */
#if ( CFG_TUSB_MCU == OPT_MCU_RX72N )
USB0.PHYSLEW.LONG = 0x5;
IR(PERIB, INTB185) = 0;
#else
IR(USB0, USBI0) = 0;
#endif
/* Setup default control pipe */
USB0.DCPMAXP.BIT.MXPS = 64;
USB0.INTENB0.WORD = USB_IS0_VBINT | USB_IS0_BRDY | USB_IS0_BEMP | USB_IS0_DVST | USB_IS0_CTRT;
USB0.INTENB0.WORD = USB_IS0_VBINT | USB_IS0_BRDY | USB_IS0_BEMP |
USB_IS0_DVST | USB_IS0_CTRT | (USE_SOF ? USB_IS0_SOFR: 0) | USB_IS0_RESM;
USB0.BEMPENB.WORD = 1;
USB0.BRDYENB.WORD = 1;
@@ -529,13 +642,21 @@ void dcd_init(uint8_t rhport)
void dcd_int_enable(uint8_t rhport)
{
(void)rhport;
#if ( CFG_TUSB_MCU == OPT_MCU_RX72N )
IEN(PERIB, INTB185) = 1;
#else
IEN(USB0, USBI0) = 1;
#endif
}
void dcd_int_disable(uint8_t rhport)
{
(void)rhport;
#if ( CFG_TUSB_MCU == OPT_MCU_RX72N )
IEN(PERIB, INTB185) = 0;
#else
IEN(USB0, USBI0) = 0;
#endif
}
void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
@@ -547,7 +668,7 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
void dcd_remote_wakeup(uint8_t rhport)
{
(void)rhport;
/* TODO */
USB0.DVSTCTR0.BIT.WKUP = 1;
}
void dcd_connect(uint8_t rhport)
@@ -574,7 +695,7 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
const unsigned dir = tu_edpt_dir(ep_addr);
const unsigned xfer = ep_desc->bmAttributes.xfer;
const unsigned mps = ep_desc->wMaxPacketSize.size;
const unsigned mps = tu_le16toh(ep_desc->wMaxPacketSize.size);
if (xfer == TUSB_XFER_ISOCHRONOUS && mps > 256) {
/* USBa supports up to 256 bytes */
return false;
@@ -590,7 +711,7 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
USB0.PIPESEL.WORD = num;
USB0.PIPEMAXP.WORD = mps;
volatile uint16_t *ctr = get_pipectr(num);
*ctr = USB_PIPECTR_ACLRM;
*ctr = USB_PIPECTR_ACLRM | USB_PIPECTR_SQCLR;
*ctr = 0;
unsigned cfg = (dir << 4) | epn;
if (xfer == TUSB_XFER_BULK) {
@@ -606,12 +727,24 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
if (dir || (xfer != TUSB_XFER_BULK)) {
*ctr = USB_PIPECTR_PID_BUF;
}
// TU_LOG1("O %d %x %x\r\n", USB0.PIPESEL.WORD, USB0.PIPECFG.WORD, USB0.PIPEMAXP.WORD);
// TU_LOG1("O %d %x %x\r\n", USB0.PIPESEL.WORD, USB0.PIPECFG.WORD, USB0.PIPEMAXP.WORD);
dcd_int_enable(rhport);
return true;
}
void dcd_edpt_close_all(uint8_t rhport)
{
unsigned i = TU_ARRAY_SIZE(_dcd.pipe);
dcd_int_disable(rhport);
while (--i) { /* Close all pipes except 0 */
const unsigned ep_addr = _dcd.pipe[i].ep;
if (!ep_addr) continue;
dcd_edpt_close(rhport, ep_addr);
}
dcd_int_enable(rhport);
}
void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
{
(void)rhport;
@@ -631,13 +764,19 @@ void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes)
{
bool r;
const unsigned epn = tu_edpt_number(ep_addr);
dcd_int_disable(rhport);
if (0 == epn) {
r = process_edpt0_xfer(rhport, ep_addr, buffer, total_bytes);
} else {
r = process_pipe_xfer(rhport, ep_addr, buffer, total_bytes);
}
r = process_edpt_xfer(0, ep_addr, buffer, total_bytes);
dcd_int_enable(rhport);
return r;
}
bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
{
// USB buffers always work in bytes so to avoid unnecessary divisions we demand item_size = 1
TU_ASSERT(ff->item_size == 1);
bool r;
dcd_int_disable(rhport);
r = process_edpt_xfer(1, ep_addr, ff, total_bytes);
dcd_int_enable(rhport);
return r;
}
@@ -680,8 +819,8 @@ void dcd_int_handler(uint8_t rhport)
(void)rhport;
unsigned is0 = USB0.INTSTS0.WORD;
/* clear bits except VALID */
USB0.INTSTS0.WORD = USB_IS0_VALID;
/* clear active bits except VALID (don't write 0 to already cleared bits according to the HW manual) */
USB0.INTSTS0.WORD = ~((USB_IS0_CTRT | USB_IS0_DVST | USB_IS0_SOFR | USB_IS0_RESM | USB_IS0_VBINT) & is0) | USB_IS0_VALID;
if (is0 & USB_IS0_VBINT) {
if (USB0.INTSTS0.BIT.VBSTS) {
dcd_connect(rhport);
@@ -689,6 +828,19 @@ void dcd_int_handler(uint8_t rhport)
dcd_disconnect(rhport);
}
}
if (is0 & USB_IS0_RESM) {
dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
#if (0==USE_SOF)
USB0.INTENB0.BIT.SOFE = 0;
#endif
}
if ((is0 & USB_IS0_SOFR) && USB0.INTENB0.BIT.SOFE) {
// USBD will exit suspended mode when SOF event is received
dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
#if (0==USE_SOF)
USB0.INTENB0.BIT.SOFE = 0;
#endif
}
if (is0 & USB_IS0_DVST) {
switch (is0 & USB_IS0_DVSQ) {
case USB_IS0_DVSQ_DEF:
@@ -697,6 +849,14 @@ void dcd_int_handler(uint8_t rhport)
case USB_IS0_DVSQ_ADDR:
process_set_address(rhport);
break;
case USB_IS0_DVSQ_SUSP0:
case USB_IS0_DVSQ_SUSP1:
case USB_IS0_DVSQ_SUSP2:
case USB_IS0_DVSQ_SUSP3:
dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
#if (0==USE_SOF)
USB0.INTENB0.BIT.SOFE = 1;
#endif
default:
break;
}
@@ -714,19 +874,26 @@ void dcd_int_handler(uint8_t rhport)
const unsigned s = USB0.BEMPSTS.WORD;
USB0.BEMPSTS.WORD = 0;
if (s & 1) {
process_edpt0_bemp(rhport);
process_pipe0_bemp(rhport);
}
}
if (is0 & USB_IS0_BRDY) {
const unsigned m = USB0.BRDYENB.WORD;
unsigned s = USB0.BRDYSTS.WORD & m;
USB0.BRDYSTS.WORD = 0;
if (s & 1) {
process_edpt0_brdy(rhport);
s &= ~1;
}
/* clear active bits (don't write 0 to already cleared bits according to the HW manual) */
USB0.BRDYSTS.WORD = ~s;
while (s) {
#if defined(__CCRX__)
static const int Mod37BitPosition[] = {
-1, 0, 1, 26, 2, 23, 27, 0, 3, 16, 24, 30, 28, 11, 0, 13, 4,
7, 17, 0, 25, 22, 31, 15, 29, 10, 12, 6, 0, 21, 14, 9, 5,
20, 8, 19, 18
};
const unsigned num = Mod37BitPosition[(-s & s) % 37];
#else
const unsigned num = __builtin_ctz(s);
#endif
process_pipe_brdy(rhport, num);
s &= ~TU_BIT(num);
}
+6
View File
@@ -434,6 +434,12 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
return true;
}
void dcd_edpt_close_all (uint8_t rhport)
{
(void) rhport;
// TODO implement dcd_edpt_close_all()
}
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes)
{
(void)rhport;
+6
View File
@@ -312,6 +312,12 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *p_endpoint_desc)
return true;
}
void dcd_edpt_close_all (uint8_t rhport)
{
(void) rhport;
// TODO implement dcd_edpt_close_all()
}
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes)
{
(void) rhport;
+26 -1
View File
@@ -113,7 +113,8 @@
(CFG_TUSB_MCU == OPT_MCU_STM32F0 ) || \
(CFG_TUSB_MCU == OPT_MCU_STM32F1 && defined(STM32F1_FSDEV)) || \
(CFG_TUSB_MCU == OPT_MCU_STM32F3 ) || \
(CFG_TUSB_MCU == OPT_MCU_STM32L0 ) \
(CFG_TUSB_MCU == OPT_MCU_STM32L0 ) || \
(CFG_TUSB_MCU == OPT_MCU_STM32L1 ) \
)
// In order to reduce the dependance on HAL, we undefine this.
@@ -273,6 +274,20 @@ void dcd_connect(uint8_t rhport)
USB->BCDR |= USB_BCDR_DPPU;
}
#elif defined(SYSCFG_PMC_USB_PU) // works e.g. on STM32L151
// Disable internal D+ PU
void dcd_disconnect(uint8_t rhport)
{
(void) rhport;
SYSCFG->PMC &= ~(SYSCFG_PMC_USB_PU);
}
// Enable internal D+ PU
void dcd_connect(uint8_t rhport)
{
(void) rhport;
SYSCFG->PMC |= SYSCFG_PMC_USB_PU;
}
#endif
// Enable device interrupt
@@ -284,6 +299,8 @@ void dcd_int_enable (uint8_t rhport)
__ISB();
#if CFG_TUSB_MCU == OPT_MCU_STM32F0 || CFG_TUSB_MCU == OPT_MCU_STM32L0
NVIC_EnableIRQ(USB_IRQn);
#elif CFG_TUSB_MCU == OPT_MCU_STM32L1
NVIC_EnableIRQ(USB_LP_IRQn);
#elif CFG_TUSB_MCU == OPT_MCU_STM32F3
// Some STM32F302/F303 devices allow to remap the USB interrupt vectors from
// shared USB/CAN IRQs to separate CAN and USB IRQs.
@@ -318,6 +335,8 @@ void dcd_int_disable(uint8_t rhport)
#if CFG_TUSB_MCU == OPT_MCU_STM32F0 || CFG_TUSB_MCU == OPT_MCU_STM32L0
NVIC_DisableIRQ(USB_IRQn);
#elif CFG_TUSB_MCU == OPT_MCU_STM32L1
NVIC_DisableIRQ(USB_LP_IRQn);
#elif CFG_TUSB_MCU == OPT_MCU_STM32F3
// Some STM32F302/F303 devices allow to remap the USB interrupt vectors from
// shared USB/CAN IRQs to separate CAN and USB IRQs.
@@ -781,6 +800,12 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc
return true;
}
void dcd_edpt_close_all (uint8_t rhport)
{
(void) rhport;
// TODO implement dcd_edpt_close_all()
}
/**
* Close an endpoint.
*
@@ -83,6 +83,10 @@
#include "stm32l0xx.h"
#define PMA_LENGTH (1024u)
#elif CFG_TUSB_MCU == OPT_MCU_STM32L1
#include "stm32l1xx.h"
#define PMA_LENGTH (512u)
#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
+110 -30
View File
@@ -51,7 +51,8 @@
CFG_TUSB_MCU == OPT_MCU_STM32F4 || \
CFG_TUSB_MCU == OPT_MCU_STM32F7 || \
CFG_TUSB_MCU == OPT_MCU_STM32H7 || \
(CFG_TUSB_MCU == OPT_MCU_STM32L4 && defined(STM32L4_SYNOPSYS)) \
(CFG_TUSB_MCU == OPT_MCU_STM32L4 && defined(STM32L4_SYNOPSYS) || \
CFG_TUSB_MCU == OPT_MCU_GD32VF103 ) \
)
// EP_MAX : Max number of bi-directional endpoints including EP0
@@ -92,6 +93,33 @@
#define EP_MAX_FS 6
#define EP_FIFO_SIZE_FS 1280
#elif CFG_TUSB_MCU == OPT_MCU_GD32VF103
#include "synopsys_common.h"
// for remote wakeup delay
#define __NOP() __asm volatile ("nop")
// These numbers are the same for the whole GD32VF103 family.
#define OTG_FS_IRQn 86
#define EP_MAX_FS 4
#define EP_FIFO_SIZE_FS 1280
// The GD32VF103 is a RISC-V MCU, which implements the ECLIC Core-Local
// Interrupt Controller by Nuclei. It is nearly API compatible to the
// NVIC used by ARM MCUs.
#define ECLIC_INTERRUPT_ENABLE_BASE 0xD2001001UL
#define NVIC_EnableIRQ __eclic_enable_interrupt
#define NVIC_DisableIRQ __eclic_disable_interrupt
static inline void __eclic_enable_interrupt (uint32_t irq) {
*(volatile uint8_t*)(ECLIC_INTERRUPT_ENABLE_BASE + (irq * 4)) = 1;
}
static inline void __eclic_disable_interrupt (uint32_t irq){
*(volatile uint8_t*)(ECLIC_INTERRUPT_ENABLE_BASE + (irq * 4)) = 0;
}
#else
#error "Unsupported MCUs"
#endif
@@ -188,13 +216,18 @@ static void bus_reset(uint8_t rhport)
tu_memclr(xfer_status, sizeof(xfer_status));
_out_ep_closed = false;
// clear device address
dev->DCFG &= ~USB_OTG_DCFG_DAD_Msk;
// 1. NAK for all OUT endpoints
for(uint8_t n = 0; n < EP_MAX; n++) {
out_ep[n].DOEPCTL |= USB_OTG_DOEPCTL_SNAK;
}
dev->DAINTMSK |= (1 << USB_OTG_DAINTMSK_OEPM_Pos) | (1 << USB_OTG_DAINTMSK_IEPM_Pos);
dev->DOEPMSK |= USB_OTG_DOEPMSK_STUPM | USB_OTG_DOEPMSK_XFRCM;
dev->DIEPMSK |= USB_OTG_DIEPMSK_TOM | USB_OTG_DIEPMSK_XFRCM;
// 2. Un-mask interrupt bits
dev->DAINTMSK = (1 << USB_OTG_DAINTMSK_OEPM_Pos) | (1 << USB_OTG_DAINTMSK_IEPM_Pos);
dev->DOEPMSK = USB_OTG_DOEPMSK_STUPM | USB_OTG_DOEPMSK_XFRCM;
dev->DIEPMSK = USB_OTG_DIEPMSK_TOM | USB_OTG_DIEPMSK_XFRCM;
// "USB Data FIFOs" section in reference manual
// Peripheral FIFO architecture
@@ -519,19 +552,40 @@ void dcd_int_disable (uint8_t rhport)
void dcd_set_address (uint8_t rhport, uint8_t dev_addr)
{
USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport);
dev->DCFG |= (dev_addr << USB_OTG_DCFG_DAD_Pos) & USB_OTG_DCFG_DAD_Msk;
dev->DCFG = (dev->DCFG & ~USB_OTG_DCFG_DAD_Msk) | (dev_addr << USB_OTG_DCFG_DAD_Pos);
// Response with status after changing device address
dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
}
static void remote_wakeup_delay(void)
{
// try to delay for 1 ms
uint32_t count = SystemCoreClock / 1000;
while ( count-- )
{
__NOP();
}
}
void dcd_remote_wakeup(uint8_t rhport)
{
(void) rhport;
// TODO must manually clear this bit after 1-15 ms
// USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport);
// dev->DCTL |= USB_OTG_DCTL_RWUSIG;
USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport);
USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport);
// set remote wakeup
dev->DCTL |= USB_OTG_DCTL_RWUSIG;
// enable SOF to detect bus resume
usb_otg->GINTSTS = USB_OTG_GINTSTS_SOF;
usb_otg->GINTMSK |= USB_OTG_GINTMSK_SOFM;
// Per specs: remote wakeup signal bit must be clear within 1-15ms
remote_wakeup_delay();
dev->DCTL &= ~USB_OTG_DCTL_RWUSIG;
}
void dcd_connect(uint8_t rhport)
@@ -587,8 +641,9 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
usb_otg->GRXFSIZ = sz;
}
out_ep[epnum].DOEPCTL |= (1 << USB_OTG_DOEPCTL_USBAEP_Pos) |
(desc_edpt->bmAttributes.xfer << USB_OTG_DOEPCTL_EPTYP_Pos) |
out_ep[epnum].DOEPCTL |= (1 << USB_OTG_DOEPCTL_USBAEP_Pos) |
(desc_edpt->bmAttributes.xfer << USB_OTG_DOEPCTL_EPTYP_Pos) |
(desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? USB_OTG_DOEPCTL_SD0PID_SEVNFRM : 0) |
(desc_edpt->wMaxPacketSize.size << USB_OTG_DOEPCTL_MPSIZ_Pos);
dev->DAINTMSK |= (1 << (USB_OTG_DAINTMSK_OEPM_Pos + epnum));
@@ -621,6 +676,8 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
_allocated_fifo_words_tx += fifo_size;
TU_LOG(2, " Allocated %u bytes at offset %u", fifo_size*4, EP_FIFO_SIZE-_allocated_fifo_words_tx*4);
// DIEPTXF starts at FIFO #1.
// Both TXFD and TXSA are in unit of 32-bit words.
usb_otg->DIEPTXF[epnum - 1] = (fifo_size << USB_OTG_DIEPTXF_INEPTXFD_Pos) | (EP_FIFO_SIZE/4 - _allocated_fifo_words_tx);
@@ -628,7 +685,7 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
in_ep[epnum].DIEPCTL |= (1 << USB_OTG_DIEPCTL_USBAEP_Pos) |
(epnum << USB_OTG_DIEPCTL_TXFNUM_Pos) |
(desc_edpt->bmAttributes.xfer << USB_OTG_DIEPCTL_EPTYP_Pos) |
(desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? USB_OTG_DOEPCTL_SD0PID_SEVNFRM : 0) |
(desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? USB_OTG_DIEPCTL_SD0PID_SEVNFRM : 0) |
(desc_edpt->wMaxPacketSize.size << USB_OTG_DIEPCTL_MPSIZ_Pos);
dev->DAINTMSK |= (1 << (USB_OTG_DAINTMSK_IEPM_Pos + epnum));
@@ -637,6 +694,34 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
return true;
}
// Close all non-control endpoints, cancel all pending transfers if any.
void dcd_edpt_close_all (uint8_t rhport)
{
(void) rhport;
// USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport);
USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport);
USB_OTG_OUTEndpointTypeDef * out_ep = OUT_EP_BASE(rhport);
USB_OTG_INEndpointTypeDef * in_ep = IN_EP_BASE(rhport);
// Disable non-control interrupt
dev->DAINTMSK = (1 << USB_OTG_DAINTMSK_OEPM_Pos) | (1 << USB_OTG_DAINTMSK_IEPM_Pos);
for(uint8_t n = 1; n < EP_MAX; n++)
{
// disable OUT endpoint
out_ep[n].DOEPCTL = 0;
xfer_status[n][TUSB_DIR_OUT].max_size = 0;
// disable IN endpoint
in_ep[n].DIEPCTL = 0;
xfer_status[n][TUSB_DIR_IN].max_size = 0;
}
// reset allocated fifo IN
_allocated_fifo_words_tx = 16;
}
bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
{
uint8_t const epnum = tu_edpt_number(ep_addr);
@@ -656,7 +741,7 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
}
uint16_t num_packets = (total_bytes / xfer->max_size);
uint8_t const short_packet_size = total_bytes % xfer->max_size;
uint16_t const short_packet_size = total_bytes % xfer->max_size;
// Zero-size packet is special case.
if(short_packet_size > 0 || (total_bytes == 0)) {
@@ -687,7 +772,7 @@ bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16
xfer->total_len = total_bytes;
uint16_t num_packets = (total_bytes / xfer->max_size);
uint8_t const short_packet_size = total_bytes % xfer->max_size;
uint16_t const short_packet_size = total_bytes % xfer->max_size;
// Zero-size packet is special case.
if(short_packet_size > 0 || (total_bytes == 0)) num_packets++;
@@ -796,22 +881,13 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
// Clear stall and reset data toggle
if(dir == TUSB_DIR_IN) {
in_ep[epnum].DIEPCTL &= ~USB_OTG_DIEPCTL_STALL;
uint8_t eptype = (in_ep[epnum].DIEPCTL & USB_OTG_DIEPCTL_EPTYP_Msk) >> USB_OTG_DIEPCTL_EPTYP_Pos;
// Required by USB spec to reset DATA toggle bit to DATA0 on interrupt and bulk endpoints.
if(eptype == 2 || eptype == 3) {
in_ep[epnum].DIEPCTL |= USB_OTG_DIEPCTL_SD0PID_SEVNFRM;
}
in_ep[epnum].DIEPCTL |= USB_OTG_DIEPCTL_SD0PID_SEVNFRM;
} else {
out_ep[epnum].DOEPCTL &= ~USB_OTG_DOEPCTL_STALL;
uint8_t eptype = (out_ep[epnum].DOEPCTL & USB_OTG_DOEPCTL_EPTYP_Msk) >> USB_OTG_DOEPCTL_EPTYP_Pos;
// Required by USB spec to reset DATA toggle bit to DATA0 on interrupt and bulk endpoints.
if(eptype == 2 || eptype == 3) {
out_ep[epnum].DOEPCTL |= USB_OTG_DOEPCTL_SD0PID_SEVNFRM;
}
out_ep[epnum].DOEPCTL |= USB_OTG_DOEPCTL_SD0PID_SEVNFRM;
}
}
@@ -1048,7 +1124,7 @@ void dcd_int_handler(uint8_t rhport)
USB_OTG_OUTEndpointTypeDef * out_ep = OUT_EP_BASE(rhport);
USB_OTG_INEndpointTypeDef * in_ep = IN_EP_BASE(rhport);
uint32_t int_status = usb_otg->GINTSTS;
uint32_t const int_status = usb_otg->GINTSTS & usb_otg->GINTMSK;
if(int_status & USB_OTG_GINTSTS_USBRST)
{
@@ -1081,6 +1157,9 @@ void dcd_int_handler(uint8_t rhport)
dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
}
// TODO check USB_OTG_GINTSTS_DISCINT for disconnect detection
// if(int_status & USB_OTG_GINTSTS_DISCINT)
if(int_status & USB_OTG_GINTSTS_OTGINT)
{
// OTG INT bit is read-only
@@ -1094,13 +1173,15 @@ void dcd_int_handler(uint8_t rhport)
usb_otg->GOTGINT = otg_int;
}
#if USE_SOF
if(int_status & USB_OTG_GINTSTS_SOF)
{
usb_otg->GINTSTS = USB_OTG_GINTSTS_SOF;
// Disable SOF interrupt since currently only used for remote wakeup detection
usb_otg->GINTMSK &= ~USB_OTG_GINTMSK_SOFM;
dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
}
#endif
// RxFIFO non-empty interrupt handling.
if(int_status & USB_OTG_GINTSTS_RXFLVL)
@@ -1114,8 +1195,7 @@ void dcd_int_handler(uint8_t rhport)
do
{
handle_rxflvl_ints(rhport, out_ep);
int_status = usb_otg->GINTSTS;
} while(int_status & USB_OTG_GINTSTS_RXFLVL);
} while(usb_otg->GINTSTS & USB_OTG_GINTSTS_RXFLVL);
// Manage RX FIFO size
if (_out_ep_closed)
File diff suppressed because it is too large Load Diff
+5
View File
@@ -94,6 +94,11 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
return false;
}
void dcd_edpt_close_all (uint8_t rhport)
{
(void) rhport;
}
// Submit a transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack
bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
{
@@ -298,6 +298,12 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
return true;
}
void dcd_edpt_close_all (uint8_t rhport)
{
(void) rhport;
// TODO implement dcd_edpt_close_all()
}
bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
{
(void) rhport;
@@ -429,6 +429,12 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
return true;
}
void dcd_edpt_close_all (uint8_t rhport)
{
(void) rhport;
// TODO implement dcd_edpt_close_all()
}
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;