mirror of
https://github.com/hathach/tinyusb.git
synced 2026-08-18 11:02:16 +00:00
test: add MIDI 2.0 Device and Host unit tests
Add unit tests for MIDI 2.0 drivers: - Device: UMP word count (all 16 message types), descriptor macro validation (length, byte layout, alt settings, endpoints), CS endpoint subtypes, traversal integrity - Host: UMP word count, callback struct validation, CS endpoint subtypes Also add Sphinx documentation for MIDI 2.0 class drivers (Device and Host API reference, lifecycle, configuration, examples). Tests: 60/60 PASS (FIFO 26/26, USBD 5/5, MIDI2 Device 18/18, MIDI2 Host 6/6, USBD internal 5/5)
This commit is contained in:
316
docs/reference/class_drivers.rst
Normal file
316
docs/reference/class_drivers.rst
Normal file
@ -0,0 +1,316 @@
|
||||
***************
|
||||
Class Drivers
|
||||
***************
|
||||
|
||||
USB Class Drivers implement specific USB device classes (CDC, HID, MSC, MIDI, Audio, etc.) and are the main interface between the USB core and application code.
|
||||
|
||||
MIDI 2.0 Device Driver
|
||||
=======================
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
The MIDI 2.0 Device driver enables TinyUSB to act as a USB MIDI 2.0 device. It implements both Alt Setting 0 (MIDI 1.0 fallback) and Alt Setting 1 (native UMP) as required by the USB-MIDI 2.0 specification.
|
||||
|
||||
**Key Features:**
|
||||
|
||||
- **Dual Alt Settings**: Alt 0 (MIDI 1.0) and Alt 1 (UMP native) per USB-MIDI 2.0 spec
|
||||
- **Protocol Negotiation**: Endpoint Discovery, Config Request/Notify, Function Block Discovery
|
||||
- **Group Terminal Block**: Served via GET_DESCRIPTOR automatically
|
||||
- **Atomic UMP Framing**: Read/write with correct message boundaries
|
||||
- **Memory Safe**: No dynamic allocation, static instances
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
Enable MIDI 2.0 Device support in ``tusb_config.h``:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#define CFG_TUD_ENABLED 1
|
||||
#define CFG_TUD_MIDI2 1
|
||||
|
||||
Optional configuration:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#define CFG_TUD_MIDI2_TX_BUFSIZE 256
|
||||
#define CFG_TUD_MIDI2_RX_BUFSIZE 256
|
||||
#define CFG_TUD_MIDI2_TX_EPSIZE 64
|
||||
#define CFG_TUD_MIDI2_RX_EPSIZE 64
|
||||
#define CFG_TUD_MIDI2_NUM_GROUPS 1 // 1..16
|
||||
#define CFG_TUD_MIDI2_NUM_FUNCTION_BLOCKS 1 // 1..32
|
||||
#define CFG_TUD_MIDI2_EP_NAME "TinyUSB MIDI 2.0"
|
||||
#define CFG_TUD_MIDI2_PRODUCT_ID "TinyUSB-MIDI2"
|
||||
|
||||
Public API
|
||||
----------
|
||||
|
||||
Query Functions
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
bool tud_midi2_mounted(void);
|
||||
uint32_t tud_midi2_available(void);
|
||||
uint8_t tud_midi2_alt_setting(void);
|
||||
bool tud_midi2_negotiated(void);
|
||||
uint8_t tud_midi2_protocol(void);
|
||||
|
||||
I/O Functions
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
uint32_t tud_midi2_ump_read(uint32_t* words, uint32_t max_words);
|
||||
uint32_t tud_midi2_ump_write(const uint32_t* words, uint32_t count);
|
||||
bool tud_midi2_packet_read(uint8_t packet[4]);
|
||||
bool tud_midi2_packet_write(const uint8_t packet[4]);
|
||||
|
||||
Callbacks
|
||||
^^^^^^^^^
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
void tud_midi2_rx_cb(uint8_t itf);
|
||||
void tud_midi2_set_itf_cb(uint8_t itf, uint8_t alt);
|
||||
bool tud_midi2_get_req_itf_cb(uint8_t rhport, const tusb_control_request_t* request);
|
||||
|
||||
MIDI 2.0 Host Driver
|
||||
=====================
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
The MIDI 2.0 Host driver enables TinyUSB to enumerate and communicate with USB MIDI 2.0 devices. It implements the USB MIDI 2.0 specification, supporting both MIDI 1.0 legacy devices and modern MIDI 2.0 devices with UMP (Universal MIDI Packet) protocol.
|
||||
|
||||
**Key Features:**
|
||||
|
||||
- **Reactive Architecture**: Auto-detects Alt Setting 1 (MIDI 2.0) capability during enumeration
|
||||
- **Auto-Selection**: Automatically selects the highest available protocol (MIDI 2.0 preferred)
|
||||
- **Transparent Stream Messages**: All data (UMP packets + Stream Messages) flow through callbacks
|
||||
- **Memory Safe**: No dynamic allocation, fixed-size instances per device
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
Enable MIDI 2.0 Host support in ``tusb_config.h``:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#define CFG_TUH_ENABLED 1
|
||||
#define CFG_TUH_MIDI2 4 // Number of MIDI 2.0 devices to support
|
||||
|
||||
Optional buffer configuration:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#define CFG_TUH_MIDI2_RX_BUFSIZE (4 * TUH_EPSIZE_BULK_MAX)
|
||||
#define CFG_TUH_MIDI2_TX_BUFSIZE (4 * TUH_EPSIZE_BULK_MAX)
|
||||
|
||||
Enumeration Lifecycle
|
||||
---------------------
|
||||
|
||||
When a MIDI 2.0 device is connected, the host stack invokes callbacks in this order:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
Device Connected
|
||||
|
|
||||
[Host detects Alt 0 and Alt 1 descriptors]
|
||||
|
|
||||
tuh_midi2_descriptor_cb() <- Device detected, NOT yet ready
|
||||
|
|
||||
[Auto-select highest protocol]
|
||||
|
|
||||
tuh_midi2_mount_cb() <- Device ready to use
|
||||
|
|
||||
[Application can read/write data]
|
||||
|
|
||||
tuh_midi2_rx_cb() <- Data arrived
|
||||
tuh_midi2_tx_cb() <- TX buffer space available
|
||||
|
|
||||
[Device disconnects]
|
||||
|
|
||||
tuh_midi2_umount_cb() <- Device removed
|
||||
|
||||
Public API
|
||||
----------
|
||||
|
||||
Query Functions
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
bool tuh_midi2_mounted(uint8_t idx);
|
||||
uint8_t tuh_midi2_get_protocol_version(uint8_t idx); // 0=MIDI 1.0, 1=MIDI 2.0
|
||||
uint8_t tuh_midi2_get_alt_setting_active(uint8_t idx); // 0 or 1
|
||||
uint8_t tuh_midi2_get_cable_count(uint8_t idx);
|
||||
|
||||
I/O Functions
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
Read and write UMP (Universal MIDI Packet) data:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
uint32_t tuh_midi2_ump_read(uint8_t idx, uint32_t* words, uint32_t max_words);
|
||||
uint32_t tuh_midi2_ump_write(uint8_t idx, const uint32_t* words, uint32_t count);
|
||||
uint32_t tuh_midi2_write_flush(uint8_t idx);
|
||||
|
||||
Callbacks
|
||||
---------
|
||||
|
||||
Application can define weak callback implementations to respond to device events.
|
||||
|
||||
Descriptor Callback
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Invoked when device is detected but not yet ready for I/O:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
void tuh_midi2_descriptor_cb(uint8_t idx, const tuh_midi2_descriptor_cb_t *desc_cb_data) {
|
||||
printf("MIDI %s device detected\r\n",
|
||||
desc_cb_data->protocol_version == 0 ? "1.0" : "2.0");
|
||||
}
|
||||
|
||||
Mount Callback
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
Invoked when device is ready for I/O:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
void tuh_midi2_mount_cb(uint8_t idx, const tuh_midi2_mount_cb_t *mount_cb_data) {
|
||||
printf("Device mounted at idx=%u, protocol=%u, alt_setting=%u\r\n",
|
||||
idx, mount_cb_data->protocol_version, mount_cb_data->alt_setting_active);
|
||||
}
|
||||
|
||||
RX Callback
|
||||
^^^^^^^^^^^
|
||||
|
||||
Invoked when data arrives from device (both UMP packets and Stream Messages):
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
void tuh_midi2_rx_cb(uint8_t idx, uint32_t xferred_bytes) {
|
||||
uint32_t words[4];
|
||||
uint32_t n = tuh_midi2_ump_read(idx, words, 4);
|
||||
|
||||
for (uint32_t i = 0; i < n; i++) {
|
||||
uint8_t mt = (words[i] >> 28) & 0x0F;
|
||||
if (mt == 0x0F) {
|
||||
// Stream Message - app handles discovery, negotiation, etc.
|
||||
} else {
|
||||
// Regular MIDI UMP packet
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TX Callback
|
||||
^^^^^^^^^^^
|
||||
|
||||
Invoked when TX buffer space becomes available:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
void tuh_midi2_tx_cb(uint8_t idx, uint32_t xferred_bytes) {
|
||||
// Buffer space available for writing
|
||||
}
|
||||
|
||||
Unmount Callback
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
Invoked when device is disconnected:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
void tuh_midi2_umount_cb(uint8_t idx) {
|
||||
printf("Device at idx=%u disconnected\r\n", idx);
|
||||
}
|
||||
|
||||
Complete Example
|
||||
----------------
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#include "tusb.h"
|
||||
|
||||
void tuh_midi2_mount_cb(uint8_t idx, const tuh_midi2_mount_cb_t *mount_cb_data) {
|
||||
printf("MIDI 2.0 device mounted\r\n");
|
||||
}
|
||||
|
||||
void tuh_midi2_rx_cb(uint8_t idx, uint32_t xferred_bytes) {
|
||||
uint32_t words[4];
|
||||
uint32_t n = tuh_midi2_ump_read(idx, words, 4);
|
||||
|
||||
for (uint32_t i = 0; i < n; i++) {
|
||||
printf("RX: 0x%08lx\r\n", words[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void tuh_midi2_umount_cb(uint8_t idx) {
|
||||
printf("MIDI 2.0 device disconnected\r\n");
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
board_init();
|
||||
|
||||
tusb_rhport_init_t host_init = {.role = TUSB_ROLE_HOST, .speed = TUSB_SPEED_AUTO};
|
||||
tusb_init(BOARD_TUH_RHPORT, &host_init);
|
||||
|
||||
while (1) {
|
||||
tuh_task();
|
||||
}
|
||||
}
|
||||
|
||||
Architecture
|
||||
------------
|
||||
|
||||
The MIDI 2.0 Host driver uses a **reactive, callback-driven architecture** that mirrors the proven patterns in TinyUSB's existing device drivers (CDC, HID, etc.):
|
||||
|
||||
- **Auto-Detection**: Host automatically detects Alt Setting 1 capability
|
||||
- **Auto-Selection**: Selects highest protocol available (MIDI 2.0 preferred)
|
||||
- **Application Control**: App makes protocol behavior decisions via callbacks
|
||||
- **Transparent I/O**: Stream Messages and UMP packets flow transparently
|
||||
|
||||
Differences from MIDI 1.0 Host
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
|
||||
* - Aspect
|
||||
- MIDI 1.0 Host
|
||||
- MIDI 2.0 Host
|
||||
* - Alt Settings
|
||||
- Parses only Alt 0
|
||||
- Parses Alt 0 + Alt 1
|
||||
* - Data Format
|
||||
- 4-byte MIDI packets
|
||||
- UMP (32/64/128-bit)
|
||||
* - Version Detection
|
||||
- None
|
||||
- bcdMSC from descriptor
|
||||
* - GTB
|
||||
- N/A
|
||||
- Presence detection
|
||||
* - Stream Messages
|
||||
- N/A
|
||||
- Transparent passthrough
|
||||
* - Callbacks
|
||||
- descriptor_cb, mount_cb, rx_cb, umount_cb
|
||||
- descriptor_cb, mount_cb, rx_cb, tx_cb, umount_cb
|
||||
* - Public API
|
||||
- tuh_midi_*
|
||||
- tuh_midi2_*
|
||||
|
||||
Implementation Notes
|
||||
--------------------
|
||||
|
||||
- All internal state is statically allocated (no dynamic allocation)
|
||||
- Endpoint streams use TinyUSB's tu_edpt_stream_t for buffered I/O
|
||||
- Protocol version detection via bcdMSC field
|
||||
- Alt Setting is automatically selected during mount
|
||||
- Compatible with all TinyUSB-supported MCU families
|
||||
@ -9,6 +9,7 @@ Complete reference documentation for TinyUSB APIs, configuration, and supported
|
||||
|
||||
architecture
|
||||
usb_concepts
|
||||
class_drivers
|
||||
boards
|
||||
dependencies
|
||||
concurrency
|
||||
|
||||
263
test/unit-test/test/device/midi2/test_midi2_device.c
Normal file
263
test/unit-test/test/device/midi2/test_midi2_device.c
Normal file
@ -0,0 +1,263 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2026 Saulo Verissimo
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "unity.h"
|
||||
#include "tusb_types.h"
|
||||
#include "class/audio/audio.h"
|
||||
#include "class/midi/midi.h"
|
||||
#include "device/usbd.h"
|
||||
|
||||
void setUp(void) {}
|
||||
void tearDown(void) {}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// UMP Word Count: all 16 message types
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
void test_ump_word_count_1word_types(void) {
|
||||
uint8_t types[] = {0x0, 0x1, 0x2, 0x6, 0x7};
|
||||
for (int i = 0; i < 5; i++) {
|
||||
TEST_ASSERT_EQUAL(1, midi2_ump_word_count(types[i]));
|
||||
}
|
||||
}
|
||||
|
||||
void test_ump_word_count_2word_types(void) {
|
||||
uint8_t types[] = {0x3, 0x4, 0x8, 0x9, 0xA};
|
||||
for (int i = 0; i < 5; i++) {
|
||||
TEST_ASSERT_EQUAL(2, midi2_ump_word_count(types[i]));
|
||||
}
|
||||
}
|
||||
|
||||
void test_ump_word_count_3word_types(void) {
|
||||
TEST_ASSERT_EQUAL(3, midi2_ump_word_count(0xB));
|
||||
TEST_ASSERT_EQUAL(3, midi2_ump_word_count(0xC));
|
||||
}
|
||||
|
||||
void test_ump_word_count_4word_types(void) {
|
||||
uint8_t types[] = {0x5, 0xD, 0xE, 0xF};
|
||||
for (int i = 0; i < 4; i++) {
|
||||
TEST_ASSERT_EQUAL(4, midi2_ump_word_count(types[i]));
|
||||
}
|
||||
}
|
||||
|
||||
void test_ump_word_count_covers_all_16(void) {
|
||||
for (uint8_t mt = 0; mt <= 0xF; mt++) {
|
||||
uint8_t wc = midi2_ump_word_count(mt);
|
||||
TEST_ASSERT_TRUE(wc >= 1 && wc <= 4);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// CS Endpoint subtypes (defined in midi.h)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
void test_cs_endpoint_subtypes(void) {
|
||||
TEST_ASSERT_EQUAL(0x01, MIDI_CS_ENDPOINT_GENERAL);
|
||||
TEST_ASSERT_EQUAL(0x02, MIDI_CS_ENDPOINT_GENERAL_2_0);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Descriptor macro length calculations
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
void test_midi1_desc_len(void) {
|
||||
TEST_ASSERT_EQUAL(TUD_MIDI_DESC_HEAD_LEN + TUD_MIDI_DESC_JACK_LEN + TUD_MIDI_DESC_EP_LEN(1) * 2,
|
||||
TUD_MIDI_DESC_LEN);
|
||||
}
|
||||
|
||||
void test_midi2_alt1_head_len(void) {
|
||||
TEST_ASSERT_EQUAL(16, TUD_MIDI2_DESC_ALT1_HEAD_LEN);
|
||||
}
|
||||
|
||||
void test_midi2_alt1_ep_len(void) {
|
||||
// EP(7) + CS base(4) + numgtbs
|
||||
TEST_ASSERT_EQUAL(12, TUD_MIDI2_DESC_ALT1_EP_LEN(1));
|
||||
TEST_ASSERT_EQUAL(13, TUD_MIDI2_DESC_ALT1_EP_LEN(2));
|
||||
TEST_ASSERT_EQUAL(18, TUD_MIDI2_DESC_ALT1_EP_LEN(7));
|
||||
}
|
||||
|
||||
void test_midi2_desc_len(void) {
|
||||
int expected = TUD_MIDI_DESC_LEN + TUD_MIDI2_DESC_ALT1_HEAD_LEN + TUD_MIDI2_DESC_ALT1_EP_LEN(1) * 2;
|
||||
TEST_ASSERT_EQUAL(expected, TUD_MIDI2_DESC_LEN);
|
||||
}
|
||||
|
||||
void test_midi2_desc_len_greater_than_midi1(void) {
|
||||
TEST_ASSERT_TRUE(TUD_MIDI2_DESC_LEN > TUD_MIDI_DESC_LEN);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Descriptor macro byte validation
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
void test_midi2_descriptor_bytes(void) {
|
||||
uint8_t desc[] = { TUD_MIDI2_DESCRIPTOR(0, 0, 0x01, 0x81, 64) };
|
||||
|
||||
TEST_ASSERT_EQUAL(TUD_MIDI2_DESC_LEN, sizeof(desc));
|
||||
|
||||
// First byte: Audio Control Interface descriptor length = 9
|
||||
TEST_ASSERT_EQUAL(9, desc[0]);
|
||||
TEST_ASSERT_EQUAL(TUSB_DESC_INTERFACE, desc[1]);
|
||||
TEST_ASSERT_EQUAL(0, desc[2]);
|
||||
|
||||
// Find Alt Setting 1 by scanning
|
||||
int alt1_offset = -1;
|
||||
int pos = 0;
|
||||
while (pos < (int)sizeof(desc)) {
|
||||
if (desc[pos + 1] == TUSB_DESC_INTERFACE && desc[pos + 3] == 1) {
|
||||
alt1_offset = pos;
|
||||
break;
|
||||
}
|
||||
pos += desc[pos];
|
||||
}
|
||||
|
||||
TEST_ASSERT_TRUE_MESSAGE(alt1_offset >= 0, "Alt Setting 1 interface not found");
|
||||
|
||||
TEST_ASSERT_EQUAL(9, desc[alt1_offset]);
|
||||
TEST_ASSERT_EQUAL(TUSB_DESC_INTERFACE, desc[alt1_offset + 1]);
|
||||
TEST_ASSERT_EQUAL(1, desc[alt1_offset + 2]); // bInterfaceNumber
|
||||
TEST_ASSERT_EQUAL(1, desc[alt1_offset + 3]); // bAlternateSetting
|
||||
TEST_ASSERT_EQUAL(2, desc[alt1_offset + 4]); // bNumEndpoints
|
||||
TEST_ASSERT_EQUAL(TUSB_CLASS_AUDIO, desc[alt1_offset + 5]);
|
||||
|
||||
// MS Header after Alt Setting 1 interface: bcdMSC = 0x0200
|
||||
int ms2_offset = alt1_offset + 9;
|
||||
TEST_ASSERT_EQUAL(7, desc[ms2_offset]);
|
||||
TEST_ASSERT_EQUAL(TUSB_DESC_CS_INTERFACE, desc[ms2_offset + 1]);
|
||||
TEST_ASSERT_EQUAL(MIDI_CS_INTERFACE_HEADER, desc[ms2_offset + 2]);
|
||||
TEST_ASSERT_EQUAL(0x00, desc[ms2_offset + 3]);
|
||||
TEST_ASSERT_EQUAL(0x02, desc[ms2_offset + 4]);
|
||||
}
|
||||
|
||||
void test_midi2_descriptor_alt1_cs_endpoint_subtype(void) {
|
||||
uint8_t desc[] = { TUD_MIDI2_DESCRIPTOR(0, 0, 0x01, 0x81, 64) };
|
||||
|
||||
int cs_ep_count = 0;
|
||||
int pos = 0;
|
||||
while (pos < (int)sizeof(desc)) {
|
||||
if (desc[pos + 1] == TUSB_DESC_CS_ENDPOINT &&
|
||||
desc[pos + 2] == MIDI_CS_ENDPOINT_GENERAL_2_0) {
|
||||
cs_ep_count++;
|
||||
TEST_ASSERT_EQUAL(1, desc[pos + 3]);
|
||||
}
|
||||
pos += desc[pos];
|
||||
}
|
||||
TEST_ASSERT_EQUAL(2, cs_ep_count);
|
||||
}
|
||||
|
||||
void test_midi2_descriptor_has_both_alt_settings(void) {
|
||||
uint8_t desc[] = { TUD_MIDI2_DESCRIPTOR(0, 0, 0x01, 0x81, 64) };
|
||||
|
||||
int alt0_count = 0;
|
||||
int alt1_count = 0;
|
||||
int pos = 0;
|
||||
while (pos < (int)sizeof(desc)) {
|
||||
if (desc[pos + 1] == TUSB_DESC_INTERFACE) {
|
||||
if (desc[pos + 3] == 0) alt0_count++;
|
||||
if (desc[pos + 3] == 1) alt1_count++;
|
||||
}
|
||||
pos += desc[pos];
|
||||
}
|
||||
TEST_ASSERT_TRUE(alt0_count >= 2);
|
||||
TEST_ASSERT_EQUAL(1, alt1_count);
|
||||
}
|
||||
|
||||
void test_midi2_descriptor_endpoint_addresses(void) {
|
||||
uint8_t desc[] = { TUD_MIDI2_DESCRIPTOR(0, 0, 0x02, 0x82, 64) };
|
||||
|
||||
int ep_out_count = 0;
|
||||
int ep_in_count = 0;
|
||||
int pos = 0;
|
||||
while (pos < (int)sizeof(desc)) {
|
||||
if (desc[pos + 1] == TUSB_DESC_ENDPOINT) {
|
||||
uint8_t ep_addr = desc[pos + 2];
|
||||
if (ep_addr == 0x02) ep_out_count++;
|
||||
if (ep_addr == 0x82) ep_in_count++;
|
||||
TEST_ASSERT_EQUAL(TUSB_XFER_BULK, desc[pos + 3]);
|
||||
TEST_ASSERT_EQUAL(64, desc[pos + 4]);
|
||||
TEST_ASSERT_EQUAL(0, desc[pos + 5]);
|
||||
}
|
||||
pos += desc[pos];
|
||||
}
|
||||
TEST_ASSERT_EQUAL(2, ep_out_count);
|
||||
TEST_ASSERT_EQUAL(2, ep_in_count);
|
||||
}
|
||||
|
||||
void test_midi2_descriptor_nonzero_itfnum(void) {
|
||||
uint8_t desc[] = { TUD_MIDI2_DESCRIPTOR(2, 0, 0x03, 0x83, 64) };
|
||||
|
||||
TEST_ASSERT_EQUAL(2, desc[2]);
|
||||
|
||||
int pos = desc[0];
|
||||
while (pos < (int)sizeof(desc)) {
|
||||
if (desc[pos + 1] == TUSB_DESC_INTERFACE) {
|
||||
TEST_ASSERT_EQUAL(3, desc[pos + 2]);
|
||||
break;
|
||||
}
|
||||
pos += desc[pos];
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Descriptor traversal integrity
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
void test_midi2_descriptor_no_zero_length(void) {
|
||||
uint8_t desc[] = { TUD_MIDI2_DESCRIPTOR(0, 0, 0x01, 0x81, 64) };
|
||||
|
||||
int pos = 0;
|
||||
int desc_count = 0;
|
||||
while (pos < (int)sizeof(desc)) {
|
||||
TEST_ASSERT_TRUE_MESSAGE(desc[pos] > 0, "Zero-length descriptor found");
|
||||
TEST_ASSERT_TRUE_MESSAGE(desc[pos] <= (int)sizeof(desc) - pos,
|
||||
"Descriptor length exceeds remaining bytes");
|
||||
pos += desc[pos];
|
||||
desc_count++;
|
||||
}
|
||||
TEST_ASSERT_EQUAL((int)sizeof(desc), pos);
|
||||
TEST_ASSERT_TRUE(desc_count > 5);
|
||||
}
|
||||
|
||||
void test_midi2_descriptor_valid_types(void) {
|
||||
uint8_t desc[] = { TUD_MIDI2_DESCRIPTOR(0, 0, 0x01, 0x81, 64) };
|
||||
|
||||
int pos = 0;
|
||||
while (pos < (int)sizeof(desc)) {
|
||||
uint8_t dtype = desc[pos + 1];
|
||||
bool valid = (dtype == TUSB_DESC_INTERFACE ||
|
||||
dtype == TUSB_DESC_ENDPOINT ||
|
||||
dtype == TUSB_DESC_CS_INTERFACE ||
|
||||
dtype == TUSB_DESC_CS_ENDPOINT);
|
||||
TEST_ASSERT_TRUE_MESSAGE(valid, "Invalid descriptor type found");
|
||||
pos += desc[pos];
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Edge cases
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
void test_ump_word_count_with_values_beyond_0xf(void) {
|
||||
TEST_ASSERT_EQUAL(4, midi2_ump_word_count(0x10));
|
||||
TEST_ASSERT_EQUAL(4, midi2_ump_word_count(0xFF));
|
||||
}
|
||||
101
test/unit-test/test/host/midi2/test_midi2_host.c
Normal file
101
test/unit-test/test/host/midi2/test_midi2_host.c
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2026 Saulo Verissimo
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "unity.h"
|
||||
#include "tusb_option.h"
|
||||
#include "class/midi/midi.h"
|
||||
#include "class/midi/midi2_host.h"
|
||||
|
||||
void setUp(void) {}
|
||||
void tearDown(void) {}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// UMP Word Count (shared helper, defined in midi.h)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
void test_midi2_host_ump_word_count_1word(void) {
|
||||
uint8_t types[] = {0x0, 0x1, 0x2, 0x6, 0x7};
|
||||
for (int i = 0; i < 5; i++) {
|
||||
TEST_ASSERT_EQUAL(1, midi2_ump_word_count(types[i]));
|
||||
}
|
||||
}
|
||||
|
||||
void test_midi2_host_ump_word_count_2word(void) {
|
||||
uint8_t types[] = {0x3, 0x4, 0x8, 0x9, 0xA};
|
||||
for (int i = 0; i < 5; i++) {
|
||||
TEST_ASSERT_EQUAL(2, midi2_ump_word_count(types[i]));
|
||||
}
|
||||
}
|
||||
|
||||
void test_midi2_host_ump_word_count_4word(void) {
|
||||
uint8_t types[] = {0x5, 0xD, 0xE, 0xF};
|
||||
for (int i = 0; i < 4; i++) {
|
||||
TEST_ASSERT_EQUAL(4, midi2_ump_word_count(types[i]));
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Callback struct field validation
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
void test_midi2_descriptor_cb_struct_fields(void) {
|
||||
tuh_midi2_descriptor_cb_t desc = {
|
||||
.protocol_version = 1,
|
||||
.bcdMSC_hi = 0x02,
|
||||
.bcdMSC_lo = 0x00,
|
||||
.rx_cable_count = 1,
|
||||
.tx_cable_count = 1
|
||||
};
|
||||
TEST_ASSERT_EQUAL(1, desc.protocol_version);
|
||||
TEST_ASSERT_EQUAL(0x02, desc.bcdMSC_hi);
|
||||
TEST_ASSERT_EQUAL(0x00, desc.bcdMSC_lo);
|
||||
TEST_ASSERT_EQUAL(1, desc.rx_cable_count);
|
||||
TEST_ASSERT_EQUAL(1, desc.tx_cable_count);
|
||||
}
|
||||
|
||||
void test_midi2_mount_cb_struct_fields(void) {
|
||||
tuh_midi2_mount_cb_t mount = {
|
||||
.daddr = 1,
|
||||
.bInterfaceNumber = 0,
|
||||
.protocol_version = 1,
|
||||
.alt_setting_active = 1,
|
||||
.rx_cable_count = 2,
|
||||
.tx_cable_count = 2
|
||||
};
|
||||
TEST_ASSERT_EQUAL(1, mount.daddr);
|
||||
TEST_ASSERT_EQUAL(0, mount.bInterfaceNumber);
|
||||
TEST_ASSERT_EQUAL(1, mount.protocol_version);
|
||||
TEST_ASSERT_EQUAL(1, mount.alt_setting_active);
|
||||
TEST_ASSERT_EQUAL(2, mount.rx_cable_count);
|
||||
TEST_ASSERT_EQUAL(2, mount.tx_cable_count);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// CS Endpoint subtypes
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
void test_midi2_host_cs_endpoint_subtypes(void) {
|
||||
TEST_ASSERT_EQUAL(0x01, MIDI_CS_ENDPOINT_GENERAL);
|
||||
TEST_ASSERT_EQUAL(0x02, MIDI_CS_ENDPOINT_GENERAL_2_0);
|
||||
}
|
||||
Reference in New Issue
Block a user