nusb 0.2.3

Cross-platform low-level access to USB devices in pure Rust
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
use crate::{
    descriptors::{
        decode_string_descriptor, validate_string_descriptor, ConfigurationDescriptor,
        DeviceDescriptor, InterfaceDescriptor, DESCRIPTOR_TYPE_STRING,
    },
    io::{EndpointRead, EndpointWrite},
    platform,
    transfer::{
        Buffer, BulkOrInterrupt, Completion, ControlIn, ControlOut, Direction, EndpointDirection,
        EndpointType, In, Out, TransferError,
    },
    ActiveConfigurationError, DeviceInfo, Error, ErrorKind, GetDescriptorError, MaybeFuture, Speed,
};
use log::{error, warn};
use std::{
    fmt::Debug,
    future::{poll_fn, Future},
    marker::PhantomData,
    num::NonZeroU8,
    sync::Arc,
    task::{Context, Poll},
    time::Duration,
};

/// An opened USB device.
///
/// Obtain a `Device` by calling [`DeviceInfo::open`]:
///
/// ```no_run
/// use nusb::{self, MaybeFuture};
/// let device_info = nusb::list_devices().wait().unwrap()
///     .find(|dev| dev.vendor_id() == 0xAAAA && dev.product_id() == 0xBBBB)
///     .expect("device not connected");
///
/// let device = device_info.open().wait().expect("failed to open device");
/// let interface = device.claim_interface(0);
/// ```
///
/// This type is reference-counted with an [`Arc`] internally, and can be cloned cheaply for
/// use in multiple places in your program. The device is closed when all clones and all
/// associated [`Interface`]s are dropped.
///
/// Use [`.claim_interface(i)`][`Device::claim_interface`] to open an interface to submit
/// transfers.
#[derive(Clone)]
pub struct Device {
    backend: Arc<crate::platform::Device>,
}

impl Device {
    pub(crate) fn wrap(backend: Arc<platform::Device>) -> Device {
        Device { backend }
    }

    pub(crate) fn open(d: &DeviceInfo) -> impl MaybeFuture<Output = Result<Device, Error>> {
        platform::Device::from_device_info(d).map(|d| d.map(Device::wrap))
    }

    /// Wrap a usbdevfs file descriptor that is already open.
    ///
    /// This opens a device from a file descriptor for a `/dev/bus/usb/*` device
    /// provided externally, such as from
    /// [Android](https://developer.android.com/reference/android/hardware/usb/UsbDeviceConnection#getFileDescriptor()),
    /// [xdg-desktop-portal](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Usb.html),
    /// etc.
    ///
    /// *Supported on Linux and Android only.*
    #[cfg(any(target_os = "android", target_os = "linux"))]
    pub fn from_fd(fd: std::os::fd::OwnedFd) -> impl MaybeFuture<Output = Result<Device, Error>> {
        platform::Device::from_fd(fd).map(|d| d.map(Device::wrap))
    }

    /// Open an interface of the device and claim it for exclusive use.
    pub fn claim_interface(
        &self,
        interface: u8,
    ) -> impl MaybeFuture<Output = Result<Interface, Error>> {
        self.backend
            .clone()
            .claim_interface(interface)
            .map(|i| i.map(Interface::wrap))
    }

    /// Detach kernel drivers and open an interface of the device and claim it for exclusive use.
    ///
    /// ### Platform-specific details
    /// This function can only detach kernel drivers on Linux. Calling on other platforms has
    /// the same effect as [`claim_interface`][`Device::claim_interface`].
    pub fn detach_and_claim_interface(
        &self,
        interface: u8,
    ) -> impl MaybeFuture<Output = Result<Interface, Error>> {
        self.backend
            .clone()
            .detach_and_claim_interface(interface)
            .map(|i| i.map(Interface::wrap))
    }

    /// Detach kernel drivers for the specified interface.
    ///
    /// ### Platform-specific details
    /// This function can only detach kernel drivers on Linux. Calling on other platforms has
    /// no effect.
    pub fn detach_kernel_driver(&self, interface: u8) -> Result<(), Error> {
        #[cfg(target_os = "linux")]
        self.backend.detach_kernel_driver(interface)?;
        let _ = interface;

        Ok(())
    }

