summary refs log blame commit diff
path: root/devices/src/usb/host_backend/host_backend_device_provider.rs
blob: de80521956716a67f6efb865848769621e7c19e7 (plain) (tree)
1
2
3
4
5
6
7
8
9
10









                                                                         



                                                                              





                                                    




































                                                                                                  
                                         










                                                                                                 
                                                                   
























                                                                     
                                         
























                                                                                                  
                                     







                                                         
                                         













                                                             
                                             










































































































































































                                                                                                    

                                                       




                                                                 
// 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 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 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 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<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: 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 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<dyn EventHandler> = 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<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_fds(&self) -> Vec<RawFd> {
        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<dyn FailHandle>,
    job_queue: Arc<AsyncJobQueue>,
    ctx: Context,
    sock: MsgSocket<UsbControlResult, UsbControlCommand>,
    usb_hub: Arc<UsbHub>,
}

impl ProviderInner {
    fn new(
        fail_handle: Arc<dyn FailHandle>,
        job_queue: Arc<AsyncJobQueue>,
        ctx: Context,
        sock: MsgSocket<UsbControlResult, UsbControlCommand>,
        usb_hub: Arc<UsbHub>,
    ) -> ProviderInner {
        ProviderInner {
            fail_handle,
            job_queue,
            ctx,
            sock,
            usb_hub,
        }
    }

    fn on_event_helper(&self) -> 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) -> std::result::Result<(), ()> {
        self.on_event_helper().map_err(|e| {
            error!("host backend device provider failed: {}", e);
            ()
        })
    }
}