…or “how I got Retropie to handle all the 10 buttons of the Razer Atrox changing a bit in xpad.ko”.
What does a videogame programmer on vacation? Well, many things, actually, but to somehow stick to stereotypes, he surely must *play* some videogames, too.
So, recipe:
- a Raspberry PI sitting in a drawer since months
- the Razer Atrox fight stick, bought on sale and sitting in another drawer since then
- the amazing RetroPIE SD image and some childhood game ROMs
Result:
- all working in a very straightforward way, EXCEPT for the fact that during the input configuration, only six of the main eight buttons worked (the top ones, because the Atrox also features two side buttons, “start” and “select”, that were working).
The buttons that didn’t work were the ones labeled LT and RT (Left Top and Right Top).
After excluding the chance of a hardware problem (trying the joystick under Windows), I decided that it was a driver issue… probably a simple one: 8 buttons out of 10 were working already, after all.
So, listing the loaded kernel modules and through quick Googling I found out that the code handling the joystick was
in xpad.c, and in a few minutes of code analysis I noticed that the two buttons not working were handled by this code block, only if the xpad->mapping
field was set to MAP_TRIGGERS_TO_BUTTONS
:
static void xpad360_process_packet(struct usb_xpad *xpad,
u16 cmd, unsigned char *data)
{
...
/* triggers left/right */
if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) {
input_report_key(dev, BTN_TL2, data[4]);
input_report_key(dev, BTN_TR2, data[5]);
} else {
input_report_abs(dev, ABS_Z, data[4]);
input_report_abs(dev, ABS_RZ, data[5]);
}
...
}
Before processing the packets, the xpad->mapping
field is set in xpad_probe
, after identifying the device by idVendor
and idProduct
:
static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
...
for (i = 0; xpad_device[i].idVendor; i++) {
if ((le16_to_cpu(udev->descriptor.idVendor) == xpad_device[i].idVendor) &&
(le16_to_cpu(udev->descriptor.idProduct) == xpad_device[i].idProduct))
break;
}
...
xpad->udev = udev;
xpad->intf = intf;
xpad->mapping = xpad_device[i].mapping;
xpad->xtype = xpad_device[i].xtype;
...
}
So, the fix looked to be setting the proper flag (MAP_TRIGGERS_TO_BUTTONS
) into the mapping
field of the Razer Atrox definition in the xpad_device array
of structs, that is a series of definitions used to tune the driver behaviour according to the detected hardware.
Another hint of that was that some similar fight sticks had the mapping field set to MAP_TRIGGERS_TO_BUTTONS
, while the Razer Atrox didn’t:
static const struct xpad_device {
u16 idVendor;
u16 idProduct;
char *name;
u8 mapping;
u8 xtype;
} xpad_device[] = {
...
{ 0x0738, 0x4728, "Mad Catz Street Fighter IV FightPad", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
{ 0x0738, 0x4738, "Mad Catz Wired Xbox 360 Controller (SFIV)", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
...
{ 0x24c6, 0x5000, "Razer Atrox Arcade Stick", 0, XTYPE_XBOX360 },
...
};
At this point, I wanted to try my simple fix:
...
{ 0x24c6, 0x5000, "Razer Atrox Arcade Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
..
I didn’t want to recompile the whole kernel, but only the xpad module. The process didn’t look totally straightforward, because Retropie was using a custom kernel image, and not the one distributed through APT (that would have made things easier).
The process was made harder by the lack of a proper Internet connection (due to my vacation setting: I only had available an unrealiable 3G connection accessed through wi-fi tethering).
So, after considering that if I was right I only had to change a byte in that struct array, I opted for the binary patching approach.
The C structs are usually compiled to the binaries in very simple and predictable way, and this wasn’t an exception: after a couple of two-bytes fields (idVendor
and idProduct
), we have a pointer to the name
string constant describing the model (4 bytes on 32 bit ARM), and after that two one-byte fields, mapping
and xtype
, the first of which contains the value we need to change.
The flags used as values for the “mapping” field are so defined:
#define MAP_DPAD_TO_BUTTONS (1 << 0)
#define MAP_TRIGGERS_TO_BUTTONS (1 << 1)
#define MAP_STICKS_TO_NULL (1 << 2)
#define DANCEPAD_MAP_CONFIG (MAP_DPAD_TO_BUTTONS | \
MAP_TRIGGERS_TO_BUTTONS | MAP_STICKS_TO_NULL)
...meaning that we're looking for a byte with value 0x00
in the original definition, and we need to change it to 0x02
(that is, 1 << 1
).
The two bytes fields used for identification, 0x24c6
and 0x24c6
, are a perfect candidate to search into the binary, after remembering to fix the byte ordering field by field: 0x24c6
gets compiled to C6 04
and 0x5000
to 00 50
.
After firing and hex-editor on my version of xpad.ko, and searching for the hex string C6 24 00 50
, I found out the binary translation of the Razer Atrox definition at offset 1CA8
:
C6 24 00 50 D8 09 00 00 00 01 -----|-----|-----------|--|-- { 0x24c6, 0x5000, "Razer Atrox Arcade Stick", 0, XTYPE_XBOX360 }
That needs to be patched to
C6 24 00 50 D8 09 00 00 02 01 -----|-----|-----------|--|-- { 0x24c6, 0x5000, "Razer Atrox Arcade Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }
I did the simple modification with an hex-editor, replaced the original xpad.ko file with the patched one (in /lib/modules/3.18.11+/kernel/drivers/input/jostick/), rebooted, and the Razer Atrox buttons were all working nicely.
I submitted the patch to the Linux kernel tree following the standard procedure.
Hello Sir!
It is possible to use razer atrox xbox one in retropie system?
Thanks in advance!
Sorry, I didn’t get notified about this comment… I don’t know, but I guess it should be possible.
Hi!
Just to say thank you.
I had this problem also, but with the MadCatz SoulCalibur V
This fightstick is recognized as an “generic x-box”, so, in found the hex in my xpad.ko and did the same as you said and it worked!
Many Thanks man!
Great hack man! Was this with the older “Xboxdrv Driver”, shown here in the RetroPie docs:
https://retropie.org.uk/docs/Xbox-360-Controller/#xboxdrv-driver
I don’t know when, but they discontinued using that driver some time ago, and now use “Xpad Driver” instead (talked about on that same link above). Have you updated your Pi in the last couple years, and did it work without doing this mod (i.e. did the newer Xpad Driver fix the issue)? If this hack is still needed (but on Xpad Driver now, since you can’t use Xboxdrv anymore), was the process the same?
I just ordered a Razer Atrox for Xbox 360 (used) off eBay so curious if it’s going to work on my Raspberry Pi 3 or 4 running RetroPie 4.7 and 4.8. Worst case I’ll install another PCB control board but would rather not… (especially since that would remove the built-in turbo feature, the light-up logo, etc.). Thanks! (and hope you still check this blog lol 😉
Hi! I think it was with the “Xpad Driver” already (I mean, the file needing the fix was named “xpad.c”). I sent the patch and it was approved, so I think you should have it working without doing anything. Not sure about the first RetroPie version that included it, but anything recent should be fine, it’s been some years by now.
Have fun!
FINALLY got the fightstick all built-up and finished and tested on my Raspberry Pi 4 running RetroPie – IT WORKS!!! Also works on Windows 10 as well if anybody is wondering. Thanks!
Great! Thanks for reporting back! 🙂