From 42c3fe6470b40445903f875fba8b8eeaeb3ac757 Mon Sep 17 00:00:00 2001 From: Matt Jackson <1085847+MattJackson@users.noreply.github.com> Date: Tue, 21 Apr 2026 18:01:48 +0000 Subject: [PATCH] Update docs: async sg transport, Drive::read recovery phases --- docs/architecture.md | 6 +++--- docs/drive-access.md | 35 +++++++++++++++++++++-------------- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/docs/architecture.md b/docs/architecture.md index 32226fa..dd33d94 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -42,7 +42,7 @@ libfreemkv (lib.rs) │ ├── Drive Access │ ├── 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 │ │ └── mt1959 MediaTek MT1959 driver (LG, ASUS, HP) │ ├── profile DriveProfile loading, matching, bundled JSON @@ -83,7 +83,7 @@ libfreemkv (lib.rs) ``` 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 ├─ profile::find_by_drive_id() Match against bundled profiles ├─ Platform::new() Instantiate chipset driver (Mt1959) @@ -175,7 +175,7 @@ is baked into the library. | 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 | | Windows | SPTI (`IOCTL_SCSI_PASS_THROUGH_DIRECT`) | Supported | diff --git a/docs/drive-access.md b/docs/drive-access.md index 1fe7719..5d7f585 100644 --- a/docs/drive-access.md +++ b/docs/drive-access.md @@ -46,14 +46,20 @@ platform driver. The drive is ready for `wait_ready()` and `init()`. ### 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 -2. Reset device (close/reopen/TUR) -3. Wait 2s for drive to settle -4. Retry at min speed, min batch (3 sectors) -5. If still failing: skip sectors, zero-fill, log -6. Stay at min speed for 500 MB after error (recovery window) +On error with `recovery = true`: + +1. **Phase 1 — gentle retry (5 attempts):** set min speed, sleep 30s, retry. + Each retry has a hard wall-clock timeout via async SG_IO. +2. **Phase 2 — fresh start:** close transport, reset device, reopen, reinit. +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], timeout_ms: u32, ) -> Result; - - fn reset(&mut self, device: &str) -> Result<()>; } ``` @@ -82,15 +86,18 @@ descriptors or calls ioctls outside of a `ScsiTransport` implementation. | 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 | | Windows | `WindowsScsiTransport` — SPTI | `\\.\CdRomN` | -The Linux backend opens with `O_RDWR | O_NONBLOCK`, constructs `sg_io_hdr`, -and returns `ScsiResult` with status, bytes transferred, and sense data. +The Linux backend uses the sg driver's asynchronous interface: `write()` submits +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 -sense buffer and returns `Error::ScsiError`. +On non-zero SCSI status, the transport parses sense key from the sense buffer +and returns `Error::ScsiError`. ### CDB Builders