OpenLogi

0x1C00 · persistentRemappableAction

读写按控件和主机保存的非易失性 HID 动作分配。

persistentRemappableAction(0x1c00)将控件的 HID 动作保存在设备非易失性 存储中。它支持为每个控件和每个主机槽保存独立映射。它的 ControlId 与 0x1b04 共用。

当两个功能同时存在时,实时 0x1b04 分流优先于此处的持久映射。这个功能与 键盘相关,但 v0.8.15 的 OpenLogi 没有设备层、 agent、CLI 或 GUI 使用者。

**规格:**没有公开 Logitech 规格。布局由 Logi Options+ 2.8(静态分析)逆向得出。**交叉核对:**Solaar。**用于:**OpenLogi 未使用。

函数参考

PersistentRemappableActionFeature 实现 0x1c00。

方法

函数HID++ fn签名返回值
get_feature_info0()RemappableCapabilities
get_count1()RemappableInfo
get_cid_info2(index: u8, host: HostIndex)ControlId
get_persistent_action3(cid: ControlId, host: HostIndex)PersistentAction
set_persistent_action4(cid: ControlId, host: HostIndex, config: PersistentActionConfig)()
reset_persistent_action5(cid: ControlId, host: HostIndex)()
reset_to_factory_settings6(hosts: HostMask)()

所有方法都是 async。它们返回 Result<…, Hidpp20Error>。

类型

RemappableCapabilities

标志位说明
KEYBOARD_REPORT1 << 0键盘或小键盘输出。
MOUSE_BUTTONS1 << 1鼠标按钮输出。
X_DISPLACEMENT1 << 2鼠标 X 位移。
Y_DISPLACEMENT1 << 3鼠标 Y 位移。
VERTICAL_ROLLER1 << 4垂直滚轮输出。
HORIZONTAL_ROLLER1 << 5水平滚轮或 AC pan 输出。
CONSUMER_CONTROL1 << 6Consumer Control 输出。
INTERNAL_FUNCTION1 << 7内部函数输出。
POWER_KEY1 << 8电源键输出。

RemappableInfo 和 HostIndex

类型字段或变体说明
RemappableInfocount: u8控件 ID 数量。
RemappableInfohost_count: u8设备主机槽数量。
HostIndexCurrent当前设备主机。编码为 0xff。
HostIndexSlot(u8)从零开始的主机槽。

PersistentAction

字段类型说明
cidControlId已读取动作的控件。
hostHostIndex此映射的主机槽。
action_idActionId动作类型。
valueu16usage、位移或内部函数索引。
modifier_maskModifierMask键盘动作的修饰键。
statusPersistentActionStatus完整响应状态字节。

PersistentAction::is_remapped() 测试状态位 0。

PersistentActionConfig

字段类型说明
action_idActionId要保存的动作。
valueu16usage、位移或内部函数索引。
modifier_maskModifierMask键盘动作的修饰键。

ActionId

变体值说明
Empty0x00无输出。读取响应报告零值和零修饰键。
SendKeyboard0x01键盘或小键盘报告,HID usage page 7。
SendMouseButton0x02鼠标按钮报告,HID usage page 9。
SendXDisplacement0x03鼠标 X 位移,usage page 1 code 0x30。
SendYDisplacement0x04鼠标 Y 位移,usage page 1 code 0x31。
SendVerticalRoller0x05垂直滚轮,usage page 1 code 0x38。
SendHorizontalRoller0x06水平滚轮或 AC pan,usage page 12 code 0x0238。
SendConsumerControl0x07Consumer Control 报告,HID usage page 12。
ExecuteInternalFunction0x08内部函数。value 是函数索引。
SendPowerKey0x09电源键报告,HID usage page 1。

ModifierMask

标志位说明
LEFT_CTRL1 << 0左 Control。
LEFT_SHIFT1 << 1左 Shift。
LEFT_ALT1 << 2左 Alt。
LEFT_GUI1 << 3左 GUI。
RIGHT_CTRL1 << 4右 Control。
RIGHT_SHIFT1 << 5右 Shift。
RIGHT_ALT1 << 6右 Alt。
RIGHT_GUI1 << 7右 GUI。

PersistentActionStatus

标志位说明
REMAPPED1 << 0动作不同于出厂默认值。

PersistentActionStatus 保留位 1–7。这些位的含义未知。

HostMask

标志位说明
HOST_11 << 0主机 1。
HOST_21 << 1主机 2。
HOST_31 << 2主机 3。

事件

PersistentRemappableActionFeature 不实现事件监听器。

