The bluez sixaxis driver has a new patch submitted that allows for the proper passing of the device name through to the kernel when connected over bluetooth. https://patches.linaro.org/patch/384498/ Prior to the referenced bluez patch, applicable sixaxis devices were all being named "Sony PLAYSTATION(R)3 Controller", a static value from a struct. Moving forward, the device name will be pulled from the udev HID_NAME property and we can now properly identify Shanwan and Gasia sixaxis devices in the hid driver. Some sixaxis shanwan controllers have an issue with rumble over bluetooth - every rumble event continues for 10 seconds and then all rumble events do not work for the following 10 seconds after that. I was able to identify that sending a soft-reset to the gamepad will stop the 10 second rumble, which led me to discovering that these delays are due to a timeout waiting for HIDP_WAITING_FOR_SEND_ACK in hidp_set_raw_report. So I've added a report-timeout member to the hid device struct and set it's default to the 10 seconds that was previously hard coded. I then change this value to zero in the sony hid driver probe for shanwan bluetooth devices to make rumble work properly. There is already a quirk for shanwan devices, but the existing quirk should only be applied on the USB bus, so I have changed this quirk to be named appropriately, added that check when applied, and added a new quirk for shanwan bluetooth devices. I have also changed the way the shanwan quirks are detected - instead of searching for a single specific device name, it will now apply when the device name includes "shanwan" in it at all. An example why this is important would be that I have a shanwan device that is identified as "Shanwan PLAYSTATION(R)3 Controller" - yes there are 5 spaces after Shanwan. I then use the same method to detect gasia devices, which share a similar code path as shanwan usb devices. Lastly, the report struct had members for rumble duration that were not being populated or passed to the report that's sent to the gamepad. This may be because ff memless tries to control this, but the default values in the sixaxis report were previously hard-set in the data. I have added these so that the report data being sent to the gamepad has the proper duration included. Signed-off-by: Adam Smith <mrfixit2001@xxxxxxxxx> --- drivers/hid/hid-sony.c | 71 ++++++++++++++++++++++++++++++++++++----------- include/linux/hid.h | 1 + net/bluetooth/hidp/core.c | 4 ++- 3 files changed, 59 insertions(+), 17 deletions(-) diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index e3a557d..ff49296 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -58,8 +58,10 @@ #define FUTUREMAX_DANCE_MAT BIT(13) #define NSG_MR5U_REMOTE_BT BIT(14) #define NSG_MR7U_REMOTE_BT BIT(15) -#define SHANWAN_GAMEPAD BIT(16) -#define GHL_GUITAR_PS3WIIU BIT(17) +#define SHANWAN_GAMEPAD_USB BIT(16) +#define SHANWAN_GAMEPAD_BT BIT(17) +#define GHL_GUITAR_PS3WIIU BIT(18) +#define GASIA_GAMEPAD BIT(19) #define SIXAXIS_CONTROLLER (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT) #define MOTION_CONTROLLER (MOTION_CONTROLLER_USB | MOTION_CONTROLLER_BT) @@ -573,6 +575,7 @@ struct sony_sc { #ifdef CONFIG_SONY_FF u8 left; u8 right; + u8 length; #endif u8 mac_address[6]; @@ -1664,7 +1667,7 @@ static int sixaxis_set_operational_usb(struct hid_device *hdev) * But the USB interrupt would cause SHANWAN controllers to * start rumbling non-stop, so skip step 3 for these controllers. */ - if (sc->quirks & SHANWAN_GAMEPAD) + if (sc->quirks & SHANWAN_GAMEPAD_USB) goto out; ret = hid_hw_output_report(hdev, buf, 1); @@ -2217,7 +2220,7 @@ static void sixaxis_send_output_report(struct sony_sc *sc) static const union sixaxis_output_report_01 default_report = { .buf = { 0x01, - 0x01, 0xff, 0x00, 0xff, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x27, 0x10, 0x00, 0x32, 0xff, 0x27, 0x10, 0x00, 0x32, @@ -2229,6 +2232,7 @@ static void sixaxis_send_output_report(struct sony_sc *sc) struct sixaxis_output_report *report = (struct sixaxis_output_report *)sc->output_report_dmabuf; int n; + u8 duration; /* Initialize the report with default values */ memcpy(report, &default_report, sizeof(struct sixaxis_output_report)); @@ -2236,6 +2240,13 @@ static void sixaxis_send_output_report(struct sony_sc *sc) #ifdef CONFIG_SONY_FF report->rumble.right_motor_on = sc->right ? 1 : 0; report->rumble.left_motor_force = sc->left; + + duration = sc->length; + if (duration > 0xff || duration == 0) duration = 0xff; + else if (duration < 4) duration = 4; + + report->rumble.left_duration = duration; + report->rumble.right_duration = duration; #endif report->leds_bitmap |= sc->led_state[0] << 1; @@ -2263,14 +2274,18 @@ static void sixaxis_send_output_report(struct sony_sc *sc) } } - /* SHANWAN controllers require output reports via intr channel */ - if (sc->quirks & SHANWAN_GAMEPAD) - hid_hw_output_report(sc->hdev, (u8 *)report, - sizeof(struct sixaxis_output_report)); - else - hid_hw_raw_request(sc->hdev, report->report_id, (u8 *)report, - sizeof(struct sixaxis_output_report), - HID_OUTPUT_REPORT, HID_REQ_SET_REPORT); + /* + * Some Shanwan and Gasia controllers require output reports via intr channel + * So try hid_hw_output_report first and if it fails then use hid_hw_raw_request + */ + if (sc->quirks & (SHANWAN_GAMEPAD_USB | GASIA_GAMEPAD)) + if(hid_hw_output_report(sc->hdev, (u8 *)report, + sizeof(struct sixaxis_output_report)) >= 0) + return; + + hid_hw_raw_request(sc->hdev, report->report_id, (u8 *)report, + sizeof(struct sixaxis_output_report), + HID_OUTPUT_REPORT, HID_REQ_SET_REPORT); } static void dualshock4_send_output_report(struct sony_sc *sc) @@ -2409,6 +2424,7 @@ static int sony_play_effect(struct input_dev *dev, void *data, sc->left = effect->u.rumble.strong_magnitude / 256; sc->right = effect->u.rumble.weak_magnitude / 256; + sc->length = effect->replay.length / 256; sony_schedule_work(sc, SONY_WORKER_STATE); return 0; @@ -2976,17 +2992,31 @@ static int sony_input_configured(struct hid_device *hdev, static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) { - int ret; + int ret, i; unsigned long quirks = id->driver_data; struct sony_sc *sc; unsigned int connect_mask = HID_CONNECT_DEFAULT; + char name[128]; + + // Get the device name in all uppercase + strncpy(name, hdev->name, 128); + for (i = 0; name[i] != '\0'; i++) + if(name[i] >= 'a' && name[i] <= 'z') + name[i] = name[i] -32; + + if (strstr(name, "SHANWAN")) { + if(quirks & SIXAXIS_CONTROLLER_BT) + quirks |= SHANWAN_GAMEPAD_BT; + else if(quirks & SIXAXIS_CONTROLLER_USB) + quirks |= SHANWAN_GAMEPAD_USB; + } + + if (strstr(name, "GASIA")) && (quirks & SIXAXIS_CONTROLLER)) + quirks |= GASIA_GAMEPAD; if (!strcmp(hdev->name, "FutureMax Dance Mat")) quirks |= FUTUREMAX_DANCE_MAT; - if (!strcmp(hdev->name, "SHANWAN PS3 GamePad")) - quirks |= SHANWAN_GAMEPAD; - sc = devm_kzalloc(&hdev->dev, sizeof(*sc), GFP_KERNEL); if (sc == NULL) { hid_err(hdev, "can't alloc sony descriptor\n"); @@ -3039,6 +3069,15 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) return -ENODEV; } +#ifdef CONFIG_SONY_FF + /* Rumble on some shanwan sixaxis bluetooth gamepads will get "stuck" waiting + * on HIDP_WAITING_FOR_SEND_ACK, so don't wait for the response at all. + * This must be set AFTER the HID has been started and claimed. + */ + if (sc->quirks & SHANWAN_GAMEPAD_BT) + hdev->report_timeout = 0; +#endif + if (sc->quirks & GHL_GUITAR_PS3WIIU) { timer_setup(&sc->ghl_poke_timer, ghl_magic_poke, 0); mod_timer(&sc->ghl_poke_timer, diff --git a/include/linux/hid.h b/include/linux/hid.h index c39d71e..b4a4f54 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -562,6 +562,7 @@ struct hid_device { /* device report descriptor */ unsigned country; /* HID country */ struct hid_report_enum report_enum[HID_REPORT_TYPES]; struct work_struct led_work; /* delayed LED worker */ + int report_timeout; /* seconds to wait for HIDP_WAITING_FOR_SEND_ACK */ struct semaphore driver_input_lock; /* protects the current driver */ struct device dev; /* device */ diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 0db48c8..b44aef0 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -354,7 +354,7 @@ static int hidp_set_raw_report(struct hid_device *hid, unsigned char reportnum, res = wait_event_interruptible_timeout(session->report_queue, !test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags) || atomic_read(&session->terminate), - 10*HZ); + hid->report_timeout*HZ); if (res == 0) { /* timeout */ ret = -EIO; @@ -780,6 +780,8 @@ static int hidp_setup_hid(struct hidp_session *session, hid->version = req->version; hid->country = req->country; + hid->report_timeout = 10; + strscpy(hid->name, req->name, sizeof(hid->name)); snprintf(hid->phys, sizeof(hid->phys), "%pMR",