playdate_device/usb/
mod.rs

1use std::borrow::Cow;
2
3use std::pin::Pin;
4use std::task::Context;
5use std::task::Poll;
6
7use futures::FutureExt;
8use futures::TryFutureExt;
9use nusb::transfer::RequestBuffer;
10use nusb::transfer::TransferError;
11use nusb::DeviceInfo;
12use nusb::InterfaceInfo;
13use object_pool::Pool;
14use object_pool::Reusable;
15
16use crate::device::command::Command;
17use crate::device::Device;
18use crate::error::Error;
19
20use self::mode::DeviceMode;
21use self::mode::Mode;
22
23pub mod mode;
24pub mod discover;
25pub mod io;
26
27
28const BULK_IN: u8 = 0x81;
29const BULK_OUT: u8 = 0x01;
30
31#[allow(dead_code)]
32const INTERRUPT_IN: u8 = 0x82;
33
34
35pub trait HaveDataInterface {
36	fn data_interface_number(&self) -> Option<u8>;
37	fn have_data_interface(&self) -> bool { self.data_interface_number().is_some() }
38}
39
40impl HaveDataInterface for DeviceInfo {
41	#[cfg_attr(feature = "tracing", tracing::instrument)]
42	fn data_interface_number(&self) -> Option<u8> {
43		self.interfaces()
44		    .find(|i| i.class() == 0xA | 2)
45		    .map(|i| i.interface_number())
46	}
47}
48
49impl HaveDataInterface for nusb::Device {
50	#[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
51	fn data_interface_number(&self) -> Option<u8> {
52		let cfg = self.active_configuration().ok()?;
53		for i in cfg.interfaces() {
54			let bulk = i.alt_settings().find(|i| i.class() == 0xA | 2);
55			if bulk.is_some() {
56				return bulk.map(|i| i.interface_number());
57			}
58		}
59		None
60	}
61}
62
63impl HaveDataInterface for Device {
64	#[cfg_attr(feature = "tracing", tracing::instrument)]
65	fn data_interface_number(&self) -> Option<u8> {
66		self.info
67		    .data_interface_number()
68		    .or_else(|| self.inner.as_ref()?.data_interface_number())
69	}
70}
71
72pub trait MassStorageInterface {
73	fn storage_interface(&self) -> Option<&InterfaceInfo>;
74	fn have_storage_interface(&self) -> bool;
75}
76
77impl MassStorageInterface for DeviceInfo {
78	#[cfg_attr(feature = "tracing", tracing::instrument)]
79	fn storage_interface(&self) -> Option<&InterfaceInfo> { self.interfaces().find(|i| i.class() == 8) }
80	#[cfg_attr(feature = "tracing", tracing::instrument)]
81	fn have_storage_interface(&self) -> bool { self.storage_interface().is_some() }
82}
83
84
85impl Device {
86	/// 1. Find this device
87	/// 1. Compare `mode` of `this` vs. just found
88	/// 1. [if changed] Update state of `this`, drop all pending transfers if needed
89	///    to prevent future errors when send to unexisting interface.
90	/// 1. Return `true` if `mode` changed.
91	#[cfg_attr(feature = "tracing", tracing::instrument)]
92	pub fn refresh(&mut self) -> Result<bool, Error> {
93		let mode = self.info.mode();
94		if mode != self.mode {
95			self.mode = mode;
96			self.interface.take();
97			self.inner.take();
98			debug!(
99			       "{}: refreshed by existing.",
100			       self.info.serial_number().unwrap_or("unknown")
101			);
102			Ok(true)
103		} else {
104			let updated = crate::usb::discover::devices()?.find(|dev| {
105				                                              let serial = dev.info().serial_number();
106				                                              serial.is_some() && serial == self.info.serial_number()
107			                                              });
108			if let Some(dev) = updated {
109				let mode = dev.mode_cached();
110				let changed = mode != self.mode;
111				if changed {
112					self.mode = mode;
113					self.info = dev.info;
114					self.interface.take();
115					self.inner.take();
116					debug!(
117					       "{}: refreshed by existing new.",
118					       self.info.serial_number().unwrap_or("unknown")
119					);
120				}
121				Ok(changed)
122			} else {
123				debug!(
124				       "{}: device not found.",
125				       self.info.serial_number().unwrap_or("unknown")
126				);
127				self.interface.take();
128				self.inner.take();
129				Ok(true)
130			}
131		}
132	}
133
134
135	/// Open USB interface if available,
136	/// otherwise try open serial port if available.
137	#[cfg_attr(feature = "tracing", tracing::instrument)]
138	pub fn open(&mut self) -> Result<(), Error> {
139		if !matches!(self.mode, Mode::Data) {
140			return Err(Error::WrongState(self.mode));
141		}
142
143		trace!("opening device");
144
145		// Special case: if we already have an interface, mostly possible serial:
146		if let Some(interface) = self.interface.as_mut() {
147			if let crate::interface::Interface::Serial(serial) = interface {
148				serial.open()?
149			}
150			return Ok(());
151		}
152
153		if self.have_data_interface() {
154			let bulk = self.try_bulk().map(|_| {});
155			if let Some(err) = bulk.err() {
156				self.try_serial().map_err(|err2| Error::chain(err2, [err]))
157			} else {
158				self.interface()
159			}
160		} else {
161			self.try_serial()
162		}?;
163		Ok(())
164	}
165
166	#[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
167	fn try_bulk(&mut self) -> Result<&crate::interface::Interface, Error> {
168		if let Some(ref io) = self.interface {
169			Ok(io)
170		} else if let Some(ref dev) = self.inner {
171			let id = self.info
172			             .data_interface_number()
173			             .or_else(|| dev.data_interface_number())
174			             .ok_or_else(|| Error::not_found())?;
175			// previously used 0x01.
176			self.interface = Some(Interface::from(dev.claim_interface(id)?).into());
177			Ok(self.interface.as_ref().unwrap())
178		} else {
179			let dev = self.info.open()?;
180			let id = self.info
181			             .data_interface_number()
182			             .or_else(|| dev.data_interface_number())
183			             .ok_or_else(|| Error::not_found())?;
184			self.interface = Some(Interface::from(dev.claim_interface(id)?).into());
185			self.inner = Some(dev);
186			Ok(self.interface.as_ref().unwrap())
187		}
188	}
189
190	#[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
191	fn try_serial(&mut self) -> Result<&crate::interface::Interface, Error> {
192		use crate::serial::Interface;
193
194
195		let mut errors = Vec::new();
196		let port = {
197			crate::serial::discover::ports_for(self).map(|ports| ports.map(Interface::new))?
198			                                        .find_map(|mut port| {
199				                                        // try to open port, we could get an permission error
200				                                        match port.open() {
201					                                        Ok(_) => Some(port),
202				                                           Err(err) => {
203					                                           errors.push(err);
204					                                           None
205				                                           },
206				                                        }
207			                                        })
208		};
209
210		if let Some(port) = port {
211			self.interface = Some(port.into());
212			self.interface()
213		} else {
214			Err(Error::chain(Error::not_found(), errors))
215		}
216	}
217
218
219	/// Async read-write interface.
220	pub fn interface(&self) -> Result<&crate::interface::Interface, Error> {
221		self.interface.as_ref().ok_or_else(|| Error::not_ready())
222	}
223
224	pub fn interface_mut(&mut self) -> Result<&mut crate::interface::Interface, Error> {
225		self.interface.as_mut().ok_or_else(|| Error::not_ready())
226	}
227
228	#[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
229	pub fn set_interface(&mut self, interface: crate::interface::Interface) {
230		self.close();
231		self.interface = Some(interface);
232	}
233
234	#[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
235	pub fn close(&mut self) { self.close_inner(); }
236
237	#[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
238	pub fn close_with_reset(&mut self) -> Result<(), Error> {
239		if let Some(dev) = self.close_inner() {
240			dev.reset()?;
241		}
242		Ok(())
243	}
244
245	#[inline]
246	fn close_inner(&mut self) -> Option<nusb::Device> {
247		self.info.serial_number().inspect(|sn| debug!("closing {sn}"));
248		self.interface.take();
249		self.inner.take()
250	}
251}
252
253impl crate::interface::blocking::Out for Interface {
254	#[cfg_attr(feature = "tracing", tracing::instrument)]
255	fn send_cmd(&self, cmd: Command) -> Result<usize, Error> {
256		use crate::interface::r#async;
257		let fut = <Self as r#async::Out>::send_cmd(self, cmd);
258		futures_lite::future::block_on(fut).map_err(Into::into)
259	}
260}
261
262impl crate::interface::blocking::In for Interface {}
263
264
265impl crate::interface::r#async::Out for Interface {
266	#[cfg_attr(feature = "tracing", tracing::instrument)]
267	async fn send(&self, data: &[u8]) -> Result<usize, Error> { self.write(data).map_err(Into::into).await }
268}
269
270impl crate::interface::r#async::In for Interface {}
271
272
273pub struct Interface {
274	inner: nusb::Interface,
275}
276
277impl From<nusb::Interface> for Interface {
278	fn from(interface: nusb::Interface) -> Self { Self { inner: interface } }
279}
280
281impl std::fmt::Display for Interface {
282	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "usb") }
283}
284
285impl std::fmt::Debug for Interface {
286	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "usb") }
287}
288
289impl Interface {
290	#[cfg_attr(feature = "tracing", tracing::instrument)]
291	pub fn write(&self, data: &[u8]) -> impl std::future::Future<Output = Result<usize, TransferError>> {
292		trace!("writing {} bytes", data.len());
293		self.inner.bulk_out(BULK_OUT, data.to_vec()).map(|comp| {
294			                                            // TODO: attach data to the pool
295			                                            let written = comp.data.actual_length();
296			                                            let data = comp.data.reuse();
297			                                            let s =
298				                                            std::str::from_utf8(&data).map(Cow::Borrowed)
299				                                                                      .unwrap_or_else(|_| {
300					                                                                      Cow::Owned(hex::encode_upper(&data))
301				                                                                      });
302			                                            trace!("sent, resp: ({written}) '{s}'");
303			                                            comp.status.map(|_| written)
304		                                            })
305	}
306}
307
308
309pub struct PoolStream<'pool> {
310	pool: &'pool Pool<Vec<u8>>,
311	queue: nusb::transfer::Queue<RequestBuffer>,
312	buffer_size: usize,
313	// inner: futures_lite::stream::PollFn<Option<Result<Vec<u8>, TransferError>>>,
314}
315
316impl<'pool> futures::Stream for PoolStream<'pool> {
317	type Item = Result<Reusable<'pool, Vec<u8>>, TransferError>;
318
319	fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
320		self.queue.poll_next(ctx).map(|comp| {
321			                         let data = comp.data;
322			                         match comp.status {
323				                         Ok(_) => {
324				                            // prepare next request
325				                            let buffer_size = self.buffer_size;
326				                            let (_, buf) =
327					                            self.pool.pull(|| Vec::with_capacity(buffer_size)).detach();
328				                            self.queue.submit(RequestBuffer::reuse(buf, buffer_size));
329				                            // make received data reusable
330				                            let data = Reusable::new(self.pool, data);
331				                            Some(Ok(data))
332			                            },
333			                            Err(err) => {
334				                            self.pool.attach(data);
335				                            self.queue.cancel_all();
336				                            Some(Err(err))
337			                            },
338			                         }
339		                         })
340	}
341
342	fn size_hint(&self) -> (usize, Option<usize>) { (0, Some(self.queue.pending())) }
343}