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 /** USBX Component */
14 /** */
15 /** Device Audio Class */
16 /** */
17 /**************************************************************************/
18 /**************************************************************************/
19
20 #define UX_SOURCE_CODE
21
22
23 /* Include necessary system files. */
24
25 #include "ux_api.h"
26 #include "ux_device_class_audio.h"
27 #include "ux_device_stack.h"
28
29
30 /**************************************************************************/
31 /* */
32 /* FUNCTION RELEASE */
33 /* */
34 /* _ux_device_class_audio_change PORTABLE C */
35 /* 6.3.0 */
36 /* AUTHOR */
37 /* */
38 /* Chaoqiong Xiao, Microsoft Corporation */
39 /* */
40 /* DESCRIPTION */
41 /* */
42 /* This function changes the interface of the Audio device */
43 /* */
44 /* INPUT */
45 /* */
46 /* command Pointer to audio command */
47 /* */
48 /* OUTPUT */
49 /* */
50 /* Completion Status */
51 /* */
52 /* CALLS */
53 /* */
54 /* _ux_system_error_handler System error trap */
55 /* */
56 /* CALLED BY */
57 /* */
58 /* Device Audio Class */
59 /* */
60 /* RELEASE HISTORY */
61 /* */
62 /* DATE NAME DESCRIPTION */
63 /* */
64 /* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */
65 /* 09-30-2020 Chaoqiong Xiao Modified comment(s), */
66 /* resulting in version 6.1 */
67 /* 10-15-2021 Chaoqiong Xiao Modified comment(s), */
68 /* replaced wMaxPacketSize by */
69 /* calculated payload size, */
70 /* resulting in version 6.1.9 */
71 /* 01-31-2022 Chaoqiong Xiao Modified comment(s), */
72 /* added feedback support, */
73 /* resulting in version 6.1.10 */
74 /* 04-25-2022 Chaoqiong Xiao Modified comment(s), */
75 /* fixed standalone compile, */
76 /* resulting in version 6.1.11 */
77 /* 07-29-2022 Chaoqiong Xiao Modified comment(s), */
78 /* fixed parameter/variable */
79 /* names conflict C++ keyword, */
80 /* rx full packet for */
81 /* feedback, */
82 /* resulting in version 6.1.12 */
83 /* 10-31-2022 Yajun Xia Modified comment(s), */
84 /* added standalone support, */
85 /* resulting in version 6.2.0 */
86 /* 10-31-2023 Chaoqiong Xiao Modified comment(s), */
87 /* added a new mode to manage */
88 /* endpoint buffer in classes */
89 /* with zero copy enabled, */
90 /* resulting in version 6.3.0 */
91 /* */
92 /**************************************************************************/
_ux_device_class_audio_change(UX_SLAVE_CLASS_COMMAND * command)93 UINT _ux_device_class_audio_change(UX_SLAVE_CLASS_COMMAND *command)
94 {
95
96 UX_DEVICE_CLASS_AUDIO *audio;
97 UX_DEVICE_CLASS_AUDIO_STREAM *stream;
98 UX_SLAVE_CLASS *class_ptr;
99 UX_SLAVE_INTERFACE *interface_ptr;
100 UX_SLAVE_ENDPOINT *endpoint;
101 UCHAR *frame_buffer;
102 ULONG stream_index;
103 ULONG endpoint_dir;
104
105
106 /* Get the class container. */
107 class_ptr = command -> ux_slave_class_command_class_ptr;
108
109 /* Get the class instance in the container. */
110 audio = (UX_DEVICE_CLASS_AUDIO *) class_ptr -> ux_slave_class_instance;
111
112 /* Get the interface that owns this instance. */
113 interface_ptr = (UX_SLAVE_INTERFACE *) command -> ux_slave_class_command_interface;
114
115 /* Get the interface number (base 0). */
116 if (audio -> ux_device_class_audio_interface)
117 {
118
119 /* If IAD used, calculate stream index based on interface number. */
120 stream_index = interface_ptr -> ux_slave_interface_descriptor.bInterfaceNumber;
121 stream_index -= audio -> ux_device_class_audio_interface -> ux_slave_interface_descriptor.bInterfaceNumber;
122 stream_index --;
123 }
124 else
125
126 /* One stream for one driver! */
127 stream_index = 0;
128
129 /* Get the stream instance. */
130 stream = &audio -> ux_device_class_audio_streams[stream_index];
131
132 /* Update the interface. */
133 stream -> ux_device_class_audio_stream_interface = interface_ptr;
134
135 /* If the interface to mount has a non zero alternate setting, the class is really active with
136 the endpoints active. If the interface reverts to alternate setting 0, it needs to have
137 the pending transactions terminated. */
138 if (interface_ptr -> ux_slave_interface_descriptor.bAlternateSetting != 0)
139 {
140
141 /* Locate the endpoints. ISO IN(write)/OUT(read) for Streaming Interface. */
142 endpoint = interface_ptr -> ux_slave_interface_first_endpoint;
143
144 /* Parse all endpoints. */
145 #if defined(UX_DEVICE_STANDALONE)
146
147 endpoint_dir = (stream -> ux_device_class_audio_stream_task_function ==
148 _ux_device_class_audio_read_task_function) ?
149 UX_ENDPOINT_OUT: UX_ENDPOINT_IN;
150 #else
151
152 endpoint_dir = (stream -> ux_device_class_audio_stream_thread.tx_thread_entry ==
153 _ux_device_class_audio_read_thread_entry) ?
154 UX_ENDPOINT_OUT : UX_ENDPOINT_IN;
155 #endif
156 stream -> ux_device_class_audio_stream_endpoint = UX_NULL;
157
158 #if defined(UX_DEVICE_CLASS_AUDIO_FEEDBACK_SUPPORT)
159 stream -> ux_device_class_audio_stream_feedback = UX_NULL;
160 #endif
161 while(endpoint != UX_NULL)
162 {
163
164 /* Check the endpoint attributes. */
165 if((endpoint -> ux_slave_endpoint_descriptor.bmAttributes &
166 UX_DEVICE_CLASS_AUDIO_EP_TRANSFER_TYPE_MASK) == UX_ISOCHRONOUS_ENDPOINT)
167 {
168
169 /* Check the endpoint direction. */
170 if ((endpoint->ux_slave_endpoint_descriptor.bEndpointAddress &
171 UX_ENDPOINT_DIRECTION) == endpoint_dir)
172 {
173
174 /* We found the data endpoint, check its size. */
175 if (endpoint -> ux_slave_endpoint_transfer_request.ux_slave_transfer_request_transfer_length > stream -> ux_device_class_audio_stream_frame_buffer_size - 8)
176 {
177
178 /* Error trap! */
179 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_MEMORY_INSUFFICIENT);
180
181 /* Frame buffer too small for endpoints. */
182 return(UX_MEMORY_INSUFFICIENT);
183 }
184
185 /* Save it. */
186 stream -> ux_device_class_audio_stream_endpoint = endpoint;
187 }
188 #if defined(UX_DEVICE_CLASS_AUDIO_FEEDBACK_SUPPORT)
189 else
190 {
191
192 /* We found the feedback endpoint, check its size. */
193 if (endpoint -> ux_slave_endpoint_transfer_request.ux_slave_transfer_request_transfer_length <
194 (_ux_system_slave->ux_system_slave_speed == UX_HIGH_SPEED_DEVICE ? 4 : 3))
195 {
196
197 /* Error trap! */
198 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_MEMORY_INSUFFICIENT);
199
200 /* Frame buffer too small for endpoints. */
201 return(UX_MEMORY_INSUFFICIENT);
202 }
203
204 /* Set request length, uses full packet for OUT to avoid possible overflow. */
205 endpoint -> ux_slave_endpoint_transfer_request.ux_slave_transfer_request_requested_length =
206 endpoint_dir == UX_ENDPOINT_OUT ?
207 endpoint -> ux_slave_endpoint_transfer_request.ux_slave_transfer_request_transfer_length :
208 ((_ux_system_slave -> ux_system_slave_speed == UX_HIGH_SPEED_DEVICE) ?
209 UX_FEEDBACK_SIZE_HIGH_SPEED : UX_FEEDBACK_SIZE_FULL_SPEED);
210
211 /* Save it. */
212 stream -> ux_device_class_audio_stream_feedback = endpoint;
213 }
214 #endif
215 }
216
217 /* Check if done. */
218 if (stream -> ux_device_class_audio_stream_endpoint
219 #if defined(UX_DEVICE_CLASS_AUDIO_FEEDBACK_SUPPORT)
220 && stream -> ux_device_class_audio_stream_feedback
221 #endif
222 )
223 break;
224
225 /* Next endpoint. */
226 endpoint = endpoint -> ux_slave_endpoint_next_endpoint;
227 }
228
229 /* Now check if all endpoints have been found. */
230 if (stream -> ux_device_class_audio_stream_endpoint == UX_NULL)
231 {
232
233 /* Error trap! */
234 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED);
235
236 /* Not all endpoints have been found. Major error, do not proceed. */
237 return(UX_DESCRIPTOR_CORRUPTED);
238 }
239
240 #if UX_DEVICE_ENDPOINT_BUFFER_OWNER == 1
241 #if defined(UX_DEVICE_CLASS_AUDIO_FEEDBACK_SUPPORT)
242 if (stream -> ux_device_class_audio_stream_feedback)
243 stream -> ux_device_class_audio_stream_feedback ->
244 ux_slave_endpoint_transfer_request.ux_slave_transfer_request_data_pointer =
245 stream -> ux_device_class_audio_stream_feedback_buffer;
246 #endif
247 #endif
248
249 #if defined(UX_DEVICE_STANDALONE)
250
251 /* Reset background transfer state. */
252 stream -> ux_device_class_audio_stream_task_state = UX_STATE_RESET;
253 #endif
254
255 /* Now reset payload buffer error count. */
256 stream -> ux_device_class_audio_stream_buffer_error_count = 0;
257
258 /* Now reset frame buffers. */
259 frame_buffer = stream -> ux_device_class_audio_stream_buffer;
260 while(frame_buffer < stream -> ux_device_class_audio_stream_buffer + stream -> ux_device_class_audio_stream_buffer_size)
261 {
262
263 /* Reset header information. */
264 *((ULONG *) frame_buffer ) = 0;
265 *((ULONG *)(frame_buffer + 4)) = 0;
266
267 /* Next. */
268 frame_buffer += stream -> ux_device_class_audio_stream_frame_buffer_size;
269 }
270 stream -> ux_device_class_audio_stream_transfer_pos = stream -> ux_device_class_audio_stream_access_pos;
271
272 #if defined(UX_DEVICE_CLASS_AUDIO_FEEDBACK_SUPPORT) && !defined(UX_DEVICE_STANDALONE)
273
274 /* If feedback supported, resume the thread. */
275 if (stream -> ux_device_class_audio_stream_feedback_thread_stack)
276 _ux_utility_thread_resume(&stream -> ux_device_class_audio_stream_feedback_thread);
277 #endif
278 }
279 else
280 {
281
282 /* There is no data endpoint. */
283 stream -> ux_device_class_audio_stream_endpoint = UX_NULL;
284 #if defined(UX_DEVICE_CLASS_AUDIO_FEEDBACK_SUPPORT)
285 stream -> ux_device_class_audio_stream_feedback = UX_NULL;
286 #endif
287
288 /* In this case, we are reverting to the Alternate Setting 0. We need to terminate the pending transactions. */
289 /* Endpoints actually aborted and destroyed before change command. */
290 /*
291 _ux_device_stack_transfer_all_request_abort(stream -> ux_device_class_audio_stream_endpoint, UX_TRANSFER_APPLICATION_RESET);
292 */
293 }
294
295 /* Invoke stream change callback. */
296 if (stream -> ux_device_class_audio_stream_callbacks.ux_device_class_audio_stream_change)
297 stream -> ux_device_class_audio_stream_callbacks.ux_device_class_audio_stream_change(stream, interface_ptr -> ux_slave_interface_descriptor.bAlternateSetting);
298
299 /* Return completion status. */
300 return(UX_SUCCESS);
301 }
302