OpenLogi

接收器

无线 Logitech 设备配对所用的 USB 接收器,通过 HID++ 1.0 寄存器驱动,以 USB 厂商/产品 ID 识别。

接收器(receiver) 是无线 Logitech 设备配对所用的 USB 适配器。OpenLogi 在其 USB/HID 通道上检测接收器,再枚举与之配对的设备。

与设备功能(走 HID++ 2.0)不同,接收器通过 HID++ 1.0 寄存器驱动,并且始终以 设备索引 0xFFRECEIVER_DEVICE_INDEX)寻址。识别只看 USB 厂商/产品 ID —— openlogi-hidppreceiver::detect() 将通道的 VID/PID 与已知集合匹配,返回一个 Receiver

实现状态

vendor 进来的 hidpp crate 对接收器的支持刻意保守 —— 公开文档很稀少。Logi Bolt 最完整(基于真实硬件开发并验证);Unifying 覆盖发现与枚举,但配对/管理 面较薄,欢迎硬件实测反馈。

检测

接收器VID:PID传输方式
Logi Bolt046D:C548HID++ 1.0 寄存器(基于 BLE)
Unifying046D:C52B046D:C532046D:C537046D:C539HID++ 1.0 寄存器(Unifying / DJ)
Lightspeed046D:C53F046D:C547Unifying 寄存器,G 系列适配器

receiver::detect(chan) 对已知适配器返回 Some(Receiver),否则返回 None

G 系列设备附带的 Lightspeed 适配器应答与 Unifying 相同的 HID++ 1.0 寄存器, 因此检测、枚举、路由、配对都走 Unifying 代码路径 —— 只有面向用户的名称不同 (Lightspeed Receiver)。C53F 是 G305 等无线鼠标的 nano 接收器,已用 G305 实测(配对设备 wpid 0x4074);C547 随 G915 键盘、G502 X LIGHTSPEED 等较新设备 出货,已用 G915 实测(wpid 0x407c)。C539 是 Lightspeed 游戏接收器,因按 Unifying 路由而与 Unifying 的 PID 列在一起。C537 是 G602 附带的 Nano 接收器, 应答相同的枚举与配对信息寄存器,同样按 Unifying 路由。

通用 API

Receiver 枚举包装具体的接收器类型,暴露共享接口:

说明
Receiver::Bolt(_) / Receiver::Unifying(_)检测到的接收器类型。
name()人类可读的名称(如 "Logi Bolt Receiver")。
get_unique_id()唯一标识该接收器的字符串(序列号或等价物)。
RECEIVER_DEVICE_INDEX0xFF在通道上寻址接收器所用的设备索引。

错误以 ReceiverError 呈现 —— UnknownReceiver,或包装后的 HID++ 1.0 Protocol 错误。

Logi Bolt

现款 Logitech 接收器。Bolt 基于 BLE,最多配对 6 台设备,配对前用 passkey 认证新设备。bolt::Receiver 提供:

函数用途
count_pairings()当前已配对设备数(含离线设备)。
collect_paired_devices()枚举全部已配对设备(Vec<DeviceConnection>)。
get_device_pairing_information(index)单个槽位的配对信息(DevicePairingInformation)。
get_device_codename(index)设备的代号 / 名称。
get_notification_state() / set_notification_state(..)读取 / 启用接收器通知。
trigger_device_arrival()为全部已配对设备重发 DeviceConnection 事件(枚举用)。
discover_devices(timeout) / cancel_device_discovery()开始 / 停止发现可配对设备(≤ 60 秒)。
pair_device(slot, address, authentication, entropy)开始配对一台已发现的设备。
unpair_device(index)移除一个配对。
listen()订阅接收器 Event

配对流程

  1. discover_devices(Some(secs)) —— 接收器为附近每台设备发出 Event::DeviceDiscoveryDeviceDetails(携带设备 addresskindwpidauthentication)与 Event::DeviceDiscoveryDeviceName
  2. pair_device(slot, address, authentication, entropy) —— entropy 设定 passkey 复杂度(对鼠标而言,是用户需要输入的左/右键点击次数)。
  3. 接收器完成配对,发出 Event::DeviceConnection

