CP210x (USB to TTL adapter) for Android devices

Few days ago, while searching for something interesting I can do with my Android phone I found out that Sony is publishing kernel source of all their (and old SE ones too) ROMs. Beside hacking the kernel itself I was wondering if it is possible to compile additional kernel modules (stock ROM provides only internal WiFi module’s drivers). Well.. it was. The first one I tried was driver of my cheap, Chinese USB to serial adapter based on CP2102 chip.

Prerequisites

  • kernel source – it is important to be the same kernel as the one working on your device. Otherwise it would probably be necessary to boot your system with kernel compiled with source you have and it will not be described in this tutorial. The reason it is important is that between kernel version compatibility is not guaranteed. Different configuration could mess with functionality too (but not tried myself). I have Sony Ericsson device so I downloaded from its official repository.
  • cross-compiler – while compiling programs for Android you definitely should get special version of a compiler because Android is different than usual Linux box so compiler options are different too. The easiest way is to download official NDK which has built-in compilers and easy-to-use script to make standalone toolchain out of them. Same toolchain should also work as compiler for kernel but I haven’t tested it myself. In case it didn’t there is very powerful tool for making your own toolchain for any platform supported by GCC so in practice any you can imagine called crosstool-ng (on Arch available on AUR).
  • uucp source code

Kernel module

We will start with a kernel module. First of all we will unpack our kernel. In case of SE kernel need to be uncompressed and then unpacked so I did:

bzcat 4_1_B_0_431_tar.bz2 | tar -xv
cd kernel

And changed dir to kernel. Next thing I had to do was patching Makefile, because it complained about unused variables. If you are using Sony or SE kernel you probably need to do it too. If so create file named Makefile.patch with your favorite editor, i.e.

vim Makefile.patch

and paste following content:

--- Makefile	2012-05-25 12:07:05.000000000 +0200
+++ Makefile.new	2014-08-20 21:16:50.642703198 +0200
@@ -342,7 +342,6 @@
 KBUILD_CPPFLAGS := -D__KERNEL__
 
 KBUILD_CFLAGS   := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
-		   -Werror \
 		   -fno-strict-aliasing -fno-common \
 		   -Werror-implicit-function-declaration \
 		   -Wno-format-security \

Now you can patch it with:

patch Makefile < Makefile.patch

Then we can start the compilation process which should end in a second. I assume you have working toolchain in your $PATH and it is prefixed with arm-unknown-eabi- (arm-unknown-eabi-gcc, etc.). If its name is different change all occurrences of arm-unknown-eabi- below.

make mrproper
ARCH=arm CROSS_COMPILE=arm-unknown-eabi- make semc_iyokan_defconfig
ARCH=arm CROSS_COMPILE=arm-unknown-eabi- make prepare
ARCH=arm CROSS_COMPILE=arm-unknown-eabi- make modules_prepare
ARCH=arm CROSS_COMPILE=arm-unknown-eabi- make modules SUBDIRS=drivers/usb/serial CONFIG_USB_SERIAL=m CONFIG_USB_SERIAL_CP210X=m

If everything worked you should have your module compiled in drivers/usb/serial directory. It is worth to note you could compile any other module the same way. It is only important to change SUBDIRS to your module location and ensure it is configured to be built by issuing make menuconfig or setting appropriate CONFIG-* variable to ‘m’.

Now you can copy modules to your devices. With adb it will be:

adb push drivers/usb/serial/usbserial.ko /mnt/sdcard/others
adb push drivers/usb/serial/cp210x.ko /mnt/sdcard/others

We have two modules to copy here since cp210x (and any other serial device driver) depends on usbserial and without it being already in kernel it would be impossible to insert cp210x and furthermore it will give you strange error message (tested :). So now, again with help of ADB, we can insert our modules.

adb shell
su
cd /mnt/sdcard/others
insmod ./usbserial.ko
insmod ./cp210x.ko

After that you can connect your device and device you compiled module for and test if it works. Serial adapters should create new device file in /dev and, at least with CP210x, it uses ttyUSB* name so you can list it with:

