M480 BSP V3.05.005
The Board Support Package for M480 Series
ehci_iso.c
Go to the documentation of this file.
1/**************************************************************************/
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13
14#include "NuMicro.h"
15
16#include "usb.h"
17#include "hub.h"
18
19
21
22uint32_t g_flr_cnt; /* frame list rollover counter */
23
24ISO_EP_T *iso_ep_list; /* list of activated isochronous pipes */
25
26extern uint32_t _PFList[FL_SIZE]; /* Periodic frame list */
27
28static const uint16_t sitd_OUT_Smask [] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f };
29
30static int ehci_iso_split_xfer(UTR_T *utr, ISO_EP_T *iso_ep);
31
32/*
33 * Inspect the iTD can be reclaimed or not. If yes, collect the transaction results.
34 * Return: 1 - reclaimed
35 * 0 - not completed
36 */
37static int review_itd(iTD_T *itd)
38{
39 UTR_T *utr;
40 uint32_t frnidx = itd->sched_frnidx;
41 uint32_t now_frame = (_ehci->UFINDR >> 3) & 0x3FF;
42 int i, fidx;
43
44 // printf("R - %d %d, 0x%x\n", now_frame, frnidx, itd->Transaction[0]);
45
46 if (now_frame == frnidx)
47 {
48 for (i = 0; i < 8; i++)
49 {
50 if (itd->Transaction[i] & ITD_STATUS_ACTIVE)
51 return 0; /* have any not completed frames */
52 }
53 }
54 else if (now_frame > frnidx)
55 {
56 if ((now_frame - frnidx) > EHCI_ISO_RCLM_RANGE)
57 return 0; /* don't touch it */
58 }
59 else
60 {
61 if (now_frame + FL_SIZE - frnidx > EHCI_ISO_RCLM_RANGE)
62 return 0; /* don't touch it */
63 }
64
65 /*
66 * Reclaim this iTD
67 */
68 utr = itd->utr;
69 fidx = itd->fidx;
70 for (i = 0; i < 8; i++)
71 {
72 if (!(itd->trans_mask & (0x1<<i)))
73 continue; /* not scheduled micro-frame */
74
75 if (ITD_STATUS(itd->Transaction[i]))
76 {
77 if (itd->Transaction[i] & ITD_STATUS_ACTIVE)
78 {
79 utr->iso_status[fidx] = USBH_ERR_NOT_ACCESS0;
80 utr->status = USBH_ERR_NOT_ACCESS0;
81 }
82 else if (itd->Transaction[i] & ITD_STATUS_BABBLE)
83 {
84 utr->iso_status[fidx] = USBH_ERR_BABBLE_DETECTED;
85 utr->status = USBH_ERR_TRANSFER;
86 }
87 else if (itd->Transaction[i] & ITD_STATUS_BUFF_ERR)
88 {
89 utr->iso_status[fidx] = USBH_ERR_DATA_BUFF;
90 utr->status = USBH_ERR_TRANSFER;
91 }
92 else
93 {
94 utr->iso_status[fidx] = USBH_ERR_TRANSACTION;
95 utr->status = USBH_ERR_TRANSFER;
96 }
97 }
98 else
99 {
100 utr->iso_status[fidx] = 0;
101 utr->iso_xlen[fidx] = ITD_XFER_LEN(itd->Transaction[i]);
102 }
103 fidx++;
104 }
105 utr->td_cnt--;
106
107 if (utr->td_cnt == 0) /* All iTD of this UTR done */
108 {
109 utr->bIsTransferDone = 1;
110 if (utr->func)
111 utr->func(utr);
112 }
113
114 return 1; /* to be reclaimed */
115}
116
117/*
118 * Inspect the siTD can be reclaimed or not. If yes, collect the transaction results.
119 * Return: 1 - reclaimed
120 * 0 - not completed
121 */
122static int review_sitd(siTD_T *sitd)
123{
124 UTR_T *utr;
125 uint32_t frnidx = sitd->sched_frnidx;
126 uint32_t now_frame = (_ehci->UFINDR >> 3) & 0x3FF;
127 int fidx;
128 uint32_t TotalBytesToTransfer;
129
130 if (now_frame == frnidx)
131 {
132 if (SITD_STATUS(sitd->StsCtrl) == SITD_STATUS_ACTIVE)
133 return 0;
134 }
135 else if (now_frame > frnidx)
136 {
137 if ((now_frame - frnidx) > EHCI_ISO_RCLM_RANGE)
138 return 0; /* don't touch it */
139 }
140 else
141 {
142 if (now_frame + FL_SIZE - frnidx > EHCI_ISO_RCLM_RANGE)
143 return 0; /* don't touch it */
144 }
145
146 /*
147 * Reclaim this siTD
148 */
149 utr = sitd->utr;
150 fidx = sitd->fidx;
151
152 if (SITD_STATUS(sitd->StsCtrl))
153 {
154 if (sitd->StsCtrl & SITD_STATUS_ACTIVE)
155 {
156 utr->iso_status[fidx] = USBH_ERR_NOT_ACCESS0;
157 }
158 else if (sitd->StsCtrl & SITD_BABBLE_DETECTED)
159 {
160 utr->iso_status[fidx] = USBH_ERR_BABBLE_DETECTED;
161 utr->status = USBH_ERR_TRANSFER;
162 }
163 else if (sitd->StsCtrl & SITD_STATUS_BUFF_ERR)
164 {
165 utr->iso_status[fidx] = USBH_ERR_DATA_BUFF;
166 utr->status = USBH_ERR_TRANSFER;
167 }
168 else
169 {
170 utr->iso_status[fidx] = USBH_ERR_TRANSACTION;
171 utr->status = USBH_ERR_TRANSFER;
172 }
173 }
174 else
175 {
176 TotalBytesToTransfer = (sitd->StsCtrl & SITD_XFER_CNT_Msk) >> SITD_XFER_CNT_Pos;
177 utr->iso_xlen[fidx] = utr->iso_xlen[fidx] - TotalBytesToTransfer;
178 utr->iso_status[fidx] = 0;
179 }
180 utr->td_cnt--;
181
182 if (utr->td_cnt == 0) /* All iTD of this UTR done */
183 {
184 utr->bIsTransferDone = 1;
185 if (utr->func)
186 utr->func(utr);
187 }
188 return 1; /* to be reclaimed */
189}
190
191/*
192 * Some iTD/siTD may be scheduled but not serviced due to time missed.
193 * This function scan several earlier frames and drop unserviced iTD/siTD if found.
194 */
195void scan_isochronous_list(void)
196{
197 ISO_EP_T *iso_ep = iso_ep_list;
198 iTD_T *itd, *itd_pre, *p;
199 siTD_T *sitd, *sitd_pre, *sp;
200 uint32_t frnidx;
201
202 DISABLE_EHCI_IRQ();
203
204 while (iso_ep != NULL) /* Search all activated iso endpoints */
205 {
206 /*--------------------------------------------------------------------------------*/
207 /* Scan all iTDs */
208 /*--------------------------------------------------------------------------------*/
209 itd = iso_ep->itd_list; /* get the first iTD from iso_ep's iTD list */
210 itd_pre = NULL;
211 while (itd != NULL) /* traverse all iTDs of itd list */
212 {
213 if (review_itd(itd)) /* inspect and reclaim iTD */
214 {
215 /*------------------------------------------------------------------------*/
216 /* Remove this iTD from period frame list */
217 /*------------------------------------------------------------------------*/
218 frnidx = itd->sched_frnidx;
219 if (_PFList[frnidx] == ITD_HLNK_ITD(itd))
220 {
221 /* is the first entry, just change to next */
222 _PFList[frnidx] = itd->Next_Link;
223 }
224 else
225 {
226 p = ITD_PTR(_PFList[frnidx]); /* find the preceding iTD */
227 while ((ITD_PTR(p->Next_Link) != itd) && (p != NULL))
228 {
229 p = ITD_PTR(p->Next_Link);
230 }
231
232 if (p == NULL) /* link list out of control! */
233 {
234 USB_error("An iTD lost refernece to periodic frame list! 0x%x -> %d\n", (int)itd, frnidx);
235 }
236 else /* remove iTD from list */
237 {
238 p->Next_Link = itd->Next_Link;
239 }
240 }
241
242 /*------------------------------------------------------------------------*/
243 /* Remove this iTD from iso_ep's iTD list */
244 /*------------------------------------------------------------------------*/
245 if (itd_pre == NULL)
246 {
247 iso_ep->itd_list = itd->next;
248 }
249 else
250 {
251 itd_pre->next = itd->next;
252 }
253 p = itd->next;
254 free_ehci_iTD(itd);
255 itd = p;
256 }
257 else
258 {
259 itd_pre = itd;
260 itd = itd->next; /* traverse to the next iTD of iTD list */
261 }
262 }
263
264 /*--------------------------------------------------------------------------------*/
265 /* Scan all siTDs */
266 /*--------------------------------------------------------------------------------*/
267 sitd = iso_ep->sitd_list; /* get the first siTD from iso_ep's siTD list */
268 sitd_pre = NULL;
269 while (sitd != NULL) /* traverse all siTDs of sitd list */
270 {
271 if (review_sitd(sitd)) /* inspect and reclaim siTD */
272 {
273 /*------------------------------------------------------------------------*/
274 /* Remove this siTD from period frame list */
275 /*------------------------------------------------------------------------*/
276 frnidx = sitd->sched_frnidx;
277 if (_PFList[frnidx] == SITD_HLNK_SITD(sitd))
278 {
279 /* is the first entry, just change to next */
280 _PFList[frnidx] = sitd->Next_Link;
281 }
282 else
283 {
284 sp = SITD_PTR(_PFList[frnidx]); /* find the preceding siTD */
285 while ((SITD_PTR(sp->Next_Link) != sitd) && (sp != NULL))
286 {
287 sp = SITD_PTR(sp->Next_Link);
288 }
289
290 if (sp == NULL) /* link list out of control! */
291 {
292 USB_error("An siTD lost reference to periodic frame list! 0x%x -> %d\n", (int)sitd, frnidx);
293 }
294 else /* remove iTD from list */
295 {
296 sp->Next_Link = sitd->Next_Link;
297 }
298 }
299
300 /*------------------------------------------------------------------------*/
301 /* Remove this siTD from iso_ep's siTD list */
302 /*------------------------------------------------------------------------*/
303 if (sitd_pre == NULL)
304 {
305 iso_ep->sitd_list = sitd->next;
306 }
307 else
308 {
309 sitd_pre->next = sitd->next;
310 }
311 sp = sitd->next;
312 free_ehci_siTD(sitd);
313 sitd = sp;
314 }
315 else
316 {
317 sitd_pre = sitd;
318 sitd = sitd->next; /* traverse to the next siTD of siTD list */
319 }
320 }
321
322 iso_ep = iso_ep->next;
323 }
324
325 ENABLE_EHCI_IRQ();
326}
327
328
329static void write_itd_info(UTR_T *utr, iTD_T *itd)
330{
331 UDEV_T *udev = utr->udev;
332 EP_INFO_T *ep = utr->ep; /* reference to isochronous endpoint */
333 uint32_t buff_page_addr;
334 int i;
335
336 buff_page_addr = itd->buff_base & 0xFFFFF000; /* 4K page */
337
338 for (i = 0; i < 7; i++)
339 {
340 itd->Bptr[i] = buff_page_addr + (0x1000 * i);
341 }
342 /* EndPtr R Device Address */
343 itd->Bptr[0] |= (udev->dev_num) | ((ep->bEndpointAddress & 0xF) << ITD_EP_NUM_Pos);
344 itd->Bptr[1] |= ep->wMaxPacketSize; /* Maximum Packet Size */
345
346 if ((ep->bEndpointAddress & EP_ADDR_DIR_MASK) == EP_ADDR_DIR_IN) /* I/O */
347 itd->Bptr[1] |= ITD_DIR_IN;
348 else
349 itd->Bptr[1] |= ITD_DIR_OUT;
350
351 itd->Bptr[2] |= (ep->wMaxPacketSize + 1023)/1024; /* Mult */
352}
353
354static void write_itd_micro_frame(UTR_T *utr, int fidx, iTD_T *itd, int mf)
355{
356 uint32_t buff_addr;
357
358 buff_addr = (uint32_t)(utr->iso_buff[fidx]); /* xfer buffer start address of this frame */
359
360 itd->Transaction[mf] = ITD_STATUS_ACTIVE | /* Status */
361 ((utr->iso_xlen[fidx] & 0xFFF) << ITD_XLEN_Pos) | /* Transaction Length */
362 ((buff_addr & 0xFFFFF000) - (itd->buff_base & 0xFFFFF000)) | /* PG */
363 (buff_addr & 0xFFF); /* Transaction offset */
364}
365
366
367static void remove_iso_ep_from_list(ISO_EP_T *iso_ep)
368{
369 ISO_EP_T *p;
370
371 if (iso_ep_list == iso_ep)
372 {
373 iso_ep_list = iso_ep->next; /* it's the first entry, remove it */
374 return;
375 }
376
377 p = iso_ep_list; /* find the previous entry of iso_ep */
378 while (p->next != NULL)
379 {
380 if (p->next == iso_ep)
381 {
382 break;
383 }
384 p = p->next;
385 }
386
387 if (p->next == NULL)
388 {
389 return; /* not found */
390 }
391 p->next = iso_ep->next; /* remove iso_ep from list */
392}
393
394
395static __inline void add_itd_to_iso_ep(ISO_EP_T *iso_ep, iTD_T *itd)
396{
397 iTD_T *p;
398
399 itd->next = NULL;
400
401 if (iso_ep->itd_list == NULL)
402 {
403 iso_ep->itd_list = itd;
404 return;
405 }
406
407 /*
408 * Find the tail entry of iso_ep->itd_list
409 */
410 p = iso_ep->itd_list;
411 while (p->next != NULL)
412 {
413 p = p->next;
414 }
415 p->next = itd;
416}
417
418int ehci_iso_xfer(UTR_T *utr)
419{
420 EP_INFO_T *ep = utr->ep; /* reference to isochronous endpoint */
421 ISO_EP_T *iso_ep; /* software iso endpoint descriptor */
422 iTD_T *itd, *itd_next, *itd_list = NULL;
423 int i, itd_cnt;
424 int trans_mask; /* bit mask of used xfer in an iTD */
425 int fidx; /* index to the 8 iso frames of UTR */
426 int interval; /* frame interval of iTD */
427
428 if (ep->hw_pipe != NULL)
429 {
430 iso_ep = (ISO_EP_T *)ep->hw_pipe; /* get reference of the isochronous endpoint */
431
432 if (utr->bIsoNewSched)
433 iso_ep->next_frame = (((_ehci->UFINDR + (EHCI_ISO_DELAY * 8)) & HSUSBH_UFINDR_FI_Msk) >> 3) & 0x3FF;
434 }
435 else
436 {
437 /* first time transfer of this iso endpoint */
438 iso_ep = usbh_alloc_mem(sizeof(*iso_ep));
439 if (iso_ep == NULL)
440 return USBH_ERR_MEMORY_OUT;
441
442 memset(iso_ep, 0, sizeof(*iso_ep));
443 iso_ep->ep = ep;
444 iso_ep->next_frame = (((_ehci->UFINDR + (EHCI_ISO_DELAY * 8)) & HSUSBH_UFINDR_FI_Msk) >> 3) & 0x3FF;
445
446 ep->hw_pipe = iso_ep;
447
448 /*
449 * Add this iso_ep into iso_ep_list
450 */
451 DISABLE_EHCI_IRQ();
452 iso_ep->next = iso_ep_list;
453 iso_ep_list = iso_ep;
454 ENABLE_EHCI_IRQ();
455 }
456
457 if (utr->udev->speed == SPEED_FULL)
458 return ehci_iso_split_xfer(utr, iso_ep);
459
460 /*------------------------------------------------------------------------------------*/
461 /* Allocate iTDs */
462 /*------------------------------------------------------------------------------------*/
463
464 if (ep->bInterval <= 1) /* transfer interval is 1 micro-frame */
465 {
466 trans_mask = 0xFF;
467 itd_cnt = 1; /* required 1 iTD for one UTR */
468 interval = 1; /* iTD frame interval of this endpoint */
469 }
470 else if (ep->bInterval == 2) /* transfer interval is 2 micro-frames */
471 {
472 trans_mask = 0x55;
473 itd_cnt = 2; /* required 2 iTDs for one UTR */
474 interval = 1; /* iTD frame interval of this endpoint */
475 }
476 else if (ep->bInterval == 3) /* transfer interval is 4 micro-frames */
477 {
478 trans_mask = 0x44;
479 itd_cnt = 4; /* required 4 iTDs for one UTR */
480 interval = 1; /* iTD frame interval of this endpoint */
481 }
482 else if (ep->bInterval == 4) /* transfer interval is 8 micro-frames */
483 {
484 trans_mask = 0x08; /* there's 1 transfer in one iTD */
485 itd_cnt = 8; /* required 8 iTDs for one UTR */
486 interval = 1; /* iTD frame interval of this endpoint */
487 }
488 else if (ep->bInterval == 5) /* transfer interval is 16 micro-frames */
489 {
490 trans_mask = 0x10; /* there's 1 transfer in one iTD */
491 itd_cnt = 8; /* required 8 iTDs for one UTR */
492 interval = 2; /* iTD frame interval of this endpoint */
493 }
494 else if (ep->bInterval == 6) /* transfer interval is 32 micro-frames */
495 {
496 trans_mask = 0x02; /* there's 1 transfer in one iTD */
497 itd_cnt = 8; /* required 8 iTDs for one UTR */
498 interval = 4; /* iTD frame interval of this endpoint */
499 }
500 else /* transfer interval is 64 micro-frames */
501 {
502 trans_mask = 0x04; /* there's 1 transfer in one iTD */
503 itd_cnt = 8; /* required 8 iTDs for one UTR */
504 interval = 8; /* iTD frame interval of this endpoint */
505 }
506
507 for (i = 0; i < itd_cnt; i++) /* allocate all iTDs required by UTR */
508 {
509 itd = alloc_ehci_iTD();
510 if (itd == NULL)
511 goto malloc_failed;
512
513 if (itd_list == NULL) /* link all iTDs */
514 {
515 itd_list = itd;
516 }
517 else
518 {
519 itd->next = itd_list;
520 itd_list = itd;
521 }
522 }
523
524 utr->td_cnt = itd_cnt;
525
526 /*------------------------------------------------------------------------------------*/
527 /* Fill and link all iTDs */
528 /*------------------------------------------------------------------------------------*/
529
530 utr->iso_sf = iso_ep->next_frame;
531 fidx = 0; /* index to UTR iso frmes (total IF_PER_UTR) */
532
533 for (itd = itd_list; (itd != NULL); )
534 {
535 if (fidx >= IF_PER_UTR) /* unlikely */
536 {
537 USB_error("EHCI driver ITD bug!?\n");
538 goto malloc_failed;
539 }
540
541 itd->utr = utr;
542 itd->fidx = fidx; /* index to UTR's n'th IF_PER_UTR frame */
543 itd->buff_base = (uint32_t)(utr->iso_buff[fidx]); /* iTD buffer base is buffer of the first UTR iso frame serviced by this iTD */
544 itd->trans_mask = trans_mask;
545
546 write_itd_info(utr, itd);
547
548 for (i = 0; i < 8; i++) /* settle xfer into micro-frames */
549 {
550 if (!(trans_mask & (0x1<<i)))
551 {
552 itd->Transaction[i] = 0; /* not accesed */
553 continue; /* not scheduled micro-frame */
554 }
555
556 write_itd_micro_frame(utr, fidx, itd, i);
557
558 fidx++; /* preceed to next UTR iso frame */
559
560 if (fidx == IF_PER_UTR) /* is the last scheduled micro-frame? */
561 {
562 /* raise interrupt on completed */
563 itd->Transaction[i] |= ITD_IOC;
564 break;
565 }
566 }
567
568 itd_next = itd->next; /* remember the next itd */
569
570 // USB_debug("Link iTD 0x%x, %d\n", (int)itd, iso_ep->next_frame);
571 /*
572 * Link iTD to period frame list
573 */
574 DISABLE_EHCI_IRQ();
575 itd->sched_frnidx = iso_ep->next_frame; /* remember it for reclamation scan */
576 add_itd_to_iso_ep(iso_ep, itd); /* add to software itd list */
577 itd->Next_Link = _PFList[itd->sched_frnidx]; /* keep the next link */
578 _PFList[itd->sched_frnidx] = ITD_HLNK_ITD(itd);
579 iso_ep->next_frame = (iso_ep->next_frame + interval) % FL_SIZE;
580 ENABLE_EHCI_IRQ();
581
582 itd = itd_next;
583 }
584
585 _ehci->UCMDR |= HSUSBH_UCMDR_PSEN_Msk; /* periodic list enable */
586 return 0;
587
588malloc_failed:
589
590 while (itd_list != NULL)
591 {
592 itd = itd_list;
593 itd_list = itd->next;
594 free_ehci_iTD(itd);
595 }
596 return USBH_ERR_MEMORY_OUT;
597}
598
599static __inline void add_sitd_to_iso_ep(ISO_EP_T *iso_ep, siTD_T *sitd)
600{
601 siTD_T *p;
602
603 sitd->next = NULL;
604
605 if (iso_ep->sitd_list == NULL)
606 {
607 iso_ep->sitd_list = sitd;
608 return;
609 }
610
611 /*
612 * Find the tail entry of iso_ep->itd_list
613 */
614 p = iso_ep->sitd_list;
615 while (p->next != NULL)
616 {
617 p = p->next;
618 }
619 p->next = sitd;
620}
621
622static void write_sitd_info(UTR_T *utr, siTD_T *sitd)
623{
624 UDEV_T *udev = utr->udev;
625 EP_INFO_T *ep = utr->ep; /* reference to isochronous endpoint */
626 uint32_t buff_page_addr;
627 int xlen = utr->iso_xlen[sitd->fidx];
628 int scnt;
629
630 sitd->Chrst = (udev->port_num << SITD_PORT_NUM_Pos) |
631 (udev->parent->iface->udev->dev_num << SITD_HUB_ADDR_Pos) |
632 ((ep->bEndpointAddress & 0xF) << SITD_EP_NUM_Pos) |
633 (udev->dev_num << SITD_DEV_ADDR_Pos);
634
635 buff_page_addr = ((uint32_t)utr->iso_buff[sitd->fidx]) & 0xFFFFF000;
636 sitd->Bptr[0] = (uint32_t)(utr->iso_buff[sitd->fidx]);
637 sitd->Bptr[1] = buff_page_addr + 0x1000;
638
639 scnt = (xlen + 187) / 188;
640
641 if ((ep->bEndpointAddress & EP_ADDR_DIR_MASK) == EP_ADDR_DIR_IN) /* I/O */
642 {
643 sitd->Chrst |= SITD_XFER_IN;
644 sitd->Sched = (1 << (scnt + 2)) - 1;
645 sitd->Sched = (sitd->Sched << 10) | 0x1;
646 //sitd->Sched <<= 1;
647 }
648 else
649 {
650 sitd->Chrst |= SITD_XFER_OUT;
651 sitd->Sched = sitd_OUT_Smask[scnt-1];
652 if (scnt > 1)
653 {
654 sitd->Bptr[1] |= (0x1 << 3); /* Transaction position (TP) 01b: Begin */
655 }
656 sitd->Bptr[1] |= scnt; /* Transaction count (T-Count) */
657 }
658
659 if (sitd->fidx == IF_PER_UTR)
660 {
661 sitd->Sched |= SITD_IOC;
662 }
663
664 sitd->StsCtrl = (xlen << SITD_XFER_CNT_Pos) | SITD_STATUS_ACTIVE;
665
666 sitd->BackLink = SITD_LIST_END;
667}
668
669
670static void ehci_sitd_adjust_schedule(siTD_T *sitd)
671{
672 siTD_T *hlink = (siTD_T *)_PFList[sitd->sched_frnidx];
673 uint32_t uframe_mask = 0x00;
674
675 while (hlink && !HLINK_IS_TERMINATED(hlink) && HLINK_IS_SITD(hlink))
676 {
677 hlink = SITD_PTR(hlink);
678 if (hlink != sitd)
679 {
680 if ((hlink->Chrst & SITD_XFER_IO_Msk) == SITD_XFER_IN)
681 {
682 uframe_mask |= (hlink->Sched & 0xFF); /* mark micro-frames used by IN S-mask */
683 uframe_mask |= ((hlink->Sched >> 8) & 0xFF); /* mark micro-frames used by IN C-mask */
684 }
685 else
686 {
687 uframe_mask |= (hlink->Sched & 0xFF); /* mark micro-frames used by OUT S-mask */
688 }
689 }
690 hlink = SITD_PTR(hlink->Next_Link);
691 }
692
693 uframe_mask = uframe_mask | (uframe_mask << 8); /* mark both S-mask and C-mask */
694
695 if (uframe_mask)
696 {
697 /*
698 * Shift afterward one micro-frame until no conflicts.
699 */
700 while (1)
701 {
702 if (sitd->Sched & uframe_mask)
703 {
704 sitd->Sched = (sitd->Sched & 0xFFFF0000) | ((sitd->Sched << 1) & 0xFFFF);
705 }
706 else
707 {
708 break; /* no conflit, done. */
709 }
710 }
711 }
712}
713
714
715static int ehci_iso_split_xfer(UTR_T *utr, ISO_EP_T *iso_ep)
716{
717 EP_INFO_T *ep = utr->ep; /* reference to isochronous endpoint */
718 siTD_T *sitd, *sitd_next, *sitd_list = NULL;
719 int i;
720 int fidx; /* index to the 8 iso frames of UTR */
721
722 if (utr->udev->parent == NULL)
723 {
724 USB_error("siso xfer - parent lost!\n");
726 }
727
728 /*------------------------------------------------------------------------------------*/
729 /* Allocate siTDs */
730 /*------------------------------------------------------------------------------------*/
731 for (i = 0; i < IF_PER_UTR; i++) /* allocate all siTDs required by UTR */
732 {
733 sitd = alloc_ehci_siTD();
734 if (sitd == NULL)
735 goto malloc_failed;
736
737 if (sitd_list == NULL) /* link all siTDs */
738 {
739 sitd_list = sitd;
740 }
741 else
742 {
743 sitd->next = sitd_list;
744 sitd_list = sitd;
745 }
746 }
747
748 utr->td_cnt = IF_PER_UTR;
749
750 /*------------------------------------------------------------------------------------*/
751 /* Fill and link all siTDs */
752 /*------------------------------------------------------------------------------------*/
753
754 utr->iso_sf = iso_ep->next_frame;
755 fidx = 0; /* index to UTR iso frmes (total IF_PER_UTR) */
756
757 for (sitd = sitd_list; (sitd != NULL); fidx++)
758 {
759 if (fidx >= IF_PER_UTR) /* unlikely */
760 {
761 USB_error("EHCI driver siTD bug!?\n");
762 goto malloc_failed;
763 }
764
765 sitd->utr = utr;
766 sitd->fidx = fidx; /* index to UTR's n'th IF_PER_UTR frame */
767
768 write_sitd_info(utr, sitd);
769
770 sitd_next = sitd->next; /* remember the next itd */
771
772 // USB_debug("Link iTD 0x%x, %d\n", (int)itd, iso_ep->next_frame);
773 /*
774 * Link iTD to period frame list
775 */
776 sitd->sched_frnidx = iso_ep->next_frame; /* remember it for reclamation scan */
777 DISABLE_EHCI_IRQ();
778 ehci_sitd_adjust_schedule(sitd);
779 add_sitd_to_iso_ep(iso_ep, sitd); /* add to software itd list */
780 sitd->Next_Link = _PFList[sitd->sched_frnidx];/* keep the next link */
781 _PFList[sitd->sched_frnidx] = SITD_HLNK_SITD(sitd);
782 iso_ep->next_frame = (iso_ep->next_frame + ep->bInterval) % FL_SIZE;
783 ENABLE_EHCI_IRQ();
784
785 sitd = sitd_next;
786 }
787
788 _ehci->UCMDR |= HSUSBH_UCMDR_PSEN_Msk; /* periodic list enable */
789 return 0;
790
791malloc_failed:
792
793 while (sitd_list != NULL)
794 {
795 sitd = sitd_list;
796 sitd_list = sitd->next;
797 free_ehci_siTD(sitd);
798 }
799 return USBH_ERR_MEMORY_OUT;
800}
801
802/*
803 * If it's an isochronous endpoint, quit current transfer via UTR or hardware EP.
804 */
805int ehci_quit_iso_xfer(UTR_T *utr, EP_INFO_T *ep)
806{
807 ISO_EP_T *iso_ep;
808 iTD_T *itd, *itd_next, *p;
809 uint32_t frnidx;
810 uint32_t now_frame;
811
812 if (ep == NULL)
813 {
814 if (utr == NULL)
815 return USBH_ERR_NOT_FOUND;
816
817 if (utr->ep == NULL)
818 return USBH_ERR_NOT_FOUND;
819
820 ep = utr->ep;
821 }
822
823 if ((ep->bmAttributes & EP_ATTR_TT_MASK) != EP_ATTR_TT_ISO)
824 return USBH_ERR_NOT_FOUND; /* not isochronous endpoint */
825
826 /*------------------------------------------------------------------------------------*/
827 /* It's an iso endpoint. Remove it as required. */
828 /*------------------------------------------------------------------------------------*/
829 iso_ep = iso_ep_list;
830 while (iso_ep != NULL) /* Search all activated iso endpoints */
831 {
832 if (iso_ep->ep == ep)
833 break;
834 iso_ep = iso_ep->next;
835 }
836 if (iso_ep == NULL)
837 return 0; /* should have been removed */
838
839 itd = iso_ep->itd_list; /* get the first iTD from iso_ep's iTD list */
840
841 while (itd != NULL) /* traverse all iTDs of itd list */
842 {
843 itd_next = itd->next; /* remember the next iTD */
844 utr = itd->utr;
845
846 /*--------------------------------------------------------------------------------*/
847 /* Remove this iTD from period frame list */
848 /*--------------------------------------------------------------------------------*/
849 frnidx = itd->sched_frnidx;
850
851 /*
852 * Prevent to race with Host Controller. If the iTD to be removed is located in
853 * current or next frame, wait until HC passed through it.
854 */
855 while (1)
856 {
857 now_frame = (_ehci->UFINDR >> 3) & 0x3FF;
858 if ((now_frame == frnidx) || (((now_frame+1)%1024) == frnidx))
859 continue;
860 break;
861 }
862
863 if (_PFList[frnidx] == ITD_HLNK_ITD(itd))
864 {
865 /* is the first entry, just change to next */
866 _PFList[frnidx] = itd->Next_Link;
867 }
868 else
869 {
870 p = ITD_PTR(_PFList[frnidx]); /* find the preceding iTD */
871 while ((ITD_PTR(p->Next_Link) != itd) && (p != NULL))
872 {
873 p = ITD_PTR(p->Next_Link);
874 }
875
876 if (p == NULL) /* link list out of control! */
877 {
878 USB_error("ehci_quit_iso_xfer - An iTD lost reference to periodic frame list! 0x%x on %d\n", (int)itd, frnidx);
879 }
880 else /* remove iTD from list */
881 {
882 p->Next_Link = itd->Next_Link;
883 }
884 }
885
886 utr->td_cnt--;
887
888 if (utr->td_cnt == 0) /* All iTD of this UTR done */
889 {
890 utr->bIsTransferDone = 1;
891 if (utr->func)
892 utr->func(utr);
893 utr->status = USBH_ERR_ABORT;
894 }
895 free_ehci_iTD(itd);
896 itd = itd_next;
897 }
898
899 /*
900 * Remove iso_ep from iso_ep_list
901 */
902 remove_iso_ep_from_list(iso_ep);
903 usbh_free_mem(iso_ep, sizeof(*iso_ep)); /* free this iso_ep */
904 ep->hw_pipe = NULL;
905
906 if (iso_ep_list == NULL)
907 _ehci->UCMDR &= ~HSUSBH_UCMDR_PSEN_Msk;
908
909 return 0;
910}
911
912
914
915/*** (C) COPYRIGHT 2017 Nuvoton Technology Corp. ***/
NuMicro peripheral access layer header file.
#define NULL
NULL pointer.
Definition: M480.h:605
#define HSUSBH_UFINDR_FI_Msk
Definition: hsusbh_reg.h:1095
#define HSUSBH_UCMDR_PSEN_Msk
Definition: hsusbh_reg.h:1035
#define USBH_ERR_NOT_ACCESS0
Definition: usbh_lib.h:68
#define USBH_ERR_TRANSFER
Definition: usbh_lib.h:45
#define USBH_ERR_BABBLE_DETECTED
Definition: usbh_lib.h:53
#define USBH_ERR_INVALID_PARAM
Definition: usbh_lib.h:38
#define USBH_ERR_TRANSACTION
Definition: usbh_lib.h:52
#define USBH_ERR_ABORT
Definition: usbh_lib.h:47
#define USBH_ERR_NOT_FOUND
Definition: usbh_lib.h:39
#define USBH_ERR_DATA_BUFF
Definition: usbh_lib.h:54
#define USBH_ERR_MEMORY_OUT
Definition: usbh_lib.h:32
USB Host hub class driver header file.
USB Host library header file.