LPCOpen Platform
LPCOpen Platform for NXP LPC Microcontrollers
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
OHCI.c
Go to the documentation of this file.
1 /*
2  * @brief Open Host Controller Interface
3  *
4  * @note
5  * Copyright(C) NXP Semiconductors, 2012
6  * All rights reserved.
7  *
8  * @par
9  * Software that is described herein is for illustrative purposes only
10  * which provides customers with programming information regarding the
11  * LPC products. This software is supplied "AS IS" without any warranties of
12  * any kind, and NXP Semiconductors and its licensor disclaim any and
13  * all warranties, express or implied, including all implied warranties of
14  * merchantability, fitness for a particular purpose and non-infringement of
15  * intellectual property rights. NXP Semiconductors assumes no responsibility
16  * or liability for the use of the software, conveys no license or rights under any
17  * patent, copyright, mask work right, or any other intellectual property rights in
18  * or to any products. NXP Semiconductors reserves the right to make changes
19  * in the software without notification. NXP Semiconductors also makes no
20  * representation or warranty that such application will be suitable for the
21  * specified use without further testing or modification.
22  *
23  * @par
24  * Permission to use, copy, modify, and distribute this software and its
25  * documentation is hereby granted, under NXP Semiconductors' and its
26  * licensor's relevant copyrights in the software, without fee, provided that it
27  * is used in conjunction with NXP Semiconductors microcontrollers. This
28  * copyright, permission, and disclaimer notice must appear in all copies of
29  * this code.
30  */
31 
32 /*=======================================================================*/
33 /* I N C L U D E S */
34 /*=======================================================================*/
35 #define __INCLUDE_FROM_USB_DRIVER
36 #include "../../../USBMode.h"
37 
38 #if (defined(USB_CAN_BE_HOST) && defined(__LPC_OHCI__))
39 
40 #define __LPC_OHCI_C__
41 #include "../../../../../../Common/Common.h"
42 #include "../../../USBTask.h"
43 #include "../HCD.h"
44 #include "OHCI.h"
45 
46 OHCI_HOST_DATA_Type ohci_data[MAX_USB_CORE] __DATA(USBRAM_SECTION);
47 
48 /*=======================================================================*/
49 /* G L O B A L S Y M B O L D E C L A R A T I O N S */
50 /*=======================================================================*/
51 void USB_Host_Enumerate (uint8_t HostID);
52 
53 void USB_Host_DeEnumerate(uint8_t HostID);
54 
55 /*********************************************************************
56  * IMPLEMENTATION
57  **********************************************************************/
58 HCD_STATUS HcdGetDeviceSpeed(uint8_t HostID, uint8_t PortNum, HCD_USB_SPEED *DeviceSpeed)
59 {
60  if ( OHCI_REG(HostID)->HcRhPortStatus1 & HC_RH_PORT_STATUS_CurrentConnectStatus) { /* If device is connected */
61  *DeviceSpeed =
63  return HCD_STATUS_OK;
64  }
65  else {
67  }
68 }
69 
70 uint32_t HcdGetFrameNumber(uint8_t HostID)
71 {
72  return ohci_data[HostID].hcca.HccaFrameNumber;
73 }
74 
75 HCD_STATUS HcdRhPortReset(uint8_t HostID, uint8_t uPortNumber)
76 {
77  HcdDelayMS(400);// TODO delay should be on Host_LPC
78  OHCI_REG(HostID)->HcRhPortStatus1 = HC_RH_PORT_STATUS_PortResetStatus; /* SetPortReset */
79  /* should have time-out */
80  while ( OHCI_REG(HostID)->HcRhPortStatus1 & HC_RH_PORT_STATUS_PortResetStatus) {}
81 
82  OHCI_REG(HostID)->HcRhPortStatus1 = HC_RH_PORT_STATUS_PortResetStatusChange;/* Clear Port Reset Status Change */
83 
84  HcdDelayMS(400);// TODO delay should be on Host_LPC
85  return HCD_STATUS_OK;
86 }
87 
88 HCD_STATUS HcdRhPortEnable(uint8_t HostID, uint8_t uPortNumber)
89 {
90  OHCI_REG(HostID)->HcRhPortStatus1 = HC_RH_PORT_STATUS_PowerEnableStatus;/* SetPortEnable */
91 
92  return HCD_STATUS_OK;
93 }
94 
95 HCD_STATUS HcdRhPortDisable(uint8_t HostID, uint8_t uPortNumber)
96 {
97  OHCI_REG(HostID)->HcRhPortStatus1 = HC_RH_PORT_STATUS_CurrentConnectStatus; /* ClearPortEnable */
98 
99  return HCD_STATUS_OK;
100 }
101 
102 HCD_STATUS HcdInitDriver(uint8_t HostID)
103 {
104  OHCI_REG(HostID)->OTGClkCtrl = 0x00000019; /* enable Host clock, OTG clock and AHB clock */
105  while ((OHCI_REG(HostID)->OTGClkSt & 0x00000019) != 0x00000019) ;
106 #if defined(__LPC17XX__)
107  OHCI_REG(HostID)->OTGStCtrl = 0x3; /* ??? */
108 #elif defined(__LPC177X_8X__)
109  OHCI_REG(HostID)->OTGStCtrl = 0x1; /* Port 1 is host */
110 #endif
111  OHciHostReset(HostID); /* Software Reset */
112  return OHciHostInit(HostID);
113 }
114 
115 HCD_STATUS HcdDeInitDriver(uint8_t HostID)
116 {
117  OHCI_REG(HostID)->OTGStCtrl = 0;
118  return HCD_STATUS_OK;
119 }
120 
121 HCD_STATUS HcdOpenPipe(uint8_t HostID,
122  uint8_t DeviceAddr,
123  HCD_USB_SPEED DeviceSpeed,
124  uint8_t EndpointNumber,
125  HCD_TRANSFER_TYPE TransferType,
126  HCD_TRANSFER_DIR TransferDir,
127  uint16_t MaxPacketSize,
128  uint8_t Interval,
129  uint8_t Mult,
130  uint8_t HSHubDevAddr,
131  uint8_t HSHubPortNum,
132  uint32_t *const PipeHandle)
133 {
134  uint32_t EdIdx;
135  uint8_t ListIdx;
136 
137  (void) Mult; (void) HSHubDevAddr; (void) HSHubPortNum; /* Disable compiler warnings */
138 
139 #if !ISO_LIST_ENABLE
140  if ( TransferType == ISOCHRONOUS_TRANSFER ) {
141  ASSERT_STATUS_OK_MESSAGE(HCD_STATUS_TRANSFER_TYPE_NOT_SUPPORTED, "Please set ISO_LIST_ENABLE to YES");
142  }
143 #endif
144 
145 #if !INTERRUPT_LIST_ENABLE
146  if ( TransferType == INTERRUPT_TRANSFER ) {
147  ASSERT_STATUS_OK_MESSAGE(HCD_STATUS_TRANSFER_TYPE_NOT_SUPPORTED, "Please set INTERRUPT_LIST_ENABLE to YES");
148  }
149 #endif
150 
151  /********************************* Parameters Verify *********************************/
152  ASSERT_STATUS_OK(OpenPipe_VerifyParameters(HostID, DeviceAddr, DeviceSpeed, EndpointNumber, TransferType,
153  TransferDir, MaxPacketSize, Interval, 0) );
154 
155  EndpointNumber &= 0xF; /* Endpoint number is in range 0-15 */
156  MaxPacketSize &= 0x3FF; /* Max Packet Size is in range 0-1024 */
157 
158  switch (TransferType) {
159  case CONTROL_TRANSFER:
160  ListIdx = CONTROL_LIST_HEAD;
161  break;
162 
163  case BULK_TRANSFER:
164  ListIdx = BULK_LIST_HEAD;
165  break;
166 
167  case INTERRUPT_TRANSFER:
168  // ListIdx = FindInterruptTransferListIndex(Interval);
169  ListIdx = INTERRUPT_1ms_LIST_HEAD;
170  break;
171 
173  ListIdx = ISO_LIST_HEAD;
174  break;
175  }
176 
177  ASSERT_STATUS_OK(AllocEd(DeviceAddr, DeviceSpeed, EndpointNumber, TransferType, TransferDir, MaxPacketSize,
178  Interval, &EdIdx) );
179 
180  /* Add new ED to the EDs List */
181  HcdED(EdIdx)->ListIndex = ListIdx;
182  InsertEndpoint(HostID, EdIdx, ListIdx);
183 
184  PipehandleCreate(PipeHandle, HostID, EdIdx);
185  return HCD_STATUS_OK;
186 }
187 
189 {
190  uint8_t HostID, EdIdx;
191 
192  ASSERT_STATUS_OK(PipehandleParse(PipeHandle, &HostID, &EdIdx) );
193 
194  HcdED(EdIdx)->hcED.Skip = 1;
195 
196  /* Clear SOF and wait for the next frame */
197  OHCI_REG(HostID)->HcInterruptStatus = HC_INTERRUPT_StartofFrame;
198  while ( !(OHCI_REG(HostID)->HcInterruptStatus & HC_INTERRUPT_StartofFrame) )/* TODO Should have timeout */
199 
200  /* ISO TD & General TD have the same offset for nextTD, we can use GTD as pointer to travel on TD list */
201  while ( Align16(HcdED(EdIdx)->hcED.HeadP.HeadTD) != Align16(HcdED(EdIdx)->hcED.TailP) ) {
202  uint32_t HeadTD = Align16(HcdED(EdIdx)->hcED.HeadP.HeadTD);
203  if ( IsIsoEndpoint(EdIdx) ) {
204  HcdED(EdIdx)->hcED.HeadP.HeadTD = ((PHCD_IsoTransferDescriptor) HeadTD)->NextTD;
206  }
207  else {
208  HcdED(EdIdx)->hcED.HeadP.HeadTD = ((PHCD_GeneralTransferDescriptor) HeadTD)->hcGTD.NextTD;
209  FreeGtd((PHCD_GeneralTransferDescriptor) HeadTD);
210  }
211  }
212  HcdED(EdIdx)->hcED.HeadP.HeadTD = Align16(HcdED(EdIdx)->hcED.TailP);/*-- Toggle Carry/Halted are also set to 0 --*/
213  HcdED(EdIdx)->hcED.HeadP.ToggleCarry = 0;
214 
215  HcdED(EdIdx)->hcED.Skip = 0;
216  return HCD_STATUS_OK;
217 }
218 
219 HCD_STATUS HcdClosePipe(uint32_t PipeHandle)
220 {
221  uint8_t HostID, EdIdx;
222 
223  ASSERT_STATUS_OK(PipehandleParse(PipeHandle, &HostID, &EdIdx) );
224 
225  ASSERT_STATUS_OK(HcdCancelTransfer(PipeHandle) );
226 
227  HcdED(EdIdx)->hcED.Skip = 1;/* no need for delay, it is already delayed in cancel transfer */
228  RemoveEndpoint(HostID, EdIdx);
229 
230  FreeED(EdIdx);
231 
232  return HCD_STATUS_OK;
233 }
234 
236 {
237  uint8_t HostID, EdIdx;
238  ASSERT_STATUS_OK(PipehandleParse(PipeHandle, &HostID, &EdIdx) );
239  /* TODO should we call HcdCancelTrnasfer ? */
240  HcdED(EdIdx)->hcED.HeadP.Halted = 0;
241  HcdED(EdIdx)->hcED.HeadP.ToggleCarry = 0;
242 
243  HcdED(EdIdx)->status = HCD_STATUS_OK;
244 
245  return HCD_STATUS_OK;
246 }
247 
249  const USB_Request_Header_t *const pDeviceRequest,
250  uint8_t *const buffer)
251 {
252  uint8_t HostID, EdIdx;
253 
254  if ((pDeviceRequest == NULL) || (buffer == NULL)) {
255  ASSERT_STATUS_OK_MESSAGE(HCD_STATUS_PARAMETER_INVALID, "Device Request or Data Buffer is NULL");
256  }
257 
258  ASSERT_STATUS_OK(PipehandleParse(PipeHandle, &HostID, &EdIdx) );
259 
260  /************************************************************************/
261  /* Setup Stage */
262  /************************************************************************/
263  ASSERT_STATUS_OK(QueueOneGTD(EdIdx, (uint8_t *const) pDeviceRequest, 8, 0, 2, 0) ); /* Setup TD: DirectionPID=00 - DataToggle=10b (always DATA0) */
264 
265  /************************************************************************/
266  /* Data Stage */
267  /************************************************************************/
268  if (pDeviceRequest->wLength) { /* Could have problem if the wLength is larger than pipe size */
269  ASSERT_STATUS_OK(QueueOneGTD(EdIdx, buffer, pDeviceRequest->wLength,
270  (pDeviceRequest->bmRequestType & 0x80) ? 2 : 1, 3, 0) ); /* DataToggle=11b (always DATA1) */
271  }
272  /************************************************************************/
273  /* Status Stage */
274  /************************************************************************/
275  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) */
276 
277  /* set control list filled */
278  OHCI_REG(HostID)->HcCommandStatus |= HC_COMMAND_STATUS_ControlListFilled;
279 
280  HcdED(EdIdx)->status = HCD_STATUS_TRANSFER_QUEUED;
281 
282  /* wait for semaphore compete TDs */
284 
285  return HCD_STATUS_OK;
286 }
287 
288 static HCD_STATUS QueueOneITD(uint32_t EdIdx, uint8_t *dataBuff, uint32_t TDLen, uint16_t StartingFrame)
289 {
290  uint32_t i;
292 
293  pItd->StartingFrame = StartingFrame;
294  pItd->FrameCount =
295  (TDLen / HcdED(EdIdx)->hcED.MaxPackageSize) + (TDLen % HcdED(EdIdx)->hcED.MaxPackageSize ? 1 : 0) - 1;
296  pItd->BufferPage0 = Align4k( (uint32_t) dataBuff);
297  pItd->BufferEnd = (uint32_t) (dataBuff + TDLen - 1);
298 
299  for (i = 0; TDLen > 0 && i < 8; i++) {
300  uint32_t XactLen = MIN(TDLen, HcdED(EdIdx)->hcED.MaxPackageSize);
301 
302  pItd->OffsetPSW[i] =
304  12) | (Align4k((uint32_t) dataBuff) != Align4k(pItd->BufferPage0) ? _BIT(12) : 0) |
305  Offset4k((uint32_t) dataBuff); /*-- FIXME take into cross page account later 15-12: ConditionCode, 11-0: offset --*/
306 
307  TDLen -= XactLen;
308  dataBuff += XactLen;
309  }
310 
311  /* Create a new place holder TD & link setup TD to the new place holder */
313 
314  return HCD_STATUS_OK;
315 }
316 
317 static HCD_STATUS QueueITDs(uint32_t EdIdx, uint8_t *dataBuff, uint32_t xferLen)
318 {
319  uint32_t FrameIdx;
320  uint32_t MaxDataSize;
321 
322 #if 0 /* Maximum bandwidth (Interval = 1) regardless of Interval value */
323  uint8_t MaxXactPerITD, FramePeriod;
324  if (HcdED(EdIdx)->Interval < 4) { /*-- Period < 8 --*/
325  MaxXactPerITD = 1 << ( 4 - HcdED(EdIdx)->Interval ); /*-- Interval 1 => 8, 2 => 4, 3 => 2 --*/
326  FramePeriod = 1;
327  }
328  else {
329  MaxXactPerITD = 1;
330  FramePeriod = 1 << ( HcdED(EdIdx)->Interval - 4 ); /*-- Frame step 4 => 1, 5 => 2, 6 => 3 --*/
331  }
332 #else
333  #define MaxXactPerITD 8
334  #define FramePeriod 1
335 #endif
336 
337  MaxDataSize = MaxXactPerITD * HcdED(EdIdx)->hcED.MaxPackageSize;
338  FrameIdx = HcdGetFrameNumber(0) + 1; /* FIXME dual controller */
339 
340  while (xferLen > 0) {
341  uint16_t TdLen;
342  uint32_t MaxTDLen = TD_MAX_XFER_LENGTH - Offset4k((uint32_t) dataBuff);
343  MaxTDLen = MIN(MaxDataSize, MaxTDLen);
344 
345  TdLen = MIN(xferLen, MaxTDLen);
346  xferLen -= TdLen;
347 
348  /*---------- Fill data to Place hodler TD ----------*/
349  ASSERT_STATUS_OK(QueueOneITD(EdIdx, dataBuff, TdLen, FrameIdx) );
350 
351  FrameIdx = (FrameIdx + FramePeriod) % (1 << 16);
352  dataBuff += TdLen;
353  }
354  return HCD_STATUS_OK;
355 }
356 
358  uint8_t *const buffer,
359  uint32_t const length,
360  uint16_t *const pActualTransferred)
361 {
362  uint8_t HostID, EdIdx;
363  uint32_t ExpectedLength;
364 
365  if ((buffer == NULL) || (length == 0)) {
366  ASSERT_STATUS_OK_MESSAGE(HCD_STATUS_PARAMETER_INVALID, "Data Buffer is NULL or Transfer Length is 0");
367  }
368 
369  ASSERT_STATUS_OK(PipehandleParse(PipeHandle, &HostID, &EdIdx) );
370  ASSERT_STATUS_OK(HcdED(EdIdx)->hcED.HeadP.Halted ? HCD_STATUS_TRANSFER_Stall : HCD_STATUS_OK);
371 
372  ExpectedLength = (length != HCD_ENDPOINT_MAXPACKET_XFER_LEN) ? length : HcdED(EdIdx)->hcED.MaxPackageSize; /* LUFA adaption, receive only 1 data transaction */
373 
374  if ( IsIsoEndpoint(EdIdx) ) { /* Iso Transfer */
375  ASSERT_STATUS_OK(QueueITDs(EdIdx, buffer, ExpectedLength) );
376  }
377  else {
378  ASSERT_STATUS_OK(QueueGTDs(EdIdx, buffer, ExpectedLength, 0) );
379  if (HcdED(EdIdx)->ListIndex == BULK_LIST_HEAD) {
380  OHCI_REG(HostID)->HcCommandStatus |= HC_COMMAND_STATUS_BulkListFilled;
381  }
382  }
383 
384  HcdED(EdIdx)->status = HCD_STATUS_TRANSFER_QUEUED;
385  HcdED(EdIdx)->pActualTransferCount = pActualTransferred;/* TODO refractor Actual length transfer */
386 
387  return HCD_STATUS_OK;
388 }
389 
391 {
392  uint8_t HostID, EdIdx;
393 
394  ASSERT_STATUS_OK(PipehandleParse(PipeHandle, &HostID, &EdIdx) );
395 
396  return HcdED(EdIdx)->status;
397 }
398 
399 static void OHciRhStatusChangeIsr(uint8_t HostID, uint32_t deviceConnect)
400 {
401  if (deviceConnect) {/* Device Attached */
402  USB_Host_Enumerate(HostID);
403  }
404  else { /* Device detached */
405  USB_Host_DeEnumerate(HostID);
406  }
407 }
408 
409 static void ProcessDoneQueue(uint8_t HostID, uint32_t donehead)
410 {
411  PHC_GTD pCurTD = (PHC_GTD) donehead;
412  PHC_GTD pTDList = NULL;
413 
414  /* do nothing if done queue is empty */
415  if (!donehead) {
416  return;
417  }
418 
419  /* reverse done queue order */
420  do {
421  uint32_t nextTD = pCurTD->NextTD;
422  pCurTD->NextTD = (uint32_t) pTDList;
423  pTDList = pCurTD;
424  pCurTD = (PHC_GTD) nextTD;
425  } while (pCurTD);
426 
427  while (pTDList != NULL) {
428  uint32_t EdIdx;
429 
430  pCurTD = pTDList;
431  pTDList = (PHC_GTD) pTDList->NextTD;
432 
433  /* TODO Cannot determine EdIdx because GTD and ITD have different offsets for EdIdx */
434  if ( ((uint32_t) pCurTD) <= ((uint32_t) HcdITD(MAX_ITD - 1)) ) { /* ISO TD address range */
436  EdIdx = pItd->EdIdx;
437  }
438  else { /* GTD */
439  PHCD_GeneralTransferDescriptor pGtd = (PHCD_GeneralTransferDescriptor) pCurTD;
440  EdIdx = pGtd->EdIdx;
441 
442  if (pGtd->hcGTD.CurrentBufferPointer) {
443  pGtd->TransferCount -=
444  ( Align4k( ((uint32_t) pGtd->hcGTD.BufferEnd) ^
445  ((uint32_t) pGtd->hcGTD.CurrentBufferPointer) ) ? 0x00001000 : 0 ) +
446  Offset4k((uint32_t) pGtd->hcGTD.BufferEnd) - Offset4k(
447  (uint32_t) pGtd->hcGTD.CurrentBufferPointer) + 1;
448  }
449  if (HcdED(EdIdx)->pActualTransferCount) {
450  *(HcdED(EdIdx)->pActualTransferCount) = pGtd->TransferCount;/* increase usb request transfer count */
451 
452  }
453  }
454 
455  if (pCurTD->DelayInterrupt != TD_NoInterruptOnComplete) { /* Update ED status if Interrupt on Complete is set */
456  HcdED(EdIdx)->status = pCurTD->ConditionCode;
457  }
458 
459  if ( pCurTD->ConditionCode ) { /* also update ED status if TD complete with error */
460  HcdED(EdIdx)->status =
461  (HcdED(EdIdx)->hcED.HeadP.Halted == 1) ? HCD_STATUS_TRANSFER_Stall : pCurTD->ConditionCode;
462  HcdED(EdIdx)->hcED.HeadP.Halted = 0;
463  hcd_printf("Error on Endpoint 0x%X has HCD_STATUS code %d\r\n",
464  HcdED(EdIdx)->hcED.FunctionAddr | (HcdED(EdIdx)->hcED.Direction == 2 ? 0x80 : 0x00),
465  pCurTD->ConditionCode);
466  }
467 
468  /* remove completed TD from usb request list, if request list is now empty complete usb request */
469  if (IsIsoEndpoint(EdIdx)) {
471  }
472  else {
473  FreeGtd( (PHCD_GeneralTransferDescriptor) pCurTD);
474  }
475 
476  /* Post Semaphore to signal TDs are transfer */
477  }
478 }
479 
480 #if SCHEDULING_OVRERRUN_INTERRUPT
481 static void OHciSchedulingOverrunIsr(uint8_t HostID)
482 {}
483 
484 #endif
485 
486 #if SOF_INTERRUPT
487 static void OHciStartofFrameIsr(uint8_t HostID)
488 {}
489 
490 #endif
491 
492 #if RESUME_DETECT_INTERRUPT
493 static void OHciResumeDetectedIsr(uint8_t HostID)
494 {}
495 
496 #endif
497 
498 #if UNRECOVERABLE_ERROR_INTERRUPT
499 static void OHciUnrecoverableErrorIsr(uint8_t HostID)
500 {}
501 
502 #endif
503 
504 #if FRAMENUMBER_OVERFLOW_INTERRUPT
505 static void OHciFramenumberOverflowIsr(uint8_t HostID)
506 {}
507 
508 #endif
509 
510 #if OWNERSHIP_CHANGE_INTERRUPT
511 static void OHciOwnershipChangeIsr(uint8_t HostID)
512 {}
513 
514 #endif
515 
516 void HcdIrqHandler(uint8_t HostID)
517 {
519 
520  IntStatus = OHCI_REG(HostID)->HcInterruptStatus & OHCI_REG(HostID)->HcInterruptEnable;
521 
522  if (IntStatus == 0) {
523  return;
524  }
525 
526  /* disable all interrupt for processing */
527  OHCI_REG(HostID)->HcInterruptDisable = HC_INTERRUPT_MasterInterruptEnable;
528 
529  /* Process RootHub Status Change */
530  if (IntStatus & HC_INTERRUPT_RootHubStatusChange) {
531  /* only 1 port/host --> skip to get the number of ports */
532  if (OHCI_REG(HostID)->HcRhPortStatus1 & HC_RH_PORT_STATUS_ConnectStatusChange) {
533  if (OHCI_REG(HostID)->HcRhStatus & HC_RH_STATUS_DeviceRemoteWakeupEnable) { /* means a remote wakeup event */
534 
535  }
536  else {}
537 
538  OHCI_REG(HostID)->HcRhPortStatus1 = HC_RH_PORT_STATUS_ConnectStatusChange; /* clear CSC bit */
539  OHciRhStatusChangeIsr(HostID, OHCI_REG(HostID)->HcRhPortStatus1 & HC_RH_PORT_STATUS_CurrentConnectStatus);
540  }
541 
542  if (OHCI_REG(HostID)->HcRhPortStatus1 & HC_RH_PORT_STATUS_PortEnableStatusChange) {
543  OHCI_REG(HostID)->HcRhPortStatus1 = HC_RH_PORT_STATUS_PortEnableStatusChange; /* clear PESC */
544  }
545 
546  if (OHCI_REG(HostID)->HcRhPortStatus1 & HC_RH_PORT_STATUS_PortSuspendStatusChange) {
547  OHCI_REG(HostID)->HcRhPortStatus1 = HC_RH_PORT_STATUS_PortSuspendStatusChange; /* clear PSSC */
548  }
549 
550  if (OHCI_REG(HostID)->HcRhPortStatus1 & HC_RH_PORT_STATUS_OverCurrentIndicatorChange) { /* Over-current handler to avoid physical damage */
551  OHCI_REG(HostID)->HcRhPortStatus1 = HC_RH_PORT_STATUS_OverCurrentIndicatorChange; /* clear OCIC */
552  }
553 
554  if (OHCI_REG(HostID)->HcRhPortStatus1 & HC_RH_PORT_STATUS_PortResetStatusChange) {
555  OHCI_REG(HostID)->HcRhPortStatus1 = HC_RH_PORT_STATUS_PortResetStatusChange; /* clear PRSC */
556  }
557  }
558 
559  if (IntStatus & HC_INTERRUPT_WritebackDoneHead) {
560  ProcessDoneQueue(HostID, Align16(ohci_data[HostID].hcca.HccaDoneHead) );
561  }
562 
563 #if SCHEDULING_OVRERRUN_INTERRUPT
564  if (OHCI_REG(HostID)->HcInterruptStatus & HC_INTERRUPT_SchedulingOverrun) {
565  OHciSchedulingOverrunIsr(HostID);
566  }
567 #endif
568 
569 #if SOF_INTERRUPT
570  if (OHCI_REG(HostID)->HcInterruptStatus & HC_INTERRUPT_StartofFrame) {
571  OHciStartofFrameIsr(HostID);
572  }
573 #endif
574 
575 #if RESUME_DETECT_INTERRUPT
576  if (OHCI_REG(HostID)->HcInterruptStatus & HC_INTERRUPT_ResumeDetected) {
577  OHciResumeDetectedIsr(HostID);
578  }
579 #endif
580 
581 #if UNRECOVERABLE_ERROR_INTERRUPT
582  if (OHCI_REG(HostID)->HcInterruptStatus & HC_INTERRUPT_UnrecoverableError) {
583  OHciUnrecoverableErrorIsr(HostID);
584  }
585 #endif
586 
587 #if FRAMENUMBER_OVERFLOW_INTERRUPT
588  if (OHCI_REG(HostID)->HcInterruptStatus & HC_INTERRUPT_FrameNumberOverflow) {
589  OHciFramenumberOverflowIsr(HostID);
590  }
591 #endif
592 
593 #if OWNERSHIP_CHANGE_INTERRUPT
594  if (OHCI_REG(HostID)->HcInterruptStatus & HC_INTERRUPT_OwnershipChange) {
595  OHciOwnershipChangeIsr(HostID);
596  }
597 #endif
598 
599  OHCI_REG(HostID)->HcInterruptStatus = IntStatus;/* Clear HcInterruptStatus */
600  OHCI_REG(HostID)->HcInterruptEnable = HC_INTERRUPT_MasterInterruptEnable;
601 }
602 
603 static HCD_STATUS QueueOneGTD(uint32_t EdIdx,
604  uint8_t *const CurrentBufferPointer,
605  uint32_t xferLen,
606  uint8_t DirectionPID,
607  uint8_t DataToggle,
608  uint8_t IOC)
609 {
610  PHCD_GeneralTransferDescriptor TailP;
611 
612  TailP = ( (PHCD_GeneralTransferDescriptor) HcdED(EdIdx)->hcED.TailP );
613  TailP->hcGTD.DirectionPID = DirectionPID;
614  TailP->hcGTD.DataToggle = DataToggle;
615  TailP->hcGTD.CurrentBufferPointer = CurrentBufferPointer;
616  TailP->hcGTD.BufferEnd = (xferLen) ? (CurrentBufferPointer + xferLen - 1) : NULL;
617  TailP->TransferCount = xferLen;
618  if (!IOC) {
619  TailP->hcGTD.DelayInterrupt = TD_NoInterruptOnComplete; /* Delay Interrupt with */
620  }
621 
622  /* Create a new place holder TD & link setup TD to the new place holder */
624 
625  return HCD_STATUS_OK;
626 }
627 
628 static HCD_STATUS QueueGTDs(uint32_t EdIdx, uint8_t *dataBuff, uint32_t xferLen, uint8_t Direction)
629 {
630  while (xferLen > 0) {
631  uint16_t TdLen;
632  uint32_t MaxTDLen = TD_MAX_XFER_LENGTH - Offset4k((uint32_t) dataBuff);
633 
634  TdLen = MIN(xferLen, MaxTDLen);
635  xferLen -= TdLen;
636 
637  ASSERT_STATUS_OK(QueueOneGTD(EdIdx, dataBuff, TdLen, Direction, 0, (xferLen ? 0 : 1)) );
638  dataBuff += TdLen;
639  }
640  return HCD_STATUS_OK;
641 }
642 
643 static HCD_STATUS WaitForTransferComplete(uint8_t EdIdx)
644 {
645 #ifndef __TEST__
646  while ( HcdED(EdIdx)->status == HCD_STATUS_TRANSFER_QUEUED ) {}
647  return (HCD_STATUS) HcdED(EdIdx)->status;
648 #else
649  return HCD_STATUS_OK;
650 #endif
651 }
652 
653 static __INLINE HCD_STATUS InsertEndpoint(uint8_t HostID, uint32_t EdIdx, uint8_t ListIndex)
654 {
655  PHC_ED list_head;
656  list_head = &(ohci_data[HostID].staticEDs[ListIndex]);
657 
658  HcdED(EdIdx)->hcED.NextED = list_head->NextED;
659  list_head->NextED = (uint32_t) HcdED(EdIdx);
660 
661  // if ( IsInterruptEndpoint(EdIdx) )
662  // {
663  // OHCI_HOST_DATA->staticEDs[ListIndex].TailP += HcdED(EdIdx)->hcED.MaxPackageSize; /* increase the bandwidth for the found list */
664  // }
665 
666  return HCD_STATUS_OK;
667 }
668 
669 static __INLINE HCD_STATUS RemoveEndpoint(uint8_t HostID, uint32_t EdIdx)
670 {
671  PHCD_EndpointDescriptor prevED;
672 
673  prevED = (PHCD_EndpointDescriptor) & (ohci_data[HostID].staticEDs[HcdED(EdIdx)->ListIndex]);
674  while (prevED->hcED.NextED != (uint32_t) HcdED(EdIdx) ) {
675  prevED = (PHCD_EndpointDescriptor) (prevED->hcED.NextED);
676  }
677 
678  // if ( IsInterruptEndpoint(EdIdx) )
679  // {
680  // OHCI_HOST_DATA->staticEDs[HcdED(EdIdx)->ListIndex].TailP -= HcdED(EdIdx)->hcED.MaxPackageSize; /* decrease the bandwidth for the removed list */
681  // }
682  prevED->hcED.NextED = HcdED(EdIdx)->hcED.NextED;
683 
684  return HCD_STATUS_OK;
685 }
686 
687 #if 0 /* We dont need to manage bandwidth this hard */
688 
689 __INLINE uint8_t FindInterruptTransferListIndex(uint8_t HostID, uint8_t Interval)
690 {
691  uint8_t ListLeastBandwidth;
692  uint8_t ListEnd;
693  uint8_t ListIdx = INTERRUPT_32ms_LIST_HEAD;
694 
695  /* Find the correct interval list with right power of 2, i.e: 1,2,4,8,16,32 ms */
696  while ( (ListIdx >= Interval) && (ListIdx >>= 1) ) {}
697  ListEnd = ListIdx << 1;
698 
699  /* Find the least bandwidth in the same interval */
700  /* Note: For Interrupt Static ED (0 to 62), TailP is used to store the accumulated bandwidth of the list */
701  for (ListLeastBandwidth = ListIdx; ListIdx <= ListEnd; ListIdx++ )
702  if ( ohci_data[HostID].staticEDs[ListIdx].TailP < ohci_data[HostID].staticEDs[ListLeastBandwidth].TailP ) {
703  ListLeastBandwidth = ListIdx;
704  }
705  return ListLeastBandwidth;
706 }
707 
708 static __INLINE void BuildPeriodicStaticEdTree(uint8_t HostID)
709 {
710 #if INTERRUPT_LIST_ENABLE
711  /* Build full binary tree for interrupt list */
712  uint32_t idx, count;
713  uint32_t Balance[16] = {0x0, 0x8, 0x4, 0xC, 0x2, 0xA, 0x6, 0xE, 0x1, 0x9, 0x5, 0xD, 0x3, 0xB, 0x7, 0xF};
714 
715  /* build static tree for 1 -> 16 ms */
716  OHCI_HOST_DATA->staticEDs[0].NextED = 0;
717  for (idx = 1; idx < INTERRUPT_32ms_LIST_HEAD; idx++)
718  OHCI_HOST_DATA->staticEDs[idx].NextED = (uint32_t) &(OHCI_HOST_DATA->staticEDs[(idx - 1) / 2]);
719  /* create 32ms EDs which will be assigned to HccaInterruptTable */
720  for (count = 0, idx = INTERRUPT_32ms_LIST_HEAD; count < 32; count++, idx++)
721  OHCI_HOST_DATA->staticEDs[idx].NextED =
722  (uint32_t) &(OHCI_HOST_DATA->staticEDs[Balance[count & 0xF] + INTERRUPT_16ms_LIST_HEAD]);
723  /* Hook to HCCA interrupt Table */
724  for (idx = 0; idx < 32; idx++)
725  OHCI_HOST_DATA->hcca.HccaIntTable[idx] = (uint32_t) &(OHCI_HOST_DATA->staticEDs[idx + INTERRUPT_32ms_LIST_HEAD]);
726  OHCI_HOST_DATA->staticEDs[INTERRUPT_1ms_LIST_HEAD].NextED = (uint32_t) &(OHCI_HOST_DATA->staticEDs[ISO_LIST_HEAD]);
727 #elif ISO_LIST_ENABLE
728  for (idx = 0; idx < 32; idx++)
729  OHCI_HOST_DATA->hcca.HccaIntTable[idx] = (uint32_t) &(OHCI_HOST_DATA->staticEDs[ISO_LIST_HEAD]);
730 
731 #endif
732 }
733 
734 #else
735 
736 static __INLINE void BuildPeriodicStaticEdTree(uint8_t HostID)
737 {
738  /* Treat all interrupt interval as 1ms (maximum rate) */
739  uint32_t idx;
740  for (idx = 0; idx < 32; idx++)
741  ohci_data[HostID].hcca.HccaIntTable[idx] = (uint32_t) &(ohci_data[HostID].staticEDs[INTERRUPT_1ms_LIST_HEAD]);
742  /* ISO_LIST_HEAD is an alias for INTERRUPT_1ms_LIST_HEAD */
743 }
744 
745 #endif
746 
747 static __INLINE uint32_t Align16(uint32_t Value)
748 {
749  return Value & 0xFFFFFFF0UL; /* Bit 31 .. 4 */
750 }
751 
752 static __INLINE PHCD_EndpointDescriptor HcdED(uint8_t idx)
753 {
754  return &(ohci_data[0 /*HostID*/].EDs[idx]);
755 }
756 
757 static __INLINE PHCD_GeneralTransferDescriptor HcdGTD(uint8_t idx)
758 {
759  return &(ohci_data[0 /*HostID*/].gTDs[idx]);
760 }
761 
762 static __INLINE PHCD_IsoTransferDescriptor HcdITD(uint8_t idx)
763 {
764 #if ISO_LIST_ENABLE
765  return &(ohci_data[0 /*HostID*/].iTDs[idx]);
766 #else
767  return 0;
768 #endif
769 }
770 
771 static __INLINE Bool IsIsoEndpoint(uint8_t EdIdx)
772 {
773  return HcdED(EdIdx)->hcED.Format;
774 }
775 
776 static __INLINE Bool IsInterruptEndpoint(uint8_t EdIdx)
777 {
778  return (HcdED(EdIdx)->ListIndex < CONTROL_LIST_HEAD) && !IsIsoEndpoint(EdIdx);
779 }
780 
781 static void PipehandleCreate(uint32_t *pPipeHandle, uint8_t HostID, uint8_t EdIdx)
782 {
783  *pPipeHandle = ((uint32_t) (HostID << 8)) + EdIdx;
784 }
785 
786 static HCD_STATUS PipehandleParse(uint32_t Pipehandle, uint8_t *HostID, uint8_t *EdIdx)
787 {
788  *HostID = Pipehandle >> 8;
789  *EdIdx = Pipehandle & 0xFF;
790  if ((*HostID >= MAX_USB_CORE) || (*EdIdx >= MAX_ED) || (HcdED(*EdIdx)->inUse == 0)) {
792  }
793  else {
794  return HCD_STATUS_OK;
795  }
796 }
797 
798 static __INLINE HCD_STATUS AllocEd(uint8_t DeviceAddr,
799  HCD_USB_SPEED DeviceSpeed,
800  uint8_t EndpointNumber,
801  HCD_TRANSFER_TYPE TransferType,
802  HCD_TRANSFER_DIR TransferDir,
803  uint16_t MaxPacketSize,
804  uint8_t Interval,
805  uint32_t *pEdIdx)
806 {
807  /* Looking for free EDs */
808  for ((*pEdIdx) = 0; ((*pEdIdx) < MAX_ED) && HcdED((*pEdIdx))->inUse; (*pEdIdx)++) {}
809  if ((*pEdIdx) >= MAX_ED) {
811  }
812 
813  /* Init Data for new ED */
814  memset(HcdED(*pEdIdx), 0, sizeof(HCD_EndpointDescriptor) );
815 
816  HcdED((*pEdIdx))->inUse = 1;
817 
818  HcdED((*pEdIdx))->hcED.FunctionAddr = DeviceAddr;
819  HcdED((*pEdIdx))->hcED.EndpointNumber = EndpointNumber; /* Endpoint number only has 4 bits */
820  HcdED((*pEdIdx))->hcED.Direction = (TransferType == CONTROL_TRANSFER) ? 0 : ((TransferDir == OUT_TRANSFER) ? 1 : 2 );
821  HcdED((*pEdIdx))->hcED.Speed = (DeviceSpeed == FULL_SPEED) ? 0 : 1;
822  HcdED((*pEdIdx))->hcED.Skip = 0;
823  HcdED((*pEdIdx))->hcED.Format = (TransferType == ISOCHRONOUS_TRANSFER) ? 1 : 0;
824  HcdED((*pEdIdx))->hcED.MaxPackageSize = MaxPacketSize;
825  HcdED((*pEdIdx))->Interval = Interval;
826 
827  /* Allocate Place Holder TD as suggested by OHCI 5.2.8 */
828  if (TransferType != ISOCHRONOUS_TRANSFER) {
829  ASSERT_STATUS_OK(AllocGtdForEd(*pEdIdx) );
830  }
831  else {
832  ASSERT_STATUS_OK(AllocItdForEd(*pEdIdx) );
833  }
834 
835  return HCD_STATUS_OK;
836 }
837 
838 static HCD_STATUS AllocGtdForEd(uint8_t EdIdx)
839 {
840  uint32_t GtdIdx;
841 
842  /* Allocate new GTD */
843  for (GtdIdx = 0; (GtdIdx < MAX_GTD) && HcdGTD(GtdIdx)->inUse; GtdIdx++) {}
844 
845  if (GtdIdx < MAX_GTD) {
846  /*************** Control (word 0) ****************/
847  /* Buffer rounding: R = 1b (yes) */
848  /* Direction/PID: DP = 00b (SETUP) */
849  /* Delay Interrupt: DI = 000b (interrupt) */
850  /* Data Toggle: DT = 00b (from ED) */
851  /* Error Count: EC = 00b */
852  /* Condition Code: CC = 1110b (not accessed) */
853  /****************************************************/
854  memset(HcdGTD(GtdIdx), 0, sizeof(HCD_GeneralTransferDescriptor));
855 
856  HcdGTD(GtdIdx)->inUse = 1;
857  HcdGTD(GtdIdx)->EdIdx = EdIdx;
858 
859  HcdGTD(GtdIdx)->hcGTD.BufferRounding = 1;
860  HcdGTD(GtdIdx)->hcGTD.ConditionCode = (uint32_t) HCD_STATUS_TRANSFER_NotAccessed;
861 
862  /* link new GTD to the Endpoint */
863  if (HcdED(EdIdx)->hcED.TailP) { /* already have place holder */
864  ( (PHCD_GeneralTransferDescriptor) HcdED(EdIdx)->hcED.TailP )->hcGTD.NextTD = (uint32_t) HcdGTD(GtdIdx);
865  }
866  else { /* have no dummy TD attached to the ED */
867  HcdED(EdIdx)->hcED.HeadP.HeadTD = ((uint32_t) HcdGTD(GtdIdx));
868  }
869  HcdED(EdIdx)->hcED.TailP = (uint32_t) HcdGTD(GtdIdx);
870 
871  return HCD_STATUS_OK;
872  }
873  else {
875  }
876 
877 }
878 
879 static HCD_STATUS AllocItdForEd(uint8_t EdIdx)
880 {
881  uint32_t ItdIdx;
882 
883  for (ItdIdx = 0; (ItdIdx < MAX_ITD) && HcdITD(ItdIdx)->inUse; ItdIdx++) {}
884 
885  if (ItdIdx < MAX_ITD) {
886  memset(HcdITD(ItdIdx), 0, sizeof(HCD_IsoTransferDescriptor) );
887  HcdITD(ItdIdx)->inUse = 1;
888  HcdITD(ItdIdx)->EdIdx = EdIdx;
889 
890  HcdITD(ItdIdx)->ConditionCode = (uint32_t) HCD_STATUS_TRANSFER_NotAccessed;
891 
892  /* link new ITD to the Endpoint */
893  if (HcdED(EdIdx)->hcED.TailP) { /* already have place holder */
894  ( (PHCD_IsoTransferDescriptor) HcdED(EdIdx)->hcED.TailP )->NextTD = (uint32_t) HcdITD(ItdIdx);
895  }
896  else { /* have no dummy TD attached to the ED */
897  HcdED(EdIdx)->hcED.HeadP.HeadTD = ((uint32_t) HcdITD(ItdIdx));
898  }
899  HcdED(EdIdx)->hcED.TailP = (uint32_t) HcdITD(ItdIdx);
900 
901  return HCD_STATUS_OK;
902  }
903  else {
905  }
906 }
907 
908 static __INLINE HCD_STATUS FreeED(uint8_t EdIdx)
909 {
910  /* Remove Place holder TD */
911  if ( IsIsoEndpoint(EdIdx) ) {
912  FreeItd( (PHCD_IsoTransferDescriptor) HcdED(EdIdx)->hcED.TailP);
913  }
914  else {
915  FreeGtd( (PHCD_GeneralTransferDescriptor) HcdED(EdIdx)->hcED.TailP);
916  }
917 
918  HcdED(EdIdx)->status = HCD_STATUS_TRANSFER_NotAccessed;
919  HcdED(EdIdx)->inUse = 0;
920 
921  return HCD_STATUS_OK;
922 }
923 
924 static __INLINE HCD_STATUS FreeGtd(PHCD_GeneralTransferDescriptor pGtd)
925 {
926  pGtd->inUse = 0;
927  return HCD_STATUS_OK;
928 }
929 
930 static __INLINE HCD_STATUS FreeItd(PHCD_IsoTransferDescriptor pItd)
931 {
932  pItd->inUse = 0;
933  return HCD_STATUS_OK;
934 }
935 
936 static __INLINE HCD_STATUS OHciHostInit(uint8_t HostID)
937 {
938  uint32_t idx;
939 
940  if ( sizeof(OHCI_HOST_DATA_Type) > 0x4000 ) { /* Host data exceed 16 KB */
942  }
943 
944  memset(&ohci_data[HostID], 0, sizeof(OHCI_HOST_DATA_Type));
945  /* Skip writing 1s to HcHCCA, assume it is 256 aligned */
946 
947  /* set skip bit for all static EDs */
948  for (idx = 0; idx < MAX_STATIC_ED; idx++)
949  ohci_data[HostID].staticEDs[idx].Skip = 1;
950 
951  /* Periodic List Initialization */
953 
954  /* Initialize OHCI registers */
955  OHCI_REG(HostID)->HcControl = 0;
956  OHciHostOperational(HostID);/* have to turn HC to operational mode before setting up below registers*/
957 
958  OHCI_REG(HostID)->HcFmInterval = HC_FMINTERVAL_DEFAULT;
959  OHCI_REG(HostID)->HcPeriodicStart = PERIODIC_START;
960 
961  OHCI_REG(HostID)->HcControlHeadED = (uint32_t) &(ohci_data[HostID].staticEDs[CONTROL_LIST_HEAD]);
962  OHCI_REG(HostID)->HcBulkHeadED = (uint32_t) &(ohci_data[HostID].staticEDs[BULK_LIST_HEAD]);
963 
964  OHCI_REG(HostID)->HcHCCA = (uint32_t) &(ohci_data[HostID].hcca); /* Hook Hcca */
965 
966  /* Set up HcControl */
967  OHCI_REG(HostID)->HcControl |= CONTROL_BULK_SERVICE_RATIO |
974 
975  /* Set Global Power */
976  OHCI_REG(HostID)->HcRhStatus = HC_RH_STATUS_LocalPowerStatusChange;
977 
978  // HcInterrupt Registers Init
979  OHCI_REG(HostID)->HcInterruptStatus |= OHCI_REG(HostID)->HcInterruptStatus; /* Clear Interrupt Status */
980  OHCI_REG(HostID)->HcInterruptDisable = HC_INTERRUPT_ALL;/* Disable all interrupts */
981  /* Enable necessary Interrupts */
982  OHCI_REG(HostID)->HcInterruptEnable = HC_INTERRUPT_MasterInterruptEnable | HC_INTERRUPT_WritebackDoneHead |
983  HC_INTERRUPT_RootHubStatusChange |
984  (SCHEDULING_OVRERRUN_INTERRUPT ? HC_INTERRUPT_SchedulingOverrun : 0 ) |
985  (SOF_INTERRUPT ? HC_INTERRUPT_StartofFrame : 0) |
986  (RESUME_DETECT_INTERRUPT ? HC_INTERRUPT_ResumeDetected : 0) |
987  (UNRECOVERABLE_ERROR_INTERRUPT ? HC_INTERRUPT_UnrecoverableError : 0) |
988  (FRAMENUMBER_OVERFLOW_INTERRUPT ? HC_INTERRUPT_FrameNumberOverflow : 0) |
990 
991  return HCD_STATUS_OK;
992 }
993 
994 static __INLINE HCD_STATUS OHciHostReset(uint8_t HostID)
995 {
996  OHCI_REG(HostID)->HcCommandStatus = HC_COMMAND_STATUS_HostControllerReset;
997  while ( OHCI_REG(HostID)->HcCommandStatus & HC_COMMAND_STATUS_HostControllerReset) {} /* FIXME Wait indefinitely (may need a time-out here) */
998 
999  return HCD_STATUS_OK;
1000 }
1001 
1002 static __INLINE HCD_STATUS OHciHostSuspend(uint8_t HostID)
1003 {
1004  OHCI_REG(HostID)->HcControl =
1005  (OHCI_REG(HostID)->HcControl & (~HC_CONTROL_HostControllerFunctionalState)) | (HC_HOST_SUSPEND << 6);
1006  return HCD_STATUS_OK;
1007 }
1008 
1009 static __INLINE HCD_STATUS OHciHostOperational(uint8_t HostID)
1010 {
1011  OHCI_REG(HostID)->HcControl =
1012  (OHCI_REG(HostID)->HcControl & (~HC_CONTROL_HostControllerFunctionalState)) | (HC_HOST_OPERATIONAL << 6);
1013  return HCD_STATUS_OK;
1014 }
1015 
1016 static __INLINE HCD_STATUS OHciRhPortPowerOn(uint8_t HostID, uint8_t uPortNumber)
1017 {
1018  OHCI_REG(HostID)->HcRhPortStatus1 = HC_RH_PORT_STATUS_PortPowerStatus; /* SetPortPower */
1019  HcdDelayMS(2 * ( (OHCI_REG(HostID)->HcRhDescriptorA & HC_RH_DESCRIPTORA_PowerOnToPowerGoodTime) >> 24 ) ); /* FIXME need to delay here POTPGT */
1020 
1021  return HCD_STATUS_OK;
1022 }
1023 
1024 static __INLINE HCD_STATUS OHciRhPortPowerOff(uint8_t HostID, uint8_t uPortNumber)
1025 {
1026  OHCI_REG(HostID)->HcRhPortStatus1 = HC_RH_PORT_STATUS_LowSpeedDeviceAttached; /* ClearPortPower */
1027  return HCD_STATUS_OK;
1028 }
1029 
1030 static __INLINE HCD_STATUS OHciRhPortSuspend(uint8_t HostID, uint8_t uPortNumber)
1031 {
1032  if ( OHCI_REG(HostID)->HcRhPortStatus1 & HC_RH_PORT_STATUS_CurrentConnectStatus) { /* If device is connected */
1033  OHCI_REG(HostID)->HcRhPortStatus1 = HC_RH_PORT_STATUS_PortSuspendStatus;/* SetPortSuspend */
1034  }
1035  HcdDelayMS(3); /* FIXME 3ms for device to suspend */
1036 
1037  return HCD_STATUS_OK;
1038 }
1039 
1040 static __INLINE HCD_STATUS OHciRhPortResume(uint8_t HostID, uint8_t uPortNumber)
1041 {
1042  if ( OHCI_REG(HostID)->HcRhPortStatus1 & HC_RH_PORT_STATUS_CurrentConnectStatus) { /* If port is currently suspended */
1043  OHCI_REG(HostID)->HcRhPortStatus1 = HC_RH_PORT_STATUS_PortOverCurrentIndicator; /* ClearSuspendStatus */
1044  }
1045  HcdDelayMS(20); /* FIXME 20ms for device to resume */
1046 
1047  return HCD_STATUS_OK;
1048 }
1049 
1050 #endif