diff --git a/doc-dev/other/flashing/README.md b/doc-dev/other/flashing/README.md new file mode 100644 index 000000000..3d8579c3c --- /dev/null +++ b/doc-dev/other/flashing/README.md @@ -0,0 +1,103 @@ +# Firmware Flashing Implementation + +This directory contains documentation for implementing autonomous module firmware flashing on the UHK right half. + +## Goal + +Move K-boot protocol implementation from the Agent (desktop tool) to the right half firmware, enabling autonomous module firmware updates without Agent involvement in the flashing process. + +## Documentation Files + +| File | Description | +|------|-------------| +| [configuration-buffer.md](configuration-buffer.md) | Config buffer APIs and memory layout | +| [usb-communication.md](usb-communication.md) | USB command infrastructure | +| [module-communication.md](module-communication.md) | I2C/UART slave scheduler and module discovery | +| [kboot-protocol.md](kboot-protocol.md) | K-boot protocol details for porting | + +## Current Architecture + +``` +┌─────────────┐ USB ┌─────────────┐ I2C ┌─────────────┐ +│ Agent │ ────────────────▶│ Right Half │ ───────────────▶│ Module │ +│ (Desktop) │ │ (Buspal) │ │ (Bootloader)│ +└─────────────┘ └─────────────┘ └─────────────┘ + │ │ │ + │ K-boot protocol │ Forward packets │ + │ implementation │ transparently │ + │ │ │ +``` + +**Problem**: K-boot protocol logic is in the Agent. Right half only forwards packets. + +## Target Architecture + +``` +┌─────────────┐ USB ┌─────────────┐ I2C ┌─────────────┐ +│ Agent │ ────────────────▶│ Right Half │ ───────────────▶│ Module │ +│ (Desktop) │ │ (K-boot) │ │ (Bootloader)│ +└─────────────┘ └─────────────┘ └─────────────┘ + │ │ │ + │ Upload firmware │ K-boot protocol │ + │ binary only │ implementation │ + │ │ │ +``` + +**Goal**: Agent uploads firmware binary. Right half handles K-boot protocol. + +## Two-Phase Implementation + +### Phase 1: USB Transfer to Right Half + +1. Analyze existing configuration buffer APIs (see [configuration-buffer.md](configuration-buffer.md)) +2. Design firmware upload protocol +3. Implement chunked USB transfer (similar to config write) +4. Store firmware in staging buffer or stream directly + +### Phase 2: Right Half to Module + +1. Port K-boot protocol from Agent (see [kboot-protocol.md](kboot-protocol.md)) +2. Implement I2C transport layer +3. Implement flashing state machine: + - Reboot module to bootloader + - Erase flash + - Write firmware chunks + - Verify and reset + +## Proposed USB Commands + +| Command | ID | Description | +|---------|-----|-------------| +| StartModuleFirmwareUpload | 0x20 | Begin firmware upload (module ID, size) | +| WriteModuleFirmwareChunk | 0x21 | Write firmware data chunk | +| FlashModule | 0x22 | Start flashing (uses uploaded firmware) | +| GetFlashingStatus | 0x23 | Query flashing progress | +| AbortFlashing | 0x24 | Cancel flashing operation | + +## Key Challenges + +1. **Memory**: Module firmware (~40-60 KB) exceeds config buffer (~32 KB) + - Solution: Stream directly to module, or use UHK80 external flash + +2. **Timing**: I2C transfers while maintaining keyboard responsiveness + - Solution: Use slave scheduler, low priority for flashing + +3. **Error Recovery**: Handle I2C failures, power loss during flash + - Solution: Verify writes, implement retry logic + +4. **State Management**: Track flashing progress across multiple USB commands + - Solution: Implement state machine with persistent state + +## Technical Constraints + +- Firmware must fit in available RAM or be streamed +- K-boot protocol requires both I2C and UART support +- Must handle communication failures gracefully +- Module must remain functional if flashing fails + +## Success Criteria + +- [ ] Firmware can be uploaded to right half via USB +- [ ] Right half can autonomously flash connected module +- [ ] No dependency on Agent for K-boot protocol execution +- [ ] Agent only uploads firmware binary, doesn't manage flashing diff --git a/doc-dev/other/flashing/configuration-buffer.md b/doc-dev/other/flashing/configuration-buffer.md new file mode 100644 index 000000000..e9d34a18d --- /dev/null +++ b/doc-dev/other/flashing/configuration-buffer.md @@ -0,0 +1,141 @@ +# Configuration Buffer APIs + +This document describes the user configuration buffer system that could potentially be used for firmware image storage during module flashing. + +## Buffer Architecture + +The firmware uses a **three-buffer system** for configuration management: + +```c +typedef enum { + ConfigBufferId_HardwareConfig, + ConfigBufferId_StagingUserConfig, + ConfigBufferId_ValidatedUserConfig, +} config_buffer_id_t; +``` + +### Memory Sizes + +| Platform | Hardware Config | User Config | +|----------|----------------|-------------| +| UHK60 | 64 bytes | 32,704 bytes | +| UHK80 | 64 bytes | 32,768 bytes | + +**Note**: Module firmware images are typically 40-60 KB for UHK60 modules, which exceeds the current user config buffer capacity. Alternative storage strategies may be needed. + +## Core Data Structures + +### Buffer Structure + +From `right/src/config_parser/basic_types.h`: + +```c +typedef struct { + bool isValid; // Flag indicating if buffer contains valid configuration + uint8_t *buffer; // Pointer to raw byte buffer + uint16_t offset; // Current read/write position for parsing +} config_buffer_t; +``` + +### Buffer Instances + +From `right/src/config_parser/config_globals.c`: + +```c +config_buffer_t HardwareConfigBuffer; +config_buffer_t StagingUserConfigBuffer; +config_buffer_t ValidatedUserConfigBuffer; +``` + +## Key Functions + +### Buffer Access + +```c +// Get buffer descriptor from ID +config_buffer_t* ConfigBufferIdToConfigBuffer(config_buffer_id_t configBufferId); + +// Get buffer size +uint16_t ConfigBufferIdToBufferSize(config_buffer_id_t configBufferId); + +// Validate buffer ID +bool IsConfigBufferIdValid(config_buffer_id_t configBufferId); +``` + +### Parsing Primitives + +From `right/src/config_parser/basic_types.h`: + +```c +uint8_t ReadUInt8(config_buffer_t *buffer); +uint16_t ReadUInt16(config_buffer_t *buffer); +uint32_t ReadUInt32(config_buffer_t *buffer); +``` + +## USB Read/Write APIs + +### Read Config Command + +**File**: `right/src/usb_commands/usb_command_read_config.c` + +**Protocol**: +- Byte 0: Command (0x04) +- Byte 1: Config buffer ID +- Byte 2: Length to read (max 62 bytes) +- Bytes 3-4: Offset (little-endian uint16) +- Response: [Status(1)] [Data(length)] + +### Write Config Command + +**File**: `right/src/usb_commands/usb_command_write_config.c` + +**Protocol**: +- Byte 0: Command (0x05 or 0x06) +- Byte 1: Length to write +- Bytes 2-3: Offset (little-endian uint16) +- Bytes 4+: Data to write (max 59 bytes per packet) + +## Storage Operations + +### UHK60 - EEPROM + +**File**: `right/src/eeprom.c` + +```c +status_t EEPROM_LaunchTransfer(storage_operation_t operation, + config_buffer_id_t configBufferId, + void (*successCallback)); +``` + +- I2C-based EEPROM at 100 kHz (I2C1 at 1 MHz) +- 64-byte page writes +- Sequential page writes with callbacks + +### UHK80 - Flash + +**File**: `device/src/flash.c` + +```c +uint8_t Flash_LaunchTransfer(storage_operation_t operation, + config_buffer_id_t configBufferId, + void (*successCallback)); +``` + +- Flash area API with 4K alignment +- Erase before write required + +## Implications for Firmware Storage + +Current configuration buffer capacity (~32 KB) is insufficient for typical module firmware images (~40-60 KB). Options include: + +1. **Streaming approach**: Transfer firmware in chunks directly to module without storing in RAM +2. **External storage**: Use separate flash region on UHK80 +3. **Multiple transfers**: Split firmware and transfer in multiple sessions + +## Key Source Files + +- `right/src/config_manager.h` - Runtime config struct +- `right/src/config_parser/config_globals.h` - Buffer types & IDs +- `right/src/config_parser/basic_types.h` - Parsing primitives +- `right/src/eeprom.c` - UHK60 storage +- `device/src/flash.c` - UHK80 storage diff --git a/doc-dev/other/flashing/firmware-upload-proposal.md b/doc-dev/other/flashing/firmware-upload-proposal.md new file mode 100644 index 000000000..f6f93ab99 --- /dev/null +++ b/doc-dev/other/flashing/firmware-upload-proposal.md @@ -0,0 +1,225 @@ +# Firmware Upload Proposal + +## 1. USB Protocol + +63-byte HID reports. All multi-byte integers are little-endian. + +Every response starts with a status byte (0 = success). + +## 2. Current: UserConfig Flashing + +```mermaid +sequenceDiagram + participant A as Agent + participant R as Right Half + + loop for each 59-byte chunk + A->>R: WriteStagingUserConfig + end + A->>R: ApplyConfig + A->>R: LaunchStorageTransfer + loop poll + A->>R: GetDeviceState + R-->>A: isEepromBusy + end +``` + +### WriteStagingUserConfig (0x06) + +| Byte | Field | +|------|---------------| +| 0 | 0x06 | +| 1 | payload length (max 59) | +| 2-3 | offset into staging buffer | +| 4+ | data | + +Response: status byte only. + +### ApplyConfig (0x07) + +| Byte | Field | +|------|-------| +| 0 | 0x07 | + +Validates the staging buffer. On success, swaps staging ↔ validated buffers. + +Response: + +| Byte | Field | +|------|-------| +| 0 | status | +| 1-2 | parser offset (where parsing stopped) | +| 3 | parser stage (0=validate, 1=apply) | + +### LaunchStorageTransfer (0x08) + +| Byte | Field | +|------|-------| +| 0 | 0x08 | +| 1 | operation (0=read, 1=write) | +| 2 | buffer id (0=hardware, 1=stagingUser, 2=validatedUser) | + +Response: status byte only. + +### GetDeviceState (0x09) + +| Byte | Field | +|------|-------| +| 0 | 0x09 | + +Response: + +| Byte | Field | +|------|-------| +| 0 | status | +| 1 | busy flags: bit 0 = isEepromBusy, bit 1 = isModuleFlashBusy (**new**) | +| 2 | flags (halvesMerged, pairing, zephyrLog) | +| 3-5 | connected module IDs (left half, left mod, right mod) | +| 6 | active layer (bits 0-6) + toggled flag (bit 7) | +| 7 | macro status dirty | +| 8 | current keymap index | + +## 3. Current: Module Firmware Flashing + +Currently, module firmware flashing is Agent-driven. The right half acts as a USB-to-I2C bridge ("Buspal" mode) and the Agent speaks K-boot protocol directly over that bridge: + +```mermaid +sequenceDiagram + participant A as Agent + participant R as Right Half (Buspal) + participant M as Module (K-boot bootloader) + + A->>R: SendKbootCommandToModule(ping) + R->>M: K-boot ping (I2C) + A->>R: JumpToModuleBootloader(slotId) + R->>M: jump command (I2C) + Note over R: reenumerate as Buspal USB device + A->>R: K-boot configureI2c(i2cAddr) + A->>R: K-boot flashEraseAllUnsecure + R->>M: forwarded via I2C + A->>R: K-boot writeMemory(0x0000, firmwareData) + R->>M: forwarded via I2C + A->>R: K-boot reset + R->>M: forwarded via I2C + Note over R: reenumerate back to normal keyboard + A->>R: SendKbootCommandToModule(reset) + A->>R: SendKbootCommandToModule(idle) +``` + +### SendKbootCommandToModule (0x03) + +| Byte | Field | +|------|-------| +| 0 | 0x03 | +| 1 | command (0=idle, 1=ping, 2=reset) | +| 2 | i2c address (omitted for idle) | + +Triggers the kboot_driver state machine on the right half, which sends K-boot framed ping/reset packets over I2C. + +### JumpToModuleBootloader (0x02) + +| Byte | Field | +|------|-------| +| 0 | 0x02 | +| 1 | slot id | + +Sets the module phase to `JumpToBootloader`, causing the module driver to send a jump command via I2C. + +### Buspal Mode + +After jumping the module to bootloader, the Agent reenumerates the right half into Buspal mode (EnumerationMode 1). In Buspal mode, the right half becomes a transparent USB↔I2C bridge. The Agent then uses a K-boot library (`KBoot` class) to speak the K-boot framing protocol directly, sending `flashEraseAllUnsecure`, `writeMemory`, and `reset` commands. + +## 4. Proposed: Firmware-Driven Module Flashing + +Move K-boot protocol handling from the Agent into the firmware. The Agent sends firmware data using the same chunked transfer pattern as config flashing. The firmware handles K-boot protocol internally. + +```mermaid +sequenceDiagram + participant A as Agent + participant R as Right Half + participant M as Module (K-boot bootloader) + + loop for each 59-byte chunk + A->>R: WriteModuleFirmware + end + A->>R: ValidateBufferCrc(moduleFirmware, size, crc) + A->>R: FlashModule(slotId) + R->>M: K-boot: jump to bootloader + R->>M: K-boot: flashEraseAllUnsecure + R->>M: K-boot: writeMemory (from buffer) + R->>M: K-boot: reset + loop poll + A->>R: GetDeviceState + R-->>A: isModuleFlashBusy + opt if detailed state needed + A->>R: GetModuleFlashState + R-->>A: state + error code + end + end +``` + +### WriteModuleFirmware (new, 0x20) + +| Byte | Field | +|------|-------| +| 0 | 0x20 | +| 1 | payload length (max 59) | +| 2-3 | offset into firmware buffer | +| 4+ | data | + +Same chunked transfer pattern as WriteStagingUserConfig. + +### FlashModule (new, 0x21) + +| Byte | Field | +|------|-------| +| 0 | 0x21 | +| 1 | slot id | + +Triggers the full K-boot flash sequence internally: jump to bootloader → erase → write → reset. + +### GetModuleFlashState (new, 0x22) + +| Byte | Field | +|------|-------| +| 0 | 0x22 | + +Response: + +| Byte | Field | +|------|-------| +| 0 | status | +| 1 | flash state (0=idle, 1=erasing, 2=writing, 3=done, 4=error) | +| 2 | error code (if state=error) | + +### ValidateBufferCrc (new, 0x23) + +| Byte | Field | +|------|-------| +| 0 | 0x23 | +| 1 | buffer id (0=hardware, 1=stagingUser, 2=validatedUser, 3=moduleFirmware) | +| 2-3 | expected size | +| 4-5 | expected CRC16 | + +Firmware computes CRC over the first `size` bytes of the given buffer and compares it to the expected value. + +Response: + +| Byte | Field | +|------|-------| +| 0 | status (0=match, non-zero=mismatch) | + +### Advantages + +- No Buspal reenumeration needed (disruptive, platform-dependent) +- Agent doesn't need K-boot library or I2C knowledge +- Same pattern as config flashing — simple for Agent authors +- Firmware can manage timing and retries internally + +### Open question: buffering + +Module firmware can be up to ~128KB. The 16-bit offset field supports up to 64KB. Options: + +- **A. Buffer in RAM**: Requires large allocation. Possible on UHK80 (nRF has more RAM), tight on UHK60. +- **B. Stream to module**: Forward each chunk to the module bootloader immediately via K-boot writeMemory. No buffer needed, but the module must already be in bootloader mode during the upload. Requires the Agent to wait for each chunk's I2C completion. +- **C. Use 24-bit offset**: Extend the offset field to 3 bytes (payload drops to 58 bytes) to support >64KB. Only needed if buffering. diff --git a/doc-dev/other/flashing/kboot-protocol.md b/doc-dev/other/flashing/kboot-protocol.md new file mode 100644 index 000000000..a64bc234a --- /dev/null +++ b/doc-dev/other/flashing/kboot-protocol.md @@ -0,0 +1,194 @@ +# K-Boot Protocol + +This document describes the K-boot bootloader protocol used for flashing module firmware. The protocol is currently implemented in the Agent (TypeScript) and needs to be ported to the right half firmware. + +## Protocol Overview + +K-boot is NXP's bootloader protocol for Kinetis MCUs. The UHK uses it for: +- Direct USB flashing of the right half (KL04 bootloader) +- I2C-forwarded flashing of modules via Buspal mode + +## Command IDs + +From `lib/agent/packages/kboot/src/enums/commands.ts`: + +| ID | Command | Description | +|----|---------|-------------| +| 0x01 | FlashEraseAll | Erase entire flash | +| 0x02 | FlashEraseRegion | Erase region (start + count) | +| 0x03 | ReadMemory | Read from address | +| 0x04 | WriteMemory | Write to address (has data phase) | +| 0x05 | FillMemory | Fill memory region | +| 0x06 | FlashSecurityDisable | Unlock flash (8-byte key) | +| 0x07 | GetProperty | Query bootloader property | +| 0x0B | Reset | Reset to firmware | +| 0x0C | SetProperty | Set bootloader property | +| 0x0D | FlashEraseAllUnsecure | Erase all (unsecured) | +| 0xC1 | ConfigureI2c | Configure I2C for buspal | + +## Packet Structure + +### Command Packet (32 bytes) + +``` +Header (4 bytes): + [0] = 1 // Channel (always 1 for commands) + [1] = 0 // Reserved + [2-3] = Payload length // Little-endian 16-bit + +Payload (variable): + [0] = Command ID + [1] = HasDataPhase flag // 1 if data follows, 0 otherwise + [2] = 0 // Reserved + [3] = Param count // Number of 4-byte parameters + [4+] = Parameters // 32-bit little-endian each + +Padding: Fill to 32 bytes with zeros +``` + +### Response Packet + +``` +Header (4 bytes): + [0] = 3 // Channel (always 3 for response) + [1] = Reserved + [2-3] = Response data length + +Response Data: + [4] = ResponseTag // 0xA0=generic, 0xA3=readMem, 0xA7=property + [5-7] = Reserved + [8-10] = ResponseCode // 24-bit little-endian status + [11+] = Response-specific data +``` + +### Data Packet (WriteMemory data phase) + +``` +[0] = 2 // Channel 2 (data) +[1] = 0 +[2] = Data chunk length // 1-28 bytes typically +[3] = 0 +[4+] = Actual firmware data +``` + +## Response Tags + +| Tag | Description | +|-----|-------------| +| 0xA0 | Generic response | +| 0xA3 | ReadMemory response | +| 0xA7 | Property response | +| 0xAF | FlashReadOnce response | + +## Response Codes + +| Code | Description | +|------|-------------| +| 0 | Success | +| 1 | Fail | +| 2 | ReadOnly | +| 3 | OutOfRange | +| 4 | InvalidArgument | +| 100-106 | Flash driver errors | +| 200-202 | I2C driver errors | +| 10000-10005 | Bootloader errors | + +## Properties + +| ID | Property | +|----|----------| +| 0x01 | BootloaderVersion | +| 0x03 | FlashStartAddress | +| 0x04 | FlashSize | +| 0x05 | FlashSectorSize | +| 0x0B | MaxPacketSize | +| 0x0E | RAMStartAddress | +| 0x0F | RAMSize | +| 0x11 | FlashSecurityState | + +## Flashing State Machine + +### Right Half (Direct USB) + +1. Reenumerate device into bootloader mode +2. Create KBoot USB peripheral instance +3. `flashSecurityDisable([0x01-0x08])` - Unlock flash +4. `flashEraseRegion(0xC000, 475136)` - Erase application region +5. Read firmware from .hex file +6. `writeMemory(startAddress, data)` - Write in chunks +7. `reset()` - Reboot to firmware + +### Module (I2C via Buspal) + +1. Reenumerate to NormalKeyboard mode +2. Send ping to module via I2C (100 retries) +3. Send jump-to-bootloader to module +4. Poll CurrentKbootCommand until idle +5. Reenumerate into Buspal mode +6. `configureI2c(moduleAddress, speed=64)` +7. `flashEraseAllUnsecure()` +8. Read firmware from binary file +9. `writeMemory(0, data)` +10. `reset()` +11. Reenumerate back to normal keyboard +12. Send reset command to module +13. Send idle command to module + +## Module I2C Addresses (Buspal) + +From `lib/agent/packages/uhk-common`: + +| Module | Address | +|--------|---------| +| Left Half | 0x40 | +| Key Cluster Left | 0x41 | +| Trackball Right | 0x42 | +| Trackpoint Right | 0x43 | +| Touchpad Right | 0x44 | + +## WriteMemory Two-Phase Protocol + +1. **Command phase**: Send WriteMemory with address and total size +2. **Data phase**: Send data in 32-byte HID packets (channel 2) +3. **Response phase**: Receive confirmation after all data sent + +## Timeout Handling + +- Response timeout: 2000 ms +- USB read timeout: 1000 ms +- Buspal connection retries: 30 seconds with 2-second intervals + +## Agent Implementation Files + +| File | Purpose | +|------|---------| +| `lib/agent/packages/kboot/src/kboot.ts` | Main KBoot class | +| `lib/agent/packages/kboot/src/usb-peripheral.ts` | USB peripheral | +| `lib/agent/packages/kboot/src/util/usb/encode-command-option.ts` | Command encoding | +| `lib/agent/packages/kboot/src/util/usb/decode-command-response.ts` | Response parsing | +| `lib/agent/packages/uhk-usb/src/uhk-operations.ts` | Firmware update workflows | + +## Porting Considerations + +### For Firmware Implementation + +1. **Packet Encoding**: Implement 32-byte command packet builder +2. **Response Parsing**: Handle 0xA0/0xA3/0xA7 response tags +3. **Data Phase**: Support multi-packet data transfer +4. **I2C Transport**: Replace USB with I2C for module communication +5. **State Machine**: Implement erase → write → reset sequence +6. **Error Handling**: Handle I2C timeouts and retry logic + +### Memory Requirements + +- Command buffer: 32 bytes +- Response buffer: 32 bytes +- Data chunk buffer: 32 bytes +- Firmware staging: Depends on approach (streaming vs buffered) + +### Key Differences from Agent + +1. No USB layer (direct I2C to module bootloader) +2. No async/await (use callbacks or blocking I2C) +3. Limited RAM (streaming approach preferred) +4. CRC verification may be needed for I2C reliability diff --git a/doc-dev/other/flashing/module-communication.md b/doc-dev/other/flashing/module-communication.md new file mode 100644 index 000000000..4564a8546 --- /dev/null +++ b/doc-dev/other/flashing/module-communication.md @@ -0,0 +1,222 @@ +# Module Communication + +This document describes how the right half communicates with modules (key cluster, trackball, trackpad) over I2C and UART. + +## I2C Addresses + +From `shared/i2c_addresses.h`: + +| Module | Firmware Address | Bootloader Address | +|--------|-----------------|-------------------| +| Left Half | 0x08 | 0x10 | +| Left Module | 0x18 | 0x20 | +| Right Module | 0x28 | 0x30 | +| Touchpad | 0x2D | 0x6D | + +## I2C API (UHK60) + +**File**: `right/src/i2c.c` + +```c +// Non-blocking I2C operations +I2cAsyncWrite(uint8_t i2cAddress, uint8_t *data, size_t dataSize); +I2cAsyncRead(uint8_t i2cAddress, uint8_t *data, size_t dataSize); + +// Message-based operations with CRC16 +I2cAsyncWriteMessage(uint8_t i2cAddress, i2c_message_t *message); +I2cAsyncReadMessage(uint8_t i2cAddress, i2c_message_t *message); +``` + +**Hardware Configuration**: +- Main bus: I2C0 at 100 kHz (30 kHz for bootloader) +- EEPROM bus: I2C1 at 1 MHz + +## UART API (UHK80) + +**File**: `device/src/keyboard/uart_modules.c` + +- Runs in separate kernel thread +- Semaphore-based synchronization +- Configurable timeout (MODULE_CONNECTION_TIMEOUT) + +## Slave Scheduler + +**File**: `right/src/slave_scheduler.c` + +The slave scheduler is a **round-robin, interrupt-driven I2C master** managing all module communication. + +### Slave Types + +```c +typedef enum { + SlaveId_LeftKeyboardHalf, + SlaveId_LeftModule, + SlaveId_RightModule, + SlaveId_RightTouchpad, + SlaveId_RightLedDriver, + SlaveId_LeftLedDriver, + SlaveId_ModuleLeftLedDriver, + SlaveId_KbootDriver, +} slave_id_t; +``` + +### Slave Data Structure + +```c +typedef struct { + uint8_t perDriverId; + slave_init_t *init; + slave_update_t *update; + slave_disconnect_t *disconnect; + slave_connect_t *connect; + bool isConnected; + status_t previousStatus; +} uhk_slave_t; + +extern uhk_slave_t Slaves[SLAVE_COUNT]; // 8 slaves +``` + +### Scheduling Flow + +1. `slaveSchedulerCallback()` - I2C interrupt handler +2. Finalize previous transfer, check for errors +3. Update connection status (connect/disconnect callbacks) +4. Try next slave(s) in round-robin order +5. Call `slave->update()` to get next transfer +6. Issue scheduled I2C transfer + +## Module Discovery Protocol + +**File**: `right/src/slave_drivers/uhk_module_driver.c` + +Discovery phases executed in sequence: + +1. **Sync Request**: Validate "SYNC" string +2. **Protocol Version**: Get 3-byte version +3. **Firmware Version**: Get 3-byte version +4. **Module ID**: Identify module type +5. **Key Count**: Number of keys +6. **Pointer Count**: Number of pointer inputs +7. **Git Tag**: Version tag (protocol v4.2+) +8. **Git Repo**: Repository name +9. **Firmware Checksum**: MD5 checksum +10. **Update Loop**: Periodic key state polling + +### Module State Structure + +```c +typedef struct { + uint8_t moduleId; + version_t moduleProtocolVersion; + version_t firmwareVersion; + uhk_module_phase_t phase; + i2c_message_t rxMessage; + uint8_t firmwareI2cAddress; + uint8_t bootloaderI2cAddress; + uint8_t keyCount; + uint8_t pointerCount; + char gitTag[MAX_STRING_PROPERTY]; + char firmwareChecksum[MD5_CHECKSUM]; +} uhk_module_state_t; +``` + +## Slave Protocol + +**File**: `shared/slave_protocol.h` + +### Message Format + +```c +typedef struct { + uint8_t length; + uint16_t crc; + uint8_t data[SLAVE_PROTOCOL_MAX_PAYLOAD]; // max 64 bytes + uint8_t padding; +} ATTR_PACKED i2c_message_t; +``` + +### Commands + +```c +typedef enum { + SlaveCommand_RequestProperty = 0, + SlaveCommand_JumpToBootloader = 1, + SlaveCommand_RequestKeyStates = 2, + SlaveCommand_SetTestLed = 3, + SlaveCommand_SetLedPwmBrightness = 4, + SlaveCommand_ModuleSpecificCommand = 5, +} slave_command_t; +``` + +### Properties + +```c +typedef enum { + SlaveProperty_Sync = 0, + SlaveProperty_ModuleProtocolVersion = 1, + SlaveProperty_FirmwareVersion = 2, + SlaveProperty_ModuleId = 3, + SlaveProperty_KeyCount = 4, + SlaveProperty_PointerCount = 5, + SlaveProperty_GitTag = 6, + SlaveProperty_GitRepo = 7, + SlaveProperty_FirmwareChecksum = 8, +} slave_property_t; +``` + +## Module Reset Mechanisms + +### Jump to Bootloader + +```c +case UhkModulePhase_JumpToBootloader: + txMessage.data[0] = SlaveCommand_JumpToBootloader; + txMessage.length = 1; + res.status = tx(i2cAddress); + break; +``` + +### Trackpoint Reset + +```c +void UhkModuleSlaveDriver_SendTrackpointCommand(module_specific_command_t command); +``` + +Commands: `ResetTrackpoint`, `RunTrackpoint`, `TrackpointSignalData`, `TrackpointSignalClock` + +## Connection Status Management + +```c +void UhkModuleSlaveDriver_Disconnect(uint8_t uhkModuleDriverId) { + // Clear module state + // Clear key states + // Schedule reconnection timeout (350ms) +} +``` + +## Communication Flow Diagram + +``` +Right Half Module (I2C) + | | + |-- SlaveCommand_RequestProperty -->| + | (SlaveProperty_Sync) | + |<--------- "SYNC" response --------| + | | + |-- SlaveCommand_RequestProperty -->| + | (SlaveProperty_ModuleId) | + |<--------- ModuleId (1 byte) ------| + | | + |-- [Loop: RequestKeyStates] ------>| + |<--------- KeyStates + Pointer ----| +``` + +## Key Source Files + +- `right/src/slave_scheduler.c` and `.h` - Round-robin scheduler +- `right/src/slave_drivers/uhk_module_driver.c` - Module discovery/polling +- `right/src/slave_drivers/kboot_driver.c` - Bootloader communication +- `right/src/i2c.c` - I2C primitives (UHK60) +- `device/src/keyboard/uart_modules.c` - UART modules (UHK80) +- `shared/slave_protocol.h` - Protocol definitions +- `shared/i2c_addresses.h` - I2C address mapping diff --git a/doc-dev/other/flashing/usb-communication.md b/doc-dev/other/flashing/usb-communication.md new file mode 100644 index 000000000..50a323bb6 --- /dev/null +++ b/doc-dev/other/flashing/usb-communication.md @@ -0,0 +1,178 @@ +# USB Communication APIs + +This document describes the USB command infrastructure used for host-to-device communication. + +## USB Command Handling + +### Main Protocol Handler + +**File**: `right/src/usb_protocol_handler.c` + +The `UsbProtocolHandler()` function is the central dispatcher that: +- Receives command byte from position 0 in RX buffer +- Routes to appropriate command handler via switch statement +- Clears RX buffer after processing + +### Command IDs + +From `right/src/usb_protocol_handler.h`: + +```c +typedef enum { + UsbCommandId_GetDeviceProperty = 0x00, + UsbCommandId_Reenumerate = 0x01, + UsbCommandId_JumpToModuleBootloader = 0x02, + UsbCommandId_SendKbootCommandToModule = 0x03, + UsbCommandId_ReadConfig = 0x04, + UsbCommandId_WriteHardwareConfig = 0x05, + UsbCommandId_WriteStagingUserConfig = 0x06, + UsbCommandId_ApplyConfig = 0x07, + UsbCommandId_LaunchStorageTransfer = 0x08, + UsbCommandId_GetDeviceState = 0x09, + // ... more commands up to 0x1f +} usb_command_id_t; +``` + +## Buffer Specifications + +From `right/src/usb_interfaces/usb_interface_generic_hid.h`: + +```c +#define USB_GENERIC_HID_INTERRUPT_IN_PACKET_SIZE 63 +#define USB_GENERIC_HID_INTERRUPT_OUT_PACKET_SIZE 63 +#define USB_GENERIC_HID_IN_BUFFER_LENGTH 63 +#define USB_GENERIC_HID_OUT_BUFFER_LENGTH 63 +``` + +- **Packet size**: 63 bytes (USB interrupt endpoint) +- **Available payload**: 62 bytes (after status code) + +## Buffer Access Macros + +From `right/src/usb_protocol_handler.h`: + +```c +#define GetUsbRxBufferUint8(OFFSET) (GetBufferUint8(GenericHidOutBuffer, OFFSET)) +#define GetUsbRxBufferUint16(OFFSET) (GetBufferUint16(GenericHidOutBuffer, OFFSET)) +#define GetUsbRxBufferUint32(OFFSET) (GetBufferUint32(GenericHidOutBuffer, OFFSET)) + +#define SetUsbTxBufferUint8(OFFSET, VALUE) (SetBufferUint8(GenericHidInBuffer, OFFSET, VALUE)) +#define SetUsbTxBufferUint16(OFFSET, VALUE) (SetBufferUint16(GenericHidInBuffer, OFFSET, VALUE)) +#define SetUsbTxBufferUint32(OFFSET, VALUE) (SetBufferUint32(GenericHidInBuffer, OFFSET, VALUE)) +``` + +## Chunked Transfer Pattern + +Large data transfers use a 4-byte header followed by data: + +``` +Header (4 bytes): + [0] = Command ID + [1] = Length + [2] = Offset low byte + [3] = Offset high byte + +Data (up to 59 bytes): + [4+] = Payload +``` + +This allows transfers up to 64 KB using 16-bit offset addressing. + +## Status Codes + +### General Status Codes + +```c +typedef enum { + UsbStatusCode_Success = 0, + UsbStatusCode_InvalidCommand = 1, + UsbStatusCode_Busy = 2, +} usb_status_code_general_t; +``` + +### Command-Specific Status Codes + +Each command defines its own status enum. Example from read config: + +```c +typedef enum { + UsbStatusCode_ReadConfig_InvalidConfigBufferId = 2, + UsbStatusCode_ReadConfig_LengthTooLarge = 3, + UsbStatusCode_ReadConfig_BufferOutOfBounds = 4, +} usb_status_code_read_config_t; +``` + +## Existing Module-Related Commands + +### JumpToModuleBootloader (0x02) + +``` +Request: + [0] = 0x02 + [1] = Module slot ID + +Response: + [0] = Status code +``` + +### SendKbootCommandToModule (0x03) + +``` +Request: + [0] = 0x03 + [1] = K-boot command (0=idle, 1=ping, 2=reset) + [2] = Module I2C address (if command != idle) + +Response: + [0] = Status code +``` + +## Agent-Side Implementation + +### USB Device Wrapper + +**File**: `lib/agent/packages/uhk-usb/src/uhk-hid-device.ts` + +```typescript +public async write(buffer: Buffer): Promise { + const device = await this.getDevice(); + await device.write(sendData); + let receivedData = await device.read(1000); + if (receivedData[0] !== 0) { + throw new Error(`Response code: ${receivedData[0]}`); + } + return Buffer.from(receivedData); +} +``` + +### Fragment Generation + +**File**: `lib/agent/packages/uhk-usb/src/util.ts` + +```typescript +export function getTransferBuffers(usbCommand: UsbCommand, configBuffer: Buffer): Buffer[] { + const MAX_SENDING_PAYLOAD_SIZE = MAX_USB_PAYLOAD_SIZE - 4; // 59 bytes + // Creates array of 63-byte buffers with header + data chunks +} +``` + +## Extension Points for Firmware Upload + +1. **New USB Command IDs**: Reserve 0x20-0x2f for firmware operations +2. **Chunked Transfer**: Use existing 4-byte header pattern +3. **Flash Management**: + - Erase flash region command + - Write flash command with address/data + - Verify checksum command +4. **Status Monitoring**: + - Query transfer progress + - Get remaining space + - Query write status + +## Key Source Files + +- `right/src/usb_protocol_handler.c` and `.h` - Main dispatcher +- `right/src/usb_commands/` - Individual command handlers +- `right/src/usb_interfaces/usb_interface_generic_hid.c` - HID interface +- `shared/buffer.h` - Buffer utilities +- `lib/agent/packages/uhk-usb/src/uhk-operations.ts` - Agent operations diff --git a/right/src/CMakeLists.txt b/right/src/CMakeLists.txt index 18b5b38ef..c2ae57250 100644 --- a/right/src/CMakeLists.txt +++ b/right/src/CMakeLists.txt @@ -50,6 +50,7 @@ target_sources(${PROJECT_NAME} PRIVATE macro_recorder.c $<$:${CMAKE_CURRENT_SOURCE_DIR}/main.c> $<$:${CMAKE_CURRENT_SOURCE_DIR}/module.c> + module_flash.c mouse_controller.c mouse_keys.c module.c diff --git a/right/src/config_parser/config_globals.c b/right/src/config_parser/config_globals.c index 616d2762c..2e44e76ae 100644 --- a/right/src/config_parser/config_globals.c +++ b/right/src/config_parser/config_globals.c @@ -30,7 +30,7 @@ bool ParserRunDry; bool IsConfigBufferIdValid(config_buffer_id_t configBufferId) { - return ConfigBufferId_HardwareConfig <= configBufferId && configBufferId <= ConfigBufferId_ValidatedUserConfig; + return ConfigBufferId_HardwareConfig <= configBufferId && configBufferId <= ConfigBufferId_ModuleFirmware; } config_buffer_t* ConfigBufferIdToConfigBuffer(config_buffer_id_t configBufferId) @@ -42,6 +42,8 @@ config_buffer_t* ConfigBufferIdToConfigBuffer(config_buffer_id_t configBufferId) return &StagingUserConfigBuffer; case ConfigBufferId_ValidatedUserConfig: return &ValidatedUserConfigBuffer; + case ConfigBufferId_ModuleFirmware: + return &StagingUserConfigBuffer; default: return NULL; } @@ -54,6 +56,7 @@ uint16_t ConfigBufferIdToBufferSize(config_buffer_id_t configBufferId) return HARDWARE_CONFIG_SIZE; case ConfigBufferId_StagingUserConfig: case ConfigBufferId_ValidatedUserConfig: + case ConfigBufferId_ModuleFirmware: return USER_CONFIG_SIZE; default: return 0; diff --git a/right/src/config_parser/config_globals.h b/right/src/config_parser/config_globals.h index 4462e2f1d..7019da237 100644 --- a/right/src/config_parser/config_globals.h +++ b/right/src/config_parser/config_globals.h @@ -19,6 +19,7 @@ ConfigBufferId_HardwareConfig, ConfigBufferId_StagingUserConfig, ConfigBufferId_ValidatedUserConfig, + ConfigBufferId_ModuleFirmware, } config_buffer_id_t; typedef struct { diff --git a/right/src/module_flash.c b/right/src/module_flash.c new file mode 100644 index 000000000..092a71c8b --- /dev/null +++ b/right/src/module_flash.c @@ -0,0 +1,5 @@ +#include "module_flash.h" + +module_flash_state_t ModuleFlashState = ModuleFlashState_Idle; +bool ModuleFlashBusy = false; +uint8_t ModuleFlashErrorCode = 0; diff --git a/right/src/module_flash.h b/right/src/module_flash.h new file mode 100644 index 000000000..3a82c1393 --- /dev/null +++ b/right/src/module_flash.h @@ -0,0 +1,25 @@ +#ifndef __MODULE_FLASH_H__ +#define __MODULE_FLASH_H__ + +// Includes: + + #include + #include + +// Typedefs: + + typedef enum { + ModuleFlashState_Idle = 0, + ModuleFlashState_Erasing = 1, + ModuleFlashState_Writing = 2, + ModuleFlashState_Done = 3, + ModuleFlashState_Error = 4, + } module_flash_state_t; + +// Variables: + + extern module_flash_state_t ModuleFlashState; + extern bool ModuleFlashBusy; + extern uint8_t ModuleFlashErrorCode; + +#endif diff --git a/right/src/usb_commands/CMakeLists.txt b/right/src/usb_commands/CMakeLists.txt index 9ba4fc83c..e1228c2e1 100644 --- a/right/src/usb_commands/CMakeLists.txt +++ b/right/src/usb_commands/CMakeLists.txt @@ -25,4 +25,8 @@ target_sources(${PROJECT_NAME} PRIVATE usb_command_set_variable.c usb_command_switch_keymap.c usb_command_write_config.c + usb_command_write_module_firmware.c + usb_command_flash_module.c + usb_command_get_module_flash_state.c + usb_command_validate_buffer_crc.c ) diff --git a/right/src/usb_commands/usb_command_flash_module.c b/right/src/usb_commands/usb_command_flash_module.c new file mode 100644 index 000000000..edead304b --- /dev/null +++ b/right/src/usb_commands/usb_command_flash_module.c @@ -0,0 +1,32 @@ +#include "usb_commands/usb_command_flash_module.h" +#include "usb_protocol_handler.h" +#include "module_flash.h" +#include "slot.h" + +typedef enum { + UsbStatusCode_FlashModule_InvalidSlotId = 2, + UsbStatusCode_FlashModule_Busy = 3, +} usb_status_code_flash_module_t; + +void UsbCommand_FlashModule(const uint8_t *GenericHidOutBuffer, uint8_t *GenericHidInBuffer) +{ + uint8_t slotId = GetUsbRxBufferUint8(1); + + if (!IS_VALID_MODULE_SLOT(slotId)) { + SetUsbTxBufferUint8(0, UsbStatusCode_FlashModule_InvalidSlotId); + return; + } + + if (ModuleFlashBusy) { + SetUsbTxBufferUint8(0, UsbStatusCode_FlashModule_Busy); + return; + } + + ModuleFlashBusy = true; + ModuleFlashErrorCode = 0; + + // TODO: Trigger actual K-boot flash sequence here. + // For now, stub: immediately mark as done. + ModuleFlashState = ModuleFlashState_Done; + ModuleFlashBusy = false; +} diff --git a/right/src/usb_commands/usb_command_flash_module.h b/right/src/usb_commands/usb_command_flash_module.h new file mode 100644 index 000000000..1da96ba37 --- /dev/null +++ b/right/src/usb_commands/usb_command_flash_module.h @@ -0,0 +1,12 @@ +#ifndef __USB_COMMAND_FLASH_MODULE_H__ +#define __USB_COMMAND_FLASH_MODULE_H__ + +// Includes: + + #include + +// Functions: + + void UsbCommand_FlashModule(const uint8_t *GenericHidOutBuffer, uint8_t *GenericHidInBuffer); + +#endif diff --git a/right/src/usb_commands/usb_command_get_device_state.c b/right/src/usb_commands/usb_command_get_device_state.c index a2c567f17..9b617b7da 100644 --- a/right/src/usb_commands/usb_command_get_device_state.c +++ b/right/src/usb_commands/usb_command_get_device_state.c @@ -21,6 +21,7 @@ #include "config_manager.h" #include "usb_log_buffer.h" +#include "module_flash.h" #ifdef __ZEPHYR__ #include "flash.h" @@ -70,11 +71,14 @@ void UsbCommand_GetKeyboardState(const uint8_t *GenericHidOutBuffer, uint8_t *Ge { detectFreezes(); + uint8_t byte1 = 0; #ifdef __ZEPHYR__ - SetUsbTxBufferUint8(1, Flash_IsBusy()); + byte1 |= (Flash_IsBusy() ? GetDeviceStateByte1_EepromBusy : 0); #else - SetUsbTxBufferUint8(1, IsStorageBusy); + byte1 |= (IsStorageBusy ? GetDeviceStateByte1_EepromBusy : 0); #endif + byte1 |= (ModuleFlashBusy ? GetDeviceStateByte1_ModuleFlashBusy : 0); + SetUsbTxBufferUint8(1, byte1); uint8_t byte2 = 0 | (MergeSensor_IsMerged() == MergeSensorState_Joined ? GetDeviceStateByte2_HalvesMerged : 0) diff --git a/right/src/usb_commands/usb_command_get_device_state.h b/right/src/usb_commands/usb_command_get_device_state.h index 4c146ae56..879c93e24 100644 --- a/right/src/usb_commands/usb_command_get_device_state.h +++ b/right/src/usb_commands/usb_command_get_device_state.h @@ -7,12 +7,16 @@ // Typedefs: +typedef enum { + GetDeviceStateByte1_EepromBusy = 1 << 0, + GetDeviceStateByte1_ModuleFlashBusy = 1 << 1, +} usb_command_get_device_state_byte1_mask_t; + typedef enum { GetDeviceStateByte2_HalvesMerged = 1 << 0, GetDeviceStateByte2_PairingInProgress = 1 << 1, GetDeviceStateByte2_NewPairedDevice = 1 << 2, GetDeviceStateByte2_ZephyrLog = 1 << 3, - } usb_command_get_device_state_byte2_mask_t; typedef enum { diff --git a/right/src/usb_commands/usb_command_get_module_flash_state.c b/right/src/usb_commands/usb_command_get_module_flash_state.c new file mode 100644 index 000000000..f86459b9a --- /dev/null +++ b/right/src/usb_commands/usb_command_get_module_flash_state.c @@ -0,0 +1,9 @@ +#include "usb_commands/usb_command_get_module_flash_state.h" +#include "usb_protocol_handler.h" +#include "module_flash.h" + +void UsbCommand_GetModuleFlashState(const uint8_t *GenericHidOutBuffer, uint8_t *GenericHidInBuffer) +{ + SetUsbTxBufferUint8(1, ModuleFlashState); + SetUsbTxBufferUint8(2, ModuleFlashErrorCode); +} diff --git a/right/src/usb_commands/usb_command_get_module_flash_state.h b/right/src/usb_commands/usb_command_get_module_flash_state.h new file mode 100644 index 000000000..0cbdc697c --- /dev/null +++ b/right/src/usb_commands/usb_command_get_module_flash_state.h @@ -0,0 +1,12 @@ +#ifndef __USB_COMMAND_GET_MODULE_FLASH_STATE_H__ +#define __USB_COMMAND_GET_MODULE_FLASH_STATE_H__ + +// Includes: + + #include + +// Functions: + + void UsbCommand_GetModuleFlashState(const uint8_t *GenericHidOutBuffer, uint8_t *GenericHidInBuffer); + +#endif diff --git a/right/src/usb_commands/usb_command_validate_buffer_crc.c b/right/src/usb_commands/usb_command_validate_buffer_crc.c new file mode 100644 index 000000000..f3d7e7e09 --- /dev/null +++ b/right/src/usb_commands/usb_command_validate_buffer_crc.c @@ -0,0 +1,36 @@ +#include "usb_commands/usb_command_validate_buffer_crc.h" +#include "usb_protocol_handler.h" +#include "config_parser/config_globals.h" +#include "crc16.h" + +void UsbCommand_ValidateBufferCrc(const uint8_t *GenericHidOutBuffer, uint8_t *GenericHidInBuffer) +{ + uint8_t bufferId = GetUsbRxBufferUint8(1); + uint16_t expectedSize = GetUsbRxBufferUint16(2); + uint16_t expectedCrc = GetUsbRxBufferUint16(4); + + if (!IsConfigBufferIdValid(bufferId)) { + SetUsbTxBufferUint8(0, UsbStatusCode_ValidateBufferCrc_InvalidBufferId); + return; + } + + config_buffer_t *buffer = ConfigBufferIdToConfigBuffer(bufferId); + uint16_t bufferSize = ConfigBufferIdToBufferSize(bufferId); + + if (expectedSize > bufferSize) { + SetUsbTxBufferUint8(0, UsbStatusCode_ValidateBufferCrc_SizeOutOfBounds); + return; + } + + crc16_data_t crcData; + crc16_init(&crcData); + crc16_update(&crcData, buffer->buffer, expectedSize); + + uint16_t computedCrc; + crc16_finalize(&crcData, &computedCrc); + + if (computedCrc != expectedCrc) { + SetUsbTxBufferUint8(0, UsbStatusCode_ValidateBufferCrc_CrcMismatch); + return; + } +} diff --git a/right/src/usb_commands/usb_command_validate_buffer_crc.h b/right/src/usb_commands/usb_command_validate_buffer_crc.h new file mode 100644 index 000000000..64fc99f58 --- /dev/null +++ b/right/src/usb_commands/usb_command_validate_buffer_crc.h @@ -0,0 +1,20 @@ +#ifndef __USB_COMMAND_VALIDATE_BUFFER_CRC_H__ +#define __USB_COMMAND_VALIDATE_BUFFER_CRC_H__ + +// Includes: + + #include + +// Typedefs: + + typedef enum { + UsbStatusCode_ValidateBufferCrc_CrcMismatch = 2, + UsbStatusCode_ValidateBufferCrc_InvalidBufferId = 3, + UsbStatusCode_ValidateBufferCrc_SizeOutOfBounds = 4, + } usb_status_code_validate_buffer_crc_t; + +// Functions: + + void UsbCommand_ValidateBufferCrc(const uint8_t *GenericHidOutBuffer, uint8_t *GenericHidInBuffer); + +#endif diff --git a/right/src/usb_commands/usb_command_write_module_firmware.c b/right/src/usb_commands/usb_command_write_module_firmware.c new file mode 100644 index 000000000..20fc1f74e --- /dev/null +++ b/right/src/usb_commands/usb_command_write_module_firmware.c @@ -0,0 +1,10 @@ +#include +#include "usb_commands/usb_command_write_module_firmware.h" +#include "usb_commands/usb_command_write_config.h" +#include "usb_protocol_handler.h" +#include "config_parser/config_globals.h" + +void UsbCommand_WriteModuleFirmware(const uint8_t *GenericHidOutBuffer, uint8_t *GenericHidInBuffer) +{ + UsbCommand_WriteConfig(ConfigBufferId_ModuleFirmware, GenericHidOutBuffer, GenericHidInBuffer); +} diff --git a/right/src/usb_commands/usb_command_write_module_firmware.h b/right/src/usb_commands/usb_command_write_module_firmware.h new file mode 100644 index 000000000..c4f6599b0 --- /dev/null +++ b/right/src/usb_commands/usb_command_write_module_firmware.h @@ -0,0 +1,12 @@ +#ifndef __USB_COMMAND_WRITE_MODULE_FIRMWARE_H__ +#define __USB_COMMAND_WRITE_MODULE_FIRMWARE_H__ + +// Includes: + + #include + +// Functions: + + void UsbCommand_WriteModuleFirmware(const uint8_t *GenericHidOutBuffer, uint8_t *GenericHidInBuffer); + +#endif diff --git a/right/src/usb_protocol_handler.c b/right/src/usb_protocol_handler.c index edae63f14..b3d430de0 100644 --- a/right/src/usb_protocol_handler.c +++ b/right/src/usb_protocol_handler.c @@ -16,6 +16,10 @@ #include "usb_commands/usb_command_launch_storage_transfer.h" #include "usb_commands/usb_command_get_module_property.h" #include "usb_commands/usb_command_exec_shell_command.h" +#include "usb_commands/usb_command_write_module_firmware.h" +#include "usb_commands/usb_command_flash_module.h" +#include "usb_commands/usb_command_get_module_flash_state.h" +#include "usb_commands/usb_command_validate_buffer_crc.h" #ifdef __ZEPHYR__ #include "usb_commands/usb_command_draw_oled.h" @@ -86,6 +90,18 @@ void UsbProtocolHandler(uint8_t *GenericHidOutBuffer, uint8_t *GenericHidInBuffe case UsbCommandId_GetModuleProperty: UsbCommand_GetModuleProperty(GenericHidOutBuffer, GenericHidInBuffer); break; + case UsbCommandId_WriteModuleFirmware: + UsbCommand_WriteModuleFirmware(GenericHidOutBuffer, GenericHidInBuffer); + break; + case UsbCommandId_FlashModule: + UsbCommand_FlashModule(GenericHidOutBuffer, GenericHidInBuffer); + break; + case UsbCommandId_GetModuleFlashState: + UsbCommand_GetModuleFlashState(GenericHidOutBuffer, GenericHidInBuffer); + break; + case UsbCommandId_ValidateBufferCrc: + UsbCommand_ValidateBufferCrc(GenericHidOutBuffer, GenericHidInBuffer); + break; #ifdef __ZEPHYR__ case UsbCommandId_ExecShellCommand: UsbCommand_ExecShellCommand(GenericHidOutBuffer, GenericHidInBuffer); diff --git a/right/src/usb_protocol_handler.h b/right/src/usb_protocol_handler.h index 744923117..44811f732 100644 --- a/right/src/usb_protocol_handler.h +++ b/right/src/usb_protocol_handler.h @@ -69,6 +69,11 @@ UsbCommandId_EraseBleSettings = 0x1d, UsbCommandId_ExecShellCommand = 0x1e, UsbCommandId_ReadOled = 0x1f, + + UsbCommandId_WriteModuleFirmware = 0x20, + UsbCommandId_FlashModule = 0x21, + UsbCommandId_GetModuleFlashState = 0x22, + UsbCommandId_ValidateBufferCrc = 0x23, } usb_command_id_t; typedef enum {