diff --git a/driver/lib/libdrv.vcxproj b/driver/lib/libdrv.vcxproj
index 52036da3..cf8a38ad 100644
--- a/driver/lib/libdrv.vcxproj
+++ b/driver/lib/libdrv.vcxproj
@@ -118,6 +118,7 @@
true
..\..\include;$(IntDir);%(AdditionalIncludeDirectories)
true
+ _NO_CRT_STDIO_INLINE;%(PreprocessorDefinitions)
$(DDK_LIB_PATH)wdmsec.lib;$(DDK_LIB_PATH)ntstrsafe.lib;%(AdditionalDependencies)
@@ -134,6 +135,7 @@
true
..\..\include;$(IntDir);%(AdditionalIncludeDirectories)
true
+ _NO_CRT_STDIO_INLINE;%(PreprocessorDefinitions)
$(DDK_LIB_PATH)wdmsec.lib;$(DDK_LIB_PATH)ntstrsafe.lib;%(AdditionalDependencies)
@@ -150,6 +152,7 @@
true
..\..\include;$(IntDir);%(AdditionalIncludeDirectories)
true
+ _NO_CRT_STDIO_INLINE;%(PreprocessorDefinitions)
$(DDK_LIB_PATH)wdmsec.lib;$(DDK_LIB_PATH)ntstrsafe.lib;%(AdditionalDependencies)
@@ -166,6 +169,7 @@
true
..\..\include;$(IntDir);%(AdditionalIncludeDirectories)
true
+ _NO_CRT_STDIO_INLINE;%(PreprocessorDefinitions)
$(DDK_LIB_PATH)wdmsec.lib;$(DDK_LIB_PATH)ntstrsafe.lib;%(AdditionalDependencies)
@@ -190,4 +194,4 @@
-
+
\ No newline at end of file
diff --git a/driver/lib/usbd_helper.c b/driver/lib/usbd_helper.c
index f7a74999..d2bfe67a 100644
--- a/driver/lib/usbd_helper.c
+++ b/driver/lib/usbd_helper.c
@@ -3,123 +3,256 @@
#include
#include
-#define EPIPE 32
-#define EOVERFLOW 75
-#define EREMOTEIO 121
+#include
+/*
+ * Linux error codes.
+ * See: include/uapi/asm-generic/errno-base.h, include/uapi/asm-generic/errno.h
+ */
+enum {
+ ENOENT_LNX = ENOENT,
+ ENXIO_LNX = ENXIO,
+ ENOMEM_LNX = ENOMEM,
+ EBUSY_LNX = EBUSY,
+ EXDEV_LNX = EXDEV,
+ ENODEV_LNX = ENODEV,
+ EINVAL_LNX = EINVAL,
+ ENOSPC_LNX = ENOSPC,
+ EPIPE_LNX = EPIPE,
+ ETIME_LNX = 62,
+ ENOSR_LNX = 63,
+ ECOMM_LNX = 70,
+ EPROTO_LNX = 71,
+ EOVERFLOW_LNX = 75,
+ EILSEQ_LNX = 84,
+ ECONNRESET_LNX = 104,
+ ESHUTDOWN_LNX = 108,
+ ETIMEDOUT_LNX = 110,
+ EINPROGRESS_LNX = 115,
+ EREMOTEIO_LNX = 121,
+};
+
+static_assert(ENOENT == 2, "assert");
+static_assert(ENXIO == 6, "assert");
+static_assert(ENOMEM == 12, "assert");
+static_assert(EBUSY == 16, "assert");
+static_assert(EXDEV == 18, "assert");
+static_assert(ENODEV == 19, "assert");
+static_assert(EINVAL == 22, "assert");
+static_assert(ENOSPC == 28, "assert");
+static_assert(EPIPE == 32, "assert");
+static_assert(ETIME_LNX != ETIME, "assert");
+static_assert(ENOSR_LNX != ENOSR, "assert");
+//static_assert(ECOMM_LNX != ECOMM, "assert");
+static_assert(EPROTO_LNX != EPROTO, "assert");
+static_assert(EOVERFLOW_LNX != EOVERFLOW, "assert");
+static_assert(EILSEQ_LNX != EILSEQ, "assert");
+static_assert(ECONNRESET_LNX != ECONNRESET, "assert");
+//static_assert(ESHUTDOWN_LNX != ESHUTDOWN, "assert");
+static_assert(ETIMEDOUT_LNX != ETIMEDOUT, "assert");
+static_assert(EINPROGRESS_LNX != EINPROGRESS, "assert");
+//static_assert(EREMOTEIO_LNX != EREMOTEIO, "assert");
+
+/*
+ * See:
+ * /doc/Documentation/usb/error-codes.txt
+ * winddk, usb.h
+ */
USBD_STATUS
to_usbd_status(int usbip_status)
{
- switch (usbip_status) {
+ switch (abs(usbip_status)) {
case 0:
return USBD_STATUS_SUCCESS;
- /* I guess it */
- case -EPIPE:
- return USBD_STATUS_STALL_PID;
- case -EOVERFLOW:
- return USBD_STATUS_DATA_OVERRUN;
- case -EREMOTEIO:
+ case EPIPE_LNX:
+ return USBD_STATUS_STALL_PID; /* USBD_STATUS_ENDPOINT_HALTED */
+ case EREMOTEIO_LNX:
return USBD_STATUS_ERROR_SHORT_TRANSFER;
- default:
- return USBD_STATUS_ERROR;
+ case ETIME_LNX:
+ return USBD_STATUS_DEV_NOT_RESPONDING;
+ case ETIMEDOUT_LNX:
+ return USBD_STATUS_TIMEOUT;
+ case ENOENT_LNX:
+ case ECONNRESET_LNX:
+ return USBD_STATUS_CANCELED;
+ case EINPROGRESS_LNX:
+ return USBD_STATUS_PENDING;
+ case EOVERFLOW_LNX:
+ return USBD_STATUS_BABBLE_DETECTED;
+ case ENODEV_LNX:
+ case ESHUTDOWN_LNX:
+ return USBD_STATUS_DEVICE_GONE;
+ case EILSEQ_LNX:
+ return USBD_STATUS_CRC;
+ case ECOMM_LNX:
+ return USBD_STATUS_DATA_OVERRUN;
+ case ENOSR_LNX:
+ return USBD_STATUS_DATA_UNDERRUN;
+ case ENOMEM_LNX:
+ return USBD_STATUS_INSUFFICIENT_RESOURCES;
+ case EPROTO_LNX:
+ return USBD_STATUS_BTSTUFF; /* USBD_STATUS_INTERNAL_HC_ERROR */
+ case ENOSPC_LNX:
+ return USBD_STATUS_NO_BANDWIDTH;
+ case EXDEV_LNX:
+ return USBD_STATUS_ISOCH_REQUEST_FAILED;
+ case ENXIO_LNX:
+ return USBD_STATUS_INTERNAL_HC_ERROR;
}
+
+ return USBD_STATUS_INVALID_PARAMETER;
}
+/*
+ * See:
+ * winddk, usb.h
+ * /doc/Documentation/usb/error-codes.txt
+ */
int
to_usbip_status(USBD_STATUS status)
{
switch (status) {
- case 0:
+ case USBD_STATUS_SUCCESS:
return 0;
case USBD_STATUS_STALL_PID:
- return -EPIPE;
- default:
- return -1;
+ case USBD_STATUS_ENDPOINT_HALTED:
+ return -EPIPE_LNX;
+ case USBD_STATUS_ERROR_SHORT_TRANSFER:
+ return -EREMOTEIO_LNX;
+ case USBD_STATUS_TIMEOUT:
+ return -ETIMEDOUT_LNX; /* ETIME */
+ case USBD_STATUS_CANCELED:
+ return -ECONNRESET_LNX; /* ENOENT */
+ case USBD_STATUS_PENDING:
+ return -EINPROGRESS_LNX;
+ case USBD_STATUS_BABBLE_DETECTED:
+ return -EOVERFLOW_LNX;
+ case USBD_STATUS_DEVICE_GONE:
+ return -ENODEV_LNX;
+ case USBD_STATUS_CRC:
+ return -EILSEQ_LNX;
+ case USBD_STATUS_DATA_OVERRUN:
+ return -ECOMM_LNX;
+ case USBD_STATUS_DATA_UNDERRUN:
+ return -ENOSR_LNX;
+ case USBD_STATUS_INSUFFICIENT_RESOURCES:
+ return -ENOMEM_LNX;
+ case USBD_STATUS_BTSTUFF:
+ case USBD_STATUS_INTERNAL_HC_ERROR:
+ case USBD_STATUS_HUB_INTERNAL_ERROR:
+ case USBD_STATUS_DEV_NOT_RESPONDING:
+ return -EPROTO_LNX;
+ case USBD_STATUS_ERROR_BUSY:
+ return -EBUSY_LNX;
}
+
+ return status < 0 ? -EINVAL_LNX : 0; /* see USBD_ERROR */
}
-#define URB_SHORT_NOT_OK 0x0001
-#define URB_ISO_ASAP 0x0002
-#define URB_DIR_IN 0x0200
+/*
+* , urb->transfer_flags
+*/
+enum {
+ URB_SHORT_NOT_OK = 0x0001,
+ URB_ISO_ASAP = 0x0002,
+ URB_DIR_IN = 0x0200
+};
+
+ /*
+ TransferFlags
+ Specifies zero, one, or a combination of the following flags:
+
+ USBD_TRANSFER_DIRECTION_IN
+ Is set to request data from a device. To transfer data to a device, this flag must be clear.
+ USBD_SHORT_TRANSFER_OK
+ This flag should not be set unless USBD_TRANSFER_DIRECTION_IN is also set.
+
+ USBD_START_ISO_TRANSFER_ASAP
+ Causes the transfer to begin on the next frame, if no transfers have been submitted to the pipe
+ since the pipe was opened or last reset. Otherwise, the transfer begins on the first frame that
+ follows all currently queued requests for the pipe. The actual frame that the transfer begins on
+ will be adjusted for bus latency by the host controller driver.
+
+ For control endpoints:
+ 1.Direction in endpoint address or transfer flags should be ignored
+ 2.Direction is determined by bits of bmRequestType in the Setup packet (D7 Data Phase Transfer Direction)
+ */
ULONG
to_usbd_flags(int flags)
{
- ULONG usbd_flags = 0;
-
- if (flags & URB_SHORT_NOT_OK)
- usbd_flags |= USBD_SHORT_TRANSFER_OK;
- if (flags & URB_ISO_ASAP)
- usbd_flags |= USBD_START_ISO_TRANSFER_ASAP;
- if (flags & URB_DIR_IN)
- usbd_flags |= USBD_TRANSFER_DIRECTION_IN;
- return usbd_flags;
+ ULONG TransferFlags = 0;
+
+ if (flags & URB_DIR_IN) {
+ TransferFlags |= USBD_TRANSFER_DIRECTION_IN;
+ if (!(flags & URB_SHORT_NOT_OK)) {
+ TransferFlags |= USBD_SHORT_TRANSFER_OK;
+ }
+ } else {
+ static_assert(!USBD_TRANSFER_DIRECTION_OUT, "assert");
+ }
+
+ if (flags & URB_ISO_ASAP) {
+ TransferFlags |= USBD_START_ISO_TRANSFER_ASAP;
+ }
+
+ return TransferFlags;
}
void
-to_usbd_iso_descs(ULONG n_pkts, USBD_ISO_PACKET_DESCRIPTOR *usbd_iso_descs, struct usbip_iso_packet_descriptor *iso_descs, BOOLEAN as_result)
+to_usbd_iso_descs(ULONG n_pkts, USBD_ISO_PACKET_DESCRIPTOR *usbd_iso_desc, const struct usbip_iso_packet_descriptor *iso_desc, BOOLEAN as_result)
{
- USBD_ISO_PACKET_DESCRIPTOR *usbd_iso_desc;
- struct usbip_iso_packet_descriptor *iso_desc;
- ULONG i;
-
- usbd_iso_desc = usbd_iso_descs;
- iso_desc = iso_descs;
- for (i = 0; i < n_pkts; i++) {
- usbd_iso_desc->Offset = iso_desc->offset;
+ for (ULONG i = 0; i < n_pkts; ++i) {
+
+ USBD_ISO_PACKET_DESCRIPTOR *dest = usbd_iso_desc + i;
+ const struct usbip_iso_packet_descriptor *src = iso_desc + i;
+
+ dest->Offset = src->offset;
if (as_result) {
- usbd_iso_desc->Length = iso_desc->actual_length;
- usbd_iso_desc->Status = to_usbd_status(iso_desc->status);
+ dest->Length = src->actual_length;
+ dest->Status = to_usbd_status(src->status);
}
- usbd_iso_desc++;
- iso_desc++;
}
}
void
-to_iso_descs(ULONG n_pkts, struct usbip_iso_packet_descriptor *iso_descs, USBD_ISO_PACKET_DESCRIPTOR *usbd_iso_descs, BOOLEAN as_result)
+to_iso_descs(ULONG n_pkts, struct usbip_iso_packet_descriptor *iso_desc, const USBD_ISO_PACKET_DESCRIPTOR *usbd_iso_desc, BOOLEAN as_result)
{
- USBD_ISO_PACKET_DESCRIPTOR *usbd_iso_desc;
- struct usbip_iso_packet_descriptor *iso_desc;
- ULONG i;
-
- iso_desc = iso_descs;
- usbd_iso_desc = usbd_iso_descs;
- for (i = 0; i < n_pkts; i++) {
- iso_desc->offset = usbd_iso_desc->Offset;
+ for (ULONG i = 0; i < n_pkts; ++i) {
+
+ struct usbip_iso_packet_descriptor *dest = iso_desc + i;
+ const USBD_ISO_PACKET_DESCRIPTOR *src = usbd_iso_desc + i;
+
+ dest->offset = src->Offset;
if (as_result) {
- iso_desc->actual_length = usbd_iso_desc->Length;
- iso_desc->status = to_usbip_status(usbd_iso_desc->Status);
+ dest->actual_length = src->Length;
+ dest->status = to_usbip_status(src->Status);
}
- usbd_iso_desc++;
- iso_desc++;
}
}
ULONG
-get_iso_descs_len(ULONG n_pkts, struct usbip_iso_packet_descriptor *iso_descs, BOOLEAN is_actual)
+get_iso_descs_len(ULONG n_pkts, const struct usbip_iso_packet_descriptor *iso_desc, BOOLEAN is_actual)
{
- ULONG len = 0;
- struct usbip_iso_packet_descriptor *iso_desc = iso_descs;
- ULONG i;
-
- for (i = 0; i < n_pkts; i++) {
- len += (is_actual ? iso_desc->actual_length: iso_desc->length);
- iso_desc++;
+ ULONG len = 0;
+
+ for (ULONG i = 0; i < n_pkts; ++i) {
+ const struct usbip_iso_packet_descriptor *pkt = iso_desc + i;
+ len += is_actual ? pkt->actual_length: pkt->length;
}
+
return len;
}
ULONG
-get_usbd_iso_descs_len(ULONG n_pkts, USBD_ISO_PACKET_DESCRIPTOR *usbd_iso_descs)
+get_usbd_iso_descs_len(ULONG n_pkts, const USBD_ISO_PACKET_DESCRIPTOR *usbd_iso_desc)
{
- ULONG len = 0;
- USBD_ISO_PACKET_DESCRIPTOR *usbd_iso_desc = usbd_iso_descs;
- ULONG i;
+ ULONG len = 0;
- for (i = 0; i < n_pkts; i++) {
- len += usbd_iso_desc->Length;
- usbd_iso_desc++;
+ for (ULONG i = 0; i < n_pkts; ++i) {
+ len += usbd_iso_desc[i].Length;
+
}
+
return len;
-}
\ No newline at end of file
+}
diff --git a/driver/lib/usbd_helper.h b/driver/lib/usbd_helper.h
index 31b8586c..244a2f8e 100644
--- a/driver/lib/usbd_helper.h
+++ b/driver/lib/usbd_helper.h
@@ -9,10 +9,11 @@ int to_usbip_status(USBD_STATUS usbd_status);
ULONG to_usbd_flags(int flags);
void to_usbd_iso_descs(ULONG n_pkts, USBD_ISO_PACKET_DESCRIPTOR *usbd_iso_descs,
- struct usbip_iso_packet_descriptor *iso_descs, BOOLEAN as_result);
-void to_iso_descs(ULONG n_pkts, struct usbip_iso_packet_descriptor *iso_descs, USBD_ISO_PACKET_DESCRIPTOR *usbd_iso_descs, BOOLEAN as_result);
+ const struct usbip_iso_packet_descriptor *iso_descs, BOOLEAN as_result);
-ULONG get_iso_descs_len(ULONG n_pkts, struct usbip_iso_packet_descriptor *iso_descs, BOOLEAN is_actual);
-ULONG get_usbd_iso_descs_len(ULONG n_pkts, USBD_ISO_PACKET_DESCRIPTOR *usbd_iso_descs);
+void to_iso_descs(ULONG n_pkts, struct usbip_iso_packet_descriptor *iso_descs, const USBD_ISO_PACKET_DESCRIPTOR *usbd_iso_descs, BOOLEAN as_result);
-#define USB_REQUEST_RESET_PIPE 0xfe
\ No newline at end of file
+ULONG get_iso_descs_len(ULONG n_pkts, const struct usbip_iso_packet_descriptor *iso_descs, BOOLEAN is_actual);
+ULONG get_usbd_iso_descs_len(ULONG n_pkts, const USBD_ISO_PACKET_DESCRIPTOR *usbd_iso_descs);
+
+enum { USB_REQUEST_RESET_PIPE = 0xfe };
diff --git a/driver/stub/stub_usbd.c b/driver/stub/stub_usbd.c
index 2eb22278..c7670259 100644
--- a/driver/stub/stub_usbd.c
+++ b/driver/stub/stub_usbd.c
@@ -359,6 +359,30 @@ reset_pipe(usbip_stub_dev_t *devstub, USBD_PIPE_HANDLE hPipe)
return FALSE;
}
+int
+set_feature(usbip_stub_dev_t *devstub, USHORT func, USHORT feature, USHORT index)
+{
+ URB urb;
+ NTSTATUS status;
+
+ urb.UrbHeader.Function = func;
+ urb.UrbHeader.Length = sizeof(struct _URB_CONTROL_FEATURE_REQUEST);
+ urb.UrbControlFeatureRequest.FeatureSelector = feature;
+ urb.UrbControlFeatureRequest.Index = index;
+ /* should be NULL. If not, usbd returns STATUS_INVALID_PARAMETER */
+ urb.UrbControlFeatureRequest.UrbLink = NULL;
+ status = call_usbd(devstub, &urb);
+ if (NT_SUCCESS(status))
+ return 0;
+ /*
+ * TODO: Only applied to this routine beause it's unclear that the status is
+ * unsuccessful when a device is stalled.
+ */
+ if (status == STATUS_UNSUCCESSFUL && urb.UrbHeader.Status == USBD_STATUS_STALL_PID)
+ return to_usbip_status(urb.UrbHeader.Status);
+ return -1;
+}
+
BOOLEAN
submit_class_vendor_req(usbip_stub_dev_t *devstub, BOOLEAN is_in, USHORT cmd, UCHAR reservedBits, UCHAR request, USHORT value, USHORT index, PVOID data, PULONG plen)
{
diff --git a/driver/stub/stub_usbd.h b/driver/stub/stub_usbd.h
index d41c48ec..07e303ff 100644
--- a/driver/stub/stub_usbd.h
+++ b/driver/stub/stub_usbd.h
@@ -13,6 +13,7 @@ BOOLEAN select_usb_conf(usbip_stub_dev_t *devstub, USHORT idx);
BOOLEAN select_usb_intf(usbip_stub_dev_t *devstub, UCHAR intf_num, USHORT alt_setting);
BOOLEAN reset_pipe(usbip_stub_dev_t *devstub, USBD_PIPE_HANDLE hPipe);
+int set_feature(usbip_stub_dev_t *devstub, USHORT func, USHORT feature, USHORT index);
BOOLEAN submit_class_vendor_req(usbip_stub_dev_t *devstub, BOOLEAN is_in, USHORT cmd,
UCHAR rv, UCHAR request, USHORT value, USHORT index, PVOID data, PULONG plen);
diff --git a/driver/stub/stub_write.c b/driver/stub/stub_write.c
index ad3cb0a8..cfb8eccc 100644
--- a/driver/stub/stub_write.c
+++ b/driver/stub/stub_write.c
@@ -126,6 +126,33 @@ process_clear_feature(usbip_stub_dev_t *devstub, unsigned int seqnum, usb_cspkt_
}
}
+static void
+process_set_feature(usbip_stub_dev_t *devstub, unsigned int seqnum, usb_cspkt_t *csp)
+{
+ int res;
+
+ DBGI(DBG_READWRITE, "set_feature: %s\n", dbg_cspkt_recipient(CSPKT_RECIPIENT(csp)));
+
+ switch (CSPKT_RECIPIENT(csp)) {
+ case BMREQUEST_TO_DEVICE:
+ res = set_feature(devstub, URB_FUNCTION_SET_FEATURE_TO_DEVICE, csp->wValue.W, csp->wIndex.W);
+ break;
+ case BMREQUEST_TO_ENDPOINT:
+ res = set_feature(devstub, URB_FUNCTION_SET_FEATURE_TO_ENDPOINT, csp->wValue.W, csp->wIndex.W);
+ break;
+ default:
+ DBGE(DBG_READWRITE, "set_feature: not supported: %s\n", dbg_cspkt_recipient(CSPKT_RECIPIENT(csp)));
+ reply_stub_req_err(devstub, USBIP_RET_SUBMIT, seqnum, -1);
+ return;
+ }
+ if (res == 0)
+ reply_stub_req_hdr(devstub, USBIP_RET_SUBMIT, seqnum);
+ else {
+ DBGI(DBG_READWRITE, "failed to set feature\n");
+ reply_stub_req_err(devstub, USBIP_RET_SUBMIT, seqnum, res);
+ }
+}
+
static void
process_select_conf(usbip_stub_dev_t *devstub, unsigned int seqnum, usb_cspkt_t *csp)
{
@@ -157,6 +184,9 @@ process_standard_request(usbip_stub_dev_t *devstub, unsigned int seqnum, usb_csp
case USB_REQUEST_CLEAR_FEATURE:
process_clear_feature(devstub, seqnum, csp);
break;
+ case USB_REQUEST_SET_FEATURE:
+ process_set_feature(devstub, seqnum, csp);
+ break;
case USB_REQUEST_SET_CONFIGURATION:
process_select_conf(devstub, seqnum, csp);
break;
diff --git a/driver/stub/usbip_stub.vcxproj b/driver/stub/usbip_stub.vcxproj
index d607c4e1..177574d3 100644
--- a/driver/stub/usbip_stub.vcxproj
+++ b/driver/stub/usbip_stub.vcxproj
@@ -138,6 +138,7 @@
true
..\..\include;..\lib;$(IntDir);%(AdditionalIncludeDirectories)
true
+ _NO_CRT_STDIO_INLINE;%(PreprocessorDefinitions)
$(DDK_LIB_PATH)wdmsec.lib;$(DDK_LIB_PATH)ntstrsafe.lib;$(DDK_LIB_PATH)usbd.lib;$(DDK_LIB_PATH)usbdex.lib;%(AdditionalDependencies)
@@ -154,6 +155,7 @@
true
..\..\include;..\lib;$(IntDir);%(AdditionalIncludeDirectories)
true
+ _NO_CRT_STDIO_INLINE;%(PreprocessorDefinitions)
$(DDK_LIB_PATH)wdmsec.lib;$(DDK_LIB_PATH)ntstrsafe.lib;$(DDK_LIB_PATH)usbd.lib;$(DDK_LIB_PATH)usbdex.lib;%(AdditionalDependencies)
@@ -170,6 +172,7 @@
true
..\..\include;..\lib;$(IntDir);%(AdditionalIncludeDirectories)
true
+ _NO_CRT_STDIO_INLINE;%(PreprocessorDefinitions)
$(DDK_LIB_PATH)wdmsec.lib;$(DDK_LIB_PATH)ntstrsafe.lib;$(DDK_LIB_PATH)usbd.lib;$(DDK_LIB_PATH)usbdex.lib;%(AdditionalDependencies)
@@ -190,6 +193,7 @@
..\..\include;..\lib;$(IntDir);%(AdditionalIncludeDirectories)
true
true
+ _NO_CRT_STDIO_INLINE;%(PreprocessorDefinitions)
$(DDK_LIB_PATH)wdmsec.lib;$(DDK_LIB_PATH)ntstrsafe.lib;$(DDK_LIB_PATH)usbd.lib;$(DDK_LIB_PATH)usbdex.lib;%(AdditionalDependencies)
diff --git a/driver/vhci/usbip_root.inf b/driver/vhci/usbip_root.inf
index 0150ea69..b274de71 100644
--- a/driver/vhci/usbip_root.inf
+++ b/driver/vhci/usbip_root.inf
@@ -11,6 +11,8 @@ ClassGuid={4D36E97D-E325-11CE-BFC1-08002BE10318}
Provider=%Provider%
CatalogFile=usbip_vhci.cat
+PnpLockDown=1
+
[Manufacturer]
%StdMfg%=Standard,NT$ARCH$
@@ -46,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 90381ecc..7c27f9c0 100644
--- a/driver/vhci/usbip_vhci.inf
+++ b/driver/vhci/usbip_vhci.inf
@@ -11,12 +11,14 @@ ClassGuid={36FC9E60-C465-11CF-8056-444553540000}
Provider=%Provider%
CatalogFile=usbip_vhci.cat
+PnpLockDown=1
+
[Manufacturer]
%StdMfg%=Standard,NT$ARCH$
[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
@@ -36,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
@@ -47,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
@@ -60,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 c2f10efd..b8a0f446 100644
--- a/driver/vhci/usbip_vhci.vcxproj
+++ b/driver/vhci/usbip_vhci.vcxproj
@@ -164,6 +164,7 @@
true
..\..\include;..\lib;$(UM_IncludePath);$(IntDir);%(AdditionalIncludeDirectories)
true
+ _NO_CRT_STDIO_INLINE;%(PreprocessorDefinitions)
$(DDK_LIB_PATH)wdmsec.lib;$(DDK_LIB_PATH)ntstrsafe.lib;usbd.lib;%(AdditionalDependencies)
@@ -184,6 +185,7 @@
true
..\..\include;..\lib;$(UM_IncludePath);$(IntDir);%(AdditionalIncludeDirectories)
true
+ _NO_CRT_STDIO_INLINE;%(PreprocessorDefinitions)
$(DDK_LIB_PATH)wdmsec.lib;$(DDK_LIB_PATH)ntstrsafe.lib;usbd.lib;%(AdditionalDependencies)
@@ -205,6 +207,7 @@
..\..\include;..\lib;$(UM_IncludePath);$(IntDir);%(AdditionalIncludeDirectories)
true
/driver:aaaa\temp\
+ _NO_CRT_STDIO_INLINE;%(PreprocessorDefinitions)
$(DDK_LIB_PATH)wdmsec.lib;$(DDK_LIB_PATH)ntstrsafe.lib;usbd.lib;%(AdditionalDependencies)
@@ -212,9 +215,7 @@
0.3.4.0
-
- sha256
-
+
gencat.bat $(OutDir) 10_$(DDKPlatform) $(SolutionDir)/driver/usbip_test.pfx
@@ -229,6 +230,7 @@
..\..\include;..\lib;$(UM_IncludePath);$(IntDir);%(AdditionalIncludeDirectories)
true
true
+ _NO_CRT_STDIO_INLINE;%(PreprocessorDefinitions)
$(DDK_LIB_PATH)wdmsec.lib;$(DDK_LIB_PATH)ntstrsafe.lib;usbd.lib;%(AdditionalDependencies)
@@ -236,9 +238,7 @@
0.3.4.0
-
- sha256
-
+
gencat.bat $(OutDir) 10_$(DDKPlatform) $(SolutionDir)/driver/usbip_test.pfx
@@ -255,4 +255,4 @@
-
+
\ No newline at end of file
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_ioctl_vhci.c b/driver/vhci/vhci_ioctl_vhci.c
index 8c1820f2..ec94148e 100644
--- a/driver/vhci/vhci_ioctl_vhci.c
+++ b/driver/vhci/vhci_ioctl_vhci.c
@@ -91,7 +91,7 @@ vhci_ioctl_vhci(pvhci_dev_t vhci, PIO_STACK_LOCATION irpstack, ULONG ioctl_code,
switch (ioctl_code) {
case IOCTL_USBIP_VHCI_PLUGIN_HARDWARE:
status = vhci_plugin_vpdo(vhci, (pvhci_pluginfo_t)buffer, inlen, irpstack->FileObject);
- *poutlen = 0;
+ *poutlen = sizeof(vhci_pluginfo_t);
break;
case IOCTL_USBIP_VHCI_GET_PORTS_STATUS:
if (*poutlen == sizeof(ioctl_usbip_vhci_get_ports_status))
diff --git a/driver/vhci/vhci_plugin.c b/driver/vhci/vhci_plugin.c
index 623f43a4..4987f9b0 100644
--- a/driver/vhci/vhci_plugin.c
+++ b/driver/vhci/vhci_plugin.c
@@ -9,7 +9,7 @@
#include "usb_util.h"
#include "usbip_proto.h"
-extern BOOLEAN vhub_is_empty_port(pvhub_dev_t vhub, ULONG port);
+extern CHAR vhub_get_empty_port(pvhub_dev_t vhub);
extern void vhub_attach_vpdo(pvhub_dev_t vhub, pvpdo_dev_t vpdo);
extern void vhub_mark_unplugged_all_vpdos(pvhub_dev_t vhub);
@@ -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;
+ }
}
}
@@ -119,14 +122,11 @@ vhci_plugin_vpdo(pvhci_dev_t vhci, pvhci_pluginfo_t pluginfo, ULONG inlen, PFILE
DBGE(DBG_IOCTL, "invalid pluginfo format: %lld != %lld", inlen, sizeof(vhci_pluginfo_t) + *pdscr_fullsize - 9);
return STATUS_INVALID_PARAMETER;
}
-
- DBGI(DBG_VPDO, "Plugin vpdo: port: %hhd\n", pluginfo->port);
-
+ pluginfo->port = vhub_get_empty_port(VHUB_FROM_VHCI(vhci));
if (pluginfo->port < 0)
- return STATUS_INVALID_PARAMETER;
+ return STATUS_END_OF_FILE;
- if (!vhub_is_empty_port(VHUB_FROM_VHCI(vhci), pluginfo->port))
- return STATUS_INVALID_PARAMETER;
+ DBGI(DBG_VPDO, "Plugin vpdo: port: %hhd\n", pluginfo->port);
if ((devobj = vdev_create(TO_DEVOBJ(vhci)->DriverObject, VDEV_VPDO)) == NULL)
return STATUS_UNSUCCESSFUL;
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
+}
diff --git a/driver/vhci/vhci_vhub.c b/driver/vhci/vhci_vhub.c
index e8c594d4..d25d8903 100644
--- a/driver/vhci/vhci_vhub.c
+++ b/driver/vhci/vhci_vhub.c
@@ -3,43 +3,51 @@
#include "vhci_dev.h"
#include "usbip_vhci_api.h"
-PAGEABLE pvpdo_dev_t
-vhub_find_vpdo(pvhub_dev_t vhub, unsigned port)
+static PAGEABLE pvpdo_dev_t
+find_vpdo(pvhub_dev_t vhub, unsigned port)
{
PLIST_ENTRY entry;
- ExAcquireFastMutex(&vhub->Mutex);
-
for (entry = vhub->head_vpdo.Flink; entry != &vhub->head_vpdo; entry = entry->Flink) {
pvpdo_dev_t vpdo = CONTAINING_RECORD(entry, vpdo_dev_t, Link);
if (vpdo->port == port) {
- vdev_add_ref((pvdev_t)vpdo);
- ExReleaseFastMutex(&vhub->Mutex);
return vpdo;
}
}
- ExReleaseFastMutex(&vhub->Mutex);
-
return NULL;
}
-PAGEABLE BOOLEAN
-vhub_is_empty_port(pvhub_dev_t vhub, ULONG port)
+PAGEABLE pvpdo_dev_t
+vhub_find_vpdo(pvhub_dev_t vhub, unsigned port)
{
pvpdo_dev_t vpdo;
- vpdo = vhub_find_vpdo(vhub, port);
- if (vpdo == NULL)
- return TRUE;
- if (vpdo->common.DevicePnPState == SurpriseRemovePending) {
- vdev_del_ref((pvdev_t)vpdo);
- return TRUE;
+ ExAcquireFastMutex(&vhub->Mutex);
+ vpdo = find_vpdo(vhub, port);
+ if (vpdo)
+ vdev_add_ref((pvdev_t)vpdo);
+ ExReleaseFastMutex(&vhub->Mutex);
+
+ return vpdo;
+}
+
+PAGEABLE CHAR
+vhub_get_empty_port(pvhub_dev_t vhub)
+{
+ CHAR i;
+
+ ExAcquireFastMutex(&vhub->Mutex);
+ for (i = 0; i < (CHAR)vhub->n_max_ports; i++) {
+ if (find_vpdo(vhub, i) == NULL) {
+ ExReleaseFastMutex(&vhub->Mutex);
+ return i;
+ }
}
+ ExReleaseFastMutex(&vhub->Mutex);
- vdev_del_ref((pvdev_t)vpdo);
- return FALSE;
+ return -1;
}
PAGEABLE void
diff --git a/driver/vhci_ude/usbip_vhci_ude.inf b/driver/vhci_ude/usbip_vhci_ude.inf
index 74544826..5f4dfcbd 100644
--- a/driver/vhci_ude/usbip_vhci_ude.inf
+++ b/driver/vhci_ude/usbip_vhci_ude.inf
@@ -9,6 +9,7 @@ ClassGuid={36FC9E60-C465-11CF-8056-444553540000}
Provider=%ManufacturerName%
CatalogFile=usbip_vhci_ude.cat
DriverVer=
+PnpLockDown=1
[DestinationDirs]
DefaultDestDir = 12
diff --git a/driver/vhci_ude/usbip_vhci_ude.vcxproj b/driver/vhci_ude/usbip_vhci_ude.vcxproj
index 229eb395..3dbec4ee 100644
--- a/driver/vhci_ude/usbip_vhci_ude.vcxproj
+++ b/driver/vhci_ude/usbip_vhci_ude.vcxproj
@@ -47,6 +47,7 @@
+
@@ -258,4 +259,4 @@
-
+
\ No newline at end of file
diff --git a/driver/vhci_ude/usbip_vhci_ude.vcxproj.filters b/driver/vhci_ude/usbip_vhci_ude.vcxproj.filters
index 01bb7f78..e4eaaae9 100644
--- a/driver/vhci_ude/usbip_vhci_ude.vcxproj.filters
+++ b/driver/vhci_ude/usbip_vhci_ude.vcxproj.filters
@@ -123,6 +123,9 @@
Source Files
+
+ Source Files
+
diff --git a/driver/vhci_ude/vhci_dbg.h b/driver/vhci_ude/vhci_dbg.h
index 24064bca..81b7be2c 100644
--- a/driver/vhci_ude/vhci_dbg.h
+++ b/driver/vhci_ude/vhci_dbg.h
@@ -23,4 +23,4 @@ extern const char *dbg_urbr(purb_req_t urbr);
#ifndef DBG
const char *dbg_usbd_status(USBD_STATUS status);
-#endif
\ No newline at end of file
+#endif
diff --git a/driver/vhci_ude/vhci_dev.h b/driver/vhci_ude/vhci_dev.h
index dbe23917..da2fd62f 100644
--- a/driver/vhci_ude/vhci_dev.h
+++ b/driver/vhci_ude/vhci_dev.h
@@ -5,12 +5,15 @@
EXTERN_C_START
+#define MAX_HUB_20PORTS 12
+#define MAX_HUB_30PORTS 4
+
struct _ctx_vusb;
typedef struct
{
WDFDEVICE hdev;
- ULONG n_max_ports;
+ ULONG n_max_ports, n_used_ports;
WDFQUEUE queue;
struct _ctx_vusb **vusbs;
WDFSPINLOCK spin_lock;
@@ -34,6 +37,9 @@ typedef struct _ctx_vusb
*/
BOOLEAN is_simple_ep_alloc;
BOOLEAN invalid;
+ /* reference count to prevent from removal while being used */
+ ULONG refcnt;
+
// pending req which doesn't find an available urbr
WDFREQUEST pending_req_read;
// a partially transferred urbr
@@ -69,6 +75,7 @@ typedef struct _ctx_vusb
} ctx_vusb_t, *pctx_vusb_t;
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(ctx_vusb_t, TO_VUSB)
+WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(pctx_vusb_t, TO_PVUSB)
typedef struct _ctx_ep
{
@@ -90,15 +97,20 @@ typedef struct _ctx_safe_vusb
{
pctx_vhci_t vhci;
ULONG port;
- pctx_vusb_t vusb;
} ctx_safe_vusb_t, *pctx_safe_vusb_t;
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(ctx_safe_vusb_t, TO_SAFE_VUSB)
+#define TO_SAFE_VUSB_FROM_REQ(req) TO_SAFE_VUSB(WdfRequestGetFileObject(req))
+
#define VUSB_CREATING ((pctx_vusb_t)-1)
-#define VUSB_DELETING ((pctx_vusb_t)1)
-#define VUSB_IS_VALID(vusb) ((vusb) != NULL && (vusb) != VUSB_CREATING && (vusb) != VUSB_DELETING)
+#define VUSB_IS_VALID(vusb) ((vusb) != NULL && (vusb) != VUSB_CREATING && !(vusb)->invalid)
extern NTSTATUS plugout_vusb(pctx_vhci_t vhci, CHAR port);
+extern pctx_vusb_t get_vusb(pctx_vhci_t vhci, ULONG port);
+extern pctx_vusb_t get_vusb_by_req(WDFREQUEST req);
+extern void put_vusb(pctx_vusb_t vusb);
+extern void put_vusb_passively(pctx_vusb_t vusb);
+
EXTERN_C_END
diff --git a/driver/vhci_ude/vhci_hc.c b/driver/vhci_ude/vhci_hc.c
index 0ecb4170..81f112c6 100644
--- a/driver/vhci_ude/vhci_hc.c
+++ b/driver/vhci_ude/vhci_hc.c
@@ -1,8 +1,6 @@
#include "vhci_driver.h"
#include "vhci_hc.tmh"
-#define MAX_HUB_PORTS 16
-
#include "usbip_vhci_api.h"
extern NTSTATUS create_queue_hc(pctx_vhci_t vhci);
@@ -40,8 +38,8 @@ create_ucx_controller(WDFDEVICE hdev)
UDECX_WDF_DEVICE_CONFIG_INIT(&conf, controller_query_usb_capability);
conf.EvtUdecxWdfDeviceReset = controller_reset;
- /* conf.NumberOfUsb30Ports=1 by UDECX_WDF_DEVICE_CONFIG_INIT */
- conf.NumberOfUsb20Ports = MAX_HUB_PORTS;
+ conf.NumberOfUsb30Ports = MAX_HUB_30PORTS;
+ conf.NumberOfUsb20Ports = MAX_HUB_20PORTS;
/* UdecxWdfDeviceAddUsbDeviceEmulation() will fail if NumberOfUsb20Ports or NumberOfUsb30Ports is 0 */
status = UdecxWdfDeviceAddUsbDeviceEmulation(hdev, &conf);
if (NT_ERROR(status)) {
@@ -61,31 +59,13 @@ create_fileobject(_In_ WDFDEVICE hdev, WDFREQUEST req, _In_ WDFFILEOBJECT fo)
TRD(VHCI, "Enter");
svusb->vhci = vhci;
- svusb->vusb = NULL;
+ svusb->port = (ULONG)-1;
WdfRequestComplete(req, STATUS_SUCCESS);
TRD(VHCI, "Leave");
}
-static VOID
-cleanup_fileobject(_In_ WDFFILEOBJECT fo)
-{
- pctx_safe_vusb_t svusb = TO_SAFE_VUSB(fo);
-
- TRD(VHCI, "Enter");
-
- /*
- * Not sure but the vusb maybe already be destroyed.
- * So after checked, proceed to plug out.
- */
- if (svusb->vusb != NULL && svusb->vhci->vusbs[svusb->port] == svusb->vusb) {
- plugout_vusb(svusb->vhci, (CHAR)svusb->port);
- }
-
- TRD(VHCI, "Leave");
-}
-
static PAGEABLE VOID
setup_fileobject(PWDFDEVICE_INIT dinit)
{
@@ -95,7 +75,7 @@ setup_fileobject(PWDFDEVICE_INIT dinit)
PAGED_CODE();
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attrs, ctx_safe_vusb_t);
- WDF_FILEOBJECT_CONFIG_INIT(&conf, create_fileobject, NULL, cleanup_fileobject);
+ WDF_FILEOBJECT_CONFIG_INIT(&conf, create_fileobject, NULL, NULL);
WdfDeviceInitSetFileObjectConfig(dinit, &conf, &attrs);
}
@@ -129,7 +109,8 @@ setup_vhci(pctx_vhci_t vhci)
TRE(VHCI, "failed to create spin lock: %!STATUS!", status);
return FALSE;
}
- vhci->n_max_ports = MAX_HUB_PORTS;
+ vhci->n_max_ports = MAX_HUB_30PORTS + MAX_HUB_20PORTS;
+ vhci->n_used_ports = 0;
vhci->vusbs = ExAllocatePoolWithTag(NonPagedPool, sizeof(pctx_vusb_t) * vhci->n_max_ports, VHCI_POOLTAG);
if (vhci->vusbs == NULL) {
diff --git a/driver/vhci_ude/vhci_ioctl.c b/driver/vhci_ude/vhci_ioctl.c
index 8b7b6394..534bac77 100644
--- a/driver/vhci_ude/vhci_ioctl.c
+++ b/driver/vhci_ude/vhci_ioctl.c
@@ -105,7 +105,7 @@ ioctl_get_imported_devices(WDFQUEUE queue, WDFREQUEST req, size_t outlen)
}
static NTSTATUS
-ioctl_plugin_vusb(WDFQUEUE queue, WDFREQUEST req, size_t inlen)
+ioctl_plugin_vusb(WDFQUEUE queue, WDFREQUEST req, size_t inlen, size_t outlen)
{
pctx_vhci_t vhci;
pvhci_pluginfo_t pluginfo;
@@ -117,6 +117,10 @@ ioctl_plugin_vusb(WDFQUEUE queue, WDFREQUEST req, size_t inlen)
TRE(IOCTL, "too small input length: %lld < %lld", inlen, sizeof(vhci_pluginfo_t));
return STATUS_INVALID_PARAMETER;
}
+ if (outlen < sizeof(vhci_pluginfo_t)) {
+ TRE(IOCTL, "too small output length: %lld < %lld", outlen, sizeof(vhci_pluginfo_t));
+ return STATUS_INVALID_PARAMETER;
+ }
status = WdfRequestRetrieveInputBuffer(req, sizeof(vhci_pluginfo_t), &pluginfo, &len);
if (NT_ERROR(status)) {
TRE(IOCTL, "failed to get pluginfo buffer: %!STATUS!", status);
@@ -128,8 +132,8 @@ ioctl_plugin_vusb(WDFQUEUE queue, WDFREQUEST req, size_t inlen)
return STATUS_INVALID_PARAMETER;
}
vhci = *TO_PVHCI(queue);
- if (pluginfo->port < 0 || (ULONG)pluginfo->port >= vhci->n_max_ports)
- return STATUS_INVALID_PARAMETER;
+
+ WdfRequestSetInformation(req, sizeof(vhci_pluginfo_t));
return plugin_vusb(vhci, req, pluginfo);
}
@@ -160,6 +164,27 @@ ioctl_plugout_vusb(WDFQUEUE queue, WDFREQUEST req, size_t inlen)
return plugout_vusb(vhci, port);
}
+static NTSTATUS
+ioctl_shutdown_vusb(WDFQUEUE queue, WDFREQUEST req)
+{
+ pctx_vhci_t vhci;
+ pctx_vusb_t vusb;
+ NTSTATUS status;
+
+ vusb = get_vusb_by_req(req);
+ if (vusb == NULL) {
+ /* already detached */
+ return STATUS_SUCCESS;
+ }
+
+ vhci = *TO_PVHCI(queue);
+
+ status = plugout_vusb(vhci, (CHAR)vusb->port);
+ put_vusb(vusb);
+
+ return status;
+}
+
VOID
io_device_control(_In_ WDFQUEUE queue, _In_ WDFREQUEST req,
_In_ size_t outlen, _In_ size_t inlen, _In_ ULONG ioctl_code)
@@ -178,11 +203,14 @@ io_device_control(_In_ WDFQUEUE queue, _In_ WDFREQUEST req,
status = ioctl_get_imported_devices(queue, req, outlen);
break;
case IOCTL_USBIP_VHCI_PLUGIN_HARDWARE:
- status = ioctl_plugin_vusb(queue, req, inlen);
+ status = ioctl_plugin_vusb(queue, req, inlen, outlen);
break;
case IOCTL_USBIP_VHCI_UNPLUG_HARDWARE:
status = ioctl_plugout_vusb(queue, req, inlen);
break;
+ case IOCTL_USBIP_VHCI_SHUTDOWN_HARDWARE:
+ status = ioctl_shutdown_vusb(queue, req);
+ break;
default:
if (UdecxWdfDeviceTryHandleUserIoctl((*TO_PVHCI(queue))->hdev, req)) {
TRD(IOCTL, "Leave: handled by Udecx");
diff --git a/driver/vhci_ude/vhci_plugin.c b/driver/vhci_ude/vhci_plugin.c
index b5063614..384ec941 100644
--- a/driver/vhci_ude/vhci_plugin.c
+++ b/driver/vhci_ude/vhci_plugin.c
@@ -94,6 +94,7 @@ setup_vusb(UDECXUSBDEVICE ude_usbdev, pvhci_pluginfo_t pluginfo)
}
vusb->devid = pluginfo->devid;
+ vusb->port = pluginfo->port;
vusb->ude_usbdev = ude_usbdev;
vusb->pending_req_read = NULL;
@@ -101,6 +102,7 @@ setup_vusb(UDECXUSBDEVICE ude_usbdev, pvhci_pluginfo_t pluginfo)
vusb->len_sent_partial = 0;
vusb->seq_num = 0;
vusb->invalid = FALSE;
+ vusb->refcnt = 0;
if (vusb->iSerial > 0 && pluginfo->wserial[0] != L'\0')
vusb->wserial = libdrv_strdupW(pluginfo->wserial);
@@ -266,6 +268,21 @@ get_device_speed(pvhci_pluginfo_t pluginfo)
}
}
+static char
+get_free_port(pctx_vhci_t vhci, BOOLEAN is_usb30)
+{
+ ULONG port_start = is_usb30 ? MAX_HUB_20PORTS: 0;
+ ULONG i;
+
+ for (i = port_start; i != vhci->n_max_ports; i++) {
+ pctx_vusb_t vusb = vhci->vusbs[i];
+ if (vusb == NULL)
+ return (CHAR)i;
+ }
+ /* Never happen */
+ return (CHAR)-1;
+}
+
static pctx_vusb_t
vusb_plugin(pctx_vhci_t vhci, pvhci_pluginfo_t pluginfo)
{
@@ -300,7 +317,10 @@ vusb_plugin(pctx_vhci_t vhci, pvhci_pluginfo_t pluginfo)
vusb->is_simple_ep_alloc = (eptype == UdecxEndpointTypeSimple) ? TRUE : FALSE;
UDECX_USB_DEVICE_PLUG_IN_OPTIONS_INIT(&opts);
- opts.Usb20PortNumber = pluginfo->port + 1;
+ if (speed == UdecxUsbSuperSpeed)
+ opts.Usb30PortNumber = pluginfo->port + 1;
+ else
+ opts.Usb20PortNumber = pluginfo->port + 1;
if (!setup_vusb(ude_usbdev, pluginfo)) {
WdfObjectDelete(ude_usbdev);
@@ -320,6 +340,8 @@ vusb_plugin(pctx_vhci_t vhci, pvhci_pluginfo_t pluginfo)
return vusb;
}
+#define IS_USB30_PLUGINFO(pluginfo) ((get_device_speed(pluginfo) == UdecxUsbSuperSpeed))
+
NTSTATUS
plugin_vusb(pctx_vhci_t vhci, WDFREQUEST req, pvhci_pluginfo_t pluginfo)
{
@@ -328,11 +350,12 @@ plugin_vusb(pctx_vhci_t vhci, WDFREQUEST req, pvhci_pluginfo_t pluginfo)
WdfSpinLockAcquire(vhci->spin_lock);
- if (vhci->vusbs[pluginfo->port] != NULL) {
+ if (vhci->n_used_ports == vhci->n_max_ports) {
WdfSpinLockRelease(vhci->spin_lock);
- return STATUS_OBJECT_NAME_COLLISION;
+ return STATUS_END_OF_FILE;
}
+ pluginfo->port = get_free_port(vhci, IS_USB30_PLUGINFO(pluginfo));
/* assign a temporary non-null value indicating on-going vusb allocation */
vhci->vusbs[pluginfo->port] = VUSB_CREATING;
WdfSpinLockRelease(vhci->spin_lock);
@@ -341,20 +364,13 @@ plugin_vusb(pctx_vhci_t vhci, WDFREQUEST req, pvhci_pluginfo_t pluginfo)
WdfSpinLockAcquire(vhci->spin_lock);
if (vusb != NULL) {
- WDFFILEOBJECT fo = WdfRequestGetFileObject(req);
- if (fo != NULL) {
- pctx_safe_vusb_t svusb = TO_SAFE_VUSB(fo);
+ pctx_safe_vusb_t svusb = TO_SAFE_VUSB_FROM_REQ(req);
- svusb->vhci = vhci;
- svusb->port = pluginfo->port;
- svusb->vusb = vusb;
- }
- else {
- TRE(PLUGIN, "empty fileobject. setup failed");
- }
+ svusb->port = pluginfo->port;
status = STATUS_SUCCESS;
}
vhci->vusbs[pluginfo->port] = vusb;
+ vhci->n_used_ports++;
WdfSpinLockRelease(vhci->spin_lock);
if ((vusb != NULL) && (vusb->is_simple_ep_alloc)) {
diff --git a/driver/vhci_ude/vhci_plugout.c b/driver/vhci_ude/vhci_plugout.c
index b95bc518..273a626c 100644
--- a/driver/vhci_ude/vhci_plugout.c
+++ b/driver/vhci_ude/vhci_plugout.c
@@ -50,27 +50,19 @@ abort_all_pending_urbrs(pctx_vusb_t vusb)
WdfSpinLockRelease(vusb->spin_lock);
}
-static NTSTATUS
+static void
vusb_plugout(pctx_vusb_t vusb)
{
- NTSTATUS status;
-
/*
* invalidate first to prevent requests from an upper layer.
* If requests are consistently fed into a vusb about to be plugged out,
- * a live deadlock may occur where vusb aborts pending urbs indefinately.
+ * a live deadlock may occur where vusb aborts pending urbs indefinately.
*/
vusb->invalid = TRUE;
abort_pending_req_read(vusb);
abort_all_pending_urbrs(vusb);
- status = UdecxUsbDevicePlugOutAndDelete(vusb->ude_usbdev);
- if (NT_ERROR(status)) {
- vusb->invalid = FALSE;
- TRD(PLUGIN, "failed to plug out: %!STATUS!", status);
- return status;
- }
- return STATUS_SUCCESS;
+ TRD(PLUGIN, "plugged out: port: %d", vusb->port);
}
static NTSTATUS
@@ -80,27 +72,16 @@ plugout_all_vusbs(pctx_vhci_t vhci)
TRD(PLUGIN, "plugging out all the devices!");
- WdfSpinLockAcquire(vhci->spin_lock);
for (i = 0; i < vhci->n_max_ports; i++) {
- NTSTATUS status;
- pctx_vusb_t vusb = vhci->vusbs[i];
+ pctx_vusb_t vusb;
+
+ vusb = get_vusb(vhci, i);
if (vusb == NULL)
continue;
- vhci->vusbs[i] = VUSB_DELETING;
- WdfSpinLockRelease(vhci->spin_lock);
-
- status = vusb_plugout(vusb);
-
- WdfSpinLockAcquire(vhci->spin_lock);
- if (NT_ERROR(status)) {
- vhci->vusbs[i] = vusb;
- WdfSpinLockRelease(vhci->spin_lock);
- return STATUS_UNSUCCESSFUL;
- }
- vhci->vusbs[i] = NULL;
+ vusb_plugout(vusb);
+ put_vusb(vusb);
}
- WdfSpinLockRelease(vhci->spin_lock);
return STATUS_SUCCESS;
}
@@ -109,36 +90,20 @@ NTSTATUS
plugout_vusb(pctx_vhci_t vhci, CHAR port)
{
pctx_vusb_t vusb;
- NTSTATUS status;
if (port < 0)
return plugout_all_vusbs(vhci);
TRD(IOCTL, "plugging out device: port: %u", port);
- WdfSpinLockAcquire(vhci->spin_lock);
-
- vusb = vhci->vusbs[port];
+ vusb = get_vusb(vhci, port);
if (vusb == NULL) {
TRD(PLUGIN, "no matching vusb: port: %u", port);
- WdfSpinLockRelease(vhci->spin_lock);
return STATUS_NO_SUCH_DEVICE;
}
- vhci->vusbs[port] = VUSB_DELETING;
- WdfSpinLockRelease(vhci->spin_lock);
-
- status = vusb_plugout(vusb);
-
- WdfSpinLockAcquire(vhci->spin_lock);
- if (NT_ERROR(status)) {
- vhci->vusbs[port] = vusb;
- WdfSpinLockRelease(vhci->spin_lock);
- return STATUS_UNSUCCESSFUL;
- }
- vhci->vusbs[port] = NULL;
-
- WdfSpinLockRelease(vhci->spin_lock);
+ vusb_plugout(vusb);
+ put_vusb(vusb);
TRD(IOCTL, "completed to plug out: port: %u", port);
diff --git a/driver/vhci_ude/vhci_read.c b/driver/vhci_ude/vhci_read.c
index d7c445ed..e798a2fb 100644
--- a/driver/vhci_ude/vhci_read.c
+++ b/driver/vhci_ude/vhci_read.c
@@ -42,19 +42,21 @@ get_partial_urbr(pctx_vusb_t vusb)
static VOID
req_read_cancelled(WDFREQUEST req_read)
{
- pctx_safe_vusb_t svusb;
pctx_vusb_t vusb;
TRD(READ, "a pending read req cancelled");
- svusb = TO_SAFE_VUSB(WdfRequestGetFileObject(req_read));
- vusb = svusb->vusb;
+ vusb = get_vusb_by_req(req_read);
+ if (vusb != NULL) {
+ WdfSpinLockAcquire(vusb->spin_lock);
+ if (vusb->pending_req_read == req_read) {
+ vusb->pending_req_read = NULL;
+ }
+ WdfSpinLockRelease(vusb->spin_lock);
- WdfSpinLockAcquire(vusb->spin_lock);
- if (vusb->pending_req_read == req_read) {
- vusb->pending_req_read = NULL;
+ /* put_vusb() at <= DISPATCH sometimes causes BSOD */
+ put_vusb_passively(vusb);
}
- WdfSpinLockRelease(vusb->spin_lock);
WdfRequestComplete(req_read, STATUS_CANCELLED);
}
@@ -132,7 +134,6 @@ read_vusb(pctx_vusb_t vusb, WDFREQUEST req)
VOID
io_read(_In_ WDFQUEUE queue, _In_ WDFREQUEST req, _In_ size_t len)
{
- pctx_safe_vusb_t svusb;
pctx_vusb_t vusb;
NTSTATUS status;
@@ -140,15 +141,15 @@ io_read(_In_ WDFQUEUE queue, _In_ WDFREQUEST req, _In_ size_t len)
TRD(READ, "Enter: len: %u", (ULONG)len);
- svusb = TO_SAFE_VUSB(WdfRequestGetFileObject(req));
- vusb = svusb->vusb;
-
- if (vusb->invalid) {
- TRD(READ, "vusb disconnected: port: %u", vusb->port);
+ vusb = get_vusb_by_req(req);
+ if (vusb == NULL) {
+ TRD(READ, "vusb disconnected: port: %u", TO_SAFE_VUSB_FROM_REQ(req)->port);
status = STATUS_DEVICE_NOT_CONNECTED;
}
- else
+ else {
status = read_vusb(vusb, req);
+ put_vusb(vusb);
+ }
if (status != STATUS_PENDING) {
WdfRequestComplete(req, status);
diff --git a/driver/vhci_ude/vhci_urbr_fetch.c b/driver/vhci_ude/vhci_urbr_fetch.c
index 3a3b1a09..bda1d4e6 100644
--- a/driver/vhci_ude/vhci_urbr_fetch.c
+++ b/driver/vhci_ude/vhci_urbr_fetch.c
@@ -98,9 +98,10 @@ handle_urbr_error(purb_req_t urbr, struct usbip_header *hdr)
PURB urb = urbr->u.urb.urb;
urb->UrbHeader.Status = to_usbd_status(hdr->u.ret_submit.status);
- if (urb->UrbHeader.Status == USBD_STATUS_STALL_PID && urbr->ep->vusb->is_simple_ep_alloc) {
+ if (urb->UrbHeader.Status == USBD_STATUS_STALL_PID) {
/*
- * TODO: UDE framework discards URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL for a simple vusb.
+ * TODO: UDE framework seems to discard URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL.
+ * For a simple vusb, such the problem was observed by an usb packet monitoring tool.
* Thus an explicit reset is requested if a STALL occurs.
* This workaround resolved some USB disk problems.
*/
diff --git a/driver/vhci_ude/vhci_urbr_store_control.c b/driver/vhci_ude/vhci_urbr_store_control.c
index 48efd126..341c8ead 100644
--- a/driver/vhci_ude/vhci_urbr_store_control.c
+++ b/driver/vhci_ude/vhci_urbr_store_control.c
@@ -157,7 +157,7 @@ store_urbr_control_transfer_ex(WDFREQUEST req_read, purb_req_t urbr)
RtlCopyMemory(hdr->u.cmd_submit.setup, urb_ctltrans_ex->SetupPacket, 8);
nread = sizeof(struct usbip_header);
- if (!in) {
+ if (!in && urb_ctltrans_ex->TransferBufferLength > 0) {
if (get_read_payload_length(req_read) >= urb_ctltrans_ex->TransferBufferLength) {
PVOID buf = get_buf(urb_ctltrans_ex->TransferBuffer, urb_ctltrans_ex->TransferBufferMDL);
if (buf == NULL) {
diff --git a/driver/vhci_ude/vhci_vusb.c b/driver/vhci_ude/vhci_vusb.c
new file mode 100644
index 00000000..5e82a461
--- /dev/null
+++ b/driver/vhci_ude/vhci_vusb.c
@@ -0,0 +1,88 @@
+#include "vhci_driver.h"
+#include "vhci_vusb.tmh"
+
+pctx_vusb_t
+get_vusb(pctx_vhci_t vhci, ULONG port)
+{
+ pctx_vusb_t vusb;
+
+ WdfSpinLockAcquire(vhci->spin_lock);
+
+ vusb = vhci->vusbs[port];
+ if (vusb == NULL || vusb->invalid) {
+ WdfSpinLockRelease(vhci->spin_lock);
+ return NULL;
+ }
+ vusb->refcnt++;
+
+ WdfSpinLockRelease(vhci->spin_lock);
+
+ return vusb;
+}
+
+pctx_vusb_t
+get_vusb_by_req(WDFREQUEST req)
+{
+ pctx_safe_vusb_t svusb;
+
+ svusb = TO_SAFE_VUSB_FROM_REQ(req);
+ return get_vusb(svusb->vhci, svusb->port);
+}
+
+void
+put_vusb(pctx_vusb_t vusb)
+{
+ pctx_vhci_t vhci = vusb->vhci;
+
+ WdfSpinLockAcquire(vhci->spin_lock);
+
+ ASSERT(vusb->refcnt > 0);
+ vusb->refcnt--;
+ if (vusb->refcnt == 0 && vusb->invalid) {
+ NTSTATUS status;
+
+ vhci->vusbs[vusb->port] = NULL;
+ vhci->n_used_ports--;
+ WdfSpinLockRelease(vhci->spin_lock);
+
+ status = UdecxUsbDevicePlugOutAndDelete(vusb->ude_usbdev);
+ if (NT_ERROR(status)) {
+ TRD(VUSB, "failed to plug out: %!STATUS!", status);
+ }
+ }
+ else {
+ WdfSpinLockRelease(vhci->spin_lock);
+ }
+}
+
+static VOID
+async_put_vusb(WDFWORKITEM workitem)
+{
+ pctx_vusb_t vusb = *TO_PVUSB(workitem);
+
+ TRD(VUSB, "async put vusb");
+ put_vusb(vusb);
+ WdfObjectDelete(workitem);
+}
+
+void
+put_vusb_passively(pctx_vusb_t vusb)
+{
+ WDFWORKITEM workitem;
+ WDF_WORKITEM_CONFIG conf;
+ WDF_OBJECT_ATTRIBUTES attrs;
+ NTSTATUS status;
+
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attrs, pctx_vusb_t);
+ attrs.ParentObject = vusb->ude_usbdev;
+ WDF_WORKITEM_CONFIG_INIT(&conf, async_put_vusb);
+
+ status = WdfWorkItemCreate(&conf, &attrs, &workitem);
+ if (NT_ERROR(status)) {
+ TRW(VUSB, "failed to create a workitem: %!STATUS!", status);
+ }
+ else {
+ *TO_PVUSB(workitem) = vusb;
+ WdfWorkItemEnqueue(workitem);
+ }
+}
\ No newline at end of file
diff --git a/driver/vhci_ude/vhci_write.c b/driver/vhci_ude/vhci_write.c
index ce9d3ae1..12ac2b66 100644
--- a/driver/vhci_ude/vhci_write.c
+++ b/driver/vhci_ude/vhci_write.c
@@ -68,19 +68,25 @@ write_vusb(pctx_vusb_t vusb, WDFREQUEST req_write)
VOID
io_write(_In_ WDFQUEUE queue, _In_ WDFREQUEST req, _In_ size_t len)
{
- pctx_safe_vusb_t svusb;
pctx_vusb_t vusb;
+ NTSTATUS status;
UNREFERENCED_PARAMETER(queue);
TRD(WRITE, "Enter: len: %u", (ULONG)len);
- svusb = TO_SAFE_VUSB(WdfRequestGetFileObject(req));
- vusb = svusb->vusb;
-
- write_vusb(vusb, req);
+ vusb = get_vusb_by_req(req);
+ if (vusb == NULL) {
+ TRD(WRITE, "vusb disconnected: port: %u", TO_SAFE_VUSB_FROM_REQ(req)->port);
+ status = STATUS_DEVICE_NOT_CONNECTED;
+ }
+ else {
+ write_vusb(vusb, req);
+ put_vusb(vusb);
+ status = STATUS_SUCCESS;
+ }
- WdfRequestCompleteWithInformation(req, STATUS_SUCCESS, len);
+ WdfRequestCompleteWithInformation(req, status, len);
TRD(WRITE, "Leave");
-}
\ No newline at end of file
+}
diff --git a/include/usbip_vhci_api.h b/include/usbip_vhci_api.h
index de08592e..6a3d198d 100644
--- a/include/usbip_vhci_api.h
+++ b/include/usbip_vhci_api.h
@@ -52,7 +52,8 @@ DEFINE_GUID(USBIP_NOTIFY_DEVICE_ARRIVAL_EVENT,
#define IOCTL_USBIP_VHCI_PLUGIN_HARDWARE USBIP_VHCI_IOCTL(0x0)
#define IOCTL_USBIP_VHCI_UNPLUG_HARDWARE USBIP_VHCI_IOCTL(0x1)
-/* EJECT(0x2) removed. 0x2 will be used later */
+/* used by attacher */
+#define IOCTL_USBIP_VHCI_SHUTDOWN_HARDWARE USBIP_VHCI_IOCTL(0x2)
#define IOCTL_USBIP_VHCI_GET_PORTS_STATUS USBIP_VHCI_IOCTL(0x3)
#define IOCTL_USBIP_VHCI_GET_IMPORTED_DEVICES USBIP_VHCI_IOCTL(0x4)
diff --git a/userspace/lib/usbip_common.vcxproj b/userspace/lib/usbip_common.vcxproj
index 2d8e3c0f..87c834a0 100644
--- a/userspace/lib/usbip_common.vcxproj
+++ b/userspace/lib/usbip_common.vcxproj
@@ -91,7 +91,7 @@
Level3
Disabled
MultiThreadedDebug
- HAVE_CONFIG_H
+ HAVE_CONFIG_H;_NO_CRT_STDIO_INLINE
..\..\include
true
@@ -102,13 +102,16 @@
+
+ legacy_stdio_definitions.lib;%(AdditionalDependencies)
+
Level3
Disabled
MultiThreadedDebug
- HAVE_CONFIG_H
+ HAVE_CONFIG_H;_NO_CRT_STDIO_INLINE
..\..\include
true
@@ -119,6 +122,9 @@
+
+ legacy_stdio_definitions.lib;%(AdditionalDependencies)
+
@@ -129,13 +135,16 @@
MultiThreaded
..\..\include
true
- HAVE_CONFIG_H
+ HAVE_CONFIG_H;_NO_CRT_STDIO_INLINE
true
true
true
+
+ legacy_stdio_definitions.lib;%(AdditionalDependencies)
+
@@ -145,7 +154,7 @@
true
MultiThreaded
..\..\include
- HAVE_CONFIG_H
+ HAVE_CONFIG_H;_NO_CRT_STDIO_INLINE
true
@@ -153,6 +162,9 @@
true
true
+
+ legacy_stdio_definitions.lib;%(AdditionalDependencies)
+
diff --git a/userspace/src/attacher/attacher.c b/userspace/src/attacher/attacher.c
index f114785c..77f6c936 100644
--- a/userspace/src/attacher/attacher.c
+++ b/userspace/src/attacher/attacher.c
@@ -2,6 +2,7 @@
#include
#include "usbip_forward.h"
+#include "usbip_vhci_api.h"
static HANDLE
read_handle_value(HANDLE hStdin)
@@ -23,6 +24,14 @@ read_handle_value(HANDLE hStdin)
return handle;
}
+static void
+shutdown_device(HANDLE hdev)
+{
+ unsigned long unused;
+
+ DeviceIoControl(hdev, IOCTL_USBIP_VHCI_SHUTDOWN_HARDWARE, NULL, 0, NULL, 0, &unused, NULL);
+}
+
static BOOL
setup_forwarder(void)
{
@@ -36,6 +45,10 @@ setup_forwarder(void)
sockfd = read_handle_value(hStdin);
usbip_forward(hdev, sockfd, FALSE);
+ shutdown_device(hdev);
+
+ CloseHandle(sockfd);
+ CloseHandle(hdev);
CloseHandle(hStdin);
CloseHandle(hStdout);
diff --git a/userspace/src/usbip/usbip_attach.c b/userspace/src/usbip/usbip_attach.c
index 1f14f2df..71904dce 100644
--- a/userspace/src/usbip/usbip_attach.c
+++ b/userspace/src/usbip/usbip_attach.c
@@ -44,7 +44,6 @@ static int
import_device(SOCKET sockfd, pvhci_pluginfo_t pluginfo, HANDLE *phdev)
{
HANDLE hdev;
- int port;
int rc;
hdev = usbip_vhci_driver_open();
@@ -53,26 +52,20 @@ import_device(SOCKET sockfd, pvhci_pluginfo_t pluginfo, HANDLE *phdev)
return ERR_DRIVER;
}
- port = usbip_vhci_get_free_port(hdev);
- if (port < 0) {
- dbg("no free port");
- usbip_vhci_driver_close(hdev);
- return ERR_PORTFULL;
- }
-
- dbg("got free port: %d", port);
-
- pluginfo->port = port;
-
rc = usbip_vhci_attach_device(hdev, pluginfo);
if (rc < 0) {
- dbg("failed to attach device: %d", rc);
+ if (rc == ERR_PORTFULL) {
+ dbg("no free port");
+ }
+ else {
+ dbg("failed to attach device: %d", rc);
+ }
usbip_vhci_driver_close(hdev);
- return ERR_GENERAL;
+ return rc;
}
*phdev = hdev;
- return port;
+ return pluginfo->port;
}
static pvhci_pluginfo_t
diff --git a/userspace/src/usbip/usbip_vhci.c b/userspace/src/usbip/usbip_vhci.c
index cc9ac8c6..2950bc38 100644
--- a/userspace/src/usbip/usbip_vhci.c
+++ b/userspace/src/usbip/usbip_vhci.c
@@ -149,9 +149,12 @@ usbip_vhci_attach_device(HANDLE hdev, pvhci_pluginfo_t pluginfo)
unsigned long unused;
if (!DeviceIoControl(hdev, IOCTL_USBIP_VHCI_PLUGIN_HARDWARE,
- pluginfo, pluginfo->size, NULL, 0, &unused, NULL)) {
+ pluginfo, pluginfo->size, pluginfo, sizeof(vhci_pluginfo_t), &unused, NULL)) {
+ DWORD err = GetLastError();
+ if (err == ERROR_HANDLE_EOF)
+ return ERR_PORTFULL;
dbg("usbip_vhci_attach_device: DeviceIoControl failed: err: 0x%lx", GetLastError());
- return -1;
+ return ERR_GENERAL;
}
return 0;
diff --git a/userspace/src/usbip/usbip_vhci.h b/userspace/src/usbip/usbip_vhci.h
index 4664bdb7..e420efbf 100644
--- a/userspace/src/usbip/usbip_vhci.h
+++ b/userspace/src/usbip/usbip_vhci.h
@@ -7,7 +7,6 @@
HANDLE usbip_vhci_driver_open(void);
void usbip_vhci_driver_close(HANDLE hdev);
-int usbip_vhci_get_free_port(HANDLE hdev);
int usbip_vhci_get_imported_devs(HANDLE hdev, pioctl_usbip_vhci_imported_dev_t *pidevs);
int usbip_vhci_attach_device(HANDLE hdev, pvhci_pluginfo_t pluginfo);
int usbip_vhci_detach_device(HANDLE hdev, int port);