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