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 CDC_ACM 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_cdc_acm.h"
28 #include "ux_device_stack.h"
29
30
31 #if !defined(UX_DEVICE_CLASS_CDC_ACM_TRANSMISSION_DISABLE) && !defined(UX_DEVICE_STANDALONE)
32 /**************************************************************************/
33 /* */
34 /* FUNCTION RELEASE */
35 /* */
36 /* _ux_device_class_cdc_acm_bulkin_thread PORTABLE C */
37 /* 6.3.0 */
38 /* AUTHOR */
39 /* */
40 /* Chaoqiong Xiao, Microsoft Corporation */
41 /* */
42 /* DESCRIPTION */
43 /* */
44 /* This function is the thread of the cdc_acm bulkin endpoint. The bulk*/
45 /* IN endpoint is used when the device wants to write data to be sent */
46 /* to the host. */
47 /* */
48 /* It's for RTOS mode. */
49 /* */
50 /* INPUT */
51 /* */
52 /* cdc_acm_class Address of cdc_acm class */
53 /* container */
54 /* */
55 /* OUTPUT */
56 /* */
57 /* None */
58 /* */
59 /* CALLS */
60 /* */
61 /* _ux_device_stack_transfer_request Request transfer */
62 /* */
63 /* CALLED BY */
64 /* */
65 /* ThreadX */
66 /* */
67 /* RELEASE HISTORY */
68 /* */
69 /* DATE NAME DESCRIPTION */
70 /* */
71 /* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */
72 /* 09-30-2020 Chaoqiong Xiao Modified comment(s), */
73 /* verified memset and memcpy */
74 /* cases, used UX prefix to */
75 /* refer to TX symbols instead */
76 /* of using them directly, */
77 /* resulting in version 6.1 */
78 /* 04-02-2021 Chaoqiong Xiao Modified comment(s), */
79 /* added macro to disable */
80 /* transmission support, */
81 /* resulting in version 6.1.6 */
82 /* 01-31-2022 Chaoqiong Xiao Modified comment(s), */
83 /* refined macros names, */
84 /* resulting in version 6.1.10 */
85 /* 04-25-2022 Chaoqiong Xiao Modified comment(s), */
86 /* used whole buffer for write,*/
87 /* refined macros names, */
88 /* resulting in version 6.1.11 */
89 /* 07-29-2022 Chaoqiong Xiao Modified comment(s), */
90 /* fixed parameter/variable */
91 /* names conflict C++ keyword, */
92 /* added auto ZLP support, */
93 /* resulting in version 6.1.12 */
94 /* 10-31-2023 Chaoqiong Xiao Modified comment(s), */
95 /* added zero copy support, */
96 /* added a new mode to manage */
97 /* endpoint buffer in classes, */
98 /* resulting in version 6.3.0 */
99 /* */
100 /**************************************************************************/
_ux_device_class_cdc_acm_bulkin_thread(ULONG cdc_acm_class)101 VOID _ux_device_class_cdc_acm_bulkin_thread(ULONG cdc_acm_class)
102 {
103
104 UX_SLAVE_CLASS_CDC_ACM *cdc_acm;
105 UX_SLAVE_DEVICE *device;
106 UX_SLAVE_ENDPOINT *endpoint;
107 UX_SLAVE_INTERFACE *interface_ptr;
108 UX_SLAVE_TRANSFER *transfer_request;
109 UINT status;
110 ULONG actual_flags;
111 ULONG transfer_length;
112 ULONG host_length;
113 ULONG total_length;
114 ULONG sent_length;
115
116
117 /* Get the cdc_acm instance from this class container. */
118 UX_THREAD_EXTENSION_PTR_GET(cdc_acm, UX_SLAVE_CLASS_CDC_ACM, cdc_acm_class)
119
120 /* Get the pointer to the device. */
121 device = &_ux_system_slave -> ux_system_slave_device;
122
123 /* This is the first time we are activated. We need the interface to the class. */
124 interface_ptr = cdc_acm -> ux_slave_class_cdc_acm_interface;
125
126 /* Locate the endpoints. */
127 endpoint = interface_ptr -> ux_slave_interface_first_endpoint;
128
129 /* Check the endpoint direction, if IN we have the correct endpoint. */
130 if ((endpoint -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) != UX_ENDPOINT_IN)
131 {
132
133 /* So the next endpoint has to be the IN endpoint. */
134 endpoint = endpoint -> ux_slave_endpoint_next_endpoint;
135 }
136
137 #if (UX_DEVICE_ENDPOINT_BUFFER_OWNER == 1) && !defined(UX_DEVICE_CLASS_CDC_ACM_ZERO_COPY)
138 endpoint -> ux_slave_endpoint_transfer_request.ux_slave_transfer_request_data_pointer =
139 UX_DEVICE_CLASS_CDC_ACM_WRITE_BUFFER(cdc_acm);
140 #endif
141
142 /* This thread runs forever but can be suspended or resumed. */
143 while(1)
144 {
145
146 /* Get the transfer request for the bulk IN pipe. */
147 transfer_request = &endpoint -> ux_slave_endpoint_transfer_request;
148
149 /* As long as the device is in the CONFIGURED state. */
150 while (device -> ux_slave_device_state == UX_DEVICE_CONFIGURED)
151 {
152
153 /* Wait until we have a event sent by the application. */
154 status = _ux_utility_event_flags_get(&cdc_acm -> ux_slave_class_cdc_acm_event_flags_group, UX_DEVICE_CLASS_CDC_ACM_WRITE_EVENT,
155 UX_OR_CLEAR, &actual_flags, UX_WAIT_FOREVER);
156
157 /* Check the completion code. */
158 if (status == UX_SUCCESS)
159 {
160
161 /* Get the length of the entire buffer to send. */
162 total_length = cdc_acm -> ux_slave_class_cdc_acm_callback_total_length;
163
164 #if defined(UX_DEVICE_CLASS_CDC_ACM_ZERO_COPY)
165
166 #if !defined(UX_DEVICE_CLASS_CDC_ACM_WRITE_AUTO_ZLP)
167
168 /* Assume host request length just equal. */
169 host_length = total_length;
170 #else
171
172 /* Assume host request length larger to append ZLP automatically. */
173 host_length = total_length + 1;
174 #endif
175
176 /* Pass all data to lower level transfer. */
177 transfer_length = total_length;
178
179 /* Setup the data pointer. */
180 transfer_request -> ux_slave_transfer_request_data_pointer = cdc_acm -> ux_slave_class_cdc_acm_callback_data_pointer;
181
182 /* Issue the transfer request. */
183 status = _ux_device_stack_transfer_request(transfer_request, transfer_length, host_length);
184
185 /* Update length sent. */
186 sent_length = transfer_request -> ux_slave_transfer_request_actual_length;
187 #else
188
189 /* Duplicate the data pointer to keep a current pointer. */
190 cdc_acm -> ux_slave_class_cdc_acm_callback_current_data_pointer = cdc_acm -> ux_slave_class_cdc_acm_callback_data_pointer;
191
192 /* Reset sent length. */
193 sent_length = 0;
194
195 /* Special ZLP case. */
196 if (total_length == 0)
197
198 /* Send the zlp to the host. */
199 status = _ux_device_stack_transfer_request(transfer_request, 0, 0);
200
201 else
202 {
203
204 /* We should send the total length. But we may have a case of ZLP. */
205 host_length = UX_DEVICE_CLASS_CDC_ACM_WRITE_BUFFER_SIZE;
206 while (total_length)
207 {
208
209 /* Check the length remaining to send. */
210 if (total_length > UX_DEVICE_CLASS_CDC_ACM_WRITE_BUFFER_SIZE)
211
212 /* We can't fit all the length. */
213 transfer_length = UX_DEVICE_CLASS_CDC_ACM_WRITE_BUFFER_SIZE;
214
215 else
216 {
217
218 /* We can send everything. */
219 transfer_length = total_length;
220
221 #if !defined(UX_DEVICE_CLASS_CDC_ACM_WRITE_AUTO_ZLP)
222
223 /* Assume expected length matches length to send. */
224 host_length = total_length;
225 #else
226
227 /* Assume expected more to let stack append ZLP if needed. */
228 host_length = UX_DEVICE_CLASS_CDC_ACM_WRITE_BUFFER_SIZE + 1;
229 #endif
230 }
231
232 /* Copy the payload locally. */
233 _ux_utility_memory_copy (transfer_request -> ux_slave_transfer_request_data_pointer,
234 cdc_acm -> ux_slave_class_cdc_acm_callback_current_data_pointer,
235 transfer_length); /* Use case of memcpy is verified. */
236
237 /* Send the acm payload to the host. */
238 status = _ux_device_stack_transfer_request(transfer_request, transfer_length, host_length);
239
240 /* Check the status. */
241 if (status != UX_SUCCESS)
242 {
243
244 /* Reset total_length as we need to get out of loop. */
245 total_length = 0;
246 }
247 else
248 {
249
250 /* Update sent length. */
251 sent_length += transfer_length;
252
253 /* Decrement length to be sent. */
254 total_length -= transfer_length;
255
256 /* And update the current pointer. */
257 cdc_acm -> ux_slave_class_cdc_acm_callback_current_data_pointer += transfer_length;
258 }
259 }
260 }
261 #endif
262
263 /* Schedule of transmission was completed. */
264 cdc_acm -> ux_slave_class_cdc_acm_scheduled_write = UX_FALSE;
265
266 /* We get here when the entire user data payload has been sent or if there is an error. */
267 /* If there is a callback defined by the application, send the transaction event to it. */
268 if (cdc_acm -> ux_device_class_cdc_acm_write_callback != UX_NULL)
269
270 /* Callback exists. */
271 cdc_acm -> ux_device_class_cdc_acm_write_callback(cdc_acm, status, sent_length);
272
273 /* Now we return to wait for an event from the application or the user to stop the transmission. */
274 }
275 else
276 {
277 break;
278 }
279 }
280
281 /* We need to suspend ourselves. We will be resumed by the device enumeration module or when a change of alternate setting happens. */
282 _ux_device_thread_suspend(&cdc_acm -> ux_slave_class_cdc_acm_bulkin_thread);
283 }
284 }
285 #endif
286