usb_utils: Add hotplug interface

Hotplug callback would be invoked when device attach or detach

TEST=run crosvm locally
BUG=chromium:831850
CQ-DEPEND=CL:1506826

Change-Id: Ifc1acc00a12c32dd00abbb5467874632e94f60b4
Reviewed-on: https://chromium-review.googlesource.com/1506827
Commit-Ready: Zach Reizner <zachr@chromium.org>
Tested-by: Jingkui Wang <jkwang@google.com>
Reviewed-by: Zach Reizner <zachr@chromium.org>
This commit is contained in:
Jingkui Wang 2019-03-06 21:25:03 -08:00 committed by chrome-bot
parent b07540c7f9
commit 699170b6b7
3 changed files with 104 additions and 0 deletions

77
usb_util/src/hotplug.rs Normal file
View file

@ -0,0 +1,77 @@
// 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 bindings;
use libusb_context::LibUsbContextInner;
use 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<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

@ -19,6 +19,7 @@ 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;

View file

@ -8,6 +8,7 @@ use std::os::unix::io::RawFd;
use bindings;
use error::{Error, Result};
use hotplug::{hotplug_cb, UsbHotplugHandler, UsbHotplugHandlerHolder};
use libusb_device::LibUsbDevice;
use std::sync::Arc;
@ -160,6 +161,31 @@ impl LibUsbContext {
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.