/// <summary> /// Sets up an OUT transaction and adds it to the specified transfer. /// </summary> /// <param name="transfer">The transfer to which the transaction should be added.</param> /// <param name="controlHandshake">Whether the transaction is part of a control handshake or not.</param> /// <param name="buffer">The buffer of outgoing data.</param> /// <param name="length">The length of the buffer.</param> public void OUTTransaction(USBTransfer transfer, bool controlHandshake, void *buffer, ushort length) { ushort clampedLength = FOS_System.Math.Min(transfer.packetSize, length); length -= clampedLength; ushort remainingTransactions = (ushort)(length / transfer.packetSize); if (length % transfer.packetSize != 0) { remainingTransactions++; } USBTransaction transaction = new USBTransaction(); transaction.type = USBTransactionType.OUT; if (controlHandshake) // Handshake transaction of control transfers always have toggle set to 1 { ((Endpoint)transfer.device.Endpoints[transfer.endpoint]).Toggle = true; } _OUTTransaction(transfer, transaction, ((Endpoint)transfer.device.Endpoints[transfer.endpoint]).Toggle, buffer, clampedLength); transfer.transactions.Add(transaction); ((Endpoint)transfer.device.Endpoints[transfer.endpoint]).Toggle = !((Endpoint)transfer.device.Endpoints[transfer.endpoint]).Toggle; // Switch toggle if (remainingTransactions > 0) { OUTTransaction(transfer, controlHandshake, ((byte *)buffer + clampedLength), length); } }
protected override void _OUTTransaction(USBTransfer transfer, USBTransaction uTransaction, bool toggle, void *buffer, ushort length) { #if UHCI_TRACE BasicConsole.WriteLine("UHCI: OUT Transaction"); BasicConsole.DelayOutput(5); #endif UHCITransaction uT = new UHCITransaction(); uTransaction.underlyingTz = uT; uT.inBuffer = null; uT.inLength = 0; uT.qTD = CreateQTD_IO((UHCI_QueueHead_Struct *)transfer.underlyingTransferData, (uint *)1, UHCI_Consts.TD_OUT, toggle, length, transfer.device.address, transfer.endpoint, transfer.packetSize); uT.qTDBuffer = uT.qTD->virtBuffer; if (buffer != null && length != 0) { MemoryUtils.MemCpy_32((byte *)uT.qTDBuffer, (byte *)buffer, length); } if (transfer.transactions.Count > 0) { UHCITransaction uLastTransaction = (UHCITransaction)((USBTransaction)(transfer.transactions[transfer.transactions.Count - 1])).underlyingTz; uLastTransaction.qTD->next = (((uint)VirtMemManager.GetPhysicalAddress(uT.qTD) & 0xFFFFFFF0) | UHCI_Consts.BIT_Vf); // build TD queue uLastTransaction.qTD->q_next = uT.qTD; } }
protected override void _SetupTransfer(USBTransfer transfer) { #if UHCI_TRACE BasicConsole.WriteLine("UHCI: Setup Transfer"); BasicConsole.DelayOutput(5); #endif transfer.underlyingTransferData = qhPointer; // QH }
/// <summary> /// Sets up a SETUP transaction and adds it to the specified transfer. /// </summary> /// <param name="transfer">The transfer to which the transaction should be added.</param> /// <param name="tokenBytes">The number of bytes to send.</param> /// <param name="type">The type of the USB Request.</param> /// <param name="req">The specific USB Request.</param> /// <param name="hiVal">The USB Request Hi-Val.</param> /// <param name="loVal">The USB Request Lo-Val.</param> /// <param name="index">The USB request index.</param> /// <param name="length">The length of the USB request.</param> public void SETUPTransaction(USBTransfer transfer, ushort tokenBytes, byte type, byte req, byte hiVal, byte loVal, ushort index, ushort length) { USBTransaction transaction = new USBTransaction(); transaction.type = USBTransactionType.SETUP; _SETUPTransaction(transfer, transaction, false, tokenBytes, type, req, hiVal, loVal, index, length); transfer.transactions.Add(transaction); ((Endpoint)transfer.device.Endpoints[transfer.endpoint]).Toggle = true; }
/// <summary> /// Sets up an IN transaction and adds it to the specified transfer. /// </summary> /// <param name="transfer">The transfer to which the transaction should be added.</param> /// <param name="controlHandshake">Whether the transaction is part of a control handshake or not.</param> /// <param name="buffer">The buffer to store the incoming data in.</param> /// <param name="length">The length of the buffer.</param> public void INTransaction(USBTransfer transfer, bool controlHandshake, void *buffer, ushort length) { #if HCI_TRACE || USB_TRACE BasicConsole.WriteLine(((FOS_System.String) "transfer.packetSize=") + transfer.packetSize + ", length=" + length); #endif ushort clampedLength = FOS_System.Math.Min(transfer.packetSize, length); length -= clampedLength; #if HCI_TRACE || USB_TRACE BasicConsole.WriteLine(((FOS_System.String) "clampedLength=") + clampedLength); BasicConsole.DelayOutput(1); #endif ushort remainingTransactions = (ushort)(length / transfer.packetSize); #if HCI_TRACE || USB_TRACE BasicConsole.WriteLine("Division passed."); BasicConsole.DelayOutput(1); #endif if (length % transfer.packetSize != 0) { remainingTransactions++; } USBTransaction transaction = new USBTransaction(); transaction.type = USBTransactionType.IN; if (controlHandshake) // Handshake transaction of control transfers always have toggle set to 1 { ((Endpoint)transfer.device.Endpoints[transfer.endpoint]).Toggle = true; } #if HCI_TRACE BasicConsole.WriteLine("Call _INTransaction..."); BasicConsole.DelayOutput(1); #endif _INTransaction(transfer, transaction, ((Endpoint)transfer.device.Endpoints[transfer.endpoint]).Toggle, buffer, clampedLength); #if HCI_TRACE BasicConsole.WriteLine("Done."); BasicConsole.DelayOutput(1); #endif transfer.transactions.Add(transaction); ((Endpoint)transfer.device.Endpoints[transfer.endpoint]).Toggle = !((Endpoint)transfer.device.Endpoints[transfer.endpoint]).Toggle; // Switch toggle if (remainingTransactions > 0) { INTransaction(transfer, controlHandshake, ((byte *)buffer + clampedLength), length); } }
/// <summary> /// Sets up a USB transfer for sending via the EHCI. /// </summary> /// <param name="usbDevice">The USb device to send the transfer to.</param> /// <param name="transfer">The transfer to send.</param> /// <param name="type">The type of USB transfer.</param> /// <param name="endpoint">The endpoint of the device to send the transfer to.</param> /// <param name="maxLength">The maximum packet size to use when transferring.</param> public void SetupTransfer(Devices.USBDeviceInfo usbDevice, USBTransfer transfer, USBTransferType type, byte endpoint, ushort maxLength) { transfer.device = usbDevice; transfer.endpoint = endpoint; transfer.type = type; #if HCI_TRACE BasicConsole.WriteLine(((FOS_System.String) "SetupTransfer: maxLength=") + maxLength + ", endpoint=" + endpoint + ", mps=" + ((Endpoint)usbDevice.Endpoints[endpoint]).mps); #endif transfer.packetSize = FOS_System.Math.Min(maxLength, ((Endpoint)usbDevice.Endpoints[endpoint]).MPS); #if HCI_TRACE BasicConsole.WriteLine(((FOS_System.String) "SetupTransfer: packetSize=") + transfer.packetSize); #endif transfer.success = false; transfer.transactions = new List(3); _SetupTransfer(transfer); }
protected override void _SETUPTransaction(USBTransfer transfer, USBTransaction uTransaction, bool toggle, ushort tokenBytes, byte type, byte req, byte hiVal, byte loVal, ushort index, ushort length) { #if UHCI_TRACE BasicConsole.WriteLine("UHCI: SETUP Transaction"); BasicConsole.DelayOutput(5); #endif UHCITransaction uT = new UHCITransaction(); uTransaction.underlyingTz = uT; uT.inBuffer = null; uT.inLength = 0; uT.qTD = CreateQTD_SETUP((UHCI_QueueHead_Struct *)transfer.underlyingTransferData, (uint *)1, toggle, tokenBytes, type, req, hiVal, loVal, index, length, transfer.device.address, transfer.endpoint, transfer.packetSize); uT.qTDBuffer = uT.qTD->virtBuffer; if (transfer.transactions.Count > 0) { UHCITransaction uLastTransaction = (UHCITransaction)((USBTransaction)(transfer.transactions[transfer.transactions.Count - 1])).underlyingTz; uLastTransaction.qTD->next = (((uint)VirtMemManager.GetPhysicalAddress(uT.qTD) & 0xFFFFFFF0) | UHCI_Consts.BIT_Vf); // build TD queue uLastTransaction.qTD->q_next = uT.qTD; } }
/// <summary> /// Sends a SCSI command that receives data. /// </summary> /// <param name="SCSIcommand">The SCSI command to send.</param> /// <param name="LBA">The LBA to access.</param> /// <param name="TransferLength">The length of the data to receive.</param> /// <param name="dataBuffer">The data buffer - must be at least as big as the transfer length.</param> /// <param name="statusBuffer">The buffer to store the command status result in. Must be at least 13 bytes long.</param> /// <see cref="!:http://www.beyondlogic.org/usbnutshell/usb4.htm#Bulk"/> public bool SendSCSICommand_IN(byte SCSIcommand, uint LBA, ushort TransferLength, void* dataBuffer, void* statusBuffer, bool resetOnFail = true) { #if MSD_TRACE DBGMSG("OUT part"); DBGMSG(((FOS_System.String)"Toggle OUT ") + ((Endpoint)DeviceInfo.Endpoints[DeviceInfo.MSD_OUTEndpointID]).toggle); #endif bool OK = true; //Allocate memory for the command block // This is passed to the out transaction as the SCSI command data. #if MSD_TRACE BasicConsole.Write("CBW Size: "); BasicConsole.Write(sizeof(CommandBlockWrapper)); if(sizeof(CommandBlockWrapper) != 31) { BasicConsole.WriteLine(" - INCORRECT! FAILED!"); } else { BasicConsole.WriteLine(" - correct."); } #endif CommandBlockWrapper* cbw = (CommandBlockWrapper*)FOS_System.Heap.AllocZeroed((uint)sizeof(CommandBlockWrapper), "MassStorageDevice : SendSCSICommand_IN (1)"); bool FreeStatusBuffer = false; try { //Initialise the command data SetupSCSICommand(SCSIcommand, cbw, LBA, TransferLength); #if MSD_TRACE BasicConsole.WriteLine("cbw: "); BasicConsole.DumpMemory(cbw, sizeof(CommandBlockWrapper)); DBGMSG("Setup command. Transferring data..."); #endif // Create a new USB transfer // This transfer is re-used for all transfers throughout this method USBTransfer transfer = new USBTransfer(); // Initialises the transfer // Sets the transfer packet size, points it to the MSD OUT endpoint and sets it as a bulk transfer DeviceInfo.hc.SetupTransfer(DeviceInfo, transfer, USBTransferType.Bulk, DeviceInfo.MSD_OUTEndpointID, 512); // Adds an OUT transaction to the transfer. // This OUT transaction ouptuts the SCSI command to the MSD DeviceInfo.hc.OUTTransaction(transfer, false, cbw, 31); // Issues the complete transfer to the device. DeviceInfo.hc.IssueTransfer(transfer); // If the transfer completed successfully. if (transfer.success) { #if MSD_TRACE DBGMSG("IN part"); #endif // If the caller didn't provide a pre-allocated status buffer, we // must allocate one. if (statusBuffer == null) { #if MSD_TRACE DBGMSG("Alloc 13 bytes of mem..."); #endif // And we must remember to only free it later if we created it. FreeStatusBuffer = true; // Create the pre-allocated buffer. Size 13 is the size of the response. statusBuffer = FOS_System.Heap.AllocZeroed(13u, "MassStorageDevice : SendSCSICommand_IN (2)"); } #if MSD_TRACE DBGMSG("Setup transfer..."); #endif // Setup a new transfer to receive the response from the device DeviceInfo.hc.SetupTransfer(DeviceInfo, transfer, USBTransferType.Bulk, DeviceInfo.MSD_INEndpointID, 512); #if MSD_TRACE DBGMSG("Done."); #endif // If the amount of data to receive is greater than 0 bytes in length: if (TransferLength > 0) { #if MSD_TRACE DBGMSG("Setup IN transactions..."); #endif // We must do an IN transaction to receive the data from the device DeviceInfo.hc.INTransaction(transfer, false, dataBuffer, TransferLength); // And then do the normal IN transaction to receive the status response. DeviceInfo.hc.INTransaction(transfer, false, statusBuffer, 13); #if MSD_TRACE DBGMSG("Done."); #endif } else { // No data to receive so just do the IN transaction to receive the status // response. DeviceInfo.hc.INTransaction(transfer, false, statusBuffer, 13); } #if MSD_TRACE DBGMSG("Issue transfer..."); #endif // Issue the transfer of IN data. DeviceInfo.hc.IssueTransfer(transfer); #if MSD_TRACE DBGMSG("Done."); DBGMSG("Check command..."); #endif // If the transfer failed or the status response indicates failure of some form: if (!transfer.success || CheckSCSICommand(statusBuffer, SCSIcommand) != 0) { // TODO: Handle failure/timeout #if MSD_TRACE DBGMSG("SCSI IN command failed!"); #endif OK = false; if (resetOnFail) { ResetRecoveryMSD(); } } #if MSD_TRACE else { DBGMSG("Command OK."); BasicConsole.DelayOutput(1); } #endif } else { // TODO: Handle failure/timeout OK = false; #if MSD_TRACE DBGMSG("SCSI OUT command failed!"); #endif } } finally { FOS_System.Heap.Free(cbw); // Only free the status buffer if we allocated it if (FreeStatusBuffer) { FOS_System.Heap.Free(statusBuffer); } } return OK; }
/// <summary> /// Issues the specified transfer to the physical device via the async schedule. /// </summary> /// <param name="transfer">The transfer to issue.</param> protected override void _IssueTransfer(USBTransfer transfer) { if (HostSystemErrors > 0 || IrrecoverableError) { ExceptionMethods.Throw(new FOS_System.Exception("Cannot issue transfer through EHCI because the host controller has encountered a host system error.")); } else if (HCHalted) { ExceptionMethods.Throw(new FOS_System.Exception("Cannot issue transfer through EHCI because the host controller is currently halted.")); } // Please note: The word "completed" is not synonymous with "succeeded". // "Completed" means the hardware, firmware or software finished // processing something. // "Succeeded" means the hardware, firmware or software finished // processing something and there were no errors during processing. // Get the last qTD of the transfer EHCITransaction lastTransaction = (EHCITransaction)((USBTransaction)transfer.transactions[transfer.transactions.Count - 1]).underlyingTz; EHCI_qTD lastQTD = lastTransaction.qTD; // Enable the Interrupt on Complete. This allows us to detect the end of the entire transfer // when the USB Interrupt occurs. lastQTD.InterruptOnComplete = true; #if EHCI_TRACE //Test walking the transaction tree bool treeOK = true; for (int k = 0; k < transfer.transactions.Count - 1; k++) { EHCITransaction transaction1 = (EHCITransaction)((USBTransaction)transfer.transactions[k]).underlyingTz; EHCITransaction transaction2 = (EHCITransaction)((USBTransaction)transfer.transactions[k + 1]).underlyingTz; EHCI_qTD qtd1 = transaction1.qTD; treeOK = treeOK && (qtd1.NextqTDPointer == VirtMemManager.GetPhysicalAddress(transaction2.qTD.qtd)) && !qtd1.NextqTDPointerTerminate; if (!treeOK) { BasicConsole.Write(((FOS_System.String)"Incorrect tansfer index: ") + k); if (qtd1.NextqTDPointer != VirtMemManager.GetPhysicalAddress(transaction2.qTD.qtd)) { BasicConsole.WriteLine(((FOS_System.String)" > Pointers incorrect! QTD1.NextPtr=") + (uint)qtd1.NextqTDPointer + ", &QTD2=" + (uint)VirtMemManager.GetPhysicalAddress(transaction2.qTD.qtd)); } else if (qtd1.NextqTDPointerTerminate) { BasicConsole.WriteLine(" > QTD1.NextTerminate incorrect!"); } else { BasicConsole.WriteLine(" > Previous transaction was incorrect."); } } } { treeOK = treeOK && lastQTD.NextqTDPointerTerminate; if (!treeOK) { BasicConsole.WriteLine("Incorrect tansfer index: last"); if (!lastQTD.NextqTDPointerTerminate) { BasicConsole.WriteLine(" > LastQTD.NextTerminate incorrect!"); } else { BasicConsole.WriteLine(" > Previous transaction was incorrect."); } } } DBGMSG(((FOS_System.String)"Transfer transactions tree OK: ") + treeOK); BasicConsole.DelayOutput(10); #endif // Get the first qTD of the transfer. This is passed to InitQH to tell it the start of the linked // list of transactions. EHCITransaction firstTransaction = (EHCITransaction)((USBTransaction)(transfer.transactions[0])).underlyingTz; // Init the Queue Head for this transfer InitQH((EHCI_QueueHead_Struct*)transfer.underlyingTransferData, (EHCI_QueueHead_Struct*)transfer.underlyingTransferData, firstTransaction.qTD.qtd, false, transfer.device.address, transfer.endpoint, transfer.packetSize); // Attempt to issue the transfer until it either succeeds or we reach our // maximum number of retries or an irrecoverable host system error occurs. // The maxmimum number of retries is an entirely arbitary, internal number. for (byte i = 0; i < EHCI_Consts.NumAsyncListRetries && !transfer.success && !IrrecoverableError; i++) { #if EHCI_TRACE transfer.success = true; for (int k = 0; k < transfer.transactions.Count; k++) { EHCITransaction transaction = (EHCITransaction)((USBTransaction)transfer.transactions[k]).underlyingTz; byte status = transaction.qTD.Status; transfer.success = transfer.success && (status == 0 || status == Utils.BIT(0)); DBGMSG(((FOS_System.String)"PRE Issue: Transaction ") + k + " status: " + status); } if (!transfer.success) { DBGMSG("EHCI: PRE Issue - Transfer detected as failed."); BasicConsole.DelayOutput(1); } else { DBGMSG("EHCI: PRE Issue - Transfer OK."); } #endif // Add it to the async schedule. This will cause the HC to attempt to send the transactions. // This is a blocking method that waits until the HC signals the last transaction of the // transfer is complete. This does not mean all or the last transaction completed succesfully. AddToAsyncSchedule(transfer); // If during the transfer we hit an irrecoverable host system error: if (IrrecoverableError) { // Mark the transfer as failed transfer.success = false; #if EHCI_TRACE DBGMSG("EHCI: Irrecoverable error! No retry."); BasicConsole.DelayOutput(2); #endif } else { // Assume the transfer succeeded to start with transfer.success = true; // Then check each transaction to see if it succeeded. for (int k = 0; k < transfer.transactions.Count; k++) { // Get the transaction to check EHCITransaction transaction = (EHCITransaction)((USBTransaction)transfer.transactions[k]).underlyingTz; // Get the transaction's status byte status = transaction.qTD.Status; // If the status == 0, it indicates success // If bit 0 of the status is set and the other buts are 0, // then since this must be a High Speed endpoint, it is just the // Ping State. // It is worth noting that the Ping State bit is only valid if the // endpoint is an OUT endpoint. However, the specification also suggests // the this bit is only an error bit if the device is a low- or full-speed // device. This means the value for IN endpoints on High Speed devices is // somewhat undefined and I am lead to assume the value must always be 0. transfer.success = transfer.success && (status == 0 || status == Utils.BIT(0)); #if EHCI_TRACE DBGMSG(((FOS_System.String)"POST Issue: Transaction ") + k + " status: " + status); #endif } #if EHCI_TRACE if (!transfer.success) { DBGMSG(((FOS_System.String)"EHCI: Retry transfer: ") + (i + 1)); BasicConsole.DelayOutput(2); // Reset the status bits so the transactions are active again for (int k = 0; k < transfer.transactions.Count; k++) { EHCITransaction transaction = (EHCITransaction)((USBTransaction)transfer.transactions[k]).underlyingTz; byte status = transaction.qTD.Status; if (!(status == 0 || status == Utils.BIT(0))) { transaction.qTD.Status = 0x80; } } } #endif } } // After the transfer has completed, we can free the underlying queue head memory. // At this point we use the Async Doorbell to confirm the // HC has finished using the queue head and that all caches of // pointers to the queue head have been released. AsyncDoorbellIntCount = 1; USBCMD |= EHCI_Consts.CMD_AsyncInterruptDoorbellMask; while (AsyncDoorbellIntCount > 0) { Kernel.Processes.SystemCalls.SleepThread(5); } FOS_System.Heap.Free(transfer.underlyingTransferData); // Loop through each transaction in the transfer for (int k = 0; k < transfer.transactions.Count; k++) { // Get the current transaction EHCITransaction transaction = (EHCITransaction)((USBTransaction)transfer.transactions[k]).underlyingTz; // Create a wrapper for the underlying qTD of the transaction EHCI_qTD theQTD = transaction.qTD; // If the transaction has an input buffer, we must copy the input data from the qTD // to the input buffer. // Note: The reason the qTD has a different buffer to the input buffer is because when // the input buffer was provided, there was no guarantee it had been properly // allocated (i.e. size and alignment may have been incorrect) if (transaction.inBuffer != null && transaction.inLength != 0) { #if EHCI_TRACE DBGMSG(((FOS_System.String)"Doing MemCpy of in data... inBuffer=") + (uint)transaction.inBuffer + ", qTDBuffer=" + (uint)transaction.qTD.qtd + ", inLength=" + transaction.inLength + ", Data to copy: "); #endif // Copy the memory Utilities.MemoryUtils.MemCpy_32((byte*)transaction.inBuffer, theQTD.Buffer0VirtAddr, transaction.inLength); #if EHCI_TRACE //for (int i = 0; i < transaction.inLength; i++) //{ // DBGMSG(((FOS_System.String)"i=") + i + ", qTDBuffer[i]=" + ((byte*)transaction.qTD.qtd)[i] + ", inBuffer[i]=" + ((byte*)transaction.inBuffer)[i]); //} #endif #if EHCI_TRACE DBGMSG("Done."); BasicConsole.DelayOutput(2); #endif } // Free the qTD buffer(s) FOS_System.Heap.Free(theQTD.Buffer0VirtAddr); // Free the qTD theQTD.Free(); } #if EHCI_TRACE if (transfer.success) { DBGMSG("EHCI: Transfer succeeded."); } else { DBGMSG("EHCI:IssueTransfer(): Transfer failed."); } BasicConsole.DelayOutput(2); #endif }
/// <summary> /// Sets up an IN transaction and adds it to the specified transfer. /// </summary> /// <param name="transfer">The transfer to which the transaction should be added.</param> /// <param name="uTransaction">The USB Transaction to convert to an EHCI transaction.</param> /// <param name="toggle">The transaction toggle state.</param> /// <param name="buffer">The buffer to store the incoming data in.</param> /// <param name="length">The length of the buffer.</param> protected override void _INTransaction(USBTransfer transfer, USBTransaction uTransaction, bool toggle, void* buffer, ushort length) { // Create an EHCI-specific object to describe the transaction EHCITransaction eTransaction = new EHCITransaction(); // Store the underlying HC-specific transaction info uTransaction.underlyingTz = eTransaction; // IN transaction so use the supplied input data buffer eTransaction.inBuffer = buffer; eTransaction.inLength = length; #if EHCI_TRACE DBGMSG(((FOS_System.String)"IN Transaction : buffer=") + (uint)buffer); DBGMSG(((FOS_System.String)"IN Transaction : Before CreateQTD : bufferPtr=&qTDBuffer=") + (uint)buffer); #endif // Create and initialise the IN queue transfer descriptor eTransaction.qTD = CreateQTD_IO(null, 1, toggle, length, length); #if EHCI_TRACE DBGMSG(((FOS_System.String)"IN Transaction : After CreateQTD : bufferPtr=&qTDBuffer=") + (uint)buffer + ", Buffer0=" + (uint)eTransaction.qTD.Buffer0); #endif // If the number of existing transactions is greater than 0 // i.e. some transactions have already been added. if (transfer.transactions.Count > 0) { // Get the previous (i.e. last) transaction then the underlying transaction from it EHCITransaction eLastTransaction = (EHCITransaction)((USBTransaction)(transfer.transactions[transfer.transactions.Count - 1])).underlyingTz; // Create a wrapper for the last transaction (qTD) EHCI_qTD lastQTD = eLastTransaction.qTD; // Set the Next Transaction (qTD) Pointer on the previous qTD to point to the qTD // we just created. // Note: The NextqTDPointer must be the physical address of qTD data. lastQTD.NextqTDPointer = (EHCI_qTD_Struct*)VirtMemManager.GetPhysicalAddress(eTransaction.qTD.qtd); // Mark the previous qTD's Next Transaction Pointer as valid. lastQTD.NextqTDPointerTerminate = false; } }
/// <summary> /// Sets up a USB transfer for sending via the EHCI. /// </summary> /// <param name="transfer">The transfer to set up.</param> protected override void _SetupTransfer(USBTransfer transfer) { // Allocate memory for the transfer strcture. This is a queue head strcture. // It gets appended to the end of the Async Queue in AddToAsyncSchedule. // This memory must be allocated on a 32-byte boundary as per EHCI spec. // "The memory structure referenced by this physical memory pointer is // assumed to be 32-byte (cache line) aligned." // Section 2.3.7 of Intel EHCI Spec // Note: This sets the virtual address. This allows it to be accessed and freed by the // driver. However, when passing the address to the HC, it must be converted to // a physical address. transfer.underlyingTransferData = (EHCI_QueueHead_Struct*)FOS_System.Heap.AllocZeroedAPB((uint)sizeof(EHCI_QueueHead_Struct), 32, "EHCI : _SetupTransfer"); }
/// <summary> /// Sets up an OUT transaction and adds it to the specified transfer. /// </summary> /// <param name="transfer">The transfer to which the transaction should be added.</param> /// <param name="controlHandshake">Whether the transaction is part of a control handshake or not.</param> /// <param name="buffer">The buffer of outgoing data.</param> /// <param name="length">The length of the buffer.</param> public void OUTTransaction(USBTransfer transfer, bool controlHandshake, void* buffer, ushort length) { ushort clampedLength = FOS_System.Math.Min(transfer.packetSize, length); length -= clampedLength; ushort remainingTransactions = (ushort)(length / transfer.packetSize); if (length % transfer.packetSize != 0) remainingTransactions++; USBTransaction transaction = new USBTransaction(); transaction.type = USBTransactionType.OUT; if (controlHandshake) // Handshake transaction of control transfers always have toggle set to 1 { ((Endpoint)transfer.device.Endpoints[transfer.endpoint]).Toggle = true; } _OUTTransaction(transfer, transaction, ((Endpoint)transfer.device.Endpoints[transfer.endpoint]).Toggle, buffer, clampedLength); transfer.transactions.Add(transaction); ((Endpoint)transfer.device.Endpoints[transfer.endpoint]).Toggle = !((Endpoint)transfer.device.Endpoints[transfer.endpoint]).Toggle; // Switch toggle if (remainingTransactions > 0) { OUTTransaction(transfer, controlHandshake, ((byte*)buffer + clampedLength), length); } }
/// <summary> /// Sends a SCSI command that sends data. /// </summary> /// <param name="SCSIcommand">The SCSI command to send.</param> /// <param name="LBA">The LBA to access.</param> /// <param name="TransferLength">The length of the data to send.</param> /// <param name="dataBuffer">The data buffer - must be at least as long as the transfer length.</param> /// <param name="statusBuffer">The data buffer to store the command status result in. Must be at least 13 bytes long.</param> public void SendSCSICommand_OUT(byte SCSIcommand, uint LBA, ushort TransferLength, void* dataBuffer, void* statusBuffer) { #if MSD_TRACE DBGMSG("OUT part"); DBGMSG(((FOS_System.String)"Toggle OUT ") + ((Endpoint)DeviceInfo.Endpoints[DeviceInfo.MSD_OUTEndpointID]).toggle); #endif //This method work pretty much the same as the SendSCSICommand_IN method so see there for docs. CommandBlockWrapper* cbw = (CommandBlockWrapper*)FOS_System.Heap.AllocZeroed((uint)sizeof(CommandBlockWrapper), "MassStorageDevice : SendSCSICommand_OUT (1)"); bool FreeStatusBuffer = false; try { SetupSCSICommand(SCSIcommand, cbw, LBA, TransferLength); #if MSD_TRACE DBGMSG("Setup transfer..."); #endif //Issue the command transfer USBTransfer transfer = new USBTransfer(); DeviceInfo.hc.SetupTransfer(DeviceInfo, transfer, USBTransferType.Bulk, DeviceInfo.MSD_OUTEndpointID, 512); DeviceInfo.hc.OUTTransaction(transfer, false, cbw, 31); DeviceInfo.hc.IssueTransfer(transfer); // If the command transfer completed successfully: if (transfer.success) { //Issue the output data transfer // Through much testing / debugging I discovered that you cannot do the data OUT // transaction in the same transfer as the command OUT transaction. This seems odd // to me and I have not found any documentation or specification to back up this // behaviour. There are two possibilities here: // 1) This is a genuine part of some spec / requirement, in which case we have // no issue. // 2) Or my implementation for issusing multiple OUT transactions in a row in // one is broken. In this case, I have failed to find out what causes ths issue // / why it occurs. DeviceInfo.hc.SetupTransfer(DeviceInfo, transfer, USBTransferType.Bulk, DeviceInfo.MSD_OUTEndpointID, 512); DeviceInfo.hc.OUTTransaction(transfer, false, dataBuffer, TransferLength); DeviceInfo.hc.IssueTransfer(transfer); #if MSD_TRACE if (!transfer.success) { DBGMSG("Transfer 2 failed!"); BasicConsole.DelayOutput(5); } #endif } #if MSD_TRACE else { DBGMSG("Transfer 1 failed!"); BasicConsole.DelayOutput(5); } #endif // If the command and data transfers completed successfully: if (transfer.success) { #if MSD_TRACE DBGMSG("IN part"); #endif if (statusBuffer == null) { #if MSD_TRACE DBGMSG("Alloc 13 bytes of mem..."); #endif FreeStatusBuffer = true; statusBuffer = FOS_System.Heap.AllocZeroed(13u, "MassStorageDevice : SendSCSICommand_OUT (2)"); } #if MSD_TRACE DBGMSG("Setup transfer..."); #endif DeviceInfo.hc.SetupTransfer(DeviceInfo, transfer, USBTransferType.Bulk, DeviceInfo.MSD_INEndpointID, 512); #if MSD_TRACE DBGMSG("Done."); #endif DeviceInfo.hc.INTransaction(transfer, false, statusBuffer, 13); #if MSD_TRACE DBGMSG("Issue transfer..."); #endif DeviceInfo.hc.IssueTransfer(transfer); #if MSD_TRACE DBGMSG("Done."); DBGMSG("Check command..."); #endif if (!transfer.success || CheckSCSICommand(statusBuffer, SCSIcommand) != 0) { // TODO: Handle failure/timeout #if MSD_TRACE DBGMSG("SCSI IN command failed!"); #endif } #if MSD_TRACE else { DBGMSG("Command OK."); BasicConsole.DelayOutput(1); } #endif } else { // TODO: Handle failure/timeout #if MSD_TRACE DBGMSG("SCSI OUT command failed!"); #endif } } finally { FOS_System.Heap.Free(cbw); if (FreeStatusBuffer) { FOS_System.Heap.Free(statusBuffer); } } }
/// <summary> /// Issues the specified transfer to the physical device. /// </summary> /// <param name="transfer">The transfer to issue.</param> public void IssueTransfer(USBTransfer transfer) { _IssueTransfer(transfer); }
/// <summary> /// When overridden in a derived class, handles HC implementation specific method of issuing a transfer. /// </summary> /// <param name="transfer">The transfer to issue.</param> protected abstract void _IssueTransfer(USBTransfer transfer);
/// <summary> /// When overridden in a derived class, handles HC implementation specific OUT transaction initialisation. /// </summary> /// <param name="transfer">The transfer to which the transaction should be added.</param> /// <param name="uTransaction">The USB Transaction to convert to an EHCI transaction.</param> /// <param name="toggle">The transaction toggle state.</param> /// <param name="buffer">The buffer of outgoing data.</param> /// <param name="length">The length of the buffer.</param> protected abstract void _OUTTransaction(USBTransfer transfer, USBTransaction uTransaction, bool toggle, void* buffer, ushort length);
/// <summary> /// When overridden in a derived class, handles HC implementation specific SETUP transaction initialisation. /// </summary> /// <param name="transfer">The transfer to which the transaction should be added.</param> /// <param name="uTransaction">The USB Transaction to convert to an EHCI Transaction.</param> /// <param name="toggle">The transaction toggle state.</param> /// <param name="tokenBytes">The number of bytes to send.</param> /// <param name="type">The type of the USB Request.</param> /// <param name="req">The specific USB Request.</param> /// <param name="hiVal">The USB Request Hi-Val.</param> /// <param name="loVal">The USB Request Lo-Val.</param> /// <param name="index">The USB request index.</param> /// <param name="length">The length of the USB request.</param> protected abstract void _SETUPTransaction(USBTransfer transfer, USBTransaction uTransaction, bool toggle, ushort tokenBytes, byte type, byte req, byte hiVal, byte loVal, ushort index, ushort length);
/// <summary> /// When overridden in a derived class, handles HC implementation specific transfer initialisation. /// </summary> /// <param name="transfer">The transfer to set up.</param> protected abstract void _SetupTransfer(USBTransfer transfer);
/// <summary> /// Sends a SCSI Sync Cache command. /// </summary> /// <param name="SyncNV"> /// Whether the device is required to sync volatile and non-volatile caches or not. Specify /// "false" to force full cache sync including non-volatile caches. Specify "true" to force /// sync volatile caches and optionally sync non-volatile caches (up to the specific device /// what it does). /// </param> /// <param name="ImmediateResponse"> /// Whether the device should send the status response as soon as the command is recognised /// or the device should only send the response once the sync is completed. Specify "false" /// for the latter option. /// </param> /// <param name="LBA">The LBA of the first block to sync.</param> /// <param name="Blocks"> /// The number of blocks to sync. Specify 0 to sync all the blocks from the start LBA through /// to the end of the device. /// </param> /// <param name="statusBuffer"> /// Buffer for the status response. Specify null if you don't want to keep the response data. /// </param> public void SendSCSI_SyncCacheCommand(bool SyncNV, bool ImmediateResponse, uint LBA, ushort Blocks, void* statusBuffer) { #if MSD_TRACE DBGMSG("SyncCache Command"); DBGMSG("OUT part"); DBGMSG(((FOS_System.String)"Toggle OUT ") + ((Endpoint)DeviceInfo.Endpoints[DeviceInfo.MSD_OUTEndpointID]).toggle); #endif CommandBlockWrapper* cbw = (CommandBlockWrapper*)FOS_System.Heap.AllocZeroed((uint)sizeof(CommandBlockWrapper), "MassStorageDevice : SendSCSI_SyncCacheCommand (1)"); bool FreeStatusBuffer = false; try { SetupSCSICommand(0x35, cbw, LBA, Blocks); cbw->commandByte[1] = (byte)((SyncNV ? 0x4 : 0) | (ImmediateResponse ? 0x2 : 0)); #if MSD_TRACE DBGMSG("Setup transfer..."); #endif USBTransfer transfer = new USBTransfer(); DeviceInfo.hc.SetupTransfer(DeviceInfo, transfer, USBTransferType.Bulk, DeviceInfo.MSD_OUTEndpointID, 512); DeviceInfo.hc.OUTTransaction(transfer, false, cbw, 31); DeviceInfo.hc.IssueTransfer(transfer); if (transfer.success) { #if MSD_TRACE DBGMSG("IN part"); #endif if (statusBuffer == null) { #if MSD_TRACE DBGMSG("Alloc 13 bytes of mem..."); #endif FreeStatusBuffer = true; statusBuffer = FOS_System.Heap.AllocZeroed(13u, "MassStorageDevice : SendSCSI_SyncCacheCommand (2)"); } #if MSD_TRACE DBGMSG("Setup transfer..."); #endif DeviceInfo.hc.SetupTransfer(DeviceInfo, transfer, USBTransferType.Bulk, DeviceInfo.MSD_INEndpointID, 512); #if MSD_TRACE DBGMSG("Done."); #endif DeviceInfo.hc.INTransaction(transfer, false, statusBuffer, 13); #if MSD_TRACE DBGMSG("Issue transfer..."); #endif DeviceInfo.hc.IssueTransfer(transfer); #if MSD_TRACE DBGMSG("Done."); DBGMSG("Check command..."); #endif if (!transfer.success || CheckSCSICommand(statusBuffer, 0x35) != 0) { // TODO: Handle failure/timeout #if MSD_TRACE DBGMSG("SCSI SyncCaches (10) (In) command failed!"); #endif } #if MSD_TRACE else { DBGMSG("Command OK."); BasicConsole.DelayOutput(1); } #endif } else { // TODO: Handle failure/timeout #if MSD_TRACE DBGMSG("SCSI SyncCache (10) (Out) command failed!"); #endif } } finally { FOS_System.Heap.Free(cbw); if (FreeStatusBuffer) { FOS_System.Heap.Free(statusBuffer); } } }
protected override void _SETUPTransaction(USBTransfer transfer, USBTransaction uTransaction, bool toggle, ushort tokenBytes, byte type, byte req, byte hiVal, byte loVal, ushort index, ushort length) { #if UHCI_TRACE BasicConsole.WriteLine("UHCI: SETUP Transaction"); BasicConsole.DelayOutput(5); #endif UHCITransaction uT = new UHCITransaction(); uTransaction.underlyingTz = uT; uT.inBuffer = null; uT.inLength = 0; uT.qTD = CreateQTD_SETUP((UHCI_QueueHead_Struct*)transfer.underlyingTransferData, (uint*)1, toggle, tokenBytes, type, req, hiVal, loVal, index, length, transfer.device.address, transfer.endpoint, transfer.packetSize); uT.qTDBuffer = uT.qTD->virtBuffer; if (transfer.transactions.Count > 0) { UHCITransaction uLastTransaction = (UHCITransaction)((USBTransaction)(transfer.transactions[transfer.transactions.Count - 1])).underlyingTz; uLastTransaction.qTD->next = (((uint)VirtMemManager.GetPhysicalAddress(uT.qTD) & 0xFFFFFFF0) | UHCI_Consts.BIT_Vf); // build TD queue uLastTransaction.qTD->q_next = uT.qTD; } }
/// <summary> /// Sets up an IN transaction and adds it to the specified transfer. /// </summary> /// <param name="transfer">The transfer to which the transaction should be added.</param> /// <param name="controlHandshake">Whether the transaction is part of a control handshake or not.</param> /// <param name="buffer">The buffer to store the incoming data in.</param> /// <param name="length">The length of the buffer.</param> public void INTransaction(USBTransfer transfer, bool controlHandshake, void* buffer, ushort length) { #if HCI_TRACE || USB_TRACE BasicConsole.WriteLine(((FOS_System.String)"transfer.packetSize=") + transfer.packetSize + ", length=" + length); #endif ushort clampedLength = FOS_System.Math.Min(transfer.packetSize, length); length -= clampedLength; #if HCI_TRACE || USB_TRACE BasicConsole.WriteLine(((FOS_System.String)"clampedLength=") + clampedLength); BasicConsole.DelayOutput(1); #endif ushort remainingTransactions = (ushort)(length / transfer.packetSize); #if HCI_TRACE || USB_TRACE BasicConsole.WriteLine("Division passed."); BasicConsole.DelayOutput(1); #endif if (length % transfer.packetSize != 0) { remainingTransactions++; } USBTransaction transaction = new USBTransaction(); transaction.type = USBTransactionType.IN; if (controlHandshake) // Handshake transaction of control transfers always have toggle set to 1 { ((Endpoint)transfer.device.Endpoints[transfer.endpoint]).Toggle = true; } #if HCI_TRACE BasicConsole.WriteLine("Call _INTransaction..."); BasicConsole.DelayOutput(1); #endif _INTransaction(transfer, transaction, ((Endpoint)transfer.device.Endpoints[transfer.endpoint]).Toggle, buffer, clampedLength); #if HCI_TRACE BasicConsole.WriteLine("Done."); BasicConsole.DelayOutput(1); #endif transfer.transactions.Add(transaction); ((Endpoint)transfer.device.Endpoints[transfer.endpoint]).Toggle = !((Endpoint)transfer.device.Endpoints[transfer.endpoint]).Toggle; // Switch toggle if (remainingTransactions > 0) { INTransaction(transfer, controlHandshake, ((byte*)buffer + clampedLength), length); } }
protected override void _IssueTransfer(USBTransfer transfer) { #if UHCI_TRACE BasicConsole.WriteLine("UHCI: Issue Transfer"); BasicConsole.DelayOutput(5); #endif UHCITransaction firstTransaction = (UHCITransaction)((USBTransaction)transfer.transactions[0]).underlyingTz; UHCITransaction lastTransaction = (UHCITransaction)((USBTransaction)transfer.transactions[transfer.transactions.Count - 1]).underlyingTz; UHCI_qTD.SetIntOnComplete(lastTransaction.qTD, true); // We want an interrupt after complete transfer CreateQH((UHCI_QueueHead_Struct *)transfer.underlyingTransferData, (uint)transfer.underlyingTransferData, firstTransaction.qTD); #if UHCI_TRACE BasicConsole.WriteLine(" Queue head data:"); BasicConsole.DumpMemory(transfer.underlyingTransferData, sizeof(UHCI_QueueHead_Struct)); BasicConsole.WriteLine(" Transactions data:"); for (int i = 0; i < transfer.transactions.Count; i++) { BasicConsole.Write(" "); BasicConsole.Write(i); BasicConsole.WriteLine(" : "); BasicConsole.WriteLine(" - qTD:"); BasicConsole.DumpMemory( ((UHCITransaction)((USBTransaction)transfer.transactions[i]).underlyingTz).qTD, sizeof(UHCI_qTD_Struct)); BasicConsole.WriteLine(" - qTDBuffer:"); BasicConsole.DumpMemory( ((UHCITransaction)((USBTransaction)transfer.transactions[i]).underlyingTz).qTDBuffer, 16); } BasicConsole.DelayOutput(60); BasicConsole.WriteLine("UHCI: Issuing transfer..."); #endif for (byte i = 0; i < UHCI_Consts.NUMBER_OF_UHCI_RETRIES && !transfer.success; i++) { TransactionsCompleted = 0; for (int j = 0; j < transfer.transactions.Count; j++) { USBTransaction elem = (USBTransaction)transfer.transactions[j]; UHCITransaction uT = (UHCITransaction)(elem.underlyingTz); uT.qTD->u1 = uT.qTD->u1 & 0xFF00FFFF; UHCI_qTD.SetActive(uT.qTD, true); } // stop scheduler USBSTS.Write_UInt16(UHCI_Consts.STS_MASK); USBCMD.Write_UInt16((ushort)(USBCMD.Read_UInt16() & ~UHCI_Consts.CMD_RS)); while ((USBSTS.Read_UInt16() & UHCI_Consts.STS_HCHALTED) == 0) { Hardware.Devices.Timer.Default.Wait(10); } // update scheduler uint qhPhysAddr = ((uint)VirtMemManager.GetPhysicalAddress(transfer.underlyingTransferData) | UHCI_Consts.BIT_QH); FrameList[0] = qhPhysAddr; FRBASEADD.Write_UInt32((uint)VirtMemManager.GetPhysicalAddress(FrameList)); FRNUM.Write_UInt16(0); // start scheduler USBSTS.Write_UInt16(UHCI_Consts.STS_MASK); USBCMD.Write_UInt16((ushort)(USBCMD.Read_UInt16() | UHCI_Consts.CMD_RS)); while ((USBSTS.Read_UInt16() & UHCI_Consts.STS_HCHALTED) != 0) { Hardware.Devices.Timer.Default.Wait(10); } #if UHCI_TRACE BasicConsole.WriteLine(((FOS_System.String) "USBINT val: ") + USBINTR.Read_UInt16()); #endif // run transactions bool active = true; int timeout = 100; //5 seconds while (active && timeout > 0) { active = false; for (int j = 0; j < transfer.transactions.Count; j++) { USBTransaction elem = (USBTransaction)transfer.transactions[j]; UHCITransaction uT = (UHCITransaction)(elem.underlyingTz); active = active || ((uT.qTD->u1 & 0x00FF0000) == 0x00800000); } Hardware.Devices.Timer.Default.Wait(50); timeout--; } #if UHCI_TRACE BasicConsole.WriteLine("Finished waiting."); #endif FrameList[0] = UHCI_Consts.BIT_T; if (timeout == 0 || TransactionsCompleted != transfer.transactions.Count) { #if UHCI_TRACE BasicConsole.SetTextColour(BasicConsole.error_colour); BasicConsole.WriteLine("UHCI: Error! Transactions wait timed out or wrong number of transactions completed."); BasicConsole.SetTextColour(BasicConsole.default_colour); BasicConsole.WriteLine(((FOS_System.String) "Transactions completed: ") + TransactionsCompleted); if (timeout == 0) { BasicConsole.SetTextColour(BasicConsole.error_colour); BasicConsole.WriteLine("UHCI: Error! Transfer timed out."); BasicConsole.SetTextColour(BasicConsole.default_colour); } #endif transfer.success = false; bool completeDespiteNoInterrupt = true; for (int j = 0; j < transfer.transactions.Count; j++) { USBTransaction elem = (USBTransaction)transfer.transactions[j]; UHCITransaction uT = (UHCITransaction)(elem.underlyingTz); #if UHCI_TRACE BasicConsole.WriteLine(((FOS_System.String) "u1=") + uT.qTD->u1 + ", u2=" + uT.qTD->u2); BasicConsole.WriteLine(((FOS_System.String) "Status=") + (byte)(uT.qTD->u1 >> 16)); #endif completeDespiteNoInterrupt = completeDespiteNoInterrupt && isTransactionSuccessful(uT); } transfer.success = completeDespiteNoInterrupt; #if UHCI_TRACE BasicConsole.SetTextColour(BasicConsole.warning_colour); BasicConsole.WriteLine(((FOS_System.String) "Complete despite no interrupts: ") + completeDespiteNoInterrupt); BasicConsole.SetTextColour(BasicConsole.default_colour); BasicConsole.DelayOutput(5); #endif } else { transfer.success = true; } if (transfer.success) { // check conditions and save data for (int j = 0; j < transfer.transactions.Count; j++) { USBTransaction elem = (USBTransaction)transfer.transactions[j]; UHCITransaction uT = (UHCITransaction)(elem.underlyingTz); transfer.success = transfer.success && isTransactionSuccessful(uT); // executed w/o error if (uT.inBuffer != null && uT.inLength != 0) { MemoryUtils.MemCpy_32((byte *)uT.inBuffer, (byte *)uT.qTDBuffer, uT.inLength); } } } #if UHCI_TRACE if (!transfer.success) { BasicConsole.SetTextColour(BasicConsole.error_colour); BasicConsole.WriteLine("UHCI: Transfer failed."); BasicConsole.SetTextColour(BasicConsole.default_colour); } else { BasicConsole.SetTextColour((char)0x0200); BasicConsole.WriteLine("Transfer succeeded."); BasicConsole.SetTextColour(BasicConsole.default_colour); } #endif } }
protected override void _OUTTransaction(USBTransfer transfer, USBTransaction uTransaction, bool toggle, void* buffer, ushort length) { #if UHCI_TRACE BasicConsole.WriteLine("UHCI: OUT Transaction"); BasicConsole.DelayOutput(5); #endif UHCITransaction uT = new UHCITransaction(); uTransaction.underlyingTz = uT; uT.inBuffer = null; uT.inLength = 0; uT.qTD = CreateQTD_IO((UHCI_QueueHead_Struct*)transfer.underlyingTransferData, (uint*)1, UHCI_Consts.TD_OUT, toggle, length, transfer.device.address, transfer.endpoint, transfer.packetSize); uT.qTDBuffer = uT.qTD->virtBuffer; if (buffer != null && length != 0) { MemoryUtils.MemCpy_32((byte*)uT.qTDBuffer, (byte*)buffer, length); } if (transfer.transactions.Count > 0) { UHCITransaction uLastTransaction = (UHCITransaction)((USBTransaction)(transfer.transactions[transfer.transactions.Count - 1])).underlyingTz; uLastTransaction.qTD->next = (((uint)VirtMemManager.GetPhysicalAddress(uT.qTD) & 0xFFFFFFF0) | UHCI_Consts.BIT_Vf); // build TD queue uLastTransaction.qTD->q_next = uT.qTD; } }
protected override void _IssueTransfer(USBTransfer transfer) { #if UHCI_TRACE BasicConsole.WriteLine("UHCI: Issue Transfer"); BasicConsole.DelayOutput(5); #endif UHCITransaction firstTransaction = (UHCITransaction)((USBTransaction)transfer.transactions[0]).underlyingTz; UHCITransaction lastTransaction = (UHCITransaction)((USBTransaction)transfer.transactions[transfer.transactions.Count - 1]).underlyingTz; UHCI_qTD.SetIntOnComplete(lastTransaction.qTD, true); // We want an interrupt after complete transfer CreateQH((UHCI_QueueHead_Struct*)transfer.underlyingTransferData, (uint)transfer.underlyingTransferData, firstTransaction.qTD); #if UHCI_TRACE BasicConsole.WriteLine(" Queue head data:"); BasicConsole.DumpMemory(transfer.underlyingTransferData, sizeof(UHCI_QueueHead_Struct)); BasicConsole.WriteLine(" Transactions data:"); for (int i = 0; i < transfer.transactions.Count; i++) { BasicConsole.Write(" "); BasicConsole.Write(i); BasicConsole.WriteLine(" : "); BasicConsole.WriteLine(" - qTD:"); BasicConsole.DumpMemory( ((UHCITransaction)((USBTransaction)transfer.transactions[i]).underlyingTz).qTD, sizeof(UHCI_qTD_Struct)); BasicConsole.WriteLine(" - qTDBuffer:"); BasicConsole.DumpMemory( ((UHCITransaction)((USBTransaction)transfer.transactions[i]).underlyingTz).qTDBuffer, 16); } BasicConsole.DelayOutput(60); BasicConsole.WriteLine("UHCI: Issuing transfer..."); #endif for (byte i = 0; i < UHCI_Consts.NUMBER_OF_UHCI_RETRIES && !transfer.success; i++) { TransactionsCompleted = 0; for (int j = 0; j < transfer.transactions.Count; j++) { USBTransaction elem = (USBTransaction)transfer.transactions[j]; UHCITransaction uT = (UHCITransaction)(elem.underlyingTz); uT.qTD->u1 = uT.qTD->u1 & 0xFF00FFFF; UHCI_qTD.SetActive(uT.qTD, true); } // stop scheduler USBSTS.Write_UInt16(UHCI_Consts.STS_MASK); USBCMD.Write_UInt16((ushort)(USBCMD.Read_UInt16() & ~UHCI_Consts.CMD_RS)); while ((USBSTS.Read_UInt16() & UHCI_Consts.STS_HCHALTED) == 0) { Hardware.Devices.Timer.Default.Wait(10); } // update scheduler uint qhPhysAddr = ((uint)VirtMemManager.GetPhysicalAddress(transfer.underlyingTransferData) | UHCI_Consts.BIT_QH); FrameList[0] = qhPhysAddr; FRBASEADD.Write_UInt32((uint)VirtMemManager.GetPhysicalAddress(FrameList)); FRNUM.Write_UInt16(0); // start scheduler USBSTS.Write_UInt16(UHCI_Consts.STS_MASK); USBCMD.Write_UInt16((ushort)(USBCMD.Read_UInt16() | UHCI_Consts.CMD_RS)); while ((USBSTS.Read_UInt16() & UHCI_Consts.STS_HCHALTED) != 0) { Hardware.Devices.Timer.Default.Wait(10); } #if UHCI_TRACE BasicConsole.WriteLine(((FOS_System.String)"USBINT val: ") + USBINTR.Read_UInt16()); #endif // run transactions bool active = true; int timeout = 100; //5 seconds while (active && timeout > 0) { active = false; for (int j = 0; j < transfer.transactions.Count; j++) { USBTransaction elem = (USBTransaction)transfer.transactions[j]; UHCITransaction uT = (UHCITransaction)(elem.underlyingTz); active = active || ((uT.qTD->u1 & 0x00FF0000) == 0x00800000); } Hardware.Devices.Timer.Default.Wait(50); timeout--; } #if UHCI_TRACE BasicConsole.WriteLine("Finished waiting."); #endif FrameList[0] = UHCI_Consts.BIT_T; if (timeout == 0 || TransactionsCompleted != transfer.transactions.Count) { #if UHCI_TRACE BasicConsole.SetTextColour(BasicConsole.error_colour); BasicConsole.WriteLine("UHCI: Error! Transactions wait timed out or wrong number of transactions completed."); BasicConsole.SetTextColour(BasicConsole.default_colour); BasicConsole.WriteLine(((FOS_System.String)"Transactions completed: ") + TransactionsCompleted); if (timeout == 0) { BasicConsole.SetTextColour(BasicConsole.error_colour); BasicConsole.WriteLine("UHCI: Error! Transfer timed out."); BasicConsole.SetTextColour(BasicConsole.default_colour); } #endif transfer.success = false; bool completeDespiteNoInterrupt = true; for (int j = 0; j < transfer.transactions.Count; j++) { USBTransaction elem = (USBTransaction)transfer.transactions[j]; UHCITransaction uT = (UHCITransaction)(elem.underlyingTz); #if UHCI_TRACE BasicConsole.WriteLine(((FOS_System.String)"u1=") + uT.qTD->u1 + ", u2=" + uT.qTD->u2); BasicConsole.WriteLine(((FOS_System.String)"Status=") + (byte)(uT.qTD->u1 >> 16)); #endif completeDespiteNoInterrupt = completeDespiteNoInterrupt && isTransactionSuccessful(uT); } transfer.success = completeDespiteNoInterrupt; #if UHCI_TRACE BasicConsole.SetTextColour(BasicConsole.warning_colour); BasicConsole.WriteLine(((FOS_System.String)"Complete despite no interrupts: ") + completeDespiteNoInterrupt); BasicConsole.SetTextColour(BasicConsole.default_colour); BasicConsole.DelayOutput(5); #endif } else { transfer.success = true; } if (transfer.success) { // check conditions and save data for (int j = 0; j < transfer.transactions.Count; j++) { USBTransaction elem = (USBTransaction)transfer.transactions[j]; UHCITransaction uT = (UHCITransaction)(elem.underlyingTz); transfer.success = transfer.success && isTransactionSuccessful(uT); // executed w/o error if (uT.inBuffer != null && uT.inLength != 0) { MemoryUtils.MemCpy_32((byte*)uT.inBuffer, (byte*)uT.qTDBuffer, uT.inLength); } } } #if UHCI_TRACE if (!transfer.success) { BasicConsole.SetTextColour(BasicConsole.error_colour); BasicConsole.WriteLine("UHCI: Transfer failed."); BasicConsole.SetTextColour(BasicConsole.default_colour); } else { BasicConsole.SetTextColour((char)0x0200); BasicConsole.WriteLine("Transfer succeeded."); BasicConsole.SetTextColour(BasicConsole.default_colour); } #endif } }
/// <summary> /// When overridden in a derived class, handles HC implementation specific OUT transaction initialisation. /// </summary> /// <param name="transfer">The transfer to which the transaction should be added.</param> /// <param name="uTransaction">The USB Transaction to convert to an EHCI transaction.</param> /// <param name="toggle">The transaction toggle state.</param> /// <param name="buffer">The buffer of outgoing data.</param> /// <param name="length">The length of the buffer.</param> protected abstract void _OUTTransaction(USBTransfer transfer, USBTransaction uTransaction, bool toggle, void *buffer, ushort length);
/// <summary> /// Sets up a SETUP transaction and adds it to the specified transfer. /// </summary> /// <param name="transfer">The transfer to which the transaction should be added.</param> /// <param name="uTransaction">The USB Transaction to convert to an EHCI Transaction.</param> /// <param name="toggle">The transaction toggle state.</param> /// <param name="tokenBytes">The number of bytes to send.</param> /// <param name="type">The type of the USB Request.</param> /// <param name="req">The specific USB Request.</param> /// <param name="hiVal">The USB Request Hi-Val.</param> /// <param name="loVal">The USB Request Lo-Val.</param> /// <param name="index">The USB request index.</param> /// <param name="length">The length of the USB request.</param> protected override void _SETUPTransaction(USBTransfer transfer, USBTransaction uTransaction, bool toggle, ushort tokenBytes, byte type, byte req, byte hiVal, byte loVal, ushort index, ushort length) { // Create an EHCI-specific object to describe the transaction EHCITransaction eTransaction = new EHCITransaction(); // Store the underlying HC-specific transaction info uTransaction.underlyingTz = eTransaction; // SETUP transaction so there is no input buffer eTransaction.inBuffer = null; eTransaction.inLength = 0u; // Create and initialise the SETUP queue transfer descriptor eTransaction.qTD = CreateQTD_SETUP(null, toggle, tokenBytes, type, req, hiVal, loVal, index, length); // If the number of existing transactions is greater than 0 // i.e. some transactions have already been added. if (transfer.transactions.Count > 0) { // Get the previous (i.e. last) transaction then the underlying transaction from it EHCITransaction eLastTransaction = (EHCITransaction)((USBTransaction)(transfer.transactions[transfer.transactions.Count - 1])).underlyingTz; // Create a wrapper for the last transaction (qTD) EHCI_qTD lastQTD = eLastTransaction.qTD; // Set the Next Transaction (qTD) Pointer on the previous qTD to point to the qTD // we just created. // Note: The NextqTDPointer must be the physical address of qTD data. lastQTD.NextqTDPointer = (EHCI_qTD_Struct*)VirtMemManager.GetPhysicalAddress(eTransaction.qTD.qtd); // Mark the previous qTD's Next Transaction Pointer as valid. lastQTD.NextqTDPointerTerminate = false; } }
/// <summary> /// Sets up an IN transaction and adds it to the specified transfer. /// </summary> /// <param name="transfer">The transfer to which the transaction should be added.</param> /// <param name="uTransaction">The USB Transaction to convert to an EHCI transaction.</param> /// <param name="toggle">The transaction toggle state.</param> /// <param name="buffer">The buffer of outgoing data.</param> /// <param name="length">The length of the buffer.</param> protected override void _OUTTransaction(USBTransfer transfer, USBTransaction uTransaction, bool toggle, void* buffer, ushort length) { // Create an EHCI-specific object to describe the transaction EHCITransaction eTransaction = new EHCITransaction(); // Store the underlying HC-specific transaction info uTransaction.underlyingTz = eTransaction; // OUT transaction so there is no input buffer eTransaction.inBuffer = null; eTransaction.inLength = 0u; // Create and initialise the OUT queue transfer descriptor EHCI_qTD theQTD = CreateQTD_IO(null, 0, toggle, length, length); // Set the qTD structure in the transaction description object eTransaction.qTD = theQTD; // If there is an output buffer and it has > 0 length: if (buffer != null && length != 0) { // Copy the data from the output buffer to the transaction's output buffer // The transaction's output buffer has been allocated so it as aligned correctly // where as there is no guarantee the output buffer passed to us has been so we // must copy the data across. Utilities.MemoryUtils.MemCpy_32(theQTD.Buffer0VirtAddr, (byte*)buffer, length); #if EHCI_TRACE BasicConsole.WriteLine("EHCI: OUTTransaction - Buffer0:"); BasicConsole.DumpMemory(theQTD.Buffer0VirtAddr, length); #endif } // If the number of existing transactions is greater than 0 // i.e. some transactions have already been added. if (transfer.transactions.Count > 0) { // Get the previous (i.e. last) transaction then the underlying transaction from it EHCITransaction eLastTransaction = (EHCITransaction)((USBTransaction)(transfer.transactions[transfer.transactions.Count - 1])).underlyingTz; // Create a wrapper for the last transaction (qTD) EHCI_qTD lastQTD = eLastTransaction.qTD; // Set the Next Transaction (qTD) Pointer on the previous qTD to point to the qTD // we just created. // Note: The NextqTDPointer must be the physical address of qTD data. lastQTD.NextqTDPointer = (EHCI_qTD_Struct*)VirtMemManager.GetPhysicalAddress(eTransaction.qTD.qtd); // Mark the previous qTD's Next Transaction Pointer as valid. lastQTD.NextqTDPointerTerminate = false; } }
/// <summary> /// Resets the bulk tarnsfer interface. /// </summary> /// <param name="numInterface">The bulk transfer interface number to reset.</param> public void BulkReset(byte numInterface) { #if MSD_TRACE DBGMSG(((FOS_System.String)"USB MSD: TransferBulkOnly - MassStorageReset, interface: ") + numInterface); #endif USBTransfer transfer = new USBTransfer(); DeviceInfo.hc.SetupTransfer(DeviceInfo, transfer, USBTransferType.Control, 0, 64); // RequestType Request Value Index Length Data // 00100001b 11111111b 0000h Interface 0000h None DeviceInfo.hc.SETUPTransaction(transfer, 8, 0x21, 0xFF, 0, 0, numInterface, 0); DeviceInfo.hc.INTransaction(transfer, true, null, 0); // Handshake DeviceInfo.hc.IssueTransfer(transfer); }
/// <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; }
/// <summary> /// Sets up a USB transfer for sending via the EHCI. /// </summary> /// <param name="usbDevice">The USb device to send the transfer to.</param> /// <param name="transfer">The transfer to send.</param> /// <param name="type">The type of USB transfer.</param> /// <param name="endpoint">The endpoint of the device to send the transfer to.</param> /// <param name="maxLength">The maximum packet size to use when transferring.</param> public void SetupTransfer(Devices.USBDeviceInfo usbDevice, USBTransfer transfer, USBTransferType type, byte endpoint, ushort maxLength) { transfer.device = usbDevice; transfer.endpoint = endpoint; transfer.type = type; #if HCI_TRACE BasicConsole.WriteLine(((FOS_System.String)"SetupTransfer: maxLength=") + maxLength + ", endpoint=" + endpoint + ", mps=" + ((Endpoint)usbDevice.Endpoints[endpoint]).mps); #endif transfer.packetSize = FOS_System.Math.Min(maxLength, ((Endpoint)usbDevice.Endpoints[endpoint]).MPS); #if HCI_TRACE BasicConsole.WriteLine(((FOS_System.String)"SetupTransfer: packetSize=") + transfer.packetSize); #endif transfer.success = false; transfer.transactions = new List(3); _SetupTransfer(transfer); }