diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,12 @@
+## hidapi [![Build Status](https://travis-ci.org/chpatrick/haskell-hidapi.svg?branch=master)](https://travis-ci.org/chpatrick/haskell-hidapi)
+
+Haskell FFI bindings to the HIDAPI library
+
+### Vendored HIDAPI
+
+hidapi bundles C source from [the libusb project][vendor0-libusb].  The current
+version of the HIDAPI library bundled with this library is [libusb/hidapi@`f8a47b5`][vendor1-commit].
+
+[vendor0-libusb]:https://libusb.info
+[vendor1-commit]:https://github.com/libusb/hidapi/tree/3cd17427375df568aaeff8aba51b6d200f8a47b5
+
diff --git a/System/HIDAPI.hsc b/System/HIDAPI.hsc
--- a/System/HIDAPI.hsc
+++ b/System/HIDAPI.hsc
@@ -8,6 +8,8 @@
   , open, openPath, openDeviceInfo
   , close
   , System.HIDAPI.read
+  , System.HIDAPI.readTimeout
+  , System.HIDAPI.setBlocking
   , System.HIDAPI.write
   , System.HIDAPI.getFeatureReport
   , System.HIDAPI.sendFeatureReport
@@ -234,6 +236,9 @@
 foreign import ccall unsafe "hidapi/hidapi.h hid_read"
   hid_read :: Device -> Ptr CChar -> CSize -> IO CInt
 
+foreign import ccall unsafe "hidapi/hidapi.h hid_read_timeout"
+  hid_read_timeout :: Device -> Ptr CChar -> CSize -> CInt -> IO CInt
+
 foreign import ccall unsafe "hidapi/hidapi.h hid_write"
   hid_write :: Device -> Ptr CChar -> CSize -> IO CInt
 
@@ -243,10 +248,26 @@
 foreign import ccall unsafe "hidapi/hidapi.h hid_send_feature_report"
   hid_send_feature_report :: Device -> Ptr CChar -> CSize -> IO CInt
 
+foreign import ccall unsafe "hidapi/hidapi.h hid_set_nonblocking"
+  hid_set_nonblocking :: Device -> CInt -> IO CInt
+
+setBlocking :: Device -- USB Device to act on, see open, openPath or openDeviceInfo
+            -> Bool   -- True -> read blocks; False -> read may return immediately with 0 bytes read
+            -> IO ()
+setBlocking dev blocking = do
+  n' <- hid_set_nonblocking dev $ if blocking then 0 else 1
+  checkWithHidError (n' /= -1) dev "Unable to set blocking mode" "hid_set_nonblocking returned -1"
+
 read :: Device -> Int -> IO ByteString
 read dev n = allocaBytes n $ \b -> do
   n' <- hid_read dev b (fromIntegral n)
   checkWithHidError (n' /= -1) dev "Read failed" "hid_read returned -1"
+  packCStringLen ( b, fromIntegral n' )
+
+readTimeout :: Device -> Int -> Int -> IO ByteString
+readTimeout dev n time = allocaBytes n $ \b -> do
+  n' <- hid_read_timeout dev b (fromIntegral n) (fromIntegral time)
+  checkWithHidError (n' /= -1) dev "Read failed" "hid_read_timeout returned -1"
   packCStringLen ( b, fromIntegral n' )
 
 write :: Device -> ByteString -> IO Int
diff --git a/cbits/hidapi/hidapi.h b/cbits/hidapi/hidapi.h
--- a/cbits/hidapi/hidapi.h
+++ b/cbits/hidapi/hidapi.h
@@ -17,7 +17,7 @@
  files located at the root of the source distribution.
  These files may also be found in the public source
  code repository located at:
-        http://github.com/signal11/hidapi .
+        https://github.com/libusb/hidapi .
 ********************************************************/
 
 /** @file
@@ -39,9 +39,42 @@
 
 #define HID_API_EXPORT_CALL HID_API_EXPORT HID_API_CALL /**< API export and call macro*/
 
+/** @brief Static/compile-time major version of the library.
+
+	@ingroup API
+*/
+#define HID_API_VERSION_MAJOR 0
+/** @brief Static/compile-time minor version of the library.
+
+	@ingroup API
+*/
+#define HID_API_VERSION_MINOR 10
+/** @brief Static/compile-time patch version of the library.
+
+	@ingroup API
+*/
+#define HID_API_VERSION_PATCH 1
+
+/* Helper macros */
+#define HID_API_AS_STR_IMPL(x) #x
+#define HID_API_AS_STR(x) HID_API_AS_STR_IMPL(x)
+#define HID_API_TO_VERSION_STR(v1, v2, v3) HID_API_AS_STR(v1.v2.v3)
+
+/** @brief Static/compile-time string version of the library.
+
+	@ingroup API
+*/
+#define HID_API_VERSION_STR HID_API_TO_VERSION_STR(HID_API_VERSION_MAJOR, HID_API_VERSION_MINOR, HID_API_VERSION_PATCH)
+
 #ifdef __cplusplus
 extern "C" {
 #endif
+		struct hid_api_version {
+			int major;
+			int minor;
+			int patch;
+		};
+
 		struct hid_device_;
 		typedef struct hid_device_ hid_device; /**< opaque hidapi structure */
 
@@ -63,15 +96,19 @@
 			/** Product string */
 			wchar_t *product_string;
 			/** Usage Page for this Device/Interface
-			    (Windows/Mac only). */
+			    (Windows/Mac/hidraw only) */
 			unsigned short usage_page;
 			/** Usage for this Device/Interface
-			    (Windows/Mac only).*/
+			    (Windows/Mac/hidraw only) */
 			unsigned short usage;
 			/** The USB interface which this logical device
-			    represents. Valid on both Linux implementations
-			    in all cases, and valid on the Windows implementation
-			    only if the device contains more than one interface. */
+			    represents.
+
+				* Valid on both Linux implementations in all cases.
+				* Valid on the Windows implementation only if the device
+				  contains more than one interface.
+				* Valid on the Mac implementation if and only if the device
+				  is a USB HID device. */
 			int interface_number;
 
 			/** Pointer to the next device */
@@ -87,7 +124,7 @@
 			needed.  This function should be called at the beginning of
 			execution however, if there is a chance of HIDAPI handles
 			being opened by different threads simultaneously.
-			
+
 			@ingroup API
 
 			@returns
@@ -125,7 +162,7 @@
 
 		    @returns
 		    	This function returns a pointer to a linked list of type
-		    	struct #hid_device, containing information about the HID devices
+		    	struct #hid_device_info, containing information about the HID devices
 		    	attached to the system, or NULL in the case of failure. Free
 		    	this linked list by calling hid_free_enumeration().
 		*/
@@ -147,6 +184,8 @@
 			If @p serial_number is NULL, the first device with the
 			specified VID and PID is opened.
 
+			This function sets the return value of hid_error().
+
 			@ingroup API
 			@param vendor_id The Vendor ID (VID) of the device to open.
 			@param product_id The Product ID (PID) of the device to open.
@@ -165,6 +204,8 @@
 			platform-specific path name can be used (eg: /dev/hidraw0 on
 			Linux).
 
+			This function sets the return value of hid_error().
+
 			@ingroup API
 		    @param path The path name of the device to open
 
@@ -190,8 +231,10 @@
 			one exists. If it does not, it will send the data through
 			the Control Endpoint (Endpoint 0).
 
+			This function sets the return value of hid_error().
+
 			@ingroup API
-			@param device A device handle returned from hid_open().
+			@param dev A device handle returned from hid_open().
 			@param data The data to send, including the report number as
 				the first byte.
 			@param length The length in bytes of the data to send.
@@ -200,7 +243,7 @@
 				This function returns the actual number of bytes written and
 				-1 on error.
 		*/
-		int  HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length);
+		int  HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length);
 
 		/** @brief Read an Input report from a HID device with timeout.
 
@@ -208,8 +251,10 @@
 			to the host through the INTERRUPT IN endpoint. The first byte will
 			contain the Report number if the device uses numbered reports.
 
+			This function sets the return value of hid_error().
+
 			@ingroup API
-			@param device A device handle returned from hid_open().
+			@param dev A device handle returned from hid_open().
 			@param data A buffer to put the read data into.
 			@param length The number of bytes to read. For devices with
 				multiple reports, make sure to read an extra byte for
@@ -229,8 +274,10 @@
 		    to the host through the INTERRUPT IN endpoint. The first byte will
 			contain the Report number if the device uses numbered reports.
 
+			This function sets the return value of hid_error().
+
 			@ingroup API
-			@param device A device handle returned from hid_open().
+			@param dev A device handle returned from hid_open().
 			@param data A buffer to put the read data into.
 			@param length The number of bytes to read. For devices with
 				multiple reports, make sure to read an extra byte for
@@ -241,7 +288,7 @@
 				-1 on error. If no packet was available to be read and
 				the handle is in non-blocking mode, this function returns 0.
 		*/
-		int  HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length);
+		int  HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length);
 
 		/** @brief Set the device handle to be non-blocking.
 
@@ -253,7 +300,7 @@
 			Nonblocking can be turned on and off at any time.
 
 			@ingroup API
-			@param device A device handle returned from hid_open().
+			@param dev A device handle returned from hid_open().
 			@param nonblock enable or not the nonblocking reads
 			 - 1 to enable nonblocking
 			 - 0 to disable nonblocking.
@@ -261,7 +308,7 @@
 			@returns
 				This function returns 0 on success and -1 on error.
 		*/
-		int  HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *device, int nonblock);
+		int  HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock);
 
 		/** @brief Send a Feature report to the device.
 
@@ -278,8 +325,10 @@
 			report data (16 bytes). In this example, the length passed
 			in would be 17.
 
+			This function sets the return value of hid_error().
+
 			@ingroup API
-			@param device A device handle returned from hid_open().
+			@param dev A device handle returned from hid_open().
 			@param data The data to send, including the report number as
 				the first byte.
 			@param length The length in bytes of the data to send, including
@@ -289,7 +338,7 @@
 				This function returns the actual number of bytes written and
 				-1 on error.
 		*/
-		int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *device, const unsigned char *data, size_t length);
+		int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length);
 
 		/** @brief Get a feature report from a HID device.
 
@@ -299,7 +348,34 @@
 			still contain the Report ID, and the report data will
 			start in data[1].
 
+			This function sets the return value of hid_error().
+
 			@ingroup API
+			@param dev A device handle returned from hid_open().
+			@param data A buffer to put the read data into, including
+				the Report ID. Set the first byte of @p data[] to the
+				Report ID of the report to be read, or set it to zero
+				if your device does not use numbered reports.
+			@param length The number of bytes to read, including an
+				extra byte for the report ID. The buffer can be longer
+				than the actual report.
+
+			@returns
+				This function returns the number of bytes read plus
+				one for the report ID (which is still in the first
+				byte), or -1 on error.
+		*/
+		int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length);
+
+		/** @brief Get a input report from a HID device.
+
+			Set the first byte of @p data[] to the Report ID of the
+			report to be read. Make sure to allow space for this
+			extra byte in @p data[]. Upon return, the first byte will
+			still contain the Report ID, and the report data will
+			start in data[1].
+
+			@ingroup API
 			@param device A device handle returned from hid_open().
 			@param data A buffer to put the read data into, including
 				the Report ID. Set the first byte of @p data[] to the
@@ -314,55 +390,57 @@
 				one for the report ID (which is still in the first
 				byte), or -1 on error.
 		*/
-		int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length);
+		int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length);
 
 		/** @brief Close a HID device.
 
+			This function sets the return value of hid_error().
+
 			@ingroup API
-			@param device A device handle returned from hid_open().
+			@param dev A device handle returned from hid_open().
 		*/
-		void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device);
+		void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev);
 
 		/** @brief Get The Manufacturer String from a HID device.
 
 			@ingroup API
-			@param device A device handle returned from hid_open().
+			@param dev A device handle returned from hid_open().
 			@param string A wide string buffer to put the data into.
 			@param maxlen The length of the buffer in multiples of wchar_t.
 
 			@returns
 				This function returns 0 on success and -1 on error.
 		*/
