Using a Stadia controller on my OUYA

The OUYA's servers were shut down in 2019, and Google shut down the servers for its Stadia game console in january 2023. I thought it would be cool to use the controller of one dead console on a resurrected dead gaming console :)

Google Stadia controller, 8BitDo adapter and OUYA developer console

Bluetooth

The Stadia controllers did connect directly to Google's internet servers via WiFi and only used Bluetooth for initial setup. After shutdown, Google provided a website that let you install a controller firmware update, finally making the controller a standard Bluetooth one.

The Stadia controller only supports Bluetooth Low Energy (BLE). The OUYA contains a AzureWave AW-NH660 chipset that is based on BCM4330, whose datasheet says it is forward-compatible with the impending Bluetooth Low Energy operating mode. Android supports BLE starting from version 4.3:

Bluetooth Low Energy (BLE), available in Android 4.3 and later, creates short connections between devices to transfer bursts of data

The OUYA runs Android 4.1, so you cannot use BLE controllers on an OUYA.

USB

The Stadia controller has a USB-C port. Connecting it to the OUYA's own USB-A port gives you a partially working controller: The d-pad, right thumb stick and L2+R2 do not work at all - only the left thumb stick works.

A custom key layout file is needed, just as I did before with a playstation controller.

Key layout file creation

Connect the controller via USB to your Android device and then run getevent -i in a terminal, e.g. via adb shell:

$ getevent -i
add device 2: /dev/input/event0
  bus:      0003
  vendor    18d1
  product   9400
  version   0111
  name:     "Google LLC Stadia Controller rev. A"
  location: "usb-tegra-ehci.2-1/input1"
  id:       "99200YCAC2LJ5L"
  version:  1.0.1
  events:
    KEY (0001): 0072  0073  00a4  0130  0131  0133  0134  0136
                0137  013a  013b  013c  013d  013e  0140  0141
                0142  0143
    ABS (0003): 0000  : value 0, min 1, max 255, fuzz 0, flat 15, resolution 0
                0001  : value 0, min 1, max 255, fuzz 0, flat 15, resolution 0
                0002  : value 0, min 1, max 255, fuzz 0, flat 15, resolution 0
                0005  : value 0, min 1, max 255, fuzz 0, flat 15, resolution 0
                0009  : value 0, min 0, max 255, fuzz 0, flat 15, resolution 0
                000a  : value 0, min 0, max 255, fuzz 0, flat 15, resolution 0
                0010  : value 0, min -1, max 1, fuzz 0, flat 0, resolution 0
                0011  : value 0, min -1, max 1, fuzz 0, flat 0, resolution 0
    MSC (0004): 0004
  input props:
    <none>

Here we see that the controller has 18 buttons (keys) and 8 axes.

Now run getevent without parameters, press the buttons and axes and make notes of their IDs:

$ getevent
[...]
/dev/input/event1: 0004 0004 00090001
/dev/input/event1: 0001 0130 00000001
/dev/input/event1: 0003 0000 0000007f
/dev/input/event1: 0003 0001 0000007f
/dev/input/event1: 0000 0000 00000000
/dev/input/event1: 0004 0004 00090001
/dev/input/event1: 0001 0130 00000000
/dev/input/event1: 0003 0000 0000007c
/dev/input/event1: 0003 0001 0000007d
/dev/input/event1: 0000 0000 00000000

Here button A was pressed and released - it has the Linux key code 0x130. Doing that for all the buttons gives us this list:

A      0x130
B      0x131
X      0x133
Y      0x134
...    0x13a
===    0x13b
L1/LB  0x136
R1/RB  0x137
L3     0x13d
R3     0x13e
Stadia 0x13c

Now do the same for the axes:

D-Pad        left-right 0010
             up-down    0011
Left stick   left-right 0000
             up-down    0001
Right stick  left-right 0002
             up-down    0005
Left trigger            000a
Right trigger           0009

And this is all the information needed to write the key layout file:

Vendor_18d1_Product_9400.kl
# Google Stadia Controller via USB
key 0x130  BUTTON_A
key 0x131  BUTTON_B
key 0x133 BUTTON_X
key 0x134 BUTTON_Y

#L1
key 0x136 BUTTON_L1
#R1
key 0x137 BUTTON_R1

#L3 Stick
key 0x13d BUTTON_THUMBL
#R3 Stick
key 0x13e BUTTON_THUMBR

#Stadia symbol
key 0x13c HOME
#===
key 0x13b MENU
#...
key 0x13a BACK

#D-pad
axis 0x10 HAT_X
axis 0x11 HAT_Y

#Left stick
axis 0x00 X
axis 0x01 Y

#Right stick
axis 0x02 Z
axis 0x05 RZ

axis 0x0a LTRIGGER
axis 0x09 RTRIGGER

