From 257d004b0ce0376e39fb8f94d2347eaf0e1d65ff Mon Sep 17 00:00:00 2001 From: Jingkui Wang Date: Fri, 8 Mar 2019 13:21:02 -0800 Subject: [PATCH] usb: add host backend Host backend implement backend device. It will attach a read device to xhci controller. Also squashed from ce6b35, author: zachr@. CQ-DEPEND=CL:1510820 BUG=chromium:831850 TEST=local build Change-Id: Idcf2d7d6aca92de9859b7c38d1bf1d98032eae91 Reviewed-on: https://chromium-review.googlesource.com/1512761 Commit-Ready: Jingkui Wang Tested-by: kokoro Tested-by: Zach Reizner Reviewed-by: Zach Reizner --- devices/src/usb/host_backend/context.rs | 157 +++++ devices/src/usb/host_backend/error.rs | 78 +++ .../host_backend_device_provider.rs | 324 +++++++++++ devices/src/usb/host_backend/host_device.rs | 541 ++++++++++++++++++ devices/src/usb/host_backend/hotplug.rs | 45 ++ devices/src/usb/host_backend/mod.rs | 11 + devices/src/usb/host_backend/usb_endpoint.rs | 253 ++++++++ devices/src/usb/host_backend/utils.rs | 109 ++++ devices/src/usb/mod.rs | 3 + devices/src/usb/xhci/mod.rs | 12 +- devices/src/usb/xhci/xhci_transfer.rs | 4 +- vm_control/Cargo.toml | 3 + vm_control/src/lib.rs | 60 +- 13 files changed, 1588 insertions(+), 12 deletions(-) create mode 100644 devices/src/usb/host_backend/context.rs create mode 100644 devices/src/usb/host_backend/error.rs create mode 100644 devices/src/usb/host_backend/host_backend_device_provider.rs create mode 100644 devices/src/usb/host_backend/host_device.rs create mode 100644 devices/src/usb/host_backend/hotplug.rs create mode 100644 devices/src/usb/host_backend/mod.rs create mode 100644 devices/src/usb/host_backend/usb_endpoint.rs create mode 100644 devices/src/usb/host_backend/utils.rs diff --git a/devices/src/usb/host_backend/context.rs b/devices/src/usb/host_backend/context.rs new file mode 100644 index 0000000000..3fd8d3d602 --- /dev/null +++ b/devices/src/usb/host_backend/context.rs @@ -0,0 +1,157 @@ +// 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::error::*; +use std::os::raw::c_short; +use std::os::unix::io::RawFd; +use std::sync::{Arc, Weak}; +use sys_util::WatchingEvents; +use usb::usb_util::hotplug::UsbHotplugHandler; +use usb::usb_util::libusb_context::{LibUsbContext, LibUsbPollfdChangeHandler}; +use usb::usb_util::libusb_device::LibUsbDevice; +use utils::{EventHandler, EventLoop}; +use vm_control::MaybeOwnedFd; + +/// Context wraps libusb context with libusb event handling. +pub struct Context { + context: LibUsbContext, + event_loop: Arc, + event_handler: Arc, +} + +impl Context { + /// Create a new context. + #[cfg(not(feature = "sandboxed-libusb"))] + pub fn new(event_loop: Arc) -> Result { + let context = LibUsbContext::new().map_err(Error::CreateLibUsbContext)?; + let ctx = Context { + context: context.clone(), + event_loop, + event_handler: Arc::new(LibUsbEventHandler { + context: context.clone(), + }), + }; + ctx.init_event_handler()?; + Ok(ctx) + } + + #[cfg(feature = "sandboxed-libusb")] + pub fn new(event_loop: Arc) -> Result { + let context = LibUsbContext::new_jailed().map_err(Error::CreateLibUsbContext)?; + let ctx = Context { + context: context.clone(), + event_loop, + event_handler: Arc::new(LibUsbEventHandler { + context: context.clone(), + }), + }; + ctx.init_event_handler()?; + Ok(ctx) + } + + pub fn set_hotplug_handler(&self, handler: H) { + if let Err(e) = self.context.set_hotplug_cb(handler) { + error!("cannot set hotplug handler: {:?}", e); + } + } + + fn init_event_handler(&self) -> Result<()> { + for pollfd in self.context.get_pollfd_iter() { + usb_debug!("event loop add event {} events handler", pollfd.fd); + self.event_loop + .add_event( + &MaybeOwnedFd::Borrowed(pollfd.fd), + WatchingEvents::new(pollfd.events as u32), + Arc::downgrade(&self.event_handler), + ) + .map_err(Error::AddToEventLoop)?; + } + + self.context + .set_pollfd_notifiers(Box::new(PollfdChangeHandler { + event_loop: self.event_loop.clone(), + event_handler: Arc::downgrade(&self.event_handler), + })); + Ok(()) + } + + /// Get libusb device with matching bus, addr, vid and pid. + #[cfg(not(feature = "sandboxed-libusb"))] + pub fn get_device(&self, bus: u8, addr: u8, vid: u16, pid: u16) -> Option { + let device_iter = match self.context.get_device_iter() { + Ok(iter) => iter, + Err(e) => { + error!("could not get libusb device iterator: {:?}", e); + return None; + } + }; + for device in device_iter { + if device.get_bus_number() == bus && device.get_address() == addr { + if let Ok(descriptor) = device.get_device_descriptor() { + if descriptor.idProduct == pid && descriptor.idVendor == vid { + return Some(device); + } + } + } + } + error!("device not found bus {}, addr {}", bus, addr); + None + } + + #[cfg(feature = "sandboxed-libusb")] + pub fn get_device(&self, fd: std::fs::File) -> Option { + match self.context.get_device_from_fd(fd) { + Ok(dev) => Some(dev), + Err(e) => { + error!("could not build device from fd: {:?}", e); + None + } + } + } +} + +struct LibUsbEventHandler { + context: LibUsbContext, +} + +impl EventHandler for LibUsbEventHandler { + fn on_event(&self, _fd: RawFd) -> std::result::Result<(), ()> { + self.context.handle_events_nonblock(); + Ok(()) + } +} + +struct PollfdChangeHandler { + event_loop: Arc, + event_handler: Weak, +} + +impl LibUsbPollfdChangeHandler for PollfdChangeHandler { + fn add_poll_fd(&self, fd: RawFd, events: c_short) { + self.event_loop.add_event( + &MaybeOwnedFd::Borrowed(fd), + WatchingEvents::new(events as u32), + self.event_handler.clone(), + ); + } + + fn remove_poll_fd(&self, fd: RawFd) { + if let Some(h) = self.event_handler.upgrade() { + match h.on_event(0) { + Ok(()) => {} + Err(e) => error!("cannot handle event: {:?}", e), + } + } + match self + .event_loop + .remove_event_for_fd(&MaybeOwnedFd::Borrowed(fd)) + { + Ok(_) => {} + Err(e) => error!( + "failed to remove poll change handler from event loop: {}", + e + ), + } + } +} diff --git a/devices/src/usb/host_backend/error.rs b/devices/src/usb/host_backend/error.rs new file mode 100644 index 0000000000..b414c97e73 --- /dev/null +++ b/devices/src/usb/host_backend/error.rs @@ -0,0 +1,78 @@ +// 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 msg_socket::MsgError; +use std::fmt::{self, Display}; +use usb::usb_util::error::Error as UsbUtilError; +use usb::xhci::scatter_gather_buffer::Error as BufferError; +use usb::xhci::xhci_transfer::Error as XhciTransferError; +use utils::Error as UtilsError; + +#[derive(Debug)] +pub enum Error { + AddToEventLoop(UtilsError), + StartAsyncJobQueue(UtilsError), + QueueAsyncJob(UtilsError), + CreateLibUsbContext(UsbUtilError), + GetActiveConfig(UsbUtilError), + SetActiveConfig(UsbUtilError), + SetInterfaceAltSetting(UsbUtilError), + ClearHalt(UsbUtilError), + GetEndpointType, + CreateControlSock(std::io::Error), + SetupControlSock(std::io::Error), + ReadControlSock(MsgError), + WriteControlSock(MsgError), + GetXhciTransferType(XhciTransferError), + TransferComplete(XhciTransferError), + ReadBuffer(BufferError), + WriteBuffer(BufferError), + BufferLen(BufferError), + /// Cannot get interface descriptor for (interface, altsetting). + GetInterfaceDescriptor((i32, u16)), + GetEndpointDescriptor(u8), + GetRequestSetupType, + BadXhciTransferState, + BadBackendProviderState, +} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::Error::*; + + match self { + AddToEventLoop(e) => write!(f, "failed to add to event loop: {}", e), + StartAsyncJobQueue(e) => write!(f, "failed to start async job queue: {}", e), + QueueAsyncJob(e) => write!(f, "failed to queue async job: {}", e), + CreateLibUsbContext(e) => write!(f, "failed to create libusb context: {:?}", e), + GetActiveConfig(e) => write!(f, "failed to get active config: {:?}", e), + SetActiveConfig(e) => write!(f, "failed to set active config: {:?}", e), + SetInterfaceAltSetting(e) => write!(f, "failed to set interface alt setting: {:?}", e), + ClearHalt(e) => write!(f, "failed to clear halt: {:?}", e), + GetEndpointType => write!(f, "failed to get endpoint type"), + CreateControlSock(e) => write!(f, "failed to create contro sock: {}", e), + SetupControlSock(e) => write!(f, "failed to setup control sock: {}", e), + ReadControlSock(e) => write!(f, "failed to read control sock: {}", e), + WriteControlSock(e) => write!(f, "failed to write control sock: {}", e), + GetXhciTransferType(e) => write!(f, "failed to get xhci transfer type: {}", e), + TransferComplete(e) => write!(f, "xhci transfer completed: {}", e), + ReadBuffer(e) => write!(f, "failed to read buffer: {}", e), + WriteBuffer(e) => write!(f, "failed to write buffer: {}", e), + BufferLen(e) => write!(f, "failed to get buffer length: {}", e), + GetInterfaceDescriptor((i, alt_setting)) => write!( + f, + "failed to get interface descriptor for interface {}, alt setting {}", + i, alt_setting + ), + GetEndpointDescriptor(ep_idx) => { + write!(f, "failed to get endpoint descriptor for ep: {}", ep_idx) + } + GetRequestSetupType => write!(f, "failed to get request setup"), + BadXhciTransferState => write!(f, "xhci transfer is in a bad state"), + BadBackendProviderState => write!(f, "backend provider is in a bad state"), + } + } +} + +pub type Result = std::result::Result; diff --git a/devices/src/usb/host_backend/host_backend_device_provider.rs b/devices/src/usb/host_backend/host_backend_device_provider.rs new file mode 100644 index 0000000000..95b52fde9a --- /dev/null +++ b/devices/src/usb/host_backend/host_backend_device_provider.rs @@ -0,0 +1,324 @@ +// 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::sync::Arc; + +use super::context::Context; +use super::error::*; +use super::host_device::HostDevice; +use super::hotplug::HotplugHandler; +use msg_socket::{MsgReceiver, MsgSender, MsgSocket}; +use std::mem; +use std::os::unix::io::{AsRawFd, RawFd}; +use std::time::Duration; +use sys_util::net::UnixSeqpacket; +use sys_util::WatchingEvents; +use usb::xhci::usb_hub::UsbHub; +use usb::xhci::xhci_backend_device_provider::XhciBackendDeviceProvider; +use utils::AsyncJobQueue; +use utils::{EventHandler, EventLoop, FailHandle}; +use vm_control::{UsbControlCommand, UsbControlResult, UsbControlSocket}; + +const SOCKET_TIMEOUT_MS: u64 = 2000; + +/// Host backend device provider is a xhci backend device provider that would provide pass through +/// devices. +pub enum HostBackendDeviceProvider { + // The provider is created but not yet started. + Created { + sock: MsgSocket, + }, + // The provider is started on an event loop. + Started { + inner: Arc, + }, + // The provider has failed. + Failed, +} + +impl HostBackendDeviceProvider { + pub fn new() -> Result<(UsbControlSocket, HostBackendDeviceProvider)> { + let (child_sock, control_sock) = UnixSeqpacket::pair().map_err(Error::CreateControlSock)?; + control_sock + .set_write_timeout(Some(Duration::from_millis(SOCKET_TIMEOUT_MS))) + .map_err(Error::SetupControlSock)?; + control_sock + .set_read_timeout(Some(Duration::from_millis(SOCKET_TIMEOUT_MS))) + .map_err(Error::SetupControlSock)?; + + let provider = HostBackendDeviceProvider::Created { + sock: MsgSocket::new(child_sock), + }; + Ok((MsgSocket::new(control_sock), provider)) + } + + fn start_helper( + &mut self, + fail_handle: Arc, + event_loop: Arc, + hub: Arc, + ) -> Result<()> { + match mem::replace(self, HostBackendDeviceProvider::Failed) { + HostBackendDeviceProvider::Created { sock } => { + let ctx = Context::new(event_loop.clone())?; + let hotplug_handler = HotplugHandler::new(hub.clone()); + ctx.set_hotplug_handler(hotplug_handler); + let job_queue = + AsyncJobQueue::init(&event_loop).map_err(Error::StartAsyncJobQueue)?; + let inner = Arc::new(ProviderInner::new(fail_handle, job_queue, ctx, sock, hub)); + let handler: Arc = inner.clone(); + event_loop + .add_event( + &inner.sock, + WatchingEvents::empty().set_read(), + Arc::downgrade(&handler), + ) + .map_err(Error::AddToEventLoop)?; + *self = HostBackendDeviceProvider::Started { inner }; + Ok(()) + } + HostBackendDeviceProvider::Started { inner: _ } => { + error!("Host backend provider has already started"); + Err(Error::BadBackendProviderState) + } + HostBackendDeviceProvider::Failed => { + error!("Host backend provider has already failed"); + Err(Error::BadBackendProviderState) + } + } + } +} + +impl XhciBackendDeviceProvider for HostBackendDeviceProvider { + fn start( + &mut self, + fail_handle: Arc, + event_loop: Arc, + hub: Arc, + ) -> std::result::Result<(), ()> { + self.start_helper(fail_handle, event_loop, hub) + .map_err(|e| { + error!("failed to start host backend device provider: {}", e); + () + }) + } + + fn keep_fds(&self) -> Vec { + match self { + HostBackendDeviceProvider::Created { sock } => vec![sock.as_raw_fd()], + _ => { + error!( + "Trying to get keepfds when HostBackendDeviceProvider is not in created state" + ); + vec![] + } + } + } +} + +/// ProviderInner listens to control socket. +pub struct ProviderInner { + fail_handle: Arc, + job_queue: Arc, + ctx: Context, + sock: MsgSocket, + usb_hub: Arc, +} + +impl ProviderInner { + fn new( + fail_handle: Arc, + job_queue: Arc, + ctx: Context, + sock: MsgSocket, + usb_hub: Arc, + ) -> ProviderInner { + ProviderInner { + fail_handle, + job_queue, + ctx, + sock, + usb_hub, + } + } + + fn on_event_helper(&self, _fd: RawFd) -> Result<()> { + let cmd = self.sock.recv().map_err(Error::ReadControlSock)?; + match cmd { + UsbControlCommand::AttachDevice { + bus, + addr, + vid, + pid, + fd: usb_fd, + } => { + let _ = usb_fd; + #[cfg(not(feature = "sandboxed-libusb"))] + let device = match self.ctx.get_device(bus, addr, vid, pid) { + Some(d) => d, + None => { + error!( + "cannot get device bus: {}, addr: {}, vid: {}, pid: {}", + bus, addr, vid, pid + ); + // The send failure will be logged, but event loop still think the event is + // handled. + let _ = self + .sock + .send(&UsbControlResult::NoSuchDevice) + .map_err(Error::WriteControlSock)?; + return Ok(()); + } + }; + #[cfg(feature = "sandboxed-libusb")] + let (device, device_handle) = { + use vm_control::MaybeOwnedFd; + + let usb_file = match usb_fd { + Some(MaybeOwnedFd::Owned(file)) => file, + _ => { + let _ = self + .sock + .send(&UsbControlResult::FailedToOpenDevice) + .map_err(Error::WriteControlSock); + return Ok(()); + } + }; + + let device_fd = usb_file.as_raw_fd(); + + let device = match self.ctx.get_device(usb_file) { + Some(d) => d, + None => { + error!( + "cannot get device bus: {}, addr: {}, vid: {}, pid: {}", + bus, addr, vid, pid + ); + // The send failure will be logged, but event loop still think the event + // is handled. + let _ = self + .sock + .send(&UsbControlResult::NoSuchDevice) + .map_err(Error::WriteControlSock); + return Ok(()); + } + }; + + let device_handle = { + // This is safe only when fd is an fd of the current device. + match unsafe { device.open_fd(device_fd) } { + Ok(handle) => handle, + Err(e) => { + error!("fail to open device: {:?}", e); + // The send failure will be logged, but event loop still think + // the event is handled. + let _ = self + .sock + .send(&UsbControlResult::FailedToOpenDevice) + .map_err(Error::WriteControlSock); + return Ok(()); + } + } + }; + (device, device_handle) + }; + + #[cfg(not(feature = "sandboxed-libusb"))] + let device_handle = match device.open() { + Ok(handle) => handle, + Err(e) => { + error!("fail to open device: {:?}", e); + // The send failure will be logged, but event loop still think the event is + // handled. + let _ = self + .sock + .send(&UsbControlResult::FailedToOpenDevice) + .map_err(Error::WriteControlSock); + return Ok(()); + } + }; + let device = Box::new(HostDevice::new( + self.fail_handle.clone(), + self.job_queue.clone(), + device, + device_handle, + )); + let port = self.usb_hub.connect_backend(device); + match port { + Ok(port) => { + // The send failure will be logged, but event loop still think the event is + // handled. + let _ = self + .sock + .send(&UsbControlResult::Ok { port }) + .map_err(Error::WriteControlSock); + } + Err(e) => { + error!("failed to connect device to hub: {}", e); + // The send failure will be logged, but event loop still think the event is + // handled. + let _ = self + .sock + .send(&UsbControlResult::NoAvailablePort) + .map_err(Error::WriteControlSock); + } + } + Ok(()) + } + UsbControlCommand::DetachDevice { port } => { + match self.usb_hub.disconnect_port(port) { + Ok(()) => { + // The send failure will be logged, but event loop still think the event is + // handled. + let _ = self + .sock + .send(&UsbControlResult::Ok { port }) + .map_err(Error::WriteControlSock); + } + Err(e) => { + error!("failed to disconnect device from port {}: {}", port, e); + // The send failure will be logged, but event loop still think the event is + // handled. + let _ = self + .sock + .send(&UsbControlResult::NoSuchDevice) + .map_err(Error::WriteControlSock); + } + } + Ok(()) + } + UsbControlCommand::ListDevice { port } => { + let port_number = port; + let result = match self.usb_hub.get_port(port_number) { + Some(port) => match *port.get_backend_device() { + Some(ref device) => { + let vid = device.get_vid(); + let pid = device.get_pid(); + UsbControlResult::Device { + port: port_number, + vid, + pid, + } + } + None => UsbControlResult::NoSuchDevice, + }, + None => UsbControlResult::NoSuchPort, + }; + // The send failure will be logged, but event loop still think the event is + // handled. + let _ = self.sock.send(&result).map_err(Error::WriteControlSock); + Ok(()) + } + } + } +} + +impl EventHandler for ProviderInner { + fn on_event(&self, fd: RawFd) -> std::result::Result<(), ()> { + self.on_event_helper(fd).map_err(|e| { + error!("host backend device provider failed: {}", e); + () + }) + } +} diff --git a/devices/src/usb/host_backend/host_device.rs b/devices/src/usb/host_backend/host_device.rs new file mode 100644 index 0000000000..ae3faa014f --- /dev/null +++ b/devices/src/usb/host_backend/host_device.rs @@ -0,0 +1,541 @@ +// 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::mem::drop; +use std::sync::Arc; +use sync::Mutex; + +use super::error::*; +use super::usb_endpoint::UsbEndpoint; +use super::utils::{submit_transfer, update_transfer_state}; +use std::collections::HashMap; +use usb::usb_util::device_handle::DeviceHandle; +use usb::usb_util::error::Error as LibUsbError; +use usb::usb_util::libusb_device::LibUsbDevice; +use usb::usb_util::types::{ + ControlRequestDataPhaseTransferDirection, ControlRequestRecipient, ControlRequestType, + StandardControlRequest, UsbRequestSetup, +}; +use usb::usb_util::usb_transfer::{ + control_transfer, ControlTransferBuffer, TransferStatus, UsbTransfer, +}; +use usb::xhci::scatter_gather_buffer::ScatterGatherBuffer; +use usb::xhci::xhci_backend_device::{BackendType, UsbDeviceAddress, XhciBackendDevice}; +use usb::xhci::xhci_transfer::{XhciTransfer, XhciTransferState, XhciTransferType}; +use utils::AsyncJobQueue; +use utils::FailHandle; + +#[derive(PartialEq)] +pub enum ControlEndpointState { + /// Control endpoint should receive setup stage next. + SetupStage, + /// Control endpoint should receive data stage next. + DataStage, + /// Control endpoint should receive status stage next. + StatusStage, +} + +// Types of host to device control requests. We want to handle it use libusb functions instead of +// control transfers. +enum HostToDeviceControlRequest { + SetAddress, + SetConfig, + SetInterface, + ClearFeature, + // It could still be some standard control request. + Other, +} + +impl HostToDeviceControlRequest { + /// Analyze request setup. + pub fn analyze_request_setup( + request_setup: &UsbRequestSetup, + ) -> Result { + match request_setup.get_type().ok_or(Error::GetRequestSetupType)? { + ControlRequestType::Standard => {} + _ => return Ok(HostToDeviceControlRequest::Other), + }; + + let recipient = request_setup.get_recipient(); + let standard_request = request_setup.get_standard_request(); + + match (recipient, standard_request) { + (ControlRequestRecipient::Device, Some(StandardControlRequest::SetAddress)) => { + Ok(HostToDeviceControlRequest::SetAddress) + } + + (ControlRequestRecipient::Device, Some(StandardControlRequest::SetConfiguration)) => { + Ok(HostToDeviceControlRequest::SetConfig) + } + + (ControlRequestRecipient::Interface, Some(StandardControlRequest::SetInterface)) => { + Ok(HostToDeviceControlRequest::SetInterface) + } + + (ControlRequestRecipient::Endpoint, Some(StandardControlRequest::ClearFeature)) => { + Ok(HostToDeviceControlRequest::ClearFeature) + } + _ => Ok(HostToDeviceControlRequest::Other), + } + } +} + +/// Host device is a device connected to host. +pub struct HostDevice { + fail_handle: Arc, + // Endpoints only contains data endpoints (1 to 30). Control transfers are handled at device + // level. + endpoints: Vec, + device: LibUsbDevice, + device_handle: Arc>, + ctl_ep_state: ControlEndpointState, + alt_settings: HashMap, + claimed_interfaces: Vec, + control_request_setup: UsbRequestSetup, + buffer: Option, + job_queue: Arc, +} + +impl Drop for HostDevice { + fn drop(&mut self) { + self.release_interfaces(); + } +} + +impl HostDevice { + /// Create a new host device. + pub fn new( + fail_handle: Arc, + job_queue: Arc, + device: LibUsbDevice, + device_handle: DeviceHandle, + ) -> HostDevice { + let device = HostDevice { + fail_handle, + endpoints: vec![], + device, + device_handle: Arc::new(Mutex::new(device_handle)), + ctl_ep_state: ControlEndpointState::SetupStage, + alt_settings: HashMap::new(), + claimed_interfaces: vec![], + control_request_setup: UsbRequestSetup::new(0, 0, 0, 0, 0), + buffer: None, + job_queue, + }; + device + } + + fn get_interface_number_of_active_config(&self) -> i32 { + match self.device.get_active_config_descriptor() { + Err(LibUsbError::NotFound) => { + usb_debug!("device is in unconfigured state"); + 0 + } + Err(e) => { + // device might be disconnected now. + error!("unexpected error: {:?}", e); + 0 + } + Ok(descriptor) => descriptor.bNumInterfaces as i32, + } + } + + fn release_interfaces(&mut self) { + for i in &self.claimed_interfaces { + if let Err(e) = self.device_handle.lock().release_interface(*i) { + error!("could not release interface: {:?}", e); + } + } + self.claimed_interfaces = Vec::new(); + } + + fn handle_control_transfer(&mut self, transfer: XhciTransfer) -> Result<()> { + let xhci_transfer = Arc::new(transfer); + match xhci_transfer + .get_transfer_type() + .map_err(Error::GetXhciTransferType)? + { + XhciTransferType::SetupStage(setup) => { + if self.ctl_ep_state != ControlEndpointState::SetupStage { + error!("Control endpoint is in an inconsistant state"); + return Ok(()); + } + usb_debug!("setup stage setup buffer: {:?}", setup); + self.control_request_setup = setup; + xhci_transfer + .on_transfer_complete(&TransferStatus::Completed, 0) + .map_err(Error::TransferComplete)?; + self.ctl_ep_state = ControlEndpointState::DataStage; + } + XhciTransferType::DataStage(buffer) => { + if self.ctl_ep_state != ControlEndpointState::DataStage { + error!("Control endpoint is in an inconsistant state"); + return Ok(()); + } + self.buffer = Some(buffer); + xhci_transfer + .on_transfer_complete(&TransferStatus::Completed, 0) + .map_err(Error::TransferComplete)?; + self.ctl_ep_state = ControlEndpointState::StatusStage; + } + XhciTransferType::StatusStage => { + if self.ctl_ep_state == ControlEndpointState::SetupStage { + error!("Control endpoint is in an inconsistant state"); + return Ok(()); + } + let buffer = self.buffer.take(); + match self.control_request_setup.get_direction() { + Some(ControlRequestDataPhaseTransferDirection::HostToDevice) => { + match HostToDeviceControlRequest::analyze_request_setup( + &self.control_request_setup, + )? { + HostToDeviceControlRequest::Other => { + let mut control_transfer = control_transfer(0); + control_transfer + .buffer_mut() + .set_request_setup(&self.control_request_setup); + if let Some(buffer) = buffer { + buffer + .read(&mut control_transfer.buffer_mut().data_buffer) + .map_err(Error::ReadBuffer)?; + } + let tmp_transfer = xhci_transfer.clone(); + let callback = move |t: UsbTransfer| { + update_transfer_state(&xhci_transfer, &t)?; + let state = xhci_transfer.state().lock(); + match *state { + XhciTransferState::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)?; + } + _ => { + // update_transfer_state is already invoked before + // match. This transfer could only be `Cancelled` or + // `Completed`. Any other states means there is a bug + // in crosvm implementation. + error!("should not take this branch"); + return Err(Error::BadXhciTransferState); + } + } + Ok(()) + }; + let fail_handle = self.fail_handle.clone(); + control_transfer.set_callback( + move |t: UsbTransfer| match callback(t) { + Ok(_) => {} + Err(e) => { + error!("control transfer callback failed {:?}", e); + fail_handle.fail(); + } + }, + ); + submit_transfer( + self.fail_handle.clone(), + &self.job_queue, + tmp_transfer, + &self.device_handle, + control_transfer, + )?; + } + HostToDeviceControlRequest::SetAddress => { + usb_debug!("host device handling set address"); + let addr = self.control_request_setup.value as u32; + self.set_address(addr); + xhci_transfer + .on_transfer_complete(&TransferStatus::Completed, 0) + .map_err(Error::TransferComplete)?; + } + HostToDeviceControlRequest::SetConfig => { + usb_debug!("host device handling set config"); + let status = self.set_config()?; + xhci_transfer + .on_transfer_complete(&status, 0) + .map_err(Error::TransferComplete)?; + } + HostToDeviceControlRequest::SetInterface => { + usb_debug!("host device handling set interface"); + let status = self.set_interface()?; + xhci_transfer + .on_transfer_complete(&status, 0) + .map_err(Error::TransferComplete)?; + } + HostToDeviceControlRequest::ClearFeature => { + usb_debug!("host device handling clear feature"); + let status = self.clear_feature()?; + xhci_transfer + .on_transfer_complete(&status, 0) + .map_err(Error::TransferComplete)?; + } + }; + } + Some(ControlRequestDataPhaseTransferDirection::DeviceToHost) => { + let mut control_transfer = control_transfer(0); + control_transfer + .buffer_mut() + .set_request_setup(&self.control_request_setup); + let tmp_transfer = xhci_transfer.clone(); + let callback = move |t: UsbTransfer| { + usb_debug!("setup token control transfer callback invoked"); + update_transfer_state(&xhci_transfer, &t)?; + let state = xhci_transfer.state().lock(); + match *state { + XhciTransferState::Cancelled => { + usb_debug!("transfer cancelled"); + drop(state); + xhci_transfer + .on_transfer_complete(&TransferStatus::Cancelled, 0) + .map_err(Error::TransferComplete)?; + } + XhciTransferState::Completed => { + let status = t.status(); + if let Some(ref buffer) = buffer { + let _bytes = buffer + .write(&t.buffer().data_buffer) + .map_err(Error::WriteBuffer)? + as u32; + let _actual_length = t.actual_length(); + usb_debug!( + "transfer completed bytes: {} actual length {}", + _bytes, + _actual_length + ); + } + drop(state); + xhci_transfer + .on_transfer_complete(&status, 0) + .map_err(Error::TransferComplete)?; + } + _ => { + // update_transfer_state is already invoked before this match. + // Any other states indicates a bug in crosvm. + error!("should not take this branch"); + return Err(Error::BadXhciTransferState); + } + } + Ok(()) + }; + let fail_handle = self.fail_handle.clone(); + control_transfer.set_callback( + move |t: UsbTransfer| match callback(t) { + Ok(_) => {} + Err(e) => { + error!("control transfer callback failed {:?}", e); + fail_handle.fail(); + } + }, + ); + submit_transfer( + self.fail_handle.clone(), + &self.job_queue, + tmp_transfer, + &self.device_handle, + control_transfer, + )?; + } + None => error!("Unknown transfer direction!"), + } + + self.ctl_ep_state = ControlEndpointState::SetupStage; + } + _ => { + // Non control transfer should not be handled in this function. + error!("Non control (could be noop) transfer sent to control endpoint."); + xhci_transfer + .on_transfer_complete(&TransferStatus::Completed, 0) + .map_err(Error::TransferComplete)?; + } + } + Ok(()) + } + + fn set_config(&mut self) -> Result { + // It's a standard, set_config, device request. + let config = (self.control_request_setup.value & 0xff) as i32; + usb_debug!( + "Set config control transfer is received with config: {}", + config + ); + self.release_interfaces(); + let cur_config = self + .device_handle + .lock() + .get_active_configuration() + .map_err(Error::GetActiveConfig)?; + usb_debug!("current config is: {}", cur_config); + if config != cur_config { + self.device_handle + .lock() + .set_active_configuration(config) + .map_err(Error::SetActiveConfig)?; + } + self.claim_interfaces(); + self.create_endpoints()?; + Ok(TransferStatus::Completed) + } + + fn set_interface(&mut self) -> Result { + usb_debug!("set interface"); + // It's a standard, set_interface, interface request. + let interface = self.control_request_setup.index; + let alt_setting = self.control_request_setup.value; + self.device_handle + .lock() + .set_interface_alt_setting(interface as i32, alt_setting as i32) + .map_err(Error::SetInterfaceAltSetting)?; + self.alt_settings.insert(interface, alt_setting); + self.create_endpoints()?; + Ok(TransferStatus::Completed) + } + + fn clear_feature(&mut self) -> Result { + usb_debug!("clear feature"); + let request_setup = &self.control_request_setup; + // It's a standard, clear_feature, endpoint request. + const STD_FEATURE_ENDPOINT_HALT: u16 = 0; + if request_setup.value == STD_FEATURE_ENDPOINT_HALT { + self.device_handle + .lock() + .clear_halt(request_setup.index as u8) + .map_err(Error::ClearHalt)?; + } + Ok(TransferStatus::Completed) + } + + fn claim_interfaces(&mut self) { + for i in 0..self.get_interface_number_of_active_config() { + match self.device_handle.lock().claim_interface(i) { + Ok(()) => { + usb_debug!("claimed interface {}", i); + self.claimed_interfaces.push(i); + } + Err(e) => { + error!("unable to claim interface {}: {:?}", i, e); + } + } + } + } + + fn create_endpoints(&mut self) -> Result<()> { + self.endpoints = Vec::new(); + let config_descriptor = match self.device.get_active_config_descriptor() { + Err(e) => { + error!("device might be disconnected: {:?}", e); + return Ok(()); + } + Ok(descriptor) => descriptor, + }; + for i in &self.claimed_interfaces { + let alt_setting = self.alt_settings.get(&(*i as u16)).unwrap_or(&0); + let interface = config_descriptor + .get_interface_descriptor(*i as u8, *alt_setting as i32) + .ok_or(Error::GetInterfaceDescriptor((*i, *alt_setting)))?; + for ep_idx in 0..interface.bNumEndpoints { + let ep_dp = interface + .endpoint_descriptor(ep_idx) + .ok_or(Error::GetEndpointDescriptor(ep_idx))?; + let ep_num = ep_dp.get_endpoint_number(); + if ep_num == 0 { + usb_debug!("endpoint 0 in endpoint descriptors"); + continue; + } + let direction = ep_dp.get_direction(); + let ty = ep_dp.get_endpoint_type().ok_or(Error::GetEndpointType)?; + self.endpoints.push(UsbEndpoint::new( + self.fail_handle.clone(), + self.job_queue.clone(), + self.device_handle.clone(), + ep_num, + direction, + ty, + )); + } + } + Ok(()) + } + + fn submit_transfer_helper(&mut self, transfer: XhciTransfer) -> Result<()> { + if transfer.get_endpoint_number() == 0 { + return self.handle_control_transfer(transfer); + } + for ep in &self.endpoints { + if ep.match_ep(transfer.get_endpoint_number(), transfer.get_transfer_dir()) { + return ep.handle_transfer(transfer); + } + } + warn!("Could not find endpoint for transfer"); + transfer + .on_transfer_complete(&TransferStatus::Error, 0) + .map_err(Error::TransferComplete) + } +} + +impl XhciBackendDevice for HostDevice { + fn get_backend_type(&self) -> BackendType { + let d = match self.device.get_device_descriptor() { + Ok(d) => d, + Err(_) => return BackendType::Usb2, + }; + + // See definition of bcdUsb. + const USB3_MASK: u16 = 0x0300; + match d.bcdUSB & USB3_MASK { + USB3_MASK => BackendType::Usb3, + _ => BackendType::Usb2, + } + } + + fn host_bus(&self) -> u8 { + self.device.get_bus_number() + } + + fn host_address(&self) -> u8 { + self.device.get_address() + } + + fn get_vid(&self) -> u16 { + match self.device.get_device_descriptor() { + Ok(d) => d.idVendor, + Err(e) => { + error!("cannot get device descriptor: {:?}", e); + 0 + } + } + } + + fn get_pid(&self) -> u16 { + match self.device.get_device_descriptor() { + Ok(d) => d.idProduct, + Err(e) => { + error!("cannot get device descriptor: {:?}", e); + 0 + } + } + } + + fn submit_transfer(&mut self, transfer: XhciTransfer) -> std::result::Result<(), ()> { + self.submit_transfer_helper(transfer).map_err(|e| { + error!("failed to submit transfer: {}", e); + () + }) + } + + fn set_address(&mut self, _address: UsbDeviceAddress) { + // It's a standard, set_address, device request. We do nothing here. As described in XHCI + // spec. See set address command ring trb. + usb_debug!( + "Set address control transfer is received with address: {}", + _address + ); + } +} diff --git a/devices/src/usb/host_backend/hotplug.rs b/devices/src/usb/host_backend/hotplug.rs new file mode 100644 index 0000000000..218402abbf --- /dev/null +++ b/devices/src/usb/host_backend/hotplug.rs @@ -0,0 +1,45 @@ +// 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::sync::Arc; + +use usb::usb_util::hotplug::{HotplugEvent, UsbHotplugHandler}; +use usb::usb_util::libusb_device::LibUsbDevice; +use usb::xhci::usb_hub::UsbHub; + +pub struct HotplugHandler { + hub: Arc, +} + +impl HotplugHandler { + pub fn new(hub: Arc) -> Self { + HotplugHandler { hub } + } +} + +impl UsbHotplugHandler for HotplugHandler { + fn hotplug_event(&self, device: LibUsbDevice, event: HotplugEvent) { + match event { + HotplugEvent::DeviceLeft => { + let bus = device.get_bus_number(); + let address = device.get_address(); + let descriptor = match device.get_device_descriptor() { + Ok(d) => d, + Err(e) => { + error!("cannot get device descriptor: {:?}", e); + return; + } + }; + let vid = descriptor.idVendor; + let pid = descriptor.idProduct; + + if let Err(e) = self.hub.try_detach(bus, address, vid, pid) { + error!("device left event triggered failed detach from hub: {}", e); + return; + } + } + _ => {} + } + } +} diff --git a/devices/src/usb/host_backend/mod.rs b/devices/src/usb/host_backend/mod.rs new file mode 100644 index 0000000000..8951a086f4 --- /dev/null +++ b/devices/src/usb/host_backend/mod.rs @@ -0,0 +1,11 @@ +// 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. + +pub mod context; +mod error; +pub mod host_backend_device_provider; +pub mod host_device; +mod hotplug; +pub mod usb_endpoint; +mod utils; diff --git a/devices/src/usb/host_backend/usb_endpoint.rs b/devices/src/usb/host_backend/usb_endpoint.rs new file mode 100644 index 0000000000..fcdacaa3ba --- /dev/null +++ b/devices/src/usb/host_backend/usb_endpoint.rs @@ -0,0 +1,253 @@ +// 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 usb::usb_util::device_handle::DeviceHandle; +use usb::usb_util::types::{EndpointDirection, EndpointType, ENDPOINT_DIRECTION_OFFSET}; +use usb::usb_util::usb_transfer::{ + bulk_transfer, interrupt_transfer, BulkTransferBuffer, TransferStatus, UsbTransfer, +}; +use usb::xhci::scatter_gather_buffer::ScatterGatherBuffer; +use usb::xhci::xhci_transfer::{ + TransferDirection, XhciTransfer, XhciTransferState, XhciTransferType, +}; +use utils::AsyncJobQueue; +use utils::FailHandle; + +/// Isochronous, Bulk or Interrupt endpoint. +pub struct UsbEndpoint { + fail_handle: Arc, + job_queue: Arc, + device_handle: Arc>, + 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, + job_queue: Arc, + device_handle: Arc>, + 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, + 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| { + 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| 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| { + 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| 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(()) + } +} diff --git a/devices/src/usb/host_backend/utils.rs b/devices/src/usb/host_backend/utils.rs new file mode 100644 index 0000000000..9f1abc9540 --- /dev/null +++ b/devices/src/usb/host_backend/utils.rs @@ -0,0 +1,109 @@ +// 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::mem; +use std::sync::Arc; +use sync::Mutex; + +use super::error::*; +use usb::usb_util::device_handle::DeviceHandle; +use usb::usb_util::usb_transfer::{TransferStatus, UsbTransfer, UsbTransferBuffer}; +use usb::xhci::xhci_transfer::{XhciTransfer, XhciTransferState}; +use utils::AsyncJobQueue; +use utils::FailHandle; + +/// Helper function to update xhci_transfer state. +pub fn update_transfer_state( + xhci_transfer: &Arc, + usb_transfer: &UsbTransfer, +) -> Result<()> { + let status = usb_transfer.status(); + let mut state = xhci_transfer.state().lock(); + + if status == TransferStatus::Cancelled { + *state = XhciTransferState::Cancelled; + return Ok(()); + } + + match *state { + XhciTransferState::Cancelling => { + *state = XhciTransferState::Cancelled; + } + XhciTransferState::Submitted { cancel_callback: _ } => { + *state = XhciTransferState::Completed; + } + _ => { + error!("xhci trasfer state is invalid"); + *state = XhciTransferState::Completed; + return Err(Error::BadXhciTransferState); + } + } + Ok(()) +} + +/// Helper function to submit usb_transfer to device handle. +pub fn submit_transfer( + fail_handle: Arc, + job_queue: &Arc, + xhci_transfer: Arc, + device_handle: &Arc>, + usb_transfer: UsbTransfer, +) -> Result<()> { + let transfer_status = { + // We need to hold the lock to avoid race condition. + // While we are trying to submit the transfer, another thread might want to cancel the same + // transfer. Holding the lock here makes sure one of them is cancelled. + let mut state = xhci_transfer.state().lock(); + match mem::replace(&mut *state, XhciTransferState::Cancelled) { + XhciTransferState::Created => { + let canceller = usb_transfer.get_canceller(); + // TODO(jkwang) refactor canceller to return Cancel::Ok or Cancel::Err. + let cancel_callback = Box::new(move || match canceller.try_cancel() { + true => { + usb_debug!("cancel issued to libusb backend"); + } + false => { + usb_debug!("fail to cancel"); + } + }); + *state = XhciTransferState::Submitted { cancel_callback }; + match device_handle.lock().submit_async_transfer(usb_transfer) { + Err(e) => { + error!("fail to submit transfer {:?}", e); + *state = XhciTransferState::Completed; + TransferStatus::NoDevice + } + // If it's submitted, we don't need to send on_transfer_complete now. + Ok(_) => return Ok(()), + } + } + XhciTransferState::Cancelled => { + warn!("Transfer is already cancelled"); + TransferStatus::Cancelled + } + _ => { + // The transfer could not be in the following states: + // Submitted: A transfer should only be submitted once. + // Cancelling: Transfer is cancelling only when it's submitted and someone is + // trying to cancel it. + // Completed: A completed transfer should not be submitted again. + error!("xhci trasfer state is invalid"); + return Err(Error::BadXhciTransferState); + } + } + }; + // We are holding locks to of backends, we want to call on_transfer_complete + // without any lock. + job_queue + .queue_job( + move || match xhci_transfer.on_transfer_complete(&transfer_status, 0) { + Ok(_) => {} + Err(e) => { + error!("transfer complete failed: {:?}", e); + fail_handle.fail(); + } + }, + ) + .map_err(Error::QueueAsyncJob) +} diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index 15c42b57b6..78d93bdf54 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +extern crate usb_util; + #[macro_use] mod log; +mod host_backend; pub mod xhci; diff --git a/devices/src/usb/xhci/mod.rs b/devices/src/usb/xhci/mod.rs index f1815291c9..3aabdc6053 100644 --- a/devices/src/usb/xhci/mod.rs +++ b/devices/src/usb/xhci/mod.rs @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -extern crate usb_util; - mod command_ring_controller; mod device_slot; mod event_ring; @@ -12,12 +10,12 @@ mod intr_resample_handler; mod ring_buffer; mod ring_buffer_controller; mod ring_buffer_stop_cb; -mod scatter_gather_buffer; +pub mod scatter_gather_buffer; mod transfer_ring_controller; -mod usb_hub; +pub mod usb_hub; mod xhci_abi; mod xhci_abi_schema; -mod xhci_backend_device; -mod xhci_backend_device_provider; +pub mod xhci_backend_device; +pub mod xhci_backend_device_provider; mod xhci_regs; -mod xhci_transfer; +pub mod xhci_transfer; diff --git a/devices/src/usb/xhci/xhci_transfer.rs b/devices/src/usb/xhci/xhci_transfer.rs index 9dc64ead52..76fecdcead 100644 --- a/devices/src/usb/xhci/xhci_transfer.rs +++ b/devices/src/usb/xhci/xhci_transfer.rs @@ -5,8 +5,6 @@ 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::usb_util::types::UsbRequestSetup; -use super::usb_util::usb_transfer::TransferStatus; use super::xhci_abi::{ AddressedTrb, Error as TrbError, EventDataTrb, SetupStageTrb, TransferDescriptor, TrbCast, TrbCompletionCode, TrbType, @@ -18,6 +16,8 @@ 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 { diff --git a/vm_control/Cargo.toml b/vm_control/Cargo.toml index 5e03b9e966..924389a865 100644 --- a/vm_control/Cargo.toml +++ b/vm_control/Cargo.toml @@ -3,6 +3,9 @@ name = "vm_control" version = "0.1.0" authors = ["The Chromium OS Authors"] +[features] +sandboxed-libusb = [] + [dependencies] byteorder = "*" data_model = { path = "../data_model" } diff --git a/vm_control/src/lib.rs b/vm_control/src/lib.rs index 72531fbf8a..ee24b61ab3 100644 --- a/vm_control/src/lib.rs +++ b/vm_control/src/lib.rs @@ -23,7 +23,7 @@ use std::fs::File; use std::io::{Seek, SeekFrom}; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; -use libc::{EINVAL, ENODEV}; +use libc::{EINVAL, EIO, ENODEV}; use byteorder::{LittleEndian, WriteBytesExt}; use kvm::{Datamatch, IoeventAddress, Vm}; @@ -34,6 +34,7 @@ use sys_util::{ }; /// A file descriptor either borrowed or owned by this. +#[derive(Debug)] pub enum MaybeOwnedFd { /// Owned by this enum variant, and will be destructed automatically if not moved out. Owned(File), @@ -98,10 +99,54 @@ impl Default for VmRunMode { } } +#[derive(MsgOnSocket, Debug)] +pub enum UsbControlCommand { + AttachDevice { + bus: u8, + addr: u8, + vid: u16, + pid: u16, + fd: Option, + }, + DetachDevice { + port: u8, + }, + ListDevice { + port: u8, + }, +} + +#[derive(MsgOnSocket, Debug)] +pub enum UsbControlResult { + Ok { port: u8 }, + NoAvailablePort, + NoSuchDevice, + NoSuchPort, + FailedToOpenDevice, + Device { port: u8, vid: u16, pid: u16 }, +} + +impl Display for UsbControlResult { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::UsbControlResult::*; + + match self { + Ok { port } => write!(f, "ok {}", port), + NoAvailablePort => write!(f, "no_available_port"), + NoSuchDevice => write!(f, "no_such_device"), + NoSuchPort => write!(f, "no_such_port"), + FailedToOpenDevice => write!(f, "failed_to_open_device"), + Device { port, vid, pid } => write!(f, "device {} {:04x} {:04x}", port, vid, pid), + } + } +} + +pub type UsbControlSocket = MsgSocket; + /// A request to the main process to perform some operation on the VM. /// /// Unless otherwise noted, each request should expect a `VmResponse::Ok` to be received on success. -#[derive(MsgOnSocket)] +#[derive(MsgOnSocket, Debug)] pub enum VmRequest { /// Set the size of the VM's balloon in bytes. BalloonAdjust(u64), @@ -130,6 +175,8 @@ pub enum VmRequest { /// Resize a disk chosen by `disk_index` to `new_size` in bytes. /// `disk_index` is a 0-based count of `--disk`, `--rwdisk`, and `-r` command-line options. DiskResize { disk_index: usize, new_size: u64 }, + /// Command to use controller. + UsbCommand(UsbControlCommand), } fn register_memory( @@ -268,6 +315,10 @@ impl VmRequest { VmResponse::Err(SysError::new(ENODEV)) } } + VmRequest::UsbCommand(ref cmd) => { + error!("not implemented yet"); + VmResponse::Ok + } } } } @@ -275,7 +326,7 @@ impl VmRequest { /// Indication of success or failure of a `VmRequest`. /// /// Success is usually indicated `VmResponse::Ok` unless there is data associated with the response. -#[derive(MsgOnSocket)] +#[derive(MsgOnSocket, Debug)] pub enum VmResponse { /// Indicates the request was executed successfully. Ok, @@ -292,6 +343,8 @@ pub enum VmResponse { slot: u32, desc: GpuMemoryDesc, }, + /// Results of usb control commands. + UsbResponse(UsbControlResult), } impl Display for VmResponse { @@ -311,6 +364,7 @@ impl Display for VmResponse { "gpu memory allocated and registered to page frame number {:#x} and memory slot {}", pfn, slot ), + UsbResponse(result) => write!(f, "usb control request get result {:?}", result), } } }