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
34
35 static inline VOID _ux_host_class_hub_inst_tasks_run(UX_HOST_CLASS_HUB *hub);
36
37 /**************************************************************************/
38 /* */
39 /* FUNCTION RELEASE */
40 /* */
41 /* _ux_host_class_hub_tasks_run PORTABLE C */
42 /* 6.2.1 */
43 /* AUTHOR */
44 /* */
45 /* Chaoqiong Xiao, Microsoft Corporation */
46 /* */
47 /* DESCRIPTION */
48 /* */
49 /* This function runs HUB background tasks. */
50 /* */
51 /* This function is for standalone mode. */
52 /* */
53 /* INPUT */
54 /* */
55 /* hub Pointer to HUB instance */
56 /* */
57 /* OUTPUT */
58 /* */
59 /* None */
60 /* */
61 /* CALLS */
62 /* */
63 /* _ux_host_class_hub_port_change_connection_process */
64 /* Process connection */
65 /* _ux_host_class_hub_port_change_enable_process */
66 /* Enable process */
67 /* _ux_host_class_hub_port_change_over_current_process */
68 /* Change over current process */
69 /* _ux_host_class_hub_port_change_reset_process */
70 /* Reset process */
71 /* _ux_host_class_hub_port_change_suspend_process */
72 /* Suspend process */
73 /* _ux_host_class_hub_feature Prepare feature request */
74 /* _ux_host_class_hub_status_get Prepare get status request */
75 /* _ux_utility_short_get Get 16-bit word */
76 /* _ux_utility_memory_free Memory free */
77 /* _ux_host_stack_new_device_create Obtain a free device instance */
78 /* _ux_host_stack_transfer_run Process the transfer */
79 /* */
80 /* CALLED BY */
81 /* */
82 /* USBX Host Stack */
83 /* */
84 /* RELEASE HISTORY */
85 /* */
86 /* DATE NAME DESCRIPTION */
87 /* */
88 /* 07-29-2022 Chaoqiong Xiao Initial Version 6.1.12 */
89 /* 10-31-2022 Chaoqiong Xiao Modified comment(s), */
90 /* fixed reset speed handling, */
91 /* resulting in version 6.2.0 */
92 /* 03-08-2023 Chaoqiong Xiao Modified comment(s), */
93 /* fixed compile issue if only */
94 /* one device is supported, */
95 /* resulting in version 6.2.1 */
96 /* */
97 /**************************************************************************/
_ux_host_class_hub_tasks_run(UX_HOST_CLASS * hub_class)98 UINT _ux_host_class_hub_tasks_run(UX_HOST_CLASS *hub_class)
99 {
100 UX_HOST_CLASS_HUB *hub;
101
102 /* Validate class entry. */
103 if (hub_class -> ux_host_class_status != UX_USED ||
104 hub_class -> ux_host_class_entry_function != _ux_host_class_hub_entry)
105 return(UX_STATE_IDLE);
106
107 /* Run for class instances. */
108 hub = (UX_HOST_CLASS_HUB *)hub_class -> ux_host_class_first_instance;
109 while(hub)
110 {
111
112 /* Run tasks for each HUB instance. */
113 _ux_host_class_hub_inst_tasks_run(hub);
114 hub = hub -> ux_host_class_hub_next_instance;
115 }
116 return(UX_STATE_WAIT);
117 }
118
119 /* Return non-zero if there is change. */
_ux_host_class_hub_change_check(UX_HOST_CLASS_HUB * hub)120 static inline UCHAR _ux_host_class_hub_change_check(UX_HOST_CLASS_HUB *hub)
121 {
122 UX_ENDPOINT *endpoint = hub -> ux_host_class_hub_interrupt_endpoint;
123 UX_TRANSFER *transfer = &endpoint -> ux_endpoint_transfer_request;
124 UCHAR *buffer = transfer -> ux_transfer_request_data_pointer;
125 UCHAR byte_i = (UCHAR)(hub -> ux_host_class_hub_run_port >> 3);
126 UCHAR bit_i = (UCHAR)(hub -> ux_host_class_hub_run_port & 0x7u);
127 UCHAR res = (UCHAR)(buffer[byte_i] & (1u << bit_i));
128 buffer[byte_i] = (UCHAR)(buffer[byte_i] & ~(1u << bit_i));
129 return(res);
130 }
_ux_host_class_hub_status_process(UX_HOST_CLASS_HUB * hub,UINT port)131 static inline VOID _ux_host_class_hub_status_process(UX_HOST_CLASS_HUB *hub, UINT port)
132 {
133 USHORT port_status = hub -> ux_host_class_hub_run_port_status;
134 USHORT port_change = hub -> ux_host_class_hub_run_port_change;
135
136 /* Next port, if all changes handled. */
137 if (port_change == 0)
138 {
139 hub -> ux_host_class_hub_run_state = UX_HOST_CLASS_HUB_CHANGE_NEXT;
140 return;
141 }
142
143 if (port_change & UX_HOST_CLASS_HUB_PORT_CHANGE_OVER_CURRENT)
144 {
145 hub -> ux_host_class_hub_run_port_change = (USHORT)
146 (hub -> ux_host_class_hub_run_port_change &
147 ~UX_HOST_CLASS_HUB_PORT_CHANGE_OVER_CURRENT);
148 _ux_host_class_hub_port_change_over_current_process(hub, port, port_status);
149
150 /* Next: wait transfer done, then check change again. */
151 hub -> ux_host_class_hub_run_state = UX_HOST_CLASS_HUB_TRANS_WAIT;
152 hub -> ux_host_class_hub_next_state = UX_HOST_CLASS_HUB_STATUS_PROCESS;
153 return;
154 }
155 if (port_change & UX_HOST_CLASS_HUB_PORT_CHANGE_ENABLE)
156 {
157 hub -> ux_host_class_hub_run_port_change = (USHORT)
158 (hub -> ux_host_class_hub_run_port_change &
159 ~UX_HOST_CLASS_HUB_PORT_CHANGE_ENABLE);
160 _ux_host_class_hub_port_change_enable_process(hub, port, port_status);
161
162 /* Next: wait transfer done, then check change again. */
163 hub -> ux_host_class_hub_run_state = UX_HOST_CLASS_HUB_TRANS_WAIT;
164 hub -> ux_host_class_hub_next_state = UX_HOST_CLASS_HUB_STATUS_PROCESS;
165 return;
166 }
167 if (port_change & UX_HOST_CLASS_HUB_PORT_CHANGE_SUSPEND)
168 {
169 hub -> ux_host_class_hub_run_port_change = (USHORT)
170 (hub -> ux_host_class_hub_run_port_change &
171 ~UX_HOST_CLASS_HUB_PORT_CHANGE_SUSPEND);
172 _ux_host_class_hub_port_change_suspend_process(hub, port, port_status);
173
174 /* Next: wait transfer done, then check change again. */
175 hub -> ux_host_class_hub_run_state = UX_HOST_CLASS_HUB_TRANS_WAIT;
176 hub -> ux_host_class_hub_next_state = UX_HOST_CLASS_HUB_STATUS_PROCESS;
177 return;
178 }
179 if (port_change & UX_HOST_CLASS_HUB_PORT_CHANGE_RESET)
180 {
181 hub -> ux_host_class_hub_run_port_change = (USHORT)
182 (hub -> ux_host_class_hub_run_port_change &
183 ~UX_HOST_CLASS_HUB_PORT_CHANGE_RESET);
184 _ux_host_class_hub_port_change_reset_process(hub, port, port_status);
185
186 /* Next: wait transfer done, then process reset
187 (get status and update enum device). */
188 hub -> ux_host_class_hub_run_state = UX_HOST_CLASS_HUB_TRANS_WAIT;
189 hub -> ux_host_class_hub_next_state = UX_HOST_CLASS_HUB_RESET_PROCESS;
190 return;
191 }
192 if (port_change & UX_HOST_CLASS_HUB_PORT_CHANGE_CONNECTION)
193 {
194 hub -> ux_host_class_hub_run_port_change = (USHORT)
195 (hub -> ux_host_class_hub_run_port_change &
196 ~UX_HOST_CLASS_HUB_PORT_CHANGE_CONNECTION);
197 _ux_host_class_hub_port_change_connection_process(hub, port, port_status);
198
199 if (hub -> ux_host_class_hub_port_state & (1u << port))
200 {
201
202 /* Next: clear c_connection, then process connect. */
203 _ux_host_class_hub_feature(hub, port, UX_CLEAR_FEATURE, UX_HOST_CLASS_HUB_C_PORT_CONNECTION);
204 hub -> ux_host_class_hub_run_state = UX_HOST_CLASS_HUB_TRANS_WAIT;
205 hub -> ux_host_class_hub_next_state = UX_HOST_CLASS_HUB_CONNECT_PROCESS;
206 return;
207 }
208
209 /* Disable port, then clear c_enable, c_connection. */
210 _ux_host_class_hub_feature(hub, port, UX_CLEAR_FEATURE, UX_HOST_CLASS_HUB_PORT_ENABLE);
211 hub -> ux_host_class_hub_run_state = UX_HOST_CLASS_HUB_TRANS_WAIT;
212 hub -> ux_host_class_hub_next_state = UX_HOST_CLASS_HUB_DISC_DISABLED;
213 return;
214 }
215 }
_ux_host_class_hub_inst_tasks_run(UX_HOST_CLASS_HUB * hub)216 static inline VOID _ux_host_class_hub_inst_tasks_run(UX_HOST_CLASS_HUB *hub)
217 {
218 UX_HCD *hcd;
219 UX_DEVICE *device;
220 UX_DEVICE *hub_device = hub -> ux_host_class_hub_device;
221 UX_ENDPOINT *ep0 = &hub_device -> ux_device_control_endpoint;
222 UX_ENDPOINT *dev_ep0;
223 UX_TRANSFER *trans0 = &ep0 -> ux_endpoint_transfer_request;
224 UINT status;
225
226 /* Task runs when hub is live. */
227 if (hub -> ux_host_class_hub_state != UX_HOST_CLASS_INSTANCE_LIVE)
228 return;
229
230 while(1)
231 {
232
233 /* Immediate state change: continue.
234 Wait/pending state : return. */
235 switch(hub -> ux_host_class_hub_run_state)
236 {
237
238 case UX_HOST_CLASS_HUB_CHANGE_CHECK :
239
240 /* Check if current port has changes. */
241 if (_ux_host_class_hub_change_check(hub))
242 {
243 if (hub -> ux_host_class_hub_run_port == 0)
244 {
245
246 /* Device state change. */
247 /* There is nothing handled now. */
248
249 /* Next state: try next, port 1. */
250 hub -> ux_host_class_hub_run_state = UX_HOST_CLASS_HUB_CHANGE_NEXT;
251 continue;
252 }
253
254 /* Port state change. */
255 /* Next: read status. */
256 hub -> ux_host_class_hub_run_state = UX_HOST_CLASS_HUB_STATUS_GET;
257 continue;
258 }
259
260 /* No change detected,
261 * still need to check if there is enumeration reset request. */
262 device = _ux_system_host -> ux_system_host_enum_device;
263 while(device != UX_NULL)
264 {
265
266 /* Check if a device is waiting on the port for reset. */
267 if ((device -> ux_device_flags & UX_DEVICE_FLAG_RESET) &&
268 UX_DEVICE_PARENT_MATCH(device, hub_device) &&
269 (device -> ux_device_port_location == hub -> ux_host_class_hub_run_port))
270 {
271 device -> ux_device_flags &= ~UX_DEVICE_FLAG_RESET;
272 break;
273 }
274
275 /* Next enumerating device. */
276 device = device -> ux_device_enum_next;
277 }
278 if (device)
279 {
280
281 /* Next: reset then check port status ... */
282 hub -> ux_host_class_hub_run_state = UX_HOST_CLASS_HUB_RESET;
283 continue;
284 }
285
286 /* No change, try next port. */
287
288 /* Fall through. */
289 case UX_HOST_CLASS_HUB_CHANGE_NEXT :
290 if (hub -> ux_host_class_hub_run_port <
291 hub -> ux_host_class_hub_descriptor.bNbPorts)
292 {
293
294 /* Check next port. */
295 hub -> ux_host_class_hub_run_port ++;
296 hub -> ux_host_class_hub_run_state = UX_HOST_CLASS_HUB_CHANGE_CHECK;
297 continue;
298 }
299
300 /* All ports checked, next round. */
301 hub -> ux_host_class_hub_run_port = 0;
302 hub -> ux_host_class_hub_run_state = UX_HOST_CLASS_HUB_CHANGE_CHECK;
303 return;
304
305 case UX_HOST_CLASS_HUB_RESET :
306 status = _ux_host_class_hub_feature(hub, hub -> ux_host_class_hub_run_port,
307 UX_SET_FEATURE, UX_HOST_CLASS_HUB_PORT_RESET);
308
309 /* Next: check another port, actions are taken on C_RESET detection. */
310 hub -> ux_host_class_hub_run_state = UX_HOST_CLASS_HUB_TRANS_WAIT;
311 hub -> ux_host_class_hub_next_state = UX_HOST_CLASS_HUB_CHANGE_NEXT;
312 continue;
313
314 case UX_HOST_CLASS_HUB_STATUS_GET :
315 status = _ux_host_class_hub_status_get(hub, hub -> ux_host_class_hub_run_port, UX_NULL, UX_NULL);
316
317 if (status != UX_SUCCESS)
318 {
319
320 /* Fail, retry. */
321 hub -> ux_host_class_hub_run_port = 0;
322 hub -> ux_host_class_hub_run_state = UX_HOST_CLASS_HUB_CHANGE_CHECK;
323 return;
324 }
325 hub -> ux_host_class_hub_transfer = trans0;
326 hub -> ux_host_class_hub_run_state = UX_HOST_CLASS_HUB_TRANS_WAIT;
327 hub -> ux_host_class_hub_next_state = UX_HOST_CLASS_HUB_STATUS_GET_DONE;
328 continue;
329
330 case UX_HOST_CLASS_HUB_STATUS_GET_DONE:
331
332 /* Store status change for later usage. */
333 hub -> ux_host_class_hub_run_port_status = (USHORT)
334 _ux_utility_short_get(hub -> ux_host_class_hub_allocated);
335 hub -> ux_host_class_hub_run_port_change = (USHORT)
336 _ux_utility_short_get(hub -> ux_host_class_hub_allocated + 2);
337
338 /* Free allocated buffer. */
339 _ux_utility_memory_free(hub -> ux_host_class_hub_allocated);
340 hub -> ux_host_class_hub_allocated = UX_NULL;
341
342 /* Fall through. */
343 case UX_HOST_CLASS_HUB_STATUS_PROCESS :
344 _ux_host_class_hub_status_process(hub, hub -> ux_host_class_hub_run_port);
345 hub -> ux_host_class_hub_transfer = trans0;
346 continue;
347
348 case UX_HOST_CLASS_HUB_CONNECT_PROCESS:
349
350 status = _ux_host_stack_new_device_create(UX_DEVICE_HCD_GET(hub_device),
351 hub_device, hub -> ux_host_class_hub_run_port,
352 UX_FULL_SPEED_DEVICE, UX_MAX_SELF_POWER,
353 &device);
354 if (status == UX_SUCCESS)
355 {
356
357 /* Put device in enumeration list. */
358 device -> ux_device_flags |= UX_DEVICE_FLAG_ENUM;
359 }
360
361 /* Try next port. */
362 hub -> ux_host_class_hub_run_state = UX_HOST_CLASS_HUB_CHANGE_NEXT;
363 return;
364
365 case UX_HOST_CLASS_HUB_DISC_DISABLED:
366
367 /* Next: clear C_ENABLE, then clear C_CONNECTION. */
368 _ux_host_class_hub_feature(hub, hub -> ux_host_class_hub_run_port, UX_CLEAR_FEATURE, UX_HOST_CLASS_HUB_C_PORT_ENABLE);
369 hub -> ux_host_class_hub_run_state = UX_HOST_CLASS_HUB_TRANS_WAIT;
370 hub -> ux_host_class_hub_next_state = UX_HOST_CLASS_HUB_DISC_CLEAR_1;
371 continue;
372
373 case UX_HOST_CLASS_HUB_DISC_CLEAR_1:
374
375 /* Next: clear C_CONNECTION, then next port. */
376 _ux_host_class_hub_feature(hub, hub -> ux_host_class_hub_run_port, UX_CLEAR_FEATURE, UX_HOST_CLASS_HUB_C_PORT_CONNECTION);
377 hub -> ux_host_class_hub_run_state = UX_HOST_CLASS_HUB_TRANS_WAIT;
378 hub -> ux_host_class_hub_next_state = UX_HOST_CLASS_HUB_CHANGE_NEXT;
379 continue;
380
381 case UX_HOST_CLASS_HUB_RESET_PROCESS :
382
383 /* Find enum device, update enum port status, state -> ADDR_SET. */
384 device = _ux_system_host -> ux_system_host_enum_device;
385 while(device != UX_NULL)
386 {
387
388 /* If there is a device connected to the port, put its state to ADDR_SET. */
389 if (UX_DEVICE_PARENT_MATCH(device, hub -> ux_host_class_hub_device) &&
390 (device -> ux_device_port_location == hub -> ux_host_class_hub_run_port))
391 {
392
393 /* Save status and speed. */
394 device -> ux_device_enum_port_status = (UCHAR)hub -> ux_host_class_hub_run_port_status;
395
396 /* Append speed information. */
397 switch(hub -> ux_host_class_hub_run_port_status &
398 (UX_HOST_CLASS_HUB_PORT_STATUS_LOW_SPEED | UX_HOST_CLASS_HUB_PORT_STATUS_HIGH_SPEED))
399 {
400 case 0: /* It's Full speed. */
401 device -> ux_device_enum_port_status |= UX_PS_DS_FS;
402 device -> ux_device_speed = UX_FULL_SPEED_DEVICE;
403 break;
404
405 case UX_HOST_CLASS_HUB_PORT_STATUS_HIGH_SPEED: /* It's high speed. */
406 device -> ux_device_enum_port_status |= UX_PS_DS_HS;
407 device -> ux_device_speed = UX_HIGH_SPEED_DEVICE;
408 break;
409
410 default: /* It's Low speed. */
411 device -> ux_device_speed = UX_LOW_SPEED_DEVICE;
412 break;
413 }
414
415 /* Return device address to 0. */
416 hcd = UX_DEVICE_HCD_GET(device);
417
418 #if UX_MAX_DEVICES > 1
419 if (device -> ux_device_address)
420 {
421
422 /* Free the address. */
423 hcd -> ux_hcd_address[(device -> ux_device_address-1) >> 3] &=
424 (UCHAR)(1u << ((device -> ux_device_address-1) & 7u));
425
426 }
427 #endif
428
429 /* Assume speed change, re-create EP0 at the HCD level. */
430 dev_ep0 = &device -> ux_device_control_endpoint;
431 hcd -> ux_hcd_entry_function(hcd, UX_HCD_DESTROY_ENDPOINT, (VOID *)dev_ep0);
432 hcd -> ux_hcd_entry_function(hcd, UX_HCD_CREATE_ENDPOINT, (VOID *)dev_ep0);
433
434 /* Wait a while and set address. */
435 device -> ux_device_enum_next_state = UX_HOST_STACK_ENUM_DEVICE_ADDR_SET;
436 device -> ux_device_enum_state = UX_HOST_STACK_ENUM_WAIT;
437 device -> ux_device_enum_wait_start = _ux_utility_time_get();
438 device -> ux_device_enum_wait_ms = UX_MS_TO_TICK_NON_ZERO(2);
439 break;
440 }
441
442 /* Next device. */
443 device = device -> ux_device_enum_next;
444 }
445
446 /* Reset processed check other status. */
447 hub -> ux_host_class_hub_run_state = UX_HOST_CLASS_HUB_STATUS_PROCESS;
448 continue;
449
450 case UX_HOST_CLASS_HUB_TRANS_WAIT :
451
452 /* Poll transfer task. */
453 status = _ux_host_stack_transfer_run(hub -> ux_host_class_hub_transfer);
454
455 /* Transfer done - next state. */
456 if (status == UX_STATE_NEXT || status == UX_STATE_IDLE)
457 {
458 hub -> ux_host_class_hub_run_state = hub -> ux_host_class_hub_next_state;
459 continue;
460 }
461
462 /* Check error. */
463 if (status < UX_STATE_NEXT)
464 {
465
466 /* Fail, just check next port. */
467 hub -> ux_host_class_hub_run_state = UX_HOST_CLASS_HUB_CHANGE_NEXT;
468 hub -> ux_host_class_hub_run_status =
469 hub -> ux_host_class_hub_transfer ->
470 ux_transfer_request_completion_code;
471 continue;
472 }
473
474 /* Transfer in progress, wait. */
475 return;
476
477 default:
478 break;
479 }
480 }
481 }
482 #endif
483