Merge branch 'master' into zhangslice-master
This commit is contained in:
@@ -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
@@ -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 -------------//
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user