use std::sync::Weak;
use std::time::Instant;
use base::error;
use base::Clock;
use sync::Mutex;
use usb_util::TransferBuffer;
use usb_util::TransferStatus;
use crate::usb::backend::error::Error as BackendError;
use crate::usb::backend::error::Result as BackendResult;
use crate::usb::backend::fido_backend::constants::USB_TRANSFER_TIMEOUT_MILLIS;
use crate::usb::backend::transfer::BackendTransfer;
use crate::usb::backend::transfer::BackendTransferType;
use crate::usb::backend::transfer::GenericTransferHandle;
pub struct FidoTransfer {
pub buffer: TransferBuffer,
status: TransferStatus,
pub actual_length: usize,
pub endpoint: u8,
submission_time: Instant,
pub callback: Option<Box<dyn Fn(FidoTransfer) + Send + Sync>>,
}
impl FidoTransfer {
pub fn new(endpoint: u8, buffer: TransferBuffer) -> FidoTransfer {
let clock = Clock::new();
FidoTransfer {
buffer,
status: TransferStatus::Error, actual_length: 0,
endpoint,
submission_time: clock.now(),
callback: None,
}
}
pub fn signal_device_lost(&mut self) {
self.status = TransferStatus::NoDevice;
}
pub fn timeout_expired(&self) -> bool {
self.submission_time.elapsed().as_millis() >= USB_TRANSFER_TIMEOUT_MILLIS.into()
}
pub fn complete_transfer(mut self) {
if self.status == TransferStatus::Error {
self.status = TransferStatus::Completed;
}
if let Some(cb) = self.callback.take() {
cb(self);
}
}
}
impl BackendTransfer for FidoTransfer {
fn status(&self) -> TransferStatus {
self.status
}
fn actual_length(&self) -> usize {
self.actual_length
}
fn buffer(&self) -> &TransferBuffer {
&self.buffer
}
fn set_callback<C: 'static + Fn(BackendTransferType) + Send + Sync>(&mut self, cb: C) {
let callback = move |t: FidoTransfer| cb(BackendTransferType::FidoDevice(t));
self.callback = Some(Box::new(callback));
}
}
pub struct FidoTransferHandle {
pub weak_transfer: Weak<Mutex<Option<FidoTransfer>>>,
}
impl GenericTransferHandle for FidoTransferHandle {
fn cancel(&self) -> BackendResult<()> {
let rc_transfer = match self.weak_transfer.upgrade() {
None => {
return Err(BackendError::TransferHandleAlreadyComplete);
}
Some(rc_transfer) => rc_transfer,
};
let mut lock = rc_transfer.lock();
let mut transfer = match lock.take() {
Some(t) => t,
None => {
error!("Transfer has already been lost while being cancelled. Ignore");
return Err(BackendError::TransferHandleAlreadyComplete);
}
};
transfer.status = TransferStatus::Cancelled;
*lock = Some(transfer);
Ok(())
}
}