Harden firmware state and transfer handling

This commit is contained in:
2026-07-24 09:49:21 +02:00
parent 50dbf8fbee
commit ce5db617a1
27 changed files with 558 additions and 231 deletions
+49 -64
View File
@@ -1,93 +1,78 @@
# Bekannte Einschränkungen und Risiken
Diese Liste beschreibt den aktuellen Implementierungsstand. Sie ist keine
Liste bereits umgesetzter Features.
Diese Liste beschreibt den aktuellen Implementierungsstand nach den
Robustheitskorrekturen. Sie ist keine Liste bereits umgesetzter Features.
## Flash-Reservierung stimmt nicht vollständig mit dem NVM-Zugriff überein
## Bootloader-Ziel bleibt nicht unterstützt
Die Firmware liest und schreibt:
```text
0x1FB00..0x1FCFF Makros (512 Byte)
0x1FD00..0x1FFFF Config (768 Byte, davon 740 Byte genutzt)
```
Das aktive Linker-Skript `flash_without_bootloader.ld` erlaubt Firmware jedoch
bis einschließlich `0x1FDFF` und reserviert nur `0x1FE00..0x1FFFF`. Damit sind
`0x1FB00..0x1FDFF` nicht gegen ein zukünftig wachsendes Firmware-Image
geschützt. Das aktuelle Image liegt deutlich darunter, aber der Build prüft
diese NVM-Grenze nicht.
Das Bootloader-Linker-Skript reserviert aktuell gar keinen separaten
NVM-Bereich. Das auskommentierte USB-Bootloader-Environment ist daher kein
unterstütztes Ziel.
Das aktive Ziel `versapad_nobl` reserviert den kompletten Bereich
`0x1FB00..0x1FFFF` für Makros und Config. Das auskommentierte
USB-Bootloader-Environment verwendet dagegen weiterhin eine historische
Board-/Linker-Konfiguration und ist nicht als Produktionsziel verifiziert.
Die aktive Boarddatei benennt die MCU als `samd21g17d`, setzt für den
Arduino-Core aber weiterhin das Kompatibilitätsmakro `__SAMD21G18A__`. Der
PlatformIO-Build meldet korrekt 128 KiB Flash und 16 KiB RAM; vor
device-spezifischen Änderungen sollte diese historische Makro-Abweichung
trotzdem geprüft werden.
PlatformIO-Build meldet korrekt 128 KiB physischen Flash, 16 KiB RAM und
129.792 Byte nutzbaren Firmwarebereich. Vor device-spezifischen
Core-Änderungen sollte die historische Makro-Abweichung trotzdem geprüft
werden.
## Event-Queue hat gemischte Producer
## Event-Queue hat eine feste Kapazität
Matrixevents werden im Loop erzeugt, Encoderevents in EIC-Interrupts. Beide
rufen `CEventQueue::push()` auf und verändern denselben Tail-Index ohne
Interruptschutz. Ein Encoderinterrupt kann einen Matrix-Push unterbrechen.
Die bisherige Annahme eines reinen Single-Producer/Single-Consumer-Ringbuffers
ist deshalb nicht vollständig erfüllt; seltene verlorene oder überschriebene
Events sind theoretisch möglich.
Matrix- und Encoder-Producer verändern den Tail-Index nicht mehr gleichzeitig:
Der Matrixcallback maskiert Interrupts während seines Queue-Pushs.
Zusätzlich werden Events bei voller Queue still verworfen.
Die Queue besitzt aber weiterhin nur 16 nutzbare Slots. Bei Überlauf wird ein
neues Event ohne Hostmeldung verworfen. Das kann vor allem während
blockierender Makro-, NVM- oder Feedbackpfade auftreten.
## `HOST_COMMAND` nutzt seine `data` nicht
## Host-Command-Ausführung liegt in der Desktop-App
`SAction.data` ist für `ActionType::HOST_COMMAND` vorhanden, wird in
`execute_action_down()` aber nicht übertragen. Gesendet wird nur
`USB_EVT_KEY_DOWN (0x81)` mit der Matrix-Key-ID beziehungsweise Encoder-ID.
Die Firmware überträgt Command-ID, Key-/Encoder-ID und Eventrichtung
vollständig. VersaGUI validiert und empfängt diese Pakete, führt eine
Command-ID aber noch nicht als Prozess-, URL- oder frei konfigurierbare
Hostaktion aus. Eine spätere Implementierung benötigt ein explizites,
sicheres Mapping; beliebige Command-Strings sollten nicht direkt an eine Shell
weitergegeben werden.
`USB_EVT_KEY_UP (0x82)`, `USB_EVT_ENC_CW (0x83)` und
`USB_EVT_ENC_CCW (0x84)` sind definiert, werden vom aktuellen Controller aber
nicht gesendet. Bei Encoder-Host-Actions geht dadurch die Richtung im
gesendeten Paket verloren, sofern die Host-Anwendung sie nicht anderweitig aus
der konfigurierten Action ableitet.
## Grenzen des HID-Reports
## HID-Holds sind global
Keyboard-Holds und Modifier werden referenzgezählt. Der USB-Descriptor kann
maximal sechs unterschiedliche Keyboard-Usages gleichzeitig darstellen.
Weitere Holds bleiben intern aktiv und rücken nach, sobald ein Report-Slot
frei wird.
Der Keyboard-Report enthält zwar sechs Keycode-Felder, die Implementierung
setzt aber nur das erste. Jeder neue `HID_KEY`-Down ersetzt den vorherigen
Report; jedes Release sendet einen komplett leeren Report. Analog existiert
nur ein globaler Consumer-Usage-Wert. Gleichzeitige unabhängige Holds werden
daher nicht korrekt verwaltet.
Der Consumer-Descriptor enthält genau ein Usage. Mehrere Consumer-Holds werden
intern verwaltet, sichtbar ist jeweils das zuletzt gedrückte aktive Usage.
## CDC-Transfers sind nur schwach validiert
## CDC bleibt ein festes, ungeframtes Paketprotokoll
- `BEGIN` speichert die angekündigte Chunkzahl, `COMMIT` vergleicht sie aber
nicht mit empfangenen Chunks.
- Doppelte, fehlende oder ungeordnete Chunks werden nicht verfolgt.
- Config-Commit prüft Magic, Version und CRC, aber keine Feldwerte oder
Enum-Bereiche.
- Makro-Commit hat weder CRC noch Vollständigkeitsprüfung.
- Das 8-Byte-Protokoll besitzt kein Framing. Ein verlorenes oder zusätzliches
Byte desynchronisiert alle folgenden Pakete.
Config- und Makrotransfers prüfen jetzt Chunkzahl, eindeutige Indizes und
Vollständigkeit. Config besitzt zusätzlich CRC und Feldvalidierung.
Insbesondere kann ein formal CRC-korrektes Profil ungültige Animationswerte
enthalten. `PULSE` benötigt in der aktuellen Arithmetik eine Periode von
mindestens 2 ms; dieser Mindestwert wird nicht validiert.
Weiterhin gilt:
## Farbanimationen umgehen Teile der Helligkeits-/Override-Logik
- Das Protokoll hat kein Byte-Framing. Ein verlorenes oder zusätzliches Byte
verschiebt die 8-Byte-Paketgrenzen bis zum Reconnect.
- Einzelpakete besitzen keine Sequenznummer oder Prüfsumme.
- Die Makrotabelle besitzt im NVM keine persistente CRC; beim Transfer werden
nur Vollständigkeit und HID-Keycode-Bereiche geprüft.
## Farbanimationen und Helligkeit
Globale und LED-spezifische Helligkeit werden beim Laden in die Base-Farbe
eingerechnet. `COLOR_CYCLE` berechnet RGB dagegen direkt mit festen 40 %
Helligkeit und ignoriert Base-Farbe sowie Override. `COLOR_FADE` benötigt den
separaten Aufruf `set_color_fade()`; das reine Laden des Enum-Werts aus einer
Config setzt keine Start- und Zielfarbe.
Helligkeit und ignoriert Base-Farbe sowie Override.
`COLOR_FADE` aus der Config wird als einmaliger Fade von Schwarz zur
gespeicherten Base-Farbe interpretiert.
## Reservierte beziehungsweise noch ungenutzte Hardware und Felder
- Die drei Fader-Pins sind im Variant und in `config/pins.h` definiert, werden
von der Firmware aber nicht eingelesen.
- `enc_sensitivity[4]` wird gespeichert und mit Default `1` befüllt, beeinflusst
die Encoderdekodierung derzeit aber nicht.
absichtlich noch nicht von der Firmware eingelesen.
- `enc_sensitivity[4]` wird gespeichert und mit Default `1` befüllt,
beeinflusst die Encoderdekodierung derzeit aber nicht.
- `SET_LED_BASE` verändert nur den RAM-Zustand und wird nicht in NVM
persistiert.