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