Cockpit: Unpacked

July 21, 2024

Looking at raw bytes is no way to live, not unless you have other choices. So this is about exploring what tools are there for those that have gone down the same path. But before that, I've made some progress in understanding what we are dealing with.

Tying loose ends

$ lsusb -t

/:  Bus 02.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/6p, 5000M
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/12p, 480M
    |__ Port 1: Dev 5, If 0, Class=Human Interface Device, Driver=usbhid, 12M
    |__ Port 6: Dev 2, If 0, Class=Video, Driver=uvcvideo, 480M
    |__ Port 6: Dev 2, If 1, Class=Video, Driver=uvcvideo, 480M
    |__ Port 8: Dev 3, If 1, Class=Wireless, Driver=btusb, 12M
    |__ Port 8: Dev 3, If 0, Class=Wireless, Driver=btusb, 12M

Linux does recognize this as an input device and it has assigned a generic driver to it. It's a "Human Interface Device" and the driver assigned is usbhid. I'll let you guess what hid stands for. That is why we are even able to peek into the handler and read the input. That's great but I am still unsure if I should be writing my own to replace this or build on top of it...

If you also remember the handlers from the previous post of this series. While we were using event14 to listen to all the input, there is a second one attached, js0 which turns out to be short for "joystick". Looking back, things are now painfully obvious:

$ dmesg | grep usb

...
[   93.725773] input: Winwing WINWING UFC1 as /devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.0/0003:4098:BED0.0002/input/input16
[   93.726406] hid-generic 0003:4098:BED0.0002: input,hidraw1: USB HID v1.11 Joystick [Winwing WINWING UFC1] on usb-0000:00:14.0-1/input0
[   93.726602] usbcore: registered new interface driver usbhid
[   93.726612] usbhid: USB HID core driver

It's clearly shown that the usbhid driver is selected and js0 lines up with the fact that this is marked as a Joystick.

Untangling the mess

evtest

Given that there is a driver we now have a way of decoding the packets with evtest. evtest is described as an "Input device event monitor and query tool" and it lives up to its name:

$ sudo evtest /dev/input/event14

Input driver version is 1.0.1
Input device ID: bus 0x3 vendor 0x4098 product 0xbed0 version 0x111
Input device name: "Winwing WINWING UFC1"
Supported events:
  Event type 0 (EV_SYN)
  Event type 1 (EV_KEY)
    Event code 288 (BTN_TRIGGER)
    Event code 289 (BTN_THUMB)
    Event code 290 (BTN_THUMB2)
    Event code 291 (BTN_TOP)
    Event code 292 (BTN_TOP2)
    Event code 293 (BTN_PINKIE)
    Event code 294 (BTN_BASE)
    Event code 295 (BTN_BASE2)
    Event code 296 (BTN_BASE3)
    Event code 297 (BTN_BASE4)
    Event code 298 (BTN_BASE5)
    Event code 299 (BTN_BASE6)
    Event code 300 (?)
    Event code 301 (?)
    Event code 302 (?)
    Event code 303 (BTN_DEAD)
    Event code 704 (BTN_TRIGGER_HAPPY1)
    Event code 705 (BTN_TRIGGER_HAPPY2)
    Event code 706 (BTN_TRIGGER_HAPPY3)
    Event code 707 (BTN_TRIGGER_HAPPY4)
    Event code 708 (BTN_TRIGGER_HAPPY5)
    Event code 709 (BTN_TRIGGER_HAPPY6)
    Event code 710 (BTN_TRIGGER_HAPPY7)
    Event code 711 (BTN_TRIGGER_HAPPY8)
    Event code 712 (BTN_TRIGGER_HAPPY9)
    Event code 713 (BTN_TRIGGER_HAPPY10)
    Event code 714 (BTN_TRIGGER_HAPPY11)
    Event code 715 (BTN_TRIGGER_HAPPY12)
    Event code 716 (BTN_TRIGGER_HAPPY13)
    Event code 717 (BTN_TRIGGER_HAPPY14)
    Event code 718 (BTN_TRIGGER_HAPPY15)
    Event code 719 (BTN_TRIGGER_HAPPY16)
    Event code 720 (BTN_TRIGGER_HAPPY17)
    Event code 721 (BTN_TRIGGER_HAPPY18)
    Event code 722 (BTN_TRIGGER_HAPPY19)
    Event code 723 (BTN_TRIGGER_HAPPY20)
    Event code 724 (BTN_TRIGGER_HAPPY21)
    Event code 725 (BTN_TRIGGER_HAPPY22)
    Event code 726 (BTN_TRIGGER_HAPPY23)
    Event code 727 (BTN_TRIGGER_HAPPY24)
    Event code 728 (BTN_TRIGGER_HAPPY25)
  Event type 3 (EV_ABS)
    Event code 3 (ABS_RX)
      Value      0
      Min        0
      Max     4095
      Fuzz      15
      Flat     255
    Event code 4 (ABS_RY)
      Value   1457
      Min        0
      Max     4095
      Fuzz      15
      Flat     255
    Event code 5 (ABS_RZ)
      Value    822
      Min        0
      Max     4095
      Fuzz      15
      Flat     255
    Event code 6 (ABS_THROTTLE)
      Value      0
      Min        0
      Max    65535
      Fuzz     255
      Flat    4095
    Event code 7 (ABS_RUDDER)
      Value      0
      Min        0
      Max    65535
      Fuzz     255
      Flat    4095
  Event type 4 (EV_MSC)
    Event code 4 (MSC_SCAN)
