Merge branch 'master' into test-mode-support
This commit is contained in:
@@ -30,6 +30,7 @@
|
||||
|
||||
#include <stdatomic.h>
|
||||
#include "host/hcd.h"
|
||||
#include "host/usbh.h"
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
//
|
||||
@@ -166,6 +167,17 @@ enum {
|
||||
DEFAULT_HIEN = HIRQ_CONDET_IRQ | HIRQ_FRAME_IRQ | HIRQ_HXFRDN_IRQ | HIRQ_RCVDAV_IRQ
|
||||
};
|
||||
|
||||
enum {
|
||||
MAX_NAK_DEFAULT = 1 // Number of NAK per endpoint per usb frame
|
||||
};
|
||||
|
||||
enum {
|
||||
EP_STATE_IDLE = 0,
|
||||
EP_STATE_COMPLETE = 1,
|
||||
EP_STATE_ATTEMPT_1 = 2, // pending 1st attempt
|
||||
EP_STATE_ATTEMPT_MAX = 15
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
//
|
||||
//--------------------------------------------------------------------+
|
||||
@@ -173,18 +185,21 @@ enum {
|
||||
typedef struct {
|
||||
uint8_t daddr;
|
||||
|
||||
struct TU_ATTR_PACKED {
|
||||
uint8_t ep_dir : 1;
|
||||
uint8_t is_iso : 1;
|
||||
uint8_t is_setup : 1;
|
||||
uint8_t data_toggle : 1;
|
||||
uint8_t xfer_pending : 1;
|
||||
uint8_t xfer_complete : 1;
|
||||
union { ;
|
||||
struct TU_ATTR_PACKED {
|
||||
uint8_t ep_num : 4;
|
||||
uint8_t is_setup : 1;
|
||||
uint8_t is_out : 1;
|
||||
uint8_t is_iso : 1;
|
||||
}hxfr_bm;
|
||||
|
||||
uint8_t hxfr;
|
||||
};
|
||||
|
||||
struct TU_ATTR_PACKED {
|
||||
uint8_t ep_num : 4;
|
||||
uint16_t packet_size : 12;
|
||||
uint8_t state : 4;
|
||||
uint8_t data_toggle : 1;
|
||||
uint16_t packet_size : 11;
|
||||
};
|
||||
|
||||
uint16_t total_len;
|
||||
@@ -195,6 +210,8 @@ typedef struct {
|
||||
TU_VERIFY_STATIC(sizeof(max3421_ep_t) == 12, "size is not correct");
|
||||
|
||||
typedef struct {
|
||||
volatile uint16_t frame_count;
|
||||
|
||||
// cached register
|
||||
uint8_t sndbc;
|
||||
uint8_t hirq;
|
||||
@@ -204,18 +221,24 @@ typedef struct {
|
||||
uint8_t hxfr;
|
||||
|
||||
atomic_flag busy; // busy transferring
|
||||
volatile uint16_t frame_count;
|
||||
|
||||
max3421_ep_t ep[CFG_TUH_MAX3421_ENDPOINT_TOTAL]; // [0] is reserved for addr0
|
||||
|
||||
OSAL_MUTEX_DEF(spi_mutexdef);
|
||||
#if OSAL_MUTEX_REQUIRED
|
||||
OSAL_MUTEX_DEF(spi_mutexdef);
|
||||
osal_mutex_t spi_mutex;
|
||||
#endif
|
||||
|
||||
max3421_ep_t ep[CFG_TUH_MAX3421_ENDPOINT_TOTAL]; // [0] is reserved for addr0
|
||||
} max3421_data_t;
|
||||
|
||||
static max3421_data_t _hcd_data;
|
||||
|
||||
// max NAK before giving up in a frame. 0 means infinite NAKs
|
||||
static tuh_configure_max3421_t _tuh_cfg = {
|
||||
.max_nak = MAX_NAK_DEFAULT,
|
||||
.cpuctl = 0, // default: INT pulse width = 10.6 us
|
||||
.pinctl = 0, // default: negative edge interrupt
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// API: SPI transfer with MAX3421E
|
||||
// - spi_cs_api(), spi_xfer_api(), int_api(): must be implemented by application
|
||||
@@ -304,7 +327,6 @@ static void fifo_write(uint8_t rhport, uint8_t reg, uint8_t const * buffer, uint
|
||||
tuh_max3421_spi_xfer_api(rhport, buffer, NULL, len);
|
||||
|
||||
max3421_spi_unlock(rhport, in_isr);
|
||||
|
||||
}
|
||||
|
||||
static void fifo_read(uint8_t rhport, uint8_t * buffer, uint16_t len, bool in_isr) {
|
||||
@@ -321,35 +343,35 @@ static void fifo_read(uint8_t rhport, uint8_t * buffer, uint16_t len, bool in_is
|
||||
}
|
||||
|
||||
//------------- register write helper -------------//
|
||||
static inline void hirq_write(uint8_t rhport, uint8_t data, bool in_isr) {
|
||||
TU_ATTR_ALWAYS_INLINE static inline void hirq_write(uint8_t rhport, uint8_t data, bool in_isr) {
|
||||
reg_write(rhport, HIRQ_ADDR, data, in_isr);
|
||||
// HIRQ write 1 is clear
|
||||
_hcd_data.hirq &= (uint8_t) ~data;
|
||||
}
|
||||
|
||||
static inline void hien_write(uint8_t rhport, uint8_t data, bool in_isr) {
|
||||
TU_ATTR_ALWAYS_INLINE static inline void hien_write(uint8_t rhport, uint8_t data, bool in_isr) {
|
||||
_hcd_data.hien = data;
|
||||
reg_write(rhport, HIEN_ADDR, data, in_isr);
|
||||
}
|
||||
|
||||
static inline void mode_write(uint8_t rhport, uint8_t data, bool in_isr) {
|
||||
TU_ATTR_ALWAYS_INLINE static inline void mode_write(uint8_t rhport, uint8_t data, bool in_isr) {
|
||||
_hcd_data.mode = data;
|
||||
reg_write(rhport, MODE_ADDR, data, in_isr);
|
||||
}
|
||||
|
||||
static inline void peraddr_write(uint8_t rhport, uint8_t data, bool in_isr) {
|
||||
TU_ATTR_ALWAYS_INLINE static inline void peraddr_write(uint8_t rhport, uint8_t data, bool in_isr) {
|
||||
if ( _hcd_data.peraddr == data ) return; // no need to change address
|
||||
|
||||
_hcd_data.peraddr = data;
|
||||
reg_write(rhport, PERADDR_ADDR, data, in_isr);
|
||||
}
|
||||
|
||||
static inline void hxfr_write(uint8_t rhport, uint8_t data, bool in_isr) {
|
||||
TU_ATTR_ALWAYS_INLINE static inline void hxfr_write(uint8_t rhport, uint8_t data, bool in_isr) {
|
||||
_hcd_data.hxfr = data;
|
||||
reg_write(rhport, HXFR_ADDR, data, in_isr);
|
||||
}
|
||||
|
||||
static inline void sndbc_write(uint8_t rhport, uint8_t data, bool in_isr) {
|
||||
TU_ATTR_ALWAYS_INLINE static inline void sndbc_write(uint8_t rhport, uint8_t data, bool in_isr) {
|
||||
_hcd_data.sndbc = data;
|
||||
reg_write(rhport, SNDBC_ADDR, data, in_isr);
|
||||
}
|
||||
@@ -359,10 +381,11 @@ static inline void sndbc_write(uint8_t rhport, uint8_t data, bool in_isr) {
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
static max3421_ep_t* find_ep_not_addr0(uint8_t daddr, uint8_t ep_num, uint8_t ep_dir) {
|
||||
uint8_t const is_out = 1-ep_dir;
|
||||
for(size_t i=1; i<CFG_TUH_MAX3421_ENDPOINT_TOTAL; i++) {
|
||||
max3421_ep_t* ep = &_hcd_data.ep[i];
|
||||
// for control endpoint, skip direction check
|
||||
if (daddr == ep->daddr && ep_num == ep->ep_num && (ep_dir == ep->ep_dir || ep_num == 0)) {
|
||||
// control endpoint is bi-direction (skip check)
|
||||
if (daddr == ep->daddr && ep_num == ep->hxfr_bm.ep_num && (ep_num == 0 || is_out == ep->hxfr_bm.is_out)) {
|
||||
return ep;
|
||||
}
|
||||
}
|
||||
@@ -393,14 +416,23 @@ static void free_ep(uint8_t daddr) {
|
||||
}
|
||||
}
|
||||
|
||||
// Check if endpoint has an queued transfer and not reach max NAK
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool is_ep_pending(max3421_ep_t const * ep) {
|
||||
uint8_t const state = ep->state;
|
||||
return ep->packet_size && (state >= EP_STATE_ATTEMPT_1) &&
|
||||
(_tuh_cfg.max_nak == 0 || state < EP_STATE_ATTEMPT_1 + _tuh_cfg.max_nak);
|
||||
}
|
||||
|
||||
// Find the next pending endpoint using round-robin scheduling, starting from next endpoint.
|
||||
// return NULL if not found
|
||||
// TODO respect interrupt endpoint's interval
|
||||
static max3421_ep_t * find_next_pending_ep(max3421_ep_t * cur_ep) {
|
||||
size_t const idx = (size_t) (cur_ep - _hcd_data.ep);
|
||||
|
||||
// starting from next endpoint
|
||||
for (size_t i = idx + 1; i < CFG_TUH_MAX3421_ENDPOINT_TOTAL; i++) {
|
||||
max3421_ep_t* ep = &_hcd_data.ep[i];
|
||||
if (ep->xfer_pending && ep->packet_size) {
|
||||
// TU_LOG3("next pending i = %u\r\n", i);
|
||||
if (is_ep_pending(ep)) {
|
||||
return ep;
|
||||
}
|
||||
}
|
||||
@@ -408,8 +440,7 @@ static max3421_ep_t * find_next_pending_ep(max3421_ep_t * cur_ep) {
|
||||
// wrap around including current endpoint
|
||||
for (size_t i = 0; i <= idx; i++) {
|
||||
max3421_ep_t* ep = &_hcd_data.ep[i];
|
||||
if (ep->xfer_pending && ep->packet_size) {
|
||||
// TU_LOG3("next pending i = %u\r\n", i);
|
||||
if (is_ep_pending(ep)) {
|
||||
return ep;
|
||||
}
|
||||
}
|
||||
@@ -424,10 +455,11 @@ static max3421_ep_t * find_next_pending_ep(max3421_ep_t * cur_ep) {
|
||||
// optional hcd configuration, called by tuh_configure()
|
||||
bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param) {
|
||||
(void) rhport;
|
||||
(void) cfg_id;
|
||||
(void) cfg_param;
|
||||
TU_VERIFY(cfg_id == TUH_CFGID_MAX3421 && cfg_param != NULL);
|
||||
|
||||
return false;
|
||||
tuh_configure_param_t const* cfg = (tuh_configure_param_t const*) cfg_param;
|
||||
_tuh_cfg = cfg->max3421;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Initialize controller to host mode
|
||||
@@ -438,6 +470,7 @@ bool hcd_init(uint8_t rhport) {
|
||||
|
||||
TU_LOG2_INT(sizeof(max3421_ep_t));
|
||||
TU_LOG2_INT(sizeof(max3421_data_t));
|
||||
TU_LOG2_INT(offsetof(max3421_data_t, ep));
|
||||
|
||||
tu_memclr(&_hcd_data, sizeof(_hcd_data));
|
||||
_hcd_data.peraddr = 0xff; // invalid
|
||||
@@ -446,13 +479,15 @@ bool hcd_init(uint8_t rhport) {
|
||||
_hcd_data.spi_mutex = osal_mutex_create(&_hcd_data.spi_mutexdef);
|
||||
#endif
|
||||
|
||||
// full duplex, interrupt negative edge
|
||||
reg_write(rhport, PINCTL_ADDR, PINCTL_FDUPSPI, false);
|
||||
// NOTE: driver does not seem to work without nRST pin signal
|
||||
|
||||
// V1 is 0x01, V2 is 0x12, V3 is 0x13
|
||||
// full duplex, interrupt negative edge
|
||||
reg_write(rhport, PINCTL_ADDR, _tuh_cfg.pinctl | PINCTL_FDUPSPI, false);
|
||||
|
||||
// v1 is 0x01, v2 is 0x12, v3 is 0x13
|
||||
uint8_t const revision = reg_read(rhport, REVISION_ADDR, false);
|
||||
TU_ASSERT(revision == 0x01 || revision == 0x12 || revision == 0x13, false);
|
||||
TU_LOG2_HEX(revision);
|
||||
TU_ASSERT(revision == 0x01 || revision == 0x12 || revision == 0x13, false);
|
||||
|
||||
// reset
|
||||
reg_write(rhport, USBCTL_ADDR, USBCTL_CHIPRES, false);
|
||||
@@ -476,7 +511,25 @@ bool hcd_init(uint8_t rhport) {
|
||||
tuh_max3421_int_api(rhport, true);
|
||||
|
||||
// Enable Interrupt pin
|
||||
reg_write(rhport, CPUCTL_ADDR, CPUCTL_IE, false);
|
||||
reg_write(rhport, CPUCTL_ADDR, _tuh_cfg.cpuctl | CPUCTL_IE, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hcd_deinit(uint8_t rhport) {
|
||||
(void) rhport;
|
||||
|
||||
// disable interrupt
|
||||
tuh_max3421_int_api(rhport, false);
|
||||
|
||||
// reset max3421 and power down
|
||||
reg_write(rhport, USBCTL_ADDR, USBCTL_CHIPRES, false);
|
||||
reg_write(rhport, USBCTL_ADDR, USBCTL_PWRDOWN, false);
|
||||
|
||||
#if OSAL_MUTEX_REQUIRED
|
||||
osal_mutex_delete(_hcd_data.spi_mutex);
|
||||
_hcd_data.spi_mutex = NULL;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -539,7 +592,6 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) {
|
||||
// Open an endpoint
|
||||
bool hcd_edpt_open(uint8_t rhport, uint8_t daddr, tusb_desc_endpoint_t const * ep_desc) {
|
||||
(void) rhport;
|
||||
(void) daddr;
|
||||
|
||||
uint8_t const ep_num = tu_edpt_number(ep_desc->bEndpointAddress);
|
||||
tusb_dir_t const ep_dir = tu_edpt_dir(ep_desc->bEndpointAddress);
|
||||
@@ -551,12 +603,9 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t daddr, tusb_desc_endpoint_t const * e
|
||||
ep = allocate_ep();
|
||||
TU_ASSERT(ep);
|
||||
ep->daddr = daddr;
|
||||
ep->ep_num = (uint8_t) (ep_num & 0x0f);
|
||||
ep->ep_dir = (ep_dir == TUSB_DIR_IN) ? 1 : 0;
|
||||
}
|
||||
|
||||
if ( TUSB_XFER_ISOCHRONOUS == ep_desc->bmAttributes.xfer ) {
|
||||
ep->is_iso = 1;
|
||||
ep->hxfr_bm.ep_num = (uint8_t) (ep_num & 0x0f);
|
||||
ep->hxfr_bm.is_out = (ep_dir == TUSB_DIR_OUT) ? 1 : 0;
|
||||
ep->hxfr_bm.is_iso = (TUSB_XFER_ISOCHRONOUS == ep_desc->bmAttributes.xfer) ? 1 : 0;
|
||||
}
|
||||
|
||||
ep->packet_size = (uint16_t) (tu_edpt_packet_size(ep_desc) & 0x7ff);
|
||||
@@ -564,7 +613,7 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t daddr, tusb_desc_endpoint_t const * e
|
||||
return true;
|
||||
}
|
||||
|
||||
void xact_out(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool in_isr) {
|
||||
static void xact_out(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool in_isr) {
|
||||
// Page 12: Programming BULK-OUT Transfers
|
||||
// TODO double buffered
|
||||
if (switch_ep) {
|
||||
@@ -580,12 +629,10 @@ void xact_out(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool in_isr) {
|
||||
fifo_write(rhport, SNDFIFO_ADDR, ep->buf, xact_len, in_isr);
|
||||
}
|
||||
sndbc_write(rhport, xact_len, in_isr);
|
||||
|
||||
uint8_t const hxfr = (uint8_t ) (ep->ep_num | HXFR_OUT_NIN | (ep->is_iso ? HXFR_ISO : 0));
|
||||
hxfr_write(rhport, hxfr, in_isr);
|
||||
hxfr_write(rhport, ep->hxfr, in_isr);
|
||||
}
|
||||
|
||||
void xact_in(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool in_isr) {
|
||||
static void xact_in(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool in_isr) {
|
||||
// Page 13: Programming BULK-IN Transfers
|
||||
if (switch_ep) {
|
||||
peraddr_write(rhport, ep->daddr, in_isr);
|
||||
@@ -594,33 +641,36 @@ void xact_in(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool in_isr) {
|
||||
reg_write(rhport, HCTL_ADDR, hctl, in_isr);
|
||||
}
|
||||
|
||||
uint8_t const hxfr = (uint8_t) (ep->ep_num | (ep->is_iso ? HXFR_ISO : 0));
|
||||
hxfr_write(rhport, hxfr, in_isr);
|
||||
hxfr_write(rhport, ep->hxfr, in_isr);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void xact_inout(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool in_isr) {
|
||||
if (ep->ep_num == 0 ) {
|
||||
static void xact_setup(uint8_t rhport, max3421_ep_t *ep, bool in_isr) {
|
||||
peraddr_write(rhport, ep->daddr, in_isr);
|
||||
fifo_write(rhport, SUDFIFO_ADDR, ep->buf, 8, in_isr);
|
||||
hxfr_write(rhport, HXFR_SETUP, in_isr);
|
||||
}
|
||||
|
||||
static void xact_generic(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool in_isr) {
|
||||
if (ep->hxfr_bm.ep_num == 0 ) {
|
||||
// setup
|
||||
if (ep->is_setup) {
|
||||
peraddr_write(rhport, ep->daddr, in_isr);
|
||||
fifo_write(rhport, SUDFIFO_ADDR, ep->buf, 8, in_isr);
|
||||
hxfr_write(rhport, HXFR_SETUP, in_isr);
|
||||
if (ep->hxfr_bm.is_setup) {
|
||||
xact_setup(rhport, ep, in_isr);
|
||||
return;
|
||||
}
|
||||
|
||||
// status
|
||||
if (ep->buf == NULL || ep->total_len == 0) {
|
||||
uint8_t const hxfr = HXFR_HS | (ep->ep_dir ? 0 : HXFR_OUT_NIN);
|
||||
uint8_t const hxfr = (uint8_t) (HXFR_HS | (ep->hxfr & HXFR_OUT_NIN));
|
||||
peraddr_write(rhport, ep->daddr, in_isr);
|
||||
hxfr_write(rhport, hxfr, in_isr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (ep->ep_dir) {
|
||||
xact_in(rhport, ep, switch_ep, in_isr);
|
||||
}else {
|
||||
if (ep->hxfr_bm.is_out) {
|
||||
xact_out(rhport, ep, switch_ep, in_isr);
|
||||
}else {
|
||||
xact_in(rhport, ep, switch_ep, in_isr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -632,25 +682,21 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t daddr, uint8_t ep_addr, uint8_t * buf
|
||||
max3421_ep_t* ep = find_opened_ep(daddr, ep_num, ep_dir);
|
||||
TU_VERIFY(ep);
|
||||
|
||||
// control transfer can switch direction
|
||||
ep->ep_dir = ep_dir ? 1u : 0u;
|
||||
if (ep_num == 0) {
|
||||
// control transfer can switch direction
|
||||
ep->hxfr_bm.is_out = ep_dir ? 0 : 1;
|
||||
ep->hxfr_bm.is_setup = 0;
|
||||
ep->data_toggle = 1;
|
||||
}
|
||||
|
||||
ep->buf = buffer;
|
||||
ep->total_len = buflen;
|
||||
ep->xferred_len = 0;
|
||||
ep->xfer_complete = 0;
|
||||
ep->xfer_pending = 1;
|
||||
|
||||
if ( ep_num == 0 ) {
|
||||
ep->is_setup = 0;
|
||||
ep->data_toggle = 1;
|
||||
}
|
||||
ep->state = EP_STATE_ATTEMPT_1;
|
||||
|
||||
// carry out transfer if not busy
|
||||
if ( !atomic_flag_test_and_set(&_hcd_data.busy) ) {
|
||||
xact_inout(rhport, ep, true, false);
|
||||
} else {
|
||||
return true;
|
||||
if (!atomic_flag_test_and_set(&_hcd_data.busy)) {
|
||||
xact_generic(rhport, ep, true, false);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -673,17 +719,16 @@ bool hcd_setup_send(uint8_t rhport, uint8_t daddr, uint8_t const setup_packet[8]
|
||||
max3421_ep_t* ep = find_opened_ep(daddr, 0, 0);
|
||||
TU_ASSERT(ep);
|
||||
|
||||
ep->ep_dir = 0;
|
||||
ep->is_setup = 1;
|
||||
ep->hxfr_bm.is_out = 1;
|
||||
ep->hxfr_bm.is_setup = 1;
|
||||
ep->buf = (uint8_t*)(uintptr_t) setup_packet;
|
||||
ep->total_len = 8;
|
||||
ep->xferred_len = 0;
|
||||
ep->xfer_complete = 0;
|
||||
ep->xfer_pending = 1;
|
||||
ep->state = EP_STATE_ATTEMPT_1;
|
||||
|
||||
// carry out transfer if not busy
|
||||
if ( !atomic_flag_test_and_set(&_hcd_data.busy) ) {
|
||||
xact_inout(rhport, ep, true, false);
|
||||
if (!atomic_flag_test_and_set(&_hcd_data.busy)) {
|
||||
xact_setup(rhport, ep, false);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -748,22 +793,23 @@ static void handle_connect_irq(uint8_t rhport, bool in_isr) {
|
||||
}
|
||||
|
||||
static void xfer_complete_isr(uint8_t rhport, max3421_ep_t *ep, xfer_result_t result, uint8_t hrsl, bool in_isr) {
|
||||
uint8_t const ep_addr = tu_edpt_addr(ep->ep_num, ep->ep_dir);
|
||||
uint8_t const ep_dir = 1-ep->hxfr_bm.is_out;
|
||||
uint8_t const ep_addr = tu_edpt_addr(ep->hxfr_bm.ep_num, ep_dir);
|
||||
|
||||
// save data toggle
|
||||
if (ep->ep_dir) {
|
||||
if (ep_dir) {
|
||||
ep->data_toggle = (hrsl & HRSL_RCVTOGRD) ? 1u : 0u;
|
||||
}else {
|
||||
ep->data_toggle = (hrsl & HRSL_SNDTOGRD) ? 1u : 0u;
|
||||
}
|
||||
|
||||
ep->xfer_pending = 0;
|
||||
ep->state = EP_STATE_IDLE;
|
||||
hcd_event_xfer_complete(ep->daddr, ep_addr, ep->xferred_len, result, in_isr);
|
||||
|
||||
// Find next pending endpoint
|
||||
max3421_ep_t *next_ep = find_next_pending_ep(ep);
|
||||
max3421_ep_t * next_ep = find_next_pending_ep(ep);
|
||||
if (next_ep) {
|
||||
xact_inout(rhport, next_ep, true, in_isr);
|
||||
xact_generic(rhport, next_ep, true, in_isr);
|
||||
}else {
|
||||
// no more pending
|
||||
atomic_flag_clear(&_hcd_data.busy);
|
||||
@@ -793,20 +839,23 @@ static void handle_xfer_done(uint8_t rhport, bool in_isr) {
|
||||
|
||||
case HRSL_NAK:
|
||||
if (ep_num == 0) {
|
||||
// NAK on control, retry immediately
|
||||
// control endpoint -> retry immediately
|
||||
hxfr_write(rhport, _hcd_data.hxfr, in_isr);
|
||||
}else {
|
||||
// NAK on non-control, find next pending to switch
|
||||
max3421_ep_t *next_ep = find_next_pending_ep(ep);
|
||||
} else {
|
||||
if (ep->state < EP_STATE_ATTEMPT_MAX) {
|
||||
ep->state++;
|
||||
}
|
||||
|
||||
max3421_ep_t * next_ep = find_next_pending_ep(ep);
|
||||
if (ep == next_ep) {
|
||||
// this endpoint is only one pending, retry immediately
|
||||
// this endpoint is only one pending -> retry immediately
|
||||
hxfr_write(rhport, _hcd_data.hxfr, in_isr);
|
||||
}else if (next_ep) {
|
||||
// switch to next pending TODO could have issue with double buffered if not clear previously out data
|
||||
xact_inout(rhport, next_ep, true, in_isr);
|
||||
}else {
|
||||
TU_ASSERT(false,);
|
||||
} else if (next_ep) {
|
||||
// switch to next pending endpoint TODO could have issue with double buffered if not clear previously out data
|
||||
xact_generic(rhport, next_ep, true, in_isr);
|
||||
} else {
|
||||
// no more pending in this frame -> clear busy
|
||||
atomic_flag_clear(&_hcd_data.busy);
|
||||
}
|
||||
}
|
||||
return;
|
||||
@@ -828,12 +877,14 @@ static void handle_xfer_done(uint8_t rhport, bool in_isr) {
|
||||
|
||||
if (ep_dir) {
|
||||
// IN transfer: fifo data is already received in RCVDAV IRQ
|
||||
if ( hxfr_type & HXFR_HS ) {
|
||||
ep->xfer_complete = 1;
|
||||
|
||||
// mark control handshake as complete
|
||||
if (hxfr_type & HXFR_HS) {
|
||||
ep->state = EP_STATE_COMPLETE;
|
||||
}
|
||||
|
||||
// short packet or all bytes transferred
|
||||
if ( ep->xfer_complete ) {
|
||||
if (ep->state == EP_STATE_COMPLETE) {
|
||||
xfer_complete_isr(rhport, ep, xfer_result, hrsl, in_isr);
|
||||
}else {
|
||||
// more to transfer
|
||||
@@ -867,13 +918,13 @@ static void handle_xfer_done(uint8_t rhport, bool in_isr) {
|
||||
void print_hirq(uint8_t hirq) {
|
||||
TU_LOG3_HEX(hirq);
|
||||
|
||||
if (hirq & HIRQ_HXFRDN_IRQ) TU_LOG3(" HXFRDN");
|
||||
if (hirq & HIRQ_FRAME_IRQ) TU_LOG3(" FRAME");
|
||||
if (hirq & HIRQ_CONDET_IRQ) TU_LOG3(" CONDET");
|
||||
if (hirq & HIRQ_SUSDN_IRQ) TU_LOG3(" SUSDN");
|
||||
if (hirq & HIRQ_SNDBAV_IRQ) TU_LOG3(" SNDBAV");
|
||||
if (hirq & HIRQ_RCVDAV_IRQ) TU_LOG3(" RCVDAV");
|
||||
if (hirq & HIRQ_RWU_IRQ) TU_LOG3(" RWU");
|
||||
if (hirq & HIRQ_HXFRDN_IRQ) TU_LOG3(" HXFRDN");
|
||||
if (hirq & HIRQ_FRAME_IRQ) TU_LOG3(" FRAME");
|
||||
if (hirq & HIRQ_CONDET_IRQ) TU_LOG3(" CONDET");
|
||||
if (hirq & HIRQ_SUSDN_IRQ) TU_LOG3(" SUSDN");
|
||||
if (hirq & HIRQ_SNDBAV_IRQ) TU_LOG3(" SNDBAV");
|
||||
if (hirq & HIRQ_RCVDAV_IRQ) TU_LOG3(" RCVDAV");
|
||||
if (hirq & HIRQ_RWU_IRQ) TU_LOG3(" RWU");
|
||||
if (hirq & HIRQ_BUSEVENT_IRQ) TU_LOG3(" BUSEVENT");
|
||||
|
||||
TU_LOG3("\r\n");
|
||||
@@ -890,6 +941,25 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) {
|
||||
|
||||
if (hirq & HIRQ_FRAME_IRQ) {
|
||||
_hcd_data.frame_count++;
|
||||
|
||||
max3421_ep_t* ep_retry = NULL;
|
||||
|
||||
// reset all endpoints attempt counter
|
||||
for (size_t i = 0; i < CFG_TUH_MAX3421_ENDPOINT_TOTAL; i++) {
|
||||
max3421_ep_t* ep = &_hcd_data.ep[i];
|
||||
if (ep->packet_size && ep->state > EP_STATE_ATTEMPT_1) {
|
||||
ep->state = EP_STATE_ATTEMPT_1;
|
||||
|
||||
if (ep_retry == NULL) {
|
||||
ep_retry = ep;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// start usb transfer if not busy
|
||||
if (ep_retry != NULL && !atomic_flag_test_and_set(&_hcd_data.busy)) {
|
||||
xact_generic(rhport, ep_retry, true, in_isr);
|
||||
}
|
||||
}
|
||||
|
||||
if (hirq & HIRQ_CONDET_IRQ) {
|
||||
@@ -898,17 +968,17 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) {
|
||||
|
||||
// queue more transfer in handle_xfer_done() can cause hirq to be set again while external IRQ may not catch and/or
|
||||
// not call this handler again. So we need to loop until all IRQ are cleared
|
||||
while ( hirq & (HIRQ_RCVDAV_IRQ | HIRQ_HXFRDN_IRQ) ) {
|
||||
if ( hirq & HIRQ_RCVDAV_IRQ ) {
|
||||
while (hirq & (HIRQ_RCVDAV_IRQ | HIRQ_HXFRDN_IRQ)) {
|
||||
if (hirq & HIRQ_RCVDAV_IRQ) {
|
||||
uint8_t const ep_num = _hcd_data.hxfr & HXFR_EPNUM_MASK;
|
||||
max3421_ep_t *ep = find_opened_ep(_hcd_data.peraddr, ep_num, 1);
|
||||
max3421_ep_t* ep = find_opened_ep(_hcd_data.peraddr, ep_num, 1);
|
||||
uint8_t xact_len = 0;
|
||||
|
||||
// RCVDAV_IRQ can trigger 2 times (dual buffered)
|
||||
while ( hirq & HIRQ_RCVDAV_IRQ ) {
|
||||
while (hirq & HIRQ_RCVDAV_IRQ) {
|
||||
uint8_t rcvbc = reg_read(rhport, RCVBC_ADDR, in_isr);
|
||||
xact_len = (uint8_t) tu_min16(rcvbc, ep->total_len - ep->xferred_len);
|
||||
if ( xact_len ) {
|
||||
if (xact_len) {
|
||||
fifo_read(rhport, ep->buf, xact_len, in_isr);
|
||||
ep->buf += xact_len;
|
||||
ep->xferred_len += xact_len;
|
||||
@@ -919,12 +989,12 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) {
|
||||
hirq = reg_read(rhport, HIRQ_ADDR, in_isr);
|
||||
}
|
||||
|
||||
if ( xact_len < ep->packet_size || ep->xferred_len >= ep->total_len ) {
|
||||
ep->xfer_complete = 1;
|
||||
if (xact_len < ep->packet_size || ep->xferred_len >= ep->total_len) {
|
||||
ep->state = EP_STATE_COMPLETE;
|
||||
}
|
||||
}
|
||||
|
||||
if ( hirq & HIRQ_HXFRDN_IRQ ) {
|
||||
if (hirq & HIRQ_HXFRDN_IRQ) {
|
||||
hirq_write(rhport, HIRQ_HXFRDN_IRQ, in_isr);
|
||||
handle_xfer_done(rhport, in_isr);
|
||||
}
|
||||
|
||||
@@ -36,14 +36,12 @@
|
||||
#define CI_FS_REG(_port) ((ci_fs_regs_t*) USB0_BASE)
|
||||
#define CI_REG CI_FS_REG(0)
|
||||
|
||||
void dcd_int_enable(uint8_t rhport)
|
||||
{
|
||||
void dcd_int_enable(uint8_t rhport) {
|
||||
(void) rhport;
|
||||
NVIC_EnableIRQ(USB0_IRQn);
|
||||
}
|
||||
|
||||
void dcd_int_disable(uint8_t rhport)
|
||||
{
|
||||
void dcd_int_disable(uint8_t rhport) {
|
||||
(void) rhport;
|
||||
NVIC_DisableIRQ(USB0_IRQn);
|
||||
}
|
||||
|
||||
@@ -29,19 +29,28 @@
|
||||
|
||||
#include "fsl_device_registers.h"
|
||||
|
||||
#define CI_FS_REG(_port) ((ci_fs_regs_t*) USBFS0_BASE)
|
||||
#define CI_REG CI_FS_REG(0)
|
||||
#if CFG_TUSB_MCU == OPT_MCU_MCXN9
|
||||
#define CI_FS_REG(_port) ((ci_fs_regs_t*) USBFS0_BASE)
|
||||
#define CIFS_IRQN USB0_FS_IRQn
|
||||
|
||||
void dcd_int_enable(uint8_t rhport)
|
||||
{
|
||||
#elif CFG_TUSB_MCU == OPT_MCU_MCXA15
|
||||
#define CI_FS_REG(_port) ((ci_fs_regs_t*) USB0_BASE)
|
||||
#define CIFS_IRQN USB0_IRQn
|
||||
|
||||
#else
|
||||
#error "MCU is not supported"
|
||||
#endif
|
||||
|
||||
#define CI_REG CI_FS_REG(0)
|
||||
|
||||
void dcd_int_enable(uint8_t rhport) {
|
||||
(void) rhport;
|
||||
NVIC_EnableIRQ(USB0_FS_IRQn);
|
||||
NVIC_EnableIRQ(CIFS_IRQN);
|
||||
}
|
||||
|
||||
void dcd_int_disable(uint8_t rhport)
|
||||
{
|
||||
void dcd_int_disable(uint8_t rhport) {
|
||||
(void) rhport;
|
||||
NVIC_DisableIRQ(USB0_FS_IRQn);
|
||||
NVIC_DisableIRQ(CIFS_IRQN);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -271,9 +271,21 @@ void dcd_init(uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
|
||||
// save crystal-less setting (if available)
|
||||
#if defined(FSL_FEATURE_USB_KHCI_IRC48M_MODULE_CLOCK_ENABLED) && FSL_FEATURE_USB_KHCI_IRC48M_MODULE_CLOCK_ENABLED == 1
|
||||
uint32_t clk_recover_irc_en = CI_REG->CLK_RECOVER_IRC_EN;
|
||||
uint32_t clk_recover_ctrl = CI_REG->CLK_RECOVER_CTRL;
|
||||
#endif
|
||||
|
||||
CI_REG->USBTRC0 |= USB_USBTRC0_USBRESET_MASK;
|
||||
while (CI_REG->USBTRC0 & USB_USBTRC0_USBRESET_MASK);
|
||||
|
||||
// restore crystal-less setting (if available)
|
||||
#if defined(FSL_FEATURE_USB_KHCI_IRC48M_MODULE_CLOCK_ENABLED) && FSL_FEATURE_USB_KHCI_IRC48M_MODULE_CLOCK_ENABLED == 1
|
||||
CI_REG->CLK_RECOVER_IRC_EN = clk_recover_irc_en;
|
||||
CI_REG->CLK_RECOVER_CTRL |= clk_recover_ctrl;
|
||||
#endif
|
||||
|
||||
tu_memclr(&_dcd, sizeof(_dcd));
|
||||
CI_REG->USBTRC0 |= TU_BIT(6); /* software must set this bit to 1 */
|
||||
CI_REG->BDT_PAGE1 = (uint8_t)((uintptr_t)_dcd.bdt >> 8);
|
||||
@@ -283,7 +295,7 @@ void dcd_init(uint8_t rhport)
|
||||
CI_REG->INT_EN = USB_INTEN_USBRSTEN_MASK;
|
||||
|
||||
dcd_connect(rhport);
|
||||
// NVIC_ClearPendingIRQ(USB0_IRQn);
|
||||
// NVIC_ClearPendingIRQ(CIFS_IRQN);
|
||||
}
|
||||
|
||||
void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
|
||||
|
||||
@@ -92,7 +92,7 @@ CFG_TUH_MEM_SECTION TU_ATTR_ALIGNED(4096) static ehci_data_t ehci_data;
|
||||
//--------------------------------------------------------------------+
|
||||
// Debug
|
||||
//--------------------------------------------------------------------+
|
||||
#if CFG_TUSB_DEBUG >= (EHCI_DBG + 1)
|
||||
#if 0 && CFG_TUSB_DEBUG >= (EHCI_DBG + 1)
|
||||
static inline void print_portsc(ehci_registers_t* regs) {
|
||||
TU_LOG_HEX(EHCI_DBG, regs->portsc);
|
||||
TU_LOG(EHCI_DBG, " Connect Status : %u\r\n", regs->portsc_bm.current_connect_status);
|
||||
|
||||
@@ -31,16 +31,26 @@
|
||||
#if (((CFG_TUSB_MCU == OPT_MCU_ESP32S2) || (CFG_TUSB_MCU == OPT_MCU_ESP32S3)) && CFG_TUD_ENABLED)
|
||||
|
||||
// Espressif
|
||||
#include "freertos/xtensa_api.h"
|
||||
#include "xtensa_api.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "esp_log.h"
|
||||
#include "soc/dport_reg.h"
|
||||
#include "soc/gpio_sig_map.h"
|
||||
#include "soc/usb_periph.h"
|
||||
#include "soc/usb_reg.h"
|
||||
#include "soc/usb_struct.h"
|
||||
#include "soc/periph_defs.h" // for interrupt source
|
||||
|
||||
#include "device/dcd.h"
|
||||
|
||||
#ifndef USB_OUT_EP_NUM
|
||||
#define USB_OUT_EP_NUM ((int) (sizeof(USB0.out_ep_reg) / sizeof(USB0.out_ep_reg[0])))
|
||||
#endif
|
||||
|
||||
#ifndef USB_IN_EP_NUM
|
||||
#define USB_IN_EP_NUM ((int) (sizeof(USB0.in_ep_reg) / sizeof(USB0.in_ep_reg[0])))
|
||||
#endif
|
||||
|
||||
// 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)
|
||||
|
||||
@@ -283,7 +283,18 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
|
||||
/* Response with status first before changing device address */
|
||||
dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
|
||||
}
|
||||
|
||||
#ifdef __GNUC__ // caused by extra declaration of SystemCoreClock in freeRTOSConfig.h
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wredundant-decls"
|
||||
#endif
|
||||
|
||||
extern u32 SystemCoreClock;
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
void dcd_remote_wakeup(uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -269,9 +269,21 @@ void dcd_init(uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
|
||||
// save crystal-less setting (if available)
|
||||
#if defined(FSL_FEATURE_USB_KHCI_IRC48M_MODULE_CLOCK_ENABLED) && FSL_FEATURE_USB_KHCI_IRC48M_MODULE_CLOCK_ENABLED == 1
|
||||
uint32_t clk_recover_irc_en = KHCI->CLK_RECOVER_IRC_EN;
|
||||
uint32_t clk_recover_ctrl = KHCI->CLK_RECOVER_CTRL;
|
||||
#endif
|
||||
|
||||
KHCI->USBTRC0 |= USB_USBTRC0_USBRESET_MASK;
|
||||
while (KHCI->USBTRC0 & USB_USBTRC0_USBRESET_MASK);
|
||||
|
||||
// restore crystal-less setting (if available)
|
||||
#if defined(FSL_FEATURE_USB_KHCI_IRC48M_MODULE_CLOCK_ENABLED) && FSL_FEATURE_USB_KHCI_IRC48M_MODULE_CLOCK_ENABLED == 1
|
||||
KHCI->CLK_RECOVER_IRC_EN = clk_recover_irc_en;
|
||||
KHCI->CLK_RECOVER_CTRL |= clk_recover_ctrl;
|
||||
#endif
|
||||
|
||||
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);
|
||||
|
||||
@@ -42,7 +42,18 @@
|
||||
|
||||
#if TU_CHECK_MCU(OPT_MCU_LPC11UXX, OPT_MCU_LPC13XX, OPT_MCU_LPC15XX)
|
||||
// LPCOpen
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
#pragma GCC diagnostic ignored "-Wstrict-prototypes"
|
||||
#endif
|
||||
|
||||
#include "chip.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#else
|
||||
// SDK
|
||||
#include "fsl_device_registers.h"
|
||||
@@ -239,7 +250,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool ep_is_iso(ep_cmd_sts_t* ep_cs, bool is_
|
||||
return is_highspeed ? (ep_cs[0].cmd_sts.type && !ep_cs[0].cmd_sts.rf_tv) : ep_cs->cmd_sts.type;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool ep_is_bulk(ep_cmd_sts_t* ep_cs) {
|
||||
TU_ATTR_ALWAYS_INLINE TU_ATTR_UNUSED static inline bool ep_is_bulk(ep_cmd_sts_t* ep_cs) {
|
||||
return (ep_cs[0].cmd_sts.type == 0) && (ep_cs[0].cmd_sts.rf_tv == 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ typedef struct TU_ATTR_ALIGNED(16)
|
||||
volatile uint32_t condition_code : 4;
|
||||
|
||||
// Word 1
|
||||
volatile uint8_t* current_buffer_pointer;
|
||||
uint8_t* volatile current_buffer_pointer;
|
||||
|
||||
// Word 2 : next TD
|
||||
volatile uint32_t next;
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
// Init these in dcd_init
|
||||
static uint8_t *next_buffer_ptr;
|
||||
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];
|
||||
@@ -56,79 +56,70 @@ static struct hw_endpoint hw_endpoints[USB_MAX_ENDPOINTS][2];
|
||||
// SOF may be used by remote wakeup as RESUME, this indicate whether SOF is actually used by usbd
|
||||
static bool _sof_enable = false;
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline struct hw_endpoint *hw_endpoint_get_by_num(uint8_t num, tusb_dir_t dir)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline struct hw_endpoint* hw_endpoint_get_by_num(uint8_t num, tusb_dir_t dir) {
|
||||
return &hw_endpoints[num][dir];
|
||||
}
|
||||
|
||||
static struct hw_endpoint *hw_endpoint_get_by_addr(uint8_t ep_addr)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline 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);
|
||||
}
|
||||
|
||||
static void _hw_endpoint_alloc(struct hw_endpoint *ep, uint8_t transfer_type)
|
||||
{
|
||||
static void _hw_endpoint_alloc(struct hw_endpoint* ep, uint8_t transfer_type) {
|
||||
// size must be multiple of 64
|
||||
uint size = tu_div_ceil(ep->wMaxPacketSize, 64) * 64u;
|
||||
|
||||
// double buffered Bulk endpoint
|
||||
if ( transfer_type == TUSB_XFER_BULK )
|
||||
{
|
||||
if (transfer_type == TUSB_XFER_BULK) {
|
||||
size *= 2u;
|
||||
}
|
||||
|
||||
ep->hw_data_buf = next_buffer_ptr;
|
||||
next_buffer_ptr += size;
|
||||
|
||||
assert(((uintptr_t )next_buffer_ptr & 0b111111u) == 0);
|
||||
assert(((uintptr_t) next_buffer_ptr & 0b111111u) == 0);
|
||||
uint dpram_offset = hw_data_offset(ep->hw_data_buf);
|
||||
hard_assert(hw_data_offset(next_buffer_ptr) <= USB_DPRAM_MAX);
|
||||
|
||||
pico_info(" Allocated %d bytes at offset 0x%x (0x%p)\r\n", size, dpram_offset, ep->hw_data_buf);
|
||||
|
||||
// Fill in endpoint control register with buffer offset
|
||||
uint32_t const reg = EP_CTRL_ENABLE_BITS | ((uint)transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | dpram_offset;
|
||||
uint32_t const reg = EP_CTRL_ENABLE_BITS | ((uint) transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | dpram_offset;
|
||||
|
||||
*ep->endpoint_control = reg;
|
||||
}
|
||||
|
||||
static void _hw_endpoint_close(struct hw_endpoint *ep)
|
||||
{
|
||||
// Clear hardware registers and then zero the struct
|
||||
// Clears endpoint enable
|
||||
*ep->endpoint_control = 0;
|
||||
// Clears buffer available, etc
|
||||
*ep->buffer_control = 0;
|
||||
// Clear any endpoint state
|
||||
memset(ep, 0, sizeof(struct hw_endpoint));
|
||||
static void _hw_endpoint_close(struct hw_endpoint* ep) {
|
||||
// Clear hardware registers and then zero the struct
|
||||
// Clears endpoint enable
|
||||
*ep->endpoint_control = 0;
|
||||
// Clears buffer available, etc
|
||||
*ep->buffer_control = 0;
|
||||
// Clear any endpoint state
|
||||
memset(ep, 0, sizeof(struct hw_endpoint));
|
||||
|
||||
// Reclaim buffer space if all endpoints are closed
|
||||
bool reclaim_buffers = true;
|
||||
for ( uint8_t i = 1; i < USB_MAX_ENDPOINTS; i++ )
|
||||
{
|
||||
if (hw_endpoint_get_by_num(i, TUSB_DIR_OUT)->hw_data_buf != NULL || hw_endpoint_get_by_num(i, TUSB_DIR_IN)->hw_data_buf != NULL)
|
||||
{
|
||||
reclaim_buffers = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (reclaim_buffers)
|
||||
{
|
||||
next_buffer_ptr = &usb_dpram->epx_data[0];
|
||||
// Reclaim buffer space if all endpoints are closed
|
||||
bool reclaim_buffers = true;
|
||||
for (uint8_t i = 1; i < USB_MAX_ENDPOINTS; i++) {
|
||||
if (hw_endpoint_get_by_num(i, TUSB_DIR_OUT)->hw_data_buf != NULL ||
|
||||
hw_endpoint_get_by_num(i, TUSB_DIR_IN)->hw_data_buf != NULL) {
|
||||
reclaim_buffers = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (reclaim_buffers) {
|
||||
next_buffer_ptr = &usb_dpram->epx_data[0];
|
||||
}
|
||||
}
|
||||
|
||||
static void hw_endpoint_close(uint8_t ep_addr)
|
||||
{
|
||||
struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr);
|
||||
_hw_endpoint_close(ep);
|
||||
static void hw_endpoint_close(uint8_t ep_addr) {
|
||||
struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr);
|
||||
_hw_endpoint_close(ep);
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
const uint8_t num = tu_edpt_number(ep_addr);
|
||||
const tusb_dir_t dir = tu_edpt_dir(ep_addr);
|
||||
@@ -143,35 +134,26 @@ static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t t
|
||||
ep->transfer_type = transfer_type;
|
||||
|
||||
// Every endpoint has a buffer control register in dpram
|
||||
if ( dir == TUSB_DIR_IN )
|
||||
{
|
||||
if (dir == TUSB_DIR_IN) {
|
||||
ep->buffer_control = &usb_dpram->ep_buf_ctrl[num].in;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
ep->buffer_control = &usb_dpram->ep_buf_ctrl[num].out;
|
||||
}
|
||||
|
||||
// Clear existing buffer control state
|
||||
*ep->buffer_control = 0;
|
||||
|
||||
if ( num == 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
|
||||
{
|
||||
} else {
|
||||
// Set the endpoint control register (starts at EP1, hence num-1)
|
||||
if ( dir == TUSB_DIR_IN )
|
||||
{
|
||||
if (dir == TUSB_DIR_IN) {
|
||||
ep->endpoint_control = &usb_dpram->ep_ctrl[num - 1].in;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
ep->endpoint_control = &usb_dpram->ep_ctrl[num - 1].out;
|
||||
}
|
||||
|
||||
@@ -180,76 +162,82 @@ static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t t
|
||||
}
|
||||
}
|
||||
|
||||
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_start(ep, buffer, total_bytes);
|
||||
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_start(ep, buffer, total_bytes);
|
||||
}
|
||||
|
||||
static void __tusb_irq_path_func(hw_handle_buff_status)(void)
|
||||
{
|
||||
uint32_t remaining_buffers = usb_hw->buf_status;
|
||||
pico_trace("buf_status = 0x%08lx\r\n", remaining_buffers);
|
||||
uint bit = 1u;
|
||||
for (uint8_t i = 0; remaining_buffers && i < USB_MAX_ENDPOINTS * 2; i++)
|
||||
{
|
||||
if (remaining_buffers & bit)
|
||||
{
|
||||
// clear this in advance
|
||||
usb_hw_clear->buf_status = bit;
|
||||
static void __tusb_irq_path_func(hw_handle_buff_status)(void) {
|
||||
uint32_t remaining_buffers = usb_hw->buf_status;
|
||||
pico_trace("buf_status = 0x%08lx\r\n", remaining_buffers);
|
||||
uint bit = 1u;
|
||||
for (uint8_t i = 0; remaining_buffers && i < USB_MAX_ENDPOINTS * 2; i++) {
|
||||
if (remaining_buffers & bit) {
|
||||
// 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) ? TUSB_DIR_OUT : TUSB_DIR_IN);
|
||||
// IN transfer for even i, OUT transfer for odd i
|
||||
struct hw_endpoint* ep = hw_endpoint_get_by_num(i >> 1u, (i & 1u) ? TUSB_DIR_OUT : TUSB_DIR_IN);
|
||||
|
||||
// Continue xfer
|
||||
bool done = hw_endpoint_xfer_continue(ep);
|
||||
if (done)
|
||||
{
|
||||
// Notify
|
||||
dcd_event_xfer_complete(0, ep->ep_addr, ep->xferred_len, XFER_RESULT_SUCCESS, true);
|
||||
hw_endpoint_reset_transfer(ep);
|
||||
}
|
||||
remaining_buffers &= ~bit;
|
||||
}
|
||||
bit <<= 1u;
|
||||
// Continue xfer
|
||||
bool done = hw_endpoint_xfer_continue(ep);
|
||||
if (done) {
|
||||
// Notify
|
||||
dcd_event_xfer_complete(0, ep->ep_addr, ep->xferred_len, XFER_RESULT_SUCCESS, true);
|
||||
hw_endpoint_reset_transfer(ep);
|
||||
}
|
||||
remaining_buffers &= ~bit;
|
||||
}
|
||||
bit <<= 1u;
|
||||
}
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void reset_ep0_pid(void)
|
||||
{
|
||||
// If we have finished this transfer on EP0 set pid back to 1 for next
|
||||
// setup transfer. Also clear a stall in case
|
||||
uint8_t addrs[] = {0x0, 0x80};
|
||||
for (uint i = 0 ; i < TU_ARRAY_SIZE(addrs); i++)
|
||||
{
|
||||
struct hw_endpoint *ep = hw_endpoint_get_by_addr(addrs[i]);
|
||||
ep->next_pid = 1u;
|
||||
TU_ATTR_ALWAYS_INLINE static inline void reset_ep0(void) {
|
||||
// If we have finished this transfer on EP0 set pid back to 1 for next
|
||||
// setup transfer. Also clear a stall in case
|
||||
for (uint8_t dir = 0; dir < 2; dir++) {
|
||||
struct hw_endpoint* ep = hw_endpoint_get_by_num(0, dir);
|
||||
if (ep->active) {
|
||||
// Abort any pending transfer from a prior control transfer per USB specs
|
||||
// Due to Errata RP2040-E2: ABORT flag is only applicable for B2 and later (unusable for B0, B1).
|
||||
// Which means we are not guaranteed to safely abort pending transfer on B0 and B1.
|
||||
uint32_t const abort_mask = (dir ? USB_EP_ABORT_EP0_IN_BITS : USB_EP_ABORT_EP0_OUT_BITS);
|
||||
if (rp2040_chip_version() >= 2) {
|
||||
usb_hw_set->abort = abort_mask;
|
||||
while ((usb_hw->abort_done & abort_mask) != abort_mask) {}
|
||||
}
|
||||
|
||||
_hw_endpoint_buffer_control_set_value32(ep, USB_BUF_CTRL_DATA1_PID | USB_BUF_CTRL_SEL);
|
||||
hw_endpoint_reset_transfer(ep);
|
||||
|
||||
if (rp2040_chip_version() >= 2) {
|
||||
usb_hw_clear->abort_done = abort_mask;
|
||||
usb_hw_clear->abort = abort_mask;
|
||||
}
|
||||
}
|
||||
ep->next_pid = 1u;
|
||||
}
|
||||
}
|
||||
|
||||
static void __tusb_irq_path_func(reset_non_control_endpoints)(void)
|
||||
{
|
||||
static void __tusb_irq_path_func(reset_non_control_endpoints)(void) {
|
||||
// Disable all non-control
|
||||
for ( uint8_t i = 0; i < USB_MAX_ENDPOINTS-1; i++ )
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// clear non-control hw endpoints
|
||||
tu_memclr(hw_endpoints[1], sizeof(hw_endpoints) - 2*sizeof(hw_endpoint_t));
|
||||
tu_memclr(hw_endpoints[1], sizeof(hw_endpoints) - 2 * sizeof(hw_endpoint_t));
|
||||
|
||||
// reclaim buffer space
|
||||
next_buffer_ptr = &usb_dpram->epx_data[0];
|
||||
}
|
||||
|
||||
static void __tusb_irq_path_func(dcd_rp2040_irq)(void)
|
||||
{
|
||||
static void __tusb_irq_path_func(dcd_rp2040_irq)(void) {
|
||||
uint32_t const status = usb_hw->ints;
|
||||
uint32_t handled = 0;
|
||||
|
||||
if ( status & USB_INTF_DEV_SOF_BITS )
|
||||
{
|
||||
if (status & USB_INTF_DEV_SOF_BITS) {
|
||||
bool keep_sof_alive = false;
|
||||
|
||||
handled |= USB_INTF_DEV_SOF_BITS;
|
||||
@@ -258,20 +246,17 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void)
|
||||
// Errata 15 workaround for Device Bulk-In endpoint
|
||||
e15_last_sof = time_us_32();
|
||||
|
||||
for ( uint8_t i = 0; i < USB_MAX_ENDPOINTS; i++ )
|
||||
{
|
||||
struct hw_endpoint * ep = hw_endpoint_get_by_num(i, TUSB_DIR_IN);
|
||||
for (uint8_t i = 0; i < USB_MAX_ENDPOINTS; i++) {
|
||||
struct hw_endpoint* ep = hw_endpoint_get_by_num(i, TUSB_DIR_IN);
|
||||
|
||||
// Active Bulk IN endpoint requires SOF
|
||||
if ( (ep->transfer_type == TUSB_XFER_BULK) && ep->active )
|
||||
{
|
||||
if ((ep->transfer_type == TUSB_XFER_BULK) && ep->active) {
|
||||
keep_sof_alive = true;
|
||||
|
||||
hw_endpoint_lock_update(ep, 1);
|
||||
|
||||
// Deferred enable?
|
||||
if ( ep->pending )
|
||||
{
|
||||
if (ep->pending) {
|
||||
ep->pending = 0;
|
||||
hw_endpoint_start_next_buffer(ep);
|
||||
}
|
||||
@@ -282,26 +267,24 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void)
|
||||
#endif
|
||||
|
||||
// disable SOF interrupt if it is used for RESUME in remote wakeup
|
||||
if ( !keep_sof_alive && !_sof_enable ) usb_hw_clear->inte = USB_INTS_DEV_SOF_BITS;
|
||||
if (!keep_sof_alive && !_sof_enable) usb_hw_clear->inte = USB_INTS_DEV_SOF_BITS;
|
||||
|
||||
dcd_event_sof(0, usb_hw->sof_rd & USB_SOF_RD_BITS, true);
|
||||
}
|
||||
|
||||
// xfer events are handled before setup req. So if a transfer completes immediately
|
||||
// before closing the EP, the events will be delivered in same order.
|
||||
if ( status & USB_INTS_BUFF_STATUS_BITS )
|
||||
{
|
||||
if (status & USB_INTS_BUFF_STATUS_BITS) {
|
||||
handled |= USB_INTS_BUFF_STATUS_BITS;
|
||||
hw_handle_buff_status();
|
||||
}
|
||||
|
||||
if ( status & USB_INTS_SETUP_REQ_BITS )
|
||||
{
|
||||
if (status & USB_INTS_SETUP_REQ_BITS) {
|
||||
handled |= USB_INTS_SETUP_REQ_BITS;
|
||||
uint8_t const * setup = remove_volatile_cast(uint8_t const*, &usb_dpram->setup_packet);
|
||||
uint8_t const* setup = remove_volatile_cast(uint8_t const*, &usb_dpram->setup_packet);
|
||||
|
||||
// reset pid to both 1 (data and ack)
|
||||
reset_ep0_pid();
|
||||
reset_ep0();
|
||||
|
||||
// Pass setup packet to tiny usb
|
||||
dcd_event_setup_received(0, setup, true);
|
||||
@@ -329,8 +312,7 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void)
|
||||
#endif
|
||||
|
||||
// SE0 for 2.5 us or more (will last at least 10ms)
|
||||
if ( status & USB_INTS_BUS_RESET_BITS )
|
||||
{
|
||||
if (status & USB_INTS_BUS_RESET_BITS) {
|
||||
pico_trace("BUS RESET\r\n");
|
||||
|
||||
handled |= USB_INTS_BUS_RESET_BITS;
|
||||
@@ -342,7 +324,7 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void)
|
||||
|
||||
#if TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX
|
||||
// Only run enumeration workaround 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
|
||||
}
|
||||
|
||||
@@ -354,22 +336,19 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void)
|
||||
* because without VBUS detection, it is impossible to tell the difference between
|
||||
* being disconnected and suspended.
|
||||
*/
|
||||
if ( status & USB_INTS_DEV_SUSPEND_BITS )
|
||||
{
|
||||
if (status & USB_INTS_DEV_SUSPEND_BITS) {
|
||||
handled |= USB_INTS_DEV_SUSPEND_BITS;
|
||||
dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
|
||||
usb_hw_clear->sie_status = USB_SIE_STATUS_SUSPENDED_BITS;
|
||||
}
|
||||
|
||||
if ( status & USB_INTS_DEV_RESUME_FROM_HOST_BITS )
|
||||
{
|
||||
if (status & USB_INTS_DEV_RESUME_FROM_HOST_BITS) {
|
||||
handled |= USB_INTS_DEV_RESUME_FROM_HOST_BITS;
|
||||
dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
|
||||
usb_hw_clear->sie_status = USB_SIE_STATUS_RESUME_BITS;
|
||||
}
|
||||
|
||||
if ( status ^ handled )
|
||||
{
|
||||
if (status ^ handled) {
|
||||
panic("Unhandled IRQ 0x%x\n", (uint) (status ^ handled));
|
||||
}
|
||||
}
|
||||
@@ -390,10 +369,11 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void)
|
||||
#define PICO_SHARED_IRQ_HANDLER_HIGHEST_ORDER_PRIORITY 0xff
|
||||
#endif
|
||||
|
||||
void dcd_init (uint8_t rhport)
|
||||
{
|
||||
void dcd_init(uint8_t rhport) {
|
||||
assert(rhport == 0);
|
||||
|
||||
TU_LOG(2, "Chip Version B%u\r\n", rp2040_chip_version());
|
||||
|
||||
// Reset hardware to default state
|
||||
rp2040_usb_init();
|
||||
|
||||
@@ -405,7 +385,7 @@ void dcd_init (uint8_t rhport)
|
||||
irq_add_shared_handler(USBCTRL_IRQ, dcd_rp2040_irq, PICO_SHARED_IRQ_HANDLER_HIGHEST_ORDER_PRIORITY);
|
||||
|
||||
// Init control endpoints
|
||||
tu_memclr(hw_endpoints[0], 2*sizeof(hw_endpoint_t));
|
||||
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);
|
||||
|
||||
@@ -420,27 +400,37 @@ void dcd_init (uint8_t rhport)
|
||||
// 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);
|
||||
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(__unused uint8_t rhport)
|
||||
{
|
||||
assert(rhport == 0);
|
||||
irq_set_enabled(USBCTRL_IRQ, true);
|
||||
bool dcd_deinit(uint8_t rhport) {
|
||||
(void) rhport;
|
||||
|
||||
reset_non_control_endpoints();
|
||||
irq_remove_handler(USBCTRL_IRQ, dcd_rp2040_irq);
|
||||
|
||||
// reset usb hardware into initial state
|
||||
reset_block(RESETS_RESET_USBCTRL_BITS);
|
||||
unreset_block_wait(RESETS_RESET_USBCTRL_BITS);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void dcd_int_disable(__unused uint8_t rhport)
|
||||
{
|
||||
assert(rhport == 0);
|
||||
irq_set_enabled(USBCTRL_IRQ, false);
|
||||
void dcd_int_enable(__unused uint8_t rhport) {
|
||||
assert(rhport == 0);
|
||||
irq_set_enabled(USBCTRL_IRQ, true);
|
||||
}
|
||||
|
||||
void dcd_set_address (__unused uint8_t rhport, __unused uint8_t dev_addr)
|
||||
{
|
||||
void dcd_int_disable(__unused uint8_t rhport) {
|
||||
assert(rhport == 0);
|
||||
irq_set_enabled(USBCTRL_IRQ, false);
|
||||
}
|
||||
|
||||
void dcd_set_address(__unused uint8_t rhport, __unused uint8_t dev_addr) {
|
||||
assert(rhport == 0);
|
||||
|
||||
// Can't set device address in hardware until status xfer has complete
|
||||
@@ -448,8 +438,7 @@ void dcd_set_address (__unused uint8_t rhport, __unused uint8_t dev_addr)
|
||||
hw_endpoint_xfer(0x80, NULL, 0);
|
||||
}
|
||||
|
||||
void dcd_remote_wakeup(__unused uint8_t rhport)
|
||||
{
|
||||
void dcd_remote_wakeup(__unused uint8_t rhport) {
|
||||
pico_info("dcd_remote_wakeup %d\n", rhport);
|
||||
assert(rhport == 0);
|
||||
|
||||
@@ -460,100 +449,88 @@ void dcd_remote_wakeup(__unused uint8_t rhport)
|
||||
}
|
||||
|
||||
// disconnect by disabling internal pull-up resistor on D+/D-
|
||||
void dcd_disconnect(__unused uint8_t rhport)
|
||||
{
|
||||
void dcd_disconnect(__unused uint8_t rhport) {
|
||||
(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(__unused uint8_t rhport)
|
||||
{
|
||||
void dcd_connect(__unused uint8_t rhport) {
|
||||
(void) rhport;
|
||||
usb_hw_set->sie_ctrl = USB_SIE_CTRL_PULLUP_EN_BITS;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
void dcd_sof_enable(uint8_t rhport, bool en) {
|
||||
(void) rhport;
|
||||
|
||||
_sof_enable = en;
|
||||
|
||||
if (en)
|
||||
{
|
||||
if (en) {
|
||||
usb_hw_set->inte = USB_INTS_DEV_SOF_BITS;
|
||||
}else
|
||||
{
|
||||
}
|
||||
#if !TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
|
||||
else {
|
||||
// Don't clear immediately if the SOF workaround is in use.
|
||||
// The SOF handler will conditionally disable the interrupt.
|
||||
#if !TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
|
||||
usb_hw_clear->inte = USB_INTS_DEV_SOF_BITS;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* DCD Endpoint port
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request)
|
||||
{
|
||||
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 )
|
||||
{
|
||||
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 (__unused uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
|
||||
{
|
||||
assert(rhport == 0);
|
||||
hw_endpoint_init(desc_edpt->bEndpointAddress, tu_edpt_packet_size(desc_edpt), desc_edpt->bmAttributes.xfer);
|
||||
return true;
|
||||
bool dcd_edpt_open(__unused uint8_t rhport, tusb_desc_endpoint_t const* desc_edpt) {
|
||||
assert(rhport == 0);
|
||||
hw_endpoint_init(desc_edpt->bEndpointAddress, tu_edpt_packet_size(desc_edpt), desc_edpt->bmAttributes.xfer);
|
||||
return true;
|
||||
}
|
||||
|
||||
void dcd_edpt_close_all (uint8_t rhport)
|
||||
{
|
||||
void dcd_edpt_close_all(uint8_t rhport) {
|
||||
(void) rhport;
|
||||
|
||||
// may need to use EP Abort
|
||||
reset_non_control_endpoints();
|
||||
}
|
||||
|
||||
bool dcd_edpt_xfer(__unused uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
|
||||
{
|
||||
assert(rhport == 0);
|
||||
hw_endpoint_xfer(ep_addr, buffer, total_bytes);
|
||||
return true;
|
||||
bool dcd_edpt_xfer(__unused uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) {
|
||||
assert(rhport == 0);
|
||||
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) {
|
||||
(void) rhport;
|
||||
|
||||
if ( tu_edpt_number(ep_addr) == 0 )
|
||||
{
|
||||
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;
|
||||
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);
|
||||
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) {
|
||||
(void) rhport;
|
||||
|
||||
if (tu_edpt_number(ep_addr))
|
||||
{
|
||||
struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr);
|
||||
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;
|
||||
@@ -561,16 +538,13 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
|
||||
}
|
||||
}
|
||||
|
||||
void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr)
|
||||
{
|
||||
(void) rhport;
|
||||
|
||||
pico_trace("dcd_edpt_close %02x\r\n", ep_addr);
|
||||
hw_endpoint_close(ep_addr);
|
||||
void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) {
|
||||
(void) rhport;
|
||||
pico_trace("dcd_edpt_close %02x\r\n", ep_addr);
|
||||
hw_endpoint_close(ep_addr);
|
||||
}
|
||||
|
||||
void __tusb_irq_path_func(dcd_int_handler)(uint8_t rhport)
|
||||
{
|
||||
void __tusb_irq_path_func(dcd_int_handler)(uint8_t rhport) {
|
||||
(void) rhport;
|
||||
dcd_rp2040_irq();
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ static void __tusb_irq_path_func(_handle_buff_status_bit)(uint bit, struct hw_en
|
||||
static void __tusb_irq_path_func(hw_handle_buff_status)(void)
|
||||
{
|
||||
uint32_t remaining_buffers = usb_hw->buf_status;
|
||||
pico_trace("buf_status 0x%08x\n", remaining_buffers);
|
||||
pico_trace("buf_status 0x%08lx\n", remaining_buffers);
|
||||
|
||||
// Check EPX first
|
||||
uint bit = 0b1;
|
||||
@@ -325,10 +325,8 @@ static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t
|
||||
ep->wMaxPacketSize = wMaxPacketSize;
|
||||
ep->transfer_type = transfer_type;
|
||||
|
||||
pico_trace("hw_endpoint_init dev %d ep %d %s xfer %d\n", ep->dev_addr, tu_edpt_number(ep->ep_addr),
|
||||
ep_dir_string[tu_edpt_dir(ep->ep_addr)], ep->transfer_type);
|
||||
pico_trace("dev %d ep %d %s setup buffer @ 0x%p\n", ep->dev_addr, tu_edpt_number(ep->ep_addr),
|
||||
ep_dir_string[tu_edpt_dir(ep->ep_addr)], ep->hw_data_buf);
|
||||
pico_trace("hw_endpoint_init dev %d ep %02X xfer %d\n", ep->dev_addr, ep->ep_addr, ep->transfer_type);
|
||||
pico_trace("dev %d ep %02X setup buffer @ 0x%p\n", ep->dev_addr, ep->ep_addr, ep->hw_data_buf);
|
||||
uint dpram_offset = hw_data_offset(ep->hw_data_buf);
|
||||
// Bits 0-5 should be 0
|
||||
assert(!(dpram_offset & 0b111111));
|
||||
@@ -343,7 +341,7 @@ static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t
|
||||
ep_reg |= (uint32_t) ((bmInterval - 1) << EP_CTRL_HOST_INTERRUPT_INTERVAL_LSB);
|
||||
}
|
||||
*ep->endpoint_control = ep_reg;
|
||||
pico_trace("endpoint control (0x%p) <- 0x%x\n", ep->endpoint_control, ep_reg);
|
||||
pico_trace("endpoint control (0x%p) <- 0x%lx\n", ep->endpoint_control, ep_reg);
|
||||
ep->configured = true;
|
||||
|
||||
if ( ep != &epx )
|
||||
@@ -411,6 +409,16 @@ bool hcd_init(uint8_t rhport)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hcd_deinit(uint8_t rhport) {
|
||||
(void) rhport;
|
||||
|
||||
irq_remove_handler(USBCTRL_IRQ, hcd_rp2040_irq);
|
||||
reset_block(RESETS_RESET_USBCTRL_BITS);
|
||||
unreset_block_wait(RESETS_RESET_USBCTRL_BITS);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void hcd_port_reset(uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
|
||||
@@ -35,26 +35,18 @@
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF PROTOTYPE
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Direction strings for debug
|
||||
const char *ep_dir_string[] = {
|
||||
"out",
|
||||
"in",
|
||||
};
|
||||
|
||||
static void _hw_endpoint_xfer_sync(struct hw_endpoint *ep);
|
||||
static void _hw_endpoint_xfer_sync(struct hw_endpoint* ep);
|
||||
|
||||
#if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
|
||||
static bool e15_is_bulkin_ep(struct hw_endpoint *ep);
|
||||
static bool e15_is_critical_frame_period(struct hw_endpoint *ep);
|
||||
static bool e15_is_bulkin_ep(struct hw_endpoint* ep);
|
||||
static bool e15_is_critical_frame_period(struct hw_endpoint* ep);
|
||||
#else
|
||||
#define e15_is_bulkin_ep(x) (false)
|
||||
#define e15_is_critical_frame_period(x) (false)
|
||||
#endif
|
||||
|
||||
// if usb hardware is in host mode
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool is_host_mode(void)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool is_host_mode(void) {
|
||||
return (usb_hw->main_ctrl & USB_MAIN_CTRL_HOST_NDEVICE_BITS) ? true : false;
|
||||
}
|
||||
|
||||
@@ -62,8 +54,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool is_host_mode(void)
|
||||
// Implementation
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
void rp2040_usb_init(void)
|
||||
{
|
||||
void rp2040_usb_init(void) {
|
||||
// Reset usb controller
|
||||
reset_block(RESETS_RESET_USBCTRL_BITS);
|
||||
unreset_block_wait(RESETS_RESET_USBCTRL_BITS);
|
||||
@@ -88,46 +79,33 @@ void rp2040_usb_init(void)
|
||||
TU_LOG2_INT(sizeof(hw_endpoint_t));
|
||||
}
|
||||
|
||||
void __tusb_irq_path_func(hw_endpoint_reset_transfer)(struct hw_endpoint *ep)
|
||||
{
|
||||
void __tusb_irq_path_func(hw_endpoint_reset_transfer)(struct hw_endpoint* ep) {
|
||||
ep->active = false;
|
||||
ep->remaining_len = 0;
|
||||
ep->xferred_len = 0;
|
||||
ep->user_buf = 0;
|
||||
}
|
||||
|
||||
void __tusb_irq_path_func(_hw_endpoint_buffer_control_update32)(struct hw_endpoint *ep, uint32_t and_mask, uint32_t or_mask)
|
||||
{
|
||||
void __tusb_irq_path_func(_hw_endpoint_buffer_control_update32)(struct hw_endpoint* ep, uint32_t and_mask,
|
||||
uint32_t or_mask) {
|
||||
uint32_t value = 0;
|
||||
|
||||
if ( and_mask )
|
||||
{
|
||||
if (and_mask) {
|
||||
value = *ep->buffer_control & and_mask;
|
||||
}
|
||||
|
||||
if ( or_mask )
|
||||
{
|
||||
if (or_mask) {
|
||||
value |= or_mask;
|
||||
if ( or_mask & USB_BUF_CTRL_AVAIL )
|
||||
{
|
||||
if ( *ep->buffer_control & USB_BUF_CTRL_AVAIL )
|
||||
{
|
||||
panic("ep %d %s was already available", tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]);
|
||||
if (or_mask & USB_BUF_CTRL_AVAIL) {
|
||||
if (*ep->buffer_control & USB_BUF_CTRL_AVAIL) {
|
||||
panic("ep %02X was already available", ep->ep_addr);
|
||||
}
|
||||
*ep->buffer_control = value & ~USB_BUF_CTRL_AVAIL;
|
||||
// 12 cycle delay.. (should be good for 48*12Mhz = 576Mhz)
|
||||
// 4.1.2.5.1 Con-current access: 12 cycles (should be good for 48*12Mhz = 576Mhz) after write to buffer control
|
||||
// Don't need delay in host mode as host is in charge
|
||||
#if !CFG_TUH_ENABLED
|
||||
__asm volatile (
|
||||
"b 1f\n"
|
||||
"1: b 1f\n"
|
||||
"1: b 1f\n"
|
||||
"1: b 1f\n"
|
||||
"1: b 1f\n"
|
||||
"1: b 1f\n"
|
||||
"1:\n"
|
||||
: : : "memory");
|
||||
#endif
|
||||
if ( !is_host_mode()) {
|
||||
busy_wait_at_least_cycles(12);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,10 +113,9 @@ void __tusb_irq_path_func(_hw_endpoint_buffer_control_update32)(struct hw_endpoi
|
||||
}
|
||||
|
||||
// prepare buffer, return buffer control
|
||||
static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint *ep, uint8_t buf_id)
|
||||
{
|
||||
static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint* ep, uint8_t buf_id) {
|
||||
uint16_t const buflen = tu_min16(ep->remaining_len, ep->wMaxPacketSize);
|
||||
ep->remaining_len = (uint16_t)(ep->remaining_len - buflen);
|
||||
ep->remaining_len = (uint16_t) (ep->remaining_len - buflen);
|
||||
|
||||
uint32_t buf_ctrl = buflen | USB_BUF_CTRL_AVAIL;
|
||||
|
||||
@@ -146,10 +123,9 @@ static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint *ep,
|
||||
buf_ctrl |= ep->next_pid ? USB_BUF_CTRL_DATA1_PID : USB_BUF_CTRL_DATA0_PID;
|
||||
ep->next_pid ^= 1u;
|
||||
|
||||
if ( !ep->rx )
|
||||
{
|
||||
if (!ep->rx) {
|
||||
// Copy data from user buffer to hw buffer
|
||||
memcpy(ep->hw_data_buf + buf_id*64, ep->user_buf, buflen);
|
||||
memcpy(ep->hw_data_buf + buf_id * 64, ep->user_buf, buflen);
|
||||
ep->user_buf += buflen;
|
||||
|
||||
// Mark as full
|
||||
@@ -159,8 +135,7 @@ static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint *ep,
|
||||
// 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)
|
||||
{
|
||||
if (ep->remaining_len == 0) {
|
||||
buf_ctrl |= USB_BUF_CTRL_LAST;
|
||||
}
|
||||
|
||||
@@ -170,8 +145,7 @@ static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint *ep,
|
||||
}
|
||||
|
||||
// Prepare buffer control register value
|
||||
void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint *ep)
|
||||
{
|
||||
void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint* ep) {
|
||||
uint32_t ep_ctrl = *ep->endpoint_control;
|
||||
|
||||
// always compute and start with buffer 0
|
||||
@@ -186,8 +160,7 @@ void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint *ep)
|
||||
bool const force_single = (!is_host && !tu_edpt_dir(ep->ep_addr)) ||
|
||||
(is_host && tu_edpt_number(ep->ep_addr) != 0);
|
||||
|
||||
if(ep->remaining_len && !force_single)
|
||||
{
|
||||
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)
|
||||
|
||||
@@ -196,8 +169,7 @@ void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint *ep)
|
||||
// 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
|
||||
{
|
||||
} 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;
|
||||
@@ -212,35 +184,28 @@ void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint *ep)
|
||||
_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)
|
||||
{
|
||||
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 )
|
||||
{
|
||||
if (ep->active) {
|
||||
// TODO: Is this acceptable for interrupt packets?
|
||||
TU_LOG(1, "WARN: starting new transfer on already active ep %d %s\r\n", tu_edpt_number(ep->ep_addr),
|
||||
ep_dir_string[tu_edpt_dir(ep->ep_addr)]);
|
||||
|
||||
TU_LOG(1, "WARN: starting new transfer on already active ep %02X\r\n", 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;
|
||||
ep->xferred_len = 0;
|
||||
ep->active = true;
|
||||
ep->user_buf = buffer;
|
||||
|
||||
if ( e15_is_bulkin_ep(ep) )
|
||||
{
|
||||
if (e15_is_bulkin_ep(ep)) {
|
||||
usb_hw_set->inte = USB_INTS_DEV_SOF_BITS;
|
||||
}
|
||||
|
||||
if ( e15_is_critical_frame_period(ep) )
|
||||
{
|
||||
if (e15_is_critical_frame_period(ep)) {
|
||||
ep->pending = 1;
|
||||
} else
|
||||
{
|
||||
} else {
|
||||
hw_endpoint_start_next_buffer(ep);
|
||||
}
|
||||
|
||||
@@ -248,34 +213,30 @@ void hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, uint16_t to
|
||||
}
|
||||
|
||||
// sync endpoint buffer and return transferred bytes
|
||||
static uint16_t __tusb_irq_path_func(sync_ep_buffer)(struct hw_endpoint *ep, uint8_t buf_id)
|
||||
{
|
||||
static uint16_t __tusb_irq_path_func(sync_ep_buffer)(struct hw_endpoint* ep, uint8_t buf_id) {
|
||||
uint32_t buf_ctrl = _hw_endpoint_buffer_control_get_value32(ep);
|
||||
if (buf_id) buf_ctrl = buf_ctrl >> 16;
|
||||
if (buf_id) buf_ctrl = buf_ctrl >> 16;
|
||||
|
||||
uint16_t xferred_bytes = buf_ctrl & USB_BUF_CTRL_LEN_MASK;
|
||||
|
||||
if ( !ep->rx )
|
||||
{
|
||||
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 = (uint16_t)(ep->xferred_len + xferred_bytes);
|
||||
}else
|
||||
{
|
||||
ep->xferred_len = (uint16_t) (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 = (uint16_t)(ep->xferred_len + xferred_bytes);
|
||||
memcpy(ep->user_buf, ep->hw_data_buf + buf_id * 64, xferred_bytes);
|
||||
ep->xferred_len = (uint16_t) (ep->xferred_len + xferred_bytes);
|
||||
ep->user_buf += xferred_bytes;
|
||||
}
|
||||
|
||||
// Short packet
|
||||
if (xferred_bytes < ep->wMaxPacketSize)
|
||||
{
|
||||
if (xferred_bytes < ep->wMaxPacketSize) {
|
||||
pico_trace(" Short packet on buffer %d with %u bytes\r\n", buf_id, xferred_bytes);
|
||||
// Reduce total length as this is last packet
|
||||
ep->remaining_len = 0;
|
||||
@@ -284,8 +245,7 @@ static uint16_t __tusb_irq_path_func(sync_ep_buffer)(struct hw_endpoint *ep, uin
|
||||
return xferred_bytes;
|
||||
}
|
||||
|
||||
static void __tusb_irq_path_func(_hw_endpoint_xfer_sync) (struct hw_endpoint *ep)
|
||||
{
|
||||
static void __tusb_irq_path_func(_hw_endpoint_xfer_sync)(struct hw_endpoint* ep) {
|
||||
// Update hw endpoint struct with info from hardware
|
||||
// after a buff status interrupt
|
||||
|
||||
@@ -296,14 +256,11 @@ static void __tusb_irq_path_func(_hw_endpoint_xfer_sync) (struct hw_endpoint *ep
|
||||
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)
|
||||
{
|
||||
if ((*ep->endpoint_control) & EP_CTRL_DOUBLE_BUFFERED_BITS) {
|
||||
if (buf0_bytes == ep->wMaxPacketSize) {
|
||||
// sync buffer 1 if not short packet
|
||||
sync_ep_buffer(ep, 1);
|
||||
}else
|
||||
{
|
||||
} else {
|
||||
// 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
|
||||
@@ -335,14 +292,12 @@ static void __tusb_irq_path_func(_hw_endpoint_xfer_sync) (struct hw_endpoint *ep
|
||||
}
|
||||
|
||||
// Returns true if transfer is complete
|
||||
bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint *ep)
|
||||
{
|
||||
bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint* ep) {
|
||||
hw_endpoint_lock_update(ep, 1);
|
||||
|
||||
// Part way through a transfer
|
||||
if (!ep->active)
|
||||
{
|
||||
panic("Can't continue xfer on inactive ep %d %s", tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]);
|
||||
if (!ep->active) {
|
||||
panic("Can't continue xfer on inactive ep %02X", ep->ep_addr);
|
||||
}
|
||||
|
||||
// Update EP struct from hardware state
|
||||
@@ -350,21 +305,15 @@ bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint *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\r\n",
|
||||
ep->xferred_len, tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]);
|
||||
if (ep->remaining_len == 0) {
|
||||
pico_trace("Completed transfer of %d bytes on ep %02X\r\n", ep->xferred_len, ep->ep_addr);
|
||||
// Notify caller we are done so it can notify the tinyusb stack
|
||||
hw_endpoint_lock_update(ep, -1);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( e15_is_critical_frame_period(ep) )
|
||||
{
|
||||
} else {
|
||||
if (e15_is_critical_frame_period(ep)) {
|
||||
ep->pending = 1;
|
||||
} else
|
||||
{
|
||||
} else {
|
||||
hw_endpoint_start_next_buffer(ep);
|
||||
}
|
||||
}
|
||||
@@ -399,16 +348,14 @@ bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint *ep)
|
||||
volatile uint32_t e15_last_sof = 0;
|
||||
|
||||
// check if Errata 15 is needed for this endpoint i.e device bulk-in
|
||||
static bool __tusb_irq_path_func(e15_is_bulkin_ep) (struct hw_endpoint *ep)
|
||||
{
|
||||
static bool __tusb_irq_path_func(e15_is_bulkin_ep)(struct hw_endpoint* ep) {
|
||||
return (!is_host_mode() && tu_edpt_dir(ep->ep_addr) == TUSB_DIR_IN &&
|
||||
ep->transfer_type == TUSB_XFER_BULK);
|
||||
}
|
||||
|
||||
// check if we need to apply Errata 15 workaround : i.e
|
||||
// Endpoint is BULK IN and is currently in critical frame period i.e 20% of last usb frame
|
||||
static bool __tusb_irq_path_func(e15_is_critical_frame_period) (struct hw_endpoint *ep)
|
||||
{
|
||||
static bool __tusb_irq_path_func(e15_is_critical_frame_period)(struct hw_endpoint* ep) {
|
||||
TU_VERIFY(e15_is_bulkin_ep(ep));
|
||||
|
||||
/* Avoid the last 200us (uframe 6.5-7) of a frame, up to the EOF2 point.
|
||||
@@ -419,11 +366,10 @@ static bool __tusb_irq_path_func(e15_is_critical_frame_period) (struct hw_endpoi
|
||||
if (delta < 800 || delta > 998) {
|
||||
return false;
|
||||
}
|
||||
TU_LOG(3, "Avoiding sof %lu now %lu last %lu\r\n", (usb_hw->sof_rd + 1) & USB_SOF_RD_BITS, time_us_32(), e15_last_sof);
|
||||
TU_LOG(3, "Avoiding sof %lu now %lu last %lu\r\n", (usb_hw->sof_rd + 1) & USB_SOF_RD_BITS, time_us_32(),
|
||||
e15_last_sof);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif // TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
|
||||
#endif
|
||||
|
||||
@@ -57,29 +57,22 @@
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO TYPEDEF CONSTANT ENUM
|
||||
//--------------------------------------------------------------------+
|
||||
enum {
|
||||
PIPE_COUNT = 10,
|
||||
};
|
||||
|
||||
/* Start of definition of packed structs (used by the CCRX toolchain) */
|
||||
TU_ATTR_PACKED_BEGIN
|
||||
TU_ATTR_BIT_FIELD_ORDER_BEGIN
|
||||
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
typedef struct {
|
||||
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
|
||||
uint8_t ep; /* an assigned endpoint address */
|
||||
uint8_t ff; /* `buf` is TU_FUFO or POD */
|
||||
} pipe_state_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
pipe_state_t pipe[10];
|
||||
pipe_state_t pipe[PIPE_COUNT];
|
||||
uint8_t ep[2][16]; /* a lookup table for a pipe index from an endpoint address */
|
||||
} dcd_data_t;
|
||||
|
||||
@@ -89,52 +82,44 @@ static dcd_data_t _dcd;
|
||||
// INTERNAL OBJECT & FUNCTION DECLARATION
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Transfer conditions specifiable for each pipe:
|
||||
|
||||
// Transfer conditions specifiable for each pipe for most MCUs
|
||||
// - Pipe 0: Control transfer with 64-byte single buffer
|
||||
// - Pipes 1 and 2: Bulk isochronous transfer continuous transfer mode with programmable buffer size up
|
||||
// to 2 KB and optional double buffer
|
||||
// - Pipes 3 to 5: Bulk transfer continuous transfer mode with programmable buffer size up to 2 KB and
|
||||
// optional double buffer
|
||||
// - Pipes 6 to 9: Interrupt transfer with 64-byte single buffer
|
||||
enum {
|
||||
PIPE_1ST_BULK = 3,
|
||||
PIPE_1ST_INTERRUPT = 6,
|
||||
PIPE_COUNT = 10,
|
||||
};
|
||||
// - Pipes 1 and 2: Bulk or ISO
|
||||
// - Pipes 3 to 5: Bulk
|
||||
// - Pipes 6 to 9: Interrupt
|
||||
//
|
||||
// Note: for small mcu such as
|
||||
// - RA2A1: only pipe 4-7 are available, and no support for ISO
|
||||
static unsigned find_pipe(unsigned xfer_type) {
|
||||
#if defined(BSP_MCU_GROUP_RA2A1)
|
||||
const uint8_t pipe_idx_arr[4][2] = {
|
||||
{ 0, 0 }, // Control
|
||||
{ 0, 0 }, // Isochronous not supported
|
||||
{ 4, 5 }, // Bulk
|
||||
{ 6, 7 }, // Interrupt
|
||||
};
|
||||
#else
|
||||
const uint8_t pipe_idx_arr[4][2] = {
|
||||
{ 0, 0 }, // Control
|
||||
{ 1, 2 }, // Isochronous
|
||||
{ 1, 5 }, // Bulk
|
||||
{ 6, 9 }, // Interrupt
|
||||
};
|
||||
#endif
|
||||
|
||||
static unsigned find_pipe(unsigned xfer)
|
||||
{
|
||||
switch (xfer) {
|
||||
case TUSB_XFER_ISOCHRONOUS:
|
||||
for (int i = 1; i < PIPE_1ST_BULK; ++i) {
|
||||
if (0 == _dcd.pipe[i].ep) return i;
|
||||
}
|
||||
break;
|
||||
// find backward since only pipe 1, 2 support ISO
|
||||
const uint8_t idx_first = pipe_idx_arr[xfer_type][0];
|
||||
const uint8_t idx_last = pipe_idx_arr[xfer_type][1];
|
||||
|
||||
case TUSB_XFER_BULK:
|
||||
for (int i = PIPE_1ST_BULK; i < PIPE_1ST_INTERRUPT; ++i) {
|
||||
if (0 == _dcd.pipe[i].ep) return i;
|
||||
}
|
||||
for (int i = 1; i < PIPE_1ST_BULK; ++i) {
|
||||
if (0 == _dcd.pipe[i].ep) return i;
|
||||
}
|
||||
break;
|
||||
|
||||
case TUSB_XFER_INTERRUPT:
|
||||
for (int i = PIPE_1ST_INTERRUPT; i < PIPE_COUNT; ++i) {
|
||||
if (0 == _dcd.pipe[i].ep) return i;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
/* No support for control transfer */
|
||||
break;
|
||||
for (int i = idx_last; i >= idx_first; i--) {
|
||||
if (0 == _dcd.pipe[i].ep) return i;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static volatile uint16_t* get_pipectr(rusb2_reg_t *rusb, unsigned num)
|
||||
{
|
||||
static volatile uint16_t* get_pipectr(rusb2_reg_t *rusb, unsigned num) {
|
||||
if (num) {
|
||||
return (volatile uint16_t*)&(rusb->PIPE_CTR[num - 1]);
|
||||
} else {
|
||||
@@ -142,8 +127,7 @@ static volatile uint16_t* get_pipectr(rusb2_reg_t *rusb, unsigned num)
|
||||
}
|
||||
}
|
||||
|
||||
static volatile reg_pipetre_t* get_pipetre(rusb2_reg_t *rusb, unsigned num)
|
||||
{
|
||||
static volatile reg_pipetre_t* get_pipetre(rusb2_reg_t *rusb, unsigned num) {
|
||||
volatile reg_pipetre_t* tre = NULL;
|
||||
if ((1 <= num) && (num <= 5)) {
|
||||
tre = (volatile reg_pipetre_t*)&(rusb->PIPE_TR[num - 1].E);
|
||||
@@ -151,8 +135,7 @@ static volatile reg_pipetre_t* get_pipetre(rusb2_reg_t *rusb, unsigned num)
|
||||
return tre;
|
||||
}
|
||||
|
||||
static volatile uint16_t* ep_addr_to_pipectr(uint8_t rhport, unsigned ep_addr)
|
||||
{
|
||||
static volatile uint16_t* ep_addr_to_pipectr(uint8_t rhport, unsigned ep_addr) {
|
||||
rusb2_reg_t *rusb = RUSB2_REG(rhport);
|
||||
const unsigned epn = tu_edpt_number(ep_addr);
|
||||
|
||||
@@ -165,19 +148,16 @@ static volatile uint16_t* ep_addr_to_pipectr(uint8_t rhport, unsigned ep_addr)
|
||||
}
|
||||
}
|
||||
|
||||
static uint16_t edpt0_max_packet_size(rusb2_reg_t* rusb)
|
||||
{
|
||||
static uint16_t edpt0_max_packet_size(rusb2_reg_t* rusb) {
|
||||
return rusb->DCPMAXP_b.MXPS;
|
||||
}
|
||||
|
||||
static uint16_t edpt_max_packet_size(rusb2_reg_t *rusb, unsigned num)
|
||||
{
|
||||
static uint16_t edpt_max_packet_size(rusb2_reg_t *rusb, unsigned num) {
|
||||
rusb->PIPESEL = num;
|
||||
return rusb->PIPEMAXP;
|
||||
}
|
||||
|
||||
static inline void pipe_wait_for_ready(rusb2_reg_t * rusb, unsigned num)
|
||||
{
|
||||
static inline void pipe_wait_for_ready(rusb2_reg_t * rusb, unsigned num) {
|
||||
while ( rusb->D0FIFOSEL_b.CURPIPE != num ) {}
|
||||
while ( !rusb->D0FIFOCTR_b.FRDY ) {}
|
||||
}
|
||||
@@ -266,14 +246,6 @@ static void pipe_read_packet_ff(rusb2_reg_t * rusb, tu_fifo_t *f, volatile void
|
||||
tu_fifo_advance_write_pointer(f, count);
|
||||
}
|
||||
|
||||
|
||||
static bool wait_pipe_fifo_empty(rusb2_reg_t* rusb, uint8_t num) {
|
||||
TU_ASSERT(num);
|
||||
while( (rusb->PIPE_CTR[num-1] & RUSB2_PIPE_CTR_INBUFM_Msk) > 0 ) {}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Pipe Transfer
|
||||
//--------------------------------------------------------------------+
|
||||
@@ -347,7 +319,6 @@ static bool pipe_xfer_in(rusb2_reg_t* rusb, unsigned num)
|
||||
const unsigned rem = pipe->remaining;
|
||||
|
||||
if (!rem) {
|
||||
wait_pipe_fifo_empty(rusb, num);
|
||||
pipe->buf = NULL;
|
||||
return true;
|
||||
}
|
||||
@@ -844,7 +815,7 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
|
||||
}
|
||||
|
||||
rusb->PIPECFG = cfg;
|
||||
rusb->BRDYSTS = 0x1FFu ^ TU_BIT(num);
|
||||
rusb->BRDYSTS = 0x3FFu ^ TU_BIT(num);
|
||||
rusb->BRDYENB |= TU_BIT(num);
|
||||
|
||||
if (dir || (xfer != TUSB_XFER_BULK)) {
|
||||
@@ -944,6 +915,18 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
|
||||
//--------------------------------------------------------------------+
|
||||
// ISR
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#if defined(__CCRX__)
|
||||
TU_ATTR_ALWAYS_INLINE static inline unsigned __builtin_ctz(unsigned int value) {
|
||||
unsigned int count = 0;
|
||||
while ((value & 1) == 0) {
|
||||
value >>= 1;
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
#endif
|
||||
|
||||
void dcd_int_handler(uint8_t rhport)
|
||||
{
|
||||
rusb2_reg_t* rusb = RUSB2_REG(rhport);
|
||||
@@ -1035,17 +1018,7 @@ void dcd_int_handler(uint8_t rhport)
|
||||
/* clear active bits (don't write 0 to already cleared bits according to the HW manual) */
|
||||
rusb->BRDYSTS = ~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);
|
||||
}
|
||||
|
||||
@@ -45,6 +45,9 @@
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
|
||||
//--------------------------------------------------------------------+
|
||||
enum {
|
||||
PIPE_COUNT = 10,
|
||||
};
|
||||
|
||||
TU_ATTR_PACKED_BEGIN
|
||||
TU_ATTR_BIT_FIELD_ORDER_BEGIN
|
||||
@@ -75,7 +78,7 @@ TU_ATTR_BIT_FIELD_ORDER_END
|
||||
typedef struct
|
||||
{
|
||||
bool need_reset; /* The device has not been reset after connection. */
|
||||
pipe_state_t pipe[10];
|
||||
pipe_state_t pipe[PIPE_COUNT];
|
||||
uint8_t ep[4][2][15]; /* a lookup table for a pipe index from an endpoint address */
|
||||
uint8_t ctl_mps[5]; /* EP0 max packet size for each device */
|
||||
} hcd_data_t;
|
||||
@@ -86,46 +89,30 @@ typedef struct
|
||||
static hcd_data_t _hcd;
|
||||
|
||||
// TODO merged with DCD
|
||||
// Transfer conditions specifiable for each pipe:
|
||||
// Transfer conditions specifiable for each pipe for most MCUs
|
||||
// - Pipe 0: Control transfer with 64-byte single buffer
|
||||
// - Pipes 1 and 2: Bulk isochronous transfer continuous transfer mode with programmable buffer size up
|
||||
// to 2 KB and optional double buffer
|
||||
// - Pipes 3 to 5: Bulk transfer continuous transfer mode with programmable buffer size up to 2 KB and
|
||||
// optional double buffer
|
||||
// - Pipes 6 to 9: Interrupt transfer with 64-byte single buffer
|
||||
enum {
|
||||
PIPE_1ST_BULK = 3,
|
||||
PIPE_1ST_INTERRUPT = 6,
|
||||
PIPE_COUNT = 10,
|
||||
};
|
||||
// - Pipes 1 and 2: Bulk or ISO
|
||||
// - Pipes 3 to 5: Bulk
|
||||
// - Pipes 6 to 9: Interrupt
|
||||
//
|
||||
// Note: for small mcu such as
|
||||
// - RA2A1: only pipe 4-7 are available, and no support for ISO
|
||||
static unsigned find_pipe(unsigned xfer_type) {
|
||||
const uint8_t pipe_idx_arr[4][2] = {
|
||||
{ 0, 0 }, // Control
|
||||
{ 1, 2 }, // Isochronous
|
||||
{ 1, 5 }, // Bulk
|
||||
{ 6, 9 }, // Interrupt
|
||||
};
|
||||
|
||||
static unsigned find_pipe(unsigned xfer) {
|
||||
switch ( xfer ) {
|
||||
case TUSB_XFER_ISOCHRONOUS:
|
||||
for (int i = 1; i < PIPE_1ST_BULK; ++i) {
|
||||
if ( 0 == _hcd.pipe[i].ep ) return i;
|
||||
}
|
||||
break;
|
||||
// find backward since only pipe 1, 2 support ISO
|
||||
const uint8_t idx_first = pipe_idx_arr[xfer_type][0];
|
||||
const uint8_t idx_last = pipe_idx_arr[xfer_type][1];
|
||||
|
||||
case TUSB_XFER_BULK:
|
||||
for (int i = PIPE_1ST_BULK; i < PIPE_1ST_INTERRUPT; ++i) {
|
||||
if ( 0 == _hcd.pipe[i].ep ) return i;
|
||||
}
|
||||
for (int i = 1; i < PIPE_1ST_BULK; ++i) {
|
||||
if ( 0 == _hcd.pipe[i].ep ) return i;
|
||||
}
|
||||
break;
|
||||
|
||||
case TUSB_XFER_INTERRUPT:
|
||||
for (int i = PIPE_1ST_INTERRUPT; i < PIPE_COUNT; ++i) {
|
||||
if ( 0 == _hcd.pipe[i].ep ) return i;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
/* No support for control transfer */
|
||||
break;
|
||||
for (int i = idx_last; i >= idx_first; i--) {
|
||||
if (0 == _hcd.pipe[i].ep) return i;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -718,7 +705,7 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const
|
||||
}
|
||||
|
||||
rusb->PIPECFG = cfg;
|
||||
rusb->BRDYSTS = 0x1FFu ^ TU_BIT(num);
|
||||
rusb->BRDYSTS = 0x3FFu ^ TU_BIT(num);
|
||||
rusb->NRDYENB |= TU_BIT(num);
|
||||
rusb->BRDYENB |= TU_BIT(num);
|
||||
|
||||
@@ -771,6 +758,17 @@ bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
|
||||
//--------------------------------------------------------------------+
|
||||
// ISR
|
||||
//--------------------------------------------------------------------+
|
||||
#if defined(__CCRX__)
|
||||
TU_ATTR_ALWAYS_INLINE static inline unsigned __builtin_ctz(unsigned int value) {
|
||||
unsigned int count = 0;
|
||||
while ((value & 1) == 0) {
|
||||
value >>= 1;
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
#endif
|
||||
|
||||
void hcd_int_handler(uint8_t rhport, bool in_isr) {
|
||||
(void) in_isr;
|
||||
|
||||
@@ -820,23 +818,12 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) {
|
||||
}
|
||||
}
|
||||
|
||||
#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};
|
||||
#endif
|
||||
|
||||
if (is0 & RUSB2_INTSTS0_NRDY_Msk) {
|
||||
const unsigned m = rusb->NRDYENB;
|
||||
unsigned s = rusb->NRDYSTS & m;
|
||||
rusb->NRDYSTS = ~s;
|
||||
while (s) {
|
||||
#if defined(__CCRX__)
|
||||
const unsigned num = Mod37BitPosition[(-s & s) % 37];
|
||||
#else
|
||||
const unsigned num = __builtin_ctz(s);
|
||||
#endif
|
||||
process_pipe_nrdy(rhport, num);
|
||||
s &= ~TU_BIT(num);
|
||||
}
|
||||
@@ -847,11 +834,7 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) {
|
||||
/* clear active bits (don't write 0 to already cleared bits according to the HW manual) */
|
||||
rusb->BRDYSTS = ~s;
|
||||
while (s) {
|
||||
#if defined(__CCRX__)
|
||||
const unsigned num = Mod37BitPosition[(-s & s) % 37];
|
||||
#else
|
||||
const unsigned num = __builtin_ctz(s);
|
||||
#endif
|
||||
process_pipe_brdy(rhport, num);
|
||||
s &= ~TU_BIT(num);
|
||||
}
|
||||
|
||||
@@ -102,17 +102,25 @@ static int _dcd_bind(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_
|
||||
usbdev = dev;
|
||||
usbdcd_driver.ep[0] = dev->ep0;
|
||||
|
||||
#ifdef EP_ALLOCREQ
|
||||
// SDK v2
|
||||
usbdcd_driver.req[0] = EP_ALLOCREQ(usbdcd_driver.ep[0]);
|
||||
if (usbdcd_driver.req[0] != NULL)
|
||||
{
|
||||
if (usbdcd_driver.req[0] != NULL) {
|
||||
usbdcd_driver.req[0]->len = 64;
|
||||
usbdcd_driver.req[0]->buf = EP_ALLOCBUFFER(usbdcd_driver.ep[0], 64);
|
||||
if (!usbdcd_driver.req[0]->buf)
|
||||
{
|
||||
if (!usbdcd_driver.req[0]->buf) {
|
||||
EP_FREEREQ(usbdcd_driver.ep[0], usbdcd_driver.req[0]);
|
||||
usbdcd_driver.req[0] = NULL;
|
||||
return ENOMEM;
|
||||
}
|
||||
}
|
||||
#else
|
||||
// SDK v3
|
||||
usbdcd_driver.req[0] = usbdev_allocreq(usbdcd_driver.ep[0], 64);
|
||||
if (usbdcd_driver.req[0] == NULL) {
|
||||
return ENOMEM;
|
||||
}
|
||||
#endif
|
||||
|
||||
usbdcd_driver.req[0]->callback = usbdcd_ep0incomplete;
|
||||
|
||||
@@ -295,13 +303,19 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *p_endpoint_desc)
|
||||
}
|
||||
|
||||
usbdcd_driver.req[epnum] = NULL;
|
||||
|
||||
#ifdef EP_ALLOCREQ
|
||||
// sdk v2
|
||||
usbdcd_driver.req[epnum] = EP_ALLOCREQ(usbdcd_driver.ep[epnum]);
|
||||
if (usbdcd_driver.req[epnum] != NULL)
|
||||
{
|
||||
if (usbdcd_driver.req[epnum] != NULL) {
|
||||
usbdcd_driver.req[epnum]->len = ep_mps;
|
||||
}
|
||||
else
|
||||
{
|
||||
#else
|
||||
// sdk v3
|
||||
usbdcd_driver.req[epnum] = usbdev_allocreq(usbdcd_driver.ep[epnum], ep_mps);
|
||||
#endif
|
||||
|
||||
if(usbdcd_driver.req[epnum] == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,30 +1,32 @@
|
||||
/**
|
||||
* Copyright(c) 2016 STMicroelectronics
|
||||
* Copyright(c) N Conrad
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of STMicroelectronics nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
/*
|
||||
* Copyright(c) 2016 STMicroelectronics
|
||||
* Copyright(c) N Conrad
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of STMicroelectronics nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
// This file contains source copied from ST's HAL, and thus should have their copyright statement.
|
||||
|
||||
@@ -113,6 +115,11 @@
|
||||
#elif CFG_TUSB_MCU == OPT_MCU_STM32H5
|
||||
#include "stm32h5xx.h"
|
||||
#define FSDEV_BUS_32BIT
|
||||
|
||||
#if !defined(USB_DRD_BASE) && defined(USB_DRD_FS_BASE)
|
||||
#define USB_DRD_BASE USB_DRD_FS_BASE
|
||||
#endif
|
||||
|
||||
#define FSDEV_PMA_SIZE (2048u)
|
||||
#undef USB_PMAADDR
|
||||
#define USB_PMAADDR USB_DRD_PMAADDR
|
||||
@@ -138,7 +145,6 @@
|
||||
#define USB_CNTR_LPMODE USB_CNTR_SUSPRDY
|
||||
#define USB_CNTR_FSUSP USB_CNTR_SUSPEN
|
||||
|
||||
|
||||
#elif CFG_TUSB_MCU == OPT_MCU_STM32WB
|
||||
#include "stm32wbxx.h"
|
||||
#define FSDEV_PMA_SIZE (1024u)
|
||||
@@ -182,27 +188,23 @@ typedef uint16_t fsdev_bus_t;
|
||||
// Volatile is also needed to prevent the optimizer from changing access to 32-bit (as 32-bit access is forbidden)
|
||||
static __IO uint16_t * const pma = (__IO uint16_t*)USB_PMAADDR;
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline __IO uint16_t * pcd_btable_word_ptr(USB_TypeDef * USBx, size_t x)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline __IO uint16_t * pcd_btable_word_ptr(USB_TypeDef * USBx, size_t x) {
|
||||
size_t total_word_offset = (((USBx)->BTABLE)>>1) + x;
|
||||
total_word_offset *= FSDEV_PMA_STRIDE;
|
||||
return &(pma[total_word_offset]);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline __IO uint16_t* pcd_ep_tx_cnt_ptr(USB_TypeDef * USBx, uint32_t bEpIdx)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline __IO uint16_t* pcd_ep_tx_cnt_ptr(USB_TypeDef * USBx, uint32_t bEpIdx) {
|
||||
return pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 1u);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline __IO uint16_t* pcd_ep_rx_cnt_ptr(USB_TypeDef * USBx, uint32_t bEpIdx)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline __IO uint16_t* pcd_ep_rx_cnt_ptr(USB_TypeDef * USBx, uint32_t bEpIdx) {
|
||||
return pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 3u);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Aligned buffer size according to hardware */
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint16_t pcd_aligned_buffer_size(uint16_t size)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint16_t pcd_aligned_buffer_size(uint16_t size) {
|
||||
/* The STM32 full speed USB peripheral supports only a limited set of
|
||||
* buffer sizes given by the RX buffer entry format in the USB_BTABLE. */
|
||||
uint16_t blocksize = (size > 62) ? 32 : 2;
|
||||
@@ -213,9 +215,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint16_t pcd_aligned_buffer_size(uint16_t si
|
||||
return numblocks * blocksize;
|
||||
}
|
||||
|
||||
/* SetENDPOINT */
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_endpoint(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wRegValue)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_endpoint(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wRegValue) {
|
||||
#ifdef FSDEV_BUS_32BIT
|
||||
(void) USBx;
|
||||
__O uint32_t *reg = (__O uint32_t *)(USB_DRD_BASE + bEpIdx*4);
|
||||
@@ -226,7 +226,6 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_endpoint(USB_TypeDef * USBx, ui
|
||||
#endif
|
||||
}
|
||||
|
||||
/* GetENDPOINT */
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_endpoint(USB_TypeDef * USBx, uint32_t bEpIdx) {
|
||||
#ifdef FSDEV_BUS_32BIT
|
||||
(void) USBx;
|
||||
@@ -237,8 +236,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_endpoint(USB_TypeDef * USBx
|
||||
return *reg;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_eptype(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wType)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_eptype(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wType) {
|
||||
uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
|
||||
regVal &= (uint32_t)USB_EP_T_MASK;
|
||||
regVal |= wType;
|
||||
@@ -246,20 +244,19 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_eptype(USB_TypeDef * USBx, uint
|
||||
pcd_set_endpoint(USBx, bEpIdx, regVal);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_eptype(USB_TypeDef * USBx, uint32_t bEpIdx)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_eptype(USB_TypeDef * USBx, uint32_t bEpIdx) {
|
||||
uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
|
||||
regVal &= USB_EP_T_FIELD;
|
||||
return regVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clears bit CTR_RX / CTR_TX in the endpoint register.
|
||||
* @param USBx USB peripheral instance register address.
|
||||
* @param bEpIdx Endpoint Number.
|
||||
* @retval None
|
||||
*/
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_rx_ep_ctr(USB_TypeDef * USBx, uint32_t bEpIdx)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_rx_ep_ctr(USB_TypeDef * USBx, uint32_t bEpIdx) {
|
||||
uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
|
||||
regVal &= USB_EPREG_MASK;
|
||||
regVal &= ~USB_EP_CTR_RX;
|
||||
@@ -267,22 +264,21 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_rx_ep_ctr(USB_TypeDef * USBx,
|
||||
pcd_set_endpoint(USBx, bEpIdx, regVal);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_tx_ep_ctr(USB_TypeDef * USBx, uint32_t bEpIdx)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_tx_ep_ctr(USB_TypeDef * USBx, uint32_t bEpIdx) {
|
||||
uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
|
||||
regVal &= USB_EPREG_MASK;
|
||||
regVal &= ~USB_EP_CTR_TX;
|
||||
regVal |= USB_EP_CTR_RX; // preserve CTR_RX (clears on writing 0)
|
||||
pcd_set_endpoint(USBx, bEpIdx,regVal);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief gets counter of the tx buffer.
|
||||
* @param USBx USB peripheral instance register address.
|
||||
* @param bEpIdx Endpoint Number.
|
||||
* @retval Counter value
|
||||
*/
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_tx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_tx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx) {
|
||||
#ifdef FSDEV_BUS_32BIT
|
||||
(void) USBx;
|
||||
return (pma32[2*bEpIdx] & 0x03FF0000) >> 16;
|
||||
@@ -292,8 +288,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_tx_cnt(USB_TypeDef * USB
|
||||
#endif
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx) {
|
||||
#ifdef FSDEV_BUS_32BIT
|
||||
(void) USBx;
|
||||
return (pma32[2*bEpIdx + 1] & 0x03FF0000) >> 16;
|
||||
@@ -303,6 +298,9 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_cnt(USB_TypeDef * USB
|
||||
#endif
|
||||
}
|
||||
|
||||
#define pcd_get_ep_dbuf0_cnt pcd_get_ep_tx_cnt
|
||||
#define pcd_get_ep_dbuf1_cnt pcd_get_ep_rx_cnt
|
||||
|
||||
/**
|
||||
* @brief Sets address in an endpoint register.
|
||||
* @param USBx USB peripheral instance register address.
|
||||
@@ -310,8 +308,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_cnt(USB_TypeDef * USB
|
||||
* @param bAddr Address.
|
||||
* @retval None
|
||||
*/
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_address(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t bAddr)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_address(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t bAddr) {
|
||||
uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
|
||||
regVal &= USB_EPREG_MASK;
|
||||
regVal |= bAddr;
|
||||
@@ -319,8 +316,7 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_address(USB_TypeDef * USBx,
|
||||
pcd_set_endpoint(USBx, bEpIdx,regVal);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_tx_address(USB_TypeDef * USBx, uint32_t bEpIdx)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_tx_address(USB_TypeDef * USBx, uint32_t bEpIdx) {
|
||||
#ifdef FSDEV_BUS_32BIT
|
||||
(void) USBx;
|
||||
return pma32[2*bEpIdx] & 0x0000FFFFu ;
|
||||
@@ -329,8 +325,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_tx_address(USB_TypeDef *
|
||||
#endif
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_address(USB_TypeDef * USBx, uint32_t bEpIdx)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_address(USB_TypeDef * USBx, uint32_t bEpIdx) {
|
||||
#ifdef FSDEV_BUS_32BIT
|
||||
(void) USBx;
|
||||
return pma32[2*bEpIdx + 1] & 0x0000FFFFu;
|
||||
@@ -339,8 +334,10 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_address(USB_TypeDef *
|
||||
#endif
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_address(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t addr)
|
||||
{
|
||||
#define pcd_get_ep_dbuf0_address pcd_get_ep_tx_address
|
||||
#define pcd_get_ep_dbuf1_address pcd_get_ep_rx_address
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_address(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t addr) {
|
||||
#ifdef FSDEV_BUS_32BIT
|
||||
(void) USBx;
|
||||
pma32[2*bEpIdx] = (pma32[2*bEpIdx] & 0xFFFF0000u) | (addr & 0x0000FFFCu);
|
||||
@@ -349,8 +346,7 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_address(USB_TypeDef * USB
|
||||
#endif
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_address(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t addr)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_address(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t addr) {
|
||||
#ifdef FSDEV_BUS_32BIT
|
||||
(void) USBx;
|
||||
pma32[2*bEpIdx + 1] = (pma32[2*bEpIdx + 1] & 0xFFFF0000u) | (addr & 0x0000FFFCu);
|
||||
@@ -359,8 +355,10 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_address(USB_TypeDef * USB
|
||||
#endif
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount)
|
||||
{
|
||||
#define pcd_set_ep_dbuf0_address pcd_set_ep_tx_address
|
||||
#define pcd_set_ep_dbuf1_address pcd_set_ep_rx_address
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount) {
|
||||
#ifdef FSDEV_BUS_32BIT
|
||||
(void) USBx;
|
||||
pma32[2*bEpIdx] = (pma32[2*bEpIdx] & ~0x03FF0000u) | ((wCount & 0x3FFu) << 16);
|
||||
@@ -370,8 +368,9 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_cnt(USB_TypeDef * USBx, u
|
||||
#endif
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount)
|
||||
{
|
||||
#define pcd_set_ep_tx_dbuf0_cnt pcd_set_ep_tx_cnt
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_dbuf1_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount) {
|
||||
#ifdef FSDEV_BUS_32BIT
|
||||
(void) USBx;
|
||||
pma32[2*bEpIdx + 1] = (pma32[2*bEpIdx + 1] & ~0x03FF0000u) | ((wCount & 0x3FFu) << 16);
|
||||
@@ -381,8 +380,8 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_cnt(USB_TypeDef * USBx, u
|
||||
#endif
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_blsize_num_blocks(USB_TypeDef * USBx, uint32_t rxtx_idx, uint32_t blocksize, uint32_t numblocks)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_blsize_num_blocks(USB_TypeDef * USBx, uint32_t rxtx_idx,
|
||||
uint32_t blocksize, uint32_t numblocks) {
|
||||
/* Encode into register. When BLSIZE==1, we need to subtract 1 block count */
|
||||
#ifdef FSDEV_BUS_32BIT
|
||||
(void) USBx;
|
||||
@@ -393,8 +392,7 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_blsize_num_blocks(USB_TypeDe
|
||||
#endif
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_bufsize(USB_TypeDef * USBx, uint32_t rxtx_idx, uint32_t wCount)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_bufsize(USB_TypeDef * USBx, uint32_t rxtx_idx, uint32_t wCount) {
|
||||
wCount = pcd_aligned_buffer_size(wCount);
|
||||
|
||||
/* We assume that the buffer size is already aligned to hardware requirements. */
|
||||
@@ -408,16 +406,16 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_bufsize(USB_TypeDef * USBx,
|
||||
pcd_set_ep_blsize_num_blocks(USBx, rxtx_idx, blocksize, numblocks);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_bufsize(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_dbuf0_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount) {
|
||||
pcd_set_ep_bufsize(USBx, 2*bEpIdx, wCount);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_bufsize(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount) {
|
||||
pcd_set_ep_bufsize(USBx, 2*bEpIdx + 1, wCount);
|
||||
}
|
||||
|
||||
#define pcd_set_ep_rx_dbuf1_cnt pcd_set_ep_rx_cnt
|
||||
|
||||
/**
|
||||
* @brief sets the status for tx transfer (bits STAT_TX[1:0]).
|
||||
* @param USBx USB peripheral instance register address.
|
||||
@@ -425,8 +423,7 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_bufsize(USB_TypeDef * USB
|
||||
* @param wState new state
|
||||
* @retval None
|
||||
*/
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_status(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wState)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_status(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wState) {
|
||||
uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
|
||||
regVal &= USB_EPTX_DTOGMASK;
|
||||
|
||||
@@ -443,7 +440,7 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_status(USB_TypeDef * USBx
|
||||
|
||||
regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX;
|
||||
pcd_set_endpoint(USBx, bEpIdx, regVal);
|
||||
} /* pcd_set_ep_tx_status */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief sets the status for rx transfer (bits STAT_TX[1:0])
|
||||
@@ -453,31 +450,27 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_status(USB_TypeDef * USBx
|
||||
* @retval None
|
||||
*/
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_status(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wState)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_status(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wState) {
|
||||
uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
|
||||
regVal &= USB_EPRX_DTOGMASK;
|
||||
|
||||
/* toggle first bit ? */
|
||||
if((USB_EPRX_DTOG1 & wState)!= 0U)
|
||||
{
|
||||
if((USB_EPRX_DTOG1 & wState)!= 0U) {
|
||||
regVal ^= USB_EPRX_DTOG1;
|
||||
}
|
||||
/* toggle second bit ? */
|
||||
if((USB_EPRX_DTOG2 & wState)!= 0U)
|
||||
{
|
||||
if((USB_EPRX_DTOG2 & wState)!= 0U) {
|
||||
regVal ^= USB_EPRX_DTOG2;
|
||||
}
|
||||
|
||||
regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX;
|
||||
pcd_set_endpoint(USBx, bEpIdx, regVal);
|
||||
} /* pcd_set_ep_rx_status */
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_status(USB_TypeDef * USBx, uint32_t bEpIdx)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_status(USB_TypeDef * USBx, uint32_t bEpIdx) {
|
||||
uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
|
||||
return (regVal & USB_EPRX_STAT) >> (12u);
|
||||
} /* pcd_get_ep_rx_status */
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@@ -486,16 +479,14 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_status(USB_TypeDef *
|
||||
* @param bEpIdx Endpoint Number.
|
||||
* @retval None
|
||||
*/
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_rx_dtog(USB_TypeDef * USBx, uint32_t bEpIdx)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_rx_dtog(USB_TypeDef * USBx, uint32_t bEpIdx) {
|
||||
uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
|
||||
regVal &= USB_EPREG_MASK;
|
||||
regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX|USB_EP_DTOG_RX;
|
||||
pcd_set_endpoint(USBx, bEpIdx, regVal);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_tx_dtog(USB_TypeDef * USBx, uint32_t bEpIdx)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_tx_dtog(USB_TypeDef * USBx, uint32_t bEpIdx) {
|
||||
uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
|
||||
regVal &= USB_EPREG_MASK;
|
||||
regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX|USB_EP_DTOG_TX;
|
||||
@@ -508,21 +499,16 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_tx_dtog(USB_TypeDef * USBx, uint32
|
||||
* @param bEpIdx Endpoint Number.
|
||||
* @retval None
|
||||
*/
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_rx_dtog(USB_TypeDef * USBx, uint32_t bEpIdx)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_rx_dtog(USB_TypeDef * USBx, uint32_t bEpIdx) {
|
||||
uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
|
||||
if((regVal & USB_EP_DTOG_RX) != 0)
|
||||
{
|
||||
if((regVal & USB_EP_DTOG_RX) != 0) {
|
||||
pcd_rx_dtog(USBx,bEpIdx);
|
||||
}
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_tx_dtog(USB_TypeDef * USBx, uint32_t bEpIdx)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_tx_dtog(USB_TypeDef * USBx, uint32_t bEpIdx) {
|
||||
uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
|
||||
if((regVal & USB_EP_DTOG_TX) != 0)
|
||||
{
|
||||
if((regVal & USB_EP_DTOG_TX) != 0) {
|
||||
pcd_tx_dtog(USBx,bEpIdx);
|
||||
}
|
||||
}
|
||||
@@ -533,17 +519,15 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_tx_dtog(USB_TypeDef * USBx,
|
||||
* @param bEpIdx Endpoint Number.
|
||||
* @retval None
|
||||
*/
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_kind(USB_TypeDef * USBx, uint32_t bEpIdx)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_kind(USB_TypeDef * USBx, uint32_t bEpIdx) {
|
||||
uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
|
||||
regVal |= USB_EP_KIND;
|
||||
regVal &= USB_EPREG_MASK;
|
||||
regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX;
|
||||
pcd_set_endpoint(USBx, bEpIdx, regVal);
|
||||
}
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_ep_kind(USB_TypeDef * USBx, uint32_t bEpIdx)
|
||||
{
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_ep_kind(USB_TypeDef * USBx, uint32_t bEpIdx) {
|
||||
uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
|
||||
regVal &= USB_EPKIND_MASK;
|
||||
regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX;
|
||||
@@ -35,7 +35,9 @@
|
||||
#include <f1c100s-irq.h>
|
||||
#include <device/dcd.h>
|
||||
#include "musb_def.h"
|
||||
#include "bsp/board.h"
|
||||
|
||||
//#include "bsp/board_api.h"
|
||||
extern uint32_t board_millis(void); // TODO remove
|
||||
|
||||
typedef uint32_t u32;
|
||||
typedef uint16_t u16;
|
||||
@@ -58,7 +60,7 @@ typedef struct TU_ATTR_PACKED
|
||||
|
||||
typedef struct
|
||||
{
|
||||
tusb_control_request_t setup_packet;
|
||||
CFG_TUD_MEM_ALIGN tusb_control_request_t setup_packet;
|
||||
uint16_t remaining_ctrl; /* The number of bytes remaining in data stage of control transfer. */
|
||||
int8_t status_out;
|
||||
pipe_state_t pipe0;
|
||||
@@ -350,7 +352,7 @@ static void USBC_INT_DisableRxEp(u8 ep_index)
|
||||
* INTERNAL FUNCTION DECLARATION
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
static dcd_data_t _dcd;
|
||||
CFG_TUD_MEM_ALIGN static dcd_data_t _dcd;
|
||||
|
||||
static inline free_block_t *find_containing_block(free_block_t *beg, free_block_t *end, uint_fast16_t addr)
|
||||
{
|
||||
@@ -560,7 +562,7 @@ static void pipe_read_write_packet_ff(tu_fifo_t *f, volatile void *fifo, unsigne
|
||||
|
||||
static void process_setup_packet(uint8_t rhport)
|
||||
{
|
||||
uint32_t *p = (uint32_t*)&_dcd.setup_packet;
|
||||
uint32_t *p = (uint32_t*)(uintptr_t) &_dcd.setup_packet;
|
||||
p[0] = USBC_Readl(USBC_REG_EPFIFO0(USBC0_BASE));
|
||||
p[1] = USBC_Readl(USBC_REG_EPFIFO0(USBC0_BASE));
|
||||
|
||||
@@ -594,7 +596,7 @@ static bool handle_xfer_in(uint_fast8_t ep_addr)
|
||||
if (len) {
|
||||
volatile void* addr = (volatile void*)(USBC_REG_EPFIFO1(USBC0_BASE) + (epnum_minus1 << 2));
|
||||
if (_dcd.pipe_buf_is_fifo[TUSB_DIR_IN] & TU_BIT(epnum_minus1)) {
|
||||
pipe_read_write_packet_ff((tu_fifo_t *)buf, addr, len, TUSB_DIR_IN);
|
||||
pipe_read_write_packet_ff((tu_fifo_t *)(uintptr_t) buf, addr, len, TUSB_DIR_IN);
|
||||
} else {
|
||||
pipe_write_packet(buf, addr, len);
|
||||
pipe->buf = buf + len;
|
||||
@@ -622,7 +624,7 @@ static bool handle_xfer_out(uint_fast8_t ep_addr)
|
||||
if (len) {
|
||||
volatile void* addr = (volatile void*)(USBC_REG_EPFIFO1(USBC0_BASE) + (epnum_minus1 << 2));
|
||||
if (_dcd.pipe_buf_is_fifo[TUSB_DIR_OUT] & TU_BIT(epnum_minus1)) {
|
||||
pipe_read_write_packet_ff((tu_fifo_t *)buf, addr, len, TUSB_DIR_OUT);
|
||||
pipe_read_write_packet_ff((tu_fifo_t *)(uintptr_t )buf, addr, len, TUSB_DIR_OUT);
|
||||
} else {
|
||||
pipe_read_packet(buf, addr, len);
|
||||
pipe->buf = buf + len;
|
||||
|
||||
@@ -97,28 +97,170 @@ static uint16_t ep0_pending[2]; // Index determines direction as t
|
||||
|
||||
// TX FIFO RAM allocation so far in words - RX FIFO size is readily available from dwc2->grxfsiz
|
||||
static uint16_t _allocated_fifo_words_tx; // TX FIFO size in words (IN EPs)
|
||||
static bool _out_ep_closed; // Flag to check if RX FIFO size needs an update (reduce its size)
|
||||
|
||||
// SOF enabling flag - required for SOF to not get disabled in ISR when SOF was enabled by
|
||||
static bool _sof_en;
|
||||
|
||||
// Calculate the RX FIFO size according to recommendations from reference manual
|
||||
// Calculate the RX FIFO size according to minimum recommendations from reference manual
|
||||
// RxFIFO = (5 * number of control endpoints + 8) +
|
||||
// ((largest USB packet used / 4) + 1 for status information) +
|
||||
// (2 * number of OUT endpoints) + 1 for Global NAK
|
||||
// with number of control endpoints = 1 we have
|
||||
// RxFIFO = 15 + (largest USB packet used / 4) + 2 * number of OUT endpoints
|
||||
// we double the largest USB packet size to be able to hold up to 2 packets
|
||||
static inline uint16_t calc_grxfsiz(uint16_t max_ep_size, uint8_t ep_count) {
|
||||
return 15 + 2 * (max_ep_size / 4) + 2 * ep_count;
|
||||
}
|
||||
|
||||
static void update_grxfsiz(uint8_t rhport) {
|
||||
TU_ATTR_ALWAYS_INLINE static inline void fifo_flush_tx(dwc2_regs_t* dwc2, uint8_t epnum) {
|
||||
// flush TX fifo and wait for it cleared
|
||||
dwc2->grstctl = GRSTCTL_TXFFLSH | (epnum << GRSTCTL_TXFNUM_Pos);
|
||||
while (dwc2->grstctl & GRSTCTL_TXFFLSH_Msk) {}
|
||||
}
|
||||
TU_ATTR_ALWAYS_INLINE static inline void fifo_flush_rx(dwc2_regs_t* dwc2) {
|
||||
// flush RX fifo and wait for it cleared
|
||||
dwc2->grstctl = GRSTCTL_RXFFLSH;
|
||||
while (dwc2->grstctl & GRSTCTL_RXFFLSH_Msk) {}
|
||||
}
|
||||
|
||||
static bool fifo_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t packet_size) {
|
||||
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
|
||||
uint8_t const ep_count = _dwc2_controller[rhport].ep_count;
|
||||
uint8_t const epnum = tu_edpt_number(ep_addr);
|
||||
uint8_t const dir = tu_edpt_dir(ep_addr);
|
||||
|
||||
// Determine largest EP size for RX FIFO
|
||||
uint16_t max_epsize = 0;
|
||||
for (uint8_t epnum = 0; epnum < ep_count; epnum++) {
|
||||
max_epsize = tu_max16(max_epsize, xfer_status[epnum][TUSB_DIR_OUT].max_size);
|
||||
TU_ASSERT(epnum < ep_count);
|
||||
|
||||
uint16_t fifo_size = tu_div_ceil(packet_size, 4);
|
||||
|
||||
// "USB Data FIFOs" section in reference manual
|
||||
// Peripheral FIFO architecture
|
||||
//
|
||||
// --------------- 320 or 1024 ( 1280 or 4096 bytes )
|
||||
// | IN FIFO 0 |
|
||||
// --------------- (320 or 1024) - 16
|
||||
// | IN FIFO 1 |
|
||||
// --------------- (320 or 1024) - 16 - x
|
||||
// | . . . . |
|
||||
// --------------- (320 or 1024) - 16 - x - y - ... - z
|
||||
// | IN FIFO MAX |
|
||||
// ---------------
|
||||
// | FREE |
|
||||
// --------------- GRXFSIZ
|
||||
// | OUT FIFO |
|
||||
// | ( Shared ) |
|
||||
// --------------- 0
|
||||
//
|
||||
// In FIFO is allocated by following rules:
|
||||
// - IN EP 1 gets FIFO 1, IN EP "n" gets FIFO "n".
|
||||
if (dir == TUSB_DIR_OUT) {
|
||||
// Calculate required size of RX FIFO
|
||||
uint16_t const sz = calc_grxfsiz(4 * fifo_size, ep_count);
|
||||
|
||||
// If size_rx needs to be extended check if possible and if so enlarge it
|
||||
if (dwc2->grxfsiz < sz) {
|
||||
TU_ASSERT(sz + _allocated_fifo_words_tx <= _dwc2_controller[rhport].ep_fifo_size / 4);
|
||||
|
||||
// Enlarge RX FIFO
|
||||
dwc2->grxfsiz = sz;
|
||||
}
|
||||
} else {
|
||||
// Note if The TXFELVL is configured as half empty. In order
|
||||
// to be able to write a packet at that point, the fifo must be twice the max_size.
|
||||
if ((dwc2->gahbcfg & GAHBCFG_TXFELVL) == 0) {
|
||||
fifo_size *= 2;
|
||||
}
|
||||
|
||||
// Check if free space is available
|
||||
TU_ASSERT(_allocated_fifo_words_tx + fifo_size + dwc2->grxfsiz <= _dwc2_controller[rhport].ep_fifo_size / 4);
|
||||
_allocated_fifo_words_tx += fifo_size;
|
||||
TU_LOG(DWC2_DEBUG, " Allocated %u bytes at offset %" PRIu32, fifo_size * 4,
|
||||
_dwc2_controller[rhport].ep_fifo_size - _allocated_fifo_words_tx * 4);
|
||||
|
||||
// DIEPTXF starts at FIFO #1.
|
||||
// Both TXFD and TXSA are in unit of 32-bit words.
|
||||
dwc2->dieptxf[epnum - 1] = (fifo_size << DIEPTXF_INEPTXFD_Pos) |
|
||||
(_dwc2_controller[rhport].ep_fifo_size / 4 - _allocated_fifo_words_tx);
|
||||
}
|
||||
|
||||
// Update size of RX FIFO
|
||||
dwc2->grxfsiz = calc_grxfsiz(max_epsize, ep_count);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void edpt_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) {
|
||||
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
|
||||
uint8_t const epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress);
|
||||
uint8_t const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress);
|
||||
|
||||
xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, dir);
|
||||
xfer->max_size = tu_edpt_packet_size(p_endpoint_desc);
|
||||
xfer->interval = p_endpoint_desc->bInterval;
|
||||
|
||||
// USBAEP, EPTYP, SD0PID_SEVNFRM, MPSIZ are the same for IN and OUT endpoints.
|
||||
uint32_t const dxepctl = (1 << DOEPCTL_USBAEP_Pos) |
|
||||
(p_endpoint_desc->bmAttributes.xfer << DOEPCTL_EPTYP_Pos) |
|
||||
(p_endpoint_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? DOEPCTL_SD0PID_SEVNFRM : 0) |
|
||||
(xfer->max_size << DOEPCTL_MPSIZ_Pos);
|
||||
|
||||
if (dir == TUSB_DIR_OUT) {
|
||||
dwc2->epout[epnum].doepctl = dxepctl;
|
||||
dwc2->daintmsk |= TU_BIT(DAINTMSK_OEPM_Pos + epnum);
|
||||
} else {
|
||||
dwc2->epin[epnum].diepctl = dxepctl | (epnum << DIEPCTL_TXFNUM_Pos);
|
||||
dwc2->daintmsk |= (1 << (DAINTMSK_IEPM_Pos + epnum));
|
||||
}
|
||||
}
|
||||
|
||||
static void edpt_disable(uint8_t rhport, uint8_t ep_addr, bool stall) {
|
||||
(void) rhport;
|
||||
|
||||
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
|
||||
uint8_t const epnum = tu_edpt_number(ep_addr);
|
||||
uint8_t const dir = tu_edpt_dir(ep_addr);
|
||||
|
||||
if (dir == TUSB_DIR_IN) {
|
||||
dwc2_epin_t* epin = dwc2->epin;
|
||||
|
||||
// Only disable currently enabled non-control endpoint
|
||||
if ((epnum == 0) || !(epin[epnum].diepctl & DIEPCTL_EPENA)) {
|
||||
epin[epnum].diepctl |= DIEPCTL_SNAK | (stall ? DIEPCTL_STALL : 0);
|
||||
} else {
|
||||
// Stop transmitting packets and NAK IN xfers.
|
||||
epin[epnum].diepctl |= DIEPCTL_SNAK;
|
||||
while ((epin[epnum].diepint & DIEPINT_INEPNE) == 0) {}
|
||||
|
||||
// Disable the endpoint.
|
||||
epin[epnum].diepctl |= DIEPCTL_EPDIS | (stall ? DIEPCTL_STALL : 0);
|
||||
while ((epin[epnum].diepint & DIEPINT_EPDISD_Msk) == 0) {}
|
||||
|
||||
epin[epnum].diepint = DIEPINT_EPDISD;
|
||||
}
|
||||
|
||||
// Flush the FIFO, and wait until we have confirmed it cleared.
|
||||
fifo_flush_tx(dwc2, epnum);
|
||||
} else {
|
||||
dwc2_epout_t* epout = dwc2->epout;
|
||||
|
||||
// Only disable currently enabled non-control endpoint
|
||||
if ((epnum == 0) || !(epout[epnum].doepctl & DOEPCTL_EPENA)) {
|
||||
epout[epnum].doepctl |= stall ? DOEPCTL_STALL : 0;
|
||||
} else {
|
||||
// Asserting GONAK is required to STALL an OUT endpoint.
|
||||
// Simpler to use polling here, we don't use the "B"OUTNAKEFF interrupt
|
||||
// anyway, and it can't be cleared by user code. If this while loop never
|
||||
// finishes, we have bigger problems than just the stack.
|
||||
dwc2->dctl |= DCTL_SGONAK;
|
||||
while ((dwc2->gintsts & GINTSTS_BOUTNAKEFF_Msk) == 0) {}
|
||||
|
||||
// Ditto here- disable the endpoint.
|
||||
epout[epnum].doepctl |= DOEPCTL_EPDIS | (stall ? DOEPCTL_STALL : 0);
|
||||
while ((epout[epnum].doepint & DOEPINT_EPDISD_Msk) == 0) {}
|
||||
|
||||
epout[epnum].doepint = DOEPINT_EPDISD;
|
||||
|
||||
// Allow other OUT endpoints to keep receiving.
|
||||
dwc2->dctl |= DCTL_CGONAK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start of Bus Reset
|
||||
@@ -127,7 +269,6 @@ static void bus_reset(uint8_t rhport) {
|
||||
uint8_t const ep_count = _dwc2_controller[rhport].ep_count;
|
||||
|
||||
tu_memclr(xfer_status, sizeof(xfer_status));
|
||||
_out_ep_closed = false;
|
||||
|
||||
_sof_en = false;
|
||||
|
||||
@@ -139,7 +280,17 @@ static void bus_reset(uint8_t rhport) {
|
||||
dwc2->epout[n].doepctl |= DOEPCTL_SNAK;
|
||||
}
|
||||
|
||||
// 2. Set up interrupt mask
|
||||
// 2. Disable all IN endpoints
|
||||
for (uint8_t n = 0; n < ep_count; n++) {
|
||||
if (dwc2->epin[n].diepctl & DIEPCTL_EPENA) {
|
||||
dwc2->epin[n].diepctl |= DIEPCTL_SNAK | DIEPCTL_EPDIS;
|
||||
}
|
||||
}
|
||||
|
||||
fifo_flush_tx(dwc2, 0x10); // all tx fifo
|
||||
fifo_flush_rx(dwc2);
|
||||
|
||||
// 3. Set up interrupt mask
|
||||
dwc2->daintmsk = TU_BIT(DAINTMSK_OEPM_Pos) | TU_BIT(DAINTMSK_IEPM_Pos);
|
||||
dwc2->doepmsk = DOEPMSK_STUPM | DOEPMSK_XFRCM;
|
||||
dwc2->diepmsk = DIEPMSK_TOM | DIEPMSK_XFRCM;
|
||||
@@ -269,18 +420,16 @@ static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t c
|
||||
/* Controller API
|
||||
*------------------------------------------------------------------*/
|
||||
#if CFG_TUSB_DEBUG >= DWC2_DEBUG
|
||||
|
||||
void print_dwc2_info(dwc2_regs_t* dwc2) {
|
||||
// print guid, gsnpsid, ghwcfg1, ghwcfg2, ghwcfg3, ghwcfg4
|
||||
// use dwc2_info.py/md for bit-field value and comparison with other ports
|
||||
volatile uint32_t const* p = (volatile uint32_t const*) &dwc2->guid;
|
||||
TU_LOG(DWC2_DEBUG, "guid, gsnpsid, ghwcfg1, ghwcfg2, ghwcfg3, ghwcfg4\r\n");
|
||||
for (size_t i = 0; i < 5; i++) {
|
||||
TU_LOG(DWC2_DEBUG, "0x%08lX, ", p[i]);
|
||||
TU_LOG(DWC2_DEBUG, "0x%08" PRIX32 ", ", p[i]);
|
||||
}
|
||||
TU_LOG(DWC2_DEBUG, "0x%08lX\r\n", p[5]);
|
||||
TU_LOG(DWC2_DEBUG, "0x%08" PRIX32 "\r\n", p[5]);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void reset_core(dwc2_regs_t* dwc2) {
|
||||
@@ -298,11 +447,15 @@ static void reset_core(dwc2_regs_t* dwc2) {
|
||||
}
|
||||
|
||||
static bool phy_hs_supported(dwc2_regs_t* dwc2) {
|
||||
// note: esp32 incorrect report its hs_phy_type as utmi
|
||||
(void) dwc2;
|
||||
|
||||
#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
|
||||
// note: esp32 incorrect report its hs_phy_type as utmi
|
||||
return false;
|
||||
#elif !TUD_OPT_HIGH_SPEED
|
||||
return false;
|
||||
#else
|
||||
return TUD_OPT_HIGH_SPEED && dwc2->ghwcfg2_bm.hs_phy_type != HS_PHY_TYPE_NONE;
|
||||
return dwc2->ghwcfg2_bm.hs_phy_type != HS_PHY_TYPE_NONE;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -447,13 +600,8 @@ void dcd_init(uint8_t rhport) {
|
||||
// (non zero-length packet), send STALL back and discard.
|
||||
dwc2->dcfg |= DCFG_NZLSOHSK;
|
||||
|
||||
// flush all TX fifo and wait for it cleared
|
||||
dwc2->grstctl = GRSTCTL_TXFFLSH | (0x10u << GRSTCTL_TXFNUM_Pos);
|
||||
while (dwc2->grstctl & GRSTCTL_TXFFLSH_Msk) {}
|
||||
|
||||
// flush RX fifo and wait for it cleared
|
||||
dwc2->grstctl = GRSTCTL_RXFFLSH;
|
||||
while (dwc2->grstctl & GRSTCTL_RXFFLSH_Msk) {}
|
||||
fifo_flush_tx(dwc2, 0x10); // all tx fifo
|
||||
fifo_flush_rx(dwc2);
|
||||
|
||||
// Clear all interrupts
|
||||
uint32_t int_mask = dwc2->gintsts;
|
||||
@@ -462,11 +610,12 @@ void dcd_init(uint8_t rhport) {
|
||||
dwc2->gotgint |= int_mask;
|
||||
|
||||
// Required as part of core initialization.
|
||||
// TODO: How should mode mismatch be handled? It will cause
|
||||
// the core to stop working/require reset.
|
||||
dwc2->gintmsk = GINTMSK_OTGINT | GINTMSK_MMISM | GINTMSK_RXFLVLM |
|
||||
dwc2->gintmsk = GINTMSK_OTGINT | GINTMSK_RXFLVLM |
|
||||
GINTMSK_USBSUSPM | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM;
|
||||
|
||||
// Configure TX FIFO empty level for interrupt. Default is complete empty
|
||||
dwc2->gahbcfg |= GAHBCFG_TXFELVL;
|
||||
|
||||
// Enable global interrupt
|
||||
dwc2->gahbcfg |= GAHBCFG_GINT;
|
||||
|
||||
@@ -547,84 +696,8 @@ void dcd_sof_enable(uint8_t rhport, bool en) {
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_edpt) {
|
||||
(void) rhport;
|
||||
|
||||
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
|
||||
uint8_t const ep_count = _dwc2_controller[rhport].ep_count;
|
||||
|
||||
uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress);
|
||||
uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress);
|
||||
|
||||
TU_ASSERT(epnum < ep_count);
|
||||
|
||||
xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, dir);
|
||||
xfer->max_size = tu_edpt_packet_size(desc_edpt);
|
||||
xfer->interval = desc_edpt->bInterval;
|
||||
|
||||
uint16_t const fifo_size = tu_div_ceil(xfer->max_size, 4);
|
||||
|
||||
if (dir == TUSB_DIR_OUT) {
|
||||
// Calculate required size of RX FIFO
|
||||
uint16_t const sz = calc_grxfsiz(4 * fifo_size, ep_count);
|
||||
|
||||
// If size_rx needs to be extended check if possible and if so enlarge it
|
||||
if (dwc2->grxfsiz < sz) {
|
||||
TU_ASSERT(sz + _allocated_fifo_words_tx <= _dwc2_controller[rhport].ep_fifo_size / 4);
|
||||
|
||||
// Enlarge RX FIFO
|
||||
dwc2->grxfsiz = sz;
|
||||
}
|
||||
|
||||
dwc2->epout[epnum].doepctl |= (1 << DOEPCTL_USBAEP_Pos) |
|
||||
(desc_edpt->bmAttributes.xfer << DOEPCTL_EPTYP_Pos) |
|
||||
(desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? DOEPCTL_SD0PID_SEVNFRM : 0) |
|
||||
(xfer->max_size << DOEPCTL_MPSIZ_Pos);
|
||||
|
||||
dwc2->daintmsk |= TU_BIT(DAINTMSK_OEPM_Pos + epnum);
|
||||
} else {
|
||||
// "USB Data FIFOs" section in reference manual
|
||||
// Peripheral FIFO architecture
|
||||
//
|
||||
// --------------- 320 or 1024 ( 1280 or 4096 bytes )
|
||||
// | IN FIFO 0 |
|
||||
// --------------- (320 or 1024) - 16
|
||||
// | IN FIFO 1 |
|
||||
// --------------- (320 or 1024) - 16 - x
|
||||
// | . . . . |
|
||||
// --------------- (320 or 1024) - 16 - x - y - ... - z
|
||||
// | IN FIFO MAX |
|
||||
// ---------------
|
||||
// | FREE |
|
||||
// --------------- GRXFSIZ
|
||||
// | OUT FIFO |
|
||||
// | ( Shared ) |
|
||||
// --------------- 0
|
||||
//
|
||||
// In FIFO is allocated by following rules:
|
||||
// - IN EP 1 gets FIFO 1, IN EP "n" gets FIFO "n".
|
||||
|
||||
// Check if free space is available
|
||||
TU_ASSERT(_allocated_fifo_words_tx + fifo_size + dwc2->grxfsiz <= _dwc2_controller[rhport].ep_fifo_size / 4);
|
||||
|
||||
_allocated_fifo_words_tx += fifo_size;
|
||||
|
||||
TU_LOG(DWC2_DEBUG, " Allocated %u bytes at offset %lu", fifo_size * 4,
|
||||
_dwc2_controller[rhport].ep_fifo_size - _allocated_fifo_words_tx * 4);
|
||||
|
||||
// DIEPTXF starts at FIFO #1.
|
||||
// Both TXFD and TXSA are in unit of 32-bit words.
|
||||
dwc2->dieptxf[epnum - 1] = (fifo_size << DIEPTXF_INEPTXFD_Pos) |
|
||||
(_dwc2_controller[rhport].ep_fifo_size / 4 - _allocated_fifo_words_tx);
|
||||
|
||||
dwc2->epin[epnum].diepctl |= (1 << DIEPCTL_USBAEP_Pos) |
|
||||
(epnum << DIEPCTL_TXFNUM_Pos) |
|
||||
(desc_edpt->bmAttributes.xfer << DIEPCTL_EPTYP_Pos) |
|
||||
(desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? DIEPCTL_SD0PID_SEVNFRM : 0) |
|
||||
(xfer->max_size << DIEPCTL_MPSIZ_Pos);
|
||||
|
||||
dwc2->daintmsk |= (1 << (DAINTMSK_IEPM_Pos + epnum));
|
||||
}
|
||||
|
||||
TU_ASSERT(fifo_alloc(rhport, desc_edpt->bEndpointAddress, tu_edpt_packet_size(desc_edpt)));
|
||||
edpt_activate(rhport, desc_edpt);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -638,16 +711,39 @@ void dcd_edpt_close_all(uint8_t rhport) {
|
||||
|
||||
for (uint8_t n = 1; n < ep_count; n++) {
|
||||
// disable OUT endpoint
|
||||
dwc2->epout[n].doepctl = 0;
|
||||
if (dwc2->epout[n].doepctl & DOEPCTL_EPENA) {
|
||||
dwc2->epout[n].doepctl |= DOEPCTL_SNAK | DOEPCTL_EPDIS;
|
||||
}
|
||||
xfer_status[n][TUSB_DIR_OUT].max_size = 0;
|
||||
|
||||
// disable IN endpoint
|
||||
dwc2->epin[n].diepctl = 0;
|
||||
if (dwc2->epin[n].diepctl & DIEPCTL_EPENA) {
|
||||
dwc2->epin[n].diepctl |= DIEPCTL_SNAK | DIEPCTL_EPDIS;
|
||||
}
|
||||
xfer_status[n][TUSB_DIR_IN].max_size = 0;
|
||||
}
|
||||
|
||||
// reset allocated fifo OUT
|
||||
dwc2->grxfsiz = calc_grxfsiz(64, ep_count);
|
||||
// reset allocated fifo IN
|
||||
_allocated_fifo_words_tx = 16;
|
||||
|
||||
fifo_flush_tx(dwc2, 0x10); // all tx fifo
|
||||
fifo_flush_rx(dwc2);
|
||||
}
|
||||
|
||||
bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
|
||||
TU_ASSERT(fifo_alloc(rhport, ep_addr, largest_packet_size));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) {
|
||||
// Disable EP to clear potential incomplete transfers
|
||||
edpt_disable(rhport, p_endpoint_desc->bEndpointAddress, false);
|
||||
|
||||
edpt_activate(rhport, p_endpoint_desc);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) {
|
||||
@@ -707,89 +803,12 @@ bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t* ff, uint16_t
|
||||
return true;
|
||||
}
|
||||
|
||||
static void dcd_edpt_disable(uint8_t rhport, uint8_t ep_addr, bool stall) {
|
||||
(void) rhport;
|
||||
|
||||
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
|
||||
|
||||
uint8_t const epnum = tu_edpt_number(ep_addr);
|
||||
uint8_t const dir = tu_edpt_dir(ep_addr);
|
||||
|
||||
if (dir == TUSB_DIR_IN) {
|
||||
dwc2_epin_t* epin = dwc2->epin;
|
||||
|
||||
// Only disable currently enabled non-control endpoint
|
||||
if ((epnum == 0) || !(epin[epnum].diepctl & DIEPCTL_EPENA)) {
|
||||
epin[epnum].diepctl |= DIEPCTL_SNAK | (stall ? DIEPCTL_STALL : 0);
|
||||
} else {
|
||||
// Stop transmitting packets and NAK IN xfers.
|
||||
epin[epnum].diepctl |= DIEPCTL_SNAK;
|
||||
while ((epin[epnum].diepint & DIEPINT_INEPNE) == 0) {}
|
||||
|
||||
// Disable the endpoint.
|
||||
epin[epnum].diepctl |= DIEPCTL_EPDIS | (stall ? DIEPCTL_STALL : 0);
|
||||
while ((epin[epnum].diepint & DIEPINT_EPDISD_Msk) == 0) {}
|
||||
|
||||
epin[epnum].diepint = DIEPINT_EPDISD;
|
||||
}
|
||||
|
||||
// Flush the FIFO, and wait until we have confirmed it cleared.
|
||||
dwc2->grstctl = ((epnum << GRSTCTL_TXFNUM_Pos) | GRSTCTL_TXFFLSH);
|
||||
while ((dwc2->grstctl & GRSTCTL_TXFFLSH_Msk) != 0) {}
|
||||
} else {
|
||||
dwc2_epout_t* epout = dwc2->epout;
|
||||
|
||||
// Only disable currently enabled non-control endpoint
|
||||
if ((epnum == 0) || !(epout[epnum].doepctl & DOEPCTL_EPENA)) {
|
||||
epout[epnum].doepctl |= stall ? DOEPCTL_STALL : 0;
|
||||
} else {
|
||||
// Asserting GONAK is required to STALL an OUT endpoint.
|
||||
// Simpler to use polling here, we don't use the "B"OUTNAKEFF interrupt
|
||||
// anyway, and it can't be cleared by user code. If this while loop never
|
||||
// finishes, we have bigger problems than just the stack.
|
||||
dwc2->dctl |= DCTL_SGONAK;
|
||||
while ((dwc2->gintsts & GINTSTS_BOUTNAKEFF_Msk) == 0) {}
|
||||
|
||||
// Ditto here- disable the endpoint.
|
||||
epout[epnum].doepctl |= DOEPCTL_EPDIS | (stall ? DOEPCTL_STALL : 0);
|
||||
while ((epout[epnum].doepint & DOEPINT_EPDISD_Msk) == 0) {}
|
||||
|
||||
epout[epnum].doepint = DOEPINT_EPDISD;
|
||||
|
||||
// Allow other OUT endpoints to keep receiving.
|
||||
dwc2->dctl |= DCTL_CGONAK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close an endpoint.
|
||||
*/
|
||||
void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) {
|
||||
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
|
||||
|
||||
uint8_t const epnum = tu_edpt_number(ep_addr);
|
||||
uint8_t const dir = tu_edpt_dir(ep_addr);
|
||||
|
||||
dcd_edpt_disable(rhport, ep_addr, false);
|
||||
|
||||
// Update max_size
|
||||
xfer_status[epnum][dir].max_size = 0; // max_size = 0 marks a disabled EP - required for changing FIFO allocation
|
||||
|
||||
if (dir == TUSB_DIR_IN) {
|
||||
uint16_t const fifo_size = (dwc2->dieptxf[epnum - 1] & DIEPTXF_INEPTXFD_Msk) >> DIEPTXF_INEPTXFD_Pos;
|
||||
uint16_t const fifo_start = (dwc2->dieptxf[epnum - 1] & DIEPTXF_INEPTXSA_Msk) >> DIEPTXF_INEPTXSA_Pos;
|
||||
|
||||
// For now only the last opened endpoint can be closed without fuss.
|
||||
TU_ASSERT(fifo_start == _dwc2_controller[rhport].ep_fifo_size / 4 - _allocated_fifo_words_tx,);
|
||||
_allocated_fifo_words_tx -= fifo_size;
|
||||
} else {
|
||||
_out_ep_closed = true; // Set flag such that RX FIFO gets reduced in size once RX FIFO is empty
|
||||
}
|
||||
edpt_disable(rhport, ep_addr, false);
|
||||
}
|
||||
|
||||
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
|
||||
dcd_edpt_disable(rhport, ep_addr, true);
|
||||
edpt_disable(rhport, ep_addr, true);
|
||||
}
|
||||
|
||||
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
|
||||
@@ -1130,18 +1149,16 @@ void dcd_int_handler(uint8_t rhport) {
|
||||
dwc2->gotgint = otg_int;
|
||||
}
|
||||
|
||||
if (int_status & GINTSTS_SOF) {
|
||||
dwc2->gotgint = GINTSTS_SOF;
|
||||
if(int_status & GINTSTS_SOF) {
|
||||
dwc2->gintsts = GINTSTS_SOF;
|
||||
const uint32_t frame = (dwc2->dsts & DSTS_FNSOF) >> DSTS_FNSOF_Pos;
|
||||
|
||||
if (_sof_en) {
|
||||
uint32_t frame = (dwc2->dsts & (DSTS_FNSOF)) >> 8;
|
||||
dcd_event_sof(rhport, frame, true);
|
||||
} else {
|
||||
// Disable SOF interrupt if SOF was not explicitly enabled. SOF was used for remote wakeup detection
|
||||
// Disable SOF interrupt if SOF was not explicitly enabled since SOF was used for remote wakeup detection
|
||||
if (!_sof_en) {
|
||||
dwc2->gintmsk &= ~GINTMSK_SOFM;
|
||||
}
|
||||
|
||||
dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
|
||||
dcd_event_sof(rhport, frame, true);
|
||||
}
|
||||
|
||||
// RxFIFO non-empty interrupt handling.
|
||||
@@ -1154,15 +1171,7 @@ void dcd_int_handler(uint8_t rhport) {
|
||||
// Loop until all available packets were handled
|
||||
do {
|
||||
handle_rxflvl_irq(rhport);
|
||||
} while (dwc2->gotgint & GINTSTS_RXFLVL);
|
||||
|
||||
// Manage RX FIFO size
|
||||
if (_out_ep_closed) {
|
||||
update_grxfsiz(rhport);
|
||||
|
||||
// Disable flag
|
||||
_out_ep_closed = false;
|
||||
}
|
||||
} while(dwc2->gintsts & GINTSTS_RXFLVL);
|
||||
|
||||
dwc2->gintmsk |= GINTMSK_RXFLVLM;
|
||||
}
|
||||
|
||||
@@ -94,7 +94,8 @@ static void bus_reset(void)
|
||||
USBOEPCNT_0 &= ~NAK;
|
||||
USBIEPCNT_0 &= ~NAK;
|
||||
|
||||
USBCTL |= FEN; // Enable responding to packets.
|
||||
// Enable responding to packets.
|
||||
USBCTL |= FEN;
|
||||
|
||||
// Dedicated buffers in hardware for SETUP and EP0, no setup needed.
|
||||
// Now safe to respond to SETUP packets.
|
||||
@@ -103,6 +104,28 @@ static void bus_reset(void)
|
||||
USBKEYPID = 0;
|
||||
}
|
||||
|
||||
// Controls reset behavior of the USB module on receipt of a bus reset event.
|
||||
// - enable: When true, bus reset events will cause a reset the USB module.
|
||||
static void enable_functional_reset(const bool enable)
|
||||
{
|
||||
// Check whether or not the USB configuration registers were
|
||||
// locked prior to this function being called so that, if
|
||||
// necessary, the lock state can be restored on exit.
|
||||
bool unlocked = (USBKEYPID == 0xA528) ? true : false;
|
||||
|
||||
if(!unlocked) USBKEYPID = USBKEY;
|
||||
|
||||
if(enable)
|
||||
{
|
||||
USBCTL |= FRSTE;
|
||||
}
|
||||
else
|
||||
{
|
||||
USBCTL &= ~FRSTE;
|
||||
}
|
||||
|
||||
if(!unlocked) USBKEYPID = 0;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* Controller API
|
||||
@@ -131,11 +154,14 @@ void dcd_init (uint8_t rhport)
|
||||
|
||||
USBVECINT = 0;
|
||||
|
||||
// Enable reset and wait for it before continuing.
|
||||
USBIE |= RSTRIE;
|
||||
|
||||
// Enable pullup.
|
||||
USBCNF |= PUR_EN;
|
||||
if(USBPWRCTL & USBBGVBV) {// Bus power detected?
|
||||
USBPWRCTL |= VBOFFIE; // Enable bus-power-removed interrupt.
|
||||
USBIE |= RSTRIE; // Enable reset and wait for it before continuing.
|
||||
USBCNF |= PUR_EN; // Enable pullup.
|
||||
} else {
|
||||
USBPWRCTL |= VBONIE; // Enable bus-power-applied interrupt.
|
||||
USBCNF &= ~USB_EN; // Disable USB module until bus power is detected.
|
||||
}
|
||||
|
||||
USBKEYPID = 0;
|
||||
}
|
||||
@@ -610,14 +636,76 @@ static void handle_setup_packet(void)
|
||||
_setup_packet[i] = setup_buf[i];
|
||||
}
|
||||
|
||||
// Clearing SETUPIFG by reading USBVECINT does not set NAK, so now that we
|
||||
// have a SETUP packet, force NAKs until tinyusb can handle the SETUP
|
||||
// packet and prepare for a new xfer.
|
||||
// Force NAKs until tinyusb can handle the SETUP packet and prepare for a new xfer.
|
||||
USBIEPCNT_0 |= NAK;
|
||||
USBOEPCNT_0 |= NAK;
|
||||
|
||||
// Clear SETUPIFG to avoid handling in the USBVECINT switch statement.
|
||||
// When handled there the NAKs applied to the endpoints above are
|
||||
// cleared by hardware and the host will receive stale/duplicate data.
|
||||
//
|
||||
// Excerpt from MSP430x5xx and MSP430x6xx Family User's Guide:
|
||||
//
|
||||
// "...the SETUPIFG is cleared upon reading USBIV. In addition, the NAK on
|
||||
// input endpoint 0 and output endpoint 0 is also cleared."
|
||||
USBIEPCNF_0 &= ~UBME; // Errata USB10 workaround.
|
||||
USBOEPCNF_0 &= ~UBME; // Errata USB10 workaround.
|
||||
USBIFG &= ~SETUPIFG;
|
||||
USBIEPCNF_0 |= UBME; // Errata USB10 workaround.
|
||||
USBOEPCNF_0 |= UBME; // Errata USB10 workaround.
|
||||
dcd_event_setup_received(0, (uint8_t*) &_setup_packet[0], true);
|
||||
}
|
||||
|
||||
#if CFG_TUSB_OS == OPT_OS_NONE
|
||||
TU_ATTR_ALWAYS_INLINE static inline void tu_delay(uint32_t ms) {
|
||||
// msp430 can run up to 25Mhz -> 40ns per cycle. 1 ms = 25000 cycles
|
||||
// each loop need 4 cycle: 1 sub, 1 cmp, 1 jump, 1 nop
|
||||
volatile uint32_t cycles = (25000 * ms) >> 2;
|
||||
while (cycles > 0) {
|
||||
cycles--;
|
||||
asm("nop");
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define tu_delay(ms) osal_task_delay(ms)
|
||||
#endif
|
||||
|
||||
static void handle_bus_power_event(void *param) {
|
||||
(void) param;
|
||||
|
||||
tu_delay(5); // Bus power settling delay.
|
||||
|
||||
USBKEYPID = USBKEY;
|
||||
|
||||
if(USBPWRCTL & USBBGVBV) { // Event caused by application of bus power.
|
||||
USBPWRCTL |= VBOFFIE; // Enable bus-power-removed interrupt.
|
||||
USBPLLDIVB = USBPLLDIVB; // For some reason the PLL will *NOT* lock unless the divider
|
||||
// register is re-written. The assumption here is that this
|
||||
// register was already properly configured during board-level
|
||||
// initialization.
|
||||
USBPLLCTL |= (UPLLEN | UPFDEN); // Enable the PLL.
|
||||
|
||||
uint16_t attempts = 0;
|
||||
do { // Poll the PLL, checking for a successful lock.
|
||||
USBPLLIR = 0;
|
||||
tu_delay(1);
|
||||
attempts++;
|
||||
} while ((attempts < 10) && (USBPLLIR != 0));
|
||||
|
||||
// A successful lock is indicated by all PLL-related interrupt flags being cleared.
|
||||
if(!USBPLLIR) {
|
||||
dcd_init(0); // Re-initialize the USB module.
|
||||
}
|
||||
} else { // Event caused by removal of bus power.
|
||||
USBPWRCTL |= VBONIE; // Enable bus-power-applied interrupt.
|
||||
USBPLLCTL &= ~(UPLLEN | UPFDEN); // Disable the PLL.
|
||||
USBCNF = 0; // Disable the USB module.
|
||||
dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, false);
|
||||
}
|
||||
|
||||
USBKEYPID = 0;
|
||||
}
|
||||
|
||||
void dcd_int_handler(uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
@@ -628,6 +716,7 @@ void dcd_int_handler(uint8_t rhport)
|
||||
|
||||
if(setup_status)
|
||||
{
|
||||
enable_functional_reset(true);
|
||||
handle_setup_packet();
|
||||
}
|
||||
|
||||
@@ -646,11 +735,32 @@ void dcd_int_handler(uint8_t rhport)
|
||||
|
||||
switch(curr_vector)
|
||||
{
|
||||
case USBVECINT_NONE:
|
||||
break;
|
||||
|
||||
case USBVECINT_RSTR:
|
||||
enable_functional_reset(false); // Errata USB4 workaround.
|
||||
bus_reset();
|
||||
dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
|
||||
break;
|
||||
|
||||
case USBVECINT_PWR_VBUSOn:
|
||||
case USBVECINT_PWR_VBUSOff: {
|
||||
USBKEYPID = USBKEY;
|
||||
// Prevent (possibly) unstable power from generating spurious interrupts.
|
||||
USBPWRCTL &= ~(VBONIE | VBOFFIE);
|
||||
USBKEYPID = 0;
|
||||
|
||||
dcd_event_t event;
|
||||
|
||||
event.rhport = 0;
|
||||
event.event_id = USBD_EVENT_FUNC_CALL;
|
||||
event.func_call.func = handle_bus_power_event;
|
||||
|
||||
dcd_event_handler(&event, true);
|
||||
}
|
||||
break;
|
||||
|
||||
// Clear the (hardware-enforced) NAK on EP 0 after a SETUP packet
|
||||
// is received. At this point, even though the hardware is no longer
|
||||
// forcing NAKs, the EP0 NAK bits should still be set to avoid
|
||||
@@ -675,10 +785,12 @@ void dcd_int_handler(uint8_t rhport)
|
||||
break;
|
||||
|
||||
case USBVECINT_INPUT_ENDPOINT0:
|
||||
enable_functional_reset(true);
|
||||
transmit_packet(0);
|
||||
break;
|
||||
|
||||
case USBVECINT_OUTPUT_ENDPOINT0:
|
||||
enable_functional_reset(true);
|
||||
receive_packet(0);
|
||||
break;
|
||||
|
||||
@@ -710,7 +822,6 @@ void dcd_int_handler(uint8_t rhport)
|
||||
|
||||
default:
|
||||
while(true);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -135,6 +135,16 @@ void dcd_remote_wakeup(uint8_t rhport)
|
||||
(void) rhport;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
if (en) {
|
||||
USBHSD->INT_EN |= USBHS_SOF_ACT_EN;
|
||||
} else {
|
||||
USBHSD->INT_EN &= ~(USBHS_SOF_ACT_EN);
|
||||
}
|
||||
}
|
||||
|
||||
void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const *request) {
|
||||
(void)rhport;
|
||||
|
||||
@@ -332,7 +342,10 @@ void dcd_int_handler(uint8_t rhport) {
|
||||
|
||||
xfer_ctl_t *xfer = XFER_CTL_BASE(end_num, tu_edpt_dir(endp));
|
||||
|
||||
if (rx_token == PID_OUT) {
|
||||
if (rx_token == PID_SOF) {
|
||||
dcd_event_sof(rhport, USBHSD->FRAME_NO, true);
|
||||
|
||||
} else if (rx_token == PID_OUT) {
|
||||
uint16_t rx_len = USBHSD->RX_LEN;
|
||||
|
||||
receive_packet(xfer, rx_len);
|
||||
|
||||
Reference in New Issue
Block a user