Update docs: async sg transport, Drive::read recovery phases

This commit is contained in:
Matt Jackson
2026-04-21 18:01:48 +00:00
parent 71e1f57364
commit ce8132d181
2 changed files with 24 additions and 17 deletions
+3 -3
View File
@@ -42,7 +42,7 @@ libfreemkv (lib.rs)
├── Drive Access ├── Drive Access
│ ├── drive Drive — open, identify, init, unlock, read (with recovery) │ ├── drive Drive — open, identify, init, unlock, read (with recovery)
│ ├── scsi ScsiTransport trait + platform backends (SG_IO, IOKit, SPTI) │ ├── scsi ScsiTransport trait + platform backends (sg async, IOKit, SPTI)
│ ├── platform/ Platform trait — per-chipset command handlers │ ├── platform/ Platform trait — per-chipset command handlers
│ │ └── mt1959 MediaTek MT1959 driver (LG, ASUS, HP) │ │ └── mt1959 MediaTek MT1959 driver (LG, ASUS, HP)
│ ├── profile DriveProfile loading, matching, bundled JSON │ ├── profile DriveProfile loading, matching, bundled JSON
@@ -83,7 +83,7 @@ libfreemkv (lib.rs)
``` ```
Drive::open(Path::new("/dev/sg4")) Drive::open(Path::new("/dev/sg4"))
├─ scsi::open() Open /dev/sg4 via SG_IO ├─ scsi::open() Open /dev/sg4 (async write/poll/read)
├─ DriveId::from_drive() INQUIRY + GET_CONFIG 010C ├─ DriveId::from_drive() INQUIRY + GET_CONFIG 010C
├─ profile::find_by_drive_id() Match against bundled profiles ├─ profile::find_by_drive_id() Match against bundled profiles
├─ Platform::new() Instantiate chipset driver (Mt1959) ├─ Platform::new() Instantiate chipset driver (Mt1959)
@@ -175,7 +175,7 @@ is baked into the library.
| Platform | Transport | Status | | Platform | Transport | Status |
|----------|-----------|--------| |----------|-----------|--------|
| Linux | SG_IO ioctl on `/dev/sg*` | Supported | | Linux | async sg write/poll/read on `/dev/sg*` | Supported |
| macOS | IOKit SCSITask | Supported | | macOS | IOKit SCSITask | Supported |
| Windows | SPTI (`IOCTL_SCSI_PASS_THROUGH_DIRECT`) | Supported | | Windows | SPTI (`IOCTL_SCSI_PASS_THROUGH_DIRECT`) | Supported |
+21 -14
View File
@@ -46,14 +46,20 @@ platform driver. The drive is ready for `wait_ready()` and `init()`.
### read() with Recovery ### read() with Recovery
`Drive::read()` is the single read method. On error: `Drive::read(lba, count, buf, recovery)` is the single read method. The
`recovery` parameter controls whether to attempt multi-phase recovery on
failure or return immediately (used by DiscStream's binary search for
single-sector probes).
1. Set minimum speed immediately On error with `recovery = true`:
2. Reset device (close/reopen/TUR)
3. Wait 2s for drive to settle 1. **Phase 1 — gentle retry (5 attempts):** set min speed, sleep 30s, retry.
4. Retry at min speed, min batch (3 sectors) Each retry has a hard wall-clock timeout via async SG_IO.
5. If still failing: skip sectors, zero-fill, log 2. **Phase 2 — fresh start:** close transport, reset device, reopen, reinit.
6. Stay at min speed for 500 MB after error (recovery window) 3. **Phase 3 — gentle retry on fresh connection (5 attempts).**
4. If all fail: return `Err(DiscRead)`. DiscStream handles it (binary search,
skip, zero-fill).
5. Stay at min speed for 500 MB after any recovery (recovery window).
--- ---
@@ -70,8 +76,6 @@ pub trait ScsiTransport: Send {
data: &mut [u8], data: &mut [u8],
timeout_ms: u32, timeout_ms: u32,
) -> Result<ScsiResult>; ) -> Result<ScsiResult>;
fn reset(&mut self, device: &str) -> Result<()>;
} }
``` ```
@@ -82,15 +86,18 @@ descriptors or calls ioctls outside of a `ScsiTransport` implementation.
| Platform | Implementation | Device | | Platform | Implementation | Device |
|----------|---------------|--------| |----------|---------------|--------|
| Linux | `SgIoTransport``ioctl(fd, SG_IO, &hdr)` | `/dev/sg*` | | Linux | `SgIoTransport`async `write`/`poll`/`read` on `/dev/sg*` | `/dev/sg*` |
| macOS | `MacScsiTransport` — IOKit SCSITask | IOKit service | | macOS | `MacScsiTransport` — IOKit SCSITask | IOKit service |
| Windows | `WindowsScsiTransport` — SPTI | `\\.\CdRomN` | | Windows | `WindowsScsiTransport` — SPTI | `\\.\CdRomN` |
The Linux backend opens with `O_RDWR | O_NONBLOCK`, constructs `sg_io_hdr`, The Linux backend uses the sg driver's asynchronous interface: `write()` submits
and returns `ScsiResult` with status, bytes transferred, and sense data. the command, `poll()` waits with an enforceable wall-clock timeout, `read()`
retrieves the result. If `poll()` times out, the fd is abandoned (closed in a
background thread) and a fresh fd opened — the kernel's USB error recovery
cannot block us. Opens with `O_RDWR | O_NONBLOCK`.
On non-zero SCSI status, the transport parses sense key, ASC, and ASCQ from the On non-zero SCSI status, the transport parses sense key from the sense buffer
sense buffer and returns `Error::ScsiError`. and returns `Error::ScsiError`.
### CDB Builders ### CDB Builders