# Persistent Configuration

## Flash Config Storage

The ATSAMG55G19 provides a **512-byte user signature** region in Flash, used for persistent configuration storage.

### TLV+CRC Format

All config variables are stored in **Tag-Length-Value-CRC** format:

```
| Tag (1 byte) | Length (1 byte) | Value (N bytes) | CRC-8 (1 byte) |
```

- **Tag:** Identifies the variable type (see table below)
- **Length:** Number of data bytes
- **Value:** The actual setting data
- **CRC-8:** Integrity check (polynomial `0x07`, init `0xFF`)
- **End marker:** Tag `0xFF` indicates end of valid data (all unused bytes are `0xFF`)

Variables are stored sequentially — there is no fixed byte offset for any variable. The entire 512-byte region must be read and parsed from start to finish.

### Config Tags

| Tag | ID | Length | Description |
|-----|-----|--------|-------------|
| Serial | 0x01 | Variable | PCBA serial number (factory-programmed) |
| RGB Color | 0x02 | 3 | Default LED color: R, G, B bytes |
| Fan Speed | 0x03 | 1 | Default fan speed, 0–100% |
| Proximity Disable | 0x04 | 1 | Non-zero = proximity disabled (displays always on) |
| Linkbox V1 | 0x05 | 1 | HPD polarity inversion (deprecated) |
| Proximity Calibration | 0x06 | 2 | Null offset (uint16 LE) subtracted from readings |
| FATP Mode | 0x07 | 1 | Not used at boot; must use HID command |
| HMD Serial | 0x08 | Variable | Assembled HMD serial number |
| Tracking Serial | 0x09 | Variable | Tracking module serial number |
| Display Brightness | 0x0A | 2 | Startup brightness (uint16 LE), 0–1023 |
| Proximity Threshold | 0x0B | 2 | On/off trigger level (uint16 LE) |
| Proximity Hysteresis | 0x0C | 2 | Hysteresis window (uint16 LE) |
| EDID Override | 0x0D | 1 | 0=both 75+90Hz, 1=90Hz only, 2=75Hz only |
| Proximity User Trim | 0x0E | 2 | Signed 16-bit proximity threshold adjustment (v0.3.15) |
| VXR Sleep Enable | 0x0F | 1 | 0=no sleep (default), non-zero=enable VXR auto-sleep (v0.3.18) |
| Invalid/Blank | 0xFF | — | End of data marker |

### CRC-8 Calculation

**Polynomial:** `0x07`  
**Initial value:** `0xFF`  
**No input/output inversion**

```c
uint8_t crc8(const uint8_t* data, uint32_t len) {
    uint8_t crc = 0xFF;
    for (uint32_t i = 0; i < len; i++) {
        crc ^= data[i];
        for (uint8_t j = 0; j < 8; j++) {
            crc = (crc & 0x80) ? (crc << 1) ^ 0x07 : (crc << 1);
        }
    }
    return crc;
}
```

### Reading / Writing Config via HID

The 512-byte region is accessed in **32-byte blocks** (16 blocks total):

1. **Read:** Send `READ_SIG` (`U`) with block number 0–15. Returns 32 bytes.
2. **Write:** Send `WRITE_SIG` (`W`) with block number 0–15 and 32 bytes of data. Repeat for all 16 blocks.
3. **Save:** Send `SAVE_SIG` (`V`) to commit all written blocks to permanent Flash.

All 512 bytes must be written before saving, even if only one tag changed.

**Source files:** `src/eeprom_emulation.c/h`, `src/Drivers/signature.c/h`  
**Full specification:** `docs/Config_Format.md`

## EEPROM Emulation

For frequently-updated values (like usage timers), the firmware implements EEPROM emulation using two Flash pages:

- Values are appended to the current page until full
- When a page fills, valid entries are compacted and copied to the other blank page
- The original page is then erased
- This provides wear leveling across the Flash

**Bug fixed in v0.2.19:** A stuck state occurred every time the usage timers filled a Flash page and needed to be copied.

**Source files:** `src/eeprom_emulation.c/h`

## Usage Timers

Four usage counters are maintained in emulated EEPROM:

| Timer ID | Description | Added |
|----------|-------------|-------|
| 0 | Total powered-on time | v0.2.1 |
| 1 | Left display on time | v0.2.1 |
| 2 | Longest continuous display-on time | v0.2.1 |
| 3 | Right display on time | v0.2.13 |

- **Resolution:** 10-minute increments
- **Storage:** 32-bit unsigned integer (LSB first)
- **Read:** HID command `Z` with timer ID
- **Write:** HID command `z` with timer ID and 32-bit value

### Bug Fixes

| Version | Fix |
|---------|-----|
| v0.2.9 | Fixed longest continuous timer using wrong memory location |
| v0.2.19 | Fixed stuck state when EEPROM page copy triggered |
| v0.2.22 | Fixed timers counting up even with displays off |

## Flash Access Protection

Flash access is serialized with a FreeRTOS mutex to prevent concurrent access from multiple tasks:

- `flash_mutex_take()` / `flash_mutex_give()` wrapper functions
- Protects both user signature writes and EEPROM emulation operations

**Source files:** `src/Drivers/flash_mutex.c/h`

## Python Configuration Tools

| Tool | Description |
|------|-------------|
| `scripts/config_editor.py` | PyQt5-based GUI for editing all config tags |
| `scripts/config_editor_imgui.py` | Alternative ImGui-based config editor |
| `scripts/config_lib.py` | Shared library for config parsing and CRC |
| `scripts/restore_config.py` | Backup and restore config region |
| `scripts/update_hmd_serial.py` | Program HMD serial number |
| `scripts/SNPatcher/` | Packaged serial number patcher (PyInstaller) |
