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