Lock down Platform trait: pub(crate), only init/set_read_speed/is_ready
Platform trait is no longer publicly exported. External code uses DriveSession only — cannot call unlock, load_firmware, calibrate directly. - Platform trait: pub(crate) with 3 methods only - All handlers are private methods on Mt1959 - DriveStatus moved to mt1959 internal struct - init() has guard: no re-init if already ready - set_read_speed() has guard: no-op if not calibrated - Removed open_unlocked() — open() is the only entry - Removed Platform and DriveStatus from public exports Prevents: out-of-sequence SCSI commands, double-init, wrong firmware writes.
This commit is contained in:
+4
-4
@@ -14,7 +14,7 @@ use crate::error::{Error, Result};
|
|||||||
use crate::scsi::ScsiTransport;
|
use crate::scsi::ScsiTransport;
|
||||||
use crate::identity::DriveId;
|
use crate::identity::DriveId;
|
||||||
use crate::profile::{self, DriveProfile, Chipset};
|
use crate::profile::{self, DriveProfile, Chipset};
|
||||||
use crate::platform::{Platform, DriveStatus};
|
use crate::platform::Platform;
|
||||||
use crate::platform::mt1959::Mt1959;
|
use crate::platform::mt1959::Mt1959;
|
||||||
|
|
||||||
/// A drive session with identification, platform, and SCSI transport.
|
/// A drive session with identification, platform, and SCSI transport.
|
||||||
@@ -116,9 +116,9 @@ impl DriveSession {
|
|||||||
self.platform.init(self.scsi.as_mut())
|
self.platform.init(self.scsi.as_mut())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if raw disc access mode is active.
|
/// Check if drive is initialized and ready for reads.
|
||||||
pub fn is_unlocked(&self) -> bool {
|
pub fn is_ready(&self) -> bool {
|
||||||
self.platform.is_unlocked()
|
self.platform.is_ready()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called per zone change during content reads.
|
/// Called per zone change during content reads.
|
||||||
|
|||||||
+1
-1
@@ -86,7 +86,7 @@ pub use error::{Error, Result};
|
|||||||
pub use drive::{DriveSession, find_drive, find_drives, resolve_device};
|
pub use drive::{DriveSession, find_drive, find_drives, resolve_device};
|
||||||
pub use identity::DriveId;
|
pub use identity::DriveId;
|
||||||
pub use profile::{DriveProfile, Chipset};
|
pub use profile::{DriveProfile, Chipset};
|
||||||
pub use platform::{Platform, DriveStatus};
|
// Platform trait is pub(crate) — callers use DriveSession, not Platform directly
|
||||||
pub use scsi::ScsiTransport;
|
pub use scsi::ScsiTransport;
|
||||||
pub use speed::DriveSpeed;
|
pub use speed::DriveSpeed;
|
||||||
pub use disc::{Disc, DiscFormat, Title, Clip, Stream, VideoStream, AudioStream, SubtitleStream,
|
pub use disc::{Disc, DiscFormat, Title, Clip, Stream, VideoStream, AudioStream, SubtitleStream,
|
||||||
|
|||||||
+23
-69
@@ -1,81 +1,35 @@
|
|||||||
//! Platform-specific implementations of raw disc access commands.
|
//! Platform-specific drive initialization and speed management.
|
||||||
//!
|
//!
|
||||||
//! Each chipset family (MT1959, Pioneer) implements the Platform trait.
|
//! The Platform trait is minimal by design. Callers cannot access
|
||||||
//! The trait methods correspond to the 10 command handlers in the per-drive
|
//! individual handlers (unlock, firmware upload, calibrate) directly.
|
||||||
|
//! This prevents out-of-sequence operations that could damage drives.
|
||||||
//!
|
//!
|
||||||
//! x86 dispatch order (proven from code + hardware traces):
|
//! Pipeline: DriveSession::open() calls init() once. After that,
|
||||||
//! 1. unlock() — try activate raw mode
|
//! only set_read_speed() is available during reads.
|
||||||
//! 2. load_firmware() — if unlock fails, write ld_microcode then retry
|
|
||||||
//! 3. calibrate() — probe disc zones, build speed table, triple SET_CD_SPEED
|
|
||||||
//! 5. read_register() — read hardware registers A and B (mid-rip, retried)
|
|
||||||
//! 6. status() — query feature flags
|
|
||||||
//! 7. probe() — parameterized register read
|
|
||||||
//! 8. set_read_speed()— per-zone SET_CD_SPEED during content reads
|
|
||||||
//!
|
|
||||||
//! The full init sequence is:
|
|
||||||
//! unlock → [load_firmware if fail] × 6 → calibrate × 6 →
|
|
||||||
//! drive_info → registers × 5 → status × 6 → probe
|
|
||||||
|
|
||||||
pub mod mt1959;
|
pub mod mt1959;
|
||||||
|
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use crate::scsi::ScsiTransport;
|
use crate::scsi::ScsiTransport;
|
||||||
|
|
||||||
pub trait Platform {
|
/// Platform trait — locked-down interface.
|
||||||
///
|
///
|
||||||
/// Sends READ_BUFFER with drive-specific mode/buf_id.
|
/// Only three operations exposed:
|
||||||
/// Checks response against drive_signature and mode_active_magic ("MMkv").
|
/// init() — called once by DriveSession::open()
|
||||||
/// Returns Ok if mode already active (warm), Err if firmware needed (cold).
|
/// set_read_speed() — called per zone during reads
|
||||||
fn unlock(&mut self, scsi: &mut dyn ScsiTransport) -> Result<()>;
|
/// is_ready() — state check
|
||||||
|
///
|
||||||
///
|
/// All internal handlers (unlock, firmware, calibrate, registers)
|
||||||
/// Sends WRITE_BUFFER mode=6 with ld_microcode (1888 bytes).
|
/// are private to the implementation. Cannot be called externally.
|
||||||
/// Verifies with READ_BUFFER buf=0x45 (expects response == 2).
|
pub(crate) trait Platform {
|
||||||
/// Then calls unlock() twice to activate mode.
|
/// One-time initialization. Called by DriveSession::open() only.
|
||||||
/// Only called when unlock() fails (cold boot, firmware not in drive RAM).
|
/// Internally: unlock → [firmware if needed] → calibrate → registers.
|
||||||
fn load_firmware(&mut self, scsi: &mut dyn ScsiTransport) -> Result<()>;
|
/// Safe to call on any drive state (warm, cold, OEM).
|
||||||
|
|
||||||
///
|
|
||||||
/// Sends pre-built hardware_register_a_cdb. Returns 16 bytes from
|
|
||||||
/// the 36-byte response at offset [4:20].
|
|
||||||
fn read_register_a(&mut self, scsi: &mut dyn ScsiTransport) -> Result<[u8; 16]>;
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Sends pre-built hardware_register_b_cdb. Returns 16 bytes from
|
|
||||||
/// the 36-byte response at offset [4:20].
|
|
||||||
fn read_register_b(&mut self, scsi: &mut dyn ScsiTransport) -> Result<[u8; 16]>;
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Probes disc zones via READ_BUFFER sub_cmd=0x14, builds speed table,
|
|
||||||
/// then commits with triple SET_CD_SPEED (max → nominal → max).
|
|
||||||
fn calibrate(&mut self, scsi: &mut dyn ScsiTransport) -> Result<()>;
|
|
||||||
|
|
||||||
fn keepalive(&mut self, scsi: &mut dyn ScsiTransport) -> Result<()>;
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Returns 16 bytes of feature data via READ_BUFFER sub_cmd=0x13.
|
|
||||||
fn status(&mut self, scsi: &mut dyn ScsiTransport) -> Result<DriveStatus>;
|
|
||||||
|
|
||||||
fn probe(&mut self, scsi: &mut dyn ScsiTransport, sub_cmd: u8, address: u32, length: u32) -> Result<Vec<u8>>;
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Looks up LBA in speed_zone_table, sends SET_CD_SPEED.
|
|
||||||
/// Called by x86 before each zone change during content reads.
|
|
||||||
fn set_read_speed(&mut self, scsi: &mut dyn ScsiTransport, lba: u32) -> Result<()>;
|
|
||||||
|
|
||||||
fn timing(&mut self, scsi: &mut dyn ScsiTransport) -> Result<()>;
|
|
||||||
|
|
||||||
/// Full init sequence — matches x86 dispatch order.
|
|
||||||
///
|
|
||||||
/// unlock → [load_firmware if fail] × 6 → calibrate × 6
|
|
||||||
fn init(&mut self, scsi: &mut dyn ScsiTransport) -> Result<()>;
|
fn init(&mut self, scsi: &mut dyn ScsiTransport) -> Result<()>;
|
||||||
|
|
||||||
/// Check if raw disc access mode is currently active.
|
/// Set read speed for a disc zone. Called during content reads.
|
||||||
fn is_unlocked(&self) -> bool;
|
fn set_read_speed(&mut self, scsi: &mut dyn ScsiTransport, lba: u32) -> Result<()>;
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
/// True after successful init().
|
||||||
pub struct DriveStatus {
|
fn is_ready(&self) -> bool;
|
||||||
pub unlocked: bool,
|
|
||||||
pub features: [u8; 16],
|
|
||||||
}
|
}
|
||||||
|
|||||||
+33
-13
@@ -10,7 +10,14 @@
|
|||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::profile::DriveProfile;
|
use crate::profile::DriveProfile;
|
||||||
use crate::scsi::{self, DataDirection, ScsiTransport};
|
use crate::scsi::{self, DataDirection, ScsiTransport};
|
||||||
use super::{Platform, DriveStatus};
|
use super::Platform;
|
||||||
|
|
||||||
|
/// Internal drive status — not exposed to callers.
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct DriveStatus {
|
||||||
|
unlocked: bool,
|
||||||
|
features: [u8; 16],
|
||||||
|
}
|
||||||
|
|
||||||
/// MT1959 driver state.
|
/// MT1959 driver state.
|
||||||
pub struct Mt1959 {
|
pub struct Mt1959 {
|
||||||
@@ -151,7 +158,29 @@ impl Mt1959 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Platform for Mt1959 {
|
impl Platform for Mt1959 {
|
||||||
/// Thin wrapper → do_unlock(). Returns Ok if mode active, Err if not.
|
fn init(&mut self, scsi: &mut dyn ScsiTransport) -> Result<()> {
|
||||||
|
// Guard: don't re-init an already-ready drive
|
||||||
|
if self.unlocked && self.calibrated {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
self.run_init(scsi)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_read_speed(&mut self, scsi: &mut dyn ScsiTransport, lba: u32) -> Result<()> {
|
||||||
|
if !self.calibrated {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
self.run_set_read_speed(scsi, lba)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_ready(&self) -> bool {
|
||||||
|
self.unlocked && self.calibrated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── All handlers are PRIVATE — only callable through init() ────────────
|
||||||
|
|
||||||
|
impl Mt1959 {
|
||||||
fn unlock(&mut self, scsi: &mut dyn ScsiTransport) -> Result<()> {
|
fn unlock(&mut self, scsi: &mut dyn ScsiTransport) -> Result<()> {
|
||||||
self.do_unlock(scsi)?;
|
self.do_unlock(scsi)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -399,7 +428,7 @@ impl Platform for Mt1959 {
|
|||||||
/// d. Build custom SET_CD_SPEED: BB 00 [r6>>8] [r6&FF] FF FF 00...
|
/// d. Build custom SET_CD_SPEED: BB 00 [r6>>8] [r6&FF] FF FF 00...
|
||||||
/// e. Send custom SET_CD_SPEED
|
/// e. Send custom SET_CD_SPEED
|
||||||
/// f. Return next speed_table entry to host
|
/// f. Return next speed_table entry to host
|
||||||
fn set_read_speed(&mut self, scsi: &mut dyn ScsiTransport, lba: u32) -> Result<()> {
|
fn run_set_read_speed(&mut self, scsi: &mut dyn ScsiTransport, lba: u32) -> Result<()> {
|
||||||
if !self.calibrated {
|
if !self.calibrated {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@@ -455,13 +484,7 @@ impl Platform for Mt1959 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Full init sequence — matches x86 dispatch exactly.
|
/// Full init sequence — matches x86 dispatch exactly.
|
||||||
///
|
fn run_init(&mut self, scsi: &mut dyn ScsiTransport) -> Result<()> {
|
||||||
/// Phase 1: cmd 0 → [cmd 1 if fail] × 6 retries (unlock + fw upload)
|
|
||||||
/// Phase 2: cmd 4 × 6 retries (calibrate)
|
|
||||||
/// Phase 3: cmd 7 → [cmd 5 fallback] (drive info)
|
|
||||||
/// Phase 4: cmd 2 + cmd 3 × 5 retries (registers)
|
|
||||||
/// Phase 5: cmd 9 × 6 retries (status)
|
|
||||||
fn init(&mut self, scsi: &mut dyn ScsiTransport) -> Result<()> {
|
|
||||||
// Phase 1: Unlock + firmware upload (6 retries)
|
// Phase 1: Unlock + firmware upload (6 retries)
|
||||||
//
|
//
|
||||||
// Three unlock outcomes:
|
// Three unlock outcomes:
|
||||||
@@ -515,9 +538,6 @@ impl Platform for Mt1959 {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_unlocked(&self) -> bool {
|
|
||||||
self.unlocked
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Private firmware upload variants ───────────────────────────────────
|
// ── Private firmware upload variants ───────────────────────────────────
|
||||||
|
|||||||
Reference in New Issue
Block a user