Skip to content

Commit

Permalink
Improve pnp id query routine for vhci
Browse files Browse the repository at this point in the history
pnp_query_id() in vhci has been improved refering #251.
BusQueryDeviceSerialNumber is newly added.
  • Loading branch information
cezanne committed Jul 19, 2021
1 parent 40d2ae9 commit 06b9b25
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 48 deletions.
6 changes: 1 addition & 5 deletions driver/vhci/vhci_dev.h
Original file line number Diff line number Diff line change
Expand Up @@ -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&VID_1209&PID_8250&REV_0000"

extern LPCWSTR devcodes[];

// These are the states a vpdo or vhub transition upon
Expand Down Expand Up @@ -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
Expand Down
14 changes: 14 additions & 0 deletions driver/vhci/vhci_pnp.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
158 changes: 115 additions & 43 deletions driver/vhci/vhci_pnp_id.c
Original file line number Diff line number Diff line change
@@ -1,56 +1,82 @@
#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&VID_1209&PID_8250\0USB\\ROOT_HUB\0"

/* 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_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
/* HW & compatible id use multi string.
* For single print function, use a semicolon as a string separator,
* which will be replaced with '\0'. */
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)
};

/*
* 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)
{
PWCHAR id_dev;
LPCWSTR id_fmt;
size_t id_size;

if (vdev_devids[vdev->type] == NULL) {
id_fmt = vdev_devids[vdev->type];
if (id_fmt == NULL) {
DBGI(DBG_PNP, "%s: query device id: NOT SUPPORTED\n", dbg_vdev_type(vdev->type));
return STATUS_NOT_SUPPORTED;
}
id_dev = ExAllocatePoolWithTag(PagedPool, vdev_devid_lens[vdev->type], USBIP_VHCI_POOL_TAG);

id_size = vdev_devid_size[vdev->type];
id_dev = ExAllocatePoolWithTag(PagedPool, id_size, USBIP_VHCI_POOL_TAG);
if (id_dev == NULL) {
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);
RtlStringCbPrintfW(id_dev, id_size, id_fmt, vpdo->vendor, vpdo->product);
}
else
RtlStringCchCopyW(id_dev, vdev_devid_lens[vdev->type], vdev_devids[vdev->type]);
RtlCopyMemory(id_dev, id_fmt, id_size);

irp->IoStatus.Information = (ULONG_PTR)id_dev;

Expand All @@ -63,35 +89,47 @@ static NTSTATUS
setup_hw_ids(pvdev_t vdev, PIRP irp)
{
PWCHAR ids_hw;
LPCWSTR ids_fmt;
size_t ids_size;

if (vdev_hwids[vdev->type] == NULL) {
ids_fmt = vdev_hwids[vdev->type];
if (ids_fmt == NULL) {
DBGI(DBG_PNP, "%s: query hw ids: NOT SUPPORTED%s\n", dbg_vdev_type(vdev->type));
return STATUS_NOT_SUPPORTED;
}
ids_hw = ExAllocatePoolWithTag(PagedPool, vdev_hwids_lens[vdev->type], USBIP_VHCI_POOL_TAG);

ids_size = vdev_hwids_size[vdev->type];
ids_hw = ExAllocatePoolWithTag(PagedPool, ids_size, USBIP_VHCI_POOL_TAG);
if (ids_hw == NULL) {
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';
pvpdo_dev_t vpdo = (pvpdo_dev_t)vdev;
RtlStringCbPrintfW(ids_hw, ids_size, ids_fmt,
vpdo->vendor, vpdo->product, vpdo->revision, vpdo->vendor, vpdo->product);
ids_hw[31 + 22 - 1] = L'\0';
}
else {
RtlCopyMemory(ids_hw, vdev_hwids[vdev->type], vdev_hwids_lens[vdev->type]);
RtlCopyMemory(ids_hw, ids_fmt, ids_size);
}

irp->IoStatus.Information = (ULONG_PTR)ids_hw;

DBGI(DBG_PNP, "%s: hw id: %S\n", dbg_vdev_type(vdev->type), ids_hw);

if (vdev->type == VDEV_VPDO) {
/* Convert into multi string by replacing a semicolon */
ids_hw[31 - 1] = L'\0';
}
irp->IoStatus.Information = (ULONG_PTR)ids_hw;

return STATUS_SUCCESS;
}

