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
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use usb_util::Transfer;
use usb_util::TransferBuffer;
use usb_util::TransferStatus;
use usb_util::UsbRequestSetup;

use crate::usb::backend::endpoint::ControlEndpointState;
use crate::usb::backend::error::Result;
use crate::usb::backend::fido_backend::transfer::FidoTransfer;

/// BackendTransferHandle is a wrapper structure around a generic transfer handle whose
/// implementation depends on the backend type that is being used.
pub struct BackendTransferHandle {
    handle: Box<dyn GenericTransferHandle>,
}

impl BackendTransferHandle {
    pub fn new(handle: impl GenericTransferHandle + 'static) -> Self {
        BackendTransferHandle {
            handle: Box::new(handle),
        }
    }

    pub fn cancel(&self) -> Result<()> {
        self.handle.cancel()
    }
}

pub enum BackendTransferType {
    HostDevice(Transfer),
    FidoDevice(FidoTransfer),
}

/// The backend transfer trait implemention is the interface of a generic transfer structure that
/// each backend type should implement to be compatible with the generic backend device provider
/// logic.
pub trait BackendTransfer {
    /// Returns the status of the transfer in a `TransferStatus` enum
    fn status(&self) -> TransferStatus;
    /// Returns the actual amount of data transferred, which may be less than the original length.
    fn actual_length(&self) -> usize;
    /// Returns a reference to the `TransferBuffer` object.
    fn buffer(&self) -> &TransferBuffer;
    /// Sets an optional callback on the transfer to be called when the transfer completes.
    fn set_callback<C: 'static + Fn(BackendTransferType) + Send + Sync>(&mut self, cb: C);
}

// TODO(morg): refactor with multi_dispatch
impl BackendTransfer for BackendTransferType {
    fn status(&self) -> TransferStatus {
        match self {
            BackendTransferType::HostDevice(transfer) => BackendTransfer::status(transfer),
            BackendTransferType::FidoDevice(transfer) => BackendTransfer::status(transfer),
        }
    }

    fn actual_length(&self) -> usize {
        match self {
            BackendTransferType::HostDevice(transfer) => BackendTransfer::actual_length(transfer),
            BackendTransferType::FidoDevice(transfer) => BackendTransfer::actual_length(transfer),
        }
    }

    fn buffer(&self) -> &TransferBuffer {
        match self {
            BackendTransferType::HostDevice(transfer) => BackendTransfer::buffer(transfer),
            BackendTransferType::FidoDevice(transfer) => BackendTransfer::buffer(transfer),
        }
    }

    fn set_callback<C: 'static + Fn(BackendTransferType) + Send + Sync>(&mut self, cb: C) {
        match self {
            BackendTransferType::HostDevice(transfer) => {
                BackendTransfer::set_callback(transfer, cb)
            }
            BackendTransferType::FidoDevice(transfer) => {
                BackendTransfer::set_callback(transfer, cb)
            }
        }
    }
}

/// Generic transfer handle is a generic handle that allows for cancellation of in-flight
/// transfers. It should be implemented by all backends that need to be plugged into a generic
/// BackendTransferHandle structure.
pub trait GenericTransferHandle: Send {
    /// All objects that implement this method need to make sure `cancel()` is safe to call
    /// multiple times as its invocation should be idempotent. A transfer that has already been
    /// canceled ought not to error if it gets canceled again.
    fn cancel(&self) -> Result<()>;
}

#[derive(Copy, Clone)]
pub struct ControlTransferState {
    pub ctl_ep_state: ControlEndpointState,
    pub control_request_setup: UsbRequestSetup,
    pub executed: bool,
}