Many changes in preparation for HID keyboard

git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@3253 42af7a65-404d-4744-a932-0658087f49c3
This commit is contained in:
patacongo 2011-01-15 22:30:23 +00:00
parent f8e8f1dec3
commit a8ddd74948

View File

@ -71,6 +71,8 @@
* Definitions
*******************************************************************************/
/* Configuration ***************************************************************/
/* All I/O buffers must lie in AHB SRAM because of the OHCI DMA. It might be
* okay if no I/O buffers are used *IF* the application can guarantee that all
* end-user I/O buffers reside in AHB SRAM.
@ -80,6 +82,7 @@
# warning "No IO buffers allocated"
#endif
/* OHCI Setup ******************************************************************/
/* Frame Interval / Periodic Start */
#define FI (12000-1) /* 12000 bits per frame (-1) */
@ -122,11 +125,6 @@
#define TDTAIL ((struct lpc17_gtd_s *)LPC17_TDTAIL_ADDR)
#define EDCTRL ((struct lpc17_ed_s *)LPC17_EDCTRL_ADDR)
#define EDFREE ((struct lpc17_ed_s *)LPC17_EDFREE_BASE)
#define TDFREE ((struct lpc17_gtd_s *)LPC17_TDFREE_BASE)
#define TBFREE ((uint8_t *)LPC17_TBFREE_BASE)
#define IOFREE ((uint8_t *)LPC17_IOFREE_BASE)
/* Descriptors *****************************************************************/
/* TD delay interrupt value */
@ -154,13 +152,11 @@ struct lpc17_usbhost_s
/* Driver status */
volatile uint8_t tdstatus; /* TD control status bits from last Writeback Done Head event */
volatile bool connected; /* Connected to device */
volatile bool lowspeed; /* Low speed device attached. */
volatile bool rhswait; /* TRUE: Thread is waiting for Root Hub Status change */
volatile bool wdhwait; /* TRUE: Thread is waiting for WDH interrupt */
sem_t exclsem; /* Support mutually exclusive access */
sem_t rhssem; /* Semaphore to wait Writeback Done Head event */
sem_t wdhsem; /* Semaphore used to wait for Writeback Done Head event */
};
/* The OCHI expects the size of an endpoint descriptor to be 16 bytes.
@ -178,7 +174,11 @@ struct lpc17_ed_s
/* Software specific fields */
uint8_t xfrtype; /* Transfer type. See SB_EP_ATTR_XFER_* in usb.h */
uint8_t pad[15];
volatile uint8_t tdstatus; /* TD control status bits from last Writeback Done Head event */
volatile bool wdhwait; /* TRUE: Thread is waiting for WDH interrupt */
uint8_t pad;
sem_t wdhsem; /* Semaphore used to wait for Writeback Done Head event */
/* Unused bytes follow, depending on the size of sem_t */
};
/* The OCHI expects the size of an transfer descriptor to be 16 bytes.
@ -235,13 +235,14 @@ static void lpc17_putle16(uint8_t *dest, uint16_t val);
/* Descriptor helper functions *************************************************/
static struct lpc17_gtd_s *lpc17_tdalloc(struct lpc17_usbhost_s *priv);
static void lpc17_tdfree(struct lpc17_usbhost_s *priv, struct lpc17_gtd_s *buffer);
static uint8_t *lpc17_tballoc(struct lpc17_usbhost_s *priv);
static void lpc17_tbfree(struct lpc17_usbhost_s *priv, uint8_t *buffer);
static inline void lpc17_edfree(struct lpc17_ed_s *ed);
static struct lpc17_gtd_s *lpc17_tdalloc(void);
static void lpc17_tdfree(struct lpc17_gtd_s *buffer);
static uint8_t *lpc17_tballoc(void);
static void lpc17_tbfree(uint8_t *buffer);
#if LPC17_IOBUFFERS > 0
static uint8_t *lpc17_ioalloc(struct lpc17_usbhost_s *priv);
static void lpc17_iofree(struct lpc17_usbhost_s *priv, uint8_t *buffer);
static uint8_t *lpc17_ioalloc(void);
static void lpc17_iofree(uint8_t *buffer);
#endif
static int lpc17_enqueuetd(struct lpc17_usbhost_s *priv,
struct lpc17_ed_s *ed, uint32_t dirpid,
@ -275,7 +276,11 @@ static int lpc17_ctrlout(FAR struct usbhost_driver_s *drvr,
static int lpc17_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
FAR uint8_t *buffer, size_t buflen);
static void lpc17_disconnect(FAR struct usbhost_driver_s *drvr);
/* Initialization **************************************************************/
static inline void lpc17_ep0init(struct lpc17_usbhost_s *priv);
/*******************************************************************************
* Private Data
*******************************************************************************/
@ -493,6 +498,24 @@ static void lpc17_putle16(uint8_t *dest, uint16_t val)
dest[1] = val >> 8;
}
/*******************************************************************************
* Name: lpc17_edfree
*
* Description:
* Return an endpoint descriptor to the free list
*
*******************************************************************************/
static inline void lpc17_edfree(struct lpc17_ed_s *ed)
{
struct lpc17_list_s *entry = (struct lpc17_list_s *)ed;
/* Put the ED back into the free list */
entry->flink = g_edfree;
g_edfree = entry;
}
/*******************************************************************************
* Name: lpc17_tdalloc
*
@ -500,12 +523,14 @@ static void lpc17_putle16(uint8_t *dest, uint16_t val)
* Allocate an transfer descriptor from the free list
*
* Assumptions:
* - Called from a single thread so no mutual exclusion is required.
* - Never called from an interrupt handler.
* - Protected from conconcurrent access to the TD pool by the interrupt
* handler
* - Protection from re-entrance must be assured by the caller
*
*******************************************************************************/
static struct lpc17_gtd_s *lpc17_tdalloc(struct lpc17_usbhost_s *priv)
static struct lpc17_gtd_s *lpc17_tdalloc(void)
{
struct lpc17_gtd_s *ret;
irqstate_t flags;
@ -537,7 +562,7 @@ static struct lpc17_gtd_s *lpc17_tdalloc(struct lpc17_usbhost_s *priv)
*
*******************************************************************************/
static void lpc17_tdfree(struct lpc17_usbhost_s *priv, struct lpc17_gtd_s *td)
static void lpc17_tdfree(struct lpc17_gtd_s *td)
{
struct lpc17_list_s *tdfree = (struct lpc17_list_s *)td;
@ -559,12 +584,12 @@ static void lpc17_tdfree(struct lpc17_usbhost_s *priv, struct lpc17_gtd_s *td)
* Allocate an request/descriptor transfer buffer from the free list
*
* Assumptions:
* - Called from a single thread so no mutual exclusion is required.
* - Never called from an interrupt handler.
* - Protection from re-entrance must be assured by the caller
*
*******************************************************************************/
static uint8_t *lpc17_tballoc(struct lpc17_usbhost_s *priv)
static uint8_t *lpc17_tballoc(void)
{
uint8_t *ret = (uint8_t *)g_tbfree;
if (ret)
@ -582,7 +607,7 @@ static uint8_t *lpc17_tballoc(struct lpc17_usbhost_s *priv)
*
*******************************************************************************/
static void lpc17_tbfree(struct lpc17_usbhost_s *priv, uint8_t *buffer)
static void lpc17_tbfree(uint8_t *buffer)
{
struct lpc17_list_s *tbfree = (struct lpc17_list_s *)buffer;
@ -600,13 +625,13 @@ static void lpc17_tbfree(struct lpc17_usbhost_s *priv, uint8_t *buffer)
* Allocate an IO buffer from the free list
*
* Assumptions:
* - Called from a single thread so no mutual exclusion is required.
* - Never called from an interrupt handler.
* - Protection from re-entrance must be assured by the caller
*
*******************************************************************************/
#if LPC17_IOBUFFERS > 0
static uint8_t *lpc17_ioalloc(struct lpc17_usbhost_s *priv)
static uint8_t *lpc17_ioalloc(void)
{
uint8_t *ret = (uint8_t *)g_iofree;
if (ret)
@ -626,7 +651,7 @@ static uint8_t *lpc17_ioalloc(struct lpc17_usbhost_s *priv)
*******************************************************************************/
#if LPC17_IOBUFFERS > 0
static void lpc17_iofree(struct lpc17_usbhost_s *priv, uint8_t *buffer)
static void lpc17_iofree(uint8_t *buffer)
{
struct lpc17_list_s *iofree = (struct lpc17_list_s *)buffer;
iofree->flink = g_iofree;
@ -652,7 +677,7 @@ static int lpc17_enqueuetd(struct lpc17_usbhost_s *priv,
/* Allocate a TD from the free list */
td = lpc17_tdalloc(priv);
td = lpc17_tdalloc();
if (td != NULL)
{
/* Initialize the allocated TD and link it before the common tail TD. */
@ -674,7 +699,6 @@ static int lpc17_enqueuetd(struct lpc17_usbhost_s *priv,
ed->hw.headp = (uint32_t)td | ((ed->hw.headp) & ED_HEADP_C);
ed->hw.tailp = (uint32_t)TDTAIL;
ed->hw.nexted = 0;
ret = OK;
}
@ -693,7 +717,7 @@ static int lpc17_enqueuetd(struct lpc17_usbhost_s *priv,
*
*******************************************************************************/
static int lpc17_wdhwait(struct lpc17_usbhost_s *priv)
static int lpc17_wdhwait(struct lpc17_usbhost_s *priv, struct lpc17_ed_s *ed)
{
irqstate_t flags = irqsave();
int ret = -ENODEV;
@ -706,8 +730,8 @@ static int lpc17_wdhwait(struct lpc17_usbhost_s *priv)
* either (1) the device is disconnected, or (2) the transfer completed.
*/
priv->wdhwait = true;
ret = OK;
ed->wdhwait = true;
ret = OK;
}
irqrestore(flags);
@ -739,7 +763,7 @@ static int lpc17_ctrltd(struct lpc17_usbhost_s *priv, uint32_t dirpid,
* transfer.
*/
ret = lpc17_wdhwait(priv);
ret = lpc17_wdhwait(priv, EDCTRL);
if (ret != OK)
{
udbg("ERROR: Device disconnected\n");
@ -759,16 +783,10 @@ static int lpc17_ctrltd(struct lpc17_usbhost_s *priv, uint32_t dirpid,
/* Then enqueue the transfer */
priv->tdstatus = TD_CC_NOERROR;
EDCTRL->tdstatus = TD_CC_NOERROR;
ret = lpc17_enqueuetd(priv, EDCTRL, dirpid, toggle, buffer, buflen);
if (ret == OK)
{
/* Set the head of the control list to the EP0 EDCTRL (this would have to
* change if we want more than on control EP queued at a time).
*/
lpc17_putreg(LPC17_EDCTRL_ADDR, LPC17_USBHOST_CTRLHEADED);
/* Set ControlListFilled. This bit is used to indicate whether there are
* TDs on the Control list.
*/
@ -777,36 +795,26 @@ static int lpc17_ctrltd(struct lpc17_usbhost_s *priv, uint32_t dirpid,
regval |= OHCI_CMDST_CLF;
lpc17_putreg(regval, LPC17_USBHOST_CMDST);
/* ControlListEnable. This bit is set to enable the processing of the
* Control list. Note: once enabled, it remains enabled and we may even
* complete list processing before we get the bit set. We really
* should never modify the control list while CLE is set.
*/
regval = lpc17_getreg(LPC17_USBHOST_CTRL);
regval |= OHCI_CTRL_CLE;
lpc17_putreg(regval, LPC17_USBHOST_CTRL);
/* Wait for the Writeback Done Head interrupt */
lpc17_takesem(&priv->wdhsem);
lpc17_takesem(&EDCTRL->wdhsem);
/* Check the TD completion status bits */
if (priv->tdstatus == TD_CC_NOERROR)
if (EDCTRL->tdstatus == TD_CC_NOERROR)
{
ret = OK;
}
else
{
uvdbg("Bad TD completion status: %d\n", priv->tdstatus);
uvdbg("Bad TD completion status: %d\n", EDCTRL->tdstatus);
ret = -EIO;
}
}
/* Make sure that there is no outstanding request on this endpoint */
priv->wdhwait = false;
EDCTRL->wdhwait = false;
return ret;
}
@ -868,7 +876,6 @@ static int lpc17_usbinterrupt(int irq, FAR void *context)
/* Yes.. connected. */
ullvdbg("Connected\n");
priv->tdstatus = TD_CC_NOERROR;
priv->connected = true;
/* Notify any waiters */
@ -949,17 +956,10 @@ static int lpc17_usbinterrupt(int irq, FAR void *context)
struct lpc17_gtd_s *next;
/* The host controller just wrote the list of finished TDs into the HCCA
* done head. Here we assume that only a single packet is "in flight"
* at any given time.
*/
/* Since there is only one TD, we can disable further TD processing. */
#warning "This logic needs to be removed"
regval = lpc17_getreg(LPC17_USBHOST_CTRL);
regval &= ~(OHCI_CTRL_PLE|OHCI_CTRL_IE|OHCI_CTRL_CLE|OHCI_CTRL_BLE);
lpc17_putreg(regval, LPC17_USBHOST_CTRL);
/* Remove the TD(s) from the Writeback Done Head in the HCCA and return
* done head. This may include multiple packets that were transferred
* in the preceding frame.
*
* Remove the TD(s) from the Writeback Done Head in the HCCA and return
* them to the free list. Note that this is safe because the hardware
* will not modify the writeback done head again until the WDH bit is
* cleared in the interrupt status register.
@ -972,20 +972,24 @@ static int lpc17_usbinterrupt(int irq, FAR void *context)
for (; td; td = next)
{
/* Get the condition code from the (single) TD status/control
* word. This obviously will not work if/when there are more
* one TD in the done list.
/* Get the ED in which this TD was enqueued */
struct lpc17_ed_s *ed = td->ed;
DEBUGASSERT(ed != NULL);
/* Save the condition code from the (single) TD status/control
* word.
*/
priv->tdstatus = (td->hw.ctrl & GTD_STATUS_CC_MASK) >> GTD_STATUS_CC_SHIFT;
ed->tdstatus = (td->hw.ctrl & GTD_STATUS_CC_MASK) >> GTD_STATUS_CC_SHIFT;
#ifdef CONFIG_DEBUG_USB
if (priv->tdstatus != TD_CC_NOERROR)
if (ed->tdstatus != TD_CC_NOERROR)
{
/* The transfer failed for some reason... dump some diagnostic info. */
ulldbg("ERROR: TD CTRL:%08x/CC:%d RHPORTST1:%08x\n",
td->hw.ctrl, priv->tdstatus,
ulldbg("ERROR: ED: xfrtype:%d TD CTRL:%08x/CC:%d RHPORTST1:%08x\n",
ed->xfrtype, td->hw.ctrl, ed->tdstatus,
lpc17_getreg(LPC17_USBHOST_RHPORTST1));
}
#endif
@ -993,15 +997,15 @@ static int lpc17_usbinterrupt(int irq, FAR void *context)
/* Return the TD to the free list */
next = (struct lpc17_gtd_s *)td->hw.nexttd;
lpc17_tdfree(priv, td);
}
lpc17_tdfree(td);
/* And wake up the thread waiting for the WDH event */
/* And wake up the thread waiting for the WDH event */
if (priv->wdhwait)
{
lpc17_givesem(&priv->wdhsem);
priv->wdhwait = false;
if (ed->wdhwait)
{
lpc17_givesem(&ed->wdhsem);
ed->wdhwait = false;
}
}
}
@ -1170,6 +1174,10 @@ static int lpc17_ep0configure(FAR struct usbhost_driver_s *drvr, uint8_t funcadd
DEBUGASSERT(drvr && funcaddr < 128 && maxpacketsize < 2048);
/* We must have exclusive access to EP0 and the control list */
lpc17_takesem(&priv->exclsem);
/* Set the EP0 ED control word */
EDCTRL->hw.ctrl = (uint32_t)funcaddr << ED_CONTROL_FA_SHIFT |
@ -1183,6 +1191,7 @@ static int lpc17_ep0configure(FAR struct usbhost_driver_s *drvr, uint8_t funcadd
/* Set the transfer type to control */
EDCTRL->xfrtype = USB_EP_ATTR_XFER_CONTROL;
lpc17_givesem(&priv->exclsem);
uvdbg("EP0 CTRL:%08x\n", EDCTRL->hw.ctrl);
return OK;
@ -1214,8 +1223,9 @@ static int lpc17_epalloc(FAR struct usbhost_driver_s *drvr,
const FAR struct usbhost_epdesc_s *epdesc, usbhost_ep_t *ep)
{
struct lpc17_usbhost_s *priv = (struct lpc17_usbhost_s *)drvr;
struct lpc17_ed_s *ed;
int ret = -ENOMEM;
struct lpc17_ed_s *ed;
uint32_t regval;
int ret = -ENOMEM;
/* Sanity check. NOTE that this method should only be called if a device is
* connected (because we need a valid low speed indication).
@ -1223,6 +1233,12 @@ static int lpc17_epalloc(FAR struct usbhost_driver_s *drvr,
DEBUGASSERT(priv && epdesc && ep && priv->connected);
/* We must have exclusive access to the ED pool, the bulk list, the periodic list
* and the interrupt table.
*/
lpc17_takesem(&priv->exclsem);
/* Take the next ED from the beginning of the free list */
ed = (struct lpc17_ed_s *)g_edfree;
@ -1271,14 +1287,41 @@ static int lpc17_epalloc(FAR struct usbhost_driver_s *drvr,
#endif
uvdbg("EP%d CTRL:%08x\n", epdesc->addr, ed->hw.ctrl);
/* Initialize the semaphore that is used to wait for the endpoint
* WDH event.
*/
sem_init(&ed->wdhsem, 0, 0);
/* Now add the endpoint descriptor to the appropriate list */
#warning "Missing logic"
#warning "Missing logic for other endpoint types"
/* Link the common tail TD to the ED's TD list */
ed->hw.headp = (uint32_t)TDTAIL;
ed->hw.tailp = (uint32_t)TDTAIL;
/* Add the new bulk ED to the head of the bulk list */
ed->hw.nexted = lpc17_getreg(LPC17_USBHOST_BULKHEADED);
lpc17_putreg((uint32_t)ed, LPC17_USBHOST_BULKHEADED);
/* BulkListEnable. This bit is set to enable the processing of the Bulk
* list. Note: once enabled, it remains. We really should never modify
* the bulk list while BLE is set.
*/
regval = lpc17_getreg(LPC17_USBHOST_CTRL);
regval |= OHCI_CTRL_BLE;
lpc17_putreg(regval, LPC17_USBHOST_CTRL);
/* Return an opaque reference to the ED */
*ep = (usbhost_ep_t)ed;
ret = OK;
}
lpc17_givesem(&priv->exclsem);
return ret;
}
@ -1304,14 +1347,71 @@ static int lpc17_epalloc(FAR struct usbhost_driver_s *drvr,
static int lpc17_epfree(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep)
{
struct lpc17_list_s *ed = (struct lpc17_list_s *)ep;
struct lpc17_usbhost_s *priv = (struct lpc17_usbhost_s *)drvr;
struct lpc17_ed_s *ed = (struct lpc17_ed_s *)ep;
struct lpc17_ed_s *curr = NULL;
struct lpc17_ed_s *prev = NULL;
uint32_t regval;
DEBUGASSERT(ed);
/* There should not be any pending, real TDs linked to this ED */
DEBUGASSERT(ed && (ed->hw.headp & ED_HEADP_ADDR_MASK) == LPC17_TDTAIL_ADDR);
/* We must have exclusive access to the ED pool, the bulk list, the periodic list
* and the interrupt table.
*/
lpc17_takesem(&priv->exclsem);
/* Find the ED in the bulk list. NOTE: We really should never be mucking
* with the bulk list while BLE is set.
*/
for (curr = (struct lpc17_ed_s *)lpc17_getreg(LPC17_USBHOST_BULKHEADED),
prev = NULL;
curr && curr != ed;
prev = curr, curr = (struct lpc17_ed_s *)curr->hw.nexted);
/* Hmmm.. It would be a bug if we do not find the ED in the bulk list. */
DEBUGASSERT(curr != NULL);
/* Remove the ED from the bulk list */
if (curr != NULL)
{
/* Is this ED the first on in the bulk list? */
if (prev == NULL)
{
/* Yes... set the head of the bulk list to skip over this ED */
lpc17_putreg(ed->hw.nexted, LPC17_USBHOST_BULKHEADED);
/* If the bulk list is now empty, then disable it */
regval = lpc17_getreg(LPC17_USBHOST_CTRL);
regval &= ~OHCI_CTRL_BLE;
lpc17_putreg(regval, LPC17_USBHOST_CTRL);
}
else
{
/* No.. set the forward link of the previous ED in the list
* skip over this ED.
*/
prev->hw.nexted = ed->hw.nexted;
}
}
/* Destroy the semaphore */
sem_destroy(&ed->wdhsem);
/* Put the ED back into the free list */
ed->flink = g_edfree;
g_edfree = ed;
lpc17_edfree(ed);
lpc17_givesem(&priv->exclsem);
return OK;
}
@ -1347,14 +1447,21 @@ static int lpc17_alloc(FAR struct usbhost_driver_s *drvr,
{
struct lpc17_usbhost_s *priv = (struct lpc17_usbhost_s *)drvr;
DEBUGASSERT(priv && buffer && maxlen);
int ret = -ENOMEM;
*buffer = lpc17_tballoc(priv);
/* We must have exclusive access to the transfer buffer pool */
lpc17_takesem(&priv->exclsem);
*buffer = lpc17_tballoc();
if (*buffer)
{
*maxlen = CONFIG_USBHOST_TDBUFSIZE;
return OK;
ret = OK;
}
return -ENOMEM;
lpc17_givesem(&priv->exclsem);
return ret;
}
/*******************************************************************************
@ -1376,7 +1483,6 @@ static int lpc17_alloc(FAR struct usbhost_driver_s *drvr,
* returned indicating the nature of the failure
*
* Assumptions:
* - Called from a single thread so no mutual exclusion is required.
* - Never called from an interrupt handler.
*
*******************************************************************************/
@ -1384,8 +1490,13 @@ static int lpc17_alloc(FAR struct usbhost_driver_s *drvr,
static int lpc17_free(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer)
{
struct lpc17_usbhost_s *priv = (struct lpc17_usbhost_s *)drvr;
DEBUGASSERT(priv && buffer);
lpc17_tbfree(priv, buffer);
DEBUGASSERT(buffer);
/* We must have exclusive access to the transfer buffer pool */
lpc17_takesem(&priv->exclsem);
lpc17_tbfree(buffer);
lpc17_givesem(&priv->exclsem);
return OK;
}
@ -1437,6 +1548,10 @@ static int lpc17_ctrlin(FAR struct usbhost_driver_s *drvr,
req->type, req->req, req->value[1], req->value[0],
req->index[1], req->index[0], req->len[1], req->len[0]);
/* We must have exclusive access to EP0 and the control list */
lpc17_takesem(&priv->exclsem);
len = lpc17_getle16(req->len);
ret = lpc17_ctrltd(priv, GTD_STATUS_DP_SETUP, (uint8_t*)req, USB_SIZEOF_CTRLREQ);
if (ret == OK)
@ -1451,6 +1566,8 @@ static int lpc17_ctrlin(FAR struct usbhost_driver_s *drvr,
ret = lpc17_ctrltd(priv, GTD_STATUS_DP_OUT, NULL, 0);
}
}
lpc17_givesem(&priv->exclsem);
return ret;
}
@ -1467,6 +1584,10 @@ static int lpc17_ctrlout(FAR struct usbhost_driver_s *drvr,
req->type, req->req, req->value[1], req->value[0],
req->index[1], req->index[0], req->len[1], req->len[0]);
/* We must have exclusive access to EP0 and the control list */
lpc17_takesem(&priv->exclsem);
len = lpc17_getle16(req->len);
ret = lpc17_ctrltd(priv, GTD_STATUS_DP_SETUP, (uint8_t*)req, USB_SIZEOF_CTRLREQ);
if (ret == OK)
@ -1481,6 +1602,8 @@ static int lpc17_ctrlout(FAR struct usbhost_driver_s *drvr,
ret = lpc17_ctrltd(priv, GTD_STATUS_DP_IN, NULL, 0);
}
}
lpc17_givesem(&priv->exclsem);
return ret;
}
@ -1515,7 +1638,7 @@ static int lpc17_ctrlout(FAR struct usbhost_driver_s *drvr,
* - Never called from an interrupt handler.
*
*******************************************************************************/
static int lpc17_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
FAR uint8_t *buffer, size_t buflen)
{
@ -1539,6 +1662,12 @@ static int lpc17_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
(ed->hw.ctrl & ED_CONTROL_MPS_MASK) >> ED_CONTROL_MPS_SHIFT,
buflen);
/* We must have exclusive access to the endpoint, the TD pool, the I/O buffer
* pool, the bulk and interrupt lists, and the HCCA interrupt table.
*/
lpc17_takesem(&priv->exclsem);
/* Allocate an IO buffer if the user buffer does not lie in AHB SRAM */
#if LPC17_IOBUFFERS > 0
@ -1558,7 +1687,7 @@ static int lpc17_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
/* Allocate an IO buffer in AHB SRAM */
origbuf = buffer;
buffer = lpc17_ioalloc(priv);
buffer = lpc17_ioalloc();
if (!buffer)
{
uvdbg("IO buffer allocation failed\n");
@ -1583,7 +1712,7 @@ static int lpc17_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
* transfer.
*/
ret = lpc17_wdhwait(priv);
ret = lpc17_wdhwait(priv, ed);
if (ret != OK)
{
udbg("ERROR: Device disconnected\n");
@ -1603,16 +1732,10 @@ static int lpc17_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
/* Then enqueue the transfer */
priv->tdstatus = TD_CC_NOERROR;
ed->tdstatus = TD_CC_NOERROR;
ret = lpc17_enqueuetd(priv, ed, dirpid, GTD_STATUS_T_TOGGLE, buffer, buflen);
if (ret == OK)
{
/* Set the head of the bulk list to the EP descriptor (this would have to
* change if we want more than on bulk EP queued at a time).
*/
lpc17_putreg((uint32_t)ed, LPC17_USBHOST_BULKHEADED);
/* BulkListFilled. This bit is used to indicate whether there are any
* TDs on the Bulk list.
*/
@ -1621,29 +1744,19 @@ static int lpc17_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
regval |= OHCI_CMDST_BLF;
lpc17_putreg(regval, LPC17_USBHOST_CMDST);
/* BulkListEnable. This bit is set to enable the processing of the Bulk
* list. Note: once enabled, it remains enabled and we may even
* complete list processing before we get the bit set. We really
* should never modify the bulk list while BLE is set.
*/
regval = lpc17_getreg(LPC17_USBHOST_CTRL);
regval |= OHCI_CTRL_BLE;
lpc17_putreg(regval, LPC17_USBHOST_CTRL);
/* Wait for the Writeback Done Head interrupt */
lpc17_takesem(&priv->wdhsem);
lpc17_takesem(&ed->wdhsem);
/* Check the TD completion status bits */
if (priv->tdstatus == TD_CC_NOERROR)
if (ed->tdstatus == TD_CC_NOERROR)
{
ret = OK;
}
else
{
uvdbg("Bad TD completion status: %d\n", priv->tdstatus);
uvdbg("Bad TD completion status: %d\n", ed->tdstatus);
ret = -EIO;
}
}
@ -1651,7 +1764,7 @@ static int lpc17_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
errout:
/* Make sure that there is no outstanding request on this endpoint */
priv->wdhwait = false;
ed->wdhwait = false;
/* Free any temporary IO buffers */
@ -1671,10 +1784,11 @@ errout:
/* Then free the temporary I/O buffer */
lpc17_iofree(priv, buffer);
lpc17_iofree(buffer);
}
#endif
lpc17_givesem(&priv->exclsem);
return ret;
}
@ -1697,7 +1811,6 @@ errout:
*
* Assumptions:
* - Only a single class bound to a single device is supported.
* - Called from a single thread so no mutual exclusion is required.
* - Never called from an interrupt handler.
*
*******************************************************************************/
@ -1708,6 +1821,60 @@ static void lpc17_disconnect(FAR struct usbhost_driver_s *drvr)
priv->class = NULL;
}
/*******************************************************************************
* Initialization
*******************************************************************************/
/*******************************************************************************
* Name: lpc17_ep0init
*
* Description:
* Initialize ED for EP0, add it to the control ED list, and enable control
* transfers.
*
* Input Parameters:
* priv - private driver state instance.
*
* Returned Values:
* None
*
*******************************************************************************/
static inline void lpc17_ep0init(struct lpc17_usbhost_s *priv)
{
uint32_t regval;
/* Set up some default values */
(void)lpc17_ep0configure(&priv->drvr, 1, 8);
/* Initialize the common tail TD. */
memset(TDTAIL, 0, sizeof(struct lpc17_gtd_s));
TDTAIL->ed = EDCTRL;
/* Link the common tail TD to the ED's TD list */
memset(EDCTRL, 0, sizeof(struct lpc17_ed_s));
EDCTRL->hw.headp = (uint32_t)TDTAIL;
EDCTRL->hw.tailp = (uint32_t)TDTAIL;
/* Set the head of the control list to the EP0 EDCTRL (this would have to
* change if we want more than on control EP queued at a time).
*/
lpc17_putreg(LPC17_EDCTRL_ADDR, LPC17_USBHOST_CTRLHEADED);
/* ControlListEnable. This bit is set to enable the processing of the
* Control list. Note: once enabled, it remains enabled and we may even
* complete list processing before we get the bit set. We really
* should never modify the control list while CLE is set.
*/
regval = lpc17_getreg(LPC17_USBHOST_CTRL);
regval |= OHCI_CTRL_CLE;
lpc17_putreg(regval, LPC17_USBHOST_CTRL);
}
/*******************************************************************************
* Public Functions
*******************************************************************************/
@ -1745,12 +1912,18 @@ FAR struct usbhost_driver_s *usbhost_initialize(int controller)
irqstate_t flags;
int i;
/* Sanity checks. NOTE: If certain OS features are enabled, it may be
* necessary to increase the size of LPC17_ED/TD_SIZE in lpc17_ohciram.h
*/
DEBUGASSERT(controller == 0);
DEBUGASSERT(sizeof(struct lpc17_ed_s) <= LPC17_ED_SIZE);
DEBUGASSERT(sizeof(struct lpc17_gtd_s) <= LPC17_TD_SIZE);
/* Initialize the state data structure */
sem_init(&priv->rhssem, 0, 0);
sem_init(&priv->wdhsem, 0, 0);
sem_init(&priv->rhssem, 0, 0);
sem_init(&priv->exclsem, 0, 1);
/* Enable power by setting PCUSB in the PCONP register. Disable interrupts
* because this register may be shared with other drivers.
@ -1827,45 +2000,50 @@ FAR struct usbhost_driver_s *usbhost_initialize(int controller)
memset((void*)HCCA, 0, sizeof(struct ohci_hcca_s));
memset((void*)TDTAIL, 0, sizeof(struct ohci_gtd_s));
memset((void*)EDCTRL, 0, sizeof(struct lpc17_ed_s));
sem_init(&EDCTRL->wdhsem, 0, 0);
/* Initialize user-configurable EDs */
buffer = (uint8_t *)LPC17_EDFREE_BASE;
for (i = 0; i < CONFIG_USBHOST_NEDS; i++)
{
/* Put the ED in a free list */
lpc17_epfree(&priv->drvr, (usbhost_ep_t)&EDFREE[i]);
lpc17_edfree((struct lpc17_ed_s *)buffer);
buffer += LPC17_ED_SIZE;
}
/* Initialize user-configurable TDs */
buffer = (uint8_t *)LPC17_TDFREE_BASE;
for (i = 0; i < CONFIG_USBHOST_NTDS; i++)
{
/* Put the ED in a free list */
lpc17_tdfree(priv, &TDFREE[i]);
lpc17_tdfree((struct lpc17_gtd_s *)buffer);
buffer += LPC17_TD_SIZE;
}
/* Initialize user-configurable request/descriptor transfer buffers */
buffer = TBFREE;
buffer = (uint8_t *)LPC17_TBFREE_BASE;
for (i = 0; i < CONFIG_USBHOST_NEDS; i++)
{
/* Put the TD buffer in a free list */
lpc17_tbfree(priv, buffer);
lpc17_tbfree(buffer);
buffer += CONFIG_USBHOST_TDBUFSIZE;
}
#if LPC17_IOBUFFERS > 0
/* Initialize user-configurable IO buffers */
buffer = IOFREE;
buffer = (uint8_t *)LPC17_IOFREE_BASE;
for (i = 0; i < LPC17_IOBUFFERS; i++)
{
/* Put the IO buffer in a free list */
lpc17_iofree(priv, buffer);
lpc17_iofree(buffer);
buffer += CONFIG_USBHOST_IOBUFSIZE;
}
#endif
@ -1904,6 +2082,10 @@ FAR struct usbhost_driver_s *usbhost_initialize(int controller)
lpc17_putreg((uint32_t)HCCA, LPC17_USBHOST_HCCA);
/* Set up EP0 */
lpc17_ep0init(priv);
/* Clear pending interrupts */
regval = lpc17_getreg(LPC17_USBHOST_INTST);