M480 BSP  V3.05.001
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 
22 uint32_t g_flr_cnt; /* frame list rollover counter */
23 
24 ISO_EP_T *iso_ep_list; /* list of activated isochronous pipes */
25 
26 extern uint32_t _PFList[FL_SIZE]; /* Periodic frame list */
27 
28 static const uint16_t sitd_OUT_Smask [] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f };
29 
30 static 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  */
37 static 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  */
122 static 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  */
195 void 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 
329 static 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 
354 static 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 
367 static 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 
395 static __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 
418 int 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 < 2) /* 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 < 4) /* 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 < 8) /* 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 < 16) /* 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 < 32) /* 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 < 64) /* 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 
588 malloc_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 
599 static __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 
622 static 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 
670 static 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 
715 static 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");
725  return USBH_ERR_INVALID_PARAM;
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 
791 malloc_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  */
805 int 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. ***/
#define USBH_ERR_BABBLE_DETECTED
Definition: usbh_lib.h:53
#define USBH_ERR_TRANSACTION
Definition: usbh_lib.h:52
#define USBH_ERR_NOT_FOUND
Definition: usbh_lib.h:39
#define USBH_ERR_INVALID_PARAM
Definition: usbh_lib.h:38
#define HSUSBH_UFINDR_FI_Msk
Definition: hsusbh_reg.h:1095
#define USBH_ERR_DATA_BUFF
Definition: usbh_lib.h:54
#define USBH_ERR_TRANSFER
Definition: usbh_lib.h:45
NuMicro peripheral access layer header file.
#define USBH_ERR_NOT_ACCESS0
Definition: usbh_lib.h:68
USB Host hub class driver header file.
#define HSUSBH_UCMDR_PSEN_Msk
Definition: hsusbh_reg.h:1035
#define USBH_ERR_ABORT
Definition: usbh_lib.h:47
#define USBH_ERR_MEMORY_OUT
Definition: usbh_lib.h:32
UDEV_T * udev
Definition: usbh_uac.h:111
USB Host library header file.
#define NULL
NULL pointer.
Definition: M480.h:604