WebHID API How to get Input Report without sending Output Report

1k Views Asked by At

Is there a way to just ask for an Input Report using the WebHID API? The API seems to have a function to read a Feature Report directly, but I don't understand why I have to wait for an event to listen to the Input Report. From what I understood about the Input Report, the host PC has to trigger the request to get the report and I think when I write an Output Report then it could trigger the read. Opening the device should also trigger the read.

const dataView = await device.receiveFeatureReport(/* reportId= */ 1);

Since I know that the ReportID I'm trying to read is not a feature report type, this function doesn't work.

Using the event listening method, I'm having problems trying to get the Input Report to trigger after opening the device or writing a report to the Output Report. The device is an STM32L4x device that implements USB HID. I have no issue writing Output Report to it, but I don't seem to be able to trigger the read Input Report event. The event never seems to trigger. Same problem as this post here (WEBHID API: Inputreport not triggering with barcode scanner) but I don't want to switch to using WebUSB due to the driver install requirement.

This is the same event listening code that several examples use.

if (!device.opened) await device.open();

device.addEventListener("inputreport", event => {
  const { data, device, reportId } = event;

  if (device.productId !== 0x5751) return;

  const value = data.getUint8(0);
  if (value == 0) return;
 
  console.log(`Data: ${value}.`);
});

I can interate through the device.collections to get all the reports type correctly

for (let collection of device.collections) {
  // An HID collection includes usage, usage page, reports, and subcollections.

  msg += `Usage: ${collection.usage}` + "<br>";
  msg += `Usage page: ${collection.usagePage}`+ "<br>";

  for (let inputReport of collection.inputReports) {
    msg += `Input report: ${inputReport.reportId}`+ "<br>";
    // Loop through inputReport.items
  }

  for (let outputReport of collection.outputReports) {
    msg += `Output report: ${outputReport.reportId}`+ "<br>";
    // Loop through outputReport.items
  }

  for (let featureReport of collection.featureReports) {
    msg += `Feature report: ${featureReport.reportId}`+ "<br>";
    // Loop through featureReport.items
  }
}
showMessage(msg);

This is the output:

Usage: 1
Usage page: 255
Input report: 1
Input report: 3
Output report: 2
Output report: 4

There are not many examples that I could find that would just let me get an Input Report directly without doing the listening event. Maybe my understanding of USB HID report is not complete, so any help would be appreciated.

2

There are 2 best solutions below

0
On BEST ANSWER

Based on the discussion we have on WebHID GitHub open issue, our hardware is likely not correctly implementing USB HID according to the standard. Therefore, it makes more sense to work on the firmware to correctly implement the USB HID standard rather than trying to use the get_report function to bypass the normal method.

Closing this issue.

2
On

I learned today that to get the input report event to trigger, the device would have to send an input report during an interrupt request like when writing to an output report or performing something like a connection. If the device doesn't implement it then nothing comes back as an input report during the event. The way my device works, I have to use the transfer control to retrieve the input report. This is not an issue in Windows since HID.dll has an input report request function. There's also one in LibUSB that you could use with Linux and MacOS, the get_input_report is implemented as followed:

int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length)
{
int res = -1;
int skipped_report_id = 0;
int report_number = data[0];

if (report_number == 0x0) {
    /* Offset the return buffer by 1, so that the report ID
       will remain in byte 0. */
    data++;
    length--;
    skipped_report_id = 1;
}
res = libusb_control_transfer(dev->device_handle,
    LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_IN,
    0x01/*HID get_report*/,
    (1/*HID Input*/ << 8) | report_number,
    dev->interface,
    (unsigned char *)data, length,
    1000/*timeout millis*/);

if (res < 0)
    return -1;

if (skipped_report_id)
    res++;

return res;
}

Unfortunately, I can't seem to find a way for the current version of WebHID to use a low-level transfer control function call to duplicate this. Until WebHID implements some sort of a "get_input_report" method, I guess I'm out of luck for the moment. We might have to move to a feature report since WebHID implements the receiveFeatureReport function.