Port Android to NVIDIA Jetson TK1 15


What is Jetson TK1

Jetson TK1 is NVIDIA’s high performance development platform base on Tegra K1. See this wiki page for more information: http://elinux.org/Jetson_TK1.

Currently NVIDIA provide Linux packages(fastboot, u-boot, linux kernel, ubuntu, gstreamer, etc.) only, but there are products(MiPad, for example) with Android running on Tegra K1 already. In case of Jetson TK1, there is also a project called Jedroid that have ported Android to it.

I’ve ported AOSP to Jetson TK1 too, but just for fun, and to learn OS concepts from both the porting and Android. I’ll only fix bugs on my spare time, so if you are seeking for commercial support, you’d better contact NVIDIA or the Jedroid team.

Preparation

Download AOSP

If you are in China, and without VPN at hand, you might not be able to download AOSP. Although this github hosts a AOSP mirror, I had no luck to download AOSP from it. Finally I had to make use of a VPN tunnel to download. Visit the official download introduction page to start the download: http://source.android.com/source/downloading.html.

Here are some tips if you try to download AOSP in China:

  1. Use shell command like: “while [ 1 -eq 1 ]; do repo sync; done” to restart the download automatically since there will be a lot of download failure.
  2. The download speed will be very fast(almost 1MiB/s, without VPN) during the early morning(between six to nine, maybe). You’d better download via VPN since the speed can down to 5KiB/s in other time.

The steps I used to download:

mkdir aosp
cd aosp

repo init -u https://android.googlesource.com/platform/manifest -b android-4.4.4_r2.0.1

while [ 1 -eq 1 ]; do repo sync; done

# once download complete(there are visible folders under aosp folder), kill the
# session(close the terminal tab) because I have no other way to stop the infinite while loop

Download Jetson TK1 Linux Packages

Go to the official site and download the following packages(note that some packages might require you to register and login first):

  1. Driver Packages -> Jetson TK1(Tegra124_Linux_R19.3.0_armhf.tbz2). It contains the flash tool, bootloader and tegra firmware, etc.
  2. Source Packages -> Kernel sources(kernel_src.tbz2).
  3. Source Packages -> U-boot sources(optional, on if you want to use u-boot as bootload).

Install TADP

TADP is short for Tegra Android Development Pack, which contians many useful tools to help to develop on Tegra. To meet our requirement, we only need to download TADP and then select to download “Tegra K1 OS Image for Ardbeg development kits”. Here is the download page.

From the “Tegra K1 OS Image for Ardbeg development kits”, we can extract many Tegra K1 specific Android libaries and configs.

Porting

Setup Flash Tool

After we install TADP and “Tegra K1 OS Image for Ardbeg development kits”, we can find the TK1 OS images from somewhere like “NVPACK/Android_OS_Images/Ardbeg/android_k_ardbeg_os_image-19r16”.

First go to the android_k_ardbeg_os_image-19r16 directory. The following files are the key files for flashing images to the board:

  1. flashos.sh: the shell script to wrap the real flash command.
  2. nvflash(nvflash_linux/nvflash_macosx): the real flash tool that do the flashing.
  3. bct.cfg: BCT is short for Boot Configuration Table, you can get more info from here.
  4. flash.cfg: defines the partition table and which images to flash.
  5. bootloader.bin: the bootloader.

Since these files are mainly for Ardbeg, they are not necessary suitable for Jetson TK1(which has the board id of “pm375”, it is said that pm375 is a sub-kind of Ardbeg), we need to replace them for the right ones. But where to get the right files?

The answer comes from the Tegra124_Linux_R19.3.0_armhf.tbz2 package. First unpack the package:

tar xvf Tegra124_Linux_R19.3.0_armhf.tbz2
# after unpacking, there will be a directory named "Linux_for_Tegra"

Now copy and replace the files:

# current directory is android_k_ardbeg_os_image-19r16, and Linux_for_Tegra
# is extracted under current directory
cp Linux_for_Tegra/bootloader/ardbeg/BCT/PM375_Hynix_2GB_H5TC4G63AFR_RDA_924MHz.cfg bct.cfg
cp Linux_for_Tegra/bootloader/ardbeg/fastboot.bin bootloader.bin
cp Linux_for_Tegra/bootloader/nvflash nvflash_linux
cp Linux_for_Tegra/kernel/dtb/tegra124-pm375.dtb .

Then, we need to change the content of flashos.sh. The flashos.sh is pretty simple:

#!/bin/bash
SYSTYPE=$(uname -s)
if [ $SYSTYPE = Linux ]; then
    sudo ./nvflash_linux --bct bct.cfg --setbct --odmdata 0x80098000 --configfile flash.cfg --create --bl bootloader.bin --go
elif [ $SYSTYPE = Darwin ]; then
    ./nvflash_macosx --bct bct.cfg --setbct --odmdata 0x80098000 --configfile flash.cfg --create --bl bootloader.bin --go
fi

It calls the different nvflash tools according to the OS. Since we don’t have a OSX version of nvflash for Jetson TK1, we can only flash images under Linux, so we only focus on the Linux branch of code. There is nothing to change excpet the argument for “–odmdata”. ODMDATA is a field that contains boot setting data, you can find more info from the BCT page. Every type of board has it own odmdata value, the corresponding value for pm375 can be found in Linux_for_Tegra/pm375.conf.

ODMDATA=0x6009C000;
NET_BSF=ardbeg_net.hush;
EMMC_BSF=ardbeg_emmc.hush;
EMMC_BCT=PM375_Hynix_2GB_H5TC4G63AFR_RDA_792MHz.cfg;
EMMC_UBOOT_BCT=PM358_Hynix_2GB_H5TC4G63AFR_RDA_792MHz.cfg;
...

So we must replace the “0x80098000” to “0x6009C000” in flashos.sh.

Next we’ll modify the flash.cfg. As we have mentioned, flash.cfg defines the partition table. Here is an excerpt from flash.cfg:

[device]
type=sdmmc
instance=3

[partition]
name=BCT
id=2
type=boot_config_table
allocation_policy=sequential
filesystem_type=basic
size=3145728
file_system_attribute=0
partition_attribute=0
allocation_attribute=8
percent_reserved=0

[partition]
name=EBT
id=4
type=bootloader
allocation_policy=sequential
filesystem_type=basic
size=6291456
file_system_attribute=0
partition_attribute=0
allocation_attribute=8
percent_reserved=0
filename=bootloader.bin

This config file begins with a “device” section, followed by several “partition” sections. The “device” section defines the storage media type(sdmmc, spi, etc.) and instance(don’t know its meaning).

Every “partition” section defines a partition. There are many config items, but we only need to concern the following ones:

  1. name: The name of the partition. The name of a partition is important since the “init” process will mount partitions by name(defined in fstab). Some partition name that you may need to pay attention to are: EBT(bootloader), SOS(recovery.img), DTB(device tree), LNX(boot.img), APP(system.img), CAC(cache.img) and UDA(userdata.img).
  2. id: every partition should has its unique id.
  3. type: type of the partition.
  4. size: size of the partition. Can be a 64 bits integer, so a partition can be larger than 4GB.
  5. filename: the file/image to flash into this partition.
  6. allocation_attribute: normal partition use “8” so the partition size will conform to the “size”; the last partition(the partition previous to GPT, in fact) use “0x808” to allocate all remaining storage to this partition.

We can change the flash.cfg to contains only necessary partitions(see Linux_for_Tegra/bootloader/ardbeg/cfg/gnu_linux_fastboot_emmc_full.cfg for reference). I didn’t change the file a lot, just dismissed to flash nvboot.bin(we don’t have a nvboot.bin for Jetson TK1), changed the DTB from tegra124-ardbeg.dtb to tegra124-pm375.dtb, added cache.img and userdata.img to flash into CAC and UDA respectively.

Compile Linux Kernel for Android

First extract the kernel source:

tar xvf kernel_src.tbz2

Or, download the source from NVIDIA’s git server:

git clone git://nv-tegra.nvidia.com/linux-3.10.git -b tegra-l4t-r19.3

I recommend you to download from git server, so all further changes can be tracked.

Once we have the kernel source code ready, we need to figure out the configurations. It had been a time-consuming and frustrating process that I can’t remember all the details. Here are the summary of the final config:

  1. Copy arch/arm/configs/tegra12_android_defconfig to arch/arm/configs/tegra12_mydroid_defconfig
  2. Delete Broadcom Wifi driver relevant options
  3. Delete touchscreen relevant options
  4. Disable LPAE(because it will be conflict with the r8169 ethernet driver)
  5. Disable PASR(it does not work, I don’t know why). We’d better to set pasr_enable to 0 in init.t124.rc too.

