libfreemkv 0.5.0

Open source raw disc access library for optical drives
Documentation
//! Platform-specific implementations of raw disc access commands.
//!
//! Each chipset family (MT1959, Pioneer) implements the Platform trait.
//! The trait methods correspond to the 10 command handlers in the per-drive
//! ARM blob, dispatched by the x86 host in a specific sequence.
//!
//! x86 dispatch order (proven from code + hardware traces):
//!   1. unlock()        — try activate raw mode
//!   2. load_firmware() — if unlock fails, write ld_microcode then retry
//!   3. calibrate()     — probe disc zones, build speed table, triple SET_CD_SPEED
//!   4. drive_info()    — get drive info string (handler 7)
//!   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;

use crate::error::Result;
use crate::scsi::ScsiTransport;

/// Platform trait — all 10 command handlers from the per-drive ARM blob.
pub trait Platform {
    /// Handler 0: Try to activate raw disc access mode.
    ///
    /// Sends READ_BUFFER with drive-specific mode/buf_id.
    /// Checks response against drive_signature and mode_active_magic ("MMkv").
    /// Returns Ok if mode already active (warm), Err if firmware needed (cold).
    fn unlock(&mut self, scsi: &mut dyn ScsiTransport) -> Result<()>;

    /// Handler 1: Upload LibreDrive microcode to drive.
    ///
    /// Sends WRITE_BUFFER mode=6 with ld_microcode (1888 bytes).
    /// Verifies with READ_BUFFER buf=0x45 (expects response == 2).
    /// Then calls unlock() twice to activate mode.
    /// Only called when unlock() fails (cold boot, firmware not in drive RAM).
    fn load_firmware(&mut self, scsi: &mut dyn ScsiTransport) -> Result<()>;

    /// Handler 2: Read hardware register A.
    ///
    /// 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]>;

    /// Handler 3: Read hardware register B.
    ///
    /// 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]>;

    /// Handler 4: Speed calibration.
    ///
    /// 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<()>;

    /// Handler 5: Keepalive — read 16 bytes from host.
    fn keepalive(&mut self, scsi: &mut dyn ScsiTransport) -> Result<()>;

    /// Handler 6: Query LibreDrive status and feature flags.
    ///
    /// Returns 16 bytes of feature data via READ_BUFFER sub_cmd=0x13.
    fn status(&mut self, scsi: &mut dyn ScsiTransport) -> Result<DriveStatus>;

    /// Handler 7: Generic probe with caller-provided parameters.
    fn probe(&mut self, scsi: &mut dyn ScsiTransport, sub_cmd: u8, address: u32, length: u32) -> Result<Vec<u8>>;

    /// Handler 8: Set read speed for a disc zone.
    ///
    /// 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<()>;

    /// Handler 9: Timing ping — read 8 bytes.
    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<()>;

    /// Check if raw disc access mode is currently active.
    fn is_unlocked(&self) -> bool;
}

/// Drive status from handler 6.
#[derive(Debug, Clone)]
pub struct DriveStatus {
    pub unlocked: bool,
    pub features: [u8; 16],
}