diff --git a/xpad.c b/xpad.c index 2a1576f..6d8edfb 100644 --- a/xpad.c +++ b/xpad.c @@ -71,9 +71,15 @@ #include #include #include +#include #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 @@ -108,6 +114,7 @@ #define QUIRK_360_START_PKT_1 (1 << 0) #define QUIRK_360_START_PKT_2 (1 << 1) #define QUIRK_360_START_PKT_3 (1 << 2) +#define QUIRK_GHL_XBOXONE (1 << 3) #define QUIRK_360_START (QUIRK_360_START_PKT_1 | \ QUIRK_360_START_PKT_2 | QUIRK_360_START_PKT_3) @@ -300,6 +307,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 }, @@ -465,6 +473,12 @@ static const signed short xpad_btn_paddles[] = { -1 /* terminating entry */ }; +/* used for GHL dpad mapping */ +static const struct {int x; int y; } dpad_mapping[] = { + {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 @@ -524,6 +538,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 X-Box One controllers */ XPAD_XBOX360_VENDOR(0x146b), /* Bigben Interactive controllers */ XPAD_XBOX360_VENDOR(0x1532), /* Razer Sabertooth */ XPAD_XBOXONE_VENDOR(0x1532), /* Razer Wildcat */ @@ -690,6 +705,11 @@ static const u8 xboxone_rumbleend_init[] = { 0x00, GIP_MOTOR_ALL, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +/* GHL Xbox One magic data */ +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 @@ -699,13 +719,16 @@ 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(0x20d6, 0xa01a, xboxone_pdp_led_on), XBOXONE_INIT_PKT(0x0e6f, 0x0000, xboxone_pdp_auth), + XBOXONE_INIT_PKT(0x1430, 0x079b, xboxone_pdp_auth), XBOXONE_INIT_PKT(0x20d6, 0xa01a, xboxone_pdp_auth), XBOXONE_INIT_PKT(0x24c6, 0x541a, xboxone_rumblebegin_init), XBOXONE_INIT_PKT(0x24c6, 0x542a, xboxone_rumblebegin_init), @@ -768,6 +791,8 @@ struct usb_xpad { struct work_struct work; /* init/remove device from callback */ struct delayed_work poweroff_work; /* work struct for poweroff on mode long press */ time64_t mode_btn_down_ts; + struct urb *ghl_urb; /* URB for GHL Xbox One magic data */ + struct timer_list ghl_poke_timer; /* Timer for periodic poke of GHL magic data */ }; static int xpad_init_input(struct usb_xpad *xpad); @@ -775,6 +800,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 + * + * Call back function that resets the timer for the next magic data poke. + */ +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 GHL magic_data URB. + */ +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 GHL magic_data. + */ +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 == NULL) + 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 * @@ -1032,6 +1114,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) { @@ -1178,6 +1261,48 @@ 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 */ + + /* The 6 fret buttons */ + 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_A, data[4] & BIT(0)); + 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, dpad_mapping[dpad_value].x); + input_report_abs(dev, ABS_HAT0Y, dpad_mapping[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; } @@ -2007,15 +2132,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, -32767, 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, -32767, 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, -32767, 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: @@ -2315,6 +2454,21 @@ 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) + return -ENOMEM; + + error = ghl_init_urb(xpad, udev, ghl_xboxone_magic_data, ARRAY_SIZE(ghl_xboxone_magic_data), ep_irq_out); + + if (error) + return error; + + 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_deinit_output: @@ -2346,6 +2500,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);