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
+27 -48
View File
@@ -774,9 +774,7 @@ pub struct ContentReader<'a> {
/// Consecutive errors at current position
error_streak: u32,
/// Current speed tier index (0 = max, higher = slower)
speed_tier: usize,
/// Last time maintain_speed was called
last_speed_maintain: std::time::Instant,
/// Total read errors encountered
pub errors: u32,
}
@@ -823,8 +821,6 @@ impl Disc {
max_batch_sectors: max_batch,
ok_streak: 0,
error_streak: 0,
speed_tier: 0,
last_speed_maintain: std::time::Instant::now(),
errors: 0,
})
}
@@ -879,15 +875,6 @@ const RAMP_BATCH_AFTER: u32 = 5; // successes before doubling batch size
const RAMP_SPEED_AFTER: u32 = 50; // successes at max batch before restoring speed
const SLOW_SPEED_AFTER: u32 = 3; // consecutive errors before reducing disc speed
/// Disc speed tiers (KB/s for SET CD SPEED).
/// Blu-ray: 1x=4500, 2x=9000, 4x=18000, 8x=36000, 12x=54000
const SPEED_TIERS: &[u16] = &[
0xFFFF, // tier 0: max (drive decides, typically 8-12x)
36000, // tier 1: 8x BD (~36 MB/s)
18000, // tier 2: 4x BD (~18 MB/s)
9000, // tier 3: 2x BD (~9 MB/s)
4500, // tier 4: 1x BD (~4.5 MB/s) — last resort
];
impl<'a> ContentReader<'a> {
/// Total bytes across all extents (for progress display).
@@ -973,29 +960,10 @@ impl<'a> ContentReader<'a> {
Ok(())
}
/// Set disc spin speed via SCSI SET CD SPEED.
fn set_speed(&mut self, tier: usize) {
let tier = tier.min(SPEED_TIERS.len() - 1);
if tier != self.speed_tier {
self.speed_tier = tier;
let speed_kbs = SPEED_TIERS[tier];
let cdb = crate::scsi::build_set_cd_speed(speed_kbs);
let mut dummy = [0u8; 0];
let _ = self.session.scsi_execute(
&cdb, crate::scsi::DataDirection::None, &mut dummy, 5_000,
);
}
}
/// Read a batch of sectors into the internal buffer.
///
/// Adaptive strategy:
/// 1. Read at current batch size
/// 2. On success: ramp batch up (double after 5 successes),
/// then restore disc speed (after 50 at max batch)
/// 3. On error: halve batch, pause. After 3 consecutive errors,
/// also reduce disc spin speed (scratched/damaged region).
/// 4. At min batch + still failing: retry once, then skip + zero-fill.
/// Speed management: speed table checked before each read.
/// On error: reduce speed, halve batch. On recovery: resume from table.
fn fill_buffer(&mut self) -> Result<bool> {
loop {
if self.current_extent >= self.extents.len() {
@@ -1019,6 +987,15 @@ impl<'a> ContentReader<'a> {
let byte_count = sectors_to_read as usize * 2048;
self.read_buf.resize(byte_count, 0);
// Check speed table — send SET_CD_SPEED if zone changed
if let Some(speed_kbs) = self.session.speed_table.speed_for(lba) {
let cdb = crate::scsi::build_set_cd_speed(speed_kbs);
let mut dummy = [0u8; 0];
let _ = self.session.scsi_execute(
&cdb, crate::scsi::DataDirection::None, &mut dummy, 5_000,
);
}
match self.read_sectors(lba, sectors_to_read) {
Ok(_) => {
self.buf_len = sectors_to_read as usize / 3;
@@ -1031,19 +1008,18 @@ impl<'a> ContentReader<'a> {
self.current_offset = 0;
}
// Ramp up: batch size first, then disc speed
// Ramp up batch size after consecutive successes
self.ok_streak += 1;
if self.batch_sectors < self.max_batch_sectors {
if self.ok_streak >= RAMP_BATCH_AFTER {
self.batch_sectors = (self.batch_sectors * 2).min(self.max_batch_sectors);
self.ok_streak = 0;
}
} else if self.speed_tier > 0 && self.ok_streak >= RAMP_SPEED_AFTER {
// At max batch for a while — try faster disc speed
self.set_speed(self.speed_tier - 1);
if self.batch_sectors < self.max_batch_sectors && self.ok_streak >= RAMP_BATCH_AFTER {
self.batch_sectors = (self.batch_sectors * 2).min(self.max_batch_sectors);
self.ok_streak = 0;
}
// Resume table-driven speed after sustained success
if self.error_streak == 0 && self.ok_streak >= RAMP_SPEED_AFTER {
self.session.speed_table.resume(lba);
}
return Ok(true);
}
Err(_) => {
@@ -1051,11 +1027,14 @@ impl<'a> ContentReader<'a> {
self.error_streak += 1;
self.ok_streak = 0;
// Reduce disc speed after repeated errors (physical problem)
if self.error_streak >= SLOW_SPEED_AFTER
&& self.speed_tier < SPEED_TIERS.len() - 1
{
self.set_speed(self.speed_tier + 1);
// Reduce speed after repeated errors
if self.error_streak >= SLOW_SPEED_AFTER {
let speed = self.session.speed_table.reduce();
let cdb = crate::scsi::build_set_cd_speed(speed);
let mut dummy = [0u8; 0];
let _ = self.session.scsi_execute(
&cdb, crate::scsi::DataDirection::None, &mut dummy, 5_000,
);
self.error_streak = 0; // reset — give new speed a chance
}