/// <summary>
        /// Builds an interface-selection request.
        /// </summary>
        /// <param name="configuration">The result of configuration-selection.</param>
        /// <param name="index">The index of the interface to be selected.</param>
        /// <returns>The interface-selection request.</returns>
        public TS_URB_SELECT_INTERFACE BuildSelectionInterfaceRequest(TS_URB_SELECT_CONFIGURATION_RESULT configuration, int index)
        {
            if (null == configuration)
            {
                throw new ArgumentNullException("configuration");
            }
            if (index >= configuration.Interface.Length || null == configuration.Interface[index])
            {
                throw new ArgumentException("index specified interface doesn't exist.");
            }

            TS_URB_SELECT_INTERFACE urb = new TS_URB_SELECT_INTERFACE(this.requestId, this.noAck);

            urb.ConfigurationHandle = configuration.ConfigurationHandle;
            urb.Header.Size         = 8 + 4; // Header + ConfigurationHandle
            urb.TsUsbdIInfo         = new TS_USBD_INTERFACE_INFORMATION();
            TS_USBD_INTERFACE_INFORMATION_RESULT inf = configuration.Interface[index];

            if (inf.NumberOfPipes != inf.Pipes.Length)
            {
                throw new ArgumentException(String.Format(
                                                "The selecting interface count doesn't match. NumberOfPipes: {0}, Pipes count: {1}.",
                                                inf.NumberOfPipes,
                                                inf.Pipes.Length
                                                ));
            }

            urb.TsUsbdIInfo.Length = 2 + 2 + 1 + 1 + 2 + 4;
            urb.TsUsbdIInfo.NumberOfPipesExpected = (ushort)inf.NumberOfPipes;
            urb.TsUsbdIInfo.InterfaceNumber       = inf.InterfaceNumber;
            urb.TsUsbdIInfo.AlternateSetting      = inf.AlternateSetting;
            urb.TsUsbdIInfo.Padding       = PaddingGenerator.GeneratePadding();
            urb.TsUsbdIInfo.NumberOfPipes = inf.NumberOfPipes;

            urb.TsUsbdIInfo.Infomations = new TS_USBD_PIPE_INFORMATION[inf.NumberOfPipes];
            for (int i = 0; i < inf.NumberOfPipes; i++)
            {
                urb.TsUsbdIInfo.Length        += 2 + 2 + 4 + 4; // count Pipe sizes
                urb.TsUsbdIInfo.Infomations[i] = new TS_USBD_PIPE_INFORMATION();
                urb.TsUsbdIInfo.Infomations[i].MaximumPacketSize   = inf.Pipes[i].MaximumPacketSize;
                urb.TsUsbdIInfo.Infomations[i].Padding             = PaddingGenerator.GeneratePadding();
                urb.TsUsbdIInfo.Infomations[i].MaximumTransferSize = inf.Pipes[i].MaximumTransferSize;
                urb.TsUsbdIInfo.Infomations[i].PipeFlags           = inf.Pipes[i].PipeFlags;
            }

            urb.Header.Size += urb.TsUsbdIInfo.Length; // interface information size.
            return(urb);
        }
        /// <summary>
        /// This method is used to verify the URB_COMPLETION_NO_DATA PDU.
        /// </summary>
        /// <param name="responsePdu">The PDU from the client.</param>
        /// <param name="tsUrb">The TS_URB in the request.</param>
        /// <param name="isTransferInRequest">This specify if the request is TRANSFER_IN_REQUEST or TRANSFER_OUT_REQUEST.</param>
        /// <param name="requestCompletion">A unique InterfaceID to be set in the Register Request Callback Message.</param>
        public static void VerifyUrbCompletionNoData(EusbUrbCompletionNoDataPdu responsePdu, TS_URB tsUrb, bool isTransferInRequest, uint requestCompletion)
        {
            Site.Assert.AreEqual<uint>(
                requestCompletion,
                responsePdu.InterfaceId,
                "Expect that the InterfaceId in the response PDU equals the RequestCompletion field of the REGISTER_REQUEST_CALLBACK PDU. The actual value is 0x{0:x8}.",
                responsePdu.InterfaceId);

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

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

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

            if (isTransferInRequest)
            {
                #region Verify Response For TRANSFER_IN_REQUEST
                Site.Assert.AreEqual<uint>(
                    0,
                    responsePdu.OutputBufferSize,
                    "Expect that the OutputBufferSize in the response PDU is zero. The actual value is 0x{0:x8}.",
                    responsePdu.OutputBufferSize);

                if (tsUrb is TS_URB_SELECT_CONFIGURATION)
                {
                    #region Verify TS_URB_SELECT_CONFIGURATION_RESULT
                    Site.Log.Add(LogEntryKind.Debug,
                        "Expect the TsUrbResult is TS_URB_SELECT_CONFIGURATION_RESULT when the TsUrb in the request is TS_URB_SELECT_CONFIGURATION.");
                    TS_URB_SELECT_CONFIGURATION_RESULT urb = new TS_URB_SELECT_CONFIGURATION_RESULT();

                    if (!PduMarshaler.Unmarshal(responsePdu.TsUrbResult, urb))
                    {
                        // TsUrbResult can not be unmarshaled to TS_URB_SELECT_CONFIGURATION_RESULT
                        TS_URB_UNKNOWN unknowUrb = new TS_URB_UNKNOWN();
                        Site.Assume.IsTrue(PduMarshaler.Unmarshal(responsePdu.TsUrbResult, unknowUrb),
                            "Marshaling the data to an unknown PDU MUST succeed.");

                        Site.Log.Add(LogEntryKind.CheckFailed,
                            "The TsUrbResult is not valid TS_URB_SELECT_CONFIGURATION_RESULT. The data is:\r\n{0}", unknowUrb.ToString());
                    }
                    else
                    {

                        Site.Log.Add(LogEntryKind.CheckSucceeded, "The TsUrbResult is expected TS_URB_SELECT_CONFIGURATION_RESULT.");
                    }
                    #endregion
                }
                else if (tsUrb is TS_URB_SELECT_INTERFACE)
                {
                    #region Verify TS_URB_SELECT_INTERFACE_RESULT
                    Site.Log.Add(LogEntryKind.Debug,
                        "Expect the TsUrbResult is TS_URB_SELECT_INTERFACE_RESULT when the TsUrb in the request is TS_URB_SELECT_INTERFACE.");
                    TS_URB_SELECT_INTERFACE_RESULT urb = new TS_URB_SELECT_INTERFACE_RESULT();

                    if (!PduMarshaler.Unmarshal(responsePdu.TsUrbResult, urb))
                    {
                        // TsUrbResult can not be unmarshaled to TS_URB_SELECT_INTERFACE_RESULT
                        TS_URB_UNKNOWN unknowUrb = new TS_URB_UNKNOWN();
                        Site.Assume.IsTrue(PduMarshaler.Unmarshal(responsePdu.TsUrbResult, unknowUrb),
                            "Marshaling the data to an unknown PDU MUST succeed.");

                        Site.Log.Add(LogEntryKind.CheckFailed,
                            "The TsUrbResult is not valid TS_URB_SELECT_INTERFACE_RESULT. The data is:\r\n{0}", unknowUrb.ToString());
                    }
                    else
                    {

                        Site.Log.Add(LogEntryKind.CheckSucceeded, "The TsUrbResult is expected TS_URB_SELECT_INTERFACE_RESULT.");
                    }
                    #endregion
                }
                else if (tsUrb is TS_URB_GET_CURRENT_FRAME_NUMBER)
                {
                    #region Verify TS_URB_GET_CURRENT_FRAME_NUMBER_RESULT
                    Site.Log.Add(LogEntryKind.Debug,
                        "Expect the TsUrbResult is TS_URB_GET_CURRENT_FRAME_NUMBER_RESULT when the TsUrb in the request is TS_URB_GET_CURRENT_FRAME_NUMBER.");
                    TS_URB_GET_CURRENT_FRAME_NUMBER_RESULT urb = new TS_URB_GET_CURRENT_FRAME_NUMBER_RESULT();

                    if (!PduMarshaler.Unmarshal(responsePdu.TsUrbResult, urb))
                    {
                        // TsUrbResult can not be unmarshaled to TS_URB_GET_CURRENT_FRAME_NUMBER_RESULT
                        TS_URB_UNKNOWN unknowUrb = new TS_URB_UNKNOWN();
                        Site.Assume.IsTrue(PduMarshaler.Unmarshal(responsePdu.TsUrbResult, unknowUrb),
                            "Marshaling the data to an unknown PDU MUST succeed.");

                        Site.Log.Add(LogEntryKind.CheckFailed,
                            "The TsUrbResult is not valid TS_URB_GET_CURRENT_FRAME_NUMBER_RESULT. The data is:\r\n{0}", unknowUrb.ToString());
                    }
                    else
                    {

                        Site.Log.Add(LogEntryKind.CheckSucceeded, "The TsUrbResult is expected TS_URB_GET_CURRENT_FRAME_NUMBER_RESULT.");
                    }
                    #endregion
                }
                #endregion
            }
            else
            {
                #region Verify Response For TRANSFER_OUT_REQUEST
                Site.Assert.AreNotEqual<uint>(
                    0,
                    responsePdu.OutputBufferSize,
                    "Expect that the OutputBufferSize in the response PDU is not zero. The actual value is 0x{0:x8}.",
                    responsePdu.OutputBufferSize);
                #endregion
            }

            #region Verify TS_URB_ISOCH_TRANSFER_RESULT
            if (tsUrb is TS_URB_ISOCH_TRANSFER)
            {
                Site.Log.Add(LogEntryKind.Debug,
                    "Expect the TsUrbResult is TS_URB_ISOCH_TRANSFER_RESULT when the TsUrb in the request is TS_URB_ISOCH_TRANSFER.");
                TS_URB_ISOCH_TRANSFER_RESULT urb = new TS_URB_ISOCH_TRANSFER_RESULT();

                if (!PduMarshaler.Unmarshal(responsePdu.TsUrbResult, urb))
                {
                    // TsUrbResult can not be unmarshaled to TS_URB_ISOCH_TRANSFER_RESULT
                    TS_URB_UNKNOWN unknowUrb = new TS_URB_UNKNOWN();
                    Site.Assume.IsTrue(PduMarshaler.Unmarshal(responsePdu.TsUrbResult, unknowUrb),
                        "Marshaling the data to an unknown PDU MUST succeed.");

                    Site.Log.Add(LogEntryKind.CheckFailed,
                        "The TsUrbResult is not valid TS_URB_ISOCH_TRANSFER_RESULT. The data is:\r\n{0}", unknowUrb.ToString());
                }
                else
                {

                    Site.Log.Add(LogEntryKind.CheckSucceeded, "Expect that the TsUrbResult is TS_URB_ISOCH_TRANSFER_RESULT.");
                }
            }
            #endregion
        }
        public void S3_EUSB_OperateIo_SelectConfiguration()
        {
            LogComment("S3_EUSB_OperateIo_SelectConfiguration");

            LogComment("1. Creates the control virtual channel, exchanges capabilities then notifies that the channel is created.");
            context.ControlChannel =  CreateVirtualChannel();

            LogComment("2. Receives an add virtual channel request.");
            rdpeusbAdapter.ExpectAddVirtualChannel(context.ControlChannel);

            LogComment("3. Creates a new virtual channel for the device.");
            uint channelId = context.NewChannelId();
            DynamicVirtualChannel channel =  CreateVirtualChannel();

            LogComment("4. Receives an add device request.");
            EusbDeviceContext device = rdpeusbAdapter.ExpectAddDevice(channel);

            LogComment("5. Registers a callback to provide the Request Completion Interface to the client.");
            uint interfaceId = IdGenerator.NewId();
            rdpeusbAdapter.RegisterCallback(device, 1, interfaceId);

            LogComment("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);

            LogComment("7. Receives a completion message with the result for USB_DEVICE_DESCRIPTOR.");
            EusbPdu pdu = rdpeusbAdapter.ExpectCompletion(device.VirtualChannel);
            ReqCapturer.VerifyUrbCompletion((EusbUrbCompletionPdu)pdu, des, interfaceId);
            EusbUrbCompletionPdu completionPdu = (EusbUrbCompletionPdu)pdu;
            USB_DEVICE_DESCRIPTOR desDevice = UsbStructParser.Parse<USB_DEVICE_DESCRIPTOR>(completionPdu);

            // A device may support multiple configurations, numbered starting at zero.
            // Test case will assume the device only have a single configuration.
            Site.Assume.AreEqual<byte>(1, desDevice.bNumConfigurations, "This test case only supports the device with the single configuration.");

            LogComment("Retrieved the device descriptor: {0}", desDevice.ToString());

            LogComment("8. Sends TS_URB_CONTROL_DESCRIPTOR_REQUEST with the descriptor type of USB_CONFIGURATION_DESCRIPTOR_TYPE to query " +
                "the total length of the configuration.");
            requestId = IdGenerator.NewId();
            des = new UrbBuilder(
                URB_FUNCTIONID.URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE,
                requestId,
                0).BuildConfigurationDescriptorRequest(0);
            rdpeusbAdapter.TransferInRequest(device, des, USB_CONFIGURATION_DESCRIPTOR.DefaultSize);

            LogComment("9. Receives a completion message with the result for USB_CONFIGURATION_DESCRIPTOR.");
            pdu = rdpeusbAdapter.ExpectCompletion(device.VirtualChannel);
            ReqCapturer.VerifyUrbCompletion((EusbUrbCompletionPdu)pdu, des, interfaceId);
            completionPdu = (EusbUrbCompletionPdu)pdu;
            USB_CONFIGURATION_DESCRIPTOR desConfig = UsbStructParser.Parse<USB_CONFIGURATION_DESCRIPTOR>(completionPdu);

            LogComment("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(0);
            rdpeusbAdapter.TransferInRequest(device, des, desConfig.wTotalLength);

            LogComment("11. Receives a completion message with the complete result for USB_CONFIGURATION_DESCRIPTOR.");
            pdu = rdpeusbAdapter.ExpectCompletion(device.VirtualChannel);
            ReqCapturer.VerifyUrbCompletion((EusbUrbCompletionPdu)pdu, des, interfaceId);
            completionPdu = (EusbUrbCompletionPdu)pdu;

            LogComment("12. Sends TS_URB_SELECT_CONFIGURATION URB request.");
            UsbConfigurationParser configParser = new UsbConfigurationParser();
            configParser.ParseAll(completionPdu);
            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);

            LogComment("13. Receives a completion message with the result for configuration selection.");
            pdu = rdpeusbAdapter.ExpectCompletion(device.VirtualChannel);
            Site.Assert.IsInstanceOfType(
                pdu,
                typeof(EusbUrbCompletionNoDataPdu),
                "The result must be type of EusbUrbCompletionNoDataPdu.");
            EusbUrbCompletionNoDataPdu pduRes = (EusbUrbCompletionNoDataPdu)pdu;
            Site.Assert.IsSuccess((int)pduRes.HResult, "The EusbUrbCompletionNoDataPdu must indicate successful.");
            ReqCapturer.VerifyUrbCompletionNoData(pduRes, sel, true, interfaceId);

            TS_URB_SELECT_CONFIGURATION_RESULT urb = new TS_URB_SELECT_CONFIGURATION_RESULT();
            Site.Assert.IsTrue(
                PduMarshaler.Unmarshal(pduRes.TsUrbResult, urb),
                "The completion PDU must contain the result.");

            LogComment("The configuration-selection result is {0}.", urb);

            LogComment("14. Sends retract device request and the channel for the device is expected to be closed.");
            rdpeusbAdapter.RetractDevice(device, USB_RETRACT_REASON.UsbRetractReason_BlockedByPolicy);
        }
        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;
        }