Merge branch 'master' into zhangslice-master
This commit is contained in:
+28
-1
@@ -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
@@ -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))
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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 */ \
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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_ */
|
||||
|
||||
/// @}
|
||||
/// @}
|
||||
|
||||
Vendored
+9
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Vendored
+11
-4
@@ -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)
|
||||
|
||||
Vendored
+2
-2
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user