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