Key repeat handling:
  Repeat type 20 (EV_REP)
    Repeat code 0 (REP_DELAY)
      Value    250
    Repeat code 1 (REP_PERIOD)
      Value     33
Properties:
Testing ... (interrupt to exit)
Event: time 1721556084.837383, type 4 (EV_MSC), code 4 (MSC_SCAN), value 9001a
Event: time 1721556084.837383, type 1 (EV_KEY), code 713 (BTN_TRIGGER_HAPPY10), value 1
Event: time 1721556084.837383, -------------- SYN_REPORT ------------
Event: time 1721556084.967497, type 4 (EV_MSC), code 4 (MSC_SCAN), value 9001a
Event: time 1721556084.967497, type 1 (EV_KEY), code 713 (BTN_TRIGGER_HAPPY10), value 0
Event: time 1721556084.967497, -------------- SYN_REPORT ------------
Event: time 1721556086.557322, type 3 (EV_ABS), code 4 (ABS_RY), value 1459
Event: time 1721556086.557322, -------------- SYN_REPORT ------------
Event: time 1721556086.567535, type 3 (EV_ABS), code 4 (ABS_RY), value 1461
Event: time 1721556086.567535, -------------- SYN_REPORT ------------
Event: time 1721556086.577537, type 3 (EV_ABS), code 4 (ABS_RY), value 1463
Event: time 1721556086.577537, -------------- SYN_REPORT ------------

Whole lot of information, some raise more questions than they answer. We get a list of supported events, along with their codes which seem to be mapped to specific input. The buttons sort of make sense except that there are more buttons listed than I can count on the device, and then there are these:

    Event code 300 (?)
    Event code 301 (?)
    Event code 302 (?)

We can also see joystick related event codes where ABS_RY has been triggered below. Turns out those are mapped to the knobs, yes I have 3 knobs but 5 event codes. I can only assume that this is because the company might have started with making joysticks at first and when they expanded their range they wanted to keep a uniform interface to make things easier for them to deal with.

This is a regular button press:

Event: time 1721556084.967497, type 4 (EV_MSC), code 4 (MSC_SCAN), value 9001a
Event: time 1721556084.967497, type 1 (EV_KEY), code 713 (BTN_TRIGGER_HAPPY10), value 0
Event: time 1721556084.967497, -------------- SYN_REPORT ------------

Still unsure what the MSC_SCAN part means but I know that this event is not emitted for switches (which are also buttons). You can also see that a pressed button has a value of 1 and when it gets released it turns to 0. What I had originally thought to be 3 events are actually 2 and I was tricked by MSC_SCAN packets being sent.

Shifting perspective

Given all that, I can correlate what we see here with the raw bytes we were looking at just to fully bridge the gap. What follows is going to be a bizarre dance of hex values so let me explain some xxd flags first. -c sets the number of bytes to be printed per row and -g defines how many bytes should be in the same group (default is 4).

$ cat /dev/input/event14 | xxd -c 24

00000000: f7fc 9c66 0000 0000 877a 0a00 0000 0000 0400 0400 1a00 0900  ...f.....z..............
00000018: f7fc 9c66 0000 0000 877a 0a00 0000 0000 0100 c902 0100 0000  ...f.....z..............
00000030: f7fc 9c66 0000 0000 877a 0a00 0000 0000 0000 0000 0000 0000  ...f.....z..............
00000048: f7fc 9c66 0000 0000 ee64 0b00 0000 0000 0400 0400 1a00 0900  ...f.....d..............
00000060: f7fc 9c66 0000 0000 ee64 0b00 0000 0000 0100 c902 0000 0000  ...f.....d..............
00000078: f7fc 9c66 0000 0000 ee64 0b00 0000 0000 0000 0000 0000 0000  ...f.....d..............

which corresponds to:

Event: time 1721564407.686727, type 4 (EV_MSC), code 4 (MSC_SCAN), value 9001a
Event: time 1721564407.686727, type 1 (EV_KEY), code 713 (BTN_TRIGGER_HAPPY10), value 1
Event: time 1721564407.686727, -------------- SYN_REPORT ------------
Event: time 1721564407.746734, type 4 (EV_MSC), code 4 (MSC_SCAN), value 9001a
Event: time 1721564407.746734, type 1 (EV_KEY), code 713 (BTN_TRIGGER_HAPPY10), value 0
Event: time 1721564407.746734, -------------- SYN_REPORT ------------

and all of a sudden things are a bit clearer. The first 2 bytes act as a clock, the first byte looks like it corresponds to seconds and the second to minutes from my testing. Not in terms of values but in terms of how they change between button presses.

