diff options
-rw-r--r-- | libusb/core.c | 6 | ||||
-rw-r--r-- | libusb/libusbi.h | 1 | ||||
-rw-r--r-- | libusb/os/windows_usbdk.c | 952 | ||||
-rw-r--r-- | libusb/os/windows_usbdk.h | 126 | ||||
-rw-r--r-- | libusb/os/windows_winusb.c | 4 | ||||
-rw-r--r-- | libusb/version_nano.h | 2 |
6 files changed, 1090 insertions, 1 deletions
diff --git a/libusb/core.c b/libusb/core.c index 067cd49..90491e6 100644 --- a/libusb/core.c +++ b/libusb/core.c @@ -53,7 +53,13 @@ const struct usbi_os_backend * const usbi_backend = &openbsd_backend; #elif defined(OS_NETBSD) const struct usbi_os_backend * const usbi_backend = &netbsd_backend; #elif defined(OS_WINDOWS) + +#if defined(USE_USBDK) +const struct usbi_os_backend * const usbi_backend = &usbdk_backend; +#else const struct usbi_os_backend * const usbi_backend = &windows_backend; +#endif + #elif defined(OS_WINCE) const struct usbi_os_backend * const usbi_backend = &wince_backend; #elif defined(OS_HAIKU) diff --git a/libusb/libusbi.h b/libusb/libusbi.h index d69d613..2d229da 100644 --- a/libusb/libusbi.h +++ b/libusb/libusbi.h @@ -1118,6 +1118,7 @@ extern const struct usbi_os_backend darwin_backend; extern const struct usbi_os_backend openbsd_backend; extern const struct usbi_os_backend netbsd_backend; extern const struct usbi_os_backend windows_backend; +extern const struct usbi_os_backend usbdk_backend; extern const struct usbi_os_backend wince_backend; extern const struct usbi_os_backend haiku_usb_raw_backend; diff --git a/libusb/os/windows_usbdk.c b/libusb/os/windows_usbdk.c new file mode 100644 index 0000000..3336d9a --- /dev/null +++ b/libusb/os/windows_usbdk.c @@ -0,0 +1,952 @@ +/* + * windows UsbDk backend for libusb 1.0 + * Copyright © 2014 Red Hat, Inc. + + * Authors: + * Dmitry Fleytman <dmitry@daynix.com> + * Pavel Gurvich <pavel@daynix.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#if defined(USE_USBDK) + +#include <windows.h> +#include <cfgmgr32.h> +#include <stdio.h> + +#include "libusbi.h" +#include "windows_common.h" +#include "windows_nt_common.h" + +#define ULONG64 uint64_t +#define PVOID64 uint64_t + +typedef CONST WCHAR *PCWCHAR; +#define wcsncpy_s wcsncpy + +#include "windows_usbdk.h" + +#if !defined(STATUS_SUCCESS) +typedef LONG NTSTATUS; +#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) +#endif + +#if !defined(STATUS_CANCELLED) +#define STATUS_CANCELLED ((NTSTATUS)0xC0000120L) +#endif + +#if !defined(STATUS_REQUEST_CANCELED) +#define STATUS_REQUEST_CANCELED ((NTSTATUS)0xC0000703L) +#endif + +#if !defined(USBD_SUCCESS) +typedef int32_t USBD_STATUS; +#define USBD_SUCCESS(Status) ((USBD_STATUS) (Status) >= 0) +#define USBD_PENDING(Status) ((ULONG) (Status) >> 30 == 1) +#define USBD_ERROR(Status) ((USBD_STATUS) (Status) < 0) +#define USBD_STATUS_STALL_PID ((USBD_STATUS) 0xc0000004) +#define USBD_STATUS_ENDPOINT_HALTED ((USBD_STATUS) 0xc0000030) +#define USBD_STATUS_BAD_START_FRAME ((USBD_STATUS) 0xc0000a00) +#define USBD_STATUS_TIMEOUT ((USBD_STATUS) 0xc0006000) +#define USBD_STATUS_CANCELED ((USBD_STATUS) 0xc0010000) +#endif + +static int concurrent_usage = -1; +static int init_succeeded = 0; + +struct usbdk_device_priv { + USB_DK_DEVICE_INFO info; + PUSB_CONFIGURATION_DESCRIPTOR *config_descriptors; + HANDLE redirector_handle; + uint8_t active_configuration; +}; + +struct usbdk_device_handle_priv { + //This field is to solve compilation error on VS2013: + //error C2016: C requires that a struct or union has at least one member + int dummy; +}; + +struct usbdk_transfer_priv { + USB_DK_TRANSFER_REQUEST request; + struct winfd pollable_fd; + PULONG64 IsochronousPacketsArray; + PUSB_DK_ISO_TRANSFER_RESULT IsochronousResultsArray; +}; + +static inline struct usbdk_device_priv *_usbdk_device_priv(struct libusb_device *dev) +{ + return (struct usbdk_device_priv*) dev->os_priv; +} + +static inline struct usbdk_transfer_priv *_usbdk_transfer_priv(struct usbi_transfer *itransfer) +{ + return (struct usbdk_transfer_priv*) usbi_transfer_get_os_priv(itransfer); +} + +static struct { + HMODULE module; + + USBDK_GET_DEVICES_LIST GetDevicesList; + USBDK_RELEASE_DEVICES_LIST ReleaseDevicesList; + USBDK_START_REDIRECT StartRedirect; + USBDK_STOP_REDIRECT StopRedirect; + USBDK_GET_CONFIGURATION_DESCRIPTOR GetConfigurationDescriptor; + USBDK_RELEASE_CONFIGURATION_DESCRIPTOR ReleaseConfigurationDescriptor; + USBDK_READ_PIPE ReadPipe; + USBDK_WRITE_PIPE WritePipe; + USBDK_ABORT_PIPE AbortPipe; + USBDK_RESET_PIPE ResetPipe; + USBDK_SET_ALTSETTING SetAltsetting; + USBDK_RESET_DEVICE ResetDevice; + USBDK_GET_REDIRECTOR_SYSTEM_HANDLE GetRedirectorSystemHandle; +} usbdk_helper; + +static FARPROC get_usbdk_proc_addr(struct libusb_context *ctx, LPCSTR api_name) +{ + FARPROC api_ptr = GetProcAddress(usbdk_helper.module, api_name); + if (api_ptr == NULL) { + DWORD err = GetLastError(); + usbi_err(ctx, "UsbDkHelper API %s not found, error %d", api_name, err); + } + + return api_ptr; +} + +static void unload_usbdk_helper_dll(void) +{ + FreeLibrary(usbdk_helper.module); +} + +static int load_usbdk_helper_dll(struct libusb_context *ctx) +{ + usbdk_helper.module = LoadLibraryA("UsbDkHelper"); + if (usbdk_helper.module == NULL) { + DWORD err = GetLastError(); + usbi_err(ctx, "Failed to load UsbDkHelper.dll, error %d", err); + return LIBUSB_ERROR_NOT_FOUND; + } + + usbdk_helper.GetDevicesList = (USBDK_GET_DEVICES_LIST) get_usbdk_proc_addr(ctx, "UsbDk_GetDevicesList"); + if (usbdk_helper.GetDevicesList == NULL) { + goto error_unload; + } + + usbdk_helper.ReleaseDevicesList = (USBDK_RELEASE_DEVICES_LIST) get_usbdk_proc_addr(ctx, "UsbDk_ReleaseDevicesList"); + if (usbdk_helper.ReleaseDevicesList == NULL) { + goto error_unload; + } + + usbdk_helper.StartRedirect = (USBDK_START_REDIRECT) get_usbdk_proc_addr(ctx, "UsbDk_StartRedirect"); + if (usbdk_helper.StartRedirect == NULL) { + goto error_unload; + } + + usbdk_helper.StopRedirect = (USBDK_STOP_REDIRECT) get_usbdk_proc_addr(ctx, "UsbDk_StopRedirect"); + if (usbdk_helper.StopRedirect == NULL) { + goto error_unload; + } + + usbdk_helper.GetConfigurationDescriptor = (USBDK_GET_CONFIGURATION_DESCRIPTOR) get_usbdk_proc_addr(ctx, "UsbDk_GetConfigurationDescriptor"); + if (usbdk_helper.GetConfigurationDescriptor == NULL) { + goto error_unload; + } + + usbdk_helper.ReleaseConfigurationDescriptor = (USBDK_RELEASE_CONFIGURATION_DESCRIPTOR) get_usbdk_proc_addr(ctx, "UsbDk_ReleaseConfigurationDescriptor"); + if (usbdk_helper.ReleaseConfigurationDescriptor == NULL) { + goto error_unload; + } + + usbdk_helper.ReadPipe = (USBDK_READ_PIPE) get_usbdk_proc_addr(ctx, "UsbDk_ReadPipe"); + if (usbdk_helper.ReadPipe == NULL) { + goto error_unload; + } + + usbdk_helper.WritePipe = (USBDK_WRITE_PIPE) get_usbdk_proc_addr(ctx, "UsbDk_WritePipe"); + if (usbdk_helper.WritePipe == NULL) { + goto error_unload; + } + + usbdk_helper.AbortPipe = (USBDK_ABORT_PIPE)get_usbdk_proc_addr(ctx, "UsbDk_AbortPipe"); + if (usbdk_helper.AbortPipe == NULL) { + goto error_unload; + } + + usbdk_helper.ResetPipe = (USBDK_RESET_PIPE)get_usbdk_proc_addr(ctx, "UsbDk_ResetPipe"); + if (usbdk_helper.ResetPipe == NULL) { + goto error_unload; + } + + usbdk_helper.SetAltsetting = (USBDK_SET_ALTSETTING)get_usbdk_proc_addr(ctx, "UsbDk_SetAltsetting"); + if (usbdk_helper.SetAltsetting == NULL) { + goto error_unload; + } + + usbdk_helper.ResetDevice = (USBDK_RESET_DEVICE)get_usbdk_proc_addr(ctx, "UsbDk_ResetDevice"); + if (usbdk_helper.ResetDevice == NULL) { + goto error_unload; + } + + usbdk_helper.GetRedirectorSystemHandle = (USBDK_GET_REDIRECTOR_SYSTEM_HANDLE)get_usbdk_proc_addr(ctx, "UsbDk_GetRedirectorSystemHandle"); + if (usbdk_helper.GetRedirectorSystemHandle == NULL) { + goto error_unload; + } + + return LIBUSB_SUCCESS; + +error_unload: + FreeLibrary(usbdk_helper.module); + return LIBUSB_ERROR_NOT_FOUND; +} + +static int usbdk_init(struct libusb_context *ctx) +{ + int r; + + if ((++concurrent_usage == 0) || !init_succeeded) { + r = load_usbdk_helper_dll(ctx); + if (r) { + return r; + } + + init_polling(); + + r = windows_common_init(ctx); + if (r) { + goto error_roll_back; + } + + init_succeeded = 1; + } + + return LIBUSB_SUCCESS; + +error_roll_back: + windows_common_exit(); + unload_usbdk_helper_dll(); + return r; +} + +static int usbdk_get_session_id_for_device(struct libusb_context *ctx, + PUSB_DK_DEVICE_ID id, + unsigned long* session_id) +{ + char dev_identity[ARRAYSIZE(id->DeviceID) + ARRAYSIZE(id->InstanceID)]; + + if (sprintf(dev_identity, "%S%S", id->DeviceID, id->InstanceID) == -1) { + usbi_warn(ctx, "cannot form device identity", id->DeviceID); + return LIBUSB_ERROR_NOT_SUPPORTED; + } + + *session_id = htab_hash(dev_identity); + + return LIBUSB_SUCCESS; +} + +static void usbdk_release_config_descriptors(struct usbdk_device_priv* p, uint8_t count) +{ + uint8_t i; + for (i = 0; i < count; i++) { + usbdk_helper.ReleaseConfigurationDescriptor(p->config_descriptors[i]); + } + free(p->config_descriptors); + p->config_descriptors = NULL; +} + +static int usbdk_cache_config_descriptors(struct libusb_context *ctx, + struct usbdk_device_priv* p, + PUSB_DK_DEVICE_INFO info) +{ + uint8_t i; + USB_DK_CONFIG_DESCRIPTOR_REQUEST Request; + Request.ID = info->ID; + + p->config_descriptors = calloc(info->DeviceDescriptor.bNumConfigurations, + sizeof(PUSB_CONFIGURATION_DESCRIPTOR)); + if (p->config_descriptors == NULL) { + usbi_err(ctx, "failed to allocate configuration descriptors holder"); + return LIBUSB_ERROR_NO_MEM; + } + + for (i = 0; i < info->DeviceDescriptor.bNumConfigurations; i++) { + ULONG Length; + + Request.Index = i; + if (!usbdk_helper.GetConfigurationDescriptor(&Request, + &p->config_descriptors[i], + &Length)) { + usbi_err(ctx, "failed to retrieve configuration descriptors"); + usbdk_release_config_descriptors(p, i); + return LIBUSB_ERROR_OTHER; + } + } + + return LIBUSB_SUCCESS; +} + +static inline int usbdk_device_priv_init(struct libusb_context *ctx, libusb_device* dev, PUSB_DK_DEVICE_INFO info) +{ + struct usbdk_device_priv* p = _usbdk_device_priv(dev); + p->info = *info; + p->active_configuration = 0; + return usbdk_cache_config_descriptors(ctx, p, info); +} + +static void usbdk_device_init(libusb_device* dev, PUSB_DK_DEVICE_INFO info) +{ + dev->bus_number = (uint8_t) info->FilterID; + dev->port_number = (uint8_t) info->Port; + dev->parent_dev = NULL; + + //Addresses in libusb are 1-based + dev->device_address = (uint8_t) info->Port + 1; + + dev->num_configurations = info->DeviceDescriptor.bNumConfigurations; + dev->device_descriptor = info->DeviceDescriptor; + + switch (info->Speed) + { + case LowSpeed: + dev->speed = LIBUSB_SPEED_LOW; + break; + case FullSpeed: + dev->speed = LIBUSB_SPEED_FULL; + break; + case HighSpeed: + dev->speed = LIBUSB_SPEED_HIGH; + break; + case SuperSpeed: + dev->speed = LIBUSB_SPEED_SUPER; + break; + case NoSpeed: + default: + dev->speed = LIBUSB_SPEED_UNKNOWN; + break; + } +} + +static int usbdk_get_device_list(struct libusb_context *ctx, struct discovered_devs **_discdevs) +{ + int r = LIBUSB_SUCCESS; + ULONG i; + + struct discovered_devs *discdevs = NULL; + ULONG dev_number; + PUSB_DK_DEVICE_INFO devices; + + if(!usbdk_helper.GetDevicesList(&devices, &dev_number)) { + return LIBUSB_ERROR_OTHER; + } + + for (i = 0; i < dev_number; ++i) { + unsigned long session_id; + struct libusb_device *dev = NULL; + + if (usbdk_get_session_id_for_device(ctx, &devices[i].ID, &session_id)) { + continue; + } + + dev = usbi_get_device_by_session_id(ctx, session_id); + if (dev == NULL) { + dev = usbi_alloc_device(ctx, session_id); + if (dev == NULL) { + usbi_err(ctx, "failed to allocate a new device structure"); + continue; + } + + usbdk_device_init(dev, &devices[i]); + if (usbdk_device_priv_init(ctx, dev, &devices[i]) != LIBUSB_SUCCESS) { + libusb_unref_device(dev); + continue; + } + } + + discdevs = discovered_devs_append(*_discdevs, dev); + + libusb_unref_device(dev); + + if (!discdevs) + { + usbi_err(ctx, "cannot append new device to list"); + r = LIBUSB_ERROR_NO_MEM; + goto func_exit; + } + *_discdevs = discdevs; + } + +func_exit: + usbdk_helper.ReleaseDevicesList(devices); + return r; +} + +static void usbdk_exit(void) +{ + if ((--concurrent_usage < 0) && init_succeeded) { + windows_common_exit(); + exit_polling(); + unload_usbdk_helper_dll(); + init_succeeded = 0; + } +} + +static int usbdk_get_device_descriptor(struct libusb_device *dev, unsigned char *buffer, int *host_endian) +{ + struct usbdk_device_priv *priv = _usbdk_device_priv(dev); + + memcpy(buffer, &priv->info.DeviceDescriptor, DEVICE_DESC_LENGTH); + *host_endian = 0; + + return LIBUSB_SUCCESS; +} + +static int usbdk_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian) +{ + struct usbdk_device_priv *priv = _usbdk_device_priv(dev); + PUSB_CONFIGURATION_DESCRIPTOR config_header; + size_t size; + + if (config_index >= dev->num_configurations) + return LIBUSB_ERROR_INVALID_PARAM; + + config_header = (PUSB_CONFIGURATION_DESCRIPTOR) priv->config_descriptors[config_index]; + + size = min(config_header->wTotalLength, len); + memcpy(buffer, config_header, size); + *host_endian = 0; + + return (int)size; +} + +static inline int usbdk_get_active_config_descriptor(struct libusb_device *dev, unsigned char *buffer, size_t len, int *host_endian) +{ + return usbdk_get_config_descriptor(dev, _usbdk_device_priv(dev)->active_configuration, + buffer, len, host_endian); +} + +static int usbdk_open(struct libusb_device_handle *dev_handle) +{ + struct usbdk_device_priv *priv = _usbdk_device_priv(dev_handle->dev); + + priv->redirector_handle = usbdk_helper.StartRedirect(&priv->info.ID); + if (priv->redirector_handle == INVALID_HANDLE_VALUE) + { + usbi_err(DEVICE_CTX(dev_handle->dev), "Redirector startup failed"); + return LIBUSB_ERROR_OTHER; + } + + return LIBUSB_SUCCESS; +} + +static void usbdk_close(struct libusb_device_handle *dev_handle) +{ + struct usbdk_device_priv *priv = _usbdk_device_priv(dev_handle->dev); + + if (!usbdk_helper.StopRedirect(priv->redirector_handle)) + { + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + usbi_err(ctx, "Redirector shutdown failed"); + } +} + +static int usbdk_get_configuration(struct libusb_device_handle *dev_handle, int *config) +{ + *config = _usbdk_device_priv(dev_handle->dev)->active_configuration; + return LIBUSB_SUCCESS; +} + +static int usbdk_set_configuration(struct libusb_device_handle *dev_handle, int config) +{ + UNREFERENCED_PARAMETER(dev_handle); + UNREFERENCED_PARAMETER(config); + return LIBUSB_SUCCESS; +} + +static int usbdk_claim_interface(struct libusb_device_handle *dev_handle, int iface) +{ + UNREFERENCED_PARAMETER(dev_handle); + UNREFERENCED_PARAMETER(iface); + return LIBUSB_SUCCESS; +} + +static int usbdk_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct usbdk_device_priv *priv = _usbdk_device_priv(dev_handle->dev); + + if (!usbdk_helper.SetAltsetting(priv->redirector_handle, iface, altsetting)) { + usbi_err(ctx, "SetAltsetting failed: %s", windows_error_str(0)); + return LIBUSB_ERROR_NO_DEVICE; + } + + return LIBUSB_SUCCESS; +} + +static int usbdk_release_interface(struct libusb_device_handle *dev_handle, int iface) +{ + UNREFERENCED_PARAMETER(dev_handle); + UNREFERENCED_PARAMETER(iface); + return LIBUSB_SUCCESS; +} + +static int usbdk_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct usbdk_device_priv *priv = _usbdk_device_priv(dev_handle->dev); + + if (!usbdk_helper.ResetPipe(priv->redirector_handle, endpoint)) { + usbi_err(ctx, "ResetPipe failed: %s", windows_error_str(0)); + return LIBUSB_ERROR_NO_DEVICE; + } + + return LIBUSB_SUCCESS; +} + +static int usbdk_reset_device(struct libusb_device_handle *dev_handle) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct usbdk_device_priv *priv = _usbdk_device_priv(dev_handle->dev); + + if (!usbdk_helper.ResetDevice(priv->redirector_handle)) { + usbi_err(ctx, "ResetDevice failed: %s", windows_error_str(0)); + return LIBUSB_ERROR_NO_DEVICE; + } + + return LIBUSB_SUCCESS; +} + +static int usbdk_kernel_driver_active(struct libusb_device_handle *dev_handle, int iface) +{ + UNREFERENCED_PARAMETER(dev_handle); + UNREFERENCED_PARAMETER(iface); + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static int usbdk_attach_kernel_driver(struct libusb_device_handle *dev_handle, int iface) +{ + UNREFERENCED_PARAMETER(dev_handle); + UNREFERENCED_PARAMETER(iface); + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static int usbdk_detach_kernel_driver(struct libusb_device_handle *dev_handle, int iface) +{ + UNREFERENCED_PARAMETER(dev_handle); + UNREFERENCED_PARAMETER(iface); + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static void usbdk_destroy_device(struct libusb_device *dev) +{ + struct usbdk_device_priv* p = _usbdk_device_priv(dev); + + if (p->config_descriptors != NULL) + { + usbdk_release_config_descriptors(p, p->info.DeviceDescriptor.bNumConfigurations); + } +} + +void windows_clear_transfer_priv(struct usbi_transfer *itransfer) +{ + struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer); + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + usbi_free_fd(&transfer_priv->pollable_fd); + + if (transfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS){ + safe_free(transfer_priv->IsochronousPacketsArray); + safe_free(transfer_priv->IsochronousResultsArray); + } +} + +static int usbdk_do_control_transfer(struct usbi_transfer *itransfer) +{ + ULONG Length; + TransferResult transResult; + + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct usbdk_device_priv *priv = _usbdk_device_priv(transfer->dev_handle->dev); + struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer); + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + + HANDLE sysHandle = usbdk_helper.GetRedirectorSystemHandle(priv->redirector_handle); + + struct winfd wfd = usbi_create_fd(sysHandle, RW_READ, NULL, NULL); + // Always use the handle returned from usbi_create_fd (wfd.handle) + if (wfd.fd < 0) { + return LIBUSB_ERROR_NO_MEM; + } + + transfer_priv->request.Buffer = (PVOID64)(uintptr_t)transfer->buffer; + transfer_priv->request.BufferLength = transfer->length; + transfer_priv->request.TransferType = ControlTransferType; + transfer_priv->pollable_fd = INVALID_WINFD; + Length = (ULONG)transfer->length; + + if (IS_XFERIN(transfer)) { + transResult = usbdk_helper.ReadPipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped); + } + else { + transResult = usbdk_helper.WritePipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped); + } + + + switch (transResult) + { + case TransferSuccess: + wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY; + wfd.overlapped->InternalHigh = (DWORD)Length; + break; + case TransferSuccessAsync: + break; + case TransferFailure: + { + usbi_err(ctx, "ControlTransfer failed: %s", windows_error_str(0)); + usbi_free_fd(&wfd); + return LIBUSB_ERROR_IO; + } + } + + // Use priv_transfer to store data needed for async polling + transfer_priv->pollable_fd = wfd; + usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, POLLIN); + return LIBUSB_SUCCESS; +} + +static int usbdk_do_bulk_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct usbdk_device_priv *priv = _usbdk_device_priv(transfer->dev_handle->dev); + struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer); + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + struct winfd wfd; + TransferResult transferRes; + HANDLE sysHandle; + + transfer_priv->request.Buffer = (PVOID64) (uintptr_t) transfer->buffer; + transfer_priv->request.BufferLength = transfer->length; + transfer_priv->request.EndpointAddress = transfer->endpoint; + + switch (transfer->type) + { + case LIBUSB_TRANSFER_TYPE_BULK: + transfer_priv->request.TransferType = BulkTransferType; + break; + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + transfer_priv->request.TransferType = IntertuptTransferType; + break; + default: + usbi_err(ctx, "Wrong transfer type (%d) in usbdk_do_bulk_transfer. %s", transfer->type, windows_error_str(0)); + return LIBUSB_ERROR_INVALID_PARAM; + } + + transfer_priv->pollable_fd = INVALID_WINFD; + + sysHandle = usbdk_helper.GetRedirectorSystemHandle(priv->redirector_handle); + + wfd = usbi_create_fd(sysHandle, IS_XFERIN(transfer) ? RW_READ : RW_WRITE, NULL, NULL); + // Always use the handle returned from usbi_create_fd (wfd.handle) + if (wfd.fd < 0) { + return LIBUSB_ERROR_NO_MEM; + } + + if (IS_XFERIN(transfer)) { + transferRes = usbdk_helper.ReadPipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped); + } + else { + transferRes = usbdk_helper.WritePipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped); + } + + switch (transferRes) + { + case TransferSuccess: + wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY; + break; + case TransferSuccessAsync: + break; + case TransferFailure: + { + usbi_err(ctx, "ReadPipe/WritePipe failed: %s", windows_error_str(0)); + usbi_free_fd(&wfd); + return LIBUSB_ERROR_IO; + } + } + + transfer_priv->pollable_fd = wfd; + usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, (short)(IS_XFERIN(transfer) ? POLLIN : POLLOUT)); + return LIBUSB_SUCCESS; +} + +static int usbdk_do_iso_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct usbdk_device_priv *priv = _usbdk_device_priv(transfer->dev_handle->dev); + struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer); + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + struct winfd wfd; + TransferResult transferRes; + int i; + HANDLE sysHandle; + + transfer_priv->request.Buffer = (PVOID64)(uintptr_t)transfer->buffer; + transfer_priv->request.BufferLength = transfer->length; + transfer_priv->request.EndpointAddress = transfer->endpoint; + transfer_priv->request.TransferType = IsochronousTransferType; + transfer_priv->request.IsochronousPacketsArraySize = transfer->num_iso_packets; + transfer_priv->IsochronousPacketsArray = malloc(transfer->num_iso_packets * sizeof(ULONG64)); + transfer_priv->request.IsochronousPacketsArray = (PVOID64)(uintptr_t)transfer_priv->IsochronousPacketsArray; + if (!transfer_priv->IsochronousPacketsArray){ + usbi_err(ctx, "Allocation of IsochronousPacketsArray is failed, %s", windows_error_str(0)); + return LIBUSB_ERROR_IO; + } + + transfer_priv->IsochronousResultsArray = malloc(transfer->num_iso_packets * sizeof(USB_DK_ISO_TRANSFER_RESULT)); + transfer_priv->request.Result.IsochronousResultsArray = (PVOID64)(uintptr_t)transfer_priv->IsochronousResultsArray; + if (!transfer_priv->IsochronousResultsArray){ + usbi_err(ctx, "Allocation of isochronousResultsArray is failed, %s", windows_error_str(0)); + free(transfer_priv->IsochronousPacketsArray); + return LIBUSB_ERROR_IO; + } + + for (i = 0; i < transfer->num_iso_packets; i++){ + transfer_priv->IsochronousPacketsArray[i] = transfer->iso_packet_desc[i].length; + } + + transfer_priv->pollable_fd = INVALID_WINFD; + + sysHandle = usbdk_helper.GetRedirectorSystemHandle(priv->redirector_handle); + + wfd = usbi_create_fd(sysHandle, IS_XFERIN(transfer) ? RW_READ : RW_WRITE, NULL, NULL); + // Always use the handle returned from usbi_create_fd (wfd.handle) + if (wfd.fd < 0) { + free(transfer_priv->IsochronousPacketsArray); + free(transfer_priv->IsochronousResultsArray); + return LIBUSB_ERROR_NO_MEM; + } + + if (IS_XFERIN(transfer)) { + transferRes = usbdk_helper.ReadPipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped); + } + else { + transferRes = usbdk_helper.WritePipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped); + } + + switch (transferRes){ + case TransferSuccess: + wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY; + break; + case TransferSuccessAsync: + break; + case TransferFailure: + { + usbi_err(ctx, "ReadPipe/WritePipe failed: %s", windows_error_str(0)); + usbi_free_fd(&wfd); + free(transfer_priv->IsochronousPacketsArray); + free(transfer_priv->IsochronousResultsArray); + return LIBUSB_ERROR_IO; + } + } + + transfer_priv->pollable_fd = wfd; + usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, (short)(IS_XFERIN(transfer) ? POLLIN : POLLOUT)); + + return LIBUSB_SUCCESS; +} + +static int usbdk_submit_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + return usbdk_do_control_transfer(itransfer); + + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + if (IS_XFEROUT(transfer) && transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET) { + //TODO: Check whether we can support this in UsbDk + return LIBUSB_ERROR_NOT_SUPPORTED; + } else { + return usbdk_do_bulk_transfer(itransfer); + } + + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + return usbdk_do_iso_transfer(itransfer); + default: + usbi_err(TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type); + return LIBUSB_ERROR_INVALID_PARAM; + } +} + +static int usbdk_abort_transfers(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 usbdk_device_priv *priv = _usbdk_device_priv(transfer->dev_handle->dev); + + if (!usbdk_helper.AbortPipe(priv->redirector_handle, transfer->endpoint)) { + usbi_err(ctx, "AbortPipe failed: %s", windows_error_str(0)); + return LIBUSB_ERROR_NO_DEVICE; + } + + return LIBUSB_SUCCESS; +} + +static int usbdk_cancel_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + // Control transfers cancelled by IoCancelXXX() API + // No special treatment needed + return LIBUSB_SUCCESS; + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + return usbdk_abort_transfers(itransfer); + default: + usbi_err(ITRANSFER_CTX(itransfer), "unknown endpoint type %d", transfer->type); + return LIBUSB_ERROR_INVALID_PARAM; + } +} + +int windows_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size) +{ + itransfer->transferred += io_size; + return LIBUSB_TRANSFER_COMPLETED; +} + +struct winfd *windows_get_fd(struct usbi_transfer *transfer) +{ + struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(transfer); + return &transfer_priv->pollable_fd; +} + +static DWORD usbdk_translate_usbd_status(USBD_STATUS UsbdStatus) +{ + if (USBD_SUCCESS(UsbdStatus)) + { + return NO_ERROR; + } + + switch (UsbdStatus) + { + case USBD_STATUS_STALL_PID: + case USBD_STATUS_ENDPOINT_HALTED: + case USBD_STATUS_BAD_START_FRAME: + return ERROR_GEN_FAILURE; + case USBD_STATUS_TIMEOUT: + return ERROR_SEM_TIMEOUT; + case USBD_STATUS_CANCELED: + return ERROR_OPERATION_ABORTED; + default: + return ERROR_FUNCTION_FAILED; + } +} + +void windows_get_overlapped_result(struct usbi_transfer *transfer, struct winfd *pollable_fd, DWORD *io_result, DWORD *io_size) +{ + if (HasOverlappedIoCompletedSync(pollable_fd->overlapped) || // Handle async requests that completed synchronously first + GetOverlappedResult(pollable_fd->handle, pollable_fd->overlapped, io_size, false)) { // Regular async overlapped + struct libusb_transfer *ltransfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer); + struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(transfer); + + if (ltransfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS){ + int i; + for (i = 0; i < transfer_priv->request.IsochronousPacketsArraySize; i++) { + struct libusb_iso_packet_descriptor *lib_desc = <ransfer->iso_packet_desc[i]; + + switch (transfer_priv->IsochronousResultsArray[i].TransferResult){ + case STATUS_SUCCESS: + case STATUS_CANCELLED: + case STATUS_REQUEST_CANCELED: + lib_desc->status = LIBUSB_TRANSFER_COMPLETED; // == ERROR_SUCCESS + break; + default: + lib_desc->status = LIBUSB_TRANSFER_ERROR; // ERROR_UNKNOWN_EXCEPTION; + break; + } + + lib_desc->actual_length = (unsigned int) transfer_priv->IsochronousResultsArray[i].ActualLength; + } + } + + *io_size = (DWORD) transfer_priv->request.Result.GenResult.BytesTransferred; + *io_result = usbdk_translate_usbd_status((USBD_STATUS) transfer_priv->request.Result.GenResult.UsbdStatus); + } + else { + *io_result = GetLastError(); + } +} + +static int usbdk_clock_gettime(int clk_id, struct timespec *tp) +{ + return windows_clock_gettime(clk_id, tp); +} + +const struct usbi_os_backend usbdk_backend = { + "Windows", + USBI_CAP_HAS_HID_ACCESS, + usbdk_init, + usbdk_exit, + + usbdk_get_device_list, + NULL, + usbdk_open, + usbdk_close, + + usbdk_get_device_descriptor, + usbdk_get_active_config_descriptor, + usbdk_get_config_descriptor, + NULL, + + usbdk_get_configuration, + usbdk_set_configuration, + usbdk_claim_interface, + usbdk_release_interface, + + usbdk_set_interface_altsetting, + usbdk_clear_halt, + usbdk_reset_device, + + NULL, + NULL, + + usbdk_kernel_driver_active, + usbdk_detach_kernel_driver, + usbdk_attach_kernel_driver, + + usbdk_destroy_device, + + usbdk_submit_transfer, + usbdk_cancel_transfer, + windows_clear_transfer_priv, + + windows_handle_events, + NULL, + + usbdk_clock_gettime, +#if defined(USBI_TIMERFD_AVAILABLE) + NULL, +#endif + sizeof(struct usbdk_device_priv), + sizeof(struct usbdk_device_handle_priv), + sizeof(struct usbdk_transfer_priv), +}; + +#endif /* USE_USBDK */ diff --git a/libusb/os/windows_usbdk.h b/libusb/os/windows_usbdk.h new file mode 100644 index 0000000..6b71735 --- /dev/null +++ b/libusb/os/windows_usbdk.h @@ -0,0 +1,126 @@ +/* +* windows UsbDk backend for libusb 1.0 +* Copyright © 2014 Red Hat, Inc. + +* Authors: +* Dmitry Fleytman <dmitry@daynix.com> +* Pavel Gurvich <pavel@daynix.com> +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#pragma once + +typedef struct tag_USB_DK_DEVICE_ID +{ + WCHAR DeviceID[MAX_DEVICE_ID_LEN]; + WCHAR InstanceID[MAX_DEVICE_ID_LEN]; +} USB_DK_DEVICE_ID, *PUSB_DK_DEVICE_ID; + +static inline +void UsbDkFillIDStruct(USB_DK_DEVICE_ID *ID, PCWCHAR DeviceID, PCWCHAR InstanceID) +{ + wcsncpy_s(ID->DeviceID, DeviceID, MAX_DEVICE_ID_LEN); + wcsncpy_s(ID->InstanceID, InstanceID, MAX_DEVICE_ID_LEN); +} + +typedef struct tag_USB_DK_DEVICE_INFO +{ + USB_DK_DEVICE_ID ID; + ULONG64 FilterID; + ULONG64 Port; + ULONG64 Speed; + USB_DEVICE_DESCRIPTOR DeviceDescriptor; +} USB_DK_DEVICE_INFO, *PUSB_DK_DEVICE_INFO; + +typedef struct tag_USB_DK_CONFIG_DESCRIPTOR_REQUEST +{ + USB_DK_DEVICE_ID ID; + ULONG64 Index; +} USB_DK_CONFIG_DESCRIPTOR_REQUEST, *PUSB_DK_CONFIG_DESCRIPTOR_REQUEST; + +typedef struct tag_USB_DK_ISO_TRANSFER_RESULT +{ + ULONG64 ActualLength; + ULONG64 TransferResult; +} USB_DK_ISO_TRANSFER_RESULT, *PUSB_DK_ISO_TRANSFER_RESULT; + +typedef struct tag_USB_DK_GEN_TRANSFER_RESULT +{ + ULONG64 BytesTransferred; + ULONG64 UsbdStatus; // USBD_STATUS code +} USB_DK_GEN_TRANSFER_RESULT, *PUSB_DK_GEN_TRANSFER_RESULT; + +typedef struct tag_USB_DK_TRANSFER_RESULT +{ + USB_DK_GEN_TRANSFER_RESULT GenResult; + PVOID64 IsochronousResultsArray; // array of USB_DK_ISO_TRANSFER_RESULT +} USB_DK_TRANSFER_RESULT, *PUSB_DK_TRANSFER_RESULT; + +typedef struct tag_USB_DK_TRANSFER_REQUEST +{ + ULONG64 EndpointAddress; + PVOID64 Buffer; + ULONG64 BufferLength; + ULONG64 TransferType; + ULONG64 IsochronousPacketsArraySize; + PVOID64 IsochronousPacketsArray; + + USB_DK_TRANSFER_RESULT Result; +} USB_DK_TRANSFER_REQUEST, *PUSB_DK_TRANSFER_REQUEST; + +typedef enum +{ + TransferFailure = 0, + TransferSuccess, + TransferSuccessAsync +} TransferResult; + +typedef enum +{ + NoSpeed = 0, + LowSpeed, + FullSpeed, + HighSpeed, + SuperSpeed +} USB_DK_DEVICE_SPEED; + +typedef enum +{ + ControlTransferType, + BulkTransferType, + IntertuptTransferType, + IsochronousTransferType +} USB_DK_TRANSFER_TYPE; + +typedef BOOL (__cdecl *USBDK_GET_DEVICES_LIST) (PUSB_DK_DEVICE_INFO *, PULONG); +typedef void (__cdecl *USBDK_RELEASE_DEVICES_LIST) (PUSB_DK_DEVICE_INFO); + +typedef HANDLE (__cdecl *USBDK_START_REDIRECT) (PUSB_DK_DEVICE_ID); +typedef BOOL (__cdecl *USBDK_STOP_REDIRECT) (HANDLE); + +typedef BOOL (__cdecl *USBDK_GET_CONFIGURATION_DESCRIPTOR) (PUSB_DK_CONFIG_DESCRIPTOR_REQUEST Request, + PUSB_CONFIGURATION_DESCRIPTOR *Descriptor, + PULONG Length); +typedef void (__cdecl *USBDK_RELEASE_CONFIGURATION_DESCRIPTOR) (PUSB_CONFIGURATION_DESCRIPTOR Descriptor); + +typedef TransferResult (__cdecl *USBDK_WRITE_PIPE) (HANDLE DeviceHandle, PUSB_DK_TRANSFER_REQUEST Request, LPOVERLAPPED lpOverlapped); +typedef TransferResult (__cdecl *USBDK_READ_PIPE) (HANDLE DeviceHandle, PUSB_DK_TRANSFER_REQUEST Request, LPOVERLAPPED lpOverlapped); +typedef BOOL (__cdecl *USBDK_ABORT_PIPE) (HANDLE DeviceHandle, ULONG64 PipeAddress); +typedef BOOL (__cdecl *USBDK_RESET_PIPE) (HANDLE DeviceHandle, ULONG64 PipeAddress); +typedef BOOL (__cdecl *USBDK_SET_ALTSETTING) (HANDLE DeviceHandle, ULONG64 InterfaceIdx, ULONG64 AltSettingIdx); +typedef BOOL (__cdecl *USBDK_RESET_DEVICE) (HANDLE DeviceHandle); + +typedef HANDLE (__cdecl *USBDK_GET_REDIRECTOR_SYSTEM_HANDLE) (HANDLE DeviceHandle); diff --git a/libusb/os/windows_winusb.c b/libusb/os/windows_winusb.c index 34ce461..ef40096 100644 --- a/libusb/os/windows_winusb.c +++ b/libusb/os/windows_winusb.c @@ -24,6 +24,8 @@ #include <config.h> +#if !defined(USE_USBDK) + #include <windows.h> #include <setupapi.h> #include <ctype.h> @@ -4138,3 +4140,5 @@ static int composite_copy_transfer_data(int sub_api, struct usbi_transfer *itran return priv->usb_interface[transfer_priv->interface_number].apib-> copy_transfer_data(priv->usb_interface[transfer_priv->interface_number].sub_api, itransfer, io_size); } + +#endif /* !USE_USBDK */ diff --git a/libusb/version_nano.h b/libusb/version_nano.h index 36ec18e..dc96b18 100644 --- a/libusb/version_nano.h +++ b/libusb/version_nano.h @@ -1 +1 @@ -#define LIBUSB_NANO 11029 +#define LIBUSB_NANO 11030 |