GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usbx_host_classes/src/ux_host_class_storage_transport_bo.c Lines: 88 88 100.0 %
Date: 2026-03-06 18:57:10 Branches: 48 48 100.0 %

Line Branch Exec Source
1
/***************************************************************************
2
 * Copyright (c) 2024 Microsoft Corporation
3
 * Copyright (c) 2026-present Eclipse ThreadX contributors
4
 *
5
 * This program and the accompanying materials are made available under the
6
 * terms of the MIT License which is available at
7
 * https://opensource.org/licenses/MIT.
8
 *
9
 * SPDX-License-Identifier: MIT
10
 **************************************************************************/
11
12
13
/**************************************************************************/
14
/**************************************************************************/
15
/**                                                                       */
16
/** USBX Component                                                        */
17
/**                                                                       */
18
/**   Storage Class                                                       */
19
/**                                                                       */
20
/**************************************************************************/
21
/**************************************************************************/
22
23
24
/* Include necessary system files.  */
25
26
#define UX_SOURCE_CODE
27
28
#include "ux_api.h"
29
#include "ux_host_class_storage.h"
30
#include "ux_host_stack.h"
31
32
33
#if !defined(UX_HOST_STANDALONE)
34
/**************************************************************************/
35
/*                                                                        */
36
/*  FUNCTION                                               RELEASE        */
37
/*                                                                        */
38
/*    _ux_host_class_storage_transport_bo                 PORTABLE C      */
39
/*                                                           6.1.10       */
40
/*  AUTHOR                                                                */
41
/*                                                                        */
42
/*    Chaoqiong Xiao, Microsoft Corporation                               */
43
/*                                                                        */
44
/*  DESCRIPTION                                                           */
45
/*                                                                        */
46
/*    This function is the transport layer for the Bulk Only protocol.    */
47
/*                                                                        */
48
/*  INPUT                                                                 */
49
/*                                                                        */
50
/*    storage                               Pointer to storage class      */
51
/*    data_pointer                          Pointer to data               */
52
/*                                                                        */
53
/*  OUTPUT                                                                */
54
/*                                                                        */
55
/*    The transfer completion status. It's possible for the transfer to   */
56
/*    succeed but for the command to fail. The CSW must be checked to     */
57
/*    determine command status.                                           */
58
/*                                                                        */
59
/*  CALLS                                                                 */
60
/*                                                                        */
61
/*    _ux_host_class_storage_device_reset   Reset mass storage            */
62
/*    _ux_host_stack_endpoint_reset         Reset endpoint                */
63
/*    _ux_host_stack_transfer_request       Process host stack transfer   */
64
/*    _ux_host_stack_transfer_request_abort Abort transfer request        */
65
/*    _ux_utility_memory_allocate           Allocate memory               */
66
/*    _ux_utility_memory_free               Free memory                   */
67
/*    _ux_utility_long_get                  Get 32-bit word               */
68
/*    _ux_host_semaphore_get                Get semaphore                 */
69
/*                                                                        */
70
/*  CALLED BY                                                             */
71
/*                                                                        */
72
/*    Storage Class                                                       */
73
/*                                                                        */
74
/**************************************************************************/
75
2632
UINT  _ux_host_class_storage_transport_bo(UX_HOST_CLASS_STORAGE *storage, UCHAR *data_pointer)
76
{
77
78
UX_TRANSFER     *transfer_request;
79
UINT            status;
80
UCHAR           *cbw;
81
UINT            retry;
82
ULONG           data_phase_requested_length;
83
ULONG           data_phase_transfer_size;
84
UCHAR           *get_status_response;
85
86
87
    /* Get the pointer to the transfer request.  */
88
2632
    transfer_request =  &storage -> ux_host_class_storage_bulk_out_endpoint -> ux_endpoint_transfer_request;
89
90
    /* Use a pointer for the cbw, easier to manipulate.  */
91
2632
    cbw =  (UCHAR *) storage -> ux_host_class_storage_cbw;
92
93
    /* Fill in the transfer request parameters.  */
94
2632
    transfer_request -> ux_transfer_request_data_pointer =      cbw;
95
2632
    transfer_request -> ux_transfer_request_requested_length =  UX_HOST_CLASS_STORAGE_CBW_LENGTH;
96
97
    /* It's possible for the bulk out endpoint to stall; in that case, we clear
98
       it and try again.  */
99
2632
    for (retry =  0; ; retry++)
100
    {
101
102
        /* Send the CBW on the bulk out endpoint.  */
103
2640
        status =  _ux_host_stack_transfer_request(transfer_request);
104
105
        /* Check the status of the command (NB, the command is not sent yet since
106
           the bulk transport is non blocking).  */
107
2640
        if (status != UX_SUCCESS)
108
12
            return(status);
109
110
        /* Wait for the completion of the transfer request.  */
111
2628
        status =  _ux_host_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, UX_MS_TO_TICK(UX_HOST_CLASS_STORAGE_TRANSFER_TIMEOUT));
112
113
        /* If the semaphore did not succeed we probably have a time out.  */
114
2628
        if (status != UX_SUCCESS)
115
        {
116
117
            /* All transfers pending need to abort. There may have been a partial transfer.  */
118
6
            _ux_host_stack_transfer_request_abort(transfer_request);
119
120
            /* Set the completion code.  */
121
6
            transfer_request -> ux_transfer_request_completion_code =  UX_TRANSFER_TIMEOUT;
122
123
            /* Error trap.  */
124
6
            _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_TIMEOUT);
125
126
            /* If trace is enabled, insert this event into the trace buffer.  */
127
            UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_TIMEOUT, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0)
