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