/*************************************************************************** * 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) /**************************************************************************/ /* */ /* FUNCTION RELEASE */ /* */ /* _ux_device_class_storage_thread PORTABLE C */ /* 6.3.0 */ /* AUTHOR */ /* */ /* Chaoqiong Xiao, Microsoft Corporation */ /* */ /* DESCRIPTION */ /* */ /* This function is the thread of the storage class. */ /* */ /* It's for RTOS mode. */ /* */ /* INPUT */ /* */ /* class Address of storage class */ /* container */ /* */ /* OUTPUT */ /* */ /* None */ /* */ /* 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_device_semaphore_create Create semaphore */ /* _ux_utility_delay_ms Sleep thread for several ms */ /* _ux_device_thread_suspend Suspend thread */ /* */ /* CALLED BY */ /* */ /* ThreadX */ /* */ /**************************************************************************/ VOID _ux_device_class_storage_thread(ULONG storage_class) { UX_SLAVE_CLASS *class_ptr; UX_SLAVE_CLASS_STORAGE *storage; UX_SLAVE_TRANSFER *transfer_request; UX_SLAVE_DEVICE *device; UX_SLAVE_INTERFACE *interface_ptr; UX_SLAVE_ENDPOINT *endpoint_in; UX_SLAVE_ENDPOINT *endpoint_out; UINT status; ULONG length; ULONG cbwcb_length; ULONG lun; UCHAR *scsi_command; UCHAR *cbw_cb; /* This thread runs forever but can be suspended or resumed. */ while(1) { /* Cast properly the storage instance. */ UX_THREAD_EXTENSION_PTR_GET(class_ptr, UX_SLAVE_CLASS, storage_class) /* Get the storage instance from this class container. */ storage = (UX_SLAVE_CLASS_STORAGE *) class_ptr -> ux_slave_class_instance; /* Get the pointer to the device. */ device = &_ux_system_slave -> ux_system_slave_device; /* As long as the device is in the CONFIGURED state. */ while (device -> ux_slave_device_state == UX_DEVICE_CONFIGURED) { /* We are activated. We need the interface to the class. */ interface_ptr = storage -> ux_slave_class_storage_interface; /* We assume the worst situation. */ status = UX_ERROR; /* Locate the endpoints. */ endpoint_in = interface_ptr -> ux_slave_interface_first_endpoint; /* Check the endpoint direction, if IN we have the correct endpoint. */ if ((endpoint_in -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) != UX_ENDPOINT_IN) { /* Wrong direction, we found the OUT endpoint first. */ endpoint_out = endpoint_in; /* So the next endpoint has to be the IN endpoint. */ endpoint_in = endpoint_out -> ux_slave_endpoint_next_endpoint; } else { /* We found the endpoint IN first, so next endpoint is OUT. */ endpoint_out = endpoint_in -> ux_slave_endpoint_next_endpoint; } #if UX_DEVICE_ENDPOINT_BUFFER_OWNER == 1 /* Assign endpoint buffers. */ endpoint_out -> ux_slave_endpoint_transfer_request. ux_slave_transfer_request_data_pointer = UX_DEVICE_CLASS_STORAGE_BULKOUT_BUFFER(storage); endpoint_in -> ux_slave_endpoint_transfer_request. ux_slave_transfer_request_data_pointer = UX_DEVICE_CLASS_STORAGE_BULKIN_BUFFER(storage); #endif /* All SCSI commands are on the endpoint OUT, from the host. */ transfer_request = &endpoint_out -> ux_slave_endpoint_transfer_request; /* Check state, they must be both RESET. */ if (endpoint_out -> ux_slave_endpoint_state == UX_ENDPOINT_RESET && (UCHAR)storage -> ux_slave_class_storage_csw_status != UX_SLAVE_CLASS_STORAGE_CSW_PHASE_ERROR) { /* Send the request to the device controller. */ status = _ux_device_stack_transfer_request(transfer_request, 64, 64); } /* Check the status. Our status is UX_ERROR if one of the endpoint was STALLED. We must wait for the host to clear the mess. */ if (status == UX_SUCCESS) { /* Obtain the length of the transaction. */ length = transfer_request -> ux_slave_transfer_request_actual_length; /* Obtain the buffer address containing the SCSI command. */ scsi_command = transfer_request -> ux_slave_transfer_request_data_pointer; /* Obtain the lun from the CBW. */ lun = (ULONG) *(scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_LUN); storage -> ux_slave_class_storage_cbw_lun = (UCHAR)lun; /* We have to memorize the SCSI command tag for the CSW phase. */ storage -> ux_slave_class_storage_scsi_tag = _ux_utility_long_get(scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_TAG); /* Get dCBWDataTransferLength: number of bytes to transfer. */ storage -> ux_slave_class_storage_host_length = _ux_utility_long_get(scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_DATA_LENGTH); /* Save bmCBWFlags. */ storage -> ux_slave_class_storage_cbw_flags = *(scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_FLAGS); /* Reset CSW status. */ storage -> ux_slave_class_storage_csw_residue = 0; storage -> ux_slave_class_storage_csw_status = 0; /* Ensure the LUN number is within our declared values and check the command content and format. First we make sure we have a complete CBW. */ if ((lun < storage -> ux_slave_class_storage_number_lun) && (length == UX_SLAVE_CLASS_STORAGE_CBW_LENGTH)) { /* The length of the CBW is correct, analyze the header. */ if (_ux_utility_long_get(scsi_command) == UX_SLAVE_CLASS_STORAGE_CBW_SIGNATURE_MASK) { /* Get the length of the CBWCB. */ cbwcb_length = (ULONG) *(scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_CB_LENGTH); /* Check the length of the CBWCB to ensure there is at least a command. */ if (cbwcb_length != 0) { /* Analyze the command stored in the CBWCB. */ cbw_cb = scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_CB; switch (*(cbw_cb)) { case UX_SLAVE_CLASS_STORAGE_SCSI_TEST_READY: _ux_device_class_storage_test_ready(storage, lun, endpoint_in, endpoint_out, cbw_cb); break; case UX_SLAVE_CLASS_STORAGE_SCSI_REQUEST_SENSE: _ux_device_class_storage_request_sense(storage, lun, endpoint_in, endpoint_out, cbw_cb); break; case UX_SLAVE_CLASS_STORAGE_SCSI_FORMAT: _ux_device_class_storage_format(storage, lun, endpoint_in, endpoint_out, cbw_cb); break; case UX_SLAVE_CLASS_STORAGE_SCSI_INQUIRY: _ux_device_class_storage_inquiry(storage, lun, endpoint_in, endpoint_out, cbw_cb); break; case UX_SLAVE_CLASS_STORAGE_SCSI_START_STOP: _ux_device_class_storage_start_stop(storage, lun, endpoint_in, endpoint_out, cbw_cb); 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, cbw_cb); break; case UX_SLAVE_CLASS_STORAGE_SCSI_READ_FORMAT_CAPACITY: _ux_device_class_storage_read_format_capacity(storage, lun, endpoint_in, endpoint_out, cbw_cb); break; case UX_SLAVE_CLASS_STORAGE_SCSI_READ_CAPACITY: _ux_device_class_storage_read_capacity(storage, lun, endpoint_in, endpoint_out, cbw_cb); break; case UX_SLAVE_CLASS_STORAGE_SCSI_VERIFY: _ux_device_class_storage_verify(storage, lun, endpoint_in, endpoint_out, cbw_cb); break; case UX_SLAVE_CLASS_STORAGE_SCSI_MODE_SELECT: _ux_device_class_storage_mode_select(storage, lun, endpoint_in, endpoint_out, cbw_cb); 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, cbw_cb); break; case UX_SLAVE_CLASS_STORAGE_SCSI_READ32: _ux_device_class_storage_read(storage, lun, endpoint_in, endpoint_out, cbw_cb, 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, cbw_cb, 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, cbw_cb, 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, cbw_cb, 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, cbw_cb, *(cbw_cb)); 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, cbw_cb); break; case UX_SLAVE_CLASS_STORAGE_SCSI_GET_CONFIGURATION: _ux_device_class_storage_get_configuration(storage, lun, endpoint_in, endpoint_out, cbw_cb); break; case UX_SLAVE_CLASS_STORAGE_SCSI_READ_DISK_INFORMATION: _ux_device_class_storage_read_disk_information(storage, lun, endpoint_in, endpoint_out, cbw_cb); break; case UX_SLAVE_CLASS_STORAGE_SCSI_REPORT_KEY: _ux_device_class_storage_report_key(storage, lun, endpoint_in, endpoint_out, cbw_cb); break; case UX_SLAVE_CLASS_STORAGE_SCSI_GET_PERFORMANCE: _ux_device_class_storage_get_performance(storage, lun, endpoint_in, endpoint_out, cbw_cb); break; case UX_SLAVE_CLASS_STORAGE_SCSI_READ_DVD_STRUCTURE: _ux_device_class_storage_read_dvd_structure(storage, lun, endpoint_in, endpoint_out, cbw_cb); break; case UX_SLAVE_CLASS_STORAGE_SCSI_READ_TOC: status = _ux_device_class_storage_read_toc(storage, lun, endpoint_in, endpoint_out, cbw_cb); /* Special treatment of TOC command. If error, default to Stall endpoint. */ if (status == UX_SUCCESS) break; #endif /* fall through */ default: /* The command is unknown or unsupported, so we stall the endpoint. */ if (storage -> ux_slave_class_storage_host_length > 0 && ((storage -> ux_slave_class_storage_cbw_flags & 0x80) == 0)) /* Data-Out from host to device, stall OUT. */ _ux_device_stack_endpoint_stall(endpoint_out); else /* Data-In from device to host, stall IN. */ _ux_device_stack_endpoint_stall(endpoint_in); /* 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); /* This is the tricky part of the SCSI state machine. We must send the CSW BUT need to wait for the endpoint_in to be reset by the host. */ while (device -> ux_slave_device_state == UX_DEVICE_CONFIGURED) { /* Check the endpoint state. */ if (endpoint_in -> ux_slave_endpoint_state == UX_ENDPOINT_RESET) { /* Now we set the CSW with failure. */ storage -> ux_slave_class_storage_csw_status = UX_SLAVE_CLASS_STORAGE_CSW_FAILED; break; } else /* We must therefore wait a while. */ _ux_device_thread_relinquish(); } break; } /* Send CSW if not SYNC_CACHE. */ status = _ux_device_class_storage_csw_send(storage, lun, endpoint_in, 0 /* Don't care */); /* Check error code. */ if (status != UX_SUCCESS) /* Error trap. */ _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, status); } else /* Phase error! */ storage -> ux_slave_class_storage_csw_status = UX_SLAVE_CLASS_STORAGE_CSW_PHASE_ERROR; } else /* Phase error! */ storage -> ux_slave_class_storage_csw_status = UX_SLAVE_CLASS_STORAGE_CSW_PHASE_ERROR; } else /* Phase error! */ storage -> ux_slave_class_storage_csw_status = UX_SLAVE_CLASS_STORAGE_CSW_PHASE_ERROR; } else { if ((UCHAR)storage -> ux_slave_class_storage_csw_status == UX_SLAVE_CLASS_STORAGE_CSW_PHASE_ERROR) { /* We should keep the endpoints stalled. */ _ux_device_stack_endpoint_stall(endpoint_out); _ux_device_stack_endpoint_stall(endpoint_in); } /* We must therefore wait a while. */ _ux_utility_delay_ms(2); } } /* We need to suspend ourselves. We will be resumed by the device enumeration module. */ _ux_device_thread_suspend(&class_ptr -> ux_slave_class_thread); } } #endif