aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoderick Colenbrander <roderick.colenbrander@sony.com>2016-10-07 15:39:34 -0400
committerJiri Kosina <jkosina@suse.cz>2016-10-10 04:43:24 -0400
commite1bc84d0071f59c8b38232e2cb093c47c47e4f9f (patch)
tree69ede131b1412765e2ee2b092727d277b352a2a4
parentbc75450cc3db3485db1e289fef8c1028ba38296a (diff)
HID: sony: Fix race condition in sony_probe
Early on the sony_probe function calls hid_hw_start to start the hardware. Afterwards it issues some hardware requests, initializes other functionality like Force Feedback, power classes and others. However by the time hid_hw_start returns, the device nodes have already been created, which leads to a race condition by user space applications which may detect the device prior to completion of initialization. We have observed this problem many times, this patch fixes the problem. This patch moves most of sony_probe to sony_input_configured, which is called prior to device registration. This fixes the race condition and the same approach is used in other HID drivers. Signed-off-by: Roderick Colenbrander <roderick.colenbrander@sony.com> Reviewed-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
-rw-r--r--drivers/hid/hid-sony.c117
1 files changed, 58 insertions, 59 deletions
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index b0bb99a821bd..afa82198defb 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -1387,28 +1387,6 @@ static int sony_register_touchpad(struct hid_input *hi, int touch_count,
1387 return 0; 1387 return 0;
1388} 1388}
1389 1389
1390static int sony_input_configured(struct hid_device *hdev,
1391 struct hid_input *hidinput)
1392{
1393 struct sony_sc *sc = hid_get_drvdata(hdev);
1394 int ret;
1395
1396 /*
1397 * The Dualshock 4 touchpad supports 2 touches and has a
1398 * resolution of 1920x942 (44.86 dots/mm).
1399 */
1400 if (sc->quirks & DUALSHOCK4_CONTROLLER) {
1401 ret = sony_register_touchpad(hidinput, 2, 1920, 942);
1402 if (ret) {
1403 hid_err(sc->hdev,
1404 "Unable to initialize multi-touch slots: %d\n",
1405 ret);
1406 return ret;
1407 }
1408 }
1409
1410 return 0;
1411}
1412 1390
1413/* 1391/*
1414 * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller 1392 * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller
@@ -2329,45 +2307,12 @@ static inline void sony_cancel_work_sync(struct sony_sc *sc)
2329 cancel_work_sync(&sc->state_worker); 2307 cancel_work_sync(&sc->state_worker);
2330} 2308}
2331 2309
2332static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) 2310static int sony_input_configured(struct hid_device *hdev,
2311 struct hid_input *hidinput)
2333{ 2312{
2334 int ret; 2313 struct sony_sc *sc = hid_get_drvdata(hdev);
2335 int append_dev_id; 2314 int append_dev_id;
2336 unsigned long quirks = id->driver_data; 2315 int ret;
2337 struct sony_sc *sc;
2338 unsigned int connect_mask = HID_CONNECT_DEFAULT;
2339
2340 if (!strcmp(hdev->name, "FutureMax Dance Mat"))
2341 quirks |= FUTUREMAX_DANCE_MAT;
2342
2343 sc = devm_kzalloc(&hdev->dev, sizeof(*sc), GFP_KERNEL);
2344 if (sc == NULL) {
2345 hid_err(hdev, "can't alloc sony descriptor\n");
2346 return -ENOMEM;
2347 }
2348
2349 spin_lock_init(&sc->lock);
2350
2351 sc->quirks = quirks;
2352 hid_set_drvdata(hdev, sc);
2353 sc->hdev = hdev;
2354
2355 ret = hid_parse(hdev);
2356 if (ret) {
2357 hid_err(hdev, "parse failed\n");
2358 return ret;
2359 }
2360
2361 if (sc->quirks & VAIO_RDESC_CONSTANT)
2362 connect_mask |= HID_CONNECT_HIDDEV_FORCE;
2363 else if (sc->quirks & SIXAXIS_CONTROLLER)
2364 connect_mask |= HID_CONNECT_HIDDEV_FORCE;
2365
2366 ret = hid_hw_start(hdev, connect_mask);
2367 if (ret) {
2368 hid_err(hdev, "hw start failed\n");
2369 return ret;
2370 }
2371 2316
2372 ret = sony_set_device_id(sc); 2317 ret = sony_set_device_id(sc);
2373 if (ret < 0) { 2318 if (ret < 0) {
@@ -2427,6 +2372,18 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
2427 } 2372 }
2428 } 2373 }
2429 2374
2375 /*
2376 * The Dualshock 4 touchpad supports 2 touches and has a
2377 * resolution of 1920x942 (44.86 dots/mm).
2378 */
2379 ret = sony_register_touchpad(hidinput, 2, 1920, 942);
2380 if (ret) {
2381 hid_err(sc->hdev,
2382 "Unable to initialize multi-touch slots: %d\n",
2383 ret);
2384 return ret;
2385 }
2386
2430 sony_init_output_report(sc, dualshock4_send_output_report); 2387 sony_init_output_report(sc, dualshock4_send_output_report);
2431 } else if (sc->quirks & MOTION_CONTROLLER) { 2388 } else if (sc->quirks & MOTION_CONTROLLER) {
2432 sony_init_output_report(sc, motion_send_output_report); 2389 sony_init_output_report(sc, motion_send_output_report);
@@ -2482,6 +2439,48 @@ err_stop:
2482 return ret; 2439 return ret;
2483} 2440}
2484 2441
2442static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
2443{
2444 int ret;
2445 unsigned long quirks = id->driver_data;
2446 struct sony_sc *sc;
2447 unsigned int connect_mask = HID_CONNECT_DEFAULT;
2448
2449 if (!strcmp(hdev->name, "FutureMax Dance Mat"))
2450 quirks |= FUTUREMAX_DANCE_MAT;
2451
2452 sc = devm_kzalloc(&hdev->dev, sizeof(*sc), GFP_KERNEL);
2453 if (sc == NULL) {
2454 hid_err(hdev, "can't alloc sony descriptor\n");
2455 return -ENOMEM;
2456 }
2457
2458 spin_lock_init(&sc->lock);
2459
2460 sc->quirks = quirks;
2461 hid_set_drvdata(hdev, sc);
2462 sc->hdev = hdev;
2463
2464 ret = hid_parse(hdev);
2465 if (ret) {
2466 hid_err(hdev, "parse failed\n");
2467 return ret;
2468 }
2469
2470 if (sc->quirks & VAIO_RDESC_CONSTANT)
2471 connect_mask |= HID_CONNECT_HIDDEV_FORCE;
2472 else if (sc->quirks & SIXAXIS_CONTROLLER)
2473 connect_mask |= HID_CONNECT_HIDDEV_FORCE;
2474
2475 ret = hid_hw_start(hdev, connect_mask);
2476 if (ret) {
2477 hid_err(hdev, "hw start failed\n");
2478 return ret;
2479 }
2480
2481 return ret;
2482}
2483
2485static void sony_remove(struct hid_device *hdev) 2484static void sony_remove(struct hid_device *hdev)
2486{ 2485{
2487 struct sony_sc *sc = hid_get_drvdata(hdev); 2486 struct sony_sc *sc = hid_get_drvdata(hdev);