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 /** Host Simulator Controller Driver */
19 /** */
20 /**************************************************************************/
21 /**************************************************************************/
22
23 #define UX_SOURCE_CODE
24
25
26 /* Include necessary system files. */
27
28 #include "ux_api.h"
29 #include "ux_hcd_sim_host.h"
30 #include "ux_dcd_sim_slave.h"
31 #include "ux_device_stack.h"
32
33
34 /**************************************************************************/
35 /* */
36 /* FUNCTION RELEASE */
37 /* */
38 /* _ux_hcd_sim_host_transaction_schedule PORTABLE C */
39 /* 6.1.12 */
40 /* AUTHOR */
41 /* */
42 /* Chaoqiong Xiao, Microsoft Corporation */
43 /* */
44 /* DESCRIPTION */
45 /* */
46 /* This function bridges a transaction from the host to the slave */
47 /* simulation controller. */
48 /* */
49 /* INPUT */
50 /* */
51 /* hcd_sim_host Pointer to host controller */
52 /* ed Pointer to ED */
53 /* */
54 /* OUTPUT */
55 /* */
56 /* Completion Status */
57 /* */
58 /* CALLS */
59 /* */
60 /* (ux_transfer_request_completion_function) */
61 /* Completion function */
62 /* _ux_device_stack_control_request_process */
63 /* Process request */
64 /* _ux_utility_memory_copy Copy memory block */
65 /* _ux_utility_semaphore_put Semaphore put */
66 /* */
67 /* CALLED BY */
68 /* */
69 /* Host Simulator Controller Driver */
70 /* */
71 /* RELEASE HISTORY */
72 /* */
73 /* DATE NAME DESCRIPTION */
74 /* */
75 /* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */
76 /* 09-30-2020 Chaoqiong Xiao Modified comment(s), */
77 /* verified memset and memcpy */
78 /* cases, */
79 /* resulting in version 6.1 */
80 /* 04-02-2021 Chaoqiong Xiao Modified comment(s), */
81 /* fixed control OUT transfer, */
82 /* supported bi-dir-endpoints, */
83 /* resulting in version 6.1.6 */
84 /* 10-15-2021 Chaoqiong Xiao Modified comment(s), */
85 /* improved check for tests, */
86 /* added error trap case, */
87 /* resulting in version 6.1.9 */
88 /* 01-31-2022 Chaoqiong Xiao Modified comment(s), */
89 /* added standalone support, */
90 /* cleared transfer status */
91 /* before semaphore wakeup to */
92 /* avoid a race condition, */
93 /* resulting in version 6.1.10 */
94 /* 07-29-2022 Chaoqiong Xiao Modified comment(s), */
95 /* refined device ZLP flow, */
96 /* adjusted control request */
97 /* data length handling, */
98 /* resulting in version 6.1.12 */
99 /* */
100 /**************************************************************************/
_ux_hcd_sim_host_transaction_schedule(UX_HCD_SIM_HOST * hcd_sim_host,UX_HCD_SIM_HOST_ED * ed)101 UINT _ux_hcd_sim_host_transaction_schedule(UX_HCD_SIM_HOST *hcd_sim_host, UX_HCD_SIM_HOST_ED *ed)
102 {
103
104 UX_DCD_SIM_SLAVE *dcd_sim_slave;
105 UX_HCD_SIM_HOST_TD *td;
106 UX_HCD_SIM_HOST_TD *head_td;
107 UX_HCD_SIM_HOST_TD *tail_td;
108 UX_HCD_SIM_HOST_TD *data_td;
109 UX_ENDPOINT *endpoint;
110 UX_SLAVE_ENDPOINT *slave_endpoint;
111 UX_DCD_SIM_SLAVE_ED *slave_ed;
112 ULONG slave_transfer_remaining;
113 UCHAR wake_host;
114 UCHAR wake_slave;
115 ULONG transaction_length;
116 ULONG td_length;
117 UX_SLAVE_TRANSFER *slave_transfer_request;
118 UX_TRANSFER *transfer_request;
119 ULONG endpoint_index;
120 UX_SLAVE_DCD *dcd;
121
122 UX_PARAMETER_NOT_USED(hcd_sim_host);
123
124 /* Get the pointer to the DCD portion of the simulator. */
125 dcd = &_ux_system_slave -> ux_system_slave_dcd;
126
127 /* Check the state of the controller if OPERATIONAL . */
128 if (dcd -> ux_slave_dcd_status != UX_DCD_STATUS_OPERATIONAL)
129 return(UX_ERROR);
130
131 /* Get the pointer to the candidate TD on the host. */
132 td = ed -> ux_sim_host_ed_head_td;
133
134 /* Get the pointer to the endpoint. */
135 endpoint = ed -> ux_sim_host_ed_endpoint;
136
137 /* Get the pointer to the transfer_request attached with this TD. */
138 transfer_request = td -> ux_sim_host_td_transfer_request;
139
140 /* Get the index of the endpoint from the host. */
141 endpoint_index = endpoint -> ux_endpoint_descriptor.bEndpointAddress & ~(ULONG)UX_ENDPOINT_DIRECTION;
142
143 /* Get the address of the device controller. */
144 dcd_sim_slave = (UX_DCD_SIM_SLAVE *) dcd -> ux_slave_dcd_controller_hardware;
145
146 /* Get the endpoint as seen from the device side. */
147 #ifdef UX_DEVICE_BIDIRECTIONAL_ENDPOINT_SUPPORT
148 slave_ed = ((endpoint -> ux_endpoint_descriptor.bEndpointAddress == 0) ?
149 &dcd_sim_slave -> ux_dcd_sim_slave_ed[0] :
150 ((endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) ?
151 &dcd_sim_slave -> ux_dcd_sim_slave_ed_in[endpoint_index] :
152 &dcd_sim_slave -> ux_dcd_sim_slave_ed[endpoint_index]));
153 #else
154 slave_ed = &dcd_sim_slave -> ux_dcd_sim_slave_ed[endpoint_index];
155 #endif
156
157 /* Is this ED used? */
158 if ((slave_ed -> ux_sim_slave_ed_status & UX_DCD_SIM_SLAVE_ED_STATUS_USED) == 0)
159 return(UX_ERROR);
160
161 /* Is this ED ready for transaction or stalled ? */
162 if ((slave_ed -> ux_sim_slave_ed_status & (UX_DCD_SIM_SLAVE_ED_STATUS_TRANSFER | UX_DCD_SIM_SLAVE_ED_STATUS_STALLED)) == 0)
163 return(UX_ERROR);
164
165 /* Get the logical endpoint from the physical endpoint. */
166 slave_endpoint = slave_ed -> ux_sim_slave_ed_endpoint;
167
168 /* Get the pointer to the transfer request. */
169 slave_transfer_request = &slave_endpoint -> ux_slave_endpoint_transfer_request;
170
171 /* Check the phase for this transfer, if this is the SETUP phase, treatment is different. Explanation of how
172 control transfers are handled in the simulator: if the data phase is OUT, we handle it immediately, meaning we
173 send all the data to the device and remove the STATUS TD in the same scheduler call. If the data phase is IN, we
174 only take out the SETUP TD and handle the data phase like any other non-control transactions (i.e. the scheduler
175 calls us again with the DATA TDs). */
176 if (td -> ux_sim_host_td_status & UX_HCD_SIM_HOST_TD_SETUP_PHASE)
177 {
178
179 /* For control transfer, stall is for protocol error and it's cleared any time when SETUP is received */
180 slave_ed -> ux_sim_slave_ed_status &= ~(ULONG)UX_DCD_SIM_SLAVE_ED_STATUS_STALLED;
181
182 /* Validate the length to the setup transaction buffer. */
183 UX_ASSERT(td -> ux_sim_host_td_length == 8);
184
185 /* Reset actual data length (not including SETUP received) so far. */
186 slave_transfer_request -> ux_slave_transfer_request_actual_length = 0;
187
188 /* Move the buffer from the host TD to the device TD. */
189 _ux_utility_memory_copy(slave_transfer_request -> ux_slave_transfer_request_setup,
190 td -> ux_sim_host_td_buffer,
191 td -> ux_sim_host_td_length); /* Use case of memcpy is verified. */
192
193 #if defined(UX_HOST_STANDALONE)
194
195 /* The setup buffer is allocated, release it since it's used. */
196 _ux_utility_memory_free(td -> ux_sim_host_td_buffer);
197 td -> ux_sim_host_td_buffer = UX_NULL;
198 #endif
199
200 /* The setup phase never fails. We acknowledge the transfer code here by taking the TD out of the endpoint. */
201 ed -> ux_sim_host_ed_head_td = td -> ux_sim_host_td_next_td;
202
203 /* Free the TD that was used here. */
204 td -> ux_sim_host_td_status = UX_UNUSED;
205
206 /* Check if the transaction is OUT from the host and there is data payload. */
207 if (((*slave_transfer_request -> ux_slave_transfer_request_setup & UX_REQUEST_IN) == 0) &&
208 (*(slave_transfer_request -> ux_slave_transfer_request_setup + 6) != 0 ||
209 *(slave_transfer_request -> ux_slave_transfer_request_setup + 7) != 0))
210 {
211
212 /* This is the case where there is a data payload OUT from host to device.
213 the data needs to be copied into the device buffer first before invoking the control
214 dispatcher. */
215
216 /* Get the length we expect from the SETUP packet (target the entire available control buffer). */
217 slave_transfer_request -> ux_slave_transfer_request_requested_length = _ux_utility_short_get(slave_transfer_request -> ux_slave_transfer_request_setup + 6);
218
219 /* Avoid buffer overflow. */
220 if (slave_transfer_request -> ux_slave_transfer_request_requested_length > UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH)
221 {
222 /* Error trap. */
223 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_DCD, UX_TRANSFER_BUFFER_OVERFLOW);
224 slave_transfer_request -> ux_slave_transfer_request_requested_length = UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH;
225 }
226
227 /* And reprogram the current buffer address to the beginning of the buffer. */
228 slave_transfer_request -> ux_slave_transfer_request_current_data_pointer = slave_transfer_request -> ux_slave_transfer_request_data_pointer;
229
230 /* Get the pointer to the first data TD. this is the TD right after the SETUP one. */
231 data_td = td -> ux_sim_host_td_next_td;
232
233 /* Get the data length we expect. */
234 transaction_length = slave_transfer_request -> ux_slave_transfer_request_requested_length;
235
236 /* It may have taken multiple TDs to send all the data. */
237 while (transaction_length != 0)
238 {
239
240 /* Do a sanity check. TD must be data. */
241 if (data_td -> ux_sim_host_td_status & UX_HCD_SIM_HOST_TD_DATA_PHASE)
242 {
243
244 /* Copy the amount of data in the td into the slave transaction buffer. */
245 td_length = UX_MIN(data_td -> ux_sim_host_td_length, transaction_length);
246 _ux_utility_memory_copy(slave_transfer_request -> ux_slave_transfer_request_current_data_pointer,
247 data_td -> ux_sim_host_td_buffer,
248 td_length); /* Use case of memcpy is verified. */
249
250 /* Add to the actual payload length. */
251 slave_transfer_request -> ux_slave_transfer_request_actual_length += td_length;
252
253 /* Update the host transfer's actual length. */
254 transfer_request -> ux_transfer_request_actual_length += transaction_length;
255
256 /* Decrement the total length. */
257 transaction_length -= td_length;
258
259 /* Update buffer pointer. */
260 slave_transfer_request -> ux_slave_transfer_request_current_data_pointer += td_length;
261
262 /* Update the td in the head. */
263 ed -> ux_sim_host_ed_head_td = data_td;
264
265 /* Get the pointer to the next data TD. */
266 data_td = data_td -> ux_sim_host_td_next_td;
267
268 /* Free the TD that was used here. */
269 ed -> ux_sim_host_ed_head_td -> ux_sim_host_td_status = UX_UNUSED;
270 }
271 }
272
273 /* Make the head TD point to the STATUS TD. */
274 ed -> ux_sim_host_ed_head_td = ed -> ux_sim_host_ed_head_td -> ux_sim_host_td_next_td;
275 }
276
277 /* Is there no hub? */
278 if (dcd_sim_slave -> ux_dcd_sim_slave_dcd_control_request_process_hub == UX_NULL)
279 {
280
281 /* There's no hub to worry about. This control transfer is for the
282 device itself. */
283
284 /* Pass the transfer to the regular device stack. */
285 _ux_device_stack_control_request_process(slave_transfer_request);
286 }
287 else
288 {
289
290 /* There is a hub. We need to call the correct Control Transfer dispatcher.
291 If the device is a hub and this transfer is for one of the devices on the
292 hub, then we must invoke a separate Control Transfer dispatcher besides
293 the regular device stack's. This is because the current device stack doesn't
294 handle control transfers to device's other than itself. */
295
296 /* Is this meant for the device itself? */
297 if (/* If the device isn't ADDRESSED yet, then the address may be invalid since we don't
298 clear it upon disconnection/reconnection. So we assume that if the device is RESET
299 or ATTACHED, the control transfer is meant for the device itself. */
300 (_ux_system_slave->ux_system_slave_device.ux_slave_device_state == UX_DEVICE_RESET ||
301 _ux_system_slave->ux_system_slave_device.ux_slave_device_state == UX_DEVICE_ATTACHED) ||
302
303 /* If we get to this check, then the device has been ADDRESSED and we can compare addresses. */
304 endpoint -> ux_endpoint_device -> ux_device_address == dcd -> ux_slave_dcd_device_address)
305 {
306
307 /* Yes, this control transfer is meant for the device itself. */
308
309 /* Pass the transfer to the regular device stack. */
310 _ux_device_stack_control_request_process(slave_transfer_request);
311 }
312 else
313 {
314
315 /* No, this control transfer is meant for a device on the hub. */
316
317 /* Pass the transfer to the callback. */
318 dcd_sim_slave -> ux_dcd_sim_slave_dcd_control_request_process_hub(slave_transfer_request);
319 }
320 }
321
322 /* Check if the transaction is OUT from the host. */
323 if ((*slave_transfer_request -> ux_slave_transfer_request_setup & UX_REQUEST_IN) == 0)
324 {
325
326 /* Check if there is a problem with the endpoint (maybe stalled). */
327 if (slave_ed -> ux_sim_slave_ed_status & UX_DCD_SIM_SLAVE_ED_STATUS_STALLED)
328 {
329
330 /* Protocol error, stall the transaction. */
331 transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_STALLED;
332 if (transfer_request -> ux_transfer_request_completion_function != UX_NULL)
333 transfer_request -> ux_transfer_request_completion_function(transfer_request);
334
335 /* Error trap. */
336 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_TRANSFER_STALLED);
337
338 /* If trace is enabled, insert this event into the trace buffer. */
339 UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_STALLED, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0)
340 }
341 else
342 /* No error in simulation. */
343 transfer_request -> ux_transfer_request_completion_code = UX_SUCCESS;
344
345 /* In this case the transfer is completed! We take out the status TD. */
346 td = ed -> ux_sim_host_ed_head_td;
347
348 /* Adjust the ED. */
349 ed -> ux_sim_host_ed_head_td = td -> ux_sim_host_td_next_td;
350
351 /* Free the TD that was used here. */
352 td -> ux_sim_host_td_status = UX_UNUSED;
353
354 /* Then, we wake up the host. */
355 _ux_host_semaphore_put(&transfer_request -> ux_transfer_request_semaphore);
356 }
357 }
358 else
359 {
360
361 /* Check if there is a problem with the endpoint (maybe stalled). */
362 if (slave_ed -> ux_sim_slave_ed_status & UX_DCD_SIM_SLAVE_ED_STATUS_STALLED)
363 {
364
365 /* Stall the transaction. */
366 transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_STALLED;
367 if (transfer_request -> ux_transfer_request_completion_function != UX_NULL)
368 transfer_request -> ux_transfer_request_completion_function(transfer_request);
369
370 /* Error trap. */
371 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_TRANSFER_STALLED);
372
373 /* If trace is enabled, insert this event into the trace buffer. */
374 UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_STALLED, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0)
375
376 /* Wake up the host side. */
377 _ux_host_semaphore_put(&transfer_request -> ux_transfer_request_semaphore);
378
379 /* Clean up this ED. */
380 head_td = ed -> ux_sim_host_ed_head_td;
381 tail_td = ed -> ux_sim_host_ed_tail_td;
382
383 /* Free all TDs attached to the ED. */
384 while (head_td != tail_td)
385 {
386
387 /* Mark the current head TD as free. */
388 head_td -> ux_sim_host_td_status = UX_UNUSED;
389
390 /* Update the head TD with the next TD. */
391 ed -> ux_sim_host_ed_head_td = head_td -> ux_sim_host_td_next_td;
392
393 /* Now the new head_td is the next TD in the chain. */
394 head_td = ed -> ux_sim_host_ed_head_td;
395 }
396 }
397 else
398 {
399
400 slave_transfer_remaining = 0;
401
402 /* If the device tries to send a NULL packet, we don't reset the actual length to 0. */
403 if (slave_transfer_request -> ux_slave_transfer_request_requested_length != 0)
404 slave_transfer_remaining = slave_transfer_request -> ux_slave_transfer_request_requested_length - slave_transfer_request -> ux_slave_transfer_request_actual_length;
405
406 /* Get the transaction length to be transferred. It could be a ZLP condition. */
407 if (slave_transfer_remaining <= td -> ux_sim_host_td_length)
408 transaction_length = slave_transfer_remaining;
409 else
410 transaction_length = td -> ux_sim_host_td_length;
411
412 if (transaction_length)
413 {
414 if (td -> ux_sim_host_td_direction == UX_HCD_SIM_HOST_TD_OUT)
415
416 /* Send the requested host data to the device. */
417 _ux_utility_memory_copy(slave_transfer_request -> ux_slave_transfer_request_current_data_pointer,
418 td -> ux_sim_host_td_buffer,
419 transaction_length); /* Use case of memcpy is verified. */
420
421 else
422
423 /* Send the requested host data to the device. */
424 _ux_utility_memory_copy(td -> ux_sim_host_td_buffer,
425 slave_transfer_request -> ux_slave_transfer_request_current_data_pointer,
426 transaction_length); /* Use case of memcpy is verified. */
427 }
428
429 /* Update buffers. */
430 td -> ux_sim_host_td_buffer += transaction_length;
431 slave_transfer_request -> ux_slave_transfer_request_current_data_pointer += transaction_length;
432
433 /* Update actual length values. */
434 td -> ux_sim_host_td_actual_length += transaction_length;
435 transfer_request -> ux_transfer_request_actual_length += transaction_length;
436 slave_transfer_request -> ux_slave_transfer_request_actual_length += transaction_length;
437
438 /* Update requested length values. */
439 td -> ux_sim_host_td_length -= transaction_length;
440
441 /* Are we done with this TD (It's possible for the TD to expect more data; for example, the slave
442 sent/received a smaller amount)? */
443 if (td -> ux_sim_host_td_length == 0)
444 {
445
446 /* Free the TD that was used here. */
447 td -> ux_sim_host_td_status = UX_UNUSED;
448
449 /* Adjust the ED. */
450 ed -> ux_sim_host_ed_head_td = td -> ux_sim_host_td_next_td;
451 }
452
453 /* Reset wake booleans. */
454 wake_host = UX_FALSE;
455 wake_slave = UX_FALSE;
456
457 /* Does the slave have absolutely no more data to send? */
458 if (slave_endpoint -> ux_slave_endpoint_descriptor.wMaxPacketSize == 0) /* Special for tests (avoid DIV0/MOD0) */
459 {
460
461 /* This happens only if device descriptor bMaxPacketSize0 is zero, assume it's OK for the first control
462 requests to let host check the descriptor.
463 If wMaxPacketSize is zero, host reject the device and transfer never started to get here. */
464 wake_host = UX_TRUE;
465 wake_slave = UX_TRUE;
466 }
467 else if ((transaction_length == 0) ||
468 (transaction_length % slave_endpoint -> ux_slave_endpoint_descriptor.wMaxPacketSize))
469 {
470
471 /* Host got ZLP or short packet. */
472 wake_host = UX_TRUE;
473 wake_slave = UX_TRUE;
474 }
475 else
476 {
477
478 /* Is the host's transfer completed? */
479 if (transfer_request -> ux_transfer_request_actual_length == transfer_request -> ux_transfer_request_requested_length)
480 wake_host = UX_TRUE;
481
482 /* Is the slaves's transfer completed? */
483 if (slave_transfer_request -> ux_slave_transfer_request_actual_length ==
484 slave_transfer_request -> ux_slave_transfer_request_requested_length)
485 {
486 if (slave_transfer_request -> ux_slave_transfer_request_requested_length == 0 ||
487 slave_transfer_request -> ux_slave_transfer_request_force_zlp == 0)
488 wake_slave = UX_TRUE;
489 else
490 slave_transfer_request -> ux_slave_transfer_request_force_zlp = 0;
491 }
492 }
493
494 if (wake_slave == UX_TRUE)
495 {
496
497 /* Set the completion code to no error. */
498 slave_transfer_request -> ux_slave_transfer_request_completion_code = UX_SUCCESS;
499
500 /* Set the transfer status to COMPLETED. */
501 slave_transfer_request -> ux_slave_transfer_request_status = UX_TRANSFER_STATUS_COMPLETED;
502
503 /* Is this not the control endpoint? */
504 if (slave_ed -> ux_sim_slave_ed_index != 0)
505 {
506
507 /* Clear pending flag. */
508 slave_ed -> ux_sim_slave_ed_status &= ~(ULONG)UX_DCD_SIM_SLAVE_ED_STATUS_TRANSFER;
509
510 /* Set done flag. */
511 slave_ed -> ux_sim_slave_ed_status |= UX_DCD_SIM_SLAVE_ED_STATUS_DONE;
512
513 /* Wake up the slave side. */
514 _ux_device_semaphore_put(&slave_transfer_request -> ux_slave_transfer_request_semaphore);
515 }
516 }
517
518 if (wake_host == UX_TRUE)
519 {
520
521 /* If the slave has less data to send than the host wants to receive, then there may still be
522 TDs left to free. Note that this should only happen for IN transactions, since the only way
523 an OUT transfer can complete is if all the data was sent i.e. all the TDs were sent and freed. */
524 if (ed -> ux_sim_host_ed_head_td != ed -> ux_sim_host_ed_tail_td)
525 {
526
527 /* Free all TDs associated with this transfer. Note that if this is a control transfer (which
528 means it must be IN), then this also gets rid of the STATUS phase, which is okay. Also note
529 that assumes that there is only one transfer occurring on this endpoint; if there were
530 multiple, then we'd erase that one's TDs as well. Luckily, with the way USBX is designed,
531 there should never be multiple transfers occurring simultaneously on a single endpoint
532 (tldr; data pointer for transfers is shared). */
533 head_td = ed -> ux_sim_host_ed_head_td;
534 while (head_td != ed -> ux_sim_host_ed_tail_td)
535 {
536
537 /* Free the TD that was used here. */
538 head_td -> ux_sim_host_td_status = UX_UNUSED;
539
540 /* Move to the next. */
541 head_td = head_td -> ux_sim_host_td_next_td;
542 }
543
544 /* Update the head and tail TD. */
545 ed -> ux_sim_host_ed_head_td = head_td;
546 ed -> ux_sim_host_ed_tail_td = head_td;
547 }
548
549 /* Set the completion code to no error. */
550 transfer_request -> ux_transfer_request_completion_code = UX_SUCCESS;
551
552 /* Set the transfer status to COMPLETED. */
553 transfer_request -> ux_transfer_request_status = UX_TRANSFER_STATUS_COMPLETED;
554
555 /* Is there a callback on the host? */
556 if (transfer_request -> ux_transfer_request_completion_function != UX_NULL)
557 transfer_request -> ux_transfer_request_completion_function(transfer_request);
558
559 /* Wake up the host side. */
560 _ux_host_semaphore_put(&transfer_request -> ux_transfer_request_semaphore);
561 }
562 }
563 }
564
565 /* Return successful completion. */
566 return(UX_SUCCESS);
567 }
568
569