| 1 |  |  | /*************************************************************************** | 
    
    | 2 |  |  |  * Copyright (c) 2024 Microsoft Corporation | 
    
    | 3 |  |  |  * | 
    
    | 4 |  |  |  * This program and the accompanying materials are made available under the | 
    
    | 5 |  |  |  * terms of the MIT License which is available at | 
    
    | 6 |  |  |  * https://opensource.org/licenses/MIT. | 
    
    | 7 |  |  |  * | 
    
    | 8 |  |  |  * SPDX-License-Identifier: MIT | 
    
    | 9 |  |  |  **************************************************************************/ | 
    
    | 10 |  |  |  | 
    
    | 11 |  |  |  | 
    
    | 12 |  |  | /**************************************************************************/ | 
    
    | 13 |  |  | /**************************************************************************/ | 
    
    | 14 |  |  | /**                                                                       */ | 
    
    | 15 |  |  | /** USBX Component                                                        */ | 
    
    | 16 |  |  | /**                                                                       */ | 
    
    | 17 |  |  | /**   Storage Class                                                       */ | 
    
    | 18 |  |  | /**                                                                       */ | 
    
    | 19 |  |  | /**************************************************************************/ | 
    
    | 20 |  |  | /**************************************************************************/ | 
    
    | 21 |  |  |  | 
    
    | 22 |  |  |  | 
    
    | 23 |  |  | /* Include necessary system files.  */ | 
    
    | 24 |  |  |  | 
    
    | 25 |  |  | #define UX_SOURCE_CODE | 
    
    | 26 |  |  |  | 
    
    | 27 |  |  | #include "ux_api.h" | 
    
    | 28 |  |  | #include "ux_host_class_storage.h" | 
    
    | 29 |  |  | #include "ux_host_stack.h" | 
    
    | 30 |  |  |  | 
    
    | 31 |  |  |  | 
    
    | 32 |  |  | #if !defined(UX_HOST_STANDALONE) | 
    
    | 33 |  |  | /**************************************************************************/ | 
    
    | 34 |  |  | /*                                                                        */ | 
    
    | 35 |  |  | /*  FUNCTION                                               RELEASE        */ | 
    
    | 36 |  |  | /*                                                                        */ | 
    
    | 37 |  |  | /*    _ux_host_class_storage_transport_bo                 PORTABLE C      */ | 
    
    | 38 |  |  | /*                                                           6.1.10       */ | 
    
    | 39 |  |  | /*  AUTHOR                                                                */ | 
    
    | 40 |  |  | /*                                                                        */ | 
    
    | 41 |  |  | /*    Chaoqiong Xiao, Microsoft Corporation                               */ | 
    
    | 42 |  |  | /*                                                                        */ | 
    
    | 43 |  |  | /*  DESCRIPTION                                                           */ | 
    
    | 44 |  |  | /*                                                                        */ | 
    
    | 45 |  |  | /*    This function is the transport layer for the Bulk Only protocol.    */ | 
    
    | 46 |  |  | /*                                                                        */ | 
    
    | 47 |  |  | /*  INPUT                                                                 */ | 
    
    | 48 |  |  | /*                                                                        */ | 
    
    | 49 |  |  | /*    storage                               Pointer to storage class      */ | 
    
    | 50 |  |  | /*    data_pointer                          Pointer to data               */ | 
    
    | 51 |  |  | /*                                                                        */ | 
    
    | 52 |  |  | /*  OUTPUT                                                                */ | 
    
    | 53 |  |  | /*                                                                        */ | 
    
    | 54 |  |  | /*    The transfer completion status. It's possible for the transfer to   */ | 
    
    | 55 |  |  | /*    succeed but for the command to fail. The CSW must be checked to     */ | 
    
    | 56 |  |  | /*    determine command status.                                           */ | 
    
    | 57 |  |  | /*                                                                        */ | 
    
    | 58 |  |  | /*  CALLS                                                                 */ | 
    
    | 59 |  |  | /*                                                                        */ | 
    
    | 60 |  |  | /*    _ux_host_class_storage_device_reset   Reset mass storage            */ | 
    
    | 61 |  |  | /*    _ux_host_stack_endpoint_reset         Reset endpoint                */ | 
    
    | 62 |  |  | /*    _ux_host_stack_transfer_request       Process host stack transfer   */ | 
    
    | 63 |  |  | /*    _ux_host_stack_transfer_request_abort Abort transfer request        */ | 
    
    | 64 |  |  | /*    _ux_utility_memory_allocate           Allocate memory               */ | 
    
    | 65 |  |  | /*    _ux_utility_memory_free               Free memory                   */ | 
    
    | 66 |  |  | /*    _ux_utility_long_get                  Get 32-bit word               */ | 
    
    | 67 |  |  | /*    _ux_host_semaphore_get                Get semaphore                 */ | 
    
    | 68 |  |  | /*                                                                        */ | 
    
    | 69 |  |  | /*  CALLED BY                                                             */ | 
    
    | 70 |  |  | /*                                                                        */ | 
    
    | 71 |  |  | /*    Storage Class                                                       */ | 
    
    | 72 |  |  | /*                                                                        */ | 
    
    | 73 |  |  | /*  RELEASE HISTORY                                                       */ | 
    
    | 74 |  |  | /*                                                                        */ | 
    
    | 75 |  |  | /*    DATE              NAME                      DESCRIPTION             */ | 
    
    | 76 |  |  | /*                                                                        */ | 
    
    | 77 |  |  | /*  05-19-2020     Chaoqiong Xiao           Initial Version 6.0           */ | 
    
    | 78 |  |  | /*  09-30-2020     Chaoqiong Xiao           Modified comment(s),          */ | 
    
    | 79 |  |  | /*                                            prefixed UX to MS_TO_TICK,  */ | 
    
    | 80 |  |  | /*                                            resulting in version 6.1    */ | 
    
    | 81 |  |  | /*  01-31-2022     Chaoqiong Xiao           Modified comment(s),          */ | 
    
    | 82 |  |  | /*                                            refined macros names,       */ | 
    
    | 83 |  |  | /*                                            resulting in version 6.1.10 */ | 
    
    | 84 |  |  | /*                                                                        */ | 
    
    | 85 |  |  | /**************************************************************************/ | 
    
    | 86 |  | 2624 | UINT  _ux_host_class_storage_transport_bo(UX_HOST_CLASS_STORAGE *storage, UCHAR *data_pointer) | 
    
    | 87 |  |  | { | 
    
    | 88 |  |  |  | 
    
    | 89 |  |  | UX_TRANSFER     *transfer_request; | 
    
    | 90 |  |  | UINT            status; | 
    
    | 91 |  |  | UCHAR           *cbw; | 
    
    | 92 |  |  | UINT            retry; | 
    
    | 93 |  |  | ULONG           data_phase_requested_length; | 
    
    | 94 |  |  | ULONG           data_phase_transfer_size; | 
    
    | 95 |  |  | UCHAR           *get_status_response; | 
    
    | 96 |  |  |  | 
    
    | 97 |  |  |  | 
    
    | 98 |  |  |     /* Get the pointer to the transfer request.  */ | 
    
    | 99 |  | 2624 |     transfer_request =  &storage -> ux_host_class_storage_bulk_out_endpoint -> ux_endpoint_transfer_request; | 
    
    | 100 |  |  |  | 
    
    | 101 |  |  |     /* Use a pointer for the cbw, easier to manipulate.  */ | 
    
    | 102 |  | 2624 |     cbw =  (UCHAR *) storage -> ux_host_class_storage_cbw; | 
    
    | 103 |  |  |  | 
    
    | 104 |  |  |     /* Fill in the transfer request parameters.  */ | 
    
    | 105 |  | 2624 |     transfer_request -> ux_transfer_request_data_pointer =      cbw; | 
    
    | 106 |  | 2624 |     transfer_request -> ux_transfer_request_requested_length =  UX_HOST_CLASS_STORAGE_CBW_LENGTH; | 
    
    | 107 |  |  |  | 
    
    | 108 |  |  |     /* It's possible for the bulk out endpoint to stall; in that case, we clear | 
    
    | 109 |  |  |        it and try again.  */ | 
    
    | 110 |  | 2624 |     for (retry =  0; ; retry++) | 
    
    | 111 |  |  |     { | 
    
    | 112 |  |  |  | 
    
    | 113 |  |  |         /* Send the CBW on the bulk out endpoint.  */ | 
    
    | 114 |  | 2632 |         status =  _ux_host_stack_transfer_request(transfer_request); | 
    
    | 115 |  |  |  | 
    
    | 116 |  |  |         /* Check the status of the command (NB, the command is not sent yet since | 
    
    | 117 |  |  |            the bulk transport is non blocking).  */ | 
    
    | 118 | ✓✓ | 2632 |         if (status != UX_SUCCESS) | 
    
    | 119 |  | 12 |             return(status); | 
    
    | 120 |  |  |  | 
    
    | 121 |  |  |         /* Wait for the completion of the transfer request.  */ | 
    
    | 122 |  | 2620 |         status =  _ux_host_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, UX_MS_TO_TICK(UX_HOST_CLASS_STORAGE_TRANSFER_TIMEOUT)); | 
    
    | 123 |  |  |  | 
    
    | 124 |  |  |         /* If the semaphore did not succeed we probably have a time out.  */ | 
    
    | 125 | ✓✓ | 2620 |         if (status != UX_SUCCESS) | 
    
    | 126 |  |  |         { | 
    
    | 127 |  |  |  | 
    
    | 128 |  |  |             /* All transfers pending need to abort. There may have been a partial transfer.  */ | 
    
    | 129 |  | 6 |             _ux_host_stack_transfer_request_abort(transfer_request); | 
    
    | 130 |  |  |  | 
    
    | 131 |  |  |             /* Set the completion code.  */ | 
    
    | 132 |  | 6 |             transfer_request -> ux_transfer_request_completion_code =  UX_TRANSFER_TIMEOUT; | 
    
    | 133 |  |  |  | 
    
    | 134 |  |  |             /* Error trap.  */ | 
    
    | 135 |  | 6 |             _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_TIMEOUT); | 
    
    | 136 |  |  |  | 
    
    | 137 |  |  |             /* If trace is enabled, insert this event into the trace buffer.  */ | 
    
    | 138 |  |  |             UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_TIMEOUT, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) | 
    
    | 139 |  |  |  | 
    
    | 140 |  |  |             /* There was an error, return to the caller.  */ | 
    
    | 141 |  | 6 |             return(UX_TRANSFER_TIMEOUT); | 
    
    | 142 |  |  |         } | 
    
    | 143 |  |  |  | 
    
    | 144 |  |  |         /* Did we successfully send the CBW?  */ | 
    
    | 145 | ✓✓ | 2614 |         if (transfer_request -> ux_transfer_request_completion_code == UX_SUCCESS) | 
    
    | 146 |  |  |  | 
    
    | 147 |  |  |             /* Jump to the data stage.  */ | 
    
    | 148 |  | 2605 |             break; | 
    
    | 149 |  |  |  | 
    
    | 150 |  |  |         /* The transfer stalled. Is this the first time?  */ | 
    
    | 151 | ✓✓ | 9 |         if (retry == 0) | 
    
    | 152 |  |  |         { | 
    
    | 153 |  |  |  | 
    
    | 154 |  |  |             /* We must do a reset recovery.  */ | 
    
    | 155 |  | 8 |             _ux_host_class_storage_device_reset(storage); | 
    
    | 156 |  |  |  | 
    
    | 157 |  |  |             /* In virtually all cases, the reset should've fixed it, so just | 
    
    | 158 |  |  |                resend the command.  */ | 
    
    | 159 |  |  |         } | 
    
    | 160 |  |  |         else | 
    
    | 161 |  |  |         { | 
    
    | 162 |  |  |  | 
    
    | 163 |  |  |             /* Well, we tried!  */ | 
    
    | 164 |  | 1 |             return(transfer_request -> ux_transfer_request_completion_code); | 
    
    | 165 |  |  |         } | 
    
    | 166 |  |  |     } | 
    
    | 167 |  |  |  | 
    
    | 168 |  |  |     /* Get the length of the data payload.  */ | 
    
    | 169 |  | 2605 |     data_phase_requested_length =  _ux_utility_long_get(cbw + UX_HOST_CLASS_STORAGE_CBW_DATA_LENGTH); | 
    
    | 170 |  |  |  | 
    
    | 171 |  |  |     /* Reset the data phase memory size.  */ | 
    
    | 172 |  | 2605 |     storage -> ux_host_class_storage_data_phase_length =  0; | 
    
    | 173 |  |  |  | 
    
    | 174 |  |  |     /* Perform the data stage - if there is any.  */ | 
    
    | 175 | ✓✓ | 4546 |     while (data_phase_requested_length != 0) | 
    
    | 176 |  |  |     { | 
    
    | 177 |  |  |  | 
    
    | 178 |  |  |         /* Check if we can finish the transaction with one data phase.  */ | 
    
    | 179 | ✓✓ | 2234 |         if (data_phase_requested_length > UX_HOST_CLASS_STORAGE_MAX_TRANSFER_SIZE) | 
    
    | 180 |  |  |  | 
    
    | 181 |  |  |             /* We have too much data to send in one phase. Split into smaller chunks.  */ | 
    
    | 182 |  | 61 |             data_phase_transfer_size =  UX_HOST_CLASS_STORAGE_MAX_TRANSFER_SIZE; | 
    
    | 183 |  |  |  | 
    
    | 184 |  |  |         else | 
    
    | 185 |  |  |  | 
    
    | 186 |  |  |             /* The transfer size can be the requested length.  */ | 
    
    | 187 |  | 2173 |             data_phase_transfer_size =  data_phase_requested_length; | 
    
    | 188 |  |  |  | 
    
    | 189 |  |  |         /* Check the direction and determine which endpoint to use.  */ | 
    
    | 190 | ✓✓ | 2234 |         if (*(cbw + UX_HOST_CLASS_STORAGE_CBW_FLAGS) == UX_HOST_CLASS_STORAGE_DATA_IN) | 
    
    | 191 |  | 1822 |             transfer_request =  &storage -> ux_host_class_storage_bulk_in_endpoint -> ux_endpoint_transfer_request; | 
    
    | 192 |  |  |  | 
    
    | 193 |  |  |         /* Fill in the transfer request data payload buffer.  */ | 
    
    | 194 |  | 2234 |         transfer_request -> ux_transfer_request_data_pointer =  data_pointer; | 
    
    | 195 |  |  |  | 
    
    | 196 |  |  |         /* Store the requested length in the transfer request.  */ | 
    
    | 197 |  | 2234 |         transfer_request -> ux_transfer_request_requested_length =  data_phase_transfer_size; | 
    
    | 198 |  |  |  | 
    
    | 199 |  |  |         /* Perform data payload transfer (in or out).  */ | 
    
    | 200 |  | 2234 |         status =  _ux_host_stack_transfer_request(transfer_request); | 
    
    | 201 |  |  |  | 
    
    | 202 |  |  |         /* Check the status of the data payload.  */ | 
    
    | 203 | ✓✓ | 2234 |         if (status == UX_SUCCESS) | 
    
    | 204 |  |  |  | 
    
    | 205 |  |  |             /* Wait for the completion of the transfer request.  */ | 
    
    | 206 |  | 2233 |             status =  _ux_host_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, UX_MS_TO_TICK(UX_HOST_CLASS_STORAGE_TRANSFER_TIMEOUT)); | 
    
    | 207 |  |  |  | 
    
    | 208 |  |  |         /* If the semaphore did not succeed we may have a time out or if we had a problem during the preparation of the transaction | 
    
    | 209 |  |  |            we should abort the SCSI transaction.  */ | 
    
    | 210 | ✓✓ | 2234 |         if (status != UX_SUCCESS) | 
    
    | 211 |  |  |         { | 
    
    | 212 |  |  |  | 
    
    | 213 |  |  |             /* All transfers pending need to abort. There may have been a partial transfer.  */ | 
    
    | 214 |  | 2 |             _ux_host_stack_transfer_request_abort(transfer_request); | 
    
    | 215 |  |  |  | 
    
    | 216 |  |  |             /* Set the completion code.  */ | 
    
    | 217 |  | 2 |             transfer_request -> ux_transfer_request_completion_code =  UX_TRANSFER_TIMEOUT; | 
    
    | 218 |  |  |  | 
    
    | 219 |  |  |             /* Error trap. */ | 
    
    | 220 |  | 2 |             _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_TIMEOUT); | 
    
    | 221 |  |  |  | 
    
    | 222 |  |  |             /* If trace is enabled, insert this event into the trace buffer.  */ | 
    
    | 223 |  |  |             UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_TIMEOUT, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) | 
    
    | 224 |  |  |  | 
    
    | 225 |  |  |             /* There was an error that cannot be recovered, return to the caller.  */ | 
    
    | 226 |  | 2 |             return(UX_TRANSFER_TIMEOUT); | 
    
    | 227 |  |  |         } | 
    
    | 228 |  |  |  | 
    
    | 229 |  |  |         /* Check the transfer status. If there is a transport error, we still need to read the CSW | 
    
    | 230 |  |  |            and let the upper layer retry.  */ | 
    
    | 231 | ✓✓ | 2232 |         if (transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) | 
    
    | 232 |  |  |         { | 
    
    | 233 |  |  |  | 
    
    | 234 |  |  |             /* This is most likely a STALL. We must clear it and go straight | 
    
    | 235 |  |  |                to reading the CSW. Note this doesn't necessarily mean the transfer | 
    
    | 236 |  |  |                failed completely failed. For example, if this was a read, it | 
    
    | 237 |  |  |                could mean the device had less data to send than we requested, | 
    
    | 238 |  |  |                but we still received data.  */ | 
    
    | 239 |  |  |  | 
    
    | 240 |  |  |             /* Check the direction and determine which endpoint to reset.  */ | 
    
    | 241 | ✓✓ | 291 |             if (*(cbw + UX_HOST_CLASS_STORAGE_CBW_FLAGS) == UX_HOST_CLASS_STORAGE_DATA_IN) | 
    
    | 242 |  | 289 |                 _ux_host_stack_endpoint_reset(storage -> ux_host_class_storage_bulk_in_endpoint); | 
    
    | 243 |  |  |             else | 
    
    | 244 |  | 2 |                 _ux_host_stack_endpoint_reset(storage -> ux_host_class_storage_bulk_out_endpoint); | 
    
    | 245 |  |  |  | 
    
    | 246 |  |  |             /* We need to read the CSW now.   */ | 
    
    | 247 |  | 291 |             break; | 
    
    | 248 |  |  |         } | 
    
    | 249 |  |  |  | 
    
    | 250 |  |  |         /* Adjust the total size that was requested.  */ | 
    
    | 251 |  | 1941 |         data_phase_requested_length -=  data_phase_transfer_size; | 
    
    | 252 |  |  |  | 
    
    | 253 |  |  |         /* And the data pointer.  */ | 
    
    | 254 |  | 1941 |         data_pointer +=  data_phase_transfer_size; | 
    
    | 255 |  |  |     } | 
    
    | 256 |  |  |  | 
    
    | 257 |  |  |     /* Now perform the status phase, i.e. try to get the CSW from the device. | 
    
    | 258 |  |  |        According to the spec, if there is a failure the first time, we need to retry once | 
    
    | 259 |  |  |        before reporting an error.  */ | 
    
    | 260 |  |  |  | 
    
    | 261 |  |  |     /* Get the pointer to the transfer request, on the bulk in endpoint.  */ | 
    
    | 262 |  | 2603 |     transfer_request =  &storage -> ux_host_class_storage_bulk_in_endpoint -> ux_endpoint_transfer_request; | 
    
    | 263 |  |  |  | 
    
    | 264 |  |  |     /* Fill in the transfer_request parameters.  */ | 
    
    | 265 |  | 2603 |     transfer_request -> ux_transfer_request_data_pointer =      (UCHAR *) &storage -> ux_host_class_storage_csw; | 
    
    | 266 |  | 2603 |     transfer_request -> ux_transfer_request_requested_length =  UX_HOST_CLASS_STORAGE_CSW_LENGTH; | 
    
    | 267 |  |  |  | 
    
    | 268 |  |  |     /* Retry loop, we have 2 tries here.  */ | 
    
    | 269 | ✓✓ | 3117 |     for (retry =  0; retry < 2; retry++) | 
    
    | 270 |  |  |     { | 
    
    | 271 |  |  |  | 
    
    | 272 |  |  |         /* Get the CSW on the bulk in endpoint.  */ | 
    
    | 273 |  | 2862 |         status =  _ux_host_stack_transfer_request(transfer_request); | 
    
    | 274 | ✓✓ | 2862 |         if (status != UX_SUCCESS) | 
    
    | 275 |  | 1 |             return(status); | 
    
    | 276 |  |  |  | 
    
    | 277 |  |  |         /* Wait for the completion of the transfer request.  */ | 
    
    | 278 |  | 2861 |         status =  _ux_host_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, UX_MS_TO_TICK(UX_HOST_CLASS_STORAGE_TRANSFER_TIMEOUT)); | 
    
    | 279 |  |  |  | 
    
    | 280 |  |  |         /* Check semaphore status.  */ | 
    
    | 281 | ✓✓ | 2861 |         if (status != UX_SUCCESS) | 
    
    | 282 |  |  |         { | 
    
    | 283 |  |  |  | 
    
    | 284 |  |  |             /* All transfers pending need to abort. There may have been a partial transfer.  */ | 
    
    | 285 |  | 1 |             _ux_host_stack_transfer_request_abort(transfer_request); | 
    
    | 286 |  |  |  | 
    
    | 287 |  |  |             /* Set the completion code.  */ | 
    
    | 288 |  | 1 |             transfer_request -> ux_transfer_request_completion_code =  UX_TRANSFER_TIMEOUT; | 
    
    | 289 |  |  |  | 
    
    | 290 |  |  |             /* Error trap. */ | 
    
    | 291 |  | 1 |             _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_TIMEOUT); | 
    
    | 292 |  |  |  | 
    
    | 293 |  |  |             /* If trace is enabled, insert this event into the trace buffer.  */ | 
    
    | 294 |  |  |             UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_TIMEOUT, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) | 
    
    | 295 |  |  |  | 
    
    | 296 |  |  |             /* There was an error, return to the caller.  */ | 
    
    | 297 |  | 1 |             return(UX_TRANSFER_TIMEOUT); | 
    
    | 298 |  |  |         } | 
    
    | 299 |  |  |  | 
    
    | 300 |  |  |         /* Did the transfer succeed?  */ | 
    
    | 301 | ✓✓ | 2860 |         if (transfer_request -> ux_transfer_request_completion_code == UX_SUCCESS) | 
    
    | 302 |  |  |         { | 
    
    | 303 |  |  |  | 
    
    | 304 |  |  |             /* The PASSED and COMMAND_FAILED error codes are nearly interchangeable | 
    
    | 305 |  |  |                according to the spec, so we treat them similarly.  */ | 
    
    | 306 | ✓✓ | 2346 |             if (storage -> ux_host_class_storage_csw[UX_HOST_CLASS_STORAGE_CSW_STATUS] == UX_HOST_CLASS_STORAGE_CSW_PASSED || | 
    
    | 307 | ✓✓ | 194 |                 storage -> ux_host_class_storage_csw[UX_HOST_CLASS_STORAGE_CSW_STATUS] == UX_HOST_CLASS_STORAGE_CSW_FAILED) | 
    
    | 308 |  |  |             { | 
    
    | 309 |  |  |  | 
    
    | 310 |  |  |                 /* Was this an OUT transport?  */ | 
    
    | 311 | ✓✓ | 2343 |                 if (*(cbw + UX_HOST_CLASS_STORAGE_CBW_FLAGS) == UX_HOST_CLASS_STORAGE_DATA_OUT) | 
    
    | 312 |  |  |                 { | 
    
    | 313 |  |  |  | 
    
    | 314 |  |  |                     /* Did the command fail, or succeed with non-zero data residue?  */ | 
    
    | 315 | ✓✓✓✓ 
 | 1482 |                     if (storage -> ux_host_class_storage_csw[UX_HOST_CLASS_STORAGE_CSW_STATUS] == UX_HOST_CLASS_STORAGE_CSW_FAILED || | 
    
    | 316 |  | 669 |                         _ux_utility_long_get(storage -> ux_host_class_storage_csw + UX_HOST_CLASS_STORAGE_CSW_DATA_RESIDUE) != 0) | 
    
    | 317 |  |  |                     { | 
    
    | 318 |  |  |  | 
    
    | 319 |  |  |                         /* It's possible the bulk out endpoint is stalled. | 
    
    | 320 |  |  |                            This happens when 1) the device expects less data | 
    
    | 321 |  |  |                            than the host wants to send and 2) the endpoint | 
    
    | 322 |  |  |                            stalls after the last packet was sent (otherwise, | 
    
    | 323 |  |  |                            we'd have seen the stall during the data stage). | 
    
    | 324 |  |  |                            Query its status before clearing the stall.  */ | 
    
    | 325 |  |  |  | 
    
    | 326 |  |  |                         /* Allocate memory for Get Status response.  */ | 
    
    | 327 |  | 147 |                         get_status_response =  _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, 2); | 
    
    | 328 | ✓✓ | 147 |                         if (get_status_response == UX_NULL) | 
    
    | 329 |  | 1 |                             return(UX_MEMORY_INSUFFICIENT); | 
    
    | 330 |  |  |  | 
    
    | 331 |  |  |                         /* Get the control endpoint's transfer request.  */ | 
    
    | 332 |  | 146 |                         transfer_request =  &storage -> ux_host_class_storage_device -> ux_device_control_endpoint.ux_endpoint_transfer_request; | 
    
    | 333 |  |  |  | 
    
    | 334 |  |  |                         /* Setup transfer request for Get Status command.  */ | 
    
    | 335 |  | 146 |                         transfer_request -> ux_transfer_request_data_pointer =      get_status_response; | 
    
    | 336 |  | 146 |                         transfer_request -> ux_transfer_request_requested_length =  0x02; | 
    
    | 337 |  | 146 |                         transfer_request -> ux_transfer_request_function =          UX_GET_STATUS; | 
    
    | 338 |  | 146 |                         transfer_request -> ux_transfer_request_type =              UX_REQUEST_IN | UX_REQUEST_TYPE_STANDARD | UX_REQUEST_TARGET_ENDPOINT; | 
    
    | 339 |  | 146 |                         transfer_request -> ux_transfer_request_value =             0; | 
    
    | 340 |  | 146 |                         transfer_request -> ux_transfer_request_index =             storage -> ux_host_class_storage_bulk_out_endpoint-> ux_endpoint_descriptor.bEndpointAddress; | 
    
    | 341 |  |  |  | 
    
    | 342 |  |  |                         /* Send transfer request.  */ | 
    
    | 343 |  | 146 |                         status =  _ux_host_stack_transfer_request(transfer_request); | 
    
    | 344 |  |  |  | 
    
    | 345 |  |  |                         /* Check status of transfer.  */ | 
    
    | 346 | ✓✓ | 146 |                         if (status == UX_SUCCESS) | 
    
    | 347 |  |  |                         { | 
    
    | 348 |  |  |  | 
    
    | 349 |  |  |                             /* Check the Halt bit.  */ | 
    
    | 350 | ✓✓ | 145 |                             if (get_status_response[0] & 0x01) | 
    
    | 351 |  |  |                             { | 
    
    | 352 |  |  |  | 
    
    | 353 |  |  |                                 /* Clear the halt.  */ | 
    
    | 354 |  | 32 |                                 status =  _ux_host_stack_endpoint_reset(storage -> ux_host_class_storage_bulk_out_endpoint); | 
    
    | 355 |  |  |                             } | 
    
    | 356 |  |  |                         } | 
    
    | 357 |  |  |  | 
    
    | 358 |  |  |                         /* Free the get status memory.  */ | 
    
    | 359 |  | 146 |                         _ux_utility_memory_free(get_status_response); | 
    
    | 360 |  |  |  | 
    
    | 361 |  |  |                         /* Check result of Get Status and Clear Feature commands. */ | 
    
    | 362 | ✓✓ | 146 |                         if (status != UX_SUCCESS) | 
    
    | 363 |  | 1 |                             return(status); | 
    
    | 364 |  |  |                     } | 
    
    | 365 |  |  |                 } | 
    
    | 366 |  |  |  | 
    
    | 367 |  |  |                 /* Save the amount of relevant data.  */ | 
    
    | 368 |  | 4682 |                 storage -> ux_host_class_storage_data_phase_length =  _ux_utility_long_get(cbw + UX_HOST_CLASS_STORAGE_CBW_DATA_LENGTH) - | 
    
    | 369 |  | 2341 |                     _ux_utility_long_get(storage -> ux_host_class_storage_csw + UX_HOST_CLASS_STORAGE_CSW_DATA_RESIDUE); | 
    
    | 370 |  |  |             } | 
    
    | 371 |  |  |             else | 
    
    | 372 |  |  |             { | 
    
    | 373 |  |  |  | 
    
    | 374 |  |  |                 /* Must be phase error. Need to reset the device.  */ | 
    
    | 375 |  | 3 |                 _ux_host_class_storage_device_reset(storage); | 
    
    | 376 |  |  |             } | 
    
    | 377 |  |  |  | 
    
    | 378 |  |  |             /* Return success since the transfer succeeded. The caller should | 
    
    | 379 |  |  |                look at the CSW to determine if the command's status.  */ | 
    
    | 380 |  | 2344 |             return(UX_SUCCESS); | 
    
    | 381 |  |  |         } | 
    
    | 382 |  |  |  | 
    
    | 383 |  |  |         /* The transfer stalled. We must clear the stall and attempt to read | 
    
    | 384 |  |  |            the CSW again.  */ | 
    
    | 385 |  | 514 |         _ux_host_stack_endpoint_reset(storage -> ux_host_class_storage_bulk_in_endpoint); | 
    
    | 386 |  |  |     } | 
    
    | 387 |  |  |  | 
    
    | 388 |  |  |     /* If we get here, the CSW transfer stalled twice. We must reset the device.  */ | 
    
    | 389 |  | 255 |     _ux_host_class_storage_device_reset(storage); | 
    
    | 390 |  |  |  | 
    
    | 391 |  |  |     /* Return the error.  */ | 
    
    | 392 |  | 255 |     return(transfer_request -> ux_transfer_request_completion_code); | 
    
    | 393 |  |  | } | 
    
    | 394 |  |  | #endif |