Wireless Joystick for P4 Model No 8952

## Input

* 13× Digital Buttons
	+ Face: , , ,
	+ Shoulder: L1, R1
	+ Other: "PS"/🏠, SHARE, OPTIONS, touchpad click, L3, R3, CLEAR
* Digital Directional Pad
* Touch Pad
	+ 2× Point
	+ Capacitive
	+ Data Resolution: 940x940
* 2× Analog Sticks
* 2× Analog Triggers
	+ L2, R2
* Motion
	+ 3 Axis Accelerometer
	+ 3 Axis Gyroscope
	+ Mono Microphone via TRRS jack

## Output

* Rumble via ERM
	+ Right: Light (2 weight)
	+ Left: Heavy (4 weights)
* RGB LED (24 bit color)
* Mono Speaker
	+ Bluetooth Mode Only (untested)
* Stereo Audio via TRRS jack
	+ Bluetooth Mode Only (untested)

## Ports

* microUSB (Micro-B) 2.0 ⚡ 🖥️
* 3.5mm Phone TRRS 🎤 🔈

# HID Interface

This gamepad is a variation of the Sony DualShock 4 that matches most of its interface.

## Quirks

### MAC / Serial Number

The MAC can be accessed by feature read 0x12 on USB and by reading the Serial Number as a hex string on Bluetooth.

### Temperature

The battery temperature value is always 0x00.

### Pad Resolution

This pad has a reduced touch data resolution vs the normal pad that should be accounted for.

### Additional Button

Additionally, the button intended for clearing the turbo function can be read from the input report and utilized as an additional button. For USB, the button can safely be read from `USBGetStateData.ButtonClear` at any input frame. For Bluetooth the button can be read from `BTGetStateData.ButtonClear` but must be ring-buffered to ensure the value is influenced by the button and not the counter element of the report. The best solution for this is to use a single byte and left shift per report, pushing the newest bit to the least significant bit. If this entire byte is 0xff, the button can be considered pressed. A quicker but less accurate check can be done by checking that only bits 0x7f, 0x3f, 0x1f, 0x0f, or less are high.

```
ExtraButtonRingBuffer = ExtraButtonRingBuffer << 1 | BTGetStateData.ButtonClear;
ButtonExtra = ExtraButtonRingBuffer == 0xff;
ButtonExtraFast = (ExtraButtonRingBuffer & 0x7f) == 0x7f;
ButtonExtraFaster = (ExtraButtonRingBuffer & 0x3f) == 0x3f;
ButtonExtraFasterer = (ExtraButtonRingBuffer & 0x1f) == 0x1f;
ButtonExtraFastest = (ExtraButtonRingBuffer & 0x0f) == 0x0f;

```

### Enable Bits

On USB the bits `SetStateData.Enable*` have no affect for this controller. It is impossible to set rumble and and not set the LED color or visa-versa. On Bluetooth the `SetStateData.EnableRumbleUpdate` flag has no affect. The `SetStateData.EnableLedUpdate` flag does double duty allowing both rumble and LED color setting. LED colors of 0x000000 are ignored.

## Descriptor

### USB

