blob: 329d6ff2a747d8f9c5a04e731d0782e05951166b [file] [log] [blame]
// Copyright 2019 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::cmp;
use std::sync::Arc;
use sync::Mutex;
use super::error::*;
use super::utils::{submit_transfer, update_transfer_state};
use crate::usb::xhci::scatter_gather_buffer::ScatterGatherBuffer;
use crate::usb::xhci::xhci_transfer::{
TransferDirection, XhciTransfer, XhciTransferState, XhciTransferType,
};
use crate::utils::AsyncJobQueue;
use crate::utils::FailHandle;
use sys_util::error;
use usb_util::device_handle::DeviceHandle;
use usb_util::types::{EndpointDirection, EndpointType, ENDPOINT_DIRECTION_OFFSET};
use usb_util::usb_transfer::{
bulk_transfer, interrupt_transfer, BulkTransferBuffer, TransferStatus, UsbTransfer,
};
/// Isochronous, Bulk or Interrupt endpoint.
pub struct UsbEndpoint {
fail_handle: Arc<dyn FailHandle>,
job_queue: Arc<AsyncJobQueue>,
device_handle: Arc<Mutex<DeviceHandle>>,
endpoint_number: u8,
direction: EndpointDirection,
ty: EndpointType,
}
impl UsbEndpoint {
/// Create new endpoint. This function will panic if endpoint type is control.
pub fn new(
fail_handle: Arc<dyn FailHandle>,
job_queue: Arc<AsyncJobQueue>,
device_handle: Arc<Mutex<DeviceHandle>>,
endpoint_number: u8,
direction: EndpointDirection,
ty: EndpointType,
) -> UsbEndpoint {
assert!(ty != EndpointType::Control);
UsbEndpoint {
fail_handle,
job_queue,
device_handle,
endpoint_number,
direction,
ty,
}
}
fn ep_addr(&self) -> u8 {
self.endpoint_number | ((self.direction as u8) << ENDPOINT_DIRECTION_OFFSET)
}
/// Returns true is this endpoint matches number and direction.
pub fn match_ep(&self, endpoint_number: u8, dir: TransferDirection) -> bool {
let self_dir = match self.direction {
EndpointDirection::HostToDevice => TransferDirection::Out,
EndpointDirection::DeviceToHost => TransferDirection::In,
};
self.endpoint_number == endpoint_number && self_dir == dir
}
/// Handle a xhci transfer.
pub fn handle_transfer(&self, transfer: XhciTransfer) -> Result<()> {
let buffer = match transfer
.get_transfer_type()
.map_err(Error::GetXhciTransferType)?
{
XhciTransferType::Normal(buffer) => buffer,
XhciTransferType::Noop => {
return transfer
.on_transfer_complete(&TransferStatus::Completed, 0)
.map_err(Error::TransferComplete);
}
_ => {
error!("unhandled xhci transfer type by usb endpoint");
return transfer
.on_transfer_complete(&TransferStatus::Error, 0)
.map_err(Error::TransferComplete);
}
};
match self.ty {
EndpointType::Bulk => {
self.handle_bulk_transfer(transfer, buffer)?;
}
EndpointType::Interrupt => {
self.handle_interrupt_transfer(transfer, buffer)?;
}
_ => {
return transfer
.on_transfer_complete(&TransferStatus::Error, 0)
.map_err(Error::TransferComplete);
}
}
Ok(())
}
fn handle_bulk_transfer(
&self,
xhci_transfer: XhciTransfer,
buffer: ScatterGatherBuffer,
) -> Result<()> {
let usb_transfer =
bulk_transfer(self.ep_addr(), 0, buffer.len().map_err(Error::BufferLen)?);
self.do_handle_transfer(xhci_transfer, usb_transfer, buffer)
}
fn handle_interrupt_transfer(
&self,
xhci_transfer: XhciTransfer,
buffer: ScatterGatherBuffer,
) -> Result<()> {
let usb_transfer =
interrupt_transfer(self.ep_addr(), 0, buffer.len().map_err(Error::BufferLen)?);
self.do_handle_transfer(xhci_transfer, usb_transfer, buffer)
}
fn do_handle_transfer(
&self,
xhci_transfer: XhciTransfer,
mut usb_transfer: UsbTransfer<BulkTransferBuffer>,
buffer: ScatterGatherBuffer,
) -> Result<()> {
let xhci_transfer = Arc::new(xhci_transfer);
let tmp_transfer = xhci_transfer.clone();
match self.direction {
EndpointDirection::HostToDevice => {
// Read data from ScatterGatherBuffer to a continuous memory.
buffer
.read(usb_transfer.buffer_mut().as_mut_slice())
.map_err(Error::ReadBuffer)?;
usb_debug!(
"out transfer ep_addr {:#x}, buffer len {:?}, data {:#x?}",
self.ep_addr(),
buffer.len(),
usb_transfer.buffer_mut().as_mut_slice()
);
let callback = move |t: UsbTransfer<BulkTransferBuffer>| {
usb_debug!("out transfer callback");
update_transfer_state(&xhci_transfer, &t)?;
let state = xhci_transfer.state().lock();
match *state {
XhciTransferState::Cancelled => {
usb_debug!("transfer has been cancelled");
drop(state);
xhci_transfer
.on_transfer_complete(&TransferStatus::Cancelled, 0)
.map_err(Error::TransferComplete)
}
XhciTransferState::Completed => {
let status = t.status();
let actual_length = t.actual_length();
drop(state);
xhci_transfer
.on_transfer_complete(&status, actual_length as u32)
.map_err(Error::TransferComplete)
}
_ => {
error!("xhci trasfer state (host to device) is invalid");
Err(Error::BadXhciTransferState)
}
}
};
let fail_handle = self.fail_handle.clone();
usb_transfer.set_callback(
move |t: UsbTransfer<BulkTransferBuffer>| match callback(t) {
Ok(_) => {}
Err(e) => {
error!("bulk transfer callback failed: {:?}", e);
fail_handle.fail();
}
},
);
submit_transfer(
self.fail_handle.clone(),
&self.job_queue,
tmp_transfer,
&self.device_handle,
usb_transfer,
)?;
}
EndpointDirection::DeviceToHost => {
usb_debug!(
"in transfer ep_addr {:#x}, buffer len {:?}",
self.ep_addr(),
buffer.len()
);
let _addr = self.ep_addr();
let callback = move |t: UsbTransfer<BulkTransferBuffer>| {
usb_debug!(
"ep {:#x} in transfer data {:?}",
_addr,
t.buffer().as_slice()
);
update_transfer_state(&xhci_transfer, &t)?;
let state = xhci_transfer.state().lock();
match *state {
XhciTransferState::Cancelled => {
usb_debug!("transfer has been cancelled");
drop(state);
xhci_transfer
.on_transfer_complete(&TransferStatus::Cancelled, 0)
.map_err(Error::TransferComplete)
}
XhciTransferState::Completed => {
let status = t.status();
let actual_length = t.actual_length() as usize;
let copied_length = buffer
.write(t.buffer().as_slice())
.map_err(Error::WriteBuffer)?;
let actual_length = cmp::min(actual_length, copied_length);
drop(state);
xhci_transfer
.on_transfer_complete(&status, actual_length as u32)
.map_err(Error::TransferComplete)
}
_ => {
// update state is already invoked. This match should not be in any
// other state.
error!("xhci trasfer state (device to host) is invalid");
Err(Error::BadXhciTransferState)
}
}
};
let fail_handle = self.fail_handle.clone();
usb_transfer.set_callback(
move |t: UsbTransfer<BulkTransferBuffer>| match callback(t) {
Ok(_) => {}
Err(e) => {
error!("bulk transfer callback {:?}", e);
fail_handle.fail();
}
},
);
submit_transfer(
self.fail_handle.clone(),
&self.job_queue,
tmp_transfer,
&self.device_handle,
usb_transfer,
)?;
}
}
Ok(())
}
}