cat_dev/fsemul/sdio/
proto.rs

1//! SDIO Protocol implementations live here.
2//!
3//! NOTE: this is not a traditional SDIO protocol that you may be familiar with
4//! this is very specific to the nintendo's CAT-DEV environment. It is also
5//! split over two TCP ports.
6//!
7//! The only part that "has a protocol" too is the control port. The data port
8//! is literally just transferring files around.
9
10use crate::{errors::NetworkParseError, fsemul::sdio::errors::SDIOProtocolError};
11use bytes::{Bytes, BytesMut};
12use tokio::io::Error as IoError;
13use tokio_util::codec::{Decoder, Encoder};
14use tracing::debug;
15use valuable::{Fields, NamedField, NamedValues, StructDef, Structable, Valuable, Value, Visit};
16
17/// The size of an SDIO Block we end up serving.
18pub const SDIO_BLOCK_SIZE: usize = 0x200_usize;
19/// The size of an SDIO Block we end up serving.
20pub const SDIO_BLOCK_SIZE_AS_U32: u32 = 0x200_u32;
21/// The size of a single TCP packet we should end up serving.
22pub const SDIO_TCP_PACKET_SIZE: usize = 0x10000_usize;
23/// The amount of blocks that can fit within a single packet.
24pub const SDIO_BLOCKS_PER_PACKET: usize = SDIO_TCP_PACKET_SIZE / SDIO_BLOCK_SIZE;
25
26/// A codec that chunks a stream into SDIO Control packets.
27///
28/// All SDIO Control packets are 512 bytes long. ALWAYS.
29#[derive(Copy, Clone, Debug, PartialEq, Eq)]
30pub struct ChunkSDIOControlCodec;
31
32impl Decoder for ChunkSDIOControlCodec {
33	type Item = BytesMut;
34	type Error = IoError;
35
36	fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
37		if src.len() < 512 {
38			Ok(None)
39		} else {
40			Ok(Some(src.split_to(512)))
41		}
42	}
43}
44
45impl Encoder<Bytes> for ChunkSDIOControlCodec {
46	type Error = IoError;
47
48	fn encode(&mut self, item: Bytes, dst: &mut BytesMut) -> Result<(), Self::Error> {
49		dst.reserve(512);
50		dst.extend(item);
51		Ok(())
52	}
53}
54
55/// The types of packets that can be received by the SDIO Control Port.
56#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
57pub enum SdioControlPacketType {
58	/// A series of simple log messages, or control type messages.
59	Message,
60	/// Instruction to read on the data stream.
61	Read,
62	/// Instruction to write on the data stream.
63	Write,
64	/// TODO(mythra): confirm this is what it does.
65	///
66	/// Seems to be used in older firmwares to tell the server to
67	/// 'start' the SDIO block channel.
68	StartBlockChannel,
69	/// TODO(mythra): confirm this is what it does.
70	///
71	/// Seems to be used in older firmwares to tell the server to
72	/// 'start' the CTRL Character channel.
73	StartControlListeningChannel,
74}
75
76impl From<SdioControlPacketType> for u8 {
77	fn from(value: SdioControlPacketType) -> Self {
78		match value {
79			SdioControlPacketType::Message => 8,
80			SdioControlPacketType::Read => 0,
81			SdioControlPacketType::Write => 1,
82			SdioControlPacketType::StartBlockChannel => 0xA,
83			SdioControlPacketType::StartControlListeningChannel => 0xB,
84		}
85	}
86}
87
88impl TryFrom<u8> for SdioControlPacketType {
89	type Error = SDIOProtocolError;
90
91	fn try_from(value: u8) -> Result<Self, Self::Error> {
92		match value {
93			0 => Ok(Self::Read),
94			1 => Ok(Self::Write),
95			8 => Ok(Self::Message),
96			0xA => Ok(Self::StartBlockChannel),
97			0xB => Ok(Self::StartControlListeningChannel),
98			_ => Err(SDIOProtocolError::UnknownPrintfPacketType(value)),
99		}
100	}
101}
102
103/// Handle a Read Request coming over the SDIO Control port.
104#[derive(Debug, PartialEq, Eq)]
105pub struct SdioControlReadRequest {
106	lba: u32,
107	blocks: u32,
108	channel: u32,
109}
110
111impl SdioControlReadRequest {
112	/// Get the address to read from.
113	#[must_use]
114	pub const fn lba(&self) -> u32 {
115		self.lba * SDIO_BLOCK_SIZE_AS_U32
116	}
117
118	/// Get the amount of blocks to read.
119	#[must_use]
120	pub const fn blocks(&self) -> u32 {
121		self.blocks
122	}
123
124	/// Get the channel to read from.
125	#[must_use]
126	pub const fn channel(&self) -> u32 {
127		self.channel
128	}
129}
130
131impl TryFrom<Bytes> for SdioControlReadRequest {
132	type Error = SDIOProtocolError;
133
134	fn try_from(value: Bytes) -> Result<Self, Self::Error> {
135		if value.len() != 512 {
136			return Err(SDIOProtocolError::PrintfInvalidSize(value.len()));
137		}
138		if value[0] != 0 {
139			return Err(SDIOProtocolError::UnknownPrintfPacketType(value[0]));
140		}
141
142		let lba = u32::from_le_bytes([value[4], value[5], value[6], value[7]]);
143		let blocks = u32::from_le_bytes([value[8], value[9], value[10], value[11]]);
144		let channel = u32::from_le_bytes([value[12], value[13], value[14], value[15]]);
145		if value[12] >= 0xC {
146			return Err(SDIOProtocolError::PrintfInvalidChannel(value[12], channel));
147		}
148
149		Ok(Self {
150			lba,
151			blocks,
152			channel,
153		})
154	}
155}
156
157const CONTROL_READ_REQUEST_FIELDS: &[NamedField<'static>] = &[
158	NamedField::new("lba"),
159	NamedField::new("blocks"),
160	NamedField::new("channel"),
161];
162
163impl Structable for SdioControlReadRequest {
164	fn definition(&self) -> StructDef<'_> {
165		StructDef::new_static(
166			"SdioControlReadRequest",
167			Fields::Named(CONTROL_READ_REQUEST_FIELDS),
168		)
169	}
170}
171
172impl Valuable for SdioControlReadRequest {
173	fn as_value(&self) -> Value<'_> {
174		Value::Structable(self)
175	}
176
177	fn visit(&self, visitor: &mut dyn Visit) {
178		visitor.visit_named_fields(&NamedValues::new(
179			CONTROL_READ_REQUEST_FIELDS,
180			&[
181				Valuable::as_value(&self.lba),
182				Valuable::as_value(&self.blocks),
183				Valuable::as_value(&self.channel),
184			],
185		));
186	}
187}
188
189/// Handle a Write Request coming over the SDIO Control port.
190#[derive(Debug, PartialEq, Eq)]
191pub struct SdioControlWriteRequest {
192	lba: u32,
193	blocks: u32,
194	channel: u32,
195}
196
197impl SdioControlWriteRequest {
198	/// Get the address to write too.
199	#[must_use]
200	pub const fn lba(&self) -> u32 {
201		self.lba * SDIO_BLOCK_SIZE_AS_U32
202	}
203
204	/// Get the amount of blocks to read.
205	#[must_use]
206	pub const fn blocks(&self) -> u32 {
207		self.blocks
208	}
209
210	/// Get the channel to read from.
211	#[must_use]
212	pub const fn channel(&self) -> u32 {
213		self.channel
214	}
215}
216
217impl TryFrom<Bytes> for SdioControlWriteRequest {
218	type Error = SDIOProtocolError;
219
220	fn try_from(value: Bytes) -> Result<Self, Self::Error> {
221		if value.len() != 512 {
222			return Err(SDIOProtocolError::PrintfInvalidSize(value.len()));
223		}
224		if value[0] != 1 {
225			return Err(SDIOProtocolError::UnknownPrintfPacketType(value[0]));
226		}
227
228		let lba = u32::from_le_bytes([value[4], value[5], value[6], value[7]]);
229		let blocks = u32::from_le_bytes([value[8], value[9], value[10], value[11]]);
230		let channel = u32::from_le_bytes([value[12], value[13], value[14], value[15]]);
231		if value[12] >= 0xC {
232			return Err(SDIOProtocolError::PrintfInvalidChannel(value[12], channel));
233		}
234
235		Ok(Self {
236			lba,
237			blocks,
238			channel,
239		})
240	}
241}
242
243const CONTROL_WRITE_REQUEST_FIELDS: &[NamedField<'static>] = &[
244	NamedField::new("lba"),
245	NamedField::new("blocks"),
246	NamedField::new("channel"),
247];
248
249impl Structable for SdioControlWriteRequest {
250	fn definition(&self) -> StructDef<'_> {
251		StructDef::new_static(
252			"SdioControlReadRequest",
253			Fields::Named(CONTROL_WRITE_REQUEST_FIELDS),
254		)
255	}
256}
257
258impl Valuable for SdioControlWriteRequest {
259	fn as_value(&self) -> Value<'_> {
260		Value::Structable(self)
261	}
262
263	fn visit(&self, visitor: &mut dyn Visit) {
264		visitor.visit_named_fields(&NamedValues::new(
265			CONTROL_WRITE_REQUEST_FIELDS,
266			&[
267				Valuable::as_value(&self.lba),
268				Valuable::as_value(&self.blocks),
269				Valuable::as_value(&self.channel),
270			],
271		));
272	}
273}
274
275#[derive(Clone, Debug, PartialEq, Eq, Valuable)]
276pub enum SdioControlMessage {
277	/// A message to print to the screen.
278	Printf(String),
279}
280
281/// Handle a Write Request coming over the SDIO Control port.
282#[derive(Debug, PartialEq, Eq)]
283pub struct SdioControlMessageRequest {
284	character_length: u16,
285	messages: Vec<SdioControlMessage>,
286}
287
288impl SdioControlMessageRequest {
289	#[must_use]
290	pub const fn character_length(&self) -> u16 {
291		self.character_length
292	}
293
294	#[must_use]
295	pub const fn messages(&self) -> &Vec<SdioControlMessage> {
296		&self.messages
297	}
298
299	#[must_use]
300	pub fn messages_owned(self) -> Vec<SdioControlMessage> {
301		self.messages
302	}
303}
304
305impl TryFrom<Bytes> for SdioControlMessageRequest {
306	type Error = NetworkParseError;
307
308	#[allow(
309    // We will actually loop in the future.
310    clippy::never_loop,
311  )]
312	fn try_from(value: Bytes) -> Result<Self, Self::Error> {
313		if value.len() != 512 {
314			return Err(SDIOProtocolError::PrintfInvalidSize(value.len()).into());
315		}
316		if value[0] != 8 {
317			return Err(SDIOProtocolError::UnknownPrintfPacketType(value[0]).into());
318		}
319
320		let character_length = u16::from_le_bytes([value[0x2], value[0x3]]);
321		let mut messages = Vec::with_capacity(1);
322		loop {
323			let message_ty = u16::from_le_bytes([
324				value[4 + (messages.len() * 4)],
325				value[4 + (messages.len() * 4) + 1],
326			]);
327			// puVar1 & bytes to send after two u16s.
328			if message_ty == 4 {
329				// first two bytes are unknown
330				let mut buff = value.slice((4 + (messages.len() * 4) + 8)..);
331				if let Some(eol) = buff.iter().position(|item| *item == 0x0) {
332					buff.truncate(eol);
333				}
334				messages.push(SdioControlMessage::Printf(
335					// Yes they sometimes dump junk to us.
336					//
337					// But alas, what can you do.
338					String::from_utf8_lossy(&buff).to_string(),
339				));
340				// These message types consume the whole buffer probably idk
341				break;
342			} else if message_ty == 9 {
343				debug!(
344					buff = format!("{:02X}", value),
345					"Unknown message type == 9 for SDIO, Not Sure How to Respond?"
346				);
347				break;
348			}
349
350			return Err(SDIOProtocolError::UnknownPrintfMessageType(message_ty).into());
351		}
352
353		Ok(Self {
354			character_length,
355			messages,
356		})
357	}
358}
359
360const MESSAGE_REQUEST_FIELDS: &[NamedField<'static>] = &[
361	NamedField::new("character_length"),
362	NamedField::new("messages"),
363];
364
365impl Structable for SdioControlMessageRequest {
366	fn definition(&self) -> StructDef<'_> {
367		StructDef::new_static(
368			"SdioControlMessageRequest",
369			Fields::Named(MESSAGE_REQUEST_FIELDS),
370		)
371	}
372}
373
374impl Valuable for SdioControlMessageRequest {
375	fn as_value(&self) -> Value<'_> {
376		Value::Structable(self)
377	}
378
379	fn visit(&self, visitor: &mut dyn Visit) {
380		visitor.visit_named_fields(&NamedValues::new(
381			MESSAGE_REQUEST_FIELDS,
382			&[
383				Valuable::as_value(&self.character_length),
384				Valuable::as_value(&self.messages),
385			],
386		));
387	}
388}
389
390#[cfg(test)]
391mod unit_tests {
392	use super::*;
393
394	#[test]
395	pub fn decoder_chunks_chunkily() {
396		// Empty
397		{
398			let mut codec = ChunkSDIOControlCodec;
399			let mut buff = BytesMut::with_capacity(0);
400
401			codec
402				.decode(&mut buff)
403				.expect("Failed to call SDIO Control codec on empty.");
404		}
405
406		// Too smol owo.
407		{
408			let mut codec = ChunkSDIOControlCodec;
409			let mut buff = BytesMut::zeroed(511);
410
411			assert_eq!(
412        codec.decode(&mut buff).expect("Failed to call SDIO Control Codec"),
413        None,
414        "Codec could not successfully decode a buffer that was one byte too short for SDIO Control.",
415      );
416		}
417
418		// owo are you sure this data can fit???
419		{
420			let mut codec = ChunkSDIOControlCodec;
421			let mut buff = BytesMut::with_capacity(1024);
422
423			buff.extend(vec![0x1; 512]);
424			buff.extend(vec![0x2; 512]);
425
426			assert_eq!(
427        codec.decode(&mut buff).expect("Failed to call SDIO Control Codec"),
428        Some(BytesMut::from(&*vec![0x1_u8; 512])),
429        "Codec did not successfully decode first part of a too large packet to SDIO Control.",
430      );
431			assert_eq!(
432        codec.decode(&mut buff).expect("Failed to call SDIO Control Codec"),
433        Some(BytesMut::from(&*vec![0x2_u8; 512])),
434        "Codec did not successfully decode second part of a too large packet to SDIO Control."
435      );
436		}
437
438		// owo okay
439		{
440			let mut codec = ChunkSDIOControlCodec;
441			let mut buff = BytesMut::zeroed(512);
442
443			assert_eq!(
444				codec
445					.decode(&mut buff)
446					.expect("Failed to call SDIO Control Codec"),
447				Some(BytesMut::zeroed(512)),
448				"Codec did not succsesfully decode a just right packet size to SDIO Control.",
449			);
450		}
451	}
452
453	#[test]
454	pub fn roundtrip_control_packet_type() {
455		for packet_ty in vec![
456			SdioControlPacketType::Message,
457			SdioControlPacketType::Read,
458			SdioControlPacketType::Write,
459		] {
460			assert_eq!(
461				Ok(packet_ty),
462				SdioControlPacketType::try_from(u8::from(packet_ty)),
463				"Round-tripped control packet type was not the same?"
464			);
465		}
466	}
467
468	#[test]
469	pub fn parse_read_request() {
470		// Real life read request packet being parsed.
471		{
472			let read_request = SdioControlReadRequest::try_from(Bytes::from(vec![
473				0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0x7f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
474				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
475				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
476				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
477				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
478				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
479				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
480				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
481				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
482				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
483				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
484				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
485				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
486				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
487				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
488				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
489				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
490				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
491				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
492				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
493				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
494				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
495				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
496				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
497				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
498				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
499				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
500				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
501				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
502				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
503				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
504				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
505				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
506				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
507				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
508				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
509				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
510			]))
511			.expect("Failed to parse real life SDIO Control Read Request");
512
513			assert_eq!(
514				read_request.lba(),
515				0xFFFF_0000,
516				"Failed to parse correct address to read from on a real life read request.",
517			);
518			assert_eq!(
519				read_request.blocks(),
520				2,
521				"Failed to parse correct amount of blocks to read from real life read request.",
522			);
523			assert_eq!(
524				read_request.channel(),
525				0,
526				"Failed to parse the correct channel to read from on a real life read request.",
527			);
528		}
529	}
530
531	// TODO(mythra): get real life write request.
532
533	#[test]
534	pub fn parse_real_life_message_request() {
535		{
536			let message_request = SdioControlMessageRequest::try_from(Bytes::from(vec![
537				0x08, 0x00, 0x3a, 0x00, 0x04, 0x00, 0x0c, 0x00, 0xff, 0xff, 0x3a, 0x00, 0x42, 0x4f,
538				0x4f, 0x54, 0x31, 0x3a, 0x20, 0x52, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x44,
539				0x55, 0x41, 0x4c, 0x20, 0x62, 0x6f, 0x6f, 0x74, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x72,
540				0x20, 0x61, 0x74, 0x20, 0x30, 0x78, 0x30, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
541				0x2e, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
542				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
543				0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
544				0x08, 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04,
545				0x18, 0x00, 0x00, 0x40, 0x00, 0x00, 0x20, 0x00, 0x02, 0x00, 0x10, 0x00, 0x00, 0x00,
546				0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
547				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
548				0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
549				0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
550				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
551				0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
552				0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
553				0x00, 0x00, 0x00, 0x10, 0x80, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
554				0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x00, 0x40, 0x00, 0x04,
555				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
556				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
557				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
558				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
559				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
560				0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
561				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
562				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x10, 0x82,
563				0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
564				0x00, 0x00, 0x40, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
565				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
566				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
567				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
568				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
569				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
570				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
571				0x00, 0x04, 0x00, 0x00, 0x20, 0x00, 0x80, 0x00, 0x01, 0x08, 0x00, 0x00, 0x20, 0x00,
572				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0xc0,
573				0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
574			]))
575			.expect("Failed to parse real life SDIO Control Message Request");
576
577			assert_eq!(
578				message_request.messages(),
579				&vec![SdioControlMessage::Printf(
580					"BOOT1: Running DUAL bootloader at 0x08000000.\n".to_owned()
581				)],
582				"Cannot Parse SDIO Control Message Request.",
583			);
584		}
585	}
586}