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 /** HUB Class */
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_class_hub.h"
29 #include "ux_host_stack.h"
30
31
32 /**************************************************************************/
33 /* */
34 /* FUNCTION RELEASE */
35 /* */
36 /* _ux_host_class_hub_port_change_connection_process PORTABLE C */
37 /* 6.1.12 */
38 /* AUTHOR */
39 /* */
40 /* Chaoqiong Xiao, Microsoft Corporation */
41 /* */
42 /* DESCRIPTION */
43 /* */
44 /* This function will process a connection change on the port. This */
45 /* indicates that a device has been attached to the port. */
46 /* */
47 /* INPUT */
48 /* */
49 /* hub Pointer to HUB class */
50 /* port Port number */
51 /* port_status Port status */
52 /* */
53 /* OUTPUT */
54 /* */
55 /* None */
56 /* */
57 /* CALLS */
58 /* */
59 /* _ux_host_class_hub_feature Set HUB feature */
60 /* _ux_host_class_hub_port_reset Reset port */
61 /* _ux_host_class_hub_status_get Get status */
62 /* _ux_host_stack_new_device_create Create new device */
63 /* _ux_host_stack_device_remove Remove device */
64 /* _ux_utility_delay_ms Thread sleep */
65 /* */
66 /* CALLED BY */
67 /* */
68 /* HUB Class */
69 /* */
70 /* RELEASE HISTORY */
71 /* */
72 /* DATE NAME DESCRIPTION */
73 /* */
74 /* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */
75 /* 09-30-2020 Chaoqiong Xiao Modified comment(s), */
76 /* optimized based on compile */
77 /* definitions, */
78 /* resulting in version 6.1 */
79 /* 02-02-2021 Chaoqiong Xiao Modified comment(s), */
80 /* handled more fail cases, */
81 /* updated internal call, */
82 /* added notification for */
83 /* device connection, */
84 /* added disconnection check */
85 /* in enumeration retries, */
86 /* resulting in version 6.1.4 */
87 /* 07-29-2022 Chaoqiong Xiao Modified comment(s), */
88 /* added standalone support, */
89 /* resulting in version 6.1.12 */
90 /* */
91 /**************************************************************************/
_ux_host_class_hub_port_change_connection_process(UX_HOST_CLASS_HUB * hub,UINT port,UINT port_status)92 VOID _ux_host_class_hub_port_change_connection_process(UX_HOST_CLASS_HUB *hub, UINT port, UINT port_status)
93 {
94
95 UX_HCD *hcd;
96 #if !defined(UX_HOST_STANDALONE)
97 UX_DEVICE *device = UX_NULL;
98 UINT device_speed;
99 UINT device_enumeration_retry;
100 USHORT port_power;
101 UINT status;
102 USHORT local_port_status;
103 USHORT local_port_change;
104 #endif
105
106 /* If trace is enabled, insert this event into the trace buffer. */
107 UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_HUB_PORT_CHANGE_CONNECTION_PROCESS, hub, port, port_status, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0)
108
109 /* Get the HCD used by this instance. */
110 hcd = UX_DEVICE_HCD_GET(hub -> ux_host_class_hub_device);
111
112 /* If there is a device attached on this HUB, there is a new device and it should
113 be enumerated. */
114 if (port_status & UX_HOST_CLASS_HUB_PORT_STATUS_CONNECTION)
115 {
116
117 /* Check if there was a previous device already attached on this port. This may happen when a device
118 disconnects and reconnect very quickly before the hub has a chance to poll the port state. In this
119 case we do a device removal before doing a device connection. */
120 if (hub -> ux_host_class_hub_port_state & (UINT)(1 << port))
121 {
122
123 /* There was a device attached previously. Perform a removal. */
124 _ux_host_stack_device_remove(hcd, hub -> ux_host_class_hub_device, port);
125
126 }
127 else
128
129 /* Mark device connection. */
130 hub -> ux_host_class_hub_port_state |= (UINT)(1 << port);
131
132 #if defined(UX_HOST_STANDALONE)
133 /* Port operations are done outside. */
134 #else
135
136 /* Tell the hub to clear the change bit for this port so that we do
137 not process the same change event again. */
138 _ux_host_class_hub_feature(hub, port, UX_CLEAR_FEATURE, UX_HOST_CLASS_HUB_C_PORT_CONNECTION);
139
140 /* Some devices are known to fail on the first try. */
141 for (device_enumeration_retry = 0; device_enumeration_retry < UX_HOST_CLASS_HUB_ENUMERATION_RETRY; device_enumeration_retry++)
142 {
143
144 /* Wait for debounce. */
145 _ux_utility_delay_ms(UX_HOST_CLASS_HUB_ENUMERATION_DEBOUNCE_DELAY);
146
147 /* The port must be reset. */
148 status = _ux_host_class_hub_port_reset(hub, port);
149 if (status != UX_SUCCESS)
150 return;
151
152 /* Reset succeeded, so perform a new port status after reset to force speed reevaluation. */
153 status = _ux_host_class_hub_status_get(hub, port, &local_port_status, &local_port_change);
154 if (status != UX_SUCCESS)
155 return;
156
157 /* Check if device is still connected. */
158 if ((local_port_status & UX_HOST_CLASS_HUB_PORT_STATUS_CONNECTION) == 0)
159 return;
160
161 /* Device connected. Get the device speed. */
162 if (local_port_status & UX_HOST_CLASS_HUB_PORT_STATUS_LOW_SPEED)
163 device_speed = UX_LOW_SPEED_DEVICE;
164 else
165 {
166
167 if (local_port_status & UX_HOST_CLASS_HUB_PORT_STATUS_HIGH_SPEED)
168 device_speed = UX_HIGH_SPEED_DEVICE;
169 else
170 device_speed = UX_FULL_SPEED_DEVICE;
171 }
172
173 /* Decide how much power this device can draw. This depends if the HUB is self
174 powered or bus powered. */
175 if (hub -> ux_host_class_hub_device -> ux_device_power_source == UX_DEVICE_BUS_POWERED)
176
177 /* Hub is bus powered. */
178 port_power = UX_MAX_BUS_POWER;
179 else
180
181 /* Hub is self powered. */
182 port_power = UX_MAX_SELF_POWER;
183
184 /* Wait for reset recovery. */
185 _ux_utility_delay_ms(UX_HOST_CLASS_HUB_ENUMERATION_RESET_RECOVERY_DELAY);
186
187 /* Perform the device creation. */
188 status = _ux_host_stack_new_device_create(UX_DEVICE_HCD_GET(hub -> ux_host_class_hub_device),
189 hub -> ux_host_class_hub_device,
190 port, device_speed, port_power,
191 &device);
192
193 /* Check return status. */
194 if (status == UX_SUCCESS)
195 {
196
197 /* Successful device creation. */
198
199 /* If the device instance is ready, notify application for unconfigured device. */
200 if (_ux_system_host -> ux_system_host_change_function)
201 {
202 _ux_system_host -> ux_system_host_change_function(UX_DEVICE_CONNECTION, UX_NULL, (VOID*)device);
203 }
204
205 /* Just return. */
206 return;
207 }
208 else
209 {
210
211 /* No retry if there are too many devices. */
212 if (status == UX_TOO_MANY_DEVICES)
213 break;
214
215 /* No retry if there is no class found. */
216 if (status == UX_NO_CLASS_MATCH)
217 break;
218
219 if (device_enumeration_retry < UX_HOST_CLASS_HUB_ENUMERATION_RETRY - 1)
220 {
221
222 /* Simulate remove to free allocated resources before retry. */
223 _ux_host_stack_device_remove(hcd, hub -> ux_host_class_hub_device, port);
224
225 /* Wait for a while. */
226 _ux_utility_delay_ms(UX_HOST_CLASS_HUB_ENUMERATION_RETRY_DELAY);
227 }
228 }
229 }
230
231 /* If we get here, the device did not enumerate completely.
232 The device is still attached to the hub and therefore there is a
233 physical connection with a unenumerated device. */
234
235 /* If the device instance is ready, notify application for unconfigured device. */
236 if (_ux_system_host -> ux_system_host_change_function)
237 {
238 _ux_system_host -> ux_system_host_change_function(UX_DEVICE_CONNECTION, UX_NULL, (VOID*)device);
239 }
240
241 /* Error trap. */
242 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ROOT_HUB, UX_DEVICE_ENUMERATION_FAILURE);
243
244 /* If trace is enabled, insert this event into the trace buffer. */
245 UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DEVICE_ENUMERATION_FAILURE, port, 0, 0, UX_TRACE_ERRORS, 0, 0)
246 #endif
247 }
248 else
249 {
250
251 /* Check if there was a no previous device attached on this port. */
252 if ((hub -> ux_host_class_hub_port_state & (UINT)(1 << port)))
253 {
254 /* Mark device disconnection. */
255 hub -> ux_host_class_hub_port_state &= (UINT)~(1 << port);
256
257 /* We get here when there is a device extraction. */
258 _ux_host_stack_device_remove(hcd, hub -> ux_host_class_hub_device, port);
259 }
260
261 #if defined(UX_HOST_STANDALONE)
262 /* Port operations are done outside. */
263 #else
264
265 /* The port should be disabled now. Power is still applied. */
266 status = _ux_host_class_hub_feature(hub, port, UX_CLEAR_FEATURE, UX_HOST_CLASS_HUB_PORT_ENABLE);
267
268 /* We must clear the enable change condition so that we don't get awaken again. */
269 _ux_host_class_hub_feature(hub, port, UX_CLEAR_FEATURE, UX_HOST_CLASS_HUB_C_PORT_ENABLE);
270
271 /* We must clear the connection change condition so that we don't get awaken again. */
272 _ux_host_class_hub_feature(hub, port, UX_CLEAR_FEATURE, UX_HOST_CLASS_HUB_C_PORT_CONNECTION);
273 #endif
274 }
275
276 /* Return to caller. */
277 return;
278 }
279
280