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