1use crate::{
5 errors::NetworkParseError,
6 fsemul::sdio::errors::{SdioApiError, SdioProtocolError},
7};
8use bytes::{Buf, BufMut, Bytes, BytesMut};
9use std::fmt::Write;
10use valuable::{
11 EnumDef, Enumerable, Fields, NamedField, NamedValues, StructDef, Structable, Valuable, Value,
12 Variant, VariantDef, Visit,
13};
14
15#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Valuable)]
18pub enum SdioControlTelnetChannel {
19 SysConfigTool,
21 CafeOS,
23 DevkitMsg,
25 Arbitrary(u16),
27}
28
29impl From<SdioControlTelnetChannel> for u16 {
30 fn from(value: SdioControlTelnetChannel) -> Self {
31 match value {
32 SdioControlTelnetChannel::SysConfigTool => 4,
33 SdioControlTelnetChannel::DevkitMsg => 8,
34 SdioControlTelnetChannel::CafeOS => 9,
35 SdioControlTelnetChannel::Arbitrary(val) => val,
36 }
37 }
38}
39
40impl TryFrom<u16> for SdioControlTelnetChannel {
41 type Error = SdioProtocolError;
42
43 fn try_from(value: u16) -> Result<Self, Self::Error> {
44 match value {
45 4 => Ok(Self::SysConfigTool),
46 8 => Ok(Self::DevkitMsg),
47 9 => Ok(Self::CafeOS),
48 _ => Ok(SdioControlTelnetChannel::Arbitrary(value)),
49 }
50 }
51}
52
53#[derive(Clone, Debug, PartialEq, Eq, Hash)]
58pub enum SdioControlTelnetData {
59 StringData(Bytes),
61 Backspace,
63 ClearLine,
65}
66
67impl From<&SdioControlTelnetData> for Bytes {
68 fn from(value: &SdioControlTelnetData) -> Self {
69 match value {
70 SdioControlTelnetData::Backspace => Bytes::from(vec![0xFF, 0xF7]),
71 SdioControlTelnetData::ClearLine => Bytes::from(vec![0xFF, 0xF8]),
72 SdioControlTelnetData::StringData(by) => by.clone(),
73 }
74 }
75}
76
77impl From<SdioControlTelnetData> for Bytes {
78 fn from(value: SdioControlTelnetData) -> Self {
79 Self::from(&value)
80 }
81}
82
83static SDIO_CONTROL_TELNET_DATA_VARIANTS: &[VariantDef<'static>] = &[
84 VariantDef::new("Backspace", Fields::Unnamed(0)),
85 VariantDef::new("ClearLine", Fields::Unnamed(0)),
86 VariantDef::new("StringData", Fields::Unnamed(1)),
87];
88
89impl Enumerable for SdioControlTelnetData {
90 fn definition(&self) -> EnumDef<'_> {
91 EnumDef::new_static("SdioControlTelnetData", SDIO_CONTROL_TELNET_DATA_VARIANTS)
92 }
93
94 fn variant(&self) -> Variant<'_> {
95 match self {
96 Self::Backspace => Variant::Static(&SDIO_CONTROL_TELNET_DATA_VARIANTS[0]),
97 Self::ClearLine => Variant::Static(&SDIO_CONTROL_TELNET_DATA_VARIANTS[1]),
98 Self::StringData(_) => Variant::Static(&SDIO_CONTROL_TELNET_DATA_VARIANTS[2]),
99 }
100 }
101}
102
103impl Valuable for SdioControlTelnetData {
104 fn as_value(&self) -> Value<'_> {
105 Value::Enumerable(self)
106 }
107
108 fn visit(&self, visitor: &mut dyn Visit) {
109 match self {
110 Self::StringData(line) => {
111 let mut data = String::with_capacity(line.len() * 2);
112 for byte in line {
113 _ = write!(&mut data, "{byte:02x}");
114 }
115 visitor.visit_unnamed_fields(&[Valuable::as_value(&data)]);
116 }
117 Self::Backspace | Self::ClearLine => visitor.visit_unnamed_fields(&[]),
118 }
119 }
120}
121
122#[derive(Clone, Debug, PartialEq, Eq, Valuable)]
124pub struct SdioControlTelnetMessage {
125 buff: Vec<SdioControlTelnetData>,
130 read_amount: usize,
133 telnet_channel: SdioControlTelnetChannel,
135}
136
137impl SdioControlTelnetMessage {
138 pub fn new(
144 buff: String,
145 telnet_channel: SdioControlTelnetChannel,
146 ) -> Result<Self, SdioApiError> {
147 let this = Self {
148 buff: vec![SdioControlTelnetData::StringData(Bytes::from(
149 buff.into_bytes(),
150 ))],
151 read_amount: 0,
152 telnet_channel,
153 };
154 if this.calculate_size() > 499 {
155 return Err(SdioApiError::TelnetMessageToLong(this.serialize_all()));
156 }
157 Ok(this)
158 }
159
160 pub fn new_raw(
166 buff: Vec<SdioControlTelnetData>,
167 telnet_channel: SdioControlTelnetChannel,
168 ) -> Result<Self, SdioApiError> {
169 let this = Self {
170 buff,
171 read_amount: 0,
172 telnet_channel,
173 };
174 if this.calculate_size() > 499 {
175 return Err(SdioApiError::TelnetMessageToLong(this.serialize_all()));
176 }
177 Ok(this)
178 }
179
180 #[cfg(test)]
186 #[must_use]
187 pub fn new_with_read_amount(
188 buff: String,
189 read_amount: usize,
190 telnet_channel: SdioControlTelnetChannel,
191 ) -> Result<Self, SdioApiError> {
192 let this = Self {
193 buff: vec![SdioControlTelnetData::StringData(Bytes::from(
194 buff.into_bytes(),
195 ))],
196 read_amount,
197 telnet_channel,
198 };
199 if this.calculate_size() > 499 {
200 return Err(SdioApiError::TelnetMessageToLong(this.serialize_all()));
201 }
202 Ok(this)
203 }
204
205 #[must_use]
207 pub fn buffer(&self) -> &Vec<SdioControlTelnetData> {
208 &self.buff
209 }
210
211 #[must_use]
214 pub fn buffer_as_complete_string(self) -> String {
215 let mut result = String::new();
216 for item in self.buff {
217 match item {
218 SdioControlTelnetData::Backspace => {
219 _ = result.pop();
220 }
221 SdioControlTelnetData::ClearLine => {
222 result = String::new();
223 }
224 SdioControlTelnetData::StringData(data) => {
225 result.push_str(&String::from_utf8_lossy(&data));
226 }
227 }
228 }
229 result
230 }
231
232 pub fn set_buffer(&mut self, new: String) -> Result<(), SdioApiError> {
238 if new.len() > 499 {
239 return Err(SdioApiError::TelnetMessageToLong(Bytes::from(
240 new.into_bytes(),
241 )));
242 }
243
244 self.buff = vec![SdioControlTelnetData::StringData(Bytes::from(
245 new.into_bytes(),
246 ))];
247 Ok(())
248 }
249
250 pub fn set_buffer_raw(
256 &mut self,
257 mut new: Vec<SdioControlTelnetData>,
258 ) -> Result<(), SdioApiError> {
259 std::mem::swap(&mut new, &mut self.buff);
260 if self.calculate_size() > 499 {
261 let serialized = self.serialize_all();
262 std::mem::swap(&mut new, &mut self.buff);
263 return Err(SdioApiError::TelnetMessageToLong(serialized));
264 }
265
266 Ok(())
267 }
268
269 #[must_use]
271 pub const fn channel(&self) -> SdioControlTelnetChannel {
272 self.telnet_channel
273 }
274
275 pub const fn set_channel(&mut self, new: SdioControlTelnetChannel) {
276 self.telnet_channel = new;
277 }
278
279 #[must_use]
281 fn calculate_size(&self) -> usize {
282 let mut result = 0_usize;
283 for item in &self.buff {
284 result += Bytes::from(item).len();
285 }
286 result
287 }
288
289 fn serialize_all(&self) -> Bytes {
290 let mut final_result = BytesMut::new();
291 for item in &self.buff {
292 final_result.extend(Bytes::from(item));
293 }
294 final_result.freeze()
295 }
296}
297
298impl From<&SdioControlTelnetMessage> for Bytes {
299 fn from(value: &SdioControlTelnetMessage) -> Self {
300 let mut bytes = BytesMut::with_capacity(8 + value.buff.len());
301 bytes.put_u16_le(u16::from(value.telnet_channel));
302 bytes.extend_from_slice(&[0x0C, 0x00, 0xFF, 0xFF]);
303 let serialized = value.serialize_all();
304 bytes.put_u16_le(u16::try_from(serialized.len() + 12).unwrap_or(u16::MAX));
305 bytes.extend(serialized);
306 bytes.put_u8(0x00);
307 bytes.freeze()
308 }
309}
310
311impl From<SdioControlTelnetMessage> for Bytes {
312 fn from(value: SdioControlTelnetMessage) -> Self {
313 Self::from(&value)
314 }
315}
316
317impl TryFrom<Bytes> for SdioControlTelnetMessage {
318 type Error = NetworkParseError;
319
320 fn try_from(value: Bytes) -> Result<Self, Self::Error> {
321 if value.len() < 8 {
322 return Err(NetworkParseError::NotEnoughData(
323 "SdioControlTelnetMessage",
324 8,
325 value.len(),
326 value,
327 ));
328 }
329
330 let telnet_channel =
331 SdioControlTelnetChannel::try_from(u16::from_le_bytes([value[0], value[1]]))?;
332 debug_assert_eq!(
333 [value[2], value[3], value[4], value[5]],
334 [0x0C, 0x00, 0xFF, 0xFF],
335 "Seems to be constant across all telnet messages? MYTHRA to validate",
336 );
337 let character_len = std::cmp::min(
339 std::cmp::min(
340 u16::from_le_bytes([value[6], value[7]]).saturating_sub(4),
341 500,
342 ),
343 u16::try_from(value.len().saturating_sub(8)).unwrap_or_default(),
344 );
345 let read_bytes = 8 + character_len;
346
347 let mut final_buffer = Vec::new();
349 let mut current_string_data = Vec::new();
350
351 let mut my_iter = value
352 .iter()
353 .skip(8)
354 .take(usize::from(character_len))
355 .peekable();
356 while let Some(byte) = my_iter.next() {
357 if *byte == 0xFF {
361 let next_byte = my_iter.peek().map(|b| **b).unwrap_or_default();
362 if (251..=254).contains(&next_byte) {
364 _ = my_iter.next();
365 _ = my_iter.next();
366 } else if next_byte == 247 {
367 if !current_string_data.is_empty() {
368 final_buffer.push(SdioControlTelnetData::StringData(Bytes::from(
369 std::mem::take(&mut current_string_data),
370 )));
371 }
372 final_buffer.push(SdioControlTelnetData::Backspace);
373 } else if next_byte == 248 {
374 if !current_string_data.is_empty() {
375 final_buffer.push(SdioControlTelnetData::StringData(Bytes::from(
376 std::mem::take(&mut current_string_data),
377 )));
378 }
379 final_buffer.push(SdioControlTelnetData::ClearLine);
380 } else {
381 _ = my_iter.next();
382 }
383
384 continue;
385 } else if *byte == 0x00 {
386 break;
387 }
388 current_string_data.push(*byte);
389 }
390
391 if !current_string_data.is_empty() {
392 final_buffer.push(SdioControlTelnetData::StringData(Bytes::from(
393 std::mem::take(&mut current_string_data),
394 )));
395 }
396
397 Ok(Self {
398 buff: final_buffer,
399 read_amount: usize::from(read_bytes),
400 telnet_channel,
401 })
402 }
403}
404
405#[derive(Debug, PartialEq, Eq)]
407pub struct SdioControlMessageRequest {
408 messages: Vec<SdioControlTelnetMessage>,
409}
410
411impl SdioControlMessageRequest {
412 #[must_use]
413 pub const fn new(messages: Vec<SdioControlTelnetMessage>) -> Self {
414 Self { messages }
415 }
416
417 #[must_use]
418 pub const fn messages(&self) -> &Vec<SdioControlTelnetMessage> {
419 &self.messages
420 }
421
422 #[must_use]
423 pub fn messages_owned(self) -> Vec<SdioControlTelnetMessage> {
424 self.messages
425 }
426}
427
428impl TryFrom<&SdioControlMessageRequest> for Bytes {
429 type Error = NetworkParseError;
430
431 #[allow(
432 clippy::never_loop,
434 )]
435 fn try_from(value: &SdioControlMessageRequest) -> Result<Self, Self::Error> {
436 let mut inner_size = 3_usize;
437 let mut request_body = BytesMut::with_capacity(500);
438
439 for msg in value.messages() {
440 let value = Bytes::from(msg);
441 inner_size += value.len();
442 request_body.extend(value);
443 }
444 let pad_amount = 500_usize.saturating_sub(request_body.len());
445 request_body.extend(BytesMut::zeroed(pad_amount));
446 if request_body.len() > 500 {
447 return Err(NetworkParseError::UnexpectedTrailer(
448 "SdioControlMessageRequest",
449 request_body.freeze().slice(500..),
450 ));
451 }
452
453 let mut final_buff = BytesMut::with_capacity(512);
454 final_buff.put_u16_le(8);
455 final_buff.put_u16_le(u16::try_from(inner_size).unwrap_or(u16::MAX));
456 final_buff.extend(request_body);
457 final_buff.extend(BytesMut::zeroed(512_usize.saturating_sub(final_buff.len())));
458 Ok(final_buff.freeze())
459 }
460}
461
462impl TryFrom<SdioControlMessageRequest> for Bytes {
463 type Error = NetworkParseError;
464
465 fn try_from(value: SdioControlMessageRequest) -> Result<Self, Self::Error> {
466 Self::try_from(&value)
467 }
468}
469
470impl TryFrom<Bytes> for SdioControlMessageRequest {
471 type Error = NetworkParseError;
472
473 fn try_from(mut value: Bytes) -> Result<Self, Self::Error> {
474 if value.len() != 512 {
475 return Err(SdioProtocolError::PrintfInvalidSize(value.len()).into());
476 }
477 let packet_type = value.get_u16_le();
478 if packet_type != 8 {
479 return Err(SdioProtocolError::UnknownPrintfPacketType(packet_type).into());
480 }
481
482 let total_character_length = usize::from(value.get_u16_le());
483 let mut messages = Vec::with_capacity(1);
484 let mut read_amount = 4_usize;
485 while read_amount < total_character_length {
486 let actual_msg = SdioControlTelnetMessage::try_from(value.clone())?;
487 read_amount += actual_msg.read_amount;
488 value.advance(actual_msg.read_amount);
489 messages.push(actual_msg);
490 }
491
492 Ok(Self { messages })
493 }
494}
495
496const MESSAGE_REQUEST_FIELDS: &[NamedField<'static>] = &[NamedField::new("messages")];
497
498impl Structable for SdioControlMessageRequest {
499 fn definition(&self) -> StructDef<'_> {
500 StructDef::new_static(
501 "SdioControlMessageRequest",
502 Fields::Named(MESSAGE_REQUEST_FIELDS),
503 )
504 }
505}
506
507impl Valuable for SdioControlMessageRequest {
508 fn as_value(&self) -> Value<'_> {
509 Value::Structable(self)
510 }
511
512 fn visit(&self, visitor: &mut dyn Visit) {
513 visitor.visit_named_fields(&NamedValues::new(
514 MESSAGE_REQUEST_FIELDS,
515 &[Valuable::as_value(&self.messages)],
516 ));
517 }
518}
519
520#[cfg(test)]
521mod unit_tests {
522 use super::*;
523
524 #[test]
525 pub fn serdeser_real_life_message_request() {
526 {
527 let message_request = SdioControlMessageRequest::try_from(Bytes::from(vec![
528 0x08, 0x00, 0x3a, 0x00, 0x04, 0x00, 0x0c, 0x00, 0xff, 0xff, 0x3a, 0x00, 0x42, 0x4f,
529 0x4f, 0x54, 0x31, 0x3a, 0x20, 0x52, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x44,
530 0x55, 0x41, 0x4c, 0x20, 0x62, 0x6f, 0x6f, 0x74, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x72,
531 0x20, 0x61, 0x74, 0x20, 0x30, 0x78, 0x30, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
532 0x2e, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
533 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
534 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
535 0x08, 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04,
536 0x18, 0x00, 0x00, 0x40, 0x00, 0x00, 0x20, 0x00, 0x02, 0x00, 0x10, 0x00, 0x00, 0x00,
537 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
538 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
539 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
540 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
541 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
542 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
543 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
544 0x00, 0x00, 0x00, 0x10, 0x80, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
545 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x00, 0x40, 0x00, 0x04,
546 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
547 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
548 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
549 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
550 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
551 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
552 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
553 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x10, 0x82,
554 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
555 0x00, 0x00, 0x40, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
556 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
557 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
558 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
559 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
560 0x00, 0x00, 0x00, 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, 0x04, 0x00, 0x00, 0x20, 0x00, 0x80, 0x00, 0x01, 0x08, 0x00, 0x00, 0x20, 0x00,
563 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0xc0,
564 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
565 ]))
566 .expect("Failed to parse real life SDIO Control Message Request");
567
568 assert_eq!(
569 message_request.messages(),
570 &vec![
571 SdioControlTelnetMessage::new_with_read_amount(
572 "BOOT1: Running DUAL bootloader at 0x08000000.\n".to_owned(),
573 0x3E,
574 SdioControlTelnetChannel::SysConfigTool,
575 )
576 .expect("Failed to create telnet message to match against!")
577 ],
578 );
579
580 assert_eq!(
581 Bytes::try_from(&message_request)
582 .expect("Failed to serialize real message request!"),
583 Bytes::from(vec![
585 0x08, 0x00, 0x3a, 0x00, 0x04, 0x00, 0x0c, 0x00, 0xff, 0xff, 0x3a, 0x00, 0x42,
586 0x4f, 0x4f, 0x54, 0x31, 0x3a, 0x20, 0x52, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67,
587 0x20, 0x44, 0x55, 0x41, 0x4c, 0x20, 0x62, 0x6f, 0x6f, 0x74, 0x6c, 0x6f, 0x61,
588 0x64, 0x65, 0x72, 0x20, 0x61, 0x74, 0x20, 0x30, 0x78, 0x30, 0x38, 0x30, 0x30,
589 0x30, 0x30, 0x30, 0x30, 0x2e, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
590 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
591 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
592 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
593 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
594 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
595 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
596 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
597 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
598 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
599 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
600 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
601 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
602 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
603 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
604 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
605 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
606 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
607 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
608 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
609 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
610 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
611 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
612 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
613 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
614 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
615 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
616 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
617 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
618 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
619 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
620 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
621 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
622 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
623 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
624 0x00, 0x00, 0x00, 0x00, 0x00,
625 ]),
626 );
627 }
628 }
629
630 #[test]
631 pub fn roundtrip_telnet_channel_type() {
632 for channel_ty in vec![
633 SdioControlTelnetChannel::SysConfigTool,
634 SdioControlTelnetChannel::DevkitMsg,
635 SdioControlTelnetChannel::CafeOS,
636 ] {
637 assert_eq!(
638 Ok(channel_ty),
639 SdioControlTelnetChannel::try_from(u16::from(channel_ty)),
640 "Round-tripped telnet channel type was not the same?"
641 );
642 }
643 }
644}