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 SdioControlReadRequest {
lba: u32,
blocks: u32,
channel: u32,
}
impl SdioControlReadRequest {
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 SdioControlReadRequest {
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 != 0 {
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 TryFrom<&SdioControlReadRequest> for Bytes {
type Error = SdioProtocolError;
fn try_from(value: &SdioControlReadRequest) -> Result<Self, Self::Error> {
let channel_first_byte = value.channel.to_le_bytes()[0];
if channel_first_byte >= 0xC {
return Err(SdioProtocolError::PrintfInvalidChannel(
channel_first_byte,
value.channel,
));
}
let mut serialized = BytesMut::with_capacity(512);
serialized.put_u32_le(0);
serialized.put_u32_le(value.lba);
serialized.put_u32_le(value.blocks);
serialized.put_u32_le(value.channel);
serialized.extend_from_slice(&[0; 0x1F0]);
Ok(serialized.freeze())
}
}
impl TryFrom<SdioControlReadRequest> for Bytes {
type Error = SdioProtocolError;
fn try_from(value: SdioControlReadRequest) -> Result<Self, Self::Error> {
Self::try_from(&value)
}
}
const CONTROL_READ_REQUEST_FIELDS: &[NamedField<'static>] = &[
NamedField::new("raw_lba"),
NamedField::new("blocks"),
NamedField::new("channel"),
];
impl Structable for SdioControlReadRequest {
fn definition(&self) -> StructDef<'_> {
StructDef::new_static(
"SdioControlReadRequest",
Fields::Named(CONTROL_READ_REQUEST_FIELDS),
)
}
}
impl Valuable for SdioControlReadRequest {
fn as_value(&self) -> Value<'_> {
Value::Structable(self)
}
fn visit(&self, visitor: &mut dyn Visit) {
visitor.visit_named_fields(&NamedValues::new(
CONTROL_READ_REQUEST_FIELDS,
&[
Valuable::as_value(&self.lba),
Valuable::as_value(&self.blocks),
Valuable::as_value(&self.channel),
],
));
}
}
#[cfg(test)]
mod unit_tests {
use super::*;
#[test]
pub fn parse_read_request() {
{
let read_request = SdioControlReadRequest::try_from(Bytes::from(vec![
0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0x7f, 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, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]))
.expect("Failed to parse real life SDIO Control Read Request");
assert_eq!(
read_request.lba(),
0xFFFF_0000,
"Failed to parse correct address to read from on a real life read request.",
);
assert_eq!(
read_request.blocks(),
2,
"Failed to parse correct amount of blocks to read from real life read request.",
);
assert_eq!(
read_request.channel(),
0,
"Failed to parse the correct channel to read from on a real life read request.",
);
}
}
}