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 <jkwang@google.com>
Tested-by: kokoro <noreply+kokoro@google.com>
Tested-by: Zach Reizner <zachr@chromium.org>
Reviewed-by: Zach Reizner <zachr@chromium.org>
This commit is contained in:
Jingkui Wang 2019-03-08 13:21:02 -08:00 committed by chrome-bot
parent 874f2e83ed
commit 257d004b0c
13 changed files with 1588 additions and 12 deletions

View file

@ -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<EventLoop>,
event_handler: Arc<EventHandler>,
}
impl Context {
/// Create a new context.
#[cfg(not(feature = "sandboxed-libusb"))]
pub fn new(event_loop: Arc<EventLoop>) -> Result<Context> {
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<EventLoop>) -> Result<Context> {
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<H: UsbHotplugHandler + Sized>(&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<LibUsbDevice> {
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<LibUsbDevice> {
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<EventLoop>,
event_handler: Weak<EventHandler>,
}
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
),
}
}
}

View file

@ -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<T> = std::result::Result<T, Error>;

View file

@ -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<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<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<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<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<FailHandle>,
job_queue: Arc<AsyncJobQueue>,
ctx: Context,
sock: MsgSocket<UsbControlResult, UsbControlCommand>,
usb_hub: Arc<UsbHub>,
}
impl ProviderInner {
fn new(
fail_handle: Arc<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, _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);
()
})
}
}

View file

@ -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<HostToDeviceControlRequest> {
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<FailHandle>,
// Endpoints only contains data endpoints (1 to 30). Control transfers are handled at device
// level.
endpoints: Vec<UsbEndpoint>,
device: LibUsbDevice,
device_handle: Arc<Mutex<DeviceHandle>>,
ctl_ep_state: ControlEndpointState,
alt_settings: HashMap<u16, u16>,
claimed_interfaces: Vec<i32>,
control_request_setup: UsbRequestSetup,
buffer: Option<ScatterGatherBuffer>,
job_queue: Arc<AsyncJobQueue>,
}
impl Drop for HostDevice {
fn drop(&mut self) {
self.release_interfaces();
}
}
impl HostDevice {
/// Create a new host device.
pub fn new(
fail_handle: Arc<FailHandle>,
job_queue: Arc<AsyncJobQueue>,
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<ControlTransferBuffer>| {
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<ControlTransferBuffer>| 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<ControlTransferBuffer>| {
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<ControlTransferBuffer>| 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<TransferStatus> {
// 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<TransferStatus> {
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<TransferStatus> {
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
);
}
}

View file

@ -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<UsbHub>,
}
impl HotplugHandler {
pub fn new(hub: Arc<UsbHub>) -> 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;
}
}
_ => {}
}
}
}

View file

@ -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;

View file

@ -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<FailHandle>,
job_queue: Arc<AsyncJobQueue>,
device_handle: Arc<Mutex<DeviceHandle>>,
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<FailHandle>,
job_queue: Arc<AsyncJobQueue>,
device_handle: Arc<Mutex<DeviceHandle>>,
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<BulkTransferBuffer>,
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<BulkTransferBuffer>| {
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<BulkTransferBuffer>| 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<BulkTransferBuffer>| {
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<BulkTransferBuffer>| 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(())
}
}

View file

@ -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<T: UsbTransferBuffer>(
xhci_transfer: &Arc<XhciTransfer>,
usb_transfer: &UsbTransfer<T>,
) -> 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<T: UsbTransferBuffer>(
fail_handle: Arc<FailHandle>,
job_queue: &Arc<AsyncJobQueue>,
xhci_transfer: Arc<XhciTransfer>,
device_handle: &Arc<Mutex<DeviceHandle>>,
usb_transfer: UsbTransfer<T>,
) -> 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)
}

View file

@ -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;

View file

@ -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;

View file

@ -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 {

View file

@ -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" }

View file

@ -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<MaybeOwnedFd>,
},
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<UsbControlCommand, UsbControlResult>;
/// 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),
}
}
}