/*************************************************************************** * Copyright (c) 2024 Microsoft Corporation * Copyright (c) 2026-present Eclipse ThreadX contributors * * This program and the accompanying materials are made available under the * terms of the MIT License which is available at * https://opensource.org/licenses/MIT. * * SPDX-License-Identifier: MIT **************************************************************************/ /**************************************************************************/ /**************************************************************************/ /** */ /** USBX Component */ /** */ /** Device Storage Class */ /** */ /**************************************************************************/ /**************************************************************************/ #define UX_SOURCE_CODE /* Include necessary system files. */ #include "ux_api.h" #include "ux_device_class_storage.h" #include "ux_device_stack.h" #if defined(UX_DEVICE_STANDALONE) /* Internal static inline implements. */ static inline UINT _ux_device_class_storage_task_usb(UX_SLAVE_CLASS_STORAGE *storage); static inline UINT _ux_device_class_storage_reset_wait(UX_SLAVE_CLASS_STORAGE *storage); static inline VOID _ux_device_class_storage_cbw_receive(UX_SLAVE_CLASS_STORAGE *storage); static inline VOID _ux_device_class_storage_cbw_process(UX_SLAVE_CLASS_STORAGE *storage); static inline VOID _ux_device_class_storage_cmd_process(UX_SLAVE_CLASS_STORAGE *storage, UCHAR *cbw); static inline VOID _ux_device_class_storage_data_cases_check(UX_SLAVE_CLASS_STORAGE *storage); static inline VOID _ux_device_class_storage_trans_start(UX_SLAVE_CLASS_STORAGE *storage); static inline UINT _ux_device_class_storage_trans_wait(UX_SLAVE_CLASS_STORAGE *storage); static inline VOID _ux_device_class_storage_trans_error(UX_SLAVE_CLASS_STORAGE *storage); static inline UINT _ux_device_class_storage_data_next(UX_SLAVE_CLASS_STORAGE *storage); static inline VOID _ux_device_class_storage_halt_out(UX_SLAVE_CLASS_STORAGE *storage); static inline VOID _ux_device_class_storage_halt_in(UX_SLAVE_CLASS_STORAGE *storage); static inline VOID _ux_device_class_storage_halt_trans(UX_SLAVE_CLASS_STORAGE *storage); static inline VOID _ux_device_class_storage_task_disk(UX_SLAVE_CLASS_STORAGE *storage); static inline VOID _ux_device_class_storage_disk_start(UX_SLAVE_CLASS_STORAGE *storage); static inline UINT _ux_device_class_storage_disk_wait(UX_SLAVE_CLASS_STORAGE *storage); static inline VOID _ux_device_class_storage_disk_next(UX_SLAVE_CLASS_STORAGE *storage); static inline VOID _ux_device_class_storage_disk_read_next(UX_SLAVE_CLASS_STORAGE *storage); static inline VOID _ux_device_class_storage_disk_write_next(UX_SLAVE_CLASS_STORAGE *storage); static inline VOID _ux_device_class_storage_disk_error(UX_SLAVE_CLASS_STORAGE *storage); /**************************************************************************/ /* */ /* FUNCTION RELEASE */ /* */ /* _ux_device_class_storage_tasks_run PORTABLE C */ /* 6.2.0 */ /* AUTHOR */ /* */ /* Chaoqiong Xiao, Microsoft Corporation */ /* */ /* DESCRIPTION */ /* */ /* This function runs tasks of the storage class. */ /* E.g., CBW-DATA-CSW state machine. */ /* */ /* It's for standalone mode. */ /* */ /* INPUT */ /* */ /* instance Address of storage instance */ /* */ /* OUTPUT */ /* */ /* UX_STATE_RESET Tasks suspended */ /* UX_STATE_IDLE Activated but no task ran */ /* (others > UX_STATE_IDLE) Tasks running */ /* */ /* CALLS */ /* */ /* _ux_device_class_storage_format Storage class format */ /* _ux_device_class_storage_inquiry Storage class inquiry */ /* _ux_device_class_storage_mode_select Mode select */ /* _ux_device_class_storage_mode_sense Mode sense */ /* _ux_device_class_storage_prevent_allow_media_removal */ /* Prevent media removal */ /* _ux_device_class_storage_read Read */ /* _ux_device_class_storage_read_capacity */ /* Read capacity */ /* _ux_device_class_storage_read_format_capacity */ /* Read format capacity */ /* _ux_device_class_storage_request_sense */ /* Sense request */ /* _ux_device_class_storage_start_stop Start/Stop */ /* _ux_device_class_storage_synchronize_cache */ /* Synchronize cache */ /* _ux_device_class_storage_test_ready Ready test */ /* _ux_device_class_storage_verify Verify */ /* _ux_device_class_storage_write Write */ /* _ux_device_stack_endpoint_stall Endpoint stall */ /* _ux_device_stack_interface_delete Interface delete */ /* _ux_device_stack_transfer_request Transfer request */ /* _ux_utility_long_get Get 32-bit value */ /* _ux_utility_memory_allocate Allocate memory */ /* _ux_utility_semaphore_create Create semaphore */ /* _ux_utility_thread_suspend Suspend thread */ /* */ /* CALLED BY */ /* */ /* Device Stack */ /* */ /**************************************************************************/ UINT _ux_device_class_storage_tasks_run(VOID *instance) { UX_SLAVE_CLASS_STORAGE *storage; UINT status; /* Get storage instance. */ storage = (UX_SLAVE_CLASS_STORAGE *) instance; /* Run USB and disk tasks. */ status = _ux_device_class_storage_task_usb(storage); _ux_device_class_storage_task_disk(storage); return(status); } static inline UINT _ux_device_class_storage_task_usb(UX_SLAVE_CLASS_STORAGE *storage) { UX_SLAVE_DEVICE *device; UCHAR state; UINT status; INT immediate_state = UX_TRUE; /* Get pointer to the device. */ device = &_ux_system_slave -> ux_system_slave_device; /* Run states once. */ while(immediate_state) { /* General check for MSC ready. */ if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED || storage -> ux_device_class_storage_ep_in == UX_NULL || storage -> ux_device_class_storage_ep_out == UX_NULL) { storage -> ux_device_class_storage_state = UX_STATE_RESET; return(UX_STATE_EXIT); } /* Get current state. */ state = storage -> ux_device_class_storage_state; /* Handle different state. */ switch(state) { case UX_STATE_RESET: /* Initial, reset. */ _ux_device_class_storage_cbw_receive(storage); /* Roll back to next state directly. */ continue; case UX_DEVICE_CLASS_STORAGE_STATE_RESET: _ux_device_class_storage_halt_in(storage); _ux_device_class_storage_halt_out(storage); storage -> ux_device_class_storage_state = UX_DEVICE_CLASS_STORAGE_STATE_RESET_WAIT; /* Fall through. */ case UX_DEVICE_CLASS_STORAGE_STATE_RESET_WAIT: /* Wait reset recovery. */ return _ux_device_class_storage_reset_wait(storage); case UX_DEVICE_CLASS_STORAGE_STATE_TRANS_START: _ux_device_class_storage_trans_start(storage); /* Fall through. */ case UX_DEVICE_CLASS_STORAGE_STATE_TRANS_WAIT: status = _ux_device_class_storage_trans_wait(storage); /* Fatal case. */ if (status < UX_STATE_ERROR) { /* USB idle. */ storage -> ux_device_class_storage_state = UX_DEVICE_CLASS_STORAGE_STATE_IDLE; /* Disk notified with USB error. */ if (storage -> ux_device_class_storage_disk_state != UX_DEVICE_CLASS_STORAGE_DISK_IDLE) { storage -> ux_device_class_storage_disk_state = UX_DEVICE_CLASS_STORAGE_DISK_USB_ERROR; } return(UX_STATE_WAIT); } /* Error case. */ if (status == UX_STATE_ERROR) { /* Stall. */ _ux_device_class_storage_halt_trans(storage); /* Disk notified with USB error. */ if (storage -> ux_device_class_storage_disk_state != UX_DEVICE_CLASS_STORAGE_DISK_IDLE) { storage -> ux_device_class_storage_disk_state = UX_DEVICE_CLASS_STORAGE_DISK_USB_ERROR; } /* Update residue. */ storage -> ux_slave_class_storage_csw_residue = storage -> ux_slave_class_storage_host_length - storage -> ux_device_class_storage_data_count; /* Update the REQUEST_SENSE codes. */ storage -> ux_slave_class_storage_lun[ storage -> ux_slave_class_storage_cbw_lun]. ux_slave_class_storage_request_sense_status = UX_DEVICE_CLASS_STORAGE_SENSE_STATUS(0x02,0x54,0x00); /* Issue CSW. */ _ux_device_class_storage_csw_send(storage, storage -> ux_slave_class_storage_cbw_lun, storage -> ux_device_class_storage_ep_in, 0); return(UX_STATE_WAIT); } /* Success case. */ if (status == UX_STATE_NEXT) { /* Update data count. */ storage -> ux_device_class_storage_data_count += storage -> ux_device_class_storage_transfer -> ux_slave_transfer_request_actual_length; storage -> ux_device_class_storage_state = UX_DEVICE_CLASS_STORAGE_STATE_TRANS_NEXT; } /* Other cases, keep waiting. */ return(UX_STATE_WAIT); case UX_DEVICE_CLASS_STORAGE_STATE_TRANS_NEXT: /* CBW received: -> CBW handle. */ if (storage -> ux_device_class_storage_cmd_state == UX_DEVICE_CLASS_STORAGE_CMD_CBW) { _ux_device_class_storage_cbw_process(storage); /* Apply command in next call anyway. */ return(UX_STATE_WAIT); } /* CSW sent: -> CSW done - CBW start. */ if (storage -> ux_device_class_storage_cmd_state == UX_DEVICE_CLASS_STORAGE_CMD_CSW) { if (storage -> ux_slave_class_storage_csw_status == UX_SLAVE_CLASS_STORAGE_CSW_PHASE_ERROR) { storage -> ux_device_class_storage_state = UX_DEVICE_CLASS_STORAGE_STATE_RESET; } else { _ux_device_class_storage_cbw_receive(storage); } continue; } /* DATA done: -> process data. */ return _ux_device_class_storage_data_next(storage); case UX_DEVICE_CLASS_STORAGE_STATE_DISK_ERROR: _ux_device_class_storage_trans_error(storage); continue; case UX_DEVICE_CLASS_STORAGE_STATE_IDLE: /* Nothing to do, fall through. */ case UX_DEVICE_CLASS_STORAGE_STATE_DISK_WAIT: /* Nothing to do, fall through. */ default: /* Do nothing. */ break; } /* Unhandled, just break the loop and do again by app call. */ immediate_state = UX_FALSE; } /* Unhandled state. */ return(UX_STATE_EXIT); } static inline VOID _ux_device_class_storage_cbw_receive(UX_SLAVE_CLASS_STORAGE *storage) { UX_SLAVE_ENDPOINT *endpoint; UX_SLAVE_TRANSFER *transfer; ULONG max_packet_size; /* Command state: CBW. */ storage -> ux_device_class_storage_cmd_state = UX_DEVICE_CLASS_STORAGE_CMD_CBW; storage -> ux_device_class_storage_data_buffer = UX_NULL; /* Transfer state: START: OUT 31. */ storage -> ux_device_class_storage_state = UX_DEVICE_CLASS_STORAGE_STATE_TRANS_START; endpoint = storage -> ux_device_class_storage_ep_out; max_packet_size = endpoint -> ux_slave_endpoint_descriptor.wMaxPacketSize; transfer = &endpoint -> ux_slave_endpoint_transfer_request; storage -> ux_device_class_storage_transfer = transfer; storage -> ux_device_class_storage_data_length = max_packet_size; storage -> ux_device_class_storage_data_count = 0; } static inline VOID _ux_device_class_storage_cbw_process(UX_SLAVE_CLASS_STORAGE *storage) { UX_SLAVE_TRANSFER *cbw_trans; ULONG cbw_length; UCHAR *cbw; /* Get transfer. */ cbw_trans = storage -> ux_device_class_storage_transfer; /* If CBW is stalled, just retry. */ if (cbw_trans -> ux_slave_transfer_request_completion_code == UX_TRANSFER_STALLED) { _ux_device_class_storage_cbw_receive(storage); return; } /* Get CBW and length. */ cbw_length = cbw_trans -> ux_slave_transfer_request_actual_length; cbw = cbw_trans -> ux_slave_transfer_request_data_pointer; if (cbw_length != UX_SLAVE_CLASS_STORAGE_CBW_LENGTH) { storage -> ux_slave_class_storage_csw_status = UX_SLAVE_CLASS_STORAGE_CSW_PHASE_ERROR; } else { _ux_device_class_storage_cmd_process(storage, cbw); } /* If still in CBW phase, there must be CBW structure error, wait reset. */ if (storage -> ux_device_class_storage_cmd_state == UX_DEVICE_CLASS_STORAGE_CMD_CBW) { storage -> ux_slave_class_storage_csw_status = UX_SLAVE_CLASS_STORAGE_CSW_PHASE_ERROR; storage -> ux_device_class_storage_state = UX_DEVICE_CLASS_STORAGE_STATE_RESET; return; } /* If no error, error cases need check. */ if (storage -> ux_slave_class_storage_csw_status == UX_SLAVE_CLASS_STORAGE_CSW_PASSED) { _ux_device_class_storage_data_cases_check(storage); } /* Error, stall and send CSW. */ if (storage -> ux_slave_class_storage_csw_status) { /* There will not be any disk operation in this case. */ storage -> ux_device_class_storage_disk_state = UX_DEVICE_CLASS_STORAGE_DISK_IDLE; if (storage -> ux_slave_class_storage_host_length && (storage -> ux_slave_class_storage_cbw_flags & UX_DEVICE_CLASS_STORAGE_CBW_FLAG_DIR) == 0) { _ux_device_class_storage_halt_out(storage); } else { _ux_device_class_storage_halt_in(storage); } /* Still need CSW phase. */ if (storage -> ux_device_class_storage_cmd_state != UX_DEVICE_CLASS_STORAGE_CMD_CBW) storage -> ux_device_class_storage_cmd_state = UX_DEVICE_CLASS_STORAGE_CMD_ERR; } /* No error, state not changed. */ /* Start CSW if there is no data. */ if (storage -> ux_device_class_storage_cmd_state < UX_DEVICE_CLASS_STORAGE_CMD_WRITE) { _ux_device_class_storage_csw_send(storage, storage -> ux_slave_class_storage_cbw_lun, storage -> ux_device_class_storage_ep_in, 0); } } static inline VOID _ux_device_class_storage_cmd_process(UX_SLAVE_CLASS_STORAGE *storage, UCHAR *cbw) { UX_SLAVE_ENDPOINT *endpoint_in, *endpoint_out; ULONG cbwcb_length; UCHAR *cbwcb; UCHAR lun; /* Get bCBWLUN. */ lun = *(cbw + UX_SLAVE_CLASS_STORAGE_CBW_LUN); storage -> ux_slave_class_storage_cbw_lun = lun; /* Get bmCBWFlags. */ storage -> ux_slave_class_storage_cbw_flags = *(cbw + UX_SLAVE_CLASS_STORAGE_CBW_FLAGS); /* Get dCBWTag. */ storage -> ux_slave_class_storage_scsi_tag = _ux_utility_long_get(cbw + UX_SLAVE_CLASS_STORAGE_CBW_TAG); /* Get dCBWDataTransferLength: number of bytes to transfer. */ storage -> ux_slave_class_storage_host_length = _ux_utility_long_get(cbw + UX_SLAVE_CLASS_STORAGE_CBW_DATA_LENGTH); /* Reset CSW status. */ storage -> ux_slave_class_storage_csw_residue = 0; storage -> ux_slave_class_storage_csw_status = 0; /* Check LUN error. */ if (lun >= storage -> ux_slave_class_storage_number_lun) { /* Phase error! */ storage -> ux_slave_class_storage_csw_status = UX_SLAVE_CLASS_STORAGE_CSW_PHASE_ERROR; return; } /* Check Signature error. */ if (_ux_utility_long_get(cbw) != UX_SLAVE_CLASS_STORAGE_CBW_SIGNATURE_MASK) { /* Phase error! */ storage -> ux_slave_class_storage_csw_status = UX_SLAVE_CLASS_STORAGE_CSW_PHASE_ERROR; return; } /* Get CBWCB length. */ cbwcb_length = (ULONG)*(cbw + UX_SLAVE_CLASS_STORAGE_CBW_CB_LENGTH); /* Check CBWCB length. */ if (cbwcb_length == 0) { /* Phase error! */ storage -> ux_slave_class_storage_csw_status = UX_SLAVE_CLASS_STORAGE_CSW_PHASE_ERROR; return; } /* Get endpoints. */ endpoint_in = storage -> ux_device_class_storage_ep_in; endpoint_out = storage -> ux_device_class_storage_ep_out; /* By default set next command state to CSW (no DATA). */ storage -> ux_device_class_storage_cmd_state = UX_DEVICE_CLASS_STORAGE_CMD_CSW; storage -> ux_device_class_storage_device_length = 0; storage -> ux_device_class_storage_data_length = 0; storage -> ux_device_class_storage_data_count = 0; /* Reset disk access state. */ storage -> ux_device_class_storage_disk_state = UX_DEVICE_CLASS_STORAGE_DISK_IDLE; /* Analyze the CBWCB command. */ cbwcb = cbw + UX_SLAVE_CLASS_STORAGE_CBW_CB; storage -> ux_device_class_storage_cmd = *cbwcb; switch(storage -> ux_device_class_storage_cmd) { case UX_SLAVE_CLASS_STORAGE_SCSI_TEST_READY: _ux_device_class_storage_test_ready(storage, lun, endpoint_in, endpoint_out, cbwcb); break; case UX_SLAVE_CLASS_STORAGE_SCSI_REQUEST_SENSE: _ux_device_class_storage_request_sense(storage, lun, endpoint_in, endpoint_out, cbwcb); break; case UX_SLAVE_CLASS_STORAGE_SCSI_FORMAT: _ux_device_class_storage_format(storage, lun, endpoint_in, endpoint_out, cbwcb); break; case UX_SLAVE_CLASS_STORAGE_SCSI_INQUIRY: _ux_device_class_storage_inquiry(storage, lun, endpoint_in, endpoint_out, cbwcb); break; case UX_SLAVE_CLASS_STORAGE_SCSI_START_STOP: _ux_device_class_storage_start_stop(storage, lun, endpoint_in, endpoint_out, cbwcb); break; case UX_SLAVE_CLASS_STORAGE_SCSI_PREVENT_ALLOW_MEDIA_REMOVAL: _ux_device_class_storage_prevent_allow_media_removal(storage, lun, endpoint_in, endpoint_out, cbwcb); break; case UX_SLAVE_CLASS_STORAGE_SCSI_READ_FORMAT_CAPACITY: _ux_device_class_storage_read_format_capacity(storage, lun, endpoint_in, endpoint_out, cbwcb); break; case UX_SLAVE_CLASS_STORAGE_SCSI_READ_CAPACITY: _ux_device_class_storage_read_capacity(storage, lun, endpoint_in, endpoint_out, cbwcb); break; case UX_SLAVE_CLASS_STORAGE_SCSI_VERIFY: _ux_device_class_storage_verify(storage, lun, endpoint_in, endpoint_out, cbwcb); break; case UX_SLAVE_CLASS_STORAGE_SCSI_MODE_SELECT: _ux_device_class_storage_mode_select(storage, lun, endpoint_in, endpoint_out, cbwcb); break; case UX_SLAVE_CLASS_STORAGE_SCSI_MODE_SENSE_SHORT: case UX_SLAVE_CLASS_STORAGE_SCSI_MODE_SENSE: _ux_device_class_storage_mode_sense(storage, lun, endpoint_in, endpoint_out, cbwcb); break; case UX_SLAVE_CLASS_STORAGE_SCSI_READ32: _ux_device_class_storage_read(storage, lun, endpoint_in, endpoint_out, cbwcb, UX_SLAVE_CLASS_STORAGE_SCSI_READ32); break; case UX_SLAVE_CLASS_STORAGE_SCSI_READ16: _ux_device_class_storage_read(storage, lun, endpoint_in, endpoint_out, cbwcb, UX_SLAVE_CLASS_STORAGE_SCSI_READ16); break; case UX_SLAVE_CLASS_STORAGE_SCSI_WRITE32: _ux_device_class_storage_write(storage, lun, endpoint_in, endpoint_out, cbwcb, UX_SLAVE_CLASS_STORAGE_SCSI_WRITE32); break; case UX_SLAVE_CLASS_STORAGE_SCSI_WRITE16: _ux_device_class_storage_write(storage, lun, endpoint_in, endpoint_out, cbwcb, UX_SLAVE_CLASS_STORAGE_SCSI_WRITE16); break; case UX_SLAVE_CLASS_STORAGE_SCSI_SYNCHRONIZE_CACHE: _ux_device_class_storage_synchronize_cache(storage, lun, endpoint_in, endpoint_out, cbwcb, *(cbwcb)); break; #ifdef UX_SLAVE_CLASS_STORAGE_INCLUDE_MMC case UX_SLAVE_CLASS_STORAGE_SCSI_GET_STATUS_NOTIFICATION: _ux_device_class_storage_get_status_notification(storage, lun, endpoint_in, endpoint_out, cbwcb); break; case UX_SLAVE_CLASS_STORAGE_SCSI_GET_CONFIGURATION: _ux_device_class_storage_get_configuration(storage, lun, endpoint_in, endpoint_out, cbwcb); break; case UX_SLAVE_CLASS_STORAGE_SCSI_READ_DISK_INFORMATION: _ux_device_class_storage_read_disk_information(storage, lun, endpoint_in, endpoint_out, cbwcb); break; case UX_SLAVE_CLASS_STORAGE_SCSI_REPORT_KEY: _ux_device_class_storage_report_key(storage, lun, endpoint_in, endpoint_out, cbwcb); break; case UX_SLAVE_CLASS_STORAGE_SCSI_GET_PERFORMANCE: _ux_device_class_storage_get_performance(storage, lun, endpoint_in, endpoint_out, cbwcb); break; case UX_SLAVE_CLASS_STORAGE_SCSI_READ_DVD_STRUCTURE: _ux_device_class_storage_read_dvd_structure(storage, lun, endpoint_in, endpoint_out, cbwcb); break; case UX_SLAVE_CLASS_STORAGE_SCSI_READ_TOC: _ux_device_class_storage_read_toc(storage, lun, endpoint_in, endpoint_out, cbwcb); break; #endif default: /* The command is unknown or unsupported, fail. */ storage -> ux_slave_class_storage_csw_status = UX_SLAVE_CLASS_STORAGE_CSW_FAILED; /* Initialize the request sense keys. */ storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_sense_status = UX_DEVICE_CLASS_STORAGE_SENSE_STATUS(UX_SLAVE_CLASS_STORAGE_SENSE_KEY_ILLEGAL_REQUEST, UX_SLAVE_CLASS_STORAGE_ASC_KEY_INVALID_COMMAND,0); } } static inline VOID _ux_device_class_storage_data_cases_check(UX_SLAVE_CLASS_STORAGE *storage) { /* ============ 13 cases check. */ /* (1)Hn=Dn, (2)HnDn, (5)Hi>Di, (6)Hi=Di, (7)HiDo : DATA */ /* (9)Ho>Dn, (10)Ho<>Di, (11)Ho>Do, (12)Ho=Do, (13)Ho ux_slave_class_storage_cbw_flags & UX_DEVICE_CLASS_STORAGE_CBW_FLAG_DIR) { /* Hi. */ /* Case (2), (8). */ if (storage -> ux_device_class_storage_cmd_state == UX_DEVICE_CLASS_STORAGE_CMD_WRITE || storage -> ux_slave_class_storage_host_length == 0) { /* Phase error. */ storage -> ux_slave_class_storage_csw_status = UX_SLAVE_CLASS_STORAGE_CSW_PHASE_ERROR; return; } /* Case (7). */ if (storage -> ux_slave_class_storage_host_length < storage -> ux_device_class_storage_data_length) { /* Part of data will be sent. */ storage -> ux_device_class_storage_data_length = storage -> ux_slave_class_storage_host_length; } /* Case (4), (5), (6), (7). */ /* No touch for prepared transfer. */ } else { /* Ho. */ /* Case (3), (10), (13). */ if (storage -> ux_slave_class_storage_host_length < storage -> ux_device_class_storage_device_length || storage -> ux_device_class_storage_cmd_state == UX_DEVICE_CLASS_STORAGE_CMD_READ) { /* Phase error. */ storage -> ux_slave_class_storage_csw_status = UX_SLAVE_CLASS_STORAGE_CSW_PHASE_ERROR; return; } /* Case (9), (11). */ if (storage -> ux_slave_class_storage_host_length != storage -> ux_device_class_storage_device_length) { /* Update dCSWDataResidue. */ storage -> ux_slave_class_storage_csw_residue = storage -> ux_slave_class_storage_host_length - storage -> ux_device_class_storage_device_length; /* Failed. */ storage -> ux_slave_class_storage_csw_status = UX_SLAVE_CLASS_STORAGE_CSW_FAILED; return; } } } static inline UINT _ux_device_class_storage_reset_wait(UX_SLAVE_CLASS_STORAGE *storage) { /* Check PhaseError. */ if ((UCHAR)storage -> ux_slave_class_storage_csw_status != UX_SLAVE_CLASS_STORAGE_CSW_PHASE_ERROR) { /* Reset states. */ storage -> ux_device_class_storage_state = UX_STATE_RESET; } else { /* Keep CBW endpoints halted until status is reset. */ storage -> ux_device_class_storage_state = UX_DEVICE_CLASS_STORAGE_STATE_RESET; } return(UX_STATE_WAIT); } static inline UINT _ux_device_class_storage_data_next(UX_SLAVE_CLASS_STORAGE *storage) { switch(storage -> ux_device_class_storage_cmd) { /* Disk read. */ case UX_SLAVE_CLASS_STORAGE_SCSI_READ16: /* Fall through. */ case UX_SLAVE_CLASS_STORAGE_SCSI_READ32: /* Check if all data is done. */ if (storage -> ux_device_class_storage_data_count >= storage -> ux_device_class_storage_data_length) { /* Stall if host expects more data. */ if (storage -> ux_slave_class_storage_host_length > storage -> ux_device_class_storage_data_length) { _ux_device_class_storage_halt_in(storage); /* Update dCSWDataResidue. */ storage -> ux_slave_class_storage_csw_residue = storage -> ux_slave_class_storage_host_length - storage -> ux_device_class_storage_data_length; } /* Set to fail if device expects more data. */ if (storage -> ux_device_class_storage_device_length > storage -> ux_device_class_storage_data_count) { /* Update bCSWStatus. */ storage -> ux_slave_class_storage_csw_status = UX_SLAVE_CLASS_STORAGE_CSW_FAILED; } /* Issue CSW. */ _ux_device_class_storage_csw_send(storage, storage -> ux_slave_class_storage_cbw_lun, storage -> ux_device_class_storage_ep_in, 0 /* Not used. */); } else { /* Buffer sent, update buffer states. */ storage -> ux_device_class_storage_buffer_state[storage -> ux_device_class_storage_buffer_usb] = UX_DEVICE_CLASS_STORAGE_BUFFER_EMPTY; storage -> ux_device_class_storage_buffer_usb = !storage -> ux_device_class_storage_buffer_usb; storage -> ux_device_class_storage_transfer -> ux_slave_transfer_request_data_pointer = storage -> ux_device_class_storage_buffer[storage -> ux_device_class_storage_buffer_usb]; /* If disk read not started (waiting free buffer), start it. */ if (storage -> ux_device_class_storage_disk_state == UX_DEVICE_CLASS_STORAGE_DISK_USB_WAIT) { storage -> ux_device_class_storage_disk_state = UX_DEVICE_CLASS_STORAGE_DISK_OP_START; } /* If there is buffer ready, send next. */ if (storage -> ux_device_class_storage_buffer_state[storage->ux_device_class_storage_buffer_usb] == UX_DEVICE_CLASS_STORAGE_BUFFER_FULL) { storage -> ux_device_class_storage_state = UX_DEVICE_CLASS_STORAGE_STATE_TRANS_START; } else { /* Wait disk operation to fill buffer. */ storage -> ux_device_class_storage_state = UX_DEVICE_CLASS_STORAGE_STATE_DISK_WAIT; } } break; /* Disk write. */ case UX_SLAVE_CLASS_STORAGE_SCSI_WRITE16: /* Fall through. */ case UX_SLAVE_CLASS_STORAGE_SCSI_WRITE32: /* Buffer received, update buffer state. */ storage -> ux_device_class_storage_buffer_state[ storage -> ux_device_class_storage_buffer_usb] = UX_DEVICE_CLASS_STORAGE_BUFFER_FULL; /* If disk waiting data, Start disk write. */ if (storage -> ux_device_class_storage_disk_state == UX_DEVICE_CLASS_STORAGE_DISK_USB_WAIT) { storage -> ux_device_class_storage_disk_state = UX_DEVICE_CLASS_STORAGE_DISK_OP_START; } /* Check if all data is done. */ if (storage -> ux_device_class_storage_data_count >= storage -> ux_device_class_storage_data_length) { /* Wait disk operation done. */ storage -> ux_device_class_storage_state = UX_DEVICE_CLASS_STORAGE_STATE_DISK_WAIT; } else { /* Buffer received, update buffer states. */ storage -> ux_device_class_storage_buffer_usb = !storage -> ux_device_class_storage_buffer_usb; storage -> ux_device_class_storage_transfer -> ux_slave_transfer_request_data_pointer = storage -> ux_device_class_storage_buffer[ storage -> ux_device_class_storage_buffer_usb]; /* If there is buffer empty, start it. */ if (storage -> ux_device_class_storage_buffer_state[ storage -> ux_device_class_storage_buffer_usb] == UX_DEVICE_CLASS_STORAGE_BUFFER_EMPTY) { storage -> ux_device_class_storage_state = UX_DEVICE_CLASS_STORAGE_STATE_TRANS_START; } else { /* No buffer available, wait disk operation done. */ storage -> ux_device_class_storage_state = UX_DEVICE_CLASS_STORAGE_STATE_DISK_WAIT; } } break; /* No further data to send. */ default: /* Stall if host expects more data. */ if (storage -> ux_slave_class_storage_host_length > storage -> ux_device_class_storage_data_count) { _ux_device_class_storage_halt_trans(storage); /* Update dCSWDataResidue. */ storage -> ux_slave_class_storage_csw_residue = storage -> ux_slave_class_storage_host_length - storage -> ux_device_class_storage_data_length; } /* Set to fail if device expects more data. */ if (storage -> ux_device_class_storage_device_length > storage -> ux_device_class_storage_data_count) { /* Update bCSWStatus. */ storage -> ux_slave_class_storage_csw_status = UX_SLAVE_CLASS_STORAGE_CSW_FAILED; } /* Issue CSW. */ _ux_device_class_storage_csw_send(storage, storage -> ux_slave_class_storage_cbw_lun, storage -> ux_device_class_storage_ep_in, 0 /* Not used. */); } /* Next task state. */ return(UX_STATE_NEXT); } static inline VOID _ux_device_class_storage_trans_start(UX_SLAVE_CLASS_STORAGE *storage) { ULONG remaining, host_length, device_length; /* Get remaining transfer length. */ remaining = storage -> ux_device_class_storage_data_length - storage -> ux_device_class_storage_data_count; /* Check if data exceeds buffer length. */ if (remaining > UX_SLAVE_CLASS_STORAGE_BUFFER_SIZE) { /* Send full packets, without ZLP. */ host_length = UX_SLAVE_CLASS_STORAGE_BUFFER_SIZE; device_length = UX_SLAVE_CLASS_STORAGE_BUFFER_SIZE; } else { /* Send packets sliced based on host length and device length. */ device_length = remaining; /* USB CV test expecting stall but not ZLP, so host length is same, but not remaining. */ host_length = remaining; } /* Prepare data if necessary. */ if (storage -> ux_device_class_storage_data_buffer && device_length && storage -> ux_device_class_storage_cmd_state == UX_DEVICE_CLASS_STORAGE_CMD_READ) { _ux_utility_memory_copy(storage -> ux_device_class_storage_transfer -> ux_slave_transfer_request_data_pointer, storage -> ux_device_class_storage_data_buffer + storage -> ux_device_class_storage_data_count, device_length); /* Use case of memcpy is verified. */ } /* Save host length and device length for task states. */ storage -> ux_device_class_storage_trans_device_length = device_length; storage -> ux_device_class_storage_trans_host_length = host_length; /* To TRANS_WAIT. */ storage -> ux_device_class_storage_state = UX_DEVICE_CLASS_STORAGE_STATE_TRANS_WAIT; /* Reset transfer state. */ UX_SLAVE_TRANSFER_STATE_RESET(storage -> ux_device_class_storage_transfer); } static inline UINT _ux_device_class_storage_trans_wait(UX_SLAVE_CLASS_STORAGE *storage) { return _ux_device_stack_transfer_run(storage -> ux_device_class_storage_transfer, storage -> ux_device_class_storage_trans_device_length, storage -> ux_device_class_storage_trans_host_length); } static inline VOID _ux_device_class_storage_trans_error(UX_SLAVE_CLASS_STORAGE *storage) { /* Abort USB operation if transfer not done. */ if (storage -> ux_device_class_storage_data_count < storage -> ux_device_class_storage_data_length) { if (storage -> ux_slave_class_storage_cbw_flags & UX_DEVICE_CLASS_STORAGE_CBW_FLAG_IN) { _ux_device_class_storage_halt_in(storage); } else { _ux_device_class_storage_halt_out(storage); } /* Update dCSWDataResidue. */ storage -> ux_slave_class_storage_csw_residue = storage -> ux_slave_class_storage_host_length - storage -> ux_device_class_storage_data_count; } else { _ux_device_class_storage_halt_in(storage); } /* Issue CSW. */ _ux_device_class_storage_csw_send(storage, storage -> ux_slave_class_storage_cbw_lun, storage -> ux_device_class_storage_ep_in, 0 /* Not used. */); } static inline VOID _ux_device_class_storage_halt_out(UX_SLAVE_CLASS_STORAGE *storage) { _ux_device_stack_endpoint_stall(storage -> ux_device_class_storage_ep_out); } static inline VOID _ux_device_class_storage_halt_in(UX_SLAVE_CLASS_STORAGE *storage) { _ux_device_stack_endpoint_stall(storage -> ux_device_class_storage_ep_in); } static inline VOID _ux_device_class_storage_halt_trans(UX_SLAVE_CLASS_STORAGE *storage) { UX_SLAVE_TRANSFER *trans = storage -> ux_device_class_storage_transfer; UX_SLAVE_ENDPOINT *endp = trans -> ux_slave_transfer_request_endpoint; _ux_device_stack_endpoint_stall(endp); } static inline VOID _ux_device_class_storage_task_disk(UX_SLAVE_CLASS_STORAGE *storage) { UCHAR state = storage -> ux_device_class_storage_disk_state; UINT status; INT immediate_state = UX_TRUE; /* Run states once. */ while(immediate_state) { /* Update state. */ state = storage -> ux_device_class_storage_disk_state; switch(state) { case UX_DEVICE_CLASS_STORAGE_DISK_OP_START: _ux_device_class_storage_disk_start(storage); storage -> ux_device_class_storage_disk_state = UX_DEVICE_CLASS_STORAGE_DISK_OP_WAIT; /* Fall through. */ case UX_DEVICE_CLASS_STORAGE_DISK_OP_WAIT: status = _ux_device_class_storage_disk_wait(storage); /* Error case. */ if (status < UX_STATE_NEXT) { /* Disk idle. */ storage -> ux_device_class_storage_disk_state = UX_DEVICE_CLASS_STORAGE_DISK_IDLE; /* USB notified with disk error. */ storage -> ux_device_class_storage_state = UX_DEVICE_CLASS_STORAGE_STATE_DISK_ERROR; } /* Success case. */ if (status == UX_STATE_NEXT) { storage -> ux_device_class_storage_disk_state = UX_DEVICE_CLASS_STORAGE_DISK_OP_NEXT; } /* Other cases, keep waiting. */ return; case UX_DEVICE_CLASS_STORAGE_DISK_OP_NEXT: _ux_device_class_storage_disk_next(storage); return; case UX_DEVICE_CLASS_STORAGE_DISK_USB_ERROR: _ux_device_class_storage_disk_error(storage); break; case UX_DEVICE_CLASS_STORAGE_DISK_USB_WAIT: /* Nothing to do, fall through. */ case UX_DEVICE_CLASS_STORAGE_DISK_IDLE: /* Nothing to do, fall through. */ default: /* Nothing to do. */ break; } /* Task run once, break the loop. */ immediate_state = UX_FALSE; } } static inline VOID _ux_device_class_storage_disk_start(UX_SLAVE_CLASS_STORAGE *storage) { ULONG block_size; ULONG max_n_blocks; if (storage -> ux_device_class_storage_cmd == UX_SLAVE_CLASS_STORAGE_SCSI_SYNCHRONIZE_CACHE) { /* All things sync in one call. */ storage -> ux_device_class_storage_disk_n_lb = storage -> ux_device_class_storage_cmd_n_lb; return; } /* Read/write, split the operation by buffer sizes. */ /* Max blocks for one buffer. */ block_size = storage -> ux_slave_class_storage_lun[storage -> ux_slave_class_storage_cbw_lun]. ux_slave_class_storage_media_block_length; if (block_size == 0) UX_ASSERT(UX_FALSE); max_n_blocks = UX_SLAVE_CLASS_STORAGE_BUFFER_SIZE / block_size; /* Prepare next disk read. */ if (storage -> ux_device_class_storage_cmd_n_lb > max_n_blocks) { storage -> ux_device_class_storage_disk_n_lb = max_n_blocks; } else { storage -> ux_device_class_storage_disk_n_lb = storage -> ux_device_class_storage_cmd_n_lb; } } static inline UINT _ux_device_class_storage_disk_wait(UX_SLAVE_CLASS_STORAGE *storage) { switch (storage -> ux_device_class_storage_cmd) { case UX_SLAVE_CLASS_STORAGE_SCSI_READ16: case UX_SLAVE_CLASS_STORAGE_SCSI_READ32: return storage -> ux_slave_class_storage_lun[storage -> ux_slave_class_storage_cbw_lun]. ux_slave_class_storage_media_read(storage, storage -> ux_slave_class_storage_cbw_lun, storage -> ux_device_class_storage_buffer[ storage -> ux_device_class_storage_buffer_disk], storage -> ux_device_class_storage_disk_n_lb, storage -> ux_device_class_storage_cmd_lba, &storage -> ux_device_class_storage_media_status); case UX_SLAVE_CLASS_STORAGE_SCSI_WRITE16: case UX_SLAVE_CLASS_STORAGE_SCSI_WRITE32: return storage -> ux_slave_class_storage_lun[storage -> ux_slave_class_storage_cbw_lun]. ux_slave_class_storage_media_write(storage, storage -> ux_slave_class_storage_cbw_lun, storage -> ux_device_class_storage_buffer[ storage -> ux_device_class_storage_buffer_disk], storage -> ux_device_class_storage_disk_n_lb, storage -> ux_device_class_storage_cmd_lba, &storage -> ux_device_class_storage_media_status); case UX_SLAVE_CLASS_STORAGE_SCSI_SYNCHRONIZE_CACHE: return storage -> ux_slave_class_storage_lun[storage -> ux_slave_class_storage_cbw_lun]. ux_slave_class_storage_media_flush(storage, storage -> ux_slave_class_storage_cbw_lun, storage -> ux_device_class_storage_disk_n_lb, storage -> ux_device_class_storage_cmd_lba, &storage -> ux_device_class_storage_media_status); case UX_SLAVE_CLASS_STORAGE_SCSI_VERIFY: /* No nothing for now. */ default: break; } return(UX_STATE_NEXT); } static inline VOID _ux_device_class_storage_disk_next(UX_SLAVE_CLASS_STORAGE *storage) { /* Update disk operation status. */ storage -> ux_device_class_storage_cmd_lba += storage -> ux_device_class_storage_disk_n_lb; storage -> ux_device_class_storage_cmd_n_lb -= storage -> ux_device_class_storage_disk_n_lb; /* Next check is different for read/write. */ switch (storage -> ux_device_class_storage_cmd) { case UX_SLAVE_CLASS_STORAGE_SCSI_READ16: case UX_SLAVE_CLASS_STORAGE_SCSI_READ32: _ux_device_class_storage_disk_read_next(storage); return; case UX_SLAVE_CLASS_STORAGE_SCSI_WRITE16: case UX_SLAVE_CLASS_STORAGE_SCSI_WRITE32: _ux_device_class_storage_disk_write_next(storage); return; case UX_SLAVE_CLASS_STORAGE_SCSI_SYNCHRONIZE_CACHE: /* Disk is idle now. */ storage -> ux_device_class_storage_disk_state = UX_DEVICE_CLASS_STORAGE_DISK_IDLE; /* Send CSW if not sent yet. */ if (storage -> ux_device_class_storage_state == UX_DEVICE_CLASS_STORAGE_STATE_DISK_WAIT) { _ux_device_class_storage_csw_send(storage, storage -> ux_slave_class_storage_cbw_lun, storage -> ux_device_class_storage_ep_in, 0 /* Not used. */); } return; default: storage -> ux_device_class_storage_disk_state = UX_DEVICE_CLASS_STORAGE_DISK_IDLE; break; } } static inline VOID _ux_device_class_storage_disk_read_next(UX_SLAVE_CLASS_STORAGE *storage) { /* Update buffer state : full. */ storage -> ux_device_class_storage_buffer_state[ storage -> ux_device_class_storage_buffer_disk] = UX_DEVICE_CLASS_STORAGE_BUFFER_FULL; /* Check if all disk operation is done. */ if (storage -> ux_device_class_storage_cmd_n_lb == 0) { storage -> ux_device_class_storage_disk_state = UX_DEVICE_CLASS_STORAGE_DISK_IDLE; } else { /* Update buffer index. */ storage -> ux_device_class_storage_buffer_disk = !storage -> ux_device_class_storage_buffer_disk; /* If buffer is free, start next read. */ if (UX_DEVICE_CLASS_STORAGE_BUFFER_EMPTY == storage -> ux_device_class_storage_buffer_state[ storage -> ux_device_class_storage_buffer_disk]) { /* Start next read. */ storage -> ux_device_class_storage_disk_state = UX_DEVICE_CLASS_STORAGE_DISK_OP_START; } else { /* Wait until buffer sent by USB. */ storage -> ux_device_class_storage_disk_state = UX_DEVICE_CLASS_STORAGE_DISK_USB_WAIT; } } /* Start USB transfer. */ if (storage -> ux_device_class_storage_state == UX_DEVICE_CLASS_STORAGE_STATE_DISK_WAIT && UX_DEVICE_CLASS_STORAGE_BUFFER_FULL == storage -> ux_device_class_storage_buffer_state[ storage -> ux_device_class_storage_buffer_usb]) { storage -> ux_device_class_storage_state = UX_DEVICE_CLASS_STORAGE_STATE_TRANS_START; } } static inline VOID _ux_device_class_storage_disk_write_next(UX_SLAVE_CLASS_STORAGE *storage) { /* Update buffer state : empty. */ storage -> ux_device_class_storage_buffer_state[ storage -> ux_device_class_storage_buffer_disk] = UX_DEVICE_CLASS_STORAGE_BUFFER_EMPTY; /* Check if all disk operation is done. */ if (storage -> ux_device_class_storage_cmd_n_lb == 0) { /* Disk is idle now. */ storage -> ux_device_class_storage_disk_state = UX_DEVICE_CLASS_STORAGE_DISK_IDLE; /* Issue CSW. */ _ux_device_class_storage_csw_send(storage, storage -> ux_slave_class_storage_cbw_lun, storage -> ux_device_class_storage_ep_in, 0 /* Not used. */); } else { /* Update buffer index. */ storage -> ux_device_class_storage_buffer_disk = !storage -> ux_device_class_storage_buffer_disk; /* If buffer is full, start next write. */ if (UX_DEVICE_CLASS_STORAGE_BUFFER_FULL == storage -> ux_device_class_storage_buffer_state[ storage -> ux_device_class_storage_buffer_disk]) { /* Start next write. */ storage -> ux_device_class_storage_disk_state = UX_DEVICE_CLASS_STORAGE_DISK_OP_START; } else { /* Wait until buffer filled by USB. */ storage -> ux_device_class_storage_disk_state = UX_DEVICE_CLASS_STORAGE_DISK_USB_WAIT; } /* Start USB transfer. */ if (storage -> ux_device_class_storage_state == UX_DEVICE_CLASS_STORAGE_STATE_DISK_WAIT && UX_DEVICE_CLASS_STORAGE_BUFFER_EMPTY == storage -> ux_device_class_storage_buffer_state[ storage -> ux_device_class_storage_buffer_usb]) { storage -> ux_device_class_storage_state = UX_DEVICE_CLASS_STORAGE_STATE_TRANS_START; } } } static inline VOID _ux_device_class_storage_disk_error(UX_SLAVE_CLASS_STORAGE *storage) { /* Abort disk operation: read or write with NULL! */ switch (storage -> ux_device_class_storage_cmd) { case UX_SLAVE_CLASS_STORAGE_SCSI_READ16: case UX_SLAVE_CLASS_STORAGE_SCSI_READ32: storage -> ux_slave_class_storage_lun[storage -> ux_slave_class_storage_cbw_lun]. ux_slave_class_storage_media_read(storage, storage -> ux_slave_class_storage_cbw_lun, UX_NULL, 0, 0, UX_NULL); break; case UX_SLAVE_CLASS_STORAGE_SCSI_WRITE16: case UX_SLAVE_CLASS_STORAGE_SCSI_WRITE32: storage -> ux_slave_class_storage_lun[storage -> ux_slave_class_storage_cbw_lun]. ux_slave_class_storage_media_write(storage, storage -> ux_slave_class_storage_cbw_lun, UX_NULL, 0, 0, UX_NULL); break; default: break; } /* Change disk state to IDLE. */ storage -> ux_device_class_storage_disk_state = UX_DEVICE_CLASS_STORAGE_DISK_IDLE; } #endif