Merge remote-tracking branch 'upstream/master' into async_io
Signed-off-by: HiFiPhile <admin@hifiphile.com>
This commit is contained in:
@@ -661,6 +661,7 @@ typedef struct TU_ATTR_PACKED
|
||||
uint16_t wTotalLength ; ///< Total number of bytes returned for the class-specific AudioControl interface descriptor. Includes the combined length of this descriptor header and all Clock Source, Unit and Terminal descriptors.
|
||||
uint8_t bmControls ; ///< See: audio_cs_ac_interface_control_pos_t.
|
||||
} audio_desc_cs_ac_interface_t;
|
||||
TU_VERIFY_STATIC(sizeof(audio_desc_cs_ac_interface_t) == 9, "size is not correct");
|
||||
|
||||
/// AUDIO Clock Source Descriptor (4.7.2.1)
|
||||
typedef struct TU_ATTR_PACKED
|
||||
|
||||
@@ -208,15 +208,15 @@ tu_static CFG_TUD_MEM_SECTION struct {
|
||||
#endif// CFG_TUD_AUDIO_ENABLE_EP_OUT && (USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_DECODING)
|
||||
|
||||
// Control buffers
|
||||
tu_static uint8_t ctrl_buf_1[CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ];
|
||||
|
||||
#if CFG_TUD_AUDIO > 1
|
||||
tu_static uint8_t ctrl_buf_2[CFG_TUD_AUDIO_FUNC_2_CTRL_BUF_SZ];
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_AUDIO > 2
|
||||
tu_static uint8_t ctrl_buf_3[CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ];
|
||||
#endif
|
||||
tu_static CFG_TUD_MEM_SECTION struct {
|
||||
TUD_EPBUF_DEF(buf1, CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ);
|
||||
#if CFG_TUD_AUDIO > 1
|
||||
TUD_EPBUF_DEF(buf2, CFG_TUD_AUDIO_FUNC_2_CTRL_BUF_SZ);
|
||||
#endif
|
||||
#if CFG_TUD_AUDIO > 2
|
||||
TUD_EPBUF_DEF(buf3, CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ);
|
||||
#endif
|
||||
} ctrl_buf;
|
||||
|
||||
// Active alternate setting of interfaces
|
||||
tu_static uint8_t alt_setting_1[CFG_TUD_AUDIO_FUNC_1_N_AS_INT];
|
||||
@@ -628,10 +628,6 @@ static uint8_t audiod_get_audio_fct_idx(audiod_function_t *audio);
|
||||
|
||||
#if (CFG_TUD_AUDIO_ENABLE_EP_IN && (CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL || CFG_TUD_AUDIO_ENABLE_ENCODING)) || (CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING)
|
||||
static void audiod_parse_for_AS_params(audiod_function_t *audio, uint8_t const *p_desc, uint8_t const *p_desc_end, uint8_t const as_itf);
|
||||
|
||||
static inline uint8_t tu_desc_subtype(void const *desc) {
|
||||
return ((uint8_t const *) desc)[2];
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
|
||||
@@ -1227,18 +1223,18 @@ void audiod_init(void) {
|
||||
// Initialize control buffers
|
||||
switch (i) {
|
||||
case 0:
|
||||
audio->ctrl_buf = ctrl_buf_1;
|
||||
audio->ctrl_buf = ctrl_buf.buf1;
|
||||
audio->ctrl_buf_sz = CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ;
|
||||
break;
|
||||
#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_CTRL_BUF_SZ > 0
|
||||
case 1:
|
||||
audio->ctrl_buf = ctrl_buf_2;
|
||||
audio->ctrl_buf = ctrl_buf.buf2;
|
||||
audio->ctrl_buf_sz = CFG_TUD_AUDIO_FUNC_2_CTRL_BUF_SZ;
|
||||
break;
|
||||
#endif
|
||||
#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ > 0
|
||||
case 2:
|
||||
audio->ctrl_buf = ctrl_buf_3;
|
||||
audio->ctrl_buf = ctrl_buf.buf3;
|
||||
audio->ctrl_buf_sz = CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ;
|
||||
break;
|
||||
#endif
|
||||
|
||||
@@ -82,7 +82,7 @@ typedef struct {
|
||||
static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC];
|
||||
CFG_TUD_MEM_SECTION static cdcd_epbuf_t _cdcd_epbuf[CFG_TUD_CDC];
|
||||
|
||||
static tud_cdc_configure_fifo_t _cdcd_fifo_cfg;
|
||||
static tud_cdc_configure_t _cdcd_cfg = TUD_CDC_CONFIGURE_DEFAULT();
|
||||
|
||||
static bool _prep_out_transaction(uint8_t itf) {
|
||||
const uint8_t rhport = 0;
|
||||
@@ -119,9 +119,9 @@ static bool _prep_out_transaction(uint8_t itf) {
|
||||
// APPLICATION API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
bool tud_cdc_configure_fifo(const tud_cdc_configure_fifo_t* cfg) {
|
||||
TU_VERIFY(cfg);
|
||||
_cdcd_fifo_cfg = (*cfg);
|
||||
bool tud_cdc_configure(const tud_cdc_configure_t* driver_cfg) {
|
||||
TU_VERIFY(driver_cfg);
|
||||
_cdcd_cfg = *driver_cfg;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ void tud_cdc_n_read_flush(uint8_t itf) {
|
||||
//--------------------------------------------------------------------+
|
||||
uint32_t tud_cdc_n_write(uint8_t itf, const void* buffer, uint32_t bufsize) {
|
||||
cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
|
||||
uint16_t ret = tu_fifo_write_n(&p_cdc->tx_ff, buffer, (uint16_t) TU_MIN(bufsize, UINT16_MAX));
|
||||
uint16_t wr_count = tu_fifo_write_n(&p_cdc->tx_ff, buffer, (uint16_t) TU_MIN(bufsize, UINT16_MAX));
|
||||
|
||||
// flush if queue more than packet size
|
||||
if (tu_fifo_count(&p_cdc->tx_ff) >= BULK_PACKET_SIZE
|
||||
@@ -186,7 +186,7 @@ uint32_t tud_cdc_n_write(uint8_t itf, const void* buffer, uint32_t bufsize) {
|
||||
tud_cdc_n_write_flush(itf);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return wr_count;
|
||||
}
|
||||
|
||||
uint32_t tud_cdc_n_write_flush(uint8_t itf) {
|
||||
@@ -233,8 +233,6 @@ bool tud_cdc_n_write_clear(uint8_t itf) {
|
||||
//--------------------------------------------------------------------+
|
||||
void cdcd_init(void) {
|
||||
tu_memclr(_cdcd_itf, sizeof(_cdcd_itf));
|
||||
tu_memclr(&_cdcd_fifo_cfg, sizeof(_cdcd_fifo_cfg));
|
||||
|
||||
for (uint8_t i = 0; i < CFG_TUD_CDC; i++) {
|
||||
cdcd_interface_t* p_cdc = &_cdcd_itf[i];
|
||||
|
||||
@@ -249,10 +247,10 @@ void cdcd_init(void) {
|
||||
// Config RX fifo
|
||||
tu_fifo_config(&p_cdc->rx_ff, p_cdc->rx_ff_buf, TU_ARRAY_SIZE(p_cdc->rx_ff_buf), 1, false);
|
||||
|
||||
// Config TX fifo as overwritable at initialization and will be changed to non-overwritable
|
||||
// if terminal supports DTR bit. Without DTR we do not know if data is actually polled by terminal.
|
||||
// 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);
|
||||
// TX fifo can be configured to change to overwritable if not connected (DTR bit not set). Without DTR we do not
|
||||
// know if data is actually polled by terminal. This way the most current data is prioritized.
|
||||
// Default: is overwritable
|
||||
tu_fifo_config(&p_cdc->tx_ff, p_cdc->tx_ff_buf, TU_ARRAY_SIZE(p_cdc->tx_ff_buf), 1, _cdcd_cfg.tx_overwritabe_if_not_connected);
|
||||
|
||||
#if OSAL_MUTEX_REQUIRED
|
||||
osal_mutex_t mutex_rd = osal_mutex_create(&p_cdc->rx_ff_mutex);
|
||||
@@ -294,13 +292,13 @@ void cdcd_reset(uint8_t rhport) {
|
||||
cdcd_interface_t* p_cdc = &_cdcd_itf[i];
|
||||
|
||||
tu_memclr(p_cdc, ITF_MEM_RESET_SIZE);
|
||||
if (!_cdcd_fifo_cfg.rx_persistent) {
|
||||
if (!_cdcd_cfg.rx_persistent) {
|
||||
tu_fifo_clear(&p_cdc->rx_ff);
|
||||
}
|
||||
if (!_cdcd_fifo_cfg.tx_persistent) {
|
||||
if (!_cdcd_cfg.tx_persistent) {
|
||||
tu_fifo_clear(&p_cdc->tx_ff);
|
||||
}
|
||||
tu_fifo_set_overwritable(&p_cdc->tx_ff, true);
|
||||
tu_fifo_set_overwritable(&p_cdc->tx_ff, _cdcd_cfg.tx_overwritabe_if_not_connected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -414,8 +412,12 @@ bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_requ
|
||||
|
||||
p_cdc->line_state = (uint8_t) request->wValue;
|
||||
|
||||
// Disable fifo overwriting if DTR bit is set
|
||||
tu_fifo_set_overwritable(&p_cdc->tx_ff, !dtr);
|
||||
// If enabled: fifo overwriting is disabled if DTR bit is set and vice versa
|
||||
if (_cdcd_cfg.tx_overwritabe_if_not_connected) {
|
||||
tu_fifo_set_overwritable(&p_cdc->tx_ff, !dtr);
|
||||
} else {
|
||||
tu_fifo_set_overwritable(&p_cdc->tx_ff, false);
|
||||
}
|
||||
|
||||
TU_LOG_DRV(" Set Control Line State: DTR = %d, RTS = %d\r\n", dtr, rts);
|
||||
|
||||
@@ -496,7 +498,7 @@ bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_
|
||||
// xferred_bytes is multiple of EP Packet size and not zero
|
||||
if (!tu_fifo_count(&p_cdc->tx_ff) && xferred_bytes && (0 == (xferred_bytes & (BULK_PACKET_SIZE - 1)))) {
|
||||
if (usbd_edpt_claim(rhport, p_cdc->ep_in)) {
|
||||
usbd_edpt_xfer(rhport, p_cdc->ep_in, NULL, 0);
|
||||
TU_ASSERT(usbd_edpt_xfer(rhport, p_cdc->ep_in, NULL, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,14 +48,24 @@
|
||||
//--------------------------------------------------------------------+
|
||||
// Driver Configuration
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t rx_persistent : 1; // keep rx fifo on bus reset or disconnect
|
||||
uint8_t tx_persistent : 1; // keep tx fifo on bus reset or disconnect
|
||||
} tud_cdc_configure_fifo_t;
|
||||
uint8_t rx_persistent : 1; // keep rx fifo data even with bus reset or disconnect
|
||||
uint8_t tx_persistent : 1; // keep tx fifo data even with reset or disconnect
|
||||
uint8_t tx_overwritabe_if_not_connected : 1; // if not connected, tx fifo can be overwritten
|
||||
} tud_cdc_configure_t;
|
||||
|
||||
// Configure CDC FIFOs behavior
|
||||
bool tud_cdc_configure_fifo(tud_cdc_configure_fifo_t const* cfg);
|
||||
#define TUD_CDC_CONFIGURE_DEFAULT() { \
|
||||
.rx_persistent = 0, \
|
||||
.tx_persistent = 0, \
|
||||
.tx_overwritabe_if_not_connected = 1, \
|
||||
}
|
||||
|
||||
// Configure CDC driver behavior
|
||||
bool tud_cdc_configure(const tud_cdc_configure_t* driver_cfg);
|
||||
|
||||
// Backward compatible
|
||||
#define tud_cdc_configure_fifo_t tud_cdc_configure_t
|
||||
#define tud_cdc_configure_fifo tud_cdc_configure
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application API (Multiple Ports) i.e. CFG_TUD_CDC > 1
|
||||
|
||||
@@ -691,10 +691,10 @@ bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t
|
||||
}
|
||||
} else if ( ep_addr == p_cdc->stream.rx.ep_addr ) {
|
||||
#if CFG_TUH_CDC_FTDI
|
||||
if (p_cdc->serial_drid == SERIAL_DRIVER_FTDI) {
|
||||
if (p_cdc->serial_drid == SERIAL_DRIVER_FTDI && xferred_bytes > 2) {
|
||||
// FTDI reserve 2 bytes for status
|
||||
// uint8_t status[2] = {p_cdc->stream.rx.ep_buf[0], p_cdc->stream.rx.ep_buf[1]};
|
||||
tu_edpt_stream_read_xfer_complete_offset(&p_cdc->stream.rx, xferred_bytes, 2);
|
||||
tu_edpt_stream_read_xfer_complete_with_buf(&p_cdc->stream.rx, p_cdc->stream.rx.ep_buf+2, xferred_bytes-2);
|
||||
}else
|
||||
#endif
|
||||
{
|
||||
|
||||
@@ -49,22 +49,22 @@
|
||||
|
||||
// RX FIFO size
|
||||
#ifndef CFG_TUH_CDC_RX_BUFSIZE
|
||||
#define CFG_TUH_CDC_RX_BUFSIZE USBH_EPSIZE_BULK_MAX
|
||||
#define CFG_TUH_CDC_RX_BUFSIZE TUH_EPSIZE_BULK_MPS
|
||||
#endif
|
||||
|
||||
// RX Endpoint size
|
||||
#ifndef CFG_TUH_CDC_RX_EPSIZE
|
||||
#define CFG_TUH_CDC_RX_EPSIZE USBH_EPSIZE_BULK_MAX
|
||||
#define CFG_TUH_CDC_RX_EPSIZE TUH_EPSIZE_BULK_MPS
|
||||
#endif
|
||||
|
||||
// TX FIFO size
|
||||
#ifndef CFG_TUH_CDC_TX_BUFSIZE
|
||||
#define CFG_TUH_CDC_TX_BUFSIZE USBH_EPSIZE_BULK_MAX
|
||||
#define CFG_TUH_CDC_TX_BUFSIZE TUH_EPSIZE_BULK_MPS
|
||||
#endif
|
||||
|
||||
// TX Endpoint size
|
||||
#ifndef CFG_TUH_CDC_TX_EPSIZE
|
||||
#define CFG_TUH_CDC_TX_EPSIZE USBH_EPSIZE_BULK_MAX
|
||||
#define CFG_TUH_CDC_TX_EPSIZE TUH_EPSIZE_BULK_MPS
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
@@ -325,6 +325,29 @@ typedef enum
|
||||
|
||||
/// @}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Digitizer Stylus Pen
|
||||
//--------------------------------------------------------------------+
|
||||
/** \addtogroup ClassDriver_HID_Stylus Stylus
|
||||
* @{ */
|
||||
|
||||
// Standard Stylus Pen Report.
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t attr; /**< Attribute mask for describing current status of the stylus pen. */
|
||||
uint16_t x; /**< Current x position of the mouse. */
|
||||
uint16_t y; /**< Current y position of the mouse. */
|
||||
} hid_stylus_report_t;
|
||||
|
||||
// Standard Stylus Pen Attributes Bitmap.
|
||||
typedef enum
|
||||
{
|
||||
STYLUS_ATTR_TIP_SWITCH = TU_BIT(0), ///< Tip switch
|
||||
STYLUS_ATTR_IN_RANGE = TU_BIT(1), ///< In-range bit.
|
||||
} hid_stylus_attr_bm_t;
|
||||
|
||||
/// @}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Keyboard
|
||||
//--------------------------------------------------------------------+
|
||||
@@ -740,6 +763,21 @@ enum {
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Usage Table
|
||||
/* Usage Types Data
|
||||
Sel Selector Array
|
||||
SV Static Value Constant, Variable, Absolute
|
||||
SF Static Flag Constant, Variable, Absolute
|
||||
DV Dynamic Value Constant, Variable, Absolute
|
||||
DF Dynamic Flag Constant, Variable, Absolute
|
||||
*/
|
||||
/* Usage Types Collection
|
||||
NAry Named Array Logical
|
||||
CA Collection Application Application
|
||||
CL Collection Logical Logical
|
||||
CP Collection Physical Physical
|
||||
US Usage Switch Logical
|
||||
UM Usage Modifier Logical
|
||||
*/
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
/// HID Usage Table - Table 1: Usage Page Summary
|
||||
@@ -759,8 +797,14 @@ enum {
|
||||
HID_USAGE_PAGE_DIGITIZER = 0x0d,
|
||||
HID_USAGE_PAGE_PID = 0x0f,
|
||||
HID_USAGE_PAGE_UNICODE = 0x10,
|
||||
HID_USAGE_PAGE_ALPHA_DISPLAY = 0x14,
|
||||
HID_USAGE_PAGE_MEDICAL = 0x40,
|
||||
HID_USAGE_PAGE_SOC = 0x11,
|
||||
HID_USAGE_PAGE_EYE_AND_HEAD_TRACKERS = 0x12,
|
||||
// 0x13 is reserved
|
||||
HID_USAGE_PAGE_AUXILIARY_DISPLAY = 0x14,
|
||||
// 0x15 - 0x1f is reserved
|
||||
HID_USAGE_PAGE_SENSORS = 0x20,
|
||||
// 0x21 - 0x3f is reserved
|
||||
HID_USAGE_PAGE_MEDICAL_INSTRUMENT = 0x40,
|
||||
HID_USAGE_PAGE_LIGHTING_AND_ILLUMINATION = 0x59,
|
||||
HID_USAGE_PAGE_MONITOR = 0x80, // 0x80 - 0x83
|
||||
HID_USAGE_PAGE_POWER = 0x84, // 0x084 - 0x87
|
||||
@@ -846,7 +890,6 @@ enum {
|
||||
HID_USAGE_DESKTOP_SYSTEM_DISPLAY_LCD_AUTOSCALE = 0xB7
|
||||
};
|
||||
|
||||
|
||||
/// HID Usage Table: Consumer Page (0x0C)
|
||||
/// Only contains controls that supported by Windows (whole list is too long)
|
||||
enum {
|
||||
@@ -905,48 +948,125 @@ enum {
|
||||
HID_USAGE_CONSUMER_AC_PAN = 0x0238,
|
||||
};
|
||||
|
||||
/// HID Usage Table - Lighting And Illumination Page (0x59)
|
||||
/// HID Usage Table: Digitizer Page (0x0D)
|
||||
enum {
|
||||
HID_USAGE_LIGHTING_LAMP_ARRAY = 0x01,
|
||||
HID_USAGE_LIGHTING_LAMP_ARRAY_ATTRIBUTES_REPORT = 0x02,
|
||||
HID_USAGE_LIGHTING_LAMP_COUNT = 0x03,
|
||||
HID_USAGE_LIGHTING_BOUNDING_BOX_WIDTH_IN_MICROMETERS = 0x04,
|
||||
HID_USAGE_LIGHTING_BOUNDING_BOX_HEIGHT_IN_MICROMETERS = 0x05,
|
||||
HID_USAGE_LIGHTING_BOUNDING_BOX_DEPTH_IN_MICROMETERS = 0x06,
|
||||
HID_USAGE_LIGHTING_LAMP_ARRAY_KIND = 0x07,
|
||||
HID_USAGE_LIGHTING_MIN_UPDATE_INTERVAL_IN_MICROSECONDS = 0x08,
|
||||
HID_USAGE_LIGHTING_LAMP_ATTRIBUTES_REQUEST_REPORT = 0x20,
|
||||
HID_USAGE_LIGHTING_LAMP_ID = 0x21,
|
||||
HID_USAGE_LIGHTING_LAMP_ATTRIBUTES_RESPONSE_REPORT = 0x22,
|
||||
HID_USAGE_LIGHTING_POSITION_X_IN_MICROMETERS = 0x23,
|
||||
HID_USAGE_LIGHTING_POSITION_Y_IN_MICROMETERS = 0x24,
|
||||
HID_USAGE_LIGHTING_POSITION_Z_IN_MICROMETERS = 0x25,
|
||||
HID_USAGE_LIGHTING_LAMP_PURPOSES = 0x26,
|
||||
HID_USAGE_LIGHTING_UPDATE_LATENCY_IN_MICROSECONDS = 0x27,
|
||||
HID_USAGE_LIGHTING_RED_LEVEL_COUNT = 0x28,
|
||||
HID_USAGE_LIGHTING_GREEN_LEVEL_COUNT = 0x29,
|
||||
HID_USAGE_LIGHTING_BLUE_LEVEL_COUNT = 0x2A,
|
||||
HID_USAGE_LIGHTING_INTENSITY_LEVEL_COUNT = 0x2B,
|
||||
HID_USAGE_LIGHTING_IS_PROGRAMMABLE = 0x2C,
|
||||
HID_USAGE_LIGHTING_INPUT_BINDING = 0x2D,
|
||||
HID_USAGE_LIGHTING_LAMP_MULTI_UPDATE_REPORT = 0x50,
|
||||
HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL = 0x51,
|
||||
HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL = 0x52,
|
||||
HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL = 0x53,
|
||||
HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL = 0x54,
|
||||
HID_USAGE_LIGHTING_LAMP_UPDATE_FLAGS = 0x55,
|
||||
HID_USAGE_LIGHTING_LAMP_RANGE_UPDATE_REPORT = 0x60,
|
||||
HID_USAGE_LIGHTING_LAMP_ID_START = 0x61,
|
||||
HID_USAGE_LIGHTING_LAMP_ID_END = 0x62,
|
||||
HID_USAGE_LIGHTING_LAMP_ARRAY_CONTROL_REPORT = 0x70,
|
||||
HID_USAGE_LIGHTING_AUTONOMOUS_MODE = 0x71,
|
||||
};
|
||||
HID_USAGE_DIGITIZER_UNDEFINED = 0x00,
|
||||
HID_USAGE_DIGITIZER_DIGITIZER = 0x01, // CA
|
||||
HID_USAGE_DIGITIZER_PEN = 0x02, // CA
|
||||
HID_USAGE_DIGITIZER_LIGHT_PEN = 0x03, // CA
|
||||
HID_USAGE_DIGITIZER_TOUCH_SCREEN = 0x04, // CA
|
||||
HID_USAGE_DIGITIZER_TOUCH_PAD = 0x05, // CA
|
||||
HID_USAGE_DIGITIZER_WHITEBOARD = 0x06, // CA
|
||||
HID_USAGE_DIGITIZER_COORDINATE_MEASURING_MACHINE = 0x07, // CA
|
||||
HID_USAGE_DIGITIZER_3D_DIGITIZER = 0x08, // CA
|
||||
HID_USAGE_DIGITIZER_STEREO_PLOTTER = 0x09, // CA
|
||||
HID_USAGE_DIGITIZER_ARTICULATED_ARM = 0x0A, // CA
|
||||
HID_USAGE_DIGITIZER_ARMATURE = 0x0B, // CA
|
||||
HID_USAGE_DIGITIZER_MULTIPLE_POINT_DIGITIZER = 0x0C, // CA
|
||||
HID_USAGE_DIGITIZER_FREE_SPACE_WAND = 0x0D, // CA
|
||||
HID_USAGE_DIGITIZER_DEVICE_CONFIGURATION = 0x0E, // CA
|
||||
HID_USAGE_DIGITIZER_CAPACITIVE_HEAT_MAP_DIGITIZER = 0x0F, // CA
|
||||
// Reserved (0x10 - 0x1F)
|
||||
HID_USAGE_DIGITIZER_STYLUS = 0x20, // CA/CL
|
||||
HID_USAGE_DIGITIZER_PUCK = 0x21, // CL
|
||||
HID_USAGE_DIGITIZER_FINGER = 0x22, // CL
|
||||
HID_USAGE_DIGITIZER_DEVICE_SETTINGS = 0x23, // CL
|
||||
HID_USAGE_DIGITIZER_CHARACTER_GESTURE = 0x24, // CL
|
||||
// Reserved (0x25 - 0x2F)
|
||||
HID_USAGE_DIGITIZER_TIP_PRESSURE = 0x30, // DV
|
||||
HID_USAGE_DIGITIZER_BARREL_PRESSURE = 0x31, // DV
|
||||
HID_USAGE_DIGITIZER_IN_RANGE = 0x32, // MC
|
||||
HID_USAGE_DIGITIZER_TOUCH = 0x33, // MC
|
||||
HID_USAGE_DIGITIZER_UNTOUCH = 0x34, // OSC
|
||||
HID_USAGE_DIGITIZER_TAP = 0x35, // OSC
|
||||
HID_USAGE_DIGITIZER_QUALITY = 0x36, // DV
|
||||
HID_USAGE_DIGITIZER_DATA_VALID = 0x37, // MC
|
||||
HID_USAGE_DIGITIZER_TRANSDUCER_INDEX = 0x38, // DV
|
||||
HID_USAGE_DIGITIZER_TABLET_FUNCTION_KEYS = 0x39, // CL
|
||||
HID_USAGE_DIGITIZER_PROGRAM_CHANGE_KEYS = 0x3A, // CL
|
||||
HID_USAGE_DIGITIZER_BATTERY_STRENGTH = 0x3B, // DV
|
||||
HID_USAGE_DIGITIZER_INVERT = 0x3C, // MC
|
||||
HID_USAGE_DIGITIZER_X_TILT = 0x3D, // DV
|
||||
HID_USAGE_DIGITIZER_Y_TILT = 0x3E, // DV
|
||||
HID_USAGE_DIGITIZER_AZIMUTH = 0x3F, // DV
|
||||
HID_USAGE_DIGITIZER_ALTITUDE = 0x40, // DV
|
||||
HID_USAGE_DIGITIZER_TWIST = 0x41, // DV
|
||||
HID_USAGE_DIGITIZER_TIP_SWITCH = 0x42, // MC
|
||||
HID_USAGE_DIGITIZER_SECONDARY_TIP_SWITCH = 0x43, // MC
|
||||
HID_USAGE_DIGITIZER_BARREL_SWITCH = 0x44, // MC
|
||||
HID_USAGE_DIGITIZER_ERASER = 0x45, // MC
|
||||
HID_USAGE_DIGITIZER_TABLET_PICK = 0x46, // MC
|
||||
HID_USAGE_DIGITIZER_TOUCH_VALID = 0x47, // MC
|
||||
HID_USAGE_DIGITIZER_WIDTH = 0x48, // DV
|
||||
HID_USAGE_DIGITIZER_HEIGHT = 0x49, // DV
|
||||
// Reserved (0x4A - 0x50)
|
||||
HID_USAGE_DIGITIZER_CONTACT_IDENTIFIER = 0x51, // DV
|
||||
HID_USAGE_DIGITIZER_DEVICE_MODE = 0x52, // DV
|
||||
HID_USAGE_DIGITIZER_DEVICE_IDENTIFIER = 0x53, // DV/SV
|
||||
HID_USAGE_DIGITIZER_CONTACT_COUNT = 0x54, // DV
|
||||
HID_USAGE_DIGITIZER_CONTACT_COUNT_MAXIMUM = 0x55, // SV
|
||||
HID_USAGE_DIGITIZER_SCAN_TIME = 0x56, // DV
|
||||
HID_USAGE_DIGITIZER_SURFACE_SWITCH = 0x57, // DF
|
||||
HID_USAGE_DIGITIZER_BUTTON_SWITCH = 0x58, // DF
|
||||
HID_USAGE_DIGITIZER_PAD_TYPE = 0x59, // SF
|
||||
HID_USAGE_DIGITIZER_TRANSDUCER_SERIAL_NUMBER = 0x5B, // SV
|
||||
HID_USAGE_DIGITIZER_PREFERRED_COLOR = 0x5C, // DV
|
||||
HID_USAGE_DIGITIZER_PREFERRED_COLOR_LOCKED = 0x5D, // MC
|
||||
HID_USAGE_DIGITIZER_PREFERRED_LINE_WIDTH = 0x5E, // DV
|
||||
HID_USAGE_DIGITIZER_PREFERRED_LINE_WIDTH_LOCKED = 0x5F, // MC
|
||||
HID_USAGE_DIGITIZER_LATENCY_MODE = 0x60, // DF
|
||||
HID_USAGE_DIGITIZER_GESTURE_CHARACTER_QUALITY = 0x61, // DV
|
||||
HID_USAGE_DIGITIZER_CHARACTER_GESTURE_DATA_LENGTH = 0x62, // DV
|
||||
HID_USAGE_DIGITIZER_CHARACTER_GESTURE_DATA = 0x63, // DV
|
||||
HID_USAGE_DIGITIZER_GESTURE_CHARACTER_ENCODING = 0x64, // NAry
|
||||
HID_USAGE_DIGITIZER_UTF8_CHARACTER_GESTURE_ENCODING = 0x65, // Sel
|
||||
HID_USAGE_DIGITIZER_UTF16_LE_CHARACTER_GESTURE_ENCODING = 0x66, // Sel
|
||||
HID_USAGE_DIGITIZER_UTF16_BE_CHARACTER_GESTURE_ENCODING = 0x67, // Sel
|
||||
HID_USAGE_DIGITIZER_UTF32_LE_CHARACTER_GESTURE_ENCODING = 0x68, // Sel
|
||||
HID_USAGE_DIGITIZER_UTF32_BE_CHARACTER_GESTURE_ENCODING = 0x69, // Sel
|
||||
HID_USAGE_DIGITIZER_CAPACITIVE_HEAT_MAP_VENDOR_ID = 0x6A, // SV
|
||||
HID_USAGE_DIGITIZER_CAPACITIVE_HEAT_MAP_VERSION = 0x6B, // SV
|
||||
HID_USAGE_DIGITIZER_CAPACITIVE_HEAT_MAP_FRAME_DATA = 0x6C, // DV
|
||||
HID_USAGE_DIGITIZER_GESTURE_CHARACTER_ENABLE = 0x6D, // DF
|
||||
HID_USAGE_DIGITIZER_TRANSDUCER_SERIAL_NUMBER_PART2 = 0x6E, // SV
|
||||
HID_USAGE_DIGITIZER_NO_PREFERRED_COLOR = 0x6F, // DF
|
||||
HID_USAGE_DIGITIZER_PREFERRED_LINE_STYLE = 0x70, // NAry
|
||||
HID_USAGE_DIGITIZER_PREFERRED_LINE_STYLE_LOCKED = 0x71, // MC
|
||||
HID_USAGE_DIGITIZER_INK = 0x72, // Sel
|
||||
HID_USAGE_DIGITIZER_PENCIL = 0x73, // Sel
|
||||
HID_USAGE_DIGITIZER_HIGHLIGHTER = 0x74, // Sel
|
||||
HID_USAGE_DIGITIZER_CHISEL_MARKER = 0x75, // Sel
|
||||
HID_USAGE_DIGITIZER_BRUSH = 0x76, // Sel
|
||||
HID_USAGE_DIGITIZER_NO_PREFERENCE = 0x77, // Sel
|
||||
// Reserved (0x78 - 0x7F)
|
||||
HID_USAGE_DIGITIZER_DIGITIZER_DIAGNOSTIC = 0x80, // CL
|
||||
HID_USAGE_DIGITIZER_DIGITIZER_ERROR = 0x81, // NAry
|
||||
HID_USAGE_DIGITIZER_ERR_NORMAL_STATUS = 0x82, // Sel
|
||||
HID_USAGE_DIGITIZER_ERR_TRANSDUCERS_EXCEEDED = 0x83, // Sel
|
||||
HID_USAGE_DIGITIZER_ERR_FULL_TRANS_FEATURES_UNAVAILABLE = 0x84, // Sel
|
||||
HID_USAGE_DIGITIZER_ERR_CHARGE_LOW = 0x85, // Sel
|
||||
// Reserved (0x86 - 0x8F)
|
||||
HID_USAGE_DIGITIZER_TRANSDUCER_SOFTWARE_INFO = 0x90, // CL
|
||||
HID_USAGE_DIGITIZER_TRANSDUCER_VENDOR_ID = 0x91, // SV
|
||||
HID_USAGE_DIGITIZER_TRANSDUCER_PRODUCT_ID = 0x92, // SV
|
||||
HID_USAGE_DIGITIZER_DEVICE_SUPPORTED_PROTOCOLS = 0x93, // NAry/CL
|
||||
HID_USAGE_DIGITIZER_TRANSDUCER_SUPPORTED_PROTOCOLS = 0x94, // NAry/CL
|
||||
HID_USAGE_DIGITIZER_NO_PROTOCOL = 0x95, // Sel
|
||||
HID_USAGE_DIGITIZER_WACOM_AES_PROTOCOL = 0x96, // Sel
|
||||
HID_USAGE_DIGITIZER_USI_PROTOCOL = 0x97, // Sel
|
||||
HID_USAGE_DIGITIZER_MICROSOFT_PEN_PROTOCOL = 0x98, // Sel
|
||||
// Reserved (0x99 - 0x9F)
|
||||
HID_USAGE_DIGITIZER_SUPPORTED_REPORT_RATES = 0xA0, // SV/CL
|
||||
HID_USAGE_DIGITIZER_REPORT_RATE = 0xA1, // DV
|
||||
HID_USAGE_DIGITIZER_TRANSDUCER_CONNECTED = 0xA2, // SF
|
||||
HID_USAGE_DIGITIZER_SWITCH_DISABLED = 0xA3, // Sel
|
||||
HID_USAGE_DIGITIZER_SWITCH_UNIMPLEMENTED = 0xA4, // Sel
|
||||
HID_USAGE_DIGITIZER_TRANSDUCER_SWITCHES = 0xA5, // CL
|
||||
HID_USAGE_DIGITIZER_TRANSDUCER_INDEX_SELECTOR = 0xA6, // DV
|
||||
// Reserved (0xA7 - 0xAF)
|
||||
HID_USAGE_DIGITIZER_BUTTON_PRESS_THRESHOLD = 0xB0, // DV
|
||||
|
||||
/// HID Usage Table: FIDO Alliance Page (0xF1D0)
|
||||
enum {
|
||||
HID_USAGE_FIDO_U2FHID = 0x01, // U2FHID usage for top-level collection
|
||||
HID_USAGE_FIDO_DATA_IN = 0x20, // Raw IN data report
|
||||
HID_USAGE_FIDO_DATA_OUT = 0x21 // Raw OUT data report
|
||||
// Reserved (0xB1 - 0xFFFF)
|
||||
};
|
||||
|
||||
/// HID Usage Table: Physical Input Device Page (0x0F)
|
||||
@@ -1059,6 +1179,50 @@ enum {
|
||||
HID_USAGE_PID_RAM_POOL_AVAILABLE = 0xac,
|
||||
};
|
||||
|
||||
/// HID Usage Table - Lighting And Illumination Page (0x59)
|
||||
enum {
|
||||
HID_USAGE_LIGHTING_LAMP_ARRAY = 0x01,
|
||||
HID_USAGE_LIGHTING_LAMP_ARRAY_ATTRIBUTES_REPORT = 0x02,
|
||||
HID_USAGE_LIGHTING_LAMP_COUNT = 0x03,
|
||||
HID_USAGE_LIGHTING_BOUNDING_BOX_WIDTH_IN_MICROMETERS = 0x04,
|
||||
HID_USAGE_LIGHTING_BOUNDING_BOX_HEIGHT_IN_MICROMETERS = 0x05,
|
||||
HID_USAGE_LIGHTING_BOUNDING_BOX_DEPTH_IN_MICROMETERS = 0x06,
|
||||
HID_USAGE_LIGHTING_LAMP_ARRAY_KIND = 0x07,
|
||||
HID_USAGE_LIGHTING_MIN_UPDATE_INTERVAL_IN_MICROSECONDS = 0x08,
|
||||
HID_USAGE_LIGHTING_LAMP_ATTRIBUTES_REQUEST_REPORT = 0x20,
|
||||
HID_USAGE_LIGHTING_LAMP_ID = 0x21,
|
||||
HID_USAGE_LIGHTING_LAMP_ATTRIBUTES_RESPONSE_REPORT = 0x22,
|
||||
HID_USAGE_LIGHTING_POSITION_X_IN_MICROMETERS = 0x23,
|
||||
HID_USAGE_LIGHTING_POSITION_Y_IN_MICROMETERS = 0x24,
|
||||
HID_USAGE_LIGHTING_POSITION_Z_IN_MICROMETERS = 0x25,
|
||||
HID_USAGE_LIGHTING_LAMP_PURPOSES = 0x26,
|
||||
HID_USAGE_LIGHTING_UPDATE_LATENCY_IN_MICROSECONDS = 0x27,
|
||||
HID_USAGE_LIGHTING_RED_LEVEL_COUNT = 0x28,
|
||||
HID_USAGE_LIGHTING_GREEN_LEVEL_COUNT = 0x29,
|
||||
HID_USAGE_LIGHTING_BLUE_LEVEL_COUNT = 0x2A,
|
||||
HID_USAGE_LIGHTING_INTENSITY_LEVEL_COUNT = 0x2B,
|
||||
HID_USAGE_LIGHTING_IS_PROGRAMMABLE = 0x2C,
|
||||
HID_USAGE_LIGHTING_INPUT_BINDING = 0x2D,
|
||||
HID_USAGE_LIGHTING_LAMP_MULTI_UPDATE_REPORT = 0x50,
|
||||
HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL = 0x51,
|
||||
HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL = 0x52,
|
||||
HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL = 0x53,
|
||||
HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL = 0x54,
|
||||
HID_USAGE_LIGHTING_LAMP_UPDATE_FLAGS = 0x55,
|
||||
HID_USAGE_LIGHTING_LAMP_RANGE_UPDATE_REPORT = 0x60,
|
||||
HID_USAGE_LIGHTING_LAMP_ID_START = 0x61,
|
||||
HID_USAGE_LIGHTING_LAMP_ID_END = 0x62,
|
||||
HID_USAGE_LIGHTING_LAMP_ARRAY_CONTROL_REPORT = 0x70,
|
||||
HID_USAGE_LIGHTING_AUTONOMOUS_MODE = 0x71,
|
||||
};
|
||||
|
||||
/// HID Usage Table: FIDO Alliance Page (0xF1D0)
|
||||
enum {
|
||||
HID_USAGE_FIDO_U2FHID = 0x01, // U2FHID usage for top-level collection
|
||||
HID_USAGE_FIDO_DATA_IN = 0x20, // Raw IN data report
|
||||
HID_USAGE_FIDO_DATA_OUT = 0x21 // Raw OUT data report
|
||||
};
|
||||
|
||||
/*--------------------------------------------------------------------
|
||||
* ASCII to KEYCODE Conversion
|
||||
* Expand to array of [128][2] (shift, keycode)
|
||||
|
||||
@@ -194,6 +194,16 @@ bool tud_hid_n_gamepad_report(uint8_t instance, uint8_t report_id,
|
||||
return tud_hid_n_report(instance, report_id, &report, sizeof(report));
|
||||
}
|
||||
|
||||
bool tud_hid_n_stylus_report(uint8_t instance, uint8_t report_id, uint8_t attrs, uint16_t x, uint16_t y) {
|
||||
hid_stylus_report_t report = {
|
||||
.attr = attrs,
|
||||
.x = x,
|
||||
.y = y,
|
||||
};
|
||||
|
||||
return tud_hid_n_report(instance, report_id, &report, sizeof(report));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// USBD-CLASS API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
@@ -79,6 +79,9 @@ bool tud_hid_n_abs_mouse_report(uint8_t instance, uint8_t report_id, uint8_t but
|
||||
// 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);
|
||||
|
||||
// STYLUS PEN: convenient helper to send absolute stylus pen report if application
|
||||
bool tud_hid_n_stylus_report(uint8_t instance, uint8_t report_id, uint8_t attrs, uint16_t x, uint16_t y);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application API (Single Port)
|
||||
//--------------------------------------------------------------------+
|
||||
@@ -114,6 +117,10 @@ TU_ATTR_ALWAYS_INLINE static inline bool tud_hid_gamepad_report(uint8_t report_i
|
||||
return tud_hid_n_gamepad_report(0, report_id, x, y, z, rz, rx, ry, hat, buttons);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tud_hid_stylus_report(uint8_t report_id, uint8_t attrs, uint16_t x, uint16_t y) {
|
||||
return tud_hid_n_stylus_report(0, report_id, attrs, x, y);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application Callbacks
|
||||
//--------------------------------------------------------------------+
|
||||
@@ -257,6 +264,41 @@ void tud_hid_report_failed_cb(uint8_t instance, hid_report_type_t report_type, u
|
||||
HID_COLLECTION_END , \
|
||||
HID_COLLECTION_END \
|
||||
|
||||
// Stylus Pen Report Descriptor Template
|
||||
#define TUD_HID_REPORT_DESC_STYLUS_PEN(...) \
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_DIGITIZER ) , \
|
||||
HID_USAGE ( HID_USAGE_DIGITIZER_TOUCH_SCREEN ) , \
|
||||
HID_COLLECTION ( HID_COLLECTION_APPLICATION ) , \
|
||||
/* Report ID if any */\
|
||||
__VA_ARGS__ \
|
||||
HID_USAGE ( HID_USAGE_DIGITIZER_STYLUS ) , \
|
||||
HID_COLLECTION ( HID_COLLECTION_PHYSICAL ) , \
|
||||
HID_USAGE_PAGE ( HID_USAGE_DIGITIZER_TIP_SWITCH ) , \
|
||||
HID_USAGE_PAGE ( HID_USAGE_DIGITIZER_IN_RANGE ) , \
|
||||
HID_LOGICAL_MIN ( 0 ), \
|
||||
HID_LOGICAL_MAX ( 1 ), \
|
||||
HID_REPORT_SIZE ( 1 ), \
|
||||
HID_REPORT_COUNT( 2 ), \
|
||||
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ), \
|
||||
HID_REPORT_SIZE ( 1 ), \
|
||||
HID_REPORT_COUNT( 6 ), \
|
||||
HID_INPUT ( HID_CONSTANT | HID_ARRAY | HID_ABSOLUTE), \
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ), \
|
||||
HID_PHYSICAL_MAX_N( 0x7fff, 2 ), \
|
||||
HID_LOGICAL_MAX_N ( 0x7fff, 2 ), \
|
||||
HID_REPORT_SIZE ( 16 ), \
|
||||
HID_REPORT_COUNT( 1 ), \
|
||||
HID_UNIT_EXPONENT( 0x0f ), \
|
||||
HID_UNIT ( HID_VARIABLE | HID_NONLINEAR ), \
|
||||
HID_PHYSICAL_MIN( 0 ), \
|
||||
HID_PHYSICAL_MAX( 0 ), \
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_X ), \
|
||||
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ), \
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_Y ), \
|
||||
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ), \
|
||||
HID_COLLECTION_END , \
|
||||
HID_COLLECTION_END \
|
||||
|
||||
// Absolute Mouse Report Descriptor Template
|
||||
#define TUD_HID_REPORT_DESC_ABSMOUSE(...) \
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
|
||||
|
||||
111
src/class/midi/README_midi_host.md
Normal file
111
src/class/midi/README_midi_host.md
Normal file
@@ -0,0 +1,111 @@
|
||||
# MIDI HOST DRIVER
|
||||
This README file contains the design notes and limitations of the
|
||||
MIDI host driver.
|
||||
|
||||
# MAXIMUM NUMBER OF MIDI DEVICES ATTACHED TO HOST
|
||||
In this version of the driver, only one MIDI device is supported. This
|
||||
constraint may change in the future.
|
||||
|
||||
# MAXIMUM NUMBER OF ENDPOINTS
|
||||
Although the USB MIDI 1.0 Class specification allows an arbitrary number
|
||||
of endpoints, this driver supports at most one USB BULK DATA IN endpoint
|
||||
and one USB BULK DATA OUT endpoint. Each endpoint can support up to 16
|
||||
virtual cables. If a device has multiple IN endpoints or multiple OUT
|
||||
endpoints, it will fail to enumerate.
|
||||
|
||||
Most USB MIDI devices contain both an IN endpoint and an OUT endpoint,
|
||||
but not all do. For example, some USB pedals only support an OUT endpoint.
|
||||
This driver allows that.
|
||||
|
||||
# PUBLIC API
|
||||
Applications interact with this driver via 8-bit buffers of MIDI messages
|
||||
formed using the rules for sending bytes on a 5-pin DIN cable per the
|
||||
original MIDI 1.0 specification.
|
||||
|
||||
To send a message to a device, the Host application composes a sequence
|
||||
of status and data bytes in a byte array and calls the API function.
|
||||
The arguments of the function are a pointer to the byte array, the number
|
||||
of bytes in the array, and the target virtual cable number 0-15.
|
||||
|
||||
When the host driver receives a message from the device, the host driver
|
||||
will call a callback function that the host application registers. This
|
||||
callback function contains a pointer to a message buffer, a message length,
|
||||
and the virtual cable number of the message buffer. One complete bulk IN
|
||||
endpoint transfer might contain multiple messages targeted to different
|
||||
virtual cables.
|
||||
|
||||
# SUBCLASS AUDIO CONTROL
|
||||
A MIDI device does not absolutely need to have an Audio Control Interface,
|
||||
unless it adheres to the USB Audio Class 2 spec, but many devices
|
||||
have them even if the devices do not have an audio streaming interface.
|
||||
Because this driver does not support audio streaming, the descriptor parser
|
||||
will skip past any audio control interface and audio streaming interface
|
||||
and open only the MIDI interface.
|
||||
|
||||
An audio streaming host driver can use this driver by passing a pointer
|
||||
to the MIDI interface descriptor that is found after the audio streaming
|
||||
interface to the midih_open() function. That is, an audio streaming host
|
||||
driver would parse the audio control interface descriptor and then the
|
||||
audio streaming interface and endpoint descriptors. When the next descriptor
|
||||
pointer points to a MIDI interface descriptor, call midih_open() with that
|
||||
descriptor pointer.
|
||||
|
||||
# CLASS SPECIFIC INTERFACE AND REQUESTS
|
||||
The host driver does not make use of the information in the class specific
|
||||
interface descriptors. In the future, a public API could be created to
|
||||
retrieve the string descriptors for the names of each ELEMENT,
|
||||
IN JACK and OUT JACK, and how the device describes the connections.
|
||||
|
||||
This driver also does not support class specific requests to control
|
||||
ELEMENT items, nor does it support non-MIDI Streaming bulk endpoints.
|
||||
|
||||
# MIDI CLASS SPECIFIC DESCRIPTOR TOTAL LENGTH FIELD IGNORED
|
||||
I have observed at least one keyboard by a leading manufacturer that
|
||||
sets the wTotalLength field of the Class-Specific MS Interface Header
|
||||
Descriptor to include the length of the MIDIStreaming Endpoint
|
||||
Descriptors. This is wrong per my reading of the specification.
|
||||
|
||||
# MESSAGE BUFFER DETAILS
|
||||
Messages buffers composed from USB data received on the IN endpoint will never contain
|
||||
running status because USB MIDI 1.0 class does not support that. Messages
|
||||
buffers to be sent to the device on the OUT endpoint may contain running status
|
||||
(the message might come from a UART data stream from a 5-pin DIN MIDI IN
|
||||
cable on the host, for example). The driver may in the future correctly compose
|
||||
4-byte USB MIDI Class packets using the running status if need be. However,
|
||||
it does not currently do that. Also, use of running status is not a good idea
|
||||
overall because a single byte error can really mess up the data stream with no
|
||||
way to recover until the next non-real time status byte is in the message buffer.
|
||||
|
||||
Message buffers to be sent to the device may contain Real time messages
|
||||
such as MIDI clock. Real time messages may be inserted in the message
|
||||
byte stream between status and data bytes of another message without disrupting
|
||||
the running status. However, because MIDI 1.0 class messages are sent
|
||||
as four byte packets, a real-time message so inserted will be re-ordered
|
||||
to be sent to the device in a new 4-byte packet immediately before the
|
||||
interrupted data stream.
|
||||
|
||||
Real time messages the device sends to the host can only appear between
|
||||
the status byte and data bytes of the message in System Exclusive messages
|
||||
that are longer than 3 bytes.
|
||||
|
||||
# POORLY FORMED USB MIDI DATA PACKETS FROM THE DEVICE
|
||||
Some devices do not properly encode the code index number (CIN) for the
|
||||
MIDI message status byte even though the 3-byte data payload correctly encodes
|
||||
the MIDI message. This driver looks to the byte after the CIN byte to decide
|
||||
how many bytes to place in the message buffer.
|
||||
|
||||
Some devices do not properly encode the virtual cable number. If the virtual
|
||||
cable number in the CIN data byte of the packet is not less than bNumEmbMIDIJack
|
||||
for that endpoint, then the host driver assumes virtual cable 0 and does not
|
||||
report an error.
|
||||
|
||||
Some MIDI devices will always send back exactly wMaxPacketSize bytes on
|
||||
every endpoint even if only one 4-byte packet is required (e.g., NOTE ON).
|
||||
These devices send packets with 4 packet bytes 0. This driver ignores all
|
||||
zero packets without reporting an error.
|
||||
|
||||
# ENUMERATION FAILURES
|
||||
The host may fail to enumerate a device if it has too many endpoints, if it has
|
||||
if it has a Standard MS Transfer Bulk Data Endpoint Descriptor (not supported),
|
||||
if it has a poorly formed descriptor, or if the descriptor is too long for
|
||||
the host to read the whole thing.
|
||||
@@ -24,13 +24,8 @@
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
/** \ingroup group_class
|
||||
* \defgroup ClassDriver_CDC Communication Device Class (CDC)
|
||||
* Currently only Abstract Control Model subclass is supported
|
||||
* @{ */
|
||||
|
||||
#ifndef _TUSB_MIDI_H__
|
||||
#define _TUSB_MIDI_H__
|
||||
#ifndef TUSB_MIDI_H_
|
||||
#define TUSB_MIDI_H_
|
||||
|
||||
#include "common/tusb_common.h"
|
||||
|
||||
@@ -39,30 +34,31 @@
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Class Specific Descriptor
|
||||
// Constants
|
||||
//--------------------------------------------------------------------+
|
||||
enum {
|
||||
MIDI_VERSION_1_0 = 0x0100,
|
||||
MIDI_VERSION_2_0 = 0x0200,
|
||||
};
|
||||
|
||||
typedef enum
|
||||
{
|
||||
typedef enum {
|
||||
MIDI_CS_INTERFACE_HEADER = 0x01,
|
||||
MIDI_CS_INTERFACE_IN_JACK = 0x02,
|
||||
MIDI_CS_INTERFACE_OUT_JACK = 0x03,
|
||||
MIDI_CS_INTERFACE_ELEMENT = 0x04,
|
||||
} midi_cs_interface_subtype_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
MIDI_CS_ENDPOINT_GENERAL = 0x01
|
||||
typedef enum {
|
||||
MIDI_CS_ENDPOINT_GENERAL = 0x01,
|
||||
MIDI_CS_ENDPOINT_GENERAL_2_0 = 0x02,
|
||||
} midi_cs_endpoint_subtype_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
typedef enum {
|
||||
MIDI_JACK_EMBEDDED = 0x01,
|
||||
MIDI_JACK_EXTERNAL = 0x02
|
||||
} midi_jack_type_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
typedef enum {
|
||||
MIDI_CIN_MISC = 0,
|
||||
MIDI_CIN_CABLE_EVENT = 1,
|
||||
MIDI_CIN_SYSCOM_2BYTE = 2, // 2 byte system common message e.g MTC, SongSelect
|
||||
@@ -82,8 +78,7 @@ typedef enum
|
||||
} midi_code_index_number_t;
|
||||
|
||||
// MIDI 1.0 status byte
|
||||
enum
|
||||
{
|
||||
enum {
|
||||
//------------- System Exclusive -------------//
|
||||
MIDI_STATUS_SYSEX_START = 0xF0,
|
||||
MIDI_STATUS_SYSEX_END = 0xF7,
|
||||
@@ -106,80 +101,54 @@ enum
|
||||
MIDI_STATUS_SYSREAL_SYSTEM_RESET = 0xFF,
|
||||
};
|
||||
|
||||
enum {
|
||||
MIDI_MAX_DATA_VAL = 0x7F,
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Class Specific Descriptor
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
/// MIDI Interface Header Descriptor
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes.
|
||||
uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
|
||||
uint8_t bDescriptorSubType ; ///< Descriptor SubType
|
||||
uint16_t bcdMSC ; ///< MidiStreaming SubClass release number in Binary-Coded Decimal
|
||||
uint16_t wTotalLength ;
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength; ///< Size of this descriptor in bytes.
|
||||
uint8_t bDescriptorType; ///< must be TUSB_DESC_CS_INTERFACE
|
||||
uint8_t bDescriptorSubType;///< Descriptor SubType
|
||||
uint16_t bcdMSC; ///< MidiStreaming SubClass release number in Binary-Coded Decimal
|
||||
uint16_t wTotalLength;
|
||||
} midi_desc_header_t;
|
||||
TU_VERIFY_STATIC(sizeof(midi_desc_header_t) == 7, "size is not correct");
|
||||
|
||||
/// MIDI In Jack Descriptor
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes.
|
||||
uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
|
||||
uint8_t bDescriptorSubType ; ///< Descriptor SubType
|
||||
uint8_t bJackType ; ///< Embedded or External
|
||||
uint8_t bJackID ; ///< Unique ID for MIDI IN Jack
|
||||
uint8_t iJack ; ///< string descriptor
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength; ///< Size of this descriptor in bytes.
|
||||
uint8_t bDescriptorType; ///< Descriptor Type, must be Class-Specific
|
||||
uint8_t bDescriptorSubType;///< Descriptor SubType
|
||||
uint8_t bJackType; ///< Embedded or External
|
||||
uint8_t bJackID; ///< Unique ID for MIDI IN Jack
|
||||
uint8_t iJack; ///< string descriptor
|
||||
} midi_desc_in_jack_t;
|
||||
TU_VERIFY_STATIC(sizeof(midi_desc_in_jack_t) == 6, "size is not correct");
|
||||
|
||||
|
||||
/// MIDI Out Jack Descriptor with single pin
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes.
|
||||
uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
|
||||
uint8_t bDescriptorSubType ; ///< Descriptor SubType
|
||||
uint8_t bJackType ; ///< Embedded or External
|
||||
uint8_t bJackID ; ///< Unique ID for MIDI IN Jack
|
||||
uint8_t bNrInputPins;
|
||||
|
||||
uint8_t baSourceID;
|
||||
uint8_t baSourcePin;
|
||||
|
||||
uint8_t iJack ; ///< string descriptor
|
||||
} midi_desc_out_jack_t ;
|
||||
|
||||
/// MIDI Out Jack Descriptor with multiple pins
|
||||
/// MIDI Out Jack Descriptor with multiple input pins
|
||||
#define midi_desc_out_jack_n_t(input_num) \
|
||||
struct TU_ATTR_PACKED { \
|
||||
uint8_t bLength ; \
|
||||
uint8_t bDescriptorType ; \
|
||||
uint8_t bDescriptorSubType ; \
|
||||
uint8_t bJackType ; \
|
||||
uint8_t bJackID ; \
|
||||
uint8_t bNrInputPins ; \
|
||||
struct TU_ATTR_PACKED { \
|
||||
uint8_t baSourceID; \
|
||||
uint8_t baSourcePin; \
|
||||
} pins[input_num]; \
|
||||
uint8_t iJack ; \
|
||||
struct TU_ATTR_PACKED { \
|
||||
uint8_t bLength; \
|
||||
uint8_t bDescriptorType; \
|
||||
uint8_t bDescriptorSubType; \
|
||||
uint8_t bJackType; \
|
||||
uint8_t bJackID; \
|
||||
uint8_t bNrInputPins; \
|
||||
struct TU_ATTR_PACKED { \
|
||||
uint8_t baSourceID; \
|
||||
uint8_t baSourcePin; \
|
||||
} input[input_num]; \
|
||||
uint8_t iJack; \
|
||||
}
|
||||
|
||||
/// MIDI Element Descriptor
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes.
|
||||
uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
|
||||
uint8_t bDescriptorSubType ; ///< Descriptor SubType
|
||||
uint8_t bElementID;
|
||||
|
||||
uint8_t bNrInputPins;
|
||||
uint8_t baSourceID;
|
||||
uint8_t baSourcePin;
|
||||
|
||||
uint8_t bNrOutputPins;
|
||||
uint8_t bInTerminalLink;
|
||||
uint8_t bOutTerminalLink;
|
||||
uint8_t bElCapsSize;
|
||||
|
||||
uint16_t bmElementCaps;
|
||||
uint8_t iElement;
|
||||
} midi_desc_element_t;
|
||||
typedef midi_desc_out_jack_n_t(1) midi_desc_out_jack_1in_t; // 1 input
|
||||
typedef midi_desc_out_jack_1in_t midi_desc_out_jack_t; // backward compatible
|
||||
TU_VERIFY_STATIC(sizeof(midi_desc_out_jack_1in_t) == 7 + 2 * 1, "size is not correct");
|
||||
|
||||
/// MIDI Element Descriptor with multiple pins
|
||||
#define midi_desc_element_n_t(input_num) \
|
||||
@@ -201,12 +170,32 @@ typedef struct TU_ATTR_PACKED
|
||||
uint8_t iElement; \
|
||||
}
|
||||
|
||||
/** @} */
|
||||
// This descriptor follows the standard bulk data endpoint descriptor
|
||||
#define midi_desc_cs_endpoint_n_t(jack_num) \
|
||||
struct TU_ATTR_PACKED { \
|
||||
uint8_t bLength; \
|
||||
uint8_t bDescriptorType; \
|
||||
uint8_t bDescriptorSubType; \
|
||||
uint8_t bNumEmbMIDIJack; \
|
||||
uint8_t baAssocJackID[jack_num]; \
|
||||
}
|
||||
|
||||
typedef midi_desc_cs_endpoint_n_t() midi_desc_cs_endpoint_t; // empty/flexible jack list
|
||||
typedef midi_desc_cs_endpoint_n_t(1) midi_desc_cs_endpoint_1jack_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(midi_desc_cs_endpoint_1jack_t) == 4+1, "size is not correct");
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// For Internal Driver Use
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct {
|
||||
uint8_t buffer[4];
|
||||
uint8_t index;
|
||||
uint8_t total;
|
||||
} midi_driver_stream_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
|
||||
@@ -39,13 +39,6 @@
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
typedef struct {
|
||||
uint8_t buffer[4];
|
||||
uint8_t index;
|
||||
uint8_t total;
|
||||
} midid_stream_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t itf_num;
|
||||
uint8_t ep_in;
|
||||
@@ -54,8 +47,8 @@ typedef struct {
|
||||
// For Stream read()/write() API
|
||||
// Messages are always 4 bytes long, queue them for reading and writing so the
|
||||
// callers can use the Stream interface with single-byte read/write calls.
|
||||
midid_stream_t stream_write;
|
||||
midid_stream_t stream_read;
|
||||
midi_driver_stream_t stream_write;
|
||||
midi_driver_stream_t stream_read;
|
||||
|
||||
/*------------- From this point, data is not cleared by bus reset -------------*/
|
||||
// FIFO
|
||||
@@ -122,7 +115,7 @@ uint32_t tud_midi_n_available(uint8_t itf, uint8_t cable_num)
|
||||
(void) cable_num;
|
||||
|
||||
midid_interface_t* midi = &_midid_itf[itf];
|
||||
const midid_stream_t* stream = &midi->stream_read;
|
||||
const midi_driver_stream_t* stream = &midi->stream_read;
|
||||
|
||||
// when using with packet API stream total & index are both zero
|
||||
return tu_fifo_count(&midi->rx_ff) + (uint8_t) (stream->total - stream->index);
|
||||
@@ -136,7 +129,7 @@ uint32_t tud_midi_n_stream_read(uint8_t itf, uint8_t cable_num, void* buffer, ui
|
||||
uint8_t* buf8 = (uint8_t*) buffer;
|
||||
|
||||
midid_interface_t* midi = &_midid_itf[itf];
|
||||
midid_stream_t* stream = &midi->stream_read;
|
||||
midi_driver_stream_t* stream = &midi->stream_read;
|
||||
|
||||
uint32_t total_read = 0;
|
||||
while( bufsize )
|
||||
@@ -241,7 +234,7 @@ uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, const uint8_t*
|
||||
midid_interface_t* midi = &_midid_itf[itf];
|
||||
TU_VERIFY(midi->ep_in, 0);
|
||||
|
||||
midid_stream_t* stream = &midi->stream_write;
|
||||
midi_driver_stream_t* stream = &midi->stream_write;
|
||||
|
||||
uint32_t i = 0;
|
||||
while ( (i < bufsize) && (tu_fifo_remaining(&midi->tx_ff) >= 4) )
|
||||
@@ -429,21 +422,21 @@ void midid_reset(uint8_t rhport)
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t midid_open(uint8_t rhport, const tusb_desc_interface_t* desc_itf, uint16_t max_len)
|
||||
{
|
||||
// 1st Interface is Audio Control v1
|
||||
TU_VERIFY(TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass &&
|
||||
AUDIO_SUBCLASS_CONTROL == desc_itf->bInterfaceSubClass &&
|
||||
AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_itf->bInterfaceProtocol, 0);
|
||||
uint16_t midid_open(uint8_t rhport, const tusb_desc_interface_t* desc_itf, uint16_t max_len) {
|
||||
uint16_t drv_len = 0;
|
||||
uint8_t const * p_desc = (uint8_t const *)desc_itf;
|
||||
|
||||
uint16_t drv_len = tu_desc_len(desc_itf);
|
||||
const uint8_t* p_desc = tu_desc_next(desc_itf);
|
||||
|
||||
// Skip Class Specific descriptors
|
||||
while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len )
|
||||
{
|
||||
drv_len += tu_desc_len(p_desc);
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
// 1st Interface is Audio Control v1 (optional)
|
||||
if (TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass &&
|
||||
AUDIO_SUBCLASS_CONTROL == desc_itf->bInterfaceSubClass &&
|
||||
AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_itf->bInterfaceProtocol) {
|
||||
drv_len = tu_desc_len(desc_itf);
|
||||
p_desc = tu_desc_next(desc_itf);
|
||||
// Skip Class Specific descriptors
|
||||
while (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len) {
|
||||
drv_len += tu_desc_len(p_desc);
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
}
|
||||
}
|
||||
|
||||
// 2nd Interface is MIDI Streaming
|
||||
|
||||
622
src/class/midi/midi_host.c
Normal file
622
src/class/midi/midi_host.c
Normal file
@@ -0,0 +1,622 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "tusb_option.h"
|
||||
|
||||
#if (CFG_TUH_ENABLED && CFG_TUH_MIDI)
|
||||
|
||||
#include "host/usbh.h"
|
||||
#include "host/usbh_pvt.h"
|
||||
|
||||
#include "midi_host.h"
|
||||
|
||||
// Level where CFG_TUSB_DEBUG must be at least for this driver is logged
|
||||
#ifndef CFG_TUH_MIDI_LOG_LEVEL
|
||||
#define CFG_TUH_MIDI_LOG_LEVEL CFG_TUH_LOG_LEVEL
|
||||
#endif
|
||||
|
||||
#define TU_LOG_DRV(...) TU_LOG(CFG_TUH_MIDI_LOG_LEVEL, __VA_ARGS__)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Weak stubs: invoked if no strong implementation is available
|
||||
//--------------------------------------------------------------------+
|
||||
TU_ATTR_WEAK void tuh_midi_descriptor_cb(uint8_t idx, const tuh_midi_descriptor_cb_t * desc_cb_data) { (void) idx; (void) desc_cb_data; }
|
||||
TU_ATTR_WEAK void tuh_midi_mount_cb(uint8_t idx, const tuh_midi_mount_cb_t* mount_cb_data) { (void) idx; (void) mount_cb_data; }
|
||||
TU_ATTR_WEAK void tuh_midi_umount_cb(uint8_t idx) { (void) idx; }
|
||||
TU_ATTR_WEAK void tuh_midi_rx_cb(uint8_t idx, uint32_t xferred_bytes) { (void) idx; (void) xferred_bytes; }
|
||||
TU_ATTR_WEAK void tuh_midi_tx_cb(uint8_t idx, uint32_t xferred_bytes) { (void) idx; (void) xferred_bytes; }
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
typedef struct {
|
||||
uint8_t daddr;
|
||||
uint8_t bInterfaceNumber; // interface number of MIDI streaming
|
||||
uint8_t iInterface;
|
||||
uint8_t itf_count; // number of interface including Audio Control + MIDI streaming
|
||||
|
||||
uint8_t ep_in; // IN endpoint address
|
||||
uint8_t ep_out; // OUT endpoint address
|
||||
|
||||
uint8_t rx_cable_count; // IN endpoint CS descriptor bNumEmbMIDIJack value
|
||||
uint8_t tx_cable_count; // OUT endpoint CS descriptor bNumEmbMIDIJack value
|
||||
|
||||
#if CFG_TUH_MIDI_STREAM_API
|
||||
// For Stream read()/write() API
|
||||
// Messages are always 4 bytes long, queue them for reading and writing so the
|
||||
// callers can use the Stream interface with single-byte read/write calls.
|
||||
midi_driver_stream_t stream_write;
|
||||
midi_driver_stream_t stream_read;
|
||||
#endif
|
||||
|
||||
// Endpoint stream
|
||||
struct {
|
||||
tu_edpt_stream_t tx;
|
||||
tu_edpt_stream_t rx;
|
||||
|
||||
uint8_t rx_ff_buf[CFG_TUH_MIDI_RX_BUFSIZE];
|
||||
uint8_t tx_ff_buf[CFG_TUH_MIDI_TX_BUFSIZE];
|
||||
} ep_stream;
|
||||
|
||||
bool mounted;
|
||||
}midih_interface_t;
|
||||
|
||||
typedef struct {
|
||||
TUH_EPBUF_DEF(tx, TUH_EPSIZE_BULK_MPS);
|
||||
TUH_EPBUF_DEF(rx, TUH_EPSIZE_BULK_MPS);
|
||||
} midih_epbuf_t;
|
||||
|
||||
static midih_interface_t _midi_host[CFG_TUH_MIDI];
|
||||
CFG_TUH_MEM_SECTION static midih_epbuf_t _midi_epbuf[CFG_TUH_MIDI];
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Helper
|
||||
//--------------------------------------------------------------------+
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t find_new_midi_index(void) {
|
||||
for (uint8_t idx = 0; idx < CFG_TUH_MIDI; idx++) {
|
||||
if (_midi_host[idx].daddr == 0) {
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
return TUSB_INDEX_INVALID_8;
|
||||
}
|
||||
|
||||
static inline uint8_t get_idx_by_ep_addr(uint8_t daddr, uint8_t ep_addr) {
|
||||
for (uint8_t idx = 0; idx < CFG_TUH_MIDI; idx++) {
|
||||
const midih_interface_t *p_midi = &_midi_host[idx];
|
||||
if ((p_midi->daddr == daddr) &&
|
||||
(ep_addr == p_midi->ep_stream.rx.ep_addr || ep_addr == p_midi->ep_stream.tx.ep_addr)) {
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
return TUSB_INDEX_INVALID_8;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// USBH API
|
||||
//--------------------------------------------------------------------+
|
||||
bool midih_init(void) {
|
||||
tu_memclr(&_midi_host, sizeof(_midi_host));
|
||||
for (int inst = 0; inst < CFG_TUH_MIDI; inst++) {
|
||||
midih_interface_t *p_midi_host = &_midi_host[inst];
|
||||
tu_edpt_stream_init(&p_midi_host->ep_stream.rx, true, false, false,
|
||||
p_midi_host->ep_stream.rx_ff_buf, CFG_TUH_MIDI_RX_BUFSIZE, _midi_epbuf->rx, TUH_EPSIZE_BULK_MPS);
|
||||
tu_edpt_stream_init(&p_midi_host->ep_stream.tx, true, true, false,
|
||||
p_midi_host->ep_stream.tx_ff_buf, CFG_TUH_MIDI_TX_BUFSIZE, _midi_epbuf->tx, TUH_EPSIZE_BULK_MPS);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool midih_deinit(void) {
|
||||
for (size_t i = 0; i < CFG_TUH_MIDI; i++) {
|
||||
midih_interface_t* p_midi = &_midi_host[i];
|
||||
tu_edpt_stream_deinit(&p_midi->ep_stream.rx);
|
||||
tu_edpt_stream_deinit(&p_midi->ep_stream.tx);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void midih_close(uint8_t daddr) {
|
||||
for (uint8_t idx = 0; idx < CFG_TUH_MIDI; idx++) {
|
||||
midih_interface_t* p_midi = &_midi_host[idx];
|
||||
if (p_midi->daddr == daddr) {
|
||||
TU_LOG_DRV(" MIDI close addr = %u index = %u\r\n", daddr, idx);
|
||||
tuh_midi_umount_cb(idx);
|
||||
|
||||
p_midi->ep_in = 0;
|
||||
p_midi->ep_out = 0;
|
||||
p_midi->bInterfaceNumber = 0;
|
||||
p_midi->rx_cable_count = 0;
|
||||
p_midi->tx_cable_count = 0;
|
||||
p_midi->daddr = 0;
|
||||
p_midi->mounted = false;
|
||||
#if CFG_TUH_MIDI_STREAM_API
|
||||
tu_memclr(&p_midi->stream_read, sizeof(p_midi->stream_read));
|
||||
tu_memclr(&p_midi->stream_write, sizeof(p_midi->stream_write));
|
||||
#endif
|
||||
tu_edpt_stream_close(&p_midi->ep_stream.rx);
|
||||
tu_edpt_stream_close(&p_midi->ep_stream.tx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool midih_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
|
||||
(void) result;
|
||||
const uint8_t idx = get_idx_by_ep_addr(dev_addr, ep_addr);
|
||||
TU_VERIFY(idx < CFG_TUH_MIDI);
|
||||
midih_interface_t *p_midi = &_midi_host[idx];
|
||||
|
||||
if (ep_addr == p_midi->ep_stream.rx.ep_addr) {
|
||||
// receive new data, put it into FIFO and invoke callback if available
|
||||
// Note: some devices send back all zero packets even if there is no data ready
|
||||
if (xferred_bytes && !tu_mem_is_zero(p_midi->ep_stream.rx.ep_buf, xferred_bytes)) {
|
||||
tu_edpt_stream_read_xfer_complete(&p_midi->ep_stream.rx, xferred_bytes);
|
||||
tuh_midi_rx_cb(idx, xferred_bytes);
|
||||
}
|
||||
|
||||
tu_edpt_stream_read_xfer(dev_addr, &p_midi->ep_stream.rx); // prepare for next transfer
|
||||
} else if (ep_addr == p_midi->ep_stream.tx.ep_addr) {
|
||||
tuh_midi_tx_cb(idx, xferred_bytes);
|
||||
|
||||
if (0 == tu_edpt_stream_write_xfer(dev_addr, &p_midi->ep_stream.tx)) {
|
||||
// If there is no data left, a ZLP should be sent if
|
||||
// xferred_bytes is multiple of EP size and not zero
|
||||
tu_edpt_stream_write_zlp_if_needed(dev_addr, &p_midi->ep_stream.tx, xferred_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Enumeration
|
||||
//--------------------------------------------------------------------+
|
||||
bool midih_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len) {
|
||||
(void) rhport;
|
||||
|
||||
TU_VERIFY(TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass);
|
||||
const uint8_t *p_end = ((const uint8_t *) desc_itf) + max_len;
|
||||
const uint8_t *p_desc = (const uint8_t *) desc_itf;
|
||||
|
||||
const uint8_t idx = find_new_midi_index();
|
||||
TU_VERIFY(idx < CFG_TUH_MIDI);
|
||||
midih_interface_t *p_midi = &_midi_host[idx];
|
||||
p_midi->itf_count = 0;
|
||||
|
||||
tuh_midi_descriptor_cb_t desc_cb = { 0 };
|
||||
desc_cb.jack_num = 0;
|
||||
|
||||
// There can be just a MIDI or an Audio + MIDI interface
|
||||
// If there is Audio Control Interface + Audio Header descriptor, skip it
|
||||
if (AUDIO_SUBCLASS_CONTROL == desc_itf->bInterfaceSubClass) {
|
||||
TU_VERIFY(max_len > 2*sizeof(tusb_desc_interface_t) + sizeof(audio_desc_cs_ac_interface_t));
|
||||
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
TU_VERIFY(tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE &&
|
||||
tu_desc_subtype(p_desc) == AUDIO_CS_AC_INTERFACE_HEADER);
|
||||
desc_cb.desc_audio_control = desc_itf;
|
||||
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
desc_itf = (const tusb_desc_interface_t *)p_desc;
|
||||
TU_VERIFY(TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass);
|
||||
p_midi->itf_count = 1;
|
||||
}
|
||||
TU_VERIFY(AUDIO_SUBCLASS_MIDI_STREAMING == desc_itf->bInterfaceSubClass);
|
||||
|
||||
TU_LOG_DRV("MIDI opening Interface %u (addr = %u)\r\n", desc_itf->bInterfaceNumber, dev_addr);
|
||||
p_midi->bInterfaceNumber = desc_itf->bInterfaceNumber;
|
||||
p_midi->iInterface = desc_itf->iInterface;
|
||||
p_midi->itf_count++;
|
||||
desc_cb.desc_midi = desc_itf;
|
||||
|
||||
p_desc = tu_desc_next(p_desc); // next to CS Header
|
||||
|
||||
bool found_new_interface = false;
|
||||
while ((p_desc < p_end) && (tu_desc_next(p_desc) <= p_end) && !found_new_interface) {
|
||||
switch (tu_desc_type(p_desc)) {
|
||||
case TUSB_DESC_INTERFACE:
|
||||
found_new_interface = true;
|
||||
break;
|
||||
|
||||
case TUSB_DESC_CS_INTERFACE:
|
||||
switch (tu_desc_subtype(p_desc)) {
|
||||
case MIDI_CS_INTERFACE_HEADER:
|
||||
TU_LOG_DRV(" Interface Header descriptor\r\n");
|
||||
desc_cb.desc_header = p_desc;
|
||||
break;
|
||||
|
||||
case MIDI_CS_INTERFACE_IN_JACK:
|
||||
case MIDI_CS_INTERFACE_OUT_JACK: {
|
||||
TU_LOG_DRV(" Jack %s %s descriptor \r\n",
|
||||
tu_desc_subtype(p_desc) == MIDI_CS_INTERFACE_IN_JACK ? "IN" : "OUT",
|
||||
p_desc[3] == MIDI_JACK_EXTERNAL ? "External" : "Embedded");
|
||||
if (desc_cb.jack_num < TU_ARRAY_SIZE(desc_cb.desc_jack)) {
|
||||
desc_cb.desc_jack[desc_cb.jack_num++] = p_desc;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MIDI_CS_INTERFACE_ELEMENT:
|
||||
TU_LOG_DRV(" Element descriptor\r\n");
|
||||
desc_cb.desc_element = p_desc;
|
||||
break;
|
||||
|
||||
default:
|
||||
TU_LOG_DRV(" Unknown CS Interface sub-type %u\r\n", tu_desc_subtype(p_desc));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case TUSB_DESC_ENDPOINT: {
|
||||
const tusb_desc_endpoint_t *p_ep = (const tusb_desc_endpoint_t *) p_desc;
|
||||
p_desc = tu_desc_next(p_desc); // next to CS endpoint
|
||||
TU_VERIFY(p_desc < p_end && tu_desc_next(p_desc) <= p_end);
|
||||
const midi_desc_cs_endpoint_t *p_csep = (const midi_desc_cs_endpoint_t *) p_desc;
|
||||
|
||||
TU_LOG_DRV(" Endpoint and CS_Endpoint descriptor %02x\r\n", p_ep->bEndpointAddress);
|
||||
if (tu_edpt_dir(p_ep->bEndpointAddress) == TUSB_DIR_OUT) {
|
||||
p_midi->ep_out = p_ep->bEndpointAddress;
|
||||
p_midi->tx_cable_count = p_csep->bNumEmbMIDIJack;
|
||||
desc_cb.desc_epout = p_ep;
|
||||
|
||||
TU_ASSERT(tuh_edpt_open(dev_addr, p_ep));
|
||||
tu_edpt_stream_open(&p_midi->ep_stream.tx, p_ep);
|
||||
} else {
|
||||
p_midi->ep_in = p_ep->bEndpointAddress;
|
||||
p_midi->rx_cable_count = p_csep->bNumEmbMIDIJack;
|
||||
desc_cb.desc_epin = p_ep;
|
||||
|
||||
TU_ASSERT(tuh_edpt_open(dev_addr, p_ep));
|
||||
tu_edpt_stream_open(&p_midi->ep_stream.rx, p_ep);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: break; // skip unknown descriptor
|
||||
}
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
}
|
||||
desc_cb.desc_midi_total_len = (uint16_t) ((uintptr_t)p_desc - (uintptr_t) desc_itf);
|
||||
|
||||
p_midi->daddr = dev_addr;
|
||||
tuh_midi_descriptor_cb(idx, &desc_cb);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool midih_set_config(uint8_t dev_addr, uint8_t itf_num) {
|
||||
uint8_t idx = tuh_midi_itf_get_index(dev_addr, itf_num);
|
||||
TU_ASSERT(idx < CFG_TUH_MIDI);
|
||||
midih_interface_t *p_midi = &_midi_host[idx];
|
||||
p_midi->mounted = true;
|
||||
|
||||
const tuh_midi_mount_cb_t mount_cb_data = {
|
||||
.daddr = dev_addr,
|
||||
.bInterfaceNumber = itf_num,
|
||||
.rx_cable_count = p_midi->rx_cable_count,
|
||||
.tx_cable_count = p_midi->tx_cable_count,
|
||||
};
|
||||
tuh_midi_mount_cb(idx, &mount_cb_data);
|
||||
|
||||
tu_edpt_stream_read_xfer(dev_addr, &p_midi->ep_stream.rx); // prepare for incoming data
|
||||
|
||||
// No special config things to do for MIDI
|
||||
usbh_driver_set_config_complete(dev_addr, p_midi->bInterfaceNumber);
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// API
|
||||
//--------------------------------------------------------------------+
|
||||
bool tuh_midi_mounted(uint8_t idx) {
|
||||
TU_VERIFY(idx < CFG_TUH_MIDI);
|
||||
midih_interface_t *p_midi = &_midi_host[idx];
|
||||
return p_midi->mounted;
|
||||
}
|
||||
|
||||
uint8_t tuh_midi_itf_get_index(uint8_t daddr, uint8_t itf_num) {
|
||||
for (uint8_t idx = 0; idx < CFG_TUH_MIDI; idx++) {
|
||||
const midih_interface_t *p_midi = &_midi_host[idx];
|
||||
if (p_midi->daddr == daddr &&
|
||||
(p_midi->bInterfaceNumber == itf_num ||
|
||||
p_midi->bInterfaceNumber == (uint8_t) (itf_num + p_midi->itf_count - 1))) {
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
return TUSB_INDEX_INVALID_8;
|
||||
}
|
||||
|
||||
bool tuh_midi_itf_get_info(uint8_t idx, tuh_itf_info_t* info) {
|
||||
midih_interface_t* p_midi = &_midi_host[idx];
|
||||
TU_VERIFY(p_midi && info);
|
||||
|
||||
info->daddr = p_midi->daddr;
|
||||
|
||||
// re-construct descriptor
|
||||
tusb_desc_interface_t* desc = &info->desc;
|
||||
desc->bLength = sizeof(tusb_desc_interface_t);
|
||||
desc->bDescriptorType = TUSB_DESC_INTERFACE;
|
||||
|
||||
desc->bInterfaceNumber = p_midi->bInterfaceNumber;
|
||||
desc->bAlternateSetting = 0;
|
||||
desc->bNumEndpoints = (uint8_t)((p_midi->ep_in != 0 ? 1:0) + (p_midi->ep_out != 0 ? 1:0));
|
||||
desc->bInterfaceClass = TUSB_CLASS_AUDIO;
|
||||
desc->bInterfaceSubClass = AUDIO_SUBCLASS_MIDI_STREAMING;
|
||||
desc->bInterfaceProtocol = 0;
|
||||
desc->iInterface = p_midi->iInterface;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t tuh_midi_get_tx_cable_count (uint8_t idx) {
|
||||
TU_VERIFY(idx < CFG_TUH_MIDI);
|
||||
midih_interface_t *p_midi = &_midi_host[idx];
|
||||
TU_VERIFY(p_midi->ep_stream.tx.ep_addr != 0, 0);
|
||||
return p_midi->tx_cable_count;
|
||||
}
|
||||
|
||||
uint8_t tuh_midi_get_rx_cable_count (uint8_t idx) {
|
||||
TU_VERIFY(idx < CFG_TUH_MIDI);
|
||||
midih_interface_t *p_midi = &_midi_host[idx];
|
||||
TU_VERIFY(p_midi->ep_stream.rx.ep_addr != 0, 0);
|
||||
return p_midi->rx_cable_count;
|
||||
}
|
||||
|
||||
uint32_t tuh_midi_read_available(uint8_t idx) {
|
||||
TU_VERIFY(idx < CFG_TUH_MIDI);
|
||||
midih_interface_t *p_midi = &_midi_host[idx];
|
||||
return tu_edpt_stream_read_available(&p_midi->ep_stream.rx);
|
||||
}
|
||||
|
||||
uint32_t tuh_midi_write_flush(uint8_t idx) {
|
||||
TU_VERIFY(idx < CFG_TUH_MIDI);
|
||||
midih_interface_t *p_midi = &_midi_host[idx];
|
||||
return tu_edpt_stream_write_xfer(p_midi->daddr, &p_midi->ep_stream.tx);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Packet API
|
||||
//--------------------------------------------------------------------+
|
||||
uint32_t tuh_midi_packet_read_n(uint8_t idx, uint8_t* buffer, uint32_t bufsize) {
|
||||
TU_VERIFY(idx < CFG_TUH_MIDI && buffer && bufsize > 0, 0);
|
||||
midih_interface_t *p_midi = &_midi_host[idx];
|
||||
|
||||
uint32_t count4 = tu_min32(bufsize, tu_edpt_stream_read_available(&p_midi->ep_stream.rx));
|
||||
count4 = tu_align4(count4); // round down to multiple of 4
|
||||
TU_VERIFY(count4 > 0, 0);
|
||||
return tu_edpt_stream_read(p_midi->daddr, &p_midi->ep_stream.rx, buffer, count4);
|
||||
}
|
||||
|
||||
uint32_t tuh_midi_packet_write_n(uint8_t idx, const uint8_t* buffer, uint32_t bufsize) {
|
||||
TU_VERIFY(idx < CFG_TUH_MIDI && buffer && bufsize > 0, 0);
|
||||
midih_interface_t *p_midi = &_midi_host[idx];
|
||||
|
||||
const uint32_t bufsize4 = tu_align4(bufsize);
|
||||
TU_VERIFY(bufsize4 > 0, 0);
|
||||
return tu_edpt_stream_write(p_midi->daddr, &p_midi->ep_stream.tx, buffer, bufsize4);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Stream API
|
||||
//--------------------------------------------------------------------+
|
||||
#if CFG_TUH_MIDI_STREAM_API
|
||||
uint32_t tuh_midi_stream_write(uint8_t idx, uint8_t cable_num, uint8_t const *buffer, uint32_t bufsize) {
|
||||
TU_VERIFY(idx < CFG_TUH_MIDI && buffer && bufsize > 0);
|
||||
midih_interface_t *p_midi = &_midi_host[idx];
|
||||
TU_VERIFY(cable_num < p_midi->tx_cable_count);
|
||||
midi_driver_stream_t *stream = &p_midi->stream_write;
|
||||
|
||||
uint32_t byte_count = 0;
|
||||
while ((byte_count < bufsize) && (tu_edpt_stream_write_available(p_midi->daddr, &p_midi->ep_stream.tx) >= 4)) {
|
||||
uint8_t const data = buffer[byte_count];
|
||||
byte_count++;
|
||||
if (data >= MIDI_STATUS_SYSREAL_TIMING_CLOCK) {
|
||||
// real-time messages need to be sent right away
|
||||
midi_driver_stream_t streamrt;
|
||||
streamrt.buffer[0] = MIDI_CIN_SYSEX_END_1BYTE;
|
||||
streamrt.buffer[1] = data;
|
||||
streamrt.index = 2;
|
||||
streamrt.total = 2;
|
||||
uint32_t const count = tu_edpt_stream_write(p_midi->daddr, &p_midi->ep_stream.tx, streamrt.buffer, 4);
|
||||
TU_ASSERT(count == 4, byte_count); // Check FIFO overflown, since we already check fifo remaining. It is probably race condition
|
||||
} else if (stream->index == 0) {
|
||||
//------------- New event packet -------------//
|
||||
|
||||
uint8_t const msg = data >> 4;
|
||||
|
||||
stream->index = 2;
|
||||
stream->buffer[1] = data;
|
||||
|
||||
// Check to see if we're still in a SysEx transmit.
|
||||
if (stream->buffer[0] == MIDI_CIN_SYSEX_START) {
|
||||
if (data == MIDI_STATUS_SYSEX_END) {
|
||||
stream->buffer[0] = MIDI_CIN_SYSEX_END_1BYTE;
|
||||
stream->total = 2;
|
||||
} else {
|
||||
stream->total = 4;
|
||||
}
|
||||
} else if ((msg >= 0x8 && msg <= 0xB) || msg == 0xE) {
|
||||
// Channel Voice Messages
|
||||
stream->buffer[0] = (uint8_t) ((cable_num << 4) | msg);
|
||||
stream->total = 4;
|
||||
} else if (msg == 0xC || msg == 0xD) {
|
||||
// Channel Voice Messages, two-byte variants (Program Change and Channel Pressure)
|
||||
stream->buffer[0] = (uint8_t) ((cable_num << 4) | msg);
|
||||
stream->total = 3;
|
||||
} else if (msg == 0xf) {
|
||||
// System message
|
||||
if (data == MIDI_STATUS_SYSEX_START) {
|
||||
stream->buffer[0] = MIDI_CIN_SYSEX_START;
|
||||
stream->total = 4;
|
||||
} else if (data == MIDI_STATUS_SYSCOM_TIME_CODE_QUARTER_FRAME || data == MIDI_STATUS_SYSCOM_SONG_SELECT) {
|
||||
stream->buffer[0] = MIDI_CIN_SYSCOM_2BYTE;
|
||||
stream->total = 3;
|
||||
} else if (data == MIDI_STATUS_SYSCOM_SONG_POSITION_POINTER) {
|
||||
stream->buffer[0] = MIDI_CIN_SYSCOM_3BYTE;
|
||||
stream->total = 4;
|
||||
} else {
|
||||
stream->buffer[0] = MIDI_CIN_SYSEX_END_1BYTE;
|
||||
stream->total = 2;
|
||||
}
|
||||
} else {
|
||||
// Pack individual bytes if we don't support packing them into words.
|
||||
stream->buffer[0] = (uint8_t) (cable_num << 4 | 0xf);
|
||||
stream->buffer[2] = 0;
|
||||
stream->buffer[3] = 0;
|
||||
stream->index = 2;
|
||||
stream->total = 2;
|
||||
}
|
||||
} else {
|
||||
//------------- On-going (buffering) packet -------------//
|
||||
TU_ASSERT(stream->index < 4, byte_count);
|
||||
stream->buffer[stream->index] = data;
|
||||
stream->index++;
|
||||
// See if this byte ends a SysEx.
|
||||
if (stream->buffer[0] == MIDI_CIN_SYSEX_START && data == MIDI_STATUS_SYSEX_END) {
|
||||
stream->buffer[0] = MIDI_CIN_SYSEX_START + (stream->index - 1);
|
||||
stream->total = stream->index;
|
||||
}
|
||||
}
|
||||
|
||||
// Send out packet
|
||||
if (stream->index >= 2 && stream->index == stream->total) {
|
||||
// zeroes unused bytes
|
||||
for (uint8_t i = stream->total; i < 4; i++) {
|
||||
stream->buffer[i] = 0;
|
||||
}
|
||||
TU_LOG3_MEM(stream->buffer, 4, 2);
|
||||
|
||||
const uint32_t count = tu_edpt_stream_write(p_midi->daddr, &p_midi->ep_stream.tx, stream->buffer, 4);
|
||||
|
||||
// complete current event packet, reset stream
|
||||
stream->index = 0;
|
||||
stream->total = 0;
|
||||
|
||||
// FIFO overflown, since we already check fifo remaining. It is probably race condition
|
||||
TU_ASSERT(count == 4, byte_count);
|
||||
}
|
||||
}
|
||||
return byte_count;
|
||||
}
|
||||
|
||||
uint32_t tuh_midi_stream_read(uint8_t idx, uint8_t *p_cable_num, uint8_t *p_buffer, uint16_t bufsize) {
|
||||
TU_VERIFY(idx < CFG_TUH_MIDI && p_cable_num && p_buffer && bufsize > 0);
|
||||
midih_interface_t *p_midi = &_midi_host[idx];
|
||||
uint32_t bytes_buffered = 0;
|
||||
uint8_t one_byte;
|
||||
if (!tu_edpt_stream_peek(&p_midi->ep_stream.rx, &one_byte)) {
|
||||
return 0;
|
||||
}
|
||||
*p_cable_num = (one_byte >> 4) & 0xf;
|
||||
uint32_t nread = tu_edpt_stream_read(p_midi->daddr, &p_midi->ep_stream.rx, p_midi->stream_read.buffer, 4);
|
||||
static uint16_t cable_sysex_in_progress;// bit i is set if received MIDI_STATUS_SYSEX_START but not MIDI_STATUS_SYSEX_END
|
||||
while (nread == 4 && bytes_buffered < bufsize) {
|
||||
*p_cable_num = (p_midi->stream_read.buffer[0] >> 4) & 0x0f;
|
||||
uint8_t bytes_to_add_to_stream = 0;
|
||||
if (*p_cable_num < p_midi->rx_cable_count) {
|
||||
// ignore the CIN field; too many devices out there encode this wrong
|
||||
uint8_t status = p_midi->stream_read.buffer[1];
|
||||
uint16_t cable_mask = (uint16_t) (1 << *p_cable_num);
|
||||
if (status <= MIDI_MAX_DATA_VAL || status == MIDI_STATUS_SYSEX_START) {
|
||||
if (status == MIDI_STATUS_SYSEX_START) {
|
||||
cable_sysex_in_progress |= cable_mask;
|
||||
}
|
||||
// only add the packet if a sysex message is in progress
|
||||
if (cable_sysex_in_progress & cable_mask) {
|
||||
++bytes_to_add_to_stream;
|
||||
for (uint8_t i = 2; i < 4; i++) {
|
||||
if (p_midi->stream_read.buffer[i] <= MIDI_MAX_DATA_VAL) {
|
||||
++bytes_to_add_to_stream;
|
||||
} else if (p_midi->stream_read.buffer[i] == MIDI_STATUS_SYSEX_END) {
|
||||
++bytes_to_add_to_stream;
|
||||
cable_sysex_in_progress &= (uint16_t) ~cable_mask;
|
||||
i = 4;// force the loop to exit; I hate break statements in loops
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (status < MIDI_STATUS_SYSEX_START) {
|
||||
// then it is a channel message either three bytes or two
|
||||
uint8_t fake_cin = (status & 0xf0) >> 4;
|
||||
switch (fake_cin) {
|
||||
case MIDI_CIN_NOTE_OFF:
|
||||
case MIDI_CIN_NOTE_ON:
|
||||
case MIDI_CIN_POLY_KEYPRESS:
|
||||
case MIDI_CIN_CONTROL_CHANGE:
|
||||
case MIDI_CIN_PITCH_BEND_CHANGE:
|
||||
bytes_to_add_to_stream = 3;
|
||||
break;
|
||||
case MIDI_CIN_PROGRAM_CHANGE:
|
||||
case MIDI_CIN_CHANNEL_PRESSURE:
|
||||
bytes_to_add_to_stream = 2;
|
||||
break;
|
||||
default:
|
||||
break;// Should not get this
|
||||
}
|
||||
cable_sysex_in_progress &= (uint16_t) ~cable_mask;
|
||||
} else if (status < MIDI_STATUS_SYSREAL_TIMING_CLOCK) {
|
||||
switch (status) {
|
||||
case MIDI_STATUS_SYSCOM_TIME_CODE_QUARTER_FRAME:
|
||||
case MIDI_STATUS_SYSCOM_SONG_SELECT:
|
||||
bytes_to_add_to_stream = 2;
|
||||
break;
|
||||
case MIDI_STATUS_SYSCOM_SONG_POSITION_POINTER:
|
||||
bytes_to_add_to_stream = 3;
|
||||
break;
|
||||
case MIDI_STATUS_SYSCOM_TUNE_REQUEST:
|
||||
case MIDI_STATUS_SYSEX_END:
|
||||
bytes_to_add_to_stream = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
cable_sysex_in_progress &= (uint16_t) ~cable_mask;
|
||||
} else {
|
||||
// Real-time message: can be inserted into a sysex message,
|
||||
// so do don't clear cable_sysex_in_progress bit
|
||||
bytes_to_add_to_stream = 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint8_t i = 1; i <= bytes_to_add_to_stream; i++) {
|
||||
*p_buffer++ = p_midi->stream_read.buffer[i];
|
||||
}
|
||||
bytes_buffered += bytes_to_add_to_stream;
|
||||
nread = 0;
|
||||
if (tu_edpt_stream_peek(&p_midi->ep_stream.rx, &one_byte)) {
|
||||
uint8_t new_cable = (one_byte >> 4) & 0xf;
|
||||
if (new_cable == *p_cable_num) {
|
||||
// still on the same cable. Continue reading the stream
|
||||
nread = tu_edpt_stream_read(p_midi->daddr, &p_midi->ep_stream.rx, p_midi->stream_read.buffer, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bytes_buffered;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
193
src/class/midi/midi_host.h
Normal file
193
src/class/midi/midi_host.h
Normal file
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* 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 TUSB_MIDI_HOST_H_
|
||||
#define TUSB_MIDI_HOST_H_
|
||||
|
||||
#include "class/audio/audio.h"
|
||||
#include "midi.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Class Driver Configuration
|
||||
//--------------------------------------------------------------------+
|
||||
#ifndef CFG_TUH_MIDI_RX_BUFSIZE
|
||||
#define CFG_TUH_MIDI_RX_BUFSIZE TUH_EPSIZE_BULK_MPS
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUH_MIDI_TX_BUFSIZE
|
||||
#define CFG_TUH_MIDI_TX_BUFSIZE TUH_EPSIZE_BULK_MPS
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUH_MIDI_EP_BUFSIZE
|
||||
#define CFG_TUH_MIDI_EP_BUFSIZE TUH_EPSIZE_BULK_MPS
|
||||
#endif
|
||||
|
||||
// Enable the MIDI stream read/write API. Some library can work with raw USB MIDI packet
|
||||
// Disable this can save driver footprint.
|
||||
#ifndef CFG_TUH_MIDI_STREAM_API
|
||||
#define CFG_TUH_MIDI_STREAM_API 1
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application Types
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct {
|
||||
const tusb_desc_interface_t* desc_audio_control;
|
||||
const tusb_desc_interface_t* desc_midi; // start of whole midi interface descriptor
|
||||
uint16_t desc_midi_total_len;
|
||||
|
||||
const uint8_t* desc_header;
|
||||
const uint8_t* desc_element;
|
||||
const tusb_desc_endpoint_t* desc_epin; // endpoint IN descriptor, CS_ENDPOINT is right after
|
||||
const tusb_desc_endpoint_t* desc_epout; // endpoint OUT descriptor, CS_ENDPOINT is right after
|
||||
|
||||
uint8_t jack_num;
|
||||
const uint8_t* desc_jack[32]; // list of jack descriptors (embedded + external)
|
||||
} tuh_midi_descriptor_cb_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t daddr;
|
||||
uint8_t bInterfaceNumber; // interface number of MIDI streaming
|
||||
uint8_t rx_cable_count;
|
||||
uint8_t tx_cable_count;
|
||||
} tuh_midi_mount_cb_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Check if MIDI interface is mounted
|
||||
bool tuh_midi_mounted(uint8_t idx);
|
||||
|
||||
// Get Interface index from device address + interface number
|
||||
// return TUSB_INDEX_INVALID_8 (0xFF) if not found
|
||||
uint8_t tuh_midi_itf_get_index(uint8_t daddr, uint8_t itf_num);
|
||||
|
||||
// Get Interface information
|
||||
// return true if index is correct and interface is currently mounted
|
||||
bool tuh_midi_itf_get_info(uint8_t idx, tuh_itf_info_t* info);
|
||||
|
||||
// return the number of virtual midi cables on the device's IN endpoint
|
||||
uint8_t tuh_midi_get_rx_cable_count(uint8_t idx);
|
||||
|
||||
// return the number of virtual midi cables on the device's OUT endpoint
|
||||
uint8_t tuh_midi_get_tx_cable_count(uint8_t idx);
|
||||
|
||||
// return the raw number of bytes available.
|
||||
// Note: this is related but not the same as number of stream bytes available.
|
||||
uint32_t tuh_midi_read_available(uint8_t idx);
|
||||
|
||||
// Send any queued packets to the device if the host hardware is able to do it
|
||||
// Returns the number of bytes flushed to the host hardware or 0 if
|
||||
// the host hardware is busy or there is nothing in queue to send.
|
||||
uint32_t tuh_midi_write_flush(uint8_t idx);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Packet API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Read all available MIDI packets from the connected device
|
||||
// Return number of bytes read (always multiple of 4)
|
||||
uint32_t tuh_midi_packet_read_n(uint8_t idx, uint8_t* buffer, uint32_t bufsize);
|
||||
|
||||
// Read a raw MIDI packet from the connected device
|
||||
// Return true if a packet was returned
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
bool tuh_midi_packet_read (uint8_t idx, uint8_t packet[4]) {
|
||||
return 4 == tuh_midi_packet_read_n(idx, packet, 4);
|
||||
}
|
||||
|
||||
// Write all 4-byte packets, data is locally buffered and only transferred when buffered bytes
|
||||
// reach the endpoint packet size or tuh_midi_write_flush() is called
|
||||
uint32_t tuh_midi_packet_write_n(uint8_t idx, const uint8_t* buffer, uint32_t bufsize);
|
||||
|
||||
// Write a 4-bytes packet to the device.
|
||||
// Returns true if the packet was successfully queued.
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
bool tuh_midi_packet_write (uint8_t idx, uint8_t const packet[4]) {
|
||||
return 4 == tuh_midi_packet_write_n(idx, packet, 4);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Stream API
|
||||
//--------------------------------------------------------------------+
|
||||
#if CFG_TUH_MIDI_STREAM_API
|
||||
|
||||
// Queue a message to the device using stream API. data is locally buffered and only transferred when buffered bytes
|
||||
// reach the endpoint packet size or tuh_midi_write_flush() is called
|
||||
// Returns number of bytes was successfully queued.
|
||||
uint32_t tuh_midi_stream_write(uint8_t idx, uint8_t cable_num, uint8_t const *p_buffer, uint32_t bufsize);
|
||||
|
||||
// Get the MIDI stream from the device. Set the value pointed
|
||||
// to by p_cable_num to the MIDI cable number intended to receive it.
|
||||
// The MIDI stream will be stored in the buffer pointed to by p_buffer.
|
||||
// Return the number of bytes added to the buffer.
|
||||
// Note that this function ignores the CIN field of the MIDI packet
|
||||
// because a number of commercial devices out there do not encode
|
||||
// it properly.
|
||||
uint32_t tuh_midi_stream_read(uint8_t idx, uint8_t *p_cable_num, uint8_t *p_buffer, uint16_t bufsize);
|
||||
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Callbacks (Weak is optional)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Invoked when MIDI interface is detected in enumeration. Application can copy/parse descriptor if needed.
|
||||
// Note: may be fired before tuh_midi_mount_cb(), therefore midi interface is not mounted/ready.
|
||||
void tuh_midi_descriptor_cb(uint8_t idx, const tuh_midi_descriptor_cb_t * desc_cb_data);
|
||||
|
||||
// Invoked when device with MIDI interface is mounted.
|
||||
void tuh_midi_mount_cb(uint8_t idx, const tuh_midi_mount_cb_t* mount_cb_data);
|
||||
|
||||
// Invoked when device with MIDI interface is un-mounted
|
||||
void tuh_midi_umount_cb(uint8_t idx);
|
||||
|
||||
// Invoked when received new data
|
||||
void tuh_midi_rx_cb(uint8_t idx, uint32_t xferred_bytes);
|
||||
|
||||
// Invoked when a TX is complete and therefore space becomes available in TX buffer
|
||||
void tuh_midi_tx_cb(uint8_t idx, uint32_t xferred_bytes);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Internal Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
bool midih_init (void);
|
||||
bool midih_deinit (void);
|
||||
bool midih_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len);
|
||||
bool midih_set_config (uint8_t dev_addr, uint8_t itf_num);
|
||||
bool midih_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
|
||||
void midih_close (uint8_t daddr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -202,7 +202,7 @@ static bool proc_stage_status(mscd_interface_t* p_msc) {
|
||||
if (!usbd_edpt_stalled(rhport, p_msc->ep_in)) {
|
||||
if ((p_cbw->total_bytes > p_msc->xferred_len) && is_data_in(p_cbw->dir)) {
|
||||
// 6.7 The 13 Cases: case 5 (Hi > Di): STALL before status
|
||||
// TU_LOG(MSC_DEBUG, " SCSI case 5 (Hi > Di): %lu > %lu\r\n", p_cbw->total_bytes, p_msc->xferred_len);
|
||||
// TU_LOG_DRV(" SCSI case 5 (Hi > Di): %lu > %lu\r\n", p_cbw->total_bytes, p_msc->xferred_len);
|
||||
usbd_edpt_stall(rhport, p_msc->ep_in);
|
||||
} else {
|
||||
TU_ASSERT(send_csw(p_msc));
|
||||
@@ -434,7 +434,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
|
||||
memcpy(p_cbw, _mscd_epbuf.buf, sizeof(msc_cbw_t));
|
||||
|
||||
TU_LOG_DRV(" SCSI Command [Lun%u]: %s\r\n", p_cbw->lun, tu_lookup_find(&_msc_scsi_cmd_table, p_cbw->command[0]));
|
||||
//TU_LOG_MEM(MSC_DEBUG, p_cbw, xferred_bytes, 2);
|
||||
// TU_LOG_MEM(CFG_TUD_MSC_LOG_LEVEL, p_cbw, xferred_bytes, 2);
|
||||
|
||||
p_csw->signature = MSC_CSW_SIGNATURE;
|
||||
p_csw->tag = p_cbw->tag;
|
||||
@@ -491,7 +491,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
|
||||
} else if (resplen == 0) {
|
||||
if (p_cbw->total_bytes) {
|
||||
// 6.7 The 13 Cases: case 4 (Hi > Dn)
|
||||
// TU_LOG(MSC_DEBUG, " SCSI case 4 (Hi > Dn): %lu\r\n", p_cbw->total_bytes);
|
||||
// TU_LOG_DRV(" SCSI case 4 (Hi > Dn): %lu\r\n", p_cbw->total_bytes);
|
||||
fail_scsi_op(p_msc, MSC_CSW_STATUS_FAILED);
|
||||
} else {
|
||||
// case 1 Hn = Dn: all good
|
||||
@@ -500,7 +500,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
|
||||
} else {
|
||||
if (p_cbw->total_bytes == 0) {
|
||||
// 6.7 The 13 Cases: case 2 (Hn < Di)
|
||||
// TU_LOG(MSC_DEBUG, " SCSI case 2 (Hn < Di): %lu\r\n", p_cbw->total_bytes);
|
||||
// TU_LOG_DRV(" SCSI case 2 (Hn < Di): %lu\r\n", p_cbw->total_bytes);
|
||||
fail_scsi_op(p_msc, MSC_CSW_STATUS_FAILED);
|
||||
} else {
|
||||
// cannot return more than host expect
|
||||
@@ -515,7 +515,8 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
|
||||
|
||||
case MSC_STAGE_DATA:
|
||||
TU_LOG_DRV(" SCSI Data [Lun%u]\r\n", p_cbw->lun);
|
||||
//TU_LOG_MEM(MSC_DEBUG, _mscd_epbuf.buf, xferred_bytes, 2);
|
||||
TU_ASSERT(xferred_bytes <= CFG_TUD_MSC_EP_BUFSIZE); // sanity check to avoid buffer overflow
|
||||
// TU_LOG_MEM(CFG_TUD_MSC_LOG_LEVEL, _mscd_epbuf.buf, xferred_bytes, 2);
|
||||
|
||||
if (SCSI_CMD_READ_10 == p_cbw->command[0]) {
|
||||
p_msc->xferred_len += xferred_bytes;
|
||||
@@ -562,7 +563,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
|
||||
// Wait for the Status phase to complete
|
||||
if ((ep_addr == p_msc->ep_in) && (xferred_bytes == sizeof(msc_csw_t))) {
|
||||
TU_LOG_DRV(" SCSI Status [Lun%u] = %u\r\n", p_cbw->lun, p_csw->status);
|
||||
// TU_LOG_MEM(MSC_DEBUG, p_csw, xferred_bytes, 2);
|
||||
// TU_LOG_MEM(CFG_TUD_MSC_LOG_LEVEL, p_csw, xferred_bytes, 2);
|
||||
|
||||
// Invoke complete callback if defined
|
||||
// Note: There is racing issue with samd51 + qspi flash testing with arduino
|
||||
@@ -897,21 +898,19 @@ static void proc_write10_next(mscd_interface_t* p_msc, uint32_t xferred_bytes, i
|
||||
// update actual byte before failed
|
||||
p_msc->xferred_len += xferred_bytes;
|
||||
|
||||
// Set sense
|
||||
msc_cbw_t const* p_cbw = &p_msc->cbw;
|
||||
set_sense_medium_not_present(p_cbw->lun);
|
||||
|
||||
fail_scsi_op(p_msc, MSC_CSW_STATUS_FAILED);
|
||||
} else {
|
||||
// Application consume less than what we got (including zero)
|
||||
if ((uint32_t)nbytes < xferred_bytes) {
|
||||
uint32_t const left_over = xferred_bytes - (uint32_t)nbytes;
|
||||
// Application consume less than what we got (including zero)
|
||||
const uint32_t left_over = xferred_bytes - (uint32_t)nbytes;
|
||||
if (nbytes > 0) {
|
||||
p_msc->xferred_len += (uint16_t)nbytes;
|
||||
memmove(_mscd_epbuf.buf, _mscd_epbuf.buf + nbytes, left_over);
|
||||
}
|
||||
|
||||
// simulate an transfer complete with adjusted parameters --> callback will be invoked with adjusted parameter
|
||||
// simulate a transfer complete with adjusted parameters --> callback will be invoked with adjusted parameter
|
||||
uint8_t rhport = p_msc->rhport;
|
||||
dcd_event_xfer_complete(rhport, p_msc->ep_out, left_over, XFER_RESULT_SUCCESS, false);
|
||||
} else {
|
||||
|
||||
2
src/class/vendor/vendor_device.c
vendored
2
src/class/vendor/vendor_device.c
vendored
@@ -197,7 +197,7 @@ void vendord_reset(uint8_t rhport) {
|
||||
uint16_t vendord_open(uint8_t rhport, const tusb_desc_interface_t* desc_itf, uint16_t max_len) {
|
||||
TU_VERIFY(TUSB_CLASS_VENDOR_SPECIFIC == desc_itf->bInterfaceClass, 0);
|
||||
const uint8_t* p_desc = tu_desc_next(desc_itf);
|
||||
const uint8_t* desc_end = p_desc + max_len;
|
||||
const uint8_t* desc_end = (uint8_t const*)desc_itf + max_len;
|
||||
|
||||
// Find available interface
|
||||
vendord_interface_t* p_vendor = NULL;
|
||||
|
||||
Reference in New Issue
Block a user