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 /** Device Stack */
18 /** */
19 /**************************************************************************/
20 /**************************************************************************/
21
22 #define UX_SOURCE_CODE
23
24
25 /* Include necessary system files. */
26
27 #include "ux_api.h"
28 #include "ux_device_stack.h"
29
30 UX_COMPILE_TIME_ASSERT(!UX_OVERFLOW_CHECK_MULC_ULONG(sizeof(UX_SLAVE_CLASS), UX_MAX_SLAVE_CLASS_DRIVER), UX_MAX_SLAVE_CLASS_DRIVER_mul_ovf)
31
32 /* Define the names of all the USB Classes of USBX. */
33
34 UCHAR _ux_system_slave_class_storage_name[] = "ux_slave_class_storage";
35 UCHAR _ux_system_slave_class_cdc_acm_name[] = "ux_slave_class_cdc_acm";
36 UCHAR _ux_system_slave_class_dpump_name[] = "ux_slave_class_dpump";
37 UCHAR _ux_system_slave_class_pima_name[] = "ux_slave_class_pima";
38 UCHAR _ux_system_slave_class_hid_name[] = "ux_slave_class_hid";
39 UCHAR _ux_system_slave_class_rndis_name[] = "ux_slave_class_rndis";
40 UCHAR _ux_system_slave_class_cdc_ecm_name[] = "ux_slave_class_cdc_ecm";
41 UCHAR _ux_system_slave_class_dfu_name[] = "ux_slave_class_dfu";
42 UCHAR _ux_system_slave_class_audio_name[] = "ux_slave_class_audio";
43
44 UCHAR _ux_system_device_class_printer_name[] = "ux_device_class_printer";
45 UCHAR _ux_system_device_class_ccid_name[] = "ux_device_class_ccid";
46 UCHAR _ux_system_device_class_video_name[] = "ux_device_class_video";
47
48 /* Define USBX Host variable. */
49 UX_SYSTEM_SLAVE *_ux_system_slave;
50
51 /**************************************************************************/
52 /* */
53 /* FUNCTION RELEASE */
54 /* */
55 /* _ux_device_stack_initialize PORTABLE C */
56 /* 6.3.0 */
57 /* AUTHOR */
58 /* */
59 /* Chaoqiong Xiao, Microsoft Corporation */
60 /* */
61 /* DESCRIPTION */
62 /* */
63 /* This function initializes the generic portion of the device side of */
64 /* USBX. */
65 /* */
66 /* INPUT */
67 /* */
68 /* device_framework_high_speed Pointer to high speed FW */
69 /* device_framework_length_high_speed Length of high speed FW */
70 /* device_framework_full_speed Pointer to full speed FW */
71 /* device_framework_length_full_speed Length of full speed FW */
72 /* string_framework Pointer to string FW */
73 /* string_framework_length Length of string FW */
74 /* language_id_framework Pointer to language ID FW */
75 /* language_id_framework_length Length of language ID FW */
76 /* (ux_system_slave_change_function) Pointer to callback function */
77 /* for device changes */
78 /* */
79 /* OUTPUT */
80 /* */
81 /* Completion Status */
82 /* */
83 /* CALLS */
84 /* */
85 /* _ux_utility_memory_allocate Allocate memory */
86 /* _ux_utility_memory_free Free memory */
87 /* _ux_utility_semaphore_create Create semaphore */
88 /* _ux_utility_semaphore_delete Delete semaphore */
89 /* */
90 /* CALLED BY */
91 /* */
92 /* Application */
93 /* */
94 /* RELEASE HISTORY */
95 /* */
96 /* DATE NAME DESCRIPTION */
97 /* */
98 /* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */
99 /* 09-30-2020 Chaoqiong Xiao Modified comment(s), */
100 /* optimized based on compile */
101 /* definitions, */
102 /* resulting in version 6.1 */
103 /* 01-31-2022 Chaoqiong Xiao Modified comment(s), */
104 /* added standalone support, */
105 /* added printer support, */
106 /* resulting in version 6.1.10 */
107 /* 04-25-2022 Chaoqiong Xiao Modified comment(s), */
108 /* added CCID support, */
109 /* added video support, */
110 /* resulting in version 6.1.11 */
111 /* 10-31-2023 Chaoqiong Xiao Modified comment(s), */
112 /* added a new mode to manage */
113 /* endpoint buffer in classes, */
114 /* resulting in version 6.3.0 */
115 /* */
116 /**************************************************************************/
_ux_device_stack_initialize(UCHAR * device_framework_high_speed,ULONG device_framework_length_high_speed,UCHAR * device_framework_full_speed,ULONG device_framework_length_full_speed,UCHAR * string_framework,ULONG string_framework_length,UCHAR * language_id_framework,ULONG language_id_framework_length,UINT (* ux_system_slave_change_function)(ULONG))117 UINT _ux_device_stack_initialize(UCHAR * device_framework_high_speed, ULONG device_framework_length_high_speed,
118 UCHAR * device_framework_full_speed, ULONG device_framework_length_full_speed,
119 UCHAR * string_framework, ULONG string_framework_length,
120 UCHAR * language_id_framework, ULONG language_id_framework_length,
121 UINT (*ux_system_slave_change_function)(ULONG))
122 {
123 UX_SLAVE_DEVICE *device;
124 UX_SLAVE_ENDPOINT *endpoints_pool;
125 UX_SLAVE_INTERFACE *interfaces_pool;
126 UX_SLAVE_TRANSFER *transfer_request;
127 UINT status;
128 ULONG interfaces_found;
129 ULONG endpoints_found;
130 #if !defined(UX_DEVICE_INITIALIZE_FRAMEWORK_SCAN_DISABLE)
131 ULONG max_interface_number;
132 ULONG local_interfaces_found;
133 ULONG local_endpoints_found;
134 ULONG endpoints_in_interface_found;
135 UCHAR *device_framework;
136 ULONG device_framework_length;
137 UCHAR descriptor_type;
138 ULONG descriptor_length;
139 #endif
140 UCHAR *memory;
141
142 /* If trace is enabled, insert this event into the trace buffer. */
143 UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_STACK_INITIALIZE, 0, 0, 0, 0, UX_TRACE_DEVICE_STACK_EVENTS, 0, 0)
144
145 /* Get the pointer to the device. */
146 device = &_ux_system_slave -> ux_system_slave_device;
147
148 /* Store the high speed device framework address and length in the project structure. */
149 _ux_system_slave -> ux_system_slave_device_framework_high_speed = device_framework_high_speed;
150 _ux_system_slave -> ux_system_slave_device_framework_length_high_speed = device_framework_length_high_speed;
151
152 /* Store the string framework address and length in the project structure. */
153 _ux_system_slave -> ux_system_slave_device_framework_full_speed = device_framework_full_speed;
154 _ux_system_slave -> ux_system_slave_device_framework_length_full_speed = device_framework_length_full_speed;
155
156 /* Store the string framework address and length in the project structure. */
157 _ux_system_slave -> ux_system_slave_string_framework = string_framework;
158 _ux_system_slave -> ux_system_slave_string_framework_length = string_framework_length;
159
160 /* Store the language ID list in the project structure. */
161 _ux_system_slave -> ux_system_slave_language_id_framework = language_id_framework;
162 _ux_system_slave -> ux_system_slave_language_id_framework_length = language_id_framework_length;
163
164 /* Store the max number of slave class drivers in the project structure. */
165 UX_SYSTEM_DEVICE_MAX_CLASS_SET(UX_MAX_SLAVE_CLASS_DRIVER);
166
167 /* Store the device state change function callback. */
168 _ux_system_slave -> ux_system_slave_change_function = ux_system_slave_change_function;
169
170 /* Allocate memory for the classes.
171 * sizeof(UX_SLAVE_CLASS) * UX_MAX_SLAVE_CLASS_DRIVER) overflow is checked
172 * outside of the function.
173 */
174 memory = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_SLAVE_CLASS) * UX_MAX_SLAVE_CLASS_DRIVER);
175 if (memory == UX_NULL)
176 return(UX_MEMORY_INSUFFICIENT);
177
178 /* Save this memory allocation in the USBX project. */
179 _ux_system_slave -> ux_system_slave_class_array = (UX_SLAVE_CLASS *) ((void *) memory);
180
181 /* Allocate some memory for the Control Endpoint. First get the address of the transfer request for the
182 control endpoint. */
183 transfer_request = &device -> ux_slave_device_control_endpoint.ux_slave_endpoint_transfer_request;
184
185 /* Acquire a buffer for the size of the endpoint. */
186 transfer_request -> ux_slave_transfer_request_data_pointer =
187 _ux_utility_memory_allocate(UX_NO_ALIGN, UX_CACHE_SAFE_MEMORY, UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH);
188
189 /* Ensure we have enough memory. */
190 if (transfer_request -> ux_slave_transfer_request_data_pointer == UX_NULL)
191 status = UX_MEMORY_INSUFFICIENT;
192 else
193 status = UX_SUCCESS;
194
195 #if defined(UX_DEVICE_INITIALIZE_FRAMEWORK_SCAN_DISABLE)
196
197 /* No scan, just assign predefined value. */
198 interfaces_found = UX_MAX_SLAVE_INTERFACES;
199 endpoints_found = UX_MAX_DEVICE_ENDPOINTS;
200 #else
201
202 /* Reset all values we are using during the scanning of the framework. */
203 interfaces_found = 0;
204 endpoints_found = 0;
205 max_interface_number = 0;
206
207 /* Go on to scan interfaces if no error. */
208 if (status == UX_SUCCESS)
209 {
210
211 /* We need to determine the maximum number of interfaces and endpoints declared in the device framework.
212 This mechanism requires that both framework behave the same way regarding the number of interfaces
213 and endpoints. */
214 device_framework = _ux_system_slave -> ux_system_slave_device_framework_full_speed;
215 device_framework_length = _ux_system_slave -> ux_system_slave_device_framework_length_full_speed;
216
217 /* Reset all values we are using during the scanning of the framework. */
218 local_interfaces_found = 0;
219 local_endpoints_found = 0;
220 endpoints_in_interface_found = 0;
221
222 /* Parse the device framework and locate interfaces and endpoint descriptor(s). */
223 while (device_framework_length != 0)
224 {
225
226 /* Get the length of this descriptor. */
227 descriptor_length = (ULONG) *device_framework;
228
229 /* And its type. */
230 descriptor_type = *(device_framework + 1);
231
232 /* Check if this is an endpoint descriptor. */
233 switch(descriptor_type)
234 {
235
236 case UX_INTERFACE_DESCRIPTOR_ITEM:
237
238 /* Check if this is alternate setting 0. If not, do not add another interface found.
239 If this is alternate setting 0, reset the endpoints count for this interface. */
240 if (*(device_framework + 3) == 0)
241 {
242
243 /* Add the cumulated number of endpoints in the previous interface. */
244 local_endpoints_found += endpoints_in_interface_found;
245
246 /* Read the number of endpoints for this alternate setting. */
247 endpoints_in_interface_found = (ULONG) *(device_framework + 4);
248
249 /* Increment the number of interfaces found in the current configuration. */
250 local_interfaces_found++;
251 }
252 else
253 {
254
255 /* Compare the number of endpoints found in this non 0 alternate setting. */
256 if (endpoints_in_interface_found < (ULONG) *(device_framework + 4))
257
258 /* Adjust the number of maximum endpoints in this interface. */
259 endpoints_in_interface_found = (ULONG) *(device_framework + 4);
260 }
261
262 /* Check and update max interface number. */
263 if (*(device_framework + 2) > max_interface_number)
264 max_interface_number = *(device_framework + 2);
265
266 break;
267
268 case UX_CONFIGURATION_DESCRIPTOR_ITEM:
269
270 /* Check if the number of interfaces found in this configuration is the maximum so far. */
271 if (local_interfaces_found > interfaces_found)
272
273 /* We need to adjust the number of maximum interfaces. */
274 interfaces_found = local_interfaces_found;
275
276 /* We have a new configuration. We need to reset the number of local interfaces. */
277 local_interfaces_found = 0;
278
279 /* Add the cumulated number of endpoints in the previous interface. */
280 local_endpoints_found += endpoints_in_interface_found;
281
282 /* Check if the number of endpoints found in the previous configuration is the maximum so far. */
283 if (local_endpoints_found > endpoints_found)
284
285 /* We need to adjust the number of maximum endpoints. */
286 endpoints_found = local_endpoints_found;
287
288 /* We have a new configuration. We need to reset the number of local endpoints. */
289 local_endpoints_found = 0;
290 endpoints_in_interface_found = 0;
291
292 break;
293
294 default:
295 break;
296 }
297
298 /* Adjust what is left of the device framework. */
299 device_framework_length -= descriptor_length;
300
301 /* Point to the next descriptor. */
302 device_framework += descriptor_length;
303 }
304
305 /* Add the cumulated number of endpoints in the previous interface. */
306 local_endpoints_found += endpoints_in_interface_found;
307
308 /* Check if the number of endpoints found in the previous interface is the maximum so far. */
309 if (local_endpoints_found > endpoints_found)
310
311 /* We need to adjust the number of maximum endpoints. */
312 endpoints_found = local_endpoints_found;
313
314
315 /* Check if the number of interfaces found in this configuration is the maximum so far. */
316 if (local_interfaces_found > interfaces_found)
317
318 /* We need to adjust the number of maximum interfaces. */
319 interfaces_found = local_interfaces_found;
320
321 /* We do a sanity check on the finding. At least there must be one interface but endpoints are
322 not necessary. */
323 if (interfaces_found == 0)
324 {
325
326 /* Error trap. */
327 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_INIT, UX_DESCRIPTOR_CORRUPTED);
328
329 /* If trace is enabled, insert this event into the trace buffer. */
330 UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, device_framework, 0, 0, UX_TRACE_ERRORS, 0, 0)
331
332 status = UX_DESCRIPTOR_CORRUPTED;
333 }
334
335 /* We do a sanity check on the finding. Max interface number should not exceed limit. */
336 if (status == UX_SUCCESS &&
337 max_interface_number >= UX_MAX_SLAVE_INTERFACES)
338 {
339
340 /* Error trap. */
341 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_INIT, UX_MEMORY_INSUFFICIENT);
342
343 /* If trace is enabled, insert this event into the trace buffer. */
344 UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_MEMORY_INSUFFICIENT, device_framework, 0, 0, UX_TRACE_ERRORS, 0, 0)
345
346 status = UX_MEMORY_INSUFFICIENT;
347 }
348 }
349 #endif
350
351 /* Go on to allocate interfaces pool if no error. */
352 if (status == UX_SUCCESS)
353 {
354
355 /* Memorize both pool sizes. */
356 device -> ux_slave_device_interfaces_pool_number = interfaces_found;
357 device -> ux_slave_device_endpoints_pool_number = endpoints_found;
358
359 /* We assign a pool for the interfaces. */
360 interfaces_pool = _ux_utility_memory_allocate_mulc_safe(UX_NO_ALIGN, UX_REGULAR_MEMORY, interfaces_found, sizeof(UX_SLAVE_INTERFACE));
361 if (interfaces_pool == UX_NULL)
362 status = UX_MEMORY_INSUFFICIENT;
363 else
364
365 /* Save the interface pool address in the device container. */
366 device -> ux_slave_device_interfaces_pool = interfaces_pool;
367 }
368
369 /* Do we need an endpoint pool ? */
370 if (endpoints_found != 0 && status == UX_SUCCESS)
371 {
372
373 /* We assign a pool for the endpoints. */
374 endpoints_pool = _ux_utility_memory_allocate_mulc_safe(UX_NO_ALIGN, UX_REGULAR_MEMORY, endpoints_found, sizeof(UX_SLAVE_ENDPOINT));
375 if (endpoints_pool == UX_NULL)
376 status = UX_MEMORY_INSUFFICIENT;
377 else
378 {
379
380 /* Save the endpoint pool address in the device container. */
381 device -> ux_slave_device_endpoints_pool = endpoints_pool;
382
383 /* We need to assign a transfer buffer to each endpoint. Each endpoint is assigned the
384 maximum buffer size. We also assign the semaphore used by the endpoint to synchronize transfer
385 completion. */
386 while (endpoints_pool < (device -> ux_slave_device_endpoints_pool + endpoints_found))
387 {
388
389 #if UX_DEVICE_ENDPOINT_BUFFER_OWNER == 0
390
391 /* Obtain some memory. */
392 endpoints_pool -> ux_slave_endpoint_transfer_request.ux_slave_transfer_request_data_pointer =
393 _ux_utility_memory_allocate(UX_NO_ALIGN, UX_CACHE_SAFE_MEMORY, UX_SLAVE_REQUEST_DATA_MAX_LENGTH);
394
395 /* Ensure we could allocate memory. */
396 if (endpoints_pool -> ux_slave_endpoint_transfer_request.ux_slave_transfer_request_data_pointer == UX_NULL)
397 {
398 status = UX_MEMORY_INSUFFICIENT;
399 break;
400 }
401 #endif
402
403 /* Create the semaphore for the endpoint. */
404 status = _ux_device_semaphore_create(&endpoints_pool -> ux_slave_endpoint_transfer_request.ux_slave_transfer_request_semaphore,
405 "ux_transfer_request_semaphore", 0);
406
407 /* Check completion status. */
408 if (status != UX_SUCCESS)
409 {
410 status = UX_SEMAPHORE_ERROR;
411 break;
412 }
413
414 /* Next endpoint. */
415 endpoints_pool++;
416 }
417 }
418 }
419 else
420 endpoints_pool = UX_NULL;
421
422 /* Return successful completion. */
423 if (status == UX_SUCCESS)
424 return(UX_SUCCESS);
425
426 /* Free resources when there is error. */
427
428 /* Free device -> ux_slave_device_endpoints_pool. */
429 if (endpoints_pool)
430 {
431
432 /* In error cases creating endpoint resources, endpoints_pool is endpoint that failed.
433 * Previously allocated things should be freed. */
434 while(endpoints_pool >= device -> ux_slave_device_endpoints_pool)
435 {
436
437 /* Delete ux_slave_transfer_request_semaphore. */
438 if (_ux_device_semaphore_created(&endpoints_pool -> ux_slave_endpoint_transfer_request.ux_slave_transfer_request_semaphore))
439 _ux_device_semaphore_delete(&endpoints_pool -> ux_slave_endpoint_transfer_request.ux_slave_transfer_request_semaphore);
440
441 #if UX_DEVICE_ENDPOINT_BUFFER_OWNER == 0
442
443 /* Free ux_slave_transfer_request_data_pointer buffer. */
444 if (endpoints_pool -> ux_slave_endpoint_transfer_request.ux_slave_transfer_request_data_pointer)
445 _ux_utility_memory_free(endpoints_pool -> ux_slave_endpoint_transfer_request.ux_slave_transfer_request_data_pointer);
446 #endif
447
448 /* Move to previous endpoint. */
449 endpoints_pool --;
450 }
451
452 _ux_utility_memory_free(device -> ux_slave_device_endpoints_pool);
453 }
454
455 /* Free device -> ux_slave_device_interfaces_pool. */
456 if (device -> ux_slave_device_interfaces_pool)
457 _ux_utility_memory_free(device -> ux_slave_device_interfaces_pool);
458
459 /* Free device -> ux_slave_device_control_endpoint.ux_slave_endpoint_transfer_request.ux_slave_transfer_request_data_pointer. */
460 if (device -> ux_slave_device_control_endpoint.ux_slave_endpoint_transfer_request.ux_slave_transfer_request_data_pointer)
461 _ux_utility_memory_free(device -> ux_slave_device_control_endpoint.ux_slave_endpoint_transfer_request.ux_slave_transfer_request_data_pointer);
462
463 /* Free _ux_system_slave -> ux_system_slave_class_array. */
464 _ux_utility_memory_free(_ux_system_slave -> ux_system_slave_class_array);
465
466 /* Return completion status. */
467 return(status);
468 }
469
470
471 /**************************************************************************/
472 /* */
473 /* FUNCTION RELEASE */
474 /* */
475 /* _uxe_device_stack_initialize PORTABLE C */
476 /* 6.3.0 */
477 /* AUTHOR */
478 /* */
479 /* Chaoqiong Xiao, Microsoft Corporation */
480 /* */
481 /* DESCRIPTION */
482 /* */
483 /* This function checks errors in device stack initialization */
484 /* function call. */
485 /* */
486 /* INPUT */
487 /* */
488 /* class_name Name of class */
489 /* class_function_entry Class entry function */
490 /* */
491 /* OUTPUT */
492 /* */
493 /* None */
494 /* */
495 /* CALLS */
496 /* */
497 /* _ux_device_stack_initialize Device Stack Initialize */
498 /* */
499 /* CALLED BY */
500 /* */
501 /* Application */
502 /* */
503 /* RELEASE HISTORY */
504 /* */
505 /* DATE NAME DESCRIPTION */
506 /* */
507 /* 10-31-2023 Chaoqiong Xiao Initial Version 6.3.0 */
508 /* */
509 /**************************************************************************/
_uxe_device_stack_initialize(UCHAR * device_framework_high_speed,ULONG device_framework_length_high_speed,UCHAR * device_framework_full_speed,ULONG device_framework_length_full_speed,UCHAR * string_framework,ULONG string_framework_length,UCHAR * language_id_framework,ULONG language_id_framework_length,UINT (* ux_system_slave_change_function)(ULONG))510 UINT _uxe_device_stack_initialize(UCHAR * device_framework_high_speed, ULONG device_framework_length_high_speed,
511 UCHAR * device_framework_full_speed, ULONG device_framework_length_full_speed,
512 UCHAR * string_framework, ULONG string_framework_length,
513 UCHAR * language_id_framework, ULONG language_id_framework_length,
514 UINT (*ux_system_slave_change_function)(ULONG))
515 {
516
517 /* Sanity checks. */
518 if (((device_framework_high_speed == UX_NULL) && (device_framework_length_high_speed != 0)) ||
519 (device_framework_full_speed == UX_NULL) || (device_framework_length_full_speed == 0) ||
520 ((string_framework == UX_NULL) && (string_framework_length != 0)) ||
521 (language_id_framework == UX_NULL) || (language_id_framework_length == 0))
522 return(UX_INVALID_PARAMETER);
523
524 /* Invoke stack initialize function. */
525 return(_ux_device_stack_initialize(device_framework_high_speed, device_framework_length_high_speed,
526 device_framework_full_speed, device_framework_length_full_speed,
527 string_framework, string_framework_length,
528 language_id_framework, language_id_framework_length,
529 ux_system_slave_change_function));
530 }
531