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 /** Audio 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_audio.h"
29 #include "ux_host_stack.h"
30
31
32 /**************************************************************************/
33 /* */
34 /* FUNCTION RELEASE */
35 /* */
36 /* _ux_host_class_audio_activate PORTABLE C */
37 /* 6.3.0 */
38 /* AUTHOR */
39 /* */
40 /* Chaoqiong Xiao, Microsoft Corporation */
41 /* */
42 /* DESCRIPTION */
43 /* */
44 /* This function activates the audio class. It may be called twice by */
45 /* the same device if there is a audio control interface to this */
46 /* device. */
47 /* */
48 /* INPUT */
49 /* */
50 /* command Pointer to command */
51 /* */
52 /* OUTPUT */
53 /* */
54 /* Completion Status */
55 /* */
56 /* CALLS */
57 /* */
58 /* _ux_host_class_audio_configure Configure the audio class */
59 /* _ux_host_class_audio_descriptor_get Get audio descriptor */
60 /* _ux_host_class_audio_device_controls_list_get Get controls list */
61 /* _ux_host_class_audio_device_type_get Get device type */
62 /* _ux_host_class_audio_streaming_terminal_get Get streaming terminal */
63 /* _ux_host_stack_class_instance_create Create class instance */
64 /* _ux_host_stack_class_instance_destroy Destroy class instance */
65 /* _ux_utility_memory_allocate Allocate a memory block */
66 /* _ux_host_mutex_create Create protection mutex */
67 /* */
68 /* CALLED BY */
69 /* */
70 /* Audio Class */
71 /* */
72 /* RELEASE HISTORY */
73 /* */
74 /* DATE NAME DESCRIPTION */
75 /* */
76 /* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */
77 /* 09-30-2020 Chaoqiong Xiao Modified comment(s), */
78 /* resulting in version 6.1 */
79 /* 01-31-2022 Chaoqiong Xiao Modified comment(s), */
80 /* refined macros names, */
81 /* resulting in version 6.1.10 */
82 /* 07-29-2022 Chaoqiong Xiao Modified comment(s), */
83 /* fixed parameter/variable */
84 /* names conflict C++ keyword, */
85 /* added interrupt support, */
86 /* protect reentry with mutex, */
87 /* added feedback support, */
88 /* refined error handling, */
89 /* resulting in version 6.1.12 */
90 /* 10-31-2023 Chaoqiong Xiao Modified comment(s), */
91 /* improved AC AS management, */
92 /* improved error handling, */
93 /* improved descriptors check, */
94 /* resulting in version 6.3.0 */
95 /* */
96 /**************************************************************************/
_ux_host_class_audio_activate(UX_HOST_CLASS_COMMAND * command)97 UINT _ux_host_class_audio_activate(UX_HOST_CLASS_COMMAND *command)
98 {
99
100 UX_INTERFACE *interface_ptr;
101 UX_HOST_CLASS_AUDIO *audio;
102 #if defined(UX_HOST_CLASS_AUDIO_INTERRUPT_SUPPORT)
103 UX_HOST_CLASS_AUDIO_AC *ac;
104 UX_ENDPOINT *endp;
105 UX_TRANSFER *trans;
106 UCHAR *desc;
107 UCHAR *iad;
108 UCHAR *uac10_ifd;
109 UCHAR *uac10_acd;
110 UCHAR desc_type;
111 UCHAR desc_length;
112 ULONG pos;
113 ULONG as_count;
114 #endif
115 UINT status;
116
117
118 /* The audio is always activated by the interface descriptor and not the
119 device descriptor. */
120 interface_ptr = (UX_INTERFACE *) command -> ux_host_class_command_container;
121
122 /* Check the subclass of the new device. If it is a Audio Control Interface,
123 we don't need to create an instance of this function. When we get the streaming interface,
124 we will search the audio control interface for the device. */
125 if (interface_ptr -> ux_interface_descriptor.bInterfaceSubClass == UX_HOST_CLASS_AUDIO_SUBCLASS_CONTROL)
126 {
127 #if defined(UX_HOST_CLASS_AUDIO_INTERRUPT_SUPPORT)
128
129 /* Allocate memory for AC instance. */
130 ac = (UX_HOST_CLASS_AUDIO_AC *) _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_HOST_CLASS_AUDIO_AC));
131 if (ac == UX_NULL)
132 return(UX_MEMORY_INSUFFICIENT);
133
134 /* Get descriptor to check AC/AS interfaces layouts. */
135 ac -> ux_host_class_audio_device = interface_ptr -> ux_interface_configuration -> ux_configuration_device;
136 status = _ux_host_class_audio_descriptor_get((UX_HOST_CLASS_AUDIO *)ac);
137 if (status != UX_SUCCESS)
138 {
139 _ux_utility_memory_free(ac);
140 return(status);
141 }
142
143 /* Search for IAD or UAC1.0 AC header descriptor. */
144 as_count = 0;
145 iad = UX_NULL;
146 uac10_acd = UX_NULL;
147 desc = ac -> ux_host_class_audio_configuration_descriptor;
148 for (pos = 0; pos < ac -> ux_host_class_audio_configuration_descriptor_length;)
149 {
150
151 /* Get bLength@0, bDescriptorType@1. */
152 desc_length = *(desc);
153 desc_type = *(desc + 1);
154
155 /* Check length error. */
156 if (desc_length < 2)
157 {
158 _ux_utility_memory_free(ac);
159 return(UX_DESCRIPTOR_CORRUPTED);
160 }
161
162 /* Check descriptor type. */
163 switch(desc_type)
164 {
165 case UX_INTERFACE_ASSOCIATION_DESCRIPTOR_ITEM:
166
167 /* Check bFirstInterface@2, must match AC interface if exist. */
168 if (desc[2] == interface_ptr -> ux_interface_descriptor.bInterfaceNumber)
169 iad = desc;
170 break;
171
172 case UX_INTERFACE_DESCRIPTOR_ITEM:
173
174 /* Check bInterfaceNumber@2, if no IAD. */
175 if (desc[2] == interface_ptr -> ux_interface_descriptor.bInterfaceNumber)
176 uac10_ifd = desc;
177 else
178 uac10_ifd = UX_NULL;
179 break;
180
181 case UX_HOST_CLASS_AUDIO_CS_INTERFACE:
182
183 /* Check bDescriptorSubtype@3, if inside correct interface. */
184 if (uac10_ifd)
185 {
186 if (desc[2] == UX_HOST_CLASS_AUDIO_CS_HEADER)
187 uac10_acd = desc;
188 }
189 break;
190
191 default:
192 break;
193
194 } /* switch(desc_type) */
195
196 /* Check if IAD exists. */
197 if (iad)
198 {
199
200 /* In this case, bFirstInterface@2 is AC, followed ASes. */
201 /* Use bInterfaceCount@3. */
202 if (iad[3] <= 1)
203 {
204 _ux_utility_memory_free(ac);
205 return(UX_DESCRIPTOR_CORRUPTED);
206 }
207 as_count = (ULONG)iad[3] - 1;
208 break;
209 }
210
211 /* Check if AC header exists. */
212 if (uac10_acd)
213 {
214
215 /* In this case, bInCollection@7 is AS count. */
216 if (desc[7] < 1 || (8 + desc[7]) != desc[0])
217 {
218 _ux_utility_memory_free(ac);
219 return(UX_DESCRIPTOR_CORRUPTED);
220 }
221 as_count = (ULONG)uac10_acd[7];
222 break;
223 }
224
225 /* Move pos and descriptor pointer. */
226 pos += desc_length;
227 desc += desc_length;
228 }
229
230 /* If there are too many ASes, reallocate memory. */
231 if (as_count > 2)
232 {
233 _ux_utility_memory_free(ac);
234
235 /* as_count - 2 < 255 - 2, so (as_count - 2) * sizeof(UX_HOST_CLASS_AUDIO*) does not overflow. */
236 /* sizeof(UX_HOST_CLASS_AUDIO_AC) is not big (< 255), total memory size calculation does not overflow. */
237 ac = (UX_HOST_CLASS_AUDIO_AC *) _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY,
238 sizeof(UX_HOST_CLASS_AUDIO_AC) + (as_count - 2) * sizeof(UX_HOST_CLASS_AUDIO*));
239 if (ac == UX_NULL)
240 return(UX_MEMORY_INSUFFICIENT);
241 }
242
243 /* Get interrupt endpoint. */
244 endp = interface_ptr -> ux_interface_first_endpoint;
245 while(endp)
246 {
247 if (((endp -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) ==
248 UX_INTERRUPT_ENDPOINT) &&
249 ((endp -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION)))
250 {
251 ac -> ux_host_class_audio_interrupt_endpoint = endp;
252 trans = &endp -> ux_endpoint_transfer_request;
253
254 /* Allocate buffer. */
255 trans -> ux_transfer_request_data_pointer =
256 _ux_utility_memory_allocate(UX_NO_ALIGN, UX_CACHE_SAFE_MEMORY,
257 trans -> ux_transfer_request_packet_length);
258 if (trans -> ux_transfer_request_data_pointer == UX_NULL)
259 {
260 _ux_utility_memory_free(ac);
261 return(UX_MEMORY_INSUFFICIENT);
262 }
263
264 /* This transfer always have the IN direction. */
265 trans -> ux_transfer_request_type = UX_REQUEST_IN;
266
267 /* This transfer always try read a full packet. */
268 trans -> ux_transfer_request_requested_length =
269 trans -> ux_transfer_request_packet_length;
270
271 break;
272 }
273 endp = endp -> ux_endpoint_next_endpoint;
274 }
275
276 /* If there is no interrupt endpoint, that's fine. */
277 /* Fill the fields. */
278 ac -> ux_host_class_audio_class = command -> ux_host_class_command_class_ptr;
279 ac -> ux_host_class_audio_interface = interface_ptr;
280 ac -> ux_host_class_audio_device = interface_ptr -> ux_interface_configuration -> ux_configuration_device;
281
282 ac -> ux_host_class_audio_configuration_descriptor = ac -> ux_host_class_audio_device -> ux_device_packed_configuration;
283 ac -> ux_host_class_audio_configuration_descriptor_length = interface_ptr -> ux_interface_configuration -> ux_configuration_descriptor.wTotalLength;
284 ac -> ux_host_class_audio_as_count = as_count;
285
286 /* Link to interface and class. */
287 interface_ptr -> ux_interface_class_instance = (VOID *)ac;
288 _ux_host_stack_class_instance_create(ac -> ux_host_class_audio_class, (VOID *)ac);
289
290 /* If all is fine and the AC is mounted, we may need to inform the application
291 if a function has been programmed in the system structure. */
292 if (_ux_system_host -> ux_system_host_change_function != UX_NULL)
293 _ux_system_host -> ux_system_host_change_function(UX_DEVICE_INSERTION, ac -> ux_host_class_audio_class, (VOID *) ac);
294
295 /* If trace is enabled, insert this event into the trace buffer. */
296 UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_AUDIO_ACTIVATE, ac, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0)
297
298 /* If trace is enabled, register this object. */
299 UX_TRACE_OBJECT_REGISTER(UX_TRACE_HOST_OBJECT_TYPE_INTERFACE, ac, 0, 0, 0)
300 #endif
301
302 return(UX_SUCCESS);
303 }
304
305 /* Obtain memory for this class instance. */
306 audio = (UX_HOST_CLASS_AUDIO *) _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_HOST_CLASS_AUDIO));
307 if (audio == UX_NULL)
308 return(UX_MEMORY_INSUFFICIENT);
309
310 /* Store the class container into this instance. */
311 audio -> ux_host_class_audio_class = command -> ux_host_class_command_class_ptr;
312
313 /* Store the interface container into the audio class instance. */
314 audio -> ux_host_class_audio_streaming_interface = interface_ptr;
315
316 /* Store the device container into the audio class instance. */
317 audio -> ux_host_class_audio_device = interface_ptr -> ux_interface_configuration -> ux_configuration_device;
318
319 /* This instance of the device must also be stored in the interface container. */
320 interface_ptr -> ux_interface_class_instance = (VOID *) audio;
321
322 /* Create this class instance. */
323 _ux_host_stack_class_instance_create(audio -> ux_host_class_audio_class, (VOID *) audio);
324
325 /* Configure the audio. */
326 status = _ux_host_class_audio_configure(audio);
327
328 /* Get the audio descriptor (all the class specific stuff) and memorize them
329 as we will need these descriptors to change settings. */
330 if (status == UX_SUCCESS)
331 status = _ux_host_class_audio_descriptor_get(audio);
332
333 /* Locate the audio device streaming terminal. */
334 if (status == UX_SUCCESS)
335 status = _ux_host_class_audio_streaming_terminal_get(audio);
336
337 /* Get the audio device type. Here we only support input and output devices. */
338 if (status == UX_SUCCESS)
339 status = _ux_host_class_audio_device_type_get(audio);
340
341 /* Get the audio device controls. */
342 #if !defined(UX_HOST_CLASS_AUDIO_DISABLE_CONTROLS)
343 if (status == UX_SUCCESS)
344 status = _ux_host_class_audio_device_controls_list_get(audio);
345 #endif
346
347 /* Create the mutex to protect multiple threads from accessing the same
348 audio instance. */
349 if (status == UX_SUCCESS)
350 {
351 status = _ux_host_mutex_create(&audio -> ux_host_class_audio_mutex, "ux_hot_class_audio_mutex");
352 if (status != UX_SUCCESS)
353 status = UX_MUTEX_ERROR;
354 }
355
356 #if defined(UX_HOST_CLASS_AUDIO_INTERRUPT_SUPPORT)
357
358 /* Find AC and link it if there is no error. */
359 if (status == UX_SUCCESS)
360 {
361
362 ac = (UX_HOST_CLASS_AUDIO_AC *)audio -> ux_host_class_audio_class -> ux_host_class_first_instance;
363 while(ac)
364 {
365
366 /* Check interface number to see if it's right AC. */
367 if ((ac -> ux_host_class_audio_interface -> ux_interface_configuration ==
368 interface_ptr -> ux_interface_configuration) &&
369 (ac -> ux_host_class_audio_interface -> ux_interface_descriptor.bInterfaceNumber ==
370 audio -> ux_host_class_audio_control_interface_number) &&
371 (ac -> ux_host_class_audio_interface -> ux_interface_descriptor.bInterfaceClass ==
372 UX_HOST_CLASS_AUDIO_CLASS) &&
373 (ac -> ux_host_class_audio_interface -> ux_interface_descriptor.bInterfaceSubClass ==
374 UX_HOST_CLASS_AUDIO_SUBCLASS_CONTROL))
375 {
376
377 /* Save AS to AC controlled list. */
378 for (pos = 0; pos < ac -> ux_host_class_audio_as_count; pos ++)
379 {
380 if (ac -> ux_host_class_audio_as[pos] == UX_NULL)
381 {
382
383 /* This AC is for current AS. */
384 audio -> ux_host_class_audio_ac = ac;
385 audio -> ux_host_class_audio_ac_as = pos;
386 ac -> ux_host_class_audio_as[pos] = audio;
387 break;
388 }
389 }
390
391 /* If there is no place for AS, something wrong. */
392 if (pos >= ac -> ux_host_class_audio_as_count)
393 {
394 status = UX_DESCRIPTOR_CORRUPTED;
395 break;
396 }
397 }
398
399 /* If AC is found and linked, done. */
400 if (audio -> ux_host_class_audio_ac != UX_NULL)
401 break;
402
403 /* If there is error, break. */
404 if (status != UX_SUCCESS)
405 break;
406
407 /* Check next AC/AS. */
408 ac = (UX_HOST_CLASS_AUDIO_AC *)ac -> ux_host_class_audio_next_instance;
409 }
410 }
411 #endif
412
413 /* Activate is done success. */
414 if (status == UX_SUCCESS)
415 {
416
417 /* Mark the audio as live now. */
418 audio -> ux_host_class_audio_state = UX_HOST_CLASS_INSTANCE_LIVE;
419
420 /* If all is fine and the device is mounted, we may need to inform the application
421 if a function has been programmed in the system structure. */
422 if (_ux_system_host -> ux_system_host_change_function != UX_NULL)
423 {
424
425 /* Call system change function. */
426 _ux_system_host -> ux_system_host_change_function(UX_DEVICE_INSERTION, audio -> ux_host_class_audio_class, (VOID *) audio);
427 }
428
429 /* If trace is enabled, insert this event into the trace buffer. */
430 UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_AUDIO_ACTIVATE, audio, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0)
431
432 /* If trace is enabled, register this object. */
433 UX_TRACE_OBJECT_REGISTER(UX_TRACE_HOST_OBJECT_TYPE_INTERFACE, audio, 0, 0, 0)
434
435 /* Return completion status. */
436 return(UX_SUCCESS);
437 }
438
439 /* Error trap. */
440 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, status);
441
442 /* If trace is enabled, insert this event into the trace buffer. */
443 UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, status, audio, 0, 0, UX_TRACE_ERRORS, 0, 0)
444
445 #if defined(UX_HOST_CLASS_AUDIO_INTERRUPT_SUPPORT)
446
447 /* When there is error, AC is not linked (last possible error), no need to unlink. */
448 #endif
449
450 /* Error, destroy the class instance. */
451 _ux_host_stack_class_instance_destroy(audio -> ux_host_class_audio_class, (VOID *) audio);
452
453 /* Free resources. */
454 #if defined(UX_HOST_CLASS_AUDIO_FEEDBACK_SUPPORT)
455 if (audio -> ux_host_class_audio_feedback_endpoint -> ux_endpoint_transfer_request.ux_transfer_request_data_pointer)
456 _ux_utility_memory_free(audio -> ux_host_class_audio_feedback_endpoint -> ux_endpoint_transfer_request.ux_transfer_request_data_pointer);
457 #endif
458 _ux_utility_memory_free(audio);
459
460 /* Unlink instance from interface. */
461 interface_ptr -> ux_interface_class_instance = UX_NULL;
462
463 /* Return the error. */
464 return(status);
465 }
466