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 /** OHCI Controller Driver */
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_hcd_ohci.h"
29 #include "ux_host_stack.h"
30
31
32 /**************************************************************************/
33 /* */
34 /* FUNCTION RELEASE */
35 /* */
36 /* _ux_hcd_ohci_done_queue_process PORTABLE C */
37 /* 6.1.12 */
38 /* AUTHOR */
39 /* */
40 /* Chaoqiong Xiao, Microsoft Corporation */
41 /* */
42 /* DESCRIPTION */
43 /* */
44 /* This function process the done queue that was posted by the */
45 /* controller during the last interrupt. The bad news is that the */
46 /* list of the TDs in the queue is in the opposite order of their */
47 /* actual completion. This FIFO made the OHCI design easier but the */
48 /* software has to work harder! */
49 /* */
50 /* INPUT */
51 /* */
52 /* hcd_ohci Pointer to OHCI HCD */
53 /* */
54 /* OUTPUT */
55 /* */
56 /* None */
57 /* */
58 /* CALLS */
59 /* */
60 /* (ux_transfer_request_completion_function) */
61 /* Transfer completion function */
62 /* _ux_hcd_ohci_endpoint_error_clear Clear endpoint error */
63 /* _ux_hcd_ohci_endpoint_reset Reset endpoint */
64 /* _ux_hcd_ohci_frame_number_get Get frame number */
65 /* _ux_hcd_ohci_next_td_clean Clean next TD */
66 /* _ux_hcd_ohci_register_read Read OHCI register */
67 /* _ux_hcd_ohci_register_write Write OHCI register */
68 /* _ux_host_semaphore_put Put producer semaphore */
69 /* _ux_utility_virtual_address Get virtual address */
70 /* */
71 /* CALLED BY */
72 /* */
73 /* OHCI Controller Driver */
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 /* resulting in version 6.1 */
82 /* 01-31-2022 Chaoqiong Xiao Modified comment(s), */
83 /* refined macros names, */
84 /* resulting in version 6.1.10 */
85 /* 07-29-2022 Chaoqiong Xiao Modified comment(s), */
86 /* fixed an addressing issue, */
87 /* resulting in version 6.1.12 */
88 /* */
89 /**************************************************************************/
_ux_hcd_ohci_done_queue_process(UX_HCD_OHCI * hcd_ohci)90 VOID _ux_hcd_ohci_done_queue_process(UX_HCD_OHCI *hcd_ohci)
91 {
92
93 UX_ENDPOINT *endpoint;
94 UX_OHCI_TD *td;
95 UX_OHCI_ISO_TD *iso_td;
96 UX_OHCI_TD *previous_td;
97 UX_OHCI_TD *next_td;
98 UINT td_error_code;
99 UX_TRANSFER *transfer_request;
100 ULONG ohci_register_interrupt;
101 ULONG transaction_length;
102 ULONG current_frame;
103
104
105 /* Get the first entry of the done queue. It may be NULL which means there is nothing to do!
106 The LSB of the TD pointer may be set by the OHCI controller to indicate that the Interrupt
107 status of the controller should be read. The TDs in the list are using physical addresses.
108 They need to be translated into virtual addresses. */
109 next_td = _ux_utility_virtual_address((VOID *) ((ULONG) hcd_ohci -> ux_hcd_ohci_done_head & 0xfffffff0));
110
111 /* Reset the last TD in the chain. */
112 previous_td = UX_NULL;
113 td = UX_NULL;
114
115 /* The TD we have now is the last in the FIFO, re-traverse the chain to get the TDs in the
116 chronological order. */
117 while (next_td != UX_NULL)
118 {
119
120 td = next_td;
121 next_td = _ux_utility_virtual_address(td -> ux_ohci_td_next_td);
122 td -> ux_ohci_td_next_td = _ux_utility_physical_address(previous_td);
123 previous_td = td;
124 }
125
126 /* Process each TD in their chronological order now. The TD pointer now has the first TD in the
127 list, all values are in virtual addresses. */
128 while (td != UX_NULL)
129 {
130
131 /* Get the pointer to the transfer request attached with this TD. */
132 transfer_request = td -> ux_ohci_td_transfer_request;
133
134 /* Get the endpoint associated with the transfer request */
135 endpoint = transfer_request -> ux_transfer_request_endpoint;
136
137 /* Retrieve the error code for this transaction. There are 3 types of errors:
138 transmission, sequence, system. */
139 td_error_code = td -> ux_ohci_td_dw0 >> UX_OHCI_TD_CC;
140
141 /* The handling of the isoch TD is slightly different. */
142 switch ((endpoint -> ux_endpoint_descriptor.bmAttributes) & UX_MASK_ENDPOINT_TYPE)
143 {
144
145 case UX_CONTROL_ENDPOINT:
146 case UX_BULK_ENDPOINT:
147 case UX_INTERRUPT_ENDPOINT:
148
149 switch (td_error_code)
150 {
151
152 case UX_OHCI_NO_ERROR:
153
154 /* No error on the transmission of this TD. Update the length of the transfer request. */
155 transfer_request -> ux_transfer_request_actual_length += td -> ux_ohci_td_length;
156
157 /* Check at the phase of the transfer, if this is a SETUP or DATA phases for a control
158 endpoint, we wait for the setup phase or any phase for other types of endpoints. */
159 if ((td -> ux_ohci_td_status & UX_OHCI_TD_SETUP_PHASE) || (td -> ux_ohci_td_status & UX_OHCI_TD_DATA_PHASE))
160 break;
161
162 /* Either this is a non control endpoint or it is the status phase and we are done. */
163 if (transfer_request -> ux_transfer_request_actual_length == transfer_request -> ux_transfer_request_requested_length)
164 {
165
166 transfer_request -> ux_transfer_request_completion_code = UX_SUCCESS;
167 if (transfer_request -> ux_transfer_request_completion_function != UX_NULL)
168 transfer_request -> ux_transfer_request_completion_function(transfer_request);
169 _ux_host_semaphore_put(&transfer_request -> ux_transfer_request_semaphore);
170 }
171 break;
172
173
174 case UX_OHCI_ERROR_DATA_UNDERRUN:
175
176 /* No error on the transmission of this TD but all data is not accounted for. This is typically
177 a short packet and OHCI report it as an error. This allows for the ED to be halted and further
178 attached TDs to be stopped. In this case, compute the correct received\sent length and
179 process the transfer request. */
180 transfer_request -> ux_transfer_request_actual_length += td -> ux_ohci_td_length - ((ULONG) td -> ux_ohci_td_be -
181 (ULONG) td -> ux_ohci_td_cbp) - 1;
182
183 /* Check at the phase of the transfer, if this is a SETUP or DATA phases for a control endpoint,
184 we wait for the setup phase or any phase for other types of endpoints. */
185 if ((td -> ux_ohci_td_status & UX_OHCI_TD_SETUP_PHASE) || (td -> ux_ohci_td_status & UX_OHCI_TD_DATA_PHASE))
186 break;
187
188 /* We need to reset the error bit in the ED. */
189 _ux_hcd_ohci_endpoint_error_clear(hcd_ohci, endpoint);
190
191 /* Either this is a non control endpoint or it is the status phase and we are done */
192 transfer_request -> ux_transfer_request_completion_code = UX_SUCCESS;
193 _ux_hcd_ohci_next_td_clean(td);
194 if (transfer_request -> ux_transfer_request_completion_function != UX_NULL)
195 transfer_request -> ux_transfer_request_completion_function(transfer_request);
196 _ux_host_semaphore_put(&transfer_request -> ux_transfer_request_semaphore);
197
198 break;
199
200
201 case UX_OHCI_ERROR_STALL:
202
203 /* A stall condition happens when the device refuses the requested command or when a
204 parameter in the command is wrong. We retire the transfer_request and mark the error. */
205 transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_STALLED;
206 _ux_hcd_ohci_next_td_clean(td);
207 if (transfer_request -> ux_transfer_request_completion_function != UX_NULL)
208 transfer_request -> ux_transfer_request_completion_function(transfer_request);
209 _ux_host_semaphore_put(&transfer_request -> ux_transfer_request_semaphore);
210
211 /* If trace is enabled, insert this event into the trace buffer. */
212 UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_STALLED, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0)
213
214 /* We need to reset the error bit in the ED. */
215 _ux_hcd_ohci_endpoint_reset(hcd_ohci, endpoint);
216 break;
217
218
219 case UX_OHCI_ERROR_DEVICE_NOT_RESPONDING:
220
221 /* A stall condition happens when the device does not respond to the request. This mostly
222 happens at the first GET_DESCRIPTOR after the port is enabled. This error has to be
223 picked up by the enumeration module to reset the port and retry the command. */
224 transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_NO_ANSWER;
225 _ux_hcd_ohci_next_td_clean(td);
226 if (transfer_request -> ux_transfer_request_completion_function != UX_NULL)
227 transfer_request -> ux_transfer_request_completion_function(transfer_request);
228 _ux_host_semaphore_put(&transfer_request -> ux_transfer_request_semaphore);
229
230 /* If trace is enabled, insert this event into the trace buffer. */
231 UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_NO_ANSWER, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0)
232
233 /* We need to reset the error bit in the ED */
234 _ux_hcd_ohci_endpoint_reset(hcd_ohci, endpoint);
235 break;
236
237
238 default:
239
240 /* Any other errors default to this section. The command has been repeated 3 times
241 and there is still a problem. The endpoint probably should be reset. */
242 transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_ERROR;
243 _ux_hcd_ohci_next_td_clean(td);
244 if (transfer_request -> ux_transfer_request_completion_function != UX_NULL)
245 transfer_request -> ux_transfer_request_completion_function(transfer_request);
246 _ux_host_semaphore_put(&transfer_request -> ux_transfer_request_semaphore);
247
248 /* If trace is enabled, insert this event into the trace buffer. */
249 UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_ERROR, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0)
250
251 /* We need to reset the error bit in the ED. */
252 _ux_hcd_ohci_endpoint_reset(hcd_ohci, endpoint);
253 break;
254 }
255 break;
256
257
258 case UX_ISOCHRONOUS_ENDPOINT:
259
260 /* The length of the transfer is in the PSW. */
261 iso_td = (UX_OHCI_ISO_TD *) td;
262
263 switch (td_error_code)
264 {
265
266 case UX_OHCI_NO_ERROR:
267
268 /* No error on the transmission of this TD. All data is accounted for. Check for the
269 last TD in the transfer request. If last, process the transfer request. The method
270 to calculate the length of the transaction is different between a IN and OUT
271 transactions. For a OUT, if the PSW is 0, then all data was transmitted. For an IN
272 the PSW indicates the number of bytes received. */
273 transaction_length = iso_td -> ux_ohci_iso_td_offset_psw[0] & UX_OHCI_ISO_TD_OFFSET;
274
275 if ((transfer_request -> ux_transfer_request_type & UX_REQUEST_DIRECTION) == UX_REQUEST_IN)
276 {
277
278 transfer_request -> ux_transfer_request_actual_length += transaction_length;
279 }
280 else
281 {
282
283 if (transaction_length == 0)
284 transfer_request -> ux_transfer_request_actual_length += iso_td -> ux_ohci_iso_td_length;
285 else
286 transfer_request -> ux_transfer_request_actual_length += transaction_length;
287 }
288
289 /* Check if the transfer request is complete or if this is an IN transaction and the length received
290 is less than the max packet size. */
291 if ((transfer_request -> ux_transfer_request_actual_length == transfer_request -> ux_transfer_request_requested_length) ||
292 (((transfer_request -> ux_transfer_request_type & UX_REQUEST_DIRECTION) == UX_REQUEST_IN) &&
293 (transaction_length < transfer_request -> ux_transfer_request_packet_length)))
294 {
295
296 transfer_request -> ux_transfer_request_completion_code = UX_SUCCESS;
297 if (transfer_request -> ux_transfer_request_completion_function != UX_NULL)
298 transfer_request -> ux_transfer_request_completion_function(transfer_request);
299 _ux_host_semaphore_put(&transfer_request -> ux_transfer_request_semaphore);
300 }
301 break;
302
303
304 case UX_OHCI_ERROR_DATA_OVERRRUN:
305
306 /* In this case, we have missed the frame for the isoch transfer. */
307 _ux_hcd_ohci_frame_number_get(hcd_ohci, ¤t_frame);
308 transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_MISSED_FRAME;
309 if (transfer_request -> ux_transfer_request_completion_function != UX_NULL)
310 transfer_request -> ux_transfer_request_completion_function(transfer_request);
311
312 /* If trace is enabled, insert this event into the trace buffer. */
313 UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_MISSED_FRAME, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0)
314
315 _ux_host_semaphore_put(&transfer_request -> ux_transfer_request_semaphore);
316 break;
317
318
319 default:
320
321 /* Some other error happened, in isoch transfer, there is not much we can do. */
322 transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_ERROR;
323 if (transfer_request -> ux_transfer_request_completion_function != UX_NULL)
324 transfer_request -> ux_transfer_request_completion_function(transfer_request);
325
326 /* If trace is enabled, insert this event into the trace buffer. */
327 UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_ERROR, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0)
328
329 _ux_host_semaphore_put(&transfer_request -> ux_transfer_request_semaphore);
330 break;
331 }
332 }
333
334 /* Free the TD that was just treated. */
335 td -> ux_ohci_td_status = UX_UNUSED;
336
337 /* And continue the TD loop. */
338 td = _ux_utility_virtual_address(td -> ux_ohci_td_next_td);
339 }
340
341 /* The OHCI controller is now ready to receive the next done queue. We need to
342 reawake the OHCI controller on the WDH signal. */
343 ohci_register_interrupt = _ux_hcd_ohci_register_read(hcd_ohci, OHCI_HC_INTERRUPT_ENABLE);
344 ohci_register_interrupt |= OHCI_HC_INT_WDH;
345 _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_INTERRUPT_ENABLE, ohci_register_interrupt);
346 }
347
348