Merge branch 'master' into zhangslice-master

This commit is contained in:
hathach
2021-09-18 13:35:26 +07:00
273 changed files with 17041 additions and 7025 deletions
+28 -1
View File
@@ -489,7 +489,7 @@ typedef enum
AUDIO_DATA_FORMAT_TYPE_I_IEEE_FLOAT = (uint32_t) (1 << 2),
AUDIO_DATA_FORMAT_TYPE_I_ALAW = (uint32_t) (1 << 3),
AUDIO_DATA_FORMAT_TYPE_I_MULAW = (uint32_t) (1 << 4),
AUDIO_DATA_FORMAT_TYPE_I_RAW_DATA = 0x100000000,
AUDIO_DATA_FORMAT_TYPE_I_RAW_DATA = 0x80000000,
} audio_data_format_type_I_t;
/// All remaining definitions are taken from the descriptor descriptions in the UAC2 main specification
@@ -823,6 +823,33 @@ typedef struct TU_ATTR_PACKED
uint16_t wLockDelay ; ///< Indicates the time it takes this endpoint to reliably lock its internal clock recovery circuitry. Units used depend on the value of the bLockDelayUnits field.
} audio_desc_cs_as_iso_data_ep_t;
// 5.2.2 Control Request Layout
typedef struct TU_ATTR_PACKED
{
union
{
struct TU_ATTR_PACKED
{
uint8_t recipient : 5; ///< Recipient type tusb_request_recipient_t.
uint8_t type : 2; ///< Request type tusb_request_type_t.
uint8_t direction : 1; ///< Direction type. tusb_dir_t
} bmRequestType_bit;
uint8_t bmRequestType;
};
uint8_t bRequest; ///< Request type audio_cs_req_t
uint8_t bChannelNumber;
uint8_t bControlSelector;
union
{
uint8_t bInterface;
uint8_t bEndpoint;
};
uint8_t bEntityID;
uint16_t wLength;
} audio_control_request_t;
//// 5.2.3 Control Request Parameter Block Layout
// 5.2.3.1 1-byte Control CUR Parameter Block
+224 -194
View File
@@ -86,7 +86,11 @@
CFG_TUSB_MCU == OPT_MCU_STM32F4 || \
CFG_TUSB_MCU == OPT_MCU_STM32F7 || \
CFG_TUSB_MCU == OPT_MCU_STM32H7 || \
(CFG_TUSB_MCU == OPT_MCU_STM32L4 && defined(STM32L4_SYNOPSYS))
(CFG_TUSB_MCU == OPT_MCU_STM32L4 && defined(STM32L4_SYNOPSYS)) || \
CFG_TUSB_MCU == OPT_MCU_RX63X || \
CFG_TUSB_MCU == OPT_MCU_RX65X || \
CFG_TUSB_MCU == OPT_MCU_RX72N || \
CFG_TUSB_MCU == OPT_MCU_GD32VF103
#define USE_LINEAR_BUFFER 0
#else
#define USE_LINEAR_BUFFER 1
@@ -102,19 +106,19 @@
// 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_TUSB_MEM_ALIGN uint8_t audio_ep_in_sw_buf_1[CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ];
CFG_TUSB_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_TUSB_MEM_ALIGN uint8_t audio_ep_in_sw_buf_2[CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ];
CFG_TUSB_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_TUSB_MEM_ALIGN uint8_t audio_ep_in_sw_buf_3[CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ];
CFG_TUSB_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
@@ -126,32 +130,32 @@ osal_mutex_def_t ep_in_ff_mutex_wr_3;
// - the software encoding is used - in this case the linear buffers serve as a target memory where logical channels are encoded into
#if CFG_TUD_AUDIO_ENABLE_EP_IN && (USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_ENCODING)
#if CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX > 0
CFG_TUSB_MEM_ALIGN uint8_t lin_buf_in_1[CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX];
CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t lin_buf_in_1[CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX];
#endif
#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_IN_SZ_MAX > 0
CFG_TUSB_MEM_ALIGN uint8_t lin_buf_in_2[CFG_TUD_AUDIO_FUNC_2_EP_IN_SZ_MAX];
CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t lin_buf_in_2[CFG_TUD_AUDIO_FUNC_2_EP_IN_SZ_MAX];
#endif
#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_IN_SZ_MAX > 0
CFG_TUSB_MEM_ALIGN uint8_t lin_buf_in_3[CFG_TUD_AUDIO_FUNC_3_EP_IN_SZ_MAX];
CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t lin_buf_in_3[CFG_TUD_AUDIO_FUNC_3_EP_IN_SZ_MAX];
#endif
#endif // CFG_TUD_AUDIO_ENABLE_EP_IN && (USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_DECODING)
// 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_TUSB_MEM_ALIGN uint8_t audio_ep_out_sw_buf_1[CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ];
CFG_TUSB_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_TUSB_MEM_ALIGN uint8_t audio_ep_out_sw_buf_2[CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ];
CFG_TUSB_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_TUSB_MEM_ALIGN uint8_t audio_ep_out_sw_buf_3[CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ];
CFG_TUSB_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
@@ -163,52 +167,52 @@ osal_mutex_def_t ep_out_ff_mutex_rd_3;
// - the software encoding is used - in this case the linear buffers serve as a target memory where logical channels are encoded into
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && (USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_DECODING)
#if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX > 0
CFG_TUSB_MEM_ALIGN uint8_t lin_buf_out_1[CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX];
CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t lin_buf_out_1[CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX];
#endif
#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SZ_MAX > 0
CFG_TUSB_MEM_ALIGN uint8_t lin_buf_out_2[CFG_TUD_AUDIO_FUNC_2_EP_OUT_SZ_MAX];
CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t lin_buf_out_2[CFG_TUD_AUDIO_FUNC_2_EP_OUT_SZ_MAX];
#endif
#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SZ_MAX > 0
CFG_TUSB_MEM_ALIGN uint8_t lin_buf_out_3[CFG_TUD_AUDIO_FUNC_3_EP_OUT_SZ_MAX];
CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t lin_buf_out_3[CFG_TUD_AUDIO_FUNC_3_EP_OUT_SZ_MAX];
#endif
#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT && (USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_DECODING)
// Control buffers
CFG_TUSB_MEM_ALIGN uint8_t ctrl_buf_1[CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ];
CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t ctrl_buf_1[CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ];
#if CFG_TUD_AUDIO > 1
CFG_TUSB_MEM_ALIGN uint8_t ctrl_buf_2[CFG_TUD_AUDIO_FUNC_2_CTRL_BUF_SZ];
CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t ctrl_buf_2[CFG_TUD_AUDIO_FUNC_2_CTRL_BUF_SZ];
#endif
#if CFG_TUD_AUDIO > 2
CFG_TUSB_MEM_ALIGN uint8_t ctrl_buf_3[CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ];
CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t ctrl_buf_3[CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ];
#endif
// Active alternate setting of interfaces
CFG_TUSB_MEM_ALIGN uint8_t alt_setting_1[CFG_TUD_AUDIO_FUNC_1_N_AS_INT];
uint8_t alt_setting_1[CFG_TUD_AUDIO_FUNC_1_N_AS_INT];
#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_N_AS_INT > 0
CFG_TUSB_MEM_ALIGN uint8_t alt_setting_2[CFG_TUD_AUDIO_FUNC_2_N_AS_INT];
uint8_t alt_setting_2[CFG_TUD_AUDIO_FUNC_2_N_AS_INT];
#endif
#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_N_AS_INT > 0
CFG_TUSB_MEM_ALIGN uint8_t alt_setting_3[CFG_TUD_AUDIO_FUNC_3_N_AS_INT];
uint8_t alt_setting_3[CFG_TUD_AUDIO_FUNC_3_N_AS_INT];
#endif
// 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_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_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];
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
#endif
#endif
#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ > 0
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_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];
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
#endif
#endif
#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ > 0
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_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];
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
@@ -218,21 +222,21 @@ osal_mutex_def_t tx_supp_ff_mutex_wr_3[CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO];
#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_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_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];
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
#endif
#endif
#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ > 0
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_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];
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
#endif
#endif
#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ > 0
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_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];
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
@@ -294,7 +298,7 @@ typedef struct
// Audio control interrupt buffer - no FIFO - 6 Bytes according to UAC 2 specification (p. 74)
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
CFG_TUSB_MEM_ALIGN uint8_t ep_int_ctr_buf[CFG_TUD_AUDIO_INT_CTR_EP_IN_SW_BUFFER_SIZE];
CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t ep_int_ctr_buf[CFG_TUD_AUDIO_INT_CTR_EP_IN_SW_BUFFER_SIZE];
#endif
// Decoding parameters - parameters are set when alternate AS interface is set by host
@@ -393,12 +397,12 @@ static uint8_t audiod_get_audio_fct_idx(audiod_function_t * audio);
#if CFG_TUD_AUDIO_ENABLE_ENCODING || 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);
#endif
static inline uint8_t tu_desc_subtype(void const* desc)
{
return ((uint8_t const*) desc)[2];
}
#endif
bool tud_audio_n_mounted(uint8_t func_id)
{
@@ -643,7 +647,6 @@ static bool audiod_decode_type_I_pcm(uint8_t rhport, audiod_function_t* audio, u
// Determine amount of samples
uint8_t const n_ff_used = audio->n_ff_used_rx;
uint16_t const nBytesToCopy = audio->n_channels_per_ff_rx * audio->n_bytes_per_sampe_rx;
uint16_t const nBytesPerFFToRead = n_bytes_received / n_ff_used;
uint8_t cnt_ff;
@@ -662,14 +665,14 @@ static bool audiod_decode_type_I_pcm(uint8_t rhport, audiod_function_t* audio, u
info.len_lin = tu_min16(nBytesPerFFToRead, info.len_lin);
src = &audio->lin_buf_out[cnt_ff*audio->n_channels_per_ff_rx * audio->n_bytes_per_sampe_rx];
dst_end = info.ptr_lin + info.len_lin;
src = audiod_interleaved_copy_bytes_fast_decode(nBytesToCopy, info.ptr_lin, dst_end, src, n_ff_used);
src = audiod_interleaved_copy_bytes_fast_decode(audio->n_bytes_per_sampe_rx, info.ptr_lin, dst_end, src, n_ff_used);
// Handle wrapped part of FIFO
info.len_wrap = tu_min16(nBytesPerFFToRead - info.len_lin, info.len_wrap);
if (info.len_wrap != 0)
{
dst_end = info.ptr_wrap + info.len_wrap;
audiod_interleaved_copy_bytes_fast_decode(nBytesToCopy, info.ptr_wrap, dst_end, src, n_ff_used);
audiod_interleaved_copy_bytes_fast_decode(audio->n_bytes_per_sampe_rx, info.ptr_wrap, dst_end, src, n_ff_used);
}
tu_fifo_advance_write_pointer(&audio->rx_supp_ff[cnt_ff], info.len_lin + info.len_wrap);
}
@@ -885,7 +888,7 @@ range [-1, +1)
* */
// Helper function
static inline uint8_t * audiod_interleaved_copy_bytes_fast_encode(uint16_t const nBytesToCopy, void * src, uint8_t * src_end, uint8_t * dst, uint8_t const n_ff_used)
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)
{
// Optimize for fast half word copies
typedef struct{
@@ -900,15 +903,15 @@ static inline uint8_t * audiod_interleaved_copy_bytes_fast_encode(uint16_t const
switch (nBytesToCopy)
{
case 1:
while((uint8_t *)src < src_end)
while(src < src_end)
{
*dst = *(uint8_t *)src++;
*dst = *src++;
dst += n_ff_used;
}
break;
case 2:
while((uint8_t *)src < src_end)
while(src < src_end)
{
*(unaligned_uint16_t*)dst = *(unaligned_uint16_t*)src;
src += 2;
@@ -917,23 +920,23 @@ static inline uint8_t * audiod_interleaved_copy_bytes_fast_encode(uint16_t const
break;
case 3:
while((uint8_t *)src < src_end)
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++ = *(uint8_t *)src++;
*dst++ = *(uint8_t *)src++;
*dst++ = *(uint8_t *)src++;
*dst++ = *src++;
*dst++ = *src++;
*dst++ = *src++;
dst += 3 * (n_ff_used - 1);
}
break;
case 4:
while((uint8_t *)src < src_end)
while(src < src_end)
{
*(unaligned_uint32_t*)dst = *(unaligned_uint32_t*)src;
src += 4;
@@ -993,8 +996,8 @@ static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t* audi
if (info.len_lin != 0)
{
info.len_lin = tu_min16(nBytesPerFFToSend, info.len_lin); // Limit up to desired length
src_end = info.ptr_lin + info.len_lin;
dst = audiod_interleaved_copy_bytes_fast_encode(nBytesToCopy, info.ptr_lin, src_end, dst, n_ff_used);
src_end = (uint8_t *)info.ptr_lin + info.len_lin;
dst = audiod_interleaved_copy_bytes_fast_encode(audio->n_bytes_per_sampe_tx, info.ptr_lin, src_end, dst, n_ff_used);
// Limit up to desired length
info.len_wrap = tu_min16(nBytesPerFFToSend - info.len_lin, info.len_wrap);
@@ -1002,8 +1005,8 @@ static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t* audi
// Handle wrapped part of FIFO
if (info.len_wrap != 0)
{
src_end = info.ptr_wrap + info.len_wrap;
audiod_interleaved_copy_bytes_fast_encode(nBytesToCopy, info.ptr_wrap, src_end, dst, n_ff_used);
src_end = (uint8_t *)info.ptr_wrap + info.len_wrap;
audiod_interleaved_copy_bytes_fast_encode(audio->n_bytes_per_sampe_tx, info.ptr_wrap, src_end, dst, n_ff_used);
}
tu_fifo_advance_read_pointer(&audio->tx_supp_ff[cnt_ff], info.len_lin + info.len_wrap);
@@ -1481,18 +1484,20 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
audio->ep_in_as_intf_num = 0;
usbd_edpt_close(rhport, audio->ep_in);
// Invoke callback - can be used to stop data sampling
if (tud_audio_set_itf_close_EP_cb) TU_VERIFY(tud_audio_set_itf_close_EP_cb(rhport, p_request));
audio->ep_in = 0; // Necessary?
// Clear support FIFOs if used
#if CFG_TUD_AUDIO_ENABLE_ENCODING
// Clear FIFOs, since data is no longer valid
#if !CFG_TUD_AUDIO_ENABLE_ENCODING
tu_fifo_clear(&audio->ep_in_ff);
#else
for (uint8_t cnt = 0; cnt < audio->n_tx_supp_ff; cnt++)
{
tu_fifo_clear(&audio->tx_supp_ff[cnt]);
}
#endif
// Invoke callback - can be used to stop data sampling
if (tud_audio_set_itf_close_EP_cb) TU_VERIFY(tud_audio_set_itf_close_EP_cb(rhport, p_request));
audio->ep_in = 0; // Necessary?
}
#endif
@@ -1502,16 +1507,22 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
{
audio->ep_out_as_intf_num = 0;
usbd_edpt_close(rhport, audio->ep_out);
audio->ep_out = 0; // Necessary?
// Clear support FIFOs if used
#if CFG_TUD_AUDIO_ENABLE_DECODING
// Clear FIFOs, since data is no longer valid
#if !CFG_TUD_AUDIO_ENABLE_DECODING
tu_fifo_clear(&audio->ep_out_ff);
#else
for (uint8_t cnt = 0; cnt < audio->n_rx_supp_ff; cnt++)
{
tu_fifo_clear(&audio->rx_supp_ff[cnt]);
}
#endif
// Invoke callback - can be used to stop data sampling
if (tud_audio_set_itf_close_EP_cb) TU_VERIFY(tud_audio_set_itf_close_EP_cb(rhport, p_request));
audio->ep_out = 0; // Necessary?
// Close corresponding feedback EP
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
usbd_edpt_close(rhport, audio->ep_fb);
@@ -1605,9 +1616,17 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
TU_ASSERT( audio->n_ff_used_rx <= audio->n_rx_supp_ff );
#endif
#endif
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
// In case of asynchronous EP, call Cb after ep_fb is set
if (!(((tusb_desc_endpoint_t const *) p_desc)->bmAttributes.sync == 0x01 && audio->ep_fb == 0))
{
if (tud_audio_set_itf_cb) TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request));
}
#else
// Invoke callback
if (tud_audio_set_itf_cb) TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request));
#endif
// Prepare for incoming data
#if USE_LINEAR_BUFFER_RX
TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_out, audio->lin_buf_out, audio->ep_out_sz), false);
@@ -1621,8 +1640,11 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
{
audio->ep_fb = ep_addr;
// Invoke callback
if (tud_audio_set_itf_cb) TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request));
// Invoke callback after ep_out is set
if (audio->ep_out != 0)
{
if (tud_audio_set_itf_cb) TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request));
}
}
#endif
#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT
@@ -1658,64 +1680,65 @@ static bool audiod_control_complete(uint8_t rhport, tusb_control_request_t const
switch (p_request->bmRequestType_bit.recipient)
{
case TUSB_REQ_RCPT_INTERFACE: ; // The semicolon is there to enable a declaration right after the label
uint8_t itf = TU_U16_LOW(p_request->wIndex);
uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
if (entityID != 0)
case TUSB_REQ_RCPT_INTERFACE:
{
if (tud_audio_set_req_entity_cb)
{
// Check if entity is present and get corresponding driver index
TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &func_id));
uint8_t itf = TU_U16_LOW(p_request->wIndex);
uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
// Invoke callback
return tud_audio_set_req_entity_cb(rhport, p_request, _audiod_fct[func_id].ctrl_buf);
if (entityID != 0)
{
if (tud_audio_set_req_entity_cb)
{
// Check if entity is present and get corresponding driver index
TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &func_id));
// Invoke callback
return tud_audio_set_req_entity_cb(rhport, p_request, _audiod_fct[func_id].ctrl_buf);
}
else
{
TU_LOG2(" No entity set request callback available!\r\n");
return false; // In case no callback function is present or request can not be conducted we stall it
}
}
else
{
TU_LOG2(" No entity set request callback available!\r\n");
return false; // In case no callback function is present or request can not be conducted we stall it
if (tud_audio_set_req_itf_cb)
{
// Find index of audio driver structure and verify interface really exists
TU_VERIFY(audiod_verify_itf_exists(itf, &func_id));
// Invoke callback
return tud_audio_set_req_itf_cb(rhport, p_request, _audiod_fct[func_id].ctrl_buf);
}
else
{
TU_LOG2(" No interface set request callback available!\r\n");
return false; // In case no callback function is present or request can not be conducted we stall it
}
}
}
else
{
if (tud_audio_set_req_itf_cb)
{
// Find index of audio driver structure and verify interface really exists
TU_VERIFY(audiod_verify_itf_exists(itf, &func_id));
// Invoke callback
return tud_audio_set_req_itf_cb(rhport, p_request, _audiod_fct[func_id].ctrl_buf);
}
else
{
TU_LOG2(" No interface set request callback available!\r\n");
return false; // In case no callback function is present or request can not be conducted we stall it
}
}
break;
case TUSB_REQ_RCPT_ENDPOINT: ; // The semicolon is there to enable a declaration right after the label
uint8_t ep = TU_U16_LOW(p_request->wIndex);
if (tud_audio_set_req_ep_cb)
case TUSB_REQ_RCPT_ENDPOINT:
{
// Check if entity is present and get corresponding driver index
TU_VERIFY(audiod_verify_ep_exists(ep, &func_id));
uint8_t ep = TU_U16_LOW(p_request->wIndex);
// Invoke callback
return tud_audio_set_req_ep_cb(rhport, p_request, _audiod_fct[func_id].ctrl_buf);
}
else
{
TU_LOG2(" No EP set request callback available!\r\n");
return false; // In case no callback function is present or request can not be conducted we stall it
}
if (tud_audio_set_req_ep_cb)
{
// Check if entity is present and get corresponding driver index
TU_VERIFY(audiod_verify_ep_exists(ep, &func_id));
// Invoke callback
return tud_audio_set_req_ep_cb(rhport, p_request, _audiod_fct[func_id].ctrl_buf);
}
else
{
TU_LOG2(" No EP set request callback available!\r\n");
return false; // In case no callback function is present or request can not be conducted we stall it
}
}
break;
// Unknown/Unsupported recipient
default: TU_BREAKPOINT(); return false;
}
@@ -1754,69 +1777,71 @@ static bool audiod_control_request(uint8_t rhport, tusb_control_request_t const
// Conduct checks which depend on the recipient
switch (p_request->bmRequestType_bit.recipient)
{
case TUSB_REQ_RCPT_INTERFACE: ; // The semicolon is there to enable a declaration right after the label
uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
// Verify if entity is present
if (entityID != 0)
case TUSB_REQ_RCPT_INTERFACE:
{
// Find index of audio driver structure and verify entity really exists
TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &func_id));
uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
// In case we got a get request invoke callback - callback needs to answer as defined in UAC2 specification page 89 - 5. Requests
if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN)
// Verify if entity is present
if (entityID != 0)
{
if (tud_audio_get_req_entity_cb)
// Find index of audio driver structure and verify entity really exists
TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &func_id));
// In case we got a get request invoke callback - callback needs to answer as defined in UAC2 specification page 89 - 5. Requests
if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN)
{
return tud_audio_get_req_entity_cb(rhport, p_request);
}
else
{
TU_LOG2(" No entity get request callback available!\r\n");
return false; // Stall
if (tud_audio_get_req_entity_cb)
{
return tud_audio_get_req_entity_cb(rhport, p_request);
}
else
{
TU_LOG2(" No entity get request callback available!\r\n");
return false; // Stall
}
}
}
}
else
{
// Find index of audio driver structure and verify interface really exists
TU_VERIFY(audiod_verify_itf_exists(itf, &func_id));
// In case we got a get request invoke callback - callback needs to answer as defined in UAC2 specification page 89 - 5. Requests
if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN)
else
{
if (tud_audio_get_req_itf_cb)
// Find index of audio driver structure and verify interface really exists
TU_VERIFY(audiod_verify_itf_exists(itf, &func_id));
// In case we got a get request invoke callback - callback needs to answer as defined in UAC2 specification page 89 - 5. Requests
if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN)
{
return tud_audio_get_req_itf_cb(rhport, p_request);
}
else
{
TU_LOG2(" No interface get request callback available!\r\n");
return false; // Stall
if (tud_audio_get_req_itf_cb)
{
return tud_audio_get_req_itf_cb(rhport, p_request);
}
else
{
TU_LOG2(" No interface get request callback available!\r\n");
return false; // Stall
}
}
}
}
break;
case TUSB_REQ_RCPT_ENDPOINT: ; // The semicolon is there to enable a declaration right after the label
uint8_t ep = TU_U16_LOW(p_request->wIndex);
// Find index of audio driver structure and verify EP really exists
TU_VERIFY(audiod_verify_ep_exists(ep, &func_id));
// In case we got a get request invoke callback - callback needs to answer as defined in UAC2 specification page 89 - 5. Requests
if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN)
case TUSB_REQ_RCPT_ENDPOINT:
{
if (tud_audio_get_req_ep_cb)
uint8_t ep = TU_U16_LOW(p_request->wIndex);
// Find index of audio driver structure and verify EP really exists
TU_VERIFY(audiod_verify_ep_exists(ep, &func_id));
// In case we got a get request invoke callback - callback needs to answer as defined in UAC2 specification page 89 - 5. Requests
if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN)
{
return tud_audio_get_req_ep_cb(rhport, p_request);
}
else
{
TU_LOG2(" No EP get request callback available!\r\n");
return false; // Stall
if (tud_audio_get_req_ep_cb)
{
return tud_audio_get_req_ep_cb(rhport, p_request);
}
else
{
TU_LOG2(" No EP get request callback available!\r\n");
return false; // Stall
}
}
}
break;
@@ -1913,8 +1938,12 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3
{
if (tud_audio_fb_done_cb) TU_VERIFY(tud_audio_fb_done_cb(rhport));
// Schedule next transmission - value is changed bytud_audio_n_fb_set() in the meantime or the old value gets sent
return audiod_fb_send(rhport, &_audiod_fct[func_id]);
// Schedule a transmit with the new value if EP is not busy
if (!usbd_edpt_busy(rhport, _audiod_fct[func_id].ep_fb))
{
// Schedule next transmission - value is changed bytud_audio_n_fb_set() in the meantime or the old value gets sent
return audiod_fb_send(rhport, &_audiod_fct[func_id]);
}
}
#endif
#endif
@@ -1935,29 +1964,31 @@ bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_req
// Conduct checks which depend on the recipient
switch (p_request->bmRequestType_bit.recipient)
{
case TUSB_REQ_RCPT_INTERFACE: ; // The semicolon is there to enable a declaration right after the label
uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
// Verify if entity is present
if (entityID != 0)
case TUSB_REQ_RCPT_INTERFACE:
{
// Find index of audio driver structure and verify entity really exists
TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &func_id));
}
else
{
// Find index of audio driver structure and verify interface really exists
TU_VERIFY(audiod_verify_itf_exists(itf, &func_id));
uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
// Verify if entity is present
if (entityID != 0)
{
// Find index of audio driver structure and verify entity really exists
TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &func_id));
}
else
{
// Find index of audio driver structure and verify interface really exists
TU_VERIFY(audiod_verify_itf_exists(itf, &func_id));
}
}
break;
case TUSB_REQ_RCPT_ENDPOINT: ; // The semicolon is there to enable a declaration right after the label
case TUSB_REQ_RCPT_ENDPOINT:
{
uint8_t ep = TU_U16_LOW(p_request->wIndex);
uint8_t ep = TU_U16_LOW(p_request->wIndex);
// Find index of audio driver structure and verify EP really exists
TU_VERIFY(audiod_verify_ep_exists(ep, &func_id));
// Find index of audio driver structure and verify EP really exists
TU_VERIFY(audiod_verify_ep_exists(ep, &func_id));
}
break;
// Unknown/Unsupported recipient
@@ -1992,15 +2023,17 @@ static bool audiod_get_AS_interface_index(uint8_t itf, audiod_function_t * audio
while (p_desc < p_desc_end)
{
// We assume the number of alternate settings is increasing thus we return the index of alternate setting zero!
if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const * )p_desc)->bInterfaceNumber == itf)
if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const * )p_desc)->bAlternateSetting == 0)
{
*idxItf = tmp;
*pp_desc_int = p_desc;
return true;
if (((tusb_desc_interface_t const * )p_desc)->bInterfaceNumber == itf)
{
*idxItf = tmp;
*pp_desc_int = p_desc;
return true;
}
// Increase index, bytes read, and pointer
tmp++;
}
// Increase index, bytes read, and pointer
tmp++;
p_desc = tu_desc_next(p_desc);
}
}
@@ -2137,10 +2170,10 @@ static void audiod_parse_for_AS_params(audiod_function_t* audio, uint8_t const *
if (as_itf == audio->ep_in_as_intf_num)
{
audio->n_channels_tx = ((audio_desc_cs_as_interface_t const * )p_desc)->bNrChannels;
audio->format_type_tx = ((audio_desc_cs_as_interface_t const * )p_desc)->bFormatType;
audio->format_type_tx = (audio_format_type_t)(((audio_desc_cs_as_interface_t const * )p_desc)->bFormatType);
#if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
audio->format_type_I_tx = ((audio_desc_cs_as_interface_t const * )p_desc)->bmFormats;
audio->format_type_I_tx = (audio_data_format_type_I_t)(((audio_desc_cs_as_interface_t const * )p_desc)->bmFormats);
#endif
}
#endif
@@ -2202,22 +2235,19 @@ bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback)
TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
// Format the feedback value
if (_audiod_fct[func_id].rhport == 0)
{
uint8_t * fb = (uint8_t *) &_audiod_fct[func_id].fb_val;
#if !TUD_OPT_HIGH_SPEED
uint8_t * fb = (uint8_t *) &_audiod_fct[func_id].fb_val;
// For FS format is 10.14
*(fb++) = (feedback >> 2) & 0xFF;
*(fb++) = (feedback >> 10) & 0xFF;
*(fb++) = (feedback >> 18) & 0xFF;
// 4th byte is needed to work correctly with MS Windows
*fb = 0;
}
else
{
// For HS format is 16.16 as originally demanded
_audiod_fct[func_id].fb_val = feedback;
}
// For FS format is 10.14
*(fb++) = (feedback >> 2) & 0xFF;
*(fb++) = (feedback >> 10) & 0xFF;
*(fb++) = (feedback >> 18) & 0xFF;
// 4th byte is needed to work correctly with MS Windows
*fb = 0;
#else
// For HS format is 16.16 as originally demanded
_audiod_fct[func_id].fb_val = feedback;
#endif
// Schedule a transmit with the new value if EP is not busy - this triggers repetitive scheduling of the feedback value
if (!usbd_edpt_busy(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_fb))
+2 -2
View File
@@ -214,14 +214,14 @@ bool btd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t c
}
else return false;
return tud_control_xfer(rhport, request, &_btd_itf.hci_cmd, request->wLength);
return tud_control_xfer(rhport, request, &_btd_itf.hci_cmd, sizeof(_btd_itf.hci_cmd));
}
else if ( stage == CONTROL_STAGE_DATA )
{
// Handle class request only
TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
if (tud_bt_hci_cmd_cb) tud_bt_hci_cmd_cb(&_btd_itf.hci_cmd, request->wLength);
if (tud_bt_hci_cmd_cb) tud_bt_hci_cmd_cb(&_btd_itf.hci_cmd, tu_min16(request->wLength, sizeof(_btd_itf.hci_cmd)));
}
return true;
+11 -6
View File
@@ -215,6 +215,10 @@ typedef enum
// Class Specific Functional Descriptor (Communication Interface)
//--------------------------------------------------------------------+
// Start of all packed definitions for compiler without per-type packed
TU_ATTR_PACKED_BEGIN
TU_ATTR_BIT_FIELD_ORDER_BEGIN
/// Header Functional Descriptor (Communication Interface)
typedef struct TU_ATTR_PACKED
{
@@ -235,7 +239,7 @@ typedef struct TU_ATTR_PACKED
}cdc_desc_func_union_t;
#define cdc_desc_func_union_n_t(no_slave)\
struct TU_ATTR_PACKED { \
struct TU_ATTR_PACKED { \
uint8_t bLength ;\
uint8_t bDescriptorType ;\
uint8_t bDescriptorSubType ;\
@@ -254,7 +258,7 @@ typedef struct TU_ATTR_PACKED
}cdc_desc_func_country_selection_t;
#define cdc_desc_func_country_selection_n_t(no_country) \
struct TU_ATTR_PACKED {\
struct TU_ATTR_PACKED { \
uint8_t bLength ;\
uint8_t bDescriptorType ;\
uint8_t bDescriptorSubType ;\
@@ -283,7 +287,6 @@ typedef struct TU_ATTR_PACKED
uint8_t bDataInterface;
}cdc_desc_func_call_management_t;
typedef struct TU_ATTR_PACKED
{
uint8_t support_comm_request : 1; ///< Device supports the request combination of Set_Comm_Feature, Clear_Comm_Feature, and Get_Comm_Feature.
@@ -295,8 +298,8 @@ typedef struct TU_ATTR_PACKED
TU_VERIFY_STATIC(sizeof(cdc_acm_capability_t) == 1, "mostly problem with compiler");
/// \brief Abstract Control Management Functional Descriptor
/// \details This functional descriptor describes the commands supported by by the Communications Class interface with SubClass code of \ref CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL
/// Abstract Control Management Functional Descriptor
/// This functional descriptor describes the commands supported by by the Communications Class interface with SubClass code of \ref CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL
typedef struct TU_ATTR_PACKED
{
uint8_t bLength ; ///< Size of this descriptor in bytes.
@@ -367,6 +370,7 @@ typedef struct TU_ATTR_PACKED
} bmCapabilities;
}cdc_desc_func_telephone_call_state_reporting_capabilities_t;
// TODO remove
static inline uint8_t cdc_functional_desc_typeof(uint8_t const * p_desc)
{
return p_desc[2];
@@ -394,7 +398,8 @@ typedef struct TU_ATTR_PACKED
TU_VERIFY_STATIC(sizeof(cdc_line_control_state_t) == 2, "size is not correct");
/** @} */
TU_ATTR_PACKED_END // End of all packed definitions
TU_ATTR_BIT_FIELD_ORDER_END
#ifdef __cplusplus
}
+4 -6
View File
@@ -273,9 +273,6 @@ uint16_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1
TU_VERIFY( TUSB_CLASS_CDC == itf_desc->bInterfaceClass &&
CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass, 0);
// Note: 0xFF can be used with RNDIS
TU_VERIFY(tu_within(CDC_COMM_PROTOCOL_NONE, itf_desc->bInterfaceProtocol, CDC_COMM_PROTOCOL_ATCOMMAND_CDMA), 0);
// Find available interface
cdcd_interface_t * p_cdc = NULL;
for(uint8_t cdc_id=0; cdc_id<CFG_TUD_CDC; cdc_id++)
@@ -303,10 +300,11 @@ uint16_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1
if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
{
// notification endpoint if any
TU_ASSERT( usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), 0 );
// notification endpoint
tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc;
p_cdc->ep_notif = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
TU_ASSERT( usbd_edpt_open(rhport, desc_ep), 0 );
p_cdc->ep_notif = desc_ep->bEndpointAddress;
drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
+33 -33
View File
@@ -51,7 +51,7 @@ typedef struct {
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
static cdch_data_t cdch_data[CFG_TUSB_HOST_DEVICE_MAX];
static cdch_data_t cdch_data[CFG_TUH_DEVICE_MAX];
static inline cdch_data_t* get_itf(uint8_t dev_addr)
{
@@ -146,32 +146,30 @@ bool tuh_cdc_set_control_line_state(uint8_t dev_addr, bool dtr, bool rts, tuh_co
//--------------------------------------------------------------------+
void cdch_init(void)
{
tu_memclr(cdch_data, sizeof(cdch_data_t)*CFG_TUSB_HOST_DEVICE_MAX);
tu_memclr(cdch_data, sizeof(cdch_data));
}
bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length)
bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
{
// Only support ACM
TU_VERIFY( CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass);
(void) max_len;
// Only support AT commands, no protocol and vendor specific commands.
TU_VERIFY(tu_within(CDC_COMM_PROTOCOL_NONE, itf_desc->bInterfaceProtocol, CDC_COMM_PROTOCOL_ATCOMMAND_CDMA) ||
0xff == itf_desc->bInterfaceProtocol);
// Only support ACM subclass
// Protocol 0xFF can be RNDIS device for windows XP
TU_VERIFY( TUSB_CLASS_CDC == itf_desc->bInterfaceClass &&
CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass &&
0xFF != itf_desc->bInterfaceProtocol);
uint8_t const * p_desc;
cdch_data_t * p_cdc;
cdch_data_t * p_cdc = get_itf(dev_addr);
p_desc = tu_desc_next(itf_desc);
p_cdc = get_itf(dev_addr);
p_cdc->itf_num = itf_desc->bInterfaceNumber;
p_cdc->itf_protocol = itf_desc->bInterfaceProtocol; // TODO 0xff is consider as rndis candidate, other is virtual Com
p_cdc->itf_num = itf_desc->bInterfaceNumber;
p_cdc->itf_protocol = itf_desc->bInterfaceProtocol;
//------------- Communication Interface -------------//
(*p_length) = sizeof(tusb_desc_interface_t);
uint16_t drv_len = tu_desc_len(itf_desc);
uint8_t const * p_desc = tu_desc_next(itf_desc);
// Communication Functional Descriptors
while( TUSB_DESC_CS_INTERFACE == p_desc[DESC_OFFSET_TYPE] )
while( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len )
{
if ( CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT == cdc_functional_desc_typeof(p_desc) )
{
@@ -179,47 +177,47 @@ bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *it
p_cdc->acm_capability = ((cdc_desc_func_acm_t const *) p_desc)->bmCapabilities;
}
(*p_length) += p_desc[DESC_OFFSET_LEN];
drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
}
if ( TUSB_DESC_ENDPOINT == p_desc[DESC_OFFSET_TYPE])
if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
{
// notification endpoint
tusb_desc_endpoint_t const * ep_desc = (tusb_desc_endpoint_t const *) p_desc;
tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc;
TU_ASSERT( usbh_edpt_open(rhport, dev_addr, ep_desc) );
p_cdc->ep_notif = ep_desc->bEndpointAddress;
TU_ASSERT( usbh_edpt_open(rhport, dev_addr, desc_ep) );
p_cdc->ep_notif = desc_ep->bEndpointAddress;
(*p_length) += p_desc[DESC_OFFSET_LEN];
drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
}
//------------- Data Interface (if any) -------------//
if ( (TUSB_DESC_INTERFACE == p_desc[DESC_OFFSET_TYPE]) &&
if ( (TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) &&
(TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass) )
{
(*p_length) += p_desc[DESC_OFFSET_LEN];
// next to endpoint descriptor
drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
// data endpoints expected to be in pairs
for(uint32_t i=0; i<2; i++)
{
tusb_desc_endpoint_t const *ep_desc = (tusb_desc_endpoint_t const *) p_desc;
TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType);
TU_ASSERT(TUSB_XFER_BULK == ep_desc->bmAttributes.xfer);
tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) p_desc;
TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && TUSB_XFER_BULK == desc_ep->bmAttributes.xfer);
TU_ASSERT(usbh_edpt_open(rhport, dev_addr, ep_desc));
TU_ASSERT(usbh_edpt_open(rhport, dev_addr, desc_ep));
if ( tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN )
if ( tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN )
{
p_cdc->ep_in = ep_desc->bEndpointAddress;
p_cdc->ep_in = desc_ep->bEndpointAddress;
}else
{
p_cdc->ep_out = ep_desc->bEndpointAddress;
p_cdc->ep_out = desc_ep->bEndpointAddress;
}
(*p_length) += p_desc[DESC_OFFSET_LEN];
drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next( p_desc );
}
}
@@ -242,6 +240,8 @@ bool cdch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32
void cdch_close(uint8_t dev_addr)
{
TU_VERIFY(dev_addr <= CFG_TUH_DEVICE_MAX, );
cdch_data_t * p_cdc = get_itf(dev_addr);
tu_memclr(p_cdc, sizeof(cdch_data_t));
}
+5 -5
View File
@@ -121,11 +121,11 @@ void tuh_cdc_xfer_isr(uint8_t dev_addr, xfer_result_t event, cdc_pipeid_t pipe_i
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
void cdch_init(void);
bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length);
bool cdch_set_config(uint8_t dev_addr, uint8_t itf_num);
bool cdch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
void cdch_close(uint8_t dev_addr);
void cdch_init (void);
bool cdch_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
bool cdch_set_config (uint8_t dev_addr, uint8_t itf_num);
bool cdch_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
void cdch_close (uint8_t dev_addr);
#ifdef __cplusplus
}
+5 -5
View File
@@ -40,10 +40,10 @@
//--------------------------------------------------------------------+
#define RNDIS_MSG_PAYLOAD_MAX (1024*4)
CFG_TUSB_MEM_SECTION static uint8_t msg_notification[CFG_TUSB_HOST_DEVICE_MAX][8];
CFG_TUSB_MEM_SECTION static uint8_t msg_notification[CFG_TUH_DEVICE_MAX][8];
CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4) static uint8_t msg_payload[RNDIS_MSG_PAYLOAD_MAX];
static rndish_data_t rndish_data[CFG_TUSB_HOST_DEVICE_MAX];
static rndish_data_t rndish_data[CFG_TUH_DEVICE_MAX];
// TODO Microsoft requires message length for any get command must be at least 4096 bytes
@@ -88,7 +88,7 @@ static tusb_error_t rndis_body_subtask(void)
OSAL_SUBTASK_BEGIN
for (relative_addr = 0; relative_addr < CFG_TUSB_HOST_DEVICE_MAX; relative_addr++)
for (relative_addr = 0; relative_addr < CFG_TUH_DEVICE_MAX; relative_addr++)
{
}
@@ -103,12 +103,12 @@ static tusb_error_t rndis_body_subtask(void)
//--------------------------------------------------------------------+
void rndish_init(void)
{
tu_memclr(rndish_data, sizeof(rndish_data_t)*CFG_TUSB_HOST_DEVICE_MAX);
tu_memclr(rndish_data, sizeof(rndish_data_t)*CFG_TUH_DEVICE_MAX);
//------------- Task creation -------------//
//------------- semaphore creation for notificaiton pipe -------------//
for(uint8_t i=0; i<CFG_TUSB_HOST_DEVICE_MAX; i++)
for(uint8_t i=0; i<CFG_TUH_DEVICE_MAX; i++)
{
rndish_data[i].sem_notification_hdl = osal_semaphore_create( OSAL_SEM_REF(rndish_data[i].semaphore_notification) );
}
+24 -23
View File
@@ -36,6 +36,7 @@
//--------------------------------------------------------------------+
// Common Definitions
//--------------------------------------------------------------------+
// DFU Protocol
typedef enum
{
@@ -77,28 +78,28 @@ typedef enum {
// DFU Status
typedef enum {
DFU_STATUS_OK = 0x00,
DFU_STATUS_ERRTARGET = 0x01,
DFU_STATUS_ERRFILE = 0x02,
DFU_STATUS_ERRWRITE = 0x03,
DFU_STATUS_ERRERASE = 0x04,
DFU_STATUS_ERRCHECK_ERASED = 0x05,
DFU_STATUS_ERRPROG = 0x06,
DFU_STATUS_ERRVERIFY = 0x07,
DFU_STATUS_ERRADDRESS = 0x08,
DFU_STATUS_ERRNOTDONE = 0x09,
DFU_STATUS_ERRFIRMWARE = 0x0A,
DFU_STATUS_ERRVENDOR = 0x0B,
DFU_STATUS_ERRUSBR = 0x0C,
DFU_STATUS_ERRPOR = 0x0D,
DFU_STATUS_ERRUNKNOWN = 0x0E,
DFU_STATUS_ERRSTALLEDPKT = 0x0F,
} dfu_device_status_t;
DFU_STATUS_OK = 0x00,
DFU_STATUS_ERR_TARGET = 0x01,
DFU_STATUS_ERR_FILE = 0x02,
DFU_STATUS_ERR_WRITE = 0x03,
DFU_STATUS_ERR_ERASE = 0x04,
DFU_STATUS_ERR_CHECK_ERASED = 0x05,
DFU_STATUS_ERR_PROG = 0x06,
DFU_STATUS_ERR_VERIFY = 0x07,
DFU_STATUS_ERR_ADDRESS = 0x08,
DFU_STATUS_ERR_NOTDONE = 0x09,
DFU_STATUS_ERR_FIRMWARE = 0x0A,
DFU_STATUS_ERR_VENDOR = 0x0B,
DFU_STATUS_ERR_USBR = 0x0C,
DFU_STATUS_ERR_POR = 0x0D,
DFU_STATUS_ERR_UNKNOWN = 0x0E,
DFU_STATUS_ERR_STALLEDPKT = 0x0F,
} dfu_status_t;
#define DFU_FUNC_ATTR_CAN_DOWNLOAD_BITMASK (1 << 0)
#define DFU_FUNC_ATTR_CAN_UPLOAD_BITMASK (1 << 1)
#define DFU_FUNC_ATTR_MANIFESTATION_TOLERANT_BITMASK (1 << 2)
#define DFU_FUNC_ATTR_WILL_DETACH_BITMASK (1 << 3)
#define DFU_ATTR_CAN_DOWNLOAD (1u << 0)
#define DFU_ATTR_CAN_UPLOAD (1u << 1)
#define DFU_ATTR_MANIFESTATION_TOLERANT (1u << 2)
#define DFU_ATTR_WILL_DETACH (1u << 3)
// DFU Status Request Payload
typedef struct TU_ATTR_PACKED
@@ -107,9 +108,9 @@ typedef struct TU_ATTR_PACKED
uint8_t bwPollTimeout[3];
uint8_t bState;
uint8_t iString;
} dfu_status_req_payload_t;
} dfu_status_response_t;
TU_VERIFY_STATIC( sizeof(dfu_status_req_payload_t) == 6, "size is not correct");
TU_VERIFY_STATIC( sizeof(dfu_status_response_t) == 6, "size is not correct");
#ifdef __cplusplus
}
+316 -478
View File
@@ -26,7 +26,7 @@
#include "tusb_option.h"
#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_DFU_MODE)
#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_DFU)
#include "device/usbd.h"
#include "device/usbd_pvt.h"
@@ -40,24 +40,34 @@
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
typedef struct TU_ATTR_PACKED
typedef struct
{
dfu_device_status_t status;
dfu_state_t state;
uint8_t attrs;
bool blk_transfer_in_proc;
CFG_TUSB_MEM_ALIGN uint8_t transfer_buf[CFG_TUD_DFU_TRANSFER_BUFFER_SIZE];
uint8_t attrs;
uint8_t alt;
dfu_state_t state;
dfu_status_t status;
bool flashing_in_progress;
uint16_t block;
uint16_t length;
CFG_TUSB_MEM_ALIGN uint8_t transfer_buf[CFG_TUD_DFU_XFER_BUFSIZE];
} dfu_state_ctx_t;
// Only a single dfu state is allowed
CFG_TUSB_MEM_SECTION static dfu_state_ctx_t _dfu_state_ctx;
CFG_TUSB_MEM_SECTION static dfu_state_ctx_t _dfu_ctx;
static void reset_state(void)
{
_dfu_ctx.state = DFU_IDLE;
_dfu_ctx.status = DFU_STATUS_OK;
_dfu_ctx.flashing_in_progress = false;
}
static void dfu_req_dnload_setup(uint8_t rhport, tusb_control_request_t const * request);
static void dfu_req_getstatus_reply(uint8_t rhport, tusb_control_request_t const * request);
static uint16_t dfu_req_upload(uint8_t rhport, tusb_control_request_t const * request, uint16_t block_num, uint16_t wLength);
static void dfu_req_dnload_reply(uint8_t rhport, tusb_control_request_t const * request);
static bool dfu_state_machine(uint8_t rhport, tusb_control_request_t const * request);
static bool reply_getstatus(uint8_t rhport, tusb_control_request_t const * request, dfu_state_t state, dfu_status_t status, uint32_t timeout);
static bool process_download_get_status(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
static bool process_manifest_get_status(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
//--------------------------------------------------------------------+
// Debug
@@ -66,13 +76,13 @@ static bool dfu_state_machine(uint8_t rhport, tusb_control_request_t const * req
static tu_lookup_entry_t const _dfu_request_lookup[] =
{
{ .key = DFU_REQUEST_DETACH , .data = "DETACH" },
{ .key = DFU_REQUEST_DNLOAD , .data = "DNLOAD" },
{ .key = DFU_REQUEST_UPLOAD , .data = "UPLOAD" },
{ .key = DFU_REQUEST_DETACH , .data = "DETACH" },
{ .key = DFU_REQUEST_DNLOAD , .data = "DNLOAD" },
{ .key = DFU_REQUEST_UPLOAD , .data = "UPLOAD" },
{ .key = DFU_REQUEST_GETSTATUS , .data = "GETSTATUS" },
{ .key = DFU_REQUEST_CLRSTATUS , .data = "CLRSTATUS" },
{ .key = DFU_REQUEST_GETSTATE , .data = "GETSTATE" },
{ .key = DFU_REQUEST_ABORT , .data = "ABORT" },
{ .key = DFU_REQUEST_GETSTATE , .data = "GETSTATE" },
{ .key = DFU_REQUEST_ABORT , .data = "ABORT" },
};
static tu_lookup_table_t const _dfu_request_table =
@@ -83,17 +93,17 @@ static tu_lookup_table_t const _dfu_request_table =
static tu_lookup_entry_t const _dfu_state_lookup[] =
{
{ .key = APP_IDLE , .data = "APP_IDLE" },
{ .key = APP_DETACH , .data = "APP_DETACH" },
{ .key = DFU_IDLE , .data = "DFU_IDLE" },
{ .key = DFU_DNLOAD_SYNC , .data = "DFU_DNLOAD_SYNC" },
{ .key = DFU_DNBUSY , .data = "DFU_DNBUSY" },
{ .key = DFU_DNLOAD_IDLE , .data = "DFU_DNLOAD_IDLE" },
{ .key = DFU_MANIFEST_SYNC , .data = "DFU_MANIFEST_SYNC" },
{ .key = DFU_MANIFEST , .data = "DFU_MANIFEST" },
{ .key = DFU_MANIFEST_WAIT_RESET , .data = "DFU_MANIFEST_WAIT_RESET" },
{ .key = DFU_UPLOAD_IDLE , .data = "DFU_UPLOAD_IDLE" },
{ .key = DFU_ERROR , .data = "DFU_ERROR" },
{ .key = APP_IDLE , .data = "APP_IDLE" },
{ .key = APP_DETACH , .data = "APP_DETACH" },
{ .key = DFU_IDLE , .data = "IDLE" },
{ .key = DFU_DNLOAD_SYNC , .data = "DNLOAD_SYNC" },
{ .key = DFU_DNBUSY , .data = "DNBUSY" },
{ .key = DFU_DNLOAD_IDLE , .data = "DNLOAD_IDLE" },
{ .key = DFU_MANIFEST_SYNC , .data = "MANIFEST_SYNC" },
{ .key = DFU_MANIFEST , .data = "MANIFEST" },
{ .key = DFU_MANIFEST_WAIT_RESET , .data = "MANIFEST_WAIT_RESET" },
{ .key = DFU_UPLOAD_IDLE , .data = "UPLOAD_IDLE" },
{ .key = DFU_ERROR , .data = "ERROR" },
};
static tu_lookup_table_t const _dfu_state_table =
@@ -104,22 +114,22 @@ static tu_lookup_table_t const _dfu_state_table =
static tu_lookup_entry_t const _dfu_status_lookup[] =
{
{ .key = DFU_STATUS_OK , .data = "OK" },
{ .key = DFU_STATUS_ERRTARGET , .data = "errTARGET" },
{ .key = DFU_STATUS_ERRFILE , .data = "errFILE" },
{ .key = DFU_STATUS_ERRWRITE , .data = "errWRITE" },
{ .key = DFU_STATUS_ERRERASE , .data = "errERASE" },
{ .key = DFU_STATUS_ERRCHECK_ERASED , .data = "errCHECK_ERASED" },
{ .key = DFU_STATUS_ERRPROG , .data = "errPROG" },
{ .key = DFU_STATUS_ERRVERIFY , .data = "errVERIFY" },
{ .key = DFU_STATUS_ERRADDRESS , .data = "errADDRESS" },
{ .key = DFU_STATUS_ERRNOTDONE , .data = "errNOTDONE" },
{ .key = DFU_STATUS_ERRFIRMWARE , .data = "errFIRMWARE" },
{ .key = DFU_STATUS_ERRVENDOR , .data = "errVENDOR" },
{ .key = DFU_STATUS_ERRUSBR , .data = "errUSBR" },
{ .key = DFU_STATUS_ERRPOR , .data = "errPOR" },
{ .key = DFU_STATUS_ERRUNKNOWN , .data = "errUNKNOWN" },
{ .key = DFU_STATUS_ERRSTALLEDPKT , .data = "errSTALLEDPKT" },
{ .key = DFU_STATUS_OK , .data = "OK" },
{ .key = DFU_STATUS_ERR_TARGET , .data = "errTARGET" },
{ .key = DFU_STATUS_ERR_FILE , .data = "errFILE" },
{ .key = DFU_STATUS_ERR_WRITE , .data = "errWRITE" },
{ .key = DFU_STATUS_ERR_ERASE , .data = "errERASE" },
{ .key = DFU_STATUS_ERR_CHECK_ERASED , .data = "errCHECK_ERASED" },
{ .key = DFU_STATUS_ERR_PROG , .data = "errPROG" },
{ .key = DFU_STATUS_ERR_VERIFY , .data = "errVERIFY" },
{ .key = DFU_STATUS_ERR_ADDRESS , .data = "errADDRESS" },
{ .key = DFU_STATUS_ERR_NOTDONE , .data = "errNOTDONE" },
{ .key = DFU_STATUS_ERR_FIRMWARE , .data = "errFIRMWARE" },
{ .key = DFU_STATUS_ERR_VENDOR , .data = "errVENDOR" },
{ .key = DFU_STATUS_ERR_USBR , .data = "errUSBR" },
{ .key = DFU_STATUS_ERR_POR , .data = "errPOR" },
{ .key = DFU_STATUS_ERR_UNKNOWN , .data = "errUNKNOWN" },
{ .key = DFU_STATUS_ERR_STALLEDPKT , .data = "errSTALLEDPKT" },
};
static tu_lookup_table_t const _dfu_status_table =
@@ -130,57 +140,59 @@ static tu_lookup_table_t const _dfu_status_table =
#endif
#define dfu_debug_print_context() \
{ \
TU_LOG2(" DFU at State: %s\r\n Status: %s\r\n", \
tu_lookup_find(&_dfu_state_table, _dfu_state_ctx.state), \
tu_lookup_find(&_dfu_status_table, _dfu_state_ctx.status) ); \
}
//--------------------------------------------------------------------+
// USBD Driver API
//--------------------------------------------------------------------+
void dfu_moded_init(void)
{
_dfu_state_ctx.state = DFU_IDLE;
_dfu_state_ctx.status = DFU_STATUS_OK;
_dfu_state_ctx.attrs = 0;
_dfu_state_ctx.blk_transfer_in_proc = false;
dfu_debug_print_context();
}
void dfu_moded_reset(uint8_t rhport)
{
(void) rhport;
_dfu_state_ctx.state = DFU_IDLE;
_dfu_state_ctx.status = DFU_STATUS_OK;
_dfu_state_ctx.blk_transfer_in_proc = false;
dfu_debug_print_context();
_dfu_ctx.attrs = 0;
_dfu_ctx.alt = 0;
reset_state();
}
void dfu_moded_init(void)
{
dfu_moded_reset(0);
}
uint16_t dfu_moded_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
{
(void) rhport;
(void) max_len;
// Ensure this is DFU Mode
TU_VERIFY((itf_desc->bInterfaceSubClass == TUD_DFU_APP_SUBCLASS) &&
(itf_desc->bInterfaceProtocol == DFU_PROTOCOL_DFU), 0);
//------------- Interface (with Alt) descriptor -------------//
uint8_t const itf_num = itf_desc->bInterfaceNumber;
uint8_t alt_count = 0;
uint8_t const * p_desc = tu_desc_next( itf_desc );
uint16_t drv_len = sizeof(tusb_desc_interface_t);
if ( TUSB_DESC_FUNCTIONAL == tu_desc_type(p_desc) )
uint16_t drv_len = 0;
while(itf_desc->bInterfaceSubClass == TUD_DFU_APP_SUBCLASS && itf_desc->bInterfaceProtocol == DFU_PROTOCOL_DFU)
{
tusb_desc_dfu_functional_t const *dfu_desc = (tusb_desc_dfu_functional_t const *)p_desc;
_dfu_state_ctx.attrs = (uint8_t)dfu_desc->bAttributes;
TU_ASSERT(max_len > drv_len, 0);
drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
// Alternate must have the same interface number
TU_ASSERT(itf_desc->bInterfaceNumber == itf_num, 0);
// Alt should increase by one every time
TU_ASSERT(itf_desc->bAlternateSetting == alt_count, 0);
alt_count++;
drv_len += tu_desc_len(itf_desc);
itf_desc = (tusb_desc_interface_t const *) tu_desc_next(itf_desc);
}
//------------- DFU Functional descriptor -------------//
tusb_desc_dfu_functional_t const *func_desc = (tusb_desc_dfu_functional_t const *) itf_desc;
TU_ASSERT(tu_desc_type(func_desc) == TUSB_DESC_FUNCTIONAL, 0);
drv_len += sizeof(tusb_desc_dfu_functional_t);
_dfu_ctx.attrs = func_desc->bAttributes;
// CFG_TUD_DFU_XFER_BUFSIZE has to be set to the buffer size used in TUD_DFU_DESCRIPTOR
uint16_t const transfer_size = tu_le16toh( tu_unaligned_read16((uint8_t*) func_desc + offsetof(tusb_desc_dfu_functional_t, wTransferSize)) );
TU_ASSERT(transfer_size <= CFG_TUD_DFU_XFER_BUFSIZE, drv_len);
return drv_len;
}
@@ -189,432 +201,258 @@ uint16_t dfu_moded_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc,
// return false to stall control endpoint (e.g unsupported request)
bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
{
// nothing to do with DATA stage
if ( stage == CONTROL_STAGE_DATA ) return true;
TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE);
if(stage == CONTROL_STAGE_SETUP)
TU_LOG2(" DFU State : %s, Status: %s\r\n", tu_lookup_find(&_dfu_state_table, _dfu_ctx.state), tu_lookup_find(&_dfu_status_table, _dfu_ctx.status));
if ( request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD )
{
// dfu-util will try to claim the interface with SET_INTERFACE request before sending DFU request
if ( TUSB_REQ_TYPE_STANDARD == request->bmRequestType_bit.type &&
TUSB_REQ_SET_INTERFACE == request->bRequest )
// Standard request include GET/SET_INTERFACE
switch ( request->bRequest )
{
tud_control_status(rhport, request);
return true;
case TUSB_REQ_SET_INTERFACE:
if ( stage == CONTROL_STAGE_SETUP )
{
// Switch Alt interface and reset state machine
_dfu_ctx.alt = (uint8_t) request->wValue;
reset_state();
return tud_control_status(rhport, request);
}
break;
case TUSB_REQ_GET_INTERFACE:
if(stage == CONTROL_STAGE_SETUP)
{
return tud_control_xfer(rhport, request, &_dfu_ctx.alt, 1);
}
break;
// unsupported request
default: return false;
}
}
// Handle class request only from here
TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
switch (request->bRequest)
else if ( request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS )
{
case DFU_REQUEST_DNLOAD:
TU_LOG2(" DFU Request: %s\r\n", tu_lookup_find(&_dfu_request_table, request->bRequest));
// Class request
switch ( request->bRequest )
{
if ( (stage == CONTROL_STAGE_ACK)
&& ((_dfu_state_ctx.attrs & DFU_FUNC_ATTR_CAN_DOWNLOAD_BITMASK) != 0)
&& (_dfu_state_ctx.state == DFU_DNLOAD_SYNC))
{
dfu_req_dnload_reply(rhport, request);
return true;
}
} // fallthrough
case DFU_REQUEST_DETACH:
case DFU_REQUEST_UPLOAD:
case DFU_REQUEST_GETSTATUS:
case DFU_REQUEST_CLRSTATUS:
case DFU_REQUEST_GETSTATE:
case DFU_REQUEST_ABORT:
{
if(stage == CONTROL_STAGE_SETUP)
{
return dfu_state_machine(rhport, request);
}
}
break;
default:
{
TU_LOG2(" DFU Nonstandard Request: %u\r\n", request->bRequest);
return false; // stall unsupported request
}
break;
}
return true;
}
static uint16_t dfu_req_upload(uint8_t rhport, tusb_control_request_t const * request, uint16_t block_num, uint16_t wLength)
{
TU_VERIFY( wLength <= CFG_TUD_DFU_TRANSFER_BUFFER_SIZE);
uint16_t retval = tud_dfu_req_upload_data_cb(block_num, (uint8_t *)_dfu_state_ctx.transfer_buf, wLength);
tud_control_xfer(rhport, request, _dfu_state_ctx.transfer_buf, retval);
return retval;
}
static void dfu_req_getstatus_reply(uint8_t rhport, tusb_control_request_t const * request)
{
dfu_status_req_payload_t resp;
resp.bStatus = _dfu_state_ctx.status;
memset((uint8_t *)&resp.bwPollTimeout, 0x00, 3);
resp.bState = _dfu_state_ctx.state;
resp.iString = 0;
tud_control_xfer(rhport, request, &resp, sizeof(dfu_status_req_payload_t));
}
static void dfu_req_getstate_reply(uint8_t rhport, tusb_control_request_t const * request)
{
tud_control_xfer(rhport, request, &_dfu_state_ctx.state, 1);
}
static void dfu_req_dnload_setup(uint8_t rhport, tusb_control_request_t const * request)
{
// TODO: add "zero" copy mode so the buffer we read into can be provided by the user
// if they wish, there still will be the internal control buffer copy to this buffer
// but this mode would provide zero copy from the class driver to the application
// setup for data phase
tud_control_xfer(rhport, request, _dfu_state_ctx.transfer_buf, request->wLength);
}
static void dfu_req_dnload_reply(uint8_t rhport, tusb_control_request_t const * request)
{
(void) rhport;
tud_dfu_req_dnload_data_cb(request->wValue, (uint8_t *)_dfu_state_ctx.transfer_buf, request->wLength);
_dfu_state_ctx.blk_transfer_in_proc = false;
}
void tud_dfu_dnload_complete(void)
{
if (_dfu_state_ctx.state == DFU_DNBUSY)
{
_dfu_state_ctx.state = DFU_DNLOAD_SYNC;
} else if (_dfu_state_ctx.state == DFU_MANIFEST)
{
_dfu_state_ctx.state = ((_dfu_state_ctx.attrs & DFU_FUNC_ATTR_MANIFESTATION_TOLERANT_BITMASK) != 0)
? DFU_MANIFEST_WAIT_RESET : DFU_MANIFEST_SYNC;
}
}
static bool dfu_state_machine(uint8_t rhport, tusb_control_request_t const * request)
{
TU_LOG2(" DFU Request: %s\r\n", tu_lookup_find(&_dfu_request_table, request->bRequest));
TU_LOG2(" DFU State Machine: %s\r\n", tu_lookup_find(&_dfu_state_table, _dfu_state_ctx.state));
switch (_dfu_state_ctx.state)
{
case DFU_IDLE:
{
switch (request->bRequest)
{
case DFU_REQUEST_DNLOAD:
case DFU_REQUEST_DETACH:
if ( stage == CONTROL_STAGE_SETUP )
{
if( ((_dfu_state_ctx.attrs & DFU_FUNC_ATTR_CAN_DOWNLOAD_BITMASK) != 0)
&& (request->wLength > 0) )
tud_control_status(rhport, request);
}
else if ( stage == CONTROL_STAGE_ACK )
{
if ( tud_dfu_detach_cb ) tud_dfu_detach_cb();
}
break;
case DFU_REQUEST_CLRSTATUS:
if ( stage == CONTROL_STAGE_SETUP )
{
reset_state();
tud_control_status(rhport, request);
}
break;
case DFU_REQUEST_GETSTATE:
if ( stage == CONTROL_STAGE_SETUP )
{
tud_control_xfer(rhport, request, &_dfu_ctx.state, 1);
}
break;
case DFU_REQUEST_ABORT:
if ( stage == CONTROL_STAGE_SETUP )
{
reset_state();
tud_control_status(rhport, request);
}
else if ( stage == CONTROL_STAGE_ACK )
{
if ( tud_dfu_abort_cb ) tud_dfu_abort_cb(_dfu_ctx.alt);
}
break;
case DFU_REQUEST_UPLOAD:
if ( stage == CONTROL_STAGE_SETUP )
{
TU_VERIFY(_dfu_ctx.attrs & DFU_ATTR_CAN_UPLOAD);
TU_VERIFY(tud_dfu_upload_cb);
TU_VERIFY(request->wLength <= CFG_TUD_DFU_XFER_BUFSIZE);
uint16_t const xfer_len = tud_dfu_upload_cb(_dfu_ctx.alt, request->wValue, _dfu_ctx.transfer_buf, request->wLength);
return tud_control_xfer(rhport, request, _dfu_ctx.transfer_buf, xfer_len);
}
break;
case DFU_REQUEST_DNLOAD:
if ( stage == CONTROL_STAGE_SETUP )
{
TU_VERIFY(_dfu_ctx.attrs & DFU_ATTR_CAN_DOWNLOAD);
TU_VERIFY(_dfu_ctx.state == DFU_IDLE || _dfu_ctx.state == DFU_DNLOAD_IDLE);
TU_VERIFY(request->wLength <= CFG_TUD_DFU_XFER_BUFSIZE);
// set to true for both download and manifest
_dfu_ctx.flashing_in_progress = true;
// save block and length for flashing
_dfu_ctx.block = request->wValue;
_dfu_ctx.length = request->wLength;
if ( request->wLength )
{
_dfu_state_ctx.state = DFU_DNLOAD_SYNC;
_dfu_state_ctx.blk_transfer_in_proc = true;
dfu_req_dnload_setup(rhport, request);
} else {
_dfu_state_ctx.state = DFU_ERROR;
// Download with payload -> transition to DOWNLOAD SYNC
_dfu_ctx.state = DFU_DNLOAD_SYNC;
return tud_control_xfer(rhport, request, _dfu_ctx.transfer_buf, request->wLength);
}
else
{
// Download is complete -> transition to MANIFEST SYNC
_dfu_ctx.state = DFU_MANIFEST_SYNC;
return tud_control_status(rhport, request);
}
}
break;
break;
case DFU_REQUEST_UPLOAD:
case DFU_REQUEST_GETSTATUS:
switch ( _dfu_ctx.state )
{
if( ((_dfu_state_ctx.attrs & DFU_FUNC_ATTR_CAN_UPLOAD_BITMASK) != 0) )
{
_dfu_state_ctx.state = DFU_UPLOAD_IDLE;
dfu_req_upload(rhport, request, request->wValue, request->wLength);
} else {
_dfu_state_ctx.state = DFU_ERROR;
}
}
break;
case DFU_REQUEST_GETSTATUS:
{
dfu_req_getstatus_reply(rhport, request);
}
break;
case DFU_REQUEST_GETSTATE:
{
dfu_req_getstate_reply(rhport, request);
}
break;
case DFU_REQUEST_ABORT:
{
; // do nothing, but don't stall so continue on
}
break;
default:
{
_dfu_state_ctx.state = DFU_ERROR;
return false; // stall on all other requests
}
break;
}
}
break;
case DFU_DNLOAD_SYNC:
{
switch (request->bRequest)
{
case DFU_REQUEST_GETSTATUS:
{
if ( _dfu_state_ctx.blk_transfer_in_proc )
{
_dfu_state_ctx.state = DFU_DNBUSY;
dfu_req_getstatus_reply(rhport, request);
} else {
_dfu_state_ctx.state = DFU_DNLOAD_IDLE;
dfu_req_getstatus_reply(rhport, request);
}
}
break;
case DFU_REQUEST_GETSTATE:
{
dfu_req_getstate_reply(rhport, request);
}
break;
default:
{
_dfu_state_ctx.state = DFU_ERROR;
return false; // stall on all other requests
}
break;
}
}
break;
case DFU_DNBUSY:
{
switch (request->bRequest)
{
default:
{
_dfu_state_ctx.state = DFU_ERROR;
return false; // stall on all other requests
}
break;
}
}
break;
case DFU_DNLOAD_IDLE:
{
switch (request->bRequest)
{
case DFU_REQUEST_DNLOAD:
{
if( ((_dfu_state_ctx.attrs & DFU_FUNC_ATTR_CAN_DOWNLOAD_BITMASK) != 0)
&& (request->wLength > 0) )
{
_dfu_state_ctx.state = DFU_DNLOAD_SYNC;
_dfu_state_ctx.blk_transfer_in_proc = true;
dfu_req_dnload_setup(rhport, request);
} else {
if ( tud_dfu_device_data_done_check_cb() )
{
_dfu_state_ctx.state = DFU_MANIFEST_SYNC;
tud_control_status(rhport, request);
} else {
_dfu_state_ctx.state = DFU_ERROR;
return false; // stall
}
}
}
case DFU_DNLOAD_SYNC:
return process_download_get_status(rhport, stage, request);
break;
case DFU_REQUEST_GETSTATUS:
{
dfu_req_getstatus_reply(rhport, request);
}
break;
case DFU_REQUEST_GETSTATE:
{
dfu_req_getstate_reply(rhport, request);
}
break;
case DFU_REQUEST_ABORT:
{
if ( tud_dfu_abort_cb )
{
tud_dfu_abort_cb();
}
_dfu_state_ctx.state = DFU_IDLE;
}
case DFU_MANIFEST_SYNC:
return process_manifest_get_status(rhport, stage, request);
break;
default:
{
_dfu_state_ctx.state = DFU_ERROR;
return false; // stall on all other requests
}
if ( stage == CONTROL_STAGE_SETUP ) return reply_getstatus(rhport, request, _dfu_ctx.state, _dfu_ctx.status, 0);
break;
}
break;
default: return false; // stall unsupported request
}
break;
case DFU_MANIFEST_SYNC:
{
switch (request->bRequest)
{
case DFU_REQUEST_GETSTATUS:
{
if ((_dfu_state_ctx.attrs & DFU_FUNC_ATTR_MANIFESTATION_TOLERANT_BITMASK) != 0)
{
_dfu_state_ctx.state = DFU_MANIFEST;
dfu_req_getstatus_reply(rhport, request);
} else {
if ( tud_dfu_firmware_valid_check_cb() )
{
_dfu_state_ctx.state = DFU_IDLE;
}
dfu_req_getstatus_reply(rhport, request);
}
}
break;
case DFU_REQUEST_GETSTATE:
{
dfu_req_getstate_reply(rhport, request);
}
break;
default:
{
_dfu_state_ctx.state = DFU_ERROR;
return false; // stall on all other requests
}
break;
}
}
break;
case DFU_MANIFEST:
{
switch (request->bRequest)
{
default:
{
return false; // stall on all other requests
}
break;
}
}
break;
case DFU_MANIFEST_WAIT_RESET:
{
// technically we should never even get here, but we will handle it just in case
TU_LOG2(" DFU was in DFU_MANIFEST_WAIT_RESET and got unexpected request: %u\r\n", request->bRequest);
switch (request->bRequest)
{
default:
{
return false; // stall on all other requests
}
break;
}
}
break;
case DFU_UPLOAD_IDLE:
{
switch (request->bRequest)
{
case DFU_REQUEST_UPLOAD:
{
if (dfu_req_upload(rhport, request, request->wValue, request->wLength) != request->wLength)
{
_dfu_state_ctx.state = DFU_IDLE;
}
}
break;
case DFU_REQUEST_GETSTATUS:
{
dfu_req_getstatus_reply(rhport, request);
}
break;
case DFU_REQUEST_GETSTATE:
{
dfu_req_getstate_reply(rhport, request);
}
break;
case DFU_REQUEST_ABORT:
{
if (tud_dfu_abort_cb)
{
tud_dfu_abort_cb();
}
_dfu_state_ctx.state = DFU_IDLE;
}
break;
default:
{
return false; // stall on all other requests
}
break;
}
}
break;
case DFU_ERROR:
{
switch (request->bRequest)
{
case DFU_REQUEST_GETSTATUS:
{
dfu_req_getstatus_reply(rhport, request);
}
break;
case DFU_REQUEST_CLRSTATUS:
{
_dfu_state_ctx.state = DFU_IDLE;
}
break;
case DFU_REQUEST_GETSTATE:
{
dfu_req_getstate_reply(rhport, request);
}
break;
default:
{
return false; // stall on all other requests
}
break;
}
}
break;
default:
_dfu_state_ctx.state = DFU_ERROR;
TU_LOG2(" DFU ERROR: Unexpected state\r\nStalling control pipe\r\n");
return false; // Unexpected state, stall and change to error
}else
{
return false; // unsupported request
}
return true;
}
void tud_dfu_finish_flashing(uint8_t status)
{
_dfu_ctx.flashing_in_progress = false;
if ( status == DFU_STATUS_OK )
{
if (_dfu_ctx.state == DFU_DNBUSY)
{
_dfu_ctx.state = DFU_DNLOAD_SYNC;
}
else if (_dfu_ctx.state == DFU_MANIFEST)
{
_dfu_ctx.state = (_dfu_ctx.attrs & DFU_ATTR_MANIFESTATION_TOLERANT)
? DFU_MANIFEST_SYNC : DFU_MANIFEST_WAIT_RESET;
}
}
else
{
// failed while flashing, move to dfuError
_dfu_ctx.state = DFU_ERROR;
_dfu_ctx.status = (dfu_status_t)status;
}
}
static bool process_download_get_status(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
{
if ( stage == CONTROL_STAGE_SETUP )
{
// only transition to next state on CONTROL_STAGE_ACK
dfu_state_t next_state;
uint32_t timeout;
if ( _dfu_ctx.flashing_in_progress )
{
next_state = DFU_DNBUSY;
timeout = tud_dfu_get_timeout_cb(_dfu_ctx.alt, (uint8_t) next_state);
}
else
{
next_state = DFU_DNLOAD_IDLE;
timeout = 0;
}
return reply_getstatus(rhport, request, next_state, _dfu_ctx.status, timeout);
}
else if ( stage == CONTROL_STAGE_ACK )
{
if ( _dfu_ctx.flashing_in_progress )
{
_dfu_ctx.state = DFU_DNBUSY;
tud_dfu_download_cb(_dfu_ctx.alt, _dfu_ctx.block, _dfu_ctx.transfer_buf, _dfu_ctx.length);
}else
{
_dfu_ctx.state = DFU_DNLOAD_IDLE;
}
}
return true;
}
static bool process_manifest_get_status(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
{
if ( stage == CONTROL_STAGE_SETUP )
{
// only transition to next state on CONTROL_STAGE_ACK
dfu_state_t next_state;
uint32_t timeout;
if ( _dfu_ctx.flashing_in_progress )
{
next_state = DFU_MANIFEST;
timeout = tud_dfu_get_timeout_cb(_dfu_ctx.alt, next_state);
}
else
{
next_state = DFU_IDLE;
timeout = 0;
}
return reply_getstatus(rhport, request, next_state, _dfu_ctx.status, timeout);
}
else if ( stage == CONTROL_STAGE_ACK )
{
if ( _dfu_ctx.flashing_in_progress )
{
_dfu_ctx.state = DFU_MANIFEST;
tud_dfu_manifest_cb(_dfu_ctx.alt);
}
else
{
_dfu_ctx.state = DFU_IDLE;
}
}
return true;
}
static bool reply_getstatus(uint8_t rhport, tusb_control_request_t const * request, dfu_state_t state, dfu_status_t status, uint32_t timeout)
{
dfu_status_response_t resp;
resp.bStatus = (uint8_t) status;
resp.bwPollTimeout[0] = TU_U32_BYTE0(timeout);
resp.bwPollTimeout[1] = TU_U32_BYTE1(timeout);
resp.bwPollTimeout[2] = TU_U32_BYTE2(timeout);
resp.bState = (uint8_t) state;
resp.iString = 0;
return tud_control_xfer(rhport, request, &resp, sizeof(dfu_status_response_t));
}
#endif
+39 -23
View File
@@ -33,38 +33,54 @@
extern "C" {
#endif
//--------------------------------------------------------------------+
// Class Driver Default Configure & Validation
//--------------------------------------------------------------------+
#if !defined(CFG_TUD_DFU_XFER_BUFSIZE)
#error "CFG_TUD_DFU_XFER_BUFSIZE must be defined, it has to be set to the buffer size used in TUD_DFU_DESCRIPTOR"
#endif
//--------------------------------------------------------------------+
// Application API
//--------------------------------------------------------------------+
// Must be called when the application is done with flashing started by
// tud_dfu_download_cb() and tud_dfu_manifest_cb().
// status is DFU_STATUS_OK if successful, any other error status will cause state to enter dfuError
void tud_dfu_finish_flashing(uint8_t status);
//--------------------------------------------------------------------+
// Application Callback API (weak is optional)
//--------------------------------------------------------------------+
// Invoked during DFU_MANIFEST_SYNC get status request to check if firmware
// is valid
bool tud_dfu_firmware_valid_check_cb(void);
// Invoked when a DFU_DNLOAD request is received
// This callback takes the wBlockNum chunk of length length and provides it
// to the application at the data pointer. This data is only valid for this
// call, so the app must use it not or copy it.
void tud_dfu_req_dnload_data_cb(uint16_t wBlockNum, uint8_t* data, uint16_t length);
// Note: alt is used as the partition number, in order to support multiple partitions like FLASH, EEPROM, etc.
// Must be called when the application is done using the last block of data
// provided by tud_dfu_req_dnload_data_cb
void tud_dfu_dnload_complete(void);
// Invoked right before tud_dfu_download_cb() (state=DFU_DNBUSY) or tud_dfu_manifest_cb() (state=DFU_MANIFEST)
// Application return timeout in milliseconds (bwPollTimeout) for the next download/manifest operation.
// During this period, USB host won't try to communicate with us.
uint32_t tud_dfu_get_timeout_cb(uint8_t alt, uint8_t state);
// Invoked during the last DFU_DNLOAD request, signifying that the host believes
// it is done transmitting data.
// Return true if the application agrees there is no more data
// Return false if the device disagrees, which will stall the pipe, and the Host
// should initiate a recovery procedure
bool tud_dfu_device_data_done_check_cb(void);
// Invoked when received DFU_DNLOAD (wLength>0) following by DFU_GETSTATUS (state=DFU_DNBUSY) requests
// This callback could be returned before flashing op is complete (async).
// Once finished flashing, application must call tud_dfu_finish_flashing()
void tud_dfu_download_cb (uint8_t alt, uint16_t block_num, uint8_t const *data, uint16_t length);
// Invoked when download process is complete, received DFU_DNLOAD (wLength=0) following by DFU_GETSTATUS (state=Manifest)
// Application can do checksum, or actual flashing if buffered entire image previously.
// Once finished flashing, application must call tud_dfu_finish_flashing()
void tud_dfu_manifest_cb(uint8_t alt);
// Invoked when received DFU_UPLOAD request
// Application must populate data with up to length bytes and
// Return the number of written bytes
TU_ATTR_WEAK uint16_t tud_dfu_upload_cb(uint8_t alt, uint16_t block_num, uint8_t* data, uint16_t length);
// Invoked when a DFU_DETACH request is received
TU_ATTR_WEAK void tud_dfu_detach_cb(void);
// Invoked when the Host has terminated a download or upload transfer
TU_ATTR_WEAK void tud_dfu_abort_cb(void);
// Invoked when a DFU_UPLOAD request is received
// This callback must populate data with up to length bytes
// Return the number of bytes to write
uint16_t tud_dfu_req_upload_data_cb(uint16_t block_num, uint8_t* data, uint16_t length);
TU_ATTR_WEAK void tud_dfu_abort_cb(uint8_t alt);
//--------------------------------------------------------------------+
// Internal Class Driver API
+3 -3
View File
@@ -108,10 +108,10 @@ bool dfu_rtd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request
case DFU_REQUEST_GETSTATUS:
{
TU_LOG2(" DFU RT Request: GETSTATUS\r\n");
dfu_status_req_payload_t resp;
dfu_status_response_t resp;
// Status = OK, Poll timeout is ignored during RT, State = APP_IDLE, IString = 0
memset(&resp, 0x00, sizeof(dfu_status_req_payload_t));
tud_control_xfer(rhport, request, &resp, sizeof(dfu_status_req_payload_t));
memset(&resp, 0x00, sizeof(dfu_status_response_t));
tud_control_xfer(rhport, request, &resp, sizeof(dfu_status_response_t));
}
break;
+2 -2
View File
@@ -871,10 +871,10 @@ enum
{0, 0 }, /* 0x07 */ \
{0, HID_KEY_BACKSPACE }, /* 0x08 Backspace */ \
{0, HID_KEY_TAB }, /* 0x09 Tab */ \
{0, HID_KEY_RETURN }, /* 0x0A Line Feed */ \
{0, HID_KEY_ENTER }, /* 0x0A Line Feed */ \
{0, 0 }, /* 0x0B */ \
{0, 0 }, /* 0x0C */ \
{0, HID_KEY_RETURN }, /* 0x0D CR */ \
{0, HID_KEY_ENTER }, /* 0x0D CR */ \
{0, 0 }, /* 0x0E */ \
{0, 0 }, /* 0x0F */ \
{0, 0 }, /* 0x10 */ \
+27 -2
View File
@@ -121,6 +121,7 @@ bool tud_hid_n_keyboard_report(uint8_t instance, uint8_t report_id, uint8_t modi
hid_keyboard_report_t report;
report.modifier = modifier;
report.reserved = 0;
if ( keycode )
{
@@ -280,7 +281,21 @@ bool hidd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t
uint8_t const report_type = tu_u16_high(request->wValue);
uint8_t const report_id = tu_u16_low(request->wValue);
uint16_t xferlen = tud_hid_get_report_cb(hid_itf, report_id, (hid_report_type_t) report_type, p_hid->epin_buf, request->wLength);
uint8_t* report_buf = p_hid->epin_buf;
uint16_t req_len = request->wLength;
uint16_t xferlen = 0;
// If host request a specific Report ID, add ID to as 1 byte of response
if ( (report_id != HID_REPORT_TYPE_INVALID) && (req_len > 1) )
{
*report_buf++ = report_id;
req_len--;
xferlen++;
}
xferlen += tud_hid_get_report_cb(hid_itf, report_id, (hid_report_type_t) report_type, report_buf, req_len);
TU_ASSERT( xferlen > 0 );
tud_control_xfer(rhport, request, p_hid->epin_buf, xferlen);
@@ -298,7 +313,17 @@ bool hidd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t
uint8_t const report_type = tu_u16_high(request->wValue);
uint8_t const report_id = tu_u16_low(request->wValue);
tud_hid_set_report_cb(hid_itf, report_id, (hid_report_type_t) report_type, p_hid->epout_buf, request->wLength);
uint8_t const* report_buf = p_hid->epout_buf;
uint16_t report_len = request->wLength;
// If host request a specific Report ID, extract report ID in buffer before invoking callback
if ( (report_id != HID_REPORT_TYPE_INVALID) && (report_len > 1) && (report_id == report_buf[0]) )
{
report_buf++;
report_len--;
}
tud_hid_set_report_cb(hid_itf, report_id, (hid_report_type_t) report_type, report_buf, report_len);
}
break;
+12 -12
View File
@@ -195,16 +195,7 @@ static inline bool tud_hid_gamepad_report(uint8_t report_id, int8_t x, int8_t y
HID_REPORT_COUNT ( 1 ) ,\
HID_REPORT_SIZE ( 8 ) ,\
HID_INPUT ( HID_CONSTANT ) ,\
/* 6-byte Keycodes */ \
HID_USAGE_PAGE ( HID_USAGE_PAGE_KEYBOARD ) ,\
HID_USAGE_MIN ( 0 ) ,\
HID_USAGE_MAX_N ( 255, 2 ) ,\
HID_LOGICAL_MIN ( 0 ) ,\
HID_LOGICAL_MAX_N( 255, 2 ) ,\
HID_REPORT_COUNT ( 6 ) ,\
HID_REPORT_SIZE ( 8 ) ,\
HID_INPUT ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ) ,\
/* 5-bit LED Indicator Kana | Compose | ScrollLock | CapsLock | NumLock */ \
/* Output 5-bit LED Indicator Kana | Compose | ScrollLock | CapsLock | NumLock */ \
HID_USAGE_PAGE ( HID_USAGE_PAGE_LED ) ,\
HID_USAGE_MIN ( 1 ) ,\
HID_USAGE_MAX ( 5 ) ,\
@@ -215,6 +206,15 @@ static inline bool tud_hid_gamepad_report(uint8_t report_id, int8_t x, int8_t y
HID_REPORT_COUNT ( 1 ) ,\
HID_REPORT_SIZE ( 3 ) ,\
HID_OUTPUT ( HID_CONSTANT ) ,\
/* 6-byte Keycodes */ \
HID_USAGE_PAGE ( HID_USAGE_PAGE_KEYBOARD ) ,\
HID_USAGE_MIN ( 0 ) ,\
HID_USAGE_MAX_N ( 255, 2 ) ,\
HID_LOGICAL_MIN ( 0 ) ,\
HID_LOGICAL_MAX_N( 255, 2 ) ,\
HID_REPORT_COUNT ( 6 ) ,\
HID_REPORT_SIZE ( 8 ) ,\
HID_INPUT ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ) ,\
HID_COLLECTION_END \
// Mouse Report Descriptor Template
@@ -364,14 +364,14 @@ static inline bool tud_hid_gamepad_report(uint8_t report_id, int8_t x, int8_t y
/* Input */ \
HID_USAGE ( 0x02 ),\
HID_LOGICAL_MIN ( 0x00 ),\
HID_LOGICAL_MAX ( 0xff ),\
HID_LOGICAL_MAX_N ( 0xff, 2 ),\
HID_REPORT_SIZE ( 8 ),\
HID_REPORT_COUNT( report_size ),\
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
/* Output */ \
HID_USAGE ( 0x03 ),\
HID_LOGICAL_MIN ( 0x00 ),\
HID_LOGICAL_MAX ( 0xff ),\
HID_LOGICAL_MAX_N ( 0xff, 2 ),\
HID_REPORT_SIZE ( 8 ),\
HID_REPORT_COUNT( report_size ),\
HID_OUTPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
+101 -72
View File
@@ -37,16 +37,6 @@
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
/*
"KEYBOARD" : in_len=8 , out_len=1, usage_page=0x01, usage=0x06 # Generic Desktop, Keyboard
"MOUSE" : in_len=4 , out_len=0, usage_page=0x01, usage=0x02 # Generic Desktop, Mouse
"CONSUMER" : in_len=2 , out_len=0, usage_page=0x0C, usage=0x01 # Consumer, Consumer Control
"SYS_CONTROL" : in_len=1 , out_len=0, usage_page=0x01, usage=0x80 # Generic Desktop, Sys Control
"GAMEPAD" : in_len=6 , out_len=0, usage_page=0x01, usage=0x05 # Generic Desktop, Game Pad
"DIGITIZER" : in_len=5 , out_len=0, usage_page=0x0D, usage=0x02 # Digitizers, Pen
"XAC_COMPATIBLE_GAMEPAD" : in_len=3 , out_len=0, usage_page=0x01, usage=0x05 # Generic Desktop, Game Pad
"RAW" : in_len=64, out_len=0, usage_page=0xFFAF, usage=0xAF # Vendor 0xFFAF "Adafruit", 0xAF
*/
typedef struct
{
uint8_t itf_num;
@@ -62,8 +52,8 @@ typedef struct
uint16_t epin_size;
uint16_t epout_size;
uint8_t epin_buf[CFG_TUH_HID_EP_BUFSIZE];
uint8_t epout_buf[CFG_TUH_HID_EP_BUFSIZE];
uint8_t epin_buf[CFG_TUH_HID_EPIN_BUFSIZE];
uint8_t epout_buf[CFG_TUH_HID_EPOUT_BUFSIZE];
} hidh_interface_t;
typedef struct
@@ -72,7 +62,7 @@ typedef struct
hidh_interface_t instances[CFG_TUH_HID];
} hidh_device_t;
static hidh_device_t _hidh_dev[CFG_TUSB_HOST_DEVICE_MAX-1];
static hidh_device_t _hidh_dev[CFG_TUH_DEVICE_MAX];
//------------- Internal prototypes -------------//
@@ -82,13 +72,8 @@ TU_ATTR_ALWAYS_INLINE static inline hidh_interface_t* get_instance(uint8_t dev_a
static uint8_t get_instance_id_by_itfnum(uint8_t dev_addr, uint8_t itf);
static uint8_t get_instance_id_by_epaddr(uint8_t dev_addr, uint8_t ep_addr);
TU_ATTR_ALWAYS_INLINE static inline bool hidh_get_report(uint8_t dev_addr, hidh_interface_t* hid_itf)
{
return usbh_edpt_xfer(dev_addr, hid_itf->ep_in, hid_itf->epin_buf, hid_itf->epin_size);
}
//--------------------------------------------------------------------+
// Application API
// Interface API
//--------------------------------------------------------------------+
uint8_t tuh_hid_instance_count(uint8_t dev_addr)
@@ -108,7 +93,11 @@ uint8_t tuh_hid_interface_protocol(uint8_t dev_addr, uint8_t instance)
return hid_itf->itf_protocol;
}
bool tuh_hid_get_protocol(uint8_t dev_addr, uint8_t instance)
//--------------------------------------------------------------------+
// Control Endpoint API
//--------------------------------------------------------------------+
uint8_t tuh_hid_get_protocol(uint8_t dev_addr, uint8_t instance)
{
hidh_interface_t* hid_itf = get_instance(dev_addr, instance);
return hid_itf->protocol_mode;
@@ -196,6 +185,20 @@ bool tuh_hid_set_report(uint8_t dev_addr, uint8_t instance, uint8_t report_id, u
return true;
}
//--------------------------------------------------------------------+
// Interrupt Endpoint API
//--------------------------------------------------------------------+
bool tuh_hid_receive_report(uint8_t dev_addr, uint8_t instance)
{
hidh_interface_t* hid_itf = get_instance(dev_addr, instance);
// claim endpoint
TU_VERIFY( usbh_edpt_claim(dev_addr, hid_itf->ep_in) );
return usbh_edpt_xfer(dev_addr, hid_itf->ep_in, hid_itf->epin_buf, hid_itf->epin_size);
}
//bool tuh_n_hid_n_ready(uint8_t dev_addr, uint8_t instance)
//{
// TU_VERIFY(tuh_n_hid_n_mounted(dev_addr, instance));
@@ -225,11 +228,8 @@ bool hidh_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint3
if ( dir == TUSB_DIR_IN )
{
TU_LOG2(" Get Report callback (%u, %u)\r\n", dev_addr, instance);
TU_LOG1_MEM(hid_itf->epin_buf, 8, 2);
TU_LOG3_MEM(hid_itf->epin_buf, xferred_bytes, 2);
tuh_hid_report_received_cb(dev_addr, instance, hid_itf->epin_buf, xferred_bytes);
// queue next report
hidh_get_report(dev_addr, hid_itf);
}else
{
if (tuh_hid_report_sent_cb) tuh_hid_report_sent_cb(dev_addr, instance, hid_itf->epout_buf, xferred_bytes);
@@ -240,10 +240,13 @@ bool hidh_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint3
void hidh_close(uint8_t dev_addr)
{
TU_VERIFY(dev_addr <= CFG_TUH_DEVICE_MAX, );
hidh_device_t* hid_dev = get_dev(dev_addr);
if (tuh_hid_umount_cb)
{
for ( uint8_t inst = 0; inst < hid_dev->inst_count; inst++ ) tuh_hid_umount_cb(dev_addr, inst);
for (uint8_t inst = 0; inst < hid_dev->inst_count; inst++ ) tuh_hid_umount_cb(dev_addr, inst);
}
tu_memclr(hid_dev, sizeof(hidh_device_t));
@@ -253,14 +256,24 @@ void hidh_close(uint8_t dev_addr)
// Enumeration
//--------------------------------------------------------------------+
static bool config_get_protocol (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
static bool config_set_protocol (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
static bool config_get_report_desc (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
static bool config_get_report_desc_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
bool hidh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t *p_length)
static void config_driver_mount_complete(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len);
bool hidh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len)
{
(void) max_len;
TU_VERIFY(TUSB_CLASS_HID == desc_itf->bInterfaceClass);
TU_LOG2("HID opening Interface %u (addr = %u)\r\n", desc_itf->bInterfaceNumber, dev_addr);
// len = interface + hid + n*endpoints
uint16_t const drv_len = sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) + desc_itf->bNumEndpoints*sizeof(tusb_desc_endpoint_t);
TU_ASSERT(max_len >= drv_len);
uint8_t const *p_desc = (uint8_t const *) desc_itf;
//------------- HID descriptor -------------//
@@ -271,7 +284,7 @@ bool hidh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *de
// not enough interface, try to increase CFG_TUH_HID
// TODO multiple devices
hidh_device_t* hid_dev = get_dev(dev_addr);
TU_ASSERT(hid_dev->inst_count < CFG_TUH_HID);
TU_ASSERT(hid_dev->inst_count < CFG_TUH_HID, 0);
//------------- Endpoint Descriptor -------------//
p_desc = tu_desc_next(p_desc);
@@ -292,11 +305,10 @@ bool hidh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *de
hid_itf->report_desc_type = desc_hid->bReportType;
hid_itf->report_desc_len = tu_unaligned_read16(&desc_hid->wReportLength);
hid_itf->protocol_mode = HID_PROTOCOL_REPORT; // Per Specs: default is report mode
// Per HID Specs: default is Report protocol, though we will force Boot protocol when set_config
hid_itf->protocol_mode = HID_PROTOCOL_BOOT;
if ( HID_SUBCLASS_BOOT == desc_itf->bInterfaceSubClass ) hid_itf->itf_protocol = desc_itf->bInterfaceProtocol;
*p_length = sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) + desc_itf->bNumEndpoints*sizeof(tusb_desc_endpoint_t);
return true;
}
@@ -324,68 +336,82 @@ bool hidh_set_config(uint8_t dev_addr, uint8_t itf_num)
.wLength = 0
};
TU_ASSERT( tuh_control_xfer(dev_addr, &request, NULL, (hid_itf->itf_protocol != HID_ITF_PROTOCOL_NONE) ? config_get_protocol : config_get_report_desc) );
TU_ASSERT( tuh_control_xfer(dev_addr, &request, NULL, (hid_itf->itf_protocol != HID_ITF_PROTOCOL_NONE) ? config_set_protocol : config_get_report_desc) );
return true;
}
static bool config_get_protocol(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
// Force device to work in BOOT protocol
static bool config_set_protocol(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
{
// Stall is a valid response for SET_IDLE GET_PROTOCOL, therefore we could ignore its result
// Stall is a valid response for SET_IDLE, therefore we could ignore its result
(void) result;
uint8_t const itf_num = (uint8_t) request->wIndex;
uint8_t const instance = get_instance_id_by_itfnum(dev_addr, itf_num);
hidh_interface_t* hid_itf = get_instance(dev_addr, instance);
TU_LOG2("HID Get Protocol\r\n");
TU_LOG2("HID Set Protocol to Boot Mode\r\n");
hid_itf->protocol_mode = HID_PROTOCOL_BOOT;
tusb_control_request_t const new_request =
{
.bmRequestType_bit =
{
.recipient = TUSB_REQ_RCPT_INTERFACE,
.type = TUSB_REQ_TYPE_CLASS,
.direction = TUSB_DIR_IN
.direction = TUSB_DIR_OUT
},
.bRequest = HID_REQ_CONTROL_GET_PROTOCOL,
.wValue = 0,
.bRequest = HID_REQ_CONTROL_SET_PROTOCOL,
.wValue = HID_PROTOCOL_BOOT,
.wIndex = hid_itf->itf_num,
.wLength = 1
.wLength = 0
};
TU_ASSERT( tuh_control_xfer(dev_addr, &new_request, &hid_itf->protocol_mode, config_get_report_desc) );
return false;
TU_ASSERT( tuh_control_xfer(dev_addr, &new_request, NULL, config_get_report_desc) );
return true;
}
static bool config_get_report_desc(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
{
// Stall is a valid response for SET_IDLE GET_PROTOCOL, therefore we could ignore its result
(void) result;
// We can be here after SET_IDLE or SET_PROTOCOL (boot device)
// Trigger assert if result is not successful with set protocol
if ( request->bRequest != HID_REQ_CONTROL_SET_IDLE )
{
TU_ASSERT(result == XFER_RESULT_SUCCESS);
}
uint8_t const itf_num = (uint8_t) request->wIndex;
uint8_t const instance = get_instance_id_by_itfnum(dev_addr, itf_num);
hidh_interface_t* hid_itf = get_instance(dev_addr, instance);
// Get Report Descriptor
// Get Report Descriptor if possible
// using usbh enumeration buffer since report descriptor can be very long
TU_ASSERT( hid_itf->report_desc_len <= CFG_TUH_ENUMERATION_BUFSZIE );
TU_LOG2("HID Get Report Descriptor\r\n");
tusb_control_request_t const new_request =
if( hid_itf->report_desc_len > CFG_TUH_ENUMERATION_BUFSIZE )
{
.bmRequestType_bit =
{
.recipient = TUSB_REQ_RCPT_INTERFACE,
.type = TUSB_REQ_TYPE_STANDARD,
.direction = TUSB_DIR_IN
},
.bRequest = TUSB_REQ_GET_DESCRIPTOR,
.wValue = tu_u16(hid_itf->report_desc_type, 0),
.wIndex = itf_num,
.wLength = hid_itf->report_desc_len
};
TU_LOG2("HID Skip Report Descriptor since it is too large %u bytes\r\n", hid_itf->report_desc_len);
// Driver is mounted without report descriptor
config_driver_mount_complete(dev_addr, instance, NULL, 0);
}else
{
TU_LOG2("HID Get Report Descriptor\r\n");
tusb_control_request_t const new_request =
{
.bmRequestType_bit =
{
.recipient = TUSB_REQ_RCPT_INTERFACE,
.type = TUSB_REQ_TYPE_STANDARD,
.direction = TUSB_DIR_IN
},
.bRequest = TUSB_REQ_GET_DESCRIPTOR,
.wValue = tu_u16(hid_itf->report_desc_type, 0),
.wIndex = itf_num,
.wLength = hid_itf->report_desc_len
};
TU_ASSERT(tuh_control_xfer(dev_addr, &new_request, usbh_get_enum_buf(), config_get_report_desc_complete));
}
TU_ASSERT(tuh_control_xfer(dev_addr, &new_request, usbh_get_enum_buf(), config_get_report_desc_complete));
return true;
}
@@ -393,23 +419,26 @@ static bool config_get_report_desc_complete(uint8_t dev_addr, tusb_control_reque
{
TU_ASSERT(XFER_RESULT_SUCCESS == result);
uint8_t const itf_num = (uint8_t) request->wIndex;
uint8_t const instance = get_instance_id_by_itfnum(dev_addr, itf_num);
hidh_interface_t* hid_itf = get_instance(dev_addr, instance);
uint8_t const itf_num = (uint8_t) request->wIndex;
uint8_t const instance = get_instance_id_by_itfnum(dev_addr, itf_num);
uint8_t const* desc_report = usbh_get_enum_buf();
uint16_t const desc_len = request->wLength;
config_driver_mount_complete(dev_addr, instance, desc_report, desc_len);
return true;
}
static void config_driver_mount_complete(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len)
{
hidh_interface_t* hid_itf = get_instance(dev_addr, instance);
// enumeration is complete
tuh_hid_mount_cb(dev_addr, instance, desc_report, desc_len);
// queue transfer for IN endpoint
hidh_get_report(dev_addr, hid_itf);
// notify usbh that driver enumeration is complete
usbh_driver_set_config_complete(dev_addr, itf_num);
return true;
usbh_driver_set_config_complete(dev_addr, hid_itf->itf_num);
}
//--------------------------------------------------------------------+
@@ -452,9 +481,9 @@ uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* report_info_arr,
uint8_t const data8 = desc_report[0];
TU_LOG2("tag = %d, type = %d, size = %d, data = ", tag, type, size);
for(uint32_t i=0; i<size; i++) TU_LOG2("%02X ", desc_report[i]);
TU_LOG2("\r\n");
TU_LOG(3, "tag = %d, type = %d, size = %d, data = ", tag, type, size);
for(uint32_t i=0; i<size; i++) TU_LOG(3, "%02X ", desc_report[i]);
TU_LOG(3, "\r\n");
switch(type)
{
+35 -14
View File
@@ -38,10 +38,15 @@
//--------------------------------------------------------------------+
// TODO Highspeed interrupt can be up to 512 bytes
#ifndef CFG_TUH_HID_EP_BUFSIZE
#define CFG_TUH_HID_EP_BUFSIZE 64
#ifndef CFG_TUH_HID_EPIN_BUFSIZE
#define CFG_TUH_HID_EPIN_BUFSIZE 64
#endif
#ifndef CFG_TUH_HID_EPOUT_BUFSIZE
#define CFG_TUH_HID_EPOUT_BUFSIZE 64
#endif
typedef struct
{
uint8_t report_id;
@@ -54,7 +59,7 @@ typedef struct
} tuh_hid_report_info_t;
//--------------------------------------------------------------------+
// Application API
// Interface API
//--------------------------------------------------------------------+
// Get the number of HID instances
@@ -66,9 +71,18 @@ bool tuh_hid_mounted(uint8_t dev_addr, uint8_t instance);
// Get interface supported protocol (bInterfaceProtocol) check out hid_interface_protocol_enum_t for possible values
uint8_t tuh_hid_interface_protocol(uint8_t dev_addr, uint8_t instance);
// Get current active protocol: HID_PROTOCOL_BOOT (0) or HID_PROTOCOL_REPORT (1)
// Note: as HID spec, device will be initialized in Report mode
bool tuh_hid_get_protocol(uint8_t dev_addr, uint8_t instance);
// Parse report descriptor into array of report_info struct and return number of reports.
// For complicated report, application should write its own parser.
uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* reports_info_arr, uint8_t arr_count, uint8_t const* desc_report, uint16_t desc_len) TU_ATTR_UNUSED;
//--------------------------------------------------------------------+
// Control Endpoint API
//--------------------------------------------------------------------+
// Get current protocol: HID_PROTOCOL_BOOT (0) or HID_PROTOCOL_REPORT (1)
// Note: Device will be initialized in Boot protocol for simplicity.
// Application can use set_protocol() to switch back to Report protocol.
uint8_t tuh_hid_get_protocol(uint8_t dev_addr, uint8_t instance);
// 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)
@@ -78,13 +92,18 @@ bool tuh_hid_set_protocol(uint8_t dev_addr, uint8_t instance, uint8_t protocol);
// report_type is either Intput, Output or Feature, (value from hid_report_type_t)
bool tuh_hid_set_report(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, void* report, uint16_t len);
// Parse report descriptor into array of report_info struct and return number of reports.
// For complicated report, application should write its own parser.
uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* reports_info_arr, uint8_t arr_count, uint8_t const* desc_report, uint16_t desc_len) TU_ATTR_UNUSED;
//--------------------------------------------------------------------+
// Interrupt Endpoint API
//--------------------------------------------------------------------+
// Check if the interface is ready to use
//bool tuh_n_hid_n_ready(uint8_t dev_addr, uint8_t instance);
// Try to receive next report on Interrupt Endpoint. Immediately return
// - true If succeeded, tuh_hid_report_received_cb() callback will be invoked when report is available
// - false if failed to queue the transfer e.g endpoint is busy
bool tuh_hid_receive_report(uint8_t dev_addr, uint8_t instance);
// Send report using interrupt endpoint
// If report_id > 0 (composite), it will be sent as 1st byte, then report contents. Otherwise only report content is sent.
//void tuh_hid_send_report(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t const* report, uint16_t len);
@@ -96,6 +115,8 @@ uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* reports_info_arr,
// Invoked when device with hid interface is mounted
// Report descriptor is also available for use. tuh_hid_parse_report_descriptor()
// can be used to parse common/simple enough descriptor.
// Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE, it will be skipped
// therefore report_desc = NULL, desc_len = 0
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report_desc, uint16_t desc_len);
// Invoked when device with hid interface is un-mounted
@@ -118,11 +139,11 @@ TU_ATTR_WEAK void tuh_hid_set_protocol_complete_cb(uint8_t dev_addr, uint8_t ins
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
void hidh_init(void);
bool hidh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t *p_length);
bool hidh_set_config(uint8_t dev_addr, uint8_t itf_num);
bool hidh_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
void hidh_close(uint8_t dev_addr);
void hidh_init (void);
bool hidh_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len);
bool hidh_set_config (uint8_t dev_addr, uint8_t itf_num);
bool hidh_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
void hidh_close (uint8_t dev_addr);
#ifdef __cplusplus
}
+22 -21
View File
@@ -122,7 +122,12 @@ static void _prep_out_transaction (midid_interface_t* p_midi)
uint32_t tud_midi_n_available(uint8_t itf, uint8_t cable_num)
{
(void) cable_num;
return tu_fifo_count(&_midid_itf[itf].rx_ff);
midid_interface_t* midi = &_midid_itf[itf];
midid_stream_t const* stream = &midi->stream_read;
// when using with packet API stream total & index are both zero
return tu_fifo_count(&midi->rx_ff) + (stream->total - stream->index);
}
uint32_t tud_midi_n_stream_read(uint8_t itf, uint8_t cable_num, void* buffer, uint32_t bufsize)
@@ -197,9 +202,11 @@ uint32_t tud_midi_n_stream_read(uint8_t itf, uint8_t cable_num, void* buffer, ui
bool tud_midi_n_packet_read (uint8_t itf, uint8_t packet[4])
{
midid_interface_t* p_midi = &_midid_itf[itf];
uint32_t num_read = tu_fifo_read_n(&p_midi->rx_ff, packet, 4);
_prep_out_transaction(p_midi);
midid_interface_t* midi = &_midid_itf[itf];
TU_VERIFY(midi->ep_out);
uint32_t const num_read = tu_fifo_read_n(&midi->rx_ff, packet, 4);
_prep_out_transaction(midi);
return (num_read == 4);
}
@@ -234,19 +241,19 @@ static uint32_t write_flush(midid_interface_t* midi)
uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, uint8_t const* buffer, uint32_t bufsize)
{
midid_interface_t* midi = &_midid_itf[itf];
TU_VERIFY(midi->itf_num, 0);
TU_VERIFY(midi->ep_in, 0);
midid_stream_t* stream = &midi->stream_write;
uint32_t total_written = 0;
uint32_t i = 0;
while ( i < bufsize )
while ( (i < bufsize) && (tu_fifo_remaining(&midi->tx_ff) >= 4) )
{
uint8_t const data = buffer[i];
i++;
if ( stream->index == 0 )
{
// new event packet
//------------- New event packet -------------//
uint8_t const msg = data >> 4;
@@ -308,9 +315,9 @@ uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, uint8_t const*
}
else
{
// On-going (buffering) packet
//------------- On-going (buffering) packet -------------//
TU_ASSERT(stream->index < 4, total_written);
TU_ASSERT(stream->index < 4, i);
stream->buffer[stream->index] = data;
stream->index++;
@@ -333,27 +340,20 @@ uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, uint8_t const*
// complete current event packet, reset stream
stream->index = stream->total = 0;
// fifo overflow, here we assume FIFO is multiple of 4 and didn't check remaining before writing
if ( count != 4 ) break;
// updated written if succeeded
total_written = i;
// FIFO overflown, since we already check fifo remaining. It is probably race condition
TU_ASSERT(count == 4, i);
}
i++;
}
write_flush(midi);
return total_written;
return i;
}
bool tud_midi_n_packet_write (uint8_t itf, uint8_t const packet[4])
{
midid_interface_t* midi = &_midid_itf[itf];
if (midi->itf_num == 0) {
return 0;
}
TU_VERIFY(midi->ep_in);
if (tu_fifo_remaining(&midi->tx_ff) < 4) return false;
@@ -435,6 +435,7 @@ uint16_t midid_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint
}
p_midi->itf_num = desc_midi->bInterfaceNumber;
(void) p_midi->itf_num;
// next descriptor
drv_len += tu_desc_len(p_desc);
+405 -172
View File
@@ -37,12 +37,17 @@
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
// Can be selectively disabled to reduce logging when troubleshooting other driver
#define MSC_DEBUG 2
enum
{
MSC_STAGE_CMD = 0,
MSC_STAGE_DATA,
MSC_STAGE_STATUS,
MSC_STAGE_STATUS_SENT
MSC_STAGE_STATUS_SENT,
MSC_STAGE_NEED_RESET,
};
typedef struct
@@ -57,7 +62,7 @@ typedef struct
// Bulk Only Transfer (BOT) Protocol
uint8_t stage;
uint32_t total_len;
uint32_t total_len; // byte to be transferred, can be smaller than total_bytes in cbw
uint32_t xferred_len; // numbered of bytes transferred so far in the Data Stage
// Sense Response Data
@@ -74,7 +79,55 @@ CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t _mscd_buf[CFG_TUD_MSC_EP_
//--------------------------------------------------------------------+
static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_t* buffer, uint32_t bufsize);
static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc);
static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc);
static void proc_write10_new_data(uint8_t rhport, mscd_interface_t* p_msc, uint32_t xferred_bytes);
TU_ATTR_ALWAYS_INLINE static inline bool is_data_in(uint8_t dir)
{
return tu_bit_test(dir, 7);
}
static inline bool send_csw(uint8_t rhport, mscd_interface_t* p_msc)
{
// Data residue is always = host expect - actual transferred
p_msc->csw.data_residue = p_msc->cbw.total_bytes - p_msc->xferred_len;
p_msc->stage = MSC_STAGE_STATUS_SENT;
return usbd_edpt_xfer(rhport, p_msc->ep_in , (uint8_t*) &p_msc->csw, sizeof(msc_csw_t));
}
static inline bool prepare_cbw(uint8_t rhport, mscd_interface_t* p_msc)
{
p_msc->stage = MSC_STAGE_CMD;
return usbd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t));
}
static void fail_scsi_op(uint8_t rhport, mscd_interface_t* p_msc, uint8_t status)
{
msc_cbw_t const * p_cbw = &p_msc->cbw;
msc_csw_t * p_csw = &p_msc->csw;
p_csw->status = status;
p_csw->data_residue = p_msc->cbw.total_bytes - p_msc->xferred_len;
p_msc->stage = MSC_STAGE_STATUS;
// failed but sense key is not set: default to Illegal Request
if ( p_msc->sense_key == 0 ) tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
// If there is data stage and not yet complete, stall it
if ( p_cbw->total_bytes && p_csw->data_residue )
{
if ( is_data_in(p_cbw->dir) )
{
usbd_edpt_stall(rhport, p_msc->ep_in);
}
else
{
usbd_edpt_stall(rhport, p_msc->ep_out);
}
}
}
static inline uint32_t rdwr10_get_lba(uint8_t const command[])
{
@@ -85,21 +138,66 @@ static inline uint32_t rdwr10_get_lba(uint8_t const command[])
return tu_ntohl(lba);
}
static inline uint16_t rdwr10_get_blockcount(uint8_t const command[])
static inline uint16_t rdwr10_get_blockcount(msc_cbw_t const* cbw)
{
// use offsetof to avoid pointer to the odd/misaligned address
uint16_t const block_count = tu_unaligned_read16(command + offsetof(scsi_write10_t, block_count));
// block count is in Big Endian
uint16_t const block_count = tu_unaligned_read16(cbw->command + offsetof(scsi_write10_t, block_count));
return tu_ntohs(block_count);
}
static inline uint16_t rdwr10_get_blocksize(msc_cbw_t const* cbw)
{
// first extract block count in the command
uint16_t const block_count = rdwr10_get_blockcount(cbw);
// invalid block count
if (block_count == 0) return 0;
return cbw->total_bytes / block_count;
}
uint8_t rdwr10_validate_cmd(msc_cbw_t const* cbw)
{
uint8_t status = MSC_CSW_STATUS_PASSED;
uint16_t const block_count = rdwr10_get_blockcount(cbw);
if ( cbw->total_bytes == 0 )
{
if ( block_count )
{
TU_LOG(MSC_DEBUG, " SCSI case 2 (Hn < Di) or case 3 (Hn < Do) \r\n");
status = MSC_CSW_STATUS_PHASE_ERROR;
}else
{
// no data transfer, only exist in complaint test suite
}
}else
{
if ( SCSI_CMD_READ_10 == cbw->command[0] && !is_data_in(cbw->dir) )
{
TU_LOG(MSC_DEBUG, " SCSI case 10 (Ho <> Di)\r\n");
status = MSC_CSW_STATUS_PHASE_ERROR;
}
else if ( SCSI_CMD_WRITE_10 == cbw->command[0] && is_data_in(cbw->dir) )
{
TU_LOG(MSC_DEBUG, " SCSI case 8 (Hi <> Do)\r\n");
status = MSC_CSW_STATUS_PHASE_ERROR;
}
else if ( !block_count )
{
TU_LOG(MSC_DEBUG, " SCSI case 4 Hi > Dn\r\n");
status = MSC_CSW_STATUS_FAILED;
}
}
return status;
}
//--------------------------------------------------------------------+
// Debug
//--------------------------------------------------------------------+
#if CFG_TUSB_DEBUG >= 2
static tu_lookup_entry_t const _msc_scsi_cmd_lookup[] =
TU_ATTR_UNUSED static tu_lookup_entry_t const _msc_scsi_cmd_lookup[] =
{
{ .key = SCSI_CMD_TEST_UNIT_READY , .data = "Test Unit Ready" },
{ .key = SCSI_CMD_INQUIRY , .data = "Inquiry" },
@@ -114,7 +212,7 @@ static tu_lookup_entry_t const _msc_scsi_cmd_lookup[] =
{ .key = SCSI_CMD_WRITE_10 , .data = "Write10" }
};
static tu_lookup_table_t const _msc_scsi_cmd_table =
TU_ATTR_UNUSED static tu_lookup_table_t const _msc_scsi_cmd_table =
{
.count = TU_ARRAY_SIZE(_msc_scsi_cmd_lookup),
.items = _msc_scsi_cmd_lookup
@@ -170,35 +268,93 @@ uint16_t mscd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1
TU_ASSERT( usbd_open_edpt_pair(rhport, tu_desc_next(itf_desc), 2, TUSB_XFER_BULK, &p_msc->ep_out, &p_msc->ep_in), 0 );
// Prepare for Command Block Wrapper
if ( !usbd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)) )
{
TU_LOG_FAILED();
TU_BREAKPOINT();
}
TU_ASSERT( prepare_cbw(rhport, p_msc), drv_len);
return drv_len;
}
static void proc_bot_reset(mscd_interface_t* p_msc)
{
p_msc->stage = MSC_STAGE_CMD;
p_msc->total_len = 0;
p_msc->xferred_len = 0;
p_msc->sense_key = 0;
p_msc->add_sense_code = 0;
p_msc->add_sense_qualifier = 0;
}
// Invoked when a control transfer occurred on an interface of this class
// Driver response accordingly to the request and the transfer stage (setup/data/ack)
// return false to stall control endpoint (e.g unsupported request)
bool mscd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * p_request)
bool mscd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
{
// nothing to do with DATA & ACK stage
if (stage != CONTROL_STAGE_SETUP) return true;
// Handle class request only
TU_VERIFY(p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
mscd_interface_t* p_msc = &_mscd_itf;
switch ( p_request->bRequest )
// Clear Endpoint Feature (stall) for recovery
if ( TUSB_REQ_TYPE_STANDARD == request->bmRequestType_bit.type &&
TUSB_REQ_RCPT_ENDPOINT == request->bmRequestType_bit.recipient &&
TUSB_REQ_CLEAR_FEATURE == request->bRequest &&
TUSB_REQ_FEATURE_EDPT_HALT == request->wValue )
{
uint8_t const ep_addr = tu_u16_low(request->wIndex);
if ( p_msc->stage == MSC_STAGE_NEED_RESET )
{
// reset recovery is required to recover from this stage
// Clear Stall request cannot resolve this -> continue to stall endpoint
usbd_edpt_stall(rhport, ep_addr);
}
else
{
if ( ep_addr == p_msc->ep_in )
{
if ( p_msc->stage == MSC_STAGE_STATUS )
{
// resume sending SCSI status if we are in this stage previously before stalled
TU_ASSERT( send_csw(rhport, p_msc) );
}
}
else if ( ep_addr == p_msc->ep_out )
{
if ( p_msc->stage == MSC_STAGE_CMD )
{
// part of reset recovery (probably due to invalid CBW) -> prepare for new command
// Note: skip if already queued previously
if ( usbd_edpt_ready(rhport, p_msc->ep_out) )
{
TU_ASSERT( prepare_cbw(rhport, p_msc) );
}
}
}
}
return true;
}
// From this point only handle class request only
TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
switch ( request->bRequest )
{
case MSC_REQ_RESET:
// TODO: Actually reset interface.
tud_control_status(rhport, p_request);
TU_LOG(MSC_DEBUG, " MSC BOT Reset\r\n");
TU_VERIFY(request->wValue == 0 && request->wLength == 0);
// driver state reset
proc_bot_reset(p_msc);
tud_control_status(rhport, request);
break;
case MSC_REQ_GET_MAX_LUN:
{
TU_LOG(MSC_DEBUG, " MSC Get Max Lun\r\n");
TU_VERIFY(request->wValue == 0 && request->wLength == 1);
uint8_t maxlun = 1;
if (tud_msc_get_maxlun_cb) maxlun = tud_msc_get_maxlun_cb();
TU_VERIFY(maxlun);
@@ -206,7 +362,7 @@ bool mscd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t
// MAX LUN is minus 1 by specs
maxlun--;
tud_control_xfer(rhport, p_request, &maxlun, 1);
tud_control_xfer(rhport, request, &maxlun, 1);
}
break;
@@ -218,6 +374,8 @@ bool mscd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t
bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
{
(void) event;
mscd_interface_t* p_msc = &_mscd_itf;
msc_cbw_t const * p_cbw = &p_msc->cbw;
msc_csw_t * p_csw = &p_msc->csw;
@@ -229,46 +387,79 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
// Complete IN while waiting for CMD is usually Status of previous SCSI op, ignore it
if(ep_addr != p_msc->ep_out) return true;
TU_ASSERT( event == XFER_RESULT_SUCCESS &&
xferred_bytes == sizeof(msc_cbw_t) && p_cbw->signature == MSC_CBW_SIGNATURE );
if ( !(xferred_bytes == sizeof(msc_cbw_t) && p_cbw->signature == MSC_CBW_SIGNATURE) )
{
TU_LOG(MSC_DEBUG, " SCSI CBW is not valid\r\n");
TU_LOG2(" SCSI Command: %s\r\n", tu_lookup_find(&_msc_scsi_cmd_table, p_cbw->command[0]));
// TU_LOG2_MEM(p_cbw, xferred_bytes, 2);
// BOT 6.6.1 If CBW is not valid stall both endpoints until reset recovery
p_msc->stage = MSC_STAGE_NEED_RESET;
// invalid CBW stall both endpoints
usbd_edpt_stall(rhport, p_msc->ep_in);
usbd_edpt_stall(rhport, p_msc->ep_out);
return false;
}
TU_LOG(MSC_DEBUG, " SCSI Command: %s\r\n", tu_lookup_find(&_msc_scsi_cmd_table, p_cbw->command[0]));
//TU_LOG_MEM(MSC_DEBUG, p_cbw, xferred_bytes, 2);
p_csw->signature = MSC_CSW_SIGNATURE;
p_csw->tag = p_cbw->tag;
p_csw->data_residue = 0;
p_csw->status = MSC_CSW_STATUS_PASSED;
/*------------- Parse command and prepare DATA -------------*/
p_msc->stage = MSC_STAGE_DATA;
p_msc->total_len = p_cbw->total_bytes;
p_msc->xferred_len = 0;
if (SCSI_CMD_READ_10 == p_cbw->command[0])
// Read10 or Write10
if ( (SCSI_CMD_READ_10 == p_cbw->command[0]) || (SCSI_CMD_WRITE_10 == p_cbw->command[0]) )
{
proc_read10_cmd(rhport, p_msc);
}
else if (SCSI_CMD_WRITE_10 == p_cbw->command[0])
{
proc_write10_cmd(rhport, p_msc);
uint8_t const status = rdwr10_validate_cmd(p_cbw);
if ( status != MSC_CSW_STATUS_PASSED)
{
fail_scsi_op(rhport, p_msc, status);
}else if ( p_cbw->total_bytes )
{
if (SCSI_CMD_READ_10 == p_cbw->command[0])
{
proc_read10_cmd(rhport, p_msc);
}else
{
proc_write10_cmd(rhport, p_msc);
}
}else
{
// no data transfer, only exist in complaint test suite
p_msc->stage = MSC_STAGE_STATUS;
}
}
else
{
// For other SCSI commands
// 1. OUT : queue transfer (invoke app callback after done)
// 2. IN & Zero: Process if is built-in, else Invoke app callback. Skip DATA if zero length
if ( (p_cbw->total_bytes > 0 ) && !tu_bit_test(p_cbw->dir, 7) )
if ( (p_cbw->total_bytes > 0 ) && !is_data_in(p_cbw->dir) )
{
// queue transfer
TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, p_msc->total_len) );
if (p_cbw->total_bytes > sizeof(_mscd_buf))
{
TU_LOG(MSC_DEBUG, " SCSI reject non READ10/WRITE10 with large data\r\n");
fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
}else
{
// Didn't check for case 9 (Ho > Dn), which requires examining scsi command first
// but it is OK to just receive data then responded with failed status
TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, p_msc->total_len) );
}
}else
{
int32_t resplen;
// First process if it is a built-in commands
resplen = proc_builtin_scsi(p_cbw->lun, p_cbw->command, _mscd_buf, sizeof(_mscd_buf));
int32_t resplen = proc_builtin_scsi(p_cbw->lun, p_cbw->command, _mscd_buf, sizeof(_mscd_buf));
// Not built-in, invoke user callback
// Invoke user callback if not built-in
if ( (resplen < 0) && (p_msc->sense_key == 0) )
{
resplen = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_buf, p_msc->total_len);
@@ -276,28 +467,35 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
if ( resplen < 0 )
{
p_msc->total_len = 0;
p_csw->status = MSC_CSW_STATUS_FAILED;
p_msc->stage = MSC_STAGE_STATUS;
// failed but senskey is not set: default to Illegal Request
if ( p_msc->sense_key == 0 ) tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
// Stall bulk In if needed
if (p_cbw->total_bytes) usbd_edpt_stall(rhport, p_msc->ep_in);
// unsupported command
TU_LOG(MSC_DEBUG, " SCSI unsupported command\r\n");
fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
}
else if (resplen == 0)
{
if (p_cbw->total_bytes)
{
// 6.7 The 13 Cases: case 4 (Hi > Dn)
TU_LOG(MSC_DEBUG, " SCSI case 4 (Hi > Dn): %lu\r\n", p_cbw->total_bytes);
fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
}else
{
// case 1 Hn = Dn: all good
p_msc->stage = MSC_STAGE_STATUS;
}
}
else
{
p_msc->total_len = (uint32_t) resplen;
p_csw->status = MSC_CSW_STATUS_PASSED;
if (p_msc->total_len)
if ( p_cbw->total_bytes == 0 )
{
TU_ASSERT( p_cbw->total_bytes >= p_msc->total_len ); // cannot return more than host expect
TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, p_msc->total_len) );
// 6.7 The 13 Cases: case 2 (Hn < Di)
TU_LOG(MSC_DEBUG, " SCSI case 2 (Hn < Di): %lu\r\n", p_cbw->total_bytes);
fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
}else
{
p_msc->stage = MSC_STAGE_STATUS;
// cannot return more than host expect
p_msc->total_len = tu_min32((uint32_t) resplen, p_cbw->total_bytes);
TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, p_msc->total_len) );
}
}
}
@@ -305,90 +503,54 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
break;
case MSC_STAGE_DATA:
TU_LOG2(" SCSI Data\r\n");
//TU_LOG2_MEM(_mscd_buf, xferred_bytes, 2);
TU_LOG(MSC_DEBUG, " SCSI Data\r\n");
//TU_LOG_MEM(MSC_DEBUG, _mscd_buf, xferred_bytes, 2);
// OUT transfer, invoke callback if needed
if ( !tu_bit_test(p_cbw->dir, 7) )
if (SCSI_CMD_READ_10 == p_cbw->command[0])
{
if ( SCSI_CMD_WRITE_10 != p_cbw->command[0] )
p_msc->xferred_len += xferred_bytes;
if ( p_msc->xferred_len >= p_msc->total_len )
{
// Data Stage is complete
p_msc->stage = MSC_STAGE_STATUS;
}else
{
proc_read10_cmd(rhport, p_msc);
}
}
else if (SCSI_CMD_WRITE_10 == p_cbw->command[0])
{
proc_write10_new_data(rhport, p_msc, xferred_bytes);
}
else
{
p_msc->xferred_len += xferred_bytes;
// OUT transfer, invoke callback if needed
if ( !is_data_in(p_cbw->dir) )
{
int32_t cb_result = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_buf, p_msc->total_len);
if ( cb_result < 0 )
{
p_csw->status = MSC_CSW_STATUS_FAILED;
tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); // Sense = Invalid Command Operation
// unsupported command
TU_LOG(MSC_DEBUG, " SCSI unsupported command\r\n");
fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
}else
{
p_csw->status = MSC_CSW_STATUS_PASSED;
// TODO haven't implement this scenario any further yet
}
}
if ( p_msc->xferred_len >= p_msc->total_len )
{
// Data Stage is complete
p_msc->stage = MSC_STAGE_STATUS;
}
else
{
uint16_t const block_sz = p_cbw->total_bytes / rdwr10_get_blockcount(p_cbw->command);
// Adjust lba with transferred bytes
uint32_t const lba = rdwr10_get_lba(p_cbw->command) + (p_msc->xferred_len / block_sz);
// Application can consume smaller bytes
int32_t nbytes = tud_msc_write10_cb(p_cbw->lun, lba, p_msc->xferred_len % block_sz, _mscd_buf, xferred_bytes);
if ( nbytes < 0 )
{
// negative means error -> skip to status phase, status in CSW set to failed
p_csw->data_residue = p_cbw->total_bytes - p_msc->xferred_len;
p_csw->status = MSC_CSW_STATUS_FAILED;
p_msc->stage = MSC_STAGE_STATUS;
tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); // Sense = Invalid Command Operation
break;
}else
{
// Application consume less than what we got (including zero)
if ( nbytes < (int32_t) xferred_bytes )
{
if ( nbytes > 0 )
{
p_msc->xferred_len += nbytes;
memmove(_mscd_buf, _mscd_buf+nbytes, xferred_bytes-nbytes);
}
// simulate an transfer complete with adjusted parameters --> this driver callback will fired again
dcd_event_xfer_complete(rhport, p_msc->ep_out, xferred_bytes-nbytes, XFER_RESULT_SUCCESS, false);
return true; // skip the rest
}
else
{
// Application consume all bytes in our buffer. Nothing to do, process with normal flow
}
}
}
}
// Accumulate data so far
p_msc->xferred_len += xferred_bytes;
if ( p_msc->xferred_len >= p_msc->total_len )
{
// Data Stage is complete
p_msc->stage = MSC_STAGE_STATUS;
}
else
{
// READ10 & WRITE10 Can be executed with large bulk of data e.g write 8K bytes (several flash write)
// We break it into multiple smaller command whose data size is up to CFG_TUD_MSC_EP_BUFSIZE
if (SCSI_CMD_READ_10 == p_cbw->command[0])
{
proc_read10_cmd(rhport, p_msc);
}
else if (SCSI_CMD_WRITE_10 == p_cbw->command[0])
{
proc_write10_cmd(rhport, p_msc);
}else
{
// No other command take more than one transfer yet -> unlikely error
// This scenario with command that take more than one transfer is already rejected at Command stage
TU_BREAKPOINT();
}
}
@@ -402,8 +564,8 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
// Wait for the Status phase to complete
if( (ep_addr == p_msc->ep_in) && (xferred_bytes == sizeof(msc_csw_t)) )
{
TU_LOG2(" SCSI Status: %u\r\n", p_csw->status);
// TU_LOG2_MEM(p_csw, xferred_bytes, 2);
TU_LOG(MSC_DEBUG, " SCSI Status = %u\r\n", p_csw->status);
// TU_LOG_MEM(MSC_DEBUG, p_csw, xferred_bytes, 2);
// Invoke complete callback if defined
// Note: There is racing issue with samd51 + qspi flash testing with arduino
@@ -423,11 +585,11 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
break;
}
// Move to default CMD stage
p_msc->stage = MSC_STAGE_CMD;
// Queue for the next CBW
TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)) );
TU_ASSERT( prepare_cbw(rhport, p_msc) );
}else
{
// Any xfer ended here is consider unknown error, ignore it
TU_LOG1(" Warning expect SCSI Status but received unknown data\r\n");
}
break;
@@ -436,22 +598,30 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
if ( p_msc->stage == MSC_STAGE_STATUS )
{
// Either endpoints is stalled, need to wait until it is cleared by host
if ( usbd_edpt_stalled(rhport, p_msc->ep_in) || usbd_edpt_stalled(rhport, p_msc->ep_out) )
// skip status if epin is currently stalled, will do it when received Clear Stall request
if ( !usbd_edpt_stalled(rhport, p_msc->ep_in) )
{
// simulate an transfer complete with adjusted parameters --> this driver callback will fired again
// and response with status phase after halted endpoints are cleared.
// note: use ep_out to prevent confusing with STATUS complete
dcd_event_xfer_complete(rhport, p_msc->ep_out, 0, XFER_RESULT_SUCCESS, false);
if ( (p_cbw->total_bytes > p_msc->xferred_len) && is_data_in(p_cbw->dir) )
{
// 6.7 The 13 Cases: case 5 (Hi > Di): STALL before status
TU_LOG(MSC_DEBUG, " SCSI case 5 (Hi > Di): %lu > %lu\r\n", p_cbw->total_bytes, p_msc->xferred_len);
usbd_edpt_stall(rhport, p_msc->ep_in);
}else
{
TU_ASSERT( send_csw(rhport, p_msc) );
}
}
else
{
// Move to Status Sent stage
p_msc->stage = MSC_STAGE_STATUS_SENT;
// Send SCSI Status
TU_ASSERT(usbd_edpt_xfer(rhport, p_msc->ep_in , (uint8_t*) &p_msc->csw, sizeof(msc_csw_t)));
#if TU_CHECK_MCU(CXD56)
// WORKAROUND: cxd56 has its own nuttx usb stack which does not forward Set/ClearFeature(Endpoint) to DCD.
// There is no way for us to know when EP is un-stall, therefore we will unconditionally un-stall here and
// hope everything will work
if ( usbd_edpt_stalled(rhport, p_msc->ep_in) )
{
usbd_edpt_clear_stall(rhport, p_msc->ep_in);
send_csw(rhport, p_msc);
}
#endif
}
return true;
@@ -468,6 +638,8 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
(void) bufsize; // TODO refractor later
int32_t resplen;
mscd_interface_t* p_msc = &_mscd_itf;
switch ( scsi_cmd[0] )
{
case SCSI_CMD_TEST_UNIT_READY:
@@ -478,7 +650,7 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
resplen = - 1;
// If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
if ( p_msc->sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
}
break;
@@ -494,7 +666,7 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
resplen = - 1;
// If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
if ( p_msc->sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
}
}
break;
@@ -515,7 +687,7 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
resplen = -1;
// If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
if ( p_msc->sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
}else
{
scsi_read_capacity10_resp_t read_capa10;
@@ -551,7 +723,7 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
resplen = -1;
// If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
if ( p_msc->sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
}else
{
read_fmt_capa.block_num = tu_htonl(block_count);
@@ -596,9 +768,11 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
};
bool writable = true;
if (tud_msc_is_writable_cb) {
writable = tud_msc_is_writable_cb(lun);
if ( tud_msc_is_writable_cb )
{
writable = tud_msc_is_writable_cb(lun);
}
mode_resp.write_protected = !writable;
resplen = sizeof(mode_resp);
@@ -616,9 +790,9 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
sense_rsp.add_sense_len = sizeof(scsi_sense_fixed_resp_t) - 8;
sense_rsp.sense_key = _mscd_itf.sense_key;
sense_rsp.add_sense_code = _mscd_itf.add_sense_code;
sense_rsp.add_sense_qualifier = _mscd_itf.add_sense_qualifier;
sense_rsp.sense_key = p_msc->sense_key;
sense_rsp.add_sense_code = p_msc->add_sense_code;
sense_rsp.add_sense_qualifier = p_msc->add_sense_qualifier;
resplen = sizeof(sense_rsp);
memcpy(buffer, &sense_rsp, resplen);
@@ -637,13 +811,9 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc)
{
msc_cbw_t const * p_cbw = &p_msc->cbw;
msc_csw_t * p_csw = &p_msc->csw;
uint16_t const block_cnt = rdwr10_get_blockcount(p_cbw->command);
TU_ASSERT(block_cnt, ); // prevent div by zero
uint16_t const block_sz = p_cbw->total_bytes / block_cnt;
TU_ASSERT(block_sz, ); // prevent div by zero
// block size already verified not zero
uint16_t const block_sz = rdwr10_get_blocksize(p_cbw);
// Adjust lba with transferred bytes
uint32_t const lba = rdwr10_get_lba(p_cbw->command) + (p_msc->xferred_len / block_sz);
@@ -652,16 +822,18 @@ static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc)
int32_t nbytes = (int32_t) tu_min32(sizeof(_mscd_buf), p_cbw->total_bytes-p_msc->xferred_len);
// Application can consume smaller bytes
nbytes = tud_msc_read10_cb(p_cbw->lun, lba, p_msc->xferred_len % block_sz, _mscd_buf, (uint32_t) nbytes);
uint32_t const offset = p_msc->xferred_len % block_sz;
nbytes = tud_msc_read10_cb(p_cbw->lun, lba, offset, _mscd_buf, (uint32_t) nbytes);
if ( nbytes < 0 )
{
// negative means error -> pipe is stalled & status in CSW set to failed
p_csw->data_residue = p_cbw->total_bytes - p_msc->xferred_len;
p_csw->status = MSC_CSW_STATUS_FAILED;
// negative means error -> endpoint is stalled & status in CSW set to failed
TU_LOG(MSC_DEBUG, " tud_msc_read10_cb() return -1\r\n");
tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); // Sense = Invalid Command Operation
usbd_edpt_stall(rhport, p_msc->ep_in);
// Sense = Flash not ready for access
tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_MEDIUM_ERROR, 0x33, 0x00);
fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
}
else if ( nbytes == 0 )
{
@@ -678,16 +850,18 @@ static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc)
{
msc_cbw_t const * p_cbw = &p_msc->cbw;
bool writable = true;
if (tud_msc_is_writable_cb) {
if ( tud_msc_is_writable_cb )
{
writable = tud_msc_is_writable_cb(p_cbw->lun);
}
if (!writable) {
msc_csw_t* p_csw = &p_msc->csw;
p_csw->data_residue = p_cbw->total_bytes;
p_csw->status = MSC_CSW_STATUS_FAILED;
tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_DATA_PROTECT, 0x27, 0x00); // Sense = Write protected
usbd_edpt_stall(rhport, p_msc->ep_out);
if ( !writable )
{
// Not writable, complete this SCSI op with error
// Sense = Write protected
tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_DATA_PROTECT, 0x27, 0x00);
fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
return;
}
@@ -698,4 +872,63 @@ static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc)
TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, nbytes), );
}
// process new data arrived from WRITE10
static void proc_write10_new_data(uint8_t rhport, mscd_interface_t* p_msc, uint32_t xferred_bytes)
{
msc_cbw_t const * p_cbw = &p_msc->cbw;
// block size already verified not zero
uint16_t const block_sz = rdwr10_get_blocksize(p_cbw);
// Adjust lba with transferred bytes
uint32_t const lba = rdwr10_get_lba(p_cbw->command) + (p_msc->xferred_len / block_sz);
// Invoke callback to consume new data
uint32_t const offset = p_msc->xferred_len % block_sz;
int32_t nbytes = tud_msc_write10_cb(p_cbw->lun, lba, offset, _mscd_buf, xferred_bytes);
if ( nbytes < 0 )
{
// negative means error -> failed this scsi op
TU_LOG(MSC_DEBUG, " tud_msc_write10_cb() return -1\r\n");
// update actual byte before failed
p_msc->xferred_len += xferred_bytes;
// Sense = Flash not ready for access
tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_MEDIUM_ERROR, 0x33, 0x00);
fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
}else
{
// Application consume less than what we got (including zero)
if ( (uint32_t) nbytes < xferred_bytes )
{
if ( nbytes > 0 )
{
p_msc->xferred_len += nbytes;
memmove(_mscd_buf, _mscd_buf+nbytes, xferred_bytes-nbytes);
}
// simulate an transfer complete with adjusted parameters --> callback will be invoked with adjusted parameter
dcd_event_xfer_complete(rhport, p_msc->ep_out, xferred_bytes-nbytes, XFER_RESULT_SUCCESS, false);
}
else
{
// Application consume all bytes in our buffer
p_msc->xferred_len += xferred_bytes;
if ( p_msc->xferred_len >= p_msc->total_len )
{
// Data Stage is complete
p_msc->stage = MSC_STAGE_STATUS;
}else
{
// prepare to receive more data from host
proc_write10_cmd(rhport, p_msc);
}
}
}
}
#endif
+1 -1
View File
@@ -140,7 +140,7 @@ TU_ATTR_WEAK void tud_msc_write10_complete_cb(uint8_t lun);
// Invoked when command in tud_msc_scsi_cb is complete
TU_ATTR_WEAK void tud_msc_scsi_complete_cb(uint8_t lun, uint8_t const scsi_cmd[16]);
// Hook to make a mass storage device read-only. TODO remove
// Invoked to check if device is writable as part of SCSI WRITE10
TU_ATTR_WEAK bool tud_msc_is_writable_cb(uint8_t lun);
//--------------------------------------------------------------------+
+10 -4
View File
@@ -69,13 +69,14 @@ typedef struct
msc_csw_t csw;
}msch_interface_t;
CFG_TUSB_MEM_SECTION static msch_interface_t _msch_itf[CFG_TUSB_HOST_DEVICE_MAX];
CFG_TUSB_MEM_SECTION static msch_interface_t _msch_itf[CFG_TUH_DEVICE_MAX];
// buffer used to read scsi information when mounted
// largest response data currently is inquiry TODO Inquiry is not part of enum anymore
CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4)
static uint8_t _msch_buffer[sizeof(scsi_inquiry_resp_t)];
TU_ATTR_ALWAYS_INLINE
static inline msch_interface_t* get_itf(uint8_t dev_addr)
{
return &_msch_itf[dev_addr-1];
@@ -291,11 +292,13 @@ bool tuh_msc_reset(uint8_t dev_addr)
//--------------------------------------------------------------------+
void msch_init(void)
{
tu_memclr(_msch_itf, sizeof(msch_interface_t)*CFG_TUSB_HOST_DEVICE_MAX);
tu_memclr(_msch_itf, sizeof(_msch_itf));
}
void msch_close(uint8_t dev_addr)
{
TU_VERIFY(dev_addr <= CFG_TUH_DEVICE_MAX, );
msch_interface_t* p_msc = get_itf(dev_addr);
// invoke Application Callback
@@ -360,11 +363,15 @@ static bool config_test_unit_ready_complete(uint8_t dev_addr, msc_cbw_t const* c
static bool config_request_sense_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw);
static bool config_read_capacity_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw);
bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t *p_length)
bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len)
{
TU_VERIFY (MSC_SUBCLASS_SCSI == desc_itf->bInterfaceSubClass &&
MSC_PROTOCOL_BOT == desc_itf->bInterfaceProtocol);
// msc driver length is fixed
uint16_t const drv_len = 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);
@@ -385,7 +392,6 @@ bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *de
}
p_msc->itf_num = desc_itf->bInterfaceNumber;
(*p_length) += sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
return true;
}
+5 -15
View File
@@ -41,13 +41,6 @@
#define CFG_TUH_MSC_MAXLUN 4
#endif
/** \addtogroup ClassDriver_MSC
* @{
* \defgroup MSC_Host Host
* The interface API includes status checking function, data transferring function and callback functions
* @{ */
typedef bool (*tuh_msc_complete_cb_t)(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw);
//--------------------------------------------------------------------+
@@ -113,17 +106,14 @@ TU_ATTR_WEAK void tuh_msc_umount_cb(uint8_t dev_addr);
// Internal Class Driver API
//--------------------------------------------------------------------+
void msch_init(void);
bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t *p_length);
bool msch_set_config(uint8_t dev_addr, uint8_t itf_num);
bool msch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
void msch_close(uint8_t dev_addr);
void msch_init (void);
bool msch_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len);
bool msch_set_config (uint8_t dev_addr, uint8_t itf_num);
void msch_close (uint8_t dev_addr);
bool msch_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_MSC_HOST_H_ */
/// @}
/// @}
+9
View File
@@ -103,6 +103,13 @@ uint32_t tud_vendor_n_read (uint8_t itf, void* buffer, uint32_t bufsize)
return num_read;
}
void tud_vendor_n_read_flush (uint8_t itf)
{
vendord_interface_t* p_itf = &_vendord_itf[itf];
tu_fifo_clear(&p_itf->rx_ff);
_prep_out_transaction(p_itf);
}
//--------------------------------------------------------------------+
// Write API
//--------------------------------------------------------------------+
@@ -199,6 +206,8 @@ uint16_t vendord_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, ui
TU_BREAKPOINT();
}
maybe_transmit(p_vendor);
return drv_len;
}
+11 -4
View File
@@ -44,7 +44,8 @@ bool tud_vendor_n_mounted (uint8_t itf);
uint32_t tud_vendor_n_available (uint8_t itf);
uint32_t tud_vendor_n_read (uint8_t itf, void* buffer, uint32_t bufsize);
bool tud_vendor_n_peek (uint8_t itf, uint8_t* uv8);
bool tud_vendor_n_peek (uint8_t itf, uint8_t* ui8);
void tud_vendor_n_read_flush (uint8_t itf);
uint32_t tud_vendor_n_write (uint8_t itf, void const* buffer, uint32_t bufsize);
uint32_t tud_vendor_n_write_available (uint8_t itf);
@@ -58,7 +59,8 @@ uint32_t tud_vendor_n_write_str (uint8_t itf, char const* str);
static inline bool tud_vendor_mounted (void);
static inline uint32_t tud_vendor_available (void);
static inline uint32_t tud_vendor_read (void* buffer, uint32_t bufsize);
static inline bool tud_vendor_peek (uint8_t* uv8);
static inline bool tud_vendor_peek (uint8_t* ui8);
static inline void tud_vendor_read_flush (void);
static inline uint32_t tud_vendor_write (void const* buffer, uint32_t bufsize);
static inline uint32_t tud_vendor_write_str (char const* str);
static inline uint32_t tud_vendor_write_available (void);
@@ -94,9 +96,14 @@ static inline uint32_t tud_vendor_read (void* buffer, uint32_t bufsize)
return tud_vendor_n_read(0, buffer, bufsize);
}
static inline bool tud_vendor_peek (uint8_t* uv8)
static inline bool tud_vendor_peek (uint8_t* ui8)
{
return tud_vendor_n_peek(0, uv8);
return tud_vendor_n_peek(0, ui8);
}
static inline void tud_vendor_read_flush(void)
{
tud_vendor_n_read_flush(0);
}
static inline uint32_t tud_vendor_write (void const* buffer, uint32_t bufsize)
+2 -2
View File
@@ -41,7 +41,7 @@
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
custom_interface_info_t custom_interface[CFG_TUSB_HOST_DEVICE_MAX];
custom_interface_info_t custom_interface[CFG_TUH_DEVICE_MAX];
static tusb_error_t cush_validate_paras(uint8_t dev_addr, uint16_t vendor_id, uint16_t product_id, void * p_buffer, uint16_t length)
{
@@ -90,7 +90,7 @@ tusb_error_t tusbh_custom_write(uint8_t dev_addr, uint16_t vendor_id, uint16_t p
//--------------------------------------------------------------------+
void cush_init(void)
{
tu_memclr(&custom_interface, sizeof(custom_interface_info_t) * CFG_TUSB_HOST_DEVICE_MAX);
tu_memclr(&custom_interface, sizeof(custom_interface_info_t) * CFG_TUH_DEVICE_MAX);
}
tusb_error_t cush_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_interface_desc, uint16_t *p_length)