summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPete Batard <pete@akeo.ie>2014-05-18 20:18:29 +0100
committerPete Batard <pete@akeo.ie>2014-05-18 20:19:03 +0100
commitd41802053c4f20691f38072879c9dd76806f0f91 (patch)
tree6131c33fe00fa7821bb41f0fe64e8608480343c4
parentc0f5e58acb316010015e28104c425cda7e8e1c78 (diff)
downloadlibusb-d41802053c4f20691f38072879c9dd76806f0f91.tar.gz
windows: fix USB 3.0 speed detection on Windows 8 or later
* ...since Microsoft broke IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX between Windows 7 and Windows 8 * Also improve Windows version detection and reporting * Closes #10
-rw-r--r--libusb/os/poll_windows.h18
-rw-r--r--libusb/os/wince_usb.c2
-rw-r--r--libusb/os/windows_usb.c149
-rw-r--r--libusb/os/windows_usb.h35
-rw-r--r--libusb/version_nano.h2
5 files changed, 183 insertions, 23 deletions
diff --git a/libusb/os/poll_windows.h b/libusb/os/poll_windows.h
index deed206..aa4c985 100644
--- a/libusb/os/poll_windows.h
+++ b/libusb/os/poll_windows.h
@@ -40,14 +40,20 @@
#define DUMMY_HANDLE ((HANDLE)(LONG_PTR)-2)
+/* Windows versions */
enum windows_version {
- WINDOWS_UNSUPPORTED,
- WINDOWS_CE,
- WINDOWS_XP,
- WINDOWS_2003, // also includes XP 64
- WINDOWS_VISTA_AND_LATER,
+ WINDOWS_CE = -2,
+ WINDOWS_UNDEFINED = -1,
+ WINDOWS_UNSUPPORTED = 0,
+ WINDOWS_XP = 0x51,
+ WINDOWS_2003 = 0x52, // Also XP x64
+ WINDOWS_VISTA = 0x60,
+ WINDOWS_7 = 0x61,
+ WINDOWS_8 = 0x62,
+ WINDOWS_8_1_OR_LATER = 0x63,
+ WINDOWS_MAX
};
-extern enum windows_version windows_version;
+extern int windows_version;
#define MAX_FDS 256
diff --git a/libusb/os/wince_usb.c b/libusb/os/wince_usb.c
index 98304e7..4d9b3cc 100644
--- a/libusb/os/wince_usb.c
+++ b/libusb/os/wince_usb.c
@@ -38,7 +38,7 @@ unsigned __stdcall wince_clock_gettime_threaded(void* param);
uint64_t hires_frequency, hires_ticks_to_ps;
int errno;
const uint64_t epoch_time = UINT64_C(116444736000000000); // 1970.01.01 00:00:000 in MS Filetime
-enum windows_version windows_version = WINDOWS_CE;
+int windows_version = WINDOWS_CE;
static int concurrent_usage = -1;
// Timer thread
// NB: index 0 is for monotonic and 1 is for the thread exit event
diff --git a/libusb/os/windows_usb.c b/libusb/os/windows_usb.c
index 488ea7b..19b2542 100644
--- a/libusb/os/windows_usb.c
+++ b/libusb/os/windows_usb.c
@@ -100,7 +100,8 @@ static int composite_copy_transfer_data(int sub_api, struct usbi_transfer *itran
// Global variables
uint64_t hires_frequency, hires_ticks_to_ps;
const uint64_t epoch_time = UINT64_C(116444736000000000); // 1970.01.01 00:00:000 in MS Filetime
-enum windows_version windows_version = WINDOWS_UNSUPPORTED;
+int windows_version = WINDOWS_UNDEFINED;
+static char windows_version_str[128] = "Windows Undefined";
// Concurrency
static int concurrent_usage = -1;
usbi_mutex_t autoclaim_lock;
@@ -804,6 +805,118 @@ static void auto_release(struct usbi_transfer *itransfer)
usbi_mutex_unlock(&autoclaim_lock);
}
+/* Windows version dtection */
+static BOOL is_x64(void)
+{
+ BOOL ret = FALSE;
+ // Detect if we're running a 32 or 64 bit system
+ if (sizeof(uintptr_t) < 8) {
+ DLL_LOAD_PREFIXED(Kernel32.dll, p, IsWow64Process, FALSE);
+ if (pIsWow64Process != NULL) {
+ (*pIsWow64Process)(GetCurrentProcess(), &ret);
+ }
+ } else {
+ ret = TRUE;
+ }
+ return ret;
+}
+
+static void get_windows_version(void)
+{
+ OSVERSIONINFOEXA vi, vi2;
+ const char* w = 0;
+ const char* w64 = "32 bit";
+ char* vptr;
+ size_t vlen;
+ unsigned major, minor;
+ ULONGLONG major_equal, minor_equal;
+ BOOL ws;
+
+ memset(&vi, 0, sizeof(vi));
+ vi.dwOSVersionInfoSize = sizeof(vi);
+ if (!GetVersionExA((OSVERSIONINFOA *)&vi)) {
+ memset(&vi, 0, sizeof(vi));
+ vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
+ if (!GetVersionExA((OSVERSIONINFOA *)&vi))
+ return;
+ }
+
+ if (vi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
+
+ if (vi.dwMajorVersion > 6 || (vi.dwMajorVersion == 6 && vi.dwMinorVersion >= 2)) {
+ // Starting with Windows 8.1 Preview, GetVersionEx() does no longer report the actual OS version
+ // See: http://msdn.microsoft.com/en-us/library/windows/desktop/dn302074.aspx
+
+ major_equal = VerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL);
+ for (major = vi.dwMajorVersion; major <= 9; major++) {
+ memset(&vi2, 0, sizeof(vi2));
+ vi2.dwOSVersionInfoSize = sizeof(vi2); vi2.dwMajorVersion = major;
+ if (!VerifyVersionInfoA(&vi2, VER_MAJORVERSION, major_equal))
+ continue;
+ if (vi.dwMajorVersion < major) {
+ vi.dwMajorVersion = major; vi.dwMinorVersion = 0;
+ }
+
+ minor_equal = VerSetConditionMask(0, VER_MINORVERSION, VER_EQUAL);
+ for (minor = vi.dwMinorVersion; minor <= 9; minor++) {
+ memset(&vi2, 0, sizeof(vi2)); vi2.dwOSVersionInfoSize = sizeof(vi2);
+ vi2.dwMinorVersion = minor;
+ if (!VerifyVersionInfoA(&vi2, VER_MINORVERSION, minor_equal))
+ continue;
+ vi.dwMinorVersion = minor;
+ break;
+ }
+
+ break;
+ }
+ }
+
+ if (vi.dwMajorVersion <= 0xf && vi.dwMinorVersion <= 0xf) {
+ ws = (vi.wProductType <= VER_NT_WORKSTATION);
+ windows_version = vi.dwMajorVersion << 4 | vi.dwMinorVersion;
+ switch (windows_version) {
+ case 0x50: w = "2000";
+ break;
+ case 0x51: w = "XP";
+ break;
+ case 0x52: w = (!GetSystemMetrics(89)?"2003":"2003_R2");
+ break;
+ case 0x60: w = (ws?"Vista":"2008");
+ break;
+ case 0x61: w = (ws?"7":"2008_R2");
+ break;
+ case 0x62: w = (ws?"8":"2012");
+ break;
+ case 0x63: w = (ws?"8.1":"2012_R2");
+ break;
+ case 0x64: w = (ws?"8.2":"2012_R3");
+ break;
+ default:
+ if (windows_version < 0x50)
+ windows_version = WINDOWS_UNSUPPORTED;
+ else
+ w = "9 or later";
+ break;
+ }
+ }
+ }
+
+ if (is_x64())
+ w64 = "64-bit";
+
+ vptr = &windows_version_str[sizeof("Windows ") - 1];
+ vlen = sizeof(windows_version_str) - sizeof("Windows ") - 1;
+ if (!w)
+ safe_sprintf(vptr, vlen, "%s %u.%u %s", (vi.dwPlatformId==VER_PLATFORM_WIN32_NT?"NT":"??"),
+ (unsigned)vi.dwMajorVersion, (unsigned)vi.dwMinorVersion, w64);
+ else if (vi.wServicePackMinor)
+ safe_sprintf(vptr, vlen, "%s SP%u.%u %s", w, vi.wServicePackMajor, vi.wServicePackMinor, w64);
+ else if (vi.wServicePackMajor)
+ safe_sprintf(vptr, vlen, "%s SP%u %s", w, vi.wServicePackMajor, w64);
+ else
+ safe_sprintf(vptr, vlen, "%s %s", w, w64);
+}
+
/*
* init: libusb backend init function
*
@@ -814,7 +927,6 @@ static void auto_release(struct usbi_transfer *itransfer)
static int windows_init(struct libusb_context *ctx)
{
int i, r = LIBUSB_ERROR_OTHER;
- OSVERSIONINFO os_version;
HANDLE semaphore;
char sem_name[11+1+8]; // strlen(libusb_init)+'\0'+(32-bit hex PID)
@@ -836,19 +948,8 @@ static int windows_init(struct libusb_context *ctx)
// NB: concurrent usage supposes that init calls are equally balanced with
// exit calls. If init is called more than exit, we will not exit properly
if ( ++concurrent_usage == 0 ) { // First init?
- // Detect OS version
- memset(&os_version, 0, sizeof(OSVERSIONINFO));
- os_version.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
- windows_version = WINDOWS_UNSUPPORTED;
- if ((GetVersionEx(&os_version) != 0) && (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT)) {
- if ((os_version.dwMajorVersion == 5) && (os_version.dwMinorVersion == 1)) {
- windows_version = WINDOWS_XP;
- } else if ((os_version.dwMajorVersion == 5) && (os_version.dwMinorVersion == 2)) {
- windows_version = WINDOWS_2003; // also includes XP 64
- } else if (os_version.dwMajorVersion >= 6) {
- windows_version = WINDOWS_VISTA_AND_LATER;
- }
- }
+ get_windows_version();
+ usbi_dbg(windows_version_str);
if (windows_version == WINDOWS_UNSUPPORTED) {
usbi_err(ctx, "This version of Windows is NOT supported");
r = LIBUSB_ERROR_NOT_SUPPORTED;
@@ -1094,6 +1195,7 @@ static int init_device(struct libusb_device* dev, struct libusb_device* parent_d
HANDLE handle;
DWORD size;
USB_NODE_CONNECTION_INFORMATION_EX conn_info;
+ USB_NODE_CONNECTION_INFORMATION_EX_V2 conn_info_v2;
struct windows_device_priv *priv, *parent_priv;
struct libusb_context *ctx;
struct libusb_device* tmp_dev;
@@ -1172,6 +1274,23 @@ static int init_device(struct libusb_device* dev, struct libusb_device* parent_d
dev->num_configurations = 0;
priv->dev_descriptor.bNumConfigurations = 0;
}
+
+ // In their great wisdom, Microsoft decided to BREAK the USB speed report between Windows 7 and Windows 8
+ if (windows_version >= WINDOWS_8) {
+ memset(&conn_info_v2, 0, sizeof(conn_info_v2));
+ size = sizeof(conn_info_v2);
+ conn_info_v2.ConnectionIndex = (ULONG)port_number;
+ conn_info_v2.Length = size;
+ conn_info_v2.SupportedUsbProtocols.Usb300 = 1;
+ if (!DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2,
+ &conn_info_v2, size, &conn_info_v2, size, &size, NULL)) {
+ usbi_warn(ctx, "could not get node connection information (V2) for device '%s': %s",
+ device_id, windows_error_str(0));
+ } else if (conn_info_v2.Flags.DeviceIsOperatingAtSuperSpeedOrHigher) {
+ conn_info.Speed = 3;
+ }
+ }
+
safe_closehandle(handle);
if (conn_info.DeviceAddress > UINT8_MAX) {
diff --git a/libusb/os/windows_usb.h b/libusb/os/windows_usb.h
index 069f3fc..413adfc 100644
--- a/libusb/os/windows_usb.h
+++ b/libusb/os/windows_usb.h
@@ -310,6 +310,9 @@ struct driver_lookup {
/* OLE32 dependency */
DLL_DECLARE_PREFIXED(WINAPI, HRESULT, p, CLSIDFromString, (LPCOLESTR, LPCLSID));
+/* This call is only available from XP SP2 */
+DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, IsWow64Process, (HANDLE, PBOOL));
+
/* SetupAPI dependencies */
DLL_DECLARE_PREFIXED(WINAPI, HDEVINFO, p, SetupDiGetClassDevsA, (const GUID*, PCSTR, HWND, DWORD));
DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, SetupDiEnumDeviceInfo, (HDEVINFO, DWORD, PSP_DEVINFO_DATA));
@@ -364,6 +367,9 @@ typedef RETURN_TYPE CONFIGRET;
#if !defined(USB_GET_HUB_CAPABILITIES_EX)
#define USB_GET_HUB_CAPABILITIES_EX 276
#endif
+#if !defined(USB_GET_NODE_CONNECTION_INFORMATION_EX_V2)
+#define USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 279
+#endif
#ifndef METHOD_BUFFERED
#define METHOD_BUFFERED 0
@@ -424,6 +430,9 @@ DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Device_IDA, (DEVINST, PCHAR, ULONG, ULONG)
#define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX \
CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_INFORMATION_EX, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 \
+ CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_INFORMATION_EX_V2, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
#define IOCTL_USB_GET_NODE_CONNECTION_ATTRIBUTES \
CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_ATTRIBUTES, METHOD_BUFFERED, FILE_ANY_ACCESS)
@@ -564,6 +573,32 @@ typedef struct USB_NODE_CONNECTION_INFORMATION_EX {
// USB_PIPE_INFO PipeList[0];
} USB_NODE_CONNECTION_INFORMATION_EX, *PUSB_NODE_CONNECTION_INFORMATION_EX;
+typedef union _USB_PROTOCOLS {
+ ULONG ul;
+ struct {
+ ULONG Usb110:1;
+ ULONG Usb200:1;
+ ULONG Usb300:1;
+ ULONG ReservedMBZ:29;
+ };
+} USB_PROTOCOLS, *PUSB_PROTOCOLS;
+
+typedef union _USB_NODE_CONNECTION_INFORMATION_EX_V2_FLAGS {
+ ULONG ul;
+ struct {
+ ULONG DeviceIsOperatingAtSuperSpeedOrHigher:1;
+ ULONG DeviceIsSuperSpeedCapableOrHigher:1;
+ ULONG ReservedMBZ:30;
+ };
+} USB_NODE_CONNECTION_INFORMATION_EX_V2_FLAGS, *PUSB_NODE_CONNECTION_INFORMATION_EX_V2_FLAGS;
+
+typedef struct _USB_NODE_CONNECTION_INFORMATION_EX_V2 {
+ ULONG ConnectionIndex;
+ ULONG Length;
+ USB_PROTOCOLS SupportedUsbProtocols;
+ USB_NODE_CONNECTION_INFORMATION_EX_V2_FLAGS Flags;
+} USB_NODE_CONNECTION_INFORMATION_EX_V2, *PUSB_NODE_CONNECTION_INFORMATION_EX_V2;
+
typedef struct USB_HUB_CAP_FLAGS {
ULONG HubIsHighSpeedCapable:1;
ULONG HubIsHighSpeed:1;
diff --git a/libusb/version_nano.h b/libusb/version_nano.h
index 94ac72a..070de37 100644
--- a/libusb/version_nano.h
+++ b/libusb/version_nano.h
@@ -1 +1 @@
-#define LIBUSB_NANO 10889
+#define LIBUSB_NANO 10890