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