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