1 /**************************************************************************/
2 /* */
3 /* Copyright (c) Microsoft Corporation. All rights reserved. */
4 /* */
5 /* This software is licensed under the Microsoft Software License */
6 /* Terms for Microsoft Azure RTOS. Full text of the license can be */
7 /* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */
8 /* and in the root directory of this software. */
9 /* */
10 /**************************************************************************/
11
12
13 /**************************************************************************/
14 /**************************************************************************/
15 /** */
16 /** USBX Component */
17 /** */
18 /** PIMA Class */
19 /** */
20 /**************************************************************************/
21 /**************************************************************************/
22
23
24 /* Include necessary system files. */
25
26 #define UX_SOURCE_CODE
27
28 #include "ux_api.h"
29 #include "ux_host_class_pima.h"
30 #include "ux_host_stack.h"
31
32
33 /**************************************************************************/
34 /* */
35 /* FUNCTION RELEASE */
36 /* */
37 /* _ux_host_class_pima_object_send PORTABLE C */
38 /* 6.1.10 */
39 /* AUTHOR */
40 /* */
41 /* Chaoqiong Xiao, Microsoft Corporation */
42 /* */
43 /* DESCRIPTION */
44 /* */
45 /* This function sends an object to the media. This commands should be */
46 /* be proceeded by a object_info_send command. */
47 /* */
48 /* INPUT */
49 /* */
50 /* pima Pointer to pima class */
51 /* pima_session Pointer to pima session */
52 /* object Pointer to object info */
53 /* object_buffer Buffer to be used */
54 /* object_buffer_length Buffer length */
55 /* callback_function Application function to */
56 /* callback when buffer */
57 /* needs to be written */
58 /* */
59 /* OUTPUT */
60 /* */
61 /* Completion Status */
62 /* */
63 /* CALLS */
64 /* */
65 /* _ux_host_stack_transfer_request Transfer request */
66 /* _ux_host_stack_transfer_request_abort Abort transfer */
67 /* _ux_host_stack_endpoint_reset Reset endpoint */
68 /* _ux_host_semaphore_get Get semaphore */
69 /* _ux_utility_long_put Put 32-bit value */
70 /* _ux_utility_short_put Put 16-bit value */
71 /* */
72 /* CALLED BY */
73 /* */
74 /* USB application */
75 /* */
76 /* RELEASE HISTORY */
77 /* */
78 /* DATE NAME DESCRIPTION */
79 /* */
80 /* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */
81 /* 09-30-2020 Chaoqiong Xiao Modified comment(s), */
82 /* prefixed UX to MS_TO_TICK, */
83 /* verified memset and memcpy */
84 /* cases, */
85 /* resulting in version 6.1 */
86 /* 01-31-2022 Chaoqiong Xiao Modified comment(s), */
87 /* refined macros names, */
88 /* resulting in version 6.1.10 */
89 /* */
90 /**************************************************************************/
_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)91 UINT _ux_host_class_pima_object_send(UX_HOST_CLASS_PIMA *pima,
92 UX_HOST_CLASS_PIMA_SESSION *pima_session,
93 UX_HOST_CLASS_PIMA_OBJECT *object,
94 UCHAR *object_buffer, ULONG object_buffer_length)
95 {
96
97
98 UX_HOST_CLASS_PIMA_COMMAND command;
99 UX_TRANSFER *transfer_request;
100 UCHAR *ptp_payload;
101 ULONG requested_length;
102 UINT status;
103
104 /* If trace is enabled, insert this event into the trace buffer. */
105 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)
106
107 /* Check if this session is valid or not. */
108 if (pima_session -> ux_host_class_pima_session_magic != UX_HOST_CLASS_PIMA_MAGIC_NUMBER)
109 return (UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN);
110
111 /* Check if this session is opened or not. */
112 if (pima_session -> ux_host_class_pima_session_state != UX_HOST_CLASS_PIMA_SESSION_STATE_OPENED)
113 return (UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN);
114
115 /* Check if the object is already opened. */
116 if (object -> ux_host_class_pima_object_state != UX_HOST_CLASS_PIMA_OBJECT_STATE_OPENED)
117 return (UX_HOST_CLASS_PIMA_RC_OBJECT_NOT_OPENED);
118
119 /* Check the transfer status. If there was an error or transfer is completed, refuse transfer. */
120 if ((object -> ux_host_class_pima_object_transfer_status == UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_COMPLETED) ||
121 (object -> ux_host_class_pima_object_transfer_status == UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_ABORTED))
122 return (UX_HOST_CLASS_PIMA_RC_ACCESS_DENIED);
123
124 /* This variable will remain untouched if the offset is not 0 and the requested length is 0. */
125 status = UX_SUCCESS;
126
127 /* Check if the offset to be read is at 0, if so, prepare the first write command. */
128 if (object -> ux_host_class_pima_object_offset == 0)
129 {
130
131 /* Set the object transfer status to active. */
132 object -> ux_host_class_pima_object_transfer_status = UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_ACTIVE;
133
134 /* Issue command to get the object info. no parameter. */
135 command.ux_host_class_pima_command_nb_parameters = 0;
136
137 /* Then set the command to SEND_OBJECT. */
138 command.ux_host_class_pima_command_operation_code = UX_HOST_CLASS_PIMA_OC_SEND_OBJECT;
139
140 /* We use the Bulk Out pipe for sending command out.. */
141 transfer_request = &pima -> ux_host_class_pima_bulk_out_endpoint -> ux_endpoint_transfer_request;
142
143 /* Get the pointer to the ptp payload. */
144 ptp_payload = pima -> ux_host_class_pima_container ;
145
146 /* Calculate the requested length for this payload. */
147 requested_length = UX_HOST_CLASS_PIMA_COMMAND_HEADER_SIZE + ((ULONG)sizeof(ULONG) * command.ux_host_class_pima_command_nb_parameters);
148
149 /* Fill the command container. First the length of the total header and payload. */
150 _ux_utility_long_put(ptp_payload + UX_HOST_CLASS_PIMA_COMMAND_HEADER_LENGTH, requested_length);
151
152 /* Then the type of container : a command block here. */
153 _ux_utility_short_put(ptp_payload + UX_HOST_CLASS_PIMA_COMMAND_HEADER_TYPE, UX_HOST_CLASS_PIMA_CT_COMMAND_BLOCK);
154
155 /* Now the command code to send. */
156 _ux_utility_short_put(ptp_payload + UX_HOST_CLASS_PIMA_COMMAND_HEADER_CODE, (USHORT)command.ux_host_class_pima_command_operation_code);
157
158 /* Put the transaction ID. */
159 _ux_utility_long_put(ptp_payload + UX_HOST_CLASS_PIMA_COMMAND_HEADER_TRANSACTION_ID,
160 pima -> ux_host_class_pima_transaction_id++);
161
162 /* Initialize the transfer_request. */
163 transfer_request -> ux_transfer_request_data_pointer = ptp_payload;
164 transfer_request -> ux_transfer_request_requested_length = requested_length;
165
166 /* Send request to HCD layer. */
167 status = _ux_host_stack_transfer_request(transfer_request);
168
169 /* If the transfer is successful, we need to wait for the transfer request to be completed. */
170 if (status == UX_SUCCESS)
171 {
172
173 /* Wait for the completion of the transfer request. */
174 status = _ux_host_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, UX_MS_TO_TICK(UX_HOST_CLASS_PIMA_CLASS_TRANSFER_TIMEOUT));
175
176 /* If the semaphore did not succeed we probably have a time out. */
177 if (status != UX_SUCCESS)
178 {
179
180 /* All transfers pending need to abort. There may have been a partial transfer. */
181 _ux_host_stack_transfer_request_abort(transfer_request);
182
183 /* The endpoint was halted by a transfer error and needs to be reset. */
184 _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_in_endpoint);
185
186 /* The endpoint was halted by a transfer error and needs to be reset. */
187 _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_out_endpoint);
188
189 /* Set the object transfer status to aborted. */
190 object -> ux_host_class_pima_object_transfer_status = UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_ABORTED;
191
192 /* There was an error, return to the caller. */
193 return(status);
194 }
195 }
196 else
197 {
198
199 /* There was a non transfer error, no partial transfer to be checked */
200 return(status);
201 }
202
203 /* Check for completion of transfer. If the transfer is partial, return to caller.
204 Partial transfer is not OK. */
205 if (requested_length == transfer_request -> ux_transfer_request_actual_length)
206 {
207
208 /* Send the first packet. This packet contains the header and some data.
209 We use the Bulk Out pipe for sending data .. */
210 transfer_request = &pima -> ux_host_class_pima_bulk_out_endpoint -> ux_endpoint_transfer_request;
211
212 /* Get the pointer to the ptp payload. */
213 ptp_payload = pima -> ux_host_class_pima_container ;
214
215 /* Container type is a data type. */
216 *(ptp_payload + UX_HOST_CLASS_PIMA_DATA_HEADER_TYPE) = UX_HOST_CLASS_PIMA_CT_DATA_BLOCK;
217
218 /* Fill in the header values. Start with the length of the container. */
219 _ux_utility_long_put(ptp_payload + UX_HOST_CLASS_PIMA_DATA_HEADER_LENGTH,
220 object -> ux_host_class_pima_object_compressed_size + UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE);
221
222 /* If the total payload is on a boundary, we need a ZLP to mark the end. */
223 if (((object -> ux_host_class_pima_object_compressed_size + UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE) %
224 pima -> ux_host_class_pima_bulk_out_endpoint-> ux_endpoint_descriptor.wMaxPacketSize) == 0)
225
226 /* We have a ZLP condition. */
227 pima -> ux_host_class_pima_zlp_flag = UX_HOST_CLASS_PIMA_ZLP_OUT;
228 else
229
230 /* Do not expect a ZLP. */
231 pima -> ux_host_class_pima_zlp_flag = UX_HOST_CLASS_PIMA_ZLP_NONE;
232
233 /* Calculate the requested length for this payload. */
234 requested_length = UX_HOST_CLASS_PIMA_CONTAINER_SIZE;
235
236 /* Check to see if we have enough to fill the first packet. */
237 if (requested_length > (UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE + object_buffer_length))
238
239 /* This is a small object, enough to fit into the fist packet. */
240 requested_length = UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE + object_buffer_length;
241
242 /* We need to skip the header. Copying the necessary partial memory only. */
243 _ux_utility_memory_copy(ptp_payload + UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE, object_buffer,
244 requested_length - UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE); /* Use case of memcpy is verified. */
245
246 /* Update the offset. */
247 object -> ux_host_class_pima_object_offset += requested_length - UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE;
248
249 /* And the current object buffer pointer. */
250 object_buffer += requested_length - UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE;
251
252 /* Adjust the remaining buffer length. */
253 object_buffer_length -= requested_length - UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE;
254
255 /* Initialize the transfer_request. */
256 transfer_request -> ux_transfer_request_data_pointer = ptp_payload;
257 transfer_request -> ux_transfer_request_requested_length = requested_length;
258
259 /* Send request to HCD layer. */
260 status = _ux_host_stack_transfer_request(transfer_request);
261
262 /* If the transfer is successful, we need to wait for the transfer request to be completed. */
263 if (status == UX_SUCCESS)
264 {
265
266 /* Wait for the completion of the transfer request. */
267 status = _ux_host_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, UX_MS_TO_TICK(UX_HOST_CLASS_PIMA_CLASS_TRANSFER_TIMEOUT));
268
269 /* If the semaphore did not succeed we probably have a time out. */
270 if (status != UX_SUCCESS)
271 {
272
273 /* All transfers pending need to abort. There may have been a partial transfer. */
274 _ux_host_stack_transfer_request_abort(transfer_request);
275
276 /* The endpoint was halted by a transfer error and needs to be reset. */
277 _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_in_endpoint);
278
279 /* The endpoint was halted by a transfer error and needs to be reset. */
280 _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_out_endpoint);
281
282 /* Set the object transfer status to aborted. */
283 object -> ux_host_class_pima_object_transfer_status = UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_ABORTED;
284
285 /* There was an error, return to the caller. */
286 return(status);
287 }
288 }
289 else
290 {
291
292 /* There was a non transfer error, no partial transfer to be checked */
293 return(status);
294 }
295
296
297 }
298 else
299
300 /* We got a premature error. */
301 return(UX_HOST_CLASS_PIMA_RC_INCOMPLETE_TRANSFER);
302 }
303
304
305 /* We use the Bulk Out pipe for sending data .. */
306 transfer_request = &pima -> ux_host_class_pima_bulk_out_endpoint -> ux_endpoint_transfer_request;
307
308 /* We send a complete block. */
309 while(object_buffer_length != 0)
310 {
311
312 /* It may take several transactions. */
313 if (object_buffer_length > UX_HOST_CLASS_PIMA_MAX_PAYLOAD)
314
315 /* Set the requested length to the payload maximum. */
316 requested_length = UX_HOST_CLASS_PIMA_MAX_PAYLOAD;
317
318 else
319
320 /* We can use the user supplied length to complete this request. */
321 requested_length = object_buffer_length;
322
323 /* Initialize the transfer_request. */
324 transfer_request -> ux_transfer_request_data_pointer = object_buffer;
325 transfer_request -> ux_transfer_request_requested_length = requested_length;
326
327 /* Send request to HCD layer. */
328 status = _ux_host_stack_transfer_request(transfer_request);
329
330 /* If the transfer is successful, we need to wait for the transfer request to be completed. */
331 if (status == UX_SUCCESS)
332 {
333
334 /* Wait for the completion of the transfer request. */
335 status = _ux_host_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, UX_MS_TO_TICK(UX_HOST_CLASS_PIMA_CLASS_TRANSFER_TIMEOUT));
336
337 /* If the semaphore did not succeed we probably have a time out. */
338 if (status != UX_SUCCESS)
339 {
340
341 /* All transfers pending need to abort. There may have been a partial transfer. */
342 _ux_host_stack_transfer_request_abort(transfer_request);
343
344 /* The endpoint was halted by a transfer error and needs to be reset. */
345 _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_in_endpoint);
346
347 /* The endpoint was halted by a transfer error and needs to be reset. */
348 _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_out_endpoint);
349
350 /* Set the object transfer status to aborted. */
351 object -> ux_host_class_pima_object_transfer_status = UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_ABORTED;
352
353 /* There was an error, return to the caller. */
354 return(status);
355 }
356 }
357 else
358 {
359
360 /* There was a non transfer error, no partial transfer to be checked */
361 return(status);
362 }
363
364 /* Update the object length expected by the user with what we actually sent. */
365 object_buffer_length -= transfer_request -> ux_transfer_request_actual_length;
366
367 /* And the offset. */
368 object -> ux_host_class_pima_object_offset += transfer_request -> ux_transfer_request_actual_length;
369
370 /* And the object buffer pointer . */
371 object_buffer += transfer_request -> ux_transfer_request_actual_length;
372
373 /* Check to see if we are at the end of the object. */
374 if (object -> ux_host_class_pima_object_offset == object -> ux_host_class_pima_object_compressed_size)
375
376 /* The transfer for this transaction is completed. */
377 object -> ux_host_class_pima_object_transfer_status = UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_COMPLETED;
378
379 }
380
381 /* Return completion status. */
382 return(status);
383 }
384
385