Skip to content

Determining Device Write Type

Once you know your device’s protocol type, you may still need to determine which specific write function to use. HID devices in particular have two different write methods that target different USB transfer mechanisms.


device.write() sends data using a bulk or interrupt transfer on the device’s currently selected HID endpoint. This is the standard write path and works for the majority of devices.

Use device.write() when:

  • The device sends and receives fixed-length reports via interrupt transfer
  • The Wireshark capture shows URB_INTERRUPT transfers
  • The device does not use a HID Report ID on the selected endpoint (requiring zero padding — see below)
var packet = [];
packet[0] = 0xEC;
packet[1] = 0x40;
packet[2] = channel;
packet = packet.concat(RGBData);
device.write(packet, 65);

device.send_report() sends data using a HID SET_REPORT control transfer. This targets the HID feature report mechanism rather than the interrupt endpoint.

Use device.send_report() when:

  • The Wireshark capture shows URB_CONTROL transfers with a SET_REPORT request (request code 0x09)
  • device.write() returns an “incorrect function” error on all endpoints
  • The device explicitly uses HID Feature reports for its command interface
device.send_report([0x07, 0x03, 0x06, 0x01, 0x00], 65);

The companion read function for feature reports is device.get_report(), which issues a GET_REPORT control transfer. Use it when you need to read back configuration via the same feature report mechanism.


If you’re not sure, start with device.write(). It works for the majority of HID devices. If you get an “incorrect function” error, switch to device.send_report(). If both fail, verify you have the correct endpoint selected.

SymptomTry
Works as expectedYou’re done
”Incorrect function” errorSwitch to device.send_report()
”Access is denied” errorWrong endpoint — see Selecting Endpoints
No error, but device doesn’t respondWrong endpoint, or wrong device type

Some HID devices require the first byte of every packet to be 0x00. This happens when the device has no Report ID assigned to the selected endpoint — the HID driver strips byte 0 before passing the data to the device, so you need to push everything over by one position to compensate.

Signs you need zero padding:

  • The Wireshark capture shows the first meaningful command byte at offset 1, not offset 0
  • Without the zero byte, the device ignores the packet or produces garbage output

How to apply it:

// Without zero padding (packet size 64)
packet[0] = 0xEC; // command
packet[1] = 0x40;
device.write(packet, 64);
// With zero padding (packet size 65)
packet[0] = 0x00; // padding byte — consumed by HID driver
packet[1] = 0xEC; // command now at offset 1
packet[2] = 0x40;
device.write(packet, 65);

The rule of thumb: if your Wireshark capture shows a 64-byte packet but device.write(packet, 64) doesn’t work, try device.write(packet, 65) with packet[0] = 0x00 and all other bytes shifted up by one.


The read-side equivalents follow the same split:

Write methodMatching read methodTransfer type
device.write()device.read()Bulk / Interrupt
device.send_report()device.get_report()Control (GET_REPORT)

For reading input reports specifically (not feature reports), use device.input_report(). See Advanced Communication for details.