Now we have tegra12_mydroid_defconfig as our config file, it’s time to compile the kernel:

export ARCH=arm
# only needed when you use git to checkout the source code
export LOCALVERSION=
# use arm-none-eabi-gcc to compile. you can use other cross compiler too.
# use the command "sudo apt-get install gcc-arm-none-eabi" to install the arm-none-eabi- tools.
export CROSS_COMPILE=arm-none-eabi-
mkdir out
mkdir modules
# compile and generate zImage. the zImage file can be found in out/arch/arm/boot/zImage
make O=out zImage

# make modules
make O=out modules DESTDIR=$PWD/modules

#install modules to "modules" directory
make O=out modules_install INSTALL_MOD_PATH=$PWD/modules

OK, we have a workable kernel now! Let’s move on to the Android part.

Add Jetson TK1 to AOSP

  • Create a Skeleton

    To add a new product to AOSP, the first step is to create the product folder:

    # go to the AOSP source code root
    cd aosp
    # use populate-new-device.sh to generate a skeleton structure.
    # <company name>: nvidia
    # <product name>: jetsontk1. note that <product name> should not contains "-" or "_"
    ./device/common/populate-new-device.sh nvidia jetsontk1
    

    After we execute the populate-new-device.sh command, the skeleton of the product has been created. There is a new “nvidia” folder under “device/”, and there are two folders under “device/nvidia” folder: jetsontk1 and jetsontk1-kernel, the former contains device configuration files, and the later contains Linux kernel. Besides, “vendor/nvidia/jetsontk1” folder has been created too.

    Let’s have a look at the device/nvidia/jetsontk1 folder. The folder contains five files:

    1. AndroidProducts.mk: just to include full_jetsontk1.mk(and other mk, for example, aosp_jetsontk1.mk if you want to add aosp_jetsontk1).
    2. BoardConfig.mk: defines board specific properties and variables, compile flags, partiton layout, and so on.
    3. device.mk: defines device specific properties and variables.
    4. full_jetsontk1.mk: defines product variables.
    5. vendorsetup.sh: this file only contains one effective line: “add_lunch_combo full_jetsontk1-userdebug”. The net effect of this command is to add a menu item in the “lunch” result list.
  • Unpack Images from TADP Android OS Images

    There are some files that we can get from the TAD Android OS Images. There are several images under the android_k_ardbeg_os_image-19r16 folder, we only concern about these three: boot.img, recovery.img and system.img.

    cd <path_to_android_k_ardbeg_os_image>
    mkdir boot recovery system
    
    cd boot
    abootimg -x ../boot.img
    abootimg-unpack-initrd initrd.img
    # the initramfs will be extracted to ramdisk folder
    
    cd ../recovery
    abootimg -x ../recovery.img
    abootimg-unpack-initrd initrd.img
    # the initramfs will be extracted to ramdisk folder
    
    cd ../
    # convert the sparse image to raw image
    simg2img  system.img system.ext4.img
    
    # mount the image to <path_to_android_k_ardbeg_os_image>/system/ so we can get files inside the image
    sudo mount system.ext4.img system
    
  • Configure the Jetson TK1 Device
    • Copy Files to Device
      • Copy Kernel

        Just copy the zImage from kernel/out/arch/arm/boot/zImage to device/nvidia/jetsontk1-kernel/kernel.

      • Copy Config Files from TADP Android OS Images
        • Get system.prop from build.prop

          Use an editor to open <path_to_android_k_ardbeg_os_image>/system/build.prop, find “system.prop”, copy the following content, create and save the content in device/nvidia/jetsontk1/system.prop

        • Copy Media Codec Configuration Files
          cp <path_to_android_k_ardbeg_os_imag>/system/etc/media_codecs.xml device/nvidia/jetsontk1/
          cp <path_to_android_k_ardbeg_os_imag>/system/etc/media_profiles.xml device/nvidia/jetsontk1/
          
        • Copy Init Resource Files from Ramdisk

          Copy init.ardbeg.rc(because it is included by init.rc) and all relevant init files(included by any of the included files) from <path_to_android_k_ardbeg_os_image>/boot/ramdisk/ to device/nvidia/jetsontk1/.

          Since we don’t use SATA, we don’t need to copy files like init.ardbeg_sata.rc; init.rc, init.usb.rc and init.environ.rc can be generated by AOSP, we don’t need to copy them either. The same rule can apply to other type of files(for example, ueventd.rc) too.

        • Copy ueventd.rc from Ramdisk
          cp <path_to_android_k_ardbeg_os_image>/boot/ramdisk/ueventd.ardbeg.rc device/nvidia/jetsontk1/
          
        • Copy fstab from Ramdisk
          cp <path_to_android_k_ardbeg_os_image>/boot/ramdisk/fstab.ardbeg device/nvidia/jetsontk1/
          
        • Rename Ardbeg to Jetson-tk1

          Since the board we use is jetson-tk1(aware that “jetson” and “tk1” are separate by “-“) rather than “ardbeg”, we need to replace all “ardbeg” to “jetson-tk1”.

          cd device/nvidia/jetsontk1
          
          # with any file in current directory, replace "ardbeg" with "jetson-tk1" in its content
          sed -i 's/ardbeg/jetson-tk1/g' *
          
          # rename the files
          mv init.ardbeg.rc init.jetson-tk1.rc
          mv fstab.ardbeg fstab.jetson-tk1
          mv ueventd.ardbeg.rc ueventd.jetson-tk1.rc
          
    • Modify the Makefiles
      • AndroidProducts.mk

        No modifications needed.

      • BoardConfig.mk

        This is the most important file to define the product. There are many variables that we can/should define here. See device/lge/hammerhead/BoardConfig.mk for an example. Here is part of my BoardConfig.mk:

        TARGET_ARCH := arm
        TARGET_ARCH_VARIANT := armv7-a-neon
        TARGET_CPU_ABI := armeabi-v7a
        TARGET_CPU_ABI2 := armeabi
        TARGET_CPU_SMP := true
        
        TARGET_NO_BOOTLOADER := true
        
        BOARD_KERNEL_BASE := 0x10000000
        BOARD_KERNEL_PAGESIZE := 2048
        
        BOARD_KERNEL_CMDLINE := vmalloc=320M
        BOARD_MKBOOTIMG_ARGS := --ramdisk_offset 0x11000000 --tags_offset 0x10000100
        
        # Shader cache config options
        # Maximum size of the  GLES Shaders that can be cached for reuse.
        # Increase the size if shaders of size greater than 12KB are used.
        MAX_EGL_CACHE_KEY_SIZE := 12*1024
        
        # Maximum GLES shader cache size for each app to store the compiled shader
        # binaries. Decrease the size if RAM or Flash Storage size is a limitation
        # of the device.
        MAX_EGL_CACHE_SIZE := 2048*1024
        
        BOARD_USES_ALSA_AUDIO := true
        
        BOARD_USES_SECURE_SERVICES := true
        
        TARGET_NO_RADIOIMAGE := true
        TARGET_BOARD_PLATFORM := tegra
        TARGET_BOOTLOADER_BOARD_NAME := jetson-tk1
        TARGET_BOARD_INFO_FILE := device/nvidia/jetsontk1/board-info.txt
        BOARD_VENDOR_QCOM_GPS_LOC_API_HARDWARE := $(TARGET_BOARD_PLATFORM)
        TARGET_NO_RPC := true
        
        USE_OPENGL_RENDERER := true
        VSYNC_EVENT_PHASE_OFFSET_NS := 7500000
        SF_VSYNC_EVENT_PHASE_OFFSET_NS := 5000000
        TARGET_USES_ION := true
        
        TARGET_USERIMAGES_USE_EXT4 := true
        BOARD_BOOTIMAGE_PARTITION_SIZE := 8388608
        BOARD_RECOVERYIMAGE_PARTITION_SIZE := 8388608
        BOARD_SYSTEMIMAGE_PARTITION_SIZE := 1342177280
        BOARD_USERDATAIMAGE_PARTITION_SIZE := 13504610304
        BOARD_CACHEIMAGE_PARTITION_SIZE := 805306368
        BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE := ext4
        BOARD_FLASH_BLOCK_SIZE := 131072
        
        BOARD_CHARGER_DISABLE_INIT_BLANK := true
        BOARD_CHARGER_ENABLE_SUSPEND := true
        
        TARGET_RECOVERY_PIXEL_FORMAT := RGBX_8888
        #TARGET_RECOVERY_UI_LIB := librecovery_ui_jetson-tk1
        TARGET_RECOVERY_FSTAB = device/nvidia/jetsontk1/fstab.jetson-tk1
        
        TARGET_RELEASETOOLS_EXTENSIONS := device/nvidia/jetsontk1
        
        HAVE_ADRENO_SOURCE:= false
        
        TARGET_FORCE_HWC_FOR_VIRTUAL_DISPLAYS := true
        
      • device.mk

        It defines kernel and other device specific files/properties. The following is an excerpt from my device.mk:

        # where the kernel is.
        ifeq ($(TARGET_PREBUILT_KERNEL),)
        LOCAL_KERNEL := device/nvidia/jetsontk1-kernel/kernel
        else
        LOCAL_KERNEL := $(TARGET_PREBUILT_KERNEL)
        endif
        
        PRODUCT_COPY_FILES := \
                $(LOCAL_KERNEL):kernel
        
        # follow this pattern: <source>:<destination>. "root" means "root of the ramdisk" here.
        PRODUCT_COPY_FILES += \
            device/nvidia/jetsontk1/init.jetson-tk1.rc:root/init.jetson-tk1.rc \
            device/nvidia/jetsontk1/init.hdcp.rc:root/init.hdcp.rc \
            device/nvidia/jetsontk1/init.nv_dev_board.usb.rc:root/init.nv_dev_board.usb.rc \
            device/nvidia/jetsontk1/init.qvs.rc:root/init.qvs.rc \
            device/nvidia/jetsontk1/init.t124.rc:root/init.t124.rc \
            device/nvidia/jetsontk1/init.t124_emmc.rc:root/init.t124_emmc.rc \
            device/nvidia/jetsontk1/init.tlk.rc:root/init.tlk.rc \
            device/nvidia/jetsontk1/fstab.jetson-tk1:root/fstab.jetson-tk1 \
            device/nvidia/jetsontk1/ueventd.jetson-tk1.rc:root/ueventd.jetson-tk1.rc
        
        PRODUCT_COPY_FILES += \
            device/nvidia/jetsontk1/media_codecs.xml:system/etc/media_codecs.xml \
            device/nvidia/jetsontk1/media_profiles.xml:system/etc/media_profiles.xml
        
        # Our product looks like a tablet rather than a phone
        PRODUCT_CHARACTERISTICS := tablet
        
        # Resources that are put into this folder will overlay the default resources
        DEVICE_PACKAGE_OVERLAYS := \
            device/nvidia/jetsontk1/overlay
        
        # I want to add these tools. Note the tool to add may be part of a project, for example, "e2fsck" is part of external/e2fsprogs
        PRODUCT_PACKAGES += \
                tcpdump \
                setup_fs \
                e2fsck \
                init.environ.rc
        
        PRODUCT_PACKAGES += \
            audio.a2dp.default \
            audio.usb.default \
            audio.r_submix.default \
            libaudio-resampler
        
      • full_jetsontk1.mk

        We can leave it unmoidified. We can define some product relevant variables here, since I’m not familiar with it, I just changed PRODUCT_MODEL(a string to describe this product).

  • Add Vendor Specific Files

    We need to create a “proprietary” folder in vendor/nvidia/jetsontk1 first.

    mkdir vendor/nvidia/jetsontk1/proprietary/
    
    • Copy Files from TADP Android OS Images

      Go to <path_to_android_k_ardbeg_os_image>/system/, and find all files that are relevant to our platform.

      cd <path_to_android_k_ardbeg_os_image>/system/
      
      # find any file that contains "tegra" or "nvidia"(case insensitive) in its name
      find . -iname "*tegra*" -o -iname "*nvidia*"
      
      # find any file that its content contains "tegra"(case insensitive)
      find . -type f | xargs grep -i --color tegra
      
      find . -type f | xargs grep -i --color nvidia
      
      # filter the results from the find commands above, then copy all relevant files to vendor/nvidia/jetsontk1/proprietary/,
      # don't forget "system" is part of the path
      # Note: this method can't make sure that we copy all necessary files to vendor,
      # we need to test to verify and add/delete some files if the generated system.img can't work properly.
      
    • Copy Firmwares from L4T

      The firmwares under <path_to_android_k_ardbeg_os_image>system/etc/firmware/ are not suitable for jetson-tk1(graphic won’t be displayed properly if we use those firmwares), so we need to get the right ones.

      tar xvf <path_to_Linux_for_Tegra>/nv_tegra/nvidia_drivers.tbz2
      cp -ar lib/firmware vendor/nvidia/jetsontk1/proprietary/system/etc/
      
    • Generate the Copy Files List
      # use this command to generate the file list
      # the pattern: <source>:<destination>:<vendor>
      # the <vendor> is mandatory if you add "PRODUCT_RESTRICT_VENDOR_FILES := true" in device/nvidia/jetsontk1/full_jetsontk1.mk or else where.
      find vendor/nvidia/jetsontk1/proprietary/system/ -type f | while read line;  do echo "${line}:${line#vendor/nvidia/jetsontk1/proprietary/}:nvidia \\"; done >> vendor/nvidia/jetsontk1/device-vendor.mk
      

      After that, we need to edit vendor/nvidia/jetsontk1/device-vendor.mk, add “PRODUCT_COPY_FILES := \” before the list, and delete the last “\” of the list. The resulting file should be looked like:

      PRODUCT_COPY_FILES := \
              vendor/nvidia/jetsontk1/proprietary/system/vendor/bin/btmacwriter:system/vendor/bin/btmacwriter \
              vendor/nvidia/jetsontk1/proprietary/system/vendor/bin/crash-check-arm:system/vendor/bin/crash-check-arm:nvidia
      
  • Make Ethernet the Default Network

    To enable ethernet and make it the default network, we need to modify the config.xml. One way is to modify frameworks/base/core/res/res/values/config.xml, but there is a more flexible way to do this.

    Fist we need to set DEVICE_PACKAGE_OVERLAYS in device/nvidia/jetsontk1/device.mk:

    DEVICE_PACKAGE_OVERLAYS := \
        device/nvidia/jetsontk1/overlay
    

    Then, create device/nvidia/jetsontk1/overlay/frameworks/base/core/res/res/values/, copy frameworks/base/core/res/res/values/config.xml to it.

    mkdir -p device/nvidia/jetsontk1/overlay/frameworks/base/core/res/res/values/
    cp frameworks/base/core/res/res/values/config.xml device/nvidia/jetsontk1/overlay/frameworks/base/core/res/res/values/
    

    Finally, changes the content of config.xml, modify tags named “networkAttributes” and “radioAttributes” as follow:

    <string-array translatable="false" name="networkAttributes">
      <item>"ethernet,9,9,2,-1,true"</item>
      <item>"wifi,1,1,1,-1,true"</item>
    </string-array>
    
    
    <string-array translatable="false" name="radioAttributes">
      <item>"1,1"</item>
      <item>"9,1"</item>
    </string-array>
    

    The interpretation of these attributes resides in the constructor of ConnectivityService(framework/base/services/java/com/android/server/ConnectivityService.java).

    The networkAttributes node defines available network types and their attributes. Since Jetson TK1 only support ethernet and Wifi, I have deleted other network types. Every item in networkAttributes represents a network type, the content of the item is a string to define the network name and attributes, separated by “,”. The meaning of the fields, from left to right, are: <network name>,<network type>,<radio type>,<priority>,<restore time>,<dependency met on boot>. When the <network type> and <radio type> are identical, this network is a default network(framework/base/core/java/android/net/NetworkConfig.java). So we can infer that both ethernet and wifi are default networks in the above configuration. <priority> defines the priority of a network, the larger, the higher priority. <restore time> is the milliseconds to delay before automatically restore the default connection. <dependency met on boot> indicates whether the network is always available, but Android framework hasn’t made use of this flag now, so we can leave it either true or false.

    The radioAttributes node defines radio types. The value of an item defines two fields which are separated by “,” too. The first field defines the radio type . The second field(simulneity) indicates which radio type can be used simultaneously with current radio type, but Android hasn’t implement such mechanism yet, so this field is of no use.

    Note: There is no setting entry for ethernet in Settings.apk, and the implementation is pretty simple for ethernet in Android(only support DHCP, the DHCP commands are issued in framework/base/core/java/android/net/EthernetDataTracker.java).

  • Add RTL8192cu USB Wireless Adapter Support
    • Show Wifi Setting in Setting App

      Add the following to device/nvidia/jetsontk1/device.mk:

      PRODUCT_COPY_FILES += \
          frameworks/native/data/etc/android.hardware.wifi.xml:system/etc/permissions/android.hardware.wifi.xml
      

      The android.hardware.wifi.xml file indicates that this device support Wifi, with this file, there will be an Wifi setting entry in the Settings app.

    • Add RTL8192cu Support in the Linux Kernel

      In the kernel’s menuconfig, select Device Drivers->Network device support->Wireless LAN->Realtek wireless card support->RTL8192CU(as mudule), then compile kernel and modules again. Since rtl8192cu and the moudules that it depends on(cfg80211, mac80211, rtlwifi, etc.) can take up several megabytes, they should be better to stay as modules, rather than to be linked in to the kernel image, in order not to increase the kernel image significantly.

      When finish compiling, there are five modules that relevant to rtl8192cu: cfg80211.ko, mac80211.ko, rtlwifi.ko, rtl8192c-common.ko, rtl8192cu.ko. To integrate these modules to the system.img, we need to do some work.

      Note: there are rtl8192cu driver implementations that support Android, but you need some hacks to make them work with newer version of Linux kernel(I haven’t had time to port them, so I’m not sure whether they can work properly in Android):

      1. https://code.google.com/p/rtl8192cu/
      2. https://github.com/tylerx/rtl8192cu-dkms
    • Add the Compiled Kernel Modules to Vendor Folder

      The first thing is to copy the modules to aosp/vendor/:

      cd <path_to_aosp>
      mkdir -p vendor/nvidia/jetsontk1/proprietary/system/lib/modules
      
      
      cd <path_to_kernel>
      # copy all kernel modules to aosp/vendor
      find modules/ -name "*.ko" | xargs cp -t <path_to_aosp>/vendor/nvidia/jetsontk1/proprietary/system/lib/modules/
      
    • Add RTL8192cu Firmware to Vendor Folder

      When inserting, rtl8192cu need to download firmware from filesystem to the adapter. The firmware can be found in Ubuntu’s linux-firmware package.

      sudo apt-get install linux-firmware
      sudo mkdir vendor/nvidia/jetsontk1/proprietary/system/vendor/firmware/rtlwifi
      sudo cp /lib/firmware/rtlwifi/rtl8192cufw* vendor/nvidia/jetsontk1/proprietary/system/vendor/firmware/rtlwifi/
      

      Note: don’t forget to regnerate the file list in device-vendor.mk.

    • Add Configuration File to Vendor Folder

      Wifi won’t be enabled if it can’t find this configuration file.

      cp -ar <path_to_android_k_ardbeg_os_image>/system/etc/wifi vendor/nvidia/jetsontk1/proprietary/system/etc/
      
    • Load Modules in Init

      Because the Android has no modprobe, and wifi_load_driver/wifi_unload_driver(implemented in hardware/libhardware_legacy/wifi/wifi.c) can only load/unload a single module, we need to insert all modules that rtl8192cu.ko depends on before the Android framework load it. One proper way to load these modules is to load them in init.

      Add Wifi properties in device/nvidia/jetsontk1/device.mk:

      # why set the id to 9? -- just because there are settings in device.mk that set the chip id to 7 and 8.
      PRODUCT_PROPERTY_OVERRIDES += \
          wifi.interface=wlan0 \
              wifi.commchip_id=9 \
          wifi.supplicant_scan_interval=15
      

      When detects that the wifi chip id is 9, load the modules automatically(device/nvidia/jetsontk1/init.t124.rc):

      # rtl8192cu chipset
      on property:wifi.commchip_id=9
          symlink /system/vendor/firmware/rtlwifi/rtl8192cufw.bin /data/misc/wifi/firmware/rtl8192cufw.bin
          insmod /system/lib/modules/cfg80211.ko
          insmod /system/lib/modules/mac80211.ko
          insmod /system/lib/modules/rtlwifi.ko
          insmod /system/lib/modules/rtl8192c-common.ko
          setprop wifi.supplicant wpa_supplicant
      

      Add wpa_supplicant service in device/nvidia/jetsontk1/init.t124.rc:

      # this is almost identical to wpa_suppl_nl, except that this service has no "-z" option(because the wpa_supplicant from AOSP seems
      # not support this option) and no p2p setting(since I only want to make rtl8192cu work in station mode).
      # why not use wpa_suppl_nl as the service name? because hardware/libhardware_legacy/wifi/wifi.c uses "wpa_supplicant" as the service name, I don't want to change the code to make as little modifications to AOSP as possible.
      service wpa_supplicant /system/bin/wpa_supplicant \
          -iwlan0 -Dnl80211 -c/data/misc/wifi/wpa_supplicant.conf \
          -O/data/misc/wifi/sockets \
          -e/data/misc/wifi/entropy.bin  \
          -g@android:wpa_wlan0
      #   we will start as root and wpa_supplicant will switch to user wifi
      #   after setting up the capabilities required for WEXT
      #   user wifi
      #   group wifi inet keystore
          class main
          socket wpa_wlan0 dgram 660 wifi wifi
          disabled
          oneshot
      

      Then change “wpa_suppl_nl” to “wpa_supplicant” of post-fs-data in device/nvidia/jetsontk1/init.t124.rc.

    • Add HAL Implementation

      Android’s wpa_supplicant need an HAL layer to communicate to the Wifi driver, called lib_driver_cmd. If we need to implement this HAL from scratch, it must be an nightmare. Thank goodness, there are already some good examples to follow.

      All I had done are:

      1. Create hardware/realtek.
      2. Make a copy of hardware/broadcom/wlan, and rename all broadcom relevant words to realtek relevant ones.
      3. Change DRV_NUMBER_SEQUENTIAL_ERRORS from 4 to 10 in hardware/realtek/wlan/rtl8192cu/wpa_supplicant_8_lib/driver_cmd_nl80211.c, because the driver we use does not implement several commands that Android uses, we need to increase DRV_NUMBER_SEQUENTIAL_ERRORS so it won’t reset due to too many sequential errors.
    • Define Wifi Relevant Flags and Variables in BoardConfig.mk

      To make wpa_supplicant compiled with the correct flags, we need to add some code to BoardConfig.mk:

      # Wifi related definitions
      WPA_SUPPLICANT_VERSION      := VER_0_8_X
      WIFI_DRIVER_MODULE_PATH         := /system/lib/modules/rtl8192cu.ko
      WIFI_DRIVER_MODULE_NAME         := rtl8192cu
      BOARD_WLAN_DEVICE           := rtl8192cu
      BOARD_WPA_SUPPLICANT_DRIVER := NL80211
      BOARD_WPA_SUPPLICANT_PRIVATE_LIB := lib_driver_cmd_$(BOARD_WLAN_DEVICE)
      #WPA_BUILD_HOSTAPD                      := false
      #BOARD_HOSTAPD_DRIVER        := NL80211
      #BOARD_HOSTAPD_PRIVATE_LIB   := lib_driver_cmd_$(BOARD_WLAN_DEVICE)
      #WIFI_DRIVER_FW_PATH_PARAM   := "/sys/module/bcmdhd/parameters/firmware_path"
      #WIFI_DRIVER_FW_PATH_AP      := "/vendor/firmware/fw_bcmdhd_apsta.bin"
      WIFI_DRIVER_FW_PATH_STA     := "/vendor/firmware/rtlwifi/rtl8192cufw.bin"
      

      Done! These are all we need to do to enalbe rtl8192cu.

  • Show Software Buttons on the Screen

    Because we don’t have hardware buttons in Jetson TK1, it would better to show software buttons in the bottom of the screen. Just add a system property to tell Android that we don’t have hardware buttons(device/nvidia/jetsontk1/device.mk):

    # show software buttons
    PRODUCT_PROPERTY_OVERRIDES += \
            qemu.hw.mainkeys=0
    

