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 /** EHCI 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_ehci.h"
30 #include "ux_host_stack.h"
31
32
33 /**************************************************************************/
34 /* */
35 /* FUNCTION RELEASE */
36 /* */
37 /* _ux_hcd_ehci_interrupt_endpoint_create PORTABLE C */
38 /* 6.1.11 */
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 EHCI, the */
47 /* hardware assisted interrupt is from 1 to 32. */
48 /* */
49 /* This routine will match the best interval for the EHCI 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 frequency */
57 /* 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_ehci Pointer to EHCI controller */
67 /* endpoint Pointer to endpoint */
68 /* */
69 /* OUTPUT */
70 /* */
71 /* Completion Status */
72 /* */
73 /* CALLS */
74 /* */
75 /* _ux_hcd_ehci_ed_obtain Obtain an ED */
76 /* _ux_hcd_ehci_least_traffic_list_get Get least traffic list */
77 /* _ux_hcd_ehci_poll_rate_entry_get Get anchor for poll rate */
78 /* _ux_utility_physical_address Get physical address */
79 /* _ux_host_mutex_on Get mutex */
80 /* _ux_host_mutex_off Put mutex */
81 /* _ux_hcd_ehci_periodic_descriptor_link Link/unlink descriptor */
82 /* */
83 /* CALLED BY */
84 /* */
85 /* EHCI Controller Driver */
86 /* */
87 /* RELEASE HISTORY */
88 /* */
89 /* DATE NAME DESCRIPTION */
90 /* */
91 /* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */
92 /* 09-30-2020 Chaoqiong Xiao Modified comment(s), */
93 /* optimized based on compile */
94 /* definitions, */
95 /* resulting in version 6.1 */
96 /* 11-09-2020 Chaoqiong Xiao Modified comment(s), */
97 /* fixed compile warnings, */
98 /* resulting in version 6.1.2 */
99 /* 04-02-2021 Chaoqiong Xiao Modified comment(s), */
100 /* fixed compile issues with */
101 /* some macro options, */
102 /* filled max transfer length, */
103 /* resulting in version 6.1.6 */
104 /* 04-25-2022 Chaoqiong Xiao Modified comment(s), */
105 /* fixed standalone compile, */
106 /* resulting in version 6.1.11 */
107 /* */
108 /**************************************************************************/
_ux_hcd_ehci_interrupt_endpoint_create(UX_HCD_EHCI * hcd_ehci,UX_ENDPOINT * endpoint)109 UINT _ux_hcd_ehci_interrupt_endpoint_create(UX_HCD_EHCI *hcd_ehci, UX_ENDPOINT *endpoint)
110 {
111
112 UX_DEVICE *device;
113 UX_EHCI_ED *ed;
114 UX_EHCI_ED *ed_list;
115 UX_EHCI_ED *ed_anchor;
116 UINT interval;
117 UINT poll_depth;
118 ULONG max_packet_size;
119 ULONG num_transaction;
120 ULONG microframe_load[8];
121 #if defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE)
122 ULONG microframe_ssplit_count[8];
123 UINT csplit_count;
124 ULONG cmask;
125 #else
126 #define microframe_ssplit_count UX_NULL
127 #endif
128 UX_EHCI_PERIODIC_LINK_POINTER lp;
129 UINT i;
130
131
132 /* Get the pointer to the device. */
133 device = endpoint -> ux_endpoint_device;
134
135 #if !defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE)
136
137 /* Only high speed transfer supported without split transfer. */
138 if (device -> ux_device_speed != UX_HIGH_SPEED_DEVICE)
139 return(UX_FUNCTION_NOT_SUPPORTED);
140 #endif
141
142 /* We need to take into account the nature of the HCD to define the max size
143 of any transfer in the transfer request. */
144 endpoint -> ux_endpoint_transfer_request.ux_transfer_request_maximum_length = UX_EHCI_MAX_PAYLOAD;
145
146 /* Obtain a ED for this new endpoint. This ED will live as long as the endpoint is
147 active and will be the container for the tds. */
148 ed = _ux_hcd_ehci_ed_obtain(hcd_ehci);
149 if (ed == UX_NULL)
150 return(UX_NO_ED_AVAILABLE);
151
152 /* Attach the ED to the endpoint container. */
153 endpoint -> ux_endpoint_ed = (VOID *) ed;
154
155 /* Now do the opposite, attach the ED container to the physical ED. */
156 ed -> REF_AS.INTR.ux_ehci_ed_endpoint = endpoint;
157
158 /* Set the default MPS Capability info in the ED. */
159 max_packet_size = endpoint -> ux_endpoint_descriptor.wMaxPacketSize & UX_MAX_PACKET_SIZE_MASK;
160 ed -> ux_ehci_ed_cap0 = max_packet_size << UX_EHCI_QH_MPS_LOC;
161
162 /* Set the device address. */
163 ed -> ux_ehci_ed_cap0 |= device -> ux_device_address;
164
165 /* Add the endpoint address. */
166 ed -> ux_ehci_ed_cap0 |= (endpoint -> ux_endpoint_descriptor.bEndpointAddress & ~UX_ENDPOINT_DIRECTION) << UX_EHCI_QH_ED_AD_LOC;
167
168 /* Set the High Bandwidth Pipe Multiplier to number transactions. */
169 num_transaction = (endpoint -> ux_endpoint_descriptor.wMaxPacketSize & UX_MAX_NUMBER_OF_TRANSACTIONS_MASK) >> UX_MAX_NUMBER_OF_TRANSACTIONS_SHIFT;
170 if (num_transaction < 3)
171 num_transaction ++;
172 ed -> ux_ehci_ed_cap1 |= (num_transaction << UX_EHCI_QH_HBPM_LOC);
173
174 /* Set the device speed for full and low speed devices behind a HUB. The HUB address and the
175 port index must be stored in the endpoint. For low/full speed devices, the C-mask field must be set. */
176 switch (device -> ux_device_speed)
177 {
178
179 case UX_HIGH_SPEED_DEVICE:
180 ed -> ux_ehci_ed_cap0 |= UX_EHCI_QH_HIGH_SPEED;
181 break;
182
183 case UX_LOW_SPEED_DEVICE:
184 ed -> ux_ehci_ed_cap0 |= UX_EHCI_QH_LOW_SPEED;
185
186 /* Fall through. */
187 default:
188
189 #if UX_MAX_DEVICES > 1
190 /* The device must be on a hub for this code to execute. We still do a sanity check. */
191 if (device -> ux_device_parent != UX_NULL)
192 {
193
194 /* Store the parent hub device address. */
195 ed -> ux_ehci_ed_cap1 |= device -> ux_device_parent -> ux_device_address << UX_EHCI_QH_HUB_ADDR_LOC;
196
197 /* And the port index onto which this device is attached. */
198 ed -> ux_ehci_ed_cap1 |= device -> ux_device_port_location << UX_EHCI_QH_PORT_NUMBER_LOC;
199 }
200 #endif
201 break;
202 }
203
204 /* Get the interval for the endpoint and match it to a EHCI list.
205 We match anything that is > 32ms to the 32ms interval layer.
206 The 32ms list is layer 5, 16ms list is 4 ... the 1ms list is depth 0. */
207 interval = endpoint -> ux_endpoint_descriptor.bInterval;
208 #if defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE)
209 if (device -> ux_device_speed != UX_HIGH_SPEED_DEVICE)
210 {
211
212 /* Convert from ms to 2^i. */
213 for (i = 0; i < 16; i ++)
214 {
215 if (interval <= (1u << i))
216 break;
217 }
218
219 /* Index 0 for 1ms (interval 4). */
220 interval = 4 + i;
221 }
222 else
223 #endif
224 {
225 /* High-speed interval is 2^(interval - 1) * 1/2^3. */
226 if (interval <= 4)
227 i = 0;
228 else
229 i = interval - 4;
230
231 /* Index 0 for 1ms. */
232 }
233
234 /* Match > 32ms to 32ms list. */
235 /* Poll depth deeper, interval smaller. */
236 if (i > 5)
237 poll_depth = 0;
238 else
239 poll_depth = 5 - i;
240
241 /* Keep interval < 1ms for micro-frame calculation. */
242 /* Make it index steps to move. */
243 if (interval > 0)
244 {
245 interval --;
246 interval &= 0x3;
247 }
248 interval = (1u << interval); /* 1 (1/8ms), 2, 4, 8 (1ms) */
249
250 /* We are now updating the periodic list. */
251 _ux_host_mutex_on(&hcd_ehci -> ux_hcd_ehci_periodic_mutex);
252
253 /* Get the list index with the least traffic. */
254 ed_list = _ux_hcd_ehci_least_traffic_list_get(hcd_ehci, microframe_load, microframe_ssplit_count);
255
256 /* Now we need to scan the list of eds from the lowest load entry until we reach the
257 appropriate interval node. The depth index is the interval EHCI value and the
258 1st entry is pointed by the ED list entry. */
259 ed_anchor = _ux_hcd_ehci_poll_rate_entry_get(hcd_ehci, ed_list, poll_depth);
260
261 /* Save anchor pointer for interrupt ED. */
262 ed -> REF_AS.INTR.ux_ehci_ed_anchor = ed_anchor;
263
264 /* Calculate packet size with num transactions. */
265 max_packet_size *= num_transaction;
266
267 /* Go through the transaction loads for start
268 index of micro-frame. */
269 for (i = 0; i < interval; i ++)
270 {
271
272 /* Skip if load too much. */
273 if (microframe_load[i] + max_packet_size > UX_MAX_BYTES_PER_MICROFRAME_HS)
274 continue;
275
276 #if defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE)
277 if (device -> ux_device_speed != UX_HIGH_SPEED_DEVICE)
278 {
279
280 /* Skip Y6 since host must not use it. */
281 if (i == 6)
282 continue;
283
284 /* Skip if start split count over 16 split. */
285 if (microframe_ssplit_count[i] >= 16)
286 continue;
287 }
288 #endif
289
290 /* Use the load. */
291 break;
292 }
293
294 /* Sanity check, bandwidth checked before endpoint creation so there should
295 not be error but we check it any way. */
296 if (i >= interval)
297 {
298 _ux_host_mutex_off(&hcd_ehci -> ux_hcd_ehci_periodic_mutex);
299 ed -> ux_ehci_ed_status = UX_UNUSED;
300 return(UX_NO_BANDWIDTH_AVAILABLE);
301 }
302
303 /* Now start microframe index is calculated, build masks. */
304
305 /* It's interval is larger than 1ms, use any of micro-frame. */
306 if (interval >= 8)
307 {
308
309 /* Interrupt schedule. */
310 ed -> ux_ehci_ed_cap1 |= (UX_EHCI_SMASK_0 << i);
311
312 #if defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE)
313 /* For split transfer, complete split should be scheduled. */
314 if (device -> ux_device_speed != UX_HIGH_SPEED_DEVICE)
315 {
316
317 /* Interrupt IN/OUT:
318 must schedule a complete-split transaction in each of the two
319 microframes following the first microframe in which the
320 full/low speed transaction is budgeted. An additional
321 complete-split must also be scheduled in the third following
322 microframe unless the full/low speed transaction was budgeted
323 to start in Y6. */
324
325 if (i == 5)
326 {
327
328 /* Budgeted in Y6, Follow two (C7, C0). */
329 cmask = UX_EHCI_CMASK_INT_Y5;
330 csplit_count = 2;
331 }
332 else
333 {
334
335 /* Follow three. */
336 cmask = UX_EHCI_CMASK_INT_Y0 << i;
337 if (i > 3)
338 {
339 cmask |= cmask >> 8;
340 cmask &= UX_EHCI_CMASK_MASK;
341 }
342 csplit_count = 3;
343 }
344
345 /* Reserve count for SSplit (Max 16). */
346 ed_anchor ->REF_AS.ANCHOR.ux_ehci_ed_microframe_ssplit_count[i] ++;
347
348 /* Reserve packet bytes for microframe load. */
349 if (endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION)
350 {
351
352 /* Reserve load for CSplit. */
353 ed_anchor -> REF_AS.ANCHOR.ux_ehci_ed_microframe_load[(i + 2)&7] = (USHORT)(ed_anchor -> REF_AS.ANCHOR.ux_ehci_ed_microframe_load[(i + 2)&7] + max_packet_size);
354 ed_anchor -> REF_AS.ANCHOR.ux_ehci_ed_microframe_load[(i + 3)&7] = (USHORT)(ed_anchor -> REF_AS.ANCHOR.ux_ehci_ed_microframe_load[(i + 3)&7] + max_packet_size);
355
356 /* Need additional CSplit. */
357 if (csplit_count > 2)
358 ed_anchor -> REF_AS.ANCHOR.ux_ehci_ed_microframe_load[(i + 4)&7] = (USHORT)(ed_anchor -> REF_AS.ANCHOR.ux_ehci_ed_microframe_load[(i + 4)&7] + max_packet_size);
359 }
360 else
361 {
362
363 /* Reserve load for SSplit. */
364 ed_anchor -> REF_AS.ANCHOR.ux_ehci_ed_microframe_load[i] = (USHORT)(ed_anchor -> REF_AS.ANCHOR.ux_ehci_ed_microframe_load[i] + max_packet_size);
365 }
366
367 /* Update schedule masks. */
368 ed -> ux_ehci_ed_cap1 |= cmask;
369 }
370 else
371 #endif
372 {
373 /* Update anchor micro-frame load. */
374 ed_anchor -> REF_AS.ANCHOR.ux_ehci_ed_microframe_load[i] = (USHORT)(ed_anchor -> REF_AS.ANCHOR.ux_ehci_ed_microframe_load[i] + max_packet_size);
375 }
376 }
377 else
378 {
379
380 /* It must be high speed high bandwidth one. */
381 switch(interval)
382 {
383 case 1:
384 ed -> ux_ehci_ed_cap1 |= UX_EHCI_SMASK_INTERVAL_1;
385 break;
386 case 2:
387 ed -> ux_ehci_ed_cap1 |= UX_EHCI_SMASK_INTERVAL_2 << i;
388 break;
389 default: /* 4, interval 3, 1/2ms */
390 ed -> ux_ehci_ed_cap1 |= UX_EHCI_SMASK_INTERVAL_3 << i;
391 break;
392 }
393
394 /* Update anchor micro-frame loads. */
395 for (; i < 8; i += interval)
396 ed_anchor -> REF_AS.ANCHOR.ux_ehci_ed_microframe_load[i] = (USHORT)(ed_anchor -> REF_AS.ANCHOR.ux_ehci_ed_microframe_load[i] + max_packet_size);
397 }
398
399 /* We found the node entry of the ED pointer that will be the anchor for this interrupt
400 endpoint. Now we attach this endpoint to the anchor and rebuild the chain. */
401
402 /* Physical LP, with Typ QH, clear T. */
403 lp.void_ptr = _ux_utility_physical_address(ed);
404 lp.value |= UX_EHCI_TYP_QH;
405
406 /* Save previous LP: to anchor. */
407 ed -> ux_ehci_ed_previous_ed = ed_anchor;
408
409 /* Link the QH at next to anchor. */
410 _ux_hcd_ehci_periodic_descriptor_link(ed_anchor, lp.void_ptr, ed, ed_anchor -> ux_ehci_ed_queue_head);
411
412 /* Insert ED to interrupt scan list for fast done queue scan. */
413 ed -> ux_ehci_ed_next_ed = hcd_ehci -> ux_hcd_ehci_interrupt_ed_list;
414 hcd_ehci -> ux_hcd_ehci_interrupt_ed_list = ed;
415
416 /* Release the periodic list. */
417 _ux_host_mutex_off(&hcd_ehci -> ux_hcd_ehci_periodic_mutex);
418
419 /* Return successful completion. */
420 return(UX_SUCCESS);
421 }
422