Compiling kernel modules for Atheros AR5B22 (AR9462) on Jetson TK1

I recently got a Atheros AR5B22 chip for my Jetson TK1 board, in order to make it support WiFi and Bluetooth. The system provided by NVIDIA (Linux4Tegra 21.4) doesn’t have Atheros driver built-in, so I have to compile it to make use of the device.

AR5B22 installed on Jetson TK1

This is what the chip looks like when installed on TK1. AR5B22 is the Mini PCIe reference design for AR9462, which features both 2.4GHz and 5GHz WiFi and Bluetooth 4.0, according to WikiDevi.

Since it belongs to 9xxx series, Linux kernel has the well-supported driver ath9k for it. Unlike other WiFi-Bluetooth-combo chips from Atheros, this one doesn’t specify which Bluetooth chip it uses (judging by BT 4.0 support, it should be AR3012), but nevertheless you still need ath3k driver and firmware for Bluetooth support. This has bugged me for quite a while, but I figured it out anyway (with hints from this Ubuntu bug report).

If you are familiar with how to compile Linux kernel modules for Jetson TK1, above is all you need to continue. The rest of this article are detailed steps for those who don’t know about this.

Note: The following steps are to compile directly on TK1, and features some hack-y steps for installing them. Also, I am NOT responsible for bricking your device.

  1. First make sure you have the latest Linux4Tegra (L4T) 21.4 installed on your Jetson TK1, which features basic bluetooth support. You can use Jetpack to flash it.
  2. The following steps are all carried out with a shell on TK1. It could be either over SSH (ssh ubuntu@IP), or GNOME Terminal (Ctrl-Alt-T) from GUI if you have a monitor plugged in.
  3. Install the firmware (for ath3k) and dependency (for kernel config menu) packages on your TK1.
    sudo apt-get install linux-firmware libncurses5-dev
  4. Download and extract L4T kernel sources into your home directory.
    mkdir ~/kernel && cd ~/kernel
    wget -O kernel_src.tbz2 https://s.du9l.com/Iz4HK  # For L4T 21.4
    tar xf kernel_src.tbz2 && cd kernel
  5. Copy existent kernel config as a start.
    zcat /proc/config.gz > .config
  6. Enter kernel config menu, and change the following settings.
    make menuconfig
    • From “General setup” set “Local version” to “-gdacac96” (check with uname -a), otherwise your compiled module will report “Unknown symbol in module” and “ath9k: version magic … should be …” errors when you insert them.
    • Use “Exit” to go back to the top, then from “Device Drivers – Network device support – Wireless LAN”, press M on “Atheros Wireless Cards” to compile it as a module; then enter it, press M on “Atheros 802.11n wireless cards support”, and press Y on “Atheros bluetooth coexistence support” and “Atheros ath9k PCI/PCIe bus support”.
    • Again, “Exit” to the top, then from “Networking support – Bluetooth subsystem support (should already be M in 21.4 kernel) – Bluetooth device drivers”, press M on “Atheros firmware download driver”.
    • Use “Save” to save your work (default “.config” name is fine), and “Exit” until you are back to the shell.
  7. Use the following command to start the compilation. It usually needs ~5 minutes to finish.
    make -j4 modules
  8. Here comes the hack-y part: Officially you need sudo make modules_install to install the modules, but I just want to install the newly compiled ones into a separate folder, so I will use the following commands instead:
    sudo mkdir /lib/modules/`uname -r`/kernel/custom  # `uname -r` becomes "3.10.40-gdacac96" in this case
    find . -name 'ath*.ko' | xargs -I{} sudo cp {} /lib/modules/`uname -r`/kernel/custom/
    sudo depmod -a
  9. In order to use WiFi and Bluetooth together, you need to enable “Bluetooth coexistence” in ath9k module.
    echo "options ath9k btcoex_enable=1" | sudo tee /etc/modprobe.d/ath9k_btcoex.conf
  10. Finally, insert both modules into the kernel.
    sudo modprobe ath9k ath3k

You should now have both WiFi and Bluetooth working. You can check with the following commands:

iwconfig
hciconfig

Just to be clear, I used the above steps with the following hardware, but I suppose you can use the same drivers for any Atheros WiFi AR9xxx series and BT AR3xxx series chip (combo or separate), as long as the 3.10 kernel and ath9k & ath3k modules support them.

