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

9 comments:

  1. Hi,

    Are you still reading from xda-developpers? I posted the following private message there as well. If you could answer there?

    I am facing today a problem you solved a month ago. Therefore I wondered if you had any progress on your side: did you compile a kernel adding your USB device in sec_whitelist.h? Did you try compiling disabling that option in .config (CONFIG_USB_SEC_WHITELIST)?

    Tahnks for sharing anyway, your thread http://forum.xda-developers.com/showthread.php?p=18081882 has put an end to one week of investigations around connecting a USB device to my S2
    Also which source code did you use? Original from Samsung? Which one? (update_2 has probably no more support for USB OTG).

    ReplyDelete
  2. Hi,

    Any updates from users on your kernel modification? My big question is; does it work to get ADK accessory mode going on the SGS2?

    ReplyDelete
  3. Unfortunately I have had to abandon the application I was developing for the time being. Although it was working, the phone was not buffering the USB data fast enough so we would need to modify our hardware to function properly.

    As far as I'm aware the accessory mode is very different from what I was trying to do, since in that case the accessory itself acts as host with the phone as slave, whereas I was enabling custom devices to connect as slave with the phone as host.

    ReplyDelete
  4. Hi, I'm trying to do the same as you did on a Galaxy Tab.
    I've built a kernel I downloaded from Samsung's Open Source web site.
    Silly question: what do I do with zImage or its tar?
    Is there a way to do fastboot (i.e., as far as I understand: a boot from RAM without flashing) on Samsung devices?
    Thank you

    ReplyDelete
  5. I'm not really working on this any more, in fact I heard that the required API was removed by Samsung in a later Galaxy S2 release. I have no idea if the same is possible on the Galaxy Tab but if it uses the same kernel level whitelist then it is likely.

    If you managed to successfully build a zImage then it needs to be flashed to the phone in download mode using the appropriate tool (odin/heimdall) although not sure if they support the Tab. Be aware that it is very easy to soft-brick a device with a bad kernel though (I did it to my phone when experimenting with this modification), but it should be recoverable through download mode (simply flash back an official kernel).

    ReplyDelete
  6. Thanks Dan!!

    Yeah, I think Odin is the way to go and that involves flashing.
    I still struggle to understand the difference between kernels and roms. In the past I've put unofficial roms onto an old Huawei phone and those must have included the kernel because some drivers were not right and drivers are part of the kernel.

    The other thing that puzzles me is that zImage is just 3MB, how can it possibly be?

    ReplyDelete
  7. The size of kernel for my SGS2 was around 6MB I think, so 3MB does seem a little small. But then kernel sizes can vary massively depending on what options are enabled when building. Your best bet is to compare the size with an official kernel for that device and check that they are similar.

    ReplyDelete
  8. This comment has been removed by a blog administrator.

    ReplyDelete
  9. Hi, I luv your post. I have both a Samsung Galaxy S3 and a Nexus 7 will this work for both devices or will i need to get a separate cable for the Nexus 7?
    **********
    my e-shop

    ReplyDelete