Several fixes to the PIC32 USB device OUT path logic

git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@4451 42af7a65-404d-4744-a932-0658087f49c3
This commit is contained in:
patacongo 2012-03-04 17:38:00 +00:00
parent df43e1b890
commit 864b815846

View File

@ -341,9 +341,9 @@ union wb_u
struct pic32mx_req_s
{
struct usbdev_req_s req; /* Standard USB request */
uint16_t inflight; /* The number of bytes "in-flight" */
struct pic32mx_req_s *flink; /* Supports a singly linked list */
struct usbdev_req_s req; /* Standard USB request */
uint16_t inflight; /* The number of bytes "in-flight" */
struct pic32mx_req_s *flink; /* Supports a singly linked list */
};
/* This is the internal representation of an endpoint */
@ -355,18 +355,18 @@ struct pic32mx_ep_s
* to struct pic32mx_ep_s.
*/
struct usbdev_ep_s ep; /* Standard endpoint structure */
struct usbdev_ep_s ep; /* Standard endpoint structure */
/* PIC32MX-specific fields */
struct pic32mx_usbdev_s *dev; /* Reference to private driver data */
struct pic32mx_req_s *head; /* Request list for this endpoint */
struct pic32mx_usbdev_s *dev; /* Reference to private driver data */
struct pic32mx_req_s *head; /* Request list for this endpoint */
struct pic32mx_req_s *tail;
uint8_t stalled:1; /* true: Endpoint is stalled */
uint8_t halted:1; /* true: Endpoint feature halted */
uint8_t txbusy:1; /* true: TX endpoint FIFO full */
uint8_t rxoverrun:1; /* true: RX data overrun */
uint8_t txnullpkt:1; /* Null packet needed at end of transfer */
uint8_t stalled:1; /* true: Endpoint is stalled */
uint8_t halted:1; /* true: Endpoint feature halted */
uint8_t txbusy:1; /* true: TX endpoint FIFO full */
uint8_t txnullpkt:1; /* Null packet needed at end of TX transfer */
uint8_t rxdata1:1; /* Data0/1 of next RX transfer */
volatile struct usbotg_bdtentry_s *bdtin; /* BDT entry for the IN transaction*/
volatile struct usbotg_bdtentry_s *bdtout; /* BDT entry for the OUT transaction */
};
@ -439,8 +439,8 @@ static int pic32mx_rdcomplete(struct pic32mx_usbdev_s *priv,
struct pic32mx_ep_s *privep);
static int pic32mx_ep0rdsetup(struct pic32mx_usbdev_s *priv,
uint8_t *dest, int readlen);
static int pic32mx_rdsetup(struct pic32mx_usbdev_s *priv,
struct pic32mx_ep_s *privep, uint8_t *dest, int readlen);
static int pic32mx_rdsetup(struct pic32mx_ep_s *privep, uint8_t *dest,
int readlen);
static int pic32mx_rdrequest(struct pic32mx_usbdev_s *priv,
struct pic32mx_ep_s *privep);
static void pic32mx_cancelrequests(struct pic32mx_ep_s *privep);
@ -450,14 +450,14 @@ static void pic32mx_cancelrequests(struct pic32mx_ep_s *privep);
static void pic32mx_dispatchrequest(struct pic32mx_usbdev_s *priv);
static void pic32mx_ep0stall(struct pic32mx_usbdev_s *priv);
static void pic32mx_eptransfer(struct pic32mx_usbdev_s *priv, uint8_t epno,
uint16_t status);
uint16_t ustat);
static void pic32mx_ep0nextsetup(struct pic32mx_usbdev_s *priv);
static void pic32mx_ep0rdcomplete(struct pic32mx_usbdev_s *priv);
static void pic32mx_ep0setup(struct pic32mx_usbdev_s *priv);
static void pic32mx_ep0outcomplete(struct pic32mx_usbdev_s *priv);
static void pic32mx_ep0incomplete(struct pic32mx_usbdev_s *priv);
static void pic32mx_ep0transfer(struct pic32mx_usbdev_s *priv,
uint16_t status);
uint16_t ustat);
static int pic32mx_interrupt(int irq, void *context);
/* Endpoint helpers *********************************************************/
@ -980,7 +980,7 @@ static int pic32mx_wrrequest(struct pic32mx_usbdev_s *priv, struct pic32mx_ep_s
static int pic32mx_rdcomplete(struct pic32mx_usbdev_s *priv,
struct pic32mx_ep_s *privep)
{
volatile struct usbotg_bdtentry_s *bdt = privep->bdtout;
volatile struct usbotg_bdtentry_s *bdtout = privep->bdtout;
struct pic32mx_req_s *privreq;
int readlen;
@ -996,13 +996,14 @@ static int pic32mx_rdcomplete(struct pic32mx_usbdev_s *priv,
return -EINVAL;
}
ullvdbg("EP%d: len=%d xfrd=%d BDT={%08x, %08x}\n",
USB_EPNO(privep->ep.eplog), privreq->req.len, privreq->req.xfrd,
bdt->status, bdt->addr);
ullvdbg("EP%d: len=%d xfrd=%d [%p]\n",
USB_EPNO(privep->ep.eplog), privreq->req.len, privreq->req.xfrd);
bdtdbg("EP%d BDT OUT [%p] {%08x, %08x}\n",
USB_EPNO(privep->ep.eplog), bdtout, bdtout->status, bdtout->addr);
/* Get the length of the data received from the BDT */
readlen = (bdt->status & USB_BDT_BYTECOUNT_MASK) >> USB_BDT_BYTECOUNT_SHIFT;
readlen = (bdtout->status & USB_BDT_BYTECOUNT_MASK) >> USB_BDT_BYTECOUNT_SHIFT;
/* If the receive buffer is full or this is a partial packet,
* then we are finished with the transfer
@ -1031,50 +1032,92 @@ static int pic32mx_rdcomplete(struct pic32mx_usbdev_s *priv,
static int pic32mx_ep0rdsetup(struct pic32mx_usbdev_s *priv, uint8_t *dest,
int readlen)
{
volatile struct usbotg_bdtentry_s *bdt = priv->eplist[EP0].bdtout;
uint16_t status;
volatile struct usbotg_bdtentry_s *bdtout;
volatile struct usbotg_bdtentry_s *otherbdt;
struct pic32mx_ep_s *privep;
uint32_t status;
/* Clear status bits (making sure that UOWN is cleared before doing anything
* else). Preserve only the toggled data indication only.
*/
/* bdtout refers to the next ping-pong BDT to use. */
status = bdt->status;
status &= USB_BDT_DATA01;
status ^= USB_BDT_DATA01;
bdt->status = status;
privep = &priv->eplist[EP0];
bdtout = privep->bdtout;
/* Get the other BDT. Check if the current BDT the EVEN BDT? */
otherbdt = &g_bdt[EP_OUT_EVEN(EP0)];
if (bdtout == otherbdt)
{
/* Yes.. then the other BDT is the ODD BDT. */
otherbdt++;
}
/* If there is no RX transfer in progress, then the other BDT is setup
* to receive the next setup packet. There is a race condition here!
* Stop any setup packet.
*/
#warning REVISIT
if (!priv->rxbusy)
{
/* Reset the other BDT to zero... this will cause any attempted use
* of the BDT to be NAKed.
* of the other BDT to be NAKed. Set the first DATA0/1 value to 1.
*/
if (bdt == &g_bdt[EP0_OUT_EVEN])
{
g_bdt[EP0_OUT_ODD].status = 0;
}
else
{
DEBUGASSERT(bdt == &g_bdt[EP0_OUT_ODD]);
g_bdt[EP0_OUT_EVEN].status = 0;
}
otherbdt->status = 0;
privep->rxdata1 = 1;
}
/* Otherwise, there are RX transfers in progress. bdtout may be
* unavailable now. In that case, we are free to setup the other BDT
* in order to improve performance.
*/
if ((bdtout->status & USB_BDT_UOWN) != USB_BDT_COWN)
{
/* bdtout is not available. Is the other BDT available? */
if ((otherbdt->status & USB_BDT_UOWN) != USB_BDT_COWN)
{
/* Neither are available... we cannot accept the request now */
return -EBUSY;
}
/* Use the other BDT */
bdtout = otherbdt;
}
usbtrace(TRACE_READ(EP0), readlen);
/* Clear status bits (making sure that UOWN is cleared before doing anything
* else).
*/
bdtout->status = 0;
/* Get the correct data toggle (as well as other BDT bits) */
if (privep->rxdata1)
{
status = (USB_BDT_UOWN | USB_BDT_DATA1 | USB_BDT_DTS);
privep->rxdata1 = 0;
}
else
{
status = (USB_BDT_UOWN | USB_BDT_DATA0 | USB_BDT_DTS);
privep->rxdata1 = 1;
}
/* Set the data pointer, data length, and enable the endpoint */
bdt->addr = (uint8_t *)PHYS_ADDR(dest);
status |= (readlen << USB_BDT_BYTECOUNT_SHIFT);
bdtout->addr = (uint8_t *)PHYS_ADDR(dest);
status |= ((uint32_t)readlen << USB_BDT_BYTECOUNT_SHIFT);
/* Then give the BDT to the USB */
status |= (USB_BDT_UOWN | USB_BDT_DTS);
bdtdbg("EP0 BDT OUT [%p] {%08x, %08x}\n", bdt, status, bdt->addr);
bdt->status = status;
bdtdbg("EP0 BDT OUT [%p] {%08x, %08x}\n", bdtout, status, bdtout->addr);
bdtout->status = status;
priv->ctrlstate = CTRLSTATE_RDREQUEST;
priv->rxbusy = 1;
@ -1085,35 +1128,85 @@ static int pic32mx_ep0rdsetup(struct pic32mx_usbdev_s *priv, uint8_t *dest,
* Name: pic32mx_rdsetup
****************************************************************************/
static int pic32mx_rdsetup(struct pic32mx_usbdev_s *priv,
struct pic32mx_ep_s *privep, uint8_t *dest, int readlen)
static int pic32mx_rdsetup(struct pic32mx_ep_s *privep, uint8_t *dest, int readlen)
{
volatile struct usbotg_bdtentry_s *bdt = privep->bdtout;
uint16_t status;
volatile struct usbotg_bdtentry_s *bdtout;
volatile struct usbotg_bdtentry_s *otherbdt;
uint32_t status;
int epno;
/* Select a BDT. Check both the even and the ODD BDT and use the first one
* that we own.
*/
epno = USB_EPNO(privep->ep.eplog);
/* bdtout refers to the next ping-pong BDT to use. However, bdtout may be
* unavailable now. But, in that case, we are free to setup the other BDT
* in order to improve performance.
*/
bdtout = privep->bdtout;
if ((bdtout->status & USB_BDT_UOWN) != USB_BDT_COWN)
{
/* Is the current BDT the EVEN BDT? */
otherbdt = &g_bdt[EP_OUT_EVEN(epno)];
if (bdtout == otherbdt)
{
/* Yes.. select the ODD BDT */
otherbdt++;
}
/* Is the other BDT available? */
if ((otherbdt->status & USB_BDT_UOWN) != USB_BDT_COWN)
{
/* Neither are available... we cannot accept the request now */
return -EBUSY;
}
/* Use the other BDT */
bdtout = otherbdt;
}
usbtrace(TRACE_READ(USB_EPNO(privep->ep.eplog)), readlen);
/* Clear status bits (making sure that UOWN is cleared before doing anything
* else). The DATA01 is (only) is preserved.
*/
status = bdt->status & USB_BDT_DATA01;
bdt->status = status;
bdtout->status = 0;
/* Set the data pointer, data length, and enable the endpoint */
bdt->addr = (uint8_t *)PHYS_ADDR(dest);
bdtout->addr = (uint8_t *)PHYS_ADDR(dest);
/* Get the correct data toggle (as well as other BDT bits) */
if (privep->rxdata1)
{
status = (USB_BDT_UOWN | USB_BDT_DATA1 | USB_BDT_DTS);
privep->rxdata1 = 0;
}
else
{
status = (USB_BDT_UOWN | USB_BDT_DATA0 | USB_BDT_DTS);
privep->rxdata1 = 1;
}
/* Set the data length (preserving the data toggle). */
status |= (readlen << USB_BDT_BYTECOUNT_SHIFT) | USB_BDT_DTS;
status |= ((uint32_t)readlen << USB_BDT_BYTECOUNT_SHIFT);
/* Then give the BDT to the USB */
status |= USB_BDT_UOWN;
bdtdbg("EP%d BDT OUT [%p] {%08x, %08x}\n", epno, bdtout, status, bdtout->addr);
bdtdbg("EP%d BDT OUT [%p] {%08x, %08x}\n",
USB_EPNO(privep->ep.eplog), bdt, status, bdt->addr);
bdt->status = status;
bdtout->status = status;
return OK;
}
@ -1165,11 +1258,9 @@ static int pic32mx_rdrequest(struct pic32mx_usbdev_s *priv,
return OK;
}
usbtrace(TRACE_READ(USB_EPNO(privep->ep.eplog)), privreq->req.xfrd);
/* Get the destination transfer address and size */
dest = privreq->req.buf + privreq->req.xfrd;
dest = privreq->req.buf + privreq->req.xfrd;
readlen = MIN(privreq->req.len, privep->ep.maxpacket);
/* Handle EP0 in a few special ways */
@ -1180,7 +1271,7 @@ static int pic32mx_rdrequest(struct pic32mx_usbdev_s *priv,
}
else
{
ret = pic32mx_rdsetup(priv, privep, dest, readlen);
ret = pic32mx_rdsetup(privep, dest, readlen);
}
return ret;
}
@ -1250,7 +1341,7 @@ static void pic32mx_ep0stall(struct pic32mx_usbdev_s *priv)
{
/* Set ep0 BDT status to stall also */
uint16_t status = (USB_BDT_UOWN| USB_BDT_DATA0 | USB_BDT_DTS | USB_BDT_BSTALL);
uint32_t status = (USB_BDT_UOWN| USB_BDT_DATA0 | USB_BDT_DTS | USB_BDT_BSTALL);
bdtdbg("EP0 BDT OUT [%p] {%08x, %08x}\n",
ep0->bdtout, status, ep0->bdtout->addr);
@ -1270,7 +1361,7 @@ static void pic32mx_ep0stall(struct pic32mx_usbdev_s *priv)
****************************************************************************/
static void pic32mx_eptransfer(struct pic32mx_usbdev_s *priv, uint8_t epno,
uint16_t status)
uint16_t ustat)
{
struct pic32mx_ep_s *privep;
@ -1280,11 +1371,11 @@ static void pic32mx_eptransfer(struct pic32mx_usbdev_s *priv, uint8_t epno,
/* Check if the last transaction was an EP0 OUT transaction */
if ((status & USB_STAT_DIR) == USB_STAT_DIR_OUT)
if ((ustat & USB_STAT_DIR) == USB_STAT_DIR_OUT)
{
/* OUT: host-to-device */
usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_EPOUTDONE), status);
usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_EPOUTDONE), ustat);
/* Handle read requests. First check if a read request is available to
* accept the host data.
@ -1302,21 +1393,13 @@ static void pic32mx_eptransfer(struct pic32mx_usbdev_s *priv, uint8_t epno,
if (pic32mx_rqempty(privep))
{
usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_EPOUTPENDING), (uint16_t)epno);
/* Set the RX overrun condition. And do not give ownerhsip of
* of the packet to USB. If more OUT tokens are received, the
* hardware will NAK further requests.
*/
privep->rxoverrun = true;
#warning "Missing logic"
}
}
else
{
/* IN: device-to-host */
usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_EPINDONE), status);
usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_EPINDONE), ustat);
/* An outgoing IN packet has completed. Update the number of bytes transferred
* and check for completion of the transfer.
@ -2140,7 +2223,7 @@ static void pic32mx_ep0outcomplete(struct pic32mx_usbdev_s *priv)
* Name: pic32mx_ep0transfer
****************************************************************************/
static void pic32mx_ep0transfer(struct pic32mx_usbdev_s *priv, uint16_t status)
static void pic32mx_ep0transfer(struct pic32mx_usbdev_s *priv, uint16_t ustat)
{
volatile struct usbotg_bdtentry_s *bdt;
@ -2155,13 +2238,13 @@ static void pic32mx_ep0transfer(struct pic32mx_usbdev_s *priv, uint16_t status)
/* Check if the last transaction was an EP0 OUT transaction */
if ((status & USB_STAT_DIR) == USB_STAT_DIR_OUT)
if ((ustat & USB_STAT_DIR) == USB_STAT_DIR_OUT)
{
int index;
/* It was an EP0 OUT transaction. Get the index to the BDT. */
index = ((status & USB_STAT_PPBI) == 0 ? EP0_OUT_EVEN : EP0_OUT_ODD);
index = ((ustat & USB_STAT_PPBI) == 0 ? EP0_OUT_EVEN : EP0_OUT_ODD);
bdt = &g_bdt[index];
priv->eplist[0].bdtout = bdt;
@ -2193,7 +2276,7 @@ static void pic32mx_ep0transfer(struct pic32mx_usbdev_s *priv, uint16_t status)
{
/* Handle the data OUT transfer */
usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_EP0OUTDONE), status);
usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_EP0OUTDONE), ustat);
pic32mx_ep0outcomplete(priv);
}
}
@ -2202,7 +2285,7 @@ static void pic32mx_ep0transfer(struct pic32mx_usbdev_s *priv, uint16_t status)
else /* if ((status & USB_STAT_DIR) == USB_STAT_DIR_IN) */
{
usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_EP0INDONE), status);
usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_EP0INDONE), ustat);
/* Handle the IN transfer complete */
@ -2657,21 +2740,19 @@ static void pic32mx_ep0configure(struct pic32mx_usbdev_s *priv)
bdt->status = (USB_BDT_UOWN | bytecount);
priv->eplist[EP0].bdtout = bdt;
bdt = &g_bdt[EP0_OUT_ODD];
bdt++;
bdt->status = (USB_BDT_UOWN | bytecount);
bdt->addr = (uint8_t *)PHYS_ADDR(&priv->ctrl);
/* Configure the IN BDTs. Set DATA0 in the EVEN BDT because the first
* thing we will do when transmitting is toggle the bit
*/
/* Configure the IN BDTs. */
bdt = &g_bdt[EP0_IN_EVEN];
bdt->status = (USB_BDT_DATA0 | USB_BDT_DTS | USB_BDT_BSTALL);
bdt->status = 0;
bdt->addr = 0;
priv->eplist[EP0].bdtin = bdt;
bdt = &g_bdt[EP0_IN_ODD];
bdt->status = (USB_BDT_DATA0 | USB_BDT_DTS | USB_BDT_BSTALL);
bdt++;
bdt->status = 0;
bdt->addr = 0;
}
@ -2690,7 +2771,6 @@ static int pic32mx_epconfigure(struct usbdev_ep_s *ep,
volatile struct usbotg_bdtentry_s *bdt;
uint16_t maxpacket;
uint16_t regval;
uint16_t status;
uint8_t epno;
bool epin;
bool bidi;
@ -2755,28 +2835,18 @@ static int pic32mx_epconfigure(struct usbdev_ep_s *ep,
/* Mark that we own the entry */
status = bdt->status;
status &= ~USB_BDT_UOWN;
bdt->status = 0;
bdt->addr = 0;
/* Set DATA1 to one because the first thing we will do when transmitting is
* toggle the bit
*/
bdtdbg("EP%d BDT IN [%p] {%08x, %08x}\n", epno, bdt, bdt->status, bdt->addr);
status |= USB_BDT_DATA1;
bdt->status = status;
/* Now do the same for the other buffer. */
bdtdbg("EP%d BDT IN [%p] {%08x, %08x}\n", epno, bdt, status, bdt->addr);
bdt++;
bdt->status = 0;
bdt->addr = 0;
/* Now do the same for the other buffer. The only difference is the
* we clear DATA1 (making it DATA0)
*/
bdt = &g_bdt[index+1];
status = bdt->status;
status &= ~(USB_BDT_UOWN | USB_BDT_DATA01);
bdt->status = status;
bdtdbg("EP%d BDT IN [%p] {%08x, %08x}\n", epno, bdt, status, bdt->addr);
bdtdbg("EP%d BDT IN [%p] {%08x, %08x}\n", epno, bdt, bdt->status, bdt->addr);
}
if (!epin || bidi)
@ -2787,29 +2857,18 @@ static int pic32mx_epconfigure(struct usbdev_ep_s *ep,
/* Mark that we own the entry */
status = bdt->status;
status &= ~USB_BDT_UOWN;
bdt->status = 0;
bdt->addr = 0;
/* Set DATA1 to one because the first thing we will do when transmitting is
* toggle the bit
*/
bdtdbg("EP%d BDT OUT [%p] {%08x, %08x}\n", epno, bdt, bdt->status, bdt->addr);
status |= USB_BDT_DATA1;
bdt->status = status;
/* Now do the same for the other buffer. */
bdtdbg("EP%d BDT OUT [%p] {%08x, %08x}\n", epno, bdt, status, bdt->addr);
bdt++;
bdt->status = 0;
bdt->addr = 0;
/* Now do the same for the other buffer. The only difference is the
* we clear DATA1 (making it DATA0)
*/
bdt = &g_bdt[index+1];
status = bdt->status & ~USB_BDT_UOWN;
status &= ~USB_BDT_DATA01;
bdt->status = status;
bdtdbg("EP%d BDT OUT [%p] {%08x, %08x}\n", epno, bdt, status, bdt->addr);
bdtdbg("EP%d BDT OUT [%p] {%08x, %08x}\n", epno, bdt, bdt->status, bdt->addr);
}
/* Get the maxpacket size of the endpoint. */
@ -3010,6 +3069,14 @@ static int pic32mx_epsubmit(struct usbdev_ep_s *ep, struct usbdev_req_s *req)
privep->txnullpkt = 0;
pic32mx_rqenqueue(privep, privreq);
usbtrace(TRACE_OUTREQQUEUED(epno), req->len);
/* Set up the read operation. Because the PIC32MX supports ping-pong
* buffering. There may be two pending read requests. The following
* call will attempt to setup a read using this request for this
* endpoint. It is not harmful if this fails.
*/
(void)pic32mx_rdrequest(priv, privep);
}
irqrestore(flags);
@ -3052,9 +3119,9 @@ static int pic32mx_epbdtstall(struct usbdev_ep_s *ep,
{
struct pic32mx_ep_s *privep;
struct pic32mx_usbdev_s *priv;
uint32_t status;
uint32_t regaddr;
uint16_t regval;
uint16_t status;
uint8_t epno;
/* Recover pointers */
@ -3605,7 +3672,6 @@ static void pic32mx_swreset(struct pic32mx_usbdev_s *priv)
privep->stalled = false;
privep->halted = false;
privep->txbusy = false;
privep->rxoverrun = false;
privep->txnullpkt = false;
}