-		int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *device, wchar_t *string, size_t maxlen);
+		int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen);
 
 		/** @brief Get The Product String from a HID device.
 
 			@ingroup API
-			@param device A device handle returned from hid_open().
+			@param dev A device handle returned from hid_open().
 			@param string A wide string buffer to put the data into.
 			@param maxlen The length of the buffer in multiples of wchar_t.
 
 			@returns
 				This function returns 0 on success and -1 on error.
 		*/
-		int HID_API_EXPORT_CALL hid_get_product_string(hid_device *device, wchar_t *string, size_t maxlen);
+		int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen);
 
 		/** @brief Get The Serial Number String from a HID device.
 
 			@ingroup API
-			@param device A device handle returned from hid_open().
+			@param dev A device handle returned from hid_open().
 			@param string A wide string buffer to put the data into.
 			@param maxlen The length of the buffer in multiples of wchar_t.
 
 			@returns
 				This function returns 0 on success and -1 on error.
 		*/
-		int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *device, wchar_t *string, size_t maxlen);
+		int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen);
 
 		/** @brief Get a string from a HID device, based on its string index.
 
 			@ingroup API
-			@param device A device handle returned from hid_open().
+			@param dev A device handle returned from hid_open().
 			@param string_index The index of the string to get.
 			@param string A wide string buffer to put the data into.
 			@param maxlen The length of the buffer in multiples of wchar_t.
@@ -370,18 +448,47 @@
 			@returns
 				This function returns 0 on success and -1 on error.
 		*/
-		int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *device, int string_index, wchar_t *string, size_t maxlen);
+		int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen);
 
 		/** @brief Get a string describing the last error which occurred.
 
+			Whether a function sets the last error is noted in its
+			documentation. These functions will reset the last error
+			to NULL before their execution.
+
+			Strings returned from hid_error() must not be freed by the user!
+
+			This function is thread-safe, and error messages are thread-local.
+
 			@ingroup API
-			@param device A device handle returned from hid_open().
+			@param dev A device handle returned from hid_open(),
+			  or NULL to get the last non-device-specific error
+			  (e.g. for errors in hid_open() itself).
 
 			@returns
 				This function returns a string containing the last error
 				which occurred or NULL if none has occurred.
 		*/
-		HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *device);
+		HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *dev);
+
+		/** @brief Get a runtime version of the library.
+
+			@ingroup API
+
+			@returns
+				Pointer to statically allocated struct, that contains version.
+		*/
+		HID_API_EXPORT const  struct hid_api_version* HID_API_CALL hid_version(void);
+
+
+		/** @brief Get a runtime version string of the library.
+
+			@ingroup API
+
+			@returns
+				Pointer to statically allocated string, that contains version string.
+		*/
+		HID_API_EXPORT const char* HID_API_CALL hid_version_str(void);
 
 #ifdef __cplusplus
 }
diff --git a/cbits/hidapi/linux/hid.c b/cbits/hidapi/linux/hid.c
--- a/cbits/hidapi/linux/hid.c
+++ b/cbits/hidapi/linux/hid.c
@@ -18,7 +18,7 @@
  files located at the root of the source distribution.
  These files may also be found in the public source
  code repository located at:
-        http://github.com/signal11/hidapi .
+        https://github.com/libusb/hidapi .
 ********************************************************/
 
 /* C */
@@ -45,16 +45,7 @@
 
 #include "hidapi.h"
 
-/* Definitions from linux/hidraw.h. Since these are new, some distros
-   may not have header files which contain them. */
-#ifndef HIDIOCSFEATURE
-#define HIDIOCSFEATURE(len)    _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, len)
-#endif
-#ifndef HIDIOCGFEATURE
-#define HIDIOCGFEATURE(len)    _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x07, len)
-#endif
 
-
 /* USB HID device property names */
 const char *device_string_names[] = {
 	"manufacturer",
@@ -75,38 +66,26 @@
 	int device_handle;
 	int blocking;
 	int uses_numbered_reports;
+	wchar_t *last_error_str;
 };
 
-
-static __u32 kernel_version = 0;
-
-static __u32 detect_kernel_version(void)
-{
-	struct utsname name;
-	int major, minor, release;
-	int ret;
-
-	uname(&name);
-	ret = sscanf(name.release, "%d.%d.%d", &major, &minor, &release);
-	if (ret == 3) {
-		return KERNEL_VERSION(major, minor, release);
-	}
-
-	ret = sscanf(name.release, "%d.%d", &major, &minor);
-	if (ret == 2) {
-		return KERNEL_VERSION(major, minor, 0);
-	}
+static struct hid_api_version api_version = {
+	.major = HID_API_VERSION_MAJOR,
+	.minor = HID_API_VERSION_MINOR,
+	.patch = HID_API_VERSION_PATCH
+};
 
-	printf("Couldn't determine kernel version from version string \"%s\"\n", name.release);
-	return 0;
-}
+/* Global error message that is not specific to a device, e.g. for
+   hid_open(). It is thread-local like errno. */
+__thread wchar_t *last_global_error_str = NULL;
 
 static hid_device *new_hid_device(void)
 {
-	hid_device *dev = calloc(1, sizeof(hid_device));
+	hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device));
 	dev->device_handle = -1;
 	dev->blocking = 1;
 	dev->uses_numbered_reports = 0;
+	dev->last_error_str = NULL;
 
 	return dev;
 }
@@ -122,7 +101,7 @@
 		if ((size_t) -1 == wlen) {
 			return wcsdup(L"");
 		}
-		ret = calloc(wlen+1, sizeof(wchar_t));
+		ret = (wchar_t*) calloc(wlen+1, sizeof(wchar_t));
 		mbstowcs(ret, utf8, wlen+1);
 		ret[wlen] = 0x0000;
 	}
@@ -130,6 +109,61 @@
 	return ret;
 }
 
+
+/* Set the last global error to be reported by hid_error(NULL).
+ * The given error message will be copied (and decoded according to the
+ * currently locale, so do not pass in string constants).
+ * The last stored global error message is freed.
+ * Use register_global_error(NULL) to indicate "no error". */
+static void register_global_error(const char *msg)
+{
+	if (last_global_error_str)
+		free(last_global_error_str);
+
+	last_global_error_str = utf8_to_wchar_t(msg);
+}
+
+/* See register_global_error, but you can pass a format string into this function. */
+static void register_global_error_format(const char *format, ...)
+{
+	va_list args;
+	va_start(args, format);
+
+	char msg[100];
+	vsnprintf(msg, sizeof(msg), format, args);
+
+	va_end(args);
+
+	register_global_error(msg);
+}
+
+/* Set the last error for a device to be reported by hid_error(device).
+ * The given error message will be copied (and decoded according to the
+ * currently locale, so do not pass in string constants).
+ * The last stored global error message is freed.
+ * Use register_device_error(device, NULL) to indicate "no error". */
+static void register_device_error(hid_device *dev, const char *msg)
+{
+	if (dev->last_error_str)
+		free(dev->last_error_str);
+
+	dev->last_error_str = utf8_to_wchar_t(msg);
+}
+
+/* See register_device_error, but you can pass a format string into this function. */
+static void register_device_error_format(hid_device *dev, const char *format, ...)
+{
+	va_list args;
+	va_start(args, format);
+
+	char msg[100];
+	vsnprintf(msg, sizeof(msg), format, args);
+
+	va_end(args);
+
+	register_device_error(dev, msg);
+}
+
 /* Get an attribute value from a udev_device and return it as a whar_t
    string. The returned string must be freed with free() when done.*/
 static wchar_t *copy_udev_string(struct udev_device *dev, const char *udev_name)
@@ -137,11 +171,67 @@
 	return utf8_to_wchar_t(udev_device_get_sysattr_value(dev, udev_name));
 }
 
