diff options
author | Pete Batard <pbatard@gmail.com> | 2010-01-18 20:32:38 +0000 |
---|---|---|
committer | Pete Batard <pbatard@gmail.com> | 2010-01-18 20:32:38 +0000 |
commit | 7a8f7d1826c754aed2a4ae5f369b8882cc4cb884 (patch) | |
tree | 95e4b7cfe4bc9999d196974eb4f23d5ce9f4b1bd | |
parent | 2012ca7b23b2199022c51e84ca1d82838a9a570c (diff) | |
download | libusb-7a8f7d1826c754aed2a4ae5f369b8882cc4cb884.tar.gz |
r89: extended API support
- composite devices as a separate backend API
- mutidriver support in composite devices interface
- HID: device interface path, open/close (WIP)
- added HID test in xusb.c
- fixed Windows version and DDK support
- smaller fixes
-rw-r--r-- | README_MSVC.txt | 6 | ||||
-rw-r--r-- | examples/xusb.c | 10 | ||||
-rw-r--r-- | libusb/os/windows_usb.c | 663 | ||||
-rw-r--r-- | libusb/os/windows_usb.h | 84 |
4 files changed, 635 insertions, 128 deletions
diff --git a/README_MSVC.txt b/README_MSVC.txt index 1b59b73..2885364 100644 --- a/README_MSVC.txt +++ b/README_MSVC.txt @@ -1,4 +1,4 @@ -To compile libusb 1.0 using MSVC 9: +To compile libusb 1.0 using Microsoft Visual Studio: Note: in the text below, (Win32) means "when producing 32 bit binaries" and (x64) "when producing 64 bit binaries". This is independent of whether your @@ -21,8 +21,8 @@ platform is actually 32 or 64 bit. http://sourceware.org/pthreads-win32/ and create both a pthreadVC2_x64.lib and pthreadVC2_x64.dll from the latest pthread-win32 source. - To help compiling pthreadVC2_x64.dll on x64 platforms, sample .sln and .vcproj - files for pthread-win32 are provided in the msvc\pthread-win32_x64\ directory. + To help compiling pthreadVC2_x64.dll on x64 platforms, sample .sln and + .vcproj files are provided in the msvc\pthread-win32_x64\ directory. - (x64) Copy pthreadVC2_x64.lib to the msvc directory. diff --git a/examples/xusb.c b/examples/xusb.c index 4775194..ecb6070 100644 --- a/examples/xusb.c +++ b/examples/xusb.c @@ -116,6 +116,7 @@ enum test_type { USE_XBOX, USE_KEY, USE_JTAG, + USE_HID, } test_mode; uint16_t VID, PID; @@ -448,14 +449,21 @@ int main(int argc, char** argv) if (argc == 2) { if ((argv[1][0] != '-') || (argv[1][1] == 'h')) { - printf("usage: %s [-h] [-j] [-k] [-x]\n", argv[0]); + printf("usage: %s [-h] [-i] [-j] [-k] [-x]\n", argv[0]); printf(" -h: display usage\n"); + printf(" -i: test IBM HID Optical Mouse\n"); printf(" -j: test OLIMEX ARM-USB-TINY JTAG, 2 channel composite device\n"); printf(" -k: test Generic 2 GB USB Key\n"); printf(" -x: test Microsoft XBox Controller Type S (default)\n"); return 0; } switch(argv[1][1]) { + case 'i': + // IBM HID Optical mouse - 1 interface + VID = 0x04B3; + PID = 0x3108; + test_mode = USE_HID; + break; case 'j': // OLIMEX ARM-USB-TINY JTAG, 2 channel composite device - 2 interfaces VID = 0x15BA; diff --git a/libusb/os/windows_usb.c b/libusb/os/windows_usb.c index 1962391..4ebfcaf 100644 --- a/libusb/os/windows_usb.c +++ b/libusb/os/windows_usb.c @@ -17,13 +17,10 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +// If using Visual Studio 2008 or earlier, Windows 7 is not defined... #if defined(_MSC_VER) -// If the following is true, then Microsoft provided an improper WINVER -// to Visual Studio 2008 on Windows 7. Both should be set to 0x601 -#if (WINVER <= _WIN32_WINNT_LONGHORN) && (WINVER >= _WIN32_WINNT_WIN7) -#undef WINVER -#define WINVER 0x601 -#undef _WIN32_WINNT_WIN7 +#if !defined(_WIN32_WINNT_WIN7) #define _WIN32_WINNT_WIN7 0x601 #endif #include <config_msvc.h> @@ -39,13 +36,8 @@ #include <windows.h> #include <setupapi.h> #if defined(_MSC_VER) -// Fixes DDK errors -#if !defined __drv_maxIRQL -#define __drv_maxIRQL(x) -#endif -#if !defined __drv_preferredFunction -#define __drv_preferredFunction(func,why) -#endif +#include <api/sal_supp.h> +#include <api/driverspecs.h> #include <api/usbiodef.h> #include <api/usbioctl.h> #include <api/cfgmgr32.h> @@ -85,8 +77,8 @@ static int windows_get_active_config_descriptor(struct libusb_device *dev, unsigned char *buffer, size_t len, int *host_endian); static int windows_clock_gettime(int clk_id, struct timespec *tp); // WinUSB API prototypes -static int winusb_api_init(struct libusb_context *ctx); -static int winusb_api_exit(void); +static int winusb_init(struct libusb_context *ctx); +static int winusb_exit(void); static int winusb_open(struct libusb_device_handle *dev_handle); static void winusb_close(struct libusb_device_handle *dev_handle); static int winusb_claim_interface(struct libusb_device_handle *dev_handle, int iface); @@ -99,6 +91,29 @@ static int winusb_clear_halt(struct libusb_device_handle *dev_handle, unsigned c static int winusb_abort_transfers(struct usbi_transfer *itransfer); static int winusb_abort_control(struct usbi_transfer *itransfer); static int winusb_reset_device(struct libusb_device_handle *dev_handle); +// HID API prototypes +static int hid_init(struct libusb_context *ctx); +static int hid_exit(void); +static int hid_open(struct libusb_device_handle *dev_handle); +static void hid_close(struct libusb_device_handle *dev_handle); +//static int hid_claim_interface(struct libusb_device_handle *dev_handle, int iface); +//static int hid_release_interface(struct libusb_device_handle *dev_handle, int iface); +// Composite API prototypes +static int composite_init(struct libusb_context *ctx); +static int composite_exit(void); +static int composite_open(struct libusb_device_handle *dev_handle); +static void composite_close(struct libusb_device_handle *dev_handle); +static int composite_claim_interface(struct libusb_device_handle *dev_handle, int iface); +static int composite_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting); +static int composite_release_interface(struct libusb_device_handle *dev_handle, int iface); +static int composite_submit_control_transfer(struct usbi_transfer *itransfer); +static int composite_submit_bulk_transfer(struct usbi_transfer *itransfer); +static int composite_submit_iso_transfer(struct usbi_transfer *itransfer); +static int composite_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint); +static int composite_abort_transfers(struct usbi_transfer *itransfer); +static int composite_abort_control(struct usbi_transfer *itransfer); +static int composite_reset_device(struct libusb_device_handle *dev_handle); + // Global variables struct windows_hcd_priv* hcd_root = NULL; @@ -106,9 +121,12 @@ uint64_t hires_frequency, hires_ticks_to_ps; const uint64_t epoch_time = 116444736000000000; // 1970.01.01 00:00:000 in MS Filetime DWORD_PTR old_affinity_mask; enum windows_version windows_version = WINDOWS_UNSUPPORTED; -// WinUSB globals +// API globals bool api_winusb_available = false; #define CHECK_WINUSB_AVAILABLE do { if (!api_winusb_available) return LIBUSB_ERROR_ACCESS; } while (0) +bool api_hid_available = false; +#define CHECK_HID_AVAILABLE do { if (!api_hid_available) return LIBUSB_ERROR_ACCESS; } while (0) + /* * Converts a WCHAR string to UTF8 (allocate returned string) @@ -314,7 +332,7 @@ static int windows_init(struct libusb_context *ctx) SP_DEVICE_INTERFACE_DETAIL_DATA *dev_interface_details = NULL; GUID guid; libusb_bus_t bus; - int r = LIBUSB_SUCCESS; + int i, r = LIBUSB_SUCCESS; LARGE_INTEGER li_frequency; OSVERSIONINFO os_version; @@ -347,9 +365,11 @@ static int windows_init(struct libusb_context *ctx) init_polling(); // Initialize the low level APIs - r = winusb_api_init(ctx); - if (r != LIBUSB_SUCCESS) { - return r; + for (i=0; i<USB_API_MAX; i++) { + r = usb_api_backend[i].init(ctx); + if (r != LIBUSB_SUCCESS) { + return r; + } } // Because QueryPerformanceCounter might report different values when @@ -436,7 +456,6 @@ static int initialize_device(struct libusb_device *dev, libusb_bus_t busnum, // a driver issue => report this // TODO: use this for automated driver installation // TODO: can we get an error code to confirm from SetupDiWhatever? - priv->driver = safe_strdup("no_driver"); usbi_dbg("* DRIVERLESS DEVICE *"); } @@ -779,8 +798,6 @@ static int usb_enumerate_hub(struct libusb_context *ctx, struct discovered_devs // Finally, if device is a hub, recurse if (conn_info.DeviceIsHub) { - // Force the driver name - priv->driver = safe_strdup("usbhub"); // Find number of ports for this hub size = sizeof(USB_NODE_INFORMATION); if (!DeviceIoControl(handle, IOCTL_USB_GET_NODE_INFORMATION, &hub_node, size, @@ -812,7 +829,7 @@ static int usb_enumerate_hub(struct libusb_context *ctx, struct discovered_devs static int set_composite_device(struct libusb_context *ctx, DEVINST devinst, struct windows_device_priv *priv) { DEVINST child_devinst; - int i, j, max_guids, nb_paths, interface_number; + unsigned i, j, api, max_guids, nb_paths, interface_number; bool found; DWORD type, size; CONFIGRET r; @@ -824,6 +841,7 @@ static int set_composite_device(struct libusb_context *ctx, DEVINST devinst, str GUID guid; GUID guid_table[MAX_USB_DEVICES]; char* sanitized_path[MAX_USB_DEVICES]; + uint8_t api_type[MAX_USB_DEVICES]; char* sanitized_short = NULL; char path[MAX_PATH_LENGTH]; char driver[MAX_KEY_LENGTH]; @@ -903,11 +921,14 @@ static int set_composite_device(struct libusb_context *ctx, DEVINST devinst, str continue; } - if (safe_strcmp(driver, "WinUSB") == 0) { - sanitized_path[nb_paths++] = sanitize_path(dev_interface_details->DevicePath); - if (nb_paths > MAX_USB_DEVICES) { - usbi_warn(ctx, "more than %d devices - ignoring the rest", MAX_USB_DEVICES); - break; + for (api=USB_API_WINUSB; api<USB_API_MAX; api++) { + if (safe_strcmp(driver, usb_api_backend[api].driver_name) == 0) { + api_type[nb_paths] = api; + sanitized_path[nb_paths++] = sanitize_path(dev_interface_details->DevicePath); + if (nb_paths > MAX_USB_DEVICES) { + usbi_warn(ctx, "more than %d devices - ignoring the rest", MAX_USB_DEVICES); + break; + } } } } @@ -942,6 +963,8 @@ static int set_composite_device(struct libusb_context *ctx, DEVINST devinst, str for (j=0; j<nb_paths; j++) { if (safe_strncmp(sanitized_path[j], sanitized_short, strlen(sanitized_short)) == 0) { priv->usb_interface[interface_number].path = sanitized_path[j]; + priv->usb_interface[interface_number].apib = &usb_api_backend[api_type[j]]; + priv->composite_api_flags |= 1<<api_type[j]; sanitized_path[j] = NULL; } } @@ -969,6 +992,75 @@ static int set_composite_device(struct libusb_context *ctx, DEVINST devinst, str } /* + * Likewise, HID device interfaces's path (\\.\HID\...) are not enumerated through the + * generic USB devices GUID, but are actually children of one such device + */ +static int set_hid_device(struct libusb_context *ctx, struct windows_device_priv *priv) + { + char path[MAX_PATH_LENGTH]; + char *sanitized_path = NULL; + HDEVINFO dev_info; + SP_DEVICE_INTERFACE_DETAIL_DATA *dev_interface_details = NULL; + SP_DEVINFO_DATA dev_info_data; + DEVINST parent_devinst; + GUID guid; + int r = LIBUSB_SUCCESS; + unsigned i, interface_number; + + interface_number = 0; + HidD_GetHidGuid(&guid); + for (i = 0; ; i++) + { + // safe loop: free up any (unprotected) dynamic resource + safe_free(dev_interface_details); + safe_free(sanitized_path); + + dev_interface_details = get_interface_details(ctx, &dev_info, guid, i); + // safe loop: end of loop condition + if ( (dev_interface_details == NULL) + || (r != LIBUSB_SUCCESS) ) + break; + + dev_info_data.cbSize = sizeof(dev_info_data); + if (!SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data)) { + usbi_warn(ctx, "could not retrieve info data for device %s, skipping: %s", + dev_interface_details->DevicePath, windows_error_str(0)); + continue; + } + + // Retrieve parent's path using PnP Configuration Manager (CM) + if (CM_Get_Parent(&parent_devinst, dev_info_data.DevInst, 0) != CR_SUCCESS) { + usbi_warn(ctx, "could not retrieve parent info data for device %s, skipping: %s", + dev_interface_details->DevicePath, windows_error_str(0)); + continue; + } + + if (CM_Get_Device_ID(parent_devinst, path, MAX_PATH_LENGTH, 0) != CR_SUCCESS) { + usbi_warn(ctx, "could not retrieve parent's path for device %s, skipping: %s", + dev_interface_details->DevicePath, windows_error_str(0)); + continue; + } + + // Fix parent's path inconsistencies before attempting to compare + sanitized_path = sanitize_path(path); + if (sanitized_path == NULL) { + usbi_warn(ctx, "could not sanitize parent's path for device %s, skipping.", + dev_interface_details->DevicePath); + continue; + } + + // NB: we compare strings of different lengths below => strncmp + if (safe_strncmp(priv->path, sanitized_path, strlen(sanitized_path)) == 0) { + priv->usb_interface[interface_number].path = sanitize_path(dev_interface_details->DevicePath); + usbi_dbg("interface_path[%d]: %s", interface_number, priv->usb_interface[interface_number].path); + interface_number++; + } + } + + return LIBUSB_SUCCESS; +} + +/* * This function retrieves and sets the paths of all non-hub devices * NB: No I/O with device is required during this call */ @@ -986,10 +1078,10 @@ static int set_device_paths(struct libusb_context *ctx, struct discovered_devs * GUID guid; DWORD size, reg_type, port_nr; int r = LIBUSB_SUCCESS; - unsigned i, j; + unsigned i, j, k, api; bool found; - // TODO: MI_## automated driver installation: + // TODO: MI_## automated driver installation guid = GUID_DEVINTERFACE_USB_DEVICE; for (i = 0; ; i++) { @@ -1005,8 +1097,8 @@ static int set_device_paths(struct libusb_context *ctx, struct discovered_devs * dev_info_data.cbSize = sizeof(dev_info_data); if (!SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data)) { - usbi_warn(ctx, "could not retrieve info data for device #%u, skipping: %s", - i, windows_error_str(0)); + usbi_warn(ctx, "could not retrieve info data for device %s, skipping: %s", + dev_interface_details->DevicePath, windows_error_str(0)); continue; } @@ -1014,28 +1106,29 @@ static int set_device_paths(struct libusb_context *ctx, struct discovered_devs * if ( (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data, SPDRP_ADDRESS, ®_type, (BYTE*)&port_nr, 4, &size)) && (size != 4) ){ - usbi_warn(ctx, "could not retrieve port number for device #%u, skipping: %s", - i, windows_error_str(0)); + usbi_warn(ctx, "could not retrieve port number for device %s, skipping: %s", + dev_interface_details->DevicePath, windows_error_str(0)); continue; } // Retrieve parent's path using PnP Configuration Manager (CM) if (CM_Get_Parent(&parent_devinst, dev_info_data.DevInst, 0) != CR_SUCCESS) { - usbi_warn(ctx, "could not retrieve parent info data for device #%u, skipping: %s", - i, windows_error_str(0)); + usbi_warn(ctx, "could not retrieve parent info data for device %s, skipping: %s", + dev_interface_details->DevicePath, windows_error_str(0)); continue; } if (CM_Get_Device_ID(parent_devinst, path, MAX_PATH_LENGTH, 0) != CR_SUCCESS) { - usbi_warn(ctx, "could not retrieve parent's path for device #%u, skipping: %s", - i, windows_error_str(0)); + usbi_warn(ctx, "could not retrieve parent's path for device %s, skipping: %s", + dev_interface_details->DevicePath, windows_error_str(0)); continue; } - // Fix parent's path inconsistancies before attempting to compare + // Fix parent's path inconsistencies before attempting to compare sanitized_path = sanitize_path(path); if (sanitized_path == NULL) { - usbi_warn(ctx, "could not sanitize parent's path for device #%u, skipping.", i); + usbi_warn(ctx, "could not sanitize parent's path for device %s, skipping.", + dev_interface_details->DevicePath); continue; } @@ -1052,6 +1145,7 @@ static int set_device_paths(struct libusb_context *ctx, struct discovered_devs * parent_priv = __device_priv(priv->parent_dev); // NB: we compare strings of different lengths below => strncmp +// usbi_dbg("Comparing: %s vs %s", parent_priv->path, sanitized_path); if ( (safe_strncmp(parent_priv->path, sanitized_path, strlen(sanitized_path)) == 0) && (port_nr == priv->connection_index) ) { @@ -1065,34 +1159,41 @@ static int set_device_paths(struct libusb_context *ctx, struct discovered_devs * // It tells us if we can use WinUSB, if we have a composite device, and the API to use if(!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data, SPDRP_SERVICE, ®_type, (BYTE*)reg_key, MAX_KEY_LENGTH, &size)) { - usbi_err(ctx, "could not retrieve driver information for device #%u, skipping: %s", - i, windows_error_str(0)); + usbi_err(ctx, "could not retrieve driver information for device %s, skipping: %s", + dev_interface_details->DevicePath, windows_error_str(0)); break; } - priv->driver = safe_strdup(reg_key); - usbi_dbg("driver: %s", priv->driver); + usbi_dbg("driver: %s", reg_key); found = true; - if (safe_strcmp(priv->driver, "WinUSB") == 0) { - priv->apib = &windows_winusb_backend; - // For non composite, the first interface is the same as the device - priv->usb_interface[0].path = safe_strdup(priv->path); // needs strdup - } else if (safe_strcmp(reg_key, "usbccgp") == 0) { - // Composite (multi-interface) devices are identified by their use of - // the USB Common Class Generic Parent driver - if (set_composite_device(ctx, dev_info_data.DevInst, priv) == LIBUSB_SUCCESS) { - priv->apib = &windows_winusb_backend; - } else { - priv->apib = &windows_template_backend; + for (api = 0; api<USB_API_MAX; api++) { + if (safe_strcmp(reg_key, usb_api_backend[api].driver_name) == 0) { + priv->apib = &usb_api_backend[api]; + switch(api) { + case USB_API_COMPOSITE: + set_composite_device(ctx, dev_info_data.DevInst, priv); + break; + case USB_API_HID: + set_hid_device(ctx, priv); + break; + default: + // For other devices, the first interface is the same as the device + priv->usb_interface[0].path = safe_strdup(priv->path); // needs strdup + // The following is needed if we want to API calls to work for both simple + // and composite devices, as + for(k=0; k<USB_MAXINTERFACES; k++) { + priv->usb_interface[k].apib = &usb_api_backend[api]; + } + break; + } } - } else { - // All other USB access drivers (HID, Mass Storage) are ignored for now } break; } } if (!found) { + // TODO: warn about ghost drivers and how to remove them usbi_warn(ctx, "could not match %s with a libusb device.", dev_interface_details->DevicePath); continue; } @@ -1145,7 +1246,8 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered */ static void windows_exit(void) { - struct windows_hcd_priv* hcd_tmp; + struct windows_hcd_priv* hcd_tmp; + int i; while (hcd_root != NULL) { @@ -1155,7 +1257,9 @@ static void windows_exit(void) safe_free(hcd_tmp); } - winusb_api_exit(); + for (i=0; i<USB_API_MAX; i++) { + usb_api_backend[i].exit(); + } exit_polling(); SetThreadAffinityMask(GetCurrentThread(), old_affinity_mask); @@ -1219,11 +1323,7 @@ static void windows_close(struct libusb_device_handle *dev_handle) { struct windows_device_priv *priv = __device_priv(dev_handle->dev); - // Incidentally, we could just add a "close" member and have it not - // warn. - if(&windows_winusb_backend == priv->apib) { - winusb_close(dev_handle); - } + priv->apib->close(dev_handle); } static int windows_get_configuration(struct libusb_device_handle *dev_handle, int *config) @@ -1623,75 +1723,133 @@ const struct usbi_os_backend windows_backend = { }; -const struct windows_driver_backend windows_winusb_backend = { - "WinUSB", - winusb_open, - winusb_claim_interface, - winusb_set_interface_altsetting, - winusb_release_interface, - winusb_clear_halt, - winusb_reset_device, - winusb_submit_bulk_transfer, - winusb_submit_iso_transfer, - winusb_submit_control_transfer, - winusb_abort_control, - winusb_abort_transfers, -}; - +/* + * USB API backends + */ +static int unsupported_init(struct libusb_context *ctx) { + return LIBUSB_SUCCESS; +} +static int unsupported_exit(void) { + return LIBUSB_SUCCESS; +} static int unsupported_open(struct libusb_device_handle *dev_handle) { - PRINT_UNSUPPORTED_API(unsupported_open); + PRINT_UNSUPPORTED_API(open); +} +static void unsupported_close(struct libusb_device_handle *dev_handle) { + usbi_dbg("unsupported API call for 'close'"); } static int unsupported_claim_interface(struct libusb_device_handle *dev_handle, int iface) { - PRINT_UNSUPPORTED_API(unsupported_claim_interface); + PRINT_UNSUPPORTED_API(claim_interface); } static int unsupported_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting) { - PRINT_UNSUPPORTED_API(unsupported_set_interface_altsetting); + PRINT_UNSUPPORTED_API(set_interface_altsetting); } static int unsupported_release_interface(struct libusb_device_handle *dev_handle, int iface) { - PRINT_UNSUPPORTED_API(unsupported_release_interface); + PRINT_UNSUPPORTED_API(release_interface); } static int unsupported_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint) { - PRINT_UNSUPPORTED_API(unsupported_clear_halt); + PRINT_UNSUPPORTED_API(clear_halt); } static int unsupported_reset_device(struct libusb_device_handle *dev_handle) { - PRINT_UNSUPPORTED_API(unsupported_reset_device); + PRINT_UNSUPPORTED_API(reset_device); } static int unsupported_submit_bulk_transfer(struct usbi_transfer *itransfer) { - PRINT_UNSUPPORTED_API(unsupported_submit_bulk_transfer); + PRINT_UNSUPPORTED_API(submit_bulk_transfer); } static int unsupported_submit_iso_transfer(struct usbi_transfer *itransfer) { - PRINT_UNSUPPORTED_API(unsupported_submit_iso_transfer); + PRINT_UNSUPPORTED_API(submit_iso_transfer); } static int unsupported_submit_control_transfer(struct usbi_transfer *itransfer) { - PRINT_UNSUPPORTED_API(unsupported_submit_control_transfer); + PRINT_UNSUPPORTED_API(submit_control_transfer); } static int unsupported_abort_control(struct usbi_transfer *itransfer) { - PRINT_UNSUPPORTED_API(unsupported_abort_control); + PRINT_UNSUPPORTED_API(abort_control); } static int unsupported_abort_transfers(struct usbi_transfer *itransfer) { - PRINT_UNSUPPORTED_API(unsupported_abort_transfers); -} - -const struct windows_driver_backend windows_template_backend = { - "Dummy", - unsupported_open, - unsupported_claim_interface, - unsupported_set_interface_altsetting, - unsupported_release_interface, - unsupported_clear_halt, - unsupported_reset_device, - unsupported_submit_bulk_transfer, - unsupported_submit_iso_transfer, - unsupported_submit_control_transfer, - unsupported_abort_control, - unsupported_abort_transfers, + PRINT_UNSUPPORTED_API(abort_transfers); +} + +const struct windows_usb_api_backend usb_api_backend[USB_API_MAX] = { + { + "Dummy", + "", + USB_API_TEMPLATE, + unsupported_init, + unsupported_exit, + unsupported_open, + unsupported_close, + unsupported_claim_interface, + unsupported_set_interface_altsetting, + unsupported_release_interface, + unsupported_clear_halt, + unsupported_reset_device, + unsupported_submit_bulk_transfer, + unsupported_submit_iso_transfer, + unsupported_submit_control_transfer, + unsupported_abort_control, + unsupported_abort_transfers, + }, { + "Composite", + "usbccgp", + USB_API_COMPOSITE, + composite_init, + composite_exit, + composite_open, + composite_close, + composite_claim_interface, + composite_set_interface_altsetting, + composite_release_interface, + composite_clear_halt, + composite_reset_device, + composite_submit_bulk_transfer, + composite_submit_iso_transfer, + composite_submit_control_transfer, + composite_abort_control, + composite_abort_transfers, + }, { + "WinUSB", + "WinUSB", + USB_API_WINUSB, + winusb_init, + winusb_exit, + winusb_open, + winusb_close, + winusb_claim_interface, + winusb_set_interface_altsetting, + winusb_release_interface, + winusb_clear_halt, + winusb_reset_device, + winusb_submit_bulk_transfer, + winusb_submit_iso_transfer, + winusb_submit_control_transfer, + winusb_abort_control, + winusb_abort_transfers, + }, { + "HID", + "HidUsb", + USB_API_HID, + hid_init, + hid_exit, + hid_open, + hid_close, + unsupported_claim_interface, + unsupported_set_interface_altsetting, + unsupported_release_interface, + unsupported_clear_halt, + unsupported_reset_device, + unsupported_submit_bulk_transfer, + unsupported_submit_iso_transfer, + unsupported_submit_control_transfer, + unsupported_abort_control, + unsupported_abort_transfers, + }, }; /* * WinUSB API functions */ -static int winusb_api_init(struct libusb_context *ctx) +static int winusb_init(struct libusb_context *ctx) { DLL_LOAD(winusb.dll, WinUsb_Initialize, TRUE); DLL_LOAD(winusb.dll, WinUsb_Free, TRUE); @@ -1715,12 +1873,15 @@ static int winusb_api_init(struct libusb_context *ctx) return LIBUSB_SUCCESS; } -static int winusb_api_exit(void) +static int winusb_exit(void) { api_winusb_available = false; return LIBUSB_SUCCESS; } +// NB: open and close must ensure that they only handle interface of +// the right API type, as these functions can be called wholesale from +// composite_open(), with interfaces belonging to different APIs static int winusb_open(struct libusb_device_handle *dev_handle) { struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); @@ -1734,7 +1895,8 @@ static int winusb_open(struct libusb_device_handle *dev_handle) // WinUSB requires a seperate handle for each interface for (i = 0; i < USB_MAXINTERFACES; i++) { - if (priv->usb_interface[i].path != NULL) { + if ( (priv->usb_interface[i].path != NULL) + && (priv->usb_interface[i].apib->id == USB_API_WINUSB) ) { file_handle = CreateFileA(priv->usb_interface[i].path, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); if (file_handle == INVALID_HANDLE_VALUE) { @@ -1758,6 +1920,7 @@ static int winusb_open(struct libusb_device_handle *dev_handle) static void winusb_close(struct libusb_device_handle *dev_handle) { struct windows_device_handle_priv *handle_priv = (struct windows_device_handle_priv *)dev_handle->os_priv; + struct windows_device_priv *priv = __device_priv(dev_handle->dev); HANDLE file_handle; int i; @@ -1765,9 +1928,11 @@ static void winusb_close(struct libusb_device_handle *dev_handle) return; for (i = 0; i < USB_MAXINTERFACES; i++) { - file_handle = handle_priv->interface_handle[i].file; - if ( (file_handle != 0) && (file_handle != INVALID_HANDLE_VALUE)) { - CloseHandle(file_handle); + if (priv->usb_interface[i].apib->id == USB_API_WINUSB) { + file_handle = handle_priv->interface_handle[i].file; + if ( (file_handle != 0) && (file_handle != INVALID_HANDLE_VALUE)) { + CloseHandle(file_handle); + } } } } @@ -1777,7 +1942,7 @@ static int winusb_claim_interface(struct libusb_device_handle *dev_handle, int i struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); struct windows_device_handle_priv *handle_priv = (struct windows_device_handle_priv *)dev_handle->os_priv; struct windows_device_priv *priv = __device_priv(dev_handle->dev); - bool is_composite = (safe_strcmp(priv->driver, "usbccgp") == 0); + bool is_composite = (priv->apib->id == USB_API_COMPOSITE); HANDLE file_handle, winusb_handle; USB_INTERFACE_DESCRIPTOR if_desc; UCHAR policy; @@ -1891,6 +2056,7 @@ static int winusb_release_interface(struct libusb_device_handle *dev_handle, int /* * Return the first valid WinUSB handle, for control transfers */ +// TODO: make this function generic for all drivers static int winusb_get_valid_interface(struct windows_device_handle_priv *handle_priv) { int i; @@ -1985,6 +2151,7 @@ static int winusb_submit_control_transfer(struct usbi_transfer *itransfer) // Use priv_transfer to store data needed for async polling transfer_priv->pollable_fd = wfd; + transfer_priv->interface_number = current_interface; return LIBUSB_SUCCESS; } @@ -2068,6 +2235,7 @@ static int winusb_submit_bulk_transfer(struct usbi_transfer *itransfer) } transfer_priv->pollable_fd = wfd; + transfer_priv->interface_number = current_interface; return LIBUSB_SUCCESS; } @@ -2199,3 +2367,272 @@ static int winusb_reset_device(struct libusb_device_handle *dev_handle) return LIBUSB_SUCCESS; } + + +/* + * HID API functions + */ +static int hid_init(struct libusb_context *ctx) +{ + DLL_LOAD(hid.dll, HidD_GetAttributes, TRUE); + DLL_LOAD(hid.dll, HidD_GetHidGuid, TRUE); + DLL_LOAD(hid.dll, HidD_GetPreparsedData, TRUE); + DLL_LOAD(hid.dll, HidD_FreePreparsedData, TRUE); + DLL_LOAD(hid.dll, HidD_GetManufacturerString, TRUE); + DLL_LOAD(hid.dll, HidD_GetProductString, TRUE); + DLL_LOAD(hid.dll, HidD_GetSerialNumberString, TRUE); + DLL_LOAD(hid.dll, HidP_GetCaps, TRUE); + DLL_LOAD(hid.dll, HidD_SetNumInputBuffers, TRUE); + DLL_LOAD(hid.dll, HidD_SetFeature, TRUE); + DLL_LOAD(hid.dll, HidD_GetFeature, TRUE); + DLL_LOAD(hid.dll, HidD_GetPhysicalDescriptor, TRUE); + DLL_LOAD(hid.dll, HidD_GetInputReport, FALSE); + DLL_LOAD(hid.dll, HidD_SetOutputReport, FALSE); + + api_hid_available = true; + return LIBUSB_SUCCESS; +} + +static int hid_exit(void) +{ + api_hid_available = false; + return LIBUSB_SUCCESS; +} + +// NB: open and close must ensure that they only handle interface of +// the right API type, as these functions can be called wholesale from +// composite_open(), with interfaces belonging to different APIs +static int hid_open(struct libusb_device_handle *dev_handle) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = (struct windows_device_handle_priv *)dev_handle->os_priv; + + HANDLE file_handle; + int i; + + CHECK_HID_AVAILABLE; + + // TODO: do we need separate IF handles with HIDs? + for (i = 0; i < USB_MAXINTERFACES; i++) { + if ( (priv->usb_interface[i].path != NULL) + && (priv->usb_interface[i].apib->id == USB_API_HID) ) { +// file_handle = CreateFileA(priv->usb_interface[i].path, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, +// NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); + + /* + * http://www.lvr.com/hidfaq.htm: Why do I receive "Access denied" when attempting to access my HID? + * "Windows 2000 and later have exclusive read/write access to HIDs that are configured as a system + * keyboards or mice. An application can obtain a handle to a system keyboard or mouse by not + * requesting READ or WRITE access with CreateFile. Applications can then use HidD_SetFeature and + * HidD_GetFeature (if the device supports Feature reports)." + */ + file_handle = CreateFileA(priv->usb_interface[i].path, 0, FILE_SHARE_WRITE | FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); + // TODO: open above rw and fallback to no r/w if failure + if (file_handle == INVALID_HANDLE_VALUE) { + usbi_err(ctx, "could not open device %s (interface %d): %s", priv->path, i, windows_error_str(0)); + switch(GetLastError()) { + case ERROR_FILE_NOT_FOUND: // The device was disconnected + return LIBUSB_ERROR_NO_DEVICE; + case ERROR_ACCESS_DENIED: + return LIBUSB_ERROR_ACCESS; + default: + return LIBUSB_ERROR_IO; + } + } + handle_priv->interface_handle[i].file = file_handle; + } + } + + return LIBUSB_SUCCESS; +} + +static void hid_close(struct libusb_device_handle *dev_handle) +{ + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = (struct windows_device_handle_priv *)dev_handle->os_priv; + HANDLE file_handle; + int i; + + if (!api_hid_available) + return; + + for (i = 0; i < USB_MAXINTERFACES; i++) { + if (priv->usb_interface[i].apib->id == USB_API_HID) { + file_handle = handle_priv->interface_handle[i].file; + if ( (file_handle != 0) && (file_handle != INVALID_HANDLE_VALUE)) { + CloseHandle(file_handle); + } + } + } +} + +/* + * Composite API functions + */ +static int composite_init(struct libusb_context *ctx) +{ + return LIBUSB_SUCCESS; +} + +static int composite_exit(void) +{ + return LIBUSB_SUCCESS; +} + +static int composite_open(struct libusb_device_handle *dev_handle) +{ + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + unsigned api; + int r; + uint8_t flag = 1<<USB_API_WINUSB; + + for (api=USB_API_WINUSB; api<USB_API_MAX; api++) { + if (priv->composite_api_flags & flag) { + r = usb_api_backend[api].open(dev_handle); + if (r != LIBUSB_SUCCESS) { + return r; + } + } + flag <<= 1; + } + return LIBUSB_SUCCESS; +} + +static void composite_close(struct libusb_device_handle *dev_handle) +{ + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + unsigned api; + uint8_t flag = 1<<USB_API_WINUSB; + + for (api=USB_API_WINUSB; api<USB_API_MAX; api++) { + if (priv->composite_api_flags & flag) { + usb_api_backend[api].close(dev_handle); + } + flag <<= 1; + } +} + +static int composite_claim_interface(struct libusb_device_handle *dev_handle, int iface) +{ + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + return priv->usb_interface[iface].apib->claim_interface(dev_handle, iface); +} + +static int composite_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting) +{ + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + return priv->usb_interface[iface].apib->set_interface_altsetting(dev_handle, iface, altsetting); +} + +static int composite_release_interface(struct libusb_device_handle *dev_handle, int iface) +{ + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + return priv->usb_interface[iface].apib->release_interface(dev_handle, iface); +} + +static int composite_submit_control_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + struct windows_device_priv *priv = __device_priv(transfer->dev_handle->dev); + int i; + + // Interface doesn't matter for control. Might have to review this section + // for multiple drivers, if one API is better for control than another. + for (i=0; i<USB_MAXINTERFACES; i++) { + if (priv->usb_interface[i].path != NULL) { + return priv->usb_interface[i].apib->submit_control_transfer(itransfer); + } + } + + usbi_err(ctx, "no libusb supported interfaces to complete request"); + return LIBUSB_ERROR_NOT_FOUND; +} + +static int composite_submit_bulk_transfer(struct usbi_transfer *itransfer) { + struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + struct windows_device_handle_priv *handle_priv = (struct windows_device_handle_priv *)transfer->dev_handle->os_priv; + struct windows_device_priv *priv = __device_priv(transfer->dev_handle->dev); + int current_interface; + + current_interface = winusb_interface_by_endpoint(priv, handle_priv, transfer->endpoint); + if (current_interface < 0) { + usbi_err(ctx, "unable to match endpoint to an open interface - cancelling transfer"); + return LIBUSB_ERROR_NOT_FOUND; + } + + return priv->usb_interface[current_interface].apib->submit_bulk_transfer(itransfer); +} + +static int composite_submit_iso_transfer(struct usbi_transfer *itransfer) { + struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + struct windows_device_handle_priv *handle_priv = (struct windows_device_handle_priv *)transfer->dev_handle->os_priv; + struct windows_device_priv *priv = __device_priv(transfer->dev_handle->dev); + int current_interface; + + current_interface = winusb_interface_by_endpoint(priv, handle_priv, transfer->endpoint); + if (current_interface < 0) { + usbi_err(ctx, "unable to match endpoint to an open interface - cancelling transfer"); + return LIBUSB_ERROR_NOT_FOUND; + } + + return priv->usb_interface[current_interface].apib->submit_iso_transfer(itransfer); +} + +static int composite_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = (struct windows_device_handle_priv *)dev_handle->os_priv; + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + int current_interface; + + current_interface = winusb_interface_by_endpoint(priv, handle_priv, endpoint); + if (current_interface < 0) { + usbi_err(ctx, "unable to match endpoint to an open interface - cannot clear"); + return LIBUSB_ERROR_NOT_FOUND; + } + + return priv->usb_interface[current_interface].apib->clear_halt(dev_handle, endpoint); +} + +static int composite_abort_control(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + struct windows_device_priv *priv = __device_priv(transfer->dev_handle->dev); + + return priv->usb_interface[transfer_priv->interface_number].apib->abort_control(itransfer); +} + +static int composite_abort_transfers(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + struct windows_device_priv *priv = __device_priv(transfer->dev_handle->dev); + + return priv->usb_interface[transfer_priv->interface_number].apib->abort_transfers(itransfer); +} + + +static int composite_reset_device(struct libusb_device_handle *dev_handle) +{ + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + unsigned api; + int r; + uint8_t flag = 1<<USB_API_WINUSB; + + for (api=USB_API_WINUSB; api<USB_API_MAX; api++) { + if (priv->composite_api_flags & flag) { + r = usb_api_backend[api].reset_device(dev_handle); + if (r != LIBUSB_SUCCESS) { + return r; + } + } + flag <<= 1; + } + return LIBUSB_SUCCESS; +} diff --git a/libusb/os/windows_usb.h b/libusb/os/windows_usb.h index 692d2d2..63f89cd 100644 --- a/libusb/os/windows_usb.h +++ b/libusb/os/windows_usb.h @@ -75,10 +75,15 @@ void inline upperize(char* str) { #define wchar_to_utf8_ms(wstr, str, strlen) WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, strlen, NULL, NULL) #define ERRNO GetLastError() -// This is used to support multiple kernel drivers in Windows. -struct windows_driver_backend { - const char *name; // A human-readable name for your backend, e.g. "WinUSB" +// This is used to support multiple kernel drivers and USB APIs in Windows. +struct windows_usb_api_backend { + const char *name; // A human-readable name for your backend, e.g. "WinUSB" + const char *driver_name; // Driver's name, without .sys, e.g. "usbccgp" + const uint8_t id; + int (*init)(struct libusb_context *ctx); + int (*exit)(void); int (*open)(struct libusb_device_handle *dev_handle); + void (*close)(struct libusb_device_handle *dev_handle); int (*claim_interface)(struct libusb_device_handle *dev_handle, int iface); int (*set_interface_altsetting)(struct libusb_device_handle *dev_handle, int iface, int altsetting); int (*release_interface)(struct libusb_device_handle *dev_handle, int iface); @@ -90,8 +95,13 @@ struct windows_driver_backend { int (*abort_control)(struct usbi_transfer *itransfer); int (*abort_transfers)(struct usbi_transfer *itransfer); }; -extern const struct windows_driver_backend windows_template_backend; -extern const struct windows_driver_backend windows_winusb_backend; + +#define USB_API_TEMPLATE 0 +#define USB_API_COMPOSITE 1 +#define USB_API_WINUSB 2 +#define USB_API_HID 3 +#define USB_API_MAX 4 +extern const struct windows_usb_api_backend usb_api_backend[USB_API_MAX]; #define PRINT_UNSUPPORTED_API(fname) \ usbi_dbg("unsupported API call for '" \ @@ -131,12 +141,13 @@ struct windows_device_priv { ULONG connection_index; // also required for some usermode ops char *path; // path used by Windows to reference the USB node struct { - char *path; // each interface has a path as well + char *path; // each interface needs a Windows device interface path, + struct windows_usb_api_backend const *apib; // an API backend (multiple drivers support), int8_t nb_endpoints; // and a set of endpoint addresses (USB_MAXENDPOINTS) uint8_t *endpoint; } usb_interface[USB_MAXINTERFACES]; - char *driver; // driver name (eg WinUSB, USBSTOR, HidUsb, etc) - struct windows_driver_backend const *apib; + struct windows_usb_api_backend const *apib; + uint8_t composite_api_flags; uint8_t active_config; USB_DEVICE_DESCRIPTOR dev_descriptor; unsigned char **config_descriptor; // list of pointers to the cached config descriptors @@ -147,13 +158,14 @@ static inline void windows_device_priv_init(struct windows_device_priv* p) { p->parent_dev = NULL; p->connection_index = 0; p->path = NULL; - p->driver = NULL; - p->apib = &windows_template_backend; + p->apib = &usb_api_backend[USB_API_TEMPLATE]; + p->composite_api_flags = 0; p->active_config = 0; p->config_descriptor = NULL; memset(&(p->dev_descriptor), 0, sizeof(USB_DEVICE_DESCRIPTOR)); for (i=0; i<USB_MAXINTERFACES; i++) { p->usb_interface[i].path = NULL; + p->usb_interface[i].apib = &usb_api_backend[USB_API_TEMPLATE]; p->usb_interface[i].nb_endpoints = 0; p->usb_interface[i].endpoint = NULL; } @@ -162,7 +174,6 @@ static inline void windows_device_priv_init(struct windows_device_priv* p) { static inline void windows_device_priv_release(struct windows_device_priv* p, int num_configurations) { int i; safe_free(p->path); - safe_free(p->driver); if ((num_configurations > 0) && (p->config_descriptor != NULL)) { for (i=0; i < num_configurations; i++) safe_free(p->config_descriptor[i]); @@ -197,6 +208,7 @@ static inline struct windows_device_handle_priv *__device_handle_priv( // used for async polling functions struct windows_transfer_priv { struct winfd pollable_fd; + uint8_t interface_number; }; @@ -380,3 +392,53 @@ DLL_DECLARE(WINAPI, BOOL, WinUsb_AbortPipe, (WINUSB_INTERFACE_HANDLE, UCHAR)); DLL_DECLARE(WINAPI, BOOL, WinUsb_FlushPipe, (WINUSB_INTERFACE_HANDLE, UCHAR)); + +/* hid.dll interface */ + +#pragma pack(1) +typedef struct { + ULONG Size; + USHORT VendorID; + USHORT ProductID; + USHORT VersionNumber; +} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES; +#pragma pack() + +typedef USHORT USAGE; +typedef struct { + USAGE Usage; + USAGE UsagePage; + USHORT InputReportByteLength; + USHORT OutputReportByteLength; + USHORT FeatureReportByteLength; + USHORT Reserved[17]; + USHORT NumberLinkCollectionNodes; + USHORT NumberInputButtonCaps; + USHORT NumberInputValueCaps; + USHORT NumberInputDataIndices; + USHORT NumberOutputButtonCaps; + USHORT NumberOutputValueCaps; + USHORT NumberOutputDataIndices; + USHORT NumberFeatureButtonCaps; + USHORT NumberFeatureValueCaps; + USHORT NumberFeatureDataIndices; +} HIDP_CAPS, *PHIDP_CAPS; + +typedef void* PHIDP_PREPARSED_DATA; +DLL_DECLARE(WINAPI, BOOL, HidD_GetAttributes, (HANDLE, PHIDD_ATTRIBUTES)); +DLL_DECLARE(WINAPI, VOID, HidD_GetHidGuid, (LPGUID)); +DLL_DECLARE(WINAPI, BOOL, HidD_GetPreparsedData, + (HANDLE, PHIDP_PREPARSED_DATA *)); +DLL_DECLARE(WINAPI, BOOL, HidD_FreePreparsedData, (PHIDP_PREPARSED_DATA)); +DLL_DECLARE(WINAPI, BOOL, HidD_GetManufacturerString, (HANDLE, PVOID, ULONG)); +DLL_DECLARE(WINAPI, BOOL, HidD_GetProductString, (HANDLE, PVOID, ULONG)); +DLL_DECLARE(WINAPI, BOOL, HidD_GetSerialNumberString, (HANDLE, PVOID, ULONG)); +DLL_DECLARE(WINAPI, NTSTATUS, HidP_GetCaps, + (PHIDP_PREPARSED_DATA, PHIDP_CAPS)); +DLL_DECLARE(WINAPI, BOOL, HidD_SetNumInputBuffers, (HANDLE, ULONG)); +DLL_DECLARE(WINAPI, BOOL, HidD_SetFeature, (HANDLE, PVOID, ULONG)); +DLL_DECLARE(WINAPI, BOOL, HidD_GetFeature, (HANDLE, PVOID, ULONG)); +DLL_DECLARE(WINAPI, BOOL, HidD_GetPhysicalDescriptor, (HANDLE, PVOID, ULONG)); +DLL_DECLARE(WINAPI, BOOL, HidD_GetInputReport, (HANDLE, PVOID, ULONG)); +DLL_DECLARE(WINAPI, BOOL, HidD_SetOutputReport, (HANDLE, PVOID, ULONG)); + |