From 723f9d59d8830cf5a73ceacded5229a2faa93c90 Mon Sep 17 00:00:00 2001 From: vadimgrn <67631045+vadimgrn@users.noreply.github.com> Date: Wed, 14 Jul 2021 09:41:24 +0300 Subject: [PATCH] Fix vhci wdm driver installation failure Signed-off-by: vadimgrn <67631045+vadimgrn@users.noreply.github.com> --- driver/vhci/usbip_root.inf | 2 +- driver/vhci/usbip_vhci.inf | 8 +- driver/vhci/usbip_vhci.vcxproj | 8 +- driver/vhci/vhci_dev.h | 8 +- driver/vhci/vhci_plugin.c | 55 +++--- driver/vhci/vhci_pnp.h | 14 ++ driver/vhci/vhci_pnp_id.c | 334 +++++++++++++++++++++++---------- 7 files changed, 290 insertions(+), 139 deletions(-) diff --git a/driver/vhci/usbip_root.inf b/driver/vhci/usbip_root.inf index cd2c6042..b274de71 100644 --- a/driver/vhci/usbip_root.inf +++ b/driver/vhci/usbip_root.inf @@ -48,7 +48,7 @@ AddService = usbip_vhci,%SPSVCINST_ASSOCSERVICE%, usbip_vhci_Service_Inst [usbip_vhci_Service_Inst] DisplayName = %ServiceDesc% ServiceType = 1 ; SERVICE_KERNEL_DRIVER -StartType = 3 ; SERVICE_DEMAND_START +StartType = 3 ; SERVICE_DEMAND_START ErrorControl = 1 ; SERVICE_ERROR_NORMAL ServiceBinary = %12%\usbip_vhci.sys LoadOrderGroup = Extended Base diff --git a/driver/vhci/usbip_vhci.inf b/driver/vhci/usbip_vhci.inf index 38b52138..7c27f9c0 100644 --- a/driver/vhci/usbip_vhci.inf +++ b/driver/vhci/usbip_vhci.inf @@ -18,7 +18,7 @@ PnpLockDown=1 [Standard.NT$ARCH$] %DeviceDesc%=usbip_vhci_Device, USBIPWIN\vhci -%vhub_DeviceDesc%=usbip_vhci_Device, USB\ROOT_HUB&VID1209&PID8250&REV0000 +%vhub_DeviceDesc%=usbip_vhci_Device, USB\ROOT_HUB&VID_1209&PID_8250&REV_0000 [DestinationDirs] DefaultDestDir = 12 @@ -38,7 +38,7 @@ CopyFiles=Drivers_Dir [usbip_vhci_Device.NT$ARCH$.HW] AddReg=usbip_vhci_Device_AddReg -[usbip_vhci_Device_AddReg] +[usbip_vhci_Device_AddReg] HKR,,DeviceCharacteristics,0x10001,0x0100 ; Use same security checks on relative opens ;-------------- Service installation @@ -49,7 +49,7 @@ AddService = usbip_vhci,%SPSVCINST_ASSOCSERVICE%, usbip_vhci_Service_Inst [usbip_vhci_Service_Inst] DisplayName = %ServiceDesc% ServiceType = 1 ; SERVICE_KERNEL_DRIVER -StartType = 3 ; SERVICE_DEMAND_START +StartType = 3 ; SERVICE_DEMAND_START ErrorControl = 1 ; SERVICE_ERROR_NORMAL ServiceBinary = %12%\usbip_vhci.sys LoadOrderGroup = Extended Base @@ -62,4 +62,4 @@ DiskId1 = "usbip-win VHCI Disk" DeviceDesc = "usbip-win VHCI" vhub_DeviceDesc = "usbip-win VHUB" ServiceDesc = "usbip-win VHCI Service" -DisplayClassName = "usbip-win" \ No newline at end of file +DisplayClassName = "usbip-win" diff --git a/driver/vhci/usbip_vhci.vcxproj b/driver/vhci/usbip_vhci.vcxproj index efdff777..b8a0f446 100644 --- a/driver/vhci/usbip_vhci.vcxproj +++ b/driver/vhci/usbip_vhci.vcxproj @@ -215,9 +215,7 @@ 0.3.4.0 - - sha256 - + gencat.bat $(OutDir) 10_$(DDKPlatform) $(SolutionDir)/driver/usbip_test.pfx @@ -240,9 +238,7 @@ 0.3.4.0 - - sha256 - + gencat.bat $(OutDir) 10_$(DDKPlatform) $(SolutionDir)/driver/usbip_test.pfx diff --git a/driver/vhci/vhci_dev.h b/driver/vhci/vhci_dev.h index eb63968b..415f622c 100644 --- a/driver/vhci/vhci_dev.h +++ b/driver/vhci/vhci_dev.h @@ -23,10 +23,6 @@ #define IS_FDO(type) ((type) == VDEV_ROOT || (type) == VDEV_VHCI || (type) == VDEV_VHUB) -#define HWID_ROOT L"USBIPWIN\\root" -#define HWID_VHCI L"USBIPWIN\\vhci" -#define HWID_VHUB L"USB\\ROOT_HUB&VID1209&PID8250&REV0000" - extern LPCWSTR devcodes[]; // These are the states a vpdo or vhub transition upon @@ -65,7 +61,7 @@ typedef struct _vdev { PDEVICE_OBJECT Self; vdev_type_t type; - // reference count for maintaining vdev validity + // reference count for maintaining vdev validity LONG n_refs; // We track the state of the device with every PnP Irp @@ -174,7 +170,7 @@ typedef struct BOOLEAN plugged; UCHAR speed; - UCHAR unused; /* 4 bytes alignment */ + UCHAR num_configurations; // Number of Possible Configurations // a pending irp when no urb is requested PIRP pending_read_irp; diff --git a/driver/vhci/vhci_plugin.c b/driver/vhci/vhci_plugin.c index 0876016d..4987f9b0 100644 --- a/driver/vhci/vhci_plugin.c +++ b/driver/vhci/vhci_plugin.c @@ -58,8 +58,8 @@ setup_vpdo_with_dsc_dev(pvpdo_dev_t vpdo, PUSB_DEVICE_DESCRIPTOR dsc_dev) vpdo->subclass = dsc_dev->bDeviceSubClass; vpdo->protocol = dsc_dev->bDeviceProtocol; vpdo->speed = (UCHAR)get_usb_speed(dsc_dev->bcdUSB); - } - else { + vpdo->num_configurations = dsc_dev->bNumConfigurations; + } else { vpdo->vendor = 0; vpdo->product = 0; vpdo->revision = 0; @@ -67,37 +67,40 @@ setup_vpdo_with_dsc_dev(pvpdo_dev_t vpdo, PUSB_DEVICE_DESCRIPTOR dsc_dev) vpdo->subclass = 0; vpdo->protocol = 0; vpdo->speed = USB_SPEED_LOW; + vpdo->num_configurations = 0; } } static void setup_vpdo_with_dsc_conf(pvpdo_dev_t vpdo, PUSB_CONFIGURATION_DESCRIPTOR dsc_conf) { - if (dsc_conf) { - vpdo->inum = dsc_conf->bNumInterfaces; - - /* Many devices have 0 usb class number in a device descriptor. - * 0 value means that class number is determined at interface level. - * USB class, subclass and protocol numbers should be setup before importing. - * Because windows vhci driver builds a device compatible id with those numbers. - */ - if (vpdo->usbclass == 0 && vpdo->subclass == 0 && vpdo->protocol == 0) { - /* buf[4] holds the number of interfaces in USB configuration. - * Supplement class/subclass/protocol only if there exists only single interface. - * A device with multiple interfaces will be detected as a composite by vhci. - */ - if (vpdo->inum == 1) { - PUSB_INTERFACE_DESCRIPTOR dsc_intf = dsc_find_first_intf(dsc_conf); - if (dsc_intf) { - vpdo->usbclass = dsc_intf->bInterfaceClass; - vpdo->subclass = dsc_intf->bInterfaceSubClass; - vpdo->protocol = dsc_intf->bInterfaceProtocol; - } - } - } - } - else { + if (!dsc_conf) { vpdo->inum = 0; + return; + } + + vpdo->inum = dsc_conf->bNumInterfaces; + + /* Many devices have 0 usb class number in a device descriptor. + * 0 value means that class number is determined at interface level. + * USB class, subclass and protocol numbers should be setup before importing. + * Because windows vhci driver builds a device compatible id with those numbers. + */ + if (vpdo->usbclass || vpdo->subclass || vpdo->protocol) { + return; + } + + /* buf[4] holds the number of interfaces in USB configuration. + * Supplement class/subclass/protocol only if there exists only single interface. + * A device with multiple interfaces will be detected as a composite by vhci. + */ + if (vpdo->inum == 1) { + PUSB_INTERFACE_DESCRIPTOR dsc_intf = dsc_find_first_intf(dsc_conf); + if (dsc_intf) { + vpdo->usbclass = dsc_intf->bInterfaceClass; + vpdo->subclass = dsc_intf->bInterfaceSubClass; + vpdo->protocol = dsc_intf->bInterfaceProtocol; + } } } diff --git a/driver/vhci/vhci_pnp.h b/driver/vhci/vhci_pnp.h index cb70ff28..b1fd05a9 100644 --- a/driver/vhci/vhci_pnp.h +++ b/driver/vhci/vhci_pnp.h @@ -3,6 +3,20 @@ #include "basetype.h" #include "vhci_dev.h" +#define HWID_ROOT L"USBIPWIN\\root" +#define HWID_VHCI L"USBIPWIN\\vhci" + +#define VHUB_PREFIX L"USB\\ROOT_HUB" +#define VHUB_VID L"1209" +#define VHUB_PID L"8250" +#define VHUB_REV L"0000" + +#define HWID_VHUB \ + VHUB_PREFIX \ + L"&VID_" VHUB_VID \ + L"&PID_" VHUB_PID \ + L"&REV_" VHUB_REV + #define INITIALIZE_PNP_STATE(_Data_) \ (_Data_)->common.DevicePnPState = NotStarted;\ (_Data_)->common.PreviousPnPState = NotStarted; diff --git a/driver/vhci/vhci_pnp_id.c b/driver/vhci/vhci_pnp_id.c index b1eba7e5..33c700cb 100644 --- a/driver/vhci/vhci_pnp_id.c +++ b/driver/vhci/vhci_pnp_id.c @@ -1,183 +1,325 @@ #include "vhci.h" - -#include "vhci_dev.h" +#include "vhci_pnp.h" #include "usbip_vhci_api.h" #include "vhci_irp.h" -#define DEVID_VHCI HWID_VHCI -#define DEVID_VHUB L"USB\\ROOT_HUB" - -#define HWIDS_VHCI HWID_VHCI L"\0" -#define HWIDS_VHUB HWID_VHUB L"\0USB\\ROOT_HUB&VID1209&PID8250\0USB\\ROOT_HUB\0" +#include -/* Device with zero class/subclass/protocol */ -#define IS_ZERO_CLASS(vpdo) ((vpdo)->usbclass == 0x00 && (vpdo)->subclass == 0x00 && (vpdo)->protocol == 0x00 && (vpdo)->inum > 1) -/* Device with IAD(Interface Association Descriptor) */ -#define IS_IAD_DEVICE(vpdo) ((vpdo)->usbclass == 0xef && (vpdo)->subclass == 0x02 && (vpdo)->protocol == 0x01) - -static LPCWSTR vdev_devids[] = { - NULL, DEVID_VHCI, NULL, DEVID_VHUB, NULL, (LPCWSTR)TRUE +#define DEVID_VHCI HWID_VHCI +#define DEVID_VHUB HWID_VHUB + +/* + * The first hardware ID in the list should be the device ID, and + * the remaining IDs should be listed in order of decreasing suitability. + */ +#define HWIDS_VHCI DEVID_VHCI L"\0" + +#define HWIDS_VHUB \ + DEVID_VHUB L"\0" \ + VHUB_PREFIX L"&VID_" VHUB_VID L"&PID_" VHUB_PID L"\0" + +// vdev_type_t is an index +static const LPCWSTR vdev_devids[] = { + NULL, DEVID_VHCI, + NULL, DEVID_VHUB, + NULL, L"USB\\VID_%04hx&PID_%04hx" // 21 chars after formatting }; -static ULONG vdev_devid_lens[] = { - 0, sizeof(DEVID_VHCI), 0, sizeof(DEVID_VHUB), 0, 22 * sizeof(wchar_t) +static const size_t vdev_devid_size[] = { + 0, sizeof(DEVID_VHCI), + 0, sizeof(DEVID_VHUB), + 0, (21 + 1)*sizeof(WCHAR) }; -static LPCWSTR vdev_hwids[] = { - NULL, HWIDS_VHCI, NULL, HWIDS_VHUB, NULL, (LPCWSTR)TRUE +static const LPCWSTR vdev_hwids[] = { + NULL, HWIDS_VHCI, + NULL, HWIDS_VHUB, + NULL, L"USB\\VID_%04hx&PID_%04hx&REV_%04hx;" // 31 chars after formatting + L"USB\\VID_%04hx&PID_%04hx;" // 22 chars after formatting }; -static ULONG vdev_hwids_lens[] = { - 0, sizeof(HWIDS_VHCI), 0, sizeof(HWIDS_VHUB), 0, 54 * sizeof(wchar_t) +static const size_t vdev_hwids_size[] = { + 0, sizeof(HWIDS_VHCI), + 0, sizeof(HWIDS_VHUB), + 0, (31 + 22 + 1)*sizeof(WCHAR) }; +void subst_char(wchar_t *s, wchar_t ch, wchar_t rep) +{ + for ( ; *s; ++s) { + if (*s == ch) { + *s = rep; + } + } +} + +/* + Enumeration of USB Composite Devices. + + The bus driver also reports a compatible identifier (ID) of USB\COMPOSITE, + if the device meets the following requirements: + * The device class field of the device descriptor (bDeviceClass) must contain a value of zero, + or the class (bDeviceClass), subclass (bDeviceSubClass), and protocol (bDeviceProtocol) fields + of the device descriptor must have the values 0xEF, 0x02 and 0x01 respectively, as explained + in USB Interface Association Descriptor. + * The device must have multiple interfaces. + * The device must have a single configuration. + + The bus driver also checks the device class (bDeviceClass), subclass (bDeviceSubClass), + and protocol (bDeviceProtocol) fields of the device descriptor. If these fields are zero, + the device is a composite device, and the bus driver reports an extra compatible + identifier (ID) of USB\COMPOSITE for the PDO. +*/ +static bool is_composite(vpdo_dev_t *vpdo) +{ + bool ok = !vpdo->usbclass || // generic composite device + (vpdo->usbclass == 0xEF && + vpdo->subclass == 0x02 && + vpdo->protocol == 0x01); // IAD composite device + + if (ok && vpdo->inum > 1 && vpdo->num_configurations == 1) { + return true; + } + + return vpdo->num_configurations && // zero if the device descriptor wasn't read yet + !(vpdo->usbclass || vpdo->subclass || vpdo->protocol); +} + +/* + * For all USB devices, the USB bus driver reports a device ID with the following format: + * USB\VID_xxxx&PID_yyyy + */ static NTSTATUS -setup_device_id(pvdev_t vdev, PIRP irp) +setup_device_id(PWCHAR *result, bool *subst_result, pvdev_t vdev, PIRP irp) { - PWCHAR id_dev; + UNREFERENCED_PARAMETER(subst_result); + UNREFERENCED_PARAMETER(irp); + + NTSTATUS status = STATUS_SUCCESS; - if (vdev_devids[vdev->type] == NULL) { - DBGI(DBG_PNP, "%s: query device id: NOT SUPPORTED\n", dbg_vdev_type(vdev->type)); + LPCWSTR str = vdev_devids[vdev->type]; + if (!str) { return STATUS_NOT_SUPPORTED; } - id_dev = ExAllocatePoolWithTag(PagedPool, vdev_devid_lens[vdev->type], USBIP_VHCI_POOL_TAG); - if (id_dev == NULL) { + + size_t str_sz = vdev_devid_size[vdev->type]; + + PWCHAR id_dev = ExAllocatePoolWithTag(PagedPool, str_sz, USBIP_VHCI_POOL_TAG); + if (!id_dev) { DBGE(DBG_PNP, "%s: query device id: out of memory\n", dbg_vdev_type(vdev->type)); return STATUS_INSUFFICIENT_RESOURCES; } + if (vdev->type == VDEV_VPDO) { - pvpdo_dev_t vpdo = (pvpdo_dev_t)vdev; - RtlStringCchPrintfW(id_dev, 22, L"USB\\VID_%04hx&PID_%04hx", vpdo->vendor, vpdo->product); + pvpdo_dev_t vpdo = (pvpdo_dev_t)vdev; + status = RtlStringCbPrintfW(id_dev, str_sz, str, vpdo->vendor, vpdo->product); + } else { + RtlCopyMemory(id_dev, str, str_sz); } - else - RtlStringCchCopyW(id_dev, vdev_devid_lens[vdev->type], vdev_devids[vdev->type]); - - irp->IoStatus.Information = (ULONG_PTR)id_dev; - DBGI(DBG_PNP, "%s: device id: %S\n", dbg_vdev_type(vdev->type), id_dev); + if (status == STATUS_SUCCESS) { + *result = id_dev; + } - return STATUS_SUCCESS; + return status; } static NTSTATUS -setup_hw_ids(pvdev_t vdev, PIRP irp) +setup_hw_ids(PWCHAR *result, bool *subst_result, pvdev_t vdev, PIRP irp) { - PWCHAR ids_hw; + UNREFERENCED_PARAMETER(irp); + + NTSTATUS status = STATUS_SUCCESS; - if (vdev_hwids[vdev->type] == NULL) { - DBGI(DBG_PNP, "%s: query hw ids: NOT SUPPORTED%s\n", dbg_vdev_type(vdev->type)); + LPCWSTR str = vdev_hwids[vdev->type]; + if (!str) { return STATUS_NOT_SUPPORTED; } - ids_hw = ExAllocatePoolWithTag(PagedPool, vdev_hwids_lens[vdev->type], USBIP_VHCI_POOL_TAG); - if (ids_hw == NULL) { + + size_t str_sz = vdev_hwids_size[vdev->type]; + + PWCHAR ids_hw = ExAllocatePoolWithTag(PagedPool, str_sz, USBIP_VHCI_POOL_TAG); + if (!ids_hw) { DBGE(DBG_PNP, "%s: query hw ids: out of memory\n", dbg_vdev_type(vdev->type)); return STATUS_INSUFFICIENT_RESOURCES; } - if (vdev->type == VDEV_VPDO) { - pvpdo_dev_t vpdo = (pvpdo_dev_t)vdev; - RtlStringCchPrintfW(ids_hw, 31, L"USB\\VID_%04hx&PID_%04hx&REV_%04hx", vpdo->vendor, vpdo->product, vpdo->revision); - RtlStringCchPrintfW(ids_hw + 31, 22, L"USB\\VID_%04hx&PID_%04hx", vpdo->vendor, vpdo->product); - ids_hw[53] = L'\0'; - } - else { - RtlCopyMemory(ids_hw, vdev_hwids[vdev->type], vdev_hwids_lens[vdev->type]); - } - irp->IoStatus.Information = (ULONG_PTR)ids_hw; + *subst_result = vdev->type == VDEV_VPDO; - DBGI(DBG_PNP, "%s: hw id: %S\n", dbg_vdev_type(vdev->type), ids_hw); + if (*subst_result) { + pvpdo_dev_t vpdo = (pvpdo_dev_t)vdev; + status = RtlStringCbPrintfW(ids_hw, str_sz, str, + vpdo->vendor, vpdo->product, vpdo->revision, + vpdo->vendor, vpdo->product); + } else { + RtlCopyMemory(ids_hw, str, str_sz); + } - return STATUS_SUCCESS; + if (status == STATUS_SUCCESS) { + *result = ids_hw; + } + + return status; } +/* +Instance ID + +An instance ID is a device identification string that distinguishes a device +from other devices of the same type on a computer. An instance ID contains +serial number information, if supported by the underlying bus, or some kind +of location information. The string cannot contain any "\" characters; +otherwise, the generic format of the string is bus-specific. + +The number of characters of an instance ID, excluding a NULL-terminator, must be +less than MAX_DEVICE_ID_LEN. In addition, when an instance ID is concatenated +to a device ID to create a device instance ID, the lengths of the device ID +and the instance ID are further constrained by the maximum possible length +of a device instance ID. + +The UniqueID member of the DEVICE_CAPABILITIES structure for a device indicates +if a bus-supplied instance ID is unique across the system, as follows: + * If UniqueID is FALSE, the bus-supplied instance ID for a device is unique + only to the device's bus. The Plug and Play (PnP) manager modifies + the bus-supplied instance ID, and combines it with the corresponding device ID, + to create a device instance ID that is unique in the system. + * If UniqueID is TRUE, the device instance ID, formed from the bus-supplied + device ID and instance ID, uniquely identifies a device in the system. + +An instance ID is persistent across system restarts. +*/ static NTSTATUS -setup_inst_id(pvdev_t vdev, PIRP irp) +setup_inst_id_or_serial(PWCHAR *result, bool *subst_result, pvdev_t vdev, PIRP irp, bool serial) { - pvpdo_dev_t vpdo; - PWCHAR id_inst; + UNREFERENCED_PARAMETER(subst_result); + UNREFERENCED_PARAMETER(irp); + + NTSTATUS status = STATUS_SUCCESS; + + const size_t max_wchars = MAX_VHCI_SERIAL_ID + 1; + // static_assert(MAX_VHCI_SERIAL_ID <= MAX_DEVICE_ID_LEN, "assert"); if (vdev->type != VDEV_VPDO) { - DBGI(DBG_PNP, "%s: query instance id: NOT SUPPORTED\n", dbg_vdev_type(vdev->type)); return STATUS_NOT_SUPPORTED; } - vpdo = (pvpdo_dev_t)vdev; + pvpdo_dev_t vpdo = (pvpdo_dev_t)vdev; - id_inst = ExAllocatePoolWithTag(PagedPool, (MAX_VHCI_SERIAL_ID + 1) * sizeof(wchar_t), USBIP_VHCI_POOL_TAG); - if (id_inst == NULL) { - DBGE(DBG_PNP, "vpdo: query instance id: out of memory\n"); + PWCHAR id_inst = ExAllocatePoolWithTag(PagedPool, max_wchars*sizeof(wchar_t), USBIP_VHCI_POOL_TAG); + if (!id_inst) { + DBGE(DBG_PNP, "vpdo: %s(%s): out of memory\n", __func__, + serial ? "DeviceSerialNumber" : "InstanceID"); return STATUS_INSUFFICIENT_RESOURCES; } - if (vpdo->winstid != NULL) - RtlStringCchCopyW(id_inst, MAX_VHCI_SERIAL_ID + 1, vpdo->winstid); - else - RtlStringCchPrintfW(id_inst, 5, L"%04hx", vpdo->port); - - irp->IoStatus.Information = (ULONG_PTR)id_inst; + status = vpdo->winstid ? RtlStringCchCopyW(id_inst, max_wchars, vpdo->winstid) : // is a serial + serial ? RtlStringCchPrintfW(id_inst, max_wchars, L"") : // has no serial + RtlStringCchPrintfW(id_inst, max_wchars, L"%04hx", vpdo->port); // instance id - DBGI(DBG_PNP, "vpdo: instance id: %S\n", id_inst); + if (status == STATUS_SUCCESS) { + *result = id_inst; + } - return STATUS_SUCCESS; + return status; } static NTSTATUS -setup_compat_ids(pvdev_t vdev, PIRP irp) +setup_compat_ids(PWCHAR *result, bool *subst_result, pvdev_t vdev, PIRP irp) { - pvpdo_dev_t vpdo; - PWCHAR ids_compat; + UNREFERENCED_PARAMETER(irp); + + const wchar_t fmt[] = + L"USB\\Class_%02hhx&SubClass_%02hhx&Prot_%02hhx;" // 33 chars after formatting + L"USB\\Class_%02hhx&SubClass_%02hhx;" // 25 chars after formatting + L"USB\\Class_%02hhx;"; // 13 chars after formatting + + const wchar_t comp[] = L"USB\\COMPOSITE\0"; + const size_t max_wchars = 33 + 25 + 13 + sizeof(comp)/sizeof(*comp); if (vdev->type != VDEV_VPDO) { - DBGI(DBG_PNP, "%s: query compatible id: NOT SUPPORTED\n", dbg_vdev_type(vdev->type)); return STATUS_NOT_SUPPORTED; } - vpdo = (pvpdo_dev_t)vdev; + pvpdo_dev_t vpdo = (pvpdo_dev_t)vdev; - ids_compat = ExAllocatePoolWithTag(PagedPool, 86 * sizeof(wchar_t), USBIP_VHCI_POOL_TAG); - if (ids_compat == NULL) { + PWCHAR ids_compat = ExAllocatePoolWithTag(PagedPool, max_wchars * sizeof(wchar_t), USBIP_VHCI_POOL_TAG); + if (!ids_compat) { DBGE(DBG_PNP, "vpdo: query compatible id: out of memory\n"); return STATUS_INSUFFICIENT_RESOURCES; } - RtlStringCchPrintfW(ids_compat, 33, L"USB\\Class_%02hhx&SubClass_%02hhx&Prot_%02hhx", vpdo->usbclass, vpdo->subclass, vpdo->protocol); - RtlStringCchPrintfW(ids_compat + 33, 25, L"USB\\Class_%02hhx&SubClass_%02hhx", vpdo->usbclass, vpdo->subclass); - RtlStringCchPrintfW(ids_compat + 58, 13, L"USB\\Class_%02hhx", vpdo->usbclass); - if (IS_ZERO_CLASS(vpdo) || IS_IAD_DEVICE(vpdo)) { - RtlStringCchCopyW(ids_compat + 71, 14, L"USB\\COMPOSITE"); - ids_compat[85] = L'\0'; - } - else - ids_compat[71] = L'\0'; - irp->IoStatus.Information = (ULONG_PTR)ids_compat; - return STATUS_SUCCESS; + + NTSTRSAFE_PWSTR dest_end = NULL; + size_t remaining = 0; + + NTSTATUS status = RtlStringCchPrintfExW(ids_compat, max_wchars, &dest_end, &remaining, 0, fmt, + vpdo->usbclass, vpdo->subclass, vpdo->protocol, + vpdo->usbclass, vpdo->subclass, + vpdo->usbclass); + + if (status == STATUS_SUCCESS) { + *result = ids_compat; + *subst_result = true; + if (is_composite(vpdo)) { + NT_ASSERT(remaining == sizeof(comp)/sizeof(*comp)); + RtlCopyMemory(dest_end, comp, sizeof(comp)); + } + } + + return status; } +/* + * On success, a driver sets Irp->IoStatus.Information to a WCHAR pointer + * that points to the requested information. On error, a driver sets + * Irp->IoStatus.Information to zero. + */ PAGEABLE NTSTATUS pnp_query_id(pvdev_t vdev, PIRP irp, PIO_STACK_LOCATION irpstack) { - NTSTATUS status = STATUS_NOT_SUPPORTED; + NTSTATUS status = STATUS_NOT_SUPPORTED; - DBGI(DBG_PNP, "%s: query id: %s\n", dbg_vdev_type(vdev->type), dbg_bus_query_id_type(irpstack->Parameters.QueryId.IdType)); + PWCHAR result = NULL; + bool subst_result = false; + + BUS_QUERY_ID_TYPE type = irpstack->Parameters.QueryId.IdType; PAGED_CODE(); - switch (irpstack->Parameters.QueryId.IdType) { + switch (type) { case BusQueryDeviceID: - status = setup_device_id(vdev, irp); + status = setup_device_id(&result, &subst_result, vdev, irp); break; case BusQueryInstanceID: - status = setup_inst_id(vdev, irp); + status = setup_inst_id_or_serial(&result, &subst_result, vdev, irp, false); break; case BusQueryHardwareIDs: - status = setup_hw_ids(vdev, irp); + status = setup_hw_ids(&result, &subst_result, vdev, irp); break; case BusQueryCompatibleIDs: - status = setup_compat_ids(vdev, irp); + status = setup_compat_ids(&result, &subst_result, vdev, irp); + break; + case BusQueryDeviceSerialNumber: + status = setup_inst_id_or_serial(&result, &subst_result, vdev, irp, true); break; - default: - DBGI(DBG_PNP, "%s: unhandled query id: %s\n", dbg_vdev_type(vdev->type), dbg_bus_query_id_type(irpstack->Parameters.QueryId.IdType)); + case BusQueryContainerID: break; } + if (status == STATUS_SUCCESS) { + DBGI(DBG_PNP, "%s: %s: %S\n", dbg_vdev_type(vdev->type), dbg_bus_query_id_type(type), result); + if (subst_result) { + subst_char(result, L';', L'\0'); + } + } else { + DBGW(DBG_PNP, "%s: %s: %s\n", dbg_vdev_type(vdev->type), dbg_bus_query_id_type(type), + status == STATUS_NOT_SUPPORTED ? "not supported" : "failed"); + + if (result) { + ExFreePoolWithTag(result, USBIP_VHCI_POOL_TAG); + result = NULL; + } + } + + irp->IoStatus.Information = (ULONG_PTR)result; return irp_done(irp, status); -} \ No newline at end of file +}