```
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x05, // Usage (Game Pad)
0xA1, 0x01, // Collection (Application)
0x85, 0x01, // Report ID (1)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x09, 0x32, // Usage (Z)
0x09, 0x35, // Usage (Rz)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x75, 0x08, // Report Size (8)
0x95, 0x04, // Report Count (4)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x09, 0x39, // Usage (Hat switch)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x07, // Logical Maximum (7)
0x35, 0x00, // Physical Minimum (0)
0x46, 0x3B, 0x01, // Physical Maximum (315)
0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter)
0x75, 0x04, // Report Size (4)
0x95, 0x01, // Report Count (1)
0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)
0x65, 0x00, // Unit (None)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (0x01)
0x29, 0x0E, // Usage Maximum (0x0E)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x0E, // Report Count (14)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
0x09, 0x20, // Usage (0x20)
0x75, 0x06, // Report Size (6)
0x95, 0x01, // Report Count (1)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x7F, // Logical Maximum (127)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x33, // Usage (Rx)
0x09, 0x34, // Usage (Ry)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x75, 0x08, // Report Size (8)
0x95, 0x02, // Report Count (2)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
0x09, 0x21, // Usage (0x21)
0x95, 0x36, // Report Count (54)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x85, 0x05, // Report ID (5)
0x09, 0x22, // Usage (0x22)
0x95, 0x1F, // Report Count (31)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x04, // Report ID (4)
0x09, 0x23, // Usage (0x23)
0x95, 0x24, // Report Count (36)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x02, // Report ID (2)
0x09, 0x24, // Usage (0x24)
0x95, 0x24, // Report Count (36)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x08, // Report ID (8)
0x09, 0x25, // Usage (0x25)
0x95, 0x03, // Report Count (3)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x10, // Report ID (16)
0x09, 0x26, // Usage (0x26)
0x95, 0x04, // Report Count (4)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x11, // Report ID (17)
0x09, 0x27, // Usage (0x27)
0x95, 0x02, // Report Count (2)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x12, // Report ID (18)
0x06, 0x02, 0xFF, // Usage Page (Vendor Defined 0xFF02)
0x09, 0x21, // Usage (0x21)
0x95, 0x0F, // Report Count (15)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x13, // Report ID (19)
0x09, 0x22, // Usage (0x22)
0x95, 0x16, // Report Count (22)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x14, // Report ID (20)
0x06, 0x05, 0xFF, // Usage Page (Vendor Defined 0xFF05)
0x09, 0x20, // Usage (0x20)
0x95, 0x10, // Report Count (16)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x15, // Report ID (21)
0x09, 0x21, // Usage (0x21)
0x95, 0x2C, // Report Count (44)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x06, 0x80, 0xFF, // Usage Page (Vendor Defined 0xFF80)
0x85, 0x80, // Report ID (128)
0x09, 0x20, // Usage (0x20)
0x95, 0x06, // Report Count (6)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x81, // Report ID (129)
0x09, 0x21, // Usage (0x21)
0x95, 0x06, // Report Count (6)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x82, // Report ID (130)
0x09, 0x22, // Usage (0x22)
0x95, 0x05, // Report Count (5)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x83, // Report ID (131)
0x09, 0x23, // Usage (0x23)
0x95, 0x01, // Report Count (1)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x84, // Report ID (132)
0x09, 0x24, // Usage (0x24)
0x95, 0x04, // Report Count (4)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x85, // Report ID (133)
0x09, 0x25, // Usage (0x25)
0x95, 0x06, // Report Count (6)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x86, // Report ID (134)
0x09, 0x26, // Usage (0x26)
0x95, 0x06, // Report Count (6)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x87, // Report ID (135)
0x09, 0x27, // Usage (0x27)
0x95, 0x23, // Report Count (35)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x88, // Report ID (136)
0x09, 0x28, // Usage (0x28)
0x95, 0x22, // Report Count (34)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x89, // Report ID (137)
0x09, 0x29, // Usage (0x29)
0x95, 0x02, // Report Count (2)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x90, // Report ID (144)
0x09, 0x30, // Usage (0x30)
0x95, 0x05, // Report Count (5)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x91, // Report ID (145)
0x09, 0x31, // Usage (0x31)
0x95, 0x03, // Report Count (3)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x92, // Report ID (146)
0x09, 0x32, // Usage (0x32)
0x95, 0x03, // Report Count (3)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x93, // Report ID (147)
0x09, 0x33, // Usage (0x33)
0x95, 0x0C, // Report Count (12)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xA0, // Report ID (160)
0x09, 0x40, // Usage (0x40)
0x95, 0x06, // Report Count (6)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xA1, // Report ID (161)
0x09, 0x41, // Usage (0x41)
0x95, 0x01, // Report Count (1)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xA2, // Report ID (162)
0x09, 0x42, // Usage (0x42)
0x95, 0x01, // Report Count (1)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xA3, // Report ID (163)
0x09, 0x43, // Usage (0x43)
0x95, 0x30, // Report Count (48)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xA4, // Report ID (164)
0x09, 0x44, // Usage (0x44)
0x95, 0x0D, // Report Count (13)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xA5, // Report ID (165)
0x09, 0x45, // Usage (0x45)
0x95, 0x15, // Report Count (21)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xA6, // Report ID (166)
0x09, 0x46, // Usage (0x46)
0x95, 0x15, // Report Count (21)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xF0, // Report ID (240)
0x09, 0x47, // Usage (0x47)
0x95, 0x3F, // Report Count (63)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xF1, // Report ID (241)
0x09, 0x48, // Usage (0x48)
0x95, 0x3F, // Report Count (63)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xF2, // Report ID (242)
0x09, 0x49, // Usage (0x49)
0x95, 0x0F, // Report Count (15)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xA7, // Report ID (167)
0x09, 0x4A, // Usage (0x4A)
0x95, 0x01, // Report Count (1)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xA8, // Report ID (168)
0x09, 0x4B, // Usage (0x4B)
0x95, 0x01, // Report Count (1)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xA9, // Report ID (169)
0x09, 0x4C, // Usage (0x4C)
0x95, 0x08, // Report Count (8)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xAA, // Report ID (170)
0x09, 0x4E, // Usage (0x4E)
0x95, 0x01, // Report Count (1)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xAB, // Report ID (171)
0x09, 0x4F, // Usage (0x4F)
0x95, 0x39, // Report Count (57)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xAC, // Report ID (172)
0x09, 0x50, // Usage (0x50)
0x95, 0x39, // Report Count (57)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xAD, // Report ID (173)
0x09, 0x51, // Usage (0x51)
0x95, 0x0B, // Report Count (11)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xAE, // Report ID (174)
0x09, 0x52, // Usage (0x52)
0x95, 0x01, // Report Count (1)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xAF, // Report ID (175)
0x09, 0x53, // Usage (0x53)
0x95, 0x02, // Report Count (2)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xB0, // Report ID (176)
0x09, 0x54, // Usage (0x54)
0x95, 0x3F, // Report Count (63)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection

// 467 bytes

```

