1pub use vex_cdc as protocol;
4
5use std::{future::Future, time::Instant};
6
7use log::{error, trace, warn};
8use std::time::Duration;
9
10use vex_cdc::{
11 Decode, DecodeError, Encode, FixedStringSizeError, VarU16,
12 cdc::CdcReplyPacket,
13 cdc2::{Cdc2Ack, Cdc2ReplyPacket},
14};
15
16pub mod commands;
17
18use crate::commands::Command;
19
20#[cfg(feature = "bluetooth")]
21pub mod bluetooth;
22#[cfg(all(feature = "serial", feature = "bluetooth"))]
23pub mod generic;
24#[cfg(feature = "serial")]
25pub mod serial;
26
27pub trait CheckHeader {
28 fn has_valid_header(data: &[u8]) -> bool;
29}
30
31impl<const CMD: u8, const ECMD: u8, P: Decode> CheckHeader for Cdc2ReplyPacket<CMD, ECMD, P> {
32 fn has_valid_header(mut data: &[u8]) -> bool {
33 let data = &mut data;
34
35 if <[u8; 2] as Decode>::decode(data)
36 .map(|header| header != Self::HEADER)
37 .unwrap_or(true)
38 {
39 return false;
40 }
41
42 if u8::decode(data).map(|id| id != CMD).unwrap_or(true) {
43 return false;
44 }
45
46 let payload_size = VarU16::decode(data);
47 if payload_size.is_err() {
48 return false;
49 }
50
51 if u8::decode(data).map(|ecmd| ecmd != ECMD).unwrap_or(true) {
52 return false;
53 }
54
55 true
56 }
57}
58
59impl<const CMD: u8, P: Decode> CheckHeader for CdcReplyPacket<CMD, P> {
60 fn has_valid_header(data: &[u8]) -> bool {
61 let Some(data) = data.get(0..3) else {
62 return false;
63 };
64
65 data[0..2] == Self::HEADER && data[2] == CMD
66 }
67}
68
69#[derive(Debug, Clone, PartialEq, Eq)]
70pub(crate) struct RawPacket {
71 pub bytes: Vec<u8>,
72 pub used: bool,
73 pub timestamp: Instant,
74}
75impl RawPacket {
76 pub fn new(bytes: Vec<u8>) -> Self {
77 Self {
78 bytes,
79 used: false,
80 timestamp: Instant::now(),
81 }
82 }
83
84 pub fn is_obsolete(&self, timeout: Duration) -> bool {
85 self.timestamp.elapsed() > timeout || self.used
86 }
87
88 pub fn check_header<H: CheckHeader>(&self) -> bool {
89 H::has_valid_header(&self.bytes)
90 }
91
92 pub fn decode_and_use<D: Decode>(&mut self) -> Result<D, DecodeError> {
97 let decoded = D::decode(&mut self.bytes.as_slice())?;
98 self.used = true;
99 Ok(decoded)
100 }
101}
102pub(crate) fn trim_packets(packets: &mut Vec<RawPacket>) {
104 trace!("Trimming packets. Length before: {}", packets.len());
105
106 packets.retain(|packet| !packet.is_obsolete(Duration::from_secs(2)));
108
109 trace!("Trimmed packets. Length after: {}", packets.len());
110}
111
112#[allow(async_fn_in_trait)]
114pub trait Connection {
115 type Error: std::error::Error + From<DecodeError> + From<Cdc2Ack> + From<FixedStringSizeError>;
116
117 fn connection_type(&self) -> ConnectionType;
118
119 fn send(&mut self, packet: impl Encode) -> impl Future<Output = Result<(), Self::Error>>;
121
122 fn recv<P: Decode + CheckHeader>(
124 &mut self,
125 timeout: Duration,
126 ) -> impl Future<Output = Result<P, Self::Error>>;
127
128 fn read_user(&mut self, buf: &mut [u8]) -> impl Future<Output = Result<usize, Self::Error>>;
130
131 fn write_user(&mut self, buf: &[u8]) -> impl Future<Output = Result<usize, Self::Error>>;
133
134 fn execute_command<C: Command>(
136 &mut self,
137 command: C,
138 ) -> impl Future<Output = Result<C::Output, Self::Error>> {
139 command.execute(self)
140 }
141
142 async fn handshake<D: Decode + CheckHeader>(
151 &mut self,
152 timeout: Duration,
153 retries: usize,
154 packet: impl Encode + Clone,
155 ) -> Result<D, Self::Error> {
156 let mut last_error = None;
157
158 for _ in 0..=retries {
159 self.send(packet.clone()).await?;
160 match self.recv::<D>(timeout).await {
161 Ok(decoded) => return Ok(decoded),
162 Err(e) => {
163 warn!(
164 "Handshake failed while waiting for {}: {:?}. Retrying...",
165 std::any::type_name::<D>(),
166 e
167 );
168 last_error = Some(e);
169 }
170 }
171 }
172 error!(
173 "Handshake failed after {} retries with error: {:?}",
174 retries, last_error
175 );
176 Err(last_error.unwrap())
177 }
178}
179
180#[derive(Debug, Clone, Copy, Eq, PartialEq)]
181pub enum ConnectionType {
182 Wired,
183 Controller,
184 Bluetooth,
185}
186impl ConnectionType {
187 pub fn is_wired(&self) -> bool {
188 matches!(self, ConnectionType::Wired)
189 }
190 pub fn is_controller(&self) -> bool {
191 matches!(self, ConnectionType::Controller)
192 }
193 pub fn is_bluetooth(&self) -> bool {
194 matches!(self, ConnectionType::Bluetooth)
195 }
196
197 pub(crate) fn max_chunk_size(&self, window_size: u16) -> u16 {
198 const USER_PROGRAM_CHUNK_SIZE: u16 = 4096;
199
200 #[cfg(feature = "bluetooth")]
201 {
202 use crate::bluetooth::BluetoothConnection;
203
204 if self.is_bluetooth() {
205 let max_chunk_size =
206 (BluetoothConnection::MAX_PACKET_SIZE as u16).min(window_size / 2) - 14;
207 max_chunk_size - (max_chunk_size % 4)
208 } else if window_size > 0 && window_size <= USER_PROGRAM_CHUNK_SIZE {
209 window_size
210 } else {
211 USER_PROGRAM_CHUNK_SIZE
212 }
213 }
214
215 #[cfg(not(feature = "bluetooth"))]
216 if window_size > 0 && window_size <= USER_PROGRAM_CHUNK_SIZE {
217 window_size
218 } else {
219 USER_PROGRAM_CHUNK_SIZE
220 }
221 }
222}