    /// Attach kernel drivers for the specified interface.
    ///
    /// ### Platform-specific details
    /// This function can only attach kernel drivers on Linux. Calling on other platforms has
    /// no effect.
    pub fn attach_kernel_driver(&self, interface: u8) -> Result<(), Error> {
        #[cfg(target_os = "linux")]
        self.backend.attach_kernel_driver(interface)?;
        let _ = interface;

        Ok(())
    }

    /// Get the device descriptor.
    ///
    /// This returns cached data and does not perform IO.
    pub fn device_descriptor(&self) -> DeviceDescriptor {
        self.backend.device_descriptor()
    }

    /// Get the device's connection speed.
    pub fn speed(&self) -> Option<Speed> {
        self.backend.speed()
    }

    /// Get information about the active configuration.
    ///
    /// This returns cached data and does not perform IO. However, it can fail if the
    /// device is unconfigured, or if it can't find a configuration descriptor for
    /// the configuration reported as active by the OS.
    pub fn active_configuration(
        &self,
    ) -> Result<ConfigurationDescriptor<'_>, ActiveConfigurationError> {
        let active = self.backend.active_configuration_value();

        self.configurations()
            .find(|c| c.configuration_value() == active)
            .ok_or(ActiveConfigurationError {
                configuration_value: active,
            })
    }

    /// Get an iterator returning information about each configuration of the device.
    ///
    /// This returns cached data and does not perform IO.
    pub fn configurations(&self) -> impl Iterator<Item = ConfigurationDescriptor<'_>> {
        self.backend.configuration_descriptors()
    }

    /// Set the device configuration.
    ///
    /// The argument is the desired configuration's `bConfigurationValue`
    /// descriptor field from [`ConfigurationDescriptor::configuration_value`] or `0` to
    /// unconfigure the device.
    ///
    /// ### Platform-specific details
    /// * Not supported on Windows
    pub fn set_configuration(
        &self,
        configuration: u8,
    ) -> impl MaybeFuture<Output = Result<(), Error>> {
        self.backend.clone().set_configuration(configuration)
    }

    /// Request a descriptor from the device.
    ///
    /// The `language_id` should be `0` unless you are requesting a string descriptor.
    ///
    /// ### Platform-specific details
    ///
    /// * On Windows, the timeout argument is ignored, and an OS-defined timeout is used.
    /// * On Windows, this does not wake suspended devices. Reading their
    ///   descriptors will return an error.
    pub fn get_descriptor(
        &self,
        desc_type: u8,
        desc_index: u8,
        language_id: u16,
        timeout: Duration,
    ) -> impl MaybeFuture<Output = Result<Vec<u8>, GetDescriptorError>> {
        #[cfg(target_os = "windows")]
        {
            let _ = timeout;
            self.backend
                .clone()
                .get_descriptor(desc_type, desc_index, language_id)
                .map(|r| r.map_err(GetDescriptorError::Transfer))
        }

        #[cfg(not(target_os = "windows"))]
        {
            const STANDARD_REQUEST_GET_DESCRIPTOR: u8 = 0x06;
            use crate::transfer::{ControlType, Recipient};

            self.control_in(
                ControlIn {
                    control_type: ControlType::Standard,
                    recipient: Recipient::Device,
                    request: STANDARD_REQUEST_GET_DESCRIPTOR,
                    value: ((desc_type as u16) << 8) | desc_index as u16,
                    index: language_id,
                    length: 4096,
                },
                timeout,
            )
            .map(|r| r.map_err(GetDescriptorError::Transfer))
        }
    }

    /// Request the list of supported languages for string descriptors.
    ///
    /// ### Platform-specific details
    ///
    /// See notes on [`get_descriptor`][`Self::get_descriptor`].
    pub fn get_string_descriptor_supported_languages(
        &self,
        timeout: Duration,
    ) -> impl MaybeFuture<Output = Result<impl Iterator<Item = u16>, GetDescriptorError>> {
        self.get_descriptor(DESCRIPTOR_TYPE_STRING, 0, 0, timeout)
            .map(move |r| {
                let data = r?;
                if !validate_string_descriptor(&data) {
                    error!("String descriptor language list read {data:?}, not a valid string descriptor");
                    return Err(GetDescriptorError::InvalidDescriptor)
                }

                //TODO: Use array_chunks once stable
                let mut iter = data.into_iter().skip(2);
                Ok(std::iter::from_fn(move || {
                    Some(u16::from_le_bytes([iter.next()?, iter.next()?]))
                }))
            })
    }

    /// Request a string descriptor from the device.
    ///
    /// Almost all devices support only the language ID [`US_ENGLISH`][`crate::descriptors::language_id::US_ENGLISH`].
    ///
    /// Unpaired UTF-16 surrogates will be replaced with `�`, like [`String::from_utf16_lossy`].
    ///
    /// ### Platform-specific details
    ///
    /// See notes on [`get_descriptor`][`Self::get_descriptor`].
    pub fn get_string_descriptor(
        &self,
        desc_index: NonZeroU8,
        language_id: u16,
        timeout: Duration,
    ) -> impl MaybeFuture<Output = Result<String, GetDescriptorError>> {
        self.get_descriptor(
            DESCRIPTOR_TYPE_STRING,
            desc_index.get(),
            language_id,
            timeout,
        )
        .map(|r| {
            let data = r?;
            decode_string_descriptor(&data).map_err(|_| GetDescriptorError::InvalidDescriptor)
        })
    }

    /// Reset the device, forcing it to re-enumerate.
    ///
    /// This `Device` will no longer be usable, and you should drop it and call
    /// [`list_devices`][`super::list_devices`] to find and re-open it again.
    ///
    /// ### Platform-specific details
    /// * Not supported on Windows
    pub fn reset(&self) -> impl MaybeFuture<Output = Result<(), Error>> {
        self.backend.clone().reset()
    }

    /// Submit a single **IN (device-to-host)** transfer on the default **control** endpoint.
    ///
    /// ### Example
    ///
    /// ```no_run
    /// use std::time::Duration;
    /// use futures_lite::future::block_on;
    /// use nusb::transfer::{ ControlIn, ControlType, Recipient };
    /// # use nusb::MaybeFuture;
    /// # fn main() -> Result<(), std::io::Error> {
    /// # let di = nusb::list_devices().wait().unwrap().next().unwrap();
    /// # let device = di.open().wait().unwrap();
    ///
    /// let data: Vec<u8> = device.control_in(ControlIn {
    ///     control_type: ControlType::Vendor,
    ///     recipient: Recipient::Device,
    ///     request: 0x30,
    ///     value: 0x0,
    ///     index: 0x0,
    ///     length: 64,
    /// }, Duration::from_millis(100)).wait()?;
    /// # Ok(()) }
    /// ```
    ///
    /// ### Platform-specific details
    ///
    /// * Not supported on Windows. You must [claim an interface][`Device::claim_interface`]
    ///   and use the interface handle to submit transfers.
    #[cfg(any(target_os = "linux", target_os = "macos", target_os = "android"))]
    pub fn control_in(
        &self,
        data: ControlIn,
        timeout: Duration,
    ) -> impl MaybeFuture<Output = Result<Vec<u8>, TransferError>> {
        self.backend.clone().control_in(data, timeout)
    }

    /// Submit a single **OUT (host-to-device)** transfer on the default **control** endpoint.
    ///
    /// ### Example
    ///
    /// ```no_run
    /// use std::time::Duration;
    /// use futures_lite::future::block_on;
    /// use nusb::transfer::{ ControlOut, ControlType, Recipient };
    /// # use nusb::MaybeFuture;
    /// # fn main() -> Result<(), std::io::Error> {
    /// # let di = nusb::list_devices().wait().unwrap().next().unwrap();
    /// # let device = di.open().wait().unwrap();
    ///
    /// device.control_out(ControlOut {
    ///     control_type: ControlType::Vendor,
    ///     recipient: Recipient::Device,
    ///     request: 0x32,
    ///     value: 0x0,
    ///     index: 0x0,
    ///     data: &[0x01, 0x02, 0x03, 0x04],
    /// }, Duration::from_millis(100)).wait()?;
    /// # Ok(()) }
    /// ```
    ///
    /// ### Platform-specific details
    ///
    /// * Not supported on Windows. You must [claim an interface][`Device::claim_interface`]
    ///   and use the interface handle to submit transfers.
    #[cfg(any(target_os = "linux", target_os = "macos", target_os = "android"))]
    pub fn control_out(
        &self,
        data: ControlOut,
        timeout: Duration,
    ) -> impl MaybeFuture<Output = Result<(), TransferError>> {
        self.backend.clone().control_out(data, timeout)
    }
}

