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