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);