impl Debug for Device {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Device").finish()
    }
}

/// An opened interface of a USB device.
///
/// Obtain an `Interface` with the [`Device::claim_interface`] method.
///
/// This type is reference-counted with an [`Arc`] internally, and can be cloned cheaply for
/// use in multiple places in your program. The interface is released when all clones, and all
/// associated [`Endpoint`]s are dropped.
#[derive(Clone)]
pub struct Interface {
    backend: Arc<platform::Interface>,
}

impl Interface {
    pub(crate) fn wrap(backend: Arc<platform::Interface>) -> Self {
        Interface { backend }
    }

    /// Select the alternate setting of this interface.
    ///
    /// An alternate setting is a mode of the interface that makes particular endpoints available
    /// and may enable or disable functionality of the device. The OS resets the device to the default
    /// alternate setting when the interface is released or the program exits.
    ///
    /// You must not have any pending transfers or open `Endpoints` on this interface when changing
    /// the alternate setting.
    pub fn set_alt_setting(&self, alt_setting: u8) -> impl MaybeFuture<Output = Result<(), Error>> {
        self.backend.clone().set_alt_setting(alt_setting)
    }

    /// Get the current alternate setting of this interface.
    pub fn get_alt_setting(&self) -> u8 {
        self.backend.get_alt_setting()
    }