Compile AOSP

cd <path_to_aosp>
# or ". build/envsetup.sh", "." equals to "source" in bash
# setup environment variables for current shell session
source build/envsetup.sh

# specify that we want to compile product full_jetsontk1-userdebug
lunch full_jetsontk1-userdebug

# "-j4" means use four threads to compile in parallel
make -j4

The compiling is quite straightforward, but it might lasting several hours(depends on the performance of your computer). There is a hardware requirement that your machine should have more than four GBs memory(RAM), or the linker will hang a long time and finally fail when it try to link libwebviewchromium, due to lack of memory.

Issues and Solutions

GUI Crash

If your Android works fine at first, then suddenly dies with the screen messed up, it’s probably because the vmalloc value not large enough. “vmalloc” is a contiguous range of virtual memory that Linux kernel reserves. Linux kernel or drivers can request a relative large contiguous region from vmalloc region(contiguous in virtual address, but not necessary contiguous in physical address).

Linux kernel developers have increased the default vmalloc tens of MB to 240MB, but that’s not enough in our case. I have tried several vaules, from 240MB(the default value) to 280MB, and finally 320MB(300MB might be enough). We can set the value of vmalloc via kernel command line arguments(defined in device/nvidia/jetsontk1/BoardConfig.mk):

BOARD_KERNEL_CMDLINE := vmalloc=320M

