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