aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/westbridge/astoria/api/src/cyasmtp.c
diff options
context:
space:
mode:
authorDavid Cross <david.cross@cypress.com>2010-08-06 17:29:03 -0700
committerGreg Kroah-Hartman <gregkh@suse.de>2010-08-30 17:20:53 -0700
commit81eb669b9516b85a2acf4c342db2322bed37d70c (patch)
treed5339b0c1d371b6bfeb0984678ca03054f3a8339 /drivers/staging/westbridge/astoria/api/src/cyasmtp.c
parent760ffce8ab13510bb1bf0af22f88df3f855bfa6b (diff)
Staging: add West Bridge Astoria Driver
This is a driver for the Cypress West Bridge companion chip. Its function is analogous to the North/South Bridges of PC environments applied to embedded devices, in that it expands I/O and storage capabilities of an embedded processor. The Astoria version, which this driver applies to, functions as a USB, embedded memory and SDIO controller. The kernel that this patch was applied to is linux-2.6.35, although it was tested using the android kernel 2.6.29 running on the Zoom 2 platform. In this system, it was used primarily as a sideloading accelerator enabling direct data transfers between a USB host PC and embedded memory without system overheads. Minor modifications were also made to the kernel for this patch. These include changes such as EXPORTing of fat_get_block in the kernel code. Another function, mpage_cleardirty was also added to the memory management code. This function is used to clear the dirty pages from a specific inode. This allows for direct, file based DMA. None of these changes are believed to have any negative impact on the kernel and may provide additional benefit for other developers and drivers. The driver, as submitted, was placed into the drivers/staging/westbridge folder as the directory structure it will eventually reside in is not yet defined. The driver, as placed in staging is divided into four parts: 1) gadget - this implements a gadget peripheral controller and includes IOCTLs for MTP transfers 2) block -this implements a generic block device driver to enable access to embedded memory 3) api -this is the Cypress SDK, and includes USB and Storage specific functions. In addition, it includes common code for low level routines such as message passing and common data transfer routines 4) hal - this should likely be included in the arch directory as it needs to be modified for a given platform. The directory structure in the staging area is meant to reflect the eventual location of where this code likely should be. It is platform specific. In this case, the HAL included is for the Android Zoom 2 platform. Here, West Bridge is connected to the GPMC (general purpose memory controller) of the OMAP3. Specific timing needs to be enabled to ensure reliable communication. Many thanks to Greg KH for conducting initial reviews and providing pointers. Please contact david.cross@cypress.com for questions, concerns or feedback. Signed-off-by: David Cross <david.cross@cypress.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/staging/westbridge/astoria/api/src/cyasmtp.c')
-rw-r--r--drivers/staging/westbridge/astoria/api/src/cyasmtp.c1128
1 files changed, 1128 insertions, 0 deletions
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 00000000000..3725800aa7b
--- /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 ;
+}