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 /** Host Simulator Controller Driver */
18 /** */
19 /**************************************************************************/
20 /**************************************************************************/
21
22 #define UX_SOURCE_CODE
23
24
25 /* Include necessary system files. */
26
27 #include "ux_api.h"
28 #include "ux_hcd_sim_host.h"
29
30
31 /**************************************************************************/
32 /* */
33 /* FUNCTION RELEASE */
34 /* */
35 /* _ux_hcd_sim_host_interrupt_endpoint_create PORTABLE C */
36 /* 6.1 */
37 /* AUTHOR */
38 /* */
39 /* Chaoqiong Xiao, Microsoft Corporation */
40 /* */
41 /* DESCRIPTION */
42 /* */
43 /* This function will create an interrupt endpoint. The interrupt */
44 /* endpoint has an interval of operation from 1 to 255. The host */
45 /* has no hardware scheduler but we still build an interrupt tree */
46 /* similar to the host simulator controller. */
47 /* */
48 /* This routine will match the best interval for the host */
49 /* simulator. It will also determine the best node to hook the */
50 /* endpoint based on the load that already exists on the horizontal */
51 /* 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_sim_host Pointer to host controller */
67 /* endpoint Pointer to endpoint */
68 /* */
69 /* OUTPUT */
70 /* */
71 /* Completion Status */
72 /* */
73 /* CALLS */
74 /* */
75 /* _ux_hcd_sim_host_ed_obtain Obtain ED */
76 /* _ux_hcd_sim_host_regular_td_obtain Obtain regular TD */
77 /* _ux_hcd_sim_host_least_traffic_list_get Get least traffic list */
78 /* */
79 /* CALLED BY */
80 /* */
81 /* Host Simulator Controller Driver */
82 /* */
83 /* RELEASE HISTORY */
84 /* */
85 /* DATE NAME DESCRIPTION */
86 /* */
87 /* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */
88 /* 09-30-2020 Chaoqiong Xiao Modified comment(s), */
89 /* resulting in version 6.1 */
90 /* */
91 /**************************************************************************/
_ux_hcd_sim_host_interrupt_endpoint_create(UX_HCD_SIM_HOST * hcd_sim_host,UX_ENDPOINT * endpoint)92 UINT _ux_hcd_sim_host_interrupt_endpoint_create(UX_HCD_SIM_HOST *hcd_sim_host, UX_ENDPOINT *endpoint)
93 {
94
95 UX_HCD_SIM_HOST_ED *ed;
96 UX_HCD_SIM_HOST_ED *ed_list;
97 UX_HCD_SIM_HOST_ED *next_ed;
98 UX_HCD_SIM_HOST_TD *td;
99 UINT interval;
100 UINT interval_index;
101 UINT interval_sim_host;
102
103
104 /* Obtain a ED for this new endpoint. This ED will live as long as
105 the endpoint is active and will be the container for the TDs. */
106 ed = _ux_hcd_sim_host_ed_obtain(hcd_sim_host);
107 if (ed == UX_NULL)
108 return(UX_NO_ED_AVAILABLE);
109
110 /* Obtain a dummy TD for terminating the ED transfer chain. */
111 td = _ux_hcd_sim_host_regular_td_obtain(hcd_sim_host);
112 if (td == UX_NULL)
113 {
114
115 ed -> ux_sim_host_ed_status = UX_UNUSED;
116 return(UX_NO_TD_AVAILABLE);
117 }
118
119 /* Attach the ED to the endpoint container. */
120 endpoint -> ux_endpoint_ed = (VOID *)ed;
121
122 /* Now do the opposite, attach the ED container to the physical ED. */
123 ed -> ux_sim_host_ed_endpoint = endpoint;
124
125 /* Hook the TD to both the tail and head of the ED. */
126 ed -> ux_sim_host_ed_tail_td = td;
127 ed -> ux_sim_host_ed_head_td = td;
128
129 /* Get the list index with the least traffic. */
130 ed_list = _ux_hcd_sim_host_least_traffic_list_get(hcd_sim_host);
131
132 /* Get the interval for the endpoint and match it to a host simulator list. We match anything
133 that is > 32ms to the 32ms interval list, the 32ms list is list 0, 16ms list is 1...
134 the 1ms list is number 5. */
135 interval = endpoint -> ux_endpoint_descriptor.bInterval;
136 interval_index = 1;
137 interval_sim_host = 5;
138 if (interval >= 32)
139 {
140
141 interval_sim_host = 0;
142 }
143 else
144 {
145
146 while (interval_index < 32)
147 {
148
149 if (interval&interval_index)
150 interval_sim_host--;
151 interval_index = interval_index << 1;
152 }
153 }
154
155 /* Now we need to scan the list of eds from the lowest load entry until we reach
156 the appropriate interval node. The depth index is the interval_sim_host value
157 and the 1st entry is pointed by the ED list entry. */
158 while (interval_sim_host--)
159 {
160
161 ed_list = ed_list -> ux_sim_host_ed_next_ed;
162 while (!(ed_list -> ux_sim_host_ed_status & UX_HCD_SIM_HOST_ED_STATIC))
163 ed_list = ed_list -> ux_sim_host_ed_next_ed;
164 }
165
166 /* We found the node entry of the ED pointer that will be the anchor for this interrupt
167 endpoint. Now we attach this endpoint to the anchor and rebuild the chain . */
168 next_ed = ed_list -> ux_sim_host_ed_next_ed;
169 /* Note that if there is a crash here, it is most likely due to an invalid bInterval. */
170 next_ed -> ux_sim_host_ed_previous_ed = ed;
171 ed -> ux_sim_host_ed_next_ed = next_ed;
172 ed -> ux_sim_host_ed_previous_ed = ed_list;
173 ed_list -> ux_sim_host_ed_next_ed = ed;
174
175 /* There is activity in the periodic tree, the scheduler has to be active all the time. */
176 hcd_sim_host -> ux_hcd_sim_host_periodic_scheduler_active++;
177
178 /* Return successful completion. */
179 return(UX_SUCCESS);
180 }
181
182