greggersaurus / OpenSteamController

Steam Controller reverse engineering and customization project.
462 stars 39 forks source link

Document HID Controller Interface protocol #23

Open roblabla opened 4 years ago

roblabla commented 4 years ago

This is an issue to gather information about the HID Controller Interface protocol, as described here.

Look at #25 for my current progress.

Old information The HID Controller Interface is interfaced either via Firmware USB EP3 , Bootloader USB EP1, or via some currently unknown wireless mechanism. It works by transferring buffers of 0x40 bytes. The first byte is a packet id, while the second byte is the length of the additional data. Here is a packetid table containing both bootloader and firmware packets as of 57bf5c10. Host->Controller: Packet ID | Name | Controller Bootloader | Controller Firmware | Dongle | Description ----------|--------|---------------------|------------------|------------|---------- 0x80 | | ⛔️ | ✅ | | 0x81 | ExitLizardMode | ⛔️ | ✅ | ❓ | Exits the "lizard mode" - Tells the steam controller to acting like a dumb keyboard/mouse combo. Might need to be sent periodically? 0x82 | | ⛔️ | ✅ | | 0x83 | [ControllerInfoRequest](#ControllerInfoRequest) | ✅ | ✅ | | Asks the controller to send its ControllerInfo. 0x85 | | ⛔️ | ✅ | | 0x87 | [SetSettings](#SetSettings) | ⛔️ | ✅ | ❓| Sets the controller settings 0x89 | | ⛔️ | ✅ | | 0x8d | | ⛔️ | ✅ | | 0x8e | | ⛔️ | ✅ | | 0x8f | TriggerHapticPulse | ⛔️ | ✅ | | 0x90 | ReinvokeISP | ✅ | ✅ | ❓ | Reinvokes the LPC ISP Firmware (ISP being what happens when you boot while holding the right trigger). Seems to also work on the dongle? 0x91 | EraseLPCFirmware | ✅ | ⛔️ | | Erases the LPC firmware. 0x92 | FlashLPCFirmware | ✅ | ⛔️ | | Flashes a chunk of LPC firmware. 0x93 | VerifyLPCFirmware | ✅ | ⛔️ | | Finishes flashing the LPC firmware, and validates it against a checksum. 0x95 | ResetSOC | ✅ | ✅ | | Restarts both LPC and NRF CPUs. 0x96 | SetPrngEntropy | ⛔️ | ⛔️ | ✅ | Sends a block of 0x10 random bytes to the device. 0x97 | EraseNRFFirmware | ✅ | ✅ | | (Supposedly, no RE) Erases the NRF Firmware 0x98 | FlashNRFFirmware | ✅ | ✅ | | (Supposedly, no RE) Flashes a chunk of NRF Firmware 0x99 | VerifyNRFFirmware | ✅ | ✅ | | (Supposedly, no RE) Finishes flashing the NRF firmware, and validates it against a checksum. 0x9a | | ⛔️ | ✅ | | Sends data to the nRF chip, wrapped in a packet ']'. 0x9f | TurnOffController | ⛔️ | ✅ | ✅ | 0xa7 | CalibrateTrackpads | ⛔️ | ✅ | | 0xa9 | | ⛔️ | ✅ | | 0xa0 | SetHardwareVersion | ✅ | ⛔️ | | THIS COMMAND IS EXTREMELY DANGEROUS 0xaa | | ⛔️ | ✅ | | 0xab | | ⛔️ | ✅ | | 0xac | | ⛔️ | ✅ | | 0xad | SetDonglePairingMode | ⛔️ | ⛔️ | ✅ | 2 byte args 0xae | ControllerInfoRequest | ⛔️ | ✅ | | 0xb1 | SetControllerKeyboardMouseInputState | ⛔️ | ⛔️ | ✅ | 0xb2 | PairingFailed | ⛔️ | ⛔️ | ✅ | 0xb3 | PairingSuccess | ⛔️ | ⛔️ | ✅ | 0xb4 | GetControllerInfo | ⛔️ | ⛔️ | ✅ | 0xb5 | CalibrateIMU | ⛔️ | ✅ | | 0xb6 | PlayAudio | ⛔️ | ✅ | | Plays the selected jingle 0xb7 | StartFlashJingle | ⛔️ | ✅ | | Does something weird with the jingle_data_ptr :eyes: 0xb8 | FlashJingle | ⛔️ | ✅ | | Writes data to the jingle flash buffer 0xb9 | EndFlashJingle | ⛔️ | ✅ | | Finishes flashing a jingle, writing it to eeprom after making sure it looks somewhat valid. 0xba | GetChipID | ⛔️ | ✅ | | 0xbb | ReadUID | ⛔️ | ✅ | | Returns the result of ISP ReadUID. 0xbf | CalibrateJoystick | ⛔️ | ✅ | | Sets eeprom field 0x34 with some computed data 0xc1 | SetAudioMapping | ⛔️ | ✅ | | Sets the jingle to play for various events 0xc5 | SetUserLedColor | ⛔️ | ⛔️ | | 0xc6 | SendIRCode | ⛔️ | ⛔️ | | 0xc7 | StopIR | ⛔️ | ⛔️ | | # ControllerInfoRequest/ControllerInfoResponse When sent from Host to Controller, takes no data and asks the controller to send its ControllerInfo. When sent from Controller to Host: The additional data is an array of HardwareInfo, where HardwareInfo is a structure of one byte (Type) and 4 byte (Data). Here are the different types byte observed: Type ID | Name | Bootloader | Description ----------|--------|-----------|--------- 1 | USB PID | ✅ | The same as the USB PID. Likely more useful for wireless transmission. 2 | Unknown | ⛔️ | Firmware always returns 3 4 | Firmware Version | ✅ | Version/timestamp of the firmware running on the LPC side (e.g. 57bf5c10). 5 | NRF Firmware Version | ⛔️ | Version/timestamp of the firmware running on the NRF side 9 | Hardware Version | ✅ | Version of the controller hardware (as stored in EEPROM) 10 | Unknown | ⛔️ | Firmware returns the data stored at DAT_10000078 # SetSettings When sent from Host to Controller: Sets the controller settings. The additional data is an array of ControllerSetting, where ControllerSetting is a structure of one byte (type) and 2 bytes (value). Here are some observed type bytes: Type ID | Name | Description -------|----------|------------- 0x3 | ? | 0x8 | ? | Lizard mode related? Data is 7 when sent to dongle. 0x2d | ? | Sent periodically # SetDonglePairingMode Arguments: - Don't pair: 0x00 0x00 - Start pairing: 0x2 0x3c
roblabla commented 4 years ago

Alright small update: I managed to identify the code in the steam client that interacts with the Steam Controller. And fortunately, a lot of symbols and assert messages are left over, allowing me to find the official name for more or less every single packet, along with a whole host of packets that seem to be used for other valve hardware - The valve index and steam controller share a lot of code.

I'll update the table above with this newfound information as I go.

tiehichi commented 4 years ago

Hello, I am trying to analyze the protocol of Steam Controller BLE HID, I updated it here, hope it can help you. There are some data that I haven’t had time to update, because English is not my native language, I write very slowly😂. Your document is very helpful to me, looking forward to your update😄

roblabla commented 4 years ago

Hey @tiehichi thanks for this, it's pretty useful! I've been focusing on this weird proprietary protocol and haven't looked at the actual input reports at all so far :). The HID layer is identical both for BLE, USB and Enhanced ShockBurst (the protocol used to talk to the dongle), so a lot of your findings should apply everywhere.

I'll make a PR later today with my latest findings, in a couple of proper markdowns :). I've figured out a couple of interesting things - it seems there's still some hidden features in the Steam Controller firmware that are going to be fun to play around with ^^.

nrc4867 commented 4 years ago

@tiehichi I have previously categorized some of the meaning behind the bytes in valve mode.

I think that the second byte is a mode indicator. I haven't dug into what the meaning of the bytes are when the byte is 0x05 but I have the meaning of the bytes when the mode is 0x04.

The steam controller seems to have 5 distinct modes; The modes are on the second byte of the packet.

Mode Byte Value Description
Idle 0x05 The controller is in an idle state
Active 0x04 The controller is in an active state (Or waiting for input before switching back to idle)
Buttons 0x14 A button is being pressed/released
Triggers 0x24 A trigger is being depressed
Joysticks 0x84 The joystick is being used

When using more than one mode (aka, buttons and joysticks) these modes are or'ed together.

The touch pads modes are on the third byte of the packet. When the touch pads are in use the second byte is set to active and the following mode conditions are set.

Touchpad-Mode Byte Value Description
Left pad 0x01 Left touch pad
Right pad 0x02 Right touch pad

Similarly when both pads are in use the bytes are or'ed together for a value of 0x03.

Then the inputs always start on the forth byte. When all controls are being touched the controls are on the packet in the order: buttons->triggers->joystick->touchpads. All the input cannot fit on one packet so the right touch pad seems to be left off.

When buttons are active they take up 3 bytes of space: Button Byte Value
A 4 0x80
B 4 0x20
X 4 0x40
Y 4 0x10
Left Shoulder 4 0x08
Right Shoulder 4 0x04
Left Trigger Press 4 0x02
Right Trigger Press 4 0x01
Home 5 0x20
Select 5 0x10
Play 5 0x40
Back Left 5 0x80
Back Right 6 0x01
Joystick Press 6 0x40
Left Touch pad press 6 0x02
Right Touch Press 6 0x04
When the trigger is depressed it takes up 2 bytes of space. Trigger Byte
Left 4
Right 5

When the joystick is active it takes up 4 bytes of space. this can be converted into a short and then divided by 32767 for a float between -1 to +1.

Joystick Byte Description
4 & 5 The Y value
6 & 7 The X value

Finally the touch pads can appear in the packet independently. Each touch pad takes up 4 bytes of space and when both are used the bytes the right touch pad uses are shifted so they appear as left touch pad->right touch pad, in the packet.

touch pad Byte Description
Left (4, 5) & (6, 7) The X/Y value
Right (4, 5) & (6, 7) The X/Y value
tiehichi commented 4 years ago

Hey @roblabla , looking forward to your update, these features must be very cool! Hello @nrc4867 , thank you very much for these information! I am trying to connect SteamController to nintendo switch using ESP32, these data are very useful for me! And if I have any new discoveries, I will share them in time😁

MichaelZaugg commented 5 months ago

Dead?