$ lspci |grep Atheros
# 01:00.0 Network controller: Qualcomm Atheros AR9462 Wireless Network Adapter (rev 01)
$ lsusb |grep Atheros
# Bus 001 Device 003: ID 0cf3:3004 Atheros Communications, Inc.

Another afternoon wasted: How to choose two fonts with fontconfig

After upgrading to Ubuntu 16.04, the font in my GNOME is a mess (again). So I spent another afternoon trying to figure out how to choose the fonts I need using fontconfig.

This is what I achieved using Source Sans Pro and Source Han Sans CN. The default was to use DejaVu Sans and SimSun.

My goal is to use two fonts, an English one for displaying generic characters, and a Chinese one for, um, Chinese. Although I can use a single Chinese font for both, but the English characters displayed in Chinese font is disastrous. So basically I want to tell fontconfig to use the two fonts in a preferred order: Use English one wherever possible, and fallback to the native one when the former can’t do the job.

Actually I have solved this before, but I forgot what I did back then. So I had to spend another afternoon Googling and reading their long and non-standardized docs on how to express my request in their weird XML tags. After several attempts, I found that the following configuration works just fine.

The whole XML below should be saved as ~/.config/fontconfig/conf.d/99-xiaodu.conf (create non-existent folders when needed), which is the preferred location for user-specific fontconfig files.

<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
  <!-- For other languages -->
  <match target="pattern">
    <test qual="any" name="family">
     <string>sans-serif</string>
   </test>
   <edit name="family" mode="prepend" binding="strong">
      <string>Arial</string>
    </edit>
 </match>
  <match target="pattern">
    <test qual="any" name="family">
     <string>serif</string>
    </test>
   <edit name="family" mode="prepend" binding="strong">
      <string>Times New Roman</string>
    </edit>
 </match>
  <match target="pattern">
    <test qual="any" name="family">
     <string>monospace</string>
    </test>
   <edit name="family" mode="prepend" binding="strong">
      <string>Inconsolata</string>
    </edit>
 </match>
  <!-- For Chinese -->
  <match target="pattern">
    <test name="lang">
      <string>zh-cn</string>
    </test>
   <test qual="any" name="family">
     <string>sans-serif</string>
   </test>
   <edit name="family" mode="prepend" binding="strong">
      <string>Arial</string>
      <string>Source Han Sans CN</string>
   </edit>
 </match>
  <match target="pattern">
    <test name="lang">
      <string>zh-cn</string>
    </test>
   <test qual="any" name="family">
     <string>serif</string>
    </test>
   <edit name="family" mode="prepend" binding="strong">
      <string>Times New Roman</string>
      <string>Simsun</string>
   </edit>
 </match>
  <match target="pattern">
    <test name="lang">
      <string>zh-cn</string>
    </test>
   <test qual="any" name="family">
     <string>monospace</string>
    </test>
   <edit name="family" mode="prepend" binding="strong">
      <string>Inconsolata</string>
      <string>Source Han Sans CN</string>
   </edit>
 </match>

</fontconfig>

Basically this file tells fontconfig, when the locale is zh-cn (Chinese), it should to prepend the two fonts (English and Chinese) to the matching list; otherwise just prepend the English one.

Notice that the first font in each <edit> block is the English font, where the second is the native font. You should replace the language code (RFC3066 style) and all six font names using your favorite text editor, with respect to the fonts’ types (sans, serif or mono). If you want to test your work, use fc-match with something like $ LANG=zh-CN fc-match -s sans | head -2.

I just hope one day there would be a nice GUI for all this crap.

中文摘要:这篇文章介绍如何在 Ubuntu 中通过配置 fontconfig 同时选择要使用的英文和中文字体,使用英文字体显示一般字符,中文字体显示中文字符。

Prevent a package from being installed on Ubuntu / 在 Ubuntu 系统中阻止安装某个软件包

This article shows how to prevent a certain package from being installed, manually or as a dependency, on Linux systems using apt (apt-get) like Ubuntu and Debian.
这篇文章介绍如何在使用 apt (apt-get) 的系统上(例如 Ubuntu 和 Debian),防止某个软件包被安装,包括手动或被依赖。

