cat_dev/fsemul/pcfs/sata_proto/
ping.rs

1//! Definitions, and handlers for the 'PING' packet type.
2//!
3//! Ping packets are much like ping packet types in any sort of scenario, they
4//! are built to check availability. Not to mention ping packets confer what
5//! features are enabled.
6
7use crate::{
8	errors::NetworkParseError,
9	fsemul::pcfs::{
10		errors::PCFSApiError,
11		sata_proto::{construct_sata_response, SataCommandInfo, SataPacketHeader},
12	},
13};
14use bytes::{BufMut, Bytes, BytesMut};
15use valuable::{Fields, NamedField, NamedValues, StructDef, Structable, Valuable, Value, Visit};
16
17/// A ZST that represents a ping packet coming in.
18#[derive(Clone, Debug, PartialEq, Eq, Valuable)]
19pub struct SataPingPacketBody;
20
21impl SataPingPacketBody {
22	/// Handle a ping packet.
23	///
24	/// ## Errors
25	///
26	/// Should never error, but could error if we fail to construct the response
27	/// for some reason.
28	pub fn handle(
29		&self,
30		request_header: &SataPacketHeader,
31		command_info: &SataCommandInfo,
32		server_and_client_supports_ffio: bool,
33		server_and_client_supports_csr: bool,
34	) -> Result<Bytes, PCFSApiError> {
35		let supports_ffio = command_info.capabilities.0 != 0 && server_and_client_supports_ffio;
36		let supports_csr = command_info.capabilities.0 != 0 && server_and_client_supports_csr;
37
38		construct_sata_response(
39			request_header,
40			0,
41			SataPongBody::new(supports_ffio, supports_csr),
42		)
43	}
44}
45
46impl TryFrom<Bytes> for SataPingPacketBody {
47	type Error = NetworkParseError;
48
49	fn try_from(value: Bytes) -> Result<Self, Self::Error> {
50		if !value.is_empty() {
51			return Err(NetworkParseError::UnexpectedTrailer(
52				"SataPingPacketBody",
53				value,
54			));
55		}
56
57		Ok(Self)
58	}
59}
60
61/// A response to a `PING` over the Sata protocol of `PCFS`.
62#[derive(Clone, Debug, PartialEq, Eq)]
63pub struct SataPongBody {
64	/// If Fast File I/O is enabled and supported by both sides.
65	fast_file_io_enabled: bool,
66	/// If Combined Send/Recv is enabled and supported by both sides.
67	combined_send_recv_enabled: bool,
68}
69
70impl SataPongBody {
71	#[must_use]
72	pub const fn new(ffio_enabled: bool, csr_enabled: bool) -> Self {
73		Self {
74			fast_file_io_enabled: ffio_enabled,
75			combined_send_recv_enabled: csr_enabled,
76		}
77	}
78
79	#[must_use]
80	pub const fn ffio_enabled(&self) -> bool {
81		self.fast_file_io_enabled
82	}
83
84	pub const fn set_ffio_enabled(&mut self, enabled: bool) {
85		self.fast_file_io_enabled = enabled;
86	}
87
88	#[must_use]
89	pub const fn combined_send_recv_enabled(&self) -> bool {
90		self.combined_send_recv_enabled
91	}
92
93	pub const fn set_combined_send_recv_enabled(&mut self, enabled: bool) {
94		self.combined_send_recv_enabled = enabled;
95	}
96}
97
98impl From<SataPongBody> for Bytes {
99	fn from(value: SataPongBody) -> Self {
100		let mut buff = BytesMut::with_capacity(8);
101
102		buff.put_u32(0x0); // Success! - This is a return code.
103		buff.put_u32(
104			match (value.fast_file_io_enabled, value.combined_send_recv_enabled) {
105				(true, true) => 0xCAFE_0003,
106				(true, false) => 0xCAFE_0001,
107				(false, true) => 0xCAFE_0002,
108				(false, false) => 0x0000_0000,
109			},
110		);
111
112		buff.freeze()
113	}
114}
115
116const SATA_PONG_BODY_FIELDS: &[NamedField<'static>] = &[
117	NamedField::new("fast_file_io_enabled"),
118	NamedField::new("combined_send_recv_enabled"),
119];
120
121impl Structable for SataPongBody {
122	fn definition(&self) -> StructDef<'_> {
123		StructDef::new_static("SataPongBody", Fields::Named(SATA_PONG_BODY_FIELDS))
124	}
125}
126
127impl Valuable for SataPongBody {
128	fn as_value(&self) -> Value<'_> {
129		Value::Structable(self)
130	}
131
132	fn visit(&self, visitor: &mut dyn Visit) {
133		visitor.visit_named_fields(&NamedValues::new(
134			SATA_PONG_BODY_FIELDS,
135			&[
136				Valuable::as_value(&self.fast_file_io_enabled),
137				Valuable::as_value(&self.combined_send_recv_enabled),
138			],
139		));
140	}
141}
142
143#[cfg(test)]
144mod unit_tests {
145	use super::*;
146
147	#[test]
148	pub fn can_respond_to_ping() {
149		let ping = SataPingPacketBody;
150
151		let example_ping_packet_header = SataPacketHeader {
152			packet_data_len: 0,
153			packet_id: 0,
154			flags: 0,
155			version: 0,
156			timestamp_on_host: 0,
157			pid_on_host: 0,
158		};
159		let example_command_info = SataCommandInfo {
160			user: (0, 0),
161			capabilities: (1, 0),
162			command: 0x14,
163		};
164
165		let all_supported = ping
166			.handle(
167				&example_ping_packet_header,
168				&example_command_info,
169				true,
170				true,
171			)
172			.expect("Failed to handle ping with all features enabled!");
173		let only_ffio_supported = ping
174			.handle(
175				&example_ping_packet_header,
176				&example_command_info,
177				true,
178				false,
179			)
180			.expect("Failed to handle ping with only ffio features enabled!");
181		let only_csr_supported = ping
182			.handle(
183				&example_ping_packet_header,
184				&example_command_info,
185				false,
186				true,
187			)
188			.expect("Failed to handle ping with only csr enabled!");
189		let none_supported = ping
190			.handle(
191				&example_ping_packet_header,
192				&example_command_info,
193				false,
194				false,
195			)
196			.expect("Failed to handle ping with only csr enabled!");
197
198		assert!(all_supported.ends_with(&[0xCA, 0xFE, 0x00, 0x03]));
199		assert!(only_ffio_supported.ends_with(&[0xCA, 0xFE, 0x00, 0x01]));
200		assert!(only_csr_supported.ends_with(&[0xCA, 0xFE, 0x00, 0x02]));
201		assert!(none_supported.ends_with(&[0x00, 0x00, 0x00, 0x00]));
202	}
203}