+/*
+ * Gets the size of the HID item at the given position
+ * Returns 1 if successful, 0 if an invalid key
+ * Sets data_len and key_size when successful
+ */
+static int get_hid_item_size(__u8 *report_descriptor, unsigned int pos, __u32 size, int *data_len, int *key_size)
+{
+	int key = report_descriptor[pos];
+	int size_code;
+
+	/*
+	 * This is a Long Item. The next byte contains the
+	 * length of the data section (value) for this key.
+	 * See the HID specification, version 1.11, section
+	 * 6.2.2.3, titled "Long Items."
+	 */
+	if ((key & 0xf0) == 0xf0) {
+		if (pos + 1 < size)
+		{
+			*data_len = report_descriptor[pos + 1];
+			*key_size = 3;
+			return 1;
+		}
+		*data_len = 0; /* malformed report */
+		*key_size = 0;
+	}
+
+	/*
+	 * This is a Short Item. The bottom two bits of the
+	 * key contain the size code for the data section
+	 * (value) for this key. Refer to the HID
+	 * specification, version 1.11, section 6.2.2.2,
+	 * titled "Short Items."
+	 */
+	size_code = key & 0x3;
+	switch (size_code) {
+	case 0:
+	case 1:
+	case 2:
+		*data_len = size_code;
+		*key_size = 1;
+		return 1;
+	case 3:
+		*data_len = 4;
+		*key_size = 1;
+		return 1;
+	default:
+		/* Can't ever happen since size_code is & 0x3 */
+		*data_len = 0;
+		*key_size = 0;
+		break;
+	};
+
+	/* malformed report */
+	return 0;
+}
+
 /* uses_numbered_reports() returns 1 if report_descriptor describes a device
    which contains numbered reports. */
 static int uses_numbered_reports(__u8 *report_descriptor, __u32 size) {
 	unsigned int i = 0;
-	int size_code;
 	int data_len, key_size;
 
 	while (i < size) {
@@ -154,42 +244,9 @@
 			return 1;
 		}
 
-		//printf("key: %02hhx\n", key);
-
-		if ((key & 0xf0) == 0xf0) {
-			/* This is a Long Item. The next byte contains the
-			   length of the data section (value) for this key.
-			   See the HID specification, version 1.11, section
-			   6.2.2.3, titled "Long Items." */
-			if (i+1 < size)
-				data_len = report_descriptor[i+1];
-			else
-				data_len = 0; /* malformed report */
-			key_size = 3;
-		}
-		else {
-			/* This is a Short Item. The bottom two bits of the
-			   key contain the size code for the data section
-			   (value) for this key.  Refer to the HID
-			   specification, version 1.11, section 6.2.2.2,
-			   titled "Short Items." */
-			size_code = key & 0x3;
-			switch (size_code) {
-			case 0:
-			case 1:
-			case 2:
-				data_len = size_code;
-				break;
-			case 3:
-				data_len = 4;
-				break;
-			default:
-				/* Can't ever happen since size_code is & 0x3 */
-				data_len = 0;
-				break;
-			};
-			key_size = 1;
-		}
+		/* Determine data_len and key_size */
+		if (!get_hid_item_size(report_descriptor, i, size, &data_len, &key_size))
+			return 0; /* malformed report */
 
 		/* Skip over this key and it's associated data */
 		i += data_len + key_size;
@@ -200,11 +257,162 @@
 }
 
 /*
+ * Get bytes from a HID Report Descriptor.
+ * Only call with a num_bytes of 0, 1, 2, or 4.
+ */
+static __u32 get_hid_report_bytes(__u8 *rpt, size_t len, size_t num_bytes, size_t cur)
+{
+	/* Return if there aren't enough bytes. */
+	if (cur + num_bytes >= len)
+		return 0;
+
+	if (num_bytes == 0)
+		return 0;
+	else if (num_bytes == 1)
+		return rpt[cur + 1];
+	else if (num_bytes == 2)
+		return (rpt[cur + 2] * 256 + rpt[cur + 1]);
+	else if (num_bytes == 4)
+		return (
+			rpt[cur + 4] * 0x01000000 +
+			rpt[cur + 3] * 0x00010000 +
+			rpt[cur + 2] * 0x00000100 +
+			rpt[cur + 1] * 0x00000001
+		);
+	else
+		return 0;
+}
+
+/*
+ * Retrieves the device's Usage Page and Usage from the report descriptor.
+ * The algorithm returns the current Usage Page/Usage pair whenever a new
+ * Collection is found and a Usage Local Item is currently in scope.
+ * Usage Local Items are consumed by each Main Item (See. 6.2.2.8).
+ * The algorithm should give similar results as Apple's:
+ *   https://developer.apple.com/documentation/iokit/kiohiddeviceusagepairskey?language=objc
+ * Physical Collections are also matched (macOS does the same).
+ *
+ * This function can be called repeatedly until it returns non-0
+ * Usage is found. pos is the starting point (initially 0) and will be updated
+ * to the next search position.
+ *
+ * The return value is 0 when a pair is found.
+ * 1 when finished processing descriptor.
+ * -1 on a malformed report.
+ */
+static int get_next_hid_usage(__u8 *report_descriptor, __u32 size, unsigned int *pos, unsigned short *usage_page, unsigned short *usage)
+{
+	int data_len, key_size;
+	int initial = *pos == 0; /* Used to handle case where no top-level application collection is defined */
+	int usage_pair_ready = 0;
+
+	/* Usage is a Local Item, it must be set before each Main Item (Collection) before a pair is returned */
+	int usage_found = 0;
+
+	while (*pos < size) {
+		int key = report_descriptor[*pos];
+		int key_cmd = key & 0xfc;
+
+		/* Determine data_len and key_size */
+		if (!get_hid_item_size(report_descriptor, *pos, size, &data_len, &key_size))
+			return -1; /* malformed report */
+
+		switch (key_cmd) {
+		case 0x4: /* Usage Page 6.2.2.7 (Global) */
+			*usage_page = get_hid_report_bytes(report_descriptor, size, data_len, *pos);
+			break;
+
+		case 0x8: /* Usage 6.2.2.8 (Local) */
+			*usage = get_hid_report_bytes(report_descriptor, size, data_len, *pos);
+			usage_found = 1;
+			break;
+
+		case 0xa0: /* Collection 6.2.2.4 (Main) */
+			/* A Usage Item (Local) must be found for the pair to be valid */
+			if (usage_found)
+				usage_pair_ready = 1;
+
+			/* Usage is a Local Item, unset it */
+			usage_found = 0;
+			break;
+
+		case 0x80: /* Input 6.2.2.4 (Main) */
+		case 0x90: /* Output 6.2.2.4 (Main) */
+		case 0xb0: /* Feature 6.2.2.4 (Main) */
+		case 0xc0: /* End Collection 6.2.2.4 (Main) */
+			/* Usage is a Local Item, unset it */
+			usage_found = 0;
+			break;
+		}
+
+		/* Skip over this key and it's associated data */
+		*pos += data_len + key_size;
+
+		/* Return usage pair */
+		if (usage_pair_ready)
+			return 0;
+	}
+
+	/* If no top-level application collection is found and usage page/usage pair is found, pair is valid
+	   https://docs.microsoft.com/en-us/windows-hardware/drivers/hid/top-level-collections */
+	if (initial && usage_found)
+		return 0; /* success */
+
+	return 1; /* finished processing */
+}
+
+/*
+ * Retrieves the hidraw report descriptor from a file.
+ * When using this form, <sysfs_path>/device/report_descriptor, elevated priviledges are not required.
+ */
+static int get_hid_report_descriptor(const char *rpt_path, struct hidraw_report_descriptor *rpt_desc)
+{
+	int rpt_handle;
+	ssize_t res;
+
+	rpt_handle = open(rpt_path, O_RDONLY);
+	if (rpt_handle < 0) {
+		register_global_error_format("open failed (%s): %s", rpt_path, strerror(errno));
+		return -1;
+	}
+
+	/*
+	 * Read in the Report Descriptor
+	 * The sysfs file has a maximum size of 4096 (which is the same as HID_MAX_DESCRIPTOR_SIZE) so we should always
+	 * be ok when reading the descriptor.
+	 * In practice if the HID descriptor is any larger I suspect many other things will break.
+	 */
+	memset(rpt_desc, 0x0, sizeof(*rpt_desc));
+	res = read(rpt_handle, rpt_desc->value, HID_MAX_DESCRIPTOR_SIZE);
+	if (res < 0) {
+		register_global_error_format("read failed (%s): %s", rpt_path, strerror(errno));
+	}
+	rpt_desc->size = (__u32) res;
+
+	close(rpt_handle);
+	return (int) res;
+}
+
+static int get_hid_report_descriptor_from_sysfs(const char *sysfs_path, struct hidraw_report_descriptor *rpt_desc)
+{
+	int res = -1;
+	/* Construct <sysfs_path>/device/report_descriptor */
+	size_t rpt_path_len = strlen(sysfs_path) + 25 + 1;
+	char* rpt_path = (char*) calloc(1, rpt_path_len);
+	snprintf(rpt_path, rpt_path_len, "%s/device/report_descriptor", sysfs_path);
+
+	res = get_hid_report_descriptor(rpt_path, rpt_desc);
+	free(rpt_path);
+
+	return res;
+}
+
+/*
  * The caller is responsible for free()ing the (newly-allocated) character
  * strings pointed to by serial_number_utf8 and product_name_utf8 after use.
  */
 static int
-parse_uevent_info(const char *uevent, int *bus_type,
+parse_uevent_info(const char *uevent, unsigned *bus_type,
 	unsigned short *vendor_id, unsigned short *product_id,
 	char **serial_number_utf8, char **product_name_utf8)
 {
@@ -263,18 +471,20 @@
 	struct udev_device *udev_dev, *parent, *hid_dev;
 	struct stat s;
 	int ret = -1;
-        char *serial_number_utf8 = NULL;
-        char *product_name_utf8 = NULL;
+	char *serial_number_utf8 = NULL;
+	char *product_name_utf8 = NULL;
 
 	/* Create the udev object */
 	udev = udev_new();
 	if (!udev) {
-		printf("Can't create udev\n");
+		register_global_error("Couldn't create udev context");
 		return -1;
 	}
 
 	/* 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. */
 	udev_dev = udev_device_new_from_devnum(udev, 'c', s.st_rdev);
 	if (udev_dev) {
@@ -285,7 +495,7 @@
 		if (hid_dev) {
 			unsigned short dev_vid;
 			unsigned short dev_pid;
-			int bus_type;
+			unsigned bus_type;
 			size_t retm;
 
 			ret = parse_uevent_info(
@@ -296,27 +506,8 @@
 			           &serial_number_utf8,
 			           &product_name_utf8);
 
-			if (bus_type == BUS_BLUETOOTH) {
-				switch (key) {
-					case DEVICE_STRING_MANUFACTURER:
-						wcsncpy(string, L"", maxlen);
-						ret = 0;
-						break;
-					case DEVICE_STRING_PRODUCT:
-						retm = mbstowcs(string, product_name_utf8, maxlen);
-						ret = (retm == (size_t)-1)? -1: 0;
-						break;
-					case DEVICE_STRING_SERIAL:
-						retm = mbstowcs(string, serial_number_utf8, maxlen);
-						ret = (retm == (size_t)-1)? -1: 0;
-						break;
-					case DEVICE_STRING_COUNT:
-					default:
-						ret = -1;
-						break;
-				}
-			}
-			else {
+			/* Standard USB device */
+			if (bus_type == BUS_USB) {
 				/* This is a USB device. Find its parent USB Device node. */
 				parent = udev_device_get_parent_with_subsystem_devtype(
 					   udev_dev,
@@ -338,16 +529,46 @@
 						/* Convert the string from UTF-8 to wchar_t */
 						retm = mbstowcs(string, str, maxlen);
 						ret = (retm == (size_t)-1)? -1: 0;
-						goto end;
 					}
+
+					/* USB information parsed */
+					goto end;
 				}
+				else {
+					/* Correctly handled below */
+				}
 			}
+
+			/* USB information not available (uhid) or another type of HID bus */
+			switch (bus_type) {
+				case BUS_BLUETOOTH:
+				case BUS_I2C:
+				case BUS_USB:
+					switch (key) {
+						case DEVICE_STRING_MANUFACTURER:
+							wcsncpy(string, L"", maxlen);
+							ret = 0;
+							break;
+						case DEVICE_STRING_PRODUCT:
+							retm = mbstowcs(string, product_name_utf8, maxlen);
+							ret = (retm == (size_t)-1)? -1: 0;
+							break;
+						case DEVICE_STRING_SERIAL:
+							retm = mbstowcs(string, serial_number_utf8, maxlen);
+							ret = (retm == (size_t)-1)? -1: 0;
+							break;
+						case DEVICE_STRING_COUNT:
+						default:
+							ret = -1;
+							break;
+					}
+			}
 		}
 	}
 
 end:
-        free(serial_number_utf8);
-        free(product_name_utf8);
+	free(serial_number_utf8);
+	free(product_name_utf8);
 
 	udev_device_unref(udev_dev);
 	/* parent and hid_dev don't need to be (and can't be) unref'd.
@@ -357,6 +578,16 @@
 	return ret;
 }
 
+HID_API_EXPORT const struct hid_api_version* HID_API_CALL hid_version()
+{
+	return &api_version;
+}
+
+HID_API_EXPORT const char* HID_API_CALL hid_version_str()
+{
+	return HID_API_VERSION_STR;
+}
+
 int HID_API_EXPORT hid_init(void)
 {
 	const char *locale;
@@ -366,14 +597,14 @@
 	if (!locale)
 		setlocale(LC_CTYPE, "");
 
-	kernel_version = detect_kernel_version();
-
 	return 0;
 }
 
 int HID_API_EXPORT hid_exit(void)
 {
-	/* Nothing to do for this in the Linux/hidraw implementation. */
+	/* Free global error message */
+	register_global_error(NULL);
+
 	return 0;
 }
 
@@ -393,7 +624,7 @@
 	/* Create the udev object */
 	udev = udev_new();
 	if (!udev) {
-		printf("Can't create udev\n");
+		register_global_error("Couldn't create udev context");
 		return NULL;
 	}
 
@@ -416,8 +647,9 @@
 		unsigned short dev_pid;
 		char *serial_number_utf8 = NULL;
 		char *product_name_utf8 = NULL;
-		int bus_type;
+		unsigned bus_type;
 		int result;
+		struct hidraw_report_descriptor report_desc;
 
 		/* Get the filename of the /sys entry for the device
 		   and create a udev_device object (dev) representing it */
@@ -448,9 +680,15 @@
 			goto next;
 		}
 
-		if (bus_type != BUS_USB && bus_type != BUS_BLUETOOTH) {
-			/* We only know how to handle USB and BT devices. */
-			goto next;
+		/* Filter out unhandled devices right away */
+		switch (bus_type) {
+			case BUS_BLUETOOTH:
+			case BUS_I2C:
+			case BUS_USB:
+				break;
+
+			default:
+				goto next;
 		}
 
 		/* Check the VID/PID against the arguments */
@@ -459,7 +697,7 @@
 			struct hid_device_info *tmp;
 
 			/* VID/PID match. Create the record. */
-			tmp = malloc(sizeof(struct hid_device_info));
+			tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info));
 			if (cur_dev) {
 				cur_dev->next = tmp;
 			}
@@ -499,22 +737,14 @@
 							"usb",
 							"usb_device");
 
+					/* uhid USB devices
+					   Since this is a virtual hid interface, no USB information will
+					   be available. */
 					if (!usb_dev) {
-						/* Free this device */
-						free(cur_dev->serial_number);
-						free(cur_dev->path);
-						free(cur_dev);
-
-						/* Take it off the device list. */
-						if (prev_dev) {
-							prev_dev->next = NULL;
-							cur_dev = prev_dev;
-						}
-						else {
-							cur_dev = root = NULL;
-						}
-
-						goto next;
+						/* Manufacturer and Product strings */
+						cur_dev->manufacturer_string = wcsdup(L"");
+						cur_dev->product_string = utf8_to_wchar_t(product_name_utf8);
+						break;
 					}
 
 					/* Manufacturer and Product strings */
@@ -538,6 +768,7 @@
 					break;
 
 				case BUS_BLUETOOTH:
