summary refs log tree commit diff
path: root/usb_util/src/libusb_context.rs
diff options
context:
space:
mode:
Diffstat (limited to 'usb_util/src/libusb_context.rs')
-rw-r--r--usb_util/src/libusb_context.rs156
1 files changed, 156 insertions, 0 deletions
diff --git a/usb_util/src/libusb_context.rs b/usb_util/src/libusb_context.rs
new file mode 100644
index 0000000..821d2ad
--- /dev/null
+++ b/usb_util/src/libusb_context.rs
@@ -0,0 +1,156 @@
+// 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())
+        }
+    }
+}