1 /**************************************************************************/
2 /*                                                                        */
3 /*       Copyright (c) Microsoft Corporation. All rights reserved.        */
4 /*                                                                        */
5 /*       This software is licensed under the Microsoft Software License   */
6 /*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
7 /*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
8 /*       and in the root directory of this software.                      */
9 /*                                                                        */
10 /**************************************************************************/
11 
12 
13 /**************************************************************************/
14 /**************************************************************************/
15 /**                                                                       */
16 /** USBX Component                                                        */
17 /**                                                                       */
18 /**   Device Storage Class                                                */
19 /**                                                                       */
20 /**************************************************************************/
21 /**************************************************************************/
22 
23 #define UX_SOURCE_CODE
24 
25 
26 /* Include necessary system files.  */
27 
28 #include "ux_api.h"
29 #include "ux_device_class_storage.h"
30 #include "ux_device_stack.h"
31 
32 
33 #if !defined(UX_DEVICE_STANDALONE)
34 /**************************************************************************/
35 /*                                                                        */
36 /*  FUNCTION                                               RELEASE        */
37 /*                                                                        */
38 /*    _ux_device_class_storage_thread                     PORTABLE C      */
39 /*                                                           6.1.12       */
40 /*  AUTHOR                                                                */
41 /*                                                                        */
42 /*    Chaoqiong Xiao, Microsoft Corporation                               */
43 /*                                                                        */
44 /*  DESCRIPTION                                                           */
45 /*                                                                        */
46 /*    This function is the thread of the storage class.                   */
47 /*                                                                        */
48 /*    It's for RTOS mode.                                                 */
49 /*                                                                        */
50 /*  INPUT                                                                 */
51 /*                                                                        */
52 /*    class                                 Address of storage class      */
53 /*                                            container                   */
54 /*                                                                        */
55 /*  OUTPUT                                                                */
56 /*                                                                        */
57 /*    None                                                                */
58 /*                                                                        */
59 /*  CALLS                                                                 */
60 /*                                                                        */
61 /*    _ux_device_class_storage_format       Storage class format          */
62 /*    _ux_device_class_storage_inquiry      Storage class inquiry         */
63 /*    _ux_device_class_storage_mode_select  Mode select                   */
64 /*    _ux_device_class_storage_mode_sense   Mode sense                    */
65 /*    _ux_device_class_storage_prevent_allow_media_removal                */
66 /*                                          Prevent media removal         */
67 /*    _ux_device_class_storage_read         Read                          */
68 /*    _ux_device_class_storage_read_capacity                              */
69 /*                                          Read capacity                 */
70 /*    _ux_device_class_storage_read_format_capacity                       */
71 /*                                          Read format capacity          */
72 /*    _ux_device_class_storage_request_sense                              */
73 /*                                          Sense request                 */
74 /*    _ux_device_class_storage_start_stop   Start/Stop                    */
75 /*    _ux_device_class_storage_synchronize_cache                          */
76 /*                                          Synchronize cache             */
77 /*    _ux_device_class_storage_test_ready   Ready test                    */
78 /*    _ux_device_class_storage_verify       Verify                        */
79 /*    _ux_device_class_storage_write        Write                         */
80 /*    _ux_device_stack_endpoint_stall       Endpoint stall                */
81 /*    _ux_device_stack_interface_delete     Interface delete              */
82 /*    _ux_device_stack_transfer_request     Transfer request              */
83 /*    _ux_utility_long_get                  Get 32-bit value              */
84 /*    _ux_utility_memory_allocate           Allocate memory               */
85 /*    _ux_device_semaphore_create           Create semaphore              */
86 /*    _ux_utility_delay_ms                  Sleep thread for several ms   */
87 /*    _ux_device_thread_suspend             Suspend thread                */
88 /*                                                                        */
89 /*  CALLED BY                                                             */
90 /*                                                                        */
91 /*    ThreadX                                                             */
92 /*                                                                        */
93 /*  RELEASE HISTORY                                                       */
94 /*                                                                        */
95 /*    DATE              NAME                      DESCRIPTION             */
96 /*                                                                        */
97 /*  05-19-2020     Chaoqiong Xiao           Initial Version 6.0           */
98 /*  09-30-2020     Chaoqiong Xiao           Modified comment(s),          */
99 /*                                            used sleep instead of       */
100 /*                                            relinquish on error,        */
101 /*                                            optimized command logic,    */
102 /*                                            used UX prefix to refer to  */
103 /*                                            TX symbols instead of using */
104 /*                                            them directly,              */
105 /*                                            resulting in version 6.1    */
106 /*  12-31-2020     Chaoqiong Xiao           Modified comment(s),          */
107 /*                                            fixed USB CV test issues,   */
108 /*                                            resulting in version 6.1.3  */
109 /*  04-02-2021     Chaoqiong Xiao           Modified comment(s),          */
110 /*                                            fixed compile issues with   */
111 /*                                            some macro options,         */
112 /*                                            resulting in version 6.1.6  */
113 /*  06-02-2021     Chaoqiong Xiao           Modified comment(s),          */
114 /*                                            get interface and endpoints */
115 /*                                            from configured device,     */
116 /*                                            resulting in version 6.1.7  */
117 /*  10-15-2021     Chaoqiong Xiao           Modified comment(s),          */
118 /*                                            improved TAG management,    */
119 /*                                            resulting in version 6.1.9  */
120 /*  01-31-2022     Chaoqiong Xiao           Modified comment(s),          */
121 /*                                            refined macros names,       */
122 /*                                            resulting in version 6.1.10 */
123 /*  04-25-2022     Chaoqiong Xiao           Modified comment(s),          */
124 /*                                            internal clean up,          */
125 /*                                            resulting in version 6.1.11 */
126 /*  07-29-2022     Chaoqiong Xiao           Modified comment(s),          */
127 /*                                            fixed parameter/variable    */
128 /*                                            names conflict C++ keyword, */
129 /*                                            resulting in version 6.1.12 */
130 /*                                                                        */
131 /**************************************************************************/
_ux_device_class_storage_thread(ULONG storage_class)132 VOID  _ux_device_class_storage_thread(ULONG storage_class)
133 {
134 
135 UX_SLAVE_CLASS              *class_ptr;
136 UX_SLAVE_CLASS_STORAGE      *storage;
137 UX_SLAVE_TRANSFER           *transfer_request;
138 UX_SLAVE_DEVICE             *device;
139 UX_SLAVE_INTERFACE          *interface_ptr;
140 UX_SLAVE_ENDPOINT           *endpoint_in;
141 UX_SLAVE_ENDPOINT           *endpoint_out;
142 UINT                        status;
143 ULONG                       length;
144 ULONG                       cbwcb_length;
145 ULONG                       lun;
146 UCHAR                       *scsi_command;
147 UCHAR                       *cbw_cb;
148 
149 
150     /* This thread runs forever but can be suspended or resumed.  */
151     while(1)
152     {
153 
154         /* Cast properly the storage instance.  */
155         UX_THREAD_EXTENSION_PTR_GET(class_ptr, UX_SLAVE_CLASS, storage_class)
156 
157         /* Get the storage instance from this class container.  */
158         storage =  (UX_SLAVE_CLASS_STORAGE *) class_ptr -> ux_slave_class_instance;
159 
160         /* Get the pointer to the device.  */
161         device =  &_ux_system_slave -> ux_system_slave_device;
162 
163         /* As long as the device is in the CONFIGURED state.  */
164         while (device -> ux_slave_device_state == UX_DEVICE_CONFIGURED)
165         {
166 
167             /* We are activated. We need the interface to the class.  */
168             interface_ptr =  storage -> ux_slave_class_storage_interface;
169 
170             /* We assume the worst situation.  */
171             status =  UX_ERROR;
172 
173             /* Locate the endpoints.  */
174             endpoint_in =  interface_ptr -> ux_slave_interface_first_endpoint;
175 
176             /* Check the endpoint direction, if IN we have the correct endpoint.  */
177             if ((endpoint_in -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) != UX_ENDPOINT_IN)
178             {
179 
180                 /* Wrong direction, we found the OUT endpoint first.  */
181                 endpoint_out =  endpoint_in;
182 
183                 /* So the next endpoint has to be the IN endpoint.  */
184                 endpoint_in =  endpoint_out -> ux_slave_endpoint_next_endpoint;
185             }
186             else
187             {
188 
189                 /* We found the endpoint IN first, so next endpoint is OUT.  */
190                 endpoint_out =  endpoint_in -> ux_slave_endpoint_next_endpoint;
191             }
192 
193             /* All SCSI commands are on the endpoint OUT, from the host.  */
194             transfer_request =  &endpoint_out -> ux_slave_endpoint_transfer_request;
195 
196             /* Check state, they must be both RESET.  */
197             if (endpoint_out -> ux_slave_endpoint_state == UX_ENDPOINT_RESET &&
198                 (UCHAR)storage -> ux_slave_class_storage_csw_status != UX_SLAVE_CLASS_STORAGE_CSW_PHASE_ERROR)
199             {
200 
201                 /* Send the request to the device controller.  */
202                 status =  _ux_device_stack_transfer_request(transfer_request, 64, 64);
203 
204             }
205 
206             /* Check the status. Our status is UX_ERROR if one of the endpoint was STALLED. We must wait for the host
207                to clear the mess.   */
208             if (status == UX_SUCCESS)
209             {
210 
211                 /* Obtain the length of the transaction.  */
212                 length =  transfer_request -> ux_slave_transfer_request_actual_length;
213 
214                 /* Obtain the buffer address containing the SCSI command.  */
215                 scsi_command =  transfer_request -> ux_slave_transfer_request_data_pointer;
216 
217                 /* Obtain the lun from the CBW.  */
218                 lun =  (ULONG) *(scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_LUN);
219                 storage -> ux_slave_class_storage_cbw_lun = (UCHAR)lun;
220 
221                 /* We have to memorize the SCSI command tag for the CSW phase.  */
222                 storage -> ux_slave_class_storage_scsi_tag =  _ux_utility_long_get(scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_TAG);
223 
224                 /* Get dCBWDataTransferLength: number of bytes to transfer.  */
225                 storage -> ux_slave_class_storage_host_length = _ux_utility_long_get(scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_DATA_LENGTH);
226 
227                 /* Save bmCBWFlags.  */
228                 storage -> ux_slave_class_storage_cbw_flags = *(scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_FLAGS);
229 
230                 /* Reset CSW status.  */
231                 storage -> ux_slave_class_storage_csw_residue = 0;
232                 storage -> ux_slave_class_storage_csw_status = 0;
233 
234                 /* Ensure the LUN number is within our declared values and check the command
235                    content and format. First we make sure we have a complete CBW.  */
236                 if ((lun < storage -> ux_slave_class_storage_number_lun) && (length == UX_SLAVE_CLASS_STORAGE_CBW_LENGTH))
237                 {
238 
239                     /* The length of the CBW is correct, analyze the header.  */
240                     if (_ux_utility_long_get(scsi_command) == UX_SLAVE_CLASS_STORAGE_CBW_SIGNATURE_MASK)
241                     {
242 
243                         /* Get the length of the CBWCB.  */
244                         cbwcb_length =  (ULONG) *(scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_CB_LENGTH);
245 
246                         /* Check the length of the CBWCB to ensure there is at least a command.  */
247                         if (cbwcb_length != 0)
248                         {
249 
250                             /* Analyze the command stored in the CBWCB.  */
251                             cbw_cb = scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_CB;
252                             switch (*(cbw_cb))
253                             {
254 
255                             case UX_SLAVE_CLASS_STORAGE_SCSI_TEST_READY:
256 
257                                 _ux_device_class_storage_test_ready(storage, lun, endpoint_in, endpoint_out, cbw_cb);
258                                 break;
259 
260                             case UX_SLAVE_CLASS_STORAGE_SCSI_REQUEST_SENSE:
261 
262                                 _ux_device_class_storage_request_sense(storage, lun, endpoint_in, endpoint_out, cbw_cb);
263                                 break;
264 
265                             case UX_SLAVE_CLASS_STORAGE_SCSI_FORMAT:
266 
267                                 _ux_device_class_storage_format(storage, lun, endpoint_in, endpoint_out, cbw_cb);
268                                 break;
269 
270                             case UX_SLAVE_CLASS_STORAGE_SCSI_INQUIRY:
271 
272                                 _ux_device_class_storage_inquiry(storage, lun, endpoint_in, endpoint_out, cbw_cb);
273                                 break;
274 
275                             case UX_SLAVE_CLASS_STORAGE_SCSI_START_STOP:
276 
277                                 _ux_device_class_storage_start_stop(storage, lun, endpoint_in, endpoint_out, cbw_cb);
278                                 break;
279 
280                             case UX_SLAVE_CLASS_STORAGE_SCSI_PREVENT_ALLOW_MEDIA_REMOVAL:
281 
282                                 _ux_device_class_storage_prevent_allow_media_removal(storage, lun, endpoint_in, endpoint_out, cbw_cb);
283                                 break;
284 
285                             case UX_SLAVE_CLASS_STORAGE_SCSI_READ_FORMAT_CAPACITY:
286 
287                                 _ux_device_class_storage_read_format_capacity(storage, lun, endpoint_in, endpoint_out, cbw_cb);
288                                 break;
289 
290                             case UX_SLAVE_CLASS_STORAGE_SCSI_READ_CAPACITY:
291 
292                                 _ux_device_class_storage_read_capacity(storage, lun, endpoint_in, endpoint_out, cbw_cb);
293                                 break;
294 
295                             case UX_SLAVE_CLASS_STORAGE_SCSI_VERIFY:
296 
297                                 _ux_device_class_storage_verify(storage, lun, endpoint_in, endpoint_out, cbw_cb);
298                                 break;
299 
300                             case UX_SLAVE_CLASS_STORAGE_SCSI_MODE_SELECT:
301 
302                                 _ux_device_class_storage_mode_select(storage, lun, endpoint_in, endpoint_out, cbw_cb);
303                                 break;
304 
305                             case UX_SLAVE_CLASS_STORAGE_SCSI_MODE_SENSE_SHORT:
306                             case UX_SLAVE_CLASS_STORAGE_SCSI_MODE_SENSE:
307 
308                                 _ux_device_class_storage_mode_sense(storage, lun, endpoint_in, endpoint_out, cbw_cb);
309                                 break;
310 
311                             case UX_SLAVE_CLASS_STORAGE_SCSI_READ32:
312 
313                                 _ux_device_class_storage_read(storage, lun, endpoint_in, endpoint_out, cbw_cb,
314                                                                 UX_SLAVE_CLASS_STORAGE_SCSI_READ32);
315                                 break;
316 
317                             case UX_SLAVE_CLASS_STORAGE_SCSI_READ16:
318 
319                                 _ux_device_class_storage_read(storage, lun, endpoint_in, endpoint_out, cbw_cb,
320                                                                 UX_SLAVE_CLASS_STORAGE_SCSI_READ16);
321                                 break;
322 
323                             case UX_SLAVE_CLASS_STORAGE_SCSI_WRITE32:
324 
325                                 _ux_device_class_storage_write(storage, lun, endpoint_in, endpoint_out, cbw_cb,
326                                                                 UX_SLAVE_CLASS_STORAGE_SCSI_WRITE32);
327                                 break;
328 
329                             case UX_SLAVE_CLASS_STORAGE_SCSI_WRITE16:
330 
331                                 _ux_device_class_storage_write(storage, lun, endpoint_in, endpoint_out, cbw_cb,
332                                                                 UX_SLAVE_CLASS_STORAGE_SCSI_WRITE16);
333                                 break;
334 
335                             case UX_SLAVE_CLASS_STORAGE_SCSI_SYNCHRONIZE_CACHE:
336 
337                                 _ux_device_class_storage_synchronize_cache(storage, lun, endpoint_in, endpoint_out, cbw_cb, *(cbw_cb));
338                                 break;
339 
340 #ifdef UX_SLAVE_CLASS_STORAGE_INCLUDE_MMC
341                             case UX_SLAVE_CLASS_STORAGE_SCSI_GET_STATUS_NOTIFICATION:
342 
343                                 _ux_device_class_storage_get_status_notification(storage, lun, endpoint_in, endpoint_out, cbw_cb);
344                                 break;
345 
346                             case UX_SLAVE_CLASS_STORAGE_SCSI_GET_CONFIGURATION:
347 
348                                 _ux_device_class_storage_get_configuration(storage, lun, endpoint_in, endpoint_out, cbw_cb);
349                                 break;
350 
351                             case UX_SLAVE_CLASS_STORAGE_SCSI_READ_DISK_INFORMATION:
352 
353                                 _ux_device_class_storage_read_disk_information(storage, lun, endpoint_in, endpoint_out, cbw_cb);
354                                 break;
355 
356                             case UX_SLAVE_CLASS_STORAGE_SCSI_REPORT_KEY:
357 
358                                 _ux_device_class_storage_report_key(storage, lun, endpoint_in, endpoint_out, cbw_cb);
359                                 break;
360 
361                             case UX_SLAVE_CLASS_STORAGE_SCSI_GET_PERFORMANCE:
362 
363                                 _ux_device_class_storage_get_performance(storage, lun, endpoint_in, endpoint_out, cbw_cb);
364                                 break;
365 
366                             case UX_SLAVE_CLASS_STORAGE_SCSI_READ_DVD_STRUCTURE:
367 
368                                 _ux_device_class_storage_read_dvd_structure(storage, lun, endpoint_in, endpoint_out, cbw_cb);
369                                 break;
370 
371                             case UX_SLAVE_CLASS_STORAGE_SCSI_READ_TOC:
372 
373                                 status = _ux_device_class_storage_read_toc(storage, lun, endpoint_in, endpoint_out, cbw_cb);
374 
375                                 /* Special treatment of TOC command. If error, default to Stall endpoint.  */
376                                 if (status == UX_SUCCESS)
377                                     break;
378 #endif
379 
380                             /* fall through */
381                             default:
382 
383                                 /* The command is unknown or unsupported, so we stall the endpoint.  */
384 
385                                 if (storage -> ux_slave_class_storage_host_length > 0 &&
386                                     ((storage -> ux_slave_class_storage_cbw_flags & 0x80) == 0))
387 
388                                     /* Data-Out from host to device, stall OUT.  */
389                                     _ux_device_stack_endpoint_stall(endpoint_out);
390                                 else
391 
392                                     /* Data-In from device to host, stall IN.  */
393                                     _ux_device_stack_endpoint_stall(endpoint_in);
394 
395                                 /* Initialize the request sense keys.  */
396                                 storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_sense_status =
397                                     UX_DEVICE_CLASS_STORAGE_SENSE_STATUS(UX_SLAVE_CLASS_STORAGE_SENSE_KEY_ILLEGAL_REQUEST,
398                                                                          UX_SLAVE_CLASS_STORAGE_ASC_KEY_INVALID_COMMAND,0);
399 
400                                 /* This is the tricky part of the SCSI state machine. We must send the CSW BUT need to wait
401                                    for the endpoint_in to be reset by the host.  */
402                                 while (device -> ux_slave_device_state == UX_DEVICE_CONFIGURED)
403                                 {
404 
405                                     /* Check the endpoint state.  */
406                                     if (endpoint_in -> ux_slave_endpoint_state == UX_ENDPOINT_RESET)
407                                     {
408 
409                                         /* Now we set the CSW with failure.  */
410                                         storage -> ux_slave_class_storage_csw_status = UX_SLAVE_CLASS_STORAGE_CSW_FAILED;
411                                         break;
412                                     }
413 
414                                     else
415 
416                                         /* We must therefore wait a while.  */
417                                         _ux_device_thread_relinquish();
418                                 }
419                                 break;
420                             }
421 
422                             /* Send CSW if not SYNC_CACHE.  */
423                             status = _ux_device_class_storage_csw_send(storage, lun, endpoint_in, 0 /* Don't care */);
424 
425                             /* Check error code. */
426                             if (status != UX_SUCCESS)
427 
428                                 /* Error trap. */
429                                 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, status);
430                         }
431                         else
432 
433                             /* Phase error!  */
434                             storage -> ux_slave_class_storage_csw_status = UX_SLAVE_CLASS_STORAGE_CSW_PHASE_ERROR;
435                     }
436 
437                     else
438 
439                         /* Phase error!  */
440                         storage -> ux_slave_class_storage_csw_status = UX_SLAVE_CLASS_STORAGE_CSW_PHASE_ERROR;
441                 }
442                 else
443 
444                     /* Phase error!  */
445                     storage -> ux_slave_class_storage_csw_status = UX_SLAVE_CLASS_STORAGE_CSW_PHASE_ERROR;
446             }
447             else
448             {
449 
450                 if ((UCHAR)storage -> ux_slave_class_storage_csw_status == UX_SLAVE_CLASS_STORAGE_CSW_PHASE_ERROR)
451                 {
452 
453                     /* We should keep the endpoints stalled.  */
454                     _ux_device_stack_endpoint_stall(endpoint_out);
455                     _ux_device_stack_endpoint_stall(endpoint_in);
456                 }
457 
458                 /* We must therefore wait a while.  */
459                 _ux_utility_delay_ms(2);
460             }
461         }
462 
463         /* We need to suspend ourselves. We will be resumed by the
464            device enumeration module.  */
465         _ux_device_thread_suspend(&class_ptr -> ux_slave_class_thread);
466     }
467 }
468 #endif
469