    /// Submit a single **IN (device-to-host)** transfer on the default **control** endpoint.
    ///
    /// ### Example
    ///
    /// ```no_run
    /// use std::time::Duration;
    /// use futures_lite::future::block_on;
    /// use nusb::transfer::{ ControlIn, ControlType, Recipient };
    /// # use nusb::MaybeFuture;
    /// # fn main() -> Result<(), std::io::Error> {
    /// # let di = nusb::list_devices().wait().unwrap().next().unwrap();
    /// # let device = di.open().wait().unwrap();
    /// # let interface = device.claim_interface(0).wait().unwrap();
    ///
    /// let data: Vec<u8> = interface.control_in(ControlIn {
    ///     control_type: ControlType::Vendor,
    ///     recipient: Recipient::Device,
    ///     request: 0x30,
    ///     value: 0x0,
    ///     index: 0x0,
    ///     length: 64,
    /// }, Duration::from_millis(100)).wait()?;
    /// # Ok(()) }
    /// ```
    ///
    /// ### Platform-specific details
    /// * On Windows, if the `recipient` is `Interface`, the least significant
    ///   byte of `index` must match the interface number, or
    ///   `TransferError::InvalidArgument` will be returned. This is a WinUSB
    ///   limitation.
    /// * On Windows, the timeout is currently fixed to 5 seconds and the
    ///   timeout argument is ignored.
    pub fn control_in(
        &self,
        data: ControlIn,
        timeout: Duration,
    ) -> impl MaybeFuture<Output = Result<Vec<u8>, TransferError>> {
        self.backend.clone().control_in(data, timeout)
    }