128
129
            /* There was an error, return to the caller.  */
130
6
            return(UX_TRANSFER_TIMEOUT);
131
        }
132
133
        /* Did we successfully send the CBW?  */
134
2622
        if (transfer_request -> ux_transfer_request_completion_code == UX_SUCCESS)
135
136
            /* Jump to the data stage.  */
137
2613
            break;
138
139
        /* The transfer stalled. Is this the first time?  */
140
9
        if (retry == 0)
141
        {
142
143
            /* We must do a reset recovery.  */
144
8
            _ux_host_class_storage_device_reset(storage);
145
146
            /* In virtually all cases, the reset should've fixed it, so just
147
               resend the command.  */
148
        }
149
        else
150
        {
151
152
            /* Well, we tried!  */
153
1
            return(transfer_request -> ux_transfer_request_completion_code);
154
        }
155
    }
156
157
    /* Get the length of the data payload.  */
158
2613
    data_phase_requested_length =  _ux_utility_long_get(cbw + UX_HOST_CLASS_STORAGE_CBW_DATA_LENGTH);
159
160
    /* Reset the data phase memory size.  */
161
2613
    storage -> ux_host_class_storage_data_phase_length =  0;
162
163
    /* Perform the data stage - if there is any.  */
164
4561
    while (data_phase_requested_length != 0)
165
    {
166
167
        /* Check if we can finish the transaction with one data phase.  */
168
2241
        if (data_phase_requested_length > UX_HOST_CLASS_STORAGE_MAX_TRANSFER_SIZE)
169
170
            /* We have too much data to send in one phase. Split into smaller chunks.  */
171
61
            data_phase_transfer_size =  UX_HOST_CLASS_STORAGE_MAX_TRANSFER_SIZE;
172
173
        else
174
175
            /* The transfer size can be the requested length.  */
176
2180
            data_phase_transfer_size =  data_phase_requested_length;
177
178
        /* Check the direction and determine which endpoint to use.  */
179
2241
        if (*(cbw + UX_HOST_CLASS_STORAGE_CBW_FLAGS) == UX_HOST_CLASS_STORAGE_DATA_IN)
180
1829
            transfer_request =  &storage -> ux_host_class_storage_bulk_in_endpoint -> ux_endpoint_transfer_request;
181
182
        /* Fill in the transfer request data payload buffer.  */
183
2241
        transfer_request -> ux_transfer_request_data_pointer =  data_pointer;
184
185
        /* Store the requested length in the transfer request.  */
186
2241
        transfer_request -> ux_transfer_request_requested_length =  data_phase_transfer_size;
187
188
        /* Perform data payload transfer (in or out).  */
189
2241
        status =  _ux_host_stack_transfer_request(transfer_request);
190
191
        /* Check the status of the data payload.  */
192
2241
        if (status == UX_SUCCESS)
193
194
            /* Wait for the completion of the transfer request.  */
195
2240
            status =  _ux_host_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, UX_MS_TO_TICK(UX_HOST_CLASS_STORAGE_TRANSFER_TIMEOUT));
196
197
        /* If the semaphore did not succeed we may have a time out or if we had a problem during the preparation of the transaction
198
           we should abort the SCSI transaction.  */
199
2241
        if (status != UX_SUCCESS)
