SAMA5 EHCI: Add data transfer logic for asynchronous endpoints
This commit is contained in:
parent
da3ff83fc3
commit
ea2e4c11f8
@ -295,6 +295,13 @@ static int sam_qh_flush(struct sam_qh_s *qh);
|
|||||||
static int sam_ioc_setup(struct sam_rhport_s *rhport, struct sam_epinfo_s *epinfo);
|
static int sam_ioc_setup(struct sam_rhport_s *rhport, struct sam_epinfo_s *epinfo);
|
||||||
static int sam_ioc_wait(struct sam_epinfo_s *epinfo);
|
static int sam_ioc_wait(struct sam_epinfo_s *epinfo);
|
||||||
static void sam_qh_enqueue(struct sam_qh_s *qh);
|
static void sam_qh_enqueue(struct sam_qh_s *qh);
|
||||||
|
static struct sam_qh_s *sam_qh_create(struct sam_rhport_s *rhport,
|
||||||
|
struct sam_epinfo_s *epinfo);
|
||||||
|
static int sam_qtd_addbpl(struct sam_qtd_s *qtd, const void *buffer, size_t buflen);
|
||||||
|
static struct sam_qtd_s *sam_qtd_setupphase(const struct usb_ctrlreq_s *req);
|
||||||
|
static struct sam_qtd_s *sam_qtd_dataphase(void *buffer, int buflen,
|
||||||
|
uint32_t tokenbits);
|
||||||
|
static struct sam_qtd_s *sam_qtd_statusphase(uint32_t tokenbits);
|
||||||
static int sam_async_transfer(struct sam_rhport_s *rhport,
|
static int sam_async_transfer(struct sam_rhport_s *rhport,
|
||||||
struct sam_epinfo_s *epinfo, const struct usb_ctrlreq_s *req,
|
struct sam_epinfo_s *epinfo, const struct usb_ctrlreq_s *req,
|
||||||
uint8_t *buffer, size_t buflen);
|
uint8_t *buffer, size_t buflen);
|
||||||
@ -874,26 +881,25 @@ static int sam_qh_foreach(struct sam_qh_s *qh, uint32_t **bp, foreach_qh_t handl
|
|||||||
* Setup and call sam_qh_foreach to that every element of the asynchronous
|
* Setup and call sam_qh_foreach to that every element of the asynchronous
|
||||||
* queue is examined.
|
* queue is examined.
|
||||||
*
|
*
|
||||||
|
* Assumption: The caller holds the EHCI exclsem
|
||||||
|
*
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
static int sam_qh_forall(foreach_qh_t handler, void *arg)
|
static int sam_qh_forall(foreach_qh_t handler, void *arg)
|
||||||
{
|
{
|
||||||
struct sam_qh_s *qh;
|
struct sam_qh_s *qh;
|
||||||
uint32_t *bp;
|
uint32_t *bp;
|
||||||
int ret;
|
|
||||||
|
|
||||||
/* Preemption is disabled to prevent concurrent modification of the queue
|
/* Set the back pointer to the forward qTD pointer of the asynchronous
|
||||||
* head by the other threads.
|
* queue head.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
bp = (uint32_t *)&qh->hw.hlp;
|
bp = (uint32_t *)&g_asynchead.hw.hlp;
|
||||||
|
|
||||||
sched_lock();
|
|
||||||
qh = (struct sam_qh_s *)sam_virtramaddr(sam_swap32(*bp) & QH_HLP_MASK);
|
qh = (struct sam_qh_s *)sam_virtramaddr(sam_swap32(*bp) & QH_HLP_MASK);
|
||||||
sam_qh_foreach(qh, &bp, handler, arg);
|
|
||||||
sched_unlock();
|
|
||||||
|
|
||||||
return ret;
|
/* Then traverse and operate on every QH and qTD in the list */
|
||||||
|
|
||||||
|
return sam_qh_foreach(qh, &bp, handler, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -1186,20 +1192,15 @@ static int sam_ioc_wait(struct sam_epinfo_s *epinfo)
|
|||||||
* Description:
|
* Description:
|
||||||
* Add a new, ready-to-go QH w/attached qTDs to the asynchonous queue.
|
* Add a new, ready-to-go QH w/attached qTDs to the asynchonous queue.
|
||||||
*
|
*
|
||||||
|
* Assumptions: The caller holds the EHCI exclsem
|
||||||
|
*
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
static void sam_qh_enqueue(struct sam_qh_s *qh)
|
static void sam_qh_enqueue(struct sam_qh_s *qh)
|
||||||
{
|
{
|
||||||
uintptr_t physaddr;
|
uintptr_t physaddr;
|
||||||
|
|
||||||
/* Add the new QH to the head of the asynchronous queue list. Preemption
|
/* Add the new QH to the head of the asynchronous queue list. */
|
||||||
* is disabled momentarily to prevent concurrent modification of the queue
|
|
||||||
* head by the worker thread.
|
|
||||||
*/
|
|
||||||
|
|
||||||
physaddr = (uintptr_t)sam_physramaddr((uintptr_t)qh);
|
|
||||||
sched_lock();
|
|
||||||
|
|
||||||
/* Attach the old head as the new QH HLP and flush the new QH and its attached
|
/* Attach the old head as the new QH HLP and flush the new QH and its attached
|
||||||
* qTDs to RAM.
|
* qTDs to RAM.
|
||||||
*/
|
*/
|
||||||
@ -1211,10 +1212,330 @@ static void sam_qh_enqueue(struct sam_qh_s *qh)
|
|||||||
* modified head to RAM.
|
* modified head to RAM.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
physaddr = (uintptr_t)sam_physramaddr((uintptr_t)qh);
|
||||||
g_asynchead.hw.hlp = sam_swap32(physaddr | QH_HLP_TYP_QH);
|
g_asynchead.hw.hlp = sam_swap32(physaddr | QH_HLP_TYP_QH);
|
||||||
cp15_coherent_dcache((uintptr_t)&g_asynchead,
|
cp15_coherent_dcache((uintptr_t)&g_asynchead,
|
||||||
(uintptr_t)&g_asynchead + sizeof(struct ehci_qh_s));
|
(uintptr_t)&g_asynchead + sizeof(struct ehci_qh_s));
|
||||||
sched_unlock();
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Name: sam_qh_create
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Create a new Queue Head (QH)
|
||||||
|
*
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
static struct sam_qh_s *sam_qh_create(struct sam_rhport_s *rhport,
|
||||||
|
struct sam_epinfo_s *epinfo)
|
||||||
|
{
|
||||||
|
struct sam_qh_s *qh;
|
||||||
|
uint32_t regval;
|
||||||
|
|
||||||
|
/* Allocate a new queue head structure */
|
||||||
|
|
||||||
|
qh = sam_qh_alloc();
|
||||||
|
if (qh == NULL)
|
||||||
|
{
|
||||||
|
udbg("ERROR: Failed to allocate a QH\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Save the endpoint information with the QH itself */
|
||||||
|
|
||||||
|
qh->epinfo = epinfo;
|
||||||
|
|
||||||
|
/* Write QH endpoint characteristics:
|
||||||
|
*
|
||||||
|
* FIELD DESCRIPTION VALUE/SOURCE
|
||||||
|
* -------- ------------------------------- --------------------
|
||||||
|
* DEVADDR Device address Endpoint structure
|
||||||
|
* I Inactivate on Next Transaction 0
|
||||||
|
* ENDPT Endpoint number Endpoint structure
|
||||||
|
* EPS Endpoint speed Endpoint structure
|
||||||
|
* DTC Data toggle control 1
|
||||||
|
* MAXPKT Max packet size Endpoint structure
|
||||||
|
* C Control endpoint Calculated
|
||||||
|
* RL NAK count reloaded 8
|
||||||
|
*/
|
||||||
|
|
||||||
|
regval = ((uint32_t)epinfo->devaddr << QH_EPCHAR_DEVADDR_SHIFT) |
|
||||||
|
((uint32_t)epinfo->epno << QH_EPCHAR_ENDPT_SHIFT) |
|
||||||
|
((uint32_t)epinfo->speed << QH_EPCHAR_EPS_SHIFT) |
|
||||||
|
QH_EPCHAR_DTC |
|
||||||
|
((uint32_t)epinfo->maxpacket << QH_EPCHAR_MAXPKT_SHIFT) |
|
||||||
|
((uint32_t)8 << QH_EPCHAR_RL_SHIFT);
|
||||||
|
|
||||||
|
if (epinfo->speed != EHCI_FULL_SPEED && epinfo->epno == 0)
|
||||||
|
{
|
||||||
|
regval |= QH_EPCHAR_C;
|
||||||
|
}
|
||||||
|
|
||||||
|
qh->hw.epchar = sam_swap32(regval);
|
||||||
|
|
||||||
|
/* Write QH endpoint capabilities
|
||||||
|
*
|
||||||
|
* FIELD DESCRIPTION VALUE/SOURCE
|
||||||
|
* -------- ------------------------------- --------------------
|
||||||
|
* SSMASK Interrupt Schedule Mask 0
|
||||||
|
* SCMASK Split Completion Mask 0
|
||||||
|
* HUBADDR Hub Address Always 0 for now
|
||||||
|
* PORT Port number RH port index + 1
|
||||||
|
* MULT High band width multiplier 1
|
||||||
|
*
|
||||||
|
* REVISIT: Future HUB support will require the HUB port number
|
||||||
|
* and HUB device address to be included here.
|
||||||
|
*/
|
||||||
|
|
||||||
|
regval = ((uint32_t)0 << QH_EPCAPS_HUBADDR_SHIFT) |
|
||||||
|
((uint32_t)(rhport->rhpndx + 1) << QH_EPCAPS_PORT_SHIFT) |
|
||||||
|
((uint32_t)1 << QH_EPCAPS_MULT_SHIFT);
|
||||||
|
|
||||||
|
qh->hw.epcaps = sam_swap32(regval);
|
||||||
|
|
||||||
|
/* Mark this as the end of this list. This will be overwritten if/when the
|
||||||
|
* next qTD is added to the queue.
|
||||||
|
*/
|
||||||
|
|
||||||
|
qh->hw.overlay.nqp = sam_swap32(QH_NQP_T);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Name: sam_qtd_addbpl
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Add a buffer pointer list to a qTD.
|
||||||
|
*
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
static int sam_qtd_addbpl(struct sam_qtd_s *qtd, const void *buffer, size_t buflen)
|
||||||
|
{
|
||||||
|
uint32_t physaddr;
|
||||||
|
uint32_t nbytes;
|
||||||
|
uint32_t next;
|
||||||
|
int ndx;
|
||||||
|
|
||||||
|
physaddr = (uint32_t)sam_physramaddr((uintptr_t)buffer);
|
||||||
|
|
||||||
|
for (ndx = 0; ndx < 5; ndx++)
|
||||||
|
{
|
||||||
|
/* Write the physical address of the buffer into the qTD buffer pointer
|
||||||
|
* list.
|
||||||
|
*/
|
||||||
|
|
||||||
|
qtd->hw.bpl[ndx] = sam_swap32(physaddr);
|
||||||
|
|
||||||
|
/* Get the next buffer pointer (in the case where we will have to transfer
|
||||||
|
* more then on 4KB chunks.
|
||||||
|
*/
|
||||||
|
|
||||||
|
next = (physaddr + 4096) & ~4095;
|
||||||
|
|
||||||
|
/* How many bytes were included in the last buffer? Was the the whole
|
||||||
|
* thing?
|
||||||
|
*/
|
||||||
|
|
||||||
|
nbytes = next - physaddr;
|
||||||
|
if (nbytes >= buflen)
|
||||||
|
{
|
||||||
|
/* Yes... it was the whole thing. Break out of the loop early. */
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Adjust the buffer length and physical address for the next time
|
||||||
|
* through the loop.
|
||||||
|
*/
|
||||||
|
|
||||||
|
buflen -= nbytes;
|
||||||
|
physaddr = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle the case of a huge buffer > 4*4KB = 16KB */
|
||||||
|
|
||||||
|
if (ndx >= 5)
|
||||||
|
{
|
||||||
|
uvdbg("ERROR: Buffer too big. Remaining %d\n", buflen);
|
||||||
|
return -EFBIG;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Name: sam_qtd_setupphase
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Create a SETUP phase request qTD.
|
||||||
|
*
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
static struct sam_qtd_s *sam_qtd_setupphase(const struct usb_ctrlreq_s *req)
|
||||||
|
{
|
||||||
|
struct sam_qtd_s *qtd;
|
||||||
|
uint32_t regval;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Allocate a new Queue Element Transfer Descriptor (qTD) */
|
||||||
|
|
||||||
|
qtd = sam_qtd_alloc();
|
||||||
|
if (qtd == NULL)
|
||||||
|
{
|
||||||
|
udbg("ERROR: Failed to allocate request qTD");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mark this as the end of the list (this will be overwritten if another
|
||||||
|
* qTD is added after this one.
|
||||||
|
*/
|
||||||
|
|
||||||
|
qtd->hw.nqp = sam_swap32(QTD_NQP_T);
|
||||||
|
qtd->hw.alt = sam_swap32(QTD_AQP_T);
|
||||||
|
|
||||||
|
/* Write qTD token:
|
||||||
|
*
|
||||||
|
* FIELD DESCRIPTION VALUE/SOURCE
|
||||||
|
* -------- ------------------------------- --------------------
|
||||||
|
* STATUS Status QTD_TOKEN_ACTIVE
|
||||||
|
* PID PID Code QTD_TOKEN_PID_SETUP
|
||||||
|
* CERR Error Counter 3
|
||||||
|
* CPAGE Current Page 0
|
||||||
|
* IOC Interrupt on complete 0
|
||||||
|
* NBYTES Total Bytes to Transfer USB_SIZEOF_CTRLREQ
|
||||||
|
* TOGGLE Data Toggle 0
|
||||||
|
*/
|
||||||
|
|
||||||
|
regval = QTD_TOKEN_ACTIVE | QTD_TOKEN_PID_SETUP |
|
||||||
|
((uint32_t)3 << QTD_TOKEN_CERR_SHIFT) |
|
||||||
|
((uint32_t)USB_SIZEOF_CTRLREQ << QTD_TOKEN_NBYTES_SHIFT);
|
||||||
|
|
||||||
|
qtd->hw.token = sam_swap32(regval);
|
||||||
|
|
||||||
|
/* Add the buffer data */
|
||||||
|
|
||||||
|
ret = sam_qtd_addbpl(qtd, req, USB_SIZEOF_CTRLREQ);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
uvdbg("ERROR: sam_qtd_addbpl failed: %d\n", ret);
|
||||||
|
sam_qtd_free(qtd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return qtd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Name: sam_qtd_dataphase
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Create a data transfer or SET data phase qTD.
|
||||||
|
*
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
static struct sam_qtd_s *sam_qtd_dataphase(void *buffer, int buflen,
|
||||||
|
uint32_t tokenbits)
|
||||||
|
{
|
||||||
|
struct sam_qtd_s *qtd;
|
||||||
|
uint32_t regval;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Allocate a new Queue Element Transfer Descriptor (qTD) */
|
||||||
|
|
||||||
|
qtd = sam_qtd_alloc();
|
||||||
|
if (qtd == NULL)
|
||||||
|
{
|
||||||
|
udbg("ERROR: Failed to allocate data buffer qTD");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mark this as the end of the list (this will be overwritten if another
|
||||||
|
* qTD is added after this one.
|
||||||
|
*/
|
||||||
|
|
||||||
|
qtd->hw.nqp = sam_swap32(QTD_NQP_T);
|
||||||
|
qtd->hw.alt = sam_swap32(QTD_AQP_T);
|
||||||
|
|
||||||
|
/* Write qTD token:
|
||||||
|
*
|
||||||
|
* FIELD DESCRIPTION VALUE/SOURCE
|
||||||
|
* -------- ------------------------------- --------------------
|
||||||
|
* STATUS Status QTD_TOKEN_ACTIVE
|
||||||
|
* PID PID Code Contained in tokenbits
|
||||||
|
* CERR Error Counter 3
|
||||||
|
* CPAGE Current Page 0
|
||||||
|
* IOC Interrupt on complete Contained in tokenbits
|
||||||
|
* NBYTES Total Bytes to Transfer buflen
|
||||||
|
* TOGGLE Data Toggle Contained in tokenbits
|
||||||
|
*/
|
||||||
|
|
||||||
|
regval = tokenbits | QTD_TOKEN_ACTIVE |
|
||||||
|
((uint32_t)3 << QTD_TOKEN_CERR_SHIFT) |
|
||||||
|
((uint32_t)buflen << QTD_TOKEN_NBYTES_SHIFT);
|
||||||
|
|
||||||
|
qtd->hw.token = sam_swap32(regval);
|
||||||
|
|
||||||
|
/* Add the buffer information to the bufffer pointer list */
|
||||||
|
|
||||||
|
ret = sam_qtd_addbpl(qtd, buffer, buflen);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
udbg("ERROR: sam_qtd_addbpl failed: %d\n", ret);
|
||||||
|
sam_qtd_free(qtd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return qtd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Name: sam_qtd_statusphase
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Create a STATUS phase request qTD.
|
||||||
|
*
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
static struct sam_qtd_s *sam_qtd_statusphase(uint32_t tokenbits)
|
||||||
|
{
|
||||||
|
struct sam_qtd_s *qtd;
|
||||||
|
uint32_t regval;
|
||||||
|
|
||||||
|
/* Allocate a new Queue Element Transfer Descriptor (qTD) */
|
||||||
|
|
||||||
|
qtd = sam_qtd_alloc();
|
||||||
|
if (qtd == NULL)
|
||||||
|
{
|
||||||
|
udbg("ERROR: Failed to allocate request qTD");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mark this as the end of the list (this will be overwritten if another
|
||||||
|
* qTD is added after this one.
|
||||||
|
*/
|
||||||
|
|
||||||
|
qtd->hw.nqp = sam_swap32(QTD_NQP_T);
|
||||||
|
qtd->hw.alt = sam_swap32(QTD_AQP_T);
|
||||||
|
|
||||||
|
/* Write qTD token:
|
||||||
|
*
|
||||||
|
* FIELD DESCRIPTION VALUE/SOURCE
|
||||||
|
* -------- ------------------------------- --------------------
|
||||||
|
* STATUS Status QTD_TOKEN_ACTIVE
|
||||||
|
* PID PID Code Contained in tokenbits
|
||||||
|
* CERR Error Counter 3
|
||||||
|
* CPAGE Current Page 0
|
||||||
|
* IOC Interrupt on complete QH_TOKEN_IOC
|
||||||
|
* NBYTES Total Bytes to Transfer 0
|
||||||
|
* TOGGLE Data Toggle Contained in tokenbits
|
||||||
|
*/
|
||||||
|
|
||||||
|
regval = tokenbits | QTD_TOKEN_ACTIVE |
|
||||||
|
((uint32_t)3 << QTD_TOKEN_CERR_SHIFT) |
|
||||||
|
QH_TOKEN_IOC |
|
||||||
|
((uint32_t)USB_SIZEOF_CTRLREQ << QTD_TOKEN_NBYTES_SHIFT);
|
||||||
|
|
||||||
|
qtd->hw.token = sam_swap32(regval);
|
||||||
|
return qtd;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -1222,11 +1543,15 @@ static void sam_qh_enqueue(struct sam_qh_s *qh)
|
|||||||
*
|
*
|
||||||
* Description:
|
* Description:
|
||||||
* Process a IN or OUT request on any asynchronous endpoint (bulk or control).
|
* Process a IN or OUT request on any asynchronous endpoint (bulk or control).
|
||||||
* This function will enqueue the request and wait for it to complete.
|
* This function will enqueue the request and wait for it to complete. Bulk
|
||||||
|
* data transfers differ in that req == NULL and there are not SETUP or STATUS
|
||||||
|
* phases.
|
||||||
*
|
*
|
||||||
* This is a blocking function; it will not return until the control transfer
|
* This is a blocking function; it will not return until the control transfer
|
||||||
* has completed.
|
* has completed.
|
||||||
*
|
*
|
||||||
|
* Assumption: The caller holds the EHCI exclsem
|
||||||
|
*
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
static int sam_async_transfer(struct sam_rhport_s *rhport,
|
static int sam_async_transfer(struct sam_rhport_s *rhport,
|
||||||
@ -1234,11 +1559,19 @@ static int sam_async_transfer(struct sam_rhport_s *rhport,
|
|||||||
const struct usb_ctrlreq_s *req,
|
const struct usb_ctrlreq_s *req,
|
||||||
uint8_t *buffer, size_t buflen)
|
uint8_t *buffer, size_t buflen)
|
||||||
{
|
{
|
||||||
|
struct sam_qh_s *qh;
|
||||||
|
struct sam_qtd_s *qtd;
|
||||||
|
uintptr_t physaddr;
|
||||||
|
uint32_t *flink;
|
||||||
|
uint32_t toggle;
|
||||||
|
uint32_t datapid;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
uvdbg("RHport%d EP%d: buffer=%p, buflen=%d, req=%p\n",
|
uvdbg("RHport%d EP%d: buffer=%p, buflen=%d, req=%p\n",
|
||||||
rhport->rhpndx+1, epinfo->epno, buffer, buflen, req);
|
rhport->rhpndx+1, epinfo->epno, buffer, buflen, req);
|
||||||
|
|
||||||
|
DEBUGASSERT(rhport && epinfo);
|
||||||
|
|
||||||
if (req != NULL)
|
if (req != NULL)
|
||||||
{
|
{
|
||||||
uvdbg("req=%02x type=%02x value=%04x index=%04x\n",
|
uvdbg("req=%02x type=%02x value=%04x index=%04x\n",
|
||||||
@ -1246,6 +1579,12 @@ static int sam_async_transfer(struct sam_rhport_s *rhport,
|
|||||||
sam_read16(req->index));
|
sam_read16(req->index));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* A buffer may or may be supplied with an EP0 SETUP transfer. A buffer will
|
||||||
|
* always be present for normal endpoint data transfers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
DEBUGASSERT(req || (buffer && buflen > 0));
|
||||||
|
|
||||||
/* Set the request for the IOC event well BEFORE enabling the transfer. */
|
/* Set the request for the IOC event well BEFORE enabling the transfer. */
|
||||||
|
|
||||||
ret = sam_ioc_setup(rhport, epinfo);
|
ret = sam_ioc_setup(rhport, epinfo);
|
||||||
@ -1255,7 +1594,132 @@ static int sam_async_transfer(struct sam_rhport_s *rhport,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#warning "Missing logic"
|
/* Get the data token direction */
|
||||||
|
|
||||||
|
datapid = QTD_TOKEN_PID_OUT;
|
||||||
|
if (req)
|
||||||
|
{
|
||||||
|
if ((req->req & USB_REQ_DIR_MASK) == USB_REQ_DIR_IN)
|
||||||
|
{
|
||||||
|
datapid = QTD_TOKEN_PID_IN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (epinfo->dirin)
|
||||||
|
{
|
||||||
|
datapid = QTD_TOKEN_PID_IN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create and initialize a Queue Head (QH) structure for this transfer */
|
||||||
|
|
||||||
|
qh = sam_qh_create(rhport, epinfo);
|
||||||
|
if (qh == NULL)
|
||||||
|
{
|
||||||
|
udbg("ERROR: sam_qh_create failed\n");
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto errout_with_iocwait;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize the QH link and get the next data toggle (not used for SETUP
|
||||||
|
* transfers)
|
||||||
|
*/
|
||||||
|
|
||||||
|
flink = &qh->hw.overlay.nqp;
|
||||||
|
toggle = epinfo->toggle ? 0 : QTD_TOKEN_TOGGLE;
|
||||||
|
ret = -EIO;
|
||||||
|
|
||||||
|
/* Is the an EP0 SETUP request? If req will be non-NULL */
|
||||||
|
|
||||||
|
if (req != NULL)
|
||||||
|
{
|
||||||
|
/* Allocate a new Queue Element Transfer Descriptor (qTD) for the SETUP
|
||||||
|
* phase of the request sequence.
|
||||||
|
*/
|
||||||
|
|
||||||
|
qtd = sam_qtd_setupphase(req);
|
||||||
|
if (qtd == NULL)
|
||||||
|
{
|
||||||
|
udbg("ERROR: sam_qtd_setupphase failed\n");
|
||||||
|
goto errout_with_qh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Link the new qTD to the QH head. */
|
||||||
|
|
||||||
|
physaddr = sam_physramaddr((uintptr_t)qtd);
|
||||||
|
*flink = sam_swap32(physaddr);
|
||||||
|
|
||||||
|
/* Get the new forward link pointer and data toggle */
|
||||||
|
|
||||||
|
flink = &qtd->hw.nqp;
|
||||||
|
toggle = QTD_TOKEN_TOGGLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A buffer may or may be supplied with an EP0 SETUP transfer. A buffer will
|
||||||
|
* always be present for normal endpoint data transfers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (buffer && buflen > 0)
|
||||||
|
{
|
||||||
|
/* Extra TOKEN bits include the data toggle, the data PID, and if there
|
||||||
|
* is no request, and indication to interrupt at the end of this
|
||||||
|
* transfer.
|
||||||
|
*/
|
||||||
|
|
||||||
|
uint32_t tokenbits = toggle | datapid;
|
||||||
|
|
||||||
|
/* If this is not an EP0 SETUP request, then nothing follows the data and
|
||||||
|
* we want the IOC interrupt when the data transfer completes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!req)
|
||||||
|
{
|
||||||
|
tokenbits |= QTD_TOKEN_IOC;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate a new Queue Element Transfer Descriptor (qTD) for the data
|
||||||
|
* buffer.
|
||||||
|
*/
|
||||||
|
|
||||||
|
qtd = sam_qtd_dataphase(buffer, buflen, tokenbits);
|
||||||
|
if (qtd == NULL)
|
||||||
|
{
|
||||||
|
udbg("ERROR: sam_qtd_dataphase failed\n");
|
||||||
|
goto errout_with_qh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Link the new qTD to either QH head of the SETUP qTD. */
|
||||||
|
|
||||||
|
physaddr = sam_physramaddr((uintptr_t)qtd);
|
||||||
|
*flink = sam_swap32(physaddr);
|
||||||
|
|
||||||
|
/* Set the forward link pointer to this new qTD */
|
||||||
|
|
||||||
|
flink = &qtd->hw.nqp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req != NULL)
|
||||||
|
{
|
||||||
|
/* Extra TOKEN bits include the data toggle and the data PID. */
|
||||||
|
|
||||||
|
uint32_t tokenbits = toggle | datapid ;
|
||||||
|
|
||||||
|
/* Allocate a new Queue Element Transfer Descriptor (qTD) for the status */
|
||||||
|
|
||||||
|
qtd = sam_qtd_statusphase(tokenbits);
|
||||||
|
if (qtd == NULL)
|
||||||
|
{
|
||||||
|
udbg("ERROR: sam_qtd_statusphase failed\n");
|
||||||
|
goto errout_with_qh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Link the new qTD to either the SETUP or data qTD. */
|
||||||
|
|
||||||
|
physaddr = sam_physramaddr((uintptr_t)qtd);
|
||||||
|
*flink = sam_swap32(physaddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add the new QH to the head of the asynchronous queue list */
|
||||||
|
|
||||||
|
sam_qh_enqueue(qh);
|
||||||
|
|
||||||
/* Wait for the IOC completion event */
|
/* Wait for the IOC completion event */
|
||||||
|
|
||||||
@ -1270,6 +1734,10 @@ static int sam_async_transfer(struct sam_rhport_s *rhport,
|
|||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
|
|
||||||
|
/* Clean-up after an error */
|
||||||
|
|
||||||
|
errout_with_qh:
|
||||||
|
sam_qh_discard(qh);
|
||||||
errout_with_iocwait:
|
errout_with_iocwait:
|
||||||
epinfo->iocwait = false;
|
epinfo->iocwait = false;
|
||||||
return ret;
|
return ret;
|
||||||
|
Loading…
Reference in New Issue
Block a user