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