线格式

表格中的字节是逻辑 HID++ payload 字节。OpenLogi 发送 3 字节短请求,只有 fn 4 发送 16 字节长请求。读取方法使用扩展后的 16 字节响应视图,并忽略没有建模字段 的字节。

get_feature_info(fn 0)

请求:[0x00, 0x00, 0x00]。

字节字段备注
0–1RemappableCapabilitiesBE u16 位字段。
2–15—OpenLogi 不解析字段。

get_count(fn 1)

请求:[0x00, 0x00, 0x00]。

字节字段备注
0count控件 ID 数量。
1host_count主机槽数量。
2–15—OpenLogi 不解析字段。

get_cid_info(fn 2)

请求:[index, host, 0x00]。host 是 HostIndex 的 u8 值。Current 为 0xff,Slot(n) 为 n。

字节字段备注
0–1cidBE ControlId。
2–15—OpenLogi 不解析字段。

get_persistent_action(fn 3)

请求:[cid_hi, cid_lo, host]。cid_hi,cid_lo 是 BE u16 格式的 ControlId。

字节字段备注
0–1cidBE ControlId。
2hostHostIndex 字节。
3action_idActionId,包括 Empty = 0x00。
4–5valueBE u16。
6modifier_maskModifierMask 位。
7 bit 0REMAPPED已知的 PersistentActionStatus 位。Options+ 2.8;Solaar。
7 bits 1–7unknown status由 PersistentActionStatus 保留,含义未确认。Options+ 2.8;Solaar。
8–15—OpenLogi 不解析字段。

set_persistent_action(fn 4)

OpenLogi 发送 16 字节长请求。它在字节 7–15 写入零填充。

字节字段备注
0–1cidBE ControlId。
2hostHostIndex 字节。
3action_idActionId。
4–5valueBE u16。
6modifier_maskModifierMask 位。
7–15—OpenLogi 写入固定的零填充。

请求成功后,OpenLogi 返回 ()。它不解析响应体,也不读取动作来确认写入结果。

reset_persistent_action(fn 5)

请求:[cid_hi, cid_lo, host]。ControlId 使用 BE 顺序。OpenLogi 返回 (),且 不解析响应体。

reset_to_factory_settings(fn 6)

请求:[hosts_bits, 0x00, 0x00]。hosts_bits 是 HostMask。位 0、1 和 2 分别 选择 HOST_1、HOST_2 和 HOST_3。OpenLogi 返回 (),且不解析响应体。

OpenLogi 的用法

OpenLogi 为 0x1c00 注册类型化封装。v0.8.15 的 openlogi-device、agent、CLI 和 GUI 都不调用该封装。因此,OpenLogi 不会写入、重置、恢复或读回持久动作。

直接 Rust 调用者必须从 RemappableCapabilities 中选择受支持的动作。调用者也必须 选择目标 HostIndex。OpenLogi 不会在封装外增加能力门控、读改写层或写后读回。

未确认的细节

只有状态位 0 的含义已恢复。Options+ 2.8 和 Solaar 都保留完整状态字节,但没有 确定状态位 1–7 的含义。OpenLogi 在 PersistentActionStatus 中保留这些未知位。

协议证据来自静态分析。没有硬件 trace 验证状态位含义、0xff 当前主机 sentinel, 或设备如何处理 fn 4 填充。

Rust 用法

use hidpp::{
    device::Device,
    feature::{
        hosts_info::HostIndex,
        persistent_remappable_action::{
            ActionId, ModifierMask, PersistentActionConfig,
            PersistentRemappableActionFeature, RemappableCapabilities,
        },
    },
    protocol::v20::Hidpp20Error,
};

async fn map_first_control(mut device: Device) -> Result<(), Hidpp20Error> {
    device.enumerate_features().await?;
    let Some(feature) = device.get_feature::<PersistentRemappableActionFeature>() else {
        return Ok(());
    };

    let caps = feature.get_feature_info().await?;
    if !caps.contains(RemappableCapabilities::KEYBOARD_REPORT) {
        return Ok(());
    }
    let info = feature.get_count().await?;
    if info.count == 0 {
        return Ok(());
    }

    let host = HostIndex::Current;
    let cid = feature.get_cid_info(0, host).await?;
    let config = PersistentActionConfig {
        action_id: ActionId::SendKeyboard,
        value: 0x0006, // HID keycode for C.
        modifier_mask: ModifierMask::LEFT_CTRL,
    };
    feature.set_persistent_action(cid, host, config).await?;
    Ok(())
}

本页目录