diff --git a/hidapi/hidapi.h b/hidapi/hidapi.h index 959c9122a..09c980158 100644 --- a/hidapi/hidapi.h +++ b/hidapi/hidapi.h @@ -151,6 +151,7 @@ extern "C" { @returns This function returns 0 on success and -1 on error. + Call hid_error(NULL) to get the failure reason. */ int HID_API_EXPORT HID_API_CALL hid_init(void); @@ -162,7 +163,7 @@ extern "C" { @ingroup API - @returns + @returns This function returns 0 on success and -1 on error. */ int HID_API_EXPORT HID_API_CALL hid_exit(void); @@ -182,21 +183,25 @@ extern "C" { @param product_id The Product ID (PID) of the types of device to open. - @returns - This function returns a pointer to a linked list of type - 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(). + @returns + This function returns a pointer to a linked list of type + struct #hid_device_info, containing information about the HID devices + attached to the system, + or NULL in the case of failure or if no HID devices present in the system. + Call hid_error(NULL) to get the failure reason. + + @note The returned value by this function must to be freed by calling hid_free_enumeration(), + when not needed anymore. */ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id); /** @brief Free an enumeration Linked List - This function frees a linked list created by hid_enumerate(). + This function frees a linked list created by hid_enumerate(). @ingroup API - @param devs Pointer to a list of struct_device returned from - hid_enumerate(). + @param devs Pointer to a list of struct_device returned from + hid_enumerate(). */ void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs); @@ -206,17 +211,19 @@ extern "C" { 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. @param serial_number The Serial Number of the device to open - (Optionally NULL). + (Optionally NULL). @returns This function returns a pointer to a #hid_device object on success or NULL on failure. + Call hid_error(NULL) to get the failure reason. + + @note The returned object must be freed by calling hid_close(), + when not needed anymore. */ HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number); @@ -226,14 +233,16 @@ extern "C" { 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 + @param path The path name of the device to open @returns This function returns a pointer to a #hid_device object on success or NULL on failure. + Call hid_error(NULL) to get the failure reason. + + @note The returned object must be freed by calling hid_close(), + when not needed anymore. */ HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path); @@ -253,8 +262,6 @@ extern "C" { 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 dev A device handle returned from hid_open(). @param data The data to send, including the report number as @@ -264,6 +271,7 @@ extern "C" { @returns This function returns the actual number of bytes written and -1 on error. + Call hid_error(dev) to get the failure reason. */ int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length); @@ -273,8 +281,6 @@ extern "C" { 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 dev A device handle returned from hid_open(). @param data A buffer to put the read data into. @@ -285,7 +291,9 @@ extern "C" { @returns This function returns the actual number of bytes read and - -1 on error. If no packet was available to be read within + -1 on error. + Call hid_error(dev) to get the failure reason. + If no packet was available to be read within the timeout period, this function returns 0. */ int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds); @@ -293,11 +301,9 @@ extern "C" { /** @brief Read an Input report from a HID device. Input reports are returned - to the host through the INTERRUPT IN endpoint. The first byte will + 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 dev A device handle returned from hid_open(). @param data A buffer to put the read data into. @@ -307,7 +313,9 @@ extern "C" { @returns This function returns the actual number of bytes read and - -1 on error. If no packet was available to be read and + -1 on error. + Call hid_error(dev) to get the failure reason. + 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 *dev, unsigned char *data, size_t length); @@ -329,6 +337,7 @@ extern "C" { @returns This function returns 0 on success and -1 on error. + Call hid_error(dev) to get the failure reason. */ int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock); @@ -347,8 +356,6 @@ extern "C" { 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 dev A device handle returned from hid_open(). @param data The data to send, including the report number as @@ -359,6 +366,7 @@ extern "C" { @returns This function returns the actual number of bytes written and -1 on error. + Call hid_error(dev) to get the failure reason. */ int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length); @@ -370,8 +378,6 @@ extern "C" { 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 @@ -386,6 +392,7 @@ extern "C" { 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. + Call hid_error(dev) to get the failure reason. */ int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length); @@ -413,13 +420,12 @@ extern "C" { 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. + Call hid_error(dev) to get the failure reason. */ 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 dev A device handle returned from hid_open(). */ @@ -434,6 +440,7 @@ extern "C" { @returns This function returns 0 on success and -1 on error. + Call hid_error(dev) to get the failure reason. */ int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen); @@ -446,6 +453,7 @@ extern "C" { @returns This function returns 0 on success and -1 on error. + Call hid_error(dev) to get the failure reason. */ int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen); @@ -458,6 +466,7 @@ extern "C" { @returns This function returns 0 on success and -1 on error. + Call hid_error(dev) to get the failure reason. */ int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen); @@ -471,32 +480,41 @@ extern "C" { @returns This function returns 0 on success and -1 on error. + Call hid_error(dev) to get the failure reason. */ 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. + This function is intended for logging/debugging purposes. + + This function guarantees to never return NULL. + If there was no error in the last function call - + the returned string clearly indicates that. - Strings returned from hid_error() must not be freed by the user! + Any HIDAPI function that can explicitly indicate an execution failure + (e.g. by an error code, or by returning NULL) - may set the error string, + to be returned by this function. - This function is thread-safe, and error messages are thread-local. + Strings returned from hid_error() must not be freed by the user, + i.e. owned by HIDAPI library. + Device-specific error string may remain allocated at most until hid_close() is called. + Global error string may remain allocated at most until hid_exit() is called. @ingroup API @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). + (e.g. for errors in hid_open() or hid_enumerate()). @returns - This function returns a string containing the last error - which occurred or NULL if none has occurred. + A string describing the last error (if any). */ HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *dev); /** @brief Get a runtime version of the library. + This function is thread-safe. + @ingroup API @returns @@ -507,6 +525,8 @@ extern "C" { /** @brief Get a runtime version string of the library. + This function is thread-safe. + @ingroup API @returns diff --git a/linux/hid.c b/linux/hid.c index 1d7911700..d5adc1caf 100644 --- a/linux/hid.c +++ b/linux/hid.c @@ -97,9 +97,8 @@ static struct hid_api_version api_version = { .patch = HID_API_VERSION_PATCH }; -/* 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 wchar_t *last_global_error_str = NULL; + static hid_device *new_hid_device(void) { @@ -124,6 +123,10 @@ static wchar_t *utf8_to_wchar_t(const char *utf8) return wcsdup(L""); } ret = (wchar_t*) calloc(wlen+1, sizeof(wchar_t)); + if (ret == NULL) { + /* as much as we can do at this point */ + return NULL; + } mbstowcs(ret, utf8, wlen+1); ret[wlen] = 0x0000; } @@ -132,6 +135,25 @@ static wchar_t *utf8_to_wchar_t(const char *utf8) } +/* Makes a copy of the given error message (and decoded according to the + * currently locale) into the wide string pointer pointed by error_str. + * The last stored error string is freed. + * Use register_error_str(NULL) to free the error message completely. */ +static void register_error_str(wchar_t **error_str, const char *msg) +{ + free(*error_str); + *error_str = utf8_to_wchar_t(msg); +} + +/* Semilar to register_error_str, but allows passing a format string with va_list args into this function. */ +static void register_error_str_vformat(wchar_t **error_str, const char *format, va_list args) +{ + char msg[256]; + vsnprintf(msg, sizeof(msg), format, args); + + register_error_str(error_str, msg); +} + /* 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). @@ -139,51 +161,35 @@ static wchar_t *utf8_to_wchar_t(const char *utf8) * 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); + register_error_str(&last_global_error_str, msg); } -/* See register_global_error, but you can pass a format string into this function. */ +/* Similar to register_global_error, but allows passing 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); - + register_error_str_vformat(&last_global_error_str, format, args); va_end(args); - - register_global_error(msg); } -/* Set the last error for a device to be reported by hid_error(device). +/* Set the last error for a device to be reported by hid_error(dev). * 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". */ + * The last stored device error message is freed. + * Use register_device_error(dev, 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); + register_error_str(&dev->last_error_str, msg); } -/* See register_device_error, but you can pass a format string into this function. */ +/* Similar to 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); - + register_error_str_vformat(&dev->last_error_str, 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 @@ -496,17 +502,27 @@ static int get_device_string(hid_device *dev, enum device_string_id key, wchar_t char *serial_number_utf8 = NULL; char *product_name_utf8 = NULL; - /* Create the udev object */ - udev = udev_new(); - if (!udev) { - register_global_error("Couldn't create udev context"); + if (!string || !maxlen) { + register_device_error(dev, "Zero buffer/length"); return -1; } + register_device_error(dev, NULL); + /* Get the dev_t (major/minor numbers) from the file handle. */ ret = fstat(dev->device_handle, &s); - if (-1 == ret) + if (-1 == ret) { + register_device_error(dev, "Failed to stat device handle"); return ret; + } + + /* Create the udev object */ + udev = udev_new(); + if (!udev) { + register_device_error(dev, "Couldn't create udev context"); + return -1; + } + /* 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) { @@ -614,6 +630,9 @@ int HID_API_EXPORT hid_init(void) { const char *locale; + /* indicate no error */ + register_global_error(NULL); + /* Set the locale if it's not set. */ locale = setlocale(LC_CTYPE, NULL); if (!locale) @@ -642,6 +661,7 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, struct hid_device_info *prev_dev = NULL; /* previous device */ hid_init(); + /* register_global_error: global error is reset by hid_init */ /* Create the udev object */ udev = udev_new(); @@ -855,6 +875,14 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, udev_enumerate_unref(enumerate); udev_unref(udev); + if (root == NULL) { + if (vendor_id == 0 && product_id == 0) { + register_global_error("No HID devices found in the system."); + } else { + register_global_error("No HID devices with requested VID/PID found in the system."); + } + } + return root; } @@ -874,14 +902,17 @@ void HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs) 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; + /* register_global_error: global error is reset by hid_enumerate/hid_init */ devs = hid_enumerate(vendor_id, product_id); + if (devs == NULL) { + /* register_global_error: global error is already set by hid_enumerate */ + return NULL; + } + cur_dev = devs; while (cur_dev) { if (cur_dev->vendor_id == vendor_id && @@ -904,7 +935,7 @@ hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, const /* Open the device */ handle = hid_open_path(path_to_open); } else { - register_global_error("No such device"); + register_global_error("Device with requested VID/PID/(SerialNumber) not found"); } hid_free_enumeration(devs); @@ -914,23 +945,17 @@ hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, const 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(); + /* register_global_error: global error is reset by hid_init */ dev = new_hid_device(); - /* OPEN HERE */ dev->device_handle = open(path, O_RDWR); /* If we have a good handle, return it. */ if (dev->device_handle >= 0) { - /* Set device error to none */ - register_device_error(dev, NULL); - /* Get the report descriptor */ int res, desc_size = 0; struct hidraw_report_descriptor rpt_desc; @@ -957,9 +982,9 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path) return dev; } else { - /* Unable to open any devices. */ - register_global_error(strerror(errno)); + /* Unable to open a device. */ free(dev); + register_global_error_format("Failed to open a device with path '%s': %s", path, strerror(errno)); return NULL; } } @@ -1016,9 +1041,11 @@ int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t else { /* Check for errors on the file descriptor. This will indicate a device disconnection. */ - if (fds.revents & (POLLERR | POLLHUP | POLLNVAL)) + if (fds.revents & (POLLERR | POLLHUP | POLLNVAL)) { // We cannot use strerror() here as no -1 was returned from poll(). + register_device_error(dev, "hid_read_timeout: unexpected poll error (device disconnected)"); return -1; + } } } @@ -1053,6 +1080,8 @@ int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char { int res; + register_device_error(dev, NULL); + res = ioctl(dev->device_handle, HIDIOCSFEATURE(length), data); if (res < 0) register_device_error_format(dev, "ioctl (SFEATURE): %s", strerror(errno)); @@ -1064,6 +1093,8 @@ int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, { int res; + register_device_error(dev, NULL); + res = ioctl(dev->device_handle, HIDIOCGFEATURE(length), data); if (res < 0) register_device_error_format(dev, "ioctl (GFEATURE): %s", strerror(errno)); @@ -1075,6 +1106,8 @@ int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned c { int res; + register_device_error(dev, NULL); + res = ioctl(dev->device_handle, HIDIOCGINPUT(length), data); if (res < 0) register_device_error_format(dev, "ioctl (GINPUT): %s", strerror(errno)); @@ -1087,9 +1120,7 @@ void HID_API_EXPORT hid_close(hid_device *dev) if (!dev) return; - int ret = close(dev->device_handle); - - register_global_error((ret == -1)? strerror(errno): NULL); + close(dev->device_handle); /* Free the device error message */ register_device_error(dev, NULL); @@ -1115,10 +1146,12 @@ int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *s 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; + + register_device_error(dev, "hid_get_indexed_string: not supported by hidraw"); + return -1; } diff --git a/windows/hid.c b/windows/hid.c index da47ff6e6..3b9fbac73 100644 --- a/windows/hid.c +++ b/windows/hid.c @@ -189,6 +189,11 @@ struct hid_device_ { static hid_device *new_hid_device() { hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device)); + + if (dev == NULL) { + return NULL; + } + dev->device_handle = INVALID_HANDLE_VALUE; dev->blocking = TRUE; dev->output_report_length = 0; @@ -224,9 +229,6 @@ static void free_hid_device(hid_device *dev) static void register_winapi_error_to_buffer(wchar_t **error_buffer, const WCHAR *op) { - if (!error_buffer) - return; - free(*error_buffer); *error_buffer = NULL; @@ -281,19 +283,8 @@ static void register_winapi_error_to_buffer(wchar_t **error_buffer, const WCHAR } } -static void register_winapi_error(hid_device *dev, const WCHAR *op) -{ - if (!dev) - return; - - register_winapi_error_to_buffer(&dev->last_error_str, op); -} - static void register_string_error_to_buffer(wchar_t **error_buffer, const WCHAR *string_error) { - if (!error_buffer) - return; - free(*error_buffer); *error_buffer = NULL; @@ -302,14 +293,28 @@ static void register_string_error_to_buffer(wchar_t **error_buffer, const WCHAR } } -static void register_string_error(hid_device *dev, const WCHAR *string_error) +static void register_winapi_error(hid_device *dev, const WCHAR *op) { - if (!dev) - return; + register_winapi_error_to_buffer(&dev->last_error_str, op); +} +static void register_string_error(hid_device *dev, const WCHAR *string_error) +{ register_string_error_to_buffer(&dev->last_error_str, string_error); } +static wchar_t *last_global_error_str = NULL; + +static void register_global_winapi_error(const WCHAR *op) +{ + register_winapi_error_to_buffer(&last_global_error_str, op); +} + +static void register_global_error(const WCHAR *string_error) +{ + register_string_error_to_buffer(&last_global_error_str, string_error); +} + static HANDLE open_device(const wchar_t *path, BOOL open_rw) { HANDLE handle; @@ -339,9 +344,11 @@ HID_API_EXPORT const char* HID_API_CALL hid_version_str() int HID_API_EXPORT hid_init(void) { + register_global_error(NULL); #ifndef HIDAPI_USE_DDK if (!hidapi_initialized) { if (lookup_functions() < 0) { + register_global_winapi_error(L"resolve DLL functions"); return -1; } hidapi_initialized = TRUE; @@ -356,6 +363,7 @@ int HID_API_EXPORT hid_exit(void) free_library_handles(); hidapi_initialized = FALSE; #endif + register_global_error(NULL); return 0; } @@ -523,6 +531,9 @@ static char *hid_internal_UTF16toUTF8(const wchar_t *src) int len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, src, -1, NULL, 0, NULL, NULL); if (len) { dst = (char*)calloc(len, sizeof(char)); + if (dst == NULL) { + return NULL; + } WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, src, -1, dst, len, NULL, NULL); } @@ -535,6 +546,9 @@ static wchar_t *hid_internal_UTF8toUTF16(const char *src) int len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src, -1, NULL, 0); if (len) { dst = (wchar_t*)calloc(len, sizeof(wchar_t)); + if (dst == NULL) { + return NULL; + } MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src, -1, dst, len); } @@ -608,8 +622,10 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor wchar_t* device_interface_list = NULL; DWORD len; - if (hid_init() < 0) + if (hid_init() < 0) { + /* register_global_error: global error is reset by hid_init */ return NULL; + } /* Retrieve HID Interface Class GUID https://docs.microsoft.com/windows-hardware/drivers/install/guid-devinterface-hid */ @@ -621,6 +637,7 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor do { cr = CM_Get_Device_Interface_List_SizeW(&len, &interface_class_guid, NULL, CM_GET_DEVICE_INTERFACE_LIST_PRESENT); if (cr != CR_SUCCESS) { + register_global_error(L"Failed to get size of HID device interface list"); break; } @@ -630,9 +647,13 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor device_interface_list = (wchar_t*)calloc(len, sizeof(wchar_t)); if (device_interface_list == NULL) { + register_global_error(L"Failed to allocate memory for HID device interface list"); return NULL; } cr = CM_Get_Device_Interface_ListW(&interface_class_guid, NULL, device_interface_list, len, CM_GET_DEVICE_INTERFACE_LIST_PRESENT); + if (cr != CR_SUCCESS && cr != CR_BUFFER_SMALL) { + register_global_error(L"Failed to get HID device interface list"); + } } while (cr == CR_BUFFER_SMALL); if (cr != CR_SUCCESS) { @@ -684,6 +705,14 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor CloseHandle(device_handle); } + if (root == NULL) { + if (vendor_id == 0 && product_id == 0) { + register_global_error(L"No HID devices found in the system."); + } else { + register_global_error(L"No HID devices with requested VID/PID found in the system."); + } + } + end_of_function: free(device_interface_list); @@ -712,8 +741,10 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsi const char *path_to_open = NULL; hid_device *handle = NULL; + /* register_global_error: global error is reset by hid_enumerate/hid_init */ devs = hid_enumerate(vendor_id, product_id); if (!devs) { + /* register_global_error: global error is already set by hid_enumerate */ return NULL; } @@ -738,6 +769,8 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsi if (path_to_open) { /* Open the device */ handle = hid_open_path(path_to_open); + } else { + register_global_error(L"Device with requested VID/PID/(SerialNumber) not found"); } hid_free_enumeration(devs); @@ -753,12 +786,16 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path) PHIDP_PREPARSED_DATA pp_data = NULL; HIDP_CAPS caps; - if (hid_init() < 0) + if (hid_init() < 0) { + /* register_global_error: global error is reset by hid_init */ goto end_of_function; + } interface_path = hid_internal_UTF8toUTF16(path); - if (!interface_path) + if (!interface_path) { + register_string_error(dev, L"Path conversion failure"); goto end_of_function; + } /* Open a handle to the device */ device_handle = open_device(interface_path, TRUE); @@ -773,23 +810,36 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path) device_handle = open_device(interface_path, FALSE); /* Check the validity of the limited device_handle. */ - if (device_handle == INVALID_HANDLE_VALUE) + if (device_handle == INVALID_HANDLE_VALUE) { + register_global_winapi_error(L"open_device"); goto end_of_function; + } } /* Set the Input Report buffer size to 64 reports. */ - if (!HidD_SetNumInputBuffers(device_handle, 64)) + if (!HidD_SetNumInputBuffers(device_handle, 64)) { + register_global_winapi_error(L"set input buffers"); goto end_of_function; + } /* Get the Input Report length for the device. */ - if (!HidD_GetPreparsedData(device_handle, &pp_data)) + if (!HidD_GetPreparsedData(device_handle, &pp_data)) { + register_global_winapi_error(L"get preparsed data"); goto end_of_function; + } - if (HidP_GetCaps(pp_data, &caps) != HIDP_STATUS_SUCCESS) + if (HidP_GetCaps(pp_data, &caps) != HIDP_STATUS_SUCCESS) { + register_global_error(L"HidP_GetCaps"); goto end_of_function; + } dev = new_hid_device(); + if (dev == NULL) { + register_global_error(L"hid_device allocation error"); + goto end_of_function; + } + dev->device_handle = device_handle; device_handle = INVALID_HANDLE_VALUE; @@ -819,11 +869,13 @@ int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char * unsigned char *buf; - if (!data || (length==0)) { + if (!data || !length) { register_string_error(dev, L"Zero buffer/length"); return function_result; } + register_string_error(dev, NULL); + /* Make sure the right number of bytes are passed to WriteFile. Windows expects the number of bytes which are in the _longest_ report (plus one for the report number) bytes even if the data is a report @@ -887,6 +939,13 @@ int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char BOOL res = FALSE; BOOL overlapped = FALSE; + if (!data || !length) { + register_string_error(dev, L"Zero buffer/length"); + return -1; + } + + register_string_error(dev, NULL); + /* Copy the handle for convenience. */ HANDLE ev = dev->ol.hEvent; @@ -977,6 +1036,13 @@ int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const u unsigned char *buf; size_t length_to_send; + if (!data || !length) { + register_string_error(dev, L"Zero buffer/length"); + return -1; + } + + register_string_error(dev, NULL); + /* 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 @@ -1012,6 +1078,13 @@ static int hid_get_report(hid_device *dev, DWORD report_type, unsigned char *dat OVERLAPPED ol; memset(&ol, 0, sizeof(ol)); + if (!data || !length) { + register_string_error(dev, L"Zero buffer/length"); + return -1; + } + + register_string_error(dev, NULL); + res = DeviceIoControl(dev->device_handle, report_type, data, (DWORD) length, @@ -1068,14 +1141,12 @@ void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev) int HID_API_EXPORT_CALL HID_API_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) { - if (!dev->device_info) - { - register_string_error(dev, L"NULL device/info"); + if (!dev->device_info) { + register_string_error(dev, L"NULL device info"); return -1; } - if (!string || !maxlen) - { + if (!string || !maxlen) { register_string_error(dev, L"Zero buffer/length"); return -1; } @@ -1083,48 +1154,48 @@ int HID_API_EXPORT_CALL HID_API_CALL hid_get_manufacturer_string(hid_device *dev wcsncpy(string, dev->device_info->manufacturer_string, maxlen); string[maxlen - 1] = L'\0'; + register_string_error(dev, NULL); + return 0; } int HID_API_EXPORT_CALL HID_API_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) { - if (!dev->device_info) - { - register_string_error(dev, L"NULL device/info"); + if (!dev->device_info) { + register_string_error(dev, L"NULL device info"); return -1; } - if (!string || !maxlen) - { + if (!string || !maxlen) { register_string_error(dev, L"Zero buffer/length"); return -1; } - wcsncpy(string, dev->device_info->product_string, maxlen); string[maxlen - 1] = L'\0'; + register_string_error(dev, NULL); + return 0; } int HID_API_EXPORT_CALL HID_API_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) { - if (!dev->device_info) - { - register_string_error(dev, L"NULL device/info"); + if (!dev->device_info) { + register_string_error(dev, L"NULL device info"); return -1; } - if (!string || !maxlen) - { + if (!string || !maxlen) { register_string_error(dev, L"Zero buffer/length"); return -1; } - wcsncpy(string, dev->device_info->serial_number, maxlen); string[maxlen - 1] = L'\0'; + register_string_error(dev, NULL); + return 0; } @@ -1138,6 +1209,8 @@ int HID_API_EXPORT_CALL HID_API_CALL hid_get_indexed_string(hid_device *dev, int return -1; } + register_string_error(dev, NULL); + return 0; } @@ -1149,31 +1222,29 @@ int HID_API_EXPORT_CALL hid_winapi_get_container_id(hid_device *dev, GUID *conta DEVPROPTYPE property_type; ULONG len; - if (!container_id) - { + if (!container_id) { register_string_error(dev, L"Invalid Container ID"); return -1; } + register_string_error(dev, NULL); + interface_path = hid_internal_UTF8toUTF16(dev->device_info->path); - if (!interface_path) - { + if (!interface_path) { register_string_error(dev, L"Path conversion failure"); goto end; } /* Get the device id from interface path */ device_id = hid_internal_get_device_interface_property(interface_path, &DEVPKEY_Device_InstanceId, DEVPROP_TYPE_STRING); - if (!device_id) - { + if (!device_id) { register_string_error(dev, L"Failed to get device interface property InstanceId"); goto end; } /* Open devnode from device id */ cr = CM_Locate_DevNodeW(&dev_node, (DEVINSTID_W)device_id, CM_LOCATE_DEVNODE_NORMAL); - if (cr != CR_SUCCESS) - { + if (cr != CR_SUCCESS) { register_string_error(dev, L"Failed to locate device node"); goto end; } @@ -1203,8 +1274,9 @@ HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) 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"; + if (last_global_error_str == NULL) + return L"Success"; + return last_global_error_str; } #ifdef __cplusplus