AprilNEA / AprilNEA/OpenLogi

fixture contribute: step 2 always fails on Unifying receivers on Linux (DJ reports rejected as malformed)

Open
#1,349 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
21k
Forks
675
Avg merge
2d 5h
Merged PRs (30d)
172

Description

### Summary

`openlogi fixture contribute` step 2 can never succeed for a Unifying receiver on Linux while `hid-logitech-dj` is bound (the default). The receiver's hidraw node carries DJ reports (`0x20`/`0x21`) alongside HID++, `HidppMessage::read_raw` classifies them as `ChannelObservation::MalformedIncomingReport`, and the sanitizer rejects the whole cassette on any such observation.

### Environment

- Arch Linux x86_64, `master` at `e846e6f`, release build
- Unifying receiver `046d:c52b`, bound to `logitech-djreceiver`; `hid_logitech_dj` and `hid_logitech_hidpp` loaded
- Device: MX Ergo (wpid `406f`), slot 1
- The receiver's report descriptor declares report IDs `0x10`, `0x11`, `0x20`, `0x21`

### Reproduce

```
openlogi fixture contribute --id mx-ergo-001 --name "MX Ergo" \
--device "MX Ergo Multi-Device Trackball " --output fixtures/devices/mx-ergo-001
# step 1 (agent running) succeeds; stop the agent, rerun:
recording 1/8: feature-table
channel candidate 1: sanitizer rejected
audit: Unifying receiver identity replaced 1 occurrence(s) with synthetic 4F4C5201
rejection: incoming report was malformed
rejection: incoming report was malformed
Error: no sanitized channel candidate reproduced the captured semantic observation and completed strict replay; no fixture was written
```

Reproduced 4/4 times.

### Evidence

A passive reader on the same hidraw node during step 2, with the trackball untouched:

```
DJ reports by id/type: {'0x20/type0x41': 4, '0x20/type0x02': 443}
first 0x20/type0x41 at t+1.01s: 200141016f401e0000000400000000
first 0x20/type0x02 at t+7.47s (after the capture had already failed — me moving the mouse)
```

So the rejected reports are the DJ "device paired" notifications (`0x20 … 41`) that appear during discovery, not pointer motion. Any pointer motion during a capture would add DJ `0x02` mouse reports on the same node too. The kernel's `hid-logitech-hidpp` also talks to the device on this node (sw id `0x01`).

### Where

- `crates/openlogi-hidpp/src/channel.rs` `read_loop`: any report that fails `HidppMessage::read_raw` becomes `MalformedIncomingReport` — a report with a non-HID++ report ID is treated the same as a truncated HID++ report.
- `crates/openlogi-hid/src/recording/cassette/sanitizer.rs` `unassociated_rejection`: `MalformedIncomingReport` → `CassetteRejectionReason::MalformedIncomingReport`.

The normal channel already drops these reports (`report not HID++ — dropped`), so replay never needs them.

### Possible direction

Separate "another protocol's report on this node" (report ID not `0x10`/`0x11`) from "HID++ report ID with invalid framing", and have the recorder drop the former instead of rejecting. Happy to send a PR if that direction is acceptable, and to retest on this hardware either way.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with read_loop in crates/openlogi-hidpp/src/channel.rs and then inspect unassociated_rejection in crates/openlogi-hid/src/recording/cassette/sanitizer.rs. Compare the normal channel's handling of non-HID++ reports, and verify that fixture contribute can complete for the Unifying receiver without rejecting DJ reports while still rejecting malformed HID++ reports.

Written by the indexing model from the issue text.

Assessment

Tech stack
linux, rust
Domain
embedded-iot, operating-systems
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
74/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.