DualShock4 Reverse Engineering – Half 1
This 12 months I received a really particular reward for Christmas: a damaged DualShock 4.
Chances are you’ll say it’s a comparatively poor reward, however the one who gave me this is aware of
me very nicely. This reward stored me fascinated by it for multiple week, in
each second I had spare.
With respect to different DS4 I repaired, this one has been to date probably the most
troublesome one. Others had simply an analog management drifting, a damaged button or
one thing else extraordinarily straightforward to repair. Often it takes one hour roughly to
discover the problem and repair it. However not for this one.
The DualShock 4
The DualShock 4 is the official controller of the PlayStation 4.
There are three circuit boards inside a DS4: a main-board, a secondary-board and
the touchpad.
- The primary-board is accountable for 99% of the duties, it’s linked to the buttons, touchpad, motors, battery and the secondary-board. It handles audio, battery stats, sensors, motors, and so on. It has a
JDM-xxx
label written on it. - The secondary-board is a small board accountable for USB connection and it has the RGB LED on high of it. It’s linked to the main-board through a flat cable and is recognized by the label
JDS-xxx
. - The touchpad has its personal plastic case and is linked on to the main-board utilizing a flat cable.
I choose to not spend too many phrases in describing the {hardware}, you could find
tons and tons of information on-line, here’s a assortment of hyperlinks to start out with:
Talking of reverse engineering, lots of work has been already carried out. On the
second you’ll be able to pair a DS4 with a pc and it really works amazingly. There
are open-source drivers that do nearly all the pieces for you: I/O, leds, audio.
In these weblog posts I’m going to reverse engineer different components of the DS4 that
aren’t of common curiosity however perhaps fascinating for these of you
in some hidden functionalities of it.
Anyway, let’s return on my christmas reward!
The reward
The controller I received is an official DualShock4 V2 CUH-ZCT2E, with a JDM-055
motherboard, newest model AFAIK. The reward was purchased from FB Market,
the outline mentioned it was working solely through USB and never Bluetooth, and the
points reported by the earlier proprietor (I didn’t speak to him personally) have been:
- Damaged led, actually faraway from the secondary-board
- Drifting analog controls
- Battery not working
- Bluetooth not working
Some a part of the plastic cowl is damaged, so I think it fell down so onerous
that the led popped off from the JDS-055 board. After few days of investigation
I found different points, however we’re going to talk about them in Half 2 .
Anyway, listed here are some footage of the motherboard:
JDM-055 Entrance | JDM-055 Again |
---|---|
Authentic or clone?
Prior to now I needed to take care of some DS4 clones. It’s straightforward to acknowledge if a DS4
is authentic or a clone after having it disassembled: the clones often have a
utterly completely different motherboard than the unique, not less than those I received so
far.
So, when you’ve got a DS4-V2 and also you learn JDM-055
on the motherboard, JDS-055
on the secondary-board, often it’s an authentic one.
If you wish to go deeper, you’ll be able to google which microcontroller needs to be there
and verify if all the pieces matches. For instance, on this case the microcontroller
is a Mediatek MT3610N
, which is current.
Anyway, I found a neater strategy to discover out if the DS4 is authentic or not,
which doesn’t require a disassembly, and naturally, may give you false
positives:
Join it through USB to a Linux laptop, look into dmesg
(kernel log messages)
and see if there’s an error concerning HID Report 0x81
proper after connecting
the DS4 through USB. If the error is there, the controller is a clone.
Pefect then, let’s join the USB and…
[...] usb 1-2: new full-speed USB machine quantity 3 utilizing xhci_hcd
[...] usb 1-2: New USB machine discovered, idVendor=054c, idProduct=09cc, bcdDevice= 1.00
[...] usb 1-2: New USB machine strings: Mfr=1, Product=2, SerialNumber=0
[...] usb 1-2: Product: Wi-fi Controller
[...] usb 1-2: Producer: Sony Interactive Leisure
[...] enter: Sony Interactive Leisure Wi-fi Controller Touchpad as [...]
[...] enter: Sony Interactive Leisure Wi-fi Controller Movement Sensors as [...]
Appears working and doesn’t report any error about HID Report 0x81
, most likely
it’s not a clone!
After which, out of the blue.. Caps Lock is blinking and the system is completely frozen .
[...] divide error: 0000 [#1] PREEMPT SMP PTI
[...] CPU: 3 PID: 0 Comm: swapper/3 Tainted: G[....]
[...] RIP: 0010:dualshock4_parse_report.constprop.0+0xe8/0x5f0 [hid_sony]
Wait.. what?
Even when the dmesg
doesn’t explicitly say that, the Caps Lock blinking
and the system frozen means just one factor: Kernel Panic.
I strive once more few occasions and at all times get the identical consequence.
For some motive, this DS4 causes a kernel panic reliably brought on by the
hid-sony
driver.
What’s occurring?
After a little bit of investigation, I found what’s the explanation for the division by
zero.
A DualShock4 has an IMU
chip, which accommodates an Accelerometer and a Gyroscope. This IMU has a calibration,
which tells to the DS4 consumer what’s the vary and bias of every sensor.
The hid-sony
driver requests to the DS4 the calibration knowledge of this IMU, so
that it could present very nicely calibrated sensors to each user-space app.
The DS4 sends again these calibration knowledge to the motive force:
- Gyroscope Pitch/Uncooked/Roll bias: 3 values, one per axis.
- Gyroscope Pitch/Uncooked/Roll vary (minimal and most): 6 values, 2 per axis.
- Accelerometer X/Y/Z vary (minimal and most): 6 values, 2 per axis.
For the report, all of those calibration values are unsigned int16
.
The hid-sony
driver processes and shops them within the array ds4_calib_data
,
the place every aspect kind is struct ds4_calibration_data
.
/* Used for calibration of DS4 accelerometer and gyro. */
struct ds4_calibration_data {
int abs_code;
brief bias;
int sens_numer;
int sens_denom; /* Right here be dragons */
};
struct sony_sc { /* The context */
/* [...] */
struct ds4_calibration_data ds4_calib_data[6];
/* [...] */
};
ds4_calib_data
is later used within the dualshock4_parse_report()
operate,
which is named each few milliseconds, reads the dwell sensor values, processes
them and sends them to the user-space apps.
The fascinating code is reported right here:
static void dualshock4_parse_report(struct sony_sc *sc, u8 *rd, int dimension) {
/* [...] */
for (n = 0; n < 6; n++) rd[offset]);
struct ds4_calibration_data *calib = &sc->ds4_calib_data[n];
/* CRASH HERE */
int calib_data = mult_frac(calib->sens_numer,
raw_data - calib->bias,
calib->sens_denom);
input_report_abs(sc->sensor_dev, calib->abs_code, calib_data);
offset += 2;
input_sync(sc->sensor_dev);
/* [...] */
}
As reported within the remark within the code above, the division by zero occurs in
the mult_frac()
name.
Okay, let’s dig into the mult_frac()
macro, that is the way it’s applied:
#outline mult_frac(x, numer, denom)(
{
typeof(x) quot = (x) / (denom);
typeof(x) rem = (x) % (denom);
(quot * (numer)) + ((rem * (numer)) / (denom));
}
)
Okay, I see a division there. The denominator is calib->sens_denom
, which
is offered by the DualShock4 and isn’t sanitized wherever.
To sum-up: for every axis, the motive force takes the uncooked worth of the sensor and
applies the calibration by computing a formulation like this one: (not 100% appropriate
however provides you the thought)
$$ y_{out} = ok cdot frac{y_{uncooked} – y_{bias}}{y_{max} – y_{min}} $$
The place $ y_{uncooked} $ is the one worth learn from the sensor. All others are taken
from the calibration when the DS4 is linked.
Now, if $ y_{max} = y_{min} $, the motive force divides the uncooked worth by zero.
This explains the crash.
Debug and repair
Let’s change the motive force in order that it prints out the calibration knowledge. On this method
we are able to see what are the true values offered by the damaged DS4:
[...] gyro_pitch_plus=0 gyro_pitch_minus=0 gyro_yaw_plus=0 gyro_yaw_minus=0
[...] gyro_roll_plus=0 gyro_roll_minus=0 gyro_speed_plus=0 gyro_speed_minus=0
[...] acc_x_plus=0 acc_x_minus=0 acc_y_plus=0
[...] acc_y_minus=0 acc_z_plus=0 acc_z_minus=0
Good, a really nicely calibrated controller!
Which means that, with 99% of confidence, additionally the IMU is damaged on this DS4.
To verify this speculation, we are able to patch the motive force with a workaround: put the
denominator to 1
if it’s zero (to forestall the crash) and add a print within the
dualshock4_parse_report()
to see what are uncooked values ($y_{uncooked}$) acquired
from the sensors.
I think they are going to be all zeros, that is what I count on from a damaged IMU.
What occurs, as a substitute, is that this:
[...] rd0=4 rd1=1 rd2=17 rd3=707 rd4=-8063 rd5=-1536
[...] rd0=4 rd1=0 rd2=17 rd3=698 rd4=-8061 rd5=-1546
[...] rd0=4 rd1=-1 rd2=19 rd3=699 rd4=-8062 rd5=-1544
[...] rd0=3 rd1=0 rd2=19 rd3=695 rd4=-8052 rd5=-1554
[...] rd0=4 rd1=-1 rd2=19 rd3=691 rd4=-8052 rd5=-1547
[...] rd0=4 rd1=0 rd2=18 rd3=709 rd4=-8057 rd5=-1551
Okay I’m confused , the IMU is working very well! The numbers that
you see above are the uncooked values of gyroscope and accelerometer.
Rotating the DS4 additionally causes all values to alter together with my rotation.
Conclusion
Right here’s the top of Half 1. The DS4 {hardware} appears to be an authentic one, but it surely
does have one thing unusual. The IMU calibration is completely damaged, even when the
IMU is working.
Within the JDM-055, the IMU is an exterior chip. I nonetheless don’t know what goes
on however the truth that we are able to obtain uncooked IMU values however not the calibration makes
me suppose that the latter is exterior to the IMU and has been zeroed for some
motive.
I’ll publish quickly Half 2 the place the investigation of this DS4 will go on.
As a preview I can say that additionally the Bluetooth MAC Handle is all zeros,
and this led me to analyze if I can restore them.
Anyway, I wrote a repair for this bug within the linux driver and despatched to the
linux-kernel mailing list (linux-input),
so {that a} DS4 with a damaged calibration doesn’t trigger a kernel panic.
Thanks for studying!
EDIT: Half 2 here
~~~
If you wish to get in contact, drop me an electronic mail at ds4@the.al
or ping me on IRC
(the_al@freenode|libera|hackint
) or Discord (the_al#5510
).