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