diff options
Diffstat (limited to 'drivers/staging/westbridge/astoria/api/src')
-rw-r--r-- | drivers/staging/westbridge/astoria/api/src/cyasdma.c | 1107 | ||||
-rw-r--r-- | drivers/staging/westbridge/astoria/api/src/cyasintr.c | 143 | ||||
-rw-r--r-- | drivers/staging/westbridge/astoria/api/src/cyaslep2pep.c | 358 | ||||
-rw-r--r-- | drivers/staging/westbridge/astoria/api/src/cyaslowlevel.c | 1264 | ||||
-rw-r--r-- | drivers/staging/westbridge/astoria/api/src/cyasmisc.c | 3474 | ||||
-rw-r--r-- | drivers/staging/westbridge/astoria/api/src/cyasmtp.c | 1128 | ||||
-rw-r--r-- | drivers/staging/westbridge/astoria/api/src/cyasstorage.c | 4104 | ||||
-rw-r--r-- | drivers/staging/westbridge/astoria/api/src/cyasusb.c | 3718 |
8 files changed, 15296 insertions, 0 deletions
diff --git a/drivers/staging/westbridge/astoria/api/src/cyasdma.c b/drivers/staging/westbridge/astoria/api/src/cyasdma.c new file mode 100644 index 000000000000..de67e1310503 --- /dev/null +++ b/drivers/staging/westbridge/astoria/api/src/cyasdma.c @@ -0,0 +1,1107 @@ +/* Cypress West Bridge API source file (cyasdma.c) +## =========================== +## Copyright (C) 2010 Cypress Semiconductor +## +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either version 2 +## of the License, or (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 51 Franklin Street, Fifth Floor +## Boston, MA 02110-1301, USA. +## =========================== +*/ + +#include "../../include/linux/westbridge/cyashal.h" +#include "../../include/linux/westbridge/cyasdma.h" +#include "../../include/linux/westbridge/cyaslowlevel.h" +#include "../../include/linux/westbridge/cyaserr.h" +#include "../../include/linux/westbridge/cyasregs.h" + +/* + * Add the DMA queue entry to the free list to be re-used later + */ +static void +cy_as_dma_add_request_to_free_queue(cy_as_device *dev_p, + cy_as_dma_queue_entry *req_p) +{ + uint32_t imask; + imask = cy_as_hal_disable_interrupts(); + + req_p->next_p = dev_p->dma_freelist_p; + dev_p->dma_freelist_p = req_p; + + cy_as_hal_enable_interrupts(imask); +} + +/* + * Get a DMA queue entry from the free list. + */ +static cy_as_dma_queue_entry * +cy_as_dma_get_dma_queue_entry(cy_as_device *dev_p) +{ + cy_as_dma_queue_entry *req_p; + uint32_t imask; + + cy_as_hal_assert(dev_p->dma_freelist_p != 0); + + imask = cy_as_hal_disable_interrupts(); + req_p = dev_p->dma_freelist_p; + dev_p->dma_freelist_p = req_p->next_p; + cy_as_hal_enable_interrupts(imask); + + return req_p; +} + +/* + * Set the maximum size that the West Bridge hardware + * can handle in a single DMA operation. This size + * may change for the P <-> U endpoints as a function + * of the endpoint type and whether we are running + * at full speed or high speed. + */ +cy_as_return_status_t +cy_as_dma_set_max_dma_size(cy_as_device *dev_p, + cy_as_end_point_number_t ep, uint32_t size) +{ + /* In MTP mode, EP2 is allowed to have all max sizes. */ + if ((!dev_p->is_mtp_firmware) || (ep != 0x02)) { + if (size < 64 || size > 1024) + return CY_AS_ERROR_INVALID_SIZE; + } + + CY_AS_NUM_EP(dev_p, ep)->maxhwdata = (uint16_t)size; + return CY_AS_ERROR_SUCCESS; +} + +/* + * The callback for requests sent to West Bridge + * to relay endpoint data. Endpoint data for EP0 + * and EP1 are sent using mailbox requests. This + * is the callback that is called when a response + * to a mailbox request to send data is received. + */ +static void +cy_as_dma_request_callback( + cy_as_device *dev_p, + uint8_t context, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *resp_p, + cy_as_return_status_t ret) +{ + uint16_t v; + uint16_t datacnt; + cy_as_end_point_number_t ep; + + (void)context; + + cy_as_log_debug_message(5, "cy_as_dma_request_callback called"); + + /* + * extract the return code from the firmware + */ + if (ret == CY_AS_ERROR_SUCCESS) { + if (cy_as_ll_request_response__get_code(resp_p) != + CY_RESP_SUCCESS_FAILURE) + ret = CY_AS_ERROR_INVALID_RESPONSE; + else + ret = cy_as_ll_request_response__get_word(resp_p, 0); + } + + /* + * extract the endpoint number and the transferred byte count + * from the request. + */ + v = cy_as_ll_request_response__get_word(req_p, 0); + ep = (cy_as_end_point_number_t)((v >> 13) & 0x01); + + if (ret == CY_AS_ERROR_SUCCESS) { + /* + * if the firmware returns success, + * all of the data requested was + * transferred. there are no partial + * transfers. + */ + datacnt = v & 0x3FF; + } else { + /* + * if the firmware returned an error, no data was transferred. + */ + datacnt = 0; + } + + /* + * queue the request and response data structures for use with the + * next EP0 or EP1 request. + */ + if (ep == 0) { + dev_p->usb_ep0_dma_req = req_p; + dev_p->usb_ep0_dma_resp = resp_p; + } else { + dev_p->usb_ep1_dma_req = req_p; + dev_p->usb_ep1_dma_resp = resp_p; + } + + /* + * call the DMA complete function so we can + * signal that this portion of the transfer + * has completed. if the low level request + * was canceled, we do not need to signal + * the completed function as the only way a + * cancel can happen is via the DMA cancel + * function. + */ + if (ret != CY_AS_ERROR_CANCELED) + cy_as_dma_completed_callback(dev_p->tag, ep, datacnt, ret); +} + +/* + * Set the DRQ mask register for the given endpoint number. If state is + * CyTrue, the DRQ interrupt for the given endpoint is enabled, otherwise + * it is disabled. + */ +static void +cy_as_dma_set_drq(cy_as_device *dev_p, + cy_as_end_point_number_t ep, cy_bool state) +{ + uint16_t mask; + uint16_t v; + uint32_t intval; + + /* + * there are not DRQ register bits for EP0 and EP1 + */ + if (ep == 0 || ep == 1) + return; + + /* + * disable interrupts while we do this to be sure the state of the + * DRQ mask register is always well defined. + */ + intval = cy_as_hal_disable_interrupts(); + + /* + * set the DRQ bit to the given state for the ep given + */ + mask = (1 << ep); + v = cy_as_hal_read_register(dev_p->tag, CY_AS_MEM_P0_DRQ_MASK); + + if (state) + v |= mask; + else + v &= ~mask; + + cy_as_hal_write_register(dev_p->tag, CY_AS_MEM_P0_DRQ_MASK, v); + cy_as_hal_enable_interrupts(intval); +} + +/* +* Send the next DMA request for the endpoint given +*/ +static void +cy_as_dma_send_next_dma_request(cy_as_device *dev_p, cy_as_dma_end_point *ep_p) +{ + uint32_t datacnt; + void *buf_p; + cy_as_dma_queue_entry *dma_p; + + cy_as_log_debug_message(6, "cy_as_dma_send_next_dma_request called"); + + /* If the queue is empty, nothing to do */ + dma_p = ep_p->queue_p; + if (dma_p == 0) { + /* + * there are no pending DMA requests + * for this endpoint. disable the DRQ + * mask bits to insure no interrupts + * will be triggered by this endpoint + * until someone is interested in the data. + */ + cy_as_dma_set_drq(dev_p, ep_p->ep, cy_false); + return; + } + + cy_as_dma_end_point_set_running(ep_p); + + /* + * get the number of words that still + * need to be xferred in this request. + */ + datacnt = dma_p->size - dma_p->offset; + cy_as_hal_assert(datacnt >= 0); + + /* + * the HAL layer should never limit the size + * of the transfer to something less than the + * maxhwdata otherwise, the data will be sent + * in packets that are not correct in size. + */ + cy_as_hal_assert(ep_p->maxhaldata == CY_AS_DMA_MAX_SIZE_HW_SIZE + || ep_p->maxhaldata >= ep_p->maxhwdata); + + /* + * update the number of words that need to be xferred yet + * based on the limits of the HAL layer. + */ + if (ep_p->maxhaldata == CY_AS_DMA_MAX_SIZE_HW_SIZE) { + if (datacnt > ep_p->maxhwdata) + datacnt = ep_p->maxhwdata; + } else { + if (datacnt > ep_p->maxhaldata) + datacnt = ep_p->maxhaldata; + } + + /* + * find a pointer to the data that needs to be transferred + */ + buf_p = (((char *)dma_p->buf_p) + dma_p->offset); + + /* + * mark a request in transit + */ + cy_as_dma_end_point_set_in_transit(ep_p); + + if (ep_p->ep == 0 || ep_p->ep == 1) { + /* + * if this is a WRITE request on EP0 and EP1 + * we write the data via an EP_DATA request + * to west bridge via the mailbox registers. + * if this is a READ request, we do nothing + * and the data will arrive via an EP_DATA + * request from west bridge. in the request + * handler for the USB context we will pass + * the data back into the DMA module. + */ + if (dma_p->readreq == cy_false) { + uint16_t v; + uint16_t len; + cy_as_ll_request_response *resp_p; + cy_as_ll_request_response *req_p; + cy_as_return_status_t ret; + + len = (uint16_t)(datacnt / 2); + if (datacnt % 2) + len++; + + len++; + + if (ep_p->ep == 0) { + req_p = dev_p->usb_ep0_dma_req; + resp_p = dev_p->usb_ep0_dma_resp; + dev_p->usb_ep0_dma_req = 0; + dev_p->usb_ep0_dma_resp = 0; + } else { + req_p = dev_p->usb_ep1_dma_req; + resp_p = dev_p->usb_ep1_dma_resp; + dev_p->usb_ep1_dma_req = 0; + dev_p->usb_ep1_dma_resp = 0; + } + + cy_as_hal_assert(req_p != 0); + cy_as_hal_assert(resp_p != 0); + cy_as_hal_assert(len <= 64); + + cy_as_ll_init_request(req_p, CY_RQT_USB_EP_DATA, + CY_RQT_USB_RQT_CONTEXT, len); + + v = (uint16_t)(datacnt | (ep_p->ep << 13) | (1 << 14)); + if (dma_p->offset == 0) + v |= (1 << 12);/* Set the first packet bit */ + if (dma_p->offset + datacnt == dma_p->size) + v |= (1 << 11);/* Set the last packet bit */ + + cy_as_ll_request_response__set_word(req_p, 0, v); + cy_as_ll_request_response__pack(req_p, + 1, datacnt, buf_p); + + cy_as_ll_init_response(resp_p, 1); + + ret = cy_as_ll_send_request(dev_p, req_p, resp_p, + cy_false, cy_as_dma_request_callback); + if (ret == CY_AS_ERROR_SUCCESS) + cy_as_log_debug_message(5, + "+++ send EP 0/1 data via mailbox registers"); + else + cy_as_log_debug_message(5, + "+++ error sending EP 0/1 data via mailbox " + "registers - CY_AS_ERROR_TIMEOUT"); + + if (ret != CY_AS_ERROR_SUCCESS) + cy_as_dma_completed_callback(dev_p->tag, + ep_p->ep, 0, ret); + } + } else { + /* + * this is a DMA request on an endpoint that is accessible + * via the P port. ask the HAL DMA capabilities to + * perform this. the amount of data sent is limited by the + * HAL max size as well as what we need to send. if the + * ep_p->maxhaldata is set to a value larger than the + * endpoint buffer size, then we will pass more than a + * single buffer worth of data to the HAL layer and expect + * the HAL layer to divide the data into packets. the last + * parameter here (ep_p->maxhwdata) gives the packet size for + * the data so the HAL layer knows what the packet size should + * be. + */ + if (cy_as_dma_end_point_is_direction_in(ep_p)) + cy_as_hal_dma_setup_write(dev_p->tag, + ep_p->ep, buf_p, datacnt, ep_p->maxhwdata); + else + cy_as_hal_dma_setup_read(dev_p->tag, + ep_p->ep, buf_p, datacnt, ep_p->maxhwdata); + + /* + * the DRQ interrupt for this endpoint should be enabled + * so that the data transfer progresses at interrupt time. + */ + cy_as_dma_set_drq(dev_p, ep_p->ep, cy_true); + } +} + +/* + * This function is called when the HAL layer has + * completed the last requested DMA operation. + * This function sends/receives the next batch of + * data associated with the current DMA request, + * or it is is complete, moves to the next DMA request. + */ +void +cy_as_dma_completed_callback(cy_as_hal_device_tag tag, + cy_as_end_point_number_t ep, uint32_t cnt, cy_as_return_status_t status) +{ + uint32_t mask; + cy_as_dma_queue_entry *req_p; + cy_as_dma_end_point *ep_p; + cy_as_device *dev_p = cy_as_device_find_from_tag(tag); + + /* Make sure the HAL layer gave us good parameters */ + cy_as_hal_assert(dev_p != 0); + cy_as_hal_assert(dev_p->sig == CY_AS_DEVICE_HANDLE_SIGNATURE); + cy_as_hal_assert(ep < 16); + + + /* Get the endpoint ptr */ + ep_p = CY_AS_NUM_EP(dev_p, ep); + cy_as_hal_assert(ep_p->queue_p != 0); + + /* Get a pointer to the current entry in the queue */ + mask = cy_as_hal_disable_interrupts(); + req_p = ep_p->queue_p; + + /* Update the offset to reflect the data actually received or sent */ + req_p->offset += cnt; + + /* + * if we are still sending/receiving the current packet, + * send/receive the next chunk basically we keep going + * if we have not sent/received enough data, and we are + * not doing a packet operation, and the last packet + * sent or received was a full sized packet. in other + * words, when we are NOT doing a packet operation, a + * less than full size packet (a short packet) will + * terminate the operation. + * + * note: if this is EP1 request and the request has + * timed out, it means the buffer is not free. + * we have to resend the data. + * + * note: for the MTP data transfers, the DMA transfer + * for the next packet can only be started asynchronously, + * after a firmware event notifies that the device is ready. + */ + if (((req_p->offset != req_p->size) && (req_p->packet == cy_false) && + ((cnt == ep_p->maxhaldata) || ((cnt == ep_p->maxhwdata) && + ((ep != CY_AS_MTP_READ_ENDPOINT) || + (cnt == dev_p->usb_max_tx_size))))) + || ((ep == 1) && (status == CY_AS_ERROR_TIMEOUT))) { + cy_as_hal_enable_interrupts(mask); + + /* + * and send the request again to send the next block of + * data. special handling for MTP transfers on E_ps 2 + * and 6. the send_next_request will be processed based + * on the event sent by the firmware. + */ + if ((ep == CY_AS_MTP_WRITE_ENDPOINT) || ( + (ep == CY_AS_MTP_READ_ENDPOINT) && + (!cy_as_dma_end_point_is_direction_in(ep_p)))) + cy_as_dma_end_point_set_stopped(ep_p); + else + cy_as_dma_send_next_dma_request(dev_p, ep_p); + } else { + /* + * we get here if ... + * we have sent or received all of the data + * or + * we are doing a packet operation + * or + * we receive a short packet + */ + + /* + * remove this entry from the DMA queue for this endpoint. + */ + cy_as_dma_end_point_clear_in_transit(ep_p); + ep_p->queue_p = req_p->next_p; + if (ep_p->last_p == req_p) { + /* + * we have removed the last packet from the DMA queue, + * disable the interrupt associated with this interrupt. + */ + ep_p->last_p = 0; + cy_as_hal_enable_interrupts(mask); + cy_as_dma_set_drq(dev_p, ep, cy_false); + } else + cy_as_hal_enable_interrupts(mask); + + if (req_p->cb) { + /* + * if the request has a callback associated with it, + * call the callback to tell the interested party that + * this DMA request has completed. + * + * note, we set the in_callback bit to insure that we + * cannot recursively call an API function that is + * synchronous only from a callback. + */ + cy_as_device_set_in_callback(dev_p); + (*req_p->cb)(dev_p, ep, req_p->buf_p, + req_p->offset, status); + cy_as_device_clear_in_callback(dev_p); + } + + /* + * we are done with this request, put it on the freelist to be + * reused at a later time. + */ + cy_as_dma_add_request_to_free_queue(dev_p, req_p); + + if (ep_p->queue_p == 0) { + /* + * if the endpoint is out of DMA entries, set the + * endpoint as stopped. + */ + cy_as_dma_end_point_set_stopped(ep_p); + + /* + * the DMA queue is empty, wake any task waiting on + * the QUEUE to drain. + */ + if (cy_as_dma_end_point_is_sleeping(ep_p)) { + cy_as_dma_end_point_set_wake_state(ep_p); + cy_as_hal_wake(&ep_p->channel); + } + } else { + /* + * if the queued operation is a MTP transfer, + * wait until firmware event before sending + * down the next DMA request. + */ + if ((ep == CY_AS_MTP_WRITE_ENDPOINT) || + ((ep == CY_AS_MTP_READ_ENDPOINT) && + (!cy_as_dma_end_point_is_direction_in(ep_p))) || + ((ep == dev_p->storage_read_endpoint) && + (!cy_as_device_is_p2s_dma_start_recvd(dev_p))) + || ((ep == dev_p->storage_write_endpoint) && + (!cy_as_device_is_p2s_dma_start_recvd(dev_p)))) + cy_as_dma_end_point_set_stopped(ep_p); + else + cy_as_dma_send_next_dma_request(dev_p, ep_p); + } + } +} + +/* +* This function is used to kick start DMA on a given +* channel. If DMA is already running on the given +* endpoint, nothing happens. If DMA is not running, +* the first entry is pulled from the DMA queue and +* sent/recevied to/from the West Bridge device. +*/ +cy_as_return_status_t +cy_as_dma_kick_start(cy_as_device *dev_p, cy_as_end_point_number_t ep) +{ + cy_as_dma_end_point *ep_p; + cy_as_hal_assert(dev_p->sig == CY_AS_DEVICE_HANDLE_SIGNATURE); + + ep_p = CY_AS_NUM_EP(dev_p, ep); + + /* We are already running */ + if (cy_as_dma_end_point_is_running(ep_p)) + return CY_AS_ERROR_SUCCESS; + + cy_as_dma_send_next_dma_request(dev_p, ep_p); + return CY_AS_ERROR_SUCCESS; +} + +/* + * This function stops the given endpoint. Stopping and endpoint cancels + * any pending DMA operations and frees all resources associated with the + * given endpoint. + */ +static cy_as_return_status_t +cy_as_dma_stop_end_point(cy_as_device *dev_p, cy_as_end_point_number_t ep) +{ + cy_as_return_status_t ret; + cy_as_dma_end_point *ep_p = CY_AS_NUM_EP(dev_p, ep); + + /* + * cancel any pending DMA requests associated with this endpoint. this + * cancels any DMA requests at the HAL layer as well as dequeues any + * request that is currently pending. + */ + ret = cy_as_dma_cancel(dev_p, ep, CY_AS_ERROR_CANCELED); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + /* + * destroy the sleep channel + */ + if (!cy_as_hal_destroy_sleep_channel(&ep_p->channel) + && ret == CY_AS_ERROR_SUCCESS) + ret = CY_AS_ERROR_DESTROY_SLEEP_CHANNEL_FAILED; + + /* + * free the memory associated with this endpoint + */ + cy_as_hal_free(ep_p); + + /* + * set the data structure ptr to something sane since the + * previous pointer is now free. + */ + dev_p->endp[ep] = 0; + + return ret; +} + +/* + * This method stops the USB stack. This is an internal function that does + * all of the work of destroying the USB stack without the protections that + * we provide to the API (i.e. stopping at stack that is not running). + */ +static cy_as_return_status_t +cy_as_dma_stop_internal(cy_as_device *dev_p) +{ + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + cy_as_return_status_t lret; + cy_as_end_point_number_t i; + + /* + * stop all of the endpoints. this cancels all DMA requests, and + * frees all resources associated with each endpoint. + */ + for (i = 0; i < sizeof(dev_p->endp)/(sizeof(dev_p->endp[0])); i++) { + lret = cy_as_dma_stop_end_point(dev_p, i); + if (lret != CY_AS_ERROR_SUCCESS && ret == CY_AS_ERROR_SUCCESS) + ret = lret; + } + + /* + * now, free the list of DMA requests structures that we use to manage + * DMA requests. + */ + while (dev_p->dma_freelist_p) { + cy_as_dma_queue_entry *req_p; + uint32_t imask = cy_as_hal_disable_interrupts(); + + req_p = dev_p->dma_freelist_p; + dev_p->dma_freelist_p = req_p->next_p; + + cy_as_hal_enable_interrupts(imask); + + cy_as_hal_free(req_p); + } + + cy_as_ll_destroy_request(dev_p, dev_p->usb_ep0_dma_req); + cy_as_ll_destroy_request(dev_p, dev_p->usb_ep1_dma_req); + cy_as_ll_destroy_response(dev_p, dev_p->usb_ep0_dma_resp); + cy_as_ll_destroy_response(dev_p, dev_p->usb_ep1_dma_resp); + + return ret; +} + + +/* + * CyAsDmaStop() + * + * This function shuts down the DMA module. All resources + * associated with the DMA module will be freed. This + * routine is the API stop function. It insures that we + * are stopping a stack that is actually running and then + * calls the internal function to do the work. + */ +cy_as_return_status_t +cy_as_dma_stop(cy_as_device *dev_p) +{ + cy_as_return_status_t ret; + + ret = cy_as_dma_stop_internal(dev_p); + cy_as_device_set_dma_stopped(dev_p); + + return ret; +} + +/* + * CyAsDmaStart() + * + * This function intializes the DMA module to insure it is up and running. + */ +cy_as_return_status_t +cy_as_dma_start(cy_as_device *dev_p) +{ + cy_as_end_point_number_t i; + uint16_t cnt; + + if (cy_as_device_is_dma_running(dev_p)) + return CY_AS_ERROR_ALREADY_RUNNING; + + /* + * pre-allocate DMA queue structures to be used in the interrupt context + */ + for (cnt = 0; cnt < 32; cnt++) { + cy_as_dma_queue_entry *entry_p = (cy_as_dma_queue_entry *) + cy_as_hal_alloc(sizeof(cy_as_dma_queue_entry)); + if (entry_p == 0) { + cy_as_dma_stop_internal(dev_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + cy_as_dma_add_request_to_free_queue(dev_p, entry_p); + } + + /* + * pre-allocate the DMA requests for sending EP0 + * and EP1 data to west bridge + */ + dev_p->usb_ep0_dma_req = cy_as_ll_create_request(dev_p, + CY_RQT_USB_EP_DATA, CY_RQT_USB_RQT_CONTEXT, 64); + dev_p->usb_ep1_dma_req = cy_as_ll_create_request(dev_p, + CY_RQT_USB_EP_DATA, CY_RQT_USB_RQT_CONTEXT, 64); + + if (dev_p->usb_ep0_dma_req == 0 || dev_p->usb_ep1_dma_req == 0) { + cy_as_dma_stop_internal(dev_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + dev_p->usb_ep0_dma_req_save = dev_p->usb_ep0_dma_req; + + dev_p->usb_ep0_dma_resp = cy_as_ll_create_response(dev_p, 1); + dev_p->usb_ep1_dma_resp = cy_as_ll_create_response(dev_p, 1); + if (dev_p->usb_ep0_dma_resp == 0 || dev_p->usb_ep1_dma_resp == 0) { + cy_as_dma_stop_internal(dev_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + dev_p->usb_ep0_dma_resp_save = dev_p->usb_ep0_dma_resp; + + /* + * set the dev_p->endp to all zeros to insure cleanup is possible if + * an error occurs during initialization. + */ + cy_as_hal_mem_set(dev_p->endp, 0, sizeof(dev_p->endp)); + + /* + * now, iterate through each of the endpoints and initialize each + * one. + */ + for (i = 0; i < sizeof(dev_p->endp)/sizeof(dev_p->endp[0]); i++) { + dev_p->endp[i] = (cy_as_dma_end_point *) + cy_as_hal_alloc(sizeof(cy_as_dma_end_point)); + if (dev_p->endp[i] == 0) { + cy_as_dma_stop_internal(dev_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + cy_as_hal_mem_set(dev_p->endp[i], 0, + sizeof(cy_as_dma_end_point)); + + dev_p->endp[i]->ep = i; + dev_p->endp[i]->queue_p = 0; + dev_p->endp[i]->last_p = 0; + + cy_as_dma_set_drq(dev_p, i, cy_false); + + if (!cy_as_hal_create_sleep_channel(&dev_p->endp[i]->channel)) + return CY_AS_ERROR_CREATE_SLEEP_CHANNEL_FAILED; + } + + /* + * tell the HAL layer who to call when the + * HAL layer completes a DMA request + */ + cy_as_hal_dma_register_callback(dev_p->tag, + cy_as_dma_completed_callback); + + /* + * mark DMA as up and running on this device + */ + cy_as_device_set_dma_running(dev_p); + + return CY_AS_ERROR_SUCCESS; +} + +/* +* Wait for all entries in the DMA queue associated +* the given endpoint to be drained. This function +* will not return until all the DMA data has been +* transferred. +*/ +cy_as_return_status_t +cy_as_dma_drain_queue(cy_as_device *dev_p, + cy_as_end_point_number_t ep, cy_bool kickstart) +{ + cy_as_dma_end_point *ep_p; + int loopcount = 1000; + uint32_t mask; + + /* + * make sure the endpoint is valid + */ + if (ep >= sizeof(dev_p->endp)/sizeof(dev_p->endp[0])) + return CY_AS_ERROR_INVALID_ENDPOINT; + + /* Get the endpoint pointer based on the endpoint number */ + ep_p = CY_AS_NUM_EP(dev_p, ep); + + /* + * if the endpoint is empty of traffic, we return + * with success immediately + */ + mask = cy_as_hal_disable_interrupts(); + if (ep_p->queue_p == 0) { + cy_as_hal_enable_interrupts(mask); + return CY_AS_ERROR_SUCCESS; + } else { + /* + * add 10 seconds to the time out value for each 64 KB segment + * of data to be transferred. + */ + if (ep_p->queue_p->size > 0x10000) + loopcount += ((ep_p->queue_p->size / 0x10000) * 1000); + } + cy_as_hal_enable_interrupts(mask); + + /* If we are already sleeping on this endpoint, it is an error */ + if (cy_as_dma_end_point_is_sleeping(ep_p)) + return CY_AS_ERROR_NESTED_SLEEP; + + /* + * we disable the endpoint while the queue drains to + * prevent any additional requests from being queued while we are waiting + */ + cy_as_dma_enable_end_point(dev_p, ep, + cy_false, cy_as_direction_dont_change); + + if (kickstart) { + /* + * now, kick start the DMA if necessary + */ + cy_as_dma_kick_start(dev_p, ep); + } + + /* + * check one last time before we begin sleeping to see if the + * queue is drained. + */ + if (ep_p->queue_p == 0) { + cy_as_dma_enable_end_point(dev_p, ep, cy_true, + cy_as_direction_dont_change); + return CY_AS_ERROR_SUCCESS; + } + + while (loopcount-- > 0) { + /* + * sleep for 10 ms maximum (per loop) while + * waiting for the transfer to complete. + */ + cy_as_dma_end_point_set_sleep_state(ep_p); + cy_as_hal_sleep_on(&ep_p->channel, 10); + + /* If we timed out, the sleep bit will still be set */ + cy_as_dma_end_point_set_wake_state(ep_p); + + /* Check the queue to see if is drained */ + if (ep_p->queue_p == 0) { + /* + * clear the endpoint running and in transit flags + * for the endpoint, now that its DMA queue is empty. + */ + cy_as_dma_end_point_clear_in_transit(ep_p); + cy_as_dma_end_point_set_stopped(ep_p); + + cy_as_dma_enable_end_point(dev_p, ep, + cy_true, cy_as_direction_dont_change); + return CY_AS_ERROR_SUCCESS; + } + } + + /* + * the DMA operation that has timed out can be cancelled, so that later + * operations on this queue can proceed. + */ + cy_as_dma_cancel(dev_p, ep, CY_AS_ERROR_TIMEOUT); + cy_as_dma_enable_end_point(dev_p, ep, + cy_true, cy_as_direction_dont_change); + return CY_AS_ERROR_TIMEOUT; +} + +/* +* This function queues a write request in the DMA queue +* for a given endpoint. The direction of the +* entry will be inferred from the endpoint direction. +*/ +cy_as_return_status_t +cy_as_dma_queue_request(cy_as_device *dev_p, + cy_as_end_point_number_t ep, void *mem_p, + uint32_t size, cy_bool pkt, cy_bool readreq, cy_as_dma_callback cb) +{ + uint32_t mask; + cy_as_dma_queue_entry *entry_p; + cy_as_dma_end_point *ep_p; + + /* + * make sure the endpoint is valid + */ + if (ep >= sizeof(dev_p->endp)/sizeof(dev_p->endp[0])) + return CY_AS_ERROR_INVALID_ENDPOINT; + + /* Get the endpoint pointer based on the endpoint number */ + ep_p = CY_AS_NUM_EP(dev_p, ep); + + if (!cy_as_dma_end_point_is_enabled(ep_p)) + return CY_AS_ERROR_ENDPOINT_DISABLED; + + entry_p = cy_as_dma_get_dma_queue_entry(dev_p); + + entry_p->buf_p = mem_p; + entry_p->cb = cb; + entry_p->size = size; + entry_p->offset = 0; + entry_p->packet = pkt; + entry_p->readreq = readreq; + + mask = cy_as_hal_disable_interrupts(); + entry_p->next_p = 0; + if (ep_p->last_p) + ep_p->last_p->next_p = entry_p; + ep_p->last_p = entry_p; + if (ep_p->queue_p == 0) + ep_p->queue_p = entry_p; + cy_as_hal_enable_interrupts(mask); + + return CY_AS_ERROR_SUCCESS; +} + +/* +* This function enables or disables and endpoint for DMA +* queueing. If an endpoint is disabled, any queue requests +* continue to be processed, but no new requests can be queued. +*/ +cy_as_return_status_t +cy_as_dma_enable_end_point(cy_as_device *dev_p, + cy_as_end_point_number_t ep, cy_bool enable, cy_as_dma_direction dir) +{ + cy_as_dma_end_point *ep_p; + + /* + * make sure the endpoint is valid + */ + if (ep >= sizeof(dev_p->endp)/sizeof(dev_p->endp[0])) + return CY_AS_ERROR_INVALID_ENDPOINT; + + /* Get the endpoint pointer based on the endpoint number */ + ep_p = CY_AS_NUM_EP(dev_p, ep); + + if (dir == cy_as_direction_out) + cy_as_dma_end_point_set_direction_out(ep_p); + else if (dir == cy_as_direction_in) + cy_as_dma_end_point_set_direction_in(ep_p); + + /* + * get the maximum size of data buffer the HAL + * layer can accept. this is used when the DMA + * module is sending DMA requests to the HAL. + * the DMA module will never send down a request + * that is greater than this value. + * + * for EP0 and EP1, we can send no more than 64 + * bytes of data at one time as this is the maximum + * size of a packet that can be sent via these + * endpoints. + */ + if (ep == 0 || ep == 1) + ep_p->maxhaldata = 64; + else + ep_p->maxhaldata = cy_as_hal_dma_max_request_size( + dev_p->tag, ep); + + if (enable) + cy_as_dma_end_point_enable(ep_p); + else + cy_as_dma_end_point_disable(ep_p); + + return CY_AS_ERROR_SUCCESS; +} + +/* + * This function cancels any DMA operations pending with the HAL layer as well + * as any DMA operation queued on the endpoint. + */ +cy_as_return_status_t +cy_as_dma_cancel( + cy_as_device *dev_p, + cy_as_end_point_number_t ep, + cy_as_return_status_t err) +{ + uint32_t mask; + cy_as_dma_end_point *ep_p; + cy_as_dma_queue_entry *entry_p; + cy_bool epstate; + + /* + * make sure the endpoint is valid + */ + if (ep >= sizeof(dev_p->endp)/sizeof(dev_p->endp[0])) + return CY_AS_ERROR_INVALID_ENDPOINT; + + /* Get the endpoint pointer based on the endpoint number */ + ep_p = CY_AS_NUM_EP(dev_p, ep); + + if (ep_p) { + /* Remember the state of the endpoint */ + epstate = cy_as_dma_end_point_is_enabled(ep_p); + + /* + * disable the endpoint so no more DMA packets can be + * queued. + */ + cy_as_dma_enable_end_point(dev_p, ep, + cy_false, cy_as_direction_dont_change); + + /* + * don't allow any interrupts from this endpoint + * while we get the most current request off of + * the queue. + */ + cy_as_dma_set_drq(dev_p, ep, cy_false); + + /* + * cancel any pending request queued in the HAL layer + */ + if (cy_as_dma_end_point_in_transit(ep_p)) + cy_as_hal_dma_cancel_request(dev_p->tag, ep_p->ep); + + /* + * shutdown the DMA for this endpoint so no + * more data is transferred + */ + cy_as_dma_end_point_set_stopped(ep_p); + + /* + * mark the endpoint as not in transit, because we are + * going to consume any queued requests + */ + cy_as_dma_end_point_clear_in_transit(ep_p); + + /* + * now, remove each entry in the queue and call the + * associated callback stating that the request was + * canceled. + */ + ep_p->last_p = 0; + while (ep_p->queue_p != 0) { + /* Disable interrupts to manipulate the queue */ + mask = cy_as_hal_disable_interrupts(); + + /* Remove an entry from the queue */ + entry_p = ep_p->queue_p; + ep_p->queue_p = entry_p->next_p; + + /* Ok, the queue has been updated, we can + * turn interrupts back on */ + cy_as_hal_enable_interrupts(mask); + + /* Call the callback indicating we have + * canceled the DMA */ + if (entry_p->cb) + entry_p->cb(dev_p, ep, + entry_p->buf_p, entry_p->size, err); + + cy_as_dma_add_request_to_free_queue(dev_p, entry_p); + } + + if (ep == 0 || ep == 1) { + /* + * if this endpoint is zero or one, we need to + * clear the queue of any pending CY_RQT_USB_EP_DATA + * requests as these are pending requests to send + * data to the west bridge device. + */ + cy_as_ll_remove_ep_data_requests(dev_p, ep); + } + + if (epstate) { + /* + * the endpoint started out enabled, so we + * re-enable the endpoint here. + */ + cy_as_dma_enable_end_point(dev_p, ep, + cy_true, cy_as_direction_dont_change); + } + } + + return CY_AS_ERROR_SUCCESS; +} + +cy_as_return_status_t +cy_as_dma_received_data(cy_as_device *dev_p, + cy_as_end_point_number_t ep, uint32_t dsize, void *data) +{ + cy_as_dma_queue_entry *dma_p; + uint8_t *src_p, *dest_p; + cy_as_dma_end_point *ep_p; + uint32_t xfersize; + + /* + * make sure the endpoint is valid + */ + if (ep != 0 && ep != 1) + return CY_AS_ERROR_INVALID_ENDPOINT; + + /* Get the endpoint pointer based on the endpoint number */ + ep_p = CY_AS_NUM_EP(dev_p, ep); + dma_p = ep_p->queue_p; + if (dma_p == 0) + return CY_AS_ERROR_SUCCESS; + + /* + * if the data received exceeds the size of the DMA buffer, + * clip the data to the size of the buffer. this can lead + * to loosing some data, but is not different than doing + * non-packet reads on the other endpoints. + */ + if (dsize > dma_p->size - dma_p->offset) + dsize = dma_p->size - dma_p->offset; + + /* + * copy the data from the request packet to the DMA buffer + * for the endpoint + */ + src_p = (uint8_t *)data; + dest_p = ((uint8_t *)(dma_p->buf_p)) + dma_p->offset; + xfersize = dsize; + while (xfersize-- > 0) + *dest_p++ = *src_p++; + + /* Signal the DMA module that we have + * received data for this EP request */ + cy_as_dma_completed_callback(dev_p->tag, + ep, dsize, CY_AS_ERROR_SUCCESS); + + return CY_AS_ERROR_SUCCESS; +} diff --git a/drivers/staging/westbridge/astoria/api/src/cyasintr.c b/drivers/staging/westbridge/astoria/api/src/cyasintr.c new file mode 100644 index 000000000000..b60f69ce5985 --- /dev/null +++ b/drivers/staging/westbridge/astoria/api/src/cyasintr.c @@ -0,0 +1,143 @@ +/* Cypress West Bridge API source file (cyasintr.c) +## =========================== +## Copyright (C) 2010 Cypress Semiconductor +## +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either version 2 +## of the License, or (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 51 Franklin Street, Fifth Floor +## Boston, MA 02110-1301, USA. +## =========================== +*/ + +#include "../../include/linux/westbridge/cyashal.h" +#include "../../include/linux/westbridge/cyasdevice.h" +#include "../../include/linux/westbridge/cyasregs.h" +#include "../../include/linux/westbridge/cyaserr.h" + +extern void cy_as_mail_box_interrupt_handler(cy_as_device *); + +void +cy_as_mcu_interrupt_handler(cy_as_device *dev_p) +{ + /* Read and clear the interrupt. */ + uint16_t v; + + v = cy_as_hal_read_register(dev_p->tag, CY_AS_MEM_P0_MCU_STAT); + v = v; +} + +void +cy_as_power_management_interrupt_handler(cy_as_device *dev_p) +{ + uint16_t v; + + v = cy_as_hal_read_register(dev_p->tag, CY_AS_MEM_PWR_MAGT_STAT); + v = v; +} + +void +cy_as_pll_lock_loss_interrupt_handler(cy_as_device *dev_p) +{ + uint16_t v; + + v = cy_as_hal_read_register(dev_p->tag, CY_AS_MEM_PLL_LOCK_LOSS_STAT); + v = v; +} + +uint32_t cy_as_intr_start(cy_as_device *dev_p, cy_bool dmaintr) +{ + uint16_t v; + + cy_as_hal_assert(dev_p->sig == CY_AS_DEVICE_HANDLE_SIGNATURE); + + if (cy_as_device_is_intr_running(dev_p) != 0) + return CY_AS_ERROR_ALREADY_RUNNING; + + v = CY_AS_MEM_P0_INT_MASK_REG_MMCUINT | + CY_AS_MEM_P0_INT_MASK_REG_MMBINT | + CY_AS_MEM_P0_INT_MASK_REG_MPMINT; + + if (dmaintr) + v |= CY_AS_MEM_P0_INT_MASK_REG_MDRQINT; + + /* Enable the interrupts of interest */ + cy_as_hal_write_register(dev_p->tag, CY_AS_MEM_P0_INT_MASK_REG, v); + + /* Mark the interrupt module as initialized */ + cy_as_device_set_intr_running(dev_p); + + return CY_AS_ERROR_SUCCESS; +} + +uint32_t cy_as_intr_stop(cy_as_device *dev_p) +{ + cy_as_hal_assert(dev_p->sig == CY_AS_DEVICE_HANDLE_SIGNATURE); + + if (cy_as_device_is_intr_running(dev_p) == 0) + return CY_AS_ERROR_NOT_RUNNING; + + cy_as_hal_write_register(dev_p->tag, CY_AS_MEM_P0_INT_MASK_REG, 0); + cy_as_device_set_intr_stopped(dev_p); + + return CY_AS_ERROR_SUCCESS; +} + +void cy_as_intr_service_interrupt(cy_as_hal_device_tag tag) +{ + uint16_t v; + cy_as_device *dev_p; + + dev_p = cy_as_device_find_from_tag(tag); + + /* + * only power management interrupts can occur before the + * antioch API setup is complete. if this is a PM interrupt + * handle it here; otherwise output a warning message. + */ + if (dev_p == 0) { + v = cy_as_hal_read_register(tag, CY_AS_MEM_P0_INTR_REG); + if (v == CY_AS_MEM_P0_INTR_REG_PMINT) { + /* Read the PWR_MAGT_STAT register + * to clear this interrupt. */ + v = cy_as_hal_read_register(tag, + CY_AS_MEM_PWR_MAGT_STAT); + } else + cy_as_hal_print_message("stray antioch " + "interrupt detected" + ", tag not associated " + "with any created device."); + return; + } + + /* Make sure we got a valid object from CyAsDeviceFindFromTag */ + cy_as_hal_assert(dev_p->sig == CY_AS_DEVICE_HANDLE_SIGNATURE); + + v = cy_as_hal_read_register(dev_p->tag, CY_AS_MEM_P0_INTR_REG); + + if (v & CY_AS_MEM_P0_INTR_REG_MCUINT) + cy_as_mcu_interrupt_handler(dev_p); + + if (v & CY_AS_MEM_P0_INTR_REG_PMINT) + cy_as_power_management_interrupt_handler(dev_p); + + if (v & CY_AS_MEM_P0_INTR_REG_PLLLOCKINT) + cy_as_pll_lock_loss_interrupt_handler(dev_p); + + /* If the interrupt module is not running, no mailbox + * interrupts are expected from the west bridge. */ + if (cy_as_device_is_intr_running(dev_p) == 0) + return; + + if (v & CY_AS_MEM_P0_INTR_REG_MBINT) + cy_as_mail_box_interrupt_handler(dev_p); +} diff --git a/drivers/staging/westbridge/astoria/api/src/cyaslep2pep.c b/drivers/staging/westbridge/astoria/api/src/cyaslep2pep.c new file mode 100644 index 000000000000..60b6f3525332 --- /dev/null +++ b/drivers/staging/westbridge/astoria/api/src/cyaslep2pep.c @@ -0,0 +1,358 @@ +/* Cypress West Bridge API source file (cyaslep2pep.c) +## =========================== +## Copyright (C) 2010 Cypress Semiconductor +## +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either version 2 +## of the License, or (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 51 Franklin Street, Fifth Floor +## Boston, MA 02110-1301, USA. +## =========================== +*/ + +#include "../../include/linux/westbridge/cyashal.h" +#include "../../include/linux/westbridge/cyasusb.h" +#include "../../include/linux/westbridge/cyaserr.h" +#include "../../include/linux/westbridge/cyaslowlevel.h" +#include "../../include/linux/westbridge/cyasdma.h" + +typedef enum cy_as_physical_endpoint_state { + cy_as_e_p_free, + cy_as_e_p_in, + cy_as_e_p_out, + cy_as_e_p_iso_in, + cy_as_e_p_iso_out +} cy_as_physical_endpoint_state; + + +/* +* This map is used to map an index between 1 and 10 +* to a logical endpoint number. This is used to map +* LEP register indexes into actual EP numbers. +*/ +static cy_as_end_point_number_t end_point_map[] = { + 3, 5, 7, 9, 10, 11, 12, 13, 14, 15 }; + +#define CY_AS_EPCFG_1024 (1 << 3) +#define CY_AS_EPCFG_DBL (0x02) +#define CY_AS_EPCFG_TRIPLE (0x03) +#define CY_AS_EPCFG_QUAD (0x00) + +/* + * NB: This table contains the register values for PEP1 + * and PEP3. PEP2 and PEP4 only have a bit to change the + * direction of the PEP and therefre are not represented + * in this table. + */ +static uint8_t pep_register_values[12][4] = { + /* Bit 1:0 buffering, 0 = quad, 2 = double, 3 = triple */ + /* Bit 3 size, 0 = 512, 1 = 1024 */ + { + CY_AS_EPCFG_DBL, + CY_AS_EPCFG_DBL, + },/* Config 1 - PEP1 (2 * 512), PEP2 (2 * 512), + * PEP3 (2 * 512), PEP4 (2 * 512) */ + { + CY_AS_EPCFG_DBL, + CY_AS_EPCFG_QUAD, + }, /* Config 2 - PEP1 (2 * 512), PEP2 (2 * 512), + * PEP3 (4 * 512), PEP4 (N/A) */ + { + CY_AS_EPCFG_DBL, + CY_AS_EPCFG_DBL | CY_AS_EPCFG_1024, + },/* Config 3 - PEP1 (2 * 512), PEP2 (2 * 512), + * PEP3 (2 * 1024), PEP4(N/A) */ + { + CY_AS_EPCFG_QUAD, + CY_AS_EPCFG_DBL, + },/* Config 4 - PEP1 (4 * 512), PEP2 (N/A), + * PEP3 (2 * 512), PEP4 (2 * 512) */ + { + CY_AS_EPCFG_QUAD, + CY_AS_EPCFG_QUAD, + },/* Config 5 - PEP1 (4 * 512), PEP2 (N/A), + * PEP3 (4 * 512), PEP4 (N/A) */ + { + CY_AS_EPCFG_QUAD, + CY_AS_EPCFG_1024 | CY_AS_EPCFG_DBL, + },/* Config 6 - PEP1 (4 * 512), PEP2 (N/A), + * PEP3 (2 * 1024), PEP4 (N/A) */ + { + CY_AS_EPCFG_1024 | CY_AS_EPCFG_DBL, + CY_AS_EPCFG_DBL, + },/* Config 7 - PEP1 (2 * 1024), PEP2 (N/A), + * PEP3 (2 * 512), PEP4 (2 * 512) */ + { + CY_AS_EPCFG_1024 | CY_AS_EPCFG_DBL, + CY_AS_EPCFG_QUAD, + },/* Config 8 - PEP1 (2 * 1024), PEP2 (N/A), + * PEP3 (4 * 512), PEP4 (N/A) */ + { + CY_AS_EPCFG_1024 | CY_AS_EPCFG_DBL, + CY_AS_EPCFG_1024 | CY_AS_EPCFG_DBL, + },/* Config 9 - PEP1 (2 * 1024), PEP2 (N/A), + * PEP3 (2 * 1024), PEP4 (N/A)*/ + { + CY_AS_EPCFG_TRIPLE, + CY_AS_EPCFG_TRIPLE, + },/* Config 10 - PEP1 (3 * 512), PEP2 (N/A), + * PEP3 (3 * 512), PEP4 (2 * 512)*/ + { + CY_AS_EPCFG_TRIPLE | CY_AS_EPCFG_1024, + CY_AS_EPCFG_DBL, + },/* Config 11 - PEP1 (3 * 1024), PEP2 (N/A), + * PEP3 (N/A), PEP4 (2 * 512) */ + { + CY_AS_EPCFG_QUAD | CY_AS_EPCFG_1024, + CY_AS_EPCFG_DBL, + },/* Config 12 - PEP1 (4 * 1024), PEP2 (N/A), + * PEP3 (N/A), PEP4 (N/A) */ +}; + +static cy_as_return_status_t +find_endpoint_directions(cy_as_device *dev_p, + cy_as_physical_endpoint_state epstate[4]) +{ + int i; + cy_as_physical_endpoint_state desired; + + /* + * note, there is no error checking here becuase + * ISO error checking happens when the API is called. + */ + for (i = 0; i < 10; i++) { + int epno = end_point_map[i]; + if (dev_p->usb_config[epno].enabled) { + int pep = dev_p->usb_config[epno].physical; + if (dev_p->usb_config[epno].type == cy_as_usb_iso) { + /* + * marking this as an ISO endpoint, removes the + * physical EP from consideration when + * mapping the remaining E_ps. + */ + if (dev_p->usb_config[epno].dir == cy_as_usb_in) + desired = cy_as_e_p_iso_in; + else + desired = cy_as_e_p_iso_out; + } else { + if (dev_p->usb_config[epno].dir == cy_as_usb_in) + desired = cy_as_e_p_in; + else + desired = cy_as_e_p_out; + } + + /* + * NB: Note the API calls insure that an ISO endpoint + * has a physical and logical EP number that are the + * same, therefore this condition is not enforced here. + */ + if (epstate[pep - 1] != + cy_as_e_p_free && epstate[pep - 1] != desired) + return CY_AS_ERROR_INVALID_CONFIGURATION; + + epstate[pep - 1] = desired; + } + } + + /* + * create the EP1 config values directly. + * both EP1OUT and EP1IN are invalid by default. + */ + dev_p->usb_ep1cfg[0] = 0; + dev_p->usb_ep1cfg[1] = 0; + if (dev_p->usb_config[1].enabled) { + if ((dev_p->usb_config[1].dir == cy_as_usb_out) || + (dev_p->usb_config[1].dir == cy_as_usb_in_out)) { + /* Set the valid bit and type field. */ + dev_p->usb_ep1cfg[0] = (1 << 7); + if (dev_p->usb_config[1].type == cy_as_usb_bulk) + dev_p->usb_ep1cfg[0] |= (2 << 4); + else + dev_p->usb_ep1cfg[0] |= (3 << 4); + } + + if ((dev_p->usb_config[1].dir == cy_as_usb_in) || + (dev_p->usb_config[1].dir == cy_as_usb_in_out)) { + /* Set the valid bit and type field. */ + dev_p->usb_ep1cfg[1] = (1 << 7); + if (dev_p->usb_config[1].type == cy_as_usb_bulk) + dev_p->usb_ep1cfg[1] |= (2 << 4); + else + dev_p->usb_ep1cfg[1] |= (3 << 4); + } + } + + return CY_AS_ERROR_SUCCESS; +} + +static void +create_register_settings(cy_as_device *dev_p, + cy_as_physical_endpoint_state epstate[4]) +{ + int i; + uint8_t v; + + for (i = 0; i < 4; i++) { + if (i == 0) { + /* Start with the values that specify size */ + dev_p->usb_pepcfg[i] = + pep_register_values + [dev_p->usb_phy_config - 1][0]; + } else if (i == 2) { + /* Start with the values that specify size */ + dev_p->usb_pepcfg[i] = + pep_register_values + [dev_p->usb_phy_config - 1][1]; + } else + dev_p->usb_pepcfg[i] = 0; + + /* Adjust direction if it is in */ + if (epstate[i] == cy_as_e_p_iso_in || + epstate[i] == cy_as_e_p_in) + dev_p->usb_pepcfg[i] |= (1 << 6); + } + + /* Configure the logical EP registers */ + for (i = 0; i < 10; i++) { + int val; + int epnum = end_point_map[i]; + + v = 0x10; /* PEP 1, Bulk Endpoint, EP not valid */ + if (dev_p->usb_config[epnum].enabled) { + v |= (1 << 7); /* Enabled */ + + val = dev_p->usb_config[epnum].physical - 1; + cy_as_hal_assert(val >= 0 && val <= 3); + v |= (val << 5); + + switch (dev_p->usb_config[epnum].type) { + case cy_as_usb_bulk: + val = 2; + break; + case cy_as_usb_int: + val = 3; + break; + case cy_as_usb_iso: + val = 1; + break; + default: + cy_as_hal_assert(cy_false); + break; + } + v |= (val << 3); + } + + dev_p->usb_lepcfg[i] = v; + } +} + + +cy_as_return_status_t +cy_as_usb_map_logical2_physical(cy_as_device *dev_p) +{ + cy_as_return_status_t ret; + + /* Physical EPs 3 5 7 9 respectively in the array */ + cy_as_physical_endpoint_state epstate[4] = { + cy_as_e_p_free, cy_as_e_p_free, + cy_as_e_p_free, cy_as_e_p_free }; + + /* Find the direction for the endpoints */ + ret = find_endpoint_directions(dev_p, epstate); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + /* + * now create the register settings based on the given + * assigned of logical E_ps to physical endpoints. + */ + create_register_settings(dev_p, epstate); + + return ret; +} + +static uint16_t +get_max_dma_size(cy_as_device *dev_p, cy_as_end_point_number_t ep) +{ + uint16_t size = dev_p->usb_config[ep].size; + + if (size == 0) { + switch (dev_p->usb_config[ep].type) { + case cy_as_usb_control: + size = 64; + break; + + case cy_as_usb_bulk: + size = cy_as_device_is_usb_high_speed(dev_p) ? + 512 : 64; + break; + + case cy_as_usb_int: + size = cy_as_device_is_usb_high_speed(dev_p) ? + 1024 : 64; + break; + + case cy_as_usb_iso: + size = cy_as_device_is_usb_high_speed(dev_p) ? + 1024 : 1023; + break; + } + } + + return size; +} + +cy_as_return_status_t +cy_as_usb_set_dma_sizes(cy_as_device *dev_p) +{ + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + uint32_t i; + + for (i = 0; i < 10; i++) { + cy_as_usb_end_point_config *config_p = + &dev_p->usb_config[end_point_map[i]]; + if (config_p->enabled) { + ret = cy_as_dma_set_max_dma_size(dev_p, + end_point_map[i], + get_max_dma_size(dev_p, end_point_map[i])); + if (ret != CY_AS_ERROR_SUCCESS) + break; + } + } + + return ret; +} + +cy_as_return_status_t +cy_as_usb_setup_dma(cy_as_device *dev_p) +{ + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + uint32_t i; + + for (i = 0; i < 10; i++) { + cy_as_usb_end_point_config *config_p = + &dev_p->usb_config[end_point_map[i]]; + if (config_p->enabled) { + /* Map the endpoint direction to the DMA direction */ + cy_as_dma_direction dir = cy_as_direction_out; + if (config_p->dir == cy_as_usb_in) + dir = cy_as_direction_in; + + ret = cy_as_dma_enable_end_point(dev_p, + end_point_map[i], cy_true, dir); + if (ret != CY_AS_ERROR_SUCCESS) + break; + } + } + + return ret; +} diff --git a/drivers/staging/westbridge/astoria/api/src/cyaslowlevel.c b/drivers/staging/westbridge/astoria/api/src/cyaslowlevel.c new file mode 100644 index 000000000000..d43dd858de58 --- /dev/null +++ b/drivers/staging/westbridge/astoria/api/src/cyaslowlevel.c @@ -0,0 +1,1264 @@ +/* Cypress West Bridge API source file (cyaslowlevel.c) +## =========================== +## Copyright (C) 2010 Cypress Semiconductor +## +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either version 2 +## of the License, or (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 51 Franklin Street, Fifth Floor +## Boston, MA 02110-1301, USA. +## =========================== +*/ + +#include "../../include/linux/westbridge/cyashal.h" +#include "../../include/linux/westbridge/cyascast.h" +#include "../../include/linux/westbridge/cyasdevice.h" +#include "../../include/linux/westbridge/cyaslowlevel.h" +#include "../../include/linux/westbridge/cyasintr.h" +#include "../../include/linux/westbridge/cyaserr.h" +#include "../../include/linux/westbridge/cyasregs.h" + +static const uint32_t cy_as_low_level_timeout_count = 65536 * 4; + +/* Forward declaration */ +static cy_as_return_status_t cy_as_send_one(cy_as_device *dev_p, + cy_as_ll_request_response *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 max_request_length[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 +cy_as_ll_remove_request_queue_head(cy_as_device *dev_p, cy_as_context *ctxt_p) +{ + uint32_t mask, state; + cy_as_ll_request_list_node *node_p; + + (void)dev_p; + cy_as_hal_assert(ctxt_p->request_queue_p != 0); + + mask = cy_as_hal_disable_interrupts(); + node_p = ctxt_p->request_queue_p; + ctxt_p->request_queue_p = node_p->next; + cy_as_hal_enable_interrupts(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 = cy_as_hal_disable_interrupts(); + cy_as_hal_c_b_free(node_p); + cy_as_hal_enable_interrupts(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 +cy_as_ll_send_next_request(cy_as_device *dev_p, cy_as_context *ctxt_p) +{ + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + + /* + * ret == ret is equivalent to while (1) but eliminates compiler + * warnings for some compilers. + */ + while (ret == ret) { + cy_as_ll_request_list_node *node_p = ctxt_p->request_queue_p; + if (node_p == 0) + break; + + if (cy_as_request_get_node_state(node_p) != + CY_AS_REQUEST_LIST_STATE_QUEUED) + break; + + cy_as_request_set_node_state(node_p, + CY_AS_REQUEST_LIST_STATE_WAITING); + ret = cy_as_send_one(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. + */ + cy_as_request_set_node_state(node_p, + CY_AS_REQUEST_LIST_STATE_RECEIVED); + node_p->callback(dev_p, ctxt_p->number, + node_p->rqt, node_p->resp, ret); + cy_as_ll_remove_request_queue_head(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. +*/ +cy_as_remove_request_result_t +cy_as_ll_remove_request(cy_as_device *dev_p, cy_as_context *ctxt_p, + cy_as_ll_request_response *req_p, cy_bool force) +{ + uint32_t imask; + cy_as_ll_request_list_node *node_p; + cy_as_ll_request_list_node *tmp_p; + uint32_t state; + + imask = cy_as_hal_disable_interrupts(); + if (ctxt_p->request_queue_p != 0 && + ctxt_p->request_queue_p->rqt == req_p) { + node_p = ctxt_p->request_queue_p; + if ((cy_as_request_get_node_state(node_p) == + CY_AS_REQUEST_LIST_STATE_WAITING) && (!force)) { + cy_as_hal_enable_interrupts(imask); + return cy_as_remove_request_in_transit; + } + + 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) { + cy_as_hal_enable_interrupts(imask); + return cy_as_remove_request_not_found; + } + + 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 = cy_as_hal_disable_interrupts(); + cy_as_hal_c_b_free(node_p); + cy_as_hal_enable_interrupts(state); + + cy_as_hal_enable_interrupts(imask); + return cy_as_remove_request_sucessful; +} + +void +cy_as_ll_remove_all_requests(cy_as_device *dev_p, cy_as_context *ctxt_p) +{ + cy_as_ll_request_list_node *node = ctxt_p->request_queue_p; + + while (node) { + if (cy_as_request_get_node_state(ctxt_p->request_queue_p) != + CY_AS_REQUEST_LIST_STATE_RECEIVED) + cy_as_ll_remove_request(dev_p, ctxt_p, + node->rqt, cy_true); + node = node->next; + } +} + +static cy_bool +cy_as_ll_is_in_queue(cy_as_context *ctxt_p, cy_as_ll_request_response *req_p) +{ + uint32_t mask; + cy_as_ll_request_list_node *node_p; + + mask = cy_as_hal_disable_interrupts(); + node_p = ctxt_p->request_queue_p; + while (node_p) { + if (node_p->rqt == req_p) { + cy_as_hal_enable_interrupts(mask); + return cy_true; + } + node_p = node_p->next; + } + cy_as_hal_enable_interrupts(mask); + return cy_false; +} + +/* +* 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 +cy_as_ll_queue_mailbox_data(cy_as_device *dev_p) +{ + cy_as_context *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] = cy_as_hal_read_register(dev_p->tag, + cy_cast_int2U_int16(CY_AS_MEM_P0_MAILBOX0 + i)); + + context = cy_as_mbox_get_context(data[0]); + if (context >= CY_RQT_CONTEXT_COUNT) { + cy_as_hal_print_message("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. + */ + cy_as_hal_assert(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]; + + cy_as_hal_assert((ctxt_p->queue_index % 4) == 0); + dev_p->ll_queued_data = cy_true; +} + +void +cy_as_mail_box_process_data(cy_as_device *dev_p, uint16_t *data) +{ + cy_as_context *ctxt_p; + uint8_t context; + uint16_t *len_p; + cy_as_ll_request_response *rec_p; + uint8_t st; + uint16_t src, dest; + + context = cy_as_mbox_get_context(data[0]); + if (context >= CY_RQT_CONTEXT_COUNT) { + cy_as_hal_print_message("mailbox request/response received " + "with invalid context value (%d)\n", context); + return; + } + + ctxt_p = dev_p->context[context]; + + if (cy_as_mbox_is_request(data[0])) { + cy_as_hal_assert(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 || + cy_as_request_get_node_state(ctxt_p->request_queue_p) + != CY_AS_REQUEST_LIST_STATE_WAITING) { + cy_as_hal_print_message("mailbox response received on " + "context that was not expecting a response\n"); + cy_as_hal_print_message(" context: %d\n", context); + cy_as_hal_print_message(" 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) + cy_as_hal_print_message(" state: 0x%02x\n", + ctxt_p->request_queue_p->state); + return; + } + + /* Make sure the request has an associated response */ + cy_as_hal_assert(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 + */ + cy_as_ll_request_response__set_code(rec_p, + cy_as_mbox_get_code(data[0])); + cy_as_ll_request_response__set_context(rec_p, context); + + if (cy_as_mbox_is_last(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. */ + cy_as_hal_assert(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 (cy_as_mbox_is_last(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; + cy_as_hal_assert((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. + */ + cy_as_ll_request_response *resp_p = + ctxt_p->request_queue_p->resp; + resp_p->length = ctxt_p->request_queue_p->length; + cy_as_request_set_node_state(ctxt_p->request_queue_p, + CY_AS_REQUEST_LIST_STATE_RECEIVED); + + cy_as_device_set_in_callback(dev_p); + ctxt_p->request_queue_p->callback(dev_p, context, + ctxt_p->request_queue_p->rqt, + resp_p, CY_AS_ERROR_SUCCESS); + + cy_as_device_clear_in_callback(dev_p); + + cy_as_ll_remove_request_queue_head(dev_p, ctxt_p); + cy_as_ll_send_next_request(dev_p, ctxt_p); + } else { + /* Send the request to the appropriate + * module to handle */ + cy_as_ll_request_response *request_p = ctxt_p->req_p; + ctxt_p->req_p = 0; + if (ctxt_p->request_callback) { + cy_as_device_set_in_callback(dev_p); + ctxt_p->request_callback(dev_p, context, + request_p, 0, CY_AS_ERROR_SUCCESS); + cy_as_device_clear_in_callback(dev_p); + } + cy_as_ll_init_request(request_p, 0, + context, request_p->length); + ctxt_p->req_p = request_p; + } + } +} + +/* +* This is the handler for processing queued mailbox data +*/ +void +cy_as_mail_box_queued_data_handler(cy_as_device *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 = cy_false; + for (i = 0; i < CY_RQT_CONTEXT_COUNT; i++) { + uint16_t offset; + cy_as_context *ctxt_p = dev_p->context[i]; + cy_as_hal_assert((ctxt_p->queue_index % 4) == 0); + + offset = 0; + while (offset < ctxt_p->queue_index) { + ctxt_p->rqt_index = offset + 4; + cy_as_mail_box_process_data(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 +cy_as_mail_box_interrupt_handler(cy_as_device *dev_p) +{ + cy_as_hal_assert(dev_p->sig == CY_AS_DEVICE_HANDLE_SIGNATURE); + + /* + * queue the mailbox data to preserve + * order for later processing. + */ + cy_as_ll_queue_mailbox_data(dev_p); + + /* + * process what was queued and anything that may be pending + */ + cy_as_mail_box_queued_data_handler(dev_p); +} + +cy_as_return_status_t +cy_as_ll_start(cy_as_device *dev_p) +{ + uint16_t i; + + if (cy_as_device_is_low_level_running(dev_p)) + return CY_AS_ERROR_ALREADY_RUNNING; + + dev_p->ll_sending_rqt = cy_false; + dev_p->ll_abort_curr_rqt = cy_false; + + for (i = 0; i < CY_RQT_CONTEXT_COUNT; i++) { + dev_p->context[i] = (cy_as_context *) + cy_as_hal_alloc(sizeof(cy_as_context)); + 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 = cy_as_ll_create_request(dev_p, + 0, (uint8_t)i, max_request_length[i]); + dev_p->context[i]->queue_index = 0; + + if (!cy_as_hal_create_sleep_channel + (&dev_p->context[i]->channel)) + return CY_AS_ERROR_CREATE_SLEEP_CHANNEL_FAILED; + } + + cy_as_device_set_low_level_running(dev_p); + return CY_AS_ERROR_SUCCESS; +} + +/* +* Shutdown the low level communications module. This operation will +* also cancel any queued low level requests. +*/ +cy_as_return_status_t +cy_as_ll_stop(cy_as_device *dev_p) +{ + uint8_t i; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + cy_as_context *ctxt_p; + uint32_t mask; + + for (i = 0; i < CY_RQT_CONTEXT_COUNT; i++) { + ctxt_p = dev_p->context[i]; + if (!cy_as_hal_destroy_sleep_channel(&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; + cy_as_ll_request_list_node *node_p = + ctxt_p->request_queue_p; + + /* Mark this pair as in a cancel operation */ + cy_as_request_set_node_state(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 = cy_as_hal_disable_interrupts(); + ctxt_p->request_queue_p = node_p->next; + cy_as_hal_enable_interrupts(mask); + + /* Free the list node */ + state = cy_as_hal_disable_interrupts(); + cy_as_hal_c_b_free(node_p); + cy_as_hal_enable_interrupts(state); + } + + cy_as_ll_destroy_request(dev_p, dev_p->context[i]->req_p); + cy_as_hal_free(dev_p->context[i]); + dev_p->context[i] = 0; + + } + cy_as_device_set_low_level_stopped(dev_p); + + return ret; +} + +void +cy_as_ll_init_request(cy_as_ll_request_response *req_p, + uint16_t code, uint16_t context, uint16_t length) +{ + uint16_t totallen = sizeof(cy_as_ll_request_response) + + (length - 1) * sizeof(uint16_t); + + cy_as_hal_mem_set(req_p, 0, totallen); + req_p->length = length; + cy_as_ll_request_response__set_code(req_p, code); + cy_as_ll_request_response__set_context(req_p, context); + cy_as_ll_request_response__set_request(req_p); +} + +/* +* Create a new request. +*/ +cy_as_ll_request_response * +cy_as_ll_create_request(cy_as_device *dev_p, uint16_t code, + uint8_t context, uint16_t length) +{ + cy_as_ll_request_response *req_p; + uint32_t state; + uint16_t totallen = sizeof(cy_as_ll_request_response) + + (length - 1) * sizeof(uint16_t); + + (void)dev_p; + + state = cy_as_hal_disable_interrupts(); + req_p = cy_as_hal_c_b_alloc(totallen); + cy_as_hal_enable_interrupts(state); + if (req_p) + cy_as_ll_init_request(req_p, code, context, length); + + return req_p; +} + +/* +* Destroy a request. +*/ +void +cy_as_ll_destroy_request(cy_as_device *dev_p, cy_as_ll_request_response *req_p) +{ + uint32_t state; + (void)dev_p; + (void)req_p; + + state = cy_as_hal_disable_interrupts(); + cy_as_hal_c_b_free(req_p); + cy_as_hal_enable_interrupts(state); + +} + +void +cy_as_ll_init_response(cy_as_ll_request_response *req_p, uint16_t length) +{ + uint16_t totallen = sizeof(cy_as_ll_request_response) + + (length - 1) * sizeof(uint16_t); + + cy_as_hal_mem_set(req_p, 0, totallen); + req_p->length = length; + cy_as_ll_request_response__set_response(req_p); +} + +/* +* Create a new response +*/ +cy_as_ll_request_response * +cy_as_ll_create_response(cy_as_device *dev_p, uint16_t length) +{ + cy_as_ll_request_response *req_p; + uint32_t state; + uint16_t totallen = sizeof(cy_as_ll_request_response) + + (length - 1) * sizeof(uint16_t); + + (void)dev_p; + + state = cy_as_hal_disable_interrupts(); + req_p = cy_as_hal_c_b_alloc(totallen); + cy_as_hal_enable_interrupts(state); + if (req_p) + cy_as_ll_init_response(req_p, length); + + return req_p; +} + +/* +* Destroy the new response +*/ +void +cy_as_ll_destroy_response(cy_as_device *dev_p, cy_as_ll_request_response *req_p) +{ + uint32_t state; + (void)dev_p; + (void)req_p; + + state = cy_as_hal_disable_interrupts(); + cy_as_hal_c_b_free(req_p); + cy_as_hal_enable_interrupts(state); +} + +static uint16_t +cy_as_read_intr_status( + cy_as_device *dev_p) +{ + uint32_t mask; + cy_bool bloop = cy_true; + 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 = cy_as_hal_disable_interrupts(); + + /* + * see if there is data to be read. + */ + v = cy_as_hal_read_register(dev_p->tag, CY_AS_MEM_P0_INTR_REG); + if ((v & CY_AS_MEM_P0_INTR_REG_MBINT) == 0) { + cy_as_hal_enable_interrupts(mask); + break; + } + + /* + * queue the mailbox data for later processing. + * this allows the firmware to move forward and + * service the requst from the P port. + */ + cy_as_ll_queue_mailbox_data(dev_p); + + /* + * enable interrupts again to service mailbox + * interrupts appropriately + */ + cy_as_hal_enable_interrupts(mask); + } + + /* + * now, all data is received + */ + last = cy_as_hal_read_register(dev_p->tag, + CY_AS_MEM_MCU_MB_STAT) & CY_AS_MEM_P0_MCU_MBNOTRD; + while (bloop) { + v = cy_as_hal_read_register(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 cy_as_return_status_t +cy_as_send_one( + cy_as_device *dev_p, + cy_as_ll_request_response *req_p) +{ + int i; + uint16_t mb0, v; + int32_t loopcount; + uint32_t int_stat; + +#ifdef _DEBUG + if (cy_as_ll_request_response__is_request(req_p)) { + switch (cy_as_ll_request_response__get_context(req_p)) { + case CY_RQT_GENERAL_RQT_CONTEXT: + cy_as_hal_assert(req_p->length * 2 + 2 < + CY_CTX_GEN_MAX_DATA_SIZE); + break; + + case CY_RQT_RESOURCE_RQT_CONTEXT: + cy_as_hal_assert(req_p->length * 2 + 2 < + CY_CTX_RES_MAX_DATA_SIZE); + break; + + case CY_RQT_STORAGE_RQT_CONTEXT: + cy_as_hal_assert(req_p->length * 2 + 2 < + CY_CTX_STR_MAX_DATA_SIZE); + break; + + case CY_RQT_USB_RQT_CONTEXT: + cy_as_hal_assert(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 = cy_true; + while (which < length) { + loopcount = cy_as_low_level_timeout_count; + do { + v = cy_as_read_intr_status(dev_p); + + } while (v && loopcount-- > 0); + + if (v) { + cy_as_hal_print_message( + ">>>>>> LOW LEVEL TIMEOUT " + "%x %x %x %x\n", + cy_as_hal_read_register(dev_p->tag, + CY_AS_MEM_MCU_MAILBOX0), + cy_as_hal_read_register(dev_p->tag, + CY_AS_MEM_MCU_MAILBOX1), + cy_as_hal_read_register(dev_p->tag, + CY_AS_MEM_MCU_MAILBOX2), + cy_as_hal_read_register(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 = cy_false; + dev_p->ll_abort_curr_rqt = cy_false; + return CY_AS_ERROR_CANCELED; + } + + int_stat = cy_as_hal_disable_interrupts(); + + /* + * 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 = cy_as_hal_read_register(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. */ + cy_as_hal_enable_interrupts(int_stat); + continue; + } + + if (which == 0) { + cy_as_hal_write_register(dev_p->tag, + CY_AS_MEM_MCU_MAILBOX1, length); + st = 2; + } else { + st = 1; + } + + while ((which < length) && (st < 4)) { + cy_as_hal_write_register(dev_p->tag, + cy_cast_int2U_int16 + (CY_AS_MEM_MCU_MAILBOX0 + st), + req_p->data[which++]); + st++; + } + + mb0 = req_p->box0; + if (which == length) { + dev_p->ll_sending_rqt = cy_false; + mb0 |= CY_AS_REQUEST_RESPONSE_LAST_MASK; + } + + if (dev_p->ll_abort_curr_rqt) { + dev_p->ll_sending_rqt = cy_false; + dev_p->ll_abort_curr_rqt = cy_false; + cy_as_hal_enable_interrupts(int_stat); + return CY_AS_ERROR_CANCELED; + } + + cy_as_hal_write_register(dev_p->tag, + CY_AS_MEM_MCU_MAILBOX0, mb0); + + /* Wait for the MBOX interrupt to be high */ + cy_as_hal_sleep150(); + cy_as_hal_enable_interrupts(int_stat); + } + } else { +check_mailbox_availability: + /* + * 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 = cy_as_low_level_timeout_count; + do { + v = cy_as_read_intr_status(dev_p); + + } while (v && loopcount-- > 0); + + if (v) { + cy_as_hal_print_message( + ">>>>>> LOW LEVEL TIMEOUT %x %x %x %x\n", + cy_as_hal_read_register(dev_p->tag, + CY_AS_MEM_MCU_MAILBOX0), + cy_as_hal_read_register(dev_p->tag, + CY_AS_MEM_MCU_MAILBOX1), + cy_as_hal_read_register(dev_p->tag, + CY_AS_MEM_MCU_MAILBOX2), + cy_as_hal_read_register(dev_p->tag, + CY_AS_MEM_MCU_MAILBOX3)); + return CY_AS_ERROR_TIMEOUT; + } + + int_stat = cy_as_hal_disable_interrupts(); + + /* + * 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 = cy_as_hal_read_register(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. */ + cy_as_hal_enable_interrupts(int_stat); + goto check_mailbox_availability; + } + + /* Write the data associated with the request + * into the mbox registers 1 - 3 */ + v = 0; + for (i = req_p->length - 1; i >= 0; i--) + cy_as_hal_write_register(dev_p->tag, + cy_cast_int2U_int16(CY_AS_MEM_MCU_MAILBOX1 + i), + req_p->data[i]); + + /* Write the mbox register 0 to trigger the interrupt */ + cy_as_hal_write_register(dev_p->tag, CY_AS_MEM_MCU_MAILBOX0, + req_p->box0 | CY_AS_REQUEST_RESPONSE_LAST_MASK); + + cy_as_hal_sleep150(); + cy_as_hal_enable_interrupts(int_stat); + } + + return CY_AS_ERROR_SUCCESS; +} + +/* +* This function queues a single request to be sent to the firmware. +*/ +extern cy_as_return_status_t +cy_as_ll_send_request( + cy_as_device *dev_p, + /* The request to send */ + cy_as_ll_request_response *req, + /* Storage for a reply, must be sure + * it is of sufficient size */ + cy_as_ll_request_response *resp, + /* If true, this is a synchronous request */ + cy_bool sync, + /* Callback to call when reply is received */ + cy_as_response_callback cb +) +{ + cy_as_context *ctxt_p; + uint16_t box0 = req->box0; + uint8_t context; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + cy_as_ll_request_list_node *node_p; + uint32_t mask, state; + + cy_as_hal_assert(dev_p->sig == CY_AS_DEVICE_HANDLE_SIGNATURE); + + context = cy_as_mbox_get_context(box0); + cy_as_hal_assert(context < CY_RQT_CONTEXT_COUNT); + ctxt_p = dev_p->context[context]; + + /* Allocate the list node */ + state = cy_as_hal_disable_interrupts(); + node_p = cy_as_hal_c_b_alloc(sizeof(cy_as_ll_request_list_node)); + cy_as_hal_enable_interrupts(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) + cy_as_request_node_set_sync(node_p); + + /* Put the request into the queue */ + mask = cy_as_hal_disable_interrupts(); + 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; + } + cy_as_hal_enable_interrupts(mask); + cy_as_ll_send_next_request(dev_p, ctxt_p); + + if (!cy_as_device_is_in_callback(dev_p)) { + mask = cy_as_hal_disable_interrupts(); + cy_as_mail_box_queued_data_handler(dev_p); + cy_as_hal_enable_interrupts(mask); + } + + return ret; +} + +static void +cy_as_ll_send_callback( + cy_as_device *dev_p, + uint8_t context, + cy_as_ll_request_response *rqt, + cy_as_ll_request_response *resp, + cy_as_return_status_t ret) +{ + (void)rqt; + (void)resp; + (void)ret; + + + cy_as_hal_assert(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 + */ + cy_as_hal_wake(&dev_p->context[context]->channel); +} + +cy_as_return_status_t +cy_as_ll_send_request_wait_reply( + cy_as_device *dev_p, + /* The request to send */ + cy_as_ll_request_response *req, + /* Storage for a reply, must be + * sure it is of sufficient size */ + cy_as_ll_request_response *resp + ) +{ + cy_as_return_status_t ret; + uint8_t context; + /* Larger 8 sec time-out to handle the init + * delay for slower storage devices in USB FS. */ + uint32_t loopcount = 800; + cy_as_context *ctxt_p; + + /* Get the context for the request */ + context = cy_as_ll_request_response__get_context(req); + cy_as_hal_assert(context < CY_RQT_CONTEXT_COUNT); + ctxt_p = dev_p->context[context]; + + ret = cy_as_ll_send_request(dev_p, req, resp, + cy_true, cy_as_ll_send_callback); + 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. + */ + cy_as_hal_sleep_on(&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 (!cy_as_ll_is_in_queue(ctxt_p, req)) + return dev_p->ll_error; + } + + /* Remove the QueueListNode for this request. */ + cy_as_ll_remove_request(dev_p, ctxt_p, req, cy_true); + + return CY_AS_ERROR_TIMEOUT; +} + +cy_as_return_status_t +cy_as_ll_register_request_callback( + cy_as_device *dev_p, + uint8_t context, + cy_as_response_callback cb) +{ + cy_as_context *ctxt_p; + cy_as_hal_assert(context < CY_RQT_CONTEXT_COUNT); + ctxt_p = dev_p->context[context]; + + ctxt_p->request_callback = cb; + return CY_AS_ERROR_SUCCESS; +} + +void +cy_as_ll_request_response__pack( + cy_as_ll_request_response *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++); + cy_as_ll_request_response__set_word(req_p, offset, dt); + offset++; + length -= 2; + } + + if (length == 1) { + dt = (*dp << 8); + cy_as_ll_request_response__set_word(req_p, offset, dt); + } +} + +void +cy_as_ll_request_response__unpack( + cy_as_ll_request_response *req_p, + uint32_t offset, + uint32_t length, + void *data_p) +{ + uint8_t *dp = (uint8_t *)data_p; + + while (length-- > 0) { + uint16_t val = cy_as_ll_request_response__get_word + (req_p, offset++); + *dp++ = (uint8_t)((val >> 8) & 0xff); + + if (length) { + length--; + *dp++ = (uint8_t)(val & 0xff); + } + } +} + +extern cy_as_return_status_t +cy_as_ll_send_status_response( + cy_as_device *dev_p, + uint8_t context, + uint16_t code, + uint8_t clear_storage) +{ + cy_as_return_status_t ret; + cy_as_ll_request_response resp; + cy_as_ll_request_response *resp_p = &resp; + + cy_as_hal_mem_set(resp_p, 0, sizeof(resp)); + resp_p->length = 1; + cy_as_ll_request_response__set_response(resp_p); + cy_as_ll_request_response__set_context(resp_p, context); + + if (clear_storage) + cy_as_ll_request_response__set_clear_storage_flag(resp_p); + + cy_as_ll_request_response__set_code(resp_p, CY_RESP_SUCCESS_FAILURE); + cy_as_ll_request_response__set_word(resp_p, 0, code); + + ret = cy_as_send_one(dev_p, resp_p); + + return ret; +} + +extern cy_as_return_status_t +cy_as_ll_send_data_response( + cy_as_device *dev_p, + uint8_t context, + uint16_t code, + uint16_t length, + void *data) +{ + cy_as_ll_request_response *resp_p; + uint16_t wlen; + uint8_t respbuf[256]; + + if (length > 192) + return CY_AS_ERROR_INVALID_SIZE; + + /* Word length for bytes */ + wlen = length / 2; + + /* If byte length odd, add one more */ + if (length % 2) + wlen++; + + /* One for the length of field */ + wlen++; + + resp_p = (cy_as_ll_request_response *)respbuf; + cy_as_hal_mem_set(resp_p, 0, sizeof(respbuf)); + resp_p->length = wlen; + cy_as_ll_request_response__set_context(resp_p, context); + cy_as_ll_request_response__set_code(resp_p, code); + + cy_as_ll_request_response__set_word(resp_p, 0, length); + cy_as_ll_request_response__pack(resp_p, 1, length, data); + + return cy_as_send_one(dev_p, resp_p); +} + +static cy_bool +cy_as_ll_is_e_p_transfer_related_request(cy_as_ll_request_response *rqt_p, + cy_as_end_point_number_t ep) +{ + uint16_t v; + uint8_t type = cy_as_ll_request_response__get_code(rqt_p); + + if (cy_as_ll_request_response__get_context(rqt_p) != + CY_RQT_USB_RQT_CONTEXT) + return cy_false; + + /* + * 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 cy_true; + + if (type != CY_RQT_USB_EP_DATA) + return cy_false; + + v = cy_as_ll_request_response__get_word(rqt_p, 0); + if ((cy_as_end_point_number_t)((v >> 13) & 1) != ep) + return cy_false; + + return cy_true; +} + +cy_as_return_status_t +cy_as_ll_remove_ep_data_requests(cy_as_device *dev_p, + cy_as_end_point_number_t ep) +{ + cy_as_context *ctxt_p; + cy_as_ll_request_list_node *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 (cy_as_ll_is_e_p_transfer_related_request + (node_p->rqt, ep)) { + cy_as_ll_remove_request(dev_p, ctxt_p, + node_p->rqt, cy_false); + break; + } + } + + /* + * now, deal with any request that may be in transit + */ + imask = cy_as_hal_disable_interrupts(); + + if (ctxt_p->request_queue_p != 0 && + cy_as_ll_is_e_p_transfer_related_request + (ctxt_p->request_queue_p->rqt, ep) && + cy_as_request_get_node_state(ctxt_p->request_queue_p) == + CY_AS_REQUEST_LIST_STATE_WAITING) { + cy_as_hal_print_message("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 = cy_true; + 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 = cy_as_hal_disable_interrupts(); + cy_as_hal_c_b_free(node_p); + cy_as_hal_enable_interrupts(state); + } + } + + cy_as_hal_enable_interrupts(imask); + } + + return CY_AS_ERROR_SUCCESS; +} diff --git a/drivers/staging/westbridge/astoria/api/src/cyasmisc.c b/drivers/staging/westbridge/astoria/api/src/cyasmisc.c new file mode 100644 index 000000000000..10a52a1ac6fb --- /dev/null +++ b/drivers/staging/westbridge/astoria/api/src/cyasmisc.c @@ -0,0 +1,3474 @@ +/* Cypress West Bridge API source file (cyasmisc.c) +## =========================== +## Copyright (C) 2010 Cypress Semiconductor +## +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either version 2 +## of the License, or (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 51 Franklin Street, Fifth Floor +## Boston, MA 02110-1301, USA. +## =========================== +*/ + +#include "../../include/linux/westbridge/cyashal.h" +#include "../../include/linux/westbridge/cyasmisc.h" +#include "../../include/linux/westbridge/cyasdma.h" +#include "../../include/linux/westbridge/cyasintr.h" +#include "../../include/linux/westbridge/cyaserr.h" +#include "../../include/linux/westbridge/cyasregs.h" +#include "../../include/linux/westbridge/cyaslowlevel.h" +#include "../../include/linux/westbridge/cyasprotocol.h" + +/* +* The device list, the only global in the API +*/ +static cy_as_device *g_device_list; + +/* + * The current debug level + */ +static uint8_t debug_level; + +/* + * This function sets the debug level for the API + * + */ +void +cy_as_misc_set_log_level(uint8_t level) +{ + debug_level = level; +} + +#ifdef CY_AS_LOG_SUPPORT + +/* + * This function is a low level logger for the API. + */ +void +cy_as_log_debug_message(int level, const char *str) +{ + if (level <= debug_level) + cy_as_hal_print_message("log %d: %s\n", level, str); +} + +#endif + +#define cy_as_check_device_ready(dev_p) \ +{\ + if (!(dev_p) || ((dev_p)->sig != \ + CY_AS_DEVICE_HANDLE_SIGNATURE)) \ + return CY_AS_ERROR_INVALID_HANDLE; \ +\ + if (!cy_as_device_is_configured(dev_p)) \ + return CY_AS_ERROR_NOT_CONFIGURED; \ +\ + if (!cy_as_device_is_firmware_loaded(dev_p))\ + return CY_AS_ERROR_NO_FIRMWARE; \ +} + +/* Find an West Bridge device based on a TAG */ +cy_as_device * +cy_as_device_find_from_tag(cy_as_hal_device_tag tag) +{ + cy_as_device *dev_p; + + for (dev_p = g_device_list; dev_p != 0; dev_p = dev_p->next_p) { + if (dev_p->tag == tag) + return dev_p; + } + + return 0; +} + +/* Map a pre-V1.2 media type to the V1.2+ bus number */ +static void +cy_as_bus_from_media_type(cy_as_media_type type, + cy_as_bus_number_t *bus) +{ + if (type == cy_as_media_nand) + *bus = 0; + else + *bus = 1; +} + +static cy_as_return_status_t +my_handle_response_no_data(cy_as_device *dev_p, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *reply_p) +{ + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_SUCCESS_FAILURE) + ret = CY_AS_ERROR_INVALID_RESPONSE; + else + ret = cy_as_ll_request_response__get_word(reply_p, 0); + + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +/* +* Create a new West Bridge device +*/ +cy_as_return_status_t +cy_as_misc_create_device(cy_as_device_handle *handle_p, + cy_as_hal_device_tag tag) +{ + cy_as_device *dev_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + + cy_as_log_debug_message(6, "cy_as_misc_create_device called"); + + dev_p = (cy_as_device *)cy_as_hal_alloc(sizeof(cy_as_device)); + if (dev_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + cy_as_hal_mem_set(dev_p, 0, sizeof(cy_as_device)); + + /* + * dynamically allocating this buffer to ensure that it is + * word aligned. + */ + dev_p->usb_ep_data = (uint8_t *)cy_as_hal_alloc(64 * sizeof(uint8_t)); + if (dev_p->usb_ep_data == 0) { + cy_as_hal_free(dev_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + dev_p->sig = CY_AS_DEVICE_HANDLE_SIGNATURE; + dev_p->tag = tag; + dev_p->usb_max_tx_size = 0x40; + + dev_p->storage_write_endpoint = CY_AS_P2S_WRITE_ENDPOINT; + dev_p->storage_read_endpoint = CY_AS_P2S_READ_ENDPOINT; + + dev_p->func_cbs_misc = cy_as_create_c_b_queue(CYAS_FUNC_CB); + if (dev_p->func_cbs_misc == 0) + goto destroy; + + dev_p->func_cbs_res = cy_as_create_c_b_queue(CYAS_FUNC_CB); + if (dev_p->func_cbs_res == 0) + goto destroy; + + dev_p->func_cbs_stor = cy_as_create_c_b_queue(CYAS_FUNC_CB); + if (dev_p->func_cbs_stor == 0) + goto destroy; + + dev_p->func_cbs_usb = cy_as_create_c_b_queue(CYAS_FUNC_CB); + if (dev_p->func_cbs_usb == 0) + goto destroy; + + dev_p->func_cbs_mtp = cy_as_create_c_b_queue(CYAS_FUNC_CB); + if (dev_p->func_cbs_mtp == 0) + goto destroy; + + /* + * allocate memory for the DMA module here. it is then marked idle, and + * will be activated when cy_as_misc_configure_device is called. + */ + ret = cy_as_dma_start(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + cy_as_device_set_dma_stopped(dev_p); + + /* + * allocate memory for the low level module here. this module is also + * activated only when cy_as_misc_configure_device is called. + */ + ret = cy_as_ll_start(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + cy_as_device_set_low_level_stopped(dev_p); + + dev_p->next_p = g_device_list; + g_device_list = dev_p; + + *handle_p = dev_p; + cy_as_hal_init_dev_registers(tag, cy_false); + return CY_AS_ERROR_SUCCESS; + +destroy: + /* Free any queues that were successfully allocated. */ + if (dev_p->func_cbs_misc) + cy_as_destroy_c_b_queue(dev_p->func_cbs_misc); + + if (dev_p->func_cbs_res) + cy_as_destroy_c_b_queue(dev_p->func_cbs_res); + + if (dev_p->func_cbs_stor) + cy_as_destroy_c_b_queue(dev_p->func_cbs_stor); + + if (dev_p->func_cbs_usb) + cy_as_destroy_c_b_queue(dev_p->func_cbs_usb); + + if (dev_p->func_cbs_mtp) + cy_as_destroy_c_b_queue(dev_p->func_cbs_mtp); + + cy_as_hal_free(dev_p->usb_ep_data); + cy_as_hal_free(dev_p); + + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + else + return CY_AS_ERROR_OUT_OF_MEMORY; +} + +/* +* Destroy an existing West Bridge device +*/ +cy_as_return_status_t +cy_as_misc_destroy_device(cy_as_device_handle handle) +{ + cy_as_return_status_t ret; + cy_as_device *dev_p; + + cy_as_log_debug_message(6, "cy_as_misc_destroy_device called"); + + dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + /* + * if the USB stack is still running, + * it must be stopped first + */ + if (dev_p->usb_count > 0) + return CY_AS_ERROR_STILL_RUNNING; + + /* + * if the STORAGE stack is still running, + * it must be stopped first + */ + if (dev_p->storage_count > 0) + return CY_AS_ERROR_STILL_RUNNING; + + if (cy_as_device_is_intr_running(dev_p)) + ret = cy_as_intr_stop(dev_p); + + ret = cy_as_ll_stop(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) { + cy_as_intr_start(dev_p, dev_p->use_int_drq); + return ret; + } + + ret = cy_as_dma_stop(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) { + cy_as_intr_start(dev_p, dev_p->use_int_drq); + return ret; + } + + /* Reset the West Bridge device. */ + cy_as_hal_write_register(dev_p->tag, CY_AS_MEM_RST_CTRL_REG, + CY_AS_MEM_RST_CTRL_REG_HARD); + + /* + * remove the device from the device list + */ + if (g_device_list == dev_p) { + g_device_list = dev_p->next_p; + } else { + cy_as_device *tmp_p = g_device_list; + while (tmp_p && tmp_p->next_p != dev_p) + tmp_p = tmp_p->next_p; + + cy_as_hal_assert(tmp_p != 0); + tmp_p->next_p = dev_p->next_p; + } + + /* + * reset the signature so this will not be detected + * as a valid handle + */ + dev_p->sig = 0; + + cy_as_destroy_c_b_queue(dev_p->func_cbs_misc); + cy_as_destroy_c_b_queue(dev_p->func_cbs_res); + cy_as_destroy_c_b_queue(dev_p->func_cbs_stor); + cy_as_destroy_c_b_queue(dev_p->func_cbs_usb); + cy_as_destroy_c_b_queue(dev_p->func_cbs_mtp); + + /* + * free the memory associated with the device + */ + cy_as_hal_free(dev_p->usb_ep_data); + cy_as_hal_free(dev_p); + + return CY_AS_ERROR_SUCCESS; +} + +/* +* Determine the endian mode for the processor we are +* running on, then set the endian mode register +*/ +static void +cy_as_setup_endian_mode(cy_as_device *dev_p) +{ + /* + * In general, we always set west bridge intothe little + * endian mode. this causes the data on bit 0 internally + * to come out on data line 0 externally and it is generally + * what we want regardless of the endian mode of the + * processor. this capability in west bridge should be + * labeled as a "SWAP" capability and can be used to swap the + * bytes of data in and out of west bridge. this is + * useful if there is DMA hardware that requires this for some + * reason I cannot imagine at this time. basically if the + * wires are connected correctly, we should never need to + * change the endian-ness of west bridge. + */ + cy_as_hal_write_register(dev_p->tag, CY_AS_MEM_P0_ENDIAN, + CY_AS_LITTLE_ENDIAN); +} + +/* +* Query the West Bridge device and determine if we are an standby mode +*/ +cy_as_return_status_t +cy_as_misc_in_standby(cy_as_device_handle handle, cy_bool *standby) +{ + cy_as_device *dev_p; + + cy_as_log_debug_message(6, "cy_as_misc_in_standby called"); + + dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + if (cy_as_device_is_pin_standby(dev_p) || + cy_as_device_is_register_standby(dev_p)) { + *standby = cy_true; + } else + *standby = cy_false; + + return CY_AS_ERROR_SUCCESS; +} + +static void +cy_as_misc_func_callback(cy_as_device *dev_p, + uint8_t context, + cy_as_ll_request_response *rqt, + cy_as_ll_request_response *resp, + cy_as_return_status_t ret); + + +static void +my_misc_callback(cy_as_device *dev_p, uint8_t context, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *resp_p, + cy_as_return_status_t ret) +{ + (void)resp_p; + (void)context; + (void)ret; + + switch (cy_as_ll_request_response__get_code(req_p)) { + case CY_RQT_INITIALIZATION_COMPLETE: + { + uint16_t v; + + cy_as_ll_send_status_response(dev_p, + CY_RQT_GENERAL_RQT_CONTEXT, + CY_AS_ERROR_SUCCESS, 0); + cy_as_device_set_firmware_loaded(dev_p); + + if (cy_as_device_is_waking(dev_p)) { + /* + * this is a callback from a + * cy_as_misc_leave_standby() + * request. in this case we call + * the standby callback and clear + * the waking state. + */ + if (dev_p->misc_event_cb) + dev_p->misc_event_cb( + (cy_as_device_handle)dev_p, + cy_as_event_misc_awake, 0); + cy_as_device_clear_waking(dev_p); + } else { + v = cy_as_ll_request_response__get_word + (req_p, 3); + + /* + * store the media supported on + * each of the device buses. + */ + dev_p->media_supported[0] = + (uint8_t)(v & 0xFF); + dev_p->media_supported[1] = + (uint8_t)((v >> 8) & 0xFF); + + v = cy_as_ll_request_response__get_word + (req_p, 4); + + dev_p->is_mtp_firmware = + (cy_bool)((v >> 8) & 0xFF); + + if (dev_p->misc_event_cb) + dev_p->misc_event_cb( + (cy_as_device_handle)dev_p, + cy_as_event_misc_initialized, 0); + } + + v = cy_as_hal_read_register(dev_p->tag, + CY_AS_MEM_P0_VM_SET); + + if (v & CY_AS_MEM_P0_VM_SET_CFGMODE) + cy_as_hal_print_message( + "initialization message " + "recieved, but config bit " + "still set\n"); + + v = cy_as_hal_read_register(dev_p->tag, + CY_AS_MEM_RST_CTRL_REG); + if ((v & CY_AS_MEM_RST_RSTCMPT) == 0) + cy_as_hal_print_message( + "initialization message " + "recieved, but reset complete " + "bit still not set\n"); + } + break; + + case CY_RQT_OUT_OF_SUSPEND: + cy_as_ll_send_status_response(dev_p, CY_RQT_GENERAL_RQT_CONTEXT, + CY_AS_ERROR_SUCCESS, 0); + cy_as_device_clear_suspend_mode(dev_p); + + /* + * if the wakeup was caused by an async cy_as_misc_leave_suspend + * call, we have to call the corresponding callback. + */ + if (dev_p->func_cbs_misc->count > 0) { + cy_as_func_c_b_node *node = (cy_as_func_c_b_node *) + dev_p->func_cbs_misc->head_p; + cy_as_hal_assert(node); + + if (cy_as_funct_c_b_type_get_type(node->data_type) == + CY_FUNCT_CB_MISC_LEAVESUSPEND) { + cy_as_hal_assert(node->cb_p != 0); + + node->cb_p((cy_as_device_handle)dev_p, + CY_AS_ERROR_SUCCESS, node->client_data, + CY_FUNCT_CB_MISC_LEAVESUSPEND, 0); + cy_as_remove_c_b_node(dev_p->func_cbs_misc); + } + } + + if (dev_p->misc_event_cb) + dev_p->misc_event_cb((cy_as_device_handle)dev_p, + cy_as_event_misc_wakeup, 0); + break; + + case CY_RQT_DEBUG_MESSAGE: + if ((req_p->data[0] == 0) && (req_p->data[1] == 0) && + (req_p->data[2] == 0)) { + if (dev_p->misc_event_cb) + dev_p->misc_event_cb((cy_as_device_handle)dev_p, + cy_as_event_misc_heart_beat, 0); + } else { + cy_as_hal_print_message( + "**** debug message: %02x " + "%02x %02x %02x %02x %02x\n", + req_p->data[0] & 0xff, + (req_p->data[0] >> 8) & 0xff, + req_p->data[1] & 0xff, + (req_p->data[1] >> 8) & 0xff, + req_p->data[2] & 0xff, + (req_p->data[2] >> 8) & 0xff); + } + break; + + case CY_RQT_WB_DEVICE_MISMATCH: + { + if (dev_p->misc_event_cb) + dev_p->misc_event_cb((cy_as_device_handle)dev_p, + cy_as_event_misc_device_mismatch, 0); + } + break; + + case CY_RQT_BOOTLOAD_NO_FIRMWARE: + { + /* TODO Handle case when firmware is + * not found during bootloading. */ + cy_as_hal_print_message("no firmware image found " + "during bootload. device not started\n"); + } + break; + + default: + cy_as_hal_assert(0); + } +} + +static cy_bool +is_valid_silicon_id(uint16_t v) +{ + cy_bool idok = cy_false; + + /* + * remove the revision number from the ID value + */ + v = v & CY_AS_MEM_CM_WB_CFG_ID_HDID_MASK; + + /* + * if this is west bridge, then we are OK. + */ + if (v == CY_AS_MEM_CM_WB_CFG_ID_HDID_ANTIOCH_VALUE || + v == CY_AS_MEM_CM_WB_CFG_ID_HDID_ASTORIA_FPGA_VALUE || + v == CY_AS_MEM_CM_WB_CFG_ID_HDID_ASTORIA_VALUE) + idok = cy_true; + + return idok; +} + +/* +* Configure the West Bridge device hardware +*/ +cy_as_return_status_t +cy_as_misc_configure_device(cy_as_device_handle handle, + cy_as_device_config *config_p) +{ + cy_as_return_status_t ret; + cy_bool standby; + cy_as_device *dev_p; + uint16_t v; + uint16_t fw_present; + cy_as_log_debug_message(6, "cy_as_misc_configure_device called"); + + dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + /* Setup big endian vs little endian */ + cy_as_setup_endian_mode(dev_p); + + /* Now, confirm that we can talk to the West Bridge device */ + dev_p->silicon_id = cy_as_hal_read_register(dev_p->tag, + CY_AS_MEM_CM_WB_CFG_ID); + fw_present = cy_as_hal_read_register(dev_p->tag, + CY_AS_MEM_RST_CTRL_REG); + if (!(fw_present & CY_AS_MEM_RST_RSTCMPT)) { + if (!is_valid_silicon_id(dev_p->silicon_id)) + return CY_AS_ERROR_NO_ANTIOCH; + } + /* Check for standby mode */ + ret = cy_as_misc_in_standby(handle, &standby); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + if (ret) + return CY_AS_ERROR_IN_STANDBY; + + /* Setup P-port interface mode (CRAM / SRAM). */ + if (cy_as_device_is_astoria_dev(dev_p)) { + if (config_p->srammode) + v = CY_AS_MEM_P0_VM_SET_VMTYPE_SRAM; + else + v = CY_AS_MEM_P0_VM_SET_VMTYPE_RAM; + } else + v = CY_AS_MEM_P0_VM_SET_VMTYPE_RAM; + + /* Setup synchronous versus asynchronous mode */ + if (config_p->sync) + v |= CY_AS_MEM_P0_VM_SET_IFMODE; + if (config_p->dackmode == cy_as_device_dack_ack) + v |= CY_AS_MEM_P0_VM_SET_DACKEOB; + if (config_p->drqpol) + v |= CY_AS_MEM_P0_VM_SET_DRQPOL; + if (config_p->dackpol) + v |= CY_AS_MEM_P0_VM_SET_DACKPOL; + cy_as_hal_write_register(dev_p->tag, CY_AS_MEM_P0_VM_SET, v); + + if (config_p->crystal) + cy_as_device_set_crystal(dev_p); + else + cy_as_device_set_external_clock(dev_p); + + /* Register a callback to handle MISC requests from the firmware */ + cy_as_ll_register_request_callback(dev_p, + CY_RQT_GENERAL_RQT_CONTEXT, my_misc_callback); + + /* Now mark the DMA and low level modules as active. */ + cy_as_device_set_dma_running(dev_p); + cy_as_device_set_low_level_running(dev_p); + + /* Now, initialize the interrupt module */ + dev_p->use_int_drq = config_p->dmaintr; + ret = cy_as_intr_start(dev_p, config_p->dmaintr); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + /* Mark the interface as initialized */ + cy_as_device_set_configured(dev_p); + + return CY_AS_ERROR_SUCCESS; +} + +static void +my_dma_callback(cy_as_device *dev_p, + cy_as_end_point_number_t ep, + void *mem_p, + uint32_t size, + cy_as_return_status_t ret + ) +{ + cy_as_dma_end_point *ep_p; + + (void)size; + + /* Get the endpoint pointer based on the endpoint number */ + ep_p = CY_AS_NUM_EP(dev_p, ep); + + /* Check the queue to see if is drained */ + if (ep_p->queue_p == 0) { + cy_as_func_c_b_node *node = + (cy_as_func_c_b_node *)dev_p->func_cbs_misc->head_p; + + cy_as_hal_assert(node); + + if (ret == CY_AS_ERROR_SUCCESS) { + /* + * disable endpoint 2. the storage module + * will enable this EP if necessary. + */ + cy_as_dma_enable_end_point(dev_p, + CY_AS_FIRMWARE_ENDPOINT, + cy_false, cy_as_direction_in); + + /* + * clear the reset register. this releases the + * antioch micro-controller from reset and begins + * running the code at address zero. + */ + cy_as_hal_write_register(dev_p->tag, + CY_AS_MEM_RST_CTRL_REG, 0x00); + } + + /* Call the user Callback */ + node->cb_p((cy_as_device_handle)dev_p, ret, node->client_data, + node->data_type, node->data); + cy_as_remove_c_b_node(dev_p->func_cbs_misc); + } else { + /* This is the header data that was allocated in the + * download firmware function, and can be safely freed + * here. */ + uint32_t state = cy_as_hal_disable_interrupts(); + cy_as_hal_c_b_free(mem_p); + cy_as_hal_enable_interrupts(state); + } +} + +cy_as_return_status_t +cy_as_misc_download_firmware(cy_as_device_handle handle, + const void *mem_p, + uint16_t size, + cy_as_function_callback cb, + uint32_t client) +{ + uint8_t *header; + cy_as_return_status_t ret; + cy_bool standby; + cy_as_device *dev_p; + cy_as_dma_callback dmacb = 0; + uint32_t state; + + cy_as_log_debug_message(6, "cy_as_misc_download_firmware called"); + + /* Make sure we have a valid device */ + dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + /* + * if the device has not been initialized, we cannot download firmware + * to the device. + */ + if (!cy_as_device_is_configured(dev_p)) + return CY_AS_ERROR_NOT_CONFIGURED; + + /* + * make sure west bridge is not in standby + */ + ret = cy_as_misc_in_standby(dev_p, &standby); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (standby) + return CY_AS_ERROR_IN_STANDBY; + + if (cy_as_device_is_in_suspend_mode(dev_p)) + return CY_AS_ERROR_IN_SUSPEND; + + /* + * make sure we are in configuration mode + */ + if ((cy_as_hal_read_register(dev_p->tag, CY_AS_MEM_P0_VM_SET) & + CY_AS_MEM_P0_VM_SET_CFGMODE) == 0) + return CY_AS_ERROR_NOT_IN_CONFIG_MODE; + + /* Maximum firmware size is 24k */ + if (size > CY_AS_MAXIMUM_FIRMWARE_SIZE) + return CY_AS_ERROR_INVALID_SIZE; + + /* Make sure the size is an even number of bytes as well */ + if (size & 0x01) + return CY_AS_ERROR_ALIGNMENT_ERROR; + + /* + * write the two word header that gives the base address and + * size of the firmware image to download + */ + state = cy_as_hal_disable_interrupts(); + header = (uint8_t *)cy_as_hal_c_b_alloc(4); + cy_as_hal_enable_interrupts(state); + if (header == NULL) + return CY_AS_ERROR_OUT_OF_MEMORY; + + header[0] = 0x00; + header[1] = 0x00; + header[2] = (uint8_t)(size & 0xff); + header[3] = (uint8_t)((size >> 8) & 0xff); + + /* Enable the firmware endpoint */ + ret = cy_as_dma_enable_end_point(dev_p, CY_AS_FIRMWARE_ENDPOINT, + cy_true, cy_as_direction_in); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + /* + * setup DMA for 64 byte packets. this is the requirement for downloading + * firmware to west bridge. + */ + cy_as_dma_set_max_dma_size(dev_p, CY_AS_FIRMWARE_ENDPOINT, 64); + + if (cb) + dmacb = my_dma_callback; + + ret = cy_as_dma_queue_request(dev_p, CY_AS_FIRMWARE_ENDPOINT, header, + 4, cy_false, cy_false, dmacb); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + /* + * write the firmware image to the west bridge device + */ + ret = cy_as_dma_queue_request(dev_p, CY_AS_FIRMWARE_ENDPOINT, + (void *)mem_p, size, cy_false, cy_false, dmacb); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (cb) { + cy_as_func_c_b_node *cbnode = cy_as_create_func_c_b_node_data( + cb, client, CY_FUNCT_CB_MISC_DOWNLOADFIRMWARE, 0); + + if (cbnode == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + else + cy_as_insert_c_b_node(dev_p->func_cbs_misc, cbnode); + + ret = cy_as_dma_kick_start(dev_p, CY_AS_FIRMWARE_ENDPOINT); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + } else { + ret = cy_as_dma_drain_queue(dev_p, + CY_AS_FIRMWARE_ENDPOINT, cy_true); + + /* Free the header memory that was allocated earlier. */ + cy_as_hal_c_b_free(header); + + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + /* + * disable EP 2. the storage module will + * enable this EP if necessary. + */ + cy_as_dma_enable_end_point(dev_p, CY_AS_FIRMWARE_ENDPOINT, + cy_false, cy_as_direction_in); + + /* + * clear the reset register. this releases the west bridge + * micro-controller from reset and begins running the code at + * address zero. + */ + cy_as_hal_write_register(dev_p->tag, + CY_AS_MEM_RST_CTRL_REG, 0x00); + } + + /* + * the firmware is not marked as loaded until the firmware + * initializes west bridge and a request is sent from west bridge + * to the P port processor indicating that west bridge is ready. + */ + return CY_AS_ERROR_SUCCESS; +} + + +static cy_as_return_status_t +my_handle_response_get_firmware_version(cy_as_device *dev_p, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *reply_p, + cy_as_get_firmware_version_data *data_p) +{ + + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + uint16_t val; + + if (cy_as_ll_request_response__get_code(reply_p) + != CY_RESP_FIRMWARE_VERSION) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + data_p->major = cy_as_ll_request_response__get_word(reply_p, 0); + data_p->minor = cy_as_ll_request_response__get_word(reply_p, 1); + data_p->build = cy_as_ll_request_response__get_word(reply_p, 2); + val = cy_as_ll_request_response__get_word(reply_p, 3); + data_p->media_type = (uint8_t)(((val >> 8) & 0xFF) | (val & 0xFF)); + val = cy_as_ll_request_response__get_word(reply_p, 4); + data_p->is_debug_mode = (cy_bool)(val & 0xFF); + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +cy_as_return_status_t +cy_as_misc_get_firmware_version(cy_as_device_handle handle, + cy_as_get_firmware_version_data *data, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + cy_bool standby; + cy_as_ll_request_response *req_p, *reply_p; + + cy_as_device *dev_p; + + (void)client; + + cy_as_log_debug_message(6, "cy_as_misc_get_firmware_version called"); + + /* Make sure we have a valid device */ + dev_p = (cy_as_device *)handle; + cy_as_check_device_ready(dev_p); + + /* + * make sure antioch is not in standby + */ + ret = cy_as_misc_in_standby(dev_p, &standby); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + if (standby) + return CY_AS_ERROR_IN_STANDBY; + + /* Make sure the Antioch is not in suspend mode. */ + if (cy_as_device_is_in_suspend_mode(dev_p)) + return CY_AS_ERROR_IN_SUSPEND; + + /* Create the request to send to the West Bridge device */ + req_p = cy_as_ll_create_request(dev_p, CY_RQT_GET_FIRMWARE_VERSION, + CY_RQT_GENERAL_RQT_CONTEXT, 0); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + /* + * Reserve space for the reply, the reply data + * will not exceed three words + */ + reply_p = cy_as_ll_create_response(dev_p, 5); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + /* Request and response are freed in + * MyHandleResponseGetFirmwareVersion. */ + ret = my_handle_response_get_firmware_version(dev_p, + req_p, reply_p, data); + return ret; + } else { + + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_MISC_GETFIRMWAREVERSION, data, + dev_p->func_cbs_misc, CY_AS_REQUEST_RESPONSE_EX, + req_p, reply_p, cy_as_misc_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + /* The request and response are freed + * as part of the MiscFuncCallback */ + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} +static cy_as_return_status_t +my_handle_response_read_m_c_u_register(cy_as_device *dev_p, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *reply_p, + uint8_t *data_p) +{ + + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + + if (cy_as_ll_request_response__get_code(reply_p) + != CY_RESP_MCU_REGISTER_DATA) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + *data_p = (uint8_t) + (cy_as_ll_request_response__get_word(reply_p, 0)); + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +static cy_as_return_status_t +my_handle_response_get_gpio_value(cy_as_device *dev_p, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *reply_p, + uint8_t *data_p) +{ + + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + + if (cy_as_ll_request_response__get_code(reply_p) + != CY_RESP_GPIO_STATE) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + } else + *data_p = (uint8_t) + (cy_as_ll_request_response__get_word(reply_p, 0)); + + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + + +cy_as_return_status_t cy_as_misc_set_sd_power_polarity( + cy_as_device_handle handle, + cy_as_misc_signal_polarity polarity, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_ll_request_response *req_p, *reply_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + cy_as_device *dev_p = (cy_as_device *)handle; + + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + if (!cy_as_device_is_configured(dev_p)) + return CY_AS_ERROR_NOT_CONFIGURED; + + if (!cy_as_device_is_firmware_loaded(dev_p)) + return CY_AS_ERROR_NO_FIRMWARE; + + if (cy_as_device_is_in_suspend_mode(dev_p)) + return CY_AS_ERROR_IN_SUSPEND; + + req_p = cy_as_ll_create_request(dev_p, CY_RQT_SDPOLARITY, + CY_RQT_GENERAL_RQT_CONTEXT, 1); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + cy_as_ll_request_response__set_word(req_p, 0, + (uint16_t)polarity); + + /* + * Reserve space for the reply, the reply data will + * not exceed one word + */ + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return (my_handle_response_no_data(dev_p, req_p, reply_p)); + } else { + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_MISC_SETSDPOLARITY, 0, dev_p->func_cbs_misc, + CY_AS_REQUEST_RESPONSE_EX, req_p, reply_p, + cy_as_misc_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + /* The request and response are freed + * as part of the FuncCallback */ + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + return ret; +} + + +cy_as_return_status_t +cy_as_misc_read_m_c_u_register(cy_as_device_handle handle, + uint16_t address, + uint8_t *value, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + cy_as_ll_request_response *req_p, *reply_p; + + cy_as_device *dev_p; + + cy_as_log_debug_message(6, "cy_as_misc_read_m_c_u_register called"); + + dev_p = (cy_as_device *)handle; + cy_as_check_device_ready(dev_p); + + /* Check whether the firmware supports this command. */ + if (cy_as_device_is_nand_storage_supported(dev_p)) + return CY_AS_ERROR_NOT_SUPPORTED; + + /* Make sure the Antioch is not in suspend mode. */ + if (cy_as_device_is_in_suspend_mode(dev_p)) + return CY_AS_ERROR_IN_SUSPEND; + + /* Create the request to send to the West Bridge device */ + req_p = cy_as_ll_create_request(dev_p, CY_RQT_READ_MCU_REGISTER, + CY_RQT_GENERAL_RQT_CONTEXT, 1); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + cy_as_ll_request_response__set_word(req_p, 0, (uint16_t)address); + + /* Reserve space for the reply, the reply + * data will not exceed one word */ + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_MCU_REGISTER_DATA) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + *value = (uint8_t)(cy_as_ll_request_response__get_word + (reply_p, 0)); + } else { + + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_MISC_READMCUREGISTER, value, + dev_p->func_cbs_misc, CY_AS_REQUEST_RESPONSE_EX, + req_p, reply_p, cy_as_misc_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + /* The request and response are freed + * as part of the MiscFuncCallback */ + return ret; + } +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + + +cy_as_return_status_t +cy_as_misc_write_m_c_u_register(cy_as_device_handle handle, + uint16_t address, + uint8_t mask, + uint8_t value, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + cy_as_ll_request_response *req_p, *reply_p; + cy_as_device *dev_p; + + cy_as_log_debug_message(6, "cy_as_misc_write_m_c_u_register called"); + + dev_p = (cy_as_device *)handle; + cy_as_check_device_ready(dev_p); + + /* Check whether the firmware supports this command. */ + if (cy_as_device_is_nand_storage_supported(dev_p)) + return CY_AS_ERROR_NOT_SUPPORTED; + + /* Make sure the Antioch is not in suspend mode. */ + if (cy_as_device_is_in_suspend_mode(dev_p)) + return CY_AS_ERROR_IN_SUSPEND; + + /* Create the request to send to the West Bridge device */ + req_p = cy_as_ll_create_request(dev_p, CY_RQT_WRITE_MCU_REGISTER, + CY_RQT_GENERAL_RQT_CONTEXT, 2); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + cy_as_ll_request_response__set_word(req_p, 0, (uint16_t)address); + cy_as_ll_request_response__set_word(req_p, 1, + (uint16_t)((mask << 8) | value)); + + /* + * Reserve space for the reply, the reply data + * will not exceed one word + */ + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_SUCCESS_FAILURE) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + ret = cy_as_ll_request_response__get_word(reply_p, 0); + } else { + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_MISC_WRITEMCUREGISTER, 0, + dev_p->func_cbs_misc, CY_AS_REQUEST_RESPONSE_EX, + req_p, reply_p, cy_as_misc_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + /* + * The request and response are freed as part of the + * MiscFuncCallback + */ + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +cy_as_return_status_t +my_handle_response_reset(cy_as_device *dev_p, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *reply_p, + cy_as_reset_type type) +{ + uint16_t v; + + (void)req_p; + (void)reply_p; + + /* + * if the device is in suspend mode, it needs to be woken up + * so that the write to the reset control register succeeds. + * we need not however wait for the wake up procedure to be + * complete. + */ + if (cy_as_device_is_in_suspend_mode(dev_p)) { + v = cy_as_hal_read_register(dev_p->tag, + CY_AS_MEM_CM_WB_CFG_ID); + cy_as_hal_sleep(1); + } + + if (type == cy_as_reset_hard) { + cy_as_misc_cancel_ex_requests(dev_p); + cy_as_hal_write_register(dev_p->tag, CY_AS_MEM_RST_CTRL_REG, + CY_AS_MEM_RST_CTRL_REG_HARD); + cy_as_device_set_unconfigured(dev_p); + cy_as_device_set_firmware_not_loaded(dev_p); + cy_as_device_set_dma_stopped(dev_p); + cy_as_device_set_low_level_stopped(dev_p); + cy_as_device_set_intr_stopped(dev_p); + cy_as_device_clear_suspend_mode(dev_p); + cy_as_usb_cleanup(dev_p); + cy_as_storage_cleanup(dev_p); + + /* + * wait for a small amount of time to + * allow reset to be complete. + */ + cy_as_hal_sleep(100); + } + + cy_as_device_clear_reset_pending(dev_p); + + return CY_AS_ERROR_SUCCESS; +} + +cy_as_return_status_t +cy_as_misc_reset(cy_as_device_handle handle, + cy_as_reset_type type, + cy_bool flush, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_device *dev_p; + cy_as_end_point_number_t i; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + (void)client; + (void)cb; + + cy_as_log_debug_message(6, "cy_as_misc_reset_e_x called"); + + /* Make sure the device is ready for the command. */ + dev_p = (cy_as_device *)handle; + cy_as_check_device_ready(dev_p); + + /* + * soft reset is not supported until we close on the issues + * in the firmware with what needs to happen. + */ + if (type == cy_as_reset_soft) + return CY_AS_ERROR_NOT_YET_SUPPORTED; + + cy_as_device_set_reset_pending(dev_p); + + if (flush) { + /* Unable to DrainQueues in polling mode */ + if ((dev_p->storage_cb || dev_p->storage_cb_ms) && + cy_as_hal_is_polling()) + return CY_AS_ERROR_ASYNC_PENDING; + + /* + * shutdown the endpoints so no more traffic can be queued + */ + for (i = 0; i < 15; i++) + cy_as_dma_enable_end_point(dev_p, i, cy_false, + cy_as_direction_dont_change); + + /* + * if we are in normal mode, drain all traffic across all + * endpoints to be sure all traffic is flushed. if the + * device is suspended, data will not be coming in on any + * endpoint and all outstanding DMA operations can be + * cancelled. + */ + if (cy_as_device_is_in_suspend_mode(dev_p)) { + for (i = 0; i < 15; i++) + cy_as_dma_cancel(dev_p, i, + CY_AS_ERROR_CANCELED); + } else { + for (i = 0; i < 15; i++) { + if ((i == CY_AS_P2S_WRITE_ENDPOINT) || + (i == CY_AS_P2S_READ_ENDPOINT)) + cy_as_dma_drain_queue(dev_p, i, + cy_false); + else + cy_as_dma_drain_queue(dev_p, i, + cy_true); + } + } + } else { + /* No flush was requested, so cancel any outstanding DMAs + * so the user callbacks are called as needed + */ + if (cy_as_device_is_storage_async_pending(dev_p)) { + for (i = 0; i < 15; i++) + cy_as_dma_cancel(dev_p, i, + CY_AS_ERROR_CANCELED); + } + } + + ret = my_handle_response_reset(dev_p, 0, 0, type); + + if (cb) + /* Even though no mailbox communication was needed, + * issue the callback so the user does not need to + * special case their code. */ + cb((cy_as_device_handle)dev_p, ret, client, + CY_FUNCT_CB_MISC_RESET, 0); + + /* + * initialize any registers that may have been + * changed when the device was reset. + */ + cy_as_hal_init_dev_registers(dev_p->tag, cy_false); + + return ret; +} + +static cy_as_return_status_t +get_unallocated_resource(cy_as_device *dev_p, cy_as_resource_type resource) +{ + uint8_t shift = 0; + uint16_t v; + cy_as_return_status_t ret = CY_AS_ERROR_NOT_ACQUIRED; + + switch (resource) { + case cy_as_bus_u_s_b: + shift = 4; + break; + case cy_as_bus_1: + shift = 0; + break; + case cy_as_bus_0: + shift = 2; + break; + default: + cy_as_hal_assert(cy_false); + break; + } + + /* Get the semaphore value for this resource */ + v = cy_as_hal_read_register(dev_p->tag, CY_AS_MEM_P0_RSE_ALLOCATE); + v = (v >> shift) & 0x03; + + if (v == 0x03) { + ret = CY_AS_ERROR_RESOURCE_ALREADY_OWNED; + } else if ((v & 0x01) == 0) { + /* The resource is not owned by anyone, we can try to get it */ + cy_as_hal_write_register(dev_p->tag, + CY_AS_MEM_P0_RSE_MASK, (0x03 << shift)); + v = cy_as_hal_read_register(dev_p->tag, CY_AS_MEM_P0_RSE_MASK); + cy_as_hal_write_register(dev_p->tag, + CY_AS_MEM_P0_RSE_ALLOCATE, (0x01 << shift)); + v = cy_as_hal_read_register(dev_p->tag, CY_AS_MEM_P0_RSE_MASK); + + v = cy_as_hal_read_register(dev_p->tag, + CY_AS_MEM_P0_RSE_ALLOCATE); + v = (v >> shift) & 0x03; + if (v == 0x03) + ret = CY_AS_ERROR_SUCCESS; + } + + return ret; +} + +static cy_as_return_status_t +my_handle_response_acquire_resource(cy_as_device *dev_p, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *reply_p, + cy_as_resource_type *resource) +{ + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_SUCCESS_FAILURE) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + if (ret == CY_AS_ERROR_SUCCESS) { + ret = get_unallocated_resource(dev_p, *resource); + if (ret != CY_AS_ERROR_NOT_ACQUIRED) + ret = CY_AS_ERROR_SUCCESS; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +cy_as_return_status_t +cy_as_misc_acquire_resource(cy_as_device_handle handle, + cy_as_resource_type *resource, + cy_bool force, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_ll_request_response *req_p, *reply_p; + cy_as_return_status_t ret; + + cy_as_device *dev_p; + + (void)client; + + cy_as_log_debug_message(6, "cy_as_misc_acquire_resource called"); + + if (*resource != cy_as_bus_u_s_b && *resource != + cy_as_bus_0 && *resource != cy_as_bus_1) + return CY_AS_ERROR_INVALID_RESOURCE; + + + /* Make sure the device is ready to accept the command. */ + dev_p = (cy_as_device *)handle; + cy_as_check_device_ready(dev_p); + + if (cy_as_device_is_in_suspend_mode(dev_p)) + return CY_AS_ERROR_IN_SUSPEND; + + + ret = get_unallocated_resource(dev_p, *resource); + + /* + * make sure that the callback is called if the resource is + * successfully acquired at this point. + */ + if ((ret == CY_AS_ERROR_SUCCESS) && (cb != 0)) + cb(handle, ret, client, + CY_FUNCT_CB_MISC_ACQUIRERESOURCE, resource); + + if (ret != CY_AS_ERROR_NOT_ACQUIRED) + return ret; + + if (!force) + return CY_AS_ERROR_NOT_ACQUIRED; + + /* Create the request to acquire the resource */ + req_p = cy_as_ll_create_request(dev_p, CY_RQT_ACQUIRE_RESOURCE, + CY_RQT_RESOURCE_RQT_CONTEXT, 1); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + cy_as_ll_request_response__set_word(req_p, 0, (uint16_t)(*resource)); + + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_SUCCESS_FAILURE) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + ret = cy_as_ll_request_response__get_word(reply_p, 0); + } else { + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_MISC_ACQUIRERESOURCE, resource, + dev_p->func_cbs_res, CY_AS_REQUEST_RESPONSE_EX, + req_p, reply_p, cy_as_misc_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + /* The request and response are freed + * as part of the MiscFuncCallback */ + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + if (ret == CY_AS_ERROR_SUCCESS) { + ret = get_unallocated_resource(dev_p, *resource); + if (ret != CY_AS_ERROR_NOT_ACQUIRED) + ret = CY_AS_ERROR_SUCCESS; + } + + return ret; +} +cy_as_return_status_t +cy_as_misc_release_resource(cy_as_device_handle handle, + cy_as_resource_type resource) +{ + uint8_t shift = 0; + uint16_t v; + + cy_as_device *dev_p; + + cy_as_log_debug_message(6, "cy_as_misc_release_resource called"); + + /* Make sure the device is ready for the command. */ + dev_p = (cy_as_device *)handle; + cy_as_check_device_ready(dev_p); + + if (cy_as_device_is_in_suspend_mode(dev_p)) + return CY_AS_ERROR_IN_SUSPEND; + + if (resource != cy_as_bus_u_s_b && resource != + cy_as_bus_0 && resource != cy_as_bus_1) + return CY_AS_ERROR_INVALID_RESOURCE; + + switch (resource) { + case cy_as_bus_u_s_b: + shift = 4; + break; + case cy_as_bus_1: + shift = 0; + break; + case cy_as_bus_0: + shift = 2; + break; + default: + cy_as_hal_assert(cy_false); + break; + } + + /* Get the semaphore value for this resource */ + v = (cy_as_hal_read_register(dev_p->tag, + CY_AS_MEM_P0_RSE_ALLOCATE) >> shift) & 0x03; + if (v == 0 || v == 1 || v == 2) + return CY_AS_ERROR_RESOURCE_NOT_OWNED; + + cy_as_hal_write_register(dev_p->tag, + CY_AS_MEM_P0_RSE_MASK, (0x03 << shift)); + cy_as_hal_write_register(dev_p->tag, + CY_AS_MEM_P0_RSE_ALLOCATE, (0x02 << shift)); + cy_as_hal_write_register(dev_p->tag, + CY_AS_MEM_P0_RSE_MASK, 0); + + return CY_AS_ERROR_SUCCESS; +} + +cy_as_return_status_t +cy_as_misc_set_trace_level(cy_as_device_handle handle, + uint8_t level, + cy_as_bus_number_t bus, + uint32_t device, + uint32_t unit, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_ll_request_response *req_p, *reply_p; + cy_as_return_status_t ret; + cy_as_device *dev_p; + + cy_as_log_debug_message(6, "cy_as_misc_set_trace_level called"); + + /* Make sure the device is ready for the command. */ + dev_p = (cy_as_device *)handle; + cy_as_check_device_ready(dev_p); + + if (cy_as_device_is_in_suspend_mode(dev_p)) + return CY_AS_ERROR_IN_SUSPEND; + + if (bus < 0 || bus >= CY_AS_MAX_BUSES) + return CY_AS_ERROR_NO_SUCH_BUS; + + if (device >= CY_AS_MAX_STORAGE_DEVICES) + return CY_AS_ERROR_NO_SUCH_DEVICE; + + if (unit > 255) + return CY_AS_ERROR_NO_SUCH_UNIT; + + if (level >= CYAS_FW_TRACE_MAX_LEVEL) + return CY_AS_ERROR_INVALID_TRACE_LEVEL; + + /* Create the request to send to the West Bridge device */ + req_p = cy_as_ll_create_request(dev_p, CY_RQT_SET_TRACE_LEVEL, + CY_RQT_GENERAL_RQT_CONTEXT, 2); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + cy_as_ll_request_response__set_word(req_p, 0, + (uint16_t)level); + cy_as_ll_request_response__set_word(req_p, 1, + (uint16_t)((bus << 12) | (device << 8) | (unit))); + + /* + * Reserve space for the reply, the reply data will not + * exceed three words + */ + reply_p = cy_as_ll_create_response(dev_p, 2); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_SUCCESS_FAILURE) { + ret = CY_AS_ERROR_NOT_SUPPORTED; + goto destroy; + } + + ret = cy_as_ll_request_response__get_word(reply_p, 0); + } else { + + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_MISC_SETTRACELEVEL, 0, dev_p->func_cbs_misc, + CY_AS_REQUEST_RESPONSE_EX, req_p, reply_p, + cy_as_misc_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + /* The request and response are freed as part of the + * MiscFuncCallback */ + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +cy_as_return_status_t +cy_as_misc_heart_beat_control(cy_as_device_handle handle, + cy_bool enable, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_ll_request_response *req_p, *reply_p; + cy_as_return_status_t ret; + cy_as_device *dev_p; + + cy_as_log_debug_message(6, "cy_as_misc_heart_beat_control called"); + + /* Make sure the device is ready for the command. */ + dev_p = (cy_as_device *)handle; + cy_as_check_device_ready(dev_p); + + if (cy_as_device_is_in_suspend_mode(dev_p)) + return CY_AS_ERROR_IN_SUSPEND; + + /* Create the request to send to the West Bridge device */ + req_p = cy_as_ll_create_request(dev_p, CY_RQT_CONTROL_ANTIOCH_HEARTBEAT, + CY_RQT_GENERAL_RQT_CONTEXT, 1); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + cy_as_ll_request_response__set_word(req_p, 0, (uint16_t)enable); + + /* Reserve space for the reply, the reply + * data will not exceed one word */ + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_SUCCESS_FAILURE) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + ret = cy_as_ll_request_response__get_word(reply_p, 0); + } else { + + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_MISC_HEARTBEATCONTROL, 0, + dev_p->func_cbs_misc, CY_AS_REQUEST_RESPONSE_EX, + req_p, reply_p, cy_as_misc_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + /* The request and response are freed as part of the + * MiscFuncCallback */ + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +static cy_as_return_status_t +my_set_sd_clock_freq( + cy_as_device *dev_p, + uint8_t card_type, + uint8_t setting, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + cy_as_ll_request_response *req_p, *reply_p; + + if (cy_as_device_is_in_callback(dev_p) && (cb == 0)) + return CY_AS_ERROR_INVALID_IN_CALLBACK; + + req_p = cy_as_ll_create_request(dev_p, CY_RQT_SET_SD_CLOCK_FREQ, + CY_RQT_GENERAL_RQT_CONTEXT, 1); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + cy_as_ll_request_response__set_word(req_p, 0, + (uint16_t)((card_type << 8) | setting)); + + /* Reserve space for the reply, which will not exceed one word. */ + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_SUCCESS_FAILURE) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + ret = cy_as_ll_request_response__get_word(reply_p, 0); + } else { + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_MISC_SETSDFREQ, 0, dev_p->func_cbs_misc, + CY_AS_REQUEST_RESPONSE_EX, req_p, reply_p, + cy_as_misc_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + /* The request and response are freed as part of the + * MiscFuncCallback */ + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +cy_as_return_status_t +cy_as_misc_set_low_speed_sd_freq( + cy_as_device_handle handle, + cy_as_low_speed_sd_freq setting, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_device *dev_p; + + cy_as_log_debug_message(6, "cy_as_misc_set_low_speed_sd_freq called"); + + /* Make sure the device is ready for the command. */ + dev_p = (cy_as_device *)handle; + cy_as_check_device_ready(dev_p); + + if (cy_as_device_is_in_suspend_mode(dev_p)) + return CY_AS_ERROR_IN_SUSPEND; + + if ((setting != CY_AS_SD_DEFAULT_FREQ) && + (setting != CY_AS_SD_RATED_FREQ)) + return CY_AS_ERROR_INVALID_PARAMETER; + + return my_set_sd_clock_freq(dev_p, 0, (uint8_t)setting, cb, client); +} + +cy_as_return_status_t +cy_as_misc_set_high_speed_sd_freq( + cy_as_device_handle handle, + cy_as_high_speed_sd_freq setting, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_device *dev_p; + + cy_as_log_debug_message(6, "cy_as_misc_set_high_speed_sd_freq called"); + + /* Make sure the device is ready for the command. */ + dev_p = (cy_as_device *)handle; + cy_as_check_device_ready(dev_p); + + if (cy_as_device_is_in_suspend_mode(dev_p)) + return CY_AS_ERROR_IN_SUSPEND; + + if ((setting != CY_AS_HS_SD_FREQ_24) && + (setting != CY_AS_HS_SD_FREQ_48)) + return CY_AS_ERROR_INVALID_PARAMETER; + + return my_set_sd_clock_freq(dev_p, 1, (uint8_t)setting, cb, client); +} + +cy_as_return_status_t +cy_as_misc_get_gpio_value(cy_as_device_handle handle, + cy_as_misc_gpio pin, + uint8_t *value, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + cy_as_ll_request_response *req_p, *reply_p; + cy_as_device *dev_p; + uint16_t v; + + cy_as_log_debug_message(6, "cy_as_misc_get_gpio_value called"); + + /* Make sure the device is ready for the command. */ + dev_p = (cy_as_device *)handle; + cy_as_check_device_ready(dev_p); + + /* If the pin specified is UVALID, there is no need + * for firmware to be loaded. */ + if (pin == cy_as_misc_gpio_U_valid) { + v = cy_as_hal_read_register(dev_p->tag, CY_AS_MEM_PMU_UPDATE); + *value = (uint8_t)(v & CY_AS_MEM_PMU_UPDATE_UVALID); + + if (cb != 0) + cb(dev_p, ret, client, + CY_FUNCT_CB_MISC_GETGPIOVALUE, value); + + return ret; + } + + /* Check whether the firmware supports this command. */ + if (cy_as_device_is_nand_storage_supported(dev_p)) + return CY_AS_ERROR_NOT_SUPPORTED; + + if (cy_as_device_is_in_suspend_mode(dev_p)) + return CY_AS_ERROR_IN_SUSPEND; + + /* Make sure the pin selected is valid */ + if ((pin != cy_as_misc_gpio_1) && (pin != cy_as_misc_gpio_0)) + return CY_AS_ERROR_INVALID_PARAMETER; + + req_p = cy_as_ll_create_request(dev_p, CY_RQT_GET_GPIO_STATE, + CY_RQT_GENERAL_RQT_CONTEXT, 1); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + cy_as_ll_request_response__set_word(req_p, 0, ((uint8_t)pin << 8)); + + /* Reserve space for the reply, which will not exceed one word. */ + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_GPIO_STATE) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + *value = (uint8_t) + cy_as_ll_request_response__get_word(reply_p, 0); + } else { + + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_MISC_GETGPIOVALUE, value, + dev_p->func_cbs_misc, CY_AS_REQUEST_RESPONSE_EX, + req_p, reply_p, cy_as_misc_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + /* The request and response are freed as part of the + * MiscFuncCallback */ + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + + +cy_as_return_status_t +cy_as_misc_set_gpio_value(cy_as_device_handle handle, + cy_as_misc_gpio pin, + uint8_t value, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + cy_as_ll_request_response *req_p, *reply_p; + cy_as_device *dev_p; + uint16_t v; + + cy_as_log_debug_message(6, "cy_as_misc_set_gpio_value called"); + + /* Make sure the device is ready for the command. */ + dev_p = (cy_as_device *)handle; + cy_as_check_device_ready(dev_p); + + /* If the pin specified is UVALID, there is + * no need for firmware to be loaded. */ + if (pin == cy_as_misc_gpio_U_valid) { + v = cy_as_hal_read_register(dev_p->tag, CY_AS_MEM_PMU_UPDATE); + if (value) + cy_as_hal_write_register(dev_p->tag, + CY_AS_MEM_PMU_UPDATE, + (v | CY_AS_MEM_PMU_UPDATE_UVALID)); + else + cy_as_hal_write_register(dev_p->tag, + CY_AS_MEM_PMU_UPDATE, + (v & ~CY_AS_MEM_PMU_UPDATE_UVALID)); + + if (cb != 0) + cb(dev_p, ret, client, + CY_FUNCT_CB_MISC_SETGPIOVALUE, 0); + return ret; + } + + /* Check whether the firmware supports this command. */ + if (cy_as_device_is_nand_storage_supported(dev_p)) + return CY_AS_ERROR_NOT_SUPPORTED; + + if (cy_as_device_is_in_suspend_mode(dev_p)) + return CY_AS_ERROR_IN_SUSPEND; + + /* Make sure the pin selected is valid */ + if ((pin < cy_as_misc_gpio_0) || (pin > cy_as_misc_gpio_U_valid)) + return CY_AS_ERROR_INVALID_PARAMETER; + + /* Create and initialize the low level request to the firmware. */ + req_p = cy_as_ll_create_request(dev_p, CY_RQT_SET_GPIO_STATE, + CY_RQT_GENERAL_RQT_CONTEXT, 1); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + v = (uint16_t)(((uint8_t)pin << 8) | (value > 0)); + cy_as_ll_request_response__set_word(req_p, 0, v); + + /* Reserve space for the reply, which will not exceed one word. */ + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_SUCCESS_FAILURE) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + ret = cy_as_ll_request_response__get_word(reply_p, 0); + } else { + + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_MISC_SETGPIOVALUE, 0, + dev_p->func_cbs_misc, CY_AS_REQUEST_RESPONSE_EX, + req_p, reply_p, cy_as_misc_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + /* The request and response are freed as part of the + * MiscFuncCallback */ + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +static cy_as_return_status_t +my_enter_standby(cy_as_device *dev_p, cy_bool pin) +{ + cy_as_misc_cancel_ex_requests(dev_p); + + /* Save the current values in the critical P-port + * registers, where necessary. */ + cy_as_hal_read_regs_before_standby(dev_p->tag); + + if (pin) { + if (cy_as_hal_set_wakeup_pin(dev_p->tag, cy_false)) + cy_as_device_set_pin_standby(dev_p); + else + return CY_AS_ERROR_SETTING_WAKEUP_PIN; + } else { + /* + * put antioch in the standby mode + */ + cy_as_hal_write_register(dev_p->tag, + CY_AS_MEM_PWR_MAGT_STAT, 0x02); + cy_as_device_set_register_standby(dev_p); + } + + /* + * when the antioch comes out of standby, we have to wait until + * the firmware initialization completes before sending other + * requests down. + */ + cy_as_device_set_firmware_not_loaded(dev_p); + + /* + * keep west bridge interrupt disabled until the device is being woken + * up from standby. + */ + dev_p->stby_int_mask = cy_as_hal_disable_interrupts(); + + return CY_AS_ERROR_SUCCESS; +} + +static cy_as_return_status_t +my_handle_response_enter_standby(cy_as_device *dev_p, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *reply_p, + cy_bool pin) +{ + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_SUCCESS_FAILURE) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + ret = cy_as_ll_request_response__get_word(reply_p, 0); + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + ret = my_enter_standby(dev_p, pin); + + return ret; +} + +cy_as_return_status_t +cy_as_misc_enter_standby(cy_as_device_handle handle, + cy_bool pin, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_device *dev_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + cy_as_ll_request_response *req_p, *reply_p; + cy_bool standby; + + cy_as_log_debug_message(6, "cy_as_misc_enter_standby called"); + + /* Make sure we have a valid device */ + dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + /* + * if we already are in standby, do not do it again and let the + * user know via the error return. + */ + ret = cy_as_misc_in_standby(handle, &standby); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (standby == cy_true) + return CY_AS_ERROR_ALREADY_STANDBY; + + /* + * if the user wants to transition from suspend mode to standby mode, + * the device needs to be woken up so that it can complete all pending + * operations. + */ + if (cy_as_device_is_in_suspend_mode(dev_p)) + cy_as_misc_leave_suspend(dev_p, 0, 0); + + if (dev_p->usb_count) { + /* + * we do not allow west bridge to go into standby mode when the + * USB stack is initialized. you must stop the USB stack in + * order to enter standby mode. + */ + return CY_AS_ERROR_USB_RUNNING; + } + + /* + * if the storage stack is not running, the device can directly be + * put into sleep mode. otherwise, the firmware needs to be signaled + * to prepare for going into sleep mode. + */ + if (dev_p->storage_count) { + /* + * if there are async storage operations pending, + * make one attempt to complete them. + */ + if (cy_as_device_is_storage_async_pending(dev_p)) { + /* DrainQueue will not work in polling mode */ + if (cy_as_hal_is_polling()) + return CY_AS_ERROR_ASYNC_PENDING; + + cy_as_dma_drain_queue(dev_p, + CY_AS_P2S_READ_ENDPOINT, cy_false); + cy_as_dma_drain_queue(dev_p, + CY_AS_P2S_WRITE_ENDPOINT, cy_false); + + /* + * if more storage operations were queued + * at this stage, return an error. + */ + if (cy_as_device_is_storage_async_pending(dev_p)) + return CY_AS_ERROR_ASYNC_PENDING; + } + + req_p = cy_as_ll_create_request(dev_p, + CY_RQT_PREPARE_FOR_STANDBY, + CY_RQT_GENERAL_RQT_CONTEXT, 1); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (!cb) { + ret = cy_as_ll_send_request_wait_reply(dev_p, + req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + /* The request and response are freed + * in the HandleResponse */ + return my_handle_response_enter_standby(dev_p, + req_p, reply_p, pin); + + } else { + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_MISC_ENTERSTANDBY, (void *)pin, + dev_p->func_cbs_misc, CY_AS_REQUEST_RESPONSE_EX, + req_p, reply_p, cy_as_misc_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + /* The request and response are freed + * as part of the MiscFuncCallback */ + return ret; + } +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + } else { + ret = my_enter_standby(dev_p, pin); + if (cb) + /* Even though no mailbox communication was + * needed, issue the callback so the user + * does not need to special case their code. */ + cb((cy_as_device_handle)dev_p, ret, client, + CY_FUNCT_CB_MISC_ENTERSTANDBY, 0); + } + + return ret; +} + +cy_as_return_status_t +cy_as_misc_enter_standby_e_x_u(cy_as_device_handle handle, + cy_bool pin, + cy_bool uvalid_special, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_device *dev_p; + + dev_p = (cy_as_device *)handle; + if (uvalid_special) + cy_as_hal_write_register(dev_p->tag, 0xc5, 0x4); + + return cy_as_misc_enter_standby(handle, pin, cb, client); +} + +cy_as_return_status_t +cy_as_misc_leave_standby(cy_as_device_handle handle, + cy_as_resource_type resource) +{ + cy_as_device *dev_p; + uint16_t v; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + uint32_t count = 8; + uint8_t retry = 1; + + cy_as_log_debug_message(6, "cy_as_misc_leave_standby called"); + (void)resource; + + /* Make sure we have a valid device */ + dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + if (cy_as_device_is_register_standby(dev_p)) { + /* + * set a flag to indicate that the west bridge is waking + * up from standby. + */ + cy_as_device_set_waking(dev_p); + + /* + * the initial read will not succeed, but will just wake + * the west bridge device from standby. successive reads + * should succeed and in that way we know west bridge is awake. + */ + v = cy_as_hal_read_register(dev_p->tag, + CY_AS_MEM_CM_WB_CFG_ID); + + do { + /* + * we have initiated the operation to leave standby, now + * we need to wait at least N ms before trying to access + * the west bridge device to insure the PLLs have locked + * and we can talk to the device. + */ + if (cy_as_device_is_crystal(dev_p)) + cy_as_hal_sleep( + CY_AS_LEAVE_STANDBY_DELAY_CRYSTAL); + else + cy_as_hal_sleep( + CY_AS_LEAVE_STANDBY_DELAY_CLOCK); + v = cy_as_hal_read_register(dev_p->tag, + CY_AS_MEM_CM_WB_CFG_ID); + + /* + * if the P-SPI interface mode is in use, there may be a + * need to re-synchronise the serial clock used for + * astoria access. + */ + if (!is_valid_silicon_id(v)) { + if (cy_as_hal_sync_device_clocks(dev_p->tag) != + cy_true) { + cy_as_hal_enable_interrupts( + dev_p->stby_int_mask); + return CY_AS_ERROR_TIMEOUT; + } + } + } while (!is_valid_silicon_id(v) && count-- > 0); + + /* + * if we tried to read the register and could not, + * return a timeout + */ + if (count == 0) { + cy_as_hal_enable_interrupts( + dev_p->stby_int_mask); + return CY_AS_ERROR_TIMEOUT; + } + + /* + * the standby flag is cleared here, after the action to + * exit standby has been taken. the wait for firmware + * initialization, is ensured by marking the firmware as + * not loaded until the init event is received. + */ + cy_as_device_clear_register_standby(dev_p); + + /* + * initialize any registers that may have been changed + * while the device was in standby mode. + */ + cy_as_hal_init_dev_registers(dev_p->tag, cy_true); + } else if (cy_as_device_is_pin_standby(dev_p)) { + /* + * set a flag to indicate that the west bridge is waking + * up from standby. + */ + cy_as_device_set_waking(dev_p); + +try_wakeup_again: + /* + * try to set the wakeup pin, if this fails in the HAL + * layer, return this failure to the user. + */ + if (!cy_as_hal_set_wakeup_pin(dev_p->tag, cy_true)) { + cy_as_hal_enable_interrupts(dev_p->stby_int_mask); + return CY_AS_ERROR_SETTING_WAKEUP_PIN; + } + + /* + * we have initiated the operation to leave standby, now + * we need to wait at least N ms before trying to access + * the west bridge device to insure the PL_ls have locked + * and we can talk to the device. + */ + if (cy_as_device_is_crystal(dev_p)) + cy_as_hal_sleep(CY_AS_LEAVE_STANDBY_DELAY_CRYSTAL); + else + cy_as_hal_sleep(CY_AS_LEAVE_STANDBY_DELAY_CLOCK); + + /* + * initialize any registers that may have been changed + * while the device was in standby mode. + */ + cy_as_hal_init_dev_registers(dev_p->tag, cy_true); + + /* + * the standby flag is cleared here, after the action to + * exit standby has been taken. the wait for firmware + * initialization, is ensured by marking the firmware as + * not loaded until the init event is received. + */ + cy_as_device_clear_pin_standby(dev_p); + } else { + return CY_AS_ERROR_NOT_IN_STANDBY; + } + + /* + * the west bridge interrupt can be enabled now. + */ + cy_as_hal_enable_interrupts(dev_p->stby_int_mask); + + /* + * release the west bridge micro-_controller from reset, + * so that firmware initialization can complete. the attempt + * to release antioch reset is made upto 8 times. + */ + v = 0x03; + count = 0x08; + while ((v & 0x03) && (count)) { + cy_as_hal_write_register(dev_p->tag, + CY_AS_MEM_RST_CTRL_REG, 0x00); + v = cy_as_hal_read_register(dev_p->tag, + CY_AS_MEM_RST_CTRL_REG); + count--; + } + + if (v & 0x03) { + cy_as_hal_print_message("failed to clear antioch reset\n"); + return CY_AS_ERROR_TIMEOUT; + } + + /* + * if the wake-up pin is being used, wait here to make + * sure that the wake-up event is received within a + * reasonable delay. otherwise, toggle the wake-up pin + * again in an attempt to start the firmware properly. + */ + if (retry) { + count = 10; + while (count) { + /* If the wake-up event has been received, + * we can return. */ + if (cy_as_device_is_firmware_loaded(dev_p)) + break; + /* If we are in polling mode, the interrupt may + * not have been serviced as yet. read the + * interrupt status register. if a pending mailbox + * interrupt is seen, we can assume that the + * wake-up event will be received soon. */ + v = cy_as_hal_read_register(dev_p->tag, + CY_AS_MEM_P0_INTR_REG); + if (v & CY_AS_MEM_P0_INTR_REG_MBINT) + break; + + cy_as_hal_sleep(10); + count--; + } + + if (!count) { + retry = 0; + dev_p->stby_int_mask = cy_as_hal_disable_interrupts(); + cy_as_hal_set_wakeup_pin(dev_p->tag, cy_false); + cy_as_hal_sleep(10); + goto try_wakeup_again; + } + } + + return ret; +} + +cy_as_return_status_t +cy_as_misc_register_callback( + /* Handle to the West Bridge device */ + cy_as_device_handle handle, + /* The function to call */ + cy_as_misc_event_callback callback + ) +{ + cy_as_device *dev_p; + + cy_as_log_debug_message(6, "cy_as_misc_register_callback called"); + + /* Make sure we have a valid device */ + dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + dev_p->misc_event_cb = callback; + return CY_AS_ERROR_SUCCESS; +} + +cy_as_return_status_t +cy_as_misc_storage_changed(cy_as_device_handle handle, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_device *dev_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + cy_bool standby; + cy_as_ll_request_response *req_p, *reply_p; + + cy_as_log_debug_message(6, "cy_as_misc_storage_changed called"); + + /* Make sure the device is ready for the command. */ + dev_p = (cy_as_device *)handle; + cy_as_check_device_ready(dev_p); + + /* + * make sure antioch is not in standby + */ + ret = cy_as_misc_in_standby(dev_p, &standby); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (standby) + return CY_AS_ERROR_IN_STANDBY; + + /* + * make sure westbridge is not in suspend mode. + */ + if (cy_as_device_is_in_suspend_mode(dev_p)) + return CY_AS_ERROR_IN_SUSPEND; + + /* Create the request to send to the West Bridge device */ + req_p = cy_as_ll_create_request(dev_p, CY_RQT_STORAGE_MEDIA_CHANGED, + CY_RQT_GENERAL_RQT_CONTEXT, 0); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + /* Reserve space for the reply, the reply data will + * not exceed one word */ + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_SUCCESS_FAILURE) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + ret = cy_as_ll_request_response__get_word(reply_p, 0); + } else { + + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_MISC_STORAGECHANGED, 0, + dev_p->func_cbs_misc, CY_AS_REQUEST_RESPONSE_EX, + req_p, reply_p, cy_as_misc_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + /* The request and response are freed as part of the + * MiscFuncCallback */ + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + + +cy_as_return_status_t +cy_as_misc_enter_suspend( + cy_as_device_handle handle, + cy_bool usb_wakeup_en, + cy_bool gpio_wakeup_en, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_device *dev_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + cy_bool standby; + cy_as_ll_request_response *req_p, *reply_p; + uint16_t value; + uint32_t int_state; + + cy_as_log_debug_message(6, "cy_as_misc_enter_suspend called"); + + /* + * basic sanity checks to ensure that the device is initialised. + */ + dev_p = (cy_as_device *)handle; + cy_as_check_device_ready(dev_p); + + /* + * make sure west bridge is not already in standby + */ + cy_as_misc_in_standby(dev_p, &standby); + if (standby) + return CY_AS_ERROR_IN_STANDBY; + + /* + * make sure that the device is not already in suspend mode. + */ + if (cy_as_device_is_in_suspend_mode(dev_p)) + return CY_AS_ERROR_IN_SUSPEND; + + /* + * make sure there is no active USB connection. + */ + if ((cy_as_device_is_usb_connected(dev_p)) && (dev_p->usb_last_event + != cy_as_event_usb_suspend)) + return CY_AS_ERROR_USB_CONNECTED; + + /* + * make sure that there are no async requests at this point in time. + */ + int_state = cy_as_hal_disable_interrupts(); + if ((dev_p->func_cbs_misc->count) || (dev_p->func_cbs_res->count) || + (dev_p->func_cbs_stor->count) || (dev_p->func_cbs_usb->count)) { + cy_as_hal_enable_interrupts(int_state); + return CY_AS_ERROR_ASYNC_PENDING; + } + cy_as_hal_enable_interrupts(int_state); + + /* Create the request to send to the Antioch device */ + req_p = cy_as_ll_create_request(dev_p, CY_RQT_ENTER_SUSPEND_MODE, + CY_RQT_GENERAL_RQT_CONTEXT, 1); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + /* Reserve space for the reply, the reply data will not + * exceed one word */ + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + /* Wakeup control flags. */ + value = 0x0001; + if (usb_wakeup_en) + value |= 0x04; + if (gpio_wakeup_en) + value |= 0x02; + cy_as_ll_request_response__set_word(req_p, 0, value); + + if (cb != 0) { + + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_MISC_ENTERSUSPEND, + 0, dev_p->func_cbs_misc, CY_AS_REQUEST_RESPONSE_EX, + req_p, reply_p, + cy_as_misc_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return CY_AS_ERROR_SUCCESS; + } else { + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_SUCCESS_FAILURE) + ret = CY_AS_ERROR_INVALID_RESPONSE; + else + ret = cy_as_ll_request_response__get_word(reply_p, 0); + } + +destroy: + if (ret == CY_AS_ERROR_SUCCESS) + cy_as_device_set_suspend_mode(dev_p); + + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +cy_as_return_status_t +cy_as_misc_leave_suspend( + cy_as_device_handle handle, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_device *dev_p; + uint16_t v, count; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + + cy_as_log_debug_message(6, "cy_as_misc_leave_suspend called"); + + /* Make sure we have a valid device */ + dev_p = (cy_as_device *)handle; + cy_as_check_device_ready(dev_p); + + /* Make sure we are in suspend mode. */ + if (cy_as_device_is_in_suspend_mode(dev_p)) { + if (cb) { + cy_as_func_c_b_node *cbnode = + cy_as_create_func_c_b_node_data(cb, client, + CY_FUNCT_CB_MISC_LEAVESUSPEND, 0); + if (cbnode == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + cy_as_insert_c_b_node(dev_p->func_cbs_misc, cbnode); + } + + /* + * do a read from the ID register so that the CE assertion + * will wake west bridge. the read is repeated until the + * read comes back with valid data. + */ + count = 8; + + v = cy_as_hal_read_register(dev_p->tag, + CY_AS_MEM_CM_WB_CFG_ID); + + while (!is_valid_silicon_id(v) && count-- > 0) { + cy_as_hal_sleep(CY_AS_LEAVE_STANDBY_DELAY_CLOCK); + v = cy_as_hal_read_register(dev_p->tag, + CY_AS_MEM_CM_WB_CFG_ID); + } + + /* + * if we tried to read the register and could not, + * return a timeout + */ + if (count == 0) + return CY_AS_ERROR_TIMEOUT; + } else + return CY_AS_ERROR_NOT_IN_SUSPEND; + + if (cb == 0) { + /* + * wait until the in suspend mode flag is cleared. + */ + count = 20; + while ((cy_as_device_is_in_suspend_mode(dev_p)) + && (count--)) { + cy_as_hal_sleep(CY_AS_LEAVE_STANDBY_DELAY_CLOCK); + } + + if (cy_as_device_is_in_suspend_mode(dev_p)) + ret = CY_AS_ERROR_TIMEOUT; + } + + return ret; +} + +cy_as_return_status_t +cy_as_misc_reserve_l_n_a_boot_area(cy_as_device_handle handle, + uint8_t numzones, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + cy_bool standby; + cy_as_ll_request_response *req_p, *reply_p; + + cy_as_device *dev_p; + + (void)client; + + cy_as_log_debug_message(6, "cy_as_misc_switch_pnand_mode called"); + + /* Make sure we have a valid device */ + dev_p = (cy_as_device *)handle; + cy_as_check_device_ready(dev_p); + + /* + * make sure antioch is not in standby + */ + ret = cy_as_misc_in_standby(dev_p, &standby); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + if (standby) + return CY_AS_ERROR_IN_STANDBY; + + /* Make sure the Antioch is not in suspend mode. */ + if (cy_as_device_is_in_suspend_mode(dev_p)) + return CY_AS_ERROR_IN_SUSPEND; + + /* Create the request to send to the West Bridge device */ + req_p = cy_as_ll_create_request(dev_p, + CY_RQT_RESERVE_LNA_BOOT_AREA, + CY_RQT_GENERAL_RQT_CONTEXT, 1); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + cy_as_ll_request_response__set_word(req_p, + 0, (uint16_t)numzones); + + /* Reserve space for the reply, the reply data will not + * exceed one word */ + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, + req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_SUCCESS_FAILURE) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + ret = cy_as_ll_request_response__get_word(reply_p, 0); + } else { + + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_MISC_RESERVELNABOOTAREA, + 0, dev_p->func_cbs_misc, CY_AS_REQUEST_RESPONSE_EX, + req_p, reply_p, cy_as_misc_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + /* The request and response are freed as part of the + * MiscFuncCallback */ + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +cy_as_func_c_b_node* +cy_as_create_func_c_b_node_data(cy_as_function_callback cb, + uint32_t client, + cy_as_funct_c_b_type type, + void *data) +{ + uint32_t state = cy_as_hal_disable_interrupts(); + cy_as_func_c_b_node *node = cy_as_hal_c_b_alloc( + sizeof(cy_as_func_c_b_node)); + cy_as_hal_enable_interrupts(state); + if (node != 0) { + node->node_type = CYAS_FUNC_CB; + node->cb_p = cb; + node->client_data = client; + node->data_type = type; + if (data != 0) + node->data_type |= CY_FUNCT_CB_DATA; + else + node->data_type |= CY_FUNCT_CB_NODATA; + node->data = data; + node->next_p = 0; + } + return node; +} + +cy_as_func_c_b_node* +cy_as_create_func_c_b_node(cy_as_function_callback cb, + uint32_t client) +{ + return cy_as_create_func_c_b_node_data(cb, client, + CY_FUNCT_CB_NODATA, 0); +} + +void +cy_as_destroy_func_c_b_node(cy_as_func_c_b_node *node) +{ + uint32_t state; + + node->node_type = CYAS_INVALID; + state = cy_as_hal_disable_interrupts(); + cy_as_hal_c_b_free(node); + cy_as_hal_enable_interrupts(state); +} + +cy_as_usb_func_c_b_node* +cy_as_create_usb_func_c_b_node( + cy_as_usb_function_callback cb, uint32_t client) +{ + uint32_t state = cy_as_hal_disable_interrupts(); + cy_as_usb_func_c_b_node *node = cy_as_hal_c_b_alloc( + sizeof(cy_as_usb_func_c_b_node)); + cy_as_hal_enable_interrupts(state); + if (node != 0) { + node->type = CYAS_USB_FUNC_CB; + node->cb_p = cb; + node->client_data = client; + node->next_p = 0; + } + return node; +} + +void +cy_as_destroy_usb_func_c_b_node(cy_as_usb_func_c_b_node *node) +{ + uint32_t state; + + node->type = CYAS_INVALID; + state = cy_as_hal_disable_interrupts(); + cy_as_hal_c_b_free(node); + cy_as_hal_enable_interrupts(state); +} + +cy_as_usb_io_c_b_node* +cy_as_create_usb_io_c_b_node(cy_as_usb_io_callback cb) +{ + uint32_t state = cy_as_hal_disable_interrupts(); + cy_as_usb_io_c_b_node *node = cy_as_hal_c_b_alloc( + sizeof(cy_as_usb_io_c_b_node)); + cy_as_hal_enable_interrupts(state); + if (node != 0) { + node->type = CYAS_USB_IO_CB; + node->cb_p = cb; + node->next_p = 0; + } + return node; +} + +void +cy_as_destroy_usb_io_c_b_node(cy_as_usb_io_c_b_node *node) +{ + uint32_t state; + + node->type = CYAS_INVALID; + + state = cy_as_hal_disable_interrupts(); + cy_as_hal_c_b_free(node); + cy_as_hal_enable_interrupts(state); +} + +cy_as_storage_io_c_b_node* +cy_as_create_storage_io_c_b_node(cy_as_storage_callback cb, + cy_as_media_type media, uint32_t device_index, + uint32_t unit, uint32_t block_addr, cy_as_oper_type oper, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *reply_p) +{ + uint32_t state = cy_as_hal_disable_interrupts(); + cy_as_storage_io_c_b_node *node = cy_as_hal_c_b_alloc( + sizeof(cy_as_storage_io_c_b_node)); + cy_as_hal_enable_interrupts(state); + if (node != 0) { + node->type = CYAS_STORAGE_IO_CB; + node->cb_p = cb; + node->media = media; + node->device_index = device_index; + node->unit = unit; + node->block_addr = block_addr; + node->oper = oper; + node->req_p = req_p; + node->reply_p = reply_p; + node->next_p = 0; + } + return node; +} + +void +cy_as_destroy_storage_io_c_b_node(cy_as_storage_io_c_b_node *node) +{ + uint32_t state; + node->type = CYAS_INVALID; + state = cy_as_hal_disable_interrupts(); + cy_as_hal_c_b_free(node); + cy_as_hal_enable_interrupts(state); +} + +cy_as_c_b_queue * +cy_as_create_c_b_queue(cy_as_c_b_node_type type) +{ + uint32_t state = cy_as_hal_disable_interrupts(); + cy_as_c_b_queue *queue = cy_as_hal_c_b_alloc( + sizeof(cy_as_c_b_queue)); + cy_as_hal_enable_interrupts(state); + if (queue) { + queue->type = type; + queue->head_p = 0; + queue->tail_p = 0; + queue->count = 0; + } + + return queue; +} + +void +cy_as_destroy_c_b_queue(cy_as_c_b_queue *queue) +{ + uint32_t state; + queue->type = CYAS_INVALID; + queue->head_p = 0; + queue->tail_p = 0; + queue->count = 0; + state = cy_as_hal_disable_interrupts(); + cy_as_hal_c_b_free(queue); + cy_as_hal_enable_interrupts(state); +} + +/* Inserts a CyAsCBNode into the queue, the + * node type must match the queue type*/ +void +cy_as_insert_c_b_node(cy_as_c_b_queue *queue_p, void*cbnode) +{ + uint32_t int_state; + + int_state = cy_as_hal_disable_interrupts(); + + cy_as_hal_assert(queue_p != 0); + + switch (queue_p->type) { + case CYAS_USB_FUNC_CB: + { + cy_as_usb_func_c_b_node *node = + (cy_as_usb_func_c_b_node *)cbnode; + cy_as_usb_func_c_b_node *tail = + (cy_as_usb_func_c_b_node *)queue_p->tail_p; + + cy_as_hal_assert(node->type == CYAS_USB_FUNC_CB); + cy_as_hal_assert(tail == 0 || + tail->type == CYAS_USB_FUNC_CB); + if (queue_p->head_p == 0) + queue_p->head_p = node; + else + tail->next_p = node; + + queue_p->tail_p = node; + } + break; + + case CYAS_USB_IO_CB: + { + cy_as_usb_io_c_b_node *node = + (cy_as_usb_io_c_b_node *)cbnode; + cy_as_usb_io_c_b_node *tail = + (cy_as_usb_io_c_b_node *)queue_p->tail_p; + + cy_as_hal_assert(node->type == CYAS_USB_IO_CB); + cy_as_hal_assert(tail == 0 || + tail->type == CYAS_USB_IO_CB); + if (queue_p->head_p == 0) + queue_p->head_p = node; + else + tail->next_p = node; + + queue_p->tail_p = node; + } + break; + + case CYAS_STORAGE_IO_CB: + { + cy_as_storage_io_c_b_node *node = + (cy_as_storage_io_c_b_node *)cbnode; + cy_as_storage_io_c_b_node *tail = + (cy_as_storage_io_c_b_node *)queue_p->tail_p; + + cy_as_hal_assert(node->type == CYAS_STORAGE_IO_CB); + cy_as_hal_assert(tail == 0 || + tail->type == CYAS_STORAGE_IO_CB); + if (queue_p->head_p == 0) + queue_p->head_p = node; + else + tail->next_p = node; + + queue_p->tail_p = node; + } + break; + + case CYAS_FUNC_CB: + { + cy_as_func_c_b_node *node = + (cy_as_func_c_b_node *)cbnode; + cy_as_func_c_b_node *tail = + (cy_as_func_c_b_node *)queue_p->tail_p; + + cy_as_hal_assert(node->node_type == CYAS_FUNC_CB); + cy_as_hal_assert(tail == 0 || + tail->node_type == CYAS_FUNC_CB); + if (queue_p->head_p == 0) + queue_p->head_p = node; + else + tail->next_p = node; + + queue_p->tail_p = node; + } + break; + + default: + cy_as_hal_assert(cy_false); + break; + } + + queue_p->count++; + + cy_as_hal_enable_interrupts(int_state); +} + +/* Removes the tail node from the queue and frees it */ +void +cy_as_remove_c_b_tail_node(cy_as_c_b_queue *queue_p) +{ + uint32_t int_state; + + int_state = cy_as_hal_disable_interrupts(); + + if (queue_p->count > 0) { + /* + * the worst case length of the queue should be + * under 10 elements, and the average case should + * be just 1 element. so, we just employ a linear + * search to find the node to be freed. + */ + switch (queue_p->type) { + case CYAS_FUNC_CB: + { + cy_as_func_c_b_node *node = + (cy_as_func_c_b_node *) + queue_p->head_p; + cy_as_func_c_b_node *tail = + (cy_as_func_c_b_node *) + queue_p->tail_p; + if (node != tail) { + while (node->next_p != tail) + node = node->next_p; + node->next_p = 0; + queue_p->tail_p = node; + } + cy_as_destroy_func_c_b_node(tail); + } + break; + + case CYAS_USB_FUNC_CB: + { + cy_as_usb_func_c_b_node *node = + (cy_as_usb_func_c_b_node *) + queue_p->head_p; + cy_as_usb_func_c_b_node *tail = + (cy_as_usb_func_c_b_node *) + queue_p->tail_p; + if (node != tail) { + while (node->next_p != tail) + node = node->next_p; + node->next_p = 0; + queue_p->tail_p = node; + } + + cy_as_destroy_usb_func_c_b_node(tail); + } + break; + + case CYAS_USB_IO_CB: + { + cy_as_usb_io_c_b_node *node = + (cy_as_usb_io_c_b_node *) + queue_p->head_p; + cy_as_usb_io_c_b_node *tail = + (cy_as_usb_io_c_b_node *) + queue_p->tail_p; + if (node != tail) { + while (node->next_p != tail) + node = node->next_p; + node->next_p = 0; + queue_p->tail_p = node; + } + cy_as_destroy_usb_io_c_b_node(tail); + } + break; + + case CYAS_STORAGE_IO_CB: + { + cy_as_storage_io_c_b_node *node = + (cy_as_storage_io_c_b_node *) + queue_p->head_p; + cy_as_storage_io_c_b_node *tail = + (cy_as_storage_io_c_b_node *) + queue_p->tail_p; + if (node != tail) { + while (node->next_p != tail) + node = node->next_p; + node->next_p = 0; + queue_p->tail_p = node; + } + cy_as_destroy_storage_io_c_b_node(tail); + } + break; + + default: + cy_as_hal_assert(cy_false); + } + + queue_p->count--; + if (queue_p->count == 0) { + queue_p->head_p = 0; + queue_p->tail_p = 0; + } + } + + cy_as_hal_enable_interrupts(int_state); +} + +/* Removes the first CyAsCBNode from the queue and frees it */ +void +cy_as_remove_c_b_node(cy_as_c_b_queue *queue_p) +{ + uint32_t int_state; + + int_state = cy_as_hal_disable_interrupts(); + + cy_as_hal_assert(queue_p->count >= 0); + if (queue_p->count > 0) { + if (queue_p->type == CYAS_USB_FUNC_CB) { + cy_as_usb_func_c_b_node *node = + (cy_as_usb_func_c_b_node *) + queue_p->head_p; + queue_p->head_p = node->next_p; + cy_as_destroy_usb_func_c_b_node(node); + } else if (queue_p->type == CYAS_USB_IO_CB) { + cy_as_usb_io_c_b_node *node = + (cy_as_usb_io_c_b_node *) + queue_p->head_p; + queue_p->head_p = node->next_p; + cy_as_destroy_usb_io_c_b_node(node); + } else if (queue_p->type == CYAS_STORAGE_IO_CB) { + cy_as_storage_io_c_b_node *node = + (cy_as_storage_io_c_b_node *) + queue_p->head_p; + queue_p->head_p = node->next_p; + cy_as_destroy_storage_io_c_b_node(node); + } else if (queue_p->type == CYAS_FUNC_CB) { + cy_as_func_c_b_node *node = + (cy_as_func_c_b_node *) + queue_p->head_p; + queue_p->head_p = node->next_p; + cy_as_destroy_func_c_b_node(node); + } else { + cy_as_hal_assert(cy_false); + } + + queue_p->count--; + if (queue_p->count == 0) { + queue_p->head_p = 0; + queue_p->tail_p = 0; + } + } + + cy_as_hal_enable_interrupts(int_state); +} + +void my_print_func_c_b_node(cy_as_func_c_b_node *node) +{ + cy_as_funct_c_b_type type = + cy_as_funct_c_b_type_get_type(node->data_type); + cy_as_hal_print_message("[cd:%2u dt:%2u cb:0x%08x " + "d:0x%08x nt:%1i]", node->client_data, type, + (uint32_t)node->cb_p, (uint32_t)node->data, + node->node_type); +} + +void my_print_c_b_queue(cy_as_c_b_queue *queue_p) +{ + uint32_t i = 0; + + cy_as_hal_print_message("| count: %u type: ", queue_p->count); + + if (queue_p->type == CYAS_USB_FUNC_CB) { + cy_as_hal_print_message("USB_FUNC_CB\n"); + } else if (queue_p->type == CYAS_USB_IO_CB) { + cy_as_hal_print_message("USB_IO_CB\n"); + } else if (queue_p->type == CYAS_STORAGE_IO_CB) { + cy_as_hal_print_message("STORAGE_IO_CB\n"); + } else if (queue_p->type == CYAS_FUNC_CB) { + cy_as_func_c_b_node *node = queue_p->head_p; + cy_as_hal_print_message("FUNC_CB\n"); + if (queue_p->count > 0) { + cy_as_hal_print_message("| head->"); + + for (i = 0; i < queue_p->count; i++) { + if (node) { + cy_as_hal_print_message("->"); + my_print_func_c_b_node(node); + node = node->next_p; + } else + cy_as_hal_print_message("->[NULL]\n"); + } + + cy_as_hal_print_message("\n| tail->"); + my_print_func_c_b_node(queue_p->tail_p); + cy_as_hal_print_message("\n"); + } + } else { + cy_as_hal_print_message("INVALID\n"); + } + + cy_as_hal_print_message("|----------\n"); +} + + +/* Removes and frees all pending callbacks */ +void +cy_as_clear_c_b_queue(cy_as_c_b_queue *queue_p) +{ + uint32_t int_state = cy_as_hal_disable_interrupts(); + + while (queue_p->count != 0) + cy_as_remove_c_b_node(queue_p); + + cy_as_hal_enable_interrupts(int_state); +} + +cy_as_return_status_t +cy_as_misc_send_request(cy_as_device *dev_p, + cy_as_function_callback cb, + uint32_t client, + cy_as_funct_c_b_type type, + void *data, + cy_as_c_b_queue *queue, + uint16_t req_type, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *reply_p, + cy_as_response_callback rcb) +{ + + cy_as_func_c_b_node *cbnode = cy_as_create_func_c_b_node_data(cb, + client, type, data); + cy_as_return_status_t ret; + + if (cbnode == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + else + cy_as_insert_c_b_node(queue, cbnode); + + req_p->flags |= req_type; + + ret = cy_as_ll_send_request(dev_p, req_p, reply_p, cy_false, rcb); + if (ret != CY_AS_ERROR_SUCCESS) + cy_as_remove_c_b_tail_node(queue); + + return ret; +} + +void +cy_as_misc_cancel_ex_requests(cy_as_device *dev_p) +{ + int i; + for (i = 0; i < CY_RQT_CONTEXT_COUNT; i++) + cy_as_ll_remove_all_requests(dev_p, dev_p->context[i]); +} + + +static void +cy_as_misc_func_callback(cy_as_device *dev_p, + uint8_t context, + cy_as_ll_request_response *rqt, + cy_as_ll_request_response *resp, + cy_as_return_status_t stat) +{ + cy_as_func_c_b_node *node = NULL; + cy_as_return_status_t ret; + + cy_bool ex_request = (rqt->flags & CY_AS_REQUEST_RESPONSE_EX) + == CY_AS_REQUEST_RESPONSE_EX; + cy_bool ms_request = (rqt->flags & CY_AS_REQUEST_RESPONSE_MS) + == CY_AS_REQUEST_RESPONSE_MS; + uint8_t code; + uint32_t type; + uint8_t cntxt; + + cy_as_hal_assert(ex_request || ms_request); + (void) ex_request; + (void) ms_request; + (void)context; + + cntxt = cy_as_ll_request_response__get_context(rqt); + code = cy_as_ll_request_response__get_code(rqt); + + switch (cntxt) { + case CY_RQT_GENERAL_RQT_CONTEXT: + cy_as_hal_assert(dev_p->func_cbs_misc->count != 0); + cy_as_hal_assert(dev_p->func_cbs_misc->type == CYAS_FUNC_CB); + node = (cy_as_func_c_b_node *)dev_p->func_cbs_misc->head_p; + type = cy_as_funct_c_b_type_get_type(node->data_type); + + switch (code) { + case CY_RQT_GET_FIRMWARE_VERSION: + cy_as_hal_assert(node->data != 0); + cy_as_hal_assert(type == + CY_FUNCT_CB_MISC_GETFIRMWAREVERSION); + ret = my_handle_response_get_firmware_version(dev_p, + rqt, resp, + (cy_as_get_firmware_version_data *)node->data); + break; + case CY_RQT_READ_MCU_REGISTER: + cy_as_hal_assert(node->data != 0); + cy_as_hal_assert(type == + CY_FUNCT_CB_MISC_READMCUREGISTER); + ret = my_handle_response_read_m_c_u_register(dev_p, rqt, + resp, (uint8_t *)node->data); + break; + case CY_RQT_GET_GPIO_STATE: + cy_as_hal_assert(node->data != 0); + cy_as_hal_assert(type == + CY_FUNCT_CB_MISC_GETGPIOVALUE); + ret = my_handle_response_get_gpio_value(dev_p, rqt, + resp, (uint8_t *)node->data); + break; + case CY_RQT_SET_SD_CLOCK_FREQ: + cy_as_hal_assert(type == CY_FUNCT_CB_MISC_SETSDFREQ); + ret = my_handle_response_no_data(dev_p, rqt, resp); + break; + case CY_RQT_CONTROL_ANTIOCH_HEARTBEAT: + cy_as_hal_assert(type == + CY_FUNCT_CB_MISC_HEARTBEATCONTROL); + ret = my_handle_response_no_data(dev_p, rqt, resp); + break; + case CY_RQT_WRITE_MCU_REGISTER: + cy_as_hal_assert(type == + CY_FUNCT_CB_MISC_WRITEMCUREGISTER); + ret = my_handle_response_no_data(dev_p, rqt, resp); + break; + case CY_RQT_STORAGE_MEDIA_CHANGED: + cy_as_hal_assert(type == + CY_FUNCT_CB_MISC_STORAGECHANGED); + ret = my_handle_response_no_data(dev_p, rqt, resp); + break; + case CY_RQT_SET_GPIO_STATE: + cy_as_hal_assert(type == + CY_FUNCT_CB_MISC_SETGPIOVALUE); + ret = my_handle_response_no_data(dev_p, rqt, resp); + break; + case CY_RQT_SET_TRACE_LEVEL: + cy_as_hal_assert(type == + CY_FUNCT_CB_MISC_SETTRACELEVEL); + ret = my_handle_response_no_data(dev_p, rqt, resp); + if (ret == CY_AS_ERROR_INVALID_RESPONSE) + ret = CY_AS_ERROR_NOT_SUPPORTED; + break; + case CY_RQT_PREPARE_FOR_STANDBY: + cy_as_hal_assert(type == + CY_FUNCT_CB_MISC_ENTERSTANDBY); + ret = my_handle_response_enter_standby(dev_p, rqt, resp, + (cy_bool)node->data); + break; + case CY_RQT_ENTER_SUSPEND_MODE: + cy_as_hal_assert(type == + CY_FUNCT_CB_MISC_ENTERSUSPEND); + ret = my_handle_response_no_data(dev_p, rqt, resp); + if (ret == CY_AS_ERROR_SUCCESS) + cy_as_device_set_suspend_mode(dev_p); + + break; + case CY_RQT_RESERVE_LNA_BOOT_AREA: + cy_as_hal_assert(type == + CY_FUNCT_CB_MISC_RESERVELNABOOTAREA); + ret = my_handle_response_no_data(dev_p, rqt, resp); + break; + case CY_RQT_SDPOLARITY: + cy_as_hal_assert(type == + CY_FUNCT_CB_MISC_SETSDPOLARITY); + ret = my_handle_response_no_data(dev_p, rqt, resp); + break; + default: + ret = CY_AS_ERROR_INVALID_RESPONSE; + cy_as_hal_assert(cy_false); + break; + } + break; + + case CY_RQT_RESOURCE_RQT_CONTEXT: + cy_as_hal_assert(dev_p->func_cbs_res->count != 0); + cy_as_hal_assert(dev_p->func_cbs_res->type == CYAS_FUNC_CB); + node = (cy_as_func_c_b_node *)dev_p->func_cbs_res->head_p; + type = cy_as_funct_c_b_type_get_type(node->data_type); + + switch (code) { + case CY_RQT_ACQUIRE_RESOURCE: + /* The node->data field is actually an enum value + * which could be 0, thus no assert is done */ + cy_as_hal_assert(type == + CY_FUNCT_CB_MISC_ACQUIRERESOURCE); + ret = my_handle_response_acquire_resource(dev_p, rqt, + resp, (cy_as_resource_type *)node->data); + break; + default: + ret = CY_AS_ERROR_INVALID_RESPONSE; + cy_as_hal_assert(cy_false); + break; + } + break; + + default: + ret = CY_AS_ERROR_INVALID_RESPONSE; + cy_as_hal_assert(cy_false); + break; + } + + /* + * if the low level layer returns a direct error, use the + * corresponding error code. if not, use the error code + * based on the response from firmware. + */ + if (stat == CY_AS_ERROR_SUCCESS) + stat = ret; + + /* Call the user Callback */ + node->cb_p((cy_as_device_handle)dev_p, stat, node->client_data, + node->data_type, node->data); + if (cntxt == CY_RQT_GENERAL_RQT_CONTEXT) + cy_as_remove_c_b_node(dev_p->func_cbs_misc); + else + cy_as_remove_c_b_node(dev_p->func_cbs_res); + +} + + + +/*[]*/ diff --git a/drivers/staging/westbridge/astoria/api/src/cyasmtp.c b/drivers/staging/westbridge/astoria/api/src/cyasmtp.c new file mode 100644 index 000000000000..d5a8e45010dc --- /dev/null +++ b/drivers/staging/westbridge/astoria/api/src/cyasmtp.c @@ -0,0 +1,1128 @@ +/* Cypress West Bridge API header file (cyasmtp.h) +## =========================== +## Copyright (C) 2010 Cypress Semiconductor +## +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either version 2 +## of the License, or (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 51 Franklin Street, Fifth Floor +## Boston, MA 02110-1301, USA. +## =========================== +*/ + +#include "../../include/linux/westbridge/cyashal.h" +#include "../../include/linux/westbridge/cyasmtp.h" +#include "../../include/linux/westbridge/cyaserr.h" +#include "../../include/linux/westbridge/cyasdma.h" +#include "../../include/linux/westbridge/cyaslowlevel.h" + +static void +cy_as_mtp_func_callback(cy_as_device *dev_p, + uint8_t context, + cy_as_ll_request_response *rqt, + cy_as_ll_request_response *resp, + cy_as_return_status_t stat); + +static cy_as_return_status_t +is_mtp_active(cy_as_device *dev_p) +{ + if (!cy_as_device_is_configured(dev_p)) + return CY_AS_ERROR_NOT_CONFIGURED; + + if (!cy_as_device_is_firmware_loaded(dev_p)) + return CY_AS_ERROR_NO_FIRMWARE; + + if (dev_p->mtp_count == 0) + return CY_AS_ERROR_NOT_RUNNING; + + if (cy_as_device_is_in_suspend_mode(dev_p)) + return CY_AS_ERROR_IN_SUSPEND; + + return CY_AS_ERROR_SUCCESS; +} + +static void +my_mtp_request_callback(cy_as_device *dev_p, + uint8_t context, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *resp_p, + cy_as_return_status_t ret) +{ + uint16_t val, ev, status; + uint16_t mtp_datalen = 0; + uint32_t bytecount_l, bytecount_h; + cy_as_mtp_send_object_complete_data send_obj_data; + cy_as_mtp_get_object_complete_data get_obj_data; + cy_as_dma_end_point *ep_p; + + uint8_t code = cy_as_ll_request_response__get_code(req_p); + + (void)resp_p; + (void)context; + (void)ret; + + switch (code) { + case CY_RQT_MTP_EVENT: + val = cy_as_ll_request_response__get_word(req_p, 0); + /* MSB indicates status of read/write */ + status = (val >> 8) & 0xFF; + /* event type */ + ev = val & 0xFF; + switch (ev) { + case 0: /* SendObject Complete */ + { + bytecount_l = + cy_as_ll_request_response__get_word + (req_p, 1); + bytecount_h = + cy_as_ll_request_response__get_word + (req_p, 2); + send_obj_data.byte_count = + (bytecount_h << 16) | bytecount_l; + + send_obj_data.status = status; + + /* use the byte count again */ + bytecount_l = + cy_as_ll_request_response__get_word + (req_p, 3); + bytecount_h = + cy_as_ll_request_response__get_word + (req_p, 4); + send_obj_data.transaction_id = + (bytecount_h << 16) | bytecount_l; + + dev_p->mtp_turbo_active = cy_false; + + if (dev_p->mtp_event_cb) + dev_p->mtp_event_cb( + (cy_as_device_handle) dev_p, + cy_as_mtp_send_object_complete, + &send_obj_data); + } + break; + + case 1: /* GetObject Complete */ + { + bytecount_l = + cy_as_ll_request_response__get_word + (req_p, 1); + bytecount_h = + cy_as_ll_request_response__get_word + (req_p, 2); + + get_obj_data.byte_count = + (bytecount_h << 16) | bytecount_l; + + get_obj_data.status = status; + + dev_p->mtp_turbo_active = cy_false; + + if (dev_p->mtp_event_cb) + dev_p->mtp_event_cb( + (cy_as_device_handle) dev_p, + cy_as_mtp_get_object_complete, + &get_obj_data); + } + break; + + case 2: /* BlockTable Needed */ + { + if (dev_p->mtp_event_cb) + dev_p->mtp_event_cb( + (cy_as_device_handle) dev_p, + cy_as_mtp_block_table_needed, 0); + } + break; + default: + cy_as_hal_print_message("invalid event type\n"); + cy_as_ll_send_data_response(dev_p, + CY_RQT_TUR_RQT_CONTEXT, + CY_RESP_MTP_INVALID_EVENT, + sizeof(ev), &ev); + break; + } + break; + + case CY_RQT_TURBO_CMD_FROM_HOST: + { + mtp_datalen = + cy_as_ll_request_response__get_word(req_p, 1); + + /* Get the endpoint pointer based on + * the endpoint number */ + ep_p = CY_AS_NUM_EP(dev_p, CY_AS_MTP_READ_ENDPOINT); + + /* The event should arrive only after the DMA operation + * has been queued. */ + cy_as_hal_assert(ep_p->queue_p != 0); + + /* Put the len in ep data information in + * dmaqueue and kick start the queue */ + cy_as_hal_assert(ep_p->queue_p->size >= mtp_datalen); + + if (mtp_datalen == 0) { + cy_as_dma_completed_callback(dev_p->tag, + CY_AS_MTP_READ_ENDPOINT, 0, + CY_AS_ERROR_SUCCESS); + } else { + ep_p->maxhwdata = mtp_datalen; + + /* + * make sure that the DMA status for this + * EP is not running, so that the call to + * cy_as_dma_kick_start gets this transfer + * going. note: in MTP mode, we never leave + * a DMA transfer of greater than one packet + * running. so, it is okay to override the + * status here and start the next packet + * transfer. + */ + cy_as_dma_end_point_set_stopped(ep_p); + + /* Kick start the queue if it is not running */ + cy_as_dma_kick_start(dev_p, + CY_AS_MTP_READ_ENDPOINT); + } + } + break; + + case CY_RQT_TURBO_START_WRITE_DMA: + { + /* + * now that the firmware is ready to receive the + * next packet of data, start the corresponding + * DMA transfer. first, ensure that a DMA + * operation is still pending in the queue for the + * write endpoint. + */ + cy_as_ll_send_status_response(dev_p, + CY_RQT_TUR_RQT_CONTEXT, + CY_AS_ERROR_SUCCESS, 0); + + ep_p = CY_AS_NUM_EP(dev_p, CY_AS_MTP_WRITE_ENDPOINT); + cy_as_hal_assert(ep_p->queue_p != 0); + + cy_as_dma_end_point_set_stopped(ep_p); + cy_as_dma_kick_start(dev_p, CY_AS_MTP_WRITE_ENDPOINT); + } + break; + + default: + cy_as_hal_print_message("invalid request received " + "on TUR context\n"); + val = req_p->box0; + cy_as_ll_send_data_response(dev_p, CY_RQT_TUR_RQT_CONTEXT, + CY_RESP_INVALID_REQUEST, sizeof(val), &val); + break; + } +} + +static cy_as_return_status_t +my_handle_response_no_data(cy_as_device *dev_p, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *reply_p) +{ + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_SUCCESS_FAILURE) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + ret = cy_as_ll_request_response__get_word(reply_p, 0); + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +static cy_as_return_status_t +my_handle_response_mtp_start(cy_as_device *dev_p, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *reply_p, + cy_as_return_status_t ret) +{ + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_SUCCESS_FAILURE) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + ret = cy_as_ll_request_response__get_word(reply_p, 0); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + dev_p->mtp_count++; + + cy_as_dma_enable_end_point(dev_p, CY_AS_MTP_READ_ENDPOINT, + cy_true, cy_as_direction_out); + dev_p->usb_config[CY_AS_MTP_READ_ENDPOINT].enabled = cy_true; + dev_p->usb_config[CY_AS_MTP_READ_ENDPOINT].dir = cy_as_usb_out; + dev_p->usb_config[CY_AS_MTP_READ_ENDPOINT].type = cy_as_usb_bulk; + + cy_as_dma_enable_end_point(dev_p, CY_AS_MTP_WRITE_ENDPOINT, + cy_true, cy_as_direction_in); + dev_p->usb_config[CY_AS_MTP_WRITE_ENDPOINT].enabled = cy_true; + dev_p->usb_config[CY_AS_MTP_WRITE_ENDPOINT].dir = cy_as_usb_in; + dev_p->usb_config[CY_AS_MTP_WRITE_ENDPOINT].type = cy_as_usb_bulk; + + /* Packet size is 512 bytes */ + cy_as_dma_set_max_dma_size(dev_p, 0x02, 0x0200); + /* Packet size is 64 bytes until a switch to high speed happens.*/ + cy_as_dma_set_max_dma_size(dev_p, 0x06, 0x40); + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + if (ret != CY_AS_ERROR_SUCCESS) + cy_as_ll_register_request_callback(dev_p, + CY_RQT_TUR_RQT_CONTEXT, 0); + + cy_as_device_clear_m_s_s_pending(dev_p); + + return ret; +} + + +cy_as_return_status_t +cy_as_mtp_start(cy_as_device_handle handle, + cy_as_mtp_event_callback event_c_b, + cy_as_function_callback cb, + uint32_t client + ) +{ + cy_as_ll_request_response *req_p, *reply_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + cy_as_device *dev_p; + + dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + if (!cy_as_device_is_configured(dev_p)) + return CY_AS_ERROR_NOT_CONFIGURED; + + if (!cy_as_device_is_firmware_loaded(dev_p)) + return CY_AS_ERROR_NO_FIRMWARE; + + if (cy_as_device_is_in_suspend_mode(dev_p)) + return CY_AS_ERROR_IN_SUSPEND; + + if (cy_as_device_is_in_callback(dev_p)) + return CY_AS_ERROR_INVALID_IN_CALLBACK; + + if (cy_as_device_is_m_s_s_pending(dev_p)) + return CY_AS_ERROR_STARTSTOP_PENDING; + + if (dev_p->storage_count == 0) + return CY_AS_ERROR_NOT_RUNNING; + + if (dev_p->usb_count == 0) + return CY_AS_ERROR_NOT_RUNNING; + + if (dev_p->is_mtp_firmware == 0) + return CY_AS_ERROR_NOT_SUPPORTED; + + cy_as_device_set_m_s_s_pending(dev_p); + + if (dev_p->mtp_count == 0) { + + dev_p->mtp_event_cb = event_c_b; + /* + * we register here becuase the start request may cause + * events to occur before the response to the start request. + */ + cy_as_ll_register_request_callback(dev_p, + CY_RQT_TUR_RQT_CONTEXT, my_mtp_request_callback); + + /* Create the request to send to the West Bridge device */ + req_p = cy_as_ll_create_request(dev_p, + CY_RQT_START_MTP, CY_RQT_TUR_RQT_CONTEXT, 0); + if (req_p == 0) { + cy_as_device_clear_m_s_s_pending(dev_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + /* Reserve space for the reply, the reply data will + * not exceed one word */ + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_device_clear_m_s_s_pending(dev_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, + req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return my_handle_response_mtp_start(dev_p, req_p, + reply_p, ret); + } else { + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_MTP_START, 0, dev_p->func_cbs_mtp, + CY_AS_REQUEST_RESPONSE_EX, req_p, reply_p, + cy_as_mtp_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + } else { + dev_p->mtp_count++; + if (cb) + cb(handle, ret, client, CY_FUNCT_CB_MTP_START, 0); + } + + cy_as_device_clear_m_s_s_pending(dev_p); + + return ret; +} + +static cy_as_return_status_t +my_handle_response_mtp_stop(cy_as_device *dev_p, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *reply_p, + cy_as_return_status_t ret) +{ + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_SUCCESS_FAILURE) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + ret = cy_as_ll_request_response__get_word(reply_p, 0); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + /* + * we sucessfully shutdown the stack, so decrement + * to make the count zero. + */ + dev_p->mtp_count--; + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + if (ret != CY_AS_ERROR_SUCCESS) + cy_as_ll_register_request_callback(dev_p, + CY_RQT_TUR_RQT_CONTEXT, 0); + + cy_as_device_clear_m_s_s_pending(dev_p); + + return ret; +} + +cy_as_return_status_t +cy_as_mtp_stop(cy_as_device_handle handle, + cy_as_function_callback cb, + uint32_t client + ) +{ + cy_as_ll_request_response *req_p = 0, *reply_p = 0; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + + cy_as_device *dev_p; + + cy_as_log_debug_message(6, "cy_as_mtp_stop called"); + + dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ret = is_mtp_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (cy_as_device_is_in_callback(dev_p)) + return CY_AS_ERROR_INVALID_IN_CALLBACK; + + if (cy_as_device_is_m_s_s_pending(dev_p)) + return CY_AS_ERROR_STARTSTOP_PENDING; + + cy_as_device_set_m_s_s_pending(dev_p); + + if (dev_p->mtp_count == 1) { + /* Create the request to send to the West + * Bridge device */ + req_p = cy_as_ll_create_request(dev_p, CY_RQT_STOP_MTP, + CY_RQT_TUR_RQT_CONTEXT, 0); + if (req_p == 0) { + ret = CY_AS_ERROR_OUT_OF_MEMORY; + goto destroy; + } + + /* Reserve space for the reply, the reply data will + * not exceed one word */ + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + ret = CY_AS_ERROR_OUT_OF_MEMORY; + goto destroy; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, + req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return my_handle_response_mtp_stop(dev_p, req_p, + reply_p, ret); + } else { + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_MTP_STOP, 0, dev_p->func_cbs_mtp, + CY_AS_REQUEST_RESPONSE_EX, req_p, reply_p, + cy_as_mtp_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + } else if (dev_p->mtp_count > 1) { + + dev_p->mtp_count--; + + if (cb) + cb(handle, ret, client, CY_FUNCT_CB_MTP_STOP, 0); + } + + cy_as_device_clear_m_s_s_pending(dev_p); + + return ret; +} + +static void +mtp_write_callback( + cy_as_device *dev_p, + uint8_t context, + cy_as_ll_request_response *rqt, + cy_as_ll_request_response *resp, + cy_as_return_status_t ret) +{ + cy_as_hal_assert(context == CY_RQT_TUR_RQT_CONTEXT); + + if (ret == CY_AS_ERROR_SUCCESS) { + if (cy_as_ll_request_response__get_code(resp) != + CY_RESP_SUCCESS_FAILURE) + ret = CY_AS_ERROR_INVALID_RESPONSE; + else + ret = cy_as_ll_request_response__get_word(resp, 0); + } + + if (ret != CY_AS_ERROR_SUCCESS) { + /* Firmware failed the request. Cancel the DMA transfer. */ + cy_as_dma_cancel(dev_p, 0x04, CY_AS_ERROR_CANCELED); + cy_as_device_clear_storage_async_pending(dev_p); + } + + cy_as_ll_destroy_response(dev_p, resp); + cy_as_ll_destroy_request(dev_p, rqt); +} + +static void +async_write_request_callback(cy_as_device *dev_p, + cy_as_end_point_number_t ep, void *buf_p, uint32_t size, + cy_as_return_status_t err) +{ + cy_as_device_handle h; + cy_as_function_callback cb; + + (void)size; + (void)buf_p; + (void)ep; + + + cy_as_log_debug_message(6, "async_write_request_callback called"); + + h = (cy_as_device_handle)dev_p; + + cb = dev_p->mtp_cb; + dev_p->mtp_cb = 0; + + cy_as_device_clear_storage_async_pending(dev_p); + + if (cb) + cb(h, err, dev_p->mtp_client, dev_p->mtp_op, 0); + +} + +static void +sync_mtp_callback(cy_as_device *dev_p, cy_as_end_point_number_t ep, + void *buf_p, uint32_t size, cy_as_return_status_t err) +{ + (void)ep; + (void)buf_p; + (void)size; + + dev_p->mtp_error = err; +} + +static cy_as_return_status_t +cy_as_mtp_operation(cy_as_device *dev_p, + cy_as_mtp_block_table *blk_table, + uint32_t num_bytes, + uint32_t transaction_id, + cy_as_function_callback cb, + uint32_t client, + uint8_t rqttype + ) +{ + cy_as_ll_request_response *req_p = 0, *reply_p = 0; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + uint32_t mask = 0; + cy_as_funct_c_b_type mtp_cb_op = 0; + uint16_t size = 2; + + if (dev_p->mtp_count == 0) + return CY_AS_ERROR_NOT_RUNNING; + + if (rqttype == CY_RQT_INIT_SEND_OBJECT) { + mtp_cb_op = CY_FUNCT_CB_MTP_INIT_SEND_OBJECT; + dev_p->mtp_turbo_active = cy_true; + } else if (rqttype == CY_RQT_INIT_GET_OBJECT) { + mtp_cb_op = CY_FUNCT_CB_MTP_INIT_GET_OBJECT; + dev_p->mtp_turbo_active = cy_true; + } else + mtp_cb_op = CY_FUNCT_CB_MTP_SEND_BLOCK_TABLE; + + ret = is_mtp_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (CY_RQT_INIT_GET_OBJECT == rqttype) + size = 4; + + /* Create the request to send to the West + * Bridge device */ + req_p = cy_as_ll_create_request(dev_p, rqttype, + CY_RQT_TUR_RQT_CONTEXT, size); + if (req_p == 0) { + ret = CY_AS_ERROR_OUT_OF_MEMORY; + goto destroy; + } + + /* Reserve space for the reply, the reply data will + * not exceed one word */ + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + ret = CY_AS_ERROR_OUT_OF_MEMORY; + goto destroy; + } + + cy_as_ll_request_response__set_word(req_p, 0, + (uint16_t)(num_bytes & 0xFFFF)); + cy_as_ll_request_response__set_word(req_p, 1, + (uint16_t)((num_bytes >> 16) & 0xFFFF)); + + /* If it is GET_OBJECT, send transaction id as well*/ + if (CY_RQT_INIT_GET_OBJECT == rqttype) { + cy_as_ll_request_response__set_word(req_p, 2, + (uint16_t)(transaction_id & 0xFFFF)); + cy_as_ll_request_response__set_word(req_p, 3, + (uint16_t)((transaction_id >> 16) & 0xFFFF)); + } + + if (cb == 0) { + /* Queue the DMA request for block table write */ + ret = cy_as_dma_queue_request(dev_p, 4, blk_table, + sizeof(cy_as_mtp_block_table), cy_false, + cy_false, sync_mtp_callback); + + ret = cy_as_ll_send_request_wait_reply(dev_p, + req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) { + cy_as_dma_cancel(dev_p, 4, CY_AS_ERROR_CANCELED); + cy_as_device_clear_storage_async_pending(dev_p); + + goto destroy; + } + + ret = cy_as_dma_drain_queue(dev_p, 4, cy_true); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + ret = dev_p->mtp_error; + goto destroy; + } else { +#if 0 + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_MTP_INIT_SEND_OBJECT, + 0, dev_p->func_cbs_mtp, CY_AS_REQUEST_RESPONSE_EX, + req_p, reply_p, cy_as_mtp_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; +#endif + + /* Protection from interrupt driven code */ + /* since we are using storage EP4 check if any + * storage activity is pending */ + mask = cy_as_hal_disable_interrupts(); + if ((cy_as_device_is_storage_async_pending(dev_p)) || + (dev_p->storage_wait)) { + cy_as_hal_enable_interrupts(mask); + return CY_AS_ERROR_ASYNC_PENDING; + } + cy_as_device_set_storage_async_pending(dev_p); + cy_as_hal_enable_interrupts(mask); + + dev_p->mtp_cb = cb; + dev_p->mtp_client = client; + dev_p->mtp_op = mtp_cb_op; + + ret = cy_as_ll_send_request(dev_p, req_p, reply_p, + cy_false, mtp_write_callback); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + ret = cy_as_dma_queue_request(dev_p, 4, blk_table, + sizeof(cy_as_mtp_block_table), cy_false, cy_false, + async_write_request_callback); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + /* Kick start the queue if it is not running */ + cy_as_dma_kick_start(dev_p, 4); + + return CY_AS_ERROR_SUCCESS; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +cy_as_return_status_t +cy_as_mtp_init_send_object(cy_as_device_handle handle, + cy_as_mtp_block_table *blk_table, + uint32_t num_bytes, + cy_as_function_callback cb, + uint32_t client + ) +{ + cy_as_device *dev_p; + dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + return cy_as_mtp_operation(dev_p, blk_table, num_bytes, 0, cb, + client, CY_RQT_INIT_SEND_OBJECT); + +} + +cy_as_return_status_t +cy_as_mtp_init_get_object(cy_as_device_handle handle, + cy_as_mtp_block_table *blk_table, + uint32_t num_bytes, + uint32_t transaction_id, + cy_as_function_callback cb, + uint32_t client + ) +{ + cy_as_device *dev_p; + dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + return cy_as_mtp_operation(dev_p, blk_table, num_bytes, + transaction_id, cb, client, CY_RQT_INIT_GET_OBJECT); + +} + +static cy_as_return_status_t +my_handle_response_cancel_send_object(cy_as_device *dev_p, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *reply_p, + cy_as_return_status_t ret) +{ + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_SUCCESS_FAILURE) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + ret = cy_as_ll_request_response__get_word(reply_p, 0); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +cy_as_return_status_t +cy_as_mtp_cancel_send_object(cy_as_device_handle handle, + cy_as_function_callback cb, + uint32_t client + ) +{ + cy_as_ll_request_response *req_p = 0, *reply_p = 0; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + cy_as_device *dev_p; + + dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + if (dev_p->mtp_count == 0) + return CY_AS_ERROR_NOT_RUNNING; + + /* Create the request to send to the West Bridge device */ + req_p = cy_as_ll_create_request(dev_p, + CY_RQT_CANCEL_SEND_OBJECT, CY_RQT_TUR_RQT_CONTEXT, 0); + if (req_p == 0) { + ret = CY_AS_ERROR_OUT_OF_MEMORY; + goto destroy; + } + + /* Reserve space for the reply, the reply data will + * not exceed one word */ + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + ret = CY_AS_ERROR_OUT_OF_MEMORY; + goto destroy; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, + req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return my_handle_response_cancel_send_object(dev_p, + req_p, reply_p, ret); + } else { + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_MTP_CANCEL_SEND_OBJECT, 0, + dev_p->func_cbs_mtp, CY_AS_REQUEST_RESPONSE_EX, + req_p, reply_p, cy_as_mtp_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +static cy_as_return_status_t +my_handle_response_cancel_get_object(cy_as_device *dev_p, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *reply_p, + cy_as_return_status_t ret) +{ + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_SUCCESS_FAILURE) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + ret = cy_as_ll_request_response__get_word(reply_p, 0); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +cy_as_return_status_t +cy_as_mtp_cancel_get_object(cy_as_device_handle handle, + cy_as_function_callback cb, + uint32_t client + ) +{ + cy_as_ll_request_response *req_p = 0, *reply_p = 0; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + cy_as_device *dev_p; + + dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + if (dev_p->mtp_count == 0) + return CY_AS_ERROR_NOT_RUNNING; + + /* Create the request to send to the West Bridge device */ + req_p = cy_as_ll_create_request(dev_p, CY_RQT_CANCEL_GET_OBJECT, + CY_RQT_TUR_RQT_CONTEXT, 0); + if (req_p == 0) { + ret = CY_AS_ERROR_OUT_OF_MEMORY; + goto destroy; + } + + /* Reserve space for the reply, the reply data will + * not exceed one word */ + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + ret = CY_AS_ERROR_OUT_OF_MEMORY; + goto destroy; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, + req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return my_handle_response_cancel_get_object(dev_p, + req_p, reply_p, ret); + } else { + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_MTP_CANCEL_GET_OBJECT, 0, + dev_p->func_cbs_mtp, CY_AS_REQUEST_RESPONSE_EX, + req_p, reply_p, cy_as_mtp_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +cy_as_return_status_t +cy_as_mtp_send_block_table(cy_as_device_handle handle, + cy_as_mtp_block_table *blk_table, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_device *dev_p; + dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + return cy_as_mtp_operation(dev_p, blk_table, 0, 0, cb, + client, CY_RQT_SEND_BLOCK_TABLE); +} + +static void +cy_as_mtp_func_callback(cy_as_device *dev_p, + uint8_t context, + cy_as_ll_request_response *rqt, + cy_as_ll_request_response *resp, + cy_as_return_status_t stat) +{ + cy_as_func_c_b_node* node = (cy_as_func_c_b_node *) + dev_p->func_cbs_mtp->head_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + uint8_t code; + cy_bool delay_callback = cy_false; + + cy_as_hal_assert(dev_p->func_cbs_mtp->count != 0); + cy_as_hal_assert(dev_p->func_cbs_mtp->type == CYAS_FUNC_CB); + + (void)context; + + /* The Handlers are responsible for Deleting the + * rqt and resp when they are finished + */ + code = cy_as_ll_request_response__get_code(rqt); + switch (code) { + case CY_RQT_START_MTP: + ret = my_handle_response_mtp_start(dev_p, rqt, + resp, stat); + break; + case CY_RQT_STOP_MTP: + ret = my_handle_response_mtp_stop(dev_p, rqt, + resp, stat); + break; +#if 0 + case CY_RQT_INIT_SEND_OBJECT: + ret = my_handle_response_init_send_object(dev_p, + rqt, resp, stat, cy_true); + delay_callback = cy_true; + break; +#endif + case CY_RQT_CANCEL_SEND_OBJECT: + ret = my_handle_response_cancel_send_object(dev_p, + rqt, resp, stat); + break; +#if 0 + case CY_RQT_INIT_GET_OBJECT: + ret = my_handle_response_init_get_object(dev_p, + rqt, resp, stat, cy_true); + delay_callback = cy_true; + break; +#endif + case CY_RQT_CANCEL_GET_OBJECT: + ret = my_handle_response_cancel_get_object(dev_p, + rqt, resp, stat); + break; +#if 0 + case CY_RQT_SEND_BLOCK_TABLE: + ret = my_handle_response_send_block_table(dev_p, rqt, + resp, stat, cy_true); + delay_callback = cy_true; + break; +#endif + case CY_RQT_ENABLE_USB_PATH: + ret = my_handle_response_no_data(dev_p, rqt, resp); + if (ret == CY_AS_ERROR_SUCCESS) + dev_p->is_storage_only_mode = cy_false; + break; + default: + ret = CY_AS_ERROR_INVALID_RESPONSE; + cy_as_hal_assert(cy_false); + break; + } + + /* + * if the low level layer returns a direct error, use the + * corresponding error code. if not, use the error code + * based on the response from firmware. + */ + if (stat == CY_AS_ERROR_SUCCESS) + stat = ret; + + if (!delay_callback) { + node->cb_p((cy_as_device_handle)dev_p, stat, node->client_data, + node->data_type, node->data); + cy_as_remove_c_b_node(dev_p->func_cbs_mtp); + } +} + +cy_as_return_status_t +cy_as_mtp_storage_only_start(cy_as_device_handle handle) +{ + cy_as_device *dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + if (!cy_as_device_is_configured(dev_p)) + return CY_AS_ERROR_NOT_CONFIGURED; + + if (!cy_as_device_is_firmware_loaded(dev_p)) + return CY_AS_ERROR_NO_FIRMWARE; + + if (dev_p->storage_count == 0) + return CY_AS_ERROR_NOT_RUNNING; + + dev_p->is_storage_only_mode = cy_true; + return CY_AS_ERROR_SUCCESS; +} + +cy_as_return_status_t +cy_as_mtp_storage_only_stop(cy_as_device_handle handle, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_device *dev_p = (cy_as_device *)handle; + cy_as_ll_request_response *req_p, *reply_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + if (!cy_as_device_is_configured(dev_p)) + return CY_AS_ERROR_NOT_CONFIGURED; + + if (!cy_as_device_is_firmware_loaded(dev_p)) + return CY_AS_ERROR_NO_FIRMWARE; + + if (dev_p->storage_count == 0) + return CY_AS_ERROR_NOT_RUNNING; + + if (dev_p->is_storage_only_mode == cy_false) + return CY_AS_ERROR_SUCCESS; + + if (cy_as_device_is_in_callback(dev_p)) + return CY_AS_ERROR_INVALID_IN_CALLBACK; + + req_p = cy_as_ll_create_request(dev_p, + CY_RQT_ENABLE_USB_PATH, CY_RQT_TUR_RQT_CONTEXT, 1); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, + req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + ret = my_handle_response_no_data(dev_p, req_p, + reply_p); + if (ret == CY_AS_ERROR_SUCCESS) + dev_p->is_storage_only_mode = cy_false; + return ret; + } else { + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_MTP_STOP_STORAGE_ONLY, 0, + dev_p->func_cbs_mtp, CY_AS_REQUEST_RESPONSE_EX, + req_p, reply_p, cy_as_mtp_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} diff --git a/drivers/staging/westbridge/astoria/api/src/cyasstorage.c b/drivers/staging/westbridge/astoria/api/src/cyasstorage.c new file mode 100644 index 000000000000..083d869e57c6 --- /dev/null +++ b/drivers/staging/westbridge/astoria/api/src/cyasstorage.c @@ -0,0 +1,4104 @@ +/* Cypress West Bridge API source file (cyasstorage.c) +## =========================== +## Copyright (C) 2010 Cypress Semiconductor +## +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either version 2 +## of the License, or (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 51 Franklin Street, Fifth Floor +## Boston, MA 02110-1301, USA. +## =========================== +*/ + +/* +* Storage Design +* +* The storage module is fairly straight forward once the +* DMA and LOWLEVEL modules have been designed. The +* storage module simple takes requests from the user, queues +* the associated DMA requests for action, and then sends +* the low level requests to the West Bridge firmware. +* +*/ + +#include "../../include/linux/westbridge/cyashal.h" +#include "../../include/linux/westbridge/cyasstorage.h" +#include "../../include/linux/westbridge/cyaserr.h" +#include "../../include/linux/westbridge/cyasdevice.h" +#include "../../include/linux/westbridge/cyaslowlevel.h" +#include "../../include/linux/westbridge/cyasdma.h" +#include "../../include/linux/westbridge/cyasregs.h" + +/* Map a pre-V1.2 media type to the V1.2+ bus number */ +cy_as_return_status_t +cy_an_map_bus_from_media_type(cy_as_device *dev_p, + cy_as_media_type type, cy_as_bus_number_t *bus) +{ + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + uint8_t code = (uint8_t)(1 << type); + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + if (!cy_as_device_is_configured(dev_p)) + return CY_AS_ERROR_NOT_CONFIGURED; + + if (!cy_as_device_is_firmware_loaded(dev_p)) + return CY_AS_ERROR_NO_FIRMWARE; + + + if (dev_p->media_supported[0] & code) { + if (dev_p->media_supported[1] & code) { + /* + * this media type could be supported on multiple + * buses. so, report an address resolution error. + */ + ret = CY_AS_ERROR_ADDRESS_RESOLUTION_ERROR; + } else + *bus = 0; + } else { + if (dev_p->media_supported[1] & code) + *bus = 1; + else + ret = CY_AS_ERROR_NO_SUCH_MEDIA; + } + + return ret; +} + +static uint16_t +create_address(cy_as_bus_number_t bus, uint32_t device, uint8_t unit) +{ + cy_as_hal_assert(bus >= 0 && bus < CY_AS_MAX_BUSES); + cy_as_hal_assert(device < 16); + + return (uint16_t)(((uint8_t)bus << 12) | (device << 8) | unit); +} + +cy_as_media_type +cy_as_storage_get_media_from_address(uint16_t v) +{ + cy_as_media_type media = cy_as_media_max_media_value; + + switch (v & 0xFF) { + case 0x00: + break; + case 0x01: + media = cy_as_media_nand; + break; + case 0x02: + media = cy_as_media_sd_flash; + break; + case 0x04: + media = cy_as_media_mmc_flash; + break; + case 0x08: + media = cy_as_media_ce_ata; + break; + case 0x10: + media = cy_as_media_sdio; + break; + default: + cy_as_hal_assert(0); + break; + } + + return media; +} + +cy_as_bus_number_t +cy_as_storage_get_bus_from_address(uint16_t v) +{ + cy_as_bus_number_t bus = (cy_as_bus_number_t)((v >> 12) & 0x0f); + cy_as_hal_assert(bus >= 0 && bus < CY_AS_MAX_BUSES); + return bus; +} + +uint32_t +cy_as_storage_get_device_from_address(uint16_t v) +{ + return (uint32_t)((v >> 8) & 0x0f); +} + +static uint8_t +get_unit_from_address(uint16_t v) +{ + return (uint8_t)(v & 0xff); +} + +static cy_as_return_status_t +cy_as_map_bad_addr(uint16_t val) +{ + cy_as_return_status_t ret = CY_AS_ERROR_INVALID_RESPONSE; + + switch (val) { + case 0: + ret = CY_AS_ERROR_NO_SUCH_BUS; + break; + case 1: + ret = CY_AS_ERROR_NO_SUCH_DEVICE; + break; + case 2: + ret = CY_AS_ERROR_NO_SUCH_UNIT; + break; + case 3: + ret = CY_AS_ERROR_INVALID_BLOCK; + break; + } + + return ret; +} + +static void +my_storage_request_callback(cy_as_device *dev_p, + uint8_t context, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *resp_p, + cy_as_return_status_t ret) +{ + uint16_t val; + uint16_t addr; + cy_as_bus_number_t bus; + uint32_t device; + cy_as_device_handle h = (cy_as_device_handle)dev_p; + cy_as_dma_end_point *ep_p = NULL; + + (void)resp_p; + (void)context; + (void)ret; + + switch (cy_as_ll_request_response__get_code(req_p)) { + case CY_RQT_MEDIA_CHANGED: + cy_as_ll_send_status_response(dev_p, + CY_RQT_STORAGE_RQT_CONTEXT, CY_AS_ERROR_SUCCESS, 0); + + /* Media has either been inserted or removed */ + addr = cy_as_ll_request_response__get_word(req_p, 0); + + bus = cy_as_storage_get_bus_from_address(addr); + device = cy_as_storage_get_device_from_address(addr); + + /* Clear the entry for this device to force re-query later */ + cy_as_hal_mem_set(&(dev_p->storage_device_info[bus][device]), 0, + sizeof(dev_p->storage_device_info[bus][device])); + + val = cy_as_ll_request_response__get_word(req_p, 1); + if (dev_p->storage_event_cb_ms) { + if (val == 1) + dev_p->storage_event_cb_ms(h, bus, + device, cy_as_storage_removed, 0); + else + dev_p->storage_event_cb_ms(h, bus, + device, cy_as_storage_inserted, 0); + } else if (dev_p->storage_event_cb) { + if (val == 1) + dev_p->storage_event_cb(h, bus, + cy_as_storage_removed, 0); + else + dev_p->storage_event_cb(h, bus, + cy_as_storage_inserted, 0); + } + + break; + + case CY_RQT_ANTIOCH_CLAIM: + cy_as_ll_send_status_response(dev_p, + CY_RQT_STORAGE_RQT_CONTEXT, CY_AS_ERROR_SUCCESS, 0); + if (dev_p->storage_event_cb || dev_p->storage_event_cb_ms) { + val = cy_as_ll_request_response__get_word(req_p, 0); + if (dev_p->storage_event_cb_ms) { + if (val & 0x0100) + dev_p->storage_event_cb_ms(h, 0, 0, + cy_as_storage_antioch, 0); + if (val & 0x0200) + dev_p->storage_event_cb_ms(h, 1, 0, + cy_as_storage_antioch, 0); + } else { + if (val & 0x01) + dev_p->storage_event_cb(h, + cy_as_media_nand, + cy_as_storage_antioch, 0); + if (val & 0x02) + dev_p->storage_event_cb(h, + cy_as_media_sd_flash, + cy_as_storage_antioch, 0); + if (val & 0x04) + dev_p->storage_event_cb(h, + cy_as_media_mmc_flash, + cy_as_storage_antioch, 0); + if (val & 0x08) + dev_p->storage_event_cb(h, + cy_as_media_ce_ata, + cy_as_storage_antioch, 0); + } + } + break; + + case CY_RQT_ANTIOCH_RELEASE: + cy_as_ll_send_status_response(dev_p, + CY_RQT_STORAGE_RQT_CONTEXT, CY_AS_ERROR_SUCCESS, 0); + val = cy_as_ll_request_response__get_word(req_p, 0); + if (dev_p->storage_event_cb_ms) { + if (val & 0x0100) + dev_p->storage_event_cb_ms(h, 0, 0, + cy_as_storage_processor, 0); + if (val & 0x0200) + dev_p->storage_event_cb_ms(h, 1, 0, + cy_as_storage_processor, 0); + } else if (dev_p->storage_event_cb) { + if (val & 0x01) + dev_p->storage_event_cb(h, + cy_as_media_nand, + cy_as_storage_processor, 0); + if (val & 0x02) + dev_p->storage_event_cb(h, + cy_as_media_sd_flash, + cy_as_storage_processor, 0); + if (val & 0x04) + dev_p->storage_event_cb(h, + cy_as_media_mmc_flash, + cy_as_storage_processor, 0); + if (val & 0x08) + dev_p->storage_event_cb(h, + cy_as_media_ce_ata, + cy_as_storage_processor, 0); + } + break; + + + case CY_RQT_SDIO_INTR: + cy_as_ll_send_status_response(dev_p, + CY_RQT_STORAGE_RQT_CONTEXT, CY_AS_ERROR_SUCCESS, 0); + val = cy_as_ll_request_response__get_word(req_p, 0); + if (dev_p->storage_event_cb_ms) { + if (val & 0x0100) + dev_p->storage_event_cb_ms(h, 1, 0, + cy_as_sdio_interrupt, 0); + else + dev_p->storage_event_cb_ms(h, 0, 0, + cy_as_sdio_interrupt, 0); + + } else if (dev_p->storage_event_cb) { + dev_p->storage_event_cb(h, + cy_as_media_sdio, cy_as_sdio_interrupt, 0); + } + break; + + case CY_RQT_P2S_DMA_START: + /* Do the DMA setup for the waiting operation. */ + cy_as_ll_send_status_response(dev_p, + CY_RQT_STORAGE_RQT_CONTEXT, CY_AS_ERROR_SUCCESS, 0); + cy_as_device_set_p2s_dma_start_recvd(dev_p); + if (dev_p->storage_oper == cy_as_op_read) { + ep_p = CY_AS_NUM_EP(dev_p, CY_AS_P2S_READ_ENDPOINT); + cy_as_dma_end_point_set_stopped(ep_p); + cy_as_dma_kick_start(dev_p, CY_AS_P2S_READ_ENDPOINT); + } else { + ep_p = CY_AS_NUM_EP(dev_p, CY_AS_P2S_WRITE_ENDPOINT); + cy_as_dma_end_point_set_stopped(ep_p); + cy_as_dma_kick_start(dev_p, CY_AS_P2S_WRITE_ENDPOINT); + } + break; + + default: + cy_as_hal_print_message("invalid request received " + "on storage context\n"); + val = req_p->box0; + cy_as_ll_send_data_response(dev_p, CY_RQT_STORAGE_RQT_CONTEXT, + CY_RESP_INVALID_REQUEST, sizeof(val), &val); + break; + } +} + +static cy_as_return_status_t +is_storage_active(cy_as_device *dev_p) +{ + if (!cy_as_device_is_configured(dev_p)) + return CY_AS_ERROR_NOT_CONFIGURED; + + if (!cy_as_device_is_firmware_loaded(dev_p)) + return CY_AS_ERROR_NO_FIRMWARE; + + if (dev_p->storage_count == 0) + return CY_AS_ERROR_NOT_RUNNING; + + if (cy_as_device_is_in_suspend_mode(dev_p)) + return CY_AS_ERROR_IN_SUSPEND; + + return CY_AS_ERROR_SUCCESS; +} + +static void +cy_as_storage_func_callback(cy_as_device *dev_p, + uint8_t context, + cy_as_ll_request_response *rqt, + cy_as_ll_request_response *resp, + cy_as_return_status_t ret); + +static cy_as_return_status_t +my_handle_response_no_data(cy_as_device *dev_p, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *reply_p) +{ + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_SUCCESS_FAILURE) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + ret = cy_as_ll_request_response__get_word(reply_p, 0); + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +static cy_as_return_status_t +my_handle_response_storage_start(cy_as_device *dev_p, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *reply_p, + cy_as_return_status_t ret) +{ + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_SUCCESS_FAILURE) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + ret = cy_as_ll_request_response__get_word(reply_p, 0); + if (dev_p->storage_count > 0 && ret == + CY_AS_ERROR_ALREADY_RUNNING) + ret = CY_AS_ERROR_SUCCESS; + + ret = cy_as_dma_enable_end_point(dev_p, + CY_AS_P2S_WRITE_ENDPOINT, cy_true, cy_as_direction_in); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + ret = cy_as_dma_set_max_dma_size(dev_p, + CY_AS_P2S_WRITE_ENDPOINT, CY_AS_STORAGE_EP_SIZE); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + ret = cy_as_dma_enable_end_point(dev_p, + CY_AS_P2S_READ_ENDPOINT, cy_true, cy_as_direction_out); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + ret = cy_as_dma_set_max_dma_size(dev_p, + CY_AS_P2S_READ_ENDPOINT, CY_AS_STORAGE_EP_SIZE); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + cy_as_ll_register_request_callback(dev_p, + CY_RQT_STORAGE_RQT_CONTEXT, my_storage_request_callback); + + /* Create the request/response used for storage reads and writes. */ + dev_p->storage_rw_req_p = cy_as_ll_create_request(dev_p, + 0, CY_RQT_STORAGE_RQT_CONTEXT, 5); + if (dev_p->storage_rw_req_p == 0) { + ret = CY_AS_ERROR_OUT_OF_MEMORY; + goto destroy; + } + + dev_p->storage_rw_resp_p = cy_as_ll_create_response(dev_p, 5); + if (dev_p->storage_rw_resp_p == 0) { + cy_as_ll_destroy_request(dev_p, dev_p->storage_rw_req_p); + ret = CY_AS_ERROR_OUT_OF_MEMORY; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + /* Increment the storage count only if + * the above functionality succeeds.*/ + if (ret == CY_AS_ERROR_SUCCESS) { + if (dev_p->storage_count == 0) { + cy_as_hal_mem_set(dev_p->storage_device_info, + 0, sizeof(dev_p->storage_device_info)); + dev_p->is_storage_only_mode = cy_false; + } + + dev_p->storage_count++; + } + + cy_as_device_clear_s_s_s_pending(dev_p); + + return ret; +} + +cy_as_return_status_t +cy_as_storage_start(cy_as_device_handle handle, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_ll_request_response *req_p, *reply_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + cy_as_device *dev_p = (cy_as_device *)handle; + + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + if (!cy_as_device_is_configured(dev_p)) + return CY_AS_ERROR_NOT_CONFIGURED; + + if (!cy_as_device_is_firmware_loaded(dev_p)) + return CY_AS_ERROR_NO_FIRMWARE; + + if (cy_as_device_is_in_suspend_mode(dev_p)) + return CY_AS_ERROR_IN_SUSPEND; + + if (cy_as_device_is_s_s_s_pending(dev_p)) + return CY_AS_ERROR_STARTSTOP_PENDING; + + cy_as_device_set_s_s_s_pending(dev_p); + + if (dev_p->storage_count == 0) { + /* Create the request to send to the West Bridge device */ + req_p = cy_as_ll_create_request(dev_p, + CY_RQT_START_STORAGE, CY_RQT_STORAGE_RQT_CONTEXT, 1); + if (req_p == 0) { + cy_as_device_clear_s_s_s_pending(dev_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + /* Reserve space for the reply, the reply data + * will not exceed one word */ + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_device_clear_s_s_s_pending(dev_p); + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, + req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return my_handle_response_storage_start(dev_p, + req_p, reply_p, ret); + } else { + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_STOR_START, 0, dev_p->func_cbs_stor, + CY_AS_REQUEST_RESPONSE_EX, req_p, reply_p, + cy_as_storage_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + /* The request and response are freed as + * part of the FuncCallback */ + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + } else { + dev_p->storage_count++; + if (cb) + cb(handle, ret, client, CY_FUNCT_CB_STOR_START, 0); + } + + cy_as_device_clear_s_s_s_pending(dev_p); + + return ret; +} + + +static cy_as_return_status_t +my_handle_response_storage_stop(cy_as_device *dev_p, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *reply_p, + cy_as_return_status_t ret) +{ + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_SUCCESS_FAILURE) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + if (ret == CY_AS_ERROR_SUCCESS) { + cy_as_ll_destroy_request(dev_p, dev_p->storage_rw_req_p); + cy_as_ll_destroy_response(dev_p, dev_p->storage_rw_resp_p); + dev_p->storage_count--; + } + + cy_as_device_clear_s_s_s_pending(dev_p); + + return ret; +} +cy_as_return_status_t +cy_as_storage_stop(cy_as_device_handle handle, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_ll_request_response *req_p , *reply_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + + cy_as_device *dev_p = (cy_as_device *)handle; + + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ret = is_storage_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (cy_as_device_is_storage_async_pending(dev_p)) + return CY_AS_ERROR_ASYNC_PENDING; + + if (cy_as_device_is_s_s_s_pending(dev_p)) + return CY_AS_ERROR_STARTSTOP_PENDING; + + cy_as_device_set_s_s_s_pending(dev_p); + + if (dev_p->storage_count == 1) { + + /* Create the request to send to the West Bridge device */ + req_p = cy_as_ll_create_request(dev_p, + CY_RQT_STOP_STORAGE, CY_RQT_STORAGE_RQT_CONTEXT, 0); + if (req_p == 0) { + cy_as_device_clear_s_s_s_pending(dev_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + /* Reserve space for the reply, the reply data + * will not exceed one word */ + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_device_clear_s_s_s_pending(dev_p); + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, + req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return my_handle_response_storage_stop(dev_p, + req_p, reply_p, ret); + } else { + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_STOR_STOP, 0, dev_p->func_cbs_stor, + CY_AS_REQUEST_RESPONSE_EX, req_p, reply_p, + cy_as_storage_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + /* The request and response are freed + * as part of the MiscFuncCallback */ + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + } else if (dev_p->storage_count > 1) { + dev_p->storage_count--; + if (cb) + cb(handle, ret, client, CY_FUNCT_CB_STOR_STOP, 0); + } + + cy_as_device_clear_s_s_s_pending(dev_p); + + return ret; +} + +cy_as_return_status_t +cy_as_storage_register_callback(cy_as_device_handle handle, + cy_as_storage_event_callback callback) +{ + cy_as_device *dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + if (!cy_as_device_is_configured(dev_p)) + return CY_AS_ERROR_NOT_CONFIGURED; + + if (!cy_as_device_is_firmware_loaded(dev_p)) + return CY_AS_ERROR_NO_FIRMWARE; + + if (dev_p->storage_count == 0) + return CY_AS_ERROR_NOT_RUNNING; + + dev_p->storage_event_cb = NULL; + dev_p->storage_event_cb_ms = callback; + + return CY_AS_ERROR_SUCCESS; +} + + + +static cy_as_return_status_t +my_handle_response_storage_claim(cy_as_device *dev_p, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *reply_p) +{ + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + + if (cy_as_ll_request_response__get_code(reply_p) == + CY_RESP_NO_SUCH_ADDRESS) { + ret = cy_as_map_bad_addr( + cy_as_ll_request_response__get_word(reply_p, 3)); + goto destroy; + } + + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_MEDIA_CLAIMED_RELEASED) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + /* The response must be about the address I am + * trying to claim or the firmware is broken */ + if ((cy_as_storage_get_bus_from_address( + cy_as_ll_request_response__get_word(req_p, 0)) != + cy_as_storage_get_bus_from_address( + cy_as_ll_request_response__get_word(reply_p, 0))) || + (cy_as_storage_get_device_from_address( + cy_as_ll_request_response__get_word(req_p, 0)) != + cy_as_storage_get_device_from_address( + cy_as_ll_request_response__get_word(reply_p, 0)))) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + if (cy_as_ll_request_response__get_word(reply_p, 1) != 1) + ret = CY_AS_ERROR_NOT_ACQUIRED; + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +static cy_as_return_status_t +my_storage_claim(cy_as_device *dev_p, + void *data, + cy_as_bus_number_t bus, + uint32_t device, + uint16_t req_flags, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_ll_request_response *req_p , *reply_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ret = is_storage_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (dev_p->mtp_count > 0) + return CY_AS_ERROR_NOT_VALID_IN_MTP; + + /* Create the request to send to the West Bridge device */ + req_p = cy_as_ll_create_request(dev_p, + CY_RQT_CLAIM_STORAGE, CY_RQT_STORAGE_RQT_CONTEXT, 1); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + cy_as_ll_request_response__set_word(req_p, + 0, create_address(bus, device, 0)); + + /* Reserve space for the reply, the reply data will + * not exceed one word */ + reply_p = cy_as_ll_create_response(dev_p, 4); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return my_handle_response_storage_claim(dev_p, req_p, reply_p); + } else { + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_STOR_CLAIM, data, dev_p->func_cbs_stor, + req_flags, req_p, reply_p, + cy_as_storage_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + /* The request and response are freed as part of + * the MiscFuncCallback */ + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +cy_as_return_status_t +cy_as_storage_claim(cy_as_device_handle handle, + cy_as_bus_number_t bus, + uint32_t device, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_device *dev_p = (cy_as_device *)handle; + + if (bus < 0 || bus >= CY_AS_MAX_BUSES) + return CY_AS_ERROR_NO_SUCH_BUS; + + return my_storage_claim(dev_p, NULL, bus, device, + CY_AS_REQUEST_RESPONSE_MS, cb, client); +} + +static cy_as_return_status_t +my_handle_response_storage_release(cy_as_device *dev_p, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *reply_p) +{ + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + + if (cy_as_ll_request_response__get_code(reply_p) == + CY_RESP_NO_SUCH_ADDRESS) { + ret = cy_as_map_bad_addr( + cy_as_ll_request_response__get_word(reply_p, 3)); + goto destroy; + } + + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_MEDIA_CLAIMED_RELEASED) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + /* The response must be about the address I am + * trying to release or the firmware is broken */ + if ((cy_as_storage_get_bus_from_address( + cy_as_ll_request_response__get_word(req_p, 0)) != + cy_as_storage_get_bus_from_address( + cy_as_ll_request_response__get_word(reply_p, 0))) || + (cy_as_storage_get_device_from_address( + cy_as_ll_request_response__get_word(req_p, 0)) != + cy_as_storage_get_device_from_address( + cy_as_ll_request_response__get_word(reply_p, 0)))) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + + if (cy_as_ll_request_response__get_word(reply_p, 1) != 0) + ret = CY_AS_ERROR_NOT_RELEASED; + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +static cy_as_return_status_t +my_storage_release(cy_as_device *dev_p, + void *data, + cy_as_bus_number_t bus, + uint32_t device, + uint16_t req_flags, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_ll_request_response *req_p , *reply_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ret = is_storage_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (dev_p->mtp_count > 0) + return CY_AS_ERROR_NOT_VALID_IN_MTP; + + /* Create the request to send to the West Bridge device */ + req_p = cy_as_ll_create_request(dev_p, CY_RQT_RELEASE_STORAGE, + CY_RQT_STORAGE_RQT_CONTEXT, 1); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + cy_as_ll_request_response__set_word( + req_p, 0, create_address(bus, device, 0)); + + /* Reserve space for the reply, the reply + * data will not exceed one word */ + reply_p = cy_as_ll_create_response(dev_p, 4); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return my_handle_response_storage_release( + dev_p, req_p, reply_p); + } else { + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_STOR_RELEASE, data, dev_p->func_cbs_stor, + req_flags, req_p, reply_p, + cy_as_storage_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + /* The request and response are freed as + * part of the MiscFuncCallback */ + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +cy_as_return_status_t +cy_as_storage_release(cy_as_device_handle handle, + cy_as_bus_number_t bus, + uint32_t device, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_device *dev_p = (cy_as_device *)handle; + + if (bus < 0 || bus >= CY_AS_MAX_BUSES) + return CY_AS_ERROR_NO_SUCH_BUS; + + return my_storage_release(dev_p, NULL, bus, device, + CY_AS_REQUEST_RESPONSE_MS, cb, client); +} + +static cy_as_return_status_t +my_handle_response_storage_query_bus(cy_as_device *dev_p, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *reply_p, + uint32_t *count) +{ + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + uint8_t code = cy_as_ll_request_response__get_code(reply_p); + uint16_t v; + + if (code == CY_RESP_NO_SUCH_ADDRESS) { + ret = CY_AS_ERROR_NO_SUCH_BUS; + goto destroy; + } + + if (code != CY_RESP_BUS_DESCRIPTOR) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + /* + * verify that the response corresponds to the bus that was queried. + */ + if (cy_as_storage_get_bus_from_address( + cy_as_ll_request_response__get_word(req_p, 0)) != + cy_as_storage_get_bus_from_address( + cy_as_ll_request_response__get_word(reply_p, 0))) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + v = cy_as_ll_request_response__get_word(reply_p, 1); + if (req_p->flags & CY_AS_REQUEST_RESPONSE_MS) { + /* + * this request is only for the count of devices + * on the bus. there is no need to check the media type. + */ + if (v) + *count = 1; + else + *count = 0; + } else { + /* + * this request is for the count of devices of a + * particular type. we need to check whether the media + * type found matches the queried type. + */ + cy_as_media_type queried = (cy_as_media_type) + cy_as_ll_request_response__get_word(req_p, 1); + cy_as_media_type found = + cy_as_storage_get_media_from_address(v); + + if (queried == found) + *count = 1; + else + *count = 0; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +cy_as_return_status_t +my_storage_query_bus(cy_as_device *dev_p, + cy_as_bus_number_t bus, + cy_as_media_type type, + uint16_t req_flags, + uint32_t *count, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_return_status_t ret; + cy_as_ll_request_response *req_p, *reply_p; + cy_as_funct_c_b_type cb_type = CY_FUNCT_CB_STOR_QUERYBUS; + + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ret = is_storage_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + /* Create the request to send to the Antioch device */ + req_p = cy_as_ll_create_request(dev_p, + CY_RQT_QUERY_BUS, CY_RQT_STORAGE_RQT_CONTEXT, 2); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + cy_as_ll_request_response__set_word(req_p, + 0, create_address(bus, 0, 0)); + cy_as_ll_request_response__set_word(req_p, 1, (uint16_t)type); + + /* Reserve space for the reply, the reply data + * will not exceed two words. */ + reply_p = cy_as_ll_create_response(dev_p, 2); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, + req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + req_p->flags |= req_flags; + return my_handle_response_storage_query_bus(dev_p, + req_p, reply_p, count); + } else { + if (req_flags == CY_AS_REQUEST_RESPONSE_EX) + cb_type = CY_FUNCT_CB_STOR_QUERYMEDIA; + + ret = cy_as_misc_send_request(dev_p, cb, client, cb_type, + count, dev_p->func_cbs_stor, req_flags, + req_p, reply_p, cy_as_storage_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + /* The request and response are freed as part of + * the MiscFuncCallback */ + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +cy_as_return_status_t +cy_as_storage_query_bus(cy_as_device_handle handle, + cy_as_bus_number_t bus, + uint32_t *count, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_device *dev_p = (cy_as_device *)handle; + + return my_storage_query_bus(dev_p, bus, cy_as_media_max_media_value, + CY_AS_REQUEST_RESPONSE_MS, count, cb, client); +} + +cy_as_return_status_t +cy_as_storage_query_media(cy_as_device_handle handle, + cy_as_media_type type, + uint32_t *count, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + cy_as_bus_number_t bus; + + cy_as_device *dev_p = (cy_as_device *)handle; + + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ret = is_storage_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + ret = cy_an_map_bus_from_media_type(dev_p, type, &bus); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + return my_storage_query_bus(dev_p, bus, type, CY_AS_REQUEST_RESPONSE_EX, + count, cb, client); +} + +static cy_as_return_status_t +my_handle_response_storage_query_device(cy_as_device *dev_p, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *reply_p, + void *data_p) +{ + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + uint16_t v; + cy_as_bus_number_t bus; + cy_as_media_type type; + uint32_t device; + cy_bool removable; + cy_bool writeable; + cy_bool locked; + uint16_t block_size; + uint32_t number_units; + uint32_t number_eus; + + if (cy_as_ll_request_response__get_code(reply_p) + == CY_RESP_NO_SUCH_ADDRESS) { + ret = cy_as_map_bad_addr( + cy_as_ll_request_response__get_word(reply_p, 3)); + goto destroy; + } + + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_DEVICE_DESCRIPTOR) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + /* Unpack the response */ + v = cy_as_ll_request_response__get_word(reply_p, 0); + type = cy_as_storage_get_media_from_address(v); + bus = cy_as_storage_get_bus_from_address(v); + device = cy_as_storage_get_device_from_address(v); + + block_size = cy_as_ll_request_response__get_word(reply_p, 1); + + v = cy_as_ll_request_response__get_word(reply_p, 2); + removable = (v & 0x8000) ? cy_true : cy_false; + writeable = (v & 0x0100) ? cy_true : cy_false; + locked = (v & 0x0200) ? cy_true : cy_false; + number_units = (v & 0xff); + + number_eus = (cy_as_ll_request_response__get_word(reply_p, 3) << 16) + | cy_as_ll_request_response__get_word(reply_p, 4); + + /* Store the results based on the version of originating function */ + if (req_p->flags & CY_AS_REQUEST_RESPONSE_MS) { + cy_as_storage_query_device_data *store_p = + (cy_as_storage_query_device_data *)data_p; + + /* Make sure the response is about the address we asked + * about - if not, firmware error */ + if ((bus != store_p->bus) || (device != store_p->device)) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + store_p->desc_p.type = type; + store_p->desc_p.removable = removable; + store_p->desc_p.writeable = writeable; + store_p->desc_p.block_size = block_size; + store_p->desc_p.number_units = number_units; + store_p->desc_p.locked = locked; + store_p->desc_p.erase_unit_size = number_eus; + dev_p->storage_device_info[bus][device] = store_p->desc_p; + } else { + cy_as_storage_query_device_data_dep *store_p = + (cy_as_storage_query_device_data_dep *)data_p; + + /* Make sure the response is about the address we asked + * about - if not, firmware error */ + if ((type != store_p->type) || (device != store_p->device)) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + store_p->desc_p.type = type; + store_p->desc_p.removable = removable; + store_p->desc_p.writeable = writeable; + store_p->desc_p.block_size = block_size; + store_p->desc_p.number_units = number_units; + store_p->desc_p.locked = locked; + store_p->desc_p.erase_unit_size = number_eus; + dev_p->storage_device_info[bus][device] = store_p->desc_p; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +static cy_as_return_status_t +my_storage_query_device(cy_as_device *dev_p, + void *data_p, + uint16_t req_flags, + cy_as_bus_number_t bus, + uint32_t device, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_ll_request_response *req_p , *reply_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ret = is_storage_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + /* Create the request to send to the Antioch device */ + req_p = cy_as_ll_create_request(dev_p, + CY_RQT_QUERY_DEVICE, CY_RQT_STORAGE_RQT_CONTEXT, 1); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + cy_as_ll_request_response__set_word(req_p, 0, + create_address(bus, device, 0)); + + /* Reserve space for the reply, the reply data + * will not exceed five words. */ + reply_p = cy_as_ll_create_response(dev_p, 5); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + req_p->flags |= req_flags; + return my_handle_response_storage_query_device(dev_p, + req_p, reply_p, data_p); + } else { + + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_STOR_QUERYDEVICE, data_p, + dev_p->func_cbs_stor, req_flags, req_p, + reply_p, cy_as_storage_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + /* The request and response are freed as part of the + * MiscFuncCallback */ + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +cy_as_return_status_t +cy_as_storage_query_device(cy_as_device_handle handle, + cy_as_storage_query_device_data *data_p, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_device *dev_p = (cy_as_device *)handle; + return my_storage_query_device(dev_p, data_p, + CY_AS_REQUEST_RESPONSE_MS, data_p->bus, + data_p->device, cb, client); +} + +static cy_as_return_status_t +my_handle_response_storage_query_unit(cy_as_device *dev_p, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *reply_p, + void *data_p) +{ + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + cy_as_bus_number_t bus; + uint32_t device; + uint32_t unit; + cy_as_media_type type; + uint16_t block_size; + uint32_t start_block; + uint32_t unit_size; + uint16_t v; + + if (cy_as_ll_request_response__get_code(reply_p) == + CY_RESP_NO_SUCH_ADDRESS) { + ret = cy_as_map_bad_addr( + cy_as_ll_request_response__get_word(reply_p, 3)); + goto destroy; + } + + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_UNIT_DESCRIPTOR) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + /* Unpack the response */ + v = cy_as_ll_request_response__get_word(reply_p, 0); + bus = cy_as_storage_get_bus_from_address(v); + device = cy_as_storage_get_device_from_address(v); + unit = get_unit_from_address(v); + + type = cy_as_storage_get_media_from_address( + cy_as_ll_request_response__get_word(reply_p, 1)); + + block_size = cy_as_ll_request_response__get_word(reply_p, 2); + start_block = cy_as_ll_request_response__get_word(reply_p, 3) + | (cy_as_ll_request_response__get_word(reply_p, 4) << 16); + unit_size = cy_as_ll_request_response__get_word(reply_p, 5) + | (cy_as_ll_request_response__get_word(reply_p, 6) << 16); + + /* Store the results based on the version of + * originating function */ + if (req_p->flags & CY_AS_REQUEST_RESPONSE_MS) { + cy_as_storage_query_unit_data *store_p = + (cy_as_storage_query_unit_data *)data_p; + + /* Make sure the response is about the address we + * asked about - if not, firmware error */ + if (bus != store_p->bus || device != store_p->device || + unit != store_p->unit) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + store_p->desc_p.type = type; + store_p->desc_p.block_size = block_size; + store_p->desc_p.start_block = start_block; + store_p->desc_p.unit_size = unit_size; + } else { + cy_as_storage_query_unit_data_dep *store_p = + (cy_as_storage_query_unit_data_dep *)data_p; + + /* Make sure the response is about the media type we asked + * about - if not, firmware error */ + if ((type != store_p->type) || (device != store_p->device) || + (unit != store_p->unit)) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + store_p->desc_p.type = type; + store_p->desc_p.block_size = block_size; + store_p->desc_p.start_block = start_block; + store_p->desc_p.unit_size = unit_size; + } + + dev_p->storage_device_info[bus][device].type = type; + dev_p->storage_device_info[bus][device].block_size = block_size; + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +static cy_as_return_status_t +my_storage_query_unit(cy_as_device *dev_p, + void *data_p, + uint16_t req_flags, + cy_as_bus_number_t bus, + uint32_t device, + uint32_t unit, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_ll_request_response *req_p , *reply_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ret = is_storage_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + /* Create the request to send to the West Bridge device */ + req_p = cy_as_ll_create_request(dev_p, + CY_RQT_QUERY_UNIT, CY_RQT_STORAGE_RQT_CONTEXT, 1); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + if (device > 255) + return CY_AS_ERROR_NO_SUCH_DEVICE; + + if (unit > 255) + return CY_AS_ERROR_NO_SUCH_UNIT; + + cy_as_ll_request_response__set_word(req_p, 0, + create_address(bus, device, (uint8_t)unit)); + + /* Reserve space for the reply, the reply data + * will be of seven words. */ + reply_p = cy_as_ll_create_response(dev_p, 7); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + req_p->flags |= req_flags; + return my_handle_response_storage_query_unit(dev_p, + req_p, reply_p, data_p); + } else { + + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_STOR_QUERYUNIT, data_p, + dev_p->func_cbs_stor, req_flags, req_p, reply_p, + cy_as_storage_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + /* The request and response are freed + * as part of the MiscFuncCallback */ + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +cy_as_return_status_t +cy_as_storage_query_unit(cy_as_device_handle handle, + cy_as_storage_query_unit_data *data_p, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_device *dev_p = (cy_as_device *)handle; + return my_storage_query_unit(dev_p, data_p, CY_AS_REQUEST_RESPONSE_MS, + data_p->bus, data_p->device, data_p->unit, cb, client); +} + + +static cy_as_return_status_t +cy_as_get_block_size(cy_as_device *dev_p, + cy_as_bus_number_t bus, + uint32_t device, + cy_as_function_callback cb) +{ + cy_as_ll_request_response *req_p , *reply_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + + /* Create the request to send to the West Bridge device */ + req_p = cy_as_ll_create_request(dev_p, CY_RQT_QUERY_DEVICE, + CY_RQT_STORAGE_RQT_CONTEXT, 1); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + cy_as_ll_request_response__set_word(req_p, 0, + create_address(bus, device, 0)); + + reply_p = cy_as_ll_create_response(dev_p, 4); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + if (cy_as_ll_request_response__get_code(reply_p) + == CY_RESP_NO_SUCH_ADDRESS) { + ret = CY_AS_ERROR_NO_SUCH_BUS; + goto destroy; + } + + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_DEVICE_DESCRIPTOR) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + /* Make sure the response is about the media type we asked + * about - if not, firmware error */ + if ((cy_as_storage_get_bus_from_address + (cy_as_ll_request_response__get_word(reply_p, 0)) + != bus) || (cy_as_storage_get_device_from_address + (cy_as_ll_request_response__get_word(reply_p, 0)) + != device)) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + + dev_p->storage_device_info[bus][device].block_size = + cy_as_ll_request_response__get_word(reply_p, 1); + } else + ret = CY_AS_ERROR_INVALID_REQUEST; + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +cy_as_return_status_t +my_storage_device_control( + cy_as_device *dev_p, + cy_as_bus_number_t bus, + uint32_t device, + cy_bool card_detect_en, + cy_bool write_prot_en, + cy_as_storage_card_detect config_detect, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_ll_request_response *req_p , *reply_p; + cy_as_return_status_t ret; + cy_bool use_gpio = cy_false; + + (void)device; + + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + if (!cy_as_device_is_configured(dev_p)) + return CY_AS_ERROR_NOT_CONFIGURED; + + if (!cy_as_device_is_firmware_loaded(dev_p)) + return CY_AS_ERROR_NO_FIRMWARE; + + if (cy_as_device_is_in_suspend_mode(dev_p)) + return CY_AS_ERROR_IN_SUSPEND; + + if (bus < 0 || bus >= CY_AS_MAX_BUSES) + return CY_AS_ERROR_NO_SUCH_BUS; + + if (device >= CY_AS_MAX_STORAGE_DEVICES) + return CY_AS_ERROR_NO_SUCH_DEVICE; + + /* If SD is not supported on the specified bus, + * then return ERROR */ + if ((dev_p->media_supported[bus] == 0) || + (dev_p->media_supported[bus] & (1<<cy_as_media_nand))) + return CY_AS_ERROR_NOT_SUPPORTED; + + if (config_detect == cy_as_storage_detect_GPIO) + use_gpio = cy_true; + else if (config_detect == cy_as_storage_detect_SDAT_3) + use_gpio = cy_false; + else + return CY_AS_ERROR_INVALID_PARAMETER; + + /* Create the request to send to the West Bridge device */ + req_p = cy_as_ll_create_request(dev_p, + CY_RQT_SD_INTERFACE_CONTROL, CY_RQT_STORAGE_RQT_CONTEXT, 2); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + cy_as_ll_request_response__set_word(req_p, + 0, create_address(bus, device, 0)); + cy_as_ll_request_response__set_word(req_p, + 1, (((uint16_t)card_detect_en << 8) | + ((uint16_t)use_gpio << 1) | (uint16_t)write_prot_en)); + + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_SUCCESS_FAILURE) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + ret = cy_as_ll_request_response__get_word(reply_p, 0); + } else { + + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_STOR_DEVICECONTROL, + 0, dev_p->func_cbs_stor, CY_AS_REQUEST_RESPONSE_EX, + req_p, reply_p, cy_as_storage_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + /* The request and response are freed as part of the + * MiscFuncCallback */ + return ret; + } +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +cy_as_return_status_t +cy_as_storage_device_control(cy_as_device_handle handle, + cy_as_bus_number_t bus, + uint32_t device, + cy_bool card_detect_en, + cy_bool write_prot_en, + cy_as_storage_card_detect config_detect, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_device *dev_p = (cy_as_device *)handle; + + return my_storage_device_control(dev_p, bus, device, card_detect_en, + write_prot_en, config_detect, cb, client); +} + +static void +cy_as_async_storage_callback(cy_as_device *dev_p, + cy_as_end_point_number_t ep, void *buf_p, uint32_t size, + cy_as_return_status_t ret) +{ + cy_as_storage_callback_dep cb; + cy_as_storage_callback cb_ms; + + (void)size; + (void)buf_p; + (void)ep; + + cy_as_device_clear_storage_async_pending(dev_p); + + /* + * if the LL request callback has already been called, + * the user callback has to be called from here. + */ + if (!dev_p->storage_wait) { + cy_as_hal_assert(dev_p->storage_cb != NULL || + dev_p->storage_cb_ms != NULL); + cb = dev_p->storage_cb; + cb_ms = dev_p->storage_cb_ms; + + dev_p->storage_cb = 0; + dev_p->storage_cb_ms = 0; + + if (ret == CY_AS_ERROR_SUCCESS) + ret = dev_p->storage_error; + + if (cb_ms) { + cb_ms((cy_as_device_handle)dev_p, + dev_p->storage_bus_index, + dev_p->storage_device_index, + dev_p->storage_unit, + dev_p->storage_block_addr, + dev_p->storage_oper, ret); + } else { + cb((cy_as_device_handle)dev_p, + dev_p->storage_device_info + [dev_p->storage_bus_index] + [dev_p->storage_device_index].type, + dev_p->storage_device_index, + dev_p->storage_unit, + dev_p->storage_block_addr, + dev_p->storage_oper, ret); + } + } else + dev_p->storage_error = ret; +} + +static void +cy_as_async_storage_reply_callback( + cy_as_device *dev_p, + uint8_t context, + cy_as_ll_request_response *rqt, + cy_as_ll_request_response *resp, + cy_as_return_status_t ret) +{ + cy_as_storage_callback_dep cb; + cy_as_storage_callback cb_ms; + uint8_t reqtype; + (void)rqt; + (void)context; + + reqtype = cy_as_ll_request_response__get_code(rqt); + + if (ret == CY_AS_ERROR_SUCCESS) { + if (cy_as_ll_request_response__get_code(resp) == + CY_RESP_ANTIOCH_DEFERRED_ERROR) { + ret = cy_as_ll_request_response__get_word + (resp, 0) & 0x00FF; + } else if (cy_as_ll_request_response__get_code(resp) != + CY_RESP_SUCCESS_FAILURE) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + } + } + + if (ret != CY_AS_ERROR_SUCCESS) { + if (reqtype == CY_RQT_READ_BLOCK) + cy_as_dma_cancel(dev_p, + dev_p->storage_read_endpoint, ret); + else + cy_as_dma_cancel(dev_p, + dev_p->storage_write_endpoint, ret); + } + + dev_p->storage_wait = cy_false; + + /* + * if the DMA callback has already been called, the + * user callback has to be called from here. + */ + if (!cy_as_device_is_storage_async_pending(dev_p)) { + cy_as_hal_assert(dev_p->storage_cb != NULL || + dev_p->storage_cb_ms != NULL); + cb = dev_p->storage_cb; + cb_ms = dev_p->storage_cb_ms; + + dev_p->storage_cb = 0; + dev_p->storage_cb_ms = 0; + + if (ret == CY_AS_ERROR_SUCCESS) + ret = dev_p->storage_error; + + if (cb_ms) { + cb_ms((cy_as_device_handle)dev_p, + dev_p->storage_bus_index, + dev_p->storage_device_index, + dev_p->storage_unit, + dev_p->storage_block_addr, + dev_p->storage_oper, ret); + } else { + cb((cy_as_device_handle)dev_p, + dev_p->storage_device_info + [dev_p->storage_bus_index] + [dev_p->storage_device_index].type, + dev_p->storage_device_index, + dev_p->storage_unit, + dev_p->storage_block_addr, + dev_p->storage_oper, ret); + } + } else + dev_p->storage_error = ret; +} + +static cy_as_return_status_t +cy_as_storage_async_oper(cy_as_device *dev_p, cy_as_end_point_number_t ep, + uint8_t reqtype, uint16_t req_flags, cy_as_bus_number_t bus, + uint32_t device, uint32_t unit, uint32_t block, void *data_p, + uint16_t num_blocks, cy_as_storage_callback_dep callback, + cy_as_storage_callback callback_ms) +{ + uint32_t mask; + cy_as_ll_request_response *req_p , *reply_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + + ret = is_storage_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (bus < 0 || bus >= CY_AS_MAX_BUSES) + return CY_AS_ERROR_NO_SUCH_BUS; + + if (device >= CY_AS_MAX_STORAGE_DEVICES) + return CY_AS_ERROR_NO_SUCH_DEVICE; + + if (unit > 255) + return CY_AS_ERROR_NO_SUCH_UNIT; + + /* We are supposed to return sucess if the number of + * blocks is zero + */ + if (num_blocks == 0) { + if (callback_ms) + callback_ms((cy_as_device_handle)dev_p, + bus, device, unit, block, + ((reqtype == CY_RQT_WRITE_BLOCK) + ? cy_as_op_write : cy_as_op_read), + CY_AS_ERROR_SUCCESS); + else + callback((cy_as_device_handle)dev_p, + dev_p->storage_device_info[bus][device].type, + device, unit, block, + ((reqtype == CY_RQT_WRITE_BLOCK) ? + cy_as_op_write : cy_as_op_read), + CY_AS_ERROR_SUCCESS); + + return CY_AS_ERROR_SUCCESS; + } + + if (dev_p->storage_device_info[bus][device].block_size == 0) + return CY_AS_ERROR_QUERY_DEVICE_NEEDED; + + /* + * since async operations can be triggered by interrupt + * code, we must insure that we do not get multiple + * async operations going at one time and protect this + * test and set operation from interrupts. also need to + * check for pending async MTP writes + */ + mask = cy_as_hal_disable_interrupts(); + if ((cy_as_device_is_storage_async_pending(dev_p)) || + (dev_p->storage_wait) || + (cy_as_device_is_usb_async_pending(dev_p, 6))) { + cy_as_hal_enable_interrupts(mask); + return CY_AS_ERROR_ASYNC_PENDING; + } + + cy_as_device_set_storage_async_pending(dev_p); + cy_as_device_clear_p2s_dma_start_recvd(dev_p); + cy_as_hal_enable_interrupts(mask); + + /* + * storage information about the currently outstanding request + */ + dev_p->storage_cb = callback; + dev_p->storage_cb_ms = callback_ms; + dev_p->storage_bus_index = bus; + dev_p->storage_device_index = device; + dev_p->storage_unit = unit; + dev_p->storage_block_addr = block; + + /* Initialise the request to send to the West Bridge. */ + req_p = dev_p->storage_rw_req_p; + cy_as_ll_init_request(req_p, reqtype, CY_RQT_STORAGE_RQT_CONTEXT, 5); + + /* Initialise the space for reply from the West Bridge. */ + reply_p = dev_p->storage_rw_resp_p; + cy_as_ll_init_response(reply_p, 5); + + /* Remember which version of the API originated the request */ + req_p->flags |= req_flags; + + /* Setup the DMA request and adjust the storage + * operation if we are reading */ + if (reqtype == CY_RQT_READ_BLOCK) { + ret = cy_as_dma_queue_request(dev_p, ep, data_p, + dev_p->storage_device_info[bus][device].block_size + * num_blocks, cy_false, cy_true, + cy_as_async_storage_callback); + dev_p->storage_oper = cy_as_op_read; + } else if (reqtype == CY_RQT_WRITE_BLOCK) { + ret = cy_as_dma_queue_request(dev_p, ep, data_p, + dev_p->storage_device_info[bus][device].block_size * + num_blocks, cy_false, cy_false, + cy_as_async_storage_callback); + dev_p->storage_oper = cy_as_op_write; + } + + if (ret != CY_AS_ERROR_SUCCESS) { + cy_as_device_clear_storage_async_pending(dev_p); + return ret; + } + + cy_as_ll_request_response__set_word(req_p, + 0, create_address(bus, (uint8_t)device, (uint8_t)unit)); + cy_as_ll_request_response__set_word(req_p, + 1, (uint16_t)((block >> 16) & 0xffff)); + cy_as_ll_request_response__set_word(req_p, + 2, (uint16_t)(block & 0xffff)); + cy_as_ll_request_response__set_word(req_p, + 3, (uint16_t)((num_blocks >> 8) & 0x00ff)); + cy_as_ll_request_response__set_word(req_p, + 4, (uint16_t)((num_blocks << 8) & 0xff00)); + + /* Set the burst mode flag. */ + if (dev_p->is_storage_only_mode) + req_p->data[4] |= 0x0001; + + /* Send the request and wait for completion + * of storage request */ + dev_p->storage_wait = cy_true; + ret = cy_as_ll_send_request(dev_p, req_p, reply_p, + cy_true, cy_as_async_storage_reply_callback); + if (ret != CY_AS_ERROR_SUCCESS) { + cy_as_dma_cancel(dev_p, ep, CY_AS_ERROR_CANCELED); + cy_as_device_clear_storage_async_pending(dev_p); + } + + return ret; +} + +static void +cy_as_sync_storage_callback(cy_as_device *dev_p, + cy_as_end_point_number_t ep, void *buf_p, + uint32_t size, cy_as_return_status_t err) +{ + (void)ep; + (void)buf_p; + (void)size; + + dev_p->storage_error = err; +} + +static void +cy_as_sync_storage_reply_callback( + cy_as_device *dev_p, + uint8_t context, + cy_as_ll_request_response *rqt, + cy_as_ll_request_response *resp, + cy_as_return_status_t ret) +{ + uint8_t reqtype; + (void)rqt; + + reqtype = cy_as_ll_request_response__get_code(rqt); + + if (cy_as_ll_request_response__get_code(resp) == + CY_RESP_ANTIOCH_DEFERRED_ERROR) { + ret = cy_as_ll_request_response__get_word(resp, 0) & 0x00FF; + + if (ret != CY_AS_ERROR_SUCCESS) { + if (reqtype == CY_RQT_READ_BLOCK) + cy_as_dma_cancel(dev_p, + dev_p->storage_read_endpoint, ret); + else + cy_as_dma_cancel(dev_p, + dev_p->storage_write_endpoint, ret); + } + } else if (cy_as_ll_request_response__get_code(resp) != + CY_RESP_SUCCESS_FAILURE) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + } + + dev_p->storage_wait = cy_false; + dev_p->storage_error = ret; + + /* Wake any threads/processes that are waiting on + * the read/write completion. */ + cy_as_hal_wake(&dev_p->context[context]->channel); +} + +static cy_as_return_status_t +cy_as_storage_sync_oper(cy_as_device *dev_p, + cy_as_end_point_number_t ep, uint8_t reqtype, + cy_as_bus_number_t bus, uint32_t device, + uint32_t unit, uint32_t block, void *data_p, + uint16_t num_blocks) +{ + cy_as_ll_request_response *req_p , *reply_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + cy_as_context *ctxt_p; + uint32_t loopcount = 200; + + ret = is_storage_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (bus < 0 || bus >= CY_AS_MAX_BUSES) + return CY_AS_ERROR_NO_SUCH_BUS; + + if (device >= CY_AS_MAX_STORAGE_DEVICES) + return CY_AS_ERROR_NO_SUCH_DEVICE; + + if (unit > 255) + return CY_AS_ERROR_NO_SUCH_UNIT; + + if ((cy_as_device_is_storage_async_pending(dev_p)) || + (dev_p->storage_wait)) + return CY_AS_ERROR_ASYNC_PENDING; + + /* Also need to check for pending Async MTP writes */ + if (cy_as_device_is_usb_async_pending(dev_p, 6)) + return CY_AS_ERROR_ASYNC_PENDING; + + /* We are supposed to return sucess if the number of + * blocks is zero + */ + if (num_blocks == 0) + return CY_AS_ERROR_SUCCESS; + + if (dev_p->storage_device_info[bus][device].block_size == 0) { + /* + * normally, a given device has been queried via + * the query device call before a read request is issued. + * therefore, this normally will not be run. + */ + ret = cy_as_get_block_size(dev_p, bus, device, 0); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + } + + /* Initialise the request to send to the West Bridge. */ + req_p = dev_p->storage_rw_req_p; + cy_as_ll_init_request(req_p, reqtype, + CY_RQT_STORAGE_RQT_CONTEXT, 5); + + /* Initialise the space for reply from + * the West Bridge. */ + reply_p = dev_p->storage_rw_resp_p; + cy_as_ll_init_response(reply_p, 5); + cy_as_device_clear_p2s_dma_start_recvd(dev_p); + + /* Setup the DMA request */ + if (reqtype == CY_RQT_READ_BLOCK) { + ret = cy_as_dma_queue_request(dev_p, ep, data_p, + dev_p->storage_device_info[bus][device].block_size * + num_blocks, cy_false, + cy_true, cy_as_sync_storage_callback); + dev_p->storage_oper = cy_as_op_read; + } else if (reqtype == CY_RQT_WRITE_BLOCK) { + ret = cy_as_dma_queue_request(dev_p, ep, data_p, + dev_p->storage_device_info[bus][device].block_size * + num_blocks, cy_false, cy_false, + cy_as_sync_storage_callback); + dev_p->storage_oper = cy_as_op_write; + } + + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + cy_as_ll_request_response__set_word(req_p, 0, + create_address(bus, (uint8_t)device, (uint8_t)unit)); + cy_as_ll_request_response__set_word(req_p, 1, + (uint16_t)((block >> 16) & 0xffff)); + cy_as_ll_request_response__set_word(req_p, 2, + (uint16_t)(block & 0xffff)); + cy_as_ll_request_response__set_word(req_p, 3, + (uint16_t)((num_blocks >> 8) & 0x00ff)); + cy_as_ll_request_response__set_word(req_p, 4, + (uint16_t)((num_blocks << 8) & 0xff00)); + + /* Set the burst mode flag. */ + if (dev_p->is_storage_only_mode) + req_p->data[4] |= 0x0001; + + /* Send the request and wait for + * completion of storage request */ + dev_p->storage_wait = cy_true; + ret = cy_as_ll_send_request(dev_p, req_p, reply_p, cy_true, + cy_as_sync_storage_reply_callback); + if (ret != CY_AS_ERROR_SUCCESS) { + cy_as_dma_cancel(dev_p, ep, CY_AS_ERROR_CANCELED); + } else { + /* Setup the DMA request */ + ctxt_p = dev_p->context[CY_RQT_STORAGE_RQT_CONTEXT]; + ret = cy_as_dma_drain_queue(dev_p, ep, cy_false); + + while (loopcount-- > 0) { + if (dev_p->storage_wait == cy_false) + break; + cy_as_hal_sleep_on(&ctxt_p->channel, 10); + } + + if (dev_p->storage_wait == cy_true) { + dev_p->storage_wait = cy_false; + cy_as_ll_remove_request(dev_p, ctxt_p, req_p, cy_true); + ret = CY_AS_ERROR_TIMEOUT; + } + + if (ret == CY_AS_ERROR_SUCCESS) + ret = dev_p->storage_error; + } + + return ret; +} + +cy_as_return_status_t +cy_as_storage_read(cy_as_device_handle handle, + cy_as_bus_number_t bus, uint32_t device, + uint32_t unit, uint32_t block, + void *data_p, uint16_t num_blocks) +{ + cy_as_device *dev_p = (cy_as_device *)handle; + + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + return cy_as_storage_sync_oper(dev_p, dev_p->storage_read_endpoint, + CY_RQT_READ_BLOCK, bus, device, + unit, block, data_p, num_blocks); +} + +cy_as_return_status_t +cy_as_storage_write(cy_as_device_handle handle, + cy_as_bus_number_t bus, uint32_t device, + uint32_t unit, uint32_t block, void *data_p, + uint16_t num_blocks) +{ + cy_as_device *dev_p = (cy_as_device *)handle; + + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + if (dev_p->mtp_turbo_active) + return CY_AS_ERROR_NOT_VALID_DURING_MTP; + + return cy_as_storage_sync_oper(dev_p, + dev_p->storage_write_endpoint, + CY_RQT_WRITE_BLOCK, bus, device, + unit, block, data_p, num_blocks); +} + + +cy_as_return_status_t +cy_as_storage_read_async(cy_as_device_handle handle, + cy_as_bus_number_t bus, uint32_t device, uint32_t unit, + uint32_t block, void *data_p, uint16_t num_blocks, + cy_as_storage_callback callback) +{ + cy_as_device *dev_p = (cy_as_device *)handle; + + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + if (callback == 0) + return CY_AS_ERROR_NULL_CALLBACK; + + return cy_as_storage_async_oper(dev_p, + dev_p->storage_read_endpoint, CY_RQT_READ_BLOCK, + CY_AS_REQUEST_RESPONSE_MS, bus, device, unit, + block, data_p, num_blocks, NULL, callback); +} + +cy_as_return_status_t +cy_as_storage_write_async(cy_as_device_handle handle, + cy_as_bus_number_t bus, uint32_t device, uint32_t unit, + uint32_t block, void *data_p, uint16_t num_blocks, + cy_as_storage_callback callback) +{ + cy_as_device *dev_p = (cy_as_device *)handle; + + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + if (callback == 0) + return CY_AS_ERROR_NULL_CALLBACK; + + if (dev_p->mtp_turbo_active) + return CY_AS_ERROR_NOT_VALID_DURING_MTP; + + return cy_as_storage_async_oper(dev_p, + dev_p->storage_write_endpoint, CY_RQT_WRITE_BLOCK, + CY_AS_REQUEST_RESPONSE_MS, bus, device, unit, block, + data_p, num_blocks, NULL, callback); +} + + +static void +my_storage_cancel_callback( + cy_as_device *dev_p, + uint8_t context, + cy_as_ll_request_response *rqt, + cy_as_ll_request_response *resp, + cy_as_return_status_t stat) +{ + (void)context; + (void)stat; + + /* Nothing to do here, except free up the + * request and response structures. */ + cy_as_ll_destroy_response(dev_p, resp); + cy_as_ll_destroy_request(dev_p, rqt); +} + + +cy_as_return_status_t +cy_as_storage_cancel_async(cy_as_device_handle handle) +{ + cy_as_return_status_t ret; + cy_as_ll_request_response *req_p , *reply_p; + + cy_as_device *dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ret = is_storage_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (!cy_as_device_is_storage_async_pending(dev_p)) + return CY_AS_ERROR_ASYNC_NOT_PENDING; + + /* + * create and send a mailbox request to firmware + * asking it to abort processing of the current + * P2S operation. the rest of the cancel processing will be + * driven through the callbacks for the read/write call. + */ + req_p = cy_as_ll_create_request(dev_p, CY_RQT_ABORT_P2S_XFER, + CY_RQT_GENERAL_RQT_CONTEXT, 1); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + ret = cy_as_ll_send_request(dev_p, req_p, + reply_p, cy_false, my_storage_cancel_callback); + if (ret) { + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + } + + return CY_AS_ERROR_SUCCESS; +} + +/* + * This function does all the API side clean-up associated with + * CyAsStorageStop, without any communication with the firmware. + */ +void cy_as_storage_cleanup(cy_as_device *dev_p) +{ + if (dev_p->storage_count) { + cy_as_ll_destroy_request(dev_p, dev_p->storage_rw_req_p); + cy_as_ll_destroy_response(dev_p, dev_p->storage_rw_resp_p); + dev_p->storage_count = 0; + cy_as_device_clear_scsi_messages(dev_p); + cy_as_hal_mem_set(dev_p->storage_device_info, + 0, sizeof(dev_p->storage_device_info)); + + cy_as_device_clear_storage_async_pending(dev_p); + dev_p->storage_cb = 0; + dev_p->storage_cb_ms = 0; + dev_p->storage_wait = cy_false; + } +} + +static cy_as_return_status_t +my_handle_response_sd_reg_read( + cy_as_device *dev_p, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *reply_p, + cy_as_storage_sd_reg_read_data *info) +{ + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + uint8_t resp_type, i; + uint16_t resp_len; + uint8_t length = info->length; + uint8_t *data_p = info->buf_p; + + resp_type = cy_as_ll_request_response__get_code(reply_p); + if (resp_type == CY_RESP_SD_REGISTER_DATA) { + uint16_t *resp_p = reply_p->data + 1; + uint16_t temp; + + resp_len = cy_as_ll_request_response__get_word(reply_p, 0); + cy_as_hal_assert(resp_len >= length); + + /* + * copy the values into the output buffer after doing the + * necessary bit shifting. the bit shifting is required because + * the data comes out of the west bridge with a 6 bit offset. + */ + i = 0; + while (length) { + temp = ((resp_p[i] << 6) | (resp_p[i + 1] >> 10)); + i++; + + *data_p++ = (uint8_t)(temp >> 8); + length--; + + if (length) { + *data_p++ = (uint8_t)(temp & 0xFF); + length--; + } + } + } else { + if (resp_type == CY_RESP_SUCCESS_FAILURE) + ret = cy_as_ll_request_response__get_word(reply_p, 0); + else + ret = CY_AS_ERROR_INVALID_RESPONSE; + } + + cy_as_ll_destroy_response(dev_p, reply_p); + cy_as_ll_destroy_request(dev_p, req_p); + + return ret; +} + +cy_as_return_status_t +cy_as_storage_sd_register_read( + cy_as_device_handle handle, + cy_as_bus_number_t bus, + uint8_t device, + cy_as_sd_card_reg_type reg_type, + cy_as_storage_sd_reg_read_data *data_p, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_ll_request_response *req_p , *reply_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + uint8_t length; + + /* + * sanity checks required before sending the request to the + * firmware. + */ + cy_as_device *dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ret = is_storage_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (device >= CY_AS_MAX_STORAGE_DEVICES) + return CY_AS_ERROR_NO_SUCH_DEVICE; + + if (reg_type > cy_as_sd_reg_CSD) + return CY_AS_ERROR_INVALID_PARAMETER; + + /* If SD/MMC media is not supported on the + * addressed bus, return error. */ + if ((dev_p->media_supported[bus] & (1 << cy_as_media_sd_flash)) == 0) + return CY_AS_ERROR_INVALID_PARAMETER; + + /* + * find the amount of data to be returned. this will be the minimum of + * the actual data length, and the length requested. + */ + switch (reg_type) { + case cy_as_sd_reg_OCR: + length = CY_AS_SD_REG_OCR_LENGTH; + break; + case cy_as_sd_reg_CID: + length = CY_AS_SD_REG_CID_LENGTH; + break; + case cy_as_sd_reg_CSD: + length = CY_AS_SD_REG_CSD_LENGTH; + break; + + default: + length = 0; + cy_as_hal_assert(0); + } + + if (length < data_p->length) + data_p->length = length; + length = data_p->length; + + /* Create the request to send to the West Bridge device */ + req_p = cy_as_ll_create_request(dev_p, CY_RQT_SD_REGISTER_READ, + CY_RQT_STORAGE_RQT_CONTEXT, 1); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + cy_as_ll_request_response__set_word(req_p, 0, + (create_address(bus, device, 0) | (uint16_t)reg_type)); + + reply_p = cy_as_ll_create_response(dev_p, + CY_AS_SD_REG_MAX_RESP_LENGTH); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return my_handle_response_sd_reg_read(dev_p, + req_p, reply_p, data_p); + } else { + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_STOR_SDREGISTERREAD, data_p, + dev_p->func_cbs_stor, CY_AS_REQUEST_RESPONSE_EX, + req_p, reply_p, cy_as_storage_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + /* The request and response are freed as part of the + * MiscFuncCallback */ + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +cy_as_return_status_t +cy_as_storage_create_p_partition( + /* Handle to the device of interest */ + cy_as_device_handle handle, + cy_as_bus_number_t bus, + uint32_t device, + /* of P-port only partition in blocks */ + uint32_t size, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_ll_request_response *req_p, *reply_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + cy_as_device *dev_p = (cy_as_device *)handle; + + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ret = is_storage_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + /* Partitions cannot be created or deleted while + * the USB stack is active. */ + if (dev_p->usb_count) + return CY_AS_ERROR_USB_RUNNING; + + /* Create the request to send to the West Bridge device */ + req_p = cy_as_ll_create_request(dev_p, CY_RQT_PARTITION_STORAGE, + CY_RQT_STORAGE_RQT_CONTEXT, 3); + + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + /* Reserve space for the reply, the reply + * data will not exceed one word */ + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + cy_as_ll_request_response__set_word(req_p, 0, + create_address(bus, (uint8_t)device, 0x00)); + cy_as_ll_request_response__set_word(req_p, 1, + (uint16_t)((size >> 16) & 0xffff)); + cy_as_ll_request_response__set_word(req_p, 2, + (uint16_t)(size & 0xffff)); + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return my_handle_response_no_data(dev_p, req_p, reply_p); + } else { + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_STOR_PARTITION, 0, dev_p->func_cbs_stor, + CY_AS_REQUEST_RESPONSE_EX, req_p, reply_p, + cy_as_storage_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + /* The request and response are freed as part of the + * FuncCallback */ + return ret; + + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +cy_as_return_status_t +cy_as_storage_remove_p_partition( + cy_as_device_handle handle, + cy_as_bus_number_t bus, + uint32_t device, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_ll_request_response *req_p, *reply_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + cy_as_device *dev_p = (cy_as_device *)handle; + + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ret = is_storage_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + /* Partitions cannot be created or deleted while + * the USB stack is active. */ + if (dev_p->usb_count) + return CY_AS_ERROR_USB_RUNNING; + + /* Create the request to send to the West Bridge device */ + req_p = cy_as_ll_create_request(dev_p, CY_RQT_PARTITION_ERASE, + CY_RQT_STORAGE_RQT_CONTEXT, 1); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + /* Reserve space for the reply, the reply + * data will not exceed one word */ + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + cy_as_ll_request_response__set_word(req_p, + 0, create_address(bus, (uint8_t)device, 0x00)); + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return my_handle_response_no_data(dev_p, req_p, reply_p); + } else { + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_NODATA, 0, dev_p->func_cbs_stor, + CY_AS_REQUEST_RESPONSE_EX, req_p, reply_p, + cy_as_storage_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + /* The request and response are freed + * as part of the FuncCallback */ + return ret; + + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +static cy_as_return_status_t +my_handle_response_get_transfer_amount(cy_as_device *dev_p, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *reply_p, + cy_as_m_s_c_progress_data *data) +{ + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + uint8_t code = cy_as_ll_request_response__get_code(reply_p); + uint16_t v1, v2; + + if (code != CY_RESP_TRANSFER_COUNT) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + v1 = cy_as_ll_request_response__get_word(reply_p, 0); + v2 = cy_as_ll_request_response__get_word(reply_p, 1); + data->wr_count = (uint32_t)((v1 << 16) | v2); + + v1 = cy_as_ll_request_response__get_word(reply_p, 2); + v2 = cy_as_ll_request_response__get_word(reply_p, 3); + data->rd_count = (uint32_t)((v1 << 16) | v2); + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +cy_as_return_status_t +cy_as_storage_get_transfer_amount( + cy_as_device_handle handle, + cy_as_bus_number_t bus, + uint32_t device, + cy_as_m_s_c_progress_data *data_p, + cy_as_function_callback cb, + uint32_t client + ) +{ + cy_as_ll_request_response *req_p, *reply_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + cy_as_device *dev_p = (cy_as_device *)handle; + + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ret = is_storage_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + /* Check if the firmware image supports this feature. */ + if ((dev_p->media_supported[0]) && (dev_p->media_supported[0] + == (1 << cy_as_media_nand))) + return CY_AS_ERROR_NOT_SUPPORTED; + + /* Create the request to send to the West Bridge device */ + req_p = cy_as_ll_create_request(dev_p, CY_RQT_GET_TRANSFER_AMOUNT, + CY_RQT_STORAGE_RQT_CONTEXT, 1); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + /* Reserve space for the reply, the reply data + * will not exceed four words. */ + reply_p = cy_as_ll_create_response(dev_p, 4); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + cy_as_ll_request_response__set_word(req_p, 0, + create_address(bus, (uint8_t)device, 0x00)); + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return my_handle_response_get_transfer_amount(dev_p, + req_p, reply_p, data_p); + } else { + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_STOR_GETTRANSFERAMOUNT, (void *)data_p, + dev_p->func_cbs_stor, CY_AS_REQUEST_RESPONSE_EX, + req_p, reply_p, cy_as_storage_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + /* The request and response are freed as part of the + * FuncCallback */ + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; + +} + +cy_as_return_status_t +cy_as_storage_erase( + cy_as_device_handle handle, + cy_as_bus_number_t bus, + uint32_t device, + uint32_t erase_unit, + uint16_t num_erase_units, + cy_as_function_callback cb, + uint32_t client + ) +{ + cy_as_ll_request_response *req_p, *reply_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + cy_as_device *dev_p = (cy_as_device *)handle; + + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ret = is_storage_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (bus < 0 || bus >= CY_AS_MAX_BUSES) + return CY_AS_ERROR_NO_SUCH_BUS; + + if (device >= CY_AS_MAX_STORAGE_DEVICES) + return CY_AS_ERROR_NO_SUCH_DEVICE; + + if (dev_p->storage_device_info[bus][device].block_size == 0) + return CY_AS_ERROR_QUERY_DEVICE_NEEDED; + + /* If SD is not supported on the specified bus, then return ERROR */ + if (dev_p->storage_device_info[bus][device].type != + cy_as_media_sd_flash) + return CY_AS_ERROR_NOT_SUPPORTED; + + if (num_erase_units == 0) + return CY_AS_ERROR_SUCCESS; + + /* Create the request to send to the West Bridge device */ + req_p = cy_as_ll_create_request(dev_p, CY_RQT_ERASE, + CY_RQT_STORAGE_RQT_CONTEXT, 5); + + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + /* Reserve space for the reply, the reply + * data will not exceed four words. */ + reply_p = cy_as_ll_create_response(dev_p, 4); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + cy_as_ll_request_response__set_word(req_p, 0, + create_address(bus, (uint8_t)device, 0x00)); + cy_as_ll_request_response__set_word(req_p, 1, + (uint16_t)((erase_unit >> 16) & 0xffff)); + cy_as_ll_request_response__set_word(req_p, 2, + (uint16_t)(erase_unit & 0xffff)); + cy_as_ll_request_response__set_word(req_p, 3, + (uint16_t)((num_erase_units >> 8) & 0x00ff)); + cy_as_ll_request_response__set_word(req_p, 4, + (uint16_t)((num_erase_units << 8) & 0xff00)); + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + ret = my_handle_response_no_data(dev_p, req_p, reply_p); + + /* If error = "invalid response", this (very likely) means + * that we are not using the SD-only firmware module which + * is the only one supporting storage_erase. in this case + * force a "non supported" error code */ + if (ret == CY_AS_ERROR_INVALID_RESPONSE) + ret = CY_AS_ERROR_NOT_SUPPORTED; + + return ret; + } else { + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_STOR_ERASE, 0, dev_p->func_cbs_stor, + CY_AS_REQUEST_RESPONSE_EX, req_p, reply_p, + cy_as_storage_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + /* The request and response are freed + * as part of the FuncCallback */ + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +static void +cy_as_storage_func_callback(cy_as_device *dev_p, + uint8_t context, + cy_as_ll_request_response *rqt, + cy_as_ll_request_response *resp, + cy_as_return_status_t stat) +{ + cy_as_func_c_b_node *node = (cy_as_func_c_b_node *) + dev_p->func_cbs_stor->head_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + + cy_bool ex_request = (rqt->flags & CY_AS_REQUEST_RESPONSE_EX) + == CY_AS_REQUEST_RESPONSE_EX; + cy_bool ms_request = (rqt->flags & CY_AS_REQUEST_RESPONSE_MS) + == CY_AS_REQUEST_RESPONSE_MS; + uint8_t code; + uint8_t cntxt; + + cy_as_hal_assert(ex_request || ms_request); + cy_as_hal_assert(dev_p->func_cbs_stor->count != 0); + cy_as_hal_assert(dev_p->func_cbs_stor->type == CYAS_FUNC_CB); + (void) ex_request; + (void) ms_request; + + (void)context; + + cntxt = cy_as_ll_request_response__get_context(rqt); + cy_as_hal_assert(cntxt == CY_RQT_STORAGE_RQT_CONTEXT); + + code = cy_as_ll_request_response__get_code(rqt); + switch (code) { + case CY_RQT_START_STORAGE: + ret = my_handle_response_storage_start(dev_p, rqt, resp, stat); + break; + case CY_RQT_STOP_STORAGE: + ret = my_handle_response_storage_stop(dev_p, rqt, resp, stat); + break; + case CY_RQT_CLAIM_STORAGE: + ret = my_handle_response_storage_claim(dev_p, rqt, resp); + break; + case CY_RQT_RELEASE_STORAGE: + ret = my_handle_response_storage_release(dev_p, rqt, resp); + break; + case CY_RQT_QUERY_MEDIA: + cy_as_hal_assert(cy_false);/* Not used any more. */ + break; + case CY_RQT_QUERY_BUS: + cy_as_hal_assert(node->data != 0); + ret = my_handle_response_storage_query_bus(dev_p, + rqt, resp, (uint32_t *)node->data); + break; + case CY_RQT_QUERY_DEVICE: + cy_as_hal_assert(node->data != 0); + ret = my_handle_response_storage_query_device(dev_p, + rqt, resp, node->data); + break; + case CY_RQT_QUERY_UNIT: + cy_as_hal_assert(node->data != 0); + ret = my_handle_response_storage_query_unit(dev_p, + rqt, resp, node->data); + break; + case CY_RQT_SD_INTERFACE_CONTROL: + ret = my_handle_response_no_data(dev_p, rqt, resp); + break; + case CY_RQT_SD_REGISTER_READ: + cy_as_hal_assert(node->data != 0); + ret = my_handle_response_sd_reg_read(dev_p, rqt, resp, + (cy_as_storage_sd_reg_read_data *)node->data); + break; + case CY_RQT_PARTITION_STORAGE: + ret = my_handle_response_no_data(dev_p, rqt, resp); + break; + case CY_RQT_PARTITION_ERASE: + ret = my_handle_response_no_data(dev_p, rqt, resp); + break; + case CY_RQT_GET_TRANSFER_AMOUNT: + cy_as_hal_assert(node->data != 0); + ret = my_handle_response_get_transfer_amount(dev_p, + rqt, resp, (cy_as_m_s_c_progress_data *)node->data); + break; + case CY_RQT_ERASE: + ret = my_handle_response_no_data(dev_p, rqt, resp); + + /* If error = "invalid response", this (very likely) + * means that we are not using the SD-only firmware + * module which is the only one supporting storage_erase. + * in this case force a "non supported" error code */ + if (ret == CY_AS_ERROR_INVALID_RESPONSE) + ret = CY_AS_ERROR_NOT_SUPPORTED; + + break; + + default: + ret = CY_AS_ERROR_INVALID_RESPONSE; + cy_as_hal_assert(cy_false); + break; + } + + /* + * if the low level layer returns a direct error, use the + * corresponding error code. if not, use the error code + * based on the response from firmware. + */ + if (stat == CY_AS_ERROR_SUCCESS) + stat = ret; + + /* Call the user callback, if there is one */ + if (node->cb_p) + node->cb_p((cy_as_device_handle)dev_p, stat, + node->client_data, node->data_type, node->data); + cy_as_remove_c_b_node(dev_p->func_cbs_stor); +} + + +static void +cy_as_sdio_sync_reply_callback( + cy_as_device *dev_p, + uint8_t context, + cy_as_ll_request_response *rqt, + cy_as_ll_request_response *resp, + cy_as_return_status_t ret) +{ + (void)rqt; + + if ((cy_as_ll_request_response__get_code(resp) == + CY_RESP_SDIO_GET_TUPLE) || + (cy_as_ll_request_response__get_code(resp) == + CY_RESP_SDIO_EXT)) { + ret = cy_as_ll_request_response__get_word(resp, 0); + if ((ret & 0x00FF) != CY_AS_ERROR_SUCCESS) { + if (cy_as_ll_request_response__get_code(rqt) == + CY_RQT_SDIO_READ_EXTENDED) + cy_as_dma_cancel(dev_p, + dev_p->storage_read_endpoint, ret); + else + cy_as_dma_cancel(dev_p, + dev_p->storage_write_endpoint, ret); + } + } else { + ret = CY_AS_ERROR_INVALID_RESPONSE; + } + + dev_p->storage_rw_resp_p = resp; + dev_p->storage_wait = cy_false; + if (((ret & 0x00FF) == CY_AS_ERROR_IO_ABORTED) || ((ret & 0x00FF) + == CY_AS_ERROR_IO_SUSPENDED)) + dev_p->storage_error = (ret & 0x00FF); + else + dev_p->storage_error = (ret & 0x00FF) ? + CY_AS_ERROR_INVALID_RESPONSE : CY_AS_ERROR_SUCCESS; + + /* Wake any threads/processes that are waiting on + * the read/write completion. */ + cy_as_hal_wake(&dev_p->context[context]->channel); +} + +cy_as_return_status_t +cy_as_sdio_device_check( + cy_as_device *dev_p, + cy_as_bus_number_t bus, + uint32_t device) +{ + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + if (bus < 0 || bus >= CY_AS_MAX_BUSES) + return CY_AS_ERROR_NO_SUCH_BUS; + + if (device >= CY_AS_MAX_STORAGE_DEVICES) + return CY_AS_ERROR_NO_SUCH_DEVICE; + + if (!cy_as_device_is_astoria_dev(dev_p)) + return CY_AS_ERROR_NOT_SUPPORTED; + + return (is_storage_active(dev_p)); +} + +cy_as_return_status_t +cy_as_sdio_direct_io( + cy_as_device_handle handle, + cy_as_bus_number_t bus, + uint32_t device, + uint8_t n_function_no, + uint32_t address, + uint8_t misc_buf, + uint16_t argument, + uint8_t is_write, + uint8_t *data_p) +{ + cy_as_ll_request_response *req_p , *reply_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + uint16_t resp_data; + + /* + * sanity checks required before sending the request to the + * firmware. + */ + cy_as_device *dev_p = (cy_as_device *)handle; + ret = cy_as_sdio_device_check(dev_p, bus, device); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + + if (!(cy_as_sdio_check_function_initialized(handle, + bus, n_function_no))) + return CY_AS_ERROR_INVALID_FUNCTION; + if (cy_as_sdio_check_function_suspended(handle, bus, n_function_no)) + return CY_AS_ERROR_FUNCTION_SUSPENDED; + + req_p = cy_as_ll_create_request(dev_p, (is_write == cy_true) ? + CY_RQT_SDIO_WRITE_DIRECT : CY_RQT_SDIO_READ_DIRECT, + CY_RQT_STORAGE_RQT_CONTEXT, 3); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + /*Setting up request*/ + + cy_as_ll_request_response__set_word(req_p, 0, + create_address(bus, (uint8_t)device, n_function_no)); + /* D1 */ + if (is_write == cy_true) { + cy_as_ll_request_response__set_word(req_p, 1, + ((argument<<8) | 0x0080 | (n_function_no<<4) | + ((misc_buf&CY_SDIO_RAW)<<3) | + ((misc_buf&CY_SDIO_REARM_INT)>>5) | + (uint16_t)(address>>15))); + } else { + cy_as_ll_request_response__set_word(req_p, 1, + (n_function_no<<4) | ((misc_buf&CY_SDIO_REARM_INT)>>5) | + (uint16_t)(address>>15)); + } + /* D2 */ + cy_as_ll_request_response__set_word(req_p, 2, + ((uint16_t)((address&0x00007fff)<<1))); + + /*Create response*/ + reply_p = cy_as_ll_create_response(dev_p, 2); + + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + /*Sending the request*/ + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + /*Check reply type*/ + if (cy_as_ll_request_response__get_code(reply_p) == + CY_RESP_SDIO_DIRECT) { + resp_data = cy_as_ll_request_response__get_word(reply_p, 0); + if (resp_data >> 8) + ret = CY_AS_ERROR_INVALID_RESPONSE; + else if (data_p != 0) + *(uint8_t *)(data_p) = (uint8_t)(resp_data&0x00ff); + } else { + ret = CY_AS_ERROR_INVALID_RESPONSE; + } + +destroy: + if (req_p != 0) + cy_as_ll_destroy_request(dev_p, req_p); + if (reply_p != 0) + cy_as_ll_destroy_response(dev_p, reply_p); + return ret; +} + + +cy_as_return_status_t +cy_as_sdio_direct_read( + cy_as_device_handle handle, + cy_as_bus_number_t bus, + uint32_t device, + uint8_t n_function_no, + uint32_t address, + uint8_t misc_buf, + uint8_t *data_p) +{ + return cy_as_sdio_direct_io(handle, bus, device, n_function_no, + address, misc_buf, 0x00, cy_false, data_p); +} + +cy_as_return_status_t +cy_as_sdio_direct_write( + cy_as_device_handle handle, + cy_as_bus_number_t bus, + uint32_t device, + uint8_t n_function_no, + uint32_t address, + uint8_t misc_buf, + uint16_t argument, + uint8_t *data_p) +{ + return cy_as_sdio_direct_io(handle, bus, device, n_function_no, + address, misc_buf, argument, cy_true, data_p); +} + +/*Cmd53 IO*/ +cy_as_return_status_t +cy_as_sdio_extended_i_o( + cy_as_device_handle handle, + cy_as_bus_number_t bus, + uint32_t device, + uint8_t n_function_no, + uint32_t address, + uint8_t misc_buf, + uint16_t argument, + uint8_t is_write, + uint8_t *data_p , + uint8_t is_resume) +{ + cy_as_ll_request_response *req_p , *reply_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + uint8_t resp_type; + uint8_t reqtype; + uint16_t resp_data; + cy_as_context *ctxt_p; + uint32_t dmasize, loopcount = 200; + cy_as_end_point_number_t ep; + + cy_as_device *dev_p = (cy_as_device *)handle; + ret = cy_as_sdio_device_check(dev_p, bus, device); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (!(cy_as_sdio_check_function_initialized(handle, + bus, n_function_no))) + return CY_AS_ERROR_INVALID_FUNCTION; + if (cy_as_sdio_check_function_suspended(handle, bus, n_function_no)) + return CY_AS_ERROR_FUNCTION_SUSPENDED; + + + if ((cy_as_device_is_storage_async_pending(dev_p)) || + (dev_p->storage_wait)) + return CY_AS_ERROR_ASYNC_PENDING; + + /* Request for 0 bytes of blocks is returned as a success*/ + if (argument == 0) + return CY_AS_ERROR_SUCCESS; + + /* Initialise the request to send to the West Bridge device. */ + if (is_write == cy_true) { + reqtype = CY_RQT_SDIO_WRITE_EXTENDED; + ep = dev_p->storage_write_endpoint; + } else { + reqtype = CY_RQT_SDIO_READ_EXTENDED; + ep = dev_p->storage_read_endpoint; + } + + req_p = dev_p->storage_rw_req_p; + cy_as_ll_init_request(req_p, reqtype, CY_RQT_STORAGE_RQT_CONTEXT, 3); + + /* Initialise the space for reply from the Antioch. */ + reply_p = dev_p->storage_rw_resp_p; + cy_as_ll_init_response(reply_p, 2); + + /* Setup the DMA request */ + if (!(misc_buf&CY_SDIO_BLOCKMODE)) { + if (argument > + dev_p->sdiocard[bus]. + function[n_function_no-1].blocksize) + return CY_AS_ERROR_INVALID_BLOCKSIZE; + + } else { + if (argument > 511) + return CY_AS_ERROR_INVALID_BLOCKSIZE; + } + + if (argument == 512) + argument = 0; + + dmasize = ((misc_buf&CY_SDIO_BLOCKMODE) != 0) ? + dev_p->sdiocard[bus].function[n_function_no-1].blocksize + * argument : argument; + + ret = cy_as_dma_queue_request(dev_p, ep, (void *)(data_p), + dmasize, cy_false, (is_write & cy_true) ? cy_false : + cy_true, cy_as_sync_storage_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + cy_as_ll_request_response__set_word(req_p, 0, + create_address(bus, (uint8_t)device, + n_function_no | ((is_resume) ? 0x80 : 0x00))); + cy_as_ll_request_response__set_word(req_p, 1, + ((uint16_t)n_function_no)<<12| + ((uint16_t)(misc_buf & (CY_SDIO_BLOCKMODE|CY_SDIO_OP_INCR))) + << 9 | (uint16_t)(address >> 7) | + ((is_write == cy_true) ? 0x8000 : 0x0000)); + cy_as_ll_request_response__set_word(req_p, 2, + ((uint16_t)(address&0x0000ffff) << 9) | argument); + + + /* Send the request and wait for completion of storage request */ + dev_p->storage_wait = cy_true; + ret = cy_as_ll_send_request(dev_p, req_p, reply_p, + cy_true, cy_as_sdio_sync_reply_callback); + + if (ret != CY_AS_ERROR_SUCCESS) { + cy_as_dma_cancel(dev_p, ep, CY_AS_ERROR_CANCELED); + } else { + /* Setup the DMA request */ + ctxt_p = dev_p->context[CY_RQT_STORAGE_RQT_CONTEXT]; + ret = cy_as_dma_drain_queue(dev_p, ep, cy_true); + + while (loopcount-- > 0) { + if (dev_p->storage_wait == cy_false) + break; + cy_as_hal_sleep_on(&ctxt_p->channel, 10); + } + if (dev_p->storage_wait == cy_true) { + dev_p->storage_wait = cy_false; + cy_as_ll_remove_request(dev_p, ctxt_p, req_p, cy_true); + dev_p->storage_error = CY_AS_ERROR_TIMEOUT; + } + + ret = dev_p->storage_error; + + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + resp_type = cy_as_ll_request_response__get_code( + dev_p->storage_rw_resp_p); + if (resp_type == CY_RESP_SDIO_EXT) { + resp_data = cy_as_ll_request_response__get_word + (reply_p, 0)&0x00ff; + if (resp_data) + ret = CY_AS_ERROR_INVALID_REQUEST; + + } else { + ret = CY_AS_ERROR_INVALID_RESPONSE; + } + } + return ret; + +} + +static void +cy_as_sdio_async_reply_callback( + cy_as_device *dev_p, + uint8_t context, + cy_as_ll_request_response *rqt, + cy_as_ll_request_response *resp, + cy_as_return_status_t ret) +{ + cy_as_storage_callback cb_ms; + uint8_t reqtype; + uint32_t pendingblocks; + (void)rqt; + (void)context; + + pendingblocks = 0; + reqtype = cy_as_ll_request_response__get_code(rqt); + if (ret == CY_AS_ERROR_SUCCESS) { + if ((cy_as_ll_request_response__get_code(resp) == + CY_RESP_SUCCESS_FAILURE) || + (cy_as_ll_request_response__get_code(resp) == + CY_RESP_SDIO_EXT)) { + ret = cy_as_ll_request_response__get_word(resp, 0); + ret &= 0x00FF; + } else { + ret = CY_AS_ERROR_INVALID_RESPONSE; + } + } + + if (ret != CY_AS_ERROR_SUCCESS) { + if (reqtype == CY_RQT_SDIO_READ_EXTENDED) + cy_as_dma_cancel(dev_p, + dev_p->storage_read_endpoint, ret); + else + cy_as_dma_cancel(dev_p, + dev_p->storage_write_endpoint, ret); + + dev_p->storage_error = ret; + } + + dev_p->storage_wait = cy_false; + + /* + * if the DMA callback has already been called, + * the user callback has to be called from here. + */ + if (!cy_as_device_is_storage_async_pending(dev_p)) { + cy_as_hal_assert(dev_p->storage_cb_ms != NULL); + cb_ms = dev_p->storage_cb_ms; + + dev_p->storage_cb = 0; + dev_p->storage_cb_ms = 0; + + if ((ret == CY_AS_ERROR_SUCCESS) || + (ret == CY_AS_ERROR_IO_ABORTED) || + (ret == CY_AS_ERROR_IO_SUSPENDED)) { + ret = dev_p->storage_error; + pendingblocks = ((uint32_t) + cy_as_ll_request_response__get_word + (resp, 1)) << 16; + } else + ret = CY_AS_ERROR_INVALID_RESPONSE; + + cb_ms((cy_as_device_handle)dev_p, dev_p->storage_bus_index, + dev_p->storage_device_index, + (dev_p->storage_unit | pendingblocks), + dev_p->storage_block_addr, dev_p->storage_oper, ret); + } else + dev_p->storage_error = ret; +} + + +cy_as_return_status_t +cy_as_sdio_extended_i_o_async( + cy_as_device_handle handle, + cy_as_bus_number_t bus, + uint32_t device, + uint8_t n_function_no, + uint32_t address, + uint8_t misc_buf, + uint16_t argument, + uint8_t is_write, + uint8_t *data_p, + cy_as_storage_callback callback) +{ + + uint32_t mask; + uint32_t dmasize; + cy_as_ll_request_response *req_p , *reply_p; + uint8_t reqtype; + cy_as_end_point_number_t ep; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + cy_as_device *dev_p = (cy_as_device *)handle; + + ret = cy_as_sdio_device_check(dev_p, bus, device); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (!(cy_as_sdio_check_function_initialized(handle, + bus, n_function_no))) + return CY_AS_ERROR_INVALID_FUNCTION; + if (cy_as_sdio_check_function_suspended(handle, bus, n_function_no)) + return CY_AS_ERROR_FUNCTION_SUSPENDED; + + if (callback == 0) + return CY_AS_ERROR_NULL_CALLBACK; + + /* We are supposed to return sucess if the number of + * blocks is zero + */ + if (((misc_buf&CY_SDIO_BLOCKMODE) != 0) && (argument == 0)) { + callback(handle, bus, device, n_function_no, address, + ((is_write) ? cy_as_op_write : cy_as_op_read), + CY_AS_ERROR_SUCCESS); + return CY_AS_ERROR_SUCCESS; + } + + + /* + * since async operations can be triggered by interrupt + * code, we must insure that we do not get multiple async + * operations going at one time and protect this test and + * set operation from interrupts. + */ + mask = cy_as_hal_disable_interrupts(); + if ((cy_as_device_is_storage_async_pending(dev_p)) || + (dev_p->storage_wait)) { + cy_as_hal_enable_interrupts(mask); + return CY_AS_ERROR_ASYNC_PENDING; + } + cy_as_device_set_storage_async_pending(dev_p); + cy_as_hal_enable_interrupts(mask); + + + /* + * storage information about the currently + * outstanding request + */ + dev_p->storage_cb_ms = callback; + dev_p->storage_bus_index = bus; + dev_p->storage_device_index = device; + dev_p->storage_unit = n_function_no; + dev_p->storage_block_addr = address; + + if (is_write == cy_true) { + reqtype = CY_RQT_SDIO_WRITE_EXTENDED; + ep = dev_p->storage_write_endpoint; + } else { + reqtype = CY_RQT_SDIO_READ_EXTENDED; + ep = dev_p->storage_read_endpoint; + } + + /* Initialise the request to send to the West Bridge. */ + req_p = dev_p->storage_rw_req_p; + cy_as_ll_init_request(req_p, reqtype, + CY_RQT_STORAGE_RQT_CONTEXT, 3); + + /* Initialise the space for reply from the West Bridge. */ + reply_p = dev_p->storage_rw_resp_p; + cy_as_ll_init_response(reply_p, 2); + + if (!(misc_buf&CY_SDIO_BLOCKMODE)) { + if (argument > + dev_p->sdiocard[bus].function[n_function_no-1].blocksize) + return CY_AS_ERROR_INVALID_BLOCKSIZE; + + } else { + if (argument > 511) + return CY_AS_ERROR_INVALID_BLOCKSIZE; + } + + if (argument == 512) + argument = 0; + dmasize = ((misc_buf&CY_SDIO_BLOCKMODE) != 0) ? + dev_p->sdiocard[bus].function[n_function_no-1].blocksize * + argument : argument; + + /* Setup the DMA request and adjust the storage + * operation if we are reading */ + if (reqtype == CY_RQT_SDIO_READ_EXTENDED) { + ret = cy_as_dma_queue_request(dev_p, ep, + (void *)data_p, dmasize , cy_false, cy_true, + cy_as_async_storage_callback); + dev_p->storage_oper = cy_as_op_read; + } else if (reqtype == CY_RQT_SDIO_WRITE_EXTENDED) { + ret = cy_as_dma_queue_request(dev_p, ep, (void *)data_p, + dmasize, cy_false, cy_false, cy_as_async_storage_callback); + dev_p->storage_oper = cy_as_op_write; + } + + if (ret != CY_AS_ERROR_SUCCESS) { + cy_as_device_clear_storage_async_pending(dev_p); + return ret; + } + + cy_as_ll_request_response__set_word(req_p, 0, + create_address(bus, (uint8_t)device, n_function_no)); + cy_as_ll_request_response__set_word(req_p, 1, + ((uint16_t)n_function_no) << 12 | + ((uint16_t)(misc_buf & (CY_SDIO_BLOCKMODE | CY_SDIO_OP_INCR))) + << 9 | (uint16_t)(address>>7) | + ((is_write == cy_true) ? 0x8000 : 0x0000)); + cy_as_ll_request_response__set_word(req_p, 2, + ((uint16_t)(address&0x0000ffff) << 9) | argument); + + + /* Send the request and wait for completion of storage request */ + dev_p->storage_wait = cy_true; + ret = cy_as_ll_send_request(dev_p, req_p, reply_p, cy_true, + cy_as_sdio_async_reply_callback); + if (ret != CY_AS_ERROR_SUCCESS) { + cy_as_dma_cancel(dev_p, ep, CY_AS_ERROR_CANCELED); + cy_as_device_clear_storage_async_pending(dev_p); + } else { + cy_as_dma_kick_start(dev_p, ep); + } + + return ret; +} + +/* CMD53 Extended Read*/ +cy_as_return_status_t +cy_as_sdio_extended_read( + cy_as_device_handle handle, + cy_as_bus_number_t bus, + uint32_t device, + uint8_t n_function_no, + uint32_t address, + uint8_t misc_buf, + uint16_t argument, + uint8_t *data_p, + cy_as_sdio_callback callback) +{ + if (callback == 0) + return cy_as_sdio_extended_i_o(handle, bus, device, + n_function_no, address, misc_buf, argument, + cy_false, data_p, 0); + + return cy_as_sdio_extended_i_o_async(handle, bus, device, + n_function_no, address, misc_buf, argument, cy_false, + data_p, callback); +} + +/* CMD53 Extended Write*/ +cy_as_return_status_t +cy_as_sdio_extended_write( + cy_as_device_handle handle, + cy_as_bus_number_t bus, + uint32_t device, + uint8_t n_function_no, + uint32_t address, + uint8_t misc_buf, + uint16_t argument, + uint8_t *data_p, + cy_as_sdio_callback callback) +{ + if (callback == 0) + return cy_as_sdio_extended_i_o(handle, bus, device, + n_function_no, address, misc_buf, argument, cy_true, + data_p, 0); + + return cy_as_sdio_extended_i_o_async(handle, bus, device, + n_function_no, address, misc_buf, argument, cy_true, + data_p, callback); +} + + +/* Read the CIS info tuples for the given function and Tuple ID*/ +cy_as_return_status_t +cy_as_sdio_get_c_i_s_info( + cy_as_device_handle handle, + cy_as_bus_number_t bus, + uint32_t device, + uint8_t n_function_no, + uint16_t tuple_id, + uint8_t *data_p) +{ + + cy_as_ll_request_response *req_p , *reply_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + uint16_t resp_data; + cy_as_context *ctxt_p; + uint32_t loopcount = 200; + + cy_as_device *dev_p = (cy_as_device *)handle; + + ret = cy_as_sdio_device_check(dev_p, bus, device); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (!(cy_as_sdio_check_function_initialized(handle, bus, 0))) + return CY_AS_ERROR_INVALID_FUNCTION; + + if ((cy_as_device_is_storage_async_pending(dev_p)) || + (dev_p->storage_wait)) + return CY_AS_ERROR_ASYNC_PENDING; + + + /* Initialise the request to send to the Antioch. */ + req_p = dev_p->storage_rw_req_p; + cy_as_ll_init_request(req_p, CY_RQT_SDIO_GET_TUPLE, + CY_RQT_STORAGE_RQT_CONTEXT, 2); + + /* Initialise the space for reply from the Antioch. */ + reply_p = dev_p->storage_rw_resp_p; + cy_as_ll_init_response(reply_p, 3); + + /* Setup the DMA request */ + ret = cy_as_dma_queue_request(dev_p, dev_p->storage_read_endpoint, + data_p+1, 255, cy_false, cy_true, cy_as_sync_storage_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + cy_as_ll_request_response__set_word(req_p, 0, + create_address(bus, (uint8_t)device, n_function_no)); + + /* Set tuple id to fetch. */ + cy_as_ll_request_response__set_word(req_p, 1, tuple_id<<8); + + /* Send the request and wait for completion of storage request */ + dev_p->storage_wait = cy_true; + ret = cy_as_ll_send_request(dev_p, req_p, reply_p, cy_true, + cy_as_sdio_sync_reply_callback); + + if (ret != CY_AS_ERROR_SUCCESS) { + cy_as_dma_cancel(dev_p, + dev_p->storage_read_endpoint, CY_AS_ERROR_CANCELED); + } else { + /* Setup the DMA request */ + ctxt_p = dev_p->context[CY_RQT_STORAGE_RQT_CONTEXT]; + ret = cy_as_dma_drain_queue(dev_p, + dev_p->storage_read_endpoint, cy_true); + + while (loopcount-- > 0) { + if (dev_p->storage_wait == cy_false) + break; + cy_as_hal_sleep_on(&ctxt_p->channel, 10); + } + + if (dev_p->storage_wait == cy_true) { + dev_p->storage_wait = cy_false; + cy_as_ll_remove_request(dev_p, ctxt_p, req_p, cy_true); + return CY_AS_ERROR_TIMEOUT; + } + ret = dev_p->storage_error; + + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (cy_as_ll_request_response__get_code + (dev_p->storage_rw_resp_p) == CY_RESP_SDIO_GET_TUPLE) { + resp_data = cy_as_ll_request_response__get_word + (reply_p, 0); + if (resp_data) { + ret = CY_AS_ERROR_INVALID_REQUEST; + } else if (data_p != 0) + *(uint8_t *)data_p = (uint8_t) + (cy_as_ll_request_response__get_word + (reply_p, 0)&0x00ff); + } else { + ret = CY_AS_ERROR_INVALID_RESPONSE; + } + } + return ret; +} + +/*Query Device*/ +cy_as_return_status_t +cy_as_sdio_query_card( + cy_as_device_handle handle, + cy_as_bus_number_t bus, + uint32_t device, + cy_as_sdio_card *data_p) +{ + cy_as_ll_request_response *req_p , *reply_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + + uint8_t resp_type; + cy_as_device *dev_p = (cy_as_device *)handle; + + ret = cy_as_sdio_device_check(dev_p, bus, device); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + /* Allocating memory to the SDIO device structure in dev_p */ + + cy_as_hal_mem_set(&dev_p->sdiocard[bus], 0, sizeof(cy_as_sdio_device)); + + req_p = cy_as_ll_create_request(dev_p, CY_RQT_SDIO_QUERY_CARD, + CY_RQT_STORAGE_RQT_CONTEXT, 1); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + cy_as_ll_request_response__set_word(req_p, 0, + create_address(bus, (uint8_t)device, 0)); + + reply_p = cy_as_ll_create_response(dev_p, 5); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + ret = cy_as_ll_send_request_wait_reply(dev_p, + req_p, reply_p); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + resp_type = cy_as_ll_request_response__get_code(reply_p); + if (resp_type == CY_RESP_SDIO_QUERY_CARD) { + dev_p->sdiocard[bus].card.num_functions = + (uint8_t)((reply_p->data[0]&0xff00)>>8); + dev_p->sdiocard[bus].card.memory_present = + (uint8_t)reply_p->data[0]&0x0001; + dev_p->sdiocard[bus].card.manufacturer__id = + reply_p->data[1]; + dev_p->sdiocard[bus].card.manufacturer_info = + reply_p->data[2]; + dev_p->sdiocard[bus].card.blocksize = + reply_p->data[3]; + dev_p->sdiocard[bus].card.maxblocksize = + reply_p->data[3]; + dev_p->sdiocard[bus].card.card_capability = + (uint8_t)((reply_p->data[4]&0xff00)>>8); + dev_p->sdiocard[bus].card.sdio_version = + (uint8_t)(reply_p->data[4]&0x00ff); + dev_p->sdiocard[bus].function_init_map = 0x01; + data_p->num_functions = + dev_p->sdiocard[bus].card.num_functions; + data_p->memory_present = + dev_p->sdiocard[bus].card.memory_present; + data_p->manufacturer__id = + dev_p->sdiocard[bus].card.manufacturer__id; + data_p->manufacturer_info = + dev_p->sdiocard[bus].card.manufacturer_info; + data_p->blocksize = dev_p->sdiocard[bus].card.blocksize; + data_p->maxblocksize = + dev_p->sdiocard[bus].card.maxblocksize; + data_p->card_capability = + dev_p->sdiocard[bus].card.card_capability; + data_p->sdio_version = + dev_p->sdiocard[bus].card.sdio_version; + } else { + if (resp_type == CY_RESP_SUCCESS_FAILURE) + ret = cy_as_ll_request_response__get_word(reply_p, 0); + else + ret = CY_AS_ERROR_INVALID_RESPONSE; + } +destroy: + if (req_p != 0) + cy_as_ll_destroy_request(dev_p, req_p); + if (reply_p != 0) + cy_as_ll_destroy_response(dev_p, reply_p); + return ret; +} + +/*Reset SDIO card. */ +cy_as_return_status_t +cy_as_sdio_reset_card( + cy_as_device_handle handle, + cy_as_bus_number_t bus, + uint32_t device) +{ + + cy_as_ll_request_response *req_p , *reply_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + uint8_t resp_type; + cy_as_device *dev_p = (cy_as_device *)handle; + + ret = cy_as_sdio_device_check(dev_p, bus, device); + + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (dev_p->sdiocard != 0) { + dev_p->sdiocard[bus].function_init_map = 0; + dev_p->sdiocard[bus].function_suspended_map = 0; + } + + req_p = cy_as_ll_create_request(dev_p, CY_RQT_SDIO_RESET_DEV, + CY_RQT_STORAGE_RQT_CONTEXT, 1); + + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + /*Setup mailbox */ + cy_as_ll_request_response__set_word(req_p, 0, + create_address(bus, (uint8_t)device, 0)); + + reply_p = cy_as_ll_create_response(dev_p, 2); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + ret = cy_as_ll_send_request_wait_reply(dev_p, + req_p, reply_p); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + resp_type = cy_as_ll_request_response__get_code(reply_p); + + if (resp_type == CY_RESP_SUCCESS_FAILURE) { + ret = cy_as_ll_request_response__get_word(reply_p, 0); + if (ret == CY_AS_ERROR_SUCCESS) + ret = cy_as_sdio_query_card(handle, bus, device, 0); + } else + ret = CY_AS_ERROR_INVALID_RESPONSE; + +destroy: + if (req_p != 0) + cy_as_ll_destroy_request(dev_p, req_p); + if (reply_p != 0) + cy_as_ll_destroy_response(dev_p, reply_p); + return ret; +} + +/* Initialise an IO function*/ +cy_as_return_status_t +cy_as_sdio_init_function( + cy_as_device_handle handle, + cy_as_bus_number_t bus, + uint32_t device, + uint8_t n_function_no, + uint8_t misc_buf) +{ + cy_as_ll_request_response *req_p , *reply_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + uint8_t resp_type; + cy_as_device *dev_p = (cy_as_device *)handle; + + ret = cy_as_sdio_device_check(dev_p, bus, device); + + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (!(cy_as_sdio_check_function_initialized + (handle, bus, 0))) + return CY_AS_ERROR_NOT_RUNNING; + + if ((cy_as_sdio_check_function_initialized + (handle, bus, n_function_no))) { + if (misc_buf&CY_SDIO_FORCE_INIT) + dev_p->sdiocard[bus].function_init_map &= + (~(1 << n_function_no)); + else + return CY_AS_ERROR_ALREADY_RUNNING; + } + + req_p = cy_as_ll_create_request(dev_p, + CY_RQT_SDIO_INIT_FUNCTION, CY_RQT_STORAGE_RQT_CONTEXT, 1); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + cy_as_ll_request_response__set_word(req_p, 0, + create_address(bus, (uint8_t)device, n_function_no)); + + reply_p = cy_as_ll_create_response(dev_p, 5); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + resp_type = cy_as_ll_request_response__get_code(reply_p); + + if (resp_type == CY_RESP_SDIO_INIT_FUNCTION) { + dev_p->sdiocard[bus].function[n_function_no-1].function_code = + (uint8_t)((reply_p->data[0]&0xff00)>>8); + dev_p->sdiocard[bus].function[n_function_no-1]. + extended_func_code = (uint8_t)reply_p->data[0]&0x00ff; + dev_p->sdiocard[bus].function[n_function_no-1].blocksize = + reply_p->data[1]; + dev_p->sdiocard[bus].function[n_function_no-1]. + maxblocksize = reply_p->data[1]; + dev_p->sdiocard[bus].function[n_function_no-1].card_psn = + (uint32_t)(reply_p->data[2])<<16; + dev_p->sdiocard[bus].function[n_function_no-1].card_psn |= + (uint32_t)(reply_p->data[3]); + dev_p->sdiocard[bus].function[n_function_no-1].csa_bits = + (uint8_t)((reply_p->data[4]&0xff00)>>8); + dev_p->sdiocard[bus].function[n_function_no-1].wakeup_support = + (uint8_t)(reply_p->data[4]&0x0001); + dev_p->sdiocard[bus].function_init_map |= (1 << n_function_no); + cy_as_sdio_clear_function_suspended(handle, bus, n_function_no); + + } else { + if (resp_type == CY_RESP_SUCCESS_FAILURE) + ret = cy_as_ll_request_response__get_word(reply_p, 0); + else + ret = CY_AS_ERROR_INVALID_FUNCTION; + } + +destroy: + if (req_p != 0) + cy_as_ll_destroy_request(dev_p, req_p); + if (reply_p != 0) + cy_as_ll_destroy_response(dev_p, reply_p); + return ret; +} + +/*Query individual functions. */ +cy_as_return_status_t +cy_as_sdio_query_function( + cy_as_device_handle handle, + cy_as_bus_number_t bus, + uint32_t device, + uint8_t n_function_no, + cy_as_sdio_func *data_p) +{ + cy_as_device *dev_p = (cy_as_device *)handle; + cy_as_return_status_t ret; + + ret = cy_as_sdio_device_check(dev_p, bus, device); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (!(cy_as_sdio_check_function_initialized(handle, + bus, n_function_no))) + return CY_AS_ERROR_INVALID_FUNCTION; + + data_p->blocksize = + dev_p->sdiocard[bus].function[n_function_no-1].blocksize; + data_p->card_psn = + dev_p->sdiocard[bus].function[n_function_no-1].card_psn; + data_p->csa_bits = + dev_p->sdiocard[bus].function[n_function_no-1].csa_bits; + data_p->extended_func_code = + dev_p->sdiocard[bus].function[n_function_no-1]. + extended_func_code; + data_p->function_code = + dev_p->sdiocard[bus].function[n_function_no-1].function_code; + data_p->maxblocksize = + dev_p->sdiocard[bus].function[n_function_no-1].maxblocksize; + data_p->wakeup_support = + dev_p->sdiocard[bus].function[n_function_no-1].wakeup_support; + + return CY_AS_ERROR_SUCCESS; +} + +/* Abort the Current Extended IO Operation*/ +cy_as_return_status_t +cy_as_sdio_abort_function( + cy_as_device_handle handle, + cy_as_bus_number_t bus, + uint32_t device, + uint8_t n_function_no) +{ + cy_as_ll_request_response *req_p , *reply_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + uint8_t resp_type; + cy_as_device *dev_p = (cy_as_device *)handle; + + ret = cy_as_sdio_device_check(dev_p, bus, device); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (!(cy_as_sdio_check_function_initialized(handle, + bus, n_function_no))) + return CY_AS_ERROR_INVALID_FUNCTION; + + if ((cy_as_device_is_storage_async_pending(dev_p)) || + (dev_p->storage_wait)) { + if (!(cy_as_sdio_get_card_capability(handle, bus) & + CY_SDIO_SDC)) + return CY_AS_ERROR_INVALID_COMMAND; + } + + req_p = cy_as_ll_create_request(dev_p, CY_RQT_SDIO_ABORT_IO, + CY_RQT_GENERAL_RQT_CONTEXT, 1); + + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + /*Setup mailbox */ + cy_as_ll_request_response__set_word(req_p, 0, + create_address(bus, (uint8_t)device, n_function_no)); + + reply_p = cy_as_ll_create_response(dev_p, 2); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + resp_type = cy_as_ll_request_response__get_code(reply_p); + + if (resp_type == CY_RESP_SUCCESS_FAILURE) + ret = cy_as_ll_request_response__get_word(reply_p, 0); + else + ret = CY_AS_ERROR_INVALID_RESPONSE; + + +destroy: + if (req_p != 0) + cy_as_ll_destroy_request(dev_p, req_p); + if (reply_p != 0) + cy_as_ll_destroy_response(dev_p, reply_p); + return ret; +} + +/* Suspend IO to current function*/ +cy_as_return_status_t +cy_as_sdio_suspend( + cy_as_device_handle handle, + cy_as_bus_number_t bus, + uint32_t device, + uint8_t n_function_no) +{ + cy_as_ll_request_response *req_p , *reply_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + cy_as_device *dev_p = (cy_as_device *)handle; + + ret = cy_as_sdio_device_check(dev_p, bus, device); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (!(cy_as_sdio_check_function_initialized(handle, bus, + n_function_no))) + return CY_AS_ERROR_INVALID_FUNCTION; + if (!(cy_as_sdio_check_support_bus_suspend(handle, bus))) + return CY_AS_ERROR_INVALID_FUNCTION; + if (!(cy_as_sdio_get_card_capability(handle, bus) & CY_SDIO_SDC)) + return CY_AS_ERROR_INVALID_FUNCTION; + if (cy_as_sdio_check_function_suspended(handle, bus, n_function_no)) + return CY_AS_ERROR_FUNCTION_SUSPENDED; + + req_p = cy_as_ll_create_request(dev_p, + CY_RQT_SDIO_SUSPEND, CY_RQT_GENERAL_RQT_CONTEXT, 1); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + /*Setup mailbox */ + cy_as_ll_request_response__set_word(req_p, 0, + create_address(bus, (uint8_t)device, n_function_no)); + + reply_p = cy_as_ll_create_response(dev_p, 2); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + + if (ret == CY_AS_ERROR_SUCCESS) { + ret = cy_as_ll_request_response__get_code(reply_p); + cy_as_sdio_set_function_suspended(handle, bus, n_function_no); + } + + if (req_p != 0) + cy_as_ll_destroy_request(dev_p, req_p); + if (reply_p != 0) + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +/*Resume suspended function*/ +cy_as_return_status_t +cy_as_sdio_resume( + cy_as_device_handle handle, + cy_as_bus_number_t bus, + uint32_t device, + uint8_t n_function_no, + cy_as_oper_type op, + uint8_t misc_buf, + uint16_t pendingblockcount, + uint8_t *data_p + ) +{ + cy_as_ll_request_response *req_p , *reply_p; + cy_as_return_status_t resp_data, ret = CY_AS_ERROR_SUCCESS; + cy_as_device *dev_p = (cy_as_device *)handle; + + ret = cy_as_sdio_device_check(dev_p, bus, device); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (!(cy_as_sdio_check_function_initialized + (handle, bus, n_function_no))) + return CY_AS_ERROR_INVALID_FUNCTION; + + /* If suspend resume is not supported return */ + if (!(cy_as_sdio_check_support_bus_suspend(handle, bus))) + return CY_AS_ERROR_INVALID_FUNCTION; + + /* if the function is not suspended return. */ + if (!(cy_as_sdio_check_function_suspended + (handle, bus, n_function_no))) + return CY_AS_ERROR_INVALID_FUNCTION; + + req_p = cy_as_ll_create_request(dev_p, + CY_RQT_SDIO_RESUME, CY_RQT_STORAGE_RQT_CONTEXT, 1); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + /*Setup mailbox */ + cy_as_ll_request_response__set_word(req_p, 0, + create_address(bus, (uint8_t)device, n_function_no)); + + reply_p = cy_as_ll_create_response(dev_p, 2); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + if (cy_as_ll_request_response__get_code(reply_p) == + CY_RESP_SDIO_RESUME) { + resp_data = cy_as_ll_request_response__get_word(reply_p, 0); + if (resp_data & 0x00ff) { + /* Send extended read request to resume the read. */ + if (op == cy_as_op_read) { + ret = cy_as_sdio_extended_i_o(handle, bus, + device, n_function_no, 0, misc_buf, + pendingblockcount, cy_false, data_p, 1); + } else { + ret = cy_as_sdio_extended_i_o(handle, bus, + device, n_function_no, 0, misc_buf, + pendingblockcount, cy_true, data_p, 1); + } + } else { + ret = CY_AS_ERROR_SUCCESS; + } + } else { + ret = CY_AS_ERROR_INVALID_RESPONSE; + } + +destroy: + cy_as_sdio_clear_function_suspended(handle, bus, n_function_no); + if (req_p != 0) + cy_as_ll_destroy_request(dev_p, req_p); + if (reply_p != 0) + cy_as_ll_destroy_response(dev_p, reply_p); + return ret; + +} + +/*Set function blocksize. Size cannot exceed max + * block size for the function*/ +cy_as_return_status_t +cy_as_sdio_set_blocksize( + cy_as_device_handle handle, + cy_as_bus_number_t bus, + uint32_t device, + uint8_t n_function_no, + uint16_t blocksize) +{ + cy_as_return_status_t ret; + cy_as_device *dev_p = (cy_as_device *)handle; + ret = cy_as_sdio_device_check(dev_p, bus, device); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (!(cy_as_sdio_check_function_initialized + (handle, bus, n_function_no))) + return CY_AS_ERROR_INVALID_FUNCTION; + if (n_function_no == 0) { + if (blocksize > cy_as_sdio_get_card_max_blocksize(handle, bus)) + return CY_AS_ERROR_INVALID_BLOCKSIZE; + else if (blocksize == cy_as_sdio_get_card_blocksize + (handle, bus)) + return CY_AS_ERROR_SUCCESS; + } else { + if (blocksize > + cy_as_sdio_get_function_max_blocksize(handle, + bus, n_function_no)) + return CY_AS_ERROR_INVALID_BLOCKSIZE; + else if (blocksize == + cy_as_sdio_get_function_blocksize(handle, + bus, n_function_no)) + return CY_AS_ERROR_SUCCESS; + } + + ret = cy_as_sdio_direct_write(handle, bus, device, 0, + (uint16_t)(n_function_no << 8) | + 0x10, 0, blocksize & 0x00ff, 0); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + ret = cy_as_sdio_direct_write(handle, bus, device, 0, + (uint16_t)(n_function_no << 8) | + 0x11, 0, (blocksize & 0xff00) >> 8, 0); + + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (n_function_no == 0) + cy_as_sdio_set_card_block_size(handle, bus, blocksize); + else + cy_as_sdio_set_function_block_size(handle, + bus, n_function_no, blocksize); + return ret; +} + +/* Deinitialize an SDIO function*/ +cy_as_return_status_t +cy_as_sdio_de_init_function( + cy_as_device_handle handle, + cy_as_bus_number_t bus, + uint32_t device, + uint8_t n_function_no) +{ + cy_as_return_status_t ret; + uint8_t temp; + + if (n_function_no == 0) + return CY_AS_ERROR_INVALID_FUNCTION; + + ret = cy_as_sdio_device_check((cy_as_device *)handle, bus, device); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (!(cy_as_sdio_check_function_initialized + (handle, bus, n_function_no))) + return CY_AS_ERROR_SUCCESS; + + temp = (uint8_t)(((cy_as_device *)handle)->sdiocard[bus]. + function_init_map & (~(1 << n_function_no))); + + cy_as_sdio_direct_write(handle, bus, device, 0, 0x02, 0, temp, 0); + + ((cy_as_device *)handle)->sdiocard[bus].function_init_map &= + (~(1 << n_function_no)); + + return CY_AS_ERROR_SUCCESS; +} + + +/*[]*/ diff --git a/drivers/staging/westbridge/astoria/api/src/cyasusb.c b/drivers/staging/westbridge/astoria/api/src/cyasusb.c new file mode 100644 index 000000000000..5a2197012065 --- /dev/null +++ b/drivers/staging/westbridge/astoria/api/src/cyasusb.c @@ -0,0 +1,3718 @@ +/* Cypress West Bridge API source file (cyasusb.c) +## =========================== +## Copyright (C) 2010 Cypress Semiconductor +## +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either version 2 +## of the License, or (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 51 Franklin Street, Fifth Floor +## Boston, MA 02110-1301, USA. +## =========================== +*/ + +#include "../../include/linux/westbridge/cyashal.h" +#include "../../include/linux/westbridge/cyasusb.h" +#include "../../include/linux/westbridge/cyaserr.h" +#include "../../include/linux/westbridge/cyasdma.h" +#include "../../include/linux/westbridge/cyaslowlevel.h" +#include "../../include/linux/westbridge/cyaslep2pep.h" +#include "../../include/linux/westbridge/cyasregs.h" +#include "../../include/linux/westbridge/cyasstorage.h" + +static cy_as_return_status_t +cy_as_usb_ack_setup_packet( + /* Handle to the West Bridge device */ + cy_as_device_handle handle, + /* The callback if async call */ + cy_as_function_callback cb, + /* Client supplied data */ + uint32_t client + ); + +static void +cy_as_usb_func_callback( + cy_as_device *dev_p, + uint8_t context, + cy_as_ll_request_response *rqt, + cy_as_ll_request_response *resp, + cy_as_return_status_t ret); +/* +* Reset the USB EP0 state +*/ +static void +cy_as_usb_reset_e_p0_state(cy_as_device *dev_p) +{ + cy_as_log_debug_message(6, "cy_as_usb_reset_e_p0_state called"); + + cy_as_device_clear_ack_delayed(dev_p); + cy_as_device_clear_setup_packet(dev_p); + if (cy_as_device_is_usb_async_pending(dev_p, 0)) + cy_as_usb_cancel_async((cy_as_device_handle)dev_p, 0); + + dev_p->usb_pending_buffer = 0; +} + +/* +* External function to map logical endpoints to physical endpoints +*/ +static cy_as_return_status_t +is_usb_active(cy_as_device *dev_p) +{ + if (!cy_as_device_is_configured(dev_p)) + return CY_AS_ERROR_NOT_CONFIGURED; + + if (!cy_as_device_is_firmware_loaded(dev_p)) + return CY_AS_ERROR_NO_FIRMWARE; + + if (dev_p->usb_count == 0) + return CY_AS_ERROR_NOT_RUNNING; + + if (cy_as_device_is_in_suspend_mode(dev_p)) + return CY_AS_ERROR_IN_SUSPEND; + + return CY_AS_ERROR_SUCCESS; +} + +static void +usb_ack_callback(cy_as_device_handle h, + cy_as_return_status_t status, + uint32_t client, + cy_as_funct_c_b_type type, + void *data) +{ + cy_as_device *dev_p = (cy_as_device *)h; + + (void)client; + (void)status; + (void)data; + + cy_as_hal_assert(type == CY_FUNCT_CB_NODATA); + + if (dev_p->usb_pending_buffer) { + cy_as_usb_io_callback cb; + + cb = dev_p->usb_cb[0]; + dev_p->usb_cb[0] = 0; + cy_as_device_clear_usb_async_pending(dev_p, 0); + if (cb) + cb(h, 0, dev_p->usb_pending_size, + dev_p->usb_pending_buffer, dev_p->usb_error); + + dev_p->usb_pending_buffer = 0; + } + + cy_as_device_clear_setup_packet(dev_p); +} + +static void +my_usb_request_callback_usb_event(cy_as_device *dev_p, + cy_as_ll_request_response *req_p) +{ + uint16_t ev; + uint16_t val; + cy_as_device_handle h = (cy_as_device_handle)dev_p; + + ev = cy_as_ll_request_response__get_word(req_p, 0); + switch (ev) { + case 0: /* Reserved */ + cy_as_ll_send_status_response(dev_p, CY_RQT_USB_RQT_CONTEXT, + CY_AS_ERROR_INVALID_REQUEST, 0); + break; + + case 1: /* Reserved */ + cy_as_ll_send_status_response(dev_p, CY_RQT_USB_RQT_CONTEXT, + CY_AS_ERROR_INVALID_REQUEST, 0); + break; + + case 2: /* USB Suspend */ + dev_p->usb_last_event = cy_as_event_usb_suspend; + if (dev_p->usb_event_cb_ms) + dev_p->usb_event_cb_ms(h, cy_as_event_usb_suspend, 0); + else if (dev_p->usb_event_cb) + dev_p->usb_event_cb(h, cy_as_event_usb_suspend, 0); + cy_as_ll_send_status_response(dev_p, + CY_RQT_USB_RQT_CONTEXT, CY_AS_ERROR_SUCCESS, 0); + break; + + case 3: /* USB Resume */ + dev_p->usb_last_event = cy_as_event_usb_resume; + if (dev_p->usb_event_cb_ms) + dev_p->usb_event_cb_ms(h, cy_as_event_usb_resume, 0); + else if (dev_p->usb_event_cb) + dev_p->usb_event_cb(h, cy_as_event_usb_resume, 0); + cy_as_ll_send_status_response(dev_p, + CY_RQT_USB_RQT_CONTEXT, CY_AS_ERROR_SUCCESS, 0); + break; + + case 4: /* USB Reset */ + /* + * if we get a USB reset, the USB host did not understand + * our response or we timed out for some reason. reset + * our internal state to be ready for another set of + * enumeration based requests. + */ + if (cy_as_device_is_ack_delayed(dev_p)) + cy_as_usb_reset_e_p0_state(dev_p); + + dev_p->usb_last_event = cy_as_event_usb_reset; + if (dev_p->usb_event_cb_ms) + dev_p->usb_event_cb_ms(h, cy_as_event_usb_reset, 0); + else if (dev_p->usb_event_cb) + dev_p->usb_event_cb(h, cy_as_event_usb_reset, 0); + + cy_as_ll_send_status_response(dev_p, + CY_RQT_USB_RQT_CONTEXT, CY_AS_ERROR_SUCCESS, 0); + cy_as_device_clear_usb_high_speed(dev_p); + cy_as_usb_set_dma_sizes(dev_p); + dev_p->usb_max_tx_size = 0x40; + cy_as_dma_set_max_dma_size(dev_p, 0x06, 0x40); + break; + + case 5: /* USB Set Configuration */ + /* The configuration to set */ + val = cy_as_ll_request_response__get_word(req_p, 1); + dev_p->usb_last_event = cy_as_event_usb_set_config; + if (dev_p->usb_event_cb_ms) + dev_p->usb_event_cb_ms(h, + cy_as_event_usb_set_config, &val); + else if (dev_p->usb_event_cb) + dev_p->usb_event_cb(h, + cy_as_event_usb_set_config, &val); + + cy_as_ll_send_status_response(dev_p, + CY_RQT_USB_RQT_CONTEXT, CY_AS_ERROR_SUCCESS, 0); + break; + + case 6: /* USB Speed change */ + /* Connect speed */ + val = cy_as_ll_request_response__get_word(req_p, 1); + dev_p->usb_last_event = cy_as_event_usb_speed_change; + if (dev_p->usb_event_cb_ms) + dev_p->usb_event_cb_ms(h, + cy_as_event_usb_speed_change, &val); + else if (dev_p->usb_event_cb) + dev_p->usb_event_cb(h, + cy_as_event_usb_speed_change, &val); + + cy_as_ll_send_status_response(dev_p, + CY_RQT_USB_RQT_CONTEXT, CY_AS_ERROR_SUCCESS, 0); + cy_as_device_set_usb_high_speed(dev_p); + cy_as_usb_set_dma_sizes(dev_p); + dev_p->usb_max_tx_size = 0x200; + cy_as_dma_set_max_dma_size(dev_p, 0x06, 0x200); + break; + + case 7: /* USB Clear Feature */ + /* EP Number */ + val = cy_as_ll_request_response__get_word(req_p, 1); + if (dev_p->usb_event_cb_ms) + dev_p->usb_event_cb_ms(h, + cy_as_event_usb_clear_feature, &val); + if (dev_p->usb_event_cb) + dev_p->usb_event_cb(h, + cy_as_event_usb_clear_feature, &val); + + cy_as_ll_send_status_response(dev_p, + CY_RQT_USB_RQT_CONTEXT, CY_AS_ERROR_SUCCESS, 0); + break; + + default: + cy_as_hal_print_message("invalid event type\n"); + cy_as_ll_send_data_response(dev_p, CY_RQT_USB_RQT_CONTEXT, + CY_RESP_USB_INVALID_EVENT, sizeof(ev), &ev); + break; + } +} + +static void +my_usb_request_callback_usb_data(cy_as_device *dev_p, + cy_as_ll_request_response *req_p) +{ + cy_as_end_point_number_t ep; + uint8_t type; + uint16_t len; + uint16_t val; + cy_as_device_handle h = (cy_as_device_handle)dev_p; + + val = cy_as_ll_request_response__get_word(req_p, 0); + ep = (cy_as_end_point_number_t)((val >> 13) & 0x01); + len = (val & 0x1ff); + + cy_as_hal_assert(len <= 64); + cy_as_ll_request_response__unpack(req_p, + 1, len, dev_p->usb_ep_data); + + type = (uint8_t)((val >> 14) & 0x03); + if (type == 0) { + if (cy_as_device_is_ack_delayed(dev_p)) { + /* + * A setup packet has arrived while we are + * processing a previous setup packet. reset + * our state with respect to EP0 to be ready + * to process the new packet. + */ + cy_as_usb_reset_e_p0_state(dev_p); + } + + if (len != 8) + cy_as_ll_send_status_response(dev_p, + CY_RQT_USB_RQT_CONTEXT, + CY_AS_ERROR_INVALID_REQUEST, 0); + else { + cy_as_device_clear_ep0_stalled(dev_p); + cy_as_device_set_setup_packet(dev_p); + cy_as_ll_send_status_response(dev_p, + CY_RQT_USB_RQT_CONTEXT, + CY_AS_ERROR_SUCCESS, 0); + + if (dev_p->usb_event_cb_ms) + dev_p->usb_event_cb_ms(h, + cy_as_event_usb_setup_packet, + dev_p->usb_ep_data); + else + dev_p->usb_event_cb(h, + cy_as_event_usb_setup_packet, + dev_p->usb_ep_data); + + if ((!cy_as_device_is_ack_delayed(dev_p)) && + (!cy_as_device_is_ep0_stalled(dev_p))) + cy_as_usb_ack_setup_packet(h, + usb_ack_callback, 0); + } + } else if (type == 2) { + if (len != 0) + cy_as_ll_send_status_response(dev_p, + CY_RQT_USB_RQT_CONTEXT, + CY_AS_ERROR_INVALID_REQUEST, 0); + else { + if (dev_p->usb_event_cb_ms) + dev_p->usb_event_cb_ms(h, + cy_as_event_usb_status_packet, 0); + else + dev_p->usb_event_cb(h, + cy_as_event_usb_status_packet, 0); + + cy_as_ll_send_status_response(dev_p, + CY_RQT_USB_RQT_CONTEXT, + CY_AS_ERROR_SUCCESS, 0); + } + } else if (type == 1) { + /* + * we need to hand the data associated with these + * endpoints to the DMA module. + */ + cy_as_dma_received_data(dev_p, ep, len, dev_p->usb_ep_data); + cy_as_ll_send_status_response(dev_p, + CY_RQT_USB_RQT_CONTEXT, CY_AS_ERROR_SUCCESS, 0); + } +} + +static void +my_usb_request_callback_inquiry(cy_as_device *dev_p, + cy_as_ll_request_response *req_p) +{ + cy_as_usb_inquiry_data_dep cbdata; + cy_as_usb_inquiry_data cbdata_ms; + void *data; + uint16_t val; + cy_as_device_handle h = (cy_as_device_handle)dev_p; + uint8_t def_inq_data[64]; + uint8_t evpd; + uint8_t codepage; + cy_bool updated; + uint16_t length; + + cy_as_bus_number_t bus; + uint32_t device; + cy_as_media_type media; + + val = cy_as_ll_request_response__get_word(req_p, 0); + bus = cy_as_storage_get_bus_from_address(val); + device = cy_as_storage_get_device_from_address(val); + media = cy_as_storage_get_media_from_address(val); + + val = cy_as_ll_request_response__get_word(req_p, 1); + evpd = (uint8_t)((val >> 8) & 0x01); + codepage = (uint8_t)(val & 0xff); + + length = cy_as_ll_request_response__get_word(req_p, 2); + data = (void *)def_inq_data; + + updated = cy_false; + + if (dev_p->usb_event_cb_ms) { + cbdata_ms.bus = bus; + cbdata_ms.device = device; + cbdata_ms.updated = updated; + cbdata_ms.evpd = evpd; + cbdata_ms.codepage = codepage; + cbdata_ms.length = length; + cbdata_ms.data = data; + + cy_as_hal_assert(cbdata_ms.length <= sizeof(def_inq_data)); + cy_as_ll_request_response__unpack(req_p, + 3, cbdata_ms.length, cbdata_ms.data); + + dev_p->usb_event_cb_ms(h, + cy_as_event_usb_inquiry_before, &cbdata_ms); + + updated = cbdata_ms.updated; + data = cbdata_ms.data; + length = cbdata_ms.length; + } else if (dev_p->usb_event_cb) { + cbdata.media = media; + cbdata.updated = updated; + cbdata.evpd = evpd; + cbdata.codepage = codepage; + cbdata.length = length; + cbdata.data = data; + + cy_as_hal_assert(cbdata.length <= + sizeof(def_inq_data)); + cy_as_ll_request_response__unpack(req_p, 3, + cbdata.length, cbdata.data); + + dev_p->usb_event_cb(h, + cy_as_event_usb_inquiry_before, &cbdata); + + updated = cbdata.updated; + data = cbdata.data; + length = cbdata.length; + } + + if (updated && length > 192) + cy_as_hal_print_message("an inquiry result from a " + "cy_as_event_usb_inquiry_before event " + "was greater than 192 bytes."); + + /* Now send the reply with the data back + * to the West Bridge device */ + if (updated && length <= 192) { + /* + * the callback function modified the inquiry + * data, ship the data back to the west bridge firmware. + */ + cy_as_ll_send_data_response(dev_p, + CY_RQT_USB_RQT_CONTEXT, + CY_RESP_INQUIRY_DATA, length, data); + } else { + /* + * the callback did not modify the data, just acknowledge + * that we processed the request + */ + cy_as_ll_send_status_response(dev_p, + CY_RQT_USB_RQT_CONTEXT, CY_AS_ERROR_SUCCESS, 1); + } + + if (dev_p->usb_event_cb_ms) + dev_p->usb_event_cb_ms(h, + cy_as_event_usb_inquiry_after, &cbdata_ms); + else if (dev_p->usb_event_cb) + dev_p->usb_event_cb(h, + cy_as_event_usb_inquiry_after, &cbdata); +} + +static void +my_usb_request_callback_start_stop(cy_as_device *dev_p, + cy_as_ll_request_response *req_p) +{ + cy_as_bus_number_t bus; + cy_as_media_type media; + uint32_t device; + uint16_t val; + + if (dev_p->usb_event_cb_ms || dev_p->usb_event_cb) { + cy_bool loej; + cy_bool start; + cy_as_device_handle h = (cy_as_device_handle)dev_p; + + val = cy_as_ll_request_response__get_word(req_p, 0); + bus = cy_as_storage_get_bus_from_address(val); + device = cy_as_storage_get_device_from_address(val); + media = cy_as_storage_get_media_from_address(val); + + val = cy_as_ll_request_response__get_word(req_p, 1); + loej = (val & 0x02) ? cy_true : cy_false; + start = (val & 0x01) ? cy_true : cy_false; + + if (dev_p->usb_event_cb_ms) { + cy_as_usb_start_stop_data cbdata_ms; + + cbdata_ms.bus = bus; + cbdata_ms.device = device; + cbdata_ms.loej = loej; + cbdata_ms.start = start; + dev_p->usb_event_cb_ms(h, + cy_as_event_usb_start_stop, &cbdata_ms); + + } else if (dev_p->usb_event_cb) { + cy_as_usb_start_stop_data_dep cbdata; + + cbdata.media = media; + cbdata.loej = loej; + cbdata.start = start; + dev_p->usb_event_cb(h, + cy_as_event_usb_start_stop, &cbdata); + } + } + cy_as_ll_send_status_response(dev_p, + CY_RQT_USB_RQT_CONTEXT, CY_AS_ERROR_SUCCESS, 1); +} + +static void +my_usb_request_callback_uknown_c_b_w(cy_as_device *dev_p, + cy_as_ll_request_response *req_p) +{ + uint16_t val; + cy_as_device_handle h = (cy_as_device_handle)dev_p; + uint8_t buf[16]; + + uint8_t response[4]; + uint16_t reqlen; + void *request; + uint8_t status; + uint8_t key; + uint8_t asc; + uint8_t ascq; + + val = cy_as_ll_request_response__get_word(req_p, 0); + /* Failed by default */ + status = 1; + /* Invalid command */ + key = 0x05; + /* Invalid command */ + asc = 0x20; + /* Invalid command */ + ascq = 0x00; + reqlen = cy_as_ll_request_response__get_word(req_p, 1); + request = buf; + + cy_as_hal_assert(reqlen <= sizeof(buf)); + cy_as_ll_request_response__unpack(req_p, 2, reqlen, request); + + if (dev_p->usb_event_cb_ms) { + cy_as_usb_unknown_command_data cbdata_ms; + cbdata_ms.bus = cy_as_storage_get_bus_from_address(val); + cbdata_ms.device = + cy_as_storage_get_device_from_address(val); + cbdata_ms.reqlen = reqlen; + cbdata_ms.request = request; + cbdata_ms.status = status; + cbdata_ms.key = key; + cbdata_ms.asc = asc; + cbdata_ms.ascq = ascq; + + dev_p->usb_event_cb_ms(h, + cy_as_event_usb_unknown_storage, &cbdata_ms); + status = cbdata_ms.status; + key = cbdata_ms.key; + asc = cbdata_ms.asc; + ascq = cbdata_ms.ascq; + } else if (dev_p->usb_event_cb) { + cy_as_usb_unknown_command_data_dep cbdata; + cbdata.media = + cy_as_storage_get_media_from_address(val); + cbdata.reqlen = reqlen; + cbdata.request = request; + cbdata.status = status; + cbdata.key = key; + cbdata.asc = asc; + cbdata.ascq = ascq; + + dev_p->usb_event_cb(h, + cy_as_event_usb_unknown_storage, &cbdata); + status = cbdata.status; + key = cbdata.key; + asc = cbdata.asc; + ascq = cbdata.ascq; + } + + response[0] = status; + response[1] = key; + response[2] = asc; + response[3] = ascq; + cy_as_ll_send_data_response(dev_p, CY_RQT_USB_RQT_CONTEXT, + CY_RESP_UNKNOWN_SCSI_COMMAND, sizeof(response), response); +} + +static void +my_usb_request_callback_m_s_c_progress(cy_as_device *dev_p, + cy_as_ll_request_response *req_p) +{ + uint16_t val1, val2; + cy_as_device_handle h = (cy_as_device_handle)dev_p; + + if ((dev_p->usb_event_cb) || (dev_p->usb_event_cb_ms)) { + cy_as_m_s_c_progress_data cbdata; + + val1 = cy_as_ll_request_response__get_word(req_p, 0); + val2 = cy_as_ll_request_response__get_word(req_p, 1); + cbdata.wr_count = (uint32_t)((val1 << 16) | val2); + + val1 = cy_as_ll_request_response__get_word(req_p, 2); + val2 = cy_as_ll_request_response__get_word(req_p, 3); + cbdata.rd_count = (uint32_t)((val1 << 16) | val2); + + if (dev_p->usb_event_cb) + dev_p->usb_event_cb(h, + cy_as_event_usb_m_s_c_progress, &cbdata); + else + dev_p->usb_event_cb_ms(h, + cy_as_event_usb_m_s_c_progress, &cbdata); + } + + cy_as_ll_send_status_response(dev_p, + CY_RQT_USB_RQT_CONTEXT, CY_AS_ERROR_SUCCESS, 0); +} + +/* +* This function processes the requests delivered from the +* firmware within the West Bridge device that are delivered +* in the USB context. These requests generally are EP0 and +* EP1 related requests or USB events. +*/ +static void +my_usb_request_callback(cy_as_device *dev_p, uint8_t context, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *resp_p, + cy_as_return_status_t ret) +{ + uint16_t val; + uint8_t code = cy_as_ll_request_response__get_code(req_p); + + (void)resp_p; + (void)context; + (void)ret; + + switch (code) { + case CY_RQT_USB_EVENT: + my_usb_request_callback_usb_event(dev_p, req_p); + break; + + case CY_RQT_USB_EP_DATA: + dev_p->usb_last_event = cy_as_event_usb_setup_packet; + my_usb_request_callback_usb_data(dev_p, req_p); + break; + + case CY_RQT_SCSI_INQUIRY_COMMAND: + dev_p->usb_last_event = cy_as_event_usb_inquiry_after; + my_usb_request_callback_inquiry(dev_p, req_p); + break; + + case CY_RQT_SCSI_START_STOP_COMMAND: + dev_p->usb_last_event = cy_as_event_usb_start_stop; + my_usb_request_callback_start_stop(dev_p, req_p); + break; + + case CY_RQT_SCSI_UNKNOWN_COMMAND: + dev_p->usb_last_event = cy_as_event_usb_unknown_storage; + my_usb_request_callback_uknown_c_b_w(dev_p, req_p); + break; + + case CY_RQT_USB_ACTIVITY_UPDATE: + dev_p->usb_last_event = cy_as_event_usb_m_s_c_progress; + my_usb_request_callback_m_s_c_progress(dev_p, req_p); + break; + + default: + cy_as_hal_print_message("invalid request " + "received on USB context\n"); + val = req_p->box0; + cy_as_ll_send_data_response(dev_p, CY_RQT_USB_RQT_CONTEXT, + CY_RESP_INVALID_REQUEST, sizeof(val), &val); + break; + } +} + +static cy_as_return_status_t +my_handle_response_usb_start(cy_as_device *dev_p, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *reply_p, + cy_as_return_status_t ret) +{ + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_SUCCESS_FAILURE) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + ret = cy_as_ll_request_response__get_word(reply_p, 0); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + /* + * mark EP 0 and EP1 as 64 byte endpoints + */ + cy_as_dma_set_max_dma_size(dev_p, 0, 64); + cy_as_dma_set_max_dma_size(dev_p, 1, 64); + + dev_p->usb_count++; + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + if (ret != CY_AS_ERROR_SUCCESS) { + cy_as_destroy_c_b_queue(dev_p->usb_func_cbs); + cy_as_ll_register_request_callback(dev_p, + CY_RQT_USB_RQT_CONTEXT, 0); + } + + cy_as_device_clear_u_s_s_pending(dev_p); + + return ret; + +} + +/* +* This function starts the USB stack. The stack is reference +* counted so if the stack is already started, this function +* just increments the count. If the stack has not been started, +* a start request is sent to the West Bridge device. +* +* Note: Starting the USB stack does not cause the USB signals +* to be connected to the USB pins. To do this and therefore +* initiate enumeration, CyAsUsbConnect() must be called. +*/ +cy_as_return_status_t +cy_as_usb_start(cy_as_device_handle handle, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_ll_request_response *req_p, *reply_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + + cy_as_device *dev_p; + + cy_as_log_debug_message(6, "cy_as_usb_start called"); + + dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + if (!cy_as_device_is_configured(dev_p)) + return CY_AS_ERROR_NOT_CONFIGURED; + + if (!cy_as_device_is_firmware_loaded(dev_p)) + return CY_AS_ERROR_NO_FIRMWARE; + + if (cy_as_device_is_in_suspend_mode(dev_p)) + return CY_AS_ERROR_IN_SUSPEND; + + if (cy_as_device_is_in_callback(dev_p)) + return CY_AS_ERROR_INVALID_IN_CALLBACK; + + if (cy_as_device_is_u_s_s_pending(dev_p)) + return CY_AS_ERROR_STARTSTOP_PENDING; + + cy_as_device_set_u_s_s_pending(dev_p); + + if (dev_p->usb_count == 0) { + /* + * since we are just starting the stack, + * mark USB as not connected to the remote host + */ + cy_as_device_clear_usb_connected(dev_p); + dev_p->usb_phy_config = 0; + + /* Queue for 1.0 Async Requests, kept for + * backwards compatibility */ + dev_p->usb_func_cbs = cy_as_create_c_b_queue(CYAS_USB_FUNC_CB); + if (dev_p->usb_func_cbs == 0) { + cy_as_device_clear_u_s_s_pending(dev_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + /* Reset the EP0 state */ + cy_as_usb_reset_e_p0_state(dev_p); + + /* + * we register here becuase the start request may cause + * events to occur before the response to the start request. + */ + cy_as_ll_register_request_callback(dev_p, + CY_RQT_USB_RQT_CONTEXT, my_usb_request_callback); + + /* Create the request to send to the West Bridge device */ + req_p = cy_as_ll_create_request(dev_p, + CY_RQT_START_USB, CY_RQT_USB_RQT_CONTEXT, 0); + if (req_p == 0) { + cy_as_destroy_c_b_queue(dev_p->usb_func_cbs); + dev_p->usb_func_cbs = 0; + cy_as_device_clear_u_s_s_pending(dev_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + /* Reserve space for the reply, the reply data + * will not exceed one word */ + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_destroy_c_b_queue(dev_p->usb_func_cbs); + dev_p->usb_func_cbs = 0; + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_device_clear_u_s_s_pending(dev_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, + req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return my_handle_response_usb_start(dev_p, + req_p, reply_p, ret); + } else { + ret = cy_as_misc_send_request(dev_p, cb, + client, CY_FUNCT_CB_USB_START, 0, + dev_p->func_cbs_usb, + CY_AS_REQUEST_RESPONSE_EX, req_p, reply_p, + cy_as_usb_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + } else { + dev_p->usb_count++; + if (cb) + cb(handle, ret, client, CY_FUNCT_CB_USB_START, 0); + } + + cy_as_device_clear_u_s_s_pending(dev_p); + + return ret; +} + +void +cy_as_usb_reset(cy_as_device *dev_p) +{ + int i; + + cy_as_device_clear_usb_connected(dev_p); + + for (i = 0; i < sizeof(dev_p->usb_config) / + sizeof(dev_p->usb_config[0]); i++) { + /* + * cancel all pending USB read/write operations, as it is + * possible that the USB stack comes up in a different + * configuration with a different set of endpoints. + */ + if (cy_as_device_is_usb_async_pending(dev_p, i)) + cy_as_usb_cancel_async(dev_p, + (cy_as_end_point_number_t)i); + + dev_p->usb_cb[i] = 0; + dev_p->usb_config[i].enabled = cy_false; + } + + dev_p->usb_phy_config = 0; +} + +/* + * This function does all the API side clean-up associated + * with CyAsUsbStop, without any communication with firmware. + * This needs to be done when the device is being reset while + * the USB stack is active. + */ +void +cy_as_usb_cleanup(cy_as_device *dev_p) +{ + if (dev_p->usb_count) { + cy_as_usb_reset_e_p0_state(dev_p); + cy_as_usb_reset(dev_p); + cy_as_hal_mem_set(dev_p->usb_config, 0, + sizeof(dev_p->usb_config)); + cy_as_destroy_c_b_queue(dev_p->usb_func_cbs); + + dev_p->usb_count = 0; + } +} + +static cy_as_return_status_t +my_handle_response_usb_stop(cy_as_device *dev_p, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *reply_p, + cy_as_return_status_t ret) +{ + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_SUCCESS_FAILURE) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + ret = cy_as_ll_request_response__get_word(reply_p, 0); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + /* + * we sucessfully shutdown the stack, so + * decrement to make the count zero. + */ + cy_as_usb_cleanup(dev_p); + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + if (ret != CY_AS_ERROR_SUCCESS) + cy_as_ll_register_request_callback(dev_p, + CY_RQT_USB_RQT_CONTEXT, 0); + + cy_as_device_clear_u_s_s_pending(dev_p); + + return ret; +} + +/* +* This function stops the USB stack. The USB stack is reference +* counted so first is reference count is decremented. If the +* reference count is then zero, a request is sent to the West +* Bridge device to stop the USB stack on the West Bridge device. +*/ +cy_as_return_status_t +cy_as_usb_stop(cy_as_device_handle handle, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_ll_request_response *req_p = 0, *reply_p = 0; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + + cy_as_device *dev_p; + + cy_as_log_debug_message(6, "cy_as_usb_stop called"); + + dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ret = is_usb_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (cy_as_device_is_usb_connected(dev_p)) + return CY_AS_ERROR_USB_CONNECTED; + + if (cy_as_device_is_in_callback(dev_p)) + return CY_AS_ERROR_INVALID_IN_CALLBACK; + + if (cy_as_device_is_u_s_s_pending(dev_p)) + return CY_AS_ERROR_STARTSTOP_PENDING; + + cy_as_device_set_u_s_s_pending(dev_p); + + if (dev_p->usb_count == 1) { + /* Create the request to send to the West Bridge device */ + req_p = cy_as_ll_create_request(dev_p, CY_RQT_STOP_USB, + CY_RQT_USB_RQT_CONTEXT, 0); + if (req_p == 0) { + ret = CY_AS_ERROR_OUT_OF_MEMORY; + goto destroy; + } + + /* Reserve space for the reply, the reply data will not + * exceed one word */ + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + ret = CY_AS_ERROR_OUT_OF_MEMORY; + goto destroy; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, + req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return my_handle_response_usb_stop(dev_p, + req_p, reply_p, ret); + } else { + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_USB_STOP, 0, dev_p->func_cbs_usb, + CY_AS_REQUEST_RESPONSE_EX, req_p, reply_p, + cy_as_usb_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + } else if (dev_p->usb_count > 1) { + /* + * reset all LE_ps to inactive state, after cleaning + * up any pending async read/write calls. + */ + cy_as_usb_reset(dev_p); + dev_p->usb_count--; + + if (cb) + cb(handle, ret, client, CY_FUNCT_CB_USB_STOP, 0); + } + + cy_as_device_clear_u_s_s_pending(dev_p); + + return ret; +} + +/* +* This function registers a callback to be called when +* USB events are processed +*/ +cy_as_return_status_t +cy_as_usb_register_callback(cy_as_device_handle handle, + cy_as_usb_event_callback callback) +{ + cy_as_device *dev_p; + + cy_as_log_debug_message(6, "cy_as_usb_register_callback called"); + + dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + if (!cy_as_device_is_configured(dev_p)) + return CY_AS_ERROR_NOT_CONFIGURED; + + if (!cy_as_device_is_firmware_loaded(dev_p)) + return CY_AS_ERROR_NO_FIRMWARE; + + dev_p->usb_event_cb = NULL; + dev_p->usb_event_cb_ms = callback; + return CY_AS_ERROR_SUCCESS; +} + + +static cy_as_return_status_t +my_handle_response_no_data(cy_as_device *dev_p, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *reply_p) +{ + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_SUCCESS_FAILURE) + ret = CY_AS_ERROR_INVALID_RESPONSE; + else + ret = cy_as_ll_request_response__get_word(reply_p, 0); + + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +static cy_as_return_status_t +my_handle_response_connect(cy_as_device *dev_p, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *reply_p, + cy_as_return_status_t ret) +{ + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_SUCCESS_FAILURE) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + ret = cy_as_ll_request_response__get_word(reply_p, 0); + if (ret == CY_AS_ERROR_SUCCESS) + cy_as_device_set_usb_connected(dev_p); + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + + +/* +* This method asks the West Bridge device to connect the +* internal USB D+ and D- signals to the USB pins, thus +* starting the enumeration processes if the external pins +* are connnected to a USB host. If the external pins are +* not connect to a USB host, enumeration will begin as soon +* as the USB pins are connected to a host. +*/ +cy_as_return_status_t +cy_as_usb_connect(cy_as_device_handle handle, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_ll_request_response *req_p , *reply_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + + cy_as_device *dev_p; + + cy_as_log_debug_message(6, "cy_as_usb_connect called"); + + dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ret = is_usb_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (cy_as_device_is_in_callback(dev_p)) + return CY_AS_ERROR_INVALID_IN_CALLBACK; + + /* Create the request to send to the West Bridge device */ + req_p = cy_as_ll_create_request(dev_p, + CY_RQT_SET_CONNECT_STATE, CY_RQT_USB_RQT_CONTEXT, 1); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + /* 1 = Connect request */ + cy_as_ll_request_response__set_word(req_p, 0, 1); + + /* Reserve space for the reply, the reply + * data will not exceed one word */ + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return my_handle_response_connect(dev_p, req_p, reply_p, ret); + } else { + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_USB_CONNECT, 0, dev_p->func_cbs_usb, + CY_AS_REQUEST_RESPONSE_EX, req_p, reply_p, + cy_as_usb_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +static cy_as_return_status_t +my_handle_response_disconnect(cy_as_device *dev_p, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *reply_p, + cy_as_return_status_t ret) +{ + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_SUCCESS_FAILURE) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + ret = cy_as_ll_request_response__get_word(reply_p, 0); + if (ret == CY_AS_ERROR_SUCCESS) + cy_as_device_clear_usb_connected(dev_p); + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} +/* +* This method forces a disconnect of the D+ and D- pins +* external to the West Bridge device from the D+ and D- +* signals internally, effectively disconnecting the West +* Bridge device from any connected USB host. +*/ +cy_as_return_status_t +cy_as_usb_disconnect(cy_as_device_handle handle, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_ll_request_response *req_p , *reply_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + + cy_as_device *dev_p; + + cy_as_log_debug_message(6, "cy_as_usb_disconnect called"); + + dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ret = is_usb_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (cy_as_device_is_in_callback(dev_p)) + return CY_AS_ERROR_INVALID_IN_CALLBACK; + + if (!cy_as_device_is_usb_connected(dev_p)) + return CY_AS_ERROR_USB_NOT_CONNECTED; + + /* Create the request to send to the West Bridge device */ + req_p = cy_as_ll_create_request(dev_p, + CY_RQT_SET_CONNECT_STATE, CY_RQT_USB_RQT_CONTEXT, 1); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + cy_as_ll_request_response__set_word(req_p, 0, 0); + + /* Reserve space for the reply, the reply + * data will not exceed two bytes */ + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return my_handle_response_disconnect(dev_p, + req_p, reply_p, ret); + } else { + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_USB_DISCONNECT, 0, dev_p->func_cbs_usb, + CY_AS_REQUEST_RESPONSE_EX, req_p, reply_p, + cy_as_usb_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return ret; + } +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +static cy_as_return_status_t +my_handle_response_set_enum_config(cy_as_device *dev_p, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *reply_p) +{ + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_SUCCESS_FAILURE) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + ret = cy_as_ll_request_response__get_word(reply_p, 0); + + if (ret == CY_AS_ERROR_SUCCESS) { + /* + * we configured the west bridge device and + * enumeration is going to happen on the P port + * processor. now we must enable endpoint zero + */ + cy_as_usb_end_point_config config; + + config.dir = cy_as_usb_in_out; + config.type = cy_as_usb_control; + config.enabled = cy_true; + + ret = cy_as_usb_set_end_point_config((cy_as_device_handle *) + dev_p, 0, &config); + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +/* +* This method sets how the USB is enumerated and should +* be called before the CyAsUsbConnect() is called. +*/ +static cy_as_return_status_t +my_usb_set_enum_config(cy_as_device *dev_p, + uint8_t bus_mask, + uint8_t media_mask, + cy_bool use_antioch_enumeration, + uint8_t mass_storage_interface, + uint8_t mtp_interface, + cy_bool mass_storage_callbacks, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_ll_request_response *req_p , *reply_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + + cy_as_log_debug_message(6, "cy_as_usb_set_enum_config called"); + + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ret = is_usb_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (cy_as_device_is_usb_connected(dev_p)) + return CY_AS_ERROR_USB_CONNECTED; + + if (cy_as_device_is_in_callback(dev_p)) + return CY_AS_ERROR_INVALID_IN_CALLBACK; + + /* if we are using MTP firmware: */ + if (dev_p->is_mtp_firmware == 1) { + /* we cannot enumerate MSC */ + if (mass_storage_interface != 0) + return CY_AS_ERROR_INVALID_CONFIGURATION; + + if (bus_mask == 0) { + if (mtp_interface != 0) + return CY_AS_ERROR_INVALID_CONFIGURATION; + } else if (bus_mask == 2) { + /* enable EP 1 as it will be used */ + cy_as_dma_enable_end_point(dev_p, 1, cy_true, + cy_as_direction_in); + dev_p->usb_config[1].enabled = cy_true; + dev_p->usb_config[1].dir = cy_as_usb_in; + dev_p->usb_config[1].type = cy_as_usb_int; + } else { + return CY_AS_ERROR_INVALID_CONFIGURATION; + } + /* if we are not using MTP firmware, we cannot enumerate MTP */ + } else if (mtp_interface != 0) + return CY_AS_ERROR_INVALID_CONFIGURATION; + + /* + * if we are not enumerating mass storage, we should + * not be providing an interface number. + */ + if (bus_mask == 0 && mass_storage_interface != 0) + return CY_AS_ERROR_INVALID_CONFIGURATION; + + /* + * if we are going to use mtp_interface, bus mask must be 2. + */ + if (mtp_interface != 0 && bus_mask != 2) + return CY_AS_ERROR_INVALID_CONFIGURATION; + + + /* Create the request to send to the West Bridge device */ + req_p = cy_as_ll_create_request(dev_p, + CY_RQT_SET_USB_CONFIG, CY_RQT_USB_RQT_CONTEXT, 4); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + /* Marshal the structure */ + cy_as_ll_request_response__set_word(req_p, 0, + (uint16_t)((media_mask << 8) | bus_mask)); + cy_as_ll_request_response__set_word(req_p, 1, + (uint16_t)use_antioch_enumeration); + cy_as_ll_request_response__set_word(req_p, 2, + dev_p->is_mtp_firmware ? mtp_interface : + mass_storage_interface); + cy_as_ll_request_response__set_word(req_p, 3, + (uint16_t)mass_storage_callbacks); + + /* Reserve space for the reply, the reply + * data will not exceed one word */ + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (cb == 0) { + + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return my_handle_response_set_enum_config(dev_p, + req_p, reply_p); + } else { + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_USB_SETENUMCONFIG, 0, dev_p->func_cbs_usb, + CY_AS_REQUEST_RESPONSE_EX, req_p, reply_p, + cy_as_usb_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +/* + * This method sets how the USB is enumerated and should + * be called before the CyAsUsbConnect() is called. + */ +cy_as_return_status_t +cy_as_usb_set_enum_config(cy_as_device_handle handle, + cy_as_usb_enum_control *config_p, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_device *dev_p = (cy_as_device *)handle; + uint8_t bus_mask, media_mask; + uint32_t bus, device; + cy_as_return_status_t ret; + + dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ret = is_usb_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if ((cy_as_device_is_in_callback(dev_p)) && (cb != 0)) + return CY_AS_ERROR_INVALID_IN_CALLBACK; + + /* Since we are mapping the media types to bus with NAND to 0 + * and the rest to 1, and we are only allowing for enumerating + * all the devices on a bus we just scan the array for any + * positions where there a device is enabled and mark the bus + * to be enumerated. + */ + bus_mask = 0; + media_mask = 0; + media_mask = 0; + for (bus = 0; bus < CY_AS_MAX_BUSES; bus++) { + for (device = 0; device < CY_AS_MAX_STORAGE_DEVICES; device++) { + if (config_p->devices_to_enumerate[bus][device] == + cy_true) { + bus_mask |= (0x01 << bus); + media_mask |= dev_p->media_supported[bus]; + media_mask |= dev_p->media_supported[bus]; + } + } + } + + return my_usb_set_enum_config(dev_p, bus_mask, media_mask, + config_p->antioch_enumeration, + config_p->mass_storage_interface, + config_p->mtp_interface, + config_p->mass_storage_callbacks, + cb, + client + ); +} + + +static cy_as_return_status_t +my_handle_response_get_enum_config(cy_as_device *dev_p, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *reply_p, + void *config_p) +{ + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + uint16_t val; + uint8_t bus_mask; + uint32_t bus; + + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_USB_CONFIG) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + /* Marshal the reply */ + if (req_p->flags & CY_AS_REQUEST_RESPONSE_MS) { + uint32_t device; + cy_bool state; + cy_as_usb_enum_control *ms_config_p = + (cy_as_usb_enum_control *)config_p; + + bus_mask = (uint8_t) + (cy_as_ll_request_response__get_word + (reply_p, 0) & 0xFF); + for (bus = 0; bus < CY_AS_MAX_BUSES; bus++) { + if (bus_mask & (1 << bus)) + state = cy_true; + else + state = cy_false; + + for (device = 0; device < CY_AS_MAX_STORAGE_DEVICES; + device++) + ms_config_p->devices_to_enumerate[bus][device] + = state; + } + + ms_config_p->antioch_enumeration = + (cy_bool)cy_as_ll_request_response__get_word + (reply_p, 1); + + val = cy_as_ll_request_response__get_word(reply_p, 2); + if (dev_p->is_mtp_firmware) { + ms_config_p->mass_storage_interface = 0; + ms_config_p->mtp_interface = (uint8_t)(val & 0xFF); + } else { + ms_config_p->mass_storage_interface = + (uint8_t)(val & 0xFF); + ms_config_p->mtp_interface = 0; + } + ms_config_p->mass_storage_callbacks = (cy_bool)(val >> 8); + + /* + * firmware returns an invalid interface number for mass storage, + * if mass storage is not enabled. this needs to be converted to + * zero to match the input configuration. + */ + if (bus_mask == 0) { + if (dev_p->is_mtp_firmware) + ms_config_p->mtp_interface = 0; + else + ms_config_p->mass_storage_interface = 0; + } + } else { + cy_as_usb_enum_control_dep *ex_config_p = + (cy_as_usb_enum_control_dep *)config_p; + + ex_config_p->enum_mass_storage = (uint8_t) + ((cy_as_ll_request_response__get_word + (reply_p, 0) >> 8) & 0xFF); + ex_config_p->antioch_enumeration = (cy_bool) + cy_as_ll_request_response__get_word(reply_p, 1); + + val = cy_as_ll_request_response__get_word(reply_p, 2); + ex_config_p->mass_storage_interface = (uint8_t)(val & 0xFF); + ex_config_p->mass_storage_callbacks = (cy_bool)(val >> 8); + + /* + * firmware returns an invalid interface number for mass + * storage, if mass storage is not enabled. this needs to + * be converted to zero to match the input configuration. + */ + if (ex_config_p->enum_mass_storage == 0) + ex_config_p->mass_storage_interface = 0; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +/* +* This sets up the request for the enumerateion configuration +* information, based on if the request is from the old pre-1.2 +* functions. +*/ +static cy_as_return_status_t +my_usb_get_enum_config(cy_as_device_handle handle, + uint16_t req_flags, + void *config_p, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_ll_request_response *req_p , *reply_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + + cy_as_device *dev_p; + + cy_as_log_debug_message(6, "cy_as_usb_get_enum_config called"); + + dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ret = is_usb_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (cy_as_device_is_in_callback(dev_p)) + return CY_AS_ERROR_INVALID_IN_CALLBACK; + + /* Create the request to send to the West Bridge device */ + req_p = cy_as_ll_create_request(dev_p, + CY_RQT_GET_USB_CONFIG, CY_RQT_USB_RQT_CONTEXT, 0); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + /* Reserve space for the reply, the reply data + * will not exceed two bytes */ + reply_p = cy_as_ll_create_response(dev_p, 3); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + /* we need to know the type of request to + * know how to manage the data */ + req_p->flags |= req_flags; + return my_handle_response_get_enum_config(dev_p, + req_p, reply_p, config_p); + } else { + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_USB_GETENUMCONFIG, config_p, + dev_p->func_cbs_usb, req_flags, req_p, reply_p, + cy_as_usb_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +/* + * This method returns the enumerateion configuration information + * from the West Bridge device. Generally this is not used by + * client software but is provided mostly for debug information. + * We want a method to read all state information from the device. + */ +cy_as_return_status_t +cy_as_usb_get_enum_config(cy_as_device_handle handle, + cy_as_usb_enum_control *config_p, + cy_as_function_callback cb, + uint32_t client) +{ + return my_usb_get_enum_config(handle, + CY_AS_REQUEST_RESPONSE_MS, config_p, cb, client); +} + + +/* +* This method sets the USB descriptor for a given entity. +*/ +cy_as_return_status_t +cy_as_usb_set_descriptor(cy_as_device_handle handle, + cy_as_usb_desc_type type, + uint8_t index, + void *desc_p, + uint16_t length, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_ll_request_response *req_p , *reply_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + uint16_t pktlen; + + cy_as_device *dev_p; + + cy_as_log_debug_message(6, "cy_as_usb_set_descriptor called"); + + dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ret = is_usb_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (cy_as_device_is_in_callback(dev_p)) + return CY_AS_ERROR_INVALID_IN_CALLBACK; + + if (length > CY_AS_MAX_USB_DESCRIPTOR_SIZE) + return CY_AS_ERROR_INVALID_DESCRIPTOR; + + pktlen = (uint16_t)length / 2; + if (length % 2) + pktlen++; + pktlen += 2; /* 1 for type, 1 for length */ + + /* Create the request to send to the West Bridge device */ + req_p = cy_as_ll_create_request(dev_p, CY_RQT_SET_DESCRIPTOR, + CY_RQT_USB_RQT_CONTEXT, (uint16_t)pktlen); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + cy_as_ll_request_response__set_word(req_p, 0, + (uint16_t)((uint8_t)type | (index << 8))); + cy_as_ll_request_response__set_word(req_p, 1, + (uint16_t)length); + cy_as_ll_request_response__pack(req_p, 2, length, desc_p); + + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return my_handle_response_no_data(dev_p, req_p, reply_p); + } else { + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_USB_SETDESCRIPTOR, 0, dev_p->func_cbs_usb, + CY_AS_REQUEST_RESPONSE_EX, req_p, reply_p, + cy_as_usb_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +/* + * This method clears all descriptors that were previously + * stored on the West Bridge through CyAsUsbSetDescriptor calls. + */ +cy_as_return_status_t +cy_as_usb_clear_descriptors(cy_as_device_handle handle, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_ll_request_response *req_p , *reply_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + + cy_as_device *dev_p; + + cy_as_log_debug_message(6, "cy_as_usb_clear_descriptors called"); + + dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ret = is_usb_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if ((cy_as_device_is_in_callback(dev_p)) && (cb == 0)) + return CY_AS_ERROR_INVALID_IN_CALLBACK; + + /* Create the request to send to the West Bridge device */ + req_p = cy_as_ll_create_request(dev_p, + CY_RQT_CLEAR_DESCRIPTORS, CY_RQT_USB_RQT_CONTEXT, 1); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (cb == 0) { + + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return my_handle_response_no_data(dev_p, req_p, reply_p); + } else { + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_USB_CLEARDESCRIPTORS, 0, + dev_p->func_cbs_usb, + CY_AS_REQUEST_RESPONSE_EX, req_p, reply_p, + cy_as_usb_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +static cy_as_return_status_t +my_handle_response_get_descriptor(cy_as_device *dev_p, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *reply_p, + cy_as_get_descriptor_data *data) +{ + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + uint32_t retlen; + + if (cy_as_ll_request_response__get_code(reply_p) == + CY_RESP_SUCCESS_FAILURE) { + ret = cy_as_ll_request_response__get_word(reply_p, 0); + goto destroy; + } else if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_USB_DESCRIPTOR) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + retlen = cy_as_ll_request_response__get_word(reply_p, 0); + if (retlen > data->length) { + ret = CY_AS_ERROR_INVALID_SIZE; + goto destroy; + } + + ret = CY_AS_ERROR_SUCCESS; + cy_as_ll_request_response__unpack(reply_p, 1, + retlen, data->desc_p); + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +/* +* This method retreives the USB descriptor for a given type. +*/ +cy_as_return_status_t +cy_as_usb_get_descriptor(cy_as_device_handle handle, + cy_as_usb_desc_type type, + uint8_t index, + cy_as_get_descriptor_data *data, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_return_status_t ret; + cy_as_ll_request_response *req_p , *reply_p; + + cy_as_device *dev_p; + + cy_as_log_debug_message(6, "cy_as_usb_get_descriptor called"); + + dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ret = is_usb_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (cy_as_device_is_in_callback(dev_p)) + return CY_AS_ERROR_INVALID_IN_CALLBACK; + + /* Create the request to send to the West Bridge device */ + req_p = cy_as_ll_create_request(dev_p, + CY_RQT_GET_DESCRIPTOR, CY_RQT_USB_RQT_CONTEXT, 1); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + cy_as_ll_request_response__set_word(req_p, 0, + (uint16_t)((uint8_t)type | (index << 8))); + + /* Add one for the length field */ + reply_p = cy_as_ll_create_response(dev_p, + CY_AS_MAX_USB_DESCRIPTOR_SIZE + 1); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply( + dev_p, req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return my_handle_response_get_descriptor(dev_p, + req_p, reply_p, data); + } else { + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_USB_GETDESCRIPTOR, data, + dev_p->func_cbs_usb, + CY_AS_REQUEST_RESPONSE_EX, req_p, + reply_p, cy_as_usb_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +cy_as_return_status_t +cy_as_usb_set_physical_configuration(cy_as_device_handle handle, + uint8_t config) +{ + cy_as_return_status_t ret; + cy_as_device *dev_p; + + cy_as_log_debug_message(6, + "cy_as_usb_set_physical_configuration called"); + + dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ret = is_usb_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (cy_as_device_is_usb_connected(dev_p)) + return CY_AS_ERROR_USB_CONNECTED; + + if (config < 1 || config > 12) + return CY_AS_ERROR_INVALID_CONFIGURATION; + + dev_p->usb_phy_config = config; + + return CY_AS_ERROR_SUCCESS; +} + +static cy_bool +is_physical_valid(uint8_t config, cy_as_end_point_number_t ep) +{ + static uint8_t validmask[12] = { + 0x0f, /* Config 1 - 1, 2, 3, 4 */ + 0x07, /* Config 2 - 1, 2, 3 */ + 0x07, /* Config 3 - 1, 2, 3 */ + 0x0d, /* Config 4 - 1, 3, 4 */ + 0x05, /* Config 5 - 1, 3 */ + 0x05, /* Config 6 - 1, 3 */ + 0x0d, /* Config 7 - 1, 3, 4 */ + 0x05, /* Config 8 - 1, 3 */ + 0x05, /* Config 9 - 1, 3 */ + 0x0d, /* Config 10 - 1, 3, 4 */ + 0x09, /* Config 11 - 1, 4 */ + 0x01 /* Config 12 - 1 */ + }; + + return (validmask[config - 1] & (1 << (ep - 1))) ? cy_true : cy_false; +} + +/* +* This method sets the configuration for an endpoint +*/ +cy_as_return_status_t +cy_as_usb_set_end_point_config(cy_as_device_handle handle, + cy_as_end_point_number_t ep, cy_as_usb_end_point_config *config_p) +{ + cy_as_return_status_t ret; + cy_as_device *dev_p; + + cy_as_log_debug_message(6, "cy_as_usb_set_end_point_config called"); + + dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ret = is_usb_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (cy_as_device_is_usb_connected(dev_p)) + return CY_AS_ERROR_USB_CONNECTED; + + if (ep >= 16 || ep == 2 || ep == 4 || ep == 6 || ep == 8) + return CY_AS_ERROR_INVALID_ENDPOINT; + + if (ep == 0) { + /* Endpoint 0 must be 64 byte, dir IN/OUT, + * and control type */ + if (config_p->dir != cy_as_usb_in_out || + config_p->type != cy_as_usb_control) + return CY_AS_ERROR_INVALID_CONFIGURATION; + } else if (ep == 1) { + if ((dev_p->is_mtp_firmware == 1) && + (dev_p->usb_config[1].enabled == cy_true)) { + return CY_AS_ERROR_INVALID_ENDPOINT; + } + + /* + * EP1 can only be used either as an OUT ep, or as an IN ep. + */ + if ((config_p->type == cy_as_usb_control) || + (config_p->type == cy_as_usb_iso) || + (config_p->dir == cy_as_usb_in_out)) + return CY_AS_ERROR_INVALID_CONFIGURATION; + } else { + if (config_p->dir == cy_as_usb_in_out || + config_p->type == cy_as_usb_control) + return CY_AS_ERROR_INVALID_CONFIGURATION; + + if (!is_physical_valid(dev_p->usb_phy_config, + config_p->physical)) + return CY_AS_ERROR_INVALID_PHYSICAL_ENDPOINT; + + /* + * ISO endpoints must be on E_ps 3, 5, 7 or 9 as + * they need to align directly with the underlying + * physical endpoint. + */ + if (config_p->type == cy_as_usb_iso) { + if (ep != 3 && ep != 5 && ep != 7 && ep != 9) + return CY_AS_ERROR_INVALID_CONFIGURATION; + + if (ep == 3 && config_p->physical != 1) + return CY_AS_ERROR_INVALID_CONFIGURATION; + + if (ep == 5 && config_p->physical != 2) + return CY_AS_ERROR_INVALID_CONFIGURATION; + + if (ep == 7 && config_p->physical != 3) + return CY_AS_ERROR_INVALID_CONFIGURATION; + + if (ep == 9 && config_p->physical != 4) + return CY_AS_ERROR_INVALID_CONFIGURATION; + } + } + + /* Store the configuration information until a + * CyAsUsbCommitConfig is done */ + dev_p->usb_config[ep] = *config_p; + + /* If the endpoint is enabled, enable DMA associated + * with the endpoint */ + /* + * we make some assumptions that we check here. we assume + * that the direction fields for the DMA module are the same + * values as the direction values for the USB module. + */ + cy_as_hal_assert((int)cy_as_usb_in == (int)cy_as_direction_in); + cy_as_hal_assert((int)cy_as_usb_out == (int)cy_as_direction_out); + cy_as_hal_assert((int)cy_as_usb_in_out == (int)cy_as_direction_in_out); + + return cy_as_dma_enable_end_point(dev_p, ep, + config_p->enabled, (cy_as_dma_direction)config_p->dir); +} + +cy_as_return_status_t +cy_as_usb_get_end_point_config(cy_as_device_handle handle, + cy_as_end_point_number_t ep, cy_as_usb_end_point_config *config_p) +{ + cy_as_return_status_t ret; + + cy_as_device *dev_p; + + cy_as_log_debug_message(6, "cy_as_usb_get_end_point_config called"); + + dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ret = is_usb_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (ep >= 16 || ep == 2 || ep == 4 || ep == 6 || ep == 8) + return CY_AS_ERROR_INVALID_ENDPOINT; + + *config_p = dev_p->usb_config[ep]; + + return CY_AS_ERROR_SUCCESS; +} + +/* +* Commit the configuration of the various endpoints to the hardware. +*/ +cy_as_return_status_t +cy_as_usb_commit_config(cy_as_device_handle handle, + cy_as_function_callback cb, + uint32_t client) +{ + uint32_t i; + cy_as_return_status_t ret; + cy_as_ll_request_response *req_p , *reply_p; + cy_as_device *dev_p; + uint16_t data; + + cy_as_log_debug_message(6, "cy_as_usb_commit_config called"); + + dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ret = is_usb_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (cy_as_device_is_usb_connected(dev_p)) + return CY_AS_ERROR_USB_CONNECTED; + + if (cy_as_device_is_in_callback(dev_p)) + return CY_AS_ERROR_INVALID_IN_CALLBACK; + + /* + * this performs the mapping based on informatation that was + * previously stored on the device about the various endpoints + * and how they are configured. the output of this mapping is + * setting the the 14 register values contained in usb_lepcfg + * and usb_pepcfg + */ + ret = cy_as_usb_map_logical2_physical(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + /* + * now, package the information about the various logical and + * physical endpoint configuration registers and send it + * across to the west bridge device. + */ + req_p = cy_as_ll_create_request(dev_p, + CY_RQT_SET_USB_CONFIG_REGISTERS, CY_RQT_USB_RQT_CONTEXT, 8); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + cy_as_hal_print_message("USB configuration: %d\n", + dev_p->usb_phy_config); + cy_as_hal_print_message("EP1OUT: 0x%02x EP1IN: 0x%02x\n", + dev_p->usb_ep1cfg[0], dev_p->usb_ep1cfg[1]); + cy_as_hal_print_message("PEP registers: 0x%02x 0x%02x 0x%02x 0x%02x\n", + dev_p->usb_pepcfg[0], dev_p->usb_pepcfg[1], + dev_p->usb_pepcfg[2], dev_p->usb_pepcfg[3]); + + cy_as_hal_print_message("LEP registers: 0x%02x 0x%02x 0x%02x " + "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", + dev_p->usb_lepcfg[0], dev_p->usb_lepcfg[1], + dev_p->usb_lepcfg[2], dev_p->usb_lepcfg[3], + dev_p->usb_lepcfg[4], dev_p->usb_lepcfg[5], + dev_p->usb_lepcfg[6], dev_p->usb_lepcfg[7], + dev_p->usb_lepcfg[8], dev_p->usb_lepcfg[9]); + + /* Write the EP1OUTCFG and EP1INCFG data in the first word. */ + data = (uint16_t)((dev_p->usb_ep1cfg[0] << 8) | + dev_p->usb_ep1cfg[1]); + cy_as_ll_request_response__set_word(req_p, 0, data); + + /* Write the PEP CFG data in the next 2 words */ + for (i = 0; i < 4; i += 2) { + data = (uint16_t)((dev_p->usb_pepcfg[i] << 8) | + dev_p->usb_pepcfg[i + 1]); + cy_as_ll_request_response__set_word(req_p, + 1 + i / 2, data); + } + + /* Write the LEP CFG data in the next 5 words */ + for (i = 0; i < 10; i += 2) { + data = (uint16_t)((dev_p->usb_lepcfg[i] << 8) | + dev_p->usb_lepcfg[i + 1]); + cy_as_ll_request_response__set_word(req_p, + 3 + i / 2, data); + } + + /* A single status word response type */ + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, + req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + ret = my_handle_response_no_data(dev_p, + req_p, reply_p); + + if (ret == CY_AS_ERROR_SUCCESS) + ret = cy_as_usb_setup_dma(dev_p); + + return ret; + } else { + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_USB_COMMITCONFIG, 0, dev_p->func_cbs_usb, + CY_AS_REQUEST_RESPONSE_EX, req_p, reply_p, + cy_as_usb_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +static void +sync_request_callback(cy_as_device *dev_p, + cy_as_end_point_number_t ep, void *buf_p, + uint32_t size, cy_as_return_status_t err) +{ + (void)ep; + (void)buf_p; + + dev_p->usb_error = err; + dev_p->usb_actual_cnt = size; +} + +static void +async_read_request_callback(cy_as_device *dev_p, + cy_as_end_point_number_t ep, void *buf_p, + uint32_t size, cy_as_return_status_t err) +{ + cy_as_device_handle h; + + cy_as_log_debug_message(6, + "async_read_request_callback called"); + + h = (cy_as_device_handle)dev_p; + + if (ep == 0 && cy_as_device_is_ack_delayed(dev_p)) { + dev_p->usb_pending_buffer = buf_p; + dev_p->usb_pending_size = size; + dev_p->usb_error = err; + cy_as_usb_ack_setup_packet(h, usb_ack_callback, 0); + } else { + cy_as_usb_io_callback cb; + + cb = dev_p->usb_cb[ep]; + dev_p->usb_cb[ep] = 0; + cy_as_device_clear_usb_async_pending(dev_p, ep); + if (cb) + cb(h, ep, size, buf_p, err); + } +} + +static void +async_write_request_callback(cy_as_device *dev_p, + cy_as_end_point_number_t ep, void *buf_p, + uint32_t size, cy_as_return_status_t err) +{ + cy_as_device_handle h; + + cy_as_log_debug_message(6, + "async_write_request_callback called"); + + h = (cy_as_device_handle)dev_p; + + if (ep == 0 && cy_as_device_is_ack_delayed(dev_p)) { + dev_p->usb_pending_buffer = buf_p; + dev_p->usb_pending_size = size; + dev_p->usb_error = err; + + /* The west bridge protocol generates ZLPs as required. */ + cy_as_usb_ack_setup_packet(h, usb_ack_callback, 0); + } else { + cy_as_usb_io_callback cb; + + cb = dev_p->usb_cb[ep]; + dev_p->usb_cb[ep] = 0; + + cy_as_device_clear_usb_async_pending(dev_p, ep); + if (cb) + cb(h, ep, size, buf_p, err); + } +} + +static void +my_turbo_rqt_callback(cy_as_device *dev_p, + uint8_t context, + cy_as_ll_request_response *rqt, + cy_as_ll_request_response *resp, + cy_as_return_status_t stat) +{ + uint8_t code; + + (void)context; + (void)stat; + + /* The Handlers are responsible for Deleting the rqt and resp when + * they are finished + */ + code = cy_as_ll_request_response__get_code(rqt); + switch (code) { + case CY_RQT_TURBO_SWITCH_ENDPOINT: + cy_as_hal_assert(stat == CY_AS_ERROR_SUCCESS); + cy_as_ll_destroy_request(dev_p, rqt); + cy_as_ll_destroy_response(dev_p, resp); + break; + default: + cy_as_hal_assert(cy_false); + break; + } +} + +/* Send a mailbox request to prepare the endpoint for switching */ +static cy_as_return_status_t +my_send_turbo_switch(cy_as_device *dev_p, uint32_t size, cy_bool pktread) +{ + cy_as_return_status_t ret; + cy_as_ll_request_response *req_p , *reply_p; + + /* Create the request to send to the West Bridge device */ + req_p = cy_as_ll_create_request(dev_p, + CY_RQT_TURBO_SWITCH_ENDPOINT, CY_RQT_TUR_RQT_CONTEXT, 3); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + /* Reserve space for the reply, the reply data will + * not exceed one word */ + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + cy_as_ll_request_response__set_word(req_p, 0, + (uint16_t)pktread); + cy_as_ll_request_response__set_word(req_p, 1, + (uint16_t)((size >> 16) & 0xFFFF)); + cy_as_ll_request_response__set_word(req_p, 2, + (uint16_t)(size & 0xFFFF)); + + ret = cy_as_ll_send_request(dev_p, req_p, + reply_p, cy_false, my_turbo_rqt_callback); + if (ret != CY_AS_ERROR_SUCCESS) { + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_request(dev_p, reply_p); + return ret; + } + + return CY_AS_ERROR_SUCCESS; +} + +cy_as_return_status_t +cy_as_usb_read_data(cy_as_device_handle handle, + cy_as_end_point_number_t ep, cy_bool pktread, + uint32_t dsize, uint32_t *dataread, void *data) +{ + cy_as_return_status_t ret; + cy_as_device *dev_p; + + cy_as_log_debug_message(6, "cy_as_usb_read_data called"); + + dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ret = is_usb_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (cy_as_device_is_in_callback(dev_p)) + return CY_AS_ERROR_INVALID_IN_CALLBACK; + + if (ep >= 16 || ep == 4 || ep == 6 || ep == 8) + return CY_AS_ERROR_INVALID_ENDPOINT; + + /* EP2 is available for reading when MTP is active */ + if (dev_p->mtp_count == 0 && ep == CY_AS_MTP_READ_ENDPOINT) + return CY_AS_ERROR_INVALID_ENDPOINT; + + /* If the endpoint is disabled, we cannot + * write data to the endpoint */ + if (!dev_p->usb_config[ep].enabled) + return CY_AS_ERROR_ENDPOINT_DISABLED; + + if (dev_p->usb_config[ep].dir != cy_as_usb_out) + return CY_AS_ERROR_USB_BAD_DIRECTION; + + ret = cy_as_dma_queue_request(dev_p, ep, data, dsize, + pktread, cy_true, sync_request_callback); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (ep == CY_AS_MTP_READ_ENDPOINT) { + ret = my_send_turbo_switch(dev_p, dsize, pktread); + if (ret != CY_AS_ERROR_SUCCESS) { + cy_as_dma_cancel(dev_p, ep, ret); + return ret; + } + + ret = cy_as_dma_drain_queue(dev_p, ep, cy_false); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + } else { + ret = cy_as_dma_drain_queue(dev_p, ep, cy_true); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + } + + ret = dev_p->usb_error; + *dataread = dev_p->usb_actual_cnt; + + return ret; +} + +cy_as_return_status_t +cy_as_usb_read_data_async(cy_as_device_handle handle, + cy_as_end_point_number_t ep, cy_bool pktread, + uint32_t dsize, void *data, cy_as_usb_io_callback cb) +{ + cy_as_return_status_t ret; + uint32_t mask; + cy_as_device *dev_p; + + cy_as_log_debug_message(6, "cy_as_usb_read_data_async called"); + + dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ret = is_usb_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (ep >= 16 || ep == 4 || ep == 6 || ep == 8) + return CY_AS_ERROR_INVALID_ENDPOINT; + + /* EP2 is available for reading when MTP is active */ + if (dev_p->mtp_count == 0 && ep == CY_AS_MTP_READ_ENDPOINT) + return CY_AS_ERROR_INVALID_ENDPOINT; + + /* If the endpoint is disabled, we cannot + * write data to the endpoint */ + if (!dev_p->usb_config[ep].enabled) + return CY_AS_ERROR_ENDPOINT_DISABLED; + + if (dev_p->usb_config[ep].dir != cy_as_usb_out && + dev_p->usb_config[ep].dir != cy_as_usb_in_out) + return CY_AS_ERROR_USB_BAD_DIRECTION; + + /* + * since async operations can be triggered by interrupt + * code, we must insure that we do not get multiple async + * operations going at one time and protect this test and + * set operation from interrupts. + */ + mask = cy_as_hal_disable_interrupts(); + if (cy_as_device_is_usb_async_pending(dev_p, ep)) { + cy_as_hal_enable_interrupts(mask); + return CY_AS_ERROR_ASYNC_PENDING; + } + cy_as_device_set_usb_async_pending(dev_p, ep); + + /* + * if this is for EP0, we set this bit to delay the + * ACK response until after this read has completed. + */ + if (ep == 0) + cy_as_device_set_ack_delayed(dev_p); + + cy_as_hal_enable_interrupts(mask); + + cy_as_hal_assert(dev_p->usb_cb[ep] == 0); + dev_p->usb_cb[ep] = cb; + + ret = cy_as_dma_queue_request(dev_p, ep, data, dsize, + pktread, cy_true, async_read_request_callback); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (ep == CY_AS_MTP_READ_ENDPOINT) { + ret = my_send_turbo_switch(dev_p, dsize, pktread); + if (ret != CY_AS_ERROR_SUCCESS) { + cy_as_dma_cancel(dev_p, ep, ret); + return ret; + } + } else { + /* Kick start the queue if it is not running */ + cy_as_dma_kick_start(dev_p, ep); + } + return ret; +} + +cy_as_return_status_t +cy_as_usb_write_data(cy_as_device_handle handle, + cy_as_end_point_number_t ep, uint32_t dsize, void *data) +{ + cy_as_return_status_t ret; + cy_as_device *dev_p; + + cy_as_log_debug_message(6, "cy_as_usb_write_data called"); + + dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ret = is_usb_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (cy_as_device_is_in_callback(dev_p)) + return CY_AS_ERROR_INVALID_IN_CALLBACK; + + if (ep >= 16 || ep == 2 || ep == 4 || ep == 8) + return CY_AS_ERROR_INVALID_ENDPOINT; + + /* EP6 is available for writing when MTP is active */ + if (dev_p->mtp_count == 0 && ep == CY_AS_MTP_WRITE_ENDPOINT) + return CY_AS_ERROR_INVALID_ENDPOINT; + + /* If the endpoint is disabled, we cannot + * write data to the endpoint */ + if (!dev_p->usb_config[ep].enabled) + return CY_AS_ERROR_ENDPOINT_DISABLED; + + if (dev_p->usb_config[ep].dir != cy_as_usb_in && + dev_p->usb_config[ep].dir != cy_as_usb_in_out) + return CY_AS_ERROR_USB_BAD_DIRECTION; + + /* Write on Turbo endpoint */ + if (ep == CY_AS_MTP_WRITE_ENDPOINT) { + cy_as_ll_request_response *req_p, *reply_p; + + req_p = cy_as_ll_create_request(dev_p, + CY_RQT_TURBO_SEND_RESP_DATA_TO_HOST, + CY_RQT_TUR_RQT_CONTEXT, 3); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + cy_as_ll_request_response__set_word(req_p, + 0, 0x0006); /* EP number to use. */ + cy_as_ll_request_response__set_word(req_p, + 1, (uint16_t)((dsize >> 16) & 0xFFFF)); + cy_as_ll_request_response__set_word(req_p, + 2, (uint16_t)(dsize & 0xFFFF)); + + /* Reserve space for the reply, the reply data + * will not exceed one word */ + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (dsize) { + ret = cy_as_dma_queue_request(dev_p, + ep, data, dsize, cy_false, + cy_false, sync_request_callback); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + } + + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + if (ret == CY_AS_ERROR_SUCCESS) { + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_SUCCESS_FAILURE) + ret = CY_AS_ERROR_INVALID_RESPONSE; + else + ret = cy_as_ll_request_response__get_word + (reply_p, 0); + } + + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + if (ret != CY_AS_ERROR_SUCCESS) { + if (dsize) + cy_as_dma_cancel(dev_p, ep, ret); + return ret; + } + + /* If this is a zero-byte write, firmware will + * handle it. there is no need to do any work here. + */ + if (!dsize) + return CY_AS_ERROR_SUCCESS; + } else { + ret = cy_as_dma_queue_request(dev_p, ep, data, dsize, + cy_false, cy_false, sync_request_callback); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + } + + if (ep != CY_AS_MTP_WRITE_ENDPOINT) + ret = cy_as_dma_drain_queue(dev_p, ep, cy_true); + else + ret = cy_as_dma_drain_queue(dev_p, ep, cy_false); + + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + ret = dev_p->usb_error; + return ret; +} + +static void +mtp_write_callback( + cy_as_device *dev_p, + uint8_t context, + cy_as_ll_request_response *rqt, + cy_as_ll_request_response *resp, + cy_as_return_status_t ret) +{ + cy_as_usb_io_callback cb; + cy_as_device_handle h = (cy_as_device_handle)dev_p; + + cy_as_hal_assert(context == CY_RQT_TUR_RQT_CONTEXT); + + if (ret == CY_AS_ERROR_SUCCESS) { + if (cy_as_ll_request_response__get_code(resp) != + CY_RESP_SUCCESS_FAILURE) + ret = CY_AS_ERROR_INVALID_RESPONSE; + else + ret = cy_as_ll_request_response__get_word(resp, 0); + } + + /* If this was a zero byte transfer request, we can + * call the callback from here. */ + if ((cy_as_ll_request_response__get_word(rqt, 1) == 0) && + (cy_as_ll_request_response__get_word(rqt, 2) == 0)) { + cb = dev_p->usb_cb[CY_AS_MTP_WRITE_ENDPOINT]; + dev_p->usb_cb[CY_AS_MTP_WRITE_ENDPOINT] = 0; + cy_as_device_clear_usb_async_pending(dev_p, + CY_AS_MTP_WRITE_ENDPOINT); + if (cb) + cb(h, CY_AS_MTP_WRITE_ENDPOINT, 0, 0, ret); + + goto destroy; + } + + if (ret != CY_AS_ERROR_SUCCESS) { + /* Firmware failed the request. Cancel the DMA transfer. */ + cy_as_dma_cancel(dev_p, 0x06, CY_AS_ERROR_CANCELED); + dev_p->usb_cb[0x06] = 0; + cy_as_device_clear_usb_async_pending(dev_p, 0x06); + } + +destroy: + cy_as_ll_destroy_response(dev_p, resp); + cy_as_ll_destroy_request(dev_p, rqt); +} + +cy_as_return_status_t +cy_as_usb_write_data_async(cy_as_device_handle handle, + cy_as_end_point_number_t ep, uint32_t dsize, void *data, + cy_bool spacket, cy_as_usb_io_callback cb) +{ + uint32_t mask; + cy_as_return_status_t ret; + cy_as_device *dev_p; + + cy_as_log_debug_message(6, "cy_as_usb_write_data_async called"); + + dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ret = is_usb_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (ep >= 16 || ep == 2 || ep == 4 || ep == 8) + return CY_AS_ERROR_INVALID_ENDPOINT; + + /* EP6 is available for writing when MTP is active */ + if (dev_p->mtp_count == 0 && ep == CY_AS_MTP_WRITE_ENDPOINT) + return CY_AS_ERROR_INVALID_ENDPOINT; + + /* If the endpoint is disabled, we cannot + * write data to the endpoint */ + if (!dev_p->usb_config[ep].enabled) + return CY_AS_ERROR_ENDPOINT_DISABLED; + + if (dev_p->usb_config[ep].dir != cy_as_usb_in && + dev_p->usb_config[ep].dir != cy_as_usb_in_out) + return CY_AS_ERROR_USB_BAD_DIRECTION; + + /* + * since async operations can be triggered by interrupt + * code, we must insure that we do not get multiple + * async operations going at one time and + * protect this test and set operation from interrupts. + */ + mask = cy_as_hal_disable_interrupts(); + if (cy_as_device_is_usb_async_pending(dev_p, ep)) { + cy_as_hal_enable_interrupts(mask); + return CY_AS_ERROR_ASYNC_PENDING; + } + + cy_as_device_set_usb_async_pending(dev_p, ep); + + if (ep == 0) + cy_as_device_set_ack_delayed(dev_p); + + cy_as_hal_enable_interrupts(mask); + + cy_as_hal_assert(dev_p->usb_cb[ep] == 0); + dev_p->usb_cb[ep] = cb; + dev_p->usb_spacket[ep] = spacket; + + /* Write on Turbo endpoint */ + if (ep == CY_AS_MTP_WRITE_ENDPOINT) { + cy_as_ll_request_response *req_p, *reply_p; + + req_p = cy_as_ll_create_request(dev_p, + CY_RQT_TURBO_SEND_RESP_DATA_TO_HOST, + CY_RQT_TUR_RQT_CONTEXT, 3); + + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + cy_as_ll_request_response__set_word(req_p, 0, + 0x0006); /* EP number to use. */ + cy_as_ll_request_response__set_word(req_p, 1, + (uint16_t)((dsize >> 16) & 0xFFFF)); + cy_as_ll_request_response__set_word(req_p, 2, + (uint16_t)(dsize & 0xFFFF)); + + /* Reserve space for the reply, the reply data + * will not exceed one word */ + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (dsize) { + ret = cy_as_dma_queue_request(dev_p, ep, data, + dsize, cy_false, cy_false, + async_write_request_callback); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + } + + ret = cy_as_ll_send_request(dev_p, req_p, reply_p, + cy_false, mtp_write_callback); + if (ret != CY_AS_ERROR_SUCCESS) { + if (dsize) + cy_as_dma_cancel(dev_p, ep, ret); + return ret; + } + + /* Firmware will handle a zero byte transfer + * without any DMA transfers. */ + if (!dsize) + return CY_AS_ERROR_SUCCESS; + } else { + ret = cy_as_dma_queue_request(dev_p, ep, data, dsize, + cy_false, cy_false, async_write_request_callback); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + } + + /* Kick start the queue if it is not running */ + if (ep != CY_AS_MTP_WRITE_ENDPOINT) + cy_as_dma_kick_start(dev_p, ep); + + return CY_AS_ERROR_SUCCESS; +} + +static void +my_usb_cancel_async_callback( + cy_as_device *dev_p, + uint8_t context, + cy_as_ll_request_response *rqt, + cy_as_ll_request_response *resp, + cy_as_return_status_t ret) +{ + uint8_t ep; + (void)context; + + ep = (uint8_t)cy_as_ll_request_response__get_word(rqt, 0); + if (ret == CY_AS_ERROR_SUCCESS) { + if (cy_as_ll_request_response__get_code(resp) != + CY_RESP_SUCCESS_FAILURE) + ret = CY_AS_ERROR_INVALID_RESPONSE; + else + ret = cy_as_ll_request_response__get_word(resp, 0); + } + + cy_as_ll_destroy_request(dev_p, rqt); + cy_as_ll_destroy_response(dev_p, resp); + + if (ret == CY_AS_ERROR_SUCCESS) { + cy_as_dma_cancel(dev_p, ep, CY_AS_ERROR_CANCELED); + dev_p->usb_cb[ep] = 0; + cy_as_device_clear_usb_async_pending(dev_p, ep); + } +} + +cy_as_return_status_t +cy_as_usb_cancel_async(cy_as_device_handle handle, + cy_as_end_point_number_t ep) +{ + cy_as_return_status_t ret; + cy_as_ll_request_response *req_p, *reply_p; + + cy_as_device *dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ep &= 0x7F; /* Remove the direction bit. */ + if (!cy_as_device_is_usb_async_pending(dev_p, ep)) + return CY_AS_ERROR_ASYNC_NOT_PENDING; + + ret = is_usb_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (cy_as_device_is_in_suspend_mode(dev_p)) + return CY_AS_ERROR_IN_SUSPEND; + + if ((ep == CY_AS_MTP_WRITE_ENDPOINT) || + (ep == CY_AS_MTP_READ_ENDPOINT)) { + /* Need firmware support for the cancel operation. */ + req_p = cy_as_ll_create_request(dev_p, + CY_RQT_CANCEL_ASYNC_TRANSFER, + CY_RQT_TUR_RQT_CONTEXT, 1); + + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + cy_as_ll_request_response__set_word(req_p, 0, + (uint16_t)ep); + + ret = cy_as_ll_send_request(dev_p, req_p, reply_p, + cy_false, my_usb_cancel_async_callback); + + if (ret != CY_AS_ERROR_SUCCESS) { + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + return ret; + } + } else { + ret = cy_as_dma_cancel(dev_p, ep, CY_AS_ERROR_CANCELED); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + dev_p->usb_cb[ep] = 0; + cy_as_device_clear_usb_async_pending(dev_p, ep); + } + + return CY_AS_ERROR_SUCCESS; +} + +static void +cy_as_usb_ack_callback( + cy_as_device *dev_p, + uint8_t context, + cy_as_ll_request_response *rqt, + cy_as_ll_request_response *resp, + cy_as_return_status_t ret) +{ + cy_as_func_c_b_node *node = (cy_as_func_c_b_node *) + dev_p->func_cbs_usb->head_p; + + (void)context; + + if (ret == CY_AS_ERROR_SUCCESS) { + if (cy_as_ll_request_response__get_code(resp) != + CY_RESP_SUCCESS_FAILURE) + ret = CY_AS_ERROR_INVALID_RESPONSE; + else + ret = cy_as_ll_request_response__get_word(resp, 0); + } + + node->cb_p((cy_as_device_handle)dev_p, ret, + node->client_data, node->data_type, node->data); + cy_as_remove_c_b_node(dev_p->func_cbs_usb); + + cy_as_ll_destroy_request(dev_p, rqt); + cy_as_ll_destroy_response(dev_p, resp); + cy_as_device_clear_ack_delayed(dev_p); +} + +static cy_as_return_status_t +cy_as_usb_ack_setup_packet(cy_as_device_handle handle, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_return_status_t ret; + cy_as_ll_request_response *req_p; + cy_as_ll_request_response *reply_p; + cy_as_func_c_b_node *cbnode; + + cy_as_device *dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ret = is_usb_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (cy_as_device_is_in_callback(dev_p) && cb == 0) + return CY_AS_ERROR_INVALID_IN_CALLBACK; + + cy_as_hal_assert(cb != 0); + + cbnode = cy_as_create_func_c_b_node(cb, client); + if (cbnode == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + req_p = cy_as_ll_create_request(dev_p, 0, + CY_RQT_USB_RQT_CONTEXT, 2); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + cy_as_ll_init_request(req_p, CY_RQT_ACK_SETUP_PACKET, + CY_RQT_USB_RQT_CONTEXT, 1); + cy_as_ll_init_response(reply_p, 1); + + req_p->flags |= CY_AS_REQUEST_RESPONSE_EX; + + cy_as_insert_c_b_node(dev_p->func_cbs_usb, cbnode); + + ret = cy_as_ll_send_request(dev_p, req_p, reply_p, + cy_false, cy_as_usb_ack_callback); + + return ret; +} + +/* + * Flush all data in logical EP that is being NAK-ed or + * Stall-ed, so that this does not continue to block data + * on other LEPs that use the same physical EP. + */ +static void +cy_as_usb_flush_logical_e_p( + cy_as_device *dev_p, + uint16_t ep) +{ + uint16_t addr, val, count; + + addr = CY_AS_MEM_P0_EP2_DMA_REG + ep - 2; + val = cy_as_hal_read_register(dev_p->tag, addr); + + while (val) { + count = ((val & 0xFFF) + 1) / 2; + while (count--) + val = cy_as_hal_read_register(dev_p->tag, ep); + + cy_as_hal_write_register(dev_p->tag, addr, 0); + val = cy_as_hal_read_register(dev_p->tag, addr); + } +} + +static cy_as_return_status_t +cy_as_usb_nak_stall_request(cy_as_device_handle handle, + cy_as_end_point_number_t ep, + uint16_t request, + cy_bool state, + cy_as_usb_function_callback cb, + cy_as_function_callback fcb, + uint32_t client) +{ + cy_as_return_status_t ret; + cy_as_ll_request_response *req_p , *reply_p; + uint16_t data; + + cy_as_device *dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + if (cb) + cy_as_hal_assert(fcb == 0); + if (fcb) + cy_as_hal_assert(cb == 0); + + ret = is_usb_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (cy_as_device_is_in_callback(dev_p) && cb == 0 && fcb == 0) + return CY_AS_ERROR_INVALID_IN_CALLBACK; + + req_p = cy_as_ll_create_request(dev_p, + request, CY_RQT_USB_RQT_CONTEXT, 2); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + /* A single status word response type */ + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + /* Set the endpoint */ + data = (uint8_t)ep; + cy_as_ll_request_response__set_word(req_p, 0, data); + + /* Set stall state to stalled */ + cy_as_ll_request_response__set_word(req_p, 1, (uint8_t)state); + + if (cb || fcb) { + void *cbnode; + cy_as_c_b_queue *queue; + if (cb) { + cbnode = cy_as_create_usb_func_c_b_node(cb, client); + queue = dev_p->usb_func_cbs; + } else { + cbnode = cy_as_create_func_c_b_node(fcb, client); + queue = dev_p->func_cbs_usb; + req_p->flags |= CY_AS_REQUEST_RESPONSE_EX; + } + + if (cbnode == 0) { + ret = CY_AS_ERROR_OUT_OF_MEMORY; + goto destroy; + } else + cy_as_insert_c_b_node(queue, cbnode); + + + if (cy_as_device_is_setup_packet(dev_p)) { + /* No Ack is needed on a stall request on EP0 */ + if ((state == cy_true) && (ep == 0)) { + cy_as_device_set_ep0_stalled(dev_p); + } else { + cy_as_device_set_ack_delayed(dev_p); + req_p->flags |= + CY_AS_REQUEST_RESPONSE_DELAY_ACK; + } + } + + ret = cy_as_ll_send_request(dev_p, req_p, + reply_p, cy_false, cy_as_usb_func_callback); + if (ret != CY_AS_ERROR_SUCCESS) { + if (req_p->flags & CY_AS_REQUEST_RESPONSE_DELAY_ACK) + cy_as_device_rem_ack_delayed(dev_p); + cy_as_remove_c_b_tail_node(queue); + + goto destroy; + } + } else { + ret = cy_as_ll_send_request_wait_reply(dev_p, + req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + if (cy_as_ll_request_response__get_code(reply_p) != + CY_RESP_SUCCESS_FAILURE) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + ret = cy_as_ll_request_response__get_word(reply_p, 0); + + if ((ret == CY_AS_ERROR_SUCCESS) && + (request == CY_RQT_STALL_ENDPOINT)) { + if ((ep > 1) && (state != 0) && + (dev_p->usb_config[ep].dir == cy_as_usb_out)) + cy_as_usb_flush_logical_e_p(dev_p, ep); + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + } + + return ret; +} + +static cy_as_return_status_t +my_handle_response_get_stall(cy_as_device *dev_p, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *reply_p, + cy_bool *state_p) +{ + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + uint8_t code = cy_as_ll_request_response__get_code(reply_p); + + if (code == CY_RESP_SUCCESS_FAILURE) { + ret = cy_as_ll_request_response__get_word(reply_p, 0); + goto destroy; + } else if (code != CY_RESP_ENDPOINT_STALL) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + *state_p = (cy_bool)cy_as_ll_request_response__get_word(reply_p, 0); + ret = CY_AS_ERROR_SUCCESS; + + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +static cy_as_return_status_t +my_handle_response_get_nak(cy_as_device *dev_p, + cy_as_ll_request_response *req_p, + cy_as_ll_request_response *reply_p, + cy_bool *state_p) +{ + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + uint8_t code = cy_as_ll_request_response__get_code(reply_p); + + if (code == CY_RESP_SUCCESS_FAILURE) { + ret = cy_as_ll_request_response__get_word(reply_p, 0); + goto destroy; + } else if (code != CY_RESP_ENDPOINT_NAK) { + ret = CY_AS_ERROR_INVALID_RESPONSE; + goto destroy; + } + + *state_p = (cy_bool)cy_as_ll_request_response__get_word(reply_p, 0); + ret = CY_AS_ERROR_SUCCESS; + + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +static cy_as_return_status_t +cy_as_usb_get_nak_stall(cy_as_device_handle handle, + cy_as_end_point_number_t ep, + uint16_t request, + uint16_t response, + cy_bool *state_p, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_return_status_t ret; + cy_as_ll_request_response *req_p , *reply_p; + uint16_t data; + + cy_as_device *dev_p = (cy_as_device *)handle; + + (void)response; + + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ret = is_usb_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (cy_as_device_is_in_callback(dev_p) && !cb) + return CY_AS_ERROR_INVALID_IN_CALLBACK; + + req_p = cy_as_ll_create_request(dev_p, request, + CY_RQT_USB_RQT_CONTEXT, 1); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + /* Set the endpoint */ + data = (uint8_t)ep; + cy_as_ll_request_response__set_word(req_p, 0, (uint16_t)ep); + + /* A single status word response type */ + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, + req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + if (request == CY_RQT_GET_STALL) + return my_handle_response_get_stall(dev_p, + req_p, reply_p, state_p); + else + return my_handle_response_get_nak(dev_p, + req_p, reply_p, state_p); + + } else { + cy_as_funct_c_b_type type; + + if (request == CY_RQT_GET_STALL) + type = CY_FUNCT_CB_USB_GETSTALL; + else + type = CY_FUNCT_CB_USB_GETNAK; + + ret = cy_as_misc_send_request(dev_p, cb, client, type, + state_p, dev_p->func_cbs_usb, CY_AS_REQUEST_RESPONSE_EX, + req_p, reply_p, cy_as_usb_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +cy_as_return_status_t +cy_as_usb_set_nak(cy_as_device_handle handle, + cy_as_end_point_number_t ep, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_device *dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + /* + * we send the firmware the EP# with the appropriate direction + * bit, regardless of what the user gave us. + */ + ep &= 0x0f; + if (dev_p->usb_config[ep].dir == cy_as_usb_in) + ep |= 0x80; + + if (dev_p->mtp_count > 0) + return CY_AS_ERROR_NOT_VALID_IN_MTP; + + return cy_as_usb_nak_stall_request(handle, ep, + CY_RQT_ENDPOINT_SET_NAK, cy_true, 0, cb, client); +} + + +cy_as_return_status_t +cy_as_usb_clear_nak(cy_as_device_handle handle, + cy_as_end_point_number_t ep, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_device *dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + /* + * we send the firmware the EP# with the appropriate + * direction bit, regardless of what the user gave us. + */ + ep &= 0x0f; + if (dev_p->usb_config[ep].dir == cy_as_usb_in) + ep |= 0x80; + + if (dev_p->mtp_count > 0) + return CY_AS_ERROR_NOT_VALID_IN_MTP; + + return cy_as_usb_nak_stall_request(handle, ep, + CY_RQT_ENDPOINT_SET_NAK, cy_false, 0, cb, client); +} + +cy_as_return_status_t +cy_as_usb_get_nak(cy_as_device_handle handle, + cy_as_end_point_number_t ep, + cy_bool *nak_p, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_device *dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + /* + * we send the firmware the EP# with the appropriate + * direction bit, regardless of what the user gave us. + */ + ep &= 0x0f; + if (dev_p->usb_config[ep].dir == cy_as_usb_in) + ep |= 0x80; + + if (dev_p->mtp_count > 0) + return CY_AS_ERROR_NOT_VALID_IN_MTP; + + return cy_as_usb_get_nak_stall(handle, ep, + CY_RQT_GET_ENDPOINT_NAK, CY_RESP_ENDPOINT_NAK, + nak_p, cb, client); +} + + +cy_as_return_status_t +cy_as_usb_set_stall(cy_as_device_handle handle, + cy_as_end_point_number_t ep, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_device *dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + /* + * we send the firmware the EP# with the appropriate + * direction bit, regardless of what the user gave us. + */ + ep &= 0x0f; + if (dev_p->usb_config[ep].dir == cy_as_usb_in) + ep |= 0x80; + + if (dev_p->mtp_turbo_active) + return CY_AS_ERROR_NOT_VALID_DURING_MTP; + + return cy_as_usb_nak_stall_request(handle, ep, + CY_RQT_STALL_ENDPOINT, cy_true, 0, cb, client); +} + +cy_as_return_status_t +cy_as_usb_clear_stall(cy_as_device_handle handle, + cy_as_end_point_number_t ep, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_device *dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + /* + * we send the firmware the EP# with the appropriate + * direction bit, regardless of what the user gave us. + */ + ep &= 0x0f; + if (dev_p->usb_config[ep].dir == cy_as_usb_in) + ep |= 0x80; + + if (dev_p->mtp_turbo_active) + return CY_AS_ERROR_NOT_VALID_DURING_MTP; + + return cy_as_usb_nak_stall_request(handle, ep, + CY_RQT_STALL_ENDPOINT, cy_false, 0, cb, client); +} + +cy_as_return_status_t +cy_as_usb_get_stall(cy_as_device_handle handle, + cy_as_end_point_number_t ep, + cy_bool *stall_p, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_device *dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + /* + * we send the firmware the EP# with the appropriate + * direction bit, regardless of what the user gave us. + */ + ep &= 0x0f; + if (dev_p->usb_config[ep].dir == cy_as_usb_in) + ep |= 0x80; + + if (dev_p->mtp_turbo_active) + return CY_AS_ERROR_NOT_VALID_DURING_MTP; + + return cy_as_usb_get_nak_stall(handle, ep, + CY_RQT_GET_STALL, CY_RESP_ENDPOINT_STALL, stall_p, cb, client); +} + +cy_as_return_status_t +cy_as_usb_signal_remote_wakeup(cy_as_device_handle handle, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_return_status_t ret; + cy_as_ll_request_response *req_p , *reply_p; + + cy_as_device *dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ret = is_usb_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if (cy_as_device_is_in_callback(dev_p)) + return CY_AS_ERROR_INVALID_IN_CALLBACK; + + if (dev_p->usb_last_event != cy_as_event_usb_suspend) + return CY_AS_ERROR_NOT_IN_SUSPEND; + + req_p = cy_as_ll_create_request(dev_p, + CY_RQT_USB_REMOTE_WAKEUP, CY_RQT_USB_RQT_CONTEXT, 0); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + /* A single status word response type */ + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + if (cy_as_ll_request_response__get_code(reply_p) == + CY_RESP_SUCCESS_FAILURE) + ret = cy_as_ll_request_response__get_word(reply_p, 0); + else + ret = CY_AS_ERROR_INVALID_RESPONSE; + } else { + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_USB_SIGNALREMOTEWAKEUP, 0, + dev_p->func_cbs_usb, + CY_AS_REQUEST_RESPONSE_EX, req_p, + reply_p, cy_as_usb_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +cy_as_return_status_t +cy_as_usb_set_m_s_report_threshold(cy_as_device_handle handle, + uint32_t wr_sectors, + uint32_t rd_sectors, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_return_status_t ret; + cy_as_ll_request_response *req_p , *reply_p; + + cy_as_device *dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ret = is_usb_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + if ((cb == 0) && (cy_as_device_is_in_callback(dev_p))) + return CY_AS_ERROR_INVALID_IN_CALLBACK; + + /* Check if the firmware version supports this feature. */ + if ((dev_p->media_supported[0]) && (dev_p->media_supported[0] == + (1 << cy_as_media_nand))) + return CY_AS_ERROR_NOT_SUPPORTED; + + req_p = cy_as_ll_create_request(dev_p, CY_RQT_USB_STORAGE_MONITOR, + CY_RQT_USB_RQT_CONTEXT, 4); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + /* A single status word response type */ + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + /* Set the read and write count parameters into + * the request structure. */ + cy_as_ll_request_response__set_word(req_p, 0, + (uint16_t)((wr_sectors >> 16) & 0xFFFF)); + cy_as_ll_request_response__set_word(req_p, 1, + (uint16_t)(wr_sectors & 0xFFFF)); + cy_as_ll_request_response__set_word(req_p, 2, + (uint16_t)((rd_sectors >> 16) & 0xFFFF)); + cy_as_ll_request_response__set_word(req_p, 3, + (uint16_t)(rd_sectors & 0xFFFF)); + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + if (cy_as_ll_request_response__get_code(reply_p) == + CY_RESP_SUCCESS_FAILURE) + ret = cy_as_ll_request_response__get_word(reply_p, 0); + else + ret = CY_AS_ERROR_INVALID_RESPONSE; + } else { + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_USB_SET_MSREPORT_THRESHOLD, 0, + dev_p->func_cbs_usb, CY_AS_REQUEST_RESPONSE_EX, + req_p, reply_p, cy_as_usb_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +cy_as_return_status_t +cy_as_usb_select_m_s_partitions( + cy_as_device_handle handle, + cy_as_bus_number_t bus, + uint32_t device, + cy_as_usb_m_s_type_t type, + cy_as_function_callback cb, + uint32_t client) +{ + cy_as_return_status_t ret; + cy_as_ll_request_response *req_p , *reply_p; + uint16_t val; + + cy_as_device *dev_p = (cy_as_device *)handle; + if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) + return CY_AS_ERROR_INVALID_HANDLE; + + ret = is_usb_active(dev_p); + if (ret != CY_AS_ERROR_SUCCESS) + return ret; + + /* This API has to be made before SetEnumConfig is called. */ + if (dev_p->usb_config[0].enabled) + return CY_AS_ERROR_INVALID_CALL_SEQUENCE; + + if ((cb == 0) && (cy_as_device_is_in_callback(dev_p))) + return CY_AS_ERROR_INVALID_IN_CALLBACK; + + req_p = cy_as_ll_create_request(dev_p, CY_RQT_MS_PARTITION_SELECT, + CY_RQT_USB_RQT_CONTEXT, 2); + if (req_p == 0) + return CY_AS_ERROR_OUT_OF_MEMORY; + + /* A single status word response type */ + reply_p = cy_as_ll_create_response(dev_p, 1); + if (reply_p == 0) { + cy_as_ll_destroy_request(dev_p, req_p); + return CY_AS_ERROR_OUT_OF_MEMORY; + } + + /* Set the read and write count parameters into + * the request structure. */ + cy_as_ll_request_response__set_word(req_p, 0, + (uint16_t)((bus << 8) | device)); + + val = 0; + if ((type == cy_as_usb_m_s_unit0) || (type == cy_as_usb_m_s_both)) + val |= 1; + if ((type == cy_as_usb_m_s_unit1) || (type == cy_as_usb_m_s_both)) + val |= (1 << 8); + + cy_as_ll_request_response__set_word(req_p, 1, val); + + if (cb == 0) { + ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + + if (cy_as_ll_request_response__get_code(reply_p) == + CY_RESP_SUCCESS_FAILURE) + ret = cy_as_ll_request_response__get_word(reply_p, 0); + else + ret = CY_AS_ERROR_INVALID_RESPONSE; + } else { + ret = cy_as_misc_send_request(dev_p, cb, client, + CY_FUNCT_CB_NODATA, 0, dev_p->func_cbs_usb, + CY_AS_REQUEST_RESPONSE_EX, req_p, reply_p, + cy_as_usb_func_callback); + + if (ret != CY_AS_ERROR_SUCCESS) + goto destroy; + return ret; + } + +destroy: + cy_as_ll_destroy_request(dev_p, req_p); + cy_as_ll_destroy_response(dev_p, reply_p); + + return ret; +} + +static void +cy_as_usb_func_callback( + cy_as_device *dev_p, + uint8_t context, + cy_as_ll_request_response *rqt, + cy_as_ll_request_response *resp, + cy_as_return_status_t stat) +{ + cy_as_usb_func_c_b_node* node = (cy_as_usb_func_c_b_node *) + dev_p->usb_func_cbs->head_p; + cy_as_func_c_b_node* fnode = (cy_as_func_c_b_node *) + dev_p->func_cbs_usb->head_p; + cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; + + cy_as_device_handle h = (cy_as_device_handle)dev_p; + cy_bool delayed_ack = (rqt->flags & CY_AS_REQUEST_RESPONSE_DELAY_ACK) + == CY_AS_REQUEST_RESPONSE_DELAY_ACK; + cy_bool ex_request = (rqt->flags & CY_AS_REQUEST_RESPONSE_EX) + == CY_AS_REQUEST_RESPONSE_EX; + cy_bool ms_request = (rqt->flags & CY_AS_REQUEST_RESPONSE_MS) + == CY_AS_REQUEST_RESPONSE_MS; + uint8_t code; + uint8_t ep, state; + + if (!ex_request && !ms_request) { + cy_as_hal_assert(dev_p->usb_func_cbs->count != 0); + cy_as_hal_assert(dev_p->usb_func_cbs->type == + CYAS_USB_FUNC_CB); + } else { + cy_as_hal_assert(dev_p->func_cbs_usb->count != 0); + cy_as_hal_assert(dev_p->func_cbs_usb->type == CYAS_FUNC_CB); + } + + (void)context; + + /* The Handlers are responsible for Deleting the rqt and resp when + * they are finished + */ + code = cy_as_ll_request_response__get_code(rqt); + switch (code) { + case CY_RQT_START_USB: + ret = my_handle_response_usb_start(dev_p, rqt, resp, stat); + break; + case CY_RQT_STOP_USB: + ret = my_handle_response_usb_stop(dev_p, rqt, resp, stat); + break; + case CY_RQT_SET_CONNECT_STATE: + if (!cy_as_ll_request_response__get_word(rqt, 0)) + ret = my_handle_response_disconnect( + dev_p, rqt, resp, stat); + else + ret = my_handle_response_connect( + dev_p, rqt, resp, stat); + break; + case CY_RQT_GET_CONNECT_STATE: + break; + case CY_RQT_SET_USB_CONFIG: + ret = my_handle_response_set_enum_config(dev_p, rqt, resp); + break; + case CY_RQT_GET_USB_CONFIG: + cy_as_hal_assert(fnode->data != 0); + ret = my_handle_response_get_enum_config(dev_p, + rqt, resp, fnode->data); + break; + case CY_RQT_STALL_ENDPOINT: + ep = (uint8_t)cy_as_ll_request_response__get_word(rqt, 0); + state = (uint8_t)cy_as_ll_request_response__get_word(rqt, 1); + ret = my_handle_response_no_data(dev_p, rqt, resp); + if ((ret == CY_AS_ERROR_SUCCESS) && (ep > 1) && (state != 0) + && (dev_p->usb_config[ep].dir == cy_as_usb_out)) + cy_as_usb_flush_logical_e_p(dev_p, ep); + break; + case CY_RQT_GET_STALL: + cy_as_hal_assert(fnode->data != 0); + ret = my_handle_response_get_stall(dev_p, + rqt, resp, (cy_bool *)fnode->data); + break; + case CY_RQT_SET_DESCRIPTOR: + ret = my_handle_response_no_data(dev_p, rqt, resp); + break; + case CY_RQT_GET_DESCRIPTOR: + cy_as_hal_assert(fnode->data != 0); + ret = my_handle_response_get_descriptor(dev_p, + rqt, resp, (cy_as_get_descriptor_data *)fnode->data); + break; + case CY_RQT_SET_USB_CONFIG_REGISTERS: + ret = my_handle_response_no_data(dev_p, rqt, resp); + if (ret == CY_AS_ERROR_SUCCESS) + ret = cy_as_usb_setup_dma(dev_p); + break; + case CY_RQT_ENDPOINT_SET_NAK: + ret = my_handle_response_no_data(dev_p, rqt, resp); + break; + case CY_RQT_GET_ENDPOINT_NAK: + cy_as_hal_assert(fnode->data != 0); + ret = my_handle_response_get_nak(dev_p, + rqt, resp, (cy_bool *)fnode->data); + break; + case CY_RQT_ACK_SETUP_PACKET: + break; + case CY_RQT_USB_REMOTE_WAKEUP: + ret = my_handle_response_no_data(dev_p, rqt, resp); + break; + case CY_RQT_CLEAR_DESCRIPTORS: + ret = my_handle_response_no_data(dev_p, rqt, resp); + break; + case CY_RQT_USB_STORAGE_MONITOR: + ret = my_handle_response_no_data(dev_p, rqt, resp); + break; + case CY_RQT_MS_PARTITION_SELECT: + ret = my_handle_response_no_data(dev_p, rqt, resp); + break; + default: + ret = CY_AS_ERROR_INVALID_RESPONSE; + cy_as_hal_assert(cy_false); + break; + } + + /* + * if the low level layer returns a direct error, use + * the corresponding error code. if not, use the error + * code based on the response from firmware. + */ + if (stat == CY_AS_ERROR_SUCCESS) + stat = ret; + + if (ex_request || ms_request) { + fnode->cb_p((cy_as_device_handle)dev_p, stat, + fnode->client_data, fnode->data_type, fnode->data); + cy_as_remove_c_b_node(dev_p->func_cbs_usb); + } else { + node->cb_p((cy_as_device_handle)dev_p, stat, + node->client_data); + cy_as_remove_c_b_node(dev_p->usb_func_cbs); + } + + if (delayed_ack) { + cy_as_hal_assert(cy_as_device_is_ack_delayed(dev_p)); + cy_as_device_rem_ack_delayed(dev_p); + + /* + * send the ACK if required. + */ + if (!cy_as_device_is_ack_delayed(dev_p)) + cy_as_usb_ack_setup_packet(h, + usb_ack_callback, 0); + } +} + + +/*[]*/ |