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 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_STANDALONE)
32 /**************************************************************************/
33 /* */
34 /* FUNCTION RELEASE */
35 /* */
36 /* _ux_device_class_cdc_acm_write PORTABLE C */
37 /* 6.3.0 */
38 /* AUTHOR */
39 /* */
40 /* Chaoqiong Xiao, Microsoft Corporation */
41 /* */
42 /* DESCRIPTION */
43 /* */
44 /* This function writes to the CDC class. */
45 /* */
46 /* It's for RTOS mode. */
47 /* */
48 /* INPUT */
49 /* */
50 /* cdc_acm Address of cdc_acm class */
51 /* instance */
52 /* buffer Pointer to data to write */
53 /* requested_length Length of bytes to write, */
54 /* set to 0 to issue ZLP */
55 /* actual_length Pointer to save number of */
56 /* bytes written */
57 /* */
58 /* OUTPUT */
59 /* */
60 /* None */
61 /* */
62 /* CALLS */
63 /* */
64 /* _ux_utility_memory_copy Copy memory */
65 /* _ux_device_stack_transfer_request Transfer request */
66 /* _ux_device_mutex_on Take Mutex */
67 /* _ux_device_mutex_off Release Mutex */
68 /* */
69 /* CALLED BY */
70 /* */
71 /* Application */
72 /* */
73 /* RELEASE HISTORY */
74 /* */
75 /* DATE NAME DESCRIPTION */
76 /* */
77 /* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */
78 /* 09-30-2020 Chaoqiong Xiao Modified comment(s), */
79 /* verified memset and memcpy */
80 /* cases, */
81 /* resulting in version 6.1 */
82 /* 10-15-2021 Chaoqiong Xiao Modified comment(s), */
83 /* fixed compile issue, */
84 /* resulting in version 6.1.9 */
85 /* 01-31-2022 Chaoqiong Xiao Modified comment(s), */
86 /* resulting in version 6.1.10 */
87 /* 04-25-2022 Chaoqiong Xiao Modified comment(s), */
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 Yajun Xia, CQ 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_write(UX_SLAVE_CLASS_CDC_ACM * cdc_acm,UCHAR * buffer,ULONG requested_length,ULONG * actual_length)101 UINT _ux_device_class_cdc_acm_write(UX_SLAVE_CLASS_CDC_ACM *cdc_acm, UCHAR *buffer,
102 ULONG requested_length, ULONG *actual_length)
103 {
104
105 UX_SLAVE_ENDPOINT *endpoint;
106 UX_SLAVE_DEVICE *device;
107 UX_SLAVE_INTERFACE *interface_ptr;
108 UX_SLAVE_TRANSFER *transfer_request;
109 ULONG local_requested_length;
110 ULONG local_host_length;
111 UINT status = 0;
112
113 /* If trace is enabled, insert this event into the trace buffer. */
114 UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_CDC_ACM_WRITE, cdc_acm, buffer, requested_length, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0)
115
116 #ifndef UX_DEVICE_CLASS_CDC_ACM_TRANSMISSION_DISABLE
117
118 /* Check if current cdc-acm is using callback or not. We cannot use direct reads with callback on. */
119 if (cdc_acm -> ux_slave_class_cdc_acm_transmission_status == UX_TRUE)
120
121 /* Not allowed. */
122 return(UX_ERROR);
123 #endif
124
125 /* Get the pointer to the device. */
126 device = &_ux_system_slave -> ux_system_slave_device;
127
128 /* As long as the device is in the CONFIGURED state. */
129 if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED)
130 {
131
132 /* Error trap. */
133 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CONFIGURATION_HANDLE_UNKNOWN);
134
135 /* If trace is enabled, insert this event into the trace buffer. */
136 UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONFIGURATION_HANDLE_UNKNOWN, device, 0, 0, UX_TRACE_ERRORS, 0, 0)
137
138 /* Cannot proceed with command, the interface is down. */
139 return(UX_CONFIGURATION_HANDLE_UNKNOWN);
140 }
141
142 /* We need the interface to the class. */
143 interface_ptr = cdc_acm -> ux_slave_class_cdc_acm_interface;
144
145 /* Locate the endpoints. */
146 endpoint = interface_ptr -> ux_slave_interface_first_endpoint;
147
148 /* Check the endpoint direction, if IN we have the correct endpoint. */
149 if ((endpoint -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) != UX_ENDPOINT_IN)
150 {
151
152 /* So the next endpoint has to be the IN endpoint. */
153 endpoint = endpoint -> ux_slave_endpoint_next_endpoint;
154 }
155
156 /* Protect this thread. */
157 _ux_device_mutex_on(&cdc_acm -> ux_slave_class_cdc_acm_endpoint_in_mutex);
158
159 /* We are writing to the IN endpoint. */
160 transfer_request = &endpoint -> ux_slave_endpoint_transfer_request;
161
162 #if UX_DEVICE_ENDPOINT_BUFFER_OWNER == 1
163 #if !defined(UX_DEVICE_CLASS_CDC_ACM_ZERO_COPY)
164 transfer_request -> ux_slave_transfer_request_data_pointer =
165 UX_DEVICE_CLASS_CDC_ACM_WRITE_BUFFER(cdc_acm);
166 #else
167 transfer_request -> ux_slave_transfer_request_data_pointer = buffer;
168 #endif
169 #endif
170
171 /* Reset the actual length. */
172 *actual_length = 0;
173
174 /* Check if the application forces a 0 length packet. */
175 if (device -> ux_slave_device_state == UX_DEVICE_CONFIGURED && requested_length == 0)
176 {
177
178 /* Send the request for 0 byte packet to the device controller. */
179 status = _ux_device_stack_transfer_request(transfer_request, 0, 0);
180
181 /* Free Mutex resource. */
182 _ux_device_mutex_off(&cdc_acm -> ux_slave_class_cdc_acm_endpoint_in_mutex);
183
184 /* Return the status. */
185 return(status);
186
187
188 }
189 else
190 {
191
192 #if (UX_DEVICE_ENDPOINT_BUFFER_OWNER == 1) && defined(UX_DEVICE_CLASS_CDC_ACM_ZERO_COPY)
193
194 /* Check if device is configured. */
195 if (device -> ux_slave_device_state == UX_DEVICE_CONFIGURED)
196 {
197
198 #if defined(UX_DEVICE_CLASS_CDC_ACM_WRITE_AUTO_ZLP)
199
200 /* Issue with larger host length to append zlp if necessary. */
201 local_host_length = requested_length + 1;
202 #else
203 local_host_length = requested_length;
204 #endif
205 local_requested_length = requested_length;
206
207 /* Issue the transfer request. */
208 status = _ux_device_stack_transfer_request(transfer_request, local_requested_length, local_host_length);
209 if (status == UX_SUCCESS)
210 *actual_length = transfer_request -> ux_slave_transfer_request_actual_length;
211 }
212 #else
213
214 /* Check if we need more transactions. */
215 local_host_length = UX_DEVICE_CLASS_CDC_ACM_WRITE_BUFFER_SIZE;
216 while (device -> ux_slave_device_state == UX_DEVICE_CONFIGURED && requested_length != 0)
217 {
218
219 /* Check if we have enough in the local buffer. */
220 if (requested_length > UX_DEVICE_CLASS_CDC_ACM_WRITE_BUFFER_SIZE)
221
222 /* We have too much to transfer. */
223 local_requested_length = UX_DEVICE_CLASS_CDC_ACM_WRITE_BUFFER_SIZE;
224
225 else
226 {
227
228 /* We can proceed with the demanded length. */
229 local_requested_length = requested_length;
230
231 #if !defined(UX_DEVICE_CLASS_CDC_ACM_WRITE_AUTO_ZLP)
232
233 /* Assume the length match expectation. */
234 local_host_length = requested_length;
235 #else
236
237 /* Assume expecting more, so ZLP is appended in stack. */
238 local_host_length = UX_DEVICE_CLASS_CDC_ACM_WRITE_BUFFER_SIZE + 1;
239 #endif
240 }
241
242 /* On a out, we copy the buffer to the caller. Not very efficient but it makes the API
243 easier. */
244 _ux_utility_memory_copy(transfer_request -> ux_slave_transfer_request_data_pointer,
245 buffer, local_requested_length); /* Use case of memcpy is verified. */
246
247 /* Send the request to the device controller. */
248 status = _ux_device_stack_transfer_request(transfer_request, local_requested_length, local_host_length);
249
250 /* Check the status */
251 if (status == UX_SUCCESS)
252 {
253
254 /* Next buffer address. */
255 buffer += transfer_request -> ux_slave_transfer_request_actual_length;
256
257 /* Set the length actually received. */
258 *actual_length += transfer_request -> ux_slave_transfer_request_actual_length;
259
260 /* Decrement what left has to be done. */
261 requested_length -= transfer_request -> ux_slave_transfer_request_actual_length;
262
263 }
264
265 else
266 {
267
268 /* Free Mutex resource. */
269 _ux_device_mutex_off(&cdc_acm -> ux_slave_class_cdc_acm_endpoint_in_mutex);
270
271 /* We had an error, abort. */
272 return(status);
273 }
274 }
275 #endif /* _BUFF_OWNER && _ZERO_COPY */
276 }
277
278
279 /* Free Mutex resource. */
280 _ux_device_mutex_off(&cdc_acm -> ux_slave_class_cdc_acm_endpoint_in_mutex);
281
282 /* Check why we got here, either completion or device was extracted. */
283 if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED)
284 {
285
286 /* Error trap. */
287 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_NO_ANSWER);
288
289 /* If trace is enabled, insert this event into the trace buffer. */
290 UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_NO_ANSWER, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0)
291
292 /* Device must have been extracted. */
293 return (UX_TRANSFER_NO_ANSWER);
294 }
295 else
296
297 /* Simply return the last transaction result. */
298 return(status);
299
300 }
301
302 /**************************************************************************/
303 /* */
304 /* FUNCTION RELEASE */
305 /* */
306 /* _uxe_device_class_cdc_acm_write PORTABLE C */
307 /* 6.3.0 */
308 /* AUTHOR */
309 /* */
310 /* Yajun Xia, Microsoft Corporation */
311 /* */
312 /* DESCRIPTION */
313 /* */
314 /* This function checks errors in CDC ACM class write function. */
315 /* */
316 /* INPUT */
317 /* */
318 /* cdc_acm Address of cdc_acm class */
319 /* instance */
320 /* buffer Pointer to data to write */
321 /* requested_length Length of bytes to write, */
322 /* set to 0 to issue ZLP */
323 /* actual_length Pointer to save number of */
324 /* bytes written */
325 /* */
326 /* OUTPUT */
327 /* */
328 /* None */
329 /* */
330 /* CALLS */
331 /* */
332 /* _ux_device_class_cdc_acm_write CDC ACM class write function */
333 /* */
334 /* CALLED BY */
335 /* */
336 /* Application */
337 /* */
338 /* RELEASE HISTORY */
339 /* */
340 /* DATE NAME DESCRIPTION */
341 /* */
342 /* 10-31-2023 Yajun Xia Initial Version 6.3.0 */
343 /* */
344 /**************************************************************************/
_uxe_device_class_cdc_acm_write(UX_SLAVE_CLASS_CDC_ACM * cdc_acm,UCHAR * buffer,ULONG requested_length,ULONG * actual_length)345 UINT _uxe_device_class_cdc_acm_write(UX_SLAVE_CLASS_CDC_ACM *cdc_acm, UCHAR *buffer,
346 ULONG requested_length, ULONG *actual_length)
347 {
348
349 /* Sanity checks. */
350 if ((cdc_acm == UX_NULL) || ((buffer == UX_NULL) && (requested_length > 0)) || (actual_length == UX_NULL))
351 {
352 return (UX_INVALID_PARAMETER);
353 }
354
355 return (_ux_device_class_cdc_acm_write(cdc_acm, buffer, requested_length, actual_length));
356 }
357
358 #endif