blob: 9c3e8fd8e0b7c15f2735c51f5cb49441a1029c39 [file] [log] [blame]
/*
* 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>
#include <windows.h>
#include <stdio.h>
#include "libusbi.h"
#include "windows_common.h"
#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
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)
usbi_err(ctx, "UsbDkHelper API %s not found: %s", api_name, windows_error_str(0));
return api_ptr;
}
static void unload_usbdk_helper_dll(void)
{
if (usbdk_helper.module != NULL) {
FreeLibrary(usbdk_helper.module);
usbdk_helper.module = NULL;
}
}
static int load_usbdk_helper_dll(struct libusb_context *ctx)
{
usbdk_helper.module = LoadLibraryA("UsbDkHelper");
if (usbdk_helper.module == NULL) {
usbi_err(ctx, "Failed to load UsbDkHelper.dll: %s", windows_error_str(0));
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);
usbdk_helper.module = NULL;
return LIBUSB_ERROR_NOT_FOUND;
}
typedef SC_HANDLE (WINAPI *POPENSCMANAGERA)(LPCSTR, LPCSTR, DWORD);
typedef SC_HANDLE (WINAPI *POPENSERVICEA)(SC_HANDLE, LPCSTR, DWORD);
typedef BOOL (WINAPI *PCLOSESERVICEHANDLE)(SC_HANDLE);
static int usbdk_init(struct libusb_context *ctx)
{
POPENSCMANAGERA pOpenSCManagerA;
POPENSERVICEA pOpenServiceA;
PCLOSESERVICEHANDLE pCloseServiceHandle;
SC_HANDLE managerHandle;
SC_HANDLE serviceHandle;
HMODULE h;
h = LoadLibraryA("Advapi32");
if (h == NULL) {
usbi_warn(ctx, "failed to open Advapi32\n");
return LIBUSB_ERROR_OTHER;
}
pOpenSCManagerA = (POPENSCMANAGERA)GetProcAddress(h, "OpenSCManagerA");
if (pOpenSCManagerA == NULL) {
usbi_warn(ctx, "failed to find %s in Advapi32\n", "OpenSCManagerA");
goto error_free_library;
}
pOpenServiceA = (POPENSERVICEA)GetProcAddress(h, "OpenServiceA");
if (pOpenServiceA == NULL) {
usbi_warn(ctx, "failed to find %s in Advapi32\n", "OpenServiceA");
goto error_free_library;
}
pCloseServiceHandle = (PCLOSESERVICEHANDLE)GetProcAddress(h, "CloseServiceHandle");
if (pCloseServiceHandle == NULL) {
usbi_warn(ctx, "failed to find %s in Advapi32\n", "CloseServiceHandle");
goto error_free_library;
}
managerHandle = pOpenSCManagerA(NULL, NULL, SC_MANAGER_CONNECT);
if (managerHandle == NULL) {
usbi_warn(ctx, "failed to open service control manager: %s", windows_error_str(0));
goto error_free_library;
}
serviceHandle = pOpenServiceA(managerHandle, "UsbDk", GENERIC_READ);
pCloseServiceHandle(managerHandle);
if (serviceHandle == NULL) {
if (GetLastError() != ERROR_SERVICE_DOES_NOT_EXIST)
usbi_warn(ctx, "failed to open UsbDk service: %s", windows_error_str(0));
FreeLibrary(h);
return LIBUSB_ERROR_NOT_FOUND;
}
pCloseServiceHandle(serviceHandle);
FreeLibrary(h);
return load_usbdk_helper_dll(ctx);
error_free_library:
FreeLibrary(h);
return LIBUSB_ERROR_OTHER;
}
static void usbdk_exit(struct libusb_context *ctx)
{
UNUSED(ctx);
unload_usbdk_helper_dll();
}
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) + 1];
if (snprintf(dev_identity, sizeof(dev_identity), "%S%S", id->DeviceID, id->InstanceID) == -1) {
usbi_warn(ctx, "cannot form device identity");
return LIBUSB_ERROR_NOT_SUPPORTED;
}
*session_id = htab_hash(dev_identity);
return LIBUSB_SUCCESS;
}
static void usbdk_release_config_descriptors(struct usbdk_device_priv *priv, uint8_t count)
{
uint8_t i;
for (i = 0; i < count; i++)
usbdk_helper.ReleaseConfigurationDescriptor(priv->config_descriptors[i]);
free(priv->config_descriptors);
priv->config_descriptors = NULL;
}
static int usbdk_cache_config_descriptors(struct libusb_context *ctx,
struct usbdk_device_priv *priv, PUSB_DK_DEVICE_INFO info)
{
uint8_t i;
USB_DK_CONFIG_DESCRIPTOR_REQUEST Request;
Request.ID = info->ID;
priv->config_descriptors = calloc(info->DeviceDescriptor.bNumConfigurations, sizeof(PUSB_CONFIGURATION_DESCRIPTOR));
if (priv->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, &priv->config_descriptors[i], &Length)) {
usbi_err(ctx, "failed to retrieve configuration descriptors");
usbdk_release_config_descriptors(priv, i);
return LIBUSB_ERROR_OTHER;
}
}
return LIBUSB_SUCCESS;
}
static inline int usbdk_device_priv_init(struct libusb_context *ctx, struct libusb_device *dev, PUSB_DK_DEVICE_INFO info)
{
struct usbdk_device_priv *priv = usbi_get_device_priv(dev);
priv->info = *info;
priv->active_configuration = 0;
return usbdk_cache_config_descriptors(ctx, priv, 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;
memcpy(&dev->device_descriptor, &info->DeviceDescriptor, LIBUSB_DT_DEVICE_SIZE);
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 int usbdk_get_device_descriptor(struct libusb_device *dev, unsigned char *buffer)
{
struct usbdk_device_priv *priv = usbi_get_device_priv(dev);
memcpy(buffer, &priv->info.DeviceDescriptor, DEVICE_DESC_LENGTH);
return LIBUSB_SUCCESS;
}
static int usbdk_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, unsigned char *buffer, size_t len)
{
struct usbdk_device_priv *priv = usbi_get_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);
return (int)size;
}
static int usbdk_get_config_descriptor_by_value(struct libusb_device *dev, uint8_t bConfigurationValue,
unsigned char **buffer)
{
struct usbdk_device_priv *priv = usbi_get_device_priv(dev);
PUSB_CONFIGURATION_DESCRIPTOR config_header;
uint8_t index;
for (index = 0; index < dev->num_configurations; index++) {
config_header = priv->config_descriptors[index];
if (config_header->bConfigurationValue == bConfigurationValue) {
*buffer = (unsigned char *)priv->config_descriptors[index];
return (int)config_header->wTotalLength;
}
}
return LIBUSB_ERROR_NOT_FOUND;
}
static int usbdk_get_active_config_descriptor(struct libusb_device *dev, unsigned char *buffer, size_t len)
{
struct usbdk_device_priv *priv = usbi_get_device_priv(dev);
return usbdk_get_config_descriptor(dev, priv->active_configuration, buffer, len);
}
static int usbdk_open(struct libusb_device_handle *dev_handle)
{
struct usbdk_device_priv *priv = usbi_get_device_priv(dev_handle->dev);
priv->redirector_handle = usbdk_helper.StartRedirect(&priv->info.ID);
if (priv->redirector_handle == INVALID_HANDLE_VALUE) {
usbi_err(HANDLE_CTX(dev_handle), "Redirector startup failed");
return LIBUSB_ERROR_OTHER;
}
priv->system_handle = usbdk_helper.GetRedirectorSystemHandle(priv->redirector_handle);
return LIBUSB_SUCCESS;
}
static void usbdk_close(struct libusb_device_handle *dev_handle)
{
struct usbdk_device_priv *priv = usbi_get_device_priv(dev_handle->dev);
if (!usbdk_helper.StopRedirect(priv->redirector_handle))
usbi_err(HANDLE_CTX(dev_handle), "Redirector shutdown failed");
}
static int usbdk_get_configuration(struct libusb_device_handle *dev_handle, int *config)
{
struct usbdk_device_priv *priv = usbi_get_device_priv(dev_handle->dev);
*config = priv->active_configuration;
return LIBUSB_SUCCESS;
}
static int usbdk_set_configuration(struct libusb_device_handle *dev_handle, int config)
{
UNUSED(dev_handle);
UNUSED(config);
return LIBUSB_SUCCESS;
}
static int usbdk_claim_interface(struct libusb_device_handle *dev_handle, int iface)
{
UNUSED(dev_handle);
UNUSED(iface);
return LIBUSB_SUCCESS;
}
static int usbdk_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting)
{
struct usbdk_device_priv *priv = usbi_get_device_priv(dev_handle->dev);
if (!usbdk_helper.SetAltsetting(priv->redirector_handle, iface, altsetting)) {
usbi_err(HANDLE_CTX(dev_handle), "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)
{
UNUSED(dev_handle);
UNUSED(iface);
return LIBUSB_SUCCESS;
}
static int usbdk_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint)
{
struct usbdk_device_priv *priv = usbi_get_device_priv(dev_handle->dev);
if (!usbdk_helper.ResetPipe(priv->redirector_handle, endpoint)) {
usbi_err(HANDLE_CTX(dev_handle), "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 usbdk_device_priv *priv = usbi_get_device_priv(dev_handle->dev);
if (!usbdk_helper.ResetDevice(priv->redirector_handle)) {
usbi_err(HANDLE_CTX(dev_handle), "ResetDevice failed: %s", windows_error_str(0));
return LIBUSB_ERROR_NO_DEVICE;
}
return LIBUSB_SUCCESS;
}
static void usbdk_destroy_device(struct libusb_device *dev)
{
struct usbdk_device_priv *priv = usbi_get_device_priv(dev);
if (priv->config_descriptors != NULL)
usbdk_release_config_descriptors(priv, priv->info.DeviceDescriptor.bNumConfigurations);
}
static void usbdk_clear_transfer_priv(struct usbi_transfer *itransfer)
{
struct usbdk_transfer_priv *transfer_priv = get_usbdk_transfer_priv(itransfer);
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
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)
{
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
struct usbdk_device_priv *priv = usbi_get_device_priv(transfer->dev_handle->dev);
struct usbdk_transfer_priv *transfer_priv = get_usbdk_transfer_priv(itransfer);
OVERLAPPED *overlapped = get_transfer_priv_overlapped(itransfer);
TransferResult transResult;
transfer_priv->request.Buffer = (PVOID64)transfer->buffer;
transfer_priv->request.BufferLength = transfer->length;
transfer_priv->request.TransferType = ControlTransferType;
if (transfer->buffer[0] & LIBUSB_ENDPOINT_IN)
transResult = usbdk_helper.ReadPipe(priv->redirector_handle, &transfer_priv->request, overlapped);
else
transResult = usbdk_helper.WritePipe(priv->redirector_handle, &transfer_priv->request, overlapped);
switch (transResult) {
case TransferSuccess:
windows_force_sync_completion(overlapped, (ULONG)transfer_priv->request.Result.GenResult.BytesTransferred);
break;
case TransferSuccessAsync:
break;
case TransferFailure:
usbi_err(TRANSFER_CTX(transfer), "ControlTransfer failed: %s", windows_error_str(0));
return LIBUSB_ERROR_IO;
}
set_transfer_priv_handle(itransfer, priv->system_handle);
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 = usbi_get_device_priv(transfer->dev_handle->dev);
struct usbdk_transfer_priv *transfer_priv = get_usbdk_transfer_priv(itransfer);
OVERLAPPED *overlapped = get_transfer_priv_overlapped(itransfer);
TransferResult transferRes;
transfer_priv->request.Buffer = (PVOID64)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 = InterruptTransferType;
break;
}
if (IS_XFERIN(transfer))
transferRes = usbdk_helper.ReadPipe(priv->redirector_handle, &transfer_priv->request, overlapped);
else
transferRes = usbdk_helper.WritePipe(priv->redirector_handle, &transfer_priv->request, overlapped);
switch (transferRes) {
case TransferSuccess:
windows_force_sync_completion(overlapped, (ULONG)transfer_priv->request.Result.GenResult.BytesTransferred);
break;
case TransferSuccessAsync:
break;
case TransferFailure:
usbi_err(TRANSFER_CTX(transfer), "ReadPipe/WritePipe failed: %s", windows_error_str(0));
return LIBUSB_ERROR_IO;
}
set_transfer_priv_handle(itransfer, priv->system_handle);
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 = usbi_get_device_priv(transfer->dev_handle->dev);
struct usbdk_transfer_priv *transfer_priv = get_usbdk_transfer_priv(itransfer);
OVERLAPPED *overlapped = get_transfer_priv_overlapped(itransfer);
TransferResult transferRes;
int i;
transfer_priv->request.Buffer = (PVOID64)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)transfer_priv->IsochronousPacketsArray;
if (!transfer_priv->IsochronousPacketsArray) {
usbi_err(TRANSFER_CTX(transfer), "Allocation of IsochronousPacketsArray failed");
return LIBUSB_ERROR_NO_MEM;
}
transfer_priv->IsochronousResultsArray = malloc(transfer->num_iso_packets * sizeof(USB_DK_ISO_TRANSFER_RESULT));
transfer_priv->request.Result.IsochronousResultsArray = (PVOID64)transfer_priv->IsochronousResultsArray;
if (!transfer_priv->IsochronousResultsArray) {
usbi_err(TRANSFER_CTX(transfer), "Allocation of isochronousResultsArray failed");
return LIBUSB_ERROR_NO_MEM;
}
for (i = 0; i < transfer->num_iso_packets; i++)
transfer_priv->IsochronousPacketsArray[i] = transfer->iso_packet_desc[i].length;
if (IS_XFERIN(transfer))
transferRes = usbdk_helper.ReadPipe(priv->redirector_handle, &transfer_priv->request, overlapped);
else
transferRes = usbdk_helper.WritePipe(priv->redirector_handle, &transfer_priv->request, overlapped);
switch (transferRes) {
case TransferSuccess:
windows_force_sync_completion(overlapped, (ULONG)transfer_priv->request.Result.GenResult.BytesTransferred);
break;
case TransferSuccessAsync:
break;
case TransferFailure:
return LIBUSB_ERROR_IO;
}
set_transfer_priv_handle(itransfer, priv->system_handle);
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))
return LIBUSB_ERROR_NOT_SUPPORTED; //TODO: Check whether we can support this in UsbDk
return usbdk_do_bulk_transfer(itransfer);
case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
return usbdk_do_iso_transfer(itransfer);
default:
// Should not get here since windows_submit_transfer() validates
// the transfer->type field
usbi_err(TRANSFER_CTX(transfer), "unsupported endpoint type %d", transfer->type);
return LIBUSB_ERROR_NOT_SUPPORTED;
}
}
static enum libusb_transfer_status usbdk_copy_transfer_data(struct usbi_transfer *itransfer, DWORD length)
{
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
struct usbdk_transfer_priv *transfer_priv = get_usbdk_transfer_priv(itransfer);
UNUSED(length);
if (transfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) {
ULONG64 i;
for (i = 0; i < transfer_priv->request.IsochronousPacketsArraySize; i++) {
struct libusb_iso_packet_descriptor *lib_desc = &transfer->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;
}
}
itransfer->transferred += (int)transfer_priv->request.Result.GenResult.BytesTransferred;
return usbd_status_to_libusb_transfer_status((USBD_STATUS)transfer_priv->request.Result.GenResult.UsbdStatus);
}
const struct windows_backend usbdk_backend = {
usbdk_init,
usbdk_exit,
usbdk_get_device_list,
usbdk_open,
usbdk_close,
usbdk_get_device_descriptor,
usbdk_get_active_config_descriptor,
usbdk_get_config_descriptor,
usbdk_get_config_descriptor_by_value,
usbdk_get_configuration,
usbdk_set_configuration,
usbdk_claim_interface,
usbdk_release_interface,
usbdk_set_interface_altsetting,
usbdk_clear_halt,
usbdk_reset_device,
usbdk_destroy_device,
usbdk_submit_transfer,
NULL, /* cancel_transfer */
usbdk_clear_transfer_priv,
usbdk_copy_transfer_data,
};