Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
4c92490
docs: record iRacing rev-light protocol findings
PeposCJ Jul 28, 2026
1f5facb
feat: add guarded RS50 Dynamic OLED bridge
PeposCJ Jul 28, 2026
974fe5e
feat: add bounded RS50 telemetry stream
PeposCJ Jul 28, 2026
bbeaa6c
feat: validate first RS50 live telemetry trial
PeposCJ Jul 28, 2026
b8ee3a9
docs: record RS50 exclusive-stream side effects
PeposCJ Jul 28, 2026
36bf5bb
docs: identify RS50 FFB reset sequence
PeposCJ Jul 28, 2026
369d434
feat: add offline shared HID++ display protocol
PeposCJ Jul 28, 2026
1b569e3
feat: add disconnected RS50 shared HID++ transport
PeposCJ Jul 28, 2026
a99e11e
feat: add armed RS50 shared HID++ one-shot
PeposCJ Jul 28, 2026
ef1fefd
fix: accept distinct RS50 HID collection paths
PeposCJ Jul 28, 2026
8730106
docs: validate RS50 shared HID++ one-shot
PeposCJ Jul 28, 2026
3c82b64
feat: add bounded RS50 shared HID++ stream
PeposCJ Jul 28, 2026
b1e7aa7
docs: validate bounded RS50 shared HID++ stream
PeposCJ Jul 28, 2026
2b25c2c
feat: add stationary RS50 iRacing coexistence trial
PeposCJ Jul 28, 2026
8b57dd4
docs: validate RS50 iRacing shared-HID coexistence
PeposCJ Jul 28, 2026
bb9692a
feat: add bounded RS50 shared-HID++ telemetry trial
PeposCJ Jul 28, 2026
cb8486a
feat: add fixed RS50 OLED layout gallery
PeposCJ Jul 28, 2026
a9522bb
docs: record RS50 OLED font geometry
PeposCJ Jul 28, 2026
435a7d6
tools: add offline RS50 OLED font inspector
PeposCJ Jul 28, 2026
7972647
Add project safety and interoperability instructions
PeposCJ Jul 29, 2026
d588227
feat: classify RS50 OLED layout semantics
PeposCJ Jul 29, 2026
20bcbab
test: harden RS50 stationary telemetry trial
PeposCJ Jul 29, 2026
0002e8a
docs: record RS50 stationary telemetry result
PeposCJ Jul 29, 2026
a361992
feat: add bounded RS50 OLED transaction transcript
PeposCJ Jul 29, 2026
1c5ed00
docs: define direct USB capture for RS50 J2
PeposCJ Jul 29, 2026
46ae36d
docs: validate RS50 stationary telemetry evidence
PeposCJ Jul 29, 2026
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
4 changes: 4 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
BasedOnStyle: Microsoft
IndentWidth: 4
ColumnLimit: 80
SortIncludes: CaseSensitive
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,13 @@ bld/
# Potentially sensitive USB and HID diagnostic captures
*.pcap
*.pcapng
*.dfu
*.mov
*.mp4
*_capture.zip
rs50_passive_*.txt
[Dd]iagnostic-[Cc]aptures/
rs50-build-j-transcript-*.jsonl

# Visual Studio 2015/2017 cache/options directory
.vs/
Expand Down
186 changes: 186 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
# Codex Project Instructions

## Legal, IP, Reverse-Engineering, and Repository Safety Rules

This project is an independent interoperability project for Logitech racing hardware. Treat legal/IP hygiene as a first-class engineering constraint.

### Primary objective

Implement functionality using independently written code and experimentally verified protocol behavior.

Prefer documented or physically observed device behavior over copying vendor implementations.

### Logitech proprietary material

Do NOT commit, redistribute, embed, or reproduce proprietary Logitech material, including:

- G HUB binaries or DLLs
- Logitech firmware or DFU files
- proprietary SDK binaries
- extracted firmware assets
- fonts or glyph bitmap data extracted from firmware
- graphics or icons extracted from Logitech software/firmware
- large disassembly or decompilation listings
- substantial reconstructed vendor source code
- raw proprietary resources
- complete USB captures that may contain unrelated or sensitive traffic

Local proprietary files may be inspected for interoperability research when necessary, but they must remain outside the repository.

Document findings as independently written technical descriptions, protocol structures, behavioral observations, hashes, addresses when useful for reproducibility, and small factual byte sequences when needed to explain interoperability.

Do not turn decompiled vendor implementation into source code by transcription.

### Clean implementation principle

When implementing a discovered protocol:

1. derive the public implementation from documented protocol behavior;
2. write new source code from scratch;
3. expose only the minimum functionality required by LogiDynamicDash;
4. do not copy vendor class structures, naming, algorithms, or implementation details unless they are necessary functional interface facts;
5. separate evidence/research from production implementation.

Facts such as feature IDs, function IDs, packet formats, field lengths, layout IDs, acknowledgement behavior, USB interfaces, timing constraints, and observed device behavior may be documented as interoperability information.

### RS50 OLED protocol

The currently confirmed interoperability target is public HID++ feature:

`0x8130 — Display Game Data`

Do not assume a runtime feature index. Always discover `0x8130` through the HID++ Root feature before using it.

The confirmed protocol currently includes typed firmware-rendered layouts rather than a host framebuffer.

Do not claim arbitrary pixel/framebuffer control unless independently demonstrated.

Do not probe undocumented or unknown HID++ functions by guessing.

