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