playdate_device/usb/
mod.rs1use 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 #[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 #[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 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 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 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 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 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 }
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 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 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}