Thrustmaster HOTAS Warthog & Linux

Questions and Answers

Do the devices work under Linux?

Yes. They are regular USB-HID devices. All axes and buttons work flawlessly out of the box. Digital »POV« hat switches are reported as axes, like with all other joysticks under Linux.


Is Thrustmaster's configuration utility T.A.R.G.E.T. available for Linux?

No, and that's fine. Most of this application wouldn't be very useful under Linux, anyway.

Thrustmaster/Guillemot were very kind and, at first, seemed willing to answer my questions about how to control the LEDs and backlight, but in the end I didn't get anything that resembles a specification. What I got was something that I hadn't asked for: The confirmation that they don't support Linux and won't any time soon. As if they wanted to make sure that nobody else can support it either — cute in some way, but futile. And disappointing, considering the rather high price. Gathering the missing information will, therefore, have to be done via USB snooping and reverse engineering. All findings will be presented on this page, and eventually a utility will be made available.


Are there ways to configure the devices under Linux without T.A.R.G.E.T.?

Yes. The necessary commands can be sent in various ways, even from a terminal via echo if the kernel has hidraw support compiled in (see below).

Like any other joystick, the Warthog devices provide button, hat view and axis data, which are accessible via joystick device file (/dev/input/js*). But in addition to these »standard« values, the input reports also contain »reserved« data, which aren't exactly »secret«, but kind-of hidden by the joystick device driver. That way, both devices report raw axis values, the throttle additionally reports currently active LEDs and brightness value. The devices can be configured by sending appropriate output records. For more on that see below under the respective device sections.


Joystick

What does lsusb show about the joystick?

Note the spelling error in the vendor string, which must be repeated in udev recipes and other places where the iManufacturer string is used!

$ lsusb -vs 1:14

Bus 001 Device 014: ID 044f:0402 ThrustMaster, Inc. 
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0         8
  idVendor           0x044f ThrustMaster, Inc.
  idProduct          0x0402 
  bcdDevice            1.00
  iManufacturer           1 Thustmaster                     [sic!]
  iProduct                2 Joystick - HOTAS Warthog
  iSerial                 0 
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           41
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0x80
      (Bus Powered)
    MaxPower              100mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      0 No Subclass
      bInterfaceProtocol      0 None
      iInterface              0 
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.11
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength     104
         Report Descriptors: 
           ** UNAVAILABLE **
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x000c  1x 12 bytes
        bInterval               1
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x01  EP 1 OUT
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x000c  1x 12 bytes
        bInterval               1
Device Status:     0x0001
  Self Powered

What does the joystick's HID report (descriptor #34) look like?

  0: 05 01              Usage Page 'Generic Desktop Controls'
  2: 09 04              Usage 'Joystick'
  4: a1 01              Collection 'Application (mouse, keyboard)'
  6: 85 01                      Report ID = 1
  8: 05 09                      Usage Page 'Button'
 10: 19 01                      Usage Minimum = 1
 12: 29 13                      Usage Maximum = 19
 14: 15 00                      Logical Minimum = 0
 16: 25 01                      Logical Maximum = 1
 18: 35 00                      Physical Minimum = 0
 20: 45 01                      Physical Maximum = 1
 22: 75 01                      Report Size = 1
 24: 95 13                      Report Count = 19
 26: 81 02                      Input data [var] abs lin pref-state null-pos non-vol bit-field
 28: 75 01                      Report Size = 1
 30: 95 01                      Report Count = 1
 32: 81 01                      Input [const] array abs lin pref-state null-pos non-vol bit-field
 34: 05 01                      Usage Page 'Generic Desktop Controls'
 36: 09 39                      Usage 'Hat switch'
 38: 25 07                      Logical Maximum = 7
 40: 46 3b 01                   Physical Maximum = 315
 43: 65 14                      Unit = System(English-Rotation) Length(Centimeter)
 45: 75 04                      Report Size = 4
 47: 81 42                      Input data [var] abs lin pref-state null-pos [vol] bit-field
 49: 09 30                      Usage 'X'
 51: 09 31                      Usage 'Y'
 53: 65 00                      Unit = None
 55: 27 ff ff 00 00             Logical Maximum = 65535
 60: 47 ff ff 00 00             Physical Maximum = 65535
 65: 75 10                      Report Size = 16
 67: 95 02                      Report Count = 2
 69: 81 02                      Input data [var] abs lin pref-state null-pos non-vol bit-field
 71: 06 ff 00                   Usage Page 'Reserved'
 74: 09 01                      Usage 0x1
 76: 09 02                      Usage 0x2
 78: 81 02                      Input data [var] abs lin pref-state null-pos non-vol bit-field
 80: 09 bb                      Usage 0xbb
 82: 75 08                      Report Size = 8
 84: 95 0b                      Report Count = 11
 86: 26 ff 00                   Logical Maximum = 255
 89: 46 ff 00                   Physical Maximum = 255
 92: 91 02                      Output data [var] abs lin pref-state null-pos non-vol bit-field
 94: 85 02                      Report ID = 2
 96: 06 ff 00                   Usage Page 'Reserved'
 99: 09 03                      Usage 0x3