+				case BUS_I2C:
 					/* Manufacturer and Product strings */
 					cur_dev->manufacturer_string = wcsdup(L"");
 					cur_dev->product_string = utf8_to_wchar_t(product_name_utf8);
@@ -549,6 +780,45 @@
 					 * check for USB and Bluetooth devices above */
 					break;
 			}
+
+			/* Usage Page and Usage */
+			result = get_hid_report_descriptor_from_sysfs(sysfs_path, &report_desc);
+			if (result >= 0) {
+				unsigned short page = 0, usage = 0;
+				unsigned int pos = 0;
+				/*
+				 * Parse the first usage and usage page
+				 * out of the report descriptor.
+				 */
+				if (!get_next_hid_usage(report_desc.value, report_desc.size, &pos, &page, &usage)) {
+					cur_dev->usage_page = page;
+					cur_dev->usage = usage;
+				}
+
+				/*
+				 * Parse any additional usage and usage pages
+				 * out of the report descriptor.
+				 */
+				while (!get_next_hid_usage(report_desc.value, report_desc.size, &pos, &page, &usage)) {
+					/* Create new record for additional usage pairs */
+					tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info));
+					cur_dev->next = tmp;
+					prev_dev = cur_dev;
+					cur_dev = tmp;
+
+					/* Update fields */
+					cur_dev->path = strdup(dev_path);
+					cur_dev->vendor_id = dev_vid;
+					cur_dev->product_id = dev_pid;
+					cur_dev->serial_number = prev_dev->serial_number? wcsdup(prev_dev->serial_number): NULL;
+					cur_dev->release_number = prev_dev->release_number;
+					cur_dev->interface_number = prev_dev->interface_number;
+					cur_dev->manufacturer_string = prev_dev->manufacturer_string? wcsdup(prev_dev->manufacturer_string): NULL;
+					cur_dev->product_string = prev_dev->product_string? wcsdup(prev_dev->product_string): NULL;
+					cur_dev->usage_page = page;
+					cur_dev->usage = usage;
+				}
+			}
 		}
 
 	next:
@@ -582,6 +852,9 @@
 
 hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
 {
+	/* Set global error to none */
+	register_global_error(NULL);
+
 	struct hid_device_info *devs, *cur_dev;
 	const char *path_to_open = NULL;
 	hid_device *handle = NULL;
@@ -608,6 +881,8 @@
 	if (path_to_open) {
 		/* Open the device */
 		handle = hid_open_path(path_to_open);
+	} else {
+		register_global_error("No such device");
 	}
 
 	hid_free_enumeration(devs);
@@ -617,6 +892,9 @@
 
 hid_device * HID_API_EXPORT hid_open_path(const char *path)
 {
+	/* Set global error to none */
+	register_global_error(NULL);
+
 	hid_device *dev = NULL;
 
 	hid_init();
@@ -627,7 +905,9 @@
 	dev->device_handle = open(path, O_RDWR);
 
 	/* If we have a good handle, return it. */
-	if (dev->device_handle > 0) {
+	if (dev->device_handle >= 0) {
+		/* Set device error to none */
+		register_device_error(dev, NULL);
 
 		/* Get the report descriptor */
 		int res, desc_size = 0;
@@ -638,14 +918,13 @@
 		/* Get Report Descriptor Size */
 		res = ioctl(dev->device_handle, HIDIOCGRDESCSIZE, &desc_size);
 		if (res < 0)
-			perror("HIDIOCGRDESCSIZE");
-
+			register_device_error_format(dev, "ioctl (GRDESCSIZE): %s", strerror(errno));
 
 		/* Get Report Descriptor */
 		rpt_desc.size = desc_size;
 		res = ioctl(dev->device_handle, HIDIOCGRDESC, &rpt_desc);
 		if (res < 0) {
-			perror("HIDIOCGRDESC");
+			register_device_error_format(dev, "ioctl (GRDESC): %s", strerror(errno));
 		} else {
 			/* Determine if this device uses numbered reports. */
 			dev->uses_numbered_reports =
@@ -657,6 +936,7 @@
 	}
 	else {
 		/* Unable to open any devices. */
+		register_global_error(strerror(errno));
 		free(dev);
 		return NULL;
 	}
@@ -669,12 +949,17 @@
 
 	bytes_written = write(dev->device_handle, data, length);
 
+	register_device_error(dev, (bytes_written == -1)? strerror(errno): NULL);
+
 	return bytes_written;
 }
 
 
 int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
 {
+	/* Set device error to none */
+	register_device_error(dev, NULL);
+
 	int bytes_read;
 
 	if (milliseconds >= 0) {
@@ -691,29 +976,30 @@
 		fds.events = POLLIN;
 		fds.revents = 0;
 		ret = poll(&fds, 1, milliseconds);
-		if (ret == -1 || ret == 0) {
-			/* Error or timeout */
+		if (ret == 0) {
+			/* Timeout */
 			return ret;
 		}
+		if (ret == -1) {
+			/* Error */
+			register_device_error(dev, strerror(errno));
+			return ret;
+		}
 		else {
 			/* Check for errors on the file descriptor. This will
 			   indicate a device disconnection. */
 			if (fds.revents & (POLLERR | POLLHUP | POLLNVAL))
+				// We cannot use strerror() here as no -1 was returned from poll().
 				return -1;
 		}
 	}
 
 	bytes_read = read(dev->device_handle, data, length);
-	if (bytes_read < 0 && (errno == EAGAIN || errno == EINPROGRESS))
-		bytes_read = 0;
-
-	if (bytes_read >= 0 &&
-	    kernel_version != 0 &&
-	    kernel_version < KERNEL_VERSION(2,6,34) &&
-	    dev->uses_numbered_reports) {
-		/* Work around a kernel bug. Chop off the first byte. */
-		memmove(data, data+1, bytes_read);
-		bytes_read--;
+	if (bytes_read < 0) {
+		if (errno == EAGAIN || errno == EINPROGRESS)
+			bytes_read = 0;
+		else
+			register_device_error(dev, strerror(errno));
 	}
 
 	return bytes_read;
@@ -741,7 +1027,7 @@
 
 	res = ioctl(dev->device_handle, HIDIOCSFEATURE(length), data);
 	if (res < 0)
-		perror("ioctl (SFEATURE)");
+		register_device_error_format(dev, "ioctl (SFEATURE): %s", strerror(errno));
 
 	return res;
 }
@@ -752,18 +1038,32 @@
 
 	res = ioctl(dev->device_handle, HIDIOCGFEATURE(length), data);
 	if (res < 0)
-		perror("ioctl (GFEATURE)");
-
+		register_device_error_format(dev, "ioctl (GFEATURE): %s", strerror(errno));
 
 	return res;
 }
 
+// Not supported by Linux HidRaw yet
+int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length)
+{
+	(void)dev;
+	(void)data;
+	(void)length;
+	return -1;
+}
 
 void HID_API_EXPORT hid_close(hid_device *dev)
 {
 	if (!dev)
 		return;
-	close(dev->device_handle);
+
+	int ret = close(dev->device_handle);
+
+	register_global_error((ret == -1)? strerror(errno): NULL);
+
+	/* Free the device error message */
+	register_device_error(dev, NULL);
+
 	free(dev);
 }
 
@@ -785,11 +1085,24 @@
 
 int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen)
 {
+	(void)dev;
+	(void)string_index;
+	(void)string;
+	(void)maxlen;
 	return -1;
 }
 
 
+/* Passing in NULL means asking for the last global error message. */
 HID_API_EXPORT const wchar_t * HID_API_CALL  hid_error(hid_device *dev)
 {
-	return NULL;
+	if (dev) {
+		if (dev->last_error_str == NULL)
+			return L"Success";
+		return dev->last_error_str;
+	}
+
+	if (last_global_error_str == NULL)
+		return L"Success";
+	return last_global_error_str;
 }
diff --git a/cbits/hidapi/mac/hid.c b/cbits/hidapi/mac/hid.c
--- a/cbits/hidapi/mac/hid.c
+++ b/cbits/hidapi/mac/hid.c
@@ -17,22 +17,28 @@
  files located at the root of the source distribution.
  These files may also be found in the public source
  code repository located at:
-        http://github.com/signal11/hidapi .
+        https://github.com/libusb/hidapi .
 ********************************************************/
 
 /* See Apple Technical Note TN2187 for details on IOHidManager. */
 
 #include <IOKit/hid/IOHIDManager.h>
 #include <IOKit/hid/IOHIDKeys.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/usb/USBSpec.h>
 #include <CoreFoundation/CoreFoundation.h>
 #include <wchar.h>
 #include <locale.h>
 #include <pthread.h>
 #include <sys/time.h>
 #include <unistd.h>
+#include <dlfcn.h>
 
 #include "hidapi.h"
 
+/* As defined in AppKit.h, but we don't need the entire AppKit for a single constant. */
+extern const double NSAppKitVersionNumber;
+
 /* Barrier implementation because Mac OSX doesn't have pthread_barrier.
    It also doesn't have clock_gettime(). So much for POSIX and SUSv2.
    This implementation came from Brent Priddy and was posted on
@@ -47,6 +53,8 @@
 
 static int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count)
 {
+	(void) attr;
+
 	if(count == 0) {
 		errno = EINVAL;
 		return -1;
@@ -122,7 +130,7 @@
 
 static hid_device *new_hid_device(void)
 {
-	hid_device *dev = calloc(1, sizeof(hid_device));
+	hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device));
 	dev->device_handle = NULL;
 	dev->blocking = 1;
 	dev->uses_numbered_reports = 0;
@@ -176,16 +184,32 @@
 	free(dev);
 }
 
+static struct hid_api_version api_version = {
+	.major = HID_API_VERSION_MAJOR,
+	.minor = HID_API_VERSION_MINOR,
+	.patch = HID_API_VERSION_PATCH
+};
+
 static	IOHIDManagerRef hid_mgr = 0x0;
+static	int is_macos_10_10_or_greater = 0;
 
 
 #if 0
-static void register_error(hid_device *device, const char *op)
+static void register_error(hid_device *dev, const char *op)
 {
 
 }
 #endif
 
+static CFArrayRef get_array_property(IOHIDDeviceRef device, CFStringRef key)
+{
+	CFTypeRef ref = IOHIDDeviceGetProperty(device, key);
+	if (ref != NULL && CFGetTypeID(ref) == CFArrayGetTypeID()) {
+		return (CFArrayRef)ref;
+	} else {
+		return NULL;
+	}
+}
 
 static int32_t get_int_property(IOHIDDeviceRef device, CFStringRef key)
 {
@@ -202,6 +226,11 @@
 	return 0;
 }
 
+static CFArrayRef get_usage_pairs(IOHIDDeviceRef device)
+{
+	return get_array_property(device, CFSTR(kIOHIDDeviceUsagePairsKey));
+}
+
 static unsigned short get_vendor_id(IOHIDDeviceRef device)
 {
 	return get_int_property(device, CFSTR(kIOHIDVendorIDKey));
@@ -212,11 +241,6 @@
 	return get_int_property(device, CFSTR(kIOHIDProductIDKey));
 }
 
-static int32_t get_location_id(IOHIDDeviceRef device)
-{
-	return get_int_property(device, CFSTR(kIOHIDLocationIDKey));
-}
-
 static int32_t get_max_report_length(IOHIDDeviceRef device)
 {
 	return get_int_property(device, CFSTR(kIOHIDMaxInputReportSizeKey));
@@ -229,7 +253,7 @@
 	if (!len)
 		return 0;
 
-	str = IOHIDDeviceGetProperty(device, prop);
+	str = (CFStringRef) IOHIDDeviceGetProperty(device, prop);
 
 	buf[0] = 0;
 
@@ -242,18 +266,18 @@
 		len --;
 
 		range.location = 0;
-		range.length = ((size_t)str_len > len)? len: (size_t)str_len;
+		range.length = ((size_t) str_len > len)? len: (size_t) str_len;
 		chars_copied = CFStringGetBytes(str,
 			range,
 			kCFStringEncodingUTF32LE,
-			(char)'?',
+			(char) '?',
 			FALSE,
 			(UInt8*)buf,
 			len * sizeof(wchar_t),
 			&used_buf_len);
 
-		if (chars_copied == len)
-			buf[len] = 0; /* len is decremented above */
+		if (chars_copied <= 0)
+			buf[0] = 0;
 		else
 			buf[chars_copied] = 0;
 
@@ -264,46 +288,6 @@
 
 }
 
-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)
 {
 	return get_string_property(device, CFSTR(kIOHIDSerialNumberKey), buf, len);
@@ -324,39 +308,68 @@
 static wchar_t *dup_wcs(const wchar_t *s)
 {
 	size_t len = wcslen(s);
-	wchar_t *ret = malloc((len+1)*sizeof(wchar_t));
+	wchar_t *ret = (wchar_t*) malloc((len+1)*sizeof(wchar_t));
 	wcscpy(ret, s);
 
 	return ret;
 }
 
-
-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;
-
-	buf[0] = '\0';
-
-	res = get_string_property_utf8(
-		device, CFSTR(kIOHIDTransportKey),
-		transport, sizeof(transport));
-
-	if (!res)
-		return -1;
+	static void *iokit_framework = NULL;
+	typedef io_service_t (*dynamic_IOHIDDeviceGetService_t)(IOHIDDeviceRef device);
+	static dynamic_IOHIDDeviceGetService_t dynamic_IOHIDDeviceGetService = NULL;
 
-	location = get_location_id(device);
-	vid = get_vendor_id(device);
-	pid = get_product_id(device);
+	/* 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/Frameworks/IOKit.framework/IOKit", RTLD_LAZY);
 
-	res = snprintf(buf, len, "%s_%04hx_%04hx_%x",
-                       transport, vid, pid, location);
+		if (iokit_framework != NULL)
+			dynamic_IOHIDDeviceGetService = (dynamic_IOHIDDeviceGetService_t) dlsym(iokit_framework, "IOHIDDeviceGetService");
+	}
 
+	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;
 
-	buf[len-1] = '\0';
-	return res+1;
+		return tmp->service;
+	}
 }
 
 /* Initialize the IOHIDManager. Return 0 for success and -1 for failure. */
@@ -373,12 +386,23 @@
 	return -1;
 }
 
