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
23 /* Include necessary system files. */
24
25 #define UX_SOURCE_CODE
26
27 #include "ux_api.h"
28 #include "ux_device_class_storage.h"
29 #include "ux_device_stack.h"
30
31
32 /**************************************************************************/
33 /* */
34 /* FUNCTION RELEASE */
35 /* */
36 /* _ux_device_class_storage_read PORTABLE C */
37 /* 6.1.10 */
38 /* AUTHOR */
39 /* */
40 /* Chaoqiong Xiao, Microsoft Corporation */
41 /* */
42 /* DESCRIPTION */
43 /* */
44 /* This function performs a READ command in 32 or 16 bits. */
45 /* */
46 /* INPUT */
47 /* */
48 /* storage Pointer to storage class */
49 /* endpoint_in Pointer to IN endpoint */
50 /* endpoint_out Pointer to OUT endpoint */
51 /* cbwcb Pointer to CBWCB */
52 /* scsi_command SCSI command */
53 /* */
54 /* OUTPUT */
55 /* */
56 /* Completion Status */
57 /* */
58 /* CALLS */
59 /* */
60 /* (ux_slave_class_storage_media_read) Read from media */
61 /* (ux_slave_class_storage_media_status) Get media status */
62 /* _ux_device_stack_endpoint_stall Stall endpoint */
63 /* _ux_device_stack_transfer_request Transfer request */
64 /* _ux_utility_long_get_big_endian Get 32-bit big endian */
65 /* _ux_utility_short_get_big_endian Get 16-bit big endian */
66 /* _ux_device_class_storage_csw_send Send CSW */
67 /* */
68 /* CALLED BY */
69 /* */
70 /* Device Storage Class */
71 /* */
72 /* RELEASE HISTORY */
73 /* */
74 /* DATE NAME DESCRIPTION */
75 /* */
76 /* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */
77 /* 09-30-2020 Chaoqiong Xiao Modified comment(s), */
78 /* optimized command logic, */
79 /* resulting in version 6.1 */
80 /* 12-31-2020 Chaoqiong Xiao Modified comment(s), */
81 /* fixed USB CV test issues, */
82 /* resulting in version 6.1.3 */
83 /* 01-31-2022 Chaoqiong Xiao Modified comment(s), */
84 /* added standalone support, */
85 /* resulting in version 6.1.10 */
86 /* */
87 /**************************************************************************/
_ux_device_class_storage_read(UX_SLAVE_CLASS_STORAGE * storage,ULONG lun,UX_SLAVE_ENDPOINT * endpoint_in,UX_SLAVE_ENDPOINT * endpoint_out,UCHAR * cbwcb,UCHAR scsi_command)88 UINT _ux_device_class_storage_read(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun,
89 UX_SLAVE_ENDPOINT *endpoint_in,
90 UX_SLAVE_ENDPOINT *endpoint_out, UCHAR * cbwcb, UCHAR scsi_command)
91 {
92
93 UINT status;
94 ULONG lba;
95 UX_SLAVE_TRANSFER *transfer_request;
96 ULONG total_number_blocks;
97 ULONG media_status;
98 ULONG total_length;
99
100 #if !defined(UX_DEVICE_STANDALONE)
101 ULONG number_blocks;
102 ULONG transfer_length;
103 ULONG done_length;
104 #endif
105
106
107 UX_PARAMETER_NOT_USED(endpoint_out);
108
109 /* Get the LBA from the CBWCB. */
110 lba = _ux_utility_long_get_big_endian(cbwcb + UX_SLAVE_CLASS_STORAGE_READ_LBA);
111
112 /* The type of commands will tell us the width of the field containing the number
113 of sectors to read. */
114 if (scsi_command == UX_SLAVE_CLASS_STORAGE_SCSI_READ16)
115
116 /* Get the number of blocks from the CBWCB in 16 bits. */
117 total_number_blocks = _ux_utility_short_get_big_endian(cbwcb + UX_SLAVE_CLASS_STORAGE_READ_TRANSFER_LENGTH_16);
118
119 else
120
121 /* Get the number of blocks from the CBWCB in 32 bits. */
122 total_number_blocks = _ux_utility_long_get_big_endian(cbwcb + UX_SLAVE_CLASS_STORAGE_READ_TRANSFER_LENGTH_32);
123
124 /* Obtain the pointer to the transfer request. */
125 transfer_request = &endpoint_in -> ux_slave_endpoint_transfer_request;
126
127 /* Compute the total length to transfer and how much remains. */
128 total_length = total_number_blocks * storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_block_length;
129
130 /* Default CSW to failed. */
131 storage -> ux_slave_class_storage_csw_status = UX_SLAVE_CLASS_STORAGE_CSW_FAILED;
132
133 #if defined(UX_DEVICE_STANDALONE)
134
135 /* Obtain the status of the device. */
136 status = storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_status(storage, lun,
137 storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_id, &media_status);
138
139 /* Update the request sense. */
140 storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_sense_status = media_status;
141
142 /* Update the request to use. */
143 storage -> ux_device_class_storage_transfer = transfer_request;
144
145 /* If there is a problem, return a failed command. */
146 if (status != UX_SUCCESS)
147 {
148
149 /* Update residue. */
150 storage -> ux_slave_class_storage_csw_residue = storage -> ux_slave_class_storage_host_length;
151
152 /* Return an error. */
153 return(UX_ERROR);
154 }
155
156 /* Next: Disk read -> Transfer (DATA). */
157 storage -> ux_device_class_storage_state = UX_DEVICE_CLASS_STORAGE_STATE_DISK_WAIT;
158 storage -> ux_device_class_storage_cmd_state = UX_DEVICE_CLASS_STORAGE_CMD_READ;
159 storage -> ux_device_class_storage_disk_state = UX_DEVICE_CLASS_STORAGE_DISK_OP_START;
160 storage -> ux_device_class_storage_buffer_state[0] = UX_DEVICE_CLASS_STORAGE_BUFFER_EMPTY;
161 storage -> ux_device_class_storage_buffer_state[1] = UX_DEVICE_CLASS_STORAGE_BUFFER_EMPTY;
162 storage -> ux_device_class_storage_buffer_usb = 1;
163 storage -> ux_device_class_storage_buffer_disk = 1;
164
165 storage -> ux_device_class_storage_device_length = total_length;
166 storage -> ux_device_class_storage_data_length =
167 UX_MIN(total_length , storage -> ux_slave_class_storage_host_length);
168 storage -> ux_device_class_storage_data_count = 0;
169
170 storage -> ux_device_class_storage_cmd_lba = lba;
171 storage -> ux_device_class_storage_cmd_n_lb = total_number_blocks;
172
173 #else
174
175 /* Check transfer length. */
176
177 /* Case (7). Host length < device length. */
178 if (total_length > storage -> ux_slave_class_storage_host_length)
179 {
180 _ux_device_stack_endpoint_stall(endpoint_in);
181 storage -> ux_slave_class_storage_csw_status = UX_SLAVE_CLASS_STORAGE_CSW_PHASE_ERROR;
182 return(UX_ERROR);
183 }
184
185 /* Case (8). Hi <> Do. */
186 if ((storage -> ux_slave_class_storage_cbw_flags & 0x80) == 0)
187 {
188 _ux_device_stack_endpoint_stall(endpoint_out);
189 storage -> ux_slave_class_storage_csw_status = UX_SLAVE_CLASS_STORAGE_CSW_PHASE_ERROR;
190 return(UX_ERROR);
191 }
192
193 /* It may take several transfers to send the requested data. */
194 done_length = 0;
195 while (total_number_blocks)
196 {
197
198 /* Obtain the status of the device. */
199 status = storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_status(storage, lun,
200 storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_id, &media_status);
201
202 /* Update the request sense. */
203 storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_sense_status = media_status;
204
205 /* If there is a problem, return a failed command. */
206 if (status != UX_SUCCESS)
207 {
208
209 /* We have a problem, media status error. Return a bad completion and wait for the
210 REQUEST_SENSE command. */
211 _ux_device_stack_endpoint_stall(endpoint_in);
212
213 /* Update residue. */
214 storage -> ux_slave_class_storage_csw_residue = storage -> ux_slave_class_storage_host_length - done_length;
215
216 /* Return an error. */
217 return(UX_ERROR);
218 }
219
220 /* How much can we send in this transfer? */
221 if (total_length > UX_SLAVE_CLASS_STORAGE_BUFFER_SIZE)
222
223 /* Compute the transfer length based on the maximum allowed. */
224 transfer_length = UX_SLAVE_CLASS_STORAGE_BUFFER_SIZE;
225
226 else
227
228 /* Compute the transfer length based on what is left to transfer. */
229 transfer_length = total_length;
230
231 /* Compute the number of blocks to transfer. */
232 number_blocks = transfer_length / storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_block_length;
233
234 /* If trace is enabled, insert this event into the trace buffer. */
235 UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_STORAGE_READ, storage, lun, transfer_request -> ux_slave_transfer_request_data_pointer,
236 number_blocks, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0)
237
238 /* Execute the read command from the local media. */
239 status = storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_read(storage, lun,
240 transfer_request -> ux_slave_transfer_request_data_pointer, number_blocks, lba, &media_status);
241
242 /* If there is a problem, return a failed command. */
243 if (status != UX_SUCCESS)
244 {
245
246 /* We have a problem, request error. Return a bad completion and wait for the
247 REQUEST_SENSE command. */
248 _ux_device_stack_endpoint_stall(endpoint_in);
249
250 /* Update residue. */
251 storage -> ux_slave_class_storage_csw_residue = storage -> ux_slave_class_storage_host_length - done_length;
252
253 /* And update the REQUEST_SENSE codes. */
254 storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_sense_status = media_status;
255
256 /* Return an error. */
257 return(UX_ERROR);
258 }
259
260 /* Sends the data payload back to the caller. */
261 status = _ux_device_stack_transfer_request(transfer_request, transfer_length, transfer_length);
262
263 /* Check the status. */
264 if(status != UX_SUCCESS)
265 {
266
267 /* We have a problem, request error. Return a bad completion and wait for the
268 REQUEST_SENSE command. */
269 _ux_device_stack_endpoint_stall(endpoint_in);
270
271 /* Update residue. */
272 storage -> ux_slave_class_storage_csw_residue = storage -> ux_slave_class_storage_host_length - done_length;
273
274 /* Update the REQUEST_SENSE codes. */
275 storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_sense_status =
276 UX_DEVICE_CLASS_STORAGE_SENSE_STATUS(0x02,0x54,0x00);
277
278 /* Return an error. */
279 return(UX_ERROR);
280
281 }
282
283 /* Update the LBA address. */
284 lba += number_blocks;
285
286 /* Update the length to remain. */
287 total_length -= transfer_length;
288 done_length += transfer_length;
289
290 /* Update the number of blocks to read. */
291 total_number_blocks -= number_blocks;
292 }
293
294 /* Case (4), (5). Host length too large. */
295 if (storage -> ux_slave_class_storage_host_length > done_length)
296 {
297
298 /* Stall Bulk-In. */
299 _ux_device_stack_endpoint_stall(endpoint_in);
300
301 /* Update residure. */
302 storage -> ux_slave_class_storage_csw_residue = storage -> ux_slave_class_storage_host_length - done_length;
303 }
304
305 #endif /* else defined(UX_DEVICE_STANDALONE) */
306
307 /* Now we set the CSW with success. */
308 storage -> ux_slave_class_storage_csw_status = UX_SLAVE_CLASS_STORAGE_CSW_PASSED;
309
310 /* Return completion status. */
311 return(UX_SUCCESS);
312 }
313