Commit dcca237a authored by Reilly Grant's avatar Reilly Grant Committed by Chromium LUCI CQ

usb: Add a manual test for USB transfer completion order

This manual test attempts to reproduce a case seen in issue 1153647
where parallel calls to transferIn(), recommended for performance, can
return data out of order.

This test is a starting point in the investigation as it only sends a
small amount of data while the original report involves much larger
transfers.

Bug: 1153647
Change-Id: I4f4b8f5ad502d08ae7c4fb9cc22889047029afc4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2587950
Commit-Queue: Reilly Grant <reillyg@chromium.org>
Reviewed-by: default avatarJames Hollyer <jameshollyer@chromium.org>
Auto-Submit: Reilly Grant <reillyg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#841773}
parent f2b37712
......@@ -2183,6 +2183,7 @@ external/wpt/native-file-system/showSaveFilePicker-manual.https.html [ Skip ]
# These tests require a physical device connected.
external/wpt/webusb/usbDevice_claimInterface-manual.https.html [ Skip ]
external/wpt/webusb/usbDevice_transferIn-manual.https.html [ Skip ]
wpt_internal/hid/hidDevice_collections-manual.https.html [ Skip ]
external/wpt/serial/serialPort_loopback-manual.https.html [ Skip ]
external/wpt/serial/serialPort_loopback_BreakError-manual.https.html [ Skip ]
......
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/manual.js"></script>
</head>
<body>
<p>
This test requires a USB device implementing the USB CDC-ACM protocol
configured to loop back TX to RX. For example, this Arduino sketch could
be used:
<pre>
void setup() {
Serial.begin(115200);
while (!Serial) {
;
}
}
void loop() {
if (Serial.available()) {
Serial.write(Serial.read());
}
}
</pre>
</p>
<script>
manual_usb_test(async (t, device) => {
await device.open();
t.add_cleanup(async () => {
if (device.opened) {
await device.close();
}
});
await device.selectConfiguration(1);
let controlInterface = undefined;
for (const iface of device.configuration.interfaces) {
const alternate = iface.alternates[0];
if (alternate.interfaceClass == 2 &&
alternate.interfaceSubclass == 2 &&
alternate.interfaceProtocol == 0) {
controlInterface = iface;
break;
}
}
assert_not_equals(controlInterface, undefined,
'No control interface found.');
let dataInterface = undefined;
for (const iface of device.configuration.interfaces) {
const alternate = iface.alternates[0];
if (alternate.interfaceClass == 10 &&
alternate.interfaceSubclass == 0 &&
alternate.interfaceProtocol == 0) {
dataInterface = iface;
break;
}
}
assert_not_equals(dataInterface, undefined, 'No data interface found.');
await device.claimInterface(controlInterface.interfaceNumber);
await device.claimInterface(dataInterface.interfaceNumber);
let inEndpoint = undefined;
for (const endpoint of dataInterface.alternate.endpoints) {
if (endpoint.type == 'bulk' && endpoint.direction == 'in') {
inEndpoint = endpoint;
break;
}
}
assert_not_equals(inEndpoint, undefined, 'No IN endpoint found.');
let outEndpoint = undefined;
for (const endpoint of dataInterface.alternate.endpoints) {
if (endpoint.type == 'bulk' && endpoint.direction == 'out') {
outEndpoint = endpoint;
break;
}
}
assert_not_equals(outEndpoint, undefined, 'No OUT endpoint found.');
// Execute a SET_CONTROL_LINE_STATE command to let the device know the
// host is ready to transmit and receive data.
await device.controlTransferOut({
requestType: 'class',
recipient: 'interface',
request: 0x22,
value: 0x01,
index: controlInterface.interfaceNumber,
});
// Set up two IN transfers which should complete in order.
const transfer1 =
device.transferIn(inEndpoint.endpointNumber, inEndpoint.packetSize);
const transfer2 =
device.transferIn(inEndpoint.endpointNumber, inEndpoint.packetSize);
// Write a single byte to the port which should be echoed to complete
// transfer1.
let result = await device.transferOut(
outEndpoint.endpointNumber, new Uint8Array(['a'.charCodeAt(0)]));
assert_equals(result.status, 'ok');
assert_equals(result.bytesWritten, 1);
result = await transfer1;
assert_equals(result.status, 'ok');
assert_not_equals(result.data, null);
assert_equals(result.data.byteLength, 1);
assert_equals(result.data.getUint8(0), 'a'.charCodeAt(0));
// Set up a third IN transfer which will be canceled when the device is
// closed at the end of the test.
const transfer3 = promise_rejects_dom(
t, 'NetworkError',
device.transferIn(inEndpoint.endpointNumber,
inEndpoint.packetSize));
// Write a single byte to the port which should be echoed to complete
// transfer2.
result = await device.transferOut(
outEndpoint.endpointNumber, new Uint8Array(['b'.charCodeAt(0)]));
assert_equals(result.status, 'ok');
assert_equals(result.bytesWritten, 1);
result = await transfer2;
assert_equals(result.status, 'ok');
assert_not_equals(result.data, null);
assert_equals(result.data.byteLength, 1);
assert_equals(result.data.getUint8(0), 'b'.charCodeAt(0));
await device.close();
await transfer3;
}, 'Multiple IN transfers on an endpoint complete in order');
</script>
</body>
</html>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment