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_streaming_sampling_set         PORTABLE C      */
37 /*                                                           6.2.1        */
38 /*  AUTHOR                                                                */
39 /*                                                                        */
40 /*    Chaoqiong Xiao, Microsoft Corporation                               */
41 /*                                                                        */
42 /*  DESCRIPTION                                                           */
43 /*                                                                        */
44 /*    This function selects the right alternate setting for the audio     */
45 /*    streaming interface based on the RAW (PCM like) sampling values     */
46 /*    specified by the user, and send sampling frequency requests to      */
47 /*    select expected sample rate (if necessary).                         */
48 /*                                                                        */
49 /*  INPUT                                                                 */
50 /*                                                                        */
51 /*    audio                                 Pointer to audio class        */
52 /*    audio_sampling                        Pointer to audio sampling     */
53 /*                                                                        */
54 /*  OUTPUT                                                                */
55 /*                                                                        */
56 /*    Completion Status                                                   */
57 /*                                                                        */
58 /*  CALLS                                                                 */
59 /*                                                                        */
60 /*    _ux_host_class_audio_alternate_setting_locate                       */
61 /*                                          Locate alternate setting      */
62 /*    _ux_host_stack_class_instance_verify  Verify instance is valid      */
63 /*    _ux_host_stack_interface_endpoint_get Get interface endpoint        */
64 /*    _ux_host_stack_interface_setting_select Select interface            */
65 /*    _ux_host_semaphore_get                Get semaphore                 */
66 /*    _ux_host_semaphore_put                Put semaphore                 */
67 /*    _ux_host_mutex_on                     Get mutex                     */
68 /*    _ux_host_mutex_off                    Release mutex                 */
69 /*                                                                        */
70 /*  CALLED BY                                                             */
71 /*                                                                        */
72 /*    Application                                                         */
73 /*    Audio Class                                                         */
74 /*                                                                        */
75 /*  RELEASE HISTORY                                                       */
76 /*                                                                        */
77 /*    DATE              NAME                      DESCRIPTION             */
78 /*                                                                        */
79 /*  05-19-2020     Chaoqiong Xiao           Initial Version 6.0           */
80 /*  09-30-2020     Chaoqiong Xiao           Modified comment(s),          */
81 /*                                            resulting in version 6.1    */
82 /*  01-31-2022     Chaoqiong Xiao           Modified comment(s),          */
83 /*                                            refined macros names,       */
84 /*                                            resulting in version 6.1.10 */
85 /*  04-25-2022     Chaoqiong Xiao           Modified comment(s),          */
86 /*                                            fixed standalone compile,   */
87 /*                                            resulting in version 6.1.11 */
88 /*  07-29-2022     Chaoqiong Xiao           Modified comment(s),          */
89 /*                                            fixed parameter/variable    */
90 /*                                            names conflict C++ keyword, */
91 /*                                            added audio 2.0 support,    */
92 /*                                            internal clean up,          */
93 /*                                            set sample rate if needed,  */
94 /*                                            protect reentry with mutex, */
95 /*                                            fixed error return code,    */
96 /*                                            used endpoints get API,     */
97 /*                                            resulting in version 6.1.12 */
98 /*  03-08-2023     Chaoqiong Xiao           Modified comment(s),          */
99 /*                                            improved frequency check,   */
100 /*                                            resulting in version 6.2.1  */
101 /*                                                                        */
102 /**************************************************************************/
_ux_host_class_audio_streaming_sampling_set(UX_HOST_CLASS_AUDIO * audio,UX_HOST_CLASS_AUDIO_SAMPLING * audio_sampling)103 UINT  _ux_host_class_audio_streaming_sampling_set(UX_HOST_CLASS_AUDIO *audio, UX_HOST_CLASS_AUDIO_SAMPLING *audio_sampling)
104 {
105 
106 UINT                    status;
107 UINT                    alternate_setting;
108 UX_CONFIGURATION        *configuration;
109 UX_INTERFACE            *interface_ptr;
110 UINT                    streaming_interface;
111 UCHAR                   *descriptor;
112 ULONG                   set_frequency;
113 UCHAR                   *control_buffer;
114 UX_DEVICE               *device;
115 UX_TRANSFER             *transfer;
116 ULONG                   frequency = 0;
117 ULONG                   res_bytes;
118 
119     /* If trace is enabled, insert this event into the trace buffer.  */
120     UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_AUDIO_STREAMING_SAMPLING_SET, audio, audio_sampling, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0)
121 
122     /* Ensure the instance is valid.  */
123     if (_ux_host_stack_class_instance_verify(_ux_system_host_class_audio_name, (VOID *) audio) != UX_SUCCESS)
124         return(UX_HOST_CLASS_INSTANCE_UNKNOWN);
125 
126     /* Protect thread reentry to this instance.  */
127     _ux_host_mutex_on(&audio -> ux_host_class_audio_mutex);
128 
129     /* Find the correct alternate setting for the sampling desired.  */
130     status =  _ux_host_class_audio_alternate_setting_locate(audio, audio_sampling, &alternate_setting);
131 
132     /* Did we find the alternate setting?  */
133     if (status != UX_SUCCESS)
134     {
135 
136         /* Unprotect thread reentry to this instance.  */
137         _ux_host_mutex_off(&audio -> ux_host_class_audio_mutex);
138         return(status);
139     }
140 
141     /* We found the alternate setting for the sampling values demanded, now we need
142         to search its container.  */
143     configuration =        audio -> ux_host_class_audio_streaming_interface -> ux_interface_configuration;
144     interface_ptr =        configuration -> ux_configuration_first_interface;
145     streaming_interface =  audio -> ux_host_class_audio_streaming_interface -> ux_interface_descriptor.bInterfaceNumber;
146     device =               audio -> ux_host_class_audio_device;
147 
148     /* Scan all interfaces.  */
149     while (interface_ptr != UX_NULL)
150     {
151 
152         /* We search for both the right interface and alternate setting.  */
153         if ((interface_ptr -> ux_interface_descriptor.bInterfaceNumber == streaming_interface) &&
154             (interface_ptr -> ux_interface_descriptor.bAlternateSetting == alternate_setting))
155         {
156 
157             /* We have found the right interface/alternate setting combination
158                The stack will select it for us.  */
159             status =  _ux_host_stack_interface_setting_select(interface_ptr);
160 
161             /* If the alternate setting for the streaming interface could be selected, we memorize it.  */
162             if (status == UX_SUCCESS)
163             {
164 
165                 /* Memorize the interface.  */
166                 audio -> ux_host_class_audio_streaming_interface =  interface_ptr;
167 
168                 /* Get streaming endpoints.  */
169                 status = _ux_host_class_audio_endpoints_get(audio);
170                 if (status != UX_SUCCESS)
171                 {
172 
173                     /* Unprotect thread reentry to this instance.  */
174                     _ux_host_mutex_off(&audio -> ux_host_class_audio_mutex);
175                     return(status);
176                 }
177 
178                 /* Check if clock frequency needs modification through requests.  */
179                 set_frequency = UX_TRUE;
180                 descriptor = audio -> ux_host_class_audio_sampling_descriptor;
181 #if defined(UX_HOST_CLASS_AUDIO_2_SUPPORT)
182                 if (_ux_host_class_audio_protocol_get(audio) != UX_HOST_CLASS_AUDIO_PROTOCOL_IP_VERSION_01_00)
183                 {
184 
185                     /* Check CSD::bmControls@5.0~1.  */
186                     /* If not programmable, no need to set.  */
187                     if ((descriptor[5] & UX_CLASS_AUDIO20_CONTROL_MASK) !=
188                         UX_CLASS_AUDIO20_CONTROL_PROGRAMMABLE)
189                         set_frequency = UX_FALSE;
190                 }
191                 else
192 #endif
193                 {
194 
195                     /* Check FormatTypeI::bSamFreqType@7, tSamFreq@8, tSamFreq@11.  */
196                     /* If there is only one frequency, no need to set.  */
197                     if ((descriptor[7] == 1) || ((descriptor[7] == 0) &&
198                         (descriptor[8] == descriptor[11]) &&
199                         (descriptor[9] == descriptor[12]) &&
200                         (descriptor[10] == descriptor[13])))
201                         set_frequency = UX_FALSE;
202                 }
203 
204                 /* Send requests to set frequency.  */
205                 if (set_frequency)
206                 {
207 
208                     /* Allocate buffer.  */
209                     control_buffer = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_CACHE_SAFE_MEMORY, 4);
210                     if (control_buffer == UX_NULL)
211                     {
212                         _ux_host_mutex_off(&audio -> ux_host_class_audio_mutex);
213                         return(UX_MEMORY_INSUFFICIENT);
214                     }
215 
216                     /* Get control transfer.  */
217                     transfer = &device -> ux_device_control_endpoint.ux_endpoint_transfer_request;
218 
219                     /* Set data to send.  */
220                     control_buffer[0] = (UCHAR)UX_DW0(audio_sampling -> ux_host_class_audio_sampling_frequency);
221                     control_buffer[1] = (UCHAR)UX_DW1(audio_sampling -> ux_host_class_audio_sampling_frequency);
222                     control_buffer[2] = (UCHAR)UX_DW2(audio_sampling -> ux_host_class_audio_sampling_frequency);
223 
224                     /* Protect the control endpoint semaphore here.  It will be unprotected in the
225                         transfer request function.  */
226                     status =  _ux_host_semaphore_get(&device -> ux_device_protection_semaphore, UX_WAIT_FOREVER);
227                     if (status != UX_SUCCESS)
228                     {
229                         _ux_utility_memory_free(control_buffer);
230                         _ux_host_mutex_off(&audio -> ux_host_class_audio_mutex);
231                         return(status);
232                     }
233 
234 #if defined(UX_HOST_CLASS_AUDIO_2_SUPPORT)
235                     if (_ux_host_class_audio_protocol_get(audio) != UX_HOST_CLASS_AUDIO_PROTOCOL_IP_VERSION_01_00)
236                     {
237 
238                         /* Set last byte.  */
239                         control_buffer[3] = (UCHAR)UX_DW3(audio_sampling -> ux_host_class_audio_sampling_frequency);
240 
241                         /* Create a transfer request for the CUR request.  */
242                         transfer -> ux_transfer_request_data_pointer =      control_buffer;
243                         transfer -> ux_transfer_request_requested_length =  4;
244                         transfer -> ux_transfer_request_function =          UX_CLASS_AUDIO20_CUR;
245                         transfer -> ux_transfer_request_type =              UX_REQUEST_OUT | UX_REQUEST_TARGET_INTERFACE | UX_REQUEST_TYPE_CLASS;
246                         transfer -> ux_transfer_request_value =             UX_CLASS_AUDIO20_CS_SAM_FREQ_CONTROL << 8;
247                         transfer -> ux_transfer_request_index =             audio -> ux_host_class_audio_control_interface_number | ((UINT)descriptor[3] << 8);
248                         status = _ux_host_stack_transfer_request(transfer);
249                         if (status != UX_SUCCESS)
250                         {
251                             _ux_utility_memory_free(control_buffer);
252                             _ux_host_mutex_off(&audio -> ux_host_class_audio_mutex);
253                             return(status);
254                         }
255 
256                         /* Issue CUR request.  */
257                         status =  _ux_host_semaphore_get(&device -> ux_device_protection_semaphore, UX_WAIT_FOREVER);
258                         if (status != UX_SUCCESS)
259                         {
260                             _ux_utility_memory_free(control_buffer);
261                             _ux_host_mutex_off(&audio -> ux_host_class_audio_mutex);
262                             return(status);
263                         }
264 
265                         /* Create a transfer request for the CUR request.  */
266                         transfer -> ux_transfer_request_data_pointer =      control_buffer;
267                         transfer -> ux_transfer_request_requested_length =  4;
268                         transfer -> ux_transfer_request_function =          UX_CLASS_AUDIO20_CUR;
269                         transfer -> ux_transfer_request_type =              UX_REQUEST_IN | UX_REQUEST_TARGET_INTERFACE | UX_REQUEST_TYPE_CLASS;
270                         transfer -> ux_transfer_request_value =             UX_CLASS_AUDIO20_CS_SAM_FREQ_CONTROL << 8;
271                         transfer -> ux_transfer_request_index =             audio -> ux_host_class_audio_control_interface_number | ((UINT)descriptor[3] << 8);
272                         status = _ux_host_stack_transfer_request(transfer);
273                     }
274                     else
275 #endif
276                     {
277 
278                         /* Create a transfer request for the SET_CUR request.  */
279                         transfer -> ux_transfer_request_data_pointer =      control_buffer;
280                         transfer -> ux_transfer_request_requested_length =  3;
281                         transfer -> ux_transfer_request_function =          UX_CLASS_AUDIO10_SET_CUR;
282                         transfer -> ux_transfer_request_type =              UX_REQUEST_OUT | UX_REQUEST_TARGET_ENDPOINT | UX_REQUEST_TYPE_CLASS;
283                         transfer -> ux_transfer_request_value =             UX_CLASS_AUDIO10_EP_SAMPLING_FREQ_CONTROL << 8;
284                         transfer -> ux_transfer_request_index =             audio -> ux_host_class_audio_isochronous_endpoint -> ux_endpoint_descriptor.bEndpointAddress;
285                         status = _ux_host_stack_transfer_request(transfer);
286                         if (status != UX_SUCCESS)
287                         {
288                             _ux_utility_memory_free(control_buffer);
289                             _ux_host_mutex_off(&audio -> ux_host_class_audio_mutex);
290                             return(status);
291                         }
292 
293                         /* Issue GET_CUR request.  */
294                         status =  _ux_host_semaphore_get(&device -> ux_device_protection_semaphore, UX_WAIT_FOREVER);
295                         if (status != UX_SUCCESS)
296                         {
297                             _ux_utility_memory_free(control_buffer);
298                             _ux_host_mutex_off(&audio -> ux_host_class_audio_mutex);
299                             return(status);
300                         }
301 
302                         /* Create a transfer request for the GET_CUR request.  */
303                         transfer -> ux_transfer_request_data_pointer =      control_buffer;
304                         transfer -> ux_transfer_request_requested_length =  3;
305                         transfer -> ux_transfer_request_function =          UX_CLASS_AUDIO10_GET_CUR;
306                         transfer -> ux_transfer_request_type =              UX_REQUEST_IN | UX_REQUEST_TARGET_ENDPOINT | UX_REQUEST_TYPE_CLASS;
307                         transfer -> ux_transfer_request_value =             UX_CLASS_AUDIO10_EP_SAMPLING_FREQ_CONTROL << 8;
308                         transfer -> ux_transfer_request_index =             audio -> ux_host_class_audio_isochronous_endpoint -> ux_endpoint_descriptor.bEndpointAddress;
309                         status = _ux_host_stack_transfer_request(transfer);
310 
311                         /* Clear 4th byte in case it's changed occasionally.  */
312                         control_buffer[3] = 0;
313                     }
314 
315                     /* Check frequency.  */
316                     if (status == UX_SUCCESS)
317                     {
318                         frequency = _ux_utility_long_get(control_buffer);
319                         if (audio_sampling -> ux_host_class_audio_sampling_frequency != frequency)
320                             status = UX_HOST_CLASS_AUDIO_WRONG_FREQUENCY;
321                     }
322 
323                     /* Free buffer.  */
324                     _ux_utility_memory_free(control_buffer);
325                 }
326 
327                 /* Update packet processing parameters.  */
328                 if (status == UX_SUCCESS)
329                 {
330                     audio -> ux_host_class_audio_packet_freq =
331                             (device -> ux_device_speed == UX_HIGH_SPEED_DEVICE) ?
332                                             8000 : 1000;
333                     audio -> ux_host_class_audio_packet_freq <<=
334                             audio -> ux_host_class_audio_isochronous_endpoint ->
335                                             ux_endpoint_descriptor.bInterval - 1;   /* Max 8000 << 15, no overflow.  */
336                     res_bytes = audio_sampling -> ux_host_class_audio_sampling_resolution;
337                     if (UX_OVERFLOW_CHECK_ADD_ULONG(res_bytes, 7))
338                         status = UX_MATH_OVERFLOW;
339                 }
340                 if (status == UX_SUCCESS)
341                 {
342                     res_bytes += 7;
343                     res_bytes >>= 3;
344 
345                     frequency = audio_sampling -> ux_host_class_audio_sampling_frequency;
346                     if (UX_OVERFLOW_CHECK_MULV_ULONG(frequency, audio_sampling -> ux_host_class_audio_sampling_channels))
347                         status = UX_MATH_OVERFLOW;
348                 }
349                 if (status == UX_SUCCESS)
350                 {
351                     frequency *= audio_sampling -> ux_host_class_audio_sampling_channels;
352                     if (UX_OVERFLOW_CHECK_MULV_ULONG(frequency, res_bytes))
353                         status = UX_MATH_OVERFLOW;
354                 }
355                 if (status == UX_SUCCESS)
356                 {
357                     frequency *= res_bytes;
358                     audio -> ux_host_class_audio_packet_fraction =
359                             frequency % audio -> ux_host_class_audio_packet_freq;
360                     audio -> ux_host_class_audio_packet_size =
361                             frequency / audio -> ux_host_class_audio_packet_freq;
362                 }
363 
364                 /* Unprotect thread reentry to this instance.  */
365                 _ux_host_mutex_off(&audio -> ux_host_class_audio_mutex);
366 
367                 /* Return completion status.  */
368                 return(status);
369             }
370         }
371 
372         /* Move to next interface.  */
373         interface_ptr =  interface_ptr -> ux_interface_next_interface;
374     }
375 
376     /* Unprotect thread reentry to this instance.  */
377     _ux_host_mutex_off(&audio -> ux_host_class_audio_mutex);
378 
379     /* Error trap. */
380     _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_NO_ALTERNATE_SETTING);
381 
382     /* If trace is enabled, insert this event into the trace buffer.  */
383     UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_NO_ALTERNATE_SETTING, audio, 0, 0, UX_TRACE_ERRORS, 0, 0)
384 
385     /* We get here if we could not get the right alternate setting.  */
386     return(UX_NO_ALTERNATE_SETTING);
387 }
388 
389 
390 /**************************************************************************/
391 /*                                                                        */
392 /*  FUNCTION                                               RELEASE        */
393 /*                                                                        */
394 /*    _uxe_host_class_audio_streaming_sampling_set        PORTABLE C      */
395 /*                                                           6.3.0        */
396 /*  AUTHOR                                                                */
397 /*                                                                        */
398 /*    Chaoqiong Xiao, Microsoft Corporation                               */
399 /*                                                                        */
400 /*  DESCRIPTION                                                           */
401 /*                                                                        */
402 /*    This function checks errors in audio sampling characteristics set   */
403 /*    function call.                                                      */
404 /*                                                                        */
405 /*  INPUT                                                                 */
406 /*                                                                        */
407 /*    audio                                 Pointer to audio class        */
408 /*    audio_sampling                        Pointer to audio sampling     */
409 /*                                                                        */
410 /*  OUTPUT                                                                */
411 /*                                                                        */
412 /*    Status                                                              */
413 /*                                                                        */
414 /*  CALLS                                                                 */
415 /*                                                                        */
416 /*    _ux_host_class_audio_streaming_sampling_set                         */
417 /*                                          Set sampling characteristics  */
418 /*                                                                        */
419 /*  CALLED BY                                                             */
420 /*                                                                        */
421 /*    Application                                                         */
422 /*                                                                        */
423 /*  RELEASE HISTORY                                                       */
424 /*                                                                        */
425 /*    DATE              NAME                      DESCRIPTION             */
426 /*                                                                        */
427 /*  10-31-2023     Chaoqiong Xiao           Initial Version 6.3.0         */
428 /*                                                                        */
429 /**************************************************************************/
_uxe_host_class_audio_streaming_sampling_set(UX_HOST_CLASS_AUDIO * audio,UX_HOST_CLASS_AUDIO_SAMPLING * audio_sampling)430 UINT  _uxe_host_class_audio_streaming_sampling_set(UX_HOST_CLASS_AUDIO *audio, UX_HOST_CLASS_AUDIO_SAMPLING *audio_sampling)
431 {
432 
433     /* Sanity checks.  */
434     if ((audio == UX_NULL) || (audio_sampling == UX_NULL))
435         return(UX_INVALID_PARAMETER);
436 
437     /* Invoke sampling characteristics set function.  */
438     return(_ux_host_class_audio_streaming_sampling_set(audio, audio_sampling));
439 }
440