It utilizes the “pinning” feature of apt, which you can read about in detail with man apt_preferences .
使用的原理是 apt 的 “pinning” 功能,可以通过 man apt_preferences 获取详细文档。

  1. Say we want to block “ttf-mscorefonts-installer” from being installed, which is a recommended dependency of “ubuntu-restricted-extras”. When we use apt-get install ubuntu-restricted-extras , the package will be automatically installed. If we use the “–no-install-recommends” switch, many other packages will be ignored, but we just want to block that one specific package.
    假设我们不想安装 “ttf-mscorefonts-installer” 软件包,但它是 “ubuntu-restricted-extras” 的推荐依赖。如果我们使用 apt-get install ubuntu-restricted-extras 命令,则依赖软件包也会自动安装。我们可以用 “–no-install-recommends” 开关阻止推荐依赖,但是同时会阻止其它很多软件包。接下来的方法适用于只阻止特定的一个软件包。
  2. In the folder “/etc/apt/preferences.d” we create a plain text file “block-package”, with the following contents:
    在 “/etc/apt/preferences.d” 文件夹中创建一个文本文件 “block-package”,内容如下:

    Package: ttf-mscorefonts-installer
    Pin: version *
    Pin-Priority: -10
    
  3. With a negative priority, the package will not be installed either manually or as a dependency when using apt-get.
    由于设置了负优先级,在使用 apt-get 时,这个软件包无论手动还是依赖都不会被安装。

Reference: http://askubuntu.com/questions/75895/how-to-forbid-a-specific-package-to-be-installed

Debugging random lagging on Android

My Samsung tablet with Android (4.4.2, rooted) has been experiencing random lagging for a while. This affects me most when I’m playing games on it. Earlier today I finally decided to find the root of the problem.

Finding the cause

The ultimate solution is to know which application is causing the lag and disable it. I first went to Settings > General > Developer Options (If you don’t have it, go to Settings > General > About phone/device and tap “Build number” 7+ times to enable it), and ticked “Show CPU usage”. Then you would see an overlay on the top-right corner of your device showing the top (like the Linux command) processes, similar to the image below.

Then I used my device to play games, and wait for the random lagging to happen. When the device start to response slowly, I watched the CPU usage overlay and see “gzip” topping the list. If you are not familiar with Linux, gzip is a compression tool similar to RAR or 7-Zip.

Wrapping (first attempt)

I was expecting some Android application to cause the lagging, instead it’s a command gzip that can be called by anyone. The problem became finding out who called it. Since my device is rooted, I decided to replace the original gzip binary with a wrapper that can log who called it before actually gzipping. Here is the wrapping bash script I used (first version, read on for second version):

#!/system/bin/sh

OPERATION=/system/bin/gzip.real
LOGFILE=/sdcard/gziplog.txt

OPTIONS="$@"

echo "`date` + $EUID `id` + $OPERATION "$@"" >> $LOGFILE

exec $OPERATION "$@"

The script does the following: (1) Log the time, user ID (the $EUID variable turns out to be useless, but `id` worked) and command line parameters to $LOGFILE (a plain text file in the internal storage); then (2) hand over the control to real gzip so that it doesn’t break the gzip functionality.

Then I used Root Explorer to rename /system/bin/gzip to /system/bin/gzip.real, and put my script to /system/bin/gzip (remember to modify the permission to be the same as gzip.real, usually 0755).

After using the device for a while and several lagging happened, I read the logging file and got these lines:

Fri Aug 28 13:53:22 CST 2015 +  uid=2000(shell) gid=2000(shell) groups=1007(log),1009(mount),1015(sdcard_rw),1028(sdcard_r),3003(inet),3006(net_bw_stats) context=u:r:dumpstate:s0 + /system/bin/gzip.real -6
Fri Aug 28 13:54:32 CST 2015 +  uid=2000(shell) gid=2000(shell) groups=1007(log),1009(mount),1015(sdcard_rw),1028(sdcard_r),3003(inet),3006(net_bw_stats) context=u:r:dumpstate:s0 + /system/bin/gzip.real -6
Fri Aug 28 13:55:43 CST 2015 +  uid=2000(shell) gid=2000(shell) groups=1007(log),1009(mount),1015(sdcard_rw),1028(sdcard_r),3003(inet),3006(net_bw_stats) context=u:r:dumpstate:s0 + /system/bin/gzip.real -6
Fri Aug 28 13:56:53 CST 2015 +  uid=2000(shell) gid=2000(shell) groups=1007(log),1009(mount),1015(sdcard_rw),1028(sdcard_r),3003(inet),3006(net_bw_stats) context=u:r:dumpstate:s0 + /system/bin/gzip.real -6

