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/cyasusb.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/cyasusb.c')
-rw-r--r-- | drivers/staging/westbridge/astoria/api/src/cyasusb.c | 3718 |
1 files changed, 3718 insertions, 0 deletions
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 0000000..5a21970 --- /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); + } +} + + +/*[]*/ |