Tactility/TactilityKernel/tests/source/usb_host_midi_event_test.cpp
Ken Van Hoeylandt cb4ce5246d
USB host refactored (#684)
- Create `USB_HOST_TYPE` base driver in TactilityKernel
- Refactor esp32_usbhost dts format, and create child devices dynamically
- Use PSRAM for task stacks where possible: saving 20 kB from just the task stacks, but roughly 30 kB when nothing is connected.
- Allow the user to disable USB host interface in the USB settings app, saving an additionaly 20 kB of IRAM when nothing was connected.
- Change listener-based APIs to subscribe and poll/await pattern. This avoids callstacks of apps and services leaking into the USB API

This results in big IRAM savings: 30 kB when USB host is on and nothing is connected, and 50 kB total when USB host is disabled.
This brings the total free IRAM after boot from roughly 170 kB to 200 kB (host enabled) or 220 kB (host disabled).
2026-10-10 13:33:32 +02:00

98 lines
3.5 KiB
C++

#include "doctest.h"
#include <tactility/concurrent/task_event_group.h>
#include <tactility/device.h>
#include <tactility/drivers/usb_host_midi.h>
static usb_midi_message_t make_message(uint8_t data1) {
return usb_midi_message_t { .cable = 0, .status = 0x90, .data1 = data1, .data2 = 100 };
}
TEST_CASE("usb_midi_event_emit queues messages in FIFO order and signals the subscription bit") {
Device device = {};
TaskEventGroup group;
task_event_group_construct(&group);
UsbMidiEventSubscription sub = {};
REQUIRE_EQ(usb_midi_event_subscribe(&sub, &group, &device), ERROR_NONE);
auto first = make_message(60);
auto second = make_message(64);
CHECK_EQ(usb_midi_event_emit(&device, &first), ERROR_NONE);
CHECK_EQ(usb_midi_event_emit(&device, &second), ERROR_NONE);
uint32_t flags = 0;
CHECK_EQ(task_event_group_wait(&group, sub.bit, false, &flags, 0), ERROR_NONE);
CHECK_EQ(flags, sub.bit);
UsbMidiEvent event = {};
REQUIRE_EQ(usb_midi_event_poll(&sub, &event), ERROR_NONE);
CHECK_EQ(event.message.data1, 60);
CHECK_EQ(event.message.status, 0x90);
REQUIRE_EQ(usb_midi_event_poll(&sub, &event), ERROR_NONE);
CHECK_EQ(event.message.data1, 64);
CHECK_EQ(usb_midi_event_poll(&sub, &event), ERROR_TIMEOUT);
CHECK_EQ(usb_midi_event_unsubscribe(&sub), ERROR_NONE);
task_event_group_destruct(&group);
}
TEST_CASE("usb_midi_event_emit only delivers to subscriptions of the emitting device") {
Device device_a = {};
Device device_b = {};
TaskEventGroup group;
task_event_group_construct(&group);
UsbMidiEventSubscription sub = {};
REQUIRE_EQ(usb_midi_event_subscribe(&sub, &group, &device_a), ERROR_NONE);
auto message = make_message(60);
CHECK_EQ(usb_midi_event_emit(&device_b, &message), ERROR_NOT_FOUND);
UsbMidiEvent event = {};
CHECK_EQ(usb_midi_event_poll(&sub, &event), ERROR_TIMEOUT);
CHECK_EQ(usb_midi_event_unsubscribe(&sub), ERROR_NONE);
task_event_group_destruct(&group);
}
TEST_CASE("usb_midi_event_emit drops the newest message when the queue is full") {
Device device = {};
TaskEventGroup group;
task_event_group_construct(&group);
UsbMidiEventSubscription sub = {};
REQUIRE_EQ(usb_midi_event_subscribe(&sub, &group, &device), ERROR_NONE);
for (int i = 0; i < USB_MIDI_EVENT_QUEUE_CAPACITY; i++) {
auto message = make_message(static_cast<uint8_t>(i));
CHECK_EQ(usb_midi_event_emit(&device, &message), ERROR_NONE);
}
auto overflow = make_message(127);
CHECK_EQ(usb_midi_event_emit(&device, &overflow), ERROR_RESOURCE);
UsbMidiEvent event = {};
for (int i = 0; i < USB_MIDI_EVENT_QUEUE_CAPACITY; i++) {
REQUIRE_EQ(usb_midi_event_poll(&sub, &event), ERROR_NONE);
CHECK_EQ(event.message.data1, i);
}
CHECK_EQ(usb_midi_event_poll(&sub, &event), ERROR_TIMEOUT);
CHECK_EQ(usb_midi_event_unsubscribe(&sub), ERROR_NONE);
task_event_group_destruct(&group);
}
TEST_CASE("usb_midi_event_subscribe rejects a subscription that is already registered") {
Device device = {};
TaskEventGroup group;
task_event_group_construct(&group);
UsbMidiEventSubscription sub = {};
REQUIRE_EQ(usb_midi_event_subscribe(&sub, &group, &device), ERROR_NONE);
CHECK_EQ(usb_midi_event_subscribe(&sub, &group, &device), ERROR_INVALID_STATE);
CHECK_EQ(usb_midi_event_unsubscribe(&sub), ERROR_NONE);
CHECK_EQ(usb_midi_event_unsubscribe(&sub), ERROR_NOT_FOUND);
task_event_group_destruct(&group);
}