+HID_API_EXPORT const struct hid_api_version* HID_API_CALL hid_version()
+{
+	return &api_version;
+}
+
+HID_API_EXPORT const char* HID_API_CALL hid_version_str()
+{
+	return HID_API_VERSION_STR;
+}
+
 /* Initialize the IOHIDManager if necessary. This is the public function, and
    it is safe to call this function repeatedly. Return 0 for success and -1
    for failure. */
 int HID_API_EXPORT hid_init(void)
 {
 	if (!hid_mgr) {
+		is_macos_10_10_or_greater = (NSAppKitVersionNumber >= 1343); /* NSAppKitVersionNumber10_10 */
 		return init_hid_manager();
 	}
 
@@ -405,6 +429,123 @@
 	} while(res != kCFRunLoopRunFinished && res != kCFRunLoopRunTimedOut);
 }
 
+static struct hid_device_info *create_device_info_with_usage(IOHIDDeviceRef dev, int32_t usage_page, int32_t usage)
+{
+	unsigned short dev_vid;
+	unsigned short dev_pid;
+	int BUF_LEN = 256;
+	wchar_t buf[BUF_LEN];
+
+	struct hid_device_info *cur_dev;
+	io_object_t iokit_dev;
+	kern_return_t res;
+	io_string_t path;
+
+	if (dev == NULL) {
+		return NULL;
+	}
+
+	cur_dev = (struct hid_device_info *)calloc(1, sizeof(struct hid_device_info));
+	if (cur_dev == NULL) {
+		return NULL;
+	}
+
+	dev_vid = get_vendor_id(dev);
+	dev_pid = get_product_id(dev);
+
+	cur_dev->usage_page = usage_page;
+	cur_dev->usage = usage;
+
+	/* Fill out the record */
+	cur_dev->next = NULL;
+
+	/* 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 */
+	get_serial_number(dev, buf, BUF_LEN);
+	cur_dev->serial_number = dup_wcs(buf);
+
+	/* Manufacturer and Product strings */
+	get_manufacturer_string(dev, buf, BUF_LEN);
+	cur_dev->manufacturer_string = dup_wcs(buf);
+	get_product_string(dev, buf, BUF_LEN);
+	cur_dev->product_string = dup_wcs(buf);
+
+	/* VID/PID */
+	cur_dev->vendor_id = dev_vid;
+	cur_dev->product_id = dev_pid;
+
+	/* Release Number */
+	cur_dev->release_number = get_int_property(dev, CFSTR(kIOHIDVersionNumberKey));
+
+	/* Interface Number */
+	/* We can only retrieve the interface number for USB HID devices.
+	 * IOKit always seems to return 0 when querying a standard USB device
+	 * for its interface. */
+	int is_usb_hid = get_int_property(dev, CFSTR(kUSBInterfaceClass)) == kUSBHIDClass;
+	if (is_usb_hid) {
+		/* Get the interface number */
+		cur_dev->interface_number = get_int_property(dev, CFSTR(kUSBInterfaceNumber));
+	} else {
+		cur_dev->interface_number = -1;
+	}
+
+	return cur_dev;
+}
+
+static struct hid_device_info *create_device_info(IOHIDDeviceRef device)
+{
+	const int32_t primary_usage_page = get_int_property(device, CFSTR(kIOHIDPrimaryUsagePageKey));
+	const int32_t primary_usage = get_int_property(device, CFSTR(kIOHIDPrimaryUsageKey));
+
+	/* Primary should always be first, to match previous behavior. */
+	struct hid_device_info *root = create_device_info_with_usage(device, primary_usage_page, primary_usage);
+	struct hid_device_info *cur = root;
+
+	if (!root)
+		return NULL;
+
+	CFArrayRef usage_pairs = get_usage_pairs(device);
+
+	if (usage_pairs != NULL) {
+		struct hid_device_info *next = NULL;
+		for (CFIndex i = 0; i < CFArrayGetCount(usage_pairs); i++) {
+			CFTypeRef dict = CFArrayGetValueAtIndex(usage_pairs, i);
+			if (CFGetTypeID(dict) != CFDictionaryGetTypeID()) {
+				continue;
+			}
+
+			CFTypeRef usage_page_ref, usage_ref;
+			int32_t usage_page, usage;
+
+			if (!CFDictionaryGetValueIfPresent((CFDictionaryRef)dict, CFSTR(kIOHIDDeviceUsagePageKey), &usage_page_ref) ||
+			    !CFDictionaryGetValueIfPresent((CFDictionaryRef)dict, CFSTR(kIOHIDDeviceUsageKey), &usage_ref) ||
+					CFGetTypeID(usage_page_ref) != CFNumberGetTypeID() ||
+					CFGetTypeID(usage_ref) != CFNumberGetTypeID() ||
+					!CFNumberGetValue((CFNumberRef)usage_page_ref, kCFNumberSInt32Type, &usage_page) ||
+					!CFNumberGetValue((CFNumberRef)usage_ref, kCFNumberSInt32Type, &usage)) {
+					continue;
+			}
+			if (usage_page == primary_usage_page && usage == primary_usage)
+				continue; /* Already added. */
+
+			next = create_device_info_with_usage(device, usage_page, usage);
+			cur->next = next;
+			if (next != NULL) {
+				cur = next;
+			}
+		}
+	}
+
+	return root;
+}
+
 struct hid_device_info  HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id)
 {
 	struct hid_device_info *root = NULL; /* return object */
@@ -420,74 +561,61 @@
 	process_pending_events();
 
 	/* Get a list of the Devices */
-	IOHIDManagerSetDeviceMatching(hid_mgr, NULL);
+	CFMutableDictionaryRef matching = NULL;
+	if (vendor_id != 0 || product_id != 0) {
+		matching = CFDictionaryCreateMutable(kCFAllocatorDefault, kIOHIDOptionsTypeNone, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+		if (matching && vendor_id != 0) {
+			CFNumberRef v = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &vendor_id);
+			CFDictionarySetValue(matching, CFSTR(kIOHIDVendorIDKey), v);
+			CFRelease(v);
+		}
+
+		if (matching && product_id != 0) {
+			CFNumberRef p = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &product_id);
+			CFDictionarySetValue(matching, CFSTR(kIOHIDProductIDKey), p);
+			CFRelease(p);
+		}
+	}
+	IOHIDManagerSetDeviceMatching(hid_mgr, matching);
+	if (matching != NULL) {
+		CFRelease(matching);
+	}
+
 	CFSetRef device_set = IOHIDManagerCopyDevices(hid_mgr);
+	if (device_set == NULL) {
+		return NULL;
+	}
 
 	/* Convert the list into a C array so we can iterate easily. */
 	num_devices = CFSetGetCount(device_set);
-	IOHIDDeviceRef *device_array = calloc(num_devices, sizeof(IOHIDDeviceRef));
+	IOHIDDeviceRef *device_array = (IOHIDDeviceRef*) calloc(num_devices, sizeof(IOHIDDeviceRef));
 	CFSetGetValues(device_set, (const void **) device_array);
 
 	/* Iterate over each device, making an entry for it. */
 	for (i = 0; i < num_devices; i++) {
-		unsigned short dev_vid;
-		unsigned short dev_pid;
-		#define BUF_LEN 256
-		wchar_t buf[BUF_LEN];
-		char cbuf[BUF_LEN];
 
 		IOHIDDeviceRef dev = device_array[i];
-
-        if (!dev) {
-            continue;
-        }
-		dev_vid = get_vendor_id(dev);
-		dev_pid = get_product_id(dev);
-
-		/* Check the VID/PID against the arguments */
-		if ((vendor_id == 0x0 || vendor_id == dev_vid) &&
-		    (product_id == 0x0 || product_id == dev_pid)) {
-			struct hid_device_info *tmp;
-			size_t len;
-
-			/* VID/PID match. Create the record. */
-			tmp = malloc(sizeof(struct hid_device_info));
-			if (cur_dev) {
-				cur_dev->next = tmp;
-			}
-			else {
-				root = tmp;
-			}
-			cur_dev = tmp;
-
-			/* Get the Usage Page and Usage for this device. */
-			cur_dev->usage_page = get_int_property(dev, CFSTR(kIOHIDPrimaryUsagePageKey));
-			cur_dev->usage = get_int_property(dev, CFSTR(kIOHIDPrimaryUsageKey));
-
-			/* Fill out the record */
-			cur_dev->next = NULL;
-			len = make_path(dev, cbuf, sizeof(cbuf));
-			cur_dev->path = strdup(cbuf);
-
-			/* Serial Number */
-			get_serial_number(dev, buf, BUF_LEN);
-			cur_dev->serial_number = dup_wcs(buf);
-
-			/* Manufacturer and Product strings */
-			get_manufacturer_string(dev, buf, BUF_LEN);
-			cur_dev->manufacturer_string = dup_wcs(buf);
-			get_product_string(dev, buf, BUF_LEN);
-			cur_dev->product_string = dup_wcs(buf);
+		if (!dev) {
+			continue;
+		}
 
-			/* VID/PID */
-			cur_dev->vendor_id = dev_vid;
-			cur_dev->product_id = dev_pid;
+		struct hid_device_info *tmp = create_device_info(dev);
+		if (tmp == NULL) {
+			continue;
+		}
 
-			/* Release Number */
-			cur_dev->release_number = get_int_property(dev, CFSTR(kIOHIDVersionNumberKey));
+		if (cur_dev) {
+			cur_dev->next = tmp;
+		}
+		else {
+			root = tmp;
+		}
+		cur_dev = tmp;
 
-			/* Interface Number (Unsupported on Mac)*/
-			cur_dev->interface_number = -1;
+		/* move the pointer to the tail of returnd list */
+		while (cur_dev->next != NULL) {
+			cur_dev = cur_dev->next;
 		}
 	}
 
@@ -551,8 +679,11 @@
 static void hid_device_removal_callback(void *context, IOReturn result,
                                         void *sender)
 {
+	(void) result;
+	(void) sender;
+
 	/* Stop the Run Loop for this device. */
-	hid_device *d = context;
+	hid_device *d = (hid_device*) context;
 
 	d->disconnected = 1;
 	CFRunLoopStop(d->run_loop);
@@ -565,12 +696,17 @@
                          IOHIDReportType report_type, uint32_t report_id,
                          uint8_t *report, CFIndex report_length)
 {
+	(void) result;
+	(void) sender;
+	(void) report_type;
+	(void) report_id;
+
 	struct input_report *rpt;
-	hid_device *dev = context;
+	hid_device *dev = (hid_device*) context;
 
 	/* Make a new Input Report object */
-	rpt = calloc(1, sizeof(struct input_report));
-	rpt->data = calloc(1, report_length);
+	rpt = (struct input_report*) calloc(1, sizeof(struct input_report));
+	rpt->data = (uint8_t*) calloc(1, report_length);
 	memcpy(rpt->data, report, report_length);
 	rpt->len = report_length;
 	rpt->next = NULL;
@@ -609,17 +745,17 @@
 
 }
 
-/* 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. */
 static void perform_signal_callback(void *context)
 {
-	hid_device *dev = context;
+	hid_device *dev = (hid_device*) context;
 	CFRunLoopStop(dev->run_loop); /*TODO: CFRunLoopGetCurrent()*/
 }
 
 static void *read_thread(void *param)
 {
-	hid_device *dev = param;
+	hid_device *dev = (hid_device*) param;
 	SInt32 code;
 
 	/* Move the device's run loop to this thread. */
@@ -667,7 +803,7 @@
 	/* Now that the read thread is stopping, Wake any threads which are
 	   waiting on data (in hid_read_timeout()). Do this under a mutex to
 	   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. */
 	pthread_mutex_lock(&dev->mutex);
 	pthread_cond_broadcast(&dev->condition);
@@ -681,114 +817,142 @@
 	return NULL;
 }
 
+/* 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;
-
-	dev = new_hid_device();
+	io_registry_entry_t entry = MACH_PORT_NULL;
+	IOReturn ret = kIOReturnInvalid;
 
 	/* Set up the HID Manager if it hasn't been done */
 	if (hid_init() < 0)
-		return NULL;
-
-	/* give the IOHIDManager a chance to update itself */
-	process_pending_events();
+		goto return_error;
 
-	CFSetRef device_set = IOHIDManagerCopyDevices(hid_mgr);
+	dev = new_hid_device();
 
-	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];
+	/* 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;
+	}
 
-		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 an IOHIDDevice for the entry */
+	dev->device_handle = IOHIDDeviceCreate(kCFAllocatorDefault, entry);
+	if (dev->device_handle == NULL) {
+		/* Error creating the HID device */
+		goto return_error;
+	}
 
