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: 2024-12-12 17:16:36 Branches: 48 48 100.0 %

Line Branch Exec Source
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