    /// Submit a single **OUT (host-to-device)** transfer on the default
    /// **control** endpoint.
    ///
    /// ### Example
    ///
    /// ```no_run
    /// use std::time::Duration;
    /// use futures_lite::future::block_on;
    /// use nusb::transfer::{ ControlOut, ControlType, Recipient };
    /// # use nusb::MaybeFuture;
    /// # fn main() -> Result<(), std::io::Error> {
    /// # let di = nusb::list_devices().wait().unwrap().next().unwrap();
    /// # let device = di.open().wait().unwrap();
    /// # let interface = device.claim_interface(0).wait().unwrap();
    ///
    /// interface.control_out(ControlOut {
    ///     control_type: ControlType::Vendor,
    ///     recipient: Recipient::Device,
    ///     request: 0x32,
    ///     value: 0x0,
    ///     index: 0x0,
    ///     data: &[0x01, 0x02, 0x03, 0x04],
    /// }, Duration::from_millis(100)).wait()?;
    /// # Ok(()) }
    /// ```
    ///
    /// ### Platform-specific details
    /// * On Windows, if the `recipient` is `Interface`, the least significant
    ///   byte of `index` must match the interface number, or
    ///   `TransferError::InvalidArgument` will be returned. This is a WinUSB
    ///   limitation.
    /// * On Windows, the timeout is currently fixed to 5 seconds and the
    ///   timeout argument is ignored.
    pub fn control_out(
        &self,
        data: ControlOut,
        timeout: Duration,
    ) -> impl MaybeFuture<Output = Result<(), TransferError>> {
        self.backend.clone().control_out(data, timeout)
    }

    /// Get the interface number.
    pub fn interface_number(&self) -> u8 {
        self.backend.interface_number
    }

    /// Get the interface descriptors for the alternate settings of this interface.
    ///
    /// This returns cached data and does not perform IO.
    pub fn descriptors(&self) -> impl Iterator<Item = InterfaceDescriptor<'_>> {
        let active = self.backend.device.active_configuration_value();

        let configuration = self
            .backend
            .device
            .configuration_descriptors()
            .find(|c| c.configuration_value() == active);

        configuration
            .into_iter()
            .flat_map(|i| i.interface_alt_settings())
            .filter(|g| g.interface_number() == self.backend.interface_number)
    }

    /// Get the interface descriptor for the current alternate setting.
    pub fn descriptor(&self) -> Option<InterfaceDescriptor<'_>> {
        self.descriptors()
            .find(|i| i.alternate_setting() == self.get_alt_setting())
    }

    /// Open an endpoint.
    ///
    /// This claims exclusive access to the endpoint and returns an [`Endpoint`]
    /// that can be used to submit transfers. The type-level `EndpointType` and
    /// `EndpointDirection` parameters must match the endpoint type and
    /// direction.
    pub fn endpoint<EpType: EndpointType, Dir: EndpointDirection>(
        &self,
        address: u8,
    ) -> Result<Endpoint<EpType, Dir>, Error> {
        let intf_desc = self.descriptor();
        let ep_desc =
            intf_desc.and_then(|desc| desc.endpoints().find(|ep| ep.address() == address));
        let Some(ep_desc) = ep_desc else {
            return Err(Error::new(
                ErrorKind::NotFound,
                "specified endpoint does not exist on this interface",
            ));
        };

        if address & Direction::MASK != Dir::DIR as u8 {
            return Err(Error::new(ErrorKind::Other, "incorrect endpoint direction"));
        }

        if ep_desc.transfer_type() != EpType::TYPE {
            return Err(Error::new(ErrorKind::Other, "incorrect endpoint type"));
        }

        let backend = self.backend.endpoint(ep_desc)?;
        Ok(Endpoint {
            backend,
            ep_type: PhantomData,
            ep_dir: PhantomData,
        })
    }
}

impl Debug for Interface {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Interface")
            .field("number", &self.backend.interface_number)
            .finish()
    }
}

