usb: replace libusb with Rust usb_util library

Drop the dependency on libusb and reimplement the host USB backend using
usb_sys to wrap the Linux usbdevfs ioctls.

This allows sandboxing to work without any dependency on libusb patches,
and it gives us the flexibility to modify and update the USB backend
without depending on an external third-party library.

BUG=chromium:987833
TEST=`adb logcat` on nami with Nexus 5 attached
TEST=deploy app to phone with Android Studio
TEST=Run EdgeTPU USB accelerator demo (including DFU mode transition)

Cq-Depend: chromium:1773695
Change-Id: I4321c2b6142caac15f48f197795a37d59d268831
Signed-off-by: Daniel Verkamp <dverkamp@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1783601
Reviewed-by: Zach Reizner <zachr@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
This commit is contained in:
Daniel Verkamp 2019-07-09 17:21:54 -07:00 committed by Commit Bot
parent bed8b0017d
commit 6494117e17
32 changed files with 1544 additions and 6494 deletions

13
Cargo.lock generated
View file

@ -529,14 +529,23 @@ name = "unicode-xid"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "usb_sys"
version = "0.1.0"
dependencies = [
"sys_util 0.1.0",
]
[[package]]
name = "usb_util"
version = "0.1.0"
dependencies = [
"assertions 0.1.0",
"data_model 0.1.0",
"pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)",
"sync 0.1.0",
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
"remain 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"sys_util 0.1.0",
"usb_sys 0.1.0",
]
[[package]]

View file

@ -32,7 +32,6 @@ default-no-sandbox = []
gpu = ["devices/gpu"]
gpu-forward = ["render_node_forward"]
plugin = ["protos/plugin", "crosvm_plugin", "protobuf"]
sandboxed-libusb = ["devices/sandboxed-libusb", "vm_control/sandboxed-libusb"]
tpm = ["devices/tpm"]
wl-dmabuf = ["devices/wl-dmabuf", "gpu_buffer", "resources/wl-dmabuf"]
x = ["devices/x"]

View file

@ -6,7 +6,6 @@ edition = "2018"
[features]
gpu = ["gpu_buffer", "gpu_display", "gpu_renderer"]
sandboxed-libusb = ["usb_util/sandboxed-libusb"]
tpm = ["protos/trunks", "tpm2"]
wl-dmabuf = []
x = ["gpu_display/x"]

View file

@ -1,145 +0,0 @@
// 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 crate::utils::{EventHandler, EventLoop};
use std::os::raw::c_short;
use std::os::unix::io::RawFd;
use std::sync::{Arc, Weak};
use sys_util::{error, WatchingEvents};
#[cfg(feature = "sandboxed-libusb")]
use usb_util::device_handle::DeviceHandle;
use usb_util::hotplug::UsbHotplugHandler;
use usb_util::libusb_context::{LibUsbContext, LibUsbPollfdChangeHandler};
#[cfg(not(feature = "sandboxed-libusb"))]
use usb_util::libusb_device::LibUsbDevice;
use vm_control::MaybeOwnedFd;
/// Context wraps libusb context with libusb event handling.
pub struct Context {
context: LibUsbContext,
event_loop: Arc<EventLoop>,
event_handler: Arc<dyn EventHandler>,
}
impl Context {
/// Create a new context.
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)
}
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_handle(&self, fd: std::fs::File) -> Option<DeviceHandle> {
match self.context.handle_from_file(fd) {
Ok(handle) => Some(handle),
Err(e) => {
error!("could not build device from fd: {:?}", e);
None
}
}
}
}
struct LibUsbEventHandler {
context: LibUsbContext,
}
impl EventHandler for LibUsbEventHandler {
fn on_event(&self) -> std::result::Result<(), ()> {
self.context.handle_events_nonblock();
Ok(())
}
}
struct PollfdChangeHandler {
event_loop: Arc<EventLoop>,
event_handler: Weak<dyn EventHandler>,
}
impl LibUsbPollfdChangeHandler for PollfdChangeHandler {
fn add_poll_fd(&self, fd: RawFd, events: c_short) {
if let Err(e) = self.event_loop.add_event(
&MaybeOwnedFd::Borrowed(fd),
WatchingEvents::new(events as u32),
self.event_handler.clone(),
) {
error!("cannot add event to event loop: {}", e);
}
}
fn remove_poll_fd(&self, fd: RawFd) {
if let Some(h) = self.event_handler.upgrade() {
if let Err(e) = h.on_event() {
error!("cannot handle event: {:?}", e);
}
}
if let Err(e) = self
.event_loop
.remove_event_for_fd(&MaybeOwnedFd::Borrowed(fd))
{
error!(
"failed to remove poll change handler from event loop: {}",
e
);
}
}
}

View file

@ -7,7 +7,7 @@ use crate::usb::xhci::xhci_transfer::Error as XhciTransferError;
use crate::utils::Error as UtilsError;
use msg_socket::MsgError;
use std::fmt::{self, Display};
use usb_util::error::Error as UsbUtilError;
use usb_util::Error as UsbUtilError;
#[derive(Debug)]
pub enum Error {
@ -19,6 +19,7 @@ pub enum Error {
SetActiveConfig(UsbUtilError),
SetInterfaceAltSetting(UsbUtilError),
ClearHalt(UsbUtilError),
CreateTransfer(UsbUtilError),
GetEndpointType,
CreateControlSock(std::io::Error),
SetupControlSock(std::io::Error),
@ -30,7 +31,7 @@ pub enum Error {
WriteBuffer(BufferError),
BufferLen(BufferError),
/// Cannot get interface descriptor for (interface, altsetting).
GetInterfaceDescriptor((i32, u16)),
GetInterfaceDescriptor((u8, u8)),
GetEndpointDescriptor(u8),
BadXhciTransferState,
BadBackendProviderState,
@ -49,6 +50,7 @@ impl Display for Error {
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),
CreateTransfer(e) => write!(f, "failed to create transfer: {:?}", 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),

View file

@ -4,22 +4,23 @@
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::collections::HashMap;
use std::mem;
use std::os::unix::io::{AsRawFd, RawFd};
use std::time::Duration;
use sync::Mutex;
use sys_util::net::UnixSeqpacket;
use sys_util::{error, WatchingEvents};
use usb_util::Device;
use vm_control::{
UsbControlAttachedDevice, UsbControlCommand, UsbControlResult, UsbControlSocket,
MaybeOwnedFd, UsbControlAttachedDevice, UsbControlCommand, UsbControlResult, UsbControlSocket,
USB_CONTROL_MAX_PORTS,
};
@ -64,12 +65,15 @@ impl HostBackendDeviceProvider {
) -> 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 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(
@ -123,25 +127,125 @@ impl XhciBackendDeviceProvider for HostBackendDeviceProvider {
pub struct ProviderInner {
fail_handle: Arc<dyn FailHandle>,
job_queue: Arc<AsyncJobQueue>,
ctx: Context,
event_loop: Arc<EventLoop>,
sock: 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>,
ctx: Context,
event_loop: Arc<EventLoop>,
sock: MsgSocket<UsbControlResult, UsbControlCommand>,
usb_hub: Arc<UsbHub>,
) -> ProviderInner {
ProviderInner {
fail_handle,
job_queue,
ctx,
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<MaybeOwnedFd>) -> UsbControlResult {
let usb_file = match fd {
Some(MaybeOwnedFd::Owned(file)) => file,
_ => {
error!("missing fd in UsbControlCommand::AttachDevice message");
return UsbControlResult::FailedToOpenDevice;
}
};
let raw_fd = usb_file.as_raw_fd();
let device = match Device::new(usb_file) {
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(
&MaybeOwnedFd::Borrowed(raw_fd),
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(&MaybeOwnedFd::Borrowed(fd.as_raw_fd()))
{
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
}
}
}
@ -168,146 +272,13 @@ impl ProviderInner {
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_handle = match self.ctx.get_device_handle(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 = device_handle.get_device();
// 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) = device_handle.reset() {
error!("failed to reset device after attach: {:?}", e);
}
(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 { ports } => {
let result = self.handle_list_devices(ports);
// 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(())
}
}
let result = match cmd {
UsbControlCommand::AttachDevice { fd, .. } => self.handle_attach_device(fd),
UsbControlCommand::DetachDevice { port } => self.handle_detach_device(port),
UsbControlCommand::ListDevice { ports } => self.handle_list_devices(ports),
};
self.sock.send(&result).map_err(Error::WriteControlSock)?;
Ok(())
}
}
@ -318,3 +289,13 @@ impl EventHandler for ProviderInner {
})
}
}
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| ())
}
}

View file

