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_audio10.h"
29 #include "ux_device_stack.h"
30 
31 
32 /**************************************************************************/
33 /*                                                                        */
34 /*  FUNCTION                                               RELEASE        */
35 /*                                                                        */
36 /*    _ux_device_class_audio10_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_device_stack_transfer_request     Issue a transfer request      */
64 /*    _ux_device_stack_endpoint_stall       Endpoint stall                */
65 /*                                                                        */
66 /*  CALLED BY                                                             */
67 /*                                                                        */
68 /*    Application                                                         */
69 /*                                                                        */
70 /*  RELEASE HISTORY                                                       */
71 /*                                                                        */
72 /*    DATE              NAME                      DESCRIPTION             */
73 /*                                                                        */
74 /*  05-19-2020     Chaoqiong Xiao           Initial Version 6.0           */
75 /*  09-30-2020     Chaoqiong Xiao           Modified comment(s),          */
76 /*                                            resulting in version 6.1    */
77 /*  04-02-2021     Chaoqiong Xiao           Modified comment(s),          */
78 /*                                            added volume RES support,   */
79 /*                                            resulting in version 6.1.6  */
80 /*  04-25-2022     Chaoqiong Xiao           Modified comment(s),          */
81 /*                                            internal clean up,          */
82 /*                                            resulting in version 6.1.11 */
83 /*  07-29-2022     Chaoqiong Xiao           Modified comment(s),          */
84 /*                                            added sampling control,     */
85 /*                                            resulting in version 6.1.12 */
86 /*  03-08-2023     Chaoqiong Xiao           Modified comment(s),          */
87 /*                                            fixed a macro name,         */
88 /*                                            resulting in version 6.2.1  */
89 /*                                                                        */
90 /**************************************************************************/
_ux_device_class_audio10_control_process(UX_DEVICE_CLASS_AUDIO * audio,UX_SLAVE_TRANSFER * transfer,UX_DEVICE_CLASS_AUDIO10_CONTROL_GROUP * group)91 UINT _ux_device_class_audio10_control_process(UX_DEVICE_CLASS_AUDIO *audio,
92     UX_SLAVE_TRANSFER *transfer, UX_DEVICE_CLASS_AUDIO10_CONTROL_GROUP *group)
93 {
94 
95 UX_SLAVE_ENDPOINT                   *endpoint;
96 UX_DEVICE_CLASS_AUDIO10_CONTROL     *control;
97 UCHAR                               request;
98 UCHAR                               request_type;
99 UCHAR                               unit_id, ep_addr;
100 UCHAR                               control_selector;
101 UCHAR                               channel_number;
102 ULONG                               request_length;
103 UCHAR                               *desc;
104 ULONG                               sam, min, max, pos;
105 ULONG                               i;
106 
107 
108     /* Get instances.  */
109     endpoint = &audio -> ux_device_class_audio_device -> ux_slave_device_control_endpoint;
110     transfer = &endpoint -> ux_slave_endpoint_transfer_request;
111 
112     /* Extract all necessary fields of the request.  */
113     request          = *(transfer -> ux_slave_transfer_request_setup + UX_DEVICE_CLASS_AUDIO_REQUEST_REQUEST);
114     request_type     = *(transfer -> ux_slave_transfer_request_setup + UX_DEVICE_CLASS_AUDIO_REQUEST_REQUEST_TYPE);
115     unit_id          = *(transfer -> ux_slave_transfer_request_setup + UX_DEVICE_CLASS_AUDIO_REQUEST_ENEITY_ID);
116     ep_addr          = *(transfer -> ux_slave_transfer_request_setup + UX_DEVICE_CLASS_AUDIO_REQUEST_ENDPOINT);
117     control_selector = *(transfer -> ux_slave_transfer_request_setup + UX_DEVICE_CLASS_AUDIO_REQUEST_CONTROL_SELECTOR);
118     channel_number   = *(transfer -> ux_slave_transfer_request_setup + UX_DEVICE_CLASS_AUDIO_REQUEST_CHANNEL_NUMBER);
119     request_length   = _ux_utility_short_get(transfer -> ux_slave_transfer_request_setup + UX_SETUP_LENGTH);
120 
121     /* Process controls one by one.  */
122     for (i = 0; i < group -> ux_device_class_audio10_control_group_controls_nb; i ++)
123     {
124         control = &group -> ux_device_class_audio10_control_group_controls[i];
125 
126         /* Reset change map.  */
127         control -> ux_device_class_audio10_control_changed = 0;
128 
129         /* We handle endpoint requests.  */
130         if ((request_type & UX_REQUEST_TARGET) == UX_REQUEST_TARGET_ENDPOINT &&
131             ep_addr == control -> ux_device_class_audio10_control_ep_addr)
132         {
133 
134             /* Handle the request.  */
135             switch(request)
136             {
137             case UX_DEVICE_CLASS_AUDIO10_SET_CUR:
138 
139                 /* Only sampling frequency control is supported.  */
140                 if (control_selector != UX_DEVICE_CLASS_AUDIO10_EP_SAMPLING_FREQ_CONTROL)
141                     break;
142 
143                 /* Length check.  */
144                 if (request_length != 3)
145                     break;
146 
147                 /* If frequencies not specified, no modification accepted.  */
148                 if (control -> ux_device_class_audio10_control_sam_freq_types == UX_NULL)
149                     return(UX_SUCCESS);
150 
151                 /* Check sampling frequency types (UAC 1.0 Format Type I : bSamFreqType ..) for MIN and MAX.  */
152                 desc = control -> ux_device_class_audio10_control_sam_freq_types;
153                 if (desc[0] == 0)
154                 {
155                     min = ((ULONG)desc[1]) | ((ULONG)desc[2] << 8) | ((ULONG)desc[3] << 16);
156                     max = ((ULONG)desc[4]) | ((ULONG)desc[5] << 8) | ((ULONG)desc[6] << 16);
157                 }
158                 else
159                 {
160                     min = 0xFFFFFFFF;
161                     max = 0x00000000;
162                     for (pos = 1;
163                          pos < (ULONG)desc[0] * 3 + 1; /* Calculate from byte, no overflow.  */
164                          pos += 3)
165                     {
166                         sam = (ULONG)desc[pos + 0] | ((ULONG)desc[pos + 1] << 8) | ((ULONG)desc[pos + 2] << 16);
167                         if (sam > max)
168                             max = sam;
169                         if (sam < min)
170                             min = sam;
171                     }
172                 }
173 
174                 /* Accept frequency any way.
175                  * If it's not in range, round to min or max.
176                  * If it's in range it's not rounded, application should check and round it.  */
177                 sam = ((ULONG)transfer -> ux_slave_transfer_request_data_pointer[0]      ) |
178                       ((ULONG)transfer -> ux_slave_transfer_request_data_pointer[1] <<  8) |
179                       ((ULONG)transfer -> ux_slave_transfer_request_data_pointer[2] << 16);
180                 if (sam < min)
181                     sam = min;
182                 if (sam > max)
183                     sam = max;
184                 control -> ux_device_class_audio10_control_sam_freq = sam;
185                 control -> ux_device_class_audio10_control_changed = UX_DEVICE_CLASS_AUDIO10_CONTROL_FREQUENCY_CHANGED;
186                 return(UX_SUCCESS);
187 
188             case UX_DEVICE_CLASS_AUDIO10_GET_CUR:
189 
190                 /* Sampling frequency control is supported.  */
191                 if (control_selector != UX_DEVICE_CLASS_AUDIO10_EP_SAMPLING_FREQ_CONTROL)
192                     break;
193 
194                 /* Check host buffer.  */
195                 if (request_length < 3)
196                     break;
197 
198                 /* Put sample frequency.  */
199                 sam = control -> ux_device_class_audio10_control_sam_freq;
200                 transfer -> ux_slave_transfer_request_data_pointer[0] = UX_DW0(sam);
201                 transfer -> ux_slave_transfer_request_data_pointer[1] = UX_DW1(sam);
202                 transfer -> ux_slave_transfer_request_data_pointer[2] = UX_DW2(sam);
203                 _ux_device_stack_transfer_request(transfer, 3, request_length);
204                 return(UX_SUCCESS);
205 
206             default:
207                 break;
208             }
209         }
210 
211         /* We handle feature unit requests.  */
212         if ((request_type & UX_REQUEST_TARGET) == UX_REQUEST_TARGET_INTERFACE &&
213             unit_id == control -> ux_device_class_audio10_control_fu_id)
214         {
215 
216             /* Handle the request.  */
217             switch(request)
218             {
219             case UX_DEVICE_CLASS_AUDIO10_SET_CUR:
220 
221                 /* We only support master channel, so channel number must be 0 or 0xFF.  */
222                 if (channel_number != 0 && channel_number != 0xFF)
223                     break;
224 
225                 /* Set request.  */
226                 switch(control_selector)
227                 {
228                 case UX_DEVICE_CLASS_AUDIO10_FU_MUTE_CONTROL:
229 
230                     if (transfer -> ux_slave_transfer_request_actual_length < 1)
231 
232                         /* No change applied.  */
233                         break;
234 
235                     if (control -> ux_device_class_audio10_control_mute[0] != transfer -> ux_slave_transfer_request_data_pointer[0])
236                     {
237                         control -> ux_device_class_audio10_control_changed = UX_DEVICE_CLASS_AUDIO10_CONTROL_MUTE_CHANGED;
238                         control -> ux_device_class_audio10_control_mute[0] = transfer -> ux_slave_transfer_request_data_pointer[0];
239 
240                     }
241 
242                     /* Done success.  */
243                     return(UX_SUCCESS);
244 
245                 case UX_DEVICE_CLASS_AUDIO10_FU_VOLUME_CONTROL:
246 
247                     if (transfer -> ux_slave_transfer_request_actual_length < 2)
248 
249                         /* No change applied.  */
250                         break;
251 
252                     if (control -> ux_device_class_audio10_control_volume[0] != (SHORT)_ux_utility_short_get(transfer -> ux_slave_transfer_request_data_pointer))
253                     {
254                         control -> ux_device_class_audio10_control_changed = UX_DEVICE_CLASS_AUDIO10_CONTROL_VOLUME_CHANGED;
255                         control -> ux_device_class_audio10_control_volume[0] = (SHORT)_ux_utility_short_get(transfer -> ux_slave_transfer_request_data_pointer);
256                     }
257 
258                     /* Done success.  */
259                     return(UX_SUCCESS);
260 
261 
262                 default:
263 
264                     /* No change applied.  */
265                     break;
266                 }
267 
268                 /* Request or parameter problem.  */
269                 break;
270 
271             case UX_DEVICE_CLASS_AUDIO10_GET_MIN:
272             case UX_DEVICE_CLASS_AUDIO10_GET_MAX:
273             case UX_DEVICE_CLASS_AUDIO10_GET_RES:
274             case UX_DEVICE_CLASS_AUDIO10_GET_CUR:
275 
276                 /* We only support master channel, so channel number must be 0 or 0xFF.  */
277                 if (channel_number != 0 && channel_number != 0xFF)
278                     break;
279 
280                 /* Get request.  */
281                 switch(control_selector)
282                 {
283                 case UX_DEVICE_CLASS_AUDIO10_FU_MUTE_CONTROL:
284 
285                     /* We only support _CUR.  */
286                     if (request != UX_DEVICE_CLASS_AUDIO10_GET_CUR)
287                         break;
288 
289                     /* Not enough buffer for data.  */
290                     if (request_length < 1)
291                         break;
292 
293                     /* Send mute status.  */
294                     transfer -> ux_slave_transfer_request_data_pointer[0] = (UCHAR)control -> ux_device_class_audio10_control_mute[0];
295                     _ux_device_stack_transfer_request(transfer, 1, request_length);
296 
297                     /* Done success.  */
298                     return(UX_SUCCESS);
299 
300                 case UX_DEVICE_CLASS_AUDIO10_FU_VOLUME_CONTROL:
301 
302                     /* Not enough buffer for data.  */
303                     if (request_length < 2)
304                         break;
305 
306                     /* Send volume value.  */
307                     _ux_utility_short_put(transfer -> ux_slave_transfer_request_data_pointer, (USHORT)
308                                           (request == UX_DEVICE_CLASS_AUDIO10_GET_MIN ? control -> ux_device_class_audio10_control_volume_min[0] :
309                                            (request == UX_DEVICE_CLASS_AUDIO10_GET_MAX ? control -> ux_device_class_audio10_control_volume_max[0] :
310                                             (request == UX_DEVICE_CLASS_AUDIO10_GET_RES ? UX_MAX(1, control -> ux_device_class_audio10_control_volume_res[0]) :
311                                               control -> ux_device_class_audio10_control_volume[0]))));
312                     _ux_device_stack_transfer_request(transfer, 2, request_length);
313 
314                     /* Done success.  */
315                     return(UX_SUCCESS);
316 
317                 default:
318                     break;
319                 }
320                 break;
321 
322             default:
323                 break;
324             }
325 
326             /* We are here when there is error, break the loop.  */
327             break;
328         }
329 
330         /* Now try next.  */
331     }
332 
333 
334     /* Request or parameter not supported.  */
335     _ux_device_stack_endpoint_stall(endpoint);
336 
337     /* Done error.  */
338     return(UX_ERROR);
339 }
340 
341 
342 /**************************************************************************/
343 /*                                                                        */
344 /*  FUNCTION                                               RELEASE        */
345 /*                                                                        */
346 /*    _uxe_device_class_audio10_control_process           PORTABLE C      */
347 /*                                                           6.2.1        */
348 /*  AUTHOR                                                                */
349 /*                                                                        */
350 /*    Chaoqiong Xiao, Microsoft Corporation                               */
351 /*                                                                        */
352 /*  DESCRIPTION                                                           */
353 /*                                                                        */
354 /*    This function checks errors in control processing function call.    */
355 /*                                                                        */
356 /*  INPUT                                                                 */
357 /*                                                                        */
358 /*    audio                                 Address of audio class        */
359 /*                                            instance                    */
360 /*    transfer                              Address of transfer request   */
361 /*                                            instance                    */
362 /*    group                                 Request process data          */
363 /*                                                                        */
364 /*  OUTPUT                                                                */
365 /*                                                                        */
366 /*    Completion Status                                                   */
367 /*                                                                        */
368 /*  CALLS                                                                 */
369 /*                                                                        */
370 /*    _ux_device_class_audio10_control_process                            */
371 /*                                          Process control requests      */
372 /*                                                                        */
373 /*  CALLED BY                                                             */
374 /*                                                                        */
375 /*    Application                                                         */
376 /*                                                                        */
377 /*  RELEASE HISTORY                                                       */
378 /*                                                                        */
379 /*    DATE              NAME                      DESCRIPTION             */
380 /*                                                                        */
381 /*  03-08-2023     Chaoqiong Xiao           Initial Version 6.2.1         */
382 /*                                                                        */
383 /**************************************************************************/
_uxe_device_class_audio10_control_process(UX_DEVICE_CLASS_AUDIO * audio,UX_SLAVE_TRANSFER * transfer,UX_DEVICE_CLASS_AUDIO10_CONTROL_GROUP * group)384 UINT _uxe_device_class_audio10_control_process(UX_DEVICE_CLASS_AUDIO *audio,
385     UX_SLAVE_TRANSFER *transfer, UX_DEVICE_CLASS_AUDIO10_CONTROL_GROUP *group)
386 {
387 
388     /* Sanity checks.  */
389     if (audio == UX_NULL || transfer == UX_NULL || group == UX_NULL)
390         return(UX_INVALID_PARAMETER);
391 
392     /* Process control request.  */
393     return(_ux_device_class_audio10_control_process(audio, transfer, group));
394 }
395