HCD_STATUS HcdDataTransfer(uint32_t PipeHandle, uint8_t *const buffer, uint32_t const length, uint16_t *const pActualTransferred) { uint8_t HostID, EdIdx; uint32_t ExpectedLength; if ((buffer == NULL) || (length == 0)) { ASSERT_STATUS_OK_MESSAGE(HCD_STATUS_PARAMETER_INVALID, "Data Buffer is NULL or Transfer Length is 0"); } ASSERT_STATUS_OK(PipehandleParse(PipeHandle, &HostID, &EdIdx) ); ASSERT_STATUS_OK(HcdED(EdIdx)->hcED.HeadP.Halted ? HCD_STATUS_TRANSFER_Stall : HCD_STATUS_OK); ExpectedLength = (length != HCD_ENDPOINT_MAXPACKET_XFER_LEN) ? length : HcdED(EdIdx)->hcED.MaxPackageSize; if ( IsIsoEndpoint(EdIdx) ) { /* Iso Transfer */ ASSERT_STATUS_OK(QueueITDs(EdIdx, buffer, ExpectedLength) ); } else { ASSERT_STATUS_OK(QueueGTDs(EdIdx, buffer, ExpectedLength, 0) ); if (HcdED(EdIdx)->ListIndex == BULK_LIST_HEAD) { USB_REG(HostID)->CommandStatus |= HC_COMMAND_STATUS_BulkListFilled; } } HcdED(EdIdx)->status = HCD_STATUS_TRANSFER_QUEUED; HcdED(EdIdx)->pActualTransferCount = pActualTransferred;/* TODO refractor Actual length transfer */ return HCD_STATUS_OK; }
HCD_STATUS HcdClosePipe(uint32_t PipeHandle) { uint8_t HostID, EdIdx; ASSERT_STATUS_OK(PipehandleParse(PipeHandle, &HostID, &EdIdx) ); ASSERT_STATUS_OK(HcdCancelTransfer(PipeHandle) ); HcdED(EdIdx)->hcED.Skip = 1;/* no need for delay, it is already delayed in cancel transfer */ RemoveEndpoint(HostID, EdIdx); FreeED(EdIdx); return HCD_STATUS_OK; }
static HCD_STATUS QueueOneITD(uint32_t EdIdx, uint8_t *dataBuff, uint32_t TDLen, uint16_t StartingFrame) { uint32_t i; PHCD_IsoTransferDescriptor pItd = (PHCD_IsoTransferDescriptor) Align16(HcdED(EdIdx)->hcED.TailP); pItd->StartingFrame = StartingFrame; pItd->FrameCount = (TDLen / HcdED(EdIdx)->hcED.MaxPackageSize) + (TDLen % HcdED(EdIdx)->hcED.MaxPackageSize ? 1 : 0) - 1; pItd->BufferPage0 = Align4k( (uint32_t) dataBuff); pItd->BufferEnd = (uint32_t) (dataBuff + TDLen - 1); for (i = 0; TDLen > 0 && i < 8; i++) { uint32_t XactLen = MIN(TDLen, HcdED(EdIdx)->hcED.MaxPackageSize); pItd->OffsetPSW[i] = (HCD_STATUS_TRANSFER_NotAccessed << 12) | (Align4k((uint32_t) dataBuff) != Align4k(pItd->BufferPage0) ? _BIT(12) : 0) | Offset4k((uint32_t) dataBuff); /*-- FIXME take into cross page account later 15-12: ConditionCode, 11-0: offset --*/ TDLen -= XactLen; dataBuff += XactLen; } /* Create a new place holder TD & link setup TD to the new place holder */ ASSERT_STATUS_OK(AllocItdForEd(EdIdx) ); return HCD_STATUS_OK; }
HCD_STATUS HcdCancelTransfer(uint32_t PipeHandle) { uint8_t HostID, EdIdx; ASSERT_STATUS_OK(PipehandleParse(PipeHandle, &HostID, &EdIdx) ); HcdED(EdIdx)->hcED.Skip = 1; /* Clear SOF and wait for the next frame */ USB_REG(HostID)->InterruptStatus = HC_INTERRUPT_StartofFrame; while ( !(USB_REG(HostID)->InterruptStatus & HC_INTERRUPT_StartofFrame) )/* TODO Should have timeout */ /* ISO TD & General TD have the same offset for nextTD, we can use GTD as pointer to travel on TD list */ while ( Align16(HcdED(EdIdx)->hcED.HeadP.HeadTD) != Align16(HcdED(EdIdx)->hcED.TailP) ) { uint32_t HeadTD = Align16(HcdED(EdIdx)->hcED.HeadP.HeadTD); if ( IsIsoEndpoint(EdIdx) ) { HcdED(EdIdx)->hcED.HeadP.HeadTD = ((PHCD_IsoTransferDescriptor) HeadTD)->NextTD; FreeItd( (PHCD_IsoTransferDescriptor) HeadTD); } else { HcdED(EdIdx)->hcED.HeadP.HeadTD = ((PHCD_GeneralTransferDescriptor) HeadTD)->hcGTD.NextTD; FreeGtd((PHCD_GeneralTransferDescriptor) HeadTD); } } HcdED(EdIdx)->hcED.HeadP.HeadTD = Align16(HcdED(EdIdx)->hcED.TailP);/*-- Toggle Carry/Halted are also set to 0 --*/ HcdED(EdIdx)->hcED.HeadP.ToggleCarry = 0; HcdED(EdIdx)->hcED.Skip = 0; return HCD_STATUS_OK; }
HCD_STATUS HcdGetPipeStatus(uint32_t PipeHandle) { uint8_t HostID, EdIdx; ASSERT_STATUS_OK(PipehandleParse(PipeHandle, &HostID, &EdIdx) ); return (HCD_STATUS)HcdED(EdIdx)->status; }
HCD_STATUS HcdClearEndpointHalt(uint32_t PipeHandle) { uint8_t HostID, EdIdx; ASSERT_STATUS_OK(PipehandleParse(PipeHandle, &HostID, &EdIdx) ); /* TODO should we call HcdCancelTrnasfer ? */ HcdED(EdIdx)->hcED.HeadP.Halted = 0; HcdED(EdIdx)->hcED.HeadP.ToggleCarry = 0; HcdED(EdIdx)->status = HCD_STATUS_OK; return HCD_STATUS_OK; }
HCD_STATUS HcdControlTransfer(uint32_t PipeHandle, const USB_Request_Header_t *const pDeviceRequest, uint8_t *const buffer) { uint8_t HostID, EdIdx; if ((pDeviceRequest == NULL) || (buffer == NULL)) { ASSERT_STATUS_OK_MESSAGE(HCD_STATUS_PARAMETER_INVALID, "Device Request or Data Buffer is NULL"); } ASSERT_STATUS_OK(PipehandleParse(PipeHandle, &HostID, &EdIdx) ); /************************************************************************/ /* Setup Stage */ /************************************************************************/ ASSERT_STATUS_OK(QueueOneGTD(EdIdx, (uint8_t *) pDeviceRequest, 8, 0, 2, 0) ); /* Setup TD: DirectionPID=00 - DataToggle=10b (always DATA0) */ /************************************************************************/ /* Data Stage */ /************************************************************************/ if (pDeviceRequest->wLength) { /* Could have problem if the wLength is larger than pipe size */ ASSERT_STATUS_OK(QueueOneGTD(EdIdx, buffer, pDeviceRequest->wLength, (pDeviceRequest->bmRequestType & 0x80) ? 2 : 1, 3, 0) ); /* DataToggle=11b (always DATA1) */ } /************************************************************************/ /* Status Stage */ /************************************************************************/ ASSERT_STATUS_OK(QueueOneGTD(EdIdx, NULL, 0, (pDeviceRequest->bmRequestType & 0x80) ? 1 : 2, 3, 1) ); /* Status TD: Direction=opposite of data direction - DataToggle=11b (always DATA1) */ /* set control list filled */ USB_REG(HostID)->CommandStatus |= HC_COMMAND_STATUS_ControlListFilled; HcdED(EdIdx)->status = HCD_STATUS_TRANSFER_QUEUED; /* wait for semaphore compete TDs */ ASSERT_STATUS_OK(WaitForTransferComplete(EdIdx) ); return HCD_STATUS_OK; }
static HCD_STATUS QueueGTDs(uint32_t EdIdx, uint8_t *dataBuff, uint32_t xferLen, uint8_t Direction) { while (xferLen > 0) { uint16_t TdLen; uint32_t MaxTDLen = TD_MAX_XFER_LENGTH - Offset4k((uint32_t) dataBuff); TdLen = MIN(xferLen, MaxTDLen); xferLen -= TdLen; ASSERT_STATUS_OK(QueueOneGTD(EdIdx, dataBuff, TdLen, Direction, 0, (xferLen ? 0 : 1)) ); dataBuff += TdLen; } return HCD_STATUS_OK; }
HCD_STATUS OpenPipe_VerifyParameters(uint8_t HostID, uint8_t DeviceAddr, HCD_USB_SPEED DeviceSpeed, uint8_t EndpointNumber, HCD_TRANSFER_TYPE TransferType, HCD_TRANSFER_DIR TransferDir, uint16_t MaxPacketSize, uint8_t Interval, uint8_t Mult) { if ((HostID >= MAX_USB_CORE) || ( DeviceAddr > 127) || ( DeviceSpeed > HIGH_SPEED) || (EndpointNumber & 0x70) || ( TransferType > INTERRUPT_TRANSFER) || ( TransferDir > OUT_TRANSFER) ) { ASSERT_STATUS_OK(HCD_STATUS_PARAMETER_INVALID); } /* XXX by USB specs Low speed device should not have packet size > 8, but many market devices does */ if ((DeviceSpeed == LOW_SPEED) && ((TransferType == BULK_TRANSFER) || (TransferType == ISOCHRONOUS_TRANSFER)) ) { ASSERT_STATUS_OK(HCD_STATUS_PARAMETER_INVALID); } switch (TransferType) { case CONTROL_TRANSFER: if (MaxPacketSize > 64) { ASSERT_STATUS_OK(HCD_STATUS_PARAMETER_INVALID); } break; case BULK_TRANSFER: if (((DeviceSpeed == FULL_SPEED) && (MaxPacketSize > 64)) || ((DeviceSpeed == HIGH_SPEED) && (MaxPacketSize > 512)) ) { ASSERT_STATUS_OK(HCD_STATUS_PARAMETER_INVALID); } break; case INTERRUPT_TRANSFER: if ((Interval == 0) || ((DeviceSpeed == FULL_SPEED) && (MaxPacketSize > 64)) || ((DeviceSpeed == HIGH_SPEED) && ((MaxPacketSize > 1024) || (Interval > 16) || (Mult == 0))) ) { ASSERT_STATUS_OK(HCD_STATUS_PARAMETER_INVALID); } break; case ISOCHRONOUS_TRANSFER: if ((Interval == 0) || (Interval > 16) || ((DeviceSpeed == FULL_SPEED) && (MaxPacketSize > 1023)) || ((DeviceSpeed == HIGH_SPEED) && ((MaxPacketSize > 1024) || (Mult == 0))) ) { ASSERT_STATUS_OK(HCD_STATUS_PARAMETER_INVALID); } break; } return HCD_STATUS_OK; }
/** Direction, DataToggle parameter only has meaning for control transfer, for other transfer use 0 for these paras */ static HCD_STATUS QueueOneGTD (uint32_t EdIdx, uint8_t* const CurrentBufferPointer, uint32_t xferLen, uint8_t DirectionPID, uint8_t DataToggle, uint8_t IOC) { PHCD_GeneralTransferDescriptor TailP; TailP = ( (PHCD_GeneralTransferDescriptor) HcdED(EdIdx)->hcED.TailP ) ; TailP->hcGTD.DirectionPID = DirectionPID; TailP->hcGTD.DataToggle = DataToggle; TailP->hcGTD.CurrentBufferPointer = CurrentBufferPointer; TailP->hcGTD.BufferEnd = (xferLen) ? (CurrentBufferPointer+xferLen-1) : NULL; TailP->TransferCount = xferLen; if (!IOC) { TailP->hcGTD.DelayInterrupt = TD_NoInterruptOnComplete; /* Delay Interrupt with */ } /* Create a new place holder TD & link setup TD to the new place holder */ ASSERT_STATUS_OK ( AllocGtdForEd(EdIdx) ); return HCD_STATUS_OK; }
static HCD_STATUS QueueITDs(uint32_t EdIdx, uint8_t* dataBuff, uint32_t xferLen) { uint32_t FrameIdx; uint32_t MaxDataSize; #if 0 /* Maximum bandwidth (Interval = 1) regardless of Interval value */ uint8_t MaxXactPerITD, FramePeriod; if (HcdED(EdIdx)->Interval < 4) /*-- Period < 8 --*/ { MaxXactPerITD = 1 << ( 4 - HcdED(EdIdx)->Interval ) ; /*-- Interval 1 => 8, 2 => 4, 3 => 2 --*/ FramePeriod = 1; }else { MaxXactPerITD = 1; FramePeriod = 1 << ( HcdED(EdIdx)->Interval - 4 ); /*-- Frame step 4 => 1, 5 => 2, 6 => 3 --*/ } #else #define MaxXactPerITD 8 #define FramePeriod 1 #endif MaxDataSize = MaxXactPerITD * HcdED(EdIdx)->hcED.MaxPackageSize; FrameIdx = HcdGetFrameNumber(0)+1; /* FIXME dual controller */ while (xferLen > 0) { uint16_t TdLen; uint32_t MaxTDLen = TD_MAX_XFER_LENGTH - Offset4k((uint32_t)dataBuff); MaxTDLen = MIN(MaxDataSize, MaxTDLen); TdLen = MIN(xferLen, MaxTDLen); xferLen -= TdLen; /*---------- Fill data to Place hodler TD ----------*/ ASSERT_STATUS_OK ( QueueOneITD(EdIdx, dataBuff, TdLen, FrameIdx) ); FrameIdx = (FrameIdx + FramePeriod) % (1<<16); dataBuff += TdLen; } return HCD_STATUS_OK; }
HCD_STATUS HcdOpenPipe(uint8_t HostID, uint8_t DeviceAddr, HCD_USB_SPEED DeviceSpeed, uint8_t EndpointNumber, HCD_TRANSFER_TYPE TransferType, HCD_TRANSFER_DIR TransferDir, uint16_t MaxPacketSize, uint8_t Interval, uint8_t Mult, uint8_t HSHubDevAddr, uint8_t HSHubPortNum, uint32_t *const PipeHandle) { uint32_t EdIdx; uint8_t ListIdx; (void) Mult; (void) HSHubDevAddr; (void) HSHubPortNum; /* Disable compiler warnings */ #if !ISO_LIST_ENABLE if ( TransferType == ISOCHRONOUS_TRANSFER ) { ASSERT_STATUS_OK_MESSAGE(HCD_STATUS_TRANSFER_TYPE_NOT_SUPPORTED, "Please set ISO_LIST_ENABLE to YES"); } #endif #if !INTERRUPT_LIST_ENABLE if ( TransferType == INTERRUPT_TRANSFER ) { ASSERT_STATUS_OK_MESSAGE(HCD_STATUS_TRANSFER_TYPE_NOT_SUPPORTED, "Please set INTERRUPT_LIST_ENABLE to YES"); } #endif /********************************* Parameters Verify *********************************/ ASSERT_STATUS_OK(OpenPipe_VerifyParameters(HostID, DeviceAddr, DeviceSpeed, EndpointNumber, TransferType, TransferDir, MaxPacketSize, Interval, 0) ); EndpointNumber &= 0xF; /* Endpoint number is in range 0-15 */ MaxPacketSize &= 0x3FF; /* Max Packet Size is in range 0-1024 */ switch (TransferType) { case CONTROL_TRANSFER: ListIdx = CONTROL_LIST_HEAD; break; case BULK_TRANSFER: ListIdx = BULK_LIST_HEAD; break; case INTERRUPT_TRANSFER: // ListIdx = FindInterruptTransferListIndex(Interval); ListIdx = INTERRUPT_1ms_LIST_HEAD; break; case ISOCHRONOUS_TRANSFER: ListIdx = ISO_LIST_HEAD; break; default : // just to clear warning ListIdx = 0xFF; break; } if(ListIdx == 0xFF) return HCD_STATUS_PARAMETER_INVALID; ASSERT_STATUS_OK(AllocEd(DeviceAddr, DeviceSpeed, EndpointNumber, TransferType, TransferDir, MaxPacketSize, Interval, &EdIdx) ); /* Add new ED to the EDs List */ HcdED(EdIdx)->ListIndex = ListIdx; InsertEndpoint(HostID, EdIdx, ListIdx); PipehandleCreate(PipeHandle, HostID, EdIdx); return HCD_STATUS_OK; }