Observe that `id` produced “uid=2000(shell) gid=2000(shell) ...” and the command called is “gzip -6“. This means gzip was called by the shell user, not a specific application, and the data to be compressed was piped in to gzip and then piped out, since there are no input or output file names. (Spoiler: Actually this has shown “context=u:r:dumpstate:s0” which indicated that it was called by “dumpstate“, but I didn’t realize for the first time.)

Wrapping (second attempt)

Then I decided to take the surveillance up a level, and used the following wrapper in place of the first version:

#!/system/bin/sh

OPERATION=/system/bin/gzip.real
LOGFILE=/sdcard/gziplog.txt
TEE="/system/bin/busybox0 tee"
PSTREE="/system/bin/busybox0 pstree"

OPTIONS="$@"

echo "[$$] `date` + $EUID `id` + $OPERATION "$@"" >> $LOGFILE
$PSTREE -p >> $LOGFILE

#exec $OPERATION "$@"
$TEE -a $LOGFILE | $OPERATION "$@"

The difference between it and the previous one is that, it uses Busybox’s “pstree” command to output the process tree of that moment, and “tee” command to record the data piped in and pipe it out again. In order for this to work, I downloaded busybox-armv7l binary (my device has ARMv7 architecture) from the official website and put it to /system/bin/busybox0 with the same permission as gzip.real. You can also use Busybox installer apps and modify the busybox path accordingly.

After a while, I got the following log this time:

[8691] Fri Aug 28 14:30:46 CST 2015 +  uid=2000(shell) gid=2000(shell) groups=1007(log),1009(mount),1015(sdcard_rw),1028(sdcard_r),3003(inet),3006(net_bw_stats) context=u:r:dumpstate:s0 + /system/bin/gzip.real -6
init(1)-+-Binder_2(2299)---{Binder_1}(2360)
        |-DaemonServer(5419)---app_process(8668)
        |-DaemonServer(6687)
<...snip...>
        |-debuggerd(2276)---dumpstate(8690)-+-gzip(8691)---busybox0(8695)
        |                                   `-top(8696)
<...snip...>
========================================================
== dumpstate: 2015-08-28 14:30:46
========================================================

Build: KOT49H.P601ZCUCNH1
Build fingerprint: 'samsung/lt033gzc/lt033g:4.4.2/KOT49H/P601ZCUCNH1:user/release-keys'
Bootloader: P601ZCUCNH1
Radio: unknown
<...snip...>

Observe that [8691] is the gzip wrapper’s PID, and it’s called by “dumpstate“, which is then called by “debuggerd“. The wrapper also logged the data to be compressed, which is 5 MB long and takes 0.14s to be compressed on my PC instead of a poor mobile device.

Final solution

A quick Google told me this is for developers to debug the system state, and since I’m not a developer in this area, I decided to disable it once for all. This is done by using Root Explorer to revoke the execute permissions from the two binaries /system/bin/{debuggerd,dumpstate}.

Now the lagging is gone.

Use yumdb command with DNF to list explicitly installed packages

Since Fedora 22, DNF has replaced Yum as the default package manager. But not all yum commands have been migrated.

Today I felt like querying what packages I installed explicitly, and a quick Google told me to use yumdb search reason user, but it returned nothing.

I dug into the cause, and it turns out DNF now has its own yumdb (the database, not the command, sadly) at “/var/lib/dnf/yumdb”, along with yum’s “/var/lib/yum/yumdb”, and the command yumdb uses the latter by default.

In the man page of yum.conf, it says the “persistdir” value controls the path of yum’s db files, and it defaults to “/var/lib/yum”; but, yumdb --help tells me that it allows me to specify the location of config file. (Cheering for not touching /etc/yum.conf)

So, here’s what I did:

  1. Create a new fake config file with persistdir set to DNF’s folder.
    echo '[main]\npersistdir=/var/lib/dnf' > /tmp/dnf.conf
  2. Calls yumdb with that config file.
    yumdb -c /tmp/dnf.conf search reason user

Voila.