use crate::{
errors::NetworkParseError,
fsemul::sdio::errors::{SdioApiError, SdioProtocolError},
};
use bytes::{Buf, BufMut, Bytes, BytesMut};
use std::fmt::Write;
use valuable::{
EnumDef, Enumerable, Fields, NamedField, NamedValues, StructDef, Structable, Valuable, Value,
Variant, VariantDef, Visit,
};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Valuable)]
pub enum SdioControlTelnetChannel {
SysConfigTool,
CafeOS,
DevkitMsg,
}
impl From<SdioControlTelnetChannel> for u16 {
fn from(value: SdioControlTelnetChannel) -> Self {
match value {
SdioControlTelnetChannel::SysConfigTool => 4,
SdioControlTelnetChannel::DevkitMsg => 8,
SdioControlTelnetChannel::CafeOS => 9,
}
}
}
impl TryFrom<u16> for SdioControlTelnetChannel {
type Error = SdioProtocolError;
fn try_from(value: u16) -> Result<Self, Self::Error> {
match value {
4 => Ok(Self::SysConfigTool),
8 => Ok(Self::DevkitMsg),
9 => Ok(Self::CafeOS),
_ => Err(SdioProtocolError::UnknownPrintfTelnetChannel(value)),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum SdioControlTelnetData {
StringData(Bytes),
Backspace,
ClearLine,
}
impl From<&SdioControlTelnetData> for Bytes {
fn from(value: &SdioControlTelnetData) -> Self {
match value {
SdioControlTelnetData::Backspace => Bytes::from(vec![0xFF, 0xF7]),
SdioControlTelnetData::ClearLine => Bytes::from(vec![0xFF, 0xF8]),
SdioControlTelnetData::StringData(by) => by.clone(),
}
}
}
impl From<SdioControlTelnetData> for Bytes {
fn from(value: SdioControlTelnetData) -> Self {
Self::from(&value)
}
}
static SDIO_CONTROL_TELNET_DATA_VARIANTS: &[VariantDef<'static>] = &[
VariantDef::new("Backspace", Fields::Unnamed(0)),
VariantDef::new("ClearLine", Fields::Unnamed(0)),
VariantDef::new("StringData", Fields::Unnamed(1)),
];
impl Enumerable for SdioControlTelnetData {
fn definition(&self) -> EnumDef<'_> {
EnumDef::new_static("SdioControlTelnetData", SDIO_CONTROL_TELNET_DATA_VARIANTS)
}
fn variant(&self) -> Variant<'_> {
match self {
Self::Backspace => Variant::Static(&SDIO_CONTROL_TELNET_DATA_VARIANTS[0]),
Self::ClearLine => Variant::Static(&SDIO_CONTROL_TELNET_DATA_VARIANTS[1]),
Self::StringData(_) => Variant::Static(&SDIO_CONTROL_TELNET_DATA_VARIANTS[2]),
}
}
}
impl Valuable for SdioControlTelnetData {
fn as_value(&self) -> Value<'_> {
Value::Enumerable(self)
}
fn visit(&self, visitor: &mut dyn Visit) {
match self {
Self::StringData(line) => {
let mut data = String::with_capacity(line.len() * 2);
for byte in line {
_ = write!(&mut data, "{byte:02x}");
}
visitor.visit_unnamed_fields(&[Valuable::as_value(&data)]);
}
Self::Backspace | Self::ClearLine => visitor.visit_unnamed_fields(&[]),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Valuable)]
pub struct SdioControlTelnetMessage {
buff: Vec<SdioControlTelnetData>,
read_amount: usize,
telnet_channel: SdioControlTelnetChannel,
}
impl SdioControlTelnetMessage {
pub fn new(
buff: String,
telnet_channel: SdioControlTelnetChannel,
) -> Result<Self, SdioApiError> {
let this = Self {
buff: vec![SdioControlTelnetData::StringData(Bytes::from(
buff.into_bytes(),
))],
read_amount: 0,
telnet_channel,
};
if this.calculate_size() > 499 {
return Err(SdioApiError::TelnetMessageToLong(this.serialize_all()));
}
Ok(this)
}
pub fn new_raw(
buff: Vec<SdioControlTelnetData>,
telnet_channel: SdioControlTelnetChannel,
) -> Result<Self, SdioApiError> {
let this = Self {
buff,
read_amount: 0,
telnet_channel,
};
if this.calculate_size() > 499 {
return Err(SdioApiError::TelnetMessageToLong(this.serialize_all()));
}
Ok(this)
}
#[cfg(test)]
#[must_use]
pub fn new_with_read_amount(
buff: String,
read_amount: usize,
telnet_channel: SdioControlTelnetChannel,
) -> Result<Self, SdioApiError> {
let this = Self {
buff: vec![SdioControlTelnetData::StringData(Bytes::from(
buff.into_bytes(),
))],
read_amount,
telnet_channel,
};
if this.calculate_size() > 499 {
return Err(SdioApiError::TelnetMessageToLong(this.serialize_all()));
}
Ok(this)
}
#[must_use]
pub fn buffer(&self) -> &Vec<SdioControlTelnetData> {
&self.buff
}
#[must_use]
pub fn buffer_as_complete_string(self) -> String {
let mut result = String::new();
for item in self.buff {
match item {
SdioControlTelnetData::Backspace => {
_ = result.pop();
}
SdioControlTelnetData::ClearLine => {
result = String::new();
}
SdioControlTelnetData::StringData(data) => {
result.push_str(&String::from_utf8_lossy(&data));
}
}
}
result
}
pub fn set_buffer(&mut self, new: String) -> Result<(), SdioApiError> {
if new.len() > 499 {
return Err(SdioApiError::TelnetMessageToLong(Bytes::from(
new.into_bytes(),
)));
}
self.buff = vec![SdioControlTelnetData::StringData(Bytes::from(
new.into_bytes(),
))];
Ok(())
}
pub fn set_buffer_raw(
&mut self,
mut new: Vec<SdioControlTelnetData>,
) -> Result<(), SdioApiError> {
std::mem::swap(&mut new, &mut self.buff);
if self.calculate_size() > 499 {
let serialized = self.serialize_all();
std::mem::swap(&mut new, &mut self.buff);
return Err(SdioApiError::TelnetMessageToLong(serialized));
}
Ok(())
}
#[must_use]
pub const fn channel(&self) -> SdioControlTelnetChannel {
self.telnet_channel
}
pub const fn set_channel(&mut self, new: SdioControlTelnetChannel) {
self.telnet_channel = new;
}
#[must_use]
fn calculate_size(&self) -> usize {
let mut result = 0_usize;
for item in &self.buff {
result += Bytes::from(item).len();
}
result
}
fn serialize_all(&self) -> Bytes {
let mut final_result = BytesMut::new();
for item in &self.buff {
final_result.extend(Bytes::from(item));
}
final_result.freeze()
}
}
impl From<&SdioControlTelnetMessage> for Bytes {
fn from(value: &SdioControlTelnetMessage) -> Self {
let mut bytes = BytesMut::with_capacity(8 + value.buff.len());
bytes.put_u16_le(u16::from(value.telnet_channel));
bytes.extend_from_slice(&[0x0C, 0x00, 0xFF, 0xFF]);
let serialized = value.serialize_all();
bytes.put_u16_le(u16::try_from(serialized.len() + 12).unwrap_or(u16::MAX));
bytes.extend(serialized);
bytes.put_u8(0x00);
bytes.freeze()
}
}
impl From<SdioControlTelnetMessage> for Bytes {
fn from(value: SdioControlTelnetMessage) -> Self {
Self::from(&value)
}
}
impl TryFrom<Bytes> for SdioControlTelnetMessage {
type Error = NetworkParseError;
fn try_from(value: Bytes) -> Result<Self, Self::Error> {
if value.len() < 8 {
return Err(NetworkParseError::NotEnoughData(
"SdioControlTelnetMessage",
8,
value.len(),
value,
));
}
let telnet_channel =
SdioControlTelnetChannel::try_from(u16::from_le_bytes([value[0], value[1]]))?;
debug_assert_eq!(
[value[2], value[3], value[4], value[5]],
[0x0C, 0x00, 0xFF, 0xFF],
"Seems to be constant across all telnet messages? MYTHRA to validate",
);
let character_len = std::cmp::min(
std::cmp::min(
u16::from_le_bytes([value[6], value[7]]).saturating_sub(4),
500,
),
u16::try_from(value.len().saturating_sub(8)).unwrap_or_default(),
);
let read_bytes = 8 + character_len;
let mut final_buffer = Vec::new();
let mut current_string_data = Vec::new();
let mut my_iter = value
.iter()
.skip(8)
.take(usize::from(character_len))
.peekable();
while let Some(byte) = my_iter.next() {
if *byte == 0xFF {
let next_byte = my_iter.peek().map(|b| **b).unwrap_or_default();
if (251..=254).contains(&next_byte) {
_ = my_iter.next();
_ = my_iter.next();
} else if next_byte == 247 {
if !current_string_data.is_empty() {
final_buffer.push(SdioControlTelnetData::StringData(Bytes::from(
std::mem::take(&mut current_string_data),
)));
}
final_buffer.push(SdioControlTelnetData::Backspace);
} else if next_byte == 248 {
if !current_string_data.is_empty() {
final_buffer.push(SdioControlTelnetData::StringData(Bytes::from(
std::mem::take(&mut current_string_data),
)));
}
final_buffer.push(SdioControlTelnetData::ClearLine);
} else {
_ = my_iter.next();
}
continue;
} else if *byte == 0x00 {
break;
}
current_string_data.push(*byte);
}
if !current_string_data.is_empty() {
final_buffer.push(SdioControlTelnetData::StringData(Bytes::from(
std::mem::take(&mut current_string_data),
)));
}
Ok(Self {
buff: final_buffer,
read_amount: usize::from(read_bytes),
telnet_channel,
})
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct SdioControlMessageRequest {
messages: Vec<SdioControlTelnetMessage>,
}
impl SdioControlMessageRequest {
#[must_use]
pub const fn new(messages: Vec<SdioControlTelnetMessage>) -> Self {
Self { messages }
}
#[must_use]
pub const fn messages(&self) -> &Vec<SdioControlTelnetMessage> {
&self.messages
}
#[must_use]
pub fn messages_owned(self) -> Vec<SdioControlTelnetMessage> {
self.messages
}
}
impl TryFrom<&SdioControlMessageRequest> for Bytes {
type Error = NetworkParseError;
#[allow(
// We will actually loop in the future.
clippy::never_loop,
)]
fn try_from(value: &SdioControlMessageRequest) -> Result<Self, Self::Error> {
let mut inner_size = 3_usize;
let mut request_body = BytesMut::with_capacity(500);
for msg in value.messages() {
let value = Bytes::from(msg);
inner_size += value.len();
request_body.extend(value);
}
let pad_amount = 500_usize.saturating_sub(request_body.len());
request_body.extend(BytesMut::zeroed(pad_amount));
if request_body.len() > 500 {
return Err(NetworkParseError::UnexpectedTrailer(
"SdioControlMessageRequest",
request_body.freeze().slice(500..),
));
}
let mut final_buff = BytesMut::with_capacity(512);
final_buff.put_u16_le(8);
final_buff.put_u16_le(u16::try_from(inner_size).unwrap_or(u16::MAX));
final_buff.extend(request_body);
final_buff.extend(BytesMut::zeroed(512_usize.saturating_sub(final_buff.len())));
Ok(final_buff.freeze())
}
}
impl TryFrom<SdioControlMessageRequest> for Bytes {
type Error = NetworkParseError;
fn try_from(value: SdioControlMessageRequest) -> Result<Self, Self::Error> {
Self::try_from(&value)
}
}
impl TryFrom<Bytes> for SdioControlMessageRequest {
type Error = NetworkParseError;
fn try_from(mut value: Bytes) -> Result<Self, Self::Error> {
if value.len() != 512 {
return Err(SdioProtocolError::PrintfInvalidSize(value.len()).into());
}
let packet_type = value.get_u16_le();
if packet_type != 8 {
return Err(SdioProtocolError::UnknownPrintfPacketType(packet_type).into());
}
let total_character_length = usize::from(value.get_u16_le());
let mut messages = Vec::with_capacity(1);
let mut read_amount = 4_usize;
while read_amount < total_character_length {
let actual_msg = SdioControlTelnetMessage::try_from(value.clone())?;
read_amount += actual_msg.read_amount;
value.advance(actual_msg.read_amount);
messages.push(actual_msg);
}
Ok(Self { messages })
}
}
const MESSAGE_REQUEST_FIELDS: &[NamedField<'static>] = &[NamedField::new("messages")];
impl Structable for SdioControlMessageRequest {
fn definition(&self) -> StructDef<'_> {
StructDef::new_static(
"SdioControlMessageRequest",
Fields::Named(MESSAGE_REQUEST_FIELDS),
)
}
}
impl Valuable for SdioControlMessageRequest {
fn as_value(&self) -> Value<'_> {
Value::Structable(self)
}
fn visit(&self, visitor: &mut dyn Visit) {
visitor.visit_named_fields(&NamedValues::new(
MESSAGE_REQUEST_FIELDS,
&[Valuable::as_value(&self.messages)],
));
}
}
#[cfg(test)]
mod unit_tests {
use super::*;
#[test]
pub fn serdeser_real_life_message_request() {
{
let message_request = SdioControlMessageRequest::try_from(Bytes::from(vec![
0x08, 0x00, 0x3a, 0x00, 0x04, 0x00, 0x0c, 0x00, 0xff, 0xff, 0x3a, 0x00, 0x42, 0x4f,
0x4f, 0x54, 0x31, 0x3a, 0x20, 0x52, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x44,
0x55, 0x41, 0x4c, 0x20, 0x62, 0x6f, 0x6f, 0x74, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x72,
0x20, 0x61, 0x74, 0x20, 0x30, 0x78, 0x30, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x2e, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x08, 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04,
0x18, 0x00, 0x00, 0x40, 0x00, 0x00, 0x20, 0x00, 0x02, 0x00, 0x10, 0x00, 0x00, 0x00,
0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x10, 0x80, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x00, 0x40, 0x00, 0x04,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x10, 0x82,
0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x40, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x04, 0x00, 0x00, 0x20, 0x00, 0x80, 0x00, 0x01, 0x08, 0x00, 0x00, 0x20, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0xc0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
]))
.expect("Failed to parse real life SDIO Control Message Request");
assert_eq!(
message_request.messages(),
&vec![
SdioControlTelnetMessage::new_with_read_amount(
"BOOT1: Running DUAL bootloader at 0x08000000.\n".to_owned(),
0x3E,
SdioControlTelnetChannel::SysConfigTool,
)
.expect("Failed to create telnet message to match against!")
],
);
assert_eq!(
Bytes::try_from(&message_request)
.expect("Failed to serialize real message request!"),
Bytes::from(vec![
0x08, 0x00, 0x3a, 0x00, 0x04, 0x00, 0x0c, 0x00, 0xff, 0xff, 0x3a, 0x00, 0x42,
0x4f, 0x4f, 0x54, 0x31, 0x3a, 0x20, 0x52, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67,
0x20, 0x44, 0x55, 0x41, 0x4c, 0x20, 0x62, 0x6f, 0x6f, 0x74, 0x6c, 0x6f, 0x61,
0x64, 0x65, 0x72, 0x20, 0x61, 0x74, 0x20, 0x30, 0x78, 0x30, 0x38, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x2e, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
]),
);
}
}
#[test]
pub fn roundtrip_telnet_channel_type() {
for channel_ty in vec![
SdioControlTelnetChannel::SysConfigTool,
SdioControlTelnetChannel::DevkitMsg,
SdioControlTelnetChannel::CafeOS,
] {
assert_eq!(
Ok(channel_ty),
SdioControlTelnetChannel::try_from(u16::from(channel_ty)),
"Round-tripped telnet channel type was not the same?"
);
}
}
}