Building a Custom Firmware for the SN986-based Wifi Camera

Table of Contents

  • Device
  • SDK
  • SD Card
  • Toolchain
  • U-Boot
  • Initramfs
  • Boot Process
  • Busybox
  • Kernel
  • Driver for RTL8188EU
  • LEDs
  • Self-Burning Firmware

Device

  • Mijia Xiaofang iSC5 Smart Camera (but NOT '1S' version)
  • aka Wyze Cam v1
  • aka iSmartAlarm (…)

SDK

SDK is a software development kit provided by the vendor of processor. SDK for our chip contains patched bootloader, Linux kernel w/ drivers and userspace libraries and programs. I took source code for bootloader and kernel for my firmware from such SDK.

Strings that could be extracted from the camera's boot log and binaries show that the name (SDK version, branch, tag) of the stock firmware is: 'SN986_1.50_P2P_TUTK_050a_20160921_1712'

The latest file you can obtain from the internet which contains SDK with a similar branch is 'SN986_1.50_P2P_TUTK_043a_20160308_1000.tgz'

Google for it literally.

All SDKs for the SN986 series processors which are available on the internet are stripped. That means that they don't contain source code for vendor drivers, just binaries. Nevertheless they contain full patched source code for u-boot, kernel, busybox, multiple userspace programs, toolchain.

Note

