blob: a86d437f91a51b081776cd43b81dd4a6472ddb9e [file] [log] [blame]
<!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>
These tests require a supported HID device:
</p>
<ul>
<li>Sony DS4 Wireless Controller v2 (054c:09cc) [USB]
<li>Sony DualSense Wireless Controller (054c:0ce6) [USB]
<li>Nintendo Switch Pro Controller (0573:2009) [USB]
<li>Philips Speech Mike III (0911:0fa0) [USB]
<li>Jabra Evolve 65 Stereo Headset with Link (0b0e:0306) [USB]
<li>Google Stadia Controller (18d1:9400) [USB]
</ul>
<script>
// Formats a 32-bit integer |value| in hexadecimal with leading zeros.
const hex32 = value => {
return ('00000000' + value.toString(16)).substr(-8);
};
// Finds the item in |report| with matching |usage|, computes its bit
// index within the report map, and checks against the |expectedBitIndex|
// and |expectedBitWidth|. If a usage is reused for multiple fields within
// a report, only the first field is considered.
const checkReportUsage =
(report, usage, expectedBitIndex, expectedBitWidth) => {
const itemIndex = report.items.findIndex(item => {
if (item.isRange)
return item.usageMinimum <= usage && usage <= item.usageMaximum;
return item.usages.includes(usage);
});
assert_greater_than_equal(
itemIndex, 0,
'No report item matching usage 0x' + hex32(usage) + '.');
if (itemIndex < 0)
return null;
let bitIndex = 0;
for (let i = 0; i < itemIndex; ++i)
bitIndex += report.items[i].reportSize * report.items[i].reportCount;
const item = report.items[itemIndex];
if (!item.isArray) {
if (item.isRange) {
bitIndex += (usage - item.usageMinimum) * item.reportSize;
} else {
const usageIndex = item.usages.indexOf(usage);
bitIndex += usageIndex * item.reportSize;
}
}
assert_equals(bitIndex, expectedBitIndex,
'Incorrect bit index for usage 0x' + hex32(usage) + '.');
assert_equals(item.reportSize, expectedBitWidth,
'Incorrect bit width for usage 0x' + hex32(usage) + '.');
};
// Returns the first top-level collection in |device.collections| with a
// usage matching |usagePage| and |usage|, or undefined if no matching
// collection was found.
const getCollectionByUsage = (device, usagePage, usage) => {
return device.collections.find(c => {
return c.usagePage == usagePage && c.usage == usage;});
}
// Returns the first device in |devices| with a top-level collection
// matching |usagePage| and |usage|, or undefined if no matching device
// was found.
const getDeviceByCollectionUsage = (devices, usagePage, usage) => {
return devices.find(d => {
return getCollectionByUsage(d, usagePage, usage);
}) !== undefined;
}
// Returns the first report in |devices| with matching |reportType| and
// |reportId|, or undefined if no matching report was found.
const getReport = (devices, reportType, reportId) => {
for (const d of devices) {
for (const c of d.collections) {
let reports = [];
if (reportType == 'input')
reports = c.inputReports;
else if (reportType == 'output')
reports = c.outputReports;
else if (reportType == 'feature')
reports = c.featureReports;
const r = reports.find(r => { return r.reportId == reportId; });
if (r !== undefined)
return r;
}
}
return undefined;
};
// Returns true if |devices| contains a device with matching |vendorId|
// and |productId|.
const hasDeviceIds = (devices, vendorId, productId) => {
return devices.find(d => {
return d.vendorId == vendorId && d.productId == productId;
}) !== undefined;
};
const checkReportMapDualshock4 = devices => {
// Expect one device with one top-level collection.
assert_equals(devices.length, 1, 'device count');
assert_equals(devices[0].collections.length, 1, 'collection count');
const collection = getCollectionByUsage(devices[0], 0x0001, 0x0005);
assert_not_equals(collection, undefined, 'game pad collection');
// Input report
const input01 = getReport(devices, 'input', 0x01);
assert_not_equals(input01, undefined, 'input report 0x01');
checkReportUsage(input01, 0x00010030, 0, 8);
checkReportUsage(input01, 0x00010031, 8, 8);
checkReportUsage(input01, 0x00010032, 16, 8);
checkReportUsage(input01, 0x00010035, 24, 8);
checkReportUsage(input01, 0x00010039, 32, 4);
checkReportUsage(input01, 0x00090001, 36, 1);
checkReportUsage(input01, 0x00090002, 37, 1);
checkReportUsage(input01, 0x00090003, 38, 1);
checkReportUsage(input01, 0x00090004, 39, 1);
checkReportUsage(input01, 0x00090005, 40, 1);
checkReportUsage(input01, 0x00090006, 41, 1);
checkReportUsage(input01, 0x00090007, 42, 1);
checkReportUsage(input01, 0x00090008, 43, 1);
checkReportUsage(input01, 0x00090009, 44, 1);
checkReportUsage(input01, 0x0009000a, 45, 1);
checkReportUsage(input01, 0x0009000b, 46, 1);
checkReportUsage(input01, 0x0009000c, 47, 1);
checkReportUsage(input01, 0x0009000d, 48, 1);
checkReportUsage(input01, 0x0009000e, 49, 1);
checkReportUsage(input01, 0xff000020, 50, 6);
checkReportUsage(input01, 0x00010033, 56, 8);
checkReportUsage(input01, 0x00010034, 64, 8);
// Output report
const output05 = getReport(devices, 'output', 0x05);
assert_not_equals(output05, undefined, 'output report 0x05');
checkReportUsage(output05, 0xff000022, 0, 8);
// Feature reports
const feature02 = getReport(devices, 'feature', 0x02);
assert_not_equals(feature02, undefined, 'feature report 0x02');
checkReportUsage(feature02, 0xff000024, 0, 8);
const feature04 = getReport(devices, 'feature', 0x04);
assert_not_equals(feature04, undefined, 'feature report 0x04');
checkReportUsage(feature04, 0xff000023, 0, 8);
const feature08 = getReport(devices, 'feature', 0x08);
assert_not_equals(feature08, undefined, 'feature report 0x08');
checkReportUsage(feature08, 0xff000025, 0, 8);
const feature10 = getReport(devices, 'feature', 0x10);
assert_not_equals(feature10, undefined, 'feature report 0x10');
checkReportUsage(feature10, 0xff000026, 0, 8);
const feature11 = getReport(devices, 'feature', 0x11);
assert_not_equals(feature11, undefined, 'feature report 0x11');
checkReportUsage(feature11, 0xff000027, 0, 8);
const feature12 = getReport(devices, 'feature', 0x12);
assert_not_equals(feature12, undefined, 'feature report 0x12');
checkReportUsage(feature12, 0xff020021, 0, 8);
const feature13 = getReport(devices, 'feature', 0x13);
assert_not_equals(feature13, undefined, 'feature report 0x13');
checkReportUsage(feature13, 0xff020022, 0, 8);
const feature14 = getReport(devices, 'feature', 0x14);
assert_not_equals(feature14, undefined, 'feature report 0x14');
checkReportUsage(feature14, 0xff050020, 0, 8);
const feature15 = getReport(devices, 'feature', 0x15);
assert_not_equals(feature15, undefined, 'feature report 0x15');
checkReportUsage(feature15, 0xff050021, 0, 8);
const feature80 = getReport(devices, 'feature', 0x80);
assert_not_equals(feature80, undefined, 'feature report 0x80');
checkReportUsage(feature80, 0xff800020, 0, 8);
const feature81 = getReport(devices, 'feature', 0x81);
assert_not_equals(feature81, undefined, 'feature report 0x81');
checkReportUsage(feature81, 0xff800021, 0, 8);
const feature82 = getReport(devices, 'feature', 0x82);
assert_not_equals(feature82, undefined, 'feature report 0x82');
checkReportUsage(feature82, 0xff800022, 0, 8);
const feature83 = getReport(devices, 'feature', 0x83);
assert_not_equals(feature83, undefined, 'feature report 0x83');
checkReportUsage(feature83, 0xff800023, 0, 8);
const feature84 = getReport(devices, 'feature', 0x84);
assert_not_equals(feature84, undefined, 'feature report 0x84');
checkReportUsage(feature84, 0xff800024, 0, 8);
const feature85 = getReport(devices, 'feature', 0x85);
assert_not_equals(feature85, undefined, 'feature report 0x85');
checkReportUsage(feature85, 0xff800025, 0, 8);
const feature86 = getReport(devices, 'feature', 0x86);
assert_not_equals(feature86, undefined, 'feature report 0x86');
checkReportUsage(feature86, 0xff800026, 0, 8);
const feature87 = getReport(devices, 'feature', 0x87);
assert_not_equals(feature87, undefined, 'feature report 0x87');
checkReportUsage(feature87, 0xff800027, 0, 8);
const feature88 = getReport(devices, 'feature', 0x88);
assert_not_equals(feature88, undefined, 'feature report 0x88');
checkReportUsage(feature88, 0xff800028, 0, 8);
const feature89 = getReport(devices, 'feature', 0x89);
assert_not_equals(feature89, undefined, 'feature report 0x89');
checkReportUsage(feature89, 0xff800029, 0, 8);
const feature90 = getReport(devices, 'feature', 0x90);
assert_not_equals(feature90, undefined, 'feature report 0x90');
checkReportUsage(feature90, 0xff800030, 0, 8);
const feature91 = getReport(devices, 'feature', 0x91);
assert_not_equals(feature91, undefined, 'feature report 0x91');
checkReportUsage(feature91, 0xff800031, 0, 8);
const feature92 = getReport(devices, 'feature', 0x92);
assert_not_equals(feature92, undefined, 'feature report 0x92');
checkReportUsage(feature92, 0xff800032, 0, 8);
const feature93 = getReport(devices, 'feature', 0x93);
assert_not_equals(feature93, undefined, 'feature report 0x93');
checkReportUsage(feature93, 0xff800033, 0, 8);
const feature94 = getReport(devices, 'feature', 0x94);
assert_not_equals(feature94, undefined, 'feature report 0x94');
checkReportUsage(feature94, 0xff800034, 0, 8);
const featurea0 = getReport(devices, 'feature', 0xa0);
assert_not_equals(featurea0, undefined, 'feature report 0xa0');
checkReportUsage(featurea0, 0xff800040, 0, 8);
const featurea1 = getReport(devices, 'feature', 0xa1);
assert_not_equals(featurea1, undefined, 'feature report 0xa1');
checkReportUsage(featurea1, 0xff800041, 0, 8);
const featurea2 = getReport(devices, 'feature', 0xa2);
assert_not_equals(featurea2, undefined, 'feature report 0xa2');
checkReportUsage(featurea2, 0xff800042, 0, 8);
const featurea3 = getReport(devices, 'feature', 0xa3);
assert_not_equals(featurea3, undefined, 'feature report 0xa3');
checkReportUsage(featurea3, 0xff800043, 0, 8);
const featurea4 = getReport(devices, 'feature', 0xa4);
assert_not_equals(featurea4, undefined, 'feature report 0xa4');
checkReportUsage(featurea4, 0xff800044, 0, 8);
const featurea7 = getReport(devices, 'feature', 0xa7);
assert_not_equals(featurea7, undefined, 'feature report 0xa7');
checkReportUsage(featurea7, 0xff80004a, 0, 8);
const featurea8 = getReport(devices, 'feature', 0xa8);
assert_not_equals(featurea8, undefined, 'feature report 0xa8');
checkReportUsage(featurea8, 0xff80004b, 0, 8);
const featurea9 = getReport(devices, 'feature', 0xa9);
assert_not_equals(featurea9, undefined, 'feature report 0xa9');
checkReportUsage(featurea9, 0xff80004c, 0, 8);
const featureaa = getReport(devices, 'feature', 0xaa);
assert_not_equals(featureaa, undefined, 'feature report 0xaa');
checkReportUsage(featureaa, 0xff80004e, 0, 8);
const featureab = getReport(devices, 'feature', 0xab);
assert_not_equals(featureab, undefined, 'feature report 0xab');
checkReportUsage(featureab, 0xff80004f, 0, 8);
const featureac = getReport(devices, 'feature', 0xac);
assert_not_equals(featureac, undefined, 'feature report 0xac');
checkReportUsage(featureac, 0xff800050, 0, 8);
const featuread = getReport(devices, 'feature', 0xad);
assert_not_equals(featuread, undefined, 'feature report 0xad');
checkReportUsage(featuread, 0xff800051, 0, 8);
const featureae = getReport(devices, 'feature', 0xae);
assert_not_equals(featureae, undefined, 'feature report 0xae');
checkReportUsage(featureae, 0xff800052, 0, 8);
const featureaf = getReport(devices, 'feature', 0xaf);
assert_not_equals(featureaf, undefined, 'feature report 0xaf');
checkReportUsage(featureaf, 0xff800053, 0, 8);
const featureb0 = getReport(devices, 'feature', 0xb0);
assert_not_equals(featureb0, undefined, 'feature report 0xb0');
checkReportUsage(featureb0, 0xff800054, 0, 8);
const featureb3 = getReport(devices, 'feature', 0xb3);
assert_not_equals(featureb3, undefined, 'feature report 0xb3');
checkReportUsage(featureb3, 0xff800055, 0, 8);
const featureb4 = getReport(devices, 'feature', 0xb4);
assert_not_equals(featureb4, undefined, 'feature report 0xb4');
checkReportUsage(featureb4, 0xff800055, 0, 8);
const featureb5 = getReport(devices, 'feature', 0xb5);
assert_not_equals(featureb5, undefined, 'feature report 0xb5');
checkReportUsage(featureb5, 0xff800056, 0, 8);
const featured0 = getReport(devices, 'feature', 0xd0);
assert_not_equals(featured0, undefined, 'feature report 0xd0');
checkReportUsage(featured0, 0xff800058, 0, 8);
const featured4 = getReport(devices, 'feature', 0xd4);
assert_not_equals(featured4, undefined, 'feature report 0xd4');
checkReportUsage(featured4, 0xff800059, 0, 8);
const featuree0 = getReport(devices, 'feature', 0xe0);
assert_not_equals(featuree0, undefined, 'feature report 0xe0');
checkReportUsage(featuree0, 0xff800057, 0, 8);
const featuref0 = getReport(devices, 'feature', 0xf0);
assert_not_equals(featuref0, undefined, 'feature report 0xf0');
checkReportUsage(featuref0, 0xff800047, 0, 8);
const featuref1 = getReport(devices, 'feature', 0xf1);
assert_not_equals(featuref1, undefined, 'feature report 0xf1');
checkReportUsage(featuref1, 0xff800048, 0, 8);
const featuref2 = getReport(devices, 'feature', 0xf2);
assert_not_equals(featuref2, undefined, 'feature report 0xf2');
checkReportUsage(featuref2, 0xff800049, 0, 8);
};
checkReportMapDualSense = devices => {
// Expect one device with one top-level collection.
assert_equals(devices.length, 1, 'device count');
assert_equals(devices[0].collections.length, 1, 'collection count');
const collection = getCollectionByUsage(devices[0], 0x0001, 0x0005);
assert_not_equals(collection, undefined, 'game pad collection');
// Input report
const input01 = getReport(devices, 'input', 0x01);
assert_not_equals(input01, undefined, 'input report 0x01');
checkReportUsage(input01, 0x00010030, 0, 8);
checkReportUsage(input01, 0x00010031, 8, 8);
checkReportUsage(input01, 0x00010032, 16, 8);
checkReportUsage(input01, 0x00010035, 24, 8);
checkReportUsage(input01, 0x00010033, 32, 8);
checkReportUsage(input01, 0x00010034, 40, 8);
checkReportUsage(input01, 0xff000020, 48, 8);
checkReportUsage(input01, 0x00010039, 56, 4);
checkReportUsage(input01, 0x00090001, 60, 1);
checkReportUsage(input01, 0x00090002, 61, 1);
checkReportUsage(input01, 0x00090003, 62, 1);
checkReportUsage(input01, 0x00090004, 63, 1);
checkReportUsage(input01, 0x00090005, 64, 1);
checkReportUsage(input01, 0x00090006, 65, 1);
checkReportUsage(input01, 0x00090007, 66, 1);
checkReportUsage(input01, 0x00090008, 67, 1);
checkReportUsage(input01, 0x00090009, 68, 1);
checkReportUsage(input01, 0x0009000a, 69, 1);
checkReportUsage(input01, 0x0009000b, 70, 1);
checkReportUsage(input01, 0x0009000c, 71, 1);
checkReportUsage(input01, 0x0009000d, 72, 1);
checkReportUsage(input01, 0x0009000e, 73, 1);
checkReportUsage(input01, 0x0009000f, 74, 1);
checkReportUsage(input01, 0xff000021, 75, 1);
checkReportUsage(input01, 0xff000022, 88, 8);
// Output report
const output02 = getReport(devices, 'output', 0x02);
assert_not_equals(output02, undefined, 'output report 0x02');
checkReportUsage(output02, 0xff000023, 0, 8);
// Feature reports
const feature05 = getReport(devices, 'feature', 0x05);
assert_not_equals(feature05, undefined, 'feature report 0x05');
checkReportUsage(feature05, 0xff000033, 0, 8);
const feature08 = getReport(devices, 'feature', 0x08);
assert_not_equals(feature08, undefined, 'feature report 0x08');
checkReportUsage(feature08, 0xff000034, 0, 8);
const feature09 = getReport(devices, 'feature', 0x09);
assert_not_equals(feature09, undefined, 'feature report 0x09');
checkReportUsage(feature09, 0xff000024, 0, 8);
const feature0a = getReport(devices, 'feature', 0x0a);
assert_not_equals(feature0a, undefined, 'feature report 0x0a');
checkReportUsage(feature0a, 0xff000025, 0, 8);
const feature20 = getReport(devices, 'feature', 0x20);
assert_not_equals(feature20, undefined, 'feature report 0x20');
checkReportUsage(feature20, 0xff000026, 0, 8);
const feature21 = getReport(devices, 'feature', 0x21);
assert_not_equals(feature21, undefined, 'feature report 0x21');
checkReportUsage(feature21, 0xff000027, 0, 8);
const feature22 = getReport(devices, 'feature', 0x22);
assert_not_equals(feature22, undefined, 'feature report 0x22');
checkReportUsage(feature22, 0xff000040, 0, 8);
const feature80 = getReport(devices, 'feature', 0x80);
assert_not_equals(feature80, undefined, 'feature report 0x80');
checkReportUsage(feature80, 0xff000028, 0, 8);
const feature81 = getReport(devices, 'feature', 0x81);
assert_not_equals(feature81, undefined, 'feature report 0x81');
checkReportUsage(feature81, 0xff000029, 0, 8);
const feature82 = getReport(devices, 'feature', 0x82);
assert_not_equals(feature82, undefined, 'feature report 0x82');
checkReportUsage(feature82, 0xff00002a, 0, 8);
const feature83 = getReport(devices, 'feature', 0x83);
assert_not_equals(feature83, undefined, 'feature report 0x83');
checkReportUsage(feature83, 0xff00002b, 0, 8);
const feature84 = getReport(devices, 'feature', 0x84);
assert_not_equals(feature84, undefined, 'feature report 0x84');
checkReportUsage(feature84, 0xff00002c, 0, 8);
const feature85 = getReport(devices, 'feature', 0x85);
assert_not_equals(feature85, undefined, 'feature report 0x85');
checkReportUsage(feature85, 0xff00002d, 0, 8);
const featurea0 = getReport(devices, 'feature', 0xa0);
assert_not_equals(featurea0, undefined, 'feature report 0xa0');
checkReportUsage(featurea0, 0xff00002e, 0, 8);
const featuree0 = getReport(devices, 'feature', 0xe0);
assert_not_equals(featuree0, undefined, 'feature report 0xe0');
checkReportUsage(featuree0, 0xff00002f, 0, 8);
const featuref0 = getReport(devices, 'feature', 0xf0);
assert_not_equals(featuref0, undefined, 'feature report 0xf0');
checkReportUsage(featuref0, 0xff000030, 0, 8);
const featuref1 = getReport(devices, 'feature', 0xf1);
assert_not_equals(featuref1, undefined, 'feature report 0xf1');
checkReportUsage(featuref1, 0xff000031, 0, 8);
const featuref2 = getReport(devices, 'feature', 0xf2);
assert_not_equals(featuref2, undefined, 'feature report 0xf2');
checkReportUsage(featuref2, 0xff000032, 0, 8);
const featuref4 = getReport(devices, 'feature', 0xf4);
assert_not_equals(featuref4, undefined, 'feature report 0xf4');
checkReportUsage(featuref4, 0xff000035, 0, 8);
const featuref5 = getReport(devices, 'feature', 0xf5);
assert_not_equals(featuref5, undefined, 'feature report 0xf5');
checkReportUsage(featuref5, 0xff000036, 0, 8);
};
checkReportMapSwitchPro = devices => {
// Expect one device with one top-level collection.
assert_equals(devices.length, 1, 'device count');
assert_equals(devices[0].collections.length, 1, 'collection count');
const collection = getCollectionByUsage(devices[0], 0x0001, 0x0004);
assert_not_equals(collection, undefined, 'joystick collection');
// Input reports
const input30 = getReport(devices, 'input', 0x30);
assert_not_equals(input30, undefined, 'input report 0x30');
checkReportUsage(input30, 0x00090001, 0, 1);
checkReportUsage(input30, 0x00090002, 1, 1);
checkReportUsage(input30, 0x00090003, 2, 1);
checkReportUsage(input30, 0x00090004, 3, 1);
checkReportUsage(input30, 0x00090005, 4, 1);
checkReportUsage(input30, 0x00090006, 5, 1);
checkReportUsage(input30, 0x00090007, 6, 1);
checkReportUsage(input30, 0x00090008, 7, 1);
checkReportUsage(input30, 0x00090009, 8, 1);
checkReportUsage(input30, 0x0009000a, 9, 1);
checkReportUsage(input30, 0x0009000b, 10, 1);
checkReportUsage(input30, 0x0009000c, 11, 1);
checkReportUsage(input30, 0x0009000d, 12, 1);
checkReportUsage(input30, 0x0009000e, 13, 1);
checkReportUsage(input30, 0x00010030, 16, 16);
checkReportUsage(input30, 0x00010031, 32, 16);
checkReportUsage(input30, 0x00010032, 48, 16);
checkReportUsage(input30, 0x00010035, 64, 16);
checkReportUsage(input30, 0x00010039, 80, 4);
checkReportUsage(input30, 0x0009000f, 84, 1);
checkReportUsage(input30, 0x00090010, 85, 1);
checkReportUsage(input30, 0x00090011, 86, 1);
checkReportUsage(input30, 0x00090012, 87, 1);
const input21 = getReport(devices, 'input', 0x21);
assert_not_equals(input21, undefined, 'input report 0x21');
checkReportUsage(input21, 0xff000001, 0, 8);
const input81 = getReport(devices, 'input', 0x81);
assert_not_equals(input81, undefined, 'input report 0x81');
checkReportUsage(input81, 0xff000002, 0, 8);
// Output reports
const output01 = getReport(devices, 'output', 0x01);
assert_not_equals(output01, undefined, 'output report 0x01');
checkReportUsage(output01, 0xff000003, 0, 8);
const output10 = getReport(devices, 'output', 0x10);
assert_not_equals(output10, undefined, 'output report 0x10');
checkReportUsage(output10, 0xff000004, 0, 8);
const output80 = getReport(devices, 'output', 0x80);
assert_not_equals(output80, undefined, 'output report 0x80');
checkReportUsage(output80, 0xff000005, 0, 8);
const output82 = getReport(devices, 'output', 0x82);
assert_not_equals(output82, undefined, 'output report 0x82');
checkReportUsage(output82, 0xff000006, 0, 8);
};
checkReportMapSpeechMike = devices => {
// Speech Mike exposes five HID interfaces, two of which are blocked by
// WebHID. None of the interfaces use report IDs. Distinguish the
// interfaces by their top-level collection usage information.
assert_equals(devices.length, 3, 'device count');
const device0 = getDeviceByCollectionUsage(devices, 0xffa0, 0x0001);
assert_not_equals(device0, undefined, 'vendor device');
assert_equals(device0.collections.length, 1,
'vendor device collection count');
const device1 = getDeviceByCollectionUsage(devices, 0x000c, 0x0001);
assert_not_equals(device1, undefined, 'consumer device');
assert_equals(device1.collections.length, 1,
'consumer device collection count');
const device2 = getDeviceByCollectionUsage(devices, 0x0001, 0x0004);
assert_not_equals(device2, undefined, 'joystick device');
assert_equals(device2.collections.length, 1,
'joystick device collection count');
// These devices should be blocked by WebHID.
const device3 = getDeviceByCollectionUsage(devices, 0x0001, 0x0002);
assert_equals(device3, undefined, 'mouse device');
const device4 = getDeviceByCollectionUsage(devices, 0x0001, 0x0006);
assert_equals(device4, undefined, 'keyboard device');
const device0Input = getReport([device0], 'input', 0x00);
assert_not_equals(device0Input, undefined, 'vendor input report');
checkReportUsage(device0Input, 0xffa10003, 0, 8);
checkReportUsage(device0Input, 0xffa10004, 8, 8);
const device0Output = getReport([device0], 'output', 0x00);
assert_not_equals(device0Input, undefined, 'vendor output report');
checkReportUsage(device0Output, 0xffa10005, 0, 8);
checkReportUsage(device0Output, 0xffa10006, 8, 8);
const device1Input = getReport([device1], 'input', 0x00);
assert_not_equals(device1Input, undefined, 'consumer input report');
checkReportUsage(device1Input, 0x000c00e9, 0, 1);
checkReportUsage(device1Input, 0x000c00ea, 1, 1);
const device2Input = getReport([device2], 'input', 0x00);
assert_not_equals(device2Input, undefined, 'joystick input report');
checkReportUsage(device2Input, 0x00090001, 0, 1);
checkReportUsage(device2Input, 0x00090002, 1, 1);
checkReportUsage(device2Input, 0x00090003, 2, 1);
checkReportUsage(device2Input, 0x00090004, 3, 1);
checkReportUsage(device2Input, 0x00090005, 4, 1);
checkReportUsage(device2Input, 0x00090006, 5, 1);
checkReportUsage(device2Input, 0x00090007, 6, 1);
checkReportUsage(device2Input, 0x00090008, 7, 1);
checkReportUsage(device2Input, 0x00090009, 8, 1);
checkReportUsage(device2Input, 0x0009000a, 9, 1);
checkReportUsage(device2Input, 0x0009000b, 10, 1);
checkReportUsage(device2Input, 0x0009000c, 11, 1);
checkReportUsage(device2Input, 0x0009000d, 12, 1);
checkReportUsage(device2Input, 0x0009000e, 13, 1);
checkReportUsage(device2Input, 0x0009000f, 14, 1);
checkReportUsage(device2Input, 0x00090010, 15, 1);
checkReportUsage(device2Input, 0x00090011, 16, 1);
checkReportUsage(device2Input, 0x00090012, 17, 1);
checkReportUsage(device2Input, 0x00090013, 18, 1);
checkReportUsage(device2Input, 0x00090014, 19, 1);
checkReportUsage(device2Input, 0x00010030, 24, 8);
checkReportUsage(device2Input, 0x00010031, 32, 8);
};
checkReportMapEvolveLink = devices => {
// Expect one device with three top-level collections.
assert_equals(devices.length, 1, 'device count');
assert_equals(devices[0].collections.length, 3, 'collection count');
const collection0 = getCollectionByUsage(devices[0], 0x000b, 0x0005);
assert_not_equals(collection0, undefined, 'headset collection');
const collection1 = getCollectionByUsage(devices[0], 0xff00, 0x0005);
assert_not_equals(collection1, undefined, 'vendor collection');
const collection2 = getCollectionByUsage(devices[0], 0x000c, 0x0001);
assert_not_equals(collection2, undefined, 'consumer collection');
// Input reports
const input01 = getReport(devices, 'input', 0x01);
assert_not_equals(input01, undefined, 'input report 0x01');
const input02 = getReport(devices, 'input', 0x02);
assert_not_equals(input02, undefined, 'input report 0x02');
checkReportUsage(input02, 0x000b0020, 0, 1);
checkReportUsage(input02, 0x000b0097, 1, 1);
checkReportUsage(input02, 0x000b002f, 2, 1);
checkReportUsage(input02, 0x000b0021, 3, 1);
checkReportUsage(input02, 0x000b0024, 4, 1);
checkReportUsage(input02, 0x000b0050, 5, 1);
// The following item is buggy in a way that causes it to differ by
// platform. Here is the relevant portion of the report descriptor:
//
// 0x05, 0x0B, // Usage Page (Telephony)
// ... (some irrelevant items omitted)
// 0x09, 0x07, // Usage (Programmable Button)
// 0x05, 0x09, // Usage Page (Button)
// 0x75, 0x01, // Report Size (1)
// 0x95, 0x01, // Report Count (1)
// 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,...)
//
// The Input item on the last line emits a 1-bit report field. The usage
// is defined by the preceding Usage Page and Usage items. Usage Page
// items are global, meaning the page set by the first Usage Page item
// persists until it is overridden by a following Usage Page item.
// According to the HID specification for the Usage Page item, "Any
// usage that follows which is defined as 16 bits or less is interpreted
// as a Usage ID and concatenated with the Usage Page to form a 32 bit
// Usage." This means the Usage should be concatenated with the
// preceding Usage Page (Telephony) and not the following Usage Page
// (Button).
//
// On platforms where we parse the raw HID report descriptor, we
// correctly use the preceding Usage Page (Telephony). On Windows,
// report descriptor information is parsed by the operating system and
// provided to applications as "preparsed data". This parser incorrectly
// applies the following Usage Page (Button).
//
// To avoid platform dependency, skip verifying this item.
//checkReportUsage(input02, 0x000b0007, 6, 1);
checkReportUsage(input02, 0x000b00b0, 7, 4);
checkReportUsage(input02, 0x000b00b1, 7, 4);
checkReportUsage(input02, 0x000b00b2, 7, 4);
checkReportUsage(input02, 0x000b00b3, 7, 4);
checkReportUsage(input02, 0x000b00b4, 7, 4);
checkReportUsage(input02, 0x000b00b5, 7, 4);
checkReportUsage(input02, 0x000b00b6, 7, 4);
checkReportUsage(input02, 0x000b00b7, 7, 4);
checkReportUsage(input02, 0x000b00b8, 7, 4);
checkReportUsage(input02, 0x000b00b9, 7, 4);
checkReportUsage(input02, 0x000b00ba, 7, 4);
checkReportUsage(input02, 0x000b00bb, 7, 4);
const input04 = getReport(devices, 'input', 0x04);
assert_not_equals(input04, undefined, 'input report 0x04');
checkReportUsage(input04, 0xff300020, 0, 1);
checkReportUsage(input04, 0xff300097, 1, 1);
checkReportUsage(input04, 0xff30002f, 2, 1);
checkReportUsage(input04, 0xff300021, 3, 1);
checkReportUsage(input04, 0xff300024, 4, 1);
checkReportUsage(input04, 0xff30fffd, 5, 1);
checkReportUsage(input04, 0xff300050, 6, 1);
checkReportUsage(input04, 0xff3000b0, 7, 4);
checkReportUsage(input04, 0xff3000b1, 7, 4);
checkReportUsage(input04, 0xff3000b2, 7, 4);
checkReportUsage(input04, 0xff3000b3, 7, 4);
checkReportUsage(input04, 0xff3000b4, 7, 4);
checkReportUsage(input04, 0xff3000b5, 7, 4);
checkReportUsage(input04, 0xff3000b6, 7, 4);
checkReportUsage(input04, 0xff3000b7, 7, 4);
checkReportUsage(input04, 0xff3000b8, 7, 4);
checkReportUsage(input04, 0xff3000b9, 7, 4);
checkReportUsage(input04, 0xff3000ba, 7, 4);
checkReportUsage(input04, 0xff3000bb, 7, 4);
const input05 = getReport(devices, 'input', 0x05);
assert_not_equals(input05, undefined, 'input report 0x05');
checkReportUsage(input05, 0xff000001, 0, 8);
const input08 = getReport(devices, 'input', 0x08);
assert_not_equals(input08, undefined, 'input report 0x08');
checkReportUsage(input08, 0xff600002, 0, 1);
// Output reports
const output02 = getReport(devices, 'output', 0x02);
assert_not_equals(output02, undefined, 'output report 0x02');
checkReportUsage(output02, 0x00080017, 0, 1);
checkReportUsage(output02, 0x00080009, 1, 1);
checkReportUsage(output02, 0x00080018, 2, 1);
checkReportUsage(output02, 0x00080020, 3, 1);
checkReportUsage(output02, 0x00080021, 4, 1);
checkReportUsage(output02, 0x000b009e, 5, 1);
const output04 = getReport(devices, 'output', 0x04);
assert_not_equals(output04, undefined, 'output report 0x04');
checkReportUsage(output04, 0xff400017, 0, 1);
checkReportUsage(output04, 0xff400009, 1, 1);
checkReportUsage(output04, 0xff400018, 2, 1);
checkReportUsage(output04, 0xff400020, 3, 1);
checkReportUsage(output04, 0xff400021, 4, 1);
checkReportUsage(output04, 0xff30009e, 5, 1);
const output05 = getReport(devices, 'output', 0x05);
assert_not_equals(output05, undefined, 'output report 0x05');
checkReportUsage(output05, 0xff000001, 0, 8);
// Feature report 0x08
const feature08 = getReport(devices, 'feature', 0x08);
assert_not_equals(feature08, undefined, 'feature report 0x08');
checkReportUsage(feature08, 0xff600002, 0, 1);
};
checkReportMapStadiaController = devices => {
// Expect one device with one top-level collection.
assert_equals(devices.length, 1, 'device count');
assert_equals(devices[0].collections.length, 1, 'collection count');
const collection = getCollectionByUsage(devices[0], 0x0001, 0x0005);
assert_not_equals(collection, undefined, 'game pad collection');
// Input report 0x03
const input03 = getReport(devices, 'input', 0x03);
assert_not_equals(input03, undefined, 'input report 0x03');
checkReportUsage(input03, 0x00010039, 0, 4);
checkReportUsage(input03, 0x00090012, 8, 1);
checkReportUsage(input03, 0x00090011, 9, 1);
checkReportUsage(input03, 0x00090014, 10, 1);
checkReportUsage(input03, 0x00090013, 11, 1);
checkReportUsage(input03, 0x0009000d, 12, 1);
checkReportUsage(input03, 0x0009000c, 13, 1);
checkReportUsage(input03, 0x0009000b, 14, 1);
checkReportUsage(input03, 0x0009000f, 15, 1);
checkReportUsage(input03, 0x0009000e, 16, 1);
checkReportUsage(input03, 0x00090008, 17, 1);
checkReportUsage(input03, 0x00090007, 18, 1);
checkReportUsage(input03, 0x00090005, 19, 1);
checkReportUsage(input03, 0x00090004, 20, 1);
checkReportUsage(input03, 0x00090002, 21, 1);
checkReportUsage(input03, 0x00090001, 22, 1);
checkReportUsage(input03, 0x00010030, 24, 8);
checkReportUsage(input03, 0x00010031, 32, 8);
checkReportUsage(input03, 0x00010032, 40, 8);
checkReportUsage(input03, 0x00010035, 48, 8);
checkReportUsage(input03, 0x000200c5, 56, 8);
checkReportUsage(input03, 0x000200c4, 64, 8);
checkReportUsage(input03, 0x000c00e9, 72, 1);
checkReportUsage(input03, 0x000c00ea, 73, 1);
checkReportUsage(input03, 0x000c00cd, 74, 1);
// Output report 0x05
const output05 = getReport(devices, 'output', 0x05);
assert_not_equals(output05, undefined, 'output report 0x05');
checkReportUsage(output05, 0x000f0097, 0, 16);
};
// The content of the HIDDevice.collections member can differ by platform,
// so this test aims to only test the properties that are expected to
// remain consistent. In particular, the test verifies that all reports
// are present and that each report field with an assigned usage appears
// at the correct bit index and has the correct field bit width.
manual_hid_test(async (t, devices) => {
if (hasDeviceIds(devices, 0x054c, 0x09cc))
checkReportMapDualshock4(devices);
else if (hasDeviceIds(devices, 0x054c, 0x0ce6))
checkReportMapDualSense(devices);
else if (hasDeviceIds(devices, 0x057e, 0x2009))
checkReportMapSwitchPro(devices);
else if (hasDeviceIds(devices, 0x0911, 0x0fa0))
checkReportMapSpeechMike(devices);
else if (hasDeviceIds(devices, 0x0b0e, 0x0306))
checkReportMapEvolveLink(devices);
else if (hasDeviceIds(devices, 0x18d1, 0x9400))
checkReportMapStadiaController(devices);
else
assert_unreached('Select a supported device.');
}, 'Collection info matches the expected report map.');
</script>
</body>
</html>