Skip to main content

mediatek_optical/
cdb.rs

1//! Command descriptor blocks, as byte constructors.
2//!
3//! The flash sequence uses 12-byte CDBs byte-for-byte as the OEM update tool
4//! issues them: [`probe`] → [`test_unit_ready`] → [`enter_update`] →
5//! [`transfer`] per chunk → [`finish`] → [`test_unit_ready`] →
6//! [`request_sense`]. The drive erases and programs flash when the last
7//! [`transfer`] of a complete image lands; [`finish`] is a trailing handshake.
8
9/// READ BUFFER / WRITE BUFFER mode addressing flash at file offsets.
10pub const MODE_DATA: u8 = 0x06;
11/// Buffer id of the flash.
12pub const BUFFER_ID: u8 = 0x00;
13/// Bytes per [`transfer`] in the OEM sequence (16 KiB).
14pub const CHUNK: usize = 0x4000;
15/// Bytes returned by [`probe`].
16pub const PROBE_LEN: usize = 0x100;
17/// Bytes requested by [`request_sense`].
18pub const REQUEST_SENSE_LEN: usize = 16;
19/// INQUIRY allocation used by `crate::drive::identify`.
20pub const INQUIRY_ALLOC: u16 = 96;
21/// GET CONFIGURATION feature carrying the ASCII serial number.
22pub const FEATURE_SERIAL: u16 = 0x0108;
23/// GET CONFIGURATION feature carrying the ASCII firmware date. Its presence is
24/// one half of the MT19xx protocol probe.
25pub const FEATURE_FIRMWARE_DATE: u16 = 0x010C;
26
27/// READ BUFFER (`3C`) of `len` bytes at `off`. Both fields are 24 bits.
28pub const fn read_buffer(mode: u8, buffer_id: u8, off: u32, len: u32) -> [u8; 10] {
29    [
30        0x3C,
31        mode & 0x1f,
32        buffer_id,
33        (off >> 16) as u8,
34        (off >> 8) as u8,
35        off as u8,
36        (len >> 16) as u8,
37        (len >> 8) as u8,
38        len as u8,
39        0x00,
40    ]
41}
42
43/// WRITE BUFFER (`3B`) of `len` bytes at `off`. Both fields are 24 bits.
44pub const fn write_buffer(mode: u8, buffer_id: u8, off: u32, len: u32) -> [u8; 10] {
45    [
46        0x3B,
47        mode & 0x1f,
48        buffer_id,
49        (off >> 16) as u8,
50        (off >> 8) as u8,
51        off as u8,
52        (len >> 16) as u8,
53        (len >> 8) as u8,
54        len as u8,
55        0x00,
56    ]
57}
58
59/// READ BUFFER of flash memory at a file offset.
60pub const fn read_memory(off: u32, len: u32) -> [u8; 10] {
61    read_buffer(MODE_DATA, BUFFER_ID, off, len)
62}
63
64/// Standard INQUIRY (`12`).
65pub const fn inquiry(alloc: u16) -> [u8; 6] {
66    [0x12, 0x00, 0x00, (alloc >> 8) as u8, alloc as u8, 0x00]
67}
68
69/// GET CONFIGURATION (`46`) of the single feature `code` (RT = `02`).
70pub const fn get_configuration(code: u16, alloc: u16) -> [u8; 10] {
71    [
72        0x46,
73        0x02,
74        (code >> 8) as u8,
75        code as u8,
76        0x00,
77        0x00,
78        0x00,
79        (alloc >> 8) as u8,
80        alloc as u8,
81        0x00,
82    ]
83}
84
85/// PROBE: READ BUFFER of the identity descriptor, [`PROBE_LEN`] bytes. Opens
86/// the OEM flash sequence.
87pub const fn probe() -> [u8; 12] {
88    [
89        0x3C, 0x06, 0x00, 0x1E, 0xC0, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
90    ]
91}
92
93/// TEST UNIT READY, as the 12-byte all-zero CDB of the OEM sequence.
94pub const fn test_unit_ready() -> [u8; 12] {
95    [0; 12]
96}
97
98/// PREPARE: WRITE BUFFER mode 1 with no data; enters the update session.
99pub const fn enter_update() -> [u8; 12] {
100    [
101        0x3B, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00,
102    ]
103}
104
105/// PREPARE dialects whose flag-copy bodies are recognized by the entry parser.
106/// A byte constructor is not receiver detection or authorization to write.
107#[derive(Clone, Copy, Debug, PartialEq, Eq)]
108pub enum Prepare {
109    /// Legacy WRITE BUFFER mode 1, buffer zero.
110    ModeOne,
111    /// WRITE BUFFER mode 6, buffer 0x67, entering the same flag-copy body.
112    ModeSix67,
113    /// Legacy entry with update bit zero cleared, preserving the boot region
114    /// only when the receiver's range-selection contract establishes that meaning.
115    ModeOnePreserveBoot,
116    /// Buffer-0x67 entry with the same receiver-derived preservation policy.
117    ModeSix67PreserveBoot,
118}
119impl Prepare {
120    /// Zero-length data-out command with the selected boot policy.
121    pub const fn command(self) -> [u8; 12] {
122        let mut command = enter_update();
123        if matches!(self, Self::ModeSix67 | Self::ModeSix67PreserveBoot) {
124            command[1] = 6;
125            command[2] = 0x67;
126        }
127        if matches!(
128            self,
129            Self::ModeOnePreserveBoot | Self::ModeSix67PreserveBoot
130        ) {
131            command[9] &= !1;
132        }
133        command
134    }
135}
136
137/// STREAM: WRITE BUFFER mode 6 of `len` bytes at file offset `off` (24 bits).
138pub const fn transfer(off: u32, len: u16) -> [u8; 12] {
139    [
140        0x3B,
141        0x06,
142        0x00,
143        (off >> 16) as u8,
144        (off >> 8) as u8,
145        off as u8,
146        0x00,
147        (len >> 8) as u8,
148        len as u8,
149        0x00,
150        0x00,
151        0x00,
152    ]
153}
154
155/// COMMIT: WRITE BUFFER mode 7 with no data and the `1B 12` trailer.
156pub const fn finish() -> [u8; 12] {
157    [
158        0x3B, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x12,
159    ]
160}
161
162/// STATUS: REQUEST SENSE exactly as the OEM update tools issue it: allocation
163/// length [`REQUEST_SENSE_LEN`] in byte 4 and `80` in the control byte.
164pub const fn request_sense() -> [u8; 12] {
165    [
166        0x03,
167        0x00,
168        0x00,
169        0x00,
170        REQUEST_SENSE_LEN as u8,
171        0x80,
172        0x00,
173        0x00,
174        0x00,
175        0x00,
176        0x00,
177        0x00,
178    ]
179}
180
181#[cfg(test)]
182#[path = "cdb_tests.rs"]
183mod tests;