1#![allow(deprecated)]
40
41#[cfg(all(feature = "linux-static-rusb", not(target_os = "macos")))]
42extern crate rusb;
43
44extern crate libc;
45
46mod error;
47mod ffi;
48
49use libc::{c_int, size_t, wchar_t};
50use std::ffi::CStr;
51use std::ffi::CString;
52use std::fmt;
53use std::mem::ManuallyDrop;
54use std::sync::atomic::{AtomicBool, Ordering};
55use std::sync::Arc;
56
57pub use error::HidError;
58
59pub type HidResult<T> = Result<T, HidError>;
60
61const STRING_BUF_LEN: usize = 128;
62
63struct HidApiLock;
66
67impl HidApiLock {
68 fn acquire() -> HidResult<HidApiLock> {
69 const EXPECTED_CURRENT: bool = false;
70
71 if EXPECTED_CURRENT
72 == HID_API_LOCK.compare_and_swap(EXPECTED_CURRENT, true, Ordering::SeqCst)
73 {
74 unsafe {
76 #[cfg(target_os = "android")]
78 rusb::ffi::libusb_set_option(
79 std::ptr::null_mut(),
80 rusb::ffi::constants::LIBUSB_OPTION_WEAK_AUTHORITY,
81 );
82
83 if ffi::hid_init() == -1 {
84 HID_API_LOCK.store(false, Ordering::SeqCst);
85 return Err(HidError::InitializationError);
86 }
87 Ok(HidApiLock)
88 }
89 } else {
90 Err(HidError::InitializationError)
91 }
92 }
93}
94
95impl Drop for HidApiLock {
96 fn drop(&mut self) {
97 unsafe {
98 ffi::hid_exit();
99 }
100 HID_API_LOCK.store(false, Ordering::SeqCst);
101 }
102}
103
104pub struct HidApi {
107 devices: Vec<HidDeviceInfo>, device_list: Vec<DeviceInfo>,
109 _lock: Arc<HidApiLock>,
110}
111
112static HID_API_LOCK: AtomicBool = AtomicBool::new(false);
113
114impl HidApi {
115 pub fn new() -> HidResult<Self> {
119 let lock = HidApiLock::acquire()?;
120
121 let device_list = unsafe { HidApi::get_hid_device_info_vector()? };
122
123 Ok(HidApi {
124 device_list: device_list.clone(),
125 devices: device_list.into_iter().map(|d| d.into()).collect(),
126 _lock: Arc::new(lock),
127 })
128 }
129
130 pub fn refresh_devices(&mut self) -> HidResult<()> {
133 let device_list = unsafe { HidApi::get_hid_device_info_vector()? };
134 self.device_list = device_list.clone();
135 self.devices = device_list.into_iter().map(|d| d.into()).collect();
136 Ok(())
137 }
138
139 unsafe fn get_hid_device_info_vector() -> HidResult<Vec<DeviceInfo>> {
140 let mut device_vector = Vec::with_capacity(8);
141
142 let enumeration = ffi::hid_enumerate(0, 0);
143 {
144 let mut current_device = enumeration;
145
146 while !current_device.is_null() {
147 device_vector.push(conv_hid_device_info(current_device)?);
148 current_device = (*current_device).next;
149 }
150 }
151
152 if !enumeration.is_null() {
153 ffi::hid_free_enumeration(enumeration);
154 }
155
156 Ok(device_vector)
157 }
158
159 #[deprecated]
163 pub fn devices(&self) -> &Vec<HidDeviceInfo> {
164 &self.devices
165 }
166
167 pub fn device_list(&self) -> impl Iterator<Item = &DeviceInfo> {
169 self.device_list.iter()
170 }
171
172 pub fn open(&self, vid: u16, pid: u16) -> HidResult<HidDevice> {
178 let device = unsafe { ffi::hid_open(vid, pid, std::ptr::null()) };
179
180 if device.is_null() {
181 match self.check_error() {
182 Ok(err) => Err(err),
183 Err(e) => Err(e),
184 }
185 } else {
186 Ok(HidDevice {
187 _hid_device: device,
188 _lock: ManuallyDrop::new(self._lock.clone()),
189 })
190 }
191 }
192
193 pub fn open_serial(&self, vid: u16, pid: u16, sn: &str) -> HidResult<HidDevice> {
196 let mut chars = sn.chars().map(|c| c as wchar_t).collect::<Vec<_>>();
197 chars.push(0 as wchar_t);
198 let device = unsafe { ffi::hid_open(vid, pid, chars.as_ptr()) };
199 if device.is_null() {
200 match self.check_error() {
201 Ok(err) => Err(err),
202 Err(e) => Err(e),
203 }
204 } else {
205 Ok(HidDevice {
206 _hid_device: device,
207 _lock: ManuallyDrop::new(self._lock.clone()),
208 })
209 }
210 }
211
212 pub fn open_path(&self, device_path: &CStr) -> HidResult<HidDevice> {
216 let device = unsafe { ffi::hid_open_path(device_path.as_ptr()) };
217
218 if device.is_null() {
219 match self.check_error() {
220 Ok(err) => Err(err),
221 Err(e) => Err(e),
222 }
223 } else {
224 Ok(HidDevice {
225 _hid_device: device,
226 _lock: ManuallyDrop::new(self._lock.clone()),
227 })
228 }
229 }
230
231 #[cfg(all(unix, not(target_os = "macos")))]
239 pub fn wrap_sys_device(&self, sys_dev: i32, interface_num: i32) -> HidResult<HidDevice> {
240 let device = unsafe { ffi::hid_libusb_wrap_sys_device(sys_dev as _, interface_num) };
241
242 if device.is_null() {
243 match self.check_error() {
244 Ok(err) => Err(err),
245 Err(e) => Err(e),
246 }
247 } else {
248 Ok(HidDevice {
249 _hid_device: device,
250 _lock: ManuallyDrop::new(self._lock.clone()),
251 })
252 }
253 }
254
255 pub fn check_error(&self) -> HidResult<HidError> {
264 Ok(HidError::HidApiError {
265 message: unsafe {
266 match wchar_to_string(ffi::hid_error(std::ptr::null_mut())) {
267 WcharString::String(s) => s,
268 _ => return Err(HidError::HidApiErrorEmpty),
269 }
270 },
271 })
272 }
273}
274
275unsafe fn wchar_to_string(wstr: *const wchar_t) -> WcharString {
277 if wstr.is_null() {
278 return WcharString::None;
279 }
280
281 let mut char_vector: Vec<char> = Vec::with_capacity(8);
282 let mut raw_vector: Vec<wchar_t> = Vec::with_capacity(8);
283 let mut index: isize = 0;
284 let mut invalid_char = false;
285
286 let o = |i| *wstr.offset(i);
287
288 while o(index) != 0 {
289 use std::char;
290
291 raw_vector.push(*wstr.offset(index));
292
293 if !invalid_char {
294 if let Some(c) = char::from_u32(o(index) as u32) {
295 char_vector.push(c);
296 } else {
297 invalid_char = true;
298 }
299 }
300
301 index += 1;
302 }
303
304 if !invalid_char {
305 WcharString::String(char_vector.into_iter().collect())
306 } else {
307 WcharString::Raw(raw_vector)
308 }
309}
310
311unsafe fn conv_hid_device_info(src: *mut ffi::HidDeviceInfo) -> HidResult<DeviceInfo> {
313 Ok(DeviceInfo {
314 path: CStr::from_ptr((*src).path).to_owned(),
315 vendor_id: (*src).vendor_id,
316 product_id: (*src).product_id,
317 serial_number: wchar_to_string((*src).serial_number),
318 release_number: (*src).release_number,
319 manufacturer_string: wchar_to_string((*src).manufacturer_string),
320 product_string: wchar_to_string((*src).product_string),
321 usage_page: (*src).usage_page,
322 usage: (*src).usage,
323 interface_number: (*src).interface_number,
324 })
325}
326
327#[derive(Clone)]
328enum WcharString {
329 String(String),
330 Raw(Vec<wchar_t>),
331 None,
332}
333
334impl Into<Option<String>> for WcharString {
335 fn into(self) -> Option<String> {
336 match self {
337 WcharString::String(s) => Some(s),
338 _ => None,
339 }
340 }
341}
342
343#[derive(Debug, Clone)]
347#[deprecated]
348pub struct HidDeviceInfo {
349 pub path: CString,
350 pub vendor_id: u16,
351 pub product_id: u16,
352 pub serial_number: Option<String>,
353 pub release_number: u16,
354 pub manufacturer_string: Option<String>,
355 pub product_string: Option<String>,
356 pub usage_page: u16,
357 pub usage: u16,
358 pub interface_number: i32,
359}
360
361#[derive(Clone)]
367pub struct DeviceInfo {
368 path: CString,
369 vendor_id: u16,
370 product_id: u16,
371 serial_number: WcharString,
372 release_number: u16,
373 manufacturer_string: WcharString,
374 product_string: WcharString,
375 usage_page: u16,
376 usage: u16,
377 interface_number: i32,
378}
379
380impl DeviceInfo {
381 pub fn path(&self) -> &CStr {
382 &self.path
383 }
384 pub fn vendor_id(&self) -> u16 {
385 self.vendor_id
386 }
387 pub fn product_id(&self) -> u16 {
388 self.product_id
389 }
390
391 pub fn serial_number(&self) -> Option<&str> {
393 match self.serial_number {
394 WcharString::String(ref s) => Some(s),
395 _ => None,
396 }
397 }
398 pub fn serial_number_raw(&self) -> Option<&[wchar_t]> {
399 match self.serial_number {
400 WcharString::Raw(ref s) => Some(s),
401 _ => None,
402 }
403 }
404
405 pub fn release_number(&self) -> u16 {
406 self.release_number
407 }
408
409 pub fn manufacturer_string(&self) -> Option<&str> {
411 match self.manufacturer_string {
412 WcharString::String(ref s) => Some(s),
413 _ => None,
414 }
415 }
416 pub fn manufacturer_string_raw(&self) -> Option<&[wchar_t]> {
417 match self.manufacturer_string {
418 WcharString::Raw(ref s) => Some(s),
419 _ => None,
420 }
421 }
422
423 pub fn product_string(&self) -> Option<&str> {
425 match self.product_string {
426 WcharString::String(ref s) => Some(s),
427 _ => None,
428 }
429 }
430 pub fn product_string_raw(&self) -> Option<&[wchar_t]> {
431 match self.product_string {
432 WcharString::Raw(ref s) => Some(s),
433 _ => None,
434 }
435 }
436
437 pub fn usage_page(&self) -> u16 {
438 self.usage_page
439 }
440 pub fn usage(&self) -> u16 {
441 self.usage
442 }
443 pub fn interface_number(&self) -> i32 {
444 self.interface_number
445 }
446
447 pub fn open_device(&self, hidapi: &HidApi) -> HidResult<HidDevice> {
457 if self.path.as_bytes().len() != 0 {
458 hidapi.open_path(self.path.as_c_str())
459 } else if let Some(ref sn) = self.serial_number() {
460 hidapi.open_serial(self.vendor_id, self.product_id, sn)
461 } else {
462 Err(HidError::OpenHidDeviceWithDeviceInfoError {
463 device_info: Box::new(self.clone().into()),
464 })
465 }
466 }
467}
468
469impl fmt::Debug for DeviceInfo {
470 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
471 f.debug_struct("HidDeviceInfo")
472 .field("vendor_id", &self.vendor_id)
473 .field("product_id", &self.product_id)
474 .finish()
475 }
476}
477
478impl Into<HidDeviceInfo> for DeviceInfo {
479 fn into(self) -> HidDeviceInfo {
480 HidDeviceInfo {
481 path: self.path,
482 vendor_id: self.vendor_id,
483 product_id: self.product_id,
484 serial_number: match self.serial_number {
485 WcharString::String(s) => Some(s),
486 _ => None,
487 },
488 release_number: self.release_number,
489 manufacturer_string: match self.manufacturer_string {
490 WcharString::String(s) => Some(s),
491 _ => None,
492 },
493 product_string: match self.product_string {
494 WcharString::String(s) => Some(s),
495 _ => None,
496 },
497 usage_page: self.usage_page,
498 usage: self.usage,
499 interface_number: self.interface_number,
500 }
501 }
502}
503
504impl HidDeviceInfo {
505 pub fn open_device(&self, hidapi: &HidApi) -> HidResult<HidDevice> {
515 if self.path.as_bytes().len() != 0 {
516 hidapi.open_path(self.path.as_c_str())
517 } else if let Some(ref sn) = self.serial_number {
518 hidapi.open_serial(self.vendor_id, self.product_id, sn)
519 } else {
520 Err(HidError::OpenHidDeviceWithDeviceInfoError {
521 device_info: Box::new(self.clone()),
522 })
523 }
524 }
525}
526
527pub struct HidDevice {
529 _hid_device: *mut ffi::HidDevice,
530 _lock: ManuallyDrop<Arc<HidApiLock>>,
532}
533
534unsafe impl Send for HidDevice {}
535
536impl Drop for HidDevice {
537 fn drop(&mut self) {
538 unsafe {
539 ffi::hid_close(self._hid_device);
540 ManuallyDrop::drop(&mut self._lock);
541 };
542 }
543}
544
545impl HidDevice {
546 fn check_size(&self, res: i32) -> HidResult<usize> {
549 if res == -1 {
550 match self.check_error() {
551 Ok(err) => Err(err),
552 Err(e) => Err(e),
553 }
554 } else {
555 Ok(res as usize)
556 }
557 }
558
559 pub fn check_error(&self) -> HidResult<HidError> {
567 Ok(HidError::HidApiError {
568 message: unsafe {
569 match wchar_to_string(ffi::hid_error(self._hid_device)) {
570 WcharString::String(s) => s,
571 _ => return Err(HidError::HidApiErrorEmpty),
572 }
573 },
574 })
575 }
576
577 pub fn write(&self, data: &[u8]) -> HidResult<usize> {
590 if data.len() == 0 {
591 return Err(HidError::InvalidZeroSizeData);
592 }
593 let res = unsafe { ffi::hid_write(self._hid_device, data.as_ptr(), data.len() as size_t) };
594 self.check_size(res)
595 }
596
597 pub fn read(&self, buf: &mut [u8]) -> HidResult<usize> {
601 let res = unsafe { ffi::hid_read(self._hid_device, buf.as_mut_ptr(), buf.len() as size_t) };
602 self.check_size(res)
603 }
604
605 pub fn read_timeout(&self, buf: &mut [u8], timeout: i32) -> HidResult<usize> {
610 let res = unsafe {
611 ffi::hid_read_timeout(
612 self._hid_device,
613 buf.as_mut_ptr(),
614 buf.len() as size_t,
615 timeout,
616 )
617 };
618 self.check_size(res)
619 }
620
621 pub fn send_feature_report(&self, data: &[u8]) -> HidResult<()> {
633 if data.len() == 0 {
634 return Err(HidError::InvalidZeroSizeData);
635 }
636 let res = unsafe {
637 ffi::hid_send_feature_report(self._hid_device, data.as_ptr(), data.len() as size_t)
638 };
639 let res = self.check_size(res)?;
640 if res != data.len() {
641 Err(HidError::IncompleteSendError {
642 sent: res,
643 all: data.len(),
644 })
645 } else {
646 Ok(())
647 }
648 }
649
650 pub fn get_feature_report(&self, buf: &mut [u8]) -> HidResult<usize> {
654 let res = unsafe {
655 ffi::hid_get_feature_report(self._hid_device, buf.as_mut_ptr(), buf.len() as size_t)
656 };
657 self.check_size(res)
658 }
659
660 pub fn set_blocking_mode(&self, blocking: bool) -> HidResult<()> {
666 let res = unsafe {
667 ffi::hid_set_nonblocking(self._hid_device, if blocking { 0i32 } else { 1i32 })
668 };
669 if res == -1 {
670 Err(HidError::SetBlockingModeError {
671 mode: match blocking {
672 true => "blocking",
673 false => "not blocking",
674 },
675 })
676 } else {
677 Ok(())
678 }
679 }
680
681 pub fn get_manufacturer_string(&self) -> HidResult<Option<String>> {
683 let mut buf = [0 as wchar_t; STRING_BUF_LEN];
684 let res = unsafe {
685 ffi::hid_get_manufacturer_string(
686 self._hid_device,
687 buf.as_mut_ptr(),
688 STRING_BUF_LEN as size_t,
689 )
690 };
691 let res = self.check_size(res)?;
692 unsafe { Ok(wchar_to_string(buf[..res].as_ptr()).into()) }
693 }
694
695 pub fn get_product_string(&self) -> HidResult<Option<String>> {
697 let mut buf = [0 as wchar_t; STRING_BUF_LEN];
698 let res = unsafe {
699 ffi::hid_get_product_string(
700 self._hid_device,
701 buf.as_mut_ptr(),
702 STRING_BUF_LEN as size_t,
703 )
704 };
705 let res = self.check_size(res)?;
706 unsafe { Ok(wchar_to_string(buf[..res].as_ptr()).into()) }
707 }
708
709 pub fn get_serial_number_string(&self) -> HidResult<Option<String>> {
711 let mut buf = [0 as wchar_t; STRING_BUF_LEN];
712 let res = unsafe {
713 ffi::hid_get_serial_number_string(
714 self._hid_device,
715 buf.as_mut_ptr(),
716 STRING_BUF_LEN as size_t,
717 )
718 };
719 let res = self.check_size(res)?;
720 unsafe { Ok(wchar_to_string(buf[..res].as_ptr()).into()) }
721 }
722
723 pub fn get_indexed_string(&self, index: i32) -> HidResult<Option<String>> {
725 let mut buf = [0 as wchar_t; STRING_BUF_LEN];
726 let res = unsafe {
727 ffi::hid_get_indexed_string(
728 self._hid_device,
729 index as c_int,
730 buf.as_mut_ptr(),
731 STRING_BUF_LEN,
732 )
733 };
734 let res = self.check_size(res)?;
735 unsafe { Ok(wchar_to_string(buf[..res].as_ptr()).into()) }
736 }
737}