summaryrefslogtreecommitdiff
path: root/src/class
diff options
context:
space:
mode:
Diffstat (limited to 'src/class')
-rw-r--r--src/class/audio/audio_device.c543
-rw-r--r--src/class/audio/audio_device.h6
-rwxr-xr-xsrc/class/bth/bth_device.c4
-rwxr-xr-xsrc/class/bth/bth_device.h7
-rw-r--r--src/class/cdc/cdc.h34
-rw-r--r--src/class/cdc/cdc_host.c921
-rw-r--r--src/class/cdc/cdc_host.h15
-rw-r--r--src/class/cdc/serial/ch34x.h84
-rw-r--r--src/class/cdc/serial/cp210x.h2
-rw-r--r--src/class/cdc/serial/ftdi_sio.h5
-rw-r--r--src/class/hid/hid_host.c10
-rw-r--r--src/class/hid/hid_host.h4
-rw-r--r--src/class/msc/msc_host.c265
-rw-r--r--src/class/msc/msc_host.h8
-rw-r--r--src/class/vendor/vendor_device.c8
-rw-r--r--src/class/video/video.h234
-rw-r--r--src/class/video/video_device.c26
17 files changed, 1495 insertions, 681 deletions
diff --git a/src/class/audio/audio_device.c b/src/class/audio/audio_device.c
index 1a7ce7870..4adc558a6 100644
--- a/src/class/audio/audio_device.c
+++ b/src/class/audio/audio_device.c
@@ -110,24 +110,36 @@
#error Maximum number of audio functions restricted to three!
#endif
+// Put sw_buf in USB section only if necessary
+#if USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_ENCODING
+#define IN_SW_BUF_MEM_SECTION
+#else
+#define IN_SW_BUF_MEM_SECTION CFG_TUD_MEM_SECTION
+#endif
+#if USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_DECODING
+#define OUT_SW_BUF_MEM_SECTION
+#else
+#define OUT_SW_BUF_MEM_SECTION CFG_TUD_MEM_SECTION
+#endif
+
// EP IN software buffers and mutexes
#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
#if CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ > 0
- CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_in_sw_buf_1[CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ];
+ IN_SW_BUF_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_in_sw_buf_1[CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ];
#if CFG_FIFO_MUTEX
osal_mutex_def_t ep_in_ff_mutex_wr_1; // No need for read mutex as only USB driver reads from FIFO
#endif
#endif // CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ > 0
#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ > 0
- CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_in_sw_buf_2[CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ];
+ IN_SW_BUF_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_in_sw_buf_2[CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ];
#if CFG_FIFO_MUTEX
osal_mutex_def_t ep_in_ff_mutex_wr_2; // No need for read mutex as only USB driver reads from FIFO
#endif
#endif // CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ > 0
#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ > 0
- CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_in_sw_buf_3[CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ];
+ IN_SW_BUF_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_in_sw_buf_3[CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ];
#if CFG_FIFO_MUTEX
osal_mutex_def_t ep_in_ff_mutex_wr_3; // No need for read mutex as only USB driver reads from FIFO
#endif
@@ -154,21 +166,21 @@
// EP OUT software buffers and mutexes
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
#if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ > 0
- CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_out_sw_buf_1[CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ];
+ OUT_SW_BUF_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_out_sw_buf_1[CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ];
#if CFG_FIFO_MUTEX
osal_mutex_def_t ep_out_ff_mutex_rd_1; // No need for write mutex as only USB driver writes into FIFO
#endif
#endif // CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ > 0
#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ > 0
- CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_out_sw_buf_2[CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ];
+ OUT_SW_BUF_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_out_sw_buf_2[CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ];
#if CFG_FIFO_MUTEX
osal_mutex_def_t ep_out_ff_mutex_rd_2; // No need for write mutex as only USB driver writes into FIFO
#endif
#endif // CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ > 0
#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ > 0
- CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_out_sw_buf_3[CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ];
+ OUT_SW_BUF_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_out_sw_buf_3[CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ];
#if CFG_FIFO_MUTEX
osal_mutex_def_t ep_out_ff_mutex_rd_3; // No need for write mutex as only USB driver writes into FIFO
#endif
@@ -217,7 +229,7 @@ uint8_t alt_setting_3[CFG_TUD_AUDIO_FUNC_3_N_AS_INT];
// Software encoding/decoding support FIFOs
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
#if CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ > 0
- CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t tx_supp_ff_buf_1[CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ];
+ CFG_TUSB_MEM_ALIGN uint8_t tx_supp_ff_buf_1[CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ];
tu_fifo_t tx_supp_ff_1[CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO];
#if CFG_FIFO_MUTEX
osal_mutex_def_t tx_supp_ff_mutex_wr_1[CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO]; // No need for read mutex as only USB driver reads from FIFO
@@ -225,7 +237,7 @@ uint8_t alt_setting_3[CFG_TUD_AUDIO_FUNC_3_N_AS_INT];
#endif
#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ > 0
- CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t tx_supp_ff_buf_2[CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ];
+ CFG_TUSB_MEM_ALIGN uint8_t tx_supp_ff_buf_2[CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ];
tu_fifo_t tx_supp_ff_2[CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO];
#if CFG_FIFO_MUTEX
osal_mutex_def_t tx_supp_ff_mutex_wr_2[CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO]; // No need for read mutex as only USB driver reads from FIFO
@@ -233,7 +245,7 @@ uint8_t alt_setting_3[CFG_TUD_AUDIO_FUNC_3_N_AS_INT];
#endif
#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ > 0
- CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t tx_supp_ff_buf_3[CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ];
+ CFG_TUSB_MEM_ALIGN uint8_t tx_supp_ff_buf_3[CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ];
tu_fifo_t tx_supp_ff_3[CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO];
#if CFG_FIFO_MUTEX
osal_mutex_def_t tx_supp_ff_mutex_wr_3[CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO]; // No need for read mutex as only USB driver reads from FIFO
@@ -243,7 +255,7 @@ uint8_t alt_setting_3[CFG_TUD_AUDIO_FUNC_3_N_AS_INT];
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
#if CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ > 0
- CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t rx_supp_ff_buf_1[CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ];
+ CFG_TUSB_MEM_ALIGN uint8_t rx_supp_ff_buf_1[CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ];
tu_fifo_t rx_supp_ff_1[CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO];
#if CFG_FIFO_MUTEX
osal_mutex_def_t rx_supp_ff_mutex_rd_1[CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO]; // No need for write mutex as only USB driver writes into FIFO
@@ -251,7 +263,7 @@ uint8_t alt_setting_3[CFG_TUD_AUDIO_FUNC_3_N_AS_INT];
#endif
#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ > 0
- CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t rx_supp_ff_buf_2[CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ];
+ CFG_TUSB_MEM_ALIGN uint8_t rx_supp_ff_buf_2[CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ];
tu_fifo_t rx_supp_ff_2[CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO];
#if CFG_FIFO_MUTEX
osal_mutex_def_t rx_supp_ff_mutex_rd_2[CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO]; // No need for write mutex as only USB driver writes into FIFO
@@ -259,7 +271,7 @@ uint8_t alt_setting_3[CFG_TUD_AUDIO_FUNC_3_N_AS_INT];
#endif
#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ > 0
- CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t rx_supp_ff_buf_3[CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ];
+ CFG_TUSB_MEM_ALIGN uint8_t rx_supp_ff_buf_3[CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ];
tu_fifo_t rx_supp_ff_3[CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO];
#if CFG_FIFO_MUTEX
osal_mutex_def_t rx_supp_ff_mutex_rd_3[CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO]; // No need for write mutex as only USB driver writes into FIFO
@@ -364,14 +376,21 @@ typedef struct
#endif
#endif
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
+ uint32_t sample_rate_tx;
+ uint16_t packet_sz_tx[3];
+ uint8_t bclock_id_tx;
+ uint8_t interval_tx;
+#endif
+
// Encoding parameters - parameters are set when alternate AS interface is set by host
-#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
+#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;
#if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
audio_data_format_type_I_t format_type_I_tx;
- uint8_t n_bytes_per_sampe_tx;
uint8_t n_channels_per_ff_tx;
uint8_t n_ff_used_tx;
#endif
@@ -444,7 +463,7 @@ static bool audiod_verify_itf_exists(uint8_t itf, uint8_t *func_id);
static bool audiod_verify_ep_exists(uint8_t ep, uint8_t *func_id);
static uint8_t audiod_get_audio_fct_idx(audiod_function_t * audio);
-#if CFG_TUD_AUDIO_ENABLE_ENCODING || CFG_TUD_AUDIO_ENABLE_DECODING
+#if (CFG_TUD_AUDIO_ENABLE_EP_IN && (CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL || CFG_TUD_AUDIO_ENABLE_ENCODING)) || (CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING)
static void audiod_parse_for_AS_params(audiod_function_t* audio, uint8_t const * p_desc, uint8_t const * p_desc_end, uint8_t const as_itf);
static inline uint8_t tu_desc_subtype(void const* desc)
@@ -453,6 +472,11 @@ static inline uint8_t tu_desc_subtype(void const* desc)
}
#endif
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
+static bool audiod_calc_tx_packet_sz(audiod_function_t* audio);
+static uint16_t audiod_tx_packet_size(const uint16_t* norminal_size, uint16_t data_count, uint16_t fifo_depth, uint16_t max_size);
+#endif
+
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
static bool set_fb_params_freq(audiod_function_t* audio, uint32_t sample_freq, uint32_t mclk_freq);
#endif
@@ -631,73 +655,55 @@ static bool audiod_rx_done_cb(uint8_t rhport, audiod_function_t* audio, uint16_t
// Decoding according to 2.3.1.5 Audio Streams
// Helper function
-static inline uint8_t * audiod_interleaved_copy_bytes_fast_decode(uint16_t const nBytesToCopy, void * dst, uint8_t * dst_end, uint8_t * src, uint8_t const n_ff_used)
+static inline void * audiod_interleaved_copy_bytes_fast_decode(uint16_t const nBytesPerSample, void * dst, const void * dst_end, void * src, uint8_t const n_ff_used)
{
+ // Due to one FIFO contains 2 channels, data always aligned to (nBytesPerSample * 2)
+ uint16_t * dst16 = dst;
+ uint16_t * src16 = src;
+ const uint16_t * dst_end16 = dst_end;
+ uint32_t * dst32 = dst;
+ uint32_t * src32 = src;
+ const uint32_t * dst_end32 = dst_end;
- // This function is an optimized version of
- // while((uint8_t *)dst < dst_end)
- // {
- // memcpy(dst, src, nBytesToCopy);
- // dst = (uint8_t *)dst + nBytesToCopy;
- // src += nBytesToCopy * n_ff_used;
- // }
-
- // Optimize for fast half word copies
- typedef struct{
- uint16_t val;
- } __attribute((__packed__)) unaligned_uint16_t;
-
- // Optimize for fast word copies
- typedef struct{
- uint32_t val;
- } __attribute((__packed__)) unaligned_uint32_t;
-
- switch (nBytesToCopy)
+ if (nBytesPerSample == 1)
{
- case 1:
- while((uint8_t *)dst < dst_end)
- {
- *(uint8_t *)dst++ = *src;
- src += n_ff_used;
- }
- break;
-
- case 2:
- while((uint8_t *)dst < dst_end)
- {
- *(unaligned_uint16_t*)dst = *(unaligned_uint16_t*)src;
- dst += 2;
- src += 2 * n_ff_used;
- }
- break;
-
- case 3:
- while((uint8_t *)dst < dst_end)
- {
- // memcpy(dst, src, 3);
- // dst = (uint8_t *)dst + 3;
- // src += 3 * n_ff_used;
-
- // TODO: Is there a faster way to copy 3 bytes?
- *(uint8_t *)dst++ = *src++;
- *(uint8_t *)dst++ = *src++;
- *(uint8_t *)dst++ = *src++;
-
- src += 3 * (n_ff_used - 1);
- }
- break;
-
- case 4:
- while((uint8_t *)dst < dst_end)
- {
- *(unaligned_uint32_t*)dst = *(unaligned_uint32_t*)src;
- dst += 4;
- src += 4 * n_ff_used;
- }
- break;
+ while(dst16 < dst_end16)
+ {
+ *dst16++ = *src16++;
+ src16 += n_ff_used - 1;
+ }
+ return src16;
+ }
+ else if (nBytesPerSample == 2)
+ {
+ while(dst32 < dst_end32)
+ {
+ *dst32++ = *src32++;
+ src32 += n_ff_used - 1;
+ }
+ return src32;
+ }
+ else if (nBytesPerSample == 3)
+ {
+ while(dst16 < dst_end16)
+ {
+ *dst16++ = *src16++;
+ *dst16++ = *src16++;
+ *dst16++ = *src16++;
+ src16 += 3 * (n_ff_used - 1);
+ }
+ return src16;
+ }
+ else // nBytesPerSample == 4
+ {
+ while(dst32 < dst_end32)
+ {
+ *dst32++ = *src32++;
+ *dst32++ = *src32++;
+ src32 += 2 * (n_ff_used - 1);
+ }
+ return src32;
}
-
- return src;
}
static bool audiod_decode_type_I_pcm(uint8_t rhport, audiod_function_t* audio, uint16_t n_bytes_received)
@@ -839,7 +845,6 @@ uint16_t tud_audio_int_ctr_n_write(uint8_t func_id, uint8_t const* buffer, uint1
#endif
-
// This function is called once a transmit of an audio packet was successfully completed. Here, we encode samples and place it in IN EP's buffer for next transmission.
// If you prefer your own (more efficient) implementation suiting your purpose set CFG_TUD_AUDIO_ENABLE_ENCODING = 0 and use tud_audio_n_write.
@@ -904,9 +909,12 @@ static bool audiod_tx_done_cb(uint8_t rhport, audiod_function_t * audio)
#else
// No support FIFOs, if no linear buffer required schedule transmit, else put data into linear buffer and schedule
-
+#if CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
+ // packet_sz_tx is based on total packet size, here we want size for each support buffer.
+ n_bytes_tx = audiod_tx_packet_size(audio->packet_sz_tx, tu_fifo_count(&audio->ep_in_ff), audio->ep_in_ff.depth, audio->ep_in_sz);
+#else
n_bytes_tx = tu_min16(tu_fifo_count(&audio->ep_in_ff), audio->ep_in_sz); // Limit up to max packet size, more can not be done for ISO
-
+#endif
#if USE_LINEAR_BUFFER_TX
tu_fifo_read_n(&audio->ep_in_ff, audio->lin_buf_in, n_bytes_tx);
TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_in, audio->lin_buf_in, n_bytes_tx));
@@ -944,64 +952,55 @@ range [-1, +1)
* */
// Helper function
-static inline uint8_t * audiod_interleaved_copy_bytes_fast_encode(uint16_t const nBytesToCopy, uint8_t * src, uint8_t * src_end, uint8_t * dst, uint8_t const n_ff_used)
+static inline void * audiod_interleaved_copy_bytes_fast_encode(uint16_t const nBytesPerSample, void * src, const void * src_end, void * dst, uint8_t const n_ff_used)
{
- // Optimize for fast half word copies
- typedef struct{
- uint16_t val;
- } __attribute((__packed__)) unaligned_uint16_t;
+ // Due to one FIFO contains 2 channels, data always aligned to (nBytesPerSample * 2)
+ uint16_t * dst16 = dst;
+ uint16_t * src16 = src;
+ const uint16_t * src_end16 = src_end;
+ uint32_t * dst32 = dst;
+ uint32_t * src32 = src;
+ const uint32_t * src_end32 = src_end;
- // Optimize for fast word copies
- typedef struct{
- uint32_t val;
- } __attribute((__packed__)) unaligned_uint32_t;
-
- switch (nBytesToCopy)
+ if (nBytesPerSample == 1)
{
- case 1:
- while(src < src_end)
- {
- *dst = *src++;
- dst += n_ff_used;
- }
- break;
-
- case 2:
- while(src < src_end)
- {
- *(unaligned_uint16_t*)dst = *(unaligned_uint16_t*)src;
- src += 2;
- dst += 2 * n_ff_used;
- }
- break;
-
- case 3:
- while(src < src_end)
- {
- // memcpy(dst, src, 3);
- // src = (uint8_t *)src + 3;
- // dst += 3 * n_ff_used;
-
- // TODO: Is there a faster way to copy 3 bytes?
- *dst++ = *src++;
- *dst++ = *src++;
- *dst++ = *src++;
-
- dst += 3 * (n_ff_used - 1);
- }
- break;
-
- case 4:
- while(src < src_end)
- {
- *(unaligned_uint32_t*)dst = *(unaligned_uint32_t*)src;
- src += 4;
- dst += 4 * n_ff_used;
- }
- break;
+ while(src16 < src_end16)
+ {
+ *dst16++ = *src16++;
+ dst16 += n_ff_used - 1;
+ }
+ return dst16;
+ }
+ else if (nBytesPerSample == 2)
+ {
+ while(src32 < src_end32)
+ {
+ *dst32++ = *src32++;
+ dst32 += n_ff_used - 1;
+ }
+ return dst32;
+ }
+ else if (nBytesPerSample == 3)
+ {
+ while(src16 < src_end16)
+ {
+ *dst16++ = *src16++;
+ *dst16++ = *src16++;
+ *dst16++ = *src16++;
+ dst16 += 3 * (n_ff_used - 1);
+ }
+ return dst16;
+ }
+ else // nBytesPerSample == 4
+ {
+ while(src32 < src_end32)
+ {
+ *dst32++ = *src32++;
+ *dst32++ = *src32++;
+ dst32 += 2 * (n_ff_used - 1);
+ }
+ return dst32;
}
-
- return dst;
}
static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t* audio)
@@ -1014,8 +1013,6 @@ static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t* audi
// Determine amount of samples
uint8_t const n_ff_used = audio->n_ff_used_tx;
- uint16_t const nBytesToCopy = audio->n_channels_per_ff_tx * audio->n_bytes_per_sampe_tx;
- uint16_t const capPerFF = audio->ep_in_sz / n_ff_used; // Sample capacity per FIFO in bytes
uint16_t nBytesPerFFToSend = tu_fifo_count(&audio->tx_supp_ff[0]);
uint8_t cnt_ff;
@@ -1028,14 +1025,23 @@ static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t* audi
}
}
- // Check if there is enough
+#if CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
+ const uint16_t norm_packet_sz_tx[3] = {audio->packet_sz_tx[0] / n_ff_used,
+ audio->packet_sz_tx[1] / n_ff_used,
+ audio->packet_sz_tx[2] / n_ff_used};
+ // packet_sz_tx is based on total packet size, here we want size for each support buffer.
+ nBytesPerFFToSend = audiod_tx_packet_size(norm_packet_sz_tx, nBytesPerFFToSend, audio->tx_supp_ff[0].depth, audio->ep_in_sz / n_ff_used);
+ // Check if there is enough data
+ if (nBytesPerFFToSend == 0) return 0;
+#else
+ // Check if there is enough data
if (nBytesPerFFToSend == 0) return 0;
-
// 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, capPerFF);
-
+ nBytesPerFFToSend = tu_min16(nBytesPerFFToSend, audio->ep_in_sz / n_ff_used);
// Round to full number of samples (flooring)
- nBytesPerFFToSend = (nBytesPerFFToSend / nBytesToCopy) * nBytesToCopy;
+ uint16_t const nSlotSize = audio->n_channels_per_ff_tx * audio->n_bytes_per_sampe_tx;
+ nBytesPerFFToSend = (nBytesPerFFToSend / nSlotSize) * nSlotSize;
+#endif
// Encode
uint8_t * dst;
@@ -1298,7 +1304,7 @@ void audiod_init(void)
#endif // CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
// Set encoding parameters for Type_I formats
-#if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
switch (i)
{
#if CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ > 0
@@ -1478,82 +1484,116 @@ uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin
}
#if USE_ISO_EP_ALLOCATION
+ {
#if CFG_TUD_AUDIO_ENABLE_EP_IN
- uint8_t ep_in = 0;
- uint16_t ep_in_size = 0;
+ uint8_t ep_in = 0;
+ uint16_t ep_in_size = 0;
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
- uint8_t ep_out = 0;
- uint16_t ep_out_size = 0;
+ uint8_t ep_out = 0;
+ uint16_t ep_out_size = 0;
#endif
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
- uint8_t ep_fb = 0;
+ uint8_t ep_fb = 0;
#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)
- {
- if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT)
+ 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)
{
- tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) p_desc;
- if (desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS)
+ if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT)
{
- #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
- // Explicit feedback EP
- if (desc_ep->bmAttributes.usage == 1)
+ tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) p_desc;
+ if (desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS)
{
- ep_fb = desc_ep->bEndpointAddress;
- }
+ #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ // Explicit feedback EP
+ if (desc_ep->bmAttributes.usage == 1)
+ {
+ ep_fb = desc_ep->bEndpointAddress;
+ }
#endif
- // Data EP
- if (desc_ep->bmAttributes.usage == 0)
- {
- if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN)
+ // Data EP
+ if (desc_ep->bmAttributes.usage == 0)
{
+ if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN)
+ {
#if CFG_TUD_AUDIO_ENABLE_EP_IN
- ep_in = desc_ep->bEndpointAddress;
- ep_in_size = TU_MAX(tu_edpt_packet_size(desc_ep), ep_in_size);
+ ep_in = desc_ep->bEndpointAddress;
+ ep_in_size = TU_MAX(tu_edpt_packet_size(desc_ep), ep_in_size);
#endif
- } else
- {
+ } else
+ {
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
- ep_out = desc_ep->bEndpointAddress;
- ep_out_size = TU_MAX(tu_edpt_packet_size(desc_ep), ep_out_size);
+ ep_out = desc_ep->bEndpointAddress;
+ ep_out_size = TU_MAX(tu_edpt_packet_size(desc_ep), ep_out_size);
#endif
+ }
}
- }
+ }
}
+
+ p_desc = tu_desc_next(p_desc);
}
- p_desc = tu_desc_next(p_desc);
- }
#if CFG_TUD_AUDIO_ENABLE_EP_IN
- if (ep_in)
- {
- usbd_edpt_iso_alloc(rhport, ep_in, ep_in_size);
- }
+ if (ep_in)
+ {
+ usbd_edpt_iso_alloc(rhport, ep_in, ep_in_size);
+ }
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
- if (ep_out)
- {
- usbd_edpt_iso_alloc(rhport, ep_out, ep_out_size);
- }
+ if (ep_out)
+ {
+ usbd_edpt_iso_alloc(rhport, ep_out, ep_out_size);
+ }
#endif
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
- if (ep_fb)
- {
- usbd_edpt_iso_alloc(rhport, ep_fb, 4);
- }
+ if (ep_fb)
+ {
+ usbd_edpt_iso_alloc(rhport, ep_fb, 4);
+ }
#endif
-
+ }
#endif // USE_ISO_EP_ALLOCATION
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
+ {
+ 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;
+ // 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 *desc_ep = (tusb_desc_endpoint_t const *) p_desc;
+ if (desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS)
+ {
+ if (desc_ep->bmAttributes.usage == 0)
+ {
+ if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN)
+ {
+ _audiod_fct[i].interval_tx = desc_ep->bInterval;
+ }
+ }
+ }
+ } else
+ if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && tu_desc_subtype(p_desc) == AUDIO_CS_AC_INTERFACE_OUTPUT_TERMINAL)
+ {
+ if(tu_unaligned_read16(p_desc + 4) == AUDIO_TERM_TYPE_USB_STREAMING)
+ {
+ _audiod_fct[i].bclock_id_tx = p_desc[8];
+ }
+ }
+ p_desc = tu_desc_next(p_desc);
+ }
+ }
+#endif // CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
+
break;
}
}
@@ -1634,6 +1674,11 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
audio->ep_in = 0; // Necessary?
+ #if CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
+ audio->packet_sz_tx[0] = 0;
+ audio->packet_sz_tx[1] = 0;
+ audio->packet_sz_tx[2] = 0;
+ #endif
}
#endif // CFG_TUD_AUDIO_ENABLE_EP_IN
@@ -1684,7 +1729,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
// 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)
{
-#if CFG_TUD_AUDIO_ENABLE_ENCODING || CFG_TUD_AUDIO_ENABLE_DECODING
+#if (CFG_TUD_AUDIO_ENABLE_EP_IN && (CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL || CFG_TUD_AUDIO_ENABLE_ENCODING)) || (CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING)
uint8_t const * p_desc_parse_for_params = p_desc;
#endif
// From this point forward follow the EP descriptors associated to the current alternate setting interface - Open EPs if necessary
@@ -1713,12 +1758,13 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
audio->ep_in_sz = tu_edpt_packet_size(desc_ep);
// If software encoding is enabled, parse for the corresponding parameters - doing this here means only AS interfaces with EPs get scanned for parameters
- #if CFG_TUD_AUDIO_ENABLE_ENCODING
+ #if CFG_TUD_AUDIO_ENABLE_ENCODING || CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
audiod_parse_for_AS_params(audio, p_desc_parse_for_params, p_desc_end, itf);
// 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_ENCODING
- const uint16_t active_fifo_depth = (uint16_t) ((audio->tx_supp_ff_sz_max / audio->n_bytes_per_sampe_tx) * audio->n_bytes_per_sampe_tx);
+ #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));
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);
@@ -1850,6 +1896,10 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
if (disable) usbd_sof_enable(rhport, false);
#endif
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
+ audiod_calc_tx_packet_sz(audio);
+#endif
+
tud_control_status(rhport, p_request);
return true;
@@ -2292,6 +2342,19 @@ bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_req
// Copy into buffer
TU_VERIFY(0 == tu_memcpy_s(_audiod_fct[func_id].ctrl_buf, _audiod_fct[func_id].ctrl_buf_sz, data, (size_t)len));
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
+ // Find data for sampling_frequency_control
+ if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS && p_request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE)
+ {
+ uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
+ uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+ if (_audiod_fct[func_id].bclock_id_tx == entityID && ctrlSel == AUDIO_CS_CTRL_SAM_FREQ && p_request->bRequest == AUDIO_CS_REQ_CUR)
+ {
+ _audiod_fct[func_id].sample_rate_tx = tu_unaligned_read32(_audiod_fct[func_id].ctrl_buf);
+ }
+ }
+#endif
+
// Schedule transmit
return tud_control_xfer(rhport, p_request, (void*)_audiod_fct[func_id].ctrl_buf, len);
}
@@ -2431,7 +2494,7 @@ static bool audiod_verify_ep_exists(uint8_t ep, uint8_t *func_id)
return false;
}
-#if CFG_TUD_AUDIO_ENABLE_ENCODING || CFG_TUD_AUDIO_ENABLE_DECODING
+#if (CFG_TUD_AUDIO_ENABLE_EP_IN && (CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL || CFG_TUD_AUDIO_ENABLE_ENCODING)) || (CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING)
// p_desc points to the AS interface of alternate setting zero
// itf is the interface number of the corresponding interface - we check if the interface belongs to EP in or EP out to see if it is a TX or RX parameter
// Currently, only AS interfaces with an EP (in or out) are supposed to be parsed for!
@@ -2469,7 +2532,7 @@ static void audiod_parse_for_AS_params(audiod_function_t* audio, uint8_t const *
}
#endif
-#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
if (as_itf == audio->ep_out_as_intf_num)
{
audio->n_channels_rx = ((audio_desc_cs_as_interface_t const * )p_desc)->bNrChannels;
@@ -2482,7 +2545,7 @@ static void audiod_parse_for_AS_params(audiod_function_t* audio, uint8_t const *
}
// Look for a Type I Format Type Descriptor(2.3.1.6 - Audio Formats)
-#if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING || CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
+#if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING || CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL || CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && tu_desc_subtype(p_desc) == AUDIO_CS_AS_INTERFACE_FORMAT_TYPE && ((audio_desc_type_I_format_t const * )p_desc)->bFormatType == AUDIO_FORMAT_TYPE_I)
{
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_EP_OUT
@@ -2502,7 +2565,7 @@ static void audiod_parse_for_AS_params(audiod_function_t* audio, uint8_t const *
}
#endif
-#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+#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;
@@ -2518,6 +2581,96 @@ static void audiod_parse_for_AS_params(audiod_function_t* audio, uint8_t const *
}
#endif
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
+
+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->interval_tx);
+ TU_VERIFY(audio->sample_rate_tx);
+
+ const uint8_t interval = (tud_speed_get() == TUSB_SPEED_FULL) ? audio->interval_tx : 1 << (audio->interval_tx - 1);
+
+ 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);
+
+ // Endpoint size must larger than packet size
+ TU_ASSERT(packet_sz_tx_max <= audio->ep_in_sz);
+
+ // Frmt20.pdf 2.3.1.1 USB Packets
+ if (sample_reminder)
+ {
+ // All virtual frame packets must either contain INT(nav) audio slots (small VFP) or INT(nav)+1 (large VFP) audio slots
+ audio->packet_sz_tx[0] = packet_sz_tx_norm;
+ audio->packet_sz_tx[1] = packet_sz_tx_norm;
+ audio->packet_sz_tx[2] = packet_sz_tx_max;
+ } else
+ {
+ // In the case where nav = INT(nav), ni may vary between INT(nav)-1 (small VFP), INT(nav)
+ // (medium VFP) and INT(nav)+1 (large VFP).
+ audio->packet_sz_tx[0] = packet_sz_tx_min;
+ audio->packet_sz_tx[1] = packet_sz_tx_norm;
+ audio->packet_sz_tx[2] = packet_sz_tx_max;
+ }
+
+ return true;
+}
+
+static uint16_t audiod_tx_packet_size(const uint16_t* norminal_size, uint16_t data_count, uint16_t fifo_depth, uint16_t max_depth)
+{
+ // Flow control need a FIFO size of at least 4*Navg
+ if(norminal_size[1] && norminal_size[1] <= fifo_depth * 4)
+ {
+ // Use blackout to prioritize normal size packet
+ static int ctrl_blackout = 0;
+ uint16_t packet_size;
+ uint16_t slot_size = norminal_size[2] - norminal_size[1];
+ if (data_count < norminal_size[0])
+ {
+ // If you get here frequently, then your I2S clock deviation is too big !
+ packet_size = 0;
+ } else
+ if (data_count < fifo_depth / 2 - slot_size && !ctrl_blackout)
+ {
+ packet_size = norminal_size[0];
+ ctrl_blackout = 10;
+ } else
+ if (data_count > fifo_depth / 2 + slot_size && !ctrl_blackout)
+ {
+ packet_size = norminal_size[2];
+ if(norminal_size[0] == norminal_size[1])
+ {
+ // nav > INT(nav), eg. 44.1k, 88.2k
+ ctrl_blackout = 0;
+ } else
+ {
+ // nav = INT(nav), eg. 48k, 96k
+ ctrl_blackout = 10;
+ }
+ } else
+ {
+ packet_size = norminal_size[1];
+ if (ctrl_blackout)
+ {
+ ctrl_blackout--;
+ }
+ }
+ // Normally this cap is not necessary
+ return tu_min16(packet_size, max_depth);
+ } else
+ {
+ return tu_min16(data_count, max_depth);
+ }
+}
+
+#endif
+
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback)
diff --git a/src/class/audio/audio_device.h b/src/class/audio/audio_device.h
index 7c88b99fc..ef3e12a06 100644
--- a/src/class/audio/audio_device.h
+++ b/src/class/audio/audio_device.h
@@ -181,6 +181,11 @@
#endif
#endif
+// (For TYPE-I format only) Flow control is necessary to allow IN ep send correct amount of data, unless it's a virtual device where data is perfectly synchronized to USB clock.
+#ifndef CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
+#define CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL 1
+#endif
+
// Enable/disable feedback EP (required for asynchronous RX applications)
#ifndef CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP 0 // Feedback - 0 or 1
@@ -392,6 +397,7 @@ tu_fifo_t* tud_audio_n_get_tx_support_ff (uint8_t func_id, uint8_t ff_i
uint16_t tud_audio_int_ctr_n_write (uint8_t func_id, uint8_t const* buffer, uint16_t len);
#endif
+
//--------------------------------------------------------------------+
// Application API (Interface0)
//--------------------------------------------------------------------+
diff --git a/src/class/bth/bth_device.c b/src/class/bth/bth_device.c
index c5c74d26a..f145e2ead 100755
--- a/src/class/bth/bth_device.c
+++ b/src/class/bth/bth_device.c
@@ -204,7 +204,9 @@ bool btd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t c
request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE)
{
// HCI command packet addressing for single function Primary Controllers
- TU_VERIFY(request->bRequest == 0 && request->wValue == 0 && request->wIndex == 0);
+ // also compatible with historical mode if enabled
+ TU_VERIFY((request->bRequest == 0 && request->wValue == 0 && request->wIndex == 0) ||
+ (CFG_TUD_BTH_HISTORICAL_COMPATIBLE && request->bRequest == 0xe0));
}
else if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE)
{
diff --git a/src/class/bth/bth_device.h b/src/class/bth/bth_device.h
index 921bd7a1a..e782c532b 100755
--- a/src/class/bth/bth_device.h
+++ b/src/class/bth/bth_device.h
@@ -36,10 +36,17 @@
#ifndef CFG_TUD_BTH_EVENT_EPSIZE
#define CFG_TUD_BTH_EVENT_EPSIZE 16
#endif
+
#ifndef CFG_TUD_BTH_DATA_EPSIZE
#define CFG_TUD_BTH_DATA_EPSIZE 64
#endif
+// Allow BTH class to work in historically compatibility mode where the bRequest is always 0xe0.
+// See Bluetooth Core v5.3, Vol. 4, Part B, Section 2.2
+#ifndef CFG_TUD_BTH_HISTORICAL_COMPATIBLE
+#define CFG_TUD_BTH_HISTORICAL_COMPATIBLE 0
+#endif
+
typedef struct TU_ATTR_PACKED
{
uint16_t op_code;
diff --git a/src/class/cdc/cdc.h b/src/class/cdc/cdc.h
index 4658e43af..5cbd658fe 100644
--- a/src/class/cdc/cdc.h
+++ b/src/class/cdc/cdc.h
@@ -136,8 +136,7 @@ typedef enum{
//--------------------------------------------------------------------+
/// Communication Interface Management Element Request Codes
-typedef enum
-{
+typedef enum {
CDC_REQUEST_SEND_ENCAPSULATED_COMMAND = 0x00, ///< is used to issue a command in the format of the supported control protocol of the Communications Class interface
CDC_REQUEST_GET_ENCAPSULATED_RESPONSE = 0x01, ///< is used to request a response in the format of the supported control protocol of the Communications Class interface.
CDC_REQUEST_SET_COMM_FEATURE = 0x02,
@@ -180,37 +179,38 @@ typedef enum
CDC_REQUEST_GET_ATM_VC_STATISTICS = 0x53,
CDC_REQUEST_MDLM_SEMANTIC_MODEL = 0x60,
-}cdc_management_request_t;
+} cdc_management_request_t;
-enum
-{
+typedef enum {
CDC_CONTROL_LINE_STATE_DTR = 0x01,
CDC_CONTROL_LINE_STATE_RTS = 0x02,
-};
+} cdc_control_line_state_t;
-enum
-{
- CDC_LINE_CONDING_STOP_BITS_1 = 0, // 1 bit
- CDC_LINE_CONDING_STOP_BITS_1_5 = 1, // 1.5 bits
- CDC_LINE_CONDING_STOP_BITS_2 = 2, // 2 bits
-};
+typedef enum {
+ CDC_LINE_CODING_STOP_BITS_1 = 0, // 1 bit
+ CDC_LINE_CODING_STOP_BITS_1_5 = 1, // 1.5 bits
+ CDC_LINE_CODING_STOP_BITS_2 = 2, // 2 bits
+} cdc_line_coding_stopbits_t;
-enum
-{
+// TODO Backward compatible for typos. Maybe removed in the future release
+#define CDC_LINE_CONDING_STOP_BITS_1 CDC_LINE_CODING_STOP_BITS_1
+#define CDC_LINE_CONDING_STOP_BITS_1_5 CDC_LINE_CODING_STOP_BITS_1_5
+#define CDC_LINE_CONDING_STOP_BITS_2 CDC_LINE_CODING_STOP_BITS_2
+
+typedef enum {
CDC_LINE_CODING_PARITY_NONE = 0,
CDC_LINE_CODING_PARITY_ODD = 1,
CDC_LINE_CODING_PARITY_EVEN = 2,
CDC_LINE_CODING_PARITY_MARK = 3,
CDC_LINE_CODING_PARITY_SPACE = 4,
-};
+} cdc_line_coding_parity_t;
//--------------------------------------------------------------------+
// Management Element Notification (Notification Endpoint)
//--------------------------------------------------------------------+
/// 6.3 Notification Codes
-typedef enum
-{
+typedef enum {
CDC_NOTIF_NETWORK_CONNECTION = 0x00, ///< This notification allows the device to notify the host about network connection status.
CDC_NOTIF_RESPONSE_AVAILABLE = 0x01, ///< This notification allows the device to notify the hostthat a response is available. This response can be retrieved with a subsequent \ref CDC_REQUEST_GET_ENCAPSULATED_RESPONSE request.
CDC_NOTIF_AUX_JACK_HOOK_STATE = 0x08,
diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c
index 53e15710e..2fb37d835 100644
--- a/src/class/cdc/cdc_host.c
+++ b/src/class/cdc/cdc_host.c
@@ -22,6 +22,9 @@
* THE SOFTWARE.
*
* This file is part of the TinyUSB stack.
+ *
+ * Contribution
+ * - Heiko Kuester: CH34x support
*/
#include "tusb_option.h"
@@ -50,12 +53,18 @@ typedef struct {
uint8_t bInterfaceSubClass;
uint8_t bInterfaceProtocol;
+ uint8_t ep_notif;
uint8_t serial_drid; // Serial Driver ID
+ bool mounted; // Enumeration is complete
cdc_acm_capability_t acm_capability;
- uint8_t ep_notif;
- uint8_t line_state; // DTR (bit0), RTS (bit1)
TU_ATTR_ALIGNED(4) cdc_line_coding_t line_coding; // Baudrate, stop bits, parity, data width
+ uint8_t line_state; // DTR (bit0), RTS (bit1)
+
+ #if CFG_TUH_CDC_FTDI || CFG_TUH_CDC_CP210X || CFG_TUH_CDC_CH34X
+ cdc_line_coding_t requested_line_coding;
+ // 1 byte padding
+ #endif
tuh_xfer_cb_t user_control_cb;
@@ -69,7 +78,6 @@ typedef struct {
uint8_t rx_ff_buf[CFG_TUH_CDC_TX_BUFSIZE];
CFG_TUH_MEM_ALIGN uint8_t rx_ep_buf[CFG_TUH_CDC_TX_EPSIZE];
} stream;
-
} cdch_interface_t;
CFG_TUH_MEM_SECTION
@@ -80,47 +88,60 @@ static cdch_interface_t cdch_data[CFG_TUH_CDC];
//--------------------------------------------------------------------+
//------------- ACM prototypes -------------//
+static bool acm_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
static void acm_process_config(tuh_xfer_t* xfer);
+static bool acm_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool acm_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
static bool acm_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
static bool acm_set_control_line_state(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
-static bool acm_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
//------------- FTDI prototypes -------------//
#if CFG_TUH_CDC_FTDI
#include "serial/ftdi_sio.h"
-static uint16_t const ftdi_pids[] = { TU_FTDI_PID_LIST };
-enum {
- FTDI_PID_COUNT = sizeof(ftdi_pids) / sizeof(ftdi_pids[0])
-};
-
-// Store last request baudrate since divisor to baudrate is not easy
-static uint32_t _ftdi_requested_baud;
+static uint16_t const ftdi_vid_pid_list[][2] = {CFG_TUH_CDC_FTDI_VID_PID_LIST};
static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len);
static void ftdi_process_config(tuh_xfer_t* xfer);
-static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ftdi_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ftdi_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
#endif
//------------- CP210X prototypes -------------//
#if CFG_TUH_CDC_CP210X
#include "serial/cp210x.h"
-static uint16_t const cp210x_pids[] = { TU_CP210X_PID_LIST };
-enum {
- CP210X_PID_COUNT = sizeof(cp210x_pids) / sizeof(cp210x_pids[0])
-};
+static uint16_t const cp210x_vid_pid_list[][2] = {CFG_TUH_CDC_CP210X_VID_PID_LIST};
static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
static void cp210x_process_config(tuh_xfer_t* xfer);
-static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool cp210x_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool cp210x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+#endif
+
+//------------- CH34x prototypes -------------//
+#if CFG_TUH_CDC_CH34X
+#include "serial/ch34x.h"
+
+static uint16_t const ch34x_vid_pid_list[][2] = {CFG_TUH_CDC_CH34X_VID_PID_LIST};
+
+static bool ch34x_open(uint8_t daddr, tusb_desc_interface_t const* itf_desc, uint16_t max_len);
+static void ch34x_process_config(tuh_xfer_t* xfer);
+
+static bool ch34x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ch34x_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ch34x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ch34x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
#endif
+//------------- Common -------------//
enum {
SERIAL_DRIVER_ACM = 0,
@@ -131,60 +152,96 @@ enum {
#if CFG_TUH_CDC_CP210X
SERIAL_DRIVER_CP210X,
#endif
+
+#if CFG_TUH_CDC_CH34X
+ SERIAL_DRIVER_CH34X,
+#endif
+
+ SERIAL_DRIVER_COUNT
};
typedef struct {
+ uint16_t const (*vid_pid_list)[2];
+ uint16_t const vid_pid_count;
+ bool (*const open)(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len);
void (*const process_set_config)(tuh_xfer_t* xfer);
bool (*const set_control_line_state)(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
bool (*const set_baudrate)(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+ bool (*const set_data_format)(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+ bool (*const set_line_coding)(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
} cdch_serial_driver_t;
// Note driver list must be in the same order as SERIAL_DRIVER enum
static const cdch_serial_driver_t serial_drivers[] = {
- { .process_set_config = acm_process_config,
- .set_control_line_state = acm_set_control_line_state,
- .set_baudrate = acm_set_baudrate
+ {
+ .vid_pid_list = NULL,
+ .vid_pid_count = 0,
+ .open = acm_open,
+ .process_set_config = acm_process_config,
+ .set_control_line_state = acm_set_control_line_state,
+ .set_baudrate = acm_set_baudrate,
+ .set_data_format = acm_set_data_format,
+ .set_line_coding = acm_set_line_coding
},
#if CFG_TUH_CDC_FTDI
- { .process_set_config = ftdi_process_config,
- .set_control_line_state = ftdi_sio_set_modem_ctrl,
- .set_baudrate = ftdi_sio_set_baudrate
+ {
+ .vid_pid_list = ftdi_vid_pid_list,
+ .vid_pid_count = TU_ARRAY_SIZE(ftdi_vid_pid_list),
+ .open = ftdi_open,
+ .process_set_config = ftdi_process_config,
+ .set_control_line_state = ftdi_sio_set_modem_ctrl,
+ .set_baudrate = ftdi_sio_set_baudrate,
+ .set_data_format = ftdi_set_data_format,
+ .set_line_coding = ftdi_set_line_coding
},
#endif
#if CFG_TUH_CDC_CP210X
- { .process_set_config = cp210x_process_config,
- .set_control_line_state = cp210x_set_modem_ctrl,
- .set_baudrate = cp210x_set_baudrate
+ {
+ .vid_pid_list = cp210x_vid_pid_list,
+ .vid_pid_count = TU_ARRAY_SIZE(cp210x_vid_pid_list),
+ .open = cp210x_open,
+ .process_set_config = cp210x_process_config,
+ .set_control_line_state = cp210x_set_modem_ctrl,
+ .set_baudrate = cp210x_set_baudrate,
+ .set_data_format = cp210x_set_data_format,
+ .set_line_coding = cp210x_set_line_coding
},
#endif
-};
-enum {
- SERIAL_DRIVER_COUNT = sizeof(serial_drivers) / sizeof(serial_drivers[0])
+ #if CFG_TUH_CDC_CH34X
+ {
+ .vid_pid_list = ch34x_vid_pid_list,
+ .vid_pid_count = TU_ARRAY_SIZE(ch34x_vid_pid_list),
+ .open = ch34x_open,
+ .process_set_config = ch34x_process_config,
+ .set_control_line_state = ch34x_set_modem_ctrl,
+ .set_baudrate = ch34x_set_baudrate,
+ .set_data_format = ch34x_set_data_format,
+ .set_line_coding = ch34x_set_line_coding
+ },
+ #endif
};
+TU_VERIFY_STATIC(TU_ARRAY_SIZE(serial_drivers) == SERIAL_DRIVER_COUNT, "Serial driver count mismatch");
+
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
-static inline cdch_interface_t* get_itf(uint8_t idx)
-{
+static inline cdch_interface_t* get_itf(uint8_t idx) {
TU_ASSERT(idx < CFG_TUH_CDC, NULL);
cdch_interface_t* p_cdc = &cdch_data[idx];
return (p_cdc->daddr != 0) ? p_cdc : NULL;
}
-static inline uint8_t get_idx_by_ep_addr(uint8_t daddr, uint8_t ep_addr)
-{
- for(uint8_t i=0; i<CFG_TUH_CDC; i++)
- {
+static inline uint8_t get_idx_by_ep_addr(uint8_t daddr, uint8_t ep_addr) {
+ for(uint8_t i=0; i<CFG_TUH_CDC; i++) {
cdch_interface_t* p_cdc = &cdch_data[i];
if ( (p_cdc->daddr == daddr) &&
- (ep_addr == p_cdc->ep_notif || ep_addr == p_cdc->stream.rx.ep_addr || ep_addr == p_cdc->stream.tx.ep_addr))
- {
+ (ep_addr == p_cdc->ep_notif || ep_addr == p_cdc->stream.rx.ep_addr || ep_addr == p_cdc->stream.tx.ep_addr)) {
return i;
}
}
@@ -192,14 +249,10 @@ static inline uint8_t get_idx_by_ep_addr(uint8_t daddr, uint8_t ep_addr)
return TUSB_INDEX_INVALID_8;
}
-
-static cdch_interface_t* make_new_itf(uint8_t daddr, tusb_desc_interface_t const *itf_desc)
-{
- for(uint8_t i=0; i<CFG_TUH_CDC; i++)
- {
+static cdch_interface_t* make_new_itf(uint8_t daddr, tusb_desc_interface_t const *itf_desc) {
+ for(uint8_t i=0; i<CFG_TUH_CDC; i++) {
if (cdch_data[i].daddr == 0) {
cdch_interface_t* p_cdc = &cdch_data[i];
-
p_cdc->daddr = daddr;
p_cdc->bInterfaceNumber = itf_desc->bInterfaceNumber;
p_cdc->bInterfaceSubClass = itf_desc->bInterfaceSubClass;
@@ -220,20 +273,16 @@ static void cdch_internal_control_complete(tuh_xfer_t* xfer);
// APPLICATION API
//--------------------------------------------------------------------+
-uint8_t tuh_cdc_itf_get_index(uint8_t daddr, uint8_t itf_num)
-{
- for(uint8_t i=0; i<CFG_TUH_CDC; i++)
- {
+uint8_t tuh_cdc_itf_get_index(uint8_t daddr, uint8_t itf_num) {
+ for (uint8_t i = 0; i < CFG_TUH_CDC; i++) {
const cdch_interface_t* p_cdc = &cdch_data[i];
-
if (p_cdc->daddr == daddr && p_cdc->bInterfaceNumber == itf_num) return i;
}
return TUSB_INDEX_INVALID_8;
}
-bool tuh_cdc_itf_get_info(uint8_t idx, tuh_itf_info_t* info)
-{
+bool tuh_cdc_itf_get_info(uint8_t idx, tuh_itf_info_t* info) {
cdch_interface_t* p_cdc = get_itf(idx);
TU_VERIFY(p_cdc && info);
@@ -255,30 +304,27 @@ bool tuh_cdc_itf_get_info(uint8_t idx, tuh_itf_info_t* info)
return true;
}
-bool tuh_cdc_mounted(uint8_t idx)
-{
+bool tuh_cdc_mounted(uint8_t idx) {
cdch_interface_t* p_cdc = get_itf(idx);
- return p_cdc != NULL;
+ TU_VERIFY(p_cdc);
+ return p_cdc->mounted;
}
-bool tuh_cdc_get_dtr(uint8_t idx)
-{
+bool tuh_cdc_get_dtr(uint8_t idx) {
cdch_interface_t* p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
return (p_cdc->line_state & CDC_CONTROL_LINE_STATE_DTR) ? true : false;
}
-bool tuh_cdc_get_rts(uint8_t idx)
-{
+bool tuh_cdc_get_rts(uint8_t idx) {
cdch_interface_t* p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
return (p_cdc->line_state & CDC_CONTROL_LINE_STATE_RTS) ? true : false;
}
-bool tuh_cdc_get_local_line_coding(uint8_t idx, cdc_line_coding_t* line_coding)
-{
+bool tuh_cdc_get_local_line_coding(uint8_t idx, cdc_line_coding_t* line_coding) {
cdch_interface_t* p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
@@ -291,32 +337,28 @@ bool tuh_cdc_get_local_line_coding(uint8_t idx, cdc_line_coding_t* line_coding)
// Write
//--------------------------------------------------------------------+
-uint32_t tuh_cdc_write(uint8_t idx, void const* buffer, uint32_t bufsize)
-{
+uint32_t tuh_cdc_write(uint8_t idx, void const* buffer, uint32_t bufsize) {
cdch_interface_t* p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
return tu_edpt_stream_write(&p_cdc->stream.tx, buffer, bufsize);
}
-uint32_t tuh_cdc_write_flush(uint8_t idx)
-{
+uint32_t tuh_cdc_write_flush(uint8_t idx) {
cdch_interface_t* p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
return tu_edpt_stream_write_xfer(&p_cdc->stream.tx);
}
-bool tuh_cdc_write_clear(uint8_t idx)
-{
+bool tuh_cdc_write_clear(uint8_t idx) {
cdch_interface_t* p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
return tu_edpt_stream_clear(&p_cdc->stream.tx);
}
-uint32_t tuh_cdc_write_available(uint8_t idx)
-{
+uint32_t tuh_cdc_write_available(uint8_t idx) {
cdch_interface_t* p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
@@ -327,32 +369,28 @@ uint32_t tuh_cdc_write_available(uint8_t idx)
// Read
//--------------------------------------------------------------------+
-uint32_t tuh_cdc_read (uint8_t idx, void* buffer, uint32_t bufsize)
-{
+uint32_t tuh_cdc_read (uint8_t idx, void* buffer, uint32_t bufsize) {
cdch_interface_t* p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
return tu_edpt_stream_read(&p_cdc->stream.rx, buffer, bufsize);
}
-uint32_t tuh_cdc_read_available(uint8_t idx)
-{
+uint32_t tuh_cdc_read_available(uint8_t idx) {
cdch_interface_t* p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
return tu_edpt_stream_read_available(&p_cdc->stream.rx);
}
-bool tuh_cdc_peek(uint8_t idx, uint8_t* ch)
-{
+bool tuh_cdc_peek(uint8_t idx, uint8_t* ch) {
cdch_interface_t* p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
return tu_edpt_stream_peek(&p_cdc->stream.rx, ch);
}
-bool tuh_cdc_read_clear (uint8_t idx)
-{
+bool tuh_cdc_read_clear (uint8_t idx) {
cdch_interface_t* p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
@@ -365,28 +403,25 @@ bool tuh_cdc_read_clear (uint8_t idx)
// Control Endpoint API
//--------------------------------------------------------------------+
-// internal control complete to update state such as line state, encoding
-static void cdch_internal_control_complete(tuh_xfer_t* xfer)
-{
- uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
+static void process_internal_control_complete(tuh_xfer_t* xfer, uint8_t itf_num) {
uint8_t idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num);
cdch_interface_t* p_cdc = get_itf(idx);
TU_ASSERT(p_cdc, );
+ uint16_t const value = tu_le16toh(xfer->setup->wValue);
- if (xfer->result == XFER_RESULT_SUCCESS)
- {
+ if (xfer->result == XFER_RESULT_SUCCESS) {
switch (p_cdc->serial_drid) {
case SERIAL_DRIVER_ACM:
switch (xfer->setup->bRequest) {
case CDC_REQUEST_SET_CONTROL_LINE_STATE:
- p_cdc->line_state = (uint8_t) tu_le16toh(xfer->setup->wValue);
+ p_cdc->line_state = (uint8_t) value;
break;
case CDC_REQUEST_SET_LINE_CODING: {
uint16_t const len = tu_min16(sizeof(cdc_line_coding_t), tu_le16toh(xfer->setup->wLength));
memcpy(&p_cdc->line_coding, xfer->buffer, len);
- }
break;
+ }
default: break;
}
@@ -396,12 +431,11 @@ static void cdch_internal_control_complete(tuh_xfer_t* xfer)
case SERIAL_DRIVER_FTDI:
switch (xfer->setup->bRequest) {
case FTDI_SIO_MODEM_CTRL:
- p_cdc->line_state = (uint8_t) (tu_le16toh(xfer->setup->wValue) & 0x00ff);
+ p_cdc->line_state = (uint8_t) value;
break;
case FTDI_SIO_SET_BAUD_RATE:
- // convert from divisor to baudrate is not supported
- p_cdc->line_coding.bit_rate = _ftdi_requested_baud;
+ p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate;
break;
default: break;
@@ -413,15 +447,61 @@ static void cdch_internal_control_complete(tuh_xfer_t* xfer)
case SERIAL_DRIVER_CP210X:
switch(xfer->setup->bRequest) {
case CP210X_SET_MHS:
- p_cdc->line_state = (uint8_t) (tu_le16toh(xfer->setup->wValue) & 0x00ff);
+ p_cdc->line_state = (uint8_t) value;
break;
case CP210X_SET_BAUDRATE: {
uint32_t baudrate;
memcpy(&baudrate, xfer->buffer, sizeof(uint32_t));
p_cdc->line_coding.bit_rate = tu_le32toh(baudrate);
+ break;
}
+
+ default: break;
+ }
+ break;
+ #endif
+
+ #if CFG_TUH_CDC_CH34X
+ case SERIAL_DRIVER_CH34X:
+ switch (xfer->setup->bRequest) {
+ case CH34X_REQ_WRITE_REG:
+ // register write request
+ switch (value) {
+ case CH34X_REG16_DIVISOR_PRESCALER:
+ // baudrate
+ p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate;
+ break;
+
+ case CH32X_REG16_LCR2_LCR:
+ // data format
+ p_cdc->line_coding.stop_bits = p_cdc->requested_line_coding.stop_bits;
+ p_cdc->line_coding.parity = p_cdc->requested_line_coding.parity;
+ p_cdc->line_coding.data_bits = p_cdc->requested_line_coding.data_bits;
+ break;
+
+ default: break;
+ }
+ break;
+
+ case CH34X_REQ_MODEM_CTRL: {
+ // set modem controls RTS/DTR request. Note: signals are inverted
+ uint16_t const modem_signal = ~value;
+ if (modem_signal & CH34X_BIT_RTS) {
+ p_cdc->line_state |= CDC_CONTROL_LINE_STATE_RTS;
+ } else {
+ p_cdc->line_state &= (uint8_t) ~CDC_CONTROL_LINE_STATE_RTS;
+ }
+
+ if (modem_signal & CH34X_BIT_DTR) {
+ p_cdc->line_state |= CDC_CONTROL_LINE_STATE_DTR;
+ } else {
+ p_cdc->line_state &= (uint8_t) ~CDC_CONTROL_LINE_STATE_DTR;
+ }
break;
+ }
+
+ default: break;
}
break;
#endif
@@ -436,14 +516,20 @@ static void cdch_internal_control_complete(tuh_xfer_t* xfer)
}
}
+// internal control complete to update state such as line state, encoding
+static void cdch_internal_control_complete(tuh_xfer_t* xfer) {
+ uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
+ process_internal_control_complete(xfer, itf_num);
+}
+
bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
cdch_interface_t* p_cdc = get_itf(idx);
TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT);
cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid];
- if ( complete_cb ) {
+ if (complete_cb) {
return driver->set_control_line_state(p_cdc, line_state, complete_cb, user_data);
- }else {
+ } else {
// blocking
xfer_result_t result = XFER_RESULT_INVALID;
bool ret = driver->set_control_line_state(p_cdc, line_state, complete_cb, (uintptr_t) &result);
@@ -454,7 +540,6 @@ bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_c
}
TU_VERIFY(ret && result == XFER_RESULT_SUCCESS);
-
p_cdc->line_state = (uint8_t) line_state;
return true;
}
@@ -465,9 +550,9 @@ bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete
TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT);
cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid];
- if ( complete_cb ) {
+ if (complete_cb) {
return driver->set_baudrate(p_cdc, baudrate, complete_cb, user_data);
- }else {
+ } else {
// blocking
xfer_result_t result = XFER_RESULT_INVALID;
bool ret = driver->set_baudrate(p_cdc, baudrate, complete_cb, (uintptr_t) &result);
@@ -478,25 +563,23 @@ bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete
}
TU_VERIFY(ret && result == XFER_RESULT_SUCCESS);
-
p_cdc->line_coding.bit_rate = baudrate;
return true;
}
}
-bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
-{
+bool tuh_cdc_set_data_format(uint8_t idx, uint8_t stop_bits, uint8_t parity, uint8_t data_bits,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
cdch_interface_t* p_cdc = get_itf(idx);
- // only ACM support this set line coding request
- TU_VERIFY(p_cdc && p_cdc->serial_drid == SERIAL_DRIVER_ACM);
- TU_VERIFY(p_cdc->acm_capability.support_line_request);
+ TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT);
+ cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid];
- if ( complete_cb ) {
- return acm_set_line_coding(p_cdc, line_coding, complete_cb, user_data);
- }else {
+ if (complete_cb) {
+ return driver->set_data_format(p_cdc, stop_bits, parity, data_bits, complete_cb, user_data);
+ } else {
// blocking
xfer_result_t result = XFER_RESULT_INVALID;
- bool ret = acm_set_line_coding(p_cdc, line_coding, complete_cb, (uintptr_t) &result);
+ bool ret = driver->set_data_format(p_cdc, stop_bits, parity, data_bits, complete_cb, (uintptr_t) &result);
if (user_data) {
// user_data is not NULL, return result via user_data
@@ -504,7 +587,31 @@ bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding,
}
TU_VERIFY(ret && result == XFER_RESULT_SUCCESS);
+ p_cdc->line_coding.stop_bits = stop_bits;
+ p_cdc->line_coding.parity = parity;
+ p_cdc->line_coding.data_bits = data_bits;
+ return true;
+ }
+}
+
+bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT);
+ cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid];
+
+ if ( complete_cb ) {
+ return driver->set_line_coding(p_cdc, line_coding, complete_cb, user_data);
+ } else {
+ // blocking
+ xfer_result_t result = XFER_RESULT_INVALID;
+ bool ret = driver->set_line_coding(p_cdc, line_coding, complete_cb, (uintptr_t) &result);
+
+ if (user_data) {
+ // user_data is not NULL, return result via user_data
+ *((xfer_result_t*) user_data) = result;
+ }
+ TU_VERIFY(ret && result == XFER_RESULT_SUCCESS);
p_cdc->line_coding = *line_coding;
return true;
}
@@ -514,39 +621,34 @@ bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding,
// CLASS-USBH API
//--------------------------------------------------------------------+
-void cdch_init(void)
-{
+void cdch_init(void) {
tu_memclr(cdch_data, sizeof(cdch_data));
- for(size_t i=0; i<CFG_TUH_CDC; i++)
- {
+ for (size_t i = 0; i < CFG_TUH_CDC; i++) {
cdch_interface_t* p_cdc = &cdch_data[i];
tu_edpt_stream_init(&p_cdc->stream.tx, true, true, false,
- p_cdc->stream.tx_ff_buf, CFG_TUH_CDC_TX_BUFSIZE,
- p_cdc->stream.tx_ep_buf, CFG_TUH_CDC_TX_EPSIZE);
+ p_cdc->stream.tx_ff_buf, CFG_TUH_CDC_TX_BUFSIZE,
+ p_cdc->stream.tx_ep_buf, CFG_TUH_CDC_TX_EPSIZE);
tu_edpt_stream_init(&p_cdc->stream.rx, true, false, false,
- p_cdc->stream.rx_ff_buf, CFG_TUH_CDC_RX_BUFSIZE,
- p_cdc->stream.rx_ep_buf, CFG_TUH_CDC_RX_EPSIZE);
+ p_cdc->stream.rx_ff_buf, CFG_TUH_CDC_RX_BUFSIZE,
+ p_cdc->stream.rx_ep_buf, CFG_TUH_CDC_RX_EPSIZE);
}
}
-void cdch_close(uint8_t daddr)
-{
- for(uint8_t idx=0; idx<CFG_TUH_CDC; idx++)
- {
+void cdch_close(uint8_t daddr) {
+ for (uint8_t idx = 0; idx < CFG_TUH_CDC; idx++) {
cdch_interface_t* p_cdc = &cdch_data[idx];
- if (p_cdc->daddr == daddr)
- {
+ if (p_cdc->daddr == daddr) {
TU_LOG_DRV(" CDCh close addr = %u index = %u\r\n", daddr, idx);
// Invoke application callback
if (tuh_cdc_umount_cb) tuh_cdc_umount_cb(idx);
- //tu_memclr(p_cdc, sizeof(cdch_interface_t));
p_cdc->daddr = 0;
p_cdc->bInterfaceNumber = 0;
+ p_cdc->mounted = false;
tu_edpt_stream_close(&p_cdc->stream.tx);
tu_edpt_stream_close(&p_cdc->stream.rx);
}
@@ -570,16 +672,11 @@ bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t
// - xferred_bytes is multiple of EP Packet size and not zero
tu_edpt_stream_write_zlp_if_needed(&p_cdc->stream.tx, xferred_bytes);
}
- }
- else if ( ep_addr == p_cdc->stream.rx.ep_addr ) {
+ } else if ( ep_addr == p_cdc->stream.rx.ep_addr ) {
#if CFG_TUH_CDC_FTDI
if (p_cdc->serial_drid == SERIAL_DRIVER_FTDI) {
// FTDI reserve 2 bytes for status
- // FTDI status
-// uint8_t status[2] = {
-// p_cdc->stream.rx.ep_buf[0],
-// p_cdc->stream.rx.ep_buf[1]
-// };
+ // uint8_t status[2] = {p_cdc->stream.rx.ep_buf[0], p_cdc->stream.rx.ep_buf[1]};
tu_edpt_stream_read_xfer_complete_offset(&p_cdc->stream.rx, xferred_bytes, 2);
}else
#endif
@@ -605,22 +702,15 @@ bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t
// Enumeration
//--------------------------------------------------------------------+
-static bool acm_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
-
-static bool open_ep_stream_pair(cdch_interface_t* p_cdc, tusb_desc_endpoint_t const *desc_ep)
-{
- for(size_t i=0; i<2; i++)
- {
+static bool open_ep_stream_pair(cdch_interface_t* p_cdc, tusb_desc_endpoint_t const* desc_ep) {
+ for (size_t i = 0; i < 2; i++) {
TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType &&
- TUSB_XFER_BULK == desc_ep->bmAttributes.xfer);
-
+ TUSB_XFER_BULK == desc_ep->bmAttributes.xfer);
TU_ASSERT(tuh_edpt_open(p_cdc->daddr, desc_ep));
- if ( tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN )
- {
+ if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
tu_edpt_stream_open(&p_cdc->stream.rx, p_cdc->daddr, desc_ep);
- }else
- {
+ } else {
tu_edpt_stream_open(&p_cdc->stream.tx, p_cdc->daddr, desc_ep);
}
@@ -630,49 +720,36 @@ static bool open_ep_stream_pair(cdch_interface_t* p_cdc, tusb_desc_endpoint_t co
return true;
}
-bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
-{
+bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
(void) rhport;
- // Only support ACM subclass
+ // For CDC: only support ACM subclass
// Note: Protocol 0xFF can be RNDIS device
- if ( TUSB_CLASS_CDC == itf_desc->bInterfaceClass &&
- CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass)
- {
+ if (TUSB_CLASS_CDC == itf_desc->bInterfaceClass &&
+ CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass) {
return acm_open(daddr, itf_desc, max_len);
}
- #if CFG_TUH_CDC_FTDI || CFG_TUH_CDC_CP210X
- else if ( 0xff == itf_desc->bInterfaceClass )
- {
+ else if (SERIAL_DRIVER_COUNT > 1 &&
+ TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass) {
uint16_t vid, pid;
TU_VERIFY(tuh_vid_pid_get(daddr, &vid, &pid));
- #if CFG_TUH_CDC_FTDI
- if (TU_FTDI_VID == vid) {
- for (size_t i = 0; i < FTDI_PID_COUNT; i++) {
- if (ftdi_pids[i] == pid) {
- return ftdi_open(daddr, itf_desc, max_len);
+ for (size_t dr = 1; dr < SERIAL_DRIVER_COUNT; dr++) {
+ cdch_serial_driver_t const* driver = &serial_drivers[dr];
+ for (size_t i = 0; i < driver->vid_pid_count; i++) {
+ if (driver->vid_pid_list[i][0] == vid && driver->vid_pid_list[i][1] == pid) {
+ return driver->open(daddr, itf_desc, max_len);
}
}
}
- #endif
-
- #if CFG_TUH_CDC_CP210X
- if (TU_CP210X_VID == vid) {
- for (size_t i = 0; i < CP210X_PID_COUNT; i++) {
- if (cp210x_pids[i] == pid) {
- return cp210x_open(daddr, itf_desc, max_len);
- }
- }
- }
- #endif
}
- #endif
return false;
}
static void set_config_complete(cdch_interface_t * p_cdc, uint8_t idx, uint8_t itf_num) {
+ TU_LOG_DRV("CDCh Set Configure complete\r\n");
+ p_cdc->mounted = true;
if (tuh_cdc_mount_cb) tuh_cdc_mount_cb(idx);
// Prepare for incoming data
@@ -682,9 +759,7 @@ static void set_config_complete(cdch_interface_t * p_cdc, uint8_t idx, uint8_t i
usbh_driver_set_config_complete(p_cdc->daddr, itf_num);
}
-
-bool cdch_set_config(uint8_t daddr, uint8_t itf_num)
-{
+bool cdch_set_config(uint8_t daddr, uint8_t itf_num) {
tusb_control_request_t request;
request.wIndex = tu_htole16((uint16_t) itf_num);
@@ -713,94 +788,84 @@ enum {
CONFIG_ACM_COMPLETE,
};
-static bool acm_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
-{
- uint8_t const * p_desc_end = ((uint8_t const*) itf_desc) + max_len;
+static bool acm_open(uint8_t daddr, tusb_desc_interface_t const* itf_desc, uint16_t max_len) {
+ uint8_t const* p_desc_end = ((uint8_t const*) itf_desc) + max_len;
- cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc);
+ cdch_interface_t* p_cdc = make_new_itf(daddr, itf_desc);
TU_VERIFY(p_cdc);
-
p_cdc->serial_drid = SERIAL_DRIVER_ACM;
//------------- Control Interface -------------//
- uint8_t const * p_desc = tu_desc_next(itf_desc);
+ uint8_t const* p_desc = tu_desc_next(itf_desc);
// Communication Functional Descriptors
- while( (p_desc < p_desc_end) && (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc)) )
- {
- if ( CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT == cdc_functional_desc_typeof(p_desc) )
- {
+ while ((p_desc < p_desc_end) && (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc))) {
+ if (CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT == cdc_functional_desc_typeof(p_desc)) {
// save ACM bmCapabilities
- p_cdc->acm_capability = ((cdc_desc_func_acm_t const *) p_desc)->bmCapabilities;
+ p_cdc->acm_capability = ((cdc_desc_func_acm_t const*) p_desc)->bmCapabilities;
}
p_desc = tu_desc_next(p_desc);
}
// Open notification endpoint of control interface if any
- if (itf_desc->bNumEndpoints == 1)
- {
+ if (itf_desc->bNumEndpoints == 1) {
TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc));
- 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( tuh_edpt_open(daddr, desc_ep) );
+ TU_ASSERT(tuh_edpt_open(daddr, desc_ep));
p_cdc->ep_notif = desc_ep->bEndpointAddress;
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
p_desc = tu_desc_next(p_desc);
// data endpoints expected to be in pairs
- TU_ASSERT(open_ep_stream_pair(p_cdc, (tusb_desc_endpoint_t const *) p_desc));
+ TU_ASSERT(open_ep_stream_pair(p_cdc, (tusb_desc_endpoint_t const*) p_desc));
}
return true;
}
-static void acm_process_config(tuh_xfer_t* xfer)
-{
+static void acm_process_config(tuh_xfer_t* xfer) {
uintptr_t const state = xfer->user_data;
uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num);
- cdch_interface_t * p_cdc = get_itf(idx);
- TU_ASSERT(p_cdc, );
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_ASSERT(p_cdc,);
- switch(state)
- {
+ switch (state) {
case CONFIG_ACM_SET_CONTROL_LINE_STATE:
#if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM
- if (p_cdc->acm_capability.support_line_request)
- {
- TU_ASSERT(acm_set_control_line_state(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, acm_process_config,
- CONFIG_ACM_SET_LINE_CODING), );
+ if (p_cdc->acm_capability.support_line_request) {
+ TU_ASSERT(acm_set_control_line_state(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, acm_process_config, CONFIG_ACM_SET_LINE_CODING),);
break;
}
- #endif
+ #endif
TU_ATTR_FALLTHROUGH;
case CONFIG_ACM_SET_LINE_CODING:
- #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
- if (p_cdc->acm_capability.support_line_request)
- {
+ #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
+ if (p_cdc->acm_capability.support_line_request) {
cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM;
- TU_ASSERT(acm_set_line_coding(p_cdc, &line_coding, acm_process_config, CONFIG_ACM_COMPLETE), );
+ TU_ASSERT(acm_set_line_coding(p_cdc, &line_coding, acm_process_config, CONFIG_ACM_COMPLETE),);
break;
}
- #endif
+ #endif
TU_ATTR_FALLTHROUGH;
case CONFIG_ACM_COMPLETE:
// itf_num+1 to account for data interface as well
- set_config_complete(p_cdc, idx, itf_num+1);
+ set_config_complete(p_cdc, idx, itf_num + 1);
break;
- default: break;
+ default:
+ break;
}
}
@@ -868,6 +933,19 @@ static bool acm_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const
return true;
}
+static bool acm_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ TU_LOG_DRV("CDC ACM Set Data Format\r\n");
+
+ cdc_line_coding_t line_coding;
+ line_coding.bit_rate = p_cdc->line_coding.bit_rate;
+ line_coding.stop_bits = stop_bits;
+ line_coding.parity = parity;
+ line_coding.data_bits = data_bits;
+
+ return acm_set_line_coding(p_cdc, &line_coding, complete_cb, user_data);
+}
+
static bool acm_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
TU_VERIFY(p_cdc->acm_capability.support_line_request);
cdc_line_coding_t line_coding = p_cdc->line_coding;
@@ -897,7 +975,6 @@ static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint
TU_VERIFY(p_cdc);
TU_LOG_DRV("FTDI opened\r\n");
-
p_cdc->serial_drid = SERIAL_DRIVER_FTDI;
// endpoint pair
@@ -933,13 +1010,32 @@ static bool ftdi_sio_set_request(cdch_interface_t* p_cdc, uint8_t command, uint1
return tuh_control_xfer(&xfer);
}
-static bool ftdi_sio_reset(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
-{
+static bool ftdi_sio_reset(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
return ftdi_sio_set_request(p_cdc, FTDI_SIO_RESET, FTDI_SIO_RESET_SIO, complete_cb, user_data);
}
-static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
-{
+static bool ftdi_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ (void) p_cdc;
+ (void) stop_bits;
+ (void) parity;
+ (void) data_bits;
+ (void) complete_cb;
+ (void) user_data;
+ // TODO not implemented yet
+ return false;
+}
+
+static bool ftdi_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ (void) p_cdc;
+ (void) line_coding;
+ (void) complete_cb;
+ (void) user_data;
+ // TODO not implemented yet
+ return false;
+}
+
+static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
TU_LOG_DRV("CDC FTDI Set Control Line State\r\n");
p_cdc->user_control_cb = complete_cb;
TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_MODEM_CTRL, 0x0300 | line_state,
@@ -947,8 +1043,7 @@ static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state
return true;
}
-static uint32_t ftdi_232bm_baud_base_to_divisor(uint32_t baud, uint32_t base)
-{
+static uint32_t ftdi_232bm_baud_base_to_divisor(uint32_t baud, uint32_t base) {
const uint8_t divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 };
uint32_t divisor;
@@ -968,18 +1063,16 @@ static uint32_t ftdi_232bm_baud_base_to_divisor(uint32_t baud, uint32_t base)
return divisor;
}
-static uint32_t ftdi_232bm_baud_to_divisor(uint32_t baud)
-{
+static uint32_t ftdi_232bm_baud_to_divisor(uint32_t baud) {
return ftdi_232bm_baud_base_to_divisor(baud, 48000000u);
}
-static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
-{
+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);
p_cdc->user_control_cb = complete_cb;
- _ftdi_requested_baud = baudrate;
+ p_cdc->requested_line_coding.bit_rate = baudrate;
TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_SET_BAUD_RATE, divisor,
complete_cb ? cdch_internal_control_complete : NULL, user_data));
@@ -1001,8 +1094,7 @@ static void ftdi_process_config(tuh_xfer_t* xfer) {
case CONFIG_FTDI_MODEM_CTRL:
#if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM
- TU_ASSERT(
- ftdi_sio_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, ftdi_process_config, CONFIG_FTDI_SET_BAUDRATE),);
+ TU_ASSERT(ftdi_sio_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, ftdi_process_config, CONFIG_FTDI_SET_BAUDRATE),);
break;
#else
TU_ATTR_FALLTHROUGH;
@@ -1110,6 +1202,15 @@ static bool cp210x_ifc_enable(cdch_interface_t* p_cdc, uint16_t enabled, tuh_xfe
return cp210x_set_request(p_cdc, CP210X_IFC_ENABLE, enabled, NULL, 0, complete_cb, user_data);
}
+static bool cp210x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ // TODO implement later
+ (void) p_cdc;
+ (void) line_coding;
+ (void) complete_cb;
+ (void) user_data;
+ return false;
+}
+
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);
uint32_t baud_le = tu_htole32(baudrate);
@@ -1118,8 +1219,19 @@ static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_
complete_cb ? cdch_internal_control_complete : NULL, user_data);
}
-static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
-{
+static bool cp210x_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ (void) p_cdc;
+ (void) stop_bits;
+ (void) parity;
+ (void) data_bits;
+ (void) complete_cb;
+ (void) user_data;
+ // TODO not implemented yet
+ return false;
+}
+
+static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
TU_LOG_DRV("CDC CP210x Set Control Line State\r\n");
p_cdc->user_control_cb = complete_cb;
return cp210x_set_request(p_cdc, CP210X_SET_MHS, 0x0300 | line_state, NULL, 0,
@@ -1159,8 +1271,7 @@ static void cp210x_process_config(tuh_xfer_t* xfer) {
case CONFIG_CP210X_SET_DTR_RTS:
#if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM
- TU_ASSERT(
- cp210x_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, cp210x_process_config, CONFIG_CP210X_COMPLETE),);
+ TU_ASSERT(cp210x_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, cp210x_process_config, CONFIG_CP210X_COMPLETE),);
break;
#else
TU_ATTR_FALLTHROUGH;
@@ -1176,4 +1287,374 @@ static void cp210x_process_config(tuh_xfer_t* xfer) {
#endif
+//--------------------------------------------------------------------+
+// CH34x (CH340 & CH341)
+//--------------------------------------------------------------------+
+
+#if CFG_TUH_CDC_CH34X
+
+static uint8_t ch34x_get_lcr(uint8_t stop_bits, uint8_t parity, uint8_t data_bits);
+static uint16_t ch34x_get_divisor_prescaler(uint32_t baval);
+
+//------------- control request -------------//
+
+static bool ch34x_set_request(cdch_interface_t* p_cdc, uint8_t direction, uint8_t request, uint16_t value,
+ uint16_t index, uint8_t* buffer, uint16_t length, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ tusb_control_request_t const request_setup = {
+ .bmRequestType_bit = {
+ .recipient = TUSB_REQ_RCPT_DEVICE,
+ .type = TUSB_REQ_TYPE_VENDOR,
+ .direction = direction & 0x01u
+ },
+ .bRequest = request,
+ .wValue = tu_htole16 (value),
+ .wIndex = tu_htole16 (index),
+ .wLength = tu_htole16 (length)
+ };
+
+ // use usbh enum buf since application variable does not live long enough
+ uint8_t* enum_buf = NULL;
+
+ if (buffer && length > 0) {
+ enum_buf = usbh_get_enum_buf();
+ if (direction == TUSB_DIR_OUT) {
+ tu_memcpy_s(enum_buf, CFG_TUH_ENUMERATION_BUFSIZE, buffer, length);
+ }
+ }
+
+ tuh_xfer_t xfer = {
+ .daddr = p_cdc->daddr,
+ .ep_addr = 0,
+ .setup = &request_setup,
+ .buffer = enum_buf,
+ .complete_cb = complete_cb,
+ .user_data = user_data
+ };
+
+ return tuh_control_xfer(&xfer);
+}
+
+static inline bool ch34x_control_out(cdch_interface_t* p_cdc, uint8_t request, uint16_t value, uint16_t index,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ return ch34x_set_request(p_cdc, TUSB_DIR_OUT, request, value, index, NULL, 0, complete_cb, user_data);
+}
+
+static inline bool ch34x_control_in(cdch_interface_t* p_cdc, uint8_t request, uint16_t value, uint16_t index,
+ uint8_t* buffer, uint16_t buffersize, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ return ch34x_set_request(p_cdc, TUSB_DIR_IN, request, value, index, buffer, buffersize,
+ complete_cb, user_data);
+}
+
+static inline bool ch34x_write_reg(cdch_interface_t* p_cdc, uint16_t reg, uint16_t reg_value, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ return ch34x_control_out(p_cdc, CH34X_REQ_WRITE_REG, reg, reg_value, complete_cb, user_data);
+}
+
+//static bool ch34x_read_reg_request ( cdch_interface_t* p_cdc, uint16_t reg,
+// uint8_t *buffer, uint16_t buffersize, tuh_xfer_cb_t complete_cb, uintptr_t user_data )
+//{
+// return ch34x_control_in ( p_cdc, CH34X_REQ_READ_REG, reg, 0, buffer, buffersize, complete_cb, user_data );
+//}
+
+static bool ch34x_write_reg_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ uint16_t const div_ps = ch34x_get_divisor_prescaler(baudrate);
+ TU_VERIFY(div_ps);
+ TU_ASSERT(ch34x_write_reg(p_cdc, CH34X_REG16_DIVISOR_PRESCALER, div_ps,
+ complete_cb, user_data));
+ return true;
+}
+
+//------------- Driver API -------------//
+
+// internal control complete to update state such as line state, encoding
+static void ch34x_control_complete(tuh_xfer_t* xfer) {
+ // CH34x only has 1 interface and use wIndex as payload and not for bInterfaceNumber
+ process_internal_control_complete(xfer, 0);
+}
+
+static bool ch34x_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ p_cdc->requested_line_coding.stop_bits = stop_bits;
+ p_cdc->requested_line_coding.parity = parity;
+ p_cdc->requested_line_coding.data_bits = data_bits;
+
+ uint8_t const lcr = ch34x_get_lcr(stop_bits, parity, data_bits);
+ TU_VERIFY(lcr);
+ TU_ASSERT (ch34x_control_out(p_cdc, CH34X_REQ_WRITE_REG, CH32X_REG16_LCR2_LCR, lcr,
+ complete_cb ? ch34x_control_complete : NULL, user_data));
+ return true;
+}
+
+static bool ch34x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ p_cdc->requested_line_coding.bit_rate = baudrate;
+ p_cdc->user_control_cb = complete_cb;
+ TU_ASSERT(ch34x_write_reg_baudrate(p_cdc, baudrate,
+ complete_cb ? ch34x_control_complete : NULL, user_data));
+ return true;
+}
+
+static void ch34x_set_line_coding_stage1_complete(tuh_xfer_t* xfer) {
+ // CH34x only has 1 interface and use wIndex as payload and not for bInterfaceNumber
+ uint8_t const itf_num = 0;
+ uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num);
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_ASSERT(p_cdc, );
+
+ if (xfer->result == XFER_RESULT_SUCCESS) {
+ // stage 1 success, continue to stage 2
+ p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate;
+ TU_ASSERT(ch34x_set_data_format(p_cdc, p_cdc->requested_line_coding.stop_bits, p_cdc->requested_line_coding.parity,
+ p_cdc->requested_line_coding.data_bits, ch34x_control_complete, xfer->user_data), );
+ } else {
+ // stage 1 failed, notify user
+ xfer->complete_cb = p_cdc->user_control_cb;
+ if (xfer->complete_cb) {
+ xfer->complete_cb(xfer);
+ }
+ }
+}
+
+// 2 stages: set baudrate (stage1) + set data format (stage2)
+static bool ch34x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ p_cdc->requested_line_coding = *line_coding;
+ p_cdc->user_control_cb = complete_cb;
+
+ if (complete_cb) {
+ // stage 1 set baudrate
+ TU_ASSERT(ch34x_write_reg_baudrate(p_cdc, line_coding->bit_rate,
+ ch34x_set_line_coding_stage1_complete, user_data));
+ } else {
+ // sync call
+ xfer_result_t result;
+
+ // stage 1 set baudrate
+ TU_ASSERT(ch34x_write_reg_baudrate(p_cdc, line_coding->bit_rate, NULL, (uintptr_t) &result));
+ TU_VERIFY(result == XFER_RESULT_SUCCESS);
+ p_cdc->line_coding.bit_rate = line_coding->bit_rate;
+
+ // stage 2 set data format
+ TU_ASSERT(ch34x_set_data_format(p_cdc, line_coding->stop_bits, line_coding->parity, line_coding->data_bits,
+ NULL, (uintptr_t) &result));
+ TU_VERIFY(result == XFER_RESULT_SUCCESS);
+ p_cdc->line_coding.stop_bits = line_coding->stop_bits;
+ p_cdc->line_coding.parity = line_coding->parity;
+ p_cdc->line_coding.data_bits = line_coding->data_bits;
+
+ // update transfer result, user_data is expected to point to xfer_result_t
+ if (user_data) {
+ *((xfer_result_t*) user_data) = result;
+ }
+ }
+
+ return true;
+}
+
+static bool ch34x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ uint8_t control = 0;
+ if (line_state & CDC_CONTROL_LINE_STATE_RTS) {
+ control |= CH34X_BIT_RTS;
+ }
+ if (line_state & CDC_CONTROL_LINE_STATE_DTR) {
+ control |= CH34X_BIT_DTR;
+ }
+
+ // CH34x signals are inverted
+ control = ~control;
+
+ p_cdc->user_control_cb = complete_cb;
+ TU_ASSERT (ch34x_control_out(p_cdc, CH34X_REQ_MODEM_CTRL, control, 0,
+ complete_cb ? ch34x_control_complete : NULL, user_data));
+ return true;
+}
+
+//------------- Enumeration -------------//
+enum {
+ CONFIG_CH34X_READ_VERSION = 0,
+ CONFIG_CH34X_SERIAL_INIT,
+ CONFIG_CH34X_SPECIAL_REG_WRITE,
+ CONFIG_CH34X_FLOW_CONTROL,
+ CONFIG_CH34X_MODEM_CONTROL,
+ CONFIG_CH34X_COMPLETE
+};
+
+static bool ch34x_open(uint8_t daddr, tusb_desc_interface_t const* itf_desc, uint16_t max_len) {
+ // CH34x Interface includes 1 vendor interface + 2 bulk + 1 interrupt endpoints
+ TU_VERIFY (itf_desc->bNumEndpoints == 3);
+ TU_VERIFY (sizeof(tusb_desc_interface_t) + 3 * sizeof(tusb_desc_endpoint_t) <= max_len);
+
+ cdch_interface_t* p_cdc = make_new_itf(daddr, itf_desc);
+ TU_VERIFY (p_cdc);
+
+ TU_LOG_DRV ("CH34x opened\r\n");
+ p_cdc->serial_drid = SERIAL_DRIVER_CH34X;
+
+ tusb_desc_endpoint_t const* desc_ep = (tusb_desc_endpoint_t const*) tu_desc_next(itf_desc);
+
+ // data endpoints expected to be in pairs
+ TU_ASSERT(open_ep_stream_pair(p_cdc, desc_ep));
+ desc_ep += 2;
+
+ // Interrupt endpoint: not used for now
+ TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(desc_ep) &&
+ TUSB_XFER_INTERRUPT == desc_ep->bmAttributes.xfer);
+ TU_ASSERT(tuh_edpt_open(daddr, desc_ep));
+ p_cdc->ep_notif = desc_ep->bEndpointAddress;
+
+ return true;
+}
+
+static void ch34x_process_config(tuh_xfer_t* xfer) {
+ // CH34x only has 1 interface and use wIndex as payload and not for bInterfaceNumber
+ uint8_t const itf_num = 0;
+ uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num);
+ cdch_interface_t* p_cdc = get_itf(idx);
+ uintptr_t const state = xfer->user_data;
+ uint8_t buffer[2]; // TODO remove
+ TU_ASSERT (p_cdc,);
+ TU_ASSERT (xfer->result == XFER_RESULT_SUCCESS,);
+
+ switch (state) {
+ case CONFIG_CH34X_READ_VERSION:
+ TU_LOG_DRV("[%u] CDCh CH34x attempt to read Chip Version\r\n", p_cdc->daddr);
+ TU_ASSERT (ch34x_control_in(p_cdc, CH34X_REQ_READ_VERSION, 0, 0, buffer, 2, ch34x_process_config, CONFIG_CH34X_SERIAL_INIT),);
+ break;
+
+ case CONFIG_CH34X_SERIAL_INIT: {
+ // handle version read data, set CH34x line coding (incl. baudrate)
+ uint8_t const version = xfer->buffer[0];
+ TU_LOG_DRV("[%u] CDCh CH34x Chip Version = %02x\r\n", p_cdc->daddr, version);
+ // only versions >= 0x30 are tested, below 0x30 seems having other programming, see drivers from WCH vendor, Linux kernel and FreeBSD
+ TU_ASSERT (version >= 0x30,);
+ // init CH34x with line coding
+ cdc_line_coding_t const line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X;
+ uint16_t const div_ps = ch34x_get_divisor_prescaler(line_coding.bit_rate);
+ TU_ASSERT(div_ps, );
+ uint8_t const lcr = ch34x_get_lcr(line_coding.stop_bits, line_coding.parity, line_coding.data_bits);
+ TU_ASSERT(lcr, );
+ TU_ASSERT (ch34x_control_out(p_cdc, CH34X_REQ_SERIAL_INIT, tu_u16(lcr, 0x9c), div_ps,
+ ch34x_process_config, CONFIG_CH34X_SPECIAL_REG_WRITE),);
+ break;
+ }
+
+ case CONFIG_CH34X_SPECIAL_REG_WRITE:
+ // overtake line coding and do special reg write, purpose unknown, overtaken from WCH driver
+ p_cdc->line_coding = ((cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X);
+ TU_ASSERT (ch34x_write_reg(p_cdc, TU_U16(CH341_REG_0x0F, CH341_REG_0x2C), 0x0007, ch34x_process_config, CONFIG_CH34X_FLOW_CONTROL),);
+ break;
+
+ case CONFIG_CH34X_FLOW_CONTROL:
+ // no hardware flow control
+ TU_ASSERT (ch34x_write_reg(p_cdc, TU_U16(CH341_REG_0x27, CH341_REG_0x27), 0x0000, ch34x_process_config, CONFIG_CH34X_MODEM_CONTROL),);
+ break;
+
+ case CONFIG_CH34X_MODEM_CONTROL:
+ // !always! set modem controls RTS/DTR (CH34x has no reset state after CH34X_REQ_SERIAL_INIT)
+ TU_ASSERT (ch34x_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, ch34x_process_config, CONFIG_CH34X_COMPLETE),);
+ break;
+
+ case CONFIG_CH34X_COMPLETE:
+ set_config_complete(p_cdc, idx, itf_num);
+ break;
+
+ default:
+ TU_ASSERT (false,);
+ break;
+ }
+}
+
+//------------- CH34x helper -------------//
+
+// calculate divisor and prescaler for baudrate, return it as 16-bit combined value
+static uint16_t ch34x_get_divisor_prescaler(uint32_t baval) {
+ uint8_t a;
+ uint8_t b;
+ uint32_t c;
+
+ TU_VERIFY(baval != 0 && baval <= 2000000, 0);
+ switch (baval) {
+ case 921600:
+ a = 0xf3;
+ b = 7;
+ break;
+
+ case 307200:
+ a = 0xd9;
+ b = 7;
+ break;
+
+ default:
+ if (baval > 6000000 / 255) {
+ b = 3;
+ c = 6000000;
+ } else if (baval > 750000 / 255) {
+ b = 2;
+ c = 750000;
+ } else if (baval > 93750 / 255) {
+ b = 1;
+ c = 93750;
+ } else {
+ b = 0;
+ c = 11719;
+ }
+ a = (uint8_t) (c / baval);
+ if (a == 0 || a == 0xFF) {
+ return 0;
+ }
+ if ((c / a - baval) > (baval - c / (a + 1))) {
+ a++;
+ }
+ a = (uint8_t) (256 - a);
+ break;
+ }
+
+ // reg divisor = a, reg prescaler = b
+ // According to linux code we need to set bit 7 of UCHCOM_REG_BPS_PRE,
+ // otherwise the chip will buffer data.
+ return (uint16_t) ((uint16_t)a << 8 | 0x80 | b);
+}
+
+// calculate lcr value from data coding
+static uint8_t ch34x_get_lcr(uint8_t stop_bits, uint8_t parity, uint8_t data_bits) {
+ uint8_t lcr = CH34X_LCR_ENABLE_RX | CH34X_LCR_ENABLE_TX;
+ TU_VERIFY(data_bits >= 5 && data_bits <= 8, 0);
+ lcr |= (uint8_t) (data_bits - 5);
+
+ switch(parity) {
+ case CDC_LINE_CODING_PARITY_NONE:
+ break;
+
+ case CDC_LINE_CODING_PARITY_ODD:
+ lcr |= CH34X_LCR_ENABLE_PAR;
+ break;
+
+ case CDC_LINE_CODING_PARITY_EVEN:
+ lcr |= CH34X_LCR_ENABLE_PAR | CH34X_LCR_PAR_EVEN;
+ break;
+
+ case CDC_LINE_CODING_PARITY_MARK:
+ lcr |= CH34X_LCR_ENABLE_PAR | CH34X_LCR_MARK_SPACE;
+ break;
+
+ case CDC_LINE_CODING_PARITY_SPACE:
+ lcr |= CH34X_LCR_ENABLE_PAR | CH34X_LCR_MARK_SPACE | CH34X_LCR_PAR_EVEN;
+ break;
+
+ default: break;
+ }
+
+ // 1.5 stop bits not supported
+ TU_VERIFY(stop_bits != CDC_LINE_CODING_STOP_BITS_1_5, 0);
+ if (stop_bits == CDC_LINE_CODING_STOP_BITS_2) {
+ lcr |= CH34X_LCR_STOP_BITS_2;
+ }
+
+ return lcr;
+}
+
+
+#endif // CFG_TUH_CDC_CH34X
+
#endif
diff --git a/src/class/cdc/cdc_host.h b/src/class/cdc/cdc_host.h
index 19552f1ee..d512a23a5 100644
--- a/src/class/cdc/cdc_host.h
+++ b/src/class/cdc/cdc_host.h
@@ -44,7 +44,7 @@
// Set Line Coding on enumeration/mounted, value for cdc_line_coding_t
//#ifndef CFG_TUH_CDC_LINE_CODING_ON_ENUM
-//#define CFG_TUH_CDC_LINE_CODING_ON_ENUM { 115200, CDC_LINE_CONDING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 }
+//#define CFG_TUH_CDC_LINE_CODING_ON_ENUM { 115200, CDC_LINE_CODING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 }
//#endif
// RX FIFO size
@@ -148,8 +148,11 @@ bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_c
// Request to set baudrate
bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
-// Request to Set Line Coding (ACM only)
-// Should only use if you don't work with serial devices such as FTDI/CP210x
+// Request to set data format
+bool tuh_cdc_set_data_format(uint8_t idx, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+
+// Request to Set Line Coding = baudrate + data format
+// Note: only implemented by ACM and CH34x, not supported by FTDI and CP210x yet
bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
// Request to Get Line Coding (ACM only)
@@ -159,15 +162,13 @@ bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding,
// Connect by set both DTR, RTS
TU_ATTR_ALWAYS_INLINE static inline
-bool tuh_cdc_connect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
-{
+bool tuh_cdc_connect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
return tuh_cdc_set_control_line_state(idx, CDC_CONTROL_LINE_STATE_DTR | CDC_CONTROL_LINE_STATE_RTS, complete_cb, user_data);
}
// Disconnect by clear both DTR, RTS
TU_ATTR_ALWAYS_INLINE static inline
-bool tuh_cdc_disconnect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
-{
+bool tuh_cdc_disconnect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
return tuh_cdc_set_control_line_state(idx, 0x00, complete_cb, user_data);
}
diff --git a/src/class/cdc/serial/ch34x.h b/src/class/cdc/serial/ch34x.h
new file mode 100644
index 000000000..c18066f57
--- /dev/null
+++ b/src/class/cdc/serial/ch34x.h
@@ -0,0 +1,84 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2023 Heiko Kuester
+ *
+ * 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 _CH34X_H_
+#define _CH34X_H_
+
+// There is no official documentation for the CH34x (CH340, CH341) chips. Reference can be found
+// - https://github.com/WCHSoftGroup/ch341ser_linux
+// - https://github.com/torvalds/linux/blob/master/drivers/usb/serial/ch341.c
+// - https://github.com/freebsd/freebsd-src/blob/main/sys/dev/usb/serial/uchcom.c
+
+// set line_coding @ enumeration
+#ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
+#define CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X CFG_TUH_CDC_LINE_CODING_ON_ENUM
+#else // this default is necessary to work properly
+#define CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X { 9600, CDC_LINE_CONDING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 }
+#endif
+
+// USB requests
+#define CH34X_REQ_READ_VERSION 0x5F // dec 95
+#define CH34X_REQ_WRITE_REG 0x9A // dec 154
+#define CH34X_REQ_READ_REG 0x95 // dec 149
+#define CH34X_REQ_SERIAL_INIT 0xA1 // dec 161
+#define CH34X_REQ_MODEM_CTRL 0xA4 // dev 164
+
+// registers
+#define CH34X_REG_BREAK 0x05
+#define CH34X_REG_PRESCALER 0x12
+#define CH34X_REG_DIVISOR 0x13
+#define CH34X_REG_LCR 0x18
+#define CH34X_REG_LCR2 0x25
+#define CH34X_REG_MCR_MSR 0x06
+#define CH34X_REG_MCR_MSR2 0x07
+#define CH34X_NBREAK_BITS 0x01
+
+#define CH341_REG_0x0F 0x0F // undocumented register
+#define CH341_REG_0x2C 0x2C // undocumented register
+#define CH341_REG_0x27 0x27 // hardware flow control (cts/rts)
+
+#define CH34X_REG16_DIVISOR_PRESCALER TU_U16(CH34X_REG_DIVISOR, CH34X_REG_PRESCALER)
+#define CH32X_REG16_LCR2_LCR TU_U16(CH34X_REG_LCR2, CH34X_REG_LCR)
+
+// modem control bits
+#define CH34X_BIT_RTS ( 1 << 6 )
+#define CH34X_BIT_DTR ( 1 << 5 )
+
+// line control bits
+#define CH34X_LCR_ENABLE_RX 0x80
+#define CH34X_LCR_ENABLE_TX 0x40
+#define CH34X_LCR_MARK_SPACE 0x20
+#define CH34X_LCR_PAR_EVEN 0x10
+#define CH34X_LCR_ENABLE_PAR 0x08
+#define CH34X_LCR_PAR_MASK 0x38 // all parity bits
+#define CH34X_LCR_STOP_BITS_2 0x04
+#define CH34X_LCR_CS8 0x03
+#define CH34X_LCR_CS7 0x02
+#define CH34X_LCR_CS6 0x01
+#define CH34X_LCR_CS5 0x00
+#define CH34X_LCR_CS_MASK 0x03 // all CSx bits
+
+#endif /* _CH34X_H_ */
diff --git a/src/class/cdc/serial/cp210x.h b/src/class/cdc/serial/cp210x.h
index b01417092..2c749f522 100644
--- a/src/class/cdc/serial/cp210x.h
+++ b/src/class/cdc/serial/cp210x.h
@@ -29,8 +29,6 @@
// https://www.silabs.com/documents/public/application-notes/AN571.pdf
#define TU_CP210X_VID 0x10C4
-#define TU_CP210X_PID_LIST \
- 0xEA60, 0xEA70
/* Config request codes */
#define CP210X_IFC_ENABLE 0x00
diff --git a/src/class/cdc/serial/ftdi_sio.h b/src/class/cdc/serial/ftdi_sio.h
index 6916e4031..0825f0719 100644
--- a/src/class/cdc/serial/ftdi_sio.h
+++ b/src/class/cdc/serial/ftdi_sio.h
@@ -25,11 +25,8 @@
#ifndef TUSB_FTDI_SIO_H
#define TUSB_FTDI_SIO_H
-// VID/PID for matching FTDI devices
+// VID for matching FTDI devices
#define TU_FTDI_VID 0x0403
-#define TU_FTDI_PID_LIST \
- 0x6001, 0x6006, 0x6010, 0x6011, 0x6014, 0x6015, 0x8372, 0xFBFA, \
- 0xcd18
// Commands
#define FTDI_SIO_RESET 0 /* Reset the port */
diff --git a/src/class/hid/hid_host.c b/src/class/hid/hid_host.c
index a3551c85a..db52b503f 100644
--- a/src/class/hid/hid_host.c
+++ b/src/class/hid/hid_host.c
@@ -67,6 +67,8 @@ typedef struct
CFG_TUH_MEM_SECTION
tu_static hidh_interface_t _hidh_itf[CFG_TUH_HID];
+tu_static uint8_t _hidh_default_protocol = HID_PROTOCOL_BOOT;
+
//--------------------------------------------------------------------+
// Helper
//--------------------------------------------------------------------+
@@ -211,6 +213,10 @@ static void set_protocol_complete(tuh_xfer_t* xfer)
}
}
+void tuh_hid_set_default_protocol(uint8_t protocol) {
+ _hidh_default_protocol = protocol;
+}
+
static bool _hidh_set_protocol(uint8_t daddr, uint8_t itf_num, uint8_t protocol, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
{
TU_LOG_DRV("HID Set Protocol = %d\r\n", protocol);
@@ -521,7 +527,7 @@ bool hidh_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *desc_
p_hid->report_desc_len = tu_unaligned_read16(&desc_hid->wReportLength);
// Per HID Specs: default is Report protocol, though we will force Boot protocol when set_config
- p_hid->protocol_mode = HID_PROTOCOL_BOOT;
+ p_hid->protocol_mode = _hidh_default_protocol;
if ( HID_SUBCLASS_BOOT == desc_itf->bInterfaceSubClass )
{
p_hid->itf_protocol = desc_itf->bInterfaceProtocol;
@@ -591,7 +597,7 @@ static void process_set_config(tuh_xfer_t* xfer)
break;
case CONFIG_SET_PROTOCOL:
- _hidh_set_protocol(daddr, p_hid->itf_num, HID_PROTOCOL_BOOT, process_set_config, CONFIG_GET_REPORT_DESC);
+ _hidh_set_protocol(daddr, p_hid->itf_num, _hidh_default_protocol, process_set_config, CONFIG_GET_REPORT_DESC);
break;
case CONFIG_GET_REPORT_DESC:
diff --git a/src/class/hid/hid_host.h b/src/class/hid/hid_host.h
index 08ad421d2..238b7c627 100644
--- a/src/class/hid/hid_host.h
+++ b/src/class/hid/hid_host.h
@@ -97,6 +97,10 @@ uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* reports_info_arr,
// Application can use set_protocol() to switch back to Report protocol.
uint8_t tuh_hid_get_protocol(uint8_t dev_addr, uint8_t idx);
+// Device by default is enumerated in Boot protocol for simplicity. Application
+// can use this to modify the default protocol for next enumeration.
+void tuh_hid_set_default_protocol(uint8_t protocol);
+
// Set protocol to HID_PROTOCOL_BOOT (0) or HID_PROTOCOL_REPORT (1)
// 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);
diff --git a/src/class/msc/msc_host.c b/src/class/msc/msc_host.c
index da264142c..39f2d9f1c 100644
--- a/src/class/msc/msc_host.c
+++ b/src/class/msc/msc_host.c
@@ -43,16 +43,14 @@
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
-enum
-{
+enum {
MSC_STAGE_IDLE = 0,
MSC_STAGE_CMD,
MSC_STAGE_DATA,
MSC_STAGE_STATUS,
};
-typedef struct
-{
+typedef struct {
uint8_t itf_num;
uint8_t ep_in;
uint8_t ep_out;
@@ -69,13 +67,13 @@ typedef struct
//------------- SCSI -------------//
uint8_t stage;
- void* buffer;
+ void* buffer;
tuh_msc_complete_cb_t complete_cb;
uintptr_t complete_arg;
CFG_TUH_MEM_ALIGN msc_cbw_t cbw;
CFG_TUH_MEM_ALIGN msc_csw_t csw;
-}msch_interface_t;
+} msch_interface_t;
CFG_TUH_MEM_SECTION static msch_interface_t _msch_itf[CFG_TUH_DEVICE_MAX];
@@ -86,40 +84,34 @@ static uint8_t _msch_buffer[sizeof(scsi_inquiry_resp_t)];
// FIXME potential nul reference
TU_ATTR_ALWAYS_INLINE
-static inline msch_interface_t* get_itf(uint8_t dev_addr)
-{
- return &_msch_itf[dev_addr-1];
+static inline msch_interface_t* get_itf(uint8_t dev_addr) {
+ return &_msch_itf[dev_addr - 1];
}
//--------------------------------------------------------------------+
// PUBLIC API
//--------------------------------------------------------------------+
-uint8_t tuh_msc_get_maxlun(uint8_t dev_addr)
-{
+uint8_t tuh_msc_get_maxlun(uint8_t dev_addr) {
msch_interface_t* p_msc = get_itf(dev_addr);
return p_msc->max_lun;
}
-uint32_t tuh_msc_get_block_count(uint8_t dev_addr, uint8_t lun)
-{
+uint32_t tuh_msc_get_block_count(uint8_t dev_addr, uint8_t lun) {
msch_interface_t* p_msc = get_itf(dev_addr);
return p_msc->capacity[lun].block_count;
}
-uint32_t tuh_msc_get_block_size(uint8_t dev_addr, uint8_t lun)
-{
+uint32_t tuh_msc_get_block_size(uint8_t dev_addr, uint8_t lun) {
msch_interface_t* p_msc = get_itf(dev_addr);
return p_msc->capacity[lun].block_size;
}
-bool tuh_msc_mounted(uint8_t dev_addr)
-{
+bool tuh_msc_mounted(uint8_t dev_addr) {
msch_interface_t* p_msc = get_itf(dev_addr);
return p_msc->mounted;
}
-bool tuh_msc_ready(uint8_t dev_addr)
-{
+bool tuh_msc_ready(uint8_t dev_addr) {
msch_interface_t* p_msc = get_itf(dev_addr);
return p_msc->mounted && !usbh_edpt_busy(dev_addr, p_msc->ep_in) && !usbh_edpt_busy(dev_addr, p_msc->ep_out);
}
@@ -127,20 +119,20 @@ bool tuh_msc_ready(uint8_t dev_addr)
//--------------------------------------------------------------------+
// PUBLIC API: SCSI COMMAND
//--------------------------------------------------------------------+
-static inline void cbw_init(msc_cbw_t *cbw, uint8_t lun)
-{
+static inline void cbw_init(msc_cbw_t* cbw, uint8_t lun) {
tu_memclr(cbw, sizeof(msc_cbw_t));
cbw->signature = MSC_CBW_SIGNATURE;
cbw->tag = 0x54555342; // TUSB
cbw->lun = lun;
}
-bool tuh_msc_scsi_command(uint8_t dev_addr, msc_cbw_t const* cbw, void* data, tuh_msc_complete_cb_t complete_cb, uintptr_t arg)
-{
- msch_interface_t* p_msc = get_itf(dev_addr);
+bool tuh_msc_scsi_command(uint8_t daddr, msc_cbw_t const* cbw, void* data,
+ tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
+ msch_interface_t* p_msc = get_itf(daddr);
TU_VERIFY(p_msc->configured);
- // TODO claim endpoint
+ // claim endpoint
+ TU_VERIFY(usbh_edpt_claim(daddr, p_msc->ep_out));
p_msc->cbw = *cbw;
p_msc->stage = MSC_STAGE_CMD;
@@ -148,15 +140,18 @@ bool tuh_msc_scsi_command(uint8_t dev_addr, msc_cbw_t const* cbw, void* data, tu
p_msc->complete_cb = complete_cb;
p_msc->complete_arg = arg;
- TU_ASSERT(usbh_edpt_xfer(dev_addr, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)));
+ if (!usbh_edpt_xfer(daddr, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t))) {
+ usbh_edpt_release(daddr, p_msc->ep_out);
+ return false;
+ }
return true;
}
-bool tuh_msc_read_capacity(uint8_t dev_addr, uint8_t lun, scsi_read_capacity10_resp_t* response, tuh_msc_complete_cb_t complete_cb, uintptr_t arg)
-{
- msch_interface_t* p_msc = get_itf(dev_addr);
- TU_VERIFY(p_msc->configured);
+bool tuh_msc_read_capacity(uint8_t dev_addr, uint8_t lun, scsi_read_capacity10_resp_t* response,
+ tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ TU_VERIFY(p_msc->configured);
msc_cbw_t cbw;
cbw_init(&cbw, lun);
@@ -169,8 +164,8 @@ bool tuh_msc_read_capacity(uint8_t dev_addr, uint8_t lun, scsi_read_capacity10_r
return tuh_msc_scsi_command(dev_addr, &cbw, response, complete_cb, arg);
}
-bool tuh_msc_inquiry(uint8_t dev_addr, uint8_t lun, scsi_inquiry_resp_t* response, tuh_msc_complete_cb_t complete_cb, uintptr_t arg)
-{
+bool tuh_msc_inquiry(uint8_t dev_addr, uint8_t lun, scsi_inquiry_resp_t* response,
+ tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
msch_interface_t* p_msc = get_itf(dev_addr);
TU_VERIFY(p_msc->mounted);
@@ -181,18 +176,16 @@ bool tuh_msc_inquiry(uint8_t dev_addr, uint8_t lun, scsi_inquiry_resp_t* respons
cbw.dir = TUSB_DIR_IN_MASK;
cbw.cmd_len = sizeof(scsi_inquiry_t);
- scsi_inquiry_t const cmd_inquiry =
- {
- .cmd_code = SCSI_CMD_INQUIRY,
- .alloc_length = sizeof(scsi_inquiry_resp_t)
+ scsi_inquiry_t const cmd_inquiry = {
+ .cmd_code = SCSI_CMD_INQUIRY,
+ .alloc_length = sizeof(scsi_inquiry_resp_t)
};
memcpy(cbw.command, &cmd_inquiry, cbw.cmd_len);
return tuh_msc_scsi_command(dev_addr, &cbw, response, complete_cb, arg);
}
-bool tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, tuh_msc_complete_cb_t complete_cb, uintptr_t arg)
-{
+bool tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
msch_interface_t* p_msc = get_itf(dev_addr);
TU_VERIFY(p_msc->configured);
@@ -200,16 +193,16 @@ bool tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, tuh_msc_complete_cb_
cbw_init(&cbw, lun);
cbw.total_bytes = 0;
- cbw.dir = TUSB_DIR_OUT;
- cbw.cmd_len = sizeof(scsi_test_unit_ready_t);
- cbw.command[0] = SCSI_CMD_TEST_UNIT_READY;
- cbw.command[1] = lun; // according to wiki TODO need verification
+ cbw.dir = TUSB_DIR_OUT;
+ cbw.cmd_len = sizeof(scsi_test_unit_ready_t);
+ cbw.command[0] = SCSI_CMD_TEST_UNIT_READY;
+ cbw.command[1] = lun; // according to wiki TODO need verification
return tuh_msc_scsi_command(dev_addr, &cbw, NULL, complete_cb, arg);
}
-bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void *response, tuh_msc_complete_cb_t complete_cb, uintptr_t arg)
-{
+bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void* response,
+ tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
msc_cbw_t cbw;
cbw_init(&cbw, lun);
@@ -217,73 +210,64 @@ bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void *response, tuh_ms
cbw.dir = TUSB_DIR_IN_MASK;
cbw.cmd_len = sizeof(scsi_request_sense_t);
- scsi_request_sense_t const cmd_request_sense =
- {
- .cmd_code = SCSI_CMD_REQUEST_SENSE,
- .alloc_length = 18
+ scsi_request_sense_t const cmd_request_sense = {
+ .cmd_code = SCSI_CMD_REQUEST_SENSE,
+ .alloc_length = 18
};
-
memcpy(cbw.command, &cmd_request_sense, cbw.cmd_len);
return tuh_msc_scsi_command(dev_addr, &cbw, response, complete_cb, arg);
}
-bool tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void * buffer, uint32_t lba, uint16_t block_count, tuh_msc_complete_cb_t complete_cb, uintptr_t arg)
-{
+bool tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void* buffer, uint32_t lba, uint16_t block_count,
+ tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
msch_interface_t* p_msc = get_itf(dev_addr);
TU_VERIFY(p_msc->mounted);
msc_cbw_t cbw;
cbw_init(&cbw, lun);
- cbw.total_bytes = block_count*p_msc->capacity[lun].block_size;
- cbw.dir = TUSB_DIR_IN_MASK;
- cbw.cmd_len = sizeof(scsi_read10_t);
+ cbw.total_bytes = block_count * p_msc->capacity[lun].block_size;
+ cbw.dir = TUSB_DIR_IN_MASK;
+ cbw.cmd_len = sizeof(scsi_read10_t);
- scsi_read10_t const cmd_read10 =
- {
- .cmd_code = SCSI_CMD_READ_10,
- .lba = tu_htonl(lba),
- .block_count = tu_htons(block_count)
+ scsi_read10_t const cmd_read10 = {
+ .cmd_code = SCSI_CMD_READ_10,
+ .lba = tu_htonl(lba),
+ .block_count = tu_htons(block_count)
};
-
memcpy(cbw.command, &cmd_read10, cbw.cmd_len);
return tuh_msc_scsi_command(dev_addr, &cbw, buffer, complete_cb, arg);
}
-bool tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const * buffer, uint32_t lba, uint16_t block_count, tuh_msc_complete_cb_t complete_cb, uintptr_t arg)
-{
+bool tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const* buffer, uint32_t lba, uint16_t block_count,
+ tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
msch_interface_t* p_msc = get_itf(dev_addr);
TU_VERIFY(p_msc->mounted);
msc_cbw_t cbw;
cbw_init(&cbw, lun);
- cbw.total_bytes = block_count*p_msc->capacity[lun].block_size;
+ cbw.total_bytes = block_count * p_msc->capacity[lun].block_size;
cbw.dir = TUSB_DIR_OUT;
cbw.cmd_len = sizeof(scsi_write10_t);
- scsi_write10_t const cmd_write10 =
- {
- .cmd_code = SCSI_CMD_WRITE_10,
- .lba = tu_htonl(lba),
- .block_count = tu_htons(block_count)
+ scsi_write10_t const cmd_write10 = {
+ .cmd_code = SCSI_CMD_WRITE_10,
+ .lba = tu_htonl(lba),
+ .block_count = tu_htons(block_count)
};
-
memcpy(cbw.command, &cmd_write10, cbw.cmd_len);
- return tuh_msc_scsi_command(dev_addr, &cbw, (void*)(uintptr_t) buffer, complete_cb, arg);
+ return tuh_msc_scsi_command(dev_addr, &cbw, (void*) (uintptr_t) buffer, complete_cb, arg);
}
#if 0
// MSC interface Reset (not used now)
-bool tuh_msc_reset(uint8_t dev_addr)
-{
- tusb_control_request_t const new_request =
- {
- .bmRequestType_bit =
- {
+bool tuh_msc_reset(uint8_t dev_addr) {
+ tusb_control_request_t const new_request = {
+ .bmRequestType_bit = {
.recipient = TUSB_REQ_RCPT_INTERFACE,
.type = TUSB_REQ_TYPE_CLASS,
.direction = TUSB_DIR_OUT
@@ -300,79 +284,71 @@ bool tuh_msc_reset(uint8_t dev_addr)
//--------------------------------------------------------------------+
// CLASS-USBH API
//--------------------------------------------------------------------+
-void msch_init(void)
-{
+void msch_init(void) {
tu_memclr(_msch_itf, sizeof(_msch_itf));
}
-void msch_close(uint8_t dev_addr)
-{
- TU_VERIFY(dev_addr <= CFG_TUH_DEVICE_MAX, );
+void msch_close(uint8_t dev_addr) {
+ TU_VERIFY(dev_addr <= CFG_TUH_DEVICE_MAX,);
msch_interface_t* p_msc = get_itf(dev_addr);
- TU_VERIFY(p_msc->configured, );
+ TU_VERIFY(p_msc->configured,);
TU_LOG_DRV(" MSCh close addr = %d\r\n", dev_addr);
// invoke Application Callback
if (p_msc->mounted) {
- if(tuh_msc_umount_cb) tuh_msc_umount_cb(dev_addr);
+ if (tuh_msc_umount_cb) tuh_msc_umount_cb(dev_addr);
}
tu_memclr(p_msc, sizeof(msch_interface_t));
}
-bool msch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
-{
+bool msch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) {
msch_interface_t* p_msc = get_itf(dev_addr);
msc_cbw_t const * cbw = &p_msc->cbw;
msc_csw_t * csw = &p_msc->csw;
- switch (p_msc->stage)
- {
+ switch (p_msc->stage) {
case MSC_STAGE_CMD:
// Must be Command Block
- TU_ASSERT(ep_addr == p_msc->ep_out && event == XFER_RESULT_SUCCESS && xferred_bytes == sizeof(msc_cbw_t));
+ TU_ASSERT(ep_addr == p_msc->ep_out && event == XFER_RESULT_SUCCESS && xferred_bytes == sizeof(msc_cbw_t));
- if ( cbw->total_bytes && p_msc->buffer )
- {
+ if (cbw->total_bytes && p_msc->buffer) {
// Data stage if any
p_msc->stage = MSC_STAGE_DATA;
-
uint8_t const ep_data = (cbw->dir & TUSB_DIR_IN_MASK) ? p_msc->ep_in : p_msc->ep_out;
TU_ASSERT(usbh_edpt_xfer(dev_addr, ep_data, p_msc->buffer, (uint16_t) cbw->total_bytes));
- }else
- {
+ } else {
// Status stage
p_msc->stage = MSC_STAGE_STATUS;
TU_ASSERT(usbh_edpt_xfer(dev_addr, p_msc->ep_in, (uint8_t*) &p_msc->csw, (uint16_t) sizeof(msc_csw_t)));
}
- break;
+ break;
case MSC_STAGE_DATA:
// Status stage
p_msc->stage = MSC_STAGE_STATUS;
TU_ASSERT(usbh_edpt_xfer(dev_addr, p_msc->ep_in, (uint8_t*) &p_msc->csw, (uint16_t) sizeof(msc_csw_t)));
- break;
+ break;
case MSC_STAGE_STATUS:
// SCSI op is complete
p_msc->stage = MSC_STAGE_IDLE;
- if (p_msc->complete_cb)
- {
- tuh_msc_complete_data_t const cb_data =
- {
- .cbw = cbw,
- .csw = csw,
- .scsi_data = p_msc->buffer,
- .user_arg = p_msc->complete_arg
+ if (p_msc->complete_cb) {
+ tuh_msc_complete_data_t const cb_data = {
+ .cbw = cbw,
+ .csw = csw,
+ .scsi_data = p_msc->buffer,
+ .user_arg = p_msc->complete_arg
};
p_msc->complete_cb(dev_addr, &cb_data);
}
- break;
+ break;
- // unknown state
- default: break;
+ // unknown state
+ default:
+ break;
}
return true;
@@ -381,39 +357,35 @@ bool msch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32
//--------------------------------------------------------------------+
// MSC Enumeration
//--------------------------------------------------------------------+
-
-static void config_get_maxlun_complete (tuh_xfer_t* xfer);
-static bool config_test_unit_ready_complete(uint8_t dev_addr, tuh_msc_complete_data_t const * cb_data);
+static void config_get_maxlun_complete(tuh_xfer_t* xfer);
+static bool config_test_unit_ready_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data);
static bool config_request_sense_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data);
static bool config_read_capacity_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data);
-bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len)
-{
+bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const* desc_itf, uint16_t max_len) {
(void) rhport;
TU_VERIFY (MSC_SUBCLASS_SCSI == desc_itf->bInterfaceSubClass &&
- MSC_PROTOCOL_BOT == desc_itf->bInterfaceProtocol);
+ MSC_PROTOCOL_BOT == desc_itf->bInterfaceProtocol);
// msc driver length is fixed
- uint16_t const drv_len = (uint16_t) (sizeof(tusb_desc_interface_t) + desc_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
+ uint16_t const drv_len = (uint16_t) (sizeof(tusb_desc_interface_t) +
+ desc_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
TU_ASSERT(drv_len <= max_len);
msch_interface_t* p_msc = get_itf(dev_addr);
- tusb_desc_endpoint_t const * ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(desc_itf);
+ tusb_desc_endpoint_t const* ep_desc = (tusb_desc_endpoint_t const*) tu_desc_next(desc_itf);
- for(uint32_t i=0; i<2; i++)
- {
+ for (uint32_t i = 0; i < 2; i++) {
TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType && TUSB_XFER_BULK == ep_desc->bmAttributes.xfer);
TU_ASSERT(tuh_edpt_open(dev_addr, ep_desc));
- if ( tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN )
- {
+ if (TUSB_DIR_IN == tu_edpt_dir(ep_desc->bEndpointAddress)) {
p_msc->ep_in = ep_desc->bEndpointAddress;
- }else
- {
+ } else {
p_msc->ep_out = ep_desc->bEndpointAddress;
}
- ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(ep_desc);
+ ep_desc = (tusb_desc_endpoint_t const*) tu_desc_next(ep_desc);
}
p_msc->itf_num = desc_itf->bInterfaceNumber;
@@ -430,32 +402,31 @@ bool msch_set_config(uint8_t dev_addr, uint8_t itf_num) {
//------------- Get Max Lun -------------//
TU_LOG_DRV("MSC Get Max Lun\r\n");
tusb_control_request_t const request = {
- .bmRequestType_bit = {
- .recipient = TUSB_REQ_RCPT_INTERFACE,
- .type = TUSB_REQ_TYPE_CLASS,
- .direction = TUSB_DIR_IN
- },
- .bRequest = MSC_REQ_GET_MAX_LUN,
- .wValue = 0,
- .wIndex = itf_num,
- .wLength = 1
+ .bmRequestType_bit = {
+ .recipient = TUSB_REQ_RCPT_INTERFACE,
+ .type = TUSB_REQ_TYPE_CLASS,
+ .direction = TUSB_DIR_IN
+ },
+ .bRequest = MSC_REQ_GET_MAX_LUN,
+ .wValue = 0,
+ .wIndex = itf_num,
+ .wLength = 1
};
tuh_xfer_t xfer = {
- .daddr = dev_addr,
- .ep_addr = 0,
- .setup = &request,
- .buffer = _msch_buffer,
- .complete_cb = config_get_maxlun_complete,
- .user_data = 0
+ .daddr = dev_addr,
+ .ep_addr = 0,
+ .setup = &request,
+ .buffer = _msch_buffer,
+ .complete_cb = config_get_maxlun_complete,
+ .user_data = 0
};
TU_ASSERT(tuh_control_xfer(&xfer));
return true;
}
-static void config_get_maxlun_complete (tuh_xfer_t* xfer)
-{
+static void config_get_maxlun_complete(tuh_xfer_t* xfer) {
uint8_t const daddr = xfer->daddr;
msch_interface_t* p_msc = get_itf(daddr);
@@ -471,18 +442,16 @@ static void config_get_maxlun_complete (tuh_xfer_t* xfer)
tuh_msc_test_unit_ready(daddr, lun, config_test_unit_ready_complete, 0);
}
-static bool config_test_unit_ready_complete(uint8_t dev_addr, tuh_msc_complete_data_t const * cb_data)
-{
+static bool config_test_unit_ready_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data) {
msc_cbw_t const* cbw = cb_data->cbw;
msc_csw_t const* csw = cb_data->csw;
- if (csw->status == 0)
- {
+ if (csw->status == 0) {
// Unit is ready, read its capacity
TU_LOG_DRV("SCSI Read Capacity\r\n");
- tuh_msc_read_capacity(dev_addr, cbw->lun, (scsi_read_capacity10_resp_t*) ((void*) _msch_buffer), config_read_capacity_complete, 0);
- }else
- {
+ tuh_msc_read_capacity(dev_addr, cbw->lun, (scsi_read_capacity10_resp_t*) ((void*) _msch_buffer),
+ config_read_capacity_complete, 0);
+ } else {
// Note: During enumeration, some device fails Test Unit Ready and require a few retries
// with Request Sense to start working !!
// TODO limit number of retries
@@ -493,8 +462,7 @@ static bool config_test_unit_ready_complete(uint8_t dev_addr, tuh_msc_complete_d
return true;
}
-static bool config_request_sense_complete(uint8_t dev_addr, tuh_msc_complete_data_t const * cb_data)
-{
+static bool config_request_sense_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data) {
msc_cbw_t const* cbw = cb_data->cbw;
msc_csw_t const* csw = cb_data->csw;
@@ -503,8 +471,7 @@ static bool config_request_sense_complete(uint8_t dev_addr, tuh_msc_complete_dat
return true;
}
-static bool config_read_capacity_complete(uint8_t dev_addr, tuh_msc_complete_data_t const * cb_data)
-{
+static bool config_read_capacity_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data) {
msc_cbw_t const* cbw = cb_data->cbw;
msc_csw_t const* csw = cb_data->csw;
diff --git a/src/class/msc/msc_host.h b/src/class/msc/msc_host.h
index 6c0e5c9dd..9ca1b4703 100644
--- a/src/class/msc/msc_host.h
+++ b/src/class/msc/msc_host.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_MSC_HOST_H_
-#define _TUSB_MSC_HOST_H_
+#ifndef TUSB_MSC_HOST_H_
+#define TUSB_MSC_HOST_H_
#include "msc.h"
@@ -73,7 +73,7 @@ uint32_t tuh_msc_get_block_size(uint8_t dev_addr, uint8_t lun);
// Perform a full SCSI command (cbw, data, csw) in non-blocking manner.
// Complete callback is invoked when SCSI op is complete.
// return true if success, false if there is already pending operation.
-bool tuh_msc_scsi_command(uint8_t dev_addr, msc_cbw_t const* cbw, void* data, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
+bool tuh_msc_scsi_command(uint8_t daddr, msc_cbw_t const* cbw, void* data, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
// Perform SCSI Inquiry command
// Complete callback is invoked when SCSI op is complete.
@@ -123,4 +123,4 @@ bool msch_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, ui
}
#endif
-#endif /* _TUSB_MSC_HOST_H_ */
+#endif
diff --git a/src/class/vendor/vendor_device.c b/src/class/vendor/vendor_device.c
index 389a29696..a68cb2156 100644
--- a/src/class/vendor/vendor_device.c
+++ b/src/class/vendor/vendor_device.c
@@ -49,10 +49,8 @@ typedef struct
uint8_t rx_ff_buf[CFG_TUD_VENDOR_RX_BUFSIZE];
uint8_t tx_ff_buf[CFG_TUD_VENDOR_TX_BUFSIZE];
-#if CFG_FIFO_MUTEX
- osal_mutex_def_t rx_ff_mutex;
- osal_mutex_def_t tx_ff_mutex;
-#endif
+ OSAL_MUTEX_DEF(rx_ff_mutex);
+ OSAL_MUTEX_DEF(tx_ff_mutex);
// Endpoint Transfer buffer
CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_VENDOR_EPSIZE];
@@ -183,10 +181,8 @@ void vendord_init(void)
tu_fifo_config(&p_itf->rx_ff, p_itf->rx_ff_buf, CFG_TUD_VENDOR_RX_BUFSIZE, 1, false);
tu_fifo_config(&p_itf->tx_ff, p_itf->tx_ff_buf, CFG_TUD_VENDOR_TX_BUFSIZE, 1, false);
-#if CFG_FIFO_MUTEX
tu_fifo_config_mutex(&p_itf->rx_ff, NULL, osal_mutex_create(&p_itf->rx_ff_mutex));
tu_fifo_config_mutex(&p_itf->tx_ff, osal_mutex_create(&p_itf->tx_ff_mutex), NULL);
-#endif
}
}
diff --git a/src/class/video/video.h b/src/class/video/video.h
index d9880c291..6319c6536 100644
--- a/src/class/video/video.h
+++ b/src/class/video/video.h
@@ -29,6 +29,10 @@
#include "common/tusb_common.h"
+enum {
+ VIDEO_BCD_1_50 = 0x0150,
+};
+
// Table 3-19 Color Matching Descriptor
typedef enum {
VIDEO_COLOR_PRIMARIES_UNDEFINED = 0x00,
@@ -198,55 +202,98 @@ typedef enum {
} video_terminal_type_t;
//--------------------------------------------------------------------+
-// Descriptors
+// Video Control (VC) Descriptors
//--------------------------------------------------------------------+
/* 2.3.4.2 */
+#define tusb_desc_video_control_header_nitf_t(_nitf) \
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength; \
+ uint8_t bDescriptorType; \
+ uint8_t bDescriptorSubType; \
+ uint16_t bcdUVC; \
+ uint16_t wTotalLength; \
+ uint32_t dwClockFrequency; /* deprecated */ \
+ uint8_t bInCollection; \
+ uint8_t baInterfaceNr[_nitf]; \
+ }
+
+typedef tusb_desc_video_control_header_nitf_t() tusb_desc_video_control_header_t;
+typedef tusb_desc_video_control_header_nitf_t(1) tusb_desc_video_control_header_1itf_t;
+typedef tusb_desc_video_control_header_nitf_t(2) tusb_desc_video_control_header_2itf_t;
+typedef tusb_desc_video_control_header_nitf_t(3) tusb_desc_video_control_header_3itf_t;
+typedef tusb_desc_video_control_header_nitf_t(4) tusb_desc_video_control_header_4itf_t;
+
typedef struct TU_ATTR_PACKED {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubType;
- uint16_t bcdUVC;
- uint16_t wTotalLength;
- uint32_t dwClockFrequency;
- uint8_t bInCollection;
- uint8_t baInterfaceNr[];
-} tusb_desc_cs_video_ctl_itf_hdr_t;
+ uint8_t bTerminalID;
+ uint16_t wTerminalType;
+ uint8_t bAssocTerminal;
+ uint8_t iTerminal;
+} tusb_desc_video_control_input_terminal_t;
+
+TU_VERIFY_STATIC(sizeof(tusb_desc_video_control_input_terminal_t) == 8, "size is not correct");
-/* 2.4.3.3 */
typedef struct TU_ATTR_PACKED {
- uint8_t bHeaderLength;
- union {
- uint8_t bmHeaderInfo;
- struct {
- uint8_t FrameID: 1;
- uint8_t EndOfFrame: 1;
- uint8_t PresentationTime: 1;
- uint8_t SourceClockReference: 1;
- uint8_t PayloadSpecific: 1;
- uint8_t StillImage: 1;
- uint8_t Error: 1;
- uint8_t EndOfHeader: 1;
- };
- };
-} tusb_video_payload_header_t;
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint8_t bTerminalID;
+ uint16_t wTerminalType;
+ uint8_t bAssocTerminal;
+ uint8_t bSourceID;
+ uint8_t iTerminal;
+} tusb_desc_video_control_output_terminal_t;
+
+TU_VERIFY_STATIC(sizeof(tusb_desc_video_control_output_terminal_t) == 9, "size is not correct");
-/* 3.9.2.1 */
typedef struct TU_ATTR_PACKED {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubType;
- uint8_t bNumFormats;
- uint16_t wTotalLength;
- uint8_t bEndpointAddress;
- uint8_t bmInfo;
- uint8_t bTerminalLink;
- uint8_t bStillCaptureMethod;
- uint8_t bTriggerSupport;
- uint8_t bTriggerUsage;
+ uint8_t bTerminalID;
+ uint16_t wTerminalType;
+ uint8_t bAssocTerminal;
+ uint8_t iTerminal;
+
+ uint16_t wObjectiveFocalLengthMin;
+ uint16_t wObjectiveFocalLengthMax;
+ uint16_t wOcularFocalLength;
uint8_t bControlSize;
- uint8_t bmaControls[];
-} tusb_desc_cs_video_stm_itf_in_hdr_t;
+ uint8_t bmControls[3];
+} tusb_desc_video_control_camera_terminal_t;
+
+TU_VERIFY_STATIC(sizeof(tusb_desc_video_control_camera_terminal_t) == 18, "size is not correct");
+
+//--------------------------------------------------------------------+
+// Video Streaming (VS) Descriptors
+//--------------------------------------------------------------------+
+
+/* 3.9.2.1 */
+#define tusb_desc_video_streaming_input_header_nbyte_t(_nb) \
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength; \
+ uint8_t bDescriptorType; \
+ uint8_t bDescriptorSubType; \
+ uint8_t bNumFormats; /* Number of video payload Format descriptors for this interface */ \
+ uint16_t wTotalLength; \
+ uint8_t bEndpointAddress; \
+ uint8_t bmInfo; /* Bit 0: dynamic format change supported */ \
+ uint8_t bTerminalLink; \
+ uint8_t bStillCaptureMethod; \
+ uint8_t bTriggerSupport; /* Hardware trigger supported */ \
+ uint8_t bTriggerUsage; \
+ uint8_t bControlSize; /* sizeof of each control item */ \
+ uint8_t bmaControls[_nb]; \
+ }
+
+typedef tusb_desc_video_streaming_input_header_nbyte_t() tusb_desc_video_streaming_input_header_t;
+typedef tusb_desc_video_streaming_input_header_nbyte_t(1) tusb_desc_video_streaming_input_header_1byte_t;
+typedef tusb_desc_video_streaming_input_header_nbyte_t(2) tusb_desc_video_streaming_input_header_2byte_t;
+typedef tusb_desc_video_streaming_input_header_nbyte_t(3) tusb_desc_video_streaming_input_header_3byte_t;
+typedef tusb_desc_video_streaming_input_header_nbyte_t(4) tusb_desc_video_streaming_input_header_4byte_t;
/* 3.9.2.2 */
typedef struct TU_ATTR_PACKED {
@@ -259,7 +306,7 @@ typedef struct TU_ATTR_PACKED {
uint8_t bTerminalLink;
uint8_t bControlSize;
uint8_t bmaControls[];
-} tusb_desc_cs_video_stm_itf_out_hdr_t;
+} tusb_desc_video_streaming_output_header_t;
typedef struct TU_ATTR_PACKED {
uint8_t bLength;
@@ -285,14 +332,33 @@ typedef struct TU_ATTR_PACKED {
uint8_t bmaControls[];
} output;
};
-} tusb_desc_cs_video_stm_itf_hdr_t;
+} tusb_desc_video_streaming_inout_header_t;
+
+// 3.9.2.6 Color Matching Descriptor
+typedef struct TU_ATTR_PACKED {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint8_t bColorPrimaries;
+ uint8_t bTransferCharacteristics;
+ uint8_t bMatrixCoefficients;
+} tusb_desc_video_streaming_color_matching_t;
+TU_VERIFY_STATIC(sizeof(tusb_desc_video_streaming_color_matching_t) == 6, "size is not correct");
+
+//--------------------------------------------------------------------+
+// Format and Frame Descriptor
+// Note: bFormatIndex & bFrameIndex are 1-based index
+//--------------------------------------------------------------------+
+
+//------------- Uncompressed -------------//
+// Uncompressed payload specs: 3.1.1 format descriptor
typedef struct TU_ATTR_PACKED {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubType;
uint8_t bFormatIndex;
- uint8_t bNumFrameDescriptors;
+ uint8_t bNumFrameDescriptors; // Number of frame descriptors for this format
uint8_t guidFormat[16];
uint8_t bBitsPerPixel;
uint8_t bDefaultFrameIndex;
@@ -300,22 +366,65 @@ typedef struct TU_ATTR_PACKED {
uint8_t bAspectRatioY;
uint8_t bmInterlaceFlags;
uint8_t bCopyProtect;
-} tusb_desc_cs_video_fmt_uncompressed_t;
+} tusb_desc_video_format_uncompressed_t;
+
+// Uncompressed payload specs: 3.1.2 frame descriptor
+#define tusb_desc_video_frame_uncompressed_nint_t(_nint) \
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength; \
+ uint8_t bDescriptorType; \
+ uint8_t bDescriptorSubType; \
+ uint8_t bFrameIndex; \
+ uint8_t bmCapabilities; \
+ uint16_t wWidth; \
+ uint16_t wHeight; \
+ uint32_t dwMinBitRate; \
+ uint32_t dwMaxBitRate; \
+ uint32_t dwMaxVideoFrameBufferSize; /* deprecated in 1.5 */ \
+ uint32_t dwDefaultFrameInterval; \
+ uint8_t bFrameIntervalType; \
+ uint32_t dwFrameInterval[_nint]; \
+ }
+typedef tusb_desc_video_frame_uncompressed_nint_t() tusb_desc_video_frame_uncompressed_t;
+typedef tusb_desc_video_frame_uncompressed_nint_t(1) tusb_desc_video_frame_uncompressed_1int_t;
+typedef tusb_desc_video_frame_uncompressed_nint_t(2) tusb_desc_video_frame_uncompressed_2int_t;
+typedef tusb_desc_video_frame_uncompressed_nint_t(3) tusb_desc_video_frame_uncompressed_3int_t;
+typedef tusb_desc_video_frame_uncompressed_nint_t(4) tusb_desc_video_frame_uncompressed_4int_t;
+
+// continuous = 3 intervals: min, max, step
+typedef tusb_desc_video_frame_uncompressed_3int_t tusb_desc_video_frame_uncompressed_continuous_t;
+
+TU_VERIFY_STATIC(sizeof(tusb_desc_video_frame_uncompressed_continuous_t) == 38, "size is not correct");
+
+//------------- MJPEG -------------//
+// MJPEG payload specs: 3.1.1 format descriptor
typedef struct TU_ATTR_PACKED {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubType;
uint8_t bFormatIndex;
uint8_t bNumFrameDescriptors;
- uint8_t bmFlags;
+ uint8_t bmFlags; // Bit 0: fixed size samples (1 = yes)
uint8_t bDefaultFrameIndex;
uint8_t bAspectRatioX;
uint8_t bAspectRatioY;
uint8_t bmInterlaceFlags;
uint8_t bCopyProtect;
-} tusb_desc_cs_video_fmt_mjpeg_t;
+} tusb_desc_video_format_mjpeg_t;
+// MJPEG payload specs: 3.1.2 frame descriptor (same as uncompressed)
+typedef tusb_desc_video_frame_uncompressed_t tusb_desc_video_frame_mjpeg_t;
+typedef tusb_desc_video_frame_uncompressed_1int_t tusb_desc_video_frame_mjpeg_1int_t;
+typedef tusb_desc_video_frame_uncompressed_2int_t tusb_desc_video_frame_mjpeg_2int_t;
+typedef tusb_desc_video_frame_uncompressed_3int_t tusb_desc_video_frame_mjpeg_3int_t;
+typedef tusb_desc_video_frame_uncompressed_4int_t tusb_desc_video_frame_mjpeg_4int_t;
+
+// continuous = 3 intervals: min, max, step
+typedef tusb_desc_video_frame_mjpeg_3int_t tusb_desc_video_frame_mjpeg_continuous_t;
+
+//------------- DV -------------//
+// DV payload specs: 3.1.1
typedef struct TU_ATTR_PACKED {
uint8_t bLength;
uint8_t bDescriptorType;
@@ -323,8 +432,9 @@ typedef struct TU_ATTR_PACKED {
uint8_t bFormatIndex;
uint32_t dwMaxVideoFrameBufferSize; /* deprecated */
uint8_t bFormatType;
-} tusb_desc_cs_video_fmt_dv_t;
+} tusb_desc_video_format_dv_t;
+// Frame Based payload specs: 3.1.1
typedef struct TU_ATTR_PACKED {
uint8_t bLength;
uint8_t bDescriptorType;
@@ -339,25 +449,7 @@ typedef struct TU_ATTR_PACKED {
uint8_t bmInterlaceFlags;
uint8_t bCopyProtect;
uint8_t bVaribaleSize;
-} tusb_desc_cs_video_fmt_frame_based_t;
-
-typedef struct TU_ATTR_PACKED {
- uint8_t bLength;
- uint8_t bDescriptorType;
- uint8_t bDescriptorSubType;
- uint8_t bFrameIndex;
- uint8_t bmCapabilities;
- uint16_t wWidth;
- uint16_t wHeight;
- uint32_t dwMinBitRate;
- uint32_t dwMaxBitRate;
- uint32_t dwMaxVideoFrameBufferSize; /* deprecated */
- uint32_t dwDefaultFrameInterval;
- uint8_t bFrameIntervalType;
- uint32_t dwFrameInterval[];
-} tusb_desc_cs_video_frm_uncompressed_t;
-
-typedef tusb_desc_cs_video_frm_uncompressed_t tusb_desc_cs_video_frm_mjpeg_t;
+} tusb_desc_video_format_framebased_t;
typedef struct TU_ATTR_PACKED {
uint8_t bLength;
@@ -373,12 +465,30 @@ typedef struct TU_ATTR_PACKED {
uint8_t bFrameIntervalType;
uint32_t dwBytesPerLine;
uint32_t dwFrameInterval[];
-} tusb_desc_cs_video_frm_frame_based_t;
+} tusb_desc_video_frame_framebased_t;
//--------------------------------------------------------------------+
// Requests
//--------------------------------------------------------------------+
+/* 2.4.3.3 */
+typedef struct TU_ATTR_PACKED {
+ uint8_t bHeaderLength;
+ union {
+ uint8_t bmHeaderInfo;
+ struct {
+ uint8_t FrameID: 1;
+ uint8_t EndOfFrame: 1;
+ uint8_t PresentationTime: 1;
+ uint8_t SourceClockReference: 1;
+ uint8_t PayloadSpecific: 1;
+ uint8_t StillImage: 1;
+ uint8_t Error: 1;
+ uint8_t EndOfHeader: 1;
+ };
+ };
+} tusb_video_payload_header_t;
+
/* 4.3.1.1 */
typedef struct TU_ATTR_PACKED {
union {
@@ -537,7 +647,7 @@ TU_VERIFY_STATIC( sizeof(video_probe_and_commit_control_t) == 48, "size is not c
/* Motion-JPEG 3.1.1 Table 3-2 and 3-4 */
#define TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_DISC(_frmidx, _cap, _width, _height, _minbr, _maxbr, _maxfrmbufsz, _frminterval, ...) \
TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_DISC_LEN + (TU_ARGS_NUM(__VA_ARGS__)) * 4, \
- TUSB_DESC_CS_INTERFACE, VIDEO_CS_VS_INTERFACE_FRAME_MJPEG, \
+ TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VS_FRAME_MJPEG, \
_frmidx, _cap, U16_TO_U8S_LE(_width), U16_TO_U8S_LE(_height), U32_TO_U8S_LE(_minbr), U32_TO_U8S_LE(_maxbr), \
U32_TO_U8S_LE(_maxfrmbufsz), U32_TO_U8S_LE(_frminterval), (TU_ARGS_NUM(__VA_ARGS__)), __VA_ARGS__
diff --git a/src/class/video/video_device.c b/src/class/video/video_device.c
index 3b29454a3..1affd615e 100644
--- a/src/class/video/video_device.c
+++ b/src/class/video/video_device.c
@@ -50,17 +50,17 @@
typedef struct {
tusb_desc_interface_t std;
- tusb_desc_cs_video_ctl_itf_hdr_t ctl;
+ tusb_desc_video_control_header_t ctl;
} tusb_desc_vc_itf_t;
typedef struct {
tusb_desc_interface_t std;
- tusb_desc_cs_video_stm_itf_hdr_t stm;
+ tusb_desc_video_streaming_inout_header_t stm;
} tusb_desc_vs_itf_t;
typedef union {
- tusb_desc_cs_video_ctl_itf_hdr_t ctl;
- tusb_desc_cs_video_stm_itf_hdr_t stm;
+ tusb_desc_video_control_header_t ctl;
+ tusb_desc_video_streaming_inout_header_t stm;
} tusb_desc_video_itf_hdr_t;
typedef struct TU_ATTR_PACKED {
@@ -78,9 +78,9 @@ typedef union {
uint8_t bFormatIndex;
uint8_t bNumFrameDescriptors;
};
- tusb_desc_cs_video_fmt_uncompressed_t uncompressed;
- tusb_desc_cs_video_fmt_mjpeg_t mjpeg;
- tusb_desc_cs_video_fmt_frame_based_t frame_based;
+ tusb_desc_video_format_uncompressed_t uncompressed;
+ tusb_desc_video_format_mjpeg_t mjpeg;
+ tusb_desc_video_format_framebased_t frame_based;
} tusb_desc_cs_video_fmt_t;
typedef union {
@@ -93,9 +93,9 @@ typedef union {
uint16_t wWidth;
uint16_t wHeight;
};
- tusb_desc_cs_video_frm_uncompressed_t uncompressed;
- tusb_desc_cs_video_frm_mjpeg_t mjpeg;
- tusb_desc_cs_video_frm_frame_based_t frame_based;
+ tusb_desc_video_frame_uncompressed_t uncompressed;
+ tusb_desc_video_frame_mjpeg_t mjpeg;
+ tusb_desc_video_frame_framebased_t frame_based;
} tusb_desc_cs_video_frm_t;
/* video streaming interface */
@@ -434,8 +434,9 @@ static bool _update_streaming_parameters(videod_streaming_interface_t const *stm
uint_fast32_t interval_ms = interval / 10000;
TU_ASSERT(interval_ms);
uint_fast32_t payload_size = (frame_size + interval_ms - 1) / interval_ms + 2;
- if (CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE < payload_size)
+ if (CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE < payload_size) {
payload_size = CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE;
+ }
param->dwMaxPayloadTransferSize = payload_size;
return true;
}
@@ -577,8 +578,9 @@ static bool _negotiate_streaming_parameters(videod_streaming_interface_t const *
} else {
payload_size = (frame_size + interval_ms - 1) / interval_ms + 2;
}
- if (CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE < payload_size)
+ if (CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE < payload_size) {
payload_size = CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE;
+ }
param->dwMaxPayloadTransferSize = payload_size;
}
return true;