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