use crate::fsemul::sdio::{
errors::{SdioApiError, SdioProtocolError},
proto::SDIO_BLOCK_SIZE_AS_U32,
};
use bytes::{Buf, BufMut, Bytes, BytesMut};
use valuable::{Fields, NamedField, NamedValues, StructDef, Structable, Valuable, Value, Visit};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SdioControlWriteRequest {
lba: u32,
blocks: u32,
channel: u32,
}
impl SdioControlWriteRequest {
pub fn new(lba: u32, blocks: u32, channel: u32) -> Result<Self, SdioApiError> {
if lba < SDIO_BLOCK_SIZE_AS_U32 || !lba.is_multiple_of(SDIO_BLOCK_SIZE_AS_U32) {
return Err(SdioApiError::InvalidLBA(lba));
}
let as_bytes = channel.to_le_bytes();
if as_bytes[0] >= 0xC {
return Err(SdioApiError::InvalidChannel(as_bytes[0], channel));
}
Ok(Self {
lba: lba / SDIO_BLOCK_SIZE_AS_U32,
blocks,
channel,
})
}
pub fn new_with_raw_lba(raw_lba: u32, blocks: u32, channel: u32) -> Result<Self, SdioApiError> {
let as_bytes = channel.to_le_bytes();
if as_bytes[0] >= 0xC {
return Err(SdioApiError::InvalidChannel(as_bytes[0], channel));
}
Ok(Self {
lba: raw_lba,
blocks,
channel,
})
}
#[must_use]
pub const fn lba(&self) -> u32 {
self.lba * SDIO_BLOCK_SIZE_AS_U32
}
pub fn set_raw_lba(&mut self, new_lba: u32) {
self.lba = new_lba;
}
pub fn set_lba(&mut self, new_lba: u32) -> Result<(), SdioApiError> {
if new_lba < SDIO_BLOCK_SIZE_AS_U32 || !new_lba.is_multiple_of(SDIO_BLOCK_SIZE_AS_U32) {
return Err(SdioApiError::InvalidLBA(new_lba));
}
self.lba = new_lba / SDIO_BLOCK_SIZE_AS_U32;
Ok(())
}
#[must_use]
pub const fn blocks(&self) -> u32 {
self.blocks
}
pub fn set_blocks(&mut self, new_blocks: u32) {
self.blocks = new_blocks;
}
#[must_use]
pub const fn channel(&self) -> u32 {
self.channel
}
pub fn set_channel(&mut self, new_channel: u32) -> Result<(), SdioApiError> {
let as_bytes = new_channel.to_le_bytes();
if as_bytes[0] >= 0xC {
return Err(SdioApiError::InvalidChannel(as_bytes[0], new_channel));
}
self.channel = new_channel;
Ok(())
}
}
impl TryFrom<Bytes> for SdioControlWriteRequest {
type Error = SdioProtocolError;
fn try_from(mut value: Bytes) -> Result<Self, Self::Error> {
if value.len() != 512 {
return Err(SdioProtocolError::PrintfInvalidSize(value.len()));
}
let packet_type = value.get_u16_le();
if packet_type != 1 {
return Err(SdioProtocolError::UnknownPrintfPacketType(packet_type));
}
_ = value.get_u16_le();
let lba = value.get_u32_le();
let blocks = value.get_u32_le();
if value[0] >= 0xC {
return Err(SdioProtocolError::PrintfInvalidChannel(
value[0],
u32::from_le_bytes([value[0], value[1], value[2], value[3]]),
));
}
let channel = value.get_u32_le();
Ok(Self {
lba,
blocks,
channel,
})
}
}
impl From<&SdioControlWriteRequest> for Bytes {
fn from(value: &SdioControlWriteRequest) -> Self {
let mut serialized = BytesMut::with_capacity(512);
serialized.put_u32_le(1);
serialized.put_u32_le(value.lba);
serialized.put_u32_le(value.blocks);
serialized.put_u32_le(value.channel);
serialized.extend_from_slice(&[0; 0x1F0]);
serialized.freeze()
}
}
impl From<SdioControlWriteRequest> for Bytes {
fn from(value: SdioControlWriteRequest) -> Self {
Self::from(&value)
}
}
const CONTROL_WRITE_REQUEST_FIELDS: &[NamedField<'static>] = &[
NamedField::new("raw_lba"),
NamedField::new("blocks"),
NamedField::new("channel"),
];
impl Structable for SdioControlWriteRequest {
fn definition(&self) -> StructDef<'_> {
StructDef::new_static(
"SdioControlReadRequest",
Fields::Named(CONTROL_WRITE_REQUEST_FIELDS),
)
}
}
impl Valuable for SdioControlWriteRequest {
fn as_value(&self) -> Value<'_> {
Value::Structable(self)
}
fn visit(&self, visitor: &mut dyn Visit) {
visitor.visit_named_fields(&NamedValues::new(
CONTROL_WRITE_REQUEST_FIELDS,
&[
Valuable::as_value(&self.lba),
Valuable::as_value(&self.blocks),
Valuable::as_value(&self.channel),
],
));
}
}
#[cfg(test)]
mod unit_tests {
}