Skip to content

Commit 526f8a4

Browse files
authored
Merge pull request #41 from nicolas0p/dualshock_3
Implement support for Sony DualShock 3
2 parents 0e9b5ce + 4b2fdf8 commit 526f8a4

File tree

3 files changed

+94
-1
lines changed

3 files changed

+94
-1
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Steam Deck game controller plugin for [Decky Loader](https://github.com/SteamDec
55

66
## Supported Controllers
77
* PlayStation DualSense
8+
* Playstation DualShock 3
89
* Playstation DualShock 4
910
* Nintendo Switch Pro Controller
1011
* Xbox Series X|S Controller

backend/src/api.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,20 @@ pub fn controllers() -> Result<Vec<Controller>> {
100100
}
101101
}
102102

103-
for device_info in hidapi.device_list() {
103+
let mut unique_devices: Vec<_> = hidapi.device_list().collect();
104+
unique_devices.dedup_by(|a, b| a.serial_number() == b.serial_number());
105+
for device_info in unique_devices {
104106
match (device_info.vendor_id(), device_info.product_id()) {
107+
(playstation::DS_VENDOR_ID, playstation::DS3_PRODUCT_ID) => {
108+
debug!("Found DualShock3 controller: {:?}", device_info);
109+
let controller = playstation::parse_dualshock3_controller_data(
110+
device_info,
111+
&hidapi,
112+
"DualShock3",
113+
)?;
114+
115+
controllers.push(controller);
116+
}
105117
(playstation::DS_VENDOR_ID, playstation::DS_PRODUCT_ID) => {
106118
debug!("Found DualSense controller: {:?}", device_info);
107119
let controller = playstation::parse_dualsense_controller_data(

backend/src/api/playstation.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::cmp;
33
use anyhow::Result;
44
use hidapi::{DeviceInfo, HidApi};
55
use log::error;
6+
use log::info;
67
use serde::{Deserialize, Serialize};
78

89
use crate::controller::Status;
@@ -38,6 +39,15 @@ const DS_STATUS_BATTERY_CAPACITY: u8 = 0b1111;
3839
const DS_STATUS_CHARGING: u8 = 0b1111 << 4;
3940
const DS_STATUS_CHARGING_SHIFT: u8 = 4;
4041

42+
// DualShock3
43+
pub const DS3_PRODUCT_ID: u16 = 0x0268;
44+
45+
const DS3_INPUT_REPORT: u8 = 0x01;
46+
const DS3_INPUT_REPORT_SIZE: usize = 49;
47+
const DS3_INPUT_REPORT_BATTERY_OFFSET: usize = 30;
48+
const DS3_INPUT_REPORT_BATTERY_CHARGING: u8 = 0xee;
49+
const DS3_INPUT_REPORT_CHARGING_BIT: u8 = 0x01;
50+
4151
#[repr(C, packed)]
4252
#[derive(Copy, Clone, Debug, Deserialize)]
4353
struct DualSenseTouchPoint {
@@ -245,6 +255,76 @@ fn get_battery_status(charging_status: u8, battery_data: u8) -> BatteryInfo {
245255
}
246256
}
247257

258+
pub fn parse_dualshock3_controller_data(
259+
device_info: &DeviceInfo,
260+
hidapi: &HidApi,
261+
name: &str,
262+
) -> Result<Controller> {
263+
let mut controller = Controller::from_hidapi(device_info, name, 0, Status::Unknown);
264+
let device = device_info.open_device(hidapi)?;
265+
266+
// Read data from device_info
267+
// If the DualShock 3 controller is not "activated", if its LEDs are blinking, it will not
268+
// respond to reads, so we will timeout after 5s
269+
let mut buf = [0u8; DS3_INPUT_REPORT_SIZE];
270+
let res = device.read_timeout(&mut buf[..], 2000)?;
271+
272+
if res == 0 {
273+
info!("Inactive DualShock 3 controller");
274+
return Ok(controller);
275+
}
276+
277+
if buf[1] == 0xff {
278+
/* Comment coppied from the linux driver at drivers/hid/hid-sony.c
279+
* When connected via Bluetooth the Sixaxis occasionally sends
280+
* a report with the second byte 0xff and the rest zeroed.
281+
*
282+
* This report does not reflect the actual state of the
283+
* controller must be ignored to avoid generating false input
284+
* events.
285+
*/
286+
return Ok(controller);
287+
}
288+
289+
let battery_data: u8;
290+
if buf[0] == DS3_INPUT_REPORT && res == DS3_INPUT_REPORT_SIZE
291+
{
292+
battery_data = buf[DS3_INPUT_REPORT_BATTERY_OFFSET];
293+
} else {
294+
error!("Unhandled report ID: {}", buf[0]);
295+
return Ok(controller);
296+
}
297+
298+
let battery_status = get_ds3_battery_status(battery_data);
299+
controller.capacity = battery_status.capacity;
300+
controller.status = battery_status.status;
301+
302+
Ok(controller)
303+
}
304+
305+
fn get_ds3_battery_status(battery_data: u8) -> BatteryInfo {
306+
/*
307+
* This code was based on the linux driver for this controller.
308+
* sixaxis_parse_report() from drivers/hid/hid-sony.c
309+
*/
310+
311+
let mut battery_info = BatteryInfo{capacity:75, status:Status::Unknown};
312+
if battery_data >= DS3_INPUT_REPORT_BATTERY_CHARGING {
313+
//if the controller is charging, it does not report exact battery capacity
314+
battery_info.status = match battery_data & DS3_INPUT_REPORT_CHARGING_BIT {
315+
0 => Status::Charging,
316+
_ => {battery_info.capacity = 100; Status::Unknown},
317+
};
318+
} else {
319+
let index: usize = if battery_data <= 5 {battery_data.into()} else {5};
320+
let dualshock3_battery_capacity_values = [0, 1, 25, 50, 75, 100];
321+
battery_info.capacity = dualshock3_battery_capacity_values[index];
322+
battery_info.status = Status::Discharging;
323+
}
324+
325+
battery_info
326+
}
327+
248328
#[cfg(test)]
249329
mod tests {
250330
use crate::api::playstation::{DualSenseInputReport, DS_INPUT_REPORT_USB_SIZE};

0 commit comments

Comments
 (0)