byte    : 0 1  2 3  4 5  6 7  8 9  1011 1213 1415 1617 1819 2021 2223  manually added
00001d28: fefd 9c66 0000 0000 d36e 0000 0000 0000 0000 0000 0000 0000  ...f.....n..............
00001d40: fffd 9c66 0000 0000 7c43 0200 0000 0000 0400 0400 1a00 0900  ...f....|C..............
00001d58: fffd 9c66 0000 0000 7c43 0200 0000 0000 0100 c902 0100 0000  ...f....|C..............
00001d70: fffd 9c66 0000 0000 7c43 0200 0000 0000 0000 0000 0000 0000  ...f....|C..............
00001d88: fffd 9c66 0000 0000 6c16 0400 0000 0000 0400 0400 1a00 0900  ...f....l...............
00001da0: fffd 9c66 0000 0000 6c16 0400 0000 0000 0100 c902 0000 0000  ...f....l...............
00001db8: fffd 9c66 0000 0000 6c16 0400 0000 0000 0000 0000 0000 0000  ...f....l...............
00001dd0: 00fe 9c66 0000 0000 af77 0500 0000 0000 0400 0400 1a00 0900  ...f.....w..............
00001de8: 00fe 9c66 0000 0000 af77 0500 0000 0000 0100 c902 0100 0000  ...f.....w..............
00001e00: 00fe 9c66 0000 0000 af77 0500 0000 0000 0000 0000 0000 0000  ...f.....w..............
00001e18: 00fe 9c66 0000 0000 9073 0700 0000 0000 0400 0400 1a00 0900  ...f.....s..............
00001e30: 00fe 9c66 0000 0000 9073 0700 0000 0000 0100 c902 0000 0000  ...f.....s..............
00001e48: 00fe 9c66 0000 0000 9073 0700 0000 0000 0000 0000 0000 0000  ...f.....s..............

Third and fourth bytes so far seem to be always the same they are always 0x9C66. Then always follow 8 zero-bytes. I can only speculate as to why but for now let's just make a note of that.

Skipping right after the second lump of zero-bytes we get to what evtest shows. Bytes 16-17 make up the event type and bytes 18-19 the event code. Well how is 0xC902 equal to 713? Do you see the pattern yet? No? What if I told you that 0x02C9 is 713? So far we have been assuming a Big Endian representation but the event code has been a dead giveaway that this should be trated as little endian. Let's rewrite our xxd command:

$ cat /dev/input/event14 | xxd -c 24 -e -g 4

00000000: 669d0446 00000000 00092d91 00000000 00040004 0009001a  F..f.....-..............
00000018: 669d0446 00000000 00092d91 00000000 02c90001 00000001  F..f.....-..............
00000030: 669d0446 00000000 00092d91 00000000 00000000 00000000  F..f.....-..............
00000048: 669d0446 00000000 000a667a 00000000 00040004 0009001a  F..f....zf..............
00000060: 669d0446 00000000 000a667a 00000000 02c90001 00000000  F..f....zf..............
00000078: 669d0446 00000000 000a667a 00000000 00000000 00000000  F..f....zf..............

Now you can take the first integer convert it to decimal and you have a unix timestamp while the third integer behaves like nanoseconds. Perspective is indeed worth 80 IQ points... Now, I am willing to bet that those are not integers but longs, so the zero-bytes go to the front and not the back, like so:

$ cat /dev/input/event14 | xxd -c 24 -e -g 8

00000000: 00000000669d0583 00000000000ad3d9 0009001a00040004  ...f....................
00000018: 00000000669d0583 00000000000ad3d9 0000000102c90001  ...f....................
00000030: 00000000669d0583 00000000000ad3d9 0000000000000000  ...f....................
00000048: 00000000669d0583 00000000000c7ffc 0009001a00040004  ...f....................
00000060: 00000000669d0583 00000000000c7ffc 0000000002c90001  ...f....................
00000078: 00000000669d0583 00000000000c7ffc 0000000000000000  ...f....................

The first 2 longs make sense but the rest is made up of smaller types, let me add some spacing:

00000000: 00000000669d0583 00000000000ad3d9 0009001a 0004 0004  ...f....................
00000018: 00000000669d0583 00000000000ad3d9 00000001 02c9 0001  ...f....................
00000030: 00000000669d0583 00000000000ad3d9 00000000 0000 0000  ...f....................
00000048: 00000000669d0583 00000000000c7ffc 0009001a 0004 0004  ...f....................
00000060: 00000000669d0583 00000000000c7ffc 00000000 02c9 0001  ...f....................
00000078: 00000000669d0583 00000000000c7ffc 00000000 0000 0000  ...f....................

I can make out an integer which is the value field shown by evtest and 2 half-integer/short values which are the event code and the event type. And with that, we have fully understood what's on the wire.

What's next?

We have made a huge leap in understanding how the device communicates with the system, what gets send back and forth. While this marks the end of the investigation for now we will be coming back in order to figure out LEDs and how to actually toggle them.

The next step is to finally start writing code for this. I have yet to understand if we do need an actual driver or if we can build on top of usbhid so that will also be explained in the next episode.