summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHiFiPhile <[email protected]>2024-11-30 18:05:04 +0100
committerHiFiPhile <[email protected]>2024-11-30 18:05:04 +0100
commitc41d9db3b1cf23b10b755b55a231fc9b1a7dd147 (patch)
treecfd006e9a2b6b3cb82a1c8c9f96a6671d5cf7cbb /src
parent486e14ea9b66432396089367e61a0480bbb960a9 (diff)
parent2179fb1bd93b71d755c4bf212aff7094f5e8337d (diff)
Merge branch 'master' into rt1170
Signed-off-by: HiFiPhile <[email protected]>
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/class/audio/audio_device.c20
-rwxr-xr-xsrc/class/bth/bth_device.c28
-rw-r--r--src/class/cdc/cdc_device.c122
-rw-r--r--src/class/cdc/cdc_device.h3
-rw-r--r--src/class/cdc/cdc_host.c36
-rw-r--r--src/class/cdc/cdc_host.h3
-rw-r--r--src/class/dfu/dfu_device.c283
-rw-r--r--src/class/dfu/dfu_rt_device.c5
-rw-r--r--src/class/hid/hid_device.c68
-rw-r--r--src/class/hid/hid_host.c38
-rw-r--r--src/class/midi/midi_device.c157
-rw-r--r--src/class/msc/msc_device.c593
-rw-r--r--src/class/msc/msc_host.c97
-rw-r--r--src/class/msc/msc_host.h7
-rw-r--r--src/class/net/ecm_rndis_device.c306
-rw-r--r--src/class/net/ncm.h7
-rw-r--r--src/class/net/ncm_device.c124
-rw-r--r--src/class/usbtmc/usbtmc_device.c67
-rw-r--r--src/class/vendor/vendor_device.c64
-rw-r--r--src/class/video/video_device.c109
-rw-r--r--src/class/video/video_device.h2
-rw-r--r--src/common/tusb_common.h11
-rw-r--r--src/common/tusb_compiler.h5
-rw-r--r--src/common/tusb_mcu.h37
-rw-r--r--src/common/tusb_private.h14
-rw-r--r--src/common/tusb_types.h59
-rw-r--r--src/common/tusb_verify.h2
-rw-r--r--src/device/dcd.h6
-rw-r--r--src/device/usbd.c23
-rw-r--r--src/device/usbd_control.c51
-rw-r--r--src/host/hcd.h6
-rw-r--r--src/host/usbh.c298
-rw-r--r--src/host/usbh.h2
-rw-r--r--src/portable/chipidea/ci_hs/dcd_ci_hs.c17
-rw-r--r--src/portable/dialog/da146xx/dcd_da146xx.c1
-rw-r--r--src/portable/microchip/samd/dcd_samd.c2
-rw-r--r--src/portable/microchip/samg/dcd_samg.c40
-rw-r--r--src/portable/nordic/nrf5x/dcd_nrf5x.c3
-rw-r--r--src/portable/nxp/khci/hcd_khci.c2
-rw-r--r--src/portable/nxp/lpc17_40/hcd_lpc17_40.c1
-rw-r--r--src/portable/raspberrypi/rp2040/dcd_rp2040.c108
-rw-r--r--src/portable/renesas/rusb2/rusb2_common.c1
-rw-r--r--src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c1
-rw-r--r--src/portable/st/stm32_fsdev/fsdev_stm32.h16
-rw-r--r--src/portable/synopsys/dwc2/dcd_dwc2.c1172
-rw-r--r--src/portable/synopsys/dwc2/dwc2_common.c311
-rw-r--r--src/portable/synopsys/dwc2/dwc2_common.h101
-rw-r--r--src/portable/synopsys/dwc2/dwc2_esp32.h84
-rw-r--r--src/portable/synopsys/dwc2/dwc2_info.md116
-rwxr-xr-xsrc/portable/synopsys/dwc2/dwc2_info.py26
-rw-r--r--src/portable/synopsys/dwc2/dwc2_stm32.h24
-rw-r--r--src/portable/synopsys/dwc2/dwc2_type.h717
-rw-r--r--src/portable/synopsys/dwc2/hcd_dwc2.c1360
-rw-r--r--src/tusb.c33
-rw-r--r--src/tusb.h13
-rw-r--r--src/tusb_option.h86
57 files changed, 4467 insertions, 2422 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 272962332..cf6878389 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -67,6 +67,7 @@ function(add_tinyusb TARGET)
-Wunused-function
-Wreturn-type
-Wredundant-decls
+ -Wmissing-prototypes
)
elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
diff --git a/src/class/audio/audio_device.c b/src/class/audio/audio_device.c
index 8c86de433..cd4183cc3 100644
--- a/src/class/audio/audio_device.c
+++ b/src/class/audio/audio_device.c
@@ -490,13 +490,13 @@ TU_ATTR_WEAK bool tud_audio_feedback_format_correction_cb(uint8_t func_id) {
(void) func_id;
return CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION;
}
-#endif
TU_ATTR_WEAK TU_ATTR_FAST_FUNC void tud_audio_feedback_interval_isr(uint8_t func_id, uint32_t frame_number, uint8_t interval_shift) {
(void) func_id;
(void) frame_number;
(void) interval_shift;
}
+#endif
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
TU_ATTR_WEAK void tud_audio_int_done_cb(uint8_t rhport) {
@@ -1810,7 +1810,8 @@ static bool audiod_get_interface(uint8_t rhport, tusb_control_request_t const *
uint8_t const *dummy;
TU_VERIFY(audiod_get_AS_interface_index_global(itf, &func_id, &idxItf, &dummy));
- TU_VERIFY(tud_control_xfer(rhport, p_request, &_audiod_fct[func_id].alt_setting[idxItf], 1));
+ _audiod_fct[func_id].ctrl_buf[0] = _audiod_fct[func_id].alt_setting[idxItf];
+ TU_VERIFY(tud_control_xfer(rhport, p_request, _audiod_fct[func_id].ctrl_buf, 1));
TU_LOG2(" Get itf: %u - current alt: %u\r\n", itf, _audiod_fct[func_id].alt_setting[idxItf]);
@@ -2066,8 +2067,8 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
// Avoid 64bit division
uint32_t nominal = ((fb_param.sample_freq / 100) << 16) / (frame_div / 100);
audio->feedback.compute.fifo_count.nom_value = nominal;
- audio->feedback.compute.fifo_count.rate_const[0] = (audio->feedback.max_value - nominal) / fifo_lvl_thr;
- audio->feedback.compute.fifo_count.rate_const[1] = (nominal - audio->feedback.min_value) / fifo_lvl_thr;
+ audio->feedback.compute.fifo_count.rate_const[0] = (uint16_t) ((audio->feedback.max_value - nominal) / fifo_lvl_thr);
+ audio->feedback.compute.fifo_count.rate_const[1] = (uint16_t) ((nominal - audio->feedback.min_value) / fifo_lvl_thr);
// On HS feedback is more sensitive since packet size can vary every MSOF, could cause instability
if(tud_speed_get() == TUSB_SPEED_HIGH) {
audio->feedback.compute.fifo_count.rate_const[0] /= 8;
@@ -2137,6 +2138,13 @@ static bool audiod_control_complete(uint8_t rhport, tusb_control_request_t const
// Check if entity is present and get corresponding driver index
TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &func_id));
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
+ uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+ if (_audiod_fct[func_id].bclock_id_tx == entityID && ctrlSel == AUDIO_CS_CTRL_SAM_FREQ && p_request->bRequest == AUDIO_CS_REQ_CUR) {
+ _audiod_fct[func_id].sample_rate_tx = tu_unaligned_read32(_audiod_fct[func_id].ctrl_buf);
+ }
+#endif
+
// Invoke callback
return tud_audio_set_req_entity_cb(rhport, p_request, _audiod_fct[func_id].ctrl_buf);
}
@@ -2376,11 +2384,11 @@ static bool audiod_set_fb_params_freq(audiod_function_t* audio, uint32_t sample_
if ((mclk_freq % sample_freq) == 0 && tu_is_power_of_two(mclk_freq / sample_freq))
{
audio->feedback.compute_method = AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2;
- audio->feedback.compute.power_of_2 = 16 - (audio->feedback.frame_shift - 1) - tu_log2(mclk_freq / sample_freq);
+ audio->feedback.compute.power_of_2 = (uint8_t) (16 - (audio->feedback.frame_shift - 1) - tu_log2(mclk_freq / sample_freq));
}
else if ( audio->feedback.compute_method == AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT)
{
- audio->feedback.compute.float_const = (float)sample_freq / mclk_freq * (1UL << (16 - (audio->feedback.frame_shift - 1)));
+ audio->feedback.compute.float_const = (float)sample_freq / (float) mclk_freq * (1UL << (16 - (audio->feedback.frame_shift - 1)));
}
else
{
diff --git a/src/class/bth/bth_device.c b/src/class/bth/bth_device.c
index a79908627..45cbf2d98 100755
--- a/src/class/bth/bth_device.c
+++ b/src/class/bth/bth_device.c
@@ -37,8 +37,7 @@
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
-typedef struct
-{
+typedef struct {
uint8_t itf_num;
uint8_t ep_ev;
uint8_t ep_acl_in;
@@ -49,17 +48,18 @@ typedef struct
// Previous amount of bytes sent when issuing ZLP
uint32_t prev_xferred_bytes;
-
- // Endpoint Transfer buffer
- CFG_TUSB_MEM_ALIGN bt_hci_cmd_t hci_cmd;
- CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_BTH_DATA_EPSIZE];
-
} btd_interface_t;
+typedef struct {
+ TUD_EPBUF_DEF(epout_buf, CFG_TUD_BTH_DATA_EPSIZE);
+ TUD_EPBUF_TYPE_DEF(bt_hci_cmd_t, hci_cmd);
+} btd_epbuf_t;
+
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
-CFG_TUD_MEM_SECTION btd_interface_t _btd_itf;
+static btd_interface_t _btd_itf;
+CFG_TUD_MEM_SECTION static btd_epbuf_t _btd_epbuf;
static bool bt_tx_data(uint8_t ep, void *data, uint16_t len)
{
@@ -152,7 +152,7 @@ uint16_t btd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_
itf_desc = (tusb_desc_interface_t const *)tu_desc_next(tu_desc_next(desc_ep));
// Prepare for incoming data from host
- TU_ASSERT(usbd_edpt_xfer(rhport, _btd_itf.ep_acl_out, _btd_itf.epout_buf, CFG_TUD_BTH_DATA_EPSIZE), 0);
+ TU_ASSERT(usbd_edpt_xfer(rhport, _btd_itf.ep_acl_out, _btd_epbuf.epout_buf, CFG_TUD_BTH_DATA_EPSIZE), 0);
drv_len = hci_itf_size;
@@ -243,14 +243,16 @@ bool btd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t c
}
else return false;
- return tud_control_xfer(rhport, request, &_btd_itf.hci_cmd, sizeof(_btd_itf.hci_cmd));
+ return tud_control_xfer(rhport, request, &_btd_epbuf.hci_cmd, sizeof(bt_hci_cmd_t));
}
else if ( stage == CONTROL_STAGE_DATA )
{
// Handle class request only
TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
- if (tud_bt_hci_cmd_cb) tud_bt_hci_cmd_cb(&_btd_itf.hci_cmd, tu_min16(request->wLength, sizeof(_btd_itf.hci_cmd)));
+ if (tud_bt_hci_cmd_cb) {
+ tud_bt_hci_cmd_cb(&_btd_epbuf.hci_cmd, tu_min16(request->wLength, sizeof(bt_hci_cmd_t)));
+ }
}
return true;
@@ -261,10 +263,10 @@ bool btd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result,
// received new data from host
if (ep_addr == _btd_itf.ep_acl_out)
{
- if (tud_bt_acl_data_received_cb) tud_bt_acl_data_received_cb(_btd_itf.epout_buf, xferred_bytes);
+ if (tud_bt_acl_data_received_cb) tud_bt_acl_data_received_cb(_btd_epbuf.epout_buf, xferred_bytes);
// prepare for next data
- TU_ASSERT(usbd_edpt_xfer(rhport, _btd_itf.ep_acl_out, _btd_itf.epout_buf, CFG_TUD_BTH_DATA_EPSIZE));
+ TU_ASSERT(usbd_edpt_xfer(rhport, _btd_itf.ep_acl_out, _btd_epbuf.epout_buf, CFG_TUD_BTH_DATA_EPSIZE));
}
else if (ep_addr == _btd_itf.ep_ev)
{
diff --git a/src/class/cdc/cdc_device.c b/src/class/cdc/cdc_device.c
index ebc408f5a..efb672201 100644
--- a/src/class/cdc/cdc_device.c
+++ b/src/class/cdc/cdc_device.c
@@ -67,22 +67,27 @@ typedef struct {
OSAL_MUTEX_DEF(rx_ff_mutex);
OSAL_MUTEX_DEF(tx_ff_mutex);
-
- // Endpoint Transfer buffer
- CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_CDC_EP_BUFSIZE];
- CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_CDC_EP_BUFSIZE];
} cdcd_interface_t;
#define ITF_MEM_RESET_SIZE offsetof(cdcd_interface_t, wanted_char)
+typedef struct {
+ TUD_EPBUF_DEF(epout, CFG_TUD_CDC_EP_BUFSIZE);
+ TUD_EPBUF_DEF(epin, CFG_TUD_CDC_EP_BUFSIZE);
+} cdcd_epbuf_t;
+
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
-CFG_TUD_MEM_SECTION static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC];
+static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC];
+CFG_TUD_MEM_SECTION static cdcd_epbuf_t _cdcd_epbuf[CFG_TUD_CDC];
+
static tud_cdc_configure_fifo_t _cdcd_fifo_cfg;
-static bool _prep_out_transaction (cdcd_interface_t* p_cdc) {
- uint8_t const rhport = 0;
+static bool _prep_out_transaction(uint8_t itf) {
+ const uint8_t rhport = 0;
+ cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
+ cdcd_epbuf_t* p_epbuf = &_cdcd_epbuf[itf];
// Skip if usb is not ready yet
TU_VERIFY(tud_ready() && p_cdc->ep_out);
@@ -93,7 +98,7 @@ static bool _prep_out_transaction (cdcd_interface_t* p_cdc) {
// TODO Actually we can still carry out the transfer, keeping count of received bytes
// and slowly move it to the FIFO when read().
// This pre-check reduces endpoint claiming
- TU_VERIFY(available >= sizeof(p_cdc->epout_buf));
+ TU_VERIFY(available >= CFG_TUD_CDC_EP_BUFSIZE);
// claim endpoint
TU_VERIFY(usbd_edpt_claim(rhport, p_cdc->ep_out));
@@ -101,9 +106,9 @@ static bool _prep_out_transaction (cdcd_interface_t* p_cdc) {
// fifo can be changed before endpoint is claimed
available = tu_fifo_remaining(&p_cdc->rx_ff);
- if ( available >= sizeof(p_cdc->epout_buf) ) {
- return usbd_edpt_xfer(rhport, p_cdc->ep_out, p_cdc->epout_buf, sizeof(p_cdc->epout_buf));
- }else {
+ if (available >= CFG_TUD_CDC_EP_BUFSIZE) {
+ return usbd_edpt_xfer(rhport, p_cdc->ep_out, p_epbuf->epout, CFG_TUD_CDC_EP_BUFSIZE);
+ } else {
// Release endpoint since we don't make any transfer
usbd_edpt_release(rhport, p_cdc->ep_out);
return false;
@@ -114,7 +119,7 @@ static bool _prep_out_transaction (cdcd_interface_t* p_cdc) {
// APPLICATION API
//--------------------------------------------------------------------+
-bool tud_cdc_configure_fifo(tud_cdc_configure_fifo_t const* cfg) {
+bool tud_cdc_configure_fifo(const tud_cdc_configure_fifo_t* cfg) {
TU_VERIFY(cfg);
_cdcd_fifo_cfg = (*cfg);
return true;
@@ -151,7 +156,7 @@ uint32_t tud_cdc_n_available(uint8_t itf) {
uint32_t tud_cdc_n_read(uint8_t itf, void* buffer, uint32_t bufsize) {
cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
uint32_t num_read = tu_fifo_read_n(&p_cdc->rx_ff, buffer, (uint16_t) TU_MIN(bufsize, UINT16_MAX));
- _prep_out_transaction(p_cdc);
+ _prep_out_transaction(itf);
return num_read;
}
@@ -162,13 +167,13 @@ bool tud_cdc_n_peek(uint8_t itf, uint8_t* chr) {
void tud_cdc_n_read_flush(uint8_t itf) {
cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
tu_fifo_clear(&p_cdc->rx_ff);
- _prep_out_transaction(p_cdc);
+ _prep_out_transaction(itf);
}
//--------------------------------------------------------------------+
// WRITE API
//--------------------------------------------------------------------+
-uint32_t tud_cdc_n_write(uint8_t itf, void const* buffer, uint32_t bufsize) {
+uint32_t tud_cdc_n_write(uint8_t itf, const void* buffer, uint32_t bufsize) {
cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
uint16_t ret = tu_fifo_write_n(&p_cdc->tx_ff, buffer, (uint16_t) TU_MIN(bufsize, UINT16_MAX));
@@ -186,23 +191,26 @@ uint32_t tud_cdc_n_write(uint8_t itf, void const* buffer, uint32_t bufsize) {
uint32_t tud_cdc_n_write_flush(uint8_t itf) {
cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
+ cdcd_epbuf_t* p_epbuf = &_cdcd_epbuf[itf];
// Skip if usb is not ready yet
TU_VERIFY(tud_ready(), 0);
// No data to send
- if (!tu_fifo_count(&p_cdc->tx_ff)) return 0;
+ if (!tu_fifo_count(&p_cdc->tx_ff)) {
+ return 0;
+ }
- uint8_t const rhport = 0;
+ const uint8_t rhport = 0;
// Claim the endpoint
TU_VERIFY(usbd_edpt_claim(rhport, p_cdc->ep_in), 0);
// Pull data from FIFO
- uint16_t const count = tu_fifo_read_n(&p_cdc->tx_ff, p_cdc->epin_buf, sizeof(p_cdc->epin_buf));
+ const uint16_t count = tu_fifo_read_n(&p_cdc->tx_ff, p_epbuf->epin, CFG_TUD_CDC_EP_BUFSIZE);
if (count) {
- TU_ASSERT(usbd_edpt_xfer(rhport, p_cdc->ep_in, p_cdc->epin_buf, count), 0);
+ TU_ASSERT(usbd_edpt_xfer(rhport, p_cdc->ep_in, p_epbuf->epin, count), 0);
return count;
} else {
// Release endpoint since we don't make any transfer
@@ -286,32 +294,37 @@ void cdcd_reset(uint8_t rhport) {
cdcd_interface_t* p_cdc = &_cdcd_itf[i];
tu_memclr(p_cdc, ITF_MEM_RESET_SIZE);
- if (!_cdcd_fifo_cfg.rx_persistent) tu_fifo_clear(&p_cdc->rx_ff);
- if (!_cdcd_fifo_cfg.tx_persistent) tu_fifo_clear(&p_cdc->tx_ff);
+ if (!_cdcd_fifo_cfg.rx_persistent) {
+ tu_fifo_clear(&p_cdc->rx_ff);
+ }
+ if (!_cdcd_fifo_cfg.tx_persistent) {
+ tu_fifo_clear(&p_cdc->tx_ff);
+ }
tu_fifo_set_overwritable(&p_cdc->tx_ff, true);
}
}
-uint16_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len) {
+uint16_t cdcd_open(uint8_t rhport, const tusb_desc_interface_t* itf_desc, uint16_t max_len) {
// Only support ACM subclass
TU_VERIFY( TUSB_CLASS_CDC == itf_desc->bInterfaceClass &&
CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass, 0);
// Find available interface
- cdcd_interface_t* p_cdc = NULL;
- for (uint8_t cdc_id = 0; cdc_id < CFG_TUD_CDC; cdc_id++) {
- if (_cdcd_itf[cdc_id].ep_in == 0) {
- p_cdc = &_cdcd_itf[cdc_id];
+ cdcd_interface_t* p_cdc;
+ uint8_t cdc_id;
+ for (cdc_id = 0; cdc_id < CFG_TUD_CDC; cdc_id++) {
+ p_cdc = &_cdcd_itf[cdc_id];
+ if (p_cdc->ep_in == 0) {
break;
}
}
- TU_ASSERT(p_cdc, 0);
+ TU_ASSERT(cdc_id < CFG_TUD_CDC, 0);
//------------- Control Interface -------------//
p_cdc->itf_num = itf_desc->bInterfaceNumber;
uint16_t drv_len = sizeof(tusb_desc_interface_t);
- uint8_t const* p_desc = tu_desc_next(itf_desc);
+ const uint8_t* p_desc = tu_desc_next(itf_desc);
// Communication Functional Descriptors
while (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len) {
@@ -321,7 +334,7 @@ uint16_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1
if (TUSB_DESC_ENDPOINT == tu_desc_type(p_desc)) {
// notification endpoint
- tusb_desc_endpoint_t const* desc_ep = (tusb_desc_endpoint_t const*) p_desc;
+ const tusb_desc_endpoint_t* desc_ep = (const tusb_desc_endpoint_t*) p_desc;
TU_ASSERT(usbd_edpt_open(rhport, desc_ep), 0);
p_cdc->ep_notif = desc_ep->bEndpointAddress;
@@ -332,7 +345,7 @@ uint16_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1
//------------- Data Interface (if any) -------------//
if ((TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) &&
- (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const*) p_desc)->bInterfaceClass)) {
+ (TUSB_CLASS_CDC_DATA == ((const tusb_desc_interface_t*) p_desc)->bInterfaceClass)) {
// next to endpoint descriptor
drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
@@ -344,7 +357,7 @@ uint16_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1
}
// Prepare for incoming data
- _prep_out_transaction(p_cdc);
+ _prep_out_transaction(cdc_id);
return drv_len;
}
@@ -352,19 +365,21 @@ uint16_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1
// Invoked when a control transfer occurred on an interface of this class
// Driver response accordingly to the request and the transfer stage (setup/data/ack)
// return false to stall control endpoint (e.g unsupported request)
-bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const* request) {
+bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_request_t* request) {
// Handle class request only
TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
- uint8_t itf = 0;
- cdcd_interface_t* p_cdc = _cdcd_itf;
+ uint8_t itf;
+ cdcd_interface_t* p_cdc;
// Identify which interface to use
- for (;; itf++, p_cdc++) {
- if (itf >= TU_ARRAY_SIZE(_cdcd_itf)) return false;
-
- if (p_cdc->itf_num == request->wIndex) break;
+ for (itf = 0; itf < CFG_TUD_CDC; itf++) {
+ p_cdc = &_cdcd_itf[itf];
+ if (p_cdc->itf_num == request->wIndex) {
+ break;
+ }
}
+ TU_VERIFY(itf < CFG_TUD_CDC);
switch (request->bRequest) {
case CDC_REQUEST_SET_LINE_CODING:
@@ -372,7 +387,9 @@ bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t
TU_LOG_DRV(" Set Line Coding\r\n");
tud_control_xfer(rhport, request, &p_cdc->line_coding, sizeof(cdc_line_coding_t));
} else if (stage == CONTROL_STAGE_ACK) {
- if (tud_cdc_line_coding_cb) tud_cdc_line_coding_cb(itf, &p_cdc->line_coding);
+ if (tud_cdc_line_coding_cb) {
+ tud_cdc_line_coding_cb(itf, &p_cdc->line_coding);
+ }
}
break;
@@ -403,7 +420,9 @@ bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t
TU_LOG_DRV(" Set Control Line State: DTR = %d, RTS = %d\r\n", dtr, rts);
// Invoke callback
- if (tud_cdc_line_state_cb) tud_cdc_line_state_cb(itf, dtr, rts);
+ if (tud_cdc_line_state_cb) {
+ tud_cdc_line_state_cb(itf, dtr, rts);
+ }
}
break;
@@ -412,7 +431,9 @@ bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t
tud_control_status(rhport, request);
} else if (stage == CONTROL_STAGE_ACK) {
TU_LOG_DRV(" Send Break\r\n");
- if (tud_cdc_send_break_cb) tud_cdc_send_break_cb(itf, request->wValue);
+ if (tud_cdc_send_break_cb) {
+ tud_cdc_send_break_cb(itf, request->wValue);
+ }
}
break;
@@ -432,28 +453,33 @@ bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_
// Identify which interface to use
for (itf = 0; itf < CFG_TUD_CDC; itf++) {
p_cdc = &_cdcd_itf[itf];
- if ((ep_addr == p_cdc->ep_out) || (ep_addr == p_cdc->ep_in)) break;
+ if ((ep_addr == p_cdc->ep_out) || (ep_addr == p_cdc->ep_in)) {
+ break;
+ }
}
TU_ASSERT(itf < CFG_TUD_CDC);
+ cdcd_epbuf_t* p_epbuf = &_cdcd_epbuf[itf];
// Received new data
if (ep_addr == p_cdc->ep_out) {
- tu_fifo_write_n(&p_cdc->rx_ff, p_cdc->epout_buf, (uint16_t) xferred_bytes);
+ tu_fifo_write_n(&p_cdc->rx_ff, p_epbuf->epout, (uint16_t) xferred_bytes);
// Check for wanted char and invoke callback if needed
if (tud_cdc_rx_wanted_cb && (((signed char) p_cdc->wanted_char) != -1)) {
for (uint32_t i = 0; i < xferred_bytes; i++) {
- if ((p_cdc->wanted_char == p_cdc->epout_buf[i]) && !tu_fifo_empty(&p_cdc->rx_ff)) {
+ if ((p_cdc->wanted_char == p_epbuf->epout[i]) && !tu_fifo_empty(&p_cdc->rx_ff)) {
tud_cdc_rx_wanted_cb(itf, p_cdc->wanted_char);
}
}
}
// invoke receive callback (if there is still data)
- if (tud_cdc_rx_cb && !tu_fifo_empty(&p_cdc->rx_ff)) tud_cdc_rx_cb(itf);
+ if (tud_cdc_rx_cb && !tu_fifo_empty(&p_cdc->rx_ff)) {
+ tud_cdc_rx_cb(itf);
+ }
// prepare for OUT transaction
- _prep_out_transaction(p_cdc);
+ _prep_out_transaction(itf);
}
// Data sent to host, we continue to fetch from tx fifo to send.
@@ -461,7 +487,9 @@ bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_
// Though maybe the baudrate is not really important !!!
if (ep_addr == p_cdc->ep_in) {
// invoke transmit callback to possibly refill tx fifo
- if (tud_cdc_tx_complete_cb) tud_cdc_tx_complete_cb(itf);
+ if (tud_cdc_tx_complete_cb) {
+ tud_cdc_tx_complete_cb(itf);
+ }
if (0 == tud_cdc_n_write_flush(itf)) {
// If there is no data left, a ZLP should be sent if
diff --git a/src/class/cdc/cdc_device.h b/src/class/cdc/cdc_device.h
index 3ad7c8baf..2d5ba2575 100644
--- a/src/class/cdc/cdc_device.h
+++ b/src/class/cdc/cdc_device.h
@@ -204,6 +204,9 @@ TU_ATTR_WEAK void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts);
TU_ATTR_WEAK void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding);
// Invoked when received send break
+// \param[in] itf interface for which send break was received.
+// \param[in] duration_ms the length of time, in milliseconds, of the break signal. If a value of FFFFh, then the
+// device will send a break until another SendBreak request is received with value 0000h.
TU_ATTR_WEAK void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms);
//--------------------------------------------------------------------+
diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c
index 8717970e6..e817ebc7e 100644
--- a/src/class/cdc/cdc_host.c
+++ b/src/class/cdc/cdc_host.c
@@ -73,15 +73,17 @@ typedef struct {
tu_edpt_stream_t rx;
uint8_t tx_ff_buf[CFG_TUH_CDC_TX_BUFSIZE];
- CFG_TUH_MEM_ALIGN uint8_t tx_ep_buf[CFG_TUH_CDC_TX_EPSIZE];
-
uint8_t rx_ff_buf[CFG_TUH_CDC_TX_BUFSIZE];
- CFG_TUH_MEM_ALIGN uint8_t rx_ep_buf[CFG_TUH_CDC_TX_EPSIZE];
} stream;
} cdch_interface_t;
-CFG_TUH_MEM_SECTION
+typedef struct {
+ TUH_EPBUF_DEF(tx, CFG_TUH_CDC_TX_EPSIZE);
+ TUH_EPBUF_DEF(rx, CFG_TUH_CDC_TX_EPSIZE);
+} cdch_epbuf_t;
+
static cdch_interface_t cdch_data[CFG_TUH_CDC];
+CFG_TUH_MEM_SECTION static cdch_epbuf_t cdch_epbuf[CFG_TUH_CDC];
//--------------------------------------------------------------------+
// Serial Driver
@@ -626,13 +628,14 @@ bool cdch_init(void) {
tu_memclr(cdch_data, sizeof(cdch_data));
for (size_t i = 0; i < CFG_TUH_CDC; i++) {
cdch_interface_t* p_cdc = &cdch_data[i];
+ cdch_epbuf_t* epbuf = &cdch_epbuf[i];
tu_edpt_stream_init(&p_cdc->stream.tx, true, true, false,
p_cdc->stream.tx_ff_buf, CFG_TUH_CDC_TX_BUFSIZE,
- p_cdc->stream.tx_ep_buf, CFG_TUH_CDC_TX_EPSIZE);
+ epbuf->tx, CFG_TUH_CDC_TX_EPSIZE);
tu_edpt_stream_init(&p_cdc->stream.rx, true, false, false,
p_cdc->stream.rx_ff_buf, CFG_TUH_CDC_RX_BUFSIZE,
- p_cdc->stream.rx_ep_buf, CFG_TUH_CDC_RX_EPSIZE);
+ epbuf->rx, CFG_TUH_CDC_RX_EPSIZE);
}
return true;
@@ -654,7 +657,9 @@ void cdch_close(uint8_t daddr) {
TU_LOG_DRV(" CDCh close addr = %u index = %u\r\n", daddr, idx);
// Invoke application callback
- if (tuh_cdc_umount_cb) tuh_cdc_umount_cb(idx);
+ if (tuh_cdc_umount_cb) {
+ tuh_cdc_umount_cb(idx);
+ }
p_cdc->daddr = 0;
p_cdc->bInterfaceNumber = 0;
@@ -675,7 +680,9 @@ bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t
if ( ep_addr == p_cdc->stream.tx.ep_addr ) {
// invoke tx complete callback to possibly refill tx fifo
- if (tuh_cdc_tx_complete_cb) tuh_cdc_tx_complete_cb(idx);
+ if (tuh_cdc_tx_complete_cb) {
+ tuh_cdc_tx_complete_cb(idx);
+ }
if ( 0 == tu_edpt_stream_write_xfer(daddr, &p_cdc->stream.tx) ) {
// If there is no data left, a ZLP should be sent if:
@@ -695,7 +702,9 @@ bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t
}
// invoke receive callback
- if (tuh_cdc_rx_cb) tuh_cdc_rx_cb(idx);
+ if (tuh_cdc_rx_cb) {
+ tuh_cdc_rx_cb(idx);
+ }
// prepare for next transfer if needed
tu_edpt_stream_read_xfer(daddr, &p_cdc->stream.rx);
@@ -738,9 +747,8 @@ bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_d
if (TUSB_CLASS_CDC == itf_desc->bInterfaceClass &&
CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass) {
return acm_open(daddr, itf_desc, max_len);
- }
- else if (SERIAL_DRIVER_COUNT > 1 &&
- TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass) {
+ } else if (SERIAL_DRIVER_COUNT > 1 &&
+ TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass) {
uint16_t vid, pid;
TU_VERIFY(tuh_vid_pid_get(daddr, &vid, &pid));
@@ -760,7 +768,9 @@ bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_d
static void set_config_complete(cdch_interface_t * p_cdc, uint8_t idx, uint8_t itf_num) {
TU_LOG_DRV("CDCh Set Configure complete\r\n");
p_cdc->mounted = true;
- if (tuh_cdc_mount_cb) tuh_cdc_mount_cb(idx);
+ if (tuh_cdc_mount_cb) {
+ tuh_cdc_mount_cb(idx);
+ }
// Prepare for incoming data
tu_edpt_stream_read_xfer(p_cdc->daddr, &p_cdc->stream.rx);
diff --git a/src/class/cdc/cdc_host.h b/src/class/cdc/cdc_host.h
index b63dd1530..df975b2f0 100644
--- a/src/class/cdc/cdc_host.h
+++ b/src/class/cdc/cdc_host.h
@@ -89,8 +89,7 @@ bool tuh_cdc_get_dtr(uint8_t idx);
bool tuh_cdc_get_rts(uint8_t idx);
// Check if interface is connected (DTR active)
-TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_connected(uint8_t idx)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_connected(uint8_t idx) {
return tuh_cdc_get_dtr(idx);
}
diff --git a/src/class/dfu/dfu_device.c b/src/class/dfu/dfu_device.c
index 71e7ac2b3..d9e2d3f2f 100644
--- a/src/class/dfu/dfu_device.c
+++ b/src/class/dfu/dfu_device.c
@@ -47,8 +47,7 @@
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
-typedef struct
-{
+typedef struct {
uint8_t attrs;
uint8_t alt;
@@ -58,69 +57,65 @@ typedef struct
bool flashing_in_progress;
uint16_t block;
uint16_t length;
-
- CFG_TUSB_MEM_ALIGN uint8_t transfer_buf[CFG_TUD_DFU_XFER_BUFSIZE];
} dfu_state_ctx_t;
// Only a single dfu state is allowed
-CFG_TUD_MEM_SECTION tu_static dfu_state_ctx_t _dfu_ctx;
+static dfu_state_ctx_t _dfu_ctx;
+
+CFG_TUD_MEM_SECTION static struct {
+ TUD_EPBUF_DEF(transfer_buf, CFG_TUD_DFU_XFER_BUFSIZE);
+} _dfu_epbuf;
-static void reset_state(void)
-{
+static void reset_state(void) {
_dfu_ctx.state = DFU_IDLE;
_dfu_ctx.status = DFU_STATUS_OK;
_dfu_ctx.flashing_in_progress = false;
}
-static bool reply_getstatus(uint8_t rhport, tusb_control_request_t const * request, dfu_state_t state, dfu_status_t status, uint32_t timeout);
-static bool process_download_get_status(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
-static bool process_manifest_get_status(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
+static bool reply_getstatus(uint8_t rhport, const tusb_control_request_t* request, dfu_state_t state, dfu_status_t status, uint32_t timeout);
+static bool process_download_get_status(uint8_t rhport, uint8_t stage, const tusb_control_request_t* request);
+static bool process_manifest_get_status(uint8_t rhport, uint8_t stage, const tusb_control_request_t* request);
//--------------------------------------------------------------------+
// Debug
//--------------------------------------------------------------------+
#if CFG_TUSB_DEBUG >= 2
-tu_static tu_lookup_entry_t const _dfu_request_lookup[] =
-{
- { .key = DFU_REQUEST_DETACH , .data = "DETACH" },
- { .key = DFU_REQUEST_DNLOAD , .data = "DNLOAD" },
- { .key = DFU_REQUEST_UPLOAD , .data = "UPLOAD" },
- { .key = DFU_REQUEST_GETSTATUS , .data = "GETSTATUS" },
- { .key = DFU_REQUEST_CLRSTATUS , .data = "CLRSTATUS" },
- { .key = DFU_REQUEST_GETSTATE , .data = "GETSTATE" },
- { .key = DFU_REQUEST_ABORT , .data = "ABORT" },
+tu_static tu_lookup_entry_t const _dfu_request_lookup[] = {
+ { .key = DFU_REQUEST_DETACH , .data = "DETACH" },
+ { .key = DFU_REQUEST_DNLOAD , .data = "DNLOAD" },
+ { .key = DFU_REQUEST_UPLOAD , .data = "UPLOAD" },
+ { .key = DFU_REQUEST_GETSTATUS, .data = "GETSTATUS" },
+ { .key = DFU_REQUEST_CLRSTATUS, .data = "CLRSTATUS" },
+ { .key = DFU_REQUEST_GETSTATE , .data = "GETSTATE" },
+ { .key = DFU_REQUEST_ABORT , .data = "ABORT" },
};
-tu_static tu_lookup_table_t const _dfu_request_table =
-{
+tu_static tu_lookup_table_t const _dfu_request_table = {
.count = TU_ARRAY_SIZE(_dfu_request_lookup),
.items = _dfu_request_lookup
};
-tu_static tu_lookup_entry_t const _dfu_state_lookup[] =
-{
- { .key = APP_IDLE , .data = "APP_IDLE" },
- { .key = APP_DETACH , .data = "APP_DETACH" },
- { .key = DFU_IDLE , .data = "IDLE" },
- { .key = DFU_DNLOAD_SYNC , .data = "DNLOAD_SYNC" },
- { .key = DFU_DNBUSY , .data = "DNBUSY" },
- { .key = DFU_DNLOAD_IDLE , .data = "DNLOAD_IDLE" },
- { .key = DFU_MANIFEST_SYNC , .data = "MANIFEST_SYNC" },
- { .key = DFU_MANIFEST , .data = "MANIFEST" },
- { .key = DFU_MANIFEST_WAIT_RESET , .data = "MANIFEST_WAIT_RESET" },
- { .key = DFU_UPLOAD_IDLE , .data = "UPLOAD_IDLE" },
- { .key = DFU_ERROR , .data = "ERROR" },
+tu_static tu_lookup_entry_t const _dfu_state_lookup[] = {
+ { .key = APP_IDLE , .data = "APP_IDLE" },
+ { .key = APP_DETACH , .data = "APP_DETACH" },
+ { .key = DFU_IDLE , .data = "IDLE" },
+ { .key = DFU_DNLOAD_SYNC , .data = "DNLOAD_SYNC" },
+ { .key = DFU_DNBUSY , .data = "DNBUSY" },
+ { .key = DFU_DNLOAD_IDLE , .data = "DNLOAD_IDLE" },
+ { .key = DFU_MANIFEST_SYNC , .data = "MANIFEST_SYNC" },
+ { .key = DFU_MANIFEST , .data = "MANIFEST" },
+ { .key = DFU_MANIFEST_WAIT_RESET, .data = "MANIFEST_WAIT_RESET" },
+ { .key = DFU_UPLOAD_IDLE , .data = "UPLOAD_IDLE" },
+ { .key = DFU_ERROR , .data = "ERROR" },
};
-tu_static tu_lookup_table_t const _dfu_state_table =
-{
+tu_static tu_lookup_table_t const _dfu_state_table = {
.count = TU_ARRAY_SIZE(_dfu_state_lookup),
.items = _dfu_state_lookup
};
-tu_static tu_lookup_entry_t const _dfu_status_lookup[] =
-{
+tu_static tu_lookup_entry_t const _dfu_status_lookup[] = {
{ .key = DFU_STATUS_OK , .data = "OK" },
{ .key = DFU_STATUS_ERR_TARGET , .data = "errTARGET" },
{ .key = DFU_STATUS_ERR_FILE , .data = "errFILE" },
@@ -139,8 +134,7 @@ tu_static tu_lookup_entry_t const _dfu_status_lookup[] =
{ .key = DFU_STATUS_ERR_STALLEDPKT , .data = "errSTALLEDPKT" },
};
-tu_static tu_lookup_table_t const _dfu_status_table =
-{
+tu_static tu_lookup_table_t const _dfu_status_table = {
.count = TU_ARRAY_SIZE(_dfu_status_lookup),
.items = _dfu_status_lookup
};
@@ -150,13 +144,10 @@ tu_static tu_lookup_table_t const _dfu_status_table =
//--------------------------------------------------------------------+
// USBD Driver API
//--------------------------------------------------------------------+
-void dfu_moded_reset(uint8_t rhport)
-{
+void dfu_moded_reset(uint8_t rhport) {
(void) rhport;
-
_dfu_ctx.attrs = 0;
_dfu_ctx.alt = 0;
-
reset_state();
}
@@ -168,19 +159,17 @@ bool dfu_moded_deinit(void) {
return true;
}
-uint16_t dfu_moded_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
-{
+uint16_t dfu_moded_open(uint8_t rhport, const tusb_desc_interface_t* itf_desc, uint16_t max_len) {
(void) rhport;
//------------- Interface (with Alt) descriptor -------------//
- uint8_t const itf_num = itf_desc->bInterfaceNumber;
+ const uint8_t itf_num = itf_desc->bInterfaceNumber;
uint8_t alt_count = 0;
uint16_t drv_len = 0;
TU_VERIFY(itf_desc->bInterfaceSubClass == TUD_DFU_APP_SUBCLASS && itf_desc->bInterfaceProtocol == DFU_PROTOCOL_DFU, 0);
- while(itf_desc->bInterfaceSubClass == TUD_DFU_APP_SUBCLASS && itf_desc->bInterfaceProtocol == DFU_PROTOCOL_DFU)
- {
+ while(itf_desc->bInterfaceSubClass == TUD_DFU_APP_SUBCLASS && itf_desc->bInterfaceProtocol == DFU_PROTOCOL_DFU) {
TU_ASSERT(max_len > drv_len, 0);
// Alternate must have the same interface number
@@ -191,18 +180,18 @@ uint16_t dfu_moded_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc,
alt_count++;
drv_len += tu_desc_len(itf_desc);
- itf_desc = (tusb_desc_interface_t const *) tu_desc_next(itf_desc);
+ itf_desc = (const tusb_desc_interface_t*) tu_desc_next(itf_desc);
}
//------------- DFU Functional descriptor -------------//
- tusb_desc_dfu_functional_t const *func_desc = (tusb_desc_dfu_functional_t const *) itf_desc;
+ const tusb_desc_dfu_functional_t*func_desc = (const tusb_desc_dfu_functional_t*) itf_desc;
TU_ASSERT(tu_desc_type(func_desc) == TUSB_DESC_FUNCTIONAL, 0);
drv_len += sizeof(tusb_desc_dfu_functional_t);
_dfu_ctx.attrs = func_desc->bAttributes;
// CFG_TUD_DFU_XFER_BUFSIZE has to be set to the buffer size used in TUD_DFU_DESCRIPTOR
- uint16_t const transfer_size = tu_le16toh( tu_unaligned_read16((uint8_t const*) func_desc + offsetof(tusb_desc_dfu_functional_t, wTransferSize)) );
+ const uint16_t transfer_size = tu_le16toh( tu_unaligned_read16((const uint8_t*) func_desc + offsetof(tusb_desc_dfu_functional_t, wTransferSize)) );
TU_ASSERT(transfer_size <= CFG_TUD_DFU_XFER_BUFSIZE, drv_len);
return drv_len;
@@ -211,99 +200,85 @@ uint16_t dfu_moded_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc,
// Invoked when a control transfer occurred on an interface of this class
// Driver response accordingly to the request and the transfer stage (setup/data/ack)
// return false to stall control endpoint (e.g unsupported request)
-bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
-{
+bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_request_t* request) {
TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE);
-
TU_LOG_DRV(" DFU State : %s, Status: %s\r\n", tu_lookup_find(&_dfu_state_table, _dfu_ctx.state), tu_lookup_find(&_dfu_status_table, _dfu_ctx.status));
- if ( request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD )
- {
+ if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD) {
// Standard request include GET/SET_INTERFACE
- switch ( request->bRequest )
- {
+ switch (request->bRequest) {
case TUSB_REQ_SET_INTERFACE:
- if ( stage == CONTROL_STAGE_SETUP )
- {
+ if (stage == CONTROL_STAGE_SETUP) {
// Switch Alt interface and reset state machine
- _dfu_ctx.alt = (uint8_t) request->wValue;
+ _dfu_ctx.alt = (uint8_t)request->wValue;
reset_state();
return tud_control_status(rhport, request);
}
- break;
+ break;
case TUSB_REQ_GET_INTERFACE:
- if(stage == CONTROL_STAGE_SETUP)
- {
+ if (stage == CONTROL_STAGE_SETUP) {
return tud_control_xfer(rhport, request, &_dfu_ctx.alt, 1);
}
- break;
+ break;
// unsupported request
default: return false;
}
- }
- else if ( request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS )
- {
+ } else if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS) {
TU_LOG_DRV(" DFU Request: %s\r\n", tu_lookup_find(&_dfu_request_table, request->bRequest));
// Class request
- switch ( request->bRequest )
- {
+ switch (request->bRequest) {
case DFU_REQUEST_DETACH:
- if ( stage == CONTROL_STAGE_SETUP )
- {
+ if (stage == CONTROL_STAGE_SETUP) {
tud_control_status(rhport, request);
+ } else if (stage == CONTROL_STAGE_ACK) {
+ if (tud_dfu_detach_cb) {
+ tud_dfu_detach_cb();
+ }
}
- else if ( stage == CONTROL_STAGE_ACK )
- {
- if ( tud_dfu_detach_cb ) tud_dfu_detach_cb();
- }
- break;
+ break;
case DFU_REQUEST_CLRSTATUS:
- if ( stage == CONTROL_STAGE_SETUP )
- {
+ if (stage == CONTROL_STAGE_SETUP) {
reset_state();
tud_control_status(rhport, request);
}
- break;
+ break;
case DFU_REQUEST_GETSTATE:
- if ( stage == CONTROL_STAGE_SETUP )
- {
+ if (stage == CONTROL_STAGE_SETUP) {
tud_control_xfer(rhport, request, &_dfu_ctx.state, 1);
}
- break;
+ break;
case DFU_REQUEST_ABORT:
- if ( stage == CONTROL_STAGE_SETUP )
- {
+ if (stage == CONTROL_STAGE_SETUP) {
reset_state();
tud_control_status(rhport, request);
+ } else if (stage == CONTROL_STAGE_ACK) {
+ if (tud_dfu_abort_cb) {
+ tud_dfu_abort_cb(_dfu_ctx.alt);
+ }
}
- else if ( stage == CONTROL_STAGE_ACK )
- {
- if ( tud_dfu_abort_cb ) tud_dfu_abort_cb(_dfu_ctx.alt);
- }
- break;
+ break;
case DFU_REQUEST_UPLOAD:
- if ( stage == CONTROL_STAGE_SETUP )
- {
+ if (stage == CONTROL_STAGE_SETUP) {
TU_VERIFY(_dfu_ctx.attrs & DFU_ATTR_CAN_UPLOAD);
TU_VERIFY(tud_dfu_upload_cb);
TU_VERIFY(request->wLength <= CFG_TUD_DFU_XFER_BUFSIZE);
- uint16_t const xfer_len = tud_dfu_upload_cb(_dfu_ctx.alt, request->wValue, _dfu_ctx.transfer_buf, request->wLength);
+ const uint16_t xfer_len = tud_dfu_upload_cb(_dfu_ctx.alt, request->wValue, _dfu_epbuf.transfer_buf,
+ request->wLength);
- return tud_control_xfer(rhport, request, _dfu_ctx.transfer_buf, xfer_len);
+ return tud_control_xfer(rhport, request, _dfu_epbuf.transfer_buf, xfer_len);
}
- break;
+ break;
case DFU_REQUEST_DNLOAD:
- if ( stage == CONTROL_STAGE_SETUP )
- {
+ if (stage == CONTROL_STAGE_SETUP) {
TU_VERIFY(_dfu_ctx.attrs & DFU_ATTR_CAN_DOWNLOAD);
TU_VERIFY(_dfu_ctx.state == DFU_IDLE || _dfu_ctx.state == DFU_DNLOAD_IDLE);
TU_VERIFY(request->wLength <= CFG_TUD_DFU_XFER_BUFSIZE);
@@ -312,104 +287,86 @@ bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_reque
_dfu_ctx.flashing_in_progress = true;
// save block and length for flashing
- _dfu_ctx.block = request->wValue;
+ _dfu_ctx.block = request->wValue;
_dfu_ctx.length = request->wLength;
- if ( request->wLength )
- {
+ if (request->wLength) {
// Download with payload -> transition to DOWNLOAD SYNC
_dfu_ctx.state = DFU_DNLOAD_SYNC;
- return tud_control_xfer(rhport, request, _dfu_ctx.transfer_buf, request->wLength);
- }
- else
- {
+ return tud_control_xfer(rhport, request, _dfu_epbuf.transfer_buf, request->wLength);
+ } else {
// Download is complete -> transition to MANIFEST SYNC
_dfu_ctx.state = DFU_MANIFEST_SYNC;
return tud_control_status(rhport, request);
}
}
- break;
+ break;
case DFU_REQUEST_GETSTATUS:
- switch ( _dfu_ctx.state )
- {
+ switch (_dfu_ctx.state) {
case DFU_DNLOAD_SYNC:
return process_download_get_status(rhport, stage, request);
- break;
+ break;
case DFU_MANIFEST_SYNC:
return process_manifest_get_status(rhport, stage, request);
- break;
+ break;
default:
- if ( stage == CONTROL_STAGE_SETUP ) return reply_getstatus(rhport, request, _dfu_ctx.state, _dfu_ctx.status, 0);
- break;
+ if (stage == CONTROL_STAGE_SETUP) {
+ return reply_getstatus(rhport, request, _dfu_ctx.state, _dfu_ctx.status, 0);
+ }
+ break;
}
- break;
+ break;
default: return false; // stall unsupported request
}
- }else
- {
+ } else {
return false; // unsupported request
}
return true;
}
-void tud_dfu_finish_flashing(uint8_t status)
-{
+void tud_dfu_finish_flashing(uint8_t status) {
_dfu_ctx.flashing_in_progress = false;
- if ( status == DFU_STATUS_OK )
- {
- if (_dfu_ctx.state == DFU_DNBUSY)
- {
+ if (status == DFU_STATUS_OK) {
+ if (_dfu_ctx.state == DFU_DNBUSY) {
_dfu_ctx.state = DFU_DNLOAD_SYNC;
- }
- else if (_dfu_ctx.state == DFU_MANIFEST)
- {
+ } else if (_dfu_ctx.state == DFU_MANIFEST) {
_dfu_ctx.state = (_dfu_ctx.attrs & DFU_ATTR_MANIFESTATION_TOLERANT)
- ? DFU_MANIFEST_SYNC : DFU_MANIFEST_WAIT_RESET;
+ ? DFU_MANIFEST_SYNC
+ : DFU_MANIFEST_WAIT_RESET;
}
- }
- else
- {
+ } else {
// failed while flashing, move to dfuError
_dfu_ctx.state = DFU_ERROR;
_dfu_ctx.status = (dfu_status_t)status;
}
}
-static bool process_download_get_status(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
-{
- if ( stage == CONTROL_STAGE_SETUP )
- {
+static bool process_download_get_status(uint8_t rhport, uint8_t stage, const tusb_control_request_t* request) {
+ if (stage == CONTROL_STAGE_SETUP) {
// only transition to next state on CONTROL_STAGE_ACK
dfu_state_t next_state;
uint32_t timeout;
- if ( _dfu_ctx.flashing_in_progress )
- {
+ if (_dfu_ctx.flashing_in_progress) {
next_state = DFU_DNBUSY;
- timeout = tud_dfu_get_timeout_cb(_dfu_ctx.alt, (uint8_t) next_state);
- }
- else
- {
+ timeout = tud_dfu_get_timeout_cb(_dfu_ctx.alt, (uint8_t)next_state);
+ } else {
next_state = DFU_DNLOAD_IDLE;
timeout = 0;
}
return reply_getstatus(rhport, request, next_state, _dfu_ctx.status, timeout);
- }
- else if ( stage == CONTROL_STAGE_ACK )
- {
- if ( _dfu_ctx.flashing_in_progress )
- {
+ } else if (stage == CONTROL_STAGE_ACK) {
+ if (_dfu_ctx.flashing_in_progress) {
_dfu_ctx.state = DFU_DNBUSY;
- tud_dfu_download_cb(_dfu_ctx.alt, _dfu_ctx.block, _dfu_ctx.transfer_buf, _dfu_ctx.length);
- }else
- {
+ tud_dfu_download_cb(_dfu_ctx.alt, _dfu_ctx.block, _dfu_epbuf.transfer_buf, _dfu_ctx.length);
+ } else {
_dfu_ctx.state = DFU_DNLOAD_IDLE;
}
}
@@ -417,36 +374,26 @@ static bool process_download_get_status(uint8_t rhport, uint8_t stage, tusb_cont
return true;
}
-static bool process_manifest_get_status(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
-{
- if ( stage == CONTROL_STAGE_SETUP )
- {
+static bool process_manifest_get_status(uint8_t rhport, uint8_t stage, const tusb_control_request_t* request) {
+ if (stage == CONTROL_STAGE_SETUP) {
// only transition to next state on CONTROL_STAGE_ACK
dfu_state_t next_state;
uint32_t timeout;
- if ( _dfu_ctx.flashing_in_progress )
- {
+ if (_dfu_ctx.flashing_in_progress) {
next_state = DFU_MANIFEST;
timeout = tud_dfu_get_timeout_cb(_dfu_ctx.alt, next_state);
- }
- else
- {
+ } else {
next_state = DFU_IDLE;
timeout = 0;
}
return reply_getstatus(rhport, request, next_state, _dfu_ctx.status, timeout);
- }
- else if ( stage == CONTROL_STAGE_ACK )
- {
- if ( _dfu_ctx.flashing_in_progress )
- {
+ } else if (stage == CONTROL_STAGE_ACK) {
+ if (_dfu_ctx.flashing_in_progress) {
_dfu_ctx.state = DFU_MANIFEST;
tud_dfu_manifest_cb(_dfu_ctx.alt);
- }
- else
- {
+ } else {
_dfu_ctx.state = DFU_IDLE;
}
}
@@ -454,15 +401,15 @@ static bool process_manifest_get_status(uint8_t rhport, uint8_t stage, tusb_cont
return true;
}
-static bool reply_getstatus(uint8_t rhport, tusb_control_request_t const * request, dfu_state_t state, dfu_status_t status, uint32_t timeout)
-{
+static bool reply_getstatus(uint8_t rhport, const tusb_control_request_t* request, dfu_state_t state,
+ dfu_status_t status, uint32_t timeout) {
dfu_status_response_t resp;
- resp.bStatus = (uint8_t) status;
+ resp.bStatus = (uint8_t)status;
resp.bwPollTimeout[0] = TU_U32_BYTE0(timeout);
resp.bwPollTimeout[1] = TU_U32_BYTE1(timeout);
resp.bwPollTimeout[2] = TU_U32_BYTE2(timeout);
- resp.bState = (uint8_t) state;
- resp.iString = 0;
+ resp.bState = (uint8_t)state;
+ resp.iString = 0;
return tud_control_xfer(rhport, request, &resp, sizeof(dfu_status_response_t));
}
diff --git a/src/class/dfu/dfu_rt_device.c b/src/class/dfu/dfu_rt_device.c
index 3b801b787..a63c23d9a 100644
--- a/src/class/dfu/dfu_rt_device.c
+++ b/src/class/dfu/dfu_rt_device.c
@@ -58,9 +58,8 @@ bool dfu_rtd_deinit(void) {
return true;
}
-void dfu_rtd_reset(uint8_t rhport)
-{
- (void) rhport;
+void dfu_rtd_reset(uint8_t rhport) {
+ (void) rhport;
}
uint16_t dfu_rtd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
diff --git a/src/class/hid/hid_device.c b/src/class/hid/hid_device.c
index ef6c7f3af..eedcba984 100644
--- a/src/class/hid/hid_device.c
+++ b/src/class/hid/hid_device.c
@@ -51,14 +51,17 @@ typedef struct {
// TODO save hid descriptor since host can specifically request this after enumeration
// Note: HID descriptor may be not available from application after enumeration
- tusb_hid_descriptor_hid_t const *hid_descriptor;
-
- uint8_t ctrl_buf[CFG_TUD_HID_EP_BUFSIZE];
- CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_HID_EP_BUFSIZE];
- CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_HID_EP_BUFSIZE];
+ const tusb_hid_descriptor_hid_t*hid_descriptor;
} hidd_interface_t;
-CFG_TUD_MEM_SECTION tu_static hidd_interface_t _hidd_itf[CFG_TUD_HID];
+typedef struct {
+ TUD_EPBUF_DEF(ctrl , CFG_TUD_HID_EP_BUFSIZE);
+ TUD_EPBUF_DEF(epin , CFG_TUD_HID_EP_BUFSIZE);
+ TUD_EPBUF_DEF(epout, CFG_TUD_HID_EP_BUFSIZE);
+} hidd_epbuf_t;
+
+static hidd_interface_t _hidd_itf[CFG_TUD_HID];
+CFG_TUD_MEM_SECTION static hidd_epbuf_t _hidd_epbuf[CFG_TUD_HID];
/*------------- Helpers -------------*/
TU_ATTR_ALWAYS_INLINE static inline uint8_t get_index_by_itfnum(uint8_t itf_num) {
@@ -108,22 +111,24 @@ bool tud_hid_n_ready(uint8_t instance) {
}
bool tud_hid_n_report(uint8_t instance, uint8_t report_id, void const *report, uint16_t len) {
- uint8_t const rhport = 0;
+ TU_VERIFY(instance < CFG_TUD_HID);
+ const uint8_t rhport = 0;
hidd_interface_t *p_hid = &_hidd_itf[instance];
+ hidd_epbuf_t *p_epbuf = &_hidd_epbuf[instance];
// claim endpoint
TU_VERIFY(usbd_edpt_claim(rhport, p_hid->ep_in));
// prepare data
if (report_id) {
- p_hid->epin_buf[0] = report_id;
- TU_VERIFY(0 == tu_memcpy_s(p_hid->epin_buf + 1, CFG_TUD_HID_EP_BUFSIZE - 1, report, len));
+ p_epbuf->epin[0] = report_id;
+ TU_VERIFY(0 == tu_memcpy_s(p_epbuf->epin + 1, CFG_TUD_HID_EP_BUFSIZE - 1, report, len));
len++;
} else {
- TU_VERIFY(0 == tu_memcpy_s(p_hid->epin_buf, CFG_TUD_HID_EP_BUFSIZE, report, len));
+ TU_VERIFY(0 == tu_memcpy_s(p_epbuf->epin, CFG_TUD_HID_EP_BUFSIZE, report, len));
}
- return usbd_edpt_xfer(rhport, p_hid->ep_in, p_hid->epin_buf, len);
+ return usbd_edpt_xfer(rhport, p_hid->ep_in, p_epbuf->epin, len);
}
uint8_t tud_hid_n_interface_protocol(uint8_t instance) {
@@ -214,15 +219,16 @@ uint16_t hidd_open(uint8_t rhport, tusb_desc_interface_t const *desc_itf, uint16
TU_ASSERT(max_len >= drv_len, 0);
// Find available interface
- hidd_interface_t *p_hid = NULL;
+ hidd_interface_t *p_hid;
uint8_t hid_id;
for (hid_id = 0; hid_id < CFG_TUD_HID; hid_id++) {
- if (_hidd_itf[hid_id].ep_in == 0) {
- p_hid = &_hidd_itf[hid_id];
+ p_hid = &_hidd_itf[hid_id];
+ if (p_hid->ep_in == 0) {
break;
}
}
- TU_ASSERT(p_hid, 0);
+ TU_ASSERT(hid_id < CFG_TUD_HID, 0);
+ hidd_epbuf_t *p_epbuf = &_hidd_epbuf[hid_id];
uint8_t const *p_desc = (uint8_t const *)desc_itf;
@@ -247,10 +253,7 @@ uint16_t hidd_open(uint8_t rhport, tusb_desc_interface_t const *desc_itf, uint16
// Prepare for output endpoint
if (p_hid->ep_out) {
- if (!usbd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf))) {
- TU_LOG_FAILED();
- TU_BREAKPOINT();
- }
+ TU_ASSERT(usbd_edpt_xfer(rhport, p_hid->ep_out, p_epbuf->epout, CFG_TUD_HID_EP_BUFSIZE), drv_len);
}
return drv_len;
@@ -264,8 +267,8 @@ bool hidd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t
uint8_t const hid_itf = get_index_by_itfnum((uint8_t)request->wIndex);
TU_VERIFY(hid_itf < CFG_TUD_HID);
-
hidd_interface_t *p_hid = &_hidd_itf[hid_itf];
+ hidd_epbuf_t *p_epbuf = &_hidd_epbuf[hid_itf];
if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD) {
//------------- STD Request -------------//
@@ -291,7 +294,7 @@ bool hidd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t
uint8_t const report_type = tu_u16_high(request->wValue);
uint8_t const report_id = tu_u16_low(request->wValue);
- uint8_t* report_buf = p_hid->ctrl_buf;
+ uint8_t* report_buf = p_epbuf->ctrl;
uint16_t req_len = tu_min16(request->wLength, CFG_TUD_HID_EP_BUFSIZE);
uint16_t xferlen = 0;
@@ -305,19 +308,19 @@ bool hidd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t
xferlen += tud_hid_get_report_cb(hid_itf, report_id, (hid_report_type_t) report_type, report_buf, req_len);
TU_ASSERT(xferlen > 0);
- tud_control_xfer(rhport, request, p_hid->ctrl_buf, xferlen);
+ tud_control_xfer(rhport, request, p_epbuf->ctrl, xferlen);
}
break;
case HID_REQ_CONTROL_SET_REPORT:
if (stage == CONTROL_STAGE_SETUP) {
- TU_VERIFY(request->wLength <= sizeof(p_hid->ctrl_buf));
- tud_control_xfer(rhport, request, p_hid->ctrl_buf, request->wLength);
+ TU_VERIFY(request->wLength <= CFG_TUD_HID_EP_BUFSIZE);
+ tud_control_xfer(rhport, request, p_epbuf->ctrl, request->wLength);
} else if (stage == CONTROL_STAGE_ACK) {
uint8_t const report_type = tu_u16_high(request->wValue);
uint8_t const report_id = tu_u16_low(request->wValue);
- uint8_t const* report_buf = p_hid->ctrl_buf;
+ uint8_t const* report_buf = p_epbuf->ctrl;
uint16_t report_len = tu_min16(request->wLength, CFG_TUD_HID_EP_BUFSIZE);
// If host request a specific Report ID, extract report ID in buffer before invoking callback
@@ -371,8 +374,8 @@ bool hidd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t
}
bool hidd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
- uint8_t instance = 0;
- hidd_interface_t *p_hid = _hidd_itf;
+ uint8_t instance;
+ hidd_interface_t *p_hid;
// Identify which interface to use
for (instance = 0; instance < CFG_TUD_HID; instance++) {
@@ -382,24 +385,25 @@ bool hidd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_
}
}
TU_ASSERT(instance < CFG_TUD_HID);
+ hidd_epbuf_t *p_epbuf = &_hidd_epbuf[instance];
if (ep_addr == p_hid->ep_in) {
// Input report
if (XFER_RESULT_SUCCESS == result) {
- tud_hid_report_complete_cb(instance, p_hid->epin_buf, (uint16_t) xferred_bytes);
+ tud_hid_report_complete_cb(instance, p_epbuf->epin, (uint16_t) xferred_bytes);
} else {
- tud_hid_report_failed_cb(instance, HID_REPORT_TYPE_INPUT, p_hid->epin_buf, (uint16_t) xferred_bytes);
+ tud_hid_report_failed_cb(instance, HID_REPORT_TYPE_INPUT, p_epbuf->epin, (uint16_t) xferred_bytes);
}
} else {
// Output report
if (XFER_RESULT_SUCCESS == result) {
- tud_hid_set_report_cb(instance, 0, HID_REPORT_TYPE_OUTPUT, p_hid->epout_buf, (uint16_t)xferred_bytes);
+ tud_hid_set_report_cb(instance, 0, HID_REPORT_TYPE_OUTPUT, p_epbuf->epout, (uint16_t)xferred_bytes);
} else {
- tud_hid_report_failed_cb(instance, HID_REPORT_TYPE_OUTPUT, p_hid->epout_buf, (uint16_t) xferred_bytes);
+ tud_hid_report_failed_cb(instance, HID_REPORT_TYPE_OUTPUT, p_epbuf->epout, (uint16_t) xferred_bytes);
}
// prepare for new transfer
- TU_ASSERT(usbd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf)));
+ TU_ASSERT(usbd_edpt_xfer(rhport, p_hid->ep_out, p_epbuf->epout, CFG_TUD_HID_EP_BUFSIZE));
}
return true;
diff --git a/src/class/hid/hid_host.c b/src/class/hid/hid_host.c
index 7639a8fc6..eef584d74 100644
--- a/src/class/hid/hid_host.c
+++ b/src/class/hid/hid_host.c
@@ -45,12 +45,11 @@
//--------------------------------------------------------------------+
typedef struct {
uint8_t daddr;
-
uint8_t itf_num;
uint8_t ep_in;
uint8_t ep_out;
- bool mounted; // Enumeration is complete
+ bool mounted; // Enumeration is complete
uint8_t itf_protocol; // None, Keyboard, Mouse
uint8_t protocol_mode; // Boot (0) or Report protocol (1)
@@ -59,15 +58,17 @@ typedef struct {
uint16_t epin_size;
uint16_t epout_size;
-
- CFG_TUH_MEM_ALIGN uint8_t epin_buf[CFG_TUH_HID_EPIN_BUFSIZE];
- CFG_TUH_MEM_ALIGN uint8_t epout_buf[CFG_TUH_HID_EPOUT_BUFSIZE];
} hidh_interface_t;
-CFG_TUH_MEM_SECTION
-tu_static hidh_interface_t _hidh_itf[CFG_TUH_HID];
+typedef struct {
+ TUH_EPBUF_DEF(epin, CFG_TUH_HID_EPIN_BUFSIZE);
+ TUH_EPBUF_DEF(epout, CFG_TUH_HID_EPOUT_BUFSIZE);
+} hidh_epbuf_t;
+
+static hidh_interface_t _hidh_itf[CFG_TUH_HID];
+CFG_TUH_MEM_SECTION static hidh_epbuf_t _hidh_epbuf[CFG_TUH_HID];
-tu_static uint8_t _hidh_default_protocol = HID_PROTOCOL_BOOT;
+static uint8_t _hidh_default_protocol = HID_PROTOCOL_BOOT;
//--------------------------------------------------------------------+
// Helper
@@ -78,6 +79,10 @@ TU_ATTR_ALWAYS_INLINE static inline hidh_interface_t* get_hid_itf(uint8_t daddr,
return (p_hid->daddr == daddr) ? p_hid : NULL;
}
+TU_ATTR_ALWAYS_INLINE static inline hidh_epbuf_t* get_hid_epbuf(uint8_t idx) {
+ return &_hidh_epbuf[idx];
+}
+
// Get instance ID by endpoint address
static uint8_t get_idx_by_epaddr(uint8_t daddr, uint8_t ep_addr) {
for (uint8_t idx = 0; idx < CFG_TUH_HID; idx++) {
@@ -353,11 +358,12 @@ bool tuh_hid_receive_ready(uint8_t dev_addr, uint8_t idx) {
bool tuh_hid_receive_report(uint8_t daddr, uint8_t idx) {
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
TU_VERIFY(p_hid);
+ hidh_epbuf_t* epbuf = get_hid_epbuf(idx);
// claim endpoint
TU_VERIFY(usbh_edpt_claim(daddr, p_hid->ep_in));
- if (!usbh_edpt_xfer(daddr, p_hid->ep_in, p_hid->epin_buf, p_hid->epin_size)) {
+ if (!usbh_edpt_xfer(daddr, p_hid->ep_in, epbuf->epin, p_hid->epin_size)) {
usbh_edpt_release(daddr, p_hid->ep_in);
return false;
}
@@ -381,6 +387,7 @@ bool tuh_hid_send_report(uint8_t daddr, uint8_t idx, uint8_t report_id, const vo
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
TU_VERIFY(p_hid);
+ hidh_epbuf_t* epbuf = get_hid_epbuf(idx);
if (p_hid->ep_out == 0) {
// This HID does not have an out endpoint (other than control)
@@ -396,16 +403,16 @@ bool tuh_hid_send_report(uint8_t daddr, uint8_t idx, uint8_t report_id, const vo
if (report_id == 0) {
// No report ID in transmission
- memcpy(&p_hid->epout_buf[0], report, len);
+ memcpy(&epbuf->epout[0], report, len);
} else {
- p_hid->epout_buf[0] = report_id;
- memcpy(&p_hid->epout_buf[1], report, len);
+ epbuf->epout[0] = report_id;
+ memcpy(&epbuf->epout[1], report, len);
++len; // 1 more byte for report_id
}
TU_LOG3_MEM(p_hid->epout_buf, len, 2);
- if (!usbh_edpt_xfer(daddr, p_hid->ep_out, p_hid->epout_buf, len)) {
+ if (!usbh_edpt_xfer(daddr, p_hid->ep_out, epbuf->epout, len)) {
usbh_edpt_release(daddr, p_hid->ep_out);
return false;
}
@@ -434,14 +441,15 @@ bool hidh_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
TU_VERIFY(p_hid);
+ hidh_epbuf_t* epbuf = get_hid_epbuf(idx);
if (dir == TUSB_DIR_IN) {
TU_LOG_DRV(" Get Report callback (%u, %u)\r\n", daddr, idx);
TU_LOG3_MEM(p_hid->epin_buf, xferred_bytes, 2);
- tuh_hid_report_received_cb(daddr, idx, p_hid->epin_buf, (uint16_t) xferred_bytes);
+ tuh_hid_report_received_cb(daddr, idx, epbuf->epin, (uint16_t) xferred_bytes);
} else {
if (tuh_hid_report_sent_cb) {
- tuh_hid_report_sent_cb(daddr, idx, p_hid->epout_buf, (uint16_t) xferred_bytes);
+ tuh_hid_report_sent_cb(daddr, idx, epbuf->epout, (uint16_t) xferred_bytes);
}
}
diff --git a/src/class/midi/midi_device.c b/src/class/midi/midi_device.c
index 42905ab0d..c545f8287 100644
--- a/src/class/midi/midi_device.c
+++ b/src/class/midi/midi_device.c
@@ -40,15 +40,13 @@
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
-typedef struct
-{
+typedef struct {
uint8_t buffer[4];
uint8_t index;
uint8_t total;
-}midid_stream_t;
+} midid_stream_t;
-typedef struct
-{
+typedef struct {
uint8_t itf_num;
uint8_t ep_in;
uint8_t ep_out;
@@ -70,36 +68,36 @@ typedef struct
osal_mutex_def_t rx_ff_mutex;
osal_mutex_def_t tx_ff_mutex;
#endif
-
- // Endpoint Transfer buffer
- CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_MIDI_EP_BUFSIZE];
- CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_MIDI_EP_BUFSIZE];
-
} midid_interface_t;
#define ITF_MEM_RESET_SIZE offsetof(midid_interface_t, rx_ff)
+// Endpoint Transfer buffer
+CFG_TUD_MEM_SECTION static struct {
+ TUD_EPBUF_DEF(epin, CFG_TUD_MIDI_EP_BUFSIZE);
+ TUD_EPBUF_DEF(epout, CFG_TUD_MIDI_EP_BUFSIZE);
+} _midid_epbuf[CFG_TUD_MIDI];
+
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
-CFG_TUD_MEM_SECTION midid_interface_t _midid_itf[CFG_TUD_MIDI];
+static midid_interface_t _midid_itf[CFG_TUD_MIDI];
-bool tud_midi_n_mounted (uint8_t itf)
-{
+bool tud_midi_n_mounted (uint8_t itf) {
midid_interface_t* midi = &_midid_itf[itf];
return midi->ep_in && midi->ep_out;
}
-static void _prep_out_transaction (midid_interface_t* p_midi)
-{
- uint8_t const rhport = 0;
+static void _prep_out_transaction(uint8_t idx) {
+ const uint8_t rhport = 0;
+ midid_interface_t* p_midi = &_midid_itf[idx];
uint16_t available = tu_fifo_remaining(&p_midi->rx_ff);
// Prepare for incoming data but only allow what we can store in the ring buffer.
// TODO Actually we can still carry out the transfer, keeping count of received bytes
// and slowly move it to the FIFO when read().
// This pre-check reduces endpoint claiming
- TU_VERIFY(available >= sizeof(p_midi->epout_buf), );
+ TU_VERIFY(available >= CFG_TUD_MIDI_EP_BUFSIZE, );
// claim endpoint
TU_VERIFY(usbd_edpt_claim(rhport, p_midi->ep_out), );
@@ -107,8 +105,8 @@ static void _prep_out_transaction (midid_interface_t* p_midi)
// fifo can be changed before endpoint is claimed
available = tu_fifo_remaining(&p_midi->rx_ff);
- if ( available >= sizeof(p_midi->epout_buf) ) {
- usbd_edpt_xfer(rhport, p_midi->ep_out, p_midi->epout_buf, sizeof(p_midi->epout_buf));
+ if ( available >= CFG_TUD_MIDI_EP_BUFSIZE ) {
+ usbd_edpt_xfer(rhport, p_midi->ep_out, _midid_epbuf[idx].epout, CFG_TUD_MIDI_EP_BUFSIZE);
}else
{
// Release endpoint since we don't make any transfer
@@ -124,7 +122,7 @@ uint32_t tud_midi_n_available(uint8_t itf, uint8_t cable_num)
(void) cable_num;
midid_interface_t* midi = &_midid_itf[itf];
- midid_stream_t const* stream = &midi->stream_read;
+ const midid_stream_t* stream = &midi->stream_read;
// when using with packet API stream total & index are both zero
return tu_fifo_count(&midi->rx_ff) + (uint8_t) (stream->total - stream->index);
@@ -205,8 +203,8 @@ bool tud_midi_n_packet_read (uint8_t itf, uint8_t packet[4])
midid_interface_t* midi = &_midid_itf[itf];
TU_VERIFY(midi->ep_out);
- uint32_t const num_read = tu_fifo_read_n(&midi->rx_ff, packet, 4);
- _prep_out_transaction(midi);
+ const uint32_t num_read = tu_fifo_read_n(&midi->rx_ff, packet, 4);
+ _prep_out_transaction(itf);
return (num_read == 4);
}
@@ -214,31 +212,31 @@ bool tud_midi_n_packet_read (uint8_t itf, uint8_t packet[4])
// WRITE API
//--------------------------------------------------------------------+
-static uint32_t write_flush(midid_interface_t* midi)
-{
- // No data to send
- if ( !tu_fifo_count(&midi->tx_ff) ) return 0;
+static uint32_t write_flush(uint8_t idx) {
+ midid_interface_t* midi = &_midid_itf[idx];
- uint8_t const rhport = 0;
+ if (!tu_fifo_count(&midi->tx_ff)) {
+ return 0; // No data to send
+ }
+
+ const uint8_t rhport = 0;
// skip if previous transfer not complete
TU_VERIFY( usbd_edpt_claim(rhport, midi->ep_in), 0 );
- uint16_t count = tu_fifo_read_n(&midi->tx_ff, midi->epin_buf, CFG_TUD_MIDI_EP_BUFSIZE);
+ uint16_t count = tu_fifo_read_n(&midi->tx_ff, _midid_epbuf[idx].epin, CFG_TUD_MIDI_EP_BUFSIZE);
- if (count)
- {
- TU_ASSERT( usbd_edpt_xfer(rhport, midi->ep_in, midi->epin_buf, count), 0 );
+ if (count) {
+ TU_ASSERT( usbd_edpt_xfer(rhport, midi->ep_in, _midid_epbuf[idx].epin, count), 0 );
return count;
- }else
- {
+ }else {
// Release endpoint since we don't make any transfer
usbd_edpt_release(rhport, midi->ep_in);
return 0;
}
}
-uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, uint8_t const* buffer, uint32_t bufsize)
+uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, const uint8_t* buffer, uint32_t bufsize)
{
midid_interface_t* midi = &_midid_itf[itf];
TU_VERIFY(midi->ep_in, 0);
@@ -248,14 +246,13 @@ uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, uint8_t const*
uint32_t i = 0;
while ( (i < bufsize) && (tu_fifo_remaining(&midi->tx_ff) >= 4) )
{
- uint8_t const data = buffer[i];
+ const uint8_t data = buffer[i];
i++;
if ( stream->index == 0 )
{
//------------- New event packet -------------//
-
- uint8_t const msg = data >> 4;
+ const uint8_t msg = data >> 4;
stream->index = 2;
stream->buffer[1] = data;
@@ -340,9 +337,11 @@ uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, uint8_t const*
if ( stream->index == stream->total )
{
// zeroes unused bytes
- for(uint8_t idx = stream->total; idx < 4; idx++) stream->buffer[idx] = 0;
+ for (uint8_t idx = stream->total; idx < 4; idx++) {
+ stream->buffer[idx] = 0;
+ }
- uint16_t const count = tu_fifo_write_n(&midi->tx_ff, stream->buffer, 4);
+ const uint16_t count = tu_fifo_write_n(&midi->tx_ff, stream->buffer, 4);
// complete current event packet, reset stream
stream->index = stream->total = 0;
@@ -352,20 +351,21 @@ uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, uint8_t const*
}
}
- write_flush(midi);
+ write_flush(itf);
return i;
}
-bool tud_midi_n_packet_write (uint8_t itf, uint8_t const packet[4])
-{
+bool tud_midi_n_packet_write (uint8_t itf, const uint8_t packet[4]) {
midid_interface_t* midi = &_midid_itf[itf];
TU_VERIFY(midi->ep_in);
- if (tu_fifo_remaining(&midi->tx_ff) < 4) return false;
+ if (tu_fifo_remaining(&midi->tx_ff) < 4) {
+ return false;
+ }
tu_fifo_write_n(&midi->tx_ff, packet, 4);
- write_flush(midi);
+ write_flush(itf);
return true;
}
@@ -429,7 +429,7 @@ void midid_reset(uint8_t rhport)
}
}
-uint16_t midid_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t max_len)
+uint16_t midid_open(uint8_t rhport, const tusb_desc_interface_t* desc_itf, uint16_t max_len)
{
// 1st Interface is Audio Control v1
TU_VERIFY(TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass &&
@@ -437,7 +437,7 @@ uint16_t midid_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint
AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_itf->bInterfaceProtocol, 0);
uint16_t drv_len = tu_desc_len(desc_itf);
- uint8_t const * p_desc = tu_desc_next(desc_itf);
+ const uint8_t* p_desc = tu_desc_next(desc_itf);
// Skip Class Specific descriptors
while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len )
@@ -448,7 +448,7 @@ uint16_t midid_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint
// 2nd Interface is MIDI Streaming
TU_VERIFY(TUSB_DESC_INTERFACE == tu_desc_type(p_desc), 0);
- tusb_desc_interface_t const * desc_midi = (tusb_desc_interface_t const *) p_desc;
+ const tusb_desc_interface_t* desc_midi = (const tusb_desc_interface_t*) p_desc;
TU_VERIFY(TUSB_CLASS_AUDIO == desc_midi->bInterfaceClass &&
AUDIO_SUBCLASS_MIDI_STREAMING == desc_midi->bInterfaceSubClass &&
@@ -456,11 +456,10 @@ uint16_t midid_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint
// Find available interface
midid_interface_t * p_midi = NULL;
- for(uint8_t i=0; i<CFG_TUD_MIDI; i++)
- {
- if ( _midid_itf[i].ep_in == 0 && _midid_itf[i].ep_out == 0 )
- {
- p_midi = &_midid_itf[i];
+ uint8_t idx;
+ for(idx=0; idx<CFG_TUD_MIDI; idx++) {
+ if ( _midid_itf[idx].ep_in == 0 && _midid_itf[idx].ep_out == 0 ) {
+ p_midi = &_midid_itf[idx];
break;
}
}
@@ -479,8 +478,8 @@ uint16_t midid_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint
{
if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
{
- TU_ASSERT(usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), 0);
- uint8_t ep_addr = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
+ TU_ASSERT(usbd_edpt_open(rhport, (const tusb_desc_endpoint_t*) p_desc), 0);
+ uint8_t ep_addr = ((const tusb_desc_endpoint_t*) p_desc)->bEndpointAddress;
if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN)
{
@@ -501,7 +500,7 @@ uint16_t midid_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint
}
// Prepare for incoming data
- _prep_out_transaction(p_midi);
+ _prep_out_transaction(idx);
return drv_len;
}
@@ -509,14 +508,9 @@ uint16_t midid_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint
// Invoked when a control transfer occurred on an interface of this class
// Driver response accordingly to the request and the transfer stage (setup/data/ack)
// return false to stall control endpoint (e.g unsupported request)
-bool midid_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
-{
- (void) rhport;
- (void) stage;
- (void) request;
-
- // driver doesn't support any request yet
- return false;
+bool midid_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_request_t* request) {
+ (void) rhport; (void) stage; (void) request;
+ return false; // driver doesn't support any request yet
}
bool midid_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
@@ -524,40 +518,37 @@ bool midid_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32
(void) result;
(void) rhport;
- uint8_t itf;
+ uint8_t idx;
midid_interface_t* p_midi;
// Identify which interface to use
- for (itf = 0; itf < CFG_TUD_MIDI; itf++)
- {
- p_midi = &_midid_itf[itf];
- if ( ( ep_addr == p_midi->ep_out ) || ( ep_addr == p_midi->ep_in ) ) break;
+ for (idx = 0; idx < CFG_TUD_MIDI; idx++) {
+ p_midi = &_midid_itf[idx];
+ if ((ep_addr == p_midi->ep_out) || (ep_addr == p_midi->ep_in)) {
+ break;
+ }
}
- TU_ASSERT(itf < CFG_TUD_MIDI);
+ TU_ASSERT(idx < CFG_TUD_MIDI);
// receive new data
- if ( ep_addr == p_midi->ep_out )
- {
- tu_fifo_write_n(&p_midi->rx_ff, p_midi->epout_buf, (uint16_t) xferred_bytes);
+ if (ep_addr == p_midi->ep_out) {
+ tu_fifo_write_n(&p_midi->rx_ff, _midid_epbuf[idx].epout, (uint16_t)xferred_bytes);
// invoke receive callback if available
- if (tud_midi_rx_cb) tud_midi_rx_cb(itf);
+ if (tud_midi_rx_cb) {
+ tud_midi_rx_cb(idx);
+ }
// prepare for next
// TODO for now ep_out is not used by public API therefore there is no race condition,
// and does not need to claim like ep_in
- _prep_out_transaction(p_midi);
- }
- else if ( ep_addr == p_midi->ep_in )
- {
- if (0 == write_flush(p_midi))
- {
+ _prep_out_transaction(idx);
+ } else if (ep_addr == p_midi->ep_in) {
+ if (0 == write_flush(idx)) {
// If there is no data left, a ZLP should be sent if
// xferred_bytes is multiple of EP size and not zero
- if ( !tu_fifo_count(&p_midi->tx_ff) && xferred_bytes && (0 == (xferred_bytes % CFG_TUD_MIDI_EP_BUFSIZE)) )
- {
- if ( usbd_edpt_claim(rhport, p_midi->ep_in) )
- {
+ if (!tu_fifo_count(&p_midi->tx_ff) && xferred_bytes && (0 == (xferred_bytes % CFG_TUD_MIDI_EP_BUFSIZE))) {
+ if (usbd_edpt_claim(rhport, p_midi->ep_in)) {
usbd_edpt_xfer(rhport, p_midi->ep_in, NULL, 0);
}
}
diff --git a/src/class/msc/msc_device.c b/src/class/msc/msc_device.c
index 447560b4d..dd66bfb6f 100644
--- a/src/class/msc/msc_device.c
+++ b/src/class/msc/msc_device.c
@@ -44,8 +44,7 @@
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
-enum
-{
+enum {
MSC_STAGE_CMD = 0,
MSC_STAGE_DATA,
MSC_STAGE_STATUS,
@@ -53,11 +52,9 @@ enum
MSC_STAGE_NEED_RESET,
};
-typedef struct
-{
- // TODO optimize alignment
- CFG_TUSB_MEM_ALIGN msc_cbw_t cbw;
- CFG_TUSB_MEM_ALIGN msc_csw_t csw;
+typedef struct {
+ TU_ATTR_ALIGNED(4) msc_cbw_t cbw;
+ TU_ATTR_ALIGNED(4) msc_csw_t csw;
uint8_t itf_num;
uint8_t ep_in;
@@ -65,6 +62,7 @@ typedef struct
// Bulk Only Transfer (BOT) Protocol
uint8_t stage;
+
uint32_t total_len; // byte to be transferred, can be smaller than total_bytes in cbw
uint32_t xferred_len; // numbered of bytes transferred so far in the Data Stage
@@ -74,8 +72,11 @@ typedef struct
uint8_t add_sense_qualifier;
}mscd_interface_t;
-CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static mscd_interface_t _mscd_itf;
-CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static uint8_t _mscd_buf[CFG_TUD_MSC_EP_BUFSIZE];
+static mscd_interface_t _mscd_itf;
+
+CFG_TUD_MEM_SECTION static struct {
+ TUD_EPBUF_DEF(buf, CFG_TUD_MSC_EP_BUFSIZE);
+} _mscd_epbuf;
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
@@ -86,28 +87,24 @@ static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc);
static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc);
static void proc_write10_new_data(uint8_t rhport, mscd_interface_t* p_msc, uint32_t xferred_bytes);
-TU_ATTR_ALWAYS_INLINE static inline bool is_data_in(uint8_t dir)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool is_data_in(uint8_t dir) {
return tu_bit_test(dir, 7);
}
-static inline bool send_csw(uint8_t rhport, mscd_interface_t* p_msc)
-{
+static inline bool send_csw(uint8_t rhport, mscd_interface_t* p_msc) {
// Data residue is always = host expect - actual transferred
p_msc->csw.data_residue = p_msc->cbw.total_bytes - p_msc->xferred_len;
-
p_msc->stage = MSC_STAGE_STATUS_SENT;
- return usbd_edpt_xfer(rhport, p_msc->ep_in , (uint8_t*) &p_msc->csw, sizeof(msc_csw_t));
+ memcpy(_mscd_epbuf.buf, &p_msc->csw, sizeof(msc_csw_t));
+ return usbd_edpt_xfer(rhport, p_msc->ep_in , _mscd_epbuf.buf, sizeof(msc_csw_t));
}
-static inline bool prepare_cbw(uint8_t rhport, mscd_interface_t* p_msc)
-{
+static inline bool prepare_cbw(uint8_t rhport, mscd_interface_t* p_msc) {
p_msc->stage = MSC_STAGE_CMD;
- return usbd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t));
+ return usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_epbuf.buf, sizeof(msc_cbw_t));
}
-static void fail_scsi_op(uint8_t rhport, mscd_interface_t* p_msc, uint8_t status)
-{
+static void fail_scsi_op(uint8_t rhport, mscd_interface_t* p_msc, uint8_t status) {
msc_cbw_t const * p_cbw = &p_msc->cbw;
msc_csw_t * p_csw = &p_msc->csw;
@@ -116,82 +113,62 @@ static void fail_scsi_op(uint8_t rhport, mscd_interface_t* p_msc, uint8_t status
p_msc->stage = MSC_STAGE_STATUS;
// failed but sense key is not set: default to Illegal Request
- if ( p_msc->sense_key == 0 ) tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
+ if (p_msc->sense_key == 0) {
+ tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
+ }
// If there is data stage and not yet complete, stall it
- if ( p_cbw->total_bytes && p_csw->data_residue )
- {
- if ( is_data_in(p_cbw->dir) )
- {
+ if (p_cbw->total_bytes && p_csw->data_residue) {
+ if (is_data_in(p_cbw->dir)) {
usbd_edpt_stall(rhport, p_msc->ep_in);
- }
- else
- {
+ } else {
usbd_edpt_stall(rhport, p_msc->ep_out);
}
}
}
-static inline uint32_t rdwr10_get_lba(uint8_t const command[])
-{
+static inline uint32_t rdwr10_get_lba(uint8_t const command[]) {
// use offsetof to avoid pointer to the odd/unaligned address
- uint32_t const lba = tu_unaligned_read32(command + offsetof(scsi_write10_t, lba));
-
- // lba is in Big Endian
- return tu_ntohl(lba);
+ const uint32_t lba = tu_unaligned_read32(command + offsetof(scsi_write10_t, lba));
+ return tu_ntohl(lba); // lba is in Big Endian
}
-static inline uint16_t rdwr10_get_blockcount(msc_cbw_t const* cbw)
-{
+static inline uint16_t rdwr10_get_blockcount(msc_cbw_t const* cbw) {
uint16_t const block_count = tu_unaligned_read16(cbw->command + offsetof(scsi_write10_t, block_count));
return tu_ntohs(block_count);
}
-static inline uint16_t rdwr10_get_blocksize(msc_cbw_t const* cbw)
-{
+static inline uint16_t rdwr10_get_blocksize(msc_cbw_t const* cbw) {
// first extract block count in the command
uint16_t const block_count = rdwr10_get_blockcount(cbw);
-
- // invalid block count
- if (block_count == 0) return 0;
-
+ if (block_count == 0) {
+ return 0; // invalid block count
+ }
return (uint16_t) (cbw->total_bytes / block_count);
}
-uint8_t rdwr10_validate_cmd(msc_cbw_t const* cbw)
-{
+static uint8_t rdwr10_validate_cmd(msc_cbw_t const* cbw) {
uint8_t status = MSC_CSW_STATUS_PASSED;
uint16_t const block_count = rdwr10_get_blockcount(cbw);
- if ( cbw->total_bytes == 0 )
- {
- if ( block_count )
- {
+ if (cbw->total_bytes == 0) {
+ if (block_count) {
TU_LOG_DRV(" SCSI case 2 (Hn < Di) or case 3 (Hn < Do) \r\n");
status = MSC_CSW_STATUS_PHASE_ERROR;
- }else
- {
+ } else {
// no data transfer, only exist in complaint test suite
}
- }else
- {
- if ( SCSI_CMD_READ_10 == cbw->command[0] && !is_data_in(cbw->dir) )
- {
+ } else {
+ if (SCSI_CMD_READ_10 == cbw->command[0] && !is_data_in(cbw->dir)) {
TU_LOG_DRV(" SCSI case 10 (Ho <> Di)\r\n");
status = MSC_CSW_STATUS_PHASE_ERROR;
- }
- else if ( SCSI_CMD_WRITE_10 == cbw->command[0] && is_data_in(cbw->dir) )
- {
+ } else if (SCSI_CMD_WRITE_10 == cbw->command[0] && is_data_in(cbw->dir)) {
TU_LOG_DRV(" SCSI case 8 (Hi <> Do)\r\n");
status = MSC_CSW_STATUS_PHASE_ERROR;
- }
- else if ( 0 == block_count )
- {
+ } else if (0 == block_count) {
TU_LOG_DRV(" SCSI case 4 Hi > Dn (READ10) or case 9 Ho > Dn (WRITE10) \r\n");
- status = MSC_CSW_STATUS_FAILED;
- }
- else if ( cbw->total_bytes / block_count == 0 )
- {
+ status = MSC_CSW_STATUS_FAILED;
+ } else if (cbw->total_bytes / block_count == 0) {
TU_LOG_DRV(" Computed block size = 0. SCSI case 7 Hi < Di (READ10) or case 13 Ho < Do (WRIT10)\r\n");
status = MSC_CSW_STATUS_PHASE_ERROR;
}
@@ -205,8 +182,7 @@ uint8_t rdwr10_validate_cmd(msc_cbw_t const* cbw)
//--------------------------------------------------------------------+
#if CFG_TUSB_DEBUG >= CFG_TUD_MSC_LOG_LEVEL
-TU_ATTR_UNUSED tu_static tu_lookup_entry_t const _msc_scsi_cmd_lookup[] =
-{
+TU_ATTR_UNUSED tu_static tu_lookup_entry_t const _msc_scsi_cmd_lookup[] = {
{ .key = SCSI_CMD_TEST_UNIT_READY , .data = "Test Unit Ready" },
{ .key = SCSI_CMD_INQUIRY , .data = "Inquiry" },
{ .key = SCSI_CMD_MODE_SELECT_6 , .data = "Mode_Select 6" },
@@ -220,8 +196,7 @@ TU_ATTR_UNUSED tu_static tu_lookup_entry_t const _msc_scsi_cmd_lookup[] =
{ .key = SCSI_CMD_WRITE_10 , .data = "Write10" }
};
-TU_ATTR_UNUSED tu_static tu_lookup_table_t const _msc_scsi_cmd_table =
-{
+TU_ATTR_UNUSED tu_static tu_lookup_table_t const _msc_scsi_cmd_table = {
.count = TU_ARRAY_SIZE(_msc_scsi_cmd_lookup),
.items = _msc_scsi_cmd_lookup
};
@@ -231,19 +206,15 @@ TU_ATTR_UNUSED tu_static tu_lookup_table_t const _msc_scsi_cmd_table =
//--------------------------------------------------------------------+
// APPLICATION API
//--------------------------------------------------------------------+
-bool tud_msc_set_sense(uint8_t lun, uint8_t sense_key, uint8_t add_sense_code, uint8_t add_sense_qualifier)
-{
+bool tud_msc_set_sense(uint8_t lun, uint8_t sense_key, uint8_t add_sense_code, uint8_t add_sense_qualifier) {
(void) lun;
-
_mscd_itf.sense_key = sense_key;
_mscd_itf.add_sense_code = add_sense_code;
_mscd_itf.add_sense_qualifier = add_sense_qualifier;
-
return true;
}
-static inline void set_sense_medium_not_present(uint8_t lun)
-{
+static inline void set_sense_medium_not_present(uint8_t lun) {
// default sense is NOT READY, MEDIUM NOT PRESENT
tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3A, 0x00);
}
@@ -256,47 +227,38 @@ void mscd_init(void) {
}
bool mscd_deinit(void) {
- // nothing to do
- return true;
+ return true; // nothing to do
}
-void mscd_reset(uint8_t rhport)
-{
+void mscd_reset(uint8_t rhport) {
(void) rhport;
tu_memclr(&_mscd_itf, sizeof(mscd_interface_t));
}
-uint16_t mscd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
-{
+uint16_t mscd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len) {
// only support SCSI's BOT protocol
TU_VERIFY(TUSB_CLASS_MSC == itf_desc->bInterfaceClass &&
MSC_SUBCLASS_SCSI == itf_desc->bInterfaceSubClass &&
MSC_PROTOCOL_BOT == itf_desc->bInterfaceProtocol, 0);
-
- // msc driver length is fixed
uint16_t const drv_len = sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
-
- // Max length must be at least 1 interface + 2 endpoints
- TU_ASSERT(max_len >= drv_len, 0);
+ TU_ASSERT(max_len >= drv_len, 0); // Max length must be at least 1 interface + 2 endpoints
mscd_interface_t * p_msc = &_mscd_itf;
p_msc->itf_num = itf_desc->bInterfaceNumber;
// Open endpoint pair
- TU_ASSERT( usbd_open_edpt_pair(rhport, tu_desc_next(itf_desc), 2, TUSB_XFER_BULK, &p_msc->ep_out, &p_msc->ep_in), 0 );
+ TU_ASSERT(usbd_open_edpt_pair(rhport, tu_desc_next(itf_desc), 2, TUSB_XFER_BULK, &p_msc->ep_out, &p_msc->ep_in), 0);
// Prepare for Command Block Wrapper
- TU_ASSERT( prepare_cbw(rhport, p_msc), drv_len);
+ TU_ASSERT(prepare_cbw(rhport, p_msc), drv_len);
return drv_len;
}
-static void proc_bot_reset(mscd_interface_t* p_msc)
-{
+static void proc_bot_reset(mscd_interface_t* p_msc) {
p_msc->stage = MSC_STAGE_CMD;
p_msc->total_len = 0;
p_msc->xferred_len = 0;
-
p_msc->sense_key = 0;
p_msc->add_sense_code = 0;
p_msc->add_sense_qualifier = 0;
@@ -305,10 +267,10 @@ static void proc_bot_reset(mscd_interface_t* p_msc)
// Invoked when a control transfer occurred on an interface of this class
// Driver response accordingly to the request and the transfer stage (setup/data/ack)
// return false to stall control endpoint (e.g unsupported request)
-bool mscd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
-{
- // nothing to do with DATA & ACK stage
- if (stage != CONTROL_STAGE_SETUP) return true;
+bool mscd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) {
+ if (stage != CONTROL_STAGE_SETUP) {
+ return true; // nothing to do with DATA & ACK stage
+ }
mscd_interface_t* p_msc = &_mscd_itf;
@@ -316,35 +278,25 @@ bool mscd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t
if ( TUSB_REQ_TYPE_STANDARD == request->bmRequestType_bit.type &&
TUSB_REQ_RCPT_ENDPOINT == request->bmRequestType_bit.recipient &&
TUSB_REQ_CLEAR_FEATURE == request->bRequest &&
- TUSB_REQ_FEATURE_EDPT_HALT == request->wValue )
- {
+ TUSB_REQ_FEATURE_EDPT_HALT == request->wValue ) {
uint8_t const ep_addr = tu_u16_low(request->wIndex);
- if ( p_msc->stage == MSC_STAGE_NEED_RESET )
- {
+ if (p_msc->stage == MSC_STAGE_NEED_RESET) {
// reset recovery is required to recover from this stage
// Clear Stall request cannot resolve this -> continue to stall endpoint
usbd_edpt_stall(rhport, ep_addr);
- }
- else
- {
- if ( ep_addr == p_msc->ep_in )
- {
- if ( p_msc->stage == MSC_STAGE_STATUS )
- {
+ } else {
+ if (ep_addr == p_msc->ep_in) {
+ if (p_msc->stage == MSC_STAGE_STATUS) {
// resume sending SCSI status if we are in this stage previously before stalled
- TU_ASSERT( send_csw(rhport, p_msc) );
+ TU_ASSERT(send_csw(rhport, p_msc));
}
- }
- else if ( ep_addr == p_msc->ep_out )
- {
- if ( p_msc->stage == MSC_STAGE_CMD )
- {
+ } else if (ep_addr == p_msc->ep_out) {
+ if (p_msc->stage == MSC_STAGE_CMD) {
// part of reset recovery (probably due to invalid CBW) -> prepare for new command
// Note: skip if already queued previously
- if ( usbd_edpt_ready(rhport, p_msc->ep_out) )
- {
- TU_ASSERT( prepare_cbw(rhport, p_msc) );
+ if (usbd_edpt_ready(rhport, p_msc->ep_out)) {
+ TU_ASSERT(prepare_cbw(rhport, p_msc));
}
}
}
@@ -356,33 +308,27 @@ bool mscd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t
// From this point only handle class request only
TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
- switch ( request->bRequest )
- {
+ switch ( request->bRequest ) {
case MSC_REQ_RESET:
TU_LOG_DRV(" MSC BOT Reset\r\n");
TU_VERIFY(request->wValue == 0 && request->wLength == 0);
-
- // driver state reset
- proc_bot_reset(p_msc);
-
+ proc_bot_reset(p_msc); // driver state reset
tud_control_status(rhport, request);
break;
- case MSC_REQ_GET_MAX_LUN:
- {
+ case MSC_REQ_GET_MAX_LUN: {
TU_LOG_DRV(" MSC Get Max Lun\r\n");
TU_VERIFY(request->wValue == 0 && request->wLength == 1);
uint8_t maxlun = 1;
- if (tud_msc_get_maxlun_cb) maxlun = tud_msc_get_maxlun_cb();
+ if (tud_msc_get_maxlun_cb) {
+ maxlun = tud_msc_get_maxlun_cb();
+ }
TU_VERIFY(maxlun);
-
- // MAX LUN is minus 1 by specs
- maxlun--;
-
+ maxlun--; // MAX LUN is minus 1 by specs
tud_control_xfer(rhport, request, &maxlun, 1);
+ break;
}
- break;
default: return false; // stall unsupported request
}
@@ -390,35 +336,34 @@ bool mscd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t
return true;
}
-bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
-{
+bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) {
(void) event;
mscd_interface_t* p_msc = &_mscd_itf;
- msc_cbw_t const * p_cbw = &p_msc->cbw;
- msc_csw_t * p_csw = &p_msc->csw;
+ msc_cbw_t * p_cbw = &p_msc->cbw;
+ msc_csw_t * p_csw = &p_msc->csw;
- switch (p_msc->stage)
- {
+ switch (p_msc->stage) {
case MSC_STAGE_CMD:
//------------- new CBW received -------------//
// Complete IN while waiting for CMD is usually Status of previous SCSI op, ignore it
- if(ep_addr != p_msc->ep_out) return true;
+ if (ep_addr != p_msc->ep_out) {
+ return true;
+ }
- if ( !(xferred_bytes == sizeof(msc_cbw_t) && p_cbw->signature == MSC_CBW_SIGNATURE) )
- {
- TU_LOG_DRV(" SCSI CBW is not valid\r\n");
+ const uint32_t signature = tu_le32toh(tu_unaligned_read32(_mscd_epbuf.buf));
+ if (!(xferred_bytes == sizeof(msc_cbw_t) && signature == MSC_CBW_SIGNATURE)) {
// BOT 6.6.1 If CBW is not valid stall both endpoints until reset recovery
+ TU_LOG_DRV(" SCSI CBW is not valid\r\n");
p_msc->stage = MSC_STAGE_NEED_RESET;
-
- // invalid CBW stall both endpoints
usbd_edpt_stall(rhport, p_msc->ep_in);
usbd_edpt_stall(rhport, p_msc->ep_out);
-
return false;
}
+ memcpy(p_cbw, _mscd_epbuf.buf, sizeof(msc_cbw_t));
+
TU_LOG_DRV(" SCSI Command [Lun%u]: %s\r\n", p_cbw->lun, tu_lookup_find(&_msc_scsi_cmd_table, p_cbw->command[0]));
//TU_LOG_MEM(MSC_DEBUG, p_cbw, xferred_bytes, 2);
@@ -433,87 +378,65 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
p_msc->xferred_len = 0;
// Read10 or Write10
- if ( (SCSI_CMD_READ_10 == p_cbw->command[0]) || (SCSI_CMD_WRITE_10 == p_cbw->command[0]) )
- {
+ if ((SCSI_CMD_READ_10 == p_cbw->command[0]) || (SCSI_CMD_WRITE_10 == p_cbw->command[0])) {
uint8_t const status = rdwr10_validate_cmd(p_cbw);
- if ( status != MSC_CSW_STATUS_PASSED)
- {
+ if (status != MSC_CSW_STATUS_PASSED) {
fail_scsi_op(rhport, p_msc, status);
- }else if ( p_cbw->total_bytes )
- {
- if (SCSI_CMD_READ_10 == p_cbw->command[0])
- {
+ } else if (p_cbw->total_bytes) {
+ if (SCSI_CMD_READ_10 == p_cbw->command[0]) {
proc_read10_cmd(rhport, p_msc);
- }else
- {
+ } else {
proc_write10_cmd(rhport, p_msc);
}
- }else
- {
+ } else {
// no data transfer, only exist in complaint test suite
p_msc->stage = MSC_STAGE_STATUS;
}
- }
- else
- {
+ } else {
// For other SCSI commands
// 1. OUT : queue transfer (invoke app callback after done)
// 2. IN & Zero: Process if is built-in, else Invoke app callback. Skip DATA if zero length
- if ( (p_cbw->total_bytes > 0 ) && !is_data_in(p_cbw->dir) )
- {
- if (p_cbw->total_bytes > sizeof(_mscd_buf))
- {
+ if ((p_cbw->total_bytes > 0) && !is_data_in(p_cbw->dir)) {
+ if (p_cbw->total_bytes > CFG_TUD_MSC_EP_BUFSIZE) {
TU_LOG_DRV(" SCSI reject non READ10/WRITE10 with large data\r\n");
fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
- }else
- {
+ } else {
// Didn't check for case 9 (Ho > Dn), which requires examining scsi command first
// but it is OK to just receive data then responded with failed status
- TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, (uint16_t) p_msc->total_len) );
+ TU_ASSERT(usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_epbuf.buf, (uint16_t) p_msc->total_len));
}
- }else
- {
+ } else {
// First process if it is a built-in commands
- int32_t resplen = proc_builtin_scsi(p_cbw->lun, p_cbw->command, _mscd_buf, sizeof(_mscd_buf));
+ int32_t resplen = proc_builtin_scsi(p_cbw->lun, p_cbw->command, _mscd_epbuf.buf, CFG_TUD_MSC_EP_BUFSIZE);
// Invoke user callback if not built-in
- if ( (resplen < 0) && (p_msc->sense_key == 0) )
- {
- resplen = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_buf, (uint16_t) p_msc->total_len);
+ if ((resplen < 0) && (p_msc->sense_key == 0)) {
+ resplen = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_epbuf.buf, (uint16_t)p_msc->total_len);
}
- if ( resplen < 0 )
- {
+ if (resplen < 0) {
// unsupported command
TU_LOG_DRV(" SCSI unsupported or failed command\r\n");
fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
- }
- else if (resplen == 0)
- {
- if (p_cbw->total_bytes)
- {
+ } else if (resplen == 0) {
+ if (p_cbw->total_bytes) {
// 6.7 The 13 Cases: case 4 (Hi > Dn)
// TU_LOG(MSC_DEBUG, " SCSI case 4 (Hi > Dn): %lu\r\n", p_cbw->total_bytes);
fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
- }else
- {
+ } else {
// case 1 Hn = Dn: all good
p_msc->stage = MSC_STAGE_STATUS;
}
- }
- else
- {
- if ( p_cbw->total_bytes == 0 )
- {
+ } else {
+ if (p_cbw->total_bytes == 0) {
// 6.7 The 13 Cases: case 2 (Hn < Di)
// TU_LOG(MSC_DEBUG, " SCSI case 2 (Hn < Di): %lu\r\n", p_cbw->total_bytes);
fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
- }else
- {
+ } else {
// cannot return more than host expect
- p_msc->total_len = tu_min32((uint32_t) resplen, p_cbw->total_bytes);
- TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, (uint16_t) p_msc->total_len) );
+ p_msc->total_len = tu_min32((uint32_t)resplen, p_cbw->total_bytes);
+ TU_ASSERT(usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_epbuf.buf, (uint16_t) p_msc->total_len));
}
}
}
@@ -522,52 +445,39 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
case MSC_STAGE_DATA:
TU_LOG_DRV(" SCSI Data [Lun%u]\r\n", p_cbw->lun);
- //TU_LOG_MEM(MSC_DEBUG, _mscd_buf, xferred_bytes, 2);
+ //TU_LOG_MEM(MSC_DEBUG, _mscd_epbuf.buf, xferred_bytes, 2);
- if (SCSI_CMD_READ_10 == p_cbw->command[0])
- {
+ if (SCSI_CMD_READ_10 == p_cbw->command[0]) {
p_msc->xferred_len += xferred_bytes;
- if ( p_msc->xferred_len >= p_msc->total_len )
- {
+ if ( p_msc->xferred_len >= p_msc->total_len ) {
// Data Stage is complete
p_msc->stage = MSC_STAGE_STATUS;
- }else
- {
+ }else {
proc_read10_cmd(rhport, p_msc);
}
- }
- else if (SCSI_CMD_WRITE_10 == p_cbw->command[0])
- {
+ } else if (SCSI_CMD_WRITE_10 == p_cbw->command[0]) {
proc_write10_new_data(rhport, p_msc, xferred_bytes);
- }
- else
- {
+ } else {
p_msc->xferred_len += xferred_bytes;
// OUT transfer, invoke callback if needed
- if ( !is_data_in(p_cbw->dir) )
- {
- int32_t cb_result = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_buf, (uint16_t) p_msc->total_len);
+ if ( !is_data_in(p_cbw->dir) ) {
+ int32_t cb_result = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_epbuf.buf, (uint16_t) p_msc->total_len);
- if ( cb_result < 0 )
- {
+ if ( cb_result < 0 ) {
// unsupported command
TU_LOG_DRV(" SCSI unsupported command\r\n");
fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
- }else
- {
+ }else {
// TODO haven't implement this scenario any further yet
}
}
- if ( p_msc->xferred_len >= p_msc->total_len )
- {
+ if ( p_msc->xferred_len >= p_msc->total_len ) {
// Data Stage is complete
p_msc->stage = MSC_STAGE_STATUS;
- }
- else
- {
+ } else {
// This scenario with command that take more than one transfer is already rejected at Command stage
TU_BREAKPOINT();
}
@@ -580,53 +490,52 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
case MSC_STAGE_STATUS_SENT:
// Wait for the Status phase to complete
- if( (ep_addr == p_msc->ep_in) && (xferred_bytes == sizeof(msc_csw_t)) )
- {
+ if ((ep_addr == p_msc->ep_in) && (xferred_bytes == sizeof(msc_csw_t))) {
TU_LOG_DRV(" SCSI Status [Lun%u] = %u\r\n", p_cbw->lun, p_csw->status);
// TU_LOG_MEM(MSC_DEBUG, p_csw, xferred_bytes, 2);
// Invoke complete callback if defined
// Note: There is racing issue with samd51 + qspi flash testing with arduino
// if complete_cb() is invoked after queuing the status.
- switch(p_cbw->command[0])
- {
+ switch (p_cbw->command[0]) {
case SCSI_CMD_READ_10:
- if ( tud_msc_read10_complete_cb ) tud_msc_read10_complete_cb(p_cbw->lun);
- break;
+ if (tud_msc_read10_complete_cb) {
+ tud_msc_read10_complete_cb(p_cbw->lun);
+ }
+ break;
case SCSI_CMD_WRITE_10:
- if ( tud_msc_write10_complete_cb ) tud_msc_write10_complete_cb(p_cbw->lun);
- break;
+ if (tud_msc_write10_complete_cb) {
+ tud_msc_write10_complete_cb(p_cbw->lun);
+ }
+ break;
default:
- if ( tud_msc_scsi_complete_cb ) tud_msc_scsi_complete_cb(p_cbw->lun, p_cbw->command);
- break;
+ if (tud_msc_scsi_complete_cb) {
+ tud_msc_scsi_complete_cb(p_cbw->lun, p_cbw->command);
+ }
+ break;
}
- TU_ASSERT( prepare_cbw(rhport, p_msc) );
- }else
- {
+ TU_ASSERT(prepare_cbw(rhport, p_msc));
+ } else {
// Any xfer ended here is consider unknown error, ignore it
TU_LOG1(" Warning expect SCSI Status but received unknown data\r\n");
}
- break;
+ break;
- default : break;
+ default: break;
}
- if ( p_msc->stage == MSC_STAGE_STATUS )
- {
+ if (p_msc->stage == MSC_STAGE_STATUS) {
// skip status if epin is currently stalled, will do it when received Clear Stall request
- if ( !usbd_edpt_stalled(rhport, p_msc->ep_in) )
- {
- if ( (p_cbw->total_bytes > p_msc->xferred_len) && is_data_in(p_cbw->dir) )
- {
+ if (!usbd_edpt_stalled(rhport, p_msc->ep_in)) {
+ if ((p_cbw->total_bytes > p_msc->xferred_len) && is_data_in(p_cbw->dir)) {
// 6.7 The 13 Cases: case 5 (Hi > Di): STALL before status
// TU_LOG(MSC_DEBUG, " SCSI case 5 (Hi > Di): %lu > %lu\r\n", p_cbw->total_bytes, p_msc->xferred_len);
usbd_edpt_stall(rhport, p_msc->ep_in);
- }else
- {
- TU_ASSERT( send_csw(rhport, p_msc) );
+ } else {
+ TU_ASSERT(send_csw(rhport, p_msc));
}
}
@@ -634,8 +543,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
// WORKAROUND: cxd56 has its own nuttx usb stack which does not forward Set/ClearFeature(Endpoint) to DCD.
// There is no way for us to know when EP is un-stall, therefore we will unconditionally un-stall here and
// hope everything will work
- if ( usbd_edpt_stalled(rhport, p_msc->ep_in) )
- {
+ if ( usbd_edpt_stalled(rhport, p_msc->ep_in) ) {
usbd_edpt_clear_stall(rhport, p_msc->ep_in);
send_csw(rhport, p_msc);
}
@@ -651,84 +559,82 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
// return response's length (copied to buffer). Negative if it is not an built-in command or indicate Failed status (CSW)
// In case of a failed status, sense key must be set for reason of failure
-static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_t* buffer, uint32_t bufsize)
-{
- (void) bufsize; // TODO refractor later
+static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_t* buffer, uint32_t bufsize) {
+ (void)bufsize; // TODO refractor later
int32_t resplen;
mscd_interface_t* p_msc = &_mscd_itf;
- switch ( scsi_cmd[0] )
- {
+ switch (scsi_cmd[0]) {
case SCSI_CMD_TEST_UNIT_READY:
resplen = 0;
- if ( !tud_msc_test_unit_ready_cb(lun) )
- {
+ if (!tud_msc_test_unit_ready_cb(lun)) {
// Failed status response
- resplen = - 1;
+ resplen = -1;
// set default sense if not set by callback
- if ( p_msc->sense_key == 0 ) set_sense_medium_not_present(lun);
+ if (p_msc->sense_key == 0) {
+ set_sense_medium_not_present(lun);
+ }
}
- break;
+ break;
case SCSI_CMD_START_STOP_UNIT:
resplen = 0;
- if (tud_msc_start_stop_cb)
- {
- scsi_start_stop_unit_t const * start_stop = (scsi_start_stop_unit_t const *) scsi_cmd;
- if ( !tud_msc_start_stop_cb(lun, start_stop->power_condition, start_stop->start, start_stop->load_eject) )
- {
+ if (tud_msc_start_stop_cb) {
+ scsi_start_stop_unit_t const* start_stop = (scsi_start_stop_unit_t const*)scsi_cmd;
+ if (!tud_msc_start_stop_cb(lun, start_stop->power_condition, start_stop->start, start_stop->load_eject)) {
// Failed status response
- resplen = - 1;
+ resplen = -1;
// set default sense if not set by callback
- if ( p_msc->sense_key == 0 ) set_sense_medium_not_present(lun);
+ if (p_msc->sense_key == 0) {
+ set_sense_medium_not_present(lun);
+ }
}
}
- break;
+ break;
case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
resplen = 0;
- if (tud_msc_prevent_allow_medium_removal_cb)
- {
- scsi_prevent_allow_medium_removal_t const * prevent_allow = (scsi_prevent_allow_medium_removal_t const *) scsi_cmd;
- if ( !tud_msc_prevent_allow_medium_removal_cb(lun, prevent_allow->prohibit_removal, prevent_allow->control) )
- {
+ if (tud_msc_prevent_allow_medium_removal_cb) {
+ scsi_prevent_allow_medium_removal_t const* prevent_allow = (scsi_prevent_allow_medium_removal_t const*)scsi_cmd;
+ if (!tud_msc_prevent_allow_medium_removal_cb(lun, prevent_allow->prohibit_removal, prevent_allow->control)) {
// Failed status response
- resplen = - 1;
+ resplen = -1;
// set default sense if not set by callback
- if ( p_msc->sense_key == 0 ) set_sense_medium_not_present(lun);
+ if (p_msc->sense_key == 0) {
+ set_sense_medium_not_present(lun);
+ }
}
}
- break;
+ break;
- case SCSI_CMD_READ_CAPACITY_10:
- {
+ case SCSI_CMD_READ_CAPACITY_10: {
uint32_t block_count;
uint32_t block_size;
uint16_t block_size_u16;
tud_msc_capacity_cb(lun, &block_count, &block_size_u16);
- block_size = (uint32_t) block_size_u16;
+ block_size = (uint32_t)block_size_u16;
// Invalid block size/count from callback, possibly unit is not ready
// stall this request, set sense key to NOT READY
- if (block_count == 0 || block_size == 0)
- {
+ if (block_count == 0 || block_size == 0) {
resplen = -1;
// set default sense if not set by callback
- if ( p_msc->sense_key == 0 ) set_sense_medium_not_present(lun);
- }else
- {
+ if (p_msc->sense_key == 0) {
+ set_sense_medium_not_present(lun);
+ }
+ } else {
scsi_read_capacity10_resp_t read_capa10;
- read_capa10.last_lba = tu_htonl(block_count-1);
+ read_capa10.last_lba = tu_htonl(block_count-1);
read_capa10.block_size = tu_htonl(block_size);
resplen = sizeof(read_capa10);
@@ -737,14 +643,13 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
}
break;
- case SCSI_CMD_READ_FORMAT_CAPACITY:
- {
+ case SCSI_CMD_READ_FORMAT_CAPACITY: {
scsi_read_format_capacity_data_t read_fmt_capa =
{
- .list_length = 8,
- .block_num = 0,
- .descriptor_type = 2, // formatted media
- .block_size_u16 = 0
+ .list_length = 8,
+ .block_num = 0,
+ .descriptor_type = 2, // formatted media
+ .block_size_u16 = 0
};
uint32_t block_count;
@@ -754,14 +659,14 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
// Invalid block size/count from callback, possibly unit is not ready
// stall this request, set sense key to NOT READY
- if (block_count == 0 || block_size == 0)
- {
+ if (block_count == 0 || block_size == 0) {
resplen = -1;
// set default sense if not set by callback
- if ( p_msc->sense_key == 0 ) set_sense_medium_not_present(lun);
- }else
- {
+ if (p_msc->sense_key == 0) {
+ set_sense_medium_not_present(lun);
+ }
+ } else {
read_fmt_capa.block_num = tu_htonl(block_count);
read_fmt_capa.block_size_u16 = tu_htons(block_size);
@@ -771,14 +676,13 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
}
break;
- case SCSI_CMD_INQUIRY:
- {
+ case SCSI_CMD_INQUIRY: {
scsi_inquiry_resp_t inquiry_rsp =
{
- .is_removable = 1,
- .version = 2,
- .response_data_format = 2,
- .additional_length = sizeof(scsi_inquiry_resp_t) - 5,
+ .is_removable = 1,
+ .version = 2,
+ .response_data_format = 2,
+ .additional_length = sizeof(scsi_inquiry_resp_t) - 5,
};
// vendor_id, product_id, product_rev is space padded string
@@ -793,20 +697,18 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
}
break;
- case SCSI_CMD_MODE_SENSE_6:
- {
+ case SCSI_CMD_MODE_SENSE_6: {
scsi_mode_sense6_resp_t mode_resp =
{
- .data_len = 3,
- .medium_type = 0,
- .write_protected = false,
- .reserved = 0,
- .block_descriptor_len = 0 // no block descriptor are included
+ .data_len = 3,
+ .medium_type = 0,
+ .write_protected = false,
+ .reserved = 0,
+ .block_descriptor_len = 0 // no block descriptor are included
};
bool writable = true;
- if ( tud_msc_is_writable_cb )
- {
+ if (tud_msc_is_writable_cb) {
writable = tud_msc_is_writable_cb(lun);
}
@@ -817,26 +719,24 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
}
break;
- case SCSI_CMD_REQUEST_SENSE:
- {
+ case SCSI_CMD_REQUEST_SENSE: {
scsi_sense_fixed_resp_t sense_rsp =
{
- .response_code = 0x70, // current, fixed format
- .valid = 1
+ .response_code = 0x70, // current, fixed format
+ .valid = 1
};
- sense_rsp.add_sense_len = sizeof(scsi_sense_fixed_resp_t) - 8;
- sense_rsp.sense_key = (uint8_t) (p_msc->sense_key & 0x0F);
- sense_rsp.add_sense_code = p_msc->add_sense_code;
+ sense_rsp.add_sense_len = sizeof(scsi_sense_fixed_resp_t) - 8;
+ sense_rsp.sense_key = (uint8_t)(p_msc->sense_key & 0x0F);
+ sense_rsp.add_sense_code = p_msc->add_sense_code;
sense_rsp.add_sense_qualifier = p_msc->add_sense_qualifier;
resplen = sizeof(sense_rsp);
TU_VERIFY(0 == tu_memcpy_s(buffer, bufsize, &sense_rsp, (size_t) resplen));
// request sense callback could overwrite the sense data
- if (tud_msc_request_sense_cb)
- {
- resplen = tud_msc_request_sense_cb(lun, buffer, (uint16_t) bufsize);
+ if (tud_msc_request_sense_cb) {
+ resplen = tud_msc_request_sense_cb(lun, buffer, (uint16_t)bufsize);
}
// Clear sense data after copy
@@ -844,15 +744,15 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
}
break;
- default: resplen = -1; break;
+ default: resplen = -1;
+ break;
}
return resplen;
}
-static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc)
-{
- msc_cbw_t const * p_cbw = &p_msc->cbw;
+static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc) {
+ msc_cbw_t const* p_cbw = &p_msc->cbw;
// block size already verified not zero
uint16_t const block_sz = rdwr10_get_blocksize(p_cbw);
@@ -861,14 +761,13 @@ static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc)
uint32_t const lba = rdwr10_get_lba(p_cbw->command) + (p_msc->xferred_len / block_sz);
// remaining bytes capped at class buffer
- int32_t nbytes = (int32_t) tu_min32(sizeof(_mscd_buf), p_cbw->total_bytes-p_msc->xferred_len);
+ int32_t nbytes = (int32_t)tu_min32(CFG_TUD_MSC_EP_BUFSIZE, p_cbw->total_bytes - p_msc->xferred_len);
// Application can consume smaller bytes
uint32_t const offset = p_msc->xferred_len % block_sz;
- nbytes = tud_msc_read10_cb(p_cbw->lun, lba, offset, _mscd_buf, (uint32_t) nbytes);
+ nbytes = tud_msc_read10_cb(p_cbw->lun, lba, offset, _mscd_epbuf.buf, (uint32_t)nbytes);
- if ( nbytes < 0 )
- {
+ if (nbytes < 0) {
// negative means error -> endpoint is stalled & status in CSW set to failed
TU_LOG_DRV(" tud_msc_read10_cb() return -1\r\n");
@@ -876,30 +775,23 @@ static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc)
set_sense_medium_not_present(p_cbw->lun);
fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
- }
- else if ( nbytes == 0 )
- {
+ } else if (nbytes == 0) {
// zero means not ready -> simulate an transfer complete so that this driver callback will fired again
dcd_event_xfer_complete(rhport, p_msc->ep_in, 0, XFER_RESULT_SUCCESS, false);
- }
- else
- {
- TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, (uint16_t) nbytes), );
+ } else {
+ TU_ASSERT(usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_epbuf.buf, (uint16_t) nbytes),);
}
}
-static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc)
-{
- msc_cbw_t const * p_cbw = &p_msc->cbw;
+static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc) {
+ msc_cbw_t const* p_cbw = &p_msc->cbw;
bool writable = true;
- if ( tud_msc_is_writable_cb )
- {
+ if (tud_msc_is_writable_cb) {
writable = tud_msc_is_writable_cb(p_cbw->lun);
}
- if ( !writable )
- {
+ if (!writable) {
// Not writable, complete this SCSI op with error
// Sense = Write protected
tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_DATA_PROTECT, 0x27, 0x00);
@@ -908,16 +800,15 @@ static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc)
}
// remaining bytes capped at class buffer
- uint16_t nbytes = (uint16_t) tu_min32(sizeof(_mscd_buf), p_cbw->total_bytes-p_msc->xferred_len);
+ uint16_t nbytes = (uint16_t)tu_min32(CFG_TUD_MSC_EP_BUFSIZE, p_cbw->total_bytes - p_msc->xferred_len);
// Write10 callback will be called later when usb transfer complete
- TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, nbytes), );
+ TU_ASSERT(usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_epbuf.buf, nbytes),);
}
// process new data arrived from WRITE10
-static void proc_write10_new_data(uint8_t rhport, mscd_interface_t* p_msc, uint32_t xferred_bytes)
-{
- msc_cbw_t const * p_cbw = &p_msc->cbw;
+static void proc_write10_new_data(uint8_t rhport, mscd_interface_t* p_msc, uint32_t xferred_bytes) {
+ msc_cbw_t const* p_cbw = &p_msc->cbw;
// block size already verified not zero
uint16_t const block_sz = rdwr10_get_blocksize(p_cbw);
@@ -927,10 +818,9 @@ static void proc_write10_new_data(uint8_t rhport, mscd_interface_t* p_msc, uint3
// Invoke callback to consume new data
uint32_t const offset = p_msc->xferred_len % block_sz;
- int32_t nbytes = tud_msc_write10_cb(p_cbw->lun, lba, offset, _mscd_buf, xferred_bytes);
+ int32_t nbytes = tud_msc_write10_cb(p_cbw->lun, lba, offset, _mscd_epbuf.buf, xferred_bytes);
- if ( nbytes < 0 )
- {
+ if (nbytes < 0) {
// negative means error -> failed this scsi op
TU_LOG_DRV(" tud_msc_write10_cb() return -1\r\n");
@@ -941,32 +831,25 @@ static void proc_write10_new_data(uint8_t rhport, mscd_interface_t* p_msc, uint3
set_sense_medium_not_present(p_cbw->lun);
fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
- }else
- {
+ } else {
// Application consume less than what we got (including zero)
- if ( (uint32_t) nbytes < xferred_bytes )
- {
- uint32_t const left_over = xferred_bytes - (uint32_t) nbytes;
- if ( nbytes > 0 )
- {
- p_msc->xferred_len += (uint16_t) nbytes;
- memmove(_mscd_buf, _mscd_buf+nbytes, left_over);
+ if ((uint32_t)nbytes < xferred_bytes) {
+ uint32_t const left_over = xferred_bytes - (uint32_t)nbytes;
+ if (nbytes > 0) {
+ p_msc->xferred_len += (uint16_t)nbytes;
+ memmove(_mscd_epbuf.buf, _mscd_epbuf.buf + nbytes, left_over);
}
// simulate an transfer complete with adjusted parameters --> callback will be invoked with adjusted parameter
dcd_event_xfer_complete(rhport, p_msc->ep_out, left_over, XFER_RESULT_SUCCESS, false);
- }
- else
- {
+ } else {
// Application consume all bytes in our buffer
p_msc->xferred_len += xferred_bytes;
- if ( p_msc->xferred_len >= p_msc->total_len )
- {
+ if (p_msc->xferred_len >= p_msc->total_len) {
// Data Stage is complete
p_msc->stage = MSC_STAGE_STATUS;
- }else
- {
+ } else {
// prepare to receive more data from host
proc_write10_cmd(rhport, p_msc);
}
diff --git a/src/class/msc/msc_host.c b/src/class/msc/msc_host.c
index ce6e7fb2d..ef0635bbe 100644
--- a/src/class/msc/msc_host.c
+++ b/src/class/msc/msc_host.c
@@ -54,38 +54,37 @@ typedef struct {
uint8_t itf_num;
uint8_t ep_in;
uint8_t ep_out;
-
uint8_t max_lun;
volatile bool configured; // Receive SET_CONFIGURE
volatile bool mounted; // Enumeration is complete
- struct {
- uint32_t block_size;
- uint32_t block_count;
- } capacity[CFG_TUH_MSC_MAXLUN];
-
- //------------- SCSI -------------//
+ // SCSI command data
uint8_t stage;
void* buffer;
tuh_msc_complete_cb_t complete_cb;
uintptr_t complete_arg;
- CFG_TUH_MEM_ALIGN msc_cbw_t cbw;
- CFG_TUH_MEM_ALIGN msc_csw_t csw;
+ struct {
+ uint32_t block_size;
+ uint32_t block_count;
+ } capacity[CFG_TUH_MSC_MAXLUN];
} msch_interface_t;
-CFG_TUH_MEM_SECTION static msch_interface_t _msch_itf[CFG_TUH_DEVICE_MAX];
+typedef struct {
+ TUH_EPBUF_TYPE_DEF(msc_cbw_t, cbw);
+ TUH_EPBUF_TYPE_DEF(msc_csw_t, csw);
+} msch_epbuf_t;
-// buffer used to read scsi information when mounted
-// largest response data currently is inquiry TODO Inquiry is not part of enum anymore
-CFG_TUH_MEM_SECTION CFG_TUH_MEM_ALIGN
-static uint8_t _msch_buffer[sizeof(scsi_inquiry_resp_t)];
+static msch_interface_t _msch_itf[CFG_TUH_DEVICE_MAX];
+CFG_TUH_MEM_SECTION static msch_epbuf_t _msch_epbuf[CFG_TUH_DEVICE_MAX];
-// FIXME potential nul reference
-TU_ATTR_ALWAYS_INLINE
-static inline msch_interface_t* get_itf(uint8_t dev_addr) {
- return &_msch_itf[dev_addr - 1];
+TU_ATTR_ALWAYS_INLINE static inline msch_interface_t* get_itf(uint8_t daddr) {
+ return &_msch_itf[daddr - 1];
+}
+
+TU_ATTR_ALWAYS_INLINE static inline msch_epbuf_t* get_epbuf(uint8_t daddr) {
+ return &_msch_epbuf[daddr - 1];
}
//--------------------------------------------------------------------+
@@ -133,14 +132,15 @@ bool tuh_msc_scsi_command(uint8_t daddr, msc_cbw_t const* cbw, void* data,
// claim endpoint
TU_VERIFY(usbh_edpt_claim(daddr, p_msc->ep_out));
+ msch_epbuf_t* epbuf = get_epbuf(daddr);
- p_msc->cbw = *cbw;
- p_msc->stage = MSC_STAGE_CMD;
+ epbuf->cbw = *cbw;
p_msc->buffer = data;
p_msc->complete_cb = complete_cb;
p_msc->complete_arg = arg;
+ p_msc->stage = MSC_STAGE_CMD;
- if (!usbh_edpt_xfer(daddr, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t))) {
+ if (!usbh_edpt_xfer(daddr, p_msc->ep_out, (uint8_t*) &epbuf->cbw, sizeof(msc_cbw_t))) {
usbh_edpt_release(daddr, p_msc->ep_out);
return false;
}
@@ -286,6 +286,7 @@ bool tuh_msc_reset(uint8_t dev_addr) {
//--------------------------------------------------------------------+
bool msch_init(void) {
TU_LOG_DRV("sizeof(msch_interface_t) = %u\r\n", sizeof(msch_interface_t));
+ TU_LOG_DRV("sizeof(msch_epbuf_t) = %u\r\n", sizeof(msch_epbuf_t));
tu_memclr(_msch_itf, sizeof(_msch_itf));
return true;
}
@@ -303,7 +304,9 @@ void msch_close(uint8_t dev_addr) {
// invoke Application Callback
if (p_msc->mounted) {
- if (tuh_msc_umount_cb) tuh_msc_umount_cb(dev_addr);
+ if (tuh_msc_umount_cb) {
+ tuh_msc_umount_cb(dev_addr);
+ }
}
tu_memclr(p_msc, sizeof(msch_interface_t));
@@ -311,30 +314,28 @@ void msch_close(uint8_t dev_addr) {
bool msch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) {
msch_interface_t* p_msc = get_itf(dev_addr);
- msc_cbw_t const * cbw = &p_msc->cbw;
- msc_csw_t * csw = &p_msc->csw;
+ msch_epbuf_t* epbuf = get_epbuf(dev_addr);
+ msc_cbw_t const * cbw = &epbuf->cbw;
+ msc_csw_t * csw = &epbuf->csw;
switch (p_msc->stage) {
case MSC_STAGE_CMD:
// Must be Command Block
TU_ASSERT(ep_addr == p_msc->ep_out && event == XFER_RESULT_SUCCESS && xferred_bytes == sizeof(msc_cbw_t));
-
if (cbw->total_bytes && p_msc->buffer) {
// Data stage if any
p_msc->stage = MSC_STAGE_DATA;
uint8_t const ep_data = (cbw->dir & TUSB_DIR_IN_MASK) ? p_msc->ep_in : p_msc->ep_out;
TU_ASSERT(usbh_edpt_xfer(dev_addr, ep_data, p_msc->buffer, (uint16_t) cbw->total_bytes));
- } else {
- // Status stage
- p_msc->stage = MSC_STAGE_STATUS;
- TU_ASSERT(usbh_edpt_xfer(dev_addr, p_msc->ep_in, (uint8_t*) &p_msc->csw, (uint16_t) sizeof(msc_csw_t)));
+ break;
}
- break;
+
+ TU_ATTR_FALLTHROUGH; // fallthrough to status stage
case MSC_STAGE_DATA:
// Status stage
p_msc->stage = MSC_STAGE_STATUS;
- TU_ASSERT(usbh_edpt_xfer(dev_addr, p_msc->ep_in, (uint8_t*) &p_msc->csw, (uint16_t) sizeof(msc_csw_t)));
+ TU_ASSERT(usbh_edpt_xfer(dev_addr, p_msc->ep_in, (uint8_t*) csw, (uint16_t) sizeof(msc_csw_t)));
break;
case MSC_STAGE_STATUS:
@@ -399,10 +400,9 @@ bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const* de
return true;
}
-bool msch_set_config(uint8_t dev_addr, uint8_t itf_num) {
- msch_interface_t* p_msc = get_itf(dev_addr);
+bool msch_set_config(uint8_t daddr, uint8_t itf_num) {
+ msch_interface_t* p_msc = get_itf(daddr);
TU_ASSERT(p_msc->itf_num == itf_num);
-
p_msc->configured = true;
//------------- Get Max Lun -------------//
@@ -419,11 +419,12 @@ bool msch_set_config(uint8_t dev_addr, uint8_t itf_num) {
.wLength = 1
};
+ uint8_t* enum_buf = usbh_get_enum_buf();
tuh_xfer_t xfer = {
- .daddr = dev_addr,
+ .daddr = daddr,
.ep_addr = 0,
.setup = &request,
- .buffer = _msch_buffer,
+ .buffer = enum_buf,
.complete_cb = config_get_maxlun_complete,
.user_data = 0
};
@@ -436,9 +437,13 @@ static void config_get_maxlun_complete(tuh_xfer_t* xfer) {
uint8_t const daddr = xfer->daddr;
msch_interface_t* p_msc = get_itf(daddr);
- // STALL means zero
- p_msc->max_lun = (XFER_RESULT_SUCCESS == xfer->result) ? _msch_buffer[0] : 0;
- p_msc->max_lun++; // MAX LUN is minus 1 by specs
+ // MAXLUN's response is minus 1 by specs, STALL means 1
+ if (XFER_RESULT_SUCCESS == xfer->result) {
+ uint8_t* enum_buf = usbh_get_enum_buf();
+ p_msc->max_lun = enum_buf[0] + 1;
+ } else {
+ p_msc->max_lun = 1;
+ }
TU_LOG_DRV(" Max LUN = %u\r\n", p_msc->max_lun);
@@ -451,18 +456,19 @@ static void config_get_maxlun_complete(tuh_xfer_t* xfer) {
static bool config_test_unit_ready_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data) {
msc_cbw_t const* cbw = cb_data->cbw;
msc_csw_t const* csw = cb_data->csw;
+ uint8_t* enum_buf = usbh_get_enum_buf();
if (csw->status == 0) {
// Unit is ready, read its capacity
TU_LOG_DRV("SCSI Read Capacity\r\n");
- tuh_msc_read_capacity(dev_addr, cbw->lun, (scsi_read_capacity10_resp_t*) ((void*) _msch_buffer),
+ tuh_msc_read_capacity(dev_addr, cbw->lun, (scsi_read_capacity10_resp_t*) (uintptr_t) enum_buf,
config_read_capacity_complete, 0);
} else {
// Note: During enumeration, some device fails Test Unit Ready and require a few retries
// with Request Sense to start working !!
// TODO limit number of retries
TU_LOG_DRV("SCSI Request Sense\r\n");
- TU_ASSERT(tuh_msc_request_sense(dev_addr, cbw->lun, _msch_buffer, config_request_sense_complete, 0));
+ TU_ASSERT(tuh_msc_request_sense(dev_addr, cbw->lun, enum_buf, config_request_sense_complete, 0));
}
return true;
@@ -480,19 +486,20 @@ static bool config_request_sense_complete(uint8_t dev_addr, tuh_msc_complete_dat
static bool config_read_capacity_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data) {
msc_cbw_t const* cbw = cb_data->cbw;
msc_csw_t const* csw = cb_data->csw;
-
TU_ASSERT(csw->status == 0);
-
msch_interface_t* p_msc = get_itf(dev_addr);
+ uint8_t* enum_buf = usbh_get_enum_buf();
// Capacity response field: Block size and Last LBA are both Big-Endian
- scsi_read_capacity10_resp_t* resp = (scsi_read_capacity10_resp_t*) ((void*) _msch_buffer);
+ scsi_read_capacity10_resp_t* resp = (scsi_read_capacity10_resp_t*) (uintptr_t) enum_buf;
p_msc->capacity[cbw->lun].block_count = tu_ntohl(resp->last_lba) + 1;
p_msc->capacity[cbw->lun].block_size = tu_ntohl(resp->block_size);
// Mark enumeration is complete
p_msc->mounted = true;
- if (tuh_msc_mount_cb) tuh_msc_mount_cb(dev_addr);
+ if (tuh_msc_mount_cb) {
+ tuh_msc_mount_cb(dev_addr);
+ }
// notify usbh that driver enumeration is complete
usbh_driver_set_config_complete(dev_addr, p_msc->itf_num);
diff --git a/src/class/msc/msc_host.h b/src/class/msc/msc_host.h
index 9fda566d8..09d777066 100644
--- a/src/class/msc/msc_host.h
+++ b/src/class/msc/msc_host.h
@@ -73,10 +73,12 @@ uint32_t tuh_msc_get_block_size(uint8_t dev_addr, uint8_t lun);
// Perform a full SCSI command (cbw, data, csw) in non-blocking manner.
// Complete callback is invoked when SCSI op is complete.
// return true if success, false if there is already pending operation.
+// NOTE: buffer must be accessible by USB/DMA controller, aligned correctly and multiple of cache line if enabled
bool tuh_msc_scsi_command(uint8_t daddr, msc_cbw_t const* cbw, void* data, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
// Perform SCSI Inquiry command
// Complete callback is invoked when SCSI op is complete.
+// NOTE: response must be accessible by USB/DMA controller, aligned correctly and multiple of cache line if enabled
bool tuh_msc_inquiry(uint8_t dev_addr, uint8_t lun, scsi_inquiry_resp_t* response, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
// Perform SCSI Test Unit Ready command
@@ -85,14 +87,17 @@ bool tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, tuh_msc_complete_cb_
// Perform SCSI Request Sense 10 command
// Complete callback is invoked when SCSI op is complete.
+// NOTE: response must be accessible by USB/DMA controller, aligned correctly and multiple of cache line if enabled
bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void *response, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
// Perform SCSI Read 10 command. Read n blocks starting from LBA to buffer
// Complete callback is invoked when SCSI op is complete.
+// NOTE: buffer must be accessible by USB/DMA controller, aligned correctly and multiple of cache line if enabled
bool tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void * buffer, uint32_t lba, uint16_t block_count, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
// Perform SCSI Write 10 command. Write n blocks starting from LBA to device
// Complete callback is invoked when SCSI op is complete.
+// NOTE: buffer must be accessible by USB/DMA controller, aligned correctly and multiple of cache line if enabled
bool tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const * buffer, uint32_t lba, uint16_t block_count, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
// Perform SCSI Read Capacity 10 command
@@ -116,7 +121,7 @@ TU_ATTR_WEAK void tuh_msc_umount_cb(uint8_t dev_addr);
bool msch_init (void);
bool msch_deinit (void);
bool msch_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len);
-bool msch_set_config (uint8_t dev_addr, uint8_t itf_num);
+bool msch_set_config (uint8_t daddr, uint8_t itf_num);
void msch_close (uint8_t dev_addr);
bool msch_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
diff --git a/src/class/net/ecm_rndis_device.c b/src/class/net/ecm_rndis_device.c
index f7a5fd225..a54e6d662 100644
--- a/src/class/net/ecm_rndis_device.c
+++ b/src/class/net/ecm_rndis_device.c
@@ -35,13 +35,18 @@
#include "net_device.h"
#include "rndis_protocol.h"
-void rndis_class_set_handler(uint8_t *data, int size); /* found in ./misc/networking/rndis_reports.c */
+extern void rndis_class_set_handler(uint8_t *data, int size); /* found in ./misc/networking/rndis_reports.c */
+
+#define CFG_TUD_NET_PACKET_PREFIX_LEN sizeof(rndis_data_packet_t)
+#define CFG_TUD_NET_PACKET_SUFFIX_LEN 0
+
+#define NETD_PACKET_SIZE (CFG_TUD_NET_PACKET_PREFIX_LEN + CFG_TUD_NET_MTU + CFG_TUD_NET_PACKET_PREFIX_LEN)
+#define NETD_CONTROL_SIZE 120
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
-typedef struct
-{
+typedef struct {
uint8_t itf_num; // Index number of Management Interface, +1 for Data Interface
uint8_t itf_data_alt; // Alternate setting of Data Interface. 0 : inactive, 1 : active
@@ -55,78 +60,44 @@ typedef struct
// TODO since configuration descriptor may not be long-lived memory, we should
// keep a copy of endpoint attribute instead
uint8_t const * ecm_desc_epdata;
-
} netd_interface_t;
-#define CFG_TUD_NET_PACKET_PREFIX_LEN sizeof(rndis_data_packet_t)
-#define CFG_TUD_NET_PACKET_SUFFIX_LEN 0
-
-CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static
-uint8_t received[CFG_TUD_NET_PACKET_PREFIX_LEN + CFG_TUD_NET_MTU + CFG_TUD_NET_PACKET_PREFIX_LEN];
-
-CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static
-uint8_t transmitted[CFG_TUD_NET_PACKET_PREFIX_LEN + CFG_TUD_NET_MTU + CFG_TUD_NET_PACKET_PREFIX_LEN];
-
-struct ecm_notify_struct
-{
+typedef struct ecm_notify_struct {
tusb_control_request_t header;
uint32_t downlink, uplink;
-};
-
-tu_static const struct ecm_notify_struct ecm_notify_nc =
-{
- .header = {
- .bmRequestType = 0xA1,
- .bRequest = 0 /* NETWORK_CONNECTION aka NetworkConnection */,
- .wValue = 1 /* Connected */,
- .wLength = 0,
- },
-};
+} ecm_notify_t;
-tu_static const struct ecm_notify_struct ecm_notify_csc =
-{
- .header = {
- .bmRequestType = 0xA1,
- .bRequest = 0x2A /* CONNECTION_SPEED_CHANGE aka ConnectionSpeedChange */,
- .wLength = 8,
- },
- .downlink = 9728000,
- .uplink = 9728000,
-};
+typedef struct {
+ TUD_EPBUF_DEF(rx, NETD_PACKET_SIZE);
+ TUD_EPBUF_DEF(tx, NETD_PACKET_SIZE);
-// TODO remove CFG_TUD_MEM_SECTION, control internal buffer is already in this special section
-CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static union
-{
- uint8_t rndis_buf[120];
- struct ecm_notify_struct ecm_buf;
-} notify;
+ TUD_EPBUF_DEF(notify, sizeof(ecm_notify_t));
+ TUD_EPBUF_DEF(ctrl, NETD_CONTROL_SIZE);
+} netd_epbuf_t;
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
-// TODO remove CFG_TUD_MEM_SECTION
-CFG_TUD_MEM_SECTION tu_static netd_interface_t _netd_itf;
+static netd_interface_t _netd_itf;
+CFG_TUD_MEM_SECTION static netd_epbuf_t _netd_epbuf;
+static bool can_xmit;
-tu_static bool can_xmit;
-
-void tud_network_recv_renew(void)
-{
- usbd_edpt_xfer(0, _netd_itf.ep_out, received, sizeof(received));
+void tud_network_recv_renew(void) {
+ usbd_edpt_xfer(0, _netd_itf.ep_out, _netd_epbuf.rx, NETD_PACKET_SIZE);
}
-static void do_in_xfer(uint8_t *buf, uint16_t len)
-{
+static void do_in_xfer(uint8_t *buf, uint16_t len) {
can_xmit = false;
usbd_edpt_xfer(0, _netd_itf.ep_in, buf, len);
}
-void netd_report(uint8_t *buf, uint16_t len)
-{
- uint8_t const rhport = 0;
+void netd_report(uint8_t *buf, uint16_t len) {
+ const uint8_t rhport = 0;
+ len = tu_min16(len, sizeof(ecm_notify_t));
- // skip if previous report not yet acknowledged by host
- if ( usbd_edpt_busy(rhport, _netd_itf.ep_notif) ) return;
- usbd_edpt_xfer(rhport, _netd_itf.ep_notif, buf, len);
+ TU_VERIFY(usbd_edpt_claim(rhport, _netd_itf.ep_notif), );
+ memcpy(_netd_epbuf.notify, buf, len);
+ usbd_edpt_xfer(rhport, _netd_itf.ep_notif, _netd_epbuf.notify, len);
}
//--------------------------------------------------------------------+
@@ -140,15 +111,12 @@ bool netd_deinit(void) {
return true;
}
-void netd_reset(uint8_t rhport)
-{
+void netd_reset(uint8_t rhport) {
(void) rhport;
-
netd_init();
}
-uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
-{
+uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len) {
bool const is_rndis = (TUD_RNDIS_ITF_CLASS == itf_desc->bInterfaceClass &&
TUD_RNDIS_ITF_SUBCLASS == itf_desc->bInterfaceSubClass &&
TUD_RNDIS_ITF_PROTOCOL == itf_desc->bInterfaceProtocol);
@@ -172,21 +140,19 @@ uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1
uint8_t const * p_desc = tu_desc_next( itf_desc );
// Communication Functional Descriptors
- while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len )
- {
+ while (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len) {
drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
}
// notification endpoint (if any)
- if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
- {
- TU_ASSERT( usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), 0 );
+ if (TUSB_DESC_ENDPOINT == tu_desc_type(p_desc)) {
+ TU_ASSERT(usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), 0);
- _netd_itf.ep_notif = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
+ _netd_itf.ep_notif = ((tusb_desc_endpoint_t const*)p_desc)->bEndpointAddress;
drv_len += tu_desc_len(p_desc);
- p_desc = tu_desc_next(p_desc);
+ p_desc = tu_desc_next(p_desc);
}
//------------- Data Interface -------------//
@@ -196,27 +162,24 @@ uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1
// - 1 : IN & OUT endpoints for active networking
TU_ASSERT(TUSB_DESC_INTERFACE == tu_desc_type(p_desc), 0);
- do
- {
+ do {
tusb_desc_interface_t const * data_itf_desc = (tusb_desc_interface_t const *) p_desc;
TU_ASSERT(TUSB_CLASS_CDC_DATA == data_itf_desc->bInterfaceClass, 0);
drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
- }while( _netd_itf.ecm_mode && (TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) && (drv_len <= max_len) );
+ } while (_netd_itf.ecm_mode && (TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) && (drv_len <= max_len));
// Pair of endpoints
TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc), 0);
- if ( _netd_itf.ecm_mode )
- {
+ if (_netd_itf.ecm_mode) {
// ECM by default is in-active, save the endpoint attribute
// to open later when received setInterface
_netd_itf.ecm_desc_epdata = p_desc;
- }else
- {
+ } else {
// Open endpoint pair for RNDIS
- TU_ASSERT( usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &_netd_itf.ep_out, &_netd_itf.ep_in), 0 );
+ TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &_netd_itf.ep_out, &_netd_itf.ep_in), 0);
tud_network_init_cb();
@@ -232,38 +195,50 @@ uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1
return drv_len;
}
-static void ecm_report(bool nc)
-{
- notify.ecm_buf = (nc) ? ecm_notify_nc : ecm_notify_csc;
- notify.ecm_buf.header.wIndex = _netd_itf.itf_num;
- netd_report((uint8_t *)&notify.ecm_buf, (nc) ? sizeof(notify.ecm_buf.header) : sizeof(notify.ecm_buf));
+static void ecm_report(bool nc) {
+ const ecm_notify_t ecm_notify_nc = {
+ .header = {
+ .bmRequestType = 0xA1,
+ .bRequest = 0, /* NETWORK_CONNECTION aka NetworkConnection */
+ .wValue = 1, /* Connected */
+ .wLength = 0,
+ },
+ };
+
+ const ecm_notify_t ecm_notify_csc = {
+ .header = {
+ .bmRequestType = 0xA1,
+ .bRequest = 0x2A, /* CONNECTION_SPEED_CHANGE aka ConnectionSpeedChange */
+ .wLength = 8,
+ },
+ .downlink = 9728000,
+ .uplink = 9728000,
+ };
+
+ ecm_notify_t notify = (nc) ? ecm_notify_nc : ecm_notify_csc;
+ notify.header.wIndex = _netd_itf.itf_num;
+ netd_report((uint8_t *)&notify, (nc) ? sizeof(notify.header) : sizeof(notify));
}
// Invoked when a control transfer occurred on an interface of this class
// Driver response accordingly to the request and the transfer stage (setup/data/ack)
// return false to stall control endpoint (e.g unsupported request)
-bool netd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
-{
- if ( stage == CONTROL_STAGE_SETUP )
- {
- switch ( request->bmRequestType_bit.type )
- {
+bool netd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) {
+ if (stage == CONTROL_STAGE_SETUP) {
+ switch (request->bmRequestType_bit.type) {
case TUSB_REQ_TYPE_STANDARD:
- switch ( request->bRequest )
- {
- case TUSB_REQ_GET_INTERFACE:
- {
- uint8_t const req_itfnum = (uint8_t) request->wIndex;
+ switch (request->bRequest) {
+ case TUSB_REQ_GET_INTERFACE: {
+ uint8_t const req_itfnum = (uint8_t)request->wIndex;
TU_VERIFY(_netd_itf.itf_num+1 == req_itfnum);
tud_control_xfer(rhport, request, &_netd_itf.itf_data_alt, 1);
}
break;
- case TUSB_REQ_SET_INTERFACE:
- {
- uint8_t const req_itfnum = (uint8_t) request->wIndex;
- uint8_t const req_alt = (uint8_t) request->wValue;
+ case TUSB_REQ_SET_INTERFACE: {
+ uint8_t const req_itfnum = (uint8_t)request->wIndex;
+ uint8_t const req_alt = (uint8_t)request->wValue;
// Only valid for Data Interface with Alternate is either 0 or 1
TU_VERIFY(_netd_itf.itf_num+1 == req_itfnum && req_alt < 2);
@@ -273,14 +248,14 @@ bool netd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t
_netd_itf.itf_data_alt = req_alt;
- if ( _netd_itf.itf_data_alt )
- {
+ if (_netd_itf.itf_data_alt) {
// TODO since we don't actually close endpoint
// hack here to not re-open it
- if ( _netd_itf.ep_in == 0 && _netd_itf.ep_out == 0 )
- {
+ if (_netd_itf.ep_in == 0 && _netd_itf.ep_out == 0) {
TU_ASSERT(_netd_itf.ecm_desc_epdata);
- TU_ASSERT( usbd_open_edpt_pair(rhport, _netd_itf.ecm_desc_epdata, 2, TUSB_XFER_BULK, &_netd_itf.ep_out, &_netd_itf.ep_in) );
+ TU_ASSERT(
+ usbd_open_edpt_pair(rhport, _netd_itf.ecm_desc_epdata, 2, TUSB_XFER_BULK, &_netd_itf.ep_out, &
+ _netd_itf.ep_in));
// TODO should be merge with RNDIS's after endpoint opened
// Also should have opposite callback for application to disable network !!
@@ -288,8 +263,7 @@ bool netd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t
can_xmit = true; // we are ready to transmit a packet
tud_network_recv_renew(); // prepare for incoming packets
}
- }else
- {
+ } else {
// TODO close the endpoint pair
// For now pretend that we did, this should have no harm since host won't try to
// communicate with the endpoints again
@@ -303,50 +277,39 @@ bool netd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t
// unsupported request
default: return false;
}
- break;
+ break;
case TUSB_REQ_TYPE_CLASS:
- TU_VERIFY (_netd_itf.itf_num == request->wIndex);
+ TU_VERIFY(_netd_itf.itf_num == request->wIndex);
- if (_netd_itf.ecm_mode)
- {
+ if (_netd_itf.ecm_mode) {
/* the only required CDC-ECM Management Element Request is SetEthernetPacketFilter */
- if (0x43 /* SET_ETHERNET_PACKET_FILTER */ == request->bRequest)
- {
+ if (0x43 /* SET_ETHERNET_PACKET_FILTER */ == request->bRequest) {
tud_control_xfer(rhport, request, NULL, 0);
ecm_report(true);
}
- }
- else
- {
- if (request->bmRequestType_bit.direction == TUSB_DIR_IN)
- {
- rndis_generic_msg_t *rndis_msg = (rndis_generic_msg_t *) ((void*) notify.rndis_buf);
+ } else {
+ if (request->bmRequestType_bit.direction == TUSB_DIR_IN) {
+ rndis_generic_msg_t* rndis_msg = (rndis_generic_msg_t*)((void*)_netd_epbuf.ctrl);
uint32_t msglen = tu_le32toh(rndis_msg->MessageLength);
- TU_ASSERT(msglen <= sizeof(notify.rndis_buf));
- tud_control_xfer(rhport, request, notify.rndis_buf, (uint16_t) msglen);
- }
- else
- {
- tud_control_xfer(rhport, request, notify.rndis_buf, (uint16_t) sizeof(notify.rndis_buf));
+ TU_ASSERT(msglen <= NETD_CONTROL_SIZE);
+ tud_control_xfer(rhport, request, _netd_epbuf.ctrl, (uint16_t)msglen);
+ } else {
+ tud_control_xfer(rhport, request, _netd_epbuf.ctrl, NETD_CONTROL_SIZE);
}
}
- break;
+ break;
// unsupported request
default: return false;
}
- }
- else if ( stage == CONTROL_STAGE_DATA )
- {
+ } else if (stage == CONTROL_STAGE_DATA) {
// Handle RNDIS class control OUT only
if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS &&
- request->bmRequestType_bit.direction == TUSB_DIR_OUT &&
- _netd_itf.itf_num == request->wIndex)
- {
- if ( !_netd_itf.ecm_mode )
- {
- rndis_class_set_handler(notify.rndis_buf, request->wLength);
+ request->bmRequestType_bit.direction == TUSB_DIR_OUT &&
+ _netd_itf.itf_num == request->wIndex) {
+ if (!_netd_itf.ecm_mode) {
+ rndis_class_set_handler(_netd_epbuf.ctrl, request->wLength);
}
}
}
@@ -354,92 +317,77 @@ bool netd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t
return true;
}
-static void handle_incoming_packet(uint32_t len)
-{
- uint8_t *pnt = received;
+static void handle_incoming_packet(uint32_t len) {
+ uint8_t* pnt = _netd_epbuf.rx;
uint32_t size = 0;
- if (_netd_itf.ecm_mode)
- {
+ if (_netd_itf.ecm_mode) {
size = len;
- }
- else
- {
- rndis_data_packet_t *r = (rndis_data_packet_t *) ((void*) pnt);
- if (len >= sizeof(rndis_data_packet_t))
- if ( (r->MessageType == REMOTE_NDIS_PACKET_MSG) && (r->MessageLength <= len))
- if ( (r->DataOffset + offsetof(rndis_data_packet_t, DataOffset) + r->DataLength) <= len)
- {
- pnt = &received[r->DataOffset + offsetof(rndis_data_packet_t, DataOffset)];
+ } else {
+ rndis_data_packet_t* r = (rndis_data_packet_t*)((void*)pnt);
+ if (len >= sizeof(rndis_data_packet_t)) {
+ if ((r->MessageType == REMOTE_NDIS_PACKET_MSG) && (r->MessageLength <= len)) {
+ if ((r->DataOffset + offsetof(rndis_data_packet_t, DataOffset) + r->DataLength) <= len) {
+ pnt = &_netd_epbuf.rx[r->DataOffset + offsetof(rndis_data_packet_t, DataOffset)];
size = r->DataLength;
}
+ }
+ }
}
- if (!tud_network_recv_cb(pnt, (uint16_t) size))
- {
+ if (!tud_network_recv_cb(pnt, (uint16_t)size)) {
/* if a buffer was never handled by user code, we must renew on the user's behalf */
tud_network_recv_renew();
}
}
-bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
-{
- (void) rhport;
- (void) result;
+bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
+ (void)rhport;
+ (void)result;
/* new packet received */
- if ( ep_addr == _netd_itf.ep_out )
- {
+ if (ep_addr == _netd_itf.ep_out) {
handle_incoming_packet(xferred_bytes);
}
/* data transmission finished */
- if ( ep_addr == _netd_itf.ep_in )
- {
+ if (ep_addr == _netd_itf.ep_in) {
/* TinyUSB requires the class driver to implement ZLP (since ZLP usage is class-specific) */
- if ( xferred_bytes && (0 == (xferred_bytes % CFG_TUD_NET_ENDPOINT_SIZE)) )
- {
+ if (xferred_bytes && (0 == (xferred_bytes % CFG_TUD_NET_ENDPOINT_SIZE))) {
do_in_xfer(NULL, 0); /* a ZLP is needed */
- }
- else
- {
+ } else {
/* we're finally finished */
can_xmit = true;
}
}
- if ( _netd_itf.ecm_mode && (ep_addr == _netd_itf.ep_notif) )
- {
- if (sizeof(notify.ecm_buf.header) == xferred_bytes) ecm_report(false);
+ if (_netd_itf.ecm_mode && (ep_addr == _netd_itf.ep_notif)) {
+ if (sizeof(tusb_control_request_t) == xferred_bytes) {
+ ecm_report(false);
+ }
}
return true;
}
-bool tud_network_can_xmit(uint16_t size)
-{
+bool tud_network_can_xmit(uint16_t size) {
(void)size;
-
return can_xmit;
}
-void tud_network_xmit(void *ref, uint16_t arg)
-{
- uint8_t *data;
- uint16_t len;
-
- if (!can_xmit)
+void tud_network_xmit(void *ref, uint16_t arg) {
+ if (!can_xmit) {
return;
+ }
- len = (_netd_itf.ecm_mode) ? 0 : CFG_TUD_NET_PACKET_PREFIX_LEN;
- data = transmitted + len;
+ uint16_t len = (_netd_itf.ecm_mode) ? 0 : CFG_TUD_NET_PACKET_PREFIX_LEN;
+ uint8_t* data = _netd_epbuf.tx + len;
len += tud_network_xmit_cb(data, ref, arg);
- if (!_netd_itf.ecm_mode)
- {
- rndis_data_packet_t *hdr = (rndis_data_packet_t *) ((void*) transmitted);
+ if (!_netd_itf.ecm_mode) {
+ rndis_data_packet_t *hdr = (rndis_data_packet_t *) ((void*) _netd_epbuf.tx);
memset(hdr, 0, sizeof(rndis_data_packet_t));
hdr->MessageType = REMOTE_NDIS_PACKET_MSG;
hdr->MessageLength = len;
@@ -447,7 +395,7 @@ void tud_network_xmit(void *ref, uint16_t arg)
hdr->DataLength = len - sizeof(rndis_data_packet_t);
}
- do_in_xfer(transmitted, len);
+ do_in_xfer(_netd_epbuf.tx, len);
}
#endif
diff --git a/src/class/net/ncm.h b/src/class/net/ncm.h
index 1b987fca0..0245a87f2 100644
--- a/src/class/net/ncm.h
+++ b/src/class/net/ncm.h
@@ -155,9 +155,10 @@ typedef union TU_ATTR_PACKED {
uint8_t data[CFG_TUD_NCM_OUT_NTB_MAX_SIZE];
} recv_ntb_t;
-struct ncm_notify_t {
+typedef struct {
tusb_control_request_t header;
- uint32_t downlink, uplink;
-};
+ uint32_t downlink;
+ uint32_t uplink;
+} ncm_notify_t;
#endif
diff --git a/src/class/net/ncm_device.c b/src/class/net/ncm_device.c
index 90d747185..4e6088340 100644
--- a/src/class/net/ncm_device.c
+++ b/src/class/net/ncm_device.c
@@ -89,7 +89,6 @@ typedef struct {
uint8_t rhport; // storage of \a rhport because some callbacks are done without it
// recv handling
- CFG_TUSB_MEM_ALIGN recv_ntb_t recv_ntb[RECV_NTB_N]; // actual recv NTBs
recv_ntb_t *recv_free_ntb[RECV_NTB_N]; // free list of recv NTBs
recv_ntb_t *recv_ready_ntb[RECV_NTB_N]; // NTBs waiting for transmission to glue logic
recv_ntb_t *recv_tinyusb_ntb; // buffer for the running transfer TinyUSB -> driver
@@ -97,7 +96,6 @@ typedef struct {
uint16_t recv_glue_ntb_datagram_ndx; // index into \a recv_glue_ntb_datagram
// xmit handling
- CFG_TUSB_MEM_ALIGN xmit_ntb_t xmit_ntb[XMIT_NTB_N]; // actual xmit NTBs
xmit_ntb_t *xmit_free_ntb[XMIT_NTB_N]; // free list of xmit NTBs
xmit_ntb_t *xmit_ready_ntb[XMIT_NTB_N]; // NTBs waiting for transmission to TinyUSB
xmit_ntb_t *xmit_tinyusb_ntb; // buffer for the running transfer driver -> TinyUSB
@@ -110,11 +108,28 @@ typedef struct {
NOTIFICATION_SPEED,
NOTIFICATION_CONNECTED,
NOTIFICATION_DONE
- } notification_xmit_state; // state of notification transmission
- bool notification_xmit_is_running; // notification is currently transmitted
+ } notification_xmit_state; // state of notification transmission
+ bool notification_xmit_is_running; // notification is currently transmitted
+
+ // misc
+ bool tud_network_recv_renew_active; // tud_network_recv_renew() is active (avoid recursive invocations)
+ bool tud_network_recv_renew_process_again; // tud_network_recv_renew() should process again
} ncm_interface_t;
-CFG_TUD_MEM_SECTION CFG_TUD_MEM_ALIGN tu_static ncm_interface_t ncm_interface;
+typedef struct {
+ struct {
+ TUD_EPBUF_TYPE_DEF(recv_ntb_t, ntb);
+ } recv[RECV_NTB_N];
+
+ struct {
+ TUD_EPBUF_TYPE_DEF(xmit_ntb_t, ntb);
+ } xmit[XMIT_NTB_N];
+
+ TUD_EPBUF_TYPE_DEF(ncm_notify_t, epnotif);
+} ncm_epbuf_t;
+
+static ncm_interface_t ncm_interface;
+CFG_TUD_MEM_SECTION static ncm_epbuf_t ncm_epbuf;
/**
* This is the NTB parameter structure
@@ -122,7 +137,7 @@ CFG_TUD_MEM_SECTION CFG_TUD_MEM_ALIGN tu_static ncm_interface_t ncm_interface;
* \attention
* We are lucky, that byte order is correct
*/
-CFG_TUD_MEM_SECTION CFG_TUD_MEM_ALIGN tu_static const ntb_parameters_t ntb_parameters = {
+TU_ATTR_ALIGNED(4) static const ntb_parameters_t ntb_parameters = {
.wLength = sizeof(ntb_parameters_t),
.bmNtbFormatsSupported = 0x01,// 16-bit NTB supported
.dwNtbInMaxSize = CFG_TUD_NCM_IN_NTB_MAX_SIZE,
@@ -152,30 +167,6 @@ CFG_TUD_MEM_SECTION CFG_TUD_MEM_ALIGN tu_static const ntb_parameters_t ntb_param
//
// everything about notifications
//
-tu_static struct ncm_notify_t ncm_notify_connected = {
- .header = {
- .bmRequestType_bit = {
- .recipient = TUSB_REQ_RCPT_INTERFACE,
- .type = TUSB_REQ_TYPE_CLASS,
- .direction = TUSB_DIR_IN},
- .bRequest = CDC_NOTIF_NETWORK_CONNECTION,
- .wValue = 1 /* Connected */,
- .wLength = 0,
- },
-};
-
-tu_static struct ncm_notify_t ncm_notify_speed_change = {
- .header = {
- .bmRequestType_bit = {
- .recipient = TUSB_REQ_RCPT_INTERFACE,
- .type = TUSB_REQ_TYPE_CLASS,
- .direction = TUSB_DIR_IN},
- .bRequest = CDC_NOTIF_CONNECTION_SPEED_CHANGE,
- .wLength = 8,
- },
- .downlink = TUD_OPT_HIGH_SPEED ? 480000000 : 12000000,
- .uplink = TUD_OPT_HIGH_SPEED ? 480000000 : 12000000,
-};
/**
* Transmit next notification to the host (if appropriate).
@@ -190,14 +181,53 @@ static void notification_xmit(uint8_t rhport, bool force_next) {
if (ncm_interface.notification_xmit_state == NOTIFICATION_SPEED) {
TU_LOG_DRV(" NOTIFICATION_SPEED\n");
- ncm_notify_speed_change.header.wIndex = ncm_interface.itf_num;
- usbd_edpt_xfer(rhport, ncm_interface.ep_notif, (uint8_t *) &ncm_notify_speed_change, sizeof(ncm_notify_speed_change));
+ ncm_notify_t notify_speed_change = {
+ .header = {
+ .bmRequestType_bit = {
+ .recipient = TUSB_REQ_RCPT_INTERFACE,
+ .type = TUSB_REQ_TYPE_CLASS,
+ .direction = TUSB_DIR_IN
+ },
+ .bRequest = CDC_NOTIF_CONNECTION_SPEED_CHANGE,
+ .wValue = 0,
+ .wIndex = ncm_interface.itf_num,
+ .wLength = 8
+ }
+ };
+ if (tud_speed_get() == TUSB_SPEED_HIGH) {
+ notify_speed_change.downlink = 480000000;
+ notify_speed_change.uplink = 480000000;
+ } else {
+ notify_speed_change.downlink = 12000000;
+ notify_speed_change.uplink = 12000000;
+ }
+
+ uint16_t notif_len = sizeof(notify_speed_change.header) + notify_speed_change.header.wLength;
+ ncm_epbuf.epnotif = notify_speed_change;
+ usbd_edpt_xfer(rhport, ncm_interface.ep_notif, (uint8_t*) &ncm_epbuf.epnotif, notif_len);
+
ncm_interface.notification_xmit_state = NOTIFICATION_CONNECTED;
ncm_interface.notification_xmit_is_running = true;
} else if (ncm_interface.notification_xmit_state == NOTIFICATION_CONNECTED) {
TU_LOG_DRV(" NOTIFICATION_CONNECTED\n");
- ncm_notify_connected.header.wIndex = ncm_interface.itf_num;
- usbd_edpt_xfer(rhport, ncm_interface.ep_notif, (uint8_t *) &ncm_notify_connected, sizeof(ncm_notify_connected));
+ ncm_notify_t notify_connected = {
+ .header = {
+ .bmRequestType_bit = {
+ .recipient = TUSB_REQ_RCPT_INTERFACE,
+ .type = TUSB_REQ_TYPE_CLASS,
+ .direction = TUSB_DIR_IN
+ },
+ .bRequest = CDC_NOTIF_NETWORK_CONNECTION,
+ .wValue = 1 /* Connected */,
+ .wIndex = ncm_interface.itf_num,
+ .wLength = 0,
+ },
+ };
+
+ uint16_t notif_len = sizeof(notify_connected.header) + notify_connected.header.wLength;
+ ncm_epbuf.epnotif = notify_connected;
+ usbd_edpt_xfer(rhport, ncm_interface.ep_notif, (uint8_t *) &ncm_epbuf.epnotif, notif_len);
+
ncm_interface.notification_xmit_state = NOTIFICATION_DONE;
ncm_interface.notification_xmit_is_running = true;
} else {
@@ -689,18 +719,36 @@ void tud_network_xmit(void *ref, uint16_t arg) {
/**
* Keep the receive logic busy and transfer pending packets to the glue logic.
+ * Avoid recursive calls due to wrong expectations of the net glue logic,
+ * see https://github.com/hathach/tinyusb/issues/2711
*/
void tud_network_recv_renew(void) {
TU_LOG_DRV("tud_network_recv_renew()\n");
- recv_transfer_datagram_to_glue_logic();
+ ncm_interface.tud_network_recv_renew_process_again = true;
+
+ if (ncm_interface.tud_network_recv_renew_active) {
+ TU_LOG_DRV("Re-entrant into tud_network_recv_renew, will process later\n");
+ return;
+ }
+
+ while (ncm_interface.tud_network_recv_renew_process_again) {
+ ncm_interface.tud_network_recv_renew_process_again = false;
+
+ // If the current function is called within recv_transfer_datagram_to_glue_logic,
+ // tud_network_recv_renew_process_again will become true, and the loop will run again
+ // Otherwise the loop will not run again
+ ncm_interface.tud_network_recv_renew_active = true;
+ recv_transfer_datagram_to_glue_logic();
+ ncm_interface.tud_network_recv_renew_active = false;
+ }
recv_try_to_start_new_reception(ncm_interface.rhport);
} // tud_network_recv_renew
/**
* Same as tud_network_recv_renew() but knows \a rhport
*/
-void tud_network_recv_renew_r(uint8_t rhport) {
+static void tud_network_recv_renew_r(uint8_t rhport) {
TU_LOG_DRV("tud_network_recv_renew_r(%d)\n", rhport);
ncm_interface.rhport = rhport;
@@ -721,10 +769,10 @@ void netd_init(void) {
memset(&ncm_interface, 0, sizeof(ncm_interface));
for (int i = 0; i < XMIT_NTB_N; ++i) {
- ncm_interface.xmit_free_ntb[i] = ncm_interface.xmit_ntb + i;
+ ncm_interface.xmit_free_ntb[i] = &ncm_epbuf.xmit[i].ntb;
}
for (int i = 0; i < RECV_NTB_N; ++i) {
- ncm_interface.recv_free_ntb[i] = ncm_interface.recv_ntb + i;
+ ncm_interface.recv_free_ntb[i] = &ncm_epbuf.recv[i].ntb;
}
} // netd_init
diff --git a/src/class/usbtmc/usbtmc_device.c b/src/class/usbtmc/usbtmc_device.c
index 129ff465d..abde9679f 100644
--- a/src/class/usbtmc/usbtmc_device.c
+++ b/src/class/usbtmc/usbtmc_device.c
@@ -131,14 +131,6 @@ typedef struct
uint8_t ep_int_in;
uint32_t ep_bulk_in_wMaxPacketSize;
uint32_t ep_bulk_out_wMaxPacketSize;
- // IN buffer is only used for first packet, not the remainder
- // in order to deal with prepending header
- CFG_TUSB_MEM_ALIGN uint8_t ep_bulk_in_buf[USBTMCD_BUFFER_SIZE];
- // OUT buffer receives one packet at a time
- CFG_TUSB_MEM_ALIGN uint8_t ep_bulk_out_buf[USBTMCD_BUFFER_SIZE];
- // Buffer int msg to ensure alignment and placement correctness
- CFG_TUSB_MEM_ALIGN uint8_t ep_int_in_buf[CFG_TUD_USBTMC_INT_EP_SIZE];
-
uint32_t transfer_size_remaining; // also used for requested length for bulk IN.
uint32_t transfer_size_sent; // To keep track of data bytes that have been queued in FIFO (not header bytes)
@@ -150,11 +142,23 @@ typedef struct
usbtmc_capabilities_specific_t const * capabilities;
} usbtmc_interface_state_t;
-CFG_TUD_MEM_SECTION tu_static usbtmc_interface_state_t usbtmc_state =
-{
- .itf_id = 0xFF,
+typedef struct {
+ // IN buffer is only used for first packet, not the remainder in order to deal with prepending header
+ TUD_EPBUF_DEF(epin, USBTMCD_BUFFER_SIZE);
+
+ // OUT buffer receives one packet at a time
+ TUD_EPBUF_DEF(epout, USBTMCD_BUFFER_SIZE);
+
+ // Buffer int msg
+ TUD_EPBUF_DEF(epnotif, CFG_TUD_USBTMC_INT_EP_SIZE);
+} usbtmc_epbuf_t;
+
+static usbtmc_interface_state_t usbtmc_state = {
+ .itf_id = 0xFF,
};
+CFG_TUD_MEM_SECTION static usbtmc_epbuf_t usbtmc_epbuf;
+
// We need all headers to fit in a single packet in this implementation, 32 bytes will fit all standard USBTMC headers
TU_VERIFY_STATIC(USBTMCD_BUFFER_SIZE >= 32u,"USBTMC dev buffer size too small");
@@ -176,7 +180,7 @@ osal_mutex_t usbtmcLock;
#define criticalEnter() do { (void) osal_mutex_lock(usbtmcLock,OSAL_TIMEOUT_WAIT_FOREVER); } while (0)
#define criticalLeave() do { (void) osal_mutex_unlock(usbtmcLock); } while (0)
-bool atomicChangeState(usbtmcd_state_enum expectedState, usbtmcd_state_enum newState)
+static bool atomicChangeState(usbtmcd_state_enum expectedState, usbtmcd_state_enum newState)
{
bool ret = true;
criticalEnter();
@@ -205,7 +209,7 @@ bool tud_usbtmc_transmit_dev_msg_data(
bool endOfMessage,
bool usingTermChar)
{
- const unsigned int txBufLen = sizeof(usbtmc_state.ep_bulk_in_buf);
+ const unsigned int txBufLen = USBTMCD_BUFFER_SIZE;
#ifndef NDEBUG
TU_ASSERT(len > 0u);
@@ -220,7 +224,7 @@ bool tud_usbtmc_transmit_dev_msg_data(
#endif
TU_VERIFY(usbtmc_state.state == STATE_TX_REQUESTED);
- usbtmc_msg_dev_dep_msg_in_header_t *hdr = (usbtmc_msg_dev_dep_msg_in_header_t*)usbtmc_state.ep_bulk_in_buf;
+ usbtmc_msg_dev_dep_msg_in_header_t *hdr = (usbtmc_msg_dev_dep_msg_in_header_t*)usbtmc_epbuf.epin;
tu_varclr(hdr);
hdr->header.MsgID = USBTMC_MSGID_DEV_DEP_MSG_IN;
hdr->header.bTag = usbtmc_state.lastBulkInTag;
@@ -235,7 +239,7 @@ bool tud_usbtmc_transmit_dev_msg_data(
len : (txBufLen - headerLen);
const size_t packetLen = headerLen + dataLen;
- memcpy((uint8_t*)(usbtmc_state.ep_bulk_in_buf) + headerLen, data, dataLen);
+ memcpy((uint8_t*)(usbtmc_epbuf.epin) + headerLen, data, dataLen);
usbtmc_state.transfer_size_remaining = len - dataLen;
usbtmc_state.transfer_size_sent = dataLen;
usbtmc_state.devInBuffer = (uint8_t const*) data + (dataLen);
@@ -243,7 +247,7 @@ bool tud_usbtmc_transmit_dev_msg_data(
bool stateChanged =
atomicChangeState(STATE_TX_REQUESTED, (packetLen >= txBufLen) ? STATE_TX_INITIATED : STATE_TX_SHORTED);
TU_VERIFY(stateChanged);
- TU_VERIFY(usbd_edpt_xfer(usbtmc_state.rhport, usbtmc_state.ep_bulk_in, usbtmc_state.ep_bulk_in_buf, (uint16_t)packetLen));
+ TU_VERIFY(usbd_edpt_xfer(usbtmc_state.rhport, usbtmc_state.ep_bulk_in, usbtmc_epbuf.epin, (uint16_t)packetLen));
return true;
}
@@ -255,8 +259,8 @@ bool tud_usbtmc_transmit_notification_data(const void * data, size_t len)
#endif
TU_VERIFY(usbd_edpt_busy(usbtmc_state.rhport, usbtmc_state.ep_int_in));
- TU_VERIFY(tu_memcpy_s(usbtmc_state.ep_int_in_buf, sizeof(usbtmc_state.ep_int_in_buf), data, len) == 0);
- TU_VERIFY(usbd_edpt_xfer(usbtmc_state.rhport, usbtmc_state.ep_int_in, usbtmc_state.ep_int_in_buf, (uint16_t)len));
+ TU_VERIFY(tu_memcpy_s(usbtmc_epbuf.epnotif, CFG_TUD_USBTMC_INT_EP_SIZE, data, len) == 0);
+ TU_VERIFY(usbd_edpt_xfer(usbtmc_state.rhport, usbtmc_state.ep_int_in, usbtmc_epbuf.epnotif, (uint16_t)len));
return true;
}
@@ -396,7 +400,7 @@ bool tud_usbtmc_start_bus_read(void)
default:
return false;
}
- TU_VERIFY(usbd_edpt_xfer(usbtmc_state.rhport, usbtmc_state.ep_bulk_out, usbtmc_state.ep_bulk_out_buf, (uint16_t)usbtmc_state.ep_bulk_out_wMaxPacketSize));
+ TU_VERIFY(usbd_edpt_xfer(usbtmc_state.rhport, usbtmc_state.ep_bulk_out, usbtmc_epbuf.epout, (uint16_t)usbtmc_state.ep_bulk_out_wMaxPacketSize));
return true;
}
@@ -501,7 +505,7 @@ bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
case STATE_IDLE:
{
TU_VERIFY(xferred_bytes >= sizeof(usbtmc_msg_generic_t));
- msg = (usbtmc_msg_generic_t*)(usbtmc_state.ep_bulk_out_buf);
+ msg = (usbtmc_msg_generic_t*)(usbtmc_epbuf.epout);
uint8_t invInvTag = (uint8_t)~(msg->header.bTagInverse);
TU_VERIFY(msg->header.bTag == invInvTag);
TU_VERIFY(msg->header.bTag != 0x00);
@@ -536,7 +540,7 @@ bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
return true;
}
case STATE_RCV:
- if(!handle_devMsgOut(rhport, usbtmc_state.ep_bulk_out_buf, xferred_bytes, xferred_bytes))
+ if(!handle_devMsgOut(rhport, usbtmc_epbuf.epout, xferred_bytes, xferred_bytes))
{
usbd_edpt_stall(rhport, usbtmc_state.ep_bulk_out);
return false;
@@ -565,24 +569,23 @@ bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
break;
case STATE_TX_INITIATED:
- if(usbtmc_state.transfer_size_remaining >= sizeof(usbtmc_state.ep_bulk_in_buf))
+ if(usbtmc_state.transfer_size_remaining >= USBTMCD_BUFFER_SIZE)
{
// Copy buffer to ensure alignment correctness
- memcpy(usbtmc_state.ep_bulk_in_buf, usbtmc_state.devInBuffer, sizeof(usbtmc_state.ep_bulk_in_buf));
- TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in,
- usbtmc_state.ep_bulk_in_buf, sizeof(usbtmc_state.ep_bulk_in_buf)));
- usbtmc_state.devInBuffer += sizeof(usbtmc_state.ep_bulk_in_buf);
- usbtmc_state.transfer_size_remaining -= sizeof(usbtmc_state.ep_bulk_in_buf);
- usbtmc_state.transfer_size_sent += sizeof(usbtmc_state.ep_bulk_in_buf);
+ memcpy(usbtmc_epbuf.epin, usbtmc_state.devInBuffer, USBTMCD_BUFFER_SIZE);
+ TU_VERIFY(usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_epbuf.epin, USBTMCD_BUFFER_SIZE));
+ usbtmc_state.devInBuffer += USBTMCD_BUFFER_SIZE;
+ usbtmc_state.transfer_size_remaining -= USBTMCD_BUFFER_SIZE;
+ usbtmc_state.transfer_size_sent += USBTMCD_BUFFER_SIZE;
}
else // last packet
{
size_t packetLen = usbtmc_state.transfer_size_remaining;
- memcpy(usbtmc_state.ep_bulk_in_buf, usbtmc_state.devInBuffer, usbtmc_state.transfer_size_remaining);
+ memcpy(usbtmc_epbuf.epin, usbtmc_state.devInBuffer, usbtmc_state.transfer_size_remaining);
usbtmc_state.transfer_size_sent += sizeof(usbtmc_state.transfer_size_remaining);
usbtmc_state.transfer_size_remaining = 0;
usbtmc_state.devInBuffer = NULL;
- TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_state.ep_bulk_in_buf, (uint16_t)packetLen) );
+ TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_epbuf.epin, (uint16_t)packetLen) );
if(((packetLen % usbtmc_state.ep_bulk_in_wMaxPacketSize) != 0) || (packetLen == 0 ))
{
usbtmc_state.state = STATE_TX_SHORTED;
@@ -592,7 +595,7 @@ bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
case STATE_ABORTING_BULK_IN:
// need to send short packet (ZLP?)
- TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_state.ep_bulk_in_buf,(uint16_t)0u));
+ TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_epbuf.epin,(uint16_t)0u));
usbtmc_state.state = STATE_ABORTING_BULK_IN_SHORTED;
return true;
@@ -744,7 +747,7 @@ bool usbtmcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request
if(usbtmc_state.transfer_size_sent == 0)
{
// Send short packet, nothing is in the buffer yet
- TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_state.ep_bulk_in_buf,(uint16_t)0u));
+ TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_epbuf.epin,(uint16_t)0u));
usbtmc_state.state = STATE_ABORTING_BULK_IN_SHORTED;
}
TU_VERIFY(tud_usbtmc_initiate_abort_bulk_in_cb(&(rsp.USBTMC_status)));
diff --git a/src/class/vendor/vendor_device.c b/src/class/vendor/vendor_device.c
index ca04d9a01..6f9b4853f 100644
--- a/src/class/vendor/vendor_device.c
+++ b/src/class/vendor/vendor_device.c
@@ -40,16 +40,12 @@ typedef struct {
uint8_t itf_num;
/*------------- From this point, data is not cleared by bus reset -------------*/
- // Endpoint Transfer buffer
- CFG_TUD_MEM_ALIGN uint8_t epout_buf[CFG_TUD_VENDOR_EPSIZE];
- CFG_TUD_MEM_ALIGN uint8_t epin_buf[CFG_TUD_VENDOR_EPSIZE];
-
struct {
tu_edpt_stream_t stream;
#if CFG_TUD_VENDOR_TX_BUFSIZE > 0
uint8_t ff_buf[CFG_TUD_VENDOR_TX_BUFSIZE];
#endif
- }tx;
+ } tx;
struct {
tu_edpt_stream_t stream;
@@ -60,10 +56,17 @@ typedef struct {
} vendord_interface_t;
-CFG_TUD_MEM_SECTION static vendord_interface_t _vendord_itf[CFG_TUD_VENDOR];
-
#define ITF_MEM_RESET_SIZE (offsetof(vendord_interface_t, itf_num) + sizeof(((vendord_interface_t *)0)->itf_num))
+static vendord_interface_t _vendord_itf[CFG_TUD_VENDOR];
+
+typedef struct {
+ TUD_EPBUF_DEF(epout, CFG_TUD_VENDOR_EPSIZE);
+ TUD_EPBUF_DEF(epin, CFG_TUD_VENDOR_EPSIZE);
+} vendord_epbuf_t;
+
+CFG_TUD_MEM_SECTION static vendord_epbuf_t _vendord_epbuf[CFG_TUD_VENDOR];
+
//--------------------------------------------------------------------
// Application API
//--------------------------------------------------------------------
@@ -94,7 +97,7 @@ bool tud_vendor_n_peek(uint8_t itf, uint8_t* u8) {
uint32_t tud_vendor_n_read (uint8_t itf, void* buffer, uint32_t bufsize) {
TU_VERIFY(itf < CFG_TUD_VENDOR, 0);
vendord_interface_t* p_itf = &_vendord_itf[itf];
- uint8_t const rhport = 0;
+ const uint8_t rhport = 0;
return tu_edpt_stream_read(rhport, &p_itf->rx.stream, buffer, bufsize);
}
@@ -102,7 +105,7 @@ uint32_t tud_vendor_n_read (uint8_t itf, void* buffer, uint32_t bufsize) {
void tud_vendor_n_read_flush (uint8_t itf) {
TU_VERIFY(itf < CFG_TUD_VENDOR, );
vendord_interface_t* p_itf = &_vendord_itf[itf];
- uint8_t const rhport = 0;
+ const uint8_t rhport = 0;
tu_edpt_stream_clear(&p_itf->rx.stream);
tu_edpt_stream_read_xfer(rhport, &p_itf->rx.stream);
@@ -111,10 +114,10 @@ void tud_vendor_n_read_flush (uint8_t itf) {
//--------------------------------------------------------------------+
// Write API
//--------------------------------------------------------------------+
-uint32_t tud_vendor_n_write (uint8_t itf, void const* buffer, uint32_t bufsize) {
+uint32_t tud_vendor_n_write (uint8_t itf, const void* buffer, uint32_t bufsize) {
TU_VERIFY(itf < CFG_TUD_VENDOR, 0);
vendord_interface_t* p_itf = &_vendord_itf[itf];
- uint8_t const rhport = 0;
+ const uint8_t rhport = 0;
return tu_edpt_stream_write(rhport, &p_itf->tx.stream, buffer, (uint16_t) bufsize);
}
@@ -122,7 +125,7 @@ uint32_t tud_vendor_n_write (uint8_t itf, void const* buffer, uint32_t bufsize)
uint32_t tud_vendor_n_write_flush (uint8_t itf) {
TU_VERIFY(itf < CFG_TUD_VENDOR, 0);
vendord_interface_t* p_itf = &_vendord_itf[itf];
- uint8_t const rhport = 0;
+ const uint8_t rhport = 0;
return tu_edpt_stream_write_xfer(rhport, &p_itf->tx.stream);
}
@@ -130,7 +133,7 @@ uint32_t tud_vendor_n_write_flush (uint8_t itf) {
uint32_t tud_vendor_n_write_available (uint8_t itf) {
TU_VERIFY(itf < CFG_TUD_VENDOR, 0);
vendord_interface_t* p_itf = &_vendord_itf[itf];
- uint8_t const rhport = 0;
+ const uint8_t rhport = 0;
return tu_edpt_stream_write_available(rhport, &p_itf->tx.stream);
}
@@ -143,6 +146,7 @@ void vendord_init(void) {
for(uint8_t i=0; i<CFG_TUD_VENDOR; i++) {
vendord_interface_t* p_itf = &_vendord_itf[i];
+ vendord_epbuf_t* p_epbuf = &_vendord_epbuf[i];
uint8_t* rx_ff_buf =
#if CFG_TUD_VENDOR_RX_BUFSIZE > 0
@@ -153,7 +157,7 @@ void vendord_init(void) {
tu_edpt_stream_init(&p_itf->rx.stream, false, false, false,
rx_ff_buf, CFG_TUD_VENDOR_RX_BUFSIZE,
- p_itf->epout_buf, CFG_TUD_VENDOR_EPSIZE);
+ p_epbuf->epout, CFG_TUD_VENDOR_EPSIZE);
uint8_t* tx_ff_buf =
#if CFG_TUD_VENDOR_TX_BUFSIZE > 0
@@ -164,7 +168,7 @@ void vendord_init(void) {
tu_edpt_stream_init(&p_itf->tx.stream, false, true, false,
tx_ff_buf, CFG_TUD_VENDOR_TX_BUFSIZE,
- p_itf->epin_buf, CFG_TUD_VENDOR_EPSIZE);
+ p_epbuf->epin, CFG_TUD_VENDOR_EPSIZE);
}
}
@@ -190,7 +194,7 @@ void vendord_reset(uint8_t rhport) {
}
}
-uint16_t vendord_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t max_len) {
+uint16_t vendord_open(uint8_t rhport, const tusb_desc_interface_t* desc_itf, uint16_t max_len) {
TU_VERIFY(TUSB_CLASS_VENDOR_SPECIFIC == desc_itf->bInterfaceClass, 0);
const uint8_t* p_desc = tu_desc_next(desc_itf);
const uint8_t* desc_end = p_desc + max_len;
@@ -237,25 +241,29 @@ uint16_t vendord_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, ui
bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
(void) result;
- uint8_t itf = 0;
- vendord_interface_t* p_itf = _vendord_itf;
+ uint8_t itf;
+ vendord_interface_t* p_vendor;
- for ( ; ; itf++, p_itf++) {
- if (itf >= CFG_TUD_VENDOR) return false;
- if ((ep_addr == p_itf->rx.stream.ep_addr) || (ep_addr == p_itf->tx.stream.ep_addr)) break;
+ for (itf = 0; itf < CFG_TUD_VENDOR; itf++) {
+ p_vendor = &_vendord_itf[itf];
+ if ((ep_addr == p_vendor->rx.stream.ep_addr) || (ep_addr == p_vendor->tx.stream.ep_addr)) {
+ break;
+ }
}
+ TU_VERIFY(itf < CFG_TUD_VENDOR);
+ vendord_epbuf_t* p_epbuf = &_vendord_epbuf[itf];
- if ( ep_addr == p_itf->rx.stream.ep_addr ) {
+ if ( ep_addr == p_vendor->rx.stream.ep_addr ) {
// Received new data: put into stream's fifo
- tu_edpt_stream_read_xfer_complete(&p_itf->rx.stream, xferred_bytes);
+ tu_edpt_stream_read_xfer_complete(&p_vendor->rx.stream, xferred_bytes);
// Invoked callback if any
if (tud_vendor_rx_cb) {
- tud_vendor_rx_cb(itf, p_itf->epout_buf, (uint16_t) xferred_bytes);
+ tud_vendor_rx_cb(itf, p_epbuf->epout, (uint16_t) xferred_bytes);
}
- tu_edpt_stream_read_xfer(rhport, &p_itf->rx.stream);
- } else if ( ep_addr == p_itf->tx.stream.ep_addr ) {
+ tu_edpt_stream_read_xfer(rhport, &p_vendor->rx.stream);
+ } else if ( ep_addr == p_vendor->tx.stream.ep_addr ) {
// Send complete
if (tud_vendor_tx_cb) {
tud_vendor_tx_cb(itf, (uint16_t) xferred_bytes);
@@ -263,9 +271,9 @@ bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
#if CFG_TUD_VENDOR_TX_BUFSIZE > 0
// try to send more if possible
- if ( 0 == tu_edpt_stream_write_xfer(rhport, &p_itf->tx.stream) ) {
+ if ( 0 == tu_edpt_stream_write_xfer(rhport, &p_vendor->tx.stream) ) {
// If there is no data left, a ZLP should be sent if xferred_bytes is multiple of EP Packet size and not zero
- tu_edpt_stream_write_zlp_if_needed(rhport, &p_itf->tx.stream, xferred_bytes);
+ tu_edpt_stream_write_zlp_if_needed(rhport, &p_vendor->tx.stream, xferred_bytes);
}
#endif
}
diff --git a/src/class/video/video_device.c b/src/class/video/video_device.c
index 70dbb5b9e..86513ddf0 100644
--- a/src/class/video/video_device.c
+++ b/src/class/video/video_device.c
@@ -116,34 +116,32 @@ typedef struct TU_ATTR_PACKED {
uint8_t state; /* 0:probing 1:committed 2:streaming */
video_probe_and_commit_control_t probe_commit_payload; /* Probe and Commit control */
- /*------------- From this point, data is not cleared by bus reset -------------*/
- CFG_TUSB_MEM_ALIGN uint8_t ep_buf[CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE]; /* EP transfer buffer for streaming */
} videod_streaming_interface_t;
+typedef struct {
+ TUD_EPBUF_DEF(buf, CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE);
+} videod_streaming_epbuf_t;
+
/* video control interface */
typedef struct TU_ATTR_PACKED {
- uint8_t const *beg; /* The head of the first video control interface descriptor */
- uint16_t len; /* Byte length of the descriptors */
- uint16_t cur; /* offset for current video control interface */
- uint8_t stm[CFG_TUD_VIDEO_STREAMING]; /* Indices of streaming interface */
- uint8_t error_code; /* error code */
- uint8_t power_mode;
-
- /*------------- From this point, data is not cleared by bus reset -------------*/
- // CFG_TUSB_MEM_ALIGN uint8_t ctl_buf[64]; /* EP transfer buffer for interrupt transfer */
-
+ const uint8_t*beg; /* The head of the first video control interface descriptor */
+ uint16_t len; /* Byte length of the descriptors */
+ uint16_t cur; /* offset for current video control interface */
+ uint8_t stm[CFG_TUD_VIDEO_STREAMING]; /* Indices of streaming interface */
+ uint8_t error_code; /* error code */
+ uint8_t power_mode;
} videod_interface_t;
-#define ITF_STM_MEM_RESET_SIZE offsetof(videod_streaming_interface_t, ep_buf)
-
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
-CFG_TUD_MEM_SECTION tu_static videod_interface_t _videod_itf[CFG_TUD_VIDEO];
-CFG_TUD_MEM_SECTION tu_static videod_streaming_interface_t _videod_streaming_itf[CFG_TUD_VIDEO_STREAMING];
+static videod_interface_t _videod_itf[CFG_TUD_VIDEO];
+
+static videod_streaming_interface_t _videod_streaming_itf[CFG_TUD_VIDEO_STREAMING];
+CFG_TUD_MEM_SECTION static videod_streaming_epbuf_t _videod_streaming_epbuf[CFG_TUD_VIDEO_STREAMING];
-tu_static uint8_t const _cap_get = 0x1u; /* support for GET */
-tu_static uint8_t const _cap_get_set = 0x3u; /* support for GET and SET */
+static uint8_t const _cap_get = 0x1u; /* support for GET */
+static uint8_t const _cap_get_set = 0x3u; /* support for GET and SET */
//--------------------------------------------------------------------+
// Debug
@@ -816,21 +814,20 @@ static bool _open_vs_itf(uint8_t rhport, videod_streaming_interface_t *stm, uint
}
/** Prepare the next packet payload. */
-static uint_fast16_t _prepare_in_payload(videod_streaming_interface_t *stm)
-{
+static uint_fast16_t _prepare_in_payload(videod_streaming_interface_t *stm, uint8_t* ep_buf) {
uint_fast16_t remaining = stm->bufsize - stm->offset;
- uint_fast16_t hdr_len = stm->ep_buf[0];
+ uint_fast16_t hdr_len = ep_buf[0];
uint_fast16_t pkt_len = stm->max_payload_transfer_size;
if (hdr_len + remaining < pkt_len) {
pkt_len = hdr_len + remaining;
}
TU_ASSERT(pkt_len >= hdr_len);
uint_fast16_t data_len = pkt_len - hdr_len;
- memcpy(&stm->ep_buf[hdr_len], stm->buffer + stm->offset, data_len);
+ memcpy(&ep_buf[hdr_len], stm->buffer + stm->offset, data_len);
stm->offset += data_len;
remaining -= data_len;
if (!remaining) {
- tusb_video_payload_header_t *hdr = (tusb_video_payload_header_t*)stm->ep_buf;
+ tusb_video_payload_header_t *hdr = (tusb_video_payload_header_t*) ep_buf;
hdr->EndOfFrame = 1;
}
return hdr_len + data_len;
@@ -1001,7 +998,8 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage,
tusb_control_request_t const *request,
uint_fast8_t stm_idx) {
(void)rhport;
- videod_streaming_interface_t *self = &_videod_streaming_itf[stm_idx];
+ videod_streaming_interface_t *stm = &_videod_streaming_itf[stm_idx];
+ videod_streaming_epbuf_t *stm_epbuf = &_videod_streaming_epbuf[stm_idx];
uint8_t const ctrl_sel = TU_U16_HIGH(request->wValue);
TU_LOG_DRV("%s_Control(%s)\r\n", tu_str_video_vs_control_selector[ctrl_sel], tu_lookup_find(&tu_table_video_request, request->bRequest));
@@ -1013,7 +1011,7 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage,
case VIDEO_REQUEST_GET_CUR:
if (stage == CONTROL_STAGE_SETUP) {
/* TODO */
- TU_VERIFY(tud_control_xfer(rhport, request, &self->error_code, sizeof(uint8_t)), VIDEO_ERROR_UNKNOWN);
+ TU_VERIFY(tud_control_xfer(rhport, request, &stm->error_code, sizeof(uint8_t)), VIDEO_ERROR_UNKNOWN);
}
return VIDEO_ERROR_NONE;
@@ -1028,17 +1026,17 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage,
break;
case VIDEO_VS_CTL_PROBE:
- if (self->state != VS_STATE_PROBING) {
- self->state = VS_STATE_PROBING;
+ if (stm->state != VS_STATE_PROBING) {
+ stm->state = VS_STATE_PROBING;
}
switch (request->bRequest) {
case VIDEO_REQUEST_SET_CUR:
if (stage == CONTROL_STAGE_SETUP) {
- TU_VERIFY(tud_control_xfer(rhport, request, &self->probe_commit_payload, sizeof(video_probe_and_commit_control_t)),
+ TU_VERIFY(tud_control_xfer(rhport, request, &stm->probe_commit_payload, sizeof(video_probe_and_commit_control_t)),
VIDEO_ERROR_UNKNOWN);
} else if (stage == CONTROL_STAGE_DATA) {
- TU_VERIFY(_update_streaming_parameters(self, &self->probe_commit_payload),
+ TU_VERIFY(_update_streaming_parameters(stm, &stm->probe_commit_payload),
VIDEO_ERROR_INVALID_VALUE_WITHIN_RANGE);
}
return VIDEO_ERROR_NONE;
@@ -1046,7 +1044,7 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage,
case VIDEO_REQUEST_GET_CUR:
if (stage == CONTROL_STAGE_SETUP) {
TU_VERIFY(request->wLength, VIDEO_ERROR_UNKNOWN);
- TU_VERIFY(tud_control_xfer(rhport, request, &self->probe_commit_payload, sizeof(video_probe_and_commit_control_t)), VIDEO_ERROR_UNKNOWN);
+ TU_VERIFY(tud_control_xfer(rhport, request, &stm->probe_commit_payload, sizeof(video_probe_and_commit_control_t)), VIDEO_ERROR_UNKNOWN);
}
return VIDEO_ERROR_NONE;
@@ -1056,8 +1054,8 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage,
case VIDEO_REQUEST_GET_DEF:
if (stage == CONTROL_STAGE_SETUP) {
TU_VERIFY(request->wLength, VIDEO_ERROR_UNKNOWN);
- video_probe_and_commit_control_t tmp = self->probe_commit_payload;
- TU_VERIFY(_negotiate_streaming_parameters(self, request->bRequest, &tmp), VIDEO_ERROR_INVALID_VALUE_WITHIN_RANGE);
+ video_probe_and_commit_control_t tmp = stm->probe_commit_payload;
+ TU_VERIFY(_negotiate_streaming_parameters(stm, request->bRequest, &tmp), VIDEO_ERROR_INVALID_VALUE_WITHIN_RANGE);
TU_VERIFY(tud_control_xfer(rhport, request, &tmp, sizeof(tmp)), VIDEO_ERROR_UNKNOWN);
}
return VIDEO_ERROR_NONE;
@@ -1085,23 +1083,23 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage,
switch (request->bRequest) {
case VIDEO_REQUEST_SET_CUR:
if (stage == CONTROL_STAGE_SETUP) {
- TU_VERIFY(tud_control_xfer(rhport, request, &self->probe_commit_payload, sizeof(video_probe_and_commit_control_t)), VIDEO_ERROR_UNKNOWN);
+ TU_VERIFY(tud_control_xfer(rhport, request, &stm->probe_commit_payload, sizeof(video_probe_and_commit_control_t)), VIDEO_ERROR_UNKNOWN);
} else if (stage == CONTROL_STAGE_DATA) {
- video_probe_and_commit_control_t *param = &self->probe_commit_payload;
- TU_VERIFY(_update_streaming_parameters(self, param), VIDEO_ERROR_INVALID_VALUE_WITHIN_RANGE);
+ video_probe_and_commit_control_t *param = &stm->probe_commit_payload;
+ TU_VERIFY(_update_streaming_parameters(stm, param), VIDEO_ERROR_INVALID_VALUE_WITHIN_RANGE);
/* Set the negotiated value */
- self->max_payload_transfer_size = param->dwMaxPayloadTransferSize;
+ stm->max_payload_transfer_size = param->dwMaxPayloadTransferSize;
int ret = VIDEO_ERROR_NONE;
if (tud_video_commit_cb) {
- ret = tud_video_commit_cb(self->index_vc, self->index_vs, param);
+ ret = tud_video_commit_cb(stm->index_vc, stm->index_vs, param);
}
if (VIDEO_ERROR_NONE == ret) {
- self->state = VS_STATE_COMMITTED;
- self->buffer = NULL;
- self->bufsize = 0;
- self->offset = 0;
+ stm->state = VS_STATE_COMMITTED;
+ stm->buffer = NULL;
+ stm->bufsize = 0;
+ stm->offset = 0;
/* initialize payload header */
- tusb_video_payload_header_t *hdr = (tusb_video_payload_header_t*)self->ep_buf;
+ tusb_video_payload_header_t *hdr = (tusb_video_payload_header_t*)stm_epbuf->buf;
hdr->bHeaderLength = sizeof(*hdr);
hdr->bmHeaderInfo = 0;
}
@@ -1111,7 +1109,7 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage,
case VIDEO_REQUEST_GET_CUR:
if (stage == CONTROL_STAGE_SETUP) {
TU_VERIFY(request->wLength, VIDEO_ERROR_UNKNOWN);
- TU_VERIFY(tud_control_xfer(rhport, request, &self->probe_commit_payload, sizeof(video_probe_and_commit_control_t)), VIDEO_ERROR_UNKNOWN);
+ TU_VERIFY(tud_control_xfer(rhport, request, &stm->probe_commit_payload, sizeof(video_probe_and_commit_control_t)), VIDEO_ERROR_UNKNOWN);
}
return VIDEO_ERROR_NONE;
@@ -1199,12 +1197,14 @@ bool tud_video_n_streaming(uint_fast8_t ctl_idx, uint_fast8_t stm_idx)
return true;
}
-bool tud_video_n_frame_xfer(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, void *buffer, size_t bufsize)
-{
+bool tud_video_n_frame_xfer(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, void *buffer, size_t bufsize) {
TU_ASSERT(ctl_idx < CFG_TUD_VIDEO);
TU_ASSERT(stm_idx < CFG_TUD_VIDEO_STREAMING);
+
if (!buffer || !bufsize) return false;
videod_streaming_interface_t *stm = _get_instance_streaming(ctl_idx, stm_idx);
+ videod_streaming_epbuf_t *stm_epbuf = &_videod_streaming_epbuf[ctl_idx];
+
if (!stm || !stm->desc.ep[0] || stm->buffer) return false;
if (stm->state == VS_STATE_PROBING) return false;
@@ -1221,14 +1221,14 @@ bool tud_video_n_frame_xfer(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, void *bu
TU_VERIFY( usbd_edpt_claim(0, ep_addr) );
/* update the packet header */
- tusb_video_payload_header_t *hdr = (tusb_video_payload_header_t*)stm->ep_buf;
+ tusb_video_payload_header_t *hdr = (tusb_video_payload_header_t*)stm_epbuf->buf;
hdr->FrameID ^= 1;
hdr->EndOfFrame = 0;
/* update the packet data */
stm->buffer = (uint8_t*)buffer;
stm->bufsize = bufsize;
- uint_fast16_t pkt_len = _prepare_in_payload(stm);
- TU_ASSERT( usbd_edpt_xfer(0, ep_addr, stm->ep_buf, (uint16_t) pkt_len), 0);
+ uint_fast16_t pkt_len = _prepare_in_payload(stm, stm_epbuf->buf);
+ TU_ASSERT( usbd_edpt_xfer(0, ep_addr, stm_epbuf->buf, (uint16_t) pkt_len), 0);
return true;
}
@@ -1242,7 +1242,7 @@ void videod_init(void) {
}
for (uint_fast8_t i = 0; i < CFG_TUD_VIDEO_STREAMING; ++i) {
videod_streaming_interface_t *stm = &_videod_streaming_itf[i];
- tu_memclr(stm, ITF_STM_MEM_RESET_SIZE);
+ tu_memclr(stm, sizeof(videod_streaming_interface_t));
}
}
@@ -1258,7 +1258,7 @@ void videod_reset(uint8_t rhport) {
}
for (uint_fast8_t i = 0; i < CFG_TUD_VIDEO_STREAMING; ++i) {
videod_streaming_interface_t *stm = &_videod_streaming_itf[i];
- tu_memclr(stm, ITF_STM_MEM_RESET_SIZE);
+ tu_memclr(stm, sizeof(videod_streaming_interface_t));
}
}
@@ -1307,7 +1307,7 @@ uint16_t videod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin
#ifdef TUP_DCD_EDPT_ISO_ALLOC
/* Allocate ISO endpoints */
uint16_t ep_size = 0;
- uint16_t ep_addr = 0;
+ uint8_t ep_addr = 0;
uint8_t const *p_desc = (uint8_t const*)itf_desc + stm->desc.beg;
uint8_t const *p_desc_end = (uint8_t const*)itf_desc + stm->desc.end;
while (p_desc < p_desc_end) {
@@ -1392,13 +1392,14 @@ bool videod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3
uint8_t const *desc = ctl->beg;
if (ep_addr == _desc_ep_addr(desc + ep_ofs)) break;
}
-
TU_ASSERT(itf < CFG_TUD_VIDEO_STREAMING);
+ videod_streaming_epbuf_t *stm_epbuf = &_videod_streaming_epbuf[itf];
+
if (stm->offset < stm->bufsize) {
/* Claim the endpoint */
TU_VERIFY( usbd_edpt_claim(rhport, ep_addr), 0);
- uint_fast16_t pkt_len = _prepare_in_payload(stm);
- TU_ASSERT( usbd_edpt_xfer(rhport, ep_addr, stm->ep_buf, (uint16_t) pkt_len), 0);
+ uint_fast16_t pkt_len = _prepare_in_payload(stm, stm_epbuf->buf);
+ TU_ASSERT( usbd_edpt_xfer(rhport, ep_addr, stm_epbuf->buf, (uint16_t) pkt_len), 0);
} else {
stm->buffer = NULL;
stm->bufsize = 0;
diff --git a/src/class/video/video_device.h b/src/class/video/video_device.h
index 92930c013..648a221d5 100644
--- a/src/class/video/video_device.h
+++ b/src/class/video/video_device.h
@@ -40,6 +40,8 @@ extern "C" {
// CFG_TUD_VIDEO > 1
//--------------------------------------------------------------------+
+bool tud_video_n_connected(uint_fast8_t ctl_idx);
+
/** Return true if streaming
*
* @param[in] ctl_idx Destination control interface index
diff --git a/src/common/tusb_common.h b/src/common/tusb_common.h
index d3e0cf888..74cac965d 100644
--- a/src/common/tusb_common.h
+++ b/src/common/tusb_common.h
@@ -123,11 +123,15 @@ TU_ATTR_ALWAYS_INLINE static inline int tu_memcpy_s(void *dest, size_t destsz, c
//------------- Bytes -------------//
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_u32(uint8_t b3, uint8_t b2, uint8_t b1, uint8_t b0) {
- return ( ((uint32_t) b3) << 24) | ( ((uint32_t) b2) << 16) | ( ((uint32_t) b1) << 8) | b0;
+ return (((uint32_t)b3) << 24) | (((uint32_t)b2) << 16) | (((uint32_t)b1) << 8) | b0;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_u32_from_u16(uint16_t high, uint16_t low) {
+ return (((uint32_t)high) << 16) | low;
}
TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_u16(uint8_t high, uint8_t low) {
- return (uint16_t) ((((uint16_t) high) << 8) | low);
+ return (uint16_t)((((uint16_t)high) << 8) | low);
}
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u32_byte3(uint32_t ui32) { return TU_U32_BYTE3(ui32); }
@@ -172,7 +176,8 @@ TU_ATTR_ALWAYS_INLINE static inline bool tu_is_aligned32(uint32_t value) { retur
TU_ATTR_ALWAYS_INLINE static inline bool tu_is_aligned64(uint64_t value) { return (value & 0x3FUL) == 0; }
//------------- Mathematics -------------//
-TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_div_ceil(uint32_t v, uint32_t d) { return (v + d -1)/d; }
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_div_ceil(uint32_t v, uint32_t d) { return TU_DIV_CEIL(v, d); }
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_round_up(uint32_t v, uint32_t f) { return tu_div_ceil(v, f) * f; }
// log2 of a value is its MSB's position
// TODO use clz TODO remove
diff --git a/src/common/tusb_compiler.h b/src/common/tusb_compiler.h
index 646c22b29..9b33a6f61 100644
--- a/src/common/tusb_compiler.h
+++ b/src/common/tusb_compiler.h
@@ -136,7 +136,7 @@
#define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name)))
#define TU_ATTR_PACKED __attribute__ ((packed))
#define TU_ATTR_WEAK __attribute__ ((weak))
- // #define TU_ATTR_WEAK_ALIAS(f) __attribute__ ((weak, alias(#f))
+ // #define TU_ATTR_WEAK_ALIAS(f) __attribute__ ((weak, alias(#f)))
#ifndef TU_ATTR_ALWAYS_INLINE // allow to override for debug
#define TU_ATTR_ALWAYS_INLINE __attribute__ ((always_inline))
#endif
@@ -189,6 +189,7 @@
#define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name)))
#define TU_ATTR_PACKED __attribute__ ((packed))
#define TU_ATTR_WEAK __attribute__ ((weak))
+ // #define TU_ATTR_WEAK_ALIAS(f) __attribute__ ((weak, alias(#f)))
#define TU_ATTR_ALWAYS_INLINE __attribute__ ((always_inline))
#define TU_ATTR_DEPRECATED(mess) __attribute__ ((deprecated(mess))) // warn if function with this attribute is used
#define TU_ATTR_UNUSED __attribute__ ((unused)) // Function/Variable is meant to be possibly unused
@@ -216,6 +217,7 @@
#define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name)))
#define TU_ATTR_PACKED __attribute__ ((packed))
#define TU_ATTR_WEAK __attribute__ ((weak))
+ // #define TU_ATTR_WEAK_ALIAS(f) __attribute__ ((weak, alias(#f)))
#ifndef TU_ATTR_ALWAYS_INLINE // allow to override for debug
#define TU_ATTR_ALWAYS_INLINE __attribute__ ((always_inline))
#endif
@@ -244,6 +246,7 @@
#define TU_ATTR_SECTION(sec_name)
#define TU_ATTR_PACKED
#define TU_ATTR_WEAK
+ // #define TU_ATTR_WEAK_ALIAS(f)
#define TU_ATTR_ALWAYS_INLINE
#define TU_ATTR_DEPRECATED(mess)
#define TU_ATTR_UNUSED
diff --git a/src/common/tusb_mcu.h b/src/common/tusb_mcu.h
index c77afecad..d282c890d 100644
--- a/src/common/tusb_mcu.h
+++ b/src/common/tusb_mcu.h
@@ -169,6 +169,7 @@
defined (STM32F107xB) || defined (STM32F107xC)
#define TUP_USBIP_DWC2
#define TUP_USBIP_DWC2_STM32
+ #define CFG_TUH_DWC2_DMA_ENABLE_DEFAULT 0
#define TUP_DCD_ENDPOINT_MAX 4
#elif defined(STM32F102x6) || defined(STM32F102xB) || \
@@ -237,6 +238,11 @@
#define TUP_USBIP_FSDEV_STM32
#define TUP_DCD_ENDPOINT_MAX 8
+#elif TU_CHECK_MCU(OPT_MCU_STM32C0)
+ #define TUP_USBIP_FSDEV
+ #define TUP_USBIP_FSDEV_STM32
+ #define TUP_DCD_ENDPOINT_MAX 8
+
#elif TU_CHECK_MCU(OPT_MCU_STM32L0, OPT_MCU_STM32L1)
#define TUP_USBIP_FSDEV
#define TUP_USBIP_FSDEV_STM32
@@ -298,6 +304,16 @@
#define TUP_USBIP_FSDEV_STM32
#define TUP_DCD_ENDPOINT_MAX 8
+#elif TU_CHECK_MCU(OPT_MCU_STM32H7RS)
+ #define TUP_USBIP_DWC2
+ #define TUP_USBIP_DWC2_STM32
+
+ // FS has 6, HS has 9
+ #define TUP_DCD_ENDPOINT_MAX 9
+
+ // MCU with on-chip HS Phy
+ #define TUP_RHPORT_HIGHSPEED 1
+
//--------------------------------------------------------------------+
// Sony
//--------------------------------------------------------------------+
@@ -343,6 +359,11 @@
#define TUP_USBIP_DWC2
#define TUP_USBIP_DWC2_ESP32
#define TUP_DCD_ENDPOINT_MAX 7 // only 5 TX FIFO for endpoint IN
+ #define CFG_TUSB_OS_INC_PATH_DEFAULT freertos/
+
+ // Disable slave if DMA is enabled
+ #define CFG_TUD_DWC2_SLAVE_ENABLE_DEFAULT !CFG_TUD_DWC2_DMA_ENABLE
+ #define CFG_TUH_DWC2_SLAVE_ENABLE_DEFAULT !CFG_TUH_DWC2_DMA_ENABLE
#elif TU_CHECK_MCU(OPT_MCU_ESP32P4)
#define TUP_USBIP_DWC2
@@ -350,11 +371,24 @@
#define TUP_RHPORT_HIGHSPEED 1 // port0 FS, port1 HS
#define TUP_DCD_ENDPOINT_MAX 16 // FS 7 ep, HS 16 ep
+ #define CFG_TUSB_OS_INC_PATH_DEFAULT freertos/
+
+ // Disable slave if DMA is enabled
+ #define CFG_TUD_DWC2_SLAVE_ENABLE_DEFAULT !CFG_TUD_DWC2_DMA_ENABLE
+ #define CFG_TUH_DWC2_SLAVE_ENABLE_DEFAULT !CFG_TUH_DWC2_DMA_ENABLE
+
+ // Enable dcache if DMA is enabled
+ #define CFG_TUD_MEM_DCACHE_ENABLE_DEFAULT CFG_TUD_DWC2_DMA_ENABLE
+ #define CFG_TUH_MEM_DCACHE_ENABLE_DEFAULT CFG_TUH_DWC2_DMA_ENABLE
+ #define CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT 64
+
#elif TU_CHECK_MCU(OPT_MCU_ESP32, OPT_MCU_ESP32C2, OPT_MCU_ESP32C3, OPT_MCU_ESP32C6, OPT_MCU_ESP32H2)
#if (CFG_TUD_ENABLED || !(defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421))
#error "MCUs are only supported with CFG_TUH_MAX3421 enabled"
#endif
+
#define TUP_DCD_ENDPOINT_MAX 0
+ #define CFG_TUSB_OS_INC_PATH_DEFAULT freertos/
//--------------------------------------------------------------------+
// Dialog
@@ -366,6 +400,7 @@
// Raspberry Pi
//--------------------------------------------------------------------+
#elif TU_CHECK_MCU(OPT_MCU_RP2040)
+ #define TUP_DCD_EDPT_ISO_ALLOC
#define TUP_DCD_ENDPOINT_MAX 16
#define TU_ATTR_FAST_FUNC __attribute__((section(".time_critical.tinyusb")))
@@ -535,7 +570,7 @@
#define TUP_DCD_EDPT_ISO_ALLOC
#endif
-#if defined(TUP_USBIP_DWC2) // && CFG_TUD_DWC2_DMA == 0
+#if defined(TUP_USBIP_DWC2) // && CFG_TUD_DWC2_DMA_ENABLE == 0
#define TUP_MEM_CONST_ADDR
#endif
diff --git a/src/common/tusb_private.h b/src/common/tusb_private.h
index 8a479c042..c71775abb 100644
--- a/src/common/tusb_private.h
+++ b/src/common/tusb_private.h
@@ -24,9 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-
-#ifndef _TUSB_PRIVATE_H_
-#define _TUSB_PRIVATE_H_
+#ifndef TUSB_PRIVATE_H_
+#define TUSB_PRIVATE_H_
// Internal Helper used by Host and Device Stack
@@ -34,6 +33,13 @@
extern "C" {
#endif
+#define TUP_USBIP_CONTROLLER_NUM 2
+extern tusb_role_t _tusb_rhport_role[TUP_USBIP_CONTROLLER_NUM];
+
+//--------------------------------------------------------------------+
+// Endpoint
+//--------------------------------------------------------------------+
+
typedef struct TU_ATTR_PACKED {
volatile uint8_t busy : 1;
volatile uint8_t stalled : 1;
@@ -163,4 +169,4 @@ bool tu_edpt_stream_peek(tu_edpt_stream_t* s, uint8_t* ch) {
}
#endif
-#endif /* _TUSB_PRIVATE_H_ */
+#endif
diff --git a/src/common/tusb_types.h b/src/common/tusb_types.h
index 53bb367cb..9e4d9380c 100644
--- a/src/common/tusb_types.h
+++ b/src/common/tusb_types.h
@@ -35,14 +35,51 @@
extern "C" {
#endif
+//------------- Device DCache declaration -------------//
+#define TUD_EPBUF_DCACHE_SIZE(_size) (CFG_TUD_MEM_DCACHE_ENABLE ? \
+ (TU_DIV_CEIL(_size, CFG_TUD_MEM_DCACHE_LINE_SIZE) * CFG_TUD_MEM_DCACHE_LINE_SIZE) : (_size))
+
+// Declare an endpoint buffer with uint8_t[size]
+#define TUD_EPBUF_DEF(_name, _size) \
+ union { \
+ CFG_TUD_MEM_ALIGN uint8_t _name[_size]; \
+ uint8_t _name##_dcache_padding[TUD_EPBUF_DCACHE_SIZE(_size)]; \
+ }
+
+// Declare an endpoint buffer with a type
+#define TUD_EPBUF_TYPE_DEF(_type, _name) \
+ union { \
+ CFG_TUD_MEM_ALIGN _type _name; \
+ uint8_t _name##_dcache_padding[TUD_EPBUF_DCACHE_SIZE(sizeof(_type))]; \
+ }
+
+//------------- Host DCache declaration -------------//
+#define TUH_EPBUF_DCACHE_SIZE(_size) (CFG_TUH_MEM_DCACHE_ENABLE ? \
+ (TU_DIV_CEIL(_size, CFG_TUH_MEM_DCACHE_LINE_SIZE) * CFG_TUH_MEM_DCACHE_LINE_SIZE) : (_size))
+
+// Declare an endpoint buffer with uint8_t[size]
+#define TUH_EPBUF_DEF(_name, _size) \
+ union { \
+ CFG_TUH_MEM_ALIGN uint8_t _name[_size]; \
+ uint8_t _name##_dcache_padding[TUH_EPBUF_DCACHE_SIZE(_size)]; \
+ }
+
+// Declare an endpoint buffer with a type
+#define TUH_EPBUF_TYPE_DEF(_type, _name) \
+ union { \
+ CFG_TUH_MEM_ALIGN _type _name; \
+ uint8_t _name##_dcache_padding[TUH_EPBUF_DCACHE_SIZE(sizeof(_type))]; \
+ }
+
+
/*------------------------------------------------------------------*/
/* CONSTANTS
*------------------------------------------------------------------*/
typedef enum {
TUSB_ROLE_INVALID = 0,
- TUSB_ROLE_DEVICE,
- TUSB_ROLE_HOST,
+ TUSB_ROLE_DEVICE = 0x1,
+ TUSB_ROLE_HOST = 0x2,
} tusb_role_t;
/// defined base on EHCI specs value for Endpoint Speed
@@ -56,10 +93,10 @@ typedef enum {
/// defined base on USB Specs Endpoint's bmAttributes
typedef enum {
- TUSB_XFER_CONTROL = 0 ,
- TUSB_XFER_ISOCHRONOUS ,
- TUSB_XFER_BULK ,
- TUSB_XFER_INTERRUPT
+ TUSB_XFER_CONTROL = 0,
+ TUSB_XFER_ISOCHRONOUS = 1,
+ TUSB_XFER_BULK = 2,
+ TUSB_XFER_INTERRUPT = 3
} tusb_xfer_type_t;
typedef enum {
@@ -224,10 +261,10 @@ enum {
// USB 2.0 Spec Table 9-7: Test Mode Selectors
typedef enum {
TUSB_FEATURE_TEST_J = 1,
- TUSB_FEATURE_TEST_K,
- TUSB_FEATURE_TEST_SE0_NAK,
- TUSB_FEATURE_TEST_PACKET,
- TUSB_FEATURE_TEST_FORCE_ENABLE,
+ TUSB_FEATURE_TEST_K = 2,
+ TUSB_FEATURE_TEST_SE0_NAK = 3,
+ TUSB_FEATURE_TEST_PACKET = 4,
+ TUSB_FEATURE_TEST_FORCE_ENABLE = 5,
} tusb_feature_test_mode_t;
//--------------------------------------------------------------------+
@@ -264,7 +301,7 @@ typedef enum {
} microsoft_os_20_type_t;
enum {
- CONTROL_STAGE_IDLE,
+ CONTROL_STAGE_IDLE = 0,
CONTROL_STAGE_SETUP,
CONTROL_STAGE_DATA,
CONTROL_STAGE_ACK
diff --git a/src/common/tusb_verify.h b/src/common/tusb_verify.h
index 3e0f1f106..6d02d3572 100644
--- a/src/common/tusb_verify.h
+++ b/src/common/tusb_verify.h
@@ -83,7 +83,7 @@
if ( (*ARM_CM_DHCSR) & 1UL ) __asm("BKPT #0\n"); /* Only halt mcu if debugger is attached */ \
} while(0)
-#elif defined(__riscv) && !TUP_MCU_ESPRESSIF
+#elif defined(__riscv) && !TUSB_MCU_VENDOR_ESPRESSIF
#define TU_BREAKPOINT() do { __asm("ebreak\n"); } while(0)
#elif defined(_mips)
diff --git a/src/device/dcd.h b/src/device/dcd.h
index d01f82e01..789552d47 100644
--- a/src/device/dcd.h
+++ b/src/device/dcd.h
@@ -93,15 +93,15 @@ typedef struct TU_ATTR_ALIGNED(4) {
// clean/flush data cache: write cache -> memory.
// Required before an DMA TX transfer to make sure data is in memory
-void dcd_dcache_clean(void const* addr, uint32_t data_size) TU_ATTR_WEAK;
+bool dcd_dcache_clean(const void* addr, uint32_t data_size);
// invalidate data cache: mark cache as invalid, next read will read from memory
// Required BOTH before and after an DMA RX transfer
-void dcd_dcache_invalidate(void const* addr, uint32_t data_size) TU_ATTR_WEAK;
+bool dcd_dcache_invalidate(const void* addr, uint32_t data_size);
// clean and invalidate data cache
// Required before an DMA transfer where memory is both read/write by DMA
-void dcd_dcache_clean_invalidate(void const* addr, uint32_t data_size) TU_ATTR_WEAK;
+bool dcd_dcache_clean_invalidate(const void* addr, uint32_t data_size);
//--------------------------------------------------------------------+
// Controller API
diff --git a/src/device/usbd.c b/src/device/usbd.c
index a730b745b..2a6081673 100644
--- a/src/device/usbd.c
+++ b/src/device/usbd.c
@@ -46,9 +46,7 @@
// Weak stubs: invoked if no strong implementation is available
//--------------------------------------------------------------------+
TU_ATTR_WEAK void tud_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr) {
- (void) rhport;
- (void) eventid;
- (void) in_isr;
+ (void) rhport; (void) eventid; (void) in_isr;
}
TU_ATTR_WEAK void tud_sof_cb(uint32_t frame_count) {
@@ -82,9 +80,7 @@ TU_ATTR_WEAK void tud_resume_cb(void) {
}
TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const* request) {
- (void) rhport;
- (void) stage;
- (void) request;
+ (void) rhport; (void) stage; (void) request;
return false;
}
@@ -101,6 +97,21 @@ TU_ATTR_WEAK void dcd_disconnect(uint8_t rhport) {
(void) rhport;
}
+TU_ATTR_WEAK bool dcd_dcache_clean(const void* addr, uint32_t data_size) {
+ (void) addr; (void) data_size;
+ return true;
+}
+
+TU_ATTR_WEAK bool dcd_dcache_invalidate(const void* addr, uint32_t data_size) {
+ (void) addr; (void) data_size;
+ return true;
+}
+
+TU_ATTR_WEAK bool dcd_dcache_clean_invalidate(const void* addr, uint32_t data_size) {
+ (void) addr; (void) data_size;
+ return true;
+}
+
//--------------------------------------------------------------------+
// Device Data
//--------------------------------------------------------------------+
diff --git a/src/device/usbd_control.c b/src/device/usbd_control.c
index b1fd357aa..d964a75d0 100644
--- a/src/device/usbd_control.c
+++ b/src/device/usbd_control.c
@@ -35,7 +35,7 @@
//--------------------------------------------------------------------+
// Callback weak stubs (called if application does not provide)
//--------------------------------------------------------------------+
-TU_ATTR_WEAK void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const* request) {
+TU_ATTR_WEAK void dcd_edpt0_status_complete(uint8_t rhport, const tusb_control_request_t* request) {
(void) rhport;
(void) request;
}
@@ -61,54 +61,53 @@ typedef struct {
usbd_control_xfer_cb_t complete_cb;
} usbd_control_xfer_t;
-tu_static usbd_control_xfer_t _ctrl_xfer;
+static usbd_control_xfer_t _ctrl_xfer;
-CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN
-tu_static uint8_t _usbd_ctrl_buf[CFG_TUD_ENDPOINT0_SIZE];
+CFG_TUD_MEM_SECTION static struct {
+ TUD_EPBUF_DEF(buf, CFG_TUD_ENDPOINT0_SIZE);
+} _ctrl_epbuf;
//--------------------------------------------------------------------+
// Application API
//--------------------------------------------------------------------+
// Queue ZLP status transaction
-static inline bool _status_stage_xact(uint8_t rhport, tusb_control_request_t const* request) {
+static inline bool status_stage_xact(uint8_t rhport, const tusb_control_request_t* request) {
// Opposite to endpoint in Data Phase
- uint8_t const ep_addr = request->bmRequestType_bit.direction ? EDPT_CTRL_OUT : EDPT_CTRL_IN;
+ const uint8_t ep_addr = request->bmRequestType_bit.direction ? EDPT_CTRL_OUT : EDPT_CTRL_IN;
return usbd_edpt_xfer(rhport, ep_addr, NULL, 0);
}
// Status phase
-bool tud_control_status(uint8_t rhport, tusb_control_request_t const* request) {
+bool tud_control_status(uint8_t rhport, const tusb_control_request_t* request) {
_ctrl_xfer.request = (*request);
_ctrl_xfer.buffer = NULL;
_ctrl_xfer.total_xferred = 0;
_ctrl_xfer.data_len = 0;
- return _status_stage_xact(rhport, request);
+ return status_stage_xact(rhport, request);
}
// Queue a transaction in Data Stage
// Each transaction has up to Endpoint0's max packet size.
// This function can also transfer an zero-length packet
-static bool _data_stage_xact(uint8_t rhport) {
- uint16_t const xact_len = tu_min16(_ctrl_xfer.data_len - _ctrl_xfer.total_xferred,
- CFG_TUD_ENDPOINT0_SIZE);
-
+static bool data_stage_xact(uint8_t rhport) {
+ const uint16_t xact_len = tu_min16(_ctrl_xfer.data_len - _ctrl_xfer.total_xferred, CFG_TUD_ENDPOINT0_SIZE);
uint8_t ep_addr = EDPT_CTRL_OUT;
if (_ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_IN) {
ep_addr = EDPT_CTRL_IN;
if (xact_len) {
- TU_VERIFY(0 == tu_memcpy_s(_usbd_ctrl_buf, CFG_TUD_ENDPOINT0_SIZE, _ctrl_xfer.buffer, xact_len));
+ TU_VERIFY(0 == tu_memcpy_s(_ctrl_epbuf.buf, CFG_TUD_ENDPOINT0_SIZE, _ctrl_xfer.buffer, xact_len));
}
}
- return usbd_edpt_xfer(rhport, ep_addr, xact_len ? _usbd_ctrl_buf : NULL, xact_len);
+ return usbd_edpt_xfer(rhport, ep_addr, xact_len ? _ctrl_epbuf.buf : NULL, xact_len);
}
// Transmit data to/from the control endpoint.
// If the request's wLength is zero, a status packet is sent instead.
-bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const* request, void* buffer, uint16_t len) {
+bool tud_control_xfer(uint8_t rhport, const tusb_control_request_t* request, void* buffer, uint16_t len) {
_ctrl_xfer.request = (*request);
_ctrl_xfer.buffer = (uint8_t*) buffer;
_ctrl_xfer.total_xferred = 0U;
@@ -118,14 +117,9 @@ bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const* request, voi
if (_ctrl_xfer.data_len > 0U) {
TU_ASSERT(buffer);
}
-
-// TU_LOG2(" Control total data length is %u bytes\r\n", _ctrl_xfer.data_len);
-
- // Data stage
- TU_ASSERT(_data_stage_xact(rhport));
+ TU_ASSERT(data_stage_xact(rhport));
} else {
- // Status stage
- TU_ASSERT(_status_stage_xact(rhport, request));
+ TU_ASSERT(status_stage_xact(rhport, request));
}
return true;
@@ -135,7 +129,7 @@ bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const* request, voi
// USBD API
//--------------------------------------------------------------------+
void usbd_control_reset(void);
-void usbd_control_set_request(tusb_control_request_t const* request);
+void usbd_control_set_request(const tusb_control_request_t* request);
void usbd_control_set_complete_callback(usbd_control_xfer_cb_t fp);
bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
@@ -149,7 +143,7 @@ void usbd_control_set_complete_callback(usbd_control_xfer_cb_t fp) {
}
// for dcd_set_address where DCD is responsible for status response
-void usbd_control_set_request(tusb_control_request_t const* request) {
+void usbd_control_set_request(const tusb_control_request_t* request) {
_ctrl_xfer.request = (*request);
_ctrl_xfer.buffer = NULL;
_ctrl_xfer.total_xferred = 0;
@@ -179,8 +173,8 @@ bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result,
if (_ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_OUT) {
TU_VERIFY(_ctrl_xfer.buffer);
- memcpy(_ctrl_xfer.buffer, _usbd_ctrl_buf, xferred_bytes);
- TU_LOG_MEM(CFG_TUD_LOG_LEVEL, _usbd_ctrl_buf, xferred_bytes, 2);
+ memcpy(_ctrl_xfer.buffer, _ctrl_epbuf.buf, xferred_bytes);
+ TU_LOG_MEM(CFG_TUD_LOG_LEVEL, _ctrl_xfer.buffer, xferred_bytes, 2);
}
_ctrl_xfer.total_xferred += (uint16_t) xferred_bytes;
@@ -204,8 +198,7 @@ bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result,
}
if (is_ok) {
- // Send status
- TU_ASSERT(_status_stage_xact(rhport, &_ctrl_xfer.request));
+ TU_ASSERT(status_stage_xact(rhport, &_ctrl_xfer.request));
} else {
// Stall both IN and OUT control endpoint
dcd_edpt_stall(rhport, EDPT_CTRL_OUT);
@@ -213,7 +206,7 @@ bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result,
}
} else {
// More data to transfer
- TU_ASSERT(_data_stage_xact(rhport));
+ TU_ASSERT(data_stage_xact(rhport));
}
return true;
diff --git a/src/host/hcd.h b/src/host/hcd.h
index 6518e6fd2..56b6fdb5d 100644
--- a/src/host/hcd.h
+++ b/src/host/hcd.h
@@ -103,15 +103,15 @@ typedef struct {
// clean/flush data cache: write cache -> memory.
// Required before an DMA TX transfer to make sure data is in memory
-bool hcd_dcache_clean(void const* addr, uint32_t data_size) TU_ATTR_WEAK;
+bool hcd_dcache_clean(void const* addr, uint32_t data_size);
// invalidate data cache: mark cache as invalid, next read will read from memory
// Required BOTH before and after an DMA RX transfer
-bool hcd_dcache_invalidate(void const* addr, uint32_t data_size) TU_ATTR_WEAK;
+bool hcd_dcache_invalidate(void const* addr, uint32_t data_size);
// clean and invalidate data cache
// Required before an DMA transfer where memory is both read/write by DMA
-bool hcd_dcache_clean_invalidate(void const* addr, uint32_t data_size) TU_ATTR_WEAK;
+bool hcd_dcache_clean_invalidate(void const* addr, uint32_t data_size);
//--------------------------------------------------------------------+
// Controller API
diff --git a/src/host/usbh.c b/src/host/usbh.c
index b5df29f50..a2994cde7 100644
--- a/src/host/usbh.c
+++ b/src/host/usbh.c
@@ -65,6 +65,21 @@ TU_ATTR_WEAK void tuh_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_is
(void) in_isr;
}
+TU_ATTR_WEAK bool hcd_dcache_clean(const void* addr, uint32_t data_size) {
+ (void) addr; (void) data_size;
+ return false;
+}
+
+TU_ATTR_WEAK bool hcd_dcache_invalidate(const void* addr, uint32_t data_size) {
+ (void) addr; (void) data_size;
+ return false;
+}
+
+TU_ATTR_WEAK bool hcd_dcache_clean_invalidate(const void* addr, uint32_t data_size) {
+ (void) addr; (void) data_size;
+ return false;
+}
+
//--------------------------------------------------------------------+
// USBH-HCD common data structure
//--------------------------------------------------------------------+
@@ -137,65 +152,65 @@ typedef struct {
#endif
static usbh_class_driver_t const usbh_class_drivers[] = {
- #if CFG_TUH_CDC
- {
- .name = DRIVER_NAME("CDC"),
- .init = cdch_init,
- .deinit = cdch_deinit,
- .open = cdch_open,
- .set_config = cdch_set_config,
- .xfer_cb = cdch_xfer_cb,
- .close = cdch_close
- },
- #endif
+ #if CFG_TUH_CDC
+ {
+ .name = DRIVER_NAME("CDC"),
+ .init = cdch_init,
+ .deinit = cdch_deinit,
+ .open = cdch_open,
+ .set_config = cdch_set_config,
+ .xfer_cb = cdch_xfer_cb,
+ .close = cdch_close
+ },
+ #endif
- #if CFG_TUH_MSC
- {
- .name = DRIVER_NAME("MSC"),
- .init = msch_init,
- .deinit = msch_deinit,
- .open = msch_open,
- .set_config = msch_set_config,
- .xfer_cb = msch_xfer_cb,
- .close = msch_close
- },
- #endif
+ #if CFG_TUH_MSC
+ {
+ .name = DRIVER_NAME("MSC"),
+ .init = msch_init,
+ .deinit = msch_deinit,
+ .open = msch_open,
+ .set_config = msch_set_config,
+ .xfer_cb = msch_xfer_cb,
+ .close = msch_close
+ },
+ #endif
- #if CFG_TUH_HID
- {
- .name = DRIVER_NAME("HID"),
- .init = hidh_init,
- .deinit = hidh_deinit,
- .open = hidh_open,
- .set_config = hidh_set_config,
- .xfer_cb = hidh_xfer_cb,
- .close = hidh_close
- },
- #endif
+ #if CFG_TUH_HID
+ {
+ .name = DRIVER_NAME("HID"),
+ .init = hidh_init,
+ .deinit = hidh_deinit,
+ .open = hidh_open,
+ .set_config = hidh_set_config,
+ .xfer_cb = hidh_xfer_cb,
+ .close = hidh_close
+ },
+ #endif
- #if CFG_TUH_HUB
- {
- .name = DRIVER_NAME("HUB"),
- .init = hub_init,
- .deinit = hub_deinit,
- .open = hub_open,
- .set_config = hub_set_config,
- .xfer_cb = hub_xfer_cb,
- .close = hub_close
- },
- #endif
+ #if CFG_TUH_HUB
+ {
+ .name = DRIVER_NAME("HUB"),
+ .init = hub_init,
+ .deinit = hub_deinit,
+ .open = hub_open,
+ .set_config = hub_set_config,
+ .xfer_cb = hub_xfer_cb,
+ .close = hub_close
+ },
+ #endif
- #if CFG_TUH_VENDOR
- {
- .name = DRIVER_NAME("VENDOR"),
- .init = cush_init,
- .deinit = cush_deinit,
- .open = cush_open,
- .set_config = cush_set_config,
- .xfer_cb = cush_isr,
- .close = cush_close
- }
- #endif
+ #if CFG_TUH_VENDOR
+ {
+ .name = DRIVER_NAME("VENDOR"),
+ .init = cush_init,
+ .deinit = cush_deinit,
+ .open = cush_open,
+ .set_config = cush_set_config,
+ .xfer_cb = cush_isr,
+ .close = cush_close
+ }
+ #endif
};
enum { BUILTIN_DRIVER_COUNT = TU_ARRAY_SIZE(usbh_class_drivers) };
@@ -249,14 +264,10 @@ static usbh_device_t _usbh_devices[TOTAL_DEVICES];
OSAL_QUEUE_DEF(usbh_int_set, _usbh_qdef, CFG_TUH_TASK_QUEUE_SZ, hcd_event_t);
static osal_queue_t _usbh_q;
-CFG_TUH_MEM_SECTION CFG_TUH_MEM_ALIGN
-static uint8_t _usbh_ctrl_buf[CFG_TUH_ENUMERATION_BUFSIZE];
-
// Control transfers: since most controllers do not support multiple control transfers
// on multiple devices concurrently and control transfers are not used much except for
// enumeration, we will only execute control transfers one at a time.
-CFG_TUH_MEM_SECTION struct {
- CFG_TUH_MEM_ALIGN tusb_control_request_t request;
+static struct {
uint8_t* buffer;
tuh_xfer_cb_t complete_cb;
uintptr_t user_data;
@@ -264,7 +275,14 @@ CFG_TUH_MEM_SECTION struct {
uint8_t daddr;
volatile uint8_t stage;
volatile uint16_t actual_len;
-}_ctrl_xfer;
+} _ctrl_xfer;
+
+typedef struct {
+ TUH_EPBUF_TYPE_DEF(tusb_control_request_t, request);
+ TUH_EPBUF_DEF(ctrl, CFG_TUH_ENUMERATION_BUFSIZE);
+} usbh_epbuf_t;
+
+CFG_TUH_MEM_SECTION static usbh_epbuf_t _usbh_epbuf;
//------------- Helper Function -------------//
@@ -278,15 +296,6 @@ static void process_removing_device(uint8_t rhport, uint8_t hub_addr, uint8_t hu
static bool usbh_edpt_control_open(uint8_t dev_addr, uint8_t max_packet_size);
static bool usbh_control_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
-#if CFG_TUSB_OS == OPT_OS_NONE
-// TODO rework time-related function later
-// weak and overridable
-TU_ATTR_WEAK void osal_task_delay(uint32_t msec) {
- const uint32_t start = hcd_frame_number(_usbh_controller);
- while ( ( hcd_frame_number(_usbh_controller) - start ) < msec ) {}
-}
-#endif
-
TU_ATTR_ALWAYS_INLINE static inline bool queue_event(hcd_event_t const * event, bool in_isr) {
TU_ASSERT(osal_queue_send(_usbh_q, event, in_isr));
tuh_event_hook_cb(event->rhport, event->event_id, in_isr);
@@ -447,9 +456,9 @@ bool tuh_deinit(uint8_t rhport) {
}
bool tuh_task_event_ready(void) {
- // Skip if stack is not initialized
- if ( !tuh_inited() ) return false;
-
+ if (!tuh_inited()) {
+ return false; // Skip if stack is not initialized
+ }
return !osal_queue_empty(_usbh_q);
}
@@ -484,20 +493,30 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) {
switch (event.event_id) {
case HCD_EVENT_DEVICE_ATTACH:
- // due to the shared _usbh_ctrl_buf, we must complete enumerating one device before enumerating another one.
+ // due to the shared control buffer, we must complete enumerating one device before enumerating another one.
// TODO better to have an separated queue for newly attached devices
if (_dev0.enumerating) {
- TU_LOG_USBH("[%u:] USBH Defer Attach until current enumeration complete\r\n", event.rhport);
+ // Some device can cause multiple duplicated attach events
+ // drop current enumerating and start over for a proper port reset
+ if (event.rhport == _dev0.rhport && event.connection.hub_addr == _dev0.hub_addr &&
+ event.connection.hub_port == _dev0.hub_port) {
+ // abort/cancel current enumeration and start new one
+ TU_LOG1("[%u:] USBH Device Attach (duplicated)\r\n", event.rhport);
+ tuh_edpt_abort_xfer(0, 0);
+ enum_new_device(&event);
+ } else {
+ TU_LOG_USBH("[%u:] USBH Defer Attach until current enumeration complete\r\n", event.rhport);
- bool is_empty = osal_queue_empty(_usbh_q);
- queue_event(&event, in_isr);
+ bool is_empty = osal_queue_empty(_usbh_q);
+ queue_event(&event, in_isr);
- if (is_empty) {
- // Exit if this is the only event in the queue, otherwise we may loop forever
- return;
+ if (is_empty) {
+ // Exit if this is the only event in the queue, otherwise we may loop forever
+ return;
+ }
}
} else {
- TU_LOG_USBH("[%u:] USBH DEVICE ATTACH\r\n", event.rhport);
+ TU_LOG1("[%u:] USBH Device Attach\r\n", event.rhport);
_dev0.enumerating = 1;
enum_new_device(&event);
}
@@ -603,12 +622,12 @@ bool tuh_control_xfer (tuh_xfer_t* xfer) {
TU_VERIFY(xfer->ep_addr == 0 && xfer->setup);
// Check if device is still connected (enumerating for dev0)
- uint8_t const daddr = xfer->daddr;
- if ( daddr == 0 ) {
- if (!_dev0.enumerating) return false;
+ const uint8_t daddr = xfer->daddr;
+ if (daddr == 0) {
+ TU_VERIFY(_dev0.enumerating);
} else {
- usbh_device_t const* dev = get_device(daddr);
- if (dev && dev->connected == 0) return false;
+ const usbh_device_t* dev = get_device(daddr);
+ TU_VERIFY(dev && dev->connected);
}
// pre-check to help reducing mutex lock
@@ -621,10 +640,10 @@ bool tuh_control_xfer (tuh_xfer_t* xfer) {
_ctrl_xfer.daddr = daddr;
_ctrl_xfer.actual_len = 0;
- _ctrl_xfer.request = (*xfer->setup);
_ctrl_xfer.buffer = xfer->buffer;
_ctrl_xfer.complete_cb = xfer->complete_cb;
_ctrl_xfer.user_data = xfer->user_data;
+ _usbh_epbuf.request = (*xfer->setup);
}
(void) osal_mutex_unlock(_usbh_mutex);
@@ -638,7 +657,7 @@ bool tuh_control_xfer (tuh_xfer_t* xfer) {
TU_LOG_BUF_USBH(xfer->setup, 8);
if (xfer->complete_cb) {
- TU_ASSERT( hcd_setup_send(rhport, daddr, (uint8_t const*) &_ctrl_xfer.request) );
+ TU_ASSERT( hcd_setup_send(rhport, daddr, (uint8_t const*) &_usbh_epbuf.request) );
}else {
// blocking if complete callback is not provided
// change callback to internal blocking, and result as user argument
@@ -648,7 +667,7 @@ bool tuh_control_xfer (tuh_xfer_t* xfer) {
_ctrl_xfer.user_data = (uintptr_t) &result;
_ctrl_xfer.complete_cb = _control_blocking_complete_cb;
- TU_ASSERT( hcd_setup_send(rhport, daddr, (uint8_t*) &_ctrl_xfer.request) );
+ TU_ASSERT( hcd_setup_send(rhport, daddr, (uint8_t*) &_usbh_epbuf.request) );
while (result == XFER_RESULT_INVALID) {
// Note: this can be called within an callback ie. part of tuh_task()
@@ -680,7 +699,7 @@ static void _control_xfer_complete(uint8_t daddr, xfer_result_t result) {
TU_LOG_USBH("\r\n");
// duplicate xfer since user can execute control transfer within callback
- tusb_control_request_t const request = _ctrl_xfer.request;
+ tusb_control_request_t const request = _usbh_epbuf.request;
tuh_xfer_t xfer_temp = {
.daddr = daddr,
.ep_addr = 0,
@@ -703,7 +722,7 @@ static bool usbh_control_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t
(void) ep_addr;
const uint8_t rhport = usbh_get_rhport(daddr);
- tusb_control_request_t const * request = &_ctrl_xfer.request;
+ tusb_control_request_t const * request = &_usbh_epbuf.request;
if (XFER_RESULT_SUCCESS != result) {
TU_LOG_USBH("[%u:%u] Control %s, xferred_bytes = %" PRIu32 "\r\n", rhport, daddr, result == XFER_RESULT_STALLED ? "STALLED" : "FAILED", xferred_bytes);
@@ -778,24 +797,26 @@ bool tuh_edpt_xfer(tuh_xfer_t* xfer) {
}
bool tuh_edpt_abort_xfer(uint8_t daddr, uint8_t ep_addr) {
- usbh_device_t* dev = get_device(daddr);
- TU_VERIFY(dev);
-
TU_LOG_USBH("[%u] Aborted transfer on EP %02X\r\n", daddr, ep_addr);
- uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+ const uint8_t epnum = tu_edpt_number(ep_addr);
+ const uint8_t dir = tu_edpt_dir(ep_addr);
+
+ if (epnum == 0) {
+ // Also include dev0 for aborting enumerating
+ const uint8_t rhport = usbh_get_rhport(daddr);
- if ( epnum == 0 ) {
// control transfer: only 1 control at a time, check if we are aborting the current one
TU_VERIFY(daddr == _ctrl_xfer.daddr && _ctrl_xfer.stage != CONTROL_STAGE_IDLE);
- TU_VERIFY(hcd_edpt_abort_xfer(dev->rhport, daddr, ep_addr));
- // reset control transfer state to idle
- _set_control_xfer_stage(CONTROL_STAGE_IDLE);
+ hcd_edpt_abort_xfer(rhport, daddr, ep_addr);
+ _set_control_xfer_stage(CONTROL_STAGE_IDLE); // reset control transfer state to idle
} else {
- // non-control skip if not busy
- TU_VERIFY(dev->ep_status[epnum][dir].busy);
- TU_VERIFY(hcd_edpt_abort_xfer(dev->rhport, daddr, ep_addr));
+ usbh_device_t* dev = get_device(daddr);
+ TU_VERIFY(dev);
+
+ TU_VERIFY(dev->ep_status[epnum][dir].busy); // non-control skip if not busy
+ hcd_edpt_abort_xfer(dev->rhport, daddr, ep_addr);
+
// mark as ready and release endpoint if transfer is aborted
dev->ep_status[epnum][dir].busy = false;
tu_edpt_release(&dev->ep_status[epnum][dir], _usbh_mutex);
@@ -814,7 +835,7 @@ uint8_t usbh_get_rhport(uint8_t dev_addr) {
}
uint8_t *usbh_get_enum_buf(void) {
- return _usbh_ctrl_buf;
+ return _usbh_epbuf.ctrl;
}
void usbh_int_set(bool enabled) {
@@ -1276,14 +1297,14 @@ static void process_removing_device(uint8_t rhport, uint8_t hub_addr, uint8_t hu
// Enumeration Process
// is a lengthy process with a series of control transfer to configure
// newly attached device.
-// NOTE: due to the shared _usbh_ctrl_buf, we must complete enumerating
+// NOTE: due to the shared control buffer, we must complete enumerating
// one device before enumerating another one.
//--------------------------------------------------------------------+
enum {
- ENUM_RESET_DELAY = 50, // USB specs: 10 to 50ms
- ENUM_CONTACT_DEBOUNCING_DELAY = 450, // when plug/unplug a device, physical connection can be bouncing and may
- // generate a series of attach/detach event. This delay wait for stable connection
+ ENUM_RESET_DELAY_MS = 50, // USB specs: 10 to 50ms
+ ENUM_DEBOUNCING_DELAY_MS = 450, // when plug/unplug a device, physical connection can be bouncing and may
+ // generate a series of attach/detach event. This delay wait for stable connection
};
enum {
@@ -1322,7 +1343,7 @@ static void process_enumeration(tuh_xfer_t* xfer) {
bool retry = _dev0.enumerating && (failed_count < ATTEMPT_COUNT_MAX);
if ( retry ) {
failed_count++;
- osal_task_delay(ATTEMPT_DELAY_MS); // delay a bit
+ tusb_time_delay_ms_api(ATTEMPT_DELAY_MS); // delay a bit
TU_LOG1("Enumeration attempt %u\r\n", failed_count);
retry = tuh_control_xfer(xfer);
}
@@ -1344,7 +1365,7 @@ static void process_enumeration(tuh_xfer_t* xfer) {
case ENUM_HUB_CLEAR_RESET_1: {
hub_port_status_response_t port_status;
- memcpy(&port_status, _usbh_ctrl_buf, sizeof(hub_port_status_response_t));
+ memcpy(&port_status, _usbh_epbuf.ctrl, sizeof(hub_port_status_response_t));
if (!port_status.status.connection) {
// device unplugged while delaying, nothing else to do
@@ -1364,14 +1385,14 @@ static void process_enumeration(tuh_xfer_t* xfer) {
}
case ENUM_HUB_GET_STATUS_2:
- osal_task_delay(ENUM_RESET_DELAY);
- TU_ASSERT(hub_port_get_status(_dev0.hub_addr, _dev0.hub_port, _usbh_ctrl_buf,
+ tusb_time_delay_ms_api(ENUM_RESET_DELAY_MS);
+ TU_ASSERT(hub_port_get_status(_dev0.hub_addr, _dev0.hub_port, _usbh_epbuf.ctrl,
process_enumeration, ENUM_HUB_CLEAR_RESET_2),);
break;
case ENUM_HUB_CLEAR_RESET_2: {
hub_port_status_response_t port_status;
- memcpy(&port_status, _usbh_ctrl_buf, sizeof(hub_port_status_response_t));
+ memcpy(&port_status, _usbh_epbuf.ctrl, sizeof(hub_port_status_response_t));
// Acknowledge Port Reset Change if Reset Successful
if (port_status.change.reset) {
@@ -1389,7 +1410,7 @@ static void process_enumeration(tuh_xfer_t* xfer) {
// Get first 8 bytes of device descriptor for Control Endpoint size
TU_LOG_USBH("Get 8 byte of Device Descriptor\r\n");
- TU_ASSERT(tuh_descriptor_get_device(addr0, _usbh_ctrl_buf, 8,
+ TU_ASSERT(tuh_descriptor_get_device(addr0, _usbh_epbuf.ctrl, 8,
process_enumeration, ENUM_SET_ADDR),);
break;
}
@@ -1402,7 +1423,7 @@ static void process_enumeration(tuh_xfer_t* xfer) {
if (_dev0.hub_addr == 0) {
// connected directly to roothub
hcd_port_reset( _dev0.rhport );
- osal_task_delay(RESET_DELAY); // TODO may not work for no-OS on MCU that require reset_end() since
+ tusb_time_delay_ms_api(RESET_DELAY); // TODO may not work for no-OS on MCU that require reset_end() since
// sof of controller may not running while resetting
hcd_port_reset_end(_dev0.rhport);
// TODO: fall through to SET ADDRESS, refactor later
@@ -1424,9 +1445,9 @@ static void process_enumeration(tuh_xfer_t* xfer) {
case ENUM_GET_DEVICE_DESC: {
// Allow 2ms for address recovery time, Ref USB Spec 9.2.6.3
- osal_task_delay(2);
+ tusb_time_delay_ms_api(2);
- uint8_t const new_addr = (uint8_t) tu_le16toh(xfer->setup->wValue);
+ const uint8_t new_addr = (uint8_t) tu_le16toh(xfer->setup->wValue);
usbh_device_t* new_dev = get_device(new_addr);
TU_ASSERT(new_dev,);
@@ -1440,13 +1461,13 @@ static void process_enumeration(tuh_xfer_t* xfer) {
// Get full device descriptor
TU_LOG_USBH("Get Device Descriptor\r\n");
- TU_ASSERT(tuh_descriptor_get_device(new_addr, _usbh_ctrl_buf, sizeof(tusb_desc_device_t),
+ TU_ASSERT(tuh_descriptor_get_device(new_addr, _usbh_epbuf.ctrl, sizeof(tusb_desc_device_t),
process_enumeration, ENUM_GET_9BYTE_CONFIG_DESC),);
break;
}
case ENUM_GET_9BYTE_CONFIG_DESC: {
- tusb_desc_device_t const* desc_device = (tusb_desc_device_t const*) _usbh_ctrl_buf;
+ tusb_desc_device_t const* desc_device = (tusb_desc_device_t const*) _usbh_epbuf.ctrl;
usbh_device_t* dev = get_device(daddr);
TU_ASSERT(dev,);
@@ -1456,18 +1477,16 @@ static void process_enumeration(tuh_xfer_t* xfer) {
dev->i_product = desc_device->iProduct;
dev->i_serial = desc_device->iSerialNumber;
- // if (tuh_attach_cb) tuh_attach_cb((tusb_desc_device_t*) _usbh_ctrl_buf);
-
// Get 9-byte for total length
uint8_t const config_idx = CONFIG_NUM - 1;
TU_LOG_USBH("Get Configuration[0] Descriptor (9 bytes)\r\n");
- TU_ASSERT(tuh_descriptor_get_configuration(daddr, config_idx, _usbh_ctrl_buf, 9,
+ TU_ASSERT(tuh_descriptor_get_configuration(daddr, config_idx, _usbh_epbuf.ctrl, 9,
process_enumeration, ENUM_GET_FULL_CONFIG_DESC),);
break;
}
case ENUM_GET_FULL_CONFIG_DESC: {
- uint8_t const* desc_config = _usbh_ctrl_buf;
+ uint8_t const* desc_config = _usbh_epbuf.ctrl;
// Use offsetof to avoid pointer to the odd/misaligned address
uint16_t const total_len = tu_le16toh(
@@ -1479,7 +1498,7 @@ static void process_enumeration(tuh_xfer_t* xfer) {
// Get full configuration descriptor
uint8_t const config_idx = CONFIG_NUM - 1;
TU_LOG_USBH("Get Configuration[0] Descriptor\r\n");
- TU_ASSERT(tuh_descriptor_get_configuration(daddr, config_idx, _usbh_ctrl_buf, total_len,
+ TU_ASSERT(tuh_descriptor_get_configuration(daddr, config_idx, _usbh_epbuf.ctrl, total_len,
process_enumeration, ENUM_SET_CONFIG),);
break;
}
@@ -1497,7 +1516,7 @@ static void process_enumeration(tuh_xfer_t* xfer) {
// Parse configuration & set up drivers
// driver_open() must not make any usb transfer
- TU_ASSERT(_parse_configuration_descriptor(daddr, (tusb_desc_configuration_t*) _usbh_ctrl_buf),);
+ TU_ASSERT(_parse_configuration_descriptor(daddr, (tusb_desc_configuration_t*) _usbh_epbuf.ctrl),);
// Start the Set Configuration process for interfaces (itf = TUSB_INDEX_INVALID_8)
// Since driver can perform control transfer within its set_config, this is done asynchronously.
@@ -1514,20 +1533,25 @@ static void process_enumeration(tuh_xfer_t* xfer) {
}
}
+
+
static bool enum_new_device(hcd_event_t* event) {
_dev0.rhport = event->rhport;
_dev0.hub_addr = event->connection.hub_addr;
_dev0.hub_port = event->connection.hub_port;
if (_dev0.hub_addr == 0) {
- // connected/disconnected directly with roothub
+ // connected directly to roothub
hcd_port_reset(_dev0.rhport);
- osal_task_delay(ENUM_RESET_DELAY); // TODO may not work for no-OS on MCU that require reset_end() since
- // sof of controller may not running while resetting
+
+ // Since we are in middle of rhport reset, frame number is not available yet.
+ // need to depend on tusb_time_millis_api()
+ tusb_time_delay_ms_api(ENUM_RESET_DELAY_MS);
+
hcd_port_reset_end(_dev0.rhport);
// wait until device connection is stable TODO non blocking
- osal_task_delay(ENUM_CONTACT_DEBOUNCING_DELAY);
+ tusb_time_delay_ms_api(ENUM_DEBOUNCING_DELAY_MS);
// device unplugged while delaying
if (!hcd_port_connect_status(_dev0.rhport)) {
@@ -1548,13 +1572,12 @@ static bool enum_new_device(hcd_event_t* event) {
}
#if CFG_TUH_HUB
else {
- // connected/disconnected via external hub
+ // connected via external hub
// wait until device connection is stable TODO non blocking
- osal_task_delay(ENUM_CONTACT_DEBOUNCING_DELAY);
+ tusb_time_delay_ms_api(ENUM_DEBOUNCING_DELAY_MS);
// ENUM_HUB_GET_STATUS
- //TU_ASSERT( hub_port_get_status(_dev0.hub_addr, _dev0.hub_port, _usbh_ctrl_buf, enum_hub_get_status0_complete, 0) );
- TU_ASSERT(hub_port_get_status(_dev0.hub_addr, _dev0.hub_port, _usbh_ctrl_buf,
+ TU_ASSERT(hub_port_get_status(_dev0.hub_addr, _dev0.hub_port, _usbh_epbuf.ctrl,
process_enumeration, ENUM_HUB_CLEAR_RESET_1));
}
#endif // hub
@@ -1582,7 +1605,7 @@ static uint8_t get_new_address(bool is_hub) {
}
static bool enum_request_set_addr(void) {
- tusb_desc_device_t const* desc_device = (tusb_desc_device_t const*) _usbh_ctrl_buf;
+ tusb_desc_device_t const* desc_device = (tusb_desc_device_t const*) _usbh_epbuf.ctrl;
// Get new address
uint8_t const new_addr = get_new_address(desc_device->bDeviceClass == TUSB_CLASS_HUB);
@@ -1631,6 +1654,13 @@ static bool _parse_configuration_descriptor(uint8_t dev_addr, tusb_desc_configur
// parse each interfaces
while( p_desc < desc_end ) {
+ if ( 0 == tu_desc_len(p_desc) ) {
+ // A zero length descriptor indicates that the device is off spec (e.g. wrong wTotalLength).
+ // Parsed interfaces should still be usable
+ TU_LOG_USBH("Encountered a zero-length descriptor after %u bytes\r\n", (uint32_t)p_desc - (uint32_t)desc_cfg);
+ break;
+ }
+
uint8_t assoc_itf_count = 1;
// Class will always starts with Interface Association (if any) and then Interface descriptor
diff --git a/src/host/usbh.h b/src/host/usbh.h
index 20fad284e..72c237573 100644
--- a/src/host/usbh.h
+++ b/src/host/usbh.h
@@ -96,8 +96,6 @@ typedef union {
// APPLICATION CALLBACK
//--------------------------------------------------------------------+
-//TU_ATTR_WEAK uint8_t tuh_attach_cb (tusb_desc_device_t const *desc_device);
-
// Invoked when a device is mounted (configured)
TU_ATTR_WEAK void tuh_mount_cb (uint8_t daddr);
diff --git a/src/portable/chipidea/ci_hs/dcd_ci_hs.c b/src/portable/chipidea/ci_hs/dcd_ci_hs.c
index 76b78cb57..6d6a74524 100644
--- a/src/portable/chipidea/ci_hs/dcd_ci_hs.c
+++ b/src/portable/chipidea/ci_hs/dcd_ci_hs.c
@@ -33,18 +33,29 @@
#if CFG_TUSB_MCU == OPT_MCU_MIMXRT1XXX
#include "ci_hs_imxrt.h"
-#else
-#if TU_CHECK_MCU(OPT_MCU_LPC18XX, OPT_MCU_LPC43XX)
+ bool dcd_dcache_clean(void const* addr, uint32_t data_size) {
+ return imxrt_dcache_clean(addr, data_size);
+ }
+
+ bool dcd_dcache_invalidate(void const* addr, uint32_t data_size) {
+ return imxrt_dcache_invalidate(addr, data_size);
+ }
+
+ bool dcd_dcache_clean_invalidate(void const* addr, uint32_t data_size) {
+ return imxrt_dcache_clean_invalidate(addr, data_size);
+ }
+
+#elif TU_CHECK_MCU(OPT_MCU_LPC18XX, OPT_MCU_LPC43XX)
#include "ci_hs_lpc18_43.h"
#elif TU_CHECK_MCU(OPT_MCU_MCXN9)
// MCX N9 only port 1 use this controller
#include "ci_hs_mcx.h"
+
#else
#error "Unsupported MCUs"
#endif
-#endif
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
diff --git a/src/portable/dialog/da146xx/dcd_da146xx.c b/src/portable/dialog/da146xx/dcd_da146xx.c
index 887f59588..56ecb7575 100644
--- a/src/portable/dialog/da146xx/dcd_da146xx.c
+++ b/src/portable/dialog/da146xx/dcd_da146xx.c
@@ -896,6 +896,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool is_in_isr(void)
return (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) != 0;
}
+void tusb_vbus_changed(bool present);
void tusb_vbus_changed(bool present)
{
if (present && !_dcd.vbus_present)
diff --git a/src/portable/microchip/samd/dcd_samd.c b/src/portable/microchip/samd/dcd_samd.c
index 0c96f6ac4..357aa1549 100644
--- a/src/portable/microchip/samd/dcd_samd.c
+++ b/src/portable/microchip/samd/dcd_samd.c
@@ -335,7 +335,7 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
//--------------------------------------------------------------------+
// Interrupt Handler
//--------------------------------------------------------------------+
-void maybe_transfer_complete(void) {
+static void maybe_transfer_complete(void) {
uint32_t epints = USB->DEVICE.EPINTSMRY.reg;
for (uint8_t epnum = 0; epnum < USB_EPT_NUM; epnum++) {
diff --git a/src/portable/microchip/samg/dcd_samg.c b/src/portable/microchip/samg/dcd_samg.c
index c88b31514..a5c768839 100644
--- a/src/portable/microchip/samg/dcd_samg.c
+++ b/src/portable/microchip/samg/dcd_samg.c
@@ -52,37 +52,31 @@ typedef struct
// Endpoint 0-5, each can only be either OUT or In
xfer_desc_t _dcd_xfer[EP_COUNT];
-void xfer_epsize_set(xfer_desc_t* xfer, uint16_t epsize)
-{
+TU_ATTR_ALWAYS_INLINE static inline void xfer_epsize_set(xfer_desc_t* xfer, uint16_t epsize) {
xfer->epsize = epsize;
}
-void xfer_begin(xfer_desc_t* xfer, uint8_t * buffer, uint16_t total_bytes)
-{
+TU_ATTR_ALWAYS_INLINE static inline void xfer_begin(xfer_desc_t* xfer, uint8_t * buffer, uint16_t total_bytes) {
xfer->buffer = buffer;
// xfer->ff = NULL; // TODO support dcd_edpt_xfer_fifo API
xfer->total_len = total_bytes;
xfer->actual_len = 0;
}
-void xfer_end(xfer_desc_t* xfer)
-{
+TU_ATTR_ALWAYS_INLINE static inline void xfer_end(xfer_desc_t* xfer) {
xfer->buffer = NULL;
// xfer->ff = NULL; // TODO support dcd_edpt_xfer_fifo API
xfer->total_len = 0;
xfer->actual_len = 0;
}
-uint16_t xfer_packet_len(xfer_desc_t* xfer)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint16_t xfer_packet_len(xfer_desc_t* xfer) {
// also cover zero-length packet
return tu_min16(xfer->total_len - xfer->actual_len, xfer->epsize);
}
-void xfer_packet_done(xfer_desc_t* xfer)
-{
+TU_ATTR_ALWAYS_INLINE static inline void xfer_packet_done(xfer_desc_t* xfer) {
uint16_t const xact_len = xfer_packet_len(xfer);
-
xfer->buffer += xact_len;
xfer->actual_len += xact_len;
}
@@ -90,19 +84,15 @@ void xfer_packet_done(xfer_desc_t* xfer)
//------------- Transaction helpers -------------//
// Write data to EP FIFO, return number of written bytes
-static void xact_ep_write(uint8_t epnum, uint8_t* buffer, uint16_t xact_len)
-{
- for(uint16_t i=0; i<xact_len; i++)
- {
+static void xact_ep_write(uint8_t epnum, uint8_t* buffer, uint16_t xact_len) {
+ for(uint16_t i=0; i<xact_len; i++) {
UDP->UDP_FDR[epnum] = (uint32_t) buffer[i];
}
}
// Read data from EP FIFO
-static void xact_ep_read(uint8_t epnum, uint8_t* buffer, uint16_t xact_len)
-{
- for(uint16_t i=0; i<xact_len; i++)
- {
+static void xact_ep_read(uint8_t epnum, uint8_t* buffer, uint16_t xact_len) {
+ for(uint16_t i=0; i<xact_len; i++) {
buffer[i] = (uint8_t) UDP->UDP_FDR[epnum];
}
}
@@ -112,24 +102,24 @@ static void xact_ep_read(uint8_t epnum, uint8_t* buffer, uint16_t xact_len)
#define CSR_NO_EFFECT_1_ALL (UDP_CSR_RX_DATA_BK0 | UDP_CSR_RX_DATA_BK1 | UDP_CSR_STALLSENT | UDP_CSR_RXSETUP | UDP_CSR_TXCOMP)
// Per Specs: CSR need synchronization each write
-static inline void csr_write(uint8_t epnum, uint32_t value)
-{
+TU_ATTR_ALWAYS_INLINE static inline void csr_write(uint8_t epnum, uint32_t value) {
uint32_t const csr = value;
UDP->UDP_CSR[epnum] = csr;
volatile uint32_t nop_count;
- for (nop_count = 0; nop_count < 20; nop_count ++) __NOP();
+ for (nop_count = 0; nop_count < 20; nop_count ++) {
+ __NOP();
+ }
}
// Per Specs: CSR need synchronization each write
-static inline void csr_set(uint8_t epnum, uint32_t mask)
+TU_ATTR_ALWAYS_INLINE static inline void csr_set(uint8_t epnum, uint32_t mask)
{
csr_write(epnum, UDP->UDP_CSR[epnum] | CSR_NO_EFFECT_1_ALL | mask);
}
// Per Specs: CSR need synchronization each write
-static inline void csr_clear(uint8_t epnum, uint32_t mask)
-{
+TU_ATTR_ALWAYS_INLINE static inline void csr_clear(uint8_t epnum, uint32_t mask) {
csr_write(epnum, (UDP->UDP_CSR[epnum] | CSR_NO_EFFECT_1_ALL) & ~mask);
}
diff --git a/src/portable/nordic/nrf5x/dcd_nrf5x.c b/src/portable/nordic/nrf5x/dcd_nrf5x.c
index 3f53ee26e..5bfedae17 100644
--- a/src/portable/nordic/nrf5x/dcd_nrf5x.c
+++ b/src/portable/nordic/nrf5x/dcd_nrf5x.c
@@ -530,7 +530,7 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
/*------------------------------------------------------------------*/
/* Interrupt Handler
*------------------------------------------------------------------*/
-void bus_reset(void) {
+static void bus_reset(void) {
// 6.35.6 USB controller automatically disabled all endpoints (except control)
NRF_USBD->EPOUTEN = 1UL;
NRF_USBD->EPINEN = 1UL;
@@ -901,6 +901,7 @@ static void hfclk_disable(void) {
// Therefore this function must be called to handle USB power event by
// - nrfx_power_usbevt_init() : if Softdevice is not used or enabled
// - SoftDevice SOC event : if SD is used and enabled
+void tusb_hal_nrf_power_event(uint32_t event);
void tusb_hal_nrf_power_event(uint32_t event) {
// Value is chosen to be as same as NRFX_POWER_USB_EVT_* in nrfx_power.h
enum {
diff --git a/src/portable/nxp/khci/hcd_khci.c b/src/portable/nxp/khci/hcd_khci.c
index f5ca73c18..056dbf40b 100644
--- a/src/portable/nxp/khci/hcd_khci.c
+++ b/src/portable/nxp/khci/hcd_khci.c
@@ -140,7 +140,7 @@ typedef struct
CFG_TUH_MEM_SECTION TU_ATTR_ALIGNED(512) static hcd_data_t _hcd;
//CFG_TUH_MEM_SECTION TU_ATTR_ALIGNED(4) static uint8_t _rx_buf[1024];
-int find_pipe(uint8_t dev_addr, uint8_t ep_addr)
+static int find_pipe(uint8_t dev_addr, uint8_t ep_addr)
{
/* Find the target pipe */
int num;
diff --git a/src/portable/nxp/lpc17_40/hcd_lpc17_40.c b/src/portable/nxp/lpc17_40/hcd_lpc17_40.c
index 372dcf51f..090d1ba69 100644
--- a/src/portable/nxp/lpc17_40/hcd_lpc17_40.c
+++ b/src/portable/nxp/lpc17_40/hcd_lpc17_40.c
@@ -30,6 +30,7 @@
(CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX)
#include "chip.h"
+#include "host/hcd.h"
void hcd_int_enable(uint8_t rhport)
{
diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c
index 2e177d5cb..af08b549d 100644
--- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c
+++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c
@@ -46,7 +46,6 @@
/*------------------------------------------------------------------*/
/* Low level controller
*------------------------------------------------------------------*/
-
// Init these in dcd_init
static uint8_t* next_buffer_ptr;
@@ -66,58 +65,31 @@ TU_ATTR_ALWAYS_INLINE static inline struct hw_endpoint* hw_endpoint_get_by_addr(
return hw_endpoint_get_by_num(num, dir);
}
-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;
+// Allocate from the USB buffer space (max 3840 bytes)
+static void hw_endpoint_alloc(struct hw_endpoint* ep, size_t size) {
+ // round up size to multiple of 64
+ size = tu_round_up(ep->wMaxPacketSize, 64);
// double buffered Bulk endpoint
- if (transfer_type == TUSB_XFER_BULK) {
+ if (ep->transfer_type == TUSB_XFER_BULK) {
size *= 2u;
}
+ // assign buffer
ep->hw_data_buf = next_buffer_ptr;
next_buffer_ptr += size;
- 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;
-
- *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));
-
- // 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];
- }
+ hard_assert(next_buffer_ptr < usb_dpram->epx_data + sizeof(usb_dpram->epx_data));
+ pico_info(" Allocated %d bytes (0x%p)\r\n", size, ep->hw_data_buf);
}
-static void hw_endpoint_close(uint8_t ep_addr) {
- struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr);
- _hw_endpoint_close(ep);
+// Enable endpoint
+TU_ATTR_ALWAYS_INLINE static inline void hw_endpoint_enable(struct hw_endpoint* ep) {
+ uint32_t const reg = EP_CTRL_ENABLE_BITS | ((uint) ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | hw_data_offset(ep->hw_data_buf);
+ *ep->endpoint_control = reg;
}
+// main processing for dcd_edpt_iso_activate
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);
@@ -156,9 +128,18 @@ static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t t
} else {
ep->endpoint_control = &usb_dpram->ep_ctrl[num - 1].out;
}
+ }
+}
- // alloc a buffer and fill in endpoint control register
- _hw_endpoint_alloc(ep, transfer_type);
+// Init, allocate buffer and enable endpoint
+static void hw_endpoint_open(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type) {
+ struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr);
+ hw_endpoint_init(ep_addr, wMaxPacketSize, transfer_type);
+ const uint8_t num = tu_edpt_number(ep_addr);
+ if (num != 0) {
+ // EP0 is already enabled
+ hw_endpoint_alloc(ep, ep->wMaxPacketSize);
+ hw_endpoint_enable(ep);
}
}
@@ -387,8 +368,8 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
// Init control endpoints
tu_memclr(hw_endpoints[0], 2 * sizeof(hw_endpoint_t));
- hw_endpoint_init(0x0, 64, TUSB_XFER_CONTROL);
- hw_endpoint_init(0x80, 64, TUSB_XFER_CONTROL);
+ hw_endpoint_open(0x0, 64, TUSB_XFER_CONTROL);
+ hw_endpoint_open(0x80, 64, TUSB_XFER_CONTROL);
// Init non-control endpoints
reset_non_control_endpoints();
@@ -493,9 +474,34 @@ void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const* req
}
}
-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);
+bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_edpt) {
+ (void) rhport;
+ const uint8_t xfer_type = desc_edpt->bmAttributes.xfer;
+ TU_VERIFY(xfer_type != TUSB_XFER_ISOCHRONOUS);
+ hw_endpoint_open(desc_edpt->bEndpointAddress, tu_edpt_packet_size(desc_edpt), xfer_type);
+ return true;
+}
+
+// New API: Allocate packet buffer used by ISO endpoints
+// Some MCU need manual packet buffer allocation, we allocate the largest size to avoid clustering
+bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
+ (void) rhport;
+ struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr);
+ hw_endpoint_init(ep_addr, largest_packet_size, TUSB_XFER_ISOCHRONOUS);
+ hw_endpoint_alloc(ep, largest_packet_size);
+ return true;
+}
+
+// New API: Configure and enable an ISO endpoint according to descriptor
+bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc) {
+ (void) rhport;
+ const uint8_t ep_addr = ep_desc->bEndpointAddress;
+ // Fill in endpoint control register with buffer offset
+ struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr);
+ TU_ASSERT(ep->hw_data_buf != NULL); // must be inited and buffer allocated
+ ep->wMaxPacketSize = ep_desc->wMaxPacketSize;
+
+ hw_endpoint_enable(ep);
return true;
}
@@ -540,12 +546,6 @@ 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 __tusb_irq_path_func(dcd_int_handler)(uint8_t rhport) {
(void) rhport;
dcd_rp2040_irq();
diff --git a/src/portable/renesas/rusb2/rusb2_common.c b/src/portable/renesas/rusb2/rusb2_common.c
index 850060777..72e65736b 100644
--- a/src/portable/renesas/rusb2/rusb2_common.c
+++ b/src/portable/renesas/rusb2/rusb2_common.c
@@ -45,6 +45,7 @@ rusb2_controller_t rusb2_controller[] = {
};
// Application API for setting IRQ number. May throw warnings for missing prototypes.
+void tusb_rusb2_set_irqnum(uint8_t rhport, int32_t irqnum);
void tusb_rusb2_set_irqnum(uint8_t rhport, int32_t irqnum) {
rusb2_controller[rhport].irqnum = irqnum;
}
diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
index d921429e2..ef58957bb 100644
--- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
+++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
@@ -40,6 +40,7 @@
* F102, F103 512 byte buffer; no internal D+ pull-up (maybe many more changes?)
* F302xB/C, F303xB/C, F373 512 byte buffer; no internal D+ pull-up
* F302x6/8, F302xD/E2, F303xD/E 1024 byte buffer; no internal D+ pull-up
+ * C0 2048 byte buffer; 32-bit bus; host mode
* G0 2048 byte buffer; 32-bit bus; host mode
* G4 1024 byte buffer
* H5 2048 byte buffer; 32-bit bus; host mode
diff --git a/src/portable/st/stm32_fsdev/fsdev_stm32.h b/src/portable/st/stm32_fsdev/fsdev_stm32.h
index 03ea4c67e..ccf31e035 100644
--- a/src/portable/st/stm32_fsdev/fsdev_stm32.h
+++ b/src/portable/st/stm32_fsdev/fsdev_stm32.h
@@ -104,6 +104,20 @@
#define USB_CNTR_LPMODE USB_CNTR_SUSPRDY
#define USB_CNTR_FSUSP USB_CNTR_SUSPEN
+#elif CFG_TUSB_MCU == OPT_MCU_STM32C0
+ #include "stm32c0xx.h"
+ #define FSDEV_PMA_SIZE (2048u)
+ #define USB USB_DRD_FS
+ #define USB_EP_CTR_RX USB_CHEP_VTRX
+ #define USB_EP_CTR_TX USB_CHEP_VTTX
+ #define USB_EPREG_MASK USB_CHEP_REG_MASK
+ #define USB_CNTR_FRES USB_CNTR_USBRST
+ #define USB_CNTR_RESUME USB_CNTR_L2RES
+ #define USB_ISTR_EP_ID USB_ISTR_IDN
+ #define USB_EPADDR_FIELD USB_CHEP_ADDR
+ #define USB_CNTR_LPMODE USB_CNTR_SUSPRDY
+ #define USB_CNTR_FSUSP USB_CNTR_SUSPEN
+
#elif CFG_TUSB_MCU == OPT_MCU_STM32H5
#include "stm32h5xx.h"
#define FSDEV_PMA_SIZE (2048u)
@@ -260,6 +274,8 @@ static const IRQn_Type fsdev_irq[] = {
#else
USB_UCPD1_2_IRQn,
#endif
+ #elif CFG_TUSB_MCU == OPT_MCU_STM32C0
+ USB_DRD_FS_IRQn,
#elif TU_CHECK_MCU(OPT_MCU_STM32G4, OPT_MCU_STM32L1)
USB_HP_IRQn,
USB_LP_IRQn,
diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c
index f30ec5ee3..bde48ef57 100644
--- a/src/portable/synopsys/dwc2/dcd_dwc2.c
+++ b/src/portable/synopsys/dwc2/dcd_dwc2.c
@@ -31,56 +31,25 @@
#if CFG_TUD_ENABLED && defined(TUP_USBIP_DWC2)
+#if !(CFG_TUD_DWC2_SLAVE_ENABLE || CFG_TUD_DWC2_DMA_ENABLE)
+#error DWC2 require either CFG_TUD_DWC2_SLAVE_ENABLE or CFG_TUD_DWC2_DMA_ENABLE to be enabled
+#endif
+
// Debug level for DWC2
#define DWC2_DEBUG 2
#include "device/dcd.h"
-#include "dwc2_type.h"
-
-// Following symbols must be defined by port header
-// - _dwc2_controller[]: array of controllers
-// - DWC2_EP_MAX: largest EP counts of all controllers
-// - dwc2_phy_init/dwc2_phy_update: phy init called before and after core reset
-// - dwc2_dcd_int_enable/dwc2_dcd_int_disable
-// - dwc2_remote_wakeup_delay
+#include "dwc2_common.h"
-#if defined(TUP_USBIP_DWC2_STM32)
- #include "dwc2_stm32.h"
-#elif defined(TUP_USBIP_DWC2_ESP32)
- #include "dwc2_esp32.h"
-#elif TU_CHECK_MCU(OPT_MCU_GD32VF103)
- #include "dwc2_gd32.h"
-#elif TU_CHECK_MCU(OPT_MCU_BCM2711, OPT_MCU_BCM2835, OPT_MCU_BCM2837)
- #include "dwc2_bcm.h"
-#elif TU_CHECK_MCU(OPT_MCU_EFM32GG)
- #include "dwc2_efm32.h"
-#elif TU_CHECK_MCU(OPT_MCU_XMC4000)
- #include "dwc2_xmc.h"
+#if TU_CHECK_MCU(OPT_MCU_GD32VF103)
+ #define DWC2_EP_COUNT(_dwc2) DWC2_EP_MAX
#else
- #error "Unsupported MCUs"
+ #define DWC2_EP_COUNT(_dwc2) ((_dwc2)->ghwcfg2_bm.num_dev_ep)
#endif
-enum {
- DWC2_CONTROLLER_COUNT = TU_ARRAY_SIZE(_dwc2_controller)
-};
-
-// DWC2 registers
-//#define DWC2_REG(_port) ((dwc2_regs_t*) _dwc2_controller[_port].reg_base)
-
-TU_ATTR_ALWAYS_INLINE static inline dwc2_regs_t* DWC2_REG(uint8_t rhport) {
- if (rhport >= DWC2_CONTROLLER_COUNT) {
- // user mis-configured, ignore and use first controller
- rhport = 0;
- }
- return (dwc2_regs_t*) _dwc2_controller[rhport].reg_base;
-}
-
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM
//--------------------------------------------------------------------+
-
-static CFG_TUD_MEM_SECTION TU_ATTR_ALIGNED(4) uint32_t _setup_packet[2];
-
typedef struct {
uint8_t* buffer;
tu_fifo_t* ff;
@@ -92,34 +61,48 @@ typedef struct {
static xfer_ctl_t xfer_status[DWC2_EP_MAX][2];
#define XFER_CTL_BASE(_ep, _dir) (&xfer_status[_ep][_dir])
-// EP0 transfers are limited to 1 packet - larger sizes has to be split
-static uint16_t ep0_pending[2]; // Index determines direction as tusb_dir_t type
-static uint16_t _dfifo_top; // top free location in FIFO RAM
+typedef struct {
+ // EP0 transfers are limited to 1 packet - larger sizes has to be split
+ uint16_t ep0_pending[2]; // Index determines direction as tusb_dir_t type
+ uint16_t dfifo_top; // top free location in DFIFO in words
-// Number of IN endpoints active
-static uint8_t _allocated_ep_in_count;
+ // Number of IN endpoints active
+ uint8_t allocated_epin_count;
-// SOF enabling flag - required for SOF to not get disabled in ISR when SOF was enabled by
-static bool _sof_en;
+ // SOF enabling flag - required for SOF to not get disabled in ISR when SOF was enabled by
+ bool sof_en;
+} dcd_data_t;
+
+static dcd_data_t _dcd_data;
+
+CFG_TUD_MEM_SECTION static struct {
+ TUD_EPBUF_DEF(setup_packet, 8);
+} _dcd_usbbuf;
//--------------------------------------------------------------------
// DMA
//--------------------------------------------------------------------
+#if CFG_TUD_MEM_DCACHE_ENABLE
+bool dcd_dcache_clean(const void* addr, uint32_t data_size) {
+ TU_VERIFY(addr && data_size);
+ return dwc2_dcache_clean(addr, data_size);
+}
-TU_ATTR_ALWAYS_INLINE static inline bool dma_enabled(const dwc2_regs_t* dwc2) {
- #if !CFG_TUD_DWC2_DMA
- (void) dwc2;
- return false;
- #else
- // Internal DMA only
- return (dwc2->ghwcfg2_bm.arch == GHWCFG2_ARCH_INTERNAL_DMA);
- #endif
+bool dcd_dcache_invalidate(const void* addr, uint32_t data_size) {
+ TU_VERIFY(addr && data_size);
+ return dwc2_dcache_invalidate(addr, data_size);
}
-TU_ATTR_ALWAYS_INLINE static inline uint16_t dma_cal_epfifo_base(uint8_t rhport) {
- // Scatter/Gather DMA mode is not yet supported. Buffer DMA only need 1 words per endpoint direction
- const dwc2_controller_t* dwc2_controller = &_dwc2_controller[rhport];
- return dwc2_controller->ep_fifo_size/4 - 2*dwc2_controller->ep_count;
+bool dcd_dcache_clean_invalidate(const void* addr, uint32_t data_size) {
+ TU_VERIFY(addr && data_size);
+ return dwc2_dcache_clean_invalidate(addr, data_size);
+}
+#endif
+
+TU_ATTR_ALWAYS_INLINE static inline bool dma_device_enabled(const dwc2_regs_t* dwc2) {
+ (void) dwc2;
+ // Internal DMA only
+ return CFG_TUD_DWC2_DMA_ENABLE && dwc2->ghwcfg2_bm.arch == GHWCFG2_ARCH_INTERNAL_DMA;
}
static void dma_setup_prepare(uint8_t rhport) {
@@ -133,7 +116,7 @@ static void dma_setup_prepare(uint8_t rhport) {
// Receive only 1 packet
dwc2->epout[0].doeptsiz = (1 << DOEPTSIZ_STUPCNT_Pos) | (1 << DOEPTSIZ_PKTCNT_Pos) | (8 << DOEPTSIZ_XFRSIZ_Pos);
- dwc2->epout[0].doepdma = (uintptr_t)_setup_packet;
+ dwc2->epout[0].doepdma = (uintptr_t) _dcd_usbbuf.setup_packet;
dwc2->epout[0].doepctl |= DOEPCTL_EPENA | DOEPCTL_USBAEP;
}
@@ -141,18 +124,8 @@ static void dma_setup_prepare(uint8_t rhport) {
// Data FIFO
//--------------------------------------------------------------------+
-TU_ATTR_ALWAYS_INLINE static inline void dfifo_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 dfifo_flush_rx(dwc2_regs_t* dwc2) {
- // flush RX fifo and wait for it cleared
- dwc2->grstctl = GRSTCTL_RXFFLSH;
- while (dwc2->grstctl & GRSTCTL_RXFFLSH_Msk) {}
-}
-/* USB Data FIFO Layout
+/* Device Data FIFO scheme
The FIFO is split up into
- EPInfo: for storing DMA metadata, only required when use DMA. Maximum size is called
@@ -167,11 +140,9 @@ TU_ATTR_ALWAYS_INLINE static inline void dfifo_flush_rx(dwc2_regs_t* dwc2) {
possible since the free space is located between the RX and TX FIFOs.
---------------- ep_fifo_size
- | EPInfo |
- | for DMA |
+ | DxEPIDMAn |
|-------------|-- gdfifocfg.EPINFOBASE (max is ghwcfg3.dfifo_depth)
- | IN FIFO 0 |
- | control |
+ | IN FIFO 0 | control EP
|-------------|
| IN FIFO 1 |
|-------------|
@@ -190,153 +161,113 @@ TU_ATTR_ALWAYS_INLINE static inline void dfifo_flush_rx(dwc2_regs_t* dwc2) {
- All EP OUT shared a unique OUT FIFO which uses (for Slave or Buffer DMA, Scatt/Gather DMA use different formula):
- 13 for setup packets + control words (up to 3 setup packets).
- 1 for global NAK (not required/used here).
- - Largest-EPsize / 4 + 1. ( FS: 64 bytes, HS: 512 bytes). Recommended is "2 x (Largest-EPsize/4) + 1"
+ - Largest-EPsize/4 + 1. ( FS: 64 bytes, HS: 512 bytes). Recommended is "2 x (Largest-EPsize/4 + 1)"
- 2 for each used OUT endpoint
Therefore GRXFSIZ = 13 + 1 + 2 x (Largest-EPsize/4 + 1) + 2 x EPOUTnum
*/
-TU_ATTR_ALWAYS_INLINE static inline uint16_t calc_grxfsiz(uint16_t largest_ep_size, uint8_t ep_count) {
+TU_ATTR_ALWAYS_INLINE static inline uint16_t calc_device_grxfsiz(uint16_t largest_ep_size, uint8_t ep_count) {
return 13 + 1 + 2 * ((largest_ep_size / 4) + 1) + 2 * ep_count;
}
static bool dfifo_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t packet_size) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
const dwc2_controller_t* dwc2_controller = &_dwc2_controller[rhport];
- uint8_t const ep_count = dwc2_controller->ep_count;
- uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+ const uint8_t ep_count = dwc2_controller->ep_count;
+ const uint8_t epnum = tu_edpt_number(ep_addr);
+ const uint8_t dir = tu_edpt_dir(ep_addr);
TU_ASSERT(epnum < ep_count);
uint16_t fifo_size = tu_div_ceil(packet_size, 4);
if (dir == TUSB_DIR_OUT) {
// Calculate required size of RX FIFO
- uint16_t const new_sz = calc_grxfsiz(4 * fifo_size, ep_count);
+ const uint16_t new_sz = calc_device_grxfsiz(4 * fifo_size, ep_count);
// If size_rx needs to be extended check if there is enough free space
if (dwc2->grxfsiz < new_sz) {
- TU_ASSERT(new_sz <= _dfifo_top);
+ TU_ASSERT(new_sz <= _dcd_data.dfifo_top);
dwc2->grxfsiz = new_sz; // Enlarge RX FIFO
}
} else {
// Check IN endpoints concurrently active limit
- if(_dwc2_controller->ep_in_count) {
- TU_ASSERT(_allocated_ep_in_count < _dwc2_controller->ep_in_count);
- _allocated_ep_in_count++;
+ if(dwc2_controller->ep_in_count) {
+ TU_ASSERT(_dcd_data.allocated_epin_count < dwc2_controller->ep_in_count);
+ _dcd_data.allocated_epin_count++;
}
// If The TXFELVL is configured as half empty, the fifo must be twice the max_size.
- if ((dwc2->gahbcfg & GAHBCFG_TXFELVL) == 0) {
+ if ((dwc2->gahbcfg & GAHBCFG_TX_FIFO_EPMTY_LVL) == 0) {
fifo_size *= 2;
}
// Check if free space is available
- TU_ASSERT(_dfifo_top >= fifo_size + dwc2->grxfsiz);
- _dfifo_top -= fifo_size;
- TU_LOG(DWC2_DEBUG, " TX FIFO %u: allocated %u words at offset %u\r\n", epnum, fifo_size, _dfifo_top);
+ TU_ASSERT(_dcd_data.dfifo_top >= fifo_size + dwc2->grxfsiz);
+ _dcd_data.dfifo_top -= fifo_size;
+ // TU_LOG(DWC2_DEBUG, " TX FIFO %u: allocated %u words at offset %u\r\n", epnum, fifo_size, dfifo_top);
// Both TXFD and TXSA are in unit of 32-bit words.
if (epnum == 0) {
- dwc2->dieptxf0 = (fifo_size << DIEPTXF0_TX0FD_Pos) | _dfifo_top;
+ dwc2->dieptxf0 = (fifo_size << DIEPTXF0_TX0FD_Pos) | _dcd_data.dfifo_top;
} else {
// DIEPTXF starts at FIFO #1.
- dwc2->dieptxf[epnum - 1] = (fifo_size << DIEPTXF_INEPTXFD_Pos) | _dfifo_top;
+ dwc2->dieptxf[epnum - 1] = (fifo_size << DIEPTXF_INEPTXFD_Pos) | _dcd_data.dfifo_top;
}
}
return true;
}
-static void dfifo_init(uint8_t rhport) {
+static void dfifo_device_init(uint8_t rhport) {
const dwc2_controller_t* dwc2_controller = &_dwc2_controller[rhport];
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
- dwc2->grxfsiz = calc_grxfsiz(CFG_TUD_ENDPOINT0_SIZE, dwc2_controller->ep_count);
+ dwc2->grxfsiz = calc_device_grxfsiz(CFG_TUD_ENDPOINT0_SIZE, dwc2_controller->ep_count);
- if(dma_enabled(dwc2)) {
- // DMA use last DFIFO to store metadata
- _dfifo_top = dma_cal_epfifo_base(rhport);
- }else {
- _dfifo_top = dwc2_controller->ep_fifo_size / 4;
+ // Scatter/Gather DMA mode is not yet supported. Buffer DMA only need 1 words per endpoint direction
+ const bool is_dma = dma_device_enabled(dwc2);
+ _dcd_data.dfifo_top = dwc2_controller->ep_fifo_size/4;
+ if (is_dma) {
+ _dcd_data.dfifo_top -= 2 * dwc2_controller->ep_count;
}
+ dwc2->gdfifocfg = (_dcd_data.dfifo_top << GDFIFOCFG_EPINFOBASE_SHIFT) | _dcd_data.dfifo_top;
// Allocate FIFO for EP0 IN
dfifo_alloc(rhport, 0x80, CFG_TUD_ENDPOINT0_SIZE);
}
-// Read a single data packet from receive FIFO
-static void dfifo_read_packet(uint8_t rhport, uint8_t* dst, uint16_t len) {
- (void) rhport;
-
- dwc2_regs_t* dwc2 = DWC2_REG(rhport);
- volatile const uint32_t* rx_fifo = dwc2->fifo[0];
-
- // Reading full available 32 bit words from fifo
- uint16_t full_words = len >> 2;
- while (full_words--) {
- tu_unaligned_write32(dst, *rx_fifo);
- dst += 4;
- }
-
- // Read the remaining 1-3 bytes from fifo
- uint8_t const bytes_rem = len & 0x03;
- if (bytes_rem != 0) {
- uint32_t const tmp = *rx_fifo;
- dst[0] = tu_u32_byte0(tmp);
- if (bytes_rem > 1) dst[1] = tu_u32_byte1(tmp);
- if (bytes_rem > 2) dst[2] = tu_u32_byte2(tmp);
- }
-}
-
-// Write a single data packet to EPIN FIFO
-static void dfifo_write_packet(uint8_t rhport, uint8_t fifo_num, uint8_t const* src, uint16_t len) {
- (void) rhport;
-
- dwc2_regs_t* dwc2 = DWC2_REG(rhport);
- volatile uint32_t* tx_fifo = dwc2->fifo[fifo_num];
-
- // Pushing full available 32 bit words to fifo
- uint16_t full_words = len >> 2;
- while (full_words--) {
- *tx_fifo = tu_unaligned_read32(src);
- src += 4;
- }
-
- // Write the remaining 1-3 bytes into fifo
- uint8_t const bytes_rem = len & 0x03;
- if (bytes_rem) {
- uint32_t tmp_word = src[0];
- if (bytes_rem > 1) tmp_word |= (src[1] << 8);
- if (bytes_rem > 2) tmp_word |= (src[2] << 16);
-
- *tx_fifo = tmp_word;
- }
-}
//--------------------------------------------------------------------
// Endpoint
//--------------------------------------------------------------------
-
-static void edpt_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) {
+static void edpt_activate(uint8_t rhport, const tusb_desc_endpoint_t* 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);
+ const uint8_t epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress);
+ const uint8_t 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 epctl = (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);
+ // Endpoint control
+ union {
+ uint32_t value;
+ dwc2_depctl_t bm;
+ } depctl;
+ depctl.value = 0;
+
+ depctl.bm.mps = xfer->max_size;
+ depctl.bm.active = 1;
+ depctl.bm.type = p_endpoint_desc->bmAttributes.xfer;
+ if (p_endpoint_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS) {
+ depctl.bm.set_data0_iso_even = 1;
+ }
if (dir == TUSB_DIR_IN) {
- epctl |= (epnum << DIEPCTL_TXFNUM_Pos);
+ depctl.bm.tx_fifo_num = epnum;
}
- dwc2_dep_t* dep = &dwc2->ep[1 - dir][epnum];
- dep->ctl = epctl;
+ dwc2_dep_t* dep = &dwc2->ep[dir == TUSB_DIR_IN ? 0 : 1][epnum];
+ dep->ctl = depctl.value;
dwc2->daintmsk |= TU_BIT(epnum + DAINT_SHIFT(dir));
}
@@ -346,7 +277,7 @@ static void edpt_disable(uint8_t rhport, uint8_t ep_addr, bool stall) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
const uint8_t epnum = tu_edpt_number(ep_addr);
const uint8_t dir = tu_edpt_dir(ep_addr);
- dwc2_dep_t* dep = &dwc2->ep[1 - dir][epnum];
+ dwc2_dep_t* dep = &dwc2->ep[dir == TUSB_DIR_IN ? 0 : 1][epnum];
if (dir == TUSB_DIR_IN) {
// Only disable currently enabled non-control endpoint
@@ -390,295 +321,106 @@ static void edpt_disable(uint8_t rhport, uint8_t ep_addr, bool stall) {
}
}
-// Start of Bus Reset
-static void bus_reset(uint8_t rhport) {
- dwc2_regs_t* dwc2 = DWC2_REG(rhport);
- uint8_t const ep_count = _dwc2_controller[rhport].ep_count;
-
- tu_memclr(xfer_status, sizeof(xfer_status));
-
- _sof_en = false;
- _allocated_ep_in_count = 1;
-
- // 1. NAK for all OUT endpoints
- for (uint8_t n = 0; n < ep_count; n++) {
- dwc2->epout[n].doepctl |= DOEPCTL_SNAK;
- }
-
- // 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;
- }
- }
-
- dfifo_flush_tx(dwc2, 0x10); // all tx fifo
- dfifo_flush_rx(dwc2);
-
- // 3. Set up interrupt mask for EP0
- dwc2->daintmsk = TU_BIT(DAINTMSK_OEPM_Pos) | TU_BIT(DAINTMSK_IEPM_Pos);
- dwc2->doepmsk = DOEPMSK_STUPM | DOEPMSK_XFRCM;
- dwc2->diepmsk = DIEPMSK_TOM | DIEPMSK_XFRCM;
-
- // 4. Set up DFIFO
- dfifo_init(rhport);
-
- // 5. Reset device address
- dwc2->dcfg &= ~DCFG_DAD_Msk;
-
- // Fixed both control EP0 size to 64 bytes
- dwc2->epin[0].diepctl &= ~(0x03 << DIEPCTL_MPSIZ_Pos);
- dwc2->epout[0].doepctl &= ~(0x03 << DOEPCTL_MPSIZ_Pos);
-
- xfer_status[0][TUSB_DIR_OUT].max_size = 64;
- xfer_status[0][TUSB_DIR_IN].max_size = 64;
-
- if(dma_enabled(dwc2)) {
- dma_setup_prepare(rhport);
- } else {
- dwc2->epout[0].doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos);
- }
-
- dwc2->gintmsk |= GINTMSK_OEPINT | GINTMSK_IEPINT;
-}
-
-static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t const dir, uint16_t const num_packets,
- uint16_t total_bytes) {
- (void) rhport;
-
+static void edpt_schedule_packets(uint8_t rhport, const uint8_t epnum, const uint8_t dir) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
xfer_ctl_t* const xfer = XFER_CTL_BASE(epnum, dir);
+ dwc2_dep_t* dep = &dwc2->ep[dir == TUSB_DIR_IN ? 0 : 1][epnum];
+
+ uint16_t num_packets;
+ uint16_t total_bytes;
- // EP0 is limited to one packet each xfer
- // We use multiple transaction of xfer->max_size length to get a whole transfer done
+ // EP0 is limited to one packet per xfer
if (epnum == 0) {
- total_bytes = tu_min16(ep0_pending[dir], xfer->max_size);
- ep0_pending[dir] -= total_bytes;
+ total_bytes = tu_min16(_dcd_data.ep0_pending[dir], xfer->max_size);
+ _dcd_data.ep0_pending[dir] -= total_bytes;
+ num_packets = 1;
+ } else {
+ total_bytes = xfer->total_len;
+ num_packets = tu_div_ceil(total_bytes, xfer->max_size);
+ if (num_packets == 0) {
+ num_packets = 1; // zero length packet still count as 1
+ }
}
- // IN and OUT endpoint xfers are interrupt-driven, we just schedule them here.
- const uint8_t is_epout = 1 - dir;
- dwc2_dep_t* dep = &dwc2->ep[is_epout][epnum];
-
- if (dir == TUSB_DIR_IN) {
- // A full IN transfer (multiple packets, possibly) triggers XFRC.
- dep->dieptsiz = (num_packets << DIEPTSIZ_PKTCNT_Pos) |
- ((total_bytes << DIEPTSIZ_XFRSIZ_Pos) & DIEPTSIZ_XFRSIZ_Msk);
+ // transfer size: A full OUT transfer (multiple packets, possibly) triggers XFRC.
+ union {
+ uint32_t value;
+ dwc2_ep_tsize_t bm;
+ } deptsiz;
+ deptsiz.value = 0;
+ deptsiz.bm.xfer_size = total_bytes;
+ deptsiz.bm.packet_count = num_packets;
- if(dma_enabled(dwc2)) {
- dep->diepdma = (uintptr_t)xfer->buffer;
+ dep->tsiz = deptsiz.value;
- // For ISO endpoint set correct odd/even bit for next frame.
- if ((dep->diepctl & DIEPCTL_EPTYP) == DIEPCTL_EPTYP_0 && (XFER_CTL_BASE(epnum, dir))->interval == 1) {
- // Take odd/even bit from frame counter.
- uint32_t const odd_frame_now = (dwc2->dsts & (1u << DSTS_FNSOF_Pos));
- dep->diepctl |= (odd_frame_now ? DIEPCTL_SD0PID_SEVNFRM_Msk : DIEPCTL_SODDFRM_Msk);
- }
+ // control
+ union {
+ dwc2_depctl_t bm;
+ uint32_t value;
+ } depctl;
+ depctl.value = dep->ctl;
- dep->diepctl |= DIEPCTL_EPENA | DIEPCTL_CNAK;
+ depctl.bm.clear_nak = 1;
+ depctl.bm.enable = 1;
+ if (depctl.bm.type == DEPCTL_EPTYPE_ISOCHRONOUS && xfer->interval == 1) {
+ const uint32_t odd_now = (dwc2->dsts_bm.frame_number & 1u);
+ if (odd_now) {
+ depctl.bm.set_data0_iso_even = 1;
} else {
- dep->diepctl |= DIEPCTL_EPENA | DIEPCTL_CNAK;
-
- // For ISO endpoint set correct odd/even bit for next frame.
- if ((dep->diepctl & DIEPCTL_EPTYP) == DIEPCTL_EPTYP_0 && (XFER_CTL_BASE(epnum, dir))->interval == 1) {
- // Take odd/even bit from frame counter.
- uint32_t const odd_frame_now = (dwc2->dsts & (1u << DSTS_FNSOF_Pos));
- dep->diepctl |= (odd_frame_now ? DIEPCTL_SD0PID_SEVNFRM_Msk : DIEPCTL_SODDFRM_Msk);
- }
- // Enable fifo empty interrupt only if there are something to put in the fifo.
- if (total_bytes != 0) {
- dwc2->diepempmsk |= (1 << epnum);
- }
+ depctl.bm.set_data1_iso_odd = 1;
}
- } else {
- // A full OUT transfer (multiple packets, possibly) triggers XFRC.
- dep->doeptsiz &= ~(DOEPTSIZ_PKTCNT_Msk | DOEPTSIZ_XFRSIZ);
- dep->doeptsiz |= (num_packets << DOEPTSIZ_PKTCNT_Pos) |
- ((total_bytes << DOEPTSIZ_XFRSIZ_Pos) & DOEPTSIZ_XFRSIZ_Msk);
-
- if ((dep->doepctl & DOEPCTL_EPTYP) == DOEPCTL_EPTYP_0 &&
- XFER_CTL_BASE(epnum, dir)->interval == 1) {
- // Take odd/even bit from frame counter.
- uint32_t const odd_frame_now = (dwc2->dsts & (1u << DSTS_FNSOF_Pos));
- dep->doepctl |= (odd_frame_now ? DOEPCTL_SD0PID_SEVNFRM_Msk : DOEPCTL_SODDFRM_Msk);
- }
-
- if(dma_enabled(dwc2)) {
- dep->doepdma = (uintptr_t)xfer->buffer;
- }
-
- dep->doepctl |= DOEPCTL_EPENA | DOEPCTL_CNAK;
}
-}
-
-/*------------------------------------------------------------------*/
-/* Controller API
- *------------------------------------------------------------------*/
-static void reset_core(dwc2_regs_t* dwc2) {
- // reset core
- dwc2->grstctl |= GRSTCTL_CSRST;
-
- // wait for reset bit is cleared
- // TODO version 4.20a should wait for RESET DONE mask
- while (dwc2->grstctl & GRSTCTL_CSRST) {}
-
- // wait for AHB master IDLE
- while (!(dwc2->grstctl & GRSTCTL_AHBIDL)) {}
-
- // wait for device mode ?
-}
-
-static bool phy_hs_supported(dwc2_regs_t* dwc2) {
- (void) dwc2;
-
-#if !TUD_OPT_HIGH_SPEED
- return false;
-#else
- return dwc2->ghwcfg2_bm.hs_phy_type != GHWCFG2_HSPHY_NOT_SUPPORTED;
-#endif
-}
-
-static void phy_fs_init(dwc2_regs_t* dwc2) {
- TU_LOG(DWC2_DEBUG, "Fullspeed PHY init\r\n");
-
- // Select FS PHY
- dwc2->gusbcfg |= GUSBCFG_PHYSEL;
-
- // MCU specific PHY init before reset
- dwc2_phy_init(dwc2, GHWCFG2_HSPHY_NOT_SUPPORTED);
-
- // Reset core after selecting PHY
- reset_core(dwc2);
-
- // USB turnaround time is critical for certification where long cables and 5-Hubs are used.
- // So if you need the AHB to run at less than 30 MHz, and if USB turnaround time is not critical,
- // these bits can be programmed to a larger value. Default is 5
- dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_TRDT_Msk) | (5u << GUSBCFG_TRDT_Pos);
-
- // MCU specific PHY update post reset
- dwc2_phy_update(dwc2, GHWCFG2_HSPHY_NOT_SUPPORTED);
-
- // set max speed
- dwc2->dcfg = (dwc2->dcfg & ~DCFG_DSPD_Msk) | (DCFG_DSPD_FS << DCFG_DSPD_Pos);
-}
-
-static void phy_hs_init(dwc2_regs_t* dwc2) {
- uint32_t gusbcfg = dwc2->gusbcfg;
-
- // De-select FS PHY
- gusbcfg &= ~GUSBCFG_PHYSEL;
-
- if (dwc2->ghwcfg2_bm.hs_phy_type == GHWCFG2_HSPHY_ULPI) {
- TU_LOG(DWC2_DEBUG, "Highspeed ULPI PHY init\r\n");
-
- // Select ULPI
- gusbcfg |= GUSBCFG_ULPI_UTMI_SEL;
-
- // ULPI 8-bit interface, single data rate
- gusbcfg &= ~(GUSBCFG_PHYIF16 | GUSBCFG_DDRSEL);
-
- // default internal VBUS Indicator and Drive
- gusbcfg &= ~(GUSBCFG_ULPIEVBUSD | GUSBCFG_ULPIEVBUSI);
-
- // Disable FS/LS ULPI
- gusbcfg &= ~(GUSBCFG_ULPIFSLS | GUSBCFG_ULPICSM);
+ const bool is_dma = dma_device_enabled(dwc2);
+ if(is_dma) {
+ if (dir == TUSB_DIR_IN && total_bytes != 0) {
+ dcd_dcache_clean(xfer->buffer, total_bytes);
+ }
+ dep->diepdma = (uintptr_t) xfer->buffer;
+ dep->diepctl = depctl.value; // enable endpoint
} else {
- TU_LOG(DWC2_DEBUG, "Highspeed UTMI+ PHY init\r\n");
-
- // Select UTMI+ with 8-bit interface
- gusbcfg &= ~(GUSBCFG_ULPI_UTMI_SEL | GUSBCFG_PHYIF16);
+ dep->diepctl = depctl.value; // enable endpoint
- // Set 16-bit interface if supported
- if (dwc2->ghwcfg4_bm.phy_data_width) {
- gusbcfg |= GUSBCFG_PHYIF16;
+ // Enable tx fifo empty interrupt only if there is data. Note must after depctl enable
+ if (dir == TUSB_DIR_IN && total_bytes != 0) {
+ dwc2->diepempmsk |= (1 << epnum);
}
}
-
- // Apply config
- dwc2->gusbcfg = gusbcfg;
-
- // mcu specific phy init
- dwc2_phy_init(dwc2, dwc2->ghwcfg2_bm.hs_phy_type);
-
- // Reset core after selecting PHY
- reset_core(dwc2);
-
- // Set turn-around, must after core reset otherwise it will be clear
- // - 9 if using 8-bit PHY interface
- // - 5 if using 16-bit PHY interface
- gusbcfg &= ~GUSBCFG_TRDT_Msk;
- gusbcfg |= (dwc2->ghwcfg4_bm.phy_data_width ? 5u : 9u) << GUSBCFG_TRDT_Pos;
- dwc2->gusbcfg = gusbcfg;
-
- // MCU specific PHY update post reset
- dwc2_phy_update(dwc2, dwc2->ghwcfg2_bm.hs_phy_type);
-
- // Set max speed
- uint32_t dcfg = dwc2->dcfg;
- dcfg &= ~DCFG_DSPD_Msk;
- dcfg |= DCFG_DSPD_HS << DCFG_DSPD_Pos;
-
- // XCVRDLY: transceiver delay between xcvr_sel and txvalid during device chirp is required
- // when using with some PHYs such as USB334x (USB3341, USB3343, USB3346, USB3347)
- if (dwc2->ghwcfg2_bm.hs_phy_type == GHWCFG2_HSPHY_ULPI) {
- dcfg |= DCFG_XCVRDLY;
- }
-
- dwc2->dcfg = dcfg;
-}
-
-static bool check_dwc2(dwc2_regs_t* dwc2) {
-#if CFG_TUSB_DEBUG >= DWC2_DEBUG
- // print guid, gsnpsid, ghwcfg1, ghwcfg2, ghwcfg3, ghwcfg4
- // Run 'python dwc2_info.py' and check dwc2_info.md for bit-field value and comparison with other ports
- volatile uint32_t const* p = (volatile uint32_t const*) &dwc2->guid;
- TU_LOG1("guid, gsnpsid, ghwcfg1, ghwcfg2, ghwcfg3, ghwcfg4\r\n");
- for (size_t i = 0; i < 5; i++) {
- TU_LOG1("0x%08" PRIX32 ", ", p[i]);
- }
- TU_LOG1("0x%08" PRIX32 "\r\n", p[5]);
-#endif
-
- // For some reason: GD32VF103 snpsid and all hwcfg register are always zero (skip it)
- (void) dwc2;
-#if !TU_CHECK_MCU(OPT_MCU_GD32VF103)
- uint32_t const gsnpsid = dwc2->gsnpsid & GSNPSID_ID_MASK;
- TU_ASSERT(gsnpsid == DWC2_OTG_ID || gsnpsid == DWC2_FS_IOT_ID || gsnpsid == DWC2_HS_IOT_ID);
-#endif
-
- return true;
}
+//--------------------------------------------------------------------
+// Controller API
+//--------------------------------------------------------------------
bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
- (void) rhport;
(void) rh_init;
- // Programming model begins in the last section of the chapter on the USB
- // peripheral in each Reference Manual.
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
- // Check Synopsys ID register, failed if controller clock/power is not enabled
- TU_ASSERT(check_dwc2(dwc2));
- dcd_disconnect(rhport);
+ tu_memclr(&_dcd_data, sizeof(_dcd_data));
- if (phy_hs_supported(dwc2)) {
- phy_hs_init(dwc2); // Highspeed
+ // Core Initialization
+ const bool is_highspeed = dwc2_core_is_highspeed(dwc2, TUSB_ROLE_DEVICE);
+ const bool is_dma = dma_device_enabled(dwc2);
+ TU_ASSERT(dwc2_core_init(rhport, is_highspeed, is_dma));
+
+ //------------- 7.1 Device Initialization -------------//
+ // Set device max speed
+ uint32_t dcfg = dwc2->dcfg & ~DCFG_DSPD_Msk;
+ if (is_highspeed) {
+ dcfg |= DCFG_DSPD_HS << DCFG_DSPD_Pos;
+
+ // XCVRDLY: transceiver delay between xcvr_sel and txvalid during device chirp is required
+ // when using with some PHYs such as USB334x (USB3341, USB3343, USB3346, USB3347)
+ if (dwc2->ghwcfg2_bm.hs_phy_type == GHWCFG2_HSPHY_ULPI) {
+ dcfg |= DCFG_XCVRDLY;
+ }
} else {
- phy_fs_init(dwc2); // core does not support highspeed or hs phy is not present
+ dcfg |= DCFG_DSPD_FS << DCFG_DSPD_Pos;
}
- // Restart PHY clock
- dwc2->pcgctl &= ~(PCGCTL_STOPPCLK | PCGCTL_GATEHCLK | PCGCTL_PWRCLMP | PCGCTL_RSTPDWNMODULE);
+ dcfg |= DCFG_NZLSOHSK; // send STALL back and discard if host send non-zlp during control status
+ dwc2->dcfg = dcfg;
- /* Set HS/FS Timeout Calibration to 7 (max available value).
- * The number of PHY clocks that the application programs in
- * this field is added to the high/full speed interpacket timeout
- * duration in the core to account for any additional delays
- * introduced by the PHY. This can be required, because the delay
- * introduced by the PHY in generating the linestate condition
- * can vary from one PHY to another.
- */
- dwc2->gusbcfg |= (7ul << GUSBCFG_TOCAL_Pos);
+ dcd_disconnect(rhport);
// Force device mode
dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_FHMOD) | GUSBCFG_FDMOD;
@@ -686,48 +428,16 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
// Clear A override, force B Valid
dwc2->gotgctl = (dwc2->gotgctl & ~GOTGCTL_AVALOEN) | GOTGCTL_BVALOEN | GOTGCTL_BVALOVAL;
- // If USB host misbehaves during status portion of control xfer
- // (non zero-length packet), send STALL back and discard.
- dwc2->dcfg |= DCFG_NZLSOHSK;
-
- dfifo_flush_tx(dwc2, 0x10); // all tx fifo
- dfifo_flush_rx(dwc2);
-
- // Clear all interrupts
- uint32_t int_mask = dwc2->gintsts;
- dwc2->gintsts |= int_mask;
- int_mask = dwc2->gotgint;
- dwc2->gotgint |= int_mask;
-
- // Required as part of core initialization.
- dwc2->gintmsk = GINTMSK_OTGINT | GINTMSK_USBSUSPM | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM;
-
- // Configure TX FIFO empty level for interrupt. Default is complete empty
- dwc2->gahbcfg |= GAHBCFG_TXFELVL;
+ // Enable required interrupts
+ dwc2->gintmsk |= GINTMSK_OTGINT | GINTMSK_USBSUSPM | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM;
- if (dma_enabled(dwc2)) {
- const uint16_t epinfo_base = dma_cal_epfifo_base(rhport);
- dwc2->gdfifocfg = (epinfo_base << GDFIFOCFG_EPINFOBASE_SHIFT) | epinfo_base;
-
- // DMA seems to be only settable after a core reset
- dwc2->gahbcfg |= GAHBCFG_DMAEN | GAHBCFG_HBSTLEN_2;
- }else {
- dwc2->gintmsk |= GINTMSK_RXFLVLM;
- }
-
- // Enable global interrupt
- dwc2->gahbcfg |= GAHBCFG_GINT;
-
- // make sure we are in device mode
-// TU_ASSERT(!(dwc2->gintsts & GINTSTS_CMOD), );
-
-// TU_LOG_HEX(DWC2_DEBUG, dwc2->gotgctl);
-// TU_LOG_HEX(DWC2_DEBUG, dwc2->gusbcfg);
-// TU_LOG_HEX(DWC2_DEBUG, dwc2->dcfg);
-// TU_LOG_HEX(DWC2_DEBUG, dwc2->gahbcfg);
+ // TX FIFO empty level for interrupt is complete empty
+ uint32_t gahbcfg = dwc2->gahbcfg;
+ gahbcfg |= GAHBCFG_TX_FIFO_EPMTY_LVL;
+ gahbcfg |= GAHBCFG_GINT; // Enable global interrupt
+ dwc2->gahbcfg = gahbcfg;
dcd_connect(rhport);
-
return true;
}
@@ -804,7 +514,7 @@ void dcd_sof_enable(uint8_t rhport, bool en) {
(void) rhport;
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
- _sof_en = en;
+ _dcd_data.sof_en = en;
if (en) {
dwc2->gintsts = GINTSTS_SOF;
@@ -829,7 +539,7 @@ void dcd_edpt_close_all(uint8_t rhport) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
uint8_t const ep_count = _dwc2_controller[rhport].ep_count;
- _allocated_ep_in_count = 1;
+ _dcd_data.allocated_epin_count = 0;
// Disable non-control interrupt
dwc2->daintmsk = (1 << DAINTMSK_OEPM_Pos) | (1 << DAINTMSK_IEPM_Pos);
@@ -847,7 +557,7 @@ void dcd_edpt_close_all(uint8_t rhport) {
dfifo_flush_tx(dwc2, 0x10); // all tx fifo
dfifo_flush_rx(dwc2);
- dfifo_init(rhport); // re-init dfifo
+ dfifo_device_init(rhport); // re-init dfifo
}
bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
@@ -873,21 +583,12 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t to
// EP0 can only handle one packet
if (epnum == 0) {
- ep0_pending[dir] = total_bytes;
-
- // Schedule the first transaction for EP0 transfer
- edpt_schedule_packets(rhport, epnum, dir, 1, ep0_pending[dir]);
- } else {
- uint16_t num_packets = (total_bytes / xfer->max_size);
- uint16_t const short_packet_size = total_bytes % xfer->max_size;
-
- // Zero-size packet is special case.
- if ((short_packet_size > 0) || (total_bytes == 0)) num_packets++;
-
- // Schedule packets to be sent within interrupt
- edpt_schedule_packets(rhport, epnum, dir, num_packets, total_bytes);
+ _dcd_data.ep0_pending[dir] = total_bytes;
}
+ // Schedule packets to be sent within interrupt
+ edpt_schedule_packets(rhport, epnum, dir);
+
return true;
}
@@ -907,27 +608,17 @@ bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t* ff, uint16_t
xfer->ff = ff;
xfer->total_len = total_bytes;
- uint16_t num_packets = (total_bytes / xfer->max_size);
- uint16_t const short_packet_size = total_bytes % xfer->max_size;
-
- // Zero-size packet is special case.
- if (short_packet_size > 0 || (total_bytes == 0)) {
- num_packets++;
- }
-
// Schedule packets to be sent within interrupt
- edpt_schedule_packets(rhport, epnum, dir, num_packets, total_bytes);
+ // TODO xfer fifo may only available for slave mode
+ edpt_schedule_packets(rhport, epnum, dir);
return true;
}
-void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) {
- edpt_disable(rhport, ep_addr, false);
-}
-
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
edpt_disable(rhport, ep_addr, true);
- if((tu_edpt_number(ep_addr) == 0) && dma_enabled(DWC2_REG(rhport))) {
+ if((tu_edpt_number(ep_addr) == 0) && dma_device_enabled(dwc2)) {
dma_setup_prepare(rhport);
}
}
@@ -936,7 +627,7 @@ void dcd_edpt_clear_stall(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);
- dwc2_dep_t* dep = &dwc2->ep[1 - dir][epnum];
+ dwc2_dep_t* dep = &dwc2->ep[dir == TUSB_DIR_IN ? 0 : 1][epnum];
// Clear stall and reset data toggle
dep->ctl &= ~EPCTL_STALL;;
@@ -947,284 +638,373 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
// Interrupt Handler
//--------------------------------------------------------------------
+// 7.4.1 Initialization on USB Reset
+static void handle_bus_reset(uint8_t rhport) {
+ dwc2_regs_t *dwc2 = DWC2_REG(rhport);
+ const uint8_t ep_count = DWC2_EP_COUNT(dwc2);
+
+ tu_memclr(xfer_status, sizeof(xfer_status));
+
+ _dcd_data.sof_en = false;
+ _dcd_data.allocated_epin_count = 0;
+
+ // 1. NAK for all OUT endpoints
+ for (uint8_t n = 0; n < ep_count; n++) {
+ dwc2->epout[n].doepctl |= DOEPCTL_SNAK;
+ }
+
+ // 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;
+ }
+ }
+
+ // 2. Set up interrupt mask for EP0
+ dwc2->daintmsk = TU_BIT(DAINTMSK_OEPM_Pos) | TU_BIT(DAINTMSK_IEPM_Pos);
+ dwc2->doepmsk = DOEPMSK_STUPM | DOEPMSK_XFRCM;
+ dwc2->diepmsk = DIEPMSK_TOM | DIEPMSK_XFRCM;
+
+ // 4. Set up DFIFO
+ dfifo_flush_tx(dwc2, 0x10); // all tx fifo
+ dfifo_flush_rx(dwc2);
+ dfifo_device_init(rhport);
+
+ // 5. Reset device address
+ dwc2->dcfg_bm.address = 0;
+
+ // Fixed both control EP0 size to 64 bytes
+ dwc2->epin[0].ctl &= ~(0x03 << DIEPCTL_MPSIZ_Pos);
+ dwc2->epout[0].ctl &= ~(0x03 << DOEPCTL_MPSIZ_Pos);
+
+ xfer_status[0][TUSB_DIR_OUT].max_size = 64;
+ xfer_status[0][TUSB_DIR_IN].max_size = 64;
+
+ if(dma_device_enabled(dwc2)) {
+ dma_setup_prepare(rhport);
+ } else {
+ dwc2->epout[0].doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos);
+ }
+
+ dwc2->gintmsk |= GINTMSK_OEPINT | GINTMSK_IEPINT;
+}
+
+static void handle_enum_done(uint8_t rhport) {
+ dwc2_regs_t *dwc2 = DWC2_REG(rhport);
+ tusb_speed_t speed;
+ switch (dwc2->dsts_bm.enum_speed) {
+ case DCFG_SPEED_HIGH:
+ speed = TUSB_SPEED_HIGH;
+ break;
+
+ case DCFG_SPEED_LOW:
+ speed = TUSB_SPEED_LOW;
+ break;
+
+ case DCFG_SPEED_FULL_30_60MHZ:
+ case DCFG_SPEED_FULL_48MHZ:
+ default:
+ speed = TUSB_SPEED_FULL;
+ break;
+ }
+
+ // TODO must update GUSBCFG_TRDT according to link speed
+ dcd_event_bus_reset(rhport, speed, true);
+}
+
+#if 0
+TU_ATTR_ALWAYS_INLINE static inline void print_doepint(uint32_t doepint) {
+ const char* str[] = {
+ "XFRC", "DIS", "AHBERR", "SETUP_DONE",
+ "ORXED", "STATUS_RX", "SETUP_B2B", "RSV7",
+ "OPERR", "BNA", "RSV10", "ISODROP",
+ "BBLERR", "NAK", "NYET", "SETUP_RX"
+ };
+
+ for(uint32_t i=0; i<TU_ARRAY_SIZE(str); i++) {
+ if (doepint & TU_BIT(i)) {
+ TU_LOG1("%s ", str[i]);
+ }
+ }
+ TU_LOG1("\r\n");
+}
+#endif
+
+#if CFG_TUD_DWC2_SLAVE_ENABLE
// Process shared receive FIFO, this interrupt is only used in Slave mode
static void handle_rxflvl_irq(uint8_t rhport) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
- volatile uint32_t const* rx_fifo = dwc2->fifo[0];
+ const volatile uint32_t* rx_fifo = dwc2->fifo[0];
// Pop control word off FIFO
- uint32_t const grxstsp = dwc2->grxstsp;
- uint8_t const pktsts = (grxstsp & GRXSTSP_PKTSTS_Msk) >> GRXSTSP_PKTSTS_Pos;
- uint8_t const epnum = (grxstsp & GRXSTSP_EPNUM_Msk) >> GRXSTSP_EPNUM_Pos;
- uint16_t const bcnt = (grxstsp & GRXSTSP_BCNT_Msk) >> GRXSTSP_BCNT_Pos;
- dwc2_epout_t* epout = &dwc2->epout[epnum];
+ const dwc2_grxstsp_t grxstsp_bm = dwc2->grxstsp_bm;
+ const uint8_t epnum = grxstsp_bm.ep_ch_num;
- switch (pktsts) {
- // Global OUT NAK: do nothing
- case GRXSTS_PKTSTS_GLOBALOUTNAK:
+ dwc2_dep_t* epout = &dwc2->epout[epnum];
+
+ switch (grxstsp_bm.packet_status) {
+ case GRXSTS_PKTSTS_GLOBAL_OUT_NAK:
+ // Global OUT NAK: do nothing
break;
- case GRXSTS_PKTSTS_SETUPRX:
+ case GRXSTS_PKTSTS_SETUP_RX: {
// Setup packet received
- // We can receive up to three setup packets in succession, but only the last one is valid.
- _setup_packet[0] = (*rx_fifo);
- _setup_packet[1] = (*rx_fifo);
+ uint32_t* setup = (uint32_t*)(uintptr_t) _dcd_usbbuf.setup_packet;
+ // We can receive up to three setup packets in succession, but only the last one is valid.
+ setup[0] = (*rx_fifo);
+ setup[1] = (*rx_fifo);
break;
+ }
- case GRXSTS_PKTSTS_SETUPDONE:
+ case GRXSTS_PKTSTS_SETUP_DONE:
// Setup packet done:
// After popping this out, dwc2 asserts a DOEPINT_SETUP interrupt which is handled by handle_epout_irq()
epout->doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos);
break;
- case GRXSTS_PKTSTS_OUTRX: {
+ case GRXSTS_PKTSTS_RX_DATA: {
// Out packet received
+ const uint16_t byte_count = grxstsp_bm.byte_count;
xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
- // Read packet off RxFIFO
- if (xfer->ff) {
- // Ring buffer
- tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void*) (uintptr_t) rx_fifo, bcnt);
- } else {
- // Linear buffer
- dfifo_read_packet(rhport, xfer->buffer, bcnt);
-
- // Increment pointer to xfer data
- xfer->buffer += bcnt;
- }
+ if (byte_count) {
+ // Read packet off RxFIFO
+ if (xfer->ff) {
+ tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void*) (uintptr_t) rx_fifo, byte_count);
+ } else {
+ dfifo_read_packet(dwc2, xfer->buffer, byte_count);
+ xfer->buffer += byte_count;
+ }
- // Truncate transfer length in case of short packet
- if (bcnt < xfer->max_size) {
- xfer->total_len -= (epout->doeptsiz & DOEPTSIZ_XFRSIZ_Msk) >> DOEPTSIZ_XFRSIZ_Pos;
- if (epnum == 0) {
- xfer->total_len -= ep0_pending[TUSB_DIR_OUT];
- ep0_pending[TUSB_DIR_OUT] = 0;
+ // short packet, minus remaining bytes (xfer_size)
+ if (byte_count < xfer->max_size) {
+ xfer->total_len -= epout->tsiz_bm.xfer_size;
+ if (epnum == 0) {
+ xfer->total_len -= _dcd_data.ep0_pending[TUSB_DIR_OUT];
+ _dcd_data.ep0_pending[TUSB_DIR_OUT] = 0;
+ }
}
}
break;
}
- case GRXSTS_PKTSTS_OUTDONE:
- /* Out packet done
- After this entry is popped from the receive FIFO, dwc2 asserts a Transfer Completed interrupt on
- the specified OUT endpoint which will be handled by handle_epout_irq() */
+ case GRXSTS_PKTSTS_RX_COMPLETE:
+ // Out packet done
+ // After this entry is popped from the receive FIFO, dwc2 asserts a Transfer Completed interrupt on
+ // the specified OUT endpoint which will be handled by handle_epout_irq()
break;
- default:
- TU_BREAKPOINT();
- break;
+ default: break;
}
}
-static void handle_epout_irq(uint8_t rhport) {
- dwc2_regs_t* dwc2 = DWC2_REG(rhport);
- uint8_t const ep_count = _dwc2_controller[rhport].ep_count;
-
- // DAINT for a given EP clears when DOEPINTx is cleared.
- // OEPINT will be cleared when DAINT's out bits are cleared.
- for (uint8_t epnum = 0; epnum < ep_count; epnum++) {
- if (dwc2->daint & TU_BIT(DAINT_OEPINT_Pos + epnum)) {
- dwc2_epout_t* epout = &dwc2->epout[epnum];
- const uint32_t doepint = epout->doepint;
- TU_ASSERT((epout->doepint & DOEPINT_AHBERR) == 0, );
+static void handle_epout_slave(uint8_t rhport, uint8_t epnum, dwc2_doepint_t doepint_bm) {
+ if (doepint_bm.setup_phase_done) {
+ dcd_event_setup_received(rhport, _dcd_usbbuf.setup_packet, true);
+ return;
+ }
- // Setup and/or STPKTRX/STSPHSRX (from 3.00a) can be set along with XFRC, and also set independently.
- if (dwc2->gsnpsid >= DWC2_CORE_REV_3_00a) {
- if (doepint & DOEPINT_STSPHSRX) {
- // Status phase received for control write: In token received from Host
- epout->doepint = DOEPINT_STSPHSRX;
- }
+ // Normal OUT transfer complete
+ if (doepint_bm.xfer_complete) {
+ // only handle data skip if it is setup or status related
+ // Note: even though (xfer_complete + status_phase_rx) is for buffered DMA only, for STM32L47x (dwc2 v3.00a) they
+ // can is set when GRXSTS_PKTSTS_SETUP_RX is popped therefore they can bet set before/together with setup_phase_done
+ if (!doepint_bm.status_phase_rx && !doepint_bm.setup_packet_rx) {
+ xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
- if (doepint & DOEPINT_STPKTRX) {
- // New setup packet received, but wait for Setup done, since we can receive up to 3 setup consecutively
- epout->doepint = DOEPINT_STPKTRX;
- }
+ if ((epnum == 0) && _dcd_data.ep0_pending[TUSB_DIR_OUT]) {
+ // EP0 can only handle one packet, Schedule another packet to be received.
+ edpt_schedule_packets(rhport, epnum, TUSB_DIR_OUT);
+ } else {
+ dcd_event_xfer_complete(rhport, epnum, xfer->total_len, XFER_RESULT_SUCCESS, true);
}
+ }
+ }
+}
- if (doepint & DOEPINT_SETUP) {
- epout->doepint = DOEPINT_SETUP;
-
- if(dma_enabled(dwc2)) {
- dma_setup_prepare(rhport);
- }
+static void handle_epin_slave(uint8_t rhport, uint8_t epnum, dwc2_diepint_t diepint_bm) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ dwc2_dep_t* epin = &dwc2->epin[epnum];
+ xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_IN);
- dcd_event_setup_received(rhport, (uint8_t*) _setup_packet, true);
- }
+ if (diepint_bm.xfer_complete) {
+ if ((epnum == 0) && _dcd_data.ep0_pending[TUSB_DIR_IN]) {
+ // EP0 can only handle one packet. Schedule another packet to be transmitted.
+ edpt_schedule_packets(rhport, epnum, TUSB_DIR_IN);
+ } else {
+ dcd_event_xfer_complete(rhport, epnum | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true);
+ }
+ }
- // OUT XFER complete
- if (doepint & DOEPINT_XFRC) {
- epout->doepint = DOEPINT_XFRC;
+ // TX FIFO empty bit is read-only. It will only be cleared by hardware when written bytes is more than
+ // - 64 bytes or
+ // - Half/Empty of TX FIFO size (configured by GAHBCFG.TXFELVL)
+ if (diepint_bm.txfifo_empty && (dwc2->diepempmsk & (1 << epnum))) {
+ const uint16_t remain_packets = epin->tsiz_bm.packet_count;
- // only handle data skip if it is setup or status related
- // Normal OUT transfer complete
- if (!(doepint & (DOEPINT_SETUP | DOEPINT_STPKTRX | DOEPINT_STSPHSRX))) {
- xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
+ // Process every single packet (only whole packets can be written to fifo)
+ for (uint16_t i = 0; i < remain_packets; i++) {
+ const uint16_t remain_bytes = (uint16_t) epin->tsiz_bm.xfer_size;
+ const uint16_t xact_bytes = tu_min16(remain_bytes, xfer->max_size);
- if(dma_enabled(dwc2)) {
- if ((epnum == 0) && ep0_pending[TUSB_DIR_OUT]) {
- // EP0 can only handle one packet Schedule another packet to be received.
- edpt_schedule_packets(rhport, epnum, TUSB_DIR_OUT, 1, ep0_pending[TUSB_DIR_OUT]);
- } else {
- // Fix packet length
- uint16_t remain = (epout->doeptsiz & DOEPTSIZ_XFRSIZ_Msk) >> DOEPTSIZ_XFRSIZ_Pos;
- xfer->total_len -= remain;
- // this is ZLP, so prepare EP0 for next setup
- if(epnum == 0 && xfer->total_len == 0) {
- dma_setup_prepare(rhport);
- }
+ // Check if dtxfsts has enough space available
+ if (xact_bytes > ((epin->dtxfsts & DTXFSTS_INEPTFSAV_Msk) << 2)) {
+ break;
+ }
- dcd_event_xfer_complete(rhport, epnum, xfer->total_len, XFER_RESULT_SUCCESS, true);
- }
- } else {
- // EP0 can only handle one packet
- if ((epnum == 0) && ep0_pending[TUSB_DIR_OUT]) {
- // Schedule another packet to be received.
- edpt_schedule_packets(rhport, epnum, TUSB_DIR_OUT, 1, ep0_pending[TUSB_DIR_OUT]);
- } else {
- dcd_event_xfer_complete(rhport, epnum, xfer->total_len, XFER_RESULT_SUCCESS, true);
- }
- }
- }
+ // Push packet to Tx-FIFO
+ if (xfer->ff) {
+ volatile uint32_t* tx_fifo = dwc2->fifo[epnum];
+ tu_fifo_read_n_const_addr_full_words(xfer->ff, (void*)(uintptr_t)tx_fifo, xact_bytes);
+ } else {
+ dfifo_write_packet(dwc2, epnum, xfer->buffer, xact_bytes);
+ xfer->buffer += xact_bytes;
}
}
+
+ // Turn off TXFE if all bytes are written.
+ if (epin->tsiz_bm.xfer_size == 0) {
+ dwc2->diepempmsk &= ~(1 << epnum);
+ }
}
}
+#endif
-static void handle_epin_irq(uint8_t rhport) {
+#if CFG_TUD_DWC2_DMA_ENABLE
+static void handle_epout_dma(uint8_t rhport, uint8_t epnum, dwc2_doepint_t doepint_bm) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
- uint8_t const ep_count = _dwc2_controller[rhport].ep_count;
- dwc2_epin_t* epin = dwc2->epin;
- // DAINT for a given EP clears when DIEPINTx is cleared.
- // IEPINT will be cleared when DAINT's out bits are cleared.
- for (uint8_t n = 0; n < ep_count; n++) {
- if (dwc2->daint & TU_BIT(DAINT_IEPINT_Pos + n)) {
- // IN XFER complete (entire xfer).
- xfer_ctl_t* xfer = XFER_CTL_BASE(n, TUSB_DIR_IN);
+ if (doepint_bm.setup_phase_done) {
+ dma_setup_prepare(rhport);
+ dcd_dcache_invalidate(_dcd_usbbuf.setup_packet, 8);
+ dcd_event_setup_received(rhport, _dcd_usbbuf.setup_packet, true);
+ return;
+ }
- if (epin[n].diepint & DIEPINT_XFRC) {
- epin[n].diepint = DIEPINT_XFRC;
+ // OUT XFER complete
+ if (doepint_bm.xfer_complete) {
+ // only handle data skip if it is setup or status related
+ // Normal OUT transfer complete
+ if (!doepint_bm.status_phase_rx && !doepint_bm.setup_packet_rx) {
+ if ((epnum == 0) && _dcd_data.ep0_pending[TUSB_DIR_OUT]) {
+ // EP0 can only handle one packet Schedule another packet to be received.
+ edpt_schedule_packets(rhport, epnum, TUSB_DIR_OUT);
+ } else {
+ dwc2_dep_t* epout = &dwc2->epout[epnum];
+ xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
- // EP0 can only handle one packet
- if ((n == 0) && ep0_pending[TUSB_DIR_IN]) {
- // Schedule another packet to be transmitted.
- edpt_schedule_packets(rhport, n, TUSB_DIR_IN, 1, ep0_pending[TUSB_DIR_IN]);
- } else {
- if((n == 0) && dma_enabled(dwc2)) {
- dma_setup_prepare(rhport);
- }
- dcd_event_xfer_complete(rhport, n | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true);
+ // determine actual received bytes
+ const uint16_t remain = epout->tsiz_bm.xfer_size;
+ xfer->total_len -= remain;
+
+ // this is ZLP, so prepare EP0 for next setup
+ // TODO use status phase rx
+ if(epnum == 0 && xfer->total_len == 0) {
+ dma_setup_prepare(rhport);
}
- }
- // XFER FIFO empty
- if ((epin[n].diepint & DIEPINT_TXFE) && (dwc2->diepempmsk & (1 << n))) {
- // diepint's TXFE bit is read-only, software cannot clear it.
- // It will only be cleared by hardware when written bytes is more than
- // - 64 bytes or
- // - Half of TX FIFO size (configured by DIEPTXF)
+ dcd_dcache_invalidate(xfer->buffer, xfer->total_len);
+ dcd_event_xfer_complete(rhport, epnum, xfer->total_len, XFER_RESULT_SUCCESS, true);
+ }
+ }
+ }
+}
- uint16_t remaining_packets = (epin[n].dieptsiz & DIEPTSIZ_PKTCNT_Msk) >> DIEPTSIZ_PKTCNT_Pos;
+static void handle_epin_dma(uint8_t rhport, uint8_t epnum, dwc2_diepint_t diepint_bm) {
+ xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_IN);
- // Process every single packet (only whole packets can be written to fifo)
- for (uint16_t i = 0; i < remaining_packets; i++) {
- uint16_t const remaining_bytes = (epin[n].dieptsiz & DIEPTSIZ_XFRSIZ_Msk) >> DIEPTSIZ_XFRSIZ_Pos;
+ if (diepint_bm.xfer_complete) {
+ if ((epnum == 0) && _dcd_data.ep0_pending[TUSB_DIR_IN]) {
+ // EP0 can only handle one packet. Schedule another packet to be transmitted.
+ edpt_schedule_packets(rhport, epnum, TUSB_DIR_IN);
+ } else {
+ if(epnum == 0) {
+ dma_setup_prepare(rhport);
+ }
+ dcd_event_xfer_complete(rhport, epnum | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true);
+ }
+ }
+}
+#endif
- // Packet can not be larger than ep max size
- uint16_t const packet_size = tu_min16(remaining_bytes, xfer->max_size);
+static void handle_ep_irq(uint8_t rhport, uint8_t dir) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ const bool is_dma = dma_device_enabled(dwc2);
+ const uint8_t ep_count = DWC2_EP_COUNT(dwc2);
+ const uint8_t daint_offset = (dir == TUSB_DIR_IN) ? DAINT_IEPINT_Pos : DAINT_OEPINT_Pos;
+ dwc2_dep_t* ep_base = &dwc2->ep[dir == TUSB_DIR_IN ? 0 : 1][0];
- // It's only possible to write full packets into FIFO. Therefore DTXFSTS register of current
- // EP has to be checked if the buffer can take another WHOLE packet
- if (packet_size > ((epin[n].dtxfsts & DTXFSTS_INEPTFSAV_Msk) << 2)) break;
+ // DAINT for a given EP clears when DEPINTx is cleared.
+ // EPINT will be cleared when DAINT bits are cleared.
+ for (uint8_t epnum = 0; epnum < ep_count; epnum++) {
+ if (dwc2->daint & TU_BIT(daint_offset + epnum)) {
+ dwc2_dep_t* epout = &ep_base[epnum];
+ union {
+ uint32_t value;
+ dwc2_diepint_t diepint_bm;
+ dwc2_doepint_t doepint_bm;
+ } intr;
+ intr.value = epout->intr;
- // Push packet to Tx-FIFO
- if (xfer->ff) {
- volatile uint32_t* tx_fifo = dwc2->fifo[n];
- tu_fifo_read_n_const_addr_full_words(xfer->ff, (void*) (uintptr_t) tx_fifo, packet_size);
- } else {
- dfifo_write_packet(rhport, n, xfer->buffer, packet_size);
+ epout->intr = intr.value; // Clear interrupt
- // Increment pointer to xfer data
- xfer->buffer += packet_size;
- }
+ if (is_dma) {
+ #if CFG_TUD_DWC2_DMA_ENABLE
+ if (dir == TUSB_DIR_IN) {
+ handle_epin_dma(rhport, epnum, intr.diepint_bm);
+ } else {
+ handle_epout_dma(rhport, epnum, intr.doepint_bm);
}
-
- // Turn off TXFE if all bytes are written.
- if (((epin[n].dieptsiz & DIEPTSIZ_XFRSIZ_Msk) >> DIEPTSIZ_XFRSIZ_Pos) == 0) {
- dwc2->diepempmsk &= ~(1 << n);
+ #endif
+ } else {
+ #if CFG_TUD_DWC2_SLAVE_ENABLE
+ if (dir == TUSB_DIR_IN) {
+ handle_epin_slave(rhport, epnum, intr.diepint_bm);
+ } else {
+ handle_epout_slave(rhport, epnum, intr.doepint_bm);
}
+ #endif
}
}
}
}
/* Interrupt Hierarchy
-
- DxEPMSK.XferComplMsk DxEPINTn.XferCompl
- | |
- +---------- AND --------+
- |
- DAINT.xEPnInt DAINTMSK.xEPnMsk
- | |
- +---------- AND --------+
- |
- GINTSTS.xEPInt GINTMSK.xEPIntMsk
- | |
- +---------- AND --------+
- |
- GAHBCFG.GblIntrMsk
- |
- IRQn
+ DIEPINT DIEPINT
+ \ /
+ \ /
+ DAINT
+ / \
+ / \
+ GINTSTS: OEPInt IEPInt | USBReset | EnumDone | USBSusp | WkUpInt | OTGInt | SOF | RXFLVL
Note: when OTG_MULTI_PROC_INTRPT = 1, Device Each endpoint interrupt deachint/deachmsk/diepeachmsk/doepeachmsk
are combined to generate dedicated interrupt line for each endpoint.
*/
-
-
void dcd_int_handler(uint8_t rhport) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
- uint32_t const int_mask = dwc2->gintmsk;
- uint32_t const int_status = dwc2->gintsts & int_mask;
+ const uint32_t gintmask = dwc2->gintmsk;
+ const uint32_t gintsts = dwc2->gintsts & gintmask;
- if (int_status & GINTSTS_USBRST) {
+ if (gintsts & GINTSTS_USBRST) {
// USBRST is start of reset.
dwc2->gintsts = GINTSTS_USBRST;
- bus_reset(rhport);
+ handle_bus_reset(rhport);
}
- if (int_status & GINTSTS_ENUMDNE) {
+ if (gintsts & GINTSTS_ENUMDNE) {
// ENUMDNE is the end of reset where speed of the link is detected
dwc2->gintsts = GINTSTS_ENUMDNE;
-
- tusb_speed_t speed;
- switch ((dwc2->dsts & DSTS_ENUMSPD_Msk) >> DSTS_ENUMSPD_Pos) {
- case DSTS_ENUMSPD_HS:
- speed = TUSB_SPEED_HIGH;
- break;
-
- case DSTS_ENUMSPD_LS:
- speed = TUSB_SPEED_LOW;
- break;
-
- case DSTS_ENUMSPD_FS_HSPHY:
- case DSTS_ENUMSPD_FS:
- default:
- speed = TUSB_SPEED_FULL;
- break;
- }
-
- // TODO must update GUSBCFG_TRDT according to link speed
-
- dcd_event_bus_reset(rhport, speed, true);
+ handle_enum_done(rhport);
}
- if (int_status & GINTSTS_USBSUSP) {
+ if (gintsts & GINTSTS_USBSUSP) {
dwc2->gintsts = GINTSTS_USBSUSP;
dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
}
- if (int_status & GINTSTS_WKUINT) {
+ if (gintsts & GINTSTS_WKUINT) {
dwc2->gintsts = GINTSTS_WKUINT;
dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
}
@@ -1232,9 +1012,9 @@ void dcd_int_handler(uint8_t rhport) {
// TODO check GINTSTS_DISCINT for disconnect detection
// if(int_status & GINTSTS_DISCINT)
- if (int_status & GINTSTS_OTGINT) {
+ if (gintsts & GINTSTS_OTGINT) {
// OTG INT bit is read-only
- uint32_t const otg_int = dwc2->gotgint;
+ const uint32_t otg_int = dwc2->gotgint;
if (otg_int & GOTGINT_SEDET) {
dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true);
@@ -1243,20 +1023,21 @@ void dcd_int_handler(uint8_t rhport) {
dwc2->gotgint = otg_int;
}
- if(int_status & GINTSTS_SOF) {
+ if(gintsts & GINTSTS_SOF) {
dwc2->gintsts = GINTSTS_SOF;
const uint32_t frame = (dwc2->dsts & DSTS_FNSOF) >> DSTS_FNSOF_Pos;
// Disable SOF interrupt if SOF was not explicitly enabled since SOF was used for remote wakeup detection
- if (!_sof_en) {
+ if (!_dcd_data.sof_en) {
dwc2->gintmsk &= ~GINTMSK_SOFM;
}
dcd_event_sof(rhport, frame, true);
}
+#if CFG_TUD_DWC2_SLAVE_ENABLE
// RxFIFO non-empty interrupt handling.
- if (int_status & GINTSTS_RXFLVL) {
+ if (gintsts & GINTSTS_RXFLVL) {
// RXFLVL bit is read-only
dwc2->gintmsk &= ~GINTMSK_RXFLVLM; // disable RXFLVL interrupt while reading
@@ -1266,24 +1047,19 @@ void dcd_int_handler(uint8_t rhport) {
dwc2->gintmsk |= GINTMSK_RXFLVLM;
}
+#endif
// OUT endpoint interrupt handling.
- if (int_status & GINTSTS_OEPINT) {
+ if (gintsts & GINTSTS_OEPINT) {
// OEPINT is read-only, clear using DOEPINTn
- handle_epout_irq(rhport);
+ handle_ep_irq(rhport, TUSB_DIR_OUT);
}
// IN endpoint interrupt handling.
- if (int_status & GINTSTS_IEPINT) {
+ if (gintsts & GINTSTS_IEPINT) {
// IEPINT bit read-only, clear using DIEPINTn
- handle_epin_irq(rhport);
+ handle_ep_irq(rhport, TUSB_DIR_IN);
}
-
- // // Check for Incomplete isochronous IN transfer
- // if(int_status & GINTSTS_IISOIXFR) {
- // printf(" IISOIXFR!\r\n");
- //// TU_LOG(DWC2_DEBUG, " IISOIXFR!\r\n");
- // }
}
#if CFG_TUD_TEST_MODE
diff --git a/src/portable/synopsys/dwc2/dwc2_common.c b/src/portable/synopsys/dwc2/dwc2_common.c
new file mode 100644
index 000000000..f80ae9acb
--- /dev/null
+++ b/src/portable/synopsys/dwc2/dwc2_common.c
@@ -0,0 +1,311 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#define DWC2_COMMON_DEBUG 2
+
+#if defined(TUP_USBIP_DWC2) && (CFG_TUH_ENABLED || CFG_TUD_ENABLED)
+
+#if CFG_TUD_ENABLED
+#include "device/dcd.h"
+#endif
+
+#if CFG_TUH_ENABLED
+#include "host/hcd.h"
+#endif
+
+#include "dwc2_common.h"
+
+//--------------------------------------------------------------------
+//
+//--------------------------------------------------------------------
+static void reset_core(dwc2_regs_t* dwc2) {
+ // reset core
+ dwc2->grstctl |= GRSTCTL_CSRST;
+
+ if ((dwc2->gsnpsid & DWC2_CORE_REV_MASK) < (DWC2_CORE_REV_4_20a & DWC2_CORE_REV_MASK)) {
+ // prior v42.0 CSRST is self-clearing
+ while (dwc2->grstctl & GRSTCTL_CSRST) {}
+ } else {
+ // From v4.20a CSRST bit is write only, CSRT_DONE (w1c) is introduced for checking.
+ // CSRST must also be explicitly cleared
+ while (!(dwc2->grstctl & GRSTCTL_CSRST_DONE)) {}
+ dwc2->grstctl = (dwc2->grstctl & ~GRSTCTL_CSRST) | GRSTCTL_CSRST_DONE;
+ }
+
+ while (!(dwc2->grstctl & GRSTCTL_AHBIDL)) {} // wait for AHB master IDLE
+}
+
+static void phy_fs_init(dwc2_regs_t* dwc2) {
+ TU_LOG(DWC2_COMMON_DEBUG, "Fullspeed PHY init\r\n");
+
+ uint32_t gusbcfg = dwc2->gusbcfg;
+
+ // Select FS PHY
+ gusbcfg |= GUSBCFG_PHYSEL;
+ dwc2->gusbcfg = gusbcfg;
+
+ // MCU specific PHY init before reset
+ dwc2_phy_init(dwc2, GHWCFG2_HSPHY_NOT_SUPPORTED);
+
+ // Reset core after selecting PHY
+ reset_core(dwc2);
+
+ // USB turnaround time is critical for certification where long cables and 5-Hubs are used.
+ // So if you need the AHB to run at less than 30 MHz, and if USB turnaround time is not critical,
+ // these bits can be programmed to a larger value. Default is 5
+ gusbcfg &= ~GUSBCFG_TRDT_Msk;
+ gusbcfg |= 5u << GUSBCFG_TRDT_Pos;
+ dwc2->gusbcfg = gusbcfg;
+
+ // MCU specific PHY update post reset
+ dwc2_phy_update(dwc2, GHWCFG2_HSPHY_NOT_SUPPORTED);
+}
+
+static void phy_hs_init(dwc2_regs_t* dwc2) {
+ uint32_t gusbcfg = dwc2->gusbcfg;
+
+ // De-select FS PHY
+ gusbcfg &= ~GUSBCFG_PHYSEL;
+
+ if (dwc2->ghwcfg2_bm.hs_phy_type == GHWCFG2_HSPHY_ULPI) {
+ TU_LOG(DWC2_COMMON_DEBUG, "Highspeed ULPI PHY init\r\n");
+
+ // Select ULPI PHY (external)
+ gusbcfg |= GUSBCFG_ULPI_UTMI_SEL;
+
+ // ULPI is always 8-bit interface
+ gusbcfg &= ~GUSBCFG_PHYIF16;
+
+ // ULPI select single data rate
+ gusbcfg &= ~GUSBCFG_DDRSEL;
+
+ // default internal VBUS Indicator and Drive
+ gusbcfg &= ~(GUSBCFG_ULPIEVBUSD | GUSBCFG_ULPIEVBUSI);
+
+ // Disable FS/LS ULPI
+ gusbcfg &= ~(GUSBCFG_ULPIFSLS | GUSBCFG_ULPICSM);
+ } else {
+ TU_LOG(DWC2_COMMON_DEBUG, "Highspeed UTMI+ PHY init\r\n");
+
+ // Select UTMI+ PHY (internal)
+ gusbcfg &= ~GUSBCFG_ULPI_UTMI_SEL;
+
+ // Set 16-bit interface if supported
+ if (dwc2->ghwcfg4_bm.phy_data_width) {
+ gusbcfg |= GUSBCFG_PHYIF16; // 16 bit
+ } else {
+ gusbcfg &= ~GUSBCFG_PHYIF16; // 8 bit
+ }
+ }
+
+ // Apply config
+ dwc2->gusbcfg = gusbcfg;
+
+ // mcu specific phy init
+ dwc2_phy_init(dwc2, dwc2->ghwcfg2_bm.hs_phy_type);
+
+ // Reset core after selecting PHY
+ reset_core(dwc2);
+
+ // Set turn-around, must after core reset otherwise it will be clear
+ // - 9 if using 8-bit PHY interface
+ // - 5 if using 16-bit PHY interface
+ gusbcfg &= ~GUSBCFG_TRDT_Msk;
+ gusbcfg |= (dwc2->ghwcfg4_bm.phy_data_width ? 5u : 9u) << GUSBCFG_TRDT_Pos;
+ dwc2->gusbcfg = gusbcfg;
+
+ // MCU specific PHY update post reset
+ dwc2_phy_update(dwc2, dwc2->ghwcfg2_bm.hs_phy_type);
+}
+
+static bool check_dwc2(dwc2_regs_t* dwc2) {
+#if CFG_TUSB_DEBUG >= DWC2_COMMON_DEBUG
+ // print guid, gsnpsid, ghwcfg1, ghwcfg2, ghwcfg3, ghwcfg4
+ // Run 'python dwc2_info.py' and check dwc2_info.md for bit-field value and comparison with other ports
+ volatile uint32_t const* p = (volatile uint32_t const*) &dwc2->guid;
+ TU_LOG1("guid, gsnpsid, ghwcfg1, ghwcfg2, ghwcfg3, ghwcfg4\r\n");
+ for (size_t i = 0; i < 5; i++) {
+ TU_LOG1("0x%08" PRIX32 ", ", p[i]);
+ }
+ TU_LOG1("0x%08" PRIX32 "\r\n", p[5]);
+#endif
+
+ // For some reason: GD32VF103 gsnpsid and all hwcfg register are always zero (skip it)
+ (void)dwc2;
+#if !TU_CHECK_MCU(OPT_MCU_GD32VF103)
+ enum { GSNPSID_ID_MASK = TU_GENMASK(31, 16) };
+ const uint32_t gsnpsid = dwc2->gsnpsid & GSNPSID_ID_MASK;
+ TU_ASSERT(gsnpsid == DWC2_OTG_ID || gsnpsid == DWC2_FS_IOT_ID || gsnpsid == DWC2_HS_IOT_ID);
+#endif
+
+ return true;
+}
+
+//--------------------------------------------------------------------
+//
+//--------------------------------------------------------------------
+bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, tusb_role_t role) {
+ (void)dwc2;
+
+#if CFG_TUD_ENABLED
+ if (role == TUSB_ROLE_DEVICE && !TUD_OPT_HIGH_SPEED) {
+ return false;
+ }
+#endif
+#if CFG_TUH_ENABLED
+ if (role == TUSB_ROLE_HOST && !TUH_OPT_HIGH_SPEED) {
+ return false;
+ }
+#endif
+
+ return dwc2->ghwcfg2_bm.hs_phy_type != GHWCFG2_HSPHY_NOT_SUPPORTED;
+}
+
+/* dwc2 has several PHYs option
+ * - UTMI+ is internal highspeed PHY, clock can be 30 Mhz (8-bit) or 60 Mhz (16-bit)
+ * - ULPI is external highspeed PHY, clock is 60Mhz with only 8-bit interface
+ * - Dedicated FS PHY is internal with clock 48Mhz.
+ *
+ * In addition, UTMI+/ULPI can be shared to run at fullspeed mode with 48Mhz
+ *
+*/
+bool dwc2_core_init(uint8_t rhport, bool is_highspeed, bool is_dma) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+
+ // Check Synopsys ID register, failed if controller clock/power is not enabled
+ TU_ASSERT(check_dwc2(dwc2));
+
+ // disable global interrupt
+ dwc2->gahbcfg &= ~GAHBCFG_GINT;
+
+ if (is_highspeed) {
+ phy_hs_init(dwc2);
+ } else {
+ phy_fs_init(dwc2);
+ }
+
+ /* Set HS/FS Timeout Calibration to 7 (max available value).
+ * The number of PHY clocks that the application programs in
+ * this field is added to the high/full speed interpacket timeout
+ * duration in the core to account for any additional delays
+ * introduced by the PHY. This can be required, because the delay
+ * introduced by the PHY in generating the linestate condition
+ * can vary from one PHY to another. */
+ dwc2->gusbcfg |= (7ul << GUSBCFG_TOCAL_Pos);
+
+ // Enable PHY clock TODO stop/gate clock when suspended mode
+ dwc2->pcgcctl &= ~(PCGCCTL_STOPPCLK | PCGCCTL_GATEHCLK | PCGCCTL_PWRCLMP | PCGCCTL_RSTPDWNMODULE);
+
+ dfifo_flush_tx(dwc2, 0x10); // all tx fifo
+ dfifo_flush_rx(dwc2);
+
+ // Clear pending and disable all interrupts
+ dwc2->gintsts = 0xFFFFFFFFU;
+ dwc2->gotgint = 0xFFFFFFFFU;
+ dwc2->gintmsk = 0;
+
+ TU_LOG(DWC2_COMMON_DEBUG, "DMA = %u\r\n", is_dma);
+
+ if (is_dma) {
+ // DMA seems to be only settable after a core reset, and not possible to switch on-the-fly
+ dwc2->gahbcfg |= GAHBCFG_DMAEN | GAHBCFG_HBSTLEN_2;
+ } else {
+ dwc2->gintmsk |= GINTSTS_RXFLVL;
+ }
+
+ return true;
+}
+
+// void dwc2_core_handle_common_irq(uint8_t rhport, bool in_isr) {
+// (void) in_isr;
+// dwc2_regs_t * const dwc2 = DWC2_REG(rhport);
+// const uint32_t int_mask = dwc2->gintmsk;
+// const uint32_t int_status = dwc2->gintsts & int_mask;
+//
+// // Device disconnect
+// if (int_status & GINTSTS_DISCINT) {
+// dwc2->gintsts = GINTSTS_DISCINT;
+// }
+//
+// }
+
+//--------------------------------------------------------------------
+// DFIFO
+//--------------------------------------------------------------------
+// Read a single data packet from receive DFIFO
+void dfifo_read_packet(dwc2_regs_t* dwc2, uint8_t* dst, uint16_t len) {
+ const volatile uint32_t* rx_fifo = dwc2->fifo[0];
+
+ // Reading full available 32 bit words from fifo
+ uint16_t word_count = len >> 2;
+ while (word_count--) {
+ tu_unaligned_write32(dst, *rx_fifo);
+ dst += 4;
+ }
+
+ // Read the remaining 1-3 bytes from fifo
+ const uint8_t bytes_rem = len & 0x03;
+ if (bytes_rem != 0) {
+ const uint32_t tmp = *rx_fifo;
+ dst[0] = tu_u32_byte0(tmp);
+ if (bytes_rem > 1) {
+ dst[1] = tu_u32_byte1(tmp);
+ }
+ if (bytes_rem > 2) {
+ dst[2] = tu_u32_byte2(tmp);
+ }
+ }
+}
+
+// Write a single data packet to DFIFO
+void dfifo_write_packet(dwc2_regs_t* dwc2, uint8_t fifo_num, const uint8_t* src, uint16_t len) {
+ volatile uint32_t* tx_fifo = dwc2->fifo[fifo_num];
+
+ // Pushing full available 32 bit words to fifo
+ uint16_t word_count = len >> 2;
+ while (word_count--) {
+ *tx_fifo = tu_unaligned_read32(src);
+ src += 4;
+ }
+
+ // Write the remaining 1-3 bytes into fifo
+ const uint8_t bytes_rem = len & 0x03;
+ if (bytes_rem) {
+ uint32_t tmp_word = src[0];
+ if (bytes_rem > 1) {
+ tmp_word |= (src[1] << 8);
+ }
+ if (bytes_rem > 2) {
+ tmp_word |= (src[2] << 16);
+ }
+
+ *tx_fifo = tmp_word;
+ }
+}
+
+#endif
diff --git a/src/portable/synopsys/dwc2/dwc2_common.h b/src/portable/synopsys/dwc2/dwc2_common.h
new file mode 100644
index 000000000..18b93894f
--- /dev/null
+++ b/src/portable/synopsys/dwc2/dwc2_common.h
@@ -0,0 +1,101 @@
+/*
+* The MIT License (MIT)
+ *
+ * Copyright (c) 2024 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef TUSB_DWC2_COMMON_H
+#define TUSB_DWC2_COMMON_H
+
+#include "common/tusb_common.h"
+#include "dwc2_type.h"
+
+// Following symbols must be defined by port header
+// - _dwc2_controller[]: array of controllers
+// - DWC2_EP_MAX: largest EP counts of all controllers
+// - dwc2_phy_init/dwc2_phy_update: phy init called before and after core reset
+// - dwc2_dcd_int_enable/dwc2_dcd_int_disable
+// - dwc2_remote_wakeup_delay
+
+#if defined(TUP_USBIP_DWC2_STM32)
+ #include "dwc2_stm32.h"
+#elif defined(TUP_USBIP_DWC2_ESP32)
+ #include "dwc2_esp32.h"
+#elif TU_CHECK_MCU(OPT_MCU_GD32VF103)
+ #include "dwc2_gd32.h"
+#elif TU_CHECK_MCU(OPT_MCU_BCM2711, OPT_MCU_BCM2835, OPT_MCU_BCM2837)
+ #include "dwc2_bcm.h"
+#elif TU_CHECK_MCU(OPT_MCU_EFM32GG)
+ #include "dwc2_efm32.h"
+#elif TU_CHECK_MCU(OPT_MCU_XMC4000)
+ #include "dwc2_xmc.h"
+#else
+ #error "Unsupported MCUs"
+#endif
+
+enum {
+ DWC2_CONTROLLER_COUNT = TU_ARRAY_SIZE(_dwc2_controller)
+};
+
+enum {
+ OTG_INT_COMMON = 0 // GINTSTS_DISCINT | GINTSTS_CONIDSTSCHNG
+};
+
+//--------------------------------------------------------------------+
+// Core/Controller
+//--------------------------------------------------------------------+
+TU_ATTR_ALWAYS_INLINE static inline dwc2_regs_t* DWC2_REG(uint8_t rhport) {
+ if (rhport >= DWC2_CONTROLLER_COUNT) {
+ // user mis-configured, ignore and use first controller
+ rhport = 0;
+ }
+ return (dwc2_regs_t*)_dwc2_controller[rhport].reg_base;
+}
+
+bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, tusb_role_t role);
+bool dwc2_core_init(uint8_t rhport, bool is_highspeed, bool is_dma);
+void dwc2_core_handle_common_irq(uint8_t rhport, bool in_isr);
+
+//--------------------------------------------------------------------+
+// DFIFO
+//--------------------------------------------------------------------+
+TU_ATTR_ALWAYS_INLINE static inline void dfifo_flush_tx(dwc2_regs_t* dwc2, uint8_t fnum) {
+ // flush TX fifo and wait for it cleared
+ dwc2->grstctl = GRSTCTL_TXFFLSH | (fnum << GRSTCTL_TXFNUM_Pos);
+ while (dwc2->grstctl & GRSTCTL_TXFFLSH_Msk) {}
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void dfifo_flush_rx(dwc2_regs_t* dwc2) {
+ // flush RX fifo and wait for it cleared
+ dwc2->grstctl = GRSTCTL_RXFFLSH;
+ while (dwc2->grstctl & GRSTCTL_RXFFLSH_Msk) {}
+}
+
+void dfifo_read_packet(dwc2_regs_t* dwc2, uint8_t* dst, uint16_t len);
+void dfifo_write_packet(dwc2_regs_t* dwc2, uint8_t fifo_num, uint8_t const* src, uint16_t len);
+
+//--------------------------------------------------------------------+
+// DMA
+//--------------------------------------------------------------------+
+
+#endif
diff --git a/src/portable/synopsys/dwc2/dwc2_esp32.h b/src/portable/synopsys/dwc2/dwc2_esp32.h
index 4cdbcdb7a..ddbfd0d10 100644
--- a/src/portable/synopsys/dwc2/dwc2_esp32.h
+++ b/src/portable/synopsys/dwc2/dwc2_esp32.h
@@ -25,13 +25,14 @@
*/
-#ifndef _DWC2_ESP32_H_
-#define _DWC2_ESP32_H_
+#ifndef TUSB_DWC2_ESP32_H_
+#define TUSB_DWC2_ESP32_H_
#ifdef __cplusplus
extern "C" {
#endif
+#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_intr_alloc.h"
@@ -59,21 +60,37 @@ static const dwc2_controller_t _dwc2_controller[] = {
};
#endif
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
static intr_handle_t usb_ih[TU_ARRAY_SIZE(_dwc2_controller)];
-static void dcd_int_handler_wrap(void* arg) {
- const uint8_t rhport = (uint8_t)(uintptr_t) arg;
- dcd_int_handler(rhport);
+static void dwc2_int_handler_wrap(void* arg) {
+ const uint8_t rhport = tu_u16_low((uint16_t)(uintptr_t)arg);
+ const tusb_role_t role = (tusb_role_t) tu_u16_high((uint16_t)(uintptr_t)arg);
+#if CFG_TUD_ENABLED
+ if (role == TUSB_ROLE_DEVICE) {
+ dcd_int_handler(rhport);
+ }
+#endif
+#if CFG_TUH_ENABLED
+ if (role == TUSB_ROLE_HOST) {
+ hcd_int_handler(rhport, true);
+ }
+#endif
}
-TU_ATTR_ALWAYS_INLINE static inline void dwc2_dcd_int_enable(uint8_t rhport) {
- esp_intr_alloc(_dwc2_controller[rhport].irqnum, ESP_INTR_FLAG_LOWMED,
- dcd_int_handler_wrap, (void*)(uintptr_t) rhport, &usb_ih[rhport]);
+TU_ATTR_ALWAYS_INLINE static inline void dwc2_int_set(uint8_t rhport, tusb_role_t role, bool enabled) {
+ if (enabled) {
+ esp_intr_alloc(_dwc2_controller[rhport].irqnum, ESP_INTR_FLAG_LOWMED,
+ dwc2_int_handler_wrap, (void*)(uintptr_t)tu_u16(role, rhport), &usb_ih[rhport]);
+ } else {
+ esp_intr_free(usb_ih[rhport]);
+ }
}
-TU_ATTR_ALWAYS_INLINE static inline void dwc2_dcd_int_disable(uint8_t rhport) {
- esp_intr_free(usb_ih[rhport]);
-}
+#define dwc2_dcd_int_enable(_rhport) dwc2_int_set(_rhport, TUSB_ROLE_DEVICE, true)
+#define dwc2_dcd_int_disable(_rhport) dwc2_int_set(_rhport, TUSB_ROLE_DEVICE, false)
TU_ATTR_ALWAYS_INLINE static inline void dwc2_remote_wakeup_delay(void) {
vTaskDelay(pdMS_TO_TICKS(1));
@@ -83,16 +100,57 @@ TU_ATTR_ALWAYS_INLINE static inline void dwc2_remote_wakeup_delay(void) {
TU_ATTR_ALWAYS_INLINE static inline void dwc2_phy_init(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
(void)dwc2;
(void)hs_phy_type;
- // nothing to do
+ // maybe usb_utmi_hal_init()
+
}
// MCU specific PHY update, it is called AFTER init() and core reset
TU_ATTR_ALWAYS_INLINE static inline void dwc2_phy_update(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
(void)dwc2;
(void)hs_phy_type;
- // nothing to do
+ // maybe usb_utmi_hal_disable()
+}
+
+//--------------------------------------------------------------------+
+// Data Cache
+//--------------------------------------------------------------------+
+#if CFG_TUD_DWC2_DMA_ENABLE || CFG_TUH_DWC2_DMA_ENABLE
+#if defined(SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE) && SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
+#include "esp_cache.h"
+
+#if CFG_TUD_MEM_DCACHE_LINE_SIZE != CONFIG_CACHE_L1_CACHE_LINE_SIZE || \
+ CFG_TUH_MEM_DCACHE_LINE_SIZE != CONFIG_CACHE_L1_CACHE_LINE_SIZE
+#error "CFG_TUD/TUH_MEM_DCACHE_LINE_SIZE must match CONFIG_CACHE_L1_CACHE_LINE_SIZE"
+#endif
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t round_up_to_cache_line_size(uint32_t size) {
+ if (size & (CONFIG_CACHE_L1_CACHE_LINE_SIZE-1)) {
+ size = (size & ~(CONFIG_CACHE_L1_CACHE_LINE_SIZE-1)) + CONFIG_CACHE_L1_CACHE_LINE_SIZE;
+ }
+ return size;
}
+TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dcache_clean(const void* addr, uint32_t data_size) {
+ const int flag = ESP_CACHE_MSYNC_FLAG_TYPE_DATA | ESP_CACHE_MSYNC_FLAG_DIR_C2M;
+ data_size = round_up_to_cache_line_size(data_size);
+ return ESP_OK == esp_cache_msync((void*)addr, data_size, flag);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dcache_invalidate(const void* addr, uint32_t data_size) {
+ const int flag = ESP_CACHE_MSYNC_FLAG_TYPE_DATA | ESP_CACHE_MSYNC_FLAG_DIR_M2C;
+ data_size = round_up_to_cache_line_size(data_size);
+ return ESP_OK == esp_cache_msync((void*)addr, data_size, flag);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dcache_clean_invalidate(const void* addr, uint32_t data_size) {
+ const int flag = ESP_CACHE_MSYNC_FLAG_TYPE_DATA | ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_DIR_M2C;
+ data_size = round_up_to_cache_line_size(data_size);
+ return ESP_OK == esp_cache_msync((void*)addr, data_size, flag);
+}
+
+#endif
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/src/portable/synopsys/dwc2/dwc2_info.md b/src/portable/synopsys/dwc2/dwc2_info.md
index cfd0c81a8..dec021f59 100644
--- a/src/portable/synopsys/dwc2/dwc2_info.md
+++ b/src/portable/synopsys/dwc2/dwc2_info.md
@@ -1,58 +1,58 @@
-| | BCM2711 (Pi4) | EFM32GG | ESP32-S2/S3 | ESP32-P4 | ST F207/F407/411/429 FS | ST F407/429 HS | ST F412/767 FS | ST F723/L4P5 FS | ST F723 HS | ST F769 | ST H743/H750 | ST L476 FS | ST U5A5 HS | GD32VF103 | XMC4500 |
-|:---------------------------|:----------------|:-------------|:--------------|:-------------|:--------------------------|:-----------------|:-----------------|:------------------|:-------------|:-------------|:---------------|:-------------|:-------------|:------------|:-------------|
-| GUID | 0x2708A000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00001200 | 0x00001100 | 0x00002000 | 0x00003000 | 0x00003100 | 0x00002100 | 0x00002300 | 0x00002000 | 0x00005000 | 0x00001000 | 0x00AEC000 |
-| GSNPSID | 0x4F54280A | 0x4F54330A | 0x4F54400A | 0x4F54400A | 0x4F54281A | 0x4F54281A | 0x4F54320A | 0x4F54330A | 0x4F54330A | 0x4F54320A | 0x4F54330A | 0x4F54310A | 0x4F54411A | 0x00000000 | 0x4F54292A |
-| - specs version | 2.80a | 3.30a | 4.00a | 4.00a | 2.81a | 2.81a | 3.20a | 3.30a | 3.30a | 3.20a | 3.30a | 3.10a | 4.11a | 0.00W | 2.92a |
-| GHWCFG1 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 |
-| GHWCFG2 | 0x228DDD50 | 0x228F5910 | 0x224DD930 | 0x215FFFD0 | 0x229DCD20 | 0x229ED590 | 0x229ED520 | 0x229ED520 | 0x229FE1D0 | 0x229FE190 | 0x229FE190 | 0x229ED520 | 0x228FE052 | 0x00000000 | 0x228F5930 |
-| - op_mode | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | noHNP noSRP | HNP SRP | HNP SRP |
-| - arch | DMA internal | DMA internal | DMA internal | DMA internal | Slave only | DMA internal | Slave only | Slave only | DMA internal | DMA internal | DMA internal | Slave only | DMA internal | Slave only | DMA internal |
-| - p2p (hub support) | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 1 |
-| - hs_phy_type | UTMI+ | n/a | n/a | UTMI+/ULPI | n/a | ULPI | n/a | n/a | UTMI+/ULPI | ULPI | ULPI | n/a | UTMI+ | n/a | n/a |
-| - fs_phy_type | Dedicated | Dedicated | Dedicated | Shared ULPI | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | n/a | n/a | Dedicated |
-| - num_dev_ep | 7 | 6 | 6 | 15 | 3 | 5 | 5 | 5 | 8 | 8 | 8 | 5 | 8 | 0 | 6 |
-| - num_host_ch | 7 | 13 | 7 | 15 | 7 | 11 | 11 | 11 | 15 | 15 | 15 | 11 | 15 | 0 | 13 |
-| - period_channel_support | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 |
-| - enable_dynamic_fifo | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 |
-| - mul_proc_intrpt | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
-| - reserved21 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - nptx_q_depth | 2 | 2 | 1 | 1 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 0 | 2 |
-| - ptx_q_depth | 2 | 2 | 2 | 1 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 0 | 2 |
-| - token_q_depth | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 0 | 8 |
-| - otg_enable_ic_usb | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| GHWCFG3 | 0x0FF000E8 | 0x01F204E8 | 0x00C804B5 | 0x03805EB5 | 0x020001E8 | 0x03F403E8 | 0x0200D1E8 | 0x0200D1E8 | 0x03EED2E8 | 0x03EED2E8 | 0x03B8D2E8 | 0x0200D1E8 | 0x03B882E8 | 0x00000000 | 0x027A01E5 |
-| - xfer_size_width | 8 | 8 | 5 | 5 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 0 | 5 |
-| - packet_size_width | 6 | 6 | 3 | 3 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 0 | 6 |
-| - otg_enable | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 |
-| - i2c_enable | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 1 |
-| - vendor_ctrl_itf | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 |
-| - optional_feature_removed | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - synch_reset | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - otg_adp_support | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
-| - otg_enable_hsic | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - battery_charger_support | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
-| - lpm_mode | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 |
-| - dfifo_depth | 4080 | 498 | 200 | 896 | 512 | 1012 | 512 | 512 | 1006 | 1006 | 952 | 512 | 952 | 0 | 634 |
-| GHWCFG4 | 0x1FF00020 | 0x1BF08030 | 0xD3F0A030 | 0xDFF1A030 | 0x0FF08030 | 0x17F00030 | 0x17F08030 | 0x17F08030 | 0x23F00030 | 0x23F00030 | 0xE3F00030 | 0x17F08030 | 0xE2103E30 | 0x00000000 | 0xDBF08030 |
-| - num_dev_period_in_ep | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - partial_powerdown | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 |
-| - ahb_freq_min | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 |
-| - hibernation | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - extended_hibernation | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - reserved8 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - enhanced_lpm_support1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
-| - service_interval_flow | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
-| - ipg_isoc_support | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
-| - acg_support | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
-| - enhanced_lpm_support | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
-| - phy_data_width | 8 bit | 8/16 bit | 8/16 bit | 8/16 bit | 8/16 bit | 8 bit | 8/16 bit | 8/16 bit | 8 bit | 8 bit | 8 bit | 8/16 bit | 8 bit | 8 bit | 8/16 bit |
-| - ctrl_ep_num | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - iddg_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 |
-| - vbus_valid_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 1 |
-| - a_valid_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 1 |
-| - b_valid_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 1 |
-| - session_end_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 1 |
-| - dedicated_fifos | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 |
-| - num_dev_in_eps | 7 | 6 | 4 | 7 | 3 | 5 | 5 | 5 | 8 | 8 | 8 | 5 | 8 | 0 | 6 |
-| - dma_desc_enable | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 1 |
-| - dma_desc_dynamic | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 1 |
+| | BCM2711 (Pi4) | EFM32GG | ESP32-S2/S3 | ESP32-P4 | ST F207/F407/411/429 FS | ST F407/429 HS | ST F412/76x FS | ST F723/L4P5 FS | ST F723 HS | ST F76x HS | ST H743/H750 | ST L476 FS | ST U5A5 HS | XMC4500 | GD32VF103 |
+|:---------------------------|:----------------|:-------------|:--------------|:-------------|:--------------------------|:-----------------|:-----------------|:------------------|:-------------|:-------------|:---------------|:-------------|:-------------|:-------------|:------------|
+| GUID | 0x2708A000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00001200 | 0x00001100 | 0x00002000 | 0x00003000 | 0x00003100 | 0x00002100 | 0x00002300 | 0x00002000 | 0x00005000 | 0x00AEC000 | 0x00001000 |
+| GSNPSID | 0x4F54280A | 0x4F54330A | 0x4F54400A | 0x4F54400A | 0x4F54281A | 0x4F54281A | 0x4F54320A | 0x4F54330A | 0x4F54330A | 0x4F54320A | 0x4F54330A | 0x4F54310A | 0x4F54411A | 0x4F54292A | 0x00000000 |
+| - specs version | 2.80a | 3.30a | 4.00a | 4.00a | 2.81a | 2.81a | 3.20a | 3.30a | 3.30a | 3.20a | 3.30a | 3.10a | 4.11a | 2.92a | 0.00W |
+| GHWCFG1 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 |
+| GHWCFG2 | 0x228DDD50 | 0x228F5910 | 0x224DD930 | 0x215FFFD0 | 0x229DCD20 | 0x229ED590 | 0x229ED520 | 0x229ED520 | 0x229FE1D0 | 0x229FE190 | 0x229FE190 | 0x229ED520 | 0x228FE052 | 0x228F5930 | 0x00000000 |
+| - op_mode | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | noHNP noSRP | HNP SRP | HNP SRP |
+| - arch | DMA internal | DMA internal | DMA internal | DMA internal | Slave only | DMA internal | Slave only | Slave only | DMA internal | DMA internal | DMA internal | Slave only | DMA internal | DMA internal | Slave only |
+| - single_point | hub | hub | n/a | hub | n/a | hub | n/a | n/a | hub | hub | hub | n/a | hub | n/a | hub |
+| - hs_phy_type | UTMI+ | n/a | n/a | UTMI+/ULPI | n/a | ULPI | n/a | n/a | UTMI+/ULPI | ULPI | ULPI | n/a | UTMI+ | n/a | n/a |
+| - fs_phy_type | Dedicated | Dedicated | Dedicated | Shared ULPI | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | n/a | Dedicated | n/a |
+| - num_dev_ep | 7 | 6 | 6 | 15 | 3 | 5 | 5 | 5 | 8 | 8 | 8 | 5 | 8 | 6 | 0 |
+| - num_host_ch | 7 | 13 | 7 | 15 | 7 | 11 | 11 | 11 | 15 | 15 | 15 | 11 | 15 | 13 | 0 |
+| - period_channel_support | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
+| - enable_dynamic_fifo | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
+| - mul_proc_intrpt | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
+| - reserved21 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - nptx_q_depth | 8 | 8 | 4 | 4 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 2 |
+| - ptx_q_depth | 8 | 8 | 8 | 4 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 2 |
+| - token_q_depth | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 0 |
+| - otg_enable_ic_usb | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| GHWCFG3 | 0x0FF000E8 | 0x01F204E8 | 0x00C804B5 | 0x03805EB5 | 0x020001E8 | 0x03F403E8 | 0x0200D1E8 | 0x0200D1E8 | 0x03EED2E8 | 0x03EED2E8 | 0x03B8D2E8 | 0x0200D1E8 | 0x03B882E8 | 0x027A01E5 | 0x00000000 |
+| - xfer_size_width | 8 | 8 | 5 | 5 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 5 | 0 |
+| - packet_size_width | 6 | 6 | 3 | 3 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 0 |
+| - otg_enable | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
+| - i2c_enable | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |
+| - vendor_ctrl_itf | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 |
+| - optional_feature_removed | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - synch_reset | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - otg_adp_support | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
+| - otg_enable_hsic | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - battery_charger_support | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
+| - lpm_mode | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 |
+| - dfifo_depth | 4080 | 498 | 200 | 896 | 512 | 1012 | 512 | 512 | 1006 | 1006 | 952 | 512 | 952 | 634 | 0 |
+| GHWCFG4 | 0x1FF00020 | 0x1BF08030 | 0xD3F0A030 | 0xDFF1A030 | 0x0FF08030 | 0x17F00030 | 0x17F08030 | 0x17F08030 | 0x23F00030 | 0x23F00030 | 0xE3F00030 | 0x17F08030 | 0xE2103E30 | 0xDBF08030 | 0x00000000 |
+| - num_dev_period_in_ep | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - partial_powerdown | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
+| - ahb_freq_min | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
+| - hibernation | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - extended_hibernation | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - reserved8 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - enhanced_lpm_support1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
+| - service_interval_flow | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
+| - ipg_isoc_support | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
+| - acg_support | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
+| - enhanced_lpm_support | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
+| - phy_data_width | 8 bit | 8/16 bit | 8/16 bit | 8/16 bit | 8/16 bit | 8 bit | 8/16 bit | 8/16 bit | 8 bit | 8 bit | 8 bit | 8/16 bit | 8 bit | 8/16 bit | 8 bit |
+| - ctrl_ep_num | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - iddg_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
+| - vbus_valid_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
+| - a_valid_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
+| - b_valid_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
+| - session_end_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
+| - dedicated_fifos | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
+| - num_dev_in_eps | 7 | 6 | 4 | 7 | 3 | 5 | 5 | 5 | 8 | 8 | 8 | 5 | 8 | 6 | 0 |
+| - dma_desc_enable | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 |
+| - dma_desc_dynamic | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 |
diff --git a/src/portable/synopsys/dwc2/dwc2_info.py b/src/portable/synopsys/dwc2/dwc2_info.py
index d1793a005..25edcf22d 100755
--- a/src/portable/synopsys/dwc2/dwc2_info.py
+++ b/src/portable/synopsys/dwc2/dwc2_info.py
@@ -15,16 +15,15 @@ dwc2_reg_value = {
'ESP32-P4': [0, 0x4F54400A, 0, 0x215FFFD0, 0x03805EB5, 0xDFF1A030],
'ST F207/F407/411/429 FS': [0x1200, 0x4F54281A, 0, 0x229DCD20, 0x020001E8, 0x0FF08030],
'ST F407/429 HS': [0x1100, 0x4F54281A, 0, 0x229ED590, 0x03F403E8, 0x17F00030],
- 'ST F412/767 FS': [0x2000, 0x4F54320A, 0, 0x229ED520, 0x0200D1E8, 0x17F08030],
+ 'ST F412/76x FS': [0x2000, 0x4F54320A, 0, 0x229ED520, 0x0200D1E8, 0x17F08030],
'ST F723/L4P5 FS': [0x3000, 0x4F54330A, 0, 0x229ED520, 0x0200D1E8, 0x17F08030],
'ST F723 HS': [0x3100, 0x4F54330A, 0, 0x229FE1D0, 0x03EED2E8, 0x23F00030],
- 'ST F769': [0x2100, 0x4F54320A, 0, 0x229FE190, 0x03EED2E8, 0x23F00030],
+ 'ST F76x HS': [0x2100, 0x4F54320A, 0, 0x229FE190, 0x03EED2E8, 0x23F00030],
'ST H743/H750': [0x2300, 0x4F54330A, 0, 0x229FE190, 0x03B8D2E8, 0xE3F00030],
'ST L476 FS': [0x2000, 0x4F54310A, 0, 0x229ED520, 0x0200D1E8, 0x17F08030],
'ST U5A5 HS': [0x5000, 0x4F54411A, 0, 0x228FE052, 0x03B882E8, 0xE2103E30],
+ 'XMC4500': [0xAEC000, 0x4F54292A, 0, 0x228F5930, 0x027A01E5, 0xDBF08030],
'GD32VF103': [0x1000, 0, 0, 0, 0, 0],
- 'XMC4500': [0xAEC000, 0x4F54292A, 0, 0x228F5930, 0x027A01E5, 0xDBF08030]
-
}
# Combine dwc2_info with dwc2_reg_list
@@ -44,7 +43,7 @@ class GHWCFG2(ctypes.LittleEndianStructure):
_fields_ = [
("op_mode", ctypes.c_uint32, 3),
("arch", ctypes.c_uint32, 2),
- ("p2p (hub support)", ctypes.c_uint32, 1),
+ ("single_point", ctypes.c_uint32, 1),
("hs_phy_type", ctypes.c_uint32, 2),
("fs_phy_type", ctypes.c_uint32, 2),
("num_dev_ep", ctypes.c_uint32, 4),
@@ -119,6 +118,10 @@ GHWCFG2_field = {
1: "DMA external",
2: "DMA internal"
},
+ 'single_point': {
+ 0: "hub",
+ 1: "n/a"
+ },
'hs_phy_type': {
0: "n/a",
1: "UTMI+",
@@ -130,7 +133,18 @@ GHWCFG2_field = {
1: "Dedicated",
2: "Shared UTMI+",
3: "Shared ULPI"
- }
+ },
+ 'nptx_q_depth': {
+ 0: "2",
+ 1: "4",
+ 2: "8",
+ },
+ 'ptx_q_depth': {
+ 0: "2",
+ 1: "4",
+ 2: "8",
+ 3: "16"
+ },
}
# mapping for specific fields in GHWCFG4
diff --git a/src/portable/synopsys/dwc2/dwc2_stm32.h b/src/portable/synopsys/dwc2/dwc2_stm32.h
index 395b6d870..c11c1eb05 100644
--- a/src/portable/synopsys/dwc2/dwc2_stm32.h
+++ b/src/portable/synopsys/dwc2/dwc2_stm32.h
@@ -69,6 +69,14 @@ extern "C" {
#define OTG_FS_IRQn OTG_HS_IRQn
#endif
+#elif CFG_TUSB_MCU == OPT_MCU_STM32H7RS
+ #include "stm32h7rsxx.h"
+ #define EP_MAX_FS 6
+ #define EP_FIFO_SIZE_FS 1280
+
+ #define EP_MAX_HS 9
+ #define EP_FIFO_SIZE_HS 4096
+
#elif CFG_TUSB_MCU == OPT_MCU_STM32F7
#include "stm32f7xx.h"
#define EP_MAX_FS 6
@@ -124,13 +132,19 @@ static const dwc2_controller_t _dwc2_controller[] = {
// SystemCoreClock is already included by family header
// extern uint32_t SystemCoreClock;
-TU_ATTR_ALWAYS_INLINE static inline void dwc2_dcd_int_enable(uint8_t rhport) {
- NVIC_EnableIRQ((IRQn_Type) _dwc2_controller[rhport].irqnum);
+TU_ATTR_ALWAYS_INLINE static inline void dwc2_int_set(uint8_t rhport, tusb_role_t role, bool enabled) {
+ (void) role;
+ const IRQn_Type irqn = (IRQn_Type) _dwc2_controller[rhport].irqnum;
+ if (enabled) {
+ NVIC_EnableIRQ(irqn);
+ } else {
+ NVIC_DisableIRQ(irqn);
+ }
}
-TU_ATTR_ALWAYS_INLINE static inline void dwc2_dcd_int_disable(uint8_t rhport) {
- NVIC_DisableIRQ((IRQn_Type) _dwc2_controller[rhport].irqnum);
-}
+#define dwc2_dcd_int_enable(_rhport) dwc2_int_set(_rhport, TUSB_ROLE_DEVICE, true)
+#define dwc2_dcd_int_disable(_rhport) dwc2_int_set(_rhport, TUSB_ROLE_DEVICE, false)
+
TU_ATTR_ALWAYS_INLINE static inline void dwc2_remote_wakeup_delay(void) {
// try to delay for 1 ms
diff --git a/src/portable/synopsys/dwc2/dwc2_type.h b/src/portable/synopsys/dwc2/dwc2_type.h
index cf05bbfec..812096759 100644
--- a/src/portable/synopsys/dwc2/dwc2_type.h
+++ b/src/portable/synopsys/dwc2/dwc2_type.h
@@ -31,8 +31,8 @@
* opensource.org/licenses/BSD-3-Clause
*/
-#ifndef _TUSB_DWC2_TYPES_H_
-#define _TUSB_DWC2_TYPES_H_
+#ifndef TUSB_DWC2_TYPES_H_
+#define TUSB_DWC2_TYPES_H_
#include "stdint.h"
@@ -87,6 +87,11 @@ typedef struct
#endif
enum {
+ GOTGCTL_OTG_VERSION_1_3 = 0,
+ GOTGCTL_OTG_VERSION_2_0 = 1,
+};
+
+enum {
GHWCFG2_OPMODE_HNP_SRP = 0,
GHWCFG2_OPMODE_SRP = 1,
GHWCFG2_OPMODE_NON_HNP_NON_SRP = 2,
@@ -122,8 +127,62 @@ enum {
GHWCFFG4_PHY_DATA_WIDTH_8_16 = 2, // software selectable
};
+enum {
+ HPRT_SPEED_HIGH = 0,
+ HPRT_SPEED_FULL = 1,
+ HPRT_SPEED_LOW = 2
+};
+
+enum {
+ GINTSTS_CMODE_DEVICE = 0,
+ GINTSTS_CMODE_HOST = 1,
+};
+
+enum {
+ HCTSIZ_PID_DATA0 = 0, // 00b
+ HCTSIZ_PID_DATA2 = 1, // 01b
+ HCTSIZ_PID_DATA1 = 2, // 10b
+ HCTSIZ_PID_SETUP = 3, // 11b
+};
+enum {
+ HCTSIZ_PID_MDATA = 3,
+};
+
+enum {
+ GRXSTS_PKTSTS_GLOBAL_OUT_NAK = 1,
+ GRXSTS_PKTSTS_RX_DATA = 2,
+ GRXSTS_PKTSTS_RX_COMPLETE = 3,
+ GRXSTS_PKTSTS_SETUP_DONE = 4,
+ GRXSTS_PKTSTS_HOST_DATATOGGLE_ERR = 5,
+ GRXSTS_PKTSTS_SETUP_RX = 6,
+ GRXSTS_PKTSTS_HOST_CHANNEL_HALTED = 7
+};
+
+// Same as TUSB_XFER_*
+enum {
+ HCCHAR_EPTYPE_CONTROL = 0,
+ HCCHAR_EPTYPE_ISOCHRONOUS = 1,
+ HCCHAR_EPTYPE_BULK = 2,
+ HCCHAR_EPTYPE_INTERRUPT = 3
+};
+
+enum {
+ DCFG_SPEED_HIGH = 0, // Highspeed with 30/60 Mhz
+ DCFG_SPEED_FULL_30_60MHZ = 1, // Fullspeed with UTMI+/ULPI 30/60 Mhz
+ DCFG_SPEED_LOW = 2, // Lowspeed with FS PHY at 6 Mhz
+ DCFG_SPEED_FULL_48MHZ = 3, // Fullspeed with dedicated FS PHY at 48 Mhz
+};
+
+// Same as TUSB_XFER_*
+enum {
+ DEPCTL_EPTYPE_CONTROL = 0,
+ DEPCTL_EPTYPE_ISOCHRONOUS = 1,
+ DEPCTL_EPTYPE_BULK = 2,
+ DEPCTL_EPTYPE_INTERRUPT = 3
+};
+
//--------------------------------------------------------------------
-// Register bitfield definitions
+// Common Register Bitfield
//--------------------------------------------------------------------
typedef struct TU_ATTR_PACKED {
uint32_t ses_req_scs : 1; // 0 Session request success
@@ -146,7 +205,7 @@ typedef struct TU_ATTR_PACKED {
uint32_t ases_valid : 1; // 18 A-session valid
uint32_t bses_valid : 1; // 19 B-session valid
uint32_t otg_ver : 1; // 20 OTG version 0: v1.3, 1: v2.0
- uint32_t current_mode : 1; // 21 Current mode of operation 0: device, 1: host
+ uint32_t current_mode : 1; // 21 Current mode of operation. Only from v3.00a
uint32_t mult_val_id_bc : 5; // 22..26 Multi-valued input pin ID battery charger
uint32_t chirp_en : 1; // 27 Chirp detection enable
uint32_t rsv28_30 : 3; // 28.30: Reserved
@@ -192,7 +251,7 @@ typedef struct TU_ATTR_PACKED {
based on the speed of enumeration. The number of bit times added per PHY clock are as follows:
- High-speed: PHY clock One 30-MHz = 16 bit times, One 60-MHz = 8 bit times
- Full-speed: PHY clock One 30-MHz = 0.4 bit times, One 60-MHz = 0.2 bit times, One 48-MHz = 0.25 bit times */
- uint32_t phy_if : 1; // 3 PHY interface. 0: 8 bits, 1: 16 bits
+ uint32_t phy_if16 : 1; // 3 PHY interface. 0: 8 bits, 1: 16 bits
uint32_t ulpi_utmi_sel : 1; // 4 ULPI/UTMI select. 0: UTMI+, 1: ULPI
uint32_t fs_intf_sel : 1; // 5 Fullspeed serial interface select. 0: 6-pin, 1: 3-pin
uint32_t phy_sel : 1; // 6 HS/FS PHY selection. 0: HS UTMI+ or ULPI, 1: FS serial transceiver
@@ -241,9 +300,20 @@ typedef struct TU_ATTR_PACKED {
TU_VERIFY_STATIC(sizeof(dwc2_grstctl_t) == 4, "incorrect size");
typedef struct TU_ATTR_PACKED {
+ uint32_t ep_ch_num : 4; // 0..3 Endpoint/Channel Number
+ uint32_t byte_count :11; // 4..14 Byte Count
+ uint32_t dpid : 2; // 15..16 Data PID
+ uint32_t packet_status : 4; // 17..20 Packet Status
+ uint32_t frame_number : 4; // 21..24 Frame Number
+ uint32_t rsv25_31 : 7; // 25..31 Reserved
+} dwc2_grxstsp_t;
+TU_VERIFY_STATIC(sizeof(dwc2_grxstsp_t) == 4, "incorrect size");
+
+// Hardware Configuration
+typedef struct TU_ATTR_PACKED {
uint32_t op_mode : 3; // 0..2 HNP/SRP Host/Device/OTG mode
uint32_t arch : 2; // 3..4 Slave/External/Internal DMA
- uint32_t point2point : 1; // 5 0: support hub and split | 1: no hub, no split
+ uint32_t single_point : 1; // 5 0: support hub and split | 1: no hub, no split
uint32_t hs_phy_type : 2; // 6..7 0: not supported | 1: UTMI+ | 2: ULPI | 3: UTMI+ and ULPI
uint32_t fs_phy_type : 2; // 8..9 0: not supported | 1: dedicated | 2: UTMI+ | 3: ULPI
uint32_t num_dev_ep : 4; // 10..13 Number of device endpoints (excluding EP0)
@@ -301,56 +371,256 @@ typedef struct TU_ATTR_PACKED {
}dwc2_ghwcfg4_t;
TU_VERIFY_STATIC(sizeof(dwc2_ghwcfg4_t) == 4, "incorrect size");
+//--------------------------------------------------------------------
+// Host Register Bitfield
+//--------------------------------------------------------------------
+
+typedef struct TU_ATTR_PACKED {
+ uint32_t fifo_available : 16; // 0..15 Number of words available in the Tx FIFO
+ uint32_t req_queue_available : 8; // 16..23 Number of spaces available in the NPT transmit request queue for both IN and OU
+ // 24..31 is top entry in the request queue that is currently being processed by the MAC
+ uint32_t qtop_terminate : 1; // 24 Last entry for selected channel
+ uint32_t qtop_type : 2; // 25..26 Token (0) In/Out (1) ZLP, (2) Ping/cspit, (3) Channel halt command
+ uint32_t qtop_ch_num : 4; // 27..30 Channel number
+} dwc2_hnptxsts_t;
+TU_VERIFY_STATIC(sizeof(dwc2_hnptxsts_t) == 4, "incorrect size");
+
+typedef struct TU_ATTR_PACKED {
+ uint32_t fifo_available : 16; // 0..15 Number of words available in the Tx FIFO
+ uint32_t req_queue_available : 7; // 16..22 Number of spaces available in the PTX transmit request queue
+ uint32_t qtop_terminate : 1; // 23 Last entry for selected channel
+ uint32_t qtop_last_period : 1; // 24 Last entry for selected channel is a periodic entry
+ uint32_t qtop_type : 2; // 25..26 Token (0) In/Out (1) ZLP, (2) Ping/cspit, (3) Channel halt command
+ uint32_t qtop_ch_num : 4; // 27..30 Channel number
+ uint32_t qtop_odd_frame : 1; // 31 Send in odd frame
+} dwc2_hptxsts_t;
+TU_VERIFY_STATIC(sizeof(dwc2_hptxsts_t) == 4, "incorrect size");
+
+typedef struct TU_ATTR_PACKED {
+ uint32_t conn_status : 1; // 0 Port connect status
+ uint32_t conn_detected : 1; // 1 Port connect detected
+ uint32_t enable : 1; // 2 Port enable status
+ uint32_t enable_change : 1; // 3 Port enable change
+ uint32_t over_current_active : 1; // 4 Port Over-current active
+ uint32_t over_current_change : 1; // 5 Port Over-current change
+ uint32_t resume : 1; // 6 Port resume
+ uint32_t suspend : 1; // 7 Port suspend
+ uint32_t reset : 1; // 8 Port reset
+ uint32_t rsv9 : 1; // 9 Reserved
+ uint32_t line_status : 2; // 10..11 Line status
+ uint32_t power : 1; // 12 Port power
+ uint32_t test_control : 4; // 13..16 Port Test control
+ uint32_t speed : 2; // 17..18 Port speed
+ uint32_t rsv19_31 :13; // 19..31 Reserved
+}dwc2_hprt_t;
+TU_VERIFY_STATIC(sizeof(dwc2_hprt_t) == 4, "incorrect size");
+
+typedef struct TU_ATTR_PACKED {
+ uint32_t ep_size : 11; // 0..10 Maximum packet size
+ uint32_t ep_num : 4; // 11..14 Endpoint number
+ uint32_t ep_dir : 1; // 15 Endpoint direction
+ uint32_t rsv16 : 1; // 16 Reserved
+ uint32_t low_speed_dev : 1; // 17 Low-speed device
+ uint32_t ep_type : 2; // 18..19 Endpoint type
+ uint32_t err_multi_count : 2; // 20..21 Error (splitEn = 1) / Multi (SplitEn = 0) count
+ uint32_t dev_addr : 7; // 22..28 Device address
+ uint32_t odd_frame : 1; // 29 Odd frame
+ uint32_t disable : 1; // 30 Channel disable
+ uint32_t enable : 1; // 31 Channel enable
+} dwc2_channel_char_t;
+TU_VERIFY_STATIC(sizeof(dwc2_channel_char_t) == 4, "incorrect size");
+
+typedef struct TU_ATTR_PACKED {
+ uint32_t hub_port : 7; // 0..6 Hub port number
+ uint32_t hub_addr : 7; // 7..13 Hub address
+ uint32_t xact_pos : 2; // 14..15 Transaction position
+ uint32_t split_compl : 1; // 16 Split completion
+ uint32_t rsv17_30 : 14; // 17..30 Reserved
+ uint32_t split_en : 1; // 31 Split enable
+} dwc2_channel_split_t;
+TU_VERIFY_STATIC(sizeof(dwc2_channel_split_t) == 4, "incorrect size");
+
+typedef struct TU_ATTR_PACKED {
+ uint32_t xfer_size : 19; // 0..18 Transfer size in bytes
+ uint32_t packet_count : 10; // 19..28 Number of packets
+ uint32_t pid : 2; // 29..30 Packet ID
+ uint32_t do_ping : 1; // 31 Do PING
+} dwc2_channel_tsize_t;
+TU_VERIFY_STATIC(sizeof(dwc2_channel_tsize_t) == 4, "incorrect size");
+
+typedef struct TU_ATTR_PACKED {
+ uint32_t num : 16; // 0..15 Frame number
+ uint32_t remainning : 16; // 16..31 Frame remaining
+} dwc2_hfnum_t;
+TU_VERIFY_STATIC(sizeof(dwc2_hfnum_t) == 4, "incorrect size");
+
// Host Channel
typedef struct {
- volatile uint32_t hcchar; // 500 + 20*ch Host Channel Characteristics
- volatile uint32_t hcsplt; // 504 + 20*ch Host Channel Split Control
- volatile uint32_t hcint; // 508 + 20*ch Host Channel Interrupt
- volatile uint32_t hcintmsk; // 50C + 20*ch Host Channel Interrupt Mask
- volatile uint32_t hctsiz; // 510 + 20*ch Host Channel Transfer Size
- volatile uint32_t hcdma; // 514 + 20*ch Host Channel DMA Address
- uint32_t reserved518; // 518 + 20*ch
- volatile uint32_t hcdmab; // 51C + 20*ch Host Channel DMA Address
+ union {
+ volatile uint32_t hcchar; // 500 + 20*ch Host Channel Characteristics
+ volatile dwc2_channel_char_t hcchar_bm;
+ };
+ union {
+ volatile uint32_t hcsplt; // 504 + 20*ch Host Channel Split Control
+ volatile dwc2_channel_split_t hcsplt_bm;
+ };
+ volatile uint32_t hcint; // 508 + 20*ch Host Channel Interrupt
+ volatile uint32_t hcintmsk; // 50C + 20*ch Host Channel Interrupt Mask
+ union {
+ volatile uint32_t hctsiz; // 510 + 20*ch Host Channel Transfer Size
+ volatile dwc2_channel_tsize_t hctsiz_bm;
+ };
+ volatile uint32_t hcdma; // 514 + 20*ch Host Channel DMA Address
+ uint32_t reserved518; // 518 + 20*ch
+ volatile uint32_t hcdmab; // 51C + 20*ch Host Channel DMA Address
} dwc2_channel_t;
-// Endpoint IN
-typedef struct {
- volatile uint32_t diepctl; // 900 + 20*ep Device IN Endpoint Control
- uint32_t reserved04; // 904
- volatile uint32_t diepint; // 908 + 20*ep Device IN Endpoint Interrupt
- uint32_t reserved0c; // 90C
- volatile uint32_t dieptsiz; // 910 + 20*ep Device IN Endpoint Transfer Size
- volatile uint32_t diepdma; // 914 + 20*ep Device IN Endpoint DMA Address
- volatile uint32_t dtxfsts; // 918 + 20*ep Device IN Endpoint Tx FIFO Status
- uint32_t reserved1c; // 91C
-} dwc2_epin_t;
+//--------------------------------------------------------------------
+// Device Register Bitfield
+//--------------------------------------------------------------------
+typedef struct TU_ATTR_PACKED {
+ uint32_t speed : 2; // 0..1 Speed
+ uint32_t nzsts_out_handshake : 1; // 2 Non-zero-length status OUT handshake
+ uint32_t en_32khz_suspsend : 1; // 3 Enable 32-kHz SUSPEND mode
+ uint32_t address : 7; // 4..10 Device address
+ uint32_t period_frame_interval : 2; // 11..12 Periodic frame interval
+ uint32_t en_out_nak : 1; // 13 Enable Device OUT NAK
+ uint32_t xcvr_delay : 1; // 14 Transceiver delay
+ uint32_t erratic_int_mask : 1; // 15 Erratic interrupt mask
+ uint32_t rsv16 : 1; // 16 Reserved
+ uint32_t ipg_iso_support : 1; // 17 Interpacket gap ISO support
+ uint32_t epin_mismatch_count : 5; // 18..22 EP IN mismatch count
+ uint32_t dma_desc : 1; // 23 Enable scatter/gatter DMA descriptor
+ uint32_t period_schedule_interval : 2; // 24..25 Periodic schedule interval for scatter/gatter DMA
+ uint32_t resume_valid : 6; // 26..31 Resume valid period
+} dwc2_dcfg_t;
+TU_VERIFY_STATIC(sizeof(dwc2_dcfg_t) == 4, "incorrect size");
-// Endpoint OUT
-typedef struct {
- volatile uint32_t doepctl; // B00 + 20*ep Device OUT Endpoint Control
- uint32_t reserved04; // B04
- volatile uint32_t doepint; // B08 + 20*ep Device OUT Endpoint Interrupt
- uint32_t reserved0c; // B0C
- volatile uint32_t doeptsiz; // B10 + 20*ep Device OUT Endpoint Transfer Size
- volatile uint32_t doepdma; // B14 + 20*ep Device OUT Endpoint DMA Address
- uint32_t reserved18[2]; // B18..B1C
-} dwc2_epout_t;
+typedef struct TU_ATTR_PACKED {
+ uint32_t remote_wakeup_signal : 1; // 0 Remote wakeup signal
+ uint32_t soft_disconnet : 1; // 1 Soft disconnect
+ uint32_t gnp_in_nak_status : 1; // 2 Global non-periodic NAK IN status
+ uint32_t gout_nak_status : 1; // 3 Global OUT NAK status
+ uint32_t test_control : 3; // 4..6 Test control
+ uint32_t set_gnp_in_nak : 1; // 7 Set global non-periodic IN NAK
+ uint32_t clear_gnp_in_nak : 1; // 8 Clear global non-periodic IN NAK
+ uint32_t set_gout_nak : 1; // 9 Set global OUT NAK
+ uint32_t clear_gout_nak : 1; // 10 Clear global OUT NAK
+ uint32_t poweron_prog_done : 1; // 11 Power-on programming done
+ uint32_t rsv12 : 1; // 12 Reserved
+ uint32_t global_multi_count : 2; // 13..14 Global multi-count
+ uint32_t ignore_frame_number : 1; // 15 Ignore frame number
+ uint32_t nak_on_babble : 1; // 16 NAK on babble
+ uint32_t en_cont_on_bna : 1; // 17 Enable continue on BNA
+ uint32_t deep_sleep_besl_reject : 1; // 18 Deep sleep BESL reject
+ uint32_t service_interval : 1; // 19 Service interval for ISO IN endpoint
+ uint32_t rsv20_31 :12; // 20..31 Reserved
+} dwc2_dctl_t;
+TU_VERIFY_STATIC(sizeof(dwc2_dctl_t) == 4, "incorrect size");
+
+typedef struct TU_ATTR_PACKED {
+ uint32_t suspend_status : 1; // 0 Suspend status
+ uint32_t enum_speed : 2; // 1..2 Enumerated speed
+ uint32_t erratic_err : 1; // 3 Erratic error
+ uint32_t rsv4_7 : 4; // 4..7 Reserved
+ uint32_t frame_number : 14; // 8..21 Frame/MicroFrame number
+ uint32_t line_status : 2; // 22..23 Line status
+ uint32_t rsv24_31 : 8; // 24..31 Reserved
+} dwc2_dsts_t;
+TU_VERIFY_STATIC(sizeof(dwc2_dsts_t) == 4, "incorrect size");
+
+typedef struct TU_ATTR_PACKED {
+ uint32_t xfer_complete : 1; // 0 Transfer complete
+ uint32_t disabled : 1; // 1 Endpoint disabled
+ uint32_t ahb_err : 1; // 2 AHB error
+ uint32_t timeout : 1; // 3 Timeout
+ uint32_t in_rx_txfe : 1; // 4 IN token received when TxFIFO is empty
+ uint32_t in_rx_ep_mismatch : 1; // 5 IN token received with EP mismatch
+ uint32_t in_ep_nak_effective : 1; // 6 IN endpoint NAK effective
+ uint32_t txfifo_empty : 1; // 7 TX FIFO empty
+ uint32_t txfifo_underrun : 1; // 8 Tx FIFO under run
+ uint32_t bna : 1; // 9 Buffer not available
+ uint32_t rsv10 : 1; // 10 Reserved
+ uint32_t iso_packet_drop : 1; // 11 Isochronous OUT packet drop status
+ uint32_t babble_err : 1; // 12 Babble error
+ uint32_t nak : 1; // 13 NAK
+ uint32_t nyet : 1; // 14 NYET
+ uint32_t rsv14_31 :17; // 15..31 Reserved
+} dwc2_diepint_t;
+TU_VERIFY_STATIC(sizeof(dwc2_diepint_t) == 4, "incorrect size");
+typedef struct TU_ATTR_PACKED {
+ uint32_t mps : 11; // 0..10 Maximum packet size, EP0 only use 2 bit
+ uint32_t next_ep : 4; // 11..14 Next endpoint number
+ uint32_t active : 1; // 15 Active
+ const uint32_t dpid_iso_odd : 1; // 16 DATA0/DATA1 for bulk/interrupt, odd frame for isochronous
+ const uint32_t nak_status : 1; // 17 NAK status
+ uint32_t type : 2; // 18..19 Endpoint type
+ uint32_t rsv20 : 1; // 20 Reserved
+ uint32_t stall : 1; // 21 Stall
+ uint32_t tx_fifo_num : 4; // 22..25 Tx FIFO number (IN)
+ uint32_t clear_nak : 1; // 26 Clear NAK
+ uint32_t set_nak : 1; // 27 Set NAK
+ uint32_t set_data0_iso_even : 1; // 28 Set DATA0 if bulk/interrupt, even frame for isochronous
+ uint32_t set_data1_iso_odd : 1; // 29 Set DATA1 if bulk/interrupt, odd frame for isochronous
+ uint32_t disable : 1; // 30 Disable
+ uint32_t enable : 1; // 31 Enable
+} dwc2_depctl_t;
+TU_VERIFY_STATIC(sizeof(dwc2_depctl_t) == 4, "incorrect size");
+
+typedef struct TU_ATTR_PACKED {
+ uint32_t xfer_complete : 1; // 0 Transfer complete
+ uint32_t disabled : 1; // 1 Endpoint disabled
+ uint32_t ahb_err : 1; // 2 AHB error
+ uint32_t setup_phase_done : 1; // 3 Setup phase done
+ uint32_t out_rx_ep_disabled : 1; // 4 OUT token received when endpoint disabled
+ uint32_t status_phase_rx : 1; // 5 Status phase received
+ uint32_t setup_b2b : 1; // 6 Setup packet back-to-back
+ uint32_t rsv7 : 1; // 7 Reserved
+ uint32_t out_packet_err : 1; // 8 OUT packet error
+ uint32_t bna : 1; // 9 Buffer not available
+ uint32_t rsv10 : 1; // 10 Reserved
+ uint32_t iso_packet_drop : 1; // 11 Isochronous OUT packet drop status
+ uint32_t babble_err : 1; // 12 Babble error
+ uint32_t nak : 1; // 13 NAK
+ uint32_t nyet : 1; // 14 NYET
+ uint32_t setup_packet_rx : 1; // 15 Setup packet received (Buffer DMA Mode only)
+ uint32_t rsv16_31 :16; // 16..31 Reserved
+} dwc2_doepint_t;
+TU_VERIFY_STATIC(sizeof(dwc2_doepint_t) == 4, "incorrect size");
+
+typedef struct TU_ATTR_PACKED {
+ uint32_t xfer_size : 19; // 0..18 Transfer size in bytes
+ uint32_t packet_count : 10; // 19..28 Number of packets
+ uint32_t mc_pid : 2; // 29..30 IN: Multi Count, OUT: PID
+} dwc2_ep_tsize_t;
+TU_VERIFY_STATIC(sizeof(dwc2_ep_tsize_t) == 4, "incorrect size");
+
+// Device IN/OUT Endpoint
typedef struct {
union {
volatile uint32_t diepctl;
volatile uint32_t doepctl;
+
volatile uint32_t ctl;
+ volatile dwc2_depctl_t ctl_bm;
};
uint32_t rsv04;
union {
+ volatile uint32_t intr;
+
volatile uint32_t diepint;
+ volatile dwc2_diepint_t diepint_bm;
+
volatile uint32_t doepint;
+ volatile dwc2_doepint_t doepint_bm;
};
uint32_t rsv0c;
union {
volatile uint32_t dieptsiz;
volatile uint32_t doeptsiz;
+ volatile uint32_t tsiz;
+ volatile dwc2_ep_tsize_t tsiz_bm;
};
union {
volatile uint32_t diepdma;
@@ -367,21 +637,43 @@ TU_VERIFY_STATIC(sizeof(dwc2_dep_t) == 0x20, "incorrect size");
//--------------------------------------------------------------------
typedef struct {
//------------- Core Global -------------//
+ union {
volatile uint32_t gotgctl; // 000 OTG Control and Status
+ volatile dwc2_gotgctl_t gotgctl_bm;
+ };
+ union {
volatile uint32_t gotgint; // 004 OTG Interrupt
+ volatile dwc2_gotgint_t gotgint_bm;
+ };
+ union {
volatile uint32_t gahbcfg; // 008 AHB Configuration
+ volatile dwc2_gahbcfg_t gahbcfg_bm;
+ };
+ union {
volatile uint32_t gusbcfg; // 00c USB Configuration
+ volatile dwc2_gusbcfg_t gusbcfg_bm;
+ };
+ union {
volatile uint32_t grstctl; // 010 Reset
+ volatile dwc2_grstctl_t grstctl_bm;
+ };
volatile uint32_t gintsts; // 014 Interrupt
volatile uint32_t gintmsk; // 018 Interrupt Mask
volatile uint32_t grxstsr; // 01c Receive Status Debug Read
+ union {
volatile uint32_t grxstsp; // 020 Receive Status Read/Pop
+ volatile dwc2_grxstsp_t grxstsp_bm;
+ };
volatile uint32_t grxfsiz; // 024 Receive FIFO Size
union {
volatile uint32_t dieptxf0; // 028 EP0 Tx FIFO Size
volatile uint32_t gnptxfsiz; // 028 Non-periodic Transmit FIFO Size
};
- volatile uint32_t gnptxsts; // 02c Non-periodic Transmit FIFO/Queue Status
+ union {
+ volatile uint32_t hnptxsts; // 02c Non-periodic Transmit FIFO/Queue Status
+ volatile dwc2_hnptxsts_t hnptxsts_bm;
+ volatile uint32_t gnptxsts;
+ };
volatile uint32_t gi2cctl; // 030 I2C Address
volatile uint32_t gpvndctl; // 034 PHY Vendor Control
union {
@@ -415,14 +707,23 @@ typedef struct {
//------------ Host -------------//
volatile uint32_t hcfg; // 400 Host Configuration
volatile uint32_t hfir; // 404 Host Frame Interval
+ union {
volatile uint32_t hfnum; // 408 Host Frame Number / Frame Remaining
+ volatile dwc2_hfnum_t hfnum_bm;
+ };
uint32_t reserved40c; // 40C
+ union {
volatile uint32_t hptxsts; // 410 Host Periodic TX FIFO / Queue Status
+ volatile dwc2_hptxsts_t hptxsts_bm;
+ };
volatile uint32_t haint; // 414 Host All Channels Interrupt
volatile uint32_t haintmsk; // 418 Host All Channels Interrupt Mask
volatile uint32_t hflbaddr; // 41C Host Frame List Base Address
uint32_t reserved420[8]; // 420..43F
+ union {
volatile uint32_t hprt; // 440 Host Port Control and Status
+ volatile dwc2_hprt_t hprt_bm;
+ };
uint32_t reserved444[47]; // 444..4FF
//------------- Host Channel -------------//
@@ -430,12 +731,27 @@ typedef struct {
uint32_t reserved700[64]; // 700..7FF
//------------- Device -----------//
+ union {
volatile uint32_t dcfg; // 800 Device Configuration
+ volatile dwc2_dcfg_t dcfg_bm;
+ };
+ union {
volatile uint32_t dctl; // 804 Device Control
+ volatile dwc2_dctl_t dctl_bm;
+ };
+ union {
volatile uint32_t dsts; // 808 Device Status (RO)
+ volatile dwc2_dsts_t dsts_bm;
+ };
uint32_t reserved80c; // 80C
+ union {
volatile uint32_t diepmsk; // 810 Device IN Endpoint Interrupt Mask
+ volatile dwc2_diepint_t diepmsk_bm;
+ };
+ union {
volatile uint32_t doepmsk; // 814 Device OUT Endpoint Interrupt Mask
+ volatile dwc2_doepint_t doepmsk_bm;
+ };
volatile uint32_t daint; // 818 Device All Endpoints Interrupt
volatile uint32_t daintmsk; // 81C Device All Endpoints Interrupt Mask
volatile uint32_t dtknqr1; // 820 Device IN token sequence learning queue read1
@@ -457,15 +773,15 @@ typedef struct {
union {
dwc2_dep_t ep[2][16]; // 0: IN, 1 OUT
struct {
- dwc2_epin_t epin[16]; // 900..AFF IN Endpoints
- dwc2_epout_t epout[16]; // B00..CFF OUT Endpoints
+ dwc2_dep_t epin[16]; // 900..AFF IN Endpoints
+ dwc2_dep_t epout[16]; // B00..CFF OUT Endpoints
};
};
uint32_t reservedd00[64]; // D00..DFF
//------------- Power Clock -------------//
- volatile uint32_t pcgctl; // E00 Power and Clock Gating Control
- volatile uint32_t pcgctl1; // E04
+ volatile uint32_t pcgcctl; // E00 Power and Clock Gating Characteristic Control
+ volatile uint32_t pcgcctl1; // E04 Power and Clock Gating Characteristic Control 1
uint32_t reservede08[126]; // E08..FFF
//------------- FIFOs -------------//
@@ -478,7 +794,7 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, channel) == 0x0500, "incorrect size");
TU_VERIFY_STATIC(offsetof(dwc2_regs_t, dcfg ) == 0x0800, "incorrect size");
TU_VERIFY_STATIC(offsetof(dwc2_regs_t, epin ) == 0x0900, "incorrect size");
TU_VERIFY_STATIC(offsetof(dwc2_regs_t, epout ) == 0x0B00, "incorrect size");
-TU_VERIFY_STATIC(offsetof(dwc2_regs_t, pcgctl ) == 0x0E00, "incorrect size");
+TU_VERIFY_STATIC(offsetof(dwc2_regs_t, pcgcctl) == 0x0E00, "incorrect size");
TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
//--------------------------------------------------------------------+
@@ -542,14 +858,16 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define GOTGCTL_OTGVER GOTGCTL_OTGVER_Msk // OTG version
/******************** Bit definition for HCFG register ********************/
-#define HCFG_FSLSPCS_Pos (0U)
-#define HCFG_FSLSPCS_Msk (0x3UL << HCFG_FSLSPCS_Pos) // 0x00000003
-#define HCFG_FSLSPCS HCFG_FSLSPCS_Msk // FS/LS PHY clock select
-#define HCFG_FSLSPCS_0 (0x1UL << HCFG_FSLSPCS_Pos) // 0x00000001
-#define HCFG_FSLSPCS_1 (0x2UL << HCFG_FSLSPCS_Pos) // 0x00000002
-#define HCFG_FSLSS_Pos (2U)
-#define HCFG_FSLSS_Msk (0x1UL << HCFG_FSLSS_Pos) // 0x00000004
-#define HCFG_FSLSS HCFG_FSLSS_Msk // FS- and LS-only support
+#define HCFG_FSLS_PHYCLK_SEL_Pos (0U)
+#define HCFG_FSLS_PHYCLK_SEL_Msk (0x3UL << HCFG_FSLS_PHYCLK_SEL_Pos) // 0x00000003
+#define HCFG_FSLS_PHYCLK_SEL HCFG_FSLS_PHYCLK_SEL_Msk // FS/LS PHY clock select
+#define HCFG_FSLS_PHYCLK_SEL_30_60MHZ (0x0UL << HCFG_FSLS_PHYCLK_SEL_Pos) // 0x00000000
+#define HCFG_FSLS_PHYCLK_SEL_48MHZ (0x1UL << HCFG_FSLS_PHYCLK_SEL_Pos) // 0x00000001
+#define HCFG_FSLS_PHYCLK_SEL_6MHZ (0x2UL << HCFG_FSLS_PHYCLK_SEL_Pos) // 0x00000002
+
+#define HCFG_FSLS_ONLY_Pos (2U)
+#define HCFG_FSLS_ONLY_Msk (0x1UL << HCFG_FSLS_ONLY_Pos) // 0x00000004
+#define HCFG_FSLS_ONLY HCFG_FSLS_ONLY_Msk // FS- and LS-only support
/******************** Bit definition for PCGCR register ********************/
#define PCGCR_STPPCLK_Pos (0U)
@@ -664,6 +982,9 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define HFIR_FRIVL_Pos (0U)
#define HFIR_FRIVL_Msk (0xFFFFUL << HFIR_FRIVL_Pos) // 0x0000FFFF
#define HFIR_FRIVL HFIR_FRIVL_Msk // Frame interval
+#define HFIR_RELOAD_CTRL_Pos (16U) // available since v2.92a
+#define HFIR_RELOAD_CTRL_Msk (0x1UL << HFIR_RELOAD_CTRL_Pos)
+#define HFIR_RELOAD_CTRL HFIR_RELOAD_CTRL_Msk
/******************** Bit definition for HFNUM register ********************/
#define HFNUM_FRNUM_Pos (0U)
@@ -708,19 +1029,17 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define GAHBCFG_DMAEN_Pos (5U)
#define GAHBCFG_DMAEN_Msk (0x1UL << GAHBCFG_DMAEN_Pos) // 0x00000020
#define GAHBCFG_DMAEN GAHBCFG_DMAEN_Msk // DMA enable
-#define GAHBCFG_TXFELVL_Pos (7U)
-#define GAHBCFG_TXFELVL_Msk (0x1UL << GAHBCFG_TXFELVL_Pos) // 0x00000080
-#define GAHBCFG_TXFELVL GAHBCFG_TXFELVL_Msk // TxFIFO empty level
-#define GAHBCFG_PTXFELVL_Pos (8U)
-#define GAHBCFG_PTXFELVL_Msk (0x1UL << GAHBCFG_PTXFELVL_Pos) // 0x00000100
-#define GAHBCFG_PTXFELVL GAHBCFG_PTXFELVL_Msk // Periodic TxFIFO empty level
-
-#define GSNPSID_ID_MASK TU_GENMASK(31, 16)
+#define GAHBCFG_TX_FIFO_EPMTY_LVL_Pos (7U)
+#define GAHBCFG_TX_FIFO_EPMTY_LVL_Msk (0x1UL << GAHBCFG_TX_FIFO_EPMTY_LVL_Pos) // 0x00000080
+#define GAHBCFG_TX_FIFO_EPMTY_LVL GAHBCFG_TX_FIFO_EPMTY_LVL_Msk // TxFIFO empty level
+#define GAHBCFG_PTX_FIFO_EPMTY_LVL_Pos (8U)
+#define GAHBCFG_PTX_FIFO_EPMTY_LVL_Msk (0x1UL << GAHBCFG_PTX_FIFO_EPMTY_LVL_Pos) // 0x00000100
+#define GAHBCFG_PTX_FIFO_EPMTY_LVL GAHBCFG_PTX_FIFO_EPMTY_LVL_Msk // Periodic TxFIFO empty level
/******************** Bit definition for GUSBCFG register ********************/
#define GUSBCFG_TOCAL_Pos (0U)
#define GUSBCFG_TOCAL_Msk (0x7UL << GUSBCFG_TOCAL_Pos) // 0x00000007
-#define GUSBCFG_TOCAL GUSBCFG_TOCAL_Msk // FS timeout calibration
+#define GUSBCFG_TOCAL GUSBCFG_TOCAL_Msk // HS/FS timeout calibration
#define GUSBCFG_PHYIF16_Pos (3U)
#define GUSBCFG_PHYIF16_Msk (0x1UL << GUSBCFG_PHYIF16_Pos) // 0x00000008
#define GUSBCFG_PHYIF16 GUSBCFG_PHYIF16_Msk // PHY Interface (PHYIf)
@@ -804,8 +1123,8 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define GRSTCTL_TXFNUM_2 (0x04UL << GRSTCTL_TXFNUM_Pos) // 0x00000100
#define GRSTCTL_TXFNUM_3 (0x08UL << GRSTCTL_TXFNUM_Pos) // 0x00000200
#define GRSTCTL_TXFNUM_4 (0x10UL << GRSTCTL_TXFNUM_Pos) // 0x00000400
-#define GRSTCTL_CSFTRST_DONE_Pos (29)
-#define GRSTCTL_CSFTRST_DONE (1u << GRSTCTL_CSFTRST_DONE_Pos) // Reset Done, only available from v4.20a
+#define GRSTCTL_CSRST_DONE_Pos (29)
+#define GRSTCTL_CSRST_DONE (1u << GRSTCTL_CSRST_DONE_Pos) // Reset Done, only available from v4.20a
#define GRSTCTL_DMAREQ_Pos (30U)
#define GRSTCTL_DMAREQ_Msk (0x1UL << GRSTCTL_DMAREQ_Pos) // 0x40000000
#define GRSTCTL_DMAREQ GRSTCTL_DMAREQ_Msk // DMA request signal
@@ -926,9 +1245,9 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define GINTSTS_RXFLVL_Pos (4U)
#define GINTSTS_RXFLVL_Msk (0x1UL << GINTSTS_RXFLVL_Pos) // 0x00000010
#define GINTSTS_RXFLVL GINTSTS_RXFLVL_Msk // RxFIFO nonempty
-#define GINTSTS_NPTXFE_Pos (5U)
-#define GINTSTS_NPTXFE_Msk (0x1UL << GINTSTS_NPTXFE_Pos) // 0x00000020
-#define GINTSTS_NPTXFE GINTSTS_NPTXFE_Msk // Nonperiodic TxFIFO empty
+#define GINTSTS_NPTX_FIFO_EMPTY_Pos (5U)
+#define GINTSTS_NPTX_FIFO_EMPTY_Msk (0x1UL << GINTSTS_NPTX_FIFO_EMPTY_Pos) // 0x00000020
+#define GINTSTS_NPTX_FIFO_EMPTY GINTSTS_NPTX_FIFO_EMPTY_Msk // Nonperiodic TxFIFO empty
#define GINTSTS_GINAKEFF_Pos (6U)
#define GINTSTS_GINAKEFF_Msk (0x1UL << GINTSTS_GINAKEFF_Pos) // 0x00000040
#define GINTSTS_GINAKEFF GINTSTS_GINAKEFF_Msk // Global IN nonperiodic NAK effective
@@ -977,15 +1296,15 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define GINTSTS_HCINT_Pos (25U)
#define GINTSTS_HCINT_Msk (0x1UL << GINTSTS_HCINT_Pos) // 0x02000000
#define GINTSTS_HCINT GINTSTS_HCINT_Msk // Host channels interrupt
-#define GINTSTS_PTXFE_Pos (26U)
-#define GINTSTS_PTXFE_Msk (0x1UL << GINTSTS_PTXFE_Pos) // 0x04000000
-#define GINTSTS_PTXFE GINTSTS_PTXFE_Msk // Periodic TxFIFO empty
+#define GINTSTS_PTX_FIFO_EMPTY_Pos (26U)
+#define GINTSTS_PTX_FIFO_EMPTY_Msk (0x1UL << GINTSTS_PTX_FIFO_EMPTY_Pos) // 0x04000000
+#define GINTSTS_PTX_FIFO_EMPTY GINTSTS_PTX_FIFO_EMPTY_Msk // Periodic TxFIFO empty
#define GINTSTS_LPMINT_Pos (27U)
#define GINTSTS_LPMINT_Msk (0x1UL << GINTSTS_LPMINT_Pos) // 0x08000000
#define GINTSTS_LPMINT GINTSTS_LPMINT_Msk // LPM interrupt
-#define GINTSTS_CIDSCHG_Pos (28U)
-#define GINTSTS_CIDSCHG_Msk (0x1UL << GINTSTS_CIDSCHG_Pos) // 0x10000000
-#define GINTSTS_CIDSCHG GINTSTS_CIDSCHG_Msk // Connector ID status change
+#define GINTSTS_CONIDSTSCHNG_Pos (28U)
+#define GINTSTS_CONIDSTSCHNG_Msk (0x1UL << GINTSTS_CONIDSTSCHNG_Pos) // 0x10000000
+#define GINTSTS_CONIDSTSCHNG GINTSTS_CONIDSTSCHNG_Msk // Connector ID status change
#define GINTSTS_DISCINT_Pos (29U)
#define GINTSTS_DISCINT_Msk (0x1UL << GINTSTS_DISCINT_Pos) // 0x20000000
#define GINTSTS_DISCINT GINTSTS_DISCINT_Msk // Disconnect detected interrupt
@@ -1069,9 +1388,9 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define GINTMSK_LPMINTM_Pos (27U)
#define GINTMSK_LPMINTM_Msk (0x1UL << GINTMSK_LPMINTM_Pos) // 0x08000000
#define GINTMSK_LPMINTM GINTMSK_LPMINTM_Msk // LPM interrupt Mask
-#define GINTMSK_CIDSCHGM_Pos (28U)
-#define GINTMSK_CIDSCHGM_Msk (0x1UL << GINTMSK_CIDSCHGM_Pos) // 0x10000000
-#define GINTMSK_CIDSCHGM GINTMSK_CIDSCHGM_Msk // Connector ID status change mask
+#define GINTMSK_CONIDSTSCHNGM_Pos (28U)
+#define GINTMSK_CONIDSTSCHNGM_Msk (0x1UL << GINTMSK_CONIDSTSCHNGM_Pos) // 0x10000000
+#define GINTMSK_CONIDSTSCHNGM GINTMSK_CONIDSTSCHNGM_Msk // Connector ID status change mask
#define GINTMSK_DISCINT_Pos (29U)
#define GINTMSK_DISCINT_Msk (0x1UL << GINTMSK_DISCINT_Pos) // 0x20000000
#define GINTMSK_DISCINT GINTMSK_DISCINT_Msk // Disconnect detected interrupt mask
@@ -1109,17 +1428,6 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define GRXSTSP_PKTSTS_Msk (0xFUL << GRXSTSP_PKTSTS_Pos) // 0x001E0000
#define GRXSTSP_PKTSTS GRXSTSP_PKTSTS_Msk // OUT EP interrupt mask bits
-#define GRXSTS_PKTSTS_GLOBALOUTNAK 1
-#define GRXSTS_PKTSTS_OUTRX 2
-#define GRXSTS_PKTSTS_HCHIN 2
-#define GRXSTS_PKTSTS_OUTDONE 3
-#define GRXSTS_PKTSTS_HCHIN_XFER_COMP 3
-#define GRXSTS_PKTSTS_SETUPDONE 4
-#define GRXSTS_PKTSTS_DATATOGGLEERR 5
-#define GRXSTS_PKTSTS_SETUPRX 6
-#define GRXSTS_PKTSTS_HCHHALTED 7
-
-
/******************** Bit definition for DAINTMSK register ********************/
#define DAINTMSK_IEPM_Pos (0U)
#define DAINTMSK_IEPM_Msk (0xFFFFUL << DAINTMSK_IEPM_Pos) // 0x0000FFFF
@@ -1448,56 +1756,53 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define DIEPEACHMSK1_NAKM DIEPEACHMSK1_NAKM_Msk // NAK interrupt mask
/******************** Bit definition for HPRT register ********************/
-#define HPRT_PCSTS_Pos (0U)
-#define HPRT_PCSTS_Msk (0x1UL << HPRT_PCSTS_Pos) // 0x00000001
-#define HPRT_PCSTS HPRT_PCSTS_Msk // Port connect status
-#define HPRT_PCDET_Pos (1U)
-#define HPRT_PCDET_Msk (0x1UL << HPRT_PCDET_Pos) // 0x00000002
-#define HPRT_PCDET HPRT_PCDET_Msk // Port connect detected
-#define HPRT_PENA_Pos (2U)
-#define HPRT_PENA_Msk (0x1UL << HPRT_PENA_Pos) // 0x00000004
-#define HPRT_PENA HPRT_PENA_Msk // Port enable
-#define HPRT_PENCHNG_Pos (3U)
-#define HPRT_PENCHNG_Msk (0x1UL << HPRT_PENCHNG_Pos) // 0x00000008
-#define HPRT_PENCHNG HPRT_PENCHNG_Msk // Port enable/disable change
-#define HPRT_POCA_Pos (4U)
-#define HPRT_POCA_Msk (0x1UL << HPRT_POCA_Pos) // 0x00000010
-#define HPRT_POCA HPRT_POCA_Msk // Port overcurrent active
-#define HPRT_POCCHNG_Pos (5U)
-#define HPRT_POCCHNG_Msk (0x1UL << HPRT_POCCHNG_Pos) // 0x00000020
-#define HPRT_POCCHNG HPRT_POCCHNG_Msk // Port overcurrent change
-#define HPRT_PRES_Pos (6U)
-#define HPRT_PRES_Msk (0x1UL << HPRT_PRES_Pos) // 0x00000040
-#define HPRT_PRES HPRT_PRES_Msk // Port resume
-#define HPRT_PSUSP_Pos (7U)
-#define HPRT_PSUSP_Msk (0x1UL << HPRT_PSUSP_Pos) // 0x00000080
-#define HPRT_PSUSP HPRT_PSUSP_Msk // Port suspend
-#define HPRT_PRST_Pos (8U)
-#define HPRT_PRST_Msk (0x1UL << HPRT_PRST_Pos) // 0x00000100
-#define HPRT_PRST HPRT_PRST_Msk // Port reset
-
-#define HPRT_PLSTS_Pos (10U)
-#define HPRT_PLSTS_Msk (0x3UL << HPRT_PLSTS_Pos) // 0x00000C00
-#define HPRT_PLSTS HPRT_PLSTS_Msk // Port line status
-#define HPRT_PLSTS_0 (0x1UL << HPRT_PLSTS_Pos) // 0x00000400
-#define HPRT_PLSTS_1 (0x2UL << HPRT_PLSTS_Pos) // 0x00000800
-#define HPRT_PPWR_Pos (12U)
-#define HPRT_PPWR_Msk (0x1UL << HPRT_PPWR_Pos) // 0x00001000
-#define HPRT_PPWR HPRT_PPWR_Msk // Port power
-
-#define HPRT_PTCTL_Pos (13U)
-#define HPRT_PTCTL_Msk (0xFUL << HPRT_PTCTL_Pos) // 0x0001E000
-#define HPRT_PTCTL HPRT_PTCTL_Msk // Port test control
-#define HPRT_PTCTL_0 (0x1UL << HPRT_PTCTL_Pos) // 0x00002000
-#define HPRT_PTCTL_1 (0x2UL << HPRT_PTCTL_Pos) // 0x00004000
-#define HPRT_PTCTL_2 (0x4UL << HPRT_PTCTL_Pos) // 0x00008000
-#define HPRT_PTCTL_3 (0x8UL << HPRT_PTCTL_Pos) // 0x00010000
-
-#define HPRT_PSPD_Pos (17U)
-#define HPRT_PSPD_Msk (0x3UL << HPRT_PSPD_Pos) // 0x00060000
-#define HPRT_PSPD HPRT_PSPD_Msk // Port speed
-#define HPRT_PSPD_0 (0x1UL << HPRT_PSPD_Pos) // 0x00020000
-#define HPRT_PSPD_1 (0x2UL << HPRT_PSPD_Pos) // 0x00040000
+#define HPRT_CONN_STATUS_Pos (0U)
+#define HPRT_CONN_STATUS_Msk (0x1UL << HPRT_CONN_STATUS_Pos) // 0x00000001
+#define HPRT_CONN_STATUS HPRT_CONN_STATUS_Msk // Port connect status
+#define HPRT_CONN_DETECT_Pos (1U)
+#define HPRT_CONN_DETECT_Msk (0x1UL << HPRT_CONN_DETECT_Pos) // 0x00000002
+#define HPRT_CONN_DETECT HPRT_CONN_DETECT_Msk // Port connect detected
+#define HPRT_ENABLE_Pos (2U)
+#define HPRT_ENABLE_Msk (0x1UL << HPRT_ENABLE_Pos) // 0x00000004
+#define HPRT_ENABLE HPRT_ENABLE_Msk // Port enable
+#define HPRT_ENABLE_CHANGE_Pos (3U)
+#define HPRT_ENABLE_CHANGE_Msk (0x1UL << HPRT_ENABLE_CHANGE_Pos) // 0x00000008
+#define HPRT_ENABLE_CHANGE HPRT_ENABLE_CHANGE_Msk // Port enable/disable change
+#define HPRT_OVER_CURRENT_ACTIVE_Pos (4U)
+#define HPRT_OVER_CURRENT_ACTIVE_Msk (0x1UL << HPRT_OVER_CURRENT_ACTIVE_Pos) // 0x00000010
+#define HPRT_OVER_CURRENT_ACTIVE HPRT_OVER_CURRENT_ACTIVE_Msk // Port overcurrent active
+#define HPRT_OVER_CURRENT_CHANGE_Pos (5U)
+#define HPRT_OVER_CURRENT_CHANGE_Msk (0x1UL << HPRT_OVER_CURRENT_CHANGE_Pos) // 0x00000020
+#define HPRT_OVER_CURRENT_CHANGE HPRT_OVER_CURRENT_CHANGE_Msk // Port overcurrent change
+#define HPRT_RESUME_Pos (6U)
+#define HPRT_RESUME_Msk (0x1UL << HPRT_RESUME_Pos) // 0x00000040
+#define HPRT_RESUME HPRT_RESUME_Msk // Port resume
+#define HPRT_SUSPEND_Pos (7U)
+#define HPRT_SUSPEND_Msk (0x1UL << HPRT_SUSPEND_Pos) // 0x00000080
+#define HPRT_SUSPEND HPRT_SUSPEND_Msk // Port suspend
+#define HPRT_RESET_Pos (8U)
+#define HPRT_RESET_Msk (0x1UL << HPRT_RESET_Pos) // 0x00000100
+#define HPRT_RESET HPRT_RESET_Msk // Port reset
+#define HPRT_LINE_STATUS_Pos (10U)
+#define HPRT_LINE_STATUS_Msk (0x3UL << HPRT_LINE_STATUS_Pos) // 0x00000C00
+#define HPRT_LINE_STATUS HPRT_LINE_STATUS_Msk // Port line status
+#define HPRT_LINE_STATUS_0 (0x1UL << HPRT_LINE_STATUS_Pos) // 0x00000400
+#define HPRT_LINE_STATUS_1 (0x2UL << HPRT_LINE_STATUS_Pos) // 0x00000800
+#define HPRT_POWER_Pos (12U)
+#define HPRT_POWER_Msk (0x1UL << HPRT_POWER_Pos) // 0x00001000
+#define HPRT_POWER HPRT_POWER_Msk // Port power
+#define HPRT_TEST_CONTROL_Pos (13U)
+#define HPRT_TEST_CONTROL_Msk (0xFUL << HPRT_TEST_CONTROL_Pos) // 0x0001E000
+#define HPRT_TEST_CONTROL HPRT_TEST_CONTROL_Msk // Port test control
+#define HPRT_TEST_CONTROL_0 (0x1UL << HPRT_TEST_CONTROL_Pos) // 0x00002000
+#define HPRT_TEST_CONTROL_1 (0x2UL << HPRT_TEST_CONTROL_Pos) // 0x00004000
+#define HPRT_TEST_CONTROL_2 (0x4UL << HPRT_TEST_CONTROL_Pos) // 0x00008000
+#define HPRT_TEST_CONTROL_3 (0x8UL << HPRT_TEST_CONTROL_Pos) // 0x00010000
+#define HPRT_SPEED_Pos (17U)
+#define HPRT_SPEED_Msk (0x3UL << HPRT_SPEED_Pos) // 0x00060000
+#define HPRT_SPEED HPRT_SPEED_Msk // Port speed
+#define HPRT_SPEED_0 (0x1UL << HPRT_SPEED_Pos) // 0x00020000
+#define HPRT_SPEED_1 (0x2UL << HPRT_SPEED_Pos) // 0x00040000
/******************** Bit definition for DOEPEACHMSK1 register ********************/
#define DOEPEACHMSK1_XFRCM_Pos (0U)
@@ -1679,15 +1984,15 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define HCSPLT_SPLITEN HCSPLT_SPLITEN_Msk // Split enable
/******************** Bit definition for HCINT register ********************/
-#define HCINT_XFRC_Pos (0U)
-#define HCINT_XFRC_Msk (0x1UL << HCINT_XFRC_Pos) // 0x00000001
-#define HCINT_XFRC HCINT_XFRC_Msk // Transfer completed
-#define HCINT_CHH_Pos (1U)
-#define HCINT_CHH_Msk (0x1UL << HCINT_CHH_Pos) // 0x00000002
-#define HCINT_CHH HCINT_CHH_Msk // Channel halted
-#define HCINT_AHBERR_Pos (2U)
-#define HCINT_AHBERR_Msk (0x1UL << HCINT_AHBERR_Pos) // 0x00000004
-#define HCINT_AHBERR HCINT_AHBERR_Msk // AHB error
+#define HCINT_XFER_COMPLETE_Pos (0U)
+#define HCINT_XFER_COMPLETE_Msk (0x1UL << HCINT_XFER_COMPLETE_Pos) // 0x00000001
+#define HCINT_XFER_COMPLETE HCINT_XFER_COMPLETE_Msk // Transfer completed
+#define HCINT_HALTED_Pos (1U)
+#define HCINT_HALTED_Msk (0x1UL << HCINT_HALTED_Pos) // 0x00000002
+#define HCINT_HALTED HCINT_HALTED_Msk // Channel halted
+#define HCINT_AHB_ERR_Pos (2U)
+#define HCINT_AHB_ERR_Msk (0x1UL << HCINT_AHB_ERR_Pos) // 0x00000004
+#define HCINT_AHB_ERR HCINT_AHB_ERR_Msk // AHB error
#define HCINT_STALL_Pos (3U)
#define HCINT_STALL_Msk (0x1UL << HCINT_STALL_Pos) // 0x00000008
#define HCINT_STALL HCINT_STALL_Msk // STALL response received interrupt
@@ -1700,18 +2005,27 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define HCINT_NYET_Pos (6U)
#define HCINT_NYET_Msk (0x1UL << HCINT_NYET_Pos) // 0x00000040
#define HCINT_NYET HCINT_NYET_Msk // Response received interrupt
-#define HCINT_TXERR_Pos (7U)
-#define HCINT_TXERR_Msk (0x1UL << HCINT_TXERR_Pos) // 0x00000080
-#define HCINT_TXERR HCINT_TXERR_Msk // Transaction error
-#define HCINT_BBERR_Pos (8U)
-#define HCINT_BBERR_Msk (0x1UL << HCINT_BBERR_Pos) // 0x00000100
-#define HCINT_BBERR HCINT_BBERR_Msk // Babble error
-#define HCINT_FRMOR_Pos (9U)
-#define HCINT_FRMOR_Msk (0x1UL << HCINT_FRMOR_Pos) // 0x00000200
-#define HCINT_FRMOR HCINT_FRMOR_Msk // Frame overrun
-#define HCINT_DTERR_Pos (10U)
-#define HCINT_DTERR_Msk (0x1UL << HCINT_DTERR_Pos) // 0x00000400
-#define HCINT_DTERR HCINT_DTERR_Msk // Data toggle error
+#define HCINT_XACT_ERR_Pos (7U)
+#define HCINT_XACT_ERR_Msk (0x1UL << HCINT_XACT_ERR_Pos) // 0x00000080
+#define HCINT_XACT_ERR HCINT_XACT_ERR_Msk // Transaction error
+#define HCINT_BABBLE_ERR_Pos (8U)
+#define HCINT_BABBLE_ERR_Msk (0x1UL << HCINT_BABBLE_ERR_Pos) // 0x00000100
+#define HCINT_BABBLE_ERR HCINT_BABBLE_ERR_Msk // Babble error
+#define HCINT_FARME_OVERRUN_Pos (9U)
+#define HCINT_FARME_OVERRUN_Msk (0x1UL << HCINT_FARME_OVERRUN_Pos) // 0x00000200
+#define HCINT_FARME_OVERRUN HCINT_FARME_OVERRUN_Msk // Frame overrun
+#define HCINT_DATATOGGLE_ERR_Pos (10U)
+#define HCINT_DATATOGGLE_ERR_Msk (0x1UL << HCINT_DATATOGGLE_ERR_Pos) // 0x00000400
+#define HCINT_DATATOGGLE_ERR HCINT_DATATOGGLE_ERR_Msk // Data toggle error
+#define HCINT_BUFFER_NA_Pos (11U)
+#define HCINT_BUFFER_NA_Msk (0x1UL << HCINT_BUFFER_NA_Pos) // 0x00000800
+#define HCINT_BUFFER_NA HCINT_BUFFER_NA_Msk // Buffer not available interrupt
+#define HCINT_XCS_XACT_ERR_Pos (12U)
+#define HCINT_XCS_XACT_ERR_Msk (0x1UL << HCINT_XCS_XACT_ERR_Pos) // 0x00001000
+#define HCINT_XCS_XACT_ERR HCINT_XCS_XACT_ERR_Msk // Excessive transaction error
+#define HCINT_DESC_ROLLOVER_Pos (13U)
+#define HCINT_DESC_ROLLOVER_Msk (0x1UL << HCINT_DESC_ROLLOVER_Pos) // 0x00002000
+#define HCINT_DESC_ROLLOVER HCINT_DESC_ROLLOVER_Msk // Descriptor rollover
/******************** Bit definition for DIEPINT register ********************/
#define DIEPINT_XFRC_Pos (0U)
@@ -1754,41 +2068,6 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define DIEPINT_NAK_Msk (0x1UL << DIEPINT_NAK_Pos) // 0x00002000
#define DIEPINT_NAK DIEPINT_NAK_Msk // NAK interrupt
-/******************** Bit definition for HCINTMSK register ********************/
-#define HCINTMSK_XFRCM_Pos (0U)
-#define HCINTMSK_XFRCM_Msk (0x1UL << HCINTMSK_XFRCM_Pos) // 0x00000001
-#define HCINTMSK_XFRCM HCINTMSK_XFRCM_Msk // Transfer completed mask
-#define HCINTMSK_CHHM_Pos (1U)
-#define HCINTMSK_CHHM_Msk (0x1UL << HCINTMSK_CHHM_Pos) // 0x00000002
-#define HCINTMSK_CHHM HCINTMSK_CHHM_Msk // Channel halted mask
-#define HCINTMSK_AHBERR_Pos (2U)
-#define HCINTMSK_AHBERR_Msk (0x1UL << HCINTMSK_AHBERR_Pos) // 0x00000004
-#define HCINTMSK_AHBERR HCINTMSK_AHBERR_Msk // AHB error
-#define HCINTMSK_STALLM_Pos (3U)
-#define HCINTMSK_STALLM_Msk (0x1UL << HCINTMSK_STALLM_Pos) // 0x00000008
-#define HCINTMSK_STALLM HCINTMSK_STALLM_Msk // STALL response received interrupt mask
-#define HCINTMSK_NAKM_Pos (4U)
-#define HCINTMSK_NAKM_Msk (0x1UL << HCINTMSK_NAKM_Pos) // 0x00000010
-#define HCINTMSK_NAKM HCINTMSK_NAKM_Msk // NAK response received interrupt mask
-#define HCINTMSK_ACKM_Pos (5U)
-#define HCINTMSK_ACKM_Msk (0x1UL << HCINTMSK_ACKM_Pos) // 0x00000020
-#define HCINTMSK_ACKM HCINTMSK_ACKM_Msk // ACK response received/transmitted interrupt mask
-#define HCINTMSK_NYET_Pos (6U)
-#define HCINTMSK_NYET_Msk (0x1UL << HCINTMSK_NYET_Pos) // 0x00000040
-#define HCINTMSK_NYET HCINTMSK_NYET_Msk // response received interrupt mask
-#define HCINTMSK_TXERRM_Pos (7U)
-#define HCINTMSK_TXERRM_Msk (0x1UL << HCINTMSK_TXERRM_Pos) // 0x00000080
-#define HCINTMSK_TXERRM HCINTMSK_TXERRM_Msk // Transaction error mask
-#define HCINTMSK_BBERRM_Pos (8U)
-#define HCINTMSK_BBERRM_Msk (0x1UL << HCINTMSK_BBERRM_Pos) // 0x00000100
-#define HCINTMSK_BBERRM HCINTMSK_BBERRM_Msk // Babble error mask
-#define HCINTMSK_FRMORM_Pos (9U)
-#define HCINTMSK_FRMORM_Msk (0x1UL << HCINTMSK_FRMORM_Pos) // 0x00000200
-#define HCINTMSK_FRMORM HCINTMSK_FRMORM_Msk // Frame overrun mask
-#define HCINTMSK_DTERRM_Pos (10U)
-#define HCINTMSK_DTERRM_Msk (0x1UL << HCINTMSK_DTERRM_Pos) // 0x00000400
-#define HCINTMSK_DTERRM HCINTMSK_DTERRM_Msk // Data toggle error mask
-
/******************** Bit definition for DIEPTSIZ register ********************/
#define DIEPTSIZ_XFRSIZ_Pos (0U)
@@ -1810,11 +2089,9 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define HCTSIZ_DOPING_Pos (31U)
#define HCTSIZ_DOPING_Msk (0x1UL << HCTSIZ_DOPING_Pos) // 0x80000000
#define HCTSIZ_DOPING HCTSIZ_DOPING_Msk // Do PING
-#define HCTSIZ_DPID_Pos (29U)
-#define HCTSIZ_DPID_Msk (0x3UL << HCTSIZ_DPID_Pos) // 0x60000000
-#define HCTSIZ_DPID HCTSIZ_DPID_Msk // Data PID
-#define HCTSIZ_DPID_0 (0x1UL << HCTSIZ_DPID_Pos) // 0x20000000
-#define HCTSIZ_DPID_1 (0x2UL << HCTSIZ_DPID_Pos) // 0x40000000
+#define HCTSIZ_PID_Pos (29U)
+#define HCTSIZ_PID_Msk (0x3UL << HCTSIZ_PID_Pos) // 0x60000000
+#define HCTSIZ_PID HCTSIZ_PID_Msk // Data PID
/******************** Bit definition for DIEPDMA register ********************/
#define DIEPDMA_DMAADDR_Pos (0U)
@@ -1973,32 +2250,32 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define DOEPTSIZ_STUPCNT_1 (0x2UL << DOEPTSIZ_STUPCNT_Pos) // 0x40000000
/******************** Bit definition for PCGCTL register ********************/
-#define PCGCTL_IF_DEV_MODE TU_BIT(31)
-#define PCGCTL_P2HD_PRT_SPD_MASK (0x3ul << 29)
-#define PCGCTL_P2HD_PRT_SPD_SHIFT 29
-#define PCGCTL_P2HD_DEV_ENUM_SPD_MASK (0x3ul << 27)
-#define PCGCTL_P2HD_DEV_ENUM_SPD_SHIFT 27
-#define PCGCTL_MAC_DEV_ADDR_MASK (0x7ful << 20)
-#define PCGCTL_MAC_DEV_ADDR_SHIFT 20
-#define PCGCTL_MAX_TERMSEL TU_BIT(19)
-#define PCGCTL_MAX_XCVRSELECT_MASK (0x3ul << 17)
-#define PCGCTL_MAX_XCVRSELECT_SHIFT 17
-#define PCGCTL_PORT_POWER TU_BIT(16)
-#define PCGCTL_PRT_CLK_SEL_MASK (0x3ul << 14)
-#define PCGCTL_PRT_CLK_SEL_SHIFT 14
-#define PCGCTL_ESS_REG_RESTORED TU_BIT(13)
-#define PCGCTL_EXTND_HIBER_SWITCH TU_BIT(12)
-#define PCGCTL_EXTND_HIBER_PWRCLMP TU_BIT(11)
-#define PCGCTL_ENBL_EXTND_HIBER TU_BIT(10)
-#define PCGCTL_RESTOREMODE TU_BIT(9)
-#define PCGCTL_RESETAFTSUSP TU_BIT(8)
-#define PCGCTL_DEEP_SLEEP TU_BIT(7)
-#define PCGCTL_PHY_IN_SLEEP TU_BIT(6)
-#define PCGCTL_ENBL_SLEEP_GATING TU_BIT(5)
-#define PCGCTL_RSTPDWNMODULE TU_BIT(3)
-#define PCGCTL_PWRCLMP TU_BIT(2)
-#define PCGCTL_GATEHCLK TU_BIT(1)
-#define PCGCTL_STOPPCLK TU_BIT(0)
+#define PCGCCTL_IF_DEV_MODE TU_BIT(31)
+#define PCGCCTL_P2HD_PRT_SPD_MASK (0x3ul << 29)
+#define PCGCCTL_P2HD_PRT_SPD_SHIFT 29
+#define PCGCCTL_P2HD_DEV_ENUM_SPD_MASK (0x3ul << 27)
+#define PCGCCTL_P2HD_DEV_ENUM_SPD_SHIFT 27
+#define PCGCCTL_MAC_DEV_ADDR_MASK (0x7ful << 20)
+#define PCGCCTL_MAC_DEV_ADDR_SHIFT 20
+#define PCGCCTL_MAX_TERMSEL TU_BIT(19)
+#define PCGCCTL_MAX_XCVRSELECT_MASK (0x3ul << 17)
+#define PCGCCTL_MAX_XCVRSELECT_SHIFT 17
+#define PCGCCTL_PORT_POWER TU_BIT(16)
+#define PCGCCTL_PRT_CLK_SEL_MASK (0x3ul << 14)
+#define PCGCCTL_PRT_CLK_SEL_SHIFT 14
+#define PCGCCTL_ESS_REG_RESTORED TU_BIT(13)
+#define PCGCCTL_EXTND_HIBER_SWITCH TU_BIT(12)
+#define PCGCCTL_EXTND_HIBER_PWRCLMP TU_BIT(11)
+#define PCGCCTL_ENBL_EXTND_HIBER TU_BIT(10)
+#define PCGCCTL_RESTOREMODE TU_BIT(9)
+#define PCGCCTL_RESETAFTSUSP TU_BIT(8)
+#define PCGCCTL_DEEP_SLEEP TU_BIT(7)
+#define PCGCCTL_PHY_IN_SLEEP TU_BIT(6)
+#define PCGCCTL_ENBL_SLEEP_GATING TU_BIT(5)
+#define PCGCCTL_RSTPDWNMODULE TU_BIT(3)
+#define PCGCCTL_PWRCLMP TU_BIT(2)
+#define PCGCCTL_GATEHCLK TU_BIT(1)
+#define PCGCCTL_STOPPCLK TU_BIT(0)
#define PCGCTL1_TIMER (0x3ul << 1)
#define PCGCTL1_GATEEN TU_BIT(0)
diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c
new file mode 100644
index 000000000..ebbb6200a
--- /dev/null
+++ b/src/portable/synopsys/dwc2/hcd_dwc2.c
@@ -0,0 +1,1360 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if CFG_TUH_ENABLED && defined(TUP_USBIP_DWC2)
+
+#if !(CFG_TUH_DWC2_SLAVE_ENABLE || CFG_TUH_DWC2_DMA_ENABLE)
+#error DWC2 require either CFG_TUH_DWC2_SLAVE_ENABLE or CFG_TUH_DWC2_DMA_ENABLE to be enabled
+#endif
+
+// Debug level for DWC2
+#define DWC2_DEBUG 2
+
+#include "host/hcd.h"
+#include "dwc2_common.h"
+
+// Max number of endpoints application can open, can be larger than DWC2_CHANNEL_COUNT_MAX
+#ifndef CFG_TUH_DWC2_ENDPOINT_MAX
+#define CFG_TUH_DWC2_ENDPOINT_MAX 16
+#endif
+
+#define DWC2_CHANNEL_COUNT_MAX 16 // absolute max channel count
+#define DWC2_CHANNEL_COUNT(_dwc2) tu_min8((_dwc2)->ghwcfg2_bm.num_host_ch + 1, DWC2_CHANNEL_COUNT_MAX)
+
+TU_VERIFY_STATIC(CFG_TUH_DWC2_ENDPOINT_MAX <= 255, "currently only use 8-bit for index");
+
+enum {
+ HPRT_W1_MASK = HPRT_CONN_DETECT | HPRT_ENABLE | HPRT_ENABLE_CHANGE | HPRT_OVER_CURRENT_CHANGE | HPRT_SUSPEND
+};
+
+enum {
+ HCD_XFER_ERROR_MAX = 3
+};
+
+enum {
+ HCD_XFER_PERIOD_SPLIT_NYET_MAX = 3
+};
+
+//--------------------------------------------------------------------
+//
+//--------------------------------------------------------------------
+
+// Host driver struct for each opened endpoint
+typedef struct {
+ union {
+ uint32_t hcchar;
+ dwc2_channel_char_t hcchar_bm;
+ };
+ union {
+ uint32_t hcsplt;
+ dwc2_channel_split_t hcsplt_bm;
+ };
+
+ struct TU_ATTR_PACKED {
+ uint32_t uframe_interval : 18; // micro-frame interval
+ uint32_t speed : 2;
+ uint32_t next_pid : 2;
+ uint32_t do_ping : 1;
+ // uint32_t : 9;
+ };
+
+ uint32_t uframe_countdown; // micro-frame count down to transfer for periodic, only need 18-bit
+
+ uint8_t* buffer;
+ uint16_t buflen;
+} hcd_endpoint_t;
+
+// Additional info for each channel when it is active
+typedef struct {
+ volatile bool allocated;
+ uint8_t ep_id;
+ struct TU_ATTR_PACKED {
+ uint8_t err_count : 3;
+ uint8_t period_split_nyet_count : 3;
+ uint8_t halted_nyet : 1;
+ uint8_t halted_sof_schedule : 1;
+ };
+ uint8_t result;
+
+ uint16_t xferred_bytes; // bytes that accumulate transferred though USB bus for the whole hcd_edpt_xfer(), which can
+ // be composed of multiple channel_xfer_start() (retry with NAK/NYET)
+ uint16_t fifo_bytes; // bytes written/read from/to FIFO (may not be transferred on USB bus).
+} hcd_xfer_t;
+
+typedef struct {
+ hcd_xfer_t xfer[DWC2_CHANNEL_COUNT_MAX];
+ hcd_endpoint_t edpt[CFG_TUH_DWC2_ENDPOINT_MAX];
+} hcd_data_t;
+
+hcd_data_t _hcd_data;
+
+//--------------------------------------------------------------------
+//
+//--------------------------------------------------------------------
+TU_ATTR_ALWAYS_INLINE static inline tusb_speed_t hprt_speed_get(dwc2_regs_t* dwc2) {
+ tusb_speed_t speed;
+ switch(dwc2->hprt_bm.speed) {
+ case HPRT_SPEED_HIGH: speed = TUSB_SPEED_HIGH; break;
+ case HPRT_SPEED_FULL: speed = TUSB_SPEED_FULL; break;
+ case HPRT_SPEED_LOW : speed = TUSB_SPEED_LOW ; break;
+ default:
+ speed = TUSB_SPEED_INVALID;
+ TU_BREAKPOINT();
+ break;
+ }
+ return speed;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool dma_host_enabled(const dwc2_regs_t* dwc2) {
+ (void) dwc2;
+ // Internal DMA only
+ return CFG_TUH_DWC2_DMA_ENABLE && dwc2->ghwcfg2_bm.arch == GHWCFG2_ARCH_INTERNAL_DMA;
+}
+
+#if CFG_TUH_MEM_DCACHE_ENABLE
+bool hcd_dcache_clean(const void* addr, uint32_t data_size) {
+ TU_VERIFY(addr && data_size);
+ return dwc2_dcache_clean(addr, data_size);
+}
+
+bool hcd_dcache_invalidate(const void* addr, uint32_t data_size) {
+ TU_VERIFY(addr && data_size);
+ return dwc2_dcache_invalidate(addr, data_size);
+}
+
+bool hcd_dcache_clean_invalidate(const void* addr, uint32_t data_size) {
+ TU_VERIFY(addr && data_size);
+ return dwc2_dcache_clean_invalidate(addr, data_size);
+}
+#endif
+
+// Allocate a channel for new transfer
+TU_ATTR_ALWAYS_INLINE static inline uint8_t channel_alloc(dwc2_regs_t* dwc2) {
+ const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2);
+ for (uint8_t ch_id = 0; ch_id < max_channel; ch_id++) {
+ hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
+ if (!xfer->allocated) {
+ tu_memclr(xfer, sizeof(hcd_xfer_t));
+ xfer->allocated = true;
+ return ch_id;
+ }
+ }
+ return TUSB_INDEX_INVALID_8;
+}
+
+// Check if is periodic (interrupt/isochronous)
+TU_ATTR_ALWAYS_INLINE static inline bool edpt_is_periodic(uint8_t ep_type) {
+ return ep_type == HCCHAR_EPTYPE_INTERRUPT || ep_type == HCCHAR_EPTYPE_ISOCHRONOUS;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint8_t req_queue_avail(const dwc2_regs_t* dwc2, bool is_period) {
+ if (is_period) {
+ return dwc2->hptxsts_bm.req_queue_available;
+ } else {
+ return dwc2->hnptxsts_bm.req_queue_available;
+ }
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void channel_dealloc(dwc2_regs_t* dwc2, uint8_t ch_id) {
+ hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
+ xfer->allocated = false;
+ dwc2->haintmsk &= ~TU_BIT(ch_id);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool channel_disable(const dwc2_regs_t* dwc2, dwc2_channel_t* channel) {
+ // disable also require request queue
+ TU_ASSERT(req_queue_avail(dwc2, edpt_is_periodic(channel->hcchar_bm.ep_type)));
+ channel->hcintmsk |= HCINT_HALTED;
+ channel->hcchar |= HCCHAR_CHDIS | HCCHAR_CHENA; // must set both CHDIS and CHENA
+ return true;
+}
+
+// attempt to send IN token to receive data
+TU_ATTR_ALWAYS_INLINE static inline bool channel_send_in_token(const dwc2_regs_t* dwc2, dwc2_channel_t* channel) {
+ TU_ASSERT(req_queue_avail(dwc2, edpt_is_periodic(channel->hcchar_bm.ep_type)));
+ channel->hcchar |= HCCHAR_CHENA;
+ return true;
+}
+
+// Find currently enabled channel. Note: EP0 is bidirectional
+TU_ATTR_ALWAYS_INLINE static inline uint8_t channel_find_enabled(dwc2_regs_t* dwc2, uint8_t dev_addr, uint8_t ep_num, uint8_t ep_dir) {
+ const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2);
+ for (uint8_t ch_id = 0; ch_id < max_channel; ch_id++) {
+ if (_hcd_data.xfer[ch_id].allocated) {
+ const dwc2_channel_char_t hcchar_bm = dwc2->channel[ch_id].hcchar_bm;
+ if (hcchar_bm.dev_addr == dev_addr && hcchar_bm.ep_num == ep_num && (ep_num == 0 || hcchar_bm.ep_dir == ep_dir)) {
+ return ch_id;
+ }
+ }
+ }
+ return TUSB_INDEX_INVALID_8;
+}
+
+
+// Allocate a new endpoint
+TU_ATTR_ALWAYS_INLINE static inline uint8_t edpt_alloc(void) {
+ for (uint32_t i = 0; i < CFG_TUH_DWC2_ENDPOINT_MAX; i++) {
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[i];
+ if (edpt->hcchar_bm.enable == 0) {
+ tu_memclr(edpt, sizeof(hcd_endpoint_t));
+ edpt->hcchar_bm.enable = 1;
+ return i;
+ }
+ }
+ return TUSB_INDEX_INVALID_8;
+}
+
+// Find a endpoint that is opened previously with hcd_edpt_open()
+// Note: EP0 is bidirectional
+TU_ATTR_ALWAYS_INLINE static inline uint8_t edpt_find_opened(uint8_t dev_addr, uint8_t ep_num, uint8_t ep_dir) {
+ for (uint8_t i = 0; i < (uint8_t)CFG_TUH_DWC2_ENDPOINT_MAX; i++) {
+ const dwc2_channel_char_t* hcchar_bm = &_hcd_data.edpt[i].hcchar_bm;
+ if (hcchar_bm->enable && hcchar_bm->dev_addr == dev_addr &&
+ hcchar_bm->ep_num == ep_num && (ep_num == 0 || hcchar_bm->ep_dir == ep_dir)) {
+ return i;
+ }
+ }
+ return TUSB_INDEX_INVALID_8;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint16_t cal_packet_count(uint16_t len, uint16_t ep_size) {
+ if (len == 0) {
+ return 1;
+ } else {
+ return tu_div_ceil(len, ep_size);
+ }
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint8_t cal_next_pid(uint8_t pid, uint8_t packet_count) {
+ if (packet_count & 0x01) {
+ return pid ^ 0x02; // toggle DATA0 and DATA1
+ } else {
+ return pid;
+ }
+}
+
+//--------------------------------------------------------------------
+//
+//--------------------------------------------------------------------
+
+/* USB Data FIFO Layout
+
+ The FIFO is split up into
+ - EPInfo: for storing DMA metadata (check dcd_dwc2.c for more details)
+ - 1 RX FIFO: for receiving data
+ - 1 TX FIFO for non-periodic (NPTX)
+ - 1 TX FIFO for periodic (PTX)
+
+ We allocated TX FIFO from top to bottom (using top pointer), this to allow the RX FIFO to grow dynamically which is
+ possible since the free space is located between the RX and TX FIFOs.
+
+ ----------------- ep_fifo_size
+ | HCDMAn |
+ |--------------|-- gdfifocfg.EPINFOBASE (max is ghwcfg3.dfifo_depth)
+ | Non-Periodic |
+ | TX FIFO |
+ |--------------|--- GNPTXFSIZ.addr (fixed size)
+ | Periodic |
+ | TX FIFO |
+ |--------------|--- HPTXFSIZ.addr (expandable downward)
+ | FREE |
+ | |
+ |--------------|-- GRXFSIZ (expandable upward)
+ | RX FIFO |
+ ---------------- 0
+*/
+
+/* Programming Guide 2.1.2 FIFO RAM allocation
+ * RX
+ * - Largest-EPsize/4 + 2 (status info). recommended x2 if high bandwidth or multiple ISO are used.
+ * - 2 for transfer complete and channel halted status
+ * - 1 for each Control/Bulk out endpoint to Handle NAK/NYET (i.e max is number of host channel)
+ *
+ * TX non-periodic (NPTX)
+ * - At least largest-EPsize/4, recommended x2
+ *
+ * TX periodic (PTX)
+ * - At least largest-EPsize*MulCount/4 (MulCount up to 3 for high-bandwidth ISO/interrupt)
+*/
+static void dfifo_host_init(uint8_t rhport) {
+ const dwc2_controller_t* dwc2_controller = &_dwc2_controller[rhport];
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+
+ // Scatter/Gather DMA mode is not yet supported. Buffer DMA only need 1 words per channel
+ const bool is_dma = dma_host_enabled(dwc2);
+ uint16_t dfifo_top = dwc2_controller->ep_fifo_size/4;
+ if (is_dma) {
+ dfifo_top -= dwc2->ghwcfg2_bm.num_host_ch;
+ }
+
+ // fixed allocation for now, improve later:
+ // - ptx_largest is limited to 256 for FS since most FS core only has 1024 bytes total
+ bool is_highspeed = dwc2_core_is_highspeed(dwc2, TUSB_ROLE_HOST);
+ uint32_t nptx_largest = is_highspeed ? TUSB_EPSIZE_BULK_HS/4 : TUSB_EPSIZE_BULK_FS/4;
+ uint32_t ptx_largest = is_highspeed ? TUSB_EPSIZE_ISO_HS_MAX/4 : 256/4;
+
+ uint16_t nptxfsiz = 2 * nptx_largest;
+ uint16_t rxfsiz = 2 * (ptx_largest + 2) + dwc2->ghwcfg2_bm.num_host_ch;
+ TU_ASSERT(dfifo_top >= (nptxfsiz + rxfsiz),);
+ uint16_t ptxfsiz = dfifo_top - (nptxfsiz + rxfsiz);
+
+ dwc2->gdfifocfg = (dfifo_top << GDFIFOCFG_EPINFOBASE_SHIFT) | dfifo_top;
+
+ dfifo_top -= rxfsiz;
+ dwc2->grxfsiz = rxfsiz;
+
+ dfifo_top -= nptxfsiz;
+ dwc2->gnptxfsiz = tu_u32_from_u16(nptxfsiz, dfifo_top);
+
+ dfifo_top -= ptxfsiz;
+ dwc2->hptxfsiz = tu_u32_from_u16(ptxfsiz, dfifo_top);
+}
+
+//--------------------------------------------------------------------+
+// Controller API
+//--------------------------------------------------------------------+
+
+// 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;
+
+ return true;
+}
+
+// Initialize controller to host mode
+bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+
+ tu_memclr(&_hcd_data, sizeof(_hcd_data));
+
+ // Core Initialization
+ const bool is_highspeed = dwc2_core_is_highspeed(dwc2, TUSB_ROLE_HOST);
+ const bool is_dma = dma_host_enabled(dwc2);
+ TU_ASSERT(dwc2_core_init(rhport, is_highspeed, is_dma));
+
+ //------------- 3.1 Host Initialization -------------//
+
+ // work at max supported speed
+ dwc2->hcfg &= ~HCFG_FSLS_ONLY;
+
+ // Enable HFIR reload
+ if (dwc2->gsnpsid >= DWC2_CORE_REV_2_92a) {
+ dwc2->hfir |= HFIR_RELOAD_CTRL;
+ }
+
+ // force host mode and wait for mode switch
+ dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_FDMOD) | GUSBCFG_FHMOD;
+ while ((dwc2->gintsts & GINTSTS_CMOD) != GINTSTS_CMODE_HOST) {}
+
+ // configure fixed-allocated fifo scheme
+ dfifo_host_init(rhport);
+
+ dwc2->hprt = HPRT_W1_MASK; // clear all write-1-clear bits
+ dwc2->hprt = HPRT_POWER; // turn on VBUS
+
+ // Enable required interrupts
+ dwc2->gintmsk |= GINTSTS_OTGINT | GINTSTS_CONIDSTSCHNG | GINTSTS_HPRTINT | GINTSTS_HCINT;
+
+ // NPTX can hold at least 2 packet, change interrupt level to half-empty
+ uint32_t gahbcfg = dwc2->gahbcfg & ~GAHBCFG_TX_FIFO_EPMTY_LVL;
+ gahbcfg |= GAHBCFG_GINT; // Enable global interrupt
+ dwc2->gahbcfg = gahbcfg;
+
+ return true;
+}
+
+// Enable USB interrupt
+void hcd_int_enable (uint8_t rhport) {
+ dwc2_int_set(rhport, TUSB_ROLE_HOST, true);
+}
+
+// Disable USB interrupt
+void hcd_int_disable(uint8_t rhport) {
+ dwc2_int_set(rhport, TUSB_ROLE_HOST, false);
+}
+
+// Get frame number (1ms)
+uint32_t hcd_frame_number(uint8_t rhport) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ return dwc2->hfnum & HFNUM_FRNUM_Msk;
+}
+
+//--------------------------------------------------------------------+
+// Port API
+//--------------------------------------------------------------------+
+
+// Get the current connect status of roothub port
+bool hcd_port_connect_status(uint8_t rhport) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ return dwc2->hprt & HPRT_CONN_STATUS;
+}
+
+// Reset USB bus on the port. Return immediately, bus reset sequence may not be complete.
+// Some port would require hcd_port_reset_end() to be invoked after 10ms to complete the reset sequence.
+void hcd_port_reset(uint8_t rhport) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ uint32_t hprt = dwc2->hprt & ~HPRT_W1_MASK;
+ hprt |= HPRT_RESET;
+ dwc2->hprt = hprt;
+}
+
+// Complete bus reset sequence, may be required by some controllers
+void hcd_port_reset_end(uint8_t rhport) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ uint32_t hprt = dwc2->hprt & ~HPRT_W1_MASK; // skip w1c bits
+ hprt &= ~HPRT_RESET;
+ dwc2->hprt = hprt;
+}
+
+// Get port link speed
+tusb_speed_t hcd_port_speed_get(uint8_t rhport) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ const tusb_speed_t speed = hprt_speed_get(dwc2);
+ return speed;
+}
+
+// HCD closes all opened endpoints belong to this device
+void hcd_device_close(uint8_t rhport, uint8_t dev_addr) {
+ (void) rhport;
+ for (uint8_t i = 0; i < (uint8_t) CFG_TUH_DWC2_ENDPOINT_MAX; i++) {
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[i];
+ if (edpt->hcchar_bm.enable && edpt->hcchar_bm.dev_addr == dev_addr) {
+ tu_memclr(edpt, sizeof(hcd_endpoint_t));
+ }
+ }
+}
+
+//--------------------------------------------------------------------+
+// Endpoints API
+//--------------------------------------------------------------------+
+
+// Open an endpoint
+bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t* desc_ep) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ const tusb_speed_t rh_speed = hprt_speed_get(dwc2);
+
+ hcd_devtree_info_t devtree_info;
+ hcd_devtree_get_info(dev_addr, &devtree_info);
+
+ // find a free endpoint
+ const uint8_t ep_id = edpt_alloc();
+ TU_ASSERT(ep_id < CFG_TUH_DWC2_ENDPOINT_MAX);
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id];
+
+ dwc2_channel_char_t* hcchar_bm = &edpt->hcchar_bm;
+ hcchar_bm->ep_size = tu_edpt_packet_size(desc_ep);
+ hcchar_bm->ep_num = tu_edpt_number(desc_ep->bEndpointAddress);
+ hcchar_bm->ep_dir = tu_edpt_dir(desc_ep->bEndpointAddress);
+ hcchar_bm->low_speed_dev = (devtree_info.speed == TUSB_SPEED_LOW) ? 1 : 0;
+ hcchar_bm->ep_type = desc_ep->bmAttributes.xfer; // ep_type matches TUSB_XFER_*
+ hcchar_bm->err_multi_count = 0;
+ hcchar_bm->dev_addr = dev_addr;
+ hcchar_bm->odd_frame = 0;
+ hcchar_bm->disable = 0;
+ hcchar_bm->enable = 1;
+
+ dwc2_channel_split_t* hcsplt_bm = &edpt->hcsplt_bm;
+ hcsplt_bm->hub_port = devtree_info.hub_port;
+ hcsplt_bm->hub_addr = devtree_info.hub_addr;
+ hcsplt_bm->xact_pos = 0;
+ hcsplt_bm->split_compl = 0;
+ hcsplt_bm->split_en = (rh_speed == TUSB_SPEED_HIGH && devtree_info.speed != TUSB_SPEED_HIGH) ? 1 : 0;
+
+ edpt->speed = devtree_info.speed;
+ edpt->next_pid = HCTSIZ_PID_DATA0;
+ if (desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) {
+ edpt->uframe_interval = 1 << (desc_ep->bInterval - 1);
+ if (devtree_info.speed == TUSB_SPEED_FULL) {
+ edpt->uframe_interval <<= 3;
+ }
+ } else if (desc_ep->bmAttributes.xfer == TUSB_XFER_INTERRUPT) {
+ if (devtree_info.speed == TUSB_SPEED_HIGH) {
+ edpt->uframe_interval = 1 << (desc_ep->bInterval - 1);
+ } else {
+ edpt->uframe_interval = desc_ep->bInterval << 3;
+ }
+ }
+
+ return true;
+}
+
+// clean up channel after part of transfer is done but the whole urb is not complete
+static void channel_xfer_out_wrapup(dwc2_regs_t* dwc2, uint8_t ch_id) {
+ hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
+ dwc2_channel_t* channel = &dwc2->channel[ch_id];
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
+
+ edpt->next_pid = channel->hctsiz_bm.pid; // save PID
+
+ /* Since hctsiz.xfersize field reflects the number of bytes transferred via the AHB, not the USB)
+ * For IN: we can use hctsiz.xfersize as remaining bytes.
+ * For OUT: Must use the hctsiz.pktcnt field to determine how much data has been transferred. This field reflects the
+ * number of packets that have been transferred via the USB. This is always an integral number of packets if the
+ * transfer was halted before its normal completion.
+ */
+ const uint16_t remain_packets = channel->hctsiz_bm.packet_count;
+ const uint16_t total_packets = cal_packet_count(edpt->buflen, channel->hcchar_bm.ep_size);
+ const uint16_t actual_bytes = (total_packets - remain_packets) * channel->hcchar_bm.ep_size;
+
+ xfer->fifo_bytes = 0;
+ xfer->xferred_bytes += actual_bytes;
+ edpt->buffer += actual_bytes;
+ edpt->buflen -= actual_bytes;
+}
+
+static bool channel_xfer_start(dwc2_regs_t* dwc2, uint8_t ch_id) {
+ hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
+ dwc2_channel_char_t* hcchar_bm = &edpt->hcchar_bm;
+ dwc2_channel_t* channel = &dwc2->channel[ch_id];
+ bool const is_period = edpt_is_periodic(hcchar_bm->ep_type);
+
+ // clear previous state
+ xfer->fifo_bytes = 0;
+
+ // hchar: restore but don't enable yet
+ if (is_period) {
+ hcchar_bm->odd_frame = 1 - (dwc2->hfnum & 1); // transfer on next frame
+ }
+ channel->hcchar = (edpt->hcchar & ~HCCHAR_CHENA);
+
+ // hctsiz: zero length packet still count as 1
+ const uint16_t packet_count = cal_packet_count(edpt->buflen, hcchar_bm->ep_size);
+ uint32_t hctsiz = (edpt->next_pid << HCTSIZ_PID_Pos) | (packet_count << HCTSIZ_PKTCNT_Pos) | edpt->buflen;
+ if (edpt->do_ping && edpt->speed == TUSB_SPEED_HIGH &&
+ edpt->next_pid != HCTSIZ_PID_SETUP && hcchar_bm->ep_dir == TUSB_DIR_OUT) {
+ hctsiz |= HCTSIZ_DOPING;
+ }
+ channel->hctsiz = hctsiz;
+ edpt->do_ping = 0;
+
+ // pre-calculate next PID based on packet count, adjusted in transfer complete interrupt if short packet
+ if (hcchar_bm->ep_num == 0) {
+ edpt->next_pid = HCTSIZ_PID_DATA1; // control data and status stage always start with DATA1
+ } else {
+ edpt->next_pid = cal_next_pid(edpt->next_pid, packet_count);
+ }
+
+ channel->hcsplt = edpt->hcsplt;
+ channel->hcint = 0xFFFFFFFFU; // clear all channel interrupts
+
+ if (dma_host_enabled(dwc2)) {
+ uint32_t hcintmsk = HCINT_HALTED;
+ channel->hcintmsk = hcintmsk;
+ dwc2->haintmsk |= TU_BIT(ch_id);
+
+ channel->hcdma = (uint32_t) edpt->buffer;
+
+ if (hcchar_bm->ep_dir == TUSB_DIR_IN) {
+ channel_send_in_token(dwc2, channel);
+ } else {
+ hcd_dcache_clean(edpt->buffer, edpt->buflen);
+ channel->hcchar |= HCCHAR_CHENA;
+ }
+ } else {
+ uint32_t hcintmsk = HCINT_NAK | HCINT_XACT_ERR | HCINT_STALL | HCINT_XFER_COMPLETE | HCINT_DATATOGGLE_ERR;
+ if (hcchar_bm->ep_dir == TUSB_DIR_IN) {
+ hcintmsk |= HCINT_BABBLE_ERR | HCINT_DATATOGGLE_ERR | HCINT_ACK;
+ } else {
+ hcintmsk |= HCINT_NYET;
+ if (edpt->hcsplt_bm.split_en) {
+ hcintmsk |= HCINT_ACK;
+ }
+ }
+ channel->hcintmsk = hcintmsk;
+ dwc2->haintmsk |= TU_BIT(ch_id);
+
+ // enable channel for slave mode:
+ // - OUT: it will enable corresponding FIFO channel
+ // - IN : it will write an IN request to the Non-periodic Request Queue, this will have dwc2 trying to send
+ // IN Token. If we got NAK, we have to re-enable the channel again in the interrupt. Due to the way usbh stack only
+ // call hcd_edpt_xfer() once, we will need to manage de-allocate/re-allocate IN channel dynamically.
+ if (hcchar_bm->ep_dir == TUSB_DIR_IN) {
+ channel_send_in_token(dwc2, channel);
+ } else {
+ channel->hcchar |= HCCHAR_CHENA;
+ if (edpt->buflen > 0) {
+ // To prevent conflict with other channel, we will enable periodic/non-periodic FIFO empty interrupt accordingly
+ // And write packet in the interrupt handler
+ dwc2->gintmsk |= (is_period ? GINTSTS_PTX_FIFO_EMPTY : GINTSTS_NPTX_FIFO_EMPTY);
+ }
+ }
+ }
+
+ return true;
+}
+
+// kick-off transfer with an endpoint
+static bool edpt_xfer_kickoff(dwc2_regs_t* dwc2, uint8_t ep_id) {
+ uint8_t ch_id = channel_alloc(dwc2);
+ TU_ASSERT(ch_id < 16); // all channel are in used
+ hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
+ xfer->ep_id = ep_id;
+ xfer->result = XFER_RESULT_INVALID;
+
+ return channel_xfer_start(dwc2, ch_id);
+}
+
+// Submit a transfer, when complete hcd_event_xfer_complete() must be invoked
+bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ const uint8_t ep_num = tu_edpt_number(ep_addr);
+ const uint8_t ep_dir = tu_edpt_dir(ep_addr);
+
+ uint8_t ep_id = edpt_find_opened(dev_addr, ep_num, ep_dir);
+ TU_ASSERT(ep_id < CFG_TUH_DWC2_ENDPOINT_MAX);
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id];
+
+ edpt->buffer = buffer;
+ edpt->buflen = buflen;
+
+ if (ep_num == 0) {
+ // update ep_dir since control endpoint can switch direction
+ edpt->hcchar_bm.ep_dir = ep_dir;
+ }
+
+ return edpt_xfer_kickoff(dwc2, ep_id);
+}
+
+// Abort a queued transfer. Note: it can only abort transfer that has not been started
+// Return true if a queued transfer is aborted, false if there is no transfer to abort
+bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ const uint8_t ep_num = tu_edpt_number(ep_addr);
+ const uint8_t ep_dir = tu_edpt_dir(ep_addr);
+ const uint8_t ep_id = edpt_find_opened(dev_addr, ep_num, ep_dir);
+ TU_VERIFY(ep_id < CFG_TUH_DWC2_ENDPOINT_MAX);
+
+ // hcd_int_disable(rhport);
+
+ // Find enabled channeled and disable it, channel will be de-allocated in the interrupt handler
+ const uint8_t ch_id = channel_find_enabled(dwc2, dev_addr, ep_num, ep_dir);
+ if (ch_id < 16) {
+ dwc2_channel_t* channel = &dwc2->channel[ch_id];
+ channel_disable(dwc2, channel);
+ }
+
+ // hcd_int_enable(rhport);
+
+ return true;
+}
+
+// Submit a special transfer to send 8-byte Setup Packet, when complete hcd_event_xfer_complete() must be invoked
+bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, const uint8_t setup_packet[8]) {
+ uint8_t ep_id = edpt_find_opened(dev_addr, 0, TUSB_DIR_OUT);
+ TU_ASSERT(ep_id < CFG_TUH_DWC2_ENDPOINT_MAX); // no opened endpoint
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id];
+ edpt->next_pid = HCTSIZ_PID_SETUP;
+
+ return hcd_edpt_xfer(rhport, dev_addr, 0, (uint8_t*)(uintptr_t) setup_packet, 8);
+}
+
+// clear stall, data toggle is also reset to DATA0
+bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
+ (void) rhport;
+ const uint8_t ep_num = tu_edpt_number(ep_addr);
+ const uint8_t ep_dir = tu_edpt_dir(ep_addr);
+ const uint8_t ep_id = edpt_find_opened(dev_addr, ep_num, ep_dir);
+ TU_VERIFY(ep_id < CFG_TUH_DWC2_ENDPOINT_MAX);
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id];
+
+ edpt->next_pid = HCTSIZ_PID_DATA0;
+
+ return true;
+}
+
+//--------------------------------------------------------------------
+// HCD Event Handler
+//--------------------------------------------------------------------
+static void channel_xfer_in_retry(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hcint) {
+ hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
+ dwc2_channel_t* channel = &dwc2->channel[ch_id];
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
+
+ if (edpt_is_periodic(channel->hcchar_bm.ep_type)){
+ // retry immediately for periodic split NYET if we haven't reach max retry
+ if (channel->hcsplt_bm.split_en && channel->hcsplt_bm.split_compl && (hcint & HCINT_NYET || xfer->halted_nyet)) {
+ xfer->period_split_nyet_count++;
+ xfer->halted_nyet = 0;
+ if (xfer->period_split_nyet_count < HCD_XFER_PERIOD_SPLIT_NYET_MAX) {
+ channel->hcchar_bm.odd_frame = 1 - (dwc2->hfnum & 1); // transfer on next frame
+ channel_send_in_token(dwc2, channel);
+ return;
+ } else {
+ // too many NYET, de-allocate channel with below code
+ xfer->period_split_nyet_count = 0;
+ }
+ }
+
+ // for periodic, de-allocate channel, enable SOF set frame counter for later transfer
+ edpt->next_pid = channel->hctsiz_bm.pid; // save PID
+ edpt->uframe_countdown = edpt->uframe_interval;
+ dwc2->gintmsk |= GINTSTS_SOF;
+
+ if (hcint & HCINT_HALTED) {
+ // already halted, de-allocate channel (called from DMA isr)
+ channel_dealloc(dwc2, ch_id);
+ } else {
+ // disable channel first if not halted (called slave isr)
+ xfer->halted_sof_schedule = 1;
+ channel_disable(dwc2, channel);
+ }
+ } else {
+ // for control/bulk: retry immediately
+ channel_send_in_token(dwc2, channel);
+ }
+}
+
+#if CFG_TUSB_DEBUG
+TU_ATTR_ALWAYS_INLINE static inline void print_hcint(uint32_t hcint) {
+ const char* str[] = {
+ "XFRC", "HALTED", "AHBERR", "STALL",
+ "NAK", "ACK", "NYET", "XERR",
+ "BBLERR", "FRMOR", "DTERR", "BNA",
+ "XCSERR", "DESC_LST"
+ };
+
+ for(uint32_t i=0; i<14; i++) {
+ if (hcint & TU_BIT(i)) {
+ TU_LOG1("%s ", str[i]);
+ }
+ }
+ TU_LOG1("\r\n");
+}
+#endif
+
+#if CFG_TUH_DWC2_SLAVE_ENABLE
+static void handle_rxflvl_irq(uint8_t rhport) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+
+ // Pop control word off FIFO
+ const dwc2_grxstsp_t grxstsp_bm = dwc2->grxstsp_bm;
+ const uint8_t ch_id = grxstsp_bm.ep_ch_num;
+
+ switch (grxstsp_bm.packet_status) {
+ case GRXSTS_PKTSTS_RX_DATA: {
+ // In packet received, pop this entry --> ACK interrupt
+ const uint16_t byte_count = grxstsp_bm.byte_count;
+ hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
+ TU_ASSERT(xfer->ep_id < CFG_TUH_DWC2_ENDPOINT_MAX,);
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
+
+ if (byte_count) {
+ dfifo_read_packet(dwc2, edpt->buffer + xfer->xferred_bytes, byte_count);
+ xfer->xferred_bytes += byte_count;
+ xfer->fifo_bytes = byte_count;
+ }
+ break;
+ }
+
+ case GRXSTS_PKTSTS_RX_COMPLETE:
+ // In transfer complete: After this entry is popped from the rx FIFO, dwc2 asserts a Transfer Completed
+ // interrupt --> handle_channel_irq()
+ break;
+
+ case GRXSTS_PKTSTS_HOST_DATATOGGLE_ERR:
+ TU_ASSERT(0, ); // maybe try to change DToggle
+ break;
+
+ case GRXSTS_PKTSTS_HOST_CHANNEL_HALTED:
+ // triggered when channel.hcchar_bm.disable is set
+ // TODO handle later
+ break;
+
+ default: break; // ignore other status
+ }
+}
+
+// return true if there is still pending data and need more ISR
+static bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) {
+ // Use period txsts for both p/np to get request queue space available (1-bit difference, it is small enough)
+ volatile dwc2_hptxsts_t* txsts_bm = (volatile dwc2_hptxsts_t*) (is_periodic ? &dwc2->hptxsts : &dwc2->hnptxsts);
+
+ const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2);
+ for (uint8_t ch_id = 0; ch_id < max_channel; ch_id++) {
+ dwc2_channel_t* channel = &dwc2->channel[ch_id];
+ // skip writing to FIFO if channel is expecting halted.
+ if (!(channel->hcintmsk & HCINT_HALTED) && (channel->hcchar_bm.ep_dir == TUSB_DIR_OUT)) {
+ hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
+ TU_ASSERT(xfer->ep_id < CFG_TUH_DWC2_ENDPOINT_MAX);
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
+
+ const uint16_t remain_packets = channel->hctsiz_bm.packet_count;
+ for (uint16_t i = 0; i < remain_packets; i++) {
+ const uint16_t remain_bytes = edpt->buflen - xfer->fifo_bytes;
+ const uint16_t xact_bytes = tu_min16(remain_bytes, channel->hcchar_bm.ep_size);
+
+ // skip if there is not enough space in FIFO and RequestQueue.
+ // Packet's last word written to FIFO will trigger a request queue
+ if ((xact_bytes > (txsts_bm->fifo_available << 2)) || (txsts_bm->req_queue_available == 0)) {
+ return true;
+ }
+
+ dfifo_write_packet(dwc2, ch_id, edpt->buffer + xfer->fifo_bytes, xact_bytes);
+ xfer->fifo_bytes += xact_bytes;
+ }
+ }
+ }
+
+ return false; // no channel has pending data
+}
+
+static bool handle_channel_in_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hcint) {
+ hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
+ dwc2_channel_t* channel = &dwc2->channel[ch_id];
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
+ bool is_done = false;
+
+ // if (channel->hcsplt_bm.split_en) {
+ // if (edpt->hcchar_bm.ep_num == 1) {
+ // TU_LOG1("Frame %u, ch %u: ep %u, hcint 0x%04lX ", dwc2->hfnum_bm.num, ch_id, channel->hcchar_bm.ep_num, hcint);
+ // print_hcint(hcint);
+ // }
+
+ if (hcint & HCINT_XFER_COMPLETE) {
+ if (edpt->hcchar_bm.ep_num != 0) {
+ edpt->next_pid = channel->hctsiz_bm.pid; // save pid (already toggled)
+ }
+
+ const uint16_t remain_packets = channel->hctsiz_bm.packet_count;
+ if (channel->hcsplt_bm.split_en && remain_packets && xfer->fifo_bytes == edpt->hcchar_bm.ep_size) {
+ // Split can only complete 1 transaction (up to 1 packet) at a time, schedule more
+ channel->hcsplt_bm.split_compl = 0;
+ } else {
+ xfer->result = XFER_RESULT_SUCCESS;
+ }
+
+ channel_disable(dwc2, channel);
+ } else if (hcint & (HCINT_XACT_ERR | HCINT_BABBLE_ERR | HCINT_STALL)) {
+ if (hcint & HCINT_STALL) {
+ xfer->result = XFER_RESULT_STALLED;
+ } else if (hcint & HCINT_BABBLE_ERR) {
+ xfer->result = XFER_RESULT_FAILED;
+ } else if (hcint & HCINT_XACT_ERR) {
+ xfer->err_count++;
+ channel->hcintmsk |= HCINT_ACK;
+ }
+
+ channel_disable(dwc2, channel);
+ } else if (hcint & HCINT_NYET) {
+ // restart complete split
+ channel->hcsplt_bm.split_compl = 1;
+ xfer->halted_nyet = 1;
+ channel_disable(dwc2, channel);
+ } else if (hcint & HCINT_NAK) {
+ // NAK received, re-enable channel if request queue is available
+ if (channel->hcsplt_bm.split_en) {
+ channel->hcsplt_bm.split_compl = 0; // restart with start-split
+ }
+
+ channel_disable(dwc2, channel);
+ } else if (hcint & HCINT_ACK) {
+ xfer->err_count = 0;
+
+ if (channel->hcsplt_bm.split_en) {
+ if (!channel->hcsplt_bm.split_compl) {
+ // start split is ACK --> do complete split
+ channel->hcintmsk |= HCINT_NYET;
+ channel->hcsplt_bm.split_compl = 1;
+ channel_send_in_token(dwc2, channel);
+ } else {
+ // do nothing for complete split with DATA, this will trigger XferComplete and handled there
+ }
+ } else {
+ // ACK with data
+ const uint16_t remain_packets = channel->hctsiz_bm.packet_count;
+ if (remain_packets) {
+ // still more packet to receive, also reset to start split
+ channel->hcsplt_bm.split_compl = 0;
+ channel_send_in_token(dwc2, channel);
+ }
+ }
+ } else if (hcint & HCINT_HALTED) {
+ channel->hcintmsk &= ~HCINT_HALTED;
+ if (xfer->halted_sof_schedule) {
+ // de-allocate channel but does not complete xfer, we schedule it in the SOF interrupt
+ channel_dealloc(dwc2, ch_id);
+ } else if (xfer->result != XFER_RESULT_INVALID) {
+ is_done = true;
+ } else if (xfer->err_count == HCD_XFER_ERROR_MAX) {
+ xfer->result = XFER_RESULT_FAILED;
+ is_done = true;
+ } else {
+ // got here due to NAK or NYET
+ channel_xfer_in_retry(dwc2, ch_id, hcint);
+ }
+ } else if (hcint & HCINT_DATATOGGLE_ERR) {
+ xfer->err_count = 0;
+ TU_ASSERT(false);
+ }
+ return is_done;
+}
+
+static bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hcint) {
+ hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
+ dwc2_channel_t* channel = &dwc2->channel[ch_id];
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
+ bool is_done = false;
+
+ if (hcint & HCINT_XFER_COMPLETE) {
+ is_done = true;
+ xfer->result = XFER_RESULT_SUCCESS;
+ channel->hcintmsk &= ~HCINT_ACK;
+ } else if (hcint & HCINT_STALL) {
+ xfer->result = XFER_RESULT_STALLED;
+ channel_disable(dwc2, channel);
+ } else if (hcint & HCINT_NYET) {
+ xfer->err_count = 0;
+ if (channel->hcsplt_bm.split_en) {
+ // retry complete split
+ channel->hcsplt_bm.split_compl = 1;
+ channel->hcchar |= HCCHAR_CHENA;
+ } else {
+ edpt->do_ping = 1;
+ channel_xfer_out_wrapup(dwc2, ch_id);
+ channel_disable(dwc2, channel);
+ }
+ } else if (hcint & (HCINT_NAK | HCINT_XACT_ERR)) {
+ // clean up transfer so far, disable and start again later
+ channel_xfer_out_wrapup(dwc2, ch_id);
+ channel_disable(dwc2, channel);
+ if (hcint & HCINT_XACT_ERR) {
+ xfer->err_count++;
+ channel->hcintmsk |= HCINT_ACK;
+ } else {
+ // NAK disable channel to flush all posted request and try again
+ edpt->do_ping = 1;
+ xfer->err_count = 0;
+ }
+ } else if (hcint & HCINT_HALTED) {
+ channel->hcintmsk &= ~HCINT_HALTED;
+ if (xfer->result != XFER_RESULT_INVALID) {
+ is_done = true;
+ } else if (xfer->err_count == HCD_XFER_ERROR_MAX) {
+ xfer->result = XFER_RESULT_FAILED;
+ is_done = true;
+ } else {
+ // Got here due to NAK or NYET
+ TU_ASSERT(channel_xfer_start(dwc2, ch_id));
+ }
+ } else if (hcint & HCINT_ACK) {
+ xfer->err_count = 0;
+ channel->hcintmsk &= ~HCINT_ACK;
+ if (channel->hcsplt_bm.split_en && !channel->hcsplt_bm.split_compl) {
+ // start split is ACK --> do complete split
+ channel->hcsplt_bm.split_compl = 1;
+ channel->hcchar |= HCCHAR_CHENA;
+ }
+ }
+
+ if (is_done) {
+ xfer->xferred_bytes += xfer->fifo_bytes;
+ xfer->fifo_bytes = 0;
+ }
+
+ return is_done;
+}
+#endif
+
+#if CFG_TUH_DWC2_DMA_ENABLE
+static bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hcint) {
+ hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
+ dwc2_channel_t* channel = &dwc2->channel[ch_id];
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
+
+ bool is_done = false;
+
+ // TU_LOG1("in hcint = %02lX\r\n", hcint);
+
+ if (hcint & HCINT_HALTED) {
+ if (hcint & (HCINT_XFER_COMPLETE | HCINT_STALL | HCINT_BABBLE_ERR)) {
+ const uint16_t remain_bytes = (uint16_t) channel->hctsiz_bm.xfer_size;
+ const uint16_t remain_packets = channel->hctsiz_bm.packet_count;
+ const uint16_t actual_len = edpt->buflen - remain_bytes;
+ xfer->xferred_bytes += actual_len;
+
+ is_done = true;
+
+ if (hcint & HCINT_STALL) {
+ xfer->result = XFER_RESULT_STALLED;
+ } else if (hcint & HCINT_BABBLE_ERR) {
+ xfer->result = XFER_RESULT_FAILED;
+ } else if (channel->hcsplt_bm.split_en && remain_packets && actual_len == edpt->hcchar_bm.ep_size) {
+ // Split can only complete 1 transaction (up to 1 packet) at a time, schedule more
+ is_done = false;
+ edpt->buffer += actual_len;
+ edpt->buflen -= actual_len;
+
+ channel->hcsplt_bm.split_compl = 0;
+ channel_xfer_in_retry(dwc2, ch_id, hcint);
+ } else {
+ xfer->result = XFER_RESULT_SUCCESS;
+ }
+
+ xfer->err_count = 0;
+ channel->hcintmsk &= ~HCINT_ACK;
+ } else if (hcint & HCINT_XACT_ERR) {
+ xfer->err_count++;
+ if (xfer->err_count >= HCD_XFER_ERROR_MAX) {
+ is_done = true;
+ xfer->result = XFER_RESULT_FAILED;
+ } else {
+ channel->hcintmsk |= HCINT_ACK | HCINT_NAK | HCINT_DATATOGGLE_ERR;
+ channel->hcsplt_bm.split_compl = 0;
+ channel_xfer_in_retry(dwc2, ch_id, hcint);
+ }
+ } else if (hcint & HCINT_NYET) {
+ // Must handle nyet before nak or ack. Could get a nyet at the same time as either of those on a BULK/CONTROL
+ // OUT that started with a PING. The nyet takes precedence.
+ if (channel->hcsplt_bm.split_en) {
+ // split not yet mean hub has no data, retry complete split
+ channel->hcsplt_bm.split_compl = 1;
+ channel_xfer_in_retry(dwc2, ch_id, hcint);
+ }
+ } else if (hcint & HCINT_ACK) {
+ xfer->err_count = 0;
+ channel->hcintmsk &= ~HCINT_ACK;
+ if (channel->hcsplt_bm.split_en) {
+ // start split is ACK --> do complete split
+ // TODO: for ISO must use xact_pos to plan complete split based on microframe (up to 187.5 bytes/uframe)
+ channel->hcsplt_bm.split_compl = 1;
+ if (edpt_is_periodic(channel->hcchar_bm.ep_type)) {
+ channel->hcchar_bm.odd_frame = 1 - (dwc2->hfnum & 1); // transfer on next frame
+ }
+ channel_send_in_token(dwc2, channel);
+ }
+ } else if (hcint & (HCINT_NAK | HCINT_DATATOGGLE_ERR)) {
+ xfer->err_count = 0;
+ channel->hcintmsk &= ~(HCINT_NAK | HCINT_DATATOGGLE_ERR);
+ channel->hcsplt_bm.split_compl = 0; // restart with start-split
+ channel_xfer_in_retry(dwc2, ch_id, hcint);
+ } else if (hcint & HCINT_FARME_OVERRUN) {
+ // retry start-split in next binterval
+ channel_xfer_in_retry(dwc2, ch_id, hcint);
+ }
+ }
+
+ return is_done;
+}
+
+static bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hcint) {
+ hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
+ dwc2_channel_t* channel = &dwc2->channel[ch_id];
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
+
+ bool is_done = false;
+
+ // TU_LOG1("out hcint = %02lX\r\n", hcint);
+
+ if (hcint & HCINT_HALTED) {
+ if (hcint & (HCINT_XFER_COMPLETE | HCINT_STALL)) {
+ is_done = true;
+ xfer->err_count = 0;
+ if (hcint & HCINT_XFER_COMPLETE) {
+ xfer->result = XFER_RESULT_SUCCESS;
+ xfer->xferred_bytes += edpt->buflen;
+ } else {
+ xfer->result = XFER_RESULT_STALLED;
+ channel_xfer_out_wrapup(dwc2, ch_id);
+ }
+ channel->hcintmsk &= ~HCINT_ACK;
+ } else if (hcint & HCINT_XACT_ERR) {
+ if (hcint & (HCINT_NAK | HCINT_NYET | HCINT_ACK)) {
+ xfer->err_count = 0;
+ // clean up transfer so far and start again
+ channel_xfer_out_wrapup(dwc2, ch_id);
+ channel_xfer_start(dwc2, ch_id);
+ } else {
+ xfer->err_count++;
+ if (xfer->err_count >= HCD_XFER_ERROR_MAX) {
+ xfer->result = XFER_RESULT_FAILED;
+ is_done = true;
+ } else {
+ // clean up transfer so far and start again
+ channel_xfer_out_wrapup(dwc2, ch_id);
+ channel_xfer_start(dwc2, ch_id);
+ }
+ }
+ } else if (hcint & HCINT_NYET) {
+ if (channel->hcsplt_bm.split_en && channel->hcsplt_bm.split_compl) {
+ // split not yet mean hub has no data, retry complete split
+ channel->hcsplt_bm.split_compl = 1;
+ channel->hcchar |= HCCHAR_CHENA;
+ }
+ } else if (hcint & HCINT_ACK) {
+ xfer->err_count = 0;
+ if (channel->hcsplt_bm.split_en && !channel->hcsplt_bm.split_compl) {
+ // start split is ACK --> do complete split
+ channel->hcsplt_bm.split_compl = 1;
+ channel->hcchar |= HCCHAR_CHENA;
+ }
+ }
+ } else if (hcint & HCINT_ACK) {
+ xfer->err_count = 0;
+ channel->hcintmsk &= ~HCINT_ACK;
+ }
+
+ return is_done;
+}
+#endif
+
+static void handle_channel_irq(uint8_t rhport, bool in_isr) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ const bool is_dma = dma_host_enabled(dwc2);
+ const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2);
+
+ for (uint8_t ch_id = 0; ch_id < max_channel; ch_id++) {
+ if (tu_bit_test(dwc2->haint, ch_id)) {
+ dwc2_channel_t* channel = &dwc2->channel[ch_id];
+ hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
+ TU_ASSERT(xfer->ep_id < CFG_TUH_DWC2_ENDPOINT_MAX,);
+ dwc2_channel_char_t hcchar_bm = channel->hcchar_bm;
+
+ const uint32_t hcint = channel->hcint;
+ channel->hcint = hcint; // clear interrupt
+
+ bool is_done = false;
+ if (is_dma) {
+ #if CFG_TUH_DWC2_DMA_ENABLE
+ if (hcchar_bm.ep_dir == TUSB_DIR_OUT) {
+ is_done = handle_channel_out_dma(dwc2, ch_id, hcint);
+ } else {
+ is_done = handle_channel_in_dma(dwc2, ch_id, hcint);
+ if (is_done && (channel->hcdma > xfer->xferred_bytes)) {
+ // hcdma is increased by word --> need to align4
+ hcd_dcache_invalidate((void*) tu_align4(channel->hcdma - xfer->xferred_bytes), xfer->xferred_bytes);
+ }
+ }
+ #endif
+ } else {
+ #if CFG_TUH_DWC2_SLAVE_ENABLE
+ if (hcchar_bm.ep_dir == TUSB_DIR_OUT) {
+ is_done = handle_channel_out_slave(dwc2, ch_id, hcint);
+ } else {
+ is_done = handle_channel_in_slave(dwc2, ch_id, hcint);
+ }
+ #endif
+ }
+
+ if (is_done) {
+ const uint8_t ep_addr = tu_edpt_addr(hcchar_bm.ep_num, hcchar_bm.ep_dir);
+ hcd_event_xfer_complete(hcchar_bm.dev_addr, ep_addr, xfer->xferred_bytes, xfer->result, in_isr);
+ channel_dealloc(dwc2, ch_id);
+ }
+ }
+ }
+}
+
+// SOF is enabled for scheduled periodic transfer
+static bool handle_sof_irq(uint8_t rhport, bool in_isr) {
+ (void) in_isr;
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+
+ bool more_isr = false;
+
+ // If highspeed then SOF is 125us, else 1ms
+ const uint32_t ucount = (hprt_speed_get(dwc2) == TUSB_SPEED_HIGH ? 1 : 8);
+
+ for(uint8_t ep_id = 0; ep_id < CFG_TUH_DWC2_ENDPOINT_MAX; ep_id++) {
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id];
+ if (edpt->hcchar_bm.enable && edpt_is_periodic(edpt->hcchar_bm.ep_type) && edpt->uframe_countdown > 0) {
+ edpt->uframe_countdown -= tu_min32(ucount, edpt->uframe_countdown);
+ if (edpt->uframe_countdown == 0) {
+ if (!edpt_xfer_kickoff(dwc2, ep_id)) {
+ edpt->uframe_countdown = ucount; // failed to start, try again next frame
+ }
+ }
+
+ more_isr = true;
+ }
+ }
+
+ return more_isr;
+}
+
+// Config HCFG FS/LS clock and HFIR for SOF interval according to link speed (value is in PHY clock unit)
+static void port0_enable(dwc2_regs_t* dwc2, tusb_speed_t speed) {
+ uint32_t hcfg = dwc2->hcfg & ~HCFG_FSLS_PHYCLK_SEL;
+
+ const dwc2_gusbcfg_t gusbcfg_bm = dwc2->gusbcfg_bm;
+ uint32_t phy_clock;
+
+ if (gusbcfg_bm.phy_sel) {
+ phy_clock = 48; // dedicated FS is 48Mhz
+ if (speed == TUSB_SPEED_LOW) {
+ hcfg |= HCFG_FSLS_PHYCLK_SEL_6MHZ;
+ } else {
+ hcfg |= HCFG_FSLS_PHYCLK_SEL_48MHZ;
+ }
+ } else {
+ if (gusbcfg_bm.ulpi_utmi_sel) {
+ phy_clock = 60; // ULPI 8-bit is 60Mhz
+ } else {
+ // UTMI+ 16-bit is 30Mhz, 8-bit is 60Mhz
+ phy_clock = gusbcfg_bm.phy_if16 ? 30 : 60;
+
+ // Enable UTMI+ low power mode 48Mhz external clock if not highspeed
+ if (speed == TUSB_SPEED_HIGH) {
+ dwc2->gusbcfg &= ~GUSBCFG_PHYLPCS;
+ } else {
+ dwc2->gusbcfg |= GUSBCFG_PHYLPCS;
+ // may need to reset port
+ }
+ }
+ hcfg |= HCFG_FSLS_PHYCLK_SEL_30_60MHZ;
+ }
+
+ dwc2->hcfg = hcfg;
+
+ uint32_t hfir = dwc2->hfir & ~HFIR_FRIVL_Msk;
+ if (speed == TUSB_SPEED_HIGH) {
+ hfir |= 125*phy_clock;
+ } else {
+ hfir |= 1000*phy_clock;
+ }
+
+ dwc2->hfir = hfir;
+}
+
+/* Handle Host Port interrupt, possible source are:
+ - Connection Detection
+ - Enable Change
+ - Over Current Change
+*/
+static void handle_hprt_irq(uint8_t rhport, bool in_isr) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ uint32_t hprt = dwc2->hprt & ~HPRT_W1_MASK;
+ const dwc2_hprt_t hprt_bm = dwc2->hprt_bm;
+
+ if (dwc2->hprt & HPRT_CONN_DETECT) {
+ // Port Connect Detect
+ hprt |= HPRT_CONN_DETECT;
+
+ if (hprt_bm.conn_status) {
+ hcd_event_device_attach(rhport, in_isr);
+ } else {
+ hcd_event_device_remove(rhport, in_isr);
+ }
+ }
+
+ if (dwc2->hprt & HPRT_ENABLE_CHANGE) {
+ // Port enable change
+ hprt |= HPRT_ENABLE_CHANGE;
+
+ if (hprt_bm.enable) {
+ // Port enable
+ const tusb_speed_t speed = hprt_speed_get(dwc2);
+ port0_enable(dwc2, speed);
+ } else {
+ // TU_ASSERT(false, );
+ }
+ }
+
+ dwc2->hprt = hprt; // clear interrupt
+}
+
+/* Interrupt Hierarchy
+ HCINTn HPRT
+ | |
+ HAINT.CHn |
+ | |
+ GINTSTS : HCInt | PrtInt | NPTxFEmp | PTxFEmpp | RXFLVL | SOF
+*/
+void hcd_int_handler(uint8_t rhport, bool in_isr) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ const uint32_t gintmsk = dwc2->gintmsk;
+ const uint32_t gintsts = dwc2->gintsts & gintmsk;
+
+ // TU_LOG1_HEX(gintsts);
+
+ if (gintsts & GINTSTS_CONIDSTSCHNG) {
+ // Connector ID status change
+ dwc2->gintsts = GINTSTS_CONIDSTSCHNG;
+
+ //if (dwc2->gotgctl)
+ // dwc2->hprt = HPRT_POWER; // power on port to turn on VBUS
+ //dwc2->gintmsk |= GINTMSK_PRTIM;
+ // TODO wait for SRP if OTG
+ }
+
+ if (gintsts & GINTSTS_SOF) {
+ const bool more_sof = handle_sof_irq(rhport, in_isr);
+ if (!more_sof) {
+ dwc2->gintmsk &= ~GINTSTS_SOF;
+ }
+ }
+
+ if (gintsts & GINTSTS_HPRTINT) {
+ // Host port interrupt: source is cleared in HPRT register
+ // TU_LOG1_HEX(dwc2->hprt);
+ handle_hprt_irq(rhport, in_isr);
+ }
+
+ if (gintsts & GINTSTS_HCINT) {
+ // Host Channel interrupt: source is cleared in HCINT register
+ // must be handled after TX FIFO empty
+ handle_channel_irq(rhport, in_isr);
+ }
+
+#if CFG_TUH_DWC2_SLAVE_ENABLE
+ // RxFIFO non-empty interrupt handling
+ if (gintsts & GINTSTS_RXFLVL) {
+ // RXFLVL bit is read-only
+ dwc2->gintmsk &= ~GINTSTS_RXFLVL; // disable RXFLVL interrupt while reading
+
+ do {
+ handle_rxflvl_irq(rhport); // read all packets
+ } while(dwc2->gintsts & GINTSTS_RXFLVL);
+
+ dwc2->gintmsk |= GINTSTS_RXFLVL;
+ }
+
+ if (gintsts & GINTSTS_NPTX_FIFO_EMPTY) {
+ // NPTX FIFO empty interrupt, this is read-only and cleared by hardware when FIFO is written
+ const bool more_nptxfe = handle_txfifo_empty(dwc2, false);
+ if (!more_nptxfe) {
+ // no more pending packet, disable interrupt
+ dwc2->gintmsk &= ~GINTSTS_NPTX_FIFO_EMPTY;
+ }
+ }
+
+ if (gintsts & GINTSTS_PTX_FIFO_EMPTY) {
+ // PTX FIFO empty interrupt, this is read-only and cleared by hardware when FIFO is written
+ const bool more_ptxfe = handle_txfifo_empty(dwc2, true);
+ if (!more_ptxfe) {
+ // no more pending packet, disable interrupt
+ dwc2->gintmsk &= ~GINTSTS_PTX_FIFO_EMPTY;
+ }
+ }
+#endif
+}
+
+#endif
diff --git a/src/tusb.c b/src/tusb.c
index e6f8055b7..85ab1d6ae 100644
--- a/src/tusb.c
+++ b/src/tusb.c
@@ -39,14 +39,25 @@
#include "host/usbh_pvt.h"
#endif
-#define TUP_USBIP_CONTROLLER_NUM 2
+tusb_role_t _tusb_rhport_role[TUP_USBIP_CONTROLLER_NUM] = { TUSB_ROLE_INVALID };
-static tusb_role_t _rhport_role[TUP_USBIP_CONTROLLER_NUM] = { 0 };
+//--------------------------------------------------------------------
+// Weak/Default API, can be overwritten by Application
+//--------------------------------------------------------------------
+
+TU_ATTR_WEAK void tusb_time_delay_ms_api(uint32_t ms) {
+#if CFG_TUSB_OS != OPT_OS_NONE
+ osal_task_delay(ms);
+#else
+ // delay using millis() (if implemented) and/or frame number if possible
+ const uint32_t time_ms = tusb_time_millis_api();
+ while ((tusb_time_millis_api() - time_ms) < ms) {}
+#endif
+}
//--------------------------------------------------------------------+
// Public API
//--------------------------------------------------------------------+
-
bool tusb_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
// backward compatible called with tusb_init(void)
#if defined(TUD_OPT_RHPORT) || defined(TUH_OPT_RHPORT)
@@ -57,8 +68,8 @@ bool tusb_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
.role = TUSB_ROLE_DEVICE,
.speed = TUD_OPT_HIGH_SPEED ? TUSB_SPEED_HIGH : TUSB_SPEED_FULL
};
- TU_ASSERT ( tud_rhport_init(rhport, &dev_init) );
- _rhport_role[TUD_OPT_RHPORT] = TUSB_ROLE_DEVICE;
+ TU_ASSERT ( tud_rhport_init(TUD_OPT_RHPORT, &dev_init) );
+ _tusb_rhport_role[TUD_OPT_RHPORT] = TUSB_ROLE_DEVICE;
#endif
#if CFG_TUH_ENABLED && defined(TUH_OPT_RHPORT)
@@ -68,7 +79,7 @@ bool tusb_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
.speed = TUH_OPT_HIGH_SPEED ? TUSB_SPEED_HIGH : TUSB_SPEED_FULL
};
TU_ASSERT( tuh_rhport_init(TUH_OPT_RHPORT, &host_init) );
- _rhport_role[TUH_OPT_RHPORT] = TUSB_ROLE_HOST;
+ _tusb_rhport_role[TUH_OPT_RHPORT] = TUSB_ROLE_HOST;
#endif
return true;
@@ -77,6 +88,7 @@ bool tusb_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
// new API with explicit rhport and role
TU_ASSERT(rhport < TUP_USBIP_CONTROLLER_NUM && rh_init->role != TUSB_ROLE_INVALID);
+ _tusb_rhport_role[rhport] = rh_init->role;
#if CFG_TUD_ENABLED
if (rh_init->role == TUSB_ROLE_DEVICE) {
@@ -90,7 +102,6 @@ bool tusb_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
}
#endif
- _rhport_role[rhport] = rh_init->role;
return true;
}
@@ -112,14 +123,14 @@ void tusb_int_handler(uint8_t rhport, bool in_isr) {
TU_VERIFY(rhport < TUP_USBIP_CONTROLLER_NUM,);
#if CFG_TUD_ENABLED
- if (_rhport_role[rhport] == TUSB_ROLE_DEVICE) {
+ if (_tusb_rhport_role[rhport] == TUSB_ROLE_DEVICE) {
(void) in_isr;
dcd_int_handler(rhport);
}
#endif
#if CFG_TUH_ENABLED
- if (_rhport_role[rhport] == TUSB_ROLE_HOST) {
+ if (_tusb_rhport_role[rhport] == TUSB_ROLE_HOST) {
hcd_int_handler(rhport, in_isr);
}
#endif
@@ -247,6 +258,10 @@ uint16_t tu_desc_get_interface_total_len(tusb_desc_interface_t const* desc_itf,
p_desc = tu_desc_next(p_desc);
while (len < max_len) {
+ if (tu_desc_len(p_desc) == 0) {
+ // Escape infinite loop
+ break;
+ }
// return on IAD regardless of itf count
if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE_ASSOCIATION) {
return len;
diff --git a/src/tusb.h b/src/tusb.h
index 2f30a5739..cb6021b33 100644
--- a/src/tusb.h
+++ b/src/tusb.h
@@ -127,10 +127,8 @@
//--------------------------------------------------------------------+
-// APPLICATION API
+// User API
//--------------------------------------------------------------------+
-
-
#if CFG_TUH_ENABLED || CFG_TUD_ENABLED
// Internal helper for backward compatible with tusb_init(void)
@@ -167,6 +165,15 @@ void tusb_int_handler(uint8_t rhport, bool in_isr);
#endif
+//--------------------------------------------------------------------+
+// API Implemented by user
+//--------------------------------------------------------------------+
+
+// Get current milliseconds, required by some port/configuration without RTOS
+uint32_t tusb_time_millis_api(void);
+
+// Delay in milliseconds, use tusb_time_millis_api() by default. required by some port/configuration with no RTOS
+void tusb_time_delay_ms_api(uint32_t ms);
#ifdef __cplusplus
}
diff --git a/src/tusb_option.h b/src/tusb_option.h
index fdc949747..dca1e4109 100644
--- a/src/tusb_option.h
+++ b/src/tusb_option.h
@@ -92,6 +92,8 @@
#define OPT_MCU_STM32L5 314 ///< ST L5
#define OPT_MCU_STM32H5 315 ///< ST H5
#define OPT_MCU_STM32U0 316 ///< ST U0
+#define OPT_MCU_STM32H7RS 317 ///< ST F7RS
+#define OPT_MCU_STM32C0 318 ///< ST C0
// Sony
#define OPT_MCU_CXD56 400 ///< SONY CXD56
@@ -125,7 +127,8 @@
#define OPT_MCU_ESP32C2 905 ///< Espressif ESP32-C2
#define OPT_MCU_ESP32H2 906 ///< Espressif ESP32-H2
#define OPT_MCU_ESP32P4 907 ///< Espressif ESP32-P4
-#define TUP_MCU_ESPRESSIF (CFG_TUSB_MCU >= 900 && CFG_TUSB_MCU < 1000) // check if Espressif MCU
+#define TUSB_MCU_VENDOR_ESPRESSIF (CFG_TUSB_MCU >= 900 && CFG_TUSB_MCU < 1000) // check if Espressif MCU
+#define TUP_MCU_ESPRESSIF TUSB_MCU_VENDOR_ESPRESSIF // for backward compatibility
// Dialog
#define OPT_MCU_DA1469X 1000 ///< Dialog Semiconductor DA1469x
@@ -240,17 +243,45 @@
#include "tusb_config.h"
#endif
+#include "common/tusb_mcu.h"
+
//--------------------------------------------------------------------+
// USBIP
//--------------------------------------------------------------------+
-// DWC2 controller: use DMA for data transfer
-// For processors with data cache enabled, USB endpoint buffer region
-// (defined by CFG_TUSB_MEM_SECTION) must be declared as non-cacheable.
-// For example, on Cortex-M7 the MPU region can be configured as normal
-// non-cacheable, with RASR register value: TEX=1 C=0 B=0 S=0.
-#ifndef CFG_TUD_DWC2_DMA
- #define CFG_TUD_DWC2_DMA 0
+#ifndef CFG_TUD_DWC2_SLAVE_ENABLE
+ #ifndef CFG_TUD_DWC2_SLAVE_ENABLE_DEFAULT
+ #define CFG_TUD_DWC2_SLAVE_ENABLE_DEFAULT 1
+ #endif
+
+ #define CFG_TUD_DWC2_SLAVE_ENABLE CFG_TUD_DWC2_SLAVE_ENABLE_DEFAULT
+#endif
+
+// Enable DWC2 DMA for device
+#ifndef CFG_TUD_DWC2_DMA_ENABLE
+ #ifndef CFG_TUD_DWC2_DMA_ENABLE_DEFAULT
+ #define CFG_TUD_DWC2_DMA_ENABLE_DEFAULT 0
+ #endif
+
+ #define CFG_TUD_DWC2_DMA_ENABLE CFG_TUD_DWC2_DMA_ENABLE_DEFAULT
+#endif
+
+// Enable DWC2 Slave mode for host
+#ifndef CFG_TUH_DWC2_SLAVE_ENABLE
+ #ifndef CFG_TUH_DWC2_SLAVE_ENABLE_DEFAULT
+ #define CFG_TUH_DWC2_SLAVE_ENABLE_DEFAULT 1
+ #endif
+
+ #define CFG_TUH_DWC2_SLAVE_ENABLE CFG_TUH_DWC2_SLAVE_ENABLE_DEFAULT
+#endif
+
+// Enable DWC2 DMA for host
+#ifndef CFG_TUH_DWC2_DMA_ENABLE
+ #ifndef CFG_TUH_DWC2_DMA_ENABLE_DEFAULT
+ #define CFG_TUH_DWC2_DMA_ENABLE_DEFAULT 0
+ #endif
+
+ #define CFG_TUH_DWC2_DMA_ENABLE CFG_TUH_DWC2_DMA_ENABLE_DEFAULT
#endif
// Enable PIO-USB software host controller
@@ -267,7 +298,6 @@
#define CFG_TUH_MAX3421 0
#endif
-#include "common/tusb_mcu.h"
//--------------------------------------------------------------------
// RootHub Mode detection
@@ -376,13 +406,25 @@
#define CFG_TUSB_MEM_ALIGN TU_ATTR_ALIGNED(4)
#endif
+#ifndef CFG_TUSB_MEM_DCACHE_LINE_SIZE
+ #ifndef CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT
+ #define CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT 32
+ #endif
+
+ #define CFG_TUSB_MEM_DCACHE_LINE_SIZE CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT
+#endif
+
// OS selection
#ifndef CFG_TUSB_OS
#define CFG_TUSB_OS OPT_OS_NONE
#endif
#ifndef CFG_TUSB_OS_INC_PATH
- #define CFG_TUSB_OS_INC_PATH
+ #ifndef CFG_TUSB_OS_INC_PATH_DEFAULT
+ #define CFG_TUSB_OS_INC_PATH_DEFAULT
+ #endif
+
+ #define CFG_TUSB_OS_INC_PATH CFG_TUSB_OS_INC_PATH_DEFAULT
#endif
//--------------------------------------------------------------------
@@ -399,6 +441,18 @@
#define CFG_TUD_MEM_ALIGN CFG_TUSB_MEM_ALIGN
#endif
+#ifndef CFG_TUD_MEM_DCACHE_ENABLE
+ #ifndef CFG_TUD_MEM_DCACHE_ENABLE_DEFAULT
+ #define CFG_TUD_MEM_DCACHE_ENABLE_DEFAULT 0
+ #endif
+
+ #define CFG_TUD_MEM_DCACHE_ENABLE CFG_TUD_MEM_DCACHE_ENABLE_DEFAULT
+#endif
+
+#ifndef CFG_TUD_MEM_DCACHE_LINE_SIZE
+ #define CFG_TUD_MEM_DCACHE_LINE_SIZE CFG_TUSB_MEM_DCACHE_LINE_SIZE
+#endif
+
#ifndef CFG_TUD_ENDPOINT0_SIZE
#define CFG_TUD_ENDPOINT0_SIZE 64
#endif
@@ -506,6 +560,18 @@
#define CFG_TUH_MEM_ALIGN CFG_TUSB_MEM_ALIGN
#endif
+#ifndef CFG_TUH_MEM_DCACHE_ENABLE
+ #ifndef CFG_TUH_MEM_DCACHE_ENABLE_DEFAULT
+ #define CFG_TUH_MEM_DCACHE_ENABLE_DEFAULT 0
+ #endif
+
+ #define CFG_TUH_MEM_DCACHE_ENABLE CFG_TUH_MEM_DCACHE_ENABLE_DEFAULT
+#endif
+
+#ifndef CFG_TUH_MEM_DCACHE_LINE_SIZE
+ #define CFG_TUH_MEM_DCACHE_LINE_SIZE CFG_TUSB_MEM_DCACHE_LINE_SIZE
+#endif
+
//------------- CLASS -------------//
#ifndef CFG_TUH_HUB