feat: add IOKit-based USB transport

Replace the stub probe with a real IOKit/IOUSBFamily transport:
discovery by VID/PID, device and interface connections, vendor
control requests on EP0, bulk writes to EP 0x04 OUT, and an async
read pump (EP 0x05 IN / EP 0x04 IN) on a dedicated CFRunLoop
thread with disconnect notification.

Add the test_usb suite, link IOKit/CoreFoundation into xone_usb,
and update the README investigation checklist.

Co-Authored-By: qwen3.8-27b@q2_k_xl: implemented USB transport layer
This commit is contained in:
portersky
2026-08-17 15:52:22 +02:00
parent 5a102bb511
commit 4f0f1ff75d
7 changed files with 690 additions and 23 deletions
+7
View File
@@ -1,5 +1,12 @@
set(TEST_TARGETS "")
add_executable(test_usb test_usb.cpp)
target_include_directories(test_usb PRIVATE "${CMAKE_SOURCE_DIR}/include")
target_link_libraries(test_usb PRIVATE xone_usb Unity::Unity)
target_compile_features(test_usb PRIVATE cxx_std_23)
add_test(NAME test_usb COMMAND test_usb)
list(APPEND TEST_TARGETS test_usb)
add_executable(test_version test_version.cpp)
target_include_directories(test_version PRIVATE "${CMAKE_SOURCE_DIR}/include")
target_link_libraries(test_version PRIVATE xone_api Unity::Unity)
+51
View File
@@ -0,0 +1,51 @@
#include "unity.h"
#include "usb/usb_transport.hpp"
void setUp() {}
void tearDown() {}
// Pin the vendor request codes to transport/mt76_defs.h values.
void test_vendor_request_codes(void)
{
TEST_ASSERT_EQUAL_UINT8(static_cast<std::uint8_t>(xone::usb::vendor_request::dev_mode), 0x01);
TEST_ASSERT_EQUAL_UINT8(static_cast<std::uint8_t>(xone::usb::vendor_request::write), 0x02);
TEST_ASSERT_EQUAL_UINT8(static_cast<std::uint8_t>(xone::usb::vendor_request::power_on), 0x04);
TEST_ASSERT_EQUAL_UINT8(static_cast<std::uint8_t>(xone::usb::vendor_request::multi_write), 0x06);
TEST_ASSERT_EQUAL_UINT8(static_cast<std::uint8_t>(xone::usb::vendor_request::multi_read), 0x07);
TEST_ASSERT_EQUAL_UINT8(static_cast<std::uint8_t>(xone::usb::vendor_request::read_eeprom), 0x09);
TEST_ASSERT_EQUAL_UINT8(static_cast<std::uint8_t>(xone::usb::vendor_request::write_fce), 0x42);
TEST_ASSERT_EQUAL_UINT8(static_cast<std::uint8_t>(xone::usb::vendor_request::write_cfg), 0x46);
TEST_ASSERT_EQUAL_UINT8(static_cast<std::uint8_t>(xone::usb::vendor_request::read_cfg), 0x47);
TEST_ASSERT_EQUAL_UINT8(static_cast<std::uint8_t>(xone::usb::vendor_request::read_ext), 0x63);
TEST_ASSERT_EQUAL_UINT8(static_cast<std::uint8_t>(xone::usb::vendor_request::write_ext), 0x66);
TEST_ASSERT_EQUAL_UINT8(static_cast<std::uint8_t>(xone::usb::vendor_request::feature_set), 0x91);
}
// Pin the endpoint numbers and buffer sizes to upstream values.
void test_endpoint_constants(void)
{
TEST_ASSERT_EQUAL_UINT8(xone::usb::ep_in_cmd, 0x05);
TEST_ASSERT_EQUAL_UINT8(xone::usb::ep_in_wlan, 0x04);
TEST_ASSERT_EQUAL_UINT8(xone::usb::ep_out, 0x04);
TEST_ASSERT_EQUAL_UINT32(xone::usb::len_cmd_pkt, 0x0654);
TEST_ASSERT_EQUAL_UINT32(xone::usb::len_wlan_pkt, 0x8400);
}
// Without a dongle connected, probe must return nullptr.
void test_probe_without_dongle(void)
{
if (xone::usb::dongle_present())
return; // skip: hardware present
TEST_ASSERT_NULL(xone::usb::transport::probe(nullptr, nullptr));
}
int main(void)
{
UNITY_BEGIN();
RUN_TEST(test_vendor_request_codes);
RUN_TEST(test_endpoint_constants);
RUN_TEST(test_probe_without_dongle);
return UNITY_END();
}