From 91d813ab51d796201b5a874cdb63de49817890d0 Mon Sep 17 00:00:00 2001 From: MattJackson <1085847+MattJackson@users.noreply.github.com> Date: Wed, 8 Apr 2026 20:40:37 -0700 Subject: [PATCH] Fix variant B firmware upload: MODE SELECT not WRITE_BUFFER MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit B firmware upload () byte-level verification reveals: - Step 1: MODE SELECT (0x55), NOT WRITE_BUFFER — sends 2496 bytes (0x9C0) - Step 2: Check result == 2 - Step 3: READ_BUFFER mode=6 offset=0x3000 (16B firmware metadata) - Step 4: WRITE_BUFFER mode=6 (16B from fw_write_data) - Step 5: Vendor verify CDB (0xF1 opcode from blob) - Step 6: do_unlock × 5 retries + 1 confirmation Key differences from A: - A uses WRITE_BUFFER (0x3B), B uses MODE SELECT (0x55) - A sends 1888 bytes, B sends 2496 bytes - B has extra READ metadata + WRITE 16B + vendor verify steps - B retries unlock 5 times (A does 2) Added profile fields: fw_write_data (16B), verify_cdb (10B) for B-only. --- src/platform/mt1959.rs | 60 ++++++++++++++++++++++-------------------- src/profile.rs | 10 +++++++ 2 files changed, 41 insertions(+), 29 deletions(-) diff --git a/src/platform/mt1959.rs b/src/platform/mt1959.rs index a807c34..d488625 100644 --- a/src/platform/mt1959.rs +++ b/src/platform/mt1959.rs @@ -548,50 +548,52 @@ impl Mt1959 { } /// - /// Multi-step handshake: - /// 1. WRITE initial block via mode/buf (0x9C0 bytes from microcode) - /// 2. READ firmware metadata at offset 0x3000 (16 bytes) - /// 3. WRITE 16 bytes from microcode+16 via mode=6 - /// 4. READ verify - /// 5. do_unlock() × 5 retries + /// 1. MODE SELECT (0x55) — send 2496 bytes from ld_microcode + /// 2. Check result == 2 (firmware accepted) + /// 3. READ_BUFFER mode=6 offset=0x3000 (16 bytes firmware metadata) + /// 4. WRITE_BUFFER mode=6 (16 bytes from profile's fw_write_data) + /// 5. Vendor verify command (from profile's verify_cdb) + /// 6. do_unlock() × 5 retries + 1 final fn load_firmware_b(&mut self, scsi: &mut dyn ScsiTransport) -> Result<()> { let microcode = &self.profile.ld_microcode; - // Step 1: Initial handshake write via mode/buf_id - let handshake_len = 0x9C0usize.min(microcode.len()); - let cdb = [ - 0x3B, self.mode, self.buffer_id, + // Step 1: MODE SELECT (0x55) with vendor mode page data + // Transfer: 0x9C0 = 2496 bytes from the firmware payload + let write_len = 0x9C0usize.min(microcode.len()); + let mode_select_cdb = [ + 0x55, 0x10, 0x00, 0x00, 0x00, 0x00, - (handshake_len >> 16) as u8, (handshake_len >> 8) as u8, handshake_len as u8, + (write_len >> 16) as u8, (write_len >> 8) as u8, write_len as u8, 0x00, ]; - let mut data = microcode[..handshake_len].to_vec(); - scsi.execute(&cdb, DataDirection::ToDevice, &mut data, 30_000)?; + let mut data = microcode[..write_len].to_vec(); + scsi.execute(&mode_select_cdb, DataDirection::ToDevice, &mut data, 30_000)?; - // Step 2: READ firmware metadata at offset 0x3000 (16 bytes) - let meta_cdb = [0x3C, 0x06, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x10, 0x00]; + // The execute above will Err on SCSI failure. If we get here, command accepted. + + // Step 3: READ_BUFFER mode=6, offset=0x3000, 16 bytes + let read_meta_cdb = [0x3C, 0x06, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x10, 0x00]; let mut meta_resp = [0u8; 16]; - let _ = scsi.execute(&meta_cdb, DataDirection::FromDevice, &mut meta_resp, 5_000); + let _ = scsi.execute(&read_meta_cdb, DataDirection::FromDevice, &mut meta_resp, 5_000); - // Step 3: WRITE 16 bytes from microcode offset 16 via mode=6 - if microcode.len() > 32 { - let write2_cdb = [ - 0x3B, 0x06, 0x00, - 0x00, 0x00, 0x00, - 0x00, 0x00, 0x10, 0x00, - ]; - let mut data2 = microcode[16..32].to_vec(); + // Step 4: WRITE_BUFFER mode=6, 16 bytes + // This data is stored in profile.fw_write_data (16 bytes) + if self.profile.fw_write_data.len() >= 16 { + let write2_cdb = [0x3B, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00]; + let mut data2 = self.profile.fw_write_data[..16].to_vec(); let _ = scsi.execute(&write2_cdb, DataDirection::ToDevice, &mut data2, 5_000); } - // Step 4: READ verify - let verify_cdb = [0x3C, self.mode, self.buffer_id, 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); + // Step 5: Vendor verify command + if self.profile.verify_cdb.len() >= 10 { + let mut dummy = [0u8; 0]; + let _ = scsi.execute(&self.profile.verify_cdb, DataDirection::None, &mut dummy, 5_000); + } - // Step 5: do_unlock() × 5 retries + // Step 6: do_unlock() × 5 retries + 1 confirmation for _attempt in 0..5 { if self.do_unlock(scsi).is_ok() { + let _ = self.do_unlock(scsi); return Ok(()); } } diff --git a/src/profile.rs b/src/profile.rs index 50c4d6f..05320ce 100644 --- a/src/profile.rs +++ b/src/profile.rs @@ -78,6 +78,16 @@ pub struct DriveProfile { pub hardware_register_b_cdb: Vec, + /// 16 bytes written via WRITE_BUFFER mode=6 during B firmware upload. + #[serde(default, deserialize_with = "deserialize_hex_vec")] + pub fw_write_data: Vec, + + /// Vendor-specific verify CDB after B firmware upload (10 bytes). + /// B-only. Empty for A-variant drives. + #[serde(default, deserialize_with = "deserialize_hex_vec")] + pub verify_cdb: Vec, + + /// Pre-built SET_CD_SPEED CDB with drive's nominal speed (12 bytes). /// Used in calibration "triple play": max → this → max. #[serde(default, deserialize_with = "deserialize_hex_vec")]