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