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 /**************************************************************************/
32 /* */
33 /* FUNCTION RELEASE */
34 /* */
35 /* _ux_host_stack_rh_device_insertion PORTABLE C */
36 /* 6.1.11 */
37 /* AUTHOR */
38 /* */
39 /* Chaoqiong Xiao, Microsoft Corporation */
40 /* */
41 /* DESCRIPTION */
42 /* */
43 /* This function handles a device insertion on a downstream port of */
44 /* the root hub pointed by HCD. */
45 /* */
46 /* INPUT */
47 /* */
48 /* HCD Pointer to HCD structure */
49 /* port_index Port index of insertion */
50 /* */
51 /* OUTPUT */
52 /* */
53 /* Completion Status */
54 /* */
55 /* CALLS */
56 /* */
57 /* _ux_utility_delay_ms Thread sleep */
58 /* _ux_host_stack_new_device_create New device create */
59 /* _ux_host_stack_device_remove Device remove */
60 /* (hcd_entry_function) Entry function of HCD driver */
61 /* */
62 /* CALLED BY */
63 /* */
64 /* USBX Components */
65 /* */
66 /* RELEASE HISTORY */
67 /* */
68 /* DATE NAME DESCRIPTION */
69 /* */
70 /* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */
71 /* 09-30-2020 Chaoqiong Xiao Modified comment(s), */
72 /* checked HCD status before */
73 /* retrying enumeration, */
74 /* resulting in version 6.1 */
75 /* 02-02-2021 Chaoqiong Xiao Modified comment(s), */
76 /* handled more fail cases, */
77 /* updated internal call, */
78 /* added notification for */
79 /* device connection, */
80 /* added disconnection check */
81 /* in enumeration retries, */
82 /* resulting in version 6.1.4 */
83 /* 01-31-2022 Chaoqiong Xiao Modified comment(s), */
84 /* added standalone support, */
85 /* resulting in version 6.1.10 */
86 /* 04-25-2022 Chaoqiong Xiao Modified comment(s), */
87 /* internal clean up, */
88 /* resulting in version 6.1.11 */
89 /* */
90 /**************************************************************************/
_ux_host_stack_rh_device_insertion(UX_HCD * hcd,UINT port_index)91 UINT _ux_host_stack_rh_device_insertion(UX_HCD *hcd, UINT port_index)
92 {
93 #if defined(UX_HOST_STANDALONE)
94 UX_DEVICE *device;
95 UINT status;
96
97
98 /* If trace is enabled, insert this event into the trace buffer. */
99 UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_RH_DEVICE_INSERTION, hcd, port_index, 0, 0, UX_TRACE_HOST_STACK_EVENTS, 0, 0)
100
101 /* Anyway there is device connected. */
102 hcd -> ux_hcd_rh_device_connection |= (ULONG)(1 << port_index);
103
104 /* Create a new device slot for enumeration. */
105 status = _ux_host_stack_new_device_create(hcd, UX_NULL, port_index,
106 UX_FULL_SPEED_DEVICE, UX_MAX_SELF_POWER,
107 &device);
108
109 /* Link the device in enumeration list. */
110 if (status == UX_SUCCESS)
111 {
112
113 /* Set enumeration flag to process enumeration sequence. */
114 device -> ux_device_flags |= UX_DEVICE_FLAG_ENUM;
115
116 /* Done success. */
117 return(UX_SUCCESS);
118 }
119
120 #else
121 UX_DEVICE *device = UX_NULL;
122 UINT index_loop;
123 UINT device_speed;
124 ULONG port_status;
125 UINT status;
126
127 /* If trace is enabled, insert this event into the trace buffer. */
128 UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_RH_DEVICE_INSERTION, hcd, port_index, 0, 0, UX_TRACE_HOST_STACK_EVENTS, 0, 0)
129
130 /* Perform a PORT_ENABLE command. */
131 port_status = hcd -> ux_hcd_entry_function(hcd, UX_HCD_ENABLE_PORT, (VOID *)((ALIGN_TYPE)port_index));
132
133 /* Check return status. */
134 if (port_status == UX_PORT_INDEX_UNKNOWN)
135 return(port_status);
136
137 /* A debounce interval with a minimum duration of 100 ms on attach. */
138 _ux_utility_delay_ms(100);
139
140 /* The first attempts to do a device enumeration may fail.
141 Typically, after the port is reset and the first command is sent to
142 the device, there is no answer. In this case, we reset the port again
143 and retry. Usually that does the trick! */
144 for (index_loop = 0; index_loop < UX_RH_ENUMERATION_RETRY; index_loop++)
145 {
146
147 /* Now we have to do a PORT_RESET command. */
148 port_status = hcd -> ux_hcd_entry_function(hcd, UX_HCD_RESET_PORT, (VOID *)((ALIGN_TYPE)port_index));
149 if (port_status == UX_SUCCESS)
150 {
151
152 /* The port reset phase was successful. Before we invoke the device enumeration function,
153 we need to know the speed of the device. */
154 port_status = hcd -> ux_hcd_entry_function(hcd, UX_HCD_GET_PORT_STATUS, (VOID *)((ALIGN_TYPE)port_index));
155
156 /* Check return status. */
157 if (port_status == UX_PORT_INDEX_UNKNOWN)
158 {
159
160 /* Error trap. */
161 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ROOT_HUB, UX_DEVICE_ENUMERATION_FAILURE);
162
163 /* If trace is enabled, insert this event into the trace buffer. */
164 UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DEVICE_ENUMERATION_FAILURE, port_index, 0, 0, UX_TRACE_ERRORS, 0, 0)
165
166 return(UX_DEVICE_ENUMERATION_FAILURE);
167 }
168
169 /* Check if device is still connected. */
170 if ((port_status & UX_PS_CCS) == 0)
171 {
172
173 /* Device disconnected during enumeration retries. */
174 return(UX_DEVICE_ENUMERATION_FAILURE);
175 }
176
177 /* Set the device speed. */
178 device_speed = port_status >> UX_PS_DS;
179
180 /* Ask the USB stack to enumerate this device. A root hub is considered self
181 powered. */
182 status = _ux_host_stack_new_device_create(hcd, UX_NULL, port_index, device_speed, UX_MAX_SELF_POWER, &device);
183
184 /* Check return status. */
185 if (status == UX_SUCCESS)
186 {
187
188 /* Successful device create. */
189
190 /* The device has been mounted properly, we have to remember this
191 so when the device is removed, we have to invoke the enumeration
192 function again */
193 hcd -> ux_hcd_rh_device_connection |= (ULONG)(1 << port_index);
194
195 /* If the device instance is ready, notify application for connection. */
196 if (_ux_system_host -> ux_system_host_change_function)
197 {
198 _ux_system_host -> ux_system_host_change_function(UX_DEVICE_CONNECTION, UX_NULL, (VOID*)device);
199 }
200
201 /* Return success to the caller. */
202 return(UX_SUCCESS);
203 }
204 else
205 {
206
207 /* Return error if HCD is dead. */
208 if (hcd -> ux_hcd_status != UX_HCD_STATUS_OPERATIONAL)
209 return(UX_CONTROLLER_DEAD);
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 /* Simulate remove to free allocated resources if retry. */
220 if (index_loop < UX_RH_ENUMERATION_RETRY - 1)
221 _ux_host_stack_device_remove(hcd, UX_NULL, port_index);
222 }
223 }
224
225 /* We get here if something did not go well. Either the port did not respond
226 well to the ENABLE\RESET phases or the device did not enumerate well
227 so we try again ! */
228 _ux_utility_delay_ms(UX_RH_ENUMERATION_RETRY_DELAY);
229 }
230 #endif /* defined(UX_HOST_STANDALONE) */
231
232 /* If we get here, the device did not enumerate completely.
233 The device is still attached to the root hub and therefore
234 there could be a physical connection with a unconfigured device. */
235 hcd -> ux_hcd_rh_device_connection |= (ULONG)(1 << port_index);
236
237 /* Notify application for a physical connection failed to be enumed.
238 Device instance NULL indicates too many devices.
239 Device state unconfigured indicates enumeration fail. */
240 if (_ux_system_host -> ux_system_host_change_function)
241 {
242 _ux_system_host -> ux_system_host_change_function(UX_DEVICE_CONNECTION, UX_NULL, (VOID*)device);
243 }
244
245 /* Error trap. */
246 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ROOT_HUB, UX_DEVICE_ENUMERATION_FAILURE);
247
248 /* If trace is enabled, insert this event into the trace buffer. */
249 UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DEVICE_ENUMERATION_FAILURE, port_index, 0, 0, UX_TRACE_ERRORS, 0, 0)
250
251 /* Return a failed enumeration. */
252 return(UX_DEVICE_ENUMERATION_FAILURE);
253 }
254