sync ehci code

This commit is contained in:
sakumisu
2022-06-16 22:24:03 +08:00
parent 224c26bc23
commit 12f74971eb

View File

@@ -1,6 +1,10 @@
#include "usbh_core.h"
#include "usb_ehci.h"
#ifndef USBH_IRQHandler
#define USBH_IRQHandler USBH_IRQHandler
#endif
#define DEBUGASSERT(f)
/* Configurable number of Queue Head (QH) structures. The default is one per
@@ -120,14 +124,14 @@ struct usb_ehci_epinfo_s {
#ifndef CONFIG_USBHOST_INT_DISABLE
uint8_t interval; /* Polling interval */
#endif
uint8_t status; /* Retained token status bits (for debug purposes) */
volatile bool iocwait; /* TRUE: Thread is waiting for transfer completion */
uint16_t maxpacket : 11; /* Maximum packet size */
uint16_t xfrtype : 2; /* See USB_EP_ATTR_XFER_* definitions in usb.h */
uint16_t speed : 2; /* See USB_*_SPEED definitions in ehci.h */
int result; /* The result of the transfer */
uint32_t xfrd; /* On completion, will hold the number of bytes transferred */
bool inuse;
uint8_t status; /* Retained token status bits (for debug purposes) */
uint16_t maxpacket : 11; /* Maximum packet size */
uint16_t xfrtype : 2; /* See USB_EP_ATTR_XFER_* definitions in usb.h */
uint16_t speed : 2; /* See USB_*_SPEED definitions in ehci.h */
int result; /* The result of the transfer */
uint32_t xfrd; /* On completion, will hold the number of bytes transferred */
volatile bool iocwait; /* TRUE: Thread is waiting for transfer completion */
usb_osal_sem_t iocsem; /* Semaphore used to wait for transfer completion */
usb_osal_mutex_t exclsem; /* Support mutually exclusive access */
#ifdef CONFIG_USBHOST_ASYNCH
@@ -140,9 +144,11 @@ struct usb_ehci_epinfo_s {
/* This structure retains the overall state of the USB host controller */
struct usb_ehci_s {
struct usb_ehci_list_s *qhfree; /* List of free Queue Head (QH) structures */
struct usb_ehci_list_s *qtdfree; /* List of free Queue Element Transfer Descriptor (qTD) */
struct ehci_hcd {
struct usb_ehci_list_s *qhfree; /* List of free Queue Head (QH) structures */
struct usb_ehci_list_s *qtdfree; /* List of free Queue Element Transfer Descriptor (qTD) */
__attribute__((aligned(32))) struct usb_ehci_qh_s qhpool[CONFIG_USB_EHCI_QH_NUM]; /* Queue Head (QH) pool */
__attribute__((aligned(32))) struct usb_ehci_qtd_s qtdpool[CONFIG_USB_EHCI_QTD_NUM]; /* Queue Element Transfer Descriptor (qTD) pool */
struct usb_ehci_epinfo_s chan[CONFIG_USBHOST_PIPE_NUM];
};
@@ -197,7 +203,7 @@ static const uint8_t g_ehci_speed[4] = {
* global instance.
*/
static struct usb_ehci_s g_ehci;
static struct ehci_hcd g_ehci_hcd;
/* The head of the asynchronous queue */
@@ -207,14 +213,6 @@ static struct usb_ehci_qh_s g_asynchead __attribute__((aligned(32)));
static struct usb_ehci_qh_s g_intrhead __attribute__((aligned(32)));
/* Queue Head (QH) pool */
static struct usb_ehci_qh_s g_qhpool[CONFIG_USB_EHCI_QH_NUM] __attribute__((aligned(32)));
/* Queue Element Transfer Descriptor (qTD) pool */
static struct usb_ehci_qtd_s g_qtdpool[CONFIG_USB_EHCI_QTD_NUM] __attribute__((aligned(32)));
/* The frame list */
static uint32_t g_framelist[FRAME_LIST_SIZE] __attribute__((aligned(4096)));
/****************************************************************************
@@ -306,10 +304,10 @@ static int usb_ehci_chan_alloc(void)
for (chidx = 0; chidx < CONFIG_USBHOST_PIPE_NUM; chidx++) {
/* Is this channel available? */
if (!g_ehci.chan[chidx].inuse) {
if (!g_ehci_hcd.chan[chidx].inuse) {
/* Yes... make it "in use" and return the index */
g_ehci.chan[chidx].inuse = true;
g_ehci_hcd.chan[chidx].inuse = true;
return chidx;
}
}
@@ -340,9 +338,9 @@ static struct usb_ehci_qh_s *usb_ehci_qh_alloc(void)
/* Remove the QH structure from the freelist */
qh = (struct usb_ehci_qh_s *)g_ehci.qhfree;
qh = (struct usb_ehci_qh_s *)g_ehci_hcd.qhfree;
if (qh) {
g_ehci.qhfree = ((struct usb_ehci_list_s *)qh)->flink;
g_ehci_hcd.qhfree = ((struct usb_ehci_list_s *)qh)->flink;
memset(qh, 0, sizeof(struct usb_ehci_qh_s));
}
@@ -365,8 +363,8 @@ static void usb_ehci_qh_free(struct usb_ehci_qh_s *qh)
/* Put the QH structure back into the free list */
entry->flink = g_ehci.qhfree;
g_ehci.qhfree = entry;
entry->flink = g_ehci_hcd.qhfree;
g_ehci_hcd.qhfree = entry;
}
/****************************************************************************
@@ -386,9 +384,9 @@ static struct usb_ehci_qtd_s *usb_ehci_qtd_alloc(void)
/* Remove the qTD from the freelist */
qtd = (struct usb_ehci_qtd_s *)g_ehci.qtdfree;
qtd = (struct usb_ehci_qtd_s *)g_ehci_hcd.qtdfree;
if (qtd) {
g_ehci.qtdfree = ((struct usb_ehci_list_s *)qtd)->flink;
g_ehci_hcd.qtdfree = ((struct usb_ehci_list_s *)qtd)->flink;
memset(qtd, 0, sizeof(struct usb_ehci_qtd_s));
}
@@ -412,8 +410,8 @@ static void usb_ehci_qtd_free(struct usb_ehci_qtd_s *qtd)
/* Put the qTD back into the free list */
entry->flink = g_ehci.qtdfree;
g_ehci.qtdfree = entry;
entry->flink = g_ehci_hcd.qtdfree;
g_ehci_hcd.qtdfree = entry;
}
/****************************************************************************
@@ -1014,7 +1012,7 @@ static void usb_ehci_qh_enqueue(struct usb_ehci_qh_s *qhead, struct usb_ehci_qh_
usb_ehci_dcache_clean((uintptr_t)&qhead->hw, sizeof(struct usb_ehci_qh_s));
}
static int usb_ehci_control_setup(struct usb_ehci_epinfo_s *epinfo, struct usb_setup_packet *setup, uint8_t *buffer, uint32_t buflen)
static int usb_ehci_control_init(struct usb_ehci_epinfo_s *epinfo, struct usb_setup_packet *setup, uint8_t *buffer, uint32_t buflen)
{
struct usb_ehci_qh_s *qh;
struct usb_ehci_qtd_s *qtd;
@@ -1024,7 +1022,6 @@ static int usb_ehci_control_setup(struct usb_ehci_epinfo_s *epinfo, struct usb_s
uint32_t *alt;
uint32_t toggle;
bool dirin = false;
int ret;
/* Create and initialize a Queue Head (QH) structure for this transfer */
qh = usb_ehci_qh_create(epinfo);
@@ -1032,13 +1029,13 @@ static int usb_ehci_control_setup(struct usb_ehci_epinfo_s *epinfo, struct usb_s
return -ENOMEM;
}
epinfo->qh = qh;
/* Initialize the QH link and get the next data toggle (not used for SETUP
* transfers)
*/
flink = &qh->hw.overlay.nqp;
toggle = (uint32_t)epinfo->toggle << QTD_TOKEN_TOGGLE_SHIFT;
ret = -EIO;
/* Is there an EP0 SETUP request? If so, we will queue two or three qTDs:
*
@@ -1053,8 +1050,7 @@ static int usb_ehci_control_setup(struct usb_ehci_epinfo_s *epinfo, struct usb_s
{
qtd = usb_ehci_qtd_setupphase(epinfo, setup);
if (qtd == NULL) {
ret = -ENOMEM;
goto errout_with_qh;
return -ENOMEM;
}
/* Link the new qTD to the QH head. */
@@ -1099,8 +1095,7 @@ static int usb_ehci_control_setup(struct usb_ehci_epinfo_s *epinfo, struct usb_s
qtd = usb_ehci_qtd_dataphase(epinfo, buffer, buflen, tokenbits);
if (qtd == NULL) {
ret = -ENOMEM;
goto errout_with_qh;
return -ENOMEM;
}
/* Link the new qTD to either QH head of the SETUP qTD. */
@@ -1143,8 +1138,7 @@ static int usb_ehci_control_setup(struct usb_ehci_epinfo_s *epinfo, struct usb_s
*/
qtd = usb_ehci_qtd_statusphase(tokenbits);
if (qtd == NULL) {
ret = -ENOMEM;
goto errout_with_qh;
return -ENOMEM;
}
/* Link the new qTD to either the SETUP or data qTD. */
@@ -1162,22 +1156,15 @@ static int usb_ehci_control_setup(struct usb_ehci_epinfo_s *epinfo, struct usb_s
}
/* Add the new QH to the head of the asynchronous queue list */
usb_ehci_qh_enqueue(&g_asynchead, qh);
epinfo->qh = qh;
return 0;
errout_with_qh:
/* Clean-up after an error */
usb_ehci_qh_discard(qh);
return ret;
}
static int usb_ehci_bulk_setup(struct usb_ehci_epinfo_s *epinfo, uint8_t *buffer, uint32_t buflen)
static int usb_ehci_bulk_init(struct usb_ehci_epinfo_s *epinfo, uint8_t *buffer, uint32_t buflen)
{
struct usb_ehci_qh_s *qh;
struct usb_ehci_qtd_s *qtd;
uint32_t tokenbits;
uintptr_t physaddr;
int ret;
/* Create and initialize a Queue Head (QH) structure for this transfer */
qh = usb_ehci_qh_create(epinfo);
@@ -1185,9 +1172,10 @@ static int usb_ehci_bulk_setup(struct usb_ehci_epinfo_s *epinfo, uint8_t *buffer
return -ENOMEM;
}
epinfo->qh = qh;
/* Initialize the QH link and get the next data toggle */
tokenbits = (uint32_t)epinfo->toggle << QTD_TOKEN_TOGGLE_SHIFT;
ret = -EIO;
if (buffer != NULL && buflen > 0) {
/* Get the direction from the epinfo structure. Since this is not an EP0 SETUP request,
@@ -1205,8 +1193,7 @@ static int usb_ehci_bulk_setup(struct usb_ehci_epinfo_s *epinfo, uint8_t *buffer
qtd = usb_ehci_qtd_dataphase(epinfo, buffer, buflen, tokenbits);
if (qtd == NULL) {
ret = -ENOMEM;
goto errout_with_qh;
return -ENOMEM;
}
/* Link the new qTD to the QH. */
@@ -1216,22 +1203,16 @@ static int usb_ehci_bulk_setup(struct usb_ehci_epinfo_s *epinfo, uint8_t *buffer
/* Add the new QH to the head of the asynchronous queue list */
usb_ehci_qh_enqueue(&g_asynchead, qh);
epinfo->qh = qh;
return 0;
errout_with_qh:
usb_ehci_qh_discard(qh);
return ret;
}
static int usb_ehci_intr_setup(struct usb_ehci_epinfo_s *epinfo, uint8_t *buffer, uint32_t buflen)
static int usb_ehci_intr_init(struct usb_ehci_epinfo_s *epinfo, uint8_t *buffer, uint32_t buflen)
{
struct usb_ehci_qh_s *qh;
struct usb_ehci_qtd_s *qtd;
uint32_t tokenbits;
uintptr_t physaddr;
uint32_t regval;
int ret;
/* Create and initialize a Queue Head (QH) structure for this transfer */
qh = usb_ehci_qh_create(epinfo);
@@ -1239,6 +1220,8 @@ static int usb_ehci_intr_setup(struct usb_ehci_epinfo_s *epinfo, uint8_t *buffer
return -ENOMEM;
}
epinfo->qh = qh;
/* Initialize the QH link and get the next data toggle */
tokenbits = (uint32_t)epinfo->toggle << QTD_TOKEN_TOGGLE_SHIFT;
@@ -1257,8 +1240,7 @@ static int usb_ehci_intr_setup(struct usb_ehci_epinfo_s *epinfo, uint8_t *buffer
qtd = usb_ehci_qtd_dataphase(epinfo, buffer, buflen, tokenbits);
if (qtd == NULL) {
ret = -ENOMEM;
goto errout_with_qh;
return -ENOMEM;
}
/* Link the new qTD to the QH. */
@@ -1276,12 +1258,7 @@ static int usb_ehci_intr_setup(struct usb_ehci_epinfo_s *epinfo, uint8_t *buffer
/* Re-enable the periodic schedule */
regval |= EHCI_USBCMD_PSEN;
usb_ehci_putreg(regval, &HCOR->usbcmd);
epinfo->qh = qh;
return 0;
errout_with_qh:
usb_ehci_qh_discard(qh);
return ret;
}
/****************************************************************************
@@ -1299,7 +1276,7 @@ errout_with_qh:
static int usb_ehci_ioc_setup(struct usb_ehci_epinfo_s *epinfo)
{
size_t flags;
uint32_t flags;
int ret = -ENODEV;
DEBUGASSERT(rhport && epinfo && !epinfo->iocwait);
@@ -1353,7 +1330,7 @@ static int usb_ehci_ioc_setup(struct usb_ehci_epinfo_s *epinfo)
#ifdef CONFIG_USBHOST_ASYNCH
static int usb_ehci_ioc_async_setup(struct usb_ehci_epinfo_s *epinfo, usbh_asynch_callback_t callback, void *arg)
{
size_t flags;
uint32_t flags;
int ret = -ENODEV;
DEBUGASSERT(rhport && epinfo && !epinfo->iocwait);
@@ -1468,7 +1445,6 @@ static int usb_ehci_transfer_wait(struct usb_ehci_epinfo_s *epinfo, uint32_t tim
if (epinfo->iocwait) {
ret = usb_osal_sem_take(epinfo->iocsem, timeout);
if (ret < 0) {
usb_ehci_qh_discard(epinfo->qh);
return ret;
}
}
@@ -1845,7 +1821,7 @@ static inline void usb_ehci_portsc_bottomhalf(void)
usbh_event_notify_handler(USBH_EVENT_CONNECTED, 1);
} else {
for (uint8_t chidx = 0; chidx < CONFIG_USBHOST_PIPE_NUM; chidx++) {
struct usb_ehci_epinfo_s *epinfo = &g_ehci.chan[chidx];
struct usb_ehci_epinfo_s *epinfo = &g_ehci_hcd.chan[chidx];
if (epinfo->iocwait) {
epinfo->iocwait = false;
usb_osal_sem_give(epinfo->iocsem);
@@ -2025,31 +2001,42 @@ __WEAK void usb_hc_low_level_init(void)
{
}
int usb_hc_init(void)
int usb_hc_sw_init(void)
{
memset(&g_ehci_hcd, 0, sizeof(struct ehci_hcd));
/* Initialize the list of free Queue Head (QH) structures */
for (uint8_t i = 0; i < CONFIG_USB_EHCI_QH_NUM; i++) {
/* Put the QH structure in a free list */
usb_ehci_qh_free(&g_ehci_hcd.qhpool[i]);
}
/* Initialize the list of free Queue Head (QH) structures */
for (uint8_t i = 0; i < CONFIG_USB_EHCI_QTD_NUM; i++) {
/* Put the QH structure in a free list */
usb_ehci_qtd_free(&g_ehci_hcd.qtdpool[i]);
}
for (uint8_t chidx = 0; chidx < CONFIG_USB_EHCI_QH_NUM; chidx++) {
struct usb_ehci_epinfo_s *epinfo;
epinfo = &g_ehci_hcd.chan[chidx];
epinfo->iocsem = usb_osal_sem_create(0);
epinfo->exclsem = usb_osal_mutex_create();
}
return 0;
}
int usb_hc_hw_init(void)
{
int ret;
uint32_t regval;
uintptr_t physaddr1;
uintptr_t physaddr2;
g_ehci.qhfree = NULL;
g_ehci.qtdfree = NULL;
/* Initialize the list of free Queue Head (QH) structures */
for (uint8_t i = 0; i < CONFIG_USB_EHCI_QH_NUM; i++) {
/* Put the QH structure in a free list */
usb_ehci_qh_free(&g_qhpool[i]);
}
/* Initialize the list of free Queue Head (QH) structures */
for (uint8_t i = 0; i < CONFIG_USB_EHCI_QTD_NUM; i++) {
/* Put the QH structure in a free list */
usb_ehci_qtd_free(&g_qtdpool[i]);
}
/* Initialize the head of the asynchronous queue/reclamation list.
*
* "In order to communicate with devices via the asynchronous schedule,
@@ -2270,6 +2257,8 @@ int usbh_ep_alloc(usbh_epinfo_t *ep, const struct usbh_endpoint_cfg *ep_cfg)
{
struct usb_ehci_epinfo_s *epinfo;
struct usbh_hubport *hport;
usb_osal_sem_t iocsem;
usb_osal_mutex_t exclsem;
int chidx;
DEBUGASSERT(ep_cfg != NULL && ep_cfg->hport != NULL);
@@ -2278,10 +2267,13 @@ int usbh_ep_alloc(usbh_epinfo_t *ep, const struct usbh_endpoint_cfg *ep_cfg)
chidx = usb_ehci_chan_alloc();
epinfo = &g_ehci.chan[chidx];
epinfo = &g_ehci_hcd.chan[chidx];
iocsem = epinfo->iocsem;
exclsem = epinfo->exclsem;
memset(epinfo, 0, sizeof(struct usb_ehci_epinfo_s));
epinfo->inuse = true;
epinfo->epno = ep_cfg->ep_addr & 0x7f;
epinfo->dirin = (ep_cfg->ep_addr & 0x80) ? 1 : 0;
epinfo->devaddr = hport->dev_addr;
@@ -2293,8 +2285,10 @@ int usbh_ep_alloc(usbh_epinfo_t *ep, const struct usbh_endpoint_cfg *ep_cfg)
epinfo->speed = hport->speed;
epinfo->hport = hport;
epinfo->iocsem = usb_osal_sem_create(0);
epinfo->exclsem = usb_osal_mutex_create();
/* restore variable */
epinfo->inuse = true;
epinfo->iocsem = iocsem;
epinfo->exclsem = exclsem;
*ep = (usbh_epinfo_t)epinfo;
@@ -2305,8 +2299,6 @@ int usbh_ep_free(usbh_epinfo_t ep)
{
struct usb_ehci_epinfo_s *epinfo = (struct usb_ehci_epinfo_s *)ep;
usb_osal_sem_delete(epinfo->iocsem);
usb_osal_mutex_delete(epinfo->exclsem);
usb_ehci_chan_free(epinfo);
return 0;
@@ -2334,10 +2326,10 @@ int usbh_control_transfer(usbh_epinfo_t ep, struct usb_setup_packet *setup, uint
/* Set the request for the IOC event well BEFORE initiating the transfer. */
ret = usb_ehci_ioc_setup(epinfo);
if (ret != 0) {
goto errout_with_sem;
goto errout_with_setup;
}
ret = usb_ehci_control_setup(epinfo, setup, buffer, setup->wLength);
ret = usb_ehci_control_init(epinfo, setup, buffer, setup->wLength);
if (ret < 0) {
goto errout_with_iocwait;
}
@@ -2353,7 +2345,12 @@ int usbh_control_transfer(usbh_epinfo_t ep, struct usb_setup_packet *setup, uint
errout_with_iocwait:
epinfo->iocwait = false;
errout_with_sem:
/* Clean-up after an error */
if (epinfo->qh) {
usb_ehci_qh_discard(epinfo->qh);
epinfo->qh = NULL;
}
errout_with_setup:
usb_osal_mutex_give(epinfo->exclsem);
return ret;
}
@@ -2373,10 +2370,10 @@ int usbh_ep_bulk_transfer(usbh_epinfo_t ep, uint8_t *buffer, uint32_t buflen, ui
ret = usb_ehci_ioc_setup(epinfo);
if (ret < 0) {
goto errout_with_sem;
goto errout_with_setup;
}
ret = usb_ehci_bulk_setup(epinfo, buffer, buflen);
ret = usb_ehci_bulk_init(epinfo, buffer, buflen);
if (ret < 0) {
goto errout_with_iocwait;
}
@@ -2391,7 +2388,12 @@ int usbh_ep_bulk_transfer(usbh_epinfo_t ep, uint8_t *buffer, uint32_t buflen, ui
errout_with_iocwait:
epinfo->iocwait = false;
errout_with_sem:
/* Clean-up after an error */
if (epinfo->qh) {
usb_ehci_qh_discard(epinfo->qh);
epinfo->qh = NULL;
}
errout_with_setup:
usb_osal_mutex_give(epinfo->exclsem);
return ret;
}
@@ -2411,10 +2413,10 @@ int usbh_ep_intr_transfer(usbh_epinfo_t ep, uint8_t *buffer, uint32_t buflen, ui
ret = usb_ehci_ioc_setup(epinfo);
if (ret < 0) {
goto errout_with_sem;
goto errout_with_setup;
}
ret = usb_ehci_intr_setup(epinfo, buffer, buflen);
ret = usb_ehci_intr_init(epinfo, buffer, buflen);
if (ret < 0) {
goto errout_with_iocwait;
}
@@ -2429,7 +2431,12 @@ int usbh_ep_intr_transfer(usbh_epinfo_t ep, uint8_t *buffer, uint32_t buflen, ui
errout_with_iocwait:
epinfo->iocwait = false;
errout_with_sem:
/* Clean-up after an error */
if (epinfo->qh) {
usb_ehci_qh_discard(epinfo->qh);
epinfo->qh = NULL;
}
errout_with_setup:
usb_osal_mutex_give(epinfo->exclsem);
return ret;
}
@@ -2450,23 +2457,28 @@ int usbh_ep_bulk_async_transfer(usbh_epinfo_t ep, uint8_t *buffer, uint32_t bufl
/* Set the request for the callback well BEFORE initiating the transfer. */
ret = usb_ehci_ioc_async_setup(epinfo, callback, arg);
if (ret != 0) {
goto errout_with_sem;
goto errout_with_setup;
}
/* Check for errors in the setup of the transfer */
ret = usb_ehci_bulk_setup(epinfo, buffer, buflen);
ret = usb_ehci_bulk_init(epinfo, buffer, buflen);
if (ret < 0) {
goto errout_with_callback;
goto errout_with_qh;
}
/* The transfer is in progress */
usb_osal_mutex_give(epinfo->exclsem);
return 0;
errout_with_callback:
errout_with_qh:
/* Clean-up after an error */
if (epinfo->qh) {
usb_ehci_qh_discard(epinfo->qh);
epinfo->qh = NULL;
}
errout_with_setup:
epinfo->callback = NULL;
epinfo->arg = NULL;
errout_with_sem:
usb_osal_mutex_give(epinfo->exclsem);
return ret;
}
@@ -2487,23 +2499,28 @@ int usbh_ep_intr_async_transfer(usbh_epinfo_t ep, uint8_t *buffer, uint32_t bufl
/* Set the request for the callback well BEFORE initiating the transfer. */
ret = usb_ehci_ioc_async_setup(epinfo, callback, arg);
if (ret != 0) {
goto errout_with_sem;
goto errout_with_setup;
}
/* Check for errors in the setup of the transfer */
ret = usb_ehci_intr_setup(epinfo, buffer, buflen);
ret = usb_ehci_intr_init(epinfo, buffer, buflen);
if (ret < 0) {
goto errout_with_callback;
goto errout_with_qh;
}
/* The transfer is in progress */
usb_osal_mutex_give(epinfo->exclsem);
return 0;
errout_with_callback:
errout_with_qh:
/* Clean-up after an error */
if (epinfo->qh) {
usb_ehci_qh_discard(epinfo->qh);
epinfo->qh = NULL;
}
errout_with_setup:
epinfo->callback = NULL;
epinfo->arg = NULL;
errout_with_sem:
usb_osal_mutex_give(epinfo->exclsem);
return ret;
}
@@ -2536,7 +2553,7 @@ int usb_ep_cancel(usbh_epinfo_t ep)
void *arg;
#endif
uint32_t *bp;
size_t flags;
uint32_t flags;
bool iocwait;
int ret;
@@ -2783,7 +2800,7 @@ static void usb_ehci_bottomhalf(void *arg)
}
}
void usb_ehci_interrupt(void)
void USBH_IRQHandler(void)
{
uint32_t usbsts;
uint32_t pending;