/// <summary>
        /// Cancels an outstanding IO request.
        /// </summary>
        /// <param name="device">The context of the device which is being operated.</param>
        /// <param name="requestId">This value represents the ID of a request previously sent via IO_CONTROL, INTERNAL_IO_CONTROL,
        /// TRANSFER_IN_REQUEST, or TRANSFER_OUT_REQUEST message.</param>
        public void CancelRequest(EusbDeviceContext device, uint requestId)
        {
            Site.Log.Add(LogEntryKind.Debug, "Sending CANCEL_REQUEST. Device: {0}, Request ID {1}.", device, requestId);

            EusbCancelRequestPdu requestPdu = new EusbCancelRequestPdu();
            requestPdu.RequestId = requestId;

            SendPdu(requestPdu, device.VirtualChannel);
        }
        /// <summary>
        /// Submits data to the client USB device.
        /// </summary>
        /// <param name="device">The context of the device which is being operated.</param>
        /// <param name="tsUrb">A TS_URB structure.</param>
        /// <param name="outputBuffer">The raw data to be sent to the device.</param>
        public void TransferOutRequest(EusbDeviceContext device, TS_URB tsUrb, byte[] outputBuffer)
        {
            uint size = null == outputBuffer ? 0 : (uint)outputBuffer.Length;
            Site.Log.Add(
                LogEntryKind.Debug,
                "Sending TRANSFER_OUT_REQUEST. Device: {0}, URB: {1}, Output buffer size: {2}",
                device,
                tsUrb,
                size);

            EusbTransferOutRequestPdu requestPdu = new EusbTransferOutRequestPdu(device.UsbDeviceInterfaceId);
            byte[] buf = PduMarshaler.Marshal(tsUrb);
            requestPdu.CbTsUrb = (uint)buf.Length;

            // TODO: Need verify for negtive test cases?
            if (tsUrb.Header.Size != (ushort)requestPdu.CbTsUrb)
            {
                throw new ArgumentException(String.Format(
                    "Header.Size ({0}) doesn't match the actual size ({1}).",
                    tsUrb.Header.Size,
                    requestPdu.CbTsUrb
                    ));
            }

            requestPdu.TsUrb = buf;
            requestPdu.OutputBuffer = outputBuffer;
            requestPdu.OutputBufferSize = size;
            SendPdu(requestPdu, device.VirtualChannel);
        }
        /// <summary>
        /// Requests data from the client USB device.
        /// </summary>
        /// <param name="device">The context of the device which is being operated.</param>
        /// <param name="tsUrb">A TS_URB structure.</param>
        /// <param name="outputBufferSize">This value represents the maximum number of bytes of data that is requested from
        /// the USB device.</param>
        public void TransferInRequest(EusbDeviceContext device, TS_URB tsUrb, uint outputBufferSize)
        {
            // TODO: Check this verification.
            //Site.Assume.IsFalse(
            //    tsUrb is TS_URB_ISOCH_TRANSFER && device.NoAckIsochWriteJitterBufferSizeInMs,
            //    "The client does not support TS_URB_ISOCH_TRANSFER messages."
            //    );

            Site.Log.Add(
                LogEntryKind.Debug,
                "Sending TRANSFER_IN_REQUEST. Device: {0}, URB: {1}, Output buffer size: {2}",
                device,
                tsUrb,
                outputBufferSize);

            EusbTransferInRequestPdu requestPdu = new EusbTransferInRequestPdu(device.UsbDeviceInterfaceId);
            byte[] buf = PduMarshaler.Marshal(tsUrb);
            requestPdu.CbTsUrb = (uint)buf.Length;

            // TODO: Need verify for negtive test cases?
            if (tsUrb.Header.Size != (ushort)requestPdu.CbTsUrb)
            {
                throw new ArgumentException(String.Format(
                    "Header.Size ({0}) doesn't match the actual size ({1}).",
                    tsUrb.Header.Size,
                    requestPdu.CbTsUrb
                    ));
            }

            requestPdu.TsUrb = buf;
            requestPdu.OutputBufferSize = outputBufferSize;
            SendPdu(requestPdu, device.VirtualChannel);
        }
        /// <summary>
        /// Sends RETRACT_DEVICE request to the client to stop redirecting the USB device.
        /// </summary>
        /// <param name="device">The context of the device which is being operated.</param>
        /// <param name="reason">The reason to stop redirecting the USB device.</param>
        public void RetractDevice(EusbDeviceContext device, USB_RETRACT_REASON reason)
        {
            Site.Log.Add(LogEntryKind.Debug, "Sending RETRACT_DEVICE. Device: {0}", device);

            EusbRetractDevicePdu requestPdu = new EusbRetractDevicePdu(device.UsbDeviceInterfaceId);
            requestPdu.Reason = reason;
            SendPdu(requestPdu, device.VirtualChannel);

            bool channelClosed = IsChannelClosed(device.VirtualChannel);
            Site.Assert.IsTrue(channelClosed, "Expect the channel {0} to be closed.", device.VirtualChannel.ChannelId);
        }
        /// <summary>
        /// Registers a request completion interface on the client for a specific device.
        /// </summary>
        /// <param name="device">The information of the device which is being operated.</param>
        /// <param name="numRequestCompletion">If this field is set to 0x00000001 or greater, then the RequestCompletion field
        /// is also present. If this field is set to 0x0000000, the RequestCompletion field is not present.</param>
        /// <param name="requestCompletion">A unique InterfaceID to be used by all Request Completion messages defined in the
        /// Request Completion Interface.</param>
        public void RegisterCallback(EusbDeviceContext device, uint numRequestCompletion, uint requestCompletion)
        {
            Site.Log.Add(
                LogEntryKind.Debug,
                "Sending REGISTER_REQUEST_CALLBACK. Device: {0}, Num request Completion {1}, Request completion {2}.",
                device,
                numRequestCompletion,
                requestCompletion
                );

            EusbRegisterRequestCallbackPdu requestPdu = new EusbRegisterRequestCallbackPdu(
                device.UsbDeviceInterfaceId,
                numRequestCompletion,
                requestCompletion
                );
            SendPdu(requestPdu, device.VirtualChannel);

            // Record the interface ID for parsing Completion UDPs.
            rdpeusbServer.RequestCompletionInterfaceId = requestCompletion;
        }
        /// <summary>
        /// Queries the USB device's text from the client.
        /// </summary>
        /// <param name="device">The context of the device which is being operated.</param>
        /// <param name="textType">This value represents the type of text to query as described in [MSFT-W2KDDK], Volume 1, 
        /// Part 1, Chapter 2.</param>
        /// <param name="localeId">This value represents the locale of the text to query as described in [MSFT-W2KDDK], 
        /// Volume 1, Part 1, Chapter 2.</param>
        public void QueryDeviceText(EusbDeviceContext device, uint textType, uint localeId)
        {
            Site.Log.Add(LogEntryKind.Debug, "Sending QUERY_DEVICE_TEXT. Device: {0}, Text type: {1}, Locale ID: {2}.", device, textType, localeId);

            EusbQueryDeviceTextRequestPdu requestPdu = new EusbQueryDeviceTextRequestPdu(device.UsbDeviceInterfaceId);
            requestPdu.TextType = textType;
            requestPdu.LocaleId = localeId;
            SendPdu(requestPdu, device.VirtualChannel);

            Site.Log.Add(LogEntryKind.Debug, "Receiving EusbQueryDeviceTextResponsePdu.");
            EusbQueryDeviceTextResponsePdu responsePdu = this.rdpeusbServer.ExpectRdpeusbPdu<EusbQueryDeviceTextResponsePdu>(device.VirtualChannel.ChannelId, waitTime);

            #region Verify QUERY_DEVICE_TEXT_RSP

            Site.Assert.IsNotNull(
                responsePdu,
                "Expect that the response from the client is EusbQueryDeviceTextResponsePdu.");

            Site.Assert.AreEqual<uint>(
                requestPdu.InterfaceId,
                responsePdu.InterfaceId,
                "Expect that the InterfaceId in the response PDU equals the InterfaceId in the request. The actual value is 0x{0:x8}.",
                responsePdu.InterfaceId);

            Site.Assert.AreEqual<uint>(
                requestPdu.MessageId,
                responsePdu.MessageId,
                "Expect that the MessageId in the response PDU equals the MessageId in the request. The actual value is 0x{0:x8}.",
                responsePdu.MessageId);

            Site.Assert.AreEqual<Mask_Values>(
                Mask_Values.STREAM_ID_STUB,
                responsePdu.Mask,
                "Expect that the Mask in the response PDU is STREAM_ID_STUB.");

            Site.Assert.AreNotEqual<uint>(
                0x00000000,
                responsePdu.cchDeviceDescription,
                "Expect that the cchDeviceDescription in the response PDU is not zero. The actual value is 0x{0:x8}.",
                responsePdu.cchDeviceDescription);

            Site.Assert.AreEqual<uint>(
                0x00000000,
                responsePdu.HResult,
                "Expect that the HResult in the response PDU is zero. The actual value is 0x{0:x8}.",
                responsePdu.HResult);

            #endregion
        }
        /// <summary>
        /// Submits an I/O control request to the client USB device.
        /// </summary>
        /// <param name="device">Device instance for this operation</param>
        /// <param name="input">This value represents the input buffer for the IO control request.</param>
        /// <param name="outputBufferSize">The maximum number of bytes the client can return to the server.</param>
        /// <param name="requestId">This ID uniquely identifies the I/O control request.</param>
        public void IoControl(EusbDeviceContext device, UsbIoControlCode ioControlCode, byte[] input, uint outputBufferSize, uint requestId)
        {
            Site.Log.Add(
                LogEntryKind.Debug,
                "Sending IO_CONTROL. Device: {0}, IO control code: {1}, Input buffer size {2}, Output buffer size {3}, Request ID {4}.",
                device,
                ioControlCode,
                null == input ? 0 : input.Length,
                outputBufferSize,
                requestId
                );

            EusbIoControlPdu requestPdu = new EusbIoControlPdu(device.UsbDeviceInterfaceId);
            requestPdu.IoControlCode = ioControlCode;
            if (input == null)
            {
                requestPdu.InputBuffer = null;
                requestPdu.InputBufferSize = 0;
            }
            else
            {
                requestPdu.InputBuffer = (byte[])input;
                requestPdu.InputBufferSize = (uint)input.Length;
            }
            requestPdu.OutputBufferSize = outputBufferSize;
            requestPdu.RequestId = requestId;

            SendPdu(requestPdu, device.VirtualChannel);
        }
        /// <summary>
        /// Receives the ADD_DEVICE request from the client.
        /// </summary>
        /// <param name="channel">The channel to be received from.</param>
        /// <returns>The context of the device which is being added.</returns>
        public EusbDeviceContext ExpectAddDevice(DynamicVirtualChannel channel)
        {
            Site.Log.Add(LogEntryKind.Debug, "Receiving ADD_DEVICE, Channel ID {0}.", channel.ChannelId);

            EusbAddDevicePdu pdu = this.rdpeusbServer.ExpectRdpeusbPdu<EusbAddDevicePdu>(channel.ChannelId, waitTime);
            USB_DEVICE_CAPABILITIES cap = new USB_DEVICE_CAPABILITIES();

            if (!PduMarshaler.Unmarshal(pdu.UsbDeviceCapabilities, cap))
            {
                Site.Assert.Fail(
                    "UsbDeviceCapabilities (size in bytes: {0}) in ADD_DEVICE cannot be decoded.",
                    pdu.UsbDeviceCapabilities.Length
                    );
            }

            #region Verify ADD_DEVICE

            Site.Assert.IsNotNull(
                pdu,
                "Expect that the response from the client is EusbAddDevicePdu."
                );

            Site.Assert.AreEqual<uint>(
                0x00000001,
                pdu.InterfaceId,
                "Expect that the InterfaceId in the response PDU equals 0x00000001. The actual value is 0x{0:x8}.",
                pdu.InterfaceId);

            Site.Assert.AreEqual<Mask_Values>(
                Mask_Values.STREAM_ID_PROXY,
                pdu.Mask,
                "Expect that the Mask in the response PDU is STREAM_ID_PROXY.");

            Site.Assert.AreEqual<FunctionId_Values>(
                FunctionId_Values.ADD_DEVICE,
                (FunctionId_Values)pdu.FunctionId,
                "Expect that the FunctionId in the response PDU is ADD_DEVICE. The actual value is 0x{0:x8}.",
                pdu.FunctionId);

            Site.Assert.AreEqual<uint>(
                0x00000001,
                pdu.NumUsbDevice,
                "Expect that the NumUsbDevice in the response PDU equals 0x00000001. The actual value is 0x{0:x8}.",
                pdu.NumUsbDevice);

            Site.Assert.AreNotEqual<uint>(
                0x00000000,
                pdu.cchDeviceInstanceId,
                "Expect that the cchDeviceInstanceId in the response PDU is zero. The actual value is 0x{0:x8}.",
                pdu.cchDeviceInstanceId);

            if (Config.IsWindowsImplementation)
            {
                Site.Assert.AreNotEqual<uint>(
                    0x00000000,
                    pdu.cchHwIds,
                    "Expect that the cchHwIds in the response PDU is not zero in an implementation of Windows. The actual value is 0x{0:x8}.",
                    pdu.cchHwIds);
            }

            #region Verify UsbDeviceCapabilities

            Site.Assert.AreEqual<uint>(
                (uint)USB_DEVICE_CAPABILITIES.USB_DEVICE_CAPABILITIES_SIZE,
                cap.CbSize,
                "Expect that the CbSize of the USB_DEVICE_CAPABILITIES in the response PDU equals 28. The actual value is {0}.",
                cap.CbSize);

            Site.Assert.IsTrue(
                cap.UsbBusInterfaceVersion == (uint)UsbBusInterfaceVersion_Values.USB_BUS_VERSION_0
                || cap.UsbBusInterfaceVersion == (uint)UsbBusInterfaceVersion_Values.USB_BUS_VERSION_1
                || cap.UsbBusInterfaceVersion == (uint)UsbBusInterfaceVersion_Values.USB_BUS_VERSION_2,
                "Expect that the UsbBusInterfaceVersion of the USB_DEVICE_CAPABILITIES in the response PDU equals 0x00000000 or 0x00000001 or 0x00000002. The actual value is {0}.",
                cap.UsbBusInterfaceVersion);

            Site.Assert.IsTrue(
                cap.USBDI_Version == (uint)USBDI_VER.USBDI_VERSION_5 || cap.USBDI_Version == (uint)USBDI_VER.USBDI_VERSION_6,
                "Expect that the USBDI_Version of the USB_DEVICE_CAPABILITIES in the response PDU equals 0x00000500 or 0x00000600. The actual value is {0}.",
                cap.USBDI_Version);

            Site.Assert.IsTrue(
                cap.Supported_USB_Version == (uint)Supported_USB_Version_Values.USB_1_0
                || cap.Supported_USB_Version == (uint)Supported_USB_Version_Values.USB_1_1
                || cap.Supported_USB_Version == (uint)Supported_USB_Version_Values.USB_2_0,
                "Expect that the Supported_USB_Version of the USB_DEVICE_CAPABILITIES in the response PDU equals 0x100 or 0x110 or 0x200. The actual value is {0}.",
                cap.Supported_USB_Version);

            Site.Assert.AreEqual<uint>(
                0,
                cap.HcdCapabilities,
                "Expect that the HcdCapabilities of the USB_DEVICE_CAPABILITIES in the response PDU is zero. The actual value is {0}.",
                cap.HcdCapabilities);

            if (cap.UsbBusInterfaceVersion == (uint)UsbBusInterfaceVersion_Values.USB_BUS_VERSION_0)
            {
                Site.Assert.AreEqual<uint>(
                    (uint)DeviceSpeed_Values.FULL_SPEED,
                    cap.DeviceIsHighSpeed,
                    "Expect that the DeviceIsHighSpeed of the USB_DEVICE_CAPABILITIES in the response PDU is zero when UsbBusInterfaceVersion is 0x00000000. The actual value is {0}.",
                    cap.HcdCapabilities);
            }
            else
            {
                Site.Assert.IsTrue(
                    cap.DeviceIsHighSpeed == (uint)DeviceSpeed_Values.FULL_SPEED
                    || cap.DeviceIsHighSpeed == (uint)DeviceSpeed_Values.HIGH_SPEED,
                    "Expect that the DeviceIsHighSpeed of the USB_DEVICE_CAPABILITIES in the response PDU equals 0x100 or 0x110 or 0x200. The actual value is {0}.",
                    cap.DeviceIsHighSpeed);
            }

            if (cap.NoAckIsochWriteJitterBufferSizeInMs != 0)
            {
                Site.Assert.IsTrue(
                    cap.NoAckIsochWriteJitterBufferSizeInMs >= 10
                    && cap.NoAckIsochWriteJitterBufferSizeInMs <= 512,
                    "Expect that the NoAckIsochWriteJitterBufferSizeInMs of the USB_DEVICE_CAPABILITIES in the response PDU is greater than or equal to 10 and less than or equal to 512. The actual value is {0}.",
                    cap.NoAckIsochWriteJitterBufferSizeInMs);
            }

            #endregion

            #endregion

            EusbDeviceContext device = new EusbDeviceContext();
            device.VirtualChannel = channel;
            device.NoAckIsochWriteJitterBufferSizeInMs = (cap.NoAckIsochWriteJitterBufferSizeInMs != 0);
            device.UsbDeviceInterfaceId = pdu.UsbDevice;
            device.DeviceInstanceId = pdu.DeviceInstanceId;

            Site.Log.Add(LogEntryKind.Debug, "Received device request. Device: {0}", device);
            return device;
        }
        private bool SelectConfiguration(EusbDeviceContext device, byte configIndex)
        {
            // 6. Sends TS_URB_CONTROL_DESCRIPTOR_REQUEST with the descriptor type of USB_DEVICE_DESCRIPTOR_TYPE.
            uint requestId = IdGenerator.NewId();
            TS_URB_CONTROL_DESCRIPTOR_REQUEST des = new UrbBuilder(
                URB_FUNCTIONID.URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE,
                requestId,
                0).BuildDeviceDescriptorRequest();
            rdpeusbAdapter.TransferInRequest(device, des, USB_DEVICE_DESCRIPTOR.DefaultSize);

            // 7. Receives a completion message with the result for USB_DEVICE_DESCRIPTOR.");
            EusbUrbCompletionPdu pdu = (EusbUrbCompletionPdu)rdpeusbAdapter.ExpectCompletion(device.VirtualChannel);
            if (null == pdu || pdu.HResult != (uint)HRESULT_FROM_WIN32.ERROR_SUCCESS)
            {
                return false;
            }
            USB_DEVICE_DESCRIPTOR desDevice = UsbStructParser.Parse<USB_DEVICE_DESCRIPTOR>(pdu);

            // 8. Sends TS_URB_CONTROL_DESCRIPTOR_REQUEST to retrieve the total length of the configuration.
            requestId = IdGenerator.NewId();
            des = new UrbBuilder(
                URB_FUNCTIONID.URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE,
                requestId,
                0).BuildConfigurationDescriptorRequest(configIndex);
            rdpeusbAdapter.TransferInRequest(device, des, USB_CONFIGURATION_DESCRIPTOR.DefaultSize);

            // 9. Receives a completion message with the result for USB_CONFIGURATION_DESCRIPTOR.");
            pdu = (EusbUrbCompletionPdu)rdpeusbAdapter.ExpectCompletion(device.VirtualChannel);
            if (null == pdu || pdu.HResult != (uint)HRESULT_FROM_WIN32.ERROR_SUCCESS)
            {
                return false;
            }
            USB_CONFIGURATION_DESCRIPTOR desConfig = UsbStructParser.Parse<USB_CONFIGURATION_DESCRIPTOR>(pdu);

            // 10. Sends TS_URB_CONTROL_DESCRIPTOR_REQUEST with the actual length of USB_CONFIGURATION_DESCRIPTOR result.
            requestId = IdGenerator.NewId();
            des = new UrbBuilder(
                URB_FUNCTIONID.URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE,
                requestId,
                0).BuildConfigurationDescriptorRequest(configIndex);
            rdpeusbAdapter.TransferInRequest(device, des, desConfig.wTotalLength);

            // 11. Receives a completion message with the complete result for USB_CONFIGURATION_DESCRIPTOR.");
            pdu = (EusbUrbCompletionPdu)rdpeusbAdapter.ExpectCompletion(device.VirtualChannel);

            // 12. Sends TS_URB_SELECT_CONFIGURATION URB request.
            UsbConfigurationParser configParser = new UsbConfigurationParser();
            configParser.ParseAll(pdu);
            requestId = IdGenerator.NewId();
            TS_URB_SELECT_CONFIGURATION sel = new UrbBuilder(
                URB_FUNCTIONID.URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE,
                requestId,
                0).BuildSelectConfigRequest(configParser.Interfaces, configParser.configDescriptor);
            rdpeusbAdapter.TransferInRequest(device, sel, 0);

            // 13. Receives a completion message with the result for configuration selection.");
            EusbUrbCompletionNoDataPdu pduRes = (EusbUrbCompletionNoDataPdu)rdpeusbAdapter.ExpectCompletion(device.VirtualChannel);
            if (null == pduRes || pduRes.HResult != (uint)HRESULT_FROM_WIN32.ERROR_SUCCESS)
            {
                return false;
            }

            TS_URB_SELECT_CONFIGURATION_RESULT urb = new TS_URB_SELECT_CONFIGURATION_RESULT();
            if (!PduMarshaler.Unmarshal(pduRes.TsUrbResult, urb))
            {
                return false;
            }

            context.SelectedConfig = urb;
            return true;
        }