-				free(device_array);
-				CFRetain(os_dev);
-				CFRelease(device_set);
-				dev->device_handle = os_dev;
+	/* Open the IOHIDDevice */
+	ret = IOHIDDeviceOpen(dev->device_handle, kIOHIDOptionsTypeSeizeDevice);
+	if (ret == kIOReturnSuccess) {
+		char str[32];
 
-				/* 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));
+		/* Create the buffers for receiving data */
+		dev->max_input_report_len = (CFIndex) get_max_report_length(dev->device_handle);
+		dev->input_report_buf = (uint8_t*) calloc(dev->max_input_report_len, sizeof(uint8_t));
 
-				/* 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);
+		/* Create the Run Loop Mode for this device.
+		   printing the reference seems to work. */
+		sprintf(str, "HIDAPI_%p", (void*) dev->device_handle);
+		dev->run_loop_mode =
+			CFStringCreateWithCString(NULL, str, kCFStringEncodingASCII);
 
-				/* 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);
+		/* 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);
 
-				/* Start the read thread */
-				pthread_create(&dev->thread, NULL, read_thread, dev);
+		/* Start the read thread */
+		pthread_create(&dev->thread, NULL, read_thread, dev);
 
-				/* Wait here for the read thread to be initialized. */
-				pthread_barrier_wait(&dev->barrier);
+		/* Wait here for the read thread to be initialized. */
+		pthread_barrier_wait(&dev->barrier);
 
-				return dev;
-			}
-			else {
-				goto return_error;
-			}
-		}
+		IOObjectRelease(entry);
+		return dev;
 	}
+	else {
+		goto return_error;
+	}
 
 return_error:
