public
Last active

I’m interested in writing (or helping to spec out the protocol so someone else can write) the linux kernel driver for Sony’s DualShock 4 (PS4’s lovely controller) Currently using the output of HID RAW from a USB connected dualshock 4… For the gyro/touchpad discovery, I’ve just been using some terrible c code to throw numbers on the screen and iterating in how I’m pulling the data out, this has worked so far. Please, comment if this is helpful or you find a fault, link to your projects that use or find inspiration, generally lets improve support… Vendor 054c Device 05c4 Fullname Sony Computer Entertainment Wireless Controller

  • Download Gist
dualshock-research
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
TADA, it’s `hexdump -v -e '64/1 "%02x" "\n"' < /dev/hidraw3`
 
No idea what the first byte is… but I’m going to assume its for device ID for the many users that are connected, but it probably has to be set by the connected machine?
 
01ff777f7f0800aa0000435dfdf1ff14000200c5ff0721150300000000001b000001fc9133a32990880428008000000080000000008000000080000000008000
↑↑↑↑
left stick, value, first field is horz (00 left), second field is vertical (00 top)
 
017f80ff61080064000059f2fdfffffbff0e00d107081e9bf600000000001b0000018e94b1b00690880428008000000080000000008000000080000000008000
↑↑↑↑
right stick, value, first field is horz (00 left), second field is vertical (00 top)
 
017e7f7c8078000800009688fdfffffdfffbff8900c320d10700000000001b000001bc8e95201485413626008000000080000000008000000080000000008000
face buttons, bitmask, right side: 0 unpress, 1 for square, 2 for X, 4 for circle, 8 for triangle, combinations up to f
 
017e7f7c8078000800009688fdfffffdfffbff8900c320d10700000000001b000001bc8e95201485413626008000000080000000008000000080000000008000
face buttons, value, left side: 8 for no value, 0 for up, 1 for topright, 2 for right and it continues clockwise until 7 for topleft
 
01738b797f08401800008c7bfdffffffff02009500ab20890800000000001b000001518f0ed23a90880428008000000080000000008000000080000000008000
face buttons, bitmask, 0 unpressed, L3/R3 aka analog stick click ins, 1 for share, 2 for options, 4 for L3, 8 for R3,
 
0181827c7e0803540000b8ccfdb40021002a00f5004b1fdd0700000000001b00000185a4eab40d9fc2960f008000000080000000008000000080000000008000
triggers, bitmask, 0 unpressed, 1 for L1, 2 for R2, 4 for L2, 8 for R2
 
017e827b7e08005800001861fe0000010002008d00a220d70700000000001b000001d5a3047431a2b8631b008000000080000000008000000080000000008000
017e817b7e08005c00000764fe0000fbfffcff91009b20d10700000000001b000001d5a3047431a2b8631b008000000080000000008000000080000000008000
017e827b7e0800600000f766fe03000500f7ff9900b720bd0700000000001b000001d5a3047431a2b8631b008000000080000000008000000080000000008000
↑↑
event, 6 bits, incremented once each query, to track updates
face button, bitmask, last 2 bits of this field, 1 for PS button, 2 for trackpad click // thanks to @evilhackerdude
 
0181837c7e080c4cffffff20fddcffdfff8d0031fdcf1c09f700000000001b00000185a4eab40d9fc2960f008000000080000000008000000080000000008000
↑↑↑↑
triggers, value, first is L2 (00 unpressed), second is R2 (00 unpressed)
 
017d807f800800f00000985bfd070057009bfe8d0d1b1f6d0500000000001b0000012ba9b84116ab86e512008000000080000000008000000080000000008000
↑↑↑↑
UNKNOWN time from gyro? ever incrementing, byteorder swapped.
this data is bothering me atm…
 
### BATTERY
017d807f800800f00000985bfd070057009bfe8d0d1b1f6d0500000000001b0000012ba9b84116ab86e512008000000080000000008000000080000000008000
↑↑
battery level, 00 to ff
 
### GYRO
017d807f800800f00000985bfd070057009bfe8d0d1b1f6d0500000000001b0000012ba9b84116ab86e512008000000080000000008000000080000000008000
↑↑↑↑↑↑↑↑↑↑↑↑
Relative gyro motion, byteorderswapped, pitch, yaw, roll
 
017d807f800800f00000985bfd070057009bfe8d0d1b1f6d0500000000001b0000012ba9b84116ab86e512008000000080000000008000000080000000008000
↑↑↑↑↑↑↑↑↑↑↑↑
Absolute gyro, byteorderswapped, roll, yaw, pitch. 0 for pitch is horizon
Still debating the values and labelling here. Certainly absolute, might have naming muddled.
 
017d807f800800f00000985bfd070057009bfe8d0d1b1f6d0500000000001b0000012ba9b84116ab86e512008000000080000000008000000080000000008000
↑↑↑↑↑↑↑↑↑↑
Unknown! We still have to find LED colour and active rumble output, so I expect these here.
 
### HEADSET/EXT
018080787e0800b000009cb3fa07000200ffff71007b203d0700000000007b000001af94b9b52180000000008000000080000000008000000080000000008000
↑↑
Appears to be EXT info, bitmask, 00011011 is nothing attached. 01111011 is headset with mic. 00111011 is headphones.
 
018080787e0800b000009cb3fa07000200ffff71007b203d0700000000007b000001af94b9b52180000000008000000080000000008000000080000000008000
↑↑↑↑↑
Unknown! I expect, but cannot confirm until Sony releases additional products, that the 5 nibbles next are bitmaps for control
commands like volume, etc.
 