200
        {
201
202
            /* All transfers pending need to abort. There may have been a partial transfer.  */
203
2
            _ux_host_stack_transfer_request_abort(transfer_request);
204
205
            /* Set the completion code.  */
206
2
            transfer_request -> ux_transfer_request_completion_code =  UX_TRANSFER_TIMEOUT;
207
208
            /* Error trap. */
209
2
            _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_TIMEOUT);
210
211
            /* If trace is enabled, insert this event into the trace buffer.  */
212
            UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_TIMEOUT, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0)
213
214
            /* There was an error that cannot be recovered, return to the caller.  */
215
2
            return(UX_TRANSFER_TIMEOUT);
216
        }
217
218
        /* Check the transfer status. If there is a transport error, we still need to read the CSW
219
           and let the upper layer retry.  */
220
2239
        if (transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS)
221
        {
222
223
            /* This is most likely a STALL. We must clear it and go straight
224
               to reading the CSW. Note this doesn't necessarily mean the transfer
225
               failed completely failed. For example, if this was a read, it
226
               could mean the device had less data to send than we requested,
227
               but we still received data.  */
228
229
            /* Check the direction and determine which endpoint to reset.  */
230
291
            if (*(cbw + UX_HOST_CLASS_STORAGE_CBW_FLAGS) == UX_HOST_CLASS_STORAGE_DATA_IN)
231
289
                _ux_host_stack_endpoint_reset(storage -> ux_host_class_storage_bulk_in_endpoint);
232
            else
233
2
                _ux_host_stack_endpoint_reset(storage -> ux_host_class_storage_bulk_out_endpoint);
234
235
            /* We need to read the CSW now.   */
236
291
            break;
237
        }
238
239
        /* Adjust the total size that was requested.  */
240
1948
        data_phase_requested_length -=  data_phase_transfer_size;
241
242
        /* And the data pointer.  */
243
1948
        data_pointer +=  data_phase_transfer_size;
244
    }
245
246
    /* Now perform the status phase, i.e. try to get the CSW from the device.
247
       According to the spec, if there is a failure the first time, we need to retry once
248
       before reporting an error.  */
249
250
    /* Get the pointer to the transfer request, on the bulk in endpoint.  */
251
2611
    transfer_request =  &storage -> ux_host_class_storage_bulk_in_endpoint -> ux_endpoint_transfer_request;
252
253
    /* Fill in the transfer_request parameters.  */
254
2611
    transfer_request -> ux_transfer_request_data_pointer =      (UCHAR *) &storage -> ux_host_class_storage_csw;
255
2611
    transfer_request -> ux_transfer_request_requested_length =  UX_HOST_CLASS_STORAGE_CSW_LENGTH;
256
257
    /* Retry loop, we have 2 tries here.  */
258
3125
    for (retry =  0; retry < 2; retry++)
259
    {
260
261
        /* Get the CSW on the bulk in endpoint.  */
262
2870
        status =  _ux_host_stack_transfer_request(transfer_request);
263
2870
        if (status != UX_SUCCESS)
264
1
            return(status);
265
266
        /* Wait for the completion of the transfer request.  */
267
2869
        status =  _ux_host_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, UX_MS_TO_TICK(UX_HOST_CLASS_STORAGE_TRANSFER_TIMEOUT));
268
269
        /* Check semaphore status.  */
270
2869
        if (status != UX_SUCCESS)
271
        {
272
273
            /* All transfers pending need to abort. There may have been a partial transfer.  */
274
1
            _ux_host_stack_transfer_request_abort(transfer_request);
275
276
            /* Set the completion code.  */
277
1
            transfer_request -> ux_transfer_request_completion_code =  UX_TRANSFER_TIMEOUT;
278
279
            /* Error trap. */
280
1
            _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_TIMEOUT);
281
282
            /* If trace is enabled, insert this event into the trace buffer.  */
283
            UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_TIMEOUT, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0)
284
285
            /* There was an error, return to the caller.  */
286
1
            return(UX_TRANSFER_TIMEOUT);
287
        }
288
289
        /* Did the transfer succeed?  */
290
2868
        if (transfer_request -> ux_transfer_request_completion_code == UX_SUCCESS)
