Skip to main content

Jesse Vincent

Froyo for nookcolor

16 min read




If you've read my blog for a while, you've probably seen my unfortunate interactions with Barnes and Noble trying to buy a nook last Christmas. You've also probably seen some of my Kindle hacking and Android development work.

The big takeaway from last Christmas was "don't by a nook"

The nookcolor is B&N's latest - a 7 inch eReader.

Calling it an eReader is somewhat disingenuous. It's somewhat different from the original nook:

  • No 3G
  • No E Ink screen
  • 50% heavier than the original nook

Like the original nook, it's running Android. It has a microSD slot. And it's been rooted. (Not by me.)

I've been on the road for work a lot lately. That usually means I have rather a lot of time to kill in the evenings. Last Wednesday night, I found myself in a Barnes & Noble store in Northern Virginia. I wanted to check out the new nookcolor, but, well, I wasn't about to give Barnes & Noble any more of my money.

I walked in the front door to find one of the booksellers standing in front of the nook kisok messing around with the nookcolor. I asked if I could play with it for a moment.

As I popped into the Setttings menu, she started to tell me about the device. I'm...not quite sure how it happened, but some of the first words out of her mouth were "It's been rooted, you know."

I guess I look the part.

I played with the device for a while. It felt reasonably solid and was impressively speedy. The bookseller (I've forgotten her name :/) started talking about how much she was looking forward to a community port of Froyo.

My resolve began to weaken, but I knew that I knew nothing about the internals of the device and I wasn't about to buy another Android tablet with an anemic CPU. (I bought a ZT-180 from China and dropped my PixelQi display into it, just to see if it would work. I bought a Viewsonic G Tablet a couple weeks ago with the same intent, but haven't fully disassembled it to get to the display yet.)

I played my get out of jail free card: "So, uh, I suspect that this isn't the typical sort of question you get about the nookcolor, but how fast is the CPU?"

"It's 800MHz."

I bought one. And sat down in the B&N cafe. I used their free wifi to download nooter from nookdevs.

15 minutes later, I had ADW.Launcher, Angry Birds and K-9 Mail installed. Angry Birds was remarkably smooth.

Like everyone else, I spent some time messing around on the device, which identified itself as a "LogicPD zoom2" internally.

"Wow. This is a pretty standard Android device. I wonder how hard a vanilla Android build would be."

Flash forward to Friday.

B&N finally shipped bootloader and kernel source. The source distribution was a zip file containing "Documents and Settings/awu/Desktop/distro.tar"

The content inside the tarball left a bit to be desired. I haven't yet found enough markers to figure out what kernel tree and tag was used as the branchpoint, so I don't know precisely what the local changes are.

I have made reasonable headway on figuring that out, though.

I started reading up on the relevant bits.

Meanwhile, this YouTube video was NOT posted by me:

Over the past couple days, with help from [mbm] on (who did all this before I did ;), I've cobbled together a basic working Froyo userland.

Below is a first pass braindump of what I've done. I'm 100% sure I'm missing steps - and what I have running so far is hardly something I'd want to run full time on my device.

Per usual, what you're doing could turn your device into an expensive paperweight. (It's too flat to make a decent doorstop.)

As nooter (the nookcolor rooting tool) showed us, the nookcolor can boot from SD. In fact, it's set to prefer to boot from SD. This makes development rather less painful than it might otherwise be.

I initially started building a full cleanroom filesystem, but [mbm] convinced me that it's a bit easier to start off with the working nookcolor filesystem image and customize from there. Long term, this is obviously not the right plan.

Root your nookcolor (nc) with nooter.

Download a full filesystem image from the nc. There are a few ways to do this. The fastest is to dd to a local filesystem on the flash card. and mount that on your workstation. The most trivial is:

adb pull /dev/block/mmcblk0 ./nook-8-gig-raw-filesystem

You'll need a MicroSD card of at least 8 gigs to drop this filesystem onto. This will (sort of obviously) obliterate every single bit on that MicroSD.

dd  /dev/sdX

When you pop the MicroSD out and re-insert it, you'll likely see a whole bunch of partitions automount.

Be careful not to distribute this SD image, as the factory partition contains a zip file including a full (proprietary) OS image and the rom partition includes personal device details.

If you stick this into your nc, it should boot to the regular nook image using the boot partition on the MicroSD card.

