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;