Add a little more

git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@3201 42af7a65-404d-4744-a932-0658087f49c3
This commit is contained in:
patacongo 2010-12-20 20:59:12 +00:00
parent 55a5671383
commit f9c678f4d0
2 changed files with 365 additions and 41 deletions

View File

@ -144,7 +144,12 @@
#if CONFIG_USBHOST_TDBUFFERS > 0 && !defined(CONFIG_USBHOST_TDBUFSIZE)
# define CONFIG_USBHOST_TDBUFSIZE 128
#endif
#define LPC17_TDBUFFER_SIZE (CONFIG_USBHOST_TDBUFFERS * CONFIG_USBHOST_TDBUFSIZE)
#if (CONFIG_USBHOST_TDBUFSIZE & 3) != 0
# error "TD buffer size must be an even number of 32-bit words"
#endif
#define LPC17_TDFREE_SIZE (CONFIG_USBHOST_TDBUFFERS * CONFIG_USBHOST_TDBUFSIZE)
/* Configurable size of an IO buffer. The number of IO buffers will be determined
* by what is left at the end of the BANK1 memory setup aside of OHCI RAM.
@ -154,6 +159,10 @@
# define CONFIG_USBHOST_IOBUFSIZE 512
#endif
#if (CONFIG_USBHOST_IOBUFSIZE & 3) != 0
# error "IO buffer size must be an even number of 32-bit words"
#endif
/* OHCI Memory Layout ***************************************************************/
/* Example:
* Hardware:
@ -171,8 +180,8 @@
* Sizes of things
* CONFIG_USBHOST_NEDS 2
* LPC17_EDFREE_SIZE 48
* LPC17_TDBUFFER_SIZE 128
* LPC17_TDBUFFER_SIZE 512
* LPC17_TDFREE_SIZE 128
* LPC17_IOFREE_SIZE 512
*
* Memory Layout
* LPC17_OHCIRAM_END (0x20008000 + 16384) = 0x2000c000
@ -186,8 +195,8 @@
* LPC17_TDTAIL_ADDR 0x2000bd10
* LPC17_EDCTRL_ADDR 0x2000bd20
* LPC17_EDFREE_BASE 0x2000bd30
* LPC17_TDBUFFER_BASE 0x2000bd50
* LPC17_IOBUFFER_BASE 0x2000bdd0
* LPC17_TDFREE_BASE 0x2000bd50
* LPC17_IOFREE_BASE 0x2000bdd0
* LPC17_IOBUFFERS (0x2000c000 + 0x2000bdd0) / 512 = 560/512 = 1
*
* Wasted memory: 560-512 = 48 bytes
@ -198,8 +207,8 @@
#define LPC17_TDTAIL_ADDR (LPC17_TDHEAD_ADDR + LPC17_TD_SIZE)
#define LPC17_EDCTRL_ADDR (LPC17_TDTAIL_ADDR + LPC17_TD_SIZE)
#define LPC17_EDFREE_BASE (LPC17_EDCTRL_ADDR + LPC17_ED_SIZE)
#define LPC17_TDBUFFER_BASE (LPC17_EDFREE_BASE + LPC17_EDFREE_SIZE)
#define LPC17_IOBUFFER_BASE (LPC17_TDBUFFER_BASE + LPC17_TDBUFFER_SIZE)
#define LPC17_TDFREE_BASE (LPC17_EDFREE_BASE + LPC17_EDFREE_SIZE)
#define LPC17_IOFREE_BASE (LPC17_TDFREE_BASE + LPC17_TDFREE_SIZE)
/* Finally, use the remainder of the allocated OHCI for IO buffers */

View File

