Files
buzzer_2/protocol.md
2026-03-18 15:05:45 +01:00

365 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Buzzer Protocol (Wire Specification)
## 1. Zweck und Geltungsbereich
Das Buzzer Protocol definiert ein transportunabhaengiges, binaeres Frame-Format fuer die Kommunikation zwischen Host und Device.
Unterstuetzte Transporte sind aktuell BLE und USB CDC ACM/UART.
Das Protokoll spezifiziert:
- Frame-Struktur (Header + Payload)
- Frametypen
- Datentypen fuer Request/Response
- Semantik fuer Stream-Transfers (Verzeichnisliste, Datei, Firmware)
## 2. Transport- und Codierungsregeln
- Alle ganzzahligen Felder werden in Little Endian uebertragen.
- Die im Header angegebene `payload_length` bezieht sich ausschliesslich auf die Nutzdaten ohne Header.
- Bei UART kann optional eine Synchronisationssequenz `BUZZ` (`0x42 0x55 0x5A 0x5A`) vor einem Frame verwendet werden, um Framing nach Leitungsstoerungen zu resynchronisieren.
## 3. Frame-Format
### 3.1 Header (3 Byte)
```c
uint8_t frame_type
uint16_t payload_length // Little Endian
```
### 3.2 Gesamtframe
```
+------------------+-------------------------+
| Header (3 Byte) | Payload (optional) |
| frame_type (1B) | payload_length Byte |
| payload_len (2B) | |
+------------------+-------------------------+
```
## 4. Frametypen
### 4.1 Steuer- und Anfrageframes
| Wert | Name | Richtung | Beschreibung |
|--------|------------|----------------|---------------------------------------|
| `0x00` | `REQUEST` | Host → Device | Abfrage eines Datentyps |
| `0x10` | `RESPONSE` | Device → Host | Antwort auf `REQUEST` |
| `0x11` | `ACK` | Host ↔ Device | Flusskontrolle bei Stream-Transfers |
| `0x12` | `ERROR` | Device → Host | Fehlerantwort mit Fehlercode |
| `0x13` | `SUCCESS` | Device → Host | Bestaetigung einer Operation |
### 4.2 Datei-Transfer
| Wert | Name | Richtung | Beschreibung |
|--------|--------------|----------------|---------------------------------------------|
| `0x20` | `FILE_START` | Host ↔ Device | Beginn eines Dateitransfers |
| `0x21` | `FILE_CHUNK` | Host ↔ Device | Ein Datenblock des Dateitransfers |
| `0x22` | `FILE_END` | Host ↔ Device | Abschluss des Dateitransfers inkl. CRC32 |
### 4.3 Firmware-Transfer (reserviert, noch nicht implementiert)
| Wert | Name |
|--------|------------|
| `0x30` | `FW_START` |
| `0x31` | `FW_CHUNK` |
| `0x32` | `FW_END` |
### 4.4 Verzeichnisliste
| Wert | Name | Richtung | Beschreibung |
|--------|------------|----------------|---------------------------------|
| `0x40` | `LS_START` | Device → Host | Beginn des Listing-Streams |
| `0x41` | `LS_ENTRY` | Device → Host | Ein Verzeichniseintrag |
| `0x42` | `LS_END` | Device → Host | Ende des Listing-Streams |
## 5. Request/Response-Schema
### 5.1 Request (`frame_type = 0x00`)
Payload-Mindestformat:
```c
uint8_t data_type // Nutzt enum buzz_data_type
// optional: datentypspezifische Parameter
```
Wire-Format:
```
[0x00][payload_length LE][data_type][optional parameters]
```
### 5.2 Response (`frame_type = 0x10`)
Payload-Mindestformat:
```c
uint8_t data_type // Echo des angefragten data_type
// danach: datentypspezifische Response-Daten
```
Wire-Format:
```
[0x10][payload_length LE][data_type][response payload]
```
## 6. Datentypen (Request/Response)
Definierte `data_type`-Werte:
| Wert | Name | Beschreibung |
|--------|---------------|--------------------------------------|
| `0x01` | `PROTO_INFO` | Protokollversion und Chunk-Groesse |
| `0x02` | `DEVICE_INFO` | Geraeteinformationen (TBD) |
| `0x03` | `FS_INFO` | Dateisystem-Statistik und Pfadnamen |
| `0x20` | `FILE_GET` | Datei vom Device anfordern |
| `0x21` | `FILE_PUT` | Datei auf das Device hochladen |
| `0x22` | `TAGS_GET` | Metadaten-Tags anfordern |
| `0x23` | `TAGS_PUT` | Metadaten-Tags schreiben |
| `0x24` | `RM_FILE` | Datei loeschen |
| `0x25` | `RENAME_FILE` | Datei umbenennen |
| `0x30` | `FW_UPDATE` | Firmware-Update starten |
| `0x40` | `LS` | Verzeichnisliste starten |
### 6.1 `PROTO_INFO` (`0x01`)
Request-Parameter: keine
Response-Payload:
```c
uint8_t data_type; // 0x01
uint16_t version; // Protokollversion (LE)
uint16_t max_chunk_size; // max. Nutzdaten pro Frame ohne Header (LE)
```
Hinweis: `max_chunk_size` ergibt sich aus der internen Slab-Konfiguration (`CONFIG_BUZZ_PROTO_SLAB_SIZE - 3`).
### 6.2 `DEVICE_INFO` (`0x02`)
TBD
### 6.3 `FS_INFO` (`0x03`)
Request-Parameter: keine
Response-Payload:
```c
uint8_t data_type; // 0x03
uint32_t total_size; // Gesamtgroesse Flash in Bytes (LE)
uint32_t free_size; // Freier Speicher in Bytes (LE)
uint8_t max_path_length; // Maximal erlaubte Pfadlaenge
uint8_t sys_path_length; // Laenge des System-Pfades (ohne 0-Terminator)
uint8_t audio_path_length; // Laenge des Audio-Pfades (ohne 0-Terminator)
uint8_t data[]; // sys_path gefolgt von audio_path, nicht nullterminiert
```
### 6.4 `LS` (`0x40`) — Verzeichnisliste anfordern
Startet einen LS-Stream fuer den angegebenen Pfad.
Request-Payload:
```c
uint8_t data_type; // 0x40
char path[]; // Pfad ohne 0-Terminator, Laenge ergibt sich aus payload_length - 1
```
Wire-Format (Beispiel fuer Pfad `/a`):
```
[0x00][0x03 0x00][0x40][0x2F 0x61]
```
Das Device antwortet mit dem LS-Stream (siehe Abschnitt 8).
### 6.5 `RM_FILE` (`0x24`) — Datei loeschen
Request-Payload:
```c
uint8_t data_type; // 0x24
uint8_t path_length; // Laenge des Pfads
char path[]; // Pfad ohne 0-Terminator
```
### 6.6 `RENAME_FILE` (`0x25`) — Datei umbenennen
Request-Payload:
```c
uint8_t data_type; // 0x25
uint8_t old_path_length; // Laenge des alten Pfads
uint8_t new_path_length; // Laenge des neuen Pfads
char paths[]; // Alter Pfad, direkt gefolgt vom neuen Pfad (beide ohne 0-Terminator)
```
### 6.7 `FILE_PUT` (`0x21`) / `TAGS_PUT` (`0x23`) — Upload initiieren
Request-Payload:
```c
uint8_t data_type; // 0x21 (Datei) oder 0x23 (Tags)
uint32_t total_size; // Dateigroesse in Bytes (LE)
char path[]; // Zielpfad ohne 0-Terminator
```
## 7. ACK-, ERROR- und SUCCESS-Frames
### 7.1 ACK (`frame_type = 0x11`) — Host ↔ Device
Wird waehrend eines laufenden Stream-Transfers gesendet, um der sendenden Seite Credits (Sendeerlaubnisse) zu erteilen.
Bei einem Download (`LS` oder `FILE_GET`) sendet der Host das ACK. Bei einem Upload (`FILE_PUT` oder `TAGS_PUT`) sendet das Device das ACK.
Format:
```c
// Header:
uint8_t frame_type; // 0x11
uint16_t payload_length; // 0x0002
// Payload:
uint16_t credits; // Anzahl der Entries, die das Device senden darf (LE)
```
Wire-Format (Beispiel: 64 Credits):
```
[0x11][0x02 0x00][0x40 0x00]
```
Semantik:
- Der Host sendet nach Empfang von `LS_START` initial Credits (typisch 64).
- Das Device dekrementiert seinen internen Credit-Zaehler mit jeder gesendeten `LS_ENTRY`.
- Bei 0 Credits wartet das Device auf ein weiteres ACK (Timeout: 5 × 500 ms, danach Abbruch).
- Der Host soll bei Bedarf weitere Credits nachsenden, bevor die bisherigen aufgebraucht sind.
### 7.2 ERROR (`frame_type = 0x12`) — Device → Host
Format:
```c
// Header:
uint8_t frame_type; // 0x12
uint16_t payload_length; // 0x0002
// Payload:
uint16_t error_code; // Positiver Zephyr-errno-Wert (LE)
```
Wire-Format (Beispiel: ENOENT = 2):
```
[0x12][0x02 0x00][0x02 0x00]
```
ERROR kann jederzeit als Antwort auf einen REQUEST oder waehrend eines Streams gesendet werden.
Ein ERROR-Frame waehrend eines aktiven LS-Streams beendet diesen implizit.
Fehlercode-Tabelle (Zephyr errno, positiver Wert):
| Code | Zephyr-Name | Bedeutung |
|------|----------------|---------------------------------------------|
| 1 | `EPERM` | Fehlende Berechtigung |
| 2 | `ENOENT` | Datei oder Verzeichnis nicht gefunden |
| 5 | `EIO` | Ein-/Ausgabefehler auf dem Flash |
| 12 | `ENOMEM` | Nicht genuegend Speicher frei |
| 16 | `EBUSY` | Geraet oder Ressource belegt |
| 22 | `EINVAL` | Ungültiges Argument oder Parameter |
| 24 | `EMFILE` | Zu viele offene Dateien |
| 28 | `ENOSPC` | Kein freier Speicherplatz mehr |
| 36 | `ENAMETOOLONG` | Dateiname oder Pfad zu lang |
| 88 | `ENOSYS` | Funktion nicht implementiert |
| 134 | `ENOTSUP` | Operation nicht unterstuetzt |
### 7.3 SUCCESS (`frame_type = 0x13`) — Device → Host
Bestaetigt den erfolgreichen Abschluss einer Operation, z. B. nach Beendigung eines Uploads oder einer Dateioperation (Loeschen, Umbenennen).
Format:
```c
// Header:
uint8_t frame_type; // 0x13
uint16_t payload_length; // 0x0001
// Payload:
uint8_t data_type; // Der Befehl, der erfolgreich war (z.B. 0x21 fuer FILE_PUT)
```
Wire-Format (Beispiel: Erfolg bei RM_FILE):
```
[0x13][0x01 0x00][0x24]
```
## 8. LS-Stream (Verzeichnisliste)
Der LS-Stream wird durch einen `REQUEST` mit `data_type = 0x40` ausgeloest und laeuft wie folgt ab:
```
Host Device
| |
|-- REQUEST (data_type=LS, path) -->|
| | (oeffnet Verzeichnis)
|<--------- LS_START (leer) --------|
| |
|------ ACK (credits=64) ---------->|
| |
|<-- LS_ENTRY (entry 1) ------------|
|<-- LS_ENTRY (entry 2) ------------|
| ... |
|<-- LS_ENTRY (entry 64) -----------| (credits = 0, Device wartet)
| |
|------ ACK (credits=64) ---------->|
| |
|<-- LS_ENTRY (entry 65) -----------|
| ... |
|<--------- LS_END -----------------|
```
### 8.1 `LS_START` (`0x40`) — Device → Host
Signalisiert den Beginn des Streams. Keine Payload.
```
[0x40][0x00 0x00]
```
### 8.2 `LS_ENTRY` (`0x41`) — Device → Host
Ein Eintrag pro Verzeichniselement.
Payload:
```c
uint8_t type; // 0x00 = Datei, 0x01 = Verzeichnis (buzz_fs_entry_type)
uint32_t size; // Dateigroesse in Bytes (LE); bei Verzeichnissen 0
uint8_t name_length; // Laenge des Namens (ohne 0-Terminator)
char name[]; // Datei-/Verzeichnisname, nicht nullterminiert
```
`type`-Werte:
| Wert | Bedeutung |
|--------|---------------|
| `0x00` | Datei (FILE) |
| `0x01` | Verzeichnis (DIR) |
### 8.3 `LS_END` (`0x42`) — Device → Host
Signalisiert das Ende des Streams.
Payload:
```c
uint32_t total_entries; // Gesamtzahl gesendeter Eintraege (LE)
```
Der Host kann `total_entries` mit der empfangenen Anzahl von `LS_ENTRY`-Frames vergleichen, um Vollstaendigkeit zu pruefen.
### 8.4 Fehler- und Timeoutbehandlung
- Tritt ein Fehler beim Lesen auf, sendet das Device einen `ERROR`-Frame und beendet den Stream.
- Empfaengt das Device 5 Mal in Folge keine Credits innerhalb von je 500 ms (2,5 s gesamt), bricht es den Stream intern ab (kein ERROR-Frame, Stream wird still verworfen).
- Der Host sollte einen eigenen Watchdog implementieren; empfohlener Timeout: 3 s ohne empfangenen Frame.
## 9. Beispiele
### 9.1 PROTO_INFO abfragen
Request:
```
00 01 00 01
```
- `00`: `REQUEST`
- `01 00`: `payload_length = 1`
- `01`: `data_type = PROTO_INFO`
Response (Beispielwerte):
```
10 05 00 01 01 00 FD 00
```
- `10`: `RESPONSE`
- `05 00`: `payload_length = 5`
- `01`: `data_type = PROTO_INFO`
- `01 00`: `version = 1`
- `FD 00`: `max_chunk_size = 253`
### 9.2 Verzeichnisliste `/a` anfordern
Request:
```
00 03 00 40 2F 61
```
- `00`: `REQUEST`
- `03 00`: `payload_length = 3`
- `40`: `data_type = LS`
- `2F 61`: Pfad `/a`
Antwort (Sequenz):
```
40 00 00 // LS_START, keine Payload
// Host sendet ACK mit Credits
11 02 00 40 00 // ACK, 64 Credits
// Device sendet Eintraege
41 0A 00 00 00 00 00 00 06 73 6F 75 6E 64 31 // LS_ENTRY: FILE, size=0, name="sound1" (gekuerzt)
// ... weitere Eintraege ...
42 04 00 01 00 00 00 // LS_END, total_entries = 1
```