blob: 016ea2291c305b7e8cfeb27c27698ff64f928444 [file] [log] [blame]
// 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::os::raw::{c_short, c_void};
use std::os::unix::io::RawFd;
use bindings;
use error::{Error, Result};
use libusb_device::LibUsbDevice;
use std::sync::{Arc, 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),
}),
})
}
/// 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<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().unwrap() = Some(holder);
}
/// Remove the previous registered notifiers.
pub fn remove_pollfd_notifiers(&self) {
self.inner.remove_pollfd_notifiers();
}
}
/// 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).clone())
}
}
}
/// 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<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);
}