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 /** PIMA 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_host_class_pima.h"
29 #include "ux_host_stack.h"
30
31
32 /**************************************************************************/
33 /* */
34 /* FUNCTION RELEASE */
35 /* */
36 /* _ux_host_class_pima_object_get PORTABLE C */
37 /* 6.1.10 */
38 /* AUTHOR */
39 /* */
40 /* Chaoqiong Xiao, Microsoft Corporation */
41 /* */
42 /* DESCRIPTION */
43 /* */
44 /* This function gets an object identified by the object_handle */
45 /* INPUT */
46 /* */
47 /* pima Pointer to pima class */
48 /* pima_session Pointer to pima session */
49 /* object_handle The object handle */
50 /* object Pointer to object info */
51 /* object_buffer Buffer to be used */
52 /* object_buffer_length Buffer length */
53 /* object_actual_length Length read in that */
54 /* command */
55 /* OUTPUT */
56 /* */
57 /* Completion Status */
58 /* */
59 /* CALLS */
60 /* */
61 /* _ux_host_stack_transfer_request Transfer request */
62 /* _ux_host_stack_transfer_request_abort Abort transfer */
63 /* _ux_host_stack_endpoint_reset Reset endpoint */
64 /* _ux_host_semaphore_get Get semaphore */
65 /* _ux_utility_memory_copy Copy memory */
66 /* _ux_utility_long_get Get 32-bit value */
67 /* _ux_utility_long_put Put 32-bit value */
68 /* _ux_utility_short_put Put 16-bit value */
69 /* */
70 /* CALLED BY */
71 /* */
72 /* USB application */
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 /* prefixed UX to MS_TO_TICK, */
81 /* verified memset and memcpy */
82 /* cases, */
83 /* resulting in version 6.1 */
84 /* 01-31-2022 Chaoqiong Xiao Modified comment(s), */
85 /* refined macros names, */
86 /* resulting in version 6.1.10 */
87 /* */
88 /**************************************************************************/
_ux_host_class_pima_object_get(UX_HOST_CLASS_PIMA * pima,UX_HOST_CLASS_PIMA_SESSION * pima_session,ULONG object_handle,UX_HOST_CLASS_PIMA_OBJECT * object,UCHAR * object_buffer,ULONG object_buffer_length,ULONG * object_actual_length)89 UINT _ux_host_class_pima_object_get(UX_HOST_CLASS_PIMA *pima,
90 UX_HOST_CLASS_PIMA_SESSION *pima_session,
91 ULONG object_handle, UX_HOST_CLASS_PIMA_OBJECT *object,
92 UCHAR *object_buffer, ULONG object_buffer_length,
93 ULONG *object_actual_length)
94 {
95
96 UX_HOST_CLASS_PIMA_COMMAND command;
97 UX_TRANSFER *transfer_request;
98 UCHAR *ptp_payload;
99 ULONG requested_length;
100 ULONG total_length;
101 UINT status;
102
103 /* If trace is enabled, insert this event into the trace buffer. */
104 UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PIMA_OBJECT_GET, pima, object_handle, object, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0)
105
106 /* Check if this session is valid or not. */
107 if (pima_session -> ux_host_class_pima_session_magic != UX_HOST_CLASS_PIMA_MAGIC_NUMBER)
108 return (UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN);
109
110 /* Check if this session is opened or not. */
111 if (pima_session -> ux_host_class_pima_session_state != UX_HOST_CLASS_PIMA_SESSION_STATE_OPENED)
112 return (UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN);
113
114 /* Check if the object is already opened. */
115 if (object -> ux_host_class_pima_object_state != UX_HOST_CLASS_PIMA_OBJECT_STATE_OPENED)
116 return (UX_HOST_CLASS_PIMA_RC_OBJECT_NOT_OPENED);
117
118 /* Check the transfer status. If there was an error or transfer is completed, refuse transfer. */
119 if ((object -> ux_host_class_pima_object_transfer_status == UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_COMPLETED) ||
120 (object -> ux_host_class_pima_object_transfer_status == UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_ABORTED))
121 return (UX_HOST_CLASS_PIMA_RC_ACCESS_DENIED);
122
123 /* Reset the actual length. */
124 *object_actual_length = 0;
125
126 /* This variable will remain untouched if the offset is not 0 and the requested length is 0. */
127 status = UX_SUCCESS;
128
129 /* Check if the offset to be read is at 0, if so, prepare the first read command. */
130 if (object -> ux_host_class_pima_object_offset == 0)
131 {
132
133 /* Set the object transfer status to active. */
134 object -> ux_host_class_pima_object_transfer_status = UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_ACTIVE;
135
136 /* Issue command to get the object info. 1 parameter. */
137 command.ux_host_class_pima_command_nb_parameters = 1;
138
139 /* Parameter 1 is the Object Handle. */
140 command.ux_host_class_pima_command_parameter_1 = object_handle;
141
142 /* Then set the command to GET_OBJECT. */
143 command.ux_host_class_pima_command_operation_code = UX_HOST_CLASS_PIMA_OC_GET_OBJECT;
144
145 /* We use the Bulk Out pipe for sending data out.. */
146 transfer_request = &pima -> ux_host_class_pima_bulk_out_endpoint -> ux_endpoint_transfer_request;
147
148 /* Get the pointer to the ptp payload. */
149 ptp_payload = pima -> ux_host_class_pima_container ;
150
151 /* Calculate the requested length for this payload. */
152 requested_length = UX_HOST_CLASS_PIMA_COMMAND_HEADER_SIZE + ((ULONG)sizeof(ULONG) * command.ux_host_class_pima_command_nb_parameters);
153
154 /* Fill the command container. First the length of the total header and payload. */
155 _ux_utility_long_put(ptp_payload + UX_HOST_CLASS_PIMA_COMMAND_HEADER_LENGTH, requested_length);
156
157
158 /* Then the type of container : a command block here. */
159 _ux_utility_short_put(ptp_payload + UX_HOST_CLASS_PIMA_COMMAND_HEADER_TYPE, UX_HOST_CLASS_PIMA_CT_COMMAND_BLOCK);
160
161 /* Now the command code to send. */
162 _ux_utility_short_put(ptp_payload + UX_HOST_CLASS_PIMA_COMMAND_HEADER_CODE, (USHORT)command.ux_host_class_pima_command_operation_code);
163
164 /* Put the transaction ID. */
165 _ux_utility_long_put(ptp_payload + UX_HOST_CLASS_PIMA_COMMAND_HEADER_TRANSACTION_ID,
166 pima -> ux_host_class_pima_transaction_id++);
167
168 /* Then fill in the parameters. */
169 _ux_utility_long_put(ptp_payload + UX_HOST_CLASS_PIMA_COMMAND_HEADER_PARAMETER_1,
170 command.ux_host_class_pima_command_parameter_1);
171
172 /* Initialize the transfer_request. */
173 transfer_request -> ux_transfer_request_data_pointer = ptp_payload;
174 transfer_request -> ux_transfer_request_requested_length = requested_length;
175
176 /* Send request to HCD layer. */
177 status = _ux_host_stack_transfer_request(transfer_request);
178
179 /* If the transfer is successful, we need to wait for the transfer request to be completed. */
180 if (status == UX_SUCCESS)
181 {
182
183 /* Wait for the completion of the transfer request. */
184 status = _ux_host_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, UX_MS_TO_TICK(UX_HOST_CLASS_PIMA_CLASS_TRANSFER_TIMEOUT));
185
186 /* If the semaphore did not succeed we probably have a time out. */
187 if (status != UX_SUCCESS || transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS)
188 {
189
190 /* All transfers pending need to abort. There may have been a partial transfer. */
191 _ux_host_stack_transfer_request_abort(transfer_request);
192
193 /* The endpoint was halted by a transfer error and needs to be reset. */
194 _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_in_endpoint);
195
196 /* The endpoint was halted by a transfer error and needs to be reset. */
197 _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_out_endpoint);
198
199 /* Set the object transfer status to aborted. */
200 object -> ux_host_class_pima_object_transfer_status = UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_ABORTED;
201
202 /* There was an error, return to the caller. */
203 return(UX_TRANSFER_ERROR);
204 }
205 }
206 else
207 {
208
209 /* There was a non transfer error, no partial transfer to be checked */
210 return(status);
211 }
212
213 /* Check for completion of transfer. If the transfer is partial, return to caller.
214 Partial transfer is not OK. */
215 if (requested_length == transfer_request -> ux_transfer_request_actual_length)
216 {
217
218 /* Obtain the first packet. This packet contains the header and some data.
219 We use the Bulk In pipe for receiving data .. */
220 transfer_request = &pima -> ux_host_class_pima_bulk_in_endpoint -> ux_endpoint_transfer_request;
221
222 /* Get the pointer to the ptp payload. */
223 ptp_payload = pima -> ux_host_class_pima_container ;
224
225 /* Calculate the requested length for this payload. It is the minimum
226 of the application's requested length and the container size. */
227 if (object_buffer_length < UX_HOST_CLASS_PIMA_CONTAINER_SIZE)
228 requested_length = object_buffer_length;
229 else
230 requested_length = UX_HOST_CLASS_PIMA_CONTAINER_SIZE;
231
232 /* Initialize the transfer_request. */
233 transfer_request -> ux_transfer_request_data_pointer = ptp_payload;
234 transfer_request -> ux_transfer_request_requested_length = requested_length;
235
236 /* Send request to HCD layer. */
237 status = _ux_host_stack_transfer_request(transfer_request);
238
239 /* If the transfer is successful, we need to wait for the transfer request to be completed. */
240 if (status == UX_SUCCESS)
241 {
242
243 /* Wait for the completion of the transfer request. */
244 status = _ux_host_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, UX_MS_TO_TICK(UX_HOST_CLASS_PIMA_CLASS_TRANSFER_TIMEOUT));
245
246 /* If the semaphore did not succeed we probably have a time out. */
247 if (status != UX_SUCCESS || transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS)
248 {
249
250 /* All transfers pending need to abort. There may have been a partial transfer. */
251 _ux_host_stack_transfer_request_abort(transfer_request);
252
253 /* The endpoint was halted by a transfer error and needs to be reset. */
254 _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_in_endpoint);
255
256 /* The endpoint was halted by a transfer error and needs to be reset. */
257 _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_out_endpoint);
258
259 /* Set the object transfer status to aborted. */
260 object -> ux_host_class_pima_object_transfer_status = UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_ABORTED;
261
262 /* There was an error, return to the caller. */
263 return(UX_TRANSFER_ERROR);
264 }
265 }
266 else
267 {
268
269 /* There was a non transfer error, no partial transfer to be checked */
270 return(status);
271 }
272
273 /* Ensure the transfer is larger than the header. */
274 if (transfer_request -> ux_transfer_request_actual_length > UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE)
275 {
276
277 /* We need to skip the header. Copying the necessary partial memory only. */
278 _ux_utility_memory_copy(object_buffer, ptp_payload + UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE,
279 transfer_request -> ux_transfer_request_actual_length - UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE); /* Use case of memcpy is verified. */
280
281 /* Get the size of the entire data payload to be expected in this object transfer (data + header). If the size is on a boundary
282 the pima protocol demands that the last packet is a ZLP. */
283 total_length = _ux_utility_long_get(ptp_payload + UX_HOST_CLASS_PIMA_DATA_HEADER_LENGTH);
284
285 /* Check for remainder in last packet. */
286 if ((total_length % pima -> ux_host_class_pima_bulk_in_endpoint -> ux_endpoint_descriptor.wMaxPacketSize) == 0)
287
288 /* We have a ZLP condition on a IN. */
289 pima -> ux_host_class_pima_zlp_flag = UX_HOST_CLASS_PIMA_ZLP_IN;
290 else
291
292 /* Do not expect a ZLP. */
293 pima -> ux_host_class_pima_zlp_flag = UX_HOST_CLASS_PIMA_ZLP_NONE;
294
295 /* Compute the total length of the data only. */
296 total_length -= UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE;
297
298 /* Check if the expected length remaining will be more than the current buffer. If so, we need to have a full payload
299 on a packet boundary. */
300 if (total_length > object_buffer_length)
301
302 /* Update what is left to be received. */
303 object_buffer_length -= transfer_request -> ux_transfer_request_actual_length;
304
305 else
306
307 /* Update what is left to be received. */
308 object_buffer_length -= transfer_request -> ux_transfer_request_actual_length - UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE;
309
310 /* Update the actual length. */
311 *object_actual_length = transfer_request -> ux_transfer_request_actual_length - UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE;
312
313 /* Update where we will store the next data. */
314 object_buffer += transfer_request -> ux_transfer_request_actual_length - UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE;
315
316 /* And the offset. */
317 object -> ux_host_class_pima_object_offset += transfer_request -> ux_transfer_request_actual_length - UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE;
318 }
319 else
320 {
321
322 /* The transfer is smaller than the header, which is an error. */
323
324 /* Report error to application. */
325 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CLASS_MALFORMED_PACKET_RECEIVED_ERROR);
326
327 /* Return error. */
328 return(UX_CLASS_MALFORMED_PACKET_RECEIVED_ERROR);
329 }
330 }
331 else
332
333 /* We got a premature error. */
334 return(UX_HOST_CLASS_PIMA_RC_INCOMPLETE_TRANSFER);
335 }
336
337 /* We use the Bulk In pipe for receiving data .. */
338 transfer_request = &pima -> ux_host_class_pima_bulk_in_endpoint -> ux_endpoint_transfer_request;
339
340 /* We read a complete block. */
341 while(object_buffer_length != 0)
342 {
343
344 /* It may take several transactions. */
345 if (object_buffer_length > UX_HOST_CLASS_PIMA_MAX_PAYLOAD)
346
347 /* Set the requested length to the payload maximum. */
348 requested_length = UX_HOST_CLASS_PIMA_MAX_PAYLOAD;
349
350 else
351
352 /* We can use the user supplied length to complete this request. */
353 requested_length = object_buffer_length;
354
355 /* Initialize the transfer_request. */
356 transfer_request -> ux_transfer_request_data_pointer = object_buffer;
357 transfer_request -> ux_transfer_request_requested_length = requested_length;
358
359 /* Send request to HCD layer. */
360 status = _ux_host_stack_transfer_request(transfer_request);
361
362 /* If the transfer is successful, we need to wait for the transfer request to be completed. */
363 if (status == UX_SUCCESS)
364 {
365
366 /* Wait for the completion of the transfer request. */
367 status = _ux_host_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, UX_MS_TO_TICK(UX_HOST_CLASS_PIMA_CLASS_TRANSFER_TIMEOUT));
368
369 /* If the semaphore did not succeed we probably have a time out. */
370 if (status != UX_SUCCESS || transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS)
371 {
372
373 /* All transfers pending need to abort. There may have been a partial transfer. */
374 _ux_host_stack_transfer_request_abort(transfer_request);
375
376 /* The endpoint was halted by a transfer error and needs to be reset. */
377 _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_in_endpoint);
378
379 /* The endpoint was halted by a transfer error and needs to be reset. */
380 _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_out_endpoint);
381
382 /* Set the object transfer status to aborted. */
383 object -> ux_host_class_pima_object_transfer_status = UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_ABORTED;
384
385 /* There was an error, return to the caller. */
386 return(UX_TRANSFER_ERROR);
387 }
388 }
389 else
390 {
391
392 /* There was a non transfer error, no partial transfer to be checked */
393 return(status);
394 }
395
396 /* Update the object length expected by the user with what we actually received. */
397 object_buffer_length -= transfer_request -> ux_transfer_request_actual_length;
398
399 /* Update the actual length. */
400 *object_actual_length += transfer_request -> ux_transfer_request_actual_length;
401
402 /* And the offset. */
403 object -> ux_host_class_pima_object_offset += transfer_request -> ux_transfer_request_actual_length;
404
405 /* And the object buffer pointer . */
406 object_buffer += transfer_request -> ux_transfer_request_actual_length;
407
408 /* Check to see if we are at the end of the object. */
409 if (object -> ux_host_class_pima_object_offset == object -> ux_host_class_pima_object_compressed_size)
410
411 /* The transfer for this transaction is completed. */
412 object -> ux_host_class_pima_object_transfer_status = UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_COMPLETED;
413
414 }
415
416 /* Return completion status. */
417 return(status);
418 }
419
420 /**************************************************************************/
421 /* */
422 /* FUNCTION RELEASE */
423 /* */
424 /* _uxe_host_class_pima_object_get PORTABLE C */
425 /* 6.3.0 */
426 /* AUTHOR */
427 /* */
428 /* Yajun Xia, Microsoft Corporation */
429 /* */
430 /* DESCRIPTION */
431 /* */
432 /* This function checks errors in pima object get function call. */
433 /* */
434 /* INPUT */
435 /* */
436 /* pima Pointer to pima class */
437 /* pima_session Pointer to pima session */
438 /* object_handle The object handle */
439 /* object Pointer to object info */
440 /* object_buffer Buffer to be used */
441 /* object_buffer_length Buffer length */
442 /* object_actual_length Length read in that */
443 /* command */
444 /* */
445 /* OUTPUT */
446 /* */
447 /* Completion Status */
448 /* */
449 /* CALLS */
450 /* */
451 /* _ux_host_class_pima_object_get Get pima object */
452 /* */
453 /* CALLED BY */
454 /* */
455 /* USB application */
456 /* */
457 /* RELEASE HISTORY */
458 /* */
459 /* DATE NAME DESCRIPTION */
460 /* */
461 /* 10-31-2023 Yajun xia Initial Version 6.3.0 */
462 /* */
463 /**************************************************************************/
_uxe_host_class_pima_object_get(UX_HOST_CLASS_PIMA * pima,UX_HOST_CLASS_PIMA_SESSION * pima_session,ULONG object_handle,UX_HOST_CLASS_PIMA_OBJECT * object,UCHAR * object_buffer,ULONG object_buffer_length,ULONG * object_actual_length)464 UINT _uxe_host_class_pima_object_get(UX_HOST_CLASS_PIMA *pima,
465 UX_HOST_CLASS_PIMA_SESSION *pima_session,
466 ULONG object_handle, UX_HOST_CLASS_PIMA_OBJECT *object,
467 UCHAR *object_buffer, ULONG object_buffer_length,
468 ULONG *object_actual_length)
469 {
470
471 /* Sanity checks. */
472 if (pima == UX_NULL || pima_session == UX_NULL || object == UX_NULL || object_buffer == UX_NULL || object_actual_length == UX_NULL)
473 return(UX_INVALID_PARAMETER);
474
475 /* Call the actual pima object get function. */
476 return(_ux_host_class_pima_object_get(pima, pima_session, object_handle, object, object_buffer, object_buffer_length, object_actual_length));
477 }