Skip to content

Commit 9b2b5c9

Browse files
Frank PraznikJiri Kosina
authored andcommitted
HID: sony: Use kernel allocated buffers for HID reports
Replace stack buffers with kernel allocated buffers for sending and receiving HID reports to prevent issues with DMA transfers on certain hardware. Output report buffers are allocated at initialization time to avoid excessive calls to kmalloc and kfree. Link: https://bugzilla.kernel.org/show_bug.cgi?id=87991 Signed-off-by: Frank Praznik <[email protected]> Reviewed-by: Dmitry Torokhov <[email protected]> Signed-off-by: Jiri Kosina <[email protected]>
1 parent 6b07974 commit 9b2b5c9

File tree

1 file changed

+113
-34
lines changed

1 file changed

+113
-34
lines changed

drivers/hid/hid-sony.c

Lines changed: 113 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -798,6 +798,12 @@ union sixaxis_output_report_01 {
798798
__u8 buf[36];
799799
};
800800

801+
#define DS4_REPORT_0x02_SIZE 37
802+
#define DS4_REPORT_0x05_SIZE 32
803+
#define DS4_REPORT_0x11_SIZE 78
804+
#define DS4_REPORT_0x81_SIZE 7
805+
#define SIXAXIS_REPORT_0xF2_SIZE 18
806+
801807
static spinlock_t sony_dev_list_lock;
802808
static LIST_HEAD(sony_device_list);
803809
static DEFINE_IDA(sony_device_id_allocator);
@@ -811,6 +817,7 @@ struct sony_sc {
811817
struct work_struct state_worker;
812818
struct power_supply battery;
813819
int device_id;
820+
__u8 *output_report_dmabuf;
814821

815822
#ifdef CONFIG_SONY_FF
816823
__u8 left;
@@ -1142,9 +1149,20 @@ static int sixaxis_set_operational_usb(struct hid_device *hdev)
11421149

11431150
static int sixaxis_set_operational_bt(struct hid_device *hdev)
11441151
{
1145-
unsigned char buf[] = { 0xf4, 0x42, 0x03, 0x00, 0x00 };
1146-
return hid_hw_raw_request(hdev, buf[0], buf, sizeof(buf),
1152+
static const __u8 report[] = { 0xf4, 0x42, 0x03, 0x00, 0x00 };
1153+
__u8 *buf;
1154+
int ret;
1155+
1156+
buf = kmemdup(report, sizeof(report), GFP_KERNEL);
1157+
if (!buf)
1158+
return -ENOMEM;
1159+
1160+
ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(report),
11471161
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
1162+
1163+
kfree(buf);
1164+
1165+
return ret;
11481166
}
11491167

11501168
/*
@@ -1153,10 +1171,19 @@ static int sixaxis_set_operational_bt(struct hid_device *hdev)
11531171
*/
11541172
static int dualshock4_set_operational_bt(struct hid_device *hdev)
11551173
{
1156-
__u8 buf[37] = { 0 };
1174+
__u8 *buf;
1175+
int ret;
11571176

1158-
return hid_hw_raw_request(hdev, 0x02, buf, sizeof(buf),
1177+
buf = kmalloc(DS4_REPORT_0x02_SIZE, GFP_KERNEL);
1178+
if (!buf)
1179+
return -ENOMEM;
1180+
1181+
ret = hid_hw_raw_request(hdev, 0x02, buf, DS4_REPORT_0x02_SIZE,
11591182
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
1183+
1184+
kfree(buf);
1185+
1186+
return ret;
11601187
}
11611188

11621189
static void sixaxis_set_leds_from_id(int id, __u8 values[MAX_LEDS])
@@ -1471,9 +1498,7 @@ static int sony_leds_init(struct sony_sc *sc)
14711498

14721499
static void sixaxis_state_worker(struct work_struct *work)
14731500
{
1474-
struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
1475-
int n;
1476-
union sixaxis_output_report_01 report = {
1501+
static const union sixaxis_output_report_01 default_report = {
14771502
.buf = {
14781503
0x01,
14791504
0x00, 0xff, 0x00, 0xff, 0x00,
@@ -1485,20 +1510,27 @@ static void sixaxis_state_worker(struct work_struct *work)
14851510
0x00, 0x00, 0x00, 0x00, 0x00
14861511
}
14871512
};
1513+
struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
1514+
struct sixaxis_output_report *report =
1515+
(struct sixaxis_output_report *)sc->output_report_dmabuf;
1516+
int n;
1517+
1518+
/* Initialize the report with default values */
1519+
memcpy(report, &default_report, sizeof(struct sixaxis_output_report));
14881520

14891521
#ifdef CONFIG_SONY_FF
1490-
report.data.rumble.right_motor_on = sc->right ? 1 : 0;
1491-
report.data.rumble.left_motor_force = sc->left;
1522+
report->rumble.right_motor_on = sc->right ? 1 : 0;
1523+
report->rumble.left_motor_force = sc->left;
14921524
#endif
14931525

1494-
report.data.leds_bitmap |= sc->led_state[0] << 1;
1495-
report.data.leds_bitmap |= sc->led_state[1] << 2;
1496-
report.data.leds_bitmap |= sc->led_state[2] << 3;
1497-
report.data.leds_bitmap |= sc->led_state[3] << 4;
1526+
report->leds_bitmap |= sc->led_state[0] << 1;
1527+
report->leds_bitmap |= sc->led_state[1] << 2;
1528+
report->leds_bitmap |= sc->led_state[2] << 3;
1529+
report->leds_bitmap |= sc->led_state[3] << 4;
14981530

14991531
/* Set flag for all leds off, required for 3rd party INTEC controller */
1500-
if ((report.data.leds_bitmap & 0x1E) == 0)
1501-
report.data.leds_bitmap |= 0x20;
1532+
if ((report->leds_bitmap & 0x1E) == 0)
1533+
report->leds_bitmap |= 0x20;
15021534

15031535
/*
15041536
* The LEDs in the report are indexed in reverse order to their
@@ -1511,28 +1543,30 @@ static void sixaxis_state_worker(struct work_struct *work)
15111543
*/
15121544
for (n = 0; n < 4; n++) {
15131545
if (sc->led_delay_on[n] || sc->led_delay_off[n]) {
1514-
report.data.led[3 - n].duty_off = sc->led_delay_off[n];
1515-
report.data.led[3 - n].duty_on = sc->led_delay_on[n];
1546+
report->led[3 - n].duty_off = sc->led_delay_off[n];
1547+
report->led[3 - n].duty_on = sc->led_delay_on[n];
15161548
}
15171549
}
15181550

1519-
hid_hw_raw_request(sc->hdev, report.data.report_id, report.buf,
1520-
sizeof(report), HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
1551+
hid_hw_raw_request(sc->hdev, report->report_id, (__u8 *)report,
1552+
sizeof(struct sixaxis_output_report),
1553+
HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
15211554
}
15221555

15231556
static void dualshock4_state_worker(struct work_struct *work)
15241557
{
15251558
struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
15261559
struct hid_device *hdev = sc->hdev;
1560+
__u8 *buf = sc->output_report_dmabuf;
15271561
int offset;
15281562

1529-
__u8 buf[78] = { 0 };
1530-
15311563
if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
1564+
memset(buf, 0, DS4_REPORT_0x05_SIZE);
15321565
buf[0] = 0x05;
15331566
buf[1] = 0xFF;
15341567
offset = 4;
15351568
} else {
1569+
memset(buf, 0, DS4_REPORT_0x11_SIZE);
15361570
buf[0] = 0x11;
15371571
buf[1] = 0xB0;
15381572
buf[3] = 0x0F;
@@ -1560,12 +1594,33 @@ static void dualshock4_state_worker(struct work_struct *work)
15601594
buf[offset++] = sc->led_delay_off[3];
15611595

15621596
if (sc->quirks & DUALSHOCK4_CONTROLLER_USB)
1563-
hid_hw_output_report(hdev, buf, 32);
1597+
hid_hw_output_report(hdev, buf, DS4_REPORT_0x05_SIZE);
15641598
else
1565-
hid_hw_raw_request(hdev, 0x11, buf, 78,
1599+
hid_hw_raw_request(hdev, 0x11, buf, DS4_REPORT_0x11_SIZE,
15661600
HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
15671601
}
15681602

1603+
static int sony_allocate_output_report(struct sony_sc *sc)
1604+
{
1605+
if (sc->quirks & SIXAXIS_CONTROLLER)
1606+
sc->output_report_dmabuf =
1607+
kmalloc(sizeof(union sixaxis_output_report_01),
1608+
GFP_KERNEL);
1609+
else if (sc->quirks & DUALSHOCK4_CONTROLLER_BT)
1610+
sc->output_report_dmabuf = kmalloc(DS4_REPORT_0x11_SIZE,
1611+
GFP_KERNEL);
1612+
else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB)
1613+
sc->output_report_dmabuf = kmalloc(DS4_REPORT_0x05_SIZE,
1614+
GFP_KERNEL);
1615+
else
1616+
return 0;
1617+
1618+
if (!sc->output_report_dmabuf)
1619+
return -ENOMEM;
1620+
1621+
return 0;
1622+
}
1623+
15691624
#ifdef CONFIG_SONY_FF
15701625
static int sony_play_effect(struct input_dev *dev, void *data,
15711626
struct ff_effect *effect)
@@ -1754,6 +1809,7 @@ static int sony_get_bt_devaddr(struct sony_sc *sc)
17541809

17551810
static int sony_check_add(struct sony_sc *sc)
17561811
{
1812+
__u8 *buf = NULL;
17571813
int n, ret;
17581814

17591815
if ((sc->quirks & DUALSHOCK4_CONTROLLER_BT) ||
@@ -1769,36 +1825,44 @@ static int sony_check_add(struct sony_sc *sc)
17691825
return 0;
17701826
}
17711827
} else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
1772-
__u8 buf[7];
1828+
buf = kmalloc(DS4_REPORT_0x81_SIZE, GFP_KERNEL);
1829+
if (!buf)
1830+
return -ENOMEM;
17731831

17741832
/*
17751833
* The MAC address of a DS4 controller connected via USB can be
17761834
* retrieved with feature report 0x81. The address begins at
17771835
* offset 1.
17781836
*/
1779-
ret = hid_hw_raw_request(sc->hdev, 0x81, buf, sizeof(buf),
1780-
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
1837+
ret = hid_hw_raw_request(sc->hdev, 0x81, buf,
1838+
DS4_REPORT_0x81_SIZE, HID_FEATURE_REPORT,
1839+
HID_REQ_GET_REPORT);
17811840

1782-
if (ret != 7) {
1841+
if (ret != DS4_REPORT_0x81_SIZE) {
17831842
hid_err(sc->hdev, "failed to retrieve feature report 0x81 with the DualShock 4 MAC address\n");
1784-
return ret < 0 ? ret : -EINVAL;
1843+
ret = ret < 0 ? ret : -EINVAL;
1844+
goto out_free;
17851845
}
17861846

17871847
memcpy(sc->mac_address, &buf[1], sizeof(sc->mac_address));
17881848
} else if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
1789-
__u8 buf[18];
1849+
buf = kmalloc(SIXAXIS_REPORT_0xF2_SIZE, GFP_KERNEL);
1850+
if (!buf)
1851+
return -ENOMEM;
17901852

17911853
/*
17921854
* The MAC address of a Sixaxis controller connected via USB can
17931855
* be retrieved with feature report 0xf2. The address begins at
17941856
* offset 4.
17951857
*/
1796-
ret = hid_hw_raw_request(sc->hdev, 0xf2, buf, sizeof(buf),
1797-
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
1858+
ret = hid_hw_raw_request(sc->hdev, 0xf2, buf,
1859+
SIXAXIS_REPORT_0xF2_SIZE, HID_FEATURE_REPORT,
1860+
HID_REQ_GET_REPORT);
17981861

1799-
if (ret != 18) {
1862+
if (ret != SIXAXIS_REPORT_0xF2_SIZE) {
18001863
hid_err(sc->hdev, "failed to retrieve feature report 0xf2 with the Sixaxis MAC address\n");
1801-
return ret < 0 ? ret : -EINVAL;
1864+
ret = ret < 0 ? ret : -EINVAL;
1865+
goto out_free;
18021866
}
18031867

18041868
/*
@@ -1811,7 +1875,13 @@ static int sony_check_add(struct sony_sc *sc)
18111875
return 0;
18121876
}
18131877

1814-
return sony_check_add_dev_list(sc);
1878+
ret = sony_check_add_dev_list(sc);
1879+
1880+
out_free:
1881+
1882+
kfree(buf);
1883+
1884+
return ret;
18151885
}
18161886

18171887
static int sony_set_device_id(struct sony_sc *sc)
@@ -1895,6 +1965,12 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
18951965
return ret;
18961966
}
18971967

1968+
ret = sony_allocate_output_report(sc);
1969+
if (ret < 0) {
1970+
hid_err(hdev, "failed to allocate the output report buffer\n");
1971+
goto err_stop;
1972+
}
1973+
18981974
ret = sony_set_device_id(sc);
18991975
if (ret < 0) {
19001976
hid_err(hdev, "failed to allocate the device id\n");
@@ -1984,6 +2060,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
19842060
if (sc->quirks & SONY_BATTERY_SUPPORT)
19852061
sony_battery_remove(sc);
19862062
sony_cancel_work_sync(sc);
2063+
kfree(sc->output_report_dmabuf);
19872064
sony_remove_dev_list(sc);
19882065
sony_release_device_id(sc);
19892066
hid_hw_stop(hdev);
@@ -2004,6 +2081,8 @@ static void sony_remove(struct hid_device *hdev)
20042081

20052082
sony_cancel_work_sync(sc);
20062083

2084+
kfree(sc->output_report_dmabuf);
2085+
20072086
sony_remove_dev_list(sc);
20082087

20092088
sony_release_device_id(sc);

0 commit comments

Comments
 (0)