summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/class/audio/audio.h4
-rw-r--r--src/class/audio/audio_device.c129
-rw-r--r--src/class/cdc/cdc_device.c248
-rw-r--r--src/class/cdc/cdc_device.h191
-rw-r--r--src/class/cdc/cdc_host.c4
-rw-r--r--src/class/hid/hid.h485
-rw-r--r--src/class/hid/hid_device.c339
-rw-r--r--src/class/hid/hid_device.h277
-rw-r--r--src/class/hid/hid_host.c48
-rw-r--r--src/class/hid/hid_host.h8
-rw-r--r--src/class/msc/msc_device.c18
-rw-r--r--src/class/msc/msc_device.h3
-rw-r--r--src/class/net/ncm.h116
-rw-r--r--src/class/net/ncm_device.c1105
-rw-r--r--src/class/net/net_device.h26
-rw-r--r--src/class/usbtmc/usbtmc.h25
-rw-r--r--src/class/usbtmc/usbtmc_device.c35
-rw-r--r--src/class/usbtmc/usbtmc_device.h29
-rw-r--r--src/class/vendor/vendor_device.c16
-rw-r--r--src/class/video/video_device.c12
-rw-r--r--src/common/tusb_common.h1
-rw-r--r--src/common/tusb_compiler.h3
-rw-r--r--src/common/tusb_fifo.c31
-rw-r--r--src/common/tusb_fifo.h16
-rw-r--r--src/common/tusb_mcu.h96
-rw-r--r--src/common/tusb_types.h40
-rw-r--r--src/common/tusb_verify.h18
-rw-r--r--src/device/dcd.h46
-rw-r--r--src/device/usbd.c275
-rw-r--r--src/device/usbd.h30
-rw-r--r--src/device/usbd_pvt.h18
-rw-r--r--src/host/usbh.c11
-rw-r--r--src/osal/osal_freertos.h2
-rw-r--r--src/osal/osal_none.h1
-rw-r--r--src/osal/osal_pico.h1
-rw-r--r--src/portable/analog/max3421/hcd_max3421.c84
-rw-r--r--src/portable/chipidea/ci_hs/dcd_ci_hs.c13
-rw-r--r--src/portable/ehci/ehci.c2
-rw-r--r--src/portable/espressif/esp32sx/dcd_esp32sx.c3
-rw-r--r--src/portable/microchip/samd/dcd_samd.c11
-rw-r--r--src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c11
-rw-r--r--src/portable/nordic/nrf5x/dcd_nrf5x.c629
-rw-r--r--src/portable/nxp/khci/dcd_khci.c2
-rw-r--r--src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c13
-rw-r--r--src/portable/raspberrypi/rp2040/rp2040_usb.c13
-rw-r--r--src/portable/renesas/rusb2/dcd_rusb2.c181
-rw-r--r--src/portable/renesas/rusb2/hcd_rusb2.c89
-rw-r--r--src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c1534
-rw-r--r--src/portable/st/stm32_fsdev/dcd_stm32_fsdev.h538
-rw-r--r--src/portable/st/stm32_fsdev/fsdev_ch32.h220
-rw-r--r--src/portable/st/stm32_fsdev/fsdev_stm32.h334
-rw-r--r--src/portable/st/stm32_fsdev/fsdev_type.h282
-rw-r--r--src/portable/sunxi/dcd_sunxi_musb.c14
-rw-r--r--src/portable/synopsys/dwc2/dcd_dwc2.c102
-rw-r--r--src/portable/synopsys/dwc2/dwc2_esp32.h1
-rw-r--r--src/portable/synopsys/dwc2/dwc2_type.h33
-rw-r--r--src/portable/ti/msp430x5xx/dcd_msp430x5xx.c131
-rw-r--r--src/portable/wch/ch32_usbfs_reg.h175
-rw-r--r--src/portable/wch/ch32_usbhs_reg.h82
-rw-r--r--src/portable/wch/dcd_ch32_usbfs.c344
-rw-r--r--src/portable/wch/dcd_ch32_usbhs.c559
-rw-r--r--src/tusb.c2
-rw-r--r--src/tusb.h2
-rw-r--r--src/tusb_option.h81
-rw-r--r--src/typec/tcd.h2
66 files changed, 5342 insertions, 3854 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 076e1e1eb..272962332 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -41,7 +41,7 @@ function(add_tinyusb TARGET)
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../lib/networking
)
- if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
+ if (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "Clang")
target_compile_options(${TARGET} PRIVATE
-Wall
-Wextra
diff --git a/src/class/audio/audio.h b/src/class/audio/audio.h
index d6f3e22e2..2f97c0f23 100644
--- a/src/class/audio/audio.h
+++ b/src/class/audio/audio.h
@@ -489,7 +489,7 @@ typedef enum
AUDIO_DATA_FORMAT_TYPE_I_IEEE_FLOAT = (uint32_t) (1 << 2),
AUDIO_DATA_FORMAT_TYPE_I_ALAW = (uint32_t) (1 << 3),
AUDIO_DATA_FORMAT_TYPE_I_MULAW = (uint32_t) (1 << 4),
- AUDIO_DATA_FORMAT_TYPE_I_RAW_DATA = 0x80000000,
+ AUDIO_DATA_FORMAT_TYPE_I_RAW_DATA = 0x80000000u,
} audio_data_format_type_I_t;
/// All remaining definitions are taken from the descriptor descriptions in the UAC2 main specification
@@ -640,7 +640,7 @@ typedef enum
AUDIO_CHANNEL_CONFIG_BOTTOM_CENTER = 0x01000000,
AUDIO_CHANNEL_CONFIG_BACK_LEFT_OF_CENTER = 0x02000000,
AUDIO_CHANNEL_CONFIG_BACK_RIGHT_OF_CENTER = 0x04000000,
- AUDIO_CHANNEL_CONFIG_RAW_DATA = 0x80000000,
+ AUDIO_CHANNEL_CONFIG_RAW_DATA = 0x80000000u,
} audio_channel_config_t;
/// AUDIO Channel Cluster Descriptor (4.1)
diff --git a/src/class/audio/audio_device.c b/src/class/audio/audio_device.c
index 9050b462a..ed380424f 100644
--- a/src/class/audio/audio_device.c
+++ b/src/class/audio/audio_device.c
@@ -304,19 +304,6 @@ typedef struct
uint16_t desc_length; // Length of audio function descriptor
- // Buffer for control requests
- uint8_t * ctrl_buf;
- uint8_t ctrl_buf_sz;
-
- // Current active alternate settings
- uint8_t * alt_setting; // We need to save the current alternate setting this way, because it is possible that there are AS interfaces which do not have an EP!
-
- // EP Transfer buffers and FIFOs
-#if CFG_TUD_AUDIO_ENABLE_EP_OUT
-#if !CFG_TUD_AUDIO_ENABLE_DECODING
- tu_fifo_t ep_out_ff;
-#endif
-
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
struct {
CFG_TUSB_MEM_ALIGN uint32_t value; // Feedback value for asynchronous mode (in 16.16 format).
@@ -346,17 +333,6 @@ typedef struct
} feedback;
#endif // CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
-#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT
-
-#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
- tu_fifo_t ep_in_ff;
-#endif
-
- // Audio control interrupt buffer - no FIFO - 6 Bytes according to UAC 2 specification (p. 74)
-#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
- CFG_TUSB_MEM_ALIGN uint8_t ep_int_buf[6];
-#endif
-
// Decoding parameters - parameters are set when alternate AS interface is set by host
// Coding is currently only supported for EP. Software coding corresponding to AS interfaces without EPs are not supported currently.
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
@@ -365,8 +341,7 @@ typedef struct
#if CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
audio_data_format_type_I_t format_type_I_rx;
- uint8_t n_bytes_per_sampe_rx;
- uint8_t n_channels_per_ff_rx;
+ uint8_t n_bytes_per_sample_rx;
uint8_t n_ff_used_rx;
#endif
#endif
@@ -382,26 +357,57 @@ typedef struct
#if CFG_TUD_AUDIO_ENABLE_EP_IN && (CFG_TUD_AUDIO_ENABLE_ENCODING || CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL)
audio_format_type_t format_type_tx;
uint8_t n_channels_tx;
- uint8_t n_bytes_per_sampe_tx;
+ uint8_t n_bytes_per_sample_tx;
#if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
audio_data_format_type_I_t format_type_I_tx;
- uint8_t n_channels_per_ff_tx;
uint8_t n_ff_used_tx;
#endif
#endif
+ // Buffer for control requests
+ uint8_t * ctrl_buf;
+ uint8_t ctrl_buf_sz;
+
+ // Current active alternate settings
+ uint8_t * alt_setting; // We need to save the current alternate setting this way, because it is possible that there are AS interfaces which do not have an EP!
+
+ // EP Transfer buffers and FIFOs
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+#if !CFG_TUD_AUDIO_ENABLE_DECODING
+ tu_fifo_t ep_out_ff;
+#endif
+
+
+#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
+ tu_fifo_t ep_in_ff;
+#endif
+
+ // Audio control interrupt buffer - no FIFO - 6 Bytes according to UAC 2 specification (p. 74)
+#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
+ CFG_TUSB_MEM_ALIGN uint8_t ep_int_buf[6];
+#endif
+
+
// Support FIFOs for software encoding and decoding
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
tu_fifo_t * rx_supp_ff;
uint8_t n_rx_supp_ff;
uint16_t rx_supp_ff_sz_max;
+#if CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
+ uint8_t n_channels_per_ff_rx;
+#endif
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
tu_fifo_t * tx_supp_ff;
uint8_t n_tx_supp_ff;
uint16_t tx_supp_ff_sz_max;
+#if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
+ uint8_t n_channels_per_ff_tx;
+#endif
#endif
// Linear buffer in case target MCU is not capable of handling a ring buffer FIFO e.g. no hardware buffer is available or driver is would need to be changed dramatically OR the support FIFOs are used
@@ -707,16 +713,16 @@ static bool audiod_decode_type_I_pcm(uint8_t rhport, audiod_function_t* audio, u
if (info.len_lin != 0)
{
info.len_lin = tu_min16(nBytesPerFFToRead, info.len_lin);
- src = &audio->lin_buf_out[cnt_ff*audio->n_channels_per_ff_rx * audio->n_bytes_per_sampe_rx];
+ src = &audio->lin_buf_out[cnt_ff*audio->n_channels_per_ff_rx * audio->n_bytes_per_sample_rx];
dst_end = info.ptr_lin + info.len_lin;
- src = audiod_interleaved_copy_bytes_fast_decode(audio->n_bytes_per_sampe_rx, info.ptr_lin, dst_end, src, n_ff_used);
+ src = audiod_interleaved_copy_bytes_fast_decode(audio->n_bytes_per_sample_rx, info.ptr_lin, dst_end, src, n_ff_used);
// Handle wrapped part of FIFO
info.len_wrap = tu_min16(nBytesPerFFToRead - info.len_lin, info.len_wrap);
if (info.len_wrap != 0)
{
dst_end = info.ptr_wrap + info.len_wrap;
- audiod_interleaved_copy_bytes_fast_decode(audio->n_bytes_per_sampe_rx, info.ptr_wrap, dst_end, src, n_ff_used);
+ audiod_interleaved_copy_bytes_fast_decode(audio->n_bytes_per_sample_rx, info.ptr_wrap, dst_end, src, n_ff_used);
}
tu_fifo_advance_write_pointer(&audio->rx_supp_ff[cnt_ff], info.len_lin + info.len_wrap);
}
@@ -1024,7 +1030,7 @@ static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t* audi
// Limit to maximum sample number - THIS IS A POSSIBLE ERROR SOURCE IF TOO MANY SAMPLE WOULD NEED TO BE SENT BUT CAN NOT!
nBytesPerFFToSend = tu_min16(nBytesPerFFToSend, audio->ep_in_sz / n_ff_used);
// Round to full number of samples (flooring)
- uint16_t const nSlotSize = audio->n_channels_per_ff_tx * audio->n_bytes_per_sampe_tx;
+ uint16_t const nSlotSize = audio->n_channels_per_ff_tx * audio->n_bytes_per_sample_tx;
nBytesPerFFToSend = (nBytesPerFFToSend / nSlotSize) * nSlotSize;
#endif
@@ -1036,7 +1042,7 @@ static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t* audi
for (cnt_ff = 0; cnt_ff < n_ff_used; cnt_ff++)
{
- dst = &audio->lin_buf_in[cnt_ff*audio->n_channels_per_ff_tx*audio->n_bytes_per_sampe_tx];
+ dst = &audio->lin_buf_in[cnt_ff*audio->n_channels_per_ff_tx*audio->n_bytes_per_sample_tx];
tu_fifo_get_read_info(&audio->tx_supp_ff[cnt_ff], &info);
@@ -1044,7 +1050,7 @@ static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t* audi
{
info.len_lin = tu_min16(nBytesPerFFToSend, info.len_lin); // Limit up to desired length
src_end = (uint8_t *)info.ptr_lin + info.len_lin;
- dst = audiod_interleaved_copy_bytes_fast_encode(audio->n_bytes_per_sampe_tx, info.ptr_lin, src_end, dst, n_ff_used);
+ dst = audiod_interleaved_copy_bytes_fast_encode(audio->n_bytes_per_sample_tx, info.ptr_lin, src_end, dst, n_ff_used);
// Limit up to desired length
info.len_wrap = tu_min16(nBytesPerFFToSend - info.len_lin, info.len_wrap);
@@ -1053,7 +1059,7 @@ static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t* audi
if (info.len_wrap != 0)
{
src_end = (uint8_t *)info.ptr_wrap + info.len_wrap;
- audiod_interleaved_copy_bytes_fast_encode(audio->n_bytes_per_sampe_tx, info.ptr_wrap, src_end, dst, n_ff_used);
+ audiod_interleaved_copy_bytes_fast_encode(audio->n_bytes_per_sample_tx, info.ptr_wrap, src_end, dst, n_ff_used);
}
tu_fifo_advance_read_pointer(&audio->tx_supp_ff[cnt_ff], info.len_lin + info.len_wrap);
@@ -1490,7 +1496,8 @@ uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin
#endif
uint8_t const *p_desc = _audiod_fct[i].p_desc;
uint8_t const *p_desc_end = p_desc + _audiod_fct[i].desc_length - TUD_AUDIO_DESC_IAD_LEN;
- while (p_desc < p_desc_end)
+ // Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
+ while (p_desc_end - p_desc > 0)
{
if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT)
{
@@ -1740,7 +1747,8 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
uint8_t const *p_desc_end = audio->p_desc + audio->desc_length - TUD_AUDIO_DESC_IAD_LEN;
// p_desc starts at required interface with alternate setting zero
- while (p_desc < p_desc_end)
+ // Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
+ while (p_desc_end - p_desc > 0)
{
// Find correct interface
if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const * )p_desc)->bInterfaceNumber == itf && ((tusb_desc_interface_t const * )p_desc)->bAlternateSetting == alt)
@@ -1750,7 +1758,8 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
#endif
// From this point forward follow the EP descriptors associated to the current alternate setting interface - Open EPs if necessary
uint8_t foundEPs = 0, nEps = ((tusb_desc_interface_t const * )p_desc)->bNumEndpoints;
- while (foundEPs < nEps && p_desc < p_desc_end)
+ // Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
+ while (foundEPs < nEps && (p_desc_end - p_desc > 0))
{
if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT)
{
@@ -1779,8 +1788,8 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
// Reconfigure size of support FIFOs - this is necessary to avoid samples to get split in case of a wrap
#if CFG_TUD_AUDIO_ENABLE_ENCODING && CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
- const uint16_t active_fifo_depth = (uint16_t) ((audio->tx_supp_ff_sz_max / (audio->n_channels_per_ff_tx * audio->n_bytes_per_sampe_tx))
- * (audio->n_channels_per_ff_tx * audio->n_bytes_per_sampe_tx));
+ const uint16_t active_fifo_depth = (uint16_t) ((audio->tx_supp_ff_sz_max / (audio->n_channels_per_ff_tx * audio->n_bytes_per_sample_tx))
+ * (audio->n_channels_per_ff_tx * audio->n_bytes_per_sample_tx));
for (uint8_t cnt = 0; cnt < audio->n_tx_supp_ff; cnt++)
{
tu_fifo_config(&audio->tx_supp_ff[cnt], audio->tx_supp_ff[cnt].buffer, active_fifo_depth, 1, true);
@@ -1810,7 +1819,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
// Reconfigure size of support FIFOs - this is necessary to avoid samples to get split in case of a wrap
#if CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
- const uint16_t active_fifo_depth = (audio->rx_supp_ff_sz_max / audio->n_bytes_per_sampe_rx) * audio->n_bytes_per_sampe_rx;
+ const uint16_t active_fifo_depth = (audio->rx_supp_ff_sz_max / audio->n_bytes_per_sample_rx) * audio->n_bytes_per_sample_rx;
for (uint8_t cnt = 0; cnt < audio->n_rx_supp_ff; cnt++)
{
tu_fifo_config(&audio->rx_supp_ff[cnt], audio->rx_supp_ff[cnt].buffer, active_fifo_depth, 1, true);
@@ -1835,7 +1844,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
audio->feedback.frame_shift = desc_ep->bInterval -1;
// Enable SOF interrupt if callback is implemented
- if (tud_audio_feedback_interval_isr) usbd_sof_enable(rhport, true);
+ if (tud_audio_feedback_interval_isr) usbd_sof_enable(rhport, SOF_CONSUMER_AUDIO, true);
}
#endif
#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT
@@ -1909,7 +1918,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
break;
}
}
- if (disable) usbd_sof_enable(rhport, false);
+ if (disable) usbd_sof_enable(rhport, SOF_CONSUMER_AUDIO, false);
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
@@ -2015,7 +2024,10 @@ static bool audiod_control_request(uint8_t rhport, tusb_control_request_t const
case TUSB_REQ_SET_INTERFACE:
return audiod_set_interface(rhport, p_request);
- // Unknown/Unsupported request
+ case TUSB_REQ_CLEAR_FEATURE:
+ return true;
+
+ // Unknown/Unsupported request
default: TU_BREAKPOINT(); return false;
}
}
@@ -2391,7 +2403,8 @@ static bool audiod_get_AS_interface_index(uint8_t itf, audiod_function_t * audio
p_desc += ((audio_desc_cs_ac_interface_t const *)p_desc)->wTotalLength;
uint8_t tmp = 0;
- while (p_desc < p_desc_end)
+ // Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
+ while (p_desc_end - p_desc > 0)
{
// We assume the number of alternate settings is increasing thus we return the index of alternate setting zero!
if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const * )p_desc)->bAlternateSetting == 0)
@@ -2444,7 +2457,8 @@ static bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID, uint8_t *
uint8_t const *p_desc_end = ((audio_desc_cs_ac_interface_t const *)p_desc)->wTotalLength + p_desc;
p_desc = tu_desc_next(p_desc); // Get past CS AC descriptor
- while (p_desc < p_desc_end)
+ // Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
+ while (p_desc_end - p_desc > 0)
{
if (p_desc[3] == entityID) // Entity IDs are always at offset 3
{
@@ -2468,8 +2482,8 @@ static bool audiod_verify_itf_exists(uint8_t itf, uint8_t *func_id)
// Get pointer at beginning and end
uint8_t const *p_desc = _audiod_fct[i].p_desc;
uint8_t const *p_desc_end = _audiod_fct[i].p_desc + _audiod_fct[i].desc_length - TUD_AUDIO_DESC_IAD_LEN;
-
- while (p_desc < p_desc_end)
+ // Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
+ while (p_desc_end - p_desc > 0)
{
if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const *)_audiod_fct[i].p_desc)->bInterfaceNumber == itf)
{
@@ -2497,7 +2511,8 @@ static bool audiod_verify_ep_exists(uint8_t ep, uint8_t *func_id)
uint8_t const *p_desc = tu_desc_next(_audiod_fct[i].p_desc);
p_desc += ((audio_desc_cs_ac_interface_t const *)p_desc)->wTotalLength;
- while (p_desc < p_desc_end)
+ // Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
+ while (p_desc_end - p_desc > 0)
{
if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT && ((tusb_desc_endpoint_t const * )p_desc)->bEndpointAddress == ep)
{
@@ -2528,8 +2543,8 @@ static void audiod_parse_for_AS_params(audiod_function_t* audio, uint8_t const *
#endif
p_desc = tu_desc_next(p_desc); // Exclude standard AS interface descriptor of current alternate interface descriptor
-
- while (p_desc < p_desc_end)
+ // Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
+ while (p_desc_end - p_desc > 0)
{
// Abort if follow up descriptor is a new standard interface descriptor - indicates the last AS descriptor was already finished
if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE) break;
@@ -2578,14 +2593,14 @@ static void audiod_parse_for_AS_params(audiod_function_t* audio, uint8_t const *
#if CFG_TUD_AUDIO_ENABLE_EP_IN
if (as_itf == audio->ep_in_as_intf_num)
{
- audio->n_bytes_per_sampe_tx = ((audio_desc_type_I_format_t const * )p_desc)->bSubslotSize;
+ audio->n_bytes_per_sample_tx = ((audio_desc_type_I_format_t const * )p_desc)->bSubslotSize;
}
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
if (as_itf == audio->ep_out_as_intf_num)
{
- audio->n_bytes_per_sampe_rx = ((audio_desc_type_I_format_t const * )p_desc)->bSubslotSize;
+ audio->n_bytes_per_sample_rx = ((audio_desc_type_I_format_t const * )p_desc)->bSubslotSize;
}
#endif
}
@@ -2604,7 +2619,7 @@ static bool audiod_calc_tx_packet_sz(audiod_function_t* audio)
{
TU_VERIFY(audio->format_type_tx == AUDIO_FORMAT_TYPE_I);
TU_VERIFY(audio->n_channels_tx);
- TU_VERIFY(audio->n_bytes_per_sampe_tx);
+ TU_VERIFY(audio->n_bytes_per_sample_tx);
TU_VERIFY(audio->interval_tx);
TU_VERIFY(audio->sample_rate_tx);
@@ -2613,9 +2628,9 @@ static bool audiod_calc_tx_packet_sz(audiod_function_t* audio)
const uint16_t sample_normimal = (uint16_t)(audio->sample_rate_tx * interval / ((tud_speed_get() == TUSB_SPEED_FULL) ? 1000 : 8000));
const uint16_t sample_reminder = (uint16_t)(audio->sample_rate_tx * interval % ((tud_speed_get() == TUSB_SPEED_FULL) ? 1000 : 8000));
- const uint16_t packet_sz_tx_min = (uint16_t)((sample_normimal - 1) * audio->n_channels_tx * audio->n_bytes_per_sampe_tx);
- const uint16_t packet_sz_tx_norm = (uint16_t)(sample_normimal * audio->n_channels_tx * audio->n_bytes_per_sampe_tx);
- const uint16_t packet_sz_tx_max = (uint16_t)((sample_normimal + 1) * audio->n_channels_tx * audio->n_bytes_per_sampe_tx);
+ const uint16_t packet_sz_tx_min = (uint16_t)((sample_normimal - 1) * audio->n_channels_tx * audio->n_bytes_per_sample_tx);
+ const uint16_t packet_sz_tx_norm = (uint16_t)(sample_normimal * audio->n_channels_tx * audio->n_bytes_per_sample_tx);
+ const uint16_t packet_sz_tx_max = (uint16_t)((sample_normimal + 1) * audio->n_channels_tx * audio->n_bytes_per_sample_tx);
// Endpoint size must larger than packet size
TU_ASSERT(packet_sz_tx_max <= audio->ep_in_sz);
diff --git a/src/class/cdc/cdc_device.c b/src/class/cdc/cdc_device.c
index 7ef0530ec..ebc408f5a 100644
--- a/src/class/cdc/cdc_device.c
+++ b/src/class/cdc/cdc_device.c
@@ -43,13 +43,9 @@
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
-enum
-{
- BULK_PACKET_SIZE = (TUD_OPT_HIGH_SPEED ? 512 : 64)
-};
+#define BULK_PACKET_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
-typedef struct
-{
+typedef struct {
uint8_t itf_num;
uint8_t ep_notif;
uint8_t ep_in;
@@ -59,7 +55,7 @@ typedef struct
uint8_t line_state;
/*------------- From this point, data is not cleared by bus reset -------------*/
- char wanted_char;
+ char wanted_char;
TU_ATTR_ALIGNED(4) cdc_line_coding_t line_coding;
// FIFO
@@ -75,19 +71,22 @@ typedef struct
// 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;
+} cdcd_interface_t;
#define ITF_MEM_RESET_SIZE offsetof(cdcd_interface_t, wanted_char)
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
-CFG_TUD_MEM_SECTION tu_static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC];
+CFG_TUD_MEM_SECTION static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC];
+static tud_cdc_configure_fifo_t _cdcd_fifo_cfg;
-static bool _prep_out_transaction (cdcd_interface_t* p_cdc)
-{
+static bool _prep_out_transaction (cdcd_interface_t* p_cdc) {
uint8_t const rhport = 0;
+
+ // Skip if usb is not ready yet
+ TU_VERIFY(tud_ready() && p_cdc->ep_out);
+
uint16_t available = tu_fifo_remaining(&p_cdc->rx_ff);
// Prepare for incoming data but only allow what we can store in the ring buffer.
@@ -102,14 +101,11 @@ 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) )
- {
+ 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
- {
+ }else {
// Release endpoint since we don't make any transfer
usbd_edpt_release(rhport, p_cdc->ep_out);
-
return false;
}
}
@@ -117,51 +113,53 @@ static bool _prep_out_transaction (cdcd_interface_t* p_cdc)
//--------------------------------------------------------------------+
// APPLICATION API
//--------------------------------------------------------------------+
-bool tud_cdc_n_connected(uint8_t itf)
-{
+
+bool tud_cdc_configure_fifo(tud_cdc_configure_fifo_t const* cfg) {
+ TU_VERIFY(cfg);
+ _cdcd_fifo_cfg = (*cfg);
+ return true;
+}
+
+bool tud_cdc_n_ready(uint8_t itf) {
+ return tud_ready() && _cdcd_itf[itf].ep_in != 0 && _cdcd_itf[itf].ep_out != 0;
+}
+
+bool tud_cdc_n_connected(uint8_t itf) {
// DTR (bit 0) active is considered as connected
return tud_ready() && tu_bit_test(_cdcd_itf[itf].line_state, 0);
}
-uint8_t tud_cdc_n_get_line_state (uint8_t itf)
-{
+uint8_t tud_cdc_n_get_line_state(uint8_t itf) {
return _cdcd_itf[itf].line_state;
}
-void tud_cdc_n_get_line_coding (uint8_t itf, cdc_line_coding_t* coding)
-{
+void tud_cdc_n_get_line_coding(uint8_t itf, cdc_line_coding_t* coding) {
(*coding) = _cdcd_itf[itf].line_coding;
}
-void tud_cdc_n_set_wanted_char (uint8_t itf, char wanted)
-{
+void tud_cdc_n_set_wanted_char(uint8_t itf, char wanted) {
_cdcd_itf[itf].wanted_char = wanted;
}
-
//--------------------------------------------------------------------+
// READ API
//--------------------------------------------------------------------+
-uint32_t tud_cdc_n_available(uint8_t itf)
-{
+uint32_t tud_cdc_n_available(uint8_t itf) {
return tu_fifo_count(&_cdcd_itf[itf].rx_ff);
}
-uint32_t tud_cdc_n_read(uint8_t itf, void* buffer, uint32_t bufsize)
-{
+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);
return num_read;
}
-bool tud_cdc_n_peek(uint8_t itf, uint8_t* chr)
-{
+bool tud_cdc_n_peek(uint8_t itf, uint8_t* chr) {
return tu_fifo_peek(&_cdcd_itf[itf].rx_ff, chr);
}
-void tud_cdc_n_read_flush (uint8_t itf)
-{
+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);
@@ -170,45 +168,43 @@ void tud_cdc_n_read_flush (uint8_t 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, void const* 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));
// flush if queue more than packet size
- // may need to suppress -Wunreachable-code since most of the time CFG_TUD_CDC_TX_BUFSIZE < BULK_PACKET_SIZE
- if ( (tu_fifo_count(&p_cdc->tx_ff) >= BULK_PACKET_SIZE) || ((CFG_TUD_CDC_TX_BUFSIZE < BULK_PACKET_SIZE) && tu_fifo_full(&p_cdc->tx_ff)) )
- {
+ if (tu_fifo_count(&p_cdc->tx_ff) >= BULK_PACKET_SIZE
+ #if CFG_TUD_CDC_TX_BUFSIZE < BULK_PACKET_SIZE
+ || tu_fifo_full(&p_cdc->tx_ff) // check full if fifo size is less than packet size
+ #endif
+ ) {
tud_cdc_n_write_flush(itf);
}
return ret;
}
-uint32_t tud_cdc_n_write_flush (uint8_t itf)
-{
+uint32_t tud_cdc_n_write_flush(uint8_t itf) {
cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
// Skip if usb is not ready yet
- TU_VERIFY( tud_ready(), 0 );
+ 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;
// Claim the endpoint
- TU_VERIFY( usbd_edpt_claim(rhport, p_cdc->ep_in), 0 );
+ 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));
- if ( count )
- {
- TU_ASSERT( usbd_edpt_xfer(rhport, p_cdc->ep_in, p_cdc->epin_buf, count), 0 );
+ if (count) {
+ TU_ASSERT(usbd_edpt_xfer(rhport, p_cdc->ep_in, p_cdc->epin_buf, count), 0);
return count;
- }else
- {
+ } else {
// Release endpoint since we don't make any transfer
// Note: data is dropped if terminal is not connected
usbd_edpt_release(rhport, p_cdc->ep_in);
@@ -216,33 +212,30 @@ uint32_t tud_cdc_n_write_flush (uint8_t itf)
}
}
-uint32_t tud_cdc_n_write_available (uint8_t itf)
-{
+uint32_t tud_cdc_n_write_available(uint8_t itf) {
return tu_fifo_remaining(&_cdcd_itf[itf].tx_ff);
}
-bool tud_cdc_n_write_clear (uint8_t itf)
-{
+bool tud_cdc_n_write_clear(uint8_t itf) {
return tu_fifo_clear(&_cdcd_itf[itf].tx_ff);
}
//--------------------------------------------------------------------+
// USBD Driver API
//--------------------------------------------------------------------+
-void cdcd_init(void)
-{
+void cdcd_init(void) {
tu_memclr(_cdcd_itf, sizeof(_cdcd_itf));
+ tu_memclr(&_cdcd_fifo_cfg, sizeof(_cdcd_fifo_cfg));
- for(uint8_t i=0; i<CFG_TUD_CDC; i++)
- {
+ for (uint8_t i = 0; i < CFG_TUD_CDC; i++) {
cdcd_interface_t* p_cdc = &_cdcd_itf[i];
p_cdc->wanted_char = (char) -1;
// default line coding is : stop bit = 1, parity = none, data bits = 8
- p_cdc->line_coding.bit_rate = 115200;
+ p_cdc->line_coding.bit_rate = 115200;
p_cdc->line_coding.stop_bits = 0;
- p_cdc->line_coding.parity = 0;
+ p_cdc->line_coding.parity = 0;
p_cdc->line_coding.data_bits = 8;
// Config RX fifo
@@ -286,33 +279,28 @@ bool cdcd_deinit(void) {
return true;
}
-void cdcd_reset(uint8_t rhport)
-{
+void cdcd_reset(uint8_t rhport) {
(void) rhport;
- for(uint8_t i=0; i<CFG_TUD_CDC; i++)
- {
+ for (uint8_t i = 0; i < CFG_TUD_CDC; i++) {
cdcd_interface_t* p_cdc = &_cdcd_itf[i];
tu_memclr(p_cdc, ITF_MEM_RESET_SIZE);
- tu_fifo_clear(&p_cdc->rx_ff);
- 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, tusb_desc_interface_t const * 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 )
- {
+ 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];
break;
}
@@ -323,39 +311,36 @@ uint16_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1
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 );
+ 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);
+ p_desc = tu_desc_next(p_desc);
}
- if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
- {
+ 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;
+ tusb_desc_endpoint_t const* desc_ep = (tusb_desc_endpoint_t const*) p_desc;
- TU_ASSERT( usbd_edpt_open(rhport, desc_ep), 0 );
+ TU_ASSERT(usbd_edpt_open(rhport, desc_ep), 0);
p_cdc->ep_notif = desc_ep->bEndpointAddress;
drv_len += tu_desc_len(p_desc);
- p_desc = tu_desc_next(p_desc);
+ p_desc = tu_desc_next(p_desc);
}
//------------- 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) )
- {
+ if ((TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) &&
+ (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const*) p_desc)->bInterfaceClass)) {
// next to endpoint descriptor
drv_len += tu_desc_len(p_desc);
- p_desc = tu_desc_next(p_desc);
+ p_desc = tu_desc_next(p_desc);
// Open endpoint pair
- TU_ASSERT( usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &p_cdc->ep_out, &p_cdc->ep_in), 0 );
+ TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &p_cdc->ep_out, &p_cdc->ep_in), 0);
- drv_len += 2*sizeof(tusb_desc_endpoint_t);
+ drv_len += 2 * sizeof(tusb_desc_endpoint_t);
}
// Prepare for incoming data
@@ -367,8 +352,7 @@ 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, tusb_control_request_t const* request) {
// Handle class request only
TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
@@ -376,42 +360,33 @@ bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t
cdcd_interface_t* p_cdc = _cdcd_itf;
// Identify which interface to use
- for ( ; ; itf++, p_cdc++)
- {
+ for (;; itf++, p_cdc++) {
if (itf >= TU_ARRAY_SIZE(_cdcd_itf)) return false;
- if ( p_cdc->itf_num == request->wIndex ) break;
+ if (p_cdc->itf_num == request->wIndex) break;
}
- switch ( request->bRequest )
- {
+ switch (request->bRequest) {
case CDC_REQUEST_SET_LINE_CODING:
- if (stage == CONTROL_STAGE_SETUP)
- {
+ if (stage == CONTROL_STAGE_SETUP) {
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);
}
- else if ( stage == CONTROL_STAGE_ACK)
- {
- if ( tud_cdc_line_coding_cb ) tud_cdc_line_coding_cb(itf, &p_cdc->line_coding);
- }
- break;
+ break;
case CDC_REQUEST_GET_LINE_CODING:
- if (stage == CONTROL_STAGE_SETUP)
- {
+ if (stage == CONTROL_STAGE_SETUP) {
TU_LOG_DRV(" Get Line Coding\r\n");
tud_control_xfer(rhport, request, &p_cdc->line_coding, sizeof(cdc_line_coding_t));
}
- break;
+ break;
case CDC_REQUEST_SET_CONTROL_LINE_STATE:
- if (stage == CONTROL_STAGE_SETUP)
- {
+ if (stage == CONTROL_STAGE_SETUP) {
tud_control_status(rhport, request);
- }
- else if (stage == CONTROL_STAGE_ACK)
- {
+ } else if (stage == CONTROL_STAGE_ACK) {
// CDC PSTN v1.2 section 6.3.12
// Bit 0: Indicates if DTE is present or not.
// This signal corresponds to V.24 signal 108/2 and RS-232 signal DTR (Data Terminal Ready)
@@ -428,61 +403,54 @@ 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;
+ break;
+
case CDC_REQUEST_SEND_BREAK:
- if (stage == CONTROL_STAGE_SETUP)
- {
+ if (stage == CONTROL_STAGE_SETUP) {
tud_control_status(rhport, request);
- }
- else if (stage == CONTROL_STAGE_ACK)
- {
+ } 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;
+ break;
- default: return false; // stall unsupported request
+ default:
+ return false; // stall unsupported request
}
return true;
}
-bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
-{
+bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
(void) result;
uint8_t itf;
cdcd_interface_t* p_cdc;
// Identify which interface to use
- for (itf = 0; itf < CFG_TUD_CDC; itf++)
- {
+ 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);
// Received new data
- if ( ep_addr == p_cdc->ep_out )
- {
+ if (ep_addr == p_cdc->ep_out) {
tu_fifo_write_n(&p_cdc->rx_ff, p_cdc->epout_buf, (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 (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)) {
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);
@@ -491,19 +459,15 @@ bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_
// Data sent to host, we continue to fetch from tx fifo to send.
// Note: This will cause incorrect baudrate set in line coding.
// Though maybe the baudrate is not really important !!!
- if ( ep_addr == p_cdc->ep_in )
- {
+ 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 (0 == tud_cdc_n_write_flush(itf)) {
// If there is no data left, a ZLP should be sent if
// xferred_bytes is multiple of EP Packet size and not zero
- if ( !tu_fifo_count(&p_cdc->tx_ff) && xferred_bytes && (0 == (xferred_bytes & (BULK_PACKET_SIZE-1))) )
- {
- if ( usbd_edpt_claim(rhport, p_cdc->ep_in) )
- {
+ if (!tu_fifo_count(&p_cdc->tx_ff) && xferred_bytes && (0 == (xferred_bytes & (BULK_PACKET_SIZE - 1)))) {
+ if (usbd_edpt_claim(rhport, p_cdc->ep_in)) {
usbd_edpt_xfer(rhport, p_cdc->ep_in, NULL, 0);
}
}
diff --git a/src/class/cdc/cdc_device.h b/src/class/cdc/cdc_device.h
index 20e908451..3ad7c8baf 100644
--- a/src/class/cdc/cdc_device.h
+++ b/src/class/cdc/cdc_device.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_CDC_DEVICE_H_
-#define _TUSB_CDC_DEVICE_H_
+#ifndef TUSB_CDC_DEVICE_H_
+#define TUSB_CDC_DEVICE_H_
#include "cdc.h"
@@ -45,203 +45,166 @@
extern "C" {
#endif
-/** \addtogroup CDC_Serial Serial
- * @{
- * \defgroup CDC_Serial_Device Device
- * @{ */
+//--------------------------------------------------------------------+
+// Driver Configuration
+//--------------------------------------------------------------------+
+
+typedef struct TU_ATTR_PACKED {
+ uint8_t rx_persistent : 1; // keep rx fifo on bus reset or disconnect
+ uint8_t tx_persistent : 1; // keep tx fifo on bus reset or disconnect
+} tud_cdc_configure_fifo_t;
+
+// Configure CDC FIFOs behavior
+bool tud_cdc_configure_fifo(tud_cdc_configure_fifo_t const* cfg);
//--------------------------------------------------------------------+
-// Application API (Multiple Ports)
-// CFG_TUD_CDC > 1
+// Application API (Multiple Ports) i.e. CFG_TUD_CDC > 1
//--------------------------------------------------------------------+
+// Check if interface is ready
+bool tud_cdc_n_ready(uint8_t itf);
+
// Check if terminal is connected to this port
-bool tud_cdc_n_connected (uint8_t itf);
+bool tud_cdc_n_connected(uint8_t itf);
// Get current line state. Bit 0: DTR (Data Terminal Ready), Bit 1: RTS (Request to Send)
-uint8_t tud_cdc_n_get_line_state (uint8_t itf);
+uint8_t tud_cdc_n_get_line_state(uint8_t itf);
// Get current line encoding: bit rate, stop bits parity etc ..
-void tud_cdc_n_get_line_coding (uint8_t itf, cdc_line_coding_t* coding);
+void tud_cdc_n_get_line_coding(uint8_t itf, cdc_line_coding_t* coding);
// Set special character that will trigger tud_cdc_rx_wanted_cb() callback on receiving
-void tud_cdc_n_set_wanted_char (uint8_t itf, char wanted);
+void tud_cdc_n_set_wanted_char(uint8_t itf, char wanted);
// Get the number of bytes available for reading
-uint32_t tud_cdc_n_available (uint8_t itf);
+uint32_t tud_cdc_n_available(uint8_t itf);
// Read received bytes
-uint32_t tud_cdc_n_read (uint8_t itf, void* buffer, uint32_t bufsize);
+uint32_t tud_cdc_n_read(uint8_t itf, void* buffer, uint32_t bufsize);
// Read a byte, return -1 if there is none
-static inline
-int32_t tud_cdc_n_read_char (uint8_t itf);
+TU_ATTR_ALWAYS_INLINE static inline int32_t tud_cdc_n_read_char(uint8_t itf) {
+ uint8_t ch;
+ return tud_cdc_n_read(itf, &ch, 1) ? (int32_t) ch : -1;
+}
// Clear the received FIFO
-void tud_cdc_n_read_flush (uint8_t itf);
+void tud_cdc_n_read_flush(uint8_t itf);
// Get a byte from FIFO without removing it
-bool tud_cdc_n_peek (uint8_t itf, uint8_t* ui8);
+bool tud_cdc_n_peek(uint8_t itf, uint8_t* ui8);
// Write bytes to TX FIFO, data may remain in the FIFO for a while
-uint32_t tud_cdc_n_write (uint8_t itf, void const* buffer, uint32_t bufsize);
+uint32_t tud_cdc_n_write(uint8_t itf, void const* buffer, uint32_t bufsize);
// Write a byte
-static inline
-uint32_t tud_cdc_n_write_char (uint8_t itf, char ch);
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_n_write_char(uint8_t itf, char ch) {
+ return tud_cdc_n_write(itf, &ch, 1);
+}
// Write a null-terminated string
-static inline
-uint32_t tud_cdc_n_write_str (uint8_t itf, char const* str);
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_n_write_str(uint8_t itf, char const* str) {
+ return tud_cdc_n_write(itf, str, strlen(str));
+}
// Force sending data if possible, return number of forced bytes
-uint32_t tud_cdc_n_write_flush (uint8_t itf);
+uint32_t tud_cdc_n_write_flush(uint8_t itf);
// Return the number of bytes (characters) available for writing to TX FIFO buffer in a single n_write operation.
-uint32_t tud_cdc_n_write_available (uint8_t itf);
+uint32_t tud_cdc_n_write_available(uint8_t itf);
// Clear the transmit FIFO
-bool tud_cdc_n_write_clear (uint8_t itf);
+bool tud_cdc_n_write_clear(uint8_t itf);
//--------------------------------------------------------------------+
// Application API (Single Port)
//--------------------------------------------------------------------+
-static inline bool tud_cdc_connected (void);
-static inline uint8_t tud_cdc_get_line_state (void);
-static inline void tud_cdc_get_line_coding (cdc_line_coding_t* coding);
-static inline void tud_cdc_set_wanted_char (char wanted);
-
-static inline uint32_t tud_cdc_available (void);
-static inline int32_t tud_cdc_read_char (void);
-static inline uint32_t tud_cdc_read (void* buffer, uint32_t bufsize);
-static inline void tud_cdc_read_flush (void);
-static inline bool tud_cdc_peek (uint8_t* ui8);
-
-static inline uint32_t tud_cdc_write_char (char ch);
-static inline uint32_t tud_cdc_write (void const* buffer, uint32_t bufsize);
-static inline uint32_t tud_cdc_write_str (char const* str);
-static inline uint32_t tud_cdc_write_flush (void);
-static inline uint32_t tud_cdc_write_available (void);
-static inline bool tud_cdc_write_clear (void);
-
-//--------------------------------------------------------------------+
-// Application Callback API (weak is optional)
-//--------------------------------------------------------------------+
-// Invoked when received new data
-TU_ATTR_WEAK void tud_cdc_rx_cb(uint8_t itf);
-
-// Invoked when received `wanted_char`
-TU_ATTR_WEAK void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char);
-
-// Invoked when a TX is complete and therefore space becomes available in TX buffer
-TU_ATTR_WEAK void tud_cdc_tx_complete_cb(uint8_t itf);
-
-// Invoked when line state DTR & RTS are changed via SET_CONTROL_LINE_STATE
-TU_ATTR_WEAK void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts);
-
-// Invoked when line coding is change via SET_LINE_CODING
-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
-TU_ATTR_WEAK void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms);
-
-//--------------------------------------------------------------------+
-// Inline Functions
-//--------------------------------------------------------------------+
-static inline int32_t tud_cdc_n_read_char (uint8_t itf)
-{
- uint8_t ch;
- return tud_cdc_n_read(itf, &ch, 1) ? (int32_t) ch : -1;
-}
-
-static inline uint32_t tud_cdc_n_write_char(uint8_t itf, char ch)
-{
- return tud_cdc_n_write(itf, &ch, 1);
+TU_ATTR_ALWAYS_INLINE static inline bool tud_cdc_ready(void) {
+ return tud_cdc_n_ready(0);
}
-static inline uint32_t tud_cdc_n_write_str (uint8_t itf, char const* str)
-{
- return tud_cdc_n_write(itf, str, strlen(str));
-}
-
-static inline bool tud_cdc_connected (void)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool tud_cdc_connected(void) {
return tud_cdc_n_connected(0);
}
-static inline uint8_t tud_cdc_get_line_state (void)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint8_t tud_cdc_get_line_state(void) {
return tud_cdc_n_get_line_state(0);
}
-static inline void tud_cdc_get_line_coding (cdc_line_coding_t* coding)
-{
+TU_ATTR_ALWAYS_INLINE static inline void tud_cdc_get_line_coding(cdc_line_coding_t* coding) {
tud_cdc_n_get_line_coding(0, coding);
}
-static inline void tud_cdc_set_wanted_char (char wanted)
-{
+TU_ATTR_ALWAYS_INLINE static inline void tud_cdc_set_wanted_char(char wanted) {
tud_cdc_n_set_wanted_char(0, wanted);
}
-static inline uint32_t tud_cdc_available (void)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_available(void) {
return tud_cdc_n_available(0);
}
-static inline int32_t tud_cdc_read_char (void)
-{
+TU_ATTR_ALWAYS_INLINE static inline int32_t tud_cdc_read_char(void) {
return tud_cdc_n_read_char(0);
}
-static inline uint32_t tud_cdc_read (void* buffer, uint32_t bufsize)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_read(void* buffer, uint32_t bufsize) {
return tud_cdc_n_read(0, buffer, bufsize);
}
-static inline void tud_cdc_read_flush (void)
-{
+TU_ATTR_ALWAYS_INLINE static inline void tud_cdc_read_flush(void) {
tud_cdc_n_read_flush(0);
}
-static inline bool tud_cdc_peek (uint8_t* ui8)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool tud_cdc_peek(uint8_t* ui8) {
return tud_cdc_n_peek(0, ui8);
}
-static inline uint32_t tud_cdc_write_char (char ch)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_write_char(char ch) {
return tud_cdc_n_write_char(0, ch);
}
-static inline uint32_t tud_cdc_write (void const* buffer, uint32_t bufsize)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_write(void const* buffer, uint32_t bufsize) {
return tud_cdc_n_write(0, buffer, bufsize);
}
-static inline uint32_t tud_cdc_write_str (char const* str)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_write_str(char const* str) {
return tud_cdc_n_write_str(0, str);
}
-static inline uint32_t tud_cdc_write_flush (void)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_write_flush(void) {
return tud_cdc_n_write_flush(0);
}
-static inline uint32_t tud_cdc_write_available(void)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_write_available(void) {
return tud_cdc_n_write_available(0);
}
-static inline bool tud_cdc_write_clear(void)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool tud_cdc_write_clear(void) {
return tud_cdc_n_write_clear(0);
}
-/** @} */
-/** @} */
+//--------------------------------------------------------------------+
+// Application Callback API (weak is optional)
+//--------------------------------------------------------------------+
+
+// Invoked when received new data
+TU_ATTR_WEAK void tud_cdc_rx_cb(uint8_t itf);
+
+// Invoked when received `wanted_char`
+TU_ATTR_WEAK void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char);
+
+// Invoked when a TX is complete and therefore space becomes available in TX buffer
+TU_ATTR_WEAK void tud_cdc_tx_complete_cb(uint8_t itf);
+
+// Invoked when line state DTR & RTS are changed via SET_CONTROL_LINE_STATE
+TU_ATTR_WEAK void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts);
+
+// Invoked when line coding is change via SET_LINE_CODING
+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
+TU_ATTR_WEAK void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms);
//--------------------------------------------------------------------+
// INTERNAL USBD-CLASS DRIVER API
diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c
index 5bdd41f1f..133a10f6e 100644
--- a/src/class/cdc/cdc_host.c
+++ b/src/class/cdc/cdc_host.c
@@ -1079,7 +1079,7 @@ static uint32_t ftdi_232bm_baud_to_divisor(uint32_t baud) {
static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
uint16_t const divisor = (uint16_t) ftdi_232bm_baud_to_divisor(baudrate);
- TU_LOG_DRV("CDC FTDI Set BaudRate = %lu, divisor = 0x%04x\r\n", baudrate, divisor);
+ TU_LOG_DRV("CDC FTDI Set BaudRate = %" PRIu32 ", divisor = 0x%04x\r\n", baudrate, divisor);
p_cdc->user_control_cb = complete_cb;
p_cdc->requested_line_coding.bit_rate = baudrate;
@@ -1222,7 +1222,7 @@ static bool cp210x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t co
}
static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- TU_LOG_DRV("CDC CP210x Set BaudRate = %lu\r\n", baudrate);
+ TU_LOG_DRV("CDC CP210x Set BaudRate = %" PRIu32 "\r\n", baudrate);
uint32_t baud_le = tu_htole32(baudrate);
p_cdc->user_control_cb = complete_cb;
return cp210x_set_request(p_cdc, CP210X_SET_BAUDRATE, 0, (uint8_t *) &baud_le, 4,
diff --git a/src/class/hid/hid.h b/src/class/hid/hid.h
index fcaab7a50..c2b5a8a48 100644
--- a/src/class/hid/hid.h
+++ b/src/class/hid/hid.h
@@ -366,177 +366,224 @@ typedef enum
//--------------------------------------------------------------------+
// HID KEYCODE
//--------------------------------------------------------------------+
-#define HID_KEY_NONE 0x00
-#define HID_KEY_A 0x04
-#define HID_KEY_B 0x05
-#define HID_KEY_C 0x06
-#define HID_KEY_D 0x07
-#define HID_KEY_E 0x08
-#define HID_KEY_F 0x09
-#define HID_KEY_G 0x0A
-#define HID_KEY_H 0x0B
-#define HID_KEY_I 0x0C
-#define HID_KEY_J 0x0D
-#define HID_KEY_K 0x0E
-#define HID_KEY_L 0x0F
-#define HID_KEY_M 0x10
-#define HID_KEY_N 0x11
-#define HID_KEY_O 0x12
-#define HID_KEY_P 0x13
-#define HID_KEY_Q 0x14
-#define HID_KEY_R 0x15
-#define HID_KEY_S 0x16
-#define HID_KEY_T 0x17
-#define HID_KEY_U 0x18
-#define HID_KEY_V 0x19
-#define HID_KEY_W 0x1A
-#define HID_KEY_X 0x1B
-#define HID_KEY_Y 0x1C
-#define HID_KEY_Z 0x1D
-#define HID_KEY_1 0x1E
-#define HID_KEY_2 0x1F
-#define HID_KEY_3 0x20
-#define HID_KEY_4 0x21
-#define HID_KEY_5 0x22
-#define HID_KEY_6 0x23
-#define HID_KEY_7 0x24
-#define HID_KEY_8 0x25
-#define HID_KEY_9 0x26
-#define HID_KEY_0 0x27
-#define HID_KEY_ENTER 0x28
-#define HID_KEY_ESCAPE 0x29
-#define HID_KEY_BACKSPACE 0x2A
-#define HID_KEY_TAB 0x2B
-#define HID_KEY_SPACE 0x2C
-#define HID_KEY_MINUS 0x2D
-#define HID_KEY_EQUAL 0x2E
-#define HID_KEY_BRACKET_LEFT 0x2F
-#define HID_KEY_BRACKET_RIGHT 0x30
-#define HID_KEY_BACKSLASH 0x31
-#define HID_KEY_EUROPE_1 0x32
-#define HID_KEY_SEMICOLON 0x33
-#define HID_KEY_APOSTROPHE 0x34
-#define HID_KEY_GRAVE 0x35
-#define HID_KEY_COMMA 0x36
-#define HID_KEY_PERIOD 0x37
-#define HID_KEY_SLASH 0x38
-#define HID_KEY_CAPS_LOCK 0x39
-#define HID_KEY_F1 0x3A
-#define HID_KEY_F2 0x3B
-#define HID_KEY_F3 0x3C
-#define HID_KEY_F4 0x3D
-#define HID_KEY_F5 0x3E
-#define HID_KEY_F6 0x3F
-#define HID_KEY_F7 0x40
-#define HID_KEY_F8 0x41
-#define HID_KEY_F9 0x42
-#define HID_KEY_F10 0x43
-#define HID_KEY_F11 0x44
-#define HID_KEY_F12 0x45
-#define HID_KEY_PRINT_SCREEN 0x46
-#define HID_KEY_SCROLL_LOCK 0x47
-#define HID_KEY_PAUSE 0x48
-#define HID_KEY_INSERT 0x49
-#define HID_KEY_HOME 0x4A
-#define HID_KEY_PAGE_UP 0x4B
-#define HID_KEY_DELETE 0x4C
-#define HID_KEY_END 0x4D
-#define HID_KEY_PAGE_DOWN 0x4E
-#define HID_KEY_ARROW_RIGHT 0x4F
-#define HID_KEY_ARROW_LEFT 0x50
-#define HID_KEY_ARROW_DOWN 0x51
-#define HID_KEY_ARROW_UP 0x52
-#define HID_KEY_NUM_LOCK 0x53
-#define HID_KEY_KEYPAD_DIVIDE 0x54
-#define HID_KEY_KEYPAD_MULTIPLY 0x55
-#define HID_KEY_KEYPAD_SUBTRACT 0x56
-#define HID_KEY_KEYPAD_ADD 0x57
-#define HID_KEY_KEYPAD_ENTER 0x58
-#define HID_KEY_KEYPAD_1 0x59
-#define HID_KEY_KEYPAD_2 0x5A
-#define HID_KEY_KEYPAD_3 0x5B
-#define HID_KEY_KEYPAD_4 0x5C
-#define HID_KEY_KEYPAD_5 0x5D
-#define HID_KEY_KEYPAD_6 0x5E
-#define HID_KEY_KEYPAD_7 0x5F
-#define HID_KEY_KEYPAD_8 0x60
-#define HID_KEY_KEYPAD_9 0x61
-#define HID_KEY_KEYPAD_0 0x62
-#define HID_KEY_KEYPAD_DECIMAL 0x63
-#define HID_KEY_EUROPE_2 0x64
-#define HID_KEY_APPLICATION 0x65
-#define HID_KEY_POWER 0x66
-#define HID_KEY_KEYPAD_EQUAL 0x67
-#define HID_KEY_F13 0x68
-#define HID_KEY_F14 0x69
-#define HID_KEY_F15 0x6A
-#define HID_KEY_F16 0x6B
-#define HID_KEY_F17 0x6C
-#define HID_KEY_F18 0x6D
-#define HID_KEY_F19 0x6E
-#define HID_KEY_F20 0x6F
-#define HID_KEY_F21 0x70
-#define HID_KEY_F22 0x71
-#define HID_KEY_F23 0x72
-#define HID_KEY_F24 0x73
-#define HID_KEY_EXECUTE 0x74
-#define HID_KEY_HELP 0x75
-#define HID_KEY_MENU 0x76
-#define HID_KEY_SELECT 0x77
-#define HID_KEY_STOP 0x78
-#define HID_KEY_AGAIN 0x79
-#define HID_KEY_UNDO 0x7A
-#define HID_KEY_CUT 0x7B
-#define HID_KEY_COPY 0x7C
-#define HID_KEY_PASTE 0x7D
-#define HID_KEY_FIND 0x7E
-#define HID_KEY_MUTE 0x7F
-#define HID_KEY_VOLUME_UP 0x80
-#define HID_KEY_VOLUME_DOWN 0x81
-#define HID_KEY_LOCKING_CAPS_LOCK 0x82
-#define HID_KEY_LOCKING_NUM_LOCK 0x83
-#define HID_KEY_LOCKING_SCROLL_LOCK 0x84
-#define HID_KEY_KEYPAD_COMMA 0x85
-#define HID_KEY_KEYPAD_EQUAL_SIGN 0x86
-#define HID_KEY_KANJI1 0x87
-#define HID_KEY_KANJI2 0x88
-#define HID_KEY_KANJI3 0x89
-#define HID_KEY_KANJI4 0x8A
-#define HID_KEY_KANJI5 0x8B
-#define HID_KEY_KANJI6 0x8C
-#define HID_KEY_KANJI7 0x8D
-#define HID_KEY_KANJI8 0x8E
-#define HID_KEY_KANJI9 0x8F
-#define HID_KEY_LANG1 0x90
-#define HID_KEY_LANG2 0x91
-#define HID_KEY_LANG3 0x92
-#define HID_KEY_LANG4 0x93
-#define HID_KEY_LANG5 0x94
-#define HID_KEY_LANG6 0x95
-#define HID_KEY_LANG7 0x96
-#define HID_KEY_LANG8 0x97
-#define HID_KEY_LANG9 0x98
-#define HID_KEY_ALTERNATE_ERASE 0x99
-#define HID_KEY_SYSREQ_ATTENTION 0x9A
-#define HID_KEY_CANCEL 0x9B
-#define HID_KEY_CLEAR 0x9C
-#define HID_KEY_PRIOR 0x9D
-#define HID_KEY_RETURN 0x9E
-#define HID_KEY_SEPARATOR 0x9F
-#define HID_KEY_OUT 0xA0
-#define HID_KEY_OPER 0xA1
-#define HID_KEY_CLEAR_AGAIN 0xA2
-#define HID_KEY_CRSEL_PROPS 0xA3
-#define HID_KEY_EXSEL 0xA4
-// RESERVED 0xA5-DF
-#define HID_KEY_CONTROL_LEFT 0xE0
-#define HID_KEY_SHIFT_LEFT 0xE1
-#define HID_KEY_ALT_LEFT 0xE2
-#define HID_KEY_GUI_LEFT 0xE3
-#define HID_KEY_CONTROL_RIGHT 0xE4
-#define HID_KEY_SHIFT_RIGHT 0xE5
-#define HID_KEY_ALT_RIGHT 0xE6
-#define HID_KEY_GUI_RIGHT 0xE7
+#define HID_KEY_NONE 0x00
+#define HID_KEY_A 0x04
+#define HID_KEY_B 0x05
+#define HID_KEY_C 0x06
+#define HID_KEY_D 0x07
+#define HID_KEY_E 0x08
+#define HID_KEY_F 0x09
+#define HID_KEY_G 0x0A
+#define HID_KEY_H 0x0B
+#define HID_KEY_I 0x0C
+#define HID_KEY_J 0x0D
+#define HID_KEY_K 0x0E
+#define HID_KEY_L 0x0F
+#define HID_KEY_M 0x10
+#define HID_KEY_N 0x11
+#define HID_KEY_O 0x12
+#define HID_KEY_P 0x13
+#define HID_KEY_Q 0x14
+#define HID_KEY_R 0x15
+#define HID_KEY_S 0x16
+#define HID_KEY_T 0x17
+#define HID_KEY_U 0x18
+#define HID_KEY_V 0x19
+#define HID_KEY_W 0x1A
+#define HID_KEY_X 0x1B
+#define HID_KEY_Y 0x1C
+#define HID_KEY_Z 0x1D
+#define HID_KEY_1 0x1E
+#define HID_KEY_2 0x1F
+#define HID_KEY_3 0x20
+#define HID_KEY_4 0x21
+#define HID_KEY_5 0x22
+#define HID_KEY_6 0x23
+#define HID_KEY_7 0x24
+#define HID_KEY_8 0x25
+#define HID_KEY_9 0x26
+#define HID_KEY_0 0x27
+#define HID_KEY_ENTER 0x28
+#define HID_KEY_ESCAPE 0x29
+#define HID_KEY_BACKSPACE 0x2A
+#define HID_KEY_TAB 0x2B
+#define HID_KEY_SPACE 0x2C
+#define HID_KEY_MINUS 0x2D
+#define HID_KEY_EQUAL 0x2E
+#define HID_KEY_BRACKET_LEFT 0x2F
+#define HID_KEY_BRACKET_RIGHT 0x30
+#define HID_KEY_BACKSLASH 0x31
+#define HID_KEY_EUROPE_1 0x32
+#define HID_KEY_SEMICOLON 0x33
+#define HID_KEY_APOSTROPHE 0x34
+#define HID_KEY_GRAVE 0x35
+#define HID_KEY_COMMA 0x36
+#define HID_KEY_PERIOD 0x37
+#define HID_KEY_SLASH 0x38
+#define HID_KEY_CAPS_LOCK 0x39
+#define HID_KEY_F1 0x3A
+#define HID_KEY_F2 0x3B
+#define HID_KEY_F3 0x3C
+#define HID_KEY_F4 0x3D
+#define HID_KEY_F5 0x3E
+#define HID_KEY_F6 0x3F
+#define HID_KEY_F7 0x40
+#define HID_KEY_F8 0x41
+#define HID_KEY_F9 0x42
+#define HID_KEY_F10 0x43
+#define HID_KEY_F11 0x44
+#define HID_KEY_F12 0x45
+#define HID_KEY_PRINT_SCREEN 0x46
+#define HID_KEY_SCROLL_LOCK 0x47
+#define HID_KEY_PAUSE 0x48
+#define HID_KEY_INSERT 0x49
+#define HID_KEY_HOME 0x4A
+#define HID_KEY_PAGE_UP 0x4B
+#define HID_KEY_DELETE 0x4C
+#define HID_KEY_END 0x4D
+#define HID_KEY_PAGE_DOWN 0x4E
+#define HID_KEY_ARROW_RIGHT 0x4F
+#define HID_KEY_ARROW_LEFT 0x50
+#define HID_KEY_ARROW_DOWN 0x51
+#define HID_KEY_ARROW_UP 0x52
+#define HID_KEY_NUM_LOCK 0x53
+#define HID_KEY_KEYPAD_DIVIDE 0x54
+#define HID_KEY_KEYPAD_MULTIPLY 0x55
+#define HID_KEY_KEYPAD_SUBTRACT 0x56
+#define HID_KEY_KEYPAD_ADD 0x57
+#define HID_KEY_KEYPAD_ENTER 0x58
+#define HID_KEY_KEYPAD_1 0x59
+#define HID_KEY_KEYPAD_2 0x5A
+#define HID_KEY_KEYPAD_3 0x5B
+#define HID_KEY_KEYPAD_4 0x5C
+#define HID_KEY_KEYPAD_5 0x5D
+#define HID_KEY_KEYPAD_6 0x5E
+#define HID_KEY_KEYPAD_7 0x5F
+#define HID_KEY_KEYPAD_8 0x60
+#define HID_KEY_KEYPAD_9 0x61
+#define HID_KEY_KEYPAD_0 0x62
+#define HID_KEY_KEYPAD_DECIMAL 0x63
+#define HID_KEY_EUROPE_2 0x64
+#define HID_KEY_APPLICATION 0x65
+#define HID_KEY_POWER 0x66
+#define HID_KEY_KEYPAD_EQUAL 0x67
+#define HID_KEY_F13 0x68
+#define HID_KEY_F14 0x69
+#define HID_KEY_F15 0x6A
+#define HID_KEY_F16 0x6B
+#define HID_KEY_F17 0x6C
+#define HID_KEY_F18 0x6D
+#define HID_KEY_F19 0x6E
+#define HID_KEY_F20 0x6F
+#define HID_KEY_F21 0x70
+#define HID_KEY_F22 0x71
+#define HID_KEY_F23 0x72
+#define HID_KEY_F24 0x73
+#define HID_KEY_EXECUTE 0x74
+#define HID_KEY_HELP 0x75
+#define HID_KEY_MENU 0x76
+#define HID_KEY_SELECT 0x77
+#define HID_KEY_STOP 0x78
+#define HID_KEY_AGAIN 0x79
+#define HID_KEY_UNDO 0x7A
+#define HID_KEY_CUT 0x7B
+#define HID_KEY_COPY 0x7C
+#define HID_KEY_PASTE 0x7D
+#define HID_KEY_FIND 0x7E
+#define HID_KEY_MUTE 0x7F
+#define HID_KEY_VOLUME_UP 0x80
+#define HID_KEY_VOLUME_DOWN 0x81
+#define HID_KEY_LOCKING_CAPS_LOCK 0x82
+#define HID_KEY_LOCKING_NUM_LOCK 0x83
+#define HID_KEY_LOCKING_SCROLL_LOCK 0x84
+#define HID_KEY_KEYPAD_COMMA 0x85
+#define HID_KEY_KEYPAD_EQUAL_SIGN 0x86
+#define HID_KEY_KANJI1 0x87
+#define HID_KEY_KANJI2 0x88
+#define HID_KEY_KANJI3 0x89
+#define HID_KEY_KANJI4 0x8A
+#define HID_KEY_KANJI5 0x8B
+#define HID_KEY_KANJI6 0x8C
+#define HID_KEY_KANJI7 0x8D
+#define HID_KEY_KANJI8 0x8E
+#define HID_KEY_KANJI9 0x8F
+#define HID_KEY_LANG1 0x90
+#define HID_KEY_LANG2 0x91
+#define HID_KEY_LANG3 0x92
+#define HID_KEY_LANG4 0x93
+#define HID_KEY_LANG5 0x94
+#define HID_KEY_LANG6 0x95
+#define HID_KEY_LANG7 0x96
+#define HID_KEY_LANG8 0x97
+#define HID_KEY_LANG9 0x98
+#define HID_KEY_ALTERNATE_ERASE 0x99
+#define HID_KEY_SYSREQ_ATTENTION 0x9A
+#define HID_KEY_CANCEL 0x9B
+#define HID_KEY_CLEAR 0x9C
+#define HID_KEY_PRIOR 0x9D
+#define HID_KEY_RETURN 0x9E
+#define HID_KEY_SEPARATOR 0x9F
+#define HID_KEY_OUT 0xA0
+#define HID_KEY_OPER 0xA1
+#define HID_KEY_CLEAR_AGAIN 0xA2
+#define HID_KEY_CRSEL_PROPS 0xA3
+#define HID_KEY_EXSEL 0xA4
+// RESERVED 0xA5-AF
+#define HID_KEY_KEYPAD_00 0xB0
+#define HID_KEY_KEYPAD_000 0xB1
+#define HID_KEY_THOUSANDS_SEPARATOR 0xB2
+#define HID_KEY_DECIMAL_SEPARATOR 0xB3
+#define HID_KEY_CURRENCY_UNIT 0xB4
+#define HID_KEY_CURRENCY_SUBUNIT 0xB5
+#define HID_KEY_KEYPAD_LEFT_PARENTHESIS 0xB6
+#define HID_KEY_KEYPAD_RIGHT_PARENTHESIS 0xB7
+#define HID_KEY_KEYPAD_LEFT_BRACE 0xB8
+#define HID_KEY_KEYPAD_RIGHT_BRACE 0xB9
+#define HID_KEY_KEYPAD_TAB 0xBA
+#define HID_KEY_KEYPAD_BACKSPACE 0xBB
+#define HID_KEY_KEYPAD_A 0xBC
+#define HID_KEY_KEYPAD_B 0xBD
+#define HID_KEY_KEYPAD_C 0xBE
+#define HID_KEY_KEYPAD_D 0xBF
+#define HID_KEY_KEYPAD_E 0xC0
+#define HID_KEY_KEYPAD_F 0xC1
+#define HID_KEY_KEYPAD_XOR 0xC2
+#define HID_KEY_KEYPAD_CARET 0xC3
+#define HID_KEY_KEYPAD_PERCENT 0xC4
+#define HID_KEY_KEYPAD_LESS_THAN 0xC5
+#define HID_KEY_KEYPAD_GREATER_THAN 0xC6
+#define HID_KEY_KEYPAD_AMPERSAND 0xC7
+#define HID_KEY_KEYPAD_DOUBLE_AMPERSAND 0xC8
+#define HID_KEY_KEYPAD_VERTICAL_BAR 0xC9
+#define HID_KEY_KEYPAD_DOUBLE_VERTICAL_BAR 0xCA
+#define HID_KEY_KEYPAD_COLON 0xCB
+#define HID_KEY_KEYPAD_HASH 0xCC
+#define HID_KEY_KEYPAD_SPACE 0xCD
+#define HID_KEY_KEYPAD_AT 0xCE
+#define HID_KEY_KEYPAD_EXCLAMATION 0xCF
+#define HID_KEY_KEYPAD_MEMORY_STORE 0xD0
+#define HID_KEY_KEYPAD_MEMORY_RECALL 0xD1
+#define HID_KEY_KEYPAD_MEMORY_CLEAR 0xD2
+#define HID_KEY_KEYPAD_MEMORY_ADD 0xD3
+#define HID_KEY_KEYPAD_MEMORY_SUBTRACT 0xD4
+#define HID_KEY_KEYPAD_MEMORY_MULTIPLY 0xD5
+#define HID_KEY_KEYPAD_MEMORY_DIVIDE 0xD6
+#define HID_KEY_KEYPAD_PLUS_MINUS 0xD7
+#define HID_KEY_KEYPAD_CLEAR 0xD8
+#define HID_KEY_KEYPAD_CLEAR_ENTRY 0xD9
+#define HID_KEY_KEYPAD_BINARY 0xDA
+#define HID_KEY_KEYPAD_OCTAL 0xDB
+#define HID_KEY_KEYPAD_DECIMAL_2 0xDC
+#define HID_KEY_KEYPAD_HEXADECIMAL 0xDD
+// RESERVED 0xDE-DF
+#define HID_KEY_CONTROL_LEFT 0xE0
+#define HID_KEY_SHIFT_LEFT 0xE1
+#define HID_KEY_ALT_LEFT 0xE2
+#define HID_KEY_GUI_LEFT 0xE3
+#define HID_KEY_CONTROL_RIGHT 0xE4
+#define HID_KEY_SHIFT_RIGHT 0xE5
+#define HID_KEY_ALT_RIGHT 0xE6
+#define HID_KEY_GUI_RIGHT 0xE7
//--------------------------------------------------------------------+
@@ -697,32 +744,33 @@ enum {
/// HID Usage Table - Table 1: Usage Page Summary
enum {
- HID_USAGE_PAGE_DESKTOP = 0x01,
- HID_USAGE_PAGE_SIMULATE = 0x02,
- HID_USAGE_PAGE_VIRTUAL_REALITY = 0x03,
- HID_USAGE_PAGE_SPORT = 0x04,
- HID_USAGE_PAGE_GAME = 0x05,
- HID_USAGE_PAGE_GENERIC_DEVICE = 0x06,
- HID_USAGE_PAGE_KEYBOARD = 0x07,
- HID_USAGE_PAGE_LED = 0x08,
- HID_USAGE_PAGE_BUTTON = 0x09,
- HID_USAGE_PAGE_ORDINAL = 0x0a,
- HID_USAGE_PAGE_TELEPHONY = 0x0b,
- HID_USAGE_PAGE_CONSUMER = 0x0c,
- HID_USAGE_PAGE_DIGITIZER = 0x0d,
- HID_USAGE_PAGE_PID = 0x0f,
- HID_USAGE_PAGE_UNICODE = 0x10,
- HID_USAGE_PAGE_ALPHA_DISPLAY = 0x14,
- HID_USAGE_PAGE_MEDICAL = 0x40,
- HID_USAGE_PAGE_MONITOR = 0x80, //0x80 - 0x83
- HID_USAGE_PAGE_POWER = 0x84, // 0x084 - 0x87
- HID_USAGE_PAGE_BARCODE_SCANNER = 0x8c,
- HID_USAGE_PAGE_SCALE = 0x8d,
- HID_USAGE_PAGE_MSR = 0x8e,
- HID_USAGE_PAGE_CAMERA = 0x90,
- HID_USAGE_PAGE_ARCADE = 0x91,
- HID_USAGE_PAGE_FIDO = 0xF1D0, // FIDO alliance HID usage page
- HID_USAGE_PAGE_VENDOR = 0xFF00 // 0xFF00 - 0xFFFF
+ HID_USAGE_PAGE_DESKTOP = 0x01,
+ HID_USAGE_PAGE_SIMULATE = 0x02,
+ HID_USAGE_PAGE_VIRTUAL_REALITY = 0x03,
+ HID_USAGE_PAGE_SPORT = 0x04,
+ HID_USAGE_PAGE_GAME = 0x05,
+ HID_USAGE_PAGE_GENERIC_DEVICE = 0x06,
+ HID_USAGE_PAGE_KEYBOARD = 0x07,
+ HID_USAGE_PAGE_LED = 0x08,
+ HID_USAGE_PAGE_BUTTON = 0x09,
+ HID_USAGE_PAGE_ORDINAL = 0x0a,
+ HID_USAGE_PAGE_TELEPHONY = 0x0b,
+ HID_USAGE_PAGE_CONSUMER = 0x0c,
+ HID_USAGE_PAGE_DIGITIZER = 0x0d,
+ HID_USAGE_PAGE_PID = 0x0f,
+ HID_USAGE_PAGE_UNICODE = 0x10,
+ HID_USAGE_PAGE_ALPHA_DISPLAY = 0x14,
+ HID_USAGE_PAGE_MEDICAL = 0x40,
+ HID_USAGE_PAGE_LIGHTING_AND_ILLUMINATION = 0x59,
+ HID_USAGE_PAGE_MONITOR = 0x80, // 0x80 - 0x83
+ HID_USAGE_PAGE_POWER = 0x84, // 0x084 - 0x87
+ HID_USAGE_PAGE_BARCODE_SCANNER = 0x8c,
+ HID_USAGE_PAGE_SCALE = 0x8d,
+ HID_USAGE_PAGE_MSR = 0x8e,
+ HID_USAGE_PAGE_CAMERA = 0x90,
+ HID_USAGE_PAGE_ARCADE = 0x91,
+ HID_USAGE_PAGE_FIDO = 0xF1D0, // FIDO alliance HID usage page
+ HID_USAGE_PAGE_VENDOR = 0xFF00 // 0xFF00 - 0xFFFF
};
/// HID Usage Table - Table 6: Generic Desktop Page
@@ -801,8 +849,7 @@ enum {
/// HID Usage Table: Consumer Page (0x0C)
/// Only contains controls that supported by Windows (whole list is too long)
-enum
-{
+enum {
// Generic Control
HID_USAGE_CONSUMER_CONTROL = 0x0001,
@@ -858,9 +905,45 @@ enum
HID_USAGE_CONSUMER_AC_PAN = 0x0238,
};
+/// HID Usage Table - Lighting And Illumination Page (0x59)
+enum {
+ HID_USAGE_LIGHTING_LAMP_ARRAY = 0x01,
+ HID_USAGE_LIGHTING_LAMP_ARRAY_ATTRIBUTES_REPORT = 0x02,
+ HID_USAGE_LIGHTING_LAMP_COUNT = 0x03,
+ HID_USAGE_LIGHTING_BOUNDING_BOX_WIDTH_IN_MICROMETERS = 0x04,
+ HID_USAGE_LIGHTING_BOUNDING_BOX_HEIGHT_IN_MICROMETERS = 0x05,
+ HID_USAGE_LIGHTING_BOUNDING_BOX_DEPTH_IN_MICROMETERS = 0x06,
+ HID_USAGE_LIGHTING_LAMP_ARRAY_KIND = 0x07,
+ HID_USAGE_LIGHTING_MIN_UPDATE_INTERVAL_IN_MICROSECONDS = 0x08,
+ HID_USAGE_LIGHTING_LAMP_ATTRIBUTES_REQUEST_REPORT = 0x20,
+ HID_USAGE_LIGHTING_LAMP_ID = 0x21,
+ HID_USAGE_LIGHTING_LAMP_ATTRIBUTES_RESPONSE_REPORT = 0x22,
+ HID_USAGE_LIGHTING_POSITION_X_IN_MICROMETERS = 0x23,
+ HID_USAGE_LIGHTING_POSITION_Y_IN_MICROMETERS = 0x24,
+ HID_USAGE_LIGHTING_POSITION_Z_IN_MICROMETERS = 0x25,
+ HID_USAGE_LIGHTING_LAMP_PURPOSES = 0x26,
+ HID_USAGE_LIGHTING_UPDATE_LATENCY_IN_MICROSECONDS = 0x27,
+ HID_USAGE_LIGHTING_RED_LEVEL_COUNT = 0x28,
+ HID_USAGE_LIGHTING_GREEN_LEVEL_COUNT = 0x29,
+ HID_USAGE_LIGHTING_BLUE_LEVEL_COUNT = 0x2A,
+ HID_USAGE_LIGHTING_INTENSITY_LEVEL_COUNT = 0x2B,
+ HID_USAGE_LIGHTING_IS_PROGRAMMABLE = 0x2C,
+ HID_USAGE_LIGHTING_INPUT_BINDING = 0x2D,
+ HID_USAGE_LIGHTING_LAMP_MULTI_UPDATE_REPORT = 0x50,
+ HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL = 0x51,
+ HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL = 0x52,
+ HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL = 0x53,
+ HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL = 0x54,
+ HID_USAGE_LIGHTING_LAMP_UPDATE_FLAGS = 0x55,
+ HID_USAGE_LIGHTING_LAMP_RANGE_UPDATE_REPORT = 0x60,
+ HID_USAGE_LIGHTING_LAMP_ID_START = 0x61,
+ HID_USAGE_LIGHTING_LAMP_ID_END = 0x62,
+ HID_USAGE_LIGHTING_LAMP_ARRAY_CONTROL_REPORT = 0x70,
+ HID_USAGE_LIGHTING_AUTONOMOUS_MODE = 0x71,
+};
+
/// HID Usage Table: FIDO Alliance Page (0xF1D0)
-enum
-{
+enum {
HID_USAGE_FIDO_U2FHID = 0x01, // U2FHID usage for top-level collection
HID_USAGE_FIDO_DATA_IN = 0x20, // Raw IN data report
HID_USAGE_FIDO_DATA_OUT = 0x21 // Raw OUT data report
diff --git a/src/class/hid/hid_device.c b/src/class/hid/hid_device.c
index 5cc5a2488..00f6fc474 100644
--- a/src/class/hid/hid_device.c
+++ b/src/class/hid/hid_device.c
@@ -39,92 +39,109 @@
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
-typedef struct
-{
+typedef struct {
uint8_t itf_num;
uint8_t ep_in;
- uint8_t ep_out; // optional Out endpoint
- uint8_t itf_protocol; // Boot mouse or keyboard
+ uint8_t ep_out; // optional Out endpoint
+ uint8_t itf_protocol; // Boot mouse or keyboard
+ uint16_t report_desc_len;
uint8_t protocol_mode; // Boot (0) or Report protocol (1)
uint8_t idle_rate; // up to application to handle idle rate
- uint16_t report_desc_len;
-
- 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];
// 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;
+ 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];
} hidd_interface_t;
CFG_TUD_MEM_SECTION tu_static hidd_interface_t _hidd_itf[CFG_TUD_HID];
/*------------- Helpers -------------*/
-static inline uint8_t get_index_by_itfnum(uint8_t itf_num)
-{
- for (uint8_t i=0; i < CFG_TUD_HID; i++ )
- {
- if ( itf_num == _hidd_itf[i].itf_num ) return i;
- }
+TU_ATTR_ALWAYS_INLINE static inline uint8_t get_index_by_itfnum(uint8_t itf_num) {
+ for (uint8_t i = 0; i < CFG_TUD_HID; i++) {
+ if (itf_num == _hidd_itf[i].itf_num) {
+ return i;
+ }
+ }
+ return 0xFF;
+}
- return 0xFF;
+//--------------------------------------------------------------------+
+// Weak stubs: invoked if no strong implementation is available
+//--------------------------------------------------------------------+
+TU_ATTR_WEAK void tud_hid_set_protocol_cb(uint8_t instance, uint8_t protocol) {
+ (void) instance;
+ (void) protocol;
+}
+
+TU_ATTR_WEAK bool tud_hid_set_idle_cb(uint8_t instance, uint8_t idle_rate) {
+ (void) instance;
+ (void) idle_rate;
+ return true;
+}
+
+TU_ATTR_WEAK void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, uint16_t len) {
+ (void) instance;
+ (void) report;
+ (void) len;
+}
+
+// Invoked when a transfer wasn't successful
+TU_ATTR_WEAK void tud_hid_report_failed_cb(uint8_t instance, hid_report_type_t report_type, uint8_t const* report, uint16_t xferred_bytes) {
+ (void) instance;
+ (void) report_type;
+ (void) report;
+ (void) xferred_bytes;
}
//--------------------------------------------------------------------+
// APPLICATION API
//--------------------------------------------------------------------+
-bool tud_hid_n_ready(uint8_t instance)
-{
+bool tud_hid_n_ready(uint8_t instance) {
uint8_t const rhport = 0;
uint8_t const ep_in = _hidd_itf[instance].ep_in;
return tud_ready() && (ep_in != 0) && !usbd_edpt_busy(rhport, ep_in);
}
-bool tud_hid_n_report(uint8_t instance, uint8_t report_id, void const* report, uint16_t len)
-{
+bool tud_hid_n_report(uint8_t instance, uint8_t report_id, void const *report, uint16_t len) {
uint8_t const rhport = 0;
- hidd_interface_t * p_hid = &_hidd_itf[instance];
+ hidd_interface_t *p_hid = &_hidd_itf[instance];
// claim endpoint
- TU_VERIFY( usbd_edpt_claim(rhport, p_hid->ep_in) );
+ TU_VERIFY(usbd_edpt_claim(rhport, p_hid->ep_in));
// prepare data
- if (report_id)
- {
+ 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));
+ TU_VERIFY(0 == tu_memcpy_s(p_hid->epin_buf + 1, CFG_TUD_HID_EP_BUFSIZE - 1, report, len));
len++;
- }else
- {
+ } else {
TU_VERIFY(0 == tu_memcpy_s(p_hid->epin_buf, CFG_TUD_HID_EP_BUFSIZE, report, len));
}
return usbd_edpt_xfer(rhport, p_hid->ep_in, p_hid->epin_buf, len);
}
-uint8_t tud_hid_n_interface_protocol(uint8_t instance)
-{
+uint8_t tud_hid_n_interface_protocol(uint8_t instance) {
return _hidd_itf[instance].itf_protocol;
}
-uint8_t tud_hid_n_get_protocol(uint8_t instance)
-{
+uint8_t tud_hid_n_get_protocol(uint8_t instance) {
return _hidd_itf[instance].protocol_mode;
}
-bool tud_hid_n_keyboard_report(uint8_t instance, uint8_t report_id, uint8_t modifier, uint8_t keycode[6])
-{
+bool tud_hid_n_keyboard_report(uint8_t instance, uint8_t report_id, uint8_t modifier, uint8_t keycode[6]) {
hid_keyboard_report_t report;
-
report.modifier = modifier;
report.reserved = 0;
- if ( keycode )
- {
+ if (keycode) {
memcpy(report.keycode, keycode, sizeof(report.keycode));
- }else
- {
+ } else {
tu_memclr(report.keycode, 6);
}
@@ -132,45 +149,41 @@ bool tud_hid_n_keyboard_report(uint8_t instance, uint8_t report_id, uint8_t modi
}
bool tud_hid_n_mouse_report(uint8_t instance, uint8_t report_id,
- uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal)
-{
- hid_mouse_report_t report =
- {
+ uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal) {
+ hid_mouse_report_t report = {
.buttons = buttons,
- .x = x,
- .y = y,
- .wheel = vertical,
- .pan = horizontal
+ .x = x,
+ .y = y,
+ .wheel = vertical,
+ .pan = horizontal
};
return tud_hid_n_report(instance, report_id, &report, sizeof(report));
}
-bool tud_hid_n_abs_mouse_report(uint8_t instance, uint8_t report_id, uint8_t buttons, int16_t x, int16_t y, int8_t vertical, int8_t horizontal)
-{
- hid_abs_mouse_report_t report =
- {
+bool tud_hid_n_abs_mouse_report(uint8_t instance, uint8_t report_id,
+ uint8_t buttons, int16_t x, int16_t y, int8_t vertical, int8_t horizontal) {
+ hid_abs_mouse_report_t report = {
.buttons = buttons,
- .x = x,
- .y = y,
- .wheel = vertical,
- .pan = horizontal
+ .x = x,
+ .y = y,
+ .wheel = vertical,
+ .pan = horizontal
};
return tud_hid_n_report(instance, report_id, &report, sizeof(report));
}
bool tud_hid_n_gamepad_report(uint8_t instance, uint8_t report_id,
int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons) {
- hid_gamepad_report_t report =
- {
- .x = x,
- .y = y,
- .z = z,
- .rz = rz,
- .rx = rx,
- .ry = ry,
- .hat = hat,
- .buttons = buttons,
+ hid_gamepad_report_t report = {
+ .x = x,
+ .y = y,
+ .z = z,
+ .rz = rz,
+ .rx = rx,
+ .ry = ry,
+ .hat = hat,
+ .buttons = buttons,
};
return tud_hid_n_report(instance, report_id, &report, sizeof(report));
@@ -187,59 +200,54 @@ bool hidd_deinit(void) {
return true;
}
-void hidd_reset(uint8_t rhport)
-{
- (void) rhport;
+void hidd_reset(uint8_t rhport) {
+ (void)rhport;
tu_memclr(_hidd_itf, sizeof(_hidd_itf));
}
-uint16_t hidd_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t max_len)
- {
+uint16_t hidd_open(uint8_t rhport, tusb_desc_interface_t const *desc_itf, uint16_t max_len) {
TU_VERIFY(TUSB_CLASS_HID == desc_itf->bInterfaceClass, 0);
// len = interface + hid + n*endpoints
- uint16_t const drv_len =
- (uint16_t) (sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) +
+ uint16_t const drv_len = (uint16_t) (sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) +
desc_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
TU_ASSERT(max_len >= drv_len, 0);
// Find available interface
- hidd_interface_t * p_hid = NULL;
+ hidd_interface_t *p_hid = NULL;
uint8_t hid_id;
- for(hid_id=0; hid_id<CFG_TUD_HID; hid_id++)
- {
- if ( _hidd_itf[hid_id].ep_in == 0 )
- {
+ 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];
break;
}
}
TU_ASSERT(p_hid, 0);
- uint8_t const *p_desc = (uint8_t const *) desc_itf;
+ uint8_t const *p_desc = (uint8_t const *)desc_itf;
//------------- HID descriptor -------------//
p_desc = tu_desc_next(p_desc);
TU_ASSERT(HID_DESC_TYPE_HID == tu_desc_type(p_desc), 0);
- p_hid->hid_descriptor = (tusb_hid_descriptor_hid_t const *) p_desc;
+ p_hid->hid_descriptor = (tusb_hid_descriptor_hid_t const *)p_desc;
//------------- Endpoint Descriptor -------------//
p_desc = tu_desc_next(p_desc);
TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, desc_itf->bNumEndpoints, TUSB_XFER_INTERRUPT, &p_hid->ep_out, &p_hid->ep_in), 0);
- if ( desc_itf->bInterfaceSubClass == HID_SUBCLASS_BOOT ) p_hid->itf_protocol = desc_itf->bInterfaceProtocol;
+ if (desc_itf->bInterfaceSubClass == HID_SUBCLASS_BOOT) {
+ p_hid->itf_protocol = desc_itf->bInterfaceProtocol;
+ }
p_hid->protocol_mode = HID_PROTOCOL_REPORT; // Per Specs: default is report mode
- p_hid->itf_num = desc_itf->bInterfaceNumber;
+ p_hid->itf_num = desc_itf->bInterfaceNumber;
// Use offsetof to avoid pointer to the odd/misaligned address
- p_hid->report_desc_len = tu_unaligned_read16((uint8_t const*) p_hid->hid_descriptor + offsetof(tusb_hid_descriptor_hid_t, wReportLength));
+ p_hid->report_desc_len = tu_unaligned_read16((uint8_t const *)p_hid->hid_descriptor + offsetof(tusb_hid_descriptor_hid_t, wReportLength));
// 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)) )
- {
+ 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();
}
@@ -251,177 +259,146 @@ uint16_t hidd_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, 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 hidd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
-{
+bool hidd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) {
TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE);
- uint8_t const hid_itf = get_index_by_itfnum((uint8_t) request->wIndex);
+ 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_interface_t *p_hid = &_hidd_itf[hid_itf];
- if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD)
- {
+ if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD) {
//------------- STD Request -------------//
- if ( stage == CONTROL_STAGE_SETUP )
- {
- uint8_t const desc_type = tu_u16_high(request->wValue);
- //uint8_t const desc_index = tu_u16_low (request->wValue);
+ if (stage == CONTROL_STAGE_SETUP) {
+ uint8_t const desc_type = tu_u16_high(request->wValue);
+ // uint8_t const desc_index = tu_u16_low (request->wValue);
- if (request->bRequest == TUSB_REQ_GET_DESCRIPTOR && desc_type == HID_DESC_TYPE_HID)
- {
+ if (request->bRequest == TUSB_REQ_GET_DESCRIPTOR && desc_type == HID_DESC_TYPE_HID) {
TU_VERIFY(p_hid->hid_descriptor);
- TU_VERIFY(tud_control_xfer(rhport, request, (void*)(uintptr_t) p_hid->hid_descriptor, p_hid->hid_descriptor->bLength));
- }
- else if (request->bRequest == TUSB_REQ_GET_DESCRIPTOR && desc_type == HID_DESC_TYPE_REPORT)
- {
- uint8_t const * desc_report = tud_hid_descriptor_report_cb(hid_itf);
- tud_control_xfer(rhport, request, (void*)(uintptr_t) desc_report, p_hid->report_desc_len);
- }
- else
- {
+ TU_VERIFY(tud_control_xfer(rhport, request, (void *)(uintptr_t)p_hid->hid_descriptor, p_hid->hid_descriptor->bLength));
+ } else if (request->bRequest == TUSB_REQ_GET_DESCRIPTOR && desc_type == HID_DESC_TYPE_REPORT) {
+ uint8_t const *desc_report = tud_hid_descriptor_report_cb(hid_itf);
+ tud_control_xfer(rhport, request, (void *)(uintptr_t)desc_report, p_hid->report_desc_len);
+ } else {
return false; // stall unsupported request
}
}
- }
- else if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS)
- {
+ } else if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS) {
//------------- Class Specific Request -------------//
- switch( request->bRequest )
- {
+ switch (request->bRequest) {
case HID_REQ_CONTROL_GET_REPORT:
- if ( stage == CONTROL_STAGE_SETUP )
- {
+ if (stage == CONTROL_STAGE_SETUP) {
uint8_t const report_type = tu_u16_high(request->wValue);
- uint8_t const report_id = tu_u16_low(request->wValue);
+ uint8_t const report_id = tu_u16_low(request->wValue);
- uint8_t* report_buf = p_hid->epin_buf;
+ uint8_t* report_buf = p_hid->ctrl_buf;
uint16_t req_len = tu_min16(request->wLength, CFG_TUD_HID_EP_BUFSIZE);
-
uint16_t xferlen = 0;
// If host request a specific Report ID, add ID to as 1 byte of response
- if ( (report_id != HID_REPORT_TYPE_INVALID) && (req_len > 1) )
- {
+ if ((report_id != HID_REPORT_TYPE_INVALID) && (req_len > 1)) {
*report_buf++ = report_id;
req_len--;
-
xferlen++;
}
xferlen += tud_hid_get_report_cb(hid_itf, report_id, (hid_report_type_t) report_type, report_buf, req_len);
- TU_ASSERT( xferlen > 0 );
+ TU_ASSERT(xferlen > 0);
- tud_control_xfer(rhport, request, p_hid->epin_buf, xferlen);
+ tud_control_xfer(rhport, request, p_hid->ctrl_buf, xferlen);
}
- break;
+ break;
- case HID_REQ_CONTROL_SET_REPORT:
- if ( stage == CONTROL_STAGE_SETUP )
- {
- TU_VERIFY(request->wLength <= sizeof(p_hid->epout_buf));
- tud_control_xfer(rhport, request, p_hid->epout_buf, request->wLength);
- }
- else if ( stage == CONTROL_STAGE_ACK )
- {
+ 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);
+ } 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_id = tu_u16_low(request->wValue);
- uint8_t const* report_buf = p_hid->epout_buf;
+ uint8_t const* report_buf = p_hid->ctrl_buf;
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
- if ( (report_id != HID_REPORT_TYPE_INVALID) && (report_len > 1) && (report_id == report_buf[0]) )
- {
+ if ((report_id != HID_REPORT_TYPE_INVALID) && (report_len > 1) && (report_id == report_buf[0])) {
report_buf++;
report_len--;
}
tud_hid_set_report_cb(hid_itf, report_id, (hid_report_type_t) report_type, report_buf, report_len);
}
- break;
+ break;
case HID_REQ_CONTROL_SET_IDLE:
- if ( stage == CONTROL_STAGE_SETUP )
- {
+ if (stage == CONTROL_STAGE_SETUP) {
p_hid->idle_rate = tu_u16_high(request->wValue);
- if ( tud_hid_set_idle_cb )
- {
- // stall request if callback return false
- TU_VERIFY( tud_hid_set_idle_cb( hid_itf, p_hid->idle_rate) );
- }
-
+ TU_VERIFY(tud_hid_set_idle_cb(hid_itf, p_hid->idle_rate)); // stall if false
tud_control_status(rhport, request);
}
- break;
+ break;
case HID_REQ_CONTROL_GET_IDLE:
- if ( stage == CONTROL_STAGE_SETUP )
- {
+ if (stage == CONTROL_STAGE_SETUP) {
// TODO idle rate of report
tud_control_xfer(rhport, request, &p_hid->idle_rate, 1);
}
- break;
+ break;
case HID_REQ_CONTROL_GET_PROTOCOL:
- if ( stage == CONTROL_STAGE_SETUP )
- {
+ if (stage == CONTROL_STAGE_SETUP) {
tud_control_xfer(rhport, request, &p_hid->protocol_mode, 1);
}
- break;
+ break;
case HID_REQ_CONTROL_SET_PROTOCOL:
- if ( stage == CONTROL_STAGE_SETUP )
- {
+ if (stage == CONTROL_STAGE_SETUP) {
tud_control_status(rhport, request);
- }
- else if ( stage == CONTROL_STAGE_ACK )
- {
+ } else if (stage == CONTROL_STAGE_ACK) {
p_hid->protocol_mode = (uint8_t) request->wValue;
- if (tud_hid_set_protocol_cb)
- {
- tud_hid_set_protocol_cb(hid_itf, p_hid->protocol_mode);
- }
+ tud_hid_set_protocol_cb(hid_itf, p_hid->protocol_mode);
}
- break;
+ break;
- default: return false; // stall unsupported request
+ default:
+ return false; // stall unsupported request
}
- }else
- {
+ } else {
return false; // stall unsupported request
}
return true;
}
-bool hidd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
-{
- (void) result;
-
+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;
+ hidd_interface_t *p_hid = _hidd_itf;
// Identify which interface to use
- for (instance = 0; instance < CFG_TUD_HID; instance++)
- {
+ for (instance = 0; instance < CFG_TUD_HID; instance++) {
p_hid = &_hidd_itf[instance];
- if ( (ep_addr == p_hid->ep_out) || (ep_addr == p_hid->ep_in) ) break;
+ if ((ep_addr == p_hid->ep_out) || (ep_addr == p_hid->ep_in)) {
+ break;
+ }
}
TU_ASSERT(instance < CFG_TUD_HID);
- // Sent report successfully
- if (ep_addr == p_hid->ep_in)
- {
- if (tud_hid_report_complete_cb)
- {
+ 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);
+ } else {
+ tud_hid_report_failed_cb(instance, HID_REPORT_TYPE_INPUT, p_hid->epin_buf, (uint16_t) xferred_bytes);
}
- }
- // Received report
- else if (ep_addr == p_hid->ep_out)
- {
- tud_hid_set_report_cb(instance, 0, HID_REPORT_TYPE_INVALID, p_hid->epout_buf, (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);
+ } else {
+ tud_hid_report_failed_cb(instance, HID_REPORT_TYPE_OUTPUT, p_hid->epout_buf, (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)));
}
diff --git a/src/class/hid/hid_device.h b/src/class/hid/hid_device.h
index 040cad162..89c28e061 100644
--- a/src/class/hid/hid_device.h
+++ b/src/class/hid/hid_device.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_HID_DEVICE_H_
-#define _TUSB_HID_DEVICE_H_
+#ifndef TUSB_HID_DEVICE_H_
+#define TUSB_HID_DEVICE_H_
#include "hid.h"
@@ -48,8 +48,7 @@
#endif
//--------------------------------------------------------------------+
-// Application API (Multiple Instances)
-// CFG_TUD_HID > 1
+// Application API (Multiple Instances) i.e. CFG_TUD_HID > 1
//--------------------------------------------------------------------+
// Check if the interface is ready to use
@@ -76,12 +75,6 @@ bool tud_hid_n_mouse_report(uint8_t instance, uint8_t report_id, uint8_t buttons
// use template layout report as defined by hid_abs_mouse_report_t
bool tud_hid_n_abs_mouse_report(uint8_t instance, uint8_t report_id, uint8_t buttons, int16_t x, int16_t y, int8_t vertical, int8_t horizontal);
-
-static inline bool tud_hid_abs_mouse_report(uint8_t report_id, uint8_t buttons, int16_t x, int16_t y, int8_t vertical, int8_t horizontal)
-{
- return tud_hid_n_abs_mouse_report(0, report_id, buttons, x, y, vertical, horizontal);
-}
-
// Gamepad: convenient helper to send gamepad report if application
// use template layout report TUD_HID_REPORT_DESC_GAMEPAD
bool tud_hid_n_gamepad_report(uint8_t instance, uint8_t report_id, int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons);
@@ -89,16 +82,40 @@ bool tud_hid_n_gamepad_report(uint8_t instance, uint8_t report_id, int8_t x, int
//--------------------------------------------------------------------+
// Application API (Single Port)
//--------------------------------------------------------------------+
-static inline bool tud_hid_ready(void);
-static inline uint8_t tud_hid_interface_protocol(void);
-static inline uint8_t tud_hid_get_protocol(void);
-static inline bool tud_hid_report(uint8_t report_id, void const* report, uint16_t len);
-static inline bool tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, uint8_t keycode[6]);
-static inline bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal);
-static inline bool tud_hid_gamepad_report(uint8_t report_id, int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons);
+TU_ATTR_ALWAYS_INLINE static inline bool tud_hid_ready(void) {
+ return tud_hid_n_ready(0);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint8_t tud_hid_interface_protocol(void) {
+ return tud_hid_n_interface_protocol(0);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint8_t tud_hid_get_protocol(void) {
+ return tud_hid_n_get_protocol(0);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool tud_hid_report(uint8_t report_id, void const* report, uint16_t len) {
+ return tud_hid_n_report(0, report_id, report, len);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, uint8_t keycode[6]) {
+ return tud_hid_n_keyboard_report(0, report_id, modifier, keycode);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal) {
+ return tud_hid_n_mouse_report(0, report_id, buttons, x, y, vertical, horizontal);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool tud_hid_abs_mouse_report(uint8_t report_id, uint8_t buttons, int16_t x, int16_t y, int8_t vertical, int8_t horizontal) {
+ return tud_hid_n_abs_mouse_report(0, report_id, buttons, x, y, vertical, horizontal);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool tud_hid_gamepad_report(uint8_t report_id, int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons) {
+ return tud_hid_n_gamepad_report(0, report_id, x, y, z, rz, rx, ry, hat, buttons);
+}
//--------------------------------------------------------------------+
-// Callbacks (Weak is optional)
+// Application Callbacks
//--------------------------------------------------------------------+
// Invoked when received GET HID REPORT DESCRIPTOR request
@@ -111,61 +128,25 @@ uint8_t const * tud_hid_descriptor_report_cb(uint8_t instance);
uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen);
// Invoked when received SET_REPORT control request or
-// received data on OUT endpoint ( Report ID = 0, Type = 0 )
+// received data on OUT endpoint (Report ID = 0, Type = OUTPUT)
void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize);
// Invoked when received SET_PROTOCOL request
// protocol is either HID_PROTOCOL_BOOT (0) or HID_PROTOCOL_REPORT (1)
-TU_ATTR_WEAK void tud_hid_set_protocol_cb(uint8_t instance, uint8_t protocol);
+void tud_hid_set_protocol_cb(uint8_t instance, uint8_t protocol);
// Invoked when received SET_IDLE request. return false will stall the request
-// - Idle Rate = 0 : only send report if there is changes, i.e skip duplication
+// - Idle Rate = 0 : only send report if there is changes, i.e. skip duplication
// - Idle Rate > 0 : skip duplication, but send at least 1 report every idle rate (in unit of 4 ms).
-TU_ATTR_WEAK bool tud_hid_set_idle_cb(uint8_t instance, uint8_t idle_rate);
+bool tud_hid_set_idle_cb(uint8_t instance, uint8_t idle_rate);
// Invoked when sent REPORT successfully to host
// Application can use this to send the next report
// Note: For composite reports, report[0] is report ID
-TU_ATTR_WEAK void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, uint16_t len);
+void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, uint16_t len);
-
-//--------------------------------------------------------------------+
-// Inline Functions
-//--------------------------------------------------------------------+
-static inline bool tud_hid_ready(void)
-{
- return tud_hid_n_ready(0);
-}
-
-static inline uint8_t tud_hid_interface_protocol(void)
-{
- return tud_hid_n_interface_protocol(0);
-}
-
-static inline uint8_t tud_hid_get_protocol(void)
-{
- return tud_hid_n_get_protocol(0);
-}
-
-static inline bool tud_hid_report(uint8_t report_id, void const* report, uint16_t len)
-{
- return tud_hid_n_report(0, report_id, report, len);
-}
-
-static inline bool tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, uint8_t keycode[6])
-{
- return tud_hid_n_keyboard_report(0, report_id, modifier, keycode);
-}
-
-static inline bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal)
-{
- return tud_hid_n_mouse_report(0, report_id, buttons, x, y, vertical, horizontal);
-}
-
-static inline bool tud_hid_gamepad_report(uint8_t report_id, int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons)
-{
- return tud_hid_n_gamepad_report(0, report_id, x, y, z, rz, rx, ry, hat, buttons);
-}
+// Invoked when a transfer wasn't successful
+void tud_hid_report_failed_cb(uint8_t instance, hid_report_type_t report_type, uint8_t const* report, uint16_t xferred_bytes);
/* --------------------------------------------------------------------+
* HID Report Descriptor Template
@@ -461,6 +442,178 @@ static inline bool tud_hid_gamepad_report(uint8_t report_id, int8_t x, int8_t y
HID_OUTPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
HID_COLLECTION_END \
+// HID Lighting and Illumination Report Descriptor Template
+// - 1st parameter is report id (required)
+// Creates 6 report ids for lighting HID usages in the following order:
+// report_id+0: HID_USAGE_LIGHTING_LAMP_ARRAY_ATTRIBUTES_REPORT
+// report_id+1: HID_USAGE_LIGHTING_LAMP_ATTRIBUTES_REQUEST_REPORT
+// report_id+2: HID_USAGE_LIGHTING_LAMP_ATTRIBUTES_RESPONSE_REPORT
+// report_id+3: HID_USAGE_LIGHTING_LAMP_MULTI_UPDATE_REPORT
+// report_id+4: HID_USAGE_LIGHTING_LAMP_RANGE_UPDATE_REPORT
+// report_id+5: HID_USAGE_LIGHTING_LAMP_ARRAY_CONTROL_REPORT
+#define TUD_HID_REPORT_DESC_LIGHTING(report_id) \
+ HID_USAGE_PAGE ( HID_USAGE_PAGE_LIGHTING_AND_ILLUMINATION ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ARRAY ),\
+ HID_COLLECTION ( HID_COLLECTION_APPLICATION ),\
+ /* Lamp Array Attributes Report */ \
+ HID_REPORT_ID (report_id ) \
+ HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ARRAY_ATTRIBUTES_REPORT ),\
+ HID_COLLECTION ( HID_COLLECTION_LOGICAL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_LAMP_COUNT ),\
+ HID_LOGICAL_MIN ( 0 ),\
+ HID_LOGICAL_MAX_N ( 65535, 3 ),\
+ HID_REPORT_SIZE ( 16 ),\
+ HID_REPORT_COUNT ( 1 ),\
+ HID_FEATURE ( HID_CONSTANT | HID_VARIABLE | HID_ABSOLUTE ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_BOUNDING_BOX_WIDTH_IN_MICROMETERS ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_BOUNDING_BOX_HEIGHT_IN_MICROMETERS ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_BOUNDING_BOX_DEPTH_IN_MICROMETERS ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ARRAY_KIND ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_MIN_UPDATE_INTERVAL_IN_MICROSECONDS ),\
+ HID_LOGICAL_MIN ( 0 ),\
+ HID_LOGICAL_MAX_N ( 2147483647, 3 ),\
+ HID_REPORT_SIZE ( 32 ),\
+ HID_REPORT_COUNT ( 5 ),\
+ HID_FEATURE ( HID_CONSTANT | HID_VARIABLE | HID_ABSOLUTE ),\
+ HID_COLLECTION_END ,\
+ /* Lamp Attributes Request Report */ \
+ HID_REPORT_ID ( report_id + 1 ) \
+ HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ATTRIBUTES_REQUEST_REPORT ),\
+ HID_COLLECTION ( HID_COLLECTION_LOGICAL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ID ),\
+ HID_LOGICAL_MIN ( 0 ),\
+ HID_LOGICAL_MAX_N ( 65535, 3 ),\
+ HID_REPORT_SIZE ( 16 ),\
+ HID_REPORT_COUNT ( 1 ),\
+ HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
+ HID_COLLECTION_END ,\
+ /* Lamp Attributes Response Report */ \
+ HID_REPORT_ID ( report_id + 2 ) \
+ HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ATTRIBUTES_RESPONSE_REPORT ),\
+ HID_COLLECTION ( HID_COLLECTION_LOGICAL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ID ),\
+ HID_LOGICAL_MIN ( 0 ),\
+ HID_LOGICAL_MAX_N ( 65535, 3 ),\
+ HID_REPORT_SIZE ( 16 ),\
+ HID_REPORT_COUNT ( 1 ),\
+ HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_POSITION_X_IN_MICROMETERS ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_POSITION_Y_IN_MICROMETERS ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_POSITION_Z_IN_MICROMETERS ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_UPDATE_LATENCY_IN_MICROSECONDS ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_LAMP_PURPOSES ),\
+ HID_LOGICAL_MIN ( 0 ),\
+ HID_LOGICAL_MAX_N ( 2147483647, 3 ),\
+ HID_REPORT_SIZE ( 32 ),\
+ HID_REPORT_COUNT ( 5 ),\
+ HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_RED_LEVEL_COUNT ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_GREEN_LEVEL_COUNT ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_BLUE_LEVEL_COUNT ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_LEVEL_COUNT ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_IS_PROGRAMMABLE ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_INPUT_BINDING ),\
+ HID_LOGICAL_MIN ( 0 ),\
+ HID_LOGICAL_MAX_N ( 255, 2 ),\
+ HID_REPORT_SIZE ( 8 ),\
+ HID_REPORT_COUNT ( 6 ),\
+ HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
+ HID_COLLECTION_END ,\
+ /* Lamp Multi-Update Report */ \
+ HID_REPORT_ID ( report_id + 3 ) \
+ HID_USAGE ( HID_USAGE_LIGHTING_LAMP_MULTI_UPDATE_REPORT ),\
+ HID_COLLECTION ( HID_COLLECTION_LOGICAL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_LAMP_COUNT ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_LAMP_UPDATE_FLAGS ),\
+ HID_LOGICAL_MIN ( 0 ),\
+ HID_LOGICAL_MAX ( 8 ),\
+ HID_REPORT_SIZE ( 8 ),\
+ HID_REPORT_COUNT ( 2 ),\
+ HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ID ),\
+ HID_LOGICAL_MIN ( 0 ),\
+ HID_LOGICAL_MAX_N ( 65535, 3 ),\
+ HID_REPORT_SIZE ( 16 ),\
+ HID_REPORT_COUNT ( 8 ),\
+ HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
+ HID_LOGICAL_MIN ( 0 ),\
+ HID_LOGICAL_MAX_N ( 255, 2 ),\
+ HID_REPORT_SIZE ( 8 ),\
+ HID_REPORT_COUNT ( 32 ),\
+ HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
+ HID_COLLECTION_END ,\
+ /* Lamp Range Update Report */ \
+ HID_REPORT_ID ( report_id + 4 ) \
+ HID_USAGE ( HID_USAGE_LIGHTING_LAMP_RANGE_UPDATE_REPORT ),\
+ HID_COLLECTION ( HID_COLLECTION_LOGICAL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_LAMP_UPDATE_FLAGS ),\
+ HID_LOGICAL_MIN ( 0 ),\
+ HID_LOGICAL_MAX ( 8 ),\
+ HID_REPORT_SIZE ( 8 ),\
+ HID_REPORT_COUNT ( 1 ),\
+ HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ID_START ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ID_END ),\
+ HID_LOGICAL_MIN ( 0 ),\
+ HID_LOGICAL_MAX_N ( 65535, 3 ),\
+ HID_REPORT_SIZE ( 16 ),\
+ HID_REPORT_COUNT ( 2 ),\
+ HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
+ HID_LOGICAL_MIN ( 0 ),\
+ HID_LOGICAL_MAX_N ( 255, 2 ),\
+ HID_REPORT_SIZE ( 8 ),\
+ HID_REPORT_COUNT ( 4 ),\
+ HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
+ HID_COLLECTION_END ,\
+ /* Lamp Array Control Report */ \
+ HID_REPORT_ID ( report_id + 5 ) \
+ HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ARRAY_CONTROL_REPORT ),\
+ HID_COLLECTION ( HID_COLLECTION_LOGICAL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_AUTONOMOUS_MODE ),\
+ HID_LOGICAL_MIN ( 0 ),\
+ HID_LOGICAL_MAX ( 1 ),\
+ HID_REPORT_SIZE ( 8 ),\
+ HID_REPORT_COUNT ( 1 ),\
+ HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
+ HID_COLLECTION_END ,\
+ HID_COLLECTION_END \
+
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
@@ -475,4 +628,4 @@ bool hidd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t ev
}
#endif
-#endif /* _TUSB_HID_DEVICE_H_ */
+#endif
diff --git a/src/class/hid/hid_host.c b/src/class/hid/hid_host.c
index 621fb2a55..7639a8fc6 100644
--- a/src/class/hid/hid_host.c
+++ b/src/class/hid/hid_host.c
@@ -222,6 +222,50 @@ bool tuh_hid_set_protocol(uint8_t daddr, uint8_t idx, uint8_t protocol) {
return _hidh_set_protocol(daddr, p_hid->itf_num, protocol, set_protocol_complete, 0);
}
+static void get_report_complete(tuh_xfer_t* xfer) {
+ TU_LOG_DRV("HID Get Report complete\r\n");
+
+ if (tuh_hid_get_report_complete_cb) {
+ uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
+ uint8_t const idx = tuh_hid_itf_get_index(xfer->daddr, itf_num);
+
+ uint8_t const report_type = tu_u16_high(xfer->setup->wValue);
+ uint8_t const report_id = tu_u16_low(xfer->setup->wValue);
+
+ tuh_hid_get_report_complete_cb(xfer->daddr, idx, report_id, report_type,
+ (xfer->result == XFER_RESULT_SUCCESS) ? xfer->setup->wLength : 0);
+ }
+}
+
+bool tuh_hid_get_report(uint8_t daddr, uint8_t idx, uint8_t report_id, uint8_t report_type, void* report, uint16_t len) {
+ hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
+ TU_VERIFY(p_hid);
+ TU_LOG_DRV("HID Get Report: id = %u, type = %u, len = %u\r\n", report_id, report_type, len);
+
+ tusb_control_request_t const request = {
+ .bmRequestType_bit = {
+ .recipient = TUSB_REQ_RCPT_INTERFACE,
+ .type = TUSB_REQ_TYPE_CLASS,
+ .direction = TUSB_DIR_IN
+ },
+ .bRequest = HID_REQ_CONTROL_GET_REPORT,
+ .wValue = tu_htole16(tu_u16(report_type, report_id)),
+ .wIndex = tu_htole16((uint16_t) p_hid->itf_num),
+ .wLength = len
+ };
+
+ tuh_xfer_t xfer = {
+ .daddr = daddr,
+ .ep_addr = 0,
+ .setup = &request,
+ .buffer = report,
+ .complete_cb = get_report_complete,
+ .user_data = 0
+ };
+
+ return tuh_control_xfer(&xfer);
+}
+
static void set_report_complete(tuh_xfer_t* xfer) {
TU_LOG_DRV("HID Set Report complete\r\n");
@@ -613,7 +657,9 @@ uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* report_info_arr,
uint8_t const data8 = desc_report[0];
TU_LOG(3, "tag = %d, type = %d, size = %d, data = ", tag, type, size);
- for (uint32_t i = 0; i < size; i++) TU_LOG(3, "%02X ", desc_report[i]);
+ for (uint32_t i = 0; i < size; i++) {
+ TU_LOG(3, "%02X ", desc_report[i]);
+ }
TU_LOG(3, "\r\n");
switch (type) {
diff --git a/src/class/hid/hid_host.h b/src/class/hid/hid_host.h
index 0902bf1af..9681c704b 100644
--- a/src/class/hid/hid_host.h
+++ b/src/class/hid/hid_host.h
@@ -105,6 +105,10 @@ void tuh_hid_set_default_protocol(uint8_t protocol);
// This function is only supported by Boot interface (tuh_n_hid_interface_protocol() != NONE)
bool tuh_hid_set_protocol(uint8_t dev_addr, uint8_t idx, uint8_t protocol);
+// Get Report using control endpoint
+// report_type is either Input, Output or Feature, (value from hid_report_type_t)
+bool tuh_hid_get_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, void* report, uint16_t len);
+
// Set Report using control endpoint
// report_type is either Input, Output or Feature, (value from hid_report_type_t)
bool tuh_hid_set_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type,
@@ -153,6 +157,10 @@ void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t idx, uint8_t const* re
// Invoked when sent report to device successfully via interrupt endpoint
TU_ATTR_WEAK void tuh_hid_report_sent_cb(uint8_t dev_addr, uint8_t idx, uint8_t const* report, uint16_t len);
+// Invoked when Get Report to device via either control endpoint
+// len = 0 indicate there is error in the transfer e.g stalled response
+TU_ATTR_WEAK void tuh_hid_get_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, uint16_t len);
+
// Invoked when Sent Report to device via either control endpoint
// len = 0 indicate there is error in the transfer e.g stalled response
TU_ATTR_WEAK void tuh_hid_set_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, uint16_t len);
diff --git a/src/class/msc/msc_device.c b/src/class/msc/msc_device.c
index 588fcaaca..447560b4d 100644
--- a/src/class/msc/msc_device.c
+++ b/src/class/msc/msc_device.c
@@ -689,6 +689,24 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
}
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) )
+ {
+ // Failed status response
+ resplen = - 1;
+
+ // set default sense if not set by callback
+ if ( p_msc->sense_key == 0 ) set_sense_medium_not_present(lun);
+ }
+ }
+ break;
+
+
case SCSI_CMD_READ_CAPACITY_10:
{
uint32_t block_count;
diff --git a/src/class/msc/msc_device.h b/src/class/msc/msc_device.h
index 4247a40bd..29acd280a 100644
--- a/src/class/msc/msc_device.h
+++ b/src/class/msc/msc_device.h
@@ -131,6 +131,9 @@ TU_ATTR_WEAK uint8_t tud_msc_get_maxlun_cb(void);
// - Start = 1 : active mode, if load_eject = 1 : load disk storage
TU_ATTR_WEAK bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject);
+//Invoked when we receive the Prevent / Allow Medium Removal command
+TU_ATTR_WEAK bool tud_msc_prevent_allow_medium_removal_cb(uint8_t lun, uint8_t prohibit_removal, uint8_t control);
+
// Invoked when received REQUEST_SENSE
TU_ATTR_WEAK int32_t tud_msc_request_sense_cb(uint8_t lun, void* buffer, uint16_t bufsize);
diff --git a/src/class/net/ncm.h b/src/class/net/ncm.h
index 96ba11fbc..1b987fca0 100644
--- a/src/class/net/ncm.h
+++ b/src/class/net/ncm.h
@@ -2,6 +2,7 @@
* The MIT License (MIT)
*
* Copyright (c) 2021, Ha Thach (tinyusb.org)
+ * Copyright (c) 2024, Hardy Griech
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -24,22 +25,58 @@
* This file is part of the TinyUSB stack.
*/
-
#ifndef _TUSB_NCM_H_
#define _TUSB_NCM_H_
#include "common/tusb_common.h"
-#ifdef __cplusplus
- extern "C" {
+// NTB buffers size for reception side, must be >> MTU to avoid TCP retransmission (driver issue ?)
+// Linux use 2048 as minimal size
+#ifndef CFG_TUD_NCM_OUT_NTB_MAX_SIZE
+ #define CFG_TUD_NCM_OUT_NTB_MAX_SIZE 3200
#endif
-// Table 4.3 Data Class Interface Protocol Codes
-typedef enum
-{
- NCM_DATA_PROTOCOL_NETWORK_TRANSFER_BLOCK = 0x01
-} ncm_data_interface_protocol_code_t;
+// NTB buffers size for reception side, must be > MTU
+// Linux use 2048 as minimal size
+#ifndef CFG_TUD_NCM_IN_NTB_MAX_SIZE
+ #define CFG_TUD_NCM_IN_NTB_MAX_SIZE 3200
+#endif
+
+// Number of NTB buffers for reception side
+// Depending on the configuration, this parameter could be increased with the cost of additional RAM requirements
+// On Full-Speed (RP2040) :
+// 1 - good performance
+// 2 - up to 30% more performance with iperf with small packets
+// >2 - no performance gain
+// On High-Speed (STM32F7) :
+// No performance gain
+#ifndef CFG_TUD_NCM_OUT_NTB_N
+ #define CFG_TUD_NCM_OUT_NTB_N 1
+#endif
+// Number of NTB buffers for transmission side
+// Depending on the configuration, this parameter could be increased with the cost of additional RAM requirements
+// On Full-Speed (RP2040) :
+// 1 - good performance but SystemView shows lost events (on load test)
+// 2 - up to 50% more performance with iperf with small packets, "tud_network_can_xmit: request blocked"
+// happens from time to time with SystemView
+// 3 - "tud_network_can_xmit: request blocked" never happens
+// >3 - no performance gain
+// On High-Speed (STM32F7) :
+// No performance gain
+#ifndef CFG_TUD_NCM_IN_NTB_N
+ #define CFG_TUD_NCM_IN_NTB_N 1
+#endif
+
+// How many datagrams it is allowed to put into an NTB for transmission side
+#ifndef CFG_TUD_NCM_IN_MAX_DATAGRAMS_PER_NTB
+ #define CFG_TUD_NCM_IN_MAX_DATAGRAMS_PER_NTB 8
+#endif
+
+// This tells the host how many datagrams it is allowed to put into an NTB
+#ifndef CFG_TUD_NCM_OUT_MAX_DATAGRAMS_PER_NTB
+ #define CFG_TUD_NCM_OUT_MAX_DATAGRAMS_PER_NTB 6
+#endif
// Table 6.2 Class-Specific Request Codes for Network Control Model subclass
typedef enum
@@ -62,8 +99,65 @@ typedef enum
NCM_SET_CRC_MODE = 0x8A,
} ncm_request_code_t;
-#ifdef __cplusplus
- }
-#endif
+#define NTH16_SIGNATURE 0x484D434E
+#define NDP16_SIGNATURE_NCM0 0x304D434E
+#define NDP16_SIGNATURE_NCM1 0x314D434E
+
+typedef struct TU_ATTR_PACKED {
+ uint16_t wLength;
+ uint16_t bmNtbFormatsSupported;
+ uint32_t dwNtbInMaxSize;
+ uint16_t wNdbInDivisor;
+ uint16_t wNdbInPayloadRemainder;
+ uint16_t wNdbInAlignment;
+ uint16_t wReserved;
+ uint32_t dwNtbOutMaxSize;
+ uint16_t wNdbOutDivisor;
+ uint16_t wNdbOutPayloadRemainder;
+ uint16_t wNdbOutAlignment;
+ uint16_t wNtbOutMaxDatagrams;
+} ntb_parameters_t;
+
+typedef struct TU_ATTR_PACKED {
+ uint32_t dwSignature;
+ uint16_t wHeaderLength;
+ uint16_t wSequence;
+ uint16_t wBlockLength;
+ uint16_t wNdpIndex;
+} nth16_t;
+
+typedef struct TU_ATTR_PACKED {
+ uint16_t wDatagramIndex;
+ uint16_t wDatagramLength;
+} ndp16_datagram_t;
+
+typedef struct TU_ATTR_PACKED {
+ uint32_t dwSignature;
+ uint16_t wLength;
+ uint16_t wNextNdpIndex;
+ //ndp16_datagram_t datagram[];
+} ndp16_t;
+
+typedef union TU_ATTR_PACKED {
+ struct {
+ nth16_t nth;
+ ndp16_t ndp;
+ ndp16_datagram_t ndp_datagram[CFG_TUD_NCM_IN_MAX_DATAGRAMS_PER_NTB + 1];
+ };
+ uint8_t data[CFG_TUD_NCM_IN_NTB_MAX_SIZE];
+} xmit_ntb_t;
+
+typedef union TU_ATTR_PACKED {
+ struct {
+ nth16_t nth;
+ // only the header is at a guaranteed position
+ };
+ uint8_t data[CFG_TUD_NCM_OUT_NTB_MAX_SIZE];
+} recv_ntb_t;
+
+struct ncm_notify_t {
+ tusb_control_request_t header;
+ uint32_t downlink, uplink;
+};
#endif
diff --git a/src/class/net/ncm_device.c b/src/class/net/ncm_device.c
index f84bd9f73..90d747185 100644
--- a/src/class/net/ncm_device.c
+++ b/src/class/net/ncm_device.c
@@ -1,9 +1,10 @@
/*
* The MIT License (MIT)
*
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ * Copyright (c) 2024 Hardy Griech
* Copyright (c) 2020 Jacob Berg Potter
* Copyright (c) 2020 Peter Lawrence
- * Copyright (c) 2019 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
@@ -26,12 +27,37 @@
* This file is part of the TinyUSB stack.
*/
+/**
+ * Small Glossary (from the spec)
+ * --------------
+ * Datagram - A collection of bytes forming a single item of information, passed as a unit from source to destination.
+ * NCM - Network Control Model
+ * NDP - NCM Datagram Pointer: NTB structure that delineates Datagrams (typically Ethernet frames) within an NTB
+ * NTB - NCM Transfer Block: a data structure for efficient USB encapsulation of one or more datagrams
+ * Each NTB is designed to be a single USB transfer
+ * NTH - NTB Header: a data structure at the front of each NTB, which provides the information needed to validate
+ * the NTB and begin decoding
+ *
+ * Some explanations
+ * -----------------
+ * - rhport is the USB port of the device, in most cases "0"
+ * - itf_data_alt if != 0 -> data xmit/recv are allowed (see spec)
+ * - ep_in IN endpoints take data from the device intended to go in to the host (the device transmits)
+ * - ep_out OUT endpoints send data out of the host to the device (the device receives)
+ */
+
#include "tusb_option.h"
-#if ( CFG_TUD_ENABLED && CFG_TUD_NCM )
+#if (CFG_TUD_ENABLED && CFG_TUD_NCM)
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
#include "device/usbd.h"
#include "device/usbd_pvt.h"
+
+#include "ncm.h"
#include "net_device.h"
// Level where CFG_TUSB_DEBUG must be at least for this driver is logged
@@ -41,482 +67,829 @@
#define TU_LOG_DRV(...) TU_LOG(CFG_TUD_NCM_LOG_LEVEL, __VA_ARGS__)
-//--------------------------------------------------------------------+
-// MACRO CONSTANT TYPEDEF
-//--------------------------------------------------------------------+
+// Alignment must be 4
+#define TUD_NCM_ALIGNMENT 4
+// calculate alignment of xmit datagrams within an NTB
+#define XMIT_ALIGN_OFFSET(x) ((TUD_NCM_ALIGNMENT - ((x) & (TUD_NCM_ALIGNMENT - 1))) & (TUD_NCM_ALIGNMENT - 1))
-#define NTH16_SIGNATURE 0x484D434E
-#define NDP16_SIGNATURE_NCM0 0x304D434E
-#define NDP16_SIGNATURE_NCM1 0x314D434E
+//-----------------------------------------------------------------------------
+//
+// Module global things
+//
+#define XMIT_NTB_N CFG_TUD_NCM_IN_NTB_N
+#define RECV_NTB_N CFG_TUD_NCM_OUT_NTB_N
-typedef struct TU_ATTR_PACKED
-{
- uint16_t wLength;
- uint16_t bmNtbFormatsSupported;
- uint32_t dwNtbInMaxSize;
- uint16_t wNdbInDivisor;
- uint16_t wNdbInPayloadRemainder;
- uint16_t wNdbInAlignment;
- uint16_t wReserved;
- uint32_t dwNtbOutMaxSize;
- uint16_t wNdbOutDivisor;
- uint16_t wNdbOutPayloadRemainder;
- uint16_t wNdbOutAlignment;
- uint16_t wNtbOutMaxDatagrams;
-} ntb_parameters_t;
+typedef struct {
+ // general
+ uint8_t ep_in; // endpoint for outgoing datagrams (naming is a little bit confusing)
+ uint8_t ep_out; // endpoint for incoming datagrams (naming is a little bit confusing)
+ uint8_t ep_notif; // endpoint for notifications
+ uint8_t itf_num; // interface number
+ uint8_t itf_data_alt; // ==0 -> no endpoints, i.e. no network traffic, ==1 -> normal operation with two endpoints (spec, chapter 5.3)
+ uint8_t rhport; // storage of \a rhport because some callbacks are done without it
-typedef struct TU_ATTR_PACKED
-{
- uint32_t dwSignature;
- uint16_t wHeaderLength;
- uint16_t wSequence;
- uint16_t wBlockLength;
- uint16_t wNdpIndex;
-} nth16_t;
+ // 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
+ recv_ntb_t *recv_glue_ntb; // buffer for the running transfer driver -> glue logic
+ uint16_t recv_glue_ntb_datagram_ndx; // index into \a recv_glue_ntb_datagram
-typedef struct TU_ATTR_PACKED
-{
- uint16_t wDatagramIndex;
- uint16_t wDatagramLength;
-} ndp16_datagram_t;
+ // 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
+ xmit_ntb_t *xmit_glue_ntb; // buffer for the running transfer glue logic -> driver
+ uint16_t xmit_sequence; // NTB sequence counter
+ uint16_t xmit_glue_ntb_datagram_ndx; // index into \a xmit_glue_ntb_datagram
-typedef struct TU_ATTR_PACKED
-{
- uint32_t dwSignature;
- uint16_t wLength;
- uint16_t wNextNdpIndex;
- ndp16_datagram_t datagram[];
-} ndp16_t;
+ // notification handling
+ enum {
+ NOTIFICATION_SPEED,
+ NOTIFICATION_CONNECTED,
+ NOTIFICATION_DONE
+ } notification_xmit_state; // state of notification transmission
+ bool notification_xmit_is_running; // notification is currently transmitted
+} ncm_interface_t;
-typedef union TU_ATTR_PACKED {
- struct {
- nth16_t nth;
- ndp16_t ndp;
- };
- uint8_t data[CFG_TUD_NCM_IN_NTB_MAX_SIZE];
-} transmit_ntb_t;
+CFG_TUD_MEM_SECTION CFG_TUD_MEM_ALIGN tu_static ncm_interface_t ncm_interface;
-struct ecm_notify_struct
-{
- tusb_control_request_t header;
- uint32_t downlink, uplink;
+/**
+ * This is the NTB parameter structure
+ *
+ * \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 = {
+ .wLength = sizeof(ntb_parameters_t),
+ .bmNtbFormatsSupported = 0x01,// 16-bit NTB supported
+ .dwNtbInMaxSize = CFG_TUD_NCM_IN_NTB_MAX_SIZE,
+ .wNdbInDivisor = 1,
+ .wNdbInPayloadRemainder = 0,
+ .wNdbInAlignment = TUD_NCM_ALIGNMENT,
+ .wReserved = 0,
+ .dwNtbOutMaxSize = CFG_TUD_NCM_OUT_NTB_MAX_SIZE,
+ .wNdbOutDivisor = 1,
+ .wNdbOutPayloadRemainder = 0,
+ .wNdbOutAlignment = TUD_NCM_ALIGNMENT,
+ .wNtbOutMaxDatagrams = CFG_TUD_NCM_OUT_MAX_DATAGRAMS_PER_NTB,
};
-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
+// Some confusing remarks about wNtbOutMaxDatagrams...
+// ==1 -> SystemView packets/s goes up to 2000 and events are lost during startup
+// ==0 -> SystemView runs fine, iperf shows in wireshark a lot of error
+// ==6 -> SystemView runs fine, iperf also
+// >6 -> iperf starts to show errors
+// -> 6 seems to be the best value. Why? Don't know, perhaps only on my system?
+//
+// iperf: for MSS in 100 200 400 800 1200 1450 1500; do iperf -c 192.168.14.1 -e -i 1 -M $MSS -l 8192 -P 1; sleep 2; done
+// sysview: SYSTICKS_PER_SEC=35000, IDLE_US=1000, PRINT_MOD=1000
+//
- uint8_t ep_notif;
- uint8_t ep_in;
- uint8_t ep_out;
+//-----------------------------------------------------------------------------
+//
+// 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,
+ },
+};
- const ndp16_t *ndp;
- uint8_t num_datagrams, current_datagram_index;
+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,
+};
- enum {
- REPORT_SPEED,
- REPORT_CONNECTED,
- REPORT_DONE
- } report_state;
- bool report_pending;
+/**
+ * Transmit next notification to the host (if appropriate).
+ * Notifications are transferred to the host once during connection setup.
+ */
+static void notification_xmit(uint8_t rhport, bool force_next) {
+ TU_LOG_DRV("notification_xmit(%d, %d) - %d %d\n", force_next, rhport, ncm_interface.notification_xmit_state, ncm_interface.notification_xmit_is_running);
- uint8_t current_ntb; // Index in transmit_ntb[] that is currently being filled with datagrams
- uint8_t datagram_count; // Number of datagrams in transmit_ntb[current_ntb]
- uint16_t next_datagram_offset; // Offset in transmit_ntb[current_ntb].data to place the next datagram
- uint16_t ntb_in_size; // Maximum size of transmitted (IN to host) NTBs; initially CFG_TUD_NCM_IN_NTB_MAX_SIZE
- uint8_t max_datagrams_per_ntb; // Maximum number of datagrams per NTB; initially CFG_TUD_NCM_MAX_DATAGRAMS_PER_NTB
+ if (!force_next && ncm_interface.notification_xmit_is_running) {
+ return;
+ }
- uint16_t nth_sequence; // Sequence number counter for transmitted NTBs
+ 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_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_interface.notification_xmit_state = NOTIFICATION_DONE;
+ ncm_interface.notification_xmit_is_running = true;
+ } else {
+ TU_LOG_DRV(" NOTIFICATION_FINISHED\n");
+ }
+} // notification_xmit
- bool transferring;
+//-----------------------------------------------------------------------------
+//
+// everything about packet transmission (driver -> TinyUSB)
+//
-} ncm_interface_t;
+/**
+ * Put NTB into the transmitter free list.
+ */
+static void xmit_put_ntb_into_free_list(xmit_ntb_t *free_ntb) {
+ TU_LOG_DRV("xmit_put_ntb_into_free_list() - %p\n", ncm_interface.xmit_tinyusb_ntb);
-//--------------------------------------------------------------------+
-// INTERNAL OBJECT & FUNCTION DECLARATION
-//--------------------------------------------------------------------+
+ if (free_ntb == NULL) { // can happen due to ZLPs
+ return;
+ }
-CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static const ntb_parameters_t ntb_parameters = {
- .wLength = sizeof(ntb_parameters_t),
- .bmNtbFormatsSupported = 0x01,
- .dwNtbInMaxSize = CFG_TUD_NCM_IN_NTB_MAX_SIZE,
- .wNdbInDivisor = 4,
- .wNdbInPayloadRemainder = 0,
- .wNdbInAlignment = CFG_TUD_NCM_ALIGNMENT,
- .wReserved = 0,
- .dwNtbOutMaxSize = CFG_TUD_NCM_OUT_NTB_MAX_SIZE,
- .wNdbOutDivisor = 4,
- .wNdbOutPayloadRemainder = 0,
- .wNdbOutAlignment = CFG_TUD_NCM_ALIGNMENT,
- .wNtbOutMaxDatagrams = 0
-};
+ for (int i = 0; i < XMIT_NTB_N; ++i) {
+ if (ncm_interface.xmit_free_ntb[i] == NULL) {
+ ncm_interface.xmit_free_ntb[i] = free_ntb;
+ return;
+ }
+ }
+ TU_LOG_DRV("(EE) xmit_put_ntb_into_free_list - no entry in free list\n");// this should not happen
+} // xmit_put_ntb_into_free_list
-CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static transmit_ntb_t transmit_ntb[2];
+/**
+ * Get an NTB from the free list
+ */
+static xmit_ntb_t *xmit_get_free_ntb(void) {
+ TU_LOG_DRV("xmit_get_free_ntb()\n");
-CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static uint8_t receive_ntb[CFG_TUD_NCM_OUT_NTB_MAX_SIZE];
+ for (int i = 0; i < XMIT_NTB_N; ++i) {
+ if (ncm_interface.xmit_free_ntb[i] != NULL) {
+ xmit_ntb_t *free = ncm_interface.xmit_free_ntb[i];
+ ncm_interface.xmit_free_ntb[i] = NULL;
+ return free;
+ }
+ }
+ return NULL;
+} // xmit_get_free_ntb
-tu_static ncm_interface_t ncm_interface;
+/**
+ * Put a filled NTB into the ready list
+ */
+static void xmit_put_ntb_into_ready_list(xmit_ntb_t *ready_ntb) {
+ TU_LOG_DRV("xmit_put_ntb_into_ready_list(%p) %d\n", ready_ntb, ready_ntb->nth.wBlockLength);
-/*
- * Set up the NTB state in ncm_interface to be ready to add datagrams.
+ for (int i = 0; i < XMIT_NTB_N; ++i) {
+ if (ncm_interface.xmit_ready_ntb[i] == NULL) {
+ ncm_interface.xmit_ready_ntb[i] = ready_ntb;
+ return;
+ }
+ }
+ TU_LOG_DRV("(EE) xmit_put_ntb_into_ready_list: ready list full\n");// this should not happen
+} // xmit_put_ntb_into_ready_list
+
+/**
+ * Get the next NTB from the ready list (and remove it from the list).
+ * If the ready list is empty, return NULL.
*/
-static void ncm_prepare_for_tx(void) {
- ncm_interface.datagram_count = 0;
- // datagrams start after all the headers
- ncm_interface.next_datagram_offset = sizeof(nth16_t) + sizeof(ndp16_t)
- + ((CFG_TUD_NCM_MAX_DATAGRAMS_PER_NTB + 1) * sizeof(ndp16_datagram_t));
-}
+static xmit_ntb_t *xmit_get_next_ready_ntb(void) {
+ xmit_ntb_t *r = NULL;
-/*
- * If not already transmitting, start sending the current NTB to the host and swap buffers
- * to start filling the other one with datagrams.
+ r = ncm_interface.xmit_ready_ntb[0];
+ memmove(ncm_interface.xmit_ready_ntb + 0, ncm_interface.xmit_ready_ntb + 1, sizeof(ncm_interface.xmit_ready_ntb) - sizeof(ncm_interface.xmit_ready_ntb[0]));
+ ncm_interface.xmit_ready_ntb[XMIT_NTB_N - 1] = NULL;
+
+ TU_LOG_DRV("recv_get_next_ready_ntb: %p\n", r);
+ return r;
+} // xmit_get_next_ready_ntb
+
+/**
+ * Transmit a ZLP if required
+ *
+ * \note
+ * Insertion of the ZLPs is a little bit different then described in the spec.
+ * But the below implementation actually works. Don't know if this is a spec
+ * or TinyUSB issue.
+ *
+ * \pre
+ * This must be called from netd_xfer_cb() so that ep_in is ready
*/
-static void ncm_start_tx(void) {
- if (ncm_interface.transferring) {
+static bool xmit_insert_required_zlp(uint8_t rhport, uint32_t xferred_bytes) {
+ TU_LOG_DRV("xmit_insert_required_zlp(%d,%ld)\n", rhport, xferred_bytes);
+
+ if (xferred_bytes == 0 || xferred_bytes % CFG_TUD_NET_ENDPOINT_SIZE != 0) {
+ return false;
+ }
+
+ TU_ASSERT(ncm_interface.itf_data_alt == 1, false);
+ TU_ASSERT(!usbd_edpt_busy(rhport, ncm_interface.ep_in), false);
+
+ TU_LOG_DRV("xmit_insert_required_zlp! (%u)\n", (unsigned) xferred_bytes);
+
+ // start transmission of the ZLP
+ usbd_edpt_xfer(rhport, ncm_interface.ep_in, NULL, 0);
+
+ return true;
+} // xmit_insert_required_zlp
+
+/**
+ * Start transmission if it there is a waiting packet and if can be done from interface side.
+ */
+static void xmit_start_if_possible(uint8_t rhport) {
+ TU_LOG_DRV("xmit_start_if_possible()\n");
+
+ if (ncm_interface.xmit_tinyusb_ntb != NULL) {
+ TU_LOG_DRV(" !xmit_start_if_possible 1\n");
+ return;
+ }
+ if (ncm_interface.itf_data_alt != 1) {
+ TU_LOG_DRV("(EE) !xmit_start_if_possible 2\n");
+ return;
+ }
+ if (usbd_edpt_busy(rhport, ncm_interface.ep_in)) {
+ TU_LOG_DRV(" !xmit_start_if_possible 3\n");
return;
}
- transmit_ntb_t *ntb = &transmit_ntb[ncm_interface.current_ntb];
- size_t ntb_length = ncm_interface.next_datagram_offset;
+ ncm_interface.xmit_tinyusb_ntb = xmit_get_next_ready_ntb();
+ if (ncm_interface.xmit_tinyusb_ntb == NULL) {
+ if (ncm_interface.xmit_glue_ntb == NULL || ncm_interface.xmit_glue_ntb_datagram_ndx == 0) {
+ // -> really nothing is waiting
+ return;
+ }
+ ncm_interface.xmit_tinyusb_ntb = ncm_interface.xmit_glue_ntb;
+ ncm_interface.xmit_glue_ntb = NULL;
+ }
+
+ #if CFG_TUD_NCM_LOG_LEVEL >= 3
+ {
+ uint16_t len = ncm_interface.xmit_tinyusb_ntb->nth.wBlockLength;
+ TU_LOG_BUF(3, ncm_interface.xmit_tinyusb_ntb->data[i], len);
+ }
+ #endif
+
+ if (ncm_interface.xmit_glue_ntb_datagram_ndx != 1) {
+ TU_LOG_DRV(">> %d %d\n", ncm_interface.xmit_tinyusb_ntb->nth.wBlockLength, ncm_interface.xmit_glue_ntb_datagram_ndx);
+ }
+
+ // Kick off an endpoint transfer
+ usbd_edpt_xfer(0, ncm_interface.ep_in, ncm_interface.xmit_tinyusb_ntb->data, ncm_interface.xmit_tinyusb_ntb->nth.wBlockLength);
+} // xmit_start_if_possible
+
+/**
+ * check if a new datagram fits into the current NTB
+ */
+static bool xmit_requested_datagram_fits_into_current_ntb(uint16_t datagram_size) {
+ TU_LOG_DRV("xmit_requested_datagram_fits_into_current_ntb(%d) - %p %p\n", datagram_size, ncm_interface.xmit_tinyusb_ntb, ncm_interface.xmit_glue_ntb);
+
+ if (ncm_interface.xmit_glue_ntb == NULL) {
+ return false;
+ }
+ if (ncm_interface.xmit_glue_ntb_datagram_ndx >= CFG_TUD_NCM_IN_MAX_DATAGRAMS_PER_NTB) {
+ return false;
+ }
+ if (ncm_interface.xmit_glue_ntb->nth.wBlockLength + datagram_size + XMIT_ALIGN_OFFSET(datagram_size) > CFG_TUD_NCM_OUT_NTB_MAX_SIZE) {
+ return false;
+ }
+ return true;
+} // xmit_requested_datagram_fits_into_current_ntb
+
+/**
+ * Setup an NTB for the glue logic
+ */
+static bool xmit_setup_next_glue_ntb(void) {
+ TU_LOG_DRV("xmit_setup_next_glue_ntb - %p\n", ncm_interface.xmit_glue_ntb);
+
+ if (ncm_interface.xmit_glue_ntb != NULL) {
+ // put NTB into waiting list (the new datagram did not fit in)
+ xmit_put_ntb_into_ready_list(ncm_interface.xmit_glue_ntb);
+ }
+
+ ncm_interface.xmit_glue_ntb = xmit_get_free_ntb();// get next buffer (if any)
+ if (ncm_interface.xmit_glue_ntb == NULL) {
+ TU_LOG_DRV(" xmit_setup_next_glue_ntb - nothing free\n");// should happen rarely
+ return false;
+ }
+
+ ncm_interface.xmit_glue_ntb_datagram_ndx = 0;
+
+ xmit_ntb_t *ntb = ncm_interface.xmit_glue_ntb;
// Fill in NTB header
ntb->nth.dwSignature = NTH16_SIGNATURE;
- ntb->nth.wHeaderLength = sizeof(nth16_t);
- ntb->nth.wSequence = ncm_interface.nth_sequence++;
- ntb->nth.wBlockLength = ntb_length;
- ntb->nth.wNdpIndex = sizeof(nth16_t);
+ ntb->nth.wHeaderLength = sizeof(ntb->nth);
+ ntb->nth.wSequence = ncm_interface.xmit_sequence++;
+ ntb->nth.wBlockLength = sizeof(ntb->nth) + sizeof(ntb->ndp) + sizeof(ntb->ndp_datagram);
+ ntb->nth.wNdpIndex = sizeof(ntb->nth);
// Fill in NDP16 header and terminator
ntb->ndp.dwSignature = NDP16_SIGNATURE_NCM0;
- ntb->ndp.wLength = sizeof(ndp16_t) + (ncm_interface.datagram_count + 1) * sizeof(ndp16_datagram_t);
+ ntb->ndp.wLength = sizeof(ntb->ndp) + sizeof(ntb->ndp_datagram);
ntb->ndp.wNextNdpIndex = 0;
- ntb->ndp.datagram[ncm_interface.datagram_count].wDatagramIndex = 0;
- ntb->ndp.datagram[ncm_interface.datagram_count].wDatagramLength = 0;
-
- // Kick off an endpoint transfer
- usbd_edpt_xfer(0, ncm_interface.ep_in, ntb->data, ntb_length);
- ncm_interface.transferring = true;
- // Swap to the other NTB and clear it out
- ncm_interface.current_ntb = 1 - ncm_interface.current_ntb;
- ncm_prepare_for_tx();
-}
+ memset(ntb->ndp_datagram, 0, sizeof(ntb->ndp_datagram));
+ return true;
+} // xmit_setup_next_glue_ntb
-tu_static struct ecm_notify_struct 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,
- },
-};
+//-----------------------------------------------------------------------------
+//
+// all the recv_*() stuff (TinyUSB -> driver -> glue logic)
+//
-tu_static struct ecm_notify_struct 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 = 10000000,
- .uplink = 10000000,
-};
+/**
+ * Return pointer to an available receive buffer or NULL.
+ * Returned buffer (if any) has the size \a CFG_TUD_NCM_OUT_NTB_MAX_SIZE.
+ */
+static recv_ntb_t *recv_get_free_ntb(void) {
+ TU_LOG_DRV("recv_get_free_ntb()\n");
-void tud_network_recv_renew(void)
-{
- if (!ncm_interface.num_datagrams)
- {
- usbd_edpt_xfer(0, ncm_interface.ep_out, receive_ntb, sizeof(receive_ntb));
- return;
+ for (int i = 0; i < RECV_NTB_N; ++i) {
+ if (ncm_interface.recv_free_ntb[i] != NULL) {
+ recv_ntb_t *free = ncm_interface.recv_free_ntb[i];
+ ncm_interface.recv_free_ntb[i] = NULL;
+ return free;
+ }
}
+ return NULL;
+} // recv_get_free_ntb
- const ndp16_t *ndp = ncm_interface.ndp;
- const int i = ncm_interface.current_datagram_index;
- ncm_interface.current_datagram_index++;
- ncm_interface.num_datagrams--;
+/**
+ * Get the next NTB from the ready list (and remove it from the list).
+ * If the ready list is empty, return NULL.
+ */
+static recv_ntb_t *recv_get_next_ready_ntb(void) {
+ recv_ntb_t *r = NULL;
- tud_network_recv_cb(receive_ntb + ndp->datagram[i].wDatagramIndex, ndp->datagram[i].wDatagramLength);
-}
+ r = ncm_interface.recv_ready_ntb[0];
+ memmove(ncm_interface.recv_ready_ntb + 0, ncm_interface.recv_ready_ntb + 1, sizeof(ncm_interface.recv_ready_ntb) - sizeof(ncm_interface.recv_ready_ntb[0]));
+ ncm_interface.recv_ready_ntb[RECV_NTB_N - 1] = NULL;
-//--------------------------------------------------------------------+
-// USBD Driver API
-//--------------------------------------------------------------------+
+ TU_LOG_DRV("recv_get_next_ready_ntb: %p\n", r);
+ return r;
+} // recv_get_next_ready_ntb
-void netd_init(void)
-{
- tu_memclr(&ncm_interface, sizeof(ncm_interface));
- ncm_interface.ntb_in_size = CFG_TUD_NCM_IN_NTB_MAX_SIZE;
- ncm_interface.max_datagrams_per_ntb = CFG_TUD_NCM_MAX_DATAGRAMS_PER_NTB;
- ncm_prepare_for_tx();
-}
+/**
+ * Put NTB into the receiver free list.
+ */
+static void recv_put_ntb_into_free_list(recv_ntb_t *free_ntb) {
+ TU_LOG_DRV("recv_put_ntb_into_free_list(%p)\n", free_ntb);
-bool netd_deinit(void) {
- return true;
-}
+ for (int i = 0; i < RECV_NTB_N; ++i) {
+ if (ncm_interface.recv_free_ntb[i] == NULL) {
+ ncm_interface.recv_free_ntb[i] = free_ntb;
+ return;
+ }
+ }
+ TU_LOG_DRV("(EE) recv_put_ntb_into_free_list - no entry in free list\n");// this should not happen
+} // recv_put_ntb_into_free_list
-void netd_reset(uint8_t rhport)
-{
- (void) rhport;
+/**
+ * \a ready_ntb holds a validated NTB,
+ * put this buffer into the waiting list.
+ */
+static void recv_put_ntb_into_ready_list(recv_ntb_t *ready_ntb) {
+ TU_LOG_DRV("recv_put_ntb_into_ready_list(%p) %d\n", ready_ntb, ready_ntb->nth.wBlockLength);
- netd_init();
-}
+ for (int i = 0; i < RECV_NTB_N; ++i) {
+ if (ncm_interface.recv_ready_ntb[i] == NULL) {
+ ncm_interface.recv_ready_ntb[i] = ready_ntb;
+ return;
+ }
+ }
+ TU_LOG_DRV("(EE) recv_put_ntb_into_ready_list: ready list full\n");// this should not happen
+} // recv_put_ntb_into_ready_list
-uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
-{
- // confirm interface hasn't already been allocated
- TU_ASSERT(0 == ncm_interface.ep_notif, 0);
+/**
+ * If possible, start a new reception TinyUSB -> driver.
+ */
+static void recv_try_to_start_new_reception(uint8_t rhport) {
+ TU_LOG_DRV("recv_try_to_start_new_reception(%d)\n", rhport);
- //------------- Management Interface -------------//
- ncm_interface.itf_num = itf_desc->bInterfaceNumber;
+ if (ncm_interface.itf_data_alt != 1) {
+ return;
+ }
+ if (ncm_interface.recv_tinyusb_ntb != NULL) {
+ return;
+ }
+ if (usbd_edpt_busy(rhport, ncm_interface.ep_out)) {
+ return;
+ }
- uint16_t drv_len = sizeof(tusb_desc_interface_t);
- uint8_t const * p_desc = tu_desc_next( itf_desc );
+ ncm_interface.recv_tinyusb_ntb = recv_get_free_ntb();
+ if (ncm_interface.recv_tinyusb_ntb == NULL) {
+ return;
+ }
- // Communication Functional Descriptors
- 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);
+ // initiate transfer
+ TU_LOG_DRV(" start reception\n");
+ bool r = usbd_edpt_xfer(rhport, ncm_interface.ep_out, ncm_interface.recv_tinyusb_ntb->data, CFG_TUD_NCM_OUT_NTB_MAX_SIZE);
+ if (!r) {
+ recv_put_ntb_into_free_list(ncm_interface.recv_tinyusb_ntb);
+ ncm_interface.recv_tinyusb_ntb = NULL;
}
+} // recv_try_to_start_new_reception
- // 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 );
+/**
+ * Validate incoming datagram.
+ * \return true if valid
+ *
+ * \note
+ * \a ndp16->wNextNdpIndex != 0 is not supported
+ */
+static bool recv_validate_datagram(const recv_ntb_t *ntb, uint32_t len) {
+ const nth16_t *nth16 = &(ntb->nth);
- ncm_interface.ep_notif = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
+ TU_LOG_DRV("recv_validate_datagram(%p, %d)\n", ntb, (int) len);
- drv_len += tu_desc_len(p_desc);
- p_desc = tu_desc_next(p_desc);
+ // check header
+ if (nth16->wHeaderLength != sizeof(nth16_t)) {
+ TU_LOG_DRV("(EE) ill nth16 length: %d\n", nth16->wHeaderLength);
+ return false;
+ }
+ if (nth16->dwSignature != NTH16_SIGNATURE) {
+ TU_LOG_DRV("(EE) ill signature: 0x%08x\n", (unsigned) nth16->dwSignature);
+ return false;
+ }
+ if (len < sizeof(nth16_t) + sizeof(ndp16_t) + 2 * sizeof(ndp16_datagram_t)) {
+ TU_LOG_DRV("(EE) ill min len: %lu\n", len);
+ return false;
+ }
+ if (nth16->wBlockLength > len) {
+ TU_LOG_DRV("(EE) ill block length: %d > %lu\n", nth16->wBlockLength, len);
+ return false;
+ }
+ if (nth16->wBlockLength > CFG_TUD_NCM_OUT_NTB_MAX_SIZE) {
+ TU_LOG_DRV("(EE) ill block length2: %d > %d\n", nth16->wBlockLength, CFG_TUD_NCM_OUT_NTB_MAX_SIZE);
+ return false;
+ }
+ if (nth16->wNdpIndex < sizeof(nth16) || nth16->wNdpIndex > len - (sizeof(ndp16_t) + 2 * sizeof(ndp16_datagram_t))) {
+ TU_LOG_DRV("(EE) ill position of first ndp: %d (%lu)\n", nth16->wNdpIndex, len);
+ return false;
}
- //------------- Data Interface -------------//
- // - CDC-NCM data interface has 2 alternate settings
- // - 0 : zero endpoints for inactive (default)
- // - 1 : IN & OUT endpoints for transfer of NTBs
- TU_ASSERT(TUSB_DESC_INTERFACE == tu_desc_type(p_desc), 0);
+ // check (first) NDP(16)
+ const ndp16_t *ndp16 = (const ndp16_t *) (ntb->data + nth16->wNdpIndex);
- 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);
+ if (ndp16->wLength < sizeof(ndp16_t) + 2 * sizeof(ndp16_datagram_t)) {
+ TU_LOG_DRV("(EE) ill ndp16 length: %d\n", ndp16->wLength);
+ return false;
+ }
+ if (ndp16->dwSignature != NDP16_SIGNATURE_NCM0 && ndp16->dwSignature != NDP16_SIGNATURE_NCM1) {
+ TU_LOG_DRV("(EE) ill signature: 0x%08x\n", (unsigned) ndp16->dwSignature);
+ return false;
+ }
+ if (ndp16->wNextNdpIndex != 0) {
+ TU_LOG_DRV("(EE) cannot handle wNextNdpIndex!=0 (%d)\n", ndp16->wNextNdpIndex);
+ return false;
+ }
- drv_len += tu_desc_len(p_desc);
- p_desc = tu_desc_next(p_desc);
- } while((TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) && (drv_len <= max_len));
+ const ndp16_datagram_t *ndp16_datagram = (const ndp16_datagram_t *) (ntb->data + nth16->wNdpIndex + sizeof(ndp16_t));
+ int ndx = 0;
+ uint16_t max_ndx = (uint16_t) ((ndp16->wLength - sizeof(ndp16_t)) / sizeof(ndp16_datagram_t));
- // Pair of endpoints
- TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc), 0);
+ if (max_ndx > 2) { // number of datagrams in NTB > 1
+ TU_LOG_DRV("<< %d (%d)\n", max_ndx - 1, ntb->nth.wBlockLength);
+ }
+ if (ndp16_datagram[max_ndx - 1].wDatagramIndex != 0 || ndp16_datagram[max_ndx - 1].wDatagramLength != 0) {
+ TU_LOG_DRV(" max_ndx != 0\n");
+ return false;
+ }
+ while (ndp16_datagram[ndx].wDatagramIndex != 0 && ndp16_datagram[ndx].wDatagramLength != 0) {
+ TU_LOG_DRV(" << %d %d\n", ndp16_datagram[ndx].wDatagramIndex, ndp16_datagram[ndx].wDatagramLength);
+ if (ndp16_datagram[ndx].wDatagramIndex > len) {
+ TU_LOG_DRV("(EE) ill start of datagram[%d]: %d (%lu)\n", ndx, ndp16_datagram[ndx].wDatagramIndex, len);
+ return false;
+ }
+ if (ndp16_datagram[ndx].wDatagramIndex + ndp16_datagram[ndx].wDatagramLength > len) {
+ TU_LOG_DRV("(EE) ill end of datagram[%d]: %d (%lu)\n", ndx, ndp16_datagram[ndx].wDatagramIndex + ndp16_datagram[ndx].wDatagramLength, len);
+ return false;
+ }
+ ++ndx;
+ }
- TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &ncm_interface.ep_out, &ncm_interface.ep_in) );
+ #if CFG_TUD_NCM_LOG_LEVEL >= 3
+ TU_LOG_BUF(3, ntb->data[i], len);
+ #endif
- drv_len += 2*sizeof(tusb_desc_endpoint_t);
+ // -> ntb contains a valid packet structure
+ // ok... I did not check for garbage within the datagram indices...
+ return true;
+} // recv_validate_datagram
- return drv_len;
-}
+/**
+ * Transfer the next (pending) datagram to the glue logic and return receive buffer if empty.
+ */
+static void recv_transfer_datagram_to_glue_logic(void) {
+ TU_LOG_DRV("recv_transfer_datagram_to_glue_logic()\n");
-static void ncm_report(void)
-{
- uint8_t const rhport = 0;
- if (ncm_interface.report_state == REPORT_SPEED) {
- 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_interface.report_state = REPORT_CONNECTED;
- ncm_interface.report_pending = true;
- } else if (ncm_interface.report_state == REPORT_CONNECTED) {
- 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_interface.report_state = REPORT_DONE;
- ncm_interface.report_pending = true;
+ if (ncm_interface.recv_glue_ntb == NULL) {
+ ncm_interface.recv_glue_ntb = recv_get_next_ready_ntb();
+ TU_LOG_DRV(" new buffer for glue logic: %p\n", ncm_interface.recv_glue_ntb);
+ ncm_interface.recv_glue_ntb_datagram_ndx = 0;
}
-}
-TU_ATTR_WEAK void tud_network_link_state_cb(bool state)
-{
- (void)state;
-}
+ if (ncm_interface.recv_glue_ntb != NULL) {
+ const ndp16_datagram_t *ndp16_datagram = (ndp16_datagram_t *) (ncm_interface.recv_glue_ntb->data + ncm_interface.recv_glue_ntb->nth.wNdpIndex + sizeof(ndp16_t));
-// Handle class control request
-// 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 ) return true;
+ if (ndp16_datagram[ncm_interface.recv_glue_ntb_datagram_ndx].wDatagramIndex == 0) {
+ TU_LOG_DRV("(EE) SOMETHING WENT WRONG 1\n");
+ } else if (ndp16_datagram[ncm_interface.recv_glue_ntb_datagram_ndx].wDatagramLength == 0) {
+ TU_LOG_DRV("(EE) SOMETHING WENT WRONG 2\n");
+ } else {
+ uint16_t datagramIndex = ndp16_datagram[ncm_interface.recv_glue_ntb_datagram_ndx].wDatagramIndex;
+ uint16_t datagramLength = ndp16_datagram[ncm_interface.recv_glue_ntb_datagram_ndx].wDatagramLength;
- 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;
- TU_VERIFY(ncm_interface.itf_num + 1 == req_itfnum);
+ TU_LOG_DRV(" recv[%d] - %d %d\n", ncm_interface.recv_glue_ntb_datagram_ndx, datagramIndex, datagramLength);
+ if (tud_network_recv_cb(ncm_interface.recv_glue_ntb->data + datagramIndex, datagramLength)) {
+ // send datagram successfully to glue logic
+ TU_LOG_DRV(" OK\n");
+ datagramIndex = ndp16_datagram[ncm_interface.recv_glue_ntb_datagram_ndx + 1].wDatagramIndex;
+ datagramLength = ndp16_datagram[ncm_interface.recv_glue_ntb_datagram_ndx + 1].wDatagramLength;
- tud_control_xfer(rhport, request, &ncm_interface.itf_data_alt, 1);
+ if (datagramIndex != 0 && datagramLength != 0) {
+ // -> next datagram
+ ++ncm_interface.recv_glue_ntb_datagram_ndx;
+ } else {
+ // end of datagrams reached
+ recv_put_ntb_into_free_list(ncm_interface.recv_glue_ntb);
+ ncm_interface.recv_glue_ntb = NULL;
}
- break;
+ }
+ }
+ }
+} // recv_transfer_datagram_to_glue_logic
- case TUSB_REQ_SET_INTERFACE:
- {
- uint8_t const req_itfnum = (uint8_t) request->wIndex;
- uint8_t const req_alt = (uint8_t) request->wValue;
+//-----------------------------------------------------------------------------
+//
+// all the tud_network_*() stuff (glue logic -> driver)
+//
- // Only valid for Data Interface with Alternate is either 0 or 1
- TU_VERIFY(ncm_interface.itf_num + 1 == req_itfnum && req_alt < 2);
+/**
+ * Check if the glue logic is allowed to call tud_network_xmit().
+ * This function also fetches a next buffer if required, so that tud_network_xmit() is ready for copy
+ * and transmission operation.
+ */
+bool tud_network_can_xmit(uint16_t size) {
+ TU_LOG_DRV("tud_network_can_xmit(%d)\n", size);
- if (req_alt != ncm_interface.itf_data_alt) {
- ncm_interface.itf_data_alt = req_alt;
+ TU_ASSERT(size <= CFG_TUD_NCM_OUT_NTB_MAX_SIZE - (sizeof(nth16_t) + sizeof(ndp16_t) + 2 * sizeof(ndp16_datagram_t)), false);
- if (ncm_interface.itf_data_alt) {
- if (!usbd_edpt_busy(rhport, ncm_interface.ep_out)) {
- tud_network_recv_renew(); // prepare for incoming datagrams
- }
- if (!ncm_interface.report_pending) {
- ncm_report();
- }
- }
+ if (xmit_requested_datagram_fits_into_current_ntb(size) || xmit_setup_next_glue_ntb()) {
+ // -> everything is fine
+ return true;
+ }
+ xmit_start_if_possible(ncm_interface.rhport);
+ TU_LOG_DRV("(II) tud_network_can_xmit: request blocked\n");// could happen if all xmit buffers are full (but should happen rarely)
+ return false;
+} // tud_network_can_xmit
- tud_network_link_state_cb(ncm_interface.itf_data_alt);
- }
+/**
+ * Put a datagram into a waiting NTB.
+ * If currently no transmission is started, then initiate transmission.
+ */
+void tud_network_xmit(void *ref, uint16_t arg) {
+ TU_LOG_DRV("tud_network_xmit(%p, %d)\n", ref, arg);
- tud_control_status(rhport, request);
- }
- break;
+ if (ncm_interface.xmit_glue_ntb == NULL) {
+ TU_LOG_DRV("(EE) tud_network_xmit: no buffer\n");// must not happen (really)
+ return;
+ }
- // unsupported request
- default: return false;
- }
- break;
+ xmit_ntb_t *ntb = ncm_interface.xmit_glue_ntb;
- case TUSB_REQ_TYPE_CLASS:
- TU_VERIFY (ncm_interface.itf_num == request->wIndex);
+ // copy new datagram to the end of the current NTB
+ uint16_t size = tud_network_xmit_cb(ntb->data + ntb->nth.wBlockLength, ref, arg);
- if (NCM_GET_NTB_PARAMETERS == request->bRequest)
- {
- tud_control_xfer(rhport, request, (void*)(uintptr_t) &ntb_parameters, sizeof(ntb_parameters));
- }
+ // correct NTB internals
+ ntb->ndp_datagram[ncm_interface.xmit_glue_ntb_datagram_ndx].wDatagramIndex = ntb->nth.wBlockLength;
+ ntb->ndp_datagram[ncm_interface.xmit_glue_ntb_datagram_ndx].wDatagramLength = size;
+ ncm_interface.xmit_glue_ntb_datagram_ndx += 1;
- break;
+ ntb->nth.wBlockLength += (uint16_t) (size + XMIT_ALIGN_OFFSET(size));
- // unsupported request
- default: return false;
+ if (ntb->nth.wBlockLength > CFG_TUD_NCM_OUT_NTB_MAX_SIZE) {
+ TU_LOG_DRV("(EE) tud_network_xmit: buffer overflow\n"); // must not happen (really)
+ return;
}
- return true;
-}
+ xmit_start_if_possible(ncm_interface.rhport);
+} // tud_network_xmit
-static void handle_incoming_datagram(uint32_t len)
-{
- uint32_t size = len;
+/**
+ * Keep the receive logic busy and transfer pending packets to the glue logic.
+ */
+void tud_network_recv_renew(void) {
+ TU_LOG_DRV("tud_network_recv_renew()\n");
- if (len == 0) {
- return;
- }
+ recv_transfer_datagram_to_glue_logic();
+ recv_try_to_start_new_reception(ncm_interface.rhport);
+} // tud_network_recv_renew
- TU_ASSERT(size >= sizeof(nth16_t), );
+/**
+ * Same as tud_network_recv_renew() but knows \a rhport
+ */
+void tud_network_recv_renew_r(uint8_t rhport) {
+ TU_LOG_DRV("tud_network_recv_renew_r(%d)\n", rhport);
- const nth16_t *hdr = (const nth16_t *)receive_ntb;
- TU_ASSERT(hdr->dwSignature == NTH16_SIGNATURE, );
- TU_ASSERT(hdr->wNdpIndex >= sizeof(nth16_t) && (hdr->wNdpIndex + sizeof(ndp16_t)) <= len, );
+ ncm_interface.rhport = rhport;
+ tud_network_recv_renew();
+} // tud_network_recv_renew
- const ndp16_t *ndp = (const ndp16_t *)(receive_ntb + hdr->wNdpIndex);
- TU_ASSERT(ndp->dwSignature == NDP16_SIGNATURE_NCM0 || ndp->dwSignature == NDP16_SIGNATURE_NCM1, );
- TU_ASSERT(hdr->wNdpIndex + ndp->wLength <= len, );
+//-----------------------------------------------------------------------------
+//
+// all the netd_*() stuff (interface TinyUSB -> driver)
+//
+/**
+ * Initialize the driver data structures.
+ * Might be called several times.
+ */
+void netd_init(void) {
+ TU_LOG_DRV("netd_init()\n");
- int num_datagrams = (ndp->wLength - 12) / 4;
- ncm_interface.current_datagram_index = 0;
- ncm_interface.num_datagrams = 0;
- ncm_interface.ndp = ndp;
- for (int i = 0; i < num_datagrams && ndp->datagram[i].wDatagramIndex && ndp->datagram[i].wDatagramLength; i++)
- {
- ncm_interface.num_datagrams++;
+ 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;
}
+ for (int i = 0; i < RECV_NTB_N; ++i) {
+ ncm_interface.recv_free_ntb[i] = ncm_interface.recv_ntb + i;
+ }
+} // netd_init
- tud_network_recv_renew();
+/**
+ * Deinit driver
+ */
+bool netd_deinit(void) {
+ return true;
}
-bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
-{
+/**
+ * Resets the port.
+ * In this driver this is the same as netd_init()
+ */
+void netd_reset(uint8_t rhport) {
(void) rhport;
- (void) result;
- /* new datagram receive_ntb */
- if (ep_addr == ncm_interface.ep_out )
- {
- handle_incoming_datagram(xferred_bytes);
- }
+ netd_init();
+} // netd_reset
- /* data transmission finished */
- if (ep_addr == ncm_interface.ep_in )
- {
- if (ncm_interface.transferring) {
- ncm_interface.transferring = false;
- }
+/**
+ * Open the USB interface.
+ * - parse the USB descriptor \a TUD_CDC_NCM_DESCRIPTOR for itfnum and endpoints
+ * - a specific order of elements in the descriptor is tested.
+ *
+ * \note
+ * Actually all of the information could be read directly from \a itf_desc, because the
+ * structure and the values are well known. But we do it this way.
+ *
+ * \post
+ * - \a itf_num set
+ * - \a ep_notif, \a ep_in and \a ep_out are set
+ * - USB interface is open
+ */
+uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
+ TU_ASSERT(ncm_interface.ep_notif == 0, 0);// assure that the interface is only opened once
- // If there are datagrams queued up that we tried to send while this NTB was being emitted, send them now
- if (ncm_interface.datagram_count && ncm_interface.itf_data_alt == 1) {
- ncm_start_tx();
- }
- }
+ ncm_interface.itf_num = itf_desc->bInterfaceNumber;// management interface
- if (ep_addr == ncm_interface.ep_notif )
- {
- ncm_interface.report_pending = false;
- ncm_report();
+ // skip the two first entries and the following TUSB_DESC_CS_INTERFACE entries
+ uint16_t drv_len = sizeof(tusb_desc_interface_t);
+ uint8_t const *p_desc = tu_desc_next(itf_desc);
+ while (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && drv_len <= max_len) {
+ drv_len += tu_desc_len(p_desc);
+ p_desc = tu_desc_next(p_desc);
}
- return true;
-}
+ // get notification endpoint
+ TU_ASSERT(tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT, 0);
+ TU_ASSERT(usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), 0);
+ ncm_interface.ep_notif = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
+ drv_len += tu_desc_len(p_desc);
+ p_desc = tu_desc_next(p_desc);
-// poll network driver for its ability to accept another packet to transmit
-bool tud_network_can_xmit(uint16_t size)
-{
- TU_VERIFY(ncm_interface.itf_data_alt == 1);
+ // skip the following TUSB_DESC_INTERFACE entries (which must be TUSB_CLASS_CDC_DATA)
+ while (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && drv_len <= max_len) {
+ tusb_desc_interface_t const *data_itf_desc = (tusb_desc_interface_t const *) p_desc;
+ TU_ASSERT(data_itf_desc->bInterfaceClass == TUSB_CLASS_CDC_DATA, 0);
- if (ncm_interface.datagram_count >= ncm_interface.max_datagrams_per_ntb) {
- TU_LOG_DRV("NTB full [by count]\r\n");
- return false;
+ drv_len += tu_desc_len(p_desc);
+ p_desc = tu_desc_next(p_desc);
}
- size_t next_datagram_offset = ncm_interface.next_datagram_offset;
- if (next_datagram_offset + size > ncm_interface.ntb_in_size) {
- TU_LOG_DRV("ntb full [by size]\r\n");
- return false;
+ // a TUSB_DESC_ENDPOINT (actually two) must follow, open these endpoints
+ TU_ASSERT(tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT, 0);
+ TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &ncm_interface.ep_out, &ncm_interface.ep_in));
+ drv_len += 2 * sizeof(tusb_desc_endpoint_t);
+
+ return drv_len;
+} // netd_open
+
+/**
+ * Handle TinyUSB requests to process transfer events.
+ */
+bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
+ (void) result;
+
+ if (ep_addr == ncm_interface.ep_out) {
+ // new NTB received
+ // - make the NTB valid
+ // - if ready transfer datagrams to the glue logic for further processing
+ // - if there is a free receive buffer, initiate reception
+ if (!recv_validate_datagram(ncm_interface.recv_tinyusb_ntb, xferred_bytes)) {
+ // verification failed: ignore NTB and return it to free
+ TU_LOG_DRV("(EE) VALIDATION FAILED. WHAT CAN WE DO IN THIS CASE?\n");
+ } else {
+ // packet ok -> put it into ready list
+ recv_put_ntb_into_ready_list(ncm_interface.recv_tinyusb_ntb);
+ }
+ ncm_interface.recv_tinyusb_ntb = NULL;
+ tud_network_recv_renew_r(rhport);
+ } else if (ep_addr == ncm_interface.ep_in) {
+ // transmission of an NTB finished
+ // - free the transmitted NTB buffer
+ // - insert ZLPs when necessary
+ // - if there is another transmit NTB waiting, try to start transmission
+ xmit_put_ntb_into_free_list(ncm_interface.xmit_tinyusb_ntb);
+ ncm_interface.xmit_tinyusb_ntb = NULL;
+ if (!xmit_insert_required_zlp(rhport, xferred_bytes)) {
+ xmit_start_if_possible(rhport);
+ }
+ } else if (ep_addr == ncm_interface.ep_notif) {
+ // next transfer on notification channel
+ notification_xmit(rhport, true);
}
return true;
-}
+} // netd_xfer_cb
-void tud_network_xmit(void *ref, uint16_t arg)
-{
- transmit_ntb_t *ntb = &transmit_ntb[ncm_interface.current_ntb];
- size_t next_datagram_offset = ncm_interface.next_datagram_offset;
+/**
+ * Respond to TinyUSB control requests.
+ * At startup transmission of notification packets are done here.
+ */
+bool netd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) {
+ if (stage != CONTROL_STAGE_SETUP) {
+ return true;
+ }
- uint16_t size = tud_network_xmit_cb(ntb->data + next_datagram_offset, ref, arg);
+ switch (request->bmRequestType_bit.type) {
+ case TUSB_REQ_TYPE_STANDARD:
- ntb->ndp.datagram[ncm_interface.datagram_count].wDatagramIndex = ncm_interface.next_datagram_offset;
- ntb->ndp.datagram[ncm_interface.datagram_count].wDatagramLength = size;
+ switch (request->bRequest) {
+ case TUSB_REQ_GET_INTERFACE: {
+ TU_VERIFY(ncm_interface.itf_num + 1 == request->wIndex, false);
- ncm_interface.datagram_count++;
- next_datagram_offset += size;
+ tud_control_xfer(rhport, request, &ncm_interface.itf_data_alt, 1);
+ } break;
- // round up so the next datagram is aligned correctly
- next_datagram_offset += (CFG_TUD_NCM_ALIGNMENT - 1);
- next_datagram_offset -= (next_datagram_offset % CFG_TUD_NCM_ALIGNMENT);
+ case TUSB_REQ_SET_INTERFACE: {
+ TU_VERIFY(ncm_interface.itf_num + 1 == request->wIndex && request->wValue < 2, false);
- ncm_interface.next_datagram_offset = next_datagram_offset;
+ ncm_interface.itf_data_alt = (uint8_t) request->wValue;
- ncm_start_tx();
-}
+ if (ncm_interface.itf_data_alt == 1) {
+ tud_network_recv_renew_r(rhport);
+ notification_xmit(rhport, false);
+ }
+ tud_control_status(rhport, request);
+ } break;
-#endif
+ // unsupported request
+ default:
+ return false;
+ }
+ break;
+
+ case TUSB_REQ_TYPE_CLASS:
+ TU_VERIFY(ncm_interface.itf_num == request->wIndex, false);
+ switch (request->bRequest) {
+ case NCM_GET_NTB_PARAMETERS: {
+ // transfer NTB parameters to host.
+ tud_control_xfer(rhport, request, (void *) (uintptr_t) &ntb_parameters, sizeof(ntb_parameters));
+ } break;
+
+ // unsupported request
+ default:
+ return false;
+ }
+ break;
+ // unsupported request
+ default:
+ return false;
+ }
+
+ return true;
+} // netd_control_xfer_cb
+
+#endif // ( CFG_TUD_ENABLED && CFG_TUD_NCM )
diff --git a/src/class/net/net_device.h b/src/class/net/net_device.h
index da1a7b1e8..4c9a92f2d 100644
--- a/src/class/net/net_device.h
+++ b/src/class/net/net_device.h
@@ -28,14 +28,13 @@
#ifndef _TUSB_NET_DEVICE_H_
#define _TUSB_NET_DEVICE_H_
+#include <stdint.h>
#include "class/cdc/cdc.h"
#if CFG_TUD_ECM_RNDIS && CFG_TUD_NCM
#error "Cannot enable both ECM_RNDIS and NCM network drivers"
#endif
-#include "ncm.h"
-
/* declared here, NOT in usb_descriptors.c, so that the driver can intelligently ZLP as needed */
#define CFG_TUD_NET_ENDPOINT_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
@@ -44,21 +43,13 @@
#define CFG_TUD_NET_MTU 1514
#endif
-#ifndef CFG_TUD_NCM_IN_NTB_MAX_SIZE
-#define CFG_TUD_NCM_IN_NTB_MAX_SIZE 3200
-#endif
-
-#ifndef CFG_TUD_NCM_OUT_NTB_MAX_SIZE
-#define CFG_TUD_NCM_OUT_NTB_MAX_SIZE 3200
-#endif
-#ifndef CFG_TUD_NCM_MAX_DATAGRAMS_PER_NTB
-#define CFG_TUD_NCM_MAX_DATAGRAMS_PER_NTB 8
-#endif
+// Table 4.3 Data Class Interface Protocol Codes
+typedef enum
+{
+ NCM_DATA_PROTOCOL_NETWORK_TRANSFER_BLOCK = 0x01
+} ncm_data_interface_protocol_code_t;
-#ifndef CFG_TUD_NCM_ALIGNMENT
-#define CFG_TUD_NCM_ALIGNMENT 4
-#endif
#ifdef __cplusplus
extern "C" {
@@ -96,11 +87,6 @@ void tud_network_init_cb(void);
// TODO removed later since it is not part of tinyusb stack
extern uint8_t tud_network_mac_address[6];
-//------------- NCM -------------//
-
-// callback to client providing optional indication of internal state of network driver
-void tud_network_link_state_cb(bool state);
-
//--------------------------------------------------------------------+
// INTERNAL USBD-CLASS DRIVER API
//--------------------------------------------------------------------+
diff --git a/src/class/usbtmc/usbtmc.h b/src/class/usbtmc/usbtmc.h
index 090ab3c4a..327de087c 100644
--- a/src/class/usbtmc/usbtmc.h
+++ b/src/class/usbtmc/usbtmc.h
@@ -184,6 +184,23 @@ typedef enum {
} usmtmc_request_type_enum;
typedef enum {
+ // The last and first valid bNotify1 for use by the USBTMC class specification.
+ USBTMC_bNOTIFY1_USBTMC_FIRST = 0x00,
+ USBTMC_bNOTIFY1_USBTMC_LAST = 0x3F,
+
+ // The last and first valid bNotify1 for use by vendors.
+ USBTMC_bNOTIFY1_VENDOR_SPECIFIC_FIRST = 0x40,
+ USBTMC_bNOTIFY1_VENDOR_SPECIFIC_LAST = 0x7F,
+
+ // The last and first valid bNotify1 for use by USBTMC subclass specifications.
+ USBTMC_bNOTIFY1_SUBCLASS_FIRST = 0x80,
+ USBTMC_bNOTIFY1_SUBCLASS_LAST = 0xFF,
+
+ // From the USB488 Subclass Specification, Section 3.4.
+ USB488_bNOTIFY1_SRQ = 0x81,
+} usbtmc_int_in_payload_format;
+
+typedef enum {
USBTMC_STATUS_SUCCESS = 0x01,
USBTMC_STATUS_PENDING = 0x02,
USBTMC_STATUS_FAILED = 0x80,
@@ -305,6 +322,14 @@ TU_VERIFY_STATIC(sizeof(usbtmc_read_stb_rsp_488_t) == 3u, "struct wrong length")
typedef struct TU_ATTR_PACKED
{
+ uint8_t bNotify1; // Must be USB488_bNOTIFY1_SRQ
+ uint8_t StatusByte;
+} usbtmc_srq_interrupt_488_t;
+
+TU_VERIFY_STATIC(sizeof(usbtmc_srq_interrupt_488_t) == 2u, "struct wrong length");
+
+typedef struct TU_ATTR_PACKED
+{
struct TU_ATTR_PACKED
{
unsigned int bTag : 7;
diff --git a/src/class/usbtmc/usbtmc_device.c b/src/class/usbtmc/usbtmc_device.c
index 286cbe108..129ff465d 100644
--- a/src/class/usbtmc/usbtmc_device.c
+++ b/src/class/usbtmc/usbtmc_device.c
@@ -86,6 +86,11 @@ tu_static char logMsg[150];
// imposes a minimum buffer size of 32 bytes.
#define USBTMCD_BUFFER_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+// Interrupt endpoint buffer size, default to 2 bytes as USB488 specification.
+#ifndef CFG_TUD_USBTMC_INT_EP_SIZE
+#define CFG_TUD_USBTMC_INT_EP_SIZE 2
+#endif
+
/*
* The state machine does not allow simultaneous reading and writing. This is
* consistent with USBTMC.
@@ -124,13 +129,15 @@ typedef struct
uint8_t ep_bulk_in;
uint8_t ep_bulk_out;
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];
- uint32_t ep_bulk_in_wMaxPacketSize;
// OUT buffer receives one packet at a time
CFG_TUSB_MEM_ALIGN uint8_t ep_bulk_out_buf[USBTMCD_BUFFER_SIZE];
- uint32_t ep_bulk_out_wMaxPacketSize;
+ // 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)
@@ -240,6 +247,19 @@ bool tud_usbtmc_transmit_dev_msg_data(
return true;
}
+bool tud_usbtmc_transmit_notification_data(const void * data, size_t len)
+{
+#ifndef NDEBUG
+ TU_ASSERT(len > 0);
+ TU_ASSERT(usbtmc_state.ep_int_in != 0);
+#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));
+ return true;
+}
+
void usbtmcd_init_cb(void)
{
usbtmc_state.capabilities = tud_usbtmc_get_capabilities_cb();
@@ -360,7 +380,7 @@ uint16_t usbtmcd_open_cb(uint8_t rhport, tusb_desc_interface_t const * itf_desc,
// processing a command (such as a clear). Returns true if it was
// in the NAK state and successfully transitioned to the ACK wait
// state.
-bool tud_usbtmc_start_bus_read()
+bool tud_usbtmc_start_bus_read(void)
{
usbtmcd_state_enum oldState = usbtmc_state.state;
switch(oldState)
@@ -547,9 +567,10 @@ bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
case STATE_TX_INITIATED:
if(usbtmc_state.transfer_size_remaining >= sizeof(usbtmc_state.ep_bulk_in_buf))
{
- // FIXME! This removes const below!
+ // 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,
- (void*)(uintptr_t) usbtmc_state.devInBuffer, sizeof(usbtmc_state.ep_bulk_in_buf)));
+ 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);
@@ -585,7 +606,9 @@ bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
}
}
else if (ep_addr == usbtmc_state.ep_int_in) {
- // Good?
+ if (tud_usbtmc_notification_complete_cb) {
+ TU_VERIFY(tud_usbtmc_notification_complete_cb());
+ }
return true;
}
return false;
diff --git a/src/class/usbtmc/usbtmc_device.h b/src/class/usbtmc/usbtmc_device.h
index 3299a36eb..b85ef12b5 100644
--- a/src/class/usbtmc/usbtmc_device.h
+++ b/src/class/usbtmc/usbtmc_device.h
@@ -73,6 +73,10 @@ bool tud_usbtmc_check_abort_bulk_in_cb(usbtmc_check_abort_bulk_rsp_t *rsp);
bool tud_usbtmc_check_abort_bulk_out_cb(usbtmc_check_abort_bulk_rsp_t *rsp);
bool tud_usbtmc_check_clear_cb(usbtmc_get_clear_status_rsp_t *rsp);
+// The interrupt-IN endpoint buffer was transmitted to the host. Use
+// tud_usbtmc_transmit_notification_data to send another notification.
+TU_ATTR_WEAK bool tud_usbtmc_notification_complete_cb(void);
+
// Indicator pulse should be 0.5 to 1.0 seconds long
TU_ATTR_WEAK bool tud_usbtmc_indicator_pulse_cb(tusb_control_request_t const * msg, uint8_t *tmcResult);
@@ -82,17 +86,23 @@ TU_ATTR_WEAK bool tud_usbtmc_msg_trigger_cb(usbtmc_msg_generic_t* msg);
//TU_ATTR_WEAK bool tud_usbtmc_app_go_to_local_cb();
#endif
-/*******************************************
- * Called from app
- *
- * We keep a reference to the buffer, so it MUST not change until the app is
- * notified that the transfer is complete.
- ******************************************/
-
+// Called from app
+//
+// We keep a reference to the buffer, so it MUST not change until the app is
+// notified that the transfer is complete.
bool tud_usbtmc_transmit_dev_msg_data(
const void * data, size_t len,
bool endOfMessage, bool usingTermChar);
+// Buffers a notification to be sent to the host. The data starts
+// with the bNotify1 field, see the USBTMC Specification, Table 13.
+//
+// If the previous notification data has not yet been sent, this
+// returns false.
+//
+// Requires an interrupt endpoint in the interface.
+bool tud_usbtmc_transmit_notification_data(const void * data, size_t len);
+
bool tud_usbtmc_start_bus_read(void);
@@ -105,9 +115,4 @@ void usbtmcd_reset_cb(uint8_t rhport);
bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
bool usbtmcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
-/************************************************************
- * USBTMC Descriptor Templates
- *************************************************************/
-
-
#endif /* CLASS_USBTMC_USBTMC_DEVICE_H_ */
diff --git a/src/class/vendor/vendor_device.c b/src/class/vendor/vendor_device.c
index dcd629965..06c663f1b 100644
--- a/src/class/vendor/vendor_device.c
+++ b/src/class/vendor/vendor_device.c
@@ -36,6 +36,8 @@
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
+#define BULK_PACKET_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+
typedef struct
{
uint8_t itf_num;
@@ -331,7 +333,6 @@ 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) rhport;
(void) result;
uint8_t itf = 0;
@@ -364,7 +365,18 @@ bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
if (tud_vendor_tx_cb) tud_vendor_tx_cb(itf, (uint16_t) xferred_bytes);
#if CFG_TUD_VENDOR_TX_BUFSIZE > 0
// Send complete, try to send more if possible
- tud_vendor_n_write_flush(itf);
+ if ( 0 == tud_vendor_n_write_flush(itf) )
+ {
+ // If there is no data left, a ZLP should be sent if
+ // xferred_bytes is multiple of EP Packet size and not zero
+ if ( !tu_fifo_count(&p_itf->tx_ff) && xferred_bytes && (0 == (xferred_bytes & (BULK_PACKET_SIZE-1))) )
+ {
+ if ( usbd_edpt_claim(rhport, p_itf->ep_in) )
+ {
+ usbd_edpt_xfer(rhport, p_itf->ep_in, NULL, 0);
+ }
+ }
+ }
#endif
}
diff --git a/src/class/video/video_device.c b/src/class/video/video_device.c
index d0a88eb17..4218835aa 100644
--- a/src/class/video/video_device.c
+++ b/src/class/video/video_device.c
@@ -763,8 +763,8 @@ static bool _open_vs_itf(uint8_t rhport, videod_streaming_interface_t *stm, uint
tusb_desc_endpoint_t const *ep = (tusb_desc_endpoint_t const*)(desc + ofs_ep);
/* Only ISO endpoints needs to be closed */
if(ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) {
- usbd_edpt_close(rhport, ep->bEndpointAddress);
stm->desc.ep[i] = 0;
+ usbd_edpt_close(rhport, ep->bEndpointAddress);
TU_LOG_DRV(" close EP%02x\r\n", ep->bEndpointAddress);
}
}
@@ -1186,6 +1186,16 @@ bool tud_video_n_streaming(uint_fast8_t ctl_idx, uint_fast8_t stm_idx)
videod_streaming_interface_t *stm = _get_instance_streaming(ctl_idx, stm_idx);
if (!stm || !stm->desc.ep[0]) return false;
if (stm->state == VS_STATE_PROBING) return false;
+
+#ifdef TUP_DCD_EDPT_ISO_ALLOC
+ uint8_t const *desc = _videod_itf[stm->index_vc].beg;
+ uint_fast16_t ofs_ep = stm->desc.ep[0];
+ tusb_desc_endpoint_t const *ep = (tusb_desc_endpoint_t const*)(desc + ofs_ep);
+ if (ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) {
+ if (stm->state == VS_STATE_COMMITTED) return false;
+ }
+#endif
+
return true;
}
diff --git a/src/common/tusb_common.h b/src/common/tusb_common.h
index 1f08ce4ed..0d4082c03 100644
--- a/src/common/tusb_common.h
+++ b/src/common/tusb_common.h
@@ -65,6 +65,7 @@
// Standard Headers
#include <stdbool.h>
#include <stdint.h>
+#include <inttypes.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
diff --git a/src/common/tusb_compiler.h b/src/common/tusb_compiler.h
index 6f07bdd53..ce5566ffe 100644
--- a/src/common/tusb_compiler.h
+++ b/src/common/tusb_compiler.h
@@ -56,7 +56,7 @@
#elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
#define TU_VERIFY_STATIC _Static_assert
#elif defined(__CCRX__)
- #define TU_VERIFY_STATIC(const_expr, _mess) typedef char TU_XSTRCAT(Line, __LINE__)[(const_expr) ? 1 : 0];
+ #define TU_VERIFY_STATIC(const_expr, _mess) typedef char TU_XSTRCAT(_verify_static_, _TU_COUNTER_)[(const_expr) ? 1 : 0];
#else
#define TU_VERIFY_STATIC(const_expr, _mess) enum { TU_XSTRCAT(_verify_static_, _TU_COUNTER_) = 1/(!!(const_expr)) }
#endif
@@ -128,6 +128,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
diff --git a/src/common/tusb_fifo.c b/src/common/tusb_fifo.c
index 76696396b..8a0fd4417 100644
--- a/src/common/tusb_fifo.c
+++ b/src/common/tusb_fifo.c
@@ -62,7 +62,9 @@ TU_ATTR_ALWAYS_INLINE static inline void _ff_unlock(osal_mutex_t mutex)
typedef enum
{
TU_FIFO_COPY_INC, ///< Copy from/to an increasing source/destination address - default mode
+#ifdef TUP_MEM_CONST_ADDR
TU_FIFO_COPY_CST_FULL_WORDS, ///< Copy from/to a constant source/destination address - required for e.g. STM32 to write into USB hardware FIFO
+#endif
} tu_fifo_copy_mode_t;
bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable)
@@ -92,6 +94,7 @@ bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_si
// Pull & Push
//--------------------------------------------------------------------+
+#ifdef TUP_MEM_CONST_ADDR
// Intended to be used to read from hardware USB FIFO in e.g. STM32 where all data is read from a constant address
// Code adapted from dcd_synopsys.c
// TODO generalize with configurable 1 byte or 4 byte each read
@@ -140,6 +143,7 @@ static void _ff_pull_const_addr(void * app_buf, const uint8_t * ff_buf, uint16_t
*reg_tx = tmp32;
}
}
+#endif
// send one item to fifo WITHOUT updating write pointer
static inline void _ff_push(tu_fifo_t* f, void const * app_buf, uint16_t rel)
@@ -179,7 +183,7 @@ static void _ff_push_n(tu_fifo_t* f, void const * app_buf, uint16_t n, uint16_t
memcpy(f->buffer, ((uint8_t const*) app_buf) + lin_bytes, wrap_bytes);
}
break;
-
+#ifdef TUP_MEM_CONST_ADDR
case TU_FIFO_COPY_CST_FULL_WORDS:
// Intended for hardware buffers from which it can be read word by word only
if(n <= lin_count)
@@ -224,6 +228,7 @@ static void _ff_push_n(tu_fifo_t* f, void const * app_buf, uint16_t n, uint16_t
if (wrap_bytes > 0) _ff_push_const_addr(ff_buf, app_buf, wrap_bytes);
}
break;
+#endif
default: break;
}
}
@@ -265,7 +270,7 @@ static void _ff_pull_n(tu_fifo_t* f, void* app_buf, uint16_t n, uint16_t rd_ptr,
memcpy((uint8_t*) app_buf + lin_bytes, f->buffer, wrap_bytes);
}
break;
-
+#ifdef TUP_MEM_CONST_ADDR
case TU_FIFO_COPY_CST_FULL_WORDS:
if ( n <= lin_count )
{
@@ -310,6 +315,7 @@ static void _ff_pull_n(tu_fifo_t* f, void* app_buf, uint16_t n, uint16_t rd_ptr,
// Read data wrapped part
if (wrap_bytes > 0) _ff_pull_const_addr(app_buf, ff_buf, wrap_bytes);
}
+#endif
break;
default: break;
@@ -727,10 +733,29 @@ uint16_t tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t n)
return _tu_fifo_read_n(f, buffer, n, TU_FIFO_COPY_INC);
}
+#ifdef TUP_MEM_CONST_ADDR
+/******************************************************************************/
+/*!
+ @brief This function will read n elements from the array index specified by
+ the read pointer and increment the read index.
+ This function checks for an overflow and corrects read pointer if required.
+ The dest address will not be incremented which is useful for writing to registers.
+
+ @param[in] f
+ Pointer to the FIFO buffer to manipulate
+ @param[in] buffer
+ The pointer to data location
+ @param[in] n
+ Number of element that buffer can afford
+
+ @returns number of items read from the FIFO
+ */
+/******************************************************************************/
uint16_t tu_fifo_read_n_const_addr_full_words(tu_fifo_t* f, void * buffer, uint16_t n)
{
return _tu_fifo_read_n(f, buffer, n, TU_FIFO_COPY_CST_FULL_WORDS);
}
+#endif
/******************************************************************************/
/*!
@@ -839,6 +864,7 @@ uint16_t tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t n)
return _tu_fifo_write_n(f, data, n, TU_FIFO_COPY_INC);
}
+#ifdef TUP_MEM_CONST_ADDR
/******************************************************************************/
/*!
@brief This function will write n elements into the array index specified by
@@ -858,6 +884,7 @@ uint16_t tu_fifo_write_n_const_addr_full_words(tu_fifo_t* f, const void * data,
{
return _tu_fifo_write_n(f, data, n, TU_FIFO_COPY_CST_FULL_WORDS);
}
+#endif
/******************************************************************************/
/*!
diff --git a/src/common/tusb_fifo.h b/src/common/tusb_fifo.h
index 2d9f5e667..6c0efb509 100644
--- a/src/common/tusb_fifo.h
+++ b/src/common/tusb_fifo.h
@@ -145,22 +145,26 @@ bool tu_fifo_clear(tu_fifo_t *f);
bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable);
#if OSAL_MUTEX_REQUIRED
- TU_ATTR_ALWAYS_INLINE static inline
- void tu_fifo_config_mutex(tu_fifo_t *f, osal_mutex_t wr_mutex, osal_mutex_t rd_mutex) {
- f->mutex_wr = wr_mutex;
- f->mutex_rd = rd_mutex;
- }
+TU_ATTR_ALWAYS_INLINE static inline
+void tu_fifo_config_mutex(tu_fifo_t *f, osal_mutex_t wr_mutex, osal_mutex_t rd_mutex) {
+ f->mutex_wr = wr_mutex;
+ f->mutex_rd = rd_mutex;
+}
#else
- #define tu_fifo_config_mutex(_f, _wr_mutex, _rd_mutex)
+#define tu_fifo_config_mutex(_f, _wr_mutex, _rd_mutex)
#endif
bool tu_fifo_write (tu_fifo_t* f, void const * p_data);
uint16_t tu_fifo_write_n (tu_fifo_t* f, void const * p_data, uint16_t n);
+#ifdef TUP_MEM_CONST_ADDR
uint16_t tu_fifo_write_n_const_addr_full_words (tu_fifo_t* f, const void * data, uint16_t n);
+#endif
bool tu_fifo_read (tu_fifo_t* f, void * p_buffer);
uint16_t tu_fifo_read_n (tu_fifo_t* f, void * p_buffer, uint16_t n);
+#ifdef TUP_MEM_CONST_ADDR
uint16_t tu_fifo_read_n_const_addr_full_words (tu_fifo_t* f, void * buffer, uint16_t n);
+#endif
bool tu_fifo_peek (tu_fifo_t* f, void * p_buffer);
uint16_t tu_fifo_peek_n (tu_fifo_t* f, void * p_buffer, uint16_t n);
diff --git a/src/common/tusb_mcu.h b/src/common/tusb_mcu.h
index 0ae2573cd..637281bfc 100644
--- a/src/common/tusb_mcu.h
+++ b/src/common/tusb_mcu.h
@@ -269,16 +269,23 @@
#define TUP_DCD_ENDPOINT_MAX 8
#elif TU_CHECK_MCU(OPT_MCU_STM32U5)
- #define TUP_USBIP_DWC2
- #define TUP_USBIP_DWC2_STM32
+ #if defined (STM32U535xx) || defined (STM32U545xx)
+ #define TUP_USBIP_FSDEV
+ #define TUP_USBIP_FSDEV_STM32
+ #define TUP_DCD_ENDPOINT_MAX 8
- // U59x/5Ax/5Fx/5Gx are highspeed with built-in HS PHY
- #if defined(STM32U595xx) || defined(STM32U599xx) || defined(STM32U5A5xx) || defined(STM32U5A9xx) || \
- defined(STM32U5F7xx) || defined(STM32U5F9xx) || defined(STM32U5G7xx) || defined(STM32U5G9xx)
- #define TUP_DCD_ENDPOINT_MAX 9
- #define TUP_RHPORT_HIGHSPEED 1
#else
- #define TUP_DCD_ENDPOINT_MAX 6
+ #define TUP_USBIP_DWC2
+ #define TUP_USBIP_DWC2_STM32
+
+ // U59x/5Ax/5Fx/5Gx are highspeed with built-in HS PHY
+ #if defined(STM32U595xx) || defined(STM32U599xx) || defined(STM32U5A5xx) || defined(STM32U5A9xx) || \
+ defined(STM32U5F7xx) || defined(STM32U5F9xx) || defined(STM32U5G7xx) || defined(STM32U5G9xx)
+ #define TUP_DCD_ENDPOINT_MAX 9
+ #define TUP_RHPORT_HIGHSPEED 1
+ #else
+ #define TUP_DCD_ENDPOINT_MAX 6
+ #endif
#endif
#elif TU_CHECK_MCU(OPT_MCU_STM32L5)
@@ -329,6 +336,9 @@
#define TUP_USBIP_DWC2
#define TUP_DCD_ENDPOINT_MAX 6
+#elif TU_CHECK_MCU(OPT_MCU_ESP32, OPT_MCU_ESP32C2, OPT_MCU_ESP32C3, OPT_MCU_ESP32C6, OPT_MCU_ESP32H2) && (CFG_TUD_ENABLED || !(defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421))
+ #error "MCUs are only supported with CFG_TUH_MAX3421 enabled"
+
//--------------------------------------------------------------------+
// Dialog
//--------------------------------------------------------------------+
@@ -396,16 +406,66 @@
#elif TU_CHECK_MCU(OPT_MCU_F1C100S)
#define TUP_DCD_ENDPOINT_MAX 4
-//------------- WCH -------------//
+//--------------------------------------------------------------------+
+// WCH
+//--------------------------------------------------------------------+
+#elif TU_CHECK_MCU(OPT_MCU_CH32F20X)
+ #define TUP_USBIP_WCH_USBHS
+ #define TUP_USBIP_WCH_USBFS
+
+ #if !defined(CFG_TUD_WCH_USBIP_USBFS)
+ #define CFG_TUD_WCH_USBIP_USBFS 0
+ #endif
+
+ #if !defined(CFG_TUD_WCH_USBIP_USBHS)
+ #define CFG_TUD_WCH_USBIP_USBHS (CFG_TUD_WCH_USBIP_USBFS ? 0 : 1)
+ #endif
+
+ #define TUP_RHPORT_HIGHSPEED CFG_TUD_WCH_USBIP_USBHS
+ #define TUP_DCD_ENDPOINT_MAX (CFG_TUD_WCH_USBIP_USBHS ? 16 : 8)
+
+#elif TU_CHECK_MCU(OPT_MCU_CH32V103)
+ #define TUP_USBIP_WCH_USBFS
+
+ #if !defined(CFG_TUD_WCH_USBIP_USBFS)
+ #define CFG_TUD_WCH_USBIP_USBFS 1
+ #endif
+
+ #define TUP_DCD_ENDPOINT_MAX 8
+
+#elif TU_CHECK_MCU(OPT_MCU_CH32V20X)
+ // v20x support both FSDEV (USBD) and USBFS, default to FSDEV
+ #define TUP_USBIP_WCH_USBFS
+ #define TUP_USBIP_FSDEV
+ #define TUP_USBIP_FSDEV_CH32
+
+ #if !defined(CFG_TUD_WCH_USBIP_USBFS)
+ #define CFG_TUD_WCH_USBIP_USBFS 0
+ #endif
+
+ #if !defined(CFG_TUD_WCH_USBIP_FSDEV)
+ #define CFG_TUD_WCH_USBIP_FSDEV (CFG_TUD_WCH_USBIP_USBFS ? 0 : 1)
+ #endif
+
+ #define TUP_DCD_ENDPOINT_MAX 8
+
#elif TU_CHECK_MCU(OPT_MCU_CH32V307)
- #define TUP_DCD_ENDPOINT_MAX 16
- #define TUP_RHPORT_HIGHSPEED 1
+ // v307 support both FS and HS, default to HS
+ #define TUP_USBIP_WCH_USBHS
+ #define TUP_USBIP_WCH_USBFS
-#elif TU_CHECK_MCU(OPT_MCU_CH32F20X)
- #define TUP_DCD_ENDPOINT_MAX 16
- #define TUP_RHPORT_HIGHSPEED 1
-#endif
+ #if !defined(CFG_TUD_WCH_USBIP_USBFS)
+ #define CFG_TUD_WCH_USBIP_USBFS 0
+ #endif
+ #if !defined(CFG_TUD_WCH_USBIP_USBHS)
+ #define CFG_TUD_WCH_USBIP_USBHS (CFG_TUD_WCH_USBIP_USBFS ? 0 : 1)
+ #endif
+
+ #define TUP_RHPORT_HIGHSPEED CFG_TUD_WCH_USBIP_USBHS
+ #define TUP_DCD_ENDPOINT_MAX (CFG_TUD_WCH_USBIP_USBHS ? 16 : 8)
+
+#endif
//--------------------------------------------------------------------+
// External USB controller
@@ -426,7 +486,7 @@
#define TUP_MCU_MULTIPLE_CORE 0
#endif
-#ifndef TUP_DCD_ENDPOINT_MAX
+#if !defined(TUP_DCD_ENDPOINT_MAX) && defined(CFG_TUD_ENABLED) && CFG_TUD_ENABLED
#warning "TUP_DCD_ENDPOINT_MAX is not defined for this MCU, default to 8"
#define TUP_DCD_ENDPOINT_MAX 8
#endif
@@ -445,4 +505,8 @@
#define TUP_DCD_EDPT_ISO_ALLOC
#endif
+#if defined(TUP_USBIP_DWC2)
+ #define TUP_MEM_CONST_ADDR
+#endif
+
#endif
diff --git a/src/common/tusb_types.h b/src/common/tusb_types.h
index 2c5dce723..1501a5af6 100644
--- a/src/common/tusb_types.h
+++ b/src/common/tusb_types.h
@@ -24,12 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-/** \ingroup group_usb_definitions
- * \defgroup USBDef_Type USB Types
- * @{ */
-
-#ifndef _TUSB_TYPES_H_
-#define _TUSB_TYPES_H_
+#ifndef TUSB_TYPES_H_
+#define TUSB_TYPES_H_
#include <stdbool.h>
#include <stdint.h>
@@ -212,12 +208,21 @@ typedef enum {
} device_capability_type_t;
enum {
- TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP = TU_BIT(5),
- TUSB_DESC_CONFIG_ATT_SELF_POWERED = TU_BIT(6),
+ TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP = 1u << 5,
+ TUSB_DESC_CONFIG_ATT_SELF_POWERED = 1u << 6,
};
#define TUSB_DESC_CONFIG_POWER_MA(x) ((x)/2)
+// 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_mode_t;
+
//--------------------------------------------------------------------+
//
//--------------------------------------------------------------------+
@@ -274,11 +279,11 @@ TU_ATTR_BIT_FIELD_ORDER_BEGIN
typedef struct TU_ATTR_PACKED {
uint8_t bLength ; ///< Size of this descriptor in bytes.
uint8_t bDescriptorType ; ///< DEVICE Descriptor Type.
- uint16_t bcdUSB ; ///< BUSB Specification Release Number in Binary-Coded Decimal (i.e., 2.10 is 210H). This field identifies the release of the USB Specification with which the device and its descriptors are compliant.
+ uint16_t bcdUSB ; ///< BUSB Specification Release Number in Binary-Coded Decimal (i.e., 2.10 is 210H).
- uint8_t bDeviceClass ; ///< Class code (assigned by the USB-IF). \li If this field is reset to zero, each interface within a configuration specifies its own class information and the various interfaces operate independently. \li If this field is set to a value between 1 and FEH, the device supports different class specifications on different interfaces and the interfaces may not operate independently. This value identifies the class definition used for the aggregate interfaces. \li If this field is set to FFH, the device class is vendor-specific.
- uint8_t bDeviceSubClass ; ///< Subclass code (assigned by the USB-IF). These codes are qualified by the value of the bDeviceClass field. \li If the bDeviceClass field is reset to zero, this field must also be reset to zero. \li If the bDeviceClass field is not set to FFH, all values are reserved for assignment by the USB-IF.
- uint8_t bDeviceProtocol ; ///< Protocol code (assigned by the USB-IF). These codes are qualified by the value of the bDeviceClass and the bDeviceSubClass fields. If a device supports class-specific protocols on a device basis as opposed to an interface basis, this code identifies the protocols that the device uses as defined by the specification of the device class. \li If this field is reset to zero, the device does not use class-specific protocols on a device basis. However, it may use classspecific protocols on an interface basis. \li If this field is set to FFH, the device uses a vendor-specific protocol on a device basis.
+ uint8_t bDeviceClass ; ///< Class code (assigned by the USB-IF).
+ uint8_t bDeviceSubClass ; ///< Subclass code (assigned by the USB-IF).
+ uint8_t bDeviceProtocol ; ///< Protocol code (assigned by the USB-IF).
uint8_t bMaxPacketSize0 ; ///< Maximum packet size for endpoint zero (only 8, 16, 32, or 64 are valid). For HS devices is fixed to 64.
uint16_t idVendor ; ///< Vendor ID (assigned by the USB-IF).
@@ -493,15 +498,10 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_edpt_addr(uint8_t num, uint8_t di
}
TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_edpt_packet_size(tusb_desc_endpoint_t const* desc_ep) {
- return tu_le16toh(desc_ep->wMaxPacketSize) & TU_GENMASK(10, 0);
+ return tu_le16toh(desc_ep->wMaxPacketSize) & 0x7FF;
}
#if CFG_TUSB_DEBUG
-TU_ATTR_ALWAYS_INLINE static inline const char *tu_edpt_dir_str(tusb_dir_t dir) {
- tu_static const char *str[] = {"out", "in"};
- return str[dir];
-}
-
TU_ATTR_ALWAYS_INLINE static inline const char *tu_edpt_type_str(tusb_xfer_type_t t) {
tu_static const char *str[] = {"control", "isochronous", "bulk", "interrupt"};
return str[t];
@@ -541,6 +541,4 @@ uint8_t const * tu_desc_find3(uint8_t const* desc, uint8_t const* end, uint8_t b
}
#endif
-#endif /* _TUSB_TYPES_H_ */
-
-/** @} */
+#endif // TUSB_TYPES_H_
diff --git a/src/common/tusb_verify.h b/src/common/tusb_verify.h
index 8aa66b4df..4344575b7 100644
--- a/src/common/tusb_verify.h
+++ b/src/common/tusb_verify.h
@@ -56,8 +56,8 @@
* #define TU_VERIFY(cond) if(cond) return false;
* #define TU_VERIFY(cond,ret) if(cond) return ret;
*
- * #define TU_ASSERT(cond) if(cond) {_MESS_FAILED(); TU_BREAKPOINT(), return false;}
- * #define TU_ASSERT(cond,ret) if(cond) {_MESS_FAILED(); TU_BREAKPOINT(), return ret;}
+ * #define TU_ASSERT(cond) if(cond) {TU_MESS_FAILED(); TU_BREAKPOINT(), return false;}
+ * #define TU_ASSERT(cond,ret) if(cond) {TU_MESS_FAILED(); TU_BREAKPOINT(), return ret;}
*------------------------------------------------------------------*/
#ifdef __cplusplus
@@ -70,20 +70,20 @@
#if CFG_TUSB_DEBUG
#include <stdio.h>
- #define _MESS_FAILED() tu_printf("%s %d: ASSERT FAILED\r\n", __func__, __LINE__)
+ #define TU_MESS_FAILED() tu_printf("%s %d: ASSERT FAILED\r\n", __func__, __LINE__)
#else
- #define _MESS_FAILED() do {} while (0)
+ #define TU_MESS_FAILED() do {} while (0)
#endif
// Halt CPU (breakpoint) when hitting error, only apply for Cortex M3, M4, M7, M33. M55
-#if defined(__ARM_ARCH_7M__) || defined (__ARM_ARCH_7EM__) || defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8_1M_MAIN__)
- #define TU_BREAKPOINT() do \
- { \
+#if defined(__ARM_ARCH_7M__) || defined (__ARM_ARCH_7EM__) || defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8_1M_MAIN__) || \
+ defined(__ARM7M__) || defined (__ARM7EM__) || defined(__ARM8M_MAINLINE__) || defined(__ARM8EM_MAINLINE__)
+ #define TU_BREAKPOINT() do { \
volatile uint32_t* ARM_CM_DHCSR = ((volatile uint32_t*) 0xE000EDF0UL); /* Cortex M CoreDebug->DHCSR */ \
if ( (*ARM_CM_DHCSR) & 1UL ) __asm("BKPT #0\n"); /* Only halt mcu if debugger is attached */ \
} while(0)
-#elif defined(__riscv)
+#elif defined(__riscv) && !TUP_MCU_ESPRESSIF
#define TU_BREAKPOINT() do { __asm("ebreak\n"); } while(0)
#elif defined(_mips)
@@ -119,7 +119,7 @@
*------------------------------------------------------------------*/
#define TU_ASSERT_DEFINE(_cond, _ret) \
do { \
- if ( !(_cond) ) { _MESS_FAILED(); TU_BREAKPOINT(); return _ret; } \
+ if ( !(_cond) ) { TU_MESS_FAILED(); TU_BREAKPOINT(); return _ret; } \
} while(0)
#define TU_ASSERT_1ARGS(_cond) TU_ASSERT_DEFINE(_cond, false)
diff --git a/src/device/dcd.h b/src/device/dcd.h
index d4f105aa3..41e0fbee3 100644
--- a/src/device/dcd.h
+++ b/src/device/dcd.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_DCD_H_
-#define _TUSB_DCD_H_
+#ifndef TUSB_DCD_H_
+#define TUSB_DCD_H_
#include "common/tusb_common.h"
#include "osal/osal.h"
@@ -36,31 +36,19 @@
#endif
//--------------------------------------------------------------------+
-// Configuration
-//--------------------------------------------------------------------+
-
-#ifndef CFG_TUD_ENDPPOINT_MAX
- #define CFG_TUD_ENDPPOINT_MAX TUP_DCD_ENDPOINT_MAX
-#endif
-
-//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTYPES
//--------------------------------------------------------------------+
typedef enum {
- DCD_EVENT_INVALID = 0,
- DCD_EVENT_BUS_RESET,
- DCD_EVENT_UNPLUGGED,
- DCD_EVENT_SOF,
- DCD_EVENT_SUSPEND, // TODO LPM Sleep L1 support
- DCD_EVENT_RESUME,
-
- DCD_EVENT_SETUP_RECEIVED,
- DCD_EVENT_XFER_COMPLETE,
-
- // Not an DCD event, just a convenient way to defer ISR function
- USBD_EVENT_FUNC_CALL,
-
+ DCD_EVENT_INVALID = 0, // 0
+ DCD_EVENT_BUS_RESET, // 1
+ DCD_EVENT_UNPLUGGED, // 2
+ DCD_EVENT_SOF, // 3
+ DCD_EVENT_SUSPEND, // 4 TODO LPM Sleep L1 support
+ DCD_EVENT_RESUME, // 5
+ DCD_EVENT_SETUP_RECEIVED, // 6
+ DCD_EVENT_XFER_COMPLETE, // 7
+ USBD_EVENT_FUNC_CALL, // 8 Not an DCD event, just a convenient way to defer ISR function
DCD_EVENT_COUNT
} dcd_eventid_t;
@@ -141,14 +129,18 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr);
void dcd_remote_wakeup(uint8_t rhport);
// Connect by enabling internal pull-up resistor on D+/D-
-void dcd_connect(uint8_t rhport) TU_ATTR_WEAK;
+void dcd_connect(uint8_t rhport);
// Disconnect by disabling internal pull-up resistor on D+/D-
-void dcd_disconnect(uint8_t rhport) TU_ATTR_WEAK;
+void dcd_disconnect(uint8_t rhport);
// Enable/Disable Start-of-frame interrupt. Default is disabled
void dcd_sof_enable(uint8_t rhport, bool en);
+#if CFG_TUD_TEST_MODE
+// Put device into a test mode (needs power cycle to quit)
+void dcd_enter_test_mode(uint8_t rhport, tusb_feature_test_mode_t test_selector);
+#endif
//--------------------------------------------------------------------+
// Endpoint API
//--------------------------------------------------------------------+
@@ -188,7 +180,7 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr);
TU_ATTR_WEAK bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size);
// Configure and enable an ISO endpoint according to descriptor
-TU_ATTR_WEAK bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc);
+TU_ATTR_WEAK bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep);
//--------------------------------------------------------------------+
// Event API (implemented by stack)
@@ -239,4 +231,4 @@ TU_ATTR_ALWAYS_INLINE static inline void dcd_event_sof(uint8_t rhport, uint32_t
}
#endif
-#endif /* _TUSB_DCD_H_ */
+#endif
diff --git a/src/device/usbd.c b/src/device/usbd.c
index 96982c30b..6d27a3735 100644
--- a/src/device/usbd.c
+++ b/src/device/usbd.c
@@ -45,15 +45,60 @@
//--------------------------------------------------------------------+
// 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;
+}
+
+TU_ATTR_WEAK void tud_sof_cb(uint32_t frame_count) {
+ (void) frame_count;
+}
+
+TU_ATTR_WEAK uint8_t const* tud_descriptor_bos_cb(void) {
+ return NULL;
+}
+
+TU_ATTR_WEAK uint8_t const* tud_descriptor_device_qualifier_cb(void) {
+ return NULL;
+}
+
+TU_ATTR_WEAK uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index) {
+ (void) index;
+ return NULL;
+}
+
+TU_ATTR_WEAK void tud_mount_cb(void) {
+}
+
+TU_ATTR_WEAK void tud_umount_cb(void) {
+}
+
+TU_ATTR_WEAK void tud_suspend_cb(bool remote_wakeup_en) {
+ (void) remote_wakeup_en;
+}
+
+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;
+ return false;
+}
+
TU_ATTR_WEAK bool dcd_deinit(uint8_t rhport) {
(void) rhport;
return false;
}
-TU_ATTR_WEAK void tud_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr) {
- (void)rhport;
- (void)eventid;
- (void)in_isr;
+TU_ATTR_WEAK void dcd_connect(uint8_t rhport) {
+ (void) rhport;
+}
+
+TU_ATTR_WEAK void dcd_disconnect(uint8_t rhport) {
+ (void) rhport;
}
//--------------------------------------------------------------------+
@@ -75,7 +120,7 @@ typedef struct {
};
volatile uint8_t cfg_num; // current active configuration (0x00 is not configured)
uint8_t speed;
- volatile uint8_t setup_count;
+ volatile uint8_t sof_consumer;
uint8_t itf2drv[CFG_TUD_INTERFACE_MAX]; // map interface number to driver (0xff is invalid)
uint8_t ep2drv[CFG_TUD_ENDPPOINT_MAX][2]; // map endpoint to driver ( 0xff is invalid ), can use only 4-bit each
@@ -85,21 +130,22 @@ typedef struct {
}usbd_device_t;
tu_static usbd_device_t _usbd_dev;
+static volatile uint8_t _usbd_queued_setup;
//--------------------------------------------------------------------+
// Class Driver
//--------------------------------------------------------------------+
#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
- #define DRIVER_NAME(_name) .name = _name,
+ #define DRIVER_NAME(_name) _name
#else
- #define DRIVER_NAME(_name)
+ #define DRIVER_NAME(_name) NULL
#endif
// Built-in class drivers
tu_static usbd_class_driver_t const _usbd_driver[] = {
#if CFG_TUD_CDC
{
- DRIVER_NAME("CDC")
+ .name = DRIVER_NAME("CDC"),
.init = cdcd_init,
.deinit = cdcd_deinit,
.reset = cdcd_reset,
@@ -112,7 +158,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
#if CFG_TUD_MSC
{
- DRIVER_NAME("MSC")
+ .name = DRIVER_NAME("MSC"),
.init = mscd_init,
.deinit = NULL,
.reset = mscd_reset,
@@ -125,7 +171,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
#if CFG_TUD_HID
{
- DRIVER_NAME("HID")
+ .name = DRIVER_NAME("HID"),
.init = hidd_init,
.deinit = hidd_deinit,
.reset = hidd_reset,
@@ -138,7 +184,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
#if CFG_TUD_AUDIO
{
- DRIVER_NAME("AUDIO")
+ .name = DRIVER_NAME("AUDIO"),
.init = audiod_init,
.deinit = audiod_deinit,
.reset = audiod_reset,
@@ -151,7 +197,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
#if CFG_TUD_VIDEO
{
- DRIVER_NAME("VIDEO")
+ .name = DRIVER_NAME("VIDEO"),
.init = videod_init,
.deinit = videod_deinit,
.reset = videod_reset,
@@ -164,7 +210,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
#if CFG_TUD_MIDI
{
- DRIVER_NAME("MIDI")
+ .name = DRIVER_NAME("MIDI"),
.init = midid_init,
.deinit = midid_deinit,
.open = midid_open,
@@ -177,7 +223,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
#if CFG_TUD_VENDOR
{
- DRIVER_NAME("VENDOR")
+ .name = DRIVER_NAME("VENDOR"),
.init = vendord_init,
.deinit = vendord_deinit,
.reset = vendord_reset,
@@ -190,7 +236,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
#if CFG_TUD_USBTMC
{
- DRIVER_NAME("TMC")
+ .name = DRIVER_NAME("TMC"),
.init = usbtmcd_init_cb,
.deinit = usbtmcd_deinit,
.reset = usbtmcd_reset_cb,
@@ -203,7 +249,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
#if CFG_TUD_DFU_RUNTIME
{
- DRIVER_NAME("DFU-RUNTIME")
+ .name = DRIVER_NAME("DFU-RUNTIME"),
.init = dfu_rtd_init,
.deinit = dfu_rtd_deinit,
.reset = dfu_rtd_reset,
@@ -216,7 +262,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
#if CFG_TUD_DFU
{
- DRIVER_NAME("DFU")
+ .name = DRIVER_NAME("DFU"),
.init = dfu_moded_init,
.deinit = dfu_moded_deinit,
.reset = dfu_moded_reset,
@@ -229,7 +275,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
#if CFG_TUD_ECM_RNDIS || CFG_TUD_NCM
{
- DRIVER_NAME("NET")
+ .name = DRIVER_NAME("NET"),
.init = netd_init,
.deinit = netd_deinit,
.reset = netd_reset,
@@ -242,7 +288,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
#if CFG_TUD_BTH
{
- DRIVER_NAME("BTH")
+ .name = DRIVER_NAME("BTH"),
.init = btd_init,
.deinit = btd_deinit,
.reset = btd_reset,
@@ -275,6 +321,7 @@ TU_ATTR_ALWAYS_INLINE static inline usbd_class_driver_t const * get_driver(uint8
return driver;
}
+
//--------------------------------------------------------------------+
// DCD Event
//--------------------------------------------------------------------+
@@ -296,9 +343,9 @@ tu_static osal_queue_t _usbd_q;
#endif
TU_ATTR_ALWAYS_INLINE static inline bool queue_event(dcd_event_t const * event, bool in_isr) {
- bool ret = osal_queue_send(_usbd_q, event, in_isr);
+ TU_ASSERT(osal_queue_send(_usbd_q, event, in_isr));
tud_event_hook_cb(event->rhport, event->event_id, in_isr);
- return ret;
+ return true;
}
//--------------------------------------------------------------------+
@@ -308,6 +355,16 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
static bool process_set_config(uint8_t rhport, uint8_t cfg_num);
static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const * p_request);
+#if CFG_TUD_TEST_MODE
+static bool process_test_mode_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) {
+ TU_VERIFY(CONTROL_STAGE_ACK == stage);
+ uint8_t const selector = tu_u16_high(request->wIndex);
+ TU_LOG_USBD(" Enter Test Mode (test selector index: %d)\r\n", selector);
+ dcd_enter_test_mode(rhport, (tusb_feature_test_mode_t) selector);
+ return true;
+}
+#endif
+
// from usbd_control.c
void usbd_control_reset(void);
void usbd_control_set_request(tusb_control_request_t const *request);
@@ -371,17 +428,19 @@ bool tud_remote_wakeup(void) {
}
bool tud_disconnect(void) {
- TU_VERIFY(dcd_disconnect);
dcd_disconnect(_usbd_rhport);
return true;
}
bool tud_connect(void) {
- TU_VERIFY(dcd_connect);
dcd_connect(_usbd_rhport);
return true;
}
+void tud_sof_cb_enable(bool en) {
+ usbd_sof_enable(_usbd_rhport, SOF_CONSUMER_USER, en);
+}
+
//--------------------------------------------------------------------+
// USBD Task
//--------------------------------------------------------------------+
@@ -393,13 +452,14 @@ bool tud_init(uint8_t rhport) {
// skip if already initialized
if (tud_inited()) return true;
- TU_LOG_USBD("USBD init on controller %u\r\n", rhport);
+ TU_LOG_USBD("USBD init on controller %u, Highspeed = %u\r\n", rhport, TUD_OPT_HIGH_SPEED);
TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(usbd_device_t));
TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(dcd_event_t));
TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(tu_fifo_t));
TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(tu_edpt_stream_t));
tu_varclr(&_usbd_dev);
+ _usbd_queued_setup = 0;
#if OSAL_MUTEX_REQUIRED
// Init device mutex
@@ -531,13 +591,14 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr) {
case DCD_EVENT_UNPLUGGED:
TU_LOG_USBD("\r\n");
usbd_reset(event.rhport);
- if (tud_umount_cb) tud_umount_cb();
+ tud_umount_cb();
break;
case DCD_EVENT_SETUP_RECEIVED:
- _usbd_dev.setup_count--;
+ TU_ASSERT(_usbd_queued_setup > 0,);
+ _usbd_queued_setup--;
TU_LOG_BUF(CFG_TUD_LOG_LEVEL, &event.setup_received, 8);
- if (_usbd_dev.setup_count) {
+ if (_usbd_queued_setup) {
TU_LOG_USBD(" Skipped since there is other SETUP in queue\r\n");
break;
}
@@ -591,7 +652,7 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr) {
// e.g suspend -> resume -> unplug/plug. Skip suspend/resume if not connected
if (_usbd_dev.connected) {
TU_LOG_USBD(": Remote Wakeup = %u\r\n", _usbd_dev.remote_wakeup_en);
- if (tud_suspend_cb) tud_suspend_cb(_usbd_dev.remote_wakeup_en);
+ tud_suspend_cb(_usbd_dev.remote_wakeup_en);
} else {
TU_LOG_USBD(" Skipped\r\n");
}
@@ -600,7 +661,7 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr) {
case DCD_EVENT_RESUME:
if (_usbd_dev.connected) {
TU_LOG_USBD("\r\n");
- if (tud_resume_cb) tud_resume_cb();
+ tud_resume_cb();
} else {
TU_LOG_USBD(" Skipped\r\n");
}
@@ -612,6 +673,12 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr) {
break;
case DCD_EVENT_SOF:
+ if (tu_bit_test(_usbd_dev.sof_consumer, SOF_CONSUMER_USER)) {
+ TU_LOG_USBD("\r\n");
+ tud_sof_cb(event.sof.frame_count);
+ }
+ break;
+
default:
TU_BREAKPOINT();
break;
@@ -636,15 +703,13 @@ static bool invoke_class_control(uint8_t rhport, usbd_class_driver_t const * dri
}
// This handles the actual request and its response.
-// return false will cause its caller to stall control endpoint
+// Returns false if unable to complete the request, causing caller to stall control endpoints.
static bool process_control_request(uint8_t rhport, tusb_control_request_t const * p_request) {
usbd_control_set_complete_callback(NULL);
TU_ASSERT(p_request->bmRequestType_bit.type < TUSB_REQ_TYPE_INVALID);
// Vendor request
if ( p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_VENDOR ) {
- TU_VERIFY(tud_vendor_control_xfer_cb);
-
usbd_control_set_complete_callback(tud_vendor_control_xfer_cb);
return tud_vendor_control_xfer_cb(rhport, CONTROL_STAGE_SETUP, p_request);
}
@@ -671,7 +736,7 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
}
if ( TUSB_REQ_TYPE_STANDARD != p_request->bmRequestType_bit.type ) {
- // Non standard request is not supported
+ // Non-standard request is not supported
TU_BREAKPOINT();
return false;
}
@@ -702,6 +767,9 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
// already configured: need to clear all endpoints and driver first
TU_LOG_USBD(" Clear current Configuration (%u) before switching\r\n", _usbd_dev.cfg_num);
+ // disable SOF
+ dcd_sof_enable(rhport, false);
+
// close all non-control endpoints, cancel all pending transfers if any
dcd_edpt_close_all(rhport);
@@ -712,17 +780,23 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
_usbd_dev.speed = speed; // restore speed
}
+ _usbd_dev.cfg_num = cfg_num;
+
// Handle the new configuration and execute the corresponding callback
if ( cfg_num ) {
// switch to new configuration if not zero
- TU_ASSERT( process_set_config(rhport, cfg_num) );
- if ( tud_mount_cb ) tud_mount_cb();
+ if (!process_set_config(rhport, cfg_num)) {
+ TU_MESS_FAILED();
+ TU_BREAKPOINT();
+ _usbd_dev.cfg_num = 0;
+ return false;
+ }
+ tud_mount_cb();
} else {
- if ( tud_umount_cb ) tud_umount_cb();
+ tud_umount_cb();
}
}
- _usbd_dev.cfg_num = cfg_num;
tud_control_status(rhport, p_request);
}
break;
@@ -732,14 +806,31 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
break;
case TUSB_REQ_SET_FEATURE:
- // Only support remote wakeup for device feature
- TU_VERIFY(TUSB_REQ_FEATURE_REMOTE_WAKEUP == p_request->wValue);
+ switch(p_request->wValue) {
+ case TUSB_REQ_FEATURE_REMOTE_WAKEUP:
+ TU_LOG_USBD(" Enable Remote Wakeup\r\n");
+ // Host may enable remote wake up before suspending especially HID device
+ _usbd_dev.remote_wakeup_en = true;
+ tud_control_status(rhport, p_request);
+ break;
- TU_LOG_USBD(" Enable Remote Wakeup\r\n");
+ #if CFG_TUD_TEST_MODE
+ case TUSB_REQ_FEATURE_TEST_MODE: {
+ // Only handle the test mode if supported and valid
+ TU_VERIFY(0 == tu_u16_low(p_request->wIndex));
- // Host may enable remote wake up before suspending especially HID device
- _usbd_dev.remote_wakeup_en = true;
- tud_control_status(rhport, p_request);
+ uint8_t const selector = tu_u16_high(p_request->wIndex);
+ TU_VERIFY(TUSB_FEATURE_TEST_J <= selector && selector <= TUSB_FEATURE_TEST_FORCE_ENABLE);
+
+ usbd_control_set_complete_callback(process_test_mode_cb);
+ tud_control_status(rhport, p_request);
+ break;
+ }
+ #endif /* CFG_TUD_TEST_MODE */
+
+ // Stall unsupported feature selector
+ default: return false;
+ }
break;
case TUSB_REQ_CLEAR_FEATURE:
@@ -969,39 +1060,34 @@ static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const
switch(desc_type)
{
- case TUSB_DESC_DEVICE:
- {
+ case TUSB_DESC_DEVICE: {
TU_LOG_USBD(" Device\r\n");
void* desc_device = (void*) (uintptr_t) tud_descriptor_device_cb();
+ TU_ASSERT(desc_device);
// Only response with exactly 1 Packet if: not addressed and host requested more data than device descriptor has.
// This only happens with the very first get device descriptor and EP0 size = 8 or 16.
if ((CFG_TUD_ENDPOINT0_SIZE < sizeof(tusb_desc_device_t)) && !_usbd_dev.addressed &&
- ((tusb_control_request_t const*) p_request)->wLength > sizeof(tusb_desc_device_t))
- {
+ ((tusb_control_request_t const*) p_request)->wLength > sizeof(tusb_desc_device_t)) {
// Hack here: we modify the request length to prevent usbd_control response with zlp
// since we are responding with 1 packet & less data than wLength.
tusb_control_request_t mod_request = *p_request;
mod_request.wLength = CFG_TUD_ENDPOINT0_SIZE;
return tud_control_xfer(rhport, &mod_request, desc_device, CFG_TUD_ENDPOINT0_SIZE);
- }else
- {
+ }else {
return tud_control_xfer(rhport, p_request, desc_device, sizeof(tusb_desc_device_t));
}
}
// break; // unreachable
- case TUSB_DESC_BOS:
- {
+ case TUSB_DESC_BOS: {
TU_LOG_USBD(" BOS\r\n");
// requested by host if USB > 2.0 ( i.e 2.1 or 3.x )
- if (!tud_descriptor_bos_cb) return false;
-
uintptr_t desc_bos = (uintptr_t) tud_descriptor_bos_cb();
- TU_ASSERT(desc_bos);
+ TU_VERIFY(desc_bos);
// Use offsetof to avoid pointer to the odd/misaligned address
uint16_t const total_len = tu_le16toh( tu_unaligned_read16((const void*) (desc_bos + offsetof(tusb_desc_bos_t, wTotalLength))) );
@@ -1011,24 +1097,20 @@ static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const
// break; // unreachable
case TUSB_DESC_CONFIGURATION:
- case TUSB_DESC_OTHER_SPEED_CONFIG:
- {
+ case TUSB_DESC_OTHER_SPEED_CONFIG: {
uintptr_t desc_config;
- if ( desc_type == TUSB_DESC_CONFIGURATION )
- {
+ if ( desc_type == TUSB_DESC_CONFIGURATION ) {
TU_LOG_USBD(" Configuration[%u]\r\n", desc_index);
desc_config = (uintptr_t) tud_descriptor_configuration_cb(desc_index);
- }else
- {
+ TU_ASSERT(desc_config);
+ }else {
// Host only request this after getting Device Qualifier descriptor
TU_LOG_USBD(" Other Speed Configuration\r\n");
- TU_VERIFY( tud_descriptor_other_speed_configuration_cb );
desc_config = (uintptr_t) tud_descriptor_other_speed_configuration_cb(desc_index);
+ TU_VERIFY(desc_config);
}
- TU_ASSERT(desc_config);
-
// Use offsetof to avoid pointer to the odd/misaligned address
uint16_t const total_len = tu_le16toh( tu_unaligned_read16((const void*) (desc_config + offsetof(tusb_desc_configuration_t, wTotalLength))) );
@@ -1049,16 +1131,10 @@ static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const
}
// break; // unreachable
- case TUSB_DESC_DEVICE_QUALIFIER:
- {
+ case TUSB_DESC_DEVICE_QUALIFIER: {
TU_LOG_USBD(" Device Qualifier\r\n");
-
- TU_VERIFY( tud_descriptor_device_qualifier_cb );
-
uint8_t const* desc_qualifier = tud_descriptor_device_qualifier_cb();
TU_VERIFY(desc_qualifier);
-
- // first byte of descriptor is its size
return tud_control_xfer(rhport, p_request, (void*) (uintptr_t) desc_qualifier, tu_desc_len(desc_qualifier));
}
// break; // unreachable
@@ -1101,6 +1177,14 @@ TU_ATTR_FAST_FUNC void dcd_event_handler(dcd_event_t const* event, bool in_isr)
break;
case DCD_EVENT_SOF:
+ // SOF driver handler in ISR context
+ for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++) {
+ usbd_class_driver_t const* driver = get_driver(i);
+ if (driver && driver->sof) {
+ driver->sof(event->rhport, event->sof.frame_count);
+ }
+ }
+
// Some MCUs after running dcd_remote_wakeup() does not have way to detect the end of remote wakeup
// which last 1-15 ms. DCD can use SOF as a clear indicator that bus is back to operational
if (_usbd_dev.suspended) {
@@ -1110,19 +1194,15 @@ TU_ATTR_FAST_FUNC void dcd_event_handler(dcd_event_t const* event, bool in_isr)
queue_event(&event_resume, in_isr);
}
- // SOF driver handler in ISR context
- for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++) {
- usbd_class_driver_t const* driver = get_driver(i);
- if (driver && driver->sof) {
- driver->sof(event->rhport, event->sof.frame_count);
- }
+ if (tu_bit_test(_usbd_dev.sof_consumer, SOF_CONSUMER_USER)) {
+ dcd_event_t const event_sof = {.rhport = event->rhport, .event_id = DCD_EVENT_SOF, .sof.frame_count = event->sof.frame_count};
+ queue_event(&event_sof, in_isr);
}
-
- // skip osal queue for SOF in usbd task
break;
case DCD_EVENT_SETUP_RECEIVED:
- _usbd_dev.setup_count++;
+ // TU_ASSERT(event->setup_received.bRequest != 0,); // for catching issue with ch32v203 and windows with -O0/-Og
+ _usbd_queued_setup++;
send = true;
break;
@@ -1306,12 +1386,10 @@ void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
uint8_t const dir = tu_edpt_dir(ep_addr);
// only stalled if currently cleared
- if (!_usbd_dev.ep_status[epnum][dir].stalled) {
- TU_LOG_USBD(" Stall EP %02X\r\n", ep_addr);
- dcd_edpt_stall(rhport, ep_addr);
- _usbd_dev.ep_status[epnum][dir].stalled = 1;
- _usbd_dev.ep_status[epnum][dir].busy = 1;
- }
+ TU_LOG_USBD(" Stall EP %02X\r\n", ep_addr);
+ dcd_edpt_stall(rhport, ep_addr);
+ _usbd_dev.ep_status[epnum][dir].stalled = 1;
+ _usbd_dev.ep_status[epnum][dir].busy = 1;
}
void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
@@ -1321,12 +1399,10 @@ void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
uint8_t const dir = tu_edpt_dir(ep_addr);
// only clear if currently stalled
- if (_usbd_dev.ep_status[epnum][dir].stalled) {
- TU_LOG_USBD(" Clear Stall EP %02X\r\n", ep_addr);
- dcd_edpt_clear_stall(rhport, ep_addr);
- _usbd_dev.ep_status[epnum][dir].stalled = 0;
- _usbd_dev.ep_status[epnum][dir].busy = 0;
- }
+ TU_LOG_USBD(" Clear Stall EP %02X\r\n", ep_addr);
+ dcd_edpt_clear_stall(rhport, ep_addr);
+ _usbd_dev.ep_status[epnum][dir].stalled = 0;
+ _usbd_dev.ep_status[epnum][dir].busy = 0;
}
bool usbd_edpt_stalled(uint8_t rhport, uint8_t ep_addr) {
@@ -1359,12 +1435,21 @@ void usbd_edpt_close(uint8_t rhport, uint8_t ep_addr) {
return;
}
-void usbd_sof_enable(uint8_t rhport, bool en) {
+void usbd_sof_enable(uint8_t rhport, sof_consumer_t consumer, bool en) {
rhport = _usbd_rhport;
- // TODO: Check needed if all drivers including the user sof_cb does not need an active SOF ISR any more.
- // Only if all drivers switched off SOF calls the SOF interrupt may be disabled
- dcd_sof_enable(rhport, en);
+ uint8_t consumer_old = _usbd_dev.sof_consumer;
+ // Keep track how many class instances need the SOF interrupt
+ if (en) {
+ _usbd_dev.sof_consumer |= (uint8_t)(1 << consumer);
+ } else {
+ _usbd_dev.sof_consumer &= (uint8_t)(~(1 << consumer));
+ }
+
+ // Test logically unequal
+ if(!_usbd_dev.sof_consumer != !consumer_old) {
+ dcd_sof_enable(rhport, _usbd_dev.sof_consumer);
+ }
}
bool usbd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
diff --git a/src/device/usbd.h b/src/device/usbd.h
index 0197628e2..3d3e83f41 100644
--- a/src/device/usbd.h
+++ b/src/device/usbd.h
@@ -60,7 +60,7 @@ void tud_task (void) {
// Check if there is pending events need processing by tud_task()
bool tud_task_event_ready(void);
-#ifndef _TUSB_DCD_H_
+#ifndef TUSB_DCD_H_
extern void dcd_int_handler(uint8_t rhport);
#endif
@@ -97,6 +97,9 @@ bool tud_disconnect(void);
// Return false on unsupported MCUs
bool tud_connect(void);
+// Enable or disable the Start Of Frame callback support
+void tud_sof_cb_enable(bool en);
+
// Carry out Data and Status stage of control transfer
// - If len = 0, it is equivalent to sending status only
// - If len > wLength : it will be truncated
@@ -106,7 +109,7 @@ bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const * request, vo
bool tud_control_status(uint8_t rhport, tusb_control_request_t const * request);
//--------------------------------------------------------------------+
-// Application Callbacks (WEAK is optional)
+// Application Callbacks
//--------------------------------------------------------------------+
// Invoked when received GET DEVICE DESCRIPTOR request
@@ -123,37 +126,40 @@ uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid);
// Invoked when received GET BOS DESCRIPTOR request
// Application return pointer to descriptor
-TU_ATTR_WEAK uint8_t const * tud_descriptor_bos_cb(void);
+uint8_t const * tud_descriptor_bos_cb(void);
// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete.
// device_qualifier descriptor describes information about a high-speed capable device that would
// change if the device were operating at the other speed. If not highspeed capable stall this request.
-TU_ATTR_WEAK uint8_t const* tud_descriptor_device_qualifier_cb(void);
+uint8_t const* tud_descriptor_device_qualifier_cb(void);
// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
// Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa
-TU_ATTR_WEAK uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index);
+uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index);
// Invoked when device is mounted (configured)
-TU_ATTR_WEAK void tud_mount_cb(void);
+void tud_mount_cb(void);
// Invoked when device is unmounted
-TU_ATTR_WEAK void tud_umount_cb(void);
+void tud_umount_cb(void);
// Invoked when usb bus is suspended
// Within 7ms, device must draw an average of current less than 2.5 mA from bus
-TU_ATTR_WEAK void tud_suspend_cb(bool remote_wakeup_en);
+void tud_suspend_cb(bool remote_wakeup_en);
// Invoked when usb bus is resumed
-TU_ATTR_WEAK void tud_resume_cb(void);
+void tud_resume_cb(void);
// Invoked when there is a new usb event, which need to be processed by tud_task()/tud_task_ext()
void tud_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr);
+// Invoked when a new (micro) frame started
+void tud_sof_cb(uint32_t frame_count);
+
// Invoked when received control request with VENDOR TYPE
-TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
+bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
//--------------------------------------------------------------------+
// Binary Device Object Store (BOS) Descriptor Templates
@@ -221,8 +227,8 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_HEADER, U16_TO_U8S_LE(0x0120),\
/* CDC Call */\
5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_CALL_MANAGEMENT, 0, (uint8_t)((_itfnum) + 1),\
- /* CDC ACM: support line request */\
- 4, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT, 2,\
+ /* CDC ACM: support line request + send break */\
+ 4, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT, 6,\
/* CDC Union */\
5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_UNION, _itfnum, (uint8_t)((_itfnum) + 1),\
/* Endpoint Notification */\
diff --git a/src/device/usbd_pvt.h b/src/device/usbd_pvt.h
index 47752f32c..335d46cd8 100644
--- a/src/device/usbd_pvt.h
+++ b/src/device/usbd_pvt.h
@@ -23,8 +23,8 @@
*
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_USBD_PVT_H_
-#define _TUSB_USBD_PVT_H_
+#ifndef TUSB_USBD_PVT_H_
+#define TUSB_USBD_PVT_H_
#include "osal/osal.h"
#include "common/tusb_fifo.h"
@@ -36,14 +36,20 @@
#define TU_LOG_USBD(...) TU_LOG(CFG_TUD_LOG_LEVEL, __VA_ARGS__)
//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF PROTYPES
+//--------------------------------------------------------------------+
+
+typedef enum {
+ SOF_CONSUMER_USER = 0,
+ SOF_CONSUMER_AUDIO,
+} sof_consumer_t;
+
+//--------------------------------------------------------------------+
// Class Driver API
//--------------------------------------------------------------------+
typedef struct {
- #if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
char const* name;
- #endif
-
void (* init ) (void);
bool (* deinit ) (void);
void (* reset ) (uint8_t rhport);
@@ -111,7 +117,7 @@ bool usbd_edpt_ready(uint8_t rhport, uint8_t ep_addr) {
}
// Enable SOF interrupt
-void usbd_sof_enable(uint8_t rhport, bool en);
+void usbd_sof_enable(uint8_t rhport, sof_consumer_t consumer, bool en);
/*------------------------------------------------------------------*/
/* Helper
diff --git a/src/host/usbh.c b/src/host/usbh.c
index 6a5491998..8c488aaa2 100644
--- a/src/host/usbh.c
+++ b/src/host/usbh.c
@@ -288,9 +288,9 @@ TU_ATTR_WEAK void osal_task_delay(uint32_t msec) {
#endif
TU_ATTR_ALWAYS_INLINE static inline bool queue_event(hcd_event_t const * event, bool in_isr) {
- bool ret = osal_queue_send(_usbh_q, event, in_isr);
+ TU_ASSERT(osal_queue_send(_usbh_q, event, in_isr));
tuh_event_hook_cb(event->rhport, event->event_id, in_isr);
- return ret;
+ return true;
}
//--------------------------------------------------------------------+
@@ -704,7 +704,7 @@ static bool usbh_control_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t
tusb_control_request_t const * request = &_ctrl_xfer.request;
if (XFER_RESULT_SUCCESS != result) {
- TU_LOG_USBH("[%u:%u] Control %s, xferred_bytes = %lu\r\n", rhport, daddr, result == XFER_RESULT_STALLED ? "STALLED" : "FAILED", xferred_bytes);
+ TU_LOG_USBH("[%u:%u] Control %s, xferred_bytes = %" PRIu32 "\r\n", rhport, daddr, result == XFER_RESULT_STALLED ? "STALLED" : "FAILED", xferred_bytes);
TU_LOG_BUF_USBH(request, 8);
// terminate transfer if any stage failed
@@ -1113,7 +1113,7 @@ bool tuh_interface_set(uint8_t daddr, uint8_t itf_num, uint8_t itf_alt,
TU_LOG_USBH("Set Interface %u Alternate %u\r\n", itf_num, itf_alt);
tusb_control_request_t const request = {
.bmRequestType_bit = {
- .recipient = TUSB_REQ_RCPT_DEVICE,
+ .recipient = TUSB_REQ_RCPT_INTERFACE,
.type = TUSB_REQ_TYPE_STANDARD,
.direction = TUSB_DIR_OUT
},
@@ -1421,6 +1421,9 @@ static void process_enumeration(tuh_xfer_t* xfer) {
break;
case ENUM_GET_DEVICE_DESC: {
+ // Allow 2ms for address recovery time, Ref USB Spec 9.2.6.3
+ osal_task_delay(2);
+
uint8_t const new_addr = (uint8_t) tu_le16toh(xfer->setup->wValue);
usbh_device_t* new_dev = get_device(new_addr);
diff --git a/src/osal/osal_freertos.h b/src/osal/osal_freertos.h
index f1f05f353..a3a0f3a3f 100644
--- a/src/osal/osal_freertos.h
+++ b/src/osal/osal_freertos.h
@@ -78,7 +78,7 @@ typedef struct
// _int_set is not used with an RTOS
#define OSAL_QUEUE_DEF(_int_set, _name, _depth, _type) \
static _type _name##_##buf[_depth];\
- osal_queue_def_t _name = { .depth = _depth, .item_sz = sizeof(_type), .buf = _name##_##buf, _OSAL_Q_NAME(_name) };
+ osal_queue_def_t _name = { .depth = _depth, .item_sz = sizeof(_type), .buf = _name##_##buf, _OSAL_Q_NAME(_name) }
//--------------------------------------------------------------------+
// TASK API
diff --git a/src/osal/osal_none.h b/src/osal/osal_none.h
index c954fcfe8..c93f7a86c 100644
--- a/src/osal/osal_none.h
+++ b/src/osal/osal_none.h
@@ -180,7 +180,6 @@ TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void
_osal_q_unlock(qhdl);
}
- TU_ASSERT(success);
return success;
}
diff --git a/src/osal/osal_pico.h b/src/osal/osal_pico.h
index 00c589ef9..315de0950 100644
--- a/src/osal/osal_pico.h
+++ b/src/osal/osal_pico.h
@@ -145,7 +145,6 @@ TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void
bool success = tu_fifo_write(&qhdl->ff, data);
critical_section_exit(&qhdl->critsec);
- TU_ASSERT(success);
return success;
}
diff --git a/src/portable/analog/max3421/hcd_max3421.c b/src/portable/analog/max3421/hcd_max3421.c
index 39afb7505..d9b8e1fc3 100644
--- a/src/portable/analog/max3421/hcd_max3421.c
+++ b/src/portable/analog/max3421/hcd_max3421.c
@@ -174,7 +174,8 @@ enum {
enum {
EP_STATE_IDLE = 0,
EP_STATE_COMPLETE = 1,
- EP_STATE_ATTEMPT_1 = 2, // pending 1st attempt
+ EP_STATE_ABORTING = 2,
+ EP_STATE_ATTEMPT_1 = 3, // pending 1st attempt
EP_STATE_ATTEMPT_MAX = 15
};
@@ -459,6 +460,7 @@ bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param) {
tuh_configure_param_t const* cfg = (tuh_configure_param_t const*) cfg_param;
_tuh_cfg = cfg->max3421;
+ _tuh_cfg.max_nak = tu_min8(_tuh_cfg.max_nak, EP_STATE_ATTEMPT_MAX-EP_STATE_ATTEMPT_1);
return true;
}
@@ -479,13 +481,15 @@ bool hcd_init(uint8_t rhport) {
_hcd_data.spi_mutex = osal_mutex_create(&_hcd_data.spi_mutexdef);
#endif
+ // NOTE: driver does not seem to work without nRST pin signal
+
// full duplex, interrupt negative edge
reg_write(rhport, PINCTL_ADDR, _tuh_cfg.pinctl | PINCTL_FDUPSPI, false);
// v1 is 0x01, v2 is 0x12, v3 is 0x13
uint8_t const revision = reg_read(rhport, REVISION_ADDR, false);
- TU_ASSERT(revision == 0x01 || revision == 0x12 || revision == 0x13, false);
TU_LOG2_HEX(revision);
+ TU_ASSERT(revision == 0x01 || revision == 0x12 || revision == 0x13, false);
// reset
reg_write(rhport, USBCTL_ADDR, USBCTL_CHIPRES, false);
@@ -676,7 +680,6 @@ static void xact_generic(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool
bool hcd_edpt_xfer(uint8_t rhport, uint8_t daddr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) {
uint8_t const ep_num = tu_edpt_number(ep_addr);
uint8_t const ep_dir = (uint8_t) tu_edpt_dir(ep_addr);
-
max3421_ep_t* ep = find_opened_ep(daddr, ep_num, ep_dir);
TU_VERIFY(ep);
@@ -700,14 +703,19 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t daddr, uint8_t ep_addr, uint8_t * buf
return true;
}
-// 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) {
- (void) rhport;
- (void) dev_addr;
- (void) ep_addr;
+bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t daddr, uint8_t ep_addr) {
+ uint8_t const ep_num = tu_edpt_number(ep_addr);
+ uint8_t const ep_dir = (uint8_t) tu_edpt_dir(ep_addr);
+ max3421_ep_t* ep = find_opened_ep(daddr, ep_num, ep_dir);
+ TU_VERIFY(ep);
- return false;
+ if (EP_STATE_ATTEMPT_1 <= ep->state && ep->state < EP_STATE_ATTEMPT_MAX) {
+ hcd_int_disable(rhport);
+ ep->state = EP_STATE_ABORTING;
+ 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
@@ -817,7 +825,6 @@ static void xfer_complete_isr(uint8_t rhport, max3421_ep_t *ep, xfer_result_t re
static void handle_xfer_done(uint8_t rhport, bool in_isr) {
uint8_t const hrsl = reg_read(rhport, HRSL_ADDR, in_isr);
uint8_t const hresult = hrsl & HRSL_RESULT_MASK;
-
uint8_t const ep_num = _hcd_data.hxfr & HXFR_EPNUM_MASK;
uint8_t const hxfr_type = _hcd_data.hxfr & 0xf0;
uint8_t const ep_dir = ((hxfr_type & HXFR_SETUP) || (hxfr_type & HXFR_OUT_NIN)) ? 0 : 1;
@@ -827,34 +834,30 @@ static void handle_xfer_done(uint8_t rhport, bool in_isr) {
xfer_result_t xfer_result;
switch(hresult) {
- case HRSL_SUCCESS:
- xfer_result = XFER_RESULT_SUCCESS;
- break;
-
- case HRSL_STALL:
- xfer_result = XFER_RESULT_STALLED;
- break;
-
case HRSL_NAK:
- if (ep_num == 0) {
- // control endpoint -> retry immediately
- hxfr_write(rhport, _hcd_data.hxfr, in_isr);
+ if (ep->state == EP_STATE_ABORTING) {
+ ep->state = EP_STATE_IDLE;
} else {
- if (ep->state < EP_STATE_ATTEMPT_MAX) {
+ if (ep_num == 0) {
+ // control endpoint -> retry immediately and return
+ hxfr_write(rhport, _hcd_data.hxfr, in_isr);
+ return;
+ } else if (EP_STATE_ATTEMPT_1 <= ep->state && ep->state < EP_STATE_ATTEMPT_MAX) {
ep->state++;
}
+ }
- max3421_ep_t * next_ep = find_next_pending_ep(ep);
- if (ep == next_ep) {
- // this endpoint is only one pending -> retry immediately
- hxfr_write(rhport, _hcd_data.hxfr, in_isr);
- } else if (next_ep) {
- // switch to next pending endpoint TODO could have issue with double buffered if not clear previously out data
- xact_generic(rhport, next_ep, true, in_isr);
- } else {
- // no more pending in this frame -> clear busy
- atomic_flag_clear(&_hcd_data.busy);
- }
+ max3421_ep_t * next_ep = find_next_pending_ep(ep);
+ if (ep == next_ep) {
+ // this endpoint is only one pending -> retry immediately
+ hxfr_write(rhport, _hcd_data.hxfr, in_isr);
+ } else if (next_ep) {
+ // switch to next pending endpoint
+ // TODO could have issue with double buffered if not clear previously out data
+ xact_generic(rhport, next_ep, true, in_isr);
+ } else {
+ // no more pending in this frame -> clear busy
+ atomic_flag_clear(&_hcd_data.busy);
}
return;
@@ -862,6 +865,14 @@ static void handle_xfer_done(uint8_t rhport, bool in_isr) {
// occurred when initialized without any pending transfer. Skip for now
return;
+ case HRSL_SUCCESS:
+ xfer_result = XFER_RESULT_SUCCESS;
+ break;
+
+ case HRSL_STALL:
+ xfer_result = XFER_RESULT_STALLED;
+ break;
+
default:
TU_LOG3("HRSL: %02X\r\n", hrsl);
xfer_result = XFER_RESULT_FAILED;
@@ -940,9 +951,8 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) {
if (hirq & HIRQ_FRAME_IRQ) {
_hcd_data.frame_count++;
+ // reset all endpoints nak counter, retry with 1st pending ep.
max3421_ep_t* ep_retry = NULL;
-
- // reset all endpoints attempt counter
for (size_t i = 0; i < CFG_TUH_MAX3421_ENDPOINT_TOTAL; i++) {
max3421_ep_t* ep = &_hcd_data.ep[i];
if (ep->packet_size && ep->state > EP_STATE_ATTEMPT_1) {
@@ -1002,7 +1012,7 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) {
// clear all interrupt except SNDBAV_IRQ (never clear by us). Note RCVDAV_IRQ, HXFRDN_IRQ already clear while processing
hirq &= (uint8_t) ~HIRQ_SNDBAV_IRQ;
- if ( hirq ) {
+ if (hirq) {
hirq_write(rhport, hirq, in_isr);
}
}
diff --git a/src/portable/chipidea/ci_hs/dcd_ci_hs.c b/src/portable/chipidea/ci_hs/dcd_ci_hs.c
index f9ec666e5..93e1d78dd 100644
--- a/src/portable/chipidea/ci_hs/dcd_ci_hs.c
+++ b/src/portable/chipidea/ci_hs/dcd_ci_hs.c
@@ -309,10 +309,12 @@ void dcd_disconnect(uint8_t rhport)
void dcd_sof_enable(uint8_t rhport, bool en)
{
- (void) rhport;
- (void) en;
-
- // TODO implement later
+ ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
+ if (en) {
+ dcd_reg->USBINTR |= INTR_SOF;
+ } else {
+ dcd_reg->USBINTR &= ~INTR_SOF;
+ }
}
//--------------------------------------------------------------------+
@@ -679,7 +681,8 @@ void dcd_int_handler(uint8_t rhport)
if (int_status & INTR_SOF)
{
- dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
+ const uint32_t frame = dcd_reg->FRINDEX;
+ dcd_event_sof(rhport, frame, true);
}
}
diff --git a/src/portable/ehci/ehci.c b/src/portable/ehci/ehci.c
index e145cbb1b..01bbf62bf 100644
--- a/src/portable/ehci/ehci.c
+++ b/src/portable/ehci/ehci.c
@@ -92,7 +92,7 @@ CFG_TUH_MEM_SECTION TU_ATTR_ALIGNED(4096) static ehci_data_t ehci_data;
//--------------------------------------------------------------------+
// Debug
//--------------------------------------------------------------------+
-#if CFG_TUSB_DEBUG >= (EHCI_DBG + 1)
+#if 0 && CFG_TUSB_DEBUG >= (EHCI_DBG + 1)
static inline void print_portsc(ehci_registers_t* regs) {
TU_LOG_HEX(EHCI_DBG, regs->portsc);
TU_LOG(EHCI_DBG, " Connect Status : %u\r\n", regs->portsc_bm.current_connect_status);
diff --git a/src/portable/espressif/esp32sx/dcd_esp32sx.c b/src/portable/espressif/esp32sx/dcd_esp32sx.c
index bfc0baa56..f912bef89 100644
--- a/src/portable/espressif/esp32sx/dcd_esp32sx.c
+++ b/src/portable/espressif/esp32sx/dcd_esp32sx.c
@@ -31,7 +31,8 @@
#if (((CFG_TUSB_MCU == OPT_MCU_ESP32S2) || (CFG_TUSB_MCU == OPT_MCU_ESP32S3)) && CFG_TUD_ENABLED)
// Espressif
-#include "xtensa_api.h"
+#include "xtensa/xtensa_api.h"
+
#include "esp_intr_alloc.h"
#include "esp_log.h"
#include "soc/dport_reg.h"
diff --git a/src/portable/microchip/samd/dcd_samd.c b/src/portable/microchip/samd/dcd_samd.c
index 976d3dfd0..005b63faf 100644
--- a/src/portable/microchip/samd/dcd_samd.c
+++ b/src/portable/microchip/samd/dcd_samd.c
@@ -183,9 +183,12 @@ void dcd_connect(uint8_t rhport)
void dcd_sof_enable(uint8_t rhport, bool en)
{
(void) rhport;
- (void) en;
- // TODO implement later
+ if (en) {
+ USB->DEVICE.INTENSET.bit.SOF = 1;
+ } else {
+ USB->DEVICE.INTENCLR.bit.SOF = 1;
+ }
}
/*------------------------------------------------------------------*/
@@ -374,7 +377,9 @@ void dcd_int_handler (uint8_t rhport)
if ( int_status & USB_DEVICE_INTFLAG_SOF )
{
USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_SOF;
- dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
+ const uint32_t frame = USB->DEVICE.FNUM.bit.FNUM;
+ dcd_event_sof(0, frame, true);
+ //dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
}
// SAMD doesn't distinguish between Suspend and Disconnect state.
diff --git a/src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c b/src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c
index c3d0c7297..d5c0daaeb 100644
--- a/src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c
+++ b/src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c
@@ -283,7 +283,18 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
/* Response with status first before changing device address */
dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
}
+
+#ifdef __GNUC__ // caused by extra declaration of SystemCoreClock in freeRTOSConfig.h
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wredundant-decls"
+#endif
+
extern u32 SystemCoreClock;
+
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
void dcd_remote_wakeup(uint8_t rhport)
{
(void) rhport;
diff --git a/src/portable/nordic/nrf5x/dcd_nrf5x.c b/src/portable/nordic/nrf5x/dcd_nrf5x.c
index 80a285ef0..1cc5c1836 100644
--- a/src/portable/nordic/nrf5x/dcd_nrf5x.c
+++ b/src/portable/nordic/nrf5x/dcd_nrf5x.c
@@ -40,7 +40,6 @@
#include "nrf.h"
#include "nrf_clock.h"
-#include "nrf_power.h"
#include "nrfx_usbd_errata.h"
#ifdef __GNUC__
@@ -57,28 +56,46 @@
#include "mcu/mcu.h"
#endif
+/* Try to detect nrfx version if not configured with CFG_TUD_NRF_NRFX_VERSION
+ * nrfx v1 and v2 are concurrently developed. There is no NRFX_VERSION only MDK VERSION which is as follows:
+ * - v3.0.0: 8.53.1 (conflict with v2.11.0), v3.1.0: 8.55.0 ...
+ * - v2.11.0: 8.53.1, v2.6.0: 8.44.1, v2.5.0: 8.40.2, v2.4.0: 8.37.0, v2.3.0: 8.35.0, v2.2.0: 8.32.1, v2.1.0: 8.30.2, v2.0.0: 8.29.0
+ * - v1.9.0: 8.40.3, v1.8.6: 8.35.0 (conflict with v2.3.0), v1.8.5: 8.32.3, v1.8.4: 8.32.1 (conflict with v2.2.0),
+ * v1.8.2: 8.32.1 (conflict with v2.2.0), v1.8.1: 8.27.1
+ * Therefore the check for v1 would be:
+ * - MDK < 8.29.0 (v2.0), MDK == 8.32.3, 8.40.3
+ * - in case of conflict User of those version must upgrade to other 1.x version or set CFG_TUD_NRF_NRFX_VERSION
+*/
+#ifndef CFG_TUD_NRF_NRFX_VERSION
+ #define _MDK_VERSION (10000*MDK_MAJOR_VERSION + 100*MDK_MINOR_VERSION + MDK_MICRO_VERSION)
+
+ #if _MDK_VERSION < 82900 || _MDK_VERSION == 83203 || _MDK_VERSION == 84003
+ // nrfx <= 1.8.1, or 1.8.5 or 1.9.0
+ #define CFG_TUD_NRF_NRFX_VERSION 1
+ #else
+ #define CFG_TUD_NRF_NRFX_VERSION 2
+ #endif
+#endif
+
/*------------------------------------------------------------------*/
/* MACRO TYPEDEF CONSTANT ENUM
*------------------------------------------------------------------*/
-enum
-{
+enum {
// Max allowed by USB specs
- MAX_PACKET_SIZE = 64,
+ MAX_PACKET_SIZE = 64,
// Mask of all END event (IN & OUT) for all endpoints. ENDEPIN0-7, ENDEPOUT0-7, ENDISOIN, ENDISOOUT
EDPT_END_ALL_MASK = (0xff << USBD_INTEN_ENDEPIN0_Pos) | (0xff << USBD_INTEN_ENDEPOUT0_Pos) |
USBD_INTENCLR_ENDISOIN_Msk | USBD_INTEN_ENDISOOUT_Msk
};
-enum
-{
- EP_ISO_NUM = 8, // Endpoint number is fixed (8) for ISOOUT and ISOIN
+enum {
+ EP_ISO_NUM = 8, // Endpoint number is fixed (8) for ISOOUT and ISOIN
EP_CBI_COUNT = 8 // Control Bulk Interrupt endpoints count
};
// Transfer Descriptor
-typedef struct
-{
+typedef struct {
uint8_t* buffer;
uint16_t total_len;
volatile uint16_t actual_len;
@@ -96,113 +113,85 @@ typedef struct
} xfer_td_t;
// Data for managing dcd
-static struct
-{
+static struct {
// All 8 endpoints including control IN & OUT (offset 1)
// +1 for ISO endpoints
xfer_td_t xfer[EP_CBI_COUNT + 1][2];
// nRF can only carry one DMA at a time, this is used to guard the access to EasyDMA
- atomic_bool dma_running;
-}_dcd;
+ atomic_flag dma_running;
+
+ // Track whether sof has been manually enabled
+ bool sof_enabled;
+} _dcd;
/*------------------------------------------------------------------*/
/* Control / Bulk / Interrupt (CBI) Transfer
*------------------------------------------------------------------*/
-// NVIC_GetEnableIRQ is only available in CMSIS v5
-#ifndef NVIC_GetEnableIRQ
-static inline uint32_t NVIC_GetEnableIRQ(IRQn_Type IRQn)
-{
- if ((int32_t)(IRQn) >= 0)
- {
- return((uint32_t)(((NVIC->ISER[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL));
- }
- else
- {
- return(0U);
- }
-}
-#endif
-
// check if we are in ISR
-TU_ATTR_ALWAYS_INLINE static inline bool is_in_isr(void)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool is_in_isr(void) {
return (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) ? true : false;
}
// helper to start DMA
-static void start_dma(volatile uint32_t* reg_startep)
-{
+static void start_dma(volatile uint32_t* reg_startep) {
(*reg_startep) = 1;
- __ISB(); __DSB();
+ __ISB();
+ __DSB();
// TASKS_EP0STATUS, TASKS_EP0RCVOUT seem to need EasyDMA to be available
// However these don't trigger any DMA transfer and got ENDED event subsequently
// Therefore dma_pending is corrected right away
- if ( (reg_startep == &NRF_USBD->TASKS_EP0STATUS) || (reg_startep == &NRF_USBD->TASKS_EP0RCVOUT) )
- {
+ if ((reg_startep == &NRF_USBD->TASKS_EP0STATUS) || (reg_startep == &NRF_USBD->TASKS_EP0RCVOUT)) {
atomic_flag_clear(&_dcd.dma_running);
}
}
-static void edpt_dma_start(volatile uint32_t* reg_startep)
-{
- if ( atomic_flag_test_and_set(&_dcd.dma_running) )
- {
- usbd_defer_func((osal_task_func_t) edpt_dma_start, (void*) (uintptr_t) reg_startep, true);
- }else
- {
+static void edpt_dma_start(volatile uint32_t* reg_startep) {
+ if (atomic_flag_test_and_set(&_dcd.dma_running)) {
+ usbd_defer_func((osal_task_func_t)(uintptr_t ) edpt_dma_start, (void*) (uintptr_t) reg_startep, is_in_isr());
+ } else {
start_dma(reg_startep);
}
}
// DMA is complete
-static void edpt_dma_end(void)
-{
- TU_ASSERT(_dcd.dma_running, );
+static void edpt_dma_end(void) {
atomic_flag_clear(&_dcd.dma_running);
}
// helper getting td
-static inline xfer_td_t* get_td(uint8_t epnum, uint8_t dir)
-{
+static inline xfer_td_t* get_td(uint8_t epnum, uint8_t dir) {
return &_dcd.xfer[epnum][dir];
}
static void xact_out_dma(uint8_t epnum);
+
// Function wraps xact_out_dma which wants uint8_t while usbd_defer_func wants void (*)(void *)
-static void xact_out_dma_wrapper(void *epnum)
-{
- xact_out_dma((uint8_t)((uintptr_t)epnum));
+static void xact_out_dma_wrapper(void* epnum) {
+ xact_out_dma((uint8_t) ((uintptr_t) epnum));
}
// Start DMA to move data from Endpoint -> RAM
-static void xact_out_dma(uint8_t epnum)
-{
+static void xact_out_dma(uint8_t epnum) {
xfer_td_t* xfer = get_td(epnum, TUSB_DIR_OUT);
uint32_t xact_len;
// DMA can't be active during read of SIZE.EPOUT or SIZE.ISOOUT, so try to lock,
// If already running defer call regardless if it was called from ISR or task,
- if ( atomic_flag_test_and_set(&_dcd.dma_running) )
- {
- usbd_defer_func((osal_task_func_t)xact_out_dma_wrapper, (void *)(uint32_t)epnum, is_in_isr());
+ if (atomic_flag_test_and_set(&_dcd.dma_running)) {
+ usbd_defer_func((osal_task_func_t) xact_out_dma_wrapper, (void*) (uint32_t) epnum, is_in_isr());
return;
}
- if (epnum == EP_ISO_NUM)
- {
+ if (epnum == EP_ISO_NUM) {
xact_len = NRF_USBD->SIZE.ISOOUT;
// If ZERO bit is set, ignore ISOOUT length
- if (xact_len & USBD_SIZE_ISOOUT_ZERO_Msk)
- {
+ if (xact_len & USBD_SIZE_ISOOUT_ZERO_Msk) {
xact_len = 0;
atomic_flag_clear(&_dcd.dma_running);
- }
- else
- {
- if (xfer->started)
- {
+ } else {
+ if (xfer->started) {
// Trigger DMA move data from Endpoint -> SRAM
NRF_USBD->ISOOUT.PTR = (uint32_t) xfer->buffer;
NRF_USBD->ISOOUT.MAXCNT = xact_len;
@@ -212,9 +201,7 @@ static void xact_out_dma(uint8_t epnum)
atomic_flag_clear(&_dcd.dma_running);
}
}
- }
- else
- {
+ } else {
// limit xact len to remaining length
xact_len = tu_min16((uint16_t) NRF_USBD->SIZE.EPOUT[epnum], xfer->total_len - xfer->actual_len);
@@ -228,14 +215,13 @@ static void xact_out_dma(uint8_t epnum)
// Prepare for a CBI transaction IN, call at the start
// it start DMA to transfer data from RAM -> Endpoint
-static void xact_in_dma(uint8_t epnum)
-{
+static void xact_in_dma(uint8_t epnum) {
xfer_td_t* xfer = get_td(epnum, TUSB_DIR_IN);
// Each transaction is up to Max Packet Size
uint16_t const xact_len = tu_min16(xfer->total_len - xfer->actual_len, xfer->mps);
- NRF_USBD->EPIN[epnum].PTR = (uint32_t) xfer->buffer;
+ NRF_USBD->EPIN[epnum].PTR = (uint32_t) xfer->buffer;
NRF_USBD->EPIN[epnum].MAXCNT = xact_len;
edpt_dma_start(&NRF_USBD->TASKS_STARTEPIN[epnum]);
@@ -244,26 +230,22 @@ static void xact_in_dma(uint8_t epnum)
//--------------------------------------------------------------------+
// Controller API
//--------------------------------------------------------------------+
-void dcd_init (uint8_t rhport)
-{
+void dcd_init(uint8_t rhport) {
TU_LOG2("dcd init\r\n");
(void) rhport;
}
-void dcd_int_enable(uint8_t rhport)
-{
+void dcd_int_enable(uint8_t rhport) {
(void) rhport;
NVIC_EnableIRQ(USBD_IRQn);
}
-void dcd_int_disable(uint8_t rhport)
-{
+void dcd_int_disable(uint8_t rhport) {
(void) rhport;
NVIC_DisableIRQ(USBD_IRQn);
}
-void dcd_set_address (uint8_t rhport, uint8_t dev_addr)
-{
+void dcd_set_address(uint8_t rhport, uint8_t dev_addr) {
(void) rhport;
(void) dev_addr;
// Set Address is automatically update by hw controller, nothing to do
@@ -278,8 +260,7 @@ void dcd_set_address (uint8_t rhport, uint8_t dev_addr)
NRF_USBD->INTENSET = USBD_INTEN_USBEVENT_Msk;
}
-void dcd_remote_wakeup(uint8_t rhport)
-{
+void dcd_remote_wakeup(uint8_t rhport) {
(void) rhport;
// Bring controller out of low power mode
@@ -288,8 +269,7 @@ void dcd_remote_wakeup(uint8_t rhport)
}
// disconnect by disabling internal pull-up resistor on D+/D-
-void dcd_disconnect(uint8_t rhport)
-{
+void dcd_disconnect(uint8_t rhport) {
(void) rhport;
NRF_USBD->USBPULLUP = 0;
@@ -299,56 +279,51 @@ void dcd_disconnect(uint8_t rhport)
}
// connect by enabling internal pull-up resistor on D+/D-
-void dcd_connect(uint8_t rhport)
-{
+void dcd_connect(uint8_t rhport) {
(void) rhport;
NRF_USBD->USBPULLUP = 1;
}
-void dcd_sof_enable(uint8_t rhport, bool en)
-{
+void dcd_sof_enable(uint8_t rhport, bool en) {
(void) rhport;
- (void) en;
-
- // TODO implement later
+ if (en) {
+ _dcd.sof_enabled = true;
+ NRF_USBD->INTENSET = USBD_INTENSET_SOF_Msk;
+ } else {
+ _dcd.sof_enabled = false;
+ NRF_USBD->INTENCLR = USBD_INTENCLR_SOF_Msk;
+ }
}
//--------------------------------------------------------------------+
// Endpoint API
//--------------------------------------------------------------------+
-bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
-{
+bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_edpt) {
(void) rhport;
uint8_t const ep_addr = desc_edpt->bEndpointAddress;
- uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
_dcd.xfer[epnum][dir].mps = tu_edpt_packet_size(desc_edpt);
- if (desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS)
- {
- if (dir == TUSB_DIR_OUT)
- {
+ if (desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS) {
+ if (dir == TUSB_DIR_OUT) {
NRF_USBD->INTENSET = TU_BIT(USBD_INTEN_ENDEPOUT0_Pos + epnum);
NRF_USBD->EPOUTEN |= TU_BIT(epnum);
// Write any value to SIZE register will allow nRF to ACK/accept data
NRF_USBD->SIZE.EPOUT[epnum] = 0;
- }else
- {
+ } else {
NRF_USBD->INTENSET = TU_BIT(USBD_INTEN_ENDEPIN0_Pos + epnum);
- NRF_USBD->EPINEN |= TU_BIT(epnum);
+ NRF_USBD->EPINEN |= TU_BIT(epnum);
}
// clear stall and reset DataToggle
NRF_USBD->EPSTALL = (USBD_EPSTALL_STALL_UnStall << USBD_EPSTALL_STALL_Pos) | ep_addr;
NRF_USBD->DTOGGLE = (USBD_DTOGGLE_VALUE_Data0 << USBD_DTOGGLE_VALUE_Pos) | ep_addr;
- }
- else
- {
+ } else {
TU_ASSERT(epnum == EP_ISO_NUM);
- if (dir == TUSB_DIR_OUT)
- {
+ if (dir == TUSB_DIR_OUT) {
// SPLIT ISO buffer when ISO IN endpoint is already opened.
if (_dcd.xfer[EP_ISO_NUM][TUSB_DIR_IN].mps) NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_HalfIN;
@@ -361,9 +336,7 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
// Enable SOF and ISOOUT interrupts, and ISOOUT endpoint.
NRF_USBD->INTENSET = USBD_INTENSET_ENDISOOUT_Msk | USBD_INTENSET_SOF_Msk;
NRF_USBD->EPOUTEN |= USBD_EPOUTEN_ISOOUT_Msk;
- }
- else
- {
+ } else {
NRF_USBD->EVENTS_ENDISOIN = 0;
// SPLIT ISO buffer when ISO OUT endpoint is already opened.
@@ -374,39 +347,38 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
// Enable SOF and ISOIN interrupts, and ISOIN endpoint.
NRF_USBD->INTENSET = USBD_INTENSET_ENDISOIN_Msk | USBD_INTENSET_SOF_Msk;
- NRF_USBD->EPINEN |= USBD_EPINEN_ISOIN_Msk;
+ NRF_USBD->EPINEN |= USBD_EPINEN_ISOIN_Msk;
}
}
- __ISB(); __DSB();
+ __ISB();
+ __DSB();
return true;
}
-void dcd_edpt_close_all (uint8_t rhport)
-{
+void dcd_edpt_close_all(uint8_t rhport) {
// disable interrupt to prevent race condition
dcd_int_disable(rhport);
// disable all non-control (bulk + interrupt) endpoints
- for ( uint8_t ep = 1; ep < EP_CBI_COUNT; ep++ )
- {
+ for (uint8_t ep = 1; ep < EP_CBI_COUNT; ep++) {
NRF_USBD->INTENCLR = TU_BIT(USBD_INTEN_ENDEPOUT0_Pos + ep) | TU_BIT(USBD_INTEN_ENDEPIN0_Pos + ep);
NRF_USBD->TASKS_STARTEPIN[ep] = 0;
NRF_USBD->TASKS_STARTEPOUT[ep] = 0;
- tu_memclr(_dcd.xfer[ep], 2*sizeof(xfer_td_t));
+ tu_memclr(_dcd.xfer[ep], 2 * sizeof(xfer_td_t));
}
// disable both ISO
NRF_USBD->INTENCLR = USBD_INTENCLR_SOF_Msk | USBD_INTENCLR_ENDISOOUT_Msk | USBD_INTENCLR_ENDISOIN_Msk;
NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_OneDir;
- NRF_USBD->TASKS_STARTISOIN = 0;
+ NRF_USBD->TASKS_STARTISOIN = 0;
NRF_USBD->TASKS_STARTISOOUT = 0;
- tu_memclr(_dcd.xfer[EP_ISO_NUM], 2*sizeof(xfer_td_t));
+ tu_memclr(_dcd.xfer[EP_ISO_NUM], 2 * sizeof(xfer_td_t));
// de-activate all non-control
NRF_USBD->EPOUTEN = 1UL;
@@ -415,107 +387,89 @@ void dcd_edpt_close_all (uint8_t rhport)
dcd_int_enable(rhport);
}
-void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr)
-{
+void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) {
(void) rhport;
uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
- if (epnum != EP_ISO_NUM)
- {
+ if (epnum != EP_ISO_NUM) {
// CBI
- if (dir == TUSB_DIR_OUT)
- {
+ if (dir == TUSB_DIR_OUT) {
NRF_USBD->INTENCLR = TU_BIT(USBD_INTEN_ENDEPOUT0_Pos + epnum);
NRF_USBD->EPOUTEN &= ~TU_BIT(epnum);
- }
- else
- {
+ } else {
NRF_USBD->INTENCLR = TU_BIT(USBD_INTEN_ENDEPIN0_Pos + epnum);
NRF_USBD->EPINEN &= ~TU_BIT(epnum);
}
- }
- else
- {
+ } else {
_dcd.xfer[EP_ISO_NUM][dir].mps = 0;
// ISO
- if (dir == TUSB_DIR_OUT)
- {
+ if (dir == TUSB_DIR_OUT) {
NRF_USBD->INTENCLR = USBD_INTENCLR_ENDISOOUT_Msk;
NRF_USBD->EPOUTEN &= ~USBD_EPOUTEN_ISOOUT_Msk;
NRF_USBD->EVENTS_ENDISOOUT = 0;
- }
- else
- {
+ } else {
NRF_USBD->INTENCLR = USBD_INTENCLR_ENDISOIN_Msk;
NRF_USBD->EPINEN &= ~USBD_EPINEN_ISOIN_Msk;
}
// One of the ISO endpoints closed, no need to split buffers any more.
NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_OneDir;
// When both ISO endpoint are close there is no need for SOF any more.
- if (_dcd.xfer[EP_ISO_NUM][TUSB_DIR_IN].mps + _dcd.xfer[EP_ISO_NUM][TUSB_DIR_OUT].mps == 0) NRF_USBD->INTENCLR = USBD_INTENCLR_SOF_Msk;
+ if (_dcd.xfer[EP_ISO_NUM][TUSB_DIR_IN].mps + _dcd.xfer[EP_ISO_NUM][TUSB_DIR_OUT].mps == 0)
+ NRF_USBD->INTENCLR = USBD_INTENCLR_SOF_Msk;
}
_dcd.xfer[epnum][dir].started = false;
- __ISB(); __DSB();
+ __ISB();
+ __DSB();
}
-bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
-{
+bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) {
(void) rhport;
uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
xfer_td_t* xfer = get_td(epnum, dir);
TU_ASSERT(!xfer->started);
- xfer->buffer = buffer;
- xfer->total_len = total_bytes;
+ xfer->buffer = buffer;
+ xfer->total_len = total_bytes;
xfer->actual_len = 0;
// Control endpoint with zero-length packet and opposite direction to 1st request byte --> status stage
bool const control_status = (epnum == 0 && total_bytes == 0 && dir != tu_edpt_dir(NRF_USBD->BMREQUESTTYPE));
- if ( control_status )
- {
+ if (control_status) {
// Status Phase also requires EasyDMA has to be available as well !!!!
edpt_dma_start(&NRF_USBD->TASKS_EP0STATUS);
// The nRF doesn't interrupt on status transmit so we queue up a success response.
dcd_event_xfer_complete(0, ep_addr, 0, XFER_RESULT_SUCCESS, is_in_isr());
- }
- else if ( dir == TUSB_DIR_OUT )
- {
+ } else if (dir == TUSB_DIR_OUT) {
xfer->started = true;
- if ( epnum == 0 )
- {
+ if (epnum == 0) {
// Accept next Control Out packet. TASKS_EP0RCVOUT also require EasyDMA
edpt_dma_start(&NRF_USBD->TASKS_EP0RCVOUT);
- }else
- {
+ } else {
// started just set, it could start DMA transfer if interrupt was trigger after this line
// code only needs to start transfer (from Endpoint to RAM) when data_received was set
// before started was set. If started is NOT set but data_received is, it means that
// current transfer was already finished and next data is already present in endpoint and
// can be consumed by future transfer
- __ISB(); __DSB();
- if ( xfer->data_received && xfer->started )
- {
+ __ISB();
+ __DSB();
+ if (xfer->data_received && xfer->started) {
// Data is already received previously
// start DMA to copy to SRAM
xfer->data_received = false;
xact_out_dma(epnum);
- }
- else
- {
+ } else {
// nRF auto accept next Bulk/Interrupt OUT packet
// nothing to do
}
}
- }
- else
- {
+ } else {
// Start DMA to copy data from RAM -> Endpoint
xact_in_dma(epnum);
}
@@ -523,42 +477,37 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
return true;
}
-void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
-{
+void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
(void) rhport;
uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
xfer_td_t* xfer = get_td(epnum, dir);
- if ( epnum == 0 )
- {
+ if (epnum == 0) {
NRF_USBD->TASKS_EP0STALL = 1;
- }else if (epnum != EP_ISO_NUM)
- {
+ } else if (epnum != EP_ISO_NUM) {
NRF_USBD->EPSTALL = (USBD_EPSTALL_STALL_Stall << USBD_EPSTALL_STALL_Pos) | ep_addr;
// Note: nRF can auto ACK packet OUT before get stalled.
// There maybe data in endpoint fifo already, we need to pull it out
- if ( (dir == TUSB_DIR_OUT) && xfer->data_received )
- {
+ if ((dir == TUSB_DIR_OUT) && xfer->data_received) {
xfer->data_received = false;
xact_out_dma(epnum);
}
}
- __ISB(); __DSB();
+ __ISB();
+ __DSB();
}
-void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
-{
+void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
(void) rhport;
uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
- if ( epnum != 0 && epnum != EP_ISO_NUM )
- {
+ if (epnum != 0 && epnum != EP_ISO_NUM) {
// reset data toggle to DATA0
// First write this register with VALUE=Nop to select the endpoint, then either read it to get the status from
// VALUE, or write it again with VALUE=Data0 or Data1
@@ -571,26 +520,25 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
// Write any value to SIZE register will allow nRF to ACK/accept data
if (dir == TUSB_DIR_OUT) NRF_USBD->SIZE.EPOUT[epnum] = 0;
- __ISB(); __DSB();
+ __ISB();
+ __DSB();
}
}
/*------------------------------------------------------------------*/
/* Interrupt Handler
*------------------------------------------------------------------*/
-void bus_reset(void)
-{
+void bus_reset(void) {
// 6.35.6 USB controller automatically disabled all endpoints (except control)
NRF_USBD->EPOUTEN = 1UL;
NRF_USBD->EPINEN = 1UL;
- for(int i=0; i<8; i++)
- {
+ for (int i = 0; i < 8; i++) {
NRF_USBD->TASKS_STARTEPIN[i] = 0;
NRF_USBD->TASKS_STARTEPOUT[i] = 0;
}
- NRF_USBD->TASKS_STARTISOIN = 0;
+ NRF_USBD->TASKS_STARTISOIN = 0;
NRF_USBD->TASKS_STARTISOOUT = 0;
// Clear USB Event Interrupt
@@ -600,43 +548,40 @@ void bus_reset(void)
// Reset interrupt
NRF_USBD->INTENCLR = NRF_USBD->INTEN;
NRF_USBD->INTENSET = USBD_INTEN_USBRESET_Msk | USBD_INTEN_USBEVENT_Msk | USBD_INTEN_EPDATA_Msk |
- USBD_INTEN_EP0SETUP_Msk | USBD_INTEN_EP0DATADONE_Msk | USBD_INTEN_ENDEPIN0_Msk | USBD_INTEN_ENDEPOUT0_Msk;
+ USBD_INTEN_EP0SETUP_Msk | USBD_INTEN_EP0DATADONE_Msk | USBD_INTEN_ENDEPIN0_Msk |
+ USBD_INTEN_ENDEPOUT0_Msk;
tu_varclr(&_dcd);
_dcd.xfer[0][TUSB_DIR_IN].mps = MAX_PACKET_SIZE;
_dcd.xfer[0][TUSB_DIR_OUT].mps = MAX_PACKET_SIZE;
}
-void dcd_int_handler(uint8_t rhport)
-{
+void dcd_int_handler(uint8_t rhport) {
(void) rhport;
- uint32_t const inten = NRF_USBD->INTEN;
+ uint32_t const inten = NRF_USBD->INTEN;
uint32_t int_status = 0;
volatile uint32_t* regevt = &NRF_USBD->EVENTS_USBRESET;
- for(uint8_t i=0; i<USBD_INTEN_EPDATA_Pos+1; i++)
- {
- if ( tu_bit_test(inten, i) && regevt[i] )
- {
+ for (uint8_t i = 0; i < USBD_INTEN_EPDATA_Pos + 1; i++) {
+ if (tu_bit_test(inten, i) && regevt[i]) {
int_status |= TU_BIT(i);
// event clear
regevt[i] = 0;
- __ISB(); __DSB();
+ __ISB();
+ __DSB();
}
}
- if ( int_status & USBD_INTEN_USBRESET_Msk )
- {
+ if (int_status & USBD_INTEN_USBRESET_Msk) {
bus_reset();
dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
}
// ISOIN: Data was moved to endpoint buffer, client will be notified in SOF
- if ( int_status & USBD_INTEN_ENDISOIN_Msk )
- {
+ if (int_status & USBD_INTEN_ENDISOIN_Msk) {
xfer_td_t* xfer = get_td(EP_ISO_NUM, TUSB_DIR_IN);
xfer->actual_len = NRF_USBD->ISOIN.AMOUNT;
@@ -645,13 +590,11 @@ void dcd_int_handler(uint8_t rhport)
xfer->iso_in_transfer_ready = true;
}
- if ( int_status & USBD_INTEN_SOF_Msk )
- {
+ if (int_status & USBD_INTEN_SOF_Msk) {
bool iso_enabled = false;
// ISOOUT: Transfer data gathered in previous frame from buffer to RAM
- if (NRF_USBD->EPOUTEN & USBD_EPOUTEN_ISOOUT_Msk)
- {
+ if (NRF_USBD->EPOUTEN & USBD_EPOUTEN_ISOOUT_Msk) {
iso_enabled = true;
// Transfer from endpoint to RAM only if data is not corrupted
if ((int_status & USBD_INTEN_USBEVENT_Msk) == 0 ||
@@ -661,38 +604,39 @@ void dcd_int_handler(uint8_t rhport)
}
// ISOIN: Notify client that data was transferred
- if (NRF_USBD->EPINEN & USBD_EPINEN_ISOIN_Msk)
- {
+ if (NRF_USBD->EPINEN & USBD_EPINEN_ISOIN_Msk) {
iso_enabled = true;
xfer_td_t* xfer = get_td(EP_ISO_NUM, TUSB_DIR_IN);
- if ( xfer->iso_in_transfer_ready )
- {
+ if (xfer->iso_in_transfer_ready) {
xfer->iso_in_transfer_ready = false;
dcd_event_xfer_complete(0, EP_ISO_NUM | TUSB_DIR_IN_MASK, xfer->actual_len, XFER_RESULT_SUCCESS, true);
}
}
- if ( !iso_enabled )
- {
- // ISO endpoint is not used, SOF is only enabled one-time for remote wakeup
- // so we disable it now
- NRF_USBD->INTENCLR = USBD_INTENSET_SOF_Msk;
+ if (!iso_enabled && !_dcd.sof_enabled) {
+ // SOF interrupt not manually enabled and ISO endpoint is not used,
+ // SOF is only enabled one-time for remote wakeup so we disable it now
+
+ NRF_USBD->INTENCLR = USBD_INTENCLR_SOF_Msk;
}
- dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
+ const uint32_t frame = NRF_USBD->FRAMECNTR;
+ dcd_event_sof(0, frame, true);
+ //dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
}
- if ( int_status & USBD_INTEN_USBEVENT_Msk )
- {
- TU_LOG(3, "EVENTCAUSE = 0x%04lX\r\n", NRF_USBD->EVENTCAUSE);
+ if (int_status & USBD_INTEN_USBEVENT_Msk) {
+ TU_LOG(3, "EVENTCAUSE = 0x%04" PRIX32 "\r\n", NRF_USBD->EVENTCAUSE);
- enum { EVT_CAUSE_MASK = USBD_EVENTCAUSE_SUSPEND_Msk | USBD_EVENTCAUSE_RESUME_Msk | USBD_EVENTCAUSE_USBWUALLOWED_Msk | USBD_EVENTCAUSE_ISOOUTCRC_Msk };
+ enum {
+ EVT_CAUSE_MASK = USBD_EVENTCAUSE_SUSPEND_Msk | USBD_EVENTCAUSE_RESUME_Msk | USBD_EVENTCAUSE_USBWUALLOWED_Msk |
+ USBD_EVENTCAUSE_ISOOUTCRC_Msk
+ };
uint32_t const evt_cause = NRF_USBD->EVENTCAUSE & EVT_CAUSE_MASK;
NRF_USBD->EVENTCAUSE = evt_cause; // clear interrupt
- if ( evt_cause & USBD_EVENTCAUSE_SUSPEND_Msk )
- {
+ if (evt_cause & USBD_EVENTCAUSE_SUSPEND_Msk) {
// Put controller into low power mode
// Leave HFXO disable to application, since it may be used by other peripherals
NRF_USBD->LOWPOWER = 1;
@@ -700,8 +644,7 @@ void dcd_int_handler(uint8_t rhport)
dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
}
- if ( evt_cause & USBD_EVENTCAUSE_USBWUALLOWED_Msk )
- {
+ if (evt_cause & USBD_EVENTCAUSE_USBWUALLOWED_Msk) {
// USB is out of low power mode, and wakeup is allowed
// Initiate RESUME signal
NRF_USBD->DPDMVALUE = USBD_DPDMVALUE_STATE_Resume;
@@ -713,34 +656,29 @@ void dcd_int_handler(uint8_t rhport)
NRF_USBD->INTENSET = USBD_INTENSET_SOF_Msk;
}
- if ( evt_cause & USBD_EVENTCAUSE_RESUME_Msk )
- {
+ if (evt_cause & USBD_EVENTCAUSE_RESUME_Msk) {
dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
}
}
// Setup tokens are specific to the Control endpoint.
- if ( int_status & USBD_INTEN_EP0SETUP_Msk )
- {
- uint8_t const setup[8] =
- {
- NRF_USBD->BMREQUESTTYPE , NRF_USBD->BREQUEST, NRF_USBD->WVALUEL , NRF_USBD->WVALUEH,
- NRF_USBD->WINDEXL , NRF_USBD->WINDEXH , NRF_USBD->WLENGTHL, NRF_USBD->WLENGTHH
+ if (int_status & USBD_INTEN_EP0SETUP_Msk) {
+ uint8_t const setup[8] = {
+ NRF_USBD->BMREQUESTTYPE, NRF_USBD->BREQUEST, NRF_USBD->WVALUEL, NRF_USBD->WVALUEH,
+ NRF_USBD->WINDEXL, NRF_USBD->WINDEXH, NRF_USBD->WLENGTHL, NRF_USBD->WLENGTHH
};
// nrf5x hw auto handle set address, there is no need to inform usb stack
- tusb_control_request_t const * request = (tusb_control_request_t const *) setup;
+ tusb_control_request_t const* request = (tusb_control_request_t const*) setup;
- if ( !(TUSB_REQ_RCPT_DEVICE == request->bmRequestType_bit.recipient &&
- TUSB_REQ_TYPE_STANDARD == request->bmRequestType_bit.type &&
- TUSB_REQ_SET_ADDRESS == request->bRequest) )
- {
+ if (!(TUSB_REQ_RCPT_DEVICE == request->bmRequestType_bit.recipient &&
+ TUSB_REQ_TYPE_STANDARD == request->bmRequestType_bit.type &&
+ TUSB_REQ_SET_ADDRESS == request->bRequest)) {
dcd_event_setup_received(0, setup, true);
}
}
- if ( int_status & EDPT_END_ALL_MASK )
- {
+ if (int_status & EDPT_END_ALL_MASK) {
// DMA complete move data from SRAM <-> Endpoint
// Must before endpoint transfer handling
edpt_dma_end();
@@ -782,30 +720,24 @@ void dcd_int_handler(uint8_t rhport)
* len if Host decides to sent fewer bytes, it this case transaction is also
* complete and next transfer is not initiated here like for CBI.
*/
- for(uint8_t epnum=0; epnum<EP_CBI_COUNT+1; epnum++)
- {
- if ( tu_bit_test(int_status, USBD_INTEN_ENDEPOUT0_Pos+epnum))
- {
+ for (uint8_t epnum = 0; epnum < EP_CBI_COUNT + 1; epnum++) {
+ if (tu_bit_test(int_status, USBD_INTEN_ENDEPOUT0_Pos + epnum)) {
xfer_td_t* xfer = get_td(epnum, TUSB_DIR_OUT);
uint16_t const xact_len = NRF_USBD->EPOUT[epnum].AMOUNT;
- xfer->buffer += xact_len;
+ xfer->buffer += xact_len;
xfer->actual_len += xact_len;
// Transfer complete if transaction len < Max Packet Size or total len is transferred
- if ( (epnum != EP_ISO_NUM) && (xact_len == xfer->mps) && (xfer->actual_len < xfer->total_len) )
- {
- if ( epnum == 0 )
- {
+ if ((epnum != EP_ISO_NUM) && (xact_len == xfer->mps) && (xfer->actual_len < xfer->total_len)) {
+ if (epnum == 0) {
// Accept next Control Out packet. TASKS_EP0RCVOUT also require EasyDMA
edpt_dma_start(&NRF_USBD->TASKS_EP0RCVOUT);
- }else
- {
+ } else {
// nRF auto accept next Bulk/Interrupt OUT packet
// nothing to do
}
- }else
- {
+ } else {
TU_ASSERT(xfer->started,);
xfer->total_len = xfer->actual_len;
xfer->started = false;
@@ -819,11 +751,11 @@ void dcd_int_handler(uint8_t rhport)
}
// Endpoint <-> Host ( In & OUT )
- if ( int_status & (USBD_INTEN_EPDATA_Msk | USBD_INTEN_EP0DATADONE_Msk) )
- {
+ if (int_status & (USBD_INTEN_EPDATA_Msk | USBD_INTEN_EP0DATADONE_Msk)) {
uint32_t data_status = NRF_USBD->EPDATASTATUS;
NRF_USBD->EPDATASTATUS = data_status;
- __ISB(); __DSB();
+ __ISB();
+ __DSB();
// EP0DATADONE is set with either Control Out on IN Data
// Since EPDATASTATUS cannot be used to determine whether it is control OUT or IN.
@@ -832,22 +764,18 @@ void dcd_int_handler(uint8_t rhport)
bool const is_control_out = (int_status & USBD_INTEN_EP0DATADONE_Msk) && !(NRF_USBD->BMREQUESTTYPE & TUSB_DIR_IN_MASK);
// CBI In: Endpoint -> Host (transaction complete)
- for(uint8_t epnum=0; epnum<EP_CBI_COUNT; epnum++)
- {
- if ( tu_bit_test(data_status, epnum) || (epnum == 0 && is_control_in) )
- {
+ for (uint8_t epnum = 0; epnum < EP_CBI_COUNT; epnum++) {
+ if (tu_bit_test(data_status, epnum) || (epnum == 0 && is_control_in)) {
xfer_td_t* xfer = get_td(epnum, TUSB_DIR_IN);
uint8_t const xact_len = NRF_USBD->EPIN[epnum].AMOUNT;
- xfer->buffer += xact_len;
+ xfer->buffer += xact_len;
xfer->actual_len += xact_len;
- if ( xfer->actual_len < xfer->total_len )
- {
+ if (xfer->actual_len < xfer->total_len) {
// Start DMA to copy next data packet
xact_in_dma(epnum);
- } else
- {
+ } else {
// CBI IN complete
dcd_event_xfer_complete(0, epnum | TUSB_DIR_IN_MASK, xfer->actual_len, XFER_RESULT_SUCCESS, true);
}
@@ -855,17 +783,13 @@ void dcd_int_handler(uint8_t rhport)
}
// CBI OUT: Host -> Endpoint
- for(uint8_t epnum=0; epnum<EP_CBI_COUNT; epnum++)
- {
- if ( tu_bit_test(data_status, 16+epnum) || (epnum == 0 && is_control_out) )
- {
+ for (uint8_t epnum = 0; epnum < EP_CBI_COUNT; epnum++) {
+ if (tu_bit_test(data_status, 16 + epnum) || (epnum == 0 && is_control_out)) {
xfer_td_t* xfer = get_td(epnum, TUSB_DIR_OUT);
- if ( xfer->started && xfer->actual_len < xfer->total_len )
- {
+ if (xfer->started && xfer->actual_len < xfer->total_len) {
xact_out_dma(epnum);
- }else
- {
+ } else {
// Data overflow !!! Nah, nRF will auto accept next Bulk/Interrupt OUT packet
// Mark this endpoint with data received
xfer->data_received = true;
@@ -889,76 +813,80 @@ void dcd_int_handler(uint8_t rhport)
#define SD_MAGIC_NUMBER 0x51B1E5DB
#endif
-static inline bool is_sd_existed(void)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool is_sd_existed(void) {
return *((uint32_t*)(SOFTDEVICE_INFO_STRUCT_ADDRESS+4)) == SD_MAGIC_NUMBER;
}
// check if SD is existed and enabled
-static inline bool is_sd_enabled(void)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool is_sd_enabled(void) {
if ( !is_sd_existed() ) return false;
-
uint8_t sd_en = false;
(void) sd_softdevice_is_enabled(&sd_en);
return sd_en;
}
#endif
-static bool hfclk_running(void)
-{
+static bool hfclk_running(void) {
#ifdef SOFTDEVICE_PRESENT
- if ( is_sd_enabled() )
- {
+ if ( is_sd_enabled() ) {
uint32_t is_running = 0;
(void) sd_clock_hfclk_is_running(&is_running);
return (is_running ? true : false);
}
#endif
+#if CFG_TUD_NRF_NRFX_VERSION == 1
+ return nrf_clock_hf_is_running(NRF_CLOCK_HFCLK_HIGH_ACCURACY);
+#else
return nrf_clock_hf_is_running(NRF_CLOCK, NRF_CLOCK_HFCLK_HIGH_ACCURACY);
+#endif
}
-static void hfclk_enable(void)
-{
+static void hfclk_enable(void) {
#if CFG_TUSB_OS == OPT_OS_MYNEWT
usb_clock_request();
return;
#else
// already running, nothing to do
- if ( hfclk_running() ) return;
+ if (hfclk_running()) return;
#ifdef SOFTDEVICE_PRESENT
- if ( is_sd_enabled() )
- {
+ if ( is_sd_enabled() ) {
(void)sd_clock_hfclk_request();
return;
}
#endif
+#if CFG_TUD_NRF_NRFX_VERSION == 1
+ nrf_clock_event_clear(NRF_CLOCK_EVENT_HFCLKSTARTED);
+ nrf_clock_task_trigger(NRF_CLOCK_TASK_HFCLKSTART);
+#else
nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_HFCLKSTARTED);
nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_HFCLKSTART);
#endif
+#endif
}
-static void hfclk_disable(void)
-{
+static void hfclk_disable(void) {
#if CFG_TUSB_OS == OPT_OS_MYNEWT
usb_clock_release();
return;
#else
#ifdef SOFTDEVICE_PRESENT
- if ( is_sd_enabled() )
- {
+ if ( is_sd_enabled() ) {
(void)sd_clock_hfclk_release();
return;
}
#endif
+#if CFG_TUD_NRF_NRFX_VERSION == 1
+ nrf_clock_task_trigger(NRF_CLOCK_TASK_HFCLKSTOP);
+#else
nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_HFCLKSTOP);
#endif
+#endif
}
// Power & Clock Peripheral on nRF5x to manage USB
@@ -971,8 +899,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 {
USB_EVT_DETECTED = 0,
@@ -980,51 +907,42 @@ void tusb_hal_nrf_power_event (uint32_t event)
USB_EVT_READY = 2
};
-#if CFG_TUSB_DEBUG >= 2
- const char* const power_evt_str[] = { "Detected", "Removed", "Ready" };
- TU_LOG(2, "Power USB event: %s\r\n", power_evt_str[event]);
+#if CFG_TUSB_DEBUG >= 3
+ const char* const power_evt_str[] = {"Detected", "Removed", "Ready"};
+ TU_LOG(3, "Power USB event: %s\r\n", power_evt_str[event]);
#endif
- switch ( event )
- {
+ switch (event) {
case USB_EVT_DETECTED:
- if ( !NRF_USBD->ENABLE )
- {
+ if (!NRF_USBD->ENABLE) {
// Prepare for receiving READY event: disable interrupt since we will blocking wait
NRF_USBD->INTENCLR = USBD_INTEN_USBEVENT_Msk;
NRF_USBD->EVENTCAUSE = USBD_EVENTCAUSE_READY_Msk;
- __ISB(); __DSB(); // for sync
+ __ISB();
+ __DSB(); // for sync
#ifdef NRF52_SERIES // NRF53 does not need this errata
// ERRATA 171, 187, 166
- if ( nrfx_usbd_errata_187() )
- {
+ if (nrfx_usbd_errata_187()) {
// CRITICAL_REGION_ENTER();
- if ( *((volatile uint32_t *) (0x4006EC00)) == 0x00000000 )
- {
- *((volatile uint32_t *) (0x4006EC00)) = 0x00009375;
- *((volatile uint32_t *) (0x4006ED14)) = 0x00000003;
- *((volatile uint32_t *) (0x4006EC00)) = 0x00009375;
- }
- else
- {
- *((volatile uint32_t *) (0x4006ED14)) = 0x00000003;
+ if (*((volatile uint32_t*) (0x4006EC00)) == 0x00000000) {
+ *((volatile uint32_t*) (0x4006EC00)) = 0x00009375;
+ *((volatile uint32_t*) (0x4006ED14)) = 0x00000003;
+ *((volatile uint32_t*) (0x4006EC00)) = 0x00009375;
+ } else {
+ *((volatile uint32_t*) (0x4006ED14)) = 0x00000003;
}
// CRITICAL_REGION_EXIT();
}
- if ( nrfx_usbd_errata_171() )
- {
+ if (nrfx_usbd_errata_171()) {
// CRITICAL_REGION_ENTER();
- if ( *((volatile uint32_t *) (0x4006EC00)) == 0x00000000 )
- {
- *((volatile uint32_t *) (0x4006EC00)) = 0x00009375;
- *((volatile uint32_t *) (0x4006EC14)) = 0x000000C0;
- *((volatile uint32_t *) (0x4006EC00)) = 0x00009375;
- }
- else
- {
- *((volatile uint32_t *) (0x4006EC14)) = 0x000000C0;
+ if (*((volatile uint32_t*) (0x4006EC00)) == 0x00000000) {
+ *((volatile uint32_t*) (0x4006EC00)) = 0x00009375;
+ *((volatile uint32_t*) (0x4006EC14)) = 0x000000C0;
+ *((volatile uint32_t*) (0x4006EC00)) = 0x00009375;
+ } else {
+ *((volatile uint32_t*) (0x4006EC14)) = 0x000000C0;
}
// CRITICAL_REGION_EXIT();
}
@@ -1032,64 +950,58 @@ void tusb_hal_nrf_power_event (uint32_t event)
// Enable the peripheral (will cause Ready event)
NRF_USBD->ENABLE = 1;
- __ISB(); __DSB(); // for sync
+ __ISB();
+ __DSB(); // for sync
// Enable HFCLK
hfclk_enable();
}
- break;
+ break;
case USB_EVT_READY:
// Skip if pull-up is enabled and HCLK is already running.
// Application probably call this more than necessary.
- if ( NRF_USBD->USBPULLUP && hfclk_running() ) break;
+ if (NRF_USBD->USBPULLUP && hfclk_running()) break;
// Waiting for USBD peripheral enabled
- while ( !(USBD_EVENTCAUSE_READY_Msk & NRF_USBD->EVENTCAUSE) ) { }
+ while (!(USBD_EVENTCAUSE_READY_Msk & NRF_USBD->EVENTCAUSE)) {}
NRF_USBD->EVENTCAUSE = USBD_EVENTCAUSE_READY_Msk;
- __ISB(); __DSB(); // for sync
+ __ISB();
+ __DSB(); // for sync
#ifdef NRF52_SERIES
- if ( nrfx_usbd_errata_171() )
- {
+ if (nrfx_usbd_errata_171()) {
// CRITICAL_REGION_ENTER();
- if ( *((volatile uint32_t *) (0x4006EC00)) == 0x00000000 )
- {
- *((volatile uint32_t *) (0x4006EC00)) = 0x00009375;
- *((volatile uint32_t *) (0x4006EC14)) = 0x00000000;
- *((volatile uint32_t *) (0x4006EC00)) = 0x00009375;
- }
- else
- {
- *((volatile uint32_t *) (0x4006EC14)) = 0x00000000;
+ if (*((volatile uint32_t*) (0x4006EC00)) == 0x00000000) {
+ *((volatile uint32_t*) (0x4006EC00)) = 0x00009375;
+ *((volatile uint32_t*) (0x4006EC14)) = 0x00000000;
+ *((volatile uint32_t*) (0x4006EC00)) = 0x00009375;
+ } else {
+ *((volatile uint32_t*) (0x4006EC14)) = 0x00000000;
}
// CRITICAL_REGION_EXIT();
}
- if ( nrfx_usbd_errata_187() )
- {
+ if (nrfx_usbd_errata_187()) {
// CRITICAL_REGION_ENTER();
- if ( *((volatile uint32_t *) (0x4006EC00)) == 0x00000000 )
- {
- *((volatile uint32_t *) (0x4006EC00)) = 0x00009375;
- *((volatile uint32_t *) (0x4006ED14)) = 0x00000000;
- *((volatile uint32_t *) (0x4006EC00)) = 0x00009375;
- }
- else
- {
- *((volatile uint32_t *) (0x4006ED14)) = 0x00000000;
+ if (*((volatile uint32_t*) (0x4006EC00)) == 0x00000000) {
+ *((volatile uint32_t*) (0x4006EC00)) = 0x00009375;
+ *((volatile uint32_t*) (0x4006ED14)) = 0x00000000;
+ *((volatile uint32_t*) (0x4006EC00)) = 0x00009375;
+ } else {
+ *((volatile uint32_t*) (0x4006ED14)) = 0x00000000;
}
// CRITICAL_REGION_EXIT();
}
- if ( nrfx_usbd_errata_166() )
- {
- *((volatile uint32_t *) (NRF_USBD_BASE + 0x800)) = 0x7E3;
- *((volatile uint32_t *) (NRF_USBD_BASE + 0x804)) = 0x40;
+ if (nrfx_usbd_errata_166()) {
+ *((volatile uint32_t*) (NRF_USBD_BASE + 0x800)) = 0x7E3;
+ *((volatile uint32_t*) (NRF_USBD_BASE + 0x804)) = 0x40;
- __ISB(); __DSB();
+ __ISB();
+ __DSB();
}
#endif
@@ -1101,30 +1013,31 @@ void tusb_hal_nrf_power_event (uint32_t event)
// Enable interrupt, priorities should be set by application
NVIC_ClearPendingIRQ(USBD_IRQn);
+
// Don't enable USBD interrupt yet, if dcd_init() did not finish yet
// Interrupt will be enabled by tud_init(), when USB stack is ready
// to handle interrupts.
- if (tud_inited())
- {
+ if (tud_inited()) {
NVIC_EnableIRQ(USBD_IRQn);
}
// Wait for HFCLK
- while ( !hfclk_running() ) { }
+ while (!hfclk_running()) {}
// Enable pull up
NRF_USBD->USBPULLUP = 1;
- __ISB(); __DSB(); // for sync
- break;
+ __ISB();
+ __DSB(); // for sync
+ break;
case USB_EVT_REMOVED:
- if ( NRF_USBD->ENABLE )
- {
+ if (NRF_USBD->ENABLE) {
// Abort all transfers
// Disable pull up
NRF_USBD->USBPULLUP = 0;
- __ISB(); __DSB(); // for sync
+ __ISB();
+ __DSB(); // for sync
// Disable Interrupt
NVIC_DisableIRQ(USBD_IRQn);
@@ -1133,15 +1046,17 @@ void tusb_hal_nrf_power_event (uint32_t event)
NRF_USBD->INTENCLR = NRF_USBD->INTEN;
NRF_USBD->ENABLE = 0;
- __ISB(); __DSB(); // for sync
+ __ISB();
+ __DSB(); // for sync
hfclk_disable();
dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, is_in_isr());
}
- break;
+ break;
- default: break;
+ default:
+ break;
}
}
diff --git a/src/portable/nxp/khci/dcd_khci.c b/src/portable/nxp/khci/dcd_khci.c
index dc71117b3..ef61146aa 100644
--- a/src/portable/nxp/khci/dcd_khci.c
+++ b/src/portable/nxp/khci/dcd_khci.c
@@ -540,7 +540,7 @@ void dcd_int_handler(uint8_t rhport)
}
if (is & USB_ISTAT_SLEEP_MASK) {
- // TU_LOG2("Suspend: "); TU_LOG2_HEX(is);
+ // TU_LOG3("Suspend: "); TU_LOG2_HEX(is);
// Note Host usually has extra delay after bus reset (without SOF), which could falsely
// detected as Sleep event. Though usbd has debouncing logic so we are good
diff --git a/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c b/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c
index f4ed09d83..cc18cf59b 100644
--- a/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c
+++ b/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c
@@ -42,7 +42,18 @@
#if TU_CHECK_MCU(OPT_MCU_LPC11UXX, OPT_MCU_LPC13XX, OPT_MCU_LPC15XX)
// LPCOpen
+ #ifdef __GNUC__
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wunused-parameter"
+ #pragma GCC diagnostic ignored "-Wstrict-prototypes"
+ #endif
+
#include "chip.h"
+
+ #ifdef __GNUC__
+ #pragma GCC diagnostic pop
+ #endif
+
#else
// SDK
#include "fsl_device_registers.h"
@@ -239,7 +250,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool ep_is_iso(ep_cmd_sts_t* ep_cs, bool is_
return is_highspeed ? (ep_cs[0].cmd_sts.type && !ep_cs[0].cmd_sts.rf_tv) : ep_cs->cmd_sts.type;
}
-TU_ATTR_ALWAYS_INLINE static inline bool ep_is_bulk(ep_cmd_sts_t* ep_cs) {
+TU_ATTR_ALWAYS_INLINE TU_ATTR_UNUSED static inline bool ep_is_bulk(ep_cmd_sts_t* ep_cs) {
return (ep_cs[0].cmd_sts.type == 0) && (ep_cs[0].cmd_sts.rf_tv == 0);
}
diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c
index 1ca711c77..43f48da39 100644
--- a/src/portable/raspberrypi/rp2040/rp2040_usb.c
+++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c
@@ -53,6 +53,14 @@ TU_ATTR_ALWAYS_INLINE static inline bool is_host_mode(void) {
//--------------------------------------------------------------------+
// Implementation
//--------------------------------------------------------------------+
+// Provide own byte by byte memcpy as not all copies are aligned
+static void unaligned_memcpy(void *dst, const void *src, size_t n) {
+ uint8_t *dst_byte = (uint8_t*)dst;
+ const uint8_t *src_byte = (const uint8_t*)src;
+ while (n--) {
+ *dst_byte++ = *src_byte++;
+ }
+}
void rp2040_usb_init(void) {
// Reset usb controller
@@ -67,7 +75,6 @@ void rp2040_usb_init(void) {
#pragma GCC diagnostic ignored "-Wstringop-overflow"
#endif
#endif
- memset(usb_hw, 0, sizeof(*usb_hw));
memset(usb_dpram, 0, sizeof(*usb_dpram));
#ifdef __GNUC__
#pragma GCC diagnostic pop
@@ -125,7 +132,7 @@ static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint* ep,
if (!ep->rx) {
// Copy data from user buffer to hw buffer
- memcpy(ep->hw_data_buf + buf_id * 64, ep->user_buf, buflen);
+ unaligned_memcpy(ep->hw_data_buf + buf_id * 64, ep->user_buf, buflen);
ep->user_buf += buflen;
// Mark as full
@@ -230,7 +237,7 @@ static uint16_t __tusb_irq_path_func(sync_ep_buffer)(struct hw_endpoint* ep, uin
// we have received AFTER we have copied it to the user buffer at the appropriate offset
assert(buf_ctrl & USB_BUF_CTRL_FULL);
- memcpy(ep->user_buf, ep->hw_data_buf + buf_id * 64, xferred_bytes);
+ unaligned_memcpy(ep->user_buf, ep->hw_data_buf + buf_id * 64, xferred_bytes);
ep->xferred_len = (uint16_t) (ep->xferred_len + xferred_bytes);
ep->user_buf += xferred_bytes;
}
diff --git a/src/portable/renesas/rusb2/dcd_rusb2.c b/src/portable/renesas/rusb2/dcd_rusb2.c
index 24edc30e7..7c6044ca0 100644
--- a/src/portable/renesas/rusb2/dcd_rusb2.c
+++ b/src/portable/renesas/rusb2/dcd_rusb2.c
@@ -29,10 +29,6 @@
#if CFG_TUD_ENABLED && defined(TUP_USBIP_RUSB2)
-// Since TinyUSB doesn't use SOF for now, and this interrupt too often (1ms interval)
-// We disable SOF for now until needed later on
-#define USE_SOF 0
-
#include "device/dcd.h"
#include "rusb2_type.h"
@@ -57,30 +53,25 @@
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM
//--------------------------------------------------------------------+
+enum {
+ PIPE_COUNT = 10,
+};
-/* Start of definition of packed structs (used by the CCRX toolchain) */
-TU_ATTR_PACKED_BEGIN
-TU_ATTR_BIT_FIELD_ORDER_BEGIN
-
-typedef struct TU_ATTR_PACKED
-{
+typedef struct {
void *buf; /* the start address of a transfer data buffer */
uint16_t length; /* the number of bytes in the buffer */
uint16_t remaining; /* the number of bytes remaining in the buffer */
- struct {
- uint32_t ep : 8; /* an assigned endpoint address */
- uint32_t ff : 1; /* `buf` is TU_FUFO or POD */
- uint32_t : 0;
- };
-} pipe_state_t;
-TU_ATTR_PACKED_END // End of definition of packed structs (used by the CCRX toolchain)
-TU_ATTR_BIT_FIELD_ORDER_END
+ uint8_t ep; /* an assigned endpoint address */
+ uint8_t ff; /* `buf` is TU_FUFO or POD */
+} pipe_state_t;
typedef struct
{
- pipe_state_t pipe[10];
+ pipe_state_t pipe[PIPE_COUNT];
uint8_t ep[2][16]; /* a lookup table for a pipe index from an endpoint address */
+ // Track whether sof has been manually enabled
+ bool sof_enabled;
} dcd_data_t;
static dcd_data_t _dcd;
@@ -89,52 +80,44 @@ static dcd_data_t _dcd;
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
-// Transfer conditions specifiable for each pipe:
-// - Pipe 0: Control transfer with 64-byte single buffer
-// - Pipes 1 and 2: Bulk isochronous transfer continuous transfer mode with programmable buffer size up
-// to 2 KB and optional double buffer
-// - Pipes 3 to 5: Bulk transfer continuous transfer mode with programmable buffer size up to 2 KB and
-// optional double buffer
-// - Pipes 6 to 9: Interrupt transfer with 64-byte single buffer
-enum {
- PIPE_1ST_BULK = 3,
- PIPE_1ST_INTERRUPT = 6,
- PIPE_COUNT = 10,
-};
-
-static unsigned find_pipe(unsigned xfer)
-{
- switch (xfer) {
- case TUSB_XFER_ISOCHRONOUS:
- for (int i = 1; i < PIPE_1ST_BULK; ++i) {
- if (0 == _dcd.pipe[i].ep) return i;
- }
- break;
- case TUSB_XFER_BULK:
- for (int i = PIPE_1ST_BULK; i < PIPE_1ST_INTERRUPT; ++i) {
- if (0 == _dcd.pipe[i].ep) return i;
- }
- for (int i = 1; i < PIPE_1ST_BULK; ++i) {
- if (0 == _dcd.pipe[i].ep) return i;
- }
- break;
+// Transfer conditions specifiable for each pipe for most MCUs
+// - Pipe 0: Control transfer with 64-byte single buffer
+// - Pipes 1 and 2: Bulk or ISO
+// - Pipes 3 to 5: Bulk
+// - Pipes 6 to 9: Interrupt
+//
+// Note: for small mcu such as
+// - RA2A1: only pipe 4-7 are available, and no support for ISO
+static unsigned find_pipe(unsigned xfer_type) {
+ #if defined(BSP_MCU_GROUP_RA2A1)
+ const uint8_t pipe_idx_arr[4][2] = {
+ { 0, 0 }, // Control
+ { 0, 0 }, // Isochronous not supported
+ { 4, 5 }, // Bulk
+ { 6, 7 }, // Interrupt
+ };
+ #else
+ const uint8_t pipe_idx_arr[4][2] = {
+ { 0, 0 }, // Control
+ { 1, 2 }, // Isochronous
+ { 1, 5 }, // Bulk
+ { 6, 9 }, // Interrupt
+ };
+ #endif
- case TUSB_XFER_INTERRUPT:
- for (int i = PIPE_1ST_INTERRUPT; i < PIPE_COUNT; ++i) {
- if (0 == _dcd.pipe[i].ep) return i;
- }
- break;
+ // find backward since only pipe 1, 2 support ISO
+ const uint8_t idx_first = pipe_idx_arr[xfer_type][0];
+ const uint8_t idx_last = pipe_idx_arr[xfer_type][1];
- default:
- /* No support for control transfer */
- break;
+ for (int i = idx_last; i >= idx_first; i--) {
+ if (0 == _dcd.pipe[i].ep) return i;
}
+
return 0;
}
-static volatile uint16_t* get_pipectr(rusb2_reg_t *rusb, unsigned num)
-{
+static volatile uint16_t* get_pipectr(rusb2_reg_t *rusb, unsigned num) {
if (num) {
return (volatile uint16_t*)&(rusb->PIPE_CTR[num - 1]);
} else {
@@ -142,8 +125,7 @@ static volatile uint16_t* get_pipectr(rusb2_reg_t *rusb, unsigned num)
}
}
-static volatile reg_pipetre_t* get_pipetre(rusb2_reg_t *rusb, unsigned num)
-{
+static volatile reg_pipetre_t* get_pipetre(rusb2_reg_t *rusb, unsigned num) {
volatile reg_pipetre_t* tre = NULL;
if ((1 <= num) && (num <= 5)) {
tre = (volatile reg_pipetre_t*)&(rusb->PIPE_TR[num - 1].E);
@@ -151,8 +133,7 @@ static volatile reg_pipetre_t* get_pipetre(rusb2_reg_t *rusb, unsigned num)
return tre;
}
-static volatile uint16_t* ep_addr_to_pipectr(uint8_t rhport, unsigned ep_addr)
-{
+static volatile uint16_t* ep_addr_to_pipectr(uint8_t rhport, unsigned ep_addr) {
rusb2_reg_t *rusb = RUSB2_REG(rhport);
const unsigned epn = tu_edpt_number(ep_addr);
@@ -165,19 +146,16 @@ static volatile uint16_t* ep_addr_to_pipectr(uint8_t rhport, unsigned ep_addr)
}
}
-static uint16_t edpt0_max_packet_size(rusb2_reg_t* rusb)
-{
+static uint16_t edpt0_max_packet_size(rusb2_reg_t* rusb) {
return rusb->DCPMAXP_b.MXPS;
}
-static uint16_t edpt_max_packet_size(rusb2_reg_t *rusb, unsigned num)
-{
+static uint16_t edpt_max_packet_size(rusb2_reg_t *rusb, unsigned num) {
rusb->PIPESEL = num;
return rusb->PIPEMAXP;
}
-static inline void pipe_wait_for_ready(rusb2_reg_t * rusb, unsigned num)
-{
+static inline void pipe_wait_for_ready(rusb2_reg_t * rusb, unsigned num) {
while ( rusb->D0FIFOSEL_b.CURPIPE != num ) {}
while ( !rusb->D0FIFOCTR_b.FRDY ) {}
}
@@ -266,14 +244,6 @@ static void pipe_read_packet_ff(rusb2_reg_t * rusb, tu_fifo_t *f, volatile void
tu_fifo_advance_write_pointer(f, count);
}
-
-static bool wait_pipe_fifo_empty(rusb2_reg_t* rusb, uint8_t num) {
- TU_ASSERT(num);
- while( (rusb->PIPE_CTR[num-1] & RUSB2_PIPE_CTR_INBUFM_Msk) > 0 ) {}
- return true;
-}
-
-
//--------------------------------------------------------------------+
// Pipe Transfer
//--------------------------------------------------------------------+
@@ -347,7 +317,6 @@ static bool pipe_xfer_in(rusb2_reg_t* rusb, unsigned num)
const unsigned rem = pipe->remaining;
if (!rem) {
- wait_pipe_fifo_empty(rusb, num);
pipe->buf = NULL;
return true;
}
@@ -693,6 +662,10 @@ void dcd_init(uint8_t rhport)
rusb2_reg_t* rusb = RUSB2_REG(rhport);
rusb2_module_start(rhport, true);
+// We disable SOF for now until needed later on.
+// Since TinyUSB doesn't use SOF for now, and this interrupt often (1ms interval)
+_dcd.sof_enabled = false;
+
#ifdef RUSB2_SUPPORT_HIGHSPEED
if ( rusb2_is_highspeed_rhport(rhport) ) {
rusb->SYSCFG_b.HSE = 1;
@@ -737,7 +710,7 @@ void dcd_init(uint8_t rhport)
rusb->INTSTS0 = 0;
rusb->INTENB0 = RUSB2_INTSTS0_VBINT_Msk | RUSB2_INTSTS0_BRDY_Msk | RUSB2_INTSTS0_BEMP_Msk |
- RUSB2_INTSTS0_DVST_Msk | RUSB2_INTSTS0_CTRT_Msk | (USE_SOF ? RUSB2_INTSTS0_SOFR_Msk : 0) |
+ RUSB2_INTSTS0_DVST_Msk | RUSB2_INTSTS0_CTRT_Msk | (_dcd.sof_enabled ? RUSB2_INTSTS0_SOFR_Msk : 0) |
RUSB2_INTSTS0_RESM_Msk;
rusb->BEMPENB = 1;
rusb->BRDYENB = 1;
@@ -785,10 +758,9 @@ void dcd_disconnect(uint8_t rhport)
void dcd_sof_enable(uint8_t rhport, bool en)
{
- (void) rhport;
- (void) en;
-
- // TODO implement later
+ rusb2_reg_t* rusb = RUSB2_REG(rhport);
+ _dcd.sof_enabled = en;
+ rusb->INTENB0_b.SOFE = en ? 1: 0;
}
//--------------------------------------------------------------------+
@@ -844,7 +816,7 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
}
rusb->PIPECFG = cfg;
- rusb->BRDYSTS = 0x1FFu ^ TU_BIT(num);
+ rusb->BRDYSTS = 0x3FFu ^ TU_BIT(num);
rusb->BRDYENB |= TU_BIT(num);
if (dir || (xfer != TUSB_XFER_BULK)) {
@@ -944,6 +916,18 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
//--------------------------------------------------------------------+
// ISR
//--------------------------------------------------------------------+
+
+#if defined(__CCRX__)
+TU_ATTR_ALWAYS_INLINE static inline unsigned __builtin_ctz(unsigned int value) {
+ unsigned int count = 0;
+ while ((value & 1) == 0) {
+ value >>= 1;
+ count++;
+ }
+ return count;
+}
+#endif
+
void dcd_int_handler(uint8_t rhport)
{
rusb2_reg_t* rusb = RUSB2_REG(rhport);
@@ -966,18 +950,19 @@ void dcd_int_handler(uint8_t rhport)
// Resumed
if ( is0 & RUSB2_INTSTS0_RESM_Msk ) {
dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
-#if (0 == USE_SOF)
- rusb->INTENB0_b.SOFE = 0;
-#endif
+ if (!_dcd.sof_enabled) {
+ rusb->INTENB0_b.SOFE = 0;
+ }
}
// SOF received
if ( (is0 & RUSB2_INTSTS0_SOFR_Msk) && rusb->INTENB0_b.SOFE ) {
// USBD will exit suspended mode when SOF event is received
- dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
-#if (0 == USE_SOF)
- rusb->INTENB0_b.SOFE = 0;
-#endif
+ const uint32_t frame = rusb->FRMNUM_b.FRNM;
+ dcd_event_sof(rhport, frame, true);
+ if (!_dcd.sof_enabled) {
+ rusb->INTENB0_b.SOFE = 0;
+ }
}
// Device state changes
@@ -996,9 +981,9 @@ void dcd_int_handler(uint8_t rhport)
case RUSB2_INTSTS0_DVSQ_STATE_SUSP2:
case RUSB2_INTSTS0_DVSQ_STATE_SUSP3:
dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
-#if (0 == USE_SOF)
- rusb->INTENB0_b.SOFE = 1;
-#endif
+ if (!_dcd.sof_enabled) {
+ rusb->INTENB0_b.SOFE = 1;
+ }
default: break;
}
@@ -1035,17 +1020,7 @@ void dcd_int_handler(uint8_t rhport)
/* clear active bits (don't write 0 to already cleared bits according to the HW manual) */
rusb->BRDYSTS = ~s;
while (s) {
-#if defined(__CCRX__)
- static const int Mod37BitPosition[] = {
- -1, 0, 1, 26, 2, 23, 27, 0, 3, 16, 24, 30, 28, 11, 0, 13, 4,
- 7, 17, 0, 25, 22, 31, 15, 29, 10, 12, 6, 0, 21, 14, 9, 5,
- 20, 8, 19, 18
- };
-
- const unsigned num = Mod37BitPosition[(-s & s) % 37];
-#else
const unsigned num = __builtin_ctz(s);
-#endif
process_pipe_brdy(rhport, num);
s &= ~TU_BIT(num);
}
diff --git a/src/portable/renesas/rusb2/hcd_rusb2.c b/src/portable/renesas/rusb2/hcd_rusb2.c
index bf95be707..f140da690 100644
--- a/src/portable/renesas/rusb2/hcd_rusb2.c
+++ b/src/portable/renesas/rusb2/hcd_rusb2.c
@@ -45,6 +45,9 @@
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
//--------------------------------------------------------------------+
+enum {
+ PIPE_COUNT = 10,
+};
TU_ATTR_PACKED_BEGIN
TU_ATTR_BIT_FIELD_ORDER_BEGIN
@@ -75,7 +78,7 @@ TU_ATTR_BIT_FIELD_ORDER_END
typedef struct
{
bool need_reset; /* The device has not been reset after connection. */
- pipe_state_t pipe[10];
+ pipe_state_t pipe[PIPE_COUNT];
uint8_t ep[4][2][15]; /* a lookup table for a pipe index from an endpoint address */
uint8_t ctl_mps[5]; /* EP0 max packet size for each device */
} hcd_data_t;
@@ -86,46 +89,30 @@ typedef struct
static hcd_data_t _hcd;
// TODO merged with DCD
-// Transfer conditions specifiable for each pipe:
+// Transfer conditions specifiable for each pipe for most MCUs
// - Pipe 0: Control transfer with 64-byte single buffer
-// - Pipes 1 and 2: Bulk isochronous transfer continuous transfer mode with programmable buffer size up
-// to 2 KB and optional double buffer
-// - Pipes 3 to 5: Bulk transfer continuous transfer mode with programmable buffer size up to 2 KB and
-// optional double buffer
-// - Pipes 6 to 9: Interrupt transfer with 64-byte single buffer
-enum {
- PIPE_1ST_BULK = 3,
- PIPE_1ST_INTERRUPT = 6,
- PIPE_COUNT = 10,
-};
-
-static unsigned find_pipe(unsigned xfer) {
- switch ( xfer ) {
- case TUSB_XFER_ISOCHRONOUS:
- for (int i = 1; i < PIPE_1ST_BULK; ++i) {
- if ( 0 == _hcd.pipe[i].ep ) return i;
- }
- break;
-
- case TUSB_XFER_BULK:
- for (int i = PIPE_1ST_BULK; i < PIPE_1ST_INTERRUPT; ++i) {
- if ( 0 == _hcd.pipe[i].ep ) return i;
- }
- for (int i = 1; i < PIPE_1ST_BULK; ++i) {
- if ( 0 == _hcd.pipe[i].ep ) return i;
- }
- break;
+// - Pipes 1 and 2: Bulk or ISO
+// - Pipes 3 to 5: Bulk
+// - Pipes 6 to 9: Interrupt
+//
+// Note: for small mcu such as
+// - RA2A1: only pipe 4-7 are available, and no support for ISO
+static unsigned find_pipe(unsigned xfer_type) {
+ const uint8_t pipe_idx_arr[4][2] = {
+ { 0, 0 }, // Control
+ { 1, 2 }, // Isochronous
+ { 1, 5 }, // Bulk
+ { 6, 9 }, // Interrupt
+ };
- case TUSB_XFER_INTERRUPT:
- for (int i = PIPE_1ST_INTERRUPT; i < PIPE_COUNT; ++i) {
- if ( 0 == _hcd.pipe[i].ep ) return i;
- }
- break;
+ // find backward since only pipe 1, 2 support ISO
+ const uint8_t idx_first = pipe_idx_arr[xfer_type][0];
+ const uint8_t idx_last = pipe_idx_arr[xfer_type][1];
- default:
- /* No support for control transfer */
- break;
+ for (int i = idx_last; i >= idx_first; i--) {
+ if (0 == _hcd.pipe[i].ep) return i;
}
+
return 0;
}
@@ -718,7 +705,7 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const
}
rusb->PIPECFG = cfg;
- rusb->BRDYSTS = 0x1FFu ^ TU_BIT(num);
+ rusb->BRDYSTS = 0x3FFu ^ TU_BIT(num);
rusb->NRDYENB |= TU_BIT(num);
rusb->BRDYENB |= TU_BIT(num);
@@ -771,6 +758,17 @@ bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
//--------------------------------------------------------------------+
// ISR
//--------------------------------------------------------------------+
+#if defined(__CCRX__)
+TU_ATTR_ALWAYS_INLINE static inline unsigned __builtin_ctz(unsigned int value) {
+ unsigned int count = 0;
+ while ((value & 1) == 0) {
+ value >>= 1;
+ count++;
+ }
+ return count;
+}
+#endif
+
void hcd_int_handler(uint8_t rhport, bool in_isr) {
(void) in_isr;
@@ -820,23 +818,12 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) {
}
}
-#if defined(__CCRX__)
- static const int Mod37BitPosition[] = {
- -1, 0, 1, 26, 2, 23, 27, 0, 3, 16, 24, 30, 28, 11, 0, 13, 4,
- 7, 17, 0, 25, 22, 31, 15, 29, 10, 12, 6, 0, 21, 14, 9, 5,
- 20, 8, 19, 18};
-#endif
-
if (is0 & RUSB2_INTSTS0_NRDY_Msk) {
const unsigned m = rusb->NRDYENB;
unsigned s = rusb->NRDYSTS & m;
rusb->NRDYSTS = ~s;
while (s) {
-#if defined(__CCRX__)
- const unsigned num = Mod37BitPosition[(-s & s) % 37];
-#else
const unsigned num = __builtin_ctz(s);
-#endif
process_pipe_nrdy(rhport, num);
s &= ~TU_BIT(num);
}
@@ -847,11 +834,7 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) {
/* clear active bits (don't write 0 to already cleared bits according to the HW manual) */
rusb->BRDYSTS = ~s;
while (s) {
-#if defined(__CCRX__)
- const unsigned num = Mod37BitPosition[(-s & s) % 37];
-#else
const unsigned num = __builtin_ctz(s);
-#endif
process_pipe_brdy(rhport, num);
s &= ~TU_BIT(num);
}
diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
index 7bf726f3f..cff328418 100644
--- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
+++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
@@ -37,14 +37,20 @@
* It also should work with minimal changes for any ST MCU with an "USB A"/"PCD"/"HCD" peripheral. This
* covers:
*
- * F04x, F072, F078, 070x6/B 1024 byte buffer
+ * F04x, F072, F078, F070x6/B 1024 byte buffer
* 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
+ * G0 2048 byte buffer; 32-bit bus; host mode
+ * G4 1024 byte buffer
+ * H5 2048 byte buffer; 32-bit bus; host mode
* L0x2, L0x3 1024 byte buffer
* L1 512 byte buffer
* L4x2, L4x3 1024 byte buffer
- * G0 2048 byte buffer
+ * L5 1024 byte buffer
+ * U0 1024 byte buffer; 32-bit bus
+ * U535, U545 2048 byte buffer; 32-bit bus; host mode
+ * WB35, WB55 1024 byte buffer
*
* To use this driver, you must:
* - If you are using a device with crystal-less USB, set up the clock recovery system (CRS)
@@ -102,74 +108,53 @@
#include "tusb_option.h"
-#if CFG_TUD_ENABLED && defined(TUP_USBIP_FSDEV)
+#if CFG_TUD_ENABLED && defined(TUP_USBIP_FSDEV) && \
+ !(defined(TUP_USBIP_FSDEV_CH32) && CFG_TUD_WCH_USBIP_FSDEV == 0)
#include "device/dcd.h"
-#ifdef TUP_USBIP_FSDEV_STM32
+#if defined(TUP_USBIP_FSDEV_STM32)
// Undefine to reduce the dependence on HAL
#undef USE_HAL_DRIVER
- #include "portable/st/stm32_fsdev/dcd_stm32_fsdev.h"
-#endif
-
-/*****************************************************
- * Configuration
- *****************************************************/
-
-// HW supports max of 8 bidirectional endpoints, but this can be reduced to save RAM
-// (8u here would mean 8 IN and 8 OUT)
-#ifndef MAX_EP_COUNT
-# define MAX_EP_COUNT 8U
+ #include "fsdev_stm32.h"
+#elif defined(TUP_USBIP_FSDEV_CH32)
+ #include "fsdev_ch32.h"
+#else
+ #error "Unknown USB IP"
#endif
-// If sharing with CAN, one can set this to be non-zero to give CAN space where it wants it
-// Both of these MUST be a multiple of 2, and are in byte units.
-#ifndef DCD_STM32_BTABLE_BASE
-# define DCD_STM32_BTABLE_BASE 0U
-#endif
+#include "fsdev_type.h"
-#ifndef DCD_STM32_BTABLE_SIZE
-# define DCD_STM32_BTABLE_SIZE (FSDEV_PMA_SIZE - DCD_STM32_BTABLE_BASE)
-#endif
+//--------------------------------------------------------------------+
+// Configuration
+//--------------------------------------------------------------------+
-/***************************************************
- * Checks, structs, defines, function definitions, etc.
- */
-TU_VERIFY_STATIC((MAX_EP_COUNT) <= STFSDEV_EP_COUNT, "Only 8 endpoints supported on the hardware");
-TU_VERIFY_STATIC(((DCD_STM32_BTABLE_BASE) + (DCD_STM32_BTABLE_SIZE)) <= (FSDEV_PMA_SIZE), "BTABLE does not fit in PMA RAM");
-TU_VERIFY_STATIC(((DCD_STM32_BTABLE_BASE) % 8) == 0, "BTABLE base must be aligned to 8 bytes");
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
// One of these for every EP IN & OUT, uses a bit of RAM....
-typedef struct
-{
- uint8_t * buffer;
- tu_fifo_t * ff;
+typedef struct {
+ uint8_t *buffer;
+ tu_fifo_t *ff;
uint16_t total_len;
uint16_t queued_len;
- uint16_t pma_ptr;
uint16_t max_packet_size;
- uint16_t pma_alloc_size;
- uint8_t ep_idx; // index for USB_EPnR register
+ uint8_t ep_idx; // index for USB_EPnR register
+ bool iso_in_sending; // Workaround for ISO IN EP doesn't have interrupt mask
} xfer_ctl_t;
// EP allocator
-typedef struct
-{
+typedef struct {
uint8_t ep_num;
uint8_t ep_type;
bool allocated[2];
} ep_alloc_t;
-static xfer_ctl_t xfer_status[MAX_EP_COUNT][2];
-
-static ep_alloc_t ep_alloc_status[STFSDEV_EP_COUNT];
-
-static TU_ATTR_ALIGNED(4) uint32_t _setup_packet[6];
+static xfer_ctl_t xfer_status[CFG_TUD_ENDPPOINT_MAX][2];
+static ep_alloc_t ep_alloc_status[FSDEV_EP_COUNT];
static uint8_t remoteWakeCountdown; // When wake is requested
@@ -178,267 +163,86 @@ static uint8_t remoteWakeCountdown; // When wake is requested
//--------------------------------------------------------------------+
// into the stack.
-static void dcd_handle_bus_reset(void);
-static void dcd_transmit_packet(xfer_ctl_t * xfer, uint16_t ep_ix);
-static void dcd_ep_ctr_handler(void);
+static void handle_bus_reset(uint8_t rhport);
+static void dcd_transmit_packet(xfer_ctl_t *xfer, uint16_t ep_ix);
+static bool edpt_xfer(uint8_t rhport, uint8_t ep_num, uint8_t dir);
// PMA allocation/access
-static uint8_t open_ep_count;
static uint16_t ep_buf_ptr; ///< Points to first free memory location
-static void dcd_pma_alloc_reset(void);
-static uint16_t dcd_pma_alloc(uint8_t ep_addr, uint16_t length);
-static void dcd_pma_free(uint8_t ep_addr);
-static void dcd_ep_free(uint8_t ep_addr);
+static uint32_t dcd_pma_alloc(uint16_t len, bool dbuf);
static uint8_t dcd_ep_alloc(uint8_t ep_addr, uint8_t ep_type);
-static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t wNBytes);
-static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t wNBytes);
+static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t nbytes);
+static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t nbytes);
-static bool dcd_write_packet_memory_ff(tu_fifo_t * ff, uint16_t dst, uint16_t wNBytes);
-static bool dcd_read_packet_memory_ff(tu_fifo_t * ff, uint16_t src, uint16_t wNBytes);
+static bool dcd_write_packet_memory_ff(tu_fifo_t *ff, uint16_t dst, uint16_t wNBytes);
+static bool dcd_read_packet_memory_ff(tu_fifo_t *ff, uint16_t src, uint16_t wNBytes);
+
+static void edpt0_open(uint8_t rhport);
//--------------------------------------------------------------------+
// Inline helper
//--------------------------------------------------------------------+
-TU_ATTR_ALWAYS_INLINE static inline xfer_ctl_t* xfer_ctl_ptr(uint32_t ep_addr) {
- uint8_t epnum = tu_edpt_number(ep_addr);
- uint8_t dir = tu_edpt_dir(ep_addr);
- // Fix -Werror=null-dereference
- TU_ASSERT(epnum < MAX_EP_COUNT, &xfer_status[0][0]);
-
+TU_ATTR_ALWAYS_INLINE static inline xfer_ctl_t *xfer_ctl_ptr(uint8_t epnum, uint8_t dir) {
return &xfer_status[epnum][dir];
}
//--------------------------------------------------------------------+
// Controller API
//--------------------------------------------------------------------+
-
-void dcd_init (uint8_t rhport)
-{
- /* Clocks should already be enabled */
- /* Use __HAL_RCC_USB_CLK_ENABLE(); to enable the clocks before calling this function */
-
- /* The RM mentions to use a special ordering of PDWN and FRES, but this isn't done in HAL.
- * Here, the RM is followed. */
-
- for(uint32_t i = 0; i<200; i++) // should be a few us
- {
+void dcd_init(uint8_t rhport) {
+ // Follow the RM mentions to use a special ordering of PDWN and FRES
+ for (volatile uint32_t i = 0; i < 200; i++) { // should be a few us
asm("NOP");
}
+
// Perform USB peripheral reset
- USB->CNTR = USB_CNTR_FRES | USB_CNTR_PDWN;
- for(uint32_t i = 0; i<200; i++) // should be a few us
- {
+ FSDEV_REG->CNTR = USB_CNTR_FRES | USB_CNTR_PDWN;
+ for (volatile uint32_t i = 0; i < 200; i++) { // should be a few us
asm("NOP");
}
- USB->CNTR &= ~USB_CNTR_PDWN;
+ FSDEV_REG->CNTR &= ~USB_CNTR_PDWN;
// Wait startup time, for F042 and F070, this is <= 1 us.
- for(uint32_t i = 0; i<200; i++) // should be a few us
- {
+ for (volatile uint32_t i = 0; i < 200; i++) { // should be a few us
asm("NOP");
}
- USB->CNTR = 0; // Enable USB
+ FSDEV_REG->CNTR = 0; // Enable USB
-#if !defined(STM32G0) && !defined(STM32H5) // BTABLE register does not exist any more on STM32G0, it is fixed to USB SRAM base address
- USB->BTABLE = DCD_STM32_BTABLE_BASE;
+#if !defined(FSDEV_BUS_32BIT)
+ // BTABLE register does not exist any more on 32-bit bus devices
+ FSDEV_REG->BTABLE = FSDEV_BTABLE_BASE;
#endif
- USB->ISTR = 0; // Clear pending interrupts
+
+ FSDEV_REG->ISTR = 0; // Clear pending interrupts
// Reset endpoints to disabled
- for(uint32_t i=0; i<STFSDEV_EP_COUNT; i++)
- {
+ for (uint32_t i = 0; i < FSDEV_EP_COUNT; i++) {
// This doesn't clear all bits since some bits are "toggle", but does set the type to DISABLED.
- pcd_set_endpoint(USB,i,0u);
+ ep_write(i, 0u);
}
- USB->CNTR |= USB_CNTR_RESETM | USB_CNTR_ESOFM | USB_CNTR_CTRM | USB_CNTR_SUSPM | USB_CNTR_WKUPM;
- dcd_handle_bus_reset();
+ FSDEV_REG->CNTR |= USB_CNTR_RESETM | USB_CNTR_ESOFM | USB_CNTR_CTRM | USB_CNTR_SUSPM | USB_CNTR_WKUPM;
+ handle_bus_reset(rhport);
// Enable pull-up if supported
- if ( dcd_connect ) dcd_connect(rhport);
-}
-
-// Define only on MCU with internal pull-up. BSP can define on MCU without internal PU.
-#if defined(USB_BCDR_DPPU)
-
-// Disable internal D+ PU
-void dcd_disconnect(uint8_t rhport)
-{
- (void) rhport;
- USB->BCDR &= ~(USB_BCDR_DPPU);
-}
-
-// Enable internal D+ PU
-void dcd_connect(uint8_t rhport)
-{
- (void) rhport;
- USB->BCDR |= USB_BCDR_DPPU;
-}
-
-#elif defined(SYSCFG_PMC_USB_PU) // works e.g. on STM32L151
-// Disable internal D+ PU
-void dcd_disconnect(uint8_t rhport)
-{
- (void) rhport;
- SYSCFG->PMC &= ~(SYSCFG_PMC_USB_PU);
-}
-
-// Enable internal D+ PU
-void dcd_connect(uint8_t rhport)
-{
- (void) rhport;
- SYSCFG->PMC |= SYSCFG_PMC_USB_PU;
-}
-#endif
-
-void dcd_sof_enable(uint8_t rhport, bool en)
-{
- (void) rhport;
- (void) en;
-
- if (en)
- {
- USB->CNTR |= USB_CNTR_SOFM;
- }
- else
- {
- USB->CNTR &= ~USB_CNTR_SOFM;
- }
-}
-
-// Enable device interrupt
-void dcd_int_enable (uint8_t rhport)
-{
- (void)rhport;
- // Member here forces write to RAM before allowing ISR to execute
- __DSB();
- __ISB();
-#if CFG_TUSB_MCU == OPT_MCU_STM32F0 || CFG_TUSB_MCU == OPT_MCU_STM32L0 || \
- CFG_TUSB_MCU == OPT_MCU_STM32L4
- NVIC_EnableIRQ(USB_IRQn);
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32L1
- NVIC_EnableIRQ(USB_LP_IRQn);
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32F3
- // Some STM32F302/F303 devices allow to remap the USB interrupt vectors from
- // shared USB/CAN IRQs to separate CAN and USB IRQs.
- // This dynamically checks if this remap is active to enable the right IRQs.
- #ifdef SYSCFG_CFGR1_USB_IT_RMP
- if (SYSCFG->CFGR1 & SYSCFG_CFGR1_USB_IT_RMP)
- {
- NVIC_EnableIRQ(USB_HP_IRQn);
- NVIC_EnableIRQ(USB_LP_IRQn);
- NVIC_EnableIRQ(USBWakeUp_RMP_IRQn);
- }
- else
- #endif
- {
- NVIC_EnableIRQ(USB_HP_CAN_TX_IRQn);
- NVIC_EnableIRQ(USB_LP_CAN_RX0_IRQn);
- NVIC_EnableIRQ(USBWakeUp_IRQn);
- }
-#elif CFG_TUSB_MCU == OPT_MCU_STM32F1
- NVIC_EnableIRQ(USB_HP_CAN1_TX_IRQn);
- NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn);
- NVIC_EnableIRQ(USBWakeUp_IRQn);
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32G4
- NVIC_EnableIRQ(USB_HP_IRQn);
- NVIC_EnableIRQ(USB_LP_IRQn);
- NVIC_EnableIRQ(USBWakeUp_IRQn);
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32G0
- #ifdef STM32G0B0xx
- NVIC_EnableIRQ(USB_IRQn);
- #else
- NVIC_EnableIRQ(USB_UCPD1_2_IRQn);
- #endif
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32H5
- NVIC_EnableIRQ(USB_DRD_FS_IRQn);
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32WB
- NVIC_EnableIRQ(USB_HP_IRQn);
- NVIC_EnableIRQ(USB_LP_IRQn);
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32L5
- NVIC_EnableIRQ(USB_FS_IRQn);
-
-#else
- #error Unknown arch in USB driver
-#endif
+ dcd_connect(rhport);
}
-// Disable device interrupt
-void dcd_int_disable(uint8_t rhport)
-{
+void dcd_sof_enable(uint8_t rhport, bool en) {
(void)rhport;
-#if CFG_TUSB_MCU == OPT_MCU_STM32F0 || CFG_TUSB_MCU == OPT_MCU_STM32L0 || \
- CFG_TUSB_MCU == OPT_MCU_STM32L4
- NVIC_DisableIRQ(USB_IRQn);
-#elif CFG_TUSB_MCU == OPT_MCU_STM32L1
- NVIC_DisableIRQ(USB_LP_IRQn);
-#elif CFG_TUSB_MCU == OPT_MCU_STM32F3
- // Some STM32F302/F303 devices allow to remap the USB interrupt vectors from
- // shared USB/CAN IRQs to separate CAN and USB IRQs.
- // This dynamically checks if this remap is active to disable the right IRQs.
- #ifdef SYSCFG_CFGR1_USB_IT_RMP
- if (SYSCFG->CFGR1 & SYSCFG_CFGR1_USB_IT_RMP)
- {
- NVIC_DisableIRQ(USB_HP_IRQn);
- NVIC_DisableIRQ(USB_LP_IRQn);
- NVIC_DisableIRQ(USBWakeUp_RMP_IRQn);
- }
- else
- #endif
- {
- NVIC_DisableIRQ(USB_HP_CAN_TX_IRQn);
- NVIC_DisableIRQ(USB_LP_CAN_RX0_IRQn);
- NVIC_DisableIRQ(USBWakeUp_IRQn);
+ if (en) {
+ FSDEV_REG->CNTR |= USB_CNTR_SOFM;
+ } else {
+ FSDEV_REG->CNTR &= ~USB_CNTR_SOFM;
}
-#elif CFG_TUSB_MCU == OPT_MCU_STM32F1
- NVIC_DisableIRQ(USB_HP_CAN1_TX_IRQn);
- NVIC_DisableIRQ(USB_LP_CAN1_RX0_IRQn);
- NVIC_DisableIRQ(USBWakeUp_IRQn);
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32G4
- NVIC_DisableIRQ(USB_HP_IRQn);
- NVIC_DisableIRQ(USB_LP_IRQn);
- NVIC_DisableIRQ(USBWakeUp_IRQn);
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32G0
- #ifdef STM32G0B0xx
- NVIC_DisableIRQ(USB_IRQn);
- #else
- NVIC_DisableIRQ(USB_UCPD1_2_IRQn);
- #endif
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32H5
- NVIC_DisableIRQ(USB_DRD_FS_IRQn);
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32WB
- NVIC_DisableIRQ(USB_HP_IRQn);
- NVIC_DisableIRQ(USB_LP_IRQn);
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32L5
- NVIC_DisableIRQ(USB_FS_IRQn);
-
-#else
- #error Unknown arch in USB driver
-#endif
-
- // CMSIS has a membar after disabling interrupts
}
// Receive Set Address request, mcu port must also include status IN response
-void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
-{
- (void) rhport;
- (void) dev_addr;
+void dcd_set_address(uint8_t rhport, uint8_t dev_addr) {
+ (void)dev_addr;
// Respond with status
dcd_edpt_xfer(rhport, TUSB_DIR_IN_MASK | 0x00, NULL, 0);
@@ -447,46 +251,17 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
// do it at dcd_edpt0_status_complete()
}
-void dcd_remote_wakeup(uint8_t rhport)
-{
- (void) rhport;
+void dcd_remote_wakeup(uint8_t rhport) {
+ (void)rhport;
- USB->CNTR |= USB_CNTR_RESUME;
+ FSDEV_REG->CNTR |= USB_CNTR_RESUME;
remoteWakeCountdown = 4u; // required to be 1 to 15 ms, ESOF should trigger every 1ms.
}
-static const tusb_desc_endpoint_t ep0OUT_desc =
-{
- .bLength = sizeof(tusb_desc_endpoint_t),
- .bDescriptorType = TUSB_DESC_ENDPOINT,
-
- .bEndpointAddress = 0x00,
- .bmAttributes = { .xfer = TUSB_XFER_CONTROL },
- .wMaxPacketSize = CFG_TUD_ENDPOINT0_SIZE,
- .bInterval = 0
-};
-
-static const tusb_desc_endpoint_t ep0IN_desc =
-{
- .bLength = sizeof(tusb_desc_endpoint_t),
- .bDescriptorType = TUSB_DESC_ENDPOINT,
-
- .bEndpointAddress = 0x80,
- .bmAttributes = { .xfer = TUSB_XFER_CONTROL },
- .wMaxPacketSize = CFG_TUD_ENDPOINT0_SIZE,
- .bInterval = 0
-};
-
-static void dcd_handle_bus_reset(void)
-{
- //__IO uint16_t * const epreg = &(EPREG(0));
- USB->DADDR = 0u; // disable USB peripheral by clearing the EF flag
-
- for(uint32_t i=0; i<STFSDEV_EP_COUNT; i++)
- {
- // Clear all EPREG (or maybe this is automatic? I'm not sure)
- pcd_set_endpoint(USB,i,0u);
+static void handle_bus_reset(uint8_t rhport) {
+ FSDEV_REG->DADDR = 0u; // disable USB Function
+ for (uint32_t i = 0; i < FSDEV_EP_COUNT; i++) {
// Clear EP allocation status
ep_alloc_status[i].ep_num = 0xFF;
ep_alloc_status[i].ep_type = 0xFF;
@@ -494,47 +269,52 @@ static void dcd_handle_bus_reset(void)
ep_alloc_status[i].allocated[1] = false;
}
- dcd_pma_alloc_reset();
- dcd_edpt_open (0, &ep0OUT_desc);
- dcd_edpt_open (0, &ep0IN_desc);
+ // Reset PMA allocation
+ ep_buf_ptr = FSDEV_BTABLE_BASE + 8 * FSDEV_EP_COUNT;
+
+ edpt0_open(rhport); // open control endpoint (both IN & OUT)
- USB->DADDR = USB_DADDR_EF; // Set enable flag, and leaving the device address as zero.
+ FSDEV_REG->DADDR = USB_DADDR_EF; // Enable USB Function
}
// Handle CTR interrupt for the TX/IN direction
-//
-// Upon call, (wIstr & USB_ISTR_DIR) == 0U
-static void dcd_ep_ctr_tx_handler(uint32_t wIstr)
-{
- uint32_t EPindex = wIstr & USB_ISTR_EP_ID;
- uint32_t wEPRegVal = pcd_get_endpoint(USB, EPindex);
- uint8_t ep_addr = (wEPRegVal & USB_EPADDR_FIELD) | TUSB_DIR_IN_MASK;
+static void handle_ctr_tx(uint32_t ep_id) {
+ uint32_t ep_reg = ep_read(ep_id) & USB_EPREG_MASK;
- // Verify the CTR_TX bit is set. This was in the ST Micro code,
- // but I'm not sure it's actually necessary?
- if((wEPRegVal & USB_EP_CTR_TX) == 0U)
- {
- return;
- }
+ // Verify the CTR bit is set. This was in the ST Micro code, but I'm not sure it's actually necessary?
+ TU_VERIFY(ep_reg & USB_EP_CTR_TX, );
- /* clear int flag */
- pcd_clear_tx_ep_ctr(USB, EPindex);
+ uint8_t const ep_num = ep_reg & USB_EPADDR_FIELD;
+ uint8_t ep_addr = (ep_reg & USB_EPADDR_FIELD) | TUSB_DIR_IN_MASK;
+ xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, TUSB_DIR_IN);
- xfer_ctl_t * xfer = xfer_ctl_ptr(ep_addr);
- if((xfer->total_len != xfer->queued_len)) /* TX not complete */
- {
- dcd_transmit_packet(xfer, EPindex);
+ if (ep_is_iso(ep_reg)) {
+ // Ignore spurious interrupts that we don't schedule
+ // host can send IN token while there is no data to send, since ISO does not have NAK
+ // this will result to zero length packet --> trigger interrupt (which cannot be masked)
+ if (!xfer->iso_in_sending) {
+ return;
+ }
+ xfer->iso_in_sending = false;
+ uint8_t buf_id = (ep_reg & USB_EP_DTOG_TX) ? 0 : 1;
+ btable_set_count(ep_id, buf_id, 0);
}
- else /* TX Complete */
- {
+
+ if (xfer->total_len != xfer->queued_len) {
+ dcd_transmit_packet(xfer, ep_id); // also clear CTR bit
+ } else {
dcd_event_xfer_complete(0, ep_addr, xfer->total_len, XFER_RESULT_SUCCESS, true);
+
+ // Clear CTR TX and reserved CTR RX
+ ep_reg = (ep_reg & ~USB_EP_CTR_TX) | USB_EP_CTR_RX;
+
+ ep_write(ep_id, ep_reg);
}
}
// Handle CTR interrupt for the RX/OUT direction
-// Upon call, (wIstr & USB_ISTR_DIR) == 0U
-static void dcd_ep_ctr_rx_handler(uint32_t wIstr) {
- #ifdef FSDEV_BUS_32BIT
+static void handle_ctr_rx(uint32_t ep_id) {
+#ifdef FSDEV_BUS_32BIT
/* https://www.st.com/resource/en/errata_sheet/es0561-stm32h503cbebkbrb-device-errata-stmicroelectronics.pdf
* From STM32H503 errata 2.15.1: Buffer description table update completes after CTR interrupt triggers
* Description:
@@ -544,200 +324,157 @@ static void dcd_ep_ctr_rx_handler(uint32_t wIstr) {
* - Software should ensure that a small delay is included before accessing the SRAM contents. This delay
* should be 800 ns in Full Speed mode and 6.4 μs in Low Speed mode
* - Since H5 can run up to 250Mhz -> 1 cycle = 4ns. Per errata, we need to wait 200 cycles. Though executing code
- * also takes time, so we'll wait 40 cycles (count = 20).
+ * also takes time, so we'll wait 60 cycles (count = 20).
* - Since Low Speed mode is not supported/popular, we will ignore it for now.
*
* Note: this errata also seems to apply to G0, U5, H5 etc.
*/
volatile uint32_t cycle_count = 20; // defined as PCD_RX_PMA_CNT in stm32 hal_driver
while (cycle_count > 0U) {
- cycle_count--; // each count take 2 cycle (1 cycle for sub, 1 cycle for compare/jump)
+ cycle_count--; // each count take 3 cycles (1 for sub, jump, and compare)
}
- #endif
+#endif
- uint32_t EPindex = wIstr & USB_ISTR_EP_ID;
- uint32_t wEPRegVal = pcd_get_endpoint(USB, EPindex);
- uint8_t ep_addr = wEPRegVal & USB_EPADDR_FIELD;
+ uint32_t ep_reg = ep_read(ep_id);
- xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr);
+ // Verify the CTR bit is set. This was in the ST Micro code, but I'm not sure it's actually necessary?
+ TU_VERIFY(ep_reg & USB_EP_CTR_RX, );
+ ep_reg = (ep_reg & ~USB_EP_CTR_RX) | USB_EP_CTR_TX; // Clear CTR RX and reserved CTR TX
- // Verify the CTR_RX bit is set. This was in the ST Micro code,
- // but I'm not sure it's actually necessary?
- if((wEPRegVal & USB_EP_CTR_RX) == 0U) {
- return;
- }
+ uint8_t const ep_num = ep_reg & USB_EPADDR_FIELD;
- if((ep_addr == 0U) && ((wEPRegVal & USB_EP_SETUP) != 0U)) /* Setup packet */
- {
- uint32_t count = pcd_get_ep_rx_cnt(USB, EPindex);
- /* Get SETUP Packet*/
- if(count == 8) // Setup packet should always be 8 bytes. If not, ignore it, and try again.
- {
- // Must reset EP to NAK (in case it had been stalling) (though, maybe too late here)
- pcd_set_ep_rx_status(USB,0u,USB_EP_RX_NAK);
- pcd_set_ep_tx_status(USB,0u,USB_EP_TX_NAK);
-#ifdef FSDEV_BUS_32BIT
- dcd_event_setup_received(0, (uint8_t*)(USB_PMAADDR + pcd_get_ep_rx_address(USB, EPindex)), true);
-#else
- // The setup_received function uses memcpy, so this must first copy the setup data into
- // user memory, to allow for the 32-bit access that memcpy performs.
- uint8_t userMemBuf[8];
- dcd_read_packet_memory(userMemBuf, pcd_get_ep_rx_address(USB,EPindex), 8);
- dcd_event_setup_received(0, (uint8_t*)userMemBuf, true);
-#endif
- }
- }
- else
- {
- uint32_t count;
- /* Read from correct register when ISOCHRONOUS (double buffered) */
- if ( (wEPRegVal & USB_EP_DTOG_RX) && ( (wEPRegVal & USB_EP_TYPE_MASK) == USB_EP_ISOCHRONOUS) ) {
- count = pcd_get_ep_tx_cnt(USB, EPindex);
- } else {
- count = pcd_get_ep_rx_cnt(USB, EPindex);
- }
+ if (ep_reg & USB_EP_SETUP) {
+ uint32_t count = btable_get_count(ep_id, BTABLE_BUF_RX);
+ // Setup packet should always be 8 bytes. If not, ignore it, and try again.
+ if (count == 8) {
+ uint16_t rx_addr = btable_get_addr(ep_id, BTABLE_BUF_RX);
+ uint32_t setup_packet[2];
+ dcd_read_packet_memory(setup_packet, rx_addr, 8);
+ dcd_event_setup_received(0, (uint8_t*) setup_packet, true);
- TU_ASSERT(count <= xfer->max_packet_size, /**/);
+ // Reset EP to NAK (in case it had been stalling)
+ ep_reg = ep_add_status(ep_reg, TUSB_DIR_IN, EP_STAT_NAK);
+ ep_reg = ep_add_status(ep_reg, TUSB_DIR_OUT, EP_STAT_NAK);
- // Clear RX CTR interrupt flag
- if(ep_addr != 0u)
- {
- pcd_clear_rx_ep_ctr(USB, EPindex);
+ ep_reg = ep_add_dtog(ep_reg, TUSB_DIR_IN, 1);
+ ep_reg = ep_add_dtog(ep_reg, TUSB_DIR_OUT, 1);
+ } else {
+ ep_reg &= USB_EPREG_MASK; // reversed all toggle
}
+ } else {
+ ep_reg &= USB_EPRX_STAT | USB_EPREG_MASK; // reversed all toggle except RX Status
- if (count != 0U)
- {
- uint16_t addr = pcd_get_ep_rx_address(USB, EPindex);
-
- if (xfer->ff)
- {
- dcd_read_packet_memory_ff(xfer->ff, addr, count);
- }
- else
- {
- dcd_read_packet_memory(&(xfer->buffer[xfer->queued_len]), addr, count);
- }
+ bool const is_iso = ep_is_iso(ep_reg);
+ xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, TUSB_DIR_OUT);
- xfer->queued_len = (uint16_t)(xfer->queued_len + count);
+ uint8_t buf_id;
+ if (is_iso) {
+ buf_id = (ep_reg & USB_EP_DTOG_RX) ? 0 : 1; // ISO are double buffered
+ } else {
+ buf_id = BTABLE_BUF_RX;
}
+ uint32_t rx_count = btable_get_count(ep_id, buf_id);
+ uint16_t pma_addr = (uint16_t) btable_get_addr(ep_id, buf_id);
- if ((count < xfer->max_packet_size) || (xfer->queued_len == xfer->total_len))
- {
- /* RX COMPLETE */
- dcd_event_xfer_complete(0, ep_addr, xfer->queued_len, XFER_RESULT_SUCCESS, true);
- // Though the host could still send, we don't know.
- // Does the bulk pipe need to be reset to valid to allow for a ZLP?
- }
- else
- {
- uint32_t remaining = (uint32_t)xfer->total_len - (uint32_t)xfer->queued_len;
- if(remaining >= xfer->max_packet_size) {
- pcd_set_ep_rx_bufsize(USB, EPindex,xfer->max_packet_size);
+ if (rx_count != 0) {
+ if (xfer->ff) {
+ dcd_read_packet_memory_ff(xfer->ff, pma_addr, rx_count);
} else {
- pcd_set_ep_rx_bufsize(USB, EPindex,remaining);
+ dcd_read_packet_memory(xfer->buffer + xfer->queued_len, pma_addr, rx_count);
}
- if (!((wEPRegVal & USB_EP_TYPE_MASK) == USB_EP_ISOCHRONOUS)) {
- /* Set endpoint active again for receiving more data.
- * Note that isochronous endpoints stay active always */
- pcd_set_ep_rx_status(USB, EPindex, USB_EP_RX_VALID);
- }
+ xfer->queued_len = (uint16_t)(xfer->queued_len + rx_count);
}
- }
- // For EP0, prepare to receive another SETUP packet.
- // Clear CTR last so that a new packet does not overwrite the packing being read.
- // (Based on the docs, it seems SETUP will always be accepted after CTR is cleared)
- if(ep_addr == 0u)
- {
- // Always be prepared for a status packet...
- pcd_set_ep_rx_bufsize(USB, EPindex, CFG_TUD_ENDPOINT0_SIZE);
- pcd_clear_rx_ep_ctr(USB, EPindex);
- }
-}
-
-static void dcd_ep_ctr_handler(void) {
- uint32_t wIstr;
+ if ((rx_count < xfer->max_packet_size) || (xfer->queued_len == xfer->total_len)) {
+ uint8_t const ep_addr = ep_num;
+ // all bytes received or short packet
+ dcd_event_xfer_complete(0, ep_addr, xfer->queued_len, XFER_RESULT_SUCCESS, true);
- /* stay in loop while pending interrupts */
- while (((wIstr = USB->ISTR) & USB_ISTR_CTR) != 0U) {
- if ((wIstr & USB_ISTR_DIR) == 0U) {
- /* TX/IN */
- dcd_ep_ctr_tx_handler(wIstr);
+ if (ep_num == 0) {
+ // prepared for status packet
+ btable_set_rx_bufsize(ep_id, BTABLE_BUF_RX, CFG_TUD_ENDPOINT0_SIZE);
+ }
+ ep_reg = ep_add_status(ep_reg, TUSB_DIR_OUT, EP_STAT_NAK);
} else {
- /* RX/OUT*/
- dcd_ep_ctr_rx_handler(wIstr);
+ // Set endpoint active again for receiving more data. Note that isochronous endpoints stay active always
+ if (!is_iso) {
+ uint16_t const cnt = tu_min16(xfer->total_len - xfer->queued_len, xfer->max_packet_size);
+ btable_set_rx_bufsize(ep_id, BTABLE_BUF_RX, cnt);
+ }
+ ep_reg = ep_add_status(ep_reg, TUSB_DIR_OUT, EP_STAT_VALID);
}
}
+
+ ep_write(ep_id, ep_reg);
}
void dcd_int_handler(uint8_t rhport) {
-
- (void) rhport;
-
- uint32_t int_status = USB->ISTR;
- //const uint32_t handled_ints = USB_ISTR_CTR | USB_ISTR_RESET | USB_ISTR_WKUP
- // | USB_ISTR_SUSP | USB_ISTR_SOF | USB_ISTR_ESOF;
- // unused IRQs: (USB_ISTR_PMAOVR | USB_ISTR_ERR | USB_ISTR_L1REQ )
+ uint32_t int_status = FSDEV_REG->ISTR;
+ // const uint32_t handled_ints = USB_ISTR_CTR | USB_ISTR_RESET | USB_ISTR_WKUP
+ // | USB_ISTR_SUSP | USB_ISTR_SOF | USB_ISTR_ESOF;
+ // unused IRQs: (USB_ISTR_PMAOVR | USB_ISTR_ERR | USB_ISTR_L1REQ )
// The ST driver loops here on the CTR bit, but that loop has been moved into the
// dcd_ep_ctr_handler(), so less need to loop here. The other interrupts shouldn't
// be triggered repeatedly.
/* Put SOF flag at the beginning of ISR in case to get least amount of jitter if it is used for timing purposes */
- if(int_status & USB_ISTR_SOF) {
- USB->ISTR = (fsdev_bus_t)~USB_ISTR_SOF;
- dcd_event_sof(0, USB->FNR & USB_FNR_FN, true);
+ if (int_status & USB_ISTR_SOF) {
+ FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_SOF;
+ dcd_event_sof(0, FSDEV_REG->FNR & USB_FNR_FN, true);
}
- if(int_status & USB_ISTR_RESET) {
+ if (int_status & USB_ISTR_RESET) {
// USBRST is start of reset.
- USB->ISTR = (fsdev_bus_t)~USB_ISTR_RESET;
- dcd_handle_bus_reset();
+ FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_RESET;
+ handle_bus_reset(rhport);
dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
return; // Don't do the rest of the things here; perhaps they've been cleared?
}
- if (int_status & USB_ISTR_CTR)
- {
- /* servicing of the endpoint correct transfer interrupt */
- /* clear of the CTR flag into the sub */
- dcd_ep_ctr_handler();
- }
-
- if (int_status & USB_ISTR_WKUP)
- {
- USB->CNTR &= ~USB_CNTR_LPMODE;
- USB->CNTR &= ~USB_CNTR_FSUSP;
+ if (int_status & USB_ISTR_WKUP) {
+ FSDEV_REG->CNTR &= ~USB_CNTR_LPMODE;
+ FSDEV_REG->CNTR &= ~USB_CNTR_FSUSP;
- USB->ISTR = (fsdev_bus_t)~USB_ISTR_WKUP;
+ FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_WKUP;
dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
}
- if (int_status & USB_ISTR_SUSP)
- {
+ if (int_status & USB_ISTR_SUSP) {
/* Suspend is asserted for both suspend and unplug events. without Vbus monitoring,
* these events cannot be differentiated, so we only trigger suspend. */
/* Force low-power mode in the macrocell */
- USB->CNTR |= USB_CNTR_FSUSP;
- USB->CNTR |= USB_CNTR_LPMODE;
+ FSDEV_REG->CNTR |= USB_CNTR_FSUSP;
+ FSDEV_REG->CNTR |= USB_CNTR_LPMODE;
/* clear of the ISTR bit must be done after setting of CNTR_FSUSP */
- USB->ISTR = (fsdev_bus_t)~USB_ISTR_SUSP;
+ FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_SUSP;
dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
}
- if(int_status & USB_ISTR_ESOF) {
- if(remoteWakeCountdown == 1u)
- {
- USB->CNTR &= ~USB_CNTR_RESUME;
+ if (int_status & USB_ISTR_ESOF) {
+ if (remoteWakeCountdown == 1u) {
+ FSDEV_REG->CNTR &= ~USB_CNTR_RESUME;
}
- if(remoteWakeCountdown > 0u)
- {
+ if (remoteWakeCountdown > 0u) {
remoteWakeCountdown--;
}
- USB->ISTR = (fsdev_bus_t)~USB_ISTR_ESOF;
+ FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_ESOF;
+ }
+
+ // loop to handle all pending CTR interrupts
+ while (int_status & USB_ISTR_CTR) {
+ uint32_t const ep_id = int_status & USB_ISTR_EP_ID;
+
+ if ((int_status & USB_ISTR_DIR) == 0U) {
+ handle_ctr_tx(ep_id); // TX/IN
+ } else {
+ handle_ctr_rx(ep_id); // RX/OUT or both (RX/TX !!)
+ }
+
+ int_status = FSDEV_REG->ISTR;
}
}
@@ -747,137 +484,67 @@ void dcd_int_handler(uint8_t rhport) {
// Invoked when a control transfer's status stage is complete.
// May help DCD to prepare for next control transfer, this API is optional.
-void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request)
-{
- (void) rhport;
+void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const *request) {
+ (void)rhport;
if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE &&
request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD &&
- request->bRequest == TUSB_REQ_SET_ADDRESS )
- {
- uint8_t const dev_addr = (uint8_t) request->wValue;
-
- // Setting new address after the whole request is complete
- USB->DADDR &= ~USB_DADDR_ADD;
- USB->DADDR |= dev_addr; // leave the enable bit set
- }
-}
-
-static void dcd_pma_alloc_reset(void)
-{
- open_ep_count = 0;
- ep_buf_ptr = DCD_STM32_BTABLE_BASE + 8*MAX_EP_COUNT; // 8 bytes per endpoint (two TX and two RX words, each)
- //TU_LOG2("dcd_pma_alloc_reset()\r\n");
- for(uint32_t i=0; i<MAX_EP_COUNT; i++)
- {
- xfer_ctl_ptr(tu_edpt_addr(i,TUSB_DIR_OUT))->pma_alloc_size = 0U;
- xfer_ctl_ptr(tu_edpt_addr(i,TUSB_DIR_IN))->pma_alloc_size = 0U;
- xfer_ctl_ptr(tu_edpt_addr(i,TUSB_DIR_OUT))->pma_ptr = 0U;
- xfer_ctl_ptr(tu_edpt_addr(i,TUSB_DIR_IN))->pma_ptr = 0U;
+ request->bRequest == TUSB_REQ_SET_ADDRESS) {
+ uint8_t const dev_addr = (uint8_t)request->wValue;
+ FSDEV_REG->DADDR = (USB_DADDR_EF | dev_addr);
}
}
/***
* Allocate a section of PMA
- *
- * If the EP number has already been allocated, and the new allocation
- * is larger than the old allocation, then this will fail with a TU_ASSERT.
- * (This is done to simplify the code. More complicated algorithms could be used)
- *
+ * In case of double buffering, high 16bit is the address of 2nd buffer
* During failure, TU_ASSERT is used. If this happens, rework/reallocate memory manually.
*/
-static uint16_t dcd_pma_alloc(uint8_t ep_addr, uint16_t length)
+static uint32_t dcd_pma_alloc(uint16_t len, bool dbuf)
{
- xfer_ctl_t* epXferCtl = xfer_ctl_ptr(ep_addr);
-
- if(epXferCtl->pma_alloc_size != 0U)
- {
- //TU_LOG2("dcd_pma_alloc(%x,%x)=%x (cached)\r\n",ep_addr,length,epXferCtl->pma_ptr);
- // Previously allocated
- TU_ASSERT(length <= epXferCtl->pma_alloc_size, 0xFFFF); // Verify no larger than previous alloc
- return epXferCtl->pma_ptr;
- }
+ uint8_t blsize, num_block;
+ uint16_t aligned_len = pma_align_buffer_size(len, &blsize, &num_block);
+ (void) blsize;
+ (void) num_block;
- // Ensure allocated buffer is aligned
-#ifdef FSDEV_BUS_32BIT
- length = (length + 3) & ~0x03;
-#else
- length = (length + 1) & ~0x01;
-#endif
+ uint32_t addr = ep_buf_ptr;
+ ep_buf_ptr = (uint16_t)(ep_buf_ptr + aligned_len); // increment buffer pointer
- open_ep_count++;
-
- uint16_t addr = ep_buf_ptr;
- ep_buf_ptr = (uint16_t)(ep_buf_ptr + length); // increment buffer pointer
+ if (dbuf) {
+ addr |= ((uint32_t)ep_buf_ptr) << 16;
+ ep_buf_ptr = (uint16_t)(ep_buf_ptr + aligned_len); // increment buffer pointer
+ }
- // Verify no overflow
+ // Verify packet buffer is not overflowed
TU_ASSERT(ep_buf_ptr <= FSDEV_PMA_SIZE, 0xFFFF);
- epXferCtl->pma_ptr = addr;
- epXferCtl->pma_alloc_size = length;
- //TU_LOG1("dcd_pma_alloc(%x,%x)=%x\r\n",ep_addr,length,addr);
-
return addr;
}
/***
- * Free a block of PMA space
- */
-static void dcd_pma_free(uint8_t ep_addr)
-{
- // Presently, this should never be called for EP0 IN/OUT
- TU_ASSERT(open_ep_count > 2, /**/);
- TU_ASSERT(xfer_ctl_ptr(ep_addr)->max_packet_size != 0, /**/);
- open_ep_count--;
-
- // If count is 2, only EP0 should be open, so allocations can be mostly reset.
-
- if(open_ep_count == 2)
- {
- ep_buf_ptr = DCD_STM32_BTABLE_BASE + 8*MAX_EP_COUNT + 2*CFG_TUD_ENDPOINT0_SIZE; // 8 bytes per endpoint (two TX and two RX words, each), and EP0
-
- // Skip EP0
- for(uint32_t i=1; i<MAX_EP_COUNT; i++)
- {
- xfer_ctl_ptr(tu_edpt_addr(i,TUSB_DIR_OUT))->pma_alloc_size = 0U;
- xfer_ctl_ptr(tu_edpt_addr(i,TUSB_DIR_IN))->pma_alloc_size = 0U;
- xfer_ctl_ptr(tu_edpt_addr(i,TUSB_DIR_OUT))->pma_ptr = 0U;
- xfer_ctl_ptr(tu_edpt_addr(i,TUSB_DIR_IN))->pma_ptr = 0U;
- }
- }
-}
-
-/***
* Allocate hardware endpoint
*/
static uint8_t dcd_ep_alloc(uint8_t ep_addr, uint8_t ep_type)
{
uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
- for(uint8_t i = 0; i < STFSDEV_EP_COUNT; i++)
- {
+ for (uint8_t i = 0; i < FSDEV_EP_COUNT; i++) {
// Check if already allocated
- if(ep_alloc_status[i].allocated[dir] &&
- ep_alloc_status[i].ep_type == ep_type &&
- ep_alloc_status[i].ep_num == epnum)
- {
+ if (ep_alloc_status[i].allocated[dir] &&
+ ep_alloc_status[i].ep_type == ep_type &&
+ ep_alloc_status[i].ep_num == epnum) {
return i;
}
// If EP of current direction is not allocated
// Except for ISO endpoint, both direction should be free
- if(!ep_alloc_status[i].allocated[dir] &&
- (ep_type != TUSB_XFER_ISOCHRONOUS || !ep_alloc_status[i].allocated[dir ^ 1]))
- {
+ if (!ep_alloc_status[i].allocated[dir] &&
+ (ep_type != TUSB_XFER_ISOCHRONOUS || !ep_alloc_status[i].allocated[dir ^ 1])) {
// Check if EP number is the same
- if(ep_alloc_status[i].ep_num == 0xFF ||
- ep_alloc_status[i].ep_num == epnum)
- {
+ if (ep_alloc_status[i].ep_num == 0xFF || ep_alloc_status[i].ep_num == epnum) {
// One EP pair has to be the same type
- if(ep_alloc_status[i].ep_type == 0xFF ||
- ep_alloc_status[i].ep_type == ep_type)
- {
+ if (ep_alloc_status[i].ep_type == 0xFF || ep_alloc_status[i].ep_type == ep_type) {
ep_alloc_status[i].ep_num = epnum;
ep_alloc_status[i].ep_type = ep_type;
ep_alloc_status[i].allocated[dir] = true;
@@ -892,469 +559,420 @@ static uint8_t dcd_ep_alloc(uint8_t ep_addr, uint8_t ep_type)
TU_ASSERT(0);
}
-/***
- * Free hardware endpoint
- */
-static void dcd_ep_free(uint8_t ep_addr)
-{
- uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+void edpt0_open(uint8_t rhport) {
+ (void) rhport;
- for(uint8_t i = 0; i < STFSDEV_EP_COUNT; i++)
- {
- // Check if EP number & dir are the same
- if(ep_alloc_status[i].ep_num == epnum &&
- ep_alloc_status[i].allocated[dir] == dir)
- {
- ep_alloc_status[i].allocated[dir] = false;
- // Reset entry if ISO endpoint or both direction are free
- if(ep_alloc_status[i].ep_type == TUSB_XFER_ISOCHRONOUS ||
- !ep_alloc_status[i].allocated[dir ^ 1])
- {
- ep_alloc_status[i].ep_num = 0xFF;
- ep_alloc_status[i].ep_type = 0xFF;
+ dcd_ep_alloc(0x0, TUSB_XFER_CONTROL);
+ dcd_ep_alloc(0x80, TUSB_XFER_CONTROL);
- return;
- }
- }
- }
-}
+ xfer_status[0][0].max_packet_size = CFG_TUD_ENDPOINT0_SIZE;
+ xfer_status[0][0].ep_idx = 0;
-// The STM32F0 doesn't seem to like |= or &= to manipulate the EP#R registers,
-// so I'm using the #define from HAL here, instead.
+ xfer_status[0][1].max_packet_size = CFG_TUD_ENDPOINT0_SIZE;
+ xfer_status[0][1].ep_idx = 0;
-bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
-{
- (void)rhport;
- uint8_t const ep_idx = dcd_ep_alloc(p_endpoint_desc->bEndpointAddress, p_endpoint_desc->bmAttributes.xfer);
- uint8_t const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress);
- const uint16_t packet_size = tu_edpt_packet_size(p_endpoint_desc);
- const uint16_t buffer_size = pcd_aligned_buffer_size(packet_size);
- uint16_t pma_addr;
- uint32_t wType;
+ uint16_t pma_addr0 = dcd_pma_alloc(CFG_TUD_ENDPOINT0_SIZE, false);
+ uint16_t pma_addr1 = dcd_pma_alloc(CFG_TUD_ENDPOINT0_SIZE, false);
- TU_ASSERT(ep_idx < STFSDEV_EP_COUNT);
- TU_ASSERT(buffer_size <= 1024);
+ btable_set_addr(0, BTABLE_BUF_RX, pma_addr0);
+ btable_set_addr(0, BTABLE_BUF_TX, pma_addr1);
- // Set type
- switch(p_endpoint_desc->bmAttributes.xfer) {
- case TUSB_XFER_CONTROL:
- wType = USB_EP_CONTROL;
- break;
- case TUSB_XFER_ISOCHRONOUS:
- wType = USB_EP_ISOCHRONOUS;
- break;
- case TUSB_XFER_BULK:
- wType = USB_EP_CONTROL;
- break;
+ uint32_t ep_reg = FSDEV_REG->ep[0].reg & ~USB_EPREG_MASK;
+ ep_reg |= USB_EP_CONTROL;
+ ep_reg = ep_add_status(ep_reg, TUSB_DIR_IN, EP_STAT_NAK);
+ ep_reg = ep_add_status(ep_reg, TUSB_DIR_OUT, EP_STAT_NAK);
+ // no need to explicitly set DTOG bits since we aren't masked DTOG bit
- case TUSB_XFER_INTERRUPT:
- wType = USB_EP_INTERRUPT;
- break;
+ // prepare for setup packet
+ btable_set_rx_bufsize(0, BTABLE_BUF_RX, CFG_TUD_ENDPOINT0_SIZE);
- default:
- TU_ASSERT(false);
- }
+ ep_write(0, ep_reg);
+}
- pcd_set_eptype(USB, ep_idx, wType);
- pcd_set_ep_address(USB, ep_idx, tu_edpt_number(p_endpoint_desc->bEndpointAddress));
- // Be normal, for now, instead of only accepting zero-byte packets (on control endpoint)
- // or being double-buffered (bulk endpoints)
- pcd_clear_ep_kind(USB,0);
+bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_ep) {
+ (void)rhport;
+ uint8_t const ep_addr = desc_ep->bEndpointAddress;
+ uint8_t const ep_num = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+ const uint16_t packet_size = tu_edpt_packet_size(desc_ep);
+ uint8_t const ep_idx = dcd_ep_alloc(ep_addr, desc_ep->bmAttributes.xfer);
+ TU_ASSERT(ep_idx < FSDEV_EP_COUNT);
- /* Create a packet memory buffer area. For isochronous endpoints,
- * use the same buffer as the double buffer, essentially disabling double buffering */
- pma_addr = dcd_pma_alloc(p_endpoint_desc->bEndpointAddress, buffer_size);
+ uint32_t ep_reg = FSDEV_REG->ep[ep_idx].reg & ~USB_EPREG_MASK;
+ ep_reg |= tu_edpt_number(ep_addr) | USB_EP_CTR_RX | USB_EP_CTR_TX;
- if( (dir == TUSB_DIR_IN) || (wType == USB_EP_ISOCHRONOUS) )
- {
- pcd_set_ep_tx_address(USB, ep_idx, pma_addr);
- pcd_set_ep_tx_bufsize(USB, ep_idx, buffer_size);
- pcd_clear_tx_dtog(USB, ep_idx);
- }
+ // Set type
+ switch (desc_ep->bmAttributes.xfer) {
+ case TUSB_XFER_BULK:
+ ep_reg |= USB_EP_BULK;
+ break;
+ case TUSB_XFER_INTERRUPT:
+ ep_reg |= USB_EP_INTERRUPT;
+ break;
- if( (dir == TUSB_DIR_OUT) || (wType == USB_EP_ISOCHRONOUS) )
- {
- pcd_set_ep_rx_address(USB, ep_idx, pma_addr);
- pcd_set_ep_rx_bufsize(USB, ep_idx, buffer_size);
- pcd_clear_rx_dtog(USB, ep_idx);
+ default:
+ // Note: ISO endpoint should use alloc / active functions
+ TU_ASSERT(false);
}
- /* Enable endpoint */
- if (dir == TUSB_DIR_IN)
- {
- if(wType == USB_EP_ISOCHRONOUS) {
- pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_DIS);
- } else {
- pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_NAK);
- }
- } else
- {
- if(wType == USB_EP_ISOCHRONOUS) {
- pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_DIS);
- } else {
- pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_NAK);
- }
+ /* Create a packet memory buffer area. */
+ uint16_t pma_addr = dcd_pma_alloc(packet_size, false);
+ btable_set_addr(ep_idx, dir == TUSB_DIR_IN ? BTABLE_BUF_TX : BTABLE_BUF_RX, pma_addr);
+
+ xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir);
+ xfer->max_packet_size = packet_size;
+ xfer->ep_idx = ep_idx;
+
+ ep_reg = ep_add_status(ep_reg, dir, EP_STAT_NAK);
+ ep_reg = ep_add_dtog(ep_reg, dir, 0);
+
+ // reserve other direction toggle bits
+ if (dir == TUSB_DIR_IN) {
+ ep_reg &= ~(USB_EPRX_STAT | USB_EP_DTOG_RX);
+ } else {
+ ep_reg &= ~(USB_EPTX_STAT | USB_EP_DTOG_TX);
}
- xfer_ctl_ptr(p_endpoint_desc->bEndpointAddress)->max_packet_size = packet_size;
- xfer_ctl_ptr(p_endpoint_desc->bEndpointAddress)->ep_idx = ep_idx;
+ ep_write(ep_idx, ep_reg);
return true;
}
-void dcd_edpt_close_all (uint8_t rhport)
-{
- (void) rhport;
- // TODO implement dcd_edpt_close_all()
-}
-
-/**
- * Close an endpoint.
- *
- * This function may be called with interrupts enabled or disabled.
- *
- * This also clears transfers in progress, should there be any.
- */
-void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr)
+void dcd_edpt_close_all(uint8_t rhport)
{
(void)rhport;
- xfer_ctl_t * xfer = xfer_ctl_ptr(ep_addr);
- uint8_t const ep_idx = xfer->ep_idx;
- uint8_t const dir = tu_edpt_dir(ep_addr);
-
- if(dir == TUSB_DIR_IN)
- {
- pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_DIS);
- }
- else
- {
- pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_DIS);
+ for (uint32_t i = 1; i < FSDEV_EP_COUNT; i++) {
+ // Reset endpoint
+ ep_write(i, 0);
+ // Clear EP allocation status
+ ep_alloc_status[i].ep_num = 0xFF;
+ ep_alloc_status[i].ep_type = 0xFF;
+ ep_alloc_status[i].allocated[0] = false;
+ ep_alloc_status[i].allocated[1] = false;
}
- dcd_ep_free(ep_addr);
-
- dcd_pma_free(ep_addr);
+ // Reset PMA allocation
+ ep_buf_ptr = FSDEV_BTABLE_BASE + 8 * CFG_TUD_ENDPPOINT_MAX + 2 * CFG_TUD_ENDPOINT0_SIZE;
}
-bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size)
-{
+bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
(void)rhport;
- TU_ASSERT(largest_packet_size <= 1024);
-
+ uint8_t const ep_num = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
uint8_t const ep_idx = dcd_ep_alloc(ep_addr, TUSB_XFER_ISOCHRONOUS);
- const uint16_t buffer_size = pcd_aligned_buffer_size(largest_packet_size);
-
- /* Create a packet memory buffer area. For isochronous endpoints,
- * use the same buffer as the double buffer, essentially disabling double buffering */
- uint16_t pma_addr = dcd_pma_alloc(ep_addr, buffer_size);
- xfer_ctl_ptr(ep_addr)->ep_idx = ep_idx;
+ /* Create a packet memory buffer area. Enable double buffering for devices with 2048 bytes PMA,
+ for smaller devices double buffering occupy too much space. */
+ uint32_t pma_addr = dcd_pma_alloc(largest_packet_size, true);
+#if FSDEV_PMA_SIZE > 1024u
+ uint16_t pma_addr2 = pma_addr >> 16;
+#else
+ uint16_t pma_addr2 = pma_addr;
+#endif
- pcd_set_eptype(USB, ep_idx, USB_EP_ISOCHRONOUS);
+ btable_set_addr(ep_idx, 0, pma_addr);
+ btable_set_addr(ep_idx, 1, pma_addr2);
- pcd_set_ep_tx_address(USB, ep_idx, pma_addr);
- pcd_set_ep_rx_address(USB, ep_idx, pma_addr);
+ xfer_ctl_t* xfer = xfer_ctl_ptr(ep_num, dir);
+ xfer->ep_idx = ep_idx;
return true;
}
-bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
-{
+bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const *desc_ep) {
(void)rhport;
- uint8_t const ep_idx = xfer_ctl_ptr(p_endpoint_desc->bEndpointAddress)->ep_idx;
- uint8_t const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress);
- const uint16_t packet_size = tu_edpt_packet_size(p_endpoint_desc);
- const uint16_t buffer_size = pcd_aligned_buffer_size(packet_size);
+ uint8_t const ep_addr = desc_ep->bEndpointAddress;
+ uint8_t const ep_num = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+ xfer_ctl_t* xfer = xfer_ctl_ptr(ep_num, dir);
- /* Disable endpoint */
- if(dir == TUSB_DIR_IN)
- {
- pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_DIS);
- }
- else
- {
- pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_DIS);
- }
+ uint8_t const ep_idx = xfer->ep_idx;
- pcd_set_ep_address(USB, ep_idx, tu_edpt_number(p_endpoint_desc->bEndpointAddress));
- // Be normal, for now, instead of only accepting zero-byte packets (on control endpoint)
- // or being double-buffered (bulk endpoints)
- pcd_clear_ep_kind(USB,0);
+ xfer->max_packet_size = tu_edpt_packet_size(desc_ep);
- pcd_set_ep_tx_bufsize(USB, ep_idx, buffer_size);
- pcd_set_ep_rx_bufsize(USB, ep_idx, buffer_size);
- pcd_clear_tx_dtog(USB, ep_idx);
- pcd_clear_rx_dtog(USB, ep_idx);
+ uint32_t ep_reg = FSDEV_REG->ep[0].reg & ~USB_EPREG_MASK;
+ ep_reg |= tu_edpt_number(ep_addr) | USB_EP_ISOCHRONOUS | USB_EP_CTR_RX | USB_EP_CTR_TX;
+ ep_reg = ep_add_status(ep_reg, TUSB_DIR_IN, EP_STAT_DISABLED);
+ ep_reg = ep_add_status(ep_reg, TUSB_DIR_OUT, EP_STAT_DISABLED);
+ ep_reg = ep_add_dtog(ep_reg, dir, 0);
+ ep_reg = ep_add_dtog(ep_reg, 1-dir, 1);
- xfer_ctl_ptr(p_endpoint_desc->bEndpointAddress)->max_packet_size = packet_size;
+ ep_write(ep_idx, ep_reg);
return true;
}
// Currently, single-buffered, and only 64 bytes at a time (max)
+static void dcd_transmit_packet(xfer_ctl_t *xfer, uint16_t ep_ix) {
+ uint16_t len = tu_min16(xfer->total_len - xfer->queued_len, xfer->max_packet_size);
+ uint16_t ep_reg = ep_read(ep_ix) | EP_CTR_TXRX;
+ bool const is_iso = ep_is_iso(ep_reg);
-static void dcd_transmit_packet(xfer_ctl_t * xfer, uint16_t ep_ix)
-{
- uint16_t len = (uint16_t)(xfer->total_len - xfer->queued_len);
-
- if(len > xfer->max_packet_size) // max packet size for FS transfer
- {
- len = xfer->max_packet_size;
+ uint8_t buf_id;
+ if (is_iso) {
+ buf_id = (ep_reg & USB_EP_DTOG_TX) ? 1 : 0;
+ } else {
+ buf_id = BTABLE_BUF_TX;
}
+ uint16_t addr_ptr = (uint16_t) btable_get_addr(ep_ix, buf_id);
+ btable_set_count(ep_ix, buf_id, len);
- uint16_t ep_reg = pcd_get_endpoint(USB, ep_ix);
- uint16_t addr_ptr = pcd_get_ep_tx_address(USB, ep_ix);
-
- if (xfer->ff)
- {
+ if (xfer->ff) {
dcd_write_packet_memory_ff(xfer->ff, addr_ptr, len);
- }
- else
- {
+ } else {
dcd_write_packet_memory(addr_ptr, &(xfer->buffer[xfer->queued_len]), len);
}
xfer->queued_len = (uint16_t)(xfer->queued_len + len);
- /* Write into correct register when ISOCHRONOUS (double buffered) */
- if ( (ep_reg & USB_EP_DTOG_TX) && ( (ep_reg & USB_EP_TYPE_MASK) == USB_EP_ISOCHRONOUS) ) {
- pcd_set_ep_rx_cnt(USB, ep_ix, len);
- } else {
- pcd_set_ep_tx_cnt(USB, ep_ix, len);
- }
+ ep_reg &= USB_EPREG_MASK | EP_STAT_MASK(TUSB_DIR_IN);
+ ep_reg = ep_add_status(ep_reg, TUSB_DIR_IN, EP_STAT_VALID);
+ ep_reg = ep_clear_ctr(ep_reg, TUSB_DIR_IN);
- pcd_set_ep_tx_status(USB, ep_ix, USB_EP_TX_VALID);
+ dcd_int_disable(0);
+ ep_write(ep_ix, ep_reg);
+ if (is_iso) {
+ xfer->iso_in_sending = true;
+ }
+ dcd_int_enable(0);
}
-bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
-{
+static bool edpt_xfer(uint8_t rhport, uint8_t ep_num, uint8_t dir) {
(void) rhport;
- xfer_ctl_t * xfer = xfer_ctl_ptr(ep_addr);
+ xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir);
uint8_t const ep_idx = xfer->ep_idx;
- uint8_t const dir = tu_edpt_dir(ep_addr);
-
- xfer->buffer = buffer;
- xfer->ff = NULL;
- xfer->total_len = total_bytes;
- xfer->queued_len = 0;
- if ( dir == TUSB_DIR_OUT )
- {
- // A setup token can occur immediately after an OUT STATUS packet so make sure we have a valid
- // buffer for the control endpoint.
- if (ep_idx == 0 && buffer == NULL)
- {
- xfer->buffer = (uint8_t*)_setup_packet;
- }
+ if (dir == TUSB_DIR_IN) {
+ dcd_transmit_packet(xfer, ep_idx);
+ } else {
+ uint32_t cnt = (uint32_t) tu_min16(xfer->total_len, xfer->max_packet_size);
+ uint16_t ep_reg = ep_read(ep_idx) | USB_EP_CTR_TX;
+ ep_reg &= USB_EPREG_MASK | EP_STAT_MASK(dir); // keep CTR TX, clear CTR RX
+ ep_reg = ep_add_status(ep_reg, dir, EP_STAT_VALID);
- if(total_bytes > xfer->max_packet_size)
- {
- pcd_set_ep_rx_bufsize(USB,ep_idx,xfer->max_packet_size);
+ if (ep_is_iso(ep_reg)) {
+ btable_set_rx_bufsize(ep_idx, 0, cnt);
+ btable_set_rx_bufsize(ep_idx, 1, cnt);
} else {
- pcd_set_ep_rx_bufsize(USB,ep_idx,total_bytes);
+ btable_set_rx_bufsize(ep_idx, BTABLE_BUF_RX, cnt);
}
- pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_VALID);
- }
- else // IN
- {
- dcd_transmit_packet(xfer,ep_idx);
+
+ ep_write(ep_idx, ep_reg);
}
+
return true;
}
-bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
-{
- (void) rhport;
+bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes) {
+ uint8_t const ep_num = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+ xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir);
+
+ xfer->buffer = buffer;
+ xfer->ff = NULL;
+ xfer->total_len = total_bytes;
+ xfer->queued_len = 0;
+
+ return edpt_xfer(rhport, ep_num, dir);
+}
- xfer_ctl_t * xfer = xfer_ctl_ptr(ep_addr);
- uint8_t const epnum = xfer->ep_idx;
- uint8_t const dir = tu_edpt_dir(ep_addr);
+bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t *ff, uint16_t total_bytes) {
+ uint8_t const ep_num = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+ xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir);
xfer->buffer = NULL;
- xfer->ff = ff;
+ xfer->ff = ff;
xfer->total_len = total_bytes;
xfer->queued_len = 0;
- if ( dir == TUSB_DIR_OUT )
- {
- if(total_bytes > xfer->max_packet_size)
- {
- pcd_set_ep_rx_bufsize(USB,epnum,xfer->max_packet_size);
- } else {
- pcd_set_ep_rx_bufsize(USB,epnum,total_bytes);
- }
- pcd_set_ep_rx_status(USB, epnum, USB_EP_RX_VALID);
- }
- else // IN
- {
- dcd_transmit_packet(xfer,epnum);
- }
- return true;
+ return edpt_xfer(rhport, ep_num, dir);
}
-void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
-{
+void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
(void)rhport;
-
- xfer_ctl_t * xfer = xfer_ctl_ptr(ep_addr);
+ uint8_t const ep_num = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+ xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir);
uint8_t const ep_idx = xfer->ep_idx;
- uint8_t const dir = tu_edpt_dir(ep_addr);
- if (dir == TUSB_DIR_IN)
- { // IN
- pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_STALL);
- }
- else
- { // OUT
- pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_STALL);
- }
+ uint32_t ep_reg = ep_read(ep_idx);
+ ep_reg |= USB_EP_CTR_RX | USB_EP_CTR_TX; // reserve CTR bits
+ ep_reg &= USB_EPREG_MASK | EP_STAT_MASK(dir);
+ ep_reg = ep_add_status(ep_reg, dir, EP_STAT_STALL);
+
+ ep_write(ep_idx, ep_reg);
}
-void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
-{
+void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
(void)rhport;
- xfer_ctl_t * xfer = xfer_ctl_ptr(ep_addr);
+ uint8_t const ep_num = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+ xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir);
uint8_t const ep_idx = xfer->ep_idx;
- uint8_t const dir = tu_edpt_dir(ep_addr);
- if (dir == TUSB_DIR_IN)
- { // IN
- if (pcd_get_eptype(USB, ep_idx) != USB_EP_ISOCHRONOUS) {
- pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_NAK);
- }
+ uint32_t ep_reg = ep_read(ep_idx) | EP_CTR_TXRX;
+ ep_reg &= USB_EPREG_MASK | EP_STAT_MASK(dir) | EP_DTOG_MASK(dir);
- /* Reset to DATA0 if clearing stall condition. */
- pcd_clear_tx_dtog(USB, ep_idx);
- }
- else
- { // OUT
- if (pcd_get_eptype(USB, ep_idx) != USB_EP_ISOCHRONOUS) {
- pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_NAK);
- }
- /* Reset to DATA0 if clearing stall condition. */
- pcd_clear_rx_dtog(USB, ep_idx);
+ if (!ep_is_iso(ep_reg)) {
+ ep_reg = ep_add_status(ep_reg, dir, EP_STAT_NAK);
}
+ ep_reg = ep_add_dtog(ep_reg, dir, 0); // Reset to DATA0
+
+ ep_write(ep_idx, ep_reg);
}
#ifdef FSDEV_BUS_32BIT
-static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t wNBytes)
-{
- const uint8_t* srcVal = src;
- volatile uint32_t* dst32 = (volatile uint32_t*)(USB_PMAADDR + dst);
+static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t wNBytes) {
+ const uint8_t *src8 = src;
+ volatile uint32_t *pma32 = (volatile uint32_t *)(USB_PMAADDR + dst);
for (uint32_t n = wNBytes / 4; n > 0; --n) {
- *dst32++ = tu_unaligned_read32(srcVal);
- srcVal += 4;
+ *pma32++ = tu_unaligned_read32(src8);
+ src8 += 4;
}
- wNBytes = wNBytes & 0x03;
- if (wNBytes)
- {
- uint32_t wrVal = *srcVal;
- wNBytes--;
+ uint16_t odd = wNBytes & 0x03;
+ if (odd) {
+ uint32_t wrVal = *src8;
+ odd--;
- if (wNBytes)
- {
- wrVal |= *++srcVal << 8;
- wNBytes--;
+ if (odd) {
+ wrVal |= *++src8 << 8;
+ odd--;
- if (wNBytes)
- {
- wrVal |= *++srcVal << 16;
+ if (odd) {
+ wrVal |= *++src8 << 16;
}
}
- *dst32 = wrVal;
+ *pma32 = wrVal;
+ }
+
+ return true;
+}
+
+static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t wNBytes) {
+ uint8_t *dst8 = dst;
+ volatile uint32_t *src32 = (volatile uint32_t *)(USB_PMAADDR + src);
+
+ for (uint32_t n = wNBytes / 4; n > 0; --n) {
+ tu_unaligned_write32(dst8, *src32++);
+ dst8 += 4;
+ }
+
+ uint16_t odd = wNBytes & 0x03;
+ if (odd) {
+ uint32_t rdVal = *src32;
+
+ *dst8 = tu_u32_byte0(rdVal);
+ odd--;
+
+ if (odd) {
+ *++dst8 = tu_u32_byte1(rdVal);
+ odd--;
+
+ if (odd) {
+ *++dst8 = tu_u32_byte2(rdVal);
+ }
+ }
}
return true;
}
+
#else
// Packet buffer access can only be 8- or 16-bit.
/**
- * @brief Copy a buffer from user memory area to packet memory area (PMA).
- * This uses byte-access for user memory (so support non-aligned buffers)
- * and 16-bit access for packet memory.
- * @param dst, byte address in PMA; must be 16-bit aligned
- * @param src pointer to user memory area.
- * @param wPMABufAddr address into PMA.
- * @param wNBytes no. of bytes to be copied.
- * @retval None
- */
-static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t wNBytes)
-{
- uint32_t n = (uint32_t)wNBytes >> 1U;
- uint16_t temp1, temp2;
- const uint8_t * srcVal;
+ * @brief Copy a buffer from user memory area to packet memory area (PMA).
+ * This uses un-aligned for user memory and 16-bit access for packet memory.
+ * @param dst, byte address in PMA; must be 16-bit aligned
+ * @param src pointer to user memory area.
+ * @param wPMABufAddr address into PMA.
+ * @param nbytes no. of bytes to be copied.
+ * @retval None
+ */
+static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t nbytes) {
+ uint32_t n16 = (uint32_t)nbytes >> 1U;
+ const uint8_t *src8 = src;
+ fsdev_pma16_t* pma16 = (fsdev_pma16_t*) (USB_PMAADDR + FSDEV_PMA_STRIDE * dst);
- // The GCC optimizer will combine access to 32-bit sizes if we let it. Force
- // it volatile so that it won't do that.
- __IO uint16_t *pdwVal;
+ while (n16--) {
+ pma16->u16 = tu_unaligned_read16(src8);
+ src8 += 2;
+ pma16++;
+ }
+
+ if (nbytes & 0x01) {
+ pma16->u16 = (uint16_t) *src8;
+ }
- srcVal = src;
- pdwVal = &pma[FSDEV_PMA_STRIDE * (dst >> 1)];
+ return true;
+}
- while (n--)
- {
- temp1 = (uint16_t)*srcVal;
- srcVal++;
- temp2 = temp1 | ((uint16_t)(((uint16_t)(*srcVal)) << 8U)) ;
- *pdwVal = temp2;
- pdwVal += FSDEV_PMA_STRIDE;
- srcVal++;
+/**
+ * @brief Copy a buffer from packet memory area (PMA) to user memory area.
+ * Uses unaligned for system memory and 16-bit access of packet memory
+ * @param nbytes no. of bytes to be copied.
+ * @retval None
+ */
+static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t nbytes) {
+ uint32_t n16 = (uint32_t)nbytes >> 1U;
+ fsdev_pma16_t* pma16 = (fsdev_pma16_t*) (USB_PMAADDR + FSDEV_PMA_STRIDE * src);
+ uint8_t *dst8 = (uint8_t *)dst;
+
+ while (n16--) {
+ uint16_t temp16 = pma16->u16;
+ tu_unaligned_write16(dst8, temp16);
+ dst8 += 2;
+ pma16++;
}
- if (wNBytes)
- {
- temp1 = *srcVal;
- *pdwVal = temp1;
+ if (nbytes & 0x01) {
+ *dst8++ = tu_u16_low(pma16->u16);
}
return true;
}
+
#endif
/**
- * @brief Copy from FIFO to packet memory area (PMA).
- * Uses byte-access of system memory and 16-bit access of packet memory
- * @param wNBytes no. of bytes to be copied.
- * @retval None
- */
-static bool dcd_write_packet_memory_ff(tu_fifo_t * ff, uint16_t dst, uint16_t wNBytes)
-{
+ * @brief Copy from FIFO to packet memory area (PMA).
+ * Uses byte-access of system memory and 16-bit access of packet memory
+ * @param wNBytes no. of bytes to be copied.
+ * @retval None
+ */
+static bool dcd_write_packet_memory_ff(tu_fifo_t *ff, uint16_t dst, uint16_t wNBytes) {
// Since we copy from a ring buffer FIFO, a wrap might occur making it necessary to conduct two copies
tu_fifo_buffer_info_t info;
tu_fifo_get_read_info(ff, &info);
- uint16_t cnt_lin = TU_MIN(wNBytes, info.len_lin);
+ uint16_t cnt_lin = TU_MIN(wNBytes, info.len_lin);
uint16_t cnt_wrap = TU_MIN(wNBytes - cnt_lin, info.len_wrap);
// We want to read from the FIFO and write it into the PMA, if LIN part is ODD and has WRAPPED part,
// last lin byte will be combined with wrapped part
// To ensure PMA is always access aligned (dst aligned to 16 or 32 bit)
#ifdef FSDEV_BUS_32BIT
- if((cnt_lin & 0x03) && cnt_wrap)
- {
+ if ((cnt_lin & 0x03) && cnt_wrap) {
// Copy first linear part
- dcd_write_packet_memory(dst, info.ptr_lin, cnt_lin &~0x03);
- dst += cnt_lin &~0x03;
+ dcd_write_packet_memory(dst, info.ptr_lin, cnt_lin & ~0x03);
+ dst += cnt_lin & ~0x03;
// Copy last linear bytes & first wrapped bytes to buffer
uint32_t i;
uint8_t tmp[4];
- for (i = 0; i < (cnt_lin & 0x03); i++)
- {
- tmp[i] = ((uint8_t*)info.ptr_lin)[(cnt_lin &~0x03) + i];
+ for (i = 0; i < (cnt_lin & 0x03); i++) {
+ tmp[i] = ((uint8_t *)info.ptr_lin)[(cnt_lin & ~0x03) + i];
}
uint32_t wCnt = cnt_wrap;
- for (; i < 4 && wCnt > 0; i++, wCnt--)
- {
- tmp[i] = *(uint8_t*)info.ptr_wrap;
- info.ptr_wrap = (uint8_t*)info.ptr_wrap + 1;
+ for (; i < 4 && wCnt > 0; i++, wCnt--) {
+ tmp[i] = *(uint8_t *)info.ptr_wrap;
+ info.ptr_wrap = (uint8_t *)info.ptr_wrap + 1;
}
// Write unaligned buffer
@@ -1362,33 +980,31 @@ static bool dcd_write_packet_memory_ff(tu_fifo_t * ff, uint16_t dst, uint16_t wN
dst += 4;
// Copy rest of wrapped byte
- if (wCnt)
- dcd_write_packet_memory(dst, info.ptr_wrap, wCnt);
+ if (wCnt) {
+ dcd_write_packet_memory(dst, info.ptr_wrap, wCnt);
+ }
}
#else
- if((cnt_lin & 0x01) && cnt_wrap)
- {
+ if ((cnt_lin & 0x01) && cnt_wrap) {
// Copy first linear part
- dcd_write_packet_memory(dst, info.ptr_lin, cnt_lin &~0x01);
- dst += cnt_lin &~0x01;
+ dcd_write_packet_memory(dst, info.ptr_lin, cnt_lin & ~0x01);
+ dst += cnt_lin & ~0x01;
// Copy last linear byte & first wrapped byte
- uint16_t tmp = ((uint8_t*)info.ptr_lin)[cnt_lin - 1] | ((uint16_t)(((uint8_t*)info.ptr_wrap)[0]) << 8U);
+ uint16_t tmp = ((uint8_t *)info.ptr_lin)[cnt_lin - 1] | ((uint16_t)(((uint8_t *)info.ptr_wrap)[0]) << 8U);
dcd_write_packet_memory(dst, &tmp, 2);
dst += 2;
// Copy rest of wrapped byte
- dcd_write_packet_memory(dst, ((uint8_t*)info.ptr_wrap) + 1, cnt_wrap - 1);
+ dcd_write_packet_memory(dst, ((uint8_t *)info.ptr_wrap) + 1, cnt_wrap - 1);
}
#endif
- else
- {
+ else {
// Copy linear part
dcd_write_packet_memory(dst, info.ptr_lin, cnt_lin);
dst += info.len_lin;
- if(info.len_wrap)
- {
+ if (info.len_wrap) {
// Copy wrapped byte
dcd_write_packet_memory(dst, info.ptr_wrap, cnt_wrap);
}
@@ -1399,101 +1015,29 @@ static bool dcd_write_packet_memory_ff(tu_fifo_t * ff, uint16_t dst, uint16_t wN
return true;
}
-#ifdef FSDEV_BUS_32BIT
-static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t wNBytes)
-{
- uint8_t* dstVal = dst;
- volatile uint32_t* src32 = (volatile uint32_t*)(USB_PMAADDR + src);
-
- for (uint32_t n = wNBytes / 4; n > 0; --n) {
- tu_unaligned_write32(dstVal, *src32++);
- dstVal += 4;
- }
-
- wNBytes = wNBytes & 0x03;
- if (wNBytes)
- {
- uint32_t rdVal = *src32;
-
- *dstVal = tu_u32_byte0(rdVal);
- wNBytes--;
-
- if (wNBytes)
- {
- *++dstVal = tu_u32_byte1(rdVal);
- wNBytes--;
-
- if (wNBytes)
- {
- *++dstVal = tu_u32_byte2(rdVal);
- }
- }
- }
-
- return true;
-}
-#else
/**
- * @brief Copy a buffer from packet memory area (PMA) to user memory area.
- * Uses byte-access of system memory and 16-bit access of packet memory
- * @param wNBytes no. of bytes to be copied.
- * @retval None
- */
-static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t wNBytes)
-{
- uint32_t n = (uint32_t)wNBytes >> 1U;
- // The GCC optimizer will combine access to 32-bit sizes if we let it. Force
- // it volatile so that it won't do that.
- __IO const uint16_t *pdwVal;
- uint32_t temp;
-
- pdwVal = &pma[FSDEV_PMA_STRIDE * (src >> 1)];
- uint8_t *dstVal = (uint8_t*)dst;
-
- while (n--)
- {
- temp = *pdwVal;
- pdwVal += FSDEV_PMA_STRIDE;
- *dstVal++ = ((temp >> 0) & 0xFF);
- *dstVal++ = ((temp >> 8) & 0xFF);
- }
-
- if (wNBytes & 0x01)
- {
- temp = *pdwVal;
- pdwVal += FSDEV_PMA_STRIDE;
- *dstVal++ = ((temp >> 0) & 0xFF);
- }
- return true;
-}
-#endif
-
-/**
- * @brief Copy a buffer from user packet memory area (PMA) to FIFO.
- * Uses byte-access of system memory and 16-bit access of packet memory
- * @param wNBytes no. of bytes to be copied.
- * @retval None
- */
-static bool dcd_read_packet_memory_ff(tu_fifo_t * ff, uint16_t src, uint16_t wNBytes)
-{
+ * @brief Copy a buffer from user packet memory area (PMA) to FIFO.
+ * Uses byte-access of system memory and 16-bit access of packet memory
+ * @param wNBytes no. of bytes to be copied.
+ * @retval None
+ */
+static bool dcd_read_packet_memory_ff(tu_fifo_t *ff, uint16_t src, uint16_t wNBytes) {
// Since we copy into a ring buffer FIFO, a wrap might occur making it necessary to conduct two copies
// Check for first linear part
tu_fifo_buffer_info_t info;
- tu_fifo_get_write_info(ff, &info); // We want to read from the FIFO
+ tu_fifo_get_write_info(ff, &info); // We want to read from the FIFO
- uint16_t cnt_lin = TU_MIN(wNBytes, info.len_lin);
+ uint16_t cnt_lin = TU_MIN(wNBytes, info.len_lin);
uint16_t cnt_wrap = TU_MIN(wNBytes - cnt_lin, info.len_wrap);
-
// We want to read from PMA and write it into the FIFO, if LIN part is ODD and has WRAPPED part,
// last lin byte will be combined with wrapped part
// To ensure PMA is always access aligned (src aligned to 16 or 32 bit)
#ifdef FSDEV_BUS_32BIT
- if((cnt_lin & 0x03) && cnt_wrap)
- {
+ if ((cnt_lin & 0x03) && cnt_wrap) {
// Copy first linear part
- dcd_read_packet_memory(info.ptr_lin, src, cnt_lin &~0x03);
- src += cnt_lin &~0x03;
+ dcd_read_packet_memory(info.ptr_lin, src, cnt_lin & ~0x03);
+ src += cnt_lin & ~0x03;
// Copy last linear bytes & first wrapped bytes
uint8_t tmp[4];
@@ -1501,48 +1045,44 @@ static bool dcd_read_packet_memory_ff(tu_fifo_t * ff, uint16_t src, uint16_t wNB
src += 4;
uint32_t i;
- for (i = 0; i < (cnt_lin & 0x03); i++)
- {
- ((uint8_t*)info.ptr_lin)[(cnt_lin &~0x03) + i] = tmp[i];
+ for (i = 0; i < (cnt_lin & 0x03); i++) {
+ ((uint8_t *)info.ptr_lin)[(cnt_lin & ~0x03) + i] = tmp[i];
}
uint32_t wCnt = cnt_wrap;
- for (; i < 4 && wCnt > 0; i++, wCnt--)
- {
- *(uint8_t*)info.ptr_wrap = tmp[i];
- info.ptr_wrap = (uint8_t*)info.ptr_wrap + 1;
+ for (; i < 4 && wCnt > 0; i++, wCnt--) {
+ *(uint8_t *)info.ptr_wrap = tmp[i];
+ info.ptr_wrap = (uint8_t *)info.ptr_wrap + 1;
}
// Copy rest of wrapped byte
- if (wCnt)
+ if (wCnt) {
dcd_read_packet_memory(info.ptr_wrap, src, wCnt);
+ }
}
#else
- if((cnt_lin & 0x01) && cnt_wrap)
- {
+ if ((cnt_lin & 0x01) && cnt_wrap) {
// Copy first linear part
- dcd_read_packet_memory(info.ptr_lin, src, cnt_lin &~0x01);
- src += cnt_lin &~0x01;
+ dcd_read_packet_memory(info.ptr_lin, src, cnt_lin & ~0x01);
+ src += cnt_lin & ~0x01;
// Copy last linear byte & first wrapped byte
uint8_t tmp[2];
dcd_read_packet_memory(tmp, src, 2);
src += 2;
- ((uint8_t*)info.ptr_lin)[cnt_lin - 1] = tmp[0];
- ((uint8_t*)info.ptr_wrap)[0] = tmp[1];
+ ((uint8_t *)info.ptr_lin)[cnt_lin - 1] = tmp[0];
+ ((uint8_t *)info.ptr_wrap)[0] = tmp[1];
// Copy rest of wrapped byte
- dcd_read_packet_memory(((uint8_t*)info.ptr_wrap) + 1, src, cnt_wrap - 1);
+ dcd_read_packet_memory(((uint8_t *)info.ptr_wrap) + 1, src, cnt_wrap - 1);
}
#endif
- else
- {
+ else {
// Copy linear part
dcd_read_packet_memory(info.ptr_lin, src, cnt_lin);
src += cnt_lin;
- if(info.len_wrap)
- {
+ if (info.len_wrap) {
// Copy wrapped byte
dcd_read_packet_memory(info.ptr_wrap, src, cnt_wrap);
}
diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.h b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.h
deleted file mode 100644
index 946ad2c7c..000000000
--- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.h
+++ /dev/null
@@ -1,538 +0,0 @@
-/*
- * Copyright(c) 2016 STMicroelectronics
- * Copyright(c) N Conrad
- * Copyright (c) 2019 Ha Thach (tinyusb.org)
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * 3. Neither the name of STMicroelectronics nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * This file is part of the TinyUSB stack.
- */
-
-// This file contains source copied from ST's HAL, and thus should have their copyright statement.
-
-// FSDEV_PMA_SIZE is PMA buffer size in bytes.
-// On 512-byte devices, access with a stride of two words (use every other 16-bit address)
-// On 1024-byte devices, access with a stride of one word (use every 16-bit address)
-
-#ifndef PORTABLE_ST_STM32F0_DCD_STM32F0_FSDEV_PVT_ST_H_
-#define PORTABLE_ST_STM32F0_DCD_STM32F0_FSDEV_PVT_ST_H_
-
-#if CFG_TUSB_MCU == OPT_MCU_STM32F0
- #include "stm32f0xx.h"
- #define FSDEV_PMA_SIZE (1024u)
- // F0x2 models are crystal-less
- // All have internal D+ pull-up
- // 070RB: 2 x 16 bits/word memory LPM Support, BCD Support
- // PMA dedicated to USB (no sharing with CAN)
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32F1
- #include "stm32f1xx.h"
- #define FSDEV_PMA_SIZE (512u)
- // NO internal Pull-ups
- // *B, and *C: 2 x 16 bits/word
-
- // F1 names this differently from the rest
- #define USB_CNTR_LPMODE USB_CNTR_LP_MODE
-
-#elif defined(STM32F302xB) || defined(STM32F302xC) || \
- defined(STM32F303xB) || defined(STM32F303xC) || \
- defined(STM32F373xC)
- #include "stm32f3xx.h"
- #define FSDEV_PMA_SIZE (512u)
- // NO internal Pull-ups
- // *B, and *C: 1 x 16 bits/word
- // PMA dedicated to USB (no sharing with CAN)
-
-#elif defined(STM32F302x6) || defined(STM32F302x8) || \
- defined(STM32F302xD) || defined(STM32F302xE) || \
- defined(STM32F303xD) || defined(STM32F303xE)
- #include "stm32f3xx.h"
- #define FSDEV_PMA_SIZE (1024u)
- // NO internal Pull-ups
- // *6, *8, *D, and *E: 2 x 16 bits/word LPM Support
- // When CAN clock is enabled, USB can use first 768 bytes ONLY.
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32L0
- #include "stm32l0xx.h"
- #define FSDEV_PMA_SIZE (1024u)
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32L1
- #include "stm32l1xx.h"
- #define FSDEV_PMA_SIZE (512u)
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32G4
- #include "stm32g4xx.h"
- #define FSDEV_PMA_SIZE (1024u)
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32G0
- #include "stm32g0xx.h"
- #define FSDEV_BUS_32BIT
- #define FSDEV_PMA_SIZE (2048u)
- #undef USB_PMAADDR
- #define USB_PMAADDR USB_DRD_PMAADDR
- #define USB_TypeDef USB_DRD_TypeDef
- #define EP0R CHEP0R
- #define USB_EP_CTR_RX USB_EP_VTRX
- #define USB_EP_CTR_TX USB_EP_VTTX
- #define USB_EP_T_FIELD USB_CHEP_UTYPE
- #define USB_EPREG_MASK USB_CHEP_REG_MASK
- #define USB_EPTX_DTOGMASK USB_CHEP_TX_DTOGMASK
- #define USB_EPRX_DTOGMASK USB_CHEP_RX_DTOGMASK
- #define USB_EPTX_DTOG1 USB_CHEP_TX_DTOG1
- #define USB_EPTX_DTOG2 USB_CHEP_TX_DTOG2
- #define USB_EPRX_DTOG1 USB_CHEP_RX_DTOG1
- #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2
- #define USB_EPRX_STAT USB_CH_RX_VALID
- #define USB_EPKIND_MASK USB_EP_KIND_MASK
- #define USB USB_DRD_FS
- #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_BUS_32BIT
-
- #if !defined(USB_DRD_BASE) && defined(USB_DRD_FS_BASE)
- #define USB_DRD_BASE USB_DRD_FS_BASE
- #endif
-
- #define FSDEV_PMA_SIZE (2048u)
- #undef USB_PMAADDR
- #define USB_PMAADDR USB_DRD_PMAADDR
- #define USB_TypeDef USB_DRD_TypeDef
- #define EP0R CHEP0R
- #define USB_EP_CTR_RX USB_EP_VTRX
- #define USB_EP_CTR_TX USB_EP_VTTX
- #define USB_EP_T_FIELD USB_CHEP_UTYPE
- #define USB_EPREG_MASK USB_CHEP_REG_MASK
- #define USB_EPTX_DTOGMASK USB_CHEP_TX_DTOGMASK
- #define USB_EPRX_DTOGMASK USB_CHEP_RX_DTOGMASK
- #define USB_EPTX_DTOG1 USB_CHEP_TX_DTOG1
- #define USB_EPTX_DTOG2 USB_CHEP_TX_DTOG2
- #define USB_EPRX_DTOG1 USB_CHEP_RX_DTOG1
- #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2
- #define USB_EPRX_STAT USB_CH_RX_VALID
- #define USB_EPKIND_MASK USB_EP_KIND_MASK
- #define USB USB_DRD_FS
- #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_STM32WB
- #include "stm32wbxx.h"
- #define FSDEV_PMA_SIZE (1024u)
- /* ST provided header has incorrect value */
- #undef USB_PMAADDR
- #define USB_PMAADDR USB1_PMAADDR
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32L4
- #include "stm32l4xx.h"
- #define FSDEV_PMA_SIZE (1024u)
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32L5
- #include "stm32l5xx.h"
- #define FSDEV_PMA_SIZE (1024u)
-
- #ifndef USB_PMAADDR
- #define USB_PMAADDR (USB_BASE + (USB_PMAADDR_NS - USB_BASE_NS))
- #endif
-
-#else
- #error You are using an untested or unimplemented STM32 variant. Please update the driver.
- // This includes L1x0, L1x1, L1x2, L4x2 and L4x3, G1x1, G1x3, and G1x4
-#endif
-
-// For purposes of accessing the packet
-#if ((FSDEV_PMA_SIZE) == 512u)
- #define FSDEV_PMA_STRIDE (2u)
-#elif ((FSDEV_PMA_SIZE) == 1024u)
- #define FSDEV_PMA_STRIDE (1u)
-#endif
-
-// The fsdev_bus_t type can be used for both register and PMA access necessities
-// For type-safety create a new macro for the volatile address of PMAADDR
-// The compiler should warn us if we cast it to a non-volatile type?
-#ifdef FSDEV_BUS_32BIT
-typedef uint32_t fsdev_bus_t;
-static __IO uint32_t * const pma32 = (__IO uint32_t*)USB_PMAADDR;
-
-#else
-typedef uint16_t fsdev_bus_t;
-// Volatile is also needed to prevent the optimizer from changing access to 32-bit (as 32-bit access is forbidden)
-static __IO uint16_t * const pma = (__IO uint16_t*)USB_PMAADDR;
-
-TU_ATTR_ALWAYS_INLINE static inline __IO uint16_t * pcd_btable_word_ptr(USB_TypeDef * USBx, size_t x) {
- size_t total_word_offset = (((USBx)->BTABLE)>>1) + x;
- total_word_offset *= FSDEV_PMA_STRIDE;
- return &(pma[total_word_offset]);
-}
-
-TU_ATTR_ALWAYS_INLINE static inline __IO uint16_t* pcd_ep_tx_cnt_ptr(USB_TypeDef * USBx, uint32_t bEpIdx) {
- return pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 1u);
-}
-
-TU_ATTR_ALWAYS_INLINE static inline __IO uint16_t* pcd_ep_rx_cnt_ptr(USB_TypeDef * USBx, uint32_t bEpIdx) {
- return pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 3u);
-}
-#endif
-
-/* Aligned buffer size according to hardware */
-TU_ATTR_ALWAYS_INLINE static inline uint16_t pcd_aligned_buffer_size(uint16_t size) {
- /* The STM32 full speed USB peripheral supports only a limited set of
- * buffer sizes given by the RX buffer entry format in the USB_BTABLE. */
- uint16_t blocksize = (size > 62) ? 32 : 2;
-
- // Round up while dividing requested size by blocksize
- uint16_t numblocks = (size + blocksize - 1) / blocksize ;
-
- return numblocks * blocksize;
-}
-
-TU_ATTR_ALWAYS_INLINE static inline void pcd_set_endpoint(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wRegValue) {
-#ifdef FSDEV_BUS_32BIT
- (void) USBx;
- __O uint32_t *reg = (__O uint32_t *)(USB_DRD_BASE + bEpIdx*4);
- *reg = wRegValue;
-#else
- __O uint16_t *reg = (__O uint16_t *)((&USBx->EP0R) + bEpIdx*2u);
- *reg = (uint16_t)wRegValue;
-#endif
-}
-
-TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_endpoint(USB_TypeDef * USBx, uint32_t bEpIdx) {
-#ifdef FSDEV_BUS_32BIT
- (void) USBx;
- __I uint32_t *reg = (__I uint32_t *)(USB_DRD_BASE + bEpIdx*4);
-#else
- __I uint16_t *reg = (__I uint16_t *)((&USBx->EP0R) + bEpIdx*2u);
-#endif
- return *reg;
-}
-
-TU_ATTR_ALWAYS_INLINE static inline void pcd_set_eptype(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wType) {
- uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
- regVal &= (uint32_t)USB_EP_T_MASK;
- regVal |= wType;
- regVal |= USB_EP_CTR_RX | USB_EP_CTR_TX; // These clear on write0, so must set high
- pcd_set_endpoint(USBx, bEpIdx, regVal);
-}
-
-TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_eptype(USB_TypeDef * USBx, uint32_t bEpIdx) {
- uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
- regVal &= USB_EP_T_FIELD;
- return regVal;
-}
-
-/**
- * @brief Clears bit CTR_RX / CTR_TX in the endpoint register.
- * @param USBx USB peripheral instance register address.
- * @param bEpIdx Endpoint Number.
- * @retval None
- */
-TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_rx_ep_ctr(USB_TypeDef * USBx, uint32_t bEpIdx) {
- uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
- regVal &= USB_EPREG_MASK;
- regVal &= ~USB_EP_CTR_RX;
- regVal |= USB_EP_CTR_TX; // preserve CTR_TX (clears on writing 0)
- pcd_set_endpoint(USBx, bEpIdx, regVal);
-}
-
-TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_tx_ep_ctr(USB_TypeDef * USBx, uint32_t bEpIdx) {
- uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
- regVal &= USB_EPREG_MASK;
- regVal &= ~USB_EP_CTR_TX;
- regVal |= USB_EP_CTR_RX; // preserve CTR_RX (clears on writing 0)
- pcd_set_endpoint(USBx, bEpIdx,regVal);
-}
-
-/**
- * @brief gets counter of the tx buffer.
- * @param USBx USB peripheral instance register address.
- * @param bEpIdx Endpoint Number.
- * @retval Counter value
- */
-TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_tx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx) {
-#ifdef FSDEV_BUS_32BIT
- (void) USBx;
- return (pma32[2*bEpIdx] & 0x03FF0000) >> 16;
-#else
- __I uint16_t *regPtr = pcd_ep_tx_cnt_ptr(USBx, bEpIdx);
- return *regPtr & 0x3ffU;
-#endif
-}
-
-TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx) {
-#ifdef FSDEV_BUS_32BIT
- (void) USBx;
- return (pma32[2*bEpIdx + 1] & 0x03FF0000) >> 16;
-#else
- __I uint16_t *regPtr = pcd_ep_rx_cnt_ptr(USBx, bEpIdx);
- return *regPtr & 0x3ffU;
-#endif
-}
-
-/**
- * @brief Sets address in an endpoint register.
- * @param USBx USB peripheral instance register address.
- * @param bEpIdx Endpoint Number.
- * @param bAddr Address.
- * @retval None
- */
-TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_address(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t bAddr) {
- uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
- regVal &= USB_EPREG_MASK;
- regVal |= bAddr;
- regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX;
- pcd_set_endpoint(USBx, bEpIdx,regVal);
-}
-
-TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_tx_address(USB_TypeDef * USBx, uint32_t bEpIdx) {
-#ifdef FSDEV_BUS_32BIT
- (void) USBx;
- return pma32[2*bEpIdx] & 0x0000FFFFu ;
-#else
- return *pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 0u);
-#endif
-}
-
-TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_address(USB_TypeDef * USBx, uint32_t bEpIdx) {
-#ifdef FSDEV_BUS_32BIT
- (void) USBx;
- return pma32[2*bEpIdx + 1] & 0x0000FFFFu;
-#else
- return *pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 2u);
-#endif
-}
-
-TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_address(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t addr) {
-#ifdef FSDEV_BUS_32BIT
- (void) USBx;
- pma32[2*bEpIdx] = (pma32[2*bEpIdx] & 0xFFFF0000u) | (addr & 0x0000FFFCu);
-#else
- *pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 0u) = addr;
-#endif
-}
-
-TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_address(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t addr) {
-#ifdef FSDEV_BUS_32BIT
- (void) USBx;
- pma32[2*bEpIdx + 1] = (pma32[2*bEpIdx + 1] & 0xFFFF0000u) | (addr & 0x0000FFFCu);
-#else
- *pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 2u) = addr;
-#endif
-}
-
-TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount) {
-#ifdef FSDEV_BUS_32BIT
- (void) USBx;
- pma32[2*bEpIdx] = (pma32[2*bEpIdx] & ~0x03FF0000u) | ((wCount & 0x3FFu) << 16);
-#else
- __IO uint16_t * reg = pcd_ep_tx_cnt_ptr(USBx, bEpIdx);
- *reg = (uint16_t) (*reg & (uint16_t) ~0x3FFU) | (wCount & 0x3FFU);
-#endif
-}
-
-TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount) {
-#ifdef FSDEV_BUS_32BIT
- (void) USBx;
- pma32[2*bEpIdx + 1] = (pma32[2*bEpIdx + 1] & ~0x03FF0000u) | ((wCount & 0x3FFu) << 16);
-#else
- __IO uint16_t * reg = pcd_ep_rx_cnt_ptr(USBx, bEpIdx);
- *reg = (uint16_t) (*reg & (uint16_t) ~0x3FFU) | (wCount & 0x3FFU);
-#endif
-}
-
-TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_blsize_num_blocks(USB_TypeDef * USBx, uint32_t rxtx_idx,
- uint32_t blocksize, uint32_t numblocks) {
- /* Encode into register. When BLSIZE==1, we need to subtract 1 block count */
-#ifdef FSDEV_BUS_32BIT
- (void) USBx;
- pma32[rxtx_idx] = (pma32[rxtx_idx] & 0x0000FFFFu) | (blocksize << 31) | ((numblocks - blocksize) << 26);
-#else
- __IO uint16_t *pdwReg = pcd_btable_word_ptr(USBx, rxtx_idx*2u + 1u);
- *pdwReg = (blocksize << 15) | ((numblocks - blocksize) << 10);
-#endif
-}
-
-TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_bufsize(USB_TypeDef * USBx, uint32_t rxtx_idx, uint32_t wCount) {
- wCount = pcd_aligned_buffer_size(wCount);
-
- /* We assume that the buffer size is already aligned to hardware requirements. */
- uint16_t blocksize = (wCount > 62) ? 1 : 0;
- uint16_t numblocks = wCount / (blocksize ? 32 : 2);
-
- /* There should be no remainder in the above calculation */
- TU_ASSERT((wCount - (numblocks * (blocksize ? 32 : 2))) == 0, /**/);
-
- /* Encode into register. When BLSIZE==1, we need to subtract 1 block count */
- pcd_set_ep_blsize_num_blocks(USBx, rxtx_idx, blocksize, numblocks);
-}
-
-TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_bufsize(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount) {
- pcd_set_ep_bufsize(USBx, 2*bEpIdx, wCount);
-}
-
-TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_bufsize(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount) {
- pcd_set_ep_bufsize(USBx, 2*bEpIdx + 1, wCount);
-}
-
-/**
- * @brief sets the status for tx transfer (bits STAT_TX[1:0]).
- * @param USBx USB peripheral instance register address.
- * @param bEpIdx Endpoint Number.
- * @param wState new state
- * @retval None
- */
-TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_status(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wState) {
- uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
- regVal &= USB_EPTX_DTOGMASK;
-
- /* toggle first bit ? */
- if((USB_EPTX_DTOG1 & (wState))!= 0U)
- {
- regVal ^= USB_EPTX_DTOG1;
- }
- /* toggle second bit ? */
- if((USB_EPTX_DTOG2 & ((uint32_t)(wState)))!= 0U)
- {
- regVal ^= USB_EPTX_DTOG2;
- }
-
- regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX;
- pcd_set_endpoint(USBx, bEpIdx, regVal);
-}
-
-/**
- * @brief sets the status for rx transfer (bits STAT_TX[1:0])
- * @param USBx USB peripheral instance register address.
- * @param bEpIdx Endpoint Number.
- * @param wState new state
- * @retval None
- */
-
-TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_status(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wState) {
- uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
- regVal &= USB_EPRX_DTOGMASK;
-
- /* toggle first bit ? */
- if((USB_EPRX_DTOG1 & wState)!= 0U) {
- regVal ^= USB_EPRX_DTOG1;
- }
- /* toggle second bit ? */
- if((USB_EPRX_DTOG2 & wState)!= 0U) {
- regVal ^= USB_EPRX_DTOG2;
- }
-
- regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX;
- pcd_set_endpoint(USBx, bEpIdx, regVal);
-}
-
-TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_status(USB_TypeDef * USBx, uint32_t bEpIdx) {
- uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
- return (regVal & USB_EPRX_STAT) >> (12u);
-}
-
-
-/**
- * @brief Toggles DTOG_RX / DTOG_TX bit in the endpoint register.
- * @param USBx USB peripheral instance register address.
- * @param bEpIdx Endpoint Number.
- * @retval None
- */
-TU_ATTR_ALWAYS_INLINE static inline void pcd_rx_dtog(USB_TypeDef * USBx, uint32_t bEpIdx) {
- uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
- regVal &= USB_EPREG_MASK;
- regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX|USB_EP_DTOG_RX;
- pcd_set_endpoint(USBx, bEpIdx, regVal);
-}
-
-TU_ATTR_ALWAYS_INLINE static inline void pcd_tx_dtog(USB_TypeDef * USBx, uint32_t bEpIdx) {
- uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
- regVal &= USB_EPREG_MASK;
- regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX|USB_EP_DTOG_TX;
- pcd_set_endpoint(USBx, bEpIdx, regVal);
-}
-
-/**
- * @brief Clears DTOG_RX / DTOG_TX bit in the endpoint register.
- * @param USBx USB peripheral instance register address.
- * @param bEpIdx Endpoint Number.
- * @retval None
- */
-TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_rx_dtog(USB_TypeDef * USBx, uint32_t bEpIdx) {
- uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
- if((regVal & USB_EP_DTOG_RX) != 0) {
- pcd_rx_dtog(USBx,bEpIdx);
- }
-}
-
-TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_tx_dtog(USB_TypeDef * USBx, uint32_t bEpIdx) {
- uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
- if((regVal & USB_EP_DTOG_TX) != 0) {
- pcd_tx_dtog(USBx,bEpIdx);
- }
-}
-
-/**
- * @brief set & clear EP_KIND bit.
- * @param USBx USB peripheral instance register address.
- * @param bEpIdx Endpoint Number.
- * @retval None
- */
-TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_kind(USB_TypeDef * USBx, uint32_t bEpIdx) {
- uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
- regVal |= USB_EP_KIND;
- regVal &= USB_EPREG_MASK;
- regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX;
- pcd_set_endpoint(USBx, bEpIdx, regVal);
-}
-
-TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_ep_kind(USB_TypeDef * USBx, uint32_t bEpIdx) {
- uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
- regVal &= USB_EPKIND_MASK;
- regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX;
- pcd_set_endpoint(USBx, bEpIdx, regVal);
-}
-
-// This checks if the device has "LPM"
-#if defined(USB_ISTR_L1REQ)
-#define USB_ISTR_L1REQ_FORCED (USB_ISTR_L1REQ)
-#else
-#define USB_ISTR_L1REQ_FORCED ((uint16_t)0x0000U)
-#endif
-
-#define USB_ISTR_ALL_EVENTS (USB_ISTR_PMAOVR | USB_ISTR_ERR | USB_ISTR_WKUP | USB_ISTR_SUSP | \
- USB_ISTR_RESET | USB_ISTR_SOF | USB_ISTR_ESOF | USB_ISTR_L1REQ_FORCED )
-
-// Number of endpoints in hardware
-// TODO should use TUP_DCD_ENDPOINT_MAX
-#define STFSDEV_EP_COUNT (8u)
-
-#endif /* PORTABLE_ST_STM32F0_DCD_STM32F0_FSDEV_PVT_ST_H_ */
diff --git a/src/portable/st/stm32_fsdev/fsdev_ch32.h b/src/portable/st/stm32_fsdev/fsdev_ch32.h
new file mode 100644
index 000000000..8c0961bb9
--- /dev/null
+++ b/src/portable/st/stm32_fsdev/fsdev_ch32.h
@@ -0,0 +1,220 @@
+/*
+* The MIT License (MIT)
+ *
+ * Copyright (c) 2024, hathach (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.
+ *
+ */
+/** <h2><center>&copy; Copyright (c) 2016 STMicroelectronics.
+ * All rights reserved.</center></h2>
+ *
+ * This software component is licensed by ST under BSD 3-Clause license,
+ * the "License"; You may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ * opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef TUSB_FSDEV_CH32_H
+#define TUSB_FSDEV_CH32_H
+
+#include "common/tusb_compiler.h"
+
+// https://github.com/openwch/ch32v307/pull/90
+// https://github.com/openwch/ch32v20x/pull/12
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstrict-prototypes"
+#endif
+
+#if CFG_TUSB_MCU == OPT_MCU_CH32F20X
+ #include <ch32f20x.h>
+#elif CFG_TUSB_MCU == OPT_MCU_CH32V20X
+ #include <ch32v20x.h>
+#endif
+
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+#define FSDEV_PMA_SIZE (512u)
+#define FSDEV_REG_BASE 0x40005C00UL
+
+#define USB_BASE (APB1PERIPH_BASE + 0x00005C00UL) /*!< USB_IP Peripheral Registers base address */
+#define USB_PMAADDR (APB1PERIPH_BASE + 0x00006000UL) /*!< USB_IP Packet Memory Area base address */
+#define USB ((USB_TypeDef *)USB_BASE)
+
+/******************************************************************************/
+/* */
+/* USB Device General registers */
+/* */
+/******************************************************************************/
+#define USB_CNTR (USB_BASE + 0x40U) /*!< Control register */
+#define USB_ISTR (USB_BASE + 0x44U) /*!< Interrupt status register */
+#define USB_FNR (USB_BASE + 0x48U) /*!< Frame number register */
+#define USB_DADDR (USB_BASE + 0x4CU) /*!< Device address register */
+#define USB_BTABLE (USB_BASE + 0x50U) /*!< Buffer Table address register */
+
+/**************************** ISTR interrupt events *************************/
+#define USB_ISTR_CTR ((uint16_t)0x8000U) /*!< Correct TRansfer (clear-only bit) */
+#define USB_ISTR_PMAOVR ((uint16_t)0x4000U) /*!< DMA OVeR/underrun (clear-only bit) */
+#define USB_ISTR_ERR ((uint16_t)0x2000U) /*!< ERRor (clear-only bit) */
+#define USB_ISTR_WKUP ((uint16_t)0x1000U) /*!< WaKe UP (clear-only bit) */
+#define USB_ISTR_SUSP ((uint16_t)0x0800U) /*!< SUSPend (clear-only bit) */
+#define USB_ISTR_RESET ((uint16_t)0x0400U) /*!< RESET (clear-only bit) */
+#define USB_ISTR_SOF ((uint16_t)0x0200U) /*!< Start Of Frame (clear-only bit) */
+#define USB_ISTR_ESOF ((uint16_t)0x0100U) /*!< Expected Start Of Frame (clear-only bit) */
+#define USB_ISTR_DIR ((uint16_t)0x0010U) /*!< DIRection of transaction (read-only bit) */
+#define USB_ISTR_EP_ID ((uint16_t)0x000FU) /*!< EndPoint IDentifier (read-only bit) */
+
+/* Legacy defines */
+#define USB_ISTR_PMAOVRM USB_ISTR_PMAOVR
+
+#define USB_CLR_CTR (~USB_ISTR_CTR) /*!< clear Correct TRansfer bit */
+#define USB_CLR_PMAOVR (~USB_ISTR_PMAOVR) /*!< clear DMA OVeR/underrun bit*/
+#define USB_CLR_ERR (~USB_ISTR_ERR) /*!< clear ERRor bit */
+#define USB_CLR_WKUP (~USB_ISTR_WKUP) /*!< clear WaKe UP bit */
+#define USB_CLR_SUSP (~USB_ISTR_SUSP) /*!< clear SUSPend bit */
+#define USB_CLR_RESET (~USB_ISTR_RESET) /*!< clear RESET bit */
+#define USB_CLR_SOF (~USB_ISTR_SOF) /*!< clear Start Of Frame bit */
+#define USB_CLR_ESOF (~USB_ISTR_ESOF) /*!< clear Expected Start Of Frame bit */
+
+/* Legacy defines */
+#define USB_CLR_PMAOVRM USB_CLR_PMAOVR
+
+/************************* CNTR control register bits definitions ***********/
+#define USB_CNTR_CTRM ((uint16_t)0x8000U) /*!< Correct TRansfer Mask */
+#define USB_CNTR_PMAOVR ((uint16_t)0x4000U) /*!< DMA OVeR/underrun Mask */
+#define USB_CNTR_ERRM ((uint16_t)0x2000U) /*!< ERRor Mask */
+#define USB_CNTR_WKUPM ((uint16_t)0x1000U) /*!< WaKe UP Mask */
+#define USB_CNTR_SUSPM ((uint16_t)0x0800U) /*!< SUSPend Mask */
+#define USB_CNTR_RESETM ((uint16_t)0x0400U) /*!< RESET Mask */
+#define USB_CNTR_SOFM ((uint16_t)0x0200U) /*!< Start Of Frame Mask */
+#define USB_CNTR_ESOFM ((uint16_t)0x0100U) /*!< Expected Start Of Frame Mask */
+#define USB_CNTR_RESUME ((uint16_t)0x0010U) /*!< RESUME request */
+#define USB_CNTR_FSUSP ((uint16_t)0x0008U) /*!< Force SUSPend */
+#define USB_CNTR_LPMODE ((uint16_t)0x0004U) /*!< Low-power MODE */
+#define USB_CNTR_PDWN ((uint16_t)0x0002U) /*!< Power DoWN */
+#define USB_CNTR_FRES ((uint16_t)0x0001U) /*!< Force USB RESet */
+
+/* Legacy defines */
+#define USB_CNTR_PMAOVRM USB_CNTR_PMAOVR
+#define USB_CNTR_LP_MODE USB_CNTR_LPMODE
+
+/******************** FNR Frame Number Register bit definitions ************/
+#define USB_FNR_RXDP ((uint16_t)0x8000U) /*!< status of D+ data line */
+#define USB_FNR_RXDM ((uint16_t)0x4000U) /*!< status of D- data line */
+#define USB_FNR_LCK ((uint16_t)0x2000U) /*!< LoCKed */
+#define USB_FNR_LSOF ((uint16_t)0x1800U) /*!< Lost SOF */
+#define USB_FNR_FN ((uint16_t)0x07FFU) /*!< Frame Number */
+
+/******************** DADDR Device ADDRess bit definitions ****************/
+#define USB_DADDR_EF ((uint8_t)0x80U) /*!< USB device address Enable Function */
+#define USB_DADDR_ADD ((uint8_t)0x7FU) /*!< USB device address */
+
+/****************************** Endpoint register *************************/
+#define USB_EP0R USB_BASE /*!< endpoint 0 register address */
+#define USB_EP1R (USB_BASE + 0x04U) /*!< endpoint 1 register address */
+#define USB_EP2R (USB_BASE + 0x08U) /*!< endpoint 2 register address */
+#define USB_EP3R (USB_BASE + 0x0CU) /*!< endpoint 3 register address */
+#define USB_EP4R (USB_BASE + 0x10U) /*!< endpoint 4 register address */
+#define USB_EP5R (USB_BASE + 0x14U) /*!< endpoint 5 register address */
+#define USB_EP6R (USB_BASE + 0x18U) /*!< endpoint 6 register address */
+#define USB_EP7R (USB_BASE + 0x1CU) /*!< endpoint 7 register address */
+/* bit positions */
+#define USB_EP_CTR_RX ((uint16_t)0x8000U) /*!< EndPoint Correct TRansfer RX */
+#define USB_EP_DTOG_RX ((uint16_t)0x4000U) /*!< EndPoint Data TOGGLE RX */
+#define USB_EPRX_STAT ((uint16_t)0x3000U) /*!< EndPoint RX STATus bit field */
+#define USB_EP_SETUP ((uint16_t)0x0800U) /*!< EndPoint SETUP */
+#define USB_EP_T_FIELD ((uint16_t)0x0600U) /*!< EndPoint TYPE */
+#define USB_EP_KIND ((uint16_t)0x0100U) /*!< EndPoint KIND */
+#define USB_EP_CTR_TX ((uint16_t)0x0080U) /*!< EndPoint Correct TRansfer TX */
+#define USB_EP_DTOG_TX ((uint16_t)0x0040U) /*!< EndPoint Data TOGGLE TX */
+#define USB_EPTX_STAT ((uint16_t)0x0030U) /*!< EndPoint TX STATus bit field */
+#define USB_EPADDR_FIELD ((uint16_t)0x000FU) /*!< EndPoint ADDRess FIELD */
+
+/* EndPoint REGister MASK (no toggle fields) */
+#define USB_EPREG_MASK (USB_EP_CTR_RX|USB_EP_SETUP|USB_EP_T_FIELD|USB_EP_KIND|USB_EP_CTR_TX|USB_EPADDR_FIELD)
+ /*!< EP_TYPE[1:0] EndPoint TYPE */
+#define USB_EP_TYPE_MASK ((uint16_t)0x0600U) /*!< EndPoint TYPE Mask */
+#define USB_EP_BULK ((uint16_t)0x0000U) /*!< EndPoint BULK */
+#define USB_EP_CONTROL ((uint16_t)0x0200U) /*!< EndPoint CONTROL */
+#define USB_EP_ISOCHRONOUS ((uint16_t)0x0400U) /*!< EndPoint ISOCHRONOUS */
+#define USB_EP_INTERRUPT ((uint16_t)0x0600U) /*!< EndPoint INTERRUPT */
+#define USB_EP_T_MASK ((uint16_t) ~USB_EP_T_FIELD & USB_EPREG_MASK)
+
+#define USB_EPKIND_MASK ((uint16_t) ~USB_EP_KIND & USB_EPREG_MASK) /*!< EP_KIND EndPoint KIND */
+ /*!< STAT_TX[1:0] STATus for TX transfer */
+#define USB_EP_TX_DIS ((uint16_t)0x0000U) /*!< EndPoint TX DISabled */
+#define USB_EP_TX_STALL ((uint16_t)0x0010U) /*!< EndPoint TX STALLed */
+#define USB_EP_TX_NAK ((uint16_t)0x0020U) /*!< EndPoint TX NAKed */
+#define USB_EP_TX_VALID ((uint16_t)0x0030U) /*!< EndPoint TX VALID */
+#define USB_EPTX_DTOG1 ((uint16_t)0x0010U) /*!< EndPoint TX Data TOGgle bit1 */
+#define USB_EPTX_DTOG2 ((uint16_t)0x0020U) /*!< EndPoint TX Data TOGgle bit2 */
+#define USB_EPTX_DTOGMASK (USB_EPTX_STAT|USB_EPREG_MASK)
+ /*!< STAT_RX[1:0] STATus for RX transfer */
+#define USB_EP_RX_DIS ((uint16_t)0x0000U) /*!< EndPoint RX DISabled */
+#define USB_EP_RX_STALL ((uint16_t)0x1000U) /*!< EndPoint RX STALLed */
+#define USB_EP_RX_NAK ((uint16_t)0x2000U) /*!< EndPoint RX NAKed */
+#define USB_EP_RX_VALID ((uint16_t)0x3000U) /*!< EndPoint RX VALID */
+#define USB_EPRX_DTOG1 ((uint16_t)0x1000U) /*!< EndPoint RX Data TOGgle bit1 */
+#define USB_EPRX_DTOG2 ((uint16_t)0x2000U) /*!< EndPoint RX Data TOGgle bit1 */
+#define USB_EPRX_DTOGMASK (USB_EPRX_STAT|USB_EPREG_MASK)
+
+
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+
+#if CFG_TUSB_MCU == OPT_MCU_CH32V20X
+static const IRQn_Type fsdev_irq[] = {
+ USB_HP_CAN1_TX_IRQn,
+ USB_LP_CAN1_RX0_IRQn,
+ USBWakeUp_IRQn
+};
+enum { FSDEV_IRQ_NUM = TU_ARRAY_SIZE(fsdev_irq) };
+#else
+ #error "Unsupported MCU"
+#endif
+
+void dcd_int_enable(uint8_t rhport) {
+ (void)rhport;
+ for(uint8_t i=0; i < FSDEV_IRQ_NUM; i++) {
+ NVIC_EnableIRQ(fsdev_irq[i]);
+ }
+}
+
+void dcd_int_disable(uint8_t rhport) {
+ (void)rhport;
+ for(uint8_t i=0; i < FSDEV_IRQ_NUM; i++) {
+ NVIC_DisableIRQ(fsdev_irq[i]);
+ }
+}
+
+void dcd_disconnect(uint8_t rhport) {
+ (void) rhport;
+ EXTEN->EXTEN_CTR &= ~EXTEN_USBD_PU_EN;
+}
+
+void dcd_connect(uint8_t rhport) {
+ (void) rhport;
+ EXTEN->EXTEN_CTR |= EXTEN_USBD_PU_EN;
+}
+
+#endif
diff --git a/src/portable/st/stm32_fsdev/fsdev_stm32.h b/src/portable/st/stm32_fsdev/fsdev_stm32.h
new file mode 100644
index 000000000..bb2c72fd1
--- /dev/null
+++ b/src/portable/st/stm32_fsdev/fsdev_stm32.h
@@ -0,0 +1,334 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright(c) N Conrad
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of STMicroelectronics nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef TUSB_FSDEV_STM32_H
+#define TUSB_FSDEV_STM32_H
+
+#if CFG_TUSB_MCU == OPT_MCU_STM32F0
+ #include "stm32f0xx.h"
+ #define FSDEV_PMA_SIZE (1024u)
+ #define FSDEV_REG_BASE USB_BASE
+ // F0x2 models are crystal-less
+ // All have internal D+ pull-up
+ // 070RB: 2 x 16 bits/word memory LPM Support, BCD Support
+ // PMA dedicated to USB (no sharing with CAN)
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32F1
+ #include "stm32f1xx.h"
+ #define FSDEV_PMA_SIZE (512u)
+ // NO internal Pull-ups
+ // *B, and *C: 2 x 16 bits/word
+
+ // F1 names this differently from the rest
+ #define USB_CNTR_LPMODE USB_CNTR_LP_MODE
+
+#elif defined(STM32F302xB) || defined(STM32F302xC) || \
+ defined(STM32F303xB) || defined(STM32F303xC) || \
+ defined(STM32F373xC)
+ #include "stm32f3xx.h"
+ #define FSDEV_PMA_SIZE (512u)
+ // NO internal Pull-ups
+ // *B, and *C: 1 x 16 bits/word
+ // PMA dedicated to USB (no sharing with CAN)
+
+#elif defined(STM32F302x6) || defined(STM32F302x8) || \
+ defined(STM32F302xD) || defined(STM32F302xE) || \
+ defined(STM32F303xD) || defined(STM32F303xE)
+ #include "stm32f3xx.h"
+ #define FSDEV_PMA_SIZE (1024u)
+ // NO internal Pull-ups
+ // *6, *8, *D, and *E: 2 x 16 bits/word LPM Support
+ // When CAN clock is enabled, USB can use first 768 bytes ONLY.
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32L0
+ #include "stm32l0xx.h"
+ #define FSDEV_PMA_SIZE (1024u)
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32L1
+ #include "stm32l1xx.h"
+ #define FSDEV_PMA_SIZE (512u)
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32G4
+ #include "stm32g4xx.h"
+ #define FSDEV_PMA_SIZE (1024u)
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32G0
+ #include "stm32g0xx.h"
+ #define FSDEV_BUS_32BIT
+ #define FSDEV_PMA_SIZE (2048u)
+ #undef USB_PMAADDR
+ #define USB_PMAADDR USB_DRD_PMAADDR
+ #define USB_TypeDef USB_DRD_TypeDef
+ #define EP0R CHEP0R
+ #define USB_EP_CTR_RX USB_EP_VTRX
+ #define USB_EP_CTR_TX USB_EP_VTTX
+ #define USB_EP_T_FIELD USB_CHEP_UTYPE
+ #define USB_EPREG_MASK USB_CHEP_REG_MASK
+ #define USB_EPTX_DTOGMASK USB_CHEP_TX_DTOGMASK
+ #define USB_EPRX_DTOGMASK USB_CHEP_RX_DTOGMASK
+ #define USB_EPTX_DTOG1 USB_CHEP_TX_DTOG1
+ #define USB_EPTX_DTOG2 USB_CHEP_TX_DTOG2
+ #define USB_EPRX_DTOG1 USB_CHEP_RX_DTOG1
+ #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2
+ #define USB_EPRX_STAT USB_CH_RX_VALID
+ #define USB_EPKIND_MASK USB_EP_KIND_MASK
+ #define USB USB_DRD_FS
+ #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_BUS_32BIT
+
+ #if !defined(USB_DRD_BASE) && defined(USB_DRD_FS_BASE)
+ #define USB_DRD_BASE USB_DRD_FS_BASE
+ #endif
+
+ #define FSDEV_PMA_SIZE (2048u)
+ #undef USB_PMAADDR
+ #define USB_PMAADDR USB_DRD_PMAADDR
+ #define USB_TypeDef USB_DRD_TypeDef
+ #define EP0R CHEP0R
+ #define USB_EP_CTR_RX USB_EP_VTRX
+ #define USB_EP_CTR_TX USB_EP_VTTX
+ #define USB_EP_T_FIELD USB_CHEP_UTYPE
+ #define USB_EPREG_MASK USB_CHEP_REG_MASK
+ #define USB_EPTX_DTOGMASK USB_CHEP_TX_DTOGMASK
+ #define USB_EPRX_DTOGMASK USB_CHEP_RX_DTOGMASK
+ #define USB_EPTX_DTOG1 USB_CHEP_TX_DTOG1
+ #define USB_EPTX_DTOG2 USB_CHEP_TX_DTOG2
+ #define USB_EPRX_DTOG1 USB_CHEP_RX_DTOG1
+ #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2
+ #define USB_EPRX_STAT USB_CH_RX_VALID
+ #define USB_EPKIND_MASK USB_EP_KIND_MASK
+ #define USB USB_DRD_FS
+ #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_STM32WB
+ #include "stm32wbxx.h"
+ #define FSDEV_PMA_SIZE (1024u)
+ /* ST provided header has incorrect value */
+ #undef USB_PMAADDR
+ #define USB_PMAADDR USB1_PMAADDR
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32L4
+ #include "stm32l4xx.h"
+ #define FSDEV_PMA_SIZE (1024u)
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32L5
+ #include "stm32l5xx.h"
+ #define FSDEV_PMA_SIZE (1024u)
+
+ #ifndef USB_PMAADDR
+ #define USB_PMAADDR (USB_BASE + (USB_PMAADDR_NS - USB_BASE_NS))
+ #endif
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32U5
+ #include "stm32u5xx.h"
+ #define FSDEV_BUS_32BIT
+
+ #define FSDEV_PMA_SIZE (2048u)
+ #undef USB_PMAADDR
+ #define USB_PMAADDR USB_DRD_PMAADDR
+ #define USB_TypeDef USB_DRD_TypeDef
+ #define EP0R CHEP0R
+ #define USB_EP_CTR_RX USB_EP_VTRX
+ #define USB_EP_CTR_TX USB_EP_VTTX
+ #define USB_EP_T_FIELD USB_CHEP_UTYPE
+ #define USB_EPREG_MASK USB_CHEP_REG_MASK
+ #define USB_EPTX_DTOGMASK USB_CHEP_TX_DTOGMASK
+ #define USB_EPRX_DTOGMASK USB_CHEP_RX_DTOGMASK
+ #define USB_EPTX_DTOG1 USB_CHEP_TX_DTOG1
+ #define USB_EPTX_DTOG2 USB_CHEP_TX_DTOG2
+ #define USB_EPRX_DTOG1 USB_CHEP_RX_DTOG1
+ #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2
+ #define USB_EPRX_STAT USB_CH_RX_VALID
+ #define USB_EPKIND_MASK USB_EP_KIND_MASK
+ #define USB USB_DRD_FS
+ #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
+
+#else
+ #error You are using an untested or unimplemented STM32 variant. Please update the driver.
+ // This includes U0
+#endif
+
+#if defined(USB_BASE)
+ #define FSDEV_REG_BASE USB_BASE
+#elif defined(USB_DRD_BASE)
+ #define FSDEV_REG_BASE USB_DRD_BASE
+#elif defined(USB_DRD_FS_BASE)
+ #define FSDEV_REG_BASE USB_DRD_FS_BASE
+#else
+ #error "FSDEV_REG_BASE not defined"
+#endif
+
+// This checks if the device has "LPM"
+#if defined(USB_ISTR_L1REQ)
+#define USB_ISTR_L1REQ_FORCED (USB_ISTR_L1REQ)
+#else
+#define USB_ISTR_L1REQ_FORCED ((uint16_t)0x0000U)
+#endif
+
+#define USB_ISTR_ALL_EVENTS (USB_ISTR_PMAOVR | USB_ISTR_ERR | USB_ISTR_WKUP | USB_ISTR_SUSP | \
+ USB_ISTR_RESET | USB_ISTR_SOF | USB_ISTR_ESOF | USB_ISTR_L1REQ_FORCED )
+
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+
+#if TU_CHECK_MCU(OPT_MCU_STM32L1) && !defined(USBWakeUp_IRQn)
+ #define USBWakeUp_IRQn USB_FS_WKUP_IRQn
+#endif
+
+static const IRQn_Type fsdev_irq[] = {
+ #if TU_CHECK_MCU(OPT_MCU_STM32F0, OPT_MCU_STM32L0, OPT_MCU_STM32L4)
+ USB_IRQn,
+ #elif CFG_TUSB_MCU == OPT_MCU_STM32F1
+ USB_HP_CAN1_TX_IRQn,
+ USB_LP_CAN1_RX0_IRQn,
+ USBWakeUp_IRQn,
+ #elif CFG_TUSB_MCU == OPT_MCU_STM32F3
+ // USB remap handles dcd functions
+ USB_HP_CAN_TX_IRQn,
+ USB_LP_CAN_RX0_IRQn,
+ USBWakeUp_IRQn,
+ #elif CFG_TUSB_MCU == OPT_MCU_STM32G0
+ #ifdef STM32G0B0xx
+ USB_IRQn,
+ #else
+ USB_UCPD1_2_IRQn,
+ #endif
+ #elif TU_CHECK_MCU(OPT_MCU_STM32G4, OPT_MCU_STM32L1)
+ USB_HP_IRQn,
+ USB_LP_IRQn,
+ USBWakeUp_IRQn,
+ #elif CFG_TUSB_MCU == OPT_MCU_STM32H5
+ USB_DRD_FS_IRQn,
+ #elif CFG_TUSB_MCU == OPT_MCU_STM32L5
+ USB_FS_IRQn,
+ #elif CFG_TUSB_MCU == OPT_MCU_STM32WB
+ USB_HP_IRQn,
+ USB_LP_IRQn,
+ #elif CFG_TUSB_MCU == OPT_MCU_STM32U5
+ USB_IRQn,
+ #else
+ #error Unknown arch in USB driver
+ #endif
+};
+enum { FSDEV_IRQ_NUM = TU_ARRAY_SIZE(fsdev_irq) };
+
+void dcd_int_enable(uint8_t rhport) {
+ (void)rhport;
+
+ // forces write to RAM before allowing ISR to execute
+ __DSB(); __ISB();
+
+ #if CFG_TUSB_MCU == OPT_MCU_STM32F3 && defined(SYSCFG_CFGR1_USB_IT_RMP)
+ // Some STM32F302/F303 devices allow to remap the USB interrupt vectors from
+ // shared USB/CAN IRQs to separate CAN and USB IRQs.
+ // This dynamically checks if this remap is active to enable the right IRQs.
+ if (SYSCFG->CFGR1 & SYSCFG_CFGR1_USB_IT_RMP) {
+ NVIC_EnableIRQ(USB_HP_IRQn);
+ NVIC_EnableIRQ(USB_LP_IRQn);
+ NVIC_EnableIRQ(USBWakeUp_RMP_IRQn);
+ } else
+ #endif
+ {
+ for (uint8_t i = 0; i < FSDEV_IRQ_NUM; i++) {
+ NVIC_EnableIRQ(fsdev_irq[i]);
+ }
+ }
+}
+
+void dcd_int_disable(uint8_t rhport) {
+ (void)rhport;
+
+ #if CFG_TUSB_MCU == OPT_MCU_STM32F3 && defined(SYSCFG_CFGR1_USB_IT_RMP)
+ // Some STM32F302/F303 devices allow to remap the USB interrupt vectors from
+ // shared USB/CAN IRQs to separate CAN and USB IRQs.
+ // This dynamically checks if this remap is active to enable the right IRQs.
+ if (SYSCFG->CFGR1 & SYSCFG_CFGR1_USB_IT_RMP) {
+ NVIC_DisableIRQ(USB_HP_IRQn);
+ NVIC_DisableIRQ(USB_LP_IRQn);
+ NVIC_DisableIRQ(USBWakeUp_RMP_IRQn);
+ } else
+ #endif
+ {
+ for (uint8_t i = 0; i < FSDEV_IRQ_NUM; i++) {
+ NVIC_DisableIRQ(fsdev_irq[i]);
+ }
+ }
+
+ // CMSIS has a membar after disabling interrupts
+}
+
+// Define only on MCU with internal pull-up. BSP can define on MCU without internal PU.
+#if defined(USB_BCDR_DPPU)
+
+void dcd_disconnect(uint8_t rhport) {
+ (void)rhport;
+ USB->BCDR &= ~(USB_BCDR_DPPU);
+}
+
+void dcd_connect(uint8_t rhport) {
+ (void)rhport;
+ USB->BCDR |= USB_BCDR_DPPU;
+}
+
+#elif defined(SYSCFG_PMC_USB_PU) // works e.g. on STM32L151
+
+void dcd_disconnect(uint8_t rhport) {
+ (void)rhport;
+ SYSCFG->PMC &= ~(SYSCFG_PMC_USB_PU);
+}
+
+void dcd_connect(uint8_t rhport) {
+ (void)rhport;
+ SYSCFG->PMC |= SYSCFG_PMC_USB_PU;
+}
+#endif
+
+
+#endif /* TUSB_FSDEV_STM32_H */
diff --git a/src/portable/st/stm32_fsdev/fsdev_type.h b/src/portable/st/stm32_fsdev/fsdev_type.h
new file mode 100644
index 000000000..e4a0f3f28
--- /dev/null
+++ b/src/portable/st/stm32_fsdev/fsdev_type.h
@@ -0,0 +1,282 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright(c) 2016 STMicroelectronics
+ * Copyright(c) N Conrad
+ * Copyright (c) 2024, hathach (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.
+ *
+ */
+
+#ifndef TUSB_FSDEV_TYPE_H
+#define TUSB_FSDEV_TYPE_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+#include "stdint.h"
+
+// If sharing with CAN, one can set this to be non-zero to give CAN space where it wants it
+// Both of these MUST be a multiple of 2, and are in byte units.
+#ifndef FSDEV_BTABLE_BASE
+#define FSDEV_BTABLE_BASE 0U
+#endif
+
+TU_VERIFY_STATIC(FSDEV_BTABLE_BASE % 8 == 0, "BTABLE base must be aligned to 8 bytes");
+
+// FSDEV_PMA_SIZE is PMA buffer size in bytes.
+// - 512-byte devices, access with a stride of two words (use every other 16-bit address)
+// - 1024-byte devices, access with a stride of one word (use every 16-bit address)
+// - 2048-byte devices, access with 32-bit address
+
+// For purposes of accessing the packet
+#if FSDEV_PMA_SIZE == 512
+ #define FSDEV_PMA_STRIDE (2u) // 1x16 bit access scheme
+ #define pma_aligned TU_ATTR_ALIGNED(4)
+#elif FSDEV_PMA_SIZE == 1024
+ #define FSDEV_PMA_STRIDE (1u) // 2x16 bit access scheme
+ #define pma_aligned
+#elif FSDEV_PMA_SIZE == 2048
+ #ifndef FSDEV_BUS_32BIT
+ #warning "FSDEV_PMA_SIZE is 2048, but FSDEV_BUS_32BIT is not defined"
+ #endif
+ #define FSDEV_PMA_STRIDE (1u) // 32 bit access scheme
+ #define pma_aligned
+#endif
+
+//--------------------------------------------------------------------+
+// BTable Typedef
+//--------------------------------------------------------------------+
+enum {
+ BTABLE_BUF_TX = 0,
+ BTABLE_BUF_RX = 1
+};
+
+// hardware limit endpoint
+#define FSDEV_EP_COUNT 8
+
+// Buffer Table is located in Packet Memory Area (PMA) and therefore its address access is forced to either
+// 16-bit or 32-bit depending on FSDEV_BUS_32BIT.
+typedef union {
+ // 0: TX (IN), 1: RX (OUT)
+
+ // strictly 16-bit access (could be 32-bit aligned)
+ struct {
+ volatile pma_aligned uint16_t addr;
+ volatile pma_aligned uint16_t count;
+ } ep16[FSDEV_EP_COUNT][2];
+
+ // strictly 32-bit access
+ struct {
+ volatile uint32_t count_addr;
+ } ep32[FSDEV_EP_COUNT][2];
+} fsdev_btable_t;
+
+TU_VERIFY_STATIC(sizeof(fsdev_btable_t) == FSDEV_EP_COUNT*8*FSDEV_PMA_STRIDE, "size is not correct");
+TU_VERIFY_STATIC(FSDEV_BTABLE_BASE + FSDEV_EP_COUNT*8 <= FSDEV_PMA_SIZE, "BTABLE does not fit in PMA RAM");
+
+#define FSDEV_BTABLE ((volatile fsdev_btable_t*) (USB_PMAADDR+FSDEV_BTABLE_BASE))
+
+typedef struct {
+ volatile pma_aligned uint16_t u16;
+} fsdev_pma16_t;
+
+//--------------------------------------------------------------------+
+// Registers Typedef
+//--------------------------------------------------------------------+
+
+// volatile 32-bit aligned
+#define _va32 volatile TU_ATTR_ALIGNED(4)
+
+// The fsdev_bus_t type can be used for both register and PMA access necessities
+#ifdef FSDEV_BUS_32BIT
+typedef uint32_t fsdev_bus_t;
+#else
+typedef uint16_t fsdev_bus_t;
+#endif
+
+typedef struct {
+ struct {
+ _va32 fsdev_bus_t reg;
+ }ep[FSDEV_EP_COUNT];
+
+ _va32 uint32_t RESERVED7[8]; // Reserved
+ _va32 fsdev_bus_t CNTR; // 40: Control register
+ _va32 fsdev_bus_t ISTR; // 44: Interrupt status register
+ _va32 fsdev_bus_t FNR; // 48: Frame number register
+ _va32 fsdev_bus_t DADDR; // 4C: Device address register
+ _va32 fsdev_bus_t BTABLE; // 50: Buffer Table address register (16-bit only)
+ _va32 fsdev_bus_t LPMCSR; // 54: LPM Control and Status Register (32-bit only)
+ _va32 fsdev_bus_t BCDR; // 58: Battery Charging Detector Register (32-bit only)
+} fsdev_regs_t;
+
+TU_VERIFY_STATIC(offsetof(fsdev_regs_t, CNTR) == 0x40, "Wrong offset");
+TU_VERIFY_STATIC(sizeof(fsdev_regs_t) == 0x5C, "Size is not correct");
+
+#define FSDEV_REG ((fsdev_regs_t*) FSDEV_REG_BASE)
+
+
+#ifndef USB_EPTX_STAT
+#define USB_EPTX_STAT 0x0030U
+#endif
+
+#ifndef USB_EPRX_STAT
+#define USB_EPRX_STAT 0x3000U
+#endif
+
+#ifndef USB_EPTX_STAT_Pos
+#define USB_EPTX_STAT_Pos 4u
+#endif
+
+#ifndef USB_EP_DTOG_TX_Pos
+#define USB_EP_DTOG_TX_Pos 6u
+#endif
+
+#ifndef USB_EP_CTR_TX_Pos
+#define USB_EP_CTR_TX_Pos 7u
+#endif
+
+
+#define EP_CTR_TXRX (USB_EP_CTR_TX | USB_EP_CTR_RX)
+
+typedef enum {
+ EP_STAT_DISABLED = 0,
+ EP_STAT_STALL = 1,
+ EP_STAT_NAK = 2,
+ EP_STAT_VALID = 3
+}ep_stat_t;
+
+#define EP_STAT_MASK(_dir) (3u << (USB_EPTX_STAT_Pos + ((_dir) == TUSB_DIR_IN ? 0 : 8)))
+#define EP_DTOG_MASK(_dir) (1u << (USB_EP_DTOG_TX_Pos + ((_dir) == TUSB_DIR_IN ? 0 : 8)))
+
+//--------------------------------------------------------------------+
+// Endpoint Helper
+// - CTR is write 0 to clear
+// - DTOG and STAT are write 1 to toggle
+//--------------------------------------------------------------------+
+
+TU_ATTR_ALWAYS_INLINE static inline void ep_write(uint32_t ep_id, uint32_t value) {
+ FSDEV_REG->ep[ep_id].reg = (fsdev_bus_t) value;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t ep_read(uint32_t ep_id) {
+ return FSDEV_REG->ep[ep_id].reg;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t ep_add_status(uint32_t reg, tusb_dir_t dir, ep_stat_t state) {
+ return reg ^ (state << (USB_EPTX_STAT_Pos + (dir == TUSB_DIR_IN ? 0 : 8)));
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t ep_add_dtog(uint32_t reg, tusb_dir_t dir, uint8_t state) {
+ return reg ^ (state << (USB_EP_DTOG_TX_Pos + (dir == TUSB_DIR_IN ? 0 : 8)));
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t ep_clear_ctr(uint32_t reg, tusb_dir_t dir) {
+ return reg & ~(1 << (USB_EP_CTR_TX_Pos + (dir == TUSB_DIR_IN ? 0 : 8)));
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool ep_is_iso(uint32_t reg) {
+ return (reg & USB_EP_TYPE_MASK) == USB_EP_ISOCHRONOUS;
+}
+
+//--------------------------------------------------------------------+
+// BTable Helper
+//--------------------------------------------------------------------+
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t btable_get_addr(uint32_t ep_id, uint8_t buf_id) {
+#ifdef FSDEV_BUS_32BIT
+ return FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr & 0x0000FFFFu;
+#else
+ return FSDEV_BTABLE->ep16[ep_id][buf_id].addr;
+#endif
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void btable_set_addr(uint32_t ep_id, uint8_t buf_id, uint16_t addr) {
+#ifdef FSDEV_BUS_32BIT
+ uint32_t count_addr = FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr;
+ count_addr = (count_addr & 0xFFFF0000u) | (addr & 0x0000FFFCu);
+ FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr = count_addr;
+#else
+ FSDEV_BTABLE->ep16[ep_id][buf_id].addr = addr;
+#endif
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t btable_get_count(uint32_t ep_id, uint8_t buf_id) {
+ uint16_t count;
+#ifdef FSDEV_BUS_32BIT
+ count = (FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr >> 16);
+#else
+ count = FSDEV_BTABLE->ep16[ep_id][buf_id].count;
+#endif
+ return count & 0x3FFU;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void btable_set_count(uint32_t ep_id, uint8_t buf_id, uint16_t byte_count) {
+#ifdef FSDEV_BUS_32BIT
+ uint32_t count_addr = FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr;
+ count_addr = (count_addr & ~0x03FF0000u) | ((byte_count & 0x3FFu) << 16);
+ FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr = count_addr;
+#else
+ uint16_t cnt = FSDEV_BTABLE->ep16[ep_id][buf_id].count;
+ cnt = (cnt & ~0x3FFU) | (byte_count & 0x3FFU);
+ FSDEV_BTABLE->ep16[ep_id][buf_id].count = cnt;
+#endif
+}
+
+/* Aligned buffer size according to hardware */
+TU_ATTR_ALWAYS_INLINE static inline uint16_t pma_align_buffer_size(uint16_t size, uint8_t* blsize, uint8_t* num_block) {
+ /* The STM32 full speed USB peripheral supports only a limited set of
+ * buffer sizes given by the RX buffer entry format in the USB_BTABLE. */
+ uint16_t block_in_bytes;
+ if (size > 62) {
+ block_in_bytes = 32;
+ *blsize = 1;
+ } else {
+ block_in_bytes = 2;
+ *blsize = 0;
+ }
+
+ *num_block = tu_div_ceil(size, block_in_bytes);
+
+ return (*num_block) * block_in_bytes;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void btable_set_rx_bufsize(uint32_t ep_id, uint8_t buf_id, uint32_t wCount) {
+ uint8_t blsize, num_block;
+ (void) pma_align_buffer_size(wCount, &blsize, &num_block);
+
+ /* Encode into register. When BLSIZE==1, we need to subtract 1 block count */
+ uint16_t bl_nb = (blsize << 15) | ((num_block - blsize) << 10);
+
+#ifdef FSDEV_BUS_32BIT
+ uint32_t count_addr = FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr;
+ count_addr = (bl_nb << 16) | (count_addr & 0x0000FFFFu);
+ FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr = count_addr;
+#else
+ FSDEV_BTABLE->ep16[ep_id][buf_id].count = bl_nb;
+#endif
+}
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
diff --git a/src/portable/sunxi/dcd_sunxi_musb.c b/src/portable/sunxi/dcd_sunxi_musb.c
index 6cc1975a8..6f36ad441 100644
--- a/src/portable/sunxi/dcd_sunxi_musb.c
+++ b/src/portable/sunxi/dcd_sunxi_musb.c
@@ -35,7 +35,9 @@
#include <f1c100s-irq.h>
#include <device/dcd.h>
#include "musb_def.h"
-#include "bsp/board.h"
+
+//#include "bsp/board_api.h"
+extern uint32_t board_millis(void); // TODO remove
typedef uint32_t u32;
typedef uint16_t u16;
@@ -58,7 +60,7 @@ typedef struct TU_ATTR_PACKED
typedef struct
{
- tusb_control_request_t setup_packet;
+ CFG_TUD_MEM_ALIGN tusb_control_request_t setup_packet;
uint16_t remaining_ctrl; /* The number of bytes remaining in data stage of control transfer. */
int8_t status_out;
pipe_state_t pipe0;
@@ -350,7 +352,7 @@ static void USBC_INT_DisableRxEp(u8 ep_index)
* INTERNAL FUNCTION DECLARATION
*------------------------------------------------------------------*/
-static dcd_data_t _dcd;
+CFG_TUD_MEM_ALIGN static dcd_data_t _dcd;
static inline free_block_t *find_containing_block(free_block_t *beg, free_block_t *end, uint_fast16_t addr)
{
@@ -560,7 +562,7 @@ static void pipe_read_write_packet_ff(tu_fifo_t *f, volatile void *fifo, unsigne
static void process_setup_packet(uint8_t rhport)
{
- uint32_t *p = (uint32_t*)&_dcd.setup_packet;
+ uint32_t *p = (uint32_t*)(uintptr_t) &_dcd.setup_packet;
p[0] = USBC_Readl(USBC_REG_EPFIFO0(USBC0_BASE));
p[1] = USBC_Readl(USBC_REG_EPFIFO0(USBC0_BASE));
@@ -594,7 +596,7 @@ static bool handle_xfer_in(uint_fast8_t ep_addr)
if (len) {
volatile void* addr = (volatile void*)(USBC_REG_EPFIFO1(USBC0_BASE) + (epnum_minus1 << 2));
if (_dcd.pipe_buf_is_fifo[TUSB_DIR_IN] & TU_BIT(epnum_minus1)) {
- pipe_read_write_packet_ff((tu_fifo_t *)buf, addr, len, TUSB_DIR_IN);
+ pipe_read_write_packet_ff((tu_fifo_t *)(uintptr_t) buf, addr, len, TUSB_DIR_IN);
} else {
pipe_write_packet(buf, addr, len);
pipe->buf = buf + len;
@@ -622,7 +624,7 @@ static bool handle_xfer_out(uint_fast8_t ep_addr)
if (len) {
volatile void* addr = (volatile void*)(USBC_REG_EPFIFO1(USBC0_BASE) + (epnum_minus1 << 2));
if (_dcd.pipe_buf_is_fifo[TUSB_DIR_OUT] & TU_BIT(epnum_minus1)) {
- pipe_read_write_packet_ff((tu_fifo_t *)buf, addr, len, TUSB_DIR_OUT);
+ pipe_read_write_packet_ff((tu_fifo_t *)(uintptr_t )buf, addr, len, TUSB_DIR_OUT);
} else {
pipe_read_packet(buf, addr, len);
pipe->buf = buf + len;
diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c
index 8b13b0753..9a38b46dc 100644
--- a/src/portable/synopsys/dwc2/dcd_dwc2.c
+++ b/src/portable/synopsys/dwc2/dcd_dwc2.c
@@ -112,6 +112,17 @@ static inline uint16_t calc_grxfsiz(uint16_t max_ep_size, uint8_t ep_count) {
return 15 + 2 * (max_ep_size / 4) + 2 * ep_count;
}
+TU_ATTR_ALWAYS_INLINE static inline void fifo_flush_tx(dwc2_regs_t* dwc2, uint8_t epnum) {
+ // flush TX fifo and wait for it cleared
+ dwc2->grstctl = GRSTCTL_TXFFLSH | (epnum << GRSTCTL_TXFNUM_Pos);
+ while (dwc2->grstctl & GRSTCTL_TXFFLSH_Msk) {}
+}
+TU_ATTR_ALWAYS_INLINE static inline void fifo_flush_rx(dwc2_regs_t* dwc2) {
+ // flush RX fifo and wait for it cleared
+ dwc2->grstctl = GRSTCTL_RXFFLSH;
+ while (dwc2->grstctl & GRSTCTL_RXFFLSH_Msk) {}
+}
+
static bool fifo_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t packet_size) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
uint8_t const ep_count = _dwc2_controller[rhport].ep_count;
@@ -120,7 +131,7 @@ static bool fifo_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t packet_size) {
TU_ASSERT(epnum < ep_count);
- uint16_t const fifo_size = tu_div_ceil(packet_size, 4);
+ uint16_t fifo_size = tu_div_ceil(packet_size, 4);
// "USB Data FIFOs" section in reference manual
// Peripheral FIFO architecture
@@ -154,10 +165,16 @@ static bool fifo_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t packet_size) {
dwc2->grxfsiz = sz;
}
} else {
+ // Note if The TXFELVL is configured as half empty. In order
+ // to be able to write a packet at that point, the fifo must be twice the max_size.
+ if ((dwc2->gahbcfg & GAHBCFG_TXFELVL) == 0) {
+ fifo_size *= 2;
+ }
+
// Check if free space is available
TU_ASSERT(_allocated_fifo_words_tx + fifo_size + dwc2->grxfsiz <= _dwc2_controller[rhport].ep_fifo_size / 4);
_allocated_fifo_words_tx += fifo_size;
- TU_LOG(DWC2_DEBUG, " Allocated %u bytes at offset %lu", fifo_size * 4,
+ TU_LOG(DWC2_DEBUG, " Allocated %u bytes at offset %" PRIu32, fifo_size * 4,
_dwc2_controller[rhport].ep_fifo_size - _allocated_fifo_words_tx * 4);
// DIEPTXF starts at FIFO #1.
@@ -185,10 +202,10 @@ static void edpt_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoin
(xfer->max_size << DOEPCTL_MPSIZ_Pos);
if (dir == TUSB_DIR_OUT) {
- dwc2->epout[epnum].doepctl |= dxepctl;
+ dwc2->epout[epnum].doepctl = dxepctl;
dwc2->daintmsk |= TU_BIT(DAINTMSK_OEPM_Pos + epnum);
} else {
- dwc2->epin[epnum].diepctl |= dxepctl | (epnum << DIEPCTL_TXFNUM_Pos);
+ dwc2->epin[epnum].diepctl = dxepctl | (epnum << DIEPCTL_TXFNUM_Pos);
dwc2->daintmsk |= (1 << (DAINTMSK_IEPM_Pos + epnum));
}
}
@@ -219,8 +236,7 @@ static void edpt_disable(uint8_t rhport, uint8_t ep_addr, bool stall) {
}
// Flush the FIFO, and wait until we have confirmed it cleared.
- dwc2->grstctl = ((epnum << GRSTCTL_TXFNUM_Pos) | GRSTCTL_TXFFLSH);
- while ((dwc2->grstctl & GRSTCTL_TXFFLSH_Msk) != 0) {}
+ fifo_flush_tx(dwc2, epnum);
} else {
dwc2_epout_t* epout = dwc2->epout;
@@ -264,15 +280,17 @@ static void bus_reset(uint8_t rhport) {
dwc2->epout[n].doepctl |= DOEPCTL_SNAK;
}
- // flush all TX fifo and wait for it cleared
- dwc2->grstctl = GRSTCTL_TXFFLSH | (0x10u << GRSTCTL_TXFNUM_Pos);
- while (dwc2->grstctl & GRSTCTL_TXFFLSH_Msk) {}
+ // 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;
+ }
+ }
- // flush RX fifo and wait for it cleared
- dwc2->grstctl = GRSTCTL_RXFFLSH;
- while (dwc2->grstctl & GRSTCTL_RXFFLSH_Msk) {}
+ fifo_flush_tx(dwc2, 0x10); // all tx fifo
+ fifo_flush_rx(dwc2);
- // 2. Set up interrupt mask
+ // 3. Set up interrupt mask
dwc2->daintmsk = TU_BIT(DAINTMSK_OEPM_Pos) | TU_BIT(DAINTMSK_IEPM_Pos);
dwc2->doepmsk = DOEPMSK_STUPM | DOEPMSK_XFRCM;
dwc2->diepmsk = DIEPMSK_TOM | DIEPMSK_XFRCM;
@@ -408,9 +426,9 @@ void print_dwc2_info(dwc2_regs_t* dwc2) {
volatile uint32_t const* p = (volatile uint32_t const*) &dwc2->guid;
TU_LOG(DWC2_DEBUG, "guid, gsnpsid, ghwcfg1, ghwcfg2, ghwcfg3, ghwcfg4\r\n");
for (size_t i = 0; i < 5; i++) {
- TU_LOG(DWC2_DEBUG, "0x%08lX, ", p[i]);
+ TU_LOG(DWC2_DEBUG, "0x%08" PRIX32 ", ", p[i]);
}
- TU_LOG(DWC2_DEBUG, "0x%08lX\r\n", p[5]);
+ TU_LOG(DWC2_DEBUG, "0x%08" PRIX32 "\r\n", p[5]);
}
#endif
@@ -429,11 +447,15 @@ static void reset_core(dwc2_regs_t* dwc2) {
}
static bool phy_hs_supported(dwc2_regs_t* dwc2) {
- // note: esp32 incorrect report its hs_phy_type as utmi
+ (void) dwc2;
+
#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+ // note: esp32 incorrect report its hs_phy_type as utmi
+ return false;
+#elif !TUD_OPT_HIGH_SPEED
return false;
#else
- return TUD_OPT_HIGH_SPEED && dwc2->ghwcfg2_bm.hs_phy_type != HS_PHY_TYPE_NONE;
+ return dwc2->ghwcfg2_bm.hs_phy_type != HS_PHY_TYPE_NONE;
#endif
}
@@ -578,13 +600,8 @@ void dcd_init(uint8_t rhport) {
// (non zero-length packet), send STALL back and discard.
dwc2->dcfg |= DCFG_NZLSOHSK;
- // flush all TX fifo and wait for it cleared
- dwc2->grstctl = GRSTCTL_TXFFLSH | (0x10u << GRSTCTL_TXFNUM_Pos);
- while (dwc2->grstctl & GRSTCTL_TXFFLSH_Msk) {}
-
- // flush RX fifo and wait for it cleared
- dwc2->grstctl = GRSTCTL_RXFFLSH;
- while (dwc2->grstctl & GRSTCTL_RXFFLSH_Msk) {}
+ fifo_flush_tx(dwc2, 0x10); // all tx fifo
+ fifo_flush_rx(dwc2);
// Clear all interrupts
uint32_t int_mask = dwc2->gintsts;
@@ -596,6 +613,9 @@ void dcd_init(uint8_t rhport) {
dwc2->gintmsk = GINTMSK_OTGINT | GINTMSK_RXFLVLM |
GINTMSK_USBSUSPM | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM;
+ // Configure TX FIFO empty level for interrupt. Default is complete empty
+ dwc2->gahbcfg |= GAHBCFG_TXFELVL;
+
// Enable global interrupt
dwc2->gahbcfg |= GAHBCFG_GINT;
@@ -691,16 +711,25 @@ void dcd_edpt_close_all(uint8_t rhport) {
for (uint8_t n = 1; n < ep_count; n++) {
// disable OUT endpoint
- dwc2->epout[n].doepctl = 0;
+ if (dwc2->epout[n].doepctl & DOEPCTL_EPENA) {
+ dwc2->epout[n].doepctl |= DOEPCTL_SNAK | DOEPCTL_EPDIS;
+ }
xfer_status[n][TUSB_DIR_OUT].max_size = 0;
// disable IN endpoint
- dwc2->epin[n].diepctl = 0;
+ if (dwc2->epin[n].diepctl & DIEPCTL_EPENA) {
+ dwc2->epin[n].diepctl |= DIEPCTL_SNAK | DIEPCTL_EPDIS;
+ }
xfer_status[n][TUSB_DIR_IN].max_size = 0;
}
+ // reset allocated fifo OUT
+ dwc2->grxfsiz = calc_grxfsiz(64, ep_count);
// reset allocated fifo IN
_allocated_fifo_words_tx = 16;
+
+ fifo_flush_tx(dwc2, 0x10); // all tx fifo
+ fifo_flush_rx(dwc2);
}
bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
@@ -1122,16 +1151,14 @@ void dcd_int_handler(uint8_t rhport) {
if(int_status & GINTSTS_SOF) {
dwc2->gintsts = GINTSTS_SOF;
+ const uint32_t frame = (dwc2->dsts & DSTS_FNSOF) >> DSTS_FNSOF_Pos;
- if (_sof_en) {
- uint32_t frame = (dwc2->dsts & (DSTS_FNSOF)) >> 8;
- dcd_event_sof(rhport, frame, true);
- } else {
- // Disable SOF interrupt if SOF was not explicitly enabled. SOF was used for remote wakeup detection
+ // Disable SOF interrupt if SOF was not explicitly enabled since SOF was used for remote wakeup detection
+ if (!_sof_en) {
dwc2->gintmsk &= ~GINTMSK_SOFM;
}
- dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
+ dcd_event_sof(rhport, frame, true);
}
// RxFIFO non-empty interrupt handling.
@@ -1144,7 +1171,7 @@ void dcd_int_handler(uint8_t rhport) {
// Loop until all available packets were handled
do {
handle_rxflvl_irq(rhport);
- } while (dwc2->gotgint & GINTSTS_RXFLVL);
+ } while(dwc2->gintsts & GINTSTS_RXFLVL);
dwc2->gintmsk |= GINTMSK_RXFLVLM;
}
@@ -1168,4 +1195,13 @@ void dcd_int_handler(uint8_t rhport) {
// }
}
+#if CFG_TUD_TEST_MODE
+void dcd_enter_test_mode(uint8_t rhport, tusb_feature_test_mode_t test_selector) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+
+ // Enable the test mode
+ dwc2->dctl = (dwc2->dctl & ~DCTL_TCTL_Msk) | (((uint8_t) test_selector) << DCTL_TCTL_Pos);
+}
+#endif
+
#endif
diff --git a/src/portable/synopsys/dwc2/dwc2_esp32.h b/src/portable/synopsys/dwc2/dwc2_esp32.h
index c50dd66b8..932f52f40 100644
--- a/src/portable/synopsys/dwc2/dwc2_esp32.h
+++ b/src/portable/synopsys/dwc2/dwc2_esp32.h
@@ -35,6 +35,7 @@
#include "esp_intr_alloc.h"
#include "soc/periph_defs.h"
//#include "soc/usb_periph.h"
+#include "freertos/task.h"
#define DWC2_REG_BASE 0x60080000UL
#define DWC2_EP_MAX 6 // USB_OUT_EP_NUM. TODO ESP32Sx only has 5 tx fifo (5 endpoint IN)
diff --git a/src/portable/synopsys/dwc2/dwc2_type.h b/src/portable/synopsys/dwc2/dwc2_type.h
index c15771237..cb694b326 100644
--- a/src/portable/synopsys/dwc2/dwc2_type.h
+++ b/src/portable/synopsys/dwc2/dwc2_type.h
@@ -1,17 +1,34 @@
-/**
- * @author MCD Application Team
- * Ha Thach (tinyusb.org)
- *
- * @attention
- *
- * <h2><center>&copy; Copyright (c) 2019 STMicroelectronics.
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024, hathach (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.
+ *
+ */
+/** <h2><center>&copy; Copyright (c) 2019 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
- *
*/
#ifndef _TUSB_DWC2_TYPES_H_
diff --git a/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c b/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c
index b4dfda575..8005f5f7b 100644
--- a/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c
+++ b/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c
@@ -94,7 +94,8 @@ static void bus_reset(void)
USBOEPCNT_0 &= ~NAK;
USBIEPCNT_0 &= ~NAK;
- USBCTL |= FEN; // Enable responding to packets.
+ // Enable responding to packets.
+ USBCTL |= FEN;
// Dedicated buffers in hardware for SETUP and EP0, no setup needed.
// Now safe to respond to SETUP packets.
@@ -103,6 +104,28 @@ static void bus_reset(void)
USBKEYPID = 0;
}
+// Controls reset behavior of the USB module on receipt of a bus reset event.
+// - enable: When true, bus reset events will cause a reset the USB module.
+static void enable_functional_reset(const bool enable)
+{
+ // Check whether or not the USB configuration registers were
+ // locked prior to this function being called so that, if
+ // necessary, the lock state can be restored on exit.
+ bool unlocked = (USBKEYPID == 0xA528) ? true : false;
+
+ if(!unlocked) USBKEYPID = USBKEY;
+
+ if(enable)
+ {
+ USBCTL |= FRSTE;
+ }
+ else
+ {
+ USBCTL &= ~FRSTE;
+ }
+
+ if(!unlocked) USBKEYPID = 0;
+}
/*------------------------------------------------------------------*/
/* Controller API
@@ -131,11 +154,14 @@ void dcd_init (uint8_t rhport)
USBVECINT = 0;
- // Enable reset and wait for it before continuing.
- USBIE |= RSTRIE;
-
- // Enable pullup.
- USBCNF |= PUR_EN;
+ if(USBPWRCTL & USBBGVBV) {// Bus power detected?
+ USBPWRCTL |= VBOFFIE; // Enable bus-power-removed interrupt.
+ USBIE |= RSTRIE; // Enable reset and wait for it before continuing.
+ USBCNF |= PUR_EN; // Enable pullup.
+ } else {
+ USBPWRCTL |= VBONIE; // Enable bus-power-applied interrupt.
+ USBCNF &= ~USB_EN; // Disable USB module until bus power is detected.
+ }
USBKEYPID = 0;
}
@@ -610,14 +636,76 @@ static void handle_setup_packet(void)
_setup_packet[i] = setup_buf[i];
}
- // Clearing SETUPIFG by reading USBVECINT does not set NAK, so now that we
- // have a SETUP packet, force NAKs until tinyusb can handle the SETUP
- // packet and prepare for a new xfer.
+ // Force NAKs until tinyusb can handle the SETUP packet and prepare for a new xfer.
USBIEPCNT_0 |= NAK;
USBOEPCNT_0 |= NAK;
+
+ // Clear SETUPIFG to avoid handling in the USBVECINT switch statement.
+ // When handled there the NAKs applied to the endpoints above are
+ // cleared by hardware and the host will receive stale/duplicate data.
+ //
+ // Excerpt from MSP430x5xx and MSP430x6xx Family User's Guide:
+ //
+ // "...the SETUPIFG is cleared upon reading USBIV. In addition, the NAK on
+ // input endpoint 0 and output endpoint 0 is also cleared."
+ USBIEPCNF_0 &= ~UBME; // Errata USB10 workaround.
+ USBOEPCNF_0 &= ~UBME; // Errata USB10 workaround.
+ USBIFG &= ~SETUPIFG;
+ USBIEPCNF_0 |= UBME; // Errata USB10 workaround.
+ USBOEPCNF_0 |= UBME; // Errata USB10 workaround.
dcd_event_setup_received(0, (uint8_t*) &_setup_packet[0], true);
}
+#if CFG_TUSB_OS == OPT_OS_NONE
+TU_ATTR_ALWAYS_INLINE static inline void tu_delay(uint32_t ms) {
+ // msp430 can run up to 25Mhz -> 40ns per cycle. 1 ms = 25000 cycles
+ // each loop need 4 cycle: 1 sub, 1 cmp, 1 jump, 1 nop
+ volatile uint32_t cycles = (25000 * ms) >> 2;
+ while (cycles > 0) {
+ cycles--;
+ asm("nop");
+ }
+}
+#else
+#define tu_delay(ms) osal_task_delay(ms)
+#endif
+
+static void handle_bus_power_event(void *param) {
+ (void) param;
+
+ tu_delay(5); // Bus power settling delay.
+
+ USBKEYPID = USBKEY;
+
+ if(USBPWRCTL & USBBGVBV) { // Event caused by application of bus power.
+ USBPWRCTL |= VBOFFIE; // Enable bus-power-removed interrupt.
+ USBPLLDIVB = USBPLLDIVB; // For some reason the PLL will *NOT* lock unless the divider
+ // register is re-written. The assumption here is that this
+ // register was already properly configured during board-level
+ // initialization.
+ USBPLLCTL |= (UPLLEN | UPFDEN); // Enable the PLL.
+
+ uint16_t attempts = 0;
+ do { // Poll the PLL, checking for a successful lock.
+ USBPLLIR = 0;
+ tu_delay(1);
+ attempts++;
+ } while ((attempts < 10) && (USBPLLIR != 0));
+
+ // A successful lock is indicated by all PLL-related interrupt flags being cleared.
+ if(!USBPLLIR) {
+ dcd_init(0); // Re-initialize the USB module.
+ }
+ } else { // Event caused by removal of bus power.
+ USBPWRCTL |= VBONIE; // Enable bus-power-applied interrupt.
+ USBPLLCTL &= ~(UPLLEN | UPFDEN); // Disable the PLL.
+ USBCNF = 0; // Disable the USB module.
+ dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, false);
+ }
+
+ USBKEYPID = 0;
+}
+
void dcd_int_handler(uint8_t rhport)
{
(void) rhport;
@@ -628,6 +716,7 @@ void dcd_int_handler(uint8_t rhport)
if(setup_status)
{
+ enable_functional_reset(true);
handle_setup_packet();
}
@@ -646,11 +735,32 @@ void dcd_int_handler(uint8_t rhport)
switch(curr_vector)
{
+ case USBVECINT_NONE:
+ break;
+
case USBVECINT_RSTR:
+ enable_functional_reset(false); // Errata USB4 workaround.
bus_reset();
dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
break;
+ case USBVECINT_PWR_VBUSOn:
+ case USBVECINT_PWR_VBUSOff: {
+ USBKEYPID = USBKEY;
+ // Prevent (possibly) unstable power from generating spurious interrupts.
+ USBPWRCTL &= ~(VBONIE | VBOFFIE);
+ USBKEYPID = 0;
+
+ dcd_event_t event;
+
+ event.rhport = 0;
+ event.event_id = USBD_EVENT_FUNC_CALL;
+ event.func_call.func = handle_bus_power_event;
+
+ dcd_event_handler(&event, true);
+ }
+ break;
+
// Clear the (hardware-enforced) NAK on EP 0 after a SETUP packet
// is received. At this point, even though the hardware is no longer
// forcing NAKs, the EP0 NAK bits should still be set to avoid
@@ -675,10 +785,12 @@ void dcd_int_handler(uint8_t rhport)
break;
case USBVECINT_INPUT_ENDPOINT0:
+ enable_functional_reset(true);
transmit_packet(0);
break;
case USBVECINT_OUTPUT_ENDPOINT0:
+ enable_functional_reset(true);
receive_packet(0);
break;
@@ -710,7 +822,6 @@ void dcd_int_handler(uint8_t rhport)
default:
while(true);
- break;
}
}
diff --git a/src/portable/wch/ch32_usbfs_reg.h b/src/portable/wch/ch32_usbfs_reg.h
new file mode 100644
index 000000000..68be64f5e
--- /dev/null
+++ b/src/portable/wch/ch32_usbfs_reg.h
@@ -0,0 +1,175 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 Matthew Tran
+ * Copyright (c) 2024 hathach
+ *
+ * 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 USB_CH32_USBFS_REG_H
+#define USB_CH32_USBFS_REG_H
+
+// https://github.com/openwch/ch32v307/pull/90
+// https://github.com/openwch/ch32v20x/pull/12
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstrict-prototypes"
+#endif
+
+#if CFG_TUSB_MCU == OPT_MCU_CH32F20X
+ #include <ch32f20x.h>
+#elif CFG_TUSB_MCU == OPT_MCU_CH32V103
+ #include <ch32v10x.h>
+ typedef struct
+ {
+ __IO uint8_t BASE_CTRL;
+ __IO uint8_t UDEV_CTRL;
+ __IO uint8_t INT_EN;
+ __IO uint8_t DEV_ADDR;
+ __IO uint8_t Reserve0;
+ __IO uint8_t MIS_ST;
+ __IO uint8_t INT_FG;
+ __IO uint8_t INT_ST;
+ __IO uint32_t RX_LEN;
+ __IO uint8_t UEP4_1_MOD;
+ __IO uint8_t UEP2_3_MOD;
+ __IO uint8_t UEP5_6_MOD;
+ __IO uint8_t UEP7_MOD;
+ __IO uint32_t UEP0_DMA;
+ __IO uint32_t UEP1_DMA;
+ __IO uint32_t UEP2_DMA;
+ __IO uint32_t UEP3_DMA;
+ __IO uint32_t UEP4_DMA;
+ __IO uint32_t UEP5_DMA;
+ __IO uint32_t UEP6_DMA;
+ __IO uint32_t UEP7_DMA;
+ __IO uint16_t UEP0_TX_LEN;
+ __IO uint8_t UEP0_TX_CTRL;
+ __IO uint8_t UEP0_RX_CTRL;
+ __IO uint16_t UEP1_TX_LEN;
+ __IO uint8_t UEP1_TX_CTRL;
+ __IO uint8_t UEP1_RX_CTRL;
+ __IO uint16_t UEP2_TX_LEN;
+ __IO uint8_t UEP2_TX_CTRL;
+ __IO uint8_t UEP2_RX_CTRL;
+ __IO uint16_t UEP3_TX_LEN;
+ __IO uint8_t UEP3_TX_CTRL;
+ __IO uint8_t UEP3_RX_CTRL;
+ __IO uint16_t UEP4_TX_LEN;
+ __IO uint8_t UEP4_TX_CTRL;
+ __IO uint8_t UEP4_RX_CTRL;
+ __IO uint16_t UEP5_TX_LEN;
+ __IO uint8_t UEP5_TX_CTRL;
+ __IO uint8_t UEP5_RX_CTRL;
+ __IO uint16_t UEP6_TX_LEN;
+ __IO uint8_t UEP6_TX_CTRL;
+ __IO uint8_t UEP6_RX_CTRL;
+ __IO uint16_t UEP7_TX_LEN;
+ __IO uint8_t UEP7_TX_CTRL;
+ __IO uint8_t UEP7_RX_CTRL;
+ __IO uint32_t Reserve1;
+ __IO uint32_t OTG_CR;
+ __IO uint32_t OTG_SR;
+ } USBOTG_FS_TypeDef;
+
+ #define USBOTG_FS ((USBOTG_FS_TypeDef *) 0x40023400)
+#elif CFG_TUSB_MCU == OPT_MCU_CH32V20X
+ #include <ch32v20x.h>
+#elif CFG_TUSB_MCU == OPT_MCU_CH32V307
+ #include <ch32v30x.h>
+ #define USBHD_IRQn OTG_FS_IRQn
+#endif
+
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+// CTRL
+#define USBFS_CTRL_DMA_EN (1 << 0)
+#define USBFS_CTRL_CLR_ALL (1 << 1)
+#define USBFS_CTRL_RESET_SIE (1 << 2)
+#define USBFS_CTRL_INT_BUSY (1 << 3)
+#define USBFS_CTRL_SYS_CTRL (1 << 4)
+#define USBFS_CTRL_DEV_PUEN (1 << 5)
+#define USBFS_CTRL_LOW_SPEED (1 << 6)
+#define USBFS_CTRL_HOST_MODE (1 << 7)
+
+// INT_EN
+#define USBFS_INT_EN_BUS_RST (1 << 0)
+#define USBFS_INT_EN_DETECT (1 << 0)
+#define USBFS_INT_EN_TRANSFER (1 << 1)
+#define USBFS_INT_EN_SUSPEND (1 << 2)
+#define USBFS_INT_EN_HST_SOF (1 << 3)
+#define USBFS_INT_EN_FIFO_OV (1 << 4)
+#define USBFS_INT_EN_DEV_NAK (1 << 6)
+#define USBFS_INT_EN_DEV_SOF (1 << 7)
+
+// INT_FG
+#define USBFS_INT_FG_BUS_RST (1 << 0)
+#define USBFS_INT_FG_DETECT (1 << 0)
+#define USBFS_INT_FG_TRANSFER (1 << 1)
+#define USBFS_INT_FG_SUSPEND (1 << 2)
+#define USBFS_INT_FG_HST_SOF (1 << 3)
+#define USBFS_INT_FG_FIFO_OV (1 << 4)
+#define USBFS_INT_FG_SIE_FREE (1 << 5)
+#define USBFS_INT_FG_TOG_OK (1 << 6)
+#define USBFS_INT_FG_IS_NAK (1 << 7)
+
+// INT_ST
+#define USBFS_INT_ST_MASK_UIS_ENDP(x) (((x) >> 0) & 0x0F)
+#define USBFS_INT_ST_MASK_UIS_TOKEN(x) (((x) >> 4) & 0x03)
+
+// UDEV_CTRL
+#define USBFS_UDEV_CTRL_PORT_EN (1 << 0)
+#define USBFS_UDEV_CTRL_GP_BIT (1 << 1)
+#define USBFS_UDEV_CTRL_LOW_SPEED (1 << 2)
+#define USBFS_UDEV_CTRL_DM_PIN (1 << 4)
+#define USBFS_UDEV_CTRL_DP_PIN (1 << 5)
+#define USBFS_UDEV_CTRL_PD_DIS (1 << 7)
+
+// TX_CTRL
+#define USBFS_EP_T_RES_MASK (3 << 0)
+#define USBFS_EP_T_TOG (1 << 2)
+#define USBFS_EP_T_AUTO_TOG (1 << 3)
+
+#define USBFS_EP_T_RES_ACK (0 << 0)
+#define USBFS_EP_T_RES_NYET (1 << 0)
+#define USBFS_EP_T_RES_NAK (2 << 0)
+#define USBFS_EP_T_RES_STALL (3 << 0)
+
+// RX_CTRL
+#define USBFS_EP_R_RES_MASK (3 << 0)
+#define USBFS_EP_R_TOG (1 << 2)
+#define USBFS_EP_R_AUTO_TOG (1 << 3)
+
+#define USBFS_EP_R_RES_ACK (0 << 0)
+#define USBFS_EP_R_RES_NYET (1 << 0)
+#define USBFS_EP_R_RES_NAK (2 << 0)
+#define USBFS_EP_R_RES_STALL (3 << 0)
+
+// token PID
+#define PID_OUT 0
+#define PID_SOF 1
+#define PID_IN 2
+#define PID_SETUP 3
+
+#endif // USB_CH32_USBFS_REG_H
diff --git a/src/portable/wch/ch32_usbhs_reg.h b/src/portable/wch/ch32_usbhs_reg.h
index 9b956231f..87300b497 100644
--- a/src/portable/wch/ch32_usbhs_reg.h
+++ b/src/portable/wch/ch32_usbhs_reg.h
@@ -1,12 +1,51 @@
-#ifndef _USB_CH32_USBHS_REG_H
-#define _USB_CH32_USBHS_REG_H
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 Matthew Tran
+ * Copyright (c) 2024 hathach
+ *
+ * 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.
+ */
-#if (CFG_TUSB_MCU == OPT_MCU_CH32V307)
-#include <ch32v30x.h>
-#elif (CFG_TUSB_MCU == OPT_MCU_CH32F20X)
-#include <ch32f20x.h>
+#ifndef USB_CH32_USBHS_REG_H
+#define USB_CH32_USBHS_REG_H
+
+// https://github.com/openwch/ch32v307/pull/90
+// https://github.com/openwch/ch32v20x/pull/12
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstrict-prototypes"
+#endif
+
+#if CFG_TUSB_MCU == OPT_MCU_CH32V307
+ #include <ch32v30x.h>
+#elif CFG_TUSB_MCU == OPT_MCU_CH32F20X
+ #include <ch32f20x.h>
+#endif
+
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
#endif
+
/******************* GLOBAL ******************/
// USB CONTROL
@@ -36,8 +75,13 @@
// USB DEV AD
#define USBHS_DEV_AD_OFFSET 0x03
+
// USB FRAME_NO
#define USBHS_FRAME_NO_OFFSET 0x04
+#define USBHS_FRAME_NO_NUM_MASK (0x7FF)
+#define USBHS_FRAME_NO_MICROFRAME_SHIFT (11)
+#define USBHS_FRAME_NO_MICROFRAME_MASK (0x7 << USBHS_FRAME_NO_MICROFRAME_SHIFT)
+
// USB SUSPEND
#define USBHS_SUSPEND_OFFSET 0x06
#define USBHS_DEV_REMOTE_WAKEUP (1 << 2)
@@ -47,7 +91,10 @@
// USB SPEED TYPE
#define USBHS_SPEED_TYPE_OFFSET 0x08
-#define USBSPEED_MASK (0x03)
+#define USBHS_SPEED_TYPE_MASK 0x03
+#define USBHS_SPEED_TYPE_FULL 0
+#define USBHS_SPEED_TYPE_HIGH 1
+#define USBHS_SPEED_TYPE_LOW 2
// USB_MIS_ST
#define USBHS_MIS_ST_OFFSET 0x09
@@ -72,12 +119,16 @@
#define USBHS_ISO_ACT_FLAG (1 << 6)
// INT_ST
-#define USBHS_INT_ST_OFFSET 0x0B
-#define USBHS_DEV_UIS_IS_NAK (1 << 7)
-#define USBHS_DEV_UIS_TOG_OK (1 << 6)
-#define MASK_UIS_TOKEN (3 << 4)
-#define MASK_UIS_ENDP (0x0F)
-#define MASK_UIS_H_RES (0x0F)
+#define USBHS_INT_ST_OFFSET 0x0B
+#define USBHS_DEV_UIS_IS_NAK (1 << 7)
+#define USBHS_DEV_UIS_TOG_OK (1 << 6)
+#define MASK_UIS_TOKEN (3 << 4)
+#define USBHS_TOKEN_PID_OUT (0 << 4)
+#define USBHS_TOKEN_PID_SOF (1 << 4)
+#define USBHS_TOKEN_PID_IN (2 << 4)
+#define USBHS_TOKEN_PID_SETUP (3 << 4)
+#define MASK_UIS_ENDP (0x0F)
+#define MASK_UIS_H_RES (0x0F)
#define USBHS_TOGGLE_OK (0x40)
#define USBHS_HOST_RES (0x0f)
@@ -340,10 +391,5 @@
#define USBHS_UH_T_TOG_AUTO (1 << 5)
#define USBHS_UH_T_DATA_NO (1 << 6)
-// 00: OUT, 01:SOF, 10:IN, 11:SETUP
-#define PID_OUT 0
-#define PID_SOF 1
-#define PID_IN 2
-#define PID_SETUP 3
#endif
diff --git a/src/portable/wch/dcd_ch32_usbfs.c b/src/portable/wch/dcd_ch32_usbfs.c
new file mode 100644
index 000000000..9ed370b40
--- /dev/null
+++ b/src/portable/wch/dcd_ch32_usbfs.c
@@ -0,0 +1,344 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 Matthew Tran
+ * Copyright (c) 2024 hathach
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if CFG_TUD_ENABLED && defined(TUP_USBIP_WCH_USBFS) && CFG_TUD_WCH_USBIP_USBFS
+
+#include "device/dcd.h"
+#include "ch32_usbfs_reg.h"
+
+/* private defines */
+#define EP_MAX (8)
+
+#define EP_DMA(ep) ((&USBOTG_FS->UEP0_DMA)[ep])
+#define EP_TX_LEN(ep) ((&USBOTG_FS->UEP0_TX_LEN)[2 * ep])
+#define EP_TX_CTRL(ep) ((&USBOTG_FS->UEP0_TX_CTRL)[4 * ep])
+#define EP_RX_CTRL(ep) ((&USBOTG_FS->UEP0_RX_CTRL)[4 * ep])
+
+/* private data */
+struct usb_xfer {
+ bool valid;
+ uint8_t* buffer;
+ size_t len;
+ size_t processed_len;
+ size_t max_size;
+};
+
+static struct {
+ bool ep0_tog;
+ bool isochronous[EP_MAX];
+ struct usb_xfer xfer[EP_MAX][2];
+ TU_ATTR_ALIGNED(4) uint8_t buffer[EP_MAX][2][64];
+ TU_ATTR_ALIGNED(4) struct {
+ // OUT transfers >64 bytes will overwrite queued IN data!
+ uint8_t out[64];
+ uint8_t in[1023];
+ uint8_t pad;
+ } ep3_buffer;
+} data;
+
+/* private helpers */
+static void update_in(uint8_t rhport, uint8_t ep, bool force) {
+ struct usb_xfer* xfer = &data.xfer[ep][TUSB_DIR_IN];
+ if (xfer->valid) {
+ if (force || xfer->len) {
+ size_t len = TU_MIN(xfer->max_size, xfer->len);
+ if (ep == 0) {
+ memcpy(data.buffer[ep][TUSB_DIR_OUT], xfer->buffer, len); // ep0 uses same chunk
+ } else if (ep == 3) {
+ memcpy(data.ep3_buffer.in, xfer->buffer, len);
+ } else {
+ memcpy(data.buffer[ep][TUSB_DIR_IN], xfer->buffer, len);
+ }
+ xfer->buffer += len;
+ xfer->len -= len;
+ xfer->processed_len += len;
+
+ EP_TX_LEN(ep) = len;
+ if (ep == 0) {
+ EP_TX_CTRL(0) = USBFS_EP_T_RES_ACK | (data.ep0_tog ? USBFS_EP_T_TOG : 0);
+ data.ep0_tog = !data.ep0_tog;
+ } else if (data.isochronous[ep]) {
+ EP_TX_CTRL(ep) = (EP_TX_CTRL(ep) & ~(USBFS_EP_T_RES_MASK)) | USBFS_EP_T_RES_NYET;
+ } else {
+ EP_TX_CTRL(ep) = (EP_TX_CTRL(ep) & ~(USBFS_EP_T_RES_MASK)) | USBFS_EP_T_RES_ACK;
+ }
+ } else {
+ xfer->valid = false;
+ EP_TX_CTRL(ep) = (EP_TX_CTRL(ep) & ~(USBFS_EP_T_RES_MASK)) | USBFS_EP_T_RES_NAK;
+ dcd_event_xfer_complete(
+ rhport, ep | TUSB_DIR_IN_MASK, xfer->processed_len,
+ XFER_RESULT_SUCCESS, true);
+ }
+ }
+}
+
+static void update_out(uint8_t rhport, uint8_t ep, size_t rx_len) {
+ struct usb_xfer* xfer = &data.xfer[ep][TUSB_DIR_OUT];
+ if (xfer->valid) {
+ size_t len = TU_MIN(xfer->max_size, TU_MIN(xfer->len, rx_len));
+ if (ep == 3) {
+ memcpy(xfer->buffer, data.ep3_buffer.out, len);
+ } else {
+ memcpy(xfer->buffer, data.buffer[ep][TUSB_DIR_OUT], len);
+ }
+ xfer->buffer += len;
+ xfer->len -= len;
+ xfer->processed_len += len;
+
+ if (xfer->len == 0 || len < xfer->max_size) {
+ xfer->valid = false;
+ dcd_event_xfer_complete(rhport, ep, xfer->processed_len, XFER_RESULT_SUCCESS, true);
+ }
+
+ if (ep == 0) {
+ EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK;
+ }
+ }
+}
+
+/* public functions */
+void dcd_init(uint8_t rhport) {
+ // init registers
+ USBOTG_FS->BASE_CTRL = USBFS_CTRL_SYS_CTRL | USBFS_CTRL_INT_BUSY | USBFS_CTRL_DMA_EN;
+ USBOTG_FS->UDEV_CTRL = USBFS_UDEV_CTRL_PD_DIS | USBFS_UDEV_CTRL_PORT_EN;
+ USBOTG_FS->DEV_ADDR = 0x00;
+
+ USBOTG_FS->INT_FG = 0xFF;
+ USBOTG_FS->INT_EN = USBFS_INT_EN_BUS_RST | USBFS_INT_EN_TRANSFER | USBFS_INT_EN_SUSPEND;
+
+ // setup endpoint 0
+ EP_DMA(0) = (uint32_t) &data.buffer[0][0];
+ EP_TX_LEN(0) = 0;
+ EP_TX_CTRL(0) = USBFS_EP_T_RES_NAK;
+ EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK;
+
+ // enable other endpoints but NAK everything
+ USBOTG_FS->UEP4_1_MOD = 0xCC;
+ USBOTG_FS->UEP2_3_MOD = 0xCC;
+ USBOTG_FS->UEP5_6_MOD = 0xCC;
+ USBOTG_FS->UEP7_MOD = 0x0C;
+
+ for (uint8_t ep = 1; ep < EP_MAX; ep++) {
+ EP_DMA(ep) = (uint32_t) &data.buffer[ep][0];
+ EP_TX_LEN(ep) = 0;
+ EP_TX_CTRL(ep) = USBFS_EP_T_AUTO_TOG | USBFS_EP_T_RES_NAK;
+ EP_RX_CTRL(ep) = USBFS_EP_R_AUTO_TOG | USBFS_EP_R_RES_NAK;
+ }
+ EP_DMA(3) = (uint32_t) &data.ep3_buffer.out[0];
+
+ dcd_connect(rhport);
+}
+
+void dcd_int_handler(uint8_t rhport) {
+ (void) rhport;
+ uint8_t status = USBOTG_FS->INT_FG;
+ if (status & USBFS_INT_FG_TRANSFER) {
+ uint8_t ep = USBFS_INT_ST_MASK_UIS_ENDP(USBOTG_FS->INT_ST);
+ uint8_t token = USBFS_INT_ST_MASK_UIS_TOKEN(USBOTG_FS->INT_ST);
+
+ switch (token) {
+ case PID_OUT: {
+ uint16_t rx_len = USBOTG_FS->RX_LEN;
+ update_out(rhport, ep, rx_len);
+ break;
+ }
+
+ case PID_IN:
+ update_in(rhport, ep, false);
+ break;
+
+ case PID_SETUP:
+ // setup clears stall
+ EP_TX_CTRL(0) = USBFS_EP_T_RES_NAK;
+ EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK;
+
+ data.ep0_tog = true;
+ dcd_event_setup_received(rhport, &data.buffer[0][TUSB_DIR_OUT][0], true);
+ break;
+ }
+
+ USBOTG_FS->INT_FG = USBFS_INT_FG_TRANSFER;
+ } else if (status & USBFS_INT_FG_BUS_RST) {
+ data.ep0_tog = true;
+ data.xfer[0][TUSB_DIR_OUT].max_size = 64;
+ data.xfer[0][TUSB_DIR_IN].max_size = 64;
+
+ dcd_event_bus_signal(rhport, DCD_EVENT_BUS_RESET, true);
+
+ USBOTG_FS->DEV_ADDR = 0x00;
+ EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK;
+
+ USBOTG_FS->INT_FG = USBFS_INT_FG_BUS_RST;
+ } else if (status & USBFS_INT_FG_SUSPEND) {
+ dcd_event_t event = {.rhport = rhport, .event_id = DCD_EVENT_SUSPEND};
+ dcd_event_handler(&event, true);
+ USBOTG_FS->INT_FG = USBFS_INT_FG_SUSPEND;
+ }
+}
+
+void dcd_int_enable(uint8_t rhport) {
+ (void) rhport;
+ NVIC_EnableIRQ(USBHD_IRQn);
+}
+
+void dcd_int_disable(uint8_t rhport) {
+ (void) rhport;
+ NVIC_DisableIRQ(USBHD_IRQn);
+}
+
+void dcd_set_address(uint8_t rhport, uint8_t dev_addr) {
+ (void) dev_addr;
+ dcd_edpt_xfer(rhport, 0x80, NULL, 0); // zlp status response
+}
+
+void dcd_remote_wakeup(uint8_t rhport) {
+ (void) rhport;
+ // TODO optional
+}
+
+void dcd_connect(uint8_t rhport) {
+ (void) rhport;
+ USBOTG_FS->BASE_CTRL |= USBFS_CTRL_DEV_PUEN;
+}
+
+void dcd_disconnect(uint8_t rhport) {
+ (void) rhport;
+ USBOTG_FS->BASE_CTRL &= ~USBFS_CTRL_DEV_PUEN;
+}
+
+void dcd_sof_enable(uint8_t rhport, bool en) {
+ (void) rhport;
+ (void) en;
+
+ // TODO implement later
+}
+
+void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const* request) {
+ (void) rhport;
+ if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE &&
+ request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD &&
+ request->bRequest == TUSB_REQ_SET_ADDRESS) {
+ USBOTG_FS->DEV_ADDR = (uint8_t) request->wValue;
+ }
+ EP_TX_CTRL(0) = USBFS_EP_T_RES_NAK;
+ EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK;
+}
+
+bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_ep) {
+ (void) rhport;
+ uint8_t ep = tu_edpt_number(desc_ep->bEndpointAddress);
+ uint8_t dir = tu_edpt_dir(desc_ep->bEndpointAddress);
+ TU_ASSERT(ep < EP_MAX);
+
+ data.isochronous[ep] = desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS;
+ data.xfer[ep][dir].max_size = tu_edpt_packet_size(desc_ep);
+
+ if (ep != 0) {
+ if (dir == TUSB_DIR_OUT) {
+ if (data.isochronous[ep]) {
+ EP_RX_CTRL(ep) = USBFS_EP_R_AUTO_TOG | USBFS_EP_R_RES_NYET;
+ } else {
+ EP_RX_CTRL(ep) = USBFS_EP_R_AUTO_TOG | USBFS_EP_R_RES_ACK;
+ }
+ } else {
+ EP_TX_LEN(ep) = 0;
+ EP_TX_CTRL(ep) = USBFS_EP_T_AUTO_TOG | USBFS_EP_T_RES_NAK;
+ }
+ }
+ return true;
+}
+
+void dcd_edpt_close_all(uint8_t rhport) {
+ (void) rhport;
+ // TODO optional
+}
+
+void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) {
+ (void) rhport;
+ (void) ep_addr;
+ // TODO optional
+}
+
+bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) {
+ (void) rhport;
+ uint8_t ep = tu_edpt_number(ep_addr);
+ uint8_t dir = tu_edpt_dir(ep_addr);
+
+ struct usb_xfer* xfer = &data.xfer[ep][dir];
+ dcd_int_disable(rhport);
+ xfer->valid = true;
+ xfer->buffer = buffer;
+ xfer->len = total_bytes;
+ xfer->processed_len = 0;
+ dcd_int_enable(rhport);
+
+ if (dir == TUSB_DIR_IN) {
+ update_in(rhport, ep, true);
+ }
+ return true;
+}
+
+void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
+ (void) rhport;
+ uint8_t ep = tu_edpt_number(ep_addr);
+ uint8_t dir = tu_edpt_dir(ep_addr);
+ if (ep == 0) {
+ if (dir == TUSB_DIR_OUT) {
+ EP_RX_CTRL(0) = USBFS_EP_R_RES_STALL;
+ } else {
+ EP_TX_LEN(0) = 0;
+ EP_TX_CTRL(0) = USBFS_EP_T_RES_STALL;
+ }
+ } else {
+ if (dir == TUSB_DIR_OUT) {
+ EP_RX_CTRL(ep) = (EP_RX_CTRL(ep) & ~USBFS_EP_R_RES_MASK) | USBFS_EP_R_RES_STALL;
+ } else {
+ EP_TX_CTRL(ep) = (EP_TX_CTRL(ep) & ~USBFS_EP_T_RES_MASK) | USBFS_EP_T_RES_STALL;
+ }
+ }
+}
+
+void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
+ (void) rhport;
+ uint8_t ep = tu_edpt_number(ep_addr);
+ uint8_t dir = tu_edpt_dir(ep_addr);
+ if (ep == 0) {
+ if (dir == TUSB_DIR_OUT) {
+ EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK;
+ }
+ } else {
+ if (dir == TUSB_DIR_OUT) {
+ EP_RX_CTRL(ep) = (EP_RX_CTRL(ep) & ~(USBFS_EP_R_RES_MASK | USBFS_EP_R_TOG)) | USBFS_EP_R_RES_ACK;
+ } else {
+ EP_TX_CTRL(ep) = (EP_TX_CTRL(ep) & ~(USBFS_EP_T_RES_MASK | USBFS_EP_T_TOG)) | USBFS_EP_T_RES_NAK;
+ }
+ }
+}
+
+#endif
diff --git a/src/portable/wch/dcd_ch32_usbhs.c b/src/portable/wch/dcd_ch32_usbhs.c
index 1f1c0b876..622f9c508 100644
--- a/src/portable/wch/dcd_ch32_usbhs.c
+++ b/src/portable/wch/dcd_ch32_usbhs.c
@@ -2,6 +2,7 @@
* The MIT License (MIT)
*
* Copyright (c) 2022 Greg Davill
+ * Copyright (c) 2023 Denis Krasutski
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -26,366 +27,394 @@
#include "tusb_option.h"
-#if CFG_TUD_ENABLED && ((CFG_TUSB_MCU == OPT_MCU_CH32V307) || (CFG_TUSB_MCU == OPT_MCU_CH32F20X))
-#include "device/dcd.h"
-
+#if CFG_TUD_ENABLED && defined(TUP_USBIP_WCH_USBHS) && CFG_TUD_WCH_USBIP_USBHS
#include "ch32_usbhs_reg.h"
+#include "device/dcd.h"
// Max number of bi-directional endpoints including EP0
-#define EP_MAX 16
+#define EP_MAX 16
typedef struct {
- uint8_t *buffer;
- // tu_fifo_t * ff; // TODO support dcd_edpt_xfer_fifo API
- uint16_t total_len;
- uint16_t queued_len;
- uint16_t max_size;
- bool short_packet;
+ uint8_t* buffer;
+ uint16_t total_len;
+ uint16_t queued_len;
+ uint16_t max_size;
+ bool is_last_packet;
+ bool is_iso;
} xfer_ctl_t;
+typedef enum {
+ EP_RESPONSE_ACK,
+ EP_RESPONSE_NAK,
+} ep_response_list_t;
+
#define XFER_CTL_BASE(_ep, _dir) &xfer_status[_ep][_dir]
static xfer_ctl_t xfer_status[EP_MAX][2];
-#define EP_TX_LEN(ep) *(volatile uint16_t *)((volatile uint16_t *)&(USBHSD->UEP0_TX_LEN) + (ep)*2)
-#define EP_TX_CTRL(ep) *(volatile uint8_t *)((volatile uint8_t *)&(USBHSD->UEP0_TX_CTRL) + (ep)*4)
-#define EP_RX_CTRL(ep) *(volatile uint8_t *)((volatile uint8_t *)&(USBHSD->UEP0_RX_CTRL) + (ep)*4)
-#define EP_RX_MAX_LEN(ep) *(volatile uint16_t *)((volatile uint16_t *)&(USBHSD->UEP0_MAX_LEN) + (ep)*2)
+#define EP_TX_LEN(ep) *(volatile uint16_t *)((volatile uint16_t *)&(USBHSD->UEP0_TX_LEN) + (ep) * 2)
+#define EP_TX_CTRL(ep) *(volatile uint8_t *)((volatile uint8_t *)&(USBHSD->UEP0_TX_CTRL) + (ep) * 4)
+#define EP_RX_CTRL(ep) *(volatile uint8_t *)((volatile uint8_t *)&(USBHSD->UEP0_RX_CTRL) + (ep) * 4)
+#define EP_RX_MAX_LEN(ep) *(volatile uint16_t *)((volatile uint16_t *)&(USBHSD->UEP0_MAX_LEN) + (ep) * 2)
#define EP_TX_DMA_ADDR(ep) *(volatile uint32_t *)((volatile uint32_t *)&(USBHSD->UEP1_TX_DMA) + (ep - 1))
#define EP_RX_DMA_ADDR(ep) *(volatile uint32_t *)((volatile uint32_t *)&(USBHSD->UEP1_RX_DMA) + (ep - 1))
/* Endpoint Buffer */
-TU_ATTR_ALIGNED(4) uint8_t EP0_DatabufHD[64]; // ep0(64)
+TU_ATTR_ALIGNED(4) static uint8_t ep0_buffer[CFG_TUD_ENDPOINT0_SIZE];
+
+static void ep_set_response_and_toggle(uint8_t ep_num, tusb_dir_t ep_dir, ep_response_list_t response_type) {
+ if (ep_dir == TUSB_DIR_IN) {
+ uint8_t response = (response_type == EP_RESPONSE_ACK) ? USBHS_EP_T_RES_ACK : USBHS_EP_T_RES_NAK;
+ if (ep_num == 0) {
+ if (response_type == EP_RESPONSE_ACK) {
+ if (EP_TX_LEN(ep_num) == 0) {
+ EP_TX_CTRL(ep_num) |= USBHS_EP_T_TOG_1;
+ } else {
+ EP_TX_CTRL(ep_num) ^= USBHS_EP_T_TOG_1;
+ }
+ }
+ }
+ if (xfer_status[ep_num][TUSB_DIR_IN].is_iso == true) {
+ EP_TX_CTRL(ep_num) = USBHS_EP_T_AUTOTOG;
+ } else {
+ EP_TX_CTRL(ep_num) = (EP_TX_CTRL(ep_num) & ~(USBHS_EP_T_RES_MASK)) | response;
+ }
+ } else {
+ uint8_t response = (response_type == EP_RESPONSE_ACK) ? USBHS_EP_R_RES_ACK : USBHS_EP_R_RES_NAK;
+ if (ep_num == 0) {
+ if (response_type == EP_RESPONSE_ACK) {
+ if (xfer_status[ep_num][TUSB_DIR_OUT].queued_len == 0) {
+ EP_RX_CTRL(ep_num) |= USBHS_EP_R_TOG_1;
+ }
+ } else {
+ EP_RX_CTRL(ep_num) ^= USBHS_EP_R_TOG_1;
+ }
+ }
+ EP_RX_CTRL(ep_num) = (EP_RX_CTRL(ep_num) & ~(USBHS_EP_R_RES_MASK)) | response;
+ }
+}
+
+static void xfer_data_packet(uint8_t ep_num, tusb_dir_t ep_dir, xfer_ctl_t* xfer) {
+ if (ep_dir == TUSB_DIR_IN) {
+ uint16_t remaining = xfer->total_len - xfer->queued_len;
+ uint16_t next_tx_size = TU_MIN(remaining, xfer->max_size);
+
+ if (ep_num == 0) {
+ memcpy(ep0_buffer, &xfer->buffer[xfer->queued_len], next_tx_size);
+ } else {
+ EP_TX_DMA_ADDR(ep_num) = (uint32_t) &xfer->buffer[xfer->queued_len];
+ }
+
+ EP_TX_LEN(ep_num) = next_tx_size;
+ xfer->queued_len += next_tx_size;
+ if (xfer->queued_len == xfer->total_len) {
+ xfer->is_last_packet = true;
+ }
+ if (xfer->is_iso == true) {
+ /* Enable EP to generate ISA_ACT interrupt */
+ USBHSD->ENDP_CONFIG |= (USBHS_EP0_T_EN << ep_num);
+ }
+ } else { /* TUSB_DIR_OUT */
+ uint16_t left_to_receive = xfer->total_len - xfer->queued_len;
+ uint16_t max_possible_rx_size = TU_MIN(xfer->max_size, left_to_receive);
-volatile uint8_t USBHS_Dev_Endp0_Tog = 0x01;
+ if (max_possible_rx_size == left_to_receive) {
+ xfer->is_last_packet = true;
+ }
+
+ if (ep_num > 0) {
+ EP_RX_DMA_ADDR(ep_num) = (uint32_t) &xfer->buffer[xfer->queued_len];
+ EP_RX_MAX_LEN(ep_num) = max_possible_rx_size;
+ }
+ }
+ ep_set_response_and_toggle(ep_num, ep_dir, USBHS_EP_R_RES_ACK);
+}
void dcd_init(uint8_t rhport) {
- (void)rhport;
+ (void) rhport;
- memset(&xfer_status, 0, sizeof(xfer_status));
+ memset(&xfer_status, 0, sizeof(xfer_status));
- USBHSD->HOST_CTRL = 0x00;
- USBHSD->HOST_CTRL = USBHS_PHY_SUSPENDM;
+ USBHSD->HOST_CTRL = 0x00;
+ USBHSD->HOST_CTRL = USBHS_PHY_SUSPENDM;
- USBHSD->CONTROL = 0;
+ USBHSD->CONTROL = 0;
#if TUD_OPT_HIGH_SPEED
- USBHSD->CONTROL = USBHS_DMA_EN | USBHS_INT_BUSY_EN | USBHS_HIGH_SPEED;
+ USBHSD->CONTROL = USBHS_DMA_EN | USBHS_INT_BUSY_EN | USBHS_HIGH_SPEED;
#else
- #error OPT_MODE_FULL_SPEED not currently supported on CH32
- USBHSD->CONTROL = USBHS_DMA_EN | USBHS_INT_BUSY_EN | USBHS_FULL_SPEED;
+ #error OPT_MODE_FULL_SPEED not currently supported on CH32
+ USBHSD->CONTROL = USBHS_DMA_EN | USBHS_INT_BUSY_EN | USBHS_FULL_SPEED;
#endif
- USBHSD->INT_EN = 0;
- USBHSD->INT_EN = USBHS_SETUP_ACT_EN | USBHS_TRANSFER_EN | USBHS_DETECT_EN | USBHS_SUSPEND_EN;
-
- /* ALL endpoint enable */
- USBHSD->ENDP_CONFIG = 0xffffffff;
-
- USBHSD->ENDP_CONFIG = USBHS_EP0_T_EN | USBHS_EP0_R_EN;
- USBHSD->ENDP_TYPE = 0x00;
- USBHSD->BUF_MODE = 0x00;
+ USBHSD->INT_EN = 0;
+ USBHSD->INT_EN = USBHS_SETUP_ACT_EN | USBHS_TRANSFER_EN | USBHS_BUS_RST_EN | USBHS_SUSPEND_EN | USBHS_ISO_ACT_EN;
- USBHSD->UEP0_MAX_LEN = 64;
+ USBHSD->ENDP_CONFIG = USBHS_EP0_T_EN | USBHS_EP0_R_EN;
+ USBHSD->ENDP_TYPE = 0x00;
+ USBHSD->BUF_MODE = 0x00;
- USBHSD->UEP0_DMA = (uint32_t)EP0_DatabufHD;
+ for (int ep = 0; ep < EP_MAX; ep++) {
+ EP_TX_LEN(ep) = 0;
+ EP_TX_CTRL(ep) = USBHS_EP_T_AUTOTOG | USBHS_EP_T_RES_NAK;
+ EP_RX_CTRL(ep) = USBHS_EP_R_AUTOTOG | USBHS_EP_R_RES_NAK;
- USBHSD->UEP0_TX_LEN = 0;
- USBHSD->UEP0_TX_CTRL = USBHS_EP_T_RES_NAK;
- USBHSD->UEP0_RX_CTRL = USBHS_EP_R_RES_ACK;
+ EP_RX_MAX_LEN(ep) = 0;
+ }
- for (int ep = 1; ep < EP_MAX; ep++) {
- EP_TX_LEN(ep) = 0;
- EP_TX_CTRL(ep) = USBHS_EP_T_AUTOTOG | USBHS_EP_T_RES_NAK;
- EP_RX_CTRL(ep) = USBHS_EP_R_AUTOTOG | USBHS_EP_R_RES_NAK;
+ USBHSD->UEP0_DMA = (uint32_t) ep0_buffer;
+ USBHSD->UEP0_MAX_LEN = CFG_TUD_ENDPOINT0_SIZE;
+ xfer_status[0][TUSB_DIR_OUT].max_size = CFG_TUD_ENDPOINT0_SIZE;
+ xfer_status[0][TUSB_DIR_IN].max_size = CFG_TUD_ENDPOINT0_SIZE;
- EP_RX_MAX_LEN(ep) = 512;
- }
-
- USBHSD->DEV_AD = 0;
- USBHSD->CONTROL |= USBHS_DEV_PU_EN;
+ USBHSD->DEV_AD = 0;
+ USBHSD->CONTROL |= USBHS_DEV_PU_EN;
}
void dcd_int_enable(uint8_t rhport) {
- (void)rhport;
-
- NVIC_EnableIRQ(USBHS_IRQn);
+ (void) rhport;
+ NVIC_EnableIRQ(USBHS_IRQn);
}
void dcd_int_disable(uint8_t rhport) {
- (void)rhport;
-
- NVIC_DisableIRQ(USBHS_IRQn);
+ (void) rhport;
+ NVIC_DisableIRQ(USBHS_IRQn);
}
void dcd_edpt_close_all(uint8_t rhport) {
- (void)rhport;
+ (void) rhport;
+
+ for (size_t ep = 1; ep < EP_MAX; ep++) {
+ EP_TX_LEN(ep) = 0;
+ EP_TX_CTRL(ep) = USBHS_EP_T_AUTOTOG | USBHS_EP_T_RES_NAK;
+ EP_RX_CTRL(ep) = USBHS_EP_R_AUTOTOG | USBHS_EP_R_RES_NAK;
+
+ EP_RX_MAX_LEN(ep) = 0;
+ }
+
+ USBHSD->ENDP_CONFIG = USBHS_EP0_T_EN | USBHS_EP0_R_EN;
}
void dcd_set_address(uint8_t rhport, uint8_t dev_addr) {
- (void)dev_addr;
+ (void) dev_addr;
- // Response with zlp status
- dcd_edpt_xfer(rhport, 0x80, NULL, 0);
+ // Response with zlp status
+ dcd_edpt_xfer(rhport, 0x80, NULL, 0);
}
-void dcd_remote_wakeup(uint8_t rhport)
-{
+void dcd_remote_wakeup(uint8_t rhport) {
(void) rhport;
}
-void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const *request) {
- (void)rhport;
-
- if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE &&
- request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD &&
- request->bRequest == TUSB_REQ_SET_ADDRESS) {
- USBHSD->DEV_AD = (uint8_t)request->wValue;
- }
-
- EP_TX_CTRL(0) = USBHS_EP_T_RES_NAK;
- EP_RX_CTRL(0) = USBHS_EP_R_RES_ACK;
+void dcd_sof_enable(uint8_t rhport, bool en) {
+ (void) rhport;
+ if (en) {
+ USBHSD->INT_EN |= USBHS_SOF_ACT_EN;
+ } else {
+ USBHSD->INT_EN &= ~(USBHS_SOF_ACT_EN);
+ }
}
-bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_edpt) {
- (void)rhport;
-
- uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress);
- uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress);
-
- TU_ASSERT(epnum < EP_MAX);
-
- xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, dir);
- xfer->max_size = tu_edpt_packet_size(desc_edpt);
-
- if (epnum != 0) {
- if (tu_edpt_dir(desc_edpt->bEndpointAddress) == TUSB_DIR_OUT) {
- EP_RX_CTRL(epnum) = USBHS_EP_R_AUTOTOG | USBHS_EP_R_RES_ACK;
- } else {
- EP_TX_LEN(epnum) = 0;
- EP_TX_CTRL(epnum) = USBHS_EP_T_AUTOTOG | USBHS_EP_T_RES_NAK | USBHS_EP_T_TOG_0;
- }
- }
-
- return true;
-}
+void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const* request) {
+ (void) rhport;
-int usbd_ep_close(const uint8_t ep) {
- (void)ep;
+ if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE &&
+ request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD &&
+ request->bRequest == TUSB_REQ_SET_ADDRESS) {
+ USBHSD->DEV_AD = (uint8_t) request->wValue;
+ }
- return 0;
+ EP_TX_CTRL(0) = USBHS_EP_T_RES_NAK | USBHS_EP_T_TOG_0;
+ EP_RX_CTRL(0) = USBHS_EP_R_RES_NAK | USBHS_EP_R_TOG_0;
}
-void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
- (void)rhport;
- uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_edpt) {
+ (void) rhport;
- if (epnum == 0) {
- if (dir == TUSB_DIR_OUT) {
- USBHSD->UEP0_RX_CTRL = USBHS_EP_R_RES_STALL;
- } else {
- USBHSD->UEP0_TX_LEN = 0;
- USBHSD->UEP0_TX_CTRL = USBHS_EP_T_RES_STALL;
- }
- } else {
- if (dir == TUSB_DIR_OUT) {
- EP_RX_CTRL(epnum) = (EP_RX_CTRL(epnum) & ~USBHS_EP_R_RES_MASK) | USBHS_EP_R_RES_STALL;
+ uint8_t const ep_num = tu_edpt_number(desc_edpt->bEndpointAddress);
+ tusb_dir_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress);
- } else {
- EP_TX_CTRL(epnum) = (EP_TX_CTRL(epnum) & ~USBHS_EP_T_RES_MASK) | USBHS_EP_T_RES_STALL;
- }
- }
-}
+ TU_ASSERT(ep_num < EP_MAX);
-void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
- (void)rhport;
+ if (ep_num == 0) {
+ return true;
+ }
- uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+ xfer_ctl_t* xfer = XFER_CTL_BASE(ep_num, dir);
+ xfer->max_size = tu_edpt_packet_size(desc_edpt);
- if (epnum == 0) {
- if (dir == TUSB_DIR_OUT) {
- USBHSD->UEP0_RX_CTRL = USBHS_EP_R_RES_ACK;
- } else {
- }
+ xfer->is_iso = (desc_edpt->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS);
+ if (dir == TUSB_DIR_OUT) {
+ USBHSD->ENDP_CONFIG |= (USBHS_EP0_R_EN << ep_num);
+ EP_RX_CTRL(ep_num) = USBHS_EP_R_AUTOTOG | USBHS_EP_R_RES_NAK;
+ if (xfer->is_iso == true) {
+ USBHSD->ENDP_TYPE |= (USBHS_EP0_R_TYP << ep_num);
+ }
+ EP_RX_MAX_LEN(ep_num) = xfer->max_size;
+ } else {
+ if (xfer->is_iso == true) {
+ USBHSD->ENDP_TYPE |= (USBHS_EP0_T_TYP << ep_num);
} else {
- if (dir == TUSB_DIR_OUT) {
- EP_RX_CTRL(epnum) = (EP_RX_CTRL(epnum) & ~(USBHS_EP_R_RES_MASK | USBHS_EP_T_TOG_MASK)) | USBHS_EP_T_RES_ACK;
-
- } else {
- EP_TX_CTRL(epnum) = (EP_TX_CTRL(epnum) & ~(USBHS_EP_T_RES_MASK | USBHS_EP_T_TOG_MASK)) | USBHS_EP_T_RES_NAK;
- }
+ /* Enable all types except Isochronous to avoid ISO_ACT interrupt generation */
+ USBHSD->ENDP_CONFIG |= (USBHS_EP0_T_EN << ep_num);
}
-}
-
-bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes) {
- (void)rhport;
- uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+ EP_TX_LEN(ep_num) = 0;
+ EP_TX_CTRL(ep_num) = USBHS_EP_T_AUTOTOG | USBHS_EP_T_RES_NAK | USBHS_EP_T_TOG_0;
+ }
- xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, dir);
- xfer->buffer = buffer;
- // xfer->ff = NULL; // TODO support dcd_edpt_xfer_fifo API
- xfer->total_len = total_bytes;
- xfer->queued_len = 0;
- xfer->short_packet = false;
-
- // uint16_t num_packets = (total_bytes / xfer->max_size);
- uint16_t short_packet_size = total_bytes % (xfer->max_size + 1);
+ return true;
+}
- // Zero-size packet is special case.
- if (short_packet_size == 0 || (total_bytes == 0)) {
- xfer->short_packet = true;
- }
+void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) {
+ (void) rhport;
- if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) {
- if (!total_bytes) {
- xfer->short_packet = true;
- if (epnum == 0) {
- USBHSD->UEP0_TX_LEN = 0;
- USBHSD->UEP0_TX_CTRL = USBHS_EP_T_RES_ACK | (USBHS_Dev_Endp0_Tog ? USBHS_EP_T_TOG_1 : USBHS_EP_T_TOG_0);
- USBHS_Dev_Endp0_Tog ^= 1;
- } else {
- EP_TX_LEN(epnum) = 0;
- EP_TX_CTRL(epnum) = (EP_TX_CTRL(epnum) & ~(USBHS_EP_T_RES_MASK)) | USBHS_EP_T_RES_ACK;
- }
- } else {
- if (epnum == 0) {
- xfer->queued_len += short_packet_size;
- memcpy(&EP0_DatabufHD[0], buffer, short_packet_size);
+ uint8_t const ep_num = tu_edpt_number(ep_addr);
+ tusb_dir_t const dir = tu_edpt_dir(ep_addr);
- USBHSD->UEP0_TX_LEN = short_packet_size;
- USBHSD->UEP0_TX_CTRL = USBHS_EP_T_RES_ACK | (USBHS_Dev_Endp0_Tog ? USBHS_EP_T_TOG_1 : USBHS_EP_T_TOG_0);
- USBHS_Dev_Endp0_Tog ^= 1;
- } else {
- xfer->queued_len += short_packet_size;
+ if (dir == TUSB_DIR_OUT) {
+ EP_RX_CTRL(ep_num) = USBHS_EP_R_AUTOTOG | USBHS_EP_R_RES_NAK;
+ EP_RX_MAX_LEN(ep_num) = 0;
+ USBHSD->ENDP_TYPE &= ~(USBHS_EP0_R_TYP << ep_num);
+ USBHSD->ENDP_CONFIG &= ~(USBHS_EP0_R_EN << ep_num);
+ } else { // TUSB_DIR_IN
+ EP_TX_CTRL(ep_num) = USBHS_EP_T_AUTOTOG | USBHS_EP_T_RES_NAK | USBHS_EP_T_TOG_0;
+ EP_TX_LEN(ep_num) = 0;
+ USBHSD->ENDP_TYPE &= ~(USBHS_EP0_T_TYP << ep_num);
+ USBHSD->ENDP_CONFIG &= ~(USBHS_EP0_T_EN << ep_num);
+ }
+}
- EP_TX_DMA_ADDR(epnum) = (uint32_t)buffer;
- USBHSD->ENDP_CONFIG |= (USBHS_EP0_T_EN << epnum);
- EP_TX_LEN(epnum) = short_packet_size;
- EP_TX_CTRL(epnum) = (EP_TX_CTRL(epnum) & ~(USBHS_EP_T_RES_MASK)) | USBHS_EP_T_RES_ACK;
- }
- }
- } else { /* TUSB_DIR_OUT */
- if (epnum == 0) {
- uint32_t read_count = USBHSD->RX_LEN;
- read_count = TU_MIN(read_count, total_bytes);
+void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
+ (void) rhport;
- if ((total_bytes == 8)) {
- read_count = 8;
- memcpy(buffer, &EP0_DatabufHD[0], 8);
- } else {
- memcpy(buffer, &EP0_DatabufHD[0], read_count);
- }
- } else {
- EP_RX_DMA_ADDR(epnum) = (uint32_t)xfer->buffer;
- USBHSD->ENDP_CONFIG |= (USBHS_EP0_R_EN << epnum);
- }
+ uint8_t const ep_num = tu_edpt_number(ep_addr);
+ tusb_dir_t const dir = tu_edpt_dir(ep_addr);
- // usbd_ep_read(ep_addr, buffer, total_bytes, &ret_bytes);
- }
- return true;
+ if (dir == TUSB_DIR_OUT) {
+ EP_RX_CTRL(ep_num) = USBHS_EP_R_RES_STALL;
+ } else {
+ EP_TX_LEN(0) = 0;
+ EP_TX_CTRL(ep_num) = USBHS_EP_T_RES_STALL;
+ }
}
+void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
+ (void) rhport;
-static void receive_packet(xfer_ctl_t *xfer, uint16_t xfer_size) {
- // xfer->queued_len = xfer->total_len - remaining;
+ uint8_t const ep_num = tu_edpt_number(ep_addr);
+ tusb_dir_t const dir = tu_edpt_dir(ep_addr);
- uint16_t remaining = xfer->total_len - xfer->queued_len;
- uint16_t to_recv_size;
+ if (dir == TUSB_DIR_OUT) {
+ EP_RX_CTRL(ep_num) = USBHS_EP_R_AUTOTOG | USBHS_EP_R_RES_NAK;
+ } else {
+ EP_TX_CTRL(ep_num) = USBHS_EP_T_AUTOTOG | USBHS_EP_R_RES_NAK;
+ }
+}
- if (remaining <= xfer->max_size) {
- // Avoid buffer overflow.
- to_recv_size = (xfer_size > remaining) ? remaining : xfer_size;
- } else {
- // Room for full packet, choose recv_size based on what the microcontroller
- // claims.
- to_recv_size = (xfer_size > xfer->max_size) ? xfer->max_size : xfer_size;
- }
+bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) {
+ (void) rhport;
+ uint8_t const ep_num = tu_edpt_number(ep_addr);
+ tusb_dir_t const dir = tu_edpt_dir(ep_addr);
- if (to_recv_size) {
- }
+ xfer_ctl_t* xfer = XFER_CTL_BASE(ep_num, dir);
+ xfer->buffer = buffer;
+ xfer->total_len = total_bytes;
+ xfer->queued_len = 0;
+ xfer->is_last_packet = false;
- xfer->queued_len += xfer_size;
+ xfer_data_packet(ep_num, dir, xfer);
- // Per USB spec, a short OUT packet (including length 0) is always
- // indicative of the end of a transfer (at least for ctl, bulk, int).
- xfer->short_packet = (xfer_size < xfer->max_size);
+ return true;
}
void dcd_int_handler(uint8_t rhport) {
- (void)rhport;
-
- uint32_t end_num, rx_token;
- uint8_t intflag = 0;
-
- intflag = USBHSD->INT_FG;
-
- if (intflag & USBHS_TRANSFER_FLAG) {
-
- end_num = (USBHSD->INT_ST) & MASK_UIS_ENDP;
- rx_token = (((USBHSD->INT_ST) & MASK_UIS_TOKEN) >> 4) & 0x03;
-
- uint8_t endp = end_num | (rx_token == PID_IN ? TUSB_DIR_IN_MASK : 0);
-
- xfer_ctl_t *xfer = XFER_CTL_BASE(end_num, tu_edpt_dir(endp));
-
- if (rx_token == PID_OUT) {
- uint16_t rx_len = USBHSD->RX_LEN;
-
- receive_packet(xfer, rx_len);
+ (void) rhport;
- if (xfer->short_packet || (xfer->queued_len == xfer->total_len)) {
- xfer->short_packet = false;
+ uint8_t int_flag = USBHSD->INT_FG;
+ uint8_t int_status = USBHSD->INT_ST;
- dcd_event_xfer_complete(0, endp, xfer->queued_len, XFER_RESULT_SUCCESS, true);
- }
+ if (int_flag & (USBHS_ISO_ACT_FLAG | USBHS_TRANSFER_FLAG)) {
+ uint8_t const token = int_status & MASK_UIS_TOKEN;
- if (end_num == 0) {
- USBHSD->UEP0_RX_CTRL = USBHS_EP_R_RES_ACK | USBHS_EP_R_TOG_0;
- }
+ if (token == USBHS_TOKEN_PID_SOF) {
+ uint32_t frame_count = USBHSD->FRAME_NO & USBHS_FRAME_NO_NUM_MASK;
+ dcd_event_sof(rhport, frame_count, true);
+ }else {
+ uint8_t const ep_num = int_status & MASK_UIS_ENDP;
+ tusb_dir_t const ep_dir = (token == USBHS_TOKEN_PID_IN) ? TUSB_DIR_IN : TUSB_DIR_OUT;
+ uint8_t const ep_addr = tu_edpt_addr(ep_num, ep_dir);
+ xfer_ctl_t* xfer = XFER_CTL_BASE(ep_num, ep_dir);
- } else if (rx_token == PID_IN) {
- if (xfer->short_packet || (xfer->queued_len == xfer->total_len)) {
- xfer->short_packet = false;
- xfer->total_len = 0;
- dcd_event_xfer_complete(0, endp, xfer->queued_len, XFER_RESULT_SUCCESS, true);
+ if (token == USBHS_TOKEN_PID_OUT) {
+ uint16_t rx_len = USBHSD->RX_LEN;
- EP_TX_CTRL(end_num) = (EP_TX_CTRL(end_num) & ~(USBHS_EP_T_RES_MASK)) | USBHS_EP_T_RES_NAK;
+ if (ep_num == 0) {
+ memcpy(&xfer->buffer[xfer->queued_len], ep0_buffer, rx_len);
+ }
- if (end_num == 0) {
- }
- } else {
- dcd_edpt_xfer(0, endp, xfer->buffer + xfer->queued_len, xfer->total_len - xfer->queued_len);
- }
+ xfer->queued_len += rx_len;
+ if (rx_len < xfer->max_size) {
+ xfer->is_last_packet = true;
+ }
+ } else if (token == USBHS_TOKEN_PID_IN) {
+ if (xfer->is_iso && xfer->is_last_packet) {
+ /* Disable EP to avoid ISO_ACT interrupt generation */
+ USBHSD->ENDP_CONFIG &= ~(USBHS_EP0_T_EN << ep_num);
+ } else {
+ // Do nothing, no need to update xfer->is_last_packet, it is already updated in xfer_data_packet
}
+ }
- USBHSD->INT_FG = USBHS_TRANSFER_FLAG; /* Clear flag */
- } else if (intflag & USBHS_SETUP_FLAG) {
- USBHS_Dev_Endp0_Tog = 1;
- dcd_event_setup_received(0, EP0_DatabufHD, true);
+ if (xfer->is_last_packet == true) {
+ ep_set_response_and_toggle(ep_num, ep_dir, EP_RESPONSE_NAK);
+ dcd_event_xfer_complete(0, ep_addr, xfer->queued_len, XFER_RESULT_SUCCESS, true);
+ } else {
+ /* prepare next part of packet to xref */
+ xfer_data_packet(ep_num, ep_dir, xfer);
+ }
+ }
- USBHSD->INT_FG = USBHS_SETUP_FLAG; /* Clear flag */
- } else if (intflag & USBHS_DETECT_FLAG) {
- USBHS_Dev_Endp0_Tog = 1;
+ USBHSD->INT_FG = (int_flag & (USBHS_ISO_ACT_FLAG | USBHS_TRANSFER_FLAG)); /* Clear flag */
+ } else if (int_flag & USBHS_SETUP_FLAG) {
+ ep_set_response_and_toggle(0, TUSB_DIR_IN, EP_RESPONSE_NAK);
+ ep_set_response_and_toggle(0, TUSB_DIR_OUT, EP_RESPONSE_NAK);
+ dcd_event_setup_received(0, ep0_buffer, true);
- xfer_status[0][TUSB_DIR_OUT].max_size = 64;
- xfer_status[0][TUSB_DIR_IN].max_size = 64;
+ USBHSD->INT_FG = USBHS_SETUP_FLAG; /* Clear flag */
+ } else if (int_flag & USBHS_BUS_RST_FLAG) {
+ // TODO CH32 does not detect actual speed at this time (should be known at end of reset)
+ // This interrupt probably triggered at start of bus reset
+// tusb_speed_t actual_speed;
+// switch(USBHSD->SPEED_TYPE & USBHS_SPEED_TYPE_MASK){
+// case USBHS_SPEED_TYPE_HIGH:
+// actual_speed = TUSB_SPEED_HIGH;
+// break;
+// case USBHS_SPEED_TYPE_FULL:
+// actual_speed = TUSB_SPEED_FULL;
+// break;
+// case USBHS_SPEED_TYPE_LOW:
+// actual_speed = TUSB_SPEED_LOW;
+// break;
+// default:
+// TU_ASSERT(0,);
+// break;
+// }
+// dcd_event_bus_reset(0, actual_speed, true);
- dcd_event_bus_reset(0, TUSB_SPEED_HIGH, true);
+ dcd_event_bus_reset(0, TUSB_SPEED_HIGH, true);
- USBHSD->DEV_AD = 0;
- USBHSD->UEP0_RX_CTRL = USBHS_EP_R_RES_ACK | USBHS_EP_R_TOG_0;
+ USBHSD->DEV_AD = 0;
+ EP_RX_CTRL(0) = USBHS_EP_R_RES_ACK | USBHS_EP_R_TOG_0;
+ EP_TX_CTRL(0) = USBHS_EP_T_RES_NAK | USBHS_EP_T_TOG_0;
- USBHSD->INT_FG = USBHS_DETECT_FLAG; /* Clear flag */
- } else if (intflag & USBHS_SUSPEND_FLAG) {
- dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_SUSPEND };
- dcd_event_handler(&event, true);
+ USBHSD->INT_FG = USBHS_BUS_RST_FLAG; /* Clear flag */
+ } else if (int_flag & USBHS_SUSPEND_FLAG) {
+ dcd_event_t event = {.rhport = rhport, .event_id = DCD_EVENT_SUSPEND};
+ dcd_event_handler(&event, true);
- USBHSD->INT_FG = USBHS_SUSPEND_FLAG; /* Clear flag */
- }
+ USBHSD->INT_FG = USBHS_SUSPEND_FLAG; /* Clear flag */
+ }
}
#endif
diff --git a/src/tusb.c b/src/tusb.c
index 0092267a1..860e8ac35 100644
--- a/src/tusb.c
+++ b/src/tusb.c
@@ -398,7 +398,7 @@ static void dump_str_line(uint8_t const* buf, uint16_t count) {
tu_printf(" |");
// each line is 16 bytes
for (uint16_t i = 0; i < count; i++) {
- const char ch = buf[i];
+ int ch = buf[i];
tu_printf("%c", isprint(ch) ? ch : '.');
}
tu_printf("|\r\n");
diff --git a/src/tusb.h b/src/tusb.h
index c9d56d3c3..4f69a1414 100644
--- a/src/tusb.h
+++ b/src/tusb.h
@@ -38,8 +38,6 @@
#include "osal/osal.h"
#include "common/tusb_fifo.h"
-#include "class/hid/hid.h"
-
//------------- TypeC -------------//
#if CFG_TUC_ENABLED
#include "typec/usbc.h"
diff --git a/src/tusb_option.h b/src/tusb_option.h
index cbcb69edd..f2cc284dc 100644
--- a/src/tusb_option.h
+++ b/src/tusb_option.h
@@ -29,14 +29,12 @@
#include "common/tusb_compiler.h"
-// Version is release as major.minor.revision eg 1.0.0. though there could be notable APIs before a new release.
-// For notable API changes within a release, we increase the build number.
+// Version is release as major.minor.revision eg 1.0.0
#define TUSB_VERSION_MAJOR 0
-#define TUSB_VERSION_MINOR 16
+#define TUSB_VERSION_MINOR 17
#define TUSB_VERSION_REVISION 0
-#define TUSB_VERSION_BUILD 3
-#define TUSB_VERSION_NUMBER (TUSB_VERSION_MAJOR << 24 | TUSB_VERSION_MINOR << 16 | TUSB_VERSION_REVISION << 8 | TUSB_VERSION_BUILD)
+#define TUSB_VERSION_NUMBER (TUSB_VERSION_MAJOR * 10000 + TUSB_VERSION_MINOR * 100 + TUSB_VERSION_REVISION)
#define TUSB_VERSION_STRING TU_STRING(TUSB_VERSION_MAJOR) "." TU_STRING(TUSB_VERSION_MINOR) "." TU_STRING(TUSB_VERSION_REVISION)
//--------------------------------------------------------------------+
@@ -55,7 +53,8 @@
#define OPT_MCU_LPC18XX 6 ///< NXP LPC18xx
#define OPT_MCU_LPC40XX 7 ///< NXP LPC40xx
#define OPT_MCU_LPC43XX 8 ///< NXP LPC43xx
-#define OPT_MCU_LPC51UXX 9 ///< NXP LPC51U6x
+#define OPT_MCU_LPC51 9 ///< NXP LPC51
+#define OPT_MCU_LPC51UXX OPT_MCU_LPC51 ///< NXP LPC51
#define OPT_MCU_LPC54 10 ///< NXP LPC54
#define OPT_MCU_LPC55 11 ///< NXP LPC55
// legacy naming
@@ -119,6 +118,12 @@
// Espressif
#define OPT_MCU_ESP32S2 900 ///< Espressif ESP32-S2
#define OPT_MCU_ESP32S3 901 ///< Espressif ESP32-S3
+#define OPT_MCU_ESP32 902 ///< Espressif ESP32 (for host max3421e)
+#define OPT_MCU_ESP32C3 903 ///< Espressif ESP32-C3
+#define OPT_MCU_ESP32C6 904 ///< Espressif ESP32-C6
+#define OPT_MCU_ESP32C2 905 ///< Espressif ESP32-C2
+#define OPT_MCU_ESP32H2 906 ///< Espressif ESP32-H2
+#define TUP_MCU_ESPRESSIF (CFG_TUSB_MCU >= 900 && CFG_TUSB_MCU < 1000) // check if Espressif MCU
// Dialog
#define OPT_MCU_DA1469X 1000 ///< Dialog Semiconductor DA1469x
@@ -176,7 +181,8 @@
// WCH
#define OPT_MCU_CH32V307 2200 ///< WCH CH32V307
#define OPT_MCU_CH32F20X 2210 ///< WCH CH32F20x
-
+#define OPT_MCU_CH32V20X 2220 ///< WCH CH32V20X
+#define OPT_MCU_CH32V103 2230 ///< WCH CH32V103
// NXP LPC MCX
#define OPT_MCU_MCXN9 2300 ///< NXP MCX N9 Series
@@ -199,19 +205,9 @@
#define OPT_OS_RTTHREAD 6 ///< RT-Thread
#define OPT_OS_RTX4 7 ///< Keil RTX 4
-// Allow to use command line to change the config name/location
-#ifdef CFG_TUSB_CONFIG_FILE
- #include CFG_TUSB_CONFIG_FILE
-#else
- #include "tusb_config.h"
-#endif
-
-#include "common/tusb_mcu.h"
-
-//--------------------------------------------------------------------
-// RootHub Mode Configuration
-// CFG_TUSB_RHPORTx_MODE contains operation mode and speed for that port
-//--------------------------------------------------------------------
+//--------------------------------------------------------------------+
+// Mode and Speed
+//--------------------------------------------------------------------+
// Low byte is operational mode
#define OPT_MODE_NONE 0x0000 ///< Disabled
@@ -225,7 +221,24 @@
#define OPT_MODE_HIGH_SPEED 0x0400 ///< High Speed
#define OPT_MODE_SPEED_MASK 0xff00
-//------------- Roothub as Device -------------//
+//--------------------------------------------------------------------+
+// Include tusb_config.h and tusb_mcu.h
+//--------------------------------------------------------------------+
+
+// Allow to use command line to change the config name/location
+#ifdef CFG_TUSB_CONFIG_FILE
+ #include CFG_TUSB_CONFIG_FILE
+#else
+ #include "tusb_config.h"
+#endif
+
+#include "common/tusb_mcu.h"
+
+//--------------------------------------------------------------------
+// RootHub Mode detection
+//--------------------------------------------------------------------
+
+//------------- Root hub as Device -------------//
#if defined(CFG_TUSB_RHPORT0_MODE) && ((CFG_TUSB_RHPORT0_MODE) & OPT_MODE_DEVICE)
#define TUD_RHPORT_MODE (CFG_TUSB_RHPORT0_MODE)
@@ -253,7 +266,7 @@
// highspeed support indicator
#define TUD_OPT_HIGH_SPEED (CFG_TUD_MAX_SPEED ? (CFG_TUD_MAX_SPEED & OPT_MODE_HIGH_SPEED) : TUP_RHPORT_HIGHSPEED)
-//------------- Roothub as Host -------------//
+//------------- Root hub as Host -------------//
#if defined(CFG_TUSB_RHPORT0_MODE) && ((CFG_TUSB_RHPORT0_MODE) & OPT_MODE_HOST)
#define TUH_RHPORT_MODE (CFG_TUSB_RHPORT0_MODE)
@@ -359,6 +372,20 @@
#define CFG_TUD_INTERFACE_MAX 16
#endif
+// default to max hardware endpoint, but can be smaller to save RAM
+#ifndef CFG_TUD_ENDPPOINT_MAX
+ #define CFG_TUD_ENDPPOINT_MAX TUP_DCD_ENDPOINT_MAX
+#endif
+
+#if CFG_TUD_ENDPPOINT_MAX > TUP_DCD_ENDPOINT_MAX
+ #error "CFG_TUD_ENDPPOINT_MAX must be less than or equal to TUP_DCD_ENDPOINT_MAX"
+#endif
+
+// USB 2.0 7.1.20: compliance test mode support
+#ifndef CFG_TUD_TEST_MODE
+ #define CFG_TUD_TEST_MODE 0
+#endif
+
//------------- Device Class Driver -------------//
#ifndef CFG_TUD_BTH
#define CFG_TUD_BTH 0
@@ -454,26 +481,26 @@
#define CFG_TUH_CDC 0
#endif
+// FTDI is not part of CDC class, only to re-use CDC driver API
#ifndef CFG_TUH_CDC_FTDI
- // FTDI is not part of CDC class, only to re-use CDC driver API
#define CFG_TUH_CDC_FTDI 0
#endif
+// List of product IDs that can use the FTDI CDC driver. 0x0403 is FTDI's VID
#ifndef CFG_TUH_CDC_FTDI_VID_PID_LIST
- // List of product IDs that can use the FTDI CDC driver. 0x0403 is FTDI's VID
#define CFG_TUH_CDC_FTDI_VID_PID_LIST \
{0x0403, 0x6001}, {0x0403, 0x6006}, {0x0403, 0x6010}, {0x0403, 0x6011}, \
{0x0403, 0x6014}, {0x0403, 0x6015}, {0x0403, 0x8372}, {0x0403, 0xFBFA}, \
{0x0403, 0xCD18}
#endif
+// CP210X is not part of CDC class, only to re-use CDC driver API
#ifndef CFG_TUH_CDC_CP210X
- // CP210X is not part of CDC class, only to re-use CDC driver API
#define CFG_TUH_CDC_CP210X 0
#endif
+// List of product IDs that can use the CP210X CDC driver. 0x10C4 is Silicon Labs' VID
#ifndef CFG_TUH_CDC_CP210X_VID_PID_LIST
- // List of product IDs that can use the CP210X CDC driver. 0x10C4 is Silicon Labs' VID
#define CFG_TUH_CDC_CP210X_VID_PID_LIST \
{0x10C4, 0xEA60}, {0x10C4, 0xEA70}
#endif
@@ -483,8 +510,8 @@
#define CFG_TUH_CDC_CH34X 0
#endif
+// List of product IDs that can use the CH34X CDC driver
#ifndef CFG_TUH_CDC_CH34X_VID_PID_LIST
- // List of product IDs that can use the CH34X CDC driver
#define CFG_TUH_CDC_CH34X_VID_PID_LIST \
{ 0x1a86, 0x5523 }, /* ch341 chip */ \
{ 0x1a86, 0x7522 }, /* ch340k chip */ \
diff --git a/src/typec/tcd.h b/src/typec/tcd.h
index 86499c951..bcbdab8ed 100644
--- a/src/typec/tcd.h
+++ b/src/typec/tcd.h
@@ -63,7 +63,7 @@ typedef struct TU_ATTR_PACKED {
} xfer_complete;
};
-} tcd_event_t;;
+} tcd_event_t;
//--------------------------------------------------------------------+
//