Actually you could find the source code for some drivers. From different SDKs and folders provided by the same persons who provide SDKs, I were lucky to find sources for the following modules:

  • gpio
  • video stack: isp2, isp3, sc2035 (but NOT sc2135)
  • gadget ucd
  • usb-wifi (rtl8188eu v4.3.24 by realtek)
  • gpio, pwm, uart, watchdog (extracted from 1.10 and 1.20 branches, it's NOT for Linux but FreeRTOS)

SD Card

Main article: Notes on an SD Card

In order to use ext4 filesystem with 2.6.25 kernel you need to disable few ext4 features:

tune2fs -O ^metadata_csum /dev/sdc2
tune2fs -O ^huge_file /dev/sdc2
tune2fs -O ^64bit /dev/sdc2

When running the commads above you might be asked to run

e2fsck -f /dev/sdc2

or

resize2fs -s /dev/sdc2

it's OK, just run it.

Toolchain

The toolchain used in the original SDK is the following set of binaries: 'snx_sdk/toolchain/crosstool-4.5.2/bin/arm-linux-*'

The key variables are: 'ARCH' needs to be set to 'arm', 'CROSS_COMPILE' to 'arm-linux-' and the 'PATH' variable needs to be prepended with the absolute path of 'toolchain/crosstool-4.5.2/bin'.

When building kernel, the variable 'AUTOCONF_DIR' needs to be set to the absolute path of 'buildscript/include' directory and passed along with the 'ARCH' and 'CROSS_COMPILE' variables to the kernel's make.

Boot Process

The following paragraph describes the boot stages of my temporary firmware which uses spi flash as boot device and SD card as a kernel and root primary storage.

  1. SoC ROM code
    • checks gpio2 state (fw update trigger)
    • initializes spi flash and mmc
    • reads hardware-settings (hws) from the first 4K block of spi flash
      • commands in hws specify load addresses, size and jump addresses
      • commands in hws initialize SDRAM and some other controllers
    • loads u-boot from flash to SDRAM
    • jumps to u-boot
  2. U-Boot
    • reinitializes controllers
    • loads uImage from mmc 0:1
    • loads initramfs.cpio.gz from mmc 0:1
    • extracts kernel from uImage
    • jumps to kernel
  3. Kernel
    • reinitializes controllers
    • extracts and mounts initramfs as '/'
    • executes '/init' as pid 1
  4. '/init' of initramfs
    • loads snx_sd SD card driver
    • populates '/dev'
    • mounts SD card second partition 'mmcblk0p2' as '/mnt/root'
    • switches root to '/mnt/root'
    • executes '/linuxrc' of the new root as pid 1
  5. '/linuxrc' on mmcblk0p2
    • mounts sysfs, proc, dev, run…
    • executes busybox's sysv init '/sbin/init' as pid 1
  6. Busybox's SysV Init '/sbin/init'
    • does system run state stuff (?)
    • reads inittab
    • executes '/etc/init.d/rcS'
  7. '/etc/init.d/rcS'
    • loads vendor kernel modules
    • executes '/etc/init.d/rc.local'
  8. '/etc/init.d/rc.local'
    • sets up hostname and network interfaces
    • calls wpa_supplicant, udhcpcd, dropbear
    • does other local user stuff

Boot ROM code & Hardware Settings

Main article: Hardware Settings Explained

Hardware settings are stored in the first erase block (4096 bytes) of the spi flash. Hardware settings is a sequence of bytes which represent commands to the SoC BROM interpreter and their arguments plus some header and footer.

The syntax of commands is described at the end of document 'sn9866_series_datasheet_standard_160930.pdf' (this is the name that I gave it in my BSP, the original one was 'SN98660+series-CG-SZZD-160930.pdf').

The registers with addresses 0xFFFFxxxx are not explained in vendor documents. Howewer one may find hints to the meaning of some of them in the source files in vendor sdk.

U-Boot

soon

Initramfs

Main article: Initramfs

Busybox

Main article: How to Compile and Configure the Latest Busybox for SN986

The latest busybox (busybox-1.29.1) compiled with SDK's toolchain runs without issues on the target. When it compiled as a dynamically linked binary ensure that 'ld-uClibc.so.0', 'libc.so.0' and 'libm.so.0' are present in the '/lib' directory.

Note

Busybox'es switch_root does not move or unmount filesystems other than '/' unlike what a util-linux'es switch_root do. See extensive commentaries in the busybox'es switch_root source code about what it actually do. You may also want to read the discussion at <https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=676001>

Kernel

Prior to compile kernel from SDK you should run 'make sn98660_402mhz_sf_defconfig' in the 'buildscript' directory. This will generate some config files under the 'buildsctipt/include' directory. These files will be used when compiling some vendor kernel code.

It also needs to install some build tools like perl modules. See SDK's documentation.

Once you have installed necessary perl modules the kernel should compile without any issues. One thing you need to do is to erase obsolete 'defined' keyword in the 'kernel/timeconst.pl' file. You can use my '0001_erase_defined_keyword.patch' for this purpose.

Note

With the same config as for SDK branch 'P2P_TUTK_1.50', kernel from SDK branch '1.60' failed to load snx_nvram driver with a segfault/core dump error. The toolchain and config both are from 1.50 SDK. It needs to compare kernel source tree to resolve this.

Note

The basic things need to be changed in the menuconfig are

  • enable all of the options for EXT4 filesystem
  • enable INITRAMFS
  • disable ETHERNET
  • enable EMBEDDED config and make sure that HOTPLUG is enabled
  • (opt) enable proc/config.gz
  • (?) switch platform from SN98600 to SN98660

If you do not use my config from bootstrap directory, ensure that my 'build.mk' or default Makefile substitutes actual cpu frequency after generating default config. Actual cpu frequency is taken by the 'kernel/linux-*/Makefile' from 'buildscript/include/config/snx_/config/snx_sdk.conf'.

It's necessary to set the variable AUTOCONF_DIR to the absolute path of the 'buildscript/include' directory and pass it to the kernel make command along with the ARCH and CROSS_COMPILE variables. Otherwise the kernel build will unable to find some generated headers.

Driver for RTL8188EU

The version string of the driver as reported by the stock firmware is 'RTL871X: rtl8188eu v4.3.24_16705.20160509'.

In the source code of the driver the version string is stored in the header file 'include/wrt_version.h'.

Source code with exactly the same version as a stock firmware has can be downloaded from the github repo (my access date: Jul 29'18): 'https://github.com/hi35xx/rtl8188eu'

Alternatively, the version from SDK 'rtl8188EUS_v4.3.15_13239_simpleconfig' works as well.

Before compiling the driver's source code make sure that necessary kernel options are enabled:

WLAN, USB, CFG80211
WIRELESS_EXT
WEXT_PRIV
LIB80211
LIB80211_CRYPT_WEP
LIB80211_CRYPT_CCMP
LIB80211_CRYPT_TKIP

There is also the staging driver 'rtl8188eu' in the latest kernel source tree. Kernel options listed above are taken from its Kconfig.

You should patch the Makefile of the v4.3.24 driver in order to compile for SNX986 platform. You can refer to the SDK driver's Makefile or just apply my patch. The tip is to search for the references of 'CONFIG_PLATFORM_ARM_SONIX926' variable. Note that naming of variables in my patched makefile differ from that in makefile of vendor usb-wifi drivers. I use realtek's Makefile names directly (KVER, KSRC, MODDESTDIR), while vendor makefile set them from another variables (vKERNEL, KERNELDIR, INSTALLDIR).

The driver configuration is done in the file '<rtl8188eu src>/include/autoconf.h'. There is an option 'CONFIG_AUTOCFG_CP' in the Makefile which determines whether to use alternative configuration. If this option is set to 'y' then Makefile will look for 'autoconf_autoconf_rtl8188e_usb_linux.h' in the root of its source tree and copy it to 'include/autoconf.h'. There are several debug options in the end of 'autoconf.h' which you may want to change.

The driver is to be compiled as kernel module and some parts of the vendor kernel code are used when building. Because of that you should set or export AUTOCONF_DIR variable pointing to 'buildscript/include' directory along with other variables.

Once Makefile is patched the whole command set for building .ko file is the following (if you are building the driver from SDK, you should name the variable KERNELDIR instead of KSRC):

export PATH=/home/student/develop/xiaofang/sdk/SN986_1.50_P2P_TUTK_043a_20160308_1000/snx_sdk/toolchain/crosstool-4.5.2/bin:$PATH
export AUTOCONF_DIR=/home/student/develop/xiaofang/sdk/SN986_1.50_P2P_TUTK_043a_20160308_1000/snx_sdk/buildscript/include/
make CROSS_COMPILE=arm-linux- KSRC=/home/student/develop/xiaofang/sdk/SN986_1.50_P2P_TUTK_043a_20160308_1000/snx_sdk/kernel/linux-2.6.35.12/obj/normal/src -j5

Note that driver should be recompiled every time the kernel recompiled with different (networking) options. Otherwise segfaults and core dumps may appear.

After me had succesfully installed driver on the target and started ssh connection I noticed that ssh shell is a little bit laggy. Then I tested a ping response time and it was about stable 2ms when ping from target to the router and a regular spikes from 2ms up to 100-200ms on every 2nd packet when ping from router to the target (I have a console access to the router). I had recompiled the driver with debug printing enabled and saw that the driver constantly switching power management mode from 0 to 2 and backward. The driver attempts to go to sleep but every new ping packet woke it up again. So I completely disabled power management by adding a driver option to the driver loading call:

modprobe 8188eu rtw_power_mgnt=0

Note that for some unknown reason the driver completely ignores any changes made to files in the '/sys/module/8188eu/parameters' directory. So actually those exposed module parameters are only for information purposes at the moment. I also disabled the power management on my laptop (the ping was about stable 60ms from the router to laptop and ~2ms in other direction) so that now I have a total ping of ~3ms between the laptop and the target.

LEDs

There are two LEDs connected to the gpio controller. One is orange and other is blue. The orange one is connected to gpio3, the blue one to gpio4. They are connected such way that during a reset when all gpios are set to floating state the orange led is switched on while the blue one is off. That means that the orange led's source is pulled up and it's enough to set direction of the gpio3 to 'out' to switch it off (when gpio ditection is set to 'out' the default voltage level is 0). Otherwise, the blue led's sink is pulled up and it's enough to set gpio4's direction to output to switch it on.

Self-Burning Firmware