USB Host Mode on HTC Dream

First things first, figuring out what Android phone to use. I had a MyTouch 3G (HTC Magic) that I had disassembled to replace the digitizer, but I ended up breaking the volume button, preventing me from booting the phone in recovery mode - no jailbreaking.


There was also a G1 (HTC Dream) that I had, but it had no battery and some issue where either the battery couldn't hold a charge or the phone couldn't hold the charge. So I ordered a new battery ($10 for a decent knockoff).
First, jailbreak the phone. I'm going to be using the CyanogenMod 6 jailbreak
If you experience any issues with the phone freezing during any part of the process, try re-formatting the SD card and place all the files on the root of the SD card.


After having to reformat the SD card once, the rest of the jailbreak went smooth. After flashing the new ROM onto the phone, the first boot took awhile. ~ 5 minutes. Then as soon as I used the phone, I got a huge amount of notifications of apps crashing and I had to force close them all except for one which kept displaying a pop up to force close com.android.provisions. Boot the phone in recovery mode (Home + Power) and re-flash the ROM.


Now for the compiling the kernel. I followed the instructions here. First things first, you have to have the proper working environment so that everything else will run smoothly. Here's a couple of things you might need: Android SDK(needed for adb and other tools for android), Android NDK(for cross compiler), MacPorts (if developing on OSX). Android uses a case sensitive file system.


At the time of this writing, I used the most recent version of the NDK, r6b.


So what I did was create a Mac image, a .dmg, per the instructions here to create a case sensitive image with a max size of 12GB. You could just mount this image and do all the work in /Volumes/android

# hdiutil create -type SPARSE -fs 'Case-sensitive Journaled HFS+' -size 12g ~/android.dmg

DO ^^^^^ THIS BEFORE getting the kernel. I did it AFTER, and then tried copying it over once I ran into compilation problems. No good. This kernel has to be unzipped on a case sensitive file system in order for it to work. Otherwise, it'll lead to compilation problems such as mine where I received:
make[3]: *** No rule to make target 'net/ipv4/netfilter/ipt_ecn.o', needed by 'net/ipv4/netfilter/built-in.o'. Stop.


The file it actually needed was ipt_ECN.o, but the filename was overridden to ipt_ecn.o. Argh......


Next, check out the kernel from git. 

mkdir ~/cm-kernel-usbhost/
cd ~/cm-kernel-usbhost/
git clone git://github.com/CyanogenMod/cm-kernel.git
cd cm-kernel 
git branch -r
git checkout --track -b cm-2.6.35 origin/android-msm-2.6.35


Check the kernel version on your phone by going to Settings -> About Phone -> Kernel version. Check the output from git branch -r and select the same kernel version that appears on your phone. The output for me showed:

xx:cm-kernel xx$ git branch -r
  origin/HEAD -&> origin/android-msm-2.6.37
  origin/android-2.6.29-donut
  origin/android-2.6.29-eclair
  origin/android-msm-2.6.33
  origin/android-msm-2.6.33-ds
  origin/android-msm-2.6.34
  origin/android-msm-2.6.35
  origin/android-msm-2.6.35-froyo-stable
  origin/android-msm-2.6.35-unified
  origin/android-msm-2.6.37
  origin/supersonic-2.6.34



My kernel version was 2.6.35.9-cyanogenmod so I selected origin/android-msm-2.6.35 when using git checkout.


Download this file, its a patch for enabling USB host mode. Place the file in ~/cm-kernel-usbhost/cm-kernel/ and then: 
patch -p1 < android-kernel_msm-bca5320_Nexus-One_usb-host.patch


Next, fix a line in one of the source files. (This specific line is only for the HTC Hero). Open ~/cm-kernel-usbhost/cm-kernel/ drivers/usb/host/ehci-msm7201.c and delete line 313
Get the config file and place it in the kernel directory. ~/cm-kernel-usbhost/cm-kernel/. Then rename it to .config
mv G1-Usb-Host.config.txt .config

If you want to tinker around with the settings in the config (do it if you know what you're doing, but if you mess around with too many things in here, you'll run into problems compiling),
make ARCH=arm menuconfig


Export a path for the cross compiler.
export CCOMPILER=/path_to_android-ndk-r6b/toolchains/arm-linux-androideabi-4.4.3/prebuilt/darwin-x86/bin/arm-linux-androideabi-


Leave the dash at the end of the path. When you run make to build the kernel, it'll automatically fill in the end of the path for different utilities such as arm-linux-androideabi-gcc, arm-linux-androideabi-g++, etc. Run make:
make ARCH=arm CROSS_COMPILE=$CCOMPILER


If you get an error, "gcc: error: execvp not found", double check that the path to the cross compiler is correct. Also check that you have proper permissions to those folders. 


If you get an error, "elf.h: no such file or directory", use Mac Ports to resolve this conflict.

sudo port install libelf
sudo ln -s /opt/local/include/libelf /usr/include/libelf
copy elf.h to /usr/include
sudo port install gsed
create malloc.h in /usr/include to simply contain #include<sys/malloc.h>

Here is elf.h. If this link happens to go down, (its from a google code discussion post here), just google for elf.h or libelf and download the header. 


This should be enough to get you compiling the kernel. Now for issues I ran into while compiling this kernel to get USB Host Mode working. Compiling the kernel is to allow you to use USB Host Mode on the phone. You now have to actually load the modules. The necessary modules for using usb host mode are
drivers/usb/core/usbcore.ko
drivers/usb/host/ehci-hcd.ko
drivers/hid/usbhid/usbhid.ko



This will let you plug in a keyboard, mice, and other HID devices. For my purposes, I need serial communication over USB so I needed drivers/usb/serial/usbserial.ko