@ -4,7 +4,6 @@
use std::mem::drop;
use std::sync::Arc;
use sync::Mutex;
use super::error::*;
use super::usb_endpoint::UsbEndpoint;
@ -14,17 +13,14 @@ use crate::usb::xhci::xhci_backend_device::{BackendType, UsbDeviceAddress, XhciB
use crate::usb::xhci::xhci_transfer::{XhciTransfer, XhciTransferState, XhciTransferType};
use crate::utils::AsyncJobQueue;
use crate::utils::FailHandle;
use data_model::DataInit;
use std::collections::HashMap;
use std::mem;
use sync::Mutex;
use sys_util::{error, warn};
use usb_util::device_handle::DeviceHandle;
use usb_util::error::Error as LibUsbError;
use usb_util::libusb_device::LibUsbDevice;
use usb_util::types::{
ControlRequestDataPhaseTransferDirection, ControlRequestRecipient, StandardControlRequest,
UsbRequestSetup,
};
use usb_util::usb_transfer::{
control_transfer, ControlTransferBuffer, TransferStatus, UsbTransfer,
use usb_util::{
ConfigDescriptorTree, ControlRequestDataPhaseTransferDirection, ControlRequestRecipient,
Device, StandardControlRequest, Transfer, TransferStatus, UsbRequestSetup,
};
#[derive(PartialEq)]
@ -43,11 +39,10 @@ pub struct HostDevice {
// 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>>,
device: Arc<Mutex<Device>>,
ctl_ep_state: ControlEndpointState,
alt_settings: HashMap<u16, u16>,
claimed_interfaces: Vec<i32>,
alt_settings: HashMap<u8, u8>,
claimed_interfaces: Vec<u8>,
control_request_setup: UsbRequestSetup,
executed: bool,
job_queue: Arc<AsyncJobQueue>,
@ -64,14 +59,12 @@ impl HostDevice {
pub fn new(
fail_handle: Arc<dyn FailHandle>,
job_queue: Arc<AsyncJobQueue>,
device: LibUsbDevice,
device_handle: DeviceHandle,
device: Arc<Mutex<Device>>,
) -> HostDevice {
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![],
@ -81,30 +74,6 @@ impl HostDevice {
}
}
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();
}
// Check for requests that should be intercepted and emulated using libusb
// functions rather than passed directly to the device.
// Returns true if the request has been intercepted or false if the request
@ -167,20 +136,34 @@ impl HostDevice {
xhci_transfer: Arc<XhciTransfer>,
buffer: Option<ScatterGatherBuffer>,
) -> Result<()> {
let mut control_transfer = control_transfer(0);
control_transfer
.buffer_mut()
.set_request_setup(&self.control_request_setup);
if self.intercepted_control_transfer(&xhci_transfer)? {
return Ok(());
}
// Default buffer size for control data transfer.
const CONTROL_DATA_BUFFER_SIZE: usize = 1024;
// Buffer type for control transfer. The first 8 bytes is a UsbRequestSetup struct.
#[derive(Copy, Clone)]
#[repr(C, packed)]
struct ControlTransferBuffer {
pub setup: UsbRequestSetup,
pub data: [u8; CONTROL_DATA_BUFFER_SIZE],
}
// Safe because it only has data and has no implicit padding.
unsafe impl DataInit for ControlTransferBuffer {}
let mut control_request = ControlTransferBuffer {
setup: self.control_request_setup,
data: [0; CONTROL_DATA_BUFFER_SIZE],
};
let direction = self.control_request_setup.get_direction();
let buffer = if direction == ControlRequestDataPhaseTransferDirection::HostToDevice {
if let Some(buffer) = buffer {
buffer
.read(&mut control_transfer.buffer_mut().data_buffer)
.read(&mut control_request.data)
.map_err(Error::ReadBuffer)?;
}
// buffer is consumed here for HostToDevice transfers.
@ -190,8 +173,13 @@ impl HostDevice {
buffer
};
let control_buffer = control_request.as_slice().to_vec();
let mut control_transfer =
Transfer::new_control(control_buffer).map_err(Error::CreateTransfer)?;
let tmp_transfer = xhci_transfer.clone();
let callback = move |t: UsbTransfer<ControlTransferBuffer>| {
let callback = move |t: Transfer| {
usb_debug!("setup token control transfer callback invoked");
update_transfer_state(&xhci_transfer, &t)?;
let state = xhci_transfer.state().lock();
@ -207,10 +195,14 @@ impl HostDevice {
let status = t.status();
let actual_length = t.actual_length();
if direction == ControlRequestDataPhaseTransferDirection::DeviceToHost {
if let Some(buffer) = &buffer {
buffer
.write(&t.buffer().data_buffer)
.map_err(Error::WriteBuffer)?;
if let Some(control_request_data) =
t.buffer.get(mem::size_of::<UsbRequestSetup>()..)
{
if let Some(buffer) = &buffer {
buffer
.write(&control_request_data)
.map_err(Error::WriteBuffer)?;
}
}
}
drop(state);
@ -231,20 +223,18 @@ impl HostDevice {
};
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();
}
},
);
control_transfer.set_callback(move |t: Transfer| 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,
&mut self.device.lock(),
control_transfer,
)
}
@ -309,40 +299,50 @@ impl HostDevice {
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;
let config = (self.control_request_setup.value & 0xff) as u8;
usb_debug!(
"Set config control transfer is received with config: {}",
config
);
self.release_interfaces();
let cur_config = self
.device_handle
.device
.lock()
.get_active_configuration()
.map_err(Error::GetActiveConfig)?;
usb_debug!("current config is: {}", cur_config);
if config != cur_config {
self.device_handle
self.device
.lock()
.set_active_configuration(config)
.map_err(Error::SetActiveConfig)?;
}
self.claim_interfaces();
self.create_endpoints()?;
let config_descriptor = self
.device
.lock()
.get_active_config_descriptor()
.map_err(Error::GetActiveConfig)?;
self.claim_interfaces(&config_descriptor);
self.create_endpoints(&config_descriptor)?;
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
let interface = self.control_request_setup.index as u8;
let alt_setting = self.control_request_setup.value as u8;
self.device
.lock()
.set_interface_alt_setting(interface as i32, alt_setting as i32)
.set_interface_alt_setting(interface, alt_setting)
.map_err(Error::SetInterfaceAltSetting)?;
self.alt_settings.insert(interface, alt_setting);
self.create_endpoints()?;
let config_descriptor = self
.device
.lock()
.get_active_config_descriptor()
.map_err(Error::GetActiveConfig)?;
self.create_endpoints(&config_descriptor)?;
Ok(TransferStatus::Completed)
}
@ -352,7 +352,7 @@ impl HostDevice {
// 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
self.device
.lock()
.clear_halt(request_setup.index as u8)
.map_err(Error::ClearHalt)?;
@ -360,9 +360,9 @@ impl HostDevice {
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) {
fn claim_interfaces(&mut self, config_descriptor: &ConfigDescriptorTree) {
for i in 0..config_descriptor.num_interfaces() {
match self.device.lock().claim_interface(i) {
Ok(()) => {
usb_debug!("claimed interface {}", i);
self.claimed_interfaces.push(i);
@ -374,23 +374,16 @@ impl HostDevice {
}
}
fn create_endpoints(&mut self) -> Result<()> {
fn create_endpoints(&mut self, config_descriptor: &ConfigDescriptorTree) -> 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 alt_setting = self.alt_settings.get(i).unwrap_or(&0);
let interface = config_descriptor
.get_interface_descriptor(*i as u8, *alt_setting as i32)
.get_interface_descriptor(*i, *alt_setting)
.ok_or(Error::GetInterfaceDescriptor((*i, *alt_setting)))?;
for ep_idx in 0..interface.bNumEndpoints {
let ep_dp = interface
.endpoint_descriptor(ep_idx)
.get_endpoint_descriptor(ep_idx)
.ok_or(Error::GetEndpointDescriptor(ep_idx))?;
let ep_num = ep_dp.get_endpoint_number();
if ep_num == 0 {
@ -402,7 +395,7 @@ impl HostDevice {
self.endpoints.push(UsbEndpoint::new(
self.fail_handle.clone(),
self.job_queue.clone(),
self.device_handle.clone(),
self.device.clone(),
ep_num,
direction,
ty,
@ -412,6 +405,15 @@ impl HostDevice {
Ok(())
}
fn release_interfaces(&mut self) {
for i in &self.claimed_interfaces {
if let Err(e) = self.device.lock().release_interface(*i) {
error!("could not release interface: {:?}", e);
}
}
self.claimed_interfaces = Vec::new();
}
fn submit_transfer_helper(&mut self, transfer: XhciTransfer) -> Result<()> {
if transfer.get_endpoint_number() == 0 {
return self.handle_control_transfer(transfer);
@ -430,7 +432,7 @@ impl HostDevice {
impl XhciBackendDevice for HostDevice {
fn get_backend_type(&self) -> BackendType {
let d = match self.device.get_device_descriptor() {
let d = match self.device.lock().get_device_descriptor() {
Ok(d) => d,
Err(_) => return BackendType::Usb2,
};
@ -443,16 +445,8 @@ impl XhciBackendDevice for HostDevice {
}
}
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() {
match self.device.lock().get_device_descriptor() {
Ok(d) => d.idVendor,
Err(e) => {
error!("cannot get device descriptor: {:?}", e);
@ -462,7 +456,7 @@ impl XhciBackendDevice for HostDevice {
}
fn get_pid(&self) -> u16 {
match self.device.get_device_descriptor() {
match self.device.lock().get_device_descriptor() {
Ok(d) => d.idProduct,
Err(e) => {
error!("cannot get device descriptor: {:?}", e);
@ -488,16 +482,9 @@ impl XhciBackendDevice for HostDevice {
fn reset(&mut self) -> std::result::Result<(), ()> {
usb_debug!("resetting host device");
let result = self.device_handle.lock().reset();
match result {
Err(LibUsbError::NotFound) => {
// libusb will return NotFound if it fails to re-claim
// the interface after the reset.
Ok(())
}
_ => result.map_err(|e| {
error!("failed to reset device: {:?}", e);
}),
}
self.device
.lock()
.reset()
.map_err(|e| error!("failed to reset device: {:?}", e))
}
}

View file

@ -1,45 +0,0 @@
// 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 crate::usb::xhci::usb_hub::UsbHub;
use sys_util::error;
use usb_util::hotplug::{HotplugEvent, UsbHotplugHandler};
use usb_util::libusb_device::LibUsbDevice;
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) {
if event != HotplugEvent::DeviceLeft {
return;
}
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

@ -2,10 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
pub mod context;
pub mod error;
pub mod host_backend_device_provider;
pub mod host_device;
mod hotplug;
pub mod usb_endpoint;
mod utils;

View file

@ -4,7 +4,6 @@
use std::cmp;
use std::sync::Arc;
use sync::Mutex;
use super::error::*;
use super::utils::{submit_transfer, update_transfer_state};
@ -14,18 +13,17 @@ use crate::usb::xhci::xhci_transfer::{
};
use crate::utils::AsyncJobQueue;
use crate::utils::FailHandle;
use sync::Mutex;
use sys_util::error;
use usb_util::device_handle::DeviceHandle;
use usb_util::types::{EndpointDirection, EndpointType, ENDPOINT_DIRECTION_OFFSET};
use usb_util::usb_transfer::{
bulk_transfer, interrupt_transfer, BulkTransferBuffer, TransferStatus, UsbTransfer,
use usb_util::{
Device, EndpointDirection, EndpointType, Transfer, TransferStatus, ENDPOINT_DIRECTION_OFFSET,
};
/// Isochronous, Bulk or Interrupt endpoint.
pub struct UsbEndpoint {
fail_handle: Arc<dyn FailHandle>,
job_queue: Arc<AsyncJobQueue>,
device_handle: Arc<Mutex<DeviceHandle>>,
device: Arc<Mutex<Device>>,
endpoint_number: u8,
direction: EndpointDirection,
ty: EndpointType,
@ -36,7 +34,7 @@ impl UsbEndpoint {
pub fn new(
fail_handle: Arc<dyn FailHandle>,
job_queue: Arc<AsyncJobQueue>,
device_handle: Arc<Mutex<DeviceHandle>>,
device: Arc<Mutex<Device>>,
endpoint_number: u8,
direction: EndpointDirection,
ty: EndpointType,
@ -45,7 +43,7 @@ impl UsbEndpoint {
UsbEndpoint {
fail_handle,
job_queue,
device_handle,
device,
endpoint_number,
direction,
ty,
@ -101,13 +99,23 @@ impl UsbEndpoint {
Ok(())
}
fn get_transfer_buffer(&self, buffer: &ScatterGatherBuffer) -> Result<Vec<u8>> {
let mut v = vec![0u8; buffer.len().map_err(Error::BufferLen)?];
if self.direction == EndpointDirection::HostToDevice {
// Read data from ScatterGatherBuffer to a continuous memory.
buffer.read(v.as_mut_slice()).map_err(Error::ReadBuffer)?;
}
Ok(v)
}
fn handle_bulk_transfer(
&self,
xhci_transfer: XhciTransfer,
buffer: ScatterGatherBuffer,
) -> Result<()> {
let transfer_buffer = self.get_transfer_buffer(&buffer)?;
let usb_transfer =
bulk_transfer(self.ep_addr(), 0, buffer.len().map_err(Error::BufferLen)?);
Transfer::new_bulk(self.ep_addr(), transfer_buffer).map_err(Error::CreateTransfer)?;
self.do_handle_transfer(xhci_transfer, usb_transfer, buffer)
}
@ -116,32 +124,28 @@ impl UsbEndpoint {
xhci_transfer: XhciTransfer,
buffer: ScatterGatherBuffer,
) -> Result<()> {
let usb_transfer =
interrupt_transfer(self.ep_addr(), 0, buffer.len().map_err(Error::BufferLen)?);
let transfer_buffer = self.get_transfer_buffer(&buffer)?;
let usb_transfer = Transfer::new_interrupt(self.ep_addr(), transfer_buffer)
.map_err(Error::CreateTransfer)?;
self.do_handle_transfer(xhci_transfer, usb_transfer, buffer)
}
fn do_handle_transfer(
&self,
xhci_transfer: XhciTransfer,
mut usb_transfer: UsbTransfer<BulkTransferBuffer>,
mut usb_transfer: Transfer,
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?}",
"out transfer ep_addr {:#x}, buffer len {:?}",
self.ep_addr(),
buffer.len(),
usb_transfer.buffer_mut().as_mut_slice()
);
let callback = move |t: UsbTransfer<BulkTransferBuffer>| {
let callback = move |t: Transfer| {
usb_debug!("out transfer callback");
update_transfer_state(&xhci_transfer, &t)?;
let state = xhci_transfer.state().lock();
@ -168,20 +172,18 @@ impl UsbEndpoint {
}
};
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();
}
},
);
usb_transfer.set_callback(move |t: Transfer| 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,
&mut self.device.lock(),
usb_transfer,
)?;
}
@ -192,12 +194,8 @@ impl UsbEndpoint {
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()
);
let callback = move |t: Transfer| {
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 {
@ -212,7 +210,7 @@ impl UsbEndpoint {
let status = t.status();
let actual_length = t.actual_length() as usize;
let copied_length = buffer
.write(t.buffer().as_slice())
.write(t.buffer.as_slice())
.map_err(Error::WriteBuffer)?;
let actual_length = cmp::min(actual_length, copied_length);
drop(state);
@ -230,21 +228,19 @@ impl UsbEndpoint {
};
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();
}
},
);
usb_transfer.set_callback(move |t: Transfer| 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,
&mut self.device.lock(),
usb_transfer,
)?;
}

View file

@ -4,20 +4,18 @@
use std::mem;
use std::sync::Arc;
use sync::Mutex;
use super::error::*;
use crate::usb::xhci::xhci_transfer::{XhciTransfer, XhciTransferState};
use crate::utils::AsyncJobQueue;
use crate::utils::FailHandle;
use sys_util::{error, warn};
use usb_util::device_handle::DeviceHandle;
use usb_util::usb_transfer::{TransferStatus, UsbTransfer, UsbTransferBuffer};
use usb_util::{Device, Transfer, TransferStatus};
/// Helper function to update xhci_transfer state.
pub fn update_transfer_state<T: UsbTransferBuffer>(
pub fn update_transfer_state(
xhci_transfer: &Arc<XhciTransfer>,
usb_transfer: &UsbTransfer<T>,
usb_transfer: &Transfer,
) -> Result<()> {
let status = usb_transfer.status();
let mut state = xhci_transfer.state().lock();
@ -44,12 +42,12 @@ pub fn update_transfer_state<T: UsbTransferBuffer>(
}
/// Helper function to submit usb_transfer to device handle.
pub fn submit_transfer<T: UsbTransferBuffer>(
pub fn submit_transfer(
fail_handle: Arc<dyn FailHandle>,
job_queue: &Arc<AsyncJobQueue>,
xhci_transfer: Arc<XhciTransfer>,
device_handle: &Arc<Mutex<DeviceHandle>>,
usb_transfer: UsbTransfer<T>,
device: &mut Device,
usb_transfer: Transfer,
) -> Result<()> {
let transfer_status = {
// We need to hold the lock to avoid race condition.
@ -58,7 +56,7 @@ pub fn submit_transfer<T: UsbTransferBuffer>(
let mut state = xhci_transfer.state().lock();
match mem::replace(&mut *state, XhciTransferState::Cancelled) {
XhciTransferState::Created => {
match device_handle.lock().submit_async_transfer(usb_transfer) {
match device.submit_transfer(usb_transfer) {
Err(e) => {
error!("fail to submit transfer {:?}", e);
*state = XhciTransferState::Completed;
@ -66,13 +64,12 @@ pub fn submit_transfer<T: UsbTransferBuffer>(
}
// If it's submitted, we don't need to send on_transfer_complete now.
Ok(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");
let cancel_callback = Box::new(move || match canceller.cancel() {
Ok(()) => {
usb_debug!("cancel issued to kernel");
}
false => {
usb_debug!("fail to cancel");
Err(e) => {
usb_debug!("fail to cancel: {}", e);
}
});
*state = XhciTransferState::Submitted { cancel_callback };

View file

@ -205,41 +205,6 @@ impl UsbHub {
UsbHub { ports }
}
/// Try to detach device of bus, addr, vid, pid
pub fn try_detach(&self, bus: u8, addr: u8, vid: u16, pid: u16) -> Result<()> {
for port in &self.ports {
// This block exists so that we only hold the backend device
// lock while checking the address. It needs to be dropped before
// calling port.detach(), because that acquires the backend
// device lock again.
{
let backend_device = port.get_backend_device();
let d = match backend_device.as_ref() {
None => continue,
Some(d) => d,
};
if d.host_bus() != bus
|| d.host_address() != addr
|| d.get_vid() != vid
|| d.get_pid() != pid
{
continue;
}
}
return port.detach();
}
Err(Error::NoSuchDevice {
bus,
addr,
vid,
pid,
})
}
/// Reset all ports.
pub fn reset(&self) -> Result<()> {
usb_debug!("reseting usb hub");

View file

@ -18,10 +18,6 @@ pub enum BackendType {
pub trait XhciBackendDevice: Send {
/// Returns the type of USB device provided by this device.
fn get_backend_type(&self) -> BackendType;
/// Returns host bus number of this device.
fn host_bus(&self) -> u8;
/// Returns host address of this device.
fn host_address(&self) -> u8;
/// Get vendor id of this device.
fn get_vid(&self) -> u16;
/// Get product id of this device.

View file

@ -17,8 +17,7 @@ use std::mem;
use std::sync::{Arc, Weak};
use sync::Mutex;
use sys_util::{error, Error as SysError, EventFd, GuestMemory};
use usb_util::types::UsbRequestSetup;
use usb_util::usb_transfer::TransferStatus;
use usb_util::{TransferStatus, UsbRequestSetup};
#[derive(Debug)]
pub enum Error {
@ -67,7 +66,7 @@ pub enum XhciTransferState {
/// When transfer is submitted, it will contain a transfer callback, which should be invoked
/// when the transfer is cancelled.
Submitted {
cancel_callback: Box<dyn FnMut() + Send>,
cancel_callback: Box<dyn FnOnce() + Send>,
},
Cancelling,
Cancelled,
@ -78,9 +77,7 @@ impl XhciTransferState {
/// Try to cancel this transfer, if it's possible.
pub fn try_cancel(&mut self) {
match mem::replace(self, XhciTransferState::Created) {
XhciTransferState::Submitted {
mut cancel_callback,
} => {
XhciTransferState::Submitted { cancel_callback } => {
*self = XhciTransferState::Cancelling;
cancel_callback();
}

View file

@ -139,6 +139,10 @@ impl Default for usbdevfs_urb {
}
}
// The structure that embeds this should ensure that this is safe.
unsafe impl Send for usbdevfs_urb {}
unsafe impl Sync for usbdevfs_urb {}
#[repr(C)]
#[derive(Copy, Clone)]
pub struct usbdevfs_ioctl {

View file

@ -3,15 +3,11 @@ name = "usb_util"
version = "0.1.0"
authors = ["The Chromium OS Authors"]
edition = "2018"
build = "build.rs"
[features]
sandboxed-libusb = []
[dependencies]
assertions = { path = "../assertions" }
data_model = { path = "../data_model" }
sync = { path = "../sync" }
[build-dependencies]
pkg-config = "=0.3.11"
libc = "*"
remain = "*"
sys_util = { path = "../sys_util" }
usb_sys = { path = "../usb_sys" }

View file

@ -1,10 +0,0 @@
// Copyright 2018 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::env;
fn main() {
env::set_var("PKG_CONFIG_ALLOW_CROSS", "1");
pkg_config::probe_library("libusb-1.0").unwrap();
}

File diff suppressed because it is too large Load diff

View file

@ -1,66 +0,0 @@
// Copyright 2018 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::ops::Deref;
use crate::bindings::{self, libusb_config_descriptor};
use crate::interface_descriptor::InterfaceDescriptor;
/// ConfigDescriptor wraps libusb_config_descriptor.
pub struct ConfigDescriptor {
descriptor: *mut libusb_config_descriptor,
}
impl Drop for ConfigDescriptor {
// Free configuration descriptor.
// Safe because 'self' is intialized with a valid libusb_config_descriptor from libusb
// functions.
fn drop(&mut self) {
unsafe {
bindings::libusb_free_config_descriptor(self.descriptor);
}
}
}
impl ConfigDescriptor {
/// Build ConfigDescriptor. 'descriptor' should be a valid pointer from
/// libusb_config_descriptor. This function will panic if it's null.
pub unsafe fn new(descriptor: *mut libusb_config_descriptor) -> ConfigDescriptor {
assert!(!descriptor.is_null());
ConfigDescriptor { descriptor }
}
/// Get interface by number and alt setting.
pub fn get_interface_descriptor(
&self,
interface_num: u8,
alt_setting: i32,
) -> Option<InterfaceDescriptor> {
let config_descriptor = self.deref();
if interface_num >= config_descriptor.bNumInterfaces {
return None;
}
// Safe because interface num is checked.
let interface = unsafe { &*(config_descriptor.interface.offset(interface_num as isize)) };
if alt_setting >= interface.num_altsetting {
return None;
}
// Safe because setting num is checked.
unsafe {
Some(InterfaceDescriptor::new(
&*(interface.altsetting.offset(alt_setting as isize)),
))
}
}
}
impl Deref for ConfigDescriptor {
type Target = libusb_config_descriptor;
fn deref(&self) -> &libusb_config_descriptor {
// Safe because 'self.descriptor' is valid.
unsafe { &*(self.descriptor) }
}
}

499
usb_util/src/descriptor.rs Normal file
View file

@ -0,0 +1,499 @@
// 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 crate::types::{self, Descriptor, DescriptorHeader, EndpointDescriptor};
use crate::{Error, Result};
use data_model::DataInit;
use std::collections::BTreeMap;
use std::io::{self, Read};
use std::mem::size_of;
use std::ops::Deref;
use sys_util::warn;
#[derive(Clone)]
pub struct DeviceDescriptorTree {
inner: types::DeviceDescriptor,
// Map of bConfigurationValue to ConfigDescriptor
config_descriptors: BTreeMap<u8, ConfigDescriptorTree>,
}
#[derive(Clone)]
pub struct ConfigDescriptorTree {
inner: types::ConfigDescriptor,
// Map of (bInterfaceNumber, bAlternateSetting) to InterfaceDescriptor
interface_descriptors: BTreeMap<(u8, u8), InterfaceDescriptorTree>,
}
#[derive(Clone)]
pub struct InterfaceDescriptorTree {
inner: types::InterfaceDescriptor,
// Map of bEndpointAddress to EndpointDescriptor
endpoint_descriptors: BTreeMap<u8, EndpointDescriptor>,
}
impl DeviceDescriptorTree {
pub fn get_config_descriptor(&self, config_value: u8) -> Option<&ConfigDescriptorTree> {
self.config_descriptors.get(&config_value)
}
}
impl Deref for DeviceDescriptorTree {
type Target = types::DeviceDescriptor;
fn deref(&self) -> &types::DeviceDescriptor {
&self.inner
}
}
impl ConfigDescriptorTree {
/// Get interface by number and alt setting.
pub fn get_interface_descriptor(
&self,
interface_num: u8,
alt_setting: u8,
) -> Option<&InterfaceDescriptorTree> {
self.interface_descriptors
.get(&(interface_num, alt_setting))
}
}
impl Deref for ConfigDescriptorTree {
type Target = types::ConfigDescriptor;
fn deref(&self) -> &types::ConfigDescriptor {
&self.inner
}
}
impl InterfaceDescriptorTree {
pub fn get_endpoint_descriptor(&self, ep_idx: u8) -> Option<&EndpointDescriptor> {
self.endpoint_descriptors.get(&ep_idx)
}
}
impl Deref for InterfaceDescriptorTree {
type Target = types::InterfaceDescriptor;
fn deref(&self) -> &types::InterfaceDescriptor {
&self.inner
}
}
/// Given a `reader` for a full set of descriptors as provided by the Linux kernel
/// usbdevfs `descriptors` file, parse the descriptors into a tree data structure.
pub fn parse_usbfs_descriptors<R: Read>(mut reader: R) -> Result<DeviceDescriptorTree> {
// Given a structure of length `struct_length`, of which `bytes_consumed` have
// already been read, skip the remainder of the struct. If `bytes_consumed` is
// more than `struct_length`, no additional bytes are skipped.
fn skip<R: Read>(reader: R, bytes_consumed: usize, struct_length: u8) -> io::Result<u64> {
let bytes_to_skip = u64::from(struct_length).saturating_sub(bytes_consumed as u64);
io::copy(&mut reader.take(bytes_to_skip), &mut io::sink())
}
// Find the next descriptor of type T and return it.
// Any other descriptors encountered while searching for the expected type are skipped.
fn next_descriptor<R: Read, T: Descriptor + DataInit>(mut reader: R) -> Result<T> {
let desc_type = T::descriptor_type() as u8;
loop {
let hdr = DescriptorHeader::from_reader(&mut reader).map_err(Error::DescriptorRead)?;
if hdr.bDescriptorType == desc_type {
if usize::from(hdr.bLength) < size_of::<DescriptorHeader>() + size_of::<T>() {
return Err(Error::DescriptorParse);
}
let desc = T::from_reader(&mut reader).map_err(Error::DescriptorRead)?;
// Skip any extra data beyond the standard descriptor length.
skip(
&mut reader,
size_of::<DescriptorHeader>() + size_of::<T>(),
hdr.bLength,
)
.map_err(Error::DescriptorRead)?;
return Ok(desc);
}
// Skip this entire descriptor, since it's not the right type.
skip(&mut reader, size_of::<DescriptorHeader>(), hdr.bLength)
.map_err(Error::DescriptorRead)?;
}
}
let raw_device_descriptor: types::DeviceDescriptor = next_descriptor(&mut reader)?;
let mut device_descriptor = DeviceDescriptorTree {
inner: raw_device_descriptor,
config_descriptors: BTreeMap::new(),
};
for cfg_idx in 0..device_descriptor.bNumConfigurations {
if let Ok(raw_config_descriptor) =
next_descriptor::<_, types::ConfigDescriptor>(&mut reader)
{
let mut config_descriptor = ConfigDescriptorTree {
inner: raw_config_descriptor,
interface_descriptors: BTreeMap::new(),
};
for intf_idx in 0..config_descriptor.bNumInterfaces {
if let Ok(raw_interface_descriptor) =
next_descriptor::<_, types::InterfaceDescriptor>(&mut reader)
{
let mut interface_descriptor = InterfaceDescriptorTree {
inner: raw_interface_descriptor,
endpoint_descriptors: BTreeMap::new(),
};
for ep_idx in 0..interface_descriptor.bNumEndpoints {
if let Ok(endpoint_descriptor) =
next_descriptor::<_, EndpointDescriptor>(&mut reader)
{
interface_descriptor
.endpoint_descriptors
.insert(ep_idx, endpoint_descriptor);
} else {
warn!("Could not read endpoint descriptor {}", ep_idx);
break;
}
}
config_descriptor.interface_descriptors.insert(
(
interface_descriptor.bInterfaceNumber,
interface_descriptor.bAlternateSetting,
),
interface_descriptor,
);
} else {
warn!("Could not read interface descriptor {}", intf_idx);
break;
}
}
device_descriptor
.config_descriptors
.insert(config_descriptor.bConfigurationValue, config_descriptor);
} else {
warn!("Could not read config descriptor {}", cfg_idx);
break;
}
}
Ok(device_descriptor)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_descriptors_mass_storage() {
let data: &[u8] = &[
0x12, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x09, 0x81, 0x07, 0x80, 0x55, 0x10, 0x00,
0x01, 0x02, 0x03, 0x01, 0x09, 0x02, 0x2C, 0x00, 0x01, 0x01, 0x00, 0x80, 0x32, 0x09,
0x04, 0x00, 0x00, 0x02, 0x08, 0x06, 0x50, 0x00, 0x07, 0x05, 0x81, 0x02, 0x00, 0x04,
0x00, 0x06, 0x30, 0x0F, 0x00, 0x00, 0x00, 0x07, 0x05, 0x02, 0x02, 0x00, 0x04, 0x00,
0x06, 0x30, 0x0F, 0x00, 0x00, 0x00,
];
let d = parse_usbfs_descriptors(data).expect("parse_usbfs_descriptors failed");
// The seemingly-redundant u16::from() calls avoid borrows of packed fields.
assert_eq!(u16::from(d.bcdUSB), 0x03_00);
assert_eq!(d.bDeviceClass, 0x00);
assert_eq!(d.bDeviceSubClass, 0x00);
assert_eq!(d.bDeviceProtocol, 0x00);
assert_eq!(d.bMaxPacketSize0, 9);
assert_eq!(u16::from(d.idVendor), 0x0781);
assert_eq!(u16::from(d.idProduct), 0x5580);
assert_eq!(u16::from(d.bcdDevice), 0x00_10);
assert_eq!(d.iManufacturer, 1);
assert_eq!(d.iProduct, 2);
assert_eq!(d.iSerialNumber, 3);
assert_eq!(d.bNumConfigurations, 1);
let c = d
.get_config_descriptor(1)
.expect("could not get config descriptor 1");
assert_eq!(u16::from(c.wTotalLength), 44);
assert_eq!(c.bNumInterfaces, 1);
assert_eq!(c.bConfigurationValue, 1);
assert_eq!(c.iConfiguration, 0);
assert_eq!(c.bmAttributes, 0x80);
assert_eq!(c.bMaxPower, 50);
let i = c
.get_interface_descriptor(0, 0)
.expect("could not get interface descriptor 0 alt setting 0");
assert_eq!(i.bInterfaceNumber, 0);
assert_eq!(i.bAlternateSetting, 0);
assert_eq!(i.bNumEndpoints, 2);
assert_eq!(i.bInterfaceClass, 0x08);
assert_eq!(i.bInterfaceSubClass, 0x06);
assert_eq!(i.bInterfaceProtocol, 0x50);
assert_eq!(i.iInterface, 0);
let e = i
.get_endpoint_descriptor(0)
.expect("could not get endpoint 0 descriptor");
assert_eq!(e.bEndpointAddress, 0x81);
assert_eq!(e.bmAttributes, 0x02);
assert_eq!(u16::from(e.wMaxPacketSize), 0x0400);
assert_eq!(e.bInterval, 0);
let e = i
.get_endpoint_descriptor(1)
.expect("could not get endpoint 1 descriptor");
assert_eq!(e.bEndpointAddress, 0x02);
assert_eq!(e.bmAttributes, 0x02);
assert_eq!(u16::from(e.wMaxPacketSize), 0x0400);
assert_eq!(e.bInterval, 0);
}
#[test]
fn parse_descriptors_servo() {
let data: &[u8] = &[
0x12, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x40, 0xd1, 0x18, 0x1b, 0x50, 0x00, 0x01,
0x01, 0x02, 0x03, 0x01, 0x09, 0x02, 0x7c, 0x00, 0x06, 0x01, 0x04, 0xc0, 0xfa, 0x09,
0x04, 0x00, 0x00, 0x02, 0xff, 0x50, 0x01, 0x06, 0x07, 0x05, 0x81, 0x02, 0x40, 0x00,
0x0a, 0x07, 0x05, 0x01, 0x02, 0x40, 0x00, 0x00, 0x09, 0x04, 0x02, 0x00, 0x02, 0xff,
0x52, 0x01, 0x05, 0x07, 0x05, 0x83, 0x02, 0x40, 0x00, 0x0a, 0x07, 0x05, 0x03, 0x02,
0x40, 0x00, 0x00, 0x09, 0x04, 0x03, 0x00, 0x02, 0xff, 0x50, 0x01, 0x07, 0x07, 0x05,
0x84, 0x02, 0x10, 0x00, 0x0a, 0x07, 0x05, 0x04, 0x02, 0x10, 0x00, 0x00, 0x09, 0x04,
0x04, 0x00, 0x02, 0xff, 0x50, 0x01, 0x08, 0x07, 0x05, 0x85, 0x02, 0x10, 0x00, 0x0a,
0x07, 0x05, 0x05, 0x02, 0x10, 0x00, 0x00, 0x09, 0x04, 0x05, 0x00, 0x02, 0xff, 0x53,
0xff, 0x09, 0x07, 0x05, 0x86, 0x02, 0x40, 0x00, 0x0a, 0x07, 0x05, 0x06, 0x02, 0x40,
0x00, 0x00,
];
// Note: configuration 1 has bNumInterfaces == 6, but it actually only contains 5
// interface descriptors. This causes us to try to read beyond EOF, which should be
// silently ignored by parse_usbfs_descriptors so that we can use the rest of the
// descriptors.
let d = parse_usbfs_descriptors(data).expect("parse_usbfs_descriptors failed");
// The seemingly-redundant u16::from() calls avoid borrows of packed fields.
assert_eq!(u16::from(d.bcdUSB), 0x02_00);
assert_eq!(d.bDeviceClass, 0x00);
assert_eq!(d.bDeviceSubClass, 0x00);
assert_eq!(d.bDeviceProtocol, 0x00);
assert_eq!(d.bMaxPacketSize0, 64);
assert_eq!(u16::from(d.idVendor), 0x18d1);
assert_eq!(u16::from(d.idProduct), 0x501b);
assert_eq!(u16::from(d.bcdDevice), 0x01_00);
assert_eq!(d.iManufacturer, 1);
assert_eq!(d.iProduct, 2);
assert_eq!(d.iSerialNumber, 3);
assert_eq!(d.bNumConfigurations, 1);
let c = d
.get_config_descriptor(1)
.expect("could not get config descriptor 1");
assert_eq!(u16::from(c.wTotalLength), 124);
assert_eq!(c.bNumInterfaces, 6);
assert_eq!(c.bConfigurationValue, 1);
assert_eq!(c.iConfiguration, 4);
assert_eq!(c.bmAttributes, 0xc0);
assert_eq!(c.bMaxPower, 250);
let i = c
.get_interface_descriptor(0, 0)
.expect("could not get interface descriptor 0 alt setting 0");
assert_eq!(i.bInterfaceNumber, 0);
assert_eq!(i.bAlternateSetting, 0);
assert_eq!(i.bNumEndpoints, 2);
assert_eq!(i.bInterfaceClass, 0xff);
assert_eq!(i.bInterfaceSubClass, 0x50);
assert_eq!(i.bInterfaceProtocol, 0x01);
assert_eq!(i.iInterface, 6);
let e = i
.get_endpoint_descriptor(0)
.expect("could not get endpoint 0 descriptor");
assert_eq!(e.bEndpointAddress, 0x81);
assert_eq!(e.bmAttributes, 0x02);
assert_eq!(u16::from(e.wMaxPacketSize), 0x0040);
assert_eq!(e.bInterval, 10);
let e = i
.get_endpoint_descriptor(1)
.expect("could not get endpoint 1 descriptor");
assert_eq!(e.bEndpointAddress, 0x01);
assert_eq!(e.bmAttributes, 0x02);
assert_eq!(u16::from(e.wMaxPacketSize), 0x0040);
assert_eq!(e.bInterval, 0);
let i = c
.get_interface_descriptor(2, 0)
.expect("could not get interface descriptor 2 alt setting 0");
assert_eq!(i.bInterfaceNumber, 2);
assert_eq!(i.bAlternateSetting, 0);
assert_eq!(i.bNumEndpoints, 2);
assert_eq!(i.bInterfaceClass, 0xff);
assert_eq!(i.bInterfaceSubClass, 0x52);
assert_eq!(i.bInterfaceProtocol, 0x01);
assert_eq!(i.iInterface, 5);
let e = i
.get_endpoint_descriptor(0)
.expect("could not get endpoint 0 descriptor");
assert_eq!(e.bEndpointAddress, 0x83);
assert_eq!(e.bmAttributes, 0x02);
assert_eq!(u16::from(e.wMaxPacketSize), 0x0040);
assert_eq!(e.bInterval, 10);
let e = i
.get_endpoint_descriptor(1)
.expect("could not get endpoint 1 descriptor");
assert_eq!(e.bEndpointAddress, 0x03);
assert_eq!(e.bmAttributes, 0x02);
assert_eq!(u16::from(e.wMaxPacketSize), 0x0040);
assert_eq!(e.bInterval, 0);
let i = c
.get_interface_descriptor(3, 0)
.expect("could not get interface descriptor 3 alt setting 0");
assert_eq!(i.bInterfaceNumber, 3);
assert_eq!(i.bAlternateSetting, 0);
assert_eq!(i.bNumEndpoints, 2);
assert_eq!(i.bInterfaceClass, 0xff);
assert_eq!(i.bInterfaceSubClass, 0x50);
assert_eq!(i.bInterfaceProtocol, 0x01);
assert_eq!(i.iInterface, 7);
let e = i
.get_endpoint_descriptor(0)
.expect("could not get endpoint 0 descriptor");
assert_eq!(e.bEndpointAddress, 0x84);
assert_eq!(e.bmAttributes, 0x02);
assert_eq!(u16::from(e.wMaxPacketSize), 0x0010);
assert_eq!(e.bInterval, 10);
let e = i
.get_endpoint_descriptor(1)
.expect("could not get endpoint 1 descriptor");
assert_eq!(e.bEndpointAddress, 0x04);
assert_eq!(e.bmAttributes, 0x02);
assert_eq!(u16::from(e.wMaxPacketSize), 0x0010);
assert_eq!(e.bInterval, 0);
let i = c
.get_interface_descriptor(4, 0)
.expect("could not get interface descriptor 4 alt setting 0");
assert_eq!(i.bInterfaceNumber, 4);
assert_eq!(i.bAlternateSetting, 0);
assert_eq!(i.bNumEndpoints, 2);
assert_eq!(i.bInterfaceClass, 0xff);
assert_eq!(i.bInterfaceSubClass, 0x50);
assert_eq!(i.bInterfaceProtocol, 0x01);
assert_eq!(i.iInterface, 8);
let e = i
.get_endpoint_descriptor(0)
.expect("could not get endpoint 0 descriptor");
assert_eq!(e.bEndpointAddress, 0x85);
assert_eq!(e.bmAttributes, 0x02);
assert_eq!(u16::from(e.wMaxPacketSize), 0x0010);
assert_eq!(e.bInterval, 10);
let e = i
.get_endpoint_descriptor(1)
.expect("could not get endpoint 1 descriptor");
assert_eq!(e.bEndpointAddress, 0x05);
assert_eq!(e.bmAttributes, 0x02);
assert_eq!(u16::from(e.wMaxPacketSize), 0x0010);
assert_eq!(e.bInterval, 0);
let i = c
.get_interface_descriptor(5, 0)
.expect("could not get interface descriptor 5 alt setting 0");
assert_eq!(i.bInterfaceNumber, 5);
assert_eq!(i.bAlternateSetting, 0);
assert_eq!(i.bNumEndpoints, 2);
assert_eq!(i.bInterfaceClass, 0xff);
assert_eq!(i.bInterfaceSubClass, 0x53);
assert_eq!(i.bInterfaceProtocol, 0xff);
assert_eq!(i.iInterface, 9);
let e = i
.get_endpoint_descriptor(0)
.expect("could not get endpoint 0 descriptor");
assert_eq!(e.bEndpointAddress, 0x86);
assert_eq!(e.bmAttributes, 0x02);
assert_eq!(u16::from(e.wMaxPacketSize), 0x0040);
assert_eq!(e.bInterval, 10);
let e = i
.get_endpoint_descriptor(1)
.expect("could not get endpoint 1 descriptor");
assert_eq!(e.bEndpointAddress, 0x06);
assert_eq!(e.bmAttributes, 0x02);
assert_eq!(u16::from(e.wMaxPacketSize), 0x0040);
assert_eq!(e.bInterval, 0);
}
#[test]
fn parse_descriptors_adb() {
let data: &[u8] = &[
0x12, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x40, 0xd1, 0x18, 0xe7, 0x4e, 0x10, 0x03,
0x01, 0x02, 0x03, 0x01, 0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 0x00, 0x80, 0xfa, 0x09,
0x04, 0x00, 0x00, 0x02, 0xff, 0x42, 0x01, 0x05, 0x07, 0x05, 0x01, 0x02, 0x00, 0x02,
0x00, 0x07, 0x05, 0x81, 0x02, 0x00, 0x02, 0x00,
];
let d = parse_usbfs_descriptors(data).expect("parse_usbfs_descriptors failed");
// The seemingly-redundant u16::from() calls avoid borrows of packed fields.
assert_eq!(u16::from(d.bcdUSB), 0x02_00);
assert_eq!(d.bDeviceClass, 0x00);
assert_eq!(d.bDeviceSubClass, 0x00);
assert_eq!(d.bDeviceProtocol, 0x00);
assert_eq!(d.bMaxPacketSize0, 64);
assert_eq!(u16::from(d.idVendor), 0x18d1);
assert_eq!(u16::from(d.idProduct), 0x4ee7);
assert_eq!(u16::from(d.bcdDevice), 0x03_10);
assert_eq!(d.iManufacturer, 1);
assert_eq!(d.iProduct, 2);
assert_eq!(d.iSerialNumber, 3);
assert_eq!(d.bNumConfigurations, 1);
let c = d
.get_config_descriptor(1)
.expect("could not get config descriptor 1");
assert_eq!(u16::from(c.wTotalLength), 32);
assert_eq!(c.bNumInterfaces, 1);
assert_eq!(c.bConfigurationValue, 1);
assert_eq!(c.iConfiguration, 0);
assert_eq!(c.bmAttributes, 0x80);
assert_eq!(c.bMaxPower, 250);
let i = c
.get_interface_descriptor(0, 0)
.expect("could not get interface descriptor 0 alt setting 0");
assert_eq!(i.bInterfaceNumber, 0);
assert_eq!(i.bAlternateSetting, 0);
assert_eq!(i.bNumEndpoints, 2);
assert_eq!(i.bInterfaceClass, 0xff);
assert_eq!(i.bInterfaceSubClass, 0x42);
assert_eq!(i.bInterfaceProtocol, 0x01);
assert_eq!(i.iInterface, 5);
let e = i
.get_endpoint_descriptor(0)
.expect("could not get endpoint 0 descriptor");
assert_eq!(e.bEndpointAddress, 0x01);
assert_eq!(e.bmAttributes, 0x02);
assert_eq!(u16::from(e.wMaxPacketSize), 0x200);
assert_eq!(e.bInterval, 0);
let e = i
.get_endpoint_descriptor(1)
.expect("could not get endpoint 1 descriptor");
assert_eq!(e.bEndpointAddress, 0x81);
assert_eq!(e.bmAttributes, 0x02);
assert_eq!(u16::from(e.wMaxPacketSize), 0x0200);
assert_eq!(e.bInterval, 0);
}
}

458
usb_util/src/device.rs Normal file
View file

@ -0,0 +1,458 @@
// 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 crate::{
control_request_type, descriptor, ConfigDescriptorTree,
ControlRequestDataPhaseTransferDirection, ControlRequestRecipient, ControlRequestType,
DeviceDescriptor, DeviceDescriptorTree, Error, Result, StandardControlRequest,
};
use libc::{EAGAIN, ENODEV, ENOENT};
use std::convert::TryInto;
use std::fs::File;
use std::io::{Seek, SeekFrom};
use std::mem::{size_of, size_of_val};
use std::os::raw::{c_int, c_uint, c_ulong, c_void};
use std::sync::Arc;
use sys_util::handle_eintr_errno;
/// Device represents a USB device.
pub struct Device {
fd: Arc<File>,
device_descriptor_tree: DeviceDescriptorTree,
}
/// Transfer contains the information necessary to submit a USB request
/// and, once it has been submitted and completed, contains the response.
pub struct Transfer {
// NOTE: This Vec is actually a single URB with a trailing
// variable-length field created by vec_with_array_field().
urb: Vec<usb_sys::usbdevfs_urb>,
pub buffer: Vec<u8>,
callback: Option<Box<dyn Fn(Transfer) + Send + Sync>>,
}
/// TransferHandle is a handle that allows cancellation of in-flight transfers
/// between submit_transfer() and get_completed_transfer().
/// Attempting to cancel a transfer that has already completed is safe and will
/// return an error.
pub struct TransferHandle {
weak_transfer: std::sync::Weak<Transfer>,
fd: std::sync::Weak<File>,
}
#[derive(PartialEq)]
pub enum TransferStatus {
Completed,
Error,
Cancelled,
NoDevice,
}
impl Device {
/// Create a new `Device` from a file descriptor.
/// `fd` should be a file in usbdevfs (e.g. `/dev/bus/usb/001/002`).
pub fn new(mut fd: File) -> Result<Self> {
fd.seek(SeekFrom::Start(0)).map_err(Error::DescriptorRead)?;
let device_descriptor_tree = descriptor::parse_usbfs_descriptors(&mut fd)?;
let device = Device {
fd: Arc::new(fd),
device_descriptor_tree,
};
Ok(device)
}
pub fn fd(&self) -> Arc<File> {
self.fd.clone()
}
unsafe fn ioctl(&self, nr: c_ulong) -> Result<i32> {
let ret = handle_eintr_errno!(sys_util::ioctl(&*self.fd, nr));
if ret < 0 {
return Err(Error::IoctlFailed(nr, sys_util::Error::last()));
}
Ok(ret)
}
unsafe fn ioctl_with_ref<T>(&self, nr: c_ulong, arg: &T) -> Result<i32> {
let ret = handle_eintr_errno!(sys_util::ioctl_with_ref(&*self.fd, nr, arg));
if ret < 0 {
return Err(Error::IoctlFailed(nr, sys_util::Error::last()));
}
Ok(ret)
}
unsafe fn ioctl_with_mut_ref<T>(&self, nr: c_ulong, arg: &mut T) -> Result<i32> {
let ret = handle_eintr_errno!(sys_util::ioctl_with_mut_ref(&*self.fd, nr, arg));
if ret < 0 {
return Err(Error::IoctlFailed(nr, sys_util::Error::last()));
}
Ok(ret)
}
unsafe fn ioctl_with_mut_ptr<T>(&self, nr: c_ulong, arg: *mut T) -> Result<i32> {
let ret = handle_eintr_errno!(sys_util::ioctl_with_mut_ptr(&*self.fd, nr, arg));
if ret < 0 {
return Err(Error::IoctlFailed(nr, sys_util::Error::last()));
}
Ok(ret)
}
/// Submit a transfer to the device.
/// The transfer will be processed asynchronously by the device.
/// Call `poll_transfers()` on this device to check for completed transfers.
pub fn submit_transfer(&mut self, transfer: Transfer) -> Result<TransferHandle> {
let mut rc_transfer = Arc::new(transfer);
// Technically, Arc::from_raw() should only be called on pointers returned
// from Arc::into_raw(). However, we need to stash this value inside the
// Arc<Transfer> itself, so we manually calculate the address that would be
// returned from Arc::into_raw() via Deref and then call Arc::into_raw()
// to forget the Arc without dropping its contents.
// Do not remove the into_raw() call!
let raw_transfer = (&*rc_transfer) as *const Transfer as usize;
match Arc::get_mut(&mut rc_transfer) {
Some(t) => t.urb_mut().usercontext = raw_transfer,
None => {
// This should never happen, since there is only one strong reference
// at this point.
return Err(Error::RcGetMutFailed);
}
}
let _ = Arc::into_raw(rc_transfer.clone());
let urb_ptr = rc_transfer.urb.as_ptr() as *mut usb_sys::usbdevfs_urb;
// Safe because we control the lifetime of the URB via Arc::into_raw() and
// Arc::from_raw() in poll_transfers().
unsafe {
self.ioctl_with_mut_ptr(usb_sys::USBDEVFS_SUBMITURB(), urb_ptr)?;
}
let weak_transfer = Arc::downgrade(&rc_transfer);
Ok(TransferHandle {
weak_transfer,
fd: Arc::downgrade(&self.fd),
})
}
/// Check for completed asynchronous transfers submitted via `submit_transfer()`.
/// The callback for each completed transfer will be called.
pub fn poll_transfers(&self) -> Result<()> {
// Reap completed transfers until we get EAGAIN.
loop {
let mut urb_ptr: *mut usb_sys::usbdevfs_urb = std::ptr::null_mut();
// Safe because we provide a valid urb_ptr to be filled by the kernel.
let result =
unsafe { self.ioctl_with_mut_ref(usb_sys::USBDEVFS_REAPURBNDELAY(), &mut urb_ptr) };
match result {
Err(Error::IoctlFailed(_nr, e)) => {
if e.errno() == EAGAIN {
// No more completed transfers right now.
break;
}
}
Err(e) => return Err(e),
Ok(_) => {}
}
if urb_ptr.is_null() {
break;
}
// Safe because the URB usercontext field is always set to the result of
// Arc::into_raw() in submit_transfer().
let rc_transfer: Arc<Transfer> =
unsafe { Arc::from_raw((*urb_ptr).usercontext as *const Transfer) };
// There should always be exactly one strong reference to rc_transfer,
// so try_unwrap() should never fail.
let mut transfer = Arc::try_unwrap(rc_transfer).map_err(|_| Error::RcUnwrapFailed)?;
if let Some(cb) = transfer.callback.take() {
cb(transfer);
}
}
Ok(())
}
/// Perform a USB port reset to reinitialize a device.
pub fn reset(&self) -> Result<()> {
// Safe because self.fd is a valid usbdevfs file descriptor.
let result = unsafe { self.ioctl(usb_sys::USBDEVFS_RESET()) };
if let Err(Error::IoctlFailed(_nr, errno_err)) = result {
// The device may disappear after a reset if e.g. its firmware changed.
// Treat that as success.
if errno_err.errno() == libc::ENODEV {
return Ok(());
}
}
result?;
Ok(())
}
/// Claim an interface on this device.
pub fn claim_interface(&self, interface_number: u8) -> Result<()> {
let disconnect_claim = usb_sys::usbdevfs_disconnect_claim {
interface: interface_number.into(),
flags: 0,
driver: [0u8; 256],
};
// Safe because self.fd is a valid usbdevfs file descriptor and we pass a valid
// pointer to a usbdevs_disconnect_claim structure.
unsafe {
self.ioctl_with_ref(usb_sys::USBDEVFS_DISCONNECT_CLAIM(), &disconnect_claim)?;
}
Ok(())
}
/// Release an interface previously claimed with `claim_interface()`.
pub fn release_interface(&self, interface_number: u8) -> Result<()> {
let ifnum: c_uint = interface_number.into();
// Safe because self.fd is a valid usbdevfs file descriptor and we pass a valid
// pointer to unsigned int.
unsafe {
self.ioctl_with_ref(usb_sys::USBDEVFS_RELEASEINTERFACE(), &ifnum)?;
}
Ok(())
}
/// Activate an alternate setting for an interface.
pub fn set_interface_alt_setting(
&self,
interface_number: u8,
alternative_setting: u8,
) -> Result<()> {
let setinterface = usb_sys::usbdevfs_setinterface {
interface: interface_number.into(),
altsetting: alternative_setting.into(),
};
// Safe because self.fd is a valid usbdevfs file descriptor and we pass a valid
// pointer to a usbdevfs_setinterface structure.
unsafe {
self.ioctl_with_ref(usb_sys::USBDEVFS_SETINTERFACE(), &setinterface)?;
}
Ok(())
}
/// Set active configuration for this device.
pub fn set_active_configuration(&mut self, config: u8) -> Result<()> {
let config: c_int = config.into();
// Safe because self.fd is a valid usbdevfs file descriptor and we pass a valid
// pointer to int.
unsafe {
self.ioctl_with_ref(usb_sys::USBDEVFS_SETCONFIGURATION(), &config)?;
}
Ok(())
}
/// Get the device descriptor of this device.
pub fn get_device_descriptor(&self) -> Result<DeviceDescriptor> {
Ok(*self.device_descriptor_tree)
}
/// Get active config descriptor of this device.
pub fn get_active_config_descriptor(&self) -> Result<ConfigDescriptorTree> {
let active_config = self.get_active_configuration()?;
match self
.device_descriptor_tree
.get_config_descriptor(active_config)
{
Some(config_descriptor) => Ok(config_descriptor.clone()),
None => Err(Error::NoSuchDescriptor),
}
}
/// Get bConfigurationValue of the currently active configuration.
pub fn get_active_configuration(&self) -> Result<u8> {
// Send a synchronous control transfer to get the active configuration.
let mut active_config: u8 = 0;
let ctrl_transfer = usb_sys::usbdevfs_ctrltransfer {
bRequestType: control_request_type(
ControlRequestType::Standard,
ControlRequestDataPhaseTransferDirection::DeviceToHost,
ControlRequestRecipient::Device,
),
bRequest: StandardControlRequest::GetConfiguration as u8,
wValue: 0,
wIndex: 0,
wLength: size_of_val(&active_config) as u16,
timeout: 5000, // milliseconds
data: &mut active_config as *mut u8 as *mut c_void,
};
// Safe because self.fd is a valid usbdevfs file descriptor and we pass a valid
// pointer to a usbdevfs_ctrltransfer structure.
unsafe {
self.ioctl_with_ref(usb_sys::USBDEVFS_CONTROL(), &ctrl_transfer)?;
}
Ok(active_config)
}
/// Clear the halt/stall condition for an endpoint.
pub fn clear_halt(&self, ep_addr: u8) -> Result<()> {
let endpoint: c_uint = ep_addr.into();
// Safe because self.fd is a valid usbdevfs file descriptor and we pass a valid
// pointer to unsigned int.
unsafe {
self.ioctl_with_ref(usb_sys::USBDEVFS_CLEAR_HALT(), &endpoint)?;
}
Ok(())
}
}
// Returns a `Vec<T>` with a size in bytes at least as large as `size_in_bytes`.
fn vec_with_size_in_bytes<T: Default>(size_in_bytes: usize) -> Vec<T> {
let rounded_size = (size_in_bytes + size_of::<T>() - 1) / size_of::<T>();
let mut v = Vec::with_capacity(rounded_size);
for _ in 0..rounded_size {
v.push(T::default())
}
v
}
// This function has been borrowed from kvm - see the doc comment there for details.
fn vec_with_array_field<T: Default, F>(count: usize) -> Vec<T> {
let element_space = count * size_of::<F>();
let vec_size_bytes = size_of::<T>() + element_space;
vec_with_size_in_bytes(vec_size_bytes)
}
impl Transfer {
fn urb(&self) -> &usb_sys::usbdevfs_urb {
// self.urb is a Vec created with `vec_with_array_field`; the first entry is
// the URB itself.
&self.urb[0]
}
fn urb_mut(&mut self) -> &mut usb_sys::usbdevfs_urb {
&mut self.urb[0]
}
fn new(
transfer_type: u8,
endpoint: u8,
buffer: Vec<u8>,
iso_packets: &[usb_sys::usbdevfs_iso_packet_desc],
) -> Result<Transfer> {
let mut transfer = Transfer {
urb: vec_with_array_field::<usb_sys::usbdevfs_urb, usb_sys::usbdevfs_iso_packet_desc>(
iso_packets.len(),
),
buffer,
callback: None,
};
transfer.urb_mut().urb_type = transfer_type;
transfer.urb_mut().endpoint = endpoint;
transfer.urb_mut().buffer = transfer.buffer.as_mut_ptr() as *mut c_void;
transfer.urb_mut().buffer_length = transfer
.buffer
.len()
.try_into()
.map_err(Error::InvalidBufferLength)?;
// Safe because we ensured there is enough space in transfer.urb to hold the number of
// isochronous frames required.
let iso_frame_desc = unsafe {
transfer
.urb_mut()
.iso_frame_desc
.as_mut_slice(iso_packets.len())
};
iso_frame_desc.copy_from_slice(iso_packets);
Ok(transfer)
}
/// Create a control transfer.
pub fn new_control(buffer: Vec<u8>) -> Result<Transfer> {
let endpoint = 0;
Self::new(usb_sys::USBDEVFS_URB_TYPE_CONTROL, endpoint, buffer, &[])
}
/// Create an interrupt transfer.
pub fn new_interrupt(endpoint: u8, buffer: Vec<u8>) -> Result<Transfer> {
Self::new(usb_sys::USBDEVFS_URB_TYPE_INTERRUPT, endpoint, buffer, &[])
}
/// Create a bulk transfer.
pub fn new_bulk(endpoint: u8, buffer: Vec<u8>) -> Result<Transfer> {
Self::new(usb_sys::USBDEVFS_URB_TYPE_BULK, endpoint, buffer, &[])
}
/// Create an isochronous transfer.
pub fn new_isochronous(endpoint: u8, buffer: Vec<u8>) -> Result<Transfer> {
// TODO(dverkamp): allow user to specify iso descriptors
Self::new(usb_sys::USBDEVFS_URB_TYPE_ISO, endpoint, buffer, &[])
}
/// Get the status of a completed transfer.
pub fn status(&self) -> TransferStatus {
let status = self.urb().status;
if status == 0 {
TransferStatus::Completed
} else if status == -ENODEV {
TransferStatus::NoDevice
} else if status == -ENOENT {
TransferStatus::Cancelled
} else {
TransferStatus::Error
}
}
/// Get the actual amount of data transferred, which may be less than
/// the original length.
pub fn actual_length(&self) -> usize {
self.urb().actual_length as usize
}
/// Set callback function for transfer completion.
pub fn set_callback<C: 'static + Fn(Transfer) + Send + Sync>(&mut self, cb: C) {
self.callback = Some(Box::new(cb));
}
}
impl TransferHandle {
/// Attempt to cancel the transfer associated with this `TransferHandle`.
/// Safe to call even if the transfer has already completed;
/// `Error::TransferAlreadyCompleted` will be returned in this case.
pub fn cancel(self) -> Result<()> {
let rc_transfer = match self.weak_transfer.upgrade() {
None => return Err(Error::TransferAlreadyCompleted),
Some(rc_transfer) => rc_transfer,
};
let urb_ptr = rc_transfer.urb.as_ptr() as *mut usb_sys::usbdevfs_urb;
let fd = match self.fd.upgrade() {
None => return Err(Error::NoDevice),
Some(fd) => fd,
};
// Safe because fd is a valid usbdevfs file descriptor and we pass a valid
// pointer to a usbdevfs_urb structure.
if unsafe {
handle_eintr_errno!(sys_util::ioctl_with_mut_ptr(
&*fd,
usb_sys::USBDEVFS_DISCARDURB(),
urb_ptr
))
} < 0
{
return Err(Error::IoctlFailed(
usb_sys::USBDEVFS_DISCARDURB(),
sys_util::Error::last(),
));
}
Ok(())
}
}

View file

@ -1,161 +0,0 @@
// Copyright 2018 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::os::raw::c_int;
use std::sync::Arc;
use crate::bindings;
use crate::error::{Error, Result};
use crate::libusb_context::LibUsbContextInner;
use crate::libusb_device::LibUsbDevice;
use crate::usb_transfer::{TransferCanceller, UsbTransfer, UsbTransferBuffer};
/// DeviceHandle wraps libusb_device_handle.
pub struct DeviceHandle {
_context: Arc<LibUsbContextInner>,
handle: *mut bindings::libusb_device_handle,
}
unsafe impl Send for DeviceHandle {}
impl Drop for DeviceHandle {
fn drop(&mut self) {
// Safe because self.handle is a valid pointer to libusb_device_handle.
unsafe {
bindings::libusb_close(self.handle);
}
}
}
impl DeviceHandle {
/// Create a new DeviceHande. 'handle' should be a valid pointer to libusb_device_handle.
pub unsafe fn new(
ctx: Arc<LibUsbContextInner>,
handle: *mut bindings::libusb_device_handle,
) -> DeviceHandle {
DeviceHandle {
_context: ctx,
handle,
}
}
/// Get corresponding usb device.
pub fn get_device(&self) -> LibUsbDevice {
// Safe because 'self.handle' is a valid pointer to device handle and libusb_get_device()
// always returns a valid device.
unsafe {
LibUsbDevice::new(
self._context.clone(),
bindings::libusb_get_device(self.handle),
)
}
}
/// Reset this usb device.
pub fn reset(&self) -> Result<()> {
// Safe because 'self.handle' is a valid pointer to device handle.
try_libusb!(unsafe { bindings::libusb_reset_device(self.handle) });
Ok(())
}
/// Get bConfigurationValue of the currently active configuration.
pub fn get_active_configuration(&self) -> Result<i32> {
let mut config: c_int = 0;
// Safe because 'self.handle' is a valid pointer to device handle and '&mut config' is a
// valid output location.
try_libusb!(unsafe { bindings::libusb_get_configuration(self.handle, &mut config) });
Ok(config as i32)
}
/// Set active configuration for a device.
pub fn set_active_configuration(&mut self, config: i32) -> Result<()> {
// Safe because 'self.handle' is a valid pointer to device handle.
try_libusb!(unsafe { bindings::libusb_set_configuration(self.handle, config as c_int) });
Ok(())
}
/// Claim an interface on this deivce handle.
pub fn claim_interface(&self, interface_number: i32) -> Result<()> {
// Safe because 'self.handle' is a valid pointer to device handle.
try_libusb!(unsafe { bindings::libusb_claim_interface(self.handle, interface_number) });
Ok(())
}
/// Release an interface previously claimed with libusb_claim_interface.
pub fn release_interface(&self, interface_number: i32) -> Result<()> {
// Safe because 'self.handle' is a valid pointer to device handle.
try_libusb!(unsafe { bindings::libusb_release_interface(self.handle, interface_number) });
Ok(())
}
/// Perform a USB port reset to reinitialize a device.
pub fn reset_device(&self) -> Result<()> {
// Safe because 'self.handle' is a valid pointer to device handle.
try_libusb!(unsafe { bindings::libusb_reset_device(self.handle) });
Ok(())
}
/// Determine if a kernel driver is active on an interface.
pub fn kernel_driver_active(&self, interface_number: i32) -> Result<bool> {
// Safe because 'self.handle' is a valid pointer to device handle.
let v = try_libusb!(unsafe {
bindings::libusb_kernel_driver_active(self.handle, interface_number)
});
Ok(v != 0)
}
/// Detach a kernel driver from an interface.
pub fn detach_kernel_driver(&self, interface_number: i32) -> Result<()> {
// Safe because 'self.handle' is a valid pointer to device handle.
try_libusb!(unsafe {
bindings::libusb_detach_kernel_driver(self.handle, interface_number)
});
Ok(())
}
/// Re-attach an interfae's kernel driver, which was previously detached using
/// detach_kernel_driver.
pub fn attach_kernel_driver(&self, interface_number: i32) -> Result<()> {
// Safe because 'self.handle' is a valid pointer to device handle.
try_libusb!(unsafe {
bindings::libusb_attach_kernel_driver(self.handle, interface_number)
});
Ok(())
}
/// Active an alternate setting for an interface.
pub fn set_interface_alt_setting(
&self,
interface_number: i32,
alternative_setting: i32,
) -> Result<()> {
// Safe because 'self.handle' is a valid pointer to device handle.
try_libusb!(unsafe {
bindings::libusb_set_interface_alt_setting(
self.handle,
interface_number,
alternative_setting,
)
});
Ok(())
}
/// Clear the halt/stall condition for an endpoint.
pub fn clear_halt(&self, endpoint: u8) -> Result<()> {
// Safe because 'self.handle' is a valid pointer to device handle.
try_libusb!(unsafe { bindings::libusb_clear_halt(self.handle, endpoint) });
Ok(())
}
/// Libusb asynchronous I/O interface has a 5 step process. It gives lots of
/// flexibility but makes it hard to manage object life cycle and easy to
/// write unsafe code. We wrap this interface to a simple "transfer" and "cancel"
/// interface. Resubmission is not supported and deallocation is handled safely
/// here.
pub fn submit_async_transfer<T: UsbTransferBuffer>(
&self,
transfer: UsbTransfer<T>,
) -> Result<TransferCanceller> {
unsafe { transfer.submit(self.handle) }
}
}

View file

@ -1,57 +0,0 @@
// Copyright 2018 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::ops::Deref;
use crate::bindings::libusb_endpoint_descriptor;
use crate::types::{EndpointDirection, EndpointType};
/// EndpointDescriptor wraps libusb_endpoint_descriptor.
pub struct EndpointDescriptor<'a>(&'a libusb_endpoint_descriptor);
const ENDPOINT_DESCRIPTOR_DIRECTION_MASK: u8 = 1 << 7;
const ENDPOINT_DESCRIPTOR_NUMBER_MASK: u8 = 0xf;
const ENDPOINT_DESCRIPTOR_ATTRIBUTES_TYPE_MASK: u8 = 0x3;
impl<'a> EndpointDescriptor<'a> {
// Create new endpoint descriptor.
pub fn new(descriptor: &libusb_endpoint_descriptor) -> EndpointDescriptor {
EndpointDescriptor(descriptor)
}
// Get direction of this endpoint.
pub fn get_direction(&self) -> EndpointDirection {
let direction = self.0.bEndpointAddress & ENDPOINT_DESCRIPTOR_DIRECTION_MASK;
if direction > 0 {
EndpointDirection::DeviceToHost
} else {
EndpointDirection::HostToDevice
}
}
// Get endpoint number.
pub fn get_endpoint_number(&self) -> u8 {
self.0.bEndpointAddress & ENDPOINT_DESCRIPTOR_NUMBER_MASK
}
// Get endpoint type.
pub fn get_endpoint_type(&self) -> Option<EndpointType> {
let ep_type = self.0.bmAttributes & ENDPOINT_DESCRIPTOR_ATTRIBUTES_TYPE_MASK;
match ep_type {
0 => Some(EndpointType::Control),
1 => Some(EndpointType::Isochronous),
2 => Some(EndpointType::Bulk),
3 => Some(EndpointType::Interrupt),
_ => None,
}
}
}
impl<'a> Deref for EndpointDescriptor<'a> {
type Target = libusb_endpoint_descriptor;
fn deref(&self) -> &libusb_endpoint_descriptor {
self.0
}
}

View file

@ -1,85 +1,52 @@
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// 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 libc::c_ulong;
use remain::sorted;
use std;
use std::fmt;
use std::fmt::{self, Display};
use std::io;
use std::num;
use crate::bindings;
/// Error type for libusb.
#[sorted]
#[derive(Debug)]
pub enum Error {
Success(i32),
IO,
InvalidParam,
Access,
DescriptorParse,
DescriptorRead(io::Error),
FdCloneFailed(io::Error),
InvalidActualLength(num::TryFromIntError),
InvalidBufferLength(num::TryFromIntError),
IoctlFailed(c_ulong, sys_util::Error),
NoDevice,
NotFound,
Busy,
Timeout,
Overflow,
Pipe,
Interrupted,
NoMem,
NotSupported,
Other,
NoSuchDescriptor,
RcGetMutFailed,
RcUnwrapFailed,
TransferAlreadyCompleted,
}
impl fmt::Debug for Error {
impl Display for Error {
#[remain::check]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::Success(_v) => write!(f, "Success (no error)"),
Error::IO => write!(f, "Input/output error"),
Error::InvalidParam => write!(f, "Invalid parameter"),
Error::Access => write!(f, "Access denied (insufficient permissions)"),
Error::NoDevice => write!(f, "No such device (it may have been disconnected)"),
Error::NotFound => write!(f, "Entity not found"),
Error::Busy => write!(f, "Resource busy"),
Error::Timeout => write!(f, "Operation timed out"),
Error::Overflow => write!(f, "Overflow"),
Error::Pipe => write!(f, "Pipe error"),
Error::Interrupted => write!(f, "System call interrupted (perhaps due to signal)"),
Error::NoMem => write!(f, "Insufficient memory"),
Error::NotSupported => write!(
f,
"Operation not supported or unimplemented on this platform"
),
Error::Other => write!(f, "Other error"),
}
}
}
use self::Error::*;
impl From<bindings::libusb_error> for Error {
fn from(e: bindings::libusb_error) -> Self {
match e {
bindings::LIBUSB_ERROR_IO => Error::IO,
bindings::LIBUSB_ERROR_INVALID_PARAM => Error::InvalidParam,
bindings::LIBUSB_ERROR_ACCESS => Error::Access,
bindings::LIBUSB_ERROR_NO_DEVICE => Error::NoDevice,
bindings::LIBUSB_ERROR_NOT_FOUND => Error::NotFound,
bindings::LIBUSB_ERROR_BUSY => Error::Busy,
bindings::LIBUSB_ERROR_TIMEOUT => Error::Timeout,
bindings::LIBUSB_ERROR_OVERFLOW => Error::Overflow,
bindings::LIBUSB_ERROR_PIPE => Error::Pipe,
bindings::LIBUSB_ERROR_INTERRUPTED => Error::Interrupted,
bindings::LIBUSB_ERROR_NO_MEM => Error::NoMem,
bindings::LIBUSB_ERROR_NOT_SUPPORTED => Error::NotSupported,
bindings::LIBUSB_ERROR_OTHER => Error::Other,
// All possible errors are defined above, other values mean success,
// see libusb_get_device_list for example.
_ => Error::Success(e),
#[sorted]
match self {
DescriptorParse => write!(f, "parsing descriptors failed"),
DescriptorRead(e) => write!(f, "reading descriptors from device failed: {}", e),
FdCloneFailed(e) => write!(f, "File::try_clone() failed: {}", e),
InvalidActualLength(e) => write!(f, "invalid actual_length in URB: {}", e),
InvalidBufferLength(e) => write!(f, "invalid transfer buffer length: {}", e),
IoctlFailed(nr, e) => write!(f, "USB ioctl 0x{:x} failed: {}", nr, e),
NoDevice => write!(f, "Device has been removed"),
NoSuchDescriptor => write!(f, "Requested descriptor not found"),
RcGetMutFailed => write!(f, "Rc::get_mut failed"),
RcUnwrapFailed => write!(f, "Rc::try_unwrap failed"),
TransferAlreadyCompleted => write!(f, "attempted to cancel already-completed transfer"),
}
}
}
pub type Result<T> = std::result::Result<T, Error>;
#[macro_export]
macro_rules! try_libusb {
($x:expr) => {
match Error::from($x as i32) {
Error::Success(e) => e,
err => return Err(err),
}
};
}
impl std::error::Error for Error {}

View file

@ -1,77 +0,0 @@
// 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::os::raw::{c_int, c_void};
use std::sync::Arc;
use crate::bindings;
use crate::libusb_context::LibUsbContextInner;
use crate::libusb_device::LibUsbDevice;
#[derive(PartialEq)]
pub enum HotplugEvent {
DeviceArrived,
DeviceLeft,
}
impl HotplugEvent {
/// Create a new HotplugEvent from raw libusb_hotplug_event.
pub fn new(event: bindings::libusb_hotplug_event) -> Self {
match event {
bindings::LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED => HotplugEvent::DeviceArrived,
bindings::LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT => HotplugEvent::DeviceLeft,
_ => {
// TODO(jkwang) handle this with option.
// libusb_hotplug_event is a C enum.
panic!("Invaild libusb_hotplug_event");
}
}
}
}
pub trait UsbHotplugHandler: Send + Sync + 'static {
fn hotplug_event(&self, device: LibUsbDevice, event: HotplugEvent);
}
/// UsbHotplugHandlerHolder owns UsbHotplugHandler and LibUsbContext. It will be passed as
/// user_data to libusb_hotplug_register_callback.
pub struct UsbHotplugHandlerHolder {
context: Arc<LibUsbContextInner>,
handler: Box<dyn UsbHotplugHandler>,
}
impl UsbHotplugHandlerHolder {
/// Create UsbHotplugHandlerHodler from context and handler.
pub fn new<H: UsbHotplugHandler>(
context: Arc<LibUsbContextInner>,
handler: H,
) -> Box<UsbHotplugHandlerHolder> {
let holder = UsbHotplugHandlerHolder {
context,
handler: Box::new(handler),
};
Box::new(holder)
}
}
/// This function is safe when:
/// libusb_device is allocated by libusb
/// user_data points to valid UsbHotPlugHandlerHolder released from Box.
///
/// Do not invoke this function. It should only be used as a callback for
/// libusb_hotplug_register_callback.
pub unsafe extern "C" fn hotplug_cb(
_: *mut bindings::libusb_context,
device: *mut bindings::libusb_device,
event: bindings::libusb_hotplug_event,
user_data: *mut c_void,
) -> c_int {
// Safe because user_data was casted from holder.
let holder = &*(user_data as *mut UsbHotplugHandlerHolder);
let device = LibUsbDevice::new(holder.context.clone(), device);
let event = HotplugEvent::new(event);
holder.handler.hotplug_event(device, event);
// The handler should always succeed.
bindings::LIBUSB_SUCCESS
}

View file

@ -1,40 +0,0 @@
// Copyright 2018 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::ops::Deref;
use crate::bindings::libusb_interface_descriptor;
use crate::endpoint_descriptor::EndpointDescriptor;
/// InterfaceDescriptor wraps libusb_interface_descriptor.
pub struct InterfaceDescriptor<'a>(&'a libusb_interface_descriptor);
impl<'a> InterfaceDescriptor<'a> {
/// Create a new Interface descriptor.
pub fn new(descriptor: &'a libusb_interface_descriptor) -> InterfaceDescriptor<'a> {
InterfaceDescriptor(descriptor)
}
/// Get endpoint descriptor at index.
pub fn endpoint_descriptor(&self, ep_idx: u8) -> Option<EndpointDescriptor> {
if ep_idx >= self.0.bNumEndpoints {
return None;
}
// Safe because idx is checked.
unsafe {
Some(EndpointDescriptor::new(
&*(self.0.endpoint.offset(ep_idx as isize)),
))
}
}
}
impl<'a> Deref for InterfaceDescriptor<'a> {
type Target = libusb_interface_descriptor;
fn deref(&self) -> &libusb_interface_descriptor {
self.0
}
}

View file

@ -1,23 +1,18 @@
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// 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.
// Generated with bindgen libusb.h -no-prepend-enum-name -o bindings.rs.
#![allow(dead_code)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]
#[allow(clippy::all)]
mod bindings;
mod descriptor;
mod device;
mod error;
mod types;
#[macro_use]
pub mod error;
pub mod config_descriptor;
pub mod device_handle;
pub mod endpoint_descriptor;
pub mod hotplug;
pub mod interface_descriptor;
pub mod libusb_context;
pub mod libusb_device;
pub mod types;
pub mod usb_transfer;
pub use self::descriptor::{ConfigDescriptorTree, DeviceDescriptorTree, InterfaceDescriptorTree};
pub use self::device::{Device, Transfer, TransferStatus};
pub use self::error::{Error, Result};
pub use self::types::{
control_request_type, ConfigDescriptor, ControlRequestDataPhaseTransferDirection,
ControlRequestRecipient, ControlRequestType, DeviceDescriptor, EndpointDescriptor,
EndpointDirection, EndpointType, InterfaceDescriptor, StandardControlRequest, UsbRequestSetup,
ENDPOINT_DIRECTION_OFFSET,
};

View file

@ -1,268 +0,0 @@
// Copyright 2018 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;
#[allow(unused_imports)]
use std::os::raw::{c_long, c_short, c_void};
use std::os::unix::io::RawFd;
use std::sync::Arc;
use crate::bindings;
#[cfg(feature = "sandboxed-libusb")]
use crate::device_handle::DeviceHandle;
use crate::error::{Error, Result};
use crate::hotplug::{hotplug_cb, UsbHotplugHandler, UsbHotplugHandlerHolder};
use crate::libusb_device::LibUsbDevice;
use sync::Mutex;
pub struct LibUsbContextInner {
context: *mut bindings::libusb_context,
pollfd_change_handler: Mutex<Option<Box<PollfdChangeHandlerHolder>>>,
}
// Safe because libusb_context could be accessed from multiple threads safely.
unsafe impl Send for LibUsbContextInner {}
unsafe impl Sync for LibUsbContextInner {}
impl LibUsbContextInner {
/// Remove the previous registered notifiers.
pub fn remove_pollfd_notifiers(&self) {
// Safe because 'self.context' is valid.
unsafe {
bindings::libusb_set_pollfd_notifiers(self.context, None, None, std::ptr::null_mut());
}
}
}
impl Drop for LibUsbContextInner {
fn drop(&mut self) {
// Avoid pollfd change handler call when libusb_exit is called.
self.remove_pollfd_notifiers();
// Safe beacuse 'self.context' points to a valid context allocated by libusb_init.
unsafe {
bindings::libusb_exit(self.context);
}
}
}
/// Wrapper for libusb_context. The libusb libary initialization/deinitialization
/// is managed by this context.
/// See: http://libusb.sourceforge.net/api-1.0/group__libusb__lib.html
#[derive(Clone)]
pub struct LibUsbContext {
inner: Arc<LibUsbContextInner>,
}
impl LibUsbContext {
/// Create a new LibUsbContext.
pub fn new() -> Result<LibUsbContext> {
let mut ctx: *mut bindings::libusb_context = std::ptr::null_mut();
// Safe because '&mut ctx' points to a valid memory (on stack).
try_libusb!(unsafe { bindings::libusb_init(&mut ctx) });
Ok(LibUsbContext {
inner: Arc::new(LibUsbContextInner {
context: ctx,
pollfd_change_handler: Mutex::new(None),
}),
})
}
#[cfg(feature = "sandboxed-libusb")]
pub fn handle_from_file(&self, f: std::fs::File) -> Result<DeviceHandle> {
use std::os::unix::io::IntoRawFd;
let fd = f.into_raw_fd();
let mut handle: *mut bindings::libusb_device_handle = std::ptr::null_mut();
// Safe because fd is valid and owned, and '&mut handle' points to valid memory.
try_libusb!(unsafe {
bindings::libusb_wrap_sys_device(self.inner.context, fd as c_long, &mut handle)
});
unsafe { Ok(DeviceHandle::new(self.inner.clone(), handle)) }
}
/// Returns a list of USB devices currently attached to the system.
pub fn get_device_iter(&self) -> Result<DeviceIter> {
let mut list: *mut *mut bindings::libusb_device = std::ptr::null_mut();
// Safe because 'inner.context' points to a valid context and '&mut list' points to a valid
// memory.
try_libusb!(unsafe { bindings::libusb_get_device_list(self.inner.context, &mut list) });
Ok(DeviceIter {
context: self.inner.clone(),
list,
index: 0,
})
}
/// Check at runtime if the loaded library has a given capability.
pub fn has_capability(&self, cap: u32) -> bool {
// Safe because libusb_init is called before this call happens.
unsafe { bindings::libusb_has_capability(cap) != 0 }
}
/// Return an iter of poll fds. Those fds that should be polled to handle libusb events.
pub fn get_pollfd_iter(&self) -> PollFdIter {
// Safe because 'inner.context' is inited.
let list: *mut *const bindings::libusb_pollfd =
unsafe { bindings::libusb_get_pollfds(self.inner.context) };
PollFdIter { list, index: 0 }
}
/// Handle libusb events in a non block way.
pub fn handle_events_nonblock(&self) {
static mut zero_time: bindings::timeval = bindings::timeval {
tv_sec: 0,
tv_usec: 0,
};
// Safe because 'inner.context' points to valid context.
unsafe {
bindings::libusb_handle_events_timeout_completed(
self.inner.context,
&mut zero_time as *mut bindings::timeval,
std::ptr::null_mut(),
);
}
}
/// Set a handler that could handle pollfd change events.
pub fn set_pollfd_notifiers(&self, handler: Box<dyn LibUsbPollfdChangeHandler>) {
// LibUsbContext is alive when any libusb related function is called. It owns the handler,
// thus the handler memory is always valid when callback is invoked.
let holder = Box::new(PollfdChangeHandlerHolder { handler });
let raw_holder = Box::into_raw(holder);
unsafe {
bindings::libusb_set_pollfd_notifiers(
self.inner.context,
Some(pollfd_added_cb),
Some(pollfd_removed_cb),
raw_holder as *mut c_void,
);
}
// Safe because raw_holder is from Boxed pointer.
let holder = unsafe { Box::from_raw(raw_holder) };
*self.inner.pollfd_change_handler.lock() = Some(holder);
}
/// Remove the previous registered notifiers.
pub fn remove_pollfd_notifiers(&self) {
self.inner.remove_pollfd_notifiers();
}
/// Set a callback that could handle hotplug events. Currently, this function listen to hotplug
/// event of all devices.
pub fn set_hotplug_cb<H: UsbHotplugHandler + Sized>(&self, handler: H) -> Result<()> {
let event = bindings::LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED
| bindings::LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT;
let holder = UsbHotplugHandlerHolder::new(self.inner.clone(), handler);
let raw_holder = Box::into_raw(holder);
// Safe becuase hotpulg cb is a vaild c function and raw_holder points to memory for that
// function argument.
try_libusb!(unsafe {
bindings::libusb_hotplug_register_callback(
self.inner.context,
event,
bindings::LIBUSB_HOTPLUG_NO_FLAGS,
bindings::LIBUSB_HOTPLUG_MATCH_ANY,
bindings::LIBUSB_HOTPLUG_MATCH_ANY,
bindings::LIBUSB_HOTPLUG_MATCH_ANY,
Some(hotplug_cb),
raw_holder as *mut c_void,
std::ptr::null_mut(),
)
});
Ok(())
}
}
/// Iterator for device list.
pub struct DeviceIter {
context: Arc<LibUsbContextInner>,
list: *mut *mut bindings::libusb_device,
index: isize,
}
impl Drop for DeviceIter {
fn drop(&mut self) {
// Safe because 'self.list' is inited by a valid pointer from libusb_get_device_list.
unsafe {
bindings::libusb_free_device_list(self.list, 1);
}
}
}
impl Iterator for DeviceIter {
type Item = LibUsbDevice;
fn next(&mut self) -> Option<LibUsbDevice> {
// Safe becuase 'self.list' is valid, the list is null terminated.
unsafe {
let current_ptr = self.list.offset(self.index);
if (*current_ptr).is_null() {
return None;
}
self.index += 1;
Some(LibUsbDevice::new(self.context.clone(), *current_ptr))
}
}
}
/// Iterator for pollfds.
pub struct PollFdIter {
list: *mut *const bindings::libusb_pollfd,
index: isize,
}
impl Drop for PollFdIter {
fn drop(&mut self) {
// Safe because 'self.list' points to valid memory of pollfd list.
unsafe {
bindings::libusb_free_pollfds(self.list);
}
}
}
impl Iterator for PollFdIter {
type Item = bindings::libusb_pollfd;
fn next(&mut self) -> Option<bindings::libusb_pollfd> {
// Safe because 'self.index' never grow out of the null pointer index.
unsafe {
let current_ptr = self.list.offset(self.index);
if (*current_ptr).is_null() {
return None;
}
self.index += 1;
// Safe because '*current_ptr' is not null.
Some(**current_ptr)
}
}
}
/// Trait for handler that handles Pollfd Change events.
pub trait LibUsbPollfdChangeHandler: Send + Sync + 'static {
fn add_poll_fd(&self, fd: RawFd, events: c_short);
fn remove_poll_fd(&self, fd: RawFd);
}
// This struct owns LibUsbPollfdChangeHandler. We need it because it's not possible to cast void
// pointer to trait pointer.
struct PollfdChangeHandlerHolder {
handler: Box<dyn LibUsbPollfdChangeHandler>,
}
// This function is safe when user_data points to valid PollfdChangeHandlerHolder.
unsafe extern "C" fn pollfd_added_cb(fd: RawFd, events: c_short, user_data: *mut c_void) {
// Safe because user_data was casted from holder.
let keeper = &*(user_data as *mut PollfdChangeHandlerHolder);
keeper.handler.add_poll_fd(fd, events);
}
// This function is safe when user_data points to valid PollfdChangeHandlerHolder.
unsafe extern "C" fn pollfd_removed_cb(fd: RawFd, user_data: *mut c_void) {
// Safe because user_data was casted from holder.
let keeper = &*(user_data as *mut PollfdChangeHandlerHolder);
keeper.handler.remove_poll_fd(fd);
}

View file

@ -1,108 +0,0 @@
// Copyright 2018 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;
use std::sync::Arc;
use crate::bindings;
use crate::config_descriptor::ConfigDescriptor;
use crate::device_handle::DeviceHandle;
use crate::error::{Error, Result};
use crate::libusb_context::LibUsbContextInner;
use crate::types::Speed;
pub type DeviceDescriptor = bindings::libusb_device_descriptor;
/// LibUsbDevice wraps libusb_device.
pub struct LibUsbDevice {
_context: Arc<LibUsbContextInner>,
device: *mut bindings::libusb_device,
}
unsafe impl Send for LibUsbDevice {}
impl Drop for LibUsbDevice {
fn drop(&mut self) {
// Safe because 'self.device' is a valid pointer and libusb_ref_device is invoked when
// 'self' is built.
unsafe {
bindings::libusb_unref_device(self.device);
}
}
}
impl LibUsbDevice {
/// Create a new LibUsbDevice. 'device' should be a valid pointer to libusb_device.
pub unsafe fn new(
ctx: Arc<LibUsbContextInner>,
device: *mut bindings::libusb_device,
) -> LibUsbDevice {
bindings::libusb_ref_device(device);
LibUsbDevice {
_context: ctx,
device,
}
}
/// Get device descriptor of this device.
pub fn get_device_descriptor(&self) -> Result<DeviceDescriptor> {
// Safe because memory is initialized later.
let mut descriptor: bindings::libusb_device_descriptor = Default::default();
// Safe because 'self.device' is valid and '&mut descriptor' is valid.
try_libusb!(unsafe {
bindings::libusb_get_device_descriptor(self.device, &mut descriptor)
});
Ok(descriptor)
}
/// Get config descriptor at index of idx.
pub fn get_config_descriptor(&self, idx: u8) -> Result<ConfigDescriptor> {
let mut descriptor: *mut bindings::libusb_config_descriptor = std::ptr::null_mut();
// Safe because 'self.device' is valid and '&mut descriptor' is valid.
try_libusb!(unsafe {
bindings::libusb_get_config_descriptor(self.device, idx, &mut descriptor)
});
// Safe because descriptor is inited with valid pointer.
Ok(unsafe { ConfigDescriptor::new(descriptor) })
}
/// Get active config descriptor of this device.
pub fn get_active_config_descriptor(&self) -> Result<ConfigDescriptor> {
let mut descriptor: *mut bindings::libusb_config_descriptor = std::ptr::null_mut();
// Safe because 'self.device' is valid and '&mut descriptor' is valid.
try_libusb!(unsafe {
bindings::libusb_get_active_config_descriptor(self.device, &mut descriptor)
});
// Safe becuase descriptor points to valid memory.
Ok(unsafe { ConfigDescriptor::new(descriptor) })
}
/// Get bus number of this device.
pub fn get_bus_number(&self) -> u8 {
// Safe because 'self.device' is valid.
unsafe { bindings::libusb_get_bus_number(self.device) }
}
/// Get address of this device.
pub fn get_address(&self) -> u8 {
// Safe because 'self.device' is valid.
unsafe { bindings::libusb_get_device_address(self.device) }
}
/// Get speed of this device.
pub fn get_speed(&self) -> Speed {
// Safe because 'self.device' is valid.
let speed = unsafe { bindings::libusb_get_device_speed(self.device) };
Speed::from(speed as u32)
}
/// Get device handle of this device.
pub fn open(&self) -> Result<DeviceHandle> {
let mut handle: *mut bindings::libusb_device_handle = std::ptr::null_mut();
// Safe because 'self.device' is constructed from libusb device list and handle is on stack.
try_libusb!(unsafe { bindings::libusb_open(self.device, &mut handle) });
// Safe because handle points to valid memory.
Ok(unsafe { DeviceHandle::new(self._context.clone(), handle) })
}
}

View file

@ -7,35 +7,159 @@ use data_model::DataInit;
use std::mem::size_of;
use crate::bindings;
/// Speed of usb device. See usb spec for more details.
#[derive(Debug)]
pub enum Speed {
/// The OS doesn't report or know the device speed.
Unknown,
/// The device is operating at low speed (1.5MBit/s).
Low,
/// The device is operating at full speed (12MBit/s).
Full,
/// The device is operating at high speed (480MBit/s).
High,
/// The device is operating at super speed (5000MBit/s).
Super,
/// Standard USB descriptor types.
pub enum DescriptorType {
Device = 0x01,
Configuration = 0x02,
Interface = 0x04,
Endpoint = 0x05,
}
impl From<bindings::libusb_speed> for Speed {
fn from(speed: bindings::libusb_speed) -> Speed {
match speed {
bindings::LIBUSB_SPEED_LOW => Speed::Low,
bindings::LIBUSB_SPEED_FULL => Speed::Full,
bindings::LIBUSB_SPEED_HIGH => Speed::High,
bindings::LIBUSB_SPEED_SUPER => Speed::Super,
_ => Speed::Unknown,
}
/// Trait describing USB descriptors.
pub trait Descriptor {
/// Get the expected bDescriptorType value for this type of descriptor.
fn descriptor_type() -> DescriptorType;
}
/// Standard USB descriptor header common to all descriptor types.
#[allow(non_snake_case)]
#[derive(Copy, Clone, Debug, Default)]
#[repr(C, packed)]
pub struct DescriptorHeader {
pub bLength: u8,
pub bDescriptorType: u8,
}
// Safe because it only has data and has no implicit padding.
unsafe impl DataInit for DescriptorHeader {}
fn _assert_descriptor_header() {
const_assert!(size_of::<DescriptorHeader>() == 2);
}
/// Standard USB device descriptor as defined in USB 2.0 chapter 9,
/// not including the standard header.
#[allow(non_snake_case)]
#[derive(Copy, Clone, Debug, Default)]
#[repr(C, packed)]
pub struct DeviceDescriptor {
pub bcdUSB: u16,
pub bDeviceClass: u8,
pub bDeviceSubClass: u8,
pub bDeviceProtocol: u8,
pub bMaxPacketSize0: u8,
pub idVendor: u16,
pub idProduct: u16,
pub bcdDevice: u16,
pub iManufacturer: u8,
pub iProduct: u8,
pub iSerialNumber: u8,
pub bNumConfigurations: u8,
}
impl Descriptor for DeviceDescriptor {
fn descriptor_type() -> DescriptorType {
DescriptorType::Device
}
}
// Safe because it only has data and has no implicit padding.
unsafe impl DataInit for DeviceDescriptor {}
fn _assert_device_descriptor() {
const_assert!(size_of::<DeviceDescriptor>() == 18 - 2);
}
/// Standard USB configuration descriptor as defined in USB 2.0 chapter 9,
/// not including the standard header.
#[allow(non_snake_case)]
#[derive(Copy, Clone, Debug, Default)]
#[repr(C, packed)]
pub struct ConfigDescriptor {
pub wTotalLength: u16,
pub bNumInterfaces: u8,
pub bConfigurationValue: u8,
pub iConfiguration: u8,
pub bmAttributes: u8,
pub bMaxPower: u8,
}
impl Descriptor for ConfigDescriptor {
fn descriptor_type() -> DescriptorType {
DescriptorType::Configuration
}
}
// Safe because it only has data and has no implicit padding.
unsafe impl DataInit for ConfigDescriptor {}
fn _assert_config_descriptor() {
const_assert!(size_of::<ConfigDescriptor>() == 9 - 2);
}
impl ConfigDescriptor {
pub fn num_interfaces(&self) -> u8 {
self.bNumInterfaces
}
}
/// Standard USB interface descriptor as defined in USB 2.0 chapter 9,
/// not including the standard header.
#[allow(non_snake_case)]
#[derive(Copy, Clone, Debug, Default)]
#[repr(C, packed)]
pub struct InterfaceDescriptor {
pub bInterfaceNumber: u8,
pub bAlternateSetting: u8,
pub bNumEndpoints: u8,
pub bInterfaceClass: u8,
pub bInterfaceSubClass: u8,
pub bInterfaceProtocol: u8,
pub iInterface: u8,
}
impl Descriptor for InterfaceDescriptor {
fn descriptor_type() -> DescriptorType {
DescriptorType::Interface
}
}
// Safe because it only has data and has no implicit padding.
unsafe impl DataInit for InterfaceDescriptor {}
fn _assert_interface_descriptor() {
const_assert!(size_of::<InterfaceDescriptor>() == 9 - 2);
}
/// Standard USB endpoint descriptor as defined in USB 2.0 chapter 9,
/// not including the standard header.
#[allow(non_snake_case)]
#[derive(Copy, Clone, Debug, Default)]
#[repr(C, packed)]
pub struct EndpointDescriptor {
pub bEndpointAddress: u8,
pub bmAttributes: u8,
pub wMaxPacketSize: u16,
pub bInterval: u8,
}
impl Descriptor for EndpointDescriptor {
fn descriptor_type() -> DescriptorType {
DescriptorType::Endpoint
}
}
// Safe because it only has data and has no implicit padding.
unsafe impl DataInit for EndpointDescriptor {}
fn _assert_endpoint_descriptor() {
const_assert!(size_of::<EndpointDescriptor>() == 7 - 2);
}
const ENDPOINT_DESCRIPTOR_DIRECTION_MASK: u8 = 1 << 7;
const ENDPOINT_DESCRIPTOR_NUMBER_MASK: u8 = 0xf;
const ENDPOINT_DESCRIPTOR_ATTRIBUTES_TYPE_MASK: u8 = 0x3;
/// Endpoint types.
#[derive(PartialEq)]
pub enum EndpointType {
@ -54,6 +178,35 @@ pub enum EndpointDirection {
/// Endpoint direction offset.
pub const ENDPOINT_DIRECTION_OFFSET: u8 = 7;
impl EndpointDescriptor {
// Get direction of this endpoint.
pub fn get_direction(&self) -> EndpointDirection {
let direction = self.bEndpointAddress & ENDPOINT_DESCRIPTOR_DIRECTION_MASK;
if direction != 0 {
EndpointDirection::DeviceToHost
} else {
EndpointDirection::HostToDevice
}
}
// Get endpoint number.
pub fn get_endpoint_number(&self) -> u8 {
self.bEndpointAddress & ENDPOINT_DESCRIPTOR_NUMBER_MASK
}
// Get endpoint type.
pub fn get_endpoint_type(&self) -> Option<EndpointType> {
let ep_type = self.bmAttributes & ENDPOINT_DESCRIPTOR_ATTRIBUTES_TYPE_MASK;
match ep_type {
0 => Some(EndpointType::Control),
1 => Some(EndpointType::Isochronous),
2 => Some(EndpointType::Bulk),
3 => Some(EndpointType::Interrupt),
_ => None,
}
}
}
/// Offset of data phase transfer direction.
pub const DATA_PHASE_DIRECTION_OFFSET: u8 = 7;
/// Bit mask of data phase transfer direction.
@ -118,7 +271,7 @@ pub struct UsbRequestSetup {
pub length: u16, // wLength
}
fn _assert() {
fn _assert_usb_request_setup() {
const_assert!(size_of::<UsbRequestSetup>() == 8);
}
@ -194,3 +347,55 @@ impl UsbRequestSetup {
}
}
}
/// Construct a bmRequestType value for a control request.
pub fn control_request_type(
type_: ControlRequestType,
dir: ControlRequestDataPhaseTransferDirection,
recipient: ControlRequestRecipient,
) -> u8 {
((type_ as u8) << CONTROL_REQUEST_TYPE_OFFSET)
| ((dir as u8) << DATA_PHASE_DIRECTION_OFFSET)
| (recipient as u8)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn control_request_types() {
assert_eq!(
control_request_type(
ControlRequestType::Standard,
ControlRequestDataPhaseTransferDirection::HostToDevice,
ControlRequestRecipient::Device
),
0b0_00_00000
);
assert_eq!(
control_request_type(
ControlRequestType::Standard,
ControlRequestDataPhaseTransferDirection::DeviceToHost,
ControlRequestRecipient::Device
),
0b1_00_00000
);
assert_eq!(
control_request_type(
ControlRequestType::Standard,
ControlRequestDataPhaseTransferDirection::HostToDevice,
ControlRequestRecipient::Interface
),
0b0_00_00001
);
assert_eq!(
control_request_type(
ControlRequestType::Class,
ControlRequestDataPhaseTransferDirection::HostToDevice,
ControlRequestRecipient::Device
),
0b0_01_00000
);
}
}

View file

@ -1,382 +0,0 @@
// Copyright 2018 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::size_of;
use std::os::raw::c_void;
use std::sync::{Arc, Weak};
use crate::bindings::{
libusb_alloc_transfer, libusb_cancel_transfer, libusb_device_handle, libusb_free_transfer,
libusb_submit_transfer, libusb_transfer, libusb_transfer_status, LIBUSB_TRANSFER_CANCELLED,
LIBUSB_TRANSFER_COMPLETED, LIBUSB_TRANSFER_ERROR, LIBUSB_TRANSFER_NO_DEVICE,
LIBUSB_TRANSFER_OVERFLOW, LIBUSB_TRANSFER_STALL, LIBUSB_TRANSFER_TIMED_OUT,
LIBUSB_TRANSFER_TYPE_BULK, LIBUSB_TRANSFER_TYPE_CONTROL, LIBUSB_TRANSFER_TYPE_INTERRUPT,
};
use crate::error::{Error, Result};
use crate::types::UsbRequestSetup;
/// Status of transfer.
#[derive(PartialEq)]
pub enum TransferStatus {
Completed,
Error,
TimedOut,
Cancelled,
Stall,
NoDevice,
Overflow,
}
impl From<libusb_transfer_status> for TransferStatus {
fn from(s: libusb_transfer_status) -> Self {
match s {
LIBUSB_TRANSFER_COMPLETED => TransferStatus::Completed,
LIBUSB_TRANSFER_ERROR => TransferStatus::Error,
LIBUSB_TRANSFER_TIMED_OUT => TransferStatus::TimedOut,
LIBUSB_TRANSFER_CANCELLED => TransferStatus::Cancelled,
LIBUSB_TRANSFER_STALL => TransferStatus::Stall,
LIBUSB_TRANSFER_NO_DEVICE => TransferStatus::NoDevice,
LIBUSB_TRANSFER_OVERFLOW => TransferStatus::Overflow,
_ => TransferStatus::Error,
}
}
}
/// Trait for usb transfer buffer.
pub trait UsbTransferBuffer: Send {
fn as_ptr(&mut self) -> *mut u8;
fn len(&self) -> i32;
}
/// Default buffer size for control data transfer.
const CONTROL_DATA_BUFFER_SIZE: usize = 1024;
/// Buffer type for control transfer. The first 8-bytes is a UsbRequestSetup struct.
#[repr(C, packed)]
pub struct ControlTransferBuffer {
pub setup_buffer: UsbRequestSetup,
pub data_buffer: [u8; CONTROL_DATA_BUFFER_SIZE],
}
impl ControlTransferBuffer {
fn new() -> ControlTransferBuffer {
ControlTransferBuffer {
setup_buffer: UsbRequestSetup {
request_type: 0,
request: 0,
value: 0,
index: 0,
length: 0,
},
data_buffer: [0; CONTROL_DATA_BUFFER_SIZE],
}
}
/// Set request setup for this control buffer.
pub fn set_request_setup(&mut self, request_setup: &UsbRequestSetup) {
self.setup_buffer = *request_setup;
}
}
impl UsbTransferBuffer for ControlTransferBuffer {
fn as_ptr(&mut self) -> *mut u8 {
self as *mut ControlTransferBuffer as *mut u8
}
fn len(&self) -> i32 {
if self.setup_buffer.length as usize > CONTROL_DATA_BUFFER_SIZE {
panic!("Setup packet has an oversize length");
}
self.setup_buffer.length as i32 + size_of::<UsbRequestSetup>() as i32
}
}
/// Buffer type for Bulk transfer.
pub struct BulkTransferBuffer {
buffer: Vec<u8>,
}
impl BulkTransferBuffer {
fn with_size(buffer_size: usize) -> Self {
BulkTransferBuffer {
buffer: vec![0; buffer_size],
}
}
/// Get mutable interal slice of this buffer.
pub fn as_mut_slice(&mut self) -> &mut [u8] {
&mut self.buffer
}
/// Get interal slice of this buffer.
pub fn as_slice(&self) -> &[u8] {
&self.buffer
}
}
impl UsbTransferBuffer for BulkTransferBuffer {
fn as_ptr(&mut self) -> *mut u8 {
if self.buffer.len() == 0 {
// Vec::as_mut_ptr() won't give 0x0 even if len() is 0.
std::ptr::null_mut()
} else {
self.buffer.as_mut_ptr()
}
}
fn len(&self) -> i32 {
self.buffer.len() as i32
}
}
type UsbTransferCompletionCallback<T> = dyn Fn(UsbTransfer<T>) + Send + 'static;
// This wraps libusb_transfer pointer.
struct LibUsbTransfer {
ptr: *mut libusb_transfer,
}
impl Drop for LibUsbTransfer {
fn drop(&mut self) {
// Safe because 'self.ptr' is allocated by libusb_alloc_transfer.
unsafe {
libusb_free_transfer(self.ptr);
}
}
}
// It is safe to invoke libusb functions from multiple threads.
// We cannot modify libusb_transfer safely from multiple threads. All the modifications happens
// in construct (UsbTransfer::new) or consume (UsbTransfer::into_raw), we can consider this thread
// safe.
unsafe impl Send for LibUsbTransfer {}
unsafe impl Sync for LibUsbTransfer {}
/// TransferCanceller can cancel the transfer.
pub struct TransferCanceller {
transfer: Weak<LibUsbTransfer>,
}
impl TransferCanceller {
/// Return false if fail to cancel.
pub fn try_cancel(&self) -> bool {
match self.transfer.upgrade() {
Some(t) => {
// Safe because self.transfer has ownership of the raw pointer.
let r = unsafe { libusb_cancel_transfer(t.ptr) };
if r == 0 {
true
} else {
false
}
}
None => false,
}
}
}
struct UsbTransferInner<T: UsbTransferBuffer> {
transfer: Arc<LibUsbTransfer>,
callback: Option<Box<UsbTransferCompletionCallback<T>>>,
buffer: T,
}
/// UsbTransfer owns a LibUsbTransfer, it's buffer and callback.
pub struct UsbTransfer<T: UsbTransferBuffer> {
inner: Box<UsbTransferInner<T>>,
}
/// Build a control transfer.
pub fn control_transfer(timeout: u32) -> UsbTransfer<ControlTransferBuffer> {
UsbTransfer::<ControlTransferBuffer>::new(
0,
LIBUSB_TRANSFER_TYPE_CONTROL as u8,
timeout,
ControlTransferBuffer::new(),
)
}
/// Build a data transfer.
pub fn bulk_transfer(endpoint: u8, timeout: u32, size: usize) -> UsbTransfer<BulkTransferBuffer> {
UsbTransfer::<BulkTransferBuffer>::new(
endpoint,
LIBUSB_TRANSFER_TYPE_BULK as u8,
timeout,
BulkTransferBuffer::with_size(size),
)
}
/// Build a data transfer.
pub fn interrupt_transfer(
endpoint: u8,
timeout: u32,
size: usize,
) -> UsbTransfer<BulkTransferBuffer> {
UsbTransfer::<BulkTransferBuffer>::new(
endpoint,
LIBUSB_TRANSFER_TYPE_INTERRUPT as u8,
timeout,
BulkTransferBuffer::with_size(size),
)
}
impl<T: UsbTransferBuffer> UsbTransfer<T> {
fn new(endpoint: u8, type_: u8, timeout: u32, buffer: T) -> Self {
// Safe because alloc is safe.
let transfer: *mut libusb_transfer = unsafe { libusb_alloc_transfer(0) };
// Just panic on OOM.
assert!(!transfer.is_null());
let inner = Box::new(UsbTransferInner {
transfer: Arc::new(LibUsbTransfer { ptr: transfer }),
callback: None,
buffer,
});
// Safe because we inited transfer.
let raw_transfer: &mut libusb_transfer = unsafe { &mut *(inner.transfer.ptr) };
raw_transfer.endpoint = endpoint;
raw_transfer.type_ = type_;
raw_transfer.timeout = timeout;
raw_transfer.callback = Some(UsbTransfer::<T>::on_transfer_completed);
UsbTransfer { inner }
}
/// Set callback function for transfer completion.
pub fn set_callback<C: 'static + Fn(UsbTransfer<T>) + Send>(&mut self, cb: C) {
self.inner.callback = Some(Box::new(cb));
}
/// Get a reference to the buffer.
pub fn buffer(&self) -> &T {
&self.inner.buffer
}
/// Get a mutable reference to the buffer.
pub fn buffer_mut(&mut self) -> &mut T {
&mut self.inner.buffer
}
/// Get actual length of data that was transferred.
pub fn actual_length(&self) -> i32 {
let transfer = self.inner.transfer.ptr;
// Safe because inner.ptr is always allocated by libusb_alloc_transfer.
unsafe { (*transfer).actual_length }
}
/// Get the transfer status of this transfer.
pub fn status(&self) -> TransferStatus {
let transfer = self.inner.transfer.ptr;
// Safe because inner.ptr is always allocated by libusb_alloc_transfer.
unsafe { TransferStatus::from((*transfer).status) }
}
/// Submit this transfer to device handle. 'self' is consumed. On success, the memory will be
/// 'leaked' (and stored in user_data) and sent to libusb, when the async operation is done,
/// on_transfer_completed will recreate 'self' and deliver it to callback/free 'self'. On
/// faliure, 'self' is returned with an error.
///
/// # Safety
///
/// Assumes libusb_device_handle is an handled opened by libusb, self.inner.transfer.ptr is
/// initialized with correct buffer and length.
pub unsafe fn submit(self, handle: *mut libusb_device_handle) -> Result<TransferCanceller> {
let weak_transfer = Arc::downgrade(&self.inner.transfer);
let transfer = self.into_raw();
(*transfer).dev_handle = handle;
match Error::from(libusb_submit_transfer(transfer)) {
Error::Success(_e) => Ok(TransferCanceller {
transfer: weak_transfer,
}),
err => {
UsbTransfer::<T>::from_raw(transfer);
Err(err)
}
}
}
/// Invoke callback when transfer is completed.
///
/// # Safety
///
/// Assumes libusb_tranfser is finished. This function is called by libusb, don't call it
/// manually.
unsafe extern "C" fn on_transfer_completed(transfer: *mut libusb_transfer) {
let mut transfer = UsbTransfer::<T>::from_raw(transfer);
// Callback is reset to None.
if let Some(cb) = transfer.inner.callback.take() {
cb(transfer);
}
}
fn into_raw(mut self) -> *mut libusb_transfer {
let transfer: *mut libusb_transfer = self.inner.transfer.ptr;
// Safe because transfer is allocated by libusb_alloc_transfer.
unsafe {
(*transfer).buffer = self.buffer_mut().as_ptr();
(*transfer).length = self.buffer_mut().len();
(*transfer).user_data = Box::into_raw(self.inner) as *mut c_void;
}
transfer
}
unsafe fn from_raw(transfer: *mut libusb_transfer) -> Self {
UsbTransfer {
inner: Box::<UsbTransferInner<T>>::from_raw(
(*transfer).user_data as *mut UsbTransferInner<T>,
),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Mutex;
pub fn fake_submit_transfer<T: UsbTransferBuffer>(transfer: UsbTransfer<T>) {
let transfer = transfer.into_raw();
unsafe {
match (*transfer).callback {
Some(cb) => cb(transfer),
// Although no callback is invoked, we still need on_transfer_completed to
// free memory.
None => panic!("Memory leak!"),
};
}
}
#[test]
fn check_control_buffer_size() {
assert_eq!(
size_of::<ControlTransferBuffer>(),
size_of::<UsbRequestSetup>() + CONTROL_DATA_BUFFER_SIZE
);
}
#[test]
fn submit_transfer_no_callback_test() {
let t = control_transfer(0);
fake_submit_transfer(t);
let t = bulk_transfer(0, 0, 1);
fake_submit_transfer(t);
}
struct FakeTransferController {
data: Mutex<u8>,
}
#[test]
fn submit_transfer_with_callback() {
let c = Arc::new(FakeTransferController {
data: Mutex::new(0),
});
let c1 = Arc::downgrade(&c);
let mut t = control_transfer(0);
t.set_callback(move |_t| {
let c = c1.upgrade().unwrap();
*c.data.lock().unwrap() = 3;
});
fake_submit_transfer(t);
assert_eq!(*c.data.lock().unwrap(), 3);
}
}

View file

@ -4,9 +4,6 @@ version = "0.1.0"
authors = ["The Chromium OS Authors"]
edition = "2018"
[features]
sandboxed-libusb = []
[dependencies]
data_model = { path = "../data_model" }
kvm = { path = "../kvm" }