usbDevice_controlTransferIn-manual.https.html (13783B)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 <script src="/resources/testharness.js"></script> 7 <script src="/resources/testharnessreport.js"></script> 8 <script src="resources/manual.js"></script> 9 </head> 10 <body> 11 <p> 12 These tests require a USB device to be connected. 13 </p> 14 <script> 15 const kGetDescriptorRequest = 0x06; 16 17 const kDeviceDescriptorType = 0x01; 18 const kDeviceDescriptorLength = 18; 19 20 const kConfigurationDescriptorType = 0x02; 21 const kConfigurationDescriptorLength = 9; 22 23 const kStringDescriptorType = 0x03; 24 const kStringDescriptorMaxLength = 0xFF; 25 26 const kInterfaceDescriptorType = 0x04; 27 const kInterfaceDescriptorLength = 9; 28 29 const kEndpointDescriptorType = 0x05; 30 const kEndpointDescriptorLength = 7; 31 32 let device = null; 33 let pending_subtests = 0; 34 const string_tests = new Set(); 35 const string_languages = []; 36 37 async function subtest_complete() { 38 if (--pending_subtests == 0) { 39 await device.close(); 40 } 41 } 42 43 function manual_usb_subtest(func, name, properties) { 44 pending_subtests++; 45 promise_test(async (test) => { 46 test.add_cleanup(subtest_complete); 47 await func(test); 48 }, name, properties); 49 } 50 51 function read_string(index, expected) { 52 // A device may use the same string in multiple places. Don't bother 53 // repeating the test. 54 if (string_tests.has(index)) { 55 return; 56 } 57 string_tests.add(index); 58 59 const string_values = new Set(); 60 const decoder = new TextDecoder('utf-16le'); 61 62 for (const language of string_languages) { 63 const language_string = language.toString(16).padStart(4, '0'); 64 manual_usb_subtest(async (t) => { 65 if (expected != undefined) { 66 t.add_cleanup(() => { 67 if (string_values.size == string_languages.length) { 68 assert_true(string_values.has(expected)); 69 } 70 }); 71 } 72 73 const result = await device.controlTransferIn({ 74 requestType: 'standard', 75 recipient: 'device', 76 request: kGetDescriptorRequest, 77 value: kStringDescriptorType << 8 | index, 78 index: language 79 }, kStringDescriptorMaxLength); 80 81 assert_equals(result.status, 'ok', 'transfer status'); 82 const length = result.data.getUint8(0); 83 assert_greater_than_equal(length, 2, 'descriptor length'); 84 assert_equals(result.data.byteLength, length, 'transfer length'); 85 assert_equals(result.data.getUint8(1), kStringDescriptorType, 86 'descriptor type'); 87 const string_buffer = new Uint8Array( 88 result.data.buffer, result.data.byteOffset + 2, length - 2); 89 string_values.add(decoder.decode(string_buffer)); 90 }, 91 `Read string descriptor ${index} in language 0x${language_string}`); 92 } 93 } 94 95 function check_interface_descriptor(configuration, data) { 96 assert_greater_than_equal( 97 data.getUint8(0), kInterfaceDescriptorLength, 'descriptor length'); 98 99 const interface_number = data.getUint8(2); 100 const iface = configuration.interfaces.find((iface) => { 101 return iface.interfaceNumber == interface_number; 102 }); 103 assert_not_equals( 104 iface, undefined, `unknown interface ${interface_number}`); 105 106 const alternate_setting = data.getUint8(3); 107 const alternate = iface.alternates.find((alternate) => { 108 return alternate.alternateSetting == alternate_setting; 109 }); 110 assert_not_equals( 111 alternate, undefined, `unknown alternate ${alternate_setting}`); 112 113 assert_equals(data.getUint8(4), alternate.endpoints.length, 114 'number of endpoints'); 115 assert_equals( 116 data.getUint8(5), alternate.interfaceClass, 'interface class'); 117 assert_equals(data.getUint8(6), alternate.interfaceSubclass, 118 'interface subclass'); 119 assert_equals(data.getUint8(7), alternate.interfaceProtocol, 120 'interface protocol'); 121 122 const interface_string = data.getUint8(8); 123 if (interface_string != 0) { 124 // TODO(crbug.com/727819): Check that the string descriptor matches 125 // iface.interfaceName. 126 read_string(interface_string); 127 } 128 129 return alternate; 130 } 131 132 function check_endpoint_descriptor(alternate, data) { 133 assert_greater_than_equal( 134 data.getUint8(0), kEndpointDescriptorLength, 'descriptor length'); 135 136 const endpoint_address = data.getUint8(2); 137 const direction = endpoint_address & 0x80 ? 'in' : 'out'; 138 const endpoint_number = endpoint_address & 0x0f; 139 const endpoint = alternate.endpoints.find((endpoint) => { 140 return endpoint.direction == direction && 141 endpoint.endpointNumber == endpoint_number; 142 }); 143 assert_not_equals( 144 endpoint, undefined, `unknown endpoint ${endpoint_number}`); 145 146 const attributes = data.getUint8(3); 147 switch (attributes & 0x03) { 148 case 0: 149 assert_equals(endpoint.type, 'control', 'endpoint type'); 150 break; 151 case 1: 152 assert_equals(endpoint.type, 'isochronous', 'endpoint type'); 153 break; 154 case 2: 155 assert_equals(endpoint.type, 'bulk', 'endpoint type'); 156 break; 157 case 3: 158 assert_equals(endpoint.type, 'interrupt', 'endpoint type'); 159 break; 160 } 161 162 assert_equals(data.getUint16(4, /*littleEndian=*/true), 163 endpoint.packetSize, 'packet size'); 164 } 165 166 function read_config_descriptor(config_value) { 167 manual_usb_subtest(async (t) => { 168 const configuration = device.configurations.find((config) => { 169 return config.configurationValue == config_value; 170 }); 171 assert_not_equals(configuration, undefined); 172 173 let result = await device.controlTransferIn({ 174 requestType: 'standard', 175 recipient: 'device', 176 request: kGetDescriptorRequest, 177 value: kConfigurationDescriptorType << 8 | (config_value - 1), 178 index: 0 179 }, kConfigurationDescriptorLength); 180 181 assert_equals(result.status, 'ok', 'transfer status'); 182 let length = result.data.getUint8(0); 183 assert_greater_than_equal( 184 length, kConfigurationDescriptorLength, 'descriptor length'); 185 assert_equals(result.data.byteLength, length, 'transfer length'); 186 const total_length = result.data.getUint16(2, /*littleEndian=*/true); 187 188 result = await device.controlTransferIn({ 189 requestType: 'standard', 190 recipient: 'device', 191 request: kGetDescriptorRequest, 192 value: kConfigurationDescriptorType << 8 | (config_value - 1), 193 index: 0 194 }, total_length); 195 196 assert_equals(result.status, 'ok', 'transfer status'); 197 assert_equals( 198 result.data.byteLength, total_length, 'transfer length'); 199 assert_equals(result.data.getUint8(0), length, 'descriptor length'); 200 assert_equals(result.data.getUint8(1), kConfigurationDescriptorType, 201 'descriptor type'); 202 assert_equals(result.data.getUint16(2, /*littleEndian=*/true), 203 total_length, 'total length'); 204 assert_equals( 205 result.data.getUint8(4), configuration.interfaces.length, 206 'number of interfaces'); 207 assert_equals( 208 result.data.getUint8(5), config_value, 'configuration value'); 209 210 const configuration_string = result.data.getUint8(6); 211 if (configuration_string != 0) { 212 // TODO(crbug.com/727819): Check that the string descriptor matches 213 // configuration.configurationName. 214 read_string(configuration_string); 215 } 216 217 let offset = length; 218 let alternate = undefined; 219 while (offset < total_length) { 220 length = result.data.getUint8(offset); 221 assert_less_than_equal(offset + length, total_length); 222 223 const view = new DataView( 224 result.data.buffer, result.data.byteOffset + offset, length); 225 switch (view.getUint8(1)) { 226 case kConfigurationDescriptorType: 227 assert_unreached('cannot contain multiple config descriptors'); 228 break; 229 case kInterfaceDescriptorType: 230 alternate = check_interface_descriptor(configuration, view); 231 break; 232 case kEndpointDescriptorType: 233 assert_not_equals(alternate, undefined, 234 'endpoint not defined after interface'); 235 check_endpoint_descriptor(alternate, view); 236 break; 237 } 238 239 offset += length; 240 } 241 }, `Read config descriptor ${config_value}`); 242 } 243 244 function read_string_descriptor_languages(device_descriptor) { 245 manual_usb_subtest(async (t) => { 246 const result = await device.controlTransferIn({ 247 requestType: 'standard', 248 recipient: 'device', 249 request: kGetDescriptorRequest, 250 value: kStringDescriptorType << 8, 251 index: 0 252 }, kStringDescriptorMaxLength); 253 254 assert_equals(result.status, 'ok', 'transfer status'); 255 assert_equals(result.data.getUint8(1), kStringDescriptorType, 256 'descriptor type'); 257 const length = result.data.getUint8(0); 258 assert_greater_than_equal(length, 2, 'descriptor length') 259 assert_greater_than_equal( 260 result.data.byteLength, length, 'transfer length'); 261 262 for (let index = 2; index < length; index += 2) { 263 string_languages.push( 264 result.data.getUint16(index, /*littleEndian=*/true)); 265 } 266 267 const manufacturer_string = device_descriptor.getUint8(14); 268 if (manufacturer_string != 0) { 269 assert_not_equals(device.manufacturerName, undefined); 270 read_string(manufacturer_string, device.manufacturerName); 271 } 272 273 const product_string = device_descriptor.getUint8(15); 274 if (product_string != 0) { 275 assert_not_equals(device.productName, undefined); 276 read_string(product_string, device.productName); 277 } 278 279 const serial_number_string = device_descriptor.getUint8(16); 280 if (serial_number_string != 0) { 281 assert_not_equals(device.serialNumber, undefined); 282 read_string(serial_number_string, device.serialNumber); 283 } 284 285 const num_configurations = device_descriptor.getUint8(17); 286 for (let config_value = 1; config_value <= num_configurations; 287 ++config_value) { 288 read_config_descriptor(config_value); 289 } 290 }, `Read supported languages`); 291 } 292 293 promise_test(async (t) => { 294 device = await getDeviceForManualTest(); 295 await device.open(); 296 297 const result = await device.controlTransferIn({ 298 requestType: 'standard', 299 recipient: 'device', 300 request: kGetDescriptorRequest, 301 value: kDeviceDescriptorType << 8, 302 index: 0, 303 }, kDeviceDescriptorLength); 304 305 assert_equals(result.status, 'ok', 'transfer status'); 306 assert_equals( 307 result.data.byteLength, kDeviceDescriptorLength, 'transfer length'); 308 assert_greater_than_equal( 309 result.data.getUint8(0), 310 kDeviceDescriptorLength, 'descriptor length'); 311 assert_equals(result.data.getUint8(1), kDeviceDescriptorType, 312 'descriptor type'); 313 const bcd_usb = result.data.getUint16(2, /*littleEndian=*/true); 314 assert_equals( 315 bcd_usb >> 8, device.usbVersionMajor, 'USB version major'); 316 assert_equals( 317 (bcd_usb & 0xf0) >> 4, device.usbVersionMinor, 'USB version minor'); 318 assert_equals( 319 bcd_usb & 0xf, device.usbVersionSubminor, 'USV version subminor'); 320 assert_equals( 321 result.data.getUint8(4), device.deviceClass, 'device class'); 322 assert_equals( 323 result.data.getUint8(5), device.deviceSubclass, 'device subclass'); 324 assert_equals( 325 result.data.getUint8(6), device.deviceProtocol, 'device protocol'); 326 assert_equals(result.data.getUint16(8, /*littleEndian=*/true), 327 device.vendorId, 'vendor id'); 328 assert_equals(result.data.getUint16(10, /*littleEndian=*/true), 329 device.productId, 'product id'); 330 const bcd_device = result.data.getUint16(12, /*littleEndian=*/true); 331 assert_equals( 332 bcd_device >> 8, device.deviceVersionMajor, 'device version major'); 333 assert_equals((bcd_device & 0xf0) >> 4, device.deviceVersionMinor, 334 'device version minor'); 335 assert_equals(bcd_device & 0xf, device.deviceVersionSubminor, 336 'device version subminor'); 337 assert_equals(result.data.getUint8(17), device.configurations.length, 338 'number of configurations'); 339 340 read_string_descriptor_languages(result.data); 341 342 if (pending_subtests == 0) { 343 await device.close(); 344 } 345 }, 'Read device descriptor'); 346 </script> 347 </body> 348 </html>