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