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_stack.h"
29 
30 
31 #if defined(UX_DEVICE_STANDALONE)
32 /**************************************************************************/
33 /*                                                                        */
34 /*  FUNCTION                                               RELEASE        */
35 /*                                                                        */
36 /*    _ux_device_class_audio_write_task_function          PORTABLE C      */
37 /*                                                           6.3.0        */
38 /*  AUTHOR                                                                */
39 /*                                                                        */
40 /*    Yajun Xia, Microsoft Corporation                                    */
41 /*                                                                        */
42 /*  DESCRIPTION                                                           */
43 /*                                                                        */
44 /*    This function is the background task of the audio stream write.     */
45 /*                                                                        */
46 /*    It's for standalone mode.                                           */
47 /*                                                                        */
48 /*  INPUT                                                                 */
49 /*                                                                        */
50 /*    audio_class                                 Address of audio class  */
51 /*                                                container               */
52 /*                                                                        */
53 /*  OUTPUT                                                                */
54 /*                                                                        */
55 /*    State machine status                                                */
56 /*    UX_STATE_EXIT                         Device not configured         */
57 /*    UX_STATE_IDLE                         No streaming transfer running */
58 /*    UX_STATE_WAIT                         Streaming transfer running    */
59 /*                                                                        */
60 /*  CALLS                                                                 */
61 /*                                                                        */
62 /*    _ux_device_stack_transfer_run         Run transfer state machine    */
63 /*    _ux_utility_memory_copy               Copy memory                   */
64 /*                                                                        */
65 /*  CALLED BY                                                             */
66 /*                                                                        */
67 /*    USBX Device Stack                                                   */
68 /*                                                                        */
69 /*  RELEASE HISTORY                                                       */
70 /*                                                                        */
71 /*    DATE              NAME                      DESCRIPTION             */
72 /*                                                                        */
73 /*  10-31-2022     Yajun Xia                Initial Version 6.2.0         */
74 /*  10-31-2023     Chaoqiong Xiao           Modified comment(s),          */
75 /*                                            added a new mode to manage  */
76 /*                                            endpoint buffer in classes  */
77 /*                                            with zero copy enabled,     */
78 /*                                            resulting in version 6.3.0  */
79 /*                                                                        */
80 /**************************************************************************/
_ux_device_class_audio_write_task_function(UX_DEVICE_CLASS_AUDIO_STREAM * stream)81 UINT _ux_device_class_audio_write_task_function(UX_DEVICE_CLASS_AUDIO_STREAM *stream)
82 {
83 UX_SLAVE_DEVICE                 *device;
84 UX_SLAVE_ENDPOINT               *endpoint;
85 UX_SLAVE_TRANSFER               *transfer;
86 UCHAR                           *next_pos;
87 UX_DEVICE_CLASS_AUDIO_FRAME     *next_frame;
88 ULONG                           transfer_length;
89 ULONG                           actual_length;
90 UINT                            status;
91 
92 
93     /* Get the pointer to the device.  */
94     device = stream -> ux_device_class_audio_stream_audio -> ux_device_class_audio_device;
95 
96     /* Check if the device is configured.  */
97     if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED)
98     {
99         stream -> ux_device_class_audio_stream_task_state = UX_STATE_EXIT;
100         return(UX_STATE_EXIT);
101     }
102 
103     /* Get the endpoint.  */
104     endpoint = stream -> ux_device_class_audio_stream_endpoint;
105 
106     /* No endpoint ready, maybe it's alternate setting 0.  */
107     if (endpoint == UX_NULL)
108         return(UX_STATE_IDLE);
109 
110     /* Check if background transfer task is started.  */
111     if (stream -> ux_device_class_audio_stream_task_state == UX_DEVICE_CLASS_AUDIO_STREAM_RW_STOP)
112         return(UX_STATE_IDLE);
113 
114     /* Get transfer instance.  */
115     transfer = &endpoint -> ux_slave_endpoint_transfer_request;
116 
117     /* If not started yet, prepare data, reset transfer and start polling.  */
118     if (stream -> ux_device_class_audio_stream_task_state == UX_DEVICE_CLASS_AUDIO_STREAM_RW_START)
119     {
120 
121         /* Next state: transfer wait.  */
122         stream -> ux_device_class_audio_stream_task_state = UX_DEVICE_CLASS_AUDIO_STREAM_RW_WAIT;
123 
124         /* Start frame transfer anyway (even ZLP).  */
125         transfer_length = stream -> ux_device_class_audio_stream_transfer_pos -> ux_device_class_audio_frame_length;
126 
127 #if UX_DEVICE_ENDPOINT_BUFFER_OWNER == 0
128 
129         /* Stack owns buffer, copy data to it.  */
130         if (transfer_length)
131             _ux_utility_memory_copy(transfer -> ux_slave_transfer_request_data_pointer,
132                 stream -> ux_device_class_audio_stream_transfer_pos -> ux_device_class_audio_frame_data, transfer_length); /* Use case of memcpy is verified. */
133 #else
134 
135         /* Zero copy: directly use frame buffer to transfer.  */
136         transfer -> ux_slave_transfer_request_data_pointer = stream ->
137                     ux_device_class_audio_stream_transfer_pos -> ux_device_class_audio_frame_data;
138 #endif
139 
140         /* Reset transfer state.  */
141         UX_SLAVE_TRANSFER_STATE_RESET(transfer);
142     }
143 
144     /* Get current transfer length.  */
145     transfer_length = stream -> ux_device_class_audio_stream_transfer_pos -> ux_device_class_audio_frame_length;
146 
147     /* Run transfer states.  */
148     status = _ux_device_stack_transfer_run(transfer, transfer_length, transfer_length);
149 
150     /* Error case.  */
151     if (status < UX_STATE_NEXT)
152     {
153 
154         /* Error on background transfer task start.  */
155         stream -> ux_device_class_audio_stream_task_state = UX_STATE_RESET;
156         stream -> ux_device_class_audio_stream_task_status =
157                         transfer -> ux_slave_transfer_request_completion_code;
158 
159         /* Error notification!  */
160         _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_ERROR);
161         return(UX_STATE_EXIT);
162     }
163 
164     /* Success case.  */
165     if (status == UX_STATE_NEXT)
166     {
167 
168         /* Next state: start.  */
169         stream -> ux_device_class_audio_stream_task_state = UX_DEVICE_CLASS_AUDIO_STREAM_RW_START;
170         stream -> ux_device_class_audio_stream_task_status =
171                         transfer -> ux_slave_transfer_request_completion_code;
172 
173         /* Frame sent, free it.  */
174         stream -> ux_device_class_audio_stream_transfer_pos -> ux_device_class_audio_frame_length = 0;
175 
176         /* Get actual transfer length.  */
177         actual_length = transfer -> ux_slave_transfer_request_actual_length;
178 
179         /* Calculate next position.  */
180         next_pos = (UCHAR *)stream -> ux_device_class_audio_stream_transfer_pos;
181         next_pos += stream -> ux_device_class_audio_stream_frame_buffer_size;
182         if (next_pos >= stream -> ux_device_class_audio_stream_buffer + stream -> ux_device_class_audio_stream_buffer_size)
183             next_pos = stream -> ux_device_class_audio_stream_buffer;
184         next_frame = (UX_DEVICE_CLASS_AUDIO_FRAME *)next_pos;
185 
186         /* Underflow check!  */
187         if (transfer_length)
188         {
189 
190             /* Advance position.  */
191             stream -> ux_device_class_audio_stream_transfer_pos = next_frame;
192 
193             /* Error trap!  */
194             if (next_frame -> ux_device_class_audio_frame_length == 0)
195             {
196                 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_BUFFER_OVERFLOW);
197                 stream -> ux_device_class_audio_stream_buffer_error_count ++;
198             }
199         }
200         else
201         {
202 
203             /* Advance position if next payload available.  */
204             if (next_frame -> ux_device_class_audio_frame_length)
205                 stream -> ux_device_class_audio_stream_transfer_pos = next_frame;
206             else
207             {
208 
209                 /* Error trap!  */
210                 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_BUFFER_OVERFLOW);
211                 stream -> ux_device_class_audio_stream_buffer_error_count ++;
212             }
213         }
214 
215         /* Invoke notification callback.  */
216         if (stream -> ux_device_class_audio_stream_callbacks.ux_device_class_audio_stream_frame_done != UX_NULL)
217             stream -> ux_device_class_audio_stream_callbacks.ux_device_class_audio_stream_frame_done(stream, actual_length);
218     }
219 
220     /* Keep waiting.  */
221     return(UX_STATE_WAIT);
222 }
223 #endif
224 
225