@ -104,7 +104,7 @@
#define EDCTRL ((volatile struct lpc17_hced_s *)LPC17_EDCTRL_ADDR)
#define EDFREE ((struct lpc17_hced_s *)LPC17_EDFREE_BASE)
#define TDBuffer ((volatile uint8_t *)(LPC17_TDBUFFER_BASE)
#define TDFREE ((uint8_t *)LPC17_TDFREE_BASE)
/* Descriptors *****************************************************************/
@ -141,8 +141,10 @@ struct lpc17_usbhost_s
/* Driver status */
uint8_t tdstatus; /* TD control status bits from last Writeback Done Head event */
sem_t wdhsem; /* Semaphore used to wait for Writeback Done Head event */
volatile uint8_t tdstatus; /* TD control status bits from last Writeback Done Head event */
volatile bool connected; /* Connected to device */
sem_t rhssem; /* Semaphore to wait Root Hub Status change */
sem_t wdhsem; /* Semaphore used to wait for Writeback Done Head event */
};
/* Host Controller Communication Area */
@ -176,7 +178,7 @@ struct lpc17_hced_s
volatile uint32_t next; /* Physical address of next Endpoint descriptor */
};
/* The following is used manage a list of free EDs */
/* The following are used to manage lists of free EDs and TD buffers*/
struct lpc17_edlist_s
{
@ -184,6 +186,12 @@ struct lpc17_edlist_s
uint32_t pad[3]; /* To make the same size as struct lpc17_hced_s */
};
struct lpc17_tdlist_s
{
struct lpc17_tdlist_s *flink; /* Link to next TD buffer in the list */
/* Variable length buffer data follows */
};
/*******************************************************************************
* Private Function Prototypes
*******************************************************************************/
@ -217,6 +225,8 @@ static void lpc17_edfree(struct lpc17_usbhost_s *priv, struct lpc17_hced_s *ed);
static void lpc17_enqueuetd(volatile struct lpc17_hced_s *ed, uint32_t dirpid,
uint32_t toggle, volatile uint8_t *buffer,
size_t buflen);
static int lpc17_ctrltd(struct lpc17_usbhost_s *priv, uint32_t dirpid,
uint8_t *buffer, size_t buflen);
/* Class helper functions ******************************************************/
@ -277,9 +287,10 @@ static struct lpc17_usbhost_s g_usbhost =
.class = NULL,
};
/* This is a free list of EDs */
/* This is a free list of EDs and TD buffers */
static struct lpc17_edlist_s *g_edfree;
static struct lpc17_tdlist_s *g_tdfree;
/*******************************************************************************
* Public Data
@ -418,7 +429,7 @@ static void lpc17_putreg(uint32_t val, uint32_t addr)
* This is just a wrapper to handle the annoying behavior of semaphore
* waits that return due to the receipt of a signal.
*
****************************************************************************/
*******************************************************************************/
static void lpc17_takesem(sem_t *sem)
{
@ -440,7 +451,7 @@ static void lpc17_takesem(sem_t *sem)
* Description:
* Get a (possibly unaligned) 16-bit little endian value.
*
****************************************************************************/
*******************************************************************************/
static inline uint16_t lpc17_getle16(const uint8_t *val)
{
@ -453,7 +464,7 @@ static inline uint16_t lpc17_getle16(const uint8_t *val)
* Description:
* Put a (possibly unaligned) 16-bit little endian value.
*
****************************************************************************/
*******************************************************************************/
static void lpc17_putle16(uint8_t *dest, uint16_t val)
{
@ -494,6 +505,39 @@ static void lpc17_edfree(struct lpc17_usbhost_s *priv, struct lpc17_hced_s *ed)
g_edfree = edfree;
}
/*******************************************************************************
* Name: lpc17_tdalloc
*
* Description:
* Allocate an TD buffer from the free list
*
*******************************************************************************/
static uint8_t *lpc17_tdalloc(struct lpc17_usbhost_s *priv)
{
uint8_t *ret = (uint8_t *)g_tdfree;
if (ret)
{
g_tdfree = ((struct lpc17_tdlist_s*)ret)->flink;
}
return ret;
}
/*******************************************************************************
* Name: lpc17_tdfree
*
* Description:
* Return an TD buffer to the free list
*
*******************************************************************************/
static void lpc17_tdfree(struct lpc17_usbhost_s *priv, uint8_t *buffer)
{
struct lpc17_tdlist_s *tdfree = (struct lpc17_tdlist_s *)buffer;
tdfree->flink = g_tdfree;
g_tdfree = tdfree;
}
/*******************************************************************************
* Name: lpc17_enqueuetd
*
@ -519,14 +563,73 @@ static void lpc17_enqueuetd(volatile struct lpc17_hced_s *ed, uint32_t dirpid,
ed->next = 0;
}
/****************************************************************************
* Name: lpc17_classbind
/*******************************************************************************
* Name: lpc17_configdesc
*
* Description:
* Process a IN or OUT request on the control endpoint. This function
* will enqueue the request and wait for it to complete. Only one transfer
* may be queued; Neither these methods nor the transfer() method can be
* called again until the control transfer functions returns.
*
* These are blocking methods; these functions will not return until the
* control transfer has completed.
*
*******************************************************************************/
static int lpc17_ctrltd(struct lpc17_usbhost_s *priv, uint32_t dirpid,
uint8_t *buffer, size_t buflen)
{
uint32_t toggle;
uint32_t regval;
if (dirpid == GTD_STATUS_DP_SETUP)
{
toggle = GTD_STATUS_T_DATA0;
}
else
{
toggle = GTD_STATUS_T_DATA1;
}
/* Then enqueue the transfer */
lpc17_enqueuetd(EDCTRL, dirpid, toggle, buffer, buflen);
lpc17_putreg(LPC17_EDCTRL_ADDR, LPC17_USBHOST_CTRLHEADED);
regval = lpc17_getreg(LPC17_USBHOST_CMDST);
regval |= OHCI_CMDST_CLF;
lpc17_putreg(regval, LPC17_USBHOST_CMDST);
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);
/* Check the TDHEAD completion status bits */
if (priv->tdstatus == 0)
{
return OK;
}
else
{
return -EIO;
}
}
/*******************************************************************************
* Name: lpc17_configdesc
*
* Description:
* A configuration descriptor has been obtained from the device. Find the
* ID information for the class that supports this device.
*
****************************************************************************/
*******************************************************************************/
static inline int
lpc17_configdesc(struct lpc17_usbhost_s *priv, const uint8_t *configdesc,
@ -591,14 +694,14 @@ lpc17_configdesc(struct lpc17_usbhost_s *priv, const uint8_t *configdesc,
return -ENOENT;
}
/****************************************************************************
/*******************************************************************************
* Name: lpc17_classbind
*
* Description:
* A configuration descriptor has been obtained from the device. Try to
* bind this configuration descriptor with a supported class.
*
****************************************************************************/
*******************************************************************************/
static int lpc17_classbind(struct lpc17_usbhost_s *priv,
const uint8_t *configdesc, int desclen)
@ -664,7 +767,114 @@ static int lpc17_usbinterrupt(int irq, FAR void *context)
/* Read the device interrupt status register */
usbtrace(TRACE_INTENTRY(LPC17_TRACEINTID_USB), 0xbeef);
#warning "Not implemented"
uint32_t intstatus;
uint32_t intenable;
/* Read Interrupt Status and mask out interrupts that are not enabled. */
intstatus = lpc17_getreg(LPC17_USBHOST_INTST);
intenable = lpc17_getreg(LPC17_USBHOST_INTEN);
intstatus &= intenable;
if (intstatus != 0)
{
/* Root hub status change interrupt */
if ((intstatus & OHCI_INT_RHSC) != 0)
{
uint32_t rhportst1 = lpc17_getreg(LPC17_USBHOST_RHPORTST1);
ullvdbg("Root Hub Status Change, RHPORTST: %08x\n", portstatus);
if ((rhportst1 & OHCI_RHPORTST_CSC) != 0)
{
uint32_t rhstatus = lpc17_getreg(LPC17_USBHOST_RHSTATUS);
ullvdbg("Connect Status Change, RHSTATUS: %08x\n", portstatus);
/* If DRWE is set, Connect Status Change indicates a remote wake-up event */
if (rhstatus & OHCI_RHSTATUS_DRWE)
{
ullvdbg("DRWE: Remote wake-up\n");
}
/* Otherwise... Not a remote wake-up event */
else
{
/* Check if we are not connected */
if ((rhportst1 & OHCI_RHPORTST_CCS) != 0)
{
if (!priv->connected)
{
DEBUGASSERT(priv->rhssem.semcount <= 0);
priv->tdstatus = 0;
priv->connected = true;
lpc17_givesem(&priv->rhssem);
}
else
{
ulldbg("Spurious status change (connected)\n");
}
}
/* Check if we are now disconnected */
else if (priv->connected)
{
/* Yes.. disable interrupts */
lpc17_putreg(0, LPC17_USBHOST_INTEN);
priv->connected = false;
/* Are we bound to a class instance? */
if (priv->class)
{
/* Yes.. Disconnect the class */
CLASS_DISCONNECTED(priv->class);
}
}
else
{
ulldbg("Spurious status change (disconnected)\n");
}
}
/* Clear the CSC interrupt */
lpc17_putreg(OHCI_RHPORTST_CSC, LPC17_USBHOST_RHPORTST1);
}
/* Check for port reset status change */
if ((rhportst1 & OHCI_RHPORTST_PRSC) != 0)
{
/* Release the RH port from reset */
lpc17_putreg(OHCI_RHPORTST_PRSC, LPC17_USBHOST_RHPORTST1);
}
}
/* Writeback Done Head interrupt */
if ((intstatus & OHCI_INT_WDH) != 0)
{
/* Get the condition code from the control word */
priv->tdstatus = (TDHEAD->ctrl & GTD_STATUS_CC_MASK) >> GTD_STATUS_CC_SHIFT;
/* And wake up the thread waiting for the WDH event */
DEBUGASSERT(priv->wdhsem.semcount <= 0);
lpc17_givesem(&priv->wdhsem);
}
/* Clear interrupt status register */
lpc17_putreg(intstatus, LPC17_USBHOST_INTST);
}
usbtrace(TRACE_INTEXIT(LPC17_TRACEINTID_USB), 0);
return OK;
}
@ -673,7 +883,7 @@ static int lpc17_usbinterrupt(int irq, FAR void *context)
* USB Host Controller Operations
*******************************************************************************/
/************************************************************************************
/*******************************************************************************
* Name: lpc17_enumerate
*
* Description:
@ -698,7 +908,7 @@ static int lpc17_usbinterrupt(int irq, FAR void *context)
* Assumptions:
* This function will *not* be called from an interrupt handler.
*
************************************************************************************/
*******************************************************************************/
static int lpc17_enumerate(FAR struct usbhost_driver_s *drvr)
{
@ -706,7 +916,7 @@ static int lpc17_enumerate(FAR struct usbhost_driver_s *drvr)
return -ENOSYS;
}
/************************************************************************************
/*******************************************************************************
* Name: lpc17_alloc
*
* Description:
@ -730,7 +940,7 @@ static int lpc17_enumerate(FAR struct usbhost_driver_s *drvr)
* Assumptions:
* This function will *not* be called from an interrupt handler.
*
************************************************************************************/
*******************************************************************************/
static int lpc17_alloc(FAR struct usbhost_driver_s *drvr,
FAR uint8_t **buffer, FAR size_t *maxlen)
@ -739,7 +949,7 @@ static int lpc17_enumerate(FAR struct usbhost_driver_s *drvr)
return -ENOSYS;
}
/************************************************************************************
/*******************************************************************************
* Name: lpc17_free
*
* Description:
@ -760,7 +970,7 @@ static int lpc17_enumerate(FAR struct usbhost_driver_s *drvr)
* Assumptions:
* This function will *not* be called from an interrupt handler.
*
************************************************************************************/
*******************************************************************************/
static int lpc17_free(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer)
{
@ -768,12 +978,12 @@ static int lpc17_free(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer)
return -ENOSYS;
}
/************************************************************************************
/*******************************************************************************
* Name: lpc17_ctrlin and lpc17_ctrlout
*
* Description:
* Process a IN or OUT request on the control endpoint. These methods
* will enqueue the request and return immediately. Only one transfer may be
* will enqueue the request and wait for it to complete. Only one transfer may be
* queued; Neither these methods nor the transfer() method can be called again
* until the control transfer functions returns.
*
@ -797,25 +1007,59 @@ static int lpc17_free(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer)
* Assumptions:
* This function will *not* be called from an interrupt handler.
*
************************************************************************************/
*******************************************************************************/
static int lpc17_ctrlin(FAR struct usbhost_driver_s *drvr,
FAR const struct usb_ctrlreq_s *req,
FAR uint8_t *buffer)
{
# warning "Not Implemented"
return -ENOSYS;
struct lpc17_usbhost_s *priv = (struct lpc17_usbhost_s *)drvr;
uint16_t len;
int ret;
len = lpc17_getle16(req->len);
ret = lpc17_ctrltd(priv, GTD_STATUS_DP_SETUP, (uint8_t*)req, USB_SIZEOF_CTRLREQ);
if (ret == OK)
{
if (len)
{
ret = lpc17_ctrltd(priv, GTD_STATUS_DP_IN, buffer, len);
}
if (ret == OK)
{
ret = lpc17_ctrltd(priv, GTD_STATUS_DP_OUT, NULL, 0);
}
}
return ret;
}
static int lpc17_ctrlout(FAR struct usbhost_driver_s *drvr,
FAR const struct usb_ctrlreq_s *req,
FAR const uint8_t *buffer)
{
# warning "Not Implemented"
return -ENOSYS;
struct lpc17_usbhost_s *priv = (struct lpc17_usbhost_s *)drvr;
uint16_t len;
int ret;
len = lpc17_getle16(req->len);
ret = lpc17_ctrltd(priv, GTD_STATUS_DP_SETUP, (uint8_t*)req, USB_SIZEOF_CTRLREQ);
if (ret == OK)
{
if (len)
{
ret = lpc17_ctrltd(priv, GTD_STATUS_DP_OUT, (uint8_t*)buffer, len);
}
if (ret == OK)
{
ret = lpc17_ctrltd(priv, GTD_STATUS_DP_IN, NULL, 0);
}
}
return ret;
}
/************************************************************************************
/*******************************************************************************
* Name: lpc17_transfer
*
* Description:
@ -843,17 +1087,75 @@ static int lpc17_ctrlout(FAR struct usbhost_driver_s *drvr,
* Assumptions:
* This function will *not* be called from an interrupt handler.
*
************************************************************************************/
*******************************************************************************/
static int lpc17_transfer(FAR struct usbhost_driver_s *drvr,
FAR struct usbhost_epdesc_s *ed,
FAR struct usbhost_epdesc_s *ep,
FAR uint8_t *buffer, size_t buflen)
{
# warning "Not Implemented"
return -ENOSYS;
struct lpc17_usbhost_s *priv = (struct lpc17_usbhost_s *)drvr;
struct lpc17_hced_s *ed;
uint32_t dirpid;
uint32_t regval;
int ret = -ENOMEM;
/* Allocate an ED */
ed = lpc17_edalloc(priv);
if (ed)
{
/* Format the endpoint descriptor */
lpc17_edinit(ed);
ed->ctrl = (uint32_t)(ep->addr) << ED_CONTROL_EN_SHIFT |
(uint32_t)(ep->mxpacketsize) << ED_CONTROL_MPS_SHIFT;
/* Get the direction of the endpoint */
if (ep->in)
{
ed->ctrl |= ED_CONTROL_D_IN;
dirpid = GTD_STATUS_DP_IN;
}
else
{
ed->ctrl |= ED_CONTROL_D_OUT;
dirpid = GTD_STATUS_DP_OUT;
}
/* Then enqueue the transfer */
lpc17_enqueuetd(ed, dirpid, GTD_STATUS_T_TOGGLE, buffer, buflen);
lpc17_putreg((uint32_t)ed, LPC17_USBHOST_BULKHEADED);
regval = lpc17_getreg(LPC17_USBHOST_CMDST);
regval |= OHCI_CMDST_BLF;
lpc17_putreg(regval, LPC17_USBHOST_CMDST);
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);
/* Check the TDHEAD completion status bits */
if (priv->tdstatus == 0)
{
ret = OK;
}
else
{
ret = -EIO;
}
}
return ret;
}
/************************************************************************************
/*******************************************************************************
* Name: lpc17_disconnect
*
* Description:
@ -873,7 +1175,7 @@ static int lpc17_transfer(FAR struct usbhost_driver_s *drvr,
* Assumptions:
* This function will *not* be called from an interrupt handler.
*
************************************************************************************/
*******************************************************************************/
static void lpc17_disconnect(FAR struct usbhost_driver_s *drvr)
{
@ -933,6 +1235,7 @@ void up_usbhostinitialize(void)
{
struct lpc17_usbhost_s *priv = &g_usbhost;
uint32_t regval;
uint8_t *tdfree;
irqstate_t flags;
int i;
@ -940,6 +1243,7 @@ void up_usbhostinitialize(void)
/* Initialize the state data structure */
sem_init(&priv->rhssem, 0, 0);
sem_init(&priv->wdhsem, 0, 0);
/* Enable power by setting PCUSB in the PCONP register */
@ -995,6 +1299,17 @@ void up_usbhostinitialize(void)
lpc17_edfree(priv, &EDFREE[i]);
}
/* Initialize user-configurable TD buffers */
tdfree = TDFREE;
for (i = 0; i < CONFIG_USBHOST_NEDS; i++)
{
/* Put the TD buffer in a free list */
lpc17_tdfree(priv, tdfree);
tdfree += CONFIG_USBHOST_TDBUFSIZE;
}
/* Wait 50MS then perform hardware reset */
up_mdelay(50);