5 GHz Wifi access point on Linux Mint 19 / Atheros
+ how to just compile an Ubuntu distribution kernel without too much messing around.
Introduction
It’s not a real computer installation if the Wifi works out of the box. So these are my notes to self for setting up an access point on a 5 GHz channel. I’ll need it somehow, because each kernel upgrade will require tweaking with the kernel module.
The machine is running Linux Mint 19 (Tara, based upon Ubuntu Bionic, with kernel 4.15.0-20-generic). The NIC is Qualcomm Atheros QCA6174 802.11ac Wireless Network Adapter, Vendor/Product IDs 168c:003e.
Installing hostapd
# apt install hostapd
/etc/hostapd/hostapd.conf as follows:
macaddr_acl=0 auth_algs=1 ignore_broadcast_ssid=0 #Support older EAPOL authentication (version 1) eapol_version=1 # Uncomment these for base WPA & WPA2 support with a pre-shared key wpa=3 wpa_key_mgmt=WPA-PSK wpa_pairwise=TKIP rsn_pairwise=CCMP wpa_passphrase=mysecret # Customize these for your local configuration... interface=wlan0 hw_mode=a channel=52 ssid=mywifi country_code=GD
and /etc/default/hostapd as follows:
DAEMON_CONF="/etc/hostapd/hostapd.conf" DAEMON_OPTS=""
Then unmask the service by deleting /etc/systemd/system/hostapd.service (it’s a symbolic link to /dev/null).attempting to start the service with
5 GHz is not for plain people
Nov 27 21:14:04 hostapd[6793]: wlan0: IEEE 802.11 Configured channel (52) not found from the channel list of current mode (2) IEEE 802.11a Nov 27 21:14:04 hostapd[6793]: wlan0: IEEE 802.11 Hardware does not support configured channel
What do you mean it’s not supported? It’s on the list!
# iw list [ ... ] Band 2: [ ... ] Frequencies: * 5180 MHz [36] (17.0 dBm) (no IR) * 5200 MHz [40] (17.0 dBm) (no IR) * 5220 MHz [44] (17.0 dBm) (no IR) * 5240 MHz [48] (17.0 dBm) (no IR) * 5260 MHz [52] (24.0 dBm) (no IR, radar detection) * 5280 MHz [56] (24.0 dBm) (no IR, radar detection) * 5300 MHz [60] (24.0 dBm) (no IR, radar detection) * 5320 MHz [64] (24.0 dBm) (no IR, radar detection) * 5500 MHz [100] (24.0 dBm) (no IR, radar detection) * 5520 MHz [104] (24.0 dBm) (no IR, radar detection) * 5540 MHz [108] (24.0 dBm) (no IR, radar detection) * 5560 MHz [112] (24.0 dBm) (no IR, radar detection) * 5580 MHz [116] (24.0 dBm) (no IR, radar detection) * 5600 MHz [120] (24.0 dBm) (no IR, radar detection) * 5620 MHz [124] (24.0 dBm) (no IR, radar detection) * 5640 MHz [128] (24.0 dBm) (no IR, radar detection) * 5660 MHz [132] (24.0 dBm) (no IR, radar detection) * 5680 MHz [136] (24.0 dBm) (no IR, radar detection) * 5700 MHz [140] (24.0 dBm) (no IR, radar detection) * 5720 MHz [144] (24.0 dBm) (no IR, radar detection) * 5745 MHz [149] (30.0 dBm) (no IR) * 5765 MHz [153] (30.0 dBm) (no IR) * 5785 MHz [157] (30.0 dBm) (no IR) * 5805 MHz [161] (30.0 dBm) (no IR) * 5825 MHz [165] (30.0 dBm) (no IR) * 5845 MHz [169] (disabled) [ ... ]
The problem is evident when executing hostapd with the -dd flag (edit /etc/default/hostapd), in which case it lists the allowed channels. And none of the 5 GHz channels is listed. The underlying reason is the “no IR” part given in “iw list”, meaning no Initial Radiation, hence no access point allowed.
It’s very cute that the driver makes sure I won’t break the regulations, but it so happens that these frequencies are allowed in Israel for indoor use. My computer is indoors.
The way to work around this is to edit one of the driver’s sources, and use it instead.
Note that the typical error message when starting hostapd as a systemd service is quite misleading:
hostapd[735]: wlan0: IEEE 802.11 Configured channel (52) not found from the channel list of current mode (2) IEEE 802.11a hostapd[735]: wlan0: IEEE 802.11 Hardware does not support configured channel hostapd[735]: wlan0: IEEE 802.11 Configured channel (52) not found from the channel list of current mode (2) IEEE 802.11a hostapd[735]: Could not select hw_mode and channel. (-3) hostapd[735]: wlan0: interface state UNINITIALIZED->DISABLED hostapd[735]: wlan0: AP-DISABLED hostapd[735]: wlan0: Unable to setup interface. hostapd[735]: wlan0: interface state DISABLED->DISABLED hostapd[735]: wlan0: AP-DISABLED hostapd[735]: hostapd_free_hapd_data: Interface wlan0 wasn't started hostapd[735]: nl80211: deinit ifname=wlan0 disabled_11b_rates=0 hostapd[735]: wlan0: IEEE 802.11 Hardware does not support configured channel [ ... ] systemd[1]: hostapd.service: Control process exited, code=exited status=1 systemd[1]: hostapd.service: Failed with result 'exit-code'. systemd[1]: Failed to start Advanced IEEE 802.11 AP and IEEE 802.1X/WPA/WPA2/EAP Authenticator.
Not only is the message marked in red (as it also appears with journalctl itself) not related to the real reason, which is given a few rows earlier (configured channel not found), but these important log lines don’t appear in the output of “systemctl status hostapd”, as they’re cut out.
Preparing for kernel compilation
In theory, I could have compiled the driver only, and replaced the files in the /lib/modules directory. But I’m in for a minimal change, and minimal brain effort. So the technique is to download the entire kernel, compile things that don’t really need compilation. Then pinpoint the correction, and recompile only that.
Unfortunately, Ubuntu’s view on kernel compilation seems to be that it can only be desired for preparing a deb package. After all, who wants to do anything else? So it gets a bit off the regular kernel compilation routine.
OK, so first I had to install some stuff:
# apt install libssl-dev # apt install libelf-dev
Download the kernel (took me 25 minutes):
$ time git clone git://kernel.ubuntu.com/ubuntu/ubuntu-bionic.git
Compilation
The trick is to make the modules of a kernel that is identical to the running one (so there won’t be any bugs due to mismatches) and also match the kernel version string exactly (or the module won’t load).
Check out tag Ubuntu-4.15.0-20.21 (in my case, for 4.15.0-20-generic). This matches the kernel definition at the beginning of dmesg (and also the compilation date).
Follow this post and to prevent the “+” at the end of the kernel version.
Change directory to the kernel tree’s root, and copy the config file:
$ cp /boot/config-`uname -r` .config
Make sure the configuration is in sync:
$ make oldconfig
There will be some output, but no configuration question should be made — if that happens, it’s a sign that the wrong kernel revision has been checked out. In fact,
$ diff /boot/config-`uname -r` .config
should only output the difference in one comment line (the file’s header).
And then run the magic command:
$ fakeroot debian/rules clean
Don’t ask me what it’s for (I took it from this page), but among others, it does
cp debian/scripts/retpoline-extract-one scripts/ubuntu-retpoline-extract-one
and without it one gets the following error:
/bin/sh: ./scripts/ubuntu-retpoline-extract-one: No such file or directory
Ready to go, then. Compile only the modules. The kernel image itself is of no interest:
$ time make KERNELVERSION=`uname -r` -j 12 modules && echo Success
The -j 12 flag means running 12 processes in parallel. Pick your own favorite, depending in the CPU’s core count. Took 13 minutes on my machine.
Alternatively, compile just the relevant subdirectory. Quicker, no reason it shouldn’t work, but this is not how I did it myself:
$ make prepare scripts $ time make KERNELVERSION=`uname -r` -j 12 M=drivers/net/wireless/ath/ && echo Success
And then use the same command when repeating the compilation below, of course.
Modify the ath.c file
Following this post (more or less) , edit drivers/net/wireless/ath/regd.c and neutralize the following functions with a “return” immediately after variable declarations. Or replace them with functions just returning immediately.
- ath_reg_apply_beaconing_flags()
- ath_reg_apply_ir_flags()
- ath_reg_apply_radar_flags()
Also add a “return 0″ in ath_regd_init_wiphy() just before the call to wiphy_apply_custom_regulatory(), so the three calls to apply-something functions are skipped. In the said post, the entire init function was disabled, but I found that unnecessarily aggressive (and probably breaks something).
Note that there’s e.g. __ath_reg_apply_beaconing_flags() functions. These are not the ones to edit.
And then recompile:
$ make KERNELVERSION=`uname -r` modules && echo Success
This recompiles regd.c and ath.c, and the generates ath.ko. Never mind that the file is huge (2.6 MB) in comparison with the original one (40 kB). Once in the kernel, they occupy the same size.
As root, rename the existing ath.ko in /lib/modules/`uname -r`/kernel/drivers/net/wireless/ath/ to something else (with a non-ko extension, or it remains in the dependency files), and copy the new one (from drivers/net/wireless/ath/) to the same place.
Unload modules from kernel:
# rmmod ath10k_pci && rmmod ath10k_core && rmmod ath
and reload:
# modprobe ath10k_pci
And check the result (yay):
# iw list [ ... ] Frequencies: * 5180 MHz [36] (30.0 dBm) * 5200 MHz [40] (30.0 dBm) * 5220 MHz [44] (30.0 dBm) * 5240 MHz [48] (30.0 dBm) * 5260 MHz [52] (30.0 dBm) * 5280 MHz [56] (30.0 dBm) * 5300 MHz [60] (30.0 dBm) * 5320 MHz [64] (30.0 dBm) * 5500 MHz [100] (30.0 dBm) * 5520 MHz [104] (30.0 dBm) * 5540 MHz [108] (30.0 dBm) * 5560 MHz [112] (30.0 dBm) * 5580 MHz [116] (30.0 dBm) * 5600 MHz [120] (30.0 dBm) * 5620 MHz [124] (30.0 dBm) * 5640 MHz [128] (30.0 dBm) * 5660 MHz [132] (30.0 dBm) * 5680 MHz [136] (30.0 dBm) * 5700 MHz [140] (30.0 dBm) * 5720 MHz [144] (30.0 dBm) * 5745 MHz [149] (30.0 dBm) * 5765 MHz [153] (30.0 dBm) * 5785 MHz [157] (30.0 dBm) * 5805 MHz [161] (30.0 dBm) * 5825 MHz [165] (30.0 dBm) * 5845 MHz [169] (30.0 dBm) [ ... ]
The no-IR marks are gone, and hostapd now happily uses these channels.
Probably not: Upgrading firmware
As I first through that the the problem was an old firmware version, as discussed on this forum post, I went for upgrading it. These are my notes on that. Spoiler: It was probably unnecessary, but I’ll never know, and neither will you.
From the dmesg output:
[ 16.152377] ath10k_pci 0000:03:00.0: Direct firmware load for ath10k/pre-cal-pci-0000:03:00.0.bin failed with error -2 [ 16.152387] ath10k_pci 0000:03:00.0: Direct firmware load for ath10k/cal-pci-0000:03:00.0.bin failed with error -2 [ 16.201636] ath10k_pci 0000:03:00.0: qca6174 hw3.2 target 0x05030000 chip_id 0x00340aff sub 1a56:1535 [ 16.201638] ath10k_pci 0000:03:00.0: kconfig debug 0 debugfs 1 tracing 1 dfs 0 testmode 0 [ 16.201968] ath10k_pci 0000:03:00.0: firmware ver WLAN.RM.4.4.1-00079-QCARMSWPZ-1 api 6 features wowlan,ignore-otp crc32 fd869beb [ 16.386440] ath10k_pci 0000:03:00.0: board_file api 2 bmi_id N/A crc32 20d869c3
I was first mislead to think the firmware wasn’t loaded, but the later lines indicate it was acutally OK.
Listing the firmware files used by the kernel module:
$ modinfo ath10k_pci filename: /lib/modules/4.15.0-20-generic/kernel/drivers/net/wireless/ath/ath10k/ath10k_pci.ko firmware: ath10k/QCA9377/hw1.0/board.bin firmware: ath10k/QCA9377/hw1.0/firmware-5.bin firmware: ath10k/QCA6174/hw3.0/board-2.bin firmware: ath10k/QCA6174/hw3.0/board.bin firmware: ath10k/QCA6174/hw3.0/firmware-6.bin firmware: ath10k/QCA6174/hw3.0/firmware-5.bin firmware: ath10k/QCA6174/hw3.0/firmware-4.bin firmware: ath10k/QCA6174/hw2.1/board-2.bin firmware: ath10k/QCA6174/hw2.1/board.bin firmware: ath10k/QCA6174/hw2.1/firmware-5.bin firmware: ath10k/QCA6174/hw2.1/firmware-4.bin firmware: ath10k/QCA9887/hw1.0/board-2.bin firmware: ath10k/QCA9887/hw1.0/board.bin firmware: ath10k/QCA9887/hw1.0/firmware-5.bin firmware: ath10k/QCA988X/hw2.0/board-2.bin firmware: ath10k/QCA988X/hw2.0/board.bin firmware: ath10k/QCA988X/hw2.0/firmware-5.bin firmware: ath10k/QCA988X/hw2.0/firmware-4.bin firmware: ath10k/QCA988X/hw2.0/firmware-3.bin firmware: ath10k/QCA988X/hw2.0/firmware-2.bin
So which firmware file did it load? Well, there’s a firmware git repo for Atheros 10k:
$ git clone https://github.com/kvalo/ath10k-firmware.git
I’m not very happy running firmware found just somewhere, but the author of this Git repo is Kalle Valo, who works at Qualcomm. The Github account is active since 2010, and the files included in the Linux kernel are included there. So it looks legit.
Comparing files with the ones in the Git repo, which states the full version names, the files loaded were hw3.0/firmware-6.bin and another one (board-2.bin, I guess). The former went into the repo on Decemeber 18, 2017, which is more than a year after the problem in the forum post was solved. My firmware is hence fairly up to date.
Nevertheless, I upgraded to the ones added to the git firmware repo on November 13, 2018, and re-generated initramfs (not that it should matter — using lsinitramfs it’s clear that none of these firmware files are there). Did it help? As expected, no. But hey, now I have the latest and shiniest firmware:
[ 16.498706] ath10k_pci 0000:03:00.0: firmware ver WLAN.RM.4.4.1-00124-QCARMSWPZ-1 api 6 features wowlan,ignore-otp crc32 d8fe1bac [ 16.677095] ath10k_pci 0000:03:00.0: board_file api 2 bmi_id N/A crc32 506ce037
Exciting! Not.