Path: blob/main/devices/src/usb/backend/device_provider.rs
5394 views
// Copyright 2023 The ChromiumOS Authors1// Use of this source code is governed by a BSD-style license that can be2// found in the LICENSE file.34use std::collections::HashMap;5use std::fs::File;6use std::mem;7use std::sync::Arc;8use std::time::Duration;910use anyhow::Context;11use base::error;12use base::AsRawDescriptor;13use base::EventType;14use base::RawDescriptor;15use base::Tube;16use sync::Mutex;17use vm_control::UsbControlAttachedDevice;18use vm_control::UsbControlCommand;19use vm_control::UsbControlResult;20use vm_control::USB_CONTROL_MAX_PORTS;2122use crate::usb::backend::device::BackendDevice;23use crate::usb::backend::device::DeviceState;24use crate::usb::backend::error::Error;25use crate::usb::backend::error::Result;26use crate::usb::backend::fido_backend::fido_provider::attach_security_key;27use crate::usb::backend::host_backend::host_backend_device_provider::attach_host_backend_device;28use crate::usb::xhci::usb_hub::UsbHub;29use crate::usb::xhci::xhci_backend_device::XhciBackendDevice;30use crate::usb::xhci::xhci_backend_device_provider::XhciBackendDeviceProvider;31use crate::utils::AsyncJobQueue;32use crate::utils::EventHandler;33use crate::utils::EventLoop;34use crate::utils::FailHandle;3536const SOCKET_TIMEOUT_MS: u64 = 2000;3738/// Device provider is an xhci backend device provider that provides generic semantics to handle39/// various types of backend devices and connects them to the xhci layer.40pub enum DeviceProvider {41// The provider is created but not yet started.42Created { control_tube: Mutex<Tube> },43// The provider is started on an event loop.44Started { inner: Arc<ProviderInner> },45// The provider has failed.46Failed,47}4849impl DeviceProvider {50pub fn new() -> Result<(Tube, DeviceProvider)> {51let (child_tube, control_tube) = Tube::pair().map_err(Error::CreateControlTube)?;52control_tube53.set_send_timeout(Some(Duration::from_millis(SOCKET_TIMEOUT_MS)))54.map_err(Error::SetupControlTube)?;55control_tube56.set_recv_timeout(Some(Duration::from_millis(SOCKET_TIMEOUT_MS)))57.map_err(Error::SetupControlTube)?;5859let provider = DeviceProvider::Created {60control_tube: Mutex::new(child_tube),61};62Ok((control_tube, provider))63}6465fn start_helper(66&mut self,67fail_handle: Arc<dyn FailHandle>,68event_loop: Arc<EventLoop>,69hub: Arc<UsbHub>,70) -> Result<()> {71match mem::replace(self, DeviceProvider::Failed) {72DeviceProvider::Created { control_tube } => {73let job_queue =74AsyncJobQueue::init(&event_loop).map_err(Error::StartAsyncJobQueue)?;75let inner = Arc::new(ProviderInner::new(76fail_handle,77job_queue,78event_loop.clone(),79control_tube,80hub,81));82let handler: Arc<dyn EventHandler> = inner.clone();83event_loop84.add_event(85&*inner.control_tube.lock(),86EventType::Read,87Arc::downgrade(&handler),88)89.map_err(Error::AddToEventLoop)?;90*self = DeviceProvider::Started { inner };91Ok(())92}93DeviceProvider::Started { .. } => {94error!("Usb device provider has already started");95Err(Error::BadBackendProviderState)96}97DeviceProvider::Failed => {98error!("Usb device provider has already failed");99Err(Error::BadBackendProviderState)100}101}102}103}104105impl XhciBackendDeviceProvider for DeviceProvider {106fn start(107&mut self,108fail_handle: Arc<dyn FailHandle>,109event_loop: Arc<EventLoop>,110hub: Arc<UsbHub>,111) -> Result<()> {112self.start_helper(fail_handle, event_loop, hub)113}114115fn keep_rds(&self) -> Vec<RawDescriptor> {116match self {117DeviceProvider::Created { control_tube } => {118vec![control_tube.lock().as_raw_descriptor()]119}120_ => {121error!("Trying to get keepfds when DeviceProvider is not in created state");122vec![]123}124}125}126}127128/// ProviderInner listens to control socket.129pub struct ProviderInner {130fail_handle: Arc<dyn FailHandle>,131job_queue: Arc<AsyncJobQueue>,132event_loop: Arc<EventLoop>,133control_tube: Mutex<Tube>,134usb_hub: Arc<UsbHub>,135136// Map of USB hub port number to per-device context.137devices: Mutex<HashMap<u8, DeviceContext>>,138}139140struct DeviceContext {141event_handler: Arc<dyn EventHandler>,142device: Arc<Mutex<dyn BackendDevice>>,143}144145impl ProviderInner {146fn new(147fail_handle: Arc<dyn FailHandle>,148job_queue: Arc<AsyncJobQueue>,149event_loop: Arc<EventLoop>,150control_tube: Mutex<Tube>,151usb_hub: Arc<UsbHub>,152) -> ProviderInner {153ProviderInner {154fail_handle,155job_queue,156event_loop,157control_tube,158usb_hub,159devices: Mutex::new(HashMap::new()),160}161}162163fn handle_attach_device(&self, usb_file: File) -> UsbControlResult {164let (host_device, event_handler) = match attach_host_backend_device(165usb_file,166DeviceState::new(self.fail_handle.clone(), self.job_queue.clone()),167) {168Ok((host_device, event_handler)) => (host_device, event_handler),169Err(e) => {170error!("could not construct USB device from the given file: {}", e);171return UsbControlResult::NoSuchDevice;172}173};174175if let Err(e) = self.event_loop.add_event(176&*host_device.lock(),177EventType::ReadWrite,178Arc::downgrade(&event_handler),179) {180error!("failed to add USB device to event handler: {}", e);181return UsbControlResult::FailedToOpenDevice;182}183184// Resetting the device is used to make sure it is in a known state, but it may185// still function if the reset fails.186if let Err(e) = host_device.lock().reset() {187error!("failed to reset device after attach: {:?}", e);188}189190let device_ctx = DeviceContext {191event_handler,192device: host_device.clone(),193};194195let port = self.usb_hub.connect_backend(host_device);196match port {197Ok(port) => {198self.devices.lock().insert(port, device_ctx);199UsbControlResult::Ok { port }200}201Err(e) => {202error!("failed to connect device to hub: {}", e);203UsbControlResult::NoAvailablePort204}205}206}207208fn handle_detach_device(&self, port: u8) -> UsbControlResult {209match self.usb_hub.disconnect_port(port) {210Ok(()) => {211if let Some(device_ctx) = self.devices.lock().remove(&port) {212let _ = device_ctx.event_handler.on_event();213214if let Err(e) = device_ctx215.device216.lock()217.detach_event_handler(&self.event_loop)218{219error!(220"failed to remove poll change handler from event loop: {}",221e222);223}224}225UsbControlResult::Ok { port }226}227Err(e) => {228error!("failed to disconnect device from port {}: {}", port, e);229UsbControlResult::NoSuchDevice230}231}232}233234fn handle_attach_security_key(&self, hidraw: File) -> UsbControlResult {235let (fido_device, event_handler) = match attach_security_key(236hidraw,237self.event_loop.clone(),238DeviceState::new(self.fail_handle.clone(), self.job_queue.clone()),239) {240Ok((fido_device, event_handler)) => (fido_device, event_handler),241Err(e) => {242error!(243"could not create a virtual fido device from the given file: {}",244e245);246return UsbControlResult::NoSuchDevice;247}248};249250if let Err(e) = self.event_loop.add_event(251&*fido_device.lock(),252EventType::Read,253Arc::downgrade(&event_handler),254) {255error!("failed to add fido device to event handler: {}", e);256return UsbControlResult::FailedToOpenDevice;257}258259let device_ctx = DeviceContext {260event_handler,261device: fido_device.clone(),262};263264// Reset the device to make sure it's in a usable state.265// Resetting it also stops polling on the FD, since we only poll when there is an active266// transaction.267if let Err(e) = fido_device.lock().reset() {268error!("failed to reset fido device after attach: {:?}", e);269}270271let port = self.usb_hub.connect_backend(fido_device);272match port {273Ok(port) => {274self.devices.lock().insert(port, device_ctx);275UsbControlResult::Ok { port }276}277Err(e) => {278error!("failed to connect device to hub: {}", e);279UsbControlResult::NoAvailablePort280}281}282}283284fn handle_list_devices(&self, ports: [u8; USB_CONTROL_MAX_PORTS]) -> UsbControlResult {285let mut devices: [UsbControlAttachedDevice; USB_CONTROL_MAX_PORTS] = Default::default();286for (result_index, &port_id) in ports.iter().enumerate() {287match self.usb_hub.get_port(port_id).and_then(|p| {288p.backend_device()289.as_ref()290.map(|d| d.lock())291.map(|d| (d.get_vid(), d.get_pid()))292}) {293Some((vendor_id, product_id)) => {294devices[result_index] = UsbControlAttachedDevice {295port: port_id,296vendor_id,297product_id,298}299}300None => continue,301}302}303UsbControlResult::Devices(devices)304}305306fn on_event_helper(&self) -> Result<()> {307let tube = self.control_tube.lock();308let cmd = tube.recv().map_err(Error::ReadControlTube)?;309let result = match cmd {310UsbControlCommand::AttachDevice { file } => self.handle_attach_device(file),311UsbControlCommand::AttachSecurityKey { file } => self.handle_attach_security_key(file),312UsbControlCommand::DetachDevice { port } => self.handle_detach_device(port),313UsbControlCommand::ListDevice { ports } => self.handle_list_devices(ports),314};315tube.send(&result).map_err(Error::WriteControlTube)?;316Ok(())317}318}319320impl EventHandler for ProviderInner {321fn on_event(&self) -> anyhow::Result<()> {322self.on_event_helper()323.context("host backend device provider failed")324}325}326327328