cat_dev/fsemul/sdio/proto/
write.rs

1//! An SDIO 'write' request to write data somewhere on the disk.
2
3use crate::fsemul::sdio::{
4	errors::{SdioApiError, SdioProtocolError},
5	proto::SDIO_BLOCK_SIZE_AS_U32,
6};
7use bytes::{Buf, BufMut, Bytes, BytesMut};
8use valuable::{Fields, NamedField, NamedValues, StructDef, Structable, Valuable, Value, Visit};
9
10/// Handle a Write Request coming over the SDIO Control port.
11#[derive(Clone, Debug, PartialEq, Eq)]
12pub struct SdioControlWriteRequest {
13	lba: u32,
14	blocks: u32,
15	channel: u32,
16}
17
18impl SdioControlWriteRequest {
19	/// Create a new SDIO Write Request to head to the `CONTROL` port.
20	///
21	/// This assumes you're passing in the LBA as it appears in the DLF file,
22	/// (e.g. divisble by [`SDIO_BLOCK_SIZE_AS_U32`]). If you want the raw LBA
23	/// you can call [`Self::new_with_raw_lba`].
24	///
25	/// ## Errors
26	///
27	/// - If the LBA address is not a possible block address (must be divisble by
28	///   [`SDIO_BLOCK_SIZE_AS_U32`]).
29	/// - If the channel's first byte is greater than or equal to `0xC` when
30	///   encoded to little endian.
31	pub fn new(lba: u32, blocks: u32, channel: u32) -> Result<Self, SdioApiError> {
32		if lba < SDIO_BLOCK_SIZE_AS_U32 || !lba.is_multiple_of(SDIO_BLOCK_SIZE_AS_U32) {
33			return Err(SdioApiError::InvalidLBA(lba));
34		}
35		let as_bytes = channel.to_le_bytes();
36		if as_bytes[0] >= 0xC {
37			return Err(SdioApiError::InvalidChannel(as_bytes[0], channel));
38		}
39
40		Ok(Self {
41			lba: lba / SDIO_BLOCK_SIZE_AS_U32,
42			blocks,
43			channel,
44		})
45	}
46
47	/// Create a new SDIO Write Request to head to the `CONTROL` port.
48	///
49	/// This assumes you're passing in the LBA as it appears on the network,
50	/// (e.g. not the number being divisble by 512).
51	///
52	/// ## Errors
53	///
54	/// - If the channel's first byte is greater than or equal to `0xC` when
55	///   encoded to little endian.
56	pub fn new_with_raw_lba(raw_lba: u32, blocks: u32, channel: u32) -> Result<Self, SdioApiError> {
57		let as_bytes = channel.to_le_bytes();
58		if as_bytes[0] >= 0xC {
59			return Err(SdioApiError::InvalidChannel(as_bytes[0], channel));
60		}
61
62		Ok(Self {
63			lba: raw_lba,
64			blocks,
65			channel,
66		})
67	}
68
69	/// Get the address to write to.
70	#[must_use]
71	pub const fn lba(&self) -> u32 {
72		self.lba * SDIO_BLOCK_SIZE_AS_U32
73	}
74
75	/// Set the "raw" address to write to, this is not in the same form as
76	/// `write_request.lba()`.
77	///
78	/// This is if we took the LBA returned by the LBA block method and divided
79	/// it by block size.
80	pub fn set_raw_lba(&mut self, new_lba: u32) {
81		self.lba = new_lba;
82	}
83
84	/// Set the address to write to.
85	///
86	/// ## Errors
87	///
88	/// - If the LBA address is not a possible block address (must be divisble by
89	///   [`SDIO_BLOCK_SIZE_AS_U32`]).
90	pub fn set_lba(&mut self, new_lba: u32) -> Result<(), SdioApiError> {
91		if new_lba < SDIO_BLOCK_SIZE_AS_U32 || !new_lba.is_multiple_of(SDIO_BLOCK_SIZE_AS_U32) {
92			return Err(SdioApiError::InvalidLBA(new_lba));
93		}
94
95		self.lba = new_lba / SDIO_BLOCK_SIZE_AS_U32;
96		Ok(())
97	}
98
99	/// Get the amount of blocks to read.
100	#[must_use]
101	pub const fn blocks(&self) -> u32 {
102		self.blocks
103	}
104
105	/// Set the amount of blocks to write to SDIO.
106	pub fn set_blocks(&mut self, new_blocks: u32) {
107		self.blocks = new_blocks;
108	}
109
110	/// Get the channel to read from.
111	#[must_use]
112	pub const fn channel(&self) -> u32 {
113		self.channel
114	}
115
116	/// Set the channel this write request is on.
117	///
118	/// ## Errors
119	///
120	/// - If the channel's first byte is greater than or equal to `0xC` when
121	///   encoded to little endian.
122	pub fn set_channel(&mut self, new_channel: u32) -> Result<(), SdioApiError> {
123		let as_bytes = new_channel.to_le_bytes();
124		if as_bytes[0] >= 0xC {
125			return Err(SdioApiError::InvalidChannel(as_bytes[0], new_channel));
126		}
127
128		self.channel = new_channel;
129		Ok(())
130	}
131}
132
133impl TryFrom<Bytes> for SdioControlWriteRequest {
134	type Error = SdioProtocolError;
135
136	fn try_from(mut value: Bytes) -> Result<Self, Self::Error> {
137		if value.len() != 512 {
138			return Err(SdioProtocolError::PrintfInvalidSize(value.len()));
139		}
140		let packet_type = value.get_u16_le();
141		if packet_type != 1 {
142			return Err(SdioProtocolError::UnknownPrintfPacketType(packet_type));
143		}
144		_ = value.get_u16_le();
145
146		let lba = value.get_u32_le();
147		let blocks = value.get_u32_le();
148		if value[0] >= 0xC {
149			return Err(SdioProtocolError::PrintfInvalidChannel(
150				value[0],
151				u32::from_le_bytes([value[0], value[1], value[2], value[3]]),
152			));
153		}
154		let channel = value.get_u32_le();
155
156		Ok(Self {
157			lba,
158			blocks,
159			channel,
160		})
161	}
162}
163
164impl From<&SdioControlWriteRequest> for Bytes {
165	fn from(value: &SdioControlWriteRequest) -> Self {
166		let mut serialized = BytesMut::with_capacity(512);
167		serialized.put_u32_le(1);
168		serialized.put_u32_le(value.lba);
169		serialized.put_u32_le(value.blocks);
170		serialized.put_u32_le(value.channel);
171		serialized.extend_from_slice(&[0; 0x1F0]);
172		serialized.freeze()
173	}
174}
175
176impl From<SdioControlWriteRequest> for Bytes {
177	fn from(value: SdioControlWriteRequest) -> Self {
178		Self::from(&value)
179	}
180}
181
182const CONTROL_WRITE_REQUEST_FIELDS: &[NamedField<'static>] = &[
183	NamedField::new("raw_lba"),
184	NamedField::new("blocks"),
185	NamedField::new("channel"),
186];
187
188impl Structable for SdioControlWriteRequest {
189	fn definition(&self) -> StructDef<'_> {
190		StructDef::new_static(
191			"SdioControlReadRequest",
192			Fields::Named(CONTROL_WRITE_REQUEST_FIELDS),
193		)
194	}
195}
196
197impl Valuable for SdioControlWriteRequest {
198	fn as_value(&self) -> Value<'_> {
199		Value::Structable(self)
200	}
201
202	fn visit(&self, visitor: &mut dyn Visit) {
203		visitor.visit_named_fields(&NamedValues::new(
204			CONTROL_WRITE_REQUEST_FIELDS,
205			&[
206				Valuable::as_value(&self.lba),
207				Valuable::as_value(&self.blocks),
208				Valuable::as_value(&self.channel),
209			],
210		));
211	}
212}
213
214#[cfg(test)]
215mod unit_tests {
216	// TODO(mythra): get a real life write request.
217}