Right now, the one you REALLY care about is "boot" (On ubuntu, it'll mount as /media/boot). It's a VFAT filesystem on partition 1.

For now, what we care about on that filesystem is uRamdisk, the boot ramdisk. As of this moment, it's set up to mount the nook's internal flash and continue boot from there.

Make a backup of uRamdisk.

uRamdisk is a u-boot filesystem.

You need to unpack it, twiddle some bits and repack it. I have some tools for this, which I'll publish to github as soon as I untangle them from the rest of the nook image in my local repo (Which contains B&N code I can't distribute because it's not obviously under an opensource license).

There's likely a correct way to do this, but the quick-and-dirty way was a bit easier for me to sort out. u-boot ramdisks are gzipped cpio archives with a 64 byte header.

My "unpack" script looks like this:

mkdir work-$$
dd if=$IMAGE of=/tmp/image.cpio.gz bs=64 skip=1
zcat /tmp/image.cpio.gz > work-$$.cpio
cd work-$$
cpio -i -F ../work-$$.cpio
echo "Unpacked ramdisk is in work-$$"

The two files you really care about right now are env.txt and init.rc

Right now, all we care about is twiddling pointers to partitions.

env.txt should look like this:

bootargs=console=ttyS0,115200n8 androidboot.console=ttyS0 root=/dev/mmcblk1p2 rw rootdelay=1 mem=512M init=/init videoout=omap24xxvout omap_vout.video1_numbuffers=6 omap_vout.vid1_static_vrfb_alloc=y omapfb.vram=0:8M
bootcmd=mmc 0 read 0x800 0x81c00000 ${kernel_size}; bootm 81c00000
recovery=echo recovery mode

I've altered the bootcmd's pointer from "mmc 1" to "mmc 0" - this is very much cargo-culting based on the TI docs and not something I've looked at.

Similarly, I've flipped the "root" argument of "bootargs" from mmcblk0p2 to mmcblk1p2. It shouldn't really matter.

EDIT: Turns out this isn't strictly necessary

In init.rc, anywhere the mmcblk0 is mentioned, make it mmcblk1

I also forced adb to always start, just for ease of development.

Now you'll want to repack the ramdisk. My "repack" script looks like this:

DIR="$( basename `pwd`)"
cpio -i -t -F ../${DIR}.cpio | cpio -o -H newc -O ../ramdisk-repacked-${DIR}.cpio
cd ..
gzip ramdisk-repacked-${DIR}.cpio
./mkimage  -A ARM -O Linux -T RAMDisk -C gzip -n Image -d ramdisk-repacked-${DIR}.cpio.gz uRamdisk-${DIR}

mkimage is a standard tool from the u-boot tools directory shipped as part of the B&N nookcolor sourcedrop. I'd assume that the mkimage from any copy of u-boot would be fine.

Once you repack the ramdisk, put it back on the boot partition as uRamdisk.

Umount all the filesystems on the MicroSD, eject the MicroSD and drop it in your nc.

Boot your nc. It should boot normally.

Once it's booted, use adb shell to check that filesystems are mounted from mmcblk1 and not mmcblk0

You can now shutdown your nook.

You have a full OS image on SD.

It's now time for the interesting bit, building Android.

I built from TI's omapzoom branch of Android - They're the folks who make the innermost parts of the nc. should tell you everything you need to know about using repo to clone the repositories, check out froyo and build.

You'll need a crosscompiler. TI recommend:

Follow the instructions for zoom2

Once the build is done, put your MicroSD card back in the nc. This time, you're looking for the system partition (sdX5).

You have two options. You can either wipe out B&N's Android build entirely and install the pristine zoom2 build or you can overlay the zoom2 build on top of B&N's build of Eclair.

I went for the former, though [mbm] had better luck with the latter, as it means you won't have to pick out proprietary libraries and tools and install them one by one.

Obviously, if you use any of the B&N bits, you MUST not distribute your filesystem image. It appears that TI publishes just about every proprietary bit we could possibly want on their GForge instance, though I haven't actually dug in too deep just yet.

You'll want to neuter etc/vold.* on the system partition once you've installed the new image, as it tries to mount an sd card that's now... otherwise occupied.

Once you're done with this, it's time to go back to work on your uRamdisk image, since froyo wants somewhat different startup bits than eclair.

I ended up with something a bit wonky causing mount failures as I was migrating init.rc to froyo. Again, this is courtesy of [mbm]:

	--- ../work-20413/init.rc   2010-12-05 11:22:40.514818375 -0500
+++ init.rc 2010-12-04 21:34:36.209385337 -0500
@@ -1,4 +1,3 @@
-# init.rc used on Encore hardware
on early-init
# Give the kernel time to enumerate the internal and external MMC/SD cards
@@ -16,7 +15,8 @@
export ANDROID_ROOT /system
export ANDROID_ASSETS /system/app
export ANDROID_DATA /data
-    export EXTERNAL_STORAGE /sdcard
+    export EXTERNAL_STORAGE /mnt/sdcard
+    export ASEC_MOUNTPOINT /mnt/asec
export INTERNAL_STORAGE /media
export BOOTCLASSPATH /system/framework/core.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/android.policy.jar:/system/framework/services.jar
export DSP_PATH /system/lib/dsp
@@ -37,16 +37,39 @@
# Backward compatibility
symlink /system/etc /etc
-# create mountpoints and mount tmpfs on sqlite_stmt_journals
+# create mountpoints
+    mkdir /mnt 0775 root system
+    mkdir /mnt/sdcard 0000 system system
+# Create cgroup mount point for cpu accounting
+    mkdir /acct
+    mount cgroup none /acct cpuacct
+    mkdir /acct/uid
+# Backwards Compat - XXX: Going away in G*
+    symlink /mnt/sdcard /sdcard
mkdir /system
-    mkdir /tmp 0777
mkdir /data 0771 system system
mkdir /cache 0770 system cache
mkdir /media 0777 system system
-    mkdir /sdcard 0777 system system
-    mkdir /sqlite_stmt_journals 01777 root root
mkdir /rom 0777 root root
-    mount tmpfs tmpfs /sqlite_stmt_journals size=4m
+    mkdir /config 0500 root root
+    # Directory for putting things only root should see.
+    mkdir /mnt/secure 0700 root root
+    # Directory for staging bindmounts
+    mkdir /mnt/secure/staging 0700 root root
+    # Directory-target for where the secure container
+    # imagefile directory will be bind-mounted
+    mkdir /mnt/secure/asec  0700 root root
+    # Secure container public mount points.
+    mkdir /mnt/asec  0700 root system
+    mount tmpfs tmpfs /mnt/asec mode=0755,gid=1000
mount rootfs rootfs / ro remount
@@ -55,6 +78,46 @@
write /proc/cpu/alignment 4
write /proc/sys/kernel/sched_latency_ns 10000000
write /proc/sys/kernel/sched_wakeup_granularity_ns 2000000
+    write /proc/sys/kernel/sched_compat_yield 1
+    write /proc/sys/kernel/sched_child_runs_first 0
+# Create cgroup mount points for process groups
+    mkdir /dev/cpuctl
+    mount cgroup none /dev/cpuctl cpu
+    chown system system /dev/cpuctl
+    chown system system /dev/cpuctl/tasks
+    chmod 0777 /dev/cpuctl/tasks
+    write /dev/cpuctl/cpu.shares 1024
+    mkdir /dev/cpuctl/fg_boost
+    chown system system /dev/cpuctl/fg_boost/tasks
+    chmod 0777 /dev/cpuctl/fg_boost/tasks
+    write /dev/cpuctl/fg_boost/cpu.shares 1024
+    mkdir /dev/cpuctl/bg_non_interactive
+    chown system system /dev/cpuctl/bg_non_interactive/tasks
+    chmod 0777 /dev/cpuctl/bg_non_interactive/tasks
+    # 5.0 %
+    write /dev/cpuctl/bg_non_interactive/cpu.shares 52
+    # Create dump dir and collect dumps.
+    # Do this before we mount cache so eventually we can use cache for
+    # storing dumps on platforms which do not have a dedicated dump partition.
+    mkdir /data/dontpanic
+    chown root log /data/dontpanic
+    chmod 0750 /data/dontpanic
+    # Collect apanic data, free resources and re-arm trigger
+    copy /proc/apanic_console /data/dontpanic/apanic_console
+    chown root log /data/dontpanic/apanic_console
+    chmod 0640 /data/dontpanic/apanic_console
+    copy /proc/apanic_threads /data/dontpanic/apanic_threads
+    chown root log /data/dontpanic/apanic_threads
+    chmod 0640 /data/dontpanic/apanic_threads
+    write /proc/apanic_console 1
# mount MMC partitions
mount vfat /dev/block/mmcblk1p2 /rom sync noatime nodiratime uid=1000,gid=1000,fmask=117,dmask=007
@@ -382,8 +445,8 @@
service vold /system/bin/vold
socket vold stream 0660 root mount
-#service mountd /system/bin/mountd
-#    socket mountd stream 0660 root mount
+service netd /system/bin/netd
+    socket netd stream 0660 root system
service debuggerd /system/bin/debuggerd
@@ -396,10 +459,13 @@
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
socket zygote stream 666
onrestart write /sys/android_power/request_state wake
+    onrestart write /sys/power/state on
+    onrestart restart media
service media /system/bin/mediaserver
user media
-    group system audio camera graphics inet net_bt net_bt_admin
+    group system audio camera graphics inet net_bt net_bt_admin net_raw
+    ioprio rt 4
service fw3a /system/bin/fw3a_core
user root
@@ -421,6 +487,37 @@
user bluetooth
group bluetooth net_bt_admin
+service bluetoothd /system/bin/bluetoothd -n
+    socket bluetooth stream 660 bluetooth bluetooth
+    socket dbus_bluetooth stream 660 bluetooth bluetooth
+    # init.rc does not yet support applying capabilities, so run as root and
+    # let bluetoothd drop uid to bluetooth with the right linux capabilities
+    group bluetooth net_bt_admin misc
+    disabled
+service hfag /system/bin/sdptool add --channel=10 HFAG
+    user bluetooth
+    group bluetooth net_bt_admin
+    disabled
+    oneshot
+service hsag /system/bin/sdptool add --channel=11 HSAG
+    user bluetooth
+    group bluetooth net_bt_admin
+    disabled
+    oneshot
+service opush /system/bin/sdptool add --channel=12 OPUSH
+    user bluetooth
+    group bluetooth net_bt_admin
+    disabled
+    oneshot
+service pbap /system/bin/sdptool add --channel=19 PBAP
+    user bluetooth
+    group bluetooth net_bt_admin
+    disabled
+    oneshot
service installd /system/bin/installd
socket installd stream 600 system system
@@ -432,8 +529,6 @@
service ifcfg_ti /system/bin/ifconfig tiwlan0 up
@@ -447,6 +542,19 @@
+service racoon /system/bin/racoon
+    socket racoon stream 600 system system
+    # racoon will setuid to vpn after getting necessary resources.
+    group net_admin
+    disabled
+    oneshot
+service mtpd /system/bin/mtpd
+    socket mtpd stream 600 system system
+    user vpn
+    group vpn net_admin net_raw
+    disabled
+    oneshot
service keystore /system/bin/keystore /data/misc/keystore
user keystore
@@ -463,5 +571,7 @@
service debuglog /system/bin/
user root
+service dumpstate /system/bin/dumpstate -s
+    socket dumpstate stream 0660 shell log
+    disabled
+    oneshot

At this point, (assuming I haven't forgotten anything), you should have a MicroSD that will boot froyo on a nookcolor.

...and then immediately shut down, ostensibly due to power issues. My sneaking suspicion is that this is because the kernel the nc is booting from is B&N's original 2.6.29 image and there's been a bit of skew in how it reads some bit of information out of /proc.

The correct next step is to go and build a proper kernel for the device.

The easy next step is to perform a brutal hackjob on android's batteryservice.


Edit: I have no reason to believe that this is actually likely. But it'd be double-plus irresponsible of me to suggest that turning off a hardware safety feature was a good idea.


	jesse@puppy ~/android-omapzoom/frameworks/base/services ((6c81cb0...) *) $ git diff
diff --git a/services/java/com/android/server/ b/services/java/com/android/server/
index 5cf61bd..bf96479 100644
--- a/services/java/com/android/server/
+++ b/services/java/com/android/server/
@@ -176,8 +176,8 @@ class BatteryService extends Binder {
void systemReady() {
// check our power situation now that it is safe to display the shutdown dialog.
-        shutdownIfNoPower();
-        shutdownIfOverTemp();
+        //shutdownIfNoPower();
+       // shutdownIfOverTemp();
private final void shutdownIfNoPower() {
@@ -210,8 +210,8 @@ class BatteryService extends Binder {
boolean logOutlier = false;
long dischargeDuration = 0;
-        shutdownIfNoPower();
-        shutdownIfOverTemp();
+      //  shutdownIfNoPower();
+        //shutdownIfOverTemp();
mBatteryLevelCritical = mBatteryLevel 

Once you've rebuilt the android core and reinstalled it on your system partition, you should now have something that boots froyo on your nook.

This froyo is VERY MUCH not ready for primetime. Don't expect WIFI, Bluetooth, Google Apps, reasonable performance, sensor access, etc.

TI distributes wifi drivers and all the hardware acceleration you could possibly want:

This all would have taken me a great deal longer without [mbm]'s assistance and advice.

My next steps will (probably) be to try to build a modern kernel from source and see what that does for driver support.

Please note that I don't plan to build a distributable ROM or to port Cyanogenmod, though I'd be thrilled to see someone pick up the torch and do so.

I can't distribute the OS image I have, as it contains a mix of free stuff and proprietary stuff pulled off of my nookcolor's system partitions.

If you're looking to chatter with folks about development for the nookcolor, I'd recommend on