101: 81 02                      Input data [var] abs lin pref-state null-pos non-vol bit-field
103: c0                 End Collection

How does the joystick's HID input report look like?

The joystick sends two report types, both of which are 12 bytes long, including the report number (1 or 2) in the first byte. Report #2 is only sent on request and can contain data like the current firmware version. Decoding this information has yet to be done. Report #1 contains physical joystick data, such as button states and axis values, using this layout:

Joystick Input Report (ID #1)
Offset Bytes Bits Description
0 1 8 HID Report ID, always 1
standard data (published by the Linux joystick device driver)
1 3 19 19 buttons
1 padding
4 hat direction (reported as 2 axes under Linux)
4 2 16 X-axis (left/right)
6 2 16 Y-axis (fwd/aft)
reserved (»hidden«) data
8 2 16 raw X-axis (left/right)   [Little Endian 16]
10 2 16 raw Y-axis (fwd/aft)   [Little Endian 16]

Throttle

What does lsusb show about the throttle?

$ lsusb -vs 1:15

Bus 001 Device 015: ID 044f:0404 ThrustMaster, Inc. 
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0         8
  idVendor           0x044f ThrustMaster, Inc.
  idProduct          0x0404 
  bcdDevice            1.00
  iManufacturer           1 Thrustmaster
  iProduct                2 Throttle - HOTAS Warthog
  iSerial                 0 
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           41
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0x80
      (Bus Powered)
    MaxPower              100mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      0 No Subclass
      bInterfaceProtocol      0 None
      iInterface              0 
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.11
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength     171
         Report Descriptors: 
           ** UNAVAILABLE **
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               8
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x02  EP 2 OUT
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               8
Device Status:     0x0000
  (Bus Powered)

What does the throttle's HID report (descriptor #34) look like?


  0: 05 01              Usage Page 'Generic Desktop Controls'
  2: 09 04              Usage 'Joystick'
  4: a1 01              Collection 'Application (mouse, keyboard)'
  6: 85 01                      Report ID = 1
  8: 05 09                      Usage Page 'Button'
 10: 15 00                      Logical Minimum = 0
 12: 25 01                      Logical Maximum = 1
 14: 35 00                      Physical Minimum = 0
 16: 45 01                      Physical Maximum = 1
 18: 19 01                      Usage Minimum = 1
 20: 29 20                      Usage Maximum = 32
 22: 75 01                      Report Size = 1
 24: 95 20                      Report Count = 32
 26: 81 02                      Input data [var] abs lin pref-state null-pos non-vol bit-field
 28: 05 01                      Usage Page 'Generic Desktop Controls'
 30: 09 39                      Usage 'Hat switch'
 32: 15 00                      Logical Minimum = 0
 34: 25 07                      Logical Maximum = 7
 36: 35 00                      Physical Minimum = 0
 38: 46 3b 01                   Physical Maximum = 315
 41: 65 14                      Unit = System(English-Rotation) Length(Centimeter)
 43: 75 04                      Report Size = 4
 45: 95 01                      Report Count = 1
 47: 81 42                      Input data [var] abs lin pref-state null-pos [vol] bit-field
 49: 81 01                      Input [const] array abs lin pref-state null-pos non-vol bit-field
 51: 65 00                      Unit = None
 53: 09 30                      Usage 'X'
 55: 26 ff 03                   Logical Maximum = 1023
 58: 46 ff 03                   Physical Maximum = 1023
 61: 75 0a                      Report Size = 10
 63: 95 01                      Report Count = 1
 65: 81 02                      Input data [var] abs lin pref-state null-pos non-vol bit-field
 67: 75 06                      Report Size = 6
 69: 81 03                      Input [const] [var] abs lin pref-state null-pos non-vol bit-field
 71: 09 31                      Usage 'Y'
 73: 75 0a                      Report Size = 10
 75: 81 02                      Input data [var] abs lin pref-state null-pos non-vol bit-field
 77: 75 06                      Report Size = 6
 79: 81 03                      Input [const] [var] abs lin pref-state null-pos non-vol bit-field
 81: 09 36                      Usage 'Slider'
 83: 75 0a                      Report Size = 10
 85: 81 02                      Input data [var] abs lin pref-state null-pos non-vol bit-field
 87: 75 06                      Report Size = 6
 89: 81 03                      Input [const] [var] abs lin pref-state null-pos non-vol bit-field
 91: 09 32                      Usage 'Z'
 93: 26 ff 3f                   Logical Maximum = 16383
 96: 46 ff 3f                   Physical Maximum = 16383
 99: 75 0e                      Report Size = 14
101: 81 02                      Input data [var] abs lin pref-state null-pos non-vol bit-field
103: 75 02                      Report Size = 2
105: 81 03                      Input [const] [var] abs lin pref-state null-pos non-vol bit-field
107: 09 35                      Usage 'Rz'
109: 75 0e                      Report Size = 14
111: 81 02                      Input data [var] abs lin pref-state null-pos non-vol bit-field
113: 75 02                      Report Size = 2
115: 81 03                      Input [const] [var] abs lin pref-state null-pos non-vol bit-field
117: 75 10                      Report Size = 16
119: 27 ff ff 00 00             Logical Maximum = 65535
124: 47 ff ff 00 00             Physical Maximum = 65535
129: 06 ff 00                   Usage Page 'Reserved'
132: 09 01                      Usage 0x1
134: 09 02                      Usage 0x2
136: 09 03                      Usage 0x3
138: 09 04                      Usage 0x4
140: 09 05                      Usage 0x5
142: 95 05                      Report Count = 5
144: 81 02                      Input data [var] abs lin pref-state null-pos non-vol bit-field
146: 26 ff 00                   Logical Maximum = 255
149: 46 ff 00                   Physical Maximum = 255
152: 75 08                      Report Size = 8
154: 95 0a                      Report Count = 10
156: 81 03                      Input [const] [var] abs lin pref-state null-pos non-vol bit-field
158: 09 bb                      Usage 0xbb
160: 95 23                      Report Count = 35
162: 91 02                      Output data [var] abs lin pref-state null-pos non-vol bit-field
164: 85 02                      Report ID = 2
166: 09 06                      Usage 0x6
168: 81 02                      Input data [var] abs lin pref-state null-pos non-vol bit-field
170: c0                 End Collection

How does the throttle's HID input report look like?

The joystick sends two report types, both of which are 36 bytes long, including the report number (1 or 2) in the first byte. Report #2 is only sent on request and can contain data like the current firmware version. Decoding this information has yet to be done. Report #1 contains physical throttle data, such as button states and axis values, as shown in the following table:

Throttle Input Report (ID #1)
Offset Bytes Bits Description
0 1 8 HID Report ID, always 1
standard data (published by the joystick device driver)
1 4 32 32 buttons
5 1 4 »POV« hat direction (reported as 2 axes under Linux)
4 padding
6 2 10 X-axis (»mouse« left/right)
6 padding
8 2 10 Y-axis (»mouse« fwd/aft)
6 padding
10 2 10 trim wheel (»slider«, throttle friction on real A-10)
6 padding
12 2 14 right throttle (»Z«-axis)
2 padding
14 2 14 left throttle (»Rz«-axis)
2 padding
reserved (»hidden«) data
16 2 16 raw X-axis   [Big Endian 12 bits, 0xfff0]
18 2 16 raw Y-axis   [Big Endian 12 bits, 0xfff0]
20 2 16 raw slider-axis   [Big Endian 13 bits, 0xfff8]
22 2 16 raw Z-axis (right throttle)   [Big Endian 16 bits]
24 2 16 raw Rz-axis (left throttle)   [Big Endian 16 bits]
26 1 8 LED bitmap (see table below)
27 1 8 LED brightness (0 to 5)
28 8 64 probably unused, always 0

The LED bitmap (byte 26) encodes which of the 6 LEDs are currently on (backlight + 5 programmable LEDs), the brightness value (byte 27) ranges from 0 (off) to 5 (maximum brightness) and is used for all 6 LEDs.

LED bitmap
Bit Mask Description
0 0x01 LED #4
1 0x02 LED #2
2 0x04 LED #1 (top)
3 0x08 backlight
4 0x10 LED #3
5 0x20 unused?
6 0x40 LED #5 (bottom)
7 0x80 unused?

How can a throttle's HID output report look like?

Although an output report is officially expected to be as long as an input record — 36 bytes, tests have shown that only the first few meaningful bytes need to be sent. Otherwise it's recommended to initialize the remaining bytes to 0.

Throttle Output Reports (ID #1)
Code (Bytes) Function Description
#1 #2 #3 #4
1 3 ? ? set data? analog to »get data«?
4 a b get data Request data segment at LE16 address '(b<<8)|a'. The device responds with a #2 input report, of which the first byte is 2 (report ID), the second byte is 0 or 0x80, and the next two are the address again, this time in BE16 (big endian). Then follow the requested data bytes — 8 for the stick and 32 for the throttle. This command is also used to read out the segment that contains the firmware version. Bit 7 of b might decide if the data comes from the Flash ROM or the SRAM. Don't rely on this feature, yet — it has yet to be investigated more thorougly.
5 0 0 ?
6 L B set LEDs L is a LED bitmap as described in the above table, B is a brightness value ranging from 0 to 5, where 0 is off and 5 is bright.

The throttle's LED settings aren't preserved after plugging out and in again. For turning all LEDs on at maximum brightness (5) the following output report is to be sent: 0x01, 0x06, 0x5f, 0x05


Which processor does the throttle use?

It uses a Cypress Semiconductor CY8C24894-24LTXI.

hidraw setup under Linux

This section describes how to configure Linux for easy control of your Warthog throttle and stick using the Linux kernel's HIDRAW devices.

1. Check if your kernel supports HIDRAW

If you have a HID device plugged in (USB keyboard or mouse, joystick, etc) then you should find at least one hidraw device:

$ ls /dev/hidraw*
/dev/hidraw0  /dev/hidraw2  /dev/hidraw3

If there are no hidraw devices, make sure your kernel has the CONFIG_HIDRAW variable set to »y« or »m« and recompile/re-install your kernel if necessary.

2. Setup udev for persistent HIDRAW device links

To be able to access your Warthog devices always under the same name, configure udev to provide persistent HIDRAW device links. Put the following code in a local udev rules file. Create one, if necessary, let's say /etc/udev/rules.d/00-local.rules:

SUBSYSTEM=="input", ENV{ID_INPUT_JOYSTICK}!="?*", IMPORT{program}="input_id %p"
SUBSYSTEM=="hidraw", ENV{ID_SERIAL}!="?*", IMPORT{program}="usb_id --export %p"
SUBSYSTEM=="hidraw", SYMLINK+="input/hidraw/%E{ID_SERIAL}"
SUBSYSTEM=="hidraw", ENV{ID_INPUT_JOYSTICK}!="0", GROUP:="js"

If you don't have a user group called »js« (for joystick hardware access) you can use any other group where all joystick users are member, e.g. »users«. Now plug your devices out and in again, and check if the device links have been created, and if the hidraw devices have proper permissions:

$ ls -l /dev/input/hidraw/*
lrwxrwxrwx 1 root root 10 Oct  2 14:23 Thrustmaster_Throttle_-_HOTAS_Warthog -> ../../hidraw1
lrwxrwxrwx 1 root root 10 Oct  2 14:23 Thustmaster_Joystick_-_HOTAS_Warthog -> ../../hidraw0

$ ls -l /dev/hidraw*
crw-rw---- 1 root js 251, 0 Oct  2 09:11 /dev/hidraw0
crw-rw---- 1 root js 251, 1 Oct  2 14:23 /dev/hidraw1

3. Test

Now you can easily test your setup. The first line will turn all LEDs off, the second will turn them all on with maximum brightness.

$ echo -e "\x01\x06\x08\x00" >/dev/input/hidraw/Thrustmaster_Throttle_-_HOTAS_Warthog
$ echo -e "\x01\x06\x5f\x05" >/dev/input/hidraw/Thrustmaster_Throttle_-_HOTAS_Warthog

Firmware Upgrades

To upgrade either device, one has first to power it up while keeping two buttons pressed (»trigger« and »weapon release« on the stick, or the two push buttons on the throttle base, that is »AP-engage« and »gear warning silence«). After plugging in, one can release the buttons. This procedure resets the firmware and puts the devices in boot mode, after which they identify with a product number lowered by one, relative to the number in normal mode (0x0401 for the stick, and 0x0403 for the throttle). The USB and HID descriptors will then probably look different. More on that later.



Last change: Mon Oct 10 18:12:29 CEST 2011
If you have more questions or information, please write to  < melchior : franz # gmail : com >.