| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916 |
- /**************************************************************************//**
- * @file ehci_iso.c
- * @version V1.10
- * @brief USB EHCI isochronous transfer driver.
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * @copyright (C) 2017 Nuvoton Technology Corp. All rights reserved.
- *****************************************************************************/
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include "NuMicro.h"
- #include "usb.h"
- #include "hub.h"
- /// @cond HIDDEN_SYMBOLS
- uint32_t g_flr_cnt; /* frame list rollover counter */
- ISO_EP_T *iso_ep_list; /* list of activated isochronous pipes */
- extern uint32_t _PFList[FL_SIZE]; /* Periodic frame list */
- static const uint16_t sitd_OUT_Smask [] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f };
- static int ehci_iso_split_xfer(UTR_T *utr, ISO_EP_T *iso_ep);
- /*
- * Inspect the iTD can be reclaimed or not. If yes, collect the transaction results.
- * Return: 1 - reclaimed
- * 0 - not completed
- */
- static int review_itd(iTD_T *itd)
- {
- UTR_T *utr;
- uint32_t frnidx = itd->sched_frnidx;
- uint32_t now_frame = (_ehci->UFINDR >> 3) & 0x3FF;
- int i, fidx;
- // printf("R - %d %d, 0x%x\n", now_frame, frnidx, itd->Transaction[0]);
- if (now_frame == frnidx)
- {
- for (i = 0; i < 8; i++)
- {
- if (itd->Transaction[i] & ITD_STATUS_ACTIVE)
- return 0; /* have any not completed frames */
- }
- }
- else if (now_frame > frnidx)
- {
- if ((now_frame - frnidx) > EHCI_ISO_RCLM_RANGE)
- return 0; /* don't touch it */
- }
- else
- {
- if (now_frame + FL_SIZE - frnidx > EHCI_ISO_RCLM_RANGE)
- return 0; /* don't touch it */
- }
- /*
- * Reclaim this iTD
- */
- utr = itd->utr;
- fidx = itd->fidx;
- for (i = 0; i < 8; i++)
- {
- if (!(itd->trans_mask & (0x1<<i)))
- continue; /* not scheduled micro-frame */
- if (ITD_STATUS(itd->Transaction[i]))
- {
- if (itd->Transaction[i] & ITD_STATUS_ACTIVE)
- {
- utr->iso_status[fidx] = USBH_ERR_NOT_ACCESS0;
- utr->status = USBH_ERR_NOT_ACCESS0;
- }
- else if (itd->Transaction[i] & ITD_STATUS_BABBLE)
- {
- utr->iso_status[fidx] = USBH_ERR_BABBLE_DETECTED;
- utr->status = USBH_ERR_TRANSFER;
- }
- else if (itd->Transaction[i] & ITD_STATUS_BUFF_ERR)
- {
- utr->iso_status[fidx] = USBH_ERR_DATA_BUFF;
- utr->status = USBH_ERR_TRANSFER;
- }
- else
- {
- utr->iso_status[fidx] = USBH_ERR_TRANSACTION;
- utr->status = USBH_ERR_TRANSFER;
- }
- }
- else
- {
- utr->iso_status[fidx] = 0;
- utr->iso_xlen[fidx] = ITD_XFER_LEN(itd->Transaction[i]);
- }
- fidx++;
- }
- utr->td_cnt--;
- if (utr->td_cnt == 0) /* All iTD of this UTR done */
- {
- utr->bIsTransferDone = 1;
- if (utr->func)
- utr->func(utr);
- }
- return 1; /* to be reclaimed */
- }
- /*
- * Inspect the siTD can be reclaimed or not. If yes, collect the transaction results.
- * Return: 1 - reclaimed
- * 0 - not completed
- */
- static int review_sitd(siTD_T *sitd)
- {
- UTR_T *utr;
- uint32_t frnidx = sitd->sched_frnidx;
- uint32_t now_frame = (_ehci->UFINDR >> 3) & 0x3FF;
- int fidx;
- uint32_t TotalBytesToTransfer;
- if (now_frame == frnidx)
- {
- if (SITD_STATUS(sitd->StsCtrl) == SITD_STATUS_ACTIVE)
- return 0;
- }
- else if (now_frame > frnidx)
- {
- if ((now_frame - frnidx) > EHCI_ISO_RCLM_RANGE)
- return 0; /* don't touch it */
- }
- else
- {
- if (now_frame + FL_SIZE - frnidx > EHCI_ISO_RCLM_RANGE)
- return 0; /* don't touch it */
- }
- /*
- * Reclaim this siTD
- */
- utr = sitd->utr;
- fidx = sitd->fidx;
- if (SITD_STATUS(sitd->StsCtrl))
- {
- if (sitd->StsCtrl & SITD_STATUS_ACTIVE)
- {
- utr->iso_status[fidx] = USBH_ERR_NOT_ACCESS0;
- }
- else if (sitd->StsCtrl & SITD_BABBLE_DETECTED)
- {
- utr->iso_status[fidx] = USBH_ERR_BABBLE_DETECTED;
- utr->status = USBH_ERR_TRANSFER;
- }
- else if (sitd->StsCtrl & SITD_STATUS_BUFF_ERR)
- {
- utr->iso_status[fidx] = USBH_ERR_DATA_BUFF;
- utr->status = USBH_ERR_TRANSFER;
- }
- else
- {
- utr->iso_status[fidx] = USBH_ERR_TRANSACTION;
- utr->status = USBH_ERR_TRANSFER;
- }
- }
- else
- {
- TotalBytesToTransfer = (sitd->StsCtrl & SITD_XFER_CNT_Msk) >> SITD_XFER_CNT_Pos;
- utr->iso_xlen[fidx] = utr->iso_xlen[fidx] - TotalBytesToTransfer;
- utr->iso_status[fidx] = 0;
- }
- utr->td_cnt--;
- if (utr->td_cnt == 0) /* All iTD of this UTR done */
- {
- utr->bIsTransferDone = 1;
- if (utr->func)
- utr->func(utr);
- }
- return 1; /* to be reclaimed */
- }
- /*
- * Some iTD/siTD may be scheduled but not serviced due to time missed.
- * This function scan several earlier frames and drop unserviced iTD/siTD if found.
- */
- void scan_isochronous_list(void)
- {
- ISO_EP_T *iso_ep = iso_ep_list;
- iTD_T *itd, *itd_pre, *p;
- siTD_T *sitd, *sitd_pre, *sp;
- uint32_t frnidx;
- DISABLE_EHCI_IRQ();
- while (iso_ep != NULL) /* Search all activated iso endpoints */
- {
- /*--------------------------------------------------------------------------------*/
- /* Scan all iTDs */
- /*--------------------------------------------------------------------------------*/
- itd = iso_ep->itd_list; /* get the first iTD from iso_ep's iTD list */
- itd_pre = NULL;
- while (itd != NULL) /* traverse all iTDs of itd list */
- {
- if (review_itd(itd)) /* inspect and reclaim iTD */
- {
- /*------------------------------------------------------------------------*/
- /* Remove this iTD from period frame list */
- /*------------------------------------------------------------------------*/
- frnidx = itd->sched_frnidx;
- if (_PFList[frnidx] == ITD_HLNK_ITD(itd))
- {
- /* is the first entry, just change to next */
- _PFList[frnidx] = itd->Next_Link;
- }
- else
- {
- p = ITD_PTR(_PFList[frnidx]); /* find the preceding iTD */
- while ((ITD_PTR(p->Next_Link) != itd) && (p != NULL))
- {
- p = ITD_PTR(p->Next_Link);
- }
- if (p == NULL) /* link list out of control! */
- {
- USB_error("An iTD lost refernece to periodic frame list! 0x%x -> %d\n", (int)itd, frnidx);
- }
- else /* remove iTD from list */
- {
- p->Next_Link = itd->Next_Link;
- }
- }
- /*------------------------------------------------------------------------*/
- /* Remove this iTD from iso_ep's iTD list */
- /*------------------------------------------------------------------------*/
- if (itd_pre == NULL)
- {
- iso_ep->itd_list = itd->next;
- }
- else
- {
- itd_pre->next = itd->next;
- }
- p = itd->next;
- free_ehci_iTD(itd);
- itd = p;
- }
- else
- {
- itd_pre = itd;
- itd = itd->next; /* traverse to the next iTD of iTD list */
- }
- }
- /*--------------------------------------------------------------------------------*/
- /* Scan all siTDs */
- /*--------------------------------------------------------------------------------*/
- sitd = iso_ep->sitd_list; /* get the first siTD from iso_ep's siTD list */
- sitd_pre = NULL;
- while (sitd != NULL) /* traverse all siTDs of sitd list */
- {
- if (review_sitd(sitd)) /* inspect and reclaim siTD */
- {
- /*------------------------------------------------------------------------*/
- /* Remove this siTD from period frame list */
- /*------------------------------------------------------------------------*/
- frnidx = sitd->sched_frnidx;
- if (_PFList[frnidx] == SITD_HLNK_SITD(sitd))
- {
- /* is the first entry, just change to next */
- _PFList[frnidx] = sitd->Next_Link;
- }
- else
- {
- sp = SITD_PTR(_PFList[frnidx]); /* find the preceding siTD */
- while ((SITD_PTR(sp->Next_Link) != sitd) && (sp != NULL))
- {
- sp = SITD_PTR(sp->Next_Link);
- }
- if (sp == NULL) /* link list out of control! */
- {
- USB_error("An siTD lost reference to periodic frame list! 0x%x -> %d\n", (int)sitd, frnidx);
- }
- else /* remove iTD from list */
- {
- sp->Next_Link = sitd->Next_Link;
- }
- }
- /*------------------------------------------------------------------------*/
- /* Remove this siTD from iso_ep's siTD list */
- /*------------------------------------------------------------------------*/
- if (sitd_pre == NULL)
- {
- iso_ep->sitd_list = sitd->next;
- }
- else
- {
- sitd_pre->next = sitd->next;
- }
- sp = sitd->next;
- free_ehci_siTD(sitd);
- sitd = sp;
- }
- else
- {
- sitd_pre = sitd;
- sitd = sitd->next; /* traverse to the next siTD of siTD list */
- }
- }
- iso_ep = iso_ep->next;
- }
- ENABLE_EHCI_IRQ();
- }
- static void write_itd_info(UTR_T *utr, iTD_T *itd)
- {
- UDEV_T *udev = utr->udev;
- EP_INFO_T *ep = utr->ep; /* reference to isochronous endpoint */
- uint32_t buff_page_addr;
- int i;
- buff_page_addr = itd->buff_base & 0xFFFFF000; /* 4K page */
- for (i = 0; i < 7; i++)
- {
- itd->Bptr[i] = buff_page_addr + (0x1000 * i);
- }
- /* EndPtr R Device Address */
- itd->Bptr[0] |= (udev->dev_num) | ((ep->bEndpointAddress & 0xF) << ITD_EP_NUM_Pos);
- itd->Bptr[1] |= ep->wMaxPacketSize; /* Maximum Packet Size */
- if ((ep->bEndpointAddress & EP_ADDR_DIR_MASK) == EP_ADDR_DIR_IN) /* I/O */
- itd->Bptr[1] |= ITD_DIR_IN;
- else
- itd->Bptr[1] |= ITD_DIR_OUT;
- itd->Bptr[2] |= (ep->wMaxPacketSize + 1023)/1024; /* Mult */
- }
- static void write_itd_micro_frame(UTR_T *utr, int fidx, iTD_T *itd, int mf)
- {
- uint32_t buff_addr;
- buff_addr = (uint32_t)(utr->iso_buff[fidx]); /* xfer buffer start address of this frame */
- itd->Transaction[mf] = ITD_STATUS_ACTIVE | /* Status */
- ((utr->iso_xlen[fidx] & 0xFFF) << ITD_XLEN_Pos) | /* Transaction Length */
- ((buff_addr & 0xFFFFF000) - (itd->buff_base & 0xFFFFF000)) | /* PG */
- (buff_addr & 0xFFF); /* Transaction offset */
- }
- static void remove_iso_ep_from_list(ISO_EP_T *iso_ep)
- {
- ISO_EP_T *p;
- if (iso_ep_list == iso_ep)
- {
- iso_ep_list = iso_ep->next; /* it's the first entry, remove it */
- return;
- }
- p = iso_ep_list; /* find the previous entry of iso_ep */
- while (p->next != NULL)
- {
- if (p->next == iso_ep)
- {
- break;
- }
- p = p->next;
- }
- if (p->next == NULL)
- {
- return; /* not found */
- }
- p->next = iso_ep->next; /* remove iso_ep from list */
- }
- static __inline void add_itd_to_iso_ep(ISO_EP_T *iso_ep, iTD_T *itd)
- {
- iTD_T *p;
- itd->next = NULL;
- if (iso_ep->itd_list == NULL)
- {
- iso_ep->itd_list = itd;
- return;
- }
- /*
- * Find the tail entry of iso_ep->itd_list
- */
- p = iso_ep->itd_list;
- while (p->next != NULL)
- {
- p = p->next;
- }
- p->next = itd;
- }
- int ehci_iso_xfer(UTR_T *utr)
- {
- EP_INFO_T *ep = utr->ep; /* reference to isochronous endpoint */
- ISO_EP_T *iso_ep; /* software iso endpoint descriptor */
- iTD_T *itd, *itd_next, *itd_list = NULL;
- int i, itd_cnt;
- int trans_mask; /* bit mask of used xfer in an iTD */
- int fidx; /* index to the 8 iso frames of UTR */
- int interval; /* frame interval of iTD */
- if (ep->hw_pipe != NULL)
- {
- iso_ep = (ISO_EP_T *)ep->hw_pipe; /* get reference of the isochronous endpoint */
- if (utr->bIsoNewSched)
- iso_ep->next_frame = (((_ehci->UFINDR + (EHCI_ISO_DELAY * 8)) & HSUSBH_UFINDR_FI_Msk) >> 3) & 0x3FF;
- }
- else
- {
- /* first time transfer of this iso endpoint */
- iso_ep = usbh_alloc_mem(sizeof(*iso_ep));
- if (iso_ep == NULL)
- return USBH_ERR_MEMORY_OUT;
- memset(iso_ep, 0, sizeof(*iso_ep));
- iso_ep->ep = ep;
- iso_ep->next_frame = (((_ehci->UFINDR + (EHCI_ISO_DELAY * 8)) & HSUSBH_UFINDR_FI_Msk) >> 3) & 0x3FF;
- ep->hw_pipe = iso_ep;
- /*
- * Add this iso_ep into iso_ep_list
- */
- DISABLE_EHCI_IRQ();
- iso_ep->next = iso_ep_list;
- iso_ep_list = iso_ep;
- ENABLE_EHCI_IRQ();
- }
- if (utr->udev->speed == SPEED_FULL)
- return ehci_iso_split_xfer(utr, iso_ep);
- /*------------------------------------------------------------------------------------*/
- /* Allocate iTDs */
- /*------------------------------------------------------------------------------------*/
- if (ep->bInterval < 2) /* transfer interval is 1 micro-frame */
- {
- trans_mask = 0xFF;
- itd_cnt = 1; /* required 1 iTD for one UTR */
- interval = 1; /* iTD frame interval of this endpoint */
- }
- else if (ep->bInterval < 4) /* transfer interval is 2 micro-frames */
- {
- trans_mask = 0x55;
- itd_cnt = 2; /* required 2 iTDs for one UTR */
- interval = 1; /* iTD frame interval of this endpoint */
- }
- else if (ep->bInterval < 8) /* transfer interval is 4 micro-frames */
- {
- trans_mask = 0x44;
- itd_cnt = 4; /* required 4 iTDs for one UTR */
- interval = 1; /* iTD frame interval of this endpoint */
- }
- else if (ep->bInterval < 16) /* transfer interval is 8 micro-frames */
- {
- trans_mask = 0x08; /* there's 1 transfer in one iTD */
- itd_cnt = 8; /* required 8 iTDs for one UTR */
- interval = 1; /* iTD frame interval of this endpoint */
- }
- else if (ep->bInterval < 32) /* transfer interval is 16 micro-frames */
- {
- trans_mask = 0x10; /* there's 1 transfer in one iTD */
- itd_cnt = 8; /* required 8 iTDs for one UTR */
- interval = 2; /* iTD frame interval of this endpoint */
- }
- else if (ep->bInterval < 64) /* transfer interval is 32 micro-frames */
- {
- trans_mask = 0x02; /* there's 1 transfer in one iTD */
- itd_cnt = 8; /* required 8 iTDs for one UTR */
- interval = 4; /* iTD frame interval of this endpoint */
- }
- else /* transfer interval is 64 micro-frames */
- {
- trans_mask = 0x04; /* there's 1 transfer in one iTD */
- itd_cnt = 8; /* required 8 iTDs for one UTR */
- interval = 8; /* iTD frame interval of this endpoint */
- }
- for (i = 0; i < itd_cnt; i++) /* allocate all iTDs required by UTR */
- {
- itd = alloc_ehci_iTD();
- if (itd == NULL)
- goto malloc_failed;
- if (itd_list == NULL) /* link all iTDs */
- {
- itd_list = itd;
- }
- else
- {
- itd->next = itd_list;
- itd_list = itd;
- }
- }
- utr->td_cnt = itd_cnt;
- /*------------------------------------------------------------------------------------*/
- /* Fill and link all iTDs */
- /*------------------------------------------------------------------------------------*/
- utr->iso_sf = iso_ep->next_frame;
- fidx = 0; /* index to UTR iso frmes (total IF_PER_UTR) */
- for (itd = itd_list; (itd != NULL); )
- {
- if (fidx >= IF_PER_UTR) /* unlikely */
- {
- USB_error("EHCI driver ITD bug!?\n");
- goto malloc_failed;
- }
- itd->utr = utr;
- itd->fidx = fidx; /* index to UTR's n'th IF_PER_UTR frame */
- itd->buff_base = (uint32_t)(utr->iso_buff[fidx]); /* iTD buffer base is buffer of the first UTR iso frame serviced by this iTD */
- itd->trans_mask = trans_mask;
- write_itd_info(utr, itd);
- for (i = 0; i < 8; i++) /* settle xfer into micro-frames */
- {
- if (!(trans_mask & (0x1<<i)))
- {
- itd->Transaction[i] = 0; /* not accesed */
- continue; /* not scheduled micro-frame */
- }
- write_itd_micro_frame(utr, fidx, itd, i);
- fidx++; /* preceed to next UTR iso frame */
- if (fidx == IF_PER_UTR) /* is the last scheduled micro-frame? */
- {
- /* raise interrupt on completed */
- itd->Transaction[i] |= ITD_IOC;
- break;
- }
- }
- itd_next = itd->next; /* remember the next itd */
- // USB_debug("Link iTD 0x%x, %d\n", (int)itd, iso_ep->next_frame);
- /*
- * Link iTD to period frame list
- */
- DISABLE_EHCI_IRQ();
- itd->sched_frnidx = iso_ep->next_frame; /* remember it for reclamation scan */
- add_itd_to_iso_ep(iso_ep, itd); /* add to software itd list */
- itd->Next_Link = _PFList[itd->sched_frnidx]; /* keep the next link */
- _PFList[itd->sched_frnidx] = ITD_HLNK_ITD(itd);
- iso_ep->next_frame = (iso_ep->next_frame + interval) % FL_SIZE;
- ENABLE_EHCI_IRQ();
- itd = itd_next;
- }
- _ehci->UCMDR |= HSUSBH_UCMDR_PSEN_Msk; /* periodic list enable */
- return 0;
- malloc_failed:
- while (itd_list != NULL)
- {
- itd = itd_list;
- itd_list = itd->next;
- free_ehci_iTD(itd);
- }
- return USBH_ERR_MEMORY_OUT;
- }
- static __inline void add_sitd_to_iso_ep(ISO_EP_T *iso_ep, siTD_T *sitd)
- {
- siTD_T *p;
- sitd->next = NULL;
- if (iso_ep->sitd_list == NULL)
- {
- iso_ep->sitd_list = sitd;
- return;
- }
- /*
- * Find the tail entry of iso_ep->itd_list
- */
- p = iso_ep->sitd_list;
- while (p->next != NULL)
- {
- p = p->next;
- }
- p->next = sitd;
- }
- static void write_sitd_info(UTR_T *utr, siTD_T *sitd)
- {
- UDEV_T *udev = utr->udev;
- EP_INFO_T *ep = utr->ep; /* reference to isochronous endpoint */
- uint32_t buff_page_addr;
- int xlen = utr->iso_xlen[sitd->fidx];
- int scnt;
- sitd->Chrst = (udev->port_num << SITD_PORT_NUM_Pos) |
- (udev->parent->iface->udev->dev_num << SITD_HUB_ADDR_Pos) |
- ((ep->bEndpointAddress & 0xF) << SITD_EP_NUM_Pos) |
- (udev->dev_num << SITD_DEV_ADDR_Pos);
- buff_page_addr = ((uint32_t)utr->iso_buff[sitd->fidx]) & 0xFFFFF000;
- sitd->Bptr[0] = (uint32_t)(utr->iso_buff[sitd->fidx]);
- sitd->Bptr[1] = buff_page_addr + 0x1000;
- scnt = (xlen + 187) / 188;
- if ((ep->bEndpointAddress & EP_ADDR_DIR_MASK) == EP_ADDR_DIR_IN) /* I/O */
- {
- sitd->Chrst |= SITD_XFER_IN;
- sitd->Sched = (1 << (scnt + 2)) - 1;
- sitd->Sched = (sitd->Sched << 10) | 0x1;
- //sitd->Sched <<= 1;
- }
- else
- {
- sitd->Chrst |= SITD_XFER_OUT;
- sitd->Sched = sitd_OUT_Smask[scnt-1];
- if (scnt > 1)
- {
- sitd->Bptr[1] |= (0x1 << 3); /* Transaction position (TP) 01b: Begin */
- }
- sitd->Bptr[1] |= scnt; /* Transaction count (T-Count) */
- }
- if (sitd->fidx == IF_PER_UTR)
- {
- sitd->Sched |= SITD_IOC;
- }
- sitd->StsCtrl = (xlen << SITD_XFER_CNT_Pos) | SITD_STATUS_ACTIVE;
- sitd->BackLink = SITD_LIST_END;
- }
- static void ehci_sitd_adjust_schedule(siTD_T *sitd)
- {
- siTD_T *hlink = (siTD_T *)_PFList[sitd->sched_frnidx];
- uint32_t uframe_mask = 0x00;
- while (hlink && !HLINK_IS_TERMINATED(hlink) && HLINK_IS_SITD(hlink))
- {
- hlink = SITD_PTR(hlink);
- if (hlink != sitd)
- {
- if ((hlink->Chrst & SITD_XFER_IO_Msk) == SITD_XFER_IN)
- {
- uframe_mask |= (hlink->Sched & 0xFF); /* mark micro-frames used by IN S-mask */
- uframe_mask |= ((hlink->Sched >> 8) & 0xFF); /* mark micro-frames used by IN C-mask */
- }
- else
- {
- uframe_mask |= (hlink->Sched & 0xFF); /* mark micro-frames used by OUT S-mask */
- }
- }
- hlink = SITD_PTR(hlink->Next_Link);
- }
- uframe_mask = uframe_mask | (uframe_mask << 8); /* mark both S-mask and C-mask */
- if (uframe_mask)
- {
- /*
- * Shift afterward one micro-frame until no conflicts.
- */
- while (1)
- {
- if (sitd->Sched & uframe_mask)
- {
- sitd->Sched = (sitd->Sched & 0xFFFF0000) | ((sitd->Sched << 1) & 0xFFFF);
- }
- else
- {
- break; /* no conflit, done. */
- }
- }
- }
- }
- static int ehci_iso_split_xfer(UTR_T *utr, ISO_EP_T *iso_ep)
- {
- EP_INFO_T *ep = utr->ep; /* reference to isochronous endpoint */
- siTD_T *sitd, *sitd_next, *sitd_list = NULL;
- int i;
- int fidx; /* index to the 8 iso frames of UTR */
- if (utr->udev->parent == NULL)
- {
- USB_error("siso xfer - parent lost!\n");
- return USBH_ERR_INVALID_PARAM;
- }
- /*------------------------------------------------------------------------------------*/
- /* Allocate siTDs */
- /*------------------------------------------------------------------------------------*/
- for (i = 0; i < IF_PER_UTR; i++) /* allocate all siTDs required by UTR */
- {
- sitd = alloc_ehci_siTD();
- if (sitd == NULL)
- goto malloc_failed;
- if (sitd_list == NULL) /* link all siTDs */
- {
- sitd_list = sitd;
- }
- else
- {
- sitd->next = sitd_list;
- sitd_list = sitd;
- }
- }
- utr->td_cnt = IF_PER_UTR;
- /*------------------------------------------------------------------------------------*/
- /* Fill and link all siTDs */
- /*------------------------------------------------------------------------------------*/
- utr->iso_sf = iso_ep->next_frame;
- fidx = 0; /* index to UTR iso frmes (total IF_PER_UTR) */
- for (sitd = sitd_list; (sitd != NULL); fidx++)
- {
- if (fidx >= IF_PER_UTR) /* unlikely */
- {
- USB_error("EHCI driver siTD bug!?\n");
- goto malloc_failed;
- }
- sitd->utr = utr;
- sitd->fidx = fidx; /* index to UTR's n'th IF_PER_UTR frame */
- write_sitd_info(utr, sitd);
- sitd_next = sitd->next; /* remember the next itd */
- // USB_debug("Link iTD 0x%x, %d\n", (int)itd, iso_ep->next_frame);
- /*
- * Link iTD to period frame list
- */
- sitd->sched_frnidx = iso_ep->next_frame; /* remember it for reclamation scan */
- DISABLE_EHCI_IRQ();
- ehci_sitd_adjust_schedule(sitd);
- add_sitd_to_iso_ep(iso_ep, sitd); /* add to software itd list */
- sitd->Next_Link = _PFList[sitd->sched_frnidx];/* keep the next link */
- _PFList[sitd->sched_frnidx] = SITD_HLNK_SITD(sitd);
- iso_ep->next_frame = (iso_ep->next_frame + ep->bInterval) % FL_SIZE;
- ENABLE_EHCI_IRQ();
- sitd = sitd_next;
- }
- _ehci->UCMDR |= HSUSBH_UCMDR_PSEN_Msk; /* periodic list enable */
- return 0;
- malloc_failed:
- while (sitd_list != NULL)
- {
- sitd = sitd_list;
- sitd_list = sitd->next;
- free_ehci_siTD(sitd);
- }
- return USBH_ERR_MEMORY_OUT;
- }
- /*
- * If it's an isochronous endpoint, quit current transfer via UTR or hardware EP.
- */
- int ehci_quit_iso_xfer(UTR_T *utr, EP_INFO_T *ep)
- {
- ISO_EP_T *iso_ep;
- iTD_T *itd, *itd_next, *p;
- uint32_t frnidx;
- uint32_t now_frame;
- if (ep == NULL)
- {
- if (utr == NULL)
- return USBH_ERR_NOT_FOUND;
- if (utr->ep == NULL)
- return USBH_ERR_NOT_FOUND;
- ep = utr->ep;
- }
- if ((ep->bmAttributes & EP_ATTR_TT_MASK) != EP_ATTR_TT_ISO)
- return USBH_ERR_NOT_FOUND; /* not isochronous endpoint */
- /*------------------------------------------------------------------------------------*/
- /* It's an iso endpoint. Remove it as required. */
- /*------------------------------------------------------------------------------------*/
- iso_ep = iso_ep_list;
- while (iso_ep != NULL) /* Search all activated iso endpoints */
- {
- if (iso_ep->ep == ep)
- break;
- iso_ep = iso_ep->next;
- }
- if (iso_ep == NULL)
- return 0; /* should have been removed */
- itd = iso_ep->itd_list; /* get the first iTD from iso_ep's iTD list */
- while (itd != NULL) /* traverse all iTDs of itd list */
- {
- itd_next = itd->next; /* remember the next iTD */
- utr = itd->utr;
- /*--------------------------------------------------------------------------------*/
- /* Remove this iTD from period frame list */
- /*--------------------------------------------------------------------------------*/
- frnidx = itd->sched_frnidx;
- /*
- * Prevent to race with Host Controller. If the iTD to be removed is located in
- * current or next frame, wait until HC passed through it.
- */
- while (1)
- {
- now_frame = (_ehci->UFINDR >> 3) & 0x3FF;
- if ((now_frame == frnidx) || (((now_frame+1)%1024) == frnidx))
- continue;
- break;
- }
- if (_PFList[frnidx] == ITD_HLNK_ITD(itd))
- {
- /* is the first entry, just change to next */
- _PFList[frnidx] = itd->Next_Link;
- }
- else
- {
- p = ITD_PTR(_PFList[frnidx]); /* find the preceding iTD */
- while ((ITD_PTR(p->Next_Link) != itd) && (p != NULL))
- {
- p = ITD_PTR(p->Next_Link);
- }
- if (p == NULL) /* link list out of control! */
- {
- USB_error("ehci_quit_iso_xfer - An iTD lost reference to periodic frame list! 0x%x on %d\n", (int)itd, frnidx);
- }
- else /* remove iTD from list */
- {
- p->Next_Link = itd->Next_Link;
- }
- }
- utr->td_cnt--;
- if (utr->td_cnt == 0) /* All iTD of this UTR done */
- {
- utr->bIsTransferDone = 1;
- if (utr->func)
- utr->func(utr);
- utr->status = USBH_ERR_ABORT;
- }
- free_ehci_iTD(itd);
- itd = itd_next;
- }
- /*
- * Remove iso_ep from iso_ep_list
- */
- remove_iso_ep_from_list(iso_ep);
- usbh_free_mem(iso_ep, sizeof(*iso_ep)); /* free this iso_ep */
- ep->hw_pipe = NULL;
- if (iso_ep_list == NULL)
- _ehci->UCMDR &= ~HSUSBH_UCMDR_PSEN_Msk;
- return 0;
- }
- /// @endcond HIDDEN_SYMBOLS
- /*** (C) COPYRIGHT 2017 Nuvoton Technology Corp. ***/
|