事件(bolt::Event

变体触发时机
DeviceConnection(DeviceConnection)设备连接 / 断开(需已启用无线通知)。
DeviceDiscoveryStatus { discovery_enabled }发现模式开关变化。
DeviceDiscoveryDeviceDetails { counter, kind, wpid, address, authentication }发现一台设备(配对需要这些详情与地址)。
DeviceDiscoveryDeviceName { .. }已发现设备的名称。

DeviceKind 覆盖 KeyboardMouseNumpadPresenterRemoteTrackballTouchpadTabletGamepadJoystickHeadset(外加 Unknown)。

Unifying

上一代接收器。Unifying 通过私有的 Unifying / DJ 协议最多配对 6 台设备;已配对 设备一旦按槽位索引寻址,就说 HID++ 2.0。unifying::Receiver 提供:

函数用途
count_pairings()已配对设备数(含离线设备)。
get_receiver_info()接收器信息,含 pairing_slotsReceiverInfo)。
get_device_pairing_information(index)单个槽位的配对信息(DevicePairingInformation)。
trigger_device_arrival()为每个已配对槽位重播连接事件,离线槽位也在内(启动时枚举)。
get_unique_id()接收器序列号 / 唯一 id。
listen()订阅接收器 Event

0x41 连接通知

两种接收器都通过同一条主动上报的 HID++ 1.0 通知(sub-id 0x41)报告设备状态。 已配对设备连接或断开时它会触发;trigger_device_arrival() 则让接收器为每个 已配对槽位重播一条 —— 设备已关机或超出范围的槽位同样会收到,只是标记为离线。 通知的设备索引就是设备的配对槽位,payload 为:

字节内容
0协议类型(eQUAD 变体)。
1device-info 字节(见下)。
2–3Wireless PID,小端序。

device-info 字节打包了设备类型与四个状态标志位:

掩码含义
0–30x0F设备类型(0x1 键盘、0x2 鼠标……)。
40x10Software present —— 主机曾通过寄存器 0x00SOFTWARE_PRESENT 通知标志声明驱动软件在场。
50x20Link encrypted —— eQUAD 无线链路使用配对时建立的加密密钥。
60x40Link not established —— 置位表示设备离线。语义取反。
70x80后随 payload 数据包。

bit 5 和 bit 4 长得像,说的却是两个世界。bit 5 是 2.4 GHz 链路自身的属性:设备与 接收器之间的流量是否加密,在配对时就已决定。它关乎安全 —— 未加密的链路可以被空中 嗅探和注入(2016 年的 MouseJack 攻击打的正是这类链路),所以 Solaar 在配对结束后 发现该位为 0 会向用户告警。Bolt 的链路设计上恒为加密。bit 4 与无线电毫无关系:它 回显的是主机写入寄存器 0x00 的一个标志,声明"有软件在接管设备的部分行为", 设备固件据此决定(比如)手势键是发出转移报告(diverted report)还是退回固件内置 行为。

bit 4 不是加密

这两个位很容易混淆:OpenLogi vendor 的 hidpp fork 一直把 bit 4 当作加密标志读, 直到与 Solaar 的解码器对照才发现;而且 Unifying 与 Bolt 的布局完全一致 —— 并不存在 需要区别对待的按接收器差异。

一段真实抓包,短消息 payload 04 62 69 40:协议 0x04(eQUAD),device-info 0x62 = 0110 0010 —— bit 6 置位(离线)、bit 5 置位(配对时已建立链路加密)、 bit 4 为 0、类型 0x2(鼠标)—— wpid 0x4069,即 MX Master 2S。这就是离线槽位 重播的样子:身份完好,链路断开。

枚举已配对设备(Rust)

use std::sync::Arc;

use hidpp::{
    channel::HidppChannel,
    receiver::{self, Receiver},
};

// chan: 绑定到接收器 HID 接口的 Arc<HidppChannel>。
let Some(rx) = receiver::detect(Arc::clone(&chan)) else {
    return; // 不是已知的 Logitech 接收器
};
println!("{} — {}", rx.name(), rx.get_unique_id().await?);

match rx {
    Receiver::Bolt(bolt) => {
        for dev in bolt.collect_paired_devices().await? {
            // dev: bolt::DeviceConnection —— 一台已配对设备
            let _ = dev;
        }
    }
    Receiver::Unifying(uni) => {
        let events = uni.listen();
        uni.trigger_device_arrival().await?; // 重发 DeviceConnection 事件
        while let Ok(event) = events.recv().await {
            // 处理 unifying::Event::DeviceConnection { .. }
            let _ = event;
            break;
        }
    }
    _ => {}
}

无接收器

蓝牙直连与有线设备作为各自独立的清单枚举,不涉及接收器。参见 连接设备

本页目录