Linux之无线RFKill子系统

关于RFKill

The rfkill subsystem provides a generic interface for disabling any radio
transmitter in the system. When a transmitter is blocked, it shall not
radiate any power.

The subsystem also provides the ability to react on button presses and
disable all transmitters of a certain type (or all). This is intended for
situations where transmitters need to be turned off, for example on
aircraft.

The rfkill subsystem has a concept of “hard” and “soft” block, which
differ little in their meaning (block == transmitters off) but rather in
whether they can be changed or not:

  • hard block
    read-only radio block that cannot be overridden by software

  • soft block
    writable radio block (need not be readable) that is set by the system software.

The rfkill subsystem has two parameters, rfkill.default_state and
rfkill.master_switch_mode, which are documented in
admin-guide/kernel-parameters.rst.

rfkill 子系统提供了一个通用接口,用于禁用系统中的任何无线电发射器。当一个发射器被阻止时,它不应辐射任何功率。
子系统还提供了在按键按下时做出反应并禁用特定类型(或所有)发射器的能力。这旨在用于需要关闭发射器的情况,例如在飞机上。
rfkill 子系统有一个“硬”阻塞和“软”阻塞的概念,它们在含义上差别不大(阻塞 == 发射器关闭),而在于它们是否可以被更改:

  • 硬阻塞(hard block)
    只读的无线电阻塞,不能被软件覆盖
  • 软阻塞(soft block)
    可写的无线电阻塞(不必可读),由系统软件设置。

rfkill 子系统有两个参数,rfkill.default_staterfkill.master_switch_mode,它们在 admin-guide/kernel-parameters.rst 中有文档记录。

主要实现

rfkill 子系统由三个主要组件组成:

  • rfkill 核心(net/rfkill/core.c)
  • 已弃用的 rfkill-input 模块net/rfkill/input.c一个输入层处理程序,正在被用户空间策略代码取代)
  • rfkill 驱动程序(各个无线子系统及自定义的驱动程序)

rfkill 核心为内核驱动程序提供了注册rfkill的 API、打开和关闭它的方法,并让系统了解设备上可能实现的硬件禁用状态。

rfkill 核心代码还会通知用户空间状态变化,并提供用户空间查询当前状态的方法。具体看下面的 “向上” 部分。

当设备被硬阻塞(通过调用 rfkill_set_hw_state() 或从 query_hw_block 获取)时,set_block() 将被调用以进行额外的软件阻塞,但驱动程序可以忽略该方法调用,因为它们可以使用 rfkill_set_hw_state() 函数的返回值来同步软件状态,而不是跟踪 set_block() 的调用。事实上,除非硬件实际分别跟踪软阻塞和硬阻塞,否则驱动程序应使用 rfkill_set_hw_state() 的返回值。

向下

RFkill是怎么去控制各个无线设备的呢?

在各个无线设备驱动中,如蓝牙、Wi-Fi等,都会实现rfkill接口,用于控制设备的启用和禁用。

例如在蓝牙子系统中 net/bluetooth/hci_core.c
wireless子系统中 net/wireless/core.c
nfc子系统中 net/nfc/core.c,或在自己实现的驱动中,

只需要通过rfkill_allocrfkill_register来注册rfkill设备,然后基本上只需要实现rfkill_ops操作集中的.set_block接口即可,在这个接口里就可以对具体的设备设备进行下电或其他电源管理相关操作,当调用rfkill_set_block时,就会调用到具体设备的该接口,从而实现控制设备的启用和禁用。

向上

推荐的用户空间接口是 /dev/rfkill,它是一个杂项字符设备,允许用户空间获取和设置 rfkill 设备及设备组的状态。它还会通知用户空间设备的添加和移除。该 API 是一个简单的读/写 API,定义在 linux/rfkill.h 中,包含一个 ioctl,用于在过渡期间关闭内核中已弃用的输入处理程序。

除了这个 ioctl,与内核的通信还通过读取和写入 struct rfkill_event 实例来完成。在这个结构中,软阻塞和硬阻塞被正确分离(与 sysfs 不同,见下文),用户空间能够获取系统中所有 rfkill 设备的一致快照。此外,还可以将所有 rfkill 驱动程序(或指定类型的所有驱动程序)切换到一种状态,该状态也会更新热插拔设备的默认状态。

应用程序打开 /dev/rfkill 后,可以读取所有设备的当前状态。可以通过轮询描述符以获取热插拔或状态更改事件,或者通过监听 rfkill 核心框架发出的 uevents 来获取更改。

此外,每个 rfkill 设备都在 sysfs 中注册并能发出 uevents事件。

rfkill 设备发出的 uevents(动作为 "change")会设置以下环境变量:

RFKILL_NAME
RFKILL_STATE
RFKILL_TYPE

这些变量的内容对应于上面解释的 "name""state""type"sysfs 文件。

更多详细信息,请参阅 Documentation/ABI/stable/sysfs-class-rfkill

基本使用

Linux 提供了 rfkill 命令行工具,用于管理射频设备的状态。

常用命令如下:

# 列出所有射频设备及状态
rfkill list

# 禁用某个设备(例如 蓝牙):
rfkill block bluetooth
# 启用某个设备(例如 Wi-Fi):
rfkill unblock wifi

# 禁用或使能所有设备:
rfkill block all
rfkill unblock all

主要应用场景

  1. 笔记本物理无线开关:
    当用户按下笔记本上的无线开关时,硬件触发硬阻塞,RFKill 核心禁用所有无线设备。

  2. 飞行模式:
    用户空间工具(如 NetworkManager)通过 RFKill 接口禁用所有射频设备,模拟飞行模式。

  3. 节能模式:
    在系统休眠或电池电量低时,禁用无线设备以节省电量。

参考