diff -r b7e488c49d0d -r 117faf51deac omap3530/beagle_drivers/wb/api/src/cyaslowlevel.c --- /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 +## +## /license/license.txt +## +## where 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 ; +}