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, &current_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