| // 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::fs::File; |
| use std::sync::Arc; |
| |
| use super::error::*; |
| use super::host_device::HostDevice; |
| use crate::usb::xhci::usb_hub::UsbHub; |
| use crate::usb::xhci::xhci_backend_device_provider::XhciBackendDeviceProvider; |
| use crate::utils::AsyncJobQueue; |
| use crate::utils::{EventHandler, EventLoop, FailHandle}; |
| use base::net::UnixSeqpacket; |
| use base::{ |
| error, AsRawDescriptor, FromRawDescriptor, IntoRawDescriptor, RawDescriptor, WatchingEvents, |
| }; |
| use msg_socket::{MsgReceiver, MsgSender, MsgSocket}; |
| use std::collections::HashMap; |
| use std::mem; |
| use std::time::Duration; |
| use sync::Mutex; |
| use usb_util::Device; |
| use vm_control::{ |
| MaybeOwnedDescriptor, UsbControlAttachedDevice, UsbControlCommand, UsbControlResult, |
| UsbControlSocket, USB_CONTROL_MAX_PORTS, |
| }; |
| |
| 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: Mutex<MsgSocket<UsbControlResult, UsbControlCommand>>, |
| }, |
| // The provider is started on an event loop. |
| Started { |
| inner: Arc<ProviderInner>, |
| }, |
| // 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: Mutex::new(MsgSocket::new(child_sock)), |
| }; |
| Ok((MsgSocket::new(control_sock), provider)) |
| } |
| |
| fn start_helper( |
| &mut self, |
| fail_handle: Arc<dyn FailHandle>, |
| event_loop: Arc<EventLoop>, |
| hub: Arc<UsbHub>, |
| ) -> Result<()> { |
| match mem::replace(self, HostBackendDeviceProvider::Failed) { |
| HostBackendDeviceProvider::Created { sock } => { |
| let job_queue = |
| AsyncJobQueue::init(&event_loop).map_err(Error::StartAsyncJobQueue)?; |
| let inner = Arc::new(ProviderInner::new( |
| fail_handle, |
| job_queue, |
| event_loop.clone(), |
| sock, |
| hub, |
| )); |
| let handler: Arc<dyn EventHandler> = inner.clone(); |
| event_loop |
| .add_event( |
| &*inner.sock.lock(), |
| WatchingEvents::empty().set_read(), |
| Arc::downgrade(&handler), |
| ) |
| .map_err(Error::AddToEventLoop)?; |
| *self = HostBackendDeviceProvider::Started { inner }; |
| Ok(()) |
| } |
| HostBackendDeviceProvider::Started { .. } => { |
| 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<dyn FailHandle>, |
| event_loop: Arc<EventLoop>, |
| hub: Arc<UsbHub>, |
| ) -> 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_rds(&self) -> Vec<RawDescriptor> { |
| match self { |
| HostBackendDeviceProvider::Created { sock } => vec![sock.lock().as_raw_descriptor()], |
| _ => { |
| error!( |
| "Trying to get keepfds when HostBackendDeviceProvider is not in created state" |
| ); |
| vec![] |
| } |
| } |
| } |
| } |
| |
| /// ProviderInner listens to control socket. |
| pub struct ProviderInner { |
| fail_handle: Arc<dyn FailHandle>, |
| job_queue: Arc<AsyncJobQueue>, |
| event_loop: Arc<EventLoop>, |
| sock: Mutex<MsgSocket<UsbControlResult, UsbControlCommand>>, |
| usb_hub: Arc<UsbHub>, |
| |
| // Map of USB hub port number to per-device context. |
| devices: Mutex<HashMap<u8, HostDeviceContext>>, |
| } |
| |
| struct HostDeviceContext { |
| event_handler: Arc<dyn EventHandler>, |
| device: Arc<Mutex<Device>>, |
| } |
| |
| impl ProviderInner { |
| fn new( |
| fail_handle: Arc<dyn FailHandle>, |
| job_queue: Arc<AsyncJobQueue>, |
| event_loop: Arc<EventLoop>, |
| sock: Mutex<MsgSocket<UsbControlResult, UsbControlCommand>>, |
| usb_hub: Arc<UsbHub>, |
| ) -> ProviderInner { |
| ProviderInner { |
| fail_handle, |
| job_queue, |
| event_loop, |
| sock, |
| usb_hub, |
| devices: Mutex::new(HashMap::new()), |
| } |
| } |
| |
| /// Open a usbdevfs file to create a host USB device object. |
| /// `fd` should be an open file descriptor for a file in `/dev/bus/usb`. |
| fn handle_attach_device(&self, fd: Option<MaybeOwnedDescriptor>) -> UsbControlResult { |
| let usb_file = match fd { |
| Some(MaybeOwnedDescriptor::Owned(file)) => file, |
| _ => { |
| error!("missing fd in UsbControlCommand::AttachDevice message"); |
| return UsbControlResult::FailedToOpenDevice; |
| } |
| }; |
| |
| let raw_descriptor = usb_file.into_raw_descriptor(); |
| // Safe as it is valid to have multiple variables accessing the same fd. |
| let device = match Device::new(unsafe { File::from_raw_descriptor(raw_descriptor) }) { |
| Ok(d) => d, |
| Err(e) => { |
| error!("could not construct USB device from fd: {}", e); |
| return UsbControlResult::NoSuchDevice; |
| } |
| }; |
| |
| let arc_mutex_device = Arc::new(Mutex::new(device)); |
| |
| let event_handler: Arc<dyn EventHandler> = Arc::new(UsbUtilEventHandler { |
| device: arc_mutex_device.clone(), |
| }); |
| |
| if let Err(e) = self.event_loop.add_event( |
| &MaybeOwnedDescriptor::Borrowed(raw_descriptor), |
| WatchingEvents::empty().set_read().set_write(), |
| Arc::downgrade(&event_handler), |
| ) { |
| error!("failed to add USB device fd to event handler: {}", e); |
| return UsbControlResult::FailedToOpenDevice; |
| } |
| |
| let device_ctx = HostDeviceContext { |
| event_handler, |
| device: arc_mutex_device.clone(), |
| }; |
| |
| // Resetting the device is used to make sure it is in a known state, but it may |
| // still function if the reset fails. |
| if let Err(e) = arc_mutex_device.lock().reset() { |
| error!("failed to reset device after attach: {:?}", e); |
| } |
| |
| let host_device = Box::new(HostDevice::new( |
| self.fail_handle.clone(), |
| self.job_queue.clone(), |
| arc_mutex_device, |
| )); |
| let port = self.usb_hub.connect_backend(host_device); |
| match port { |
| Ok(port) => { |
| self.devices.lock().insert(port, device_ctx); |
| UsbControlResult::Ok { port } |
| } |
| Err(e) => { |
| error!("failed to connect device to hub: {}", e); |
| UsbControlResult::NoAvailablePort |
| } |
| } |
| } |
| |
| fn handle_detach_device(&self, port: u8) -> UsbControlResult { |
| match self.usb_hub.disconnect_port(port) { |
| Ok(()) => { |
| if let Some(device_ctx) = self.devices.lock().remove(&port) { |
| let _ = device_ctx.event_handler.on_event(); |
| let device = device_ctx.device.lock(); |
| let fd = device.fd(); |
| |
| if let Err(e) = |
| self.event_loop |
| .remove_event_for_fd(&MaybeOwnedDescriptor::Borrowed( |
| fd.as_raw_descriptor(), |
| )) |
| { |
| error!( |
| "failed to remove poll change handler from event loop: {}", |
| e |
| ); |
| } |
| } |
| UsbControlResult::Ok { port } |
| } |
| Err(e) => { |
| error!("failed to disconnect device from port {}: {}", port, e); |
| UsbControlResult::NoSuchDevice |
| } |
| } |
| } |
| |
| fn handle_list_devices(&self, ports: [u8; USB_CONTROL_MAX_PORTS]) -> UsbControlResult { |
| let mut devices: [UsbControlAttachedDevice; USB_CONTROL_MAX_PORTS] = Default::default(); |
| for (result_index, &port_id) in ports.iter().enumerate() { |
| match self.usb_hub.get_port(port_id).and_then(|p| { |
| p.get_backend_device() |
| .as_ref() |
| .map(|d| (d.get_vid(), d.get_pid())) |
| }) { |
| Some((vendor_id, product_id)) => { |
| devices[result_index] = UsbControlAttachedDevice { |
| port: port_id, |
| vendor_id, |
| product_id, |
| } |
| } |
| None => continue, |
| } |
| } |
| UsbControlResult::Devices(devices) |
| } |
| |
| fn on_event_helper(&self) -> Result<()> { |
| let sock = self.sock.lock(); |
| let cmd = sock.recv().map_err(Error::ReadControlSock)?; |
| let result = match cmd { |
| UsbControlCommand::AttachDevice { descriptor, .. } => { |
| self.handle_attach_device(descriptor) |
| } |
| UsbControlCommand::DetachDevice { port } => self.handle_detach_device(port), |
| UsbControlCommand::ListDevice { ports } => self.handle_list_devices(ports), |
| }; |
| sock.send(&result).map_err(Error::WriteControlSock)?; |
| Ok(()) |
| } |
| } |
| |
| impl EventHandler for ProviderInner { |
| fn on_event(&self) -> std::result::Result<(), ()> { |
| self.on_event_helper().map_err(|e| { |
| error!("host backend device provider failed: {}", e); |
| }) |
| } |
| } |
| |
| struct UsbUtilEventHandler { |
| device: Arc<Mutex<Device>>, |
| } |
| |
| impl EventHandler for UsbUtilEventHandler { |
| fn on_event(&self) -> std::result::Result<(), ()> { |
| self.device.lock().poll_transfers().map_err(|_e| ()) |
| } |
| } |