Skip to content

Commit 4e463ec

Browse files
Roderick ColenbranderJiri Kosina
authored andcommitted
HID: playstation: Add DualShock4 rumble support.
This patch implements DualShock4 rumble support in a similar manner as the DualSense implementation. It adds an output worker with granular control of different features of the main DualShock4 output report. Signed-off-by: Roderick Colenbrander <[email protected]> Signed-off-by: Jiri Kosina <[email protected]>
1 parent 12882ed commit 4e463ec

File tree

1 file changed

+147
-1
lines changed

1 file changed

+147
-1
lines changed

drivers/hid/hid-playstation.c

Lines changed: 147 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,8 @@ struct dualsense_output_report {
285285

286286
#define DS4_INPUT_REPORT_USB 0x01
287287
#define DS4_INPUT_REPORT_USB_SIZE 64
288+
#define DS4_OUTPUT_REPORT_USB 0x05
289+
#define DS4_OUTPUT_REPORT_USB_SIZE 32
288290

289291
#define DS4_FEATURE_REPORT_CALIBRATION 0x02
290292
#define DS4_FEATURE_REPORT_CALIBRATION_SIZE 37
@@ -306,6 +308,9 @@ struct dualsense_output_report {
306308
/* Battery status within batery_status field. */
307309
#define DS4_BATTERY_STATUS_FULL 11
308310

311+
/* Flags for DualShock4 output report. */
312+
#define DS4_OUTPUT_VALID_FLAG0_MOTOR 0x01
313+
309314
/* DualShock4 hardware limits */
310315
#define DS4_ACC_RES_PER_G 8192
311316
#define DS4_ACC_RANGE (4*DS_ACC_RES_PER_G)
@@ -328,6 +333,14 @@ struct dualshock4 {
328333
bool sensor_timestamp_initialized;
329334
uint32_t prev_sensor_timestamp;
330335
uint32_t sensor_timestamp_us;
336+
337+
bool update_rumble;
338+
uint8_t motor_left;
339+
uint8_t motor_right;
340+
341+
struct work_struct output_worker;
342+
bool output_worker_initialized;
343+
void *output_report_dmabuf;
331344
};
332345

333346
struct dualshock4_touch_point {
@@ -372,6 +385,45 @@ struct dualshock4_input_report_usb {
372385
} __packed;
373386
static_assert(sizeof(struct dualshock4_input_report_usb) == DS4_INPUT_REPORT_USB_SIZE);
374387

388+
/* Common data between Bluetooth and USB DualShock4 output reports. */
389+
struct dualshock4_output_report_common {
390+
uint8_t valid_flag0;
391+
uint8_t valid_flag1;
392+
393+
uint8_t reserved;
394+
395+
uint8_t motor_right;
396+
uint8_t motor_left;
397+
398+
uint8_t lightbar_red;
399+
uint8_t lightbar_green;
400+
uint8_t lightbar_blue;
401+
uint8_t lightbar_blink_on;
402+
uint8_t lightbar_blink_off;
403+
} __packed;
404+
405+
struct dualshock4_output_report_usb {
406+
uint8_t report_id; /* 0x5 */
407+
struct dualshock4_output_report_common common;
408+
uint8_t reserved[21];
409+
} __packed;
410+
static_assert(sizeof(struct dualshock4_output_report_usb) == DS4_OUTPUT_REPORT_USB_SIZE);
411+
412+
/*
413+
* The DualShock4 has a main output report used to control most features. It is
414+
* largely the same between Bluetooth and USB except for different headers and CRC.
415+
* This structure hide the differences between the two to simplify sending output reports.
416+
*/
417+
struct dualshock4_output_report {
418+
uint8_t *data; /* Start of data */
419+
uint8_t len; /* Size of output report */
420+
421+
/* Points to USB data payload in case for a USB report else NULL. */
422+
struct dualshock4_output_report_usb *usb;
423+
/* Points to common section of report, so past any headers. */
424+
struct dualshock4_output_report_common *common;
425+
};
426+
375427
/*
376428
* Common gamepad buttons across DualShock 3 / 4 and DualSense.
377429
* Note: for device with a touchpad, touchpad button is not included
@@ -399,6 +451,7 @@ static const struct {int x; int y; } ps_gamepad_hat_mapping[] = {
399451
};
400452

401453
static inline void dualsense_schedule_work(struct dualsense *ds);
454+
static inline void dualshock4_schedule_work(struct dualshock4 *ds4);
402455
static void dualsense_set_lightbar(struct dualsense *ds, uint8_t red, uint8_t green, uint8_t blue);
403456

404457
/*
@@ -1692,6 +1745,49 @@ static int dualshock4_get_mac_address(struct dualshock4 *ds4)
16921745
return ret;
16931746
}
16941747

1748+
static void dualshock4_init_output_report(struct dualshock4 *ds4,
1749+
struct dualshock4_output_report *rp, void *buf)
1750+
{
1751+
struct hid_device *hdev = ds4->base.hdev;
1752+
1753+
if (hdev->bus == BUS_USB) {
1754+
struct dualshock4_output_report_usb *usb = buf;
1755+
1756+
memset(usb, 0, sizeof(*usb));
1757+
usb->report_id = DS4_OUTPUT_REPORT_USB;
1758+
1759+
rp->data = buf;
1760+
rp->len = sizeof(*usb);
1761+
rp->usb = usb;
1762+
rp->common = &usb->common;
1763+
}
1764+
}
1765+
1766+
static void dualshock4_output_worker(struct work_struct *work)
1767+
{
1768+
struct dualshock4 *ds4 = container_of(work, struct dualshock4, output_worker);
1769+
struct dualshock4_output_report report;
1770+
struct dualshock4_output_report_common *common;
1771+
unsigned long flags;
1772+
1773+
dualshock4_init_output_report(ds4, &report, ds4->output_report_dmabuf);
1774+
common = report.common;
1775+
1776+
spin_lock_irqsave(&ds4->base.lock, flags);
1777+
1778+
if (ds4->update_rumble) {
1779+
/* Select classic rumble style haptics and enable it. */
1780+
common->valid_flag0 |= DS4_OUTPUT_VALID_FLAG0_MOTOR;
1781+
common->motor_left = ds4->motor_left;
1782+
common->motor_right = ds4->motor_right;
1783+
ds4->update_rumble = false;
1784+
}
1785+
1786+
spin_unlock_irqrestore(&ds4->base.lock, flags);
1787+
1788+
hid_hw_output_report(ds4->base.hdev, report.data, report.len);
1789+
}
1790+
16951791
static int dualshock4_parse_report(struct ps_device *ps_dev, struct hid_report *report,
16961792
u8 *data, int size)
16971793
{
@@ -1860,10 +1956,52 @@ static int dualshock4_parse_report(struct ps_device *ps_dev, struct hid_report *
18601956
return 0;
18611957
}
18621958

1959+
static int dualshock4_play_effect(struct input_dev *dev, void *data, struct ff_effect *effect)
1960+
{
1961+
struct hid_device *hdev = input_get_drvdata(dev);
1962+
struct dualshock4 *ds4 = hid_get_drvdata(hdev);
1963+
unsigned long flags;
1964+
1965+
if (effect->type != FF_RUMBLE)
1966+
return 0;
1967+
1968+
spin_lock_irqsave(&ds4->base.lock, flags);
1969+
ds4->update_rumble = true;
1970+
ds4->motor_left = effect->u.rumble.strong_magnitude / 256;
1971+
ds4->motor_right = effect->u.rumble.weak_magnitude / 256;
1972+
spin_unlock_irqrestore(&ds4->base.lock, flags);
1973+
1974+
dualshock4_schedule_work(ds4);
1975+
return 0;
1976+
}
1977+
1978+
static void dualshock4_remove(struct ps_device *ps_dev)
1979+
{
1980+
struct dualshock4 *ds4 = container_of(ps_dev, struct dualshock4, base);
1981+
unsigned long flags;
1982+
1983+
spin_lock_irqsave(&ds4->base.lock, flags);
1984+
ds4->output_worker_initialized = false;
1985+
spin_unlock_irqrestore(&ds4->base.lock, flags);
1986+
1987+
cancel_work_sync(&ds4->output_worker);
1988+
}
1989+
1990+
static inline void dualshock4_schedule_work(struct dualshock4 *ds4)
1991+
{
1992+
unsigned long flags;
1993+
1994+
spin_lock_irqsave(&ds4->base.lock, flags);
1995+
if (ds4->output_worker_initialized)
1996+
schedule_work(&ds4->output_worker);
1997+
spin_unlock_irqrestore(&ds4->base.lock, flags);
1998+
}
1999+
18632000
static struct ps_device *dualshock4_create(struct hid_device *hdev)
18642001
{
18652002
struct dualshock4 *ds4;
18662003
struct ps_device *ps_dev;
2004+
uint8_t max_output_report_size;
18672005
int ret;
18682006

18692007
ds4 = devm_kzalloc(&hdev->dev, sizeof(*ds4), GFP_KERNEL);
@@ -1882,8 +2020,16 @@ static struct ps_device *dualshock4_create(struct hid_device *hdev)
18822020
ps_dev->battery_capacity = 100; /* initial value until parse_report. */
18832021
ps_dev->battery_status = POWER_SUPPLY_STATUS_UNKNOWN;
18842022
ps_dev->parse_report = dualshock4_parse_report;
2023+
ps_dev->remove = dualshock4_remove;
2024+
INIT_WORK(&ds4->output_worker, dualshock4_output_worker);
2025+
ds4->output_worker_initialized = true;
18852026
hid_set_drvdata(hdev, ds4);
18862027

2028+
max_output_report_size = sizeof(struct dualshock4_output_report_usb);
2029+
ds4->output_report_dmabuf = devm_kzalloc(&hdev->dev, max_output_report_size, GFP_KERNEL);
2030+
if (!ds4->output_report_dmabuf)
2031+
return ERR_PTR(-ENOMEM);
2032+
18872033
ret = dualshock4_get_mac_address(ds4);
18882034
if (ret) {
18892035
hid_err(hdev, "Failed to get MAC address from DualShock4\n");
@@ -1907,7 +2053,7 @@ static struct ps_device *dualshock4_create(struct hid_device *hdev)
19072053
goto err;
19082054
}
19092055

1910-
ds4->gamepad = ps_gamepad_create(hdev, NULL);
2056+
ds4->gamepad = ps_gamepad_create(hdev, dualshock4_play_effect);
19112057
if (IS_ERR(ds4->gamepad)) {
19122058
ret = PTR_ERR(ds4->gamepad);
19132059
goto err;

0 commit comments

Comments
 (0)