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