/// Exclusive access to an endpoint of a USB device.
///
/// Obtain an `Endpoint` with the [`Interface::endpoint`] method.
///
/// An `Endpoint` manages a queue of pending transfers. Submitting a transfer is
/// a non-blocking operation that adds the operation to the queue. Completed
/// transfers can be popped from the queue synchronously or asynchronously.
///
/// This separation of submission and completion makes the API cancel-safe, and
/// makes it easy to submit multiple transfers at once, regardless of whether
/// you are using asynchronous or blocking waits.
///
/// When the `Endpoint` is dropped, any pending transfers are cancelled.
///
/// This type provides a low-level API letting you manage transfers directly.
/// For a higher-level buffered API implementing [`std::io::Read`] and
/// [`std::io::Write`], use the adapters from [`nusb::io`][`crate::io`]. See
/// [`Self::reader`] and [`Self::writer`].
///
/// ## Examples
///
/// ### Async
/// ```no_run
/// # use nusb::{MaybeFuture, transfer::{ Buffer, Bulk, Out, In }};
/// # use std::time::Duration;
/// # fn main() -> Result<(), std::io::Error> {
/// # futures_lite::future::block_on(async{
/// # let di = nusb::list_devices().await.unwrap().next().unwrap();
/// # let device = di.open().await.unwrap();
/// # let interface = device.claim_interface(0).await.unwrap();
/// # let mut ep_out = interface.endpoint::<Bulk, Out>(0x02).unwrap();
/// # let mut ep_in = interface.endpoint::<Bulk, In>(0x82).unwrap();
/// ep_out.submit(vec![1, 2, 3].into());
/// ep_out.next_complete().await.into_result()?;
///
/// ep_in.submit(Buffer::new(64));
/// let response = ep_in.next_complete().await.into_result()?;
/// # Ok(())
/// # })}
/// ```
///
/// ### Single transfer (blocking)
///
/// For simple use cases with blocking waits, the
/// [`transfer_blocking`](Endpoint::transfer_blocking) convenience method wraps
/// `submit` and `wait_next_complete` to handle cancelling the transfer after
/// the specified timeout.
///
/// ```no_run
/// # use nusb::{MaybeFuture, transfer::{ Buffer, Bulk, Out, In }};
/// # use std::time::Duration;
/// # fn main() -> Result<(), std::io::Error> {
/// # let di = nusb::list_devices().wait().unwrap().next().unwrap();
/// # let device = di.open().wait().unwrap();
/// # let interface = device.claim_interface(0).wait().unwrap();
/// # let mut ep_out = interface.endpoint::<Bulk, Out>(0x02).unwrap();
/// # let mut ep_in = interface.endpoint::<Bulk, In>(0x82).unwrap();
/// let timeout = Duration::from_secs(1);
/// ep_out.transfer_blocking(vec![0x00, 0x01, 0x02].into(), timeout).into_result()?;
/// let response = ep_in.transfer_blocking(Buffer::new(64), timeout).into_result()?;
/// # Ok(()) }
/// ```
///
/// ### Optimized Streaming
///
/// To maximize throughput and minimize latency when streaming data, the host
/// controller needs to attempt a transfer in every possible frame. That
/// requires always having a transfer request pending with the kernel by
/// submitting multiple transfer requests and re-submitting them as they
/// complete.
///
/// The transfer size can be tuned to balance latency and overhead.
///
/// ```no_run
/// # use nusb::{MaybeFuture, transfer::{ Buffer, Bulk, Out, In }};
/// # use std::time::Duration;
/// # fn main() -> Result<(), std::io::Error> {
/// # let di = nusb::list_devices().wait().unwrap().next().unwrap();
/// # let device = di.open().wait().unwrap();
/// # let interface = device.claim_interface(0).wait().unwrap();
/// # let mut ep_in = interface.endpoint::<Bulk, In>(0x82).unwrap();
/// # fn handle_transfer(p: &[u8]) {}
/// while ep_in.pending() < 8 {
///     let buffer = ep_in.allocate(16384);
///     ep_in.submit(buffer);
/// }
///
/// loop {
///     let completion = ep_in.wait_next_complete(Duration::MAX).unwrap();
///
///     handle_transfer(&completion.buffer[..]);
///     completion.status?;
///
///     ep_in.submit(completion.buffer);
/// }
///
/// # Ok(()) }
/// ```
pub struct Endpoint<EpType, Dir> {
    backend: platform::Endpoint,
    ep_type: PhantomData<EpType>,
    ep_dir: PhantomData<Dir>,
}

/// Methods for all endpoints.
impl<EpType: EndpointType, Dir: EndpointDirection> Endpoint<EpType, Dir> {
    /// Get the endpoint address.
    pub fn endpoint_address(&self) -> u8 {
        self.backend.endpoint_address()
    }

    /// Get the maximum packet size for this endpoint.
    ///
    /// Transfers can consist of multiple packets, but are split into packets
    /// of this size on the bus.
    pub fn max_packet_size(&self) -> usize {
        self.backend.max_packet_size
    }

