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