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