Skip to content

Commit

Permalink
Windows: Added isochronous support via libusbk
Browse files Browse the repository at this point in the history
[dickens] Rebased against latest changes and fixed some styling issues

Closes #246

Signed-off-by: Chris Dickens <[email protected]>
  • Loading branch information
JoshBlake authored and dickens committed Jan 9, 2018
1 parent ca0b6c0 commit 55ced77
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 2 deletions.
1 change: 1 addition & 0 deletions libusb/os/windows_nt_shared_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,4 +134,5 @@ struct winusb_transfer_priv {
uint8_t *hid_buffer; // 1 byte extended data buffer, required for HID
uint8_t *hid_dest; // transfer buffer destination, required for HID
size_t hid_expected_size;
void *iso_context;
};
103 changes: 102 additions & 1 deletion libusb/os/windows_winusb.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ static int winusbx_claim_interface(int sub_api, struct libusb_device_handle *dev
static int winusbx_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface);
static int winusbx_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer);
static int winusbx_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting);
static int winusbx_submit_iso_transfer(int sub_api, struct usbi_transfer *itransfer);
static int winusbx_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer);
static int winusbx_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint);
static int winusbx_abort_transfers(int sub_api, struct usbi_transfer *itransfer);
Expand Down Expand Up @@ -1734,6 +1735,8 @@ static void winusb_clear_transfer_priv(struct usbi_transfer *itransfer)
transfer_priv->pollable_fd = INVALID_WINFD;
transfer_priv->handle = NULL;
safe_free(transfer_priv->hid_buffer);
safe_free(transfer_priv->iso_context);

// When auto claim is in use, attempt to release the auto-claimed interface
auto_release(itransfer);
}
Expand Down Expand Up @@ -1966,7 +1969,7 @@ const struct windows_usb_api_backend usb_api_backend[USB_API_MAX] = {
winusbx_clear_halt,
winusbx_reset_device,
winusbx_submit_bulk_transfer,
NULL, /* submit_iso_transfer */
winusbx_submit_iso_transfer,
winusbx_submit_control_transfer,
winusbx_abort_control,
winusbx_abort_transfers,
Expand Down Expand Up @@ -2059,6 +2062,8 @@ static int winusbx_init(struct libusb_context *ctx)
WinUSBX_Set(SetCurrentAlternateSetting);
WinUSBX_Set(SetPipePolicy);
WinUSBX_Set(WritePipe);
WinUSBX_Set(IsoReadPipe);
WinUSBX_Set(IsoWritePipe);

if (WinUSBX[i].Initialize != NULL) {
WinUSBX[i].initialized = true;
Expand Down Expand Up @@ -2473,6 +2478,78 @@ static int winusbx_set_interface_altsetting(int sub_api, struct libusb_device_ha
return LIBUSB_SUCCESS;
}

static int winusbx_submit_iso_transfer(int sub_api, 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 winusb_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
struct winusb_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle);
struct winusb_device_priv *priv = _device_priv(transfer->dev_handle->dev);
HANDLE winusb_handle;
OVERLAPPED *overlapped;
bool ret;
int current_interface;
int i;
UINT offset;
PKISO_CONTEXT iso_context;
size_t iso_ctx_size;

CHECK_WINUSBX_AVAILABLE(sub_api);

if ((sub_api != SUB_API_LIBUSBK) && (sub_api != SUB_API_LIBUSB0)) {
// iso only supported on libusbk-based backends
PRINT_UNSUPPORTED_API(submit_iso_transfer);
};

current_interface = 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;
}

usbi_dbg("matched endpoint %02X with interface %d", transfer->endpoint, current_interface);

transfer_priv->handle = winusb_handle = handle_priv->interface_handle[current_interface].api_handle;
overlapped = transfer_priv->pollable_fd.overlapped;

iso_ctx_size = sizeof(KISO_CONTEXT) + (transfer->num_iso_packets * sizeof(KISO_PACKET));
transfer_priv->iso_context = iso_context = calloc(1, iso_ctx_size);
if (transfer_priv->iso_context == NULL)
return LIBUSB_ERROR_NO_MEM;

// start ASAP
iso_context->StartFrame = 0;
iso_context->NumberOfPackets = (SHORT)transfer->num_iso_packets;

// convert the transfer packet lengths to iso_packet offsets
offset = 0;
for (i = 0; i < transfer->num_iso_packets; i++) {
iso_context->IsoPackets[i].offset = offset;
offset += transfer->iso_packet_desc[i].length;
}

if (IS_XFERIN(transfer)) {
usbi_dbg("reading %d iso packets", transfer->num_iso_packets);
ret = WinUSBX[sub_api].IsoReadPipe(winusb_handle, transfer->endpoint, transfer->buffer, transfer->length, overlapped, iso_context);
} else {
usbi_dbg("writing %d iso packets", transfer->num_iso_packets);
ret = WinUSBX[sub_api].IsoWritePipe(winusb_handle, transfer->endpoint, transfer->buffer, transfer->length, overlapped, iso_context);
}

if (!ret) {
if (GetLastError() != ERROR_IO_PENDING) {
usbi_err(ctx, "IsoReadPipe/IsoWritePipe failed: %s", windows_error_str(0));
return LIBUSB_ERROR_IO;
}
} else {
windows_force_sync_completion(overlapped, (ULONG)transfer->length);
}

transfer_priv->interface_number = (uint8_t)current_interface;

return LIBUSB_SUCCESS;
}

