drive: run drive-prep unlock for EVERY disc, incl. DVD (fix riplock regression)

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.
This commit is contained in:
Matthew Jackson
2026-07-01 09:38:51 -07:00
parent 2dd98c3e32
commit 541ca2139f
2 changed files with 20 additions and 55 deletions
+20 -35
View File
@@ -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",