Copy that file into /system/usr/keylayout/:

$ adb push Vendor_18d1_Product_9400.kl /sdcard/
$ adb shell
$ su
$ mount -o remount,rw /system
$ mv /sdcard/Vendor_18d1_Product_9400.kl /system/usr/keylayout/

Simply unplugging and replugging makes Android load the custom key layout file instead of Generic.kl, and all buttons work.

Bluetooth with 8BitDo USB Wireless Adapter 2

8BitDo sells the USB Wireless Adapter 2, which bridges Bluetooth controllers to USB ports with the added benefit of letting you customize the button mappings. It supports Bluetooth Low Energy and - since firmware v1.04 - the Stadia controller:

Added support for Google Stadia Controller. (The controller is required to update to the Bluetooth mode firmware, it does not support vibration)

The adapter can behave differently depending on its mode, which can be switched when holding ··· and the directional pad keys for 3 seconds. You will see the adapter's red LED flash.

Supported modes as of firmware 1.06:

··· + d-pad left
DInput mode (USB ID 2dc8:3105, "8BitDo 8BitDo Receiver")
··· + d-pad up
Xinput mode (USB ID 2dc8:3106, "8BitDo 8BitDo Receiver")
··· + d-pad down
PSC/PSX mode (USB ID 054c:0cda, "Sony Interactive Entertainment Controller")
··· + d-pad right
Mac mode (USB ID 054c:05c4, "8BitDo 8BitDo Receiver")
··· + LB (Left shoulder button L1)
Switch mode (USB ID 057e:2009, "Nintendo Co., Ltd. Pro Controller")

I opted to use Dinput mode (··· + d-pad left), and could use the Stadia USB key layout file without changes - I only had to give it a new name to match the USB IDs: Vendor_2dc8_Product_3105.kl!

Dive into Linux input mapping

On Android, multiple stages of input mapping take place:

  1. Controller reports button bits
  2. Linux input driver map those bits to key codes
  3. Android maps Linux key codes to Android key codes via key layout files

Controller key bits

On Linux and Android you can see the keys a controller supports by inspecting its device information:

$ cat /proc/bus/input/devices
I: Bus=0003 Vendor=2dc8 Product=3105 Version=0111
N: Name="8BitDo 8BitDo Receiver"
P: Phys=usb-tegra-ehci.2-1/input0
S: Sysfs=/devices/platform/tegra-ehci.2/usb2/2-1/2-1:1.0/input/input8
U: Uniq=E417D86076B0
H: Handlers=js0 event1
B: PROP=0
B: EV=1b
B: KEY=7fff0000 0 0 0 0 0 0 0 0 0
 
$ cat /sys/devices/platform/tegra-ehci.2/usb2/2-1/2-1\:1.0/input/input8/capabilities/key
7fff0000 0 0 0 0 0 0 0 0 0

This key bit mask value 7fff0000 in bits is:

1111111111111110000000000000000

which are 15 buttons/keys.

Linux key codes

The input driver selected by Linux then takes care of mapping the bits onto key codes. You can inspect this information in the /sys/ file system as well:

$ cat /sys/devices/platform/tegra-ehci.2/usb2/2-1/2-1\:1.0/input/input8/modalias
input:b0003v2DC8p3105e0111-e0,1,3,4,k130,131,132,133,134,135,136,137,138,139,13A,13B,13C,13D,13E,ra0,1,2,5,9,A,10,11,m4,lsfw

With the help of the Linux kernel sources input.c and input.h this can be decoded:

b0003 #bustype
v2DC8 #vendor
p3105 #product
e0111- #version
e0,1,3,4, #evbit
k130,131,132,133,134,135,136,137,138,139,13A,13B,13C,13D,13E, #keybit
r
a0,1,2,5,9,A,10,11, #relbit
m4, #mscbit
l #ledbit
s #sndbit
f #ffbit
w #swbit

The key codes that were used for the key map file above are already visible here.

Not a gamepad

During development of my key layout file I got the following message from the OUYA framework's input handler:

E/OuyaInputMapper(  548): onInputDeviceAdded device=5 not found
D/ControllerInputDeviceListener(  565): InputDevice 8BitDo 8BitDo Receiver (id: 5, desc: 9c597667366a6b3d65e77ad35e1bc1f757d19732) is not a gamepad...

It turned out that I had used Android key code values (2xx range) instead of Linux key codes (3xx/0x13x range) in the key layout file as keys. After correcting the codes to 0x130 and above, the controller was correctly detected as gamepad.

Orange light

When the Stadia controller is connected via USB to the OUYA and the Stadia button glows orange instead of white, then you have to reboot the OUYA.

This often happens when connecting the USB cable 5+ minutes after bootup.

Also on: Mastodon, Twitter.

Written by Christian Weiske.

Comments? Please send an e-mail.