#include "test_support.h" #include #include #include #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(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); }