summaryrefslogtreecommitdiff
path: root/libusb/os/windows_usb.c
diff options
context:
space:
mode:
authorPete Batard <pbatard@gmail.com>2010-01-14 01:24:28 +0000
committerPete Batard <pbatard@gmail.com>2010-01-14 01:24:28 +0000
commit055b0136bf9a59abba662ea1313c516a9b847772 (patch)
tree14261ed325361c4bb367dddda531db4138764de5 /libusb/os/windows_usb.c
parent434caf7c49b819f94035643862814a0e86112ec4 (diff)
downloadlibusb-055b0136bf9a59abba662ea1313c516a9b847772.tar.gz
svn r38:
- bulk/interrupt I/O at last! - endpoint handling - minor code improvements
Diffstat (limited to 'libusb/os/windows_usb.c')
-rw-r--r--libusb/os/windows_usb.c244
1 files changed, 192 insertions, 52 deletions
diff --git a/libusb/os/windows_usb.c b/libusb/os/windows_usb.c
index fc01e59..18885f8 100644
--- a/libusb/os/windows_usb.c
+++ b/libusb/os/windows_usb.c
@@ -88,6 +88,8 @@ static int winusb_claim_interface(struct libusb_device_handle *dev_handle, int i
static int winusb_release_interface(struct libusb_device_handle *dev_handle, int iface);
static int winusb_submit_control_transfer(struct usbi_transfer *itransfer);
static int winusb_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting);
+static int winusb_submit_bulk_transfer(struct usbi_transfer *itransfer);
+static int winusb_submit_iso_transfer(struct usbi_transfer *itransfer);
// HCD private chained list
struct windows_hcd_priv* hcd_root = NULL;
@@ -267,6 +269,54 @@ err_exit:
return NULL;
}
+/*
+ * Populate the endpoints addresses of the device_priv interface helper structs
+ */
+static void windows_assign_endpoints(struct libusb_device *dev, int iface, int altsetting)
+{
+ int i;
+ struct windows_device_priv *priv = __device_priv(dev);
+ struct libusb_config_descriptor *conf_desc;
+ const struct libusb_interface_descriptor *if_desc;
+
+ if (libusb_get_config_descriptor(dev, 0, &conf_desc) == LIBUSB_SUCCESS) {
+ if_desc = &conf_desc->interface[iface].altsetting[altsetting];
+ safe_free(priv->interface[iface].endpoint);
+ priv->interface[iface].endpoint = malloc(if_desc->bNumEndpoints);
+ if (priv->interface[iface].endpoint != NULL) {
+ priv->interface[iface].nb_endpoints = if_desc->bNumEndpoints;
+ for (i=0; i<if_desc->bNumEndpoints; i++) {
+ priv->interface[iface].endpoint[i] = if_desc->endpoint[i].bEndpointAddress;
+ usbi_dbg("assigned endpoint %02X to interface %d", priv->interface[iface].endpoint[i], iface);
+ }
+ }
+ libusb_free_config_descriptor(conf_desc);
+ }
+}
+
+/*
+ * Lookup interface by endpoint address. -1 if not found
+ */
+static int interface_by_endpoint(struct windows_device_priv *priv,
+ struct windows_device_handle_priv *handle_priv, uint8_t endpoint_address)
+{
+ int i, j;
+ for (i=0; i<USB_MAXINTERFACES; i++) {
+ if (handle_priv->interface_handle[i].winusb == INVALID_HANDLE_VALUE)
+ continue;
+ if (handle_priv->interface_handle[i].winusb == 0)
+ continue;
+ if (priv->interface[i].endpoint == NULL)
+ continue;
+ for (j=0; j<priv->interface[i].nb_endpoints; j++) {
+ if (priv->interface[i].endpoint[j] == endpoint_address) {
+ return i;
+ }
+ }
+ }
+ return -1;
+}
+
/*
* init: libusb backend init function
*
@@ -335,23 +385,6 @@ static int windows_init(struct libusb_context *ctx)
usbi_dbg("hires timer available (Frequency: %"PRIu64" Hz)", hires_frequency);
}
-//#define TEST_TIMER 3333
-#ifdef TEST_TIMER
- // Test our timer
- struct timespec tp;
- uint64_t start_time, end_time;
- uint64_t duration_ms = TEST_TIMER;
- if (windows_clock_gettime(USBI_CLOCK_MONOTONIC, &tp) != LIBUSB_SUCCESS)
- return LIBUSB_ERROR_OTHER;
- // Make sure computations are 64 bit
- start_time = ((uint64_t)tp.tv_sec)*1000000000 + ((uint64_t)tp.tv_nsec);
- Sleep(duration_ms);
- if (windows_clock_gettime(USBI_CLOCK_MONOTONIC, &tp) != LIBUSB_SUCCESS)
- return LIBUSB_ERROR_OTHER;
- end_time = ((uint64_t)tp.tv_sec)*1000000000 + ((uint64_t)tp.tv_nsec);
- usbi_dbg("timed %"PRIu64" ns: %"PRIu64" ns", (uint64_t)(duration_ms*1000000), (uint64_t)(end_time-start_time));
-#endif
-
// We maintain a chained list of the Host Controllers found
struct windows_hcd_priv** _hcd_cur = &hcd_root;
@@ -512,7 +545,7 @@ static int cache_config_descriptors(struct libusb_device *dev, HANDLE hub_handle
memset(&cd_buf_short, 0, size);
cd_buf_short.req.ConnectionIndex = priv->connection_index;
- cd_buf_short.req.SetupPacket.bmRequest = 0x80;
+ cd_buf_short.req.SetupPacket.bmRequest = LIBUSB_ENDPOINT_IN;
cd_buf_short.req.SetupPacket.bRequest = USB_REQUEST_GET_DESCRIPTOR;
cd_buf_short.req.SetupPacket.wValue = (USB_CONFIGURATION_DESCRIPTOR_TYPE << 8) | i;
cd_buf_short.req.SetupPacket.wIndex = i;
@@ -539,7 +572,7 @@ static int cache_config_descriptors(struct libusb_device *dev, HANDLE hub_handle
// Actual call
cd_buf_actual->ConnectionIndex = priv->connection_index;
- cd_buf_actual->SetupPacket.bmRequest = 0x80;
+ cd_buf_actual->SetupPacket.bmRequest = LIBUSB_ENDPOINT_IN;
cd_buf_actual->SetupPacket.bRequest = USB_REQUEST_GET_DESCRIPTOR;
cd_buf_actual->SetupPacket.wValue = (USB_CONFIGURATION_DESCRIPTOR_TYPE << 8) | i;
cd_buf_actual->SetupPacket.wIndex = i;
@@ -920,18 +953,18 @@ 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->interface_path[interface_number] = sanitized_path[j];
+ priv->interface[interface_number].path = sanitized_path[j];
sanitized_path[j] = NULL;
}
}
safe_free(sanitized_short);
- if (priv->interface_path[interface_number] == NULL) {
+ if (priv->interface[interface_number].path == NULL) {
usbi_warn(ctx, "could not retrieve full path for interface %d",
interface_number);
continue;
}
- usbi_dbg("interface_path[%d]: %s", interface_number, priv->interface_path[interface_number]);
+ usbi_dbg("interface_path[%d]: %s", interface_number, priv->interface[interface_number].path);
found = true;
}
@@ -1009,6 +1042,7 @@ static int set_device_paths(struct libusb_context *ctx, struct discovered_devs *
// We're out of luck for Location Information on XP, since SPDRP_LOCATION_INFORMATION
// returns anything but an actual location. However, it looks like the port number is
// the last digit before '#{' in the path string, so let's try that.
+ // TODO: device enum that doesn't require this workaround
found = false;
for (j=0; j<safe_strlen(dev_interface_details->DevicePath); j++) {
if ( (dev_interface_details->DevicePath[j] == '{')
@@ -1087,7 +1121,7 @@ static int set_device_paths(struct libusb_context *ctx, struct discovered_devs *
if (safe_strcmp(priv->driver, "WinUSB") == 0) {
priv->api = API_WINUSB;
// For non composite, the first interface is the same as the device
- priv->interface_path[0] = safe_strdup(priv->path); // needs strdup
+ priv->interface[0].path = safe_strdup(priv->path); // needs strdup
// Composite (multi-interface) devices are identified by their use of
// the USB Common Class Generic Parent driver
} else if (safe_strcmp(reg_key, "usbccgp") == 0) {
@@ -1225,7 +1259,6 @@ static int windows_open(struct libusb_device_handle *dev_handle)
API_CALL(priv->api, open, dev_handle);
- // TODO: update pipe info here?
return r;
}
@@ -1240,8 +1273,6 @@ static void windows_close(struct libusb_device_handle *dev_handle)
default:
break;
}
-
- // TODO: free pipe?
}
/*
@@ -1255,6 +1286,13 @@ static int windows_get_configuration(struct libusb_device_handle *dev_handle, in
return LIBUSB_SUCCESS;
}
+/*
+ * 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.
+ * The current version of this function still attempts to change conf to see
+ * what happens...
+ */
static int windows_set_configuration(struct libusb_device_handle *dev_handle, int config)
{
int r = LIBUSB_SUCCESS;
@@ -1268,7 +1306,7 @@ static int windows_set_configuration(struct libusb_device_handle *dev_handle, in
0, NULL, 0, 1000);
if (r == LIBUSB_SUCCESS) {
- // TODO: update pipes data?
+ // If the above ever works, you'd need to invalidate the endpoints & interfaces
}
return r;
@@ -1282,27 +1320,42 @@ static int windows_claim_interface(struct libusb_device_handle *dev_handle, int
if (iface >= USB_MAXINTERFACES)
return LIBUSB_ERROR_INVALID_PARAM;
+ safe_free(priv->interface[iface].endpoint);
+ priv->interface[iface].nb_endpoints= 0;
+
API_CALL(priv->api, claim_interface, dev_handle, iface);
+ if (r == LIBUSB_SUCCESS) {
+ windows_assign_endpoints(dev_handle->dev, iface, 0);
+ }
+
return r;
}
-static int windows_release_interface(struct libusb_device_handle *dev_handle, int iface)
+static int windows_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting)
{
int r = LIBUSB_SUCCESS;
struct windows_device_priv *priv = __device_priv(dev_handle->dev);
- API_CALL(priv->api, release_interface, dev_handle, iface);
+ safe_free(priv->interface[iface].endpoint);
+ priv->interface[iface].nb_endpoints= 0;
+
+ API_CALL(priv->api, set_interface_altsetting, dev_handle, iface, altsetting);
+
+ if (r == LIBUSB_SUCCESS) {
+ windows_assign_endpoints(dev_handle->dev, iface, altsetting);
+ }
return r;
}
-static int windows_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting)
+static int windows_release_interface(struct libusb_device_handle *dev_handle, int iface)
{
int r = LIBUSB_SUCCESS;
struct windows_device_priv *priv = __device_priv(dev_handle->dev);
- API_CALL(priv->api, set_interface_altsetting, dev_handle, iface, altsetting);
+ windows_set_interface_altsetting(dev_handle, iface, 0);
+ API_CALL(priv->api, release_interface, dev_handle, iface);
return r;
}
@@ -1367,7 +1420,7 @@ static void windows_destroy_device(struct libusb_device *dev)
static int submit_bulk_transfer(struct usbi_transfer *itransfer)
{
-/* struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
struct windows_device_priv *priv = __device_priv(transfer->dev_handle->dev);
@@ -1378,17 +1431,30 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer)
return r;
}
- usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, POLLIN);
+ usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd,
+ (transfer->endpoint & LIBUSB_ENDPOINT_IN)?POLLIN:POLLOUT);
return LIBUSB_SUCCESS;
-*/
- return LIBUSB_ERROR_NOT_SUPPORTED;
}
// WinUSB does not support isochronous transfers
static int submit_iso_transfer(struct usbi_transfer *itransfer)
{
- return LIBUSB_ERROR_NOT_SUPPORTED;
+ struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
+ struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+ struct windows_device_priv *priv = __device_priv(transfer->dev_handle->dev);
+ int r;
+
+ API_CALL(priv->api, submit_iso_transfer, itransfer);
+ if (r != LIBUSB_SUCCESS) {
+ return r;
+ }
+
+ usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd,
+ (transfer->endpoint & LIBUSB_ENDPOINT_IN)?POLLIN:POLLOUT);
+
+ return LIBUSB_SUCCESS;
}
static int submit_control_transfer(struct usbi_transfer *itransfer)
@@ -1457,6 +1523,7 @@ static int windows_cancel_transfer(struct usbi_transfer *itransfer)
static void windows_clear_transfer_priv(struct usbi_transfer *itransfer)
{
+ // TODO
}
static void windows_control_callback (struct usbi_transfer *itransfer, uint32_t io_result, uint32_t io_size)
@@ -1483,6 +1550,29 @@ static void windows_control_callback (struct usbi_transfer *itransfer, uint32_t
usbi_handle_transfer_completion(itransfer, status);
}
+static void windows_bulk_callback (struct usbi_transfer *itransfer, uint32_t io_result, uint32_t io_size)
+{
+ int status;
+
+ usbi_dbg("handling I/O completion with status %d", io_result);
+
+ switch(io_result) {
+ case NO_ERROR:
+ status = LIBUSB_TRANSFER_COMPLETED;
+ itransfer->transferred += io_size;
+ break;
+ case ERROR_GEN_FAILURE:
+ usbi_dbg("unsupported I/O request");
+ status = LIBUSB_TRANSFER_STALL;
+ break;
+ default:
+ usbi_err(ITRANSFER_CTX(itransfer), "I/O error: %s", windows_error_str(0));
+ status = LIBUSB_TRANSFER_ERROR;
+ break;
+ }
+
+ usbi_handle_transfer_completion(itransfer, status);
+}
static void windows_handle_callback (struct usbi_transfer *itransfer, uint32_t io_result, uint32_t io_size)
{
@@ -1494,7 +1584,7 @@ static void windows_handle_callback (struct usbi_transfer *itransfer, uint32_t i
break;
case LIBUSB_TRANSFER_TYPE_BULK:
case LIBUSB_TRANSFER_TYPE_INTERRUPT:
-// windows_bulk_callback (itransfer, io_result, io_size);
+ windows_bulk_callback (itransfer, io_result, io_size);
break;
case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
// TODO: ain't gonna happen with WinUSB only
@@ -1635,7 +1725,6 @@ const struct usbi_os_backend windows_backend = {
* WinUSB API functions
*/
-// TO_DO: check if DLL has been loaded in all functions?
static int winusb_api_init(struct libusb_context *ctx)
{
DLL_LOAD(winusb.dll, WinUsb_Initialize, TRUE);
@@ -1666,9 +1755,6 @@ static int winusb_api_exit(void)
return LIBUSB_SUCCESS;
}
-/*
- * TODO: check if device has been diconnected
- */
static int winusb_open(struct libusb_device_handle *dev_handle)
{
struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
@@ -1680,12 +1766,10 @@ static int winusb_open(struct libusb_device_handle *dev_handle)
CHECK_WINUSB_AVAILABLE;
- // TODO: better check for detached devices
-
// Each interface requires a sperate handle for WinUSB
for (i = 0; i < USB_MAXINTERFACES; i++) {
- if (priv->interface_path[i] != NULL) {
- file_handle = CreateFileA(priv->interface_path[i], GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ,
+ if (priv->interface[i].path != NULL) {
+ file_handle = CreateFileA(priv->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) {
usbi_err(ctx, "could not open device %s (interface %d): %s", priv->path, i, windows_error_str(0));
@@ -1802,12 +1886,6 @@ static int winusb_claim_interface(struct libusb_device_handle *dev_handle, int i
return LIBUSB_SUCCESS;
}
-/*
- * TODO:
- * This function should also generate a SET_INTERFACE control request,
- * resetting the alternate setting of that interface to 0. It's OK for
- * this function to block as a result.
- */
static int winusb_release_interface(struct libusb_device_handle *dev_handle, int iface)
{
struct windows_device_handle_priv *handle_priv = (struct windows_device_handle_priv *)dev_handle->os_priv;
@@ -1862,7 +1940,7 @@ static int winusb_submit_control_transfer(struct usbi_transfer *itransfer)
// usbi_dbg("active interface = %d (handle = %p)", handle_priv->active_interface, handle_priv->interface_handle[handle_priv->active_interface].winusb);
wfd = create_fd_for_poll(winusb_handle, _O_RDONLY);
if (wfd.fd < 0) {
- return LIBUSB_ERROR_NO_MEM; // somtehing else
+ return LIBUSB_ERROR_NO_MEM;
}
if (!WinUsb_ControlTransfer(wfd.handle, *setup, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, size, NULL, wfd.overlapped)) {
@@ -1891,6 +1969,8 @@ static int winusb_set_interface_altsetting(struct libusb_device_handle *dev_hand
struct windows_device_handle_priv *handle_priv = (struct windows_device_handle_priv *)dev_handle->os_priv;
HANDLE winusb_handle;
+ CHECK_WINUSB_AVAILABLE;
+
winusb_handle = handle_priv->interface_handle[iface].winusb;
if ((winusb_handle == 0) || (winusb_handle == INVALID_HANDLE_VALUE)) {
usbi_err(ctx, "interface must be claimed first");
@@ -1906,3 +1986,63 @@ static int winusb_set_interface_altsetting(struct libusb_device_handle *dev_hand
return LIBUSB_SUCCESS;
}
+
+static int winusb_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_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+ 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);
+ HANDLE winusb_handle;
+ bool direction_in, ret;
+ int current_interface = -1;
+ struct winfd wfd;
+
+ CHECK_WINUSB_AVAILABLE;
+
+ transfer_priv->pollable_fd = INVALID_WINFD;
+
+ 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);
+
+ winusb_handle = handle_priv->interface_handle[current_interface].winusb;
+ direction_in = transfer->endpoint & LIBUSB_ENDPOINT_IN;
+
+ wfd = create_fd_for_poll(winusb_handle, direction_in?_O_RDONLY:_O_WRONLY);
+ if (wfd.fd < 0) {
+ return LIBUSB_ERROR_NO_MEM;
+ }
+
+ if (direction_in) {
+ usbi_dbg("reading %d bytes", transfer->length);
+ ret = WinUsb_ReadPipe(winusb_handle, transfer->endpoint, transfer->buffer, transfer->length, NULL, wfd.overlapped);
+ } else {
+ usbi_dbg("writing %d bytes", transfer->length);
+ ret = WinUsb_WritePipe(winusb_handle, transfer->endpoint, transfer->buffer, transfer->length, NULL, wfd.overlapped);
+ }
+ if (!ret) {
+ if(GetLastError() != ERROR_IO_PENDING) {
+ usbi_err(ctx, "WinUsb_Pipe Transfer failed: %s", windows_error_str(0));
+ free_fd_for_poll(wfd.fd);
+ return LIBUSB_ERROR_IO;
+ }
+ } else {
+ usbi_err(ctx, "chill out man; this is like way too fast for async I/O...");
+ free_fd_for_poll(wfd.fd);
+ return LIBUSB_ERROR_IO;
+ }
+
+ transfer_priv->pollable_fd = wfd;
+
+ return LIBUSB_SUCCESS;
+}
+
+static int winusb_submit_iso_transfer(struct usbi_transfer *itransfer) {
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+}