### TOUCHPAD
Touches are: 1 bit of event active, 7 bits of event id, followed by 3 bytes of positional data.
Supports 2 active touches, 2 previous touches. Remaining data is unset (for all I can see)
 
017e827b7f08002c0000e425fdffff0000feff95006720e90700000000001b00000148101ca22509b5b51b008000000080000000008000000080000000008000
↑↑
Says how many packets to read, only seen 01 or 02 set
A packet contains two touch events, touches can be ‘null’ by being 80000000
017e81767e0800b800001060f8fdff00000500b5008720cd0700000000001b000000008000000080000000008000000080000000008000000080000000008000
017e817f7e0800bc000017a1fdfdff010001008d00a320d10700000000001b0000023c0b5b941a87dec01b410b5e941a87dec01b008000000080000000008000
 
017e827b7f08002c0000e425fdffff0000feff95006720e90700000000001b00000148101ca22509b5b51b008000000080000000008000000080000000008000
↑↑
This appears to be another auto incrementing number to track last update, but need to do research…
 
017e827b7f08002c0000e425fdffff0000feff95006720e90700000000001b00000148101ca22509b5b51b008000000080000000008000000080000000008000
↑↑ ↑↑
these values are tracking numbers, unique to each finger down, so for each lift and repress, it gets a newly incremented figure
 
017e827f7e08003000008996fc000000000300a900af20e90700000000001b0000012c728a4028731f040e008000000080000000008000000080000000008000
↑↑↑↑↑↑ ↑↑↑↑↑↑
these values are for each fingers’ location, they stay static upon finger lifting, to maintain state. Not sure how it tracks if user is still holding, yet…
To decode, each coord is using 12 bits… you need to mask/split and swap the middle byte
01401D → 0001 468
604738 → 1888 900
Basically this, but written well :^)
short finger1x = ((buf[37] & 0x0F) << 8) | buf[36];
short finger1y = buf[38] << 4 | (buf[37]>>4);
 
017e817f7e0800bc000017a1fdfdff010001008d00a320d10700000000001b0000023c0b5b941a87dec01b410b5e941a87dec01b008000000080000000008000
↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
as finger moves over the touchpad, a duplicate motion (likely previous position?) is sent, for both fingers, even if just one is moving
 
Things I still haven’t discovered: LED status/colour, active rumble status, confirmation of gyro labelling
 
Audio is dealt with using Bluetooth’s headset spec; haven’t been able to confirm if this is exposed at all over USB, or if it
is meant to trigger automatic Bluetooth pairing and audio connection.
trackPadButton: (data[7] & 2) !== 0
psButton: (data[7] & 1) !== 0
trackPadTouch0Id: data[35] & 127,
trackPadTouch0Active: (data[35] >> 7) === 0,

Ah, many thanks for the track/ps button find, was going to dig through the other values at the bit level next.

Here's my current complete nodejs Buffer -> Object mapping code:

{
    leftAnalogX: buf[1],
    leftAnalogY: buf[2],
    rightAnalogX: buf[3],
    rightAnalogY: buf[4],
    l2Analog: buf[8],
    r2Analog: buf[9],

    dPadUp: buf[5] === 0 || buf[5] === 1 || buf [5] === 7,
    dPadRight: buf[5] === 1 || buf[5] === 2 || buf [5] === 3,
    dPadDown: buf[5] === 3 || buf[5] === 4 || buf [5] === 5,
    dPadLeft: buf[5] === 5 || buf[5] === 6 || buf [5] === 7,

    x: (buf[5] & 32) !== 0,
    cricle: (buf[5] & 64) !== 0,
    square: (buf[5] & 16) !== 0,
    triangle: (buf[5] & 128) !== 0,

    l1: (buf[6] & 0x01) !== 0,
    l2: (buf[6] & 0x04) !== 0,
    r1: (buf[6] & 0x02) !== 0,
    r2: (buf[6] & 0x08) !== 0,
    l3: (buf[6] & 0x40) !== 0,
    r3: (buf[6] & 0x80) !== 0,

    share: (buf[6] & 0x10) !== 0,
    options: (buf[6] & 0x20) !== 0,
    trackPadButton: (buf[7] & 2) !== 0,
    psButton: (buf[7] & 1) !== 0,

    //// usb only, doesn't work via bluetooth
    //// GYRO
    // TODO
    //// TRACKPAD
    trackPadTouch0Id: buf[35] & 0x7f,
    trackPadTouch0Active: (buf[35] >> 7) === 0,
    trackPadTouch0X: ((buf[37] & 0x0f) << 8) | buf[36],
    trackPadTouch0Y: buf[38] << 4 | ((buf[37] & 0xf0) >> 4),

    trackPadTouch1Id: buf[39] & 0x7f,
    trackPadTouch1Active: (buf[39] >> 7) === 0,
    trackPadTouch1X: ((buf[41] & 0x0f) << 8) | buf[40],
    trackPadTouch1Y: buf[42] << 4 | ((buf[41] & 0xf0) >> 4),

    timestamp: buf[7] >> 2,
    battery: buf[12],
    batteryShort1: buf[12] & 0x0f,
    batteryShort2: buf[12] & 0xf0,
  }

Noticed psdevwiki has barebones info, so linked to here from http://www.psdevwiki.com/ps4/Talk:DualShock_4

A dev with access to USB passthrough has got rumble and LEDs working ‐ http://www.youtube.com/watch?v=RkbjUj5nN6g but afaik wont be sharing the protocol :<

Have been messing locally with sending packets to the controller to trigger rumble, but it doesn’t appear to use anything like PS3s rumble/LED packet.

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.