P i
P i

Reputation: 30734

Reverse-engineering a HID handshake by examining bytes over USB

I'm trying to extract real-time data from a Wacom Inkling

Roel Janssen has already examined the packets here:

// Some kind of handshaking.
// Values obtained by sniffing the USB connection between SketchManager and the device.
unsigned char usb_data[33];
memset (&usb_data, '\0', 33);
int bytes = 0;

memcpy (&usb_data, "\x80\x01\x03\x01\x02\x00\x00\x00", 8);
bytes += libusb_control_transfer (handle,
                                  0x21,         // bmRequestType
                                  9,            // bRequest
                                  0x0380,       // wValue
                                  0,            // wIndex
                                  usb_data,     // data
                                  33,           // wLength
                                  0);           // timeout

memcpy (&usb_data, "\x80\x01\x0a\x01\x01\x0b\x01\x00", 8);
bytes += libusb_control_transfer (handle, 0x21, 9, 0x0380, 0, usb_data, 33, 0);

memset (&usb_data, '\0', 33);
bytes += libusb_control_transfer (handle, 0xa1, 1, 0x0380, 0, usb_data, 33, 0);

memcpy (&usb_data, "\x80\x01\x0b\x01\x00\x00\x00\x00", 8);
bytes += libusb_control_transfer (handle, 0x21, 9, 0x0380, 0, usb_data, 33, 0);

memcpy (&usb_data, "\x80\x01\x02\x01\x01\x00\x00\x00", 8);
bytes += libusb_control_transfer (handle, 0x21, 9, 0x0380, 0, usb_data, 33, 0);

memcpy (&usb_data, "\x80\x01\x0a\x01\x01\x02\x01\x00", 8);
bytes += libusb_control_transfer (handle, 0x21, 9, 0x0380, 0, usb_data, 33, 0);

memset (&usb_data, '\0', 33);
bytes += libusb_control_transfer (handle, 0xa1, 1, 0x0380, 0, usb_data, 33, 0);

I'm trying to rewrite this code using HID API which has a very minimal API (here)

I'm going to attempt to just use hid_write for now, but there is maybe a chance this handshake is sending a feature report...?

Is there anyone out there who can look at that bytestream and see what is going on?

EDIT: It appears the Inkling exposes a FlashDrive and HID interface, so I am guessing this bytecode must be selecting the HID interface and telling it to start sending data. But can I code it in a more elegant / human readable form?

EDIT: I have it working! Both hid_write and hid_send_feature_report work!

hid_device* handle = hid_open(inklingVendorId, inklingProductId, NULL);
jassert(handle != nullptr);

int bytes_written =
    hid_send_feature_report(handle, (const unsigned char *)"\x80\x01\x03\x01\x02\x00\x00\x00", 8) +
    hid_send_feature_report(handle, (const unsigned char *)"\x80\x01\x0a\x01\x01\x0b\x01\x00", 8) +
    hid_send_feature_report(handle, (const unsigned char *)"\x80\x01\x0b\x01\x00\x00\x00\x00", 8) +
    hid_send_feature_report(handle, (const unsigned char *)"\x80\x01\x02\x01\x01\x00\x00\x00", 8) +
    hid_send_feature_report(handle, (const unsigned char *)"\x80\x01\x0a\x01\x01\x02\x01\x00", 8);
jassert(bytes_written == 5*8);

const int enable_nonblocking = 1, disable_nonblocking = 0;
jassert( hid_set_nonblocking(handle, disable_nonblocking) != FAIL); // want to block

