1
1
Fork 0
mirror of https://github.com/pbatard/rufus.git synced 2026-10-22 14:24:28 +00:00

Compare commits

...

2 commits

Author SHA1 Message Date
Pete Batard
714e3ad901
[misc] add MinGW UCRT64 builds
* And fix a warning in UCRT64 builds.
2026-10-15 15:12:49 +01:00
Pete Batard
f7e714f7b4
[core] actually report USB 3.2/USB 4.0 devices
* Thanks to the moronic people of the USB Forum (yes, moronic, as evidenced by their
  refusal to update their USB versioning like an intelligent and considerate life
  form and match a maximum USB speed to a x.y version), we have to use more ill-
  devised extensions and APIs, *THAT STILL ARE UNABLE TO PROPERLY REPORT THE MAXIMUM
  ACHIEVABLE SPEED OF A USB DEVICE*!
* But at least, we can detect the actual USB 3.1 (10 Gbps), USB 3.2 (20 Gbps), USB 4.0
  (40 Gbps) and USB 4.1 (80 Gbps) speeds used by the device (yes, I *AM* equating USB
  speed with USB version, LIKE A SANE PERSON WHO CARES ABOUT END USERS SHOULD!) and
  report that, through a mix of BOS analysis and use of the new (Windows 11 24H2)
  IOCTL_USB_GET_NODE_CONNECTION_SUPERSPEEDPLUS_INFORMATION API.
2026-10-15 14:55:48 +01:00
5 changed files with 212 additions and 27 deletions

View file

@ -35,6 +35,7 @@ jobs:
include:
- { sys: mingw64, env: x86_64, exe: rufus.exe }
- { sys: mingw32, env: i686, exe: rufus_x86.exe }
- { sys: ucrt64, env: ucrt-x86_64, exe: rufus_ucrt.exe }
defaults:
run:
@ -80,7 +81,7 @@ jobs:
- name: Build
run: |
./configure --disable-debug ${{ steps.set_alpha.outputs.option }} ${{ steps.set_beta.outputs.option }}
make -j4
make -j4 ACLOCAL=: AUTOMAKE=: AUTOCONF=: AUTOHEADER=:
mv ./src/rufus.exe ./${{ matrix.exe }}
strip ./${{ matrix.exe }}
upx --lzma --best ./${{ matrix.exe }}

161
src/dev.c
View file

