Speed table: generic zone-based speed management

- SpeedTable: maps disc positions to optimal speeds
- Default: max speed everywhere (drive manages itself)
- After read_speed_table(): calibrated per-zone speeds
- One u32 comparison per read on hot path
- Error recovery: reduce() / resume() override table temporarily
- Replaces old tier-based speed management in ContentReader
- MT1959 split into mod.rs + variant_a.rs + variant_b.rs
- PlatformDriver: init() + read_speed_table() + is_ready()
This commit is contained in:
MattJackson
2026-04-09 13:33:02 -07:00
parent d7d13d2849
commit 5c73d9d5a0
7 changed files with 327 additions and 339 deletions
+8 -12
View File
@@ -1,22 +1,18 @@
//! Platform-specific drive initialization and speed management.
//!
//! The Platform trait is minimal by design. Callers use init() once,
//! then set_read_speed() during reads. Internal operations cannot be
//! called directly — this prevents out-of-sequence operations.
//! Platform-specific drive initialization and calibration.
pub mod mt1959;
use crate::error::Result;
use crate::scsi::ScsiTransport;
use crate::speed::SpeedTable;
/// Platform trait — locked-down interface.
///
/// Only three operations exposed:
/// init() — one-time initialization
/// set_read_speed() — per-zone speed during reads
/// is_ready() — state check
pub(crate) trait PlatformDriver {
/// Unlock drive + upload firmware if needed.
fn init(&mut self, scsi: &mut dyn ScsiTransport) -> Result<()>;
fn set_read_speed(&mut self, scsi: &mut dyn ScsiTransport, lba: u32) -> Result<()>;
/// Read speed zones from disc surface, fill speed table.
fn read_speed_table(&mut self, scsi: &mut dyn ScsiTransport, speed_table: &mut SpeedTable) -> Result<()>;
/// True after successful init().
fn is_ready(&self) -> bool;
}
@@ -1,27 +1,27 @@
//! MT1959 platform — unlock, firmware upload, calibration, speed management.
//! MT1959 platform — shared logic for both variants.
mod variant_a;
mod variant_b;
use crate::error::{Error, Result};
use crate::profile::DriveProfile;
use crate::scsi::{self, DataDirection, ScsiTransport};
use crate::speed::SpeedTable;
use super::PlatformDriver;
const UNLOCK_RESPONSE_SIZE: u8 = 64;
// Variant constants
const MODE_A: u8 = 0x01;
const MODE_B: u8 = 0x02;
const BUFFER_ID_A: u8 = 0x44;
const BUFFER_ID_B: u8 = 0x77;
const NOMINAL_SPEED_A: [u8; 12] = [0xBB, 0x00, 0x23, 0x28, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
const NOMINAL_SPEED_B: [u8; 12] = [0x00, 0x00, 0xBB, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00];
const FIRMWARE_EXTRA_B: [u8; 16] = [0; 16];
const VERIFY_COMMAND_B: [u8; 10] = [0xF1, 0x01, 0x02, 0x00, 0x0D, 0x30, 0x01, 0xF3, 0xAD, 0x23];
pub struct Mt1959 {
profile: DriveProfile,
mode: u8,
buffer_id: u8,
unlocked: bool,
pub(crate) profile: DriveProfile,
pub(crate) mode: u8,
pub(crate) buffer_id: u8,
pub(crate) unlocked: bool,
speed_table: [u16; 64],
disc_sectors: u32,
calibrated: bool,
@@ -36,9 +36,7 @@ impl Mt1959 {
(MODE_A, BUFFER_ID_A)
};
Mt1959 {
profile,
mode,
buffer_id,
profile, mode, buffer_id,
unlocked: false,
speed_table: [0u16; 64],
disc_sectors: 0,
@@ -47,7 +45,9 @@ impl Mt1959 {
}
}
fn read_buffer_sub(&self, sub_cmd: u8, address: u16, length: u8) -> [u8; 10] {
// ── SCSI helpers (shared by both variants) ─────────────────────────
pub(crate) fn read_buffer_sub(&self, sub_cmd: u8, address: u16, length: u8) -> [u8; 10] {
[
0x3C, self.mode, self.buffer_id, sub_cmd,
(address >> 8) as u8, address as u8,
@@ -55,7 +55,7 @@ impl Mt1959 {
]
}
fn read_buffer_probe(
pub(crate) fn read_buffer_probe(
&self, scsi: &mut dyn ScsiTransport,
sub_cmd: u8, address: u16, buf: &mut [u8], expected: usize,
) -> Result<usize> {
@@ -67,21 +67,16 @@ impl Mt1959 {
Ok(result.bytes_transferred)
}
fn set_cd_speed_max(&self, scsi: &mut dyn ScsiTransport) -> Result<()> {
pub(crate) fn set_cd_speed_max(&self, scsi: &mut dyn ScsiTransport) -> Result<()> {
let cdb = scsi::build_set_cd_speed(0xFFFF);
let mut dummy = [0u8; 0];
scsi.execute(&cdb, DataDirection::None, &mut dummy, 5_000)?;
Ok(())
}
fn set_cd_speed(&self, scsi: &mut dyn ScsiTransport, speed: u16) -> Result<()> {
let cdb = scsi::build_set_cd_speed(speed);
let mut dummy = [0u8; 0];
scsi.execute(&cdb, DataDirection::None, &mut dummy, 5_000)?;
Ok(())
}
// ── Unlock (shared) ────────────────────────────────────────────────
fn do_unlock(&mut self, scsi: &mut dyn ScsiTransport) -> Result<Vec<u8>> {
pub(crate) fn do_unlock(&mut self, scsi: &mut dyn ScsiTransport) -> Result<Vec<u8>> {
let cdb = [
0x3C, self.mode, self.buffer_id,
0x00, 0x00, 0x00,
@@ -124,49 +119,38 @@ impl Mt1959 {
}
Err(Error::ScsiError { opcode: 0x3C, status: 0xFF, sense_key: 0 })
}
}
impl PlatformDriver for Mt1959 {
fn init(&mut self, scsi: &mut dyn ScsiTransport) -> Result<()> {
if self.unlocked && self.calibrated {
return Ok(());
// ── Init (unlock + firmware) ───────────────────────────────────────
fn run_init(&mut self, scsi: &mut dyn ScsiTransport) -> Result<()> {
let mut unlocked = false;
for _attempt in 0..6 {
match self.do_unlock(scsi) {
Ok(_) => { unlocked = true; break; }
Err(Error::SignatureMismatch { .. }) => {
return Err(Error::UnlockFailed {
detail: "signature mismatch — wrong profile for this drive".into(),
});
}
Err(_) => {
let ok = if self.mode == MODE_A {
variant_a::load_firmware(self, scsi).is_ok()
} else {
variant_b::load_firmware(self, scsi).is_ok()
};
if ok { unlocked = true; break; }
}
}
}
self.run_init(scsi)
}
fn set_read_speed(&mut self, scsi: &mut dyn ScsiTransport, lba: u32) -> Result<()> {
if !self.calibrated {
return Ok(());
if !unlocked {
return Err(Error::UnlockFailed { detail: "failed after 6 attempts".into() });
}
self.run_set_read_speed(scsi, lba)
}
fn is_ready(&self) -> bool {
self.unlocked && self.calibrated
}
}
impl Mt1959 {
fn unlock(&mut self, scsi: &mut dyn ScsiTransport) -> Result<()> {
self.do_unlock(scsi)?;
Ok(())
}
fn load_firmware(&mut self, scsi: &mut dyn ScsiTransport) -> Result<()> {
if self.profile.firmware.is_empty() {
return Err(Error::UnlockFailed {
detail: "no firmware in profile".into(),
});
}
// ── Calibrate (disc surface probes) ────────────────────────────────
if self.mode == MODE_A {
self.load_firmware_a(scsi)
} else {
self.load_firmware_b(scsi)
}
}
fn calibrate(&mut self, scsi: &mut dyn ScsiTransport) -> Result<()> {
fn run_calibrate(&mut self, scsi: &mut dyn ScsiTransport) -> Result<()> {
if !self.unlocked { self.do_unlock(scsi)?; }
let cap_cdb = [0x25u8, 0, 0, 0, 0, 0, 0, 0, 0, 0];
@@ -175,7 +159,7 @@ impl Mt1959 {
self.disc_sectors = u32::from_be_bytes([cap_buf[0], cap_buf[1], cap_buf[2], cap_buf[3]]) + 1;
}
let init_addr: u16 = 0x0100; // TODO: detect disc type (0x0200 for UHD)
let init_addr: u16 = 0x0100;
let mut init_resp = [0u8; 4];
let _ = self.read_buffer_probe(scsi, 0x12, init_addr, &mut init_resp, 4);
@@ -198,9 +182,7 @@ impl Mt1959 {
self.calibration_config = [0u8; 4];
return Err(Error::ScsiError { opcode: 0x3C, status: 0xFF, sense_key: 0 });
}
if resp[0] != prev_speed {
prev_speed = resp[0];
}
if resp[0] != prev_speed { prev_speed = resp[0]; }
addr = addr.wrapping_add(0x100);
}
@@ -232,135 +214,36 @@ impl Mt1959 {
self.calibrated = true;
Ok(())
}
}
fn run_set_read_speed(&mut self, scsi: &mut dyn ScsiTransport, lba: u32) -> Result<()> {
if !self.calibrated {
return Ok(());
}
// ── PlatformDriver trait ───────────────────────────────────────────────
let mut best_idx: usize = 0;
let mut best_diff: u32 = 0x10000000;
let mut found = false;
impl PlatformDriver for Mt1959 {
fn init(&mut self, scsi: &mut dyn ScsiTransport) -> Result<()> {
if self.unlocked { return Ok(()); }
self.run_init(scsi)
}
fn read_speed_table(&mut self, scsi: &mut dyn ScsiTransport, speed_table: &mut SpeedTable) -> Result<()> {
if !self.unlocked { self.run_init(scsi)?; }
if self.calibrated { return Ok(()); }
self.run_calibrate(scsi)?;
let mut probes: Vec<(u16, u8)> = Vec::new();
for i in 0..64 {
let entry = self.speed_table[i] as u32;
if entry == 0 { continue; }
let diff = if lba > entry { lba - entry } else { entry - lba };
if diff < best_diff {
best_diff = diff;
best_idx = i;
found = true;
}
let addr = self.speed_table[i];
if addr == 0 { continue; }
let speed_idx = ((i + 1) << 1) as u8;
probes.push((addr, speed_idx));
}
if !found {
return Ok(());
}
let speed_val = self.speed_table[best_idx];
let probe_addr = 0x0100 | (speed_val.swap_bytes() as u16);
let mut probe_resp = [0u8; 4];
let _ = self.read_buffer_probe(scsi, 0x14, probe_addr, &mut probe_resp, 4);
let _ = self.set_cd_speed_max(scsi);
let _ = self.set_cd_speed(scsi, speed_val);
const PROBE_RANGE: u32 = 0x10000;
const BD_1X_KBS: u16 = 4500;
speed_table.load_calibration(self.disc_sectors, &probes, PROBE_RANGE, BD_1X_KBS);
Ok(())
}
fn run_init(&mut self, scsi: &mut dyn ScsiTransport) -> Result<()> {
let mut unlocked = false;
for _attempt in 0..6 {
match self.unlock(scsi) {
Ok(_) => { unlocked = true; break; }
Err(Error::SignatureMismatch { .. }) => {
return Err(Error::UnlockFailed {
detail: "signature mismatch — wrong profile for this drive".into(),
});
}
Err(_) => {
if self.load_firmware(scsi).is_ok() {
unlocked = true;
break;
}
}
}
}
if !unlocked {
return Err(Error::UnlockFailed {
detail: "failed after 6 attempts".into(),
});
}
let mut calibrated = false;
for _attempt in 0..6 {
if self.calibrate(scsi).is_ok() {
calibrated = true;
break;
}
}
if !calibrated {
return Err(Error::ScsiError { opcode: 0x3C, status: 0xFF, sense_key: 0 });
}
Ok(())
}
}
impl Mt1959 {
fn load_firmware_a(&mut self, scsi: &mut dyn ScsiTransport) -> Result<()> {
let firmware = &self.profile.firmware;
let len = firmware.len();
let cdb = [
0x3B, 0x06, 0x00,
0x00, 0x00, 0x00,
(len >> 16) as u8, (len >> 8) as u8, len as u8,
0x00,
];
let mut data = firmware.clone();
scsi.execute(&cdb, DataDirection::ToDevice, &mut data, 30_000)?;
let verify_cdb = [0x3C, 0x01, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00];
let mut verify_resp = [0u8; 4];
let _ = scsi.execute(&verify_cdb, DataDirection::FromDevice, &mut verify_resp, 5_000);
self.do_unlock(scsi)?;
self.do_unlock(scsi)?;
Ok(())
}
fn load_firmware_b(&mut self, scsi: &mut dyn ScsiTransport) -> Result<()> {
let firmware = &self.profile.firmware;
let write_len = 0x9C0usize.min(firmware.len());
let mode_select_cdb = [
0x55, 0x10, 0x00,
0x00, 0x00, 0x00,
(write_len >> 16) as u8, (write_len >> 8) as u8, write_len as u8,
0x00,
];
let mut data = firmware[..write_len].to_vec();
scsi.execute(&mode_select_cdb, DataDirection::ToDevice, &mut data, 30_000)?;
let read_meta_cdb = [0x3C, 0x06, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x10, 0x00];
let mut meta_resp = [0u8; 16];
let _ = scsi.execute(&read_meta_cdb, DataDirection::FromDevice, &mut meta_resp, 5_000);
let write2_cdb = [0x3B, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00];
let mut data2 = FIRMWARE_EXTRA_B.to_vec();
let _ = scsi.execute(&write2_cdb, DataDirection::ToDevice, &mut data2, 5_000);
let mut dummy = [0u8; 0];
let _ = scsi.execute(&VERIFY_COMMAND_B, DataDirection::None, &mut dummy, 5_000);
for _attempt in 0..5 {
if self.do_unlock(scsi).is_ok() {
let _ = self.do_unlock(scsi);
return Ok(());
}
}
self.do_unlock(scsi)?;
Ok(())
fn is_ready(&self) -> bool {
self.unlocked
}
}
+33
View File
@@ -0,0 +1,33 @@
//! MT1959 variant A firmware upload.
use crate::error::Result;
use crate::scsi::{DataDirection, ScsiTransport};
use super::Mt1959;
pub(super) fn load_firmware(mt: &mut Mt1959, scsi: &mut dyn ScsiTransport) -> Result<()> {
let firmware = &mt.profile.firmware;
if firmware.is_empty() {
return Err(crate::error::Error::UnlockFailed {
detail: "no firmware in profile".into(),
});
}
let len = firmware.len();
let cdb = [
0x3B, 0x06, 0x00,
0x00, 0x00, 0x00,
(len >> 16) as u8, (len >> 8) as u8, len as u8,
0x00,
];
let mut data = firmware.clone();
scsi.execute(&cdb, DataDirection::ToDevice, &mut data, 30_000)?;
// Verify (may fail, non-fatal)
let verify_cdb = [0x3C, 0x01, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00];
let mut verify_resp = [0u8; 4];
let _ = scsi.execute(&verify_cdb, DataDirection::FromDevice, &mut verify_resp, 5_000);
mt.do_unlock(scsi)?;
mt.do_unlock(scsi)?;
Ok(())
}
+52
View File
@@ -0,0 +1,52 @@
//! MT1959 variant B firmware upload.
use crate::error::Result;
use crate::scsi::{DataDirection, ScsiTransport};
use super::Mt1959;
const FIRMWARE_EXTRA: [u8; 16] = [0; 16];
const VERIFY_COMMAND: [u8; 10] = [0xF1, 0x01, 0x02, 0x00, 0x0D, 0x30, 0x01, 0xF3, 0xAD, 0x23];
pub(super) fn load_firmware(mt: &mut Mt1959, scsi: &mut dyn ScsiTransport) -> Result<()> {
let firmware = &mt.profile.firmware;
if firmware.is_empty() {
return Err(crate::error::Error::UnlockFailed {
detail: "no firmware in profile".into(),
});
}
// Step 1: MODE SELECT with firmware payload
let write_len = 0x9C0usize.min(firmware.len());
let mode_select_cdb = [
0x55, 0x10, 0x00,
0x00, 0x00, 0x00,
(write_len >> 16) as u8, (write_len >> 8) as u8, write_len as u8,
0x00,
];
let mut data = firmware[..write_len].to_vec();
scsi.execute(&mode_select_cdb, DataDirection::ToDevice, &mut data, 30_000)?;
// Step 2: Read firmware metadata
let read_meta_cdb = [0x3C, 0x06, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x10, 0x00];
let mut meta_resp = [0u8; 16];
let _ = scsi.execute(&read_meta_cdb, DataDirection::FromDevice, &mut meta_resp, 5_000);
// Step 3: Write extra firmware data
let write2_cdb = [0x3B, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00];
let mut data2 = FIRMWARE_EXTRA.to_vec();
let _ = scsi.execute(&write2_cdb, DataDirection::ToDevice, &mut data2, 5_000);
// Step 4: Vendor verify
let mut dummy = [0u8; 0];
let _ = scsi.execute(&VERIFY_COMMAND, DataDirection::None, &mut dummy, 5_000);
// Step 5: Unlock retries
for _attempt in 0..5 {
if mt.do_unlock(scsi).is_ok() {
let _ = mt.do_unlock(scsi);
return Ok(());
}
}
mt.do_unlock(scsi)?;
Ok(())
}