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 #if defined(UX_HOST_STANDALONE)
33 extern UINT _ux_host_class_hub_descriptor_parse(UX_HOST_CLASS_HUB *hub, UCHAR *descriptor);
34 static inline UINT _ux_host_class_hub_activate_wait(UX_HOST_CLASS_COMMAND *command);
35 #endif
36 
37 
38 /**************************************************************************/
39 /*                                                                        */
40 /*  FUNCTION                                               RELEASE        */
41 /*                                                                        */
42 /*    _ux_host_class_hub_entry                            PORTABLE C      */
43 /*                                                           6.3.0        */
44 /*  AUTHOR                                                                */
45 /*                                                                        */
46 /*    Chaoqiong Xiao, Microsoft Corporation                               */
47 /*                                                                        */
48 /*  DESCRIPTION                                                           */
49 /*                                                                        */
50 /*    This function is the entry point of the HUB class. It will be       */
51 /*    called by the USBX stack enumeration module when there is a new     */
52 /*    device on the bus or when there is a device extraction.             */
53 /*                                                                        */
54 /*  INPUT                                                                 */
55 /*                                                                        */
56 /*    command                               Pointer to command            */
57 /*                                                                        */
58 /*  OUTPUT                                                                */
59 /*                                                                        */
60 /*    Completion Status                                                   */
61 /*                                                                        */
62 /*  CALLS                                                                 */
63 /*                                                                        */
64 /*    _ux_host_class_hub_activate           Activate HUB class            */
65 /*    _ux_host_class_hub_deactivate         Deactivate HUB class          */
66 /*                                                                        */
67 /*  CALLED BY                                                             */
68 /*                                                                        */
69 /*    Host Stack                                                          */
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 /*                                            used query usage of device  */
78 /*                                            ClassSubclassProtocol,      */
79 /*                                            resulting in version 6.1    */
80 /*  07-29-2022     Chaoqiong Xiao           Modified comment(s),          */
81 /*                                            added standalone support,   */
82 /*                                            resulting in version 6.1.12 */
83 /*  10-31-2022     Chaoqiong Xiao           Modified comment(s),          */
84 /*                                            fixed power on delay calc,  */
85 /*                                            resulting in version 6.2.0  */
86 /*  10-31-2023     Chaoqiong Xiao           Modified comment(s),          */
87 /*                                            fixed fail code sequence,   */
88 /*                                            fixed compile warnings,     */
89 /*                                            resulting in version 6.3.0  */
90 /*                                                                        */
91 /**************************************************************************/
_ux_host_class_hub_entry(UX_HOST_CLASS_COMMAND * command)92 UINT  _ux_host_class_hub_entry(UX_HOST_CLASS_COMMAND *command)
93 {
94 
95 UINT    status;
96 
97 
98     /* The command request will tell us we need to do here, either a enumeration
99        query, an activation or a deactivation.  */
100     switch (command -> ux_host_class_command_request)
101     {
102 
103     case UX_HOST_CLASS_COMMAND_QUERY:
104 
105         /* The query command is used to let the stack enumeration process know if we want to own
106            this device or not.  */
107         if ((command -> ux_host_class_command_usage == UX_HOST_CLASS_COMMAND_USAGE_DCSP) &&
108             (command -> ux_host_class_command_class == UX_HOST_CLASS_HUB_CLASS))
109             return(UX_SUCCESS);
110         else
111             return(UX_NO_CLASS_MATCH);
112 
113 
114     case UX_HOST_CLASS_COMMAND_ACTIVATE:
115 
116         /* The activate command is used when the device inserted has found a parent and
117            is ready to complete the enumeration.  */
118         status =  _ux_host_class_hub_activate(command);
119 
120         /* Return completion status.  */
121         return(status);
122 
123 #if defined(UX_HOST_STANDALONE)
124     case UX_HOST_CLASS_COMMAND_ACTIVATE_WAIT:
125         status = _ux_host_class_hub_activate_wait(command);
126         return(status);
127 #endif
128 
129     case UX_HOST_CLASS_COMMAND_DEACTIVATE:
130 
131         /* The deactivate command is used when the device has been extracted either
132            directly or when its parents has been extracted */
133         status =  _ux_host_class_hub_deactivate(command);
134 
135         /* Return completion status.  */
136         return(status);
137 
138     default:
139 
140         /* Error trap. */
141         _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_FUNCTION_NOT_SUPPORTED);
142 
143         /* If trace is enabled, insert this event into the trace buffer.  */
144         UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0)
145 
146         /* Return error status.  */
147         return(UX_FUNCTION_NOT_SUPPORTED);
148     }
149 }
150 
151 #if defined(UX_HOST_STANDALONE)
152 #define UX_HOST_STACK_ENUM_TRANS_ERROR(t)       (                               \
153     (t)->ux_transfer_request_completion_code != UX_SUCCESS ||                   \
154     (t)->ux_transfer_request_actual_length !=                                   \
155         (t)->ux_transfer_request_requested_length)
156 
_ux_host_class_hub_enum_get_status(UX_HOST_CLASS_HUB * hub,UX_TRANSFER * trans)157 static inline UINT _ux_host_class_hub_enum_get_status(UX_HOST_CLASS_HUB *hub, UX_TRANSFER *trans)
158 {
159     hub -> ux_host_class_hub_allocated = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_CACHE_SAFE_MEMORY, 2);
160     if (hub -> ux_host_class_hub_allocated == UX_NULL)
161         return(UX_MEMORY_INSUFFICIENT);
162 
163     trans -> ux_transfer_request_requested_length =  2;
164     trans -> ux_transfer_request_data_pointer =      hub -> ux_host_class_hub_allocated;
165     trans -> ux_transfer_request_function =          UX_GET_STATUS;
166     trans -> ux_transfer_request_type =              UX_REQUEST_IN | UX_REQUEST_TYPE_STANDARD | UX_REQUEST_TARGET_DEVICE;
167     trans -> ux_transfer_request_value =             0;
168     trans -> ux_transfer_request_index =             0;
169     return(UX_SUCCESS);
170 }
_ux_host_class_hub_enum_set_config(UX_HOST_CLASS_HUB * hub,UX_TRANSFER * trans,UX_CONFIGURATION * configuration)171 static inline VOID _ux_host_class_hub_enum_set_config(UX_HOST_CLASS_HUB *hub, UX_TRANSFER *trans, UX_CONFIGURATION *configuration)
172 {
173     UX_PARAMETER_NOT_USED(hub);
174     trans -> ux_transfer_request_requested_length =  0;
175     trans -> ux_transfer_request_function =          UX_SET_CONFIGURATION;
176     trans -> ux_transfer_request_type =              UX_REQUEST_OUT | UX_REQUEST_TYPE_STANDARD | UX_REQUEST_TARGET_DEVICE;
177     trans -> ux_transfer_request_value =             (USHORT) configuration -> ux_configuration_descriptor.bConfigurationValue;
178     trans -> ux_transfer_request_index =             0;
179 }
_ux_host_class_hub_activate_wait(UX_HOST_CLASS_COMMAND * command)180 static inline UINT _ux_host_class_hub_activate_wait(UX_HOST_CLASS_COMMAND *command)
181 {
182 UX_DEVICE               *device;
183 UX_ENDPOINT             *ep0;
184 UX_TRANSFER             *trans0, *trans;
185 UX_HOST_CLASS_HUB       *hub;
186 UINT                    status;
187 ULONG                   current_ms, elapsed_ms;
188 
189     /* Get the instance for this class.  */
190     device = (UX_DEVICE *)command -> ux_host_class_command_container;
191     hub =  (UX_HOST_CLASS_HUB *) device -> ux_device_class_instance;
192 
193     /* Get endpoint 0 and transfer request.  */
194     ep0 = &device -> ux_device_control_endpoint;
195     trans0 = &ep0 -> ux_endpoint_transfer_request;
196 
197     /* Get current transfer request.  */
198     trans = hub -> ux_host_class_hub_transfer;
199 
200     /* Immediate state change: continue.
201         Wait/pending state   : return.  */
202     while(1)
203     {
204 
205         /* Run initialize state machine.  */
206         switch(hub -> ux_host_class_hub_enum_state)
207         {
208 
209         case UX_HOST_CLASS_HUB_ENUM_GET_STATUS       :
210             status = _ux_host_class_hub_enum_get_status(hub, trans0);
211             if (status != UX_SUCCESS)
212             {
213                 hub -> ux_host_class_hub_enum_state = UX_HOST_CLASS_HUB_ENUM_DONE;
214                 hub -> ux_host_class_hub_run_status = status;
215                 continue;
216             }
217 
218             UX_TRANSFER_STATE_RESET(trans0);
219             hub -> ux_host_class_hub_transfer = trans0;
220             hub -> ux_host_class_hub_enum_state = UX_HOST_CLASS_HUB_ENUM_TRANS_WAIT;
221             hub -> ux_host_class_hub_next_state = UX_HOST_CLASS_HUB_ENUM_POWER_CHECK;
222             continue;
223 
224         case UX_HOST_CLASS_HUB_ENUM_POWER_CHECK      :
225 
226             /* Transfer request error check.  */
227             if (UX_HOST_STACK_ENUM_TRANS_ERROR(trans))
228             {
229                 hub -> ux_host_class_hub_enum_state = UX_HOST_CLASS_HUB_ENUM_DONE;
230                 hub -> ux_host_class_hub_run_status = UX_CONNECTION_INCOMPATIBLE;
231                 continue;
232             }
233 
234             /* Save power source setting.  */
235             if (hub -> ux_host_class_hub_allocated[0] & UX_STATUS_DEVICE_SELF_POWERED)
236                 device -> ux_device_power_source = UX_DEVICE_SELF_POWERED;
237             else
238                 device -> ux_device_power_source = UX_DEVICE_BUS_POWERED;
239 
240             /* Free allocated buffer.  */
241             _ux_utility_memory_free(hub -> ux_host_class_hub_allocated);
242             hub -> ux_host_class_hub_allocated = UX_NULL;
243 
244 #if UX_MAX_DEVICES > 1
245 
246             /* Check the HUB power source and check the parent power source.  */
247             if (device -> ux_device_power_source == UX_DEVICE_BUS_POWERED)
248             {
249 
250                 /* Check parent power.  */
251                 if (device -> ux_device_parent != UX_NULL)
252                 {
253                     if (device -> ux_device_parent -> ux_device_power_source == UX_DEVICE_BUS_POWERED)
254                     {
255                         hub -> ux_host_class_hub_enum_state = UX_HOST_CLASS_HUB_ENUM_DONE;
256                         hub -> ux_host_class_hub_run_status = UX_CONNECTION_INCOMPATIBLE;
257                         continue;
258                     }
259                 }
260             }
261 #endif
262             /* Fall through.  */
263         case UX_HOST_CLASS_HUB_ENUM_SET_CONFIG       :
264             _ux_host_class_hub_enum_set_config(hub, trans0,
265                                     device -> ux_device_first_configuration);
266 
267             UX_TRANSFER_STATE_RESET(trans0);
268             hub -> ux_host_class_hub_transfer = trans0;
269             hub -> ux_host_class_hub_enum_state = UX_HOST_CLASS_HUB_ENUM_TRANS_WAIT;
270             hub -> ux_host_class_hub_next_state = UX_HOST_CLASS_HUB_ENUM_SET_CONFIG_DONE;
271             continue;
272 
273         case UX_HOST_CLASS_HUB_ENUM_SET_CONFIG_DONE  :
274 
275             /* Transfer request error check.  */
276             if (UX_HOST_STACK_ENUM_TRANS_ERROR(trans))
277             {
278                 hub -> ux_host_class_hub_enum_state = UX_HOST_CLASS_HUB_ENUM_DONE;
279                 hub -> ux_host_class_hub_run_status = UX_CONNECTION_INCOMPATIBLE;
280                 continue;
281             }
282 
283             /* Create configuration instance.  */
284             status = _ux_host_stack_configuration_instance_create(device -> ux_device_first_configuration);
285             if (status != UX_SUCCESS)
286             {
287                 hub -> ux_host_class_hub_enum_state = UX_HOST_CLASS_HUB_ENUM_DONE;
288                 hub -> ux_host_class_hub_run_status = status;
289                 continue;
290             }
291 
292             /* Change device state.  */
293             device -> ux_device_state = UX_DEVICE_CONFIGURED;
294             device -> ux_device_current_configuration = device -> ux_device_first_configuration;
295 
296             /* Fall through.  */
297         case UX_HOST_CLASS_HUB_ENUM_GET_HUB_DESC     :
298             status = _ux_host_class_hub_descriptor_get(hub);
299             if (UX_SUCCESS != status)
300             {
301                 hub -> ux_host_class_hub_enum_state = UX_HOST_CLASS_HUB_ENUM_DONE;
302                 hub -> ux_host_class_hub_run_status = status;
303                 continue;
304             }
305 
306             /* Request already updated, to transfer state.  */
307             hub -> ux_host_class_hub_enum_state = UX_HOST_CLASS_HUB_ENUM_TRANS_WAIT;
308             hub -> ux_host_class_hub_next_state = UX_HOST_CLASS_HUB_ENUM_GET_HUB_DESC_DONE;
309             continue;
310 
311         case UX_HOST_CLASS_HUB_ENUM_GET_HUB_DESC_DONE:
312 
313             /* Transfer request error check.  */
314             if (UX_HOST_STACK_ENUM_TRANS_ERROR(trans))
315             {
316                 hub -> ux_host_class_hub_enum_state = UX_HOST_CLASS_HUB_ENUM_DONE;
317                 hub -> ux_host_class_hub_run_status = UX_DESCRIPTOR_CORRUPTED;
318                 continue;
319             }
320 
321             /* Parse descriptor.  */
322             status = _ux_host_class_hub_descriptor_parse(hub, hub -> ux_host_class_hub_allocated);
323             if (status != UX_SUCCESS)
324             {
325                 hub -> ux_host_class_hub_enum_state = UX_HOST_CLASS_HUB_ENUM_DONE;
326                 hub -> ux_host_class_hub_run_status = status;
327                 continue;
328             }
329 
330             /* Free the allocated descriptor.  */
331             _ux_utility_memory_free(hub -> ux_host_class_hub_allocated);
332             hub -> ux_host_class_hub_allocated = UX_NULL;
333 
334             /* Initialize for port power ON.  */
335             hub -> ux_host_class_hub_run_port = 1;
336 
337             /* Fall through.  */
338         case UX_HOST_CLASS_HUB_ENUM_PORT_POWER       :
339 
340             /* Prepare for SetPortFeature(POWER).  */
341             status = _ux_host_class_hub_feature(hub,
342                                 hub -> ux_host_class_hub_run_port,
343                                 UX_SET_FEATURE, UX_HOST_CLASS_HUB_PORT_POWER);
344 
345             /* Request already updated, to transfer state.  */
346             hub -> ux_host_class_hub_enum_state = UX_HOST_CLASS_HUB_ENUM_TRANS_WAIT;
347             hub -> ux_host_class_hub_next_state = UX_HOST_CLASS_HUB_ENUM_PORT_POWER_DELAY;
348             continue;
349 
350         case UX_HOST_CLASS_HUB_ENUM_PORT_POWER_DELAY :
351 
352             /* Transfer request error check.  */
353             if (UX_HOST_STACK_ENUM_TRANS_ERROR(trans))
354             {
355 
356                 /* Set the HUB status to not powered.  */
357                 hub -> ux_host_class_hub_port_power &=
358                             (UINT)~(1u << hub -> ux_host_class_hub_run_port);
359                 hub -> ux_host_class_hub_enum_state = UX_HOST_CLASS_HUB_ENUM_PORT_NEXT;
360                 continue;
361             }
362 
363             /* Delay a while (as described by hub descriptor).  */
364             hub -> ux_host_class_hub_wait_start = _ux_utility_time_get();
365             hub -> ux_host_class_hub_wait_ms = UX_MS_TO_TICK_NON_ZERO((ULONG)hub -> ux_host_class_hub_descriptor.bPwrOn2PwrGood << 1);
366 
367             hub -> ux_host_class_hub_enum_state = UX_HOST_CLASS_HUB_ENUM_TRANS_WAIT;
368             hub -> ux_host_class_hub_next_state = UX_HOST_CLASS_HUB_ENUM_PORT_POWER_ON;
369             continue;
370 
371         case UX_HOST_CLASS_HUB_ENUM_PORT_POWER_ON:
372             hub -> ux_host_class_hub_port_power |= (UINT)(1u << hub -> ux_host_class_hub_run_port);
373 
374             /* Fall through.  */
375         case UX_HOST_CLASS_HUB_ENUM_PORT_NEXT        :
376 
377             /* Check if the last port is powered.  */
378             if (hub -> ux_host_class_hub_run_port <
379                 hub -> ux_host_class_hub_descriptor.bNbPorts)
380             {
381 
382                 /* Start another port power.  */
383                 hub -> ux_host_class_hub_run_port ++;
384                 hub -> ux_host_class_hub_enum_state = UX_HOST_CLASS_HUB_ENUM_PORT_POWER;
385                 continue;
386             }
387 
388             /* All port is powered.  */
389 
390             /* Fall through.  */
391         case UX_HOST_CLASS_HUB_ENUM_INTERRUPT_START  :
392             status = _ux_host_class_hub_interrupt_endpoint_start(hub);
393             if (status != UX_SUCCESS)
394                 hub -> ux_host_class_hub_run_status = status;
395 
396             /* Fall through.  */
397         case UX_HOST_CLASS_HUB_ENUM_DONE             :
398 
399             /* Free buffer allocated while enumerating.  */
400             if (hub -> ux_host_class_hub_allocated)
401             {
402                 _ux_utility_memory_free(hub -> ux_host_class_hub_allocated);
403                 hub -> ux_host_class_hub_allocated = UX_NULL;
404             }
405 
406             /* Error cases.  */
407             if (hub -> ux_host_class_hub_run_status != UX_SUCCESS)
408             {
409 
410                 /* Unlink from device.  */
411                 device -> ux_device_class_instance = UX_NULL;
412 
413                 /* Error trap. */
414                 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, hub -> ux_host_class_hub_run_status);
415 
416                 /* If trace is enabled, insert this event into the trace buffer.  */
417                 UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, hub -> ux_host_class_hub_run_status, hub, 0, 0, UX_TRACE_ERRORS, 0, 0)
418 
419                 /* Free hub.  */
420                 _ux_utility_memory_free(hub);
421                 return(UX_STATE_ERROR);
422             }
423 
424             /* Done success.  */
425 
426             /* Create this class instance.  */
427             _ux_host_stack_class_instance_create(hub -> ux_host_class_hub_class, (VOID *) hub);
428 
429             /* Store the instance in the device container, this is for the USBX stack
430                 when it needs to invoke the class.  */
431             device -> ux_device_class_instance =  (VOID *) hub;
432 
433             /* Mark the HUB as live now.  */
434             hub -> ux_host_class_hub_state =  UX_HOST_CLASS_INSTANCE_LIVE;
435 
436             /* If all is fine and the device is mounted, we may need to inform the application
437                 if a function has been programmed in the system structure.  */
438             if (_ux_system_host -> ux_system_host_change_function != UX_NULL)
439             {
440 
441                 /* Call system change function.  */
442                 _ux_system_host ->  ux_system_host_change_function(UX_DEVICE_INSERTION, hub -> ux_host_class_hub_class, (VOID *) hub);
443             }
444 
445             /* Next state: CHANGE_CHECK.  */
446             hub -> ux_host_class_hub_enum_state = UX_STATE_IDLE;
447             hub -> ux_host_class_hub_run_state = UX_HOST_CLASS_HUB_CHANGE_CHECK;
448             hub -> ux_host_class_hub_run_port = 0;
449 
450             /* Done activate.  */
451             return(UX_STATE_NEXT);
452 
453         case UX_HOST_CLASS_HUB_ENUM_TRANS_WAIT:
454 
455             /* Poll transfer task.  */
456             status = _ux_host_stack_transfer_run(hub -> ux_host_class_hub_transfer);
457             hub -> ux_host_class_hub_run_status = hub -> ux_host_class_hub_transfer ->
458                                             ux_transfer_request_completion_code;
459 
460             /* Transfer done - next state.  */
461             if (status == UX_STATE_NEXT || status == UX_STATE_IDLE)
462             {
463                 hub -> ux_host_class_hub_enum_state = hub -> ux_host_class_hub_next_state;
464                 continue;
465             }
466 
467             /* Check error.  */
468             if (status < UX_STATE_NEXT)
469             {
470 
471                 /* Fail.  */
472                 hub -> ux_host_class_hub_enum_state = UX_HOST_CLASS_HUB_ENUM_DONE;
473                 continue;
474             }
475 
476             /* Transfer in progress, wait.  */
477             return(UX_STATE_WAIT);
478 
479         case UX_HOST_CLASS_HUB_ENUM_DELAY_WAIT:
480             current_ms = _ux_utility_time_get();
481             elapsed_ms = _ux_utility_time_elapsed(current_ms,
482                                         hub -> ux_host_class_hub_wait_start);
483             if (elapsed_ms < hub -> ux_host_class_hub_wait_ms)
484             {
485 
486                 /* Keep waiting.  */
487                 return(UX_STATE_WAIT);
488             }
489 
490             /* Next state.  */
491             hub -> ux_host_class_hub_enum_state = hub -> ux_host_class_hub_next_state;
492             continue;
493 
494         default:
495             return(UX_STATE_NEXT);
496         }
497     }
498 }
499 #endif
500