'I cannot mimic sniffed urb interruption using libusb for Ruby

Sniffed URB_INTERRUPTions

I sniffed communication between some application (SoundLab) and device (sonometer with usb). I found a packet responsible for returning current state:

USB URB
    [Source: host]
    [Destination: 1.1.2]
    USBPcap pseudoheader length: 27
    IRP ID: 0xffff858d126f4a60
    IRP USBD_STATUS: USBD_STATUS_SUCCESS (0x00000000)
    URB Function: URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER (0x0009)
    IRP information: 0x00, Direction: FDO -> PDO
        0000 000. = Reserved: 0x00
        .... ...0 = Direction: FDO -> PDO (0x0)
    URB bus id: 1
    Device address: 1
    Endpoint: 0x02, Direction: OUT
        0... .... = Direction: OUT (0)
        .... 0010 = Endpoint number: 2
    URB transfer type: URB_INTERRUPT (0x01)
    Packet Data Length: 8
    [bInterfaceClass: Unknown (0xffff)]
Leftover Capture Data: b331eb4d00000000

It is sent by application to endpoint number 2 (out), and then device sends urb interruption with data to endpoint number 1 (in):

USB URB
    [Source: 1.1.1]
    [Destination: host]
    USBPcap pseudoheader length: 27
    IRP ID: 0xffff858d10207af0
    IRP USBD_STATUS: USBD_STATUS_SUCCESS (0x00000000)
    URB Function: URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER (0x0009)
    IRP information: 0x01, Direction: PDO -> FDO
        0000 000. = Reserved: 0x00
        .... ...1 = Direction: PDO -> FDO (0x1)
    URB bus id: 1
    Device address: 1
    Endpoint: 0x81, Direction: IN
        1... .... = Direction: IN (1)
        .... 0001 = Endpoint number: 1
    URB transfer type: URB_INTERRUPT (0x01)
    Packet Data Length: 8
    [bInterfaceClass: Unknown (0xffff)]
Leftover Capture Data: 01a9009b90ddc0ff

Attempt to mimic interruptions with libusb

Now I want to mimic this in Linux, using libusb. I wrote this code where I can test different interruptions.

require 'libusb'
require 'pry'

vendor_id = 0x64bd
product_id = 0x74e3

module Messages
  GET_STATE = ['b331eb4d00000000'].pack('H*')
end

usb = LIBUSB::Context.new
device = usb.devices(idVendor: vendor_id, idProduct: product_id).first

dev_handle = device.open

if dev_handle.kernel_driver_active?(0)
  dev_handle.detach_kernel_driver(0)
end

dev_handle.claim_interface(0)

binding.pry

dev_handle.release_interface(0)
dev_handle.close

Then I run this in pry console:

dev_handle.interrupt_transfer(endpoint: 2, dataOut: Messages::GET_STATE)

And it returns 8. That is not what I expected.

URB_INTERRUPTions I see when I run my code

USB URB
    [Source: host]
    [Destination: 1.6.2]
    URB id: 0xffff8802142fecc0
    URB type: URB_SUBMIT ('S')
    URB transfer type: URB_INTERRUPT (0x01)
    Endpoint: 0x02, Direction: OUT
    Device: 6
    URB bus id: 1
    Device setup request: not relevant ('-')
    Data: present (0)
    URB sec: 1559130571
    URB usec: 534195
    URB status: Operation now in progress (-EINPROGRESS) (-115)
    URB length [bytes]: 8
    Data length [bytes]: 8
    [Response in: 126]
    [bInterfaceClass: HID (0x03)]
    Unused Setup Header
    Interval: 8
    Start frame: 0
    Copy of Transfer Flags: 0x00000000
    Number of ISO descriptors: 0
Leftover Capture Data: b331eb4d00000000
USB URB
    [Source: 1.6.2]
    [Destination: host]
    URB id: 0xffff8802142fecc0
    URB type: URB_COMPLETE ('C')
    URB transfer type: URB_INTERRUPT (0x01)
    Endpoint: 0x02, Direction: OUT
    Device: 6
    URB bus id: 1
    Device setup request: not relevant ('-')
    Data: not present ('>')
    URB sec: 1559130571
    URB usec: 534846
    URB status: Success (0)
    URB length [bytes]: 8
    Data length [bytes]: 0
    [Request in: 125]
    [Time from request: 0.000651000 seconds]
    [bInterfaceClass: HID (0x03)]
    Unused Setup Header
    Interval: 8
    Start frame: 0
    Copy of Transfer Flags: 0x00000000
    Number of ISO descriptors: 0

Summary

In result I get URB_INTERRUPTions that are much different from what I saw on Windows. On Windows I had urb_interrupt to 2 (out) endpoint with data requesting state of device. Then device was sending urb_interrupt to 1 (in) endpoint with current state encoded in capture data. How can I mimic this with LIBUSB::DevHandle#interrupt_transfer? Here is documentation of this method: https://www.rubydoc.info/gems/libusb/LIBUSB/DevHandle#interrupt_transfer-instance_method.



Solution 1:[1]

I tried meld-ing the expected versus received USB output that you posted, and the main thing that stood out to me was that your 'actual' USB protocol captures showed one URB_INTERRUPT in (marked by the 'direction' field). In your 'actual' USB traffic dump, it looks like you have 2 URB_INTERRUPT outs.

I'm not an expert in the USB protocol (I came across this question while myself stumbling around for help reverse-engineering a USB driver as well), but from what I can tell, to get a URB_INTERRUPT 'in' (which is what you have in the expected output), you have to change the endpoint address to 0x82 instead of just 2.

This answer talks about this further: https://superuser.com/a/876773/618124

USB endpoints 0x00-0x7F are on the host, and the endpoints 0x80-0xFF are on the device (I think).

Give that shot and see if it works?

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1