• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std;
6 use std::os::raw::{c_short, c_void};
7 use std::os::unix::io::RawFd;
8 use std::sync::Arc;
9 
10 use crate::bindings;
11 use crate::error::{Error, Result};
12 use crate::hotplug::{hotplug_cb, UsbHotplugHandler, UsbHotplugHandlerHolder};
13 use crate::libusb_device::LibUsbDevice;
14 
15 use sync::Mutex;
16 
17 pub struct LibUsbContextInner {
18     context: *mut bindings::libusb_context,
19     pollfd_change_handler: Mutex<Option<Box<PollfdChangeHandlerHolder>>>,
20 }
21 
22 // Safe because libusb_context could be accessed from multiple threads safely.
23 unsafe impl Send for LibUsbContextInner {}
24 unsafe impl Sync for LibUsbContextInner {}
25 
26 impl LibUsbContextInner {
27     /// Remove the previous registered notifiers.
remove_pollfd_notifiers(&self)28     pub fn remove_pollfd_notifiers(&self) {
29         // Safe because 'self.context' is valid.
30         unsafe {
31             bindings::libusb_set_pollfd_notifiers(self.context, None, None, std::ptr::null_mut());
32         }
33     }
34 }
35 
36 impl Drop for LibUsbContextInner {
drop(&mut self)37     fn drop(&mut self) {
38         // Avoid pollfd change handler call when libusb_exit is called.
39         self.remove_pollfd_notifiers();
40         // Safe beacuse 'self.context' points to a valid context allocated by libusb_init.
41         unsafe {
42             bindings::libusb_exit(self.context);
43         }
44     }
45 }
46 
47 /// Wrapper for libusb_context. The libusb libary initialization/deinitialization
48 /// is managed by this context.
49 /// See: http://libusb.sourceforge.net/api-1.0/group__libusb__lib.html
50 #[derive(Clone)]
51 pub struct LibUsbContext {
52     inner: Arc<LibUsbContextInner>,
53 }
54 
55 impl LibUsbContext {
56     /// Create a new LibUsbContext.
new() -> Result<LibUsbContext>57     pub fn new() -> Result<LibUsbContext> {
58         let mut ctx: *mut bindings::libusb_context = std::ptr::null_mut();
59         // Safe because '&mut ctx' points to a valid memory (on stack).
60         try_libusb!(unsafe { bindings::libusb_init(&mut ctx) });
61         Ok(LibUsbContext {
62             inner: Arc::new(LibUsbContextInner {
63                 context: ctx,
64                 pollfd_change_handler: Mutex::new(None),
65             }),
66         })
67     }
68 
69     /// Create a new jailed LibUsbContext.
70     #[cfg(feature = "sandboxed-libusb")]
new_jailed() -> Result<LibUsbContext>71     pub fn new_jailed() -> Result<LibUsbContext> {
72         let mut ctx: *mut bindings::libusb_context = std::ptr::null_mut();
73         // Safe because '&mut ctx' points to a valid memory (on stack).
74         try_libusb!(unsafe { bindings::libusb_init_jailed(&mut ctx) });
75         Ok(LibUsbContext {
76             inner: Arc::new(LibUsbContextInner {
77                 context: ctx,
78                 pollfd_change_handler: Mutex::new(None),
79             }),
80         })
81     }
82 
83     /// Build device from File.
84     #[cfg(feature = "sandboxed-libusb")]
get_device_from_fd(&self, fd: std::fs::File) -> Result<LibUsbDevice>85     pub fn get_device_from_fd(&self, fd: std::fs::File) -> Result<LibUsbDevice> {
86         use std::os::unix::io::IntoRawFd;
87 
88         let fd = fd.into_raw_fd();
89         let mut device: *mut bindings::libusb_device = std::ptr::null_mut();
90         // Safe because fd is valid and owned, and '&mut device' points to valid memory.
91         try_libusb!(unsafe {
92             bindings::libusb_get_device_from_fd(self.inner.context, fd, &mut device)
93         });
94         unsafe { Ok(LibUsbDevice::new(self.inner.clone(), device)) }
95     }
96 
97     /// Returns a list of USB devices currently attached to the system.
get_device_iter(&self) -> Result<DeviceIter>98     pub fn get_device_iter(&self) -> Result<DeviceIter> {
99         let mut list: *mut *mut bindings::libusb_device = std::ptr::null_mut();
100         // Safe because 'inner.context' points to a valid context and '&mut list' points to a valid
101         // memory.
102         try_libusb!(unsafe { bindings::libusb_get_device_list(self.inner.context, &mut list) });
103 
104         Ok(DeviceIter {
105             context: self.inner.clone(),
106             list,
107             index: 0,
108         })
109     }
110 
111     /// Check at runtime if the loaded library has a given capability.
has_capability(&self, cap: u32) -> bool112     pub fn has_capability(&self, cap: u32) -> bool {
113         // Safe because libusb_init is called before this call happens.
114         unsafe { bindings::libusb_has_capability(cap) != 0 }
115     }
116 
117     /// Return an iter of poll fds. Those fds that should be polled to handle libusb events.
get_pollfd_iter(&self) -> PollFdIter118     pub fn get_pollfd_iter(&self) -> PollFdIter {
119         // Safe because 'inner.context' is inited.
120         let list: *mut *const bindings::libusb_pollfd =
121             unsafe { bindings::libusb_get_pollfds(self.inner.context) };
122         PollFdIter { list, index: 0 }
123     }
124 
125     /// Handle libusb events in a non block way.
handle_events_nonblock(&self)126     pub fn handle_events_nonblock(&self) {
127         static mut zero_time: bindings::timeval = bindings::timeval {
128             tv_sec: 0,
129             tv_usec: 0,
130         };
131         // Safe because 'inner.context' points to valid context.
132         unsafe {
133             bindings::libusb_handle_events_timeout_completed(
134                 self.inner.context,
135                 &mut zero_time as *mut bindings::timeval,
136                 std::ptr::null_mut(),
137             );
138         }
139     }
140 
141     /// Set a handler that could handle pollfd change events.
set_pollfd_notifiers(&self, handler: Box<dyn LibUsbPollfdChangeHandler>)142     pub fn set_pollfd_notifiers(&self, handler: Box<dyn LibUsbPollfdChangeHandler>) {
143         // LibUsbContext is alive when any libusb related function is called. It owns the handler,
144         // thus the handler memory is always valid when callback is invoked.
145         let holder = Box::new(PollfdChangeHandlerHolder { handler });
146         let raw_holder = Box::into_raw(holder);
147         unsafe {
148             bindings::libusb_set_pollfd_notifiers(
149                 self.inner.context,
150                 Some(pollfd_added_cb),
151                 Some(pollfd_removed_cb),
152                 raw_holder as *mut c_void,
153             );
154         }
155         // Safe because raw_holder is from Boxed pointer.
156         let holder = unsafe { Box::from_raw(raw_holder) };
157         *self.inner.pollfd_change_handler.lock() = Some(holder);
158     }
159 
160     /// Remove the previous registered notifiers.
remove_pollfd_notifiers(&self)161     pub fn remove_pollfd_notifiers(&self) {
162         self.inner.remove_pollfd_notifiers();
163     }
164 
165     /// Set a callback that could handle hotplug events. Currently, this function listen to hotplug
166     /// event of all devices.
set_hotplug_cb<H: UsbHotplugHandler + Sized>(&self, handler: H) -> Result<()>167     pub fn set_hotplug_cb<H: UsbHotplugHandler + Sized>(&self, handler: H) -> Result<()> {
168         let event = bindings::LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED
169             | bindings::LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT;
170         let holder = UsbHotplugHandlerHolder::new(self.inner.clone(), handler);
171         let raw_holder = Box::into_raw(holder);
172         // Safe becuase hotpulg cb is a vaild c function and raw_holder points to memory for that
173         // function argument.
174         try_libusb!(unsafe {
175             bindings::libusb_hotplug_register_callback(
176                 self.inner.context,
177                 event,
178                 bindings::LIBUSB_HOTPLUG_NO_FLAGS,
179                 bindings::LIBUSB_HOTPLUG_MATCH_ANY,
180                 bindings::LIBUSB_HOTPLUG_MATCH_ANY,
181                 bindings::LIBUSB_HOTPLUG_MATCH_ANY,
182                 Some(hotplug_cb),
183                 raw_holder as *mut c_void,
184                 std::ptr::null_mut(),
185             )
186         });
187         Ok(())
188     }
189 }
190 
191 /// Iterator for device list.
192 pub struct DeviceIter {
193     context: Arc<LibUsbContextInner>,
194     list: *mut *mut bindings::libusb_device,
195     index: isize,
196 }
197 
198 impl Drop for DeviceIter {
drop(&mut self)199     fn drop(&mut self) {
200         // Safe because 'self.list' is inited by a valid pointer from libusb_get_device_list.
201         unsafe {
202             bindings::libusb_free_device_list(self.list, 1);
203         }
204     }
205 }
206 
207 impl Iterator for DeviceIter {
208     type Item = LibUsbDevice;
209 
next(&mut self) -> Option<LibUsbDevice>210     fn next(&mut self) -> Option<LibUsbDevice> {
211         // Safe becuase 'self.list' is valid, the list is null terminated.
212         unsafe {
213             let current_ptr = self.list.offset(self.index);
214             if (*current_ptr).is_null() {
215                 return None;
216             }
217             self.index += 1;
218             Some(LibUsbDevice::new(self.context.clone(), *current_ptr))
219         }
220     }
221 }
222 
223 /// Iterator for pollfds.
224 pub struct PollFdIter {
225     list: *mut *const bindings::libusb_pollfd,
226     index: isize,
227 }
228 
229 impl Drop for PollFdIter {
drop(&mut self)230     fn drop(&mut self) {
231         // Safe because 'self.list' points to valid memory of pollfd list.
232         unsafe {
233             bindings::libusb_free_pollfds(self.list);
234         }
235     }
236 }
237 
238 impl Iterator for PollFdIter {
239     type Item = bindings::libusb_pollfd;
240 
next(&mut self) -> Option<bindings::libusb_pollfd>241     fn next(&mut self) -> Option<bindings::libusb_pollfd> {
242         // Safe because 'self.index' never grow out of the null pointer index.
243         unsafe {
244             let current_ptr = self.list.offset(self.index);
245             if (*current_ptr).is_null() {
246                 return None;
247             }
248 
249             self.index += 1;
250             // Safe because '*current_ptr' is not null.
251             Some(**current_ptr)
252         }
253     }
254 }
255 
256 /// Trait for handler that handles Pollfd Change events.
257 pub trait LibUsbPollfdChangeHandler: Send + Sync + 'static {
add_poll_fd(&self, fd: RawFd, events: c_short)258     fn add_poll_fd(&self, fd: RawFd, events: c_short);
remove_poll_fd(&self, fd: RawFd)259     fn remove_poll_fd(&self, fd: RawFd);
260 }
261 
262 // This struct owns LibUsbPollfdChangeHandler. We need it because it's not possible to cast void
263 // pointer to trait pointer.
264 struct PollfdChangeHandlerHolder {
265     handler: Box<dyn LibUsbPollfdChangeHandler>,
266 }
267 
268 // This function is safe when user_data points to valid PollfdChangeHandlerHolder.
pollfd_added_cb(fd: RawFd, events: c_short, user_data: *mut c_void)269 unsafe extern "C" fn pollfd_added_cb(fd: RawFd, events: c_short, user_data: *mut c_void) {
270     // Safe because user_data was casted from holder.
271     let keeper = &*(user_data as *mut PollfdChangeHandlerHolder);
272     keeper.handler.add_poll_fd(fd, events);
273 }
274 
275 // This function is safe when user_data points to valid PollfdChangeHandlerHolder.
pollfd_removed_cb(fd: RawFd, user_data: *mut c_void)276 unsafe extern "C" fn pollfd_removed_cb(fd: RawFd, user_data: *mut c_void) {
277     // Safe because user_data was casted from holder.
278     let keeper = &*(user_data as *mut PollfdChangeHandlerHolder);
279     keeper.handler.remove_poll_fd(fd);
280 }
281