Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions crates/openlogi-hid/src/route.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,14 @@ pub const BOLT_PIDS: &[u16] = &[0xc548];
/// Unifying, so it routes as [`DeviceRoute::Unifying`].
pub const UNIFYING_PIDS: &[u16] = &[0xc52b, 0xc532, 0xc539];

/// USB product IDs that identify Logitech Lightspeed nano receivers — the
/// receivers bundled with G-series wireless mice such as the G305. They speak
/// the same HID++ 1.0 receiver register protocol as Unifying, so they are
/// enumerated, routed, and paired through the Unifying code path; only the
/// user-facing receiver name (see [`receiver_display_name`]) differs.
pub const LIGHTSPEED_PIDS: &[u16] = &[0xc53f];
/// USB product IDs that identify Logitech Lightspeed receivers — the
/// receivers bundled with G-series wireless devices. `0xc53f` is the nano
/// receiver of wireless mice such as the G305; `0xc547` ships with newer
/// G-series devices such as the G915 keyboard and the G502 X LIGHTSPEED.
/// They speak the same HID++ 1.0 receiver register protocol as Unifying, so
/// they are enumerated, routed, and paired through the Unifying code path;
/// only the user-facing receiver name (see [`receiver_display_name`]) differs.
pub const LIGHTSPEED_PIDS: &[u16] = &[0xc53f, 0xc547];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Lightspeed child nodes remain unfiltered

On Linux, adding 0xc547 only to LIGHTSPEED_PIDS leaves receiver-child filtering unchanged because is_receiver_child_sysfs_path checks only BOLT_PIDS and UNIFYING_PIDS. Child HID nodes beneath this receiver are therefore treated as direct-device candidates, adding unnecessary probes and potentially creating failed or duplicate inventory entries.

Knowledge Base Used:

Fix in Codex Fix in Claude Code


/// Whether `product_id` is a receiver that speaks the Unifying HID++ 1.0
/// register protocol — a Unifying receiver proper, or a protocol-compatible
Expand Down Expand Up @@ -336,6 +338,7 @@ mod tests {
#[test]
fn lightspeed_receiver_has_its_own_display_name() {
assert_eq!(receiver_display_name(0xc53f), "Lightspeed Receiver");
assert_eq!(receiver_display_name(0xc547), "Lightspeed Receiver");
assert_eq!(receiver_display_name(0xc52b), "Unifying Receiver");
}

Expand Down
1 change: 1 addition & 0 deletions crates/openlogi-hid/src/transport.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ fn is_receiver_child_sysfs_path(path: &str) -> bool {
crate::BOLT_PIDS
.iter()
.chain(crate::UNIFYING_PIDS.iter())
.chain(crate::LIGHTSPEED_PIDS.iter())
.any(|&pid| {
let marker = format!(":{LOGITECH_VID:04X}:{pid:04X}.");
// A parent component contains the marker followed by at least one
Expand Down
8 changes: 8 additions & 0 deletions crates/openlogi-hid/src/transport/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ const UNIFYING_RECEIVER: &str = "/sys/devices/pci0000:00/0000:00:14.0/usb3/3-5/3
// Sysfs path: child of Bolt receiver
const BOLT_CHILD: &str = "/sys/devices/pci0000:00/0000:00:14.0/usb3/3-5/\
0003:046D:C548.0001/0003:046D:B037.0002";
// Sysfs path: child of a Lightspeed receiver (a G915, wpid 407C, on c547)
const LIGHTSPEED_CHILD: &str = "/sys/devices/pci0000:00/0000:00:14.0/usb3/3-5/\
0003:046D:C547.0003/0003:046D:407C.0004";
// Sysfs path: unrelated non-Logitech device
const UNRELATED: &str = "/sys/devices/pci0000:00/0000:00:15.0/i2c-0/0018:06CB:CE67.0001";

Expand All @@ -103,6 +106,11 @@ fn child_of_bolt_receiver_is_detected() {
assert!(is_receiver_child_sysfs_path(BOLT_CHILD));
}

#[test]
fn child_of_lightspeed_receiver_is_detected() {
assert!(is_receiver_child_sysfs_path(LIGHTSPEED_CHILD));
}

#[test]
fn unrelated_device_is_not_a_child() {
assert!(!is_receiver_child_sysfs_path(UNRELATED));
Expand Down
14 changes: 9 additions & 5 deletions crates/openlogi-hidpp/src/receiver/unifying.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,20 @@ use crate::{
/// receivers.
///
/// `046d:c539` is the Lightspeed gaming receiver; `046d:c53f` is the Lightspeed
/// nano receiver (bundled with G-series wireless mice such as the G305). Both
/// answer the same HID++ 1.0 registers (pairing count, connection state, pairing
/// information) as Unifying receivers. Callers that surface a user-facing
/// receiver name label Lightspeed PIDs separately (see `openlogi-hid`).
/// `0xc53f` was verified against a G305 (paired device wpid `0x4074`).
/// nano receiver (bundled with G-series wireless mice such as the G305);
/// `046d:c547` is the Lightspeed receiver bundled with newer G-series devices
/// such as the G915 keyboard and the G502 X LIGHTSPEED. All answer the same
/// HID++ 1.0 registers (pairing count, connection state, pairing information)
/// as Unifying receivers. Callers that surface a user-facing receiver name
/// label Lightspeed PIDs separately (see `openlogi-hid`).
/// `0xc53f` was verified against a G305 (paired device wpid `0x4074`);
/// `0xc547` against a G915 (paired device wpid `0x407c`).
pub const VPID_PAIRS: &[(u16, u16)] = &[
(0x046d, 0xc52b),
(0x046d, 0xc532),
(0x046d, 0xc539),
(0x046d, 0xc53f),
(0x046d, 0xc547),
];

/// All known registers of the Unifying receiver.
Expand Down