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