while(true) {
    int bytes_got = hid_read(handle, usb_data, 10);

... However I would still like to understand what is going on. This is rather hacky.

EDIT: Output of lsusb (from Roel, I don't have Linux to hand):

Bus 003 Device 002: ID 056a:0221 Wacom Co., Ltd 
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0        64
  idVendor           0x056a Wacom Co., Ltd
  idProduct          0x0221 
  bcdDevice           12.56
  iManufacturer           1 (error)
  iProduct                2 MSC Device
  iSerial                 5 4833000045C5549C0002DD012DA5549C
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           57
    bNumInterfaces          2
    bConfigurationValue     1
    iConfiguration          3 USB/MSC Inkling
    bmAttributes         0x80
      (Bus Powered)
    MaxPower              500mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      0 No Subclass
      bInterfaceProtocol      2 Mouse
      iInterface              0 
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.01
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength     215
          Report Descriptor: (length is 215)
            Item(Global): Usage Page, data= [ 0x0d ] 13
                            Digitizer
            Item(Local ): Usage, data= [ 0x02 ] 2
                            Pen
            Item(Main  ): Collection, data= [ 0x01 ] 1
                            Application
            Item(Global): Report ID, data= [ 0x02 ] 2
            Item(Local ): Usage, data= [ 0x02 ] 2
                            Pen
            Item(Main  ): Collection, data= [ 0x00 ] 0
                            Physical
            Item(Global): Usage Page, data= [ 0x01 ] 1
                            Generic Desktop Controls
            Item(Local ): Usage, data= [ 0x30 ] 48
                            Direction-X
            Item(Local ): Usage, data= [ 0x31 ] 49
                            Direction-Y
            Item(Global): Logical Minimum, data= [ 0x00 ] 0
            Item(Global): Logical Maximum, data= [ 0x80 0x07 ] 1920
            Item(Global): Physical Minimum, data= [ 0x00 ] 0
            Item(Global): Physical Maximum, data= [ 0x00 0x78 ] 30720
            Item(Global): Unit, data= [ 0x11 ] 17
                            System: SI Linear, Unit: Centimeter
            Item(Global): Unit Exponent, data= [ 0x0e ] 14
                            Unit Exponent: 14
            Item(Global): Report Size, data= [ 0x10 ] 16
            Item(Global): Report Count, data= [ 0x02 ] 2
            Item(Main  ): Input, data= [ 0x02 ] 2
                            Data Variable Absolute No_Wrap Linear
                            Preferred_State No_Null_Position Non_Volatile Bitfield
            Item(Global): Usage Page, data= [ 0x0d ] 13
                            Digitizer
            Item(Local ): Usage, data= [ 0x42 ] 66
                            Tip Switch
            Item(Local ): Usage, data= [ 0x45 ] 69
                            Eraser
            Item(Local ): Usage, data= [ 0x44 ] 68
                            Barrel Switch
            Item(Local ): Usage, data= [ 0x32 ] 50
                            In Range
            Item(Global): Logical Minimum, data= [ 0x00 ] 0
            Item(Global): Logical Maximum, data= [ 0x01 ] 1
            Item(Global): Report Size, data= [ 0x01 ] 1
            Item(Global): Report Count, data= [ 0x04 ] 4
            Item(Global): Unit, data= [ 0x00 ] 0
                            System: None, Unit: (None)
            Item(Main  ): Input, data= [ 0x02 ] 2
                            Data Variable Absolute No_Wrap Linear
                            Preferred_State No_Null_Position Non_Volatile Bitfield
            Item(Global): Usage Page, data= [ 0x09 ] 9
                            Buttons
            Item(Local ): Usage Minimum, data= [ 0x01 ] 1
                            Button 1 (Primary)
            Item(Local ): Usage Maximum, data= [ 0x04 ] 4
                            Button 4
            Item(Global): Logical Minimum, data= [ 0x00 ] 0
            Item(Global): Logical Maximum, data= [ 0x01 ] 1
            Item(Global): Report Size, data= [ 0x01 ] 1
            Item(Global): Report Count, data= [ 0x04 ] 4
            Item(Main  ): Input, data= [ 0x02 ] 2
                            Data Variable Absolute No_Wrap Linear
                            Preferred_State No_Null_Position Non_Volatile Bitfield
            Item(Global): Usage Page, data= [ 0x0d ] 13
                            Digitizer
            Item(Local ): Usage, data= [ 0x30 ] 48
                            Tip Pressure
            Item(Global): Logical Minimum, data= [ 0x00 ] 0
            Item(Global): Logical Maximum, data= [ 0x00 0x04 ] 1024
            Item(Global): Report Size, data= [ 0x10 ] 16
            Item(Global): Report Count, data= [ 0x01 ] 1
            Item(Main  ): Input, data= [ 0x02 ] 2
                            Data Variable Absolute No_Wrap Linear
                            Preferred_State No_Null_Position Non_Volatile Bitfield
            Item(Global): Usage Page, data= [ 0x0d ] 13
                            Digitizer
            Item(Local ): Usage, data= [ 0x3d ] 61
                            X Tilt
            Item(Local ): Usage, data= [ 0x3e ] 62
                            Y Tilt
            Item(Global): Logical Minimum, data= [ 0x81 ] 129
            Item(Global): Logical Maximum, data= [ 0x7f ] 127
            Item(Global): Report Size, data= [ 0x08 ] 8
            Item(Global): Report Count, data= [ 0x02 ] 2
            Item(Main  ): Input, data= [ 0x02 ] 2
                            Data Variable Absolute No_Wrap Linear
                            Preferred_State No_Null_Position Non_Volatile Bitfield
            Item(Main  ): End Collection, data=none
            Item(Global): Usage Page, data= [ 0x01 ] 1
                            Generic Desktop Controls
            Item(Local ): Usage, data= [ 0x00 ] 0
                            Undefined
            Item(Global): Report ID, data= [ 0x04 ] 4
            Item(Global): Logical Minimum, data= [ 0x00 ] 0
            Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255
            Item(Global): Report Size, data= [ 0x08 ] 8
            Item(Global): Report Count, data= [ 0x0c ] 12
            Item(Main  ): Input, data= [ 0x02 ] 2
                            Data Variable Absolute No_Wrap Linear
                            Preferred_State No_Null_Position Non_Volatile Bitfield
            Item(Global): Usage Page, data= [ 0x01 ] 1
                            Generic Desktop Controls
            Item(Local ): Usage, data= [ 0x00 ] 0
                            Undefined
            Item(Global): Report ID, data= [ 0x08 ] 8
            Item(Global): Logical Minimum, data= [ 0x00 ] 0
            Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255
            Item(Global): Report Size, data= [ 0x08 ] 8
            Item(Global): Report Count, data= [ 0x3b ] 59
            Item(Main  ): Input, data= [ 0x02 ] 2
                            Data Variable Absolute No_Wrap Linear
                            Preferred_State No_Null_Position Non_Volatile Bitfield
            Item(Local ): Usage, data= [ 0x01 ] 1
                            Pointer
            Item(Global): Report ID, data= [ 0x80 ] 128
            Item(Global): Report Size, data= [ 0x08 ] 8
            Item(Global): Report Count, data= [ 0x20 ] 32
            Item(Main  ): Feature, data= [ 0x02 ] 2
                            Data Variable Absolute No_Wrap Linear
                            Preferred_State No_Null_Position Non_Volatile Bitfield
            Item(Main  ): End Collection, data=none
            Item(Global): Usage Page, data= [ 0x01 ] 1
                            Generic Desktop Controls
            Item(Local ): Usage, data= [ 0x02 ] 2
                            Mouse
            Item(Main  ): Collection, data= [ 0x01 ] 1
                            Application
            Item(Global): Report ID, data= [ 0x01 ] 1
            Item(Local ): Usage, data= [ 0x01 ] 1
                            Pointer
            Item(Main  ): Collection, data= [ 0x00 ] 0
                            Physical
            Item(Global): Usage Page, data= [ 0x01 ] 1
                            Generic Desktop Controls
            Item(Local ): Usage, data= [ 0x30 ] 48
                            Direction-X
            Item(Local ): Usage, data= [ 0x31 ] 49
                            Direction-Y
            Item(Global): Logical Minimum, data= [ 0x00 ] 0
            Item(Global): Logical Maximum, data= [ 0x80 0x07 ] 1920
            Item(Global): Physical Minimum, data= [ 0x00 ] 0
            Item(Global): Physical Maximum, data= [ 0x00 0x78 ] 30720
            Item(Global): Unit, data= [ 0x11 ] 17
                            System: SI Linear, Unit: Centimeter
            Item(Global): Unit Exponent, data= [ 0x0e ] 14
                            Unit Exponent: 14
            Item(Global): Report Size, data= [ 0x10 ] 16
            Item(Global): Report Count, data= [ 0x02 ] 2
            Item(Main  ): Input, data= [ 0x02 ] 2
                            Data Variable Absolute No_Wrap Linear
                            Preferred_State No_Null_Position Non_Volatile Bitfield
            Item(Global): Usage Page, data= [ 0x09 ] 9
                            Buttons
            Item(Local ): Usage Minimum, data= [ 0x01 ] 1
                            Button 1 (Primary)
            Item(Local ): Usage Maximum, data= [ 0x03 ] 3
                            Button 3 (Tertiary)
            Item(Global): Logical Minimum, data= [ 0x00 ] 0
            Item(Global): Logical Maximum, data= [ 0x01 ] 1
            Item(Global): Report Size, data= [ 0x01 ] 1
            Item(Global): Report Count, data= [ 0x03 ] 3
            Item(Global): Unit, data= [ 0x00 ] 0
                            System: None, Unit: (None)
            Item(Main  ): Input, data= [ 0x02 ] 2
                            Data Variable Absolute No_Wrap Linear
                            Preferred_State No_Null_Position Non_Volatile Bitfield
            Item(Global): Report Count, data= [ 0x05 ] 5
            Item(Main  ): Input, data= [ 0x01 ] 1
                            Constant Array Absolute No_Wrap Linear
                            Preferred_State No_Null_Position Non_Volatile Bitfield
            Item(Main  ): End Collection, data=none
            Item(Main  ): End Collection, data=none
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x83  EP 3 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               4
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        1
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass         8 Mass Storage
      bInterfaceSubClass      6 SCSI
      bInterfaceProtocol     80 Bulk-Only
      iInterface              4 USB/MSC Inkling
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x02  EP 2 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               0
Device Qualifier (for other device speed):
  bLength                10
  bDescriptorType         6
  bcdUSB               2.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0        64
  bNumConfigurations      2
Device Status:     0x0000
  (Bus Powered)

Upvotes: 2

Views: 2011

Answers (2)

Lekensteyn
Lekensteyn

Reputation: 66495

So this is one single report you send over USB:

 80 01 03 01 02 00 00 00 .... (in total the buffer is 1+32 = 33 bytes)
 ^^                       Report ID

This is the relevant part from the HID descriptor:

...
Item(Global): Report ID, data= [ 0x80 ] 128
Item(Global): Report Size, data= [ 0x08 ] 8
Item(Global): Report Count, data= [ 0x20 ] 32
Item(Main  ): Feature, data= [ 0x02 ] 2
                Data Variable Absolute No_Wrap Linear
                Preferred_State No_Null_Position Non_Volatile Bitfield

This says that your data for Report ID 0x80 has to be interpreted as 32 times one byte (it's meaning is up to the driver I guess). It is a Feature which means that it can configure the device via GET_FEATURE/SET_FEATURE reports over the control endpoint.

For more information about how to interpret this descriptor, see the HID v1.1 specification from http://www.usb.org/developers/hidpage/.

Upvotes: 1

ralf htp
ralf htp

Reputation: 9432

1) Verify that the Inkling uses HID USB device class. Try lsusb -v and check the bDeviceClass or bInterfaceClass field in the output (The USB human interface device class can be used to describe both device and interface classes. The interface class is used when a USB device can contain more than one function. from https://en.wikipedia.org/wiki/USB_human_interface_device_class) In the manual (https://www.wacom.com/~/media/files/store-manuals/inkling-manual-english.pdf) is said that it is a USB flash drive, in this case it uses USB mass storage device class (BULK transfer)

2) If it is a USB HID device class you can try to get the feature reports (if the inkling sends any) similar to this website http://libusb.6.n5.nabble.com/How-to-get-HID-report-td4628.html

libusb_get_descriptor makes standard GET_DESCRIPTOR requests, where the bmRequestType field is 0x80. The DT_REPORT descriptor request must indicate that the recipient is an interface, which requires bmRequestType to be 0x81.

You have two good choices. The report descriptors are all included in the configuration descriptor, so you should be able to fetch the whole configuration descriptor en masse and parse it to extract the report descriptors.

Alternatively, libusb_get_descriptor is a very thin layer over libusb_control_transfer , so you could just expand it by hand:

res = libusb_control_transfer( devh, LIBUSB_ENDPOINT_IN |
LIBUSB_RECIPIENT_INTERFACE,
LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_REPORT << 8) | 0, 0, buf,
sizeof(buf), 1000);

more links: - http://eleccelerator.com/tutorial-about-usb-hid-report-descriptors/ http://www.beyondlogic.org/usbnutshell/usb1.shtml

---------------------------------------------------------------------------------

libusb_control_transfer performs USB control transfers. Control transfers are used for command and status operations, see this http://libusb.sourceforge.net/api-1.0/group__syncio.html and this http://www.beyondlogic.org/usbnutshell/usb4.shtml#Control

USB control requests are a subtype of USB requests, see http://www.beyondlogic.org/usbnutshell/usb6.shtml

usb_data seems to be the USB send buffer, it is always filled with the data bytes, then the libusb_control_transfer is send

This is the commented form of the USB control request (http://www.beyondlogic.org/usbnutshell/usb6.shtml)

memcpy (&usb_data, "\x80\x01\x03\x01\x02\x00\x00\x00", 8);
bytes += libusb_control_transfer (handle,
                                  0x21,         // bmRequestType
                                  9,            // bRequest
                                  0x0380,       // wValue
                                  0,            // wIndex
                                  usb_data,     // data
                                  33,           // wLength
                                  0);           // timeout

This is the short form of the USB control request

memcpy (&usb_data, "\x80\x01\x0a\x01\x01\x0b\x01\x00", 8);
bytes += libusb_control_transfer (handle, 0x21, 9, 0x0380, 0, usb_data, 33, 0);

So all the byte sequences like "\x80\x01\x03\x01\x02\x00\x00\x00" are commands codes that are used to configure the Inkling ('handshake') and only the Inkling and the Wacom folks understand...

Upvotes: 2

Related Questions