From 541ca2139f602892a77cf6e2b1f07de6cf768b85 Mon Sep 17 00:00:00 2001 From: Matthew Jackson <1085847+MattJackson@users.noreply.github.com> Date: Wed, 1 Jul 2026 09:38:51 -0700 Subject: [PATCH] drive: run drive-prep unlock for EVERY disc, incl. DVD (fix riplock regression) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The v1.0.0-rc.1 unlocker refactor added an `if disc_is_dvd() { return }` early-return in Drive::init() that skipped the whole drive-prep unlock for DVDs. That firmware unlock is what lifts riplock and readies max read speed — a DRIVE-level, disc-independent feature — so skipping it left every DVD stuck at stock/riplock speed (~0.4x, 3h ETA). UHD was unaffected because it flows through the unlock. Remove the skip: init() now runs the identity-keyed drive unlocker for all discs (disc kind is Unknown at init, so only the drive unlocker matches; the AACS host-cert handshake and CSS bus-auth still run later, gated on the real disc kind, on top of the unlocked drive). Speed stays where it belongs — SET CD SPEED(0xFFFF) at pass-1 start (disc/mod.rs) — not in the unlocker. Also drop the matching probe_disc DVD skip. Reverts the SET STREAMING stopgap (separate freemkv-unlock revert): that treated the symptom; the real bug was the skipped unlock. Drive features come from the firmware unlock, not a stock speed CDB. --- src/drive/mod.rs | 55 ++++++++++++++++---------------------------- src/unlock_bridge.rs | 20 ---------------- 2 files changed, 20 insertions(+), 55 deletions(-) diff --git a/src/drive/mod.rs b/src/drive/mod.rs index 576b34f..f9342c6 100644 --- a/src/drive/mod.rs +++ b/src/drive/mod.rs @@ -411,27 +411,18 @@ impl Drive { pub fn init(&mut self) -> Result<()> { let t0 = std::time::Instant::now(); tracing::info!(target: "freemkv::drive", phase = "init", "begin"); - // Drive-level features (max read speed / riplock lift) — matched on the - // DRIVE, applied for ANY disc. STOCK MMC commands only (no firmware - // unlock), so this is safe for a CSS DVD that must stay in stock mode. - // Runs BEFORE the DVD early-return below so a DVD is no longer riplocked: - // previously only the BD/UHD path (which returns clear content via the - // firmware unlock + its speed calibration) got up to speed, and DVD fell - // through with just the legacy SET CD SPEED the drive ignores. - crate::unlock_bridge::apply_drive_features(self.scsi.as_mut(), &self.drive_id); - if self.disc_is_dvd() { - tracing::info!(target: "freemkv::drive", phase = "init", dvd = true, elapsed_ms = t0.elapsed().as_millis() as u64, "end (stock-mode DVD, drive features applied, no bus unlock)"); - self.init_ran = true; - return Ok(()); - } - // Drive-prep dispatch: the disc structure has not been probed yet, so - // the kind is Unknown — only an identity-keyed unlocker can match here. - // The first matching unlocker runs; none matching leaves the drive in - // stock mode so the host-cert AACS handshake (the OEM route) carries the - // disc. A genuine transport fault means the bus is dead — abort init - // (the v1.1.0 invariant; `if let Ok` was silently swallowing it). Every - // other error (NotApplicable / no match) is "nothing applied" — fall - // through to stock mode. + // Drive-prep runs for EVERY disc, DVD INCLUDED. The drive-level firmware + // unlock lifts riplock and readies max read speed regardless of disc type + // — drive features are disc-independent. At init the disc kind is not yet + // probed (Unknown), so only the identity-keyed DRIVE unlocker matches + // here; the AACS host-cert handshake and the CSS bus-auth handshake run + // LATER, each gated on the actual disc kind, on TOP of the already- + // unlocked drive. A genuine transport fault means the bus is dead — abort + // init (the v1.1.0 invariant; `if let Ok` was silently swallowing it). + // Every other error (NotApplicable / no match) is "nothing applied" — + // fall through to stock mode. NOTE: the `if disc_is_dvd() { return }` skip + // that used to sit here was the v1.0.0-rc.1 regression — it skipped the + // drive-prep for DVD, leaving DVDs riplocked at stock speed. self.init_ran = true; let r: Result<()> = match crate::unlock_bridge::run_unlockers( self.scsi.as_mut(), @@ -457,13 +448,12 @@ impl Drive { }), Err(_) => Ok(()), }; - // Raise the drive to its maximum read speed with a generic SET CD SPEED — - // UNCONDITIONALLY whenever the bus is alive (init didn't transport-fault), - // whether or not an unlocker matched. A stock-mode BD/UHD drive (no - // firmware unlocker) still wants max speed; gating this on an unlocker - // match left such drives riplocked. (DVD returns earlier in stock mode; - // its sweep sets DVD speed separately.) Best-effort: a failure here must - // NOT fail the rip — a slow drive still rips. + // Raise the drive to its maximum read speed — UNCONDITIONALLY whenever + // the bus is alive (init didn't transport-fault), whether or not an + // unlocker matched, and for ANY disc type (DVD now flows through here too, + // so it gets max speed on the freshly firmware-unlocked drive instead of + // the stock riplock). A stock-mode drive with no firmware unlocker still + // wants max speed. Best-effort: a failure here must NOT fail the rip. if r.is_ok() { self.set_speed(crate::speed::DriveSpeed::Max.to_kbps()); } @@ -484,14 +474,9 @@ impl Drive { pub fn probe_disc(&mut self) -> Result<()> { let t0 = std::time::Instant::now(); tracing::info!(target: "freemkv::drive", phase = "probe_disc", "begin"); - // A DVD runs in stock mode (see `init`); skip the OEM/drive-prep - // disc calibration, which only applies to the unlocked BD/UHD drive. - if self.disc_is_dvd() { - tracing::info!(target: "freemkv::drive", phase = "probe_disc", dvd = true, elapsed_ms = t0.elapsed().as_millis() as u64, "end (stock-mode DVD, no calibration)"); - return Ok(()); - } // Disc-speed calibration is unlocker-specific and now lives inside - // the unlocker's `unlock()` (run at `init()`). Nothing to do here. + // the unlocker's `unlock()` (run at `init()`, for every disc including + // DVD). Nothing to do here — no disc-type branch. tracing::info!( target: "freemkv::drive", phase = "probe_disc", diff --git a/src/unlock_bridge.rs b/src/unlock_bridge.rs index a6ef726..87fa4d6 100644 --- a/src/unlock_bridge.rs +++ b/src/unlock_bridge.rs @@ -124,26 +124,6 @@ pub(crate) fn run_unlockers( Err(fu::UnlockError::NotApplicable) } -/// Apply drive-level feature tuning (max read speed / riplock lift) for the -/// installed drive, for ANY disc. This is the drive-features capability, kept -/// separate from bus removal: it issues only STOCK MMC commands (no firmware -/// unlock), so it is safe on a CSS DVD that must stay in stock mode. Each -/// unlocker's `apply_drive_features` is self-gating (a no-op unless it recognises -/// the drive), so we call every one; disc kind is irrelevant here, so we pass -/// `Unknown`. Best-effort — never returns an error to the caller (a slow drive -/// still rips). -pub(crate) fn apply_drive_features( - scsi: &mut dyn crate::scsi::ScsiTransport, - drive_id: &crate::identity::DriveId, -) { - let id = to_fu_drive_id(drive_id); - let ctx = fu::UnlockCtx::new(&id, fu::DiscKind::Unknown, &[]); - let mut adapter = ScsiAdapter(scsi); - for u in fu::all_unlockers() { - let _ = u.apply_drive_features(&mut adapter, &ctx); - } -} - #[cfg(test)] mod tests { use super::*;