omap3530/beagle_drivers/wb/api/src/cyaslowlevel.c
changeset 27 117faf51deac
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/omap3530/beagle_drivers/wb/api/src/cyaslowlevel.c	Wed Mar 03 13:10:32 2010 +0000
@@ -0,0 +1,1207 @@
+/* Cypress West Bridge API source file (cyaslowlevel.c)
+## ===========================
+##
+##  Copyright Cypress Semiconductor Corporation, 2006-2009,
+##  All Rights Reserved
+##  UNPUBLISHED, LICENSED SOFTWARE.
+##
+##  CONFIDENTIAL AND PROPRIETARY INFORMATION
+##  WHICH IS THE PROPERTY OF CYPRESS.
+##
+##  Use of this file is governed
+##  by the license agreement included in the file
+##
+##     <install>/license/license.txt
+##
+##  where <install> is the Cypress software
+##  installation root directory path.
+##
+## ===========================
+*/
+
+#include "cyashal.h"
+#include "cyascast.h"
+#include "cyasdevice.h"
+#include "cyaslowlevel.h"
+#include "cyasintr.h"
+#include "cyaserr.h"
+#include "cyasregs.h"
+
+static const uint32_t CyAsLowLevelTimeoutCount = 65536 * 4 ;
+
+/* Forward declaration */
+static CyAsReturnStatus_t CyAsSendOne(CyAsDevice *dev_p, CyAsLLRequestResponse *req_p) ;
+
+/*
+* This array holds the size of the largest request we will ever recevie from
+* the West Bridge device per context.  The size is in 16 bit words.  Note a size of
+* 0xffff indicates that there will be no requests on this context from West Bridge.
+*/
+static uint16_t MaxRequestLength[CY_RQT_CONTEXT_COUNT] =
+{
+    8,                          /* CY_RQT_GENERAL_RQT_CONTEXT - CY_RQT_INITIALIZATION_COMPLETE */
+    8,                          /* CY_RQT_RESOURCE_RQT_CONTEXT - none */
+    8,                          /* CY_RQT_STORAGE_RQT_CONTEXT - CY_RQT_MEDIA_CHANGED */
+    128,                        /* CY_RQT_USB_RQT_CONTEXT - CY_RQT_USB_EVENT */
+    8                           /* CY_RQT_TUR_RQT_CONTEXT - CY_RQT_TURBO_CMD_FROM_HOST */
+} ;
+
+/*
+* For the given context, this function removes the request node at the head of the
+* queue from the context.  This is called after all processing has occurred on
+* the given request and response and we are ready to remove this entry from the
+* queue.
+*/
+static void
+CyAsLLRemoveRequestQueueHead(CyAsDevice *dev_p, CyAsContext *ctxt_p)
+{
+    uint32_t mask, state ;
+    CyAsLLRequestListNode *node_p ;
+
+    (void)dev_p ;
+    CyAsHalAssert(ctxt_p->request_queue_p != 0) ;
+
+    mask = CyAsHalDisableInterrupts() ;
+    node_p = ctxt_p->request_queue_p ;
+    ctxt_p->request_queue_p = node_p->next ;
+    CyAsHalEnableInterrupts(mask) ;
+
+    node_p->callback = 0 ;
+    node_p->rqt = 0 ;
+    node_p->resp = 0 ;
+
+    /*
+    * Note that the caller allocates and destroys the request and response.  Generally the
+    * destroy happens in the callback for async requests and after the wait returns for
+    * sync.  The request and response may not actually be destroyed but may be managed in other
+    * ways as well.  It is the responsibilty of the caller to deal with these in any case.  The
+    * caller can do this in the request/response callback function.
+    */
+    state = CyAsHalDisableInterrupts() ;
+    CyAsHalCBFree(node_p) ;
+    CyAsHalEnableInterrupts(state) ;
+}
+
+/*
+* For the context given, this function sends the next request to
+* West Bridge via the mailbox register, if the next request is ready to
+* be sent and has not already been sent.
+*/
+static void
+CyAsLLSendNextRequest(CyAsDevice *dev_p, CyAsContext *ctxt_p)
+{
+    CyAsReturnStatus_t ret = CY_AS_ERROR_SUCCESS ;
+
+    /*
+    * ret == ret is equivalent to while (1) but eliminates compiler warnings for
+    * some compilers.
+    */
+    while (ret == ret)
+    {
+        CyAsLLRequestListNode *node_p = ctxt_p->request_queue_p ;
+        if (node_p == 0)
+            break ;
+
+        if (CyAsRequestGetNodeState(node_p) != CY_AS_REQUEST_LIST_STATE_QUEUED)
+            break ;
+
+        CyAsRequestSetNodeState(node_p, CY_AS_REQUEST_LIST_STATE_WAITING) ;
+        ret = CyAsSendOne(dev_p, node_p->rqt) ;
+        if (ret == CY_AS_ERROR_SUCCESS)
+        {
+            break ;
+        }
+
+        /*
+        * If an error occurs in sending the request, tell the requester
+        * about the error and remove the request from the queue.
+        */
+        CyAsRequestSetNodeState(node_p, CY_AS_REQUEST_LIST_STATE_RECEIVED) ;
+        node_p->callback(dev_p, ctxt_p->number, node_p->rqt, node_p->resp, ret) ;
+        CyAsLLRemoveRequestQueueHead(dev_p, ctxt_p) ;
+
+        /*
+        * This falls through to the while loop to send the next request since
+        * the previous request did not get sent.
+        */
+    }
+}
+
+/*
+* This method removes an entry from the request queue of a given context.  The entry
+* is removed only if it is not in transit.
+*/
+CyAsRemoveRequestResult_t
+CyAsLLRemoveRequest(CyAsDevice *dev_p, CyAsContext *ctxt_p, CyAsLLRequestResponse *req_p, CyBool force)
+{
+    uint32_t imask ;
+    CyAsLLRequestListNode *node_p ;
+    CyAsLLRequestListNode *tmp_p ;
+    uint32_t state ;
+
+    imask = CyAsHalDisableInterrupts() ;
+    if (ctxt_p->request_queue_p != 0 && ctxt_p->request_queue_p->rqt == req_p)
+    {
+        node_p = ctxt_p->request_queue_p ;
+        if ((CyAsRequestGetNodeState(node_p) == CY_AS_REQUEST_LIST_STATE_WAITING) && (!force))
+        {
+            CyAsHalEnableInterrupts(imask) ;
+            return CyAsRemoveRequestInTransit ;
+        }
+
+        ctxt_p->request_queue_p = node_p->next ;
+    }
+    else
+    {
+        tmp_p = ctxt_p->request_queue_p ;
+        while (tmp_p != 0 && tmp_p->next != 0 && tmp_p->next->rqt != req_p)
+            tmp_p = tmp_p->next ;
+
+        if (tmp_p == 0 || tmp_p->next == 0)
+        {
+            CyAsHalEnableInterrupts(imask) ;
+            return CyAsRemoveRequestNotFound ;
+        }
+
+        node_p = tmp_p->next ;
+        tmp_p->next = node_p->next ;
+    }
+
+    if (node_p->callback)
+        node_p->callback(dev_p, ctxt_p->number, node_p->rqt, node_p->resp, CY_AS_ERROR_CANCELED) ;
+
+    state = CyAsHalDisableInterrupts() ;
+    CyAsHalCBFree(node_p) ;
+    CyAsHalEnableInterrupts(state) ;
+
+    CyAsHalEnableInterrupts(imask) ;
+    return CyAsRemoveRequestSucessful ;
+}
+
+void
+CyAsLLRemoveAllRequests(CyAsDevice *dev_p, CyAsContext *ctxt_p)
+{
+    CyAsLLRequestListNode *node = ctxt_p->request_queue_p ;
+
+    while(node)
+    {
+        if(CyAsRequestGetNodeState(ctxt_p->request_queue_p) != CY_AS_REQUEST_LIST_STATE_RECEIVED)
+            CyAsLLRemoveRequest(dev_p, ctxt_p, node->rqt, CyTrue) ;
+        node = node->next ;
+    }
+}
+
+static CyBool
+CyAsLLIsInQueue(CyAsContext *ctxt_p, CyAsLLRequestResponse *req_p)
+{
+    uint32_t mask ;
+    CyAsLLRequestListNode *node_p ;
+
+    mask = CyAsHalDisableInterrupts() ;
+    node_p = ctxt_p->request_queue_p ;
+    while (node_p)
+    {
+        if (node_p->rqt == req_p)
+        {
+            CyAsHalEnableInterrupts(mask) ;
+            return CyTrue ;
+        }
+        node_p = node_p->next ;
+    }
+    CyAsHalEnableInterrupts(mask) ;
+    return CyFalse ;
+}
+
+/*
+* This is the handler for mailbox data when we are trying to send data to the West Bridge firmware.  The
+* firmware may be trying to send us data and we need to queue this data to allow the firmware to move
+* forward and be in a state to receive our request.  Here we just queue the data and it is processed
+* at a later time by the mailbox interrupt handler.
+*/
+void
+CyAsLLQueueMailboxData(CyAsDevice *dev_p)
+{
+    CyAsContext *ctxt_p ;
+    uint8_t context ;
+    uint16_t data[4] ;
+    int32_t i ;
+
+    /* Read the data from mailbox 0 to determine what to do with the data */
+    for(i = 3 ; i >= 0 ; i--)
+        data[i] = CyAsHalReadRegister(dev_p->tag, CyCastInt2UInt16(CY_AS_MEM_P0_MAILBOX0 + i)) ;
+
+    context = CyAsMboxGetContext(data[0]) ;
+    if (context >= CY_RQT_CONTEXT_COUNT)
+    {
+        CyAsHalPrintMessage("Mailbox request/response received with invalid context value (%d)\n", context) ;
+        return ;
+    }
+
+    ctxt_p = dev_p->context[context] ;
+
+    /*
+    * If we have queued too much data, drop future data.
+    */
+    CyAsHalAssert(ctxt_p->queue_index * sizeof(uint16_t) + sizeof(data) <= sizeof(ctxt_p->data_queue)) ;
+
+    for(i = 0 ; i < 4 ; i++)
+        ctxt_p->data_queue[ctxt_p->queue_index++] = data[i] ;
+
+    CyAsHalAssert((ctxt_p->queue_index % 4) == 0) ;
+    dev_p->ll_queued_data = CyTrue ;
+}
+
+void
+CyAsMailBoxProcessData(CyAsDevice *dev_p, uint16_t *data)
+{
+    CyAsContext *ctxt_p ;
+    uint8_t context ;
+    uint16_t *len_p ;
+    CyAsLLRequestResponse *rec_p ;
+    uint8_t st ;
+    uint16_t src, dest ;
+
+    context = CyAsMboxGetContext(data[0]) ;
+    if (context >= CY_RQT_CONTEXT_COUNT)
+    {
+        CyAsHalPrintMessage("Mailbox request/response received with invalid context value (%d)\n", context) ;
+        return ;
+    }
+
+    ctxt_p = dev_p->context[context] ;
+
+    if (CyAsMboxIsRequest(data[0]))
+    {
+        CyAsHalAssert(ctxt_p->req_p != 0) ;
+        rec_p = ctxt_p->req_p ;
+        len_p = &ctxt_p->request_length ;
+
+    }
+    else
+    {
+        if (ctxt_p->request_queue_p == 0 || CyAsRequestGetNodeState(ctxt_p->request_queue_p) != CY_AS_REQUEST_LIST_STATE_WAITING)
+        {
+            CyAsHalPrintMessage("Mailbox response received on context that was not expecting a response\n") ;
+            CyAsHalPrintMessage("    Context: %d\n", context) ;
+            CyAsHalPrintMessage("    Contents: 0x%04x 0x%04x 0x%04x 0x%04x\n", data[0], data[1], data[2], data[3]) ;
+            if (ctxt_p->request_queue_p != 0)
+                CyAsHalPrintMessage("    State: 0x%02x\n", ctxt_p->request_queue_p->state) ;
+            return ;
+        }
+
+        /* Make sure the request has an associated response */
+        CyAsHalAssert(ctxt_p->request_queue_p->resp != 0) ;
+
+        rec_p = ctxt_p->request_queue_p->resp ;
+        len_p = &ctxt_p->request_queue_p->length ;
+    }
+
+    if (rec_p->stored == 0)
+    {
+        /*
+        * This is the first cycle of the response
+        */
+        CyAsLLRequestResponse_SetCode(rec_p, CyAsMboxGetCode(data[0])) ;
+        CyAsLLRequestResponse_SetContext(rec_p, context) ;
+
+        if (CyAsMboxIsLast(data[0]))
+        {
+            /* This is a single cycle response */
+            *len_p = rec_p->length ;
+            st = 1 ;
+        }
+        else
+        {
+            /* Ensure that enough memory has been reserved for the response. */
+            CyAsHalAssert(rec_p->length >= data[1]) ;
+            *len_p = (data[1] < rec_p->length) ? data[1] : rec_p->length ;
+            st = 2 ;
+        }
+    }
+    else
+        st = 1 ;
+
+    /* Trasnfer the data from the mailboxes to the response */
+    while (rec_p->stored < *len_p && st < 4)
+        rec_p->data[rec_p->stored++] = data[st++] ;
+
+    if (CyAsMboxIsLast(data[0]))
+    {
+        /* NB: The call-back that is made below can cause the addition of more data
+        in this queue, thus causing a recursive overflow of the queue. This is prevented
+        by removing the request entry that is currently being passed up from the data
+        queue. If this is done, the queue only needs to be as long as two request
+        entries from West Bridge.
+        */
+        if ((ctxt_p->rqt_index > 0) && (ctxt_p->rqt_index <= ctxt_p->queue_index))
+        {
+            dest = 0 ;
+            src  = ctxt_p->rqt_index ;
+
+            while (src < ctxt_p->queue_index)
+                ctxt_p->data_queue[dest++] = ctxt_p->data_queue[src++] ;
+
+            ctxt_p->rqt_index = 0 ;
+            ctxt_p->queue_index = dest ;
+            CyAsHalAssert((ctxt_p->queue_index % 4) == 0) ;
+        }
+
+        if (ctxt_p->request_queue_p != 0 && rec_p == ctxt_p->request_queue_p->resp)
+        {
+            /*
+            * If this is the last cycle of the response, call the callback and
+            * reset for the next response.
+            */
+            CyAsLLRequestResponse *resp_p = ctxt_p->request_queue_p->resp ;
+            resp_p->length = ctxt_p->request_queue_p->length ;
+            CyAsRequestSetNodeState(ctxt_p->request_queue_p, CY_AS_REQUEST_LIST_STATE_RECEIVED) ;
+
+            CyAsDeviceSetInCallback(dev_p) ;
+            ctxt_p->request_queue_p->callback(dev_p, context, ctxt_p->request_queue_p->rqt, resp_p, CY_AS_ERROR_SUCCESS) ;
+            CyAsDeviceClearInCallback(dev_p) ;
+
+            CyAsLLRemoveRequestQueueHead(dev_p, ctxt_p) ;
+            CyAsLLSendNextRequest(dev_p, ctxt_p) ;
+        }
+        else
+        {
+            /* Send the request to the appropriate module to handle */
+            CyAsLLRequestResponse *request_p = ctxt_p->req_p ;
+            ctxt_p->req_p = 0 ;
+            if (ctxt_p->request_callback)
+            {
+                CyAsDeviceSetInCallback(dev_p) ;
+                ctxt_p->request_callback(dev_p, context, request_p, 0, CY_AS_ERROR_SUCCESS) ;
+                CyAsDeviceClearInCallback(dev_p) ;
+            }
+            CyAsLLInitRequest(request_p, 0, context, request_p->length) ;
+            ctxt_p->req_p = request_p ;
+        }
+    }
+}
+
+/*
+* This is the handler for processing queued mailbox data
+*/
+void
+CyAsMailBoxQueuedDataHandler(CyAsDevice *dev_p)
+{
+    uint16_t i ;
+
+    /*
+     * If more data gets queued in between our entering this call and the
+     * end of the iteration on all contexts; we should continue processing the
+     * queued data.
+     */
+    while (dev_p->ll_queued_data)
+    {
+        dev_p->ll_queued_data = CyFalse ;
+        for(i = 0 ; i < CY_RQT_CONTEXT_COUNT ; i++)
+        {
+            uint16_t offset ;
+            CyAsContext *ctxt_p = dev_p->context[i] ;
+            CyAsHalAssert((ctxt_p->queue_index % 4) == 0) ;
+
+            offset = 0 ;
+            while (offset < ctxt_p->queue_index)
+            {
+                ctxt_p->rqt_index = offset + 4 ;
+                CyAsMailBoxProcessData(dev_p, ctxt_p->data_queue + offset) ;
+                offset = ctxt_p->rqt_index ;
+            }
+            ctxt_p->queue_index = 0 ;
+        }
+    }
+}
+
+/*
+* This is the handler for the mailbox interrupt.  This function reads data from the mailbox registers
+* until a complete request or response is received.  When a complete request is received, the callback
+* associated with requests on that context is called.  When a complete response is recevied, the callback
+* associated with the request that generated the reponse is called.
+*/
+void
+CyAsMailBoxInterruptHandler(CyAsDevice *dev_p)
+{
+    CyAsHalAssert(dev_p->sig == CY_AS_DEVICE_HANDLE_SIGNATURE) ;
+
+    /*
+    * Queue the mailbox data to preserve order for later processing.
+    */
+    CyAsLLQueueMailboxData(dev_p) ;
+
+    /*
+    * Process what was queued and anything that may be pending
+    */
+    CyAsMailBoxQueuedDataHandler(dev_p) ;
+}
+
+CyAsReturnStatus_t
+CyAsLLStart(CyAsDevice *dev_p)
+{
+    uint16_t i ;
+
+    if (CyAsDeviceIsLowLevelRunning(dev_p))
+        return CY_AS_ERROR_ALREADY_RUNNING ;
+
+    dev_p->ll_sending_rqt = CyFalse ;
+    dev_p->ll_abort_curr_rqt = CyFalse ;
+
+    for(i = 0 ; i < CY_RQT_CONTEXT_COUNT ; i++)
+    {
+        dev_p->context[i] = (CyAsContext *)CyAsHalAlloc(sizeof(CyAsContext)) ;
+        if (dev_p->context[i] == 0)
+            return CY_AS_ERROR_OUT_OF_MEMORY ;
+
+        dev_p->context[i]->number = (uint8_t)i ;
+        dev_p->context[i]->request_callback = 0 ;
+        dev_p->context[i]->request_queue_p = 0 ;
+        dev_p->context[i]->last_node_p = 0 ;
+        dev_p->context[i]->req_p = CyAsLLCreateRequest(dev_p, 0, (uint8_t)i, MaxRequestLength[i]) ;
+        dev_p->context[i]->queue_index = 0 ;
+
+        if (!CyAsHalCreateSleepChannel(&dev_p->context[i]->channel))
+            return CY_AS_ERROR_CREATE_SLEEP_CHANNEL_FAILED ;
+    }
+
+    CyAsDeviceSetLowLevelRunning(dev_p) ;
+    return CY_AS_ERROR_SUCCESS ;
+}
+
+/*
+* Shutdown the low level communications module.  This operation will also cancel any
+* queued low level requests.
+*/
+CyAsReturnStatus_t
+CyAsLLStop(CyAsDevice *dev_p)
+{
+    uint8_t i ;
+    CyAsReturnStatus_t ret = CY_AS_ERROR_SUCCESS ;
+    CyAsContext *ctxt_p ;
+    uint32_t mask ;
+
+    for(i = 0 ; i < CY_RQT_CONTEXT_COUNT ; i++)
+    {
+        ctxt_p = dev_p->context[i] ;
+        if (!CyAsHalDestroySleepChannel(&ctxt_p->channel))
+            return CY_AS_ERROR_DESTROY_SLEEP_CHANNEL_FAILED ;
+
+        /*
+        * Now, free any queued requests and assocaited responses
+        */
+        while (ctxt_p->request_queue_p)
+        {
+            uint32_t state ;
+            CyAsLLRequestListNode *node_p = ctxt_p->request_queue_p ;
+
+            /* Mark this pair as in a cancel operation */
+            CyAsRequestSetNodeState(node_p, CY_AS_REQUEST_LIST_STATE_CANCELING) ;
+
+            /* Tell the caller that we are canceling this request */
+            /* NB: The callback is responsible for destroying the request and the
+            response.  We cannot count on the contents of these two after
+            calling the callback.
+            */
+            node_p->callback(dev_p, i, node_p->rqt, node_p->resp, CY_AS_ERROR_CANCELED) ;
+
+            /* Remove the pair from the queue */
+            mask = CyAsHalDisableInterrupts() ;
+            ctxt_p->request_queue_p = node_p->next ;
+            CyAsHalEnableInterrupts(mask) ;
+
+            /* Free the list node */
+            state = CyAsHalDisableInterrupts() ;
+            CyAsHalCBFree(node_p) ;
+            CyAsHalEnableInterrupts(state) ;
+        }
+
+        CyAsLLDestroyRequest(dev_p, dev_p->context[i]->req_p) ;
+        CyAsHalFree(dev_p->context[i]) ;
+        dev_p->context[i] = 0 ;
+
+    }
+    CyAsDeviceSetLowLevelStopped(dev_p) ;
+
+    return ret ;
+}
+
+void
+CyAsLLInitRequest(CyAsLLRequestResponse *req_p, uint16_t code, uint16_t context, uint16_t length)
+{
+    uint16_t totallen = sizeof(CyAsLLRequestResponse) + (length - 1) * sizeof(uint16_t) ;
+
+    CyAsHalMemSet(req_p, 0, totallen) ;
+    req_p->length = length ;
+    CyAsLLRequestResponse_SetCode(req_p, code) ;
+    CyAsLLRequestResponse_SetContext(req_p, context) ;
+    CyAsLLRequestResponse_SetRequest(req_p) ;
+}
+
+/*
+* Create a new request.
+*/
+CyAsLLRequestResponse *
+CyAsLLCreateRequest(CyAsDevice *dev_p, uint16_t code, uint8_t context, uint16_t length)
+{
+    CyAsLLRequestResponse *req_p ;
+    uint32_t state ;
+    uint16_t totallen = sizeof(CyAsLLRequestResponse) + (length - 1) * sizeof(uint16_t) ;
+
+    (void)dev_p ;
+
+    state = CyAsHalDisableInterrupts() ;
+    req_p = (CyAsLLRequestResponse *)CyAsHalCBAlloc(totallen) ;
+    CyAsHalEnableInterrupts(state) ;
+    if(req_p)
+        CyAsLLInitRequest(req_p, code, context, length) ;
+
+    return req_p ;
+}
+
+/*
+* Destroy a request.
+*/
+void
+CyAsLLDestroyRequest(CyAsDevice *dev_p, CyAsLLRequestResponse *req_p)
+{
+    uint32_t state ;
+    (void)dev_p ;
+    (void)req_p ;
+
+    state = CyAsHalDisableInterrupts() ;
+    CyAsHalCBFree(req_p) ;
+    CyAsHalEnableInterrupts(state) ;
+
+}
+
+void
+CyAsLLInitResponse(CyAsLLRequestResponse *req_p, uint16_t length)
+{
+    uint16_t totallen = sizeof(CyAsLLRequestResponse) + (length - 1) * sizeof(uint16_t) ;
+
+    CyAsHalMemSet(req_p, 0, totallen) ;
+    req_p->length = length ;
+    CyAsLLRequestResponse_SetResponse(req_p) ;
+}
+
+/*
+* Create a new response
+*/
+CyAsLLRequestResponse *
+CyAsLLCreateResponse(CyAsDevice *dev_p, uint16_t length)
+{
+    CyAsLLRequestResponse *req_p ;
+    uint32_t state ;
+    uint16_t totallen = sizeof(CyAsLLRequestResponse) + (length - 1) * sizeof(uint16_t) ;
+
+    (void)dev_p ;
+
+    state = CyAsHalDisableInterrupts() ;
+    req_p = (CyAsLLRequestResponse *)CyAsHalCBAlloc(totallen) ;
+    CyAsHalEnableInterrupts(state) ;
+    if(req_p)
+        CyAsLLInitResponse(req_p, length) ;
+
+    return req_p ;
+}
+
+/*
+* Destroy the new response
+*/
+void
+CyAsLLDestroyResponse(CyAsDevice *dev_p, CyAsLLRequestResponse *req_p)
+{
+    uint32_t state ;
+    (void)dev_p ;
+    (void)req_p ;
+
+    state = CyAsHalDisableInterrupts() ;
+    CyAsHalCBFree(req_p) ;
+    CyAsHalEnableInterrupts(state) ;
+}
+
+static uint16_t
+CyAsReadIntrStatus(
+                   CyAsDevice *dev_p)
+{
+    uint32_t mask ;
+    CyBool bloop = CyTrue ;
+    uint16_t v = 0, last = 0xffff;
+
+    /*
+    * Before determining if the mailboxes are ready for more data, we first check the
+    * mailbox interrupt to see if we need to receive data.  This prevents a dead-lock
+    * condition that can occur when both sides are trying to receive data.
+    */
+    while (last == last)
+    {
+        /*
+        * Disable interrupts to be sure we don't process the mailbox here and have the
+        * interrupt routine try to read this data as well.
+        */
+        mask = CyAsHalDisableInterrupts() ;
+
+        /*
+        * See if there is data to be read.
+        */
+        v = CyAsHalReadRegister(dev_p->tag, CY_AS_MEM_P0_INTR_REG) ;
+        if ((v & CY_AS_MEM_P0_INTR_REG_MBINT) == 0)
+        {
+            CyAsHalEnableInterrupts(mask) ;
+            break ;
+        }
+
+        /*
+        * Queue the mailbox data for later processing.  This allows the firmware to move
+        * forward and service the requst from the P port.
+        */
+        CyAsLLQueueMailboxData(dev_p) ;
+
+        /*
+        * Enable interrupts again to service mailbox interrupts appropriately
+        */
+        CyAsHalEnableInterrupts(mask) ;
+    }
+
+    /*
+    * Now, all data is received
+    */
+    last = CyAsHalReadRegister(dev_p->tag, CY_AS_MEM_MCU_MB_STAT) & CY_AS_MEM_P0_MCU_MBNOTRD ;
+    while (bloop)
+    {
+        v = CyAsHalReadRegister(dev_p->tag, CY_AS_MEM_MCU_MB_STAT) & CY_AS_MEM_P0_MCU_MBNOTRD ;
+        if (v == last)
+            break ;
+
+        last = v ;
+    }
+
+    return v ;
+}
+
+/*
+* Send a single request or response using the mail box register.  This function does not deal
+* with the internal queues at all, but only sends the request or response across to the firmware
+*/
+static CyAsReturnStatus_t
+CyAsSendOne(
+            CyAsDevice *dev_p,
+            CyAsLLRequestResponse *req_p)
+{
+    int i ;
+    uint16_t mb0, v ;
+    int32_t loopcount ;
+    uint32_t intStat ;
+
+#ifdef _DEBUG
+    if (CyAsLLRequestResponse_IsRequest(req_p))
+    {
+        switch(CyAsLLRequestResponse_GetContext(req_p))
+        {
+        case CY_RQT_GENERAL_RQT_CONTEXT:
+            CyAsHalAssert(req_p->length * 2 + 2 < CY_CTX_GEN_MAX_DATA_SIZE) ;
+            break ;
+
+        case CY_RQT_RESOURCE_RQT_CONTEXT:
+            CyAsHalAssert(req_p->length * 2 + 2 < CY_CTX_RES_MAX_DATA_SIZE) ;
+            break ;
+
+        case CY_RQT_STORAGE_RQT_CONTEXT:
+            CyAsHalAssert(req_p->length * 2 + 2 < CY_CTX_STR_MAX_DATA_SIZE) ;
+            break ;
+
+        case CY_RQT_USB_RQT_CONTEXT:
+            CyAsHalAssert(req_p->length * 2 + 2 < CY_CTX_USB_MAX_DATA_SIZE) ;
+            break ;
+        }
+    }
+#endif
+
+    /* Write the request to the mail box registers */
+    if (req_p->length > 3)
+    {
+        uint16_t length = req_p->length ;
+        int which = 0 ;
+        int st = 1 ;
+
+        dev_p->ll_sending_rqt = CyTrue ;
+        while (which < length)
+        {
+            loopcount = CyAsLowLevelTimeoutCount ;
+            do
+            {
+                v = CyAsReadIntrStatus(dev_p) ;
+
+            } while (v && loopcount-- > 0) ;
+
+            if (v)
+            {
+                CyAsHalPrintMessage(">>>>>> LOW LEVEL TIMEOUT %x %x %x %x\n",
+                    CyAsHalReadRegister(dev_p->tag, CY_AS_MEM_MCU_MAILBOX0),
+                    CyAsHalReadRegister(dev_p->tag, CY_AS_MEM_MCU_MAILBOX1),
+                    CyAsHalReadRegister(dev_p->tag, CY_AS_MEM_MCU_MAILBOX2),
+                    CyAsHalReadRegister(dev_p->tag, CY_AS_MEM_MCU_MAILBOX3)) ;
+                return CY_AS_ERROR_TIMEOUT ;
+            }
+
+            if (dev_p->ll_abort_curr_rqt)
+            {
+                dev_p->ll_sending_rqt = CyFalse ;
+                dev_p->ll_abort_curr_rqt = CyFalse ;
+                return CY_AS_ERROR_CANCELED ;
+            }
+
+            intStat = CyAsHalDisableInterrupts () ;
+
+            /*
+             * Check again whether the mailbox is free. It is possible that an ISR came in
+             * and wrote into the mailboxes since we last checked the status.
+             */
+            v = CyAsHalReadRegister(dev_p->tag, CY_AS_MEM_MCU_MB_STAT) & CY_AS_MEM_P0_MCU_MBNOTRD ;
+            if (v)
+            {
+                /* Go back to the original check since the mailbox is not free. */
+                CyAsHalEnableInterrupts(intStat) ;
+                continue ;
+            }
+
+            if (which == 0)
+            {
+                CyAsHalWriteRegister(dev_p->tag, CY_AS_MEM_MCU_MAILBOX1, length) ;
+                st = 2 ;
+            }
+            else
+            {
+                st = 1;
+            }
+
+            while ((which < length) && (st < 4))
+            {
+                CyAsHalWriteRegister(dev_p->tag, CyCastInt2UInt16(CY_AS_MEM_MCU_MAILBOX0 + st), req_p->data[which++]) ;
+                st++ ;
+            }
+
+            mb0 = req_p->box0 ;
+            if (which == length)
+            {
+                dev_p->ll_sending_rqt = CyFalse ;
+                mb0 |= CY_AS_REQUEST_RESPONSE_LAST_MASK ;
+            }
+
+            if (dev_p->ll_abort_curr_rqt)
+            {
+                dev_p->ll_sending_rqt = CyFalse ;
+                dev_p->ll_abort_curr_rqt = CyFalse ;
+                CyAsHalEnableInterrupts (intStat) ;
+                return CY_AS_ERROR_CANCELED ;
+            }
+
+            CyAsHalWriteRegister(dev_p->tag, CY_AS_MEM_MCU_MAILBOX0, mb0) ;
+
+            /* Wait for the MBOX interrupt to be high */
+            CyAsHalSleep150() ;
+            CyAsHalEnableInterrupts (intStat) ;
+        }
+    }
+    else
+    {
+CheckMailboxAvailability:
+        /*
+        * Wait for the mailbox registers to become available.  This should be a very quick
+        * wait as the firmware is designed to accept requests at interrupt time and queue
+        * them for future processing.
+        */
+        loopcount = CyAsLowLevelTimeoutCount ;
+        do
+        {
+            v = CyAsReadIntrStatus(dev_p) ;
+
+        } while (v && loopcount-- > 0) ;
+
+        if (v)
+        {
+            CyAsHalPrintMessage(">>>>>> LOW LEVEL TIMEOUT %x %x %x %x\n",
+                CyAsHalReadRegister(dev_p->tag, CY_AS_MEM_MCU_MAILBOX0),
+                CyAsHalReadRegister(dev_p->tag, CY_AS_MEM_MCU_MAILBOX1),
+                CyAsHalReadRegister(dev_p->tag, CY_AS_MEM_MCU_MAILBOX2),
+                CyAsHalReadRegister(dev_p->tag, CY_AS_MEM_MCU_MAILBOX3)) ;
+            return CY_AS_ERROR_TIMEOUT ;
+        }
+
+        intStat = CyAsHalDisableInterrupts ();
+
+        /*
+         * Check again whether the mailbox is free. It is possible that an ISR came in
+         * and wrote into the mailboxes since we last checked the status.
+         */
+        v = CyAsHalReadRegister(dev_p->tag, CY_AS_MEM_MCU_MB_STAT) & CY_AS_MEM_P0_MCU_MBNOTRD ;
+        if (v)
+        {
+            /* Go back to the original check since the mailbox is not free. */
+            CyAsHalEnableInterrupts(intStat) ;
+            goto CheckMailboxAvailability ;
+        }
+
+        /* Write the data associated with the request into the mbox registers 1 - 3 */
+        v = 0 ;
+        for(i = req_p->length - 1 ; i >= 0 ; i--)
+            CyAsHalWriteRegister(dev_p->tag, CyCastInt2UInt16(CY_AS_MEM_MCU_MAILBOX1 + i), req_p->data[i]) ;
+
+        /* Write the mbox register 0 to trigger the interrupt */
+        CyAsHalWriteRegister(dev_p->tag, CY_AS_MEM_MCU_MAILBOX0, req_p->box0 | CY_AS_REQUEST_RESPONSE_LAST_MASK) ;
+
+        CyAsHalSleep150() ;
+        CyAsHalEnableInterrupts (intStat);
+    }
+
+    return CY_AS_ERROR_SUCCESS ;
+}
+
+/*
+* This function queues a single request to be sent to the firmware.
+*/
+extern CyAsReturnStatus_t
+CyAsLLSendRequest(
+                  CyAsDevice *dev_p,
+                  CyAsLLRequestResponse *req,   /* The request to send */
+                  CyAsLLRequestResponse *resp,  /* Storage for a reply, must be sure it is of sufficient size */
+                  CyBool sync,                  /* If true, this is a synchronous request */
+                  CyAsResponseCallback cb       /* Callback to call when reply is received */
+                  )
+{
+    CyAsContext *ctxt_p ;
+    uint16_t box0 = req->box0 ;
+    uint8_t context ;
+    CyAsReturnStatus_t ret = CY_AS_ERROR_SUCCESS ;
+    CyAsLLRequestListNode *node_p ;
+    uint32_t mask, state ;
+
+    CyAsHalAssert(dev_p->sig == CY_AS_DEVICE_HANDLE_SIGNATURE);
+
+    context = CyAsMboxGetContext(box0) ;
+    CyAsHalAssert(context < CY_RQT_CONTEXT_COUNT) ;
+    ctxt_p = dev_p->context[context] ;
+
+    /* Allocate the list node */
+    state = CyAsHalDisableInterrupts() ;
+    node_p = (CyAsLLRequestListNode *)CyAsHalCBAlloc(sizeof(CyAsLLRequestListNode)) ;
+    CyAsHalEnableInterrupts(state) ;
+
+    if (node_p == 0)
+        return CY_AS_ERROR_OUT_OF_MEMORY ;
+
+    /* Initialize the list node */
+    node_p->callback = cb ;
+    node_p->length = 0 ;
+    node_p->next = 0 ;
+    node_p->resp = resp ;
+    node_p->rqt = req ;
+    node_p->state = CY_AS_REQUEST_LIST_STATE_QUEUED ;
+    if (sync)
+        CyAsRequestNodeSetSync(node_p) ;
+
+    /* Put the request into the queue */
+    mask = CyAsHalDisableInterrupts() ;
+    if (ctxt_p->request_queue_p == 0)
+    {
+        /* Empty queue */
+        ctxt_p->request_queue_p = node_p ;
+        ctxt_p->last_node_p = node_p ;
+    }
+    else
+    {
+        ctxt_p->last_node_p->next = node_p ;
+        ctxt_p->last_node_p = node_p ;
+    }
+    CyAsHalEnableInterrupts(mask) ;
+    CyAsLLSendNextRequest(dev_p, ctxt_p) ;
+
+    if (!CyAsDeviceIsInCallback(dev_p))
+    {
+        mask = CyAsHalDisableInterrupts() ;
+        CyAsMailBoxQueuedDataHandler(dev_p) ;
+        CyAsHalEnableInterrupts(mask) ;
+    }
+
+    return ret ;
+}
+
+static void
+CyAsLLSendCallback(
+                   CyAsDevice *dev_p,
+                   uint8_t context,
+                   CyAsLLRequestResponse *rqt,
+                   CyAsLLRequestResponse *resp,
+                   CyAsReturnStatus_t ret)
+{
+    (void)rqt ;
+    (void)resp ;
+    (void)ret ;
+
+
+    CyAsHalAssert(dev_p->sig == CY_AS_DEVICE_HANDLE_SIGNATURE) ;
+
+    /*
+    * Storage the state to return to the caller
+    */
+    dev_p->ll_error = ret ;
+
+    /*
+    * Now wake the caller
+    */
+    CyAsHalWake(&dev_p->context[context]->channel) ;
+}
+
+CyAsReturnStatus_t
+CyAsLLSendRequestWaitReply(
+                           CyAsDevice *dev_p,
+                           CyAsLLRequestResponse *req,                          /* The request to send */
+                           CyAsLLRequestResponse *resp                          /* Storage for a reply, must be sure it is of sufficient size */
+                           )
+{
+    CyAsReturnStatus_t ret ;
+    uint8_t context ;
+    uint32_t loopcount = 800 ;  /* Larger 8 sec time-out to handle the init delay for slower storage devices in USB FS. */
+    CyAsContext *ctxt_p ;
+
+    /* Get the context for the request */
+    context = CyAsLLRequestResponse_GetContext(req) ;
+    CyAsHalAssert(context < CY_RQT_CONTEXT_COUNT) ;
+    ctxt_p = dev_p->context[context] ;
+
+    ret = CyAsLLSendRequest(dev_p, req, resp, CyTrue, CyAsLLSendCallback) ;
+    if (ret != CY_AS_ERROR_SUCCESS)
+        return ret ;
+
+    while (loopcount-- > 0)
+    {
+        /*
+        * Sleep while we wait on the response.  Receiving the reply will
+        * wake this thread.  We will wait, at most 2 seconds (10 ms * 200
+        * tries) before we timeout.  Note if the reply arrives, we will not
+        * sleep the entire 10 ms, just til the reply arrives.
+        */
+        CyAsHalSleepOn(&ctxt_p->channel, 10) ;
+
+        /*
+        * If the request has left the queue, it means the request has been sent
+        * and the reply has been received.  This means we can return to the caller
+        * and be sure the reply has been received.
+        */
+        if (!CyAsLLIsInQueue(ctxt_p, req))
+            return dev_p->ll_error ;
+    }
+
+    /* Remove the QueueListNode for this request. */
+    CyAsLLRemoveRequest(dev_p, ctxt_p, req, CyTrue) ;
+
+    return CY_AS_ERROR_TIMEOUT ;
+}
+
+CyAsReturnStatus_t
+CyAsLLRegisterRequestCallback(
+                              CyAsDevice *dev_p,
+                              uint8_t context,
+                              CyAsResponseCallback cb)
+{
+    CyAsContext *ctxt_p ;
+    CyAsHalAssert(context < CY_RQT_CONTEXT_COUNT) ;
+    ctxt_p = dev_p->context[context] ;
+
+    ctxt_p->request_callback = cb ;
+    return CY_AS_ERROR_SUCCESS ;
+}
+
+void
+CyAsLLRequestResponse_Pack(
+                           CyAsLLRequestResponse *req_p,
+                           uint32_t offset,
+                           uint32_t length,
+                           void *data_p)
+{
+    uint16_t dt ;
+    uint8_t *dp = (uint8_t *)data_p ;
+
+    while (length > 1)
+    {
+        dt = ((*dp++) << 8) ;
+        dt |= (*dp++) ;
+        CyAsLLRequestResponse_SetWord(req_p, offset, dt) ;
+        offset++ ;
+        length -= 2 ;
+    }
+
+    if (length == 1)
+    {
+        dt = (*dp << 8) ;
+        CyAsLLRequestResponse_SetWord(req_p, offset, dt) ;
+    }
+}
+
+void
+CyAsLLRequestResponse_Unpack(
+                             CyAsLLRequestResponse *req_p,
+                             uint32_t offset,
+                             uint32_t length,
+                             void *data_p)
+{
+    uint8_t *dp = (uint8_t *)data_p ;
+
+    while (length-- > 0)
+    {
+        uint16_t val = CyAsLLRequestResponse_GetWord(req_p, offset++) ;
+        *dp++ = (uint8_t)((val >> 8) & 0xff) ;
+
+        if (length)
+        {
+            length-- ;
+            *dp++ = (uint8_t)(val & 0xff) ;
+        }
+    }
+}
+
+extern CyAsReturnStatus_t
+CyAsLLSendStatusResponse(
+                         CyAsDevice *dev_p,
+                         uint8_t context,
+                         uint16_t code,
+                         uint8_t clear_storage)
+{
+    CyAsReturnStatus_t ret ;
+    CyAsLLRequestResponse resp ;
+    CyAsLLRequestResponse *resp_p = &resp ;
+
+    CyAsHalMemSet(resp_p, 0, sizeof(resp)) ;
+    resp_p->length = 1 ;
+    CyAsLLRequestResponse_SetResponse(resp_p) ;
+    CyAsLLRequestResponse_SetContext(resp_p, context) ;
+
+    if (clear_storage)
+        CyAsLLRequestResponse_SetClearStorageFlag(resp_p) ;
+
+    CyAsLLRequestResponse_SetCode(resp_p, CY_RESP_SUCCESS_FAILURE) ;
+    CyAsLLRequestResponse_SetWord(resp_p, 0, code) ;
+
+    ret = CyAsSendOne(dev_p, resp_p) ;
+
+    return ret ;
+}
+
+extern CyAsReturnStatus_t
+CyAsLLSendDataResponse(
+                       CyAsDevice *dev_p,
+                       uint8_t context,
+                       uint16_t code,
+                       uint16_t length,
+                       void *data)
+{
+    CyAsLLRequestResponse *resp_p ;
+    uint16_t wlen ;
+    uint8_t respbuf[256] ;
+
+    if (length > 192)
+        return CY_AS_ERROR_INVALID_SIZE ;
+
+    wlen = length / 2 ;                                 /* Word length for bytes */
+    if (length % 2)                                     /* If byte length odd, add one more */
+        wlen++ ;
+    wlen++ ;                                            /* One for the length of field */
+
+    resp_p = (CyAsLLRequestResponse *)respbuf ;
+    CyAsHalMemSet(resp_p, 0, sizeof(respbuf)) ;
+    resp_p->length = wlen ;
+    CyAsLLRequestResponse_SetContext(resp_p, context) ;
+    CyAsLLRequestResponse_SetCode(resp_p, code) ;
+
+    CyAsLLRequestResponse_SetWord(resp_p, 0, length) ;
+    CyAsLLRequestResponse_Pack(resp_p, 1, length, data) ;
+
+    return CyAsSendOne(dev_p, resp_p) ;
+}
+
+static CyBool
+CyAsLLIsEPTransferRelatedRequest(CyAsLLRequestResponse *rqt_p, CyAsEndPointNumber_t ep)
+{
+    uint16_t v ;
+    uint8_t  type = CyAsLLRequestResponse_GetCode(rqt_p) ;
+
+    if (CyAsLLRequestResponse_GetContext(rqt_p) != CY_RQT_USB_RQT_CONTEXT)
+        return CyFalse ;
+
+    /*
+     * When cancelling outstanding EP0 data transfers, any pending
+     * Setup ACK requests also need to be cancelled.
+     */
+    if ((ep == 0) && (type == CY_RQT_ACK_SETUP_PACKET))
+        return CyTrue ;
+
+    if (type != CY_RQT_USB_EP_DATA)
+        return CyFalse ;
+
+    v = CyAsLLRequestResponse_GetWord(rqt_p, 0) ;
+    if ((CyAsEndPointNumber_t)((v >> 13) & 1) != ep)
+        return CyFalse ;
+
+    return CyTrue ;
+}
+
+CyAsReturnStatus_t
+CyAsLLRemoveEpDataRequests(CyAsDevice *dev_p, CyAsEndPointNumber_t ep)
+{
+    CyAsContext *ctxt_p ;
+    CyAsLLRequestListNode *node_p ;
+    uint32_t imask ;
+
+    /*
+    * First, remove any queued requests
+    */
+    ctxt_p = dev_p->context[CY_RQT_USB_RQT_CONTEXT] ;
+    if (ctxt_p)
+    {
+        for(node_p = ctxt_p->request_queue_p ; node_p ; node_p = node_p->next)
+        {
+            if (CyAsLLIsEPTransferRelatedRequest(node_p->rqt, ep))
+            {
+                CyAsLLRemoveRequest(dev_p, ctxt_p, node_p->rqt, CyFalse) ;
+                break ;
+            }
+        }
+
+        /*
+        * Now, deal with any request that may be in transit
+        */
+        imask = CyAsHalDisableInterrupts() ;
+
+        if (ctxt_p->request_queue_p != 0 &&
+            CyAsLLIsEPTransferRelatedRequest(ctxt_p->request_queue_p->rqt, ep) &&
+            CyAsRequestGetNodeState(ctxt_p->request_queue_p) == CY_AS_REQUEST_LIST_STATE_WAITING)
+        {
+            CyAsHalPrintMessage("Need to remove an in-transit request to Antioch\n") ;
+
+            /*
+            * If the request has not been fully sent to West Bridge yet, abort sending.
+            * Otherwise, terminate the request with a CANCELED status. Firmware will
+            * already have terminated this transfer.
+            */
+            if (dev_p->ll_sending_rqt)
+                dev_p->ll_abort_curr_rqt = CyTrue ;
+            else
+            {
+                uint32_t state ;
+
+                node_p = ctxt_p->request_queue_p ;
+                if (node_p->callback)
+                    node_p->callback(dev_p, ctxt_p->number, node_p->rqt, node_p->resp, CY_AS_ERROR_CANCELED) ;
+
+                ctxt_p->request_queue_p = node_p->next ;
+                state = CyAsHalDisableInterrupts() ;
+                CyAsHalCBFree(node_p) ;
+                CyAsHalEnableInterrupts(state) ;
+            }
+        }
+
+        CyAsHalEnableInterrupts(imask) ;
+    }
+
+    return CY_AS_ERROR_SUCCESS ;
+}