ls -l /dev/ttyUSB*

You can also read from your port as simply as by typing

cat /dev/ttyUSB0

Now after you connect some device talking UART you should see its output.

Program to easily control our port – cu and the rest of uucp package

Warning: in the following steps I assume that you have directories like /data/local/bin and /data/local/etc already on your device. As far as I remember there is only /data/local by default.

At first we need to unpack our source and create some useful directories. Then we will configure our script in build-uucp so we won’t get garbage in source directory and could easily clean things when something goes wrong.

tar -zxvf uucp-1.07.tar.gz
mkdir build-uucp
mkdir install-uucp
cd build-uucp/

Then we will patch our program so it will work on Android out of the box. If we omitted following patch it would be necessary to create configuration file, configure program to read it before work and whole bunch of similar fun. So it is easier to let him know where he could store his files now. You can obviously use the one you want to. If you stay with my config you won’t be able to start cu as normal user which is no problem because by default you won’t have permission to use serial terminal.

Now the procedure is the same as with previous patch. I assume you used policy.h.patch name and the content is:

--- policy.h	2003-05-29 08:08:45.000000000 +0200
+++ policy.h.new	2014-08-20 12:03:45.595405893 +0200
@@ -297,7 +297,7 @@
    systems the lock files are placed in /etc/locks.  On some they are
    placed in /usr/spool/locks.  On the NeXT they are placed in
    /usr/spool/uucp/LCK.  */
-/* #define LOCKDIR "/usr/spool/uucp" */
+#define LOCKDIR "/data/local/etc/spool/uucp"
 /* #define LOCKDIR "/etc/locks" */
 /* #define LOCKDIR "/usr/spool/locks" */
 /* #define LOCKDIR "/usr/spool/uucp/LCK" */
@@ -572,7 +572,7 @@
 /* The name of the default spool directory.  If HAVE_TAYLOR_CONFIG is
    set to 1, this may be overridden by the ``spool'' command in the
    configuration file.  */
-#define SPOOLDIR "/usr/spool/uucp"
+#define SPOOLDIR "/data/local/etc/spool/uucp"
 /* #define SPOOLDIR "/var/spool/uucp" */
 
 /* The name of the default public directory.  If HAVE_TAYLOR_CONFIG is
@@ -580,7 +580,7 @@
    configuration file.  Also, a particular system may be given a
    specific public directory by using the ``pubdir'' command in the
    system file.  */
-#define PUBDIR "/usr/spool/uucppublic"
+#define PUBDIR "/data/local/etc/spool/uucppublic"
 /* #define PUBDIR "/var/spool/uucppublic" */
 
 /* The default command path.  This is a space separated list of
@@ -644,21 +644,21 @@
 /* The default log file when using HAVE_TAYLOR_LOGGING.  When using
    HAVE_TAYLOR_CONFIG, this may be overridden by the ``logfile''
    command in the configuration file.  */
-#define LOGFILE "/usr/spool/uucp/Log"
+#define LOGFILE "/data/local/etc/spool/uucp/Log"
 /* #define LOGFILE "/var/spool/uucp/Log" */
 /* #define LOGFILE "/var/log/uucp/Log" */
 
 /* The default statistics file when using HAVE_TAYLOR_LOGGING.  When
    using HAVE_TAYLOR_CONFIG, this may be overridden by the
    ``statfile'' command in the configuration file.  */
-#define STATFILE "/usr/spool/uucp/Stats"
+#define STATFILE "/data/local/etc/spool/uucp/Stats"
 /* #define STATFILE "/var/spool/uucp/Stats" */
 /* #define STATFILE "/var/log/uucp/Stats" */
 
 /* The default debugging file when using HAVE_TAYLOR_LOGGING.  When
    using HAVE_TAYLOR_CONFIG, this may be overridden by the
    ``debugfile'' command in the configuration file.  */
-#define DEBUGFILE "/usr/spool/uucp/Debug"
+#define DEBUGFILE "/data/local/etc/spool/uucp/Debug"
 /* #define DEBUGFILE "/var/spool/uucp/Debug" */
 /* #define DEBUGFILE "/var/log/uucp/Debug" */
 
