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