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