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 /** USBX Component                                                        */
15 /**                                                                       */
16 /**   Device Audio Class                                                    */
17 /**                                                                       */
18 /**************************************************************************/
19 /**************************************************************************/
20 
21 #define UX_SOURCE_CODE
22 
23 
24 /* Include necessary system files.  */
25 
26 #include "ux_api.h"
27 #include "ux_device_class_audio.h"
28 #include "ux_device_class_audio20.h"
29 #include "ux_device_stack.h"
30 
31 
32 /**************************************************************************/
33 /*                                                                        */
34 /*  FUNCTION                                               RELEASE        */
35 /*                                                                        */
36 /*    _ux_device_class_audio20_control_process            PORTABLE C      */
37 /*                                                           6.2.1        */
38 /*  AUTHOR                                                                */
39 /*                                                                        */
40 /*    Chaoqiong Xiao, Microsoft Corporation                               */
41 /*                                                                        */
42 /*  DESCRIPTION                                                           */
43 /*                                                                        */
44 /*    This function manages the based sent by the host on the control     */
45 /*    endpoints with a CLASS or VENDOR SPECIFIC type.                     */
46 /*                                                                        */
47 /*  INPUT                                                                 */
48 /*                                                                        */
49 /*    audio                                 Address of audio class        */
50 /*                                            instance                    */
51 /*    transfer                              Address of transfer request   */
52 /*                                            instance                    */
53 /*    group                                 Request process data          */
54 /*                                                                        */
55 /*  OUTPUT                                                                */
56 /*                                                                        */
57 /*    Completion Status                                                   */
58 /*                                                                        */
59 /*  CALLS                                                                 */
60 /*                                                                        */
61 /*    _ux_utility_short_get                 Get 2-byte value from buffer  */
62 /*    _ux_utility_short_put                 Put 2-byte value to buffer    */
63 /*    _ux_utility_long_get                  Get 4-byte value from buffer  */
64 /*    _ux_utility_long_put                  Put 4-byte value to buffer    */
65 /*    _ux_utility_memory_copy               Copy memory                   */
66 /*    _ux_device_stack_transfer_request     Issue a transfer request      */
67 /*    _ux_device_stack_endpoint_stall       Endpoint stall                */
68 /*                                                                        */
69 /*  CALLED BY                                                             */
70 /*                                                                        */
71 /*    Application                                                         */
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 /*  04-02-2021     Chaoqiong Xiao           Modified comment(s),          */
81 /*                                            added volume RES support,   */
82 /*                                            resulting in version 6.1.6  */
83 /*  01-31-2022     Chaoqiong Xiao           Modified comment(s),          */
84 /*                                            allowed answer length only  */
85 /*                                            when requesting range,      */
86 /*                                            resulting in version 6.1.10 */
87 /*  07-29-2022     Chaoqiong Xiao           Modified comment(s),          */
88 /*                                            added support of multiple   */
89 /*                                            sampling frequencies,       */
90 /*                                            resulting in version 6.1.12 */
91 /*  03-08-2023     Chaoqiong Xiao           Modified comment(s),          */
92 /*                                            resulting in version 6.2.1  */
93 /*                                                                        */
94 /**************************************************************************/
_ux_device_class_audio20_control_process(UX_DEVICE_CLASS_AUDIO * audio,UX_SLAVE_TRANSFER * transfer,UX_DEVICE_CLASS_AUDIO20_CONTROL_GROUP * group)95 UINT _ux_device_class_audio20_control_process(UX_DEVICE_CLASS_AUDIO *audio,
96                                               UX_SLAVE_TRANSFER *transfer,
97                                               UX_DEVICE_CLASS_AUDIO20_CONTROL_GROUP *group)
98 {
99 
100 UX_SLAVE_ENDPOINT                   *endpoint;
101 UX_DEVICE_CLASS_AUDIO20_CONTROL     *control;
102 UCHAR                               request;
103 UCHAR                               request_type;
104 UCHAR                               unit_id;
105 UCHAR                               control_selector;
106 UCHAR                               channel_number;
107 ULONG                               request_length;
108 ULONG                               data_length;
109 ULONG                               i;
110 ULONG                               n_sub, pos, min, max, res, freq;
111 
112 
113     /* Get instances.  */
114     endpoint = &audio -> ux_device_class_audio_device -> ux_slave_device_control_endpoint;
115     transfer = &endpoint -> ux_slave_endpoint_transfer_request;
116 
117     /* Extract all necessary fields of the request.  */
118     request          = *(transfer -> ux_slave_transfer_request_setup + UX_DEVICE_CLASS_AUDIO_REQUEST_REQUEST);
119     request_type     = *(transfer -> ux_slave_transfer_request_setup + UX_DEVICE_CLASS_AUDIO_REQUEST_REQUEST_TYPE);
120     unit_id          = *(transfer -> ux_slave_transfer_request_setup + UX_DEVICE_CLASS_AUDIO_REQUEST_ENEITY_ID);
121     control_selector = *(transfer -> ux_slave_transfer_request_setup + UX_DEVICE_CLASS_AUDIO_REQUEST_CONTROL_SELECTOR);
122     channel_number   = *(transfer -> ux_slave_transfer_request_setup + UX_DEVICE_CLASS_AUDIO_REQUEST_CHANNEL_NUMBER);
123     request_length   = _ux_utility_short_get(transfer -> ux_slave_transfer_request_setup + UX_SETUP_LENGTH);
124 
125     for (i = 0; i < group -> ux_device_class_audio20_control_group_controls_nb; i ++)
126     {
127         control = &group -> ux_device_class_audio20_control_group_controls[i];
128 
129         /* Reset change map.  */
130         control -> ux_device_class_audio20_control_changed = 0;
131 
132         /* Is this request a clock unit request?  */
133         if (unit_id == control -> ux_device_class_audio20_control_cs_id)
134         {
135 
136             /* Clock Source request.
137              * We only support Sampling Frequency Control here.
138              * The Sampling Frequency Control must support the CUR and RANGE(MIN, MAX, RES) attributes.
139              */
140 
141             /* Sampling frequency control, SET request.  */
142             if ((request_type & UX_REQUEST_DIRECTION) == UX_REQUEST_OUT &&
143                 (control_selector == UX_DEVICE_CLASS_AUDIO20_CS_SAM_FREQ_CONTROL))
144             {
145                 switch(request)
146                 {
147                 case UX_DEVICE_CLASS_AUDIO20_CUR:
148 
149                     /* Check request parameter.  */
150                     if (request_length != 4)
151                         break;
152 
153                     /* Check if multiple frequency supported.  */
154                     if (control -> ux_device_class_audio20_control_sampling_frequency != 0)
155                         break;
156 
157                     /* Sanity check.  */
158                     UX_ASSERT(control -> ux_device_class_audio20_control_sampling_frequency_range != UX_NULL);
159 
160                     /* Get wNumSubRanges.  */
161                     n_sub = _ux_utility_short_get(control -> ux_device_class_audio20_control_sampling_frequency_range);
162 
163                     /* Get first RES.  */
164                     res = _ux_utility_long_get(control -> ux_device_class_audio20_control_sampling_frequency_range + 2 + 8);
165 
166                     /* Check if it's fixed single frequency.  */
167                     if (n_sub <= 1 && res == 0)
168                         break;
169 
170                     /* Get frequency to set.  */
171                     freq = _ux_utility_long_get(transfer -> ux_slave_transfer_request_data_pointer);
172 
173                     /* Check if frequency to set is inside range.  */
174                     for (pos = 2; pos < (2 + n_sub * 12); pos += 12)
175                     {
176                         min = _ux_utility_long_get(control -> ux_device_class_audio20_control_sampling_frequency_range + pos);
177                         max = _ux_utility_long_get(control -> ux_device_class_audio20_control_sampling_frequency_range + pos + 4);
178                         if (freq >= min && freq <= max)
179                         {
180 
181                             /* SET_CUR is accepted.  */
182                             if (control -> ux_device_class_audio20_control_sampling_frequency_cur != freq)
183                             {
184                                 control -> ux_device_class_audio20_control_sampling_frequency_cur = freq;
185                                 control -> ux_device_class_audio20_control_changed = UX_DEVICE_CLASS_AUDIO20_CONTROL_FREQUENCY_CHANGED;
186                             }
187                             return(UX_SUCCESS);
188                         }
189                     }
190                     break;
191 
192                 default:
193                     break;
194                 }
195             }
196 
197             /* We just support sampling frequency control, GET request.  */
198             if ((request_type & UX_REQUEST_DIRECTION) == UX_REQUEST_IN &&
199                 (control_selector == UX_DEVICE_CLASS_AUDIO20_CS_SAM_FREQ_CONTROL))
200             {
201 
202                 switch(request)
203                 {
204                 case UX_DEVICE_CLASS_AUDIO20_CUR:
205 
206                     /* Check request parameter.  */
207                     if (request_length < 4)
208                         break;
209 
210                     /* Send sampling frequency.  */
211                     if (control -> ux_device_class_audio20_control_sampling_frequency)
212                         _ux_utility_long_put(transfer -> ux_slave_transfer_request_data_pointer, control -> ux_device_class_audio20_control_sampling_frequency);
213                     else
214                         _ux_utility_long_put(transfer -> ux_slave_transfer_request_data_pointer, control -> ux_device_class_audio20_control_sampling_frequency_cur);
215                     _ux_device_stack_transfer_request(transfer, 4, request_length);
216                     return(UX_SUCCESS);
217 
218                 case UX_DEVICE_CLASS_AUDIO20_RANGE:
219 
220                     /* Check request parameter.  */
221                     if (request_length < 2)
222                         break;
223 
224                     if (control -> ux_device_class_audio20_control_sampling_frequency == 0)
225                     {
226 
227                         /* Send range parameters, RANGE is customized.  */
228                         UX_ASSERT(control -> ux_device_class_audio20_control_sampling_frequency_range != UX_NULL);
229 
230                         /* Get wNumSubRanges.  */
231                         n_sub = _ux_utility_short_get(control -> ux_device_class_audio20_control_sampling_frequency_range);
232                         UX_ASSERT(n_sub > 0);
233 
234                         /* Calculate length, n_sub is 16-bit width, result not overflows ULONG.  */
235                         data_length = 2 + n_sub * 12;
236                         UX_ASSERT(data_length <= UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH);
237 
238                         /* Copy data.  */
239                         data_length = UX_MIN(data_length, request_length);
240                         _ux_utility_memory_copy(transfer -> ux_slave_transfer_request_data_pointer,
241                                 control -> ux_device_class_audio20_control_sampling_frequency_range,
242                                 data_length); /* Use case of memcpy is verified. */
243                     }
244                     else
245                     {
246 
247                         /* Send range parameters.
248                          * We only support one here (from extension data).
249                          * wNumSubRanges : 1
250                          * dMIN          : sampling frequency
251                          * dMAX          : sampling frequency
252                          * dRES          : 1
253                          */
254                         _ux_utility_short_put(transfer -> ux_slave_transfer_request_data_pointer, 1);
255                         _ux_utility_long_put(transfer -> ux_slave_transfer_request_data_pointer + 2, control -> ux_device_class_audio20_control_sampling_frequency);
256                         _ux_utility_long_put(transfer -> ux_slave_transfer_request_data_pointer + 6, control -> ux_device_class_audio20_control_sampling_frequency);
257                         _ux_utility_long_put(transfer -> ux_slave_transfer_request_data_pointer + 10, 0);
258                         data_length = UX_MIN(14, request_length);
259                     }
260 
261                     /* Send data.  */
262                     _ux_device_stack_transfer_request(transfer, data_length, request_length);
263                     return(UX_SUCCESS);
264 
265                 default:
266                     break;
267                 }
268             }
269 
270             /* We are here when there is error on handling CU, break.  */
271             break;
272         } /* if (unit_id == control -> ux_device_class_audio20_control_cs_id) */
273 
274         /* Is this request a feature unit request?  */
275         if (unit_id == control -> ux_device_class_audio20_control_fu_id)
276         {
277 
278             /* Feature Unit request.
279              * We only support master channel, mute and volume here.
280              * Mute have only the CUR.
281              * Volume must support CUR and RANGE(MIN,MAX,RES).
282              */
283 
284             /* Check control selector.  */
285             switch(control_selector)
286             {
287             case UX_DEVICE_CLASS_AUDIO20_FU_MUTE_CONTROL:
288 
289                 /* Check channel number, we support master channel only.  */
290                 if (channel_number != 0)
291                     break;
292 
293                 /* Handle request.  */
294                 switch(request)
295                 {
296                 case UX_DEVICE_CLASS_AUDIO20_CUR:
297 
298 
299                     /* Check buffer size.  */
300                     if (request_length < 1)
301                         break;
302 
303                     /* GET_CUR.  */
304                     if ((request_type & UX_REQUEST_DIRECTION) == UX_REQUEST_IN)
305                     {
306 
307                         /* Send mute.  */
308                         transfer -> ux_slave_transfer_request_data_pointer[0] = (UCHAR)control -> ux_device_class_audio20_control_mute[0];
309                         _ux_device_stack_transfer_request(transfer, 1, request_length);
310                     }
311                     else if (control -> ux_device_class_audio20_control_mute[0] != transfer -> ux_slave_transfer_request_data_pointer[0])
312                     {
313 
314                         /* Update mute.  */
315                         control -> ux_device_class_audio20_control_changed = UX_DEVICE_CLASS_AUDIO20_CONTROL_MUTE_CHANGED;
316                         control -> ux_device_class_audio20_control_mute[0] = transfer -> ux_slave_transfer_request_data_pointer[0];
317                     }
318                     /* Done success.  */
319                     return(UX_SUCCESS);
320 
321                 default:
322                     break;
323                 }
324                 break;
325 
326             case UX_DEVICE_CLASS_AUDIO20_FU_VOLUME_CONTROL:
327 
328                 /* Check channel number, we support master channel only.  */
329                 if (channel_number != 0)
330                     break;
331 
332                 /* Handle request.  */
333                 switch(request)
334                 {
335                 case UX_DEVICE_CLASS_AUDIO20_CUR:
336 
337                     /* Check buffer size.  */
338                     if (request_length < 2)
339                         break;
340 
341                     /* GET_CUR.  */
342                     if ((request_type & UX_REQUEST_DIRECTION) == UX_REQUEST_IN)
343                     {
344 
345                         /* Send volume.  */
346                         _ux_utility_short_put(transfer -> ux_slave_transfer_request_data_pointer, (USHORT)control -> ux_device_class_audio20_control_volume[0]);
347                         _ux_device_stack_transfer_request(transfer, 2, request_length);
348                     }
349                     else if (control -> ux_device_class_audio20_control_volume[0] != (SHORT)_ux_utility_short_get(transfer -> ux_slave_transfer_request_data_pointer))
350                     {
351 
352                         /* Update volume.  */
353                         control -> ux_device_class_audio20_control_changed = UX_DEVICE_CLASS_AUDIO20_CONTROL_VOLUME_CHANGED;
354                         control -> ux_device_class_audio20_control_volume[0] = (SHORT)_ux_utility_short_get(transfer -> ux_slave_transfer_request_data_pointer);
355                     }
356 
357                     /* Done success.  */
358                     return(UX_SUCCESS);
359 
360                 case UX_DEVICE_CLASS_AUDIO20_RANGE:
361 
362                     /* Only support GET_CUR.  */
363                     if ((request_type & UX_REQUEST_DIRECTION) != UX_REQUEST_IN)
364                         break;
365 
366                     /* Check buffer size.  */
367                     if (request_length < 8)
368                         break;
369 
370                     /* Send wNumSubRanges,wMIN,wMAX,wRES.  */
371                     _ux_utility_short_put(transfer -> ux_slave_transfer_request_data_pointer + 0, 1);
372                     _ux_utility_short_put(transfer -> ux_slave_transfer_request_data_pointer + 2, (USHORT)control -> ux_device_class_audio20_control_volume_min[0]);
373                     _ux_utility_short_put(transfer -> ux_slave_transfer_request_data_pointer + 4, (USHORT)control -> ux_device_class_audio20_control_volume_max[0]);
374                     _ux_utility_short_put(transfer -> ux_slave_transfer_request_data_pointer + 6, (USHORT)UX_MAX(1, control -> ux_device_class_audio20_control_volume_res[0]));
375                     _ux_device_stack_transfer_request(transfer, 8, request_length);
376 
377                     /* Done success.  */
378                     return(UX_SUCCESS);
379 
380                 default:
381                     break;
382                 }
383                 break;
384 
385             default:
386                 break;
387             }
388 
389             /* We are here when there is error on handling FU, break.  */
390             break;
391         } /* if (unit_id == control -> ux_device_class_audio20_control_fu_id) */
392     }
393 
394     /* Request or parameter not supported.  */
395     _ux_device_stack_endpoint_stall(endpoint);
396     return(UX_ERROR);
397 }
398 
399 
400 /**************************************************************************/
401 /*                                                                        */
402 /*  FUNCTION                                               RELEASE        */
403 /*                                                                        */
404 /*    _uxe_device_class_audio20_control_process           PORTABLE C      */
405 /*                                                           6.2.1        */
406 /*  AUTHOR                                                                */
407 /*                                                                        */
408 /*    Chaoqiong Xiao, Microsoft Corporation                               */
409 /*                                                                        */
410 /*  DESCRIPTION                                                           */
411 /*                                                                        */
412 /*    This function checks errors in control request processing function  */
413 /*    call.                                                               */
414 /*                                                                        */
415 /*  INPUT                                                                 */
416 /*                                                                        */
417 /*    audio                                 Address of audio class        */
418 /*                                            instance                    */
419 /*    transfer                              Address of transfer request   */
420 /*                                            instance                    */
421 /*    group                                 Request process data          */
422 /*                                                                        */
423 /*  OUTPUT                                                                */
424 /*                                                                        */
425 /*    Completion Status                                                   */
426 /*                                                                        */
427 /*  CALLS                                                                 */
428 /*                                                                        */
429 /*    _ux_device_class_audio20_control_process                            */
430 /*                                          Process control requests      */
431 /*                                                                        */
432 /*  CALLED BY                                                             */
433 /*                                                                        */
434 /*    Application                                                         */
435 /*                                                                        */
436 /*  RELEASE HISTORY                                                       */
437 /*                                                                        */
438 /*    DATE              NAME                      DESCRIPTION             */
439 /*                                                                        */
440 /*  03-08-2023     Chaoqiong Xiao           Initial Version 6.2.1         */
441 /*                                                                        */
442 /**************************************************************************/
_uxe_device_class_audio20_control_process(UX_DEVICE_CLASS_AUDIO * audio,UX_SLAVE_TRANSFER * transfer,UX_DEVICE_CLASS_AUDIO20_CONTROL_GROUP * group)443 UINT _uxe_device_class_audio20_control_process(UX_DEVICE_CLASS_AUDIO *audio,
444                                               UX_SLAVE_TRANSFER *transfer,
445                                               UX_DEVICE_CLASS_AUDIO20_CONTROL_GROUP *group)
446 {
447 
448     /* Sanity checks.  */
449     if (audio == UX_NULL || transfer == UX_NULL || group == UX_NULL)
450         return(UX_INVALID_PARAMETER);
451 
452     /* Process control requests.  */
453     return(_ux_device_class_audio20_control_process(audio, transfer, group));
454 }
455