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