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 /**************************************************************************/
32 /* */
33 /* FUNCTION RELEASE */
34 /* */
35 /* _ux_device_class_audio_write_thread_entry PORTABLE C */
36 /* 6.2.0 */
37 /* AUTHOR */
38 /* */
39 /* Chaoqiong Xiao, Microsoft Corporation */
40 /* */
41 /* DESCRIPTION */
42 /* */
43 /* This function is thread of ISO IN for the Audio class. */
44 /* */
45 /* INPUT */
46 /* */
47 /* audio_stream Address of audio stream */
48 /* instance */
49 /* */
50 /* OUTPUT */
51 /* */
52 /* None */
53 /* */
54 /* CALLS */
55 /* */
56 /* _ux_system_error_handler System error trap */
57 /* _ux_device_thread_suspend Suspend thread used */
58 /* _ux_device_stack_transfer_request Issue transfer request */
59 /* _ux_utility_memory_copy Copy data */
60 /* */
61 /* CALLED BY */
62 /* */
63 /* ThreadX */
64 /* */
65 /* RELEASE HISTORY */
66 /* */
67 /* DATE NAME DESCRIPTION */
68 /* */
69 /* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */
70 /* 09-30-2020 Chaoqiong Xiao Modified comment(s), */
71 /* verified memset and memcpy */
72 /* cases, */
73 /* resulting in version 6.1 */
74 /* 01-31-2022 Chaoqiong Xiao Modified comment(s), */
75 /* refined macros names, */
76 /* resulting in version 6.1.10 */
77 /* 10-31-2022 Yajun Xia Modified comment(s), */
78 /* added standalone support, */
79 /* resulting in version 6.2.0 */
80 /* 10-31-2023 Chaoqiong Xiao Modified comment(s), */
81 /* added a new mode to manage */
82 /* endpoint buffer in classes */
83 /* with zero copy enabled, */
84 /* resulting in version 6.3.0 */
85 /* */
86 /**************************************************************************/
_ux_device_class_audio_write_thread_entry(ULONG audio_stream)87 VOID _ux_device_class_audio_write_thread_entry(ULONG audio_stream)
88 {
89
90 UINT status;
91 UX_DEVICE_CLASS_AUDIO_STREAM *stream;
92 UX_SLAVE_DEVICE *device;
93 UX_SLAVE_ENDPOINT *endpoint;
94 UX_SLAVE_TRANSFER *transfer;
95 UCHAR *next_pos;
96 UX_DEVICE_CLASS_AUDIO_FRAME *next_frame;
97 ULONG transfer_length;
98 ULONG actual_length;
99
100
101 /* Get Audio class stream instance. */
102 UX_THREAD_EXTENSION_PTR_GET(stream, UX_DEVICE_CLASS_AUDIO_STREAM, audio_stream)
103
104 /* Get stack device instance. */
105 device = stream -> ux_device_class_audio_stream_audio -> ux_device_class_audio_device;
106
107 /* This thread runs forever but can be suspended or resumed. */
108 while(1)
109 {
110 while (device -> ux_slave_device_state == UX_DEVICE_CONFIGURED)
111 {
112
113 /* Get endpoint instance. */
114 endpoint = stream -> ux_device_class_audio_stream_endpoint;
115
116 /* Endpoint not available, maybe it's alternate setting 0. */
117 if (endpoint == UX_NULL)
118 break;
119
120 /* Get transfer instance. */
121 transfer = &endpoint -> ux_slave_endpoint_transfer_request;
122
123 /* Start frame transfer anyway (even ZLP). */
124 transfer_length = stream -> ux_device_class_audio_stream_transfer_pos -> ux_device_class_audio_frame_length;
125
126 #if UX_DEVICE_ENDPOINT_BUFFER_OWNER == 0
127
128 /* Stack owns endpoint buffer, copy to buffer. */
129 if (transfer_length)
130 _ux_utility_memory_copy(transfer -> ux_slave_transfer_request_data_pointer,
131 stream -> ux_device_class_audio_stream_transfer_pos -> ux_device_class_audio_frame_data, transfer_length); /* Use case of memcpy is verified. */
132 #else
133
134 /* Zero copy: directly use frame buffer to transfer. */
135 transfer -> ux_slave_transfer_request_data_pointer = stream ->
136 ux_device_class_audio_stream_transfer_pos -> ux_device_class_audio_frame_data;
137 #endif
138
139 /* Issue transfer request, thread blocked until transfer done. */
140 status = _ux_device_stack_transfer_request(transfer, transfer_length, transfer_length);
141
142 /* Check error. */
143 if (status != UX_SUCCESS)
144 {
145
146 /* Error notification! */
147 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_ERROR);
148 break;
149 }
150
151 /* Frame sent, free it. */
152 stream -> ux_device_class_audio_stream_transfer_pos -> ux_device_class_audio_frame_length = 0;
153
154 /* Get actual transfer length. */
155 actual_length = transfer -> ux_slave_transfer_request_actual_length;
156
157 /* Calculate next position. */
158 next_pos = (UCHAR *)stream -> ux_device_class_audio_stream_transfer_pos;
159 next_pos += stream -> ux_device_class_audio_stream_frame_buffer_size;
160 if (next_pos >= stream -> ux_device_class_audio_stream_buffer + stream -> ux_device_class_audio_stream_buffer_size)
161 next_pos = stream -> ux_device_class_audio_stream_buffer;
162 next_frame = (UX_DEVICE_CLASS_AUDIO_FRAME *)next_pos;
163
164 /* Underflow check! */
165 if (transfer_length)
166 {
167
168 /* Advance position. */
169 stream -> ux_device_class_audio_stream_transfer_pos = next_frame;
170
171 /* Error trap! */
172 if (next_frame -> ux_device_class_audio_frame_length == 0)
173 {
174 stream -> ux_device_class_audio_stream_buffer_error_count ++;
175 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_BUFFER_OVERFLOW);
176 }
177 }
178 else
179 {
180
181 /* Advance position if next frame available. */
182 if (next_frame -> ux_device_class_audio_frame_length)
183 stream -> ux_device_class_audio_stream_transfer_pos = next_frame;
184 else
185 {
186
187 /* Error trap! */
188 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_BUFFER_OVERFLOW);
189 stream -> ux_device_class_audio_stream_buffer_error_count ++;
190 }
191 }
192
193 /* Invoke notification callback. */
194 if (stream -> ux_device_class_audio_stream_callbacks.ux_device_class_audio_stream_frame_done != UX_NULL)
195 stream -> ux_device_class_audio_stream_callbacks.ux_device_class_audio_stream_frame_done(stream, actual_length);
196 }
197
198 /* We need to suspend ourselves. We will be resumed by the device enumeration module or when a change of alternate setting happens. */
199 _ux_device_thread_suspend(&stream -> ux_device_class_audio_stream_thread);
200 }
201 }
202
203