Manipulating rfkill-using devices programmatically

Manipulating rfkill-using devices programmatically

If you are running Linux on a laptop, and using Wireless technologies such as WiFi or Bluetooth, you probably already came across these software switches that allow you enable/disable your wireless devices at the software level (in case you don’t have a hardware switch). If you’re using Ubuntu, this is what the Enable Wifi option does in your GNOME network menu.

Recently, I was developping a PAM (authentication) module relying on Bluetooth devices to authenticate system users, and I came across a little problem when I deployed it: when the chip wasn’t switched on (which is the default when I boot), then all authentication attempts failed, since the module couldn’t do its job. I tried to find a way to switch the chip on programatically (in C), and to my astonishment, I found no “obvious” way to do it… Everything I found online about this problem sent me back to the same thing: rfkill, which is the utility you can use on the command-line to manipulate your wireless devices’ locks. Basically:

Now, while I do like the simplicity of this thing (we could hardly make it easier to use), it really disturbed me that I couldn’t do something just as equally-simple in a program, in C. Indeed, no matter where I looked, there was no rfkill manipulation function/library/routine/tool/whatever. After looking at some straces and reading rfkill‘s source code, I finally wrote some code capable of manipulating my Bluetooth chip, and this is what this article is going to be about.

Reaching the rfkill switch device

rfkill-using devices can be reached using basic UNIX I/O system calls. Actually, open, read and write should be enough. The tricky part is finding what to send to the device, and what to expect from it…. But first things first: find your rfkill switch device. It should be located under /dev, and in my case, it’s called /dev/rfkill. Make sure you have enough permissions to receive/send data from/to that virtual device, and simply get a descriptor to it…

Once you’ve got that, we can get started.

rfkill events

The key header file here is rfkill.h and you should be able to get most of the notions just by reading it. This file provides a data structure called rfkill_event along with some enumerations such as rfkill_type, rfkill_operation and so on.

  •  32 bits for the rfkill device. Now, when I programmed with this, I skipped this parameter since I couldn’t really find a use for it in my case. I’ll talk about it a little further down.
  • 8 bits for the device type, basically: Bluetooth, Wifi, Wimax, GPS, … You can get a full list of types through the rfkill_type enumeration. Quoting the comment in the file:
    • RFKILL_TYPE_ALL: toggles all switches
    • RFKILL_TYPE_WLAN: switch is on a 802.11 wireless network device.
    • RFKILL_TYPE_BLUETOOTH: switch is on a bluetooth device.
    • RFKILL_TYPE_UWB: switch is on a ultra wideband device.
    • RFKILL_TYPE_WIMAX: switch is on a WiMAX device.
    • RFKILL_TYPE_WWAN: switch is on a wireless WAN device.
    • RFKILL_TYPE_GPS: switch is on a GPS device.
    • RFKILL_TYPE_FM: switch is on a FM radio device.
    • RFKILL_TYPE_NFC: switch is on an NFC device.
  • 8 bits for the operation to perform: add a device (RFKILL_OP_ADD), delete a device (RFKILL_OP_DEL), change a device’s state (RFKILL_OP_CHANGE), change all devices’ states (RFKILL_OP_CHANGE_ALL). These constants are defined in the rfkill_operation enumeration.
  • 8 bits for each switch (soft switch, hard switch). This is basically a boolean member, set it to 0 (unblocked) or 1 (blocked).

Getting a device’s state: read()

Reading from /dev/rfkill will provide you with the state of your devices. On calling the read() system call, the device will start sending you rfkill_event structures. You can read them quite easily with a standard I/O operation. Here’s an example to get information about local Bluetooth devices.

By the way, you might want to make your descriptor non-blocking, as your read system calls will hang once you’ve retrieved information about all devices (the rfkill driver does not EOF).

Changing a device’s state: write()

This time, you’ll need to prepare the rfkill_event structure yourself, and send it to the device driver. As I said earlier, I did not bother too much with the device index, since I was trying to switch all Bluetooth devices on (I only have one, so the distinction wasn’t worth bothering). However, this index refers to the same number you may find in rfkill‘s output in a terminal. This should allow you to target a specific switch, rather than a type of switches (which is what I’m doing here) :

An important note: this request we just sent wasn’t directly sent to the Bluetooth device. It was sent to the rfkill switch driver, which will then act on the device. For this reason, there will be a delay between the write system call and the actual state change. I strongly recommend you have your program wait until the state has changed before continuing. In my case, I checked the result of hci_get_route and hci_open_dev, which are related to Bluetooth device communication (see: Programming with BlueZ). Now, if you’re trying to manipulate a Wifi device or something else, you’ll probably need to find another way.

There you go! Now you can switch your wireless devices on and off in your programs! Now, this article is inspired by an experience I had with Bluetooth devices, but if you’d like to bring your input on other devices, or other kinds of rfkill features, don’t hesitate to post it in a comment!

See you later!