hdmi-hal-i2c-dev 0.2.0

Linux userspace hdmi-hal backend via /dev/i2c-N (i2c-dev)
Documentation
# Testing Strategy

Testing is split into two tiers based on what each tier validates and what it requires.

## Tier 1 — No hardware

Runs with `cargo test --locked`. No kernel module, no device node, no special
privileges. Runs in standard CI on any Linux host.

### `connector_ddc_adapter` (in `src/discovery.rs`)

Tested against a synthetic sysfs tree constructed in a `tempfile` temporary directory.
Each test creates the expected symlink structure under a configurable root and calls the
internal `connector_ddc_adapter_in` function with that root substituted for
`/sys/class/drm`.

**Covered:**
- Successful resolution of a valid connector with a realistic multi-component symlink target
- `ConnectorNotFound` when the connector directory is absent
- `ConnectorHasNoDdcAdapter` when the connector directory exists but has no `ddc` symlink
- `DdcAdapterIndexUnparseable` when the symlink target's final component does not start
  with `i2c-`
- `DdcAdapterIndexUnparseable` when the suffix after `i2c-` is not all digits
- `DdcAdapterIndexUnparseable` when the suffix is empty (`i2c-` with nothing after it)
- Multi-digit adapter indices

### `I2cErrorKind` errno mapping (in `src/transport.rs`)

Unit tests for the `map_errno` function, which maps raw errno integers to `I2cErrorKind`
variants following `Documentation/i2c/fault-codes.rst`.

**Covered:**
- `ENXIO` → `AddressNack`
- `EAGAIN` → `ArbitrationLost`
- `ETIMEDOUT` → `ClockStretchTimeout`
- `EBUSY` → `BusBusy`
- `EPROTO` → `BusProtocolError`
- `ESHUTDOWN` → `AdapterGone`
- `ENODEV` → `AdapterGone`
- `EIO` → `Unknown { errno: EIO }` (the only errno produced by `amdgpu`)
- Unrecognised errno → `Unknown { errno }`
- Zero errno → `Unknown { errno: 0 }` (produced by the partial-completion path)

### `StubPhy` (in `src/phy_tests.rs`)

Unit tests for the PHY stub, which forwards every `HdmiPhy` call to a callback as a
`PhyCall`.

**Covered:**
- Each `HdmiPhy` method forwards the matching `PhyCall` variant with its arguments:
  `set_frl_rate`, `send_ltp`, `set_frl_output` (`GapOnly` and `Active`),
  `adjust_equalization` (including per-lane TxFFE levels) and `set_scrambling` (on and
  off)
- The callback runs once per call
- A stub with a no-op callback accepts calls
- Recorded calls compare as a whole sequence (`PhyCall` is `Copy` and `Eq`)

## Tier 2 — `i2c-stub` integration tests

Gated behind `--features integration`. Requires the `i2c-stub` kernel module and a
device node with appropriate permissions. See [`doc/setup.md`](setup.md) for setup
instructions. **Not run in standard CI.** Must be run before any release.

These tests exercise the full SMBus byte-data (`I2C_SMBUS`) ioctl path through
`I2cDevTransport` and validate things that the Tier 1 tests cannot: that messages are
constructed correctly, that the slave address 0x54 is set, and that the ioctl round-trip
through the kernel produces correct results.

**Covered:**
- An unwritten register reads back as `0x00` (stub initialises all registers to zero)
- A write operation returns `Ok(())`
- A value written to a register can be read back (round-trip correctness)
- Multiple registers are independent
- A transaction to an unregistered address produces a `Transaction` error (the kind will
  be `AddressNack` on compliant adapters, or `Unknown { errno: EIO }` on `amdgpu`)

**Intentionally not covered:**
- Bus errors, clock stretch timeouts, and arbitration loss — `i2c-stub` always ACKs
  registered addresses; these conditions require misbehaving real hardware. Their
  errno→`I2cErrorKind` mapping is validated by the Tier 1 unit tests.
- `connector_ddc_adapter` end-to-end against a real sysfs tree — the Tier 1 synthetic
  tests are sufficient; the sysfs parsing logic has no dependency on a live system.