use core::str;
use alloc::vec::Vec;
use crate::{
Decode, DecodeError, DecodeWithLength, Encode, FixedString, Version,
cdc::{CdcReplyPacket, cmds::USER_CDC},
cdc2::{
Cdc2Ack, Cdc2CommandPacket, Cdc2ReplyPacket,
ecmds::{
FILE_CLEANUP, FILE_CTRL, FILE_DIR, FILE_DIR_ENTRY, FILE_ERASE, FILE_EXIT, FILE_FORMAT,
FILE_GET_INFO, FILE_INIT, FILE_LINK, FILE_LOAD, FILE_READ, FILE_SET_INFO,
FILE_USER_STAT, FILE_WRITE,
},
},
decode::DecodeErrorKind,
};
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[repr(u8)]
pub enum FileTransferOperation {
Write = 1,
Read = 2,
}
#[repr(u8)]
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum FileInitOption {
None = 0,
Overwrite = 1,
}
#[repr(u8)]
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum FileTransferTarget {
Ddr = 0,
Qspi = 1,
Cbuf = 2,
Vbuf = 3,
Ddrc = 4,
Ddre = 5,
Flash = 6,
Radio = 7,
A1 = 13,
B1 = 14,
B2 = 15,
}
#[repr(u8)]
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum FileVendor {
User = 1,
Sys = 15,
Dev1 = 16,
Dev2 = 24,
Dev3 = 32,
Dev4 = 40,
Dev5 = 48,
Dev6 = 56,
VexVm = 64,
Vex = 240,
Undefined = 241,
}
impl Decode for FileVendor {
fn decode(data: &mut &[u8]) -> Result<Self, DecodeError> {
match u8::decode(data)? {
1 => Ok(Self::User),
15 => Ok(Self::Sys),
16 => Ok(Self::Dev1),
24 => Ok(Self::Dev2),
32 => Ok(Self::Dev3),
40 => Ok(Self::Dev4),
48 => Ok(Self::Dev5),
56 => Ok(Self::Dev6),
64 => Ok(Self::VexVm),
240 => Ok(Self::Vex),
241 => Ok(Self::Undefined),
v => Err(DecodeError::new::<Self>(DecodeErrorKind::UnexpectedByte {
name: "FileVendor",
value: v,
expected: &[
0x01, 0x0F, 0x10, 0x18, 0x20, 0x28, 0x30, 0x38, 0x40, 0xF0, 0xF1,
],
})),
}
}
}
#[repr(u8)]
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum FileLoadAction {
Run = 0,
Stop = 128,
}
#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
#[repr(u8)]
pub enum ExtensionType {
#[default]
Binary = 0x0,
Vm = 0x61,
EncryptedBinary = 0x73,
}
impl Decode for ExtensionType {
fn decode(data: &mut &[u8]) -> Result<Self, DecodeError> {
Ok(match u8::decode(data)? {
0x0 => Self::Binary,
0x61 => Self::Vm,
0x73 => Self::EncryptedBinary,
unknown => {
return Err(DecodeError::new::<Self>(DecodeErrorKind::UnexpectedByte {
name: "ExtensionType",
value: unknown,
expected: &[0x0],
}));
}
})
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct FileMetadata {
pub extension: FixedString<3>,
pub extension_type: ExtensionType,
pub timestamp: i32,
pub version: Version,
}
impl Encode for FileMetadata {
fn size(&self) -> usize {
12
}
fn encode(&self, data: &mut [u8]) {
data[..self.extension.len()].copy_from_slice(self.extension.as_bytes());
data[3] = self.extension_type as _;
self.timestamp.encode(&mut data[4..]);
self.version.encode(&mut data[8..]);
}
}
impl Decode for FileMetadata {
fn decode(data: &mut &[u8]) -> Result<Self, DecodeError> {
Ok(Self {
extension: unsafe {
FixedString::new_unchecked(
str::from_utf8(&<[u8; 3]>::decode(data)?)
.map_err(|e| DecodeError::new::<Self>(e.into()))?,
)
},
extension_type: Decode::decode(data).unwrap(),
timestamp: i32::decode(data)?,
version: Version::decode(data)?,
})
}
}
pub type FileTransferInitializePacket =
Cdc2CommandPacket<USER_CDC, FILE_INIT, FileTransferInitializePayload>;
pub type FileTransferInitializeReplyPacket =
Cdc2ReplyPacket<USER_CDC, FILE_INIT, FileTransferInitializeReplyPayload>;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct FileTransferInitializePayload {
pub operation: FileTransferOperation,
pub target: FileTransferTarget,
pub vendor: FileVendor,
pub options: FileInitOption,
pub file_size: u32,
pub load_address: u32,
pub write_file_crc: u32,
pub metadata: FileMetadata,
pub file_name: FixedString<23>,
}
impl Encode for FileTransferInitializePayload {
fn size(&self) -> usize {
28 + self.file_name.size()
}
fn encode(&self, data: &mut [u8]) {
[
self.operation as u8,
self.target as u8,
self.vendor as u8,
self.options as u8,
]
.encode(data);
self.file_size.encode(&mut data[4..]);
self.load_address.encode(&mut data[8..]);
self.write_file_crc.encode(&mut data[12..]);
self.metadata.encode(&mut data[16..]);
self.file_name.encode(&mut data[28..]);
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct FileTransferInitializeReplyPayload {
pub window_size: u16,
pub file_size: u32,
pub file_crc: u32,
}
impl Decode for FileTransferInitializeReplyPayload {
fn decode(data: &mut &[u8]) -> Result<Self, DecodeError> {
let window_size = u16::decode(data)?;
let file_size = u32::decode(data)?;
let file_crc = u32::decode(data)?.swap_bytes();
Ok(Self {
window_size,
file_size,
file_crc,
})
}
}
pub type FileTransferExitPacket = Cdc2CommandPacket<USER_CDC, FILE_EXIT, FileExitAction>;
pub type FileTransferExitReplyPacket = Cdc2ReplyPacket<USER_CDC, FILE_EXIT, ()>;
#[repr(u8)]
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum FileExitAction {
DoNothing = 0,
RunProgram = 1,
Halt = 2,
ShowRunScreen = 3,
}
impl Encode for FileExitAction {
fn size(&self) -> usize {
1
}
fn encode(&self, data: &mut [u8]) {
data[0] = *self as _;
}
}
pub type FileDataWritePacket = Cdc2CommandPacket<USER_CDC, FILE_WRITE, FileDataWritePayload>;
pub type FileDataWriteReplyPacket = Cdc2ReplyPacket<USER_CDC, FILE_WRITE, ()>;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct FileDataWritePayload {
pub address: i32,
pub chunk_data: Vec<u8>,
}
impl Encode for FileDataWritePayload {
fn size(&self) -> usize {
4 + self.chunk_data.len()
}
fn encode(&self, data: &mut [u8]) {
self.address.encode(data);
self.chunk_data.encode(&mut data[4..]);
}
}
pub type FileDataReadPacket = Cdc2CommandPacket<USER_CDC, FILE_READ, FileDataReadPayload>;
pub type FileDataReadReplyPacket = CdcReplyPacket<USER_CDC, FileDataReadReplyPayload>;
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct FileDataReadPayload {
pub address: u32,
pub size: u16,
}
impl Encode for FileDataReadPayload {
fn size(&self) -> usize {
6
}
fn encode(&self, data: &mut [u8]) {
self.address.encode(data);
self.size.encode(&mut data[4..]);
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum FileDataReadReplyContents {
Ack { address: u32, data: Vec<u8> },
Nack(Cdc2Ack),
}
impl Decode for FileDataReadReplyContents {
fn decode(data: &mut &[u8]) -> Result<Self, DecodeError> {
if data.len() == 1 {
Ok(Self::Nack(Cdc2Ack::decode(data)?))
} else {
let address = u32::decode(data)?;
let chunk_data = Vec::decode_with_len(data, data.len())?;
Ok(Self::Ack {
address,
data: chunk_data,
})
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct FileDataReadReplyPayload {
pub contents: FileDataReadReplyContents,
pub crc: u16,
}
impl Decode for FileDataReadReplyPayload {
fn decode(data: &mut &[u8]) -> Result<Self, DecodeError> {
let ecmd = u8::decode(data)?;
if ecmd != FILE_READ {
return Err(DecodeError::new::<Self>(DecodeErrorKind::UnexpectedByte {
name: "ecmd",
value: ecmd,
expected: &[FILE_READ],
}));
}
let contents = FileDataReadReplyContents::decode(
&mut data
.get(..data.len() - 2)
.ok_or_else(|| DecodeError::new::<Self>(DecodeErrorKind::UnexpectedEnd))?,
)?;
*data = &data[data.len() - 2..];
let crc = u16::decode(data)?.swap_bytes();
Ok(Self { contents, crc })
}
}
impl FileDataReadReplyPayload {
pub fn unwrap(self) -> Result<(u32, Vec<u8>), Cdc2Ack> {
match self.contents {
FileDataReadReplyContents::Ack { address, data } => Ok((address, data)),
FileDataReadReplyContents::Nack(nack) => Err(nack),
}
}
}
pub type FileLinkPacket = Cdc2CommandPacket<USER_CDC, FILE_LINK, FileLinkPayload>;
pub type FileLinkReplyPacket = Cdc2ReplyPacket<USER_CDC, FILE_LINK, ()>;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct FileLinkPayload {
pub vendor: FileVendor,
pub reserved: u8,
pub required_file: FixedString<23>,
}
impl Encode for FileLinkPayload {
fn size(&self) -> usize {
2 + self.required_file.size()
}
fn encode(&self, data: &mut [u8]) {
data[0] = self.vendor as _;
data[1] = self.reserved;
self.required_file.encode(&mut data[2..]);
}
}
pub type DirectoryFileCountPacket =
Cdc2CommandPacket<USER_CDC, FILE_DIR, DirectoryFileCountPayload>;
pub type DirectoryFileCountReplyPacket = Cdc2ReplyPacket<USER_CDC, FILE_DIR, u16>;
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct DirectoryFileCountPayload {
pub vendor: FileVendor,
pub reserved: u8,
}
impl Encode for DirectoryFileCountPayload {
fn size(&self) -> usize {
2
}
fn encode(&self, data: &mut [u8]) {
data[0] = self.vendor as _;
data[1] = self.reserved;
}
}
pub type DirectoryEntryPacket = Cdc2CommandPacket<USER_CDC, FILE_DIR_ENTRY, DirectoryEntryPayload>;
pub type DirectoryEntryReplyPacket =
Cdc2ReplyPacket<USER_CDC, FILE_DIR_ENTRY, DirectoryEntryReplyPayload>;
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct DirectoryEntryPayload {
pub file_index: u8,
pub reserved: u8,
}
impl Encode for DirectoryEntryPayload {
fn size(&self) -> usize {
2
}
fn encode(&self, data: &mut [u8]) {
data[0] = self.file_index;
data[1] = self.reserved;
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DirectoryEntryReplyPayload {
pub file_index: u8,
pub size: u32,
pub load_address: u32,
pub crc: u32,
pub metadata: Option<FileMetadata>,
pub file_name: FixedString<23>,
}
impl Decode for DirectoryEntryReplyPayload {
fn decode(data: &mut &[u8]) -> Result<Self, DecodeError> {
let file_index = u8::decode(data)?;
let size = u32::decode(data)?;
let load_address = u32::decode(data)?;
let crc = u32::decode(data)?;
let metadata = if data.get(0) == Some(&255) {
let _ = <[u8; 12]>::decode(data);
None
} else {
Some(FileMetadata::decode(data)?)
};
let file_name = FixedString::<23>::decode(data)?;
Ok(Self {
file_index,
size,
load_address,
crc,
metadata,
file_name,
})
}
}
pub type FileLoadActionPacket = Cdc2CommandPacket<USER_CDC, FILE_LOAD, FileLoadActionPayload>;
pub type FileLoadActionReplyPacket = Cdc2ReplyPacket<USER_CDC, FILE_LOAD, ()>;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct FileLoadActionPayload {
pub vendor: FileVendor,
pub action: FileLoadAction,
pub file_name: FixedString<23>,
}
impl Encode for FileLoadActionPayload {
fn size(&self) -> usize {
2 + self.file_name.size()
}
fn encode(&self, data: &mut [u8]) {
data[0] = self.vendor as _;
data[1] = self.action as _;
self.file_name.encode(&mut data[2..]);
}
}
pub type FileMetadataPacket = Cdc2CommandPacket<USER_CDC, FILE_GET_INFO, FileMetadataPayload>;
pub type FileMetadataReplyPacket =
Cdc2ReplyPacket<USER_CDC, FILE_GET_INFO, Option<FileMetadataReplyPayload>>;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct FileMetadataPayload {
pub vendor: FileVendor,
pub reserved: u8,
pub file_name: FixedString<23>,
}
impl Encode for FileMetadataPayload {
fn size(&self) -> usize {
2 + self.file_name.size()
}
fn encode(&self, data: &mut [u8]) {
data[0] = self.vendor as _;
data[1] = self.reserved as _;
self.file_name.encode(&mut data[2..]);
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct FileMetadataReplyPayload {
pub linked_vendor: Option<FileVendor>,
pub size: u32,
pub load_address: u32,
pub crc32: u32,
pub metadata: FileMetadata,
}
impl Decode for Option<FileMetadataReplyPayload> {
fn decode(data: &mut &[u8]) -> Result<Self, DecodeError> {
let maybe_vid = u8::decode(data).unwrap();
let linked_vendor = match maybe_vid {
0 => None,
255 => return Ok(None),
vid => Some(FileVendor::decode(&mut [vid].as_slice())?),
};
let size = u32::decode(data)?;
if size == 0xFFFFFFFF {
return Ok(None);
}
let load_address = u32::decode(data)?;
let crc32 = u32::decode(data)?;
let metadata = FileMetadata::decode(data)?;
Ok(Some(FileMetadataReplyPayload {
linked_vendor,
size,
load_address,
crc32,
metadata,
}))
}
}
pub type FileMetadataSetPacket = Cdc2CommandPacket<USER_CDC, FILE_SET_INFO, FileMetadataSetPayload>;
pub type FileMetadataSetReplyPacket = Cdc2ReplyPacket<USER_CDC, FILE_SET_INFO, ()>;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct FileMetadataSetPayload {
pub vendor: FileVendor,
pub options: u8,
pub load_address: u32,
pub metadata: FileMetadata,
pub file_name: FixedString<23>,
}
impl Encode for FileMetadataSetPayload {
fn size(&self) -> usize {
18 + self.file_name.size()
}
fn encode(&self, data: &mut [u8]) {
data[0] = self.vendor as _;
data[1] = self.options as _;
self.load_address.encode(&mut data[2..]);
self.metadata.encode(&mut data[6..]);
self.file_name.encode(&mut data[18..]);
}
}
pub type FileErasePacket = Cdc2CommandPacket<USER_CDC, FILE_ERASE, FileErasePayload>;
pub type FileEraseReplyPacket = Cdc2ReplyPacket<USER_CDC, FILE_ERASE, ()>;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct FileErasePayload {
pub vendor: FileVendor,
pub reserved: u8,
pub file_name: FixedString<23>,
}
impl Encode for FileErasePayload {
fn size(&self) -> usize {
2 + self.file_name.size()
}
fn encode(&self, data: &mut [u8]) {
data[0] = self.vendor as _;
data[1] = self.reserved as _;
self.file_name.encode(&mut data[2..]);
}
}
pub type FileCleanUpPacket = Cdc2CommandPacket<USER_CDC, FILE_CLEANUP, ()>;
pub type FileCleanUpReplyPacket = Cdc2ReplyPacket<USER_CDC, FILE_CLEANUP, FileCleanUpReplyPayload>;
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct FileCleanUpReplyPayload {
count: u16,
}
impl Decode for FileCleanUpReplyPayload {
fn decode(data: &mut &[u8]) -> Result<Self, DecodeError> {
Ok(Self {
count: Decode::decode(data)?,
})
}
}
pub type FileFormatPacket = Cdc2CommandPacket<USER_CDC, FILE_FORMAT, FileFormatConfirmation>;
pub type FileFormatReplyPacket = Cdc2CommandPacket<USER_CDC, FILE_FORMAT, ()>;
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct FileFormatConfirmation {
pub confirmation_code: [u8; 4],
}
impl FileFormatConfirmation {
pub const FORMAT_CODE: [u8; 4] = [0x44, 0x43, 0x42, 0x41];
pub const fn new() -> Self {
Self {
confirmation_code: Self::FORMAT_CODE,
}
}
}
impl Default for FileFormatConfirmation {
fn default() -> Self {
Self::new()
}
}
impl Encode for FileFormatConfirmation {
fn size(&self) -> usize {
4
}
fn encode(&self, data: &mut [u8]) {
self.confirmation_code.encode(data)
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum FileControlGroup {
Radio(RadioChannel),
}
impl Encode for FileControlGroup {
fn size(&self) -> usize {
if matches!(self, Self::Radio(_)) { 2 } else { 0 }
}
fn encode(&self, data: &mut [u8]) {
#[allow(irrefutable_let_patterns)] if let Self::Radio(channel) = self {
data[0] = 0x01;
data[1] = *channel as _;
}
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[repr(u8)]
pub enum RadioChannel {
Pit = 0x00,
Download = 0x01,
}
pub type FileControlPacket = Cdc2CommandPacket<USER_CDC, FILE_CTRL, FileControlGroup>;
pub type FileControlReplyPacket = Cdc2ReplyPacket<USER_CDC, FILE_CTRL, ()>;
pub type ProgramStatusPacket = Cdc2CommandPacket<USER_CDC, FILE_USER_STAT, ProgramStatusPayload>;
pub type ProgramStatusReplyPacket =
Cdc2ReplyPacket<USER_CDC, FILE_USER_STAT, ProgramStatusReplyPayload>;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct ProgramStatusPayload {
pub vendor: FileVendor,
pub reserved: u8,
pub file_name: FixedString<23>,
}
impl Encode for ProgramStatusPayload {
fn size(&self) -> usize {
2 + self.file_name.size()
}
fn encode(&self, data: &mut [u8]) {
data[0] = self.vendor as _;
data[1] = self.reserved;
self.file_name.encode(&mut data[2..]);
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct ProgramStatusReplyPayload {
pub slot: u8,
pub requested_slot: u8,
}