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