diff options
| author | Mengsk <[email protected]> | 2022-11-07 12:47:41 +0100 |
|---|---|---|
| committer | Mengsk <[email protected]> | 2022-11-07 12:47:41 +0100 |
| commit | 9d3d8fd5b0788b811ad283d10d37dfd1b1005224 (patch) | |
| tree | f66b39fe968cc1a6e46e3074bbda9b907064bc15 /src/class | |
| parent | 1eae139aa94130a63ecc4725852cb69c02566c82 (diff) | |
| parent | e434a1dc0584e841da1ae4ff525bbe81a0f5514b (diff) | |
Merge branch 'master' of https://github.com/hathach/tinyusb into bsp_412
Diffstat (limited to 'src/class')
29 files changed, 1083 insertions, 490 deletions
diff --git a/src/class/audio/audio_device.c b/src/class/audio/audio_device.c index d9f2e284e..698fba566 100644 --- a/src/class/audio/audio_device.c +++ b/src/class/audio/audio_device.c @@ -50,7 +50,7 @@ #include "tusb_option.h" -#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_AUDIO) +#if (CFG_TUD_ENABLED && CFG_TUD_AUDIO) //--------------------------------------------------------------------+ // INCLUDE @@ -66,7 +66,7 @@ // Use ring buffer if it's available, some MCUs need extra RAM requirements #ifndef TUD_AUDIO_PREFER_RING_BUFFER -#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX +#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT #define TUD_AUDIO_PREFER_RING_BUFFER 0 #else #define TUD_AUDIO_PREFER_RING_BUFFER 1 @@ -102,7 +102,7 @@ CFG_TUSB_MCU == OPT_MCU_GD32VF103 || \ CFG_TUSB_MCU == OPT_MCU_LPC18XX || \ CFG_TUSB_MCU == OPT_MCU_LPC43XX || \ - CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || \ + CFG_TUSB_MCU == OPT_MCU_MIMXRT || \ CFG_TUSB_MCU == OPT_MCU_MSP432E4 #if TUD_AUDIO_PREFER_RING_BUFFER #define USE_LINEAR_BUFFER 0 @@ -305,9 +305,35 @@ typedef struct #endif #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP - uint32_t fb_val; // Feedback value for asynchronous mode (in 16.16 format). -#endif + struct { + uint32_t value; // Feedback value for asynchronous mode (in 16.16 format). + uint32_t min_value; // min value according to UAC2 FMT-2.0 section 2.3.1.1. + uint32_t max_value; // max value according to UAC2 FMT-2.0 section 2.3.1.1. + + uint8_t frame_shift; // bInterval-1 in unit of frame (FS), micro-frame (HS) + uint8_t compute_method; + + union { + uint8_t power_of_2; // pre-computed power of 2 shift + float float_const; // pre-computed float constant + + struct { + uint32_t sample_freq; + uint32_t mclk_freq; + }fixed; + +#if 0 // implement later + struct { + uint32_t nominal_value; + uint32_t threshold_bytes; + }fifo_count; #endif + }compute; + + } feedback; +#endif // CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP + +#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT #if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING tu_fifo_t ep_in_ff; @@ -315,7 +341,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_SECTION CFG_TUSB_MEM_ALIGN uint8_t ep_int_ctr_buf[CFG_TUD_AUDIO_INT_CTR_EP_IN_SW_BUFFER_SIZE]; + 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 @@ -421,6 +447,10 @@ static inline uint8_t tu_desc_subtype(void const* desc) } #endif +#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP +static bool set_fb_params_freq(audiod_function_t* audio, uint32_t sample_freq, uint32_t mclk_freq); +#endif + bool tud_audio_n_mounted(uint8_t func_id) { TU_VERIFY(func_id < CFG_TUD_AUDIO); @@ -511,7 +541,7 @@ tu_fifo_t* tud_audio_n_get_rx_support_ff(uint8_t func_id, uint8_t ff_idx) static bool audiod_rx_done_cb(uint8_t rhport, audiod_function_t* audio, uint16_t n_bytes_received) { - uint8_t idxItf; + uint8_t idxItf = 0; uint8_t const *dummy2; uint8_t idx_audio_fct = 0; @@ -522,7 +552,10 @@ static bool audiod_rx_done_cb(uint8_t rhport, audiod_function_t* audio, uint16_t } // Call a weak callback here - a possibility for user to get informed an audio packet was received and data gets now loaded into EP FIFO (or decoded into support RX software FIFO) - if (tud_audio_rx_done_pre_read_cb) TU_VERIFY(tud_audio_rx_done_pre_read_cb(rhport, n_bytes_received, idx_audio_fct, audio->ep_out, audio->alt_setting[idxItf])); + if (tud_audio_rx_done_pre_read_cb) + { + TU_VERIFY(tud_audio_rx_done_pre_read_cb(rhport, n_bytes_received, idx_audio_fct, audio->ep_out, audio->alt_setting[idxItf])); + } #if CFG_TUD_AUDIO_ENABLE_DECODING && CFG_TUD_AUDIO_ENABLE_EP_OUT @@ -536,7 +569,7 @@ static bool audiod_rx_done_cb(uint8_t rhport, audiod_function_t* audio, uint16_t case AUDIO_FORMAT_TYPE_I: - switch (audio->format_type_I_tx) + switch (audio->format_type_I_rx) { case AUDIO_DATA_FORMAT_TYPE_I_PCM: TU_VERIFY(audiod_decode_type_I_pcm(rhport, audio, n_bytes_received)); @@ -576,7 +609,10 @@ static bool audiod_rx_done_cb(uint8_t rhport, audiod_function_t* audio, uint16_t #endif // Call a weak callback here - a possibility for user to get informed decoding was completed - if (tud_audio_rx_done_post_read_cb) TU_VERIFY(tud_audio_rx_done_post_read_cb(rhport, n_bytes_received, idx_audio_fct, audio->ep_out, audio->alt_setting[idxItf])); + if (tud_audio_rx_done_post_read_cb) + { + TU_VERIFY(tud_audio_rx_done_post_read_cb(rhport, n_bytes_received, idx_audio_fct, audio->ep_out, audio->alt_setting[idxItf])); + } return true; } @@ -1039,7 +1075,7 @@ static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t* audi #if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP static inline bool audiod_fb_send(uint8_t rhport, audiod_function_t *audio) { - return usbd_edpt_xfer(rhport, audio->ep_fb, (uint8_t *) &audio->fb_val, 4); + return usbd_edpt_xfer(rhport, audio->ep_fb, (uint8_t *) &audio->feedback.value, 4); } #endif @@ -1510,7 +1546,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * 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)); @@ -1543,7 +1579,8 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * // Close corresponding feedback EP #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP usbd_edpt_close(rhport, audio->ep_fb); - audio->ep_fb = 0; // Necessary? + audio->ep_fb = 0; + tu_memclr(&audio->feedback, sizeof(audio->feedback)); #endif } #endif @@ -1592,7 +1629,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * // Reconfigure size of support FIFOs - this is necessary to avoid samples to get split in case of a wrap #if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING - const uint16_t active_fifo_depth = (audio->tx_supp_ff_sz_max / audio->n_bytes_per_sampe_tx) * audio->n_bytes_per_sampe_tx; + const uint16_t active_fifo_depth = (uint16_t) ((audio->tx_supp_ff_sz_max / audio->n_bytes_per_sampe_tx) * audio->n_bytes_per_sampe_tx); for (uint8_t cnt = 0; cnt < audio->n_tx_supp_ff; cnt++) { tu_fifo_config(&audio->tx_supp_ff[cnt], audio->tx_supp_ff[cnt].buffer, active_fifo_depth, 1, true); @@ -1602,8 +1639,6 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * #endif #endif - // Invoke callback - can be used to trigger data sampling if not already running - if (tud_audio_set_itf_cb) TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request)); // Schedule first transmit if alternate interface is not zero i.e. streaming is disabled - in case no sample data is available a ZLP is loaded // It is necessary to trigger this here since the refill is done with an RX FIFO empty interrupt which can only trigger if something was in there @@ -1635,16 +1670,6 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * #endif #endif -#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP - // In case of asynchronous EP, call Cb after ep_fb is set - if ( !(desc_ep->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); @@ -1657,12 +1682,10 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN && desc_ep->bmAttributes.usage == 1) // Check if usage is explicit data feedback { audio->ep_fb = ep_addr; + audio->feedback.frame_shift = desc_ep->bInterval -1; - // 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)); - } + // Enable SOF interrupt if callback is implemented + if (tud_audio_feedback_interval_isr) usbd_sof_enable(rhport, true); } #endif #endif // CFG_TUD_AUDIO_ENABLE_EP_OUT @@ -1674,6 +1697,49 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * TU_VERIFY(foundEPs == nEps); + // Invoke one callback for a final set interface + if (tud_audio_set_itf_cb) TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request)); + +#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP + // Prepare feedback computation if callback is available + if (tud_audio_feedback_params_cb) + { + audio_feedback_params_t fb_param; + + tud_audio_feedback_params_cb(func_id, alt, &fb_param); + audio->feedback.compute_method = fb_param.method; + + // Minimal/Maximum value in 16.16 format for full speed (1ms per frame) or high speed (125 us per frame) + uint32_t const frame_div = (TUSB_SPEED_FULL == tud_speed_get()) ? 1000 : 8000; + audio->feedback.min_value = (fb_param.sample_freq/frame_div - 1) << 16; + audio->feedback.max_value = (fb_param.sample_freq/frame_div + 1) << 16; + + switch(fb_param.method) + { + case AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED: + case AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT: + case AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2: + set_fb_params_freq(audio, fb_param.sample_freq, fb_param.frequency.mclk_freq); + break; + + #if 0 // implement later + case AUDIO_FEEDBACK_METHOD_FIFO_COUNT: + { + uint64_t fb64 = ((uint64_t) fb_param.sample_freq) << 16; + audio->feedback.compute.fifo_count.nominal_value = (uint32_t) (fb64 / frame_div); + audio->feedback.compute.fifo_count.threshold_bytes = fb_param.fifo_count.threshold_bytes; + + tud_audio_fb_set(audio->feedback.compute.fifo_count.nominal_value); + } + break; + #endif + + // nothing to do + default: break; + } + } +#endif + // We are done - abort loop break; } @@ -1682,6 +1748,20 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * p_desc = tu_desc_next(p_desc); } +#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP + // Disable SOF interrupt if no driver has any enabled feedback EP + bool disable = true; + for(uint8_t i=0; i < CFG_TUD_AUDIO; i++) + { + if (_audiod_fct[i].ep_fb != 0) + { + disable = false; + break; + } + } + if (disable) usbd_sof_enable(rhport, false); +#endif + tud_control_status(rhport, p_request); return true; @@ -1898,14 +1978,14 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3 (void) xferred_bytes; // Search for interface belonging to given end point address and proceed as required - uint8_t func_id; - for (func_id = 0; func_id < CFG_TUD_AUDIO; func_id++) + for (uint8_t func_id = 0; func_id < CFG_TUD_AUDIO; func_id++) { + audiod_function_t* audio = &_audiod_fct[func_id]; #if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN // Data transmission of control interrupt finished - if (_audiod_fct[func_id].ep_int_ctr == ep_addr) + if (audio->ep_int_ctr == ep_addr) { // According to USB2 specification, maximum payload of interrupt EP is 8 bytes on low speed, 64 bytes on full speed, and 1024 bytes on high speed (but only if an alternate interface other than 0 is used - see specification p. 49) // In case there is nothing to send we have to return a NAK - this is taken care of by PHY ??? @@ -1922,7 +2002,7 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3 #if CFG_TUD_AUDIO_ENABLE_EP_IN // Data transmission of audio packet finished - if (_audiod_fct[func_id].ep_in == ep_addr && _audiod_fct[func_id].alt_setting != 0) + if (audio->ep_in == ep_addr && audio->alt_setting != 0) { // USB 2.0, section 5.6.4, third paragraph, states "An isochronous endpoint must specify its required bus access period. However, an isochronous endpoint must be prepared to handle poll rates faster than the one specified." // That paragraph goes on to say "An isochronous IN endpoint must return a zero-length packet whenever data is requested at a faster interval than the specified interval and data is not available." @@ -1933,7 +2013,7 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3 // This is the only place where we can fill something into the EPs buffer! // Load new data - TU_VERIFY(audiod_tx_done_cb(rhport, &_audiod_fct[func_id])); + TU_VERIFY(audiod_tx_done_cb(rhport, audio)); // Transmission of ZLP is done by audiod_tx_done_cb() return true; @@ -1943,24 +2023,24 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3 #if CFG_TUD_AUDIO_ENABLE_EP_OUT // New audio packet received - if (_audiod_fct[func_id].ep_out == ep_addr) + if (audio->ep_out == ep_addr) { - TU_VERIFY(audiod_rx_done_cb(rhport, &_audiod_fct[func_id], (uint16_t) xferred_bytes)); + TU_VERIFY(audiod_rx_done_cb(rhport, audio, (uint16_t) xferred_bytes)); return true; } #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP // Transmission of feedback EP finished - if (_audiod_fct[func_id].ep_fb == ep_addr) + if (audio->ep_fb == ep_addr) { - if (tud_audio_fb_done_cb) TU_VERIFY(tud_audio_fb_done_cb(rhport)); + if (tud_audio_fb_done_cb) tud_audio_fb_done_cb(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)) + if (!usbd_edpt_busy(rhport, audio->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]); + return audiod_fb_send(rhport, audio); } } #endif @@ -1970,6 +2050,111 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3 return false; } +#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP + +static bool set_fb_params_freq(audiod_function_t* audio, uint32_t sample_freq, uint32_t mclk_freq) +{ + // Check if frame interval is within sane limits + // The interval value n_frames was taken from the descriptors within audiod_set_interface() + + // n_frames_min is ceil(2^10 * f_s / f_m) for full speed and ceil(2^13 * f_s / f_m) for high speed + // this lower limit ensures the measures feedback value has sufficient precision + uint32_t const k = (TUSB_SPEED_FULL == tud_speed_get()) ? 10 : 13; + uint32_t const n_frame = (1UL << audio->feedback.frame_shift); + + if ( (((1UL << k) * sample_freq / mclk_freq) + 1) > n_frame ) + { + TU_LOG1(" UAC2 feedback interval too small\r\n"); TU_BREAKPOINT(); return false; + } + + // Check if parameters really allow for a power of two division + if ((mclk_freq % sample_freq) == 0 && tu_is_power_of_two(mclk_freq / sample_freq)) + { + audio->feedback.compute_method = AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2; + audio->feedback.compute.power_of_2 = 16 - audio->feedback.frame_shift - tu_log2(mclk_freq / sample_freq); + } + else if ( audio->feedback.compute_method == AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT) + { + audio->feedback.compute.float_const = (float)sample_freq / mclk_freq * (1UL << (16 - audio->feedback.frame_shift)); + } + else + { + audio->feedback.compute.fixed.sample_freq = sample_freq; + audio->feedback.compute.fixed.mclk_freq = mclk_freq; + } + + return true; +} + +uint32_t tud_audio_feedback_update(uint8_t func_id, uint32_t cycles) +{ + audiod_function_t* audio = &_audiod_fct[func_id]; + uint32_t feedback; + + switch (audio->feedback.compute_method) + { + case AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2: + feedback = (cycles << audio->feedback.compute.power_of_2); + break; + + case AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT: + feedback = (uint32_t) ((float) cycles * audio->feedback.compute.float_const); + break; + + case AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED: + { + uint64_t fb64 = (((uint64_t) cycles) * audio->feedback.compute.fixed.sample_freq) << (16 - audio->feedback.frame_shift); + feedback = (uint32_t) (fb64 / audio->feedback.compute.fixed.mclk_freq); + } + break; + + default: return 0; + } + + // For Windows: https://docs.microsoft.com/en-us/windows-hardware/drivers/audio/usb-2-0-audio-drivers + // The size of isochronous packets created by the device must be within the limits specified in FMT-2.0 section 2.3.1.1. + // This means that the deviation of actual packet size from nominal size must not exceed +/- one audio slot + // (audio slot = channel count samples). + if ( feedback > audio->feedback.max_value ) feedback = audio->feedback.max_value; + if ( feedback < audio->feedback.min_value ) feedback = audio->feedback.min_value; + + tud_audio_n_fb_set(func_id, feedback); + + return feedback; +} +#endif + +TU_ATTR_FAST_FUNC void audiod_sof_isr (uint8_t rhport, uint32_t frame_count) +{ + (void) rhport; + (void) frame_count; + +#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP + // Determine feedback value - The feedback method is described in 5.12.4.2 of the USB 2.0 spec + // Boiled down, the feedback value Ff = n_samples / (micro)frame. + // Since an accuracy of less than 1 Sample / second is desired, at least n_frames = ceil(2^K * f_s / f_m) frames need to be measured, where K = 10 for full speed and K = 13 for high speed, f_s is the sampling frequency e.g. 48 kHz and f_m is the cpu clock frequency e.g. 100 MHz (or any other master clock whose clock count is available and locked to f_s) + // The update interval in the (4.10.2.1) Feedback Endpoint Descriptor must be less or equal to 2^(K - P), where P = min( ceil(log2(f_m / f_s)), K) + // feedback = n_cycles / n_frames * f_s / f_m in 16.16 format, where n_cycles are the number of main clock cycles within fb_n_frames + + // Iterate over audio functions and set feedback value + for(uint8_t i=0; i < CFG_TUD_AUDIO; i++) + { + audiod_function_t* audio = &_audiod_fct[i]; + + if (audio->ep_fb != 0) + { + // HS shift need to be adjusted since SOF event is generated for frame only + uint8_t const hs_adjust = (TUSB_SPEED_HIGH == tud_speed_get()) ? 3 : 0; + uint32_t const interval = 1UL << (audio->feedback.frame_shift - hs_adjust); + if ( 0 == (frame_count & (interval-1)) ) + { + if(tud_audio_feedback_interval_isr) tud_audio_feedback_interval_isr(i, frame_count, audio->feedback.frame_shift); + } + } + } +#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP +} + bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_request_t const * p_request, void* data, uint16_t len) { // Handles only sending of data not receiving @@ -2164,6 +2349,16 @@ static bool audiod_verify_ep_exists(uint8_t ep, uint8_t *func_id) // Currently, only AS interfaces with an EP (in or out) are supposed to be parsed for! 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) { +#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_EP_OUT + if (as_itf != audio->ep_in_as_intf_num && as_itf != audio->ep_out_as_intf_num) return; // Abort, this interface has no EP, this driver does not support this currently +#endif +#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_EP_OUT + if (as_itf != audio->ep_in_as_intf_num) return; +#endif +#if !CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_EP_OUT + if (as_itf != audio->ep_out_as_intf_num) return; +#endif + p_desc = tu_desc_next(p_desc); // Exclude standard AS interface descriptor of current alternate interface descriptor while (p_desc < p_desc_end) @@ -2174,16 +2369,6 @@ static void audiod_parse_for_AS_params(audiod_function_t* audio, uint8_t const * // Look for a Class-Specific AS Interface Descriptor(4.9.2) to verify format type and format and also to get number of physical channels if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && tu_desc_subtype(p_desc) == AUDIO_CS_AS_INTERFACE_AS_GENERAL) { -#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_EP_OUT - if (as_itf != audio->ep_in_as_intf_num && as_itf != audio->ep_out_as_intf_num) break; // Abort loop, this interface has no EP, this driver does not support this currently -#endif -#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_EP_OUT - if (as_itf != audio->ep_in_as_intf_num) break; -#endif -#if !CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_EP_OUT - if (as_itf != audio->ep_out_as_intf_num) break; -#endif - #if CFG_TUD_AUDIO_ENABLE_EP_IN if (as_itf == audio->ep_in_as_intf_num) { @@ -2247,24 +2432,28 @@ static void audiod_parse_for_AS_params(audiod_function_t* audio, uint8_t const * #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP -// Input value feedback has to be in 16.16 format - the format will be converted according to speed settings automatically 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 !TUD_OPT_HIGH_SPEED - uint8_t * fb = (uint8_t *) &_audiod_fct[func_id].fb_val; +#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION + if ( TUSB_SPEED_FULL == tud_speed_get() ) + { + uint8_t * fb = (uint8_t *) &_audiod_fct[func_id].feedback.value; - // 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; + // 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 #else - // For HS format is 16.16 as originally demanded - _audiod_fct[func_id].fb_val = feedback; + { + // Send value as-is, caller will choose the appropriate format + _audiod_fct[func_id].feedback.value = feedback; + } #endif // Schedule a transmit with the new value if EP is not busy - this triggers repetitive scheduling of the feedback value @@ -2287,4 +2476,4 @@ uint8_t audiod_get_audio_fct_idx(audiod_function_t * audio) return 0; } -#endif //TUSB_OPT_DEVICE_ENABLED && CFG_TUD_AUDIO +#endif //CFG_TUD_ENABLED && CFG_TUD_AUDIO diff --git a/src/class/audio/audio_device.h b/src/class/audio/audio_device.h index 5a469523c..0ef100fa4 100644 --- a/src/class/audio/audio_device.h +++ b/src/class/audio/audio_device.h @@ -186,6 +186,11 @@ #define CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP 0 // Feedback - 0 or 1 #endif +// Enable/disable conversion from 16.16 to 10.14 format on full-speed devices. See tud_audio_n_fb_set(). +#ifndef CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION +#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION 0 // 0 or 1 +#endif + // Audio interrupt control EP size - disabled if 0 #ifndef CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN #define CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN 0 // Audio interrupt control - if required - 6 Bytes according to UAC 2 specification (p. 74) @@ -453,14 +458,78 @@ TU_ATTR_WEAK bool tud_audio_rx_done_post_read_cb(uint8_t rhport, uint16_t n_byte #endif #if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP -TU_ATTR_WEAK bool tud_audio_fb_done_cb(uint8_t rhport); -// User code should call this function with feedback value in 16.16 format for FS and HS. -// Value will be corrected for FS to 10.14 format automatically. -// (see Universal Serial Bus Specification Revision 2.0 5.12.4.2). -// Feedback value will be sent at FB endpoint interval till it's changed. +TU_ATTR_WEAK void tud_audio_fb_done_cb(uint8_t func_id); + + +// determined by the user itself and set by use of tud_audio_n_fb_set(). The feedback value may be determined e.g. from some fill status of some FIFO buffer. Advantage: No ISR interrupt is enabled, hence the CPU need not to handle an ISR every 1ms or 125us and thus less CPU load, disadvantage: typically a larger FIFO is needed to compensate for jitter (e.g. 8 frames), i.e. a larger delay is introduced. + +// Feedback value is calculated within the audio driver by use of SOF interrupt. The driver needs information about the master clock f_m from which the audio sample frequency f_s is derived, f_s itself, and the cycle count of f_m at time of the SOF interrupt (e.g. by use of a hardware counter) - see tud_audio_set_fb_params(). Advantage: Reduced jitter in the feedback value computation, hence, the receive FIFO can be smaller (e.g. 2 frames) and thus a smaller delay is possible, disadvantage: higher CPU load due to SOF ISR handling every frame i.e. 1ms or 125us. This option is a great starting point to try the SOF ISR option but depending on your hardware setup (performance of the CPU) it might not work. If so, figure out why and use the next option. (The most critical point is the reading of the cycle counter value of f_m. It is read from within the SOF ISR - see: audiod_sof() -, hence, the ISR must has a high priority such that no software dependent "random" delay i.e. jitter is introduced). + +// Feedback value is determined by the user by use of SOF interrupt. The user may use tud_audio_sof_isr() which is called every SOF (of course only invoked when an alternate interface other than zero was set). The number of frames used to determine the feedback value for the currently active alternate setting can be get by tud_audio_get_fb_n_frames(). The feedback value must be set by use of tud_audio_n_fb_set(). + +// This function is used to provide data rate feedback from an asynchronous sink. Feedback value will be sent at FB endpoint interval till it's changed. +// +// The feedback format is specified to be 16.16 for HS and 10.14 for FS devices (see Universal Serial Bus Specification Revision 2.0 5.12.4.2). By default, +// the choice of format is left to the caller and feedback argument is sent as-is. If CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION is set, then tinyusb +// expects 16.16 format and handles the conversion to 10.14 on FS. +// +// Note that due to a bug in its USB Audio 2.0 driver, Windows currently requires 16.16 format for _all_ USB 2.0 devices. On Linux and macOS it seems the +// driver can work with either format. So a good compromise is to keep format correction disabled and stick to 16.16 format. + +// Feedback value can be determined from within the SOF ISR of the audio driver. This should reduce jitter. If the feature is used, the user can not set the feedback value. + +// Determine feedback value - The feedback method is described in 5.12.4.2 of the USB 2.0 spec +// Boiled down, the feedback value Ff = n_samples / (micro)frame. +// Since an accuracy of less than 1 Sample / second is desired, at least n_frames = ceil(2^K * f_s / f_m) frames need to be measured, where K = 10 for full speed and K = 13 for high speed, f_s is the sampling frequency e.g. 48 kHz and f_m is the cpu clock frequency e.g. 100 MHz (or any other master clock whose clock count is available and locked to f_s) +// The update interval in the (4.10.2.1) Feedback Endpoint Descriptor must be less or equal to 2^(K - P), where P = min( ceil(log2(f_m / f_s)), K) +// feedback = n_cycles / n_frames * f_s / f_m in 16.16 format, where n_cycles are the number of main clock cycles within fb_n_frames + bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback); static inline bool tud_audio_fb_set(uint32_t feedback); + +// Update feedback value with passed cycles since last time this update function is called. +// Typically called within tud_audio_sof_isr(). Required tud_audio_feedback_params_cb() is implemented +// This function will also call tud_audio_feedback_set() +// return feedback value in 16.16 for reference (0 for error) +uint32_t tud_audio_feedback_update(uint8_t func_id, uint32_t cycles); + +enum { + AUDIO_FEEDBACK_METHOD_DISABLED, + AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED, + AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT, + AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2, + + // impelemnt later + // AUDIO_FEEDBACK_METHOD_FIFO_COUNT +}; + +typedef struct { + uint8_t method; + uint32_t sample_freq; // sample frequency in Hz + + union { + struct { + uint32_t mclk_freq; // Main clock frequency in Hz i.e. master clock to which sample clock is based on + }frequency; + +#if 0 // implement later + struct { + uint32_t threshold_bytes; // minimum number of bytes received to be considered as filled/ready + }fifo_count; #endif + }; +}audio_feedback_params_t; + +// Invoked when needed to set feedback parameters +TU_ATTR_WEAK void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, audio_feedback_params_t* feedback_param); + +// Callback in ISR context, invoked periodically according to feedback endpoint bInterval. +// Could be used to compute and update feedback value, should be placed in RAM if possible +// frame_number : current SOF count +// interval_shift: number of bit shift i.e log2(interval) from Feedback endpoint descriptor +TU_ATTR_WEAK TU_ATTR_FAST_FUNC void tud_audio_feedback_interval_isr(uint8_t func_id, uint32_t frame_number, uint8_t interval_shift); + +#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP #if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN TU_ATTR_WEAK bool tud_audio_int_ctr_done_cb(uint8_t rhport, uint16_t n_bytes_copied); @@ -602,10 +671,12 @@ static inline uint16_t tud_audio_int_ctr_write(uint8_t const* buffer, uint16_t l #endif #if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP + static inline bool tud_audio_fb_set(uint32_t feedback) { return tud_audio_n_fb_set(0, feedback); } + #endif //--------------------------------------------------------------------+ @@ -616,6 +687,7 @@ void audiod_reset (uint8_t rhport); uint16_t audiod_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len); bool audiod_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); bool audiod_xfer_cb (uint8_t rhport, uint8_t edpt_addr, xfer_result_t result, uint32_t xferred_bytes); +void audiod_sof_isr (uint8_t rhport, uint32_t frame_count); #ifdef __cplusplus } diff --git a/src/class/bth/bth_device.c b/src/class/bth/bth_device.c index 8ef609622..f96bb3552 100755 --- a/src/class/bth/bth_device.c +++ b/src/class/bth/bth_device.c @@ -26,7 +26,7 @@ #include "tusb_option.h" -#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_BTH) +#if (CFG_TUD_ENABLED && CFG_TUD_BTH) //--------------------------------------------------------------------+ // INCLUDE @@ -59,10 +59,12 @@ CFG_TUSB_MEM_SECTION btd_interface_t _btd_itf; static bool bt_tx_data(uint8_t ep, void *data, uint16_t len) { + uint8_t const rhport = 0; + // skip if previous transfer not complete - TU_VERIFY(!usbd_edpt_busy(TUD_OPT_RHPORT, ep)); + TU_VERIFY(!usbd_edpt_busy(rhport, ep)); - TU_ASSERT(usbd_edpt_xfer(TUD_OPT_RHPORT, ep, data, len)); + TU_ASSERT(usbd_edpt_xfer(rhport, ep, data, len)); return true; } diff --git a/src/class/cdc/cdc_device.c b/src/class/cdc/cdc_device.c index 74c116192..fab6f0035 100644 --- a/src/class/cdc/cdc_device.c +++ b/src/class/cdc/cdc_device.c @@ -26,7 +26,7 @@ #include "tusb_option.h" -#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_CDC) +#if (CFG_TUD_ENABLED && CFG_TUD_CDC) #include "device/usbd.h" #include "device/usbd_pvt.h" @@ -80,30 +80,32 @@ typedef struct //--------------------------------------------------------------------+ CFG_TUSB_MEM_SECTION static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC]; -static void _prep_out_transaction (cdcd_interface_t* p_cdc) +static bool _prep_out_transaction (cdcd_interface_t* p_cdc) { - uint8_t const rhport = TUD_OPT_RHPORT; + uint8_t const rhport = 0; uint16_t available = tu_fifo_remaining(&p_cdc->rx_ff); // Prepare for incoming data but only allow what we can store in the ring buffer. // TODO Actually we can still carry out the transfer, keeping count of received bytes // and slowly move it to the FIFO when read(). // This pre-check reduces endpoint claiming - TU_VERIFY(available >= sizeof(p_cdc->epout_buf), ); + TU_VERIFY(available >= sizeof(p_cdc->epout_buf)); // claim endpoint - TU_VERIFY(usbd_edpt_claim(rhport, p_cdc->ep_out), ); + TU_VERIFY(usbd_edpt_claim(rhport, p_cdc->ep_out)); // fifo can be changed before endpoint is claimed available = tu_fifo_remaining(&p_cdc->rx_ff); if ( available >= sizeof(p_cdc->epout_buf) ) { - usbd_edpt_xfer(rhport, p_cdc->ep_out, p_cdc->epout_buf, sizeof(p_cdc->epout_buf)); + return usbd_edpt_xfer(rhport, p_cdc->ep_out, p_cdc->epout_buf, sizeof(p_cdc->epout_buf)); }else { // Release endpoint since we don't make any transfer usbd_edpt_release(rhport, p_cdc->ep_out); + + return false; } } @@ -143,7 +145,7 @@ uint32_t tud_cdc_n_available(uint8_t itf) uint32_t tud_cdc_n_read(uint8_t itf, void* buffer, uint32_t bufsize) { cdcd_interface_t* p_cdc = &_cdcd_itf[itf]; - uint32_t num_read = tu_fifo_read_n(&p_cdc->rx_ff, buffer, bufsize); + uint32_t num_read = tu_fifo_read_n(&p_cdc->rx_ff, buffer, (uint16_t) bufsize); _prep_out_transaction(p_cdc); return num_read; } @@ -166,7 +168,7 @@ void tud_cdc_n_read_flush (uint8_t itf) uint32_t tud_cdc_n_write(uint8_t itf, void const* buffer, uint32_t bufsize) { cdcd_interface_t* p_cdc = &_cdcd_itf[itf]; - uint16_t ret = tu_fifo_write_n(&p_cdc->tx_ff, buffer, bufsize); + uint16_t ret = tu_fifo_write_n(&p_cdc->tx_ff, buffer, (uint16_t) bufsize); // flush if queue more than packet size if ( tu_fifo_count(&p_cdc->tx_ff) >= BULK_PACKET_SIZE ) @@ -187,7 +189,7 @@ uint32_t tud_cdc_n_write_flush (uint8_t itf) // No data to send if ( !tu_fifo_count(&p_cdc->tx_ff) ) return 0; - uint8_t const rhport = TUD_OPT_RHPORT; + uint8_t const rhport = 0; // Claim the endpoint TU_VERIFY( usbd_edpt_claim(rhport, p_cdc->ep_in), 0 ); @@ -433,7 +435,7 @@ bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_ // Received new data if ( ep_addr == p_cdc->ep_out ) { - tu_fifo_write_n(&p_cdc->rx_ff, &p_cdc->epout_buf, xferred_bytes); + tu_fifo_write_n(&p_cdc->rx_ff, &p_cdc->epout_buf, (uint16_t) xferred_bytes); // Check for wanted char and invoke callback if needed if ( tud_cdc_rx_wanted_cb && (((signed char) p_cdc->wanted_char) != -1) ) diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index f4fb6c1d6..ee824cb4e 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -26,7 +26,7 @@ #include "tusb_option.h" -#if (TUSB_OPT_HOST_ENABLED && CFG_TUH_CDC) +#if (CFG_TUH_ENABLED && CFG_TUH_CDC) #include "host/usbh.h" #include "host/usbh_classdriver.h" @@ -100,29 +100,30 @@ bool tuh_cdc_send(uint8_t dev_addr, void const * p_data, uint32_t length, bool i { (void) is_notify; TU_VERIFY( tuh_cdc_mounted(dev_addr) ); - TU_VERIFY( p_data != NULL && length, TUSB_ERROR_INVALID_PARA); + TU_VERIFY( p_data != NULL && length); uint8_t const ep_out = cdch_data[dev_addr-1].ep_out; if ( usbh_edpt_busy(dev_addr, ep_out) ) return false; - return usbh_edpt_xfer(dev_addr, ep_out, (void*)(uintptr_t) p_data, length); + return usbh_edpt_xfer(dev_addr, ep_out, (void*)(uintptr_t) p_data, (uint16_t) length); } bool tuh_cdc_receive(uint8_t dev_addr, void * p_buffer, uint32_t length, bool is_notify) { (void) is_notify; TU_VERIFY( tuh_cdc_mounted(dev_addr) ); - TU_VERIFY( p_buffer != NULL && length, TUSB_ERROR_INVALID_PARA); + TU_VERIFY( p_buffer != NULL && length ); uint8_t const ep_in = cdch_data[dev_addr-1].ep_in; if ( usbh_edpt_busy(dev_addr, ep_in) ) return false; - return usbh_edpt_xfer(dev_addr, ep_in, p_buffer, length); + return usbh_edpt_xfer(dev_addr, ep_in, p_buffer, (uint16_t) length); } -bool tuh_cdc_set_control_line_state(uint8_t dev_addr, bool dtr, bool rts, tuh_control_complete_cb_t complete_cb) +bool tuh_cdc_set_control_line_state(uint8_t dev_addr, bool dtr, bool rts, tuh_xfer_cb_t complete_cb) { cdch_data_t const * p_cdc = get_itf(dev_addr); + tusb_control_request_t const request = { .bmRequestType_bit = @@ -132,13 +133,22 @@ bool tuh_cdc_set_control_line_state(uint8_t dev_addr, bool dtr, bool rts, tuh_co .direction = TUSB_DIR_OUT }, .bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE, - .wValue = (rts ? 2 : 0) | (dtr ? 1 : 0), - .wIndex = p_cdc->itf_num, + .wValue = tu_htole16((uint16_t) ((dtr ? 1u : 0u) | (rts ? 2u : 0u))), + .wIndex = tu_htole16(p_cdc->itf_num), .wLength = 0 }; - TU_ASSERT( tuh_control_xfer(dev_addr, &request, NULL, complete_cb) ); - return true; + tuh_xfer_t xfer = + { + .daddr = dev_addr, + .ep_addr = 0, + .setup = &request, + .buffer = NULL, + .complete_cb = complete_cb, + .user_data = 0 + }; + + return tuh_control_xfer(&xfer); } //--------------------------------------------------------------------+ @@ -151,6 +161,7 @@ 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) { + (void) rhport; (void) max_len; // Only support ACM subclass @@ -186,7 +197,7 @@ bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *it // notification endpoint tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc; - TU_ASSERT( usbh_edpt_open(rhport, dev_addr, desc_ep) ); + TU_ASSERT( tuh_edpt_open(dev_addr, desc_ep) ); p_cdc->ep_notif = desc_ep->bEndpointAddress; drv_len += tu_desc_len(p_desc); @@ -207,7 +218,7 @@ bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *it 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, desc_ep)); + TU_ASSERT(tuh_edpt_open(dev_addr, desc_ep)); if ( tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN ) { diff --git a/src/class/cdc/cdc_host.h b/src/class/cdc/cdc_host.h index 0d435138b..33dbd2efb 100644 --- a/src/class/cdc/cdc_host.h +++ b/src/class/cdc/cdc_host.h @@ -42,14 +42,14 @@ * \defgroup CDC_Serial_Host Host * @{ */ -bool tuh_cdc_set_control_line_state(uint8_t dev_addr, bool dtr, bool rts, tuh_control_complete_cb_t complete_cb); +bool tuh_cdc_set_control_line_state(uint8_t dev_addr, bool dtr, bool rts, tuh_xfer_cb_t complete_cb); -static inline bool tuh_cdc_connect(uint8_t dev_addr, tuh_control_complete_cb_t complete_cb) +static inline bool tuh_cdc_connect(uint8_t dev_addr, tuh_xfer_cb_t complete_cb) { return tuh_cdc_set_control_line_state(dev_addr, true, true, complete_cb); } -static inline bool tuh_cdc_disconnect(uint8_t dev_addr, tuh_control_complete_cb_t complete_cb) +static inline bool tuh_cdc_disconnect(uint8_t dev_addr, tuh_xfer_cb_t complete_cb) { return tuh_cdc_set_control_line_state(dev_addr, false, false, complete_cb); } diff --git a/src/class/cdc/cdc_rndis_host.c b/src/class/cdc/cdc_rndis_host.c index cc4ffd1cf..8f28f51ac 100644 --- a/src/class/cdc/cdc_rndis_host.c +++ b/src/class/cdc/cdc_rndis_host.c @@ -26,7 +26,7 @@ #include "tusb_option.h" -#if (TUSB_OPT_HOST_ENABLED && CFG_TUH_CDC && CFG_TUH_CDC_RNDIS) +#if (CFG_TUH_ENABLED && CFG_TUH_CDC && CFG_TUH_CDC_RNDIS) //--------------------------------------------------------------------+ // INCLUDE @@ -35,6 +35,16 @@ #include "cdc_host.h" #include "cdc_rndis_host.h" +#if 0 // TODO remove subtask related macros later +// Sub Task +#define OSAL_SUBTASK_BEGIN +#define OSAL_SUBTASK_END return TUSB_ERROR_NONE; + +#define STASK_RETURN(_error) return _error; +#define STASK_INVOKE(_subtask, _status) (_status) = _subtask +#define STASK_ASSERT(_cond) TU_VERIFY(_cond, TUSB_ERROR_OSAL_TASK_FAILED) +#endif + //--------------------------------------------------------------------+ // MACRO CONSTANT TYPEDEF //--------------------------------------------------------------------+ diff --git a/src/class/cdc/cdc_rndis_host.h b/src/class/cdc/cdc_rndis_host.h index 170cb3b0e..447cc4e97 100644 --- a/src/class/cdc/cdc_rndis_host.h +++ b/src/class/cdc/cdc_rndis_host.h @@ -50,7 +50,7 @@ typedef struct { }rndish_data_t; void rndish_init(void); -tusb_error_t rndish_open_subtask(uint8_t dev_addr, cdch_data_t *p_cdc); +bool rndish_open_subtask(uint8_t dev_addr, cdch_data_t *p_cdc); void rndish_xfer_isr(cdch_data_t *p_cdc, pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_bytes); void rndish_close(uint8_t dev_addr); diff --git a/src/class/dfu/dfu_device.c b/src/class/dfu/dfu_device.c index ddfa608e4..aa5891ca9 100644 --- a/src/class/dfu/dfu_device.c +++ b/src/class/dfu/dfu_device.c @@ -26,7 +26,7 @@ #include "tusb_option.h" -#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_DFU) +#if (CFG_TUD_ENABLED && CFG_TUD_DFU) #include "device/usbd.h" #include "device/usbd_pvt.h" @@ -167,6 +167,8 @@ uint16_t dfu_moded_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint8_t alt_count = 0; uint16_t drv_len = 0; + TU_VERIFY(itf_desc->bInterfaceSubClass == TUD_DFU_APP_SUBCLASS && itf_desc->bInterfaceProtocol == DFU_PROTOCOL_DFU, 0); + while(itf_desc->bInterfaceSubClass == TUD_DFU_APP_SUBCLASS && itf_desc->bInterfaceProtocol == DFU_PROTOCOL_DFU) { TU_ASSERT(max_len > drv_len, 0); diff --git a/src/class/dfu/dfu_rt_device.c b/src/class/dfu/dfu_rt_device.c index afee2aa1f..b9cd6096b 100644 --- a/src/class/dfu/dfu_rt_device.c +++ b/src/class/dfu/dfu_rt_device.c @@ -26,7 +26,7 @@ #include "tusb_option.h" -#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_DFU_RUNTIME) +#if (CFG_TUD_ENABLED && CFG_TUD_DFU_RUNTIME) #include "device/usbd.h" #include "device/usbd_pvt.h" diff --git a/src/class/hid/hid.h b/src/class/hid/hid.h index 9265a2ede..44a464be1 100644 --- a/src/class/hid/hid.h +++ b/src/class/hid/hid.h @@ -43,7 +43,7 @@ /** \defgroup ClassDriver_HID_Common Common Definitions * @{ */ - /// USB HID Descriptor +/// USB HID Descriptor typedef struct TU_ATTR_PACKED { uint8_t bLength; /**< Numeric expression that is the total size of the HID descriptor */ @@ -645,7 +645,7 @@ enum { #define HID_REPORT_SIZE_N(x, n) HID_REPORT_ITEM(x, RI_GLOBAL_REPORT_SIZE, RI_TYPE_GLOBAL, n) #define HID_REPORT_ID(x) HID_REPORT_ITEM(x, RI_GLOBAL_REPORT_ID, RI_TYPE_GLOBAL, 1), -#define HID_REPORT_ID_N(x) HID_REPORT_ITEM(x, RI_GLOBAL_REPORT_ID, RI_TYPE_GLOBAL, n), +#define HID_REPORT_ID_N(x, n) HID_REPORT_ITEM(x, RI_GLOBAL_REPORT_ID, RI_TYPE_GLOBAL, n), #define HID_REPORT_COUNT(x) HID_REPORT_ITEM(x, RI_GLOBAL_REPORT_COUNT, RI_TYPE_GLOBAL, 1) #define HID_REPORT_COUNT_N(x, n) HID_REPORT_ITEM(x, RI_GLOBAL_REPORT_COUNT, RI_TYPE_GLOBAL, n) @@ -1106,7 +1106,10 @@ enum {'8' , 0 }, /* 0x60 */ \ {'9' , 0 }, /* 0x61 */ \ {'0' , 0 }, /* 0x62 */ \ - {'0' , 0 }, /* 0x63 */ \ + {'.' , 0 }, /* 0x63 */ \ + {0 , 0 }, /* 0x64 */ \ + {0 , 0 }, /* 0x65 */ \ + {0 , 0 }, /* 0x66 */ \ {'=' , '=' }, /* 0x67 */ \ diff --git a/src/class/hid/hid_device.c b/src/class/hid/hid_device.c index 588b61254..8077e4deb 100644 --- a/src/class/hid/hid_device.c +++ b/src/class/hid/hid_device.c @@ -26,7 +26,7 @@ #include "tusb_option.h" -#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_HID) +#if (CFG_TUD_ENABLED && CFG_TUD_HID) //--------------------------------------------------------------------+ // INCLUDE @@ -76,11 +76,12 @@ static inline uint8_t get_index_by_itfnum(uint8_t itf_num) //--------------------------------------------------------------------+ bool tud_hid_n_ready(uint8_t instance) { + uint8_t const rhport = 0; uint8_t const ep_in = _hidd_itf[instance].ep_in; - return tud_ready() && (ep_in != 0) && !usbd_edpt_busy(TUD_OPT_RHPORT, ep_in); + return tud_ready() && (ep_in != 0) && !usbd_edpt_busy(rhport, ep_in); } -bool tud_hid_n_report(uint8_t instance, uint8_t report_id, void const* report, uint8_t len) +bool tud_hid_n_report(uint8_t instance, uint8_t report_id, void const* report, uint16_t len) { uint8_t const rhport = 0; hidd_interface_t * p_hid = &_hidd_itf[instance]; @@ -91,7 +92,7 @@ bool tud_hid_n_report(uint8_t instance, uint8_t report_id, void const* report, u // prepare data if (report_id) { - len = tu_min8(len, CFG_TUD_HID_EP_BUFSIZE-1); + len = tu_min16(len, CFG_TUD_HID_EP_BUFSIZE-1); p_hid->epin_buf[0] = report_id; memcpy(p_hid->epin_buf+1, report, len); @@ -99,11 +100,11 @@ bool tud_hid_n_report(uint8_t instance, uint8_t report_id, void const* report, u }else { // If report id = 0, skip ID field - len = tu_min8(len, CFG_TUD_HID_EP_BUFSIZE); + len = tu_min16(len, CFG_TUD_HID_EP_BUFSIZE); memcpy(p_hid->epin_buf, report, len); } - return usbd_edpt_xfer(TUD_OPT_RHPORT, p_hid->ep_in, p_hid->epin_buf, len); + return usbd_edpt_xfer(rhport, p_hid->ep_in, p_hid->epin_buf, len); } uint8_t tud_hid_n_interface_protocol(uint8_t instance) @@ -172,7 +173,7 @@ bool tud_hid_n_gamepad_report(uint8_t instance, uint8_t report_id, //--------------------------------------------------------------------+ void hidd_init(void) { - hidd_reset(TUD_OPT_RHPORT); + hidd_reset(0); } void hidd_reset(uint8_t rhport) @@ -186,7 +187,8 @@ uint16_t hidd_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint1 TU_VERIFY(TUSB_CLASS_HID == desc_itf->bInterfaceClass, 0); // 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); + uint16_t const drv_len = (uint16_t) (sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) + + desc_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t)); TU_ASSERT(max_len >= drv_len, 0); // Find available interface @@ -401,13 +403,13 @@ bool hidd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_ { if (tud_hid_report_complete_cb) { - tud_hid_report_complete_cb(instance, p_hid->epin_buf, (uint8_t) xferred_bytes); + tud_hid_report_complete_cb(instance, p_hid->epin_buf, (/*uint16_t*/ uint8_t) xferred_bytes); } } // Received report else if (ep_addr == p_hid->ep_out) { - tud_hid_set_report_cb(instance, 0, HID_REPORT_TYPE_INVALID, p_hid->epout_buf, xferred_bytes); + tud_hid_set_report_cb(instance, 0, HID_REPORT_TYPE_INVALID, p_hid->epout_buf, (uint16_t) xferred_bytes); TU_ASSERT(usbd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf))); } diff --git a/src/class/hid/hid_device.h b/src/class/hid/hid_device.h index 7f67fa9cf..3143b1024 100644 --- a/src/class/hid/hid_device.h +++ b/src/class/hid/hid_device.h @@ -62,7 +62,7 @@ uint8_t tud_hid_n_interface_protocol(uint8_t instance); uint8_t tud_hid_n_get_protocol(uint8_t instance); // Send report to host -bool tud_hid_n_report(uint8_t instance, uint8_t report_id, void const* report, uint8_t len); +bool tud_hid_n_report(uint8_t instance, uint8_t report_id, void const* report, uint16_t len); // KEYBOARD: convenient helper to send keyboard report if application // use template layout report as defined by hid_keyboard_report_t @@ -82,7 +82,7 @@ bool tud_hid_n_gamepad_report(uint8_t instance, uint8_t report_id, int8_t x, int static inline bool tud_hid_ready(void); static inline uint8_t tud_hid_interface_protocol(void); static inline uint8_t tud_hid_get_protocol(void); -static inline bool tud_hid_report(uint8_t report_id, void const* report, uint8_t len); +static inline bool tud_hid_report(uint8_t report_id, void const* report, uint16_t len); static inline bool tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, uint8_t keycode[6]); static inline bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal); static inline bool tud_hid_gamepad_report(uint8_t report_id, int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons); @@ -116,7 +116,7 @@ TU_ATTR_WEAK bool tud_hid_set_idle_cb(uint8_t instance, uint8_t idle_rate); // Invoked when sent REPORT successfully to host // Application can use this to send the next report // Note: For composite reports, report[0] is report ID -TU_ATTR_WEAK void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, uint8_t len); +TU_ATTR_WEAK void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, /*uint16_t*/ uint8_t len ); //--------------------------------------------------------------------+ @@ -137,7 +137,7 @@ static inline uint8_t tud_hid_get_protocol(void) return tud_hid_n_get_protocol(0); } -static inline bool tud_hid_report(uint8_t report_id, void const* report, uint8_t len) +static inline bool tud_hid_report(uint8_t report_id, void const* report, uint16_t len) { return tud_hid_n_report(0, report_id, report, len); } @@ -310,8 +310,8 @@ static inline bool tud_hid_gamepad_report(uint8_t report_id, int8_t x, int8_t y HID_COLLECTION_END \ // Gamepad Report Descriptor Template -// with 16 buttons, 2 joysticks and 1 hat/dpad with following layout -// | X | Y | Z | Rz | Rx | Ry (1 byte each) | hat/DPAD (1 byte) | Button Map (2 bytes) | +// with 32 buttons, 2 joysticks and 1 hat/dpad with following layout +// | X | Y | Z | Rz | Rx | Ry (1 byte each) | hat/DPAD (1 byte) | Button Map (4 bytes) | #define TUD_HID_REPORT_DESC_GAMEPAD(...) \ HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\ HID_USAGE ( HID_USAGE_DESKTOP_GAMEPAD ) ,\ @@ -319,37 +319,37 @@ static inline bool tud_hid_gamepad_report(uint8_t report_id, int8_t x, int8_t y /* Report ID if any */\ __VA_ARGS__ \ /* 8 bit X, Y, Z, Rz, Rx, Ry (min -127, max 127 ) */ \ - HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\ - HID_USAGE ( HID_USAGE_DESKTOP_X ) ,\ - HID_USAGE ( HID_USAGE_DESKTOP_Y ) ,\ - HID_USAGE ( HID_USAGE_DESKTOP_Z ) ,\ - HID_USAGE ( HID_USAGE_DESKTOP_RZ ) ,\ - HID_USAGE ( HID_USAGE_DESKTOP_RX ) ,\ - HID_USAGE ( HID_USAGE_DESKTOP_RY ) ,\ - HID_LOGICAL_MIN ( 0x81 ) ,\ - HID_LOGICAL_MAX ( 0x7f ) ,\ - HID_REPORT_COUNT ( 6 ) ,\ - HID_REPORT_SIZE ( 8 ) ,\ - HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\ + HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\ + HID_USAGE ( HID_USAGE_DESKTOP_X ) ,\ + HID_USAGE ( HID_USAGE_DESKTOP_Y ) ,\ + HID_USAGE ( HID_USAGE_DESKTOP_Z ) ,\ + HID_USAGE ( HID_USAGE_DESKTOP_RZ ) ,\ + HID_USAGE ( HID_USAGE_DESKTOP_RX ) ,\ + HID_USAGE ( HID_USAGE_DESKTOP_RY ) ,\ + HID_LOGICAL_MIN ( 0x81 ) ,\ + HID_LOGICAL_MAX ( 0x7f ) ,\ + HID_REPORT_COUNT ( 6 ) ,\ + HID_REPORT_SIZE ( 8 ) ,\ + HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\ /* 8 bit DPad/Hat Button Map */ \ - HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\ - HID_USAGE ( HID_USAGE_DESKTOP_HAT_SWITCH ) ,\ - HID_LOGICAL_MIN ( 1 ) ,\ - HID_LOGICAL_MAX ( 8 ) ,\ - HID_PHYSICAL_MIN ( 0 ) ,\ - HID_PHYSICAL_MAX_N ( 315, 2 ) ,\ - HID_REPORT_COUNT ( 1 ) ,\ - HID_REPORT_SIZE ( 8 ) ,\ - HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\ - /* 16 bit Button Map */ \ - HID_USAGE_PAGE ( HID_USAGE_PAGE_BUTTON ) ,\ - HID_USAGE_MIN ( 1 ) ,\ - HID_USAGE_MAX ( 32 ) ,\ - HID_LOGICAL_MIN ( 0 ) ,\ - HID_LOGICAL_MAX ( 1 ) ,\ - HID_REPORT_COUNT ( 32 ) ,\ - HID_REPORT_SIZE ( 1 ) ,\ - HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\ + HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\ + HID_USAGE ( HID_USAGE_DESKTOP_HAT_SWITCH ) ,\ + HID_LOGICAL_MIN ( 1 ) ,\ + HID_LOGICAL_MAX ( 8 ) ,\ + HID_PHYSICAL_MIN ( 0 ) ,\ + HID_PHYSICAL_MAX_N ( 315, 2 ) ,\ + HID_REPORT_COUNT ( 1 ) ,\ + HID_REPORT_SIZE ( 8 ) ,\ + HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\ + /* 32 bit Button Map */ \ + HID_USAGE_PAGE ( HID_USAGE_PAGE_BUTTON ) ,\ + HID_USAGE_MIN ( 1 ) ,\ + HID_USAGE_MAX ( 32 ) ,\ + HID_LOGICAL_MIN ( 0 ) ,\ + HID_LOGICAL_MAX ( 1 ) ,\ + HID_REPORT_COUNT ( 32 ) ,\ + HID_REPORT_SIZE ( 1 ) ,\ + HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\ HID_COLLECTION_END \ // HID Generic Input & Output diff --git a/src/class/hid/hid_host.c b/src/class/hid/hid_host.c index f19f1ba81..ca745464c 100644 --- a/src/class/hid/hid_host.c +++ b/src/class/hid/hid_host.c @@ -26,7 +26,7 @@ #include "tusb_option.h" -#if (TUSB_OPT_HOST_ENABLED && CFG_TUH_HID) +#if (CFG_TUH_ENABLED && CFG_TUH_HID) #include "host/usbh.h" #include "host/usbh_classdriver.h" @@ -103,27 +103,27 @@ uint8_t tuh_hid_get_protocol(uint8_t dev_addr, uint8_t instance) return hid_itf->protocol_mode; } -static bool set_protocol_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result) +static void set_protocol_complete(tuh_xfer_t* xfer) { - 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) tu_le16toh(xfer->setup->wIndex); + uint8_t const daddr = xfer->daddr; + uint8_t const instance = get_instance_id_by_itfnum(daddr, itf_num); + hidh_interface_t* hid_itf = get_instance(daddr, instance); - if (XFER_RESULT_SUCCESS == result) hid_itf->protocol_mode = (uint8_t) request->wValue; + if (XFER_RESULT_SUCCESS == xfer->result) + { + hid_itf->protocol_mode = (uint8_t) tu_le16toh(xfer->setup->wValue); + } if (tuh_hid_set_protocol_complete_cb) { - tuh_hid_set_protocol_complete_cb(dev_addr, instance, hid_itf->protocol_mode); + tuh_hid_set_protocol_complete_cb(daddr, instance, hid_itf->protocol_mode); } - - return true; } -bool tuh_hid_set_protocol(uint8_t dev_addr, uint8_t instance, uint8_t protocol) -{ - hidh_interface_t* hid_itf = get_instance(dev_addr, instance); - TU_VERIFY(hid_itf->itf_protocol != HID_ITF_PROTOCOL_NONE); +static bool _hidh_set_protocol(uint8_t dev_addr, uint8_t itf_num, uint8_t protocol, tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ TU_LOG2("HID Set Protocol = %d\r\n", protocol); tusb_control_request_t const request = @@ -136,30 +136,47 @@ bool tuh_hid_set_protocol(uint8_t dev_addr, uint8_t instance, uint8_t protocol) }, .bRequest = HID_REQ_CONTROL_SET_PROTOCOL, .wValue = protocol, - .wIndex = hid_itf->itf_num, + .wIndex = itf_num, .wLength = 0 }; - TU_ASSERT( tuh_control_xfer(dev_addr, &request, NULL, set_protocol_complete) ); + tuh_xfer_t xfer = + { + .daddr = dev_addr, + .ep_addr = 0, + .setup = &request, + .buffer = NULL, + .complete_cb = complete_cb, + .user_data = user_data + }; + + TU_ASSERT( tuh_control_xfer(&xfer) ); return true; } -static bool set_report_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result) +bool tuh_hid_set_protocol(uint8_t dev_addr, uint8_t instance, uint8_t protocol) +{ + hidh_interface_t* hid_itf = get_instance(dev_addr, instance); + TU_VERIFY(hid_itf->itf_protocol != HID_ITF_PROTOCOL_NONE); + + return _hidh_set_protocol(dev_addr, hid_itf->itf_num, protocol, set_protocol_complete, 0); +} + +static void set_report_complete(tuh_xfer_t* xfer) { TU_LOG2("HID Set Report complete\r\n"); if (tuh_hid_set_report_complete_cb) { - 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 itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); + uint8_t const instance = get_instance_id_by_itfnum(xfer->daddr, itf_num); - uint8_t const report_type = tu_u16_high(request->wValue); - uint8_t const report_id = tu_u16_low(request->wValue); + uint8_t const report_type = tu_u16_high(xfer->setup->wValue); + uint8_t const report_id = tu_u16_low(xfer->setup->wValue); - tuh_hid_set_report_complete_cb(dev_addr, instance, report_id, report_type, (result == XFER_RESULT_SUCCESS) ? request->wLength : 0); + tuh_hid_set_report_complete_cb(xfer->daddr, instance, report_id, report_type, + (xfer->result == XFER_RESULT_SUCCESS) ? xfer->setup->wLength : 0); } - - return true; } 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) @@ -181,7 +198,50 @@ bool tuh_hid_set_report(uint8_t dev_addr, uint8_t instance, uint8_t report_id, u .wLength = len }; - TU_ASSERT( tuh_control_xfer(dev_addr, &request, report, set_report_complete) ); + tuh_xfer_t xfer = + { + .daddr = dev_addr, + .ep_addr = 0, + .setup = &request, + .buffer = report, + .complete_cb = set_report_complete, + .user_data = 0 + }; + + TU_ASSERT( tuh_control_xfer(&xfer) ); + return true; +} + +static bool _hidh_set_idle(uint8_t dev_addr, uint8_t itf_num, uint16_t idle_rate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + // SET IDLE request, device can stall if not support this request + TU_LOG2("HID Set Idle \r\n"); + tusb_control_request_t const request = + { + .bmRequestType_bit = + { + .recipient = TUSB_REQ_RCPT_INTERFACE, + .type = TUSB_REQ_TYPE_CLASS, + .direction = TUSB_DIR_OUT + }, + .bRequest = HID_REQ_CONTROL_SET_IDLE, + .wValue = idle_rate, + .wIndex = itf_num, + .wLength = 0 + }; + + tuh_xfer_t xfer = + { + .daddr = dev_addr, + .ep_addr = 0, + .setup = &request, + .buffer = NULL, + .complete_cb = complete_cb, + .user_data = user_data + }; + + TU_ASSERT( tuh_control_xfer(&xfer) ); + return true; } @@ -196,7 +256,13 @@ bool tuh_hid_receive_report(uint8_t dev_addr, uint8_t 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); + if ( !usbh_edpt_xfer(dev_addr, hid_itf->ep_in, hid_itf->epin_buf, hid_itf->epin_size) ) + { + usbh_edpt_claim(dev_addr, hid_itf->ep_in); + return false; + } + + return true; } //bool tuh_n_hid_n_ready(uint8_t dev_addr, uint8_t instance) @@ -229,10 +295,10 @@ bool hidh_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint3 { TU_LOG2(" Get Report callback (%u, %u)\r\n", dev_addr, instance); TU_LOG3_MEM(hid_itf->epin_buf, xferred_bytes, 2); - tuh_hid_report_received_cb(dev_addr, instance, hid_itf->epin_buf, xferred_bytes); + tuh_hid_report_received_cb(dev_addr, instance, hid_itf->epin_buf, (uint16_t) xferred_bytes); }else { - if (tuh_hid_report_sent_cb) tuh_hid_report_sent_cb(dev_addr, instance, hid_itf->epout_buf, xferred_bytes); + if (tuh_hid_report_sent_cb) tuh_hid_report_sent_cb(dev_addr, instance, hid_itf->epout_buf, (uint16_t) xferred_bytes); } return true; @@ -256,22 +322,18 @@ void hidh_close(uint8_t dev_addr) // Enumeration //--------------------------------------------------------------------+ -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); - -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) rhport; (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); + TU_LOG2("[%u] HID opening Interface %u\r\n", dev_addr, desc_itf->bInterfaceNumber); // 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); + uint16_t const drv_len = (uint16_t) (sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) + + desc_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t)); TU_ASSERT(max_len >= drv_len); uint8_t const *p_desc = (uint8_t const *) desc_itf; @@ -286,28 +348,35 @@ bool hidh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *de hidh_device_t* hid_dev = get_dev(dev_addr); TU_ASSERT(hid_dev->inst_count < CFG_TUH_HID, 0); - //------------- Endpoint Descriptor -------------// + hidh_interface_t* hid_itf = get_instance(dev_addr, hid_dev->inst_count); + + //------------- Endpoint Descriptors -------------// p_desc = tu_desc_next(p_desc); tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc; - TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType); - // first endpoint may be OUT, skip to IN endpoint - // TODO also open endpoint OUT - if(tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_OUT) + for(int i = 0; i < desc_itf->bNumEndpoints; i++) { + TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType); + TU_ASSERT( tuh_edpt_open(dev_addr, desc_ep) ); + + if(tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) + { + hid_itf->ep_in = desc_ep->bEndpointAddress; + hid_itf->epin_size = tu_edpt_packet_size(desc_ep); + } + else + { + hid_itf->ep_out = desc_ep->bEndpointAddress; + hid_itf->epout_size = tu_edpt_packet_size(desc_ep); + } + p_desc = tu_desc_next(p_desc); desc_ep = (tusb_desc_endpoint_t const *) p_desc; - TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType); } - TU_ASSERT( usbh_edpt_open(rhport, dev_addr, desc_ep) ); - - hidh_interface_t* hid_itf = get_instance(dev_addr, hid_dev->inst_count); hid_dev->inst_count++; hid_itf->itf_num = desc_itf->bInterfaceNumber; - hid_itf->ep_in = desc_ep->bEndpointAddress; - hid_itf->epin_size = tu_edpt_packet_size(desc_ep); // Assume bNumDescriptors = 1 hid_itf->report_desc_type = desc_hid->bReportType; @@ -320,122 +389,93 @@ bool hidh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *de return true; } -bool hidh_set_config(uint8_t dev_addr, uint8_t itf_num) -{ - uint8_t const instance = get_instance_id_by_itfnum(dev_addr, itf_num); - hidh_interface_t* hid_itf = get_instance(dev_addr, instance); - - // Idle rate = 0 mean only report when there is changes - uint16_t const idle_rate = 0; - - // SET IDLE request, device can stall if not support this request - TU_LOG2("HID Set Idle \r\n"); - tusb_control_request_t const request = - { - .bmRequestType_bit = - { - .recipient = TUSB_REQ_RCPT_INTERFACE, - .type = TUSB_REQ_TYPE_CLASS, - .direction = TUSB_DIR_OUT - }, - .bRequest = HID_REQ_CONTROL_SET_IDLE, - .wValue = idle_rate, - .wIndex = itf_num, - .wLength = 0 - }; +//--------------------------------------------------------------------+ +// Set Configure +//--------------------------------------------------------------------+ - TU_ASSERT( tuh_control_xfer(dev_addr, &request, NULL, (hid_itf->itf_protocol != HID_ITF_PROTOCOL_NONE) ? config_set_protocol : config_get_report_desc) ); +enum { + CONFG_SET_IDLE, + CONFIG_SET_PROTOCOL, + CONFIG_GET_REPORT_DESC, + CONFIG_COMPLETE +}; - return true; -} +static void config_driver_mount_complete(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len); +static void process_set_config(tuh_xfer_t* xfer); -// 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) +bool hidh_set_config(uint8_t dev_addr, uint8_t itf_num) { - // Stall is a valid response for SET_IDLE, therefore we could ignore its result - (void) result; + tusb_control_request_t request; + request.wIndex = tu_htole16((uint16_t) itf_num); - 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); + tuh_xfer_t xfer; + xfer.daddr = dev_addr; + xfer.result = XFER_RESULT_SUCCESS; + xfer.setup = &request; + xfer.user_data = CONFG_SET_IDLE; - 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_OUT - }, - .bRequest = HID_REQ_CONTROL_SET_PROTOCOL, - .wValue = HID_PROTOCOL_BOOT, - .wIndex = hid_itf->itf_num, - .wLength = 0 - }; + // fake request to kick-off the set config process + process_set_config(&xfer); - 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) +static void process_set_config(tuh_xfer_t* xfer) { - // 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 ) + // Stall is a valid response for SET_IDLE, therefore we could ignore its result + if ( xfer->setup->bRequest != HID_REQ_CONTROL_SET_IDLE ) { - TU_ASSERT(result == XFER_RESULT_SUCCESS); + TU_ASSERT(xfer->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); + uintptr_t const state = xfer->user_data; + uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); + uint8_t const daddr = xfer->daddr; - // Get Report Descriptor if possible - // using usbh enumeration buffer since report descriptor can be very long - if( hid_itf->report_desc_len > CFG_TUH_ENUMERATION_BUFSIZE ) - { - TU_LOG2("HID Skip Report Descriptor since it is too large %u bytes\r\n", hid_itf->report_desc_len); + uint8_t const instance = get_instance_id_by_itfnum(daddr, itf_num); + hidh_interface_t* hid_itf = get_instance(daddr, instance); - // Driver is mounted without report descriptor - config_driver_mount_complete(dev_addr, instance, NULL, 0); - }else + switch(state) { - TU_LOG2("HID Get Report Descriptor\r\n"); - tusb_control_request_t const new_request = + case CONFG_SET_IDLE: { - .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)); - } + // Idle rate = 0 mean only report when there is changes + const uint16_t idle_rate = 0; + const uintptr_t next_state = (hid_itf->itf_protocol != HID_ITF_PROTOCOL_NONE) ? CONFIG_SET_PROTOCOL : CONFIG_GET_REPORT_DESC; + _hidh_set_idle(daddr, itf_num, idle_rate, process_set_config, next_state); + } + break; - return true; -} + case CONFIG_SET_PROTOCOL: + _hidh_set_protocol(daddr, hid_itf->itf_num, HID_PROTOCOL_BOOT, process_set_config, CONFIG_GET_REPORT_DESC); + break; -static bool config_get_report_desc_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result) -{ - TU_ASSERT(XFER_RESULT_SUCCESS == result); + case CONFIG_GET_REPORT_DESC: + // Get Report Descriptor if possible + // using usbh enumeration buffer since report descriptor can be very long + if( hid_itf->report_desc_len > CFG_TUH_ENUMERATION_BUFSIZE ) + { + TU_LOG2("HID Skip Report Descriptor since it is too large %u bytes\r\n", hid_itf->report_desc_len); - uint8_t const itf_num = (uint8_t) request->wIndex; - uint8_t const instance = get_instance_id_by_itfnum(dev_addr, itf_num); + // Driver is mounted without report descriptor + config_driver_mount_complete(daddr, instance, NULL, 0); + }else + { + tuh_descriptor_get_hid_report(daddr, itf_num, hid_itf->report_desc_type, 0, usbh_get_enum_buf(), hid_itf->report_desc_len, process_set_config, CONFIG_COMPLETE); + } + break; - uint8_t const* desc_report = usbh_get_enum_buf(); - uint16_t const desc_len = request->wLength; + case CONFIG_COMPLETE: + { + uint8_t const* desc_report = usbh_get_enum_buf(); + uint16_t const desc_len = tu_le16toh(xfer->setup->wLength); - config_driver_mount_complete(dev_addr, instance, desc_report, desc_len); + config_driver_mount_complete(daddr, instance, desc_report, desc_len); + } + break; - return true; + default: break; + } } static void config_driver_mount_complete(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len) diff --git a/src/class/midi/midi_device.c b/src/class/midi/midi_device.c index b08b362f9..de41706e8 100644 --- a/src/class/midi/midi_device.c +++ b/src/class/midi/midi_device.c @@ -26,7 +26,7 @@ #include "tusb_option.h" -#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_MIDI) +#if (CFG_TUD_ENABLED && CFG_TUD_MIDI) //--------------------------------------------------------------------+ // INCLUDE @@ -92,7 +92,7 @@ bool tud_midi_n_mounted (uint8_t itf) static void _prep_out_transaction (midid_interface_t* p_midi) { - uint8_t const rhport = TUD_OPT_RHPORT; + uint8_t const rhport = 0; uint16_t available = tu_fifo_remaining(&p_midi->rx_ff); // Prepare for incoming data but only allow what we can store in the ring buffer. @@ -127,7 +127,7 @@ uint32_t tud_midi_n_available(uint8_t itf, uint8_t cable_num) 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); + return tu_fifo_count(&midi->rx_ff) + (uint8_t) (stream->total - stream->index); } uint32_t tud_midi_n_stream_read(uint8_t itf, uint8_t cable_num, void* buffer, uint32_t bufsize) @@ -179,7 +179,7 @@ uint32_t tud_midi_n_stream_read(uint8_t itf, uint8_t cable_num, void* buffer, ui } // Copy data up to bufsize - uint32_t const count = tu_min32(stream->total - stream->index, bufsize); + uint8_t const count = (uint8_t) tu_min32(stream->total - stream->index, bufsize); // Skip the header (1st byte) in the buffer memcpy(buf8, stream->buffer + 1 + stream->index, count); @@ -219,7 +219,7 @@ static uint32_t write_flush(midid_interface_t* midi) // No data to send if ( !tu_fifo_count(&midi->tx_ff) ) return 0; - uint8_t const rhport = TUD_OPT_RHPORT; + uint8_t const rhport = 0; // skip if previous transfer not complete TU_VERIFY( usbd_edpt_claim(rhport, midi->ep_in), 0 ); @@ -276,13 +276,13 @@ uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, uint8_t const* else if ( (msg >= 0x8 && msg <= 0xB) || msg == 0xE ) { // Channel Voice Messages - stream->buffer[0] = (cable_num << 4) | msg; + stream->buffer[0] = (uint8_t) ((cable_num << 4) | msg); stream->total = 4; } else if ( msg == 0xC || msg == 0xD) { // Channel Voice Messages, two-byte variants (Program Change and Channel Pressure) - stream->buffer[0] = (cable_num << 4) | msg; + stream->buffer[0] = (uint8_t) ((cable_num << 4) | msg); stream->total = 3; } else if ( msg == 0xf ) @@ -312,7 +312,7 @@ uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, uint8_t const* else { // Pack individual bytes if we don't support packing them into words. - stream->buffer[0] = cable_num << 4 | 0xf; + stream->buffer[0] = (uint8_t) (cable_num << 4 | 0xf); stream->buffer[2] = 0; stream->buffer[3] = 0; stream->index = 2; @@ -513,7 +513,7 @@ bool midid_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32 // receive new data if ( ep_addr == p_midi->ep_out ) { - tu_fifo_write_n(&p_midi->rx_ff, p_midi->epout_buf, xferred_bytes); + tu_fifo_write_n(&p_midi->rx_ff, p_midi->epout_buf, (uint16_t) xferred_bytes); // invoke receive callback if available if (tud_midi_rx_cb) tud_midi_rx_cb(itf); diff --git a/src/class/msc/msc_device.c b/src/class/msc/msc_device.c index 97837b114..00b0a1d06 100644 --- a/src/class/msc/msc_device.c +++ b/src/class/msc/msc_device.c @@ -26,11 +26,11 @@ #include "tusb_option.h" -#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_MSC) +#if (CFG_TUD_ENABLED && CFG_TUD_MSC) +#include "device/dcd.h" // for faking dcd_event_xfer_complete #include "device/usbd.h" #include "device/usbd_pvt.h" -#include "device/dcd.h" // for faking dcd_event_xfer_complete #include "msc_device.h" @@ -209,7 +209,7 @@ TU_ATTR_UNUSED static tu_lookup_entry_t const _msc_scsi_cmd_lookup[] = { .key = SCSI_CMD_MODE_SELECT_6 , .data = "Mode_Select 6" }, { .key = SCSI_CMD_MODE_SENSE_6 , .data = "Mode_Sense 6" }, { .key = SCSI_CMD_START_STOP_UNIT , .data = "Start Stop Unit" }, - { .key = SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL , .data = "Prevent Allow Medium Removal" }, + { .key = SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL , .data = "Prevent/Allow Medium Removal" }, { .key = SCSI_CMD_READ_CAPACITY_10 , .data = "Read Capacity10" }, { .key = SCSI_CMD_REQUEST_SENSE , .data = "Request Sense" }, { .key = SCSI_CMD_READ_FORMAT_CAPACITY , .data = "Read Format Capacity" }, @@ -239,6 +239,12 @@ bool tud_msc_set_sense(uint8_t lun, uint8_t sense_key, uint8_t add_sense_code, u return true; } +static inline void set_sense_medium_not_present(uint8_t lun) +{ + // default sense is NOT READY, MEDIUM NOT PRESENT + tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3A, 0x00); +} + //--------------------------------------------------------------------+ // USBD Driver API //--------------------------------------------------------------------+ @@ -406,7 +412,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t return false; } - TU_LOG(MSC_DEBUG, " SCSI Command: %s\r\n", tu_lookup_find(&_msc_scsi_cmd_table, p_cbw->command[0])); + TU_LOG(MSC_DEBUG, " SCSI Command [Lun%u]: %s\r\n", p_cbw->lun, tu_lookup_find(&_msc_scsi_cmd_table, p_cbw->command[0])); //TU_LOG_MEM(MSC_DEBUG, p_cbw, xferred_bytes, 2); p_csw->signature = MSC_CSW_SIGNATURE; @@ -457,7 +463,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t { // 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) ); + TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, (uint16_t) p_msc->total_len) ); } }else { @@ -467,13 +473,13 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t // 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); + resplen = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_buf, (uint16_t) p_msc->total_len); } if ( resplen < 0 ) { // unsupported command - TU_LOG(MSC_DEBUG, " SCSI unsupported command\r\n"); + TU_LOG(MSC_DEBUG, " SCSI unsupported or failed command\r\n"); fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED); } else if (resplen == 0) @@ -500,7 +506,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t { // 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) ); + TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, (uint16_t) p_msc->total_len) ); } } } @@ -508,7 +514,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t break; case MSC_STAGE_DATA: - TU_LOG(MSC_DEBUG, " SCSI Data\r\n"); + TU_LOG(MSC_DEBUG, " SCSI Data [Lun%u]\r\n", p_cbw->lun); //TU_LOG_MEM(MSC_DEBUG, _mscd_buf, xferred_bytes, 2); if (SCSI_CMD_READ_10 == p_cbw->command[0]) @@ -535,7 +541,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t // 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); + int32_t cb_result = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_buf, (uint16_t) p_msc->total_len); if ( cb_result < 0 ) { @@ -569,7 +575,7 @@ 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_LOG(MSC_DEBUG, " SCSI Status = %u\r\n", p_csw->status); + TU_LOG(MSC_DEBUG, " SCSI Status [Lun%u] = %u\r\n", p_cbw->lun, p_csw->status); // TU_LOG_MEM(MSC_DEBUG, p_csw, xferred_bytes, 2); // Invoke complete callback if defined @@ -654,8 +660,8 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_ // Failed status response resplen = - 1; - // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable - if ( p_msc->sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00); + // set default sense if not set by callback + if ( p_msc->sense_key == 0 ) set_sense_medium_not_present(lun); } break; @@ -670,8 +676,8 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_ // Failed status response resplen = - 1; - // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable - if ( p_msc->sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00); + // set default sense if not set by callback + if ( p_msc->sense_key == 0 ) set_sense_medium_not_present(lun); } } break; @@ -691,17 +697,17 @@ 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 ( p_msc->sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00); + // set default sense if not set by callback + if ( p_msc->sense_key == 0 ) set_sense_medium_not_present(lun); }else { scsi_read_capacity10_resp_t read_capa10; - read_capa10.last_lba = tu_htonl(block_count-1); + read_capa10.last_lba = tu_htonl(block_count-1); read_capa10.block_size = tu_htonl(block_size); resplen = sizeof(read_capa10); - memcpy(buffer, &read_capa10, resplen); + memcpy(buffer, &read_capa10, (size_t) resplen); } } break; @@ -727,15 +733,15 @@ 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 ( p_msc->sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00); + // set default sense if not set by callback + if ( p_msc->sense_key == 0 ) set_sense_medium_not_present(lun); }else { read_fmt_capa.block_num = tu_htonl(block_count); read_fmt_capa.block_size_u16 = tu_htons(block_size); resplen = sizeof(read_fmt_capa); - memcpy(buffer, &read_fmt_capa, resplen); + memcpy(buffer, &read_fmt_capa, (size_t) resplen); } } break; @@ -747,6 +753,7 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_ .is_removable = 1, .version = 2, .response_data_format = 2, + .additional_length = sizeof(scsi_inquiry_resp_t) - 5, }; // vendor_id, product_id, product_rev is space padded string @@ -757,7 +764,7 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_ tud_msc_inquiry_cb(lun, inquiry_rsp.vendor_id, inquiry_rsp.product_id, inquiry_rsp.product_rev); resplen = sizeof(inquiry_rsp); - memcpy(buffer, &inquiry_rsp, resplen); + memcpy(buffer, &inquiry_rsp, (size_t) resplen); } break; @@ -765,10 +772,10 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_ { scsi_mode_sense6_resp_t mode_resp = { - .data_len = 3, - .medium_type = 0, - .write_protected = false, - .reserved = 0, + .data_len = 3, + .medium_type = 0, + .write_protected = false, + .reserved = 0, .block_descriptor_len = 0 // no block descriptor are included }; @@ -781,7 +788,7 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_ mode_resp.write_protected = !writable; resplen = sizeof(mode_resp); - memcpy(buffer, &mode_resp, resplen); + memcpy(buffer, &mode_resp, (size_t) resplen); } break; @@ -789,18 +796,23 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_ { scsi_sense_fixed_resp_t sense_rsp = { - .response_code = 0x70, + .response_code = 0x70, // current, fixed format .valid = 1 }; - sense_rsp.add_sense_len = sizeof(scsi_sense_fixed_resp_t) - 8; - - sense_rsp.sense_key = p_msc->sense_key; + sense_rsp.add_sense_len = sizeof(scsi_sense_fixed_resp_t) - 8; + sense_rsp.sense_key = (uint8_t) (p_msc->sense_key & 0x0F); sense_rsp.add_sense_code = p_msc->add_sense_code; sense_rsp.add_sense_qualifier = p_msc->add_sense_qualifier; resplen = sizeof(sense_rsp); - memcpy(buffer, &sense_rsp, resplen); + memcpy(buffer, &sense_rsp, (size_t) resplen); + + // request sense callback could overwrite the sense data + if (tud_msc_request_sense_cb) + { + resplen = tud_msc_request_sense_cb(lun, buffer, (uint16_t) bufsize); + } // Clear sense data after copy tud_msc_set_sense(lun, 0, 0, 0); @@ -835,8 +847,8 @@ static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc) // negative means error -> endpoint is stalled & status in CSW set to failed TU_LOG(MSC_DEBUG, " tud_msc_read10_cb() return -1\r\n"); - // Sense = Flash not ready for access - tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_MEDIUM_ERROR, 0x33, 0x00); + // set sense + set_sense_medium_not_present(p_cbw->lun); fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED); } @@ -847,7 +859,7 @@ static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc) } else { - TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, nbytes), ); + TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, (uint16_t) nbytes), ); } } @@ -871,7 +883,7 @@ static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc) } // remaining bytes capped at class buffer - int32_t nbytes = (int32_t) tu_min32(sizeof(_mscd_buf), p_cbw->total_bytes-p_msc->xferred_len); + uint16_t nbytes = (uint16_t) tu_min32(sizeof(_mscd_buf), p_cbw->total_bytes-p_msc->xferred_len); // Write10 callback will be called later when usb transfer complete TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, nbytes), ); @@ -900,8 +912,8 @@ static void proc_write10_new_data(uint8_t rhport, mscd_interface_t* p_msc, uint3 // 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); + // Set sense + set_sense_medium_not_present(p_cbw->lun); fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED); }else @@ -909,14 +921,15 @@ static void proc_write10_new_data(uint8_t rhport, mscd_interface_t* p_msc, uint3 // Application consume less than what we got (including zero) if ( (uint32_t) nbytes < xferred_bytes ) { + uint32_t const left_over = xferred_bytes - (uint32_t) nbytes; if ( nbytes > 0 ) { - p_msc->xferred_len += nbytes; - memmove(_mscd_buf, _mscd_buf+nbytes, xferred_bytes-nbytes); + p_msc->xferred_len += (uint16_t) nbytes; + memmove(_mscd_buf, _mscd_buf+nbytes, left_over); } // simulate an transfer complete with adjusted parameters --> callback will be invoked with adjusted parameter - dcd_event_xfer_complete(rhport, p_msc->ep_out, xferred_bytes-nbytes, XFER_RESULT_SUCCESS, false); + dcd_event_xfer_complete(rhport, p_msc->ep_out, left_over, XFER_RESULT_SUCCESS, false); } else { diff --git a/src/class/msc/msc_device.h b/src/class/msc/msc_device.h index d32694340..5839b168d 100644 --- a/src/class/msc/msc_device.h +++ b/src/class/msc/msc_device.h @@ -131,6 +131,9 @@ TU_ATTR_WEAK uint8_t tud_msc_get_maxlun_cb(void); // - Start = 1 : active mode, if load_eject = 1 : load disk storage TU_ATTR_WEAK bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject); +// Invoked when received REQUEST_SENSE +TU_ATTR_WEAK int32_t tud_msc_request_sense_cb(uint8_t lun, void* buffer, uint16_t bufsize); + // Invoked when Read10 command is complete TU_ATTR_WEAK void tud_msc_read10_complete_cb(uint8_t lun); diff --git a/src/class/msc/msc_host.c b/src/class/msc/msc_host.c index fa6519956..934f79ff7 100644 --- a/src/class/msc/msc_host.c +++ b/src/class/msc/msc_host.c @@ -26,7 +26,7 @@ #include "tusb_option.h" -#if TUSB_OPT_HOST_ENABLED & CFG_TUH_MSC +#if CFG_TUH_ENABLED && CFG_TUH_MSC #include "host/usbh.h" #include "host/usbh_classdriver.h" @@ -325,19 +325,19 @@ bool msch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32 p_msc->stage = MSC_STAGE_DATA; uint8_t const ep_data = (cbw->dir & TUSB_DIR_IN_MASK) ? p_msc->ep_in : p_msc->ep_out; - TU_ASSERT(usbh_edpt_xfer(dev_addr, ep_data, p_msc->buffer, cbw->total_bytes)); + TU_ASSERT(usbh_edpt_xfer(dev_addr, ep_data, p_msc->buffer, (uint16_t) cbw->total_bytes)); }else { // Status stage p_msc->stage = MSC_STAGE_STATUS; - TU_ASSERT(usbh_edpt_xfer(dev_addr, p_msc->ep_in, (uint8_t*) &p_msc->csw, sizeof(msc_csw_t))); + TU_ASSERT(usbh_edpt_xfer(dev_addr, p_msc->ep_in, (uint8_t*) &p_msc->csw, (uint16_t) sizeof(msc_csw_t))); } break; case MSC_STAGE_DATA: // Status stage p_msc->stage = MSC_STAGE_STATUS; - TU_ASSERT(usbh_edpt_xfer(dev_addr, p_msc->ep_in, (uint8_t*) &p_msc->csw, sizeof(msc_csw_t))); + TU_ASSERT(usbh_edpt_xfer(dev_addr, p_msc->ep_in, (uint8_t*) &p_msc->csw, (uint16_t) sizeof(msc_csw_t))); break; case MSC_STAGE_STATUS: @@ -358,18 +358,19 @@ bool msch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32 // MSC Enumeration //--------------------------------------------------------------------+ -static bool config_get_maxlun_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result); +static void config_get_maxlun_complete (tuh_xfer_t* xfer); static bool config_test_unit_ready_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw); 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 max_len) { + (void) rhport; 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); + uint16_t const drv_len = (uint16_t) (sizeof(tusb_desc_interface_t) + desc_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t)); TU_ASSERT(drv_len <= max_len); msch_interface_t* p_msc = get_itf(dev_addr); @@ -378,7 +379,7 @@ bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *de for(uint32_t i=0; i<2; i++) { TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType && TUSB_XFER_BULK == ep_desc->bmAttributes.xfer); - TU_ASSERT(usbh_edpt_open(rhport, dev_addr, ep_desc)); + TU_ASSERT(tuh_edpt_open(dev_addr, ep_desc)); if ( tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN ) { @@ -405,7 +406,7 @@ bool msch_set_config(uint8_t dev_addr, uint8_t itf_num) //------------- Get Max Lun -------------// TU_LOG2("MSC Get Max Lun\r\n"); - tusb_control_request_t request = + tusb_control_request_t const request = { .bmRequestType_bit = { @@ -418,27 +419,34 @@ bool msch_set_config(uint8_t dev_addr, uint8_t itf_num) .wIndex = itf_num, .wLength = 1 }; - TU_ASSERT(tuh_control_xfer(dev_addr, &request, &p_msc->max_lun, config_get_maxlun_complete)); + + tuh_xfer_t xfer = + { + .daddr = dev_addr, + .ep_addr = 0, + .setup = &request, + .buffer = &p_msc->max_lun, + .complete_cb = config_get_maxlun_complete, + .user_data = 0 + }; + TU_ASSERT(tuh_control_xfer(&xfer)); return true; } -static bool config_get_maxlun_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result) +static void config_get_maxlun_complete (tuh_xfer_t* xfer) { - (void) request; - - msch_interface_t* p_msc = get_itf(dev_addr); + uint8_t const daddr = xfer->daddr; + msch_interface_t* p_msc = get_itf(daddr); // STALL means zero - p_msc->max_lun = (XFER_RESULT_SUCCESS == result) ? _msch_buffer[0] : 0; + p_msc->max_lun = (XFER_RESULT_SUCCESS == xfer->result) ? _msch_buffer[0] : 0; p_msc->max_lun++; // MAX LUN is minus 1 by specs // TODO multiple LUN support TU_LOG2("SCSI Test Unit Ready\r\n"); uint8_t const lun = 0; - tuh_msc_test_unit_ready(dev_addr, lun, config_test_unit_ready_complete); - - return true; + tuh_msc_test_unit_ready(daddr, lun, config_test_unit_ready_complete); } static bool config_test_unit_ready_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw) @@ -476,7 +484,7 @@ static bool config_read_capacity_complete(uint8_t dev_addr, msc_cbw_t const* cbw // Capacity response field: Block size and Last LBA are both Big-Endian scsi_read_capacity10_resp_t* resp = (scsi_read_capacity10_resp_t*) ((void*) _msch_buffer); p_msc->capacity[cbw->lun].block_count = tu_ntohl(resp->last_lba) + 1; - p_msc->capacity[cbw->lun].block_size = tu_ntohl(resp->block_size); + p_msc->capacity[cbw->lun].block_size = tu_ntohl(resp->block_size); // Mark enumeration is complete p_msc->mounted = true; diff --git a/src/class/net/ecm_rndis_device.c b/src/class/net/ecm_rndis_device.c index c6cd388e3..5f316762f 100644 --- a/src/class/net/ecm_rndis_device.c +++ b/src/class/net/ecm_rndis_device.c @@ -27,7 +27,7 @@ #include "tusb_option.h" -#if ( TUSB_OPT_DEVICE_ENABLED && CFG_TUD_ECM_RNDIS ) +#if ( CFG_TUD_ENABLED && CFG_TUD_ECM_RNDIS ) #include "device/usbd.h" #include "device/usbd_pvt.h" @@ -108,20 +108,22 @@ static bool can_xmit; void tud_network_recv_renew(void) { - usbd_edpt_xfer(TUD_OPT_RHPORT, _netd_itf.ep_out, received, sizeof(received)); + usbd_edpt_xfer(0, _netd_itf.ep_out, received, sizeof(received)); } static void do_in_xfer(uint8_t *buf, uint16_t len) { can_xmit = false; - usbd_edpt_xfer(TUD_OPT_RHPORT, _netd_itf.ep_in, buf, len); + usbd_edpt_xfer(0, _netd_itf.ep_in, buf, len); } void netd_report(uint8_t *buf, uint16_t len) { + uint8_t const rhport = 0; + // skip if previous report not yet acknowledged by host - if ( usbd_edpt_busy(TUD_OPT_RHPORT, _netd_itf.ep_notif) ) return; - usbd_edpt_xfer(TUD_OPT_RHPORT, _netd_itf.ep_notif, buf, len); + if ( usbd_edpt_busy(rhport, _netd_itf.ep_notif) ) return; + usbd_edpt_xfer(rhport, _netd_itf.ep_notif, buf, len); } //--------------------------------------------------------------------+ @@ -316,11 +318,11 @@ bool netd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t rndis_generic_msg_t *rndis_msg = (rndis_generic_msg_t *) ((void*) notify.rndis_buf); uint32_t msglen = tu_le32toh(rndis_msg->MessageLength); TU_ASSERT(msglen <= sizeof(notify.rndis_buf)); - tud_control_xfer(rhport, request, notify.rndis_buf, msglen); + tud_control_xfer(rhport, request, notify.rndis_buf, (uint16_t) msglen); } else { - tud_control_xfer(rhport, request, notify.rndis_buf, sizeof(notify.rndis_buf)); + tud_control_xfer(rhport, request, notify.rndis_buf, (uint16_t) sizeof(notify.rndis_buf)); } } break; @@ -367,7 +369,7 @@ static void handle_incoming_packet(uint32_t len) } } - if (!tud_network_recv_cb(pnt, size)) + if (!tud_network_recv_cb(pnt, (uint16_t) size)) { /* if a buffer was never handled by user code, we must renew on the user's behalf */ tud_network_recv_renew(); diff --git a/src/class/net/ncm_device.c b/src/class/net/ncm_device.c index 3e131a85c..00892b49c 100644 --- a/src/class/net/ncm_device.c +++ b/src/class/net/ncm_device.c @@ -28,7 +28,7 @@ #include "tusb_option.h" -#if ( TUSB_OPT_DEVICE_ENABLED && CFG_TUD_NCM ) +#if ( CFG_TUD_ENABLED && CFG_TUD_NCM ) #include "device/usbd.h" #include "device/usbd_pvt.h" @@ -188,7 +188,7 @@ static void ncm_start_tx(void) { ntb->ndp.datagram[ncm_interface.datagram_count].wDatagramLength = 0; // Kick off an endpoint transfer - usbd_edpt_xfer(TUD_OPT_RHPORT, ncm_interface.ep_in, ntb->data, ntb_length); + usbd_edpt_xfer(0, ncm_interface.ep_in, ntb->data, ntb_length); ncm_interface.transferring = true; // Swap to the other NTB and clear it out @@ -229,7 +229,7 @@ void tud_network_recv_renew(void) { if (!ncm_interface.num_datagrams) { - usbd_edpt_xfer(TUD_OPT_RHPORT, ncm_interface.ep_out, receive_ntb, sizeof(receive_ntb)); + usbd_edpt_xfer(0, ncm_interface.ep_out, receive_ntb, sizeof(receive_ntb)); return; } @@ -316,14 +316,15 @@ uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1 static void ncm_report(void) { + uint8_t const rhport = 0; if (ncm_interface.report_state == REPORT_SPEED) { ncm_notify_speed_change.header.wIndex = ncm_interface.itf_num; - usbd_edpt_xfer(TUD_OPT_RHPORT, ncm_interface.ep_notif, (uint8_t *) &ncm_notify_speed_change, sizeof(ncm_notify_speed_change)); + usbd_edpt_xfer(rhport, ncm_interface.ep_notif, (uint8_t *) &ncm_notify_speed_change, sizeof(ncm_notify_speed_change)); ncm_interface.report_state = REPORT_CONNECTED; ncm_interface.report_pending = true; } else if (ncm_interface.report_state == REPORT_CONNECTED) { ncm_notify_connected.header.wIndex = ncm_interface.itf_num; - usbd_edpt_xfer(TUD_OPT_RHPORT, ncm_interface.ep_notif, (uint8_t *) &ncm_notify_connected, sizeof(ncm_notify_connected)); + usbd_edpt_xfer(rhport, ncm_interface.ep_notif, (uint8_t *) &ncm_notify_connected, sizeof(ncm_notify_connected)); ncm_interface.report_state = REPORT_DONE; ncm_interface.report_pending = true; } diff --git a/src/class/usbtmc/usbtmc.h b/src/class/usbtmc/usbtmc.h index 7d7005c2e..e7016ae24 100644 --- a/src/class/usbtmc/usbtmc.h +++ b/src/class/usbtmc/usbtmc.h @@ -189,7 +189,10 @@ typedef enum { USBTMC_STATUS_FAILED = 0x80, USBTMC_STATUS_TRANSFER_NOT_IN_PROGRESS = 0x81, USBTMC_STATUS_SPLIT_NOT_IN_PROGRESS = 0x82, - USBTMC_STATUS_SPLIT_IN_PROGRESS = 0x83 + USBTMC_STATUS_SPLIT_IN_PROGRESS = 0x83, + + /****** USBTMC 488 *************/ + USB488_STATUS_INTERRUPT_IN_BUSY = 0x20 } usbtmc_status_enum; /************************************************************ diff --git a/src/class/usbtmc/usbtmc_device.c b/src/class/usbtmc/usbtmc_device.c index 4bd1edf12..af4a92732 100644 --- a/src/class/usbtmc/usbtmc_device.c +++ b/src/class/usbtmc/usbtmc_device.c @@ -64,11 +64,12 @@ // USBTMC 3.2.2 error conditions not strictly followed // No local lock-out, REN, or GTL. // Clear message available status byte at the correct time? (488 4.3.1.3) - +// Ability to defer status byte transmission +// Transmission of status byte in response to USB488 SRQ condition #include "tusb_option.h" -#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_USBTMC) +#if (CFG_TUD_ENABLED && CFG_TUD_USBTMC) #include "device/usbd.h" #include "device/usbd_pvt.h" @@ -80,6 +81,11 @@ static char logMsg[150]; #endif +// Buffer size must be an exact multiple of the max packet size for both +// bulk (up to 64 bytes for FS, 512 bytes for HS). In addation, this driver +// imposes a minimum buffer size of 32 bytes. +#define USBTMCD_BUFFER_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) + /* * The state machine does not allow simultaneous reading and writing. This is * consistent with USBTMC. @@ -120,9 +126,12 @@ typedef struct uint8_t ep_int_in; // IN buffer is only used for first packet, not the remainder // in order to deal with prepending header - CFG_TUSB_MEM_ALIGN uint8_t ep_bulk_in_buf[USBTMCD_MAX_PACKET_SIZE]; + CFG_TUSB_MEM_ALIGN uint8_t ep_bulk_in_buf[USBTMCD_BUFFER_SIZE]; + uint32_t ep_bulk_in_wMaxPacketSize; // OUT buffer receives one packet at a time - CFG_TUSB_MEM_ALIGN uint8_t ep_bulk_out_buf[USBTMCD_MAX_PACKET_SIZE]; + CFG_TUSB_MEM_ALIGN uint8_t ep_bulk_out_buf[USBTMCD_BUFFER_SIZE]; + uint32_t ep_bulk_out_wMaxPacketSize; + uint32_t transfer_size_remaining; // also used for requested length for bulk IN. uint32_t transfer_size_sent; // To keep track of data bytes that have been queued in FIFO (not header bytes) @@ -139,11 +148,8 @@ CFG_TUSB_MEM_SECTION static usbtmc_interface_state_t usbtmc_state = .itf_id = 0xFF, }; -// We need all headers to fit in a single packet in this implementation. -TU_VERIFY_STATIC(USBTMCD_MAX_PACKET_SIZE >= 32u,"USBTMC dev EP packet size too small"); -TU_VERIFY_STATIC( - (sizeof(usbtmc_state.ep_bulk_in_buf) % USBTMCD_MAX_PACKET_SIZE) == 0, - "packet buffer must be a multiple of the packet size"); +// We need all headers to fit in a single packet in this implementation, 32 bytes will fit all standard USBTMC headers +TU_VERIFY_STATIC(USBTMCD_BUFFER_SIZE >= 32u,"USBTMC dev buffer size too small"); static bool handle_devMsgOutStart(uint8_t rhport, void *data, size_t len); static bool handle_devMsgOut(uint8_t rhport, void *data, size_t len, size_t packetLen); @@ -151,7 +157,6 @@ static bool handle_devMsgOut(uint8_t rhport, void *data, size_t len, size_t pack static uint8_t termChar; static uint8_t termCharRequested = false; - osal_mutex_def_t usbtmcLockBuffer; static osal_mutex_t usbtmcLock; @@ -235,17 +240,19 @@ void usbtmcd_init_cb(void) usbtmc_state.capabilities = tud_usbtmc_get_capabilities_cb(); #ifndef NDEBUG # if CFG_TUD_USBTMC_ENABLE_488 - if(usbtmc_state.capabilities->bmIntfcCapabilities488.supportsTrigger) - TU_ASSERT(&tud_usbtmc_msg_trigger_cb != NULL,); - // Per USB488 spec: table 8 - TU_ASSERT(!usbtmc_state.capabilities->bmIntfcCapabilities.listenOnly,); - TU_ASSERT(!usbtmc_state.capabilities->bmIntfcCapabilities.talkOnly,); + if (usbtmc_state.capabilities->bmIntfcCapabilities488.supportsTrigger) { + TU_ASSERT(&tud_usbtmc_msg_trigger_cb != NULL,); + } + // Per USB488 spec: table 8 + TU_ASSERT(!usbtmc_state.capabilities->bmIntfcCapabilities.listenOnly,); + TU_ASSERT(!usbtmc_state.capabilities->bmIntfcCapabilities.talkOnly,); # endif - if(usbtmc_state.capabilities->bmIntfcCapabilities.supportsIndicatorPulse) - TU_ASSERT(&tud_usbtmc_indicator_pulse_cb != NULL,); + if (usbtmc_state.capabilities->bmIntfcCapabilities.supportsIndicatorPulse) { + TU_ASSERT(&tud_usbtmc_indicator_pulse_cb != NULL,); + } #endif - usbtmcLock = osal_mutex_create(&usbtmcLockBuffer); + usbtmcLock = osal_mutex_create(&usbtmcLockBuffer); } uint16_t usbtmcd_open_cb(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len) @@ -280,12 +287,15 @@ uint16_t usbtmcd_open_cb(uint8_t rhport, tusb_desc_interface_t const * itf_desc, tusb_desc_endpoint_t const *ep_desc = (tusb_desc_endpoint_t const *)p_desc; switch(ep_desc->bmAttributes.xfer) { case TUSB_XFER_BULK: - TU_ASSERT(tu_edpt_packet_size(ep_desc) == USBTMCD_MAX_PACKET_SIZE, 0); + // Ensure buffer is an exact multiple of the maxPacketSize + TU_ASSERT((USBTMCD_BUFFER_SIZE % tu_edpt_packet_size(ep_desc)) == 0, 0); if (tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN) { usbtmc_state.ep_bulk_in = ep_desc->bEndpointAddress; + usbtmc_state.ep_bulk_in_wMaxPacketSize = tu_edpt_packet_size(ep_desc); } else { usbtmc_state.ep_bulk_out = ep_desc->bEndpointAddress; + usbtmc_state.ep_bulk_out_wMaxPacketSize = tu_edpt_packet_size(ep_desc); } break; @@ -393,7 +403,7 @@ static bool handle_devMsgOut(uint8_t rhport, void *data, size_t len, size_t pack // return true upon failure, as we can assume error is being handled elsewhere. TU_VERIFY(usbtmc_state.state == STATE_RCV,true); - bool shortPacket = (packetLen < USBTMCD_MAX_PACKET_SIZE); + bool shortPacket = (packetLen < usbtmc_state.ep_bulk_out_wMaxPacketSize); // Packet is to be considered complete when we get enough data or at a short packet. bool atEnd = false; @@ -520,7 +530,7 @@ bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint break; case STATE_TX_INITIATED: - if(usbtmc_state.transfer_size_remaining >=sizeof(usbtmc_state.ep_bulk_in_buf)) + if(usbtmc_state.transfer_size_remaining >= sizeof(usbtmc_state.ep_bulk_in_buf)) { // FIXME! This removes const below! TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, @@ -537,7 +547,7 @@ bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint usbtmc_state.transfer_size_remaining = 0; usbtmc_state.devInBuffer = NULL; TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_state.ep_bulk_in_buf, (uint16_t)packetLen) ); - if(((packetLen % USBTMCD_MAX_PACKET_SIZE) != 0) || (packetLen == 0 )) + if(((packetLen % usbtmc_state.ep_bulk_in_wMaxPacketSize) != 0) || (packetLen == 0 )) { usbtmc_state.state = STATE_TX_SHORTED; } @@ -587,18 +597,31 @@ bool usbtmcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request { uint32_t ep_addr = (request->wIndex); + // At this point, a transfer MAY be in progress. Based on USB spec, when clearing bulk EP HALT, + // the EP transfer buffer needs to be cleared and DTOG needs to be reset, even if + // the EP is not halted. The only USBD API interface to do this is to stall and then unstall the EP. if(ep_addr == usbtmc_state.ep_bulk_out) { criticalEnter(); + usbd_edpt_stall(rhport, (uint8_t)ep_addr); + usbd_edpt_clear_stall(rhport, (uint8_t)ep_addr); usbtmc_state.state = STATE_NAK; // USBD core has placed EP in NAK state for us criticalLeave(); tud_usbtmc_bulkOut_clearFeature_cb(); } else if (ep_addr == usbtmc_state.ep_bulk_in) { + usbd_edpt_stall(rhport, (uint8_t)ep_addr); + usbd_edpt_clear_stall(rhport, (uint8_t)ep_addr); tud_usbtmc_bulkIn_clearFeature_cb(); } - else + else if ((usbtmc_state.ep_int_in != 0) && (ep_addr == usbtmc_state.ep_int_in)) + { + // Clearing interrupt in EP + usbd_edpt_stall(rhport, (uint8_t)ep_addr); + usbd_edpt_clear_stall(rhport, (uint8_t)ep_addr); + } + else { return false; } @@ -678,7 +701,7 @@ bool usbtmcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request usbtmc_state.transfer_size_remaining = 0u; // Check if we've queued a short packet criticalEnter(); - usbtmc_state.state = ((usbtmc_state.transfer_size_sent % USBTMCD_MAX_PACKET_SIZE) == 0) ? + usbtmc_state.state = ((usbtmc_state.transfer_size_sent % usbtmc_state.ep_bulk_in_wMaxPacketSize) == 0) ? STATE_ABORTING_BULK_IN : STATE_ABORTING_BULK_IN_SHORTED; criticalLeave(); if(usbtmc_state.transfer_size_sent == 0) @@ -808,25 +831,32 @@ bool usbtmcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request bTag = request->wValue & 0x7F; TU_VERIFY(request->bmRequestType == 0xA1); - TU_VERIFY((request->wValue & (~0x7F)) == 0u); // Other bits are required to be zero + TU_VERIFY((request->wValue & (~0x7F)) == 0u); // Other bits are required to be zero (USB488v1.0 Table 11) TU_VERIFY(bTag >= 0x02 && bTag <= 127); TU_VERIFY(request->wIndex == usbtmc_state.itf_id); TU_VERIFY(request->wLength == 0x0003); rsp.bTag = (uint8_t)bTag; if(usbtmc_state.ep_int_in != 0) { - rsp.USBTMC_status = USBTMC_STATUS_SUCCESS; - rsp.statusByte = 0x00; // Use interrupt endpoint, instead. - - usbtmc_read_stb_interrupt_488_t intMsg = + rsp.statusByte = 0x00; // Use interrupt endpoint, instead. Must be 0x00 (USB488v1.0 4.3.1.2) + if(usbd_edpt_busy(rhport, usbtmc_state.ep_int_in)) { - .bNotify1 = { - .one = 1, - .bTag = bTag & 0x7Fu, - }, - .StatusByte = tud_usbtmc_get_stb_cb(&(rsp.USBTMC_status)) - }; - usbd_edpt_xfer(rhport, usbtmc_state.ep_int_in, (void*)&intMsg, sizeof(intMsg)); + rsp.USBTMC_status = USB488_STATUS_INTERRUPT_IN_BUSY; + } + else + { + rsp.USBTMC_status = USBTMC_STATUS_SUCCESS; + usbtmc_read_stb_interrupt_488_t intMsg = + { + .bNotify1 = { + .one = 1, + .bTag = bTag & 0x7Fu, + }, + .StatusByte = tud_usbtmc_get_stb_cb(&(rsp.USBTMC_status)) + }; + // Must be queued before control request response sent (USB488v1.0 4.3.1.2) + usbd_edpt_xfer(rhport, usbtmc_state.ep_int_in, (void*)&intMsg, sizeof(intMsg)); + } } else { diff --git a/src/class/usbtmc/usbtmc_device.h b/src/class/usbtmc/usbtmc_device.h index 0549a1569..144b3315d 100644 --- a/src/class/usbtmc/usbtmc_device.h +++ b/src/class/usbtmc/usbtmc_device.h @@ -35,10 +35,6 @@ #define CFG_TUD_USBTMC_ENABLE_488 (1) #endif -// USB spec says that full-speed must be 8,16,32, or 64. -// However, this driver implementation requires it to be >=32 -#define USBTMCD_MAX_PACKET_SIZE (64u) - /*********************************************** * Functions to be implemeted by the class implementation */ diff --git a/src/class/vendor/vendor_device.c b/src/class/vendor/vendor_device.c index 8a4ca1d2e..3b81a108f 100644 --- a/src/class/vendor/vendor_device.c +++ b/src/class/vendor/vendor_device.c @@ -26,7 +26,7 @@ #include "tusb_option.h" -#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_VENDOR) +#if (CFG_TUD_ENABLED && CFG_TUD_VENDOR) #include "device/usbd.h" #include "device/usbd_pvt.h" @@ -84,21 +84,23 @@ bool tud_vendor_n_peek(uint8_t itf, uint8_t* u8) //--------------------------------------------------------------------+ static void _prep_out_transaction (vendord_interface_t* p_itf) { + uint8_t const rhport = 0; + // skip if previous transfer not complete - if ( usbd_edpt_busy(TUD_OPT_RHPORT, p_itf->ep_out) ) return; + if ( usbd_edpt_busy(rhport, p_itf->ep_out) ) return; // Prepare for incoming data but only allow what we can store in the ring buffer. uint16_t max_read = tu_fifo_remaining(&p_itf->rx_ff); if ( max_read >= CFG_TUD_VENDOR_EPSIZE ) { - usbd_edpt_xfer(TUD_OPT_RHPORT, p_itf->ep_out, p_itf->epout_buf, CFG_TUD_VENDOR_EPSIZE); + usbd_edpt_xfer(rhport, p_itf->ep_out, p_itf->epout_buf, CFG_TUD_VENDOR_EPSIZE); } } uint32_t tud_vendor_n_read (uint8_t itf, void* buffer, uint32_t bufsize) { vendord_interface_t* p_itf = &_vendord_itf[itf]; - uint32_t num_read = tu_fifo_read_n(&p_itf->rx_ff, buffer, bufsize); + uint32_t num_read = tu_fifo_read_n(&p_itf->rx_ff, buffer, (uint16_t) bufsize); _prep_out_transaction(p_itf); return num_read; } @@ -113,24 +115,36 @@ void tud_vendor_n_read_flush (uint8_t itf) //--------------------------------------------------------------------+ // Write API //--------------------------------------------------------------------+ -static bool maybe_transmit(vendord_interface_t* p_itf) +static uint16_t maybe_transmit(vendord_interface_t* p_itf) { + uint8_t const rhport = 0; + // skip if previous transfer not complete - TU_VERIFY( !usbd_edpt_busy(TUD_OPT_RHPORT, p_itf->ep_in) ); + TU_VERIFY( !usbd_edpt_busy(rhport, p_itf->ep_in) ); uint16_t count = tu_fifo_read_n(&p_itf->tx_ff, p_itf->epin_buf, CFG_TUD_VENDOR_EPSIZE); if (count > 0) { - TU_ASSERT( usbd_edpt_xfer(TUD_OPT_RHPORT, p_itf->ep_in, p_itf->epin_buf, count) ); + TU_ASSERT( usbd_edpt_xfer(rhport, p_itf->ep_in, p_itf->epin_buf, count) ); } - return true; + return count; } uint32_t tud_vendor_n_write (uint8_t itf, void const* buffer, uint32_t bufsize) { vendord_interface_t* p_itf = &_vendord_itf[itf]; - uint16_t ret = tu_fifo_write_n(&p_itf->tx_ff, buffer, bufsize); - maybe_transmit(p_itf); + uint16_t ret = tu_fifo_write_n(&p_itf->tx_ff, buffer, (uint16_t) bufsize); + if (tu_fifo_count(&p_itf->tx_ff) >= CFG_TUD_VENDOR_EPSIZE) { + maybe_transmit(p_itf); + } + return ret; +} + +uint32_t tud_vendor_n_flush (uint8_t itf) +{ + vendord_interface_t* p_itf = &_vendord_itf[itf]; + uint32_t ret = maybe_transmit(p_itf); + return ret; } @@ -217,7 +231,7 @@ uint16_t vendord_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, ui if ( p_vendor->ep_in ) maybe_transmit(p_vendor); } - return (uintptr_t) p_desc - (uintptr_t) desc_itf; + return (uint16_t) ((uintptr_t) p_desc - (uintptr_t) desc_itf); } bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) @@ -238,7 +252,7 @@ bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint if ( ep_addr == p_itf->ep_out ) { // Receive new data - tu_fifo_write_n(&p_itf->rx_ff, p_itf->epout_buf, xferred_bytes); + tu_fifo_write_n(&p_itf->rx_ff, p_itf->epout_buf, (uint16_t) xferred_bytes); // Invoked callback if any if (tud_vendor_rx_cb) tud_vendor_rx_cb(itf); @@ -247,6 +261,7 @@ bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint } else if ( ep_addr == p_itf->ep_in ) { + if (tud_vendor_tx_cb) tud_vendor_tx_cb(itf, (uint16_t) xferred_bytes); // Send complete, try to send more if possible maybe_transmit(p_itf); } diff --git a/src/class/vendor/vendor_device.h b/src/class/vendor/vendor_device.h index d71c2a3e9..4a873e5fc 100644 --- a/src/class/vendor/vendor_device.h +++ b/src/class/vendor/vendor_device.h @@ -52,6 +52,7 @@ uint32_t tud_vendor_n_write_available (uint8_t itf); static inline uint32_t tud_vendor_n_write_str (uint8_t itf, char const* str); +uint32_t tud_vendor_n_flush (uint8_t itf); //--------------------------------------------------------------------+ // Application API (Single Port) @@ -64,6 +65,7 @@ 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); +static inline uint32_t tud_vendor_flush (void); //--------------------------------------------------------------------+ // Application Callback API (weak is optional) @@ -71,6 +73,8 @@ static inline uint32_t tud_vendor_write_available (void); // Invoked when received new data TU_ATTR_WEAK void tud_vendor_rx_cb(uint8_t itf); +// Invoked when last rx transfer finished +TU_ATTR_WEAK void tud_vendor_tx_cb(uint8_t itf, uint32_t sent_bytes); //--------------------------------------------------------------------+ // Inline Functions @@ -121,6 +125,11 @@ static inline uint32_t tud_vendor_write_available (void) return tud_vendor_n_write_available(0); } +static inline uint32_t tud_vendor_flush (void) +{ + return tud_vendor_n_flush(0); +} + //--------------------------------------------------------------------+ // Internal Class Driver API //--------------------------------------------------------------------+ diff --git a/src/class/vendor/vendor_host.c b/src/class/vendor/vendor_host.c index 1e28e9af4..dbea1228d 100644 --- a/src/class/vendor/vendor_host.c +++ b/src/class/vendor/vendor_host.c @@ -26,7 +26,7 @@ #include "tusb_option.h" -#if (TUSB_OPT_HOST_ENABLED && CFG_TUH_VENDOR) +#if (CFG_TUH_ENABLED && CFG_TUH_VENDOR) //--------------------------------------------------------------------+ // INCLUDE diff --git a/src/class/vendor/vendor_host.h b/src/class/vendor/vendor_host.h index 07fa56c61..65223fbca 100644 --- a/src/class/vendor/vendor_host.h +++ b/src/class/vendor/vendor_host.h @@ -49,16 +49,16 @@ static inline bool tusbh_custom_is_mounted(uint8_t dev_addr, uint16_t vendor_id, return false; } -tusb_error_t tusbh_custom_read(uint8_t dev_addr, uint16_t vendor_id, uint16_t product_id, void * p_buffer, uint16_t length); -tusb_error_t tusbh_custom_write(uint8_t dev_addr, uint16_t vendor_id, uint16_t product_id, void const * p_data, uint16_t length); +bool tusbh_custom_read(uint8_t dev_addr, uint16_t vendor_id, uint16_t product_id, void * p_buffer, uint16_t length); +bool tusbh_custom_write(uint8_t dev_addr, uint16_t vendor_id, uint16_t product_id, void const * p_data, uint16_t length); //--------------------------------------------------------------------+ // Internal Class Driver API //--------------------------------------------------------------------+ -void cush_init(void); -tusb_error_t cush_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_interface_desc, uint16_t *p_length); -void cush_isr(pipe_handle_t pipe_hdl, xfer_result_t event); -void cush_close(uint8_t dev_addr); +void cush_init(void); +bool cush_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_interface_desc, uint16_t *p_length); +void cush_isr(pipe_handle_t pipe_hdl, xfer_result_t event); +void cush_close(uint8_t dev_addr); #ifdef __cplusplus } diff --git a/src/class/video/video.h b/src/class/video/video.h index 844746546..e8227ea60 100644 --- a/src/class/video/video.h +++ b/src/class/video/video.h @@ -303,6 +303,45 @@ typedef struct TU_ATTR_PACKED { } tusb_desc_cs_video_fmt_uncompressed_t; typedef struct TU_ATTR_PACKED { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubType; + uint8_t bFormatIndex; + uint8_t bNumFrameDescriptors; + uint8_t bmFlags; + uint8_t bDefaultFrameIndex; + uint8_t bAspectRatioX; + uint8_t bAspectRatioY; + uint8_t bmInterlaceFlags; + uint8_t bCopyProtect; +} tusb_desc_cs_video_fmt_mjpeg_t; + +typedef struct TU_ATTR_PACKED { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubType; + uint8_t bFormatIndex; + uint32_t dwMaxVideoFrameBufferSize; /* deprecated */ + uint8_t bFormatType; +} tusb_desc_cs_video_fmt_dv_t; + +typedef struct TU_ATTR_PACKED { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubType; + uint8_t bFormatIndex; + uint8_t bNumFrameDescriptors; + uint8_t guidFormat[16]; + uint8_t bBitsPerPixel; + uint8_t bDefaultFrameIndex; + uint8_t bAspectRatioX; + uint8_t bAspectRatioY; + uint8_t bmInterlaceFlags; + uint8_t bCopyProtect; + uint8_t bVaribaleSize; +} tusb_desc_cs_video_fmt_frame_based_t; + +typedef struct TU_ATTR_PACKED { uint8_t bLength; uint8_t bDescriptorType; uint8_t bDescriptorSubType; @@ -318,6 +357,24 @@ typedef struct TU_ATTR_PACKED { uint32_t dwFrameInterval[]; } tusb_desc_cs_video_frm_uncompressed_t; +typedef tusb_desc_cs_video_frm_uncompressed_t tusb_desc_cs_video_frm_mjpeg_t; + +typedef struct TU_ATTR_PACKED { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubType; + uint8_t bFrameIndex; + uint8_t bmCapabilities; + uint16_t wWidth; + uint16_t wHeight; + uint32_t dwMinBitRate; + uint32_t dwMaxBitRate; + uint32_t dwDefaultFrameInterval; + uint8_t bFrameIntervalType; + uint32_t dwBytesPerLine; + uint32_t dwFrameInterval[]; +} tusb_desc_cs_video_frm_frame_based_t; + //--------------------------------------------------------------------+ // Requests //--------------------------------------------------------------------+ @@ -378,8 +435,11 @@ TU_VERIFY_STATIC( sizeof(video_probe_and_commit_control_t) == 48, "size is not c #define TUD_VIDEO_DESC_CS_VS_IN_LEN 13 #define TUD_VIDEO_DESC_CS_VS_OUT_LEN 9 #define TUD_VIDEO_DESC_CS_VS_FMT_UNCOMPR_LEN 27 +#define TUD_VIDEO_DESC_CS_VS_FMT_MJPEG_LEN 11 #define TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_CONT_LEN 38 #define TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_DISC_LEN 26 +#define TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_CONT_LEN 38 +#define TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_DISC_LEN 26 #define TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN 6 /* 2.2 compression formats */ @@ -462,6 +522,25 @@ TU_VERIFY_STATIC( sizeof(video_probe_and_commit_control_t) == 48, "size is not c _frmidx, _cap, U16_TO_U8S_LE(_width), U16_TO_U8S_LE(_height), U32_TO_U8S_LE(_minbr), U32_TO_U8S_LE(_maxbr), \ U32_TO_U8S_LE(_maxfrmbufsz), U32_TO_U8S_LE(_frminterval), (TU_ARGS_NUM(__VA_ARGS__)), __VA_ARGS__ +/* Motion-JPEG 3.1.1 Table 3-1 */ +#define TUD_VIDEO_DESC_CS_VS_FMT_MJPEG(_fmtidx, _numfrmdesc, _fixed_sz, _frmidx, _asrx, _asry, _interlace, _cp) \ + TUD_VIDEO_DESC_CS_VS_FMT_MJPEG_LEN, TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VS_FORMAT_MJPEG, \ + _fmtidx, _numfrmdesc, _fixed_sz, _frmidx, _asrx, _asry, _interlace, _cp + +/* Motion-JPEG 3.1.1 Table 3-2 and 3-3 */ +#define TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_CONT(_frmidx, _cap, _width, _height, _minbr, _maxbr, _maxfrmbufsz, _frminterval, _minfrminterval, _maxfrminterval, _frmintervalstep) \ + TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_CONT_LEN, TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VS_FRAME_MJPEG, \ + _frmidx, _cap, U16_TO_U8S_LE(_width), U16_TO_U8S_LE(_height), U32_TO_U8S_LE(_minbr), U32_TO_U8S_LE(_maxbr), \ + U32_TO_U8S_LE(_maxfrmbufsz), U32_TO_U8S_LE(_frminterval), 0, \ + U32_TO_U8S_LE(_minfrminterval), U32_TO_U8S_LE(_maxfrminterval), U32_TO_U8S_LE(_frmintervalstep) + +/* Motion-JPEG 3.1.1 Table 3-2 and 3-4 */ +#define TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_DISC(_frmidx, _cap, _width, _height, _minbr, _maxbr, _maxfrmbufsz, _frminterval, ...) \ + TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_DISC_LEN + (TU_ARGS_NUM(__VA_ARGS__)) * 4, \ + TUSB_DESC_CS_INTERFACE, VIDEO_CS_VS_INTERFACE_FRAME_MJPEG, \ + _frmidx, _cap, U16_TO_U8S_LE(_width), U16_TO_U8S_LE(_height), U32_TO_U8S_LE(_minbr), U32_TO_U8S_LE(_maxbr), \ + U32_TO_U8S_LE(_maxfrmbufsz), U32_TO_U8S_LE(_frminterval), (TU_ARGS_NUM(__VA_ARGS__)), __VA_ARGS__ + /* 3.9.2.6 */ #define TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING(_color, _trns, _mat) \ TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN, \ diff --git a/src/class/video/video_device.c b/src/class/video/video_device.c index eeb068197..a6d2724c1 100644 --- a/src/class/video/video_device.c +++ b/src/class/video/video_device.c @@ -27,7 +27,7 @@ #include "tusb_option.h" -#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_VIDEO && CFG_TUD_VIDEO_STREAMING) +#if (CFG_TUD_ENABLED && CFG_TUD_VIDEO && CFG_TUD_VIDEO_STREAMING) #include "device/usbd.h" #include "device/usbd_pvt.h" @@ -59,6 +59,34 @@ typedef struct TU_ATTR_PACKED { uint8_t bEntityId; } tusb_desc_cs_video_entity_itf_t; +typedef union { + struct TU_ATTR_PACKED { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubType; + uint8_t bFormatIndex; + uint8_t bNumFrameDescriptors; + }; + tusb_desc_cs_video_fmt_uncompressed_t uncompressed; + tusb_desc_cs_video_fmt_mjpeg_t mjpeg; + tusb_desc_cs_video_fmt_frame_based_t frame_based; +} tusb_desc_cs_video_fmt_t; + +typedef union { + struct TU_ATTR_PACKED { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubType; + uint8_t bFrameIndex; + uint8_t bmCapabilities; + uint16_t wWidth; + uint16_t wHeight; + }; + tusb_desc_cs_video_frm_uncompressed_t uncompressed; + tusb_desc_cs_video_frm_mjpeg_t mjpeg; + tusb_desc_cs_video_frm_frame_based_t frame_based; +} tusb_desc_cs_video_frm_t; + /* video streaming interface */ typedef struct TU_ATTR_PACKED { uint8_t index_vc; /* index of bound video control interface */ @@ -243,6 +271,13 @@ static void const* _find_desc_ep(void const *beg, void const *end) return end; } +/** Return the end of the video control descriptor. */ +static inline void const* _end_of_control_descriptor(void const *desc) +{ + tusb_desc_vc_itf_t const *vc = (tusb_desc_vc_itf_t const *)desc; + return desc + vc->std.bLength + vc->ctl.wTotalLength; +} + /** Find the first entity descriptor with the entity ID * specified by the argument belonging to the current video control descriptor. * @@ -253,10 +288,8 @@ static void const* _find_desc_ep(void const *beg, void const *end) * @retval end did not found interface descriptor */ static void const* _find_desc_entity(void const *desc, uint_fast8_t entityid) { - tusb_desc_vc_itf_t const *vc = (tusb_desc_vc_itf_t const*)desc; - void const *beg = vc; - void const *end = beg + vc->std.bLength + vc->ctl.wTotalLength; - for (void const *cur = beg; cur < end; cur = _find_desc(cur, end, TUSB_DESC_CS_INTERFACE)) { + void const *end = _end_of_control_descriptor(desc); + for (void const *cur = desc; cur < end; cur = _find_desc(cur, end, TUSB_DESC_CS_INTERFACE)) { tusb_desc_cs_video_entity_itf_t const *itf = (tusb_desc_cs_video_entity_itf_t const *)cur; if ((VIDEO_CS_ITF_VC_INPUT_TERMINAL <= itf->bDescriptorSubtype && itf->bDescriptorSubtype < VIDEO_CS_ITF_VC_MAX) @@ -276,17 +309,38 @@ static inline void const* _end_of_streaming_descriptor(void const *desc) } /** Find the first format descriptor with the specified format number. */ -static inline tusb_desc_cs_video_fmt_uncompressed_t const *_find_desc_format(void const *beg, void const *end, uint_fast8_t fmtnum) +static inline void const *_find_desc_format(void const *beg, void const *end, uint_fast8_t fmtnum) { - return (tusb_desc_cs_video_fmt_uncompressed_t const*) - _find_desc_3(beg, end, TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VS_FORMAT_UNCOMPRESSED, fmtnum); + for (void const *cur = beg; cur < end; cur = _find_desc(cur, end, TUSB_DESC_CS_INTERFACE)) { + uint8_t const *p = (uint8_t const *)cur; + uint_fast8_t fmt = p[2]; + if ((fmt == VIDEO_CS_ITF_VS_FORMAT_UNCOMPRESSED || + fmt == VIDEO_CS_ITF_VS_FORMAT_MJPEG || + fmt == VIDEO_CS_ITF_VS_FORMAT_DV || + fmt == VIDEO_CS_ITF_VS_FRAME_FRAME_BASED) && + fmtnum == p[3]) { + return cur; + } + cur = tu_desc_next(cur); + } + return end; } /** Find the first frame descriptor with the specified format number. */ -static inline tusb_desc_cs_video_frm_uncompressed_t const *_find_desc_frame(void const *beg, void const *end, uint_fast8_t frmnum) +static inline void const *_find_desc_frame(void const *beg, void const *end, uint_fast8_t frmnum) { - return (tusb_desc_cs_video_frm_uncompressed_t const*) - _find_desc_3(beg, end, TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VS_FRAME_UNCOMPRESSED, frmnum); + for (void const *cur = beg; cur < end; cur = _find_desc(cur, end, TUSB_DESC_CS_INTERFACE)) { + uint8_t const *p = (uint8_t const *)cur; + uint_fast8_t frm = p[2]; + if ((frm == VIDEO_CS_ITF_VS_FRAME_UNCOMPRESSED || + frm == VIDEO_CS_ITF_VS_FRAME_MJPEG || + frm == VIDEO_CS_ITF_VS_FRAME_FRAME_BASED) && + frmnum == p[3]) { + return cur; + } + cur = tu_desc_next(cur); + } + return end; } /** Set uniquely determined values to variables that have not been set @@ -297,7 +351,7 @@ static bool _update_streaming_parameters(videod_streaming_interface_t const *stm { tusb_desc_vs_itf_t const *vs = _get_desc_vs(stm); uint_fast8_t fmtnum = param->bFormatIndex; - TU_ASSERT(fmtnum <= vs->stm.bNumFormats); + TU_ASSERT(vs && fmtnum <= vs->stm.bNumFormats); if (!fmtnum) { if (1 < vs->stm.bNumFormats) return true; /* Need to negotiate all variables. */ fmtnum = 1; @@ -307,7 +361,6 @@ static bool _update_streaming_parameters(videod_streaming_interface_t const *stm /* Set the parameters determined by the format */ param->wKeyFrameRate = 1; param->wPFrameRate = 0; - param->wCompQuality = 1; /* 1 to 10000 */ param->wCompWindowSize = 1; /* GOP size? */ param->wDelay = 0; /* milliseconds */ param->dwClockFrequency = 27000000; /* same as MPEG-2 system time clock */ @@ -319,8 +372,18 @@ static bool _update_streaming_parameters(videod_streaming_interface_t const *stm param->bBitDepthLuma = 8; void const *end = _end_of_streaming_descriptor(vs); - tusb_desc_cs_video_fmt_uncompressed_t const *fmt = _find_desc_format(tu_desc_next(vs), end, fmtnum); + tusb_desc_cs_video_fmt_t const *fmt = _find_desc_format(tu_desc_next(vs), end, fmtnum); TU_ASSERT(fmt != end); + + switch (fmt->bDescriptorSubType) { + case VIDEO_CS_ITF_VS_FORMAT_UNCOMPRESSED: + param->wCompQuality = 1; /* 1 to 10000 */ + break; + case VIDEO_CS_ITF_VS_FORMAT_MJPEG: + break; + default: return false; + } + uint_fast8_t frmnum = param->bFrameIndex; TU_ASSERT(frmnum <= fmt->bNumFrameDescriptors); if (!frmnum) { @@ -328,28 +391,39 @@ static bool _update_streaming_parameters(videod_streaming_interface_t const *stm frmnum = 1; param->bFrameIndex = 1; } - tusb_desc_cs_video_frm_uncompressed_t const *frm = _find_desc_frame(tu_desc_next(fmt), end, frmnum); + tusb_desc_cs_video_frm_t const *frm = _find_desc_frame(tu_desc_next(fmt), end, frmnum); TU_ASSERT(frm != end); /* Set the parameters determined by the frame */ uint_fast32_t frame_size = param->dwMaxVideoFrameSize; if (!frame_size) { - frame_size = (uint_fast32_t)frm->wWidth * frm->wHeight * fmt->bBitsPerPixel / 8; + switch (fmt->bDescriptorSubType) { + case VIDEO_CS_ITF_VS_FORMAT_UNCOMPRESSED: + frame_size = (uint_fast32_t)frm->wWidth * frm->wHeight * fmt->uncompressed.bBitsPerPixel / 8; + break; + case VIDEO_CS_ITF_VS_FORMAT_MJPEG: + frame_size = (uint_fast32_t)frm->wWidth * frm->wHeight * 16 / 8; /* YUV422 */ + break; + default: break; + } param->dwMaxVideoFrameSize = frame_size; } uint_fast32_t interval = param->dwFrameInterval; if (!interval) { - if ((1 < frm->bFrameIntervalType) || - ((0 == frm->bFrameIntervalType) && (frm->dwFrameInterval[1] != frm->dwFrameInterval[0]))) { + if ((1 < frm->uncompressed.bFrameIntervalType) || + ((0 == frm->uncompressed.bFrameIntervalType) && + (frm->uncompressed.dwFrameInterval[1] != frm->uncompressed.dwFrameInterval[0]))) { return true; } - interval = frm->dwFrameInterval[0]; + interval = frm->uncompressed.dwFrameInterval[0]; param->dwFrameInterval = interval; } uint_fast32_t interval_ms = interval / 10000; TU_ASSERT(interval_ms); uint_fast32_t payload_size = (frame_size + interval_ms - 1) / interval_ms + 2; + if (CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE < payload_size) + payload_size = CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE; param->dwMaxPayloadTransferSize = payload_size; return true; } @@ -366,7 +440,8 @@ static bool _negotiate_streaming_parameters(videod_streaming_interface_t const * if (!fmtnum) { switch (request) { case VIDEO_REQUEST_GET_MAX: - param->bFormatIndex = _get_desc_vs(stm)->stm.bNumFormats; + if (_get_desc_vs(stm)) + param->bFormatIndex = _get_desc_vs(stm)->stm.bNumFormats; break; case VIDEO_REQUEST_GET_MIN: case VIDEO_REQUEST_GET_DEF: @@ -393,8 +468,9 @@ static bool _negotiate_streaming_parameters(videod_streaming_interface_t const * uint_fast8_t frmnum = param->bFrameIndex; if (!frmnum) { tusb_desc_vs_itf_t const *vs = _get_desc_vs(stm); + TU_ASSERT(vs); void const *end = _end_of_streaming_descriptor(vs); - tusb_desc_cs_video_fmt_uncompressed_t const *fmt = _find_desc_format(tu_desc_next(vs), end, fmtnum); + tusb_desc_cs_video_fmt_t const *fmt = _find_desc_format(tu_desc_next(vs), end, fmtnum); switch (request) { case VIDEO_REQUEST_GET_MAX: frmnum = fmt->bNumFrameDescriptors; @@ -403,31 +479,50 @@ static bool _negotiate_streaming_parameters(videod_streaming_interface_t const * frmnum = 1; break; case VIDEO_REQUEST_GET_DEF: - frmnum = fmt->bDefaultFrameIndex; + switch (fmt->bDescriptorSubType) { + case VIDEO_CS_ITF_VS_FORMAT_UNCOMPRESSED: + frmnum = fmt->uncompressed.bDefaultFrameIndex; + break; + case VIDEO_CS_ITF_VS_FORMAT_MJPEG: + frmnum = fmt->mjpeg.bDefaultFrameIndex; + break; + default: return false; + } break; default: return false; } - param->bFrameIndex = frmnum; + param->bFrameIndex = (uint8_t)frmnum; /* Set the parameters determined by the frame */ - tusb_desc_cs_video_frm_uncompressed_t const *frm = _find_desc_frame(tu_desc_next(fmt), end, frmnum); - param->dwMaxVideoFrameSize = frm->wWidth * frm->wHeight * fmt->bBitsPerPixel / 8; + tusb_desc_cs_video_frm_t const *frm = _find_desc_frame(tu_desc_next(fmt), end, frmnum); + uint_fast32_t frame_size; + switch (fmt->bDescriptorSubType) { + case VIDEO_CS_ITF_VS_FORMAT_UNCOMPRESSED: + frame_size = (uint_fast32_t)frm->wWidth * frm->wHeight * fmt->uncompressed.bBitsPerPixel / 8; + break; + case VIDEO_CS_ITF_VS_FORMAT_MJPEG: + frame_size = (uint_fast32_t)frm->wWidth * frm->wHeight * 16 / 8; /* YUV422 */ + break; + default: return false; + } + param->dwMaxVideoFrameSize = frame_size; return true; } if (!param->dwFrameInterval) { tusb_desc_vs_itf_t const *vs = _get_desc_vs(stm); + TU_ASSERT(vs); void const *end = _end_of_streaming_descriptor(vs); - tusb_desc_cs_video_fmt_uncompressed_t const *fmt = _find_desc_format(tu_desc_next(vs), end, fmtnum); - tusb_desc_cs_video_frm_uncompressed_t const *frm = _find_desc_frame(tu_desc_next(fmt), end, frmnum); + tusb_desc_cs_video_fmt_t const *fmt = _find_desc_format(tu_desc_next(vs), end, fmtnum); + tusb_desc_cs_video_frm_t const *frm = _find_desc_frame(tu_desc_next(fmt), end, frmnum); uint_fast32_t interval, interval_ms; switch (request) { case VIDEO_REQUEST_GET_MAX: { uint_fast32_t min_interval, max_interval; - uint_fast8_t num_intervals = frm->bFrameIntervalType; - max_interval = num_intervals ? frm->dwFrameInterval[num_intervals - 1]: frm->dwFrameInterval[1]; - min_interval = frm->dwFrameInterval[0]; + uint_fast8_t num_intervals = frm->uncompressed.bFrameIntervalType; + max_interval = num_intervals ? frm->uncompressed.dwFrameInterval[num_intervals - 1]: frm->uncompressed.dwFrameInterval[1]; + min_interval = frm->uncompressed.dwFrameInterval[0]; interval = max_interval; interval_ms = min_interval / 10000; } @@ -435,24 +530,24 @@ static bool _negotiate_streaming_parameters(videod_streaming_interface_t const * case VIDEO_REQUEST_GET_MIN: { uint_fast32_t min_interval, max_interval; - uint_fast8_t num_intervals = frm->bFrameIntervalType; - max_interval = num_intervals ? frm->dwFrameInterval[num_intervals - 1]: frm->dwFrameInterval[1]; - min_interval = frm->dwFrameInterval[0]; + uint_fast8_t num_intervals = frm->uncompressed.bFrameIntervalType; + max_interval = num_intervals ? frm->uncompressed.dwFrameInterval[num_intervals - 1]: frm->uncompressed.dwFrameInterval[1]; + min_interval = frm->uncompressed.dwFrameInterval[0]; interval = min_interval; interval_ms = max_interval / 10000; } break; case VIDEO_REQUEST_GET_DEF: - interval = frm->dwDefaultFrameInterval; + interval = frm->uncompressed.dwDefaultFrameInterval; interval_ms = interval / 10000; break; case VIDEO_REQUEST_GET_RES: { - uint_fast8_t num_intervals = frm->bFrameIntervalType; + uint_fast8_t num_intervals = frm->uncompressed.bFrameIntervalType; if (num_intervals) { interval = 0; } else { - interval = frm->dwFrameInterval[2]; + interval = frm->uncompressed.dwFrameInterval[2]; interval_ms = interval / 10000; } } @@ -464,11 +559,15 @@ static bool _negotiate_streaming_parameters(videod_streaming_interface_t const * param->dwMaxPayloadTransferSize = 0; } else { uint_fast32_t frame_size = param->dwMaxVideoFrameSize; + uint_fast32_t payload_size; if (!interval_ms) { - param->dwMaxPayloadTransferSize = frame_size + 2; + payload_size = frame_size + 2; } else { - param->dwMaxPayloadTransferSize = (frame_size + interval_ms - 1) / interval_ms + 2; + payload_size = (frame_size + interval_ms - 1) / interval_ms + 2; } + if (CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE < payload_size) + payload_size = CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE; + param->dwMaxPayloadTransferSize = payload_size; } return true; } @@ -485,7 +584,7 @@ static bool _close_vc_itf(uint8_t rhport, videod_interface_t *self) /* The next descriptor after the class-specific VC interface header descriptor. */ void const *cur = (void const*)vc + vc->std.bLength + vc->ctl.bLength; /* The end of the video control interface descriptor. */ - void const *end = (void const*)vc + vc->std.bLength + vc->ctl.wTotalLength; + void const *end = _end_of_control_descriptor(vc); if (vc->std.bNumEndpoints) { /* Find the notification endpoint descriptor. */ cur = _find_desc(cur, end, TUSB_DESC_ENDPOINT); @@ -517,7 +616,7 @@ static bool _open_vc_itf(uint8_t rhport, videod_interface_t *self, uint_fast8_t TU_ASSERT(vc->ctl.bInCollection <= CFG_TUD_VIDEO_STREAMING); /* Update to point the end of the video control interface descriptor. */ - end = cur + vc->std.bLength + vc->ctl.wTotalLength; + end = _end_of_control_descriptor(cur); /* Advance to the next descriptor after the class-specific VC interface header descriptor. */ cur += vc->std.bLength + vc->ctl.bLength; TU_LOG2(" bNumEndpoints %d\n", vc->std.bNumEndpoints); @@ -532,7 +631,7 @@ static bool _open_vc_itf(uint8_t rhport, videod_interface_t *self, uint_fast8_t /* Open the notification endpoint */ TU_ASSERT(usbd_edpt_open(rhport, notif)); } - self->cur = (void const*)vc - beg; + self->cur = (uint16_t) ((void const*)vc - beg); return true; } @@ -550,7 +649,7 @@ static bool _open_vs_itf(uint8_t rhport, videod_streaming_interface_t *stm, uint for (i = 0; i < TU_ARRAY_SIZE(stm->desc.ep); ++i) { uint_fast16_t ofs_ep = stm->desc.ep[i]; if (!ofs_ep) break; - uint_fast8_t ep_adr = _desc_ep_addr(desc + ofs_ep); + uint8_t ep_adr = _desc_ep_addr(desc + ofs_ep); usbd_edpt_close(rhport, ep_adr); stm->desc.ep[i] = 0; TU_LOG2(" close EP%02x\n", ep_adr); @@ -567,13 +666,14 @@ static bool _open_vs_itf(uint8_t rhport, videod_streaming_interface_t *stm, uint TU_VERIFY(cur < end); uint_fast8_t numeps = ((tusb_desc_interface_t const *)cur)->bNumEndpoints; TU_ASSERT(numeps <= TU_ARRAY_SIZE(stm->desc.ep)); - stm->desc.cur = cur - desc; /* Save the offset of the new settings */ + stm->desc.cur = (uint16_t) (cur - desc); /* Save the offset of the new settings */ if (!altnum) { /* initialize streaming settings */ stm->max_payload_transfer_size = 0; video_probe_and_commit_control_t *param = (video_probe_and_commit_control_t *)&stm->ep_buf; tu_memclr(param, sizeof(*param)); + TU_LOG2(" done 0\n"); return _update_streaming_parameters(stm, param); } /* Open endpoints of the new settings. */ @@ -594,7 +694,7 @@ static bool _open_vs_itf(uint8_t rhport, videod_streaming_interface_t *stm, uint stm->max_payload_transfer_size = max_size; } TU_ASSERT(usbd_edpt_open(rhport, ep)); - stm->desc.ep[i] = cur - desc; + stm->desc.ep[i] = (uint16_t) (cur - desc); TU_LOG2(" open EP%02x\n", _desc_ep_addr(cur)); } /* initialize payload header */ @@ -602,6 +702,7 @@ static bool _open_vs_itf(uint8_t rhport, videod_streaming_interface_t *stm, uint hdr->bHeaderLength = sizeof(*hdr); hdr->bmHeaderInfo = 0; + TU_LOG2(" done\n"); return true; } @@ -674,7 +775,7 @@ static int handle_video_ctl_cs_req(uint8_t rhport, uint8_t stage, if (stage == CONTROL_STAGE_SETUP) { TU_VERIFY(1 == request->wLength, VIDEO_ERROR_UNKNOWN); TU_VERIFY(tud_control_xfer(rhport, request, &self->power_mode, sizeof(self->power_mode)), VIDEO_ERROR_UNKNOWN); - } else if (stage == CONTROL_STAGE_ACK) { + } else if (stage == CONTROL_STAGE_DATA) { if (tud_video_power_mode_cb) return tud_video_power_mode_cb(ctl_idx, self->power_mode); } return VIDEO_ERROR_NONE; @@ -819,7 +920,7 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage, TU_VERIFY(sizeof(video_probe_and_commit_control_t) == request->wLength, VIDEO_ERROR_UNKNOWN); TU_VERIFY(tud_control_xfer(rhport, request, self->ep_buf, sizeof(video_probe_and_commit_control_t)), VIDEO_ERROR_UNKNOWN); - } else if (stage == CONTROL_STAGE_ACK) { + } else if (stage == CONTROL_STAGE_DATA) { TU_VERIFY(_update_streaming_parameters(self, (video_probe_and_commit_control_t*)self->ep_buf), VIDEO_ERROR_INVALID_VALUE_WITHIN_RANGE); } @@ -843,7 +944,7 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage, video_probe_and_commit_control_t tmp; tmp = *(video_probe_and_commit_control_t*)&self->ep_buf; TU_VERIFY(_negotiate_streaming_parameters(self, request->bRequest, &tmp), VIDEO_ERROR_INVALID_VALUE_WITHIN_RANGE); - TU_VERIFY(tud_control_xfer(rhport, request, self->ep_buf, sizeof(video_probe_and_commit_control_t)), VIDEO_ERROR_UNKNOWN); + TU_VERIFY(tud_control_xfer(rhport, request, &tmp, sizeof(tmp)), VIDEO_ERROR_UNKNOWN); } return VIDEO_ERROR_NONE; @@ -860,7 +961,7 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage, if (stage == CONTROL_STAGE_SETUP) { TU_VERIFY(1 == request->wLength, VIDEO_ERROR_UNKNOWN); - TU_VERIFY(tud_control_xfer(rhport, request, (uint8_t*)(uintptr_t) &_cap_get_set, sizeof(_cap_get_set)), VIDEO_ERROR_UNKNOWN); + TU_VERIFY(tud_control_xfer(rhport, request, (uint8_t*)(uintptr_t)&_cap_get_set, sizeof(_cap_get_set)), VIDEO_ERROR_UNKNOWN); } return VIDEO_ERROR_NONE; @@ -874,7 +975,7 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage, if (stage == CONTROL_STAGE_SETUP) { TU_VERIFY(sizeof(video_probe_and_commit_control_t) == request->wLength, VIDEO_ERROR_UNKNOWN); TU_VERIFY(tud_control_xfer(rhport, request, self->ep_buf, sizeof(video_probe_and_commit_control_t)), VIDEO_ERROR_UNKNOWN); - } else if (stage == CONTROL_STAGE_ACK) { + } else if (stage == CONTROL_STAGE_DATA) { TU_VERIFY(_update_streaming_parameters(self, (video_probe_and_commit_control_t*)self->ep_buf), VIDEO_ERROR_INVALID_VALUE_WITHIN_RANGE); if (tud_video_commit_cb) { return tud_video_commit_cb(self->index_vc, self->index_vs, (video_probe_and_commit_control_t*)self->ep_buf); @@ -976,7 +1077,7 @@ bool tud_video_n_frame_xfer(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, void *bu /* Find EP address */ void const *desc = _videod_itf[stm->index_vc].beg; - uint_fast8_t ep_addr = 0; + uint8_t ep_addr = 0; for (uint_fast8_t i = 0; i < CFG_TUD_VIDEO_STREAMING; ++i) { uint_fast16_t ofs_ep = stm->desc.ep[i]; if (!ofs_ep) continue; @@ -985,7 +1086,7 @@ bool tud_video_n_frame_xfer(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, void *bu } if (!ep_addr) return false; - TU_VERIFY( usbd_edpt_claim(0, ep_addr)); + TU_VERIFY( usbd_edpt_claim(0, ep_addr) ); /* update the packet header */ tusb_video_payload_header_t *hdr = (tusb_video_payload_header_t*)stm->ep_buf; hdr->FrameID ^= 1; @@ -994,7 +1095,7 @@ bool tud_video_n_frame_xfer(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, void *bu stm->buffer = (uint8_t*)buffer; stm->bufsize = bufsize; uint_fast16_t pkt_len = _prepare_in_payload(stm); - TU_ASSERT( usbd_edpt_xfer(0, ep_addr, stm->ep_buf, pkt_len), 0); + TU_ASSERT( usbd_edpt_xfer(0, ep_addr, stm->ep_buf, (uint16_t) pkt_len), 0); return true; } @@ -1034,7 +1135,7 @@ uint16_t videod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin /* Find available interface */ videod_interface_t *self = NULL; - uint_fast8_t ctl_idx; + uint8_t ctl_idx; for (ctl_idx = 0; ctl_idx < CFG_TUD_VIDEO; ++ctl_idx) { if (_videod_itf[ctl_idx].beg) continue; self = &_videod_itf[ctl_idx]; @@ -1051,10 +1152,10 @@ uint16_t videod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin uint_fast8_t bInCollection = vc->ctl.bInCollection; /* Find the end of the video interface descriptor */ void const *cur = _next_desc_itf(itf_desc, end); - for (uint_fast8_t stm_idx = 0; stm_idx < bInCollection; ++stm_idx) { + for (uint8_t stm_idx = 0; stm_idx < bInCollection; ++stm_idx) { videod_streaming_interface_t *stm = NULL; /* find free streaming interface handle */ - for (uint_fast8_t i = 0; i < CFG_TUD_VIDEO_STREAMING; ++i) { + for (uint8_t i = 0; i < CFG_TUD_VIDEO_STREAMING; ++i) { if (_videod_streaming_itf[i].desc.beg) continue; stm = &_videod_streaming_itf[i]; self->stm[stm_idx] = i; @@ -1063,12 +1164,12 @@ uint16_t videod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin TU_ASSERT(stm, 0); stm->index_vc = ctl_idx; stm->index_vs = stm_idx; - stm->desc.beg = (uintptr_t)cur - (uintptr_t)itf_desc; + stm->desc.beg = (uint16_t) ((uintptr_t)cur - (uintptr_t)itf_desc); cur = _next_desc_itf(cur, end); - stm->desc.end = (uintptr_t)cur - (uintptr_t)itf_desc; + stm->desc.end = (uint16_t) ((uintptr_t)cur - (uintptr_t)itf_desc); } - self->len = (uintptr_t)cur - (uintptr_t)itf_desc; - return (uintptr_t)cur - (uintptr_t)itf_desc; + self->len = (uint16_t) ((uintptr_t)cur - (uintptr_t)itf_desc); + return (uint16_t) ((uintptr_t)cur - (uintptr_t)itf_desc); } // Invoked when a control transfer occurred on an interface of this class @@ -1134,7 +1235,7 @@ bool videod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3 /* Claim the endpoint */ TU_VERIFY( usbd_edpt_claim(rhport, ep_addr), 0); uint_fast16_t pkt_len = _prepare_in_payload(stm); - TU_ASSERT( usbd_edpt_xfer(rhport, ep_addr, stm->ep_buf, pkt_len), 0); + TU_ASSERT( usbd_edpt_xfer(rhport, ep_addr, stm->ep_buf, (uint16_t) pkt_len), 0); } else { stm->buffer = NULL; stm->bufsize = 0; |
