From de48ffd54376ea0603f9446f4503757705ab2aa7 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 17 Aug 2022 21:41:26 +0100 Subject: [PATCH] tests: gadget0: Added testing for Microsoft OS descriptor retrieval and invalid request handling --- tests/gadget-zero/test_gadget0.py | 103 ++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/tests/gadget-zero/test_gadget0.py b/tests/gadget-zero/test_gadget0.py index 521207cc..8a47bc9e 100644 --- a/tests/gadget-zero/test_gadget0.py +++ b/tests/gadget-zero/test_gadget0.py @@ -47,6 +47,18 @@ DESC_TYPE_DEVICE_CAPABILITY = 0x10 DEVCAP_TYPE_PLATFORM = 5 +MICROSOFT_OS_GET_DESCRIPTOR_SET = 7 + +MICROSOFT_OS_SET_HEADER = 0 +MICROSOFT_OS_SUBSET_HEADER_CONFIGURATION = 1 +MICROSOFT_OS_SUBSET_HEADER_FUNCTION = 2 +MICROSOFT_OS_FEATURE_COMPATIBLE_ID = 3 +MICROSOFT_OS_FEATURE_REG_PROPERTY = 4 +MICROSOFT_OS_FEATURE_MIN_RESUME_TIME = 5 +MICROSOFT_OS_FEATURE_MODEL_ID = 6 +MICROSOFT_OS_FEATURE_CCGP_DEVICE = 7 +MICROSOFT_OS_FEATURE_VENDOR_REVISION = 8 + MICROSOFT_WINDOWS_VERSION_WINBLUE = 0x06030000 class find_by_serial(object): @@ -575,6 +587,97 @@ class TestBOSDescriptor(unittest.TestCase): self.assertEqual(e.errno, 32) +class TestMicrosoftOSDescriptors(unittest.TestCase): + """ + Make sure the Microsoft OS descriptor handling works per + https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-os-2-0-descriptors-specification + """ + + def setUp(self): + self.dev : usb.core.Device = usb.core.find(idVendor=VENDOR_ID, idProduct=PRODUCT_ID, custom_match=find_by_serial(DUT_SERIAL)) + self.assertIsNotNone(self.dev, "Couldn't find locm3 gadget0 device") + + def tearDown(self): + uu.dispose_resources(self.dev) + + def get_microsoft_os_descriptor(self, *, vendor_id, byte_count) -> bytes: + return self.dev.ctrl_transfer( + bmRequestType=uu.build_request_type(uu.CTRL_IN, uu.CTRL_TYPE_VENDOR, uu.CTRL_RECIPIENT_DEVICE), + bRequest=vendor_id, + wValue=0, + wIndex=MICROSOFT_OS_GET_DESCRIPTOR_SET, + data_or_wLength=byte_count + ).tobytes() + + @staticmethod + def read_le16(data : bytes): + return data[0] | (data[1] << 8) + + @staticmethod + def read_le32(data : bytes): + return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24) + + def test_partial_request(self): + # This is the Vendor Code from the BOS above (descriptor_set_info[6]) + descriptor_set = self.get_microsoft_os_descriptor(vendor_id=1, byte_count=10) + self.assertEqual(len(descriptor_set), 10) + # Check that the set descriptor returned is valid + self.assertEqual(self.read_le16(descriptor_set[0:2]), 10) + self.assertEqual(self.read_le16(descriptor_set[2:4]), MICROSOFT_OS_SET_HEADER) + self.assertEqual(self.read_le32(descriptor_set[4:8]), MICROSOFT_WINDOWS_VERSION_WINBLUE) + self.assertEqual(self.read_le16(descriptor_set[8:10]), 46) + + def test_complete_request(self): + # This is the Vendor Code from the BOS above (descriptor_set_info[6]) + descriptor_set = self.get_microsoft_os_descriptor(vendor_id=1, byte_count=46) + self.assertEqual(len(descriptor_set), 46) + # Check that the set descriptor returned is valid + self.assertEqual(self.read_le16(descriptor_set[0:2]), 10) + self.assertEqual(self.read_le16(descriptor_set[2:4]), MICROSOFT_OS_SET_HEADER) + self.assertEqual(self.read_le32(descriptor_set[4:8]), MICROSOFT_WINDOWS_VERSION_WINBLUE) + self.assertEqual(self.read_le16(descriptor_set[8:10]), 46) + + # Check that the config subset descriptor returned is valid + config_subset = descriptor_set[10:] + self.assertEqual(self.read_le16(config_subset[0:2]), 8) + self.assertEqual(self.read_le16(config_subset[2:4]), MICROSOFT_OS_SUBSET_HEADER_CONFIGURATION) + self.assertEqual(config_subset[4], 1) # bConfigurationValue + self.assertEqual(config_subset[5], 0) + self.assertEqual(self.read_le16(config_subset[6:8]), 36) + + # Check that the function subset descriptor returned is valid + function_subset = config_subset[8:] + self.assertEqual(self.read_le16(function_subset[0:2]), 8) + self.assertEqual(self.read_le16(function_subset[2:4]), MICROSOFT_OS_SUBSET_HEADER_FUNCTION) + self.assertEqual(function_subset[4], 0) # bFirstInterface + self.assertEqual(function_subset[5], 0) + self.assertEqual(self.read_le16(function_subset[6:8]), 28) + + # Check that the feature descriptor is a Compatible ID descriptor and valid + feature = function_subset[8:] + self.assertEqual(self.read_le16(feature[0:2]), 20) + self.assertEqual(self.read_le16(feature[2:4]), MICROSOFT_OS_FEATURE_COMPATIBLE_ID) + self.assertEqual(feature[4:12], b'WINUSB\x00\x00') + self.assertEqual(feature[12:20], b'\x00' * 8) + + def test_invalid_request(self): + try: + self.get_microsoft_os_descriptor(vendor_id=0, byte_count=10) + self.fail("get_microsoft_os_descriptor() for an invalid descriptor set request suceeded") + except usb.core.USBError as e: + # Make sure we got a pipe error (EP0 STALL) + # Why libusb returns stalls as this and makes them fatal is.. a subject for a different place.. + self.assertEqual(e.errno, 32) + + try: + self.get_microsoft_os_descriptor(vendor_id=2, byte_count=0) + self.fail("get_microsoft_os_descriptor() for an invalid descriptor set request suceeded") + except usb.core.USBError as e: + # Make sure we got a pipe error (EP0 STALL) + # Why libusb returns stalls as this and makes them fatal is.. a subject for a different place.. + self.assertEqual(e.errno, 32) + + def run_ci_test(dut): # Avoids the import for non-CI users! import xmlrunner