    /// Get the number of transfers that have been submitted with `submit` that
    /// have not yet been returned from `next_complete`.
    pub fn pending(&self) -> usize {
        self.backend.pending()
    }

    /// Request cancellation of all pending transfers.
    ///
    /// The transfers are cancelled asynchronously. Once cancelled, they will be
    /// returned from calls to `next_complete` so you can tell which were
    /// completed, partially-completed, or cancelled.
    pub fn cancel_all(&mut self) {
        self.backend.cancel_all()
    }
}

impl<EpType: BulkOrInterrupt> Endpoint<EpType, Out> {
    /// Create an [`EndpointWrite`] wrapping the given endpoint to provide a
    /// high-level buffered API implementing [`std::io::Write`] and async
    /// equivalents.
    ///
    /// See [`EndpointWrite::new`][`crate::io::EndpointWrite::new`] for details.
    pub fn writer(self, buffer_size: usize) -> EndpointWrite<EpType> {
        EndpointWrite::new(self, buffer_size)
    }
}

impl<EpType: BulkOrInterrupt> Endpoint<EpType, In> {
    /// Create an [`EndpointRead`] wrapping the given endpoint to provide a
    /// high-level buffered API implementing [`std::io::Read`] and  async
    /// equivalents.
    ///
    /// See [`EndpointRead::new`][`crate::io::EndpointRead::new`] for details.
    pub fn reader(self, buffer_size: usize) -> EndpointRead<EpType> {
        EndpointRead::new(self, buffer_size)
    }
}

/// Methods for Bulk and Interrupt endpoints.
impl<EpType: BulkOrInterrupt, Dir: EndpointDirection> Endpoint<EpType, Dir> {
    /// Allocate a buffer for use on this endpoint, zero-copy if possible.
    ///
    /// A zero-copy buffer allows the kernel to DMA directly to/from this
    /// buffer for improved performance. However, because it is not allocated
    /// with the system allocator, it cannot be converted to a [`Vec`] without
    /// copying.
    ///
    /// This is a somewhat expensive operation, requiring a `mmap` system call,
    /// so is likely only beneficial for buffers that will be used repeatedly.
    /// Consider using [`Buffer::new`] for one-off transfers.
    ///
    /// This is currently only supported on Linux, falling back to [`Buffer::new`]
    /// on other platforms, or if the memory allocation fails.
    pub fn allocate(&self, len: usize) -> Buffer {
        #[cfg(any(target_os = "linux", target_os = "android"))]
        {
            if let Ok(b) = self.backend.allocate(len) {
                return b;
            }
        }

        Buffer::new(len)
    }

    /// Begin a transfer on the endpoint.
    ///
    /// Submitted transfers are queued and completed in order. Once the transfer
    /// completes, it will be returned from
    /// [`next_complete()`][`Self::next_complete`]. Any error in submitting or
    /// performing the transfer is deferred until `next_complete`.
    ///
    /// For an OUT transfer, the buffer's `len` is the number of bytes
    /// initialized, which will be sent to the device.
    ///
    /// For an IN transfer, the buffer's `requested_len` is the number of bytes
    /// requested. It must be a nonzero multiple of the endpoint's [maximum
    /// packet size][`Self::max_packet_size`] or the transfer will fail with
    /// `TransferError::InvalidArgument`. Up to `requested_len /
    /// max_packet_size` packets will be received, ending early when any packet
    /// is shorter than `max_packet_size`.
    pub fn submit(&mut self, buf: Buffer) {
        if Dir::DIR == Direction::In {
            let req_len = buf.requested_len();
            if req_len == 0 || req_len % self.max_packet_size() != 0 {
                warn!(
                    "Submitting transfer with length {req_len} which is not a multiple of max packet size {} on IN endpoint {:02x}",
                    self.max_packet_size(),
                    self.endpoint_address(),
                );

                return self.backend.submit_err(buf, TransferError::InvalidArgument);
            }
        }

        self.backend.submit(buf)
    }

