1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
|
/***************************************************************************
* Copyright (c) 2024 Microsoft Corporation
* Copyright (c) 2026-present Eclipse ThreadX contributors
*
* This program and the accompanying materials are made available under the
* terms of the MIT License which is available at
* https://opensource.org/licenses/MIT.
*
* SPDX-License-Identifier: MIT
**************************************************************************/
/**************************************************************************/
/**************************************************************************/
/** */
/** USBX Component */
/** */
/** EHCI Controller Driver */
/** */
/**************************************************************************/
/**************************************************************************/
/* Include necessary system files. */
#define UX_SOURCE_CODE
#include "ux_api.h"
#include "ux_hcd_ehci.h"
#include "ux_host_stack.h"
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
/* */
/* _ux_hcd_ehci_asynch_td_process PORTABLE C */
/* 6.1.10 */
/* AUTHOR */
/* */
/* Chaoqiong Xiao, Microsoft Corporation */
/* */
/* DESCRIPTION */
/* */
/* This function processes the isochronous, periodic and asynchronous */
/* lists in search for transfers that occurred in the past */
/* (micro-)frame. */
/* */
/* INPUT */
/* */
/* ed Pointer to ED */
/* td Pointer to TD */
/* */
/* OUTPUT */
/* */
/* UX_EHCI_TD * Pointer to TD */
/* */
/* CALLS */
/* */
/* (ux_transfer_request_completion_function) Completion function */
/* _ux_hcd_ehci_ed_clean Clean ED */
/* _ux_host_semaphore_put Put semaphore */
/* _ux_utility_virtual_address Get virtual address */
/* */
/* CALLED BY */
/* */
/* EHCI Controller Driver */
/* */
/**************************************************************************/
UX_EHCI_TD *_ux_hcd_ehci_asynch_td_process(UX_EHCI_ED *ed, UX_EHCI_TD *td)
{
UX_TRANSFER *transfer_request;
ULONG td_residual_length;
UINT td_error;
UX_EHCI_TD *next_td;
ULONG td_element;
ULONG pid;
/* If the TD is still active, we know that the transfer has not taken place yet. In this case,
the TDs following this one do not need to be process and we return a NULL TD. */
if (td -> ux_ehci_td_control & UX_EHCI_TD_ACTIVE)
return(UX_NULL);
/* We need the transfer request associated with this TD. */
transfer_request = td -> ux_ehci_td_transfer_request;
/* We have a non active TD, meaning the TD was processed. We should explore the error code and
translate it into a generic USBX code. */
td_error = UX_SUCCESS;
/* Check if the TD was halted due to a major error. Note that the error status may contain an error
but unless the Halt bit is set, it is not fatal. */
if (td -> ux_ehci_td_control & UX_EHCI_TD_HALTED)
{
/* Default to STALL. */
td_error = UX_TRANSFER_STALLED;
/* What else could it be ? Buffer underrun/overrun ? */
if (td -> ux_ehci_td_control & UX_EHCI_TD_DATA_BUFFER_ERROR)
td_error = UX_TRANSFER_ERROR;
/* Babble ? */
if (td -> ux_ehci_td_control & UX_EHCI_TD_BABBLE_DETECTED)
td_error = UX_TRANSFER_ERROR;
/* Timeout, CRD, Bad PID ... ? */
if (td -> ux_ehci_td_control & UX_EHCI_TD_TRANSACTION_ERROR)
td_error = UX_TRANSFER_NO_ANSWER;
}
/* If there is an error, we should terminate this transfer and clean things. */
if (td_error != UX_SUCCESS)
{
/* Update the transfer code. */
transfer_request -> ux_transfer_request_completion_code = td_error;
/* Clean the link. */
_ux_hcd_ehci_ed_clean(ed);
/* Free the TD that was just treated. */
td -> ux_ehci_td_status = UX_UNUSED;
/* We may do a call back. */
if (transfer_request -> ux_transfer_request_completion_function != UX_NULL)
transfer_request -> ux_transfer_request_completion_function(transfer_request);
/* Notify the application for debugging purposes. */
_ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, td_error);
/* If trace is enabled, insert this event into the trace buffer. */
UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, td_error, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0)
/* Wake up the semaphore for this request. */
_ux_host_semaphore_put(&transfer_request -> ux_transfer_request_semaphore);
/* Nothing else to be processed in this queue. */
return(UX_NULL);
}
/* Update the length of this transaction if the PID is IN or OUT. */
pid = td -> ux_ehci_td_control & UX_EHCI_PID_MASK;
td_residual_length = 0;
if ((pid == UX_EHCI_PID_OUT) || (pid == UX_EHCI_PID_IN))
{
td_residual_length = (td -> ux_ehci_td_control >> UX_EHCI_TD_LG_LOC) & UX_EHCI_TD_LG_MASK;
transfer_request -> ux_transfer_request_actual_length += td -> ux_ehci_td_length - td_residual_length;
}
/* We get here when there is no error on the transfer. It may be that this transfer is not the last
one in the link and therefore we process the length received but do not complete the transfer request. */
if (td -> ux_ehci_td_control & UX_EHCI_TD_IOC)
{
/* Check if this transaction is complete or a short packet occurred, in both case, complete the
transaction. In the case of a control endpoint in STATUS phase we complete regardless. */
if ((td_residual_length != 0) ||
(transfer_request -> ux_transfer_request_actual_length == transfer_request -> ux_transfer_request_requested_length) ||
(td -> ux_ehci_td_phase & UX_EHCI_TD_STATUS_PHASE))
{
/* Update the transfer code. */
transfer_request -> ux_transfer_request_completion_code = UX_SUCCESS;
/* Clean the link. */
_ux_hcd_ehci_ed_clean(ed);
/* Free the TD that was just treated. */
td -> ux_ehci_td_status = UX_UNUSED;
/* We may do a call back. */
if (transfer_request -> ux_transfer_request_completion_function != UX_NULL)
transfer_request -> ux_transfer_request_completion_function(transfer_request);
/* Wake up the semaphore for this request. */
_ux_host_semaphore_put(&transfer_request -> ux_transfer_request_semaphore);
/* Nothing else to be processed in this queue */
return(UX_NULL);
}
}
/* Get the next TD attached to this TD. */
td_element = (ULONG) td -> ux_ehci_td_link_pointer;
td_element &= ~UX_EHCI_TD_T;
next_td = _ux_utility_virtual_address((VOID *) td_element);
/* Free the TD that was just treated. */
td -> ux_ehci_td_status = UX_UNUSED;
/* This TD is now the first TD. */
ed -> ux_ehci_ed_first_td = next_td;
/* We get here when we have reached a non completion of a request transfer
we need to return the next TD in the linked list. */
return(next_td);
}
|