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