Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
166 lines
7.6 KiB
C++
166 lines
7.6 KiB
C++
#include "test_support.h"
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
|
|
#include "../../switch2_descriptors.h"
|
|
|
|
namespace {
|
|
|
|
bool bytes_equal(
|
|
const uint8_t* actual,
|
|
std::size_t actual_length,
|
|
const uint8_t* expected,
|
|
std::size_t expected_length) {
|
|
return actual_length == expected_length &&
|
|
std::memcmp(actual, expected, expected_length) == 0;
|
|
}
|
|
|
|
bool switch2_descriptor_bytes_match_pinned_capture_subset() {
|
|
// Given: captured Pro Controller 2 bytes with the documented subset edits.
|
|
static constexpr uint8_t expected_device[] = {
|
|
0x12, 0x01, 0x00, 0x02, 0xEF, 0x02, 0x01, 0x40, 0x7E,
|
|
0x05, 0x69, 0x20, 0x00, 0x02, 0x01, 0x02, 0x03, 0x01,
|
|
};
|
|
static constexpr uint8_t expected_configuration[] = {
|
|
0x09, 0x02, 0x50, 0x00, 0x02, 0x01, 0x00, 0xC0, 0xFA,
|
|
0x08, 0x0B, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00,
|
|
0x09, 0x04, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00,
|
|
0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22, 0x61, 0x00,
|
|
0x07, 0x05, 0x81, 0x03, 0x40, 0x00, 0x04,
|
|
0x07, 0x05, 0x01, 0x03, 0x40, 0x00, 0x04,
|
|
0x08, 0x0B, 0x01, 0x01, 0xFF, 0x00, 0x00, 0x00,
|
|
0x09, 0x04, 0x01, 0x00, 0x02, 0xFF, 0x00, 0x00, 0x00,
|
|
0x07, 0x05, 0x02, 0x02, 0x40, 0x00, 0x00,
|
|
0x07, 0x05, 0x82, 0x02, 0x40, 0x00, 0x00,
|
|
};
|
|
static constexpr uint8_t expected_hid[] = {
|
|
0x05, 0x01, 0x09, 0x05, 0xA1, 0x01, 0x85, 0x05, 0x05, 0xFF, 0x09, 0x01, 0x15, 0x00, 0x26, 0xFF,
|
|
0x00, 0x95, 0x3F, 0x75, 0x08, 0x81, 0x02, 0x85, 0x09, 0x09, 0x01, 0x95, 0x02, 0x81, 0x02, 0x05,
|
|
0x09, 0x19, 0x01, 0x29, 0x15, 0x25, 0x01, 0x95, 0x15, 0x75, 0x01, 0x81, 0x02, 0x95, 0x01, 0x75,
|
|
0x03, 0x81, 0x03, 0x05, 0x01, 0x09, 0x01, 0xA1, 0x00, 0x09, 0x30, 0x09, 0x31, 0x09, 0x33, 0x09,
|
|
0x35, 0x26, 0xFF, 0x0F, 0x95, 0x04, 0x75, 0x0C, 0x81, 0x02, 0xC0, 0x05, 0xFF, 0x09, 0x02, 0x26,
|
|
0xFF, 0x00, 0x95, 0x34, 0x75, 0x08, 0x81, 0x02, 0x85, 0x02, 0x09, 0x01, 0x95, 0x3F, 0x91, 0x02,
|
|
0xC0,
|
|
};
|
|
|
|
// When/Then: all exported descriptor bytes and lengths match exactly.
|
|
CHECK(bytes_equal(switch2_device_descriptor, switch2_device_descriptor_length,
|
|
expected_device, sizeof(expected_device)));
|
|
CHECK(bytes_equal(switch2_configuration_descriptor, switch2_configuration_descriptor_length,
|
|
expected_configuration, sizeof(expected_configuration)));
|
|
CHECK(bytes_equal(switch2_hid_report_descriptor, switch2_hid_report_descriptor_length,
|
|
expected_hid, sizeof(expected_hid)));
|
|
return true;
|
|
}
|
|
|
|
bool switch2_configuration_iterates_two_interfaces_and_four_endpoints() {
|
|
// Given: the deliberately reduced configuration descriptor.
|
|
std::size_t offset = 0;
|
|
uint8_t interface_count = 0;
|
|
uint8_t endpoint_count = 0;
|
|
const uint8_t expected_endpoints[][5] = {
|
|
{0x81, 0x03, 0x40, 0x00, 0x04},
|
|
{0x01, 0x03, 0x40, 0x00, 0x04},
|
|
{0x02, 0x02, 0x40, 0x00, 0x00},
|
|
{0x82, 0x02, 0x40, 0x00, 0x00},
|
|
};
|
|
|
|
// When: every USB descriptor is iterated by bLength.
|
|
while (offset < switch2_configuration_descriptor_length) {
|
|
const uint8_t length = switch2_configuration_descriptor[offset];
|
|
CHECK(length >= 2);
|
|
CHECK(offset + length <= switch2_configuration_descriptor_length);
|
|
const uint8_t type = switch2_configuration_descriptor[offset + 1];
|
|
if (type == 0x04) {
|
|
CHECK(length == 9);
|
|
CHECK(switch2_configuration_descriptor[offset + 5] != 0x01);
|
|
CHECK(switch2_configuration_descriptor[offset + 8] == 0x00);
|
|
++interface_count;
|
|
} else if (type == 0x05) {
|
|
CHECK(length == 7);
|
|
CHECK(endpoint_count < 4);
|
|
CHECK(std::memcmp(&switch2_configuration_descriptor[offset + 2],
|
|
expected_endpoints[endpoint_count], 5) == 0);
|
|
++endpoint_count;
|
|
}
|
|
offset += length;
|
|
}
|
|
|
|
// Then: iteration is exact, contains only HID/vendor, and omits audio.
|
|
CHECK(offset == switch2_configuration_descriptor_length);
|
|
CHECK(switch2_configuration_descriptor[6] == 0x00);
|
|
CHECK(interface_count == 2);
|
|
CHECK(endpoint_count == 4);
|
|
return true;
|
|
}
|
|
|
|
bool switch2_hid_reports_are_exactly_sixty_three_payload_bytes() {
|
|
// Given: the captured HID report descriptor.
|
|
uint32_t report_size = 0;
|
|
uint32_t report_count = 0;
|
|
uint32_t report_id = 0;
|
|
uint32_t input_05_bits = 0;
|
|
uint32_t input_09_bits = 0;
|
|
uint32_t output_02_bits = 0;
|
|
|
|
// When: HID short items are parsed and report fields accumulated.
|
|
for (std::size_t offset = 0; offset < switch2_hid_report_descriptor_length;) {
|
|
const uint8_t prefix = switch2_hid_report_descriptor[offset++];
|
|
CHECK(prefix != 0xFE);
|
|
const uint8_t size_code = prefix & 0x03;
|
|
const uint8_t data_size = size_code == 3 ? 4 : size_code;
|
|
CHECK(offset + data_size <= switch2_hid_report_descriptor_length);
|
|
uint32_t value = 0;
|
|
for (uint8_t index = 0; index < data_size; ++index) {
|
|
value |= static_cast<uint32_t>(switch2_hid_report_descriptor[offset + index]) << (8 * index);
|
|
}
|
|
offset += data_size;
|
|
const uint8_t type = (prefix >> 2) & 0x03;
|
|
const uint8_t tag = (prefix >> 4) & 0x0F;
|
|
if (type == 1 && tag == 7) report_size = value;
|
|
if (type == 1 && tag == 8) report_id = value;
|
|
if (type == 1 && tag == 9) report_count = value;
|
|
if (type == 0 && tag == 8 && report_id == 0x05) input_05_bits += report_size * report_count;
|
|
if (type == 0 && tag == 8 && report_id == 0x09) input_09_bits += report_size * report_count;
|
|
if (type == 0 && tag == 9 && report_id == 0x02) output_02_bits += report_size * report_count;
|
|
}
|
|
|
|
// Then: each supported transfer is 63 payload bytes plus its report ID.
|
|
CHECK(input_05_bits == 63 * 8);
|
|
CHECK(input_09_bits == 63 * 8);
|
|
CHECK(output_02_bits == 63 * 8);
|
|
return true;
|
|
}
|
|
|
|
bool switch2_string_bytes_match_pinned_capture() {
|
|
// Given/When: the known language and ASCII strings are inspected.
|
|
static constexpr uint8_t language[] = {0x09, 0x04};
|
|
static constexpr uint8_t manufacturer[] = "Nintendo";
|
|
static constexpr uint8_t product[] = "Switch 2 Pro Controller";
|
|
static constexpr uint8_t serial[] = "00";
|
|
|
|
// Then: exported lengths exclude the C terminator and bytes remain exact.
|
|
CHECK(bytes_equal(switch2_string_language, switch2_string_language_length,
|
|
language, sizeof(language)));
|
|
CHECK(bytes_equal(switch2_string_manufacturer, switch2_string_manufacturer_length,
|
|
manufacturer, sizeof(manufacturer) - 1));
|
|
CHECK(bytes_equal(switch2_string_product, switch2_string_product_length,
|
|
product, sizeof(product) - 1));
|
|
CHECK(bytes_equal(switch2_string_serial, switch2_string_serial_length,
|
|
serial, sizeof(serial) - 1));
|
|
CHECK(switch2_string_manufacturer[switch2_string_manufacturer_length] == 0);
|
|
CHECK(switch2_string_product[switch2_string_product_length] == 0);
|
|
CHECK(switch2_string_serial[switch2_string_serial_length] == 0);
|
|
return true;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void run_switch2_descriptor_tests(TestRunner& runner) {
|
|
runner.run("Switch 2 descriptor exact bytes", switch2_descriptor_bytes_match_pinned_capture_subset);
|
|
runner.run("Switch 2 configuration iteration", switch2_configuration_iterates_two_interfaces_and_four_endpoints);
|
|
runner.run("Switch 2 HID report sizes", switch2_hid_reports_are_exactly_sixty_three_payload_bytes);
|
|
runner.run("Switch 2 string bytes", switch2_string_bytes_match_pinned_capture);
|
|
}
|