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 /** Host Stack */
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_stack.h"
29
30
31 /**************************************************************************/
32 /* */
33 /* FUNCTION RELEASE */
34 /* */
35 /* _ux_host_stack_new_device_create PORTABLE C */
36 /* 6.1.12 */
37 /* AUTHOR */
38 /* */
39 /* Chaoqiong Xiao, Microsoft Corporation */
40 /* */
41 /* DESCRIPTION */
42 /* */
43 /* This function creates a new device on the USB. It may be called */
44 /* either by a hub or by the root hub. */
45 /* */
46 /* INPUT */
47 /* */
48 /* HCD HCD that owns that device */
49 /* device_owner Either a root hub instance */
50 /* or a hub instance */
51 /* port_index Port the new device is mounted*/
52 /* device_speed Speed at which the device is */
53 /* running (low, full, high) */
54 /* port_power_available Power available on the root */
55 /* or hub port. This value is */
56 /* used to ensure that the */
57 /* device can be configured */
58 /* without creating an */
59 /* OVER_CURRENT condition on */
60 /* the bus */
61 /* created_device Destination to fill created */
62 /* device instance */
63 /* */
64 /* OUTPUT */
65 /* */
66 /* Completion Status */
67 /* */
68 /* An Error code could be an indication that the device could not be */
69 /* created in which case the error is fatal to the enumeration and */
70 /* will not be retried. The error code can also indicate a failure */
71 /* for the device to respond to the GET_DEVICE_DESCRIPTOR. This error */
72 /* is not fatal and the caller should retry the enumeration process */
73 /* after the port to which the device is attached has been reset. */
74 /* */
75 /* CALLS */
76 /* */
77 /* _ux_host_stack_class_device_scan Scan class devices */
78 /* _ux_host_stack_class_interface_scan Scan class interfaces */
79 /* _ux_host_stack_device_address_set Set device address */
80 /* _ux_host_stack_device_descriptor_read Read device descriptor */
81 /* _ux_host_stack_configuration_enumerate */
82 /* Enumerate device config */
83 /* _ux_host_stack_new_device_get Get new device */
84 /* _ux_utility_semaphore_create Create a semaphore */
85 /* (ux_hcd_entry_function) HCD entry function */
86 /* */
87 /* CALLED BY */
88 /* */
89 /* USBX Components */
90 /* */
91 /* RELEASE HISTORY */
92 /* */
93 /* DATE NAME DESCRIPTION */
94 /* */
95 /* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */
96 /* 09-30-2020 Chaoqiong Xiao Modified comment(s), */
97 /* optimized based on compile */
98 /* definitions, */
99 /* resulting in version 6.1 */
100 /* 02-02-2021 Chaoqiong Xiao Modified comment(s), */
101 /* added a new parameter to */
102 /* return created device, */
103 /* resulting in version 6.1.4 */
104 /* 01-31-2022 Chaoqiong Xiao Modified comment(s), */
105 /* added standalone support, */
106 /* reset device power source, */
107 /* resulting in version 6.1.10 */
108 /* 07-29-2022 Chaoqiong Xiao Modified comment(s), */
109 /* freed shared device config */
110 /* descriptor after enum scan, */
111 /* resulting in version 6.1.12 */
112 /* */
113 /**************************************************************************/
_ux_host_stack_new_device_create(UX_HCD * hcd,UX_DEVICE * device_owner,UINT port_index,UINT device_speed,UINT port_max_power,UX_DEVICE ** created_device)114 UINT _ux_host_stack_new_device_create(UX_HCD *hcd, UX_DEVICE *device_owner,
115 UINT port_index, UINT device_speed,
116 UINT port_max_power,
117 UX_DEVICE **created_device)
118 {
119
120 UX_DEVICE *device;
121 UINT status;
122 UX_ENDPOINT *control_endpoint;
123
124
125 #if UX_MAX_DEVICES > 1
126 /* Verify the number of devices attached to the HCD already. Normally a HCD
127 can have up to 127 devices but that can be tailored. */
128 if (hcd -> ux_hcd_nb_devices > UX_MAX_USB_DEVICES)
129 {
130
131 /* Error trap. */
132 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ENUMERATOR, UX_TOO_MANY_DEVICES);
133
134 /* If trace is enabled, insert this event into the trace buffer. */
135 UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TOO_MANY_DEVICES, 0, 0, 0, UX_TRACE_ERRORS, 0, 0)
136
137 return(UX_TOO_MANY_DEVICES);
138 }
139 #endif
140
141 /* Get a new device container to store this new device. */
142 device = _ux_host_stack_new_device_get();
143 if (device == UX_NULL)
144 {
145
146 /* Error trap. */
147 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ENUMERATOR, UX_TOO_MANY_DEVICES);
148
149 /* If trace is enabled, insert this event into the trace buffer. */
150 UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TOO_MANY_DEVICES, 0, 0, 0, UX_TRACE_ERRORS, 0, 0)
151
152 return(UX_TOO_MANY_DEVICES);
153 }
154
155 /* Store the device instance. */
156 *created_device = device;
157
158 /* Increment the number of devices on this bus. */
159 hcd -> ux_hcd_nb_devices++;
160
161 /* If trace is enabled, insert this event into the trace buffer. */
162 UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_NEW_DEVICE_CREATE, hcd, device_owner, port_index, device, UX_TRACE_HOST_STACK_EVENTS, 0, 0)
163
164 /* At this stage the device is attached but not configured.
165 we don't have to worry about power consumption yet.
166 Initialize the device structure. */
167 device -> ux_device_handle = (ULONG) (ALIGN_TYPE) device;
168 device -> ux_device_state = UX_DEVICE_ATTACHED;
169 device -> ux_device_address = 0;
170 device -> ux_device_speed = device_speed;
171 UX_DEVICE_MAX_POWER_SET(device, port_max_power);
172 UX_DEVICE_PARENT_SET(device, device_owner);
173 UX_DEVICE_HCD_SET(device, hcd);
174 UX_DEVICE_PORT_LOCATION_SET(device, port_index);
175 device -> ux_device_power_source = UX_DEVICE_BUS_POWERED;
176
177 /* Create a semaphore for the device. This is to protect endpoint 0 mostly for OTG HNP polling. The initial count is 1 as
178 a mutex mechanism. */
179 status = _ux_host_semaphore_create(&device -> ux_device_protection_semaphore, "ux_host_endpoint0_semaphore", 1);
180
181 /* Check semaphore creation. */
182 if (status != UX_SUCCESS)
183 {
184
185 /* Return error. Device resources that have been allocated until this
186 point should be freed by the caller via _ux_host_stack_device_resources_free. */
187 return(UX_SEMAPHORE_ERROR);
188 }
189
190 /* Initialize the default control endpoint permanently attached
191 to the device. */
192 control_endpoint = &device -> ux_device_control_endpoint;
193 control_endpoint -> ux_endpoint = (ULONG) (ALIGN_TYPE) control_endpoint;
194 control_endpoint -> ux_endpoint_next_endpoint = UX_NULL;
195 control_endpoint -> ux_endpoint_interface = UX_NULL;
196 control_endpoint -> ux_endpoint_device = device;
197 control_endpoint -> ux_endpoint_transfer_request.ux_transfer_request_endpoint = control_endpoint;
198
199 /* Create a semaphore for this endpoint to be attached to its transfer request. */
200 status = _ux_host_semaphore_create(&control_endpoint -> ux_endpoint_transfer_request.ux_transfer_request_semaphore, "ux_host_transfer_request_semaphore", 0);
201
202 /* Check semaphore creation. */
203 if (status != UX_SUCCESS)
204 {
205
206 /* Return error. Device resources that have been allocated until this
207 point should be freed by the caller via _ux_host_stack_device_resources_free. */
208 return(UX_SEMAPHORE_ERROR);
209 }
210
211 /* If the device is running in high speed the default max packet size for the control endpoint is 64.
212 All other speeds the size is 8. */
213 if (device_speed == UX_HIGH_SPEED_DEVICE)
214 control_endpoint -> ux_endpoint_descriptor.wMaxPacketSize = UX_DEFAULT_HS_MPS;
215 else
216 control_endpoint -> ux_endpoint_descriptor.wMaxPacketSize = UX_DEFAULT_MPS;
217
218 /* Create the default control endpoint at the HCD level. */
219 status = hcd -> ux_hcd_entry_function(hcd, UX_HCD_CREATE_ENDPOINT, (VOID *) control_endpoint);
220
221 #if defined(UX_HOST_STANDALONE)
222 if (status == UX_SUCCESS)
223 {
224
225 /* Now control endpoint is ready, set state to running. */
226 control_endpoint -> ux_endpoint_state = UX_ENDPOINT_RUNNING;
227
228 /* Setup default control request timeout. */
229 control_endpoint -> ux_endpoint_transfer_request.ux_transfer_request_timeout_value =
230 UX_MS_TO_TICK_NON_ZERO(UX_CONTROL_TRANSFER_TIMEOUT);
231
232 /* Set default control request mode to wait. */
233 control_endpoint -> ux_endpoint_transfer_request.ux_transfer_request_flags |=
234 UX_TRANSFER_FLAG_AUTO_WAIT;
235 }
236
237 /* Enumeration steps will be done in task state machine. */
238
239 #else
240
241 /* Going on to do enumeration (requests). */
242 if (status == UX_SUCCESS)
243 {
244
245 /* Now control endpoint is ready, set state to running. */
246 control_endpoint -> ux_endpoint_state = UX_ENDPOINT_RUNNING;
247
248 /* Set the address of the device. The first time a USB device is
249 accessed, it responds to the address 0. We need to change the address
250 to a free device address between 1 and 127 ASAP. */
251 status = _ux_host_stack_device_address_set(device);
252 if (status == UX_SUCCESS)
253 {
254
255 /* Get the device descriptor. */
256 status = _ux_host_stack_device_descriptor_read(device);
257 if (status == UX_SUCCESS)
258 {
259
260 /* Get the configuration descriptor(s) for the device
261 and parse all the configuration, interface, endpoints... */
262 status = _ux_host_stack_configuration_enumerate(device);
263 }
264 }
265 }
266
267 /* Check the status of the previous operations. If there was an
268 error during any of the phases, the device resources must be
269 freed based on if we want to retry. */
270 if (status == UX_SUCCESS)
271 {
272
273 /* The device, configuration(s), interface(s), endpoint(s) are
274 now in order for this device to work. No configuration is set
275 yet. First we need to find a class driver that wants to own
276 it. There is no need to have an orphan device in a configured state. */
277 status = _ux_host_stack_class_device_scan(device);
278 if (status == UX_NO_CLASS_MATCH)
279 {
280
281 status = _ux_host_stack_class_interface_scan(device);
282
283 }
284
285 /* Check if there is unnecessary resource to free. */
286 if (device -> ux_device_packed_configuration &&
287 device -> ux_device_packed_configuration_keep_count == 0)
288 {
289 _ux_utility_memory_free(device -> ux_device_packed_configuration);
290 device -> ux_device_packed_configuration = UX_NULL;
291 }
292
293 /* If trace is enabled, register this object. */
294 UX_TRACE_OBJECT_REGISTER(UX_TRACE_HOST_OBJECT_TYPE_DEVICE, hcd, device_owner, port_index, 0);
295 }
296 #endif
297
298 /* Return status. If there's an error, device resources that have been
299 allocated until this point should be freed by the caller via _ux_host_stack_device_resources_free. */
300 return(status);
301 }
302