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