LPCOpen Platform
LPCOpen Platform for NXP LPC Microcontrollers
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
EHCI.c
Go to the documentation of this file.
1 /*
2  * @brief Enhanced 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_EHCI__))
39 
40 #define __LPC_EHCI_C__
41 #include "../../../../../../Common/Common.h"
42 #include "../../../USBTask.h"
43 #include "../HCD.h"
44 #include "EHCI.h"
45 
46 // === TODO: Unify USBRAM Section ===
48 EHCI_HOST_DATA_Type ehci_data[MAX_USB_CORE] __BSS(USBRAM_SECTION);
49 // EHCI_HOST_DATA_Type ehci_data __BSS(USBRAM_SECTION);
51 NextLinkPointer PeriodFrameList0[FRAME_LIST_SIZE] ATTR_ALIGNED(4096) __DATA(USBRAM_SECTION); /* Period Frame List */
53 NextLinkPointer PeriodFrameList1[FRAME_LIST_SIZE] ATTR_ALIGNED(4096) __DATA(USBRAM_SECTION); /* Period Frame List */
54 /*=======================================================================*/
55 /* G L O B A L F U N C T I O N S */
56 /*=======================================================================*/
57 HCD_STATUS HcdInitDriver(uint8_t HostID)
58 {
59  EHciHostReset(HostID);
60  return EHciHostInit(HostID);
61 }
62 
63 HCD_STATUS HcdDeInitDriver(uint8_t HostID)
64 {
65  USB_REG(HostID)->USBSTS_H = 0xFFFFFFFF; /* clear all current interrupts */
66  USB_REG(HostID)->PORTSC1_H &= ~(1 << 12); /* clear port power */
67  USB_REG(HostID)->USBMODE_H = (1 << 0); /* set USB mode reserve */
68 
69  return HCD_STATUS_OK;
70 }
71 
72 HCD_STATUS HcdRhPortEnable(uint8_t HostID, uint8_t PortNum)
73 {
74  return HCD_STATUS_OK;
75 }
76 
77 HCD_STATUS HcdRhPortDisable(uint8_t HostID, uint8_t PortNum)
78 {
79  return HCD_STATUS_OK;
80 }
81 
82 HCD_STATUS HcdRhPortReset(uint8_t HostID, uint8_t uPortNumber)
83 {
85 
86  USB_REG(HostID)->PORTSC1_H &= ~EHC_PORTSC_PortEnable; /* Disable Port first */
87  USB_REG(HostID)->PORTSC1_H |= EHC_PORTSC_PortReset; /* Reset port */
88 
89  /* should have time-out */
90  while (USB_REG(HostID)->PORTSC1_H & EHC_PORTSC_PortReset) {}
91 
92  /* PortEnable is always set - Deviation from EHCI */
93 
95  return HCD_STATUS_OK;
96 }
97 
98 HCD_STATUS HcdClearEndpointHalt(uint32_t PipeHandle)// FIXME not implemented
99 {
100  return HCD_STATUS_OK;
101 }
102 
103 uint32_t HcdGetFrameNumber(uint8_t HostID)
104 {
105  return USB_REG(HostID)->FRINDEX_H;
106 }
107 
108 HCD_STATUS HcdGetDeviceSpeed(uint8_t HostID, uint8_t PortNumber, HCD_USB_SPEED *DeviceSpeed)
109 {
110  if ( USB_REG(HostID)->PORTSC1_H & EHC_PORTSC_CurrentConnectStatus) {/* If device is connected */
111  *DeviceSpeed = (HCD_USB_SPEED) ( (USB_REG(HostID)->PORTSC1_H & EHC_PORTSC_PortSpeed) >> 26 ); /* TODO magic number */
112  return HCD_STATUS_OK;
113  }
114  else {
116  }
117 }
118 
119 HCD_STATUS HcdOpenPipe(uint8_t HostID,
120  uint8_t DeviceAddr,
121  HCD_USB_SPEED DeviceSpeed,
122  uint8_t EndpointNumber,
123  HCD_TRANSFER_TYPE TransferType,
124  HCD_TRANSFER_DIR TransferDir,
125  uint16_t MaxPacketSize,
126  uint8_t Interval,
127  uint8_t Mult,
128  uint8_t HSHubDevAddr,
129  uint8_t HSHubPortNum,
130  uint32_t *const pPipeHandle)
131 {
132  uint32_t HeadIdx;
133 
134 #if !ISO_LIST_ENABLE
135  if ( TransferType == ISOCHRONOUS_TRANSFER ) {
136  ASSERT_STATUS_OK_MESSAGE(HCD_STATUS_TRANSFER_TYPE_NOT_SUPPORTED, "Please set ISO_LIST_ENABLE to YES");
137  }
138 #endif
139 
140 #if !INTERRUPT_LIST_ENABLE
141  if ( TransferType == INTERRUPT_TRANSFER ) {
142  ASSERT_STATUS_OK_MESSAGE(HCD_STATUS_TRANSFER_TYPE_NOT_SUPPORTED, "Please set INTERRUPT_LIST_ENABLE to YES");
143  }
144 #endif
145 
146  /********************************* Parameters Verify *********************************/
147  ASSERT_STATUS_OK(OpenPipe_VerifyParameters(HostID, DeviceAddr, DeviceSpeed, EndpointNumber, TransferType,
148  TransferDir, MaxPacketSize, Interval, Mult) );
149 
150  EndpointNumber &= 0xF; /* Endpoint number is in range 0-15 */
151  MaxPacketSize &= 0x3FF; /* Max Packet Size is in range 0-1024 */
152 
153  switch (TransferType) { // TODO should unify more, perharps later
154  case CONTROL_TRANSFER:
155  case BULK_TRANSFER:
156  ASSERT_STATUS_OK(AllocQhd(HostID, DeviceAddr, DeviceSpeed, EndpointNumber, TransferType, TransferDir,
157  MaxPacketSize, Interval, Mult, HSHubDevAddr, HSHubPortNum, &HeadIdx) );
158  DisableAsyncSchedule(HostID);
159  InsertLinkPointer(&HcdAsyncHead(HostID)->Horizontal, &HcdQHD(HostID, HeadIdx)->Horizontal, QHD_TYPE);
160  EnableAsyncSchedule(HostID);
161  break;
162 
163  case INTERRUPT_TRANSFER:
164  ASSERT_STATUS_OK(AllocQhd(HostID, DeviceAddr, DeviceSpeed, EndpointNumber, TransferType, TransferDir,
165  MaxPacketSize, Interval, Mult, HSHubDevAddr, HSHubPortNum, &HeadIdx) );
166  DisablePeriodSchedule(HostID);
167  InsertLinkPointer(&HcdIntHead(HostID)->Horizontal, &HcdQHD(HostID, HeadIdx)->Horizontal, QHD_TYPE);
168  EnablePeriodSchedule(HostID);
169  break;
170 
172 #ifndef __TEST__
173  if (( DeviceSpeed == HIGH_SPEED) || ( TransferDir == IN_TRANSFER) ) { /* TODO currently not support HS due to lack of device for testing */
175  "Highspeed ISO and ISO IN is not supported yet, due to lack of testing");
176  }
177 #endif
178  ASSERT_STATUS_OK(AllocQhd(HostID, DeviceAddr, DeviceSpeed, EndpointNumber, TransferType, TransferDir,
179  MaxPacketSize, Interval, Mult, HSHubDevAddr, HSHubPortNum, &HeadIdx) );
180  break;
181  }
182 
183  PipehandleCreate(pPipeHandle, HostID, TransferType, HeadIdx);
184  return HCD_STATUS_OK;
185 }
186 
187 HCD_STATUS HcdClosePipe(uint32_t PipeHandle)
188 {
189  uint8_t HostID, HeadIdx;
190  HCD_TRANSFER_TYPE XferType;
191 
192  ASSERT_STATUS_OK(HcdCancelTransfer(PipeHandle) );
193  ASSERT_STATUS_OK(PipehandleParse(PipeHandle, &HostID, &XferType, &HeadIdx) );
194 
195  switch (XferType) {
196  case CONTROL_TRANSFER:
197  case BULK_TRANSFER:
198  case INTERRUPT_TRANSFER:
199  RemoveQueueHead(HostID, HeadIdx);
200  USB_REG(HostID)->USBCMD_H |= EHC_USBCMD_IntAsyncAdvanceDoorbell; /* DoorBell Handshake: Queue Head will only be free in AsyncAdvanceIsr */
201  break;
202 
204  FreeQhd(HostID, HeadIdx);
205  break;
206  }
207  return HCD_STATUS_OK;
208 }
209 
211 {
212  uint8_t HostID, HeadIdx;
213  HCD_TRANSFER_TYPE XferType;
214 
215  ASSERT_STATUS_OK(PipehandleParse(PipeHandle, &HostID, &XferType, &HeadIdx) );
216 
217  DisableSchedule(HostID, (XferType == INTERRUPT_TRANSFER) || (XferType == ISOCHRONOUS_TRANSFER) ? 1 : 0);
218 
219  if (XferType == ISOCHRONOUS_TRANSFER) { /* ISOCHRONOUS_TRANSFER */
220  uint32_t i;
221  for (i = 0; i < FRAME_LIST_SIZE; i++) { /*-- Foreach link in Period List Base --*/
222  NextLinkPointer *pNextPointer = &EHCI_FRAME_LIST(HostID)[i];
223 
224  /*-- Foreach Itd/SItd in the link--*/
225  while ( isValidLink(pNextPointer->Link) && pNextPointer->Type != QHD_TYPE) {
226  if (pNextPointer->Type == ITD_TYPE) { /*-- Highspeed ISO --*/
227  PHCD_HS_ITD pItd = (PHCD_HS_ITD) Align32(pNextPointer->Link);
228 
229  if (HeadIdx == pItd->IhdIdx) {
230  /*-- remove matched ITD --*/
231  pNextPointer->Link = pItd->Horizontal.Link;
232  FreeHsItd(pItd);
233  continue; /*-- skip advance pNextPointer due to TD removal --*/
234  }
235  }
236  else if (pNextPointer->Type == SITD_TYPE) { /*-- Split ISO --*/
237  PHCD_SITD pSItd = (PHCD_SITD) Align32(pNextPointer->Link);
238 
239  if (HeadIdx == pSItd->IhdIdx) {
240  /*-- removed matched SITD --*/
241  pNextPointer->Link = pSItd->Horizontal.Link;
242  FreeSItd(pSItd);
243  continue; /*-- skip advance pNextPointer due to TD removal --*/
244  }
245  }
246  pNextPointer = (NextLinkPointer *) Align32(pNextPointer->Link);
247  }
248  }
249  }
250  else { /*-- Bulk / Control / Interrupt --*/
251  uint32_t TdLink = HcdQHD(HostID, HeadIdx)->FirstQtd;
252 
253  /*-- Foreach Qtd in Qhd --*/ /*---------- Deactivate all queued TDs ----------*/
254  while ( isValidLink(TdLink) ) {
255  PHCD_QTD pQtd = (PHCD_QTD) Align32(TdLink);
256  TdLink = pQtd->NextQtd;
257 
258  pQtd->Active = 0;
259  pQtd->IntOnComplete = 0;/* no interrupt scenario on this TD */
260  FreeQtd(pQtd);
261  }
262  HcdQHD(HostID, HeadIdx)->FirstQtd = LINK_TERMINATE;
263  }
264 
265  EnableSchedule(HostID, (XferType == INTERRUPT_TRANSFER) || (XferType == ISOCHRONOUS_TRANSFER) ? 1 : 0);
266  return HCD_STATUS_OK;
267 }
268 
270  const USB_Request_Header_t *const pDeviceRequest,
271  uint8_t *const buffer)
272 {
273  uint8_t HostID, QhdIdx;
274  HCD_TRANSFER_TYPE XferType;
275  uint32_t SetupTdIdx, DataTdIdx, StatusTdIdx;
276  uint8_t direction;
277  uint32_t Datalength;
278 
279  if ((pDeviceRequest == NULL) || (buffer == NULL)) {
280  ASSERT_STATUS_OK_MESSAGE(HCD_STATUS_PARAMETER_INVALID, "Device Request or Data Buffer is NULL");
281  }
282 
283  ASSERT_STATUS_OK(PipehandleParse(PipeHandle, &HostID, &XferType, &QhdIdx) );
284 
285  Datalength = pDeviceRequest->wLength;
286  direction = pDeviceRequest->bmRequestType & 0x80;
287 
288  /*---------- Setup Stage ----------*/
289  ASSERT_STATUS_OK(AllocQTD(HostID, &SetupTdIdx, (uint8_t *) pDeviceRequest, 8, SETUP_TRANSFER, 0, 0) ); /* Setup TD: DirectionPID=00 - DataToggle=10b (always DATA0) */
290 
291  /*---------- Data Stage ----------*/
292  if (Datalength) {
293  ASSERT_STATUS_OK(AllocQTD(HostID, &DataTdIdx, buffer, Datalength, direction ? IN_TRANSFER : OUT_TRANSFER, 1, 0) );
294  }
295  else {
296  DataTdIdx = SetupTdIdx; /* Data TD is skipped */
297  }
298 
299  /*---------- Status Stage ----------*/
300  ASSERT_STATUS_OK(AllocQTD(HostID, &StatusTdIdx, NULL, 0, direction ? OUT_TRANSFER : IN_TRANSFER, 1, 1) ); /* Status TD: Direction=opposite of data direction - DataToggle=11b (always DATA1) */
301 
302  /* Hook TDs Together */
303  HcdQTD(HostID, SetupTdIdx)->NextQtd = (uint32_t) HcdQTD(HostID, DataTdIdx);
304  HcdQTD(HostID, DataTdIdx)->NextQtd = (uint32_t) HcdQTD(HostID, StatusTdIdx);
305 
306  /* Hook TDs to QHD */
307  HcdQHD(HostID, QhdIdx)->Overlay.NextQtd = (uint32_t) HcdQTD(HostID, SetupTdIdx);
308  HcdQHD(HostID, QhdIdx)->FirstQtd = Align32( (uint32_t) HcdQTD(HostID, SetupTdIdx) );
309 
310  HcdQHD(HostID, QhdIdx)->status = (uint32_t) HCD_STATUS_TRANSFER_QUEUED;
311 
312  /* wait for semaphore compete TDs */
313  ASSERT_STATUS_OK(WaitForTransferComplete(HostID, QhdIdx) );
314 
315  return HCD_STATUS_OK;
316 }
317 
319  uint8_t *const buffer,
320  uint32_t const length,
321  uint16_t *const pActualTransferred)
322 {
323  uint8_t HostID, HeadIdx;
324  HCD_TRANSFER_TYPE XferType;
325  uint32_t DataTdIdx;
326  uint32_t ExpectedLength;
327 
328  if ((buffer == NULL) || (length == 0)) {
329  ASSERT_STATUS_OK_MESSAGE(HCD_STATUS_PARAMETER_INVALID, "Data Buffer is NULL or Transfer Length is 0");
330  }
331 
332  ASSERT_STATUS_OK(PipehandleParse(PipeHandle, &HostID, &XferType, &HeadIdx) );
333 
334  ExpectedLength = (length != HCD_ENDPOINT_MAXPACKET_XFER_LEN) ? length : HcdQHD(HostID, HeadIdx)->MaxPackageSize;/* LUFA adaption, receive only 1 data transaction */
335 
336  if (XferType == ISOCHRONOUS_TRANSFER) {
337  if ( HcdQHD(HostID, HeadIdx)->EndpointSpeed == HIGH_SPEED ) { /*-- Highspeed ISO --*/
338  ASSERT_STATUS_OK(QueueITDs(HostID, HeadIdx, buffer, ExpectedLength) );
339  }
340  else { /*-- Full/Low Speed ISO --*/
341  ASSERT_STATUS_OK(QueueSITDs(HostID, HeadIdx, buffer, ExpectedLength) );
342  }
343  }
344  else { /*-- Control / Bulk / Interrupt --*/
345  /* ASSERT_STATUS_OK( QueueQTDs(&DataTdIdx, buffer, ExpectedLength, HcdQHD(HeadIdx)->Direction ? IN_TRANSFER : OUT_TRANSFER, 0, 1) ); */
346  /* For simplicity, this only queue one TD only */
347  ASSERT_STATUS_OK(AllocQTD(HostID, &DataTdIdx, buffer, ExpectedLength,
348  HcdQHD(HostID, HeadIdx)->Direction ? IN_TRANSFER : OUT_TRANSFER, 0, 1) );
349 
350  /*---------- Hook to Queue Head ----------*/
351  HcdQHD(HostID, HeadIdx)->Overlay.NextQtd = (uint32_t) HcdQTD(HostID, DataTdIdx);
352  HcdQHD(HostID, HeadIdx)->FirstQtd = Align32( (uint32_t) HcdQTD(HostID, DataTdIdx) ); /* used as TD head to clean up TD chain when transfer done */
353  }
354 
355  HcdQHD(HostID, HeadIdx)->status = (uint32_t) HCD_STATUS_TRANSFER_QUEUED;
356  HcdQHD(HostID, HeadIdx)->pActualTransferCount = pActualTransferred; /* TODO Actual Length get rid of this */
357  if (HcdQHD(HostID, HeadIdx)->pActualTransferCount) {
358  *(HcdQHD(HostID, HeadIdx)->pActualTransferCount) = ExpectedLength;
359  }
360 
361  return HCD_STATUS_OK;
362 }
363 
364 HCD_STATUS HcdGetPipeStatus(uint32_t PipeHandle)/* TODO can be implemented based on overlay */
365 {
366  uint8_t HostID, HeadIdx;
367  HCD_TRANSFER_TYPE XferType;
368 
369  ASSERT_STATUS_OK(PipehandleParse(PipeHandle, &HostID, &XferType, &HeadIdx) );
370 
371  return (HCD_STATUS)HcdQHD(HostID, HeadIdx)->status;
372 }
373 
374 static void FreeQhd(uint8_t HostID, uint8_t QhdIdx)
375 {
376  HcdQHD(HostID, QhdIdx)->status = HCD_STATUS_STRUCTURE_IS_FREE;
377  HcdQHD(HostID, QhdIdx)->Horizontal.Link |= LINK_TERMINATE;
378  HcdQHD(HostID, QhdIdx)->inUse = 0;
379 }
380 
381 static HCD_STATUS AllocQhd(uint8_t HostID,
382  uint8_t DeviceAddr,
383  HCD_USB_SPEED DeviceSpeed,
384  uint8_t EndpointNumber,
385  HCD_TRANSFER_TYPE TransferType,
386  HCD_TRANSFER_DIR TransferDir,
387  uint16_t MaxPacketSize,
388  uint8_t Interval,
389  uint8_t Mult,
390  uint8_t HSHubDevAddr,
391  uint8_t HSHubPortNum,
392  uint32_t *pQhdIdx)
393 {
394  /* Looking for a free QHD */
395  for ( (*pQhdIdx) = 0; (*pQhdIdx) < HCD_MAX_QTD && HcdQHD(HostID, *pQhdIdx)->inUse; (*pQhdIdx)++) {}
396 
397  if ((*pQhdIdx) == HCD_MAX_QTD ) {
399  }
400 
401  memset(HcdQHD(HostID, *pQhdIdx), 0, sizeof(HCD_QHD) );
402 
403  /* Init Data For Queue Head */
404  HcdQHD(HostID, *pQhdIdx)->inUse = 1;
405  HcdQHD(HostID, *pQhdIdx)->Direction = (TransferDir == IN_TRANSFER) ? 1 : 0; /* Control Endpoint should not use this parameter */
406  HcdQHD(HostID, *pQhdIdx)->Interval = Interval;
407  HcdQHD(HostID, *pQhdIdx)->status = HCD_STATUS_OK;
408  HcdQHD(HostID, *pQhdIdx)->FirstQtd = LINK_TERMINATE;
409 
410  HcdQHD(HostID, *pQhdIdx)->Horizontal.Link = LINK_TERMINATE;
411  HcdQHD(HostID, *pQhdIdx)->DeviceAddress = DeviceAddr;
412 
413  HcdQHD(HostID, *pQhdIdx)->InActiveOnNextTransaction = 0;
414  HcdQHD(HostID, *pQhdIdx)->EndpointNumber = EndpointNumber;
415  HcdQHD(HostID, *pQhdIdx)->EndpointSpeed = (uint32_t) DeviceSpeed;
416  HcdQHD(HostID, *pQhdIdx)->DataToggleControl = (TransferType == CONTROL_TRANSFER) ? 1 : 0;
417  HcdQHD(HostID, *pQhdIdx)->HeadReclamationFlag = 0;
418  HcdQHD(HostID, *pQhdIdx)->MaxPackageSize = MaxPacketSize;
419  HcdQHD(HostID,
420  *pQhdIdx)->ControlEndpointFlag = (DeviceSpeed != HIGH_SPEED && TransferType == CONTROL_TRANSFER) ? 1 : 0;
421  HcdQHD(HostID, *pQhdIdx)->NakCountReload = 0; /* infinite NAK/NYET */
422 
423  /*-- Currently All interrupt endpoints will be served as 1 (micro)frame polling, thus Interval parameter is ignored --*/
424  /*-- For High Speed Interval should be used to compute the uFrameSMask --*/
425  HcdQHD(HostID,
426  *pQhdIdx)->uFrameSMask =
427  (TransferType == INTERRUPT_TRANSFER) ? (DeviceSpeed == HIGH_SPEED ? 0xFF : 0x01) : 0;
428  HcdQHD(HostID,
429  *pQhdIdx)->uFrameCMask = (DeviceSpeed != HIGH_SPEED && TransferType == INTERRUPT_TRANSFER) ? 0x1C : 0; /*-- Schedule Complete Split at uFrame2, uFrame3 and uFrame4 --*/
430  HcdQHD(HostID, *pQhdIdx)->HubAddress = HSHubDevAddr;
431  HcdQHD(HostID, *pQhdIdx)->PortNumber = HSHubPortNum;
432  HcdQHD(HostID, *pQhdIdx)->Mult = (DeviceSpeed == HIGH_SPEED ? Mult : 1);
433 
434  HcdQHD(HostID, *pQhdIdx)->Overlay.NextQtd = LINK_TERMINATE;
435  HcdQHD(HostID, *pQhdIdx)->Overlay.AlterNextQtd = LINK_TERMINATE;
436  HcdQHD(HostID, *pQhdIdx)->FirstQtd = LINK_TERMINATE;
437 
438  HcdQHD(HostID,
439  *pQhdIdx)->Overlay.PingState_Err =
440  (DeviceSpeed == HIGH_SPEED && TransferType != INTERRUPT_TRANSFER && TransferDir == OUT_TRANSFER) ? 1 : 0;
441 
442  return HCD_STATUS_OK;
443 }
444 
445 static HCD_STATUS InsertLinkPointer(NextLinkPointer *pList, NextLinkPointer *pNew, uint8_t type)
446 {
447  pNew->Link = pList->Link;
448  pList->Link = Align32( (uint32_t) pNew);
449  pList->Type = type;
450  return HCD_STATUS_OK;
451 }
452 
453 static HCD_STATUS RemoveQueueHead(uint8_t HostID, uint8_t QhdIdx)
454 {
455  PHCD_QHD pQhd = IsInterruptQhd(HostID, QhdIdx) ? HcdIntHead(HostID) : HcdAsyncHead(HostID);
456  /*-- Foreach Qhd in async list --*/
457  while ( isValidLink(pQhd->Horizontal.Link) &&
458  Align32(pQhd->Horizontal.Link) != (uint32_t) HcdQHD(HostID, QhdIdx) &&
459  Align32(pQhd->Horizontal.Link) != (uint32_t) HcdAsyncHead(HostID) ) {
460  pQhd = (PHCD_QHD) Align32(pQhd->Horizontal.Link);
461  }
462  if ( Align32(pQhd->Horizontal.Link) != (uint32_t) HcdQHD(HostID, QhdIdx) ) {
464  }
465 
466  HcdQHD(HostID, QhdIdx)->status = (uint32_t) HCD_STATUS_TO_BE_REMOVED; /* Will be remove in AsyncAdvanceIsr - make use of IAAD */
467  pQhd->Horizontal.Link = HcdQHD(HostID, QhdIdx)->Horizontal.Link;
468 
469  return HCD_STATUS_OK;
470 }
471 
472 /*---------- Queue TD Routines ----------*/
473 static void FreeQtd(PHCD_QTD pQtd)
474 {
475  pQtd->NextQtd |= LINK_TERMINATE;
476  pQtd->inUse = 0;
477 }
478 
480 static HCD_STATUS AllocQTD(uint8_t HostID,
481  uint32_t *pTdIdx,
482  uint8_t *const BufferPointer,
483  uint32_t xferLen,
484  HCD_TRANSFER_DIR PIDCode,
485  uint8_t DataToggle,
486  uint8_t IOC)
487 {
488  for ((*pTdIdx) = 0; (*pTdIdx) < HCD_MAX_QTD && HcdQTD(HostID, *pTdIdx)->inUse; (*pTdIdx)++) {}
489 
490  if ((*pTdIdx) < HCD_MAX_QTD) {
491  uint8_t idx = 1;
492  uint32_t BytesInPage;
493 
494  memset(HcdQTD(HostID, *pTdIdx), 0, sizeof(HCD_QTD));
495 
496  HcdQTD(HostID, *pTdIdx)->NextQtd = 1;
497 
498  HcdQTD(HostID, *pTdIdx)->AlterNextQtd = LINK_TERMINATE;
499  HcdQTD(HostID, *pTdIdx)->inUse = 1;
500 
501  HcdQTD(HostID, *pTdIdx)->Active = 1;
502  HcdQTD(HostID, *pTdIdx)->PIDCode = (PIDCode == SETUP_TRANSFER) ? 2 : (PIDCode == IN_TRANSFER ? 1 : 0);
503  HcdQTD(HostID, *pTdIdx)->TotalBytesToTransfer = xferLen;
504  HcdQTD(HostID, *pTdIdx)->DataToggle = DataToggle;
505  HcdQTD(HostID, *pTdIdx)->IntOnComplete = IOC;
506 
507  HcdQTD(HostID, *pTdIdx)->BufferPointer[0] = (uint32_t) BufferPointer;
508  BytesInPage = 0x1000 - Offset4k((uint32_t) BufferPointer);
509  xferLen -= MIN(xferLen, BytesInPage); /*-- Trim down xferlen to be multiple of 4k --*/
510 
511  for (idx = 1; idx <= 4 && xferLen > 0; idx++) {
512  HcdQTD(HostID,
513  *pTdIdx)->BufferPointer[idx] = Align4k(HcdQTD(HostID, (*pTdIdx))->BufferPointer[idx - 1]) + 0x1000;
514  xferLen -= MIN(xferLen, 0x1000);
515  }
516  return HCD_STATUS_OK;
517  }
518  else {
520  }
521 }
522 
523 #if 0
524 /* For simplicity, this only queue one TD only */
525 static HCD_STATUS QueueQTDs(uint32_t *pTdIdx,
526  uint8_t *dataBuff,
527  uint32_t xferLen,
528  HCD_TRANSFER_DIR PIDCode,
529  uint8_t DataToggle)
530 {
531  uint32_t TailTdIdx = 0xFFFFFFFF;
532 
533  while (xferLen > 0) {
534  uint32_t TdLen;
535  uint32_t MaxTDLen = QTD_MAX_XFER_LENGTH - Offset4k((uint32_t) dataBuff);
536 
537  TdLen = MIN(xferLen, MaxTDLen);
538  xferLen -= TdLen;
539 
540  if (TailTdIdx == 0xFFFFFFFF) {
541  ASSERT_STATUS_OK(AllocQTD(pTdIdx, dataBuff, TdLen, PIDCode, DataToggle, (xferLen == 0) ? 1 : 0) );
542  TailTdIdx = *pTdIdx;
543  }
544  else {
545  uint32_t NewTdIDx;
546  ASSERT_STATUS_OK(AllocQTD(&NewTdIDx, dataBuff, TdLen, PIDCode, DataToggle, (xferLen == 0) ? 1 : 0) );
547  HcdQTD(TailTdIdx)->NextQtd = Align32((uint32_t) HcdQTD(NewTdIDx));
548  TailTdIdx = NewTdIDx;
549  }
550  DataToggle = 1 - DataToggle;
551  dataBuff += TdLen;
552  }
553  return HCD_STATUS_OK;
554 }
555 
556 #endif
557 
558 static void FreeHsItd(PHCD_HS_ITD pItd)
559 {
560  pItd->Horizontal.Link |= LINK_TERMINATE;
561  pItd->inUse = 0;
562 }
563 
564 HCD_STATUS AllocHsItd(uint8_t HostID,
565  uint32_t *pTdIdx,
566  uint8_t IhdIdx,
567  uint8_t *dataBuff,
568  uint32_t TDLen,
569  uint8_t XactPerITD,
570  uint8_t IntOnComplete)
571 {
572  for ((*pTdIdx) = 0; (*pTdIdx) < HCD_MAX_HS_ITD && HcdHsITD(HostID, *pTdIdx)->inUse; (*pTdIdx)++) {}
573  if ((*pTdIdx) < HCD_MAX_HS_ITD) {
574  uint8_t i;
575  uint8_t XactStep = 8 / XactPerITD;
576  uint32_t MaxXactLen = HcdQHD(HostID, IhdIdx)->MaxPackageSize * HcdQHD(HostID, IhdIdx)->Mult;
577 
578  memset(HcdHsITD(HostID, *pTdIdx), 0, sizeof(HCD_HS_ITD));
579 
580  HcdHsITD(HostID, *pTdIdx)->inUse = 1;
581  HcdHsITD(HostID, *pTdIdx)->IhdIdx = IhdIdx;
582 
583  HcdHsITD(HostID, *pTdIdx)->Horizontal.Link = LINK_TERMINATE;
584  for (i = 0; TDLen > 0 && i < 8; i += XactStep) {
585  uint32_t XactLen = MIN(TDLen, MaxXactLen);
586  TDLen -= XactLen;
587 
588  HcdHsITD(HostID, *pTdIdx)->BufferPointer[i] = Align4k( (uint32_t) dataBuff);
589 
590  HcdHsITD(HostID, *pTdIdx)->Transaction[i].Offset = ( (uint32_t) dataBuff ) & 4095;
591  HcdHsITD(HostID, *pTdIdx)->Transaction[i].PageSelect = i;
592  HcdHsITD(HostID, *pTdIdx)->Transaction[i].IntOnComplete = (IntOnComplete && TDLen == 0) ? 1 : 0;
593  HcdHsITD(HostID, *pTdIdx)->Transaction[i].Length = XactLen;
594  HcdHsITD(HostID, *pTdIdx)->Transaction[i].Active = 1;
595 
596  dataBuff += XactLen;
597  }
598 
599  HcdHsITD(HostID, *pTdIdx)->BufferPointer[0] |= (HcdQHD(HostID, IhdIdx)->EndpointNumber << 8) | HcdQHD(HostID,
600  IhdIdx)->
602  HcdHsITD(HostID,
603  *pTdIdx)->BufferPointer[1] |=
604  (HcdQHD(HostID, IhdIdx)->Direction << 1) | HcdQHD(HostID, IhdIdx)->MaxPackageSize;
605  HcdHsITD(HostID, *pTdIdx)->BufferPointer[2] |= HcdQHD(HostID, IhdIdx)->Mult;
606 
607  return HCD_STATUS_OK;
608  }
609  else {
611  }
612 }
613 
614 static HCD_STATUS QueueITDs(uint8_t HostID, uint8_t IhdIdx, uint8_t *dataBuff, uint32_t xferLen)
615 {
616  uint32_t MaxTDLen;
617  uint32_t FrameIdx;
618 
619 #if 0 /* Maximum bandwidth (Interval = 1) regardless of Interval value */
620  uint8_t XactPerITD;
621  uint32_t FramePeriod;
622  if (HcdQHD(IhdIdx)->Interval < 4) { /*-- Period < 8 --*/
623  XactPerITD = 1 << ( 4 - HcdQHD(IhdIdx)->Interval ); /*-- Interval 1 => 8, 2 => 4, 3 => 2 --*/
624  FramePeriod = 1;
625  }
626  else {
627  XactPerITD = 1;
628  FramePeriod = 1 << ( HcdQHD(IhdIdx)->Interval - 4 ); /*-- Frame step 4 => 1, 5 => 2, 6 => 3 --*/
629  }
630 #else
631  #define XactPerITD 8
632  #define FramePeriod 1
633 #endif
634 
635  MaxTDLen = XactPerITD * HcdQHD(HostID, IhdIdx)->MaxPackageSize * HcdQHD(HostID, IhdIdx)->Mult;
636  FrameIdx = USB_REG(HostID)->FRINDEX_H >> 3;
637 
638  if (xferLen > MaxTDLen * FRAME_LIST_SIZE) { /*-- Data length overflow the Period FRAME LIST --*/
641  "ISO data length overflows the Period Frame List size, Please increase size by FRAMELIST_SIZE_BITS or reduce data length");
642  }
643 
644  while (xferLen > 0) {
645  uint32_t TdIdx;
646  uint32_t TDLen;
647 
648  TDLen = MIN(xferLen, MaxTDLen);
649  xferLen -= TDLen;
650 
651  ASSERT_STATUS_OK(AllocHsItd(HostID, &TdIdx, IhdIdx, dataBuff, TDLen, XactPerITD, xferLen ? 0 : 1) );
652 
653  FrameIdx = (FrameIdx + FramePeriod) % FRAME_LIST_SIZE;
654  /*-- Hook ITD to Period List Base --*/
655  InsertLinkPointer(&EHCI_FRAME_LIST(HostID)[FrameIdx], &HcdHsITD(HostID, TdIdx)->Horizontal, ITD_TYPE);
656  dataBuff += TDLen;
657  }
658 
659  return HCD_STATUS_OK;
660 }
661 
662 static void FreeSItd(PHCD_SITD pSItd)
663 {
664  pSItd->Horizontal.Link |= LINK_TERMINATE;
665  pSItd->inUse = 0;
666 }
667 
668 static HCD_STATUS AllocSItd(uint8_t HostID,
669  uint32_t *pTdIdx,
670  uint8_t HeadIdx,
671  uint8_t *dataBuff,
672  uint32_t TDLen,
673  uint8_t IntOnComplete)
674 {
675 #define TCount_Pos 0
676 #define TPos_Pos 3
677 
678  for ((*pTdIdx) = 0; (*pTdIdx) < HCD_MAX_SITD && HcdSITD(HostID, *pTdIdx)->inUse; (*pTdIdx)++) {}
679 
680  if ((*pTdIdx) < HCD_MAX_SITD) {
681  uint8_t TCount = TDLen / SPLIT_MAX_LEN_UFRAME + (TDLen % SPLIT_MAX_LEN_UFRAME ? 1 : 0); /*-- Number of Slipt Transactions --*/
682 
683  memset(HcdSITD(HostID, *pTdIdx), 0, sizeof(HCD_SITD) );
684 
685  HcdSITD(HostID, *pTdIdx)->inUse = 1;
686  HcdSITD(HostID, *pTdIdx)->IhdIdx = HeadIdx;
687 
688  /*-- Word 1 --*/
689  HcdSITD(HostID, *pTdIdx)->Horizontal.Link = LINK_TERMINATE;
690  /*-- Word 2 --*/
691  HcdSITD(HostID, *pTdIdx)->DeviceAddress = HcdQHD(HostID, HeadIdx)->DeviceAddress;
692  HcdSITD(HostID, *pTdIdx)->EndpointNumber = HcdQHD(HostID, HeadIdx)->EndpointNumber;
693  HcdSITD(HostID, *pTdIdx)->HubAddress = HcdQHD(HostID, HeadIdx)->HubAddress;
694  HcdSITD(HostID, *pTdIdx)->PortNumber = HcdQHD(HostID, HeadIdx)->PortNumber;
695  HcdSITD(HostID, *pTdIdx)->Direction = HcdQHD(HostID, HeadIdx)->Direction;
696  /*-- Word 3 --*/
697  HcdSITD(HostID, *pTdIdx)->uFrameSMask = (1 << TCount) - 1;
698  HcdSITD(HostID, *pTdIdx)->uFrameCMask = 0;
699  /*-- Word 4 --*/
700  HcdSITD(HostID, *pTdIdx)->Active = 1;
701  HcdSITD(HostID, *pTdIdx)->TotalBytesToTransfer = TDLen;
702  HcdSITD(HostID, *pTdIdx)->IntOnComplete = IntOnComplete;
703  /*-- Word 5 --*/
704  HcdSITD(HostID, *pTdIdx)->BufferPointer[0] = (uint32_t) dataBuff;
705  /*-- Word 6 --*/
706  HcdSITD(HostID, *pTdIdx)->BufferPointer[1] = Align4k( ((uint32_t) dataBuff) + TDLen);
707 
708  HcdSITD(HostID, *pTdIdx)->BufferPointer[1] |= TCount << TCount_Pos;
709  HcdSITD(HostID, *pTdIdx)->BufferPointer[1] |= (TCount > 1 ? 1 : 0 ) << TPos_Pos;/*-- TPosition - More than 1 split --> Begin Encoding, Otherwise All Encoding --*/
710 
711  // HcdSITD(*pTdIdx)->TCount = NoSplits;
712  // HcdSITD(*pTdIdx)->TPosition = (NoSplits > 1) ? 1 : 0 ; /*-- TPosition - More than 1 split --> Begin Encoding, Otherwise All Encoding --*/
713 
714  /*-- Word 7 --*/
715  HcdSITD(HostID, *pTdIdx)->BackPointer = LINK_TERMINATE;
716 
717  return HCD_STATUS_OK;
718  }
719  else {
721  }
722 }
723 
724 static HCD_STATUS QueueSITDs(uint8_t HostID, uint8_t HeadIdx, uint8_t *dataBuff, uint32_t xferLen)
725 {
726  uint32_t FrameIdx;
727 
728 #if 0 /* Maximum bandwidth (Interval = 1) regardless of Interval value */
729  uint8_t XactPerITD;
730  uint32_t FramePeriod;
731  if (HcdQHD(IhdIdx)->Interval < 4) { /*-- Period < 8 --*/
732  FramePeriod = 1;
733  }
734  else {
735  FramePeriod = 1 << ( HcdQHD(IhdIdx)->Interval - 4 ); /*-- Frame step 4 => 1, 5 => 2, 6 => 3 --*/
736  }
737 #else
738  #define FramePeriod 1
739 #endif
740 
741  if (xferLen > HcdQHD(HostID, HeadIdx)->MaxPackageSize * FRAME_LIST_SIZE) { /*-- Data length overflow the Period FRAME LIST --*/
744  "ISO data length overflows the Period Frame List size, Please increase size by FRAMELIST_SIZE_BITS or reduce data length");
745  }
746 
747  FrameIdx = USB_REG(HostID)->FRINDEX_H >> 3;
748  while (xferLen) {
749  uint32_t TdIdx;
750  uint32_t TDLen;
751 
752  TDLen = MIN(xferLen, HcdQHD(HostID, HeadIdx)->MaxPackageSize);
753  xferLen -= TDLen;
754 
755  ASSERT_STATUS_OK(AllocSItd(HostID, &TdIdx, HeadIdx, dataBuff, TDLen, xferLen ? 0 : 1) );
756 
757  FrameIdx = (FrameIdx + FramePeriod) % FRAME_LIST_SIZE;
758  /*-- Hook SITD to Period List Base --*/
759  InsertLinkPointer(&EHCI_FRAME_LIST(HostID)[FrameIdx], &HcdSITD(HostID, TdIdx)->Horizontal, SITD_TYPE);
760  dataBuff += TDLen;
761  }
762  return HCD_STATUS_OK;
763 }
764 
765 static HCD_STATUS WaitForTransferComplete(uint8_t HostID, uint8_t EdIdx)/* TODO indentical to OHCI now */
766 {
767 
768 #ifndef __TEST__
769  while ( HcdQHD(HostID, EdIdx)->status == HCD_STATUS_TRANSFER_QUEUED ) {
770  /* Should have time-out but left blank intentionally for bug catcher */
771  }
772  return (HCD_STATUS) HcdQHD(HostID, EdIdx)->status;
773 #else
774  return HCD_STATUS_OK;
775 #endif
776 
777 }
778 
779 static HCD_STATUS PipehandleParse(uint32_t Pipehandle, uint8_t *pHostID, HCD_TRANSFER_TYPE *XferType, uint8_t *pIdx)
780 {
781  Pipe_Handle_Type *pHandle = (Pipe_Handle_Type *) (&Pipehandle);
782 
783  if ((pHandle->HostId >= MAX_USB_CORE) ||
784  ( pHandle->Idx >= HCD_MAX_QTD) ||
785  ( HcdQHD(pHandle->HostId, pHandle->Idx)->inUse == 0) ||
786  ( HcdQHD(pHandle->HostId, pHandle->Idx)->status == HCD_STATUS_TO_BE_REMOVED) ) {
788  }
789 
790  if (pHostID) {
791  *pHostID = pHandle->HostId;
792  }
793  if (pIdx) {
794  *pIdx = pHandle->Idx;
795  }
796  if (XferType) {
797  *XferType = (HCD_TRANSFER_TYPE) pHandle->Type;
798  }
799 
800  return HCD_STATUS_OK;
801 }
802 
803 static void PipehandleCreate(uint32_t *pPipeHandle, uint8_t HostID, HCD_TRANSFER_TYPE XferType, uint8_t idx)
804 {
805  /*---------- HostID | PortNum | Type | Idx ----------*/
806  Pipe_Handle_Type *pHandle = (Pipe_Handle_Type *) pPipeHandle;
807 
808  pHandle->HostId = HostID;
809  pHandle->PortNumber = 0;
810  pHandle->Type = (uint8_t) XferType;
811  pHandle->Idx = idx;
812 }
813 
814 static __INLINE PHCD_QHD HcdAsyncHead(uint8_t HostID)
815 {
816  return &(ehci_data[HostID].AsyncHeadQhd);
817  // return &(ehci_data.AsyncHeadQhd);
818 }
819 
820 static __INLINE PHCD_QHD HcdIntHead(uint8_t HostID)
821 {
822  return &(ehci_data[HostID].IntHeadQhd);
823  // return &(ehci_data.IntHeadQhd);
824 }
825 
826 // === TODO: Deal with HostID later ===
827 static __INLINE PHCD_QHD HcdQHD(uint8_t HostID, uint8_t idx)
828 {
829  return &(ehci_data[HostID].qHDs[idx]);
830  // return &(ehci_data.qHDs[idx]);
831 }
832 
833 static __INLINE PHCD_QTD HcdQTD(uint8_t HostID, uint8_t idx)
834 {
835  return &(ehci_data[HostID].qTDs[idx]);
836  // return &(ehci_data.qTDs[idx]);
837 }
838 
839 static __INLINE PHCD_SITD HcdSITD(uint8_t HostID, uint8_t idx)
840 {
841  return &(ehci_data[HostID].siTDs[idx]);
842  // return &(ehci_data.siTDs[idx]);
843 }
844 
845 static __INLINE PHCD_HS_ITD HcdHsITD(uint8_t HostID, uint8_t idx)
846 {
847  return &(ehci_data[HostID].iTDs[idx]);
848  // return &(ehci_data.iTDs[idx]);
849 }
850 
851 static __INLINE bool isValidLink(uint32_t link)
852 {
853  return (link & LINK_TERMINATE) == 0;
854 }
855 
856 static __INLINE bool IsInterruptQhd(uint8_t HostID, uint8_t QhdIdx)
857 {
858  return HcdQHD(HostID, QhdIdx)->uFrameSMask;
859 }
860 
861 void HcdIrqHandler(uint8_t HostID)
862 {
864  uint32_t t = USB_REG(HostID)->USBINTR_H;
865  IntStatus = USB_REG(HostID)->USBSTS_H & t;
866 
867  if (IntStatus == 0) {
868  return;
869  }
870 
871  /* disable all interrupt for processing */
872  /* Acknowledge Interrrupt */
873  USB_REG(HostID)->USBSTS_H |= IntStatus;
874 
875  /* Process Interrupt Sources */
876  if (IntStatus & EHC_USBSTS_PortChangeDetect) {
877  uint32_t PortSC = USB_REG(HostID)->PORTSC1_H;
878  if (PortSC & EHC_PORTSC_ConnectStatusChange) {
879  PortStatusChangeIsr(HostID, PortSC & EHC_PORTSC_CurrentConnectStatus);
880  USB_REG(HostID)->PORTSC1_H |= EHC_PORTSC_ConnectStatusChange; /* Clear PortSC Interrupt Status */
881  }
882  if (PortSC & EHC_PORTSC_PortEnableChange) {
883  USB_REG(HostID)->PORTSC1_H |= EHC_PORTSC_PortEnableChange; /* Clear PortSC Interrupt Status */
884  }
885  if (PortSC & EHC_PORTSC_OvercurrentChange) {
886  USB_REG(HostID)->PORTSC1_H |= EHC_PORTSC_OvercurrentChange; /* Clear PortSC Interrupt Status */
887  }
888  if (PortSC & EHC_PORTSC_ForcePortResume) {
889  USB_REG(HostID)->PORTSC1_H |= EHC_PORTSC_ForcePortResume; /* Clear PortSC Interrupt Status */
890  }
891  }
892 
893  if (IntStatus & EHC_USBSTS_UsbAsyncInt) {
894  AsyncScheduleIsr(HostID);
895  }
896 
897  if (IntStatus & EHC_USBSTS_UsbPeriodInt) {
898  PeriodScheduleIsr(HostID);
899  }
900 
901  if (IntStatus & EHC_USBSTS_UsbErrorInt) {
902  UsbErrorIsr(HostID);
903  }
904 
905  if (IntStatus & EHC_USBSTS_IntAsyncAdvance) {
906  AsyncAdvanceIsr(HostID);
907  }
908  /* Enable Interrupt */
909 }
910 
911 static void RemoveCompletedQTD(PHCD_QHD pQhd)
912 {
913  PHCD_QTD pQtd;
914  uint32_t TdLink = pQhd->FirstQtd;
915 
916  /*-- Foreach Qtd in Qhd --*/
917  while ( (isValidLink(TdLink), pQtd = (PHCD_QTD) Align32(TdLink) ) &&
918  pQtd->Active == 0) {
919  TdLink = pQtd->NextQtd;
920 
921  if (pQhd->pActualTransferCount) {
922  *(pQhd->pActualTransferCount) -= pQtd->TotalBytesToTransfer;
923  }
924 
925  if (pQtd->IntOnComplete) {
926  pQhd->status = HCD_STATUS_OK;
927  }
928  if (pQtd->Halted /*|| pQtd->Babble || pQtd->BufferError || pQtd->TransactionError*/) {
929  pQhd->status = HCD_STATUS_TRANSFER_Stall;
930  }
931  FreeQtd(pQtd);
932  }
933  pQhd->FirstQtd = TdLink;
934 }
935 
936 static void RemoveErrorQTD(PHCD_QHD pQhd)
937 {
938  PHCD_QTD pQtd;
939  uint32_t TdLink = pQhd->FirstQtd;
940  bool errorfound = false;
941 
942  /*-- Scan error Qtd in Qhd --*/
943  while ( (isValidLink(TdLink), pQtd = (PHCD_QTD) Align32(TdLink) ) &&
944  pQtd->Active == 0) {
945  TdLink = pQtd->NextQtd;
946 
947  if (pQtd->Halted /*|| pQtd->Babble || pQtd->BufferError || pQtd->TransactionError*/) {
948  errorfound = true;
949  pQhd->status = HCD_STATUS_TRANSFER_Stall;
950  }
951  }
952  /*-- Remove error Qtd in Qhd --*/
953  if (errorfound) {
954  TdLink = pQhd->FirstQtd;
955  while (isValidLink(TdLink)) {
956  pQtd = (PHCD_QTD) Align32(TdLink);
957  TdLink = pQtd->NextQtd;
958  pQtd->Active = 0;
959  pQtd->IntOnComplete = 0;
960  FreeQtd(pQtd);
961  }
962  pQhd->FirstQtd = LINK_TERMINATE;
963  pQhd->Overlay.Halted = 0;
964  }
965 }
966 
967 /*---------- Interrupt On Compete has occurred, however we have no clues on which QueueHead it happened. Also IOC TD may be advanced already So we will free all TD which is not Active (transferred already) ----------*/
968 static void AsyncScheduleIsr(uint8_t HostID)
969 {
970  PHCD_QHD pQhd = HcdAsyncHead(HostID);
971 
972  /*-- Foreach Qhd in async list --*/
973  while ( isValidLink(pQhd->Horizontal.Link) &&
974  Align32(pQhd->Horizontal.Link) != (uint32_t) HcdAsyncHead(HostID) ) {
975  pQhd = (PHCD_QHD) Align32(pQhd->Horizontal.Link);
976  RemoveCompletedQTD(pQhd);
977  }
978 }
979 
980 static void PeriodScheduleIsr(uint8_t HostID)
981 {
982  uint32_t i;
983 
984  /*ISOCHRONOUS*/
985  for (i = 0; i < FRAME_LIST_SIZE; i++) { /*-- Foreach link in Period List Base --*/
986  NextLinkPointer *pNextPointer = &EHCI_FRAME_LIST(HostID)[i];
987 
988  /*-- Foreach Itd/SItd in the link--*/
989  while ( isValidLink(pNextPointer->Link) && pNextPointer->Type != QHD_TYPE ) {
990  if (pNextPointer->Type == ITD_TYPE) { /*-- Highspeed ISO --*/
991  PHCD_HS_ITD pItd = (PHCD_HS_ITD) Align32(pNextPointer->Link);
992 
993  if ((pItd->Transaction[0].Active == 0) && (pItd->Transaction[1].Active == 0) &&
994  ( pItd->Transaction[2].Active == 0) && ( pItd->Transaction[3].Active == 0) &&
995  ( pItd->Transaction[4].Active == 0) && ( pItd->Transaction[5].Active == 0) &&
996  ( pItd->Transaction[6].Active == 0) && ( pItd->Transaction[7].Active == 0) ) {
997  if ((pItd->Transaction[0].IntOnComplete == 1) || (pItd->Transaction[1].IntOnComplete == 1) ||
998  ( pItd->Transaction[2].IntOnComplete == 1) || ( pItd->Transaction[3].IntOnComplete == 1) ||
999  ( pItd->Transaction[4].IntOnComplete == 1) || ( pItd->Transaction[5].IntOnComplete == 1) ||
1000  ( pItd->Transaction[6].IntOnComplete == 1) || ( pItd->Transaction[7].IntOnComplete == 1) ) {
1001  /*-- request complete, signal on Iso Head --*/
1002  HcdQHD(HostID, pItd->IhdIdx)->status = HCD_STATUS_OK;
1003  }
1004  /*-- remove executed ITD --*/
1005  pNextPointer->Link = pItd->Horizontal.Link;
1006  FreeHsItd(pItd);
1007  continue; /*-- skip advance pNextPointer due to TD removal --*/
1008  }
1009  }
1010  else if (pNextPointer->Type == SITD_TYPE) { /*-- Split ISO --*/
1011  PHCD_SITD pSItd = (PHCD_SITD) Align32(pNextPointer->Link);
1012 
1013  if (pSItd->Active == 0) {
1014  if (pSItd->IntOnComplete) {
1015  /*-- request complete, signal on Iso Head --*/
1016  HcdQHD(HostID, pSItd->IhdIdx)->status = HCD_STATUS_OK;
1017  }
1018 
1019  /*-- removed executed SITD --*/
1020  pNextPointer->Link = pSItd->Horizontal.Link;
1021  FreeSItd(pSItd);
1022  continue; /*-- skip advance pNextPointer due to TD removal --*/
1023  }
1024  }
1025 
1026  pNextPointer = (NextLinkPointer *) Align32(pNextPointer->Link);
1027  }
1028  }
1029 
1030  /*INTERRUPT*/
1031  {
1032  PHCD_QHD pQhd = HcdIntHead(HostID);
1033 
1034  /*-- Foreach Qhd in list --*/
1035  while ( isValidLink(pQhd->Horizontal.Link) ) {
1036  pQhd = (PHCD_QHD) Align32(pQhd->Horizontal.Link);
1037  RemoveCompletedQTD(pQhd);
1038  }
1039  }
1040 }
1041 
1042 static void UsbErrorIsr(uint8_t HostID)
1043 {
1044  PHCD_QHD pQhd = HcdAsyncHead(HostID);
1045 
1046  /*-- Foreach Qhd in async list --*/
1047  while ( isValidLink(pQhd->Horizontal.Link) &&
1048  Align32(pQhd->Horizontal.Link) != (uint32_t) HcdAsyncHead(HostID) ) {
1049  pQhd = (PHCD_QHD) Align32(pQhd->Horizontal.Link);
1050  RemoveErrorQTD(pQhd);
1051  }
1052 }
1053 
1054 static HCD_STATUS PortStatusChangeIsr(uint8_t HostID, uint32_t deviceConnect)
1055 {
1056  if (deviceConnect) {/* Device Attached */
1057  USB_Host_Enumerate(HostID);
1058  }
1059  else { /* Device detached */
1060  USB_Host_DeEnumerate(HostID);
1061  }
1062  return HCD_STATUS_OK;
1063 }
1064 
1065 static void AsyncAdvanceIsr(uint8_t HostID)
1066 {
1067  uint32_t QhdIdx;
1068 
1069  for (QhdIdx = 0; QhdIdx < HCD_MAX_QHD; QhdIdx++)
1070  if ((HcdQHD(HostID, QhdIdx)->inUse == 1) && (HcdQHD(HostID, QhdIdx)->status == HCD_STATUS_TO_BE_REMOVED)) {
1071  FreeQhd(HostID, QhdIdx);
1072  }
1073 }
1074 
1075 static __INLINE HCD_STATUS EHciHostRun(uint8_t HostID)
1076 {
1077  USB_REG(HostID)->USBCMD_H |= EHC_USBCMD_RunStop;
1078  while (USB_REG(HostID)->USBSTS_H & EHC_USBSTS_HCHalted) {}
1079  return HCD_STATUS_OK;
1080 }
1081 
1082 static __INLINE HCD_STATUS EHciHostStop(uint8_t HostID)
1083 {
1084  USB_REG(HostID)->USBCMD_H &= ~EHC_USBCMD_RunStop;
1085  while ( !(USB_REG(HostID)->USBSTS_H & EHC_USBSTS_HCHalted) ) {}
1086  return HCD_STATUS_OK;
1087 }
1088 
1089 static __INLINE HCD_STATUS EHciHostReset(uint8_t HostID)
1090 {
1091  if (USB_REG(HostID)->USBSTS_H & EHC_USBSTS_HCHalted) {
1092  EHciHostStop(HostID);
1093  }
1094 
1095  USB_REG(HostID)->USBCMD_H |= EHC_USBCMD_HostReset;
1096  while ( USB_REG(HostID)->USBCMD_H & EHC_USBCMD_HostReset ) {}
1097 
1098  /* Program the controller to be the USB host controller, this can only be done after Reset */
1099  USB_REG(HostID)->USBMODE_H = 0x23; // USBMODE_HostController | USBMODE_VBusPowerSelect_High;
1100  return HCD_STATUS_OK;
1101 }
1102 
1103 static __INLINE HCD_STATUS EHciHostInit(uint8_t HostID)
1104 {
1105  uint32_t idx;
1106 
1107  /*---------- Host Data Structure Init ----------*/
1108  // memset(&ehci_data[HostID], 0, sizeof(EHCI_HOST_DATA_Type) );
1109 
1110  /*---------- USBINT ----------*/
1111  USB_REG(HostID)->USBINTR_H &= ~EHC_USBINTR_ALL; /* Disable All Interrupt */
1112  USB_REG(HostID)->USBSTS_H &= ~EHC_USBINTR_ALL; /* Clear All Interrupt Status */
1113  USB_REG(HostID)->USBINTR_H = EHC_USBINTR_UsbAsyncEnable | EHC_USBINTR_UsbPeriodEnable | /* Enable necessary interrupt source: Async Advance, System Error, Port Change, USB Error, USB Int */
1117 
1118  /*---------- Asynchronous List ----------*/
1119  /*-- Static Head Qhd with Halted/inactive --*/
1120  HcdAsyncHead(HostID)->Horizontal.Link = Align32( (uint32_t) HcdAsyncHead(HostID) );
1121  HcdAsyncHead(HostID)->Horizontal.Type = QHD_TYPE;
1122  HcdAsyncHead(HostID)->HeadReclamationFlag = 1;
1123  HcdAsyncHead(HostID)->Overlay.NextQtd = LINK_TERMINATE; /* Terminate Links */
1124  HcdAsyncHead(HostID)->Overlay.AlterNextQtd = LINK_TERMINATE; /* Terminate Links */
1125  HcdAsyncHead(HostID)->Overlay.Halted = 1;
1126 
1127  USB_REG(HostID)->ASYNCLISTADDR = (uint32_t) HcdAsyncHead(HostID);
1128 
1129  /*---------- Periodic List ----------*/
1130  /*-- Static Interrupt Qhd (1 ms) --*/
1131  HcdIntHead(HostID)->Horizontal.Link = LINK_TERMINATE;
1132  HcdIntHead(HostID)->Overlay.NextQtd = LINK_TERMINATE; /* Terminate Links */
1133  HcdIntHead(HostID)->Overlay.AlterNextQtd = LINK_TERMINATE; /* Terminate Links */
1134  HcdIntHead(HostID)->Overlay.Halted = 1;
1135  HcdIntHead(HostID)->uFrameSMask = 1;
1136 
1137  for (idx = 0; idx < FRAME_LIST_SIZE; idx++) { /* Attach 1 ms Interrupt Qhd to Period Frame List */
1138  EHCI_FRAME_LIST(HostID)[idx].Link = Align32( (uint32_t) HcdIntHead(HostID) );
1139  EHCI_FRAME_LIST(HostID)[idx].Type = QHD_TYPE;
1140  }
1141 
1142  USB_REG(HostID)->PERIODICLISTBASE = Align4k( (uint32_t) EHCI_FRAME_LIST(HostID) );
1143 
1144  /*---------- USBCMD ----------*/
1146  ((FRAMELIST_SIZE_BITS % 4) << 2) | ((FRAMELIST_SIZE_BITS / 4) << 15);
1147 
1148  /*---------- CONFIGFLAG ----------*/
1149  /* LPC18xx doesn't has CONFIGFLAG register */
1150 
1151  /*---------- Power On RhPort ----------*/
1152  USB_REG(HostID)->PORTSC1_H |= EHC_PORTSC_PortPowerControl;
1153 
1154  EHciHostRun(HostID);/* Run The HC */
1155 
1156  return HCD_STATUS_OK;
1157 }
1158 
1159 static __INLINE void DisableSchedule(uint8_t HostID, uint8_t isPeriod)
1160 {
1163 
1164  if (USB_REG(HostID)->USBSTS_H & statusMask) {
1165  USB_REG(HostID)->USBCMD_H &= ~cmdMask;
1166  while (USB_REG(HostID)->USBSTS_H & statusMask) {} /* TODO Should have time-out */
1167  }
1168 }
1169 
1170 static __INLINE void EnableSchedule(uint8_t HostID, uint8_t isPeriod)
1171 {
1174 
1175  if (!(USB_REG(HostID)->USBSTS_H & statusMask)) {
1176  USB_REG(HostID)->USBCMD_H |= cmdMask;
1177  while (!(USB_REG(HostID)->USBSTS_H & statusMask)) {}/* TODO Should have time-out */
1178  }
1179 }
1180 
1181 static void DisableAsyncSchedule(uint8_t HostID)
1182 {
1183  DisableSchedule(HostID, 0);
1184 }
1185 
1186 static void EnableAsyncSchedule(uint8_t HostID)
1187 {
1188  EnableSchedule(HostID, 0);
1189 }
1190 
1191 static void DisablePeriodSchedule(uint8_t HostID)
1192 {
1193  DisableSchedule(HostID, 1);
1194 }
1195 
1196 static void EnablePeriodSchedule(uint8_t HostID)
1197 {
1198  EnableSchedule(HostID, 1);
1199 }
1200 
1201 #endif // __LPC_EHCI__