313 lines
10 KiB
Markdown
313 lines
10 KiB
Markdown
# 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 |
|
||
|
||
### 4.2 Datei-Transfer (reserviert, noch nicht implementiert)
|
||
| Wert | Name |
|
||
|--------|--------------|
|
||
| `0x20` | `FILE_START` |
|
||
| `0x21` | `FILE_CHUNK` |
|
||
| `0x22` | `FILE_END` |
|
||
|
||
### 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 |
|
||
| `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).
|
||
|
||
## 7. ACK- und ERROR-Frames
|
||
|
||
### 7.1 ACK (`frame_type = 0x11`) — Host → Device
|
||
Wird waehrend eines laufenden LS-Streams gesendet, um dem Device Credits (Sendeerlaubnisse) zu erteilen.
|
||
|
||
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 |
|
||
|
||
## 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
|
||
``` |