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