-	free(device_array);
-	CFRelease(device_set);
+	if (dev->device_handle != NULL)
+		CFRelease(dev->device_handle);
+
+	if (entry != MACH_PORT_NULL)
+		IOObjectRelease(entry);
+
 	free_hid_device(dev);
 	return NULL;
 }
 
 static int set_report(hid_device *dev, IOHIDReportType type, const unsigned char *data, size_t length)
 {
-	const unsigned char *data_to_send;
-	size_t length_to_send;
+	const unsigned char *data_to_send = data;
+	CFIndex length_to_send = length;
 	IOReturn res;
-
-	/* Return if the device has been disconnected. */
-	if (dev->disconnected)
-		return -1;
+	const unsigned char report_id = data[0];
 
-	if (data[0] == 0x0) {
+	if (report_id == 0x0) {
 		/* Not using numbered Reports.
 		   Don't send the report number. */
 		data_to_send = data+1;
 		length_to_send = length-1;
 	}
-	else {
-		/* Using numbered Reports.
-		   Send the Report Number */
-		data_to_send = data;
-		length_to_send = length;
+
+	/* Avoid crash if the device has been unplugged. */
+	if (dev->disconnected) {
+		return -1;
 	}
 
-	if (!dev->disconnected) {
-		res = IOHIDDeviceSetReport(dev->device_handle,
-					   type,
-					   data[0], /* Report ID*/
-					   data_to_send, length_to_send);
+	res = IOHIDDeviceSetReport(dev->device_handle,
+	                           type,
+	                           report_id,
+	                           data_to_send, length_to_send);
 
-		if (res == kIOReturnSuccess) {
-			return length;
+	if (res == kIOReturnSuccess) {
+		return length;
+	}
+
+	return -1;
+}
+
+static int get_report(hid_device *dev, IOHIDReportType type, unsigned char *data, size_t length)
+{
+	unsigned char *report = data;
+	CFIndex report_length = length;
+	IOReturn res = kIOReturnSuccess;
+	const unsigned char report_id = data[0];
+
+	if (report_id == 0x0) {
+		/* Not using numbered Reports.
+		   Don't send the report number. */
+		report = data+1;
+		report_length = length-1;
+	}
+
+	/* Avoid crash if the device has been unplugged. */
+	if (dev->disconnected) {
+		return -1;
+	}
+
+	res = IOHIDDeviceGetReport(dev->device_handle,
+	                           type,
+	                           report_id,
+	                           report, &report_length);
+
+	if (res == kIOReturnSuccess) {
+		if (report_id == 0x0) { /* 0 report number still present at the beginning */
+			report_length++;
 		}
-		else
-			return -1;
+		return report_length;
 	}
 
 	return -1;
@@ -821,7 +985,7 @@
 			return res;
 
 		/* A res of 0 means we may have been signaled or it may
-		   be a spurious wakeup. Check to see that there's acutally
+		   be a spurious wakeup. Check to see that there's actually
 		   data in the queue before returning, and if not, go back
 		   to sleep. See the pthread_cond_timedwait() man page for
 		   details. */
@@ -841,7 +1005,7 @@
 			return res;
 
 		/* A res of 0 means we may have been signaled or it may
-		   be a spurious wakeup. Check to see that there's acutally
+		   be a spurious wakeup. Check to see that there's actually
 		   data in the queue before returning, and if not, go back
 		   to sleep. See the pthread_cond_timedwait() man page for
 		   details. */
@@ -948,31 +1112,23 @@
 
 int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length)
 {
-	CFIndex len = length;
-	IOReturn res;
-
-	/* Return if the device has been unplugged. */
-	if (dev->disconnected)
-		return -1;
-
-	res = IOHIDDeviceGetReport(dev->device_handle,
-	                           kIOHIDReportTypeFeature,
-	                           data[0], /* Report ID */
-	                           data, &len);
-	if (res == kIOReturnSuccess)
-		return len;
-	else
-		return -1;
+	return get_report(dev, kIOHIDReportTypeFeature, data, length);
 }
 
+int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length)
+{	
+	return get_report(dev, kIOHIDReportTypeInput, data, length);
+}
 
 void HID_API_EXPORT hid_close(hid_device *dev)
 {
 	if (!dev)
 		return;
 
-	/* Disconnect the report callback before close. */
-	if (!dev->disconnected) {
+	/* Disconnect the report callback before close.
+	   See comment below.
+	*/
+	if (is_macos_10_10_or_greater || !dev->disconnected) {
 		IOHIDDeviceRegisterInputReportCallback(
 			dev->device_handle, dev->input_report_buf, dev->max_input_report_len,
 			NULL, dev);
@@ -996,8 +1152,14 @@
 
 	/* Close the OS handle to the device, but only if it's not
 	   been unplugged. If it's been unplugged, then calling
-	   IOHIDDeviceClose() will crash. */
-	if (!dev->disconnected) {
+	   IOHIDDeviceClose() will crash.
+
+	   UPD: The crash part was true in/until some version of macOS.
+	   Starting with macOS 10.15, there is an opposite effect in some environments:
+	   crash happenes if IOHIDDeviceClose() is not called.
+	   Not leaking a resource in all tested environments.
+	*/
+	if (is_macos_10_10_or_greater || !dev->disconnected) {
 		IOHIDDeviceClose(dev->device_handle, kIOHIDOptionsTypeSeizeDevice);
 	}
 
@@ -1029,6 +1191,11 @@
 
 int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen)
 {
+	(void) dev;
+	(void) string_index;
+	(void) string;
+	(void) maxlen;
+
 	/* TODO: */
 
 	return 0;
@@ -1037,9 +1204,10 @@
 
 HID_API_EXPORT const wchar_t * HID_API_CALL  hid_error(hid_device *dev)
 {
+	(void) dev;
 	/* TODO: */
 
-	return NULL;
+	return L"hid_error is not implemented yet";
 }
 
 
@@ -1049,6 +1217,11 @@
 
 
 #if 0
+static int32_t get_location_id(IOHIDDeviceRef device)
+{
+	return get_int_property(device, CFSTR(kIOHIDLocationIDKey));
+}
+
 static int32_t get_usage(IOHIDDeviceRef device)
 {
 	int32_t res;
diff --git a/cbits/hidapi/windows/hid.c b/cbits/hidapi/windows/hid.c
--- a/cbits/hidapi/windows/hid.c
+++ b/cbits/hidapi/windows/hid.c
@@ -17,7 +17,7 @@
  files located at the root of the source distribution.
  These files may also be found in the public source
  code repository located at:
-        http://github.com/signal11/hidapi .
+        https://github.com/libusb/hidapi .
 ********************************************************/
 
 #include <windows.h>
@@ -36,6 +36,10 @@
 #define _wcsdup wcsdup
 #endif
 
+/* 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*/
 
 #ifdef __cplusplus
@@ -51,6 +55,7 @@
 	#define HID_OUT_CTL_CODE(id)  \
 		CTL_CODE(FILE_DEVICE_KEYBOARD, (id), METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
 	#define IOCTL_HID_GET_FEATURE                   HID_OUT_CTL_CODE(100)
+	#define IOCTL_HID_GET_INPUT_REPORT              HID_OUT_CTL_CODE(104)
 
 #ifdef __cplusplus
 } /* extern "C" */
@@ -62,6 +67,9 @@
 
 #include "hidapi.h"
 
+#undef MIN
+#define MIN(x,y) ((x) < (y)? (x): (y))
+
 #ifdef _MSC_VER
 	/* Thanks Microsoft, but I know how to use strncpy(). */
 	#pragma warning(disable:4996)
@@ -71,6 +79,12 @@
 extern "C" {
 #endif
 
+static struct hid_api_version api_version = {
+	.major = HID_API_VERSION_MAJOR,
+	.minor = HID_API_VERSION_MINOR,
+	.patch = HID_API_VERSION_PATCH
+};
+
 #ifndef HIDAPI_USE_DDK
 	/* Since we're not building with the DDK, and the HID header
 	   files aren't part of the SDK, we have to define all this
@@ -102,6 +116,7 @@
 	typedef BOOLEAN (__stdcall *HidD_GetProductString_)(HANDLE handle, PVOID buffer, ULONG buffer_len);
 	typedef BOOLEAN (__stdcall *HidD_SetFeature_)(HANDLE handle, PVOID data, ULONG length);
 	typedef BOOLEAN (__stdcall *HidD_GetFeature_)(HANDLE handle, PVOID data, ULONG length);
+	typedef BOOLEAN (__stdcall *HidD_GetInputReport_)(HANDLE handle, PVOID data, ULONG length);
 	typedef BOOLEAN (__stdcall *HidD_GetIndexedString_)(HANDLE handle, ULONG string_index, PVOID buffer, ULONG buffer_len);
 	typedef BOOLEAN (__stdcall *HidD_GetPreparsedData_)(HANDLE handle, PHIDP_PREPARSED_DATA *preparsed_data);
 	typedef BOOLEAN (__stdcall *HidD_FreePreparsedData_)(PHIDP_PREPARSED_DATA preparsed_data);
@@ -114,6 +129,7 @@
 	static HidD_GetProductString_ HidD_GetProductString;
 	static HidD_SetFeature_ HidD_SetFeature;
 	static HidD_GetFeature_ HidD_GetFeature;
+	static HidD_GetInputReport_ HidD_GetInputReport;
 	static HidD_GetIndexedString_ HidD_GetIndexedString;
 	static HidD_GetPreparsedData_ HidD_GetPreparsedData;
 	static HidD_FreePreparsedData_ HidD_FreePreparsedData;
@@ -129,11 +145,14 @@
 		BOOL blocking;
 		USHORT output_report_length;
 		size_t input_report_length;
+		USHORT feature_report_length;
+		unsigned char *feature_buf;
 		void *last_error_str;
 		DWORD last_error_num;
 		BOOL read_pending;
 		char *read_buf;
 		OVERLAPPED ol;
+		OVERLAPPED write_ol;			  
 };
 
 static hid_device *new_hid_device()
@@ -143,12 +162,16 @@
 	dev->blocking = TRUE;
 	dev->output_report_length = 0;
 	dev->input_report_length = 0;
+	dev->feature_report_length = 0;
+	dev->feature_buf = NULL;
 	dev->last_error_str = NULL;
 	dev->last_error_num = 0;
 	dev->read_pending = FALSE;
 	dev->read_buf = NULL;
 	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);
+	memset(&dev->write_ol, 0, sizeof(dev->write_ol));
+	dev->write_ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*inital state f=nonsignaled*/, NULL);											  
 
 	return dev;
 }
@@ -156,23 +179,25 @@
 static void free_hid_device(hid_device *dev)
 {
 	CloseHandle(dev->ol.hEvent);
+	CloseHandle(dev->write_ol.hEvent);							   
 	CloseHandle(dev->device_handle);
 	LocalFree(dev->last_error_str);
+	free(dev->feature_buf);
 	free(dev->read_buf);
 	free(dev);
 }
 
-static void register_error(hid_device *device, const char *op)
+static void register_error(hid_device *dev, const char *op)
 {
 	WCHAR *ptr, *msg;
-
+	(void)op; // unreferenced  param
 	FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
 		FORMAT_MESSAGE_FROM_SYSTEM |
 		FORMAT_MESSAGE_IGNORE_INSERTS,
 		NULL,
 		GetLastError(),
 		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
-		(LPVOID)&msg, 0/*sz*/,
+		(LPWSTR)&msg, 0/*sz*/,
 		NULL);
 	
 	/* Get rid of the CR and LF that FormatMessage() sticks at the
@@ -188,8 +213,8 @@
 
 	/* Store the message off in the Device entry so that
 	   the hid_error() function can pick it up. */
-	LocalFree(device->last_error_str);
-	device->last_error_str = msg;
+	LocalFree(dev->last_error_str);
+	dev->last_error_str = msg;
 }
 
 #ifndef HIDAPI_USE_DDK
@@ -197,6 +222,10 @@
 {
 	lib_handle = LoadLibraryA("hid.dll");
 	if (lib_handle) {
+#if defined(__GNUC__)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wcast-function-type"
+#endif
 #define RESOLVE(x) x = (x##_)GetProcAddress(lib_handle, #x); if (!x) return -1;
 		RESOLVE(HidD_GetAttributes);
 		RESOLVE(HidD_GetSerialNumberString);
@@ -204,12 +233,16 @@
 		RESOLVE(HidD_GetProductString);
 		RESOLVE(HidD_SetFeature);
 		RESOLVE(HidD_GetFeature);
+		RESOLVE(HidD_GetInputReport);
 		RESOLVE(HidD_GetIndexedString);
 		RESOLVE(HidD_GetPreparsedData);
 		RESOLVE(HidD_FreePreparsedData);
 		RESOLVE(HidP_GetCaps);
 		RESOLVE(HidD_SetNumInputBuffers);
 #undef RESOLVE
+#if defined(__GNUC__)
+# pragma GCC diagnostic pop
+#endif
 	}
 	else
 		return -1;
@@ -218,13 +251,11 @@
 }
 #endif
 
-static HANDLE open_device(const char *path, BOOL enumerate)
+static HANDLE open_device(const char *path, BOOL open_rw)
 {
 	HANDLE handle;
-	DWORD desired_access = (enumerate)? 0: (GENERIC_WRITE | GENERIC_READ);
-	DWORD share_mode = (enumerate)?
-	                      FILE_SHARE_READ|FILE_SHARE_WRITE:
-	                      FILE_SHARE_READ;
+	DWORD desired_access = (open_rw)? (GENERIC_WRITE | GENERIC_READ): 0;
+	DWORD share_mode = FILE_SHARE_READ|FILE_SHARE_WRITE;
 
 	handle = CreateFileA(path,
 		desired_access,
@@ -237,6 +268,16 @@
 	return handle;
 }
 
+HID_API_EXPORT const struct hid_api_version* HID_API_CALL hid_version()
+{
+	return &api_version;
+}
+
+HID_API_EXPORT const char* HID_API_CALL hid_version_str()
+{
+	return HID_API_VERSION_STR;
+}
+
 int HID_API_EXPORT hid_init(void)
 {
 #ifndef HIDAPI_USE_DDK
@@ -268,14 +309,16 @@
 	struct hid_device_info *root = NULL; /* return object */
 	struct hid_device_info *cur_dev = NULL;
 
-	/* Windows objects for interacting with the driver. */
+	/* Hard-coded GUID retreived by HidD_GetHidGuid */
 	GUID InterfaceClassGuid = {0x4d1e55b2, 0xf16f, 0x11cf, {0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30} };
+
+	/* Windows objects for interacting with the driver. */
 	SP_DEVINFO_DATA devinfo_data;
 	SP_DEVICE_INTERFACE_DATA device_interface_data;
 	SP_DEVICE_INTERFACE_DETAIL_DATA_A *device_interface_detail_data = NULL;
 	HDEVINFO device_info_set = INVALID_HANDLE_VALUE;
+	char driver_name[256];
 	int device_index = 0;
-	int i;
 
 	if (hid_init() < 0)
 		return NULL;
@@ -337,35 +380,23 @@
 			goto cont;
 		}
 
-		/* Make sure this device is of Setup Class "HIDClass" and has a
-		   driver bound to it. */
-		for (i = 0; ; i++) {
-			char driver_name[256];
+		/* Populate devinfo_data. This function will return failure
+		   when the device with such index doesn't exist. We've already checked it does. */
+		res = SetupDiEnumDeviceInfo(device_info_set, device_index, &devinfo_data);
+		if (!res)
+			goto cont;
 
-			/* Populate devinfo_data. This function will return failure
-			   when there are no more interfaces left. */
-			res = SetupDiEnumDeviceInfo(device_info_set, i, &devinfo_data);
-			if (!res)
-				goto cont;
 
-			res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data,
-			               SPDRP_CLASS, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL);
-			if (!res)
-				goto cont;
-
-			if (strcmp(driver_name, "HIDClass") == 0) {
-				/* See if there's a driver bound. */
-				res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data,
-				           SPDRP_DRIVER, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL);
-				if (res)
-					break;
-			}
-		}
+		/* Make sure this device has a driver bound to it. */
+		res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data,
+			   SPDRP_DRIVER, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL);
+		if (!res)
+			goto cont;
 
 		//wprintf(L"HandleName: %s\n", device_interface_detail_data->DevicePath);
 
 		/* Open a handle to the device */
-		write_handle = open_device(device_interface_detail_data->DevicePath, TRUE);
+		write_handle = open_device(device_interface_detail_data->DevicePath, FALSE);
 
 		/* Check validity of write_handle. */
 		if (write_handle == INVALID_HANDLE_VALUE) {
@@ -390,7 +421,6 @@
 			struct hid_device_info *tmp;
 			PHIDP_PREPARSED_DATA pp_data = NULL;
 			HIDP_CAPS caps;
-			BOOLEAN res;
 			NTSTATUS nt_res;
 			wchar_t wstr[WSTR_LEN]; /* TODO: Determine Size */
 			size_t len;
@@ -430,6 +460,7 @@
 				cur_dev->path = NULL;
 
 			/* Serial Number */
+			wstr[0]= 0x0000;
 			res = HidD_GetSerialNumberString(write_handle, wstr, sizeof(wstr));
 			wstr[WSTR_LEN-1] = 0x0000;
 			if (res) {
@@ -437,6 +468,7 @@
 			}
 
 			/* Manufacturer String */
+			wstr[0]= 0x0000;
 			res = HidD_GetManufacturerString(write_handle, wstr, sizeof(wstr));
 			wstr[WSTR_LEN-1] = 0x0000;
 			if (res) {
@@ -444,6 +476,7 @@
 			}
 
 			/* Product String */
+			wstr[0]= 0x0000;
 			res = HidD_GetProductString(write_handle, wstr, sizeof(wstr));
 			wstr[WSTR_LEN-1] = 0x0000;
 			if (res) {
@@ -523,7 +556,7 @@
 		if (cur_dev->vendor_id == vendor_id &&
 		    cur_dev->product_id == product_id) {
 			if (serial_number) {
-				if (wcscmp(serial_number, cur_dev->serial_number) == 0) {
+				if (cur_dev->serial_number && wcscmp(serial_number, cur_dev->serial_number) == 0) {
 					path_to_open = cur_dev->path;
 					break;
 				}
@@ -561,13 +594,23 @@
 	dev = new_hid_device();
 
 	/* Open a handle to the device */
-	dev->device_handle = open_device(path, FALSE);
+	dev->device_handle = open_device(path, TRUE);
 
 	/* Check validity of write_handle. */
 	if (dev->device_handle == INVALID_HANDLE_VALUE) {
-		/* Unable to open the device. */
-		register_error(dev, "CreateFile");
-		goto err;
+		/* System devices, such as keyboards and mice, cannot be opened in
+		   read-write mode, because the system takes exclusive control over
+		   them.  This is to prevent keyloggers.  However, feature reports
+		   can still be sent and received.  Retry opening the device, but
+		   without read/write access. */
+		dev->device_handle = open_device(path, FALSE);
+
+		/* Check the validity of the limited device_handle. */
+		if (dev->device_handle == INVALID_HANDLE_VALUE) {
+			/* Unable to open the device, even without read-write mode. */
+			register_error(dev, "CreateFile");
+			goto err;
+		}
 	}
 
 	/* Set the Input Report buffer size to 64 reports. */
@@ -590,6 +633,7 @@
 	}
 	dev->output_report_length = caps.OutputReportByteLength;
 	dev->input_report_length = caps.InputReportByteLength;
+	dev->feature_report_length = caps.FeatureReportByteLength;
 	HidD_FreePreparsedData(pp_data);
 
 	dev->read_buf = (char*) malloc(dev->input_report_length);
@@ -605,12 +649,12 @@
 
 int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length)
 {
-	DWORD bytes_written;
+	DWORD bytes_written = 0;
+	int function_result = -1;
 	BOOL res;
+	BOOL overlapped = FALSE;
 
-	OVERLAPPED ol;
 	unsigned char *buf;
-	memset(&ol, 0, sizeof(ol));
 
 	/* Make sure the right number of bytes are passed to WriteFile. Windows
 	   expects the number of bytes which are in the _longest_ report (plus
@@ -630,32 +674,44 @@
 		length = dev->output_report_length;
 	}
 
-	res = WriteFile(dev->device_handle, buf, length, NULL, &ol);
+	res = WriteFile(dev->device_handle, buf, (DWORD) length, NULL, &dev->write_ol);
 	
 	if (!res) {
 		if (GetLastError() != ERROR_IO_PENDING) {
 			/* WriteFile() failed. Return error. */
 			register_error(dev, "WriteFile");
-			bytes_written = -1;
 			goto end_of_function;
 		}
+		overlapped = TRUE;
 	}
 
-	/* Wait here until the write is done. This makes
-	   hid_write() synchronous. */
-	res = GetOverlappedResult(dev->device_handle, &ol, &bytes_written, TRUE/*wait*/);
-	if (!res) {
-		/* The Write operation failed. */
-		register_error(dev, "WriteFile");
-		bytes_written = -1;
-		goto end_of_function;
+	if (overlapped) {
+		/* Wait for the transaction to complete. This makes
+		   hid_write() synchronous. */
+		res = WaitForSingleObject(dev->write_ol.hEvent, 1000);
+		if (res != WAIT_OBJECT_0) {
+			/* There was a Timeout. */
+			register_error(dev, "WriteFile/WaitForSingleObject Timeout");
+			goto end_of_function;
+		}
+
+		/* Get the result. */
+		res = GetOverlappedResult(dev->device_handle, &dev->write_ol, &bytes_written, FALSE/*wait*/);
+		if (res) {
+			function_result = bytes_written;
+		}
+		else {
+			/* The Write operation failed. */
+			register_error(dev, "WriteFile");
+			goto end_of_function;
+		}
 	}
 
 end_of_function:
 	if (buf != data)
 		free(buf);
 
-	return bytes_written;
+	return function_result;
 }
 
 
@@ -663,7 +719,8 @@
 {
 	DWORD bytes_read = 0;
 	size_t copy_len = 0;
-	BOOL res;
+	BOOL res = FALSE;
+	BOOL overlapped = FALSE;
 
 	/* Copy the handle for convenience. */
 	HANDLE ev = dev->ol.hEvent;
@@ -673,7 +730,7 @@
 		dev->read_pending = TRUE;
 		memset(dev->read_buf, 0, dev->input_report_length);
 		ResetEvent(ev);
-		res = ReadFile(dev->device_handle, dev->read_buf, dev->input_report_length, &bytes_read, &dev->ol);
+		res = ReadFile(dev->device_handle, dev->read_buf, (DWORD) dev->input_report_length, &bytes_read, &dev->ol);
 		
 		if (!res) {
 			if (GetLastError() != ERROR_IO_PENDING) {
@@ -683,24 +740,29 @@
 				dev->read_pending = FALSE;
 				goto end_of_function;
 			}
-		}
+			overlapped = TRUE;	   
+		}																		   
 	}
+	else {
+		overlapped = TRUE;	
+	}
 
-	if (milliseconds >= 0) {
-		/* See if there is any data yet. */
-		res = WaitForSingleObject(ev, milliseconds);
-		if (res != WAIT_OBJECT_0) {
-			/* There was no data this time. Return zero bytes available,
-			   but leave the Overlapped I/O running. */
-			return 0;
+	if (overlapped) {
+		if (milliseconds >= 0) {
+			/* See if there is any data yet. */
+			res = WaitForSingleObject(ev, milliseconds);
+			if (res != WAIT_OBJECT_0) {
+				/* There was no data this time. Return zero bytes available,
+				   but leave the Overlapped I/O running. */
+				return 0;
+			}
 		}
-	}
 
-	/* Either WaitForSingleObject() told us that ReadFile has completed, or
-	   we are in non-blocking mode. Get the number of bytes read. The actual
-	   data has been copied to the data[] array which was passed to ReadFile(). */
-	res = GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, TRUE/*wait*/);
-	
+		/* Either WaitForSingleObject() told us that ReadFile has completed, or
+		   we are in non-blocking mode. Get the number of bytes read. The actual
+		   data has been copied to the data[] array which was passed to ReadFile(). */
+		res = GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, TRUE/*wait*/);
+	}
 	/* Set pending back to false, even if GetOverlappedResult() returned error. */
 	dev->read_pending = FALSE;
 
@@ -727,7 +789,7 @@
 		return -1;
 	}
 	
-	return copy_len;
+	return (int) copy_len;
 }
 
 int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length)
@@ -743,13 +805,35 @@
 
 int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length)
 {
-	BOOL res = HidD_SetFeature(dev->device_handle, (PVOID)data, length);
+	BOOL res = FALSE;
+	unsigned char *buf;
+	size_t length_to_send;
+
+	/* Windows expects at least caps.FeatureReportByteLength bytes passed
+	   to HidD_SetFeature(), even if the report is shorter. Any less sent and
+	   the function fails with error ERROR_INVALID_PARAMETER set. Any more
+	   and HidD_SetFeature() silently truncates the data sent in the report
+	   to caps.FeatureReportByteLength. */
+	if (length >= dev->feature_report_length) {
+		buf = (unsigned char *) data;
+		length_to_send = length;
+	} else {
+		if (dev->feature_buf == NULL)
+			dev->feature_buf = (unsigned char *) malloc(dev->feature_report_length);
+		buf = dev->feature_buf;
+		memcpy(buf, data, length);
+		memset(buf + length, 0, dev->feature_report_length - length);
+		length_to_send = dev->feature_report_length;
+	}
+
+	res = HidD_SetFeature(dev->device_handle, (PVOID)buf, (DWORD) length_to_send);
+
 	if (!res) {
 		register_error(dev, "HidD_SetFeature");
 		return -1;
 	}
 
-	return length;
+	return (int) length;
 }
 
 
