Hello Dmitry, On Fri, Jul 19, 2024 at 10:11 AM Daniel Nguyen <daniel.cm.nguyen@xxxxxxxxx> wrote: > > From: Daniel Nguyen <daniel.nguyen.1@xxxxxxxxxxxxx> > > This commit adds support for the Guitar Hero Live (GHL) Xbox One dongles. > > These dongles require a "magic" USB interrupt message to be sent every 8 > seconds, otherwise the dongle will not report events where the strumbar > is hit while a fret is being held. > > Therefore, ghl_poke_timer and ghl_urb are added to struct usb_xpad. > ghl_poke_timer ensures periodicity and ghl_urb contains the "magic" > data. > > The mapping of buttons and axes was chosen to be coherent with GHL > dongles of other consoles. Refer to drivers/hid/hid-sony.c. > > Co-developed-by: Pascal Giard <pascal.giard@xxxxxxxxx> > Signed-off-by: Pascal Giard <pascal.giard@xxxxxxxxx> > Signed-off-by: Daniel Nguyen <daniel.nguyen.1@xxxxxxxxxxxxx> > --- > drivers/input/joystick/xpad.c | 182 +++++++++++++++++++++++++++++++++- > 1 file changed, 179 insertions(+), 3 deletions(-) > > diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c > index 40a4ddee0b14..13c82c0d6461 100644 > --- a/drivers/input/joystick/xpad.c > +++ b/drivers/input/joystick/xpad.c > @@ -11,6 +11,8 @@ > * 2006 Adam Buchbinder <adam.buchbinder@xxxxxxxxx> > * 2007 Jan Kratochvil <honza@xxxxxxxx> > * 2010 Christoph Fritz <chf.fritz@xxxxxxxxxxxxxx> > + * 2024 Pascal Giard <pascal.giard@xxxxxxxxx> > + * 2024 Daniel Nguyen <daniel.nguyen.1@xxxxxxxxxxxxx> > * > * This driver is based on: > * - information from http://euc.jp/periphs/xbox-controller.ja.html > @@ -70,9 +72,16 @@ > #include <linux/module.h> > #include <linux/usb/input.h> > #include <linux/usb/quirks.h> > +#include <linux/timer.h> > > #define XPAD_PKT_LEN 64 > > +/* > + * The Guitar Hero Live (GHL) Xbox One dongles require a poke > + * every 8 seconds. > + */ > +#define GHL_GUITAR_POKE_INTERVAL 8 /* in seconds */ > + > /* > * xbox d-pads should map to buttons, as is required for DDR pads > * but we map them to axes when possible to simplify things > @@ -104,6 +113,8 @@ > #define PKT_XBE2_FW_5_EARLY 3 > #define PKT_XBE2_FW_5_11 4 > > +#define QUIRK_GHL_XBOXONE BIT(0) > + > static bool dpad_to_buttons; > module_param(dpad_to_buttons, bool, S_IRUGO); > MODULE_PARM_DESC(dpad_to_buttons, "Map D-PAD to buttons rather than axes for unknown pads"); > @@ -126,6 +137,7 @@ static const struct xpad_device { > char *name; > u8 mapping; > u8 xtype; > + u8 quirks; > } xpad_device[] = { > /* Please keep this list sorted by vendor and product ID. */ > { 0x0079, 0x18d4, "GPD Win 2 X-Box Controller", 0, XTYPE_XBOX360 }, > @@ -209,6 +221,7 @@ static const struct xpad_device { > { 0x0738, 0xf738, "Super SFIV FightStick TE S", 0, XTYPE_XBOX360 }, > { 0x07ff, 0xffff, "Mad Catz GamePad", 0, XTYPE_XBOX360 }, > { 0x0b05, 0x1a38, "ASUS ROG RAIKIRI", 0, XTYPE_XBOXONE }, > + { 0x0b05, 0x1abb, "ASUS ROG RAIKIRI PRO", 0, XTYPE_XBOXONE }, > { 0x0c12, 0x0005, "Intec wireless", 0, XTYPE_XBOX }, > { 0x0c12, 0x8801, "Nyko Xbox Controller", 0, XTYPE_XBOX }, > { 0x0c12, 0x8802, "Zeroplus Xbox Controller", 0, XTYPE_XBOX }, We just noticed that this addition is part of the patch by mistake. It will be removed in v2. > @@ -286,6 +299,7 @@ static const struct xpad_device { > { 0x12ab, 0x0301, "PDP AFTERGLOW AX.1", 0, XTYPE_XBOX360 }, > { 0x12ab, 0x0303, "Mortal Kombat Klassic FightStick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, > { 0x12ab, 0x8809, "Xbox DDR dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, > + { 0x1430, 0x079B, "RedOctane GHL Controller", 0, XTYPE_XBOXONE, QUIRK_GHL_XBOXONE}, > { 0x1430, 0x4748, "RedOctane Guitar Hero X-plorer", 0, XTYPE_XBOX360 }, > { 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, > { 0x1430, 0xf801, "RedOctane Controller", 0, XTYPE_XBOX360 }, > @@ -443,6 +457,12 @@ static const signed short xpad_btn_paddles[] = { > -1 /* terminating entry */ > }; > > +/* used for the dmap mapping of the GHL Xbox One dongle */ > +static const struct {int x; int y; } xpad_dpad_ghl[] = { > + {0, -1}, {1, -1}, {1, 0}, {1, 1}, {0, 1}, {-1, 1}, {-1, 0}, {-1, -1}, > + {0, 0} > +}; > + > /* > * Xbox 360 has a vendor-specific class, so we cannot match it with only > * USB_INTERFACE_INFO (also specifically refused by USB subsystem), so we > @@ -502,6 +522,7 @@ static const struct usb_device_id xpad_table[] = { > XPAD_XBOX360_VENDOR(0x1209), /* Ardwiino Controllers */ > XPAD_XBOX360_VENDOR(0x12ab), /* Xbox 360 dance pads */ > XPAD_XBOX360_VENDOR(0x1430), /* RedOctane Xbox 360 controllers */ > + XPAD_XBOXONE_VENDOR(0x1430), /* RedOctane Xbox One controllers */ > XPAD_XBOX360_VENDOR(0x146b), /* Bigben Interactive controllers */ > XPAD_XBOX360_VENDOR(0x1532), /* Razer Sabertooth */ > XPAD_XBOXONE_VENDOR(0x1532), /* Razer Wildcat */ > @@ -666,6 +687,14 @@ static const u8 xboxone_rumbleend_init[] = { > 0x00, GIP_MOTOR_ALL, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 > }; > > +/* > + * Magic data for the GHL Xbox One dongles sniffed with a USB > + * protocol analyzer. > + */ > +static const char ghl_xboxone_magic_data[] = { > + 0x22, 0x00, 0x00, 0x08, 0x02, 0x08, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00 > +}; > + > /* > * This specifies the selection of init packets that a gamepad > * will be sent on init *and* the order in which they will be > @@ -675,12 +704,15 @@ static const u8 xboxone_rumbleend_init[] = { > static const struct xboxone_init_packet xboxone_init_packets[] = { > XBOXONE_INIT_PKT(0x0e6f, 0x0165, xboxone_hori_ack_id), > XBOXONE_INIT_PKT(0x0f0d, 0x0067, xboxone_hori_ack_id), > + XBOXONE_INIT_PKT(0x1430, 0x079b, xboxone_hori_ack_id), > XBOXONE_INIT_PKT(0x0000, 0x0000, xboxone_power_on), > XBOXONE_INIT_PKT(0x045e, 0x02ea, xboxone_s_init), > XBOXONE_INIT_PKT(0x045e, 0x0b00, xboxone_s_init), > XBOXONE_INIT_PKT(0x045e, 0x0b00, extra_input_packet_init), > XBOXONE_INIT_PKT(0x0e6f, 0x0000, xboxone_pdp_led_on), > + XBOXONE_INIT_PKT(0x1430, 0x079b, xboxone_pdp_led_on), > XBOXONE_INIT_PKT(0x0e6f, 0x0000, xboxone_pdp_auth), > + XBOXONE_INIT_PKT(0x1430, 0x079b, xboxone_pdp_auth), > XBOXONE_INIT_PKT(0x24c6, 0x541a, xboxone_rumblebegin_init), > XBOXONE_INIT_PKT(0x24c6, 0x542a, xboxone_rumblebegin_init), > XBOXONE_INIT_PKT(0x24c6, 0x543a, xboxone_rumblebegin_init), > @@ -740,6 +772,10 @@ struct usb_xpad { > const char *name; /* name of the device */ > struct work_struct work; /* init/remove device from callback */ > time64_t mode_btn_down_ts; > + int quirks; > + struct urb *ghl_urb; /* URB for GHL Xbox One dongle magic data */ > + /* timer for periodic poke of Xbox One dongle with magic data */ > + struct timer_list ghl_poke_timer; > }; > > static int xpad_init_input(struct usb_xpad *xpad); > @@ -747,6 +783,63 @@ static void xpad_deinit_input(struct usb_xpad *xpad); > static void xpadone_ack_mode_report(struct usb_xpad *xpad, u8 seq_num); > static void xpad360w_poweroff_controller(struct usb_xpad *xpad); > > +/* > + * ghl_magic_poke_cb > + * > + * Callback function that resets the timer for the next magic data poke > + * as required by the GHL Xbox One dongle. > + */ > +static void ghl_magic_poke_cb(struct urb *urb) > +{ > + struct usb_xpad *xpad = urb->context; > + > + if (urb->status < 0) > + pr_warn("URB transfer failed.\n"); > + > + mod_timer(&xpad->ghl_poke_timer, jiffies + GHL_GUITAR_POKE_INTERVAL * HZ); > +} > + > +/* > + * ghl_magic_poke > + * > + * Submits the magic_data URB as required by the GHL Xbox One dongle. > + */ > +static void ghl_magic_poke(struct timer_list *t) > +{ > + int ret; > + struct usb_xpad *xpad = from_timer(xpad, t, ghl_poke_timer); > + > + ret = usb_submit_urb(xpad->ghl_urb, GFP_ATOMIC); > + if (ret < 0) > + pr_warn("URB transfer failed.\n"); > +} > + > +/* > + * ghl_init_urb > + * > + * Prepares the interrupt URB for magic_data as required by the GHL Xbox One dongle. > + */ > +static int ghl_init_urb(struct usb_xpad *xpad, struct usb_device *usbdev, > + const char ghl_magic_data[], u16 poke_size, > + struct usb_endpoint_descriptor *ep_irq_out) > +{ > + u8 *databuf; > + unsigned int pipe; > + > + pipe = usb_sndintpipe(usbdev, ep_irq_out->bEndpointAddress); > + > + databuf = devm_kzalloc(&xpad->udev->dev, poke_size, GFP_ATOMIC); > + if (!databuf) > + return -ENOMEM; > + > + memcpy(databuf, ghl_magic_data, poke_size); > + > + usb_fill_int_urb(xpad->ghl_urb, usbdev, pipe, databuf, poke_size, > + ghl_magic_poke_cb, xpad, ep_irq_out->bInterval); > + > + return 0; > +} > + > /* > * xpad_process_packet > * > @@ -995,6 +1088,7 @@ static void xpadone_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char > { > struct input_dev *dev = xpad->dev; > bool do_sync = false; > + int dpad_value; > > /* the xbox button has its own special report */ > if (data[0] == GIP_CMD_VIRTUAL_KEY) { > @@ -1141,6 +1235,50 @@ static void xpadone_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char > } > } > > + do_sync = true; > + } else if (data[0] == 0X21) { /* The main valid packet type for GHL inputs */ > + /* Mapping chosen to be coherent with GHL dongles of other consoles: > + * PS2, WiiU & PS4. > + * > + * Refer to drivers/hid/hid-sony.c. > + */ > + /* The 6 fret buttons */ > + input_report_key(dev, BTN_A, data[4] & BIT(0)); > + input_report_key(dev, BTN_B, data[4] & BIT(1)); > + input_report_key(dev, BTN_X, data[4] & BIT(2)); > + input_report_key(dev, BTN_Y, data[4] & BIT(3)); > + input_report_key(dev, BTN_TL, data[4] & BIT(4)); > + input_report_key(dev, BTN_TR, data[4] & BIT(5)); > + > + /* D-pad */ > + dpad_value = data[6] & 0xF; > + if (dpad_value > 7) > + dpad_value = 8; > + > + input_report_abs(dev, ABS_HAT0X, xpad_dpad_ghl[dpad_value].x); > + input_report_abs(dev, ABS_HAT0Y, xpad_dpad_ghl[dpad_value].y); > + > + /* Strum bar */ > + input_report_abs(dev, ABS_Y, ((data[8] - 0x80) << 9)); > + > + /* Tilt sensor */ > + input_report_abs(dev, ABS_Z, ((data[9] - 0x80) << 9)); > + > + /* Whammy bar */ > + input_report_abs(dev, ABS_RZ, ((data[10] - 0x80) << 9)); > + > + /* Power button */ > + input_report_key(dev, BTN_THUMBR, data[5] & BIT(4)); > + > + /* GHTV button */ > + input_report_key(dev, BTN_START, data[5] & BIT(2)); > + > + /* Hero Power button */ > + input_report_key(dev, BTN_MODE, data[5] & BIT(0)); > + > + /* Pause button */ > + input_report_key(dev, BTN_THUMBL, data[5] & BIT(1)); > + > do_sync = true; > } > > @@ -1862,16 +2000,29 @@ static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs) > switch (abs) { > case ABS_X: > case ABS_Y: > + /* GHL Strum bar */ > + if (xpad->xtype == XTYPE_XBOXONE && xpad->quirks & QUIRK_GHL_XBOXONE) > + input_set_abs_params(input_dev, abs, -32768, 32767, 0, 0); > + break; > case ABS_RX: > case ABS_RY: /* the two sticks */ > input_set_abs_params(input_dev, abs, -32768, 32767, 16, 128); > break; > case ABS_Z: > + /* GHL Tilt sensor */ > + if (xpad->xtype == XTYPE_XBOXONE && xpad->quirks & QUIRK_GHL_XBOXONE) > + input_set_abs_params(input_dev, abs, -32768, 32767, 0, 0); > + break; > case ABS_RZ: /* the triggers (if mapped to axes) */ > - if (xpad->xtype == XTYPE_XBOXONE) > - input_set_abs_params(input_dev, abs, 0, 1023, 0, 0); > - else > + if (xpad->xtype == XTYPE_XBOXONE) { > + /* GHL Whammy bar */ > + if (xpad->quirks & QUIRK_GHL_XBOXONE) > + input_set_abs_params(input_dev, abs, -32768, 32767, 0, 0); > + else > + input_set_abs_params(input_dev, abs, 0, 1023, 0, 0); > + } else { > input_set_abs_params(input_dev, abs, 0, 255, 0, 0); > + } > break; > case ABS_HAT0X: > case ABS_HAT0Y: /* the d-pad (only if dpad is mapped to axes */ > @@ -2047,6 +2198,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id > xpad->mapping = xpad_device[i].mapping; > xpad->xtype = xpad_device[i].xtype; > xpad->name = xpad_device[i].name; > + xpad->quirks = xpad_device[i].quirks; > xpad->packet_type = PKT_XB; > INIT_WORK(&xpad->work, xpad_presence_work); > > @@ -2169,8 +2321,26 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id > if (error) > goto err_deinit_output; > } > + > + if (xpad->quirks & QUIRK_GHL_XBOXONE) { > + xpad->ghl_urb = usb_alloc_urb(0, GFP_ATOMIC); > + if (!xpad->ghl_urb) { > + error = -ENOMEM; > + goto err_deinit_output; > + } > + > + error = ghl_init_urb(xpad, udev, ghl_xboxone_magic_data, > + ARRAY_SIZE(ghl_xboxone_magic_data), ep_irq_out); > + if (error) > + goto err_free_ghl_urb; > + > + timer_setup(&xpad->ghl_poke_timer, ghl_magic_poke, 0); > + mod_timer(&xpad->ghl_poke_timer, jiffies + GHL_GUITAR_POKE_INTERVAL * HZ); > + } > return 0; > > +err_free_ghl_urb: > + usb_free_urb(xpad->ghl_urb); > err_deinit_output: > xpad_deinit_output(xpad); > err_free_in_urb: > @@ -2200,6 +2370,12 @@ static void xpad_disconnect(struct usb_interface *intf) > xpad_deinit_output(xpad); > > usb_free_urb(xpad->irq_in); > + > + if (xpad->quirks & QUIRK_GHL_XBOXONE) { > + usb_free_urb(xpad->ghl_urb); > + del_timer_sync(&xpad->ghl_poke_timer); > + } > + > usb_free_coherent(xpad->udev, XPAD_PKT_LEN, > xpad->idata, xpad->idata_dma); > > -- > 2.34.1 > I apologize for that line of code regarding ASUS ROG RAIKIRI PRO that does not belong in this patch. I'll await your feedback before submitting a v2. Thank you for your time! Daniel