Imports System Imports System.Collections.Generic Imports System.Runtime.InteropServices Imports System.Threading Imports Test.Devices Imports libusbK Imports libusbK.Examples Namespace Read.Isochronous ' ReSharper disable InconsistentNaming Friend Class Program #Region "TODO USER: Set the test parameters for your device." public static IsoTestParameters Test = new IsoTestParameters(0x04d8, 0xfa2e, 0, 0x82, 3, 64,null, 24); 'Public Shared Test As IsoTestParameters = New IsoTestParameters(&H04b4, &H00fD, 1, &H83, 3, 12, Nothing, 24) #End Region public Shared Sub Main(ByVal args As String()) Dim success As Boolean Dim pipeInfo As WINUSB_PIPE_INFORMATION_EX Dim usb As UsbK Dim interfaceDescriptor As USB_INTERFACE_DESCRIPTOR ' Find and configure the device. If Not Test.ConfigureDevice(pipeInfo, usb, interfaceDescriptor) Then Return #If BMFW ' TODO FOR USER: Remove this block if not using benchmark firmware. ' This configures devices running benchmark firmware for streaming DeviceToHost transfers. Console.WriteLine("Configuring for benchmark device..") Dim testType As BM_TEST_TYPE = Test.Devices.BM_TEST_TYPE.READ success = Test.Devices.Benchmark.Configure(usb, Test.Devices.BM_COMMAND.SET_TEST, interfaceDescriptor.bInterfaceNumber, testType) If Not success Then Console.WriteLine("Bench_Configure failed.") End If #End If ' Create the ISO transfer queue. This class manages the pending and outstanding transfer lists. Dim readXfers As ReadIsoTransferQueue = New ReadIsoTransferQueue(usb, pipeInfo, Test.MaxOutstandingTransfers, Test.IsoPacketsPerTransfer) If Not Test.ShowTestReady() Then GoTo Done End If ' Always issue a reset pipe prior to re/starting an ISO stream. usb.ResetPipe(pipeInfo.PipeId) ' This example will not manage the start frame manually, but this is ' how I might caculate the starting point. usb.GetCurrentFrameNumber(readXfers.FrameNumber) ' Add some start latency readXfers.FrameNumber += CUInt(Test.MaxOutstandingTransfers) ' Start FrameNumber at an interval of 8. If readXfers.FrameNumber Mod 8 > 0 Then readXfers.FrameNumber = CUInt((readXfers.FrameNumber And Not &H7) + 8) ' This is a counter/timer used only for statistics gathering. Thread.Sleep(0) Call Test.Dcs.Start() ' Transfer processing loop Do Dim isoTransferItem As IsoTransferItem Dim transferred As UInteger Dim errorCode As Integer ' While buffers exist in the completed list, submit them. While readXfers.Completed.Count > 0 AndAlso readXfers.Outstanding.Count < Test.MaxOutstandingTransfers errorCode = readXfers.SubmitNextRead() If errorCode <> 0 Then Console.WriteLine("IsoReadPipe failed. ErrorCode: {0:X8}h", errorCode) GoTo Done End If End While ' Wait for the oldest transfer to complete. errorCode = readXfers.WaitRead(isoTransferItem, 1000, transferred) If errorCode <> 0 Then Console.WriteLine("OvlPool.Wait failed. ErrorCode: {0:X8}h", errorCode) GoTo Done End If ' Report iso status. IsoXferReport(readXfers, isoTransferItem, transferred) If readXfers.CompletedCount = 1 Then Call Test.Dcs.Start() Loop While readXfers.CompletedCount < Test.MaxTransfersTotal Done: readXfers.Destroy() Call Test.Free() usb.Free() End Sub Private Shared Sub IsoXferReport(ByVal readIsoTransfers As ReadIsoTransferQueue, ByVal isoTransferItem As IsoTransferItem, ByVal transferLength As UInteger) Dim packetPos As Integer Test.Dcs.Stop(transferLength) If readIsoTransfers.LastStartFrame = 0 Then Test.LogLn("#{0}: StartFrame={1:X8}h TransferLength={2} BPS-average:{3}", readIsoTransfers.CompletedCount, isoTransferItem.FrameNumber, transferLength, Math.Round(Test.Dcs.Bps, 2)) Else Dim lastFrameCount = isoTransferItem.FrameNumber - readIsoTransfers.LastStartFrame Test.LogLn("#{0}: StartFrame={1:X8}h TransferLength={2} BPS-average:{3} LastFrameCount={4}", readIsoTransfers.CompletedCount, isoTransferItem.FrameNumber, transferLength, Math.Round(Test.Dcs.Bps, 2), lastFrameCount) End If Dim numPackets As UInteger isoTransferItem.Iso.GetNumberOfPackets(numPackets) For packetPos = 0 To numPackets - 1 Dim isoPacket As KISO_PACKET Dim packetOffset As UInteger Dim packetLength As UInteger Dim packetStatus As UInteger isoTransferItem.Iso.GetPacket(packetPos, packetOffset, packetLength, packetStatus) If packetLength > 1 Then ' This is somewhat specific to data that is returned by the benchmark firmware. Dim firstPacketByte = isoTransferItem.Buffer(packetOffset) Dim secondPacketByte = isoTransferItem.Buffer(packetOffset + 1) Test.LogLn(" [{0:000}] Length={1} B0={2:X2}h B1={3:X2}h", packetPos, packetLength, firstPacketByte, secondPacketByte) Else Test.LogLn(" [{0:000}] Empty Packet", packetPos) End If Next readIsoTransfers.CompletedCount += 1 readIsoTransfers.LastStartFrame = isoTransferItem.FrameNumber End Sub End Class Friend Class IsoTransferItem Public ReadOnly Container As ReadIsoTransferQueue Public Sub New(ByVal container As ReadIsoTransferQueue) Me.Container = container End Sub Public Buffer As Byte() Public BufferGC As GCHandle Public Iso As IsochK Public Ovl As KOVL_HANDLE Public FrameNumber As UInteger End Class Friend Class ReadIsoTransferQueue #Region "Transfer lists" Public Completed As LinkedList(Of IsoTransferItem) = New LinkedList(Of IsoTransferItem)() Public Outstanding As LinkedList(Of IsoTransferItem) = New LinkedList(Of IsoTransferItem)() #End Region #Region "Core queue members" Public ReadOnly DataBufferSize As Integer Public ReadOnly OvlPool As OvlK Public ReadOnly PipeInfo As WINUSB_PIPE_INFORMATION_EX Public ReadOnly Usb As UsbK #End Region #Region "Frame management" Public FrameNumber As UInteger Public LastStartFrame As UInteger #End Region #Region "Statisitics" Public CompletedCount As UInteger Public TotalSubmittedCount As UInteger #End Region Public Sub New(ByVal usb As UsbK, ByRef pipeInfo As WINUSB_PIPE_INFORMATION_EX, ByVal maxPendingTransfers As Integer, ByVal numberOfPackets As UInteger) Me.PipeInfo = pipeInfo Me.Usb = usb Dim deviceSpeedBuffer = New Byte(0) {} Dim deviceSpeedLength As UInteger = 1 Dim gcDeviceSpeed = GCHandle.Alloc(deviceSpeedBuffer, GCHandleType.Pinned) Me.Usb.QueryDeviceInformation(DeviceInformationType.DEVICE_SPEED, deviceSpeedLength, Marshal.UnsafeAddrOfPinnedArrayElement(deviceSpeedBuffer, 0)) gcDeviceSpeed.Free() OvlPool = New OvlK(usb.Handle, maxPendingTransfers, KOVL_POOL_FLAG.NONE) DataBufferSize = CInt(pipeInfo.MaximumBytesPerInterval * numberOfPackets) For pos = 0 To maxPendingTransfers - 1 Dim isoTransferItem As IsoTransferItem = New IsoTransferItem(Me) isoTransferItem.Buffer = New Byte(DataBufferSize - 1) {} isoTransferItem.BufferGC = GCHandle.Alloc(isoTransferItem.Buffer, GCHandleType.Pinned) isoTransferItem.Iso = New IsochK(usb.Handle, pipeInfo.PipeId, numberOfPackets, isoTransferItem.BufferGC.AddrOfPinnedObject(), isoTransferItem.Buffer.Length) isoTransferItem.Iso.SetPacketOffsets(pipeInfo.MaximumBytesPerInterval) OvlPool.Acquire(isoTransferItem.Ovl) Completed.AddLast(isoTransferItem) Next End Sub Public Sub Destroy() ' Cancel any outstanding IO and release the OvlK. For Each myIsoBuffer In Outstanding Dim transferred As UInteger OvlK.WaitAndRelease(myIsoBuffer.Ovl, 0, transferred) Next For Each myIsoBuffer In Outstanding myIsoBuffer.Iso.Free() myIsoBuffer.BufferGC.Free() Next Outstanding.Clear() For Each myIsoBuffer In Completed myIsoBuffer.Iso.Free() myIsoBuffer.BufferGC.Free() Next Completed.Clear() OvlPool.Free() End Sub Public Function SubmitNextRead() As Integer ' Pull from head of Completed list and push to end of Outstanding list Dim isoTransferItem = Completed.First.Value Completed.RemoveFirst() Outstanding.AddLast(isoTransferItem) ' Prepare the OvlK handle to be submitted. OvlK.ReUse(isoTransferItem.Ovl) ' Set the frame number this transfer will be submitted on (used for logging only) isoTransferItem.FrameNumber = isoTransferItem.Container.FrameNumber ' submit the transfer. use 0 for length and 0 for number of packets to use the values the isoch handle was initialized with Usb.IsochReadPipe(isoTransferItem.Iso.Handle, 0, isoTransferItem.Container.FrameNumber, 0, isoTransferItem.Ovl) ' IsochReadPipe will always return false so we need to check the error code Dim errorCode As Integer = Marshal.GetLastWin32Error() ' 997 is ERROR_IO_PENDING. For async, this means so far so good. ' IE: The transfer is submitted but we won't know if a problem occurs until it is completed. If errorCode <> IoPending Then Return errorCode TotalSubmittedCount += 1 Return 0 End Function Public Function WaitRead( ByRef isoTransferItem As IsoTransferItem, ByVal i As Integer, ByRef transferred As UInteger) As Integer isoTransferItem = Outstanding.First.Value Dim success = OvlK.Wait(isoTransferItem.Ovl, 1000, KOVL_WAIT_FLAG.NONE, transferred) If Not success Then Return Marshal.GetLastWin32Error() End If Outstanding.RemoveFirst() Completed.AddLast(isoTransferItem) Return 0 End Function End Class End Namespace