Friday, 2 September 2011

Custom USB devices on an Android phone

I have a Samsung Galaxy S2 which is a lovely phone that I've had plenty of fun customising thanks to its unlocked bootloader and open-source kernel. One feature of the phone that is of particular interest is the USB On-The-Go (OTG) support which allows it to switch between slave and host modes, meaning that with the right adapter you can plug a mouse, keyboard or even memory stick straight into the phone and it will handle it (as long as the power requirements for the peripheral are very low).

My work involves dealing with a custom USB hardware device and so I was very keen to find out whether or not I could talk to the hardware via my phone.

Powering the device
The first step was to provide external power since the SGS2 would be incapable of supplying enough to run the device. This was simply a case of taking an old USB cable and attaching the +5V and ground wires directly to the supply pins of the device so that I can power it via a computer or phone charger (a final product could involve some form of battery instead).

Debugging the phone via adb
I also needed a way to access the logs and shell on the phone which would normally be achieved using the USB cable and adb, but since the port was going to be used for the OTG adapter this was not an option. Instead I found that it is possible to connect to adb over TCP/IP, free widgets can be found in the market that will enable it using a custom port number (5555 is the default) and then you can connect to the device using:

adb connect ip.address.of.phone

Initial testing
Having already confirmed that my OTG host adapter was working by testing a mouse and memory stick, I then started experimenting with plugging our device into the phone. I had noticed that when supported peripherals were plugged in it was logged in the standard logcat (via adb) but nothing appeared for our custom hardware. Therefore I started to dig a bit deeper and monitored the dmesg output only to find that it was noticing the device but then rejecting it and reporting that "device vXXX pYYY is not supported" where XXX was the vendor ID and YYY was the product ID.

It was time to start digging through the kernel sources (initially pulled from the repository set up by supercurio), which Samsung have very sensibly open-sourced, looking for the log message above. I tracked it down to some header files in "drivers/usb/core" that were named:
  • otg_whitelist.h
  • s3cotg_whitelist.h
  • sec_whitelist.h
So it would seem that Samsung/Android have set up whitelists of devices that are allowed to connect (primarily mass storage and HIDs), how annoying!

Building a kernel
The next big challenge was to figure out how to build my own Android kernel. It proved quite tricky finding all of the relevant information although a post by codeworkx helped a lot. At the time of writing this post the kernel sources repo was unfortunately out of date and so I switched to the one listed in the post. I also pulled the Android sources for the build toolchain and began building.

A number of soft-bricks later I managed to get a self-built kernel running successfully on my phone (including an initramfs in the kernel build is an easy thing to miss but oh so important). It was scary to test but very satisfying once working. Thank you Samsung for your wonderful download mode without which I probably would have completely bricked my phone! Thank you also to Benjamin Dobell for his Heimdall application which now fully supports the SGS2 and provides us linux users with a method of flashing our phones in download mode.

Hacking a kernel
Now I could really get down to business and started hacking those header files I found earlier. I chose the obvious one (s3cotg_whitelist.h) since it seems to refer to the Samsung "Systems-on-Chip" and added the following to the whitelist table:

{ USB_DEVICE_INFO(0xff, 0x0, 0x0) },   /* vendor specific USB devices */

After rebuilding the kernel and flashing it I still found that the custom device was being rejected. It seems that the important file is actually "sec_whitelist.h" in which there are two tables, so I added my little hack to both, rebuilt, flashed....success! The device was accepted and the connection was even visible in logcat.

Since then I have also discovered that whitelisting is a kernel option. I have not tested it but would assume that if you edit the kernel ".config" file and set "CONFIG_USB_SEC_WHITELIST=n" then it would allow any type of USB device to connect.

Writing an application
Now that the device can connect to the phone it is time to start developing an application to communicate with it. The Android SDK provides a number of classes that give direct access to USB devices and if you have experience with libraries such as libusb then it will seem fairly familiar.

One thing to note is that the UsbDevice and related classes are part of Android 3.1 and so an application needs to be built against API level 12 but to run it on the SGS2 you can get away with setting the minSdkVersion to 10 in the manifest (this will generate a warning but it's all good).