6 <script src=
"/resources/testharness.js"></script>
7 <script src=
"/resources/testharnessreport.js"></script>
8 <script src=
"resources/manual.js"></script>
12 These tests require a USB device to be connected.
15 const kGetDescriptorRequest
= 0x06;
17 const kDeviceDescriptorType
= 0x01;
18 const kDeviceDescriptorLength
= 18;
20 const kConfigurationDescriptorType
= 0x02;
21 const kConfigurationDescriptorLength
= 9;
23 const kStringDescriptorType
= 0x03;
24 const kStringDescriptorMaxLength
= 0xFF;
26 const kInterfaceDescriptorType
= 0x04;
27 const kInterfaceDescriptorLength
= 9;
29 const kEndpointDescriptorType
= 0x05;
30 const kEndpointDescriptorLength
= 7;
33 let pending_subtests
= 0;
34 const string_tests
= new Set();
35 const string_languages
= [];
37 async
function subtest_complete() {
38 if (--pending_subtests
== 0) {
43 function manual_usb_subtest(func
, name
, properties
) {
45 promise_test(async (test
) => {
46 test
.add_cleanup(subtest_complete
);
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
)) {
57 string_tests
.add(index
);
59 const string_values
= new Set();
60 const decoder
= new TextDecoder('utf-16le');
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) {
67 if (string_values
.size
== string_languages
.length
) {
68 assert_true(string_values
.has(expected
));
73 const result
= await device
.controlTransferIn({
74 requestType
: 'standard',
76 request
: kGetDescriptorRequest
,
77 value
: kStringDescriptorType
<< 8 | index
,
79 }, kStringDescriptorMaxLength
);
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
,
87 const string_buffer
= new Uint8Array(
88 result
.data
.buffer
, result
.data
.byteOffset
+ 2, length
- 2);
89 string_values
.add(decoder
.decode(string_buffer
));
91 `Read string descriptor ${index} in language 0x${language_string}`);
95 function check_interface_descriptor(configuration
, data
) {
96 assert_greater_than_equal(
97 data
.getUint8(0), kInterfaceDescriptorLength
, 'descriptor length');
99 const interface_number
= data
.getUint8(2);
100 const iface
= configuration
.interfaces
.find((iface
) => {
101 return iface
.interfaceNumber
== interface_number
;
104 iface
, undefined, `unknown interface ${interface_number}`);
106 const alternate_setting
= data
.getUint8(3);
107 const alternate
= iface
.alternates
.find((alternate
) => {
108 return alternate
.alternateSetting
== alternate_setting
;
111 alternate
, undefined, `unknown alternate ${alternate_setting}`);
113 assert_equals(data
.getUint8(4), alternate
.endpoints
.length
,
114 'number of endpoints');
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');
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
);
132 function check_endpoint_descriptor(alternate
, data
) {
133 assert_greater_than_equal(
134 data
.getUint8(0), kEndpointDescriptorLength
, 'descriptor length');
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
;
144 endpoint
, undefined, `unknown endpoint ${endpoint_number}`);
146 const attributes
= data
.getUint8(3);
147 switch (attributes
& 0x03) {
149 assert_equals(endpoint
.type
, 'control', 'endpoint type');
152 assert_equals(endpoint
.type
, 'isochronous', 'endpoint type');
155 assert_equals(endpoint
.type
, 'bulk', 'endpoint type');
158 assert_equals(endpoint
.type
, 'interrupt', 'endpoint type');
162 assert_equals(data
.getUint16(4, /*littleEndian=*/true),
163 endpoint
.packetSize
, 'packet size');
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
;
171 assert_not_equals(configuration
, undefined);
173 let result
= await device
.controlTransferIn({
174 requestType
: 'standard',
176 request
: kGetDescriptorRequest
,
177 value
: kConfigurationDescriptorType
<< 8 | (config_value
- 1),
179 }, kConfigurationDescriptorLength
);
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);
188 result
= await device
.controlTransferIn({
189 requestType
: 'standard',
191 request
: kGetDescriptorRequest
,
192 value
: kConfigurationDescriptorType
<< 8 | (config_value
- 1),
196 assert_equals(result
.status
, 'ok', 'transfer status');
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
,
202 assert_equals(result
.data
.getUint16(2, /*littleEndian=*/true),
203 total_length
, 'total length');
205 result
.data
.getUint8(4), configuration
.interfaces
.length
,
206 'number of interfaces');
208 result
.data
.getUint8(5), config_value
, 'configuration value');
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
);
218 let alternate
= undefined;
219 while (offset
< total_length
) {
220 length
= result
.data
.getUint8(offset
);
221 assert_less_than_equal(offset
+ length
, total_length
);
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');
229 case kInterfaceDescriptorType
:
230 alternate
= check_interface_descriptor(configuration
, view
);
232 case kEndpointDescriptorType
:
233 assert_not_equals(alternate
, undefined,
234 'endpoint not defined after interface');
235 check_endpoint_descriptor(alternate
, view
);
241 }, `Read config descriptor ${config_value}`);
244 function read_string_descriptor_languages(device_descriptor
) {
245 manual_usb_subtest(async (t
) => {
246 const result
= await device
.controlTransferIn({
247 requestType
: 'standard',
249 request
: kGetDescriptorRequest
,
250 value
: kStringDescriptorType
<< 8,
252 }, kStringDescriptorMaxLength
);
254 assert_equals(result
.status
, 'ok', 'transfer status');
255 assert_equals(result
.data
.getUint8(1), kStringDescriptorType
,
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');
262 for (let index
= 2; index
< length
; index
+= 2) {
263 string_languages
.push(
264 result
.data
.getUint16(index
, /*littleEndian=*/true));
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
);
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
);
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
);
285 const num_configurations
= device_descriptor
.getUint8(17);
286 for (let config_value
= 1; config_value
<= num_configurations
;
288 read_config_descriptor(config_value
);
290 }, `Read supported languages`);
293 promise_test(async (t
) => {
294 device
= await
getDeviceForManualTest();
297 const result
= await device
.controlTransferIn({
298 requestType
: 'standard',
300 request
: kGetDescriptorRequest
,
301 value
: kDeviceDescriptorType
<< 8,
303 }, kDeviceDescriptorLength
);
305 assert_equals(result
.status
, 'ok', 'transfer status');
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
,
313 const bcd_usb
= result
.data
.getUint16(2, /*littleEndian=*/true);
315 bcd_usb
>> 8, device
.usbVersionMajor
, 'USB version major');
317 (bcd_usb
& 0xf0) >> 4, device
.usbVersionMinor
, 'USB version minor');
319 bcd_usb
& 0xf, device
.usbVersionSubminor
, 'USV version subminor');
321 result
.data
.getUint8(4), device
.deviceClass
, 'device class');
323 result
.data
.getUint8(5), device
.deviceSubclass
, 'device subclass');
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);
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');
340 read_string_descriptor_languages(result
.data
);
342 if (pending_subtests
== 0) {
343 await device
.close();
345 }, 'Read device descriptor');