diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml index d59adfd5..ee7ae8d6 100644 --- a/.github/workflows/mingw.yml +++ b/.github/workflows/mingw.yml @@ -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 }} diff --git a/src/dev.c b/src/dev.c index b1f1cdf7..7ee2116e 100644 --- a/src/dev.c +++ b/src/dev.c @@ -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; diff --git a/src/dev.h b/src/dev.h index d0543d13..98710860 100644 --- a/src/dev.h +++ b/src/dev.h @@ -21,19 +21,22 @@ #include #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; + diff --git a/src/ext2fs/nt_io.c b/src/ext2fs/nt_io.c index f36dc3b2..81165e6f 100644 --- a/src/ext2fs/nt_io.c +++ b/src/ext2fs/nt_io.c @@ -277,7 +277,7 @@ static PCSTR _NormalizeDeviceName(IN PCSTR Device, IN PSTR NormalizedDeviceNameB return Device; // Handle custom paths of the form " " 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] = '?'; diff --git a/src/rufus.rc b/src/rufus.rc index 27ac0fb0..e82dbf12 100644 --- a/src/rufus.rc +++ b/src/rufus.rc @@ -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"