@ -49,18 +49,83 @@ extern RUFUS_DRIVE rufus_drive[MAX_DRIVES];
extern BOOL enable_HDDs, enable_VHDs, use_fake_units, enable_vmdk, usb_debug;
extern BOOL list_non_usb_removable_drives, its_a_me_mario;
static inline uint64_t ssp_to_bps(uint32_t v)
{
static const uint64_t scale[4] = { 1ULL, 1000ULL, 1000000ULL, 1000000000ULL };
return (uint64_t)(v >> 16) * scale[(v >> 4) & 0x3];
}
static uint64_t get_max_bps(const uint8_t* bos, uint32_t len)
{
uint8_t l, min_rx_lanes, min_tx_lanes;
uint32_t i, n, ssp, offset, attr, avail;
uint64_t bps, max_bps = 0;
if (len < 5 || bos[0] < 5 || bos[1] != 0x0F)
return 0;
offset = bos[0]; // skip the BOS header
while (offset + 3 <= len) {
l = bos[offset];
if (l < 3 || offset + l > len)
break;
// Look for the SUPERSPEED_PLUS (0x0A) Device Capability descriptor (0x10)
if (bos[offset + 1] == 0x10 && bos[offset + 2] == 0x0A && l >= 12) {
// Get the bmAttributes field
memcpy(&attr, &bos[offset + 4], sizeof(attr));
// Get the min lanes from the wFunctionalitySupport field
min_rx_lanes = bos[offset + 9] & 0x0F;
min_tx_lanes = bos[offset + 9] >> 4;
n = (attr & 0x1F) + 1; // Number of Sublink Speed Attributes
avail = (l - 12) / 4;
if (n > avail)
n = avail;
for (i = 0; i < n; i++) {
memcpy(&ssp, &bos[offset + 12 + 4 * i], sizeof(ssp));
bps = ssp_to_bps(ssp) * ((ssp & 0x80) ? min_tx_lanes : min_rx_lanes);
if (bps > max_bps)
max_bps = bps;
}
}
// Next descriptor
offset += l;
}
return max_bps;
}
static uint32_t version_to_speed(uint16_t usb_version) {
switch (usb_version) {
case 0x0100: return USB_SPEED_LOW_1_5M;
case 0x0110: return USB_SPEED_FULL_12M;
case 0x0200:
case 0x0201:
case 0x0210: return USB_SPEED_HIGH_480M;
case 0x0300: return USB_SPEED_SUPER_5G;
case 0x0310: return USB_SPEED_SUPER_10G;
case 0x0320: return USB_SPEED_SUPER_20G;
case 0x0400: return USB_SPEED_SUPER_40G;
case 0x0410: return USB_SPEED_SUPER_80G;
default: return USB_SPEED_UNKNOWN;
}
}
/*
* Get the VID, PID and current device speed
*/
static BOOL GetUSBProperties(char* parent_path, char* device_id, usb_device_props* props)
{
int pass;
BOOL r = FALSE;
CONFIGRET cr;
HANDLE handle = INVALID_HANDLE_VALUE;
DWORD size;
DEVINST device_inst;
USB_NODE_CONNECTION_INFORMATION_EX conn_info;
USB_NODE_CONNECTION_INFORMATION_EX_V2 conn_info_v2;
MY_USB_NODE_CONNECTION_SUPERSPEEDPLUS_INFORMATION ssp;
uint8_t buf[sizeof(USB_DESCRIPTOR_REQUEST) + 512] = { 0 };
uint8_t* bos = &buf[sizeof(USB_DESCRIPTOR_REQUEST)];
USB_DESCRIPTOR_REQUEST* req = (USB_DESCRIPTOR_REQUEST*)buf;
DWORD size, header_size = offsetof(USB_DESCRIPTOR_REQUEST, Data);
if ((parent_path == NULL) || (device_id == NULL) || (props == NULL))
goto out;
@ -99,26 +164,92 @@ static BOOL GetUSBProperties(char* parent_path, char* device_id, usb_device_prop
if ((conn_info.DeviceDescriptor.idVendor != 0) || (conn_info.DeviceDescriptor.idProduct != 0)) {
props->vid = conn_info.DeviceDescriptor.idVendor;
props->pid = conn_info.DeviceDescriptor.idProduct;
props->speed = conn_info.Speed + 1;
// Not exactly accurate since a USB 3.x device connected to a USB 2.0 port will use the
// 2.0 lanes and report a 2.0 device descriptor there, which means that, despite being
// able to operate at 3.0 speeds, we only see a max speed of 2.0. Our additional checks
// below should however be able to take care of detecting the max speed.
props->max_speed = version_to_speed(conn_info.DeviceDescriptor.bcdUSB);
props->current_speed = conn_info.Speed + 1;
r = TRUE;
}
// The USB speed report of modern Windows is a complete mess
size = sizeof(ssp);
memset(&ssp, 0, size);
ssp.ConnectionIndex = (ULONG)props->port;
ssp.Length = size;
size = sizeof(conn_info_v2);
memset(&conn_info_v2, 0, size);
conn_info_v2.ConnectionIndex = (ULONG)props->port;
conn_info_v2.Length = size;
conn_info_v2.SupportedUsbProtocols.Usb300 = 1;
// Requires Windows 11 24H2 or later
if (DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_SUPERSPEEDPLUS_INFORMATION, &ssp, sizeof(ssp), &ssp, sizeof(ssp), &size, NULL)) {
uint64_t cur_bps = max(ssp_to_bps(ssp.RxSuperSpeedPlus) * (ssp.RxLaneCount + 1), ssp_to_bps(ssp.TxSuperSpeedPlus) * (ssp.TxLaneCount + 1));
if (cur_bps >= 80000000000ULL)
props->current_speed = USB_SPEED_SUPER_80G;
else if (cur_bps >= 40000000000ULL)
props->current_speed = USB_SPEED_SUPER_40G;
else if (cur_bps >= 20000000000ULL)
props->current_speed = USB_SPEED_SUPER_20G;
else if (cur_bps >= 10000000000ULL)
props->current_speed = USB_SPEED_SUPER_10G;
else if (cur_bps >= 5000000000ULL)
props->current_speed = USB_SPEED_SUPER_5G;
else
goto next;
props->max_speed = props->current_speed;
// Read the BOS to compare with the advertised speed
size = 5;
for (pass = 0; pass < 2; pass++) {
req->ConnectionIndex = (ULONG)props->port;
req->SetupPacket.bmRequest = 0x80;
req->SetupPacket.bRequest = 0x06;
req->SetupPacket.wValue = (0x0F << 8);
req->SetupPacket.wIndex = 0;
req->SetupPacket.wLength = (USHORT)size;
if (!DeviceIoControl(handle, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, req, header_size + size, req, header_size + size, &size, NULL) ||
size < header_size + 5)
break;
if (pass == 0) {
size = req->Data[2] | (req->Data[3] << 8);
if (size < 5 || size > 512)
break;
}
}
if (pass == 2) {
// Now the problem is that the max bps from the BOS is for *single* lane only, and the
// USB Forum *STUPIDLY* defined a minimum amount of lanes in the BOS but NOT a maximum
// or a preferred, which means we can not actually get the max speed of a USB 3.2 (2 x
// 10G lanes) if it's operating at USB 3.1 speed (1 x 10G lane). Somebody *REALLY*
// doesn't want users to find out if they are being screwed on USB speeds...
uint64_t max_bps = get_max_bps(bos, size);
if (max_bps >= 80000000000ULL)
props->max_speed = USB_SPEED_SUPER_80G;
else if (max_bps >= 40000000000ULL)
props->max_speed = USB_SPEED_SUPER_40G;
else if (max_bps >= 20000000000ULL)
props->max_speed = USB_SPEED_SUPER_20G;
else if (max_bps >= 10000000000ULL)
props->max_speed = USB_SPEED_SUPER_10G;
else if (max_bps >= 5000000000ULL)
props->max_speed = USB_SPEED_SUPER_5G;
}
goto out;
}
next:
if (!DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2, &conn_info_v2, size, &conn_info_v2, size, &size, NULL)) {
uprintf("Could not get node connection information (V2) for device '%s': %s", device_id, WindowsErrorString());
} else if (conn_info_v2.Flags.DeviceIsOperatingAtSuperSpeedPlusOrHigher) {
props->speed = USB_SPEED_SUPER_PLUS;
props->current_speed = USB_SPEED_SUPER_10G;
} else if (conn_info_v2.Flags.DeviceIsOperatingAtSuperSpeedOrHigher) {
props->speed = USB_SPEED_SUPER;
props->current_speed = USB_SPEED_SUPER_5G;
} else if (conn_info_v2.Flags.DeviceIsSuperSpeedPlusCapableOrHigher) {
props->lower_speed = 2;
props->max_speed = USB_SPEED_SUPER_10G;
} else if (conn_info_v2.Flags.DeviceIsSuperSpeedCapableOrHigher) {
props->lower_speed = 1;
props->max_speed = USB_SPEED_SUPER_5G;
}
out:
@ -471,7 +602,12 @@ BOOL GetDevices(DWORD devnum)
const char* scsi_card_name[] = {
"_SD_", "_SDHC_", "_SDXC_", "_MMC_", "_MS_", "_MSPro_", "_xDPicture_", "_O2Media_"
};
const char* usb_speed_name[USB_SPEED_MAX] = { "USB", "USB 1.0", "USB 1.1", "USB 2.0", "USB 3.0", "USB 3.1" };
const char* usb_version[USB_SPEED_MAX] = {
"USB", "USB 1.0", "USB 1.1", "USB 2.1", "USB 3.0", "USB 3.1", "USB 3.2", "USB 4.0", "USB 4.1"
}; // See, USB Forum, it *REALLY* isn't that hard!!!
const char* usb_speed[USB_SPEED_MAX] = {
"???", "1.5 Mbps", "12 Mbps", "480 Mbps", "5 Gbps", "10 Gbps", "20 Gbps", "40 Gbps", "80 Gbps"
};
const char* windows_sandbox_vhd_label = "PortableBaseLayer";
// Hash table and String Array used to match a Device ID with the parent hub's Device Interface Path
htab_table htab_devid = HTAB_EMPTY;
@ -847,12 +983,13 @@ BOOL GetDevices(DWORD devnum)
if (s < ARRAYSIZE(ignore_vid_pid))
continue;
}
if (props.speed >= USB_SPEED_MAX)
props.speed = 0;
if (props.current_speed >= USB_SPEED_MAX)
props.current_speed = 0;
uprintf("Found %s%s%s device '%s' (%s) %s", props.is_UASP ? "UAS (" : "",
usb_speed_name[props.speed], props.is_UASP ? ")" : "", buffer, str, method_str);
if (props.lower_speed)
uprintf("NOTE: This device is a USB 3.%c device operating at lower speed...", '0' + props.lower_speed - 1);
usb_version[props.current_speed], props.is_UASP ? ")" : "", buffer, str, method_str);
if (props.max_speed > props.current_speed)
uprintf("NOTE: This a %s (%s) device operating at %s speed (%s)...", usb_version[props.max_speed],
usb_speed[props.max_speed], usb_version[props.current_speed], usb_speed[props.current_speed]);
}
devint_data.cbSize = sizeof(devint_data);
devint_detail_data = NULL;

