// 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 super::interrupter::{Error as InterrupterError, Interrupter}; use super::scatter_gather_buffer::{Error as BufferError, ScatterGatherBuffer}; use super::usb_hub::{Error as HubError, UsbPort}; use super::xhci_abi::{ AddressedTrb, Error as TrbError, EventDataTrb, SetupStageTrb, TransferDescriptor, TrbCast, TrbCompletionCode, TrbType, }; use super::xhci_regs::MAX_INTERRUPTER; use std::cmp::min; use std::fmt::{self, Display}; use std::mem; use std::sync::{Arc, Weak}; use sync::Mutex; use sys_util::{Error as SysError, EventFd, GuestMemory}; use usb::usb_util::types::UsbRequestSetup; use usb::usb_util::usb_transfer::TransferStatus; #[derive(Debug)] pub enum Error { TrbType(TrbError), CastTrb(TrbError), TransferLength(TrbError), BadTrbType(TrbType), WriteCompletionEvent(SysError), CreateBuffer(BufferError), DetachPort(HubError), SendInterrupt(InterrupterError), SubmitTransfer, } type Result = std::result::Result; impl Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::Error::*; match self { TrbType(e) => write!(f, "cannot get trb type: {}", e), CastTrb(e) => write!(f, "cannot cast trb: {}", e), TransferLength(e) => write!(f, "cannot get transfer length: {}", e), BadTrbType(t) => write!(f, "unexpected trb type: {:?}", t), WriteCompletionEvent(e) => write!(f, "cannot write completion event: {}", e), CreateBuffer(e) => write!(f, "cannot create transfer buffer: {}", e), DetachPort(e) => write!(f, "cannot detach from port: {}", e), SendInterrupt(e) => write!(f, "cannot send interrupter: {}", e), SubmitTransfer => write!(f, "failed to submit transfer to backend"), } } } /// Type of usb endpoints. #[derive(PartialEq, Clone, Copy, Debug)] pub enum TransferDirection { In, Out, Control, } /// Current state of xhci transfer. pub enum XhciTransferState { Created, /// When transfer is submitted, it will contain a transfer callback, which should be invoked /// when the transfer is cancelled. Submitted { cancel_callback: Box, }, Cancelling, Cancelled, Completed, } impl XhciTransferState { /// Try to cancel this transfer, if it's possible. pub fn try_cancel(&mut self) { match mem::replace(self, XhciTransferState::Created) { XhciTransferState::Submitted { mut cancel_callback, } => { *self = XhciTransferState::Cancelling; cancel_callback(); } XhciTransferState::Cancelling => { error!("Another cancellation is already issued."); } _ => { *self = XhciTransferState::Cancelled; } } } } /// Type of a transfer received handled by transfer ring. pub enum XhciTransferType { // Normal means bulk transfer or interrupt transfer, depending on endpoint type. // See spec 4.11.2.1. Normal(ScatterGatherBuffer), // See usb spec for setup stage, data stage and status stage, // see xHCI spec 4.11.2.2 for corresponding trbs. SetupStage(UsbRequestSetup), DataStage(ScatterGatherBuffer), StatusStage, // See xHCI spec 4.11.2.3. Isochronous(ScatterGatherBuffer), // See xHCI spec 6.4.1.4. Noop, } impl XhciTransferType { /// Analyze transfer descriptor and return transfer type. pub fn new(mem: GuestMemory, td: TransferDescriptor) -> Result { // We can figure out transfer type from the first trb. // See transfer descriptor description in xhci spec for more details. match td[0].trb.trb_type().map_err(Error::TrbType)? { TrbType::Normal => { let buffer = ScatterGatherBuffer::new(mem, td).map_err(Error::CreateBuffer)?; Ok(XhciTransferType::Normal(buffer)) } TrbType::SetupStage => { let trb = td[0].trb.cast::().map_err(Error::CastTrb)?; Ok(XhciTransferType::SetupStage(UsbRequestSetup::new( trb.get_request_type(), trb.get_request(), trb.get_value(), trb.get_index(), trb.get_length(), ))) } TrbType::DataStage => { let buffer = ScatterGatherBuffer::new(mem, td).map_err(Error::CreateBuffer)?; Ok(XhciTransferType::DataStage(buffer)) } TrbType::StatusStage => Ok(XhciTransferType::StatusStage), TrbType::Isoch => { let buffer = ScatterGatherBuffer::new(mem, td).map_err(Error::CreateBuffer)?; Ok(XhciTransferType::Isochronous(buffer)) } TrbType::Noop => Ok(XhciTransferType::Noop), t => Err(Error::BadTrbType(t)), } } } /// Xhci Transfer manager holds reference to all ongoing transfers. Can cancel them all if /// needed. #[derive(Clone)] pub struct XhciTransferManager { transfers: Arc>>>>, } impl XhciTransferManager { /// Create a new manager. pub fn new() -> XhciTransferManager { XhciTransferManager { transfers: Arc::new(Mutex::new(Vec::new())), } } /// Build a new XhciTransfer. Endpoint id is the id in xHCI device slot. pub fn create_transfer( &self, mem: GuestMemory, port: Arc, interrupter: Arc>, slot_id: u8, endpoint_id: u8, transfer_trbs: TransferDescriptor, completion_event: EventFd, ) -> XhciTransfer { assert!(!transfer_trbs.is_empty()); let transfer_dir = { if endpoint_id == 0 { TransferDirection::Control } else if (endpoint_id % 2) == 0 { TransferDirection::Out } else { TransferDirection::In } }; let t = XhciTransfer { manager: self.clone(), state: Arc::new(Mutex::new(XhciTransferState::Created)), mem, port, interrupter, transfer_completion_event: completion_event, slot_id, endpoint_id, transfer_dir, transfer_trbs, }; self.transfers.lock().push(Arc::downgrade(&t.state)); t } /// Cancel all current transfers. pub fn cancel_all(&self) { self.transfers.lock().iter().for_each(|t| { let state = match t.upgrade() { Some(state) => state, None => { error!("transfer is already cancelled or finished"); return; } }; state.lock().try_cancel(); }); } fn remove_transfer(&self, t: &Arc>) { let mut transfers = self.transfers.lock(); match transfers.iter().position(|wt| match wt.upgrade() { Some(wt) => Arc::ptr_eq(&wt, t), None => false, }) { None => error!("attempted to remove unknow transfer"), Some(i) => { transfers.swap_remove(i); } } } } /// Xhci transfer denotes a transfer initiated by guest os driver. It will be submitted to a /// XhciBackendDevice. pub struct XhciTransfer { manager: XhciTransferManager, state: Arc>, mem: GuestMemory, port: Arc, interrupter: Arc>, slot_id: u8, // id of endpoint in device slot. endpoint_id: u8, transfer_dir: TransferDirection, transfer_trbs: TransferDescriptor, transfer_completion_event: EventFd, } impl Drop for XhciTransfer { fn drop(&mut self) { self.manager.remove_transfer(&self.state); } } impl fmt::Debug for XhciTransfer { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "xhci_transfer slot id: {}, endpoint id {}, transfer_dir {:?}, transfer_trbs {:?}", self.slot_id, self.endpoint_id, self.transfer_dir, self.transfer_trbs ) } } impl XhciTransfer { /// Get state of this transfer. pub fn state(&self) -> &Arc> { &self.state } /// Get transfer type. pub fn get_transfer_type(&self) -> Result { XhciTransferType::new(self.mem.clone(), self.transfer_trbs.clone()) } /// Get endpoint number. pub fn get_endpoint_number(&self) -> u8 { // See spec 4.5.1 for dci. self.endpoint_id / 2 } /// get transfer direction. pub fn get_transfer_dir(&self) -> TransferDirection { self.transfer_dir } /// This functions should be invoked when transfer is completed (or failed). pub fn on_transfer_complete( &self, status: &TransferStatus, bytes_transferred: u32, ) -> Result<()> { match status { TransferStatus::NoDevice => { usb_debug!("device disconnected, detaching from port"); // If the device is gone, we don't need to send transfer completion event, cause we // are going to destroy everything related to this device anyway. self.port.detach().map_err(Error::DetachPort)?; return Ok(()); } TransferStatus::Cancelled => { // TODO(jkwang) According to the spec, we should send a stopped event here. But // kernel driver does not do anything meaningful when it sees a stopped event. return self .transfer_completion_event .write(1) .map_err(Error::WriteCompletionEvent); } TransferStatus::Completed => { self.transfer_completion_event .write(1) .map_err(Error::WriteCompletionEvent)?; } _ => { // Transfer failed, we are not handling this correctly yet. Guest kernel might see // short packets for in transfer and might think control transfer is successful. It // will eventually find out device is in a wrong state. self.transfer_completion_event .write(1) .map_err(Error::WriteCompletionEvent)?; } } let mut edtla: u32 = 0; // TODO(jkwang) Send event based on Status. // As noted in xHCI spec 4.11.3.1 // Transfer Event Trb only occurs under the following conditions: // 1. If the Interrupt On Completion flag is set. // 2. When a short transfer occurs during the execution of a Transfer TRB and the // Interrupter-on-Short Packet flag is set. // 3. If an error occurs during the execution of a Transfer Trb. for atrb in &self.transfer_trbs { edtla += atrb.trb.transfer_length().map_err(Error::TransferLength)?; if atrb.trb.interrupt_on_completion() { // For details about event data trb and EDTLA, see spec 4.11.5.2. if atrb.trb.trb_type().map_err(Error::TrbType)? == TrbType::EventData { let tlength = min(edtla, bytes_transferred); self.interrupter .lock() .send_transfer_event_trb( TrbCompletionCode::Success, atrb.trb .cast::() .map_err(Error::CastTrb)? .get_event_data(), tlength, true, self.slot_id, self.endpoint_id, ) .map_err(Error::SendInterrupt)?; } else { // For Short Transfer details, see xHCI spec 4.10.1.1. if edtla > bytes_transferred { usb_debug!("on transfer complete short packet"); let residual_transfer_length = edtla - bytes_transferred; self.interrupter .lock() .send_transfer_event_trb( TrbCompletionCode::ShortPacket, atrb.gpa, residual_transfer_length, true, self.slot_id, self.endpoint_id, ) .map_err(Error::SendInterrupt)?; } else { usb_debug!("on transfer complete success"); self.interrupter .lock() .send_transfer_event_trb( TrbCompletionCode::Success, atrb.gpa, 0, // transfer length true, self.slot_id, self.endpoint_id, ) .map_err(Error::SendInterrupt)?; } } } } Ok(()) } /// Send this transfer to backend if it's a valid transfer. pub fn send_to_backend_if_valid(self) -> Result<()> { if self.validate_transfer()? { // Backend should invoke on transfer complete when transfer is completed. let port = self.port.clone(); let mut backend = port.get_backend_device(); match &mut *backend { Some(backend) => backend .submit_transfer(self) .map_err(|_| Error::SubmitTransfer)?, None => { error!("backend is already disconnected"); self.transfer_completion_event .write(1) .map_err(Error::WriteCompletionEvent)?; } } } else { error!("invalid td on transfer ring"); self.transfer_completion_event .write(1) .map_err(Error::WriteCompletionEvent)?; } Ok(()) } // Check each trb in the transfer descriptor for invalid or out of bounds // parameters. Returns true iff the transfer descriptor is valid. fn validate_transfer(&self) -> Result { let mut valid = true; for atrb in &self.transfer_trbs { if !trb_is_valid(&atrb) { self.interrupter .lock() .send_transfer_event_trb( TrbCompletionCode::TrbError, atrb.gpa, 0, false, self.slot_id, self.endpoint_id, ) .map_err(Error::SendInterrupt)?; valid = false; } } Ok(valid) } } fn trb_is_valid(atrb: &AddressedTrb) -> bool { let can_be_in_transfer_ring = match atrb.trb.can_be_in_transfer_ring() { Ok(v) => v, Err(e) => { error!("unknown error {:?}", e); return false; } }; can_be_in_transfer_ring && (atrb.trb.interrupter_target() < MAX_INTERRUPTER) }