| /**************************************************************************** | ||
| Copyright (c) 2015 Osspial All Rights Reserved. | ||
| This file is part of hidapi-rs, based on hidapi_rust by Roland Ruckerbauer. | ||
| ****************************************************************************/ | ||
| //! This example shows the added possibility (after version 0.4.1), | ||
| //! to move devices into a function / or closure with static lifetime bounds. | ||
| extern crate hidapi; | ||
| use hidapi::{HidApi, HidDevice}; | ||
| use std::rc::Rc; | ||
| fn main() { | ||
| let _dev = test_lt(); | ||
| } | ||
| fn requires_static_lt_bound<F: Fn() + 'static>(f: F) { | ||
| f(); | ||
| } | ||
| fn test_lt() -> Rc<HidDevice> { | ||
| let api = HidApi::new().expect("Hidapi init failed"); | ||
| let devices = api.devices(); | ||
| let dev_info = devices | ||
| .get(0) | ||
| .expect("There is not a single hid device available"); | ||
| println!("{:#?}", dev_info); | ||
| let dev = Rc::new( | ||
| api.open(dev_info.vendor_id, dev_info.product_id) | ||
| .expect("Can not open device"), | ||
| ); | ||
| let dev_1 = dev.clone(); | ||
| requires_static_lt_bound(move || { | ||
| println!("{}", dev_1.check_error().unwrap()); //<! Can be captured by closure with static lt | ||
| }); | ||
| dev //<! Can be returned from a function, which exceeds the lifetime of the API context | ||
| } |
+43
| // ************************************************************************** | ||
| // Copyright (c) 2018 Roland Ruckerbauer All Rights Reserved. | ||
| // | ||
| // This file is part of hidapi-rs, based on hidapi-rs by Osspial | ||
| // ************************************************************************** | ||
| use super::HidDeviceInfo; | ||
| use failure::{Compat, Error}; | ||
| use libc::wchar_t; | ||
| #[derive(Debug, Fail)] | ||
| pub enum HidError { | ||
| #[fail(display = "hidapi error: {}", message)] | ||
| HidApiError { message: String }, | ||
| #[fail( | ||
| display = "hidapi error: (could not get error message), caused by: {}", | ||
| cause | ||
| )] | ||
| HidApiErrorEmptyWithCause { | ||
| #[cause] | ||
| cause: Compat<Error>, | ||
| }, | ||
| #[fail(display = "hidapi error: (could not get error message)")] | ||
| HidApiErrorEmpty, | ||
| #[fail(display = "failed converting {:#X} to rust char", wide_char)] | ||
| FromWideCharError { wide_char: wchar_t }, | ||
| #[fail(display = "Failed to initialize hidapi (maybe initialized before?)")] | ||
| InitializationError, | ||
| #[fail(display = "Failed opening hid device")] | ||
| OpenHidDeviceError, | ||
| #[fail(display = "Invalid data: size can not be 0")] | ||
| InvalidZeroSizeData, | ||
| #[fail( | ||
| display = "Failed to send all data: only sent {} out of {} bytes", | ||
| sent, | ||
| all | ||
| )] | ||
| IncompleteSendError { sent: usize, all: usize }, | ||
| #[fail(display = "Can not set blocking mode to '{}'", mode)] | ||
| SetBlockingModeError { mode: &'static str }, | ||
| #[fail(display = "Can not open hid device with: {:?}", device_info)] | ||
| OpenHidDeviceWithDeviceInfoError { device_info: HidDeviceInfo }, | ||
| } |
+2
-0
| target | ||
| Cargo.lock | ||
| .idea/ | ||
| hidapi-rs.iml |
+21
-4
@@ -1,7 +0,22 @@ | ||
| os: | ||
| - linux | ||
| - osx | ||
| language: rust | ||
| matrix: | ||
| include: | ||
| - env: TARGET=x86_64-unknown-linux-gnu FEATURE_FLAGS="linux-static-libusb" | ||
| - env: TARGET=x86_64-unknown-linux-gnu FEATURE_FLAGS="linux-static-hidraw" | ||
| - env: TARGET=x86_64-unknown-linux-gnu FEATURE_FLAGS="linux-shared-libusb" | ||
| - env: TARGET=x86_64-unknown-linux-gnu FEATURE_FLAGS="linux-shared-hidraw" | ||
| - env: TARGET=x86_64-apple-darwin | ||
| os: osx | ||
| script: | ||
| - cargo build --verbose --no-default-features --features="${FEATURE_FLAGS}" | ||
| - cargo test --verbose --no-default-features --features="${FEATURE_FLAGS}" | ||
| addons: | ||
@@ -11,1 +26,3 @@ apt: | ||
| - libusb-1.0-0-dev | ||
| - libudev-dev | ||
| - libhidapi-dev |
+85
-16
@@ -20,23 +20,93 @@ // ************************************************************************** | ||
| extern crate gcc; | ||
| extern crate cc; | ||
| extern crate pkg_config; | ||
| use std::env; | ||
| fn main() { | ||
| compile(); | ||
| let target = env::var("TARGET").unwrap(); | ||
| if target.contains("linux") { | ||
| compile_linux(); | ||
| } else if target.contains("windows") { | ||
| compile_windows(); | ||
| } else if target.contains("darwin") { | ||
| compile_macos(); | ||
| } else { | ||
| panic!("Unsupported target os for hidapi-rs"); | ||
| } | ||
| } | ||
| #[cfg(target_os = "linux")] | ||
| fn compile() { | ||
| let mut config = gcc::Config::new(); | ||
| config.file("etc/hidapi/libusb/hid.c").include("etc/hidapi/hidapi"); | ||
| let lib = pkg_config::find_library("libusb-1.0").expect("Unable to find libusb-1.0"); | ||
| for path in lib.include_paths { | ||
| config.include(path.to_str().expect("Failed to convert include path to str")); | ||
| fn compile_linux() { | ||
| // First check the features enabled for the crate. | ||
| // Only one linux backend should be enabled at a time. | ||
| let avail_backends: [(&'static str, Box<Fn()>); 4] = [ | ||
| ( | ||
| "LINUX_STATIC_HIDRAW", | ||
| Box::new(|| { | ||
| let mut config = cc::Build::new(); | ||
| config | ||
| .file("etc/hidapi/linux/hid.c") | ||
| .include("etc/hidapi/hidapi"); | ||
| pkg_config::probe_library("libudev").expect("Unable to find libudev"); | ||
| config.compile("libhidapi.a"); | ||
| }), | ||
| ), | ||
| ( | ||
| "LINUX_STATIC_LIBUSB", | ||
| Box::new(|| { | ||
| let mut config = cc::Build::new(); | ||
| config | ||
| .file("etc/hidapi/libusb/hid.c") | ||
| .include("etc/hidapi/hidapi"); | ||
| let lib = | ||
| pkg_config::find_library("libusb-1.0").expect("Unable to find libusb-1.0"); | ||
| for path in lib.include_paths { | ||
| config.include( | ||
| path.to_str() | ||
| .expect("Failed to convert include path to str"), | ||
| ); | ||
| } | ||
| config.compile("libhidapi.a"); | ||
| }), | ||
| ), | ||
| ( | ||
| "LINUX_SHARED_HIDRAW", | ||
| Box::new(|| { | ||
| pkg_config::probe_library("hidapi-hidraw").expect("Unable to find hidapi-hidraw"); | ||
| }), | ||
| ), | ||
| ( | ||
| "LINUX_SHARED_LIBUSB", | ||
| Box::new(|| { | ||
| pkg_config::probe_library("hidapi-libusb").expect("Unable to find hidapi-libusb"); | ||
| }), | ||
| ), | ||
| ]; | ||
| let mut backends = avail_backends | ||
| .iter() | ||
| .filter(|f| env::var(format!("CARGO_FEATURE_{}", f.0)).is_ok()); | ||
| if backends.clone().count() != 1 { | ||
| panic!("Exactly one linux hidapi backend must be selected."); | ||
| } | ||
| config.compile("libhidapi.a"); | ||
| // Build it! | ||
| (backends.next().unwrap().1)(); | ||
| } | ||
| #[cfg(target_os = "windows")] | ||
| fn compile() { | ||
| gcc::Config::new() | ||
| //#[cfg(all(feature = "shared-libusb", not(feature = "shared-hidraw")))] | ||
| //fn compile_linux() { | ||
| // | ||
| //} | ||
| // | ||
| //#[cfg(all(feature = "shared-hidraw"))] | ||
| //fn compile_linux() { | ||
| // | ||
| //} | ||
| fn compile_windows() { | ||
| cc::Build::new() | ||
| .file("etc/hidapi/windows/hid.c") | ||
@@ -48,5 +118,4 @@ .include("etc/hidapi/hidapi") | ||
| #[cfg(target_os = "macos")] | ||
| fn compile() { | ||
| gcc::Config::new() | ||
| fn compile_macos() { | ||
| cc::Build::new() | ||
| .file("etc/hidapi/mac/hid.c") | ||
@@ -53,0 +122,0 @@ .include("etc/hidapi/hidapi") |
+19
-6
@@ -15,4 +15,4 @@ # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO | ||
| name = "hidapi" | ||
| version = "0.4.1" | ||
| authors = ["Roland Ruckerbauer <roland.rucky@gmail.com>", "Osspial <osspial@gmail.com>", "Artyom Pavlov <newpavlov@gmail.com>"] | ||
| version = "0.5.0" | ||
| authors = ["Roland Ruckerbauer <roland.rucky@gmail.com>", "Osspial <osspial@gmail.com>", "Artyom Pavlov <newpavlov@gmail.com>", "mberndt123"] | ||
| build = "build.rs" | ||
@@ -25,8 +25,21 @@ links = "hidapi" | ||
| repository = "https://github.com/Osspial/hidapi-rs" | ||
| [dependencies.failure] | ||
| version = "0.1.2" | ||
| [dependencies.failure_derive] | ||
| version = "0.1.2" | ||
| [dependencies.libc] | ||
| version = "0.2.15" | ||
| version = "0.2.33" | ||
| [build-dependencies.cc] | ||
| version = "1.0.3" | ||
| [build-dependencies.pkg-config] | ||
| version = "0.3.5" | ||
| version = "0.3.9" | ||
| [build-dependencies.gcc] | ||
| version = "0.3" | ||
| [features] | ||
| default = ["linux-static-libusb"] | ||
| linux-shared-hidraw = [] | ||
| linux-shared-libusb = [] | ||
| linux-static-hidraw = [] | ||
| linux-static-libusb = [] |
@@ -0,0 +0,0 @@ * text=auto |
@@ -0,0 +0,0 @@ |
@@ -0,0 +0,0 @@ LOCAL_PATH:= $(call my-dir) |
@@ -0,0 +0,0 @@ |
@@ -0,0 +0,0 @@ This file is mostly for the maintainer. |
@@ -0,0 +0,0 @@ /******************************************************* |
@@ -0,0 +0,0 @@ Debug |
@@ -0,0 +0,0 @@ /******************************************************* |
@@ -0,0 +0,0 @@ *.o |
@@ -319,3 +319,3 @@ /******************************************************* | ||
| mainline libusb, it's inlined in libusb.h. This function will bear a striking | ||
| resemblence to that one, because there's about one way to code it. | ||
| resemblance to that one, because there's about one way to code it. | ||
@@ -853,3 +853,3 @@ Note that the data parameter is Unicode in UTF-16LE encoding. | ||
| make sure that a thread which is about to go to sleep waiting on | ||
| the condition acutally will go to sleep before the condition is | ||
| the condition actually will go to sleep before the condition is | ||
| signaled. */ | ||
@@ -963,3 +963,3 @@ pthread_mutex_lock(&dev->mutex); | ||
| /* Decide whether to use it for intput or output. */ | ||
| /* Decide whether to use it for input or output. */ | ||
| if (dev->input_endpoint == 0 && | ||
@@ -1020,3 +1020,3 @@ is_interrupt && is_input) { | ||
| if (dev->output_endpoint <= 0) { | ||
| /* No interrput out endpoint. Use the Control Endpoint */ | ||
| /* No interrupt out endpoint. Use the Control Endpoint */ | ||
| res = libusb_control_transfer(dev->device_handle, | ||
@@ -1023,0 +1023,0 @@ LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_OUT, |
@@ -0,0 +0,0 @@ Copyright (c) 2010, Alan Ott, Signal 11 Software |
@@ -0,0 +0,0 @@ GNU GENERAL PUBLIC LICENSE |
@@ -0,0 +0,0 @@ HIDAPI - Multi-Platform library for |
@@ -0,0 +0,0 @@ HIDAPI can be used under one of three licenses. |
@@ -0,0 +0,0 @@ Debug |
@@ -277,3 +277,5 @@ /******************************************************* | ||
| /* Get the dev_t (major/minor numbers) from the file handle. */ | ||
| fstat(dev->device_handle, &s); | ||
| ret = fstat(dev->device_handle, &s); | ||
| if (-1 == ret) | ||
| return ret; | ||
| /* Open a udev device from the dev_t. 'c' means character device. */ | ||
@@ -280,0 +282,0 @@ udev_dev = udev_device_new_from_devnum(udev, 'c', s.st_rdev); |
| There are two implementations of HIDAPI for Linux. One (hid.c) uses the | ||
| Linux hidraw driver, and the other (hid-libusb.c) uses libusb. Which one you | ||
| There are two implementations of HIDAPI for Linux. One (linux/hid.c) uses the | ||
| Linux hidraw driver, and the other (libusb/hid.c) uses libusb. Which one you | ||
| use depends on your application. Complete functionality of the hidraw | ||
@@ -12,14 +12,10 @@ version depends on patches to the Linux kernel which are not currently in | ||
| To use HIDAPI, simply drop either hid.c or hid-libusb.c into your | ||
| To use HIDAPI, simply drop either linux/hid.c or libusb/hid.c into your | ||
| application and build using the build parameters in the Makefile. | ||
| By default, on Linux, the Makefile in this directory is configured to use | ||
| the libusb implementation. To switch to the hidraw implementation, simply | ||
| change hid-libusb.c to hid.c in the Makefile. | ||
| Libusb Implementation notes | ||
| ---------------------------- | ||
| For the libusb implementation, libusb-1.0 must be installed. Libusb 1.0 is | ||
| different than the legacy libusb 0.1 which is installed on many systems. To | ||
| different than the legacy libusb 0.1 which is installed on many systems. To | ||
| install libusb-1.0 on Ubuntu and other Debian-based systems, run: | ||
@@ -26,0 +22,0 @@ sudo apt-get install libusb-1.0-0-dev |
@@ -0,0 +0,0 @@ # Ignore All, except pkg.m4, and of course this file. |
@@ -0,0 +0,0 @@ Debug |
+120
-122
@@ -27,2 +27,3 @@ /******************************************************* | ||
| #include <IOKit/hid/IOHIDKeys.h> | ||
| #include <IOKit/IOKitLib.h> | ||
| #include <CoreFoundation/CoreFoundation.h> | ||
@@ -34,2 +35,3 @@ #include <wchar.h> | ||
| #include <unistd.h> | ||
| #include <dlfcn.h> | ||
@@ -217,7 +219,2 @@ #include "hidapi.h" | ||
| static int32_t get_location_id(IOHIDDeviceRef device) | ||
| { | ||
| return get_int_property(device, CFSTR(kIOHIDLocationIDKey)); | ||
| } | ||
| static int32_t get_max_report_length(IOHIDDeviceRef device) | ||
@@ -270,42 +267,2 @@ { | ||
| static int get_string_property_utf8(IOHIDDeviceRef device, CFStringRef prop, char *buf, size_t len) | ||
| { | ||
| CFStringRef str; | ||
| if (!len) | ||
| return 0; | ||
| str = IOHIDDeviceGetProperty(device, prop); | ||
| buf[0] = 0; | ||
| if (str) { | ||
| len--; | ||
| CFIndex str_len = CFStringGetLength(str); | ||
| CFRange range; | ||
| range.location = 0; | ||
| range.length = str_len; | ||
| CFIndex used_buf_len; | ||
| CFIndex chars_copied; | ||
| chars_copied = CFStringGetBytes(str, | ||
| range, | ||
| kCFStringEncodingUTF8, | ||
| (char)'?', | ||
| FALSE, | ||
| (UInt8*)buf, | ||
| len, | ||
| &used_buf_len); | ||
| if (used_buf_len == len) | ||
| buf[len] = 0; /* len is decremented above */ | ||
| else | ||
| buf[used_buf_len] = 0; | ||
| return used_buf_len; | ||
| } | ||
| else | ||
| return 0; | ||
| } | ||
| static int get_serial_number(IOHIDDeviceRef device, wchar_t *buf, size_t len) | ||
@@ -337,29 +294,57 @@ { | ||
| static int make_path(IOHIDDeviceRef device, char *buf, size_t len) | ||
| /* hidapi_IOHIDDeviceGetService() | ||
| * | ||
| * Return the io_service_t corresponding to a given IOHIDDeviceRef, either by: | ||
| * - on OS X 10.6 and above, calling IOHIDDeviceGetService() | ||
| * - on OS X 10.5, extract it from the IOHIDDevice struct | ||
| */ | ||
| static io_service_t hidapi_IOHIDDeviceGetService(IOHIDDeviceRef device) | ||
| { | ||
| int res; | ||
| unsigned short vid, pid; | ||
| char transport[32]; | ||
| int32_t location; | ||
| static void *iokit_framework = NULL; | ||
| static io_service_t (*dynamic_IOHIDDeviceGetService)(IOHIDDeviceRef device) = NULL; | ||
| buf[0] = '\0'; | ||
| /* Use dlopen()/dlsym() to get a pointer to IOHIDDeviceGetService() if it exists. | ||
| * If any of these steps fail, dynamic_IOHIDDeviceGetService will be left NULL | ||
| * and the fallback method will be used. | ||
| */ | ||
| if (iokit_framework == NULL) { | ||
| iokit_framework = dlopen("/System/Library/IOKit.framework/IOKit", RTLD_LAZY); | ||
| res = get_string_property_utf8( | ||
| device, CFSTR(kIOHIDTransportKey), | ||
| transport, sizeof(transport)); | ||
| if (iokit_framework != NULL) | ||
| dynamic_IOHIDDeviceGetService = dlsym(iokit_framework, "IOHIDDeviceGetService"); | ||
| } | ||
| if (!res) | ||
| return -1; | ||
| if (dynamic_IOHIDDeviceGetService != NULL) { | ||
| /* Running on OS X 10.6 and above: IOHIDDeviceGetService() exists */ | ||
| return dynamic_IOHIDDeviceGetService(device); | ||
| } | ||
| else | ||
| { | ||
| /* Running on OS X 10.5: IOHIDDeviceGetService() doesn't exist. | ||
| * | ||
| * Be naughty and pull the service out of the IOHIDDevice. | ||
| * IOHIDDevice is an opaque struct not exposed to applications, but its | ||
| * layout is stable through all available versions of OS X. | ||
| * Tested and working on OS X 10.5.8 i386, x86_64, and ppc. | ||
| */ | ||
| struct IOHIDDevice_internal { | ||
| /* The first field of the IOHIDDevice struct is a | ||
| * CFRuntimeBase (which is a private CF struct). | ||
| * | ||
| * a, b, and c are the 3 fields that make up a CFRuntimeBase. | ||
| * See http://opensource.apple.com/source/CF/CF-476.18/CFRuntime.h | ||
| * | ||
| * The second field of the IOHIDDevice is the io_service_t we're looking for. | ||
| */ | ||
| uintptr_t a; | ||
| uint8_t b[4]; | ||
| #if __LP64__ | ||
| uint32_t c; | ||
| #endif | ||
| io_service_t service; | ||
| }; | ||
| struct IOHIDDevice_internal *tmp = (struct IOHIDDevice_internal *)device; | ||
| location = get_location_id(device); | ||
| vid = get_vendor_id(device); | ||
| pid = get_product_id(device); | ||
| res = snprintf(buf, len, "%s_%04hx_%04hx_%x", | ||
| transport, vid, pid, location); | ||
| buf[len-1] = '\0'; | ||
| return res+1; | ||
| return tmp->service; | ||
| } | ||
| } | ||
@@ -442,3 +427,2 @@ | ||
| wchar_t buf[BUF_LEN]; | ||
| char cbuf[BUF_LEN]; | ||
@@ -457,3 +441,5 @@ IOHIDDeviceRef dev = device_array[i]; | ||
| struct hid_device_info *tmp; | ||
| size_t len; | ||
| io_object_t iokit_dev; | ||
| kern_return_t res; | ||
| io_string_t path; | ||
@@ -476,5 +462,11 @@ /* VID/PID match. Create the record. */ | ||
| cur_dev->next = NULL; | ||
| len = make_path(dev, cbuf, sizeof(cbuf)); | ||
| cur_dev->path = strdup(cbuf); | ||
| /* Fill in the path (IOService plane) */ | ||
| iokit_dev = hidapi_IOHIDDeviceGetService(dev); | ||
| res = IORegistryEntryGetPath(iokit_dev, kIOServicePlane, path); | ||
| if (res == KERN_SUCCESS) | ||
| cur_dev->path = strdup(path); | ||
| else | ||
| cur_dev->path = strdup(""); | ||
| /* Serial Number */ | ||
@@ -620,3 +612,3 @@ get_serial_number(dev, buf, BUF_LEN); | ||
| /* This gets called when the read_thred's run loop gets signaled by | ||
| /* This gets called when the read_thread's run loop gets signaled by | ||
| hid_close(), and serves to stop the read_thread's run loop. */ | ||
@@ -679,3 +671,3 @@ static void perform_signal_callback(void *context) | ||
| make sure that a thread which is about to go to sleep waiting on | ||
| the condition acutally will go to sleep before the condition is | ||
| the condition actually will go to sleep before the condition is | ||
| signaled. */ | ||
@@ -694,7 +686,11 @@ pthread_mutex_lock(&dev->mutex); | ||
| /* hid_open_path() | ||
| * | ||
| * path must be a valid path to an IOHIDDevice in the IOService plane | ||
| * Example: "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/EHC1@1D,7/AppleUSBEHCI/PLAYSTATION(R)3 Controller@fd120000/IOUSBInterface@0/IOUSBHIDDriver" | ||
| */ | ||
| hid_device * HID_API_EXPORT hid_open_path(const char *path) | ||
| { | ||
| int i; | ||
| hid_device *dev = NULL; | ||
| CFIndex num_devices; | ||
| io_registry_entry_t entry = MACH_PORT_NULL; | ||
@@ -707,60 +703,57 @@ dev = new_hid_device(); | ||
| /* give the IOHIDManager a chance to update itself */ | ||
| process_pending_events(); | ||
| /* Get the IORegistry entry for the given path */ | ||
| entry = IORegistryEntryFromPath(kIOMasterPortDefault, path); | ||
| if (entry == MACH_PORT_NULL) { | ||
| /* Path wasn't valid (maybe device was removed?) */ | ||
| goto return_error; | ||
| } | ||
| CFSetRef device_set = IOHIDManagerCopyDevices(hid_mgr); | ||
| /* Create an IOHIDDevice for the entry */ | ||
| dev->device_handle = IOHIDDeviceCreate(kCFAllocatorDefault, entry); | ||
| if (dev->device_handle == NULL) { | ||
| /* Error creating the HID device */ | ||
| goto return_error; | ||
| } | ||
| num_devices = CFSetGetCount(device_set); | ||
| IOHIDDeviceRef *device_array = calloc(num_devices, sizeof(IOHIDDeviceRef)); | ||
| CFSetGetValues(device_set, (const void **) device_array); | ||
| for (i = 0; i < num_devices; i++) { | ||
| char cbuf[BUF_LEN]; | ||
| size_t len; | ||
| IOHIDDeviceRef os_dev = device_array[i]; | ||
| /* Open the IOHIDDevice */ | ||
| IOReturn ret = IOHIDDeviceOpen(dev->device_handle, kIOHIDOptionsTypeSeizeDevice); | ||
| if (ret == kIOReturnSuccess) { | ||
| char str[32]; | ||
| len = make_path(os_dev, cbuf, sizeof(cbuf)); | ||
| if (!strcmp(cbuf, path)) { | ||
| /* Matched Paths. Open this Device. */ | ||
| IOReturn ret = IOHIDDeviceOpen(os_dev, kIOHIDOptionsTypeSeizeDevice); | ||
| if (ret == kIOReturnSuccess) { | ||
| char str[32]; | ||
| /* Create the buffers for receiving data */ | ||
| dev->max_input_report_len = (CFIndex) get_max_report_length(dev->device_handle); | ||
| dev->input_report_buf = calloc(dev->max_input_report_len, sizeof(uint8_t)); | ||
| free(device_array); | ||
| CFRetain(os_dev); | ||
| CFRelease(device_set); | ||
| dev->device_handle = os_dev; | ||
| /* Create the Run Loop Mode for this device. | ||
| printing the reference seems to work. */ | ||
| sprintf(str, "HIDAPI_%p", dev->device_handle); | ||
| dev->run_loop_mode = | ||
| CFStringCreateWithCString(NULL, str, kCFStringEncodingASCII); | ||
| /* Create the buffers for receiving data */ | ||
| dev->max_input_report_len = (CFIndex) get_max_report_length(os_dev); | ||
| dev->input_report_buf = calloc(dev->max_input_report_len, sizeof(uint8_t)); | ||
| /* Attach the device to a Run Loop */ | ||
| IOHIDDeviceRegisterInputReportCallback( | ||
| dev->device_handle, dev->input_report_buf, dev->max_input_report_len, | ||
| &hid_report_callback, dev); | ||
| IOHIDDeviceRegisterRemovalCallback(dev->device_handle, hid_device_removal_callback, dev); | ||
| /* Create the Run Loop Mode for this device. | ||
| printing the reference seems to work. */ | ||
| sprintf(str, "HIDAPI_%p", os_dev); | ||
| dev->run_loop_mode = | ||
| CFStringCreateWithCString(NULL, str, kCFStringEncodingASCII); | ||
| /* Start the read thread */ | ||
| pthread_create(&dev->thread, NULL, read_thread, dev); | ||
| /* Attach the device to a Run Loop */ | ||
| IOHIDDeviceRegisterInputReportCallback( | ||
| os_dev, dev->input_report_buf, dev->max_input_report_len, | ||
| &hid_report_callback, dev); | ||
| IOHIDDeviceRegisterRemovalCallback(dev->device_handle, hid_device_removal_callback, dev); | ||
| /* Wait here for the read thread to be initialized. */ | ||
| pthread_barrier_wait(&dev->barrier); | ||
| /* Start the read thread */ | ||
| pthread_create(&dev->thread, NULL, read_thread, dev); | ||
| IOObjectRelease(entry); | ||
| return dev; | ||
| } | ||
| else { | ||
| goto return_error; | ||
| } | ||
| /* Wait here for the read thread to be initialized. */ | ||
| pthread_barrier_wait(&dev->barrier); | ||
| return_error: | ||
| if (dev->device_handle != NULL) | ||
| CFRelease(dev->device_handle); | ||
| return dev; | ||
| } | ||
| else { | ||
| goto return_error; | ||
| } | ||
| } | ||
| } | ||
| if (entry != MACH_PORT_NULL) | ||
| IOObjectRelease(entry); | ||
| return_error: | ||
| free(device_array); | ||
| CFRelease(device_set); | ||
| free_hid_device(dev); | ||
@@ -1064,2 +1057,7 @@ return NULL; | ||
| #if 0 | ||
| static int32_t get_location_id(IOHIDDeviceRef device) | ||
| { | ||
| return get_int_property(device, CFSTR(kIOHIDLocationIDKey)); | ||
| } | ||
| static int32_t get_usage(IOHIDDeviceRef device) | ||
@@ -1066,0 +1064,0 @@ { |
| *.pc |
@@ -0,0 +0,0 @@ prefix=@prefix@ |
@@ -0,0 +0,0 @@ prefix=@prefix@ |
@@ -0,0 +0,0 @@ prefix=@prefix@ |
@@ -32,3 +32,3 @@ HIDAPI library for Windows, Linux, FreeBSD and Mac OS X | ||
| Linux/FreeBSD/libusb (libusb/hid-libusb.c): | ||
| Linux/FreeBSD/libusb (libusb/hid.c): | ||
| This back-end uses libusb-1.0 to communicate directly to a USB device. This | ||
@@ -35,0 +35,0 @@ back-end will of course not work with Bluetooth devices. |
@@ -0,0 +0,0 @@ Debug |
@@ -0,0 +0,0 @@ #!/bin/bash |
@@ -0,0 +0,0 @@ /******************************* |
@@ -0,0 +0,0 @@ /******************************* |
@@ -0,0 +0,0 @@ /******************************* |
| #!/bin/bash | ||
| xterm -e /Users/alan/work/hidapi/testgui/TestGUI.app/Contents/MacOS/tg |
@@ -0,0 +0,0 @@ /******************************************************* |
@@ -0,0 +0,0 @@ <?xml version="1.0" encoding="UTF-8"?> |
@@ -0,0 +0,0 @@ Debug |
| *.log | ||
| obj*_*_* |
@@ -0,0 +0,0 @@ ############################################################################# |
@@ -39,2 +39,6 @@ /******************************************************* | ||
| /* The maximum number of characters that can be passed into the | ||
| HidD_Get*String() functions without it failing.*/ | ||
| #define MAX_STRING_WCHARS 0xFFF | ||
| /*#define HIDAPI_USE_DDK*/ | ||
@@ -66,2 +70,5 @@ | ||
| #undef MIN | ||
| #define MIN(x,y) ((x) < (y)? (x): (y)) | ||
| #ifdef _MSC_VER | ||
@@ -153,3 +160,3 @@ /* Thanks Microsoft, but I know how to use strncpy(). */ | ||
| memset(&dev->ol, 0, sizeof(dev->ol)); | ||
| dev->ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*inital state f=nonsignaled*/, NULL); | ||
| dev->ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*initial state f=nonsignaled*/, NULL); | ||
@@ -817,3 +824,3 @@ return dev; | ||
| res = HidD_GetManufacturerString(dev->device_handle, string, sizeof(wchar_t) * maxlen); | ||
| res = HidD_GetManufacturerString(dev->device_handle, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); | ||
| if (!res) { | ||
@@ -831,3 +838,3 @@ register_error(dev, "HidD_GetManufacturerString"); | ||
| res = HidD_GetProductString(dev->device_handle, string, sizeof(wchar_t) * maxlen); | ||
| res = HidD_GetProductString(dev->device_handle, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); | ||
| if (!res) { | ||
@@ -845,3 +852,3 @@ register_error(dev, "HidD_GetProductString"); | ||
| res = HidD_GetSerialNumberString(dev->device_handle, string, sizeof(wchar_t) * maxlen); | ||
| res = HidD_GetSerialNumberString(dev->device_handle, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); | ||
| if (!res) { | ||
@@ -859,3 +866,3 @@ register_error(dev, "HidD_GetSerialNumberString"); | ||
| res = HidD_GetIndexedString(dev->device_handle, string_index, string, sizeof(wchar_t) * maxlen); | ||
| res = HidD_GetIndexedString(dev->device_handle, string_index, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); | ||
| if (!res) { | ||
@@ -862,0 +869,0 @@ register_error(dev, "HidD_GetIndexedString"); |
+27
-21
@@ -8,3 +8,2 @@ /**************************************************************************** | ||
| //! Opens a KIT MT 8057 CO2 detector and reads data from it. This | ||
@@ -35,3 +34,3 @@ //! example will not work unless such an HID is plugged in to your system. | ||
| fn decode_temperature(value: u16) -> f32 { | ||
| (value as f32)*0.0625 - 273.15 | ||
| (value as f32) * 0.0625 - 273.15 | ||
| } | ||
@@ -48,3 +47,3 @@ | ||
| (buf[6] << 5) | (buf[5] >> 3), | ||
| (buf[5] << 5) | (buf[3] >> 3) | ||
| (buf[5] << 5) | (buf[3] >> 3), | ||
| ]; | ||
@@ -54,3 +53,3 @@ | ||
| for i in 0..PACKET_SIZE { | ||
| let sub_val:u8 = (magic_word[i] << 4) | (magic_word[i] >> 4); | ||
| let sub_val: u8 = (magic_word[i] << 4) | (magic_word[i] >> 4); | ||
| res[i] = u8::overflowing_sub(res[i], sub_val).0; | ||
@@ -62,4 +61,3 @@ } | ||
| } | ||
| let checksum = u8::overflowing_add( | ||
| u8::overflowing_add(res[0], res[1]).0, res[2]).0; | ||
| let checksum = u8::overflowing_add(u8::overflowing_add(res[0], res[1]).0, res[2]).0; | ||
| if checksum != res[3] { | ||
@@ -74,8 +72,7 @@ return CO2Result::Error("Checksum error"); | ||
| if val > 3000 { | ||
| CO2Result::Error( | ||
| "Concentration bigger than 3000 (uninitialized device?)") | ||
| CO2Result::Error("Concentration bigger than 3000 (uninitialized device?)") | ||
| } else { | ||
| CO2Result::Concentration(val) | ||
| } | ||
| }, | ||
| } | ||
| _ => CO2Result::Unknown(res[0], val), | ||
@@ -92,3 +89,3 @@ } | ||
| sleep(Duration::from_secs(RETRY_SEC)); | ||
| }, | ||
| } | ||
| } | ||
@@ -103,10 +100,20 @@ } | ||
| dev.send_feature_report(&[0; PACKET_SIZE]).expect("Feature report failed"); | ||
| dev.send_feature_report(&[0; PACKET_SIZE]) | ||
| .expect("Feature report failed"); | ||
| println!("Manufacurer:\t{}", dev.get_manufacturer_string() | ||
| .expect("Failed to read manufacurer string")); | ||
| println!("Product:\t{}", dev.get_product_string() | ||
| .expect("Failed to read product string")); | ||
| println!("Serial number:\t{}", dev.get_serial_number_string() | ||
| .expect("Failed to read serial number")); | ||
| println!( | ||
| "Manufacurer:\t{:?}", | ||
| dev.get_manufacturer_string() | ||
| .expect("Failed to read manufacurer string") | ||
| ); | ||
| println!( | ||
| "Product:\t{:?}", | ||
| dev.get_product_string() | ||
| .expect("Failed to read product string") | ||
| ); | ||
| println!( | ||
| "Serial number:\t{:?}", | ||
| dev.get_serial_number_string() | ||
| .expect("Failed to read serial number") | ||
| ); | ||
@@ -118,6 +125,5 @@ loop { | ||
| Ok(res) => { | ||
| println!("Error: unexpected length of data: {}/{}", | ||
| res, PACKET_SIZE); | ||
| println!("Error: unexpected length of data: {}/{}", res, PACKET_SIZE); | ||
| continue; | ||
| }, | ||
| } | ||
| Err(err) => { | ||
@@ -136,5 +142,5 @@ println!("Error: {:}", err); | ||
| sleep(Duration::from_secs(RETRY_SEC)); | ||
| }, | ||
| } | ||
| } | ||
| } | ||
| } |
+10
-6
@@ -7,3 +7,2 @@ /**************************************************************************** | ||
| //! Prints out a list of HID devices | ||
@@ -16,9 +15,14 @@ | ||
| fn main() { | ||
| println!("Printing all available hid devices."); | ||
| println!("Printing all available hid devices:"); | ||
| let api = HidApi::new().unwrap(); | ||
| for device in &api.devices() { | ||
| println!("{:#?}", device); | ||
| match HidApi::new() { | ||
| Ok(api) => { | ||
| for device in api.devices() { | ||
| println!("{:#?}", device); | ||
| } | ||
| } | ||
| Err(e) => { | ||
| eprintln!("Error: {}", e); | ||
| } | ||
| } | ||
| } |
@@ -7,3 +7,2 @@ /**************************************************************************** | ||
| //! Opens a Thrustmaster T-Flight HOTAS X HID and reads data from it. This | ||
@@ -18,3 +17,2 @@ //! example will not work unless such an HID is plugged in to your system. | ||
| fn main() { | ||
| let api = HidApi::new().expect("Failed to create API instance"); | ||
@@ -34,6 +32,4 @@ | ||
| println!("{}", data_string); | ||
| } | ||
| } |
+2
-6
@@ -1,6 +0,6 @@ | ||
| # hidapi [](https://travis-ci.org/Osspial/hidapi-rs) [](https://crates.io/crates/hidapi) [](https://github.com/Osspial/hidapi-rs/blob/master/LICENSE.txt) [](https://docs.rs/hidapi) | ||
| # hidapi [](https://travis-ci.org/ruabmbua/hidapi-rs) [](https://crates.io/crates/hidapi) [](https://github.com/Osspial/hidapi-rs/blob/master/LICENSE.txt) [](https://docs.rs/hidapi) | ||
| This crate provides a rust abstraction over the features of the C library | ||
| [hidapi](https://github.com/signal11/hidapi) by signal11. Based off of | ||
| [hidapi_rust](https://github.com/ruabmbua/hidapi_rust) by ruabmbua. | ||
| [hidapi-rs](https://github.com/Osspial/hidapi-rs) by Osspial. | ||
@@ -12,6 +12,2 @@ # Usage | ||
| ```toml | ||
| [dependencies] | ||
| hidapi = "0.3" | ||
| ``` | ||
| # Example | ||
@@ -18,0 +14,0 @@ |
+43
-37
@@ -6,7 +6,5 @@ /// ************************************************************************** | ||
| /// ************************************************************************* | ||
| // For documentation look at the corresponding C header file hidapi.h | ||
| use libc::{c_char, c_int, c_uchar, c_ushort, c_void, size_t, wchar_t}; | ||
| use libc::{c_void, c_ushort, wchar_t, c_int, c_uchar, size_t, c_char}; | ||
| pub type HidDevice = c_void; | ||
@@ -35,42 +33,50 @@ | ||
| pub fn hid_free_enumeration(hid_device_info: *mut HidDeviceInfo); | ||
| pub fn hid_open(vendor_id: c_ushort, | ||
| product_id: c_ushort, | ||
| serial_number: *const wchar_t) | ||
| -> *mut HidDevice; | ||
| pub fn hid_open( | ||
| vendor_id: c_ushort, | ||
| product_id: c_ushort, | ||
| serial_number: *const wchar_t, | ||
| ) -> *mut HidDevice; | ||
| pub fn hid_open_path(path: *const c_char) -> *mut HidDevice; | ||
| pub fn hid_write(device: *mut HidDevice, data: *const c_uchar, length: size_t) -> c_int; | ||
| pub fn hid_read_timeout(device: *mut HidDevice, | ||
| data: *mut c_uchar, | ||
| length: size_t, | ||
| milleseconds: c_int) | ||
| -> c_int; | ||
| pub fn hid_read_timeout( | ||
| device: *mut HidDevice, | ||
| data: *mut c_uchar, | ||
| length: size_t, | ||
| milleseconds: c_int, | ||
| ) -> c_int; | ||
| pub fn hid_read(device: *mut HidDevice, data: *mut c_uchar, length: size_t) -> c_int; | ||
| pub fn hid_set_nonblocking(device: *mut HidDevice, nonblock: c_int) -> c_int; | ||
| pub fn hid_send_feature_report(device: *mut HidDevice, | ||
| data: *const c_uchar, | ||
| length: size_t) | ||
| -> c_int; | ||
| pub fn hid_get_feature_report(device: *mut HidDevice, | ||
| data: *mut c_uchar, | ||
| length: size_t) | ||
| -> c_int; | ||
| pub fn hid_send_feature_report( | ||
| device: *mut HidDevice, | ||
| data: *const c_uchar, | ||
| length: size_t, | ||
| ) -> c_int; | ||
| pub fn hid_get_feature_report( | ||
| device: *mut HidDevice, | ||
| data: *mut c_uchar, | ||
| length: size_t, | ||
| ) -> c_int; | ||
| pub fn hid_close(device: *mut HidDevice); | ||
| pub fn hid_get_manufacturer_string(device: *mut HidDevice, | ||
| string: *mut wchar_t, | ||
| maxlen: size_t) | ||
| -> c_int; | ||
| pub fn hid_get_product_string(device: *mut HidDevice, | ||
| string: *mut wchar_t, | ||
| maxlen: size_t) | ||
| -> c_int; | ||
| pub fn hid_get_serial_number_string(device: *mut HidDevice, | ||
| string: *mut wchar_t, | ||
| maxlen: size_t) | ||
| -> c_int; | ||
| pub fn hid_get_indexed_string(device: *mut HidDevice, | ||
| string_index: c_int, | ||
| string: *mut wchar_t, | ||
| maxlen: size_t) | ||
| -> c_int; | ||
| pub fn hid_get_manufacturer_string( | ||
| device: *mut HidDevice, | ||
| string: *mut wchar_t, | ||
| maxlen: size_t, | ||
| ) -> c_int; | ||
| pub fn hid_get_product_string( | ||
| device: *mut HidDevice, | ||
| string: *mut wchar_t, | ||
| maxlen: size_t, | ||
| ) -> c_int; | ||
| pub fn hid_get_serial_number_string( | ||
| device: *mut HidDevice, | ||
| string: *mut wchar_t, | ||
| maxlen: size_t, | ||
| ) -> c_int; | ||
| pub fn hid_get_indexed_string( | ||
| device: *mut HidDevice, | ||
| string_index: c_int, | ||
| string: *mut wchar_t, | ||
| maxlen: size_t, | ||
| ) -> c_int; | ||
| pub fn hid_error(device: *mut HidDevice) -> *const wchar_t; | ||
| } |
+209
-137
@@ -15,6 +15,2 @@ // ************************************************************************** | ||
| //! | ||
| //! ```toml | ||
| //! [dependencies] | ||
| //! hidapi = "0.4" | ||
| //! ``` | ||
| //! # Example | ||
@@ -25,34 +21,69 @@ //! | ||
| //! | ||
| //! let api = hidapi::HidApi::new().unwrap(); | ||
| //! // Print out information about all connected devices | ||
| //! for device in &api.devices() { | ||
| //! println!("{:#?}", device); | ||
| //! } | ||
| //! use hidapi::HidApi; | ||
| //! | ||
| //! // Connect to device using its VID and PID | ||
| //! let (VID, PID) = (0x0123, 0x3456); | ||
| //! let device = api.open(VID, PID).unwrap(); | ||
| //! fn main() { | ||
| //! println!("Printing all available hid devices:"); | ||
| //! | ||
| //! // Read data from device | ||
| //! let mut buf = [0u8; 8]; | ||
| //! let res = device.read(&mut buf[..]).unwrap(); | ||
| //! println!("Read: {:?}", &buf[..res]); | ||
| //! | ||
| //! // Write data to device | ||
| //! let buf = [0u8, 1, 2, 3, 4]; | ||
| //! let res = device.write(&buf).unwrap(); | ||
| //! println!("Wrote: {:?} byte(s)", res); | ||
| //! match HidApi::new() { | ||
| //! Ok(api) => { | ||
| //! for device in api.devices() { | ||
| //! println!("{:#?}", device); | ||
| //! } | ||
| //! }, | ||
| //! Err(e) => { | ||
| //! eprintln!("Error: {}", e); | ||
| //! }, | ||
| //! } | ||
| //! } | ||
| //! ``` | ||
| #[macro_use] | ||
| extern crate failure_derive; | ||
| extern crate failure; | ||
| extern crate libc; | ||
| mod error; | ||
| mod ffi; | ||
| use failure::Error; | ||
| use libc::{c_int, size_t, wchar_t}; | ||
| use std::ffi::CStr; | ||
| use std::marker::PhantomData; | ||
| use libc::{wchar_t, size_t, c_int}; | ||
| use std::ffi::CString; | ||
| use std::mem::ManuallyDrop; | ||
| use std::rc::Rc; | ||
| pub type HidError = &'static str; | ||
| pub use error::HidError; | ||
| pub type HidResult<T> = Result<T, HidError>; | ||
| const STRING_BUF_LEN: usize = 128; | ||
| /// Hidapi context and device member, which ensures deinitialization | ||
| /// of the C library happens, when, and only when all devices and the api instance is dropped. | ||
| struct HidApiLock; | ||
| impl HidApiLock { | ||
| fn acquire() -> HidResult<HidApiLock> { | ||
| if unsafe { !HID_API_LOCK } { | ||
| // Initialize the HID and prevent other HIDs from being created | ||
| unsafe { | ||
| if ffi::hid_init() == -1 { | ||
| return Err(HidError::InitializationError); | ||
| } | ||
| HID_API_LOCK = true; | ||
| Ok(HidApiLock) | ||
| } | ||
| } else { | ||
| Err(HidError::InitializationError) | ||
| } | ||
| } | ||
| } | ||
| impl Drop for HidApiLock { | ||
| fn drop(&mut self) { | ||
| unsafe { | ||
| ffi::hid_exit(); | ||
| HID_API_LOCK = false; | ||
| } | ||
| } | ||
| } | ||
| /// Object for handling hidapi context and implementing RAII for it. | ||
@@ -62,2 +93,3 @@ /// Only one instance can exist at a time. | ||
| devices: Vec<HidDeviceInfo>, | ||
| _lock: Rc<HidApiLock>, | ||
| } | ||
@@ -68,20 +100,12 @@ | ||
| impl HidApi { | ||
| /// Initializes the HID | ||
| /// Initializes the hidapi. | ||
| /// | ||
| /// Will also initialize the currently available device list. | ||
| pub fn new() -> HidResult<Self> { | ||
| if unsafe { !HID_API_LOCK } { | ||
| let lock = HidApiLock::acquire()?; | ||
| // Initialize the HID and prevent other HIDs from being created | ||
| unsafe { | ||
| if ffi::hid_init() == -1 { | ||
| return Err("Failed to init hid"); | ||
| } | ||
| HID_API_LOCK = true; | ||
| } | ||
| Ok(HidApi { devices: unsafe { HidApi::get_hid_device_info_vector() } }) | ||
| } else { | ||
| Err("HidApi already in use") | ||
| } | ||
| Ok(HidApi { | ||
| devices: unsafe { HidApi::get_hid_device_info_vector()? }, | ||
| _lock: Rc::new(lock), | ||
| }) | ||
| } | ||
@@ -91,7 +115,8 @@ | ||
| /// `devices()` method) | ||
| pub fn refresh_devices(&mut self) { | ||
| self.devices = unsafe { HidApi::get_hid_device_info_vector() }; | ||
| pub fn refresh_devices(&mut self) -> HidResult<()> { | ||
| self.devices = unsafe { HidApi::get_hid_device_info_vector()? }; | ||
| Ok(()) | ||
| } | ||
| unsafe fn get_hid_device_info_vector() -> Vec<HidDeviceInfo> { | ||
| unsafe fn get_hid_device_info_vector() -> HidResult<Vec<HidDeviceInfo>> { | ||
| let mut device_vector = Vec::with_capacity(8); | ||
@@ -104,6 +129,5 @@ | ||
| while !current_device.is_null() { | ||
| device_vector.push(conv_hid_device_info(current_device)); | ||
| device_vector.push(conv_hid_device_info(current_device)?); | ||
| current_device = (*current_device).next; | ||
| } | ||
| } | ||
@@ -115,11 +139,15 @@ | ||
| device_vector | ||
| Ok(device_vector) | ||
| } | ||
| /// Returns list of objects containing information about connected devices | ||
| pub fn devices(&self) -> Vec<HidDeviceInfo> { | ||
| self.devices.clone() | ||
| pub fn devices(&self) -> &Vec<HidDeviceInfo> { | ||
| &self.devices | ||
| } | ||
| /// Open a HID device using a Vendor ID (VID) and Product ID (PID) | ||
| /// Open a HID device using a Vendor ID (VID) and Product ID (PID). | ||
| /// | ||
| /// When multiple devices with the same vid and pid are available, then the | ||
| /// first one found in the internal device list will be used. There are however | ||
| /// no grantees, which device this will be. | ||
| pub fn open(&self, vid: u16, pid: u16) -> HidResult<HidDevice> { | ||
@@ -129,7 +157,7 @@ let device = unsafe { ffi::hid_open(vid, pid, std::ptr::null()) }; | ||
| if device.is_null() { | ||
| Err("Unable to open hid device") | ||
| Err(HidError::OpenHidDeviceError) | ||
| } else { | ||
| Ok(HidDevice { | ||
| _hid_device: device, | ||
| phantom: PhantomData, | ||
| _lock: ManuallyDrop::new(self._lock.clone()), | ||
| }) | ||
@@ -142,9 +170,11 @@ } | ||
| pub fn open_serial(&self, vid: u16, pid: u16, sn: &str) -> HidResult<HidDevice> { | ||
| let device = unsafe { ffi::hid_open(vid, pid, std::mem::transmute(sn.as_ptr())) }; | ||
| let mut chars = sn.chars().map(|c| c as wchar_t).collect::<Vec<_>>(); | ||
| chars.push(0 as wchar_t); | ||
| let device = unsafe { ffi::hid_open(vid, pid, chars.as_ptr()) }; | ||
| if device.is_null() { | ||
| Err("Unable to open hid device") | ||
| Err(HidError::OpenHidDeviceError) | ||
| } else { | ||
| Ok(HidDevice { | ||
| _hid_device: device, | ||
| phantom: PhantomData, | ||
| _lock: ManuallyDrop::new(self._lock.clone()), | ||
| }) | ||
@@ -154,13 +184,14 @@ } | ||
| /// The path name be determined by calling hid_enumerate(), or a | ||
| /// platform-specific path name can be used (eg: /dev/hidraw0 on Linux). | ||
| pub fn open_path(&self, device_path: &str) -> HidResult<HidDevice> { | ||
| let device = unsafe { ffi::hid_open_path(std::mem::transmute(device_path.as_ptr())) }; | ||
| /// The path name be determined by inspecting the device list available with [HidApi::devices()](struct.HidApi.html#method.devices) | ||
| /// | ||
| /// Alternatively a platform-specific path name can be used (eg: /dev/hidraw0 on Linux). | ||
| pub fn open_path(&self, device_path: &CStr) -> HidResult<HidDevice> { | ||
| let device = unsafe { ffi::hid_open_path(device_path.as_ptr()) }; | ||
| if device.is_null() { | ||
| Err("Unable to open hid device") | ||
| Err(HidError::OpenHidDeviceError) | ||
| } else { | ||
| Ok(HidDevice { | ||
| _hid_device: device, | ||
| phantom: PhantomData, | ||
| _lock: ManuallyDrop::new(self._lock.clone()), | ||
| }) | ||
@@ -171,15 +202,6 @@ } | ||
| impl Drop for HidApi { | ||
| fn drop(&mut self) { | ||
| unsafe { | ||
| ffi::hid_exit(); | ||
| HID_API_LOCK = false; | ||
| } | ||
| } | ||
| } | ||
| /// Converts a pointer to a `wchar_t` to a string | ||
| unsafe fn wchar_to_string(wstr: *const wchar_t) -> HidResult<String> { | ||
| /// Converts a pointer to a `wchar_t` to a optional string | ||
| unsafe fn wchar_to_string(wstr: *const wchar_t) -> HidResult<Option<String>> { | ||
| if wstr.is_null() { | ||
| return Err("Null pointer!"); | ||
| return Ok(None); | ||
| } | ||
@@ -190,7 +212,11 @@ | ||
| while *wstr.offset(index) != 0 { | ||
| let o = |i| *wstr.offset(i); | ||
| while o(index) != 0 { | ||
| use std::char; | ||
| char_vector.push(match char::from_u32(*wstr.offset(index) as u32) { | ||
| char_vector.push(match char::from_u32(o(index) as u32) { | ||
| Some(ch) => ch, | ||
| None => return Err("Unable to add next char"), | ||
| None => Err(HidError::FromWideCharError { | ||
| wide_char: o(index), | ||
| })?, | ||
| }); | ||
@@ -201,19 +227,19 @@ | ||
| Ok(char_vector.into_iter().collect()) | ||
| Ok(Some(char_vector.into_iter().collect())) | ||
| } | ||
| /// Convert the CFFI `HidDeviceInfo` struct to a native `HidDeviceInfo` struct | ||
| unsafe fn conv_hid_device_info(src: *mut ffi::HidDeviceInfo) -> HidDeviceInfo { | ||
| HidDeviceInfo { | ||
| path: std::str::from_utf8(CStr::from_ptr((*src).path).to_bytes()).unwrap().to_owned(), | ||
| unsafe fn conv_hid_device_info(src: *mut ffi::HidDeviceInfo) -> HidResult<HidDeviceInfo> { | ||
| Ok(HidDeviceInfo { | ||
| path: CStr::from_ptr((*src).path).to_owned(), | ||
| vendor_id: (*src).vendor_id, | ||
| product_id: (*src).product_id, | ||
| serial_number: wchar_to_string((*src).serial_number).ok(), | ||
| serial_number: wchar_to_string((*src).serial_number)?, | ||
| release_number: (*src).release_number, | ||
| manufacturer_string: wchar_to_string((*src).manufacturer_string).ok(), | ||
| product_string: wchar_to_string((*src).product_string).ok(), | ||
| manufacturer_string: wchar_to_string((*src).manufacturer_string)?, | ||
| product_string: wchar_to_string((*src).product_string)?, | ||
| usage_page: (*src).usage_page, | ||
| usage: (*src).usage, | ||
| interface_number: (*src).interface_number, | ||
| } | ||
| }) | ||
| } | ||
@@ -224,3 +250,3 @@ | ||
| pub struct HidDeviceInfo { | ||
| pub path: String, | ||
| pub path: CString, | ||
| pub vendor_id: u16, | ||
@@ -237,16 +263,42 @@ pub product_id: u16, | ||
| impl HidDeviceInfo { | ||
| /// Use the information contained in `HidDeviceInfo` to open | ||
| /// and return a handle to a [HidDevice](struct.HidDevice.html). | ||
| /// | ||
| /// By default the device path is used to open the device. | ||
| /// When no path is available, then vid, pid and serial number are used. | ||
| /// If both path and serial number are not available, then this function will | ||
| /// fail with [HidError::OpenHidDeviceWithDeviceInfoError](enum.HidError.html#variant.OpenHidDeviceWithDeviceInfoError). | ||
| /// | ||
| /// Note, that opening a device could still be done using [HidApi::open()](struct.HidApi.html#method.open) directly. | ||
| pub fn open_device(&self, hidapi: &HidApi) -> HidResult<HidDevice> { | ||
| if self.path.as_bytes().len() != 0 { | ||
| hidapi.open_path(self.path.as_c_str()) | ||
| } else if let Some(ref sn) = self.serial_number { | ||
| hidapi.open_serial(self.vendor_id, self.product_id, sn) | ||
| } else { | ||
| Err(HidError::OpenHidDeviceWithDeviceInfoError { | ||
| device_info: self.clone(), | ||
| }) | ||
| } | ||
| } | ||
| } | ||
| /// Object for accessing HID device | ||
| pub struct HidDevice<'a> { | ||
| pub struct HidDevice { | ||
| _hid_device: *mut ffi::HidDevice, | ||
| /// Prevents this from outliving the api instance that created it | ||
| phantom: PhantomData<&'a ()>, | ||
| _lock: ManuallyDrop<Rc<HidApiLock>>, | ||
| } | ||
| impl<'a> Drop for HidDevice<'a> { | ||
| impl Drop for HidDevice { | ||
| fn drop(&mut self) { | ||
| unsafe { ffi::hid_close(self._hid_device) }; | ||
| unsafe { | ||
| ffi::hid_close(self._hid_device); | ||
| ManuallyDrop::drop(&mut self._lock); | ||
| }; | ||
| } | ||
| } | ||
| impl<'a> HidDevice<'a> { | ||
| impl HidDevice { | ||
| /// Check size returned by other methods, if it's equal to -1 check for | ||
@@ -257,14 +309,4 @@ /// error and return Error, otherwise return size as unsigned number | ||
| match self.check_error() { | ||
| Ok(err) => { | ||
| if err.is_empty() { | ||
| Err("Undetected error") | ||
| } else { | ||
| println!("{:?}", err); | ||
| Err("Detected error") | ||
| } | ||
| } | ||
| Err(_) => { | ||
| // Err(err) | ||
| Err("Failed to retrieve error message") | ||
| } | ||
| Ok(err) => Err(err), | ||
| Err(e) => Err(e), | ||
| } | ||
@@ -276,8 +318,20 @@ } else { | ||
| /// Get a string describing the last error which occurred. | ||
| pub fn check_error(&self) -> HidResult<String> { | ||
| unsafe { wchar_to_string(ffi::hid_error(self._hid_device)) } | ||
| /// Get the last error, which happened in the underlying hidapi C library. | ||
| /// | ||
| /// The `Ok()` variant of the result will contain a [HidError::HidApiError](enum.HidError.html). | ||
| /// | ||
| /// When `Err()` is returned, then acquiring the error string from the hidapi C | ||
| /// library failed. The contained [HidError](enum.HidError.html) is the cause, why no error could | ||
| /// be fetched. | ||
| pub fn check_error(&self) -> HidResult<HidError> { | ||
| Ok(HidError::HidApiError { | ||
| message: unsafe { | ||
| wchar_to_string(ffi::hid_error(self._hid_device)) | ||
| .map_err(|e| HidError::HidApiErrorEmptyWithCause { | ||
| cause: Error::from(e).compat(), | ||
| })?.ok_or(HidError::HidApiErrorEmpty)? | ||
| }, | ||
| }) | ||
| } | ||
| /// The first byte of `data` must contain the Report ID. For | ||
@@ -297,3 +351,3 @@ /// devices which only support a single report, this must be set | ||
| if data.len() == 0 { | ||
| return Err("Data must contain at least one byte"); | ||
| return Err(HidError::InvalidZeroSizeData); | ||
| } | ||
@@ -318,6 +372,8 @@ let res = unsafe { ffi::hid_write(self._hid_device, data.as_ptr(), data.len() as size_t) }; | ||
| let res = unsafe { | ||
| ffi::hid_read_timeout(self._hid_device, | ||
| buf.as_mut_ptr(), | ||
| buf.len() as size_t, | ||
| timeout) | ||
| ffi::hid_read_timeout( | ||
| self._hid_device, | ||
| buf.as_mut_ptr(), | ||
| buf.len() as size_t, | ||
| timeout, | ||
| ) | ||
| }; | ||
@@ -340,3 +396,3 @@ self.check_size(res) | ||
| if data.len() == 0 { | ||
| return Err("Data must contain at least one byte"); | ||
| return Err(HidError::InvalidZeroSizeData); | ||
| } | ||
@@ -346,5 +402,8 @@ let res = unsafe { | ||
| }; | ||
| let res = try!(self.check_size(res)); | ||
| let res = self.check_size(res)?; | ||
| if res != data.len() { | ||
| Err("Failed to send feature report completely") | ||
| Err(HidError::IncompleteSendError { | ||
| sent: res, | ||
| all: data.len(), | ||
| }) | ||
| } else { | ||
@@ -355,5 +414,5 @@ Ok(()) | ||
| /// Set the first byte of `data` to the 'Report ID' of the report to be read. | ||
| /// Set the first byte of `buf` to the 'Report ID' of the report to be read. | ||
| /// Upon return, the first byte will still contain the Report ID, and the | ||
| /// report data will start in data[1]. | ||
| /// report data will start in buf[1]. | ||
| pub fn get_feature_report(&self, buf: &mut [u8]) -> HidResult<usize> { | ||
@@ -376,3 +435,8 @@ let res = unsafe { | ||
| if res == -1 { | ||
| Err("Failed to set blocking mode") | ||
| Err(HidError::SetBlockingModeError { | ||
| mode: match blocking { | ||
| true => "blocking", | ||
| false => "not blocking", | ||
| }, | ||
| }) | ||
| } else { | ||
@@ -384,10 +448,12 @@ Ok(()) | ||
| /// Get The Manufacturer String from a HID device. | ||
| pub fn get_manufacturer_string(&self) -> HidResult<String> { | ||
| pub fn get_manufacturer_string(&self) -> HidResult<Option<String>> { | ||
| let mut buf = [0 as wchar_t; STRING_BUF_LEN]; | ||
| let res = unsafe { | ||
| ffi::hid_get_manufacturer_string(self._hid_device, | ||
| buf.as_mut_ptr(), | ||
| STRING_BUF_LEN as size_t) | ||
| ffi::hid_get_manufacturer_string( | ||
| self._hid_device, | ||
| buf.as_mut_ptr(), | ||
| STRING_BUF_LEN as size_t, | ||
| ) | ||
| }; | ||
| let res = try!(self.check_size(res)); | ||
| let res = self.check_size(res)?; | ||
| unsafe { wchar_to_string(buf[..res].as_ptr()) } | ||
@@ -397,10 +463,12 @@ } | ||
| /// Get The Manufacturer String from a HID device. | ||
| pub fn get_product_string(&self) -> HidResult<String> { | ||
| pub fn get_product_string(&self) -> HidResult<Option<String>> { | ||
| let mut buf = [0 as wchar_t; STRING_BUF_LEN]; | ||
| let res = unsafe { | ||
| ffi::hid_get_product_string(self._hid_device, | ||
| buf.as_mut_ptr(), | ||
| STRING_BUF_LEN as size_t) | ||
| ffi::hid_get_product_string( | ||
| self._hid_device, | ||
| buf.as_mut_ptr(), | ||
| STRING_BUF_LEN as size_t, | ||
| ) | ||
| }; | ||
| let res = try!(self.check_size(res)); | ||
| let res = self.check_size(res)?; | ||
| unsafe { wchar_to_string(buf[..res].as_ptr()) } | ||
@@ -410,10 +478,12 @@ } | ||
| /// Get The Serial Number String from a HID device. | ||
| pub fn get_serial_number_string(&self) -> HidResult<String> { | ||
| pub fn get_serial_number_string(&self) -> HidResult<Option<String>> { | ||
| let mut buf = [0 as wchar_t; STRING_BUF_LEN]; | ||
| let res = unsafe { | ||
| ffi::hid_get_serial_number_string(self._hid_device, | ||
| buf.as_mut_ptr(), | ||
| STRING_BUF_LEN as size_t) | ||
| ffi::hid_get_serial_number_string( | ||
| self._hid_device, | ||
| buf.as_mut_ptr(), | ||
| STRING_BUF_LEN as size_t, | ||
| ) | ||
| }; | ||
| let res = try!(self.check_size(res)); | ||
| let res = self.check_size(res)?; | ||
| unsafe { wchar_to_string(buf[..res].as_ptr()) } | ||
@@ -423,13 +493,15 @@ } | ||
| /// Get a string from a HID device, based on its string index. | ||
| pub fn get_indexed_string(&self, index: i32) -> HidResult<String> { | ||
| pub fn get_indexed_string(&self, index: i32) -> HidResult<Option<String>> { | ||
| let mut buf = [0 as wchar_t; STRING_BUF_LEN]; | ||
| let res = unsafe { | ||
| ffi::hid_get_indexed_string(self._hid_device, | ||
| index as c_int, | ||
| buf.as_mut_ptr(), | ||
| STRING_BUF_LEN) | ||
| ffi::hid_get_indexed_string( | ||
| self._hid_device, | ||
| index as c_int, | ||
| buf.as_mut_ptr(), | ||
| STRING_BUF_LEN, | ||
| ) | ||
| }; | ||
| let res = try!(self.check_size(res)); | ||
| let res = self.check_size(res)?; | ||
| unsafe { wchar_to_string(buf[..res].as_ptr()) } | ||
| } | ||
| } |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet