blob: 821d2adc862b96d76c727eba2a87ac63bea5fa76 [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 bindings;
use error::{Error, Result};
use libusb_device::LibUsbDevice;
use std::sync::Arc;
pub struct LibUsbContextInner {
context: *mut bindings::libusb_context,
}
// Safe because libusb_context could be accessed from multiple threads safely.
unsafe impl Send for LibUsbContextInner {}
unsafe impl Sync for LibUsbContextInner {}
impl Drop for LibUsbContextInner {
fn drop(&mut self) {
// 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 }),
})
}
/// 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_event_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(),
);
}
}
}
/// 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())
}
}
}