HID: sony: support for ghlive ps3/wii u dongles

This commit adds support for the Guitar Hero Live PS3 and Wii U dongles.

These dongles require a "magic" USB control message [1] to be sent
approximately every 10 seconds otherwise the dongle will not report
events where the strumbar is hit while a fret is being held.

Also, inspired by a patch sent on linux-input by Sanjay Govind [2], the
accelerometer is mapped to ABS_RY for tilt.

Interestingly, the Wii U and PS3 dongles share the same VID and PID.

[1] https://github.com/ghlre/GHLtarUtility/
[2] https://marc.info/?l=linux-input&m=157242835928542&w=2

Signed-off-by: Pascal Giard <pascal.giard@etsmtl.ca>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
This commit is contained in:
Pascal Giard 2020-11-25 22:02:30 -05:00 committed by Jiri Kosina
parent 1a8212e8fd
commit cc894ac553
3 changed files with 116 additions and 0 deletions

View File

@ -907,6 +907,7 @@ config HID_SONY
* Buzz controllers * Buzz controllers
* Sony PS3 Blue-ray Disk Remote Control (Bluetooth) * Sony PS3 Blue-ray Disk Remote Control (Bluetooth)
* Logitech Harmony adapter for Sony Playstation 3 (Bluetooth) * Logitech Harmony adapter for Sony Playstation 3 (Bluetooth)
* Guitar Hero Live PS3 and Wii U guitar dongles
config SONY_FF config SONY_FF
bool "Sony PS2/3/4 accessories force feedback support" bool "Sony PS2/3/4 accessories force feedback support"

View File

@ -1074,6 +1074,9 @@
#define USB_DEVICE_ID_SONY_BUZZ_CONTROLLER 0x0002 #define USB_DEVICE_ID_SONY_BUZZ_CONTROLLER 0x0002
#define USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER 0x1000 #define USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER 0x1000
#define USB_VENDOR_ID_SONY_GHLIVE 0x12ba
#define USB_DEVICE_ID_SONY_PS3WIIU_GHLIVE_DONGLE 0x074b
#define USB_VENDOR_ID_SINO_LITE 0x1345 #define USB_VENDOR_ID_SINO_LITE 0x1345
#define USB_DEVICE_ID_SINO_LITE_CONTROLLER 0x3008 #define USB_DEVICE_ID_SINO_LITE_CONTROLLER 0x3008

View File

