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 Stack */
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_host_stack.h"
29
30
31 #if UX_MAX_DEVICES > 1
32 /**************************************************************************/
33 /* */
34 /* FUNCTION RELEASE */
35 /* */
36 /* _ux_host_stack_bandwidth_check PORTABLE C */
37 /* 6.1 */
38 /* AUTHOR */
39 /* */
40 /* Chaoqiong Xiao, Microsoft Corporation */
41 /* */
42 /* DESCRIPTION */
43 /* */
44 /* This function will check if there is enough bandwidth on the USB */
45 /* for the specified endpoint. The bandwidth requirement is calculated */
46 /* by the MaxPacketSize field of endpoint and the speed of the */
47 /* endpoint. If the device is on a 1.1 bus or it is a 1.1 device */
48 /* behind a 2.0 hub on a 2.0 bus, the device bandwidth must be */
49 /* multiplied by 8 on the 1.1 segment. */
50 /* */
51 /* This algorithm takes into account both TT bandwidth and HCD */
52 /* bandwidth. The TTs are attached to the device structure and not */
53 /* the hub structure in order to make the stack agnostic of the hub */
54 /* class. */
55 /* */
56 /* INPUT */
57 /* */
58 /* HCD Pointer to HCD */
59 /* endpoint Pointer to endpoint */
60 /* */
61 /* OUTPUT */
62 /* */
63 /* Completion Status */
64 /* */
65 /* CALLS */
66 /* */
67 /* None */
68 /* */
69 /* CALLED BY */
70 /* */
71 /* USBX Components */
72 /* */
73 /* RELEASE HISTORY */
74 /* */
75 /* DATE NAME DESCRIPTION */
76 /* */
77 /* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */
78 /* 09-30-2020 Chaoqiong Xiao Modified comment(s), */
79 /* optimized based on compile */
80 /* definitions, */
81 /* resulting in version 6.1 */
82 /* */
83 /**************************************************************************/
_ux_host_stack_bandwidth_check(UX_HCD * hcd,UX_ENDPOINT * endpoint)84 UINT _ux_host_stack_bandwidth_check(UX_HCD *hcd, UX_ENDPOINT *endpoint)
85 {
86
87 UX_DEVICE *device;
88 UX_DEVICE *parent_device;
89 USHORT hcd_bandwidth_claimed;
90 USHORT max_packet_size;
91 LONG packet_size;
92 USHORT tt_bandwidth_claimed = 0;
93 ULONG port_index;
94 ULONG port_map;
95 ULONG tt_index;
96 const UCHAR overheads[4][3] = {
97 /* LS FS HS */
98 {63, 45, 173}, /* Control */
99 { 0, 9, 38}, /* Isochronous */
100 { 0, 13, 55}, /* Bulk */
101 {19, 13, 55} /* Interrupt */
102 };
103
104 /* Get the pointer to the device. */
105 device = endpoint -> ux_endpoint_device;
106
107 /* Calculate the bandwidth. From USB spec.
108 *
109 * The frame unit consumed per byte is like follow:
110 * Bytes/FrameUnit FrameUnit/byte FrameUnit/byte
111 * (Overhead included) (HS baseline) (FS baseline)
112 * Low Speed 187.5 40 8
113 * Full Speed 1500 5 1
114 * High Speed 7500 1 1/5
115 *
116 * The overhead is like follow:
117 * Control Isochronous Bulk Interrupt
118 * bmAttribute (0) (1) (2) (3)
119 * Low Speed 63 -- -- 19
120 * Full Speed 45 9 13 13
121 * High Speed 173 38 55 55
122 *
123 * Worst case bit stuffing is calculated as 1.1667 (7/6) times the raw time.
124 */
125
126 /* Get maximum packet size. */
127 max_packet_size = endpoint -> ux_endpoint_descriptor.wMaxPacketSize & UX_MAX_PACKET_SIZE_MASK;
128
129 /* Rough time for possible Bit Stuffing. */
130 packet_size = (max_packet_size * 7 + 5) / 6;
131
132 /* Add overhead. */
133 packet_size += overheads[endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE][device -> ux_device_speed];
134 max_packet_size = (USHORT)packet_size;
135
136 /* Check for high-speed endpoint. */
137 if (device -> ux_device_speed == UX_HIGH_SPEED_DEVICE)
138 {
139
140 /* Get number of transactions. */
141 max_packet_size = (USHORT)(max_packet_size *
142 (((endpoint -> ux_endpoint_descriptor.wMaxPacketSize & UX_MAX_NUMBER_OF_TRANSACTIONS_MASK) >>
143 UX_MAX_NUMBER_OF_TRANSACTIONS_SHIFT) + 1));
144 }
145
146 /* Calculate the bandwidth claimed by this endpoint for the main bus. */
147 if (hcd -> ux_hcd_version != 0x200)
148 {
149
150 if (device -> ux_device_speed == UX_LOW_SPEED_DEVICE)
151 /* Low speed transfer takes 40x more units than high speed. */
152 hcd_bandwidth_claimed = (USHORT)(max_packet_size * 8 * 5);
153 else
154 {
155
156 if (device -> ux_device_speed == UX_FULL_SPEED_DEVICE)
157 /* Full speed transfer takes 5x more units than high speed. */
158 hcd_bandwidth_claimed = (USHORT)(max_packet_size * 5);
159 else
160 /* Use high speed timing as base for bus bandwidth calculation. */
161 hcd_bandwidth_claimed = (USHORT)max_packet_size;
162 }
163 }
164 else
165 {
166
167 hcd_bandwidth_claimed = (USHORT)max_packet_size;
168 if (device -> ux_device_speed == UX_LOW_SPEED_DEVICE)
169 /* Low speed transfer takes 8x more units than full speed. */
170 tt_bandwidth_claimed = (USHORT)(max_packet_size * 8);
171 else
172 /* Use full speed timing as base for TT bandwidth calculation. */
173 tt_bandwidth_claimed = (USHORT)max_packet_size;
174 }
175
176 /* Do we have enough on the bus for this new endpoint? */
177 if (hcd -> ux_hcd_available_bandwidth < hcd_bandwidth_claimed)
178 {
179
180 /* If trace is enabled, insert this event into the trace buffer. */
181 UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_NO_BANDWIDTH_AVAILABLE, endpoint, 0, 0, UX_TRACE_ERRORS, 0, 0)
182
183 /* Error trap. */
184 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ENUMERATOR, UX_NO_BANDWIDTH_AVAILABLE);
185
186 return(UX_NO_BANDWIDTH_AVAILABLE);
187 }
188
189 /* We need to take care of the case where the endpoint belongs to a USB 1.1
190 device that sits behind a 2.0 hub. We ignore cases where the device
191 is either high speed or the bus is 1.1. */
192 if ((device -> ux_device_speed == UX_HIGH_SPEED_DEVICE) || (hcd -> ux_hcd_version != 0x200))
193 {
194
195 /* The device is high speed, therefore no need for TT. */
196 return(UX_SUCCESS);
197 }
198
199 /* We have a 1.1 device, check if the parent is a 2.0 hub. */
200 parent_device = device -> ux_device_parent;
201 if (parent_device == UX_NULL)
202 {
203
204 /* We are at the root, this controller must support 1.1 then! */
205 return(UX_SUCCESS);
206 }
207
208 /* We get here when the parent is a hub. The problem occurs when the hub is itself
209 connected to a chain of hubs. We need to find the first 2.0 hub parent to this
210 chain to check the TT. We need to remember the port on which the first 1.1
211 device is hooked to. */
212 port_index = device -> ux_device_port_location - 1;
213
214 /* Scan the chain of hubs upward. */
215 while (parent_device != UX_NULL)
216 {
217
218 /* Determine if the device is high speed. */
219 if (parent_device -> ux_device_speed == UX_HIGH_SPEED_DEVICE)
220 {
221
222 /* The device is a high speed hub, find the TT that manages the port.
223 The first 1.1 device is connected to. First we calculate the port
224 mapping bit. */
225 port_map = (ULONG)(1 << port_index);
226
227 /* Parse all the TTs attached to the hub. */
228 for (tt_index = 0; tt_index < UX_MAX_TT; tt_index++)
229 {
230
231 /* Check if this TT owns the port where the device is attached. */
232 if ((parent_device -> ux_device_hub_tt[tt_index].ux_hub_tt_port_mapping & port_map) != 0)
233 {
234
235 /* We have found the port, check if the tt can give us the bandwidth
236 we want to claim. */
237 if (parent_device -> ux_device_hub_tt[tt_index].ux_hub_tt_max_bandwidth < tt_bandwidth_claimed)
238 {
239
240 /* If trace is enabled, insert this event into the trace buffer. */
241 UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_NO_BANDWIDTH_AVAILABLE, endpoint, 0, 0, UX_TRACE_ERRORS, 0, 0)
242
243 /* Error trap. */
244 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ENUMERATOR, UX_NO_BANDWIDTH_AVAILABLE);
245
246 return(UX_NO_BANDWIDTH_AVAILABLE);
247 }
248
249 else
250 return(UX_SUCCESS);
251 }
252 }
253
254 /* If trace is enabled, insert this event into the trace buffer. */
255 UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_NO_BANDWIDTH_AVAILABLE, endpoint, 0, 0, UX_TRACE_ERRORS, 0, 0)
256
257 /* Error trap. */
258 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ENUMERATOR, UX_NO_BANDWIDTH_AVAILABLE);
259
260 /* We should never get here !!!!! */
261 return(UX_NO_BANDWIDTH_AVAILABLE);
262 }
263
264 /* We now remember where this hub is located on the parent. */
265 port_index = parent_device -> ux_device_port_location - 1;
266
267 /* We go up one level in the hub chain. */
268 parent_device = parent_device -> ux_device_parent;
269 }
270
271 /* We get here when we have not found a 2.0 hub in the list and we got to the root port. */
272 return(UX_SUCCESS);
273 }
274 #endif /* #if UX_MAX_DEVICES > 1 */
275