Merge remote-tracking branch 'remotes/tinyusb/master' into pr/2253
This commit is contained in:
@@ -924,6 +924,31 @@ typedef struct TU_ATTR_PACKED {
|
||||
} subrange[numSubRanges]; \
|
||||
}
|
||||
|
||||
// 6.1 Interrupt Data Message Format
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t bInfo;
|
||||
uint8_t bAttribute;
|
||||
union
|
||||
{
|
||||
uint16_t wValue;
|
||||
struct
|
||||
{
|
||||
uint8_t wValue_cn_or_mcn;
|
||||
uint8_t wValue_cs;
|
||||
};
|
||||
};
|
||||
union
|
||||
{
|
||||
uint16_t wIndex;
|
||||
struct
|
||||
{
|
||||
uint8_t wIndex_ep_or_int;
|
||||
uint8_t wIndex_entity_id;
|
||||
};
|
||||
};
|
||||
} audio_interrupt_data_t;
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -181,6 +181,11 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// (For TYPE-I format only) Flow control is necessary to allow IN ep send correct amount of data, unless it's a virtual device where data is perfectly synchronized to USB clock.
|
||||
#ifndef CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
|
||||
#define CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL 1
|
||||
#endif
|
||||
|
||||
// Enable/disable feedback EP (required for asynchronous RX applications)
|
||||
#ifndef CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
|
||||
#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP 0 // Feedback - 0 or 1
|
||||
@@ -191,13 +196,9 @@
|
||||
#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)
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_AUDIO_INT_CTR_EP_IN_SW_BUFFER_SIZE
|
||||
#define CFG_TUD_AUDIO_INT_CTR_EP_IN_SW_BUFFER_SIZE 6 // Buffer size of audio control interrupt EP - 6 Bytes according to UAC 2 specification (p. 74)
|
||||
// Enable/disable interrupt EP (required for notifying host of control changes)
|
||||
#ifndef CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
|
||||
#define CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP 0 // Feedback - 0 or 1
|
||||
#endif
|
||||
|
||||
// Use software encoding/decoding
|
||||
@@ -388,10 +389,11 @@ uint16_t tud_audio_n_write_support_ff (uint8_t func_id, uint8_t ff_i
|
||||
tu_fifo_t* tud_audio_n_get_tx_support_ff (uint8_t func_id, uint8_t ff_idx);
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
|
||||
uint16_t tud_audio_int_ctr_n_write (uint8_t func_id, uint8_t const* buffer, uint16_t len);
|
||||
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
|
||||
bool tud_audio_int_n_write (uint8_t func_id, const audio_interrupt_data_t * data);
|
||||
#endif
|
||||
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application API (Interface0)
|
||||
//--------------------------------------------------------------------+
|
||||
@@ -431,8 +433,8 @@ static inline tu_fifo_t* tud_audio_get_tx_support_ff (uint8_t ff_idx);
|
||||
|
||||
// INT CTR API
|
||||
|
||||
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
|
||||
static inline uint16_t tud_audio_int_ctr_write (uint8_t const* buffer, uint16_t len);
|
||||
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
|
||||
static inline bool tud_audio_int_write (const audio_interrupt_data_t * data);
|
||||
#endif
|
||||
|
||||
// Buffer control EP data and schedule a transmit
|
||||
@@ -531,8 +533,8 @@ TU_ATTR_WEAK TU_ATTR_FAST_FUNC void tud_audio_feedback_interval_isr(uint8_t func
|
||||
|
||||
#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);
|
||||
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
|
||||
TU_ATTR_WEAK void tud_audio_int_done_cb(uint8_t rhport);
|
||||
#endif
|
||||
|
||||
// Invoked when audio set interface request received
|
||||
@@ -663,10 +665,10 @@ static inline tu_fifo_t* tud_audio_get_tx_support_ff(uint8_t ff_idx)
|
||||
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
|
||||
static inline uint16_t tud_audio_int_ctr_write(uint8_t const* buffer, uint16_t len)
|
||||
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
|
||||
static inline bool tud_audio_int_write(const audio_interrupt_data_t * data)
|
||||
{
|
||||
return tud_audio_int_ctr_n_write(0, buffer, len);
|
||||
return tud_audio_int_n_write(0, data);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -683,6 +685,7 @@ static inline bool tud_audio_fb_set(uint32_t feedback)
|
||||
// Internal Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void audiod_init (void);
|
||||
bool audiod_deinit (void);
|
||||
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);
|
||||
|
||||
@@ -91,11 +91,14 @@ bool tud_bt_acl_data_send(void *event, uint16_t event_len)
|
||||
//--------------------------------------------------------------------+
|
||||
// USBD Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void btd_init(void)
|
||||
{
|
||||
void btd_init(void) {
|
||||
tu_memclr(&_btd_itf, sizeof(_btd_itf));
|
||||
}
|
||||
|
||||
bool btd_deinit(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void btd_reset(uint8_t rhport)
|
||||
{
|
||||
(void)rhport;
|
||||
@@ -204,7 +207,9 @@ bool btd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t c
|
||||
request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE)
|
||||
{
|
||||
// HCI command packet addressing for single function Primary Controllers
|
||||
TU_VERIFY(request->bRequest == 0 && request->wValue == 0 && request->wIndex == 0);
|
||||
// also compatible with historical mode if enabled
|
||||
TU_VERIFY((request->bRequest == 0 && request->wValue == 0 && request->wIndex == 0) ||
|
||||
(CFG_TUD_BTH_HISTORICAL_COMPATIBLE && request->bRequest == 0xe0));
|
||||
}
|
||||
else if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE)
|
||||
{
|
||||
|
||||
@@ -36,10 +36,17 @@
|
||||
#ifndef CFG_TUD_BTH_EVENT_EPSIZE
|
||||
#define CFG_TUD_BTH_EVENT_EPSIZE 16
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_BTH_DATA_EPSIZE
|
||||
#define CFG_TUD_BTH_DATA_EPSIZE 64
|
||||
#endif
|
||||
|
||||
// Allow BTH class to work in historically compatibility mode where the bRequest is always 0xe0.
|
||||
// See Bluetooth Core v5.3, Vol. 4, Part B, Section 2.2
|
||||
#ifndef CFG_TUD_BTH_HISTORICAL_COMPATIBLE
|
||||
#define CFG_TUD_BTH_HISTORICAL_COMPATIBLE 0
|
||||
#endif
|
||||
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint16_t op_code;
|
||||
@@ -97,6 +104,7 @@ bool tud_bt_acl_data_send(void *acl_data, uint16_t data_len);
|
||||
// Internal Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void btd_init (void);
|
||||
bool btd_deinit (void);
|
||||
void btd_reset (uint8_t rhport);
|
||||
uint16_t btd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
|
||||
bool btd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const *request);
|
||||
|
||||
@@ -136,8 +136,7 @@ typedef enum{
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
/// Communication Interface Management Element Request Codes
|
||||
typedef enum
|
||||
{
|
||||
typedef enum {
|
||||
CDC_REQUEST_SEND_ENCAPSULATED_COMMAND = 0x00, ///< is used to issue a command in the format of the supported control protocol of the Communications Class interface
|
||||
CDC_REQUEST_GET_ENCAPSULATED_RESPONSE = 0x01, ///< is used to request a response in the format of the supported control protocol of the Communications Class interface.
|
||||
CDC_REQUEST_SET_COMM_FEATURE = 0x02,
|
||||
@@ -180,37 +179,38 @@ typedef enum
|
||||
CDC_REQUEST_GET_ATM_VC_STATISTICS = 0x53,
|
||||
|
||||
CDC_REQUEST_MDLM_SEMANTIC_MODEL = 0x60,
|
||||
}cdc_management_request_t;
|
||||
} cdc_management_request_t;
|
||||
|
||||
enum
|
||||
{
|
||||
typedef enum {
|
||||
CDC_CONTROL_LINE_STATE_DTR = 0x01,
|
||||
CDC_CONTROL_LINE_STATE_RTS = 0x02,
|
||||
};
|
||||
} cdc_control_line_state_t;
|
||||
|
||||
enum
|
||||
{
|
||||
CDC_LINE_CONDING_STOP_BITS_1 = 0, // 1 bit
|
||||
CDC_LINE_CONDING_STOP_BITS_1_5 = 1, // 1.5 bits
|
||||
CDC_LINE_CONDING_STOP_BITS_2 = 2, // 2 bits
|
||||
};
|
||||
typedef enum {
|
||||
CDC_LINE_CODING_STOP_BITS_1 = 0, // 1 bit
|
||||
CDC_LINE_CODING_STOP_BITS_1_5 = 1, // 1.5 bits
|
||||
CDC_LINE_CODING_STOP_BITS_2 = 2, // 2 bits
|
||||
} cdc_line_coding_stopbits_t;
|
||||
|
||||
enum
|
||||
{
|
||||
// TODO Backward compatible for typos. Maybe removed in the future release
|
||||
#define CDC_LINE_CONDING_STOP_BITS_1 CDC_LINE_CODING_STOP_BITS_1
|
||||
#define CDC_LINE_CONDING_STOP_BITS_1_5 CDC_LINE_CODING_STOP_BITS_1_5
|
||||
#define CDC_LINE_CONDING_STOP_BITS_2 CDC_LINE_CODING_STOP_BITS_2
|
||||
|
||||
typedef enum {
|
||||
CDC_LINE_CODING_PARITY_NONE = 0,
|
||||
CDC_LINE_CODING_PARITY_ODD = 1,
|
||||
CDC_LINE_CODING_PARITY_EVEN = 2,
|
||||
CDC_LINE_CODING_PARITY_MARK = 3,
|
||||
CDC_LINE_CODING_PARITY_SPACE = 4,
|
||||
};
|
||||
} cdc_line_coding_parity_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Management Element Notification (Notification Endpoint)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
/// 6.3 Notification Codes
|
||||
typedef enum
|
||||
{
|
||||
typedef enum {
|
||||
CDC_NOTIF_NETWORK_CONNECTION = 0x00, ///< This notification allows the device to notify the host about network connection status.
|
||||
CDC_NOTIF_RESPONSE_AVAILABLE = 0x01, ///< This notification allows the device to notify the hostthat a response is available. This response can be retrieved with a subsequent \ref CDC_REQUEST_GET_ENCAPSULATED_RESPONSE request.
|
||||
CDC_NOTIF_AUX_JACK_HOOK_STATE = 0x08,
|
||||
|
||||
@@ -43,10 +43,7 @@
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
//--------------------------------------------------------------------+
|
||||
enum
|
||||
{
|
||||
BULK_PACKET_SIZE = (TUD_OPT_HIGH_SPEED ? 512 : 64)
|
||||
};
|
||||
#define BULK_PACKET_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@@ -176,9 +173,11 @@ uint32_t tud_cdc_n_write(uint8_t itf, void const* buffer, uint32_t bufsize)
|
||||
uint16_t ret = tu_fifo_write_n(&p_cdc->tx_ff, buffer, (uint16_t) TU_MIN(bufsize, UINT16_MAX));
|
||||
|
||||
// flush if queue more than packet size
|
||||
// may need to suppress -Wunreachable-code since most of the time CFG_TUD_CDC_TX_BUFSIZE < BULK_PACKET_SIZE
|
||||
if ( (tu_fifo_count(&p_cdc->tx_ff) >= BULK_PACKET_SIZE) || ((CFG_TUD_CDC_TX_BUFSIZE < BULK_PACKET_SIZE) && tu_fifo_full(&p_cdc->tx_ff)) )
|
||||
{
|
||||
if ( tu_fifo_count(&p_cdc->tx_ff) >= BULK_PACKET_SIZE
|
||||
#if CFG_TUD_CDC_TX_BUFSIZE < BULK_PACKET_SIZE
|
||||
|| tu_fifo_full(&p_cdc->tx_ff) // check full if fifo size is less than packet size
|
||||
#endif
|
||||
) {
|
||||
tud_cdc_n_write_flush(itf);
|
||||
}
|
||||
|
||||
@@ -253,11 +252,39 @@ void cdcd_init(void)
|
||||
// In this way, the most current data is prioritized.
|
||||
tu_fifo_config(&p_cdc->tx_ff, p_cdc->tx_ff_buf, TU_ARRAY_SIZE(p_cdc->tx_ff_buf), 1, true);
|
||||
|
||||
tu_fifo_config_mutex(&p_cdc->rx_ff, NULL, osal_mutex_create(&p_cdc->rx_ff_mutex));
|
||||
tu_fifo_config_mutex(&p_cdc->tx_ff, osal_mutex_create(&p_cdc->tx_ff_mutex), NULL);
|
||||
#if OSAL_MUTEX_REQUIRED
|
||||
osal_mutex_t mutex_rd = osal_mutex_create(&p_cdc->rx_ff_mutex);
|
||||
osal_mutex_t mutex_wr = osal_mutex_create(&p_cdc->tx_ff_mutex);
|
||||
TU_ASSERT(mutex_rd != NULL && mutex_wr != NULL, );
|
||||
|
||||
tu_fifo_config_mutex(&p_cdc->rx_ff, NULL, mutex_rd);
|
||||
tu_fifo_config_mutex(&p_cdc->tx_ff, mutex_wr, NULL);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool cdcd_deinit(void) {
|
||||
#if OSAL_MUTEX_REQUIRED
|
||||
for(uint8_t i=0; i<CFG_TUD_CDC; i++) {
|
||||
cdcd_interface_t* p_cdc = &_cdcd_itf[i];
|
||||
osal_mutex_t mutex_rd = p_cdc->rx_ff.mutex_rd;
|
||||
osal_mutex_t mutex_wr = p_cdc->tx_ff.mutex_wr;
|
||||
|
||||
if (mutex_rd) {
|
||||
osal_mutex_delete(mutex_rd);
|
||||
tu_fifo_config_mutex(&p_cdc->rx_ff, NULL, NULL);
|
||||
}
|
||||
|
||||
if (mutex_wr) {
|
||||
osal_mutex_delete(mutex_wr);
|
||||
tu_fifo_config_mutex(&p_cdc->tx_ff, NULL, NULL);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void cdcd_reset(uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
|
||||
@@ -247,6 +247,7 @@ static inline bool tud_cdc_write_clear(void)
|
||||
// INTERNAL USBD-CLASS DRIVER API
|
||||
//--------------------------------------------------------------------+
|
||||
void cdcd_init (void);
|
||||
bool cdcd_deinit (void);
|
||||
void cdcd_reset (uint8_t rhport);
|
||||
uint16_t cdcd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
|
||||
bool cdcd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -44,7 +44,7 @@
|
||||
|
||||
// Set Line Coding on enumeration/mounted, value for cdc_line_coding_t
|
||||
//#ifndef CFG_TUH_CDC_LINE_CODING_ON_ENUM
|
||||
//#define CFG_TUH_CDC_LINE_CODING_ON_ENUM { 115200, CDC_LINE_CONDING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 }
|
||||
//#define CFG_TUH_CDC_LINE_CODING_ON_ENUM { 115200, CDC_LINE_CODING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 }
|
||||
//#endif
|
||||
|
||||
// RX FIFO size
|
||||
@@ -148,8 +148,11 @@ bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_c
|
||||
// Request to set baudrate
|
||||
bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// Request to Set Line Coding (ACM only)
|
||||
// Should only use if you don't work with serial devices such as FTDI/CP210x
|
||||
// Request to set data format
|
||||
bool tuh_cdc_set_data_format(uint8_t idx, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// Request to Set Line Coding = baudrate + data format
|
||||
// Note: only implemented by ACM and CH34x, not supported by FTDI and CP210x yet
|
||||
bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// Request to Get Line Coding (ACM only)
|
||||
@@ -159,15 +162,13 @@ bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding,
|
||||
|
||||
// Connect by set both DTR, RTS
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
bool tuh_cdc_connect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
|
||||
{
|
||||
bool tuh_cdc_connect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
|
||||
return tuh_cdc_set_control_line_state(idx, CDC_CONTROL_LINE_STATE_DTR | CDC_CONTROL_LINE_STATE_RTS, complete_cb, user_data);
|
||||
}
|
||||
|
||||
// Disconnect by clear both DTR, RTS
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
bool tuh_cdc_disconnect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
|
||||
{
|
||||
bool tuh_cdc_disconnect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
|
||||
return tuh_cdc_set_control_line_state(idx, 0x00, complete_cb, user_data);
|
||||
}
|
||||
|
||||
@@ -191,7 +192,8 @@ TU_ATTR_WEAK extern void tuh_cdc_tx_complete_cb(uint8_t idx);
|
||||
//--------------------------------------------------------------------+
|
||||
// Internal Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void cdch_init (void);
|
||||
bool cdch_init (void);
|
||||
bool cdch_deinit (void);
|
||||
bool cdch_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
|
||||
bool cdch_set_config (uint8_t dev_addr, uint8_t itf_num);
|
||||
bool cdch_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
|
||||
|
||||
84
src/class/cdc/serial/ch34x.h
Normal file
84
src/class/cdc/serial/ch34x.h
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2023 Heiko Kuester
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef _CH34X_H_
|
||||
#define _CH34X_H_
|
||||
|
||||
// There is no official documentation for the CH34x (CH340, CH341) chips. Reference can be found
|
||||
// - https://github.com/WCHSoftGroup/ch341ser_linux
|
||||
// - https://github.com/torvalds/linux/blob/master/drivers/usb/serial/ch341.c
|
||||
// - https://github.com/freebsd/freebsd-src/blob/main/sys/dev/usb/serial/uchcom.c
|
||||
|
||||
// set line_coding @ enumeration
|
||||
#ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
|
||||
#define CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X CFG_TUH_CDC_LINE_CODING_ON_ENUM
|
||||
#else // this default is necessary to work properly
|
||||
#define CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X { 9600, CDC_LINE_CONDING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 }
|
||||
#endif
|
||||
|
||||
// USB requests
|
||||
#define CH34X_REQ_READ_VERSION 0x5F // dec 95
|
||||
#define CH34X_REQ_WRITE_REG 0x9A // dec 154
|
||||
#define CH34X_REQ_READ_REG 0x95 // dec 149
|
||||
#define CH34X_REQ_SERIAL_INIT 0xA1 // dec 161
|
||||
#define CH34X_REQ_MODEM_CTRL 0xA4 // dev 164
|
||||
|
||||
// registers
|
||||
#define CH34X_REG_BREAK 0x05
|
||||
#define CH34X_REG_PRESCALER 0x12
|
||||
#define CH34X_REG_DIVISOR 0x13
|
||||
#define CH34X_REG_LCR 0x18
|
||||
#define CH34X_REG_LCR2 0x25
|
||||
#define CH34X_REG_MCR_MSR 0x06
|
||||
#define CH34X_REG_MCR_MSR2 0x07
|
||||
#define CH34X_NBREAK_BITS 0x01
|
||||
|
||||
#define CH341_REG_0x0F 0x0F // undocumented register
|
||||
#define CH341_REG_0x2C 0x2C // undocumented register
|
||||
#define CH341_REG_0x27 0x27 // hardware flow control (cts/rts)
|
||||
|
||||
#define CH34X_REG16_DIVISOR_PRESCALER TU_U16(CH34X_REG_DIVISOR, CH34X_REG_PRESCALER)
|
||||
#define CH32X_REG16_LCR2_LCR TU_U16(CH34X_REG_LCR2, CH34X_REG_LCR)
|
||||
|
||||
// modem control bits
|
||||
#define CH34X_BIT_RTS ( 1 << 6 )
|
||||
#define CH34X_BIT_DTR ( 1 << 5 )
|
||||
|
||||
// line control bits
|
||||
#define CH34X_LCR_ENABLE_RX 0x80
|
||||
#define CH34X_LCR_ENABLE_TX 0x40
|
||||
#define CH34X_LCR_MARK_SPACE 0x20
|
||||
#define CH34X_LCR_PAR_EVEN 0x10
|
||||
#define CH34X_LCR_ENABLE_PAR 0x08
|
||||
#define CH34X_LCR_PAR_MASK 0x38 // all parity bits
|
||||
#define CH34X_LCR_STOP_BITS_2 0x04
|
||||
#define CH34X_LCR_CS8 0x03
|
||||
#define CH34X_LCR_CS7 0x02
|
||||
#define CH34X_LCR_CS6 0x01
|
||||
#define CH34X_LCR_CS5 0x00
|
||||
#define CH34X_LCR_CS_MASK 0x03 // all CSx bits
|
||||
|
||||
#endif /* _CH34X_H_ */
|
||||
@@ -29,8 +29,6 @@
|
||||
// https://www.silabs.com/documents/public/application-notes/AN571.pdf
|
||||
|
||||
#define TU_CP210X_VID 0x10C4
|
||||
#define TU_CP210X_PID_LIST \
|
||||
0xEA60, 0xEA70
|
||||
|
||||
/* Config request codes */
|
||||
#define CP210X_IFC_ENABLE 0x00
|
||||
|
||||
@@ -25,11 +25,8 @@
|
||||
#ifndef TUSB_FTDI_SIO_H
|
||||
#define TUSB_FTDI_SIO_H
|
||||
|
||||
// VID/PID for matching FTDI devices
|
||||
// VID for matching FTDI devices
|
||||
#define TU_FTDI_VID 0x0403
|
||||
#define TU_FTDI_PID_LIST \
|
||||
0x6001, 0x6006, 0x6010, 0x6011, 0x6014, 0x6015, 0x8372, 0xFBFA, \
|
||||
0xcd18
|
||||
|
||||
// Commands
|
||||
#define FTDI_SIO_RESET 0 /* Reset the port */
|
||||
|
||||
@@ -160,11 +160,14 @@ void dfu_moded_reset(uint8_t rhport)
|
||||
reset_state();
|
||||
}
|
||||
|
||||
void dfu_moded_init(void)
|
||||
{
|
||||
void dfu_moded_init(void) {
|
||||
dfu_moded_reset(0);
|
||||
}
|
||||
|
||||
bool dfu_moded_deinit(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t dfu_moded_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
|
||||
{
|
||||
(void) rhport;
|
||||
|
||||
@@ -86,6 +86,7 @@ TU_ATTR_WEAK void tud_dfu_abort_cb(uint8_t alt);
|
||||
// Internal Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void dfu_moded_init(void);
|
||||
bool dfu_moded_deinit(void);
|
||||
void dfu_moded_reset(uint8_t rhport);
|
||||
uint16_t dfu_moded_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
|
||||
bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
|
||||
|
||||
@@ -51,8 +51,11 @@
|
||||
//--------------------------------------------------------------------+
|
||||
// USBD Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void dfu_rtd_init(void)
|
||||
{
|
||||
void dfu_rtd_init(void) {
|
||||
}
|
||||
|
||||
bool dfu_rtd_deinit(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void dfu_rtd_reset(uint8_t rhport)
|
||||
|
||||
@@ -43,6 +43,7 @@ void tud_dfu_runtime_reboot_to_dfu_cb(void);
|
||||
// Internal Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void dfu_rtd_init(void);
|
||||
bool dfu_rtd_deinit(void);
|
||||
void dfu_rtd_reset(uint8_t rhport);
|
||||
uint16_t dfu_rtd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
|
||||
bool dfu_rtd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
|
||||
|
||||
@@ -300,6 +300,19 @@ typedef struct TU_ATTR_PACKED
|
||||
int8_t pan; // using AC Pan
|
||||
} hid_mouse_report_t;
|
||||
|
||||
|
||||
// Absolute Mouse: same as the Standard (relative) Mouse Report but
|
||||
// with int16_t instead of int8_t for X and Y coordinates.
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t buttons; /**< buttons mask for currently pressed buttons in the mouse. */
|
||||
int16_t x; /**< Current x position of the mouse. */
|
||||
int16_t y; /**< Current y position of the mouse. */
|
||||
int8_t wheel; /**< Current delta wheel movement on the mouse. */
|
||||
int8_t pan; // using AC Pan
|
||||
} hid_abs_mouse_report_t;
|
||||
|
||||
|
||||
/// Standard Mouse Buttons Bitmap
|
||||
typedef enum
|
||||
{
|
||||
|
||||
@@ -46,12 +46,13 @@ typedef struct
|
||||
uint8_t ep_out; // optional Out endpoint
|
||||
uint8_t itf_protocol; // Boot mouse or keyboard
|
||||
|
||||
uint8_t protocol_mode; // Boot (0) or Report protocol (1)
|
||||
uint8_t idle_rate; // up to application to handle idle rate
|
||||
uint16_t report_desc_len;
|
||||
CFG_TUSB_MEM_ALIGN uint8_t protocol_mode; // Boot (0) or Report protocol (1)
|
||||
CFG_TUSB_MEM_ALIGN uint8_t idle_rate; // up to application to handle idle rate
|
||||
|
||||
CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_HID_EP_BUFSIZE];
|
||||
CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_HID_EP_BUFSIZE];
|
||||
CFG_TUSB_MEM_ALIGN uint8_t ctrl_buf[CFG_TUD_HID_EP_BUFSIZE];
|
||||
|
||||
// TODO save hid descriptor since host can specifically request this after enumeration
|
||||
// Note: HID descriptor may be not available from application after enumeration
|
||||
@@ -146,6 +147,19 @@ bool tud_hid_n_mouse_report(uint8_t instance, uint8_t report_id,
|
||||
return tud_hid_n_report(instance, report_id, &report, sizeof(report));
|
||||
}
|
||||
|
||||
bool tud_hid_n_abs_mouse_report(uint8_t instance, uint8_t report_id, uint8_t buttons, int16_t x, int16_t y, int8_t vertical, int8_t horizontal)
|
||||
{
|
||||
hid_abs_mouse_report_t report =
|
||||
{
|
||||
.buttons = buttons,
|
||||
.x = x,
|
||||
.y = y,
|
||||
.wheel = vertical,
|
||||
.pan = horizontal
|
||||
};
|
||||
return tud_hid_n_report(instance, report_id, &report, sizeof(report));
|
||||
}
|
||||
|
||||
bool tud_hid_n_gamepad_report(uint8_t instance, uint8_t report_id,
|
||||
int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons) {
|
||||
hid_gamepad_report_t report =
|
||||
@@ -166,11 +180,14 @@ bool tud_hid_n_gamepad_report(uint8_t instance, uint8_t report_id,
|
||||
//--------------------------------------------------------------------+
|
||||
// USBD-CLASS API
|
||||
//--------------------------------------------------------------------+
|
||||
void hidd_init(void)
|
||||
{
|
||||
void hidd_init(void) {
|
||||
hidd_reset(0);
|
||||
}
|
||||
|
||||
bool hidd_deinit(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void hidd_reset(uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
@@ -279,7 +296,7 @@ bool hidd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t
|
||||
uint8_t const report_type = tu_u16_high(request->wValue);
|
||||
uint8_t const report_id = tu_u16_low(request->wValue);
|
||||
|
||||
uint8_t* report_buf = p_hid->epin_buf;
|
||||
uint8_t* report_buf = p_hid->ctrl_buf;
|
||||
uint16_t req_len = tu_min16(request->wLength, CFG_TUD_HID_EP_BUFSIZE);
|
||||
|
||||
uint16_t xferlen = 0;
|
||||
@@ -296,22 +313,22 @@ bool hidd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t
|
||||
xferlen += tud_hid_get_report_cb(hid_itf, report_id, (hid_report_type_t) report_type, report_buf, req_len);
|
||||
TU_ASSERT( xferlen > 0 );
|
||||
|
||||
tud_control_xfer(rhport, request, p_hid->epin_buf, xferlen);
|
||||
tud_control_xfer(rhport, request, p_hid->ctrl_buf, xferlen);
|
||||
}
|
||||
break;
|
||||
|
||||
case HID_REQ_CONTROL_SET_REPORT:
|
||||
if ( stage == CONTROL_STAGE_SETUP )
|
||||
{
|
||||
TU_VERIFY(request->wLength <= sizeof(p_hid->epout_buf));
|
||||
tud_control_xfer(rhport, request, p_hid->epout_buf, request->wLength);
|
||||
TU_VERIFY(request->wLength <= sizeof(p_hid->ctrl_buf));
|
||||
tud_control_xfer(rhport, request, p_hid->ctrl_buf, request->wLength);
|
||||
}
|
||||
else if ( stage == CONTROL_STAGE_ACK )
|
||||
{
|
||||
uint8_t const report_type = tu_u16_high(request->wValue);
|
||||
uint8_t const report_id = tu_u16_low(request->wValue);
|
||||
|
||||
uint8_t const* report_buf = p_hid->epout_buf;
|
||||
uint8_t const* report_buf = p_hid->ctrl_buf;
|
||||
uint16_t report_len = tu_min16(request->wLength, CFG_TUD_HID_EP_BUFSIZE);
|
||||
|
||||
// If host request a specific Report ID, extract report ID in buffer before invoking callback
|
||||
|
||||
@@ -72,6 +72,16 @@ bool tud_hid_n_keyboard_report(uint8_t instance, uint8_t report_id, uint8_t modi
|
||||
// use template layout report as defined by hid_mouse_report_t
|
||||
bool tud_hid_n_mouse_report(uint8_t instance, uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal);
|
||||
|
||||
// ABSOLUTE MOUSE: convenient helper to send absolute mouse report if application
|
||||
// use template layout report as defined by hid_abs_mouse_report_t
|
||||
bool tud_hid_n_abs_mouse_report(uint8_t instance, uint8_t report_id, uint8_t buttons, int16_t x, int16_t y, int8_t vertical, int8_t horizontal);
|
||||
|
||||
|
||||
static inline bool tud_hid_abs_mouse_report(uint8_t report_id, uint8_t buttons, int16_t x, int16_t y, int8_t vertical, int8_t horizontal)
|
||||
{
|
||||
return tud_hid_n_abs_mouse_report(0, report_id, buttons, x, y, vertical, horizontal);
|
||||
}
|
||||
|
||||
// Gamepad: convenient helper to send gamepad report if application
|
||||
// use template layout report TUD_HID_REPORT_DESC_GAMEPAD
|
||||
bool tud_hid_n_gamepad_report(uint8_t instance, uint8_t report_id, int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons);
|
||||
@@ -268,6 +278,55 @@ static inline bool tud_hid_gamepad_report(uint8_t report_id, int8_t x, int8_t y
|
||||
HID_COLLECTION_END , \
|
||||
HID_COLLECTION_END \
|
||||
|
||||
// Absolute Mouse Report Descriptor Template
|
||||
#define TUD_HID_REPORT_DESC_ABSMOUSE(...) \
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_MOUSE ) ,\
|
||||
HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
|
||||
/* Report ID if any */\
|
||||
__VA_ARGS__ \
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_POINTER ) ,\
|
||||
HID_COLLECTION ( HID_COLLECTION_PHYSICAL ) ,\
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_BUTTON ) ,\
|
||||
HID_USAGE_MIN ( 1 ) ,\
|
||||
HID_USAGE_MAX ( 5 ) ,\
|
||||
HID_LOGICAL_MIN ( 0 ) ,\
|
||||
HID_LOGICAL_MAX ( 1 ) ,\
|
||||
/* Left, Right, Middle, Backward, Forward buttons */ \
|
||||
HID_REPORT_COUNT( 5 ) ,\
|
||||
HID_REPORT_SIZE ( 1 ) ,\
|
||||
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
|
||||
/* 3 bit padding */ \
|
||||
HID_REPORT_COUNT( 1 ) ,\
|
||||
HID_REPORT_SIZE ( 3 ) ,\
|
||||
HID_INPUT ( HID_CONSTANT ) ,\
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
|
||||
/* X, Y absolute position [0, 32767] */ \
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_X ) ,\
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_Y ) ,\
|
||||
HID_LOGICAL_MIN ( 0x00 ) ,\
|
||||
HID_LOGICAL_MAX_N( 0x7FFF, 2 ) ,\
|
||||
HID_REPORT_SIZE ( 16 ) ,\
|
||||
HID_REPORT_COUNT ( 2 ) ,\
|
||||
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
|
||||
/* Vertical wheel scroll [-127, 127] */ \
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_WHEEL ) ,\
|
||||
HID_LOGICAL_MIN ( 0x81 ) ,\
|
||||
HID_LOGICAL_MAX ( 0x7f ) ,\
|
||||
HID_REPORT_COUNT( 1 ) ,\
|
||||
HID_REPORT_SIZE ( 8 ) ,\
|
||||
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ) ,\
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_CONSUMER ), \
|
||||
/* Horizontal wheel scroll [-127, 127] */ \
|
||||
HID_USAGE_N ( HID_USAGE_CONSUMER_AC_PAN, 2 ), \
|
||||
HID_LOGICAL_MIN ( 0x81 ), \
|
||||
HID_LOGICAL_MAX ( 0x7f ), \
|
||||
HID_REPORT_COUNT( 1 ), \
|
||||
HID_REPORT_SIZE ( 8 ), \
|
||||
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ), \
|
||||
HID_COLLECTION_END , \
|
||||
HID_COLLECTION_END \
|
||||
|
||||
// Consumer Control Report Descriptor Template
|
||||
#define TUD_HID_REPORT_DESC_CONSUMER(...) \
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_CONSUMER ) ,\
|
||||
@@ -408,6 +467,7 @@ static inline bool tud_hid_gamepad_report(uint8_t report_id, int8_t x, int8_t y
|
||||
// Internal Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void hidd_init (void);
|
||||
bool hidd_deinit (void);
|
||||
void hidd_reset (uint8_t rhport);
|
||||
uint16_t hidd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
|
||||
bool hidd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
|
||||
|
||||
@@ -39,22 +39,22 @@
|
||||
#endif
|
||||
|
||||
#define TU_LOG_DRV(...) TU_LOG(CFG_TUH_HID_LOG_LEVEL, __VA_ARGS__)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
typedef struct
|
||||
{
|
||||
typedef struct {
|
||||
uint8_t daddr;
|
||||
|
||||
uint8_t itf_num;
|
||||
uint8_t ep_in;
|
||||
uint8_t ep_out;
|
||||
bool mounted; // Enumeration is complete
|
||||
|
||||
uint8_t itf_protocol; // None, Keyboard, Mouse
|
||||
uint8_t protocol_mode; // Boot (0) or Report protocol (1)
|
||||
|
||||
uint8_t report_desc_type;
|
||||
uint8_t report_desc_type;
|
||||
uint16_t report_desc_len;
|
||||
|
||||
uint16_t epin_size;
|
||||
@@ -67,81 +67,62 @@ typedef struct
|
||||
CFG_TUH_MEM_SECTION
|
||||
tu_static hidh_interface_t _hidh_itf[CFG_TUH_HID];
|
||||
|
||||
tu_static uint8_t _hidh_default_protocol = HID_PROTOCOL_BOOT;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Helper
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
hidh_interface_t* get_hid_itf(uint8_t daddr, uint8_t idx)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline hidh_interface_t* get_hid_itf(uint8_t daddr, uint8_t idx) {
|
||||
TU_ASSERT(daddr > 0 && idx < CFG_TUH_HID, NULL);
|
||||
hidh_interface_t* p_hid = &_hidh_itf[idx];
|
||||
return (p_hid->daddr == daddr) ? p_hid : NULL;
|
||||
}
|
||||
|
||||
// Get instance ID by endpoint address
|
||||
static uint8_t get_idx_by_epaddr(uint8_t daddr, uint8_t ep_addr)
|
||||
{
|
||||
for ( uint8_t idx = 0; idx < CFG_TUH_HID; idx++ )
|
||||
{
|
||||
hidh_interface_t const * p_hid = &_hidh_itf[idx];
|
||||
|
||||
if ( p_hid->daddr == daddr &&
|
||||
(p_hid->ep_in == ep_addr || p_hid->ep_out == ep_addr) )
|
||||
{
|
||||
static uint8_t get_idx_by_epaddr(uint8_t daddr, uint8_t ep_addr) {
|
||||
for (uint8_t idx = 0; idx < CFG_TUH_HID; idx++) {
|
||||
hidh_interface_t const* p_hid = &_hidh_itf[idx];
|
||||
if (p_hid->daddr == daddr &&
|
||||
(p_hid->ep_in == ep_addr || p_hid->ep_out == ep_addr)) {
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
|
||||
return TUSB_INDEX_INVALID_8;
|
||||
}
|
||||
|
||||
static hidh_interface_t* find_new_itf(void)
|
||||
{
|
||||
for(uint8_t i=0; i<CFG_TUH_HID; i++)
|
||||
{
|
||||
static hidh_interface_t* find_new_itf(void) {
|
||||
for (uint8_t i = 0; i < CFG_TUH_HID; i++) {
|
||||
if (_hidh_itf[i].daddr == 0) return &_hidh_itf[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Interface API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
uint8_t tuh_hid_itf_get_count(uint8_t daddr)
|
||||
{
|
||||
uint8_t tuh_hid_itf_get_count(uint8_t daddr) {
|
||||
uint8_t count = 0;
|
||||
|
||||
for(uint8_t i=0; i<CFG_TUH_HID; i++)
|
||||
{
|
||||
for (uint8_t i = 0; i < CFG_TUH_HID; i++) {
|
||||
if (_hidh_itf[i].daddr == daddr) count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
uint8_t tuh_hid_itf_get_total_count(void)
|
||||
{
|
||||
uint8_t tuh_hid_itf_get_total_count(void) {
|
||||
uint8_t count = 0;
|
||||
|
||||
for(uint8_t i=0; i<CFG_TUH_HID; i++)
|
||||
{
|
||||
for (uint8_t i = 0; i < CFG_TUH_HID; i++) {
|
||||
if (_hidh_itf[i].daddr != 0) count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
bool tuh_hid_mounted(uint8_t daddr, uint8_t idx)
|
||||
{
|
||||
bool tuh_hid_mounted(uint8_t daddr, uint8_t idx) {
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
return p_hid != NULL;
|
||||
TU_VERIFY(p_hid);
|
||||
return p_hid->mounted;
|
||||
}
|
||||
|
||||
bool tuh_hid_itf_get_info(uint8_t daddr, uint8_t idx, tuh_itf_info_t* info)
|
||||
{
|
||||
bool tuh_hid_itf_get_info(uint8_t daddr, uint8_t idx, tuh_itf_info_t* info) {
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
TU_VERIFY(p_hid && info);
|
||||
|
||||
@@ -149,34 +130,30 @@ bool tuh_hid_itf_get_info(uint8_t daddr, uint8_t idx, tuh_itf_info_t* info)
|
||||
|
||||
// re-construct descriptor
|
||||
tusb_desc_interface_t* desc = &info->desc;
|
||||
desc->bLength = sizeof(tusb_desc_interface_t);
|
||||
desc->bDescriptorType = TUSB_DESC_INTERFACE;
|
||||
desc->bLength = sizeof(tusb_desc_interface_t);
|
||||
desc->bDescriptorType = TUSB_DESC_INTERFACE;
|
||||
|
||||
desc->bInterfaceNumber = p_hid->itf_num;
|
||||
desc->bAlternateSetting = 0;
|
||||
desc->bNumEndpoints = (uint8_t) ((p_hid->ep_in ? 1u : 0u) + (p_hid->ep_out ? 1u : 0u));
|
||||
desc->bInterfaceClass = TUSB_CLASS_HID;
|
||||
desc->bInterfaceNumber = p_hid->itf_num;
|
||||
desc->bAlternateSetting = 0;
|
||||
desc->bNumEndpoints = (uint8_t) ((p_hid->ep_in ? 1u : 0u) + (p_hid->ep_out ? 1u : 0u));
|
||||
desc->bInterfaceClass = TUSB_CLASS_HID;
|
||||
desc->bInterfaceSubClass = (p_hid->itf_protocol ? HID_SUBCLASS_BOOT : HID_SUBCLASS_NONE);
|
||||
desc->bInterfaceProtocol = p_hid->itf_protocol;
|
||||
desc->iInterface = 0; // not used yet
|
||||
desc->iInterface = 0; // not used yet
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t tuh_hid_itf_get_index(uint8_t daddr, uint8_t itf_num)
|
||||
{
|
||||
for ( uint8_t idx = 0; idx < CFG_TUH_HID; idx++ )
|
||||
{
|
||||
hidh_interface_t const * p_hid = &_hidh_itf[idx];
|
||||
|
||||
if ( p_hid->daddr == daddr && p_hid->itf_num == itf_num) return idx;
|
||||
uint8_t tuh_hid_itf_get_index(uint8_t daddr, uint8_t itf_num) {
|
||||
for (uint8_t idx = 0; idx < CFG_TUH_HID; idx++) {
|
||||
hidh_interface_t const* p_hid = &_hidh_itf[idx];
|
||||
if (p_hid->daddr == daddr && p_hid->itf_num == itf_num) return idx;
|
||||
}
|
||||
|
||||
return TUSB_INDEX_INVALID_8;
|
||||
}
|
||||
|
||||
uint8_t tuh_hid_interface_protocol(uint8_t daddr, uint8_t idx)
|
||||
{
|
||||
uint8_t tuh_hid_interface_protocol(uint8_t daddr, uint8_t idx) {
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
return p_hid ? p_hid->itf_protocol : 0;
|
||||
}
|
||||
@@ -184,150 +161,179 @@ uint8_t tuh_hid_interface_protocol(uint8_t daddr, uint8_t idx)
|
||||
//--------------------------------------------------------------------+
|
||||
// Control Endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
uint8_t tuh_hid_get_protocol(uint8_t daddr, uint8_t idx)
|
||||
{
|
||||
uint8_t tuh_hid_get_protocol(uint8_t daddr, uint8_t idx) {
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
return p_hid ? p_hid->protocol_mode : 0;
|
||||
}
|
||||
|
||||
static void set_protocol_complete(tuh_xfer_t* xfer)
|
||||
{
|
||||
static void set_protocol_complete(tuh_xfer_t* xfer) {
|
||||
uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
|
||||
uint8_t const daddr = xfer->daddr;
|
||||
uint8_t const idx = tuh_hid_itf_get_index(daddr, itf_num);
|
||||
uint8_t const daddr = xfer->daddr;
|
||||
uint8_t const idx = tuh_hid_itf_get_index(daddr, itf_num);
|
||||
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
TU_VERIFY(p_hid, );
|
||||
TU_VERIFY(p_hid,);
|
||||
|
||||
if (XFER_RESULT_SUCCESS == xfer->result)
|
||||
{
|
||||
if (XFER_RESULT_SUCCESS == xfer->result) {
|
||||
p_hid->protocol_mode = (uint8_t) tu_le16toh(xfer->setup->wValue);
|
||||
}
|
||||
|
||||
if (tuh_hid_set_protocol_complete_cb)
|
||||
{
|
||||
if (tuh_hid_set_protocol_complete_cb) {
|
||||
tuh_hid_set_protocol_complete_cb(daddr, idx, p_hid->protocol_mode);
|
||||
}
|
||||
}
|
||||
|
||||
static bool _hidh_set_protocol(uint8_t daddr, uint8_t itf_num, uint8_t protocol, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
|
||||
{
|
||||
void tuh_hid_set_default_protocol(uint8_t protocol) {
|
||||
_hidh_default_protocol = protocol;
|
||||
}
|
||||
|
||||
static bool _hidh_set_protocol(uint8_t daddr, uint8_t itf_num, uint8_t protocol,
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
|
||||
TU_LOG_DRV("HID Set Protocol = %d\r\n", protocol);
|
||||
|
||||
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_PROTOCOL,
|
||||
.wValue = protocol,
|
||||
.wIndex = itf_num,
|
||||
.wLength = 0
|
||||
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_PROTOCOL,
|
||||
.wValue = protocol,
|
||||
.wIndex = itf_num,
|
||||
.wLength = 0
|
||||
};
|
||||
|
||||
tuh_xfer_t xfer =
|
||||
{
|
||||
.daddr = daddr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
.buffer = NULL,
|
||||
.complete_cb = complete_cb,
|
||||
.user_data = user_data
|
||||
tuh_xfer_t xfer = {
|
||||
.daddr = daddr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
.buffer = NULL,
|
||||
.complete_cb = complete_cb,
|
||||
.user_data = user_data
|
||||
};
|
||||
|
||||
return tuh_control_xfer(&xfer);
|
||||
}
|
||||
|
||||
bool tuh_hid_set_protocol(uint8_t daddr, uint8_t idx, uint8_t protocol)
|
||||
{
|
||||
bool tuh_hid_set_protocol(uint8_t daddr, uint8_t idx, uint8_t protocol) {
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
TU_VERIFY(p_hid && p_hid->itf_protocol != HID_ITF_PROTOCOL_NONE);
|
||||
|
||||
return _hidh_set_protocol(daddr, p_hid->itf_num, protocol, set_protocol_complete, 0);
|
||||
}
|
||||
|
||||
static void set_report_complete(tuh_xfer_t* xfer)
|
||||
{
|
||||
TU_LOG_DRV("HID Set Report complete\r\n");
|
||||
static void get_report_complete(tuh_xfer_t* xfer) {
|
||||
TU_LOG_DRV("HID Get Report complete\r\n");
|
||||
|
||||
if (tuh_hid_set_report_complete_cb)
|
||||
{
|
||||
if (tuh_hid_get_report_complete_cb) {
|
||||
uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
|
||||
uint8_t const idx = tuh_hid_itf_get_index(xfer->daddr, itf_num);
|
||||
uint8_t const idx = tuh_hid_itf_get_index(xfer->daddr, itf_num);
|
||||
|
||||
uint8_t const report_type = tu_u16_high(xfer->setup->wValue);
|
||||
uint8_t const report_id = tu_u16_low(xfer->setup->wValue);
|
||||
uint8_t const report_id = tu_u16_low(xfer->setup->wValue);
|
||||
|
||||
tuh_hid_get_report_complete_cb(xfer->daddr, idx, report_id, report_type,
|
||||
(xfer->result == XFER_RESULT_SUCCESS) ? xfer->setup->wLength : 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool tuh_hid_get_report(uint8_t daddr, uint8_t idx, uint8_t report_id, uint8_t report_type, void* report, uint16_t len) {
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
TU_VERIFY(p_hid);
|
||||
TU_LOG_DRV("HID Get Report: id = %u, type = %u, len = %u\r\n", report_id, report_type, len);
|
||||
|
||||
tusb_control_request_t const request = {
|
||||
.bmRequestType_bit = {
|
||||
.recipient = TUSB_REQ_RCPT_INTERFACE,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_IN
|
||||
},
|
||||
.bRequest = HID_REQ_CONTROL_GET_REPORT,
|
||||
.wValue = tu_htole16(tu_u16(report_type, report_id)),
|
||||
.wIndex = tu_htole16((uint16_t) p_hid->itf_num),
|
||||
.wLength = len
|
||||
};
|
||||
|
||||
tuh_xfer_t xfer = {
|
||||
.daddr = daddr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
.buffer = report,
|
||||
.complete_cb = get_report_complete,
|
||||
.user_data = 0
|
||||
};
|
||||
|
||||
return tuh_control_xfer(&xfer);
|
||||
}
|
||||
|
||||
static void set_report_complete(tuh_xfer_t* xfer) {
|
||||
TU_LOG_DRV("HID Set Report complete\r\n");
|
||||
|
||||
if (tuh_hid_set_report_complete_cb) {
|
||||
uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
|
||||
uint8_t const idx = tuh_hid_itf_get_index(xfer->daddr, itf_num);
|
||||
|
||||
uint8_t const report_type = tu_u16_high(xfer->setup->wValue);
|
||||
uint8_t const report_id = tu_u16_low(xfer->setup->wValue);
|
||||
|
||||
tuh_hid_set_report_complete_cb(xfer->daddr, idx, report_id, report_type,
|
||||
(xfer->result == XFER_RESULT_SUCCESS) ? xfer->setup->wLength : 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool tuh_hid_set_report(uint8_t daddr, uint8_t idx, uint8_t report_id, uint8_t report_type, void* report, uint16_t len)
|
||||
{
|
||||
bool tuh_hid_set_report(uint8_t daddr, uint8_t idx, uint8_t report_id, uint8_t report_type, void* report, uint16_t len) {
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
TU_VERIFY(p_hid);
|
||||
|
||||
TU_LOG_DRV("HID Set Report: id = %u, type = %u, len = %u\r\n", report_id, report_type, len);
|
||||
|
||||
tusb_control_request_t const request =
|
||||
{
|
||||
.bmRequestType_bit =
|
||||
{
|
||||
.recipient = TUSB_REQ_RCPT_INTERFACE,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_OUT
|
||||
},
|
||||
.bRequest = HID_REQ_CONTROL_SET_REPORT,
|
||||
.wValue = tu_htole16(tu_u16(report_type, report_id)),
|
||||
.wIndex = tu_htole16((uint16_t)p_hid->itf_num),
|
||||
.wLength = len
|
||||
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_REPORT,
|
||||
.wValue = tu_htole16(tu_u16(report_type, report_id)),
|
||||
.wIndex = tu_htole16((uint16_t) p_hid->itf_num),
|
||||
.wLength = len
|
||||
};
|
||||
|
||||
tuh_xfer_t xfer =
|
||||
{
|
||||
.daddr = daddr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
.buffer = report,
|
||||
.complete_cb = set_report_complete,
|
||||
.user_data = 0
|
||||
tuh_xfer_t xfer = {
|
||||
.daddr = daddr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
.buffer = report,
|
||||
.complete_cb = set_report_complete,
|
||||
.user_data = 0
|
||||
};
|
||||
|
||||
return tuh_control_xfer(&xfer);
|
||||
}
|
||||
|
||||
static bool _hidh_set_idle(uint8_t daddr, uint8_t itf_num, uint16_t idle_rate, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
|
||||
{
|
||||
static bool _hidh_set_idle(uint8_t daddr, 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_LOG_DRV("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 = tu_htole16(idle_rate),
|
||||
.wIndex = tu_htole16((uint16_t)itf_num),
|
||||
.wLength = 0
|
||||
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 = tu_htole16(idle_rate),
|
||||
.wIndex = tu_htole16((uint16_t) itf_num),
|
||||
.wLength = 0
|
||||
};
|
||||
|
||||
tuh_xfer_t xfer =
|
||||
{
|
||||
.daddr = daddr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
.buffer = NULL,
|
||||
.complete_cb = complete_cb,
|
||||
.user_data = user_data
|
||||
tuh_xfer_t xfer = {
|
||||
.daddr = daddr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
.buffer = NULL,
|
||||
.complete_cb = complete_cb,
|
||||
.user_data = user_data
|
||||
};
|
||||
|
||||
return tuh_control_xfer(&xfer);
|
||||
@@ -338,68 +344,60 @@ static bool _hidh_set_idle(uint8_t daddr, uint8_t itf_num, uint16_t idle_rate, t
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Check if HID interface is ready to receive report
|
||||
bool tuh_hid_receive_ready(uint8_t dev_addr, uint8_t idx)
|
||||
{
|
||||
bool tuh_hid_receive_ready(uint8_t dev_addr, uint8_t idx) {
|
||||
hidh_interface_t* p_hid = get_hid_itf(dev_addr, idx);
|
||||
TU_VERIFY(p_hid);
|
||||
|
||||
return !usbh_edpt_busy(dev_addr, p_hid->ep_in);
|
||||
}
|
||||
|
||||
bool tuh_hid_receive_report(uint8_t daddr, uint8_t idx)
|
||||
{
|
||||
bool tuh_hid_receive_report(uint8_t daddr, uint8_t idx) {
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
TU_VERIFY(p_hid);
|
||||
|
||||
// claim endpoint
|
||||
TU_VERIFY( usbh_edpt_claim(daddr, p_hid->ep_in) );
|
||||
TU_VERIFY(usbh_edpt_claim(daddr, p_hid->ep_in));
|
||||
|
||||
if ( !usbh_edpt_xfer(daddr, p_hid->ep_in, p_hid->epin_buf, p_hid->epin_size) )
|
||||
{
|
||||
if (!usbh_edpt_xfer(daddr, p_hid->ep_in, p_hid->epin_buf, p_hid->epin_size)) {
|
||||
usbh_edpt_release(daddr, p_hid->ep_in);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tuh_hid_send_ready(uint8_t dev_addr, uint8_t idx)
|
||||
{
|
||||
bool tuh_hid_receive_abort(uint8_t dev_addr, uint8_t idx) {
|
||||
hidh_interface_t* p_hid = get_hid_itf(dev_addr, idx);
|
||||
TU_VERIFY(p_hid);
|
||||
return tuh_edpt_abort_xfer(dev_addr, p_hid->ep_in);
|
||||
}
|
||||
|
||||
bool tuh_hid_send_ready(uint8_t dev_addr, uint8_t idx) {
|
||||
hidh_interface_t* p_hid = get_hid_itf(dev_addr, idx);
|
||||
TU_VERIFY(p_hid);
|
||||
return !usbh_edpt_busy(dev_addr, p_hid->ep_out);
|
||||
}
|
||||
|
||||
bool tuh_hid_send_report(uint8_t daddr, uint8_t idx, uint8_t report_id, const void* report, uint16_t len)
|
||||
{
|
||||
bool tuh_hid_send_report(uint8_t daddr, uint8_t idx, uint8_t report_id, const void* report, uint16_t len) {
|
||||
TU_LOG_DRV("HID Send Report %d\r\n", report_id);
|
||||
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
TU_VERIFY(p_hid);
|
||||
|
||||
if (p_hid->ep_out == 0)
|
||||
{
|
||||
if (p_hid->ep_out == 0) {
|
||||
// This HID does not have an out endpoint (other than control)
|
||||
return false;
|
||||
}
|
||||
else if (len > CFG_TUH_HID_EPOUT_BUFSIZE ||
|
||||
(report_id != 0 && len > (CFG_TUH_HID_EPOUT_BUFSIZE - 1)))
|
||||
{
|
||||
} else if (len > CFG_TUH_HID_EPOUT_BUFSIZE ||
|
||||
(report_id != 0 && len > (CFG_TUH_HID_EPOUT_BUFSIZE - 1))) {
|
||||
// ep_out buffer is not large enough to hold contents
|
||||
return false;
|
||||
}
|
||||
|
||||
// claim endpoint
|
||||
TU_VERIFY( usbh_edpt_claim(daddr, p_hid->ep_out) );
|
||||
TU_VERIFY(usbh_edpt_claim(daddr, p_hid->ep_out));
|
||||
|
||||
if (report_id == 0)
|
||||
{
|
||||
if (report_id == 0) {
|
||||
// No report ID in transmission
|
||||
memcpy(&p_hid->epout_buf[0], report, len);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
p_hid->epout_buf[0] = report_id;
|
||||
memcpy(&p_hid->epout_buf[1], report, len);
|
||||
++len; // 1 more byte for report_id
|
||||
@@ -407,8 +405,7 @@ bool tuh_hid_send_report(uint8_t daddr, uint8_t idx, uint8_t report_id, const vo
|
||||
|
||||
TU_LOG3_MEM(p_hid->epout_buf, len, 2);
|
||||
|
||||
if ( !usbh_edpt_xfer(daddr, p_hid->ep_out, p_hid->epout_buf, len) )
|
||||
{
|
||||
if (!usbh_edpt_xfer(daddr, p_hid->ep_out, p_hid->epout_buf, len)) {
|
||||
usbh_edpt_release(daddr, p_hid->ep_out);
|
||||
return false;
|
||||
}
|
||||
@@ -419,13 +416,17 @@ bool tuh_hid_send_report(uint8_t daddr, uint8_t idx, uint8_t report_id, const vo
|
||||
//--------------------------------------------------------------------+
|
||||
// USBH API
|
||||
//--------------------------------------------------------------------+
|
||||
void hidh_init(void)
|
||||
{
|
||||
bool hidh_init(void) {
|
||||
TU_LOG_DRV("sizeof(hidh_interface_t) = %u\r\n", sizeof(hidh_interface_t));
|
||||
tu_memclr(_hidh_itf, sizeof(_hidh_itf));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hidh_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
|
||||
{
|
||||
bool hidh_deinit(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hidh_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
|
||||
(void) result;
|
||||
|
||||
uint8_t const dir = tu_edpt_dir(ep_addr);
|
||||
@@ -434,29 +435,26 @@ bool hidh_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
TU_VERIFY(p_hid);
|
||||
|
||||
if ( dir == TUSB_DIR_IN )
|
||||
{
|
||||
if (dir == TUSB_DIR_IN) {
|
||||
TU_LOG_DRV(" Get Report callback (%u, %u)\r\n", daddr, idx);
|
||||
TU_LOG3_MEM(p_hid->epin_buf, xferred_bytes, 2);
|
||||
tuh_hid_report_received_cb(daddr, idx, p_hid->epin_buf, (uint16_t) xferred_bytes);
|
||||
}else
|
||||
{
|
||||
if (tuh_hid_report_sent_cb) tuh_hid_report_sent_cb(daddr, idx, p_hid->epout_buf, (uint16_t) xferred_bytes);
|
||||
} else {
|
||||
if (tuh_hid_report_sent_cb) {
|
||||
tuh_hid_report_sent_cb(daddr, idx, p_hid->epout_buf, (uint16_t) xferred_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void hidh_close(uint8_t daddr)
|
||||
{
|
||||
for(uint8_t i=0; i<CFG_TUH_HID; i++)
|
||||
{
|
||||
void hidh_close(uint8_t daddr) {
|
||||
for (uint8_t i = 0; i < CFG_TUH_HID; i++) {
|
||||
hidh_interface_t* p_hid = &_hidh_itf[i];
|
||||
if (p_hid->daddr == daddr)
|
||||
{
|
||||
if (p_hid->daddr == daddr) {
|
||||
TU_LOG_DRV(" HIDh close addr = %u index = %u\r\n", daddr, i);
|
||||
if(tuh_hid_umount_cb) tuh_hid_umount_cb(daddr, i);
|
||||
p_hid->daddr = 0;
|
||||
if (tuh_hid_umount_cb) tuh_hid_umount_cb(daddr, i);
|
||||
tu_memclr(p_hid, sizeof(hidh_interface_t));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -465,25 +463,22 @@ void hidh_close(uint8_t daddr)
|
||||
// Enumeration
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
bool hidh_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *desc_itf, uint16_t max_len)
|
||||
{
|
||||
bool hidh_open(uint8_t rhport, uint8_t daddr, 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_LOG_DRV("[%u] HID opening Interface %u\r\n", daddr, desc_itf->bInterfaceNumber);
|
||||
|
||||
// len = interface + hid + n*endpoints
|
||||
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;
|
||||
uint8_t const* p_desc = (uint8_t const*) desc_itf;
|
||||
|
||||
//------------- HID descriptor -------------//
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
tusb_hid_descriptor_hid_t const *desc_hid = (tusb_hid_descriptor_hid_t const *) p_desc;
|
||||
tusb_hid_descriptor_hid_t const* desc_hid = (tusb_hid_descriptor_hid_t const*) p_desc;
|
||||
TU_ASSERT(HID_DESC_TYPE_HID == desc_hid->bDescriptorType);
|
||||
|
||||
hidh_interface_t* p_hid = find_new_itf();
|
||||
@@ -492,38 +487,33 @@ bool hidh_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *desc_
|
||||
|
||||
//------------- Endpoint Descriptors -------------//
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc;
|
||||
tusb_desc_endpoint_t const* desc_ep = (tusb_desc_endpoint_t const*) p_desc;
|
||||
|
||||
for(int i = 0; i < desc_itf->bNumEndpoints; i++)
|
||||
{
|
||||
for (int i = 0; i < desc_itf->bNumEndpoints; i++) {
|
||||
TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType);
|
||||
TU_ASSERT( tuh_edpt_open(daddr, desc_ep) );
|
||||
TU_ASSERT(tuh_edpt_open(daddr, desc_ep));
|
||||
|
||||
if(tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN)
|
||||
{
|
||||
p_hid->ep_in = desc_ep->bEndpointAddress;
|
||||
if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
|
||||
p_hid->ep_in = desc_ep->bEndpointAddress;
|
||||
p_hid->epin_size = tu_edpt_packet_size(desc_ep);
|
||||
}
|
||||
else
|
||||
{
|
||||
p_hid->ep_out = desc_ep->bEndpointAddress;
|
||||
} else {
|
||||
p_hid->ep_out = desc_ep->bEndpointAddress;
|
||||
p_hid->epout_size = tu_edpt_packet_size(desc_ep);
|
||||
}
|
||||
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
desc_ep = (tusb_desc_endpoint_t const *) p_desc;
|
||||
desc_ep = (tusb_desc_endpoint_t const*) p_desc;
|
||||
}
|
||||
|
||||
p_hid->itf_num = desc_itf->bInterfaceNumber;
|
||||
p_hid->itf_num = desc_itf->bInterfaceNumber;
|
||||
|
||||
// Assume bNumDescriptors = 1
|
||||
p_hid->report_desc_type = desc_hid->bReportType;
|
||||
p_hid->report_desc_len = tu_unaligned_read16(&desc_hid->wReportLength);
|
||||
p_hid->report_desc_len = tu_unaligned_read16(&desc_hid->wReportLength);
|
||||
|
||||
// Per HID Specs: default is Report protocol, though we will force Boot protocol when set_config
|
||||
p_hid->protocol_mode = HID_PROTOCOL_BOOT;
|
||||
if ( HID_SUBCLASS_BOOT == desc_itf->bInterfaceSubClass )
|
||||
{
|
||||
p_hid->protocol_mode = _hidh_default_protocol;
|
||||
if (HID_SUBCLASS_BOOT == desc_itf->bInterfaceSubClass) {
|
||||
p_hid->itf_protocol = desc_itf->bInterfaceProtocol;
|
||||
}
|
||||
|
||||
@@ -544,15 +534,14 @@ enum {
|
||||
static void config_driver_mount_complete(uint8_t daddr, uint8_t idx, uint8_t const* desc_report, uint16_t desc_len);
|
||||
static void process_set_config(tuh_xfer_t* xfer);
|
||||
|
||||
bool hidh_set_config(uint8_t daddr, uint8_t itf_num)
|
||||
{
|
||||
bool hidh_set_config(uint8_t daddr, uint8_t itf_num) {
|
||||
tusb_control_request_t request;
|
||||
request.wIndex = tu_htole16((uint16_t) itf_num);
|
||||
|
||||
tuh_xfer_t xfer;
|
||||
xfer.daddr = daddr;
|
||||
xfer.result = XFER_RESULT_SUCCESS;
|
||||
xfer.setup = &request;
|
||||
xfer.daddr = daddr;
|
||||
xfer.result = XFER_RESULT_SUCCESS;
|
||||
xfer.setup = &request;
|
||||
xfer.user_data = CONFG_SET_IDLE;
|
||||
|
||||
// fake request to kick-off the set config process
|
||||
@@ -561,71 +550,68 @@ bool hidh_set_config(uint8_t daddr, uint8_t itf_num)
|
||||
return true;
|
||||
}
|
||||
|
||||
static void process_set_config(tuh_xfer_t* xfer)
|
||||
{
|
||||
static void process_set_config(tuh_xfer_t* xfer) {
|
||||
// Stall is a valid response for SET_IDLE, sometime SET_PROTOCOL as well
|
||||
// therefore we could ignore its result
|
||||
if ( !(xfer->setup->bRequest == HID_REQ_CONTROL_SET_IDLE ||
|
||||
xfer->setup->bRequest == HID_REQ_CONTROL_SET_PROTOCOL) )
|
||||
{
|
||||
TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS, );
|
||||
if (!(xfer->setup->bRequest == HID_REQ_CONTROL_SET_IDLE ||
|
||||
xfer->setup->bRequest == HID_REQ_CONTROL_SET_PROTOCOL)) {
|
||||
TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS,);
|
||||
}
|
||||
|
||||
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;
|
||||
uint8_t const daddr = xfer->daddr;
|
||||
|
||||
uint8_t const idx = tuh_hid_itf_get_index(daddr, itf_num);
|
||||
uint8_t const idx = tuh_hid_itf_get_index(daddr, itf_num);
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
TU_VERIFY(p_hid, );
|
||||
TU_VERIFY(p_hid,);
|
||||
|
||||
switch(state)
|
||||
{
|
||||
case CONFG_SET_IDLE:
|
||||
{
|
||||
switch (state) {
|
||||
case CONFG_SET_IDLE: {
|
||||
// Idle rate = 0 mean only report when there is changes
|
||||
const uint16_t idle_rate = 0;
|
||||
const uintptr_t next_state = (p_hid->itf_protocol != HID_ITF_PROTOCOL_NONE) ? CONFIG_SET_PROTOCOL : CONFIG_GET_REPORT_DESC;
|
||||
const uintptr_t next_state = (p_hid->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;
|
||||
}
|
||||
break;
|
||||
|
||||
case CONFIG_SET_PROTOCOL:
|
||||
_hidh_set_protocol(daddr, p_hid->itf_num, HID_PROTOCOL_BOOT, process_set_config, CONFIG_GET_REPORT_DESC);
|
||||
break;
|
||||
_hidh_set_protocol(daddr, p_hid->itf_num, _hidh_default_protocol, process_set_config, CONFIG_GET_REPORT_DESC);
|
||||
break;
|
||||
|
||||
case CONFIG_GET_REPORT_DESC:
|
||||
// Get Report Descriptor if possible
|
||||
// using usbh enumeration buffer since report descriptor can be very long
|
||||
if( p_hid->report_desc_len > CFG_TUH_ENUMERATION_BUFSIZE )
|
||||
{
|
||||
if (p_hid->report_desc_len > CFG_TUH_ENUMERATION_BUFSIZE) {
|
||||
TU_LOG_DRV("HID Skip Report Descriptor since it is too large %u bytes\r\n", p_hid->report_desc_len);
|
||||
|
||||
// Driver is mounted without report descriptor
|
||||
config_driver_mount_complete(daddr, idx, NULL, 0);
|
||||
}else
|
||||
{
|
||||
tuh_descriptor_get_hid_report(daddr, itf_num, p_hid->report_desc_type, 0, usbh_get_enum_buf(), p_hid->report_desc_len, process_set_config, CONFIG_COMPLETE);
|
||||
} else {
|
||||
tuh_descriptor_get_hid_report(daddr, itf_num, p_hid->report_desc_type, 0,
|
||||
usbh_get_enum_buf(), p_hid->report_desc_len,
|
||||
process_set_config, CONFIG_COMPLETE);
|
||||
}
|
||||
break;
|
||||
|
||||
case CONFIG_COMPLETE:
|
||||
{
|
||||
case CONFIG_COMPLETE: {
|
||||
uint8_t const* desc_report = usbh_get_enum_buf();
|
||||
uint16_t const desc_len = tu_le16toh(xfer->setup->wLength);
|
||||
uint16_t const desc_len = tu_le16toh(xfer->setup->wLength);
|
||||
|
||||
config_driver_mount_complete(daddr, idx, desc_report, desc_len);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default: break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void config_driver_mount_complete(uint8_t daddr, uint8_t idx, uint8_t const* desc_report, uint16_t desc_len)
|
||||
{
|
||||
static void config_driver_mount_complete(uint8_t daddr, uint8_t idx, uint8_t const* desc_report, uint16_t desc_len) {
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
TU_VERIFY(p_hid, );
|
||||
TU_VERIFY(p_hid,);
|
||||
p_hid->mounted = true;
|
||||
|
||||
// enumeration is complete
|
||||
if (tuh_hid_mount_cb) tuh_hid_mount_cb(daddr, idx, desc_report, desc_len);
|
||||
@@ -638,21 +624,19 @@ static void config_driver_mount_complete(uint8_t daddr, uint8_t idx, uint8_t con
|
||||
// Report Descriptor Parser
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* report_info_arr, uint8_t arr_count, uint8_t const* desc_report, uint16_t desc_len)
|
||||
{
|
||||
uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* report_info_arr, uint8_t arr_count,
|
||||
uint8_t const* desc_report, uint16_t desc_len) {
|
||||
// Report Item 6.2.2.2 USB HID 1.11
|
||||
union TU_ATTR_PACKED
|
||||
{
|
||||
union TU_ATTR_PACKED {
|
||||
uint8_t byte;
|
||||
struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t size : 2;
|
||||
uint8_t type : 2;
|
||||
uint8_t tag : 4;
|
||||
struct TU_ATTR_PACKED {
|
||||
uint8_t size : 2;
|
||||
uint8_t type : 2;
|
||||
uint8_t tag : 4;
|
||||
};
|
||||
} header;
|
||||
|
||||
tu_memclr(report_info_arr, arr_count*sizeof(tuh_hid_report_info_t));
|
||||
tu_memclr(report_info_arr, arr_count * sizeof(tuh_hid_report_info_t));
|
||||
|
||||
uint8_t report_num = 0;
|
||||
tuh_hid_report_info_t* info = report_info_arr;
|
||||
@@ -662,114 +646,105 @@ uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* report_info_arr,
|
||||
// uint8_t ri_report_size = 0;
|
||||
|
||||
uint8_t ri_collection_depth = 0;
|
||||
|
||||
while(desc_len && report_num < arr_count)
|
||||
{
|
||||
while (desc_len && report_num < arr_count) {
|
||||
header.byte = *desc_report++;
|
||||
desc_len--;
|
||||
|
||||
uint8_t const tag = header.tag;
|
||||
uint8_t const tag = header.tag;
|
||||
uint8_t const type = header.type;
|
||||
uint8_t const size = header.size;
|
||||
|
||||
uint8_t const data8 = desc_report[0];
|
||||
|
||||
TU_LOG(3, "tag = %d, type = %d, size = %d, data = ", tag, type, size);
|
||||
for(uint32_t i=0; i<size; i++) TU_LOG(3, "%02X ", desc_report[i]);
|
||||
for (uint32_t i = 0; i < size; i++) TU_LOG(3, "%02X ", desc_report[i]);
|
||||
TU_LOG(3, "\r\n");
|
||||
|
||||
switch(type)
|
||||
{
|
||||
switch (type) {
|
||||
case RI_TYPE_MAIN:
|
||||
switch (tag)
|
||||
{
|
||||
switch (tag) {
|
||||
case RI_MAIN_INPUT: break;
|
||||
case RI_MAIN_OUTPUT: break;
|
||||
case RI_MAIN_FEATURE: break;
|
||||
|
||||
case RI_MAIN_COLLECTION:
|
||||
ri_collection_depth++;
|
||||
break;
|
||||
break;
|
||||
|
||||
case RI_MAIN_COLLECTION_END:
|
||||
ri_collection_depth--;
|
||||
if (ri_collection_depth == 0)
|
||||
{
|
||||
if (ri_collection_depth == 0) {
|
||||
info++;
|
||||
report_num++;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
default: break;
|
||||
default:break;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
case RI_TYPE_GLOBAL:
|
||||
switch(tag)
|
||||
{
|
||||
switch (tag) {
|
||||
case RI_GLOBAL_USAGE_PAGE:
|
||||
// only take in account the "usage page" before REPORT ID
|
||||
if ( ri_collection_depth == 0 ) memcpy(&info->usage_page, desc_report, size);
|
||||
break;
|
||||
if (ri_collection_depth == 0) memcpy(&info->usage_page, desc_report, size);
|
||||
break;
|
||||
|
||||
case RI_GLOBAL_LOGICAL_MIN : break;
|
||||
case RI_GLOBAL_LOGICAL_MAX : break;
|
||||
case RI_GLOBAL_PHYSICAL_MIN : break;
|
||||
case RI_GLOBAL_PHYSICAL_MAX : break;
|
||||
case RI_GLOBAL_LOGICAL_MIN: break;
|
||||
case RI_GLOBAL_LOGICAL_MAX: break;
|
||||
case RI_GLOBAL_PHYSICAL_MIN: break;
|
||||
case RI_GLOBAL_PHYSICAL_MAX: break;
|
||||
|
||||
case RI_GLOBAL_REPORT_ID:
|
||||
info->report_id = data8;
|
||||
break;
|
||||
break;
|
||||
|
||||
case RI_GLOBAL_REPORT_SIZE:
|
||||
// ri_report_size = data8;
|
||||
break;
|
||||
break;
|
||||
|
||||
case RI_GLOBAL_REPORT_COUNT:
|
||||
// ri_report_count = data8;
|
||||
break;
|
||||
break;
|
||||
|
||||
case RI_GLOBAL_UNIT_EXPONENT : break;
|
||||
case RI_GLOBAL_UNIT : break;
|
||||
case RI_GLOBAL_PUSH : break;
|
||||
case RI_GLOBAL_POP : break;
|
||||
case RI_GLOBAL_UNIT_EXPONENT: break;
|
||||
case RI_GLOBAL_UNIT: break;
|
||||
case RI_GLOBAL_PUSH: break;
|
||||
case RI_GLOBAL_POP: break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
case RI_TYPE_LOCAL:
|
||||
switch(tag)
|
||||
{
|
||||
switch (tag) {
|
||||
case RI_LOCAL_USAGE:
|
||||
// only take in account the "usage" before starting REPORT ID
|
||||
if ( ri_collection_depth == 0 ) info->usage = data8;
|
||||
break;
|
||||
if (ri_collection_depth == 0) info->usage = data8;
|
||||
break;
|
||||
|
||||
case RI_LOCAL_USAGE_MIN : break;
|
||||
case RI_LOCAL_USAGE_MAX : break;
|
||||
case RI_LOCAL_DESIGNATOR_INDEX : break;
|
||||
case RI_LOCAL_DESIGNATOR_MIN : break;
|
||||
case RI_LOCAL_DESIGNATOR_MAX : break;
|
||||
case RI_LOCAL_STRING_INDEX : break;
|
||||
case RI_LOCAL_STRING_MIN : break;
|
||||
case RI_LOCAL_STRING_MAX : break;
|
||||
case RI_LOCAL_DELIMITER : break;
|
||||
case RI_LOCAL_USAGE_MIN: break;
|
||||
case RI_LOCAL_USAGE_MAX: break;
|
||||
case RI_LOCAL_DESIGNATOR_INDEX: break;
|
||||
case RI_LOCAL_DESIGNATOR_MIN: break;
|
||||
case RI_LOCAL_DESIGNATOR_MAX: break;
|
||||
case RI_LOCAL_STRING_INDEX: break;
|
||||
case RI_LOCAL_STRING_MIN: break;
|
||||
case RI_LOCAL_STRING_MAX: break;
|
||||
case RI_LOCAL_DELIMITER: break;
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
// error
|
||||
// error
|
||||
default: break;
|
||||
}
|
||||
|
||||
desc_report += size;
|
||||
desc_len -= size;
|
||||
desc_len -= size;
|
||||
}
|
||||
|
||||
for ( uint8_t i = 0; i < report_num; i++ )
|
||||
{
|
||||
info = report_info_arr+i;
|
||||
for (uint8_t i = 0; i < report_num; i++) {
|
||||
info = report_info_arr + i;
|
||||
TU_LOG_DRV("%u: id = %u, usage_page = %u, usage = %u\r\n", i, info->report_id, info->usage_page, info->usage);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
#include "hid.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
@@ -47,10 +47,9 @@
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t report_id;
|
||||
uint8_t usage;
|
||||
typedef struct {
|
||||
uint8_t report_id;
|
||||
uint8_t usage;
|
||||
uint16_t usage_page;
|
||||
|
||||
// TODO still use the endpoint size for now
|
||||
@@ -86,7 +85,8 @@ bool tuh_hid_mounted(uint8_t dev_addr, uint8_t idx);
|
||||
|
||||
// Parse report descriptor into array of report_info struct and return number of reports.
|
||||
// For complicated report, application should write its own parser.
|
||||
uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* reports_info_arr, uint8_t arr_count, uint8_t const* desc_report, uint16_t desc_len) TU_ATTR_UNUSED;
|
||||
TU_ATTR_UNUSED uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* reports_info_arr, uint8_t arr_count,
|
||||
uint8_t const* desc_report, uint16_t desc_len);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Control Endpoint API
|
||||
@@ -97,13 +97,22 @@ uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* reports_info_arr,
|
||||
// Application can use set_protocol() to switch back to Report protocol.
|
||||
uint8_t tuh_hid_get_protocol(uint8_t dev_addr, uint8_t idx);
|
||||
|
||||
// Device by default is enumerated in Boot protocol for simplicity. Application
|
||||
// can use this to modify the default protocol for next enumeration.
|
||||
void tuh_hid_set_default_protocol(uint8_t protocol);
|
||||
|
||||
// Set protocol to HID_PROTOCOL_BOOT (0) or HID_PROTOCOL_REPORT (1)
|
||||
// This function is only supported by Boot interface (tuh_n_hid_interface_protocol() != NONE)
|
||||
bool tuh_hid_set_protocol(uint8_t dev_addr, uint8_t idx, uint8_t protocol);
|
||||
|
||||
// Get Report using control endpoint
|
||||
// report_type is either Input, Output or Feature, (value from hid_report_type_t)
|
||||
bool tuh_hid_get_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, void* report, uint16_t len);
|
||||
|
||||
// Set Report using control endpoint
|
||||
// report_type is either Input, Output or Feature, (value from hid_report_type_t)
|
||||
bool tuh_hid_set_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, void* report, uint16_t len);
|
||||
bool tuh_hid_set_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type,
|
||||
void* report, uint16_t len);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Interrupt Endpoint API
|
||||
@@ -117,6 +126,9 @@ bool tuh_hid_receive_ready(uint8_t dev_addr, uint8_t idx);
|
||||
// - false if failed to queue the transfer e.g endpoint is busy
|
||||
bool tuh_hid_receive_report(uint8_t dev_addr, uint8_t idx);
|
||||
|
||||
// Abort receiving report on Interrupt Endpoint
|
||||
bool tuh_hid_receive_abort(uint8_t dev_addr, uint8_t idx);
|
||||
|
||||
// Check if HID interface is ready to send report
|
||||
bool tuh_hid_send_ready(uint8_t dev_addr, uint8_t idx);
|
||||
|
||||
@@ -145,6 +157,10 @@ void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t idx, uint8_t const* re
|
||||
// Invoked when sent report to device successfully via interrupt endpoint
|
||||
TU_ATTR_WEAK void tuh_hid_report_sent_cb(uint8_t dev_addr, uint8_t idx, uint8_t const* report, uint16_t len);
|
||||
|
||||
// Invoked when Get Report to device via either control endpoint
|
||||
// len = 0 indicate there is error in the transfer e.g stalled response
|
||||
TU_ATTR_WEAK void tuh_hid_get_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, uint16_t len);
|
||||
|
||||
// Invoked when Sent Report to device via either control endpoint
|
||||
// len = 0 indicate there is error in the transfer e.g stalled response
|
||||
TU_ATTR_WEAK void tuh_hid_set_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, uint16_t len);
|
||||
@@ -155,11 +171,12 @@ TU_ATTR_WEAK void tuh_hid_set_protocol_complete_cb(uint8_t dev_addr, uint8_t idx
|
||||
//--------------------------------------------------------------------+
|
||||
// Internal Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void hidh_init (void);
|
||||
bool hidh_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len);
|
||||
bool hidh_set_config (uint8_t dev_addr, uint8_t itf_num);
|
||||
bool hidh_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
|
||||
void hidh_close (uint8_t dev_addr);
|
||||
bool hidh_init(void);
|
||||
bool hidh_deinit(void);
|
||||
bool hidh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const* desc_itf, uint16_t max_len);
|
||||
bool hidh_set_config(uint8_t dev_addr, uint8_t itf_num);
|
||||
bool hidh_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
|
||||
void hidh_close(uint8_t dev_addr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -373,12 +373,10 @@ bool tud_midi_n_packet_write (uint8_t itf, uint8_t const packet[4])
|
||||
//--------------------------------------------------------------------+
|
||||
// USBD Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void midid_init(void)
|
||||
{
|
||||
void midid_init(void) {
|
||||
tu_memclr(_midid_itf, sizeof(_midid_itf));
|
||||
|
||||
for(uint8_t i=0; i<CFG_TUD_MIDI; i++)
|
||||
{
|
||||
for (uint8_t i = 0; i < CFG_TUD_MIDI; i++) {
|
||||
midid_interface_t* midi = &_midid_itf[i];
|
||||
|
||||
// config fifo
|
||||
@@ -386,12 +384,38 @@ void midid_init(void)
|
||||
tu_fifo_config(&midi->tx_ff, midi->tx_ff_buf, CFG_TUD_MIDI_TX_BUFSIZE, 1, false); // OBVS.
|
||||
|
||||
#if CFG_FIFO_MUTEX
|
||||
tu_fifo_config_mutex(&midi->rx_ff, NULL, osal_mutex_create(&midi->rx_ff_mutex));
|
||||
tu_fifo_config_mutex(&midi->tx_ff, osal_mutex_create(&midi->tx_ff_mutex), NULL);
|
||||
osal_mutex_t mutex_rd = osal_mutex_create(&midi->rx_ff_mutex);
|
||||
osal_mutex_t mutex_wr = osal_mutex_create(&midi->tx_ff_mutex);
|
||||
TU_ASSERT(mutex_wr != NULL && mutex_wr != NULL, );
|
||||
|
||||
tu_fifo_config_mutex(&midi->rx_ff, NULL, mutex_rd);
|
||||
tu_fifo_config_mutex(&midi->tx_ff, mutex_wr, NULL);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool midid_deinit(void) {
|
||||
#if CFG_FIFO_MUTEX
|
||||
for(uint8_t i=0; i<CFG_TUD_MIDI; i++) {
|
||||
midid_interface_t* midi = &_midid_itf[i];
|
||||
osal_mutex_t mutex_rd = midi->rx_ff.mutex_rd;
|
||||
osal_mutex_t mutex_wr = midi->tx_ff.mutex_wr;
|
||||
|
||||
if (mutex_rd) {
|
||||
osal_mutex_delete(mutex_rd);
|
||||
tu_fifo_config_mutex(&midi->rx_ff, NULL, NULL);
|
||||
}
|
||||
|
||||
if (mutex_wr) {
|
||||
osal_mutex_delete(mutex_wr);
|
||||
tu_fifo_config_mutex(&midi->tx_ff, NULL, NULL);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void midid_reset(uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
|
||||
@@ -158,6 +158,7 @@ static inline bool tud_midi_packet_write (uint8_t const packet[4])
|
||||
// Internal Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void midid_init (void);
|
||||
bool midid_deinit (void);
|
||||
void midid_reset (uint8_t rhport);
|
||||
uint16_t midid_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
|
||||
bool midid_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
|
||||
|
||||
@@ -203,7 +203,7 @@ uint8_t rdwr10_validate_cmd(msc_cbw_t const* cbw)
|
||||
//--------------------------------------------------------------------+
|
||||
// Debug
|
||||
//--------------------------------------------------------------------+
|
||||
#if CFG_TUSB_DEBUG >= 2
|
||||
#if CFG_TUSB_DEBUG >= CFG_TUD_MSC_LOG_LEVEL
|
||||
|
||||
TU_ATTR_UNUSED tu_static tu_lookup_entry_t const _msc_scsi_cmd_lookup[] =
|
||||
{
|
||||
@@ -251,11 +251,15 @@ static inline void set_sense_medium_not_present(uint8_t lun)
|
||||
//--------------------------------------------------------------------+
|
||||
// USBD Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void mscd_init(void)
|
||||
{
|
||||
void mscd_init(void) {
|
||||
tu_memclr(&_mscd_itf, sizeof(mscd_interface_t));
|
||||
}
|
||||
|
||||
bool mscd_deinit(void) {
|
||||
// nothing to do
|
||||
return true;
|
||||
}
|
||||
|
||||
void mscd_reset(uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
|
||||
@@ -150,6 +150,7 @@ TU_ATTR_WEAK bool tud_msc_is_writable_cb(uint8_t lun);
|
||||
// Internal Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void mscd_init (void);
|
||||
bool mscd_deinit (void);
|
||||
void mscd_reset (uint8_t rhport);
|
||||
uint16_t mscd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
|
||||
bool mscd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * p_request);
|
||||
|
||||
@@ -43,16 +43,14 @@
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
//--------------------------------------------------------------------+
|
||||
enum
|
||||
{
|
||||
enum {
|
||||
MSC_STAGE_IDLE = 0,
|
||||
MSC_STAGE_CMD,
|
||||
MSC_STAGE_DATA,
|
||||
MSC_STAGE_STATUS,
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
typedef struct {
|
||||
uint8_t itf_num;
|
||||
uint8_t ep_in;
|
||||
uint8_t ep_out;
|
||||
@@ -69,13 +67,13 @@ typedef struct
|
||||
|
||||
//------------- SCSI -------------//
|
||||
uint8_t stage;
|
||||
void* buffer;
|
||||
void* buffer;
|
||||
tuh_msc_complete_cb_t complete_cb;
|
||||
uintptr_t complete_arg;
|
||||
|
||||
CFG_TUH_MEM_ALIGN msc_cbw_t cbw;
|
||||
CFG_TUH_MEM_ALIGN msc_csw_t csw;
|
||||
}msch_interface_t;
|
||||
} msch_interface_t;
|
||||
|
||||
CFG_TUH_MEM_SECTION static msch_interface_t _msch_itf[CFG_TUH_DEVICE_MAX];
|
||||
|
||||
@@ -86,40 +84,34 @@ static uint8_t _msch_buffer[sizeof(scsi_inquiry_resp_t)];
|
||||
|
||||
// FIXME potential nul reference
|
||||
TU_ATTR_ALWAYS_INLINE
|
||||
static inline msch_interface_t* get_itf(uint8_t dev_addr)
|
||||
{
|
||||
return &_msch_itf[dev_addr-1];
|
||||
static inline msch_interface_t* get_itf(uint8_t dev_addr) {
|
||||
return &_msch_itf[dev_addr - 1];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// PUBLIC API
|
||||
//--------------------------------------------------------------------+
|
||||
uint8_t tuh_msc_get_maxlun(uint8_t dev_addr)
|
||||
{
|
||||
uint8_t tuh_msc_get_maxlun(uint8_t dev_addr) {
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
return p_msc->max_lun;
|
||||
}
|
||||
|
||||
uint32_t tuh_msc_get_block_count(uint8_t dev_addr, uint8_t lun)
|
||||
{
|
||||
uint32_t tuh_msc_get_block_count(uint8_t dev_addr, uint8_t lun) {
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
return p_msc->capacity[lun].block_count;
|
||||
}
|
||||
|
||||
uint32_t tuh_msc_get_block_size(uint8_t dev_addr, uint8_t lun)
|
||||
{
|
||||
uint32_t tuh_msc_get_block_size(uint8_t dev_addr, uint8_t lun) {
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
return p_msc->capacity[lun].block_size;
|
||||
}
|
||||
|
||||
bool tuh_msc_mounted(uint8_t dev_addr)
|
||||
{
|
||||
bool tuh_msc_mounted(uint8_t dev_addr) {
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
return p_msc->mounted;
|
||||
}
|
||||
|
||||
bool tuh_msc_ready(uint8_t dev_addr)
|
||||
{
|
||||
bool tuh_msc_ready(uint8_t dev_addr) {
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
return p_msc->mounted && !usbh_edpt_busy(dev_addr, p_msc->ep_in) && !usbh_edpt_busy(dev_addr, p_msc->ep_out);
|
||||
}
|
||||
@@ -127,20 +119,20 @@ bool tuh_msc_ready(uint8_t dev_addr)
|
||||
//--------------------------------------------------------------------+
|
||||
// PUBLIC API: SCSI COMMAND
|
||||
//--------------------------------------------------------------------+
|
||||
static inline void cbw_init(msc_cbw_t *cbw, uint8_t lun)
|
||||
{
|
||||
static inline void cbw_init(msc_cbw_t* cbw, uint8_t lun) {
|
||||
tu_memclr(cbw, sizeof(msc_cbw_t));
|
||||
cbw->signature = MSC_CBW_SIGNATURE;
|
||||
cbw->tag = 0x54555342; // TUSB
|
||||
cbw->lun = lun;
|
||||
}
|
||||
|
||||
bool tuh_msc_scsi_command(uint8_t dev_addr, msc_cbw_t const* cbw, void* data, tuh_msc_complete_cb_t complete_cb, uintptr_t arg)
|
||||
{
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
bool tuh_msc_scsi_command(uint8_t daddr, msc_cbw_t const* cbw, void* data,
|
||||
tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
|
||||
msch_interface_t* p_msc = get_itf(daddr);
|
||||
TU_VERIFY(p_msc->configured);
|
||||
|
||||
// TODO claim endpoint
|
||||
// claim endpoint
|
||||
TU_VERIFY(usbh_edpt_claim(daddr, p_msc->ep_out));
|
||||
|
||||
p_msc->cbw = *cbw;
|
||||
p_msc->stage = MSC_STAGE_CMD;
|
||||
@@ -148,15 +140,18 @@ bool tuh_msc_scsi_command(uint8_t dev_addr, msc_cbw_t const* cbw, void* data, tu
|
||||
p_msc->complete_cb = complete_cb;
|
||||
p_msc->complete_arg = arg;
|
||||
|
||||
TU_ASSERT(usbh_edpt_xfer(dev_addr, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)));
|
||||
if (!usbh_edpt_xfer(daddr, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t))) {
|
||||
usbh_edpt_release(daddr, p_msc->ep_out);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tuh_msc_read_capacity(uint8_t dev_addr, uint8_t lun, scsi_read_capacity10_resp_t* response, tuh_msc_complete_cb_t complete_cb, uintptr_t arg)
|
||||
{
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
TU_VERIFY(p_msc->configured);
|
||||
bool tuh_msc_read_capacity(uint8_t dev_addr, uint8_t lun, scsi_read_capacity10_resp_t* response,
|
||||
tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
TU_VERIFY(p_msc->configured);
|
||||
|
||||
msc_cbw_t cbw;
|
||||
cbw_init(&cbw, lun);
|
||||
@@ -169,8 +164,8 @@ bool tuh_msc_read_capacity(uint8_t dev_addr, uint8_t lun, scsi_read_capacity10_r
|
||||
return tuh_msc_scsi_command(dev_addr, &cbw, response, complete_cb, arg);
|
||||
}
|
||||
|
||||
bool tuh_msc_inquiry(uint8_t dev_addr, uint8_t lun, scsi_inquiry_resp_t* response, tuh_msc_complete_cb_t complete_cb, uintptr_t arg)
|
||||
{
|
||||
bool tuh_msc_inquiry(uint8_t dev_addr, uint8_t lun, scsi_inquiry_resp_t* response,
|
||||
tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
TU_VERIFY(p_msc->mounted);
|
||||
|
||||
@@ -181,18 +176,16 @@ bool tuh_msc_inquiry(uint8_t dev_addr, uint8_t lun, scsi_inquiry_resp_t* respons
|
||||
cbw.dir = TUSB_DIR_IN_MASK;
|
||||
cbw.cmd_len = sizeof(scsi_inquiry_t);
|
||||
|
||||
scsi_inquiry_t const cmd_inquiry =
|
||||
{
|
||||
.cmd_code = SCSI_CMD_INQUIRY,
|
||||
.alloc_length = sizeof(scsi_inquiry_resp_t)
|
||||
scsi_inquiry_t const cmd_inquiry = {
|
||||
.cmd_code = SCSI_CMD_INQUIRY,
|
||||
.alloc_length = sizeof(scsi_inquiry_resp_t)
|
||||
};
|
||||
memcpy(cbw.command, &cmd_inquiry, cbw.cmd_len);
|
||||
|
||||
return tuh_msc_scsi_command(dev_addr, &cbw, response, complete_cb, arg);
|
||||
}
|
||||
|
||||
bool tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, tuh_msc_complete_cb_t complete_cb, uintptr_t arg)
|
||||
{
|
||||
bool tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
TU_VERIFY(p_msc->configured);
|
||||
|
||||
@@ -200,16 +193,16 @@ bool tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, tuh_msc_complete_cb_
|
||||
cbw_init(&cbw, lun);
|
||||
|
||||
cbw.total_bytes = 0;
|
||||
cbw.dir = TUSB_DIR_OUT;
|
||||
cbw.cmd_len = sizeof(scsi_test_unit_ready_t);
|
||||
cbw.command[0] = SCSI_CMD_TEST_UNIT_READY;
|
||||
cbw.command[1] = lun; // according to wiki TODO need verification
|
||||
cbw.dir = TUSB_DIR_OUT;
|
||||
cbw.cmd_len = sizeof(scsi_test_unit_ready_t);
|
||||
cbw.command[0] = SCSI_CMD_TEST_UNIT_READY;
|
||||
cbw.command[1] = lun; // according to wiki TODO need verification
|
||||
|
||||
return tuh_msc_scsi_command(dev_addr, &cbw, NULL, complete_cb, arg);
|
||||
}
|
||||
|
||||
bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void *response, tuh_msc_complete_cb_t complete_cb, uintptr_t arg)
|
||||
{
|
||||
bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void* response,
|
||||
tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
|
||||
msc_cbw_t cbw;
|
||||
cbw_init(&cbw, lun);
|
||||
|
||||
@@ -217,73 +210,64 @@ bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void *response, tuh_ms
|
||||
cbw.dir = TUSB_DIR_IN_MASK;
|
||||
cbw.cmd_len = sizeof(scsi_request_sense_t);
|
||||
|
||||
scsi_request_sense_t const cmd_request_sense =
|
||||
{
|
||||
.cmd_code = SCSI_CMD_REQUEST_SENSE,
|
||||
.alloc_length = 18
|
||||
scsi_request_sense_t const cmd_request_sense = {
|
||||
.cmd_code = SCSI_CMD_REQUEST_SENSE,
|
||||
.alloc_length = 18
|
||||
};
|
||||
|
||||
memcpy(cbw.command, &cmd_request_sense, cbw.cmd_len);
|
||||
|
||||
return tuh_msc_scsi_command(dev_addr, &cbw, response, complete_cb, arg);
|
||||
}
|
||||
|
||||
bool tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void * buffer, uint32_t lba, uint16_t block_count, tuh_msc_complete_cb_t complete_cb, uintptr_t arg)
|
||||
{
|
||||
bool tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void* buffer, uint32_t lba, uint16_t block_count,
|
||||
tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
TU_VERIFY(p_msc->mounted);
|
||||
|
||||
msc_cbw_t cbw;
|
||||
cbw_init(&cbw, lun);
|
||||
|
||||
cbw.total_bytes = block_count*p_msc->capacity[lun].block_size;
|
||||
cbw.dir = TUSB_DIR_IN_MASK;
|
||||
cbw.cmd_len = sizeof(scsi_read10_t);
|
||||
cbw.total_bytes = block_count * p_msc->capacity[lun].block_size;
|
||||
cbw.dir = TUSB_DIR_IN_MASK;
|
||||
cbw.cmd_len = sizeof(scsi_read10_t);
|
||||
|
||||
scsi_read10_t const cmd_read10 =
|
||||
{
|
||||
.cmd_code = SCSI_CMD_READ_10,
|
||||
.lba = tu_htonl(lba),
|
||||
.block_count = tu_htons(block_count)
|
||||
scsi_read10_t const cmd_read10 = {
|
||||
.cmd_code = SCSI_CMD_READ_10,
|
||||
.lba = tu_htonl(lba),
|
||||
.block_count = tu_htons(block_count)
|
||||
};
|
||||
|
||||
memcpy(cbw.command, &cmd_read10, cbw.cmd_len);
|
||||
|
||||
return tuh_msc_scsi_command(dev_addr, &cbw, buffer, complete_cb, arg);
|
||||
}
|
||||
|
||||
bool tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const * buffer, uint32_t lba, uint16_t block_count, tuh_msc_complete_cb_t complete_cb, uintptr_t arg)
|
||||
{
|
||||
bool tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const* buffer, uint32_t lba, uint16_t block_count,
|
||||
tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
TU_VERIFY(p_msc->mounted);
|
||||
|
||||
msc_cbw_t cbw;
|
||||
cbw_init(&cbw, lun);
|
||||
|
||||
cbw.total_bytes = block_count*p_msc->capacity[lun].block_size;
|
||||
cbw.total_bytes = block_count * p_msc->capacity[lun].block_size;
|
||||
cbw.dir = TUSB_DIR_OUT;
|
||||
cbw.cmd_len = sizeof(scsi_write10_t);
|
||||
|
||||
scsi_write10_t const cmd_write10 =
|
||||
{
|
||||
.cmd_code = SCSI_CMD_WRITE_10,
|
||||
.lba = tu_htonl(lba),
|
||||
.block_count = tu_htons(block_count)
|
||||
scsi_write10_t const cmd_write10 = {
|
||||
.cmd_code = SCSI_CMD_WRITE_10,
|
||||
.lba = tu_htonl(lba),
|
||||
.block_count = tu_htons(block_count)
|
||||
};
|
||||
|
||||
memcpy(cbw.command, &cmd_write10, cbw.cmd_len);
|
||||
|
||||
return tuh_msc_scsi_command(dev_addr, &cbw, (void*)(uintptr_t) buffer, complete_cb, arg);
|
||||
return tuh_msc_scsi_command(dev_addr, &cbw, (void*) (uintptr_t) buffer, complete_cb, arg);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// MSC interface Reset (not used now)
|
||||
bool tuh_msc_reset(uint8_t dev_addr)
|
||||
{
|
||||
tusb_control_request_t const new_request =
|
||||
{
|
||||
.bmRequestType_bit =
|
||||
{
|
||||
bool tuh_msc_reset(uint8_t dev_addr) {
|
||||
tusb_control_request_t const new_request = {
|
||||
.bmRequestType_bit = {
|
||||
.recipient = TUSB_REQ_RCPT_INTERFACE,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_OUT
|
||||
@@ -300,79 +284,77 @@ bool tuh_msc_reset(uint8_t dev_addr)
|
||||
//--------------------------------------------------------------------+
|
||||
// CLASS-USBH API
|
||||
//--------------------------------------------------------------------+
|
||||
void msch_init(void)
|
||||
{
|
||||
bool msch_init(void) {
|
||||
TU_LOG_DRV("sizeof(msch_interface_t) = %u\r\n", sizeof(msch_interface_t));
|
||||
tu_memclr(_msch_itf, sizeof(_msch_itf));
|
||||
return true;
|
||||
}
|
||||
|
||||
void msch_close(uint8_t dev_addr)
|
||||
{
|
||||
TU_VERIFY(dev_addr <= CFG_TUH_DEVICE_MAX, );
|
||||
bool msch_deinit(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void msch_close(uint8_t dev_addr) {
|
||||
TU_VERIFY(dev_addr <= CFG_TUH_DEVICE_MAX,);
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
TU_VERIFY(p_msc->configured, );
|
||||
TU_VERIFY(p_msc->configured,);
|
||||
|
||||
TU_LOG_DRV(" MSCh close addr = %d\r\n", dev_addr);
|
||||
|
||||
// invoke Application Callback
|
||||
if (p_msc->mounted) {
|
||||
if(tuh_msc_umount_cb) tuh_msc_umount_cb(dev_addr);
|
||||
if (tuh_msc_umount_cb) tuh_msc_umount_cb(dev_addr);
|
||||
}
|
||||
|
||||
tu_memclr(p_msc, sizeof(msch_interface_t));
|
||||
}
|
||||
|
||||
bool msch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
|
||||
{
|
||||
bool msch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) {
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
msc_cbw_t const * cbw = &p_msc->cbw;
|
||||
msc_csw_t * csw = &p_msc->csw;
|
||||
|
||||
switch (p_msc->stage)
|
||||
{
|
||||
switch (p_msc->stage) {
|
||||
case MSC_STAGE_CMD:
|
||||
// Must be Command Block
|
||||
TU_ASSERT(ep_addr == p_msc->ep_out && event == XFER_RESULT_SUCCESS && xferred_bytes == sizeof(msc_cbw_t));
|
||||
TU_ASSERT(ep_addr == p_msc->ep_out && event == XFER_RESULT_SUCCESS && xferred_bytes == sizeof(msc_cbw_t));
|
||||
|
||||
if ( cbw->total_bytes && p_msc->buffer )
|
||||
{
|
||||
if (cbw->total_bytes && p_msc->buffer) {
|
||||
// Data stage if any
|
||||
p_msc->stage = MSC_STAGE_DATA;
|
||||
|
||||
uint8_t const ep_data = (cbw->dir & TUSB_DIR_IN_MASK) ? p_msc->ep_in : p_msc->ep_out;
|
||||
TU_ASSERT(usbh_edpt_xfer(dev_addr, ep_data, p_msc->buffer, (uint16_t) cbw->total_bytes));
|
||||
}else
|
||||
{
|
||||
} else {
|
||||
// Status stage
|
||||
p_msc->stage = MSC_STAGE_STATUS;
|
||||
TU_ASSERT(usbh_edpt_xfer(dev_addr, p_msc->ep_in, (uint8_t*) &p_msc->csw, (uint16_t) sizeof(msc_csw_t)));
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
case MSC_STAGE_DATA:
|
||||
// Status stage
|
||||
p_msc->stage = MSC_STAGE_STATUS;
|
||||
TU_ASSERT(usbh_edpt_xfer(dev_addr, p_msc->ep_in, (uint8_t*) &p_msc->csw, (uint16_t) sizeof(msc_csw_t)));
|
||||
break;
|
||||
break;
|
||||
|
||||
case MSC_STAGE_STATUS:
|
||||
// SCSI op is complete
|
||||
p_msc->stage = MSC_STAGE_IDLE;
|
||||
|
||||
if (p_msc->complete_cb)
|
||||
{
|
||||
tuh_msc_complete_data_t const cb_data =
|
||||
{
|
||||
.cbw = cbw,
|
||||
.csw = csw,
|
||||
.scsi_data = p_msc->buffer,
|
||||
.user_arg = p_msc->complete_arg
|
||||
if (p_msc->complete_cb) {
|
||||
tuh_msc_complete_data_t const cb_data = {
|
||||
.cbw = cbw,
|
||||
.csw = csw,
|
||||
.scsi_data = p_msc->buffer,
|
||||
.user_arg = p_msc->complete_arg
|
||||
};
|
||||
p_msc->complete_cb(dev_addr, &cb_data);
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
// unknown state
|
||||
default: break;
|
||||
// unknown state
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -381,39 +363,35 @@ bool msch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32
|
||||
//--------------------------------------------------------------------+
|
||||
// MSC Enumeration
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
static void config_get_maxlun_complete (tuh_xfer_t* xfer);
|
||||
static bool config_test_unit_ready_complete(uint8_t dev_addr, tuh_msc_complete_data_t const * cb_data);
|
||||
static void config_get_maxlun_complete(tuh_xfer_t* xfer);
|
||||
static bool config_test_unit_ready_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data);
|
||||
static bool config_request_sense_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data);
|
||||
static bool config_read_capacity_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data);
|
||||
|
||||
bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len)
|
||||
{
|
||||
bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const* desc_itf, uint16_t max_len) {
|
||||
(void) rhport;
|
||||
TU_VERIFY (MSC_SUBCLASS_SCSI == desc_itf->bInterfaceSubClass &&
|
||||
MSC_PROTOCOL_BOT == desc_itf->bInterfaceProtocol);
|
||||
MSC_PROTOCOL_BOT == desc_itf->bInterfaceProtocol);
|
||||
|
||||
// msc driver length is fixed
|
||||
uint16_t const drv_len = (uint16_t) (sizeof(tusb_desc_interface_t) + desc_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
|
||||
uint16_t const drv_len = (uint16_t) (sizeof(tusb_desc_interface_t) +
|
||||
desc_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
|
||||
TU_ASSERT(drv_len <= max_len);
|
||||
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
tusb_desc_endpoint_t const * ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(desc_itf);
|
||||
tusb_desc_endpoint_t const* ep_desc = (tusb_desc_endpoint_t const*) tu_desc_next(desc_itf);
|
||||
|
||||
for(uint32_t i=0; i<2; i++)
|
||||
{
|
||||
for (uint32_t i = 0; i < 2; i++) {
|
||||
TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType && TUSB_XFER_BULK == ep_desc->bmAttributes.xfer);
|
||||
TU_ASSERT(tuh_edpt_open(dev_addr, ep_desc));
|
||||
|
||||
if ( tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN )
|
||||
{
|
||||
if (TUSB_DIR_IN == tu_edpt_dir(ep_desc->bEndpointAddress)) {
|
||||
p_msc->ep_in = ep_desc->bEndpointAddress;
|
||||
}else
|
||||
{
|
||||
} else {
|
||||
p_msc->ep_out = ep_desc->bEndpointAddress;
|
||||
}
|
||||
|
||||
ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(ep_desc);
|
||||
ep_desc = (tusb_desc_endpoint_t const*) tu_desc_next(ep_desc);
|
||||
}
|
||||
|
||||
p_msc->itf_num = desc_itf->bInterfaceNumber;
|
||||
@@ -430,32 +408,31 @@ bool msch_set_config(uint8_t dev_addr, uint8_t itf_num) {
|
||||
//------------- Get Max Lun -------------//
|
||||
TU_LOG_DRV("MSC Get Max Lun\r\n");
|
||||
tusb_control_request_t const request = {
|
||||
.bmRequestType_bit = {
|
||||
.recipient = TUSB_REQ_RCPT_INTERFACE,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_IN
|
||||
},
|
||||
.bRequest = MSC_REQ_GET_MAX_LUN,
|
||||
.wValue = 0,
|
||||
.wIndex = itf_num,
|
||||
.wLength = 1
|
||||
.bmRequestType_bit = {
|
||||
.recipient = TUSB_REQ_RCPT_INTERFACE,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_IN
|
||||
},
|
||||
.bRequest = MSC_REQ_GET_MAX_LUN,
|
||||
.wValue = 0,
|
||||
.wIndex = itf_num,
|
||||
.wLength = 1
|
||||
};
|
||||
|
||||
tuh_xfer_t xfer = {
|
||||
.daddr = dev_addr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
.buffer = _msch_buffer,
|
||||
.complete_cb = config_get_maxlun_complete,
|
||||
.user_data = 0
|
||||
.daddr = dev_addr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
.buffer = _msch_buffer,
|
||||
.complete_cb = config_get_maxlun_complete,
|
||||
.user_data = 0
|
||||
};
|
||||
TU_ASSERT(tuh_control_xfer(&xfer));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void config_get_maxlun_complete (tuh_xfer_t* xfer)
|
||||
{
|
||||
static void config_get_maxlun_complete(tuh_xfer_t* xfer) {
|
||||
uint8_t const daddr = xfer->daddr;
|
||||
msch_interface_t* p_msc = get_itf(daddr);
|
||||
|
||||
@@ -471,18 +448,16 @@ static void config_get_maxlun_complete (tuh_xfer_t* xfer)
|
||||
tuh_msc_test_unit_ready(daddr, lun, config_test_unit_ready_complete, 0);
|
||||
}
|
||||
|
||||
static bool config_test_unit_ready_complete(uint8_t dev_addr, tuh_msc_complete_data_t const * cb_data)
|
||||
{
|
||||
static bool config_test_unit_ready_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data) {
|
||||
msc_cbw_t const* cbw = cb_data->cbw;
|
||||
msc_csw_t const* csw = cb_data->csw;
|
||||
|
||||
if (csw->status == 0)
|
||||
{
|
||||
if (csw->status == 0) {
|
||||
// Unit is ready, read its capacity
|
||||
TU_LOG_DRV("SCSI Read Capacity\r\n");
|
||||
tuh_msc_read_capacity(dev_addr, cbw->lun, (scsi_read_capacity10_resp_t*) ((void*) _msch_buffer), config_read_capacity_complete, 0);
|
||||
}else
|
||||
{
|
||||
tuh_msc_read_capacity(dev_addr, cbw->lun, (scsi_read_capacity10_resp_t*) ((void*) _msch_buffer),
|
||||
config_read_capacity_complete, 0);
|
||||
} else {
|
||||
// Note: During enumeration, some device fails Test Unit Ready and require a few retries
|
||||
// with Request Sense to start working !!
|
||||
// TODO limit number of retries
|
||||
@@ -493,8 +468,7 @@ static bool config_test_unit_ready_complete(uint8_t dev_addr, tuh_msc_complete_d
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool config_request_sense_complete(uint8_t dev_addr, tuh_msc_complete_data_t const * cb_data)
|
||||
{
|
||||
static bool config_request_sense_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data) {
|
||||
msc_cbw_t const* cbw = cb_data->cbw;
|
||||
msc_csw_t const* csw = cb_data->csw;
|
||||
|
||||
@@ -503,8 +477,7 @@ static bool config_request_sense_complete(uint8_t dev_addr, tuh_msc_complete_dat
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool config_read_capacity_complete(uint8_t dev_addr, tuh_msc_complete_data_t const * cb_data)
|
||||
{
|
||||
static bool config_read_capacity_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data) {
|
||||
msc_cbw_t const* cbw = cb_data->cbw;
|
||||
msc_csw_t const* csw = cb_data->csw;
|
||||
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_MSC_HOST_H_
|
||||
#define _TUSB_MSC_HOST_H_
|
||||
#ifndef TUSB_MSC_HOST_H_
|
||||
#define TUSB_MSC_HOST_H_
|
||||
|
||||
#include "msc.h"
|
||||
|
||||
@@ -73,7 +73,7 @@ uint32_t tuh_msc_get_block_size(uint8_t dev_addr, uint8_t lun);
|
||||
// Perform a full SCSI command (cbw, data, csw) in non-blocking manner.
|
||||
// Complete callback is invoked when SCSI op is complete.
|
||||
// return true if success, false if there is already pending operation.
|
||||
bool tuh_msc_scsi_command(uint8_t dev_addr, msc_cbw_t const* cbw, void* data, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
|
||||
bool tuh_msc_scsi_command(uint8_t daddr, msc_cbw_t const* cbw, void* data, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
|
||||
|
||||
// Perform SCSI Inquiry command
|
||||
// Complete callback is invoked when SCSI op is complete.
|
||||
@@ -113,7 +113,8 @@ TU_ATTR_WEAK void tuh_msc_umount_cb(uint8_t dev_addr);
|
||||
// Internal Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
void msch_init (void);
|
||||
bool msch_init (void);
|
||||
bool msch_deinit (void);
|
||||
bool msch_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len);
|
||||
bool msch_set_config (uint8_t dev_addr, uint8_t itf_num);
|
||||
void msch_close (uint8_t dev_addr);
|
||||
@@ -123,4 +124,4 @@ bool msch_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, ui
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_MSC_HOST_H_ */
|
||||
#endif
|
||||
|
||||
@@ -132,11 +132,14 @@ void netd_report(uint8_t *buf, uint16_t len)
|
||||
//--------------------------------------------------------------------+
|
||||
// USBD Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void netd_init(void)
|
||||
{
|
||||
void netd_init(void) {
|
||||
tu_memclr(&_netd_itf, sizeof(_netd_itf));
|
||||
}
|
||||
|
||||
bool netd_deinit(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void netd_reset(uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
|
||||
@@ -260,6 +260,10 @@ void netd_init(void)
|
||||
ncm_prepare_for_tx();
|
||||
}
|
||||
|
||||
bool netd_deinit(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void netd_reset(uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
|
||||
@@ -105,6 +105,7 @@ void tud_network_link_state_cb(bool state);
|
||||
// INTERNAL USBD-CLASS DRIVER API
|
||||
//--------------------------------------------------------------------+
|
||||
void netd_init (void);
|
||||
bool netd_deinit (void);
|
||||
void netd_reset (uint8_t rhport);
|
||||
uint16_t netd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
|
||||
bool netd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
|
||||
|
||||
@@ -260,6 +260,13 @@ void usbtmcd_init_cb(void)
|
||||
usbtmcLock = osal_mutex_create(&usbtmcLockBuffer);
|
||||
}
|
||||
|
||||
bool usbtmcd_deinit(void) {
|
||||
#if OSAL_MUTEX_REQUIRED
|
||||
osal_mutex_delete(usbtmcLock);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t usbtmcd_open_cb(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
|
||||
{
|
||||
(void)rhport;
|
||||
@@ -353,7 +360,7 @@ uint16_t usbtmcd_open_cb(uint8_t rhport, tusb_desc_interface_t const * itf_desc,
|
||||
// processing a command (such as a clear). Returns true if it was
|
||||
// in the NAK state and successfully transitioned to the ACK wait
|
||||
// state.
|
||||
bool tud_usbtmc_start_bus_read()
|
||||
bool tud_usbtmc_start_bus_read(void)
|
||||
{
|
||||
usbtmcd_state_enum oldState = usbtmc_state.state;
|
||||
switch(oldState)
|
||||
|
||||
@@ -98,11 +98,12 @@ bool tud_usbtmc_start_bus_read(void);
|
||||
|
||||
/* "callbacks" from USB device core */
|
||||
|
||||
void usbtmcd_init_cb(void);
|
||||
bool usbtmcd_deinit(void);
|
||||
uint16_t usbtmcd_open_cb(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
|
||||
void usbtmcd_reset_cb(uint8_t rhport);
|
||||
bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
|
||||
bool usbtmcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
|
||||
void usbtmcd_init_cb(void);
|
||||
|
||||
/************************************************************
|
||||
* USBTMC Descriptor Templates
|
||||
|
||||
46
src/class/vendor/vendor_device.c
vendored
46
src/class/vendor/vendor_device.c
vendored
@@ -49,10 +49,8 @@ typedef struct
|
||||
uint8_t rx_ff_buf[CFG_TUD_VENDOR_RX_BUFSIZE];
|
||||
uint8_t tx_ff_buf[CFG_TUD_VENDOR_TX_BUFSIZE];
|
||||
|
||||
#if CFG_FIFO_MUTEX
|
||||
osal_mutex_def_t rx_ff_mutex;
|
||||
osal_mutex_def_t tx_ff_mutex;
|
||||
#endif
|
||||
OSAL_MUTEX_DEF(rx_ff_mutex);
|
||||
OSAL_MUTEX_DEF(tx_ff_mutex);
|
||||
|
||||
// Endpoint Transfer buffer
|
||||
CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_VENDOR_EPSIZE];
|
||||
@@ -171,25 +169,49 @@ uint32_t tud_vendor_n_write_available (uint8_t itf)
|
||||
//--------------------------------------------------------------------+
|
||||
// USBD Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void vendord_init(void)
|
||||
{
|
||||
void vendord_init(void) {
|
||||
tu_memclr(_vendord_itf, sizeof(_vendord_itf));
|
||||
|
||||
for(uint8_t i=0; i<CFG_TUD_VENDOR; i++)
|
||||
{
|
||||
for(uint8_t i=0; i<CFG_TUD_VENDOR; i++) {
|
||||
vendord_interface_t* p_itf = &_vendord_itf[i];
|
||||
|
||||
// config fifo
|
||||
tu_fifo_config(&p_itf->rx_ff, p_itf->rx_ff_buf, CFG_TUD_VENDOR_RX_BUFSIZE, 1, false);
|
||||
tu_fifo_config(&p_itf->tx_ff, p_itf->tx_ff_buf, CFG_TUD_VENDOR_TX_BUFSIZE, 1, false);
|
||||
|
||||
#if CFG_FIFO_MUTEX
|
||||
tu_fifo_config_mutex(&p_itf->rx_ff, NULL, osal_mutex_create(&p_itf->rx_ff_mutex));
|
||||
tu_fifo_config_mutex(&p_itf->tx_ff, osal_mutex_create(&p_itf->tx_ff_mutex), NULL);
|
||||
#endif
|
||||
#if OSAL_MUTEX_REQUIRED
|
||||
osal_mutex_t mutex_rd = osal_mutex_create(&p_itf->rx_ff_mutex);
|
||||
osal_mutex_t mutex_wr = osal_mutex_create(&p_itf->tx_ff_mutex);
|
||||
TU_ASSERT(mutex_rd && mutex_wr,);
|
||||
|
||||
tu_fifo_config_mutex(&p_itf->rx_ff, NULL, mutex_rd);
|
||||
tu_fifo_config_mutex(&p_itf->tx_ff, mutex_wr, NULL);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool vendord_deinit(void) {
|
||||
#if OSAL_MUTEX_REQUIRED
|
||||
for(uint8_t i=0; i<CFG_TUD_VENDOR; i++) {
|
||||
vendord_interface_t* p_itf = &_vendord_itf[i];
|
||||
osal_mutex_t mutex_rd = p_itf->rx_ff.mutex_rd;
|
||||
osal_mutex_t mutex_wr = p_itf->tx_ff.mutex_wr;
|
||||
|
||||
if (mutex_rd) {
|
||||
osal_mutex_delete(mutex_rd);
|
||||
tu_fifo_config_mutex(&p_itf->rx_ff, NULL, NULL);
|
||||
}
|
||||
|
||||
if (mutex_wr) {
|
||||
osal_mutex_delete(mutex_wr);
|
||||
tu_fifo_config_mutex(&p_itf->tx_ff, NULL, NULL);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void vendord_reset(uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
|
||||
1
src/class/vendor/vendor_device.h
vendored
1
src/class/vendor/vendor_device.h
vendored
@@ -139,6 +139,7 @@ static inline uint32_t tud_vendor_write_available (void)
|
||||
// Internal Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void vendord_init(void);
|
||||
bool vendord_deinit(void);
|
||||
void vendord_reset(uint8_t rhport);
|
||||
uint16_t vendord_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
|
||||
bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
|
||||
|
||||
@@ -29,6 +29,10 @@
|
||||
|
||||
#include "common/tusb_common.h"
|
||||
|
||||
enum {
|
||||
VIDEO_BCD_1_50 = 0x0150,
|
||||
};
|
||||
|
||||
// Table 3-19 Color Matching Descriptor
|
||||
typedef enum {
|
||||
VIDEO_COLOR_PRIMARIES_UNDEFINED = 0x00,
|
||||
@@ -156,22 +160,23 @@ typedef enum {
|
||||
/* A.9.1 VideoControl Interface Control Selectors */
|
||||
typedef enum {
|
||||
VIDEO_VC_CTL_UNDEFINED = 0x00,
|
||||
VIDEO_VC_CTL_VIDEO_POWER_MODE,
|
||||
VIDEO_VC_CTL_REQUEST_ERROR_CODE,
|
||||
VIDEO_VC_CTL_VIDEO_POWER_MODE, // 0x01
|
||||
VIDEO_VC_CTL_REQUEST_ERROR_CODE, // 0x02
|
||||
} video_interface_control_selector_t;
|
||||
|
||||
/* A.9.8 VideoStreaming Interface Control Selectors */
|
||||
typedef enum {
|
||||
VIDEO_VS_CTL_UNDEFINED = 0x00,
|
||||
VIDEO_VS_CTL_PROBE,
|
||||
VIDEO_VS_CTL_COMMIT,
|
||||
VIDEO_VS_CTL_STILL_PROBE,
|
||||
VIDEO_VS_CTL_STILL_COMMIT,
|
||||
VIDEO_VS_CTL_STILL_IMAGE_TRIGGER,
|
||||
VIDEO_VS_CTL_STREAM_ERROR_CODE,
|
||||
VIDEO_VS_CTL_GENERATE_KEY_FRAME,
|
||||
VIDEO_VS_CTL_UPDATE_FRAME_SEGMENT,
|
||||
VIDEO_VS_CTL_SYNCH_DELAY_CONTROL,
|
||||
VIDEO_VS_CTL_PROBE, // 0x01
|
||||
VIDEO_VS_CTL_COMMIT, // 0x02
|
||||
VIDEO_VS_CTL_STILL_PROBE, // 0x03
|
||||
VIDEO_VS_CTL_STILL_COMMIT, // 0x04
|
||||
VIDEO_VS_CTL_STILL_IMAGE_TRIGGER, // 0x05
|
||||
VIDEO_VS_CTL_STREAM_ERROR_CODE, // 0x06
|
||||
VIDEO_VS_CTL_GENERATE_KEY_FRAME, // 0x07
|
||||
VIDEO_VS_CTL_UPDATE_FRAME_SEGMENT, // 0x08
|
||||
VIDEO_VS_CTL_SYNCH_DELAY_CONTROL, // 0x09
|
||||
|
||||
} video_interface_streaming_selector_t;
|
||||
|
||||
/* B. Terminal Types */
|
||||
@@ -198,55 +203,98 @@ typedef enum {
|
||||
} video_terminal_type_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Descriptors
|
||||
// Video Control (VC) Descriptors
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
/* 2.3.4.2 */
|
||||
#define tusb_desc_video_control_header_nitf_t(_nitf) \
|
||||
struct TU_ATTR_PACKED { \
|
||||
uint8_t bLength; \
|
||||
uint8_t bDescriptorType; \
|
||||
uint8_t bDescriptorSubType; \
|
||||
uint16_t bcdUVC; \
|
||||
uint16_t wTotalLength; \
|
||||
uint32_t dwClockFrequency; /* deprecated */ \
|
||||
uint8_t bInCollection; \
|
||||
uint8_t baInterfaceNr[_nitf]; \
|
||||
}
|
||||
|
||||
typedef tusb_desc_video_control_header_nitf_t() tusb_desc_video_control_header_t;
|
||||
typedef tusb_desc_video_control_header_nitf_t(1) tusb_desc_video_control_header_1itf_t;
|
||||
typedef tusb_desc_video_control_header_nitf_t(2) tusb_desc_video_control_header_2itf_t;
|
||||
typedef tusb_desc_video_control_header_nitf_t(3) tusb_desc_video_control_header_3itf_t;
|
||||
typedef tusb_desc_video_control_header_nitf_t(4) tusb_desc_video_control_header_4itf_t;
|
||||
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bDescriptorSubType;
|
||||
uint16_t bcdUVC;
|
||||
uint16_t wTotalLength;
|
||||
uint32_t dwClockFrequency;
|
||||
uint8_t bInCollection;
|
||||
uint8_t baInterfaceNr[];
|
||||
} tusb_desc_cs_video_ctl_itf_hdr_t;
|
||||
uint8_t bTerminalID;
|
||||
uint16_t wTerminalType;
|
||||
uint8_t bAssocTerminal;
|
||||
uint8_t iTerminal;
|
||||
} tusb_desc_video_control_input_terminal_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(tusb_desc_video_control_input_terminal_t) == 8, "size is not correct");
|
||||
|
||||
/* 2.4.3.3 */
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bHeaderLength;
|
||||
union {
|
||||
uint8_t bmHeaderInfo;
|
||||
struct {
|
||||
uint8_t FrameID: 1;
|
||||
uint8_t EndOfFrame: 1;
|
||||
uint8_t PresentationTime: 1;
|
||||
uint8_t SourceClockReference: 1;
|
||||
uint8_t PayloadSpecific: 1;
|
||||
uint8_t StillImage: 1;
|
||||
uint8_t Error: 1;
|
||||
uint8_t EndOfHeader: 1;
|
||||
};
|
||||
};
|
||||
} tusb_video_payload_header_t;
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bDescriptorSubType;
|
||||
uint8_t bTerminalID;
|
||||
uint16_t wTerminalType;
|
||||
uint8_t bAssocTerminal;
|
||||
uint8_t bSourceID;
|
||||
uint8_t iTerminal;
|
||||
} tusb_desc_video_control_output_terminal_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(tusb_desc_video_control_output_terminal_t) == 9, "size is not correct");
|
||||
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bDescriptorSubType;
|
||||
uint8_t bTerminalID;
|
||||
uint16_t wTerminalType;
|
||||
uint8_t bAssocTerminal;
|
||||
uint8_t iTerminal;
|
||||
|
||||
uint16_t wObjectiveFocalLengthMin;
|
||||
uint16_t wObjectiveFocalLengthMax;
|
||||
uint16_t wOcularFocalLength;
|
||||
uint8_t bControlSize;
|
||||
uint8_t bmControls[3];
|
||||
} tusb_desc_video_control_camera_terminal_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(tusb_desc_video_control_camera_terminal_t) == 18, "size is not correct");
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Video Streaming (VS) Descriptors
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
/* 3.9.2.1 */
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bDescriptorSubType;
|
||||
uint8_t bNumFormats;
|
||||
uint16_t wTotalLength;
|
||||
uint8_t bEndpointAddress;
|
||||
uint8_t bmInfo;
|
||||
uint8_t bTerminalLink;
|
||||
uint8_t bStillCaptureMethod;
|
||||
uint8_t bTriggerSupport;
|
||||
uint8_t bTriggerUsage;
|
||||
uint8_t bControlSize;
|
||||
uint8_t bmaControls[];
|
||||
} tusb_desc_cs_video_stm_itf_in_hdr_t;
|
||||
#define tusb_desc_video_streaming_input_header_nbyte_t(_nb) \
|
||||
struct TU_ATTR_PACKED { \
|
||||
uint8_t bLength; \
|
||||
uint8_t bDescriptorType; \
|
||||
uint8_t bDescriptorSubType; \
|
||||
uint8_t bNumFormats; /* Number of video payload Format descriptors for this interface */ \
|
||||
uint16_t wTotalLength; \
|
||||
uint8_t bEndpointAddress; \
|
||||
uint8_t bmInfo; /* Bit 0: dynamic format change supported */ \
|
||||
uint8_t bTerminalLink; \
|
||||
uint8_t bStillCaptureMethod; \
|
||||
uint8_t bTriggerSupport; /* Hardware trigger supported */ \
|
||||
uint8_t bTriggerUsage; \
|
||||
uint8_t bControlSize; /* sizeof of each control item */ \
|
||||
uint8_t bmaControls[_nb]; \
|
||||
}
|
||||
|
||||
typedef tusb_desc_video_streaming_input_header_nbyte_t() tusb_desc_video_streaming_input_header_t;
|
||||
typedef tusb_desc_video_streaming_input_header_nbyte_t(1) tusb_desc_video_streaming_input_header_1byte_t;
|
||||
typedef tusb_desc_video_streaming_input_header_nbyte_t(2) tusb_desc_video_streaming_input_header_2byte_t;
|
||||
typedef tusb_desc_video_streaming_input_header_nbyte_t(3) tusb_desc_video_streaming_input_header_3byte_t;
|
||||
typedef tusb_desc_video_streaming_input_header_nbyte_t(4) tusb_desc_video_streaming_input_header_4byte_t;
|
||||
|
||||
/* 3.9.2.2 */
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
@@ -259,7 +307,7 @@ typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bTerminalLink;
|
||||
uint8_t bControlSize;
|
||||
uint8_t bmaControls[];
|
||||
} tusb_desc_cs_video_stm_itf_out_hdr_t;
|
||||
} tusb_desc_video_streaming_output_header_t;
|
||||
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength;
|
||||
@@ -285,14 +333,33 @@ typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bmaControls[];
|
||||
} output;
|
||||
};
|
||||
} tusb_desc_cs_video_stm_itf_hdr_t;
|
||||
} tusb_desc_video_streaming_inout_header_t;
|
||||
|
||||
// 3.9.2.6 Color Matching Descriptor
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bDescriptorSubType;
|
||||
uint8_t bColorPrimaries;
|
||||
uint8_t bTransferCharacteristics;
|
||||
uint8_t bMatrixCoefficients;
|
||||
} tusb_desc_video_streaming_color_matching_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(tusb_desc_video_streaming_color_matching_t) == 6, "size is not correct");
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Format and Frame Descriptor
|
||||
// Note: bFormatIndex & bFrameIndex are 1-based index
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
//------------- Uncompressed -------------//
|
||||
// Uncompressed payload specs: 3.1.1 format descriptor
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bDescriptorSubType;
|
||||
uint8_t bFormatIndex;
|
||||
uint8_t bNumFrameDescriptors;
|
||||
uint8_t bNumFrameDescriptors; // Number of frame descriptors for this format
|
||||
uint8_t guidFormat[16];
|
||||
uint8_t bBitsPerPixel;
|
||||
uint8_t bDefaultFrameIndex;
|
||||
@@ -300,22 +367,69 @@ typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bAspectRatioY;
|
||||
uint8_t bmInterlaceFlags;
|
||||
uint8_t bCopyProtect;
|
||||
} tusb_desc_cs_video_fmt_uncompressed_t;
|
||||
} tusb_desc_video_format_uncompressed_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(tusb_desc_video_format_uncompressed_t) == 27, "size is not correct");
|
||||
|
||||
// Uncompressed payload specs: 3.1.2 frame descriptor
|
||||
#define tusb_desc_video_frame_uncompressed_nint_t(_nint) \
|
||||
struct TU_ATTR_PACKED { \
|
||||
uint8_t bLength; \
|
||||
uint8_t bDescriptorType; \
|
||||
uint8_t bDescriptorSubType; \
|
||||
uint8_t bFrameIndex; \
|
||||
uint8_t bmCapabilities; \
|
||||
uint16_t wWidth; \
|
||||
uint16_t wHeight; \
|
||||
uint32_t dwMinBitRate; \
|
||||
uint32_t dwMaxBitRate; \
|
||||
uint32_t dwMaxVideoFrameBufferSize; /* deprecated in 1.5 */ \
|
||||
uint32_t dwDefaultFrameInterval; /* 100ns unit */\
|
||||
uint8_t bFrameIntervalType; \
|
||||
uint32_t dwFrameInterval[_nint]; \
|
||||
}
|
||||
|
||||
typedef tusb_desc_video_frame_uncompressed_nint_t() tusb_desc_video_frame_uncompressed_t;
|
||||
typedef tusb_desc_video_frame_uncompressed_nint_t(1) tusb_desc_video_frame_uncompressed_1int_t;
|
||||
typedef tusb_desc_video_frame_uncompressed_nint_t(2) tusb_desc_video_frame_uncompressed_2int_t;
|
||||
typedef tusb_desc_video_frame_uncompressed_nint_t(3) tusb_desc_video_frame_uncompressed_3int_t;
|
||||
typedef tusb_desc_video_frame_uncompressed_nint_t(4) tusb_desc_video_frame_uncompressed_4int_t;
|
||||
|
||||
// continuous = 3 intervals: min, max, step
|
||||
typedef tusb_desc_video_frame_uncompressed_3int_t tusb_desc_video_frame_uncompressed_continuous_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(tusb_desc_video_frame_uncompressed_continuous_t) == 38, "size is not correct");
|
||||
|
||||
//------------- MJPEG -------------//
|
||||
// MJPEG payload specs: 3.1.1 format descriptor
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bDescriptorSubType;
|
||||
uint8_t bFormatIndex;
|
||||
uint8_t bNumFrameDescriptors;
|
||||
uint8_t bmFlags;
|
||||
uint8_t bmFlags; // Bit 0: fixed size samples (1 = yes)
|
||||
uint8_t bDefaultFrameIndex;
|
||||
uint8_t bAspectRatioX;
|
||||
uint8_t bAspectRatioY;
|
||||
uint8_t bmInterlaceFlags;
|
||||
uint8_t bCopyProtect;
|
||||
} tusb_desc_cs_video_fmt_mjpeg_t;
|
||||
} tusb_desc_video_format_mjpeg_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(tusb_desc_video_format_mjpeg_t) == 11, "size is not correct");
|
||||
|
||||
// MJPEG payload specs: 3.1.2 frame descriptor (same as uncompressed)
|
||||
typedef tusb_desc_video_frame_uncompressed_t tusb_desc_video_frame_mjpeg_t;
|
||||
typedef tusb_desc_video_frame_uncompressed_1int_t tusb_desc_video_frame_mjpeg_1int_t;
|
||||
typedef tusb_desc_video_frame_uncompressed_2int_t tusb_desc_video_frame_mjpeg_2int_t;
|
||||
typedef tusb_desc_video_frame_uncompressed_3int_t tusb_desc_video_frame_mjpeg_3int_t;
|
||||
typedef tusb_desc_video_frame_uncompressed_4int_t tusb_desc_video_frame_mjpeg_4int_t;
|
||||
|
||||
// continuous = 3 intervals: min, max, step
|
||||
typedef tusb_desc_video_frame_mjpeg_3int_t tusb_desc_video_frame_mjpeg_continuous_t;
|
||||
|
||||
//------------- DV -------------//
|
||||
// DV payload specs: 3.1.1
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
@@ -323,8 +437,9 @@ typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bFormatIndex;
|
||||
uint32_t dwMaxVideoFrameBufferSize; /* deprecated */
|
||||
uint8_t bFormatType;
|
||||
} tusb_desc_cs_video_fmt_dv_t;
|
||||
} tusb_desc_video_format_dv_t;
|
||||
|
||||
// Frame Based payload specs: 3.1.1
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
@@ -339,25 +454,7 @@ typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bmInterlaceFlags;
|
||||
uint8_t bCopyProtect;
|
||||
uint8_t bVaribaleSize;
|
||||
} tusb_desc_cs_video_fmt_frame_based_t;
|
||||
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bDescriptorSubType;
|
||||
uint8_t bFrameIndex;
|
||||
uint8_t bmCapabilities;
|
||||
uint16_t wWidth;
|
||||
uint16_t wHeight;
|
||||
uint32_t dwMinBitRate;
|
||||
uint32_t dwMaxBitRate;
|
||||
uint32_t dwMaxVideoFrameBufferSize; /* deprecated */
|
||||
uint32_t dwDefaultFrameInterval;
|
||||
uint8_t bFrameIntervalType;
|
||||
uint32_t dwFrameInterval[];
|
||||
} tusb_desc_cs_video_frm_uncompressed_t;
|
||||
|
||||
typedef tusb_desc_cs_video_frm_uncompressed_t tusb_desc_cs_video_frm_mjpeg_t;
|
||||
} tusb_desc_video_format_framebased_t;
|
||||
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength;
|
||||
@@ -373,12 +470,30 @@ typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bFrameIntervalType;
|
||||
uint32_t dwBytesPerLine;
|
||||
uint32_t dwFrameInterval[];
|
||||
} tusb_desc_cs_video_frm_frame_based_t;
|
||||
} tusb_desc_video_frame_framebased_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Requests
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
/* 2.4.3.3 */
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bHeaderLength;
|
||||
union {
|
||||
uint8_t bmHeaderInfo;
|
||||
struct {
|
||||
uint8_t FrameID: 1;
|
||||
uint8_t EndOfFrame: 1;
|
||||
uint8_t PresentationTime: 1;
|
||||
uint8_t SourceClockReference: 1;
|
||||
uint8_t PayloadSpecific: 1;
|
||||
uint8_t StillImage: 1;
|
||||
uint8_t Error: 1;
|
||||
uint8_t EndOfHeader: 1;
|
||||
};
|
||||
};
|
||||
} tusb_video_payload_header_t;
|
||||
|
||||
/* 4.3.1.1 */
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
union {
|
||||
@@ -537,7 +652,7 @@ TU_VERIFY_STATIC( sizeof(video_probe_and_commit_control_t) == 48, "size is not c
|
||||
/* Motion-JPEG 3.1.1 Table 3-2 and 3-4 */
|
||||
#define TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_DISC(_frmidx, _cap, _width, _height, _minbr, _maxbr, _maxfrmbufsz, _frminterval, ...) \
|
||||
TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_DISC_LEN + (TU_ARGS_NUM(__VA_ARGS__)) * 4, \
|
||||
TUSB_DESC_CS_INTERFACE, VIDEO_CS_VS_INTERFACE_FRAME_MJPEG, \
|
||||
TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VS_FRAME_MJPEG, \
|
||||
_frmidx, _cap, U16_TO_U8S_LE(_width), U16_TO_U8S_LE(_height), U32_TO_U8S_LE(_minbr), U32_TO_U8S_LE(_maxbr), \
|
||||
U32_TO_U8S_LE(_maxfrmbufsz), U32_TO_U8S_LE(_frminterval), (TU_ARGS_NUM(__VA_ARGS__)), __VA_ARGS__
|
||||
|
||||
|
||||
@@ -50,17 +50,17 @@
|
||||
|
||||
typedef struct {
|
||||
tusb_desc_interface_t std;
|
||||
tusb_desc_cs_video_ctl_itf_hdr_t ctl;
|
||||
tusb_desc_video_control_header_t ctl;
|
||||
} tusb_desc_vc_itf_t;
|
||||
|
||||
typedef struct {
|
||||
tusb_desc_interface_t std;
|
||||
tusb_desc_cs_video_stm_itf_hdr_t stm;
|
||||
tusb_desc_video_streaming_inout_header_t stm;
|
||||
} tusb_desc_vs_itf_t;
|
||||
|
||||
typedef union {
|
||||
tusb_desc_cs_video_ctl_itf_hdr_t ctl;
|
||||
tusb_desc_cs_video_stm_itf_hdr_t stm;
|
||||
tusb_desc_video_control_header_t ctl;
|
||||
tusb_desc_video_streaming_inout_header_t stm;
|
||||
} tusb_desc_video_itf_hdr_t;
|
||||
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
@@ -78,9 +78,9 @@ typedef union {
|
||||
uint8_t bFormatIndex;
|
||||
uint8_t bNumFrameDescriptors;
|
||||
};
|
||||
tusb_desc_cs_video_fmt_uncompressed_t uncompressed;
|
||||
tusb_desc_cs_video_fmt_mjpeg_t mjpeg;
|
||||
tusb_desc_cs_video_fmt_frame_based_t frame_based;
|
||||
tusb_desc_video_format_uncompressed_t uncompressed;
|
||||
tusb_desc_video_format_mjpeg_t mjpeg;
|
||||
tusb_desc_video_format_framebased_t frame_based;
|
||||
} tusb_desc_cs_video_fmt_t;
|
||||
|
||||
typedef union {
|
||||
@@ -93,9 +93,9 @@ typedef union {
|
||||
uint16_t wWidth;
|
||||
uint16_t wHeight;
|
||||
};
|
||||
tusb_desc_cs_video_frm_uncompressed_t uncompressed;
|
||||
tusb_desc_cs_video_frm_mjpeg_t mjpeg;
|
||||
tusb_desc_cs_video_frm_frame_based_t frame_based;
|
||||
tusb_desc_video_frame_uncompressed_t uncompressed;
|
||||
tusb_desc_video_frame_mjpeg_t mjpeg;
|
||||
tusb_desc_video_frame_framebased_t frame_based;
|
||||
} tusb_desc_cs_video_frm_t;
|
||||
|
||||
/* video streaming interface */
|
||||
@@ -114,6 +114,8 @@ typedef struct TU_ATTR_PACKED {
|
||||
uint32_t max_payload_transfer_size;
|
||||
uint8_t error_code;/* error code */
|
||||
uint8_t state; /* 0:probing 1:committed 2:streaming */
|
||||
|
||||
video_probe_and_commit_control_t probe_commit_payload; /* Probe and Commit control */
|
||||
/*------------- From this point, data is not cleared by bus reset -------------*/
|
||||
CFG_TUSB_MEM_ALIGN uint8_t ep_buf[CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE]; /* EP transfer buffer for streaming */
|
||||
} videod_streaming_interface_t;
|
||||
@@ -143,13 +145,65 @@ CFG_TUD_MEM_SECTION tu_static videod_streaming_interface_t _videod_streaming_itf
|
||||
tu_static uint8_t const _cap_get = 0x1u; /* support for GET */
|
||||
tu_static uint8_t const _cap_get_set = 0x3u; /* support for GET and SET */
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Debug
|
||||
//--------------------------------------------------------------------+
|
||||
#if CFG_TUSB_DEBUG >= CFG_TUD_VIDEO_LOG_LEVEL
|
||||
|
||||
static tu_lookup_entry_t const tu_lookup_video_request[] = {
|
||||
{.key = VIDEO_REQUEST_UNDEFINED, .data = "Undefined"},
|
||||
{.key = VIDEO_REQUEST_SET_CUR, .data = "SetCur"},
|
||||
{.key = VIDEO_REQUEST_SET_CUR_ALL, .data = "SetCurAll"},
|
||||
{.key = VIDEO_REQUEST_GET_CUR, .data = "GetCur"},
|
||||
{.key = VIDEO_REQUEST_GET_MIN, .data = "GetMin"},
|
||||
{.key = VIDEO_REQUEST_GET_MAX, .data = "GetMax"},
|
||||
{.key = VIDEO_REQUEST_GET_RES, .data = "GetRes"},
|
||||
{.key = VIDEO_REQUEST_GET_LEN, .data = "GetLen"},
|
||||
{.key = VIDEO_REQUEST_GET_INFO, .data = "GetInfo"},
|
||||
{.key = VIDEO_REQUEST_GET_DEF, .data = "GetDef"},
|
||||
{.key = VIDEO_REQUEST_GET_CUR_ALL, .data = "GetCurAll"},
|
||||
{.key = VIDEO_REQUEST_GET_MIN_ALL, .data = "GetMinAll"},
|
||||
{.key = VIDEO_REQUEST_GET_MAX_ALL, .data = "GetMaxAll"},
|
||||
{.key = VIDEO_REQUEST_GET_RES_ALL, .data = "GetResAll"},
|
||||
{.key = VIDEO_REQUEST_GET_DEF_ALL, .data = "GetDefAll"},
|
||||
};
|
||||
|
||||
static tu_lookup_table_t const tu_table_video_request = {
|
||||
.count = TU_ARRAY_SIZE(tu_lookup_video_request),
|
||||
.items = tu_lookup_video_request
|
||||
};
|
||||
|
||||
static char const* const tu_str_video_vc_control_selector[] = {
|
||||
"Undefined",
|
||||
"Video Power Mode",
|
||||
"Request Error Code",
|
||||
};
|
||||
|
||||
static char const* const tu_str_video_vs_control_selector[] = {
|
||||
"Undefined",
|
||||
"Probe",
|
||||
"Commit",
|
||||
"Still Probe",
|
||||
"Still Commit",
|
||||
"Still Image Trigger",
|
||||
"Stream Error Code",
|
||||
"Generate Key Frame",
|
||||
"Update Frame Segment",
|
||||
"Sync Delay",
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
//
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
/** Get interface number from the interface descriptor
|
||||
*
|
||||
* @param[in] desc interface descriptor
|
||||
*
|
||||
* @return bInterfaceNumber */
|
||||
static inline uint8_t _desc_itfnum(void const *desc)
|
||||
{
|
||||
static inline uint8_t _desc_itfnum(void const *desc) {
|
||||
return ((uint8_t const*)desc)[2];
|
||||
}
|
||||
|
||||
@@ -158,8 +212,7 @@ static inline uint8_t _desc_itfnum(void const *desc)
|
||||
* @param[in] desc endpoint descriptor
|
||||
*
|
||||
* @return bEndpointAddress */
|
||||
static inline uint8_t _desc_ep_addr(void const *desc)
|
||||
{
|
||||
static inline uint8_t _desc_ep_addr(void const *desc) {
|
||||
return ((uint8_t const*)desc)[2];
|
||||
}
|
||||
|
||||
@@ -169,8 +222,7 @@ static inline uint8_t _desc_ep_addr(void const *desc)
|
||||
* @param[in] stm_idx index number of streaming interface
|
||||
*
|
||||
* @return instance */
|
||||
static videod_streaming_interface_t* _get_instance_streaming(uint_fast8_t ctl_idx, uint_fast8_t stm_idx)
|
||||
{
|
||||
static videod_streaming_interface_t* _get_instance_streaming(uint_fast8_t ctl_idx, uint_fast8_t stm_idx) {
|
||||
videod_interface_t *ctl = &_videod_itf[ctl_idx];
|
||||
if (!ctl->beg) return NULL;
|
||||
videod_streaming_interface_t *stm = &_videod_streaming_itf[ctl->stm[stm_idx]];
|
||||
@@ -178,13 +230,11 @@ static videod_streaming_interface_t* _get_instance_streaming(uint_fast8_t ctl_id
|
||||
return stm;
|
||||
}
|
||||
|
||||
static tusb_desc_vc_itf_t const* _get_desc_vc(videod_interface_t const *self)
|
||||
{
|
||||
static tusb_desc_vc_itf_t const* _get_desc_vc(videod_interface_t const *self) {
|
||||
return (tusb_desc_vc_itf_t const *)(self->beg + self->cur);
|
||||
}
|
||||
|
||||
static tusb_desc_vs_itf_t const* _get_desc_vs(videod_streaming_interface_t const *self)
|
||||
{
|
||||
static tusb_desc_vs_itf_t const* _get_desc_vs(videod_streaming_interface_t const *self) {
|
||||
if (!self->desc.cur) return NULL;
|
||||
uint8_t const *desc = _videod_itf[self->index_vc].beg;
|
||||
return (tusb_desc_vs_itf_t const*)(desc + self->desc.cur);
|
||||
@@ -198,8 +248,7 @@ static tusb_desc_vs_itf_t const* _get_desc_vs(videod_streaming_interface_t const
|
||||
*
|
||||
* @return The pointer for interface descriptor.
|
||||
* @retval end did not found interface descriptor */
|
||||
static void const* _find_desc(void const *beg, void const *end, uint_fast8_t desc_type)
|
||||
{
|
||||
static void const* _find_desc(void const *beg, void const *end, uint_fast8_t desc_type) {
|
||||
void const *cur = beg;
|
||||
while ((cur < end) && (desc_type != tu_desc_type(cur))) {
|
||||
cur = tu_desc_next(cur);
|
||||
@@ -207,6 +256,24 @@ static void const* _find_desc(void const *beg, void const *end, uint_fast8_t des
|
||||
return cur;
|
||||
}
|
||||
|
||||
/** Find the first descriptor of two given types
|
||||
*
|
||||
* @param[in] beg The head of descriptor byte array.
|
||||
* @param[in] end The tail of descriptor byte array.
|
||||
* @param[in] desc_type_0 The first target descriptor type.
|
||||
* @param[in] desc_type_1 The second target descriptor type.
|
||||
*
|
||||
* @return The pointer for interface descriptor.
|
||||
* @retval end did not found interface descriptor */
|
||||
static void const* _find_desc_2_type(void const *beg, void const *end, uint_fast8_t desc_type_0, uint_fast8_t desc_type_1)
|
||||
{
|
||||
void const *cur = beg;
|
||||
while ((cur < end) && (desc_type_0 != tu_desc_type(cur)) && (desc_type_1 != tu_desc_type(cur))) {
|
||||
cur = tu_desc_next(cur);
|
||||
}
|
||||
return cur;
|
||||
}
|
||||
|
||||
/** Find the first descriptor specified by the arguments
|
||||
*
|
||||
* @param[in] beg The head of descriptor byte array.
|
||||
@@ -220,8 +287,7 @@ static void const* _find_desc(void const *beg, void const *end, uint_fast8_t des
|
||||
static void const* _find_desc_3(void const *beg, void const *end,
|
||||
uint_fast8_t desc_type,
|
||||
uint_fast8_t element_0,
|
||||
uint_fast8_t element_1)
|
||||
{
|
||||
uint_fast8_t element_1) {
|
||||
for (void const *cur = beg; cur < end; cur = _find_desc(cur, end, desc_type)) {
|
||||
uint8_t const *p = (uint8_t const *)cur;
|
||||
if ((p[2] == element_0) && (p[3] == element_1)) {
|
||||
@@ -233,19 +299,22 @@ static void const* _find_desc_3(void const *beg, void const *end,
|
||||
}
|
||||
|
||||
/** Return the next interface descriptor which has another interface number.
|
||||
* If there are multiple VC interfaces, there will be an IAD descriptor before
|
||||
* the next interface descriptor. Check both the IAD descriptor and the interface
|
||||
* descriptor.
|
||||
* 3.1 Descriptor Layout Overview
|
||||
*
|
||||
* @param[in] beg The head of descriptor byte array.
|
||||
* @param[in] end The tail of descriptor byte array.
|
||||
*
|
||||
* @return The pointer for interface descriptor.
|
||||
* @retval end did not found interface descriptor */
|
||||
static void const* _next_desc_itf(void const *beg, void const *end)
|
||||
{
|
||||
static void const* _next_desc_itf(void const *beg, void const *end) {
|
||||
void const *cur = beg;
|
||||
uint_fast8_t itfnum = ((tusb_desc_interface_t const*)cur)->bInterfaceNumber;
|
||||
while ((cur < end) &&
|
||||
(itfnum == ((tusb_desc_interface_t const*)cur)->bInterfaceNumber)) {
|
||||
cur = _find_desc(tu_desc_next(cur), end, TUSB_DESC_INTERFACE);
|
||||
cur = _find_desc_2_type(tu_desc_next(cur), end, TUSB_DESC_INTERFACE, TUSB_DESC_INTERFACE_ASSOCIATION);
|
||||
}
|
||||
return cur;
|
||||
}
|
||||
@@ -391,8 +460,10 @@ static bool _update_streaming_parameters(videod_streaming_interface_t const *stm
|
||||
case VIDEO_CS_ITF_VS_FORMAT_UNCOMPRESSED:
|
||||
param->wCompQuality = 1; /* 1 to 10000 */
|
||||
break;
|
||||
case VIDEO_CS_ITF_VS_FORMAT_MJPEG:
|
||||
|
||||
case VIDEO_CS_ITF_VS_FORMAT_MJPEG:
|
||||
break;
|
||||
|
||||
default: return false;
|
||||
}
|
||||
|
||||
@@ -413,9 +484,11 @@ static bool _update_streaming_parameters(videod_streaming_interface_t const *stm
|
||||
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;
|
||||
@@ -434,8 +507,9 @@ static bool _update_streaming_parameters(videod_streaming_interface_t const *stm
|
||||
uint_fast32_t interval_ms = interval / 10000;
|
||||
TU_ASSERT(interval_ms);
|
||||
uint_fast32_t payload_size = (frame_size + interval_ms - 1) / interval_ms + 2;
|
||||
if (CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE < payload_size)
|
||||
if (CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE < payload_size) {
|
||||
payload_size = CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE;
|
||||
}
|
||||
param->dwMaxPayloadTransferSize = payload_size;
|
||||
return true;
|
||||
}
|
||||
@@ -455,10 +529,12 @@ static bool _negotiate_streaming_parameters(videod_streaming_interface_t const *
|
||||
if (_get_desc_vs(stm))
|
||||
param->bFormatIndex = _get_desc_vs(stm)->stm.bNumFormats;
|
||||
break;
|
||||
|
||||
case VIDEO_REQUEST_GET_MIN:
|
||||
case VIDEO_REQUEST_GET_DEF:
|
||||
param->bFormatIndex = 1;
|
||||
break;
|
||||
|
||||
default: return false;
|
||||
}
|
||||
/* Set the parameters determined by the format */
|
||||
@@ -487,18 +563,22 @@ static bool _negotiate_streaming_parameters(videod_streaming_interface_t const *
|
||||
case VIDEO_REQUEST_GET_MAX:
|
||||
frmnum = fmt->bNumFrameDescriptors;
|
||||
break;
|
||||
|
||||
case VIDEO_REQUEST_GET_MIN:
|
||||
frmnum = 1;
|
||||
break;
|
||||
|
||||
case VIDEO_REQUEST_GET_DEF:
|
||||
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;
|
||||
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;
|
||||
@@ -511,9 +591,11 @@ static bool _negotiate_streaming_parameters(videod_streaming_interface_t const *
|
||||
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;
|
||||
@@ -529,41 +611,43 @@ static bool _negotiate_streaming_parameters(videod_streaming_interface_t const *
|
||||
|
||||
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->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;
|
||||
}
|
||||
case VIDEO_REQUEST_GET_MAX: {
|
||||
uint_fast32_t min_interval, max_interval;
|
||||
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;
|
||||
break;
|
||||
case VIDEO_REQUEST_GET_MIN:
|
||||
{
|
||||
uint_fast32_t min_interval, max_interval;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
case VIDEO_REQUEST_GET_MIN: {
|
||||
uint_fast32_t min_interval, max_interval;
|
||||
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->uncompressed.dwDefaultFrameInterval;
|
||||
interval_ms = interval / 10000;
|
||||
break;
|
||||
case VIDEO_REQUEST_GET_RES:
|
||||
{
|
||||
uint_fast8_t num_intervals = frm->uncompressed.bFrameIntervalType;
|
||||
if (num_intervals) {
|
||||
interval = 0;
|
||||
} else {
|
||||
interval = frm->uncompressed.dwFrameInterval[2];
|
||||
interval_ms = interval / 10000;
|
||||
}
|
||||
|
||||
case VIDEO_REQUEST_GET_RES: {
|
||||
uint_fast8_t num_intervals = frm->uncompressed.bFrameIntervalType;
|
||||
if (num_intervals) {
|
||||
interval = 0;
|
||||
interval_ms = 0;
|
||||
} else {
|
||||
interval = frm->uncompressed.dwFrameInterval[2];
|
||||
interval_ms = interval / 10000;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: return false;
|
||||
}
|
||||
param->dwFrameInterval = interval;
|
||||
@@ -577,8 +661,9 @@ static bool _negotiate_streaming_parameters(videod_streaming_interface_t const *
|
||||
} else {
|
||||
payload_size = (frame_size + interval_ms - 1) / interval_ms + 2;
|
||||
}
|
||||
if (CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE < payload_size)
|
||||
if (CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE < payload_size) {
|
||||
payload_size = CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE;
|
||||
}
|
||||
param->dwMaxPayloadTransferSize = payload_size;
|
||||
}
|
||||
return true;
|
||||
@@ -651,13 +736,11 @@ static bool _open_vc_itf(uint8_t rhport, videod_interface_t *self, uint_fast8_t
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _init_vs_configuration(videod_streaming_interface_t *stm)
|
||||
{
|
||||
static bool _init_vs_configuration(videod_streaming_interface_t *stm) {
|
||||
/* initialize streaming settings */
|
||||
stm->state = VS_STATE_PROBING;
|
||||
stm->max_payload_transfer_size = 0;
|
||||
video_probe_and_commit_control_t *param =
|
||||
(video_probe_and_commit_control_t *)&stm->ep_buf;
|
||||
video_probe_and_commit_control_t *param = &stm->probe_commit_payload;
|
||||
tu_memclr(param, sizeof(*param));
|
||||
return _update_streaming_parameters(stm, param);
|
||||
}
|
||||
@@ -672,15 +755,20 @@ static bool _open_vs_itf(uint8_t rhport, videod_streaming_interface_t *stm, uint
|
||||
TU_LOG_DRV(" reopen VS %d\r\n", altnum);
|
||||
uint8_t const *desc = _videod_itf[stm->index_vc].beg;
|
||||
|
||||
#ifndef TUP_DCD_EDPT_ISO_ALLOC
|
||||
/* Close endpoints of previous settings. */
|
||||
for (i = 0; i < TU_ARRAY_SIZE(stm->desc.ep); ++i) {
|
||||
uint_fast16_t ofs_ep = stm->desc.ep[i];
|
||||
if (!ofs_ep) break;
|
||||
uint8_t ep_adr = _desc_ep_addr(desc + ofs_ep);
|
||||
usbd_edpt_close(rhport, ep_adr);
|
||||
stm->desc.ep[i] = 0;
|
||||
TU_LOG_DRV(" close EP%02x\r\n", ep_adr);
|
||||
tusb_desc_endpoint_t const *ep = (tusb_desc_endpoint_t const*)(desc + ofs_ep);
|
||||
/* Only ISO endpoints needs to be closed */
|
||||
if(ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) {
|
||||
stm->desc.ep[i] = 0;
|
||||
usbd_edpt_close(rhport, ep->bEndpointAddress);
|
||||
TU_LOG_DRV(" close EP%02x\r\n", ep->bEndpointAddress);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* clear transfer management information */
|
||||
stm->buffer = NULL;
|
||||
@@ -705,16 +793,18 @@ static bool _open_vs_itf(uint8_t rhport, videod_streaming_interface_t *stm, uint
|
||||
TU_ASSERT(cur < end);
|
||||
tusb_desc_endpoint_t const *ep = (tusb_desc_endpoint_t const*)cur;
|
||||
uint_fast32_t max_size = stm->max_payload_transfer_size;
|
||||
if (altnum) {
|
||||
if ((TUSB_XFER_ISOCHRONOUS == ep->bmAttributes.xfer) &&
|
||||
(tu_edpt_packet_size(ep) < max_size)) {
|
||||
/* FS must be less than or equal to max packet size */
|
||||
return false;
|
||||
}
|
||||
if (altnum && (TUSB_XFER_ISOCHRONOUS == ep->bmAttributes.xfer)) {
|
||||
/* FS must be less than or equal to max packet size */
|
||||
TU_VERIFY (tu_edpt_packet_size(ep) >= max_size);
|
||||
#ifdef TUP_DCD_EDPT_ISO_ALLOC
|
||||
usbd_edpt_iso_activate(rhport, ep);
|
||||
#else
|
||||
TU_ASSERT(usbd_edpt_open(rhport, ep));
|
||||
#endif
|
||||
} else {
|
||||
TU_VERIFY(TUSB_XFER_BULK == ep->bmAttributes.xfer);
|
||||
TU_ASSERT(usbd_edpt_open(rhport, ep));
|
||||
}
|
||||
TU_ASSERT(usbd_edpt_open(rhport, ep));
|
||||
stm->desc.ep[i] = (uint16_t) (cur - desc);
|
||||
TU_LOG_DRV(" open EP%02x\r\n", _desc_ep_addr(cur));
|
||||
}
|
||||
@@ -734,6 +824,7 @@ static uint_fast16_t _prepare_in_payload(videod_streaming_interface_t *stm)
|
||||
if (hdr_len + remaining < pkt_len) {
|
||||
pkt_len = hdr_len + remaining;
|
||||
}
|
||||
TU_ASSERT(pkt_len >= hdr_len);
|
||||
uint_fast16_t data_len = pkt_len - hdr_len;
|
||||
memcpy(&stm->ep_buf[hdr_len], stm->buffer + stm->offset, data_len);
|
||||
stm->offset += data_len;
|
||||
@@ -750,6 +841,7 @@ static int handle_video_ctl_std_req(uint8_t rhport, uint8_t stage,
|
||||
tusb_control_request_t const *request,
|
||||
uint_fast8_t ctl_idx)
|
||||
{
|
||||
TU_LOG_DRV("\r\n");
|
||||
switch (request->bRequest) {
|
||||
case TUSB_REQ_GET_INTERFACE:
|
||||
if (stage == CONTROL_STAGE_SETUP)
|
||||
@@ -787,7 +879,10 @@ static int handle_video_ctl_cs_req(uint8_t rhport, uint8_t stage,
|
||||
videod_interface_t *self = &_videod_itf[ctl_idx];
|
||||
|
||||
/* 4.2.1 Interface Control Request */
|
||||
switch (TU_U16_HIGH(request->wValue)) {
|
||||
uint8_t const ctrl_sel = TU_U16_HIGH(request->wValue);
|
||||
TU_LOG_DRV("%s_Control(%s)\r\n", tu_str_video_vc_control_selector[ctrl_sel], tu_lookup_find(&tu_table_video_request, request->bRequest));
|
||||
|
||||
switch (ctrl_sel) {
|
||||
case VIDEO_VC_CTL_VIDEO_POWER_MODE:
|
||||
switch (request->bRequest) {
|
||||
case VIDEO_REQUEST_SET_CUR:
|
||||
@@ -851,19 +946,19 @@ static int handle_video_ctl_req(uint8_t rhport, uint8_t stage,
|
||||
tusb_control_request_t const *request,
|
||||
uint_fast8_t ctl_idx)
|
||||
{
|
||||
uint_fast8_t entity_id;
|
||||
switch (request->bmRequestType_bit.type) {
|
||||
case TUSB_REQ_TYPE_STANDARD:
|
||||
return handle_video_ctl_std_req(rhport, stage, request, ctl_idx);
|
||||
|
||||
case TUSB_REQ_TYPE_CLASS:
|
||||
entity_id = TU_U16_HIGH(request->wIndex);
|
||||
case TUSB_REQ_TYPE_CLASS: {
|
||||
uint_fast8_t entity_id = TU_U16_HIGH(request->wIndex);
|
||||
if (!entity_id) {
|
||||
return handle_video_ctl_cs_req(rhport, stage, request, ctl_idx);
|
||||
} else {
|
||||
TU_VERIFY(_find_desc_entity(_get_desc_vc(&_videod_itf[ctl_idx]), entity_id), VIDEO_ERROR_INVALID_REQUEST);
|
||||
return VIDEO_ERROR_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return VIDEO_ERROR_INVALID_REQUEST;
|
||||
@@ -874,6 +969,7 @@ static int handle_video_stm_std_req(uint8_t rhport, uint8_t stage,
|
||||
tusb_control_request_t const *request,
|
||||
uint_fast8_t stm_idx)
|
||||
{
|
||||
TU_LOG_DRV("\r\n");
|
||||
videod_streaming_interface_t *self = &_videod_streaming_itf[stm_idx];
|
||||
switch (request->bRequest) {
|
||||
case TUSB_REQ_GET_INTERFACE:
|
||||
@@ -889,8 +985,7 @@ static int handle_video_stm_std_req(uint8_t rhport, uint8_t stage,
|
||||
return VIDEO_ERROR_NONE;
|
||||
|
||||
case TUSB_REQ_SET_INTERFACE:
|
||||
if (stage == CONTROL_STAGE_SETUP)
|
||||
{
|
||||
if (stage == CONTROL_STAGE_SETUP) {
|
||||
TU_VERIFY(_open_vs_itf(rhport, self, request->wValue), VIDEO_ERROR_UNKNOWN);
|
||||
tud_control_status(rhport, request);
|
||||
}
|
||||
@@ -904,26 +999,26 @@ static int handle_video_stm_std_req(uint8_t rhport, uint8_t stage,
|
||||
|
||||
static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage,
|
||||
tusb_control_request_t const *request,
|
||||
uint_fast8_t stm_idx)
|
||||
{
|
||||
uint_fast8_t stm_idx) {
|
||||
(void)rhport;
|
||||
videod_streaming_interface_t *self = &_videod_streaming_itf[stm_idx];
|
||||
|
||||
uint8_t const ctrl_sel = TU_U16_HIGH(request->wValue);
|
||||
TU_LOG_DRV("%s_Control(%s)\r\n", tu_str_video_vs_control_selector[ctrl_sel], tu_lookup_find(&tu_table_video_request, request->bRequest));
|
||||
|
||||
/* 4.2.1 Interface Control Request */
|
||||
switch (TU_U16_HIGH(request->wValue)) {
|
||||
switch (ctrl_sel) {
|
||||
case VIDEO_VS_CTL_STREAM_ERROR_CODE:
|
||||
switch (request->bRequest) {
|
||||
case VIDEO_REQUEST_GET_CUR:
|
||||
if (stage == CONTROL_STAGE_SETUP)
|
||||
{
|
||||
if (stage == CONTROL_STAGE_SETUP) {
|
||||
/* TODO */
|
||||
TU_VERIFY(tud_control_xfer(rhport, request, &self->error_code, sizeof(uint8_t)), VIDEO_ERROR_UNKNOWN);
|
||||
}
|
||||
return VIDEO_ERROR_NONE;
|
||||
|
||||
case VIDEO_REQUEST_GET_INFO:
|
||||
if (stage == CONTROL_STAGE_SETUP)
|
||||
{
|
||||
if (stage == CONTROL_STAGE_SETUP) {
|
||||
TU_VERIFY(tud_control_xfer(rhport, request, (uint8_t*)(uintptr_t) &_cap_get, sizeof(_cap_get)), VIDEO_ERROR_UNKNOWN);
|
||||
}
|
||||
return VIDEO_ERROR_NONE;
|
||||
@@ -935,25 +1030,23 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage,
|
||||
case VIDEO_VS_CTL_PROBE:
|
||||
if (self->state != VS_STATE_PROBING) {
|
||||
self->state = VS_STATE_PROBING;
|
||||
_init_vs_configuration(self);
|
||||
}
|
||||
|
||||
switch (request->bRequest) {
|
||||
case VIDEO_REQUEST_SET_CUR:
|
||||
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)),
|
||||
TU_VERIFY(tud_control_xfer(rhport, request, &self->probe_commit_payload, sizeof(video_probe_and_commit_control_t)),
|
||||
VIDEO_ERROR_UNKNOWN);
|
||||
} else if (stage == CONTROL_STAGE_DATA) {
|
||||
TU_VERIFY(_update_streaming_parameters(self, (video_probe_and_commit_control_t*)self->ep_buf),
|
||||
TU_VERIFY(_update_streaming_parameters(self, &self->probe_commit_payload),
|
||||
VIDEO_ERROR_INVALID_VALUE_WITHIN_RANGE);
|
||||
}
|
||||
return VIDEO_ERROR_NONE;
|
||||
|
||||
case VIDEO_REQUEST_GET_CUR:
|
||||
if (stage == CONTROL_STAGE_SETUP)
|
||||
{
|
||||
if (stage == CONTROL_STAGE_SETUP) {
|
||||
TU_VERIFY(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);
|
||||
TU_VERIFY(tud_control_xfer(rhport, request, &self->probe_commit_payload, sizeof(video_probe_and_commit_control_t)), VIDEO_ERROR_UNKNOWN);
|
||||
}
|
||||
return VIDEO_ERROR_NONE;
|
||||
|
||||
@@ -961,19 +1054,16 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage,
|
||||
case VIDEO_REQUEST_GET_MAX:
|
||||
case VIDEO_REQUEST_GET_RES:
|
||||
case VIDEO_REQUEST_GET_DEF:
|
||||
if (stage == CONTROL_STAGE_SETUP)
|
||||
{
|
||||
if (stage == CONTROL_STAGE_SETUP) {
|
||||
TU_VERIFY(request->wLength, VIDEO_ERROR_UNKNOWN);
|
||||
video_probe_and_commit_control_t tmp;
|
||||
tmp = *(video_probe_and_commit_control_t*)&self->ep_buf;
|
||||
video_probe_and_commit_control_t tmp = self->probe_commit_payload;
|
||||
TU_VERIFY(_negotiate_streaming_parameters(self, request->bRequest, &tmp), VIDEO_ERROR_INVALID_VALUE_WITHIN_RANGE);
|
||||
TU_VERIFY(tud_control_xfer(rhport, request, &tmp, sizeof(tmp)), VIDEO_ERROR_UNKNOWN);
|
||||
}
|
||||
return VIDEO_ERROR_NONE;
|
||||
|
||||
case VIDEO_REQUEST_GET_LEN:
|
||||
if (stage == CONTROL_STAGE_SETUP)
|
||||
{
|
||||
if (stage == CONTROL_STAGE_SETUP) {
|
||||
TU_VERIFY(2 == request->wLength, VIDEO_ERROR_UNKNOWN);
|
||||
uint16_t len = sizeof(video_probe_and_commit_control_t);
|
||||
TU_VERIFY(tud_control_xfer(rhport, request, (uint8_t*)&len, sizeof(len)), VIDEO_ERROR_UNKNOWN);
|
||||
@@ -981,8 +1071,7 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage,
|
||||
return VIDEO_ERROR_NONE;
|
||||
|
||||
case VIDEO_REQUEST_GET_INFO:
|
||||
if (stage == CONTROL_STAGE_SETUP)
|
||||
{
|
||||
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);
|
||||
}
|
||||
@@ -996,10 +1085,9 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage,
|
||||
switch (request->bRequest) {
|
||||
case VIDEO_REQUEST_SET_CUR:
|
||||
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);
|
||||
TU_VERIFY(tud_control_xfer(rhport, request, &self->probe_commit_payload, sizeof(video_probe_and_commit_control_t)), VIDEO_ERROR_UNKNOWN);
|
||||
} else if (stage == CONTROL_STAGE_DATA) {
|
||||
video_probe_and_commit_control_t *param = (video_probe_and_commit_control_t*)self->ep_buf;
|
||||
video_probe_and_commit_control_t *param = &self->probe_commit_payload;
|
||||
TU_VERIFY(_update_streaming_parameters(self, param), VIDEO_ERROR_INVALID_VALUE_WITHIN_RANGE);
|
||||
/* Set the negotiated value */
|
||||
self->max_payload_transfer_size = param->dwMaxPayloadTransferSize;
|
||||
@@ -1021,16 +1109,14 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage,
|
||||
return VIDEO_ERROR_NONE;
|
||||
|
||||
case VIDEO_REQUEST_GET_CUR:
|
||||
if (stage == CONTROL_STAGE_SETUP)
|
||||
{
|
||||
if (stage == CONTROL_STAGE_SETUP) {
|
||||
TU_VERIFY(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);
|
||||
TU_VERIFY(tud_control_xfer(rhport, request, &self->probe_commit_payload, sizeof(video_probe_and_commit_control_t)), VIDEO_ERROR_UNKNOWN);
|
||||
}
|
||||
return VIDEO_ERROR_NONE;
|
||||
|
||||
case VIDEO_REQUEST_GET_LEN:
|
||||
if (stage == CONTROL_STAGE_SETUP)
|
||||
{
|
||||
if (stage == CONTROL_STAGE_SETUP) {
|
||||
TU_VERIFY(2 == request->wLength, VIDEO_ERROR_UNKNOWN);
|
||||
uint16_t len = sizeof(video_probe_and_commit_control_t);
|
||||
TU_VERIFY(tud_control_xfer(rhport, request, (uint8_t*)&len, sizeof(len)), VIDEO_ERROR_UNKNOWN);
|
||||
@@ -1038,8 +1124,7 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage,
|
||||
return VIDEO_ERROR_NONE;
|
||||
|
||||
case VIDEO_REQUEST_GET_INFO:
|
||||
if (stage == CONTROL_STAGE_SETUP)
|
||||
{
|
||||
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);
|
||||
}
|
||||
@@ -1101,6 +1186,16 @@ bool tud_video_n_streaming(uint_fast8_t ctl_idx, uint_fast8_t stm_idx)
|
||||
videod_streaming_interface_t *stm = _get_instance_streaming(ctl_idx, stm_idx);
|
||||
if (!stm || !stm->desc.ep[0]) return false;
|
||||
if (stm->state == VS_STATE_PROBING) return false;
|
||||
|
||||
#ifdef TUP_DCD_EDPT_ISO_ALLOC
|
||||
uint8_t const *desc = _videod_itf[stm->index_vc].beg;
|
||||
uint_fast16_t ofs_ep = stm->desc.ep[0];
|
||||
tusb_desc_endpoint_t const *ep = (tusb_desc_endpoint_t const*)(desc + ofs_ep);
|
||||
if (ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) {
|
||||
if (stm->state == VS_STATE_COMMITTED) return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1140,8 +1235,7 @@ bool tud_video_n_frame_xfer(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, void *bu
|
||||
//--------------------------------------------------------------------+
|
||||
// USBD Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void videod_init(void)
|
||||
{
|
||||
void videod_init(void) {
|
||||
for (uint_fast8_t i = 0; i < CFG_TUD_VIDEO; ++i) {
|
||||
videod_interface_t* ctl = &_videod_itf[i];
|
||||
tu_memclr(ctl, sizeof(*ctl));
|
||||
@@ -1152,8 +1246,11 @@ void videod_init(void)
|
||||
}
|
||||
}
|
||||
|
||||
void videod_reset(uint8_t rhport)
|
||||
{
|
||||
bool videod_deinit(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void videod_reset(uint8_t rhport) {
|
||||
(void) rhport;
|
||||
for (uint_fast8_t i = 0; i < CFG_TUD_VIDEO; ++i) {
|
||||
videod_interface_t* ctl = &_videod_itf[i];
|
||||
@@ -1165,8 +1262,7 @@ void videod_reset(uint8_t rhport)
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t videod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
|
||||
{
|
||||
uint16_t videod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len) {
|
||||
TU_VERIFY((TUSB_CLASS_VIDEO == itf_desc->bInterfaceClass) &&
|
||||
(VIDEO_SUBCLASS_CONTROL == itf_desc->bInterfaceSubClass) &&
|
||||
(VIDEO_ITF_PROTOCOL_15 == itf_desc->bInterfaceProtocol), 0);
|
||||
@@ -1208,12 +1304,30 @@ uint16_t videod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin
|
||||
cur = _next_desc_itf(cur, end);
|
||||
stm->desc.end = (uint16_t) ((uintptr_t)cur - (uintptr_t)itf_desc);
|
||||
stm->state = VS_STATE_PROBING;
|
||||
#ifdef TUP_DCD_EDPT_ISO_ALLOC
|
||||
/* Allocate ISO endpoints */
|
||||
uint16_t ep_size = 0;
|
||||
uint16_t ep_addr = 0;
|
||||
uint8_t const *p_desc = (uint8_t const*)itf_desc + stm->desc.beg;
|
||||
uint8_t const *p_desc_end = (uint8_t const*)itf_desc + stm->desc.end;
|
||||
while (p_desc < p_desc_end) {
|
||||
if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT) {
|
||||
tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) p_desc;
|
||||
if (desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) {
|
||||
ep_addr = desc_ep->bEndpointAddress;
|
||||
ep_size = TU_MAX(tu_edpt_packet_size(desc_ep), ep_size);
|
||||
}
|
||||
}
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
}
|
||||
if(ep_addr > 0 && ep_size > 0) usbd_edpt_iso_alloc(rhport, ep_addr, ep_size);
|
||||
#endif
|
||||
if (0 == stm_idx && 1 == bInCollection) {
|
||||
/* If there is only one streaming interface and no alternate settings,
|
||||
* host may not issue set_interface so open the streaming interface here. */
|
||||
uint8_t const *sbeg = (uint8_t const*)itf_desc + stm->desc.beg;
|
||||
uint8_t const *send = (uint8_t const*)itf_desc + stm->desc.end;
|
||||
if (end == _find_desc_itf(sbeg, send, _desc_itfnum(sbeg), 1)) {
|
||||
if (send == _find_desc_itf(sbeg, send, _desc_itfnum(sbeg), 1)) {
|
||||
TU_VERIFY(_open_vs_itf(rhport, stm, 0), 0);
|
||||
}
|
||||
}
|
||||
@@ -1225,8 +1339,7 @@ uint16_t videod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin
|
||||
// Invoked when a control transfer occurred on an interface of this class
|
||||
// Driver response accordingly to the request and the transfer stage (setup/data/ack)
|
||||
// return false to stall control endpoint (e.g unsupported request)
|
||||
bool videod_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
|
||||
{
|
||||
bool videod_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) {
|
||||
int err;
|
||||
TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE);
|
||||
uint_fast8_t itfnum = tu_u16_low(request->wIndex);
|
||||
@@ -1239,6 +1352,7 @@ bool videod_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_
|
||||
}
|
||||
|
||||
if (itf < CFG_TUD_VIDEO) {
|
||||
TU_LOG_DRV(" VC[%d]: ", itf);
|
||||
err = handle_video_ctl_req(rhport, stage, request, itf);
|
||||
_videod_itf[itf].error_code = (uint8_t)err;
|
||||
if (err) return false;
|
||||
@@ -1254,6 +1368,7 @@ bool videod_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_
|
||||
}
|
||||
|
||||
if (itf < CFG_TUD_VIDEO_STREAMING) {
|
||||
TU_LOG_DRV(" VS[%d]: ", itf);
|
||||
err = handle_video_stm_req(rhport, stage, request, itf);
|
||||
_videod_streaming_itf[itf].error_code = (uint8_t)err;
|
||||
if (err) return false;
|
||||
@@ -1262,8 +1377,7 @@ bool videod_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_
|
||||
return false;
|
||||
}
|
||||
|
||||
bool videod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
|
||||
{
|
||||
bool videod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
|
||||
(void)result; (void)xferred_bytes;
|
||||
|
||||
/* find streaming handle */
|
||||
|
||||
@@ -85,6 +85,7 @@ TU_ATTR_WEAK int tud_video_commit_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx,
|
||||
// INTERNAL USBD-CLASS DRIVER API
|
||||
//--------------------------------------------------------------------+
|
||||
void videod_init (void);
|
||||
bool videod_deinit (void);
|
||||
void videod_reset (uint8_t rhport);
|
||||
uint16_t videod_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
|
||||
bool videod_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
|
||||
|
||||
Reference in New Issue
Block a user