291
        {
292
293
            /* The PASSED and COMMAND_FAILED error codes are nearly interchangeable
294
               according to the spec, so we treat them similarly.  */
295
2354
            if (storage -> ux_host_class_storage_csw[UX_HOST_CLASS_STORAGE_CSW_STATUS] == UX_HOST_CLASS_STORAGE_CSW_PASSED ||
296
194
                storage -> ux_host_class_storage_csw[UX_HOST_CLASS_STORAGE_CSW_STATUS] == UX_HOST_CLASS_STORAGE_CSW_FAILED)
297
            {
298
299
                /* Was this an OUT transport?  */
300
2351
                if (*(cbw + UX_HOST_CLASS_STORAGE_CBW_FLAGS) == UX_HOST_CLASS_STORAGE_DATA_OUT)
301
                {
302
303
                    /* Did the command fail, or succeed with non-zero data residue?  */
304

1484
                    if (storage -> ux_host_class_storage_csw[UX_HOST_CLASS_STORAGE_CSW_STATUS] == UX_HOST_CLASS_STORAGE_CSW_FAILED ||
305
670
                        _ux_utility_long_get(storage -> ux_host_class_storage_csw + UX_HOST_CLASS_STORAGE_CSW_DATA_RESIDUE) != 0)
306
                    {
307
308
                        /* It's possible the bulk out endpoint is stalled.
309
                           This happens when 1) the device expects less data
310
                           than the host wants to send and 2) the endpoint
311
                           stalls after the last packet was sent (otherwise,
312
                           we'd have seen the stall during the data stage).
313
                           Query its status before clearing the stall.  */
314
315
                        /* Allocate memory for Get Status response.  */
316
147
                        get_status_response =  _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, 2);
317
147
                        if (get_status_response == UX_NULL)
318
1
                            return(UX_MEMORY_INSUFFICIENT);
319
320
                        /* Get the control endpoint's transfer request.  */
321
146
                        transfer_request =  &storage -> ux_host_class_storage_device -> ux_device_control_endpoint.ux_endpoint_transfer_request;
322
323
                        /* Setup transfer request for Get Status command.  */
324
146
                        transfer_request -> ux_transfer_request_data_pointer =      get_status_response;
325
146
                        transfer_request -> ux_transfer_request_requested_length =  0x02;
326
146
                        transfer_request -> ux_transfer_request_function =          UX_GET_STATUS;
327
146
                        transfer_request -> ux_transfer_request_type =              UX_REQUEST_IN | UX_REQUEST_TYPE_STANDARD | UX_REQUEST_TARGET_ENDPOINT;
328
146
                        transfer_request -> ux_transfer_request_value =             0;
329
146
                        transfer_request -> ux_transfer_request_index =             storage -> ux_host_class_storage_bulk_out_endpoint-> ux_endpoint_descriptor.bEndpointAddress;
330
331
                        /* Send transfer request.  */
332
146
                        status =  _ux_host_stack_transfer_request(transfer_request);
333
334
                        /* Check status of transfer.  */
335
146
                        if (status == UX_SUCCESS)
336
                        {
337
338
                            /* Check the Halt bit.  */
339
145
                            if (get_status_response[0] & 0x01)
340
                            {
341
342
                                /* Clear the halt.  */
343
32
                                status =  _ux_host_stack_endpoint_reset(storage -> ux_host_class_storage_bulk_out_endpoint);
344
                            }
345
                        }
346
347
                        /* Free the get status memory.  */
348
146
                        _ux_utility_memory_free(get_status_response);
349
350
                        /* Check result of Get Status and Clear Feature commands. */
351
146
                        if (status != UX_SUCCESS)
352
1
                            return(status);
353
                    }
354
                }
355
356
                /* Save the amount of relevant data.  */
357
4698
                storage -> ux_host_class_storage_data_phase_length =  _ux_utility_long_get(cbw + UX_HOST_CLASS_STORAGE_CBW_DATA_LENGTH) -
358
2349
                    _ux_utility_long_get(storage -> ux_host_class_storage_csw + UX_HOST_CLASS_STORAGE_CSW_DATA_RESIDUE);
359
            }
360
            else
361
            {
362
363
                /* Must be phase error. Need to reset the device.  */
364
3
                _ux_host_class_storage_device_reset(storage);
365
            }
366
367
            /* Return success since the transfer succeeded. The caller should
368
               look at the CSW to determine if the command's status.  */
369
2352
            return(UX_SUCCESS);
370
        }
371
372
        /* The transfer stalled. We must clear the stall and attempt to read
373
           the CSW again.  */
374
514
        _ux_host_stack_endpoint_reset(storage -> ux_host_class_storage_bulk_in_endpoint);
375
    }
376
377
    /* If we get here, the CSW transfer stalled twice. We must reset the device.  */
378
255
    _ux_host_class_storage_device_reset(storage);
379
380
    /* Return the error.  */
381
255
    return(transfer_request -> ux_transfer_request_completion_code);
382
}
383
#endif