USB HID

#hardware #usb

Communicating with a USB HID (Human Interface Device) involves understanding the specific protocols and structures used by these devices. Here’s a detailed breakdown of how to communicate with a USB HID device, including the concept of a "report" and the communication flow:

Overview

USB HID devices, such as keyboards, mice, and game controllers, follow a standardized communication protocol defined by the USB Implementers Forum (USB-IF). This protocol is designed to allow easy interaction between the device and the host (e.g., a computer). Here's how communication generally works:

  1. Enumeration and Initialization:

    • When a USB HID device is connected to the host, the host performs a process called enumeration.

    • During enumeration, the host retrieves the device descriptor, configuration descriptor, and HID descriptor. These descriptors contain information about the device’s capabilities and requirements.

  2. Descriptors:

    • Device Descriptor: Basic information about the device (e.g., vendor ID, product ID).

    • Configuration Descriptor: Information about the device's power requirements and interfaces.

    • HID Descriptor: Contains details specific to the HID class, including the size and format of reports.

  3. Reports:

    • Reports are the primary data structures used in USB HID communication. Reports are used to transmit data between the device and the host.

    • There are three main types of reports: Input Reports, Output Reports, and Feature Reports.

      • Input Reports: Sent from the device to the host to convey information such as keystrokes or mouse movements.

      • Output Reports: Sent from the host to the device to control its behavior, such as turning on an LED.

      • Feature Reports: Can be sent in both directions to retrieve or set the state of the device.

Request and Response Flow

The communication flow for USB HID devices does have similarities to TCP or HTTP in terms of request and response, but it is simpler and more hardware-focused. Here's a high-level overview of the flow:

  1. Control Transfers:

    • Used for configuration and setup, typically during enumeration.

    • The host sends standard or class-specific control requests to get descriptors or set configuration.

  2. Interrupt Transfers:

    • Used for regular, low-latency data exchange (e.g., key presses from a keyboard).

    • The device periodically sends input reports to the host.

    • The host can send output reports to the device as needed.

  3. Data Flow:

    • The host initiates communication by sending requests (control or interrupt transfers).

    • The device responds with data or acknowledges the receipt of data.

    • Input reports are typically sent from the device to the host without explicit requests (polling mechanism).

    • Output and feature reports are sent by the host to control or query the device.

Example of Report Structure

A report is a structured packet of data defined by the HID Report Descriptor, which outlines the format and meaning of the data within the report. Here’s an example structure for a simple HID report:

For instance, a mouse might have a report structure like:

Practical Communication Example

To communicate with a USB HID device programmatically, you can use libraries available in various programming languages. For example, in Python, you can use the hid library:

import hid

# Open the device
device = hid.device()
device.open(vendor_id, product_id)

# Set non-blocking mode
device.set_nonblocking(1)

# Read input report (example with a report of 64 bytes)
report = device.read(64)
print(report)

# Write output report (example with a report of 64 bytes)
report_id = 0x01
data = [report_id] + [0x00] * 63
device.write(data)

# Close the device
device.close()

Summary

Transfers

In USB HID communication, reports are primarily used in interrupt transfers. Control transfers are used for specific setup and configuration tasks rather than regular data communication. Here's a breakdown of how reports and transfers are used in the context of USB HID devices:

Interrupt Transfers

Interrupt transfers are the main method for regular data exchange between the host and the HID device. This is where reports come into play.

Control Transfers

Control transfers are used for initial configuration and occasional specific commands. These are essential during the enumeration process when the host queries the device for its descriptors and sets it up for operation. Control transfers can also be used to send and receive feature reports.

Practical Examples

Input Reports (Interrupt Transfers)

A keyboard sending keystroke data to the host is a typical example of an input report using an interrupt transfer. The device sends this data at regular intervals or when there's new data to report.

Output Reports (Interrupt Transfers)

An example of an output report using an interrupt transfer is when the host sends a command to turn on an LED on the keyboard.

Feature Reports (Interrupt and Control Transfers)

Feature reports are often used to set or query the state of the device. For example, configuring the sensitivity of a gaming mouse might involve feature reports. These can be transmitted using control transfers if needed.

Summary

To programmatically interact with these transfers, libraries like hidapi in Python can be used. Here's an example to illustrate:

import hid

# Open the device
device = hid.device()
device.open(vendor_id, product_id)

# Set non-blocking mode
device.set_nonblocking(1)

# Read an input report (example with a report of 64 bytes)
input_report = device.read(64)
print("Input Report:", input_report)

# Write an output report (example with a report of 64 bytes)
output_report_id = 0x01
output_data = [output_report_id] + [0x00] * 63
device.write(output_data)

# Send a feature report using control transfer
feature_report_id = 0x02
feature_data = [feature_report_id] + [0x00] * 63
device.send_feature_report(feature_data)

# Retrieve a feature report using control transfer
feature_report = device.get_feature_report(feature_report_id, 64)
print("Feature Report:", feature_report)

# Close the device
device.close()

This code demonstrates how to read and write different types of reports using both interrupt and control transfers, providing a practical approach to USB HID communication.

The Web HID API allows web applications to interact with Human Interface Devices (HIDs) directly from the browser. This API provides a way to send and receive HID reports, enabling control and data exchange with devices like keyboards, mice, game controllers, and custom HID devices. Below is a guide on how to send input and output reports using the Web HID API.

Web HID API

Using the API

  1. Request Access to HID Device:

    • First, you need to request access to the HID device from the user.

  2. Open the Device:

    • Once access is granted, you need to open the device for communication.

  3. Send Output Reports:

    • Use the sendReport() method to send output reports to the device.

  4. Receive Input Reports:

    • Set up an event listener for input reports using the oninputreport event.

Example Code

Here is an example demonstrating how to send and receive reports using the Web HID API:

// Request access to HID devices
async function requestDevice() {
  const devices = await navigator.hid.requestDevice({
    filters: [{ vendorId: 0x1234 }] // Replace with your device's vendor ID
  });

  if (devices.length > 0) {
    return devices[0];
  } else {
    throw new Error("No HID devices selected.");
  }
}

// Open the device and start communication
async function openDevice(device) {
  await device.open();

  // Listen for input reports
  device.oninputreport = event => {
    const { data, device, reportId } = event;
    console.log(`Received input report ${reportId}: ${data}`);
  };
}

// Send an output report to the device
async function sendOutputReport(device, reportId, data) {
  await device.sendReport(reportId, data);
}

// Main function to handle the device communication
async function main() {
  try {
    const device = await requestDevice();
    await openDevice(device);

    // Example data to send as output report
    const reportId = 0x01;
    const data = new Uint8Array([0x01, 0x00, 0x00, 0x00]); // Example data

    // Send output report
    await sendOutputReport(device, reportId, data);
    console.log(`Sent output report ${reportId}: ${data}`);
  } catch (error) {
    console.error('Error:', error);
  }
}

// Run the main function
main();

Explanation

  1. Request Device:

    • The navigator.hid.requestDevice() function prompts the user to select a HID device. The filters parameter allows you to specify criteria for the devices you want to access, such as vendorId.

  2. Open Device:

    • Once a device is selected, the open() method is called to open the device for communication.

    • An event listener is set up for oninputreport to handle incoming input reports from the device. When an input report is received, the event handler logs the report data.

  3. Send Output Report:

    • The sendReport() method sends an output report to the device. It takes the reportId and the data to send (as a Uint8Array).

  4. Main Function:

    • This function orchestrates the process by requesting the device, opening it, and sending an output report with example data.

Notes

Using this example, you can modify the code to fit your specific HID device and the reports you need to send or receive.