Refactor: Chipset architecture, remove supported/status gatekeeping
- PlatformType → Chipset enum (MediaTek, Renesas) - unlock_mode + unlock_buf_id stored in profile, not derived from enum - Removed ReadinessStatus, supported field, needs_flash — library is agnostic - Removed DriveMatch/Flashable — if we have a profile, try unlock - profiles.json: chipset + unlock_mode + unlock_buf_id, no program/supported - mt1959.rs reads mode/buf_id from profile fields directly - Tests: find_known_drive, find_unknown_drive
This commit is contained in:
+824
-412
File diff suppressed because it is too large
Load Diff
@@ -100,7 +100,7 @@ fn print_human(
|
|||||||
println!();
|
println!();
|
||||||
match matched {
|
match matched {
|
||||||
Some(p) => {
|
Some(p) => {
|
||||||
println!("Profile: FOUND ({})", p.platform.name());
|
println!("Profile: FOUND ({})", p.chipset.name());
|
||||||
println!("Raw Read: Supported");
|
println!("Raw Read: Supported");
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ fn main() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
println!(" Drive ID: {}", session.profile.drive_id);
|
println!(" Drive ID: {}", session.profile.drive_id);
|
||||||
println!(" Platform: {}", session.profile.platform.name());
|
println!(" Chipset: {}", session.profile.chipset.name());
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
// Enable raw read mode
|
// Enable raw read mode
|
||||||
|
|||||||
+9
-17
@@ -8,7 +8,7 @@ use std::path::Path;
|
|||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::scsi::{SgIoTransport, ScsiTransport};
|
use crate::scsi::{SgIoTransport, ScsiTransport};
|
||||||
use crate::identity::DriveId;
|
use crate::identity::DriveId;
|
||||||
use crate::profile::{self, DriveProfile, PlatformType};
|
use crate::profile::{self, DriveProfile, Chipset};
|
||||||
use crate::platform::{Platform, DriveStatus};
|
use crate::platform::{Platform, DriveStatus};
|
||||||
use crate::platform::mt1959::Mt1959;
|
use crate::platform::mt1959::Mt1959;
|
||||||
|
|
||||||
@@ -42,23 +42,15 @@ impl DriveSession {
|
|||||||
product_revision: drive_id.product_revision.trim().to_string(),
|
product_revision: drive_id.product_revision.trim().to_string(),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
if !profile.supported {
|
let platform: Box<dyn Platform> = match profile.chipset {
|
||||||
return Err(Error::UnsupportedDrive {
|
Chipset::MediaTek => {
|
||||||
vendor_id: drive_id.vendor_id.trim().to_string(),
|
|
||||||
product_id: drive_id.product_id.trim().to_string(),
|
|
||||||
product_revision: drive_id.product_revision.trim().to_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let platform: Box<dyn Platform> = match profile.platform {
|
|
||||||
PlatformType::Mt1959A | PlatformType::Mt1959B => {
|
|
||||||
Box::new(Mt1959::new(profile.clone()))
|
Box::new(Mt1959::new(profile.clone()))
|
||||||
}
|
}
|
||||||
PlatformType::Pioneer => {
|
Chipset::Renesas => {
|
||||||
return Err(Error::UnsupportedDrive {
|
return Err(Error::UnsupportedDrive {
|
||||||
vendor_id: drive_id.vendor_id.trim().to_string(),
|
vendor_id: drive_id.vendor_id.trim().to_string(),
|
||||||
product_id: drive_id.product_id.trim().to_string(),
|
product_id: drive_id.product_id.trim().to_string(),
|
||||||
product_revision: "Pioneer not yet implemented".to_string(),
|
product_revision: "Renesas not yet implemented".to_string(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -76,15 +68,15 @@ impl DriveSession {
|
|||||||
let mut transport = SgIoTransport::open(device)?;
|
let mut transport = SgIoTransport::open(device)?;
|
||||||
let drive_id = DriveId::from_drive(&mut transport)?;
|
let drive_id = DriveId::from_drive(&mut transport)?;
|
||||||
|
|
||||||
let platform: Box<dyn Platform> = match profile.platform {
|
let platform: Box<dyn Platform> = match profile.chipset {
|
||||||
PlatformType::Mt1959A | PlatformType::Mt1959B => {
|
Chipset::MediaTek => {
|
||||||
Box::new(Mt1959::new(profile.clone()))
|
Box::new(Mt1959::new(profile.clone()))
|
||||||
}
|
}
|
||||||
PlatformType::Pioneer => {
|
Chipset::Renesas => {
|
||||||
return Err(Error::UnsupportedDrive {
|
return Err(Error::UnsupportedDrive {
|
||||||
vendor_id: drive_id.vendor_id.trim().to_string(),
|
vendor_id: drive_id.vendor_id.trim().to_string(),
|
||||||
product_id: drive_id.product_id.trim().to_string(),
|
product_id: drive_id.product_id.trim().to_string(),
|
||||||
product_revision: "Pioneer not yet implemented".to_string(),
|
product_revision: "Renesas not yet implemented".to_string(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+2
-3
@@ -25,10 +25,9 @@
|
|||||||
//!
|
//!
|
||||||
//! let mut session = DriveSession::open(
|
//! let mut session = DriveSession::open(
|
||||||
//! Path::new("/dev/sr0"),
|
//! Path::new("/dev/sr0"),
|
||||||
//! Path::new("profiles/"),
|
|
||||||
//! ).unwrap();
|
//! ).unwrap();
|
||||||
//!
|
//!
|
||||||
//! session.enable().unwrap();
|
//! session.unlock().unwrap();
|
||||||
//! session.calibrate().unwrap();
|
//! session.calibrate().unwrap();
|
||||||
//!
|
//!
|
||||||
//! let mut buf = vec![0u8; 2048];
|
//! let mut buf = vec![0u8; 2048];
|
||||||
@@ -46,7 +45,7 @@ pub mod speed;
|
|||||||
pub use error::{Error, Result};
|
pub use error::{Error, Result};
|
||||||
pub use drive::DriveSession;
|
pub use drive::DriveSession;
|
||||||
pub use identity::DriveId;
|
pub use identity::DriveId;
|
||||||
pub use profile::{DriveProfile, PlatformType};
|
pub use profile::{DriveProfile, Chipset};
|
||||||
pub use platform::{Platform, DriveStatus};
|
pub use platform::{Platform, DriveStatus};
|
||||||
pub use scsi::ScsiTransport;
|
pub use scsi::ScsiTransport;
|
||||||
pub use speed::DriveSpeed;
|
pub use speed::DriveSpeed;
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ pub struct Mt1959 {
|
|||||||
|
|
||||||
impl Mt1959 {
|
impl Mt1959 {
|
||||||
pub fn new(profile: DriveProfile) -> Self {
|
pub fn new(profile: DriveProfile) -> Self {
|
||||||
let mode = profile.platform.mode();
|
let mode = profile.unlock_mode;
|
||||||
let buffer_id = profile.platform.buffer_id();
|
let buffer_id = profile.unlock_buf_id;
|
||||||
Mt1959 {
|
Mt1959 {
|
||||||
profile,
|
profile,
|
||||||
mode,
|
mode,
|
||||||
|
|||||||
+75
-84
@@ -30,17 +30,18 @@ pub struct DriveProfile {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub firmware_date: String,
|
pub firmware_date: String,
|
||||||
|
|
||||||
/// Chipset platform type determining the READ BUFFER variant.
|
/// Chipset manufacturer determining unlock/read command structure.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub platform: PlatformType,
|
pub chipset: Chipset,
|
||||||
|
|
||||||
/// Whether this drive supports raw disc access mode.
|
/// READ BUFFER mode byte for unlock CDB (e.g. 0x01 for MT1959-A, 0x02 for MT1959-B).
|
||||||
#[serde(default)]
|
#[serde(default = "default_unlock_mode")]
|
||||||
pub supported: bool,
|
pub unlock_mode: u8,
|
||||||
|
|
||||||
|
/// READ BUFFER buffer ID for unlock CDB (e.g. 0x44 for MT1959-A, 0x77 for MT1959-B).
|
||||||
|
#[serde(default = "default_unlock_buf_id")]
|
||||||
|
pub unlock_buf_id: u8,
|
||||||
|
|
||||||
/// Current readiness status of this drive.
|
|
||||||
#[serde(default)]
|
|
||||||
pub status: ReadinessStatus,
|
|
||||||
|
|
||||||
/// Drive identifier string from the profile database.
|
/// Drive identifier string from the profile database.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
@@ -87,70 +88,39 @@ fn default_verify() -> [u8; 4] {
|
|||||||
*b"MMkv"
|
*b"MMkv"
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Chipset platform type. Determines the READ BUFFER mode and buffer ID.
|
fn default_unlock_mode() -> u8 {
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Deserialize)]
|
0x01
|
||||||
pub enum PlatformType {
|
|
||||||
/// MediaTek MT1959 variant A: mode=0x01, buffer_id=0x44.
|
|
||||||
#[serde(rename = "mt1959_a")]
|
|
||||||
Mt1959A,
|
|
||||||
/// MediaTek MT1959 variant B: mode=0x02, buffer_id=0x77.
|
|
||||||
#[serde(rename = "mt1959_b")]
|
|
||||||
Mt1959B,
|
|
||||||
/// Pioneer chipset (not yet implemented).
|
|
||||||
#[serde(rename = "pioneer")]
|
|
||||||
Pioneer,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PlatformType {
|
fn default_unlock_buf_id() -> u8 {
|
||||||
|
0x44
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Drive chipset — determines CDB structure for unlock and raw read commands.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Deserialize)]
|
||||||
|
pub enum Chipset {
|
||||||
|
/// MediaTek MT1959 — LG, ASUS, hp drives.
|
||||||
|
/// CDB: READ_BUFFER with mode and buf_id from profile.
|
||||||
|
#[serde(rename = "mediatek")]
|
||||||
|
MediaTek,
|
||||||
|
/// Renesas RS8xxx/RS9xxx — Pioneer, some HL-DT-ST drives.
|
||||||
|
/// Not yet implemented.
|
||||||
|
#[serde(rename = "renesas")]
|
||||||
|
Renesas,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Chipset {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
PlatformType::Mt1959A
|
Chipset::MediaTek
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Readiness status of a drive for raw disc access.
|
impl Chipset {
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Deserialize)]
|
/// Human-readable name for this chipset.
|
||||||
pub enum ReadinessStatus {
|
|
||||||
/// Drive is ready — raw disc access can be enabled.
|
|
||||||
Ready,
|
|
||||||
/// Drive firmware needs an update before raw access is possible.
|
|
||||||
NeedsFirmwareUpdate,
|
|
||||||
/// Drive uses encrypted commands (not yet supported).
|
|
||||||
Encrypted,
|
|
||||||
/// Status unknown.
|
|
||||||
Unknown,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for ReadinessStatus {
|
|
||||||
fn default() -> Self {
|
|
||||||
ReadinessStatus::Unknown
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PlatformType {
|
|
||||||
/// Human-readable name for this platform.
|
|
||||||
pub fn name(&self) -> &'static str {
|
pub fn name(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
PlatformType::Mt1959A => "MT1959-A",
|
Chipset::MediaTek => "MediaTek MT1959",
|
||||||
PlatformType::Mt1959B => "MT1959-B",
|
Chipset::Renesas => "Renesas",
|
||||||
PlatformType::Pioneer => "Pioneer",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// READ BUFFER mode byte for this chipset platform.
|
|
||||||
pub fn mode(&self) -> u8 {
|
|
||||||
match self {
|
|
||||||
PlatformType::Mt1959A => 0x01,
|
|
||||||
PlatformType::Mt1959B => 0x02,
|
|
||||||
PlatformType::Pioneer => 0x01, // TBD
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// READ BUFFER buffer ID for this chipset platform.
|
|
||||||
pub fn buffer_id(&self) -> u8 {
|
|
||||||
match self {
|
|
||||||
PlatformType::Mt1959A => 0x44,
|
|
||||||
PlatformType::Mt1959B => 0x77,
|
|
||||||
PlatformType::Pioneer => 0x44, // TBD
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -206,28 +176,19 @@ pub fn load_from_json(json: &serde_json::Value) -> Result<DriveProfile> {
|
|||||||
let revision = json["product_revision"].as_str().unwrap_or("").to_string();
|
let revision = json["product_revision"].as_str().unwrap_or("").to_string();
|
||||||
let firmware_type = json["vendor_specific"].as_str().unwrap_or("").to_string();
|
let firmware_type = json["vendor_specific"].as_str().unwrap_or("").to_string();
|
||||||
let firmware_date = json["firmware_date"].as_str().unwrap_or("").to_string();
|
let firmware_date = json["firmware_date"].as_str().unwrap_or("").to_string();
|
||||||
let program = json["program"].as_str().unwrap_or("unknown");
|
let chipset_str = json["chipset"].as_str().unwrap_or("unknown");
|
||||||
|
|
||||||
let platform = match program {
|
let chipset = match chipset_str {
|
||||||
"mt1959_a" => PlatformType::Mt1959A,
|
"mediatek" => Chipset::MediaTek,
|
||||||
"mt1959_b" => PlatformType::Mt1959B,
|
"renesas" => Chipset::Renesas,
|
||||||
_ => PlatformType::Mt1959A, // default
|
_ => Chipset::MediaTek,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let unlock_mode = json["unlock_mode"].as_u64().map(|v| v as u8).unwrap_or(0x01);
|
||||||
|
let unlock_buf_id = json["unlock_buf_id"].as_u64().map(|v| v as u8).unwrap_or(0x44);
|
||||||
|
|
||||||
let sig_str = json["signature"].as_str().unwrap_or("");
|
let sig_str = json["signature"].as_str().unwrap_or("");
|
||||||
|
|
||||||
// Drive is supported if it has a known program and valid signature
|
|
||||||
let has_program = matches!(program, "mt1959_a" | "mt1959_b");
|
|
||||||
let has_signature = sig_str.len() == 8;
|
|
||||||
let supported = has_program && has_signature;
|
|
||||||
|
|
||||||
let status = if supported {
|
|
||||||
ReadinessStatus::Ready
|
|
||||||
} else if json["status"].as_str() == Some("needs_flash") || program == "none" {
|
|
||||||
ReadinessStatus::NeedsFirmwareUpdate
|
|
||||||
} else {
|
|
||||||
ReadinessStatus::Unknown
|
|
||||||
};
|
|
||||||
let signature = if sig_str.len() == 8 {
|
let signature = if sig_str.len() == 8 {
|
||||||
parse_hex4(sig_str)?
|
parse_hex4(sig_str)?
|
||||||
} else {
|
} else {
|
||||||
@@ -260,9 +221,9 @@ pub fn load_from_json(json: &serde_json::Value) -> Result<DriveProfile> {
|
|||||||
product_revision: revision,
|
product_revision: revision,
|
||||||
vendor_specific: firmware_type,
|
vendor_specific: firmware_type,
|
||||||
firmware_date,
|
firmware_date,
|
||||||
platform,
|
chipset,
|
||||||
supported,
|
unlock_mode,
|
||||||
status,
|
unlock_buf_id,
|
||||||
drive_id: json["drive_id"].as_str().unwrap_or("").to_string(),
|
drive_id: json["drive_id"].as_str().unwrap_or("").to_string(),
|
||||||
drive_version: json["drive_version"].as_str().unwrap_or("").to_string(),
|
drive_version: json["drive_version"].as_str().unwrap_or("").to_string(),
|
||||||
signature,
|
signature,
|
||||||
@@ -335,3 +296,33 @@ pub fn find_by_drive_id<'a>(
|
|||||||
&& p.vendor_specific.trim() == vs
|
&& p.vendor_specific.trim() == vs
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::identity::DriveId;
|
||||||
|
|
||||||
|
fn make_drive_id(vendor: &str, rev: &str, vs: &str, date: &str) -> DriveId {
|
||||||
|
let mut inquiry = vec![0u8; 96];
|
||||||
|
inquiry[8..8+vendor.len().min(8)].copy_from_slice(&vendor.as_bytes()[..vendor.len().min(8)]);
|
||||||
|
inquiry[32..32+rev.len().min(4)].copy_from_slice(&rev.as_bytes()[..rev.len().min(4)]);
|
||||||
|
inquiry[36..36+vs.len().min(7)].copy_from_slice(&vs.as_bytes()[..vs.len().min(7)]);
|
||||||
|
DriveId::from_inquiry(&inquiry, date)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_find_known_drive() {
|
||||||
|
let profiles = load_bundled().unwrap();
|
||||||
|
let id = make_drive_id("HL-DT-ST", "1.03", "NM00000", "211810241934");
|
||||||
|
let p = find_by_drive_id(&profiles, &id).unwrap();
|
||||||
|
assert_eq!(p.vendor_id.trim(), "HL-DT-ST");
|
||||||
|
assert_eq!(p.vendor_specific.trim(), "NM00000");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_find_unknown_drive() {
|
||||||
|
let profiles = load_bundled().unwrap();
|
||||||
|
let id = make_drive_id("FAKE-VND", "9.99", "XX12345", "");
|
||||||
|
assert!(find_by_drive_id(&profiles, &id).is_none());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user