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