Remove SpeedTable, add probe_disc(), named constants, clean architecture

- Removed SpeedTable entirely — drive manages speeds after probe
- Renamed read_speed_table() → probe_disc()
- Named all SCSI constants: SUB_CMD_UNLOCK, SUB_CMD_INIT, SUB_CMD_PROBE,
  INIT_ADDR_BD, INIT_ADDR_UHD, PROBE_COARSE_END, PROBE_FINE_END, etc.
- Auto-detect BD vs UHD from disc capacity for correct probe init address
- Fixed NOMINAL_SPEED_B (was invalid CDB, removed — single max instead)
- Added session.set_speed() for simple speed control
- Error recovery: re-init on first error, BD2x on repeated errors
- Batch size uses full kernel limit (was 80%, now 100%)
- Clean variant_a/variant_b with named constants

API: open() → wait_ready() → init() → probe_disc() → scan() → read
This commit is contained in:
MattJackson
2026-04-09 15:17:25 -07:00
parent 5c73d9d5a0
commit 5949bcee89
8 changed files with 150 additions and 270 deletions
+2 -129
View File
@@ -1,133 +1,6 @@
//! Drive speed management — zone-based speed table.
//!
//! Every DriveSession has a SpeedTable. Default: max speed everywhere.
//! After init(): calibrated per-zone speeds from disc surface probes.
//! One u32 comparison per read on the hot path.
//! Drive speed constants.
/// Speed table — maps disc positions to optimal read speeds.
#[derive(Debug, Clone)]
pub struct SpeedTable {
zones: Vec<(u32, u16)>, // (start_lba, speed_kbs), sorted by lba
current_speed: u16,
next_boundary: u32,
}
impl SpeedTable {
/// Default: max speed, whole disc. Drive manages itself.
pub fn new() -> Self {
SpeedTable {
zones: vec![(0, 0xFFFF)],
current_speed: 0, // force first SET_CD_SPEED
next_boundary: 0, // force first lookup
}
}
/// Hot path: has the speed zone changed for this LBA?
/// Returns Some(speed_kbs) only when a SET_CD_SPEED is needed.
#[inline]
pub fn speed_for(&mut self, lba: u32) -> Option<u16> {
if lba < self.next_boundary {
return None;
}
self.transition(lba)
}
/// Zone transition — lookup + precompute next boundary.
fn transition(&mut self, lba: u32) -> Option<u16> {
let mut zone_idx = 0;
for (i, &(start, _)) in self.zones.iter().enumerate() {
if start <= lba {
zone_idx = i;
} else {
break;
}
}
let speed = self.zones[zone_idx].1;
self.next_boundary = if zone_idx + 1 < self.zones.len() {
self.zones[zone_idx + 1].0
} else {
u32::MAX
};
if speed == self.current_speed {
return None;
}
self.current_speed = speed;
Some(speed)
}
/// Load calibrated zones. Converts from platform probe data to generic (lba, kbs).
/// `disc_sectors`: total disc capacity from READ CAPACITY.
/// `probes`: (probe_address, speed_index) pairs from calibration scan.
/// `probe_range`: max probe address space (0x10000 for MT1959).
/// `speed_multiplier`: KB/s per speed unit (4500 for BD 1x).
pub fn load_calibration(
&mut self,
disc_sectors: u32,
probes: &[(u16, u8)],
probe_range: u32,
speed_multiplier: u16,
) {
if probes.is_empty() || disc_sectors == 0 {
return;
}
let mut zones: Vec<(u32, u16)> = Vec::new();
for &(probe_addr, speed_idx) in probes {
let lba = (probe_addr as u64 * disc_sectors as u64 / probe_range as u64) as u32;
let kbs = speed_idx as u16 * speed_multiplier;
zones.push((lba, kbs));
}
zones.sort_by_key(|&(lba, _)| lba);
// Deduplicate: keep only zone boundaries where speed changes
let mut deduped: Vec<(u32, u16)> = Vec::new();
for &(lba, kbs) in &zones {
if deduped.last().map_or(true, |&(_, prev_kbs)| prev_kbs != kbs) {
deduped.push((lba, kbs));
}
}
if deduped.is_empty() {
return;
}
self.zones = deduped;
self.current_speed = 0;
self.next_boundary = 0;
}
/// Temporarily reduce speed for error recovery.
pub fn reduce(&mut self) -> u16 {
let speed = (self.current_speed / 2).max(4500);
self.current_speed = speed;
speed
}
/// Resume table-driven speed at this LBA.
pub fn resume(&mut self, lba: u32) {
self.current_speed = 0;
self.next_boundary = 0;
self.transition(lba);
}
/// Current speed in KB/s.
pub fn current(&self) -> u16 {
self.current_speed
}
/// Number of zones.
pub fn zone_count(&self) -> usize {
self.zones.len()
}
}
// Keep DriveSpeed enum for CLI display
/// Common optical drive speeds with KB/s values for SET_CD_SPEED.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum DriveSpeed {
BD1x, BD2x, BD4x, BD6x, BD8x, BD10x, BD12x,