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