From: Pete Batard Date: Sun, 18 May 2014 19:18:29 +0000 (+0100) Subject: windows: fix USB 3.0 speed detection on Windows 8 or later X-Git-Tag: upstream/1.0.21~265 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=d41802053c4f20691f38072879c9dd76806f0f91;p=platform%2Fupstream%2Flibusb.git 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 --- 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