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