Do not fuzz the RS50.

Do not perform firmware writes, firmware modification, bootloader operations, arbitrary HID writes, or speculative commands.

### Hardware safety

Hardware experiments must remain narrowly scoped and fail closed.

Do not introduce a generic raw HID command interface.

Do not allow callers to choose arbitrary:

- feature IDs
- runtime indices
- HID++ functions
- device indices
- report IDs
- USB interfaces
- raw payload bytes

Use typed operations with strict validation.

For OLED communication, avoid operations that interfere with Force Feedback, TRUEFORCE, LEDs, profiles, or firmware.

Feature `0x8123` Force Feedback must not be invoked as part of OLED transport.

The previous exclusive DirectInput path demonstrated destructive FFB side effects and must not be treated as the production OLED transport.

Prefer the validated shared HID++ path over DirectInput acquisition.

Physical testing must progress in bounded stages and must not move to driving or full-lap testing until the previous coexistence stage has passed.

### Research evidence

Preserve enough evidence to reproduce conclusions without publishing proprietary source material.

Good repository evidence includes:

- sanitized protocol descriptions
- USB request/response summaries
- hashes of locally inspected files
- independently written diagrams
- experimental procedures
- physical observations
- decoded protocol fields
- timings
- test results

Keep raw PCAP/PCAPNG, firmware, DLLs, extracted assets, and large binary dumps local and ignored by Git.

### Third-party open-source projects

Technical findings from third-party projects may be used as references.

Do not copy or adapt third-party source code without checking its license first.

In particular, code from `mescon/logitech-trueforce-linux-driver` may be GPL/LGPL licensed depending on the component.

Distinguish clearly between:

- learning a protocol fact from another project; and
- copying or adapting that project's implementation.

If code is adapted, identify the source and license before committing it.

### Trademark / affiliation

Treat Logitech, Logitech G, G HUB, RS50, PRO Racing Wheel, TRUEFORCE, and related names as third-party trademarks.

Do not imply that LogiDynamicDash is an official Logitech product.

Project-facing documentation should describe the project as independent and unofficial when appropriate.

Do not use Logitech logos or proprietary visual assets in the project without explicit permission.

### Documentation language

Repository code, comments, commits, README content, technical documentation, issues, pull requests, and other public-facing project material should be written in English.

Explain technical work to me conversationally in Spanish.

Use first-person singular ("I") rather than "we" when drafting messages or statements on my behalf.

### Claims

Separate these evidence levels:

- Confirmed: physically or independently verified.
- Likely: supported by multiple observations but not fully verified.
- Unknown: insufficient evidence.

Do not upgrade an inference into a confirmed fact.

Do not claim that reverse engineering establishes ownership of Logitech technology.

The goal is interoperability with independently written software.

### Before committing

Before adding research-derived material to Git, check that the change does not include:

- vendor binaries
- firmware
- extracted proprietary assets
- decompiled source
- copyrighted font or bitmap data
- raw USB captures
- secrets or personal identifiers
- third-party source copied without license compliance

When uncertain whether material is appropriate to publish, stop and flag the issue instead of committing it.

### Design preference

Whenever two implementations are technically equivalent, prefer the one that:

1. uses independently written code;
2. relies on standard OS/HID interfaces;
3. uses the smallest verified protocol surface;
4. minimizes dependency on Logitech proprietary software;
5. avoids redistributing vendor material;
6. provides clear attribution for external research;
7. is easiest to audit for hardware safety and interoperability.

These constraints apply to all future RS50, OLED, HID++, G HUB, firmware, DirectInput, TRUEFORCE, and protocol-research work in this repository.

Do not "clean up" or reimplement code by translating decompiled Logitech logic line-by-line. First reduce any reverse-engineering finding to a functional interoperability specification, then implement independently from that specification.
67 changes: 67 additions & 0 deletions LogiDynamicDash.Tests/CompositeDisplaySinkTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using LogiDynamicDash.Displays;
using LogiDynamicDash.Models;

namespace LogiDynamicDash.Tests;

public sealed class CompositeDisplaySinkTests
{
[Fact]
public void Lifecycle_InitializesInOrderAndStopsInReverse()
{
List<string> events = [];
RecordingSink first = new("first", events);
RecordingSink second = new("second", events);
CompositeDisplaySink sink = new(first, second);
LayoutJFrame frame = new("SPEED", "1 KMH", "GEAR", "1");

sink.Initialize();
sink.Render(frame);
sink.Stop();

Assert.Equal(
["first:init", "second:init", "first:render", "second:render",
"second:stop", "first:stop"],
events);
}

[Fact]
public void InitializeFailure_StopsOnlyInitializedSinks()
{
List<string> events = [];
RecordingSink first = new("first", events);
RecordingSink second = new("second", events)
{
FailInitialize = true
};
RecordingSink third = new("third", events);
CompositeDisplaySink sink = new(first, second, third);

Assert.Throws<InvalidOperationException>(sink.Initialize);

Assert.Equal(
["first:init", "second:init", "first:stop"],
events);
}

private sealed class RecordingSink(
string name,
List<string> events) : IDisplaySink
{
public bool FailInitialize { get; set; }

public void Initialize()
{
events.Add($"{name}:init");
if (FailInitialize)
{
throw new InvalidOperationException("Simulated failure.");
}
}

public void Render(LayoutJFrame frame) =>
events.Add($"{name}:render");

public void Stop() =>
events.Add($"{name}:stop");
}
}
Loading