    /// Return a `Future` that waits for the next pending transfer to complete.
    ///
    /// This future is cancel-safe: it can be cancelled and re-created without
    /// side effects, enabling its use in `select!{}` or similar.
    ///
    /// An OUT transfer completes when the specified data has been sent or an
    /// error occurs. An IN transfer completes when a packet smaller than
    /// `max_packet_size` is received, the full `requested_len` is received
    /// (without waiting for or consuming any subsequent zero-length packet), or
    /// an error occurs.
    ///
    /// ## Panics
    /// * if there are no transfers pending (that is, if [`Self::pending()`]
    ///   would return 0).
    pub fn next_complete(&mut self) -> impl Future<Output = Completion> + Send + Sync + '_ {
        poll_fn(|cx| self.poll_next_complete(cx))
    }

    /// Poll for a pending transfer completion.
    ///
    /// Returns a completed transfer if one is available, or arranges for the
    /// context's waker to be notified when a transfer completes.
    ///
    /// ## Panics
    ///  * if there are no transfers pending (that is, if [`Self::pending()`]
    ///    would return 0).
    pub fn poll_next_complete(&mut self, cx: &mut Context<'_>) -> Poll<Completion> {
        self.backend.poll_next_complete(cx)
    }

    /// Wait for a pending transfer completion.
    ///
    /// Blocks for up to `timeout` waiting for a transfer to complete, or
    /// returns `None` if the timeout is reached.
    ///
    /// Note that the transfer is not cancelled after the timeout, and can still
    /// be returned from a subsequent call.
    ///
    /// ## Panics
    ///  * if there are no transfers pending (that is, if [`Self::pending()`]
    ///    would return 0).
    pub fn wait_next_complete(&mut self, timeout: Duration) -> Option<Completion> {
        self.backend.wait_next_complete(timeout)
    }

    /// Submit a single transfer and wait for it to complete.
    ///
    /// This is a convenience method that combines `submit` and
    /// `wait_next_complete` for the common case where you submit a single
    /// transfer and wait for it to complete with a timeout, cancelling it if
    /// the timeout is reached. This assumes that no transfer is already
    /// pending, and always consumes the transfer it submits such that it never
    /// leaves a transfer pending.
    ///
    /// In the case of a timeout, the returned `Completion` will have a status
    /// of `TransferError::Cancelled`.
    ///
    /// ## Panics
    ///  * if any transfer is already pending.
    pub fn transfer_blocking(&mut self, buf: Buffer, timeout: Duration) -> Completion {
        assert!(self.pending() == 0, "a transfer is already pending");
        self.submit(buf);
        if let Some(completion) = self.wait_next_complete(timeout) {
            return completion;
        }

        self.cancel_all();
        loop {
            if let Some(completion) = self.wait_next_complete(Duration::from_secs(1)) {
                return completion;
            }
            log::warn!("cancelled transfer due to timeout, but it has not yet returned");
        }
    }

    /// Clear the endpoint's halt / stall condition.
    ///
    /// Sends a `CLEAR_FEATURE` `ENDPOINT_HALT` control transfer to tell the
    /// device to reset the endpoint's data toggle and clear the halt / stall
    /// condition, and resets the host-side data toggle.
    ///
    /// Use this after receiving
    /// [`TransferError::Stall`][crate::transfer::TransferError::Stall] to clear
    /// the error and resume use of the endpoint.
    ///
    /// This should not be called when transfers are pending on the endpoint.
    pub fn clear_halt(&mut self) -> impl MaybeFuture<Output = Result<(), Error>> {
        self.backend.clear_halt()
    }
}

impl<EpType: BulkOrInterrupt, Dir: EndpointDirection> Debug for Endpoint<EpType, Dir> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Endpoint")
            .field(
                "address",
                &format_args!("0x{:02x}", self.endpoint_address()),
            )
            .field("type", &EpType::TYPE)
            .field("direction", &Dir::DIR)
            .finish()
    }
}

#[test]
fn assert_send_sync() {
    use crate::transfer::{Bulk, In, Interrupt, Out};

    fn require_send_sync<T: Send + Sync>() {}
    require_send_sync::<Interface>();
    require_send_sync::<Device>();
    require_send_sync::<Endpoint<Bulk, In>>();
    require_send_sync::<Endpoint<Bulk, Out>>();
    require_send_sync::<Endpoint<Interrupt, In>>();
    require_send_sync::<Endpoint<Interrupt, Out>>();
}