diff options
author | Greg Kroah-Hartman <gregkh@suse.de> | 2010-10-28 09:44:56 -0700 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-10-28 09:44:56 -0700 |
commit | e4c5bf8e3dca827a1b3a6fac494eae8c74b7e1e7 (patch) | |
tree | ea51b391f7d74ca695dcb9f5e46eb02688a92ed9 /drivers/staging/westbridge/astoria/api/src/cyasmtp.c | |
parent | 81280572ca6f54009edfa4deee563e8678784218 (diff) | |
parent | a4ac0d847af9dd34d5953a5e264400326144b6b2 (diff) | |
download | op-kernel-dev-e4c5bf8e3dca827a1b3a6fac494eae8c74b7e1e7.zip op-kernel-dev-e4c5bf8e3dca827a1b3a6fac494eae8c74b7e1e7.tar.gz |
Merge 'staging-next' to Linus's tree
This merges the staging-next tree to Linus's tree and resolves
some conflicts that were present due to changes in other trees that were
affected by files here.
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.c | 1128 |
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 0000000..d5a8e45 --- /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; +} |