…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.

the Razer Atrox connected to Raspberry Pi running RetroPie

the Razer Atrox connected to Raspberry Pi running RetroPie

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 }
HxDen comparing the original xpad.ko and the patched one

HxDen comparing the original xpad.ko and the patched one

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.