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