# 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 ```