mirror of
https://github.com/shadps4-emu/ext-libusb.git
synced 2026-01-31 00:55:21 +01:00
windows: Allow device GUIDs missing terminating characters or separators
Allow REG_MULTI_SZ strings with multiple GUIDs, and use the first GUID for enumerating the device interfaces. This solves issues with e.g. a composite device with four WinUSB interfaces, where the device implements "Extended Properties OS Feature Descriptor" with multiple GUIDs. An attempt to open the device fails with an error LIBUSB_ERROR_NOT_FOUND (-5). Fixes #1307 Signed-off-by: Tormod Volden <debian.tormod@gmail.com>
This commit is contained in:
committed by
Tormod Volden
parent
d66ffcdd12
commit
fdab67b14a
@@ -1450,6 +1450,137 @@ static int set_hid_interface(struct libusb_context *ctx, struct libusb_device *d
|
||||
return LIBUSB_SUCCESS;
|
||||
}
|
||||
|
||||
// get the n-th device interface GUID indexed by guid_number
|
||||
static int get_guid(struct libusb_context *ctx, char *dev_id, HDEVINFO *dev_info, SP_DEVINFO_DATA *dev_info_data,
|
||||
int guid_number, GUID **if_guid)
|
||||
{
|
||||
DWORD size, reg_type;
|
||||
HKEY key;
|
||||
char *guid_string, *new_guid_string;
|
||||
char *guid, *guid_term;
|
||||
LONG s;
|
||||
int pass, guids_left;
|
||||
int err = LIBUSB_SUCCESS;
|
||||
|
||||
key = pSetupDiOpenDevRegKey(*dev_info, dev_info_data, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
|
||||
if (key == INVALID_HANDLE_VALUE) {
|
||||
usbi_warn(ctx, "Cannot get the additional GUIDs for '%s'", dev_id);
|
||||
return LIBUSB_ERROR_ACCESS;
|
||||
}
|
||||
// Reserve buffer large enough to hold one GUID with two terminating characters
|
||||
size = MAX_GUID_STRING_LENGTH + 1;
|
||||
// Allocate memory for storing the guid_string with two extra terminating characters
|
||||
// This is necessary for parsing the REG_MULTI_SZ type below
|
||||
guid_string = malloc(size + 2);
|
||||
if (guid_string == NULL) {
|
||||
usbi_err(ctx, "failed to alloc guid_string");
|
||||
return LIBUSB_ERROR_NO_MEM;
|
||||
}
|
||||
|
||||
// The 1st pass tries to get the guid. If it fails due to ERROR_MORE_DATA
|
||||
// then reallocate enough memory for the 2nd pass
|
||||
for (pass = 0; pass < 2; pass++) {
|
||||
// Look for both DeviceInterfaceGUIDs *and* DeviceInterfaceGUID, in that order
|
||||
// If multiple GUIDs, find the n-th that is indexed by guid_number
|
||||
s = pRegQueryValueExA(key, "DeviceInterfaceGUIDs", NULL, ®_type,
|
||||
(LPBYTE)guid_string, &size);
|
||||
if (s == ERROR_FILE_NOT_FOUND)
|
||||
s = pRegQueryValueExA(key, "DeviceInterfaceGUID", NULL, ®_type,
|
||||
(LPBYTE)guid_string, &size);
|
||||
if (s == ERROR_SUCCESS) {
|
||||
// The GUID was read successfully
|
||||
break;
|
||||
} else if (s == ERROR_FILE_NOT_FOUND) {
|
||||
usbi_warn(ctx, "no DeviceInterfaceGUID registered for '%s'", dev_id);
|
||||
err = LIBUSB_ERROR_ACCESS;
|
||||
goto exit;
|
||||
} else if (s == ERROR_MORE_DATA) {
|
||||
if (pass == 1) {
|
||||
// Previous pass should have allocated enough memory, but reading failed
|
||||
usbi_warn(ctx, "unexpected error from pRegQueryValueExA for '%s'", dev_id);
|
||||
err = LIBUSB_ERROR_OTHER;
|
||||
goto exit;
|
||||
}
|
||||
new_guid_string = realloc((void *)guid_string, size + 2);
|
||||
if (new_guid_string == NULL) {
|
||||
usbi_err(ctx, "failed to realloc guid string");
|
||||
err = LIBUSB_ERROR_NO_MEM;
|
||||
goto exit;
|
||||
}
|
||||
guid_string = new_guid_string;
|
||||
} else {
|
||||
usbi_warn(ctx, "unexpected error from pRegQueryValueExA for '%s'", dev_id);
|
||||
err = LIBUSB_ERROR_ACCESS;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regqueryvalueexa#remarks
|
||||
// - "string may not have been stored with the proper terminating null characters"
|
||||
// - The following GUIDs should be consider as valid:
|
||||
// "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}\0", "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}",
|
||||
// "{xxx.....xx}\0\0\0", "{xxx.....xx}\0{xxx.....xx}\0{xxx.....xx}\0",
|
||||
// "{xxx.....xx}\0{xxx.....xx}\0{xxx.....xx}", "{xxx.....xx}{xxx.....xx}{xxx.....xx}",
|
||||
// "{xxx.....xx}\0{xxx.....xx}\0{xxx.....xx}\0\0\0\0"
|
||||
if ((reg_type == REG_SZ ) || (reg_type == REG_MULTI_SZ)) {
|
||||
/* Get the n-th GUID indexed by guid_number since the DeviceInterfaceGUIDs may
|
||||
contain more GUIDs */
|
||||
guid = guid_string;
|
||||
// Add two terminating chars for not overrunning the allocated memory while iterating
|
||||
guid[size] = '\0';
|
||||
guid[size + 1] = '\0';
|
||||
// Iterate the GUIDs in the guid string
|
||||
guids_left = guid_number;
|
||||
while (guids_left) {
|
||||
guid = strchr(guid, '}');
|
||||
if (guid == NULL) {
|
||||
usbi_warn(ctx, "no GUID with index %d registered for '%s'", guid_number, dev_id);
|
||||
err = LIBUSB_ERROR_ACCESS;
|
||||
goto exit;
|
||||
}
|
||||
guid++;
|
||||
// Skip the terminating char if available
|
||||
if (*guid == '\0') {
|
||||
guid++;
|
||||
}
|
||||
guids_left--;
|
||||
}
|
||||
// Add terminating char to the string
|
||||
guid_term = strchr(guid, '}');
|
||||
if (guid_term == NULL) {
|
||||
usbi_warn(ctx, "no GUID with index %d registered for '%s'", guid_number, dev_id);
|
||||
err = LIBUSB_ERROR_ACCESS;
|
||||
goto exit;
|
||||
}
|
||||
// Terminate the current guid string to handle the variant without separators
|
||||
guid_term++;
|
||||
*guid_term = '\0';
|
||||
} else {
|
||||
usbi_warn(ctx, "unexpected type of DeviceInterfaceGUID for '%s'", dev_id);
|
||||
err = LIBUSB_ERROR_ACCESS;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
*if_guid = malloc(sizeof(GUID));
|
||||
if (*if_guid == NULL) {
|
||||
usbi_err(ctx, "failed to alloc if_guid");
|
||||
err = LIBUSB_ERROR_NO_MEM;
|
||||
goto exit;
|
||||
}
|
||||
if (!string_to_guid(guid, *if_guid)) {
|
||||
usbi_warn(ctx, "device '%s' has malformed DeviceInterfaceGUID string '%s', skipping", dev_id, guid);
|
||||
free(*if_guid);
|
||||
*if_guid = NULL;
|
||||
err = LIBUSB_ERROR_NO_MEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
exit:
|
||||
pRegCloseKey(key);
|
||||
free(guid_string);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* get_device_list: libusb backend device enumeration function
|
||||
*/
|
||||
@@ -1469,12 +1600,10 @@ static int winusb_get_device_list(struct libusb_context *ctx, struct discovered_
|
||||
struct winusb_device_priv *priv, *parent_priv;
|
||||
char *dev_interface_path = NULL;
|
||||
unsigned long session_id;
|
||||
DWORD size, port_nr, reg_type, install_state;
|
||||
DWORD size, port_nr, install_state;
|
||||
uint8_t bus_number = 0;
|
||||
HKEY key;
|
||||
char guid_string[MAX_GUID_STRING_LENGTH];
|
||||
GUID *if_guid;
|
||||
LONG s;
|
||||
#define HUB_PASS 0
|
||||
#define DEV_PASS 1
|
||||
#define HCD_PASS 2
|
||||
@@ -1635,62 +1764,37 @@ static int winusb_get_device_list(struct libusb_context *ctx, struct discovered_
|
||||
usbi_info(ctx, "libusb will not be able to access it");
|
||||
}
|
||||
// ...and to add the additional device interface GUIDs
|
||||
key = pSetupDiOpenDevRegKey(*dev_info, &dev_info_data, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
|
||||
if (key == INVALID_HANDLE_VALUE)
|
||||
break;
|
||||
// Look for both DeviceInterfaceGUIDs *and* DeviceInterfaceGUID, in that order
|
||||
// If multiple GUIDs just process the first and ignore the others
|
||||
size = sizeof(guid_string);
|
||||
s = pRegQueryValueExA(key, "DeviceInterfaceGUIDs", NULL, ®_type,
|
||||
(LPBYTE)guid_string, &size);
|
||||
if (s == ERROR_FILE_NOT_FOUND)
|
||||
s = pRegQueryValueExA(key, "DeviceInterfaceGUID", NULL, ®_type,
|
||||
(LPBYTE)guid_string, &size);
|
||||
pRegCloseKey(key);
|
||||
if (s == ERROR_FILE_NOT_FOUND) {
|
||||
break; /* no DeviceInterfaceGUID registered */
|
||||
} else if (s != ERROR_SUCCESS && s != ERROR_MORE_DATA) {
|
||||
usbi_warn(ctx, "unexpected error from pRegQueryValueExA for '%s'", dev_id);
|
||||
break;
|
||||
}
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regqueryvalueexa#remarks
|
||||
// - "string may not have been stored with the proper terminating null characters"
|
||||
// - "Note that REG_MULTI_SZ strings could have two terminating null characters"
|
||||
if ((reg_type == REG_SZ && size >= sizeof(guid_string) - sizeof(char))
|
||||
|| (reg_type == REG_MULTI_SZ && size >= sizeof(guid_string) - 2 * sizeof(char))) {
|
||||
if (nb_guids == guid_size) {
|
||||
new_guid_list = realloc((void *)guid_list, (guid_size + GUID_SIZE_STEP) * sizeof(void *));
|
||||
if (new_guid_list == NULL) {
|
||||
usbi_err(ctx, "failed to realloc guid list");
|
||||
LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
|
||||
r = get_guid(ctx, dev_id, dev_info, &dev_info_data, 0, &if_guid);
|
||||
if (r == LIBUSB_SUCCESS) {
|
||||
// Check if we've already seen this GUID
|
||||
for (j = EXT_PASS; j < nb_guids; j++) {
|
||||
if (memcmp(guid_list[j], if_guid, sizeof(*if_guid)) == 0)
|
||||
break;
|
||||
}
|
||||
if (j == nb_guids) {
|
||||
usbi_dbg(ctx, "extra GUID: %s", guid_to_string(if_guid, guid_string));
|
||||
// Extend the guid_list capacity if needed
|
||||
if (nb_guids == guid_size) {
|
||||
new_guid_list = realloc((void *)guid_list, (guid_size + GUID_SIZE_STEP) * sizeof(void *));
|
||||
if (new_guid_list == NULL) {
|
||||
usbi_err(ctx, "failed to realloc guid list");
|
||||
free(if_guid);
|
||||
LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
|
||||
}
|
||||
guid_list = new_guid_list;
|
||||
guid_size += GUID_SIZE_STEP;
|
||||
}
|
||||
guid_list = new_guid_list;
|
||||
guid_size += GUID_SIZE_STEP;
|
||||
}
|
||||
if_guid = malloc(sizeof(*if_guid));
|
||||
if (if_guid == NULL) {
|
||||
usbi_err(ctx, "failed to alloc if_guid");
|
||||
LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
|
||||
}
|
||||
if (!string_to_guid(guid_string, if_guid)) {
|
||||
usbi_warn(ctx, "device '%s' has malformed DeviceInterfaceGUID string '%s', skipping", dev_id, guid_string);
|
||||
free(if_guid);
|
||||
guid_list[nb_guids++] = if_guid;
|
||||
} else {
|
||||
// Check if we've already seen this GUID
|
||||
for (j = EXT_PASS; j < nb_guids; j++) {
|
||||
if (memcmp(guid_list[j], if_guid, sizeof(*if_guid)) == 0)
|
||||
break;
|
||||
}
|
||||
if (j == nb_guids) {
|
||||
usbi_dbg(ctx, "extra GUID: %s", guid_string);
|
||||
guid_list[nb_guids++] = if_guid;
|
||||
} else {
|
||||
// Duplicate, ignore
|
||||
free(if_guid);
|
||||
}
|
||||
// Duplicate, ignore
|
||||
free(if_guid);
|
||||
}
|
||||
} else if (r == LIBUSB_ERROR_ACCESS) {
|
||||
r = LIBUSB_SUCCESS;
|
||||
} else if (r == LIBUSB_ERROR_NO_MEM) {
|
||||
LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
|
||||
} else {
|
||||
usbi_warn(ctx, "unexpected type/size of DeviceInterfaceGUID for '%s'", dev_id);
|
||||
usbi_warn(ctx, "unexpected error during getting DeviceInterfaceGUID for '%s'", dev_id);
|
||||
}
|
||||
break;
|
||||
case HID_PASS:
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
#define MAX_USB_STRING_LENGTH 128
|
||||
#define MAX_HID_REPORT_SIZE 1024
|
||||
#define MAX_HID_DESCRIPTOR_SIZE 256
|
||||
#define MAX_GUID_STRING_LENGTH 40
|
||||
#define MAX_GUID_STRING_LENGTH 39
|
||||
#define MAX_PATH_LENGTH 256
|
||||
#define MAX_KEY_LENGTH 256
|
||||
#define LIST_SEPARATOR ';'
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define LIBUSB_NANO 11815
|
||||
#define LIBUSB_NANO 11816
|
||||
|
||||
Reference in New Issue
Block a user