XBO Arcade Sticks, VigEm and a whole ton of "fun"...
Hey all, my first post to the forum but I've been active in the Discord discussing this stuff with @nefarius , @megadrago88 and @evilC. I decided it's time to put things somewhere a little more organised
The short version:
I'm working on getting XBO Arcade Sticks (E.g. Razer Atrox XBO, etc) working properly on Windows 10 using VigEm. This thread will serve as a development/R&D/discussion log for this project.
The long version:
About 2 weeks ago I picked up a Razer Atrox XBO Arcade Stick. It was on sale and I thought it was the XB360 version. When I got it I discovered that it was, in fact, the XBO version (That's what you get for not reading the product description properly) and the thing with XBO Arcade Sticks on Windows 10 is...well...the don't work...mostly, except when they do....which is only under very specific circumstances.
As you can see, most of them are not positive. The summary of the current situation as it stands is as follows:
- There are two drivers available for use with XBO Arcade Sticks on Windows 10
- The modern driver provided by Microsoft works...but only in applications that use the
Windows.Gaming.InputAPIs that are part of UWP / WinRT. In other words, if you want to play only Killer Instinct (Windows Store version) then for you, XBO Arcade Sticks work perfectly. When using this driver, XBO sticks are not exposed as XInput or DInput devices, are not visible to
joy.cpland can not be used in Steam.
- The beta XBO Controller drivers that were released in 2014. These can be a little tricky to find a working download for but are available. Using this driver your XBO Arcade Stick will be visible in
joy.cpland is usable in Steam. However, only 6 of the 8 face buttons on the Arcade Stick will be usable. The RT and LT buttons will mysteriously not work...
- On Linux, the
xpaddriver implements support for various XBox controllers (original XBox, XB360 and XBO) but it too suffers the 6 button issue
If you like watching videos, here is one I put together on YouTube which walks through the issue:
For further details, I'd recommend reading the following links:
The quest begins...
So, after digging up all this information my first impulse was to simply return the device and instead purchase one that was XB360 compatible as those all work fine on Windows. However, I was somewhat curious and so I began doing a little bit of digging.
First, I installed a piece of software called Device Monitoring Studio. DMS, much like Wireshark + USBPcap, is able to inspect the traffic sent and received by USB devices. Using DMS I hooked into my Atrox and took a look at what happened when I pressed the various buttons (including the ones that didn't work). Sure enough, the controller sent signals as it should. For all the buttons. And it did so even though DMS is not a UWP / WinRT application. My interest was piqued. Clearly the issue was not the device or its communications with Windows. The problem of the stick not being exposed as an XInput device could be due to a bug in the generic MS driver for XBO peripherals on Windows, or maybe just an oversight. Who knows, maybe even intentional, who's to say. However, it was still really strange the using the old drivers or on Linux that only 6 of the 8 face buttons worked. What could be going on there?
After looking at the data harvested by DMS though, I was pretty sure there must be some way to work around this. It wasn't as if the data sent by the controller was especially complex. This lead to search for virtual controller tools and in turn led me to VigEm. I joined the Discord and began to make inquiries.
@evilC suggested I take a look at the
Windows.Gaming.InputAPIs and with some help with @Sylveon I was able to quickly throw together a modified version of the VDX sample application that was able to interact with the Atrox using the
Windows.Gaming.Input.ArcadeStickAPI. It worked quite nicely, except for one problem. The
Windows.Gaming.Inputfamily of APIs have a rather specific limitation which is that they only work when the application using them is running in the foreground. This the plan of a modified version that would use
Windows.Gaming.Inputto feed input to a VigEm controller was sadly doomed to fail.
Back to the drawing board...
Windows.Gaming.Inputroute wasn't going to work and stepped back and started looking at other options. @megadrago88 suggested investigate interacting with the device via HID. Initially this looked promising but after obtaining the HID descriptor it seems like that avenue is also a dead-end as the device is vendor specific and has no HID descriptor.
Meanwhile, I had been collecting more USB packet data using DMS and also reading the source for the
xpaddriver and the documentation it referenced with regards to the XBO controller data structures:
- Data I've collected so far using DMS: https://www.dropbox.com/s/madwt7md2m1i57c/RazerAtroxUSBPacketsReference.md?dl=0
- Reverse-engineered XBO controller protocol: https://github.com/quantus/xbox-one-controller-protocol
Comparing these, I noticed something:
- According to the XBO protocol docks, an XBO controller sends 18 bytes of data for a button data message
- DMS recorded information from my Atrox reflected a 30 byte message
- According to the XBO protocol docks, bytes 7 through 10 are used to send through the LT and RT
- However, with the DMS recorded information from my Atrox, these bytes were unused
- Furthermore, and even more interesting, the 23rd byte in each button press packet contain data that mapped through to the buttons pressed, including the LT and RT
Based on this my hypothesis is as follows:
- XBO Arcade Sticks do actually behave differently from standard XBO controllers
- One key difference is the location of the data they send for the LT and RT presses
- As a result, the
xpaddriver does not handle the LT and RT presses because it looks for them in the wrong location in the data
- I suspect the reason the beta XBO drivers for Windows work in the same way is that they too treat the device as a standard XBO controller and thus look for LT/RT presses at the wrong location
The road forward...
Now that my preliminary investigations are done I am planning the following dependent tasks:
- Test my hypothesis about the arcade stick data by modifying and testing
- Ask users from https://www.reddit.com/r/Fighters to participate in some data collection using DMS to confirm/deny whether other XBO Arcade Sticks send data similar or identical to that of the Atrox
- If the
xpadtest is success, I will use ZaDig in conjunction with a VigEm client application in order to expose the Atrox (or maybe even ALL XBO Arcade Sticks) via VigEm as a XB360 controller with all its buttons working as they should
And that's the long story. I'll update this thread with new details as I go.
A quick status update for today:
Started fiddling with
xpadsource but despite making changes, getting them to compile and installing the updated driver using DKMS I'm not sure the modified driver is being used. This is because the
xpaddriver is currently bundled in with the Linux kernel and thus I'm not 100% sure the modified version I compiled is actually being used. Regardless, it did reveal the general behaviour of the Atrox when the system it is connected to does not recognise it: The two lights on the device cycle on for a second or two, the off again, then on again and so on and so forth.
dmesgindicates the device is continually connecting and disconnecting. This is probably due to XBO controllers expecting and init packet which is obviously not being sent.
I took a brief look at a Python library called WinUsbPy. Unfortunately this library doesn't seem to work, although I could just be using it wrong. It seems to abandoned anyway though.
Finally, I decided to bite the bullet and throw ZaDig into the mix so I could experiment with the VS2017 WinUSB template project. After some initial confusion I worked out that it was necessary to tell ZaDig to extract rather than install the WinUSB driver for my device in order to allow me to note the Device GUID inside the INF file it generated. With the Device GUID in hand I was able to compile the WinUSB template and run the resultant executable and observe it managing to make an initial connection with the device.
The next step, I think, is going to be to try and send the init packet to the device so that it doesn't disconnect itself
Another quick status update for today:
Tried fiddling with
xpada little further, no luck, back to WinUSB I guess.
Now...WinUSB is proving to be much more successful! I tweaked the example WinUSB application a little more today and achieved two useful things:
- Successfully queried the device for end-points and was relieved to discover that the two end-points I collected in my USB packet sniffing with DMS were the exact same and only end-points reported by the device. Nothing hiding it seems
- Was able to successfully send the Controller Init/Keepalive packet. This is a small 5-byte packet. Interestingly, according to the
xpadsource these 5 bytes should be
0x05 0x20 0x00 0x01 0x00but based on the packet sniffing for the Atrox what I should be sending is
0x05 0x20 0x08 0x01 0x05. Maybe another difference between the Atrox (and maybe all XBO arcade sticks?) and normal XBO controllers? Time will tell...anyways, I was able to send the latter packet version to the device and keep it alive and connected for a minute (at which point the for-execute-sleep loop in my code ended) which is great!
Overall, I'm really pleased with the quick progress I've made with WinUSB. Many thanks to @nefarius for suggesting the ZaDig + WinUSB route, it's proving very fruitful!
Didn't achieve too much tonight, did some investigating around the speed of the Atrox. It seems it's a full-speed device with a polling interval of 4ms for each of the end-points.
In theory this means requests to either end-point are guaranteed to complete in 4ms or less, according to USB spec anyway.
Some testing tonight that produced good results.
The main thing I tested tonight was actually receiving button input. This went quite well. I was able to receive button press data and additionally confirmed once again the 23rd packet being used to send the LT and RT buttons. There were one or two interesting things noticed during the input test:
- After sending the
initpacket the very next step should be to make a read request against the input end-point. Failure to do so will result in the Atrox disconnecting unless a steady stream of
initpackets are sent.
- After sending the
initpacket and requesting button press data an immediate response seems to be returned of the
30byte packet type. Since this happened pretty much the second I ran the test program and had no buttons held this packet obviously indicate no buttons pressed
- The next packet sent by the device is a bit odd, sitting at only
2bytes long, with byte
0x0. This is not mentioned in any of the documentation on XBO controllers and I didn't observe a packet of this length when capturing previously using the normal drivers.
- The next two packets were another
2byte packet, then I received an
8byte packet which is most likely the
heartbeatpacket I recorded previously (although I should confirm this)
- It's possible then that this models the initial handshake of the device. In other words: Send
initpacket, then read data and wait for
30byte packet and then another
2byte packet. I should probably do some more testing, maybe with a button held down before starting the test application. I could well be making an assumption here
- Once the handshake (if that is what it is) has been performed the device will stay connected and send heartbeat packets pretty regularly if not input is submitted.
I think after doing a little more investigation around the
2byte packets I should be able to start integrating with some code that actually forwards the data on to a VigEm controller
- After sending the
Rather slow week on my end. Been super busy with work and also trying hard to get over a nasty cold that has the whole family feeling low.
Still, some progress achieved. Rather than forging ahead with the VigEm integration I decided to clean up the WinUSB sample application and turn it into a more general-purpose application than can be used to play with the Atrox.
Good thing I decided to do so, since it helped me figure out a few issues that would probably get in the way later when I start integrating with VigEm. Once the application is working fully I'll post a link to the GitHub repo and release binaries, etc then move on to phase two, integrating with VigEm.
Test application done!
Next step, integrating with VigEm
Quick second update for the day. I've spun of a new project that will provide the first, simple integration between the Razer Atrox, WinUSB and VigEm.
The application will be quite basic (I.e. Another terminal application using pdcurses) with the aim being to achieve a stable, performant integration between the components mentioned in order to allow for testing with actual games and other applications to be pursued.
Assuming this project goes well I can then look at a more user-friendly implementation