// SPDX-License-Identifier: GPL-2.0-or-later /* * Xbox gamepad driver * * Copyright (c) 2002 Marko Friedemann <mfr@bmx-chemnitz.de> * 2004 Oliver Schwartz <Oliver.Schwartz@gmx.de>, * Steven Toth <steve@toth.demon.co.uk>, * Franz Lehner <franz@caos.at>, * Ivan Hawkes <blackhawk@ivanhawkes.com> * 2005 Dominic Cerquetti <binary1230@yahoo.com> * 2006 Adam Buchbinder <adam.buchbinder@gmail.com> * 2007 Jan Kratochvil <honza@jikos.cz> * 2010 Christoph Fritz <chf.fritz@googlemail.com> * * This driver is based on: * - information from http://euc.jp/periphs/xbox-controller.ja.html * - the iForce driver drivers/char/joystick/iforce.c * - the skeleton-driver drivers/usb/usb-skeleton.c * - Xbox 360 information http://www.free60.org/wiki/Gamepad * - Xbox One information https://github.com/quantus/xbox-one-controller-protocol * * Thanks to: * - ITO Takayuki for providing essential xpad information on his website * - Vojtech Pavlik - iforce driver / input subsystem * - Greg Kroah-Hartman - usb-skeleton driver * - Xbox Linux project - extra USB IDs * - Pekka Pöyry (quantus) - Xbox One controller reverse-engineering * * TODO: * - fine tune axes (especially trigger axes) * - fix "analog" buttons (reported as digital now) * - get rumble working * - need USB IDs for other dance pads * * History: * * 2002-06-27 - 0.0.1 : first version, just said "XBOX HID controller" * * 2002-07-02 - 0.0.2 : basic working version * - all axes and 9 of the 10 buttons work (german InterAct device) * - the black button does not work * * 2002-07-14 - 0.0.3 : rework by Vojtech Pavlik * - indentation fixes * - usb + input init sequence fixes * * 2002-07-16 - 0.0.4 : minor changes, merge with Vojtech's v0.0.3 * - verified the lack of HID and report descriptors * - verified that ALL buttons WORK * - fixed d-pad to axes mapping * * 2002-07-17 - 0.0.5 : simplified d-pad handling * * 2004-10-02 - 0.0.6 : DDR pad support * - borrowed from the Xbox Linux kernel * - USB id's for commonly used dance pads are present * - dance pads will map D-PAD to buttons, not axes * - pass the module paramater 'dpad_to_buttons' to force * the D-PAD to map to buttons if your pad is not detected * * Later changes can be tracked in SCM.
*/
/* * xbox d-pads should map to buttons, as is required for DDR pads * but we map them to axes when possible to simplify things
*/ #define MAP_DPAD_TO_BUTTONS BIT(0) #define MAP_TRIGGERS_TO_BUTTONS BIT(1) #define MAP_STICKS_TO_NULL BIT(2) #define MAP_SHARE_BUTTON BIT(3) #define MAP_PADDLES BIT(4) #define MAP_PROFILE_BUTTON BIT(5) #define MAP_SHARE_OFFSET BIT(6)
staticbool 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");
staticbool triggers_to_buttons;
module_param(triggers_to_buttons, bool, S_IRUGO);
MODULE_PARM_DESC(triggers_to_buttons, "Map triggers to buttons rather than axes for unknown pads");
staticbool sticks_to_null;
module_param(sticks_to_null, bool, S_IRUGO);
MODULE_PARM_DESC(sticks_to_null, "Do not map sticks at all for unknown pads");
staticbool auto_poweroff = true;
module_param(auto_poweroff, bool, S_IWUSR | S_IRUGO);
MODULE_PARM_DESC(auto_poweroff, "Power off wireless controllers on suspend");
/* used when dpad is mapped to axes */ staticconstsignedshort xpad_abs_pad[] = {
ABS_HAT0X, ABS_HAT0Y, /* d-pad axes */
-1 /* terminating entry */
};
/* used when triggers are mapped to axes */ staticconstsignedshort xpad_abs_triggers[] = {
ABS_Z, ABS_RZ, /* triggers left/right */
-1
};
/* used when the controller has extra paddle buttons */ staticconstsignedshort xpad_btn_paddles[] = {
BTN_GRIPR, BTN_GRIPR2, /* paddle upper right, lower right */
BTN_GRIPL, BTN_GRIPL2, /* paddle upper left, lower left */
-1 /* terminating entry */
};
/* * 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 * match against vendor id as well. Wired Xbox 360 devices have protocol 1, * wireless controllers have protocol 129.
*/ #define XPAD_XBOX360_VENDOR_PROTOCOL(vend, pr) \
.match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO, \
.idVendor = (vend), \
.bInterfaceClass = USB_CLASS_VENDOR_SPEC, \
.bInterfaceSubClass = 93, \
.bInterfaceProtocol = (pr) #define XPAD_XBOX360_VENDOR(vend) \
{ XPAD_XBOX360_VENDOR_PROTOCOL((vend), 1) }, \
{ XPAD_XBOX360_VENDOR_PROTOCOL((vend), 129) }
/* * This packet is required for all Xbox One pads with 2015 * or later firmware installed (or present from the factory).
*/ staticconst u8 xboxone_power_on[] = {
GIP_CMD_POWER, GIP_OPT_INTERNAL, GIP_SEQ0, GIP_PL_LEN(1), GIP_PWR_ON
};
/* * This packet is required for Xbox One S (0x045e:0x02ea) * and Xbox One Elite Series 2 (0x045e:0x0b00) pads to * initialize the controller that was previously used in * Bluetooth mode.
*/ staticconst u8 xboxone_s_init[] = {
GIP_CMD_POWER, GIP_OPT_INTERNAL, GIP_SEQ0, 0x0f, 0x06
};
/* * This packet is required to get additional input data * from Xbox One Elite Series 2 (0x045e:0x0b00) pads. * We mostly do this right now to get paddle data
*/ staticconst u8 extra_input_packet_init[] = {
0x4d, 0x10, 0x01, 0x02, 0x07, 0x00
};
/* * This packet is required for the Titanfall 2 Xbox One pads * (0x0e6f:0x0165) to finish initialization and for Hori pads * (0x0f0d:0x0067) to make the analog sticks work.
*/ staticconst u8 xboxone_hori_ack_id[] = {
GIP_CMD_ACK, GIP_OPT_INTERNAL, GIP_SEQ0, GIP_PL_LEN(9),
0x00, GIP_CMD_IDENTIFY, GIP_OPT_INTERNAL, 0x3a, 0x00, 0x00, 0x00, 0x80, 0x00
};
/* * This packet is sent by default on Windows, and is required for some pads to * start sending input reports, including most (all?) of the PDP. These pads * include: (0x0e6f:0x02ab), (0x0e6f:0x02a4), (0x0e6f:0x02a6).
*/ staticconst u8 xboxone_led_on[] = { GIP_CMD_LED, GIP_OPT_INTERNAL, GIP_SEQ0,
GIP_PL_LEN(3), 0x00, GIP_LED_ON, 0x14 };
/* * This packet is required for most (all?) of the PDP pads to start * sending input reports. These pads include: (0x0e6f:0x02ab), * (0x0e6f:0x02a4), (0x0e6f:0x02a6).
*/ staticconst u8 xboxone_auth_done[] = {
GIP_CMD_AUTHENTICATE, GIP_OPT_INTERNAL, GIP_SEQ0, GIP_PL_LEN(2), 0x01, 0x00
};
/* * A specific rumble packet is required for some PowerA pads to start * sending input reports. One of those pads is (0x24c6:0x543a).
*/ staticconst u8 xboxone_rumblebegin_init[] = {
GIP_CMD_RUMBLE, 0x00, GIP_SEQ0, GIP_PL_LEN(9),
0x00, GIP_MOTOR_ALL, 0x00, 0x00, 0x1D, 0x1D, 0xFF, 0x00, 0x00
};
/* * A rumble packet with zero FF intensity will immediately * terminate the rumbling required to init PowerA pads. * This should happen fast enough that the motors don't * spin up to enough speed to actually vibrate the gamepad.
*/ staticconst u8 xboxone_rumbleend_init[] = {
GIP_CMD_RUMBLE, 0x00, GIP_SEQ0, GIP_PL_LEN(9),
0x00, GIP_MOTOR_ALL, 0x00, 0x00, 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 * sent. The correct sequence number will be added when the * packet is going to be sent.
*/ staticconststruct 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(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(0x0000, 0x0000, xboxone_led_on),
XBOXONE_INIT_PKT(0x0000, 0x0000, xboxone_auth_done),
XBOXONE_INIT_PKT(0x24c6, 0x541a, xboxone_rumblebegin_init),
XBOXONE_INIT_PKT(0x24c6, 0x542a, xboxone_rumblebegin_init),
XBOXONE_INIT_PKT(0x24c6, 0x543a, xboxone_rumblebegin_init),
XBOXONE_INIT_PKT(0x24c6, 0x541a, xboxone_rumbleend_init),
XBOXONE_INIT_PKT(0x24c6, 0x542a, xboxone_rumbleend_init),
XBOXONE_INIT_PKT(0x24c6, 0x543a, xboxone_rumbleend_init),
};
struct urb *irq_in; /* urb for interrupt in report */ unsignedchar *idata; /* input data */
dma_addr_t idata_dma;
struct urb *irq_out; /* urb for interrupt out report */ struct usb_anchor irq_out_anchor; bool irq_out_active; /* we must not use an active URB */
u8 odata_serial; /* serial number for xbox one protocol */ unsignedchar *odata; /* output data */
dma_addr_t odata_dma;
spinlock_t odata_lock;
struct xpad_output_packet out_packets[XPAD_NUM_OUT_PACKETS]; int last_out_packet; int init_seq;
int mapping; /* map d-pad to buttons or to axes */ int xtype; /* type of xbox device */ int packet_type; /* type of the extended packet */ int pad_nr; /* the order x360 pads were attached */ constchar *name; /* name of the device */ struct work_struct work; /* init/remove device from callback */
time64_t mode_btn_down_ts; bool delay_init; /* init packets should be delayed */ bool delayed_init_done;
};
/* * xpad_process_packet * * Completes a request by converting the data into events for the * input subsystem. * * The used report descriptor was taken from ITO Takayuki's website: * http://euc.jp/periphs/xbox-controller.ja.html
*/ staticvoid xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsignedchar *data)
{ struct input_dev *dev = xpad->dev;
/* * xpad360_process_packet * * Completes a request by converting the data into events for the * input subsystem. It is version for xbox 360 controller * * The used report descriptor was taken from: * http://www.free60.org/wiki/Gamepad
*/
staticvoid xpad360_process_packet(struct usb_xpad *xpad, struct input_dev *dev,
u16 cmd, unsignedchar *data)
{ /* valid pad data */ if (data[0] != 0x00) return;
/* digital pad */ if (xpad->mapping & MAP_DPAD_TO_BUTTONS) { /* dpad as buttons (left, right, up, down) */
input_report_key(dev, BTN_DPAD_LEFT, data[2] & BIT(2));
input_report_key(dev, BTN_DPAD_RIGHT, data[2] & BIT(3));
input_report_key(dev, BTN_DPAD_UP, data[2] & BIT(0));
input_report_key(dev, BTN_DPAD_DOWN, data[2] & BIT(1));
}
/* * This should be a simple else block. However historically * xbox360w has mapped DPAD to buttons while xbox360 did not. This * made no sense, but now we can not just switch back and have to * support both behaviors.
*/ if (!(xpad->mapping & MAP_DPAD_TO_BUTTONS) ||
xpad->xtype == XTYPE_XBOX360W) {
input_report_abs(dev, ABS_HAT0X,
!!(data[2] & 0x08) - !!(data[2] & 0x04));
input_report_abs(dev, ABS_HAT0Y,
!!(data[2] & 0x02) - !!(data[2] & 0x01));
}
if (xpad->pad_present) {
error = xpad_init_input(xpad); if (error) { /* complain only, not much else we can do here */
dev_err(&xpad->dev->dev, "unable to init device: %d\n", error);
} else {
rcu_assign_pointer(xpad->x360w_dev, xpad->dev);
}
} else {
RCU_INIT_POINTER(xpad->x360w_dev, NULL);
synchronize_rcu(); /* * Now that we are sure xpad360w_process_packet is not * using input device we can get rid of it.
*/
xpad_deinit_input(xpad);
}
}
/* * xpad360w_process_packet * * Completes a request by converting the data into events for the * input subsystem. It is version for xbox 360 wireless controller. * * Byte.Bit * 00.1 - Status change: The controller or headset has connected/disconnected * Bits 01.7 and 01.6 are valid * 01.7 - Controller present * 01.6 - Headset present * 01.1 - Pad state (Bytes 4+) valid *
*/ staticvoid xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsignedchar *data)
{ struct input_dev *dev; bool present;
rcu_read_lock();
dev = rcu_dereference(xpad->x360w_dev); if (dev)
xpad360_process_packet(xpad, dev, cmd, &data[4]);
rcu_read_unlock();
}
/* * xpadone_process_packet * * Completes a request by converting the data into events for the * input subsystem. This version is for the Xbox One controller. * * The report format was gleaned from * https://github.com/kylelemons/xbox/blob/master/xbox.go
*/ staticvoid xpadone_process_packet(struct usb_xpad *xpad, u16 cmd, unsignedchar *data, u32 len)
{ struct input_dev *dev = xpad->dev; bool do_sync = false;
/* the xbox button has its own special report */ if (data[0] == GIP_CMD_VIRTUAL_KEY) { /* * The Xbox One S controller requires these reports to be * acked otherwise it continues sending them forever and * won't report further mode button events.
*/ if (data[1] == (GIP_OPT_ACK | GIP_OPT_INTERNAL))
xpadone_ack_mode_report(xpad, data[2]);
do_sync = true;
} elseif (data[0] == GIP_CMD_FIRMWARE) { /* Some packet formats force us to use this separate to poll paddle inputs */ if (xpad->packet_type == PKT_XBE2_FW_5_11) { /* Mute paddles if controller is in a custom profile slot * Checked by looking at the active profile slot to * verify it's the default slot
*/ if (data[19] != 0)
data[18] = 0;
/* Profile button has a value of 0-3, so it is reported as an axis */ if (xpad->mapping & MAP_PROFILE_BUTTON)
input_report_abs(dev, ABS_PROFILE, data[34]);
/* paddle handling */ /* based on SDL's SDL_hidapi_xboxone.c */ if (xpad->mapping & MAP_PADDLES) { if (xpad->packet_type == PKT_XBE1) { /* Mute paddles if controller has a custom mapping applied. * Checked by comparing the current mapping * config against the factory mapping config
*/ if (memcmp(&data[4], &data[18], 2) != 0)
data[32] = 0;
/* OG Elite Series Controller paddle bits */
input_report_key(dev, BTN_GRIPR, data[32] & BIT(1));
input_report_key(dev, BTN_GRIPR2, data[32] & BIT(3));
input_report_key(dev, BTN_GRIPL, data[32] & BIT(0));
input_report_key(dev, BTN_GRIPL2, data[32] & BIT(2));
} elseif (xpad->packet_type == PKT_XBE2_FW_OLD) { /* Mute paddles if controller has a custom mapping applied. * Checked by comparing the current mapping * config against the factory mapping config
*/ if (data[19] != 0)
data[18] = 0;
/* Elite Series 2 4.x firmware paddle bits */
input_report_key(dev, BTN_GRIPR, data[18] & BIT(0));
input_report_key(dev, BTN_GRIPR2, data[18] & BIT(1));
input_report_key(dev, BTN_GRIPL, data[18] & BIT(2));
input_report_key(dev, BTN_GRIPL2, data[18] & BIT(3));
} elseif (xpad->packet_type == PKT_XBE2_FW_5_EARLY) { /* Mute paddles if controller has a custom mapping applied. * Checked by comparing the current mapping * config against the factory mapping config
*/ if (data[23] != 0)
data[22] = 0;
switch (status) { case 0: /* success */ break; case -ECONNRESET: case -ENOENT: case -ESHUTDOWN: /* this urb is terminated, clean up */
dev_dbg(dev, "%s - urb shutting down with status: %d\n",
__func__, status); return; default:
dev_dbg(dev, "%s - nonzero urb status received: %d\n",
__func__, status); gotoexit;
}
switch (xpad->xtype) { case XTYPE_XBOX360:
xpad360_process_packet(xpad, xpad->dev, 0, xpad->idata); break; case XTYPE_XBOX360W:
xpad360w_process_packet(xpad, 0, xpad->idata); break; case XTYPE_XBOXONE:
xpadone_process_packet(xpad, 0, xpad->idata, urb->actual_length); break; default:
xpad_process_packet(xpad, 0, xpad->idata);
}
exit:
retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval)
dev_err(dev, "%s - usb_submit_urb failed with result %d\n",
__func__, retval);
}
/* Callers must hold xpad->odata_lock spinlock */ staticbool xpad_prepare_next_init_packet(struct usb_xpad *xpad)
{ conststruct xboxone_init_packet *init_packet;
if (xpad->xtype != XTYPE_XBOXONE) returnfalse;
/* * Some dongles will discard init packets if they're sent before the * controller connects. In these cases, we need to wait until we get * an announce packet from them to send the init packet sequence.
*/ if (xpad->delay_init && !xpad->delayed_init_done) returnfalse;
/* Perform initialization sequence for Xbox One pads that require it */ while (xpad->init_seq < ARRAY_SIZE(xboxone_init_packets)) {
init_packet = &xboxone_init_packets[xpad->init_seq++];
if (init_packet->idVendor != 0 &&
init_packet->idVendor != xpad->dev->id.vendor) continue;
if (init_packet->idProduct != 0 &&
init_packet->idProduct != xpad->dev->id.product) continue;
/* This packet applies to our device, so prepare to send it */
memcpy(xpad->odata, init_packet->data, init_packet->len);
xpad->irq_out->transfer_buffer_length = init_packet->len;
/* Update packet with current sequence number */
xpad->odata[2] = xpad->odata_serial++; returntrue;
}
returnfalse;
}
/* Callers must hold xpad->odata_lock spinlock */ staticbool xpad_prepare_next_out_packet(struct usb_xpad *xpad)
{ struct xpad_output_packet *pkt, *packet = NULL; int i;
/* We may have init packets to send before we can send user commands */ if (xpad_prepare_next_init_packet(xpad)) returntrue;
for (i = 0; i < XPAD_NUM_OUT_PACKETS; i++) { if (++xpad->last_out_packet >= XPAD_NUM_OUT_PACKETS)
xpad->last_out_packet = 0;
case -ECONNRESET: case -ENOENT: case -ESHUTDOWN: /* this urb is terminated, clean up */
dev_dbg(dev, "%s - urb shutting down with status: %d\n",
__func__, status);
xpad->irq_out_active = false; break;
/* Reset the sequence so we send out presence first */
xpad->last_out_packet = -1; return xpad_try_sending_next_out_packet(xpad);
}
staticint xpad_start_xbox_one(struct usb_xpad *xpad)
{ int error;
if (usb_ifnum_to_if(xpad->udev, GIP_WIRED_INTF_AUDIO)) { /* * Explicitly disable the audio interface. This is needed * for some controllers, such as the PowerA Enhanced Wired * Controller for Series X|S (0x20d6:0x200e) to report the * guide button.
*/
error = usb_set_interface(xpad->udev,
GIP_WIRED_INTF_AUDIO, 0); if (error)
dev_warn(&xpad->dev->dev, "unable to disable audio interface: %d\n",
error);
}
guard(spinlock_irqsave)(&xpad->odata_lock);
/* * Begin the init sequence by attempting to send a packet. * We will cycle through the init packet sequence before * sending any packets from the output ring.
*/
xpad->init_seq = 0; return xpad_try_sending_next_out_packet(xpad);
}
/* * set the LEDs on Xbox 360 / Wireless Controllers * @param command * 0: off * 1: all blink, then previous setting * 2: 1/top-left blink, then on * 3: 2/top-right blink, then on * 4: 3/bottom-left blink, then on * 5: 4/bottom-right blink, then on * 6: 1/top-left on * 7: 2/top-right on * 8: 3/bottom-left on * 9: 4/bottom-right on * 10: rotate * 11: blink, based on previous setting * 12: slow blink, based on previous setting * 13: rotate with two lights * 14: persistent slow all blink * 15: blink once, then previous setting
*/ staticvoid xpad_send_led_command(struct usb_xpad *xpad, int command)
{ struct xpad_output_packet *packet =
&xpad->out_packets[XPAD_OUT_LED_IDX];
/* * Light up the segment corresponding to the pad number on * Xbox 360 Controllers.
*/ staticvoid xpad_identify_controller(struct usb_xpad *xpad)
{
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.30 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.