diff options
Diffstat (limited to 'libusb/os/windows_usb.c')
-rw-r--r-- | libusb/os/windows_usb.c | 663 |
1 files changed, 550 insertions, 113 deletions
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; +} |