diff options
-rw-r--r-- | examples/xusb.c | 49 | ||||
-rw-r--r-- | libusb/os/windows_usb.c | 253 | ||||
-rw-r--r-- | libusb/os/windows_usb.h | 6 |
3 files changed, 244 insertions, 64 deletions
diff --git a/examples/xusb.c b/examples/xusb.c index 43e1ca7..686392d 100644 --- a/examples/xusb.c +++ b/examples/xusb.c @@ -25,6 +25,29 @@ #include <libusb/libusb.h> +//#define USE_MOUSE +#define USE_XBOX + +#ifdef USE_MOUSE +// Logitech optical mouse +#define VID 0x046D +#define PID 0xC03E +#endif + +#ifdef USE_XBOX +// Microsoft XBox Controller +#define VID 0x045E +#define PID 0x0289 +#endif + +#ifdef USE_KEY +// 2 GB Usb key +#define VID 0x0204 +#define PID 0x6025 +#endif + + + static void print_devs(libusb_device **devs) { libusb_device *dev; @@ -44,11 +67,31 @@ static void print_devs(libusb_device **devs) libusb_get_bus_number(dev), libusb_get_device_address(dev)); // DEBUG: Access an XBox gamepad through WinUSB - if ((desc.idVendor == 0x045e) && (desc.idProduct == 0x0289)) { - printf("Opening Xbox gamepad:\n"); +// if ((desc.idVendor == 0x045e) && (desc.idProduct == 0x0289)) { + if ((desc.idVendor == VID) && (desc.idProduct == PID)) { + printf("Opening device:\n"); r = libusb_open(dev, &handle); - if (r != LIBUSB_SUCCESS) + if (r != LIBUSB_SUCCESS) { + printf("libusb error: %d\n", r); + continue; + } + + printf("Claiming interface:\n"); + r = libusb_claim_interface(handle, 0); + if (r != LIBUSB_SUCCESS) { printf("libusb error: %d\n", r); + continue; + } + + printf("Releasing interface:\n"); + r = libusb_release_interface(handle, 0); + if (r != LIBUSB_SUCCESS) { + printf("libusb error: %d\n", r); + continue; + } + + printf("Closing device:\n"); + libusb_close(handle); } } } diff --git a/libusb/os/windows_usb.c b/libusb/os/windows_usb.c index 438fa26..dd75422 100644 --- a/libusb/os/windows_usb.c +++ b/libusb/os/windows_usb.c @@ -83,6 +83,8 @@ static int winusb_api_init(struct libusb_context *ctx); static int winusb_api_exit(void); static int winusb_open(struct libusb_device_handle *dev_handle); static void winusb_close(struct libusb_device_handle *dev_handle); +static int winusb_claim_interface(struct libusb_device_handle *dev_handle, int iface); +static int winusb_release_interface(struct libusb_device_handle *dev_handle, int iface); // HCD private chained list struct windows_hcd_priv* hcd_root = NULL; @@ -92,7 +94,7 @@ uint64_t hires_frequency, hires_ticks_to_ps; const uint64_t epoch_time = 116444736000000000; DWORD_PTR old_affinity_mask; bool api_winusb_available = false; -#define CHECK_WINUSB_AVAILABLE do { if (!api_winusb_available) return LIBUSB_ERROR_NOT_SUPPORTED; } while (0) +#define CHECK_WINUSB_AVAILABLE do { if (!api_winusb_available) return LIBUSB_ERROR_ACCESS; } while (0) /* * Converts a WCHAR string to UTF8 (allocate returned string) @@ -577,7 +579,7 @@ static int usb_enumerate_hub(struct libusb_context *ctx, struct discovered_devs if (conn_info.ConnectionStatus == NoDeviceConnected) { continue; - } + } if (conn_info.DeviceAddress == 0) { LOOP_CONTINUE("program assertion failed - device address is zero " @@ -661,6 +663,13 @@ static int usb_enumerate_hub(struct libusb_context *ctx, struct discovered_devs LOOP_CHECK(initialize_device(dev, busnum, devaddr, path_str, i, parent_dev)); priv = __device_priv(dev); + // Detect devices that don't have a driver + // TODO: use this for automated driver installation + if (!conn_info.CurrentConfigurationValue) { + priv->driver = safe_strdup("no_driver"); + usbi_dbg("* THIS DEVICE HAS NO DRIVER *"); + } + path_str = NULL; // protect our path from being freed // Setup the cached descriptors. Note that only non HCDs can fetch descriptors @@ -690,6 +699,8 @@ static int usb_enumerate_hub(struct libusb_context *ctx, struct discovered_devs // Finally, if device is a hub, recurse if (conn_info.DeviceIsHub) { + // Force the driver name + priv->driver = safe_strdup("usbhub"); // Find number of ports for this hub size = sizeof(USB_NODE_INFORMATION); if (!DeviceIoControl(handle, IOCTL_USB_GET_NODE_INFORMATION, &hub_node, size, @@ -739,12 +750,14 @@ static int set_device_paths(struct libusb_context *ctx, struct discovered_devs * guid = GUID_DEVINTERFACE_USB_DEVICE; dev_info = SetupDiGetClassDevs(&guid, NULL, NULL, (DIGCF_PRESENT | DIGCF_DEVICEINTERFACE)); + if (dev_info != INVALID_HANDLE_VALUE) { dev_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); for (i = 0; ; i++) { + // safe loop: free up any (unprotected) dynamic resource safe_free(dev_interface_details); safe_free(sanitized_path); @@ -800,7 +813,7 @@ static int set_device_paths(struct libusb_context *ctx, struct discovered_devs * } // Retrieve parent's path using PnP Configuration Manager (CM) - if (CM_Get_Parent(&parent_devinst, dev_info_data.DevInst, 0) != CR_SUCCESS) {
+ if (CM_Get_Parent(&parent_devinst, dev_info_data.DevInst, 0) != CR_SUCCESS) { LOOP_CONTINUE("could not retrieve parent info data for device #%u, skipping: %s", i, windows_error_str()); } @@ -832,6 +845,17 @@ static int set_device_paths(struct libusb_context *ctx, struct discovered_devs * usbi_dbg("path (%d:%d): %s", discdevs->devices[j]->bus_number, discdevs->devices[j]->device_address, priv->path); found = true; + + // The service name is really the driver name without ".sys" ("WinUSB", "HidUsb", ...) + // We store it as it tells which API we should use to access our device + if(!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data, SPDRP_SERVICE, + ®_type, (BYTE*)reg_key, MAX_KEY_LENGTH, &size)) { + LOOP_CONTINUE("could not retrieve driver information for device #%u, skipping: %s", + i, windows_error_str()); + } + usbi_dbg("driver: %s\n", reg_key); + priv->driver = safe_strdup(reg_key); + break; } } @@ -968,10 +992,14 @@ static int windows_get_active_config_descriptor(struct libusb_device *dev, unsig static int windows_open(struct libusb_device_handle *dev_handle) { int r = LIBUSB_SUCCESS; + struct windows_device_priv *priv = __device_priv(dev_handle->dev); - r = winusb_open(dev_handle); - if (r != LIBUSB_SUCCESS) - return r; + // Select the API to use + if (safe_strcmp(priv->driver, "WinUSB") == 0) { + r = winusb_open(dev_handle); + } else { + r = LIBUSB_ERROR_NOT_SUPPORTED; + } // TODO: setup polling return r; @@ -979,7 +1007,12 @@ static int windows_open(struct libusb_device_handle *dev_handle) static void windows_close(struct libusb_device_handle *dev_handle) { - winusb_close(dev_handle); + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + + if (safe_strcmp(priv->driver, "WinUSB") == 0) { + winusb_close(dev_handle); + } + // TODO: cancel polling } @@ -1007,12 +1040,33 @@ static int windows_set_configuration(struct libusb_device_handle *dev_handle, in static int windows_claim_interface(struct libusb_device_handle *dev_handle, int iface) { - return LIBUSB_ERROR_NOT_SUPPORTED; + int r = LIBUSB_SUCCESS; + + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + + // Select the API to use + if (safe_strcmp(priv->driver, "WinUSB") == 0) { + r = winusb_claim_interface(dev_handle, iface); + } else { + r = LIBUSB_ERROR_NOT_SUPPORTED; + } + + return r; } static int windows_release_interface(struct libusb_device_handle *dev_handle, int iface) { - return LIBUSB_ERROR_NOT_SUPPORTED; + int r = LIBUSB_SUCCESS; + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + + // Select the API to use + if (safe_strcmp(priv->driver, "WinUSB") == 0) { + r = winusb_release_interface(dev_handle, iface); + } else { + r = LIBUSB_ERROR_NOT_SUPPORTED; + } + + return r; } static int windows_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting) @@ -1062,6 +1116,7 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) static int submit_control_transfer(struct usbi_transfer *itransfer) { + // TODO: call WinUsb_ControlTransfer return LIBUSB_ERROR_NOT_SUPPORTED; } @@ -1227,7 +1282,6 @@ static int winusb_api_init(struct libusb_context *ctx) static int winusb_api_exit(void) { - CHECK_WINUSB_AVAILABLE; api_winusb_available = false; return LIBUSB_SUCCESS; } @@ -1242,11 +1296,6 @@ static int winusb_open(struct libusb_device_handle *dev_handle) struct windows_device_handle_priv *handle_priv = (struct windows_device_handle_priv *)dev_handle->os_priv; HANDLE handle; - WINUSB_INTERFACE_HANDLE winusb_handle; - USB_INTERFACE_DESCRIPTOR interface_desc; - WINUSB_PIPE_INFORMATION pipe_info; - uint8_t speed, i; - ULONG length; CHECK_WINUSB_AVAILABLE; /* @@ -1270,19 +1319,23 @@ static int winusb_open(struct libusb_device_handle *dev_handle) } handle_priv->file_handle = handle; +} - if (!WinUsb_Initialize(handle, &winusb_handle)) { - usbi_err(ctx, "could not initialize WinUSB: %s", windows_error_str()); - return LIBUSB_ERROR_IO; - } +/* + + + WINUSB_INTERFACE_HANDLE interface_handle; + USB_INTERFACE_DESCRIPTOR interface_desc; + WINUSB_PIPE_INFORMATION pipe_info; + uint8_t speed, i; + ULONG length; - handle_priv->winusb_handle = winusb_handle;
-
- length = sizeof(speed);
- if (!WinUsb_QueryDeviceInformation(winusb_handle, DEVICE_SPEED, &length, &speed)) {
+ + length = sizeof(speed); + if (!WinUsb_QueryDeviceInformation(winusb_handle, DEVICE_SPEED, &length, &speed)) { usbi_err(ctx, "could not get device speed: %s", windows_error_str()); return LIBUSB_ERROR_IO; - }
+ } /* TODO: * Because the Fx2 device supports only one interface that has no alternative settings, @@ -1290,55 +1343,135 @@ static int winusb_open(struct libusb_device_handle *dev_handle) * once. If the device supports multiple interfaces, call WinUsb_GetAssociatedInterface * to obtain interface handles for associated interfaces. */ - - if (!WinUsb_QueryInterfaceSettings(winusb_handle, 0, &interface_desc)) {
+/* + if (!WinUsb_QueryInterfaceSettings(winusb_handle, 0, &interface_desc)) { usbi_err(ctx, "could not get interface settings: %s", windows_error_str()); return LIBUSB_ERROR_IO; - }
-
- for(i=0; i<interface_desc.bNumEndpoints; i++)
- {
- if (!WinUsb_QueryPipe(winusb_handle, 0, i, &pipe_info)) {
+ } + + for(i=0; i<interface_desc.bNumEndpoints; i++) + { + if (!WinUsb_QueryPipe(winusb_handle, 0, i, &pipe_info)) { usbi_err(ctx, "could not query pipe: %s", windows_error_str()); return LIBUSB_ERROR_IO; - }
-
- if (pipe_info.PipeType == UsbdPipeTypeBulk && USB_ENDPOINT_DIRECTION_IN(pipe_info.PipeId)) {
- usbi_dbg("pipe %d: bulk In pipe", i);
-// bulk_in_pipe = pipe_info.PipeId;
- }
- else if(pipe_info.PipeType == UsbdPipeTypeBulk && USB_ENDPOINT_DIRECTION_OUT(pipe_info.PipeId)) {
- usbi_dbg("pipe %d: bulk Out pipe", i);
-// bulk_out_pipe = pipe_info.PipeId;
- }
- else if(pipe_info.PipeType == UsbdPipeTypeInterrupt) {
- usbi_dbg("pipe %d: interrupt pipe", i);
-// interrupt_pipe = pipe_info.PipeId;
- }
- else {
- usbi_dbg("%d: ceci n'est pas un(e) pipe", i);
- break;
- }
- }
+ } + + if (pipe_info.PipeType == UsbdPipeTypeBulk && USB_ENDPOINT_DIRECTION_IN(pipe_info.PipeId)) { + usbi_dbg("pipe %d: bulk In pipe", i); +// bulk_in_pipe = pipe_info.PipeId; + } + else if(pipe_info.PipeType == UsbdPipeTypeBulk && USB_ENDPOINT_DIRECTION_OUT(pipe_info.PipeId)) { + usbi_dbg("pipe %d: bulk Out pipe", i); +// bulk_out_pipe = pipe_info.PipeId; + } + else if(pipe_info.PipeType == UsbdPipeTypeInterrupt) { + usbi_dbg("pipe %d: interrupt pipe", i); +// interrupt_pipe = pipe_info.PipeId; + } + else { + usbi_dbg("%d: ceci n'est pas un(e) pipe", i); + break; + } + } return LIBUSB_SUCCESS; } - +*/ static void winusb_close(struct libusb_device_handle *dev_handle) { + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); struct windows_device_handle_priv *handle_priv = (struct windows_device_handle_priv *)dev_handle->os_priv; + + if (!api_winusb_available) + return; - if (api_winusb_available) - { - // libusb_open zeroes the priv struct, which is different from INVALID_HANDLE_VALUE (-1) - // However: http://www.tech-archive.net/Archive/Development/microsoft.public.win32.programmer.kernel/2005-05/msg00448.html - // "On any NT-derived platform 0 (zero) can never be a valid handle value" so we should be OK - if ((handle_priv->winusb_handle != 0) && (handle_priv->winusb_handle != INVALID_HANDLE_VALUE)) { - WinUsb_Free(handle_priv->winusb_handle); + if ((handle_priv->file_handle != 0) && (handle_priv->file_handle != INVALID_HANDLE_VALUE)) { + CloseHandle(handle_priv->file_handle); + } +} + +/* Claim an interface. When claimed, the application can then perform + * I/O to an interface's endpoints. + * + * Return: + * - LIBUSB_ERROR_NOT_FOUND if the interface does not exist + * - LIBUSB_ERROR_BUSY if the interface is in use by another driver/app + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ +static int winusb_claim_interface(struct libusb_device_handle *dev_handle, int iface) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = (struct windows_device_handle_priv *)dev_handle->os_priv; + + CHECK_WINUSB_AVAILABLE; + +/* + usbi_dbg("testing detached device: disconnect NOW!"); + Sleep(2000); +*/ + + // TODO + if (iface != 0) { + usbi_dbg("mutliple interfaces not supported yet"); + return LIBUSB_ERROR_NOT_SUPPORTED; + } + + if ((handle_priv->interface_handle[iface] != 0) && (handle_priv->interface_handle[iface] != INVALID_HANDLE_VALUE)) { + return LIBUSB_ERROR_BUSY; + } + + if ((handle_priv->file_handle == 0) || (handle_priv->file_handle == INVALID_HANDLE_VALUE)) { + return LIBUSB_ERROR_NOT_FOUND; + } + + // The handle returned by WinUsb_Initialize is the first interface handle + if (!WinUsb_Initialize(handle_priv->file_handle, &handle_priv->interface_handle[0])) { + usbi_err(ctx, "could not claim interface 0: %s", windows_error_str()); + + switch(GetLastError()) { + case ERROR_BAD_COMMAND: // The device was disconnected + return LIBUSB_ERROR_NO_DEVICE; + default: + return LIBUSB_ERROR_ACCESS; } + + // TODO: check error for LIBUSB_ERROR_BUSY + handle_priv->interface_handle[0] = INVALID_HANDLE_VALUE; + return LIBUSB_ERROR_IO; } - if ((handle_priv->file_handle != 0) && (handle_priv->file_handle != INVALID_HANDLE_VALUE)) { - CloseHandle(handle_priv->file_handle); + // TODO: for other interface handles, use WinUsb_GetAssociatedInterface + + return LIBUSB_SUCCESS; +} + +static int winusb_release_interface(struct libusb_device_handle *dev_handle, int iface) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = (struct windows_device_handle_priv *)dev_handle->os_priv; + + CHECK_WINUSB_AVAILABLE; + + // TODO + if (iface != 0) { + usbi_dbg("mutliple interfaces not supported yet"); + return LIBUSB_ERROR_NOT_SUPPORTED; } + + if ((handle_priv->file_handle == 0) || (handle_priv->file_handle == INVALID_HANDLE_VALUE)) { + return LIBUSB_ERROR_NOT_FOUND; + } + + // libusb_open zeroes the priv struct, which is different from INVALID_HANDLE_VALUE (-1) + // However: http://www.tech-archive.net/Archive/Development/microsoft.public.win32.programmer.kernel/2005-05/msg00448.html + // "On any NT-derived platform 0 (zero) can never be a valid handle value", therefore we should be OK + if ((handle_priv->interface_handle[iface] != 0) && (handle_priv->interface_handle[iface] != INVALID_HANDLE_VALUE)) { + WinUsb_Free(handle_priv->interface_handle[iface]); + } else { + return LIBUSB_ERROR_NOT_FOUND; + } + + return LIBUSB_SUCCESS; } diff --git a/libusb/os/windows_usb.h b/libusb/os/windows_usb.h index 76b31d8..c070fa8 100644 --- a/libusb/os/windows_usb.h +++ b/libusb/os/windows_usb.h @@ -80,11 +80,13 @@ static inline void windows_hcd_priv_release(struct windows_hcd_priv* p) { safe_free(p->path); } + // Nodes (Hubs & devices) struct windows_device_priv { struct libusb_device *parent_dev; // access to parent is required for usermode ops ULONG connection_index; // also required for some usermode ops char *path; // path used by Windows to reference the USB node + char *driver; // driver name (eg WinUSB, USBSTOR, HidUsb, etc) uint8_t active_config; USB_DEVICE_DESCRIPTOR dev_descriptor; unsigned char **config_descriptor; // list of pointers to the cached config descriptors @@ -94,6 +96,7 @@ static inline void windows_device_priv_init(struct windows_device_priv* p) { p->parent_dev = NULL; p->connection_index = 0; p->path = NULL; + p->driver = NULL; p->active_config = 0; p->config_descriptor = NULL; memset(&(p->dev_descriptor), 0, sizeof(USB_DEVICE_DESCRIPTOR)); @@ -102,6 +105,7 @@ static inline void windows_device_priv_init(struct windows_device_priv* p) { static inline void windows_device_priv_release(struct windows_device_priv* p, int num_configurations) { int i; safe_free(p->path); + safe_free(p->driver); if ((num_configurations > 0) && (p->config_descriptor != NULL)) { for (i=0; i < num_configurations; i++) safe_free(p->config_descriptor[i]); @@ -118,7 +122,7 @@ typedef void *WINUSB_INTERFACE_HANDLE, *PWINUSB_INTERFACE_HANDLE; struct windows_device_handle_priv { bool is_open; HANDLE file_handle; - WINUSB_INTERFACE_HANDLE winusb_handle; + WINUSB_INTERFACE_HANDLE interface_handle[USB_MAXINTERFACES]; }; struct windows_transfer_priv { |