summaryrefslogtreecommitdiff
path: root/libusb/os/windows_usb.c
diff options
context:
space:
mode:
Diffstat (limited to 'libusb/os/windows_usb.c')
-rw-r--r--libusb/os/windows_usb.c199
1 files changed, 135 insertions, 64 deletions
diff --git a/libusb/os/windows_usb.c b/libusb/os/windows_usb.c
index 3723239..610a91d 100644
--- a/libusb/os/windows_usb.c
+++ b/libusb/os/windows_usb.c
@@ -1,7 +1,9 @@
/*
* windows backend for libusb 1.0
- * Copyright (c) 2009 Pete Batard <pbatard@gmail.com>
+ * Copyright (c) 2009-2010 Pete Batard <pbatard@gmail.com>
+ * With contributions from Michael Plante, Orin Eman et al.
* Parts of this code adapted from libusb-win32-v1 by Stephan Meyer
+ * Major code testing contribution by Xiaofan Chen
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -28,12 +30,12 @@
//#define USE_HIDD_FOR_REPORTS
// - Should libusb automatically claim the interfaces it requires?
#define AUTO_CLAIM
+// - Forces instant overlapped completion on timeouts: can prevents extensive
+// wait in poll, after a timeout, but might affect subsequent API calls.
+// ***USE AT YOUR OWN RISKS***
+//#define FORCE_INSTANT_TIMEOUTS
-#if defined(_MSC_VER)
-#include <config_msvc.h>
-#else
#include <config.h>
-#endif
#include <windows.h>
#include <setupapi.h>
#include <ctype.h>
@@ -44,7 +46,7 @@
#include <inttypes.h>
#include <objbase.h> // for string to GUID conv. requires libole32.a
-#include "libusbi.h"
+#include <libusbi.h>
#include "windows_compat.h"
#include "windows_usb.h"
@@ -494,7 +496,7 @@ static int windows_init(struct libusb_context *ctx)
_hcd_cur = &((*_hcd_cur)->next);
}
- // TODO (v2): thread for hotplug (see darwin source)
+ // TODO (2nd official release): thread for hotplug (see darwin source)
}
if (hcd_root == NULL)
@@ -561,7 +563,7 @@ static int initialize_device(struct libusb_device *dev, libusb_bus_t busnum,
usbi_dbg("active config: %d", priv->active_config);
} else {
// USB devices that don't have a config value are usually missing a driver
- // TODO (v1.5): use this for automated driver installation
+ // TODO (after first official release): use this for automated driver installation
// NB: SetupDiGetDeviceRegistryProperty w/ SPDRP_INSTALL_STATE would tell us
// if the driver is properly installed, but driverless devices don't seem to
// be enumerable by SetupDi...
@@ -739,7 +741,7 @@ static int usb_enumerate_hub(struct libusb_context *ctx, struct discovered_devs
char *tmp_str = NULL, *path_str = NULL;
unsigned long session_id;
libusb_devaddr_t devaddr = 0;
- struct windows_device_priv *priv, *parent_priv;
+ struct windows_device_priv *priv, *parent_priv;
// obviously, root (HCD) hubs have no parent
is_hcd = (parent_dev == NULL);
@@ -871,6 +873,10 @@ static int usb_enumerate_hub(struct libusb_context *ctx, struct discovered_devs
if (dev) {
usbi_dbg("using existing device for session %ld", session_id);
priv = __device_priv(dev);
+ // Because we are rebuilding the list, there's no guarantee
+ // the parent device pointer is still the same.
+ // Other device data should still be reusable
+ priv->parent_dev = parent_dev;
} else {
usbi_dbg("allocating new device for session %ld", session_id);
if ((dev = usbi_alloc_device(ctx, session_id)) == NULL) {
@@ -1264,9 +1270,7 @@ static int set_device_paths(struct libusb_context *ctx, struct discovered_devs *
uint8_t api;
bool found;
-// list_driverless(ctx);
-
- // TODO (v1.5): MI_## automated driver installation
+ // TODO (after first official release): MI_## automated driver installation
guid = GUID_DEVINTERFACE_USB_DEVICE;
for (i = 0; ; i++)
{
@@ -1553,6 +1557,12 @@ static int windows_get_active_config_descriptor(struct libusb_device *dev, unsig
static int windows_open(struct libusb_device_handle *dev_handle)
{
struct windows_device_priv *priv = __device_priv(dev_handle->dev);
+ struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
+
+ if (priv->apib == NULL) {
+ usbi_err(ctx, "program assertion failed - device is not initialized");
+ return LIBUSB_ERROR_NO_DEVICE;
+ }
return priv->apib->open(dev_handle);
}
@@ -1581,10 +1591,10 @@ static int windows_get_configuration(struct libusb_device_handle *dev_handle, in
* from http://msdn.microsoft.com/en-us/library/ms793522.aspx: "The port driver
* does not currently expose a service that allows higher-level drivers to set
* the configuration."
- * TODO (>v1): See what users of devices with multiple confs report with this call
*/
static int windows_set_configuration(struct libusb_device_handle *dev_handle, int config)
{
+ struct windows_device_priv *priv = __device_priv(dev_handle->dev);
int r = LIBUSB_SUCCESS;
if (config >= USB_MAXCONFIG)
@@ -1595,6 +1605,9 @@ static int windows_set_configuration(struct libusb_device_handle *dev_handle, in
LIBUSB_REQUEST_SET_CONFIGURATION, (uint16_t)config,
0, NULL, 0, 1000);
+ if (r == LIBUSB_SUCCESS) {
+ priv->active_config = (uint8_t)config;
+ }
return r;
}
@@ -1678,9 +1691,7 @@ static void windows_destroy_device(struct libusb_device *dev)
static void windows_clear_transfer_priv(struct usbi_transfer *itransfer)
{
struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
-
- usbi_remove_pollfd(ITRANSFER_CTX(itransfer), transfer_priv->pollable_fd.fd);
- _libusb_free_fd(transfer_priv->pollable_fd.fd);
+ usbi_free_fd(transfer_priv->pollable_fd.fd);
safe_free(transfer_priv->hid_buffer);
}
@@ -1778,23 +1789,25 @@ static int windows_abort_transfers(struct usbi_transfer *itransfer)
static int windows_cancel_transfer(struct usbi_transfer *itransfer)
{
struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+#if defined(FORCE_INSTANT_TIMEOUTS)
+ struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
- windows_clear_transfer_priv(itransfer); // Cancel polling
-
+ // Forces instant overlapped completion on timeouts - use at your own risks
+ if (itransfer->flags | USBI_TRANSFER_TIMED_OUT) {
+ transfer_priv->pollable_fd.overlapped->Internal &= ~STATUS_PENDING;
+ }
+#endif
switch (transfer->type) {
case LIBUSB_TRANSFER_TYPE_CONTROL:
- windows_abort_control(itransfer);
- break;
+ return windows_abort_control(itransfer);
case LIBUSB_TRANSFER_TYPE_BULK:
case LIBUSB_TRANSFER_TYPE_INTERRUPT:
case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
- windows_abort_transfers(itransfer);
- break;
+ return windows_abort_transfers(itransfer);
default:
usbi_err(ITRANSFER_CTX(itransfer), "unknown endpoint type %d", transfer->type);
- break;
+ return LIBUSB_ERROR_INVALID_PARAM;
}
- return usbi_handle_transfer_cancellation(itransfer);
}
static void windows_transfer_callback(struct usbi_transfer *itransfer, uint32_t io_result, uint32_t io_size)
@@ -1803,7 +1816,7 @@ static void windows_transfer_callback(struct usbi_transfer *itransfer, uint32_t
struct windows_device_priv *priv = __device_priv(transfer->dev_handle->dev);
int status;
- usbi_dbg("handling I/O completion with status %d", io_result);
+ usbi_dbg("handling I/O completion with errcode %d", io_result);
switch(io_result) {
case NO_ERROR:
@@ -1813,12 +1826,25 @@ static void windows_transfer_callback(struct usbi_transfer *itransfer, uint32_t
usbi_dbg("detected endpoint stall");
status = LIBUSB_TRANSFER_STALL;
break;
+ case ERROR_SEM_TIMEOUT:
+ usbi_dbg("detected semaphore timeout");
+ status = LIBUSB_TRANSFER_TIMED_OUT;
+ break;
+ case ERROR_OPERATION_ABORTED:
+ if (itransfer->flags | USBI_TRANSFER_TIMED_OUT) {
+ usbi_dbg("detected timeout");
+ status = LIBUSB_TRANSFER_TIMED_OUT;
+ } else {
+ usbi_dbg("detected operation aborted");
+ status = LIBUSB_TRANSFER_CANCELLED;
+ }
+ break;
default:
- usbi_err(ITRANSFER_CTX(itransfer), "I/O error: %s", windows_error_str(0));
+ usbi_err(ITRANSFER_CTX(itransfer), "detected I/O error: %s", windows_error_str(0));
status = LIBUSB_TRANSFER_ERROR;
break;
}
-
+ windows_clear_transfer_priv(itransfer); // Cancel polling
usbi_handle_transfer_completion(itransfer, status);
}
@@ -1846,7 +1872,7 @@ static int windows_handle_events(struct libusb_context *ctx, struct pollfd *fds,
struct usbi_transfer *transfer;
DWORD io_size, io_result;
- pthread_mutex_lock(&ctx->open_devs_lock);
+ usbi_mutex_lock(&ctx->open_devs_lock);
for (i = 0; i < nfds && num_ready > 0; i++) {
usbi_dbg("checking fd %d with revents = %04x", fds[i].fd, fds[i].revents);
@@ -1880,7 +1906,7 @@ static int windows_handle_events(struct libusb_context *ctx, struct pollfd *fds,
io_result = GetLastError();
}
usbi_remove_pollfd(ctx, transfer_priv->pollable_fd.fd);
- _libusb_free_fd(transfer_priv->pollable_fd.fd);
+ usbi_free_fd(transfer_priv->pollable_fd.fd);
windows_handle_callback(transfer, io_result, io_size);
} else {
usbi_err(ctx, "could not find a matching transfer for fd %x", fds[i]);
@@ -1888,7 +1914,7 @@ static int windows_handle_events(struct libusb_context *ctx, struct pollfd *fds,
}
}
- pthread_mutex_unlock(&ctx->open_devs_lock);
+ usbi_mutex_unlock(&ctx->open_devs_lock);
return LIBUSB_SUCCESS;
}
@@ -2490,7 +2516,7 @@ static int winusb_submit_control_transfer(struct usbi_transfer *itransfer)
usbi_dbg("will use interface %d", current_interface);
winusb_handle = handle_priv->interface_handle[current_interface].api_handle;
- wfd = _libusb_create_fd(winusb_handle, _O_RDONLY);
+ wfd = usbi_create_fd(winusb_handle, _O_RDONLY);
if (wfd.fd < 0) {
return LIBUSB_ERROR_NO_MEM;
}
@@ -2498,7 +2524,7 @@ static int winusb_submit_control_transfer(struct usbi_transfer *itransfer)
if (!WinUsb_ControlTransfer(wfd.handle, *setup, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, size, NULL, wfd.overlapped)) {
if(GetLastError() != ERROR_IO_PENDING) {
usbi_err(ctx, "WinUsb_ControlTransfer failed: %s", windows_error_str(0));
- _libusb_free_fd(wfd.fd);
+ usbi_free_fd(wfd.fd);
return LIBUSB_ERROR_IO;
}
} else {
@@ -2566,7 +2592,7 @@ static int winusb_submit_bulk_transfer(struct usbi_transfer *itransfer)
winusb_handle = handle_priv->interface_handle[current_interface].api_handle;
direction_in = transfer->endpoint & LIBUSB_ENDPOINT_IN;
- wfd = _libusb_create_fd(winusb_handle, direction_in?_O_RDONLY:_O_WRONLY);
+ wfd = usbi_create_fd(winusb_handle, direction_in?_O_RDONLY:_O_WRONLY);
if (wfd.fd < 0) {
return LIBUSB_ERROR_NO_MEM;
}
@@ -2581,7 +2607,7 @@ static int winusb_submit_bulk_transfer(struct usbi_transfer *itransfer)
if (!ret) {
if(GetLastError() != ERROR_IO_PENDING) {
usbi_err(ctx, "WinUsb_Pipe Transfer failed: %s", windows_error_str(0));
- _libusb_free_fd(wfd.fd);
+ usbi_free_fd(wfd.fd);
return LIBUSB_ERROR_IO;
}
} else {
@@ -2670,7 +2696,7 @@ static int winusb_abort_transfers(struct usbi_transfer *itransfer)
* IOCTL_USB_HUB_CYCLE_PORT ioctl was removed from Vista => the best we can do is
* cycle the pipes (and even then, the control pipe can not be reset using WinUSB)
*/
-// TODO (v2): see if we can force eject the device and redetect it (reuse hotplug?)
+// TODO (2nd official release): see if we can force eject the device and redetect it (reuse hotplug?)
static int winusb_reset_device(struct libusb_device_handle *dev_handle)
{
struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
@@ -2689,7 +2715,7 @@ static int winusb_reset_device(struct libusb_device_handle *dev_handle)
{
// Cancel any pollable I/O
usbi_remove_pollfd(ctx, wfd.fd);
- _libusb_free_fd(wfd.fd);
+ usbi_free_fd(wfd.fd);
wfd = handle_to_winfd(winusb_handle);
}
@@ -3121,35 +3147,75 @@ static int _hid_set_report(struct hid_device_priv* dev, HANDLE hid_handle, int i
static int _hid_get_feature(struct hid_device_priv* dev, HANDLE hid_handle, int id, void *data, size_t *size)
{
- uint8_t buf[HID_MAX_REPORT_SIZE + 1];
+ uint8_t *buf;
+ ULONG read_size = (ULONG)(*size + 1);
+ int r = LIBUSB_ERROR_OTHER;
+ uint32_t err;
- if (*size >MAX_HID_REPORT_SIZE)
+ if (*size > MAX_HID_REPORT_SIZE)
return LIBUSB_ERROR_INVALID_PARAM;
+ buf = (uint8_t*)calloc(1, read_size);
+ if (buf == NULL) {
+ return LIBUSB_ERROR_NO_MEM;
+ }
buf[0] = (uint8_t)id;
+ usbi_dbg("report ID: 0x%02X", buf[0]);
- *size += 1;
- if (HidD_GetFeature(hid_handle, buf, (ULONG)*size)) {
- return LIBUSB_COMPLETED;
+ if (HidD_GetFeature(hid_handle, buf, read_size)) {
+ if (buf[0] != id) {
+ usbi_dbg("program assertion failed - mismatched report ID (got %02X instead of %02X)",
+ buf[0], id);
+ }
+ memcpy(data, buf+1, read_size);
+ r = LIBUSB_COMPLETED;
+ } else {
+ err = GetLastError();
+ switch (err) {
+ case ERROR_INVALID_FUNCTION:
+ r = LIBUSB_ERROR_NOT_FOUND;
+ default:
+ usbi_dbg("error %s", windows_error_str(r));
+ r = LIBUSB_ERROR_OTHER;
+ }
}
- return LIBUSB_ERROR_OTHER;
+ safe_free(buf);
+ return r;
}
static int _hid_set_feature(struct hid_device_priv* dev, HANDLE hid_handle, int id, void *data, size_t *size)
{
- uint8_t buf[HID_MAX_REPORT_SIZE + 1];
+ uint8_t *buf;
+ uint32_t err;
+ int r = LIBUSB_ERROR_OTHER;
+ ULONG write_size = (ULONG)(*size + 1);
if (*size >MAX_HID_REPORT_SIZE)
return LIBUSB_ERROR_INVALID_PARAM;
+ buf = (uint8_t*)malloc(write_size);
+ if (buf == NULL) {
+ return LIBUSB_ERROR_NO_MEM;
+ }
buf[0] = (uint8_t)id;
- memcpy(buf + 1, data, *size);
+ usbi_dbg("report ID: 0x%02X", buf[0]);
+
+ memcpy(buf+1, data, *size);
- *size += 1;
- if (HidD_SetFeature(hid_handle, buf, (ULONG)*size)) {
- return LIBUSB_COMPLETED;
+ if (HidD_SetFeature(hid_handle, buf, write_size)) {
+ r = LIBUSB_COMPLETED;
+ } else {
+ err = GetLastError();
+ switch (err) {
+ case ERROR_INVALID_FUNCTION:
+ r = LIBUSB_ERROR_NOT_FOUND;
+ default:
+ usbi_dbg("error %s", windows_error_str(r));
+ r = LIBUSB_ERROR_OTHER;
+ }
}
- return LIBUSB_ERROR_OTHER;
+ safe_free(buf);
+ return r;
}
static int _hid_class_request(struct hid_device_priv* dev, HANDLE hid_handle, int request_type,
@@ -3177,8 +3243,8 @@ static int _hid_class_request(struct hid_device_priv* dev, HANDLE hid_handle, in
&& report_type == HID_REPORT_TYPE_FEATURE)
return _hid_set_feature(dev, hid_handle, report_id, data, size);
- if (LIBUSB_REQ_OUT(request_type)
- && request == HID_REQ_SET_REPORT
+ if (LIBUSB_REQ_IN(request_type)
+ && request == HID_REQ_GET_REPORT
&& report_type == HID_REPORT_TYPE_FEATURE)
return _hid_get_feature(dev, hid_handle, report_id, data, size);
@@ -3308,15 +3374,15 @@ static int hid_open(struct libusb_device_handle *dev_handle)
priv->hid->input_report_id = value_caps[0].ReportID;
for (i=1; i<(int)size; i++) {
if (value_caps[i].ReportID != priv->hid->input_report_id) {
- usbi_warn(ctx, "multiple input report IDs found for HID");
- usbi_warn(ctx, " will only handle report ID 0x%02X for interrupt transfers",
+ usbi_warn(ctx, " multiple input report IDs found for HID:");
+ usbi_warn(ctx, " will only handle report ID 0x%02X for interrupt transfers",
priv->hid->input_report_id);
break;
}
}
- usbi_dbg("will use report ID 0x%02X for interrupt transfers", priv->hid->input_report_id);
+ usbi_dbg(" will use report ID 0x%02X for interrupt transfers", priv->hid->input_report_id);
} else {
- usbi_warn(ctx, "could process input report IDs");
+ usbi_warn(ctx, " could not process input report IDs");
}
safe_free(value_caps);
}
@@ -3332,20 +3398,20 @@ static int hid_open(struct libusb_device_handle *dev_handle)
priv->hid->output_report_id = value_caps[0].ReportID;
for (i=1; i<(int)size; i++) {
if (value_caps[i].ReportID != priv->hid->output_report_id) {
- usbi_warn(ctx, "multiple output report IDs found for HID");
- usbi_warn(ctx, " will only handle report ID 0x%02X for interrupt transfers",
+ usbi_warn(ctx, " multiple output report IDs found for HID:");
+ usbi_warn(ctx, " will only handle report ID 0x%02X for interrupt transfers",
priv->hid->output_report_id);
break;
}
}
- usbi_dbg("will use report ID 0x%02X for interrupt transfers", priv->hid->output_report_id);
+ usbi_dbg(" will use report ID 0x%02X for interrupt transfers", priv->hid->output_report_id);
} else {
usbi_warn(ctx, "could process output report IDs");
}
safe_free(value_caps);
}
- priv->hid->output_report_size = capabilities.OutputReportByteLength;
priv->hid->input_report_size = capabilities.InputReportByteLength;
+ priv->hid->output_report_size = capabilities.OutputReportByteLength;
priv->hid->feature_report_size = capabilities.FeatureReportByteLength;
// Fetch string descriptors
@@ -3488,7 +3554,7 @@ static int hid_submit_control_transfer(struct usbi_transfer *itransfer)
usbi_dbg("will use interface %d", current_interface);
hid_handle = handle_priv->interface_handle[current_interface].api_handle;
- wfd = _libusb_create_fd(hid_handle, _O_RDONLY);
+ wfd = usbi_create_fd(hid_handle, _O_RDONLY);
if (wfd.fd < 0) {
return LIBUSB_ERROR_NO_MEM;
}
@@ -3555,7 +3621,7 @@ static int hid_submit_control_transfer(struct usbi_transfer *itransfer)
transfer_priv->pollable_fd = wfd;
transfer_priv->interface_number = (uint8_t)current_interface;
} else {
- _libusb_free_fd(wfd.fd);
+ usbi_free_fd(wfd.fd);
}
return r;
@@ -3590,7 +3656,7 @@ static int hid_submit_bulk_transfer(struct usbi_transfer *itransfer) {
hid_handle = handle_priv->interface_handle[current_interface].api_handle;
direction_in = transfer->endpoint & LIBUSB_ENDPOINT_IN;
- wfd = _libusb_create_fd(hid_handle, direction_in?_O_RDONLY:_O_WRONLY);
+ wfd = usbi_create_fd(hid_handle, direction_in?_O_RDONLY:_O_WRONLY);
if (wfd.fd < 0) {
return LIBUSB_ERROR_NO_MEM;
}
@@ -3606,6 +3672,7 @@ static int hid_submit_bulk_transfer(struct usbi_transfer *itransfer) {
ret = ReadFile(hid_handle, transfer_priv->hid_buffer, transfer->length+1, &size, wfd.overlapped);
} else {
transfer_priv->hid_buffer[0] = priv->hid->output_report_id;
+ memcpy(transfer_priv->hid_buffer+1, transfer->buffer, transfer->length);
usbi_dbg("writing %d bytes (report ID: 0x%02X)", transfer->length+1, transfer_priv->hid_buffer[0]);
transfer_priv->hid_buffer[0] = 0;
ret = WriteFile(hid_handle, transfer_priv->hid_buffer, transfer->length+1, &size, wfd.overlapped);
@@ -3613,12 +3680,16 @@ static int hid_submit_bulk_transfer(struct usbi_transfer *itransfer) {
if (!ret) {
if (GetLastError() != ERROR_IO_PENDING) {
usbi_err(ctx, "HID transfer failed: %s", windows_error_str(0));
- _libusb_free_fd(wfd.fd);
+ usbi_free_fd(wfd.fd);
safe_free(transfer_priv->hid_buffer);
return LIBUSB_ERROR_IO;
}
} else {
- safe_free(transfer_priv->hid_buffer);
+ // Only write operations that completed synchronously need to free up
+ // hid_buffer. For reads, copy_transfer_data() handles that process.
+ if (transfer->endpoint & LIBUSB_ENDPOINT_OUT) {
+ safe_free(transfer_priv->hid_buffer);
+ }
if (size == 0) {
usbi_err(ctx, "program assertion failed - no data was transferred");
size = 1;
@@ -3628,7 +3699,7 @@ static int hid_submit_bulk_transfer(struct usbi_transfer *itransfer) {
r = LIBUSB_ERROR_OVERFLOW;
}
wfd.completed_synchronously = true;
- wfd.overlapped->InternalHigh = size - 1;
+ wfd.overlapped->InternalHigh = size;
}
transfer_priv->pollable_fd = wfd;