/*
* Some old(?) applications may use instance id as USB serial.
*/
static NTSTATUS
setup_inst_id(pvdev_t vdev, PIRP irp)
setup_inst_id_or_serial(pvdev_t vdev, PIRP irp, BOOLEAN serial)
{
pvpdo_dev_t vpdo;
PWCHAR id_inst;
Expand All @@ -105,18 +143,22 @@ setup_inst_id(pvdev_t vdev, PIRP irp)

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");
DBGE(DBG_PNP, "vpdo: query instance id or serial: out of memory\n");
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);
else {
if (serial)
id_inst[0] = '\0';
else
RtlStringCchPrintfW(id_inst, MAX_VHCI_SERIAL_ID + 1, L"%04hx", vpdo->port);
}

irp->IoStatus.Information = (ULONG_PTR)id_inst;

DBGI(DBG_PNP, "vpdo: instance id: %S\n", id_inst);
DBGI(DBG_PNP, "vpdo: %s: %S\n", serial ? "serial": "instance id", id_inst);

return STATUS_SUCCESS;
}
Expand All @@ -126,6 +168,12 @@ setup_compat_ids(pvdev_t vdev, PIRP irp)
{
pvpdo_dev_t vpdo;
PWCHAR ids_compat;
LPCWSTR ids_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
L"USB\\COMPOSITE;"; // 14 chars
size_t ids_size = (33 + 25 + 13 + 14 + 1) * sizeof(WCHAR);

if (vdev->type != VDEV_VPDO) {
DBGI(DBG_PNP, "%s: query compatible id: NOT SUPPORTED\n", dbg_vdev_type(vdev->type));
Expand All @@ -134,21 +182,40 @@ setup_compat_ids(pvdev_t vdev, PIRP irp)

vpdo = (pvpdo_dev_t)vdev;

ids_compat = ExAllocatePoolWithTag(PagedPool, 86 * sizeof(wchar_t), USBIP_VHCI_POOL_TAG);
ids_compat = ExAllocatePoolWithTag(PagedPool, ids_size, USBIP_VHCI_POOL_TAG);
if (ids_compat == NULL) {
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);

RtlStringCbPrintfW(ids_compat, ids_size, ids_fmt,
vpdo->usbclass, vpdo->subclass, vpdo->protocol,
vpdo->usbclass, vpdo->subclass,
vpdo->usbclass);

/* Convert last semicolon */
ids_compat[33 + 25 + 13 + 14 - 1] = L'\0';

if (IS_ZERO_CLASS(vpdo) || IS_IAD_DEVICE(vpdo)) {
RtlStringCchCopyW(ids_compat + 71, 14, L"USB\\COMPOSITE");
ids_compat[85] = L'\0';
/* USB\COMPOSITE is dropped */
ids_compat[33 + 25 + 13 - 1] = L'\0';
}
else
ids_compat[71] = L'\0';

DBGI(DBG_PNP, "vpdo: compatible ids: %S\n", ids_compat);

ids_compat[33 - 1] = L'\0';
ids_compat[33 + 25 - 1] = L'\0';
if (ids_compat[33 + 25 + 13 - 1] == L'\0') {
/* no composite */
ids_compat[33 + 25 + 13] = L'\0';
}
else {
ids_compat[33 + 25 + 13 - 1] = L'\0';
ids_compat[33 + 25 + 13 + 14 - 1] = L'\0';
}

irp->IoStatus.Information = (ULONG_PTR)ids_compat;

return STATUS_SUCCESS;
}

Expand All @@ -166,14 +233,19 @@ pnp_query_id(pvdev_t vdev, PIRP irp, PIO_STACK_LOCATION irpstack)
status = setup_device_id(vdev, irp);
break;
case BusQueryInstanceID:
status = setup_inst_id(vdev, irp);
status = setup_inst_id_or_serial(vdev, irp, FALSE);
break;
case BusQueryHardwareIDs:
status = setup_hw_ids(vdev, irp);
break;
case BusQueryCompatibleIDs:
status = setup_compat_ids(vdev, irp);
break;
case BusQueryDeviceSerialNumber:
status = setup_inst_id_or_serial(vdev, irp, FALSE);
break;
case BusQueryContainerID:
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));
break;
Expand Down

0 comments on commit 06b9b25

Please sign in to comment.