### Input Reports

```
struct GetStateData {
 uint8\_t LeftStickX;
 uint8\_t LeftStickY;
 uint8\_t RightStickX;
 uint8\_t RightStickY;
 Direction DPad : 4;
 uint8\_t ButtonSquare : 1;
 uint8\_t ButtonCross : 1;
 uint8\_t ButtonCircle : 1;
 uint8\_t ButtonTriangle : 1;
 uint8\_t ButtonL1 : 1;
 uint8\_t ButtonR1 : 1;
 uint8\_t ButtonL2 : 1;
 uint8\_t ButtonR2 : 1;
 uint8\_t ButtonShare : 1;
 uint8\_t ButtonOptions : 1;
 uint8\_t ButtonL3 : 1;
 uint8\_t ButtonR3 : 1;
 uint8\_t ButtonHome : 1;
 uint8\_t ButtonPad : 1;
 union {
 uint8\_t ButtonClear : 1; // this button shares a bit with Counter, so it can give false highs
 uint8\_t Counter : 6; // always 0 on USB, counts up with some skips on BT
 }
 uint8\_t TriggerLeft;
 uint8\_t TriggerRight;
 uint16\_t Timestamp; // in 5.33us units?
 uint8\_t Temperture;
 int16\_t AngularVelocityX;
 int16\_t AngularVelocityZ;
 int16\_t AngularVelocityY;
 int16\_t AccelerometerX;
 int16\_t AccelerometerY;
 int16\_t AccelerometerZ;
 uint8\_t[5] ExtData; // range can be set by EXT device
 uint8\_t PowerPercent : 4; // 0x00-0x0A or 0x01-0x0B if plugged int
 uint8\_t PluggedPowerCable : 1;
 uint8\_t PluggedHeadphones : 1;
 uint8\_t PluggedMic : 1;
 uint8\_t PluggedExt : 1;
 uint8\_t UnkExt1 : 1; // ExtCapableOfExtraData?
 uint8\_t UnkExt2 : 1; // ExtHasExtraData?
 uint8\_t NotConnected : 1; // Used by dongle to indicate no controller
 uint8\_t Unk1 : 5;
 uint8\_t Unk2; // unused?
 uint8\_t TouchCount;
};

```