@@ -669,17 +669,17 @@
 /* The default log file when using HAVE_V2_LOGGING.  When using
    HAVE_TAYLOR_CONFIG, this may be overridden by the ``logfile''
    command in the configuration file.  */
-#define LOGFILE "/usr/spool/uucp/LOGFILE"
+#define LOGFILE "/data/local/etc/spool/uucp/LOGFILE"
 
 /* The default statistics file when using HAVE_V2_LOGGING.  When using
    HAVE_TAYLOR_CONFIG, this may be overridden by the ``statfile''
    command in the configuration file.  */
-#define STATFILE "/usr/spool/uucp/SYSLOG"
+#define STATFILE "/data/local/etc/spool/uucp/SYSLOG"
 
 /* The default debugging file when using HAVE_V2_LOGGING.  When using
    HAVE_TAYLOR_CONFIG, this may be overridden by the ``debugfile''
    command in the configuration file.  */
-#define DEBUGFILE "/usr/spool/uucp/DEBUG"
+#define DEBUGFILE "/data/local/etc/spool/uucp/DEBUG"
 
 #endif /* HAVE_V2_LOGGING */
 
@@ -692,16 +692,16 @@
    be replaced by the system name (if there is no appropriate system,
    "ANY" will be used).  No other '%' character may appear in the
    string.  */
-#define LOGFILE "/usr/spool/uucp/.Log/%s/%s"
+#define LOGFILE "/data/local/etc/spool/uucp/.Log/%s/%s"
 
 /* The default statistics file when using HAVE_HDB_LOGGING.  When using
    HAVE_TAYLOR_CONFIG, this may be overridden by the ``statfile''
    command in the configuration file.  */
-#define STATFILE "/usr/spool/uucp/.Admin/xferstats"
+#define STATFILE "/data/local/etc/spool/uucp/.Admin/xferstats"
 
 /* The default debugging file when using HAVE_HDB_LOGGING.  When using
    HAVE_TAYLOR_CONFIG, this may be overridden by the ``debugfile''
    command in the configuration file.  */
-#define DEBUGFILE "/usr/spool/uucp/.Admin/audit.local"
+#define DEBUGFILE "/data/local/etc/spool/uucp/.Admin/audit.local"
 
 #endif /* HAVE_HDB_LOGGING */

If you prefer I have it on my gist so you can just issue one command and get it. Then we are patching as usually:

patch ../uucp-1.07/policy.h < policy.h.patch

As mentioned above you can change path of uucp’s files by issuing the following (remember to escape every occurrence of slash with backslash, otherwise it will fail):

sed -i "s/\/data\/local\/etc/[your-path]/" ../uucp-1.07/policy.h

Now you are ready to compile. It can be done with following commands. Your compiler should have same name (at least if you use NDK’s compiler). It is important to note that I had to switch off HAVE_SYSCONF flag since it was causing ugly errors. In my case makescript couldn’t also find a rule to make ftw.o so I had to make it myself. If you have no trouble here, just omit the line after make.

CC=arm-linux-androideabi-gcc AR=arm-linux-androideabi-ar RANLIB=arm-linux-androideabi-ranlib \
../uucp-1.07/configure --prefix=`pwd`/../install-uucp/ --host=arm-linux-androideabi
sed -i "s/#define HAVE_SYSCONF 1/#define HAVE_SYSCONF 0/" config.h
make
cd unix; make ftw.o; cd ..; make
make install
adb push ../install-uucp/bin/cu /mnt/sdcard/others
adb shell
su
cp /mnt/sdcard/others/cu /data/local/bin/

Finally you can test the program with the following and you should be able to talk RS232 with just a phone/tablet!

cu -lttyUSB0 -s115200

where 115200 is the speed the device you connect to transmits.

BTW: uucp have few other tools and by following this tutorial you compiled them all so you can explore them on your own.

Posted in Tutorials | Tagged , , , , | Leave a comment