static int winusbx_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer)
{
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
Expand Down Expand Up @@ -2654,6 +2731,30 @@ static int winusbx_reset_device(int sub_api, struct libusb_device_handle *dev_ha

static int winusbx_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size)
{
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
struct winusb_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
struct winusb_device_priv *priv = _device_priv(transfer->dev_handle->dev);
PKISO_CONTEXT iso_context;
int i;

if (transfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) {
CHECK_WINUSBX_AVAILABLE(sub_api);

// for isochronous, need to copy the individual iso packet actual_lengths and statuses
if ((sub_api == SUB_API_LIBUSBK) || (sub_api == SUB_API_LIBUSB0)) {
// iso only supported on libusbk-based backends for now
iso_context = transfer_priv->iso_context;
for (i = 0; i < transfer->num_iso_packets; i++) {
transfer->iso_packet_desc[i].actual_length = iso_context->IsoPackets[i].actual_length;
// TODO translate USDB_STATUS codes https://msdn.microsoft.com/en-us/library/ff539136(VS.85).aspx to libusb_transfer_status
//transfer->iso_packet_desc[i].status = transfer_priv->iso_context->IsoPackets[i].status;
}
} else {
// This should only occur if backend is not set correctly or other backend isoc is partially implemented
PRINT_UNSUPPORTED_API(copy_transfer_data);
}
}

itransfer->transferred += io_size;
return LIBUSB_TRANSFER_COMPLETED;
}
Expand Down
42 changes: 42 additions & 0 deletions libusb/os/windows_winusb.h
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,46 @@ typedef VOID (WINAPI *LibK_GetVersion_t)(
PKLIB_VERSION Version
);

//KISO_PACKET is equivalent of libusb_iso_packet_descriptor except uses absolute "offset" field instead of sequential Lengths
typedef struct _KISO_PACKET {
UINT offset;
USHORT actual_length; //changed from libusbk_shared.h "Length" for clarity
USHORT status;
} KISO_PACKET, *PKISO_PACKET;

typedef enum _KISO_FLAG {
KISO_FLAG_NONE = 0,
KISO_FLAG_SET_START_FRAME = 0x00000001,
} KISO_FLAG;

//KISO_CONTEXT is the conceptual equivalent of libusb_transfer except is isochronous-specific and must match libusbk's version
typedef struct _KISO_CONTEXT {
KISO_FLAG Flags;
UINT StartFrame;
SHORT ErrorCount;
SHORT NumberOfPackets;
UINT UrbHdrStatus;
KISO_PACKET IsoPackets[0];
} KISO_CONTEXT, *PKISO_CONTEXT;

typedef BOOL(WINAPI *WinUsb_IsoReadPipe_t)(
WINUSB_INTERFACE_HANDLE InterfaceHandle,
UCHAR PipeID,
PUCHAR Buffer,
ULONG BufferLength,
LPOVERLAPPED Overlapped,
PKISO_CONTEXT IsoContext
);

typedef BOOL(WINAPI *WinUsb_IsoWritePipe_t)(
WINUSB_INTERFACE_HANDLE InterfaceHandle,
UCHAR PipeID,
PUCHAR Buffer,
ULONG BufferLength,
LPOVERLAPPED Overlapped,
PKISO_CONTEXT IsoContext
);

struct winusb_interface {
bool initialized;
WinUsb_AbortPipe_t AbortPipe;
Expand All @@ -568,6 +608,8 @@ struct winusb_interface {
WinUsb_SetCurrentAlternateSetting_t SetCurrentAlternateSetting;
WinUsb_SetPipePolicy_t SetPipePolicy;
WinUsb_WritePipe_t WritePipe;
WinUsb_IsoReadPipe_t IsoReadPipe;
WinUsb_IsoWritePipe_t IsoWritePipe;
};

/* hid.dll interface */
Expand Down
2 changes: 1 addition & 1 deletion libusb/version_nano.h
Original file line number Diff line number Diff line change
@@ -1 +1 @@
#define LIBUSB_NANO 11286
#define LIBUSB_NANO 11287

0 comments on commit 55ced77

Please sign in to comment.