@ -11,6 +11,7 @@
* Copyright (c) 2013 Colin Leitner <colin.leitner@gmail.com> * Copyright (c) 2013 Colin Leitner <colin.leitner@gmail.com>
* Copyright (c) 2014-2016 Frank Praznik <frank.praznik@gmail.com> * Copyright (c) 2014-2016 Frank Praznik <frank.praznik@gmail.com>
* Copyright (c) 2018 Todd Kelner * Copyright (c) 2018 Todd Kelner
* Copyright (c) 2020 Pascal Giard <pascal.giard@etsmtl.ca>
*/ */
/* /*
@ -35,6 +36,8 @@
#include <linux/idr.h> #include <linux/idr.h>
#include <linux/input/mt.h> #include <linux/input/mt.h>
#include <linux/crc32.h> #include <linux/crc32.h>
#include <linux/usb.h>
#include <linux/timer.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
#include "hid-ids.h" #include "hid-ids.h"
@ -56,6 +59,7 @@
#define NSG_MR5U_REMOTE_BT BIT(14) #define NSG_MR5U_REMOTE_BT BIT(14)
#define NSG_MR7U_REMOTE_BT BIT(15) #define NSG_MR7U_REMOTE_BT BIT(15)
#define SHANWAN_GAMEPAD BIT(16) #define SHANWAN_GAMEPAD BIT(16)
#define GHL_GUITAR_PS3WIIU BIT(17)
#define SIXAXIS_CONTROLLER (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT) #define SIXAXIS_CONTROLLER (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT)
#define MOTION_CONTROLLER (MOTION_CONTROLLER_USB | MOTION_CONTROLLER_BT) #define MOTION_CONTROLLER (MOTION_CONTROLLER_USB | MOTION_CONTROLLER_BT)
@ -79,6 +83,17 @@
#define NSG_MRXU_MAX_X 1667 #define NSG_MRXU_MAX_X 1667
#define NSG_MRXU_MAX_Y 1868 #define NSG_MRXU_MAX_Y 1868
#define GHL_GUITAR_POKE_INTERVAL 10 /* In seconds */
#define GHL_GUITAR_TILT_USAGE 44
/* Magic value and data taken from GHLtarUtility:
* https://github.com/ghlre/GHLtarUtility/blob/master/PS3Guitar.cs
* Note: The Wii U and PS3 dongles happen to share the same!
*/
static const u16 ghl_ps3wiiu_magic_value = 0x201;
static const char ghl_ps3wiiu_magic_data[] = {
0x02, 0x08, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00
};
/* PS/3 Motion controller */ /* PS/3 Motion controller */
static u8 motion_rdesc[] = { static u8 motion_rdesc[] = {
@ -579,6 +594,10 @@ struct sony_sc {
enum ds4_dongle_state ds4_dongle_state; enum ds4_dongle_state ds4_dongle_state;
/* DS4 calibration data */ /* DS4 calibration data */
struct ds4_calibration_data ds4_calib_data[6]; struct ds4_calibration_data ds4_calib_data[6];
/* GH Live */
struct timer_list ghl_poke_timer;
struct usb_ctrlrequest *ghl_cr;
u8 *ghl_databuf;
}; };
static void sony_set_leds(struct sony_sc *sc); static void sony_set_leds(struct sony_sc *sc);
@ -602,6 +621,85 @@ static inline void sony_schedule_work(struct sony_sc *sc,
} }
} }
static void ghl_magic_poke_cb(struct urb *urb)
{
if (urb) {
/* Free sc->ghl_cr and sc->ghl_databuf allocated in
* ghl_magic_poke()
*/
kfree(urb->setup_packet);
kfree(urb->transfer_buffer);
}
}
static void ghl_magic_poke(struct timer_list *t)
{
struct sony_sc *sc = from_timer(sc, t, ghl_poke_timer);
int ret;
unsigned int pipe;
struct urb *urb;
struct usb_device *usbdev = to_usb_device(sc->hdev->dev.parent->parent);
const u16 poke_size =
ARRAY_SIZE(ghl_ps3wiiu_magic_data);
pipe = usb_sndctrlpipe(usbdev, 0);
if (!sc->ghl_cr) {
sc->ghl_cr = kzalloc(sizeof(*sc->ghl_cr), GFP_ATOMIC);
if (!sc->ghl_cr)
goto resched;
}
if (!sc->ghl_databuf) {
sc->ghl_databuf = kzalloc(poke_size, GFP_ATOMIC);
if (!sc->ghl_databuf)
goto resched;
}
urb = usb_alloc_urb(0, GFP_ATOMIC);
if (!urb)
goto resched;
sc->ghl_cr->bRequestType =
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT;
sc->ghl_cr->bRequest = USB_REQ_SET_CONFIGURATION;
sc->ghl_cr->wValue = cpu_to_le16(ghl_ps3wiiu_magic_value);
sc->ghl_cr->wIndex = 0;
sc->ghl_cr->wLength = cpu_to_le16(poke_size);
memcpy(sc->ghl_databuf, ghl_ps3wiiu_magic_data, poke_size);
usb_fill_control_urb(
urb, usbdev, pipe,
(unsigned char *) sc->ghl_cr, sc->ghl_databuf,
poke_size, ghl_magic_poke_cb, NULL);
ret = usb_submit_urb(urb, GFP_ATOMIC);
if (ret < 0) {
kfree(sc->ghl_databuf);
kfree(sc->ghl_cr);
}
usb_free_urb(urb);
resched:
/* Reschedule for next time */
mod_timer(&sc->ghl_poke_timer, jiffies + GHL_GUITAR_POKE_INTERVAL*HZ);
}
static int guitar_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
if ((usage->hid & HID_USAGE_PAGE) == HID_UP_MSVENDOR) {
unsigned int abs = usage->hid & HID_USAGE;
if (abs == GHL_GUITAR_TILT_USAGE) {
hid_map_usage_clear(hi, usage, bit, max, EV_ABS, ABS_RY);
return 1;
}
}
return 0;
}
static ssize_t ds4_show_poll_interval(struct device *dev, static ssize_t ds4_show_poll_interval(struct device *dev,
struct device_attribute struct device_attribute
*attr, char *buf) *attr, char *buf)
@ -1383,6 +1481,8 @@ static int sony_mapping(struct hid_device *hdev, struct hid_input *hi,
if (sc->quirks & DUALSHOCK4_CONTROLLER) if (sc->quirks & DUALSHOCK4_CONTROLLER)
return ds4_mapping(hdev, hi, field, usage, bit, max); return ds4_mapping(hdev, hi, field, usage, bit, max);
if (sc->quirks & GHL_GUITAR_PS3WIIU)
return guitar_mapping(hdev, hi, field, usage, bit, max);
/* Let hid-core decide for the others */ /* Let hid-core decide for the others */
return 0; return 0;
@ -2917,6 +3017,12 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
return -ENODEV; return -ENODEV;
} }
if (sc->quirks & GHL_GUITAR_PS3WIIU) {
timer_setup(&sc->ghl_poke_timer, ghl_magic_poke, 0);
mod_timer(&sc->ghl_poke_timer,
jiffies + GHL_GUITAR_POKE_INTERVAL*HZ);
}
return ret; return ret;
} }
@ -2924,6 +3030,9 @@ static void sony_remove(struct hid_device *hdev)
{ {
struct sony_sc *sc = hid_get_drvdata(hdev); struct sony_sc *sc = hid_get_drvdata(hdev);
if (sc->quirks & GHL_GUITAR_PS3WIIU)
del_timer_sync(&sc->ghl_poke_timer);
hid_hw_close(hdev); hid_hw_close(hdev);
if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) if (sc->quirks & DUALSHOCK4_CONTROLLER_BT)
@ -3035,6 +3144,9 @@ static const struct hid_device_id sony_devices[] = {
/* SMK-Link NSG-MR7U Remote Control */ /* SMK-Link NSG-MR7U Remote Control */
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_NSG_MR7U_REMOTE), { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_NSG_MR7U_REMOTE),
.driver_data = NSG_MR7U_REMOTE_BT }, .driver_data = NSG_MR7U_REMOTE_BT },
/* Guitar Hero Live PS3 and Wii U guitar dongles */
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY_GHLIVE, USB_DEVICE_ID_SONY_PS3WIIU_GHLIVE_DONGLE),
.driver_data = GHL_GUITAR_PS3WIIU},
{ } { }
}; };
MODULE_DEVICE_TABLE(hid, sony_devices); MODULE_DEVICE_TABLE(hid, sony_devices);