aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFrank Praznik <frank.praznik@gmail.com>2015-11-11 09:49:38 -0500
committerJiri Kosina <jkosina@suse.cz>2015-11-19 05:16:13 -0500
commitdecd946c99f6b3826bda0bfd5d1b2ddd56ef6b54 (patch)
treea9f7db8979aaff92e253f0787737eac43ebebc4c
parentd8aaccda7144df1c3d35251313197aed4cbea7bc (diff)
HID: sony: Save and restore the controller state on suspend and resume
On hardware which provides standby power for charging devices the state of the LEDs and force-feedback on controllers can persist even when the system is in standby. Additionally, the state of the controllers on resume may be different from the state they were in at the time when they were suspended (ie. LEDs are cleared on resume). This implements the suspend and resume callbacks which saves and clears the state of the LEDs on suspend and restores them on resume. Force-feedback is stopped on suspend but not automatically restored on resume until a new event is received to avoid potentially damaging hardware. USB Sixaxis and navigation controllers must be reinitialized when the hardware is reset on resume or they won't send any input reports. Signed-off-by: Frank Praznik <frank.praznik@gmail.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
-rw-r--r--drivers/hid/hid-sony.c65
1 files changed, 64 insertions, 1 deletions
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 4ba904b02bc2..1041c44765e1 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -1045,6 +1045,7 @@ struct sony_sc {
1045 __u8 battery_charging; 1045 __u8 battery_charging;
1046 __u8 battery_capacity; 1046 __u8 battery_capacity;
1047 __u8 led_state[MAX_LEDS]; 1047 __u8 led_state[MAX_LEDS];
1048 __u8 resume_led_state[MAX_LEDS];
1048 __u8 led_delay_on[MAX_LEDS]; 1049 __u8 led_delay_on[MAX_LEDS];
1049 __u8 led_delay_off[MAX_LEDS]; 1050 __u8 led_delay_off[MAX_LEDS];
1050 __u8 led_count; 1051 __u8 led_count;
@@ -1912,6 +1913,12 @@ static void motion_send_output_report(struct sony_sc *sc)
1912 hid_hw_output_report(hdev, (__u8 *)report, MOTION_REPORT_0x02_SIZE); 1913 hid_hw_output_report(hdev, (__u8 *)report, MOTION_REPORT_0x02_SIZE);
1913} 1914}
1914 1915
1916static inline void sony_send_output_report(struct sony_sc *sc)
1917{
1918 if (sc->send_output_report)
1919 sc->send_output_report(sc);
1920}
1921
1915static void sony_state_worker(struct work_struct *work) 1922static void sony_state_worker(struct work_struct *work)
1916{ 1923{
1917 struct sony_sc *sc = container_of(work, struct sony_sc, state_worker); 1924 struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
@@ -2427,6 +2434,56 @@ static void sony_remove(struct hid_device *hdev)
2427 hid_hw_stop(hdev); 2434 hid_hw_stop(hdev);
2428} 2435}
2429 2436
2437#ifdef CONFIG_PM
2438
2439static int sony_suspend(struct hid_device *hdev, pm_message_t message)
2440{
2441 /*
2442 * On suspend save the current LED state,
2443 * stop running force-feedback and blank the LEDS.
2444 */
2445 if (SONY_LED_SUPPORT || SONY_FF_SUPPORT) {
2446 struct sony_sc *sc = hid_get_drvdata(hdev);
2447
2448#ifdef CONFIG_SONY_FF
2449 sc->left = sc->right = 0;
2450#endif
2451
2452 memcpy(sc->resume_led_state, sc->led_state,
2453 sizeof(sc->resume_led_state));
2454 memset(sc->led_state, 0, sizeof(sc->led_state));
2455
2456 sony_send_output_report(sc);
2457 }
2458
2459 return 0;
2460}
2461
2462static int sony_resume(struct hid_device *hdev)
2463{
2464 /* Restore the state of controller LEDs on resume */
2465 if (SONY_LED_SUPPORT) {
2466 struct sony_sc *sc = hid_get_drvdata(hdev);
2467
2468 memcpy(sc->led_state, sc->resume_led_state,
2469 sizeof(sc->led_state));
2470
2471 /*
2472 * The Sixaxis and navigation controllers on USB need to be
2473 * reinitialized on resume or they won't behave properly.
2474 */
2475 if ((sc->quirks & SIXAXIS_CONTROLLER_USB) ||
2476 (sc->quirks & NAVIGATION_CONTROLLER_USB))
2477 sixaxis_set_operational_usb(sc->hdev);
2478
2479 sony_set_leds(sc);
2480 }
2481
2482 return 0;
2483}
2484
2485#endif
2486
2430static const struct hid_device_id sony_devices[] = { 2487static const struct hid_device_id sony_devices[] = {
2431 { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER), 2488 { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER),
2432 .driver_data = SIXAXIS_CONTROLLER_USB }, 2489 .driver_data = SIXAXIS_CONTROLLER_USB },
@@ -2476,7 +2533,13 @@ static struct hid_driver sony_driver = {
2476 .probe = sony_probe, 2533 .probe = sony_probe,
2477 .remove = sony_remove, 2534 .remove = sony_remove,
2478 .report_fixup = sony_report_fixup, 2535 .report_fixup = sony_report_fixup,
2479 .raw_event = sony_raw_event 2536 .raw_event = sony_raw_event,
2537
2538#ifdef CONFIG_PM
2539 .suspend = sony_suspend,
2540 .resume = sony_resume,
2541 .reset_resume = sony_resume,
2542#endif
2480}; 2543};
2481 2544
2482static int __init sony_init(void) 2545static int __init sony_init(void)