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_interrupt_endpoint_create              PORTABLE C      */
38 /*                                                           6.1.6        */
39 /*  AUTHOR                                                                */
40 /*                                                                        */
41 /*    Chaoqiong Xiao, Microsoft Corporation                               */
42 /*                                                                        */
43 /*  DESCRIPTION                                                           */
44 /*                                                                        */
45 /*     This function will create an interrupt endpoint. The interrupt     */
46 /*     endpoint has an interval of operation from 1 to 255. In OHCI, the  */
47 /*     hardware assisted interrupt is from 1 to 32.                       */
48 /*                                                                        */
49 /*     This routine will match the best interval for the OHCI hardware.   */
50 /*     It will also determine the best node to hook the endpoint based on */
51 /*     the load that already exists on the horizontal ED chain.           */
52 /*                                                                        */
53 /*     For the ones curious about this coding. The tricky part is to      */
54 /*     understand how the interrupt matrix is constructed. We have used   */
55 /*     eds with the skip bit on to build a frame of anchor eds. Each ED   */
56 /*     creates a node for an appropriate combination of interval          */
57 /*     frequency in the list.                                             */
58 /*                                                                        */
59 /*     After obtaining a pointer to the list with the lowest traffic, we  */
60 /*     traverse the list from the highest interval until we reach the     */
61 /*     interval required. At that node, we anchor our real ED to the node */
62 /*     and link the ED that was attached to the node to our ED.           */
63 /*                                                                        */
64 /*  INPUT                                                                 */
65 /*                                                                        */
66 /*    hcd_ohci                              Pointer to OHCI               */
67 /*    endpoint                              Pointer to endpoint           */
68 /*                                                                        */
69 /*  OUTPUT                                                                */
70 /*                                                                        */
71 /*    Completion Status                                                   */
72 /*                                                                        */
73 /*  CALLS                                                                 */
74 /*                                                                        */
75 /*    _ux_hcd_ohci_ed_obtain                Obtain OHCI ED                */
76 /*    _ux_hcd_ohci_least_traffic_list_get   Get least traffic list        */
77 /*    _ux_hcd_ohci_regular_td_obtain        Obtain OHCI regular TD        */
78 /*    _ux_utility_physical_address          Get physical address          */
79 /*    _ux_utility_virtual_address           Get virtual address           */
80 /*                                                                        */
81 /*  CALLED BY                                                             */
82 /*                                                                        */
83 /*    OHCI Controller Driver                                              */
84 /*                                                                        */
85 /*  RELEASE HISTORY                                                       */
86 /*                                                                        */
87 /*    DATE              NAME                      DESCRIPTION             */
88 /*                                                                        */
89 /*  05-19-2020     Chaoqiong Xiao           Initial Version 6.0           */
90 /*  09-30-2020     Chaoqiong Xiao           Modified comment(s),          */
91 /*                                            fixed physical and virtual  */
92 /*                                            address conversion,         */
93 /*                                            resulting in version 6.1    */
94 /*  04-02-2021     Chaoqiong Xiao           Modified comment(s),          */
95 /*                                            filled max transfer length, */
96 /*                                            resulting in version 6.1.6  */
97 /*                                                                        */
98 /**************************************************************************/
_ux_hcd_ohci_interrupt_endpoint_create(UX_HCD_OHCI * hcd_ohci,UX_ENDPOINT * endpoint)99 UINT  _ux_hcd_ohci_interrupt_endpoint_create(UX_HCD_OHCI *hcd_ohci, UX_ENDPOINT *endpoint)
100 {
101 
102 UX_DEVICE       *device;
103 UX_OHCI_ED      *ed;
104 UX_OHCI_ED      *ed_list;
105 UX_OHCI_ED      *next_ed;
106 UX_OHCI_TD      *td;
107 UINT            interval;
108 UINT            interval_index;
109 UINT            interval_ohci;
110 
111 
112     /* Obtain a ED for this new endpoint. This ED will live as long as the endpoint
113        is active and will be the container for the tds.  */
114     ed =  _ux_hcd_ohci_ed_obtain(hcd_ohci);
115     if (ed == UX_NULL)
116         return(UX_NO_ED_AVAILABLE);
117 
118     /* Obtain a dummy TD for terminating the ED transfer chain.  */
119     td =  _ux_hcd_ohci_regular_td_obtain(hcd_ohci);
120     if (td == UX_NULL)
121     {
122 
123         ed -> ux_ohci_ed_status =  UX_UNUSED;
124         return(UX_NO_TD_AVAILABLE);
125     }
126 
127     /* Attach the ED to the endpoint container.  */
128     endpoint -> ux_endpoint_ed =  (VOID *) ed;
129 
130     /* We need to take into account the nature of the HCD to define the max size
131        of any transfer in the transfer request.  */
132     endpoint -> ux_endpoint_transfer_request.ux_transfer_request_maximum_length =  UX_OHCI_MAX_PAYLOAD;
133 
134     /* Program the ED for subsequent transfers we need to set the following things:
135         1) Address of the device
136         2) endpoint number
137         3) speed
138         4) format of TD
139         5) maximum packet size */
140     device =                endpoint -> ux_endpoint_device;
141     ed -> ux_ohci_ed_dw0 =  device -> ux_device_address |
142                                 ((ULONG) (endpoint -> ux_endpoint_descriptor.bEndpointAddress & ~UX_ENDPOINT_DIRECTION)) << 7 |
143                                 ((ULONG) endpoint -> ux_endpoint_descriptor.wMaxPacketSize) << 16;
144 
145     if (device -> ux_device_speed == UX_LOW_SPEED_DEVICE)
146         ed -> ux_ohci_ed_dw0 |=  UX_OHCI_ED_LOW_SPEED;
147 
148     /* Hook the TD to both the tail and head of the ED.  */
149     ed -> ux_ohci_ed_tail_td =  _ux_utility_physical_address(td);
150     ed -> ux_ohci_ed_head_td =  _ux_utility_physical_address(td);
151 
152     /* Get the list index with the least traffic.  */
153     ed_list =  _ux_hcd_ohci_least_traffic_list_get(hcd_ohci);
154 
155     /* Get the interval for the endpoint and match it to a OHCI list. We match anything that
156        is > 32ms to the 32ms interval list. The 32ms list is list 0, 16ms list is 1 ...
157        the 1ms list is number 5.  */
158     interval =        endpoint -> ux_endpoint_descriptor.bInterval;
159     interval_index =  0x10;
160     interval_ohci =   1;
161 
162     /* Do a sanity check if the frequency is 0. That should not happen, so treat it as 1.  */
163     if (interval == 0)
164     {
165 
166         interval =  1;
167     }
168 
169     /* If the frequency is beyond the OHCI framework, make it the maximum of 32.  */
170     if (interval >= 32)
171     {
172 
173         interval_ohci =  0;
174     }
175     else
176     {
177 
178         /* We parse the interval from the high bits. This gives us the first power of 2 entry in the tree.  */
179         while (interval_index != 0)
180         {
181 
182             /* When we find the first bit of the interval the current value of interval_ohci is set to the the list index.  */
183             if (interval & interval_index)
184                 break;
185 
186             /* Go down the tree one entry.  */
187             interval_ohci++;
188 
189             /* And shift the bit of the device interval to check.  */
190             interval_index =  interval_index >> 1;
191         }
192     }
193 
194     /* Now we need to scan the list of eds from the lowest load entry until we reach the
195        appropriate interval node. The depth index is the interval OHCI value and the 1st
196        entry is pointed by the ED list entry.  */
197     while (interval_ohci--)
198     {
199 
200         ed_list =  _ux_utility_virtual_address(ed_list -> ux_ohci_ed_next_ed);
201         while (!(ed_list -> ux_ohci_ed_dw0 & UX_OHCI_ED_SKIP))
202             ed_list =  _ux_utility_virtual_address(ed_list -> ux_ohci_ed_next_ed);
203     }
204 
205     /* We found the node entry of the ED pointer that will be the anchor for this interrupt
206        endpoint. Now we attach this endpoint to the anchor and rebuild the chain.   */
207     next_ed =  ed_list -> ux_ohci_ed_next_ed;
208 
209     /* Check for end of tree which happens for devices with interval of 1. In this case
210        there might not be a next_ed.  */
211     if (next_ed != UX_NULL)
212     {
213         next_ed = _ux_utility_virtual_address(next_ed);
214         next_ed -> ux_ohci_ed_previous_ed =  ed;
215     }
216     ed -> ux_ohci_ed_next_ed =  _ux_utility_physical_address(next_ed);
217     ed -> ux_ohci_ed_previous_ed =  ed_list;
218     ed_list -> ux_ohci_ed_next_ed =  _ux_utility_physical_address(ed);
219 
220     /* Return successful completion.  */
221     return(UX_SUCCESS);
222 }
223 
224