public static void Test_QueueHeadWrapper() { FOS_System.String testName = "Queue Head Wrapper"; DBGMSG(testName, "START"); errors = 0; warnings = 0; EHCI_QueueHead qh = new EHCI_QueueHead(); try { byte* pQH = (byte*)qh.queueHead; //Verifications done via two methods: // 1. Check value from pointer & manual shifting to confirm set properly // 2. Check value from "get" method to confirm reading properly // 3. For boolean types, also test & verify setting to false! qh.Active = true; if ((pQH[0x18u] & 0x80u) == 0) { DBGERR(testName, "Active - Failed to set to true."); } else { if (!qh.Active) { DBGERR(testName, "Active - Failed to read as true."); } else { pQH[0x18u] = 0xFF; qh.Active = false; if ((pQH[0x18u] & 0x80u) != 0) { DBGERR(testName, "Active - Failed to set to false."); } else { if (qh.Active) { DBGERR(testName, "Active - Failed to read as false."); } } } } qh.ControlEndpointFlag = true; if ((pQH[0x07u] & 0x08u) == 0) { DBGERR(testName, "ControlEndpointFlag - Failed to set to true."); } else { if (!qh.ControlEndpointFlag) { DBGERR(testName, "ControlEndpointFlag - Failed to read as true."); } else { pQH[0x07u] = 0xFF; qh.ControlEndpointFlag = false; if ((pQH[0x07u] & 0x08u) != 0) { DBGERR(testName, "ControlEndpointFlag - Failed to set to false."); } else { if (qh.ControlEndpointFlag) { DBGERR(testName, "ControlEndpointFlag - Failed to read as false."); } } } } qh.CurrentqTDPointer = (EHCI_qTD_Struct*)0xDEADBEFFu; //- Read back should equal 0xDEADBEE0 if ((pQH[0x0Cu] & 0xF0u) != 0xF0u || pQH[0x0Du] != 0xBEu || pQH[0x0Eu] != 0xADu || pQH[0x0Fu] != 0xDEu) { DBGERR(testName, "CurrentqTDPointer - Failed to set."); } else { if ((uint)qh.CurrentqTDPointer != 0xDEADBEF0u) { DBGERR(testName, "CurrentqTDPointer - Failed to read."); } } qh.DataToggleControl = true; if ((pQH[0x05u] & 0x40u) == 0) { DBGERR(testName, "DataToggleControl - Failed to set to true."); } else { if (!qh.DataToggleControl) { DBGERR(testName, "DataToggleControl - Failed to read as true."); } else { pQH[0x05u] = 0xFF; qh.DataToggleControl = false; if ((pQH[0x05u] & 0x40u) != 0) { DBGERR(testName, "DataToggleControl - Failed to set to false."); } else { if (qh.DataToggleControl) { DBGERR(testName, "DataToggleControl - Failed to read as false."); } } } } qh.DeviceAddress = 0xDE; if ((pQH[0x04u] & 0x7Fu) != 0x5Eu) { DBGERR(testName, "DeviceAddress - Failed to set."); } else { if ((uint)qh.DeviceAddress != 0x5Eu) { DBGERR(testName, "DeviceAddress - Failed to read."); } } qh.EndpointNumber = 0xBF; //Shift! if ((pQH[0x05u] & 0x0Fu) != 0x0Fu) { DBGERR(testName, "EndpointNumber - Failed to set."); } else { if ((uint)qh.EndpointNumber != 0x0Fu) { DBGERR(testName, "EndpointNumber - Failed to read."); } } qh.EndpointSpeed = 0xB3; //Shift! if ((pQH[0x05u] & 0x30u) != 0x30u) { DBGERR(testName, "EndpointSpeed - Failed to set."); } else { if ((uint)qh.EndpointSpeed != 0x03u) { DBGERR(testName, "EndpointSpeed - Failed to read."); } } qh.HeadOfReclamationList = true; if ((pQH[0x05u] & 0x80u) == 0) { DBGERR(testName, "HeadOfReclamationList - Failed to set to true."); } else { if (!qh.HeadOfReclamationList) { DBGERR(testName, "HeadOfReclamationList - Failed to read as true."); } else { pQH[0x05u] = 0xFF; qh.HeadOfReclamationList = false; if ((pQH[0x05u] & 0x80u) != 0) { DBGERR(testName, "HeadOfReclamationList - Failed to set to false."); } else { if (qh.HeadOfReclamationList) { DBGERR(testName, "HeadOfReclamationList - Failed to read as false."); } } } } qh.HighBandwidthPipeMultiplier = 0xDF; //Shift! if ((pQH[0x0Bu] & 0xC0u) != 0xC0u) { DBGERR(testName, "HighBandwidthPipeMultiplier - Failed to set."); } else { if ((uint)qh.HighBandwidthPipeMultiplier != 0x03u) { DBGERR(testName, "HighBandwidthPipeMultiplier - Failed to read."); } } qh.HorizontalLinkPointer = (EHCI_QueueHead_Struct*)0xDEADBEFE; if ((pQH[0x00u] & 0xE0u) != 0xE0u || pQH[0x01u] != 0xBEu || pQH[0x02u] != 0xADu || pQH[0x03u] != 0xDEu) { DBGERR(testName, "HorizontalLinkPointer - Failed to set."); } else { if ((uint)qh.HorizontalLinkPointer != 0xDEADBEE0u) { DBGERR(testName, "HorizontalLinkPointer - Failed to read."); } } qh.HubAddr = 0xBE; //Shift! if ((pQH[0x0Au] & 0x7Fu) != 0x3Eu) { DBGERR(testName, "HubAddr - Failed to set."); } else { if ((uint)qh.HubAddr != 0x3Eu) { DBGERR(testName, "HubAddr - Failed to read."); } } qh.InactiveOnNextTransaction = true; if ((pQH[0x04u] & 0x80u) == 0) { DBGERR(testName, "InactiveOnNextTransaction - Failed to set to true."); } else { if (!qh.InactiveOnNextTransaction) { DBGERR(testName, "InactiveOnNextTransaction - Failed to read as true."); } else { pQH[0x04u] = 0xFF; qh.InactiveOnNextTransaction = false; if ((pQH[0x04u] & 0x80u) != 0) { DBGERR(testName, "InactiveOnNextTransaction - Failed to set to false."); } else { if (qh.InactiveOnNextTransaction) { DBGERR(testName, "InactiveOnNextTransaction - Failed to read as false."); } } } } qh.InterruptScheduleMask = 0xFE; //Shift! if (pQH[0x08u] != 0xFEu) { DBGERR(testName, "InterruptScheduleMask - Failed to set."); } else { if ((uint)qh.InterruptScheduleMask != 0xFEu) { DBGERR(testName, "InterruptScheduleMask - Failed to read."); } } qh.MaximumPacketLength = 0xDEAD; //Shift! if ( pQH[0x06u] != 0xADu || (pQH[0x07u] & 0x07u) != 0x06u) { DBGERR(testName, "MaximumPacketLength - Failed to set."); } else { if ((uint)qh.MaximumPacketLength != 0x06ADu) { DBGERR(testName, "MaximumPacketLength - Failed to read."); } } qh.NakCountReload = 0xFF; //Shift! if ((pQH[0x07u] & 0xF0u) != 0xF0u) { DBGERR(testName, "NakCountReload - Failed to set."); } else { if ((uint)qh.NakCountReload != 0x0Fu) { DBGERR(testName, "NakCountReload - Failed to read."); } } qh.NextqTDPointer = (EHCI_qTD_Struct*)0xDEADBEFF; //- Read back should equal 0xDEADBEE0 if ((pQH[0x10u] & 0xF0u) != 0xF0u || pQH[0x11u] != 0xBEu || pQH[0x12u] != 0xADu || pQH[0x13u] != 0xDEu) { DBGERR(testName, "NextqTDPointer - Failed to set."); } else { if ((uint)qh.NextqTDPointer != 0xDEADBEF0u) { DBGERR(testName, "NextqTDPointer - Failed to read."); } } qh.NextqTDPointerTerminate = true; if ((pQH[0x10u] & 0x01u) == 0) { DBGERR(testName, "NextqTDPointerTerminate - Failed to set to true."); } else { if (!qh.NextqTDPointerTerminate) { DBGERR(testName, "NextqTDPointerTerminate - Failed to read as true."); } else { pQH[0x10u] = 0xFF; qh.NextqTDPointerTerminate = false; if ((pQH[0x10u] & 0x01u) != 0) { DBGERR(testName, "NextqTDPointerTerminate - Failed to set to false."); } else { if (qh.NextqTDPointerTerminate) { DBGERR(testName, "NextqTDPointerTerminate - Failed to read as false."); } } } } qh.PortNumber = 0xFF; //Shift! if ((pQH[0x0Au] & 0x80u) != 0x80u || (pQH[0x0Bu] & 0x3Fu) != 0x3Fu) { DBGERR(testName, "PortNumber - Failed to set."); } else { if ((uint)qh.PortNumber != 0x7Fu) { DBGERR(testName, "PortNumber - Failed to read."); } } qh.SplitCompletionMask = 0xBE; //Shift! if (pQH[0x09u] != 0xBEu) { DBGERR(testName, "SplitCompletionMask - Failed to set."); } else { if ((uint)qh.SplitCompletionMask != 0xBEu) { DBGERR(testName, "SplitCompletionMask - Failed to read."); } } qh.Terminate = true; if ((pQH[0x00u] & 0x01u) == 0) { DBGERR(testName, "Terminate - Failed to set to true."); } else { if (!qh.Terminate) { DBGERR(testName, "Terminate - Failed to read as true."); } else { pQH[0x00u] = 0xFF; qh.Terminate = false; if ((pQH[0x00u] & 0x01u) != 0) { DBGERR(testName, "Terminate - Failed to set to false."); } else { if (qh.Terminate) { DBGERR(testName, "Terminate - Failed to read as false."); } } } } qh.Type = 0xFF; //Shift! if ((pQH[0x00u] & 0x06u) != 0x06u) { DBGERR(testName, "Type - Failed to set."); } else { if ((uint)qh.Type != 0x03u) { DBGERR(testName, "Type - Failed to read."); } } } catch { errors++; BasicConsole.SetTextColour(BasicConsole.warning_colour); BasicConsole.WriteLine(ExceptionMethods.CurrentException.Message); BasicConsole.SetTextColour(BasicConsole.default_colour); } finally { qh.Free(); } if (errors > 0) { DBGERR(testName, ((FOS_System.String)"Test failed! Errors: ") + errors + " Warnings: " + warnings); } else { if (warnings > 0) { DBGWRN(testName, ((FOS_System.String)"Test passed with warnings: ") + warnings); } else { DBGMSG(testName, "Test passed."); } } DBGMSG(testName, "END"); BasicConsole.DelayOutput(1); }
/// <summary> /// Initialises a queue head - memory must already be allocated. /// </summary> /// <param name="headPtr">A pointer to the queue head structure to initialise.</param> /// <param name="horizPtr"> /// The virtual address of the next queue head in the list (or the first queue head since the /// async queue is a circular buffer). This is translated into the physical address internally. /// </param> /// <param name="firstQTD">A pointer to the first qTD of the queue head.</param> /// <param name="H">The Head of Reclamation list flag.</param> /// <param name="deviceAddr">The address of the USB device to which this queue head belongs.</param> /// <param name="endpoint">The endpoint number of the USB device to which this queue head belongs.</param> /// <param name="maxPacketSize">The maximum packet size to use when transferring.</param> protected void InitQH(EHCI_QueueHead_Struct* headPtr, EHCI_QueueHead_Struct* horizPtr, EHCI_qTD_Struct* firstQTD, bool H, byte deviceAddr, byte endpoint, ushort maxPacketSize) { EHCI_QueueHead head = new EHCI_QueueHead(headPtr); head.HorizontalLinkPointer = (EHCI_QueueHead_Struct*)VirtMemManager.GetPhysicalAddress(horizPtr); head.Type = 0x1; // Types: 00b iTD, 01b QH, 10b siTD, 11b FSTN head.Terminate = false; head.DeviceAddress = deviceAddr; // The device address head.InactiveOnNextTransaction = false; head.EndpointNumber = endpoint; // endpoint 0 contains Device infos such as name head.EndpointSpeed = 2; // 00b = full speed; 01b = low speed; 10b = high speed head.DataToggleControl = true; // get the Data Toggle bit out of the included qTD head.HeadOfReclamationList = H; // mark a queue head as being the head of the reclaim list head.MaximumPacketLength = maxPacketSize; // 64 byte for a control transfer to a high speed device head.ControlEndpointFlag = false; // only used if endpoint is a control endpoint and not high speed head.NakCountReload = 0; // this value is used by EHCI to reload the Nak Counter field. 0=ignores NAK counter. head.InterruptScheduleMask = 0; // not used for async schedule head.SplitCompletionMask = 0; // unused if (not low/full speed and in periodic schedule) head.HubAddr = 0; // unused if high speed (Split transfer) head.PortNumber = 0; // unused if high speed (Split transfer) head.HighBandwidthPipeMultiplier = 1; // 1-3 transaction per micro-frame, 0 means undefined results if (firstQTD == null) { head.NextqTDPointer = null; head.NextqTDPointerTerminate = true; } else { head.NextqTDPointer = (EHCI_qTD_Struct*)VirtMemManager.GetPhysicalAddress(firstQTD); head.NextqTDPointerTerminate = false; } }
//SetupTransfer //SETUPTransaction //INTransaction //OUTTransaction //IssueTransfer //CreateQTD_SETUP //CreateQTD_IO //Create_QH public void Test_Create_QH() { FOS_System.String testName = "Queue Transfer Descrip"; EHCITesting.DBGMSG(testName, "START"); EHCITesting.errors = 0; EHCITesting.warnings = 0; EHCI_QueueHead qh = new EHCI_QueueHead(); EHCI_QueueHead_Struct* pQH = qh.queueHead; try { CreateQH(pQH, 0xDEADBEEFu, (EHCI_qTD_Struct*)0x12345678u, true, 0xFE, 0xED, 0x1234); //Confirm values (other tests check that these get/set properties work properly) } catch { EHCITesting.errors++; BasicConsole.SetTextColour(BasicConsole.warning_colour); BasicConsole.WriteLine(ExceptionMethods.CurrentException.Message); BasicConsole.SetTextColour(BasicConsole.default_colour); } finally { qh.Free(); } if (EHCITesting.errors > 0) { EHCITesting.DBGERR(testName, ((FOS_System.String)"Test failed! Errors: ") + EHCITesting.errors + " Warnings: " + EHCITesting.warnings); } else { if (EHCITesting.warnings > 0) { EHCITesting.DBGWRN(testName, ((FOS_System.String)"Test passed with warnings: ") + EHCITesting.warnings); } else { EHCITesting.DBGMSG(testName, "Test passed."); } } EHCITesting.DBGMSG(testName, "END"); BasicConsole.DelayOutput(4); }
/// <summary> /// Adds a transfer for the async schedule. /// </summary> /// <param name="transfer">The transfer to add.</param> protected void AddToAsyncSchedule(USBTransfer transfer) { // Set the expected number of USB Interrupts to 1 // 1 because we expect one and only one for the last transaction of the transfer // which we flagged to Interrupt On Complete in IssueTransfer. USBIntCount = 1; // If the schedule is disabled: // Section 2.3.2 of Intel EHCI Spec if ((USBSTS & EHCI_Consts.STS_AsyncEnabled) == 0) { // Enable / start the async schedule EnableAsyncSchedule(); } // Save the old tail queue head (which may not be the idle queue head) (save in a wrapper) EHCI_QueueHead oldTailQH = new EHCI_QueueHead(TailQueueHead); // The new queue head will now be end of the queue TailQueueHead = (EHCI_QueueHead_Struct*)transfer.underlyingTransferData; // Create wrappers for the idle and tail queue heads. EHCI_QueueHead idleQH = new EHCI_QueueHead(IdleQueueHead); EHCI_QueueHead tailQH = new EHCI_QueueHead(TailQueueHead); // Create the ring. Link the new queue head with idleQH (which is always the head of the queue) tailQH.HorizontalLinkPointer = (EHCI_QueueHead_Struct*)VirtMemManager.GetPhysicalAddress(IdleQueueHead); // Insert the queue head into the queue as an element behind old queue head oldTailQH.HorizontalLinkPointer = (EHCI_QueueHead_Struct*)VirtMemManager.GetPhysicalAddress(TailQueueHead); int timeout = 100; while (USBIntCount > 0 && (HostSystemErrors == 0) && !IrrecoverableError && --timeout > 0) { Kernel.Processes.SystemCalls.SleepThread(50); #if EHCI_TRACE if (timeout % 10 == 0) { BasicConsole.WriteLine("Waiting for transfer to complete..."); } #endif } #if EHCI_TRACE if (timeout == 0) { BasicConsole.WriteLine("Transfer timed out."); } #endif // Restore the link of the old tail queue head to the idle queue head oldTailQH.HorizontalLinkPointer = (EHCI_QueueHead_Struct*)VirtMemManager.GetPhysicalAddress(IdleQueueHead); // Queue head done. // Because nothing else touches the async queue and this method is a synchronous method, // the idle queue head will now always be the end of the queue again. TailQueueHead = oldTailQH.queueHead; }