View file

@ -21,19 +21,22 @@
#include <cfgmgr32.h>
#define USB_SPEED_UNKNOWN 0
#define USB_SPEED_LOW 1
#define USB_SPEED_FULL 2
#define USB_SPEED_HIGH 3
#define USB_SPEED_SUPER 4
#define USB_SPEED_SUPER_PLUS 5
#define USB_SPEED_MAX 6
#define USB_SPEED_LOW_1_5M 1 // USB 1.0
#define USB_SPEED_FULL_12M 2 // USB 1.1
#define USB_SPEED_HIGH_480M 3 // USB 2.0
#define USB_SPEED_SUPER_5G 4 // USB 3.0
#define USB_SPEED_SUPER_10G 5 // USB 3.1
#define USB_SPEED_SUPER_20G 6 // USB 3.2
#define USB_SPEED_SUPER_40G 7 // USB 4.0
#define USB_SPEED_SUPER_80G 8 // USB 4.1
#define USB_SPEED_MAX 9
/* List of the properties we are interested in */
typedef struct usb_device_props {
int32_t vid;
int32_t pid;
uint32_t speed;
uint32_t lower_speed;
uint32_t current_speed;
uint32_t max_speed;
uint32_t port;
BOOLEAN is_USB;
BOOLEAN is_SCSI;
@ -145,3 +148,47 @@ const GUID GUID_DEVINTERFACE_USB_HUB =
{ 0xf18a0e88L, 0xc30c, 0x11d0, {0x88, 0x15, 0x00, 0xa0, 0xc9, 0x06, 0xbe, 0xd8} };
#define DEVID_HTAB_SIZE 257
#ifndef FILE_DEVICE_USB
#define FILE_DEVICE_USB FILE_DEVICE_UNKNOWN
#endif
typedef struct _USB_DESCRIPTOR_REQUEST {
ULONG ConnectionIndex;
struct {
UCHAR bmRequest;
UCHAR bRequest;
USHORT wValue;
USHORT wIndex;
USHORT wLength;
} SetupPacket;
UCHAR Data[0];
} USB_DESCRIPTOR_REQUEST, * PUSB_DESCRIPTOR_REQUEST;
#ifndef USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION
#define USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION 260
#endif
#ifndef IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION
#define IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION CTL_CODE \
(FILE_DEVICE_USB, USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, METHOD_BUFFERED, FILE_ANY_ACCESS)
#endif
#ifndef USB_GET_NODE_CONNECTION_SUPERSPEEDPLUS_INFORMATION
#define USB_GET_NODE_CONNECTION_SUPERSPEEDPLUS_INFORMATION 289
#endif
#ifndef IOCTL_USB_GET_NODE_CONNECTION_SUPERSPEEDPLUS_INFORMATION
#define IOCTL_USB_GET_NODE_CONNECTION_SUPERSPEEDPLUS_INFORMATION CTL_CODE\
(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_SUPERSPEEDPLUS_INFORMATION, METHOD_BUFFERED, FILE_ANY_ACCESS)
#endif
typedef struct {
ULONG ConnectionIndex;
ULONG Length;
ULONG RxSuperSpeedPlus;
ULONG RxLaneCount;
ULONG TxSuperSpeedPlus;
ULONG TxLaneCount;
} MY_USB_NODE_CONNECTION_SUPERSPEEDPLUS_INFORMATION;

View file

@ -277,7 +277,7 @@ static PCSTR _NormalizeDeviceName(IN PCSTR Device, IN PSTR NormalizedDeviceNameB
return Device;
// Handle custom paths of the form "<Physical> <Offset> <Size>" used by Rufus to
// enable multi-partition access on removable devices, for pre 1703 platforms.
if (sscanf(Device, "%s %I64u %I64u", NormalizedDeviceNameBuffer, Offset, Size) < 1)
if (sscanf(Device, "%s %llu %llu", NormalizedDeviceNameBuffer, Offset, Size) < 1)
return NULL;
if ((NormalizedDeviceNameBuffer[1] == '\\') || (NormalizedDeviceNameBuffer[1] == '.'))
NormalizedDeviceNameBuffer[1] = '?';

View file

@ -33,7 +33,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
IDD_DIALOG DIALOGEX 12, 12, 232, 326
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_ACCEPTFILES
CAPTION "Rufus 4.15.2409"
CAPTION "Rufus 4.15.2411"
FONT 9, "Segoe UI Symbol", 400, 0, 0x0
BEGIN
LTEXT "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP
@ -409,8 +409,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 4,15,2409,0
PRODUCTVERSION 4,15,2409,0
FILEVERSION 4,15,2411,0
PRODUCTVERSION 4,15,2411,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -428,13 +428,13 @@ BEGIN
VALUE "Comments", "https://rufus.ie"
VALUE "CompanyName", "Akeo Consulting"
VALUE "FileDescription", "Rufus"
VALUE "FileVersion", "4.15.2409"
VALUE "FileVersion", "4.15.2411"
VALUE "InternalName", "Rufus"
VALUE "LegalCopyright", "© 2011-2026 Pete Batard (GPL v3)"
VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html"
VALUE "OriginalFilename", "rufus-4.15.exe"
VALUE "ProductName", "Rufus"
VALUE "ProductVersion", "4.15.2409"
VALUE "ProductVersion", "4.15.2411"
END
END
BLOCK "VarFileInfo"