/* 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 because 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; } EXPORT_SYMBOL(cy_as_mtp_start); 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 successfully 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); } EXPORT_SYMBOL(cy_as_mtp_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); } EXPORT_SYMBOL(cy_as_mtp_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; } EXPORT_SYMBOL(cy_as_mtp_cancel_send_object); 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; } EXPORT_SYMBOL(cy_as_mtp_cancel_get_object); 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; } EXPORT_SYMBOL(cy_as_mtp_storage_only_start); 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; } EXPORT_SYMBOL(cy_as_mtp_storage_only_stop);