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_send PORTABLE C */
37 /* 6.1.10 */
38 /* AUTHOR */
39 /* */
40 /* Chaoqiong Xiao, Microsoft Corporation */
41 /* */
42 /* DESCRIPTION */
43 /* */
44 /* This function sends an object to the media. This commands should be */
45 /* be proceeded by a object_info_send command. */
46 /* */
47 /* INPUT */
48 /* */
49 /* pima Pointer to pima class */
50 /* pima_session Pointer to pima session */
51 /* object Pointer to object info */
52 /* object_buffer Buffer to be used */
53 /* object_buffer_length Buffer length */
54 /* callback_function Application function to */
55 /* callback when buffer */
56 /* needs to be written */
57 /* */
58 /* OUTPUT */
59 /* */
60 /* Completion Status */
61 /* */
62 /* CALLS */
63 /* */
64 /* _ux_host_stack_transfer_request Transfer request */
65 /* _ux_host_stack_transfer_request_abort Abort transfer */
66 /* _ux_host_stack_endpoint_reset Reset endpoint */
67 /* _ux_host_semaphore_get Get semaphore */
68 /* _ux_utility_long_put Put 32-bit value */
69 /* _ux_utility_short_put Put 16-bit value */
70 /* */
71 /* CALLED BY */
72 /* */
73 /* USB application */
74 /* */
75 /* RELEASE HISTORY */
76 /* */
77 /* DATE NAME DESCRIPTION */
78 /* */
79 /* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */
80 /* 09-30-2020 Chaoqiong Xiao Modified comment(s), */
81 /* prefixed UX to MS_TO_TICK, */
82 /* verified memset and memcpy */
83 /* cases, */
84 /* resulting in version 6.1 */
85 /* 01-31-2022 Chaoqiong Xiao Modified comment(s), */
86 /* refined macros names, */
87 /* resulting in version 6.1.10 */
88 /* */
89 /**************************************************************************/
_ux_host_class_pima_object_send(UX_HOST_CLASS_PIMA * pima,UX_HOST_CLASS_PIMA_SESSION * pima_session,UX_HOST_CLASS_PIMA_OBJECT * object,UCHAR * object_buffer,ULONG object_buffer_length)90 UINT _ux_host_class_pima_object_send(UX_HOST_CLASS_PIMA *pima,
91 UX_HOST_CLASS_PIMA_SESSION *pima_session,
92 UX_HOST_CLASS_PIMA_OBJECT *object,
93 UCHAR *object_buffer, ULONG object_buffer_length)
94 {
95
96
97 UX_HOST_CLASS_PIMA_COMMAND command;
98 UX_TRANSFER *transfer_request;
99 UCHAR *ptp_payload;
100 ULONG requested_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_SEND, pima, object, object_buffer, object_buffer_length, 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 /* This variable will remain untouched if the offset is not 0 and the requested length is 0. */
124 status = UX_SUCCESS;
125
126 /* Check if the offset to be read is at 0, if so, prepare the first write command. */
127 if (object -> ux_host_class_pima_object_offset == 0)
128 {
129
130 /* Set the object transfer status to active. */
131 object -> ux_host_class_pima_object_transfer_status = UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_ACTIVE;
132
133 /* Issue command to get the object info. no parameter. */
134 command.ux_host_class_pima_command_nb_parameters = 0;
135
136 /* Then set the command to SEND_OBJECT. */
137 command.ux_host_class_pima_command_operation_code = UX_HOST_CLASS_PIMA_OC_SEND_OBJECT;
138
139 /* We use the Bulk Out pipe for sending command out.. */
140 transfer_request = &pima -> ux_host_class_pima_bulk_out_endpoint -> ux_endpoint_transfer_request;
141
142 /* Get the pointer to the ptp payload. */
143 ptp_payload = pima -> ux_host_class_pima_container ;
144
145 /* Calculate the requested length for this payload. */
146 requested_length = UX_HOST_CLASS_PIMA_COMMAND_HEADER_SIZE + ((ULONG)sizeof(ULONG) * command.ux_host_class_pima_command_nb_parameters);
147
148 /* Fill the command container. First the length of the total header and payload. */
149 _ux_utility_long_put(ptp_payload + UX_HOST_CLASS_PIMA_COMMAND_HEADER_LENGTH, requested_length);
150
151 /* Then the type of container : a command block here. */
152 _ux_utility_short_put(ptp_payload + UX_HOST_CLASS_PIMA_COMMAND_HEADER_TYPE, UX_HOST_CLASS_PIMA_CT_COMMAND_BLOCK);
153
154 /* Now the command code to send. */
155 _ux_utility_short_put(ptp_payload + UX_HOST_CLASS_PIMA_COMMAND_HEADER_CODE, (USHORT)command.ux_host_class_pima_command_operation_code);
156
157 /* Put the transaction ID. */
158 _ux_utility_long_put(ptp_payload + UX_HOST_CLASS_PIMA_COMMAND_HEADER_TRANSACTION_ID,
159 pima -> ux_host_class_pima_transaction_id++);
160
161 /* Initialize the transfer_request. */
162 transfer_request -> ux_transfer_request_data_pointer = ptp_payload;
163 transfer_request -> ux_transfer_request_requested_length = requested_length;
164
165 /* Send request to HCD layer. */
166 status = _ux_host_stack_transfer_request(transfer_request);
167
168 /* If the transfer is successful, we need to wait for the transfer request to be completed. */
169 if (status == UX_SUCCESS)
170 {
171
172 /* Wait for the completion of the transfer request. */
173 status = _ux_host_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, UX_MS_TO_TICK(UX_HOST_CLASS_PIMA_CLASS_TRANSFER_TIMEOUT));
174
175 /* If the semaphore did not succeed we probably have a time out. */
176 if (status != UX_SUCCESS)
177 {
178
179 /* All transfers pending need to abort. There may have been a partial transfer. */
180 _ux_host_stack_transfer_request_abort(transfer_request);
181
182 /* The endpoint was halted by a transfer error and needs to be reset. */
183 _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_in_endpoint);
184
185 /* The endpoint was halted by a transfer error and needs to be reset. */
186 _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_out_endpoint);
187
188 /* Set the object transfer status to aborted. */
189 object -> ux_host_class_pima_object_transfer_status = UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_ABORTED;
190
191 /* There was an error, return to the caller. */
192 return(status);
193 }
194 }
195 else
196 {
197
198 /* There was a non transfer error, no partial transfer to be checked */
199 return(status);
200 }
201
202 /* Check for completion of transfer. If the transfer is partial, return to caller.
203 Partial transfer is not OK. */
204 if (requested_length == transfer_request -> ux_transfer_request_actual_length)
205 {
206
207 /* Send the first packet. This packet contains the header and some data.
208 We use the Bulk Out pipe for sending data .. */
209 transfer_request = &pima -> ux_host_class_pima_bulk_out_endpoint -> ux_endpoint_transfer_request;
210
211 /* Get the pointer to the ptp payload. */
212 ptp_payload = pima -> ux_host_class_pima_container ;
213
214 /* Container type is a data type. */
215 *(ptp_payload + UX_HOST_CLASS_PIMA_DATA_HEADER_TYPE) = UX_HOST_CLASS_PIMA_CT_DATA_BLOCK;
216
217 /* Fill in the header values. Start with the length of the container. */
218 _ux_utility_long_put(ptp_payload + UX_HOST_CLASS_PIMA_DATA_HEADER_LENGTH,
219 object -> ux_host_class_pima_object_compressed_size + UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE);
220
221 /* If the total payload is on a boundary, we need a ZLP to mark the end. */
222 if (((object -> ux_host_class_pima_object_compressed_size + UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE) %
223 pima -> ux_host_class_pima_bulk_out_endpoint-> ux_endpoint_descriptor.wMaxPacketSize) == 0)
224
225 /* We have a ZLP condition. */
226 pima -> ux_host_class_pima_zlp_flag = UX_HOST_CLASS_PIMA_ZLP_OUT;
227 else
228
229 /* Do not expect a ZLP. */
230 pima -> ux_host_class_pima_zlp_flag = UX_HOST_CLASS_PIMA_ZLP_NONE;
231
232 /* Calculate the requested length for this payload. */
233 requested_length = UX_HOST_CLASS_PIMA_CONTAINER_SIZE;
234
235 /* Check to see if we have enough to fill the first packet. */
236 if (requested_length > (UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE + object_buffer_length))
237
238 /* This is a small object, enough to fit into the fist packet. */
239 requested_length = UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE + object_buffer_length;
240
241 /* We need to skip the header. Copying the necessary partial memory only. */
242 _ux_utility_memory_copy(ptp_payload + UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE, object_buffer,
243 requested_length - UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE); /* Use case of memcpy is verified. */
244
245 /* Update the offset. */
246 object -> ux_host_class_pima_object_offset += requested_length - UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE;
247
248 /* And the current object buffer pointer. */
249 object_buffer += requested_length - UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE;
250
251 /* Adjust the remaining buffer length. */
252 object_buffer_length -= requested_length - UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE;
253
254 /* Initialize the transfer_request. */
255 transfer_request -> ux_transfer_request_data_pointer = ptp_payload;
256 transfer_request -> ux_transfer_request_requested_length = requested_length;
257
258 /* Send request to HCD layer. */
259 status = _ux_host_stack_transfer_request(transfer_request);
260
261 /* If the transfer is successful, we need to wait for the transfer request to be completed. */
262 if (status == UX_SUCCESS)
263 {
264
265 /* Wait for the completion of the transfer request. */
266 status = _ux_host_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, UX_MS_TO_TICK(UX_HOST_CLASS_PIMA_CLASS_TRANSFER_TIMEOUT));
267
268 /* If the semaphore did not succeed we probably have a time out. */
269 if (status != UX_SUCCESS)
270 {
271
272 /* All transfers pending need to abort. There may have been a partial transfer. */
273 _ux_host_stack_transfer_request_abort(transfer_request);
274
275 /* The endpoint was halted by a transfer error and needs to be reset. */
276 _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_in_endpoint);
277
278 /* The endpoint was halted by a transfer error and needs to be reset. */
279 _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_out_endpoint);
280
281 /* Set the object transfer status to aborted. */
282 object -> ux_host_class_pima_object_transfer_status = UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_ABORTED;
283
284 /* There was an error, return to the caller. */
285 return(status);
286 }
287 }
288 else
289 {
290
291 /* There was a non transfer error, no partial transfer to be checked */
292 return(status);
293 }
294
295
296 }
297 else
298
299 /* We got a premature error. */
300 return(UX_HOST_CLASS_PIMA_RC_INCOMPLETE_TRANSFER);
301 }
302
303
304 /* We use the Bulk Out pipe for sending data .. */
305 transfer_request = &pima -> ux_host_class_pima_bulk_out_endpoint -> ux_endpoint_transfer_request;
306
307 /* We send a complete block. */
308 while(object_buffer_length != 0)
309 {
310
311 /* It may take several transactions. */
312 if (object_buffer_length > UX_HOST_CLASS_PIMA_MAX_PAYLOAD)
313
314 /* Set the requested length to the payload maximum. */
315 requested_length = UX_HOST_CLASS_PIMA_MAX_PAYLOAD;
316
317 else
318
319 /* We can use the user supplied length to complete this request. */
320 requested_length = object_buffer_length;
321
322 /* Initialize the transfer_request. */
323 transfer_request -> ux_transfer_request_data_pointer = object_buffer;
324 transfer_request -> ux_transfer_request_requested_length = requested_length;
325
326 /* Send request to HCD layer. */
327 status = _ux_host_stack_transfer_request(transfer_request);
328
329 /* If the transfer is successful, we need to wait for the transfer request to be completed. */
330 if (status == UX_SUCCESS)
331 {
332
333 /* Wait for the completion of the transfer request. */
334 status = _ux_host_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, UX_MS_TO_TICK(UX_HOST_CLASS_PIMA_CLASS_TRANSFER_TIMEOUT));
335
336 /* If the semaphore did not succeed we probably have a time out. */
337 if (status != UX_SUCCESS)
338 {
339
340 /* All transfers pending need to abort. There may have been a partial transfer. */
341 _ux_host_stack_transfer_request_abort(transfer_request);
342
343 /* The endpoint was halted by a transfer error and needs to be reset. */
344 _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_in_endpoint);
345
346 /* The endpoint was halted by a transfer error and needs to be reset. */
347 _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_out_endpoint);
348
349 /* Set the object transfer status to aborted. */
350 object -> ux_host_class_pima_object_transfer_status = UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_ABORTED;
351
352 /* There was an error, return to the caller. */
353 return(status);
354 }
355 }
356 else
357 {
358
359 /* There was a non transfer error, no partial transfer to be checked */
360 return(status);
361 }
362
363 /* Update the object length expected by the user with what we actually sent. */
364 object_buffer_length -= transfer_request -> ux_transfer_request_actual_length;
365
366 /* And the offset. */
367 object -> ux_host_class_pima_object_offset += transfer_request -> ux_transfer_request_actual_length;
368
369 /* And the object buffer pointer . */
370 object_buffer += transfer_request -> ux_transfer_request_actual_length;
371
372 /* Check to see if we are at the end of the object. */
373 if (object -> ux_host_class_pima_object_offset == object -> ux_host_class_pima_object_compressed_size)
374
375 /* The transfer for this transaction is completed. */
376 object -> ux_host_class_pima_object_transfer_status = UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_COMPLETED;
377
378 }
379
380 /* Return completion status. */
381 return(status);
382 }
383
384 /**************************************************************************/
385 /* */
386 /* FUNCTION RELEASE */
387 /* */
388 /* _uxe_host_class_pima_object_send PORTABLE C */
389 /* 6.3.0 */
390 /* AUTHOR */
391 /* */
392 /* Yajun Xia, Microsoft Corporation */
393 /* */
394 /* DESCRIPTION */
395 /* */
396 /* This function checks errors in pima object send function call. */
397 /* */
398 /* INPUT */
399 /* */
400 /* pima Pointer to pima class */
401 /* pima_session Pointer to pima session */
402 /* object Pointer to object info */
403 /* object_buffer Buffer to be used */
404 /* object_buffer_length Buffer length */
405 /* callback_function Application function to */
406 /* callback when buffer */
407 /* needs to be written */
408 /* */
409 /* OUTPUT */
410 /* */
411 /* Completion Status */
412 /* */
413 /* CALLS */
414 /* */
415 /* _ux_host_class_pima_object_send Send pima object */
416 /* */
417 /* CALLED BY */
418 /* */
419 /* USB application */
420 /* */
421 /* RELEASE HISTORY */
422 /* */
423 /* DATE NAME DESCRIPTION */
424 /* */
425 /* 10-31-2023 Yajun xia Initial Version 6.3.0 */
426 /* */
427 /**************************************************************************/
_uxe_host_class_pima_object_send(UX_HOST_CLASS_PIMA * pima,UX_HOST_CLASS_PIMA_SESSION * pima_session,UX_HOST_CLASS_PIMA_OBJECT * object,UCHAR * object_buffer,ULONG object_buffer_length)428 UINT _uxe_host_class_pima_object_send(UX_HOST_CLASS_PIMA *pima,
429 UX_HOST_CLASS_PIMA_SESSION *pima_session,
430 UX_HOST_CLASS_PIMA_OBJECT *object,
431 UCHAR *object_buffer, ULONG object_buffer_length)
432 {
433
434 /* Sanity checks. */
435 if ((pima == UX_NULL) || (pima_session == UX_NULL) || (object == UX_NULL) || (object_buffer == UX_NULL))
436 return(UX_INVALID_PARAMETER);
437
438 /* Call the actual pima object send function. */
439 return(_ux_host_class_pima_object_send(pima, pima_session, object, object_buffer, object_buffer_length));
440 }