| /* 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; |
| 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); |
| } |
| } |
| |
| |
| /*[]*/ |