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 Pima 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_pima.h"
29 #include "ux_device_stack.h"
30 
31 
32 /**************************************************************************/
33 /*                                                                        */
34 /*  FUNCTION                                               RELEASE        */
35 /*                                                                        */
36 /*    _ux_device_class_pima_partial_object_data_get       PORTABLE C      */
37 /*                                                           6.1.10       */
38 /*  AUTHOR                                                                */
39 /*                                                                        */
40 /*    Chaoqiong Xiao, Microsoft Corporation                               */
41 /*                                                                        */
42 /*  DESCRIPTION                                                           */
43 /*                                                                        */
44 /*    This function returns the partial object data to the host.          */
45 /*                                                                        */
46 /*  INPUT                                                                 */
47 /*                                                                        */
48 /*    pima                                  Pointer to pima class         */
49 /*                                                                        */
50 /*  OUTPUT                                                                */
51 /*                                                                        */
52 /*    Completion Status                                                   */
53 /*                                                                        */
54 /*  CALLS                                                                 */
55 /*                                                                        */
56 /*    _ux_device_stack_transfer_request     Transfer request              */
57 /*    _ux_device_stack_endpoint_stall       Stall endpoint                */
58 /*    _ux_utility_long_put                  Put 32-bit value              */
59 /*    _ux_utility_short_put                 Put 32-bit value              */
60 /*    _ux_device_class_pima_response_send   Send PIMA response            */
61 /*                                                                        */
62 /*  CALLED BY                                                             */
63 /*                                                                        */
64 /*    Device Storage Class                                                */
65 /*                                                                        */
66 /*  RELEASE HISTORY                                                       */
67 /*                                                                        */
68 /*    DATE              NAME                      DESCRIPTION             */
69 /*                                                                        */
70 /*  05-19-2020     Chaoqiong Xiao           Initial Version 6.0           */
71 /*  09-30-2020     Chaoqiong Xiao           Modified comment(s),          */
72 /*                                            resulting in version 6.1    */
73 /*  01-31-2022     Chaoqiong Xiao           Modified comment(s),          */
74 /*                                            improved cancel flow,       */
75 /*                                            updated status handling,    */
76 /*                                            improved sanity checks,     */
77 /*                                            resulting in version 6.1.10 */
78 /*                                                                        */
79 /**************************************************************************/
_ux_device_class_pima_partial_object_data_get(UX_SLAVE_CLASS_PIMA * pima,ULONG object_handle,ULONG offset_requested,ULONG length_requested)80 UINT  _ux_device_class_pima_partial_object_data_get(UX_SLAVE_CLASS_PIMA *pima,
81                                                     ULONG object_handle,
82                                                     ULONG offset_requested,
83                                                     ULONG length_requested)
84 {
85 
86 UINT                        status;
87 UX_SLAVE_TRANSFER           *transfer_request;
88 UX_SLAVE_CLASS_PIMA_OBJECT  *object;
89 UCHAR                       *object_data;
90 ULONG                       object_offset;
91 ULONG                       object_length;
92 ULONG                       total_length;
93 ULONG                       object_length_demanded;
94 ULONG                       object_length_received;
95 ULONG                       object_length_transfer;
96 ULONG                       object_bytes_sent;
97 
98     /* If trace is enabled, insert this event into the trace buffer.  */
99     UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_PIMA_PARTIAL_OBJECT_DATA_GET, pima, object_handle, offset_requested, length_requested, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0)
100 
101     /* Obtain the object info from the application.  */
102     status = pima -> ux_device_class_pima_object_info_get(pima, object_handle, &object);
103 
104     /* Check for error.  */
105     if (status == UX_SUCCESS)
106     {
107 
108         /* Get the object length.  */
109         object_length =  object -> ux_device_class_pima_object_length;
110 
111         /* Check how much to return.  */
112         if ((object_length - offset_requested) > length_requested)
113         {
114 
115             /* Return the maximum demanded.  */
116             total_length = length_requested + UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE;
117 
118             /* Set the number of bytes we will send back.  */
119             object_bytes_sent = length_requested;
120         }
121 
122         else
123         {
124 
125             /* Return the maximum we can.  */
126             total_length = (object_length - offset_requested) + UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE;
127 
128             /* Set the number of bytes we will send back.  */
129             object_bytes_sent = object_length - offset_requested;
130 
131         }
132 
133         /* Reset the offset. */
134         object_offset =  0;
135 
136         /* Obtain the pointer to the transfer request of the bulk in endpoint.  */
137         transfer_request =  &pima -> ux_device_class_pima_bulk_in_endpoint -> ux_slave_endpoint_transfer_request;
138 
139         /* Obtain memory for this object info. Use the transfer request pre-allocated memory.  */
140         object_data =  transfer_request -> ux_slave_transfer_request_data_pointer;
141 
142         /* Fill in the total length to be sent (header + payload.   */
143         _ux_utility_long_put(object_data + UX_DEVICE_CLASS_PIMA_DATA_HEADER_LENGTH,
144                                 total_length);
145 
146         /* Fill in the data container type.  */
147         _ux_utility_short_put(object_data + UX_DEVICE_CLASS_PIMA_DATA_HEADER_TYPE,
148                                 UX_DEVICE_CLASS_PIMA_CT_DATA_BLOCK);
149 
150         /* Fill in the data code.  */
151         _ux_utility_short_put(object_data + UX_DEVICE_CLASS_PIMA_DATA_HEADER_CODE,
152                                 UX_DEVICE_CLASS_PIMA_OC_GET_PARTIAL_OBJECT);
153 
154         /* Fill in the Transaction ID.  */
155         _ux_utility_long_put(object_data + UX_DEVICE_CLASS_PIMA_DATA_HEADER_TRANSACTION_ID,
156                                 pima -> ux_device_class_pima_transaction_id);
157 
158         /* Assuming the host will ask for the entire object.  */
159         while (total_length != 0)
160         {
161 
162             /* If this is the first packet, we have to take into account the
163                header.  */
164             if (object_offset == 0)
165             {
166 
167                 /* Calculate the maximum length for the first packet.  */
168                 object_length_demanded = UX_DEVICE_CLASS_PIMA_OBJECT_INFO_BUFFER_SIZE -
169                                             UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE;
170 
171                 /* Can we get that much from the application ?  */
172                 if (object_length_demanded > object_bytes_sent)
173 
174                     /* We ask too much.  */
175                     object_length_demanded =  object_bytes_sent;
176 
177                 /* Obtain some data from the application.  */
178                 status = pima -> ux_device_class_pima_object_data_get(pima, object_handle, object_data + UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE,
179                                                                         object_offset + offset_requested,
180                                                                         object_length_demanded,
181                                                                         &object_length_received);
182 
183                 /* Check status, if we have a problem, we abort.  */
184                 if (status != UX_SUCCESS)
185                 {
186 
187                     break;
188                 }
189                 else
190                 {
191 
192                     /* Adjust the length of the object.  */
193                     object_length -= object_length_received;
194 
195                     /* Adjust the length to be sent.  */
196                     object_length_transfer = object_length_received + UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE;
197                 }
198             }
199             else
200             {
201 
202                 /* Calculate the maximum length for the first packet.  */
203                 object_length_demanded = UX_DEVICE_CLASS_PIMA_TRANSFER_BUFFER_LENGTH;
204 
205                 /* Can we get that much from the application ?  */
206                 if (object_length_demanded > total_length)
207 
208                     /* We ask too much.  */
209                     object_length_demanded =  total_length;
210 
211                 /* Obtain some data from the application.  */
212                 status = pima -> ux_device_class_pima_object_data_get(pima, object_handle, object_data, object_offset + offset_requested,
213                                                                         object_length_demanded,
214                                                                         &object_length_received);
215 
216                 /* Check status, if we have a problem, we abort.  */
217                 if (status != UX_SUCCESS)
218                 {
219 
220                     /* We need to inform the host of an error.  */
221                     break;
222                 }
223                 else
224                 {
225 
226                     /* Adjust the length of the object.  */
227                     object_length -= object_length_received;
228 
229                     /* Adjust the length to be sent.  */
230                     object_length_transfer = object_length_received;
231                 }
232             }
233 
234             /* It's canceled, do not proceed.  */
235             if (pima -> ux_device_class_pima_state == UX_DEVICE_CLASS_PIMA_PHASE_IDLE)
236             {
237                 pima -> ux_device_class_pima_device_status = UX_DEVICE_CLASS_PIMA_RC_OK;
238                 return(UX_ERROR);
239             }
240 
241             /* Send the object data to the host.  */
242             status =  _ux_device_stack_transfer_request(transfer_request, object_length_transfer, UX_DEVICE_CLASS_PIMA_TRANSFER_BUFFER_LENGTH);
243 
244             /* It's canceled, do not proceed.  */
245             if (pima -> ux_device_class_pima_state == UX_DEVICE_CLASS_PIMA_PHASE_IDLE)
246             {
247                 pima -> ux_device_class_pima_device_status = UX_DEVICE_CLASS_PIMA_RC_OK;
248                 return(UX_ERROR);
249             }
250 
251             /* Check for the status. We may have had a request to cancel the transaction from the host.  */
252             if (status != UX_SUCCESS)
253             {
254 
255                 /* Check the completion code for transfer abort from the host.  */
256                 if (transfer_request -> ux_slave_transfer_request_status ==  UX_TRANSFER_STATUS_ABORT)
257                 {
258 
259                     /* Do not proceed.  */
260                     return(UX_ERROR);
261                 }
262                 else
263                 {
264 
265                     /* We need to inform the host of an error.  */
266                     status =  UX_DEVICE_CLASS_PIMA_RC_GENERAL_ERROR;
267                     break;
268                 }
269             }
270             else
271             {
272 
273                 /* Do some sanity check.  */
274                 if (total_length < object_length_received)
275                 {
276 
277                     /* We have an overflow. Do not proceed.  */
278                     status = UX_ERROR;
279                     break;
280                 }
281 
282                 /* Adjust the offset within the object data.  */
283                 object_offset += object_length_received;
284 
285                 /* Update the total length to be sent.  */
286                 total_length -= object_length_transfer;
287             }
288         }
289 
290         /* Now we return a response with success.  */
291         _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_OK, 1, object_bytes_sent, 0, 0);
292     }
293 
294     /* Check if status is OK.  */
295     if (status != UX_SUCCESS)
296 
297         /* We need to stall the bulk in pipe.  This is the method used by Pima devices to
298            cancel a transaction.  */
299         _ux_device_stack_endpoint_stall(pima -> ux_device_class_pima_bulk_in_endpoint);
300 
301     /* Return completion status.  */
302     return(status);
303 }
304