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