@@ -771,8 +855,8 @@
 
 	res = DeviceIoControl(dev->device_handle,
 		IOCTL_HID_GET_FEATURE,
-		data, length,
-		data, length,
+		data, (DWORD) length,
+		data, (DWORD) length,
 		&bytes_returned, &ol);
 
 	if (!res) {
@@ -801,6 +885,55 @@
 #endif
 }
 
+
+int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length)
+{
+	BOOL res;
+#if 0
+	res = HidD_GetInputReport(dev->device_handle, data, length);
+	if (!res) {
+		register_error(dev, "HidD_GetInputReport");
+		return -1;
+	}
+	return length;
+#else
+	DWORD bytes_returned;
+
+	OVERLAPPED ol;
+	memset(&ol, 0, sizeof(ol));
+
+	res = DeviceIoControl(dev->device_handle,
+		IOCTL_HID_GET_INPUT_REPORT,
+		data, (DWORD) length,
+		data, (DWORD) length,
+		&bytes_returned, &ol);
+
+	if (!res) {
+		if (GetLastError() != ERROR_IO_PENDING) {
+			/* DeviceIoControl() failed. Return error. */
+			register_error(dev, "Send Input Report DeviceIoControl");
+			return -1;
+		}
+	}
+
+	/* Wait here until the write is done. This makes
+	   hid_get_feature_report() synchronous. */
+	res = GetOverlappedResult(dev->device_handle, &ol, &bytes_returned, TRUE/*wait*/);
+	if (!res) {
+		/* The operation failed. */
+		register_error(dev, "Send Input Report GetOverLappedResult");
+		return -1;
+	}
+
+	/* bytes_returned does not include the first byte which contains the
+	   report ID. The data buffer actually contains one more byte than
+	   bytes_returned. */
+	bytes_returned++;
+
+	return bytes_returned;
+#endif
+}
+
 void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev)
 {
 	if (!dev)
@@ -813,7 +946,7 @@
 {
 	BOOL res;
 
-	res = HidD_GetManufacturerString(dev->device_handle, string, sizeof(wchar_t) * maxlen);
+	res = HidD_GetManufacturerString(dev->device_handle, string, sizeof(wchar_t) * (DWORD) MIN(maxlen, MAX_STRING_WCHARS));
 	if (!res) {
 		register_error(dev, "HidD_GetManufacturerString");
 		return -1;
@@ -826,7 +959,7 @@
 {
 	BOOL res;
 
-	res = HidD_GetProductString(dev->device_handle, string, sizeof(wchar_t) * maxlen);
+	res = HidD_GetProductString(dev->device_handle, string, sizeof(wchar_t) * (DWORD) MIN(maxlen, MAX_STRING_WCHARS));
 	if (!res) {
 		register_error(dev, "HidD_GetProductString");
 		return -1;
@@ -839,7 +972,7 @@
 {
 	BOOL res;
 
-	res = HidD_GetSerialNumberString(dev->device_handle, string, sizeof(wchar_t) * maxlen);
+	res = HidD_GetSerialNumberString(dev->device_handle, string, sizeof(wchar_t) * (DWORD) MIN(maxlen, MAX_STRING_WCHARS));
 	if (!res) {
 		register_error(dev, "HidD_GetSerialNumberString");
 		return -1;
@@ -852,7 +985,7 @@
 {
 	BOOL res;
 
-	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) * (DWORD) MIN(maxlen, MAX_STRING_WCHARS));
 	if (!res) {
 		register_error(dev, "HidD_GetIndexedString");
 		return -1;
@@ -864,7 +997,14 @@
 
 HID_API_EXPORT const wchar_t * HID_API_CALL  hid_error(hid_device *dev)
 {
-	return (wchar_t*)dev->last_error_str;
+	if (dev) {
+		if (dev->last_error_str == NULL)
+			return L"Success";
+		return (wchar_t*)dev->last_error_str;
+	}
+
+	// Global error messages are not (yet) implemented on Windows.
+	return L"hid_error for global errors is not implemented yet";
 }
 
 
diff --git a/hidapi.cabal b/hidapi.cabal
--- a/hidapi.cabal
+++ b/hidapi.cabal
@@ -1,27 +1,28 @@
 name:              hidapi
-version:           0.1.5
+version:           0.1.6
 build-type:        Simple
-cabal-version:     >= 1.6
+cabal-version:     >= 1.10
 category:          Hardware
 author:            Patrick Chilton <chpatrick@gmail.com>, Niklas Hambüchen <mail@nh2.me>
 maintainer:        Patrick Chilton <chpatrick@gmail.com>
-homepage:          https://github.com/vahokif/haskell-hidapi
-bug-reports:       https://github.com/vahokif/haskell-hidapi/issues
+homepage:          https://github.com/chpatrick/haskell-hidapi
+bug-reports:       https://github.com/chpatrick/haskell-hidapi/issues
 synopsis:          Haskell bindings to HIDAPI
-description:       Haskell bindings to the HIDAPI library (<http://www.signal11.us/oss/hidapi/>).
+description:       Haskell bindings to the HIDAPI library (<https://github.com/libusb/hidapi>).
                    .
                    Note you need need to have the corresponding low-level
                    library installed for your OS, e.g. libudev-dev on Debian/Ubuntu,
                    or just udev on distributions that don't split dev libraries.
 license:           MIT
 license-file:      LICENSE
+extra-source-files:  README.md
 
 extra-source-files:
   cbits/hidapi/hidapi.h
 
 source-repository head
   type:      git
-  location:  git://github.com/vahokif/haskell-hidapi.git
+  location:  git://github.com/chpatrick/haskell-hidapi.git
 
 library
   exposed-modules: System.HIDAPI
@@ -50,3 +51,4 @@
     else
       c-sources: cbits/hidapi/linux/hid.c
       extra-libraries: udev
+  default-language:  Haskell2010
