| LPCOpen Platform v1.03 LPCOpen Platform for NXP LPC Microcontrollers

- Main Page
- Modules
- Data Structures
- Files
- Related Pages

- File List
- Globals

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