System Partition Can’t be Remounted

I don’t know why, but remount command will fail when I try to remount the system partition to “rw”:

mount -o remount rw /system
# the error message: "mount: Permission denied"

A workaround is to mount it to somewhere(it will fail either), then remount it:

# this command will end up failure
mount -t ext4 /dev/block/platform/sdhci-tegra.3/by-name/APP /mnt/sdcard

# now I can remount the partition successfully
mount -o remount rw /system/

Ethernet Hardware Can’t be Recognized

  • Caused by ARM LPAE

    ARM LPAE is a kernel feature that enable Large Physical Address Extention. When enabled, the CONFIG_ARCH_DMA_ADDR_T_64BIT flag will be set, then dma_addr_t will be of type u64, otherwise dma_addr_t will be u32.

    Jetson TK1’s ethernet driver(kernel/drivers/net/ethernet/realtek/r8169.c) has a check:

    if ((sizeof(dma_addr_t) > 4) &&
        !pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) && use_dac) {
            tp->cp_cmd |= PCIDAC;
            dev->features |= NETIF_F_HIGHDMA;
    } else {
            rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
            if (rc < 0) {
                    netif_err(tp, probe, dev, "DMA configuration failed\n");
                    goto err_out_free_res_3;
            }
    }
    

    Although dma_addr_t has the size of 8, Since use_dac is set to 0 in the beginning of the code, the second branch will be executed(erroneously). Then pci_set_dma_mask will always fail due to the DMA_BIT_MASK(32) will generate a wrong mask.

    So there are two ways to fix this:

    1. Disable ARM LPAE
    2. Enable ARM LPAE and set use_dac to 1

    I have chosen the first one, Tegra K1 is an 32-bit processor.

  • Caused by Incorrect “odmdata” Value

    I once use the odmdata value from original flashos.sh to flash the images, eveything seems ok except the ethernet driver can’t be loaded properly. Do remember to use the right odmdata value.

