1. Introduction
Linux has been a valuable operating system for embedded, small, and similarly low-resource systems. Since such systems are often designed to fit specific purposes and may be away from networking infrastructure, equipping them with wireless communication devices is common. Due to the potential lack of easy physical access, having a way to toggle these onboard devices remotely can be critical.
In this tutorial, we explore the main way that many Linux systems enable users to control the state of a wireless device. First, we talk about hard and soft transmitter blocking. After that, we overview the kernel subsystem that handles radio frequency devices. Next, we go over the userspace component and its commands. Finally, we explain a deprecated functionality and its potential implications.
In this tutorial, we tested the code on Debian 12 (Bookworm) with GNU Bash 5.2.15. It should work in most POSIX-compliant environments.
2. Hard and Soft Block
Importantly, there are two behaviors when turning off (blocking) transmitters for a given device:
- hard block: read-only, not software-controllable
- soft block: writable, software-controllable
Thus, for most hardware, which doesn’t have different physical implementations for soft and hard blocks, checking the current state via a central source is important for both software and the kernel.
3. RF Kill Switch Subsystem
Linux provides the RF Kill Switch subsystem for controlling wireless transmitters’ input and output power, such as a WLAN card, other Wi-Fi devices, Bluetooth, infrared, and similar. Here, RF stands for [R]adio [F]requency.
There are three components in the RF Kill Switch subsystem:
- core
- rfkill-input module
- drivers
Notably, the rfkill-input is an input layer in the process of deprecation.
This organization enables kernel drivers to register their on, off, and status check procedures with the subsystem API. In addition, the RF Kill Switch handles state changes and communication with registered drivers.
4. Userspace rfkill
The main way userspace applications work with the RF Kill Switch subsystem is the /dev/rfkill character device. It provides data through all currently registered transmitter drivers.
Depending on the implementation, there are different ways to check for state changes:
- poll device descriptor for hot plug or state change event
- listen for an uevent by the RF Kill Switch subsystem core
The events include the RFKILL_NAME and RFKILL_TYPE variables for filtering changes based on transmitter name and type, respectively.
5. rfkill Command
The client utility for working with the RF Kill Switch subsystem is rfkill. It’s a fairly simple command that can check, enable, or disable wireless devices through their registered drivers and procedures.
5.1. Check Current Device List
For example, we can list every device the subsystem is aware of, along with its current hard and soft block status:
$ rfkill list
0: phy0: Wireless LAN
Soft blocked: no
Hard blocked: no
1: phy1: Wireless LAN
Soft blocked: no
Hard blocked: no
2: phy2: Wireless LAN
Soft blocked: no
Hard blocked: no
3: hci0: Bluetooth
Soft blocked: no
Hard blocked: no
This way, we also get to know the names of each along with their identifier (ID).
5.2. Control Device
If we want to control a device like phy1, we can use its ID of 1 with the block, unblock, or toggle subcommands:
$ rfkill block 1
$ rfkill list 1
1: phy1: Wireless LAN
Soft blocked: yes
Hard blocked: no
As expected, after we block, the list status changes. Naturally, we can’t Hard block with the rfkill software.
5.3. Check Events
To avoid scanning and filtering through the system logs manually, we can use the event flag:
$ rfkill event
2024-01-10 10:10:05,553208-05:00: idx 0 type 1 op 0 soft 0 hard 0
2024-01-10 10:10:05,153261-05:00: idx 1 type 1 op 0 soft 1 hard 0
2024-01-10 10:10:05,743075-05:00: idx 2 type 1 op 0 soft 0 hard 0
2024-01-10 10:10:11,266607-05:00: idx 1 type 1 op 2 soft 0 hard 0
Thus, we also listen for events in real-time.
6. Input Handling
Previously, the rfkill-input feature of the RF Kill Switch subsystem could toggle a switch when a specific keyboard key is pressed. Yet, the rfkill-input feature is now being deprecated.
Instead, userspace applications should invoke ioctl on /dev/rfkill with RFKILL_IOC_NOINPUT to disable this handler. When this happens, the kernel generates a message in the system log:
[ 66.601430] rfkill: input handler disabled
Notably, this is a fair warning, which may not continue in future kernel versions, once the deprecated rfkill-input isn’t part of them. Of course, at that point, all applications should handle the key press events independently.
7. Summary
In this article, we discussed the RF Kill Switch kernel subsystem and its userspace interface.
In conclusion, although it has a simple organization, the RF Kill Switch subsystem is being made even more minimalistic in sync with the UNIX doctrine do one thing, but do it well or one problem, one program.