@@ -39,6 +39,7 @@
#include <linux/crc32.h>
#include <linux/usb.h>
#include <linux/timer.h>
+#include <linux/rcupdate.h>
#include <asm/unaligned.h>
#include "hid-ids.h"
@@ -556,7 +557,7 @@ struct sony_sc {
spinlock_t lock;
struct list_head list_node;
struct hid_device *hdev;
- struct input_dev *touchpad;
+ struct input_dev __rcu *touchpad;
struct input_dev *sensor_dev;
struct led_classdev *leds[MAX_LEDS];
unsigned long quirks;
@@ -565,6 +566,7 @@ struct sony_sc {
void (*send_output_report)(struct sony_sc *);
struct power_supply *battery;
struct power_supply_desc battery_desc;
+ struct mutex mutex;
int device_id;
unsigned fw_version;
bool fw_version_created;
@@ -1041,6 +1043,7 @@ static void dualshock4_parse_report(struct sony_sc *sc, u8 *rd, int size)
struct hid_input *hidinput = list_entry(sc->hdev->inputs.next,
struct hid_input, list);
struct input_dev *input_dev = hidinput->input;
+ struct input_dev *touchpad = NULL;
unsigned long flags;
int n, m, offset, num_touch_data, max_touch_data;
u8 cable_state, battery_capacity;
@@ -1050,9 +1053,15 @@ static void dualshock4_parse_report(struct sony_sc *sc, u8 *rd, int size)
/* When using Bluetooth the header is 2 bytes longer, so skip these. */
int data_offset = (sc->quirks & DUALSHOCK4_CONTROLLER_BT) ? 2 : 0;
+ rcu_read_lock();
+ touchpad = rcu_dereference(sc->touchpad);
+ rcu_read_unlock();
+
/* Second bit of third button byte is for the touchpad button. */
- offset = data_offset + DS4_INPUT_REPORT_BUTTON_OFFSET;
- input_report_key(sc->touchpad, BTN_LEFT, rd[offset+2] & 0x2);
+ if (touchpad) {
+ offset = data_offset + DS4_INPUT_REPORT_BUTTON_OFFSET;
+ input_report_key(touchpad, BTN_LEFT, rd[offset+2] & 0x2);
+ }
/*
* The default behavior of the Dualshock 4 is to send reports using
@@ -1197,6 +1206,9 @@ static void dualshock4_parse_report(struct sony_sc *sc, u8 *rd, int size)
sc->battery_status = battery_status;
spin_unlock_irqrestore(&sc->lock, flags);
+ if (!touchpad)
+ return;
+
/*
* The Dualshock 4 multi-touch trackpad data starts at offset 33 on USB
* and 35 on Bluetooth.
@@ -1231,24 +1243,25 @@ static void dualshock4_parse_report(struct sony_sc *sc, u8 *rd, int size)
y = ((rd[offset+2] & 0xF0) >> 4) | (rd[offset+3] << 4);
active = !(rd[offset] >> 7);
- input_mt_slot(sc->touchpad, n);
- input_mt_report_slot_state(sc->touchpad, MT_TOOL_FINGER, active);
+ input_mt_slot(touchpad, n);
+ input_mt_report_slot_state(touchpad, MT_TOOL_FINGER, active);
if (active) {
- input_report_abs(sc->touchpad, ABS_MT_POSITION_X, x);
- input_report_abs(sc->touchpad, ABS_MT_POSITION_Y, y);
+ input_report_abs(touchpad, ABS_MT_POSITION_X, x);
+ input_report_abs(touchpad, ABS_MT_POSITION_Y, y);
}
offset += 4;
}
- input_mt_sync_frame(sc->touchpad);
- input_sync(sc->touchpad);
+ input_mt_sync_frame(touchpad);
+ input_sync(touchpad);
}
}
static void nsg_mrxu_parse_report(struct sony_sc *sc, u8 *rd, int size)
{
int n, offset, relx, rely;
+ struct input_dev *touchpad;
u8 active;
/*
@@ -1271,7 +1284,13 @@ static void nsg_mrxu_parse_report(struct sony_sc *sc, u8 *rd, int size)
*/
offset = 1;
- input_report_key(sc->touchpad, BTN_LEFT, rd[offset] & 0x0F);
+ rcu_read_lock();
+ touchpad = rcu_dereference(sc->touchpad);
+ rcu_read_unlock();
+ if (!touchpad)
+ return;
+
+ input_report_key(touchpad, BTN_LEFT, rd[offset] & 0x0F);
active = (rd[offset] >> 4);
relx = (s8) rd[offset+5];
rely = ((s8) rd[offset+10]) * -1;
@@ -1285,20 +1304,20 @@ static void nsg_mrxu_parse_report(struct sony_sc *sc, u8 *rd, int size)
x = rd[offset] | ((rd[offset+1] & 0x0F) << 8);
y = ((rd[offset+1] & 0xF0) >> 4) | (rd[offset+2] << 4);
- input_mt_slot(sc->touchpad, n);
- input_mt_report_slot_state(sc->touchpad, MT_TOOL_FINGER, active & 0x03);
+ input_mt_slot(touchpad, n);
+ input_mt_report_slot_state(touchpad, MT_TOOL_FINGER, active & 0x03);
if (active & 0x03) {
contactx = rd[offset+3] & 0x0F;
contacty = rd[offset+3] >> 4;
- input_report_abs(sc->touchpad, ABS_MT_TOUCH_MAJOR,
+ input_report_abs(touchpad, ABS_MT_TOUCH_MAJOR,
max(contactx, contacty));
- input_report_abs(sc->touchpad, ABS_MT_TOUCH_MINOR,
+ input_report_abs(touchpad, ABS_MT_TOUCH_MINOR,
min(contactx, contacty));
- input_report_abs(sc->touchpad, ABS_MT_ORIENTATION,
+ input_report_abs(touchpad, ABS_MT_ORIENTATION,
(bool) (contactx > contacty));
- input_report_abs(sc->touchpad, ABS_MT_POSITION_X, x);
- input_report_abs(sc->touchpad, ABS_MT_POSITION_Y,
+ input_report_abs(touchpad, ABS_MT_POSITION_X, x);
+ input_report_abs(touchpad, ABS_MT_POSITION_Y,
NSG_MRXU_MAX_Y - y);
/*
* The relative coordinates belong to the first touch
@@ -1306,8 +1325,8 @@ static void nsg_mrxu_parse_report(struct sony_sc *sc, u8 *rd, int size)
* when the first is not active.
*/
if ((n == 0) || ((n == 1) && (active & 0x01))) {
- input_report_rel(sc->touchpad, REL_X, relx);
- input_report_rel(sc->touchpad, REL_Y, rely);
+ input_report_rel(touchpad, REL_X, relx);
+ input_report_rel(touchpad, REL_Y, rely);
}
}
@@ -1315,9 +1334,9 @@ static void nsg_mrxu_parse_report(struct sony_sc *sc, u8 *rd, int size)
active >>= 2;
}
- input_mt_sync_frame(sc->touchpad);
+ input_mt_sync_frame(touchpad);
- input_sync(sc->touchpad);
+ input_sync(touchpad);
}
static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,
@@ -1496,19 +1515,20 @@ static int sony_register_touchpad(struct sony_sc *sc, int touch_count,
size_t name_sz;
char *name;
int ret;
+ struct input_dev *touchpad;
- sc->touchpad = devm_input_allocate_device(&sc->hdev->dev);
- if (!sc->touchpad)
+ touchpad = devm_input_allocate_device(&sc->hdev->dev);
+ if (!touchpad)
return -ENOMEM;
- input_set_drvdata(sc->touchpad, sc);
- sc->touchpad->dev.parent = &sc->hdev->dev;
- sc->touchpad->phys = sc->hdev->phys;
- sc->touchpad->uniq = sc->hdev->uniq;
- sc->touchpad->id.bustype = sc->hdev->bus;
- sc->touchpad->id.vendor = sc->hdev->vendor;
- sc->touchpad->id.product = sc->hdev->product;
- sc->touchpad->id.version = sc->hdev->version;
+ input_set_drvdata(touchpad, sc);
+ touchpad->dev.parent = &sc->hdev->dev;
+ touchpad->phys = sc->hdev->phys;
+ touchpad->uniq = sc->hdev->uniq;
+ touchpad->id.bustype = sc->hdev->bus;
+ touchpad->id.vendor = sc->hdev->vendor;
+ touchpad->id.product = sc->hdev->product;
+ touchpad->id.version = sc->hdev->version;
/* Append a suffix to the controller name as there are various
* DS4 compatible non-Sony devices with different names.
@@ -1518,39 +1538,41 @@ static int sony_register_touchpad(struct sony_sc *sc, int touch_count,
if (!name)
return -ENOMEM;
snprintf(name, name_sz, "%s" DS4_TOUCHPAD_SUFFIX, sc->hdev->name);
- sc->touchpad->name = name;
+ touchpad->name = name;
/* We map the button underneath the touchpad to BTN_LEFT. */
- __set_bit(EV_KEY, sc->touchpad->evbit);
- __set_bit(BTN_LEFT, sc->touchpad->keybit);
- __set_bit(INPUT_PROP_BUTTONPAD, sc->touchpad->propbit);
+ __set_bit(EV_KEY, touchpad->evbit);
+ __set_bit(BTN_LEFT, touchpad->keybit);
+ __set_bit(INPUT_PROP_BUTTONPAD, touchpad->propbit);
- input_set_abs_params(sc->touchpad, ABS_MT_POSITION_X, 0, w, 0, 0);
- input_set_abs_params(sc->touchpad, ABS_MT_POSITION_Y, 0, h, 0, 0);
+ input_set_abs_params(touchpad, ABS_MT_POSITION_X, 0, w, 0, 0);
+ input_set_abs_params(touchpad, ABS_MT_POSITION_Y, 0, h, 0, 0);
if (touch_major > 0) {
- input_set_abs_params(sc->touchpad, ABS_MT_TOUCH_MAJOR,
+ input_set_abs_params(touchpad, ABS_MT_TOUCH_MAJOR,
0, touch_major, 0, 0);
if (touch_minor > 0)
- input_set_abs_params(sc->touchpad, ABS_MT_TOUCH_MINOR,
+ input_set_abs_params(touchpad, ABS_MT_TOUCH_MINOR,
0, touch_minor, 0, 0);
if (orientation > 0)
- input_set_abs_params(sc->touchpad, ABS_MT_ORIENTATION,
+ input_set_abs_params(touchpad, ABS_MT_ORIENTATION,
0, orientation, 0, 0);
}
if (sc->quirks & NSG_MRXU_REMOTE) {
- __set_bit(EV_REL, sc->touchpad->evbit);
+ __set_bit(EV_REL, touchpad->evbit);
}
- ret = input_mt_init_slots(sc->touchpad, touch_count, INPUT_MT_POINTER);
+ ret = input_mt_init_slots(touchpad, touch_count, INPUT_MT_POINTER);
if (ret < 0)
return ret;
- ret = input_register_device(sc->touchpad);
+ ret = input_register_device(touchpad);
if (ret < 0)
return ret;
+ rcu_assign_pointer(sc->touchpad, touchpad);
+
return 0;
}
@@ -1627,6 +1649,48 @@ static int sony_register_sensors(struct sony_sc *sc)
return 0;
}
+static void sony_unregister_touchpad(struct sony_sc *sc)
+{
+ struct input_dev *touchpad;
+
+ rcu_read_lock();
+ touchpad = rcu_dereference(sc->touchpad);
+ rcu_read_unlock();
+
+ if (!touchpad)
+ return;
+
+ RCU_INIT_POINTER(sc->touchpad, NULL);
+ synchronize_rcu();
+ input_unregister_device(touchpad);
+}
+
+static int sony_register_ds4_touchpad(struct sony_sc *sc)
+{
+ struct input_dev *touchpad;
+ int ret;
+
+ rcu_read_lock();
+ touchpad = rcu_dereference(sc->touchpad);
+ rcu_read_unlock();
+
+ if (touchpad)
+ return 0;
+
+ /*
+ * The Dualshock 4 touchpad supports 2 touches and has a
+ * resolution of 1920x942 (44.86 dots/mm).
+ */
+ ret = sony_register_touchpad(sc, 2, 1920, 942, 0, 0, 0);
+ if (ret) {
+ hid_err(sc->hdev,
+ "Unable to initialize multi-touch slots: %d\n",
+ ret);
+ }
+
+ return ret;
+}
+
/*
* Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller
* to "operational". Without this, the ps3 controller will not report any
@@ -2876,17 +2940,9 @@ static int sony_input_configured(struct hid_device *hdev,
}
sc->hw_version_created = true;
- /*
- * The Dualshock 4 touchpad supports 2 touches and has a
- * resolution of 1920x942 (44.86 dots/mm).
- */
- ret = sony_register_touchpad(sc, 2, 1920, 942, 0, 0, 0);
- if (ret) {
- hid_err(sc->hdev,
- "Unable to initialize multi-touch slots: %d\n",
- ret);
+ ret = sony_register_ds4_touchpad(sc);
+ if (ret)
goto err_stop;
- }
ret = sony_register_sensors(sc);
if (ret) {
@@ -2996,6 +3052,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
}
spin_lock_init(&sc->lock);
+ mutex_init(&sc->mutex);
sc->quirks = quirks;
hid_set_drvdata(hdev, sc);
This allows the touchpad input_dev to be removed and have the driver remain functional without its presence. This will be used to allow the touchpad to be disabled, e.g. by a module parameter. Signed-off-by: Vicki Pfau <vi@endrift.com> --- drivers/hid/hid-sony.c | 163 +++++++++++++++++++++++++++-------------- 1 file changed, 110 insertions(+), 53 deletions(-)