USB Drive Can’t be Mounted

In my opinion, usb drive support ought to be mature now, but that’s not the truth. When I insert my usb drive, it can be detected, but cann’t be mounted. After inspected, I found two causes.

  • The Mountpoint Does Not Match

    The default mount point is storage/sdcard which is defined in frameworks/base/core/res/res/xml/storage_list.xml, but TADP version uses sdcard1 as the mount point(defined in init.t124.rc and fstab.jetson-tk1). The fix is pretty easy: copy frameworks/base/core/res/res/xml/storage_list.xml to device/nvidia/jetsontk1/overlay/frameworks/base/core/res/res/xml, and change the mount point to /storage/sdcard1.

  • Due to A Bug in Vold

    Vold is a daemon program to monitor and manage volumes. When starts, vold parse the config file(/fstab.{ro.hardware}), create DirectVolume objects to store volumes that are managed by vold. The content of the fstab file we use(fstab.jetson-tk1, copy from TADP):

    # Android fstab file.
    #<src>                                                  <mnt_point>         <type>    <mnt_flags>                                                                         <fs_mgr_flags>
    # The filesystem that contains the filesystem checker binary (typically /system) cannot
    # specify MF_CHECK, and must come before any filesystems that do specify MF_CHECK
    
    /dev/block/platform/sdhci-tegra.3/by-name/APP           /system             ext4      ro                                                                                  wait
    /dev/block/platform/sdhci-tegra.3/by-name/CAC           /cache              ext4      noatime,nosuid,nodev,data=writeback,nodelalloc,errors=panic    wait
    /dev/block/platform/sdhci-tegra.3/by-name/UDA           /data               ext4      noatime,nosuid,nodev,data=writeback,noauto_da_alloc,errors=panic    wait,check,encryptable=/dev/block/platform/sdhci-tegra.3/by-name/MDA
    /devices/platform/sdhci-tegra.2/mmc_host/mmc1           auto    vfat      defaults                                                             voldmanaged=sdcard1:auto
    /devices/platform/tegra-ehci.0                          auto                vfat      defaults                                                             voldmanaged=usbdrive:auto
    /devices/platform/tegra-ehci.1                          auto                vfat      defaults                                                             voldmanaged=usbdrive:auto
    /devices/platform/tegra-ehci.2                          auto                vfat      defaults                                                             voldmanaged=usbdrive:auto
    /devices/platform/tegra-xhci                            auto                vfat      defaults                                                             voldmanaged=usbdrive:auto
    

    From the fstab file, we konw there are five devices that are managed by vold(the items with fs_mgr_flags begin with voldmanaged), and four of the five voldmanaged devices use the same label “usbdrive”, that means once any of these devices plug in to the board, it will be mounted to “usbdrive”(so they should be used exclusively). Rather than put all devices with the same label into one DirectVolume object, current implementation of vold will create four objects(this will then cause the problem).

    Later on, when a usb drive is inserted, VolumeManager::handleBlockEvent will be called.

    void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {
        const char *devpath = evt->findParam("DEVPATH");
    
        /* Lookup a volume to handle this device */
        VolumeCollection::iterator it;
        bool hit = false;
        for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {
            if (!(*it)->handleBlockEvent(evt)) {
    #ifdef NETLINK_DEBUG
                SLOGD("Device '%s' event handled by volume %s\n", devpath, (*it)->getLabel());
    #endif
                hit = true;
                break;
            }
        }
    
        if (!hit) {
    #ifdef NETLINK_DEBUG
            SLOGW("No volumes handled block event for '%s'", devpath);
    #endif
        }
    }
    

    As we can see from the source, VolumeManager::handleBlockEvent iterates the volumes list to find one volume that responsible for the event. Which volume can handle the event? – The one with the device path corresponding to the inserted device.

    int DirectVolume::handleBlockEvent(NetlinkEvent *evt) {
        const char *dp = evt->findParam("DEVPATH");
    
        PathCollection::iterator  it;
        for (it = mPaths->begin(); it != mPaths->end(); ++it) {
            if (!strncmp(dp, *it, strlen(*it))) {
                /* We can handle this disk */
    

    The corresponding volume object updates its state(to State_Pending) and broadcasts the insert event(with its label and fuse mount point) to Android framework. A while later, Android framework send a command to mount the device, with only the fuse mount point or the label pass into vold. There is no way for vold to find the exactly matched volume object, only can find one volume that matches the path or label.

    int VolumeManager::mountVolume(const char *label) {
        Volume *v = lookupVolume(label);
    
        if (!v) {
            errno = ENOENT;
            return -1;
        }
    
        return v->mountVol();
    }
    

    Since there are four volume objects that hold the same label(“usbdrive”), all of them have the same fuse mount point too, the lookupVolume returns the first matched object, but the returned object is not necessary the one that handled the insert event. So the “v->mountVol()” will fail. That’s the whole story why usb drive can’t be mounted.

    To fix this issue, we can simply group all devices that have the same label to a single volume object, here’s the fix:

    diff --git a/main.cpp b/main.cpp
    index d4b7d28..d7c1d95 100644
    --- a/main.cpp
    +++ b/main.cpp
    @@ -172,6 +172,7 @@ static int process_config(VolumeManager *vm)
         for (i = 0; i < fstab->num_entries; i++) {
             if (fs_mgr_is_voldmanaged(&fstab->recs[i])) {
                 DirectVolume *dv = NULL;
    +            bool new_dv_flag = false;
                 flags = 0;
    
                 /* Set any flags that might be set for this volume */
    @@ -186,15 +187,22 @@ static int process_config(VolumeManager *vm)
                     !strcmp(fstab->recs[i].fs_type, "vfat")) {
                     flags |= VOL_PROVIDES_ASEC;
                 }
    -            dv = new DirectVolume(vm, &(fstab->recs[i]), flags);
    +            dv = (DirectVolume*)(vm->lookupVolume(fstab->recs[i].label));
    +            if (dv == NULL) {
    +                dv = new DirectVolume(vm, &(fstab->recs[i]), flags);
    +                new_dv_flag = true;
    +            }
    
                 if (dv->addPath(fstab->recs[i].blk_device)) {
                     SLOGE("Failed to add devpath %s to volume %s",
                           fstab->recs[i].blk_device, fstab->recs[i].label);
    +                if (new_dv_flag)
    +                    delete dv;
                     goto out_fail;
                 }
    
    -            vm->addVolume(dv);
    +            if (new_dv_flag)
    +                vm->addVolume(dv);
             }
         }
    

Wifi “disabled” when Connecting

One probably cause is that you have the ehternet wire pluged in. Since we have set the ethernet as the primary default network, wireless net can’t be activated if there is an ethernet connection. Just plug out the ethernet wire and try again.

DNS Not Work

A host name can always be resolved if the network is set up via Android framework(system_server). But if we set up the network via command tool netcfg(“netcfg eth0 dhcp”, for example), host name won’t be resolved. Why?

Prior to Android 4.3, when resolving host name, bionic(Android’s libc) get the name servers from system properties(net.dns1, net.wlan0.dns1, etc.). Started from Android 4.3, Android had changed the way to retrive domain name servers: it sets these system properties too, but never use them to resolve host names. Instead, Android implements a small cache in bionic, and only gets name server from the cache. The following is the process of a typical DNS query:

  1. An application(the client) calls getaddrinfo, then getaddrinfo calls android_getaddrinfoforiface with environment variable ANDROID_DNS_MODE not set(in the client process).
  2. In android_getaddrinfoforiface(in the client process), it calls getenv(“ANDROID_DNS_MODE”) and the result is NULL, so it calls android_getaddrinfo_proxy to forward(write) the query to /dev/socket/dnsproxyd, which is a socket file and the other end of the socket is netd.
  3. Netd recevices the query and calls android_getaddrinfoforiface with ANDROID_DNS_MODE set to “local”.
  4. In android_getaddrinfoforiface(in the netd process), it verifies that ANDROID_DNS_MODE is set to “local” so it won’t forward the query this time, it will do the query itself.
  5. Here is a brief call chain(int the netd process): android_getaddrinfoforiface -> explore_fqdn -> nsdispatch -> _dns_getaddrinfo -> res_searchN -> res_querydomainN -> res_queryN -> res_nsend. Client usually did not specify interface, so both interface name and DNS servers are come from cache(_resolv_populate_res_for_iface).
  6. Now android_getaddrinfoforiface returns with the query results to netd. Then netd will pass the results back to client.
  7. In client process, android_getaddrinfo_proxy returns, and then android_getaddrinfoforiface returns, finally getaddrinfo returns, the query finishes.

From the scenario we have described above, we can infer that if we set ANDROID_DNS_MODE to “local” before we call getaddrinfo(or other query APIs), then we can do the query in our process, without the participation of netd, isn’t that a good idea?

Theoretically we could do the DNS query without the help of netd, but we can’t. We need to know which interface to be used, and what name servers to query to, both of them are come from cache, there is no easy way for us to setup the cache. That’s what netd for. Android framework will issue resolver commands(setdefaultif, setifdns, etc.) to netd, then netd will cache the info for further use. Since netd has a long lifecycle(from Android boot up to shutdown, unless it is killed), the resolver infomartion that come from Android framework will always exist in netd process, so any clients that follow the scenario described above can get the right DNS results.

But what if the Android framework haven’t called the resolver commands? The answer is: no DNS query can be satisfied. It sounds this will never happen, right? Usually, but not all the time. When I had just ported Android to Jetson TK1, I didn’t know how to enable ethernet in the framework at that time, but I could make the ethernet work by running “netcfg eth0 dhcp” in the terminal. It may be enough for early version of Android, but not for 4.3 or later, since there are no way for netd to get the resolver info. Thank goodness, there is a tool named “ndc” that I can use to communicate to netd. By executing the following commands, we can make DNS work:

# set default interface to eth0
ndc resolver setdefaultif eth0

# set dns servers for eth0
# ndc resolver setifdns <ifc> <domain> <dns1> <dns2>
ndc resolver setifdns eth0 "" 8.8.8.8 8.8.4.4

Note: ndc is a netd client, it only has impact on netd, you should not expect the ndc command will set dns info for your own process.

Cannot Load Audio Libraries

NVIDIA has changes the implementation of libtinyalsa.so(add some APIs), so we must not use the one that compiled from the AOSP source, we should copy it from TADP.

No Audio Output

We need NVDIA’s audio libraries(audio.primary.tegra.so, etc.) to enable the audio output, except audio_policy.tegra.so. This library works fine in Jedroid’s version, but I fail to make it work, so I have to delete this library from my image(note that the audio_policy.default.so from TADP not work too, we should use the audio_policy.default.so that compiled with AOSP source).

Because we don’t use NVIDIA’s audio_policy.tegra.so, we have to change the audio_policy.conf(ac3 and dts are not supported):

--- vendor/nvidia/jetsontk1/proprietary/system/etc/audio_policy.conf    2014-11-02 11:11:21.116000000 +0800
+++ vendor/nvidia_new/jetsontk1/proprietary/system/etc/audio_policy.conf        2014-10-21 10:10:35.332000000 +0800
@@ -40,13 +40,6 @@
         devices AUDIO_DEVICE_OUT_SPEAKER|AUDIO_DEVICE_OUT_WIRED_HEADSET|AUDIO_DEVICE_OUT_WIRED_HEADPHONE|AUDIO_DEVICE_OUT_AUX_DIGITAL|AUDIO_DEVICE_OUT_ALL_SCO|AUDIO_DEVICE_OUT_REMOTE_SUBMIX
         flags AUDIO_OUTPUT_FLAG_DIRECT
       }
-      aux_ac3_dts_pass_through {
-        sampling_rates 44100|48000
-        channel_masks AUDIO_CHANNEL_OUT_STEREO
-        formats AUDIO_FORMAT_IEC61937_AC3|AUDIO_FORMAT_IEC61937_EAC3|AUDIO_FORMAT_IEC61937_DTS
-        devices AUDIO_DEVICE_OUT_AUX_DIGITAL|AUDIO_DEVICE_OUT_SPEAKER|AUDIO_DEVICE_OUT_WIRED_HEADSET|AUDIO_DEVICE_OUT_WIRED_HEADPHONE
-        flags AUDIO_OUTPUT_FLAG_DIRECT
-      }
       ulp_output {
         sampling_rates 48000|44100
         channel_masks AUDIO_CHANNEL_OUT_STEREO

HDMI No Audio Output

  • Due to Unsupported Video Resolution

    If there is no audio output from TV, you may see some warning from kernel’s log(dmesg): tegradc tegradc.1: hdmi: can’t set audio to 32000 at 82000000 pix_clock. The pix_clock(pixel clock) will vary from resolution to resolution. The cause to this warning is: NVIDIA use several static tables to map pixel clock to audio frequency, if a pixel clock can’t find a matched item in the table, the audio output can’t be initialized.

    • A Workaround

      The tables and the checking code are defined in kernel/drivers/video/tegra/dc/hdmi.c. The following change it’s a trick to enable HDMI audio output regardless of the resolution.

      diff --git a/drivers/video/tegra/dc/hdmi.c b/drivers/video/tegra/dc/hdmi.c
      index 9400e30..634b9b1 100644
      --- a/drivers/video/tegra/dc/hdmi.c
      +++ b/drivers/video/tegra/dc/hdmi.c
      @@ -331,6 +331,7 @@ static const struct tegra_hdmi_audio_config
                      return NULL;
              }
      
      +#if 0
              while (table->pix_clock) {
                      if (table->pix_clock > (pix_clock/100*99) &&
                       table->pix_clock < (pix_clock/100*101) &&
      @@ -340,6 +341,9 @@ static const struct tegra_hdmi_audio_config
              }
      
              return NULL;
      +#else
      +    return table;
      +#endif
       }
      
    • Todo: Calculate the Exact Value

      Google has changed the implementation to calculate the value dynamically in the ChromeOS kernel(reference: http://ac100.wikispaces.com/hdmi_audio). I had checkouted the ChromeOS kernel source but the changes are not easy to merge, I have to put it down, hoping that I can time to read the ChromeOS version thoroughly and finally apply the changes in the future.

  • Due to Erroneous Jack Insert/Remove Detection

    The GPIO detection value is opposite to the right one in NVIDIA’s kernel code, so the TV won’t make any sound unless I plug in an earphone(in fact, only a headphone jack is needed). To fix this, just invert the detection value:

    diff --git a/arch/arm/mach-tegra/board-ardbeg.c b/arch/arm/mach-tegra/board-ardbeg.c
    index 28e7fef..3dff989 100644
    --- a/arch/arm/mach-tegra/board-ardbeg.c
    +++ b/arch/arm/mach-tegra/board-ardbeg.c
    @@ -409,7 +409,12 @@ static void ardbeg_audio_init(void)
                            board_info.board_id == BOARD_PM363) {
                    /*Laguna*/
                    ardbeg_audio_pdata_rt5639.gpio_hp_det = TEGRA_GPIO_HP_DET;
    -               ardbeg_audio_pdata_rt5639.gpio_hp_det_active_high = 1;
    +        if (board_info.board_id == BOARD_PM375) {
    +            // by Rex: should be 0, or the kernel will notify insert event when
    +            // audio jack plug out, and vice verse.
    +            ardbeg_audio_pdata_rt5639.gpio_hp_det_active_high = 0;
    +        } else
    +            ardbeg_audio_pdata_rt5639.gpio_hp_det_active_high = 1;
                    if (board_info.board_id != BOARD_PM363)
                            ardbeg_audio_pdata_rt5639.gpio_ldo1_en = -1;
            } else {
    

Only Max or Zero Volume Allowed in HDMI Audio Output

Android disables other volume values for HDMI audio output, without further explanation. Since Jetson TK1 supports HDMI audio output, I just comment out the guard in the AOSP source code to make the volume control work.

diff --git a/audio/AudioPolicyManagerBase.cpp b/audio/AudioPolicyManagerBase.cpp
index 65e732f..8a55a9a 100644
--- a/audio/AudioPolicyManagerBase.cpp
+++ b/audio/AudioPolicyManagerBase.cpp
@@ -3004,7 +3004,7 @@ float AudioPolicyManagerBase::computeVolume(int stream,
     // if volume is not 0 (not muted), force media volume to max on digital output
     if (stream == AudioSystem::MUSIC &&
         index != mStreams[stream].mIndexMin &&
-        (device == AUDIO_DEVICE_OUT_AUX_DIGITAL ||
+        (/*device == AUDIO_DEVICE_OUT_AUX_DIGITAL ||*/
          device == AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET ||
          device == AUDIO_DEVICE_OUT_USB_ACCESSORY ||
          device == AUDIO_DEVICE_OUT_USB_DEVICE)) {
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 92474df..37bb68f 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -432,7 +432,7 @@ public class AudioService extends IAudioService.Stub {
     public final static int STREAM_REMOTE_MUSIC = -200;

     // Devices for which the volume is fixed and VolumePanel slider should be disabled
-    final int mFixedVolumeDevices = AudioSystem.DEVICE_OUT_AUX_DIGITAL |
+    final int mFixedVolumeDevices = /*AudioSystem.DEVICE_OUT_AUX_DIGITAL |*/
             AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET |
             AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET |
             AudioSystem.DEVICE_OUT_ALL_USB;

Audio Playback Stops Unexpectedly

  • The Problem

    There are occasions that the audio playback stops unexpectedly:

    1. If you open a web page that contains audio content, while playing, you switch to another tab, then the audio playback will stop.
    2. When playing, turn the music app to backgrond(for example, by pressing homescreen button), the music stops.
    3. When playing, click to open a sub-menu, then the audio stops.

    It’s very annoying, right? Happily, by digging into the Android source code, I finally found the cause and a solution. It is because I have enabled the tap sound effect that cause the problem. With the sound effect enabled, when I press the homescreen button, Android will try to play a short sound, during the opening of the new audio, it will close the original playing audio stream.

  • The Fix

    To fix this problem, we just need to delete the “dual_audio” section in audio_policy.conf:

    diff --git a/vendor/nvidia/jetsontk1/proprietary/system/etc/audio_policy.conf b/vendor/nvidia/jetsontk1/proprietary/system/etc/audio_policy.conf
    index 41ef9fa..5a4d1a1 100644
    --- a/vendor/nvidia/jetsontk1/proprietary/system/etc/audio_policy.conf
    +++ b/vendor/nvidia/jetsontk1/proprietary/system/etc/audio_policy.conf
    @@ -47,13 +47,6 @@ audio_hw_modules {
             devices AUDIO_DEVICE_OUT_SPEAKER|AUDIO_DEVICE_OUT_WIRED_HEADSET|AUDIO_DEVICE_OUT_WIRED_HEADPHONE
             flags AUDIO_OUTPUT_FLAG_DIRECT
           }
    -      dual_audio {
    -        sampling_rates 8000|16000|32000|64000|128000|11025|22050|44100|88200|176400|12000|24000|48000|96000|192000
    -        channel_masks AUDIO_CHANNEL_OUT_STEREO|AUDIO_CHANNEL_OUT_QUAD|AUDIO_CHANNEL_OUT_SURROUND|AUDIO_CHANNEL_OUT_5POINT1|AUDIO_CHANNEL_OUT_7POINT1
    -        formats AUDIO_FORMAT_PCM_16_BIT
    -        devices AUDIO_DEVICE_OUT_SPEAKER|AUDIO_DEVICE_OUT_WIRED_HEADSET|AUDIO_DEVICE_OUT_WIRED_HEADPHONE|AUDIO_DEVICE_OUT_AUX_DIGITAL|AUDIO_DEVICE_OUT_ALL_SCO|AUDIO_DEVICE_OUT_REMOTE_SUBMIX
    -        flags AUDIO_OUTPUT_FLAG_DIRECT
    -      }
         }
         inputs {
           primary {
    
  • Explanation

    Why the “dual_audio” section matters and how it matters? Here’s a brief explanation:

    • The original audio_policy.conf contains the following configuration:
      primary {
          outputs {
            primary {
              sampling_rates 48000
              channel_masks AUDIO_CHANNEL_OUT_STEREO
              formats AUDIO_FORMAT_PCM_16_BIT
              devices AUDIO_DEVICE_OUT_SPEAKER|AUDIO_DEVICE_OUT_WIRED_HEADSET|AUDIO_DEVICE_OUT_WIRED_HEADPHONE|AUDIO_DEVICE_OUT_EARPIECE|AUDIO_DEVICE_OUT_AUX_DIGITAL|AUDIO_DEVICE_OUT_ALL_SCO|AUDIO_DEVICE_OUT_REMOTE_SUBMIX
              flags AUDIO_OUTPUT_FLAG_PRIMARY
            }
            multichannel {
              sampling_rates 8000|16000|32000|64000|128000|11025|22050|44100|88200|176400|12000|24000|48000|96000|192000
              channel_masks AUDIO_CHANNEL_OUT_QUAD|AUDIO_CHANNEL_OUT_SURROUND|AUDIO_CHANNEL_OUT_5POINT1|AUDIO_CHANNEL_OUT_7POINT1
              formats AUDIO_FORMAT_PCM_16_BIT
              devices AUDIO_DEVICE_OUT_SPEAKER|AUDIO_DEVICE_OUT_WIRED_HEADSET|AUDIO_DEVICE_OUT_WIRED_HEADPHONE|AUDIO_DEVICE_OUT_AUX_DIGITAL|AUDIO_DEVICE_OUT_ALL_SCO|AUDIO_DEVICE_OUT_REMOTE_SUBMIX
              flags AUDIO_OUTPUT_FLAG_DIRECT
            }
            ulp_output {
              sampling_rates 48000|44100
              channel_masks AUDIO_CHANNEL_OUT_STEREO
              formats AUDIO_FORMAT_MP3|AUDIO_FORMAT_AAC
              devices AUDIO_DEVICE_OUT_SPEAKER|AUDIO_DEVICE_OUT_WIRED_HEADSET|AUDIO_DEVICE_OUT_WIRED_HEADPHONE
              flags AUDIO_OUTPUT_FLAG_DIRECT
            }
            dual_audio {
              sampling_rates 8000|16000|32000|64000|128000|11025|22050|44100|88200|176400|12000|24000|48000|96000|192000
              channel_masks AUDIO_CHANNEL_OUT_STEREO|AUDIO_CHANNEL_OUT_QUAD|AUDIO_CHANNEL_OUT_SURROUND|AUDIO_CHANNEL_OUT_5POINT1|AUDIO_CHANNEL_OUT_7POINT1
              formats AUDIO_FORMAT_PCM_16_BIT
              devices AUDIO_DEVICE_OUT_SPEAKER|AUDIO_DEVICE_OUT_WIRED_HEADSET|AUDIO_DEVICE_OUT_WIRED_HEADPHONE|AUDIO_DEVICE_OUT_AUX_DIGITAL|AUDIO_DEVICE_OUT_ALL_SCO|AUDIO_DEVICE_OUT_REMOTE_SUBMIX
              flags AUDIO_OUTPUT_FLAG_DIRECT
            }
          }
          inputs {
              ...
          }
      }
      
    • Android loops all outputs to find a suitable output profile. For example, an audio stream which has the format of AUDIO_FORMAT_PCM_16_BIT, sample rate of 44100 and channel masks of AUDIO_CHANNEL_OUT_STEREO, the output device is HDMI(AUDIO_DEVICE_OUT_AUX_DIGITAL), then the output “dual_audio” will be returned.
    • Since “dual_audio” has the output flag AUDIO_OUTPUT_FLAG_DIRECT, Android will output the audio directly.
    • Multi-stream can’t be outputed simultaneously while using direct output, when we open a new audio stream while one is playing, Android will close the original one. That’s why the audio stops.
    • But if Android can’t find a direct output, it will use mixer to mix all streams that are not using direct output.
    • So I delete the “dual_audio” section to prevent Android put audio stream to direct output.

ADB over USB Not Work

ADB is really a handy tool for Android developers, so when I found that adb on Jetson TK1 only worked at times, I decided to fix the bug. Before I had fixed the bug, I had found two workarounds.

  • Workaround one: Use ADB over TCP

    If you have network connections, you can still make adb to work by setting the data to trasmit over TCP.

    • The Manual Way

      You can enable the tcp port by typing the following commands in terminal. Note that you must type these command every time after you boot/reboot Jetson TK1.

      setprop service.adb.tcp.port 5555
      stop adbd
      start adbd
      
    • The Once for All Way

      First remount the system partition, then type the following command in terminal:

      echo "service.adb.tcp.port=5555" >> /system/build.prop
      

      After that, adbd will automatically enable TCP access when starts up.

      To connect the device, run the connect command on your host machine:

      # assume the IP address of your Jetson TK1 is: 192.168.1.5
      adb connect 192.168.1.5
      
  • Workaround two: Sleep and Wakeup

    Set the sleep time to be a very short value(15s, for example), then wake the device up after it has gone to sleep, and you will find that the device can be detected by host-side adb now.

  • Fix it

    After some inspections, I had found that when resume from suspend, the value of register USB_PHY_WAKEUP(will then save to tegra->init_status) always has the USB_VBUS_STATUS bit set, only with this bit set to 1 that Jetson TK1 can change to OTG_STATE_B_PERIPHERAL state, only when entering OTG_STATE_B_PERIPHERAL that the adb driver will be enabled. So the fix is straightforward:

    diff --git a/drivers/usb/phy/tegra-otg.c b/drivers/usb/phy/tegra-otg.c
    index 8077724..1681010 100644
    --- a/drivers/usb/phy/tegra-otg.c
    +++ b/drivers/usb/phy/tegra-otg.c
    @@ -113,7 +113,7 @@ static int otg_notifications(struct notifier_block *nb,
                            tegra->int_status &= ~USB_ID_STATUS;
                            tegra->int_status |= USB_ID_INT_EN;
                     } else
    -                       tegra->int_status |= USB_ID_STATUS;
    +                       tegra->int_status |= USB_ID_STATUS | USB_VBUS_STATUS;
            }
    
            spin_unlock_irqrestore(&tegra->lock, flags);
    

Kernel Panic on Connecting to L2TP VPN

  • Some Note about the Cause

    The problem only occurs when using L2tp VPN, everything is all right when I use PPTP. I have spent some time to try to figure out the root cause, but failed. All I have known is that the problem occurs after iommu_iova_to_phys returns 0. Before that, none 0 returned by iommu_iova_to_phys, after that, iommu_iova_to_phys returns 0 frequently.

  • Not a Fix: A Way to Avoid the Crash

    The kernel won’t crash immediately after the following change, but it might crash later. I’ll struggle to fix this bug whenever I have time.

    diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
    index 3369576..17055b8 100644
    --- a/arch/arm/mm/dma-mapping.c
    +++ b/arch/arm/mm/dma-mapping.c
    @@ -9,6 +9,8 @@
      *
      *  DMA uncached mapping support.
      */
    +
    +#define DEBUG
     #include <linux/module.h>
     #include <linux/mm.h>
     #include <linux/gfp.h>
    @@ -1209,8 +1211,11 @@ static size_t pg_iommu_unmap(struct iommu_domain *domain,
                    phys_addr_t phys_addr;
    
                    phys_addr = iommu_iova_to_phys(domain, iova + len);
    -               BUG_ON(phys_addr != iova_gap_phys);
    -               iommu_unmap(domain, iova + len, PF_PAGES_SIZE);
    +                // printk("domain: %p, phys_addr: %x, iova: %lx, len: %lu\n", domain, phys_addr, iova, len);
    +                if (phys_addr != 0) {
    +                  BUG_ON(phys_addr != iova_gap_phys);
    +                  iommu_unmap(domain, iova + len, PF_PAGES_SIZE);
    +                }
            }
    
            return iommu_unmap(domain, iova, len);
    

Conclusions

I have spent almost three weeks to do the porting and to fix these issues, and there are issues that I’m working on. It’s really time-consuming, and several times that I wanted to give up. But it’s worthy. I have learned a lot, I’m more confident, and most importantly, I have a playground to learn and to try now^_^.

This note may not cover all of my modifications, so I have uploaded my code to github, you can find it here: https://github.com/tiger-flying/JetsonTK1_Android. Feel free to correct me or give suggestions to me.

Finally, I want to give my sincere thanks to the Jedroid team, I mustn’t do this without your hard work. Thank you very much!

Author: Rex Shen

Created: 2014-11-17 Mon 10:03

Emacs 24.4.1 (Org mode 8.2.10)


Leave a comment

Your email address will not be published. Required fields are marked *

15 thoughts on “Port Android to NVIDIA Jetson TK1

  • evening dress kohls

    I am no longer sure the place you’re getting your info, however good topic. I needs to spend a while studying more or figuring out more. Thank you for fantastic info I was in search of this information for my mission.

  • call of duty

    Hey There. I found your blog using msn. This is an extremely well written article.
    I’ll make sure to bookmark it and return to read more of your useful info.
    Thanks for the post. I’ll certainly comeback.

    • Rex
      Rex Post author

      Android runs well on my Jetson TK1 now. Currently I don’t see any serious bugs, so I have not spent time on improving it recently.

      • Kumar

        Hi Rex,

        Are you following the above mentioned steps? or any other tutorial? I could get jedroid to work on Jetson TK-1 from SD Card but not emmc.

        • Rex
          Rex Post author

          Hi Kumar,
          This post is written after I had succeeded in porting Android to Jetson TK1, so I might have missed some details, but I believe you can do the porting by taking my steps.

          Jedroid uses the original partition from Linux for Tegra, rather than the Android way, but it doesn’t matter, Android is merely a “shell” running above Linux kernel. To download Jedrod into emmc, you need to use mmcblk0p1 as root_device.

          • Kumar

            Thanks for the reply Rex.

            I followed your git repo and was able to port AOSP to Jetson TK1!. Thank you very much for the post.

            However, I am running into a display issue. I am unable to get the HDMI video to work. Other than that everything works. I am able to adb and check serial logs. Can you pleast tell, if there are any problems with HDMI with fastboot?

            For some strange reason, jedroid works with the same kernel and boots using U-boot. HDMI works using Jedroid, but not AOSP?

          • Rex
            Rex Post author

            Kumar,
            Which kernel you use? Do you compile it yourself, or use the kernel along with Jedroid? As I remember, the Jedroid kernel binary seems not work. This problem should have nothing to do with bootloader, you can use u-boot to boot AOSP too. It’s the kernel that initializes video output. The Nvidia’s HDMI driver might have some minor issues, so we have to plug in HDMI before we boot Jetson.
            If HDMI can show something(kernel has initialized HDMI output), but Android can’t show up, please check the Android logcat to see if there are any segmentation faults. I had met similar problem before, the reason to it is my misusing the graphic firmware(located in system/etc/firmware).

  • Yumeng

    Hi Rex,
    Thanks you very much, this article is very useful to me.

    However, I changed the resolution to 4k (3840x2160p30) for kernel, but it no effect.
    It always display 1920x1080p60 mode, so I deleted all the 1080p60 related clock 148.5Mhz,
    But it still display 1080p60…Could you perform android 4K resolution on Jetson TK1?

    • Rex
      Rex Post author

      Yumeng,
      Since I don’t have a 4k TV, I can’t test it. You can first check /sys/class/graphics/fb0/modes(or fb1, etc.) to make sure your monitor is recognized correctly.

      I haven’t dived into the HDMI driver code, and currently I have no spare time to read the driver code, I’m not able to give you more advice right now. If you find any solution, please let me know, thank you.

  • Steven

    Hi Rex,

    would you mind uploading the compiled android image on github ?
    I have major difficulties to download since the sources are so huge.
    Thanks in advance.
    BR

    • Rex
      Rex Post author

      Hi Steven,
      Maybe you can try out Jedroid’s images. The Android images are really big(around 500MB), and they can be generated via compiling, so I don’t want them to “pollute” the project. Maybe I will upload them to a net disk, but I’m quite busy recently, it’s not on my near schedule.
      If you have any tools(for example, QQ) viable to transfer the images, I’ll happy to send them to you.

  • Greg

    Hi.

    Are there any chance to update to Android 5.x ? Or at least to update tool-chain? Nvidia changed their tools and I am stuck at the beginning :/

    Have a nice day.

    • Rex
      Rex Post author

      Greg, I don’t currently have time to work on this.

      Could you tell more details about the problem you have encountered?