Easy alternative to avoid some of these issues when compiling modules specifically for the G1 / HTC Dream is to use Android NDK r4b. I didn't think it'd be necessary to use an old version when there may be better things in a new version, so I never reverted.

All this is using Android NDK r6b.
To set up $CC, as an environment variable for the cross compiler, do:
export CC=/path-to-ndk/toolchains/arm-linux-androideabi-4.4.3/prebuilt/darwin-x86/bin/arm-linux-androideabi-

FIRST AND FOREMOST:
when loading modules on the phone, modules need to be inserted in order.

cd sdcard/
insmod usbcore.ko
insmod ehci-hcd.ko
insmod {whatever else}


usbcore.ko enables any features you set up in the kernel config for usb related stuff.
ehci-hcd.ko enables the actual host mode on the phone and will allow you to connect stuff. If you only load usbcore.ko and ehci-hcd.ko, it's essentially useless as the phone will detect devices, but no other drivers exist on the phone to deal with them. Thats what other modules handle.

Next Issue:
When doing this BEFORE loading usbcore.ko:
insmod ehci-hcd.ko


It will say insmod error File Not Found or Exec Format Error. This means something went wrong while compiling the module, obviously the file exists, but something else is at fault.
do dmesg to find the output from the error from insmod. Check the output at the last few lines to find what is really causing the issue. If the output says a bunch of these repeated:
ehci_hcd: Unknown symbol xxxxxx


You forgot to load usbcore.ko :: insmod usbcore.ko
If the output says:
ehci-hcd: Unknown symbol _GLOBAL_OFFSET_TABLE
You're using NDK r6b, do:
make ARCH=arm CROSS_COMPILE=$CC EXTRA_CFLAGS=-fno-pic modules

To create modules that don't give that error. Use the EXTRA_CFLAGS=-fno-pic to get rid of it.


NOTE: when compiling all modules, one of the wireless lan modules threw compilation errors and wouldn't let all the modules compile. To compile individual modules, find the folder where the code is stored, e.g. for usbcore
make ARCH=arm CROSS_COMPILE=$CC EXTRA_CFLAGS=-fno-pic M=drivers/usb/core/


e.g. ehci-hcd
make ARCH=arm CROSS_COMPILE=$CC EXTRA_CFLAGS=-fno-pic M=drivers/usb/host/


e.g. usbhid
make ARCH=arm CROSS_COMPILE=$CC EXTRA_CFLAGS=-fno-pic M=drivers/hid/usb/


Next Issue:
net/built-in.o: In function `wiphy_rfkill_start_polling': activity_stats.c:(.text+0xd1be4): undefined reference to `rfkill_resume_polling'

If you get this error while either compiling modules or compiling your kernel, you forgot to enable CONFIG_RFKILL_PM in the config. Search .config for CONFIG_RFKILL_PM and change to CONFIG_RFKILL_PM=y
Next Issue: 
I used my own config file when compiling the kernel. When doing insmod and you get any error, do dmesg and check the output.
Here's the solution for this output from dmesg:
usbserial: version magic '2.6.34.5 mod_unload ARMv5 ' should be '2.6.34.5-cyanogenmod preempt mod_unload ARMv6'
Solution: This damn version magic number is so damn annoying to get rid of. First off, check the Makefile in cm-kernel/. The top of the Makefile has version numbers, make sure the last extra version field is only e.g. ".5". Nothing else in that field.
Second, do:
make ARCH=arm CROSS_COMPILE=$CC menuconfig
Go to General Setup -> Local Version : Should say -cyanogenmod
That should take care of the difference in the strings.
Next Issue: '... ARMv5' should '...ARMv6' from above version magic string.
Solution: If you got this part, you were compiling the modules for the wrong system. Something got messed up in the config file when you were setting it up. do
make ARCH=arm CROSS_COMPILE=$CC menuconfig
NOTE: If you do make without specifying the ARCH parameter, it'll default to something else and compile for a different architecture. Make sure to specify ARCH=arm even if you're just modifying the config file. Go to System Type and make sure the processor is set up to be the one for the Dream. The Dream uses the Qualcomm 7201 chipset. Make sure that information is correct under System Type.
After fixing this, I got another one!
Next Issue from dmesg: ehci_hcd: version magic '2.6.34.5-cyanogenmod-g4bff154-dirty ARMv6' should be '2.6.34.5-cyanogenmod preempt mod_unload ARMv6'
recompile kernel and the compile the modules and see if that string still remains there. Recompiling the kernel got rid of it for me.
Next Issue:
When compiling ehci-hcd module with EXTRA_CFLAGS=-fno-pic, I got compilation error: ehci_debug_root undefined
Solution: open up ehci-hcd.c, search for ehci_debug_root and comment out all the lines containing ehci_debug_root . Its only there for debugging purposes and DEBUG is not defined in the file, so it's safe. (I think, let me know if I'm wrong)
Alternate Solution: Use the Android NDK r4b. I couldn't find it and didn't feel like searching for it for a half an hour so I just uncommented it. Voila.

1 comment:

  1. Sweet! Excellent write up and helped me in a few parts.

    I have been struggling to get my HTC Desire talking over USB with an Arduino.

    Just managed some success and was able to read usb serial output using "cat /dev/ttyACM0" on the device but no luck getting android applications communicating with arduino. Not entirely sure where to go next.

    My steps: http://dtbaker.net/random-bits/android-ics-on-htc-desire-with-usb-host-mode/

    Any ideas why I can communicate over /dev/ttyACM0 but not via an arduino application?

    Cheers!
    Dave

    ReplyDelete