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 /**************************************************************************/
35 /* */
36 /* FUNCTION RELEASE */
37 /* */
38 /* _ux_device_class_cdc_acm_write_run PORTABLE C */
39 /* 6.3.0 */
40 /* AUTHOR */
41 /* */
42 /* Chaoqiong Xiao, Microsoft Corporation */
43 /* */
44 /* DESCRIPTION */
45 /* */
46 /* This function writes to the CDC class. */
47 /* */
48 /* It's for standalone mode. */
49 /* */
50 /* INPUT */
51 /* */
52 /* cdc_acm Address of cdc_acm class */
53 /* instance */
54 /* buffer Pointer to data to write */
55 /* requested_length Length of bytes to write */
56 /* actual_length Pointer to save number of */
57 /* bytes written */
58 /* */
59 /* OUTPUT */
60 /* */
61 /* State machine Status to check */
62 /* UX_STATE_NEXT Transfer done, to next state */
63 /* UX_STATE_EXIT Abnormal, to reset state */
64 /* UX_STATE_ERROR Error occurred */
65 /* (others) Keep running, waiting */
66 /* */
67 /* CALLS */
68 /* */
69 /* _ux_device_stack_transfer_request Transfer request */
70 /* _ux_utility_memory_copy Copy memory */
71 /* */
72 /* CALLED BY */
73 /* */
74 /* Application */
75 /* */
76 /* RELEASE HISTORY */
77 /* */
78 /* DATE NAME DESCRIPTION */
79 /* */
80 /* 01-31-2022 Chaoqiong Xiao Initial Version 6.1.10 */
81 /* 07-29-2022 Chaoqiong Xiao Modified comment(s), */
82 /* fixed parameter/variable */
83 /* names conflict C++ keyword, */
84 /* added auto ZLP support, */
85 /* resulting in version 6.1.12 */
86 /* 10-31-2022 Yajun Xia Modified comment(s), */
87 /* fixed return code, */
88 /* resulting in version 6.2.0 */
89 /* 10-31-2023 Yajun Xia, CQ Xiao Modified comment(s), */
90 /* added zero copy support, */
91 /* added a new mode to manage */
92 /* endpoint buffer in classes, */
93 /* fixed return code, */
94 /* resulting in version 6.3.0 */
95 /* */
96 /**************************************************************************/
_ux_device_class_cdc_acm_write_run(UX_SLAVE_CLASS_CDC_ACM * cdc_acm,UCHAR * buffer,ULONG requested_length,ULONG * actual_length)97 UINT _ux_device_class_cdc_acm_write_run(UX_SLAVE_CLASS_CDC_ACM *cdc_acm,
98 UCHAR *buffer, ULONG requested_length, ULONG *actual_length)
99 {
100
101 UX_SLAVE_ENDPOINT *endpoint;
102 UX_SLAVE_DEVICE *device;
103 UX_SLAVE_INTERFACE *interface_ptr;
104 UX_SLAVE_TRANSFER *transfer_request;
105 UINT status = 0;
106 #if (UX_DEVICE_ENDPOINT_BUFFER_OWNER != 1) || !defined(UX_DEVICE_CLASS_CDC_ACM_ZERO_COPY)
107 UINT zlp = UX_FALSE;
108 #endif
109
110 /* If trace is enabled, insert this event into the trace buffer. */
111 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)
112
113 #ifndef UX_DEVICE_CLASS_CDC_ACM_TRANSMISSION_DISABLE
114
115 /* Check if current cdc-acm is using callback or not. We cannot use direct writes with callback on. */
116 if (cdc_acm -> ux_slave_class_cdc_acm_transmission_status == UX_TRUE)
117
118 /* Not allowed. */
119 return(UX_STATE_ERROR);
120 #endif
121
122 /* Get the pointer to the device. */
123 device = &_ux_system_slave -> ux_system_slave_device;
124
125 /* As long as the device is in the CONFIGURED state. */
126 if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED)
127 {
128
129 /* Error trap. */
130 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CONFIGURATION_HANDLE_UNKNOWN);
131
132 /* If trace is enabled, insert this event into the trace buffer. */
133 UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONFIGURATION_HANDLE_UNKNOWN, device, 0, 0, UX_TRACE_ERRORS, 0, 0)
134
135 /* Cannot proceed with command, the interface is down. */
136 cdc_acm -> ux_device_class_cdc_acm_write_state = UX_STATE_RESET;
137 cdc_acm -> ux_device_class_cdc_acm_write_status = UX_CONFIGURATION_HANDLE_UNKNOWN;
138
139 return(UX_STATE_EXIT);
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 /* We are writing to the IN endpoint. */
157 transfer_request = &endpoint -> ux_slave_endpoint_transfer_request;
158
159 #if defined(UX_DEVICE_CLASS_CDC_ACM_OWN_ENDPOINT_BUFFER)
160 transfer_request -> ux_slave_transfer_request_data_pointer =
161 UX_DEVICE_CLASS_CDC_ACM_WRITE_BUFFER(cdc_acm);
162 #endif
163
164 #if (UX_DEVICE_ENDPOINT_BUFFER_OWNER == 1) && defined(UX_DEVICE_CLASS_CDC_ACM_ZERO_COPY)
165
166 /* Run the transfer state machine. */
167 if(cdc_acm -> ux_device_class_cdc_acm_write_state == UX_STATE_RESET)
168 {
169
170 /* If trace is enabled, insert this event into the trace buffer. */
171 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)
172
173 cdc_acm -> ux_device_class_cdc_acm_write_state = UX_DEVICE_CLASS_CDC_ACM_WRITE_WAIT;
174 cdc_acm -> ux_device_class_cdc_acm_write_status = UX_TRANSFER_NO_ANSWER;
175 transfer_request -> ux_slave_transfer_request_data_pointer = buffer;
176 UX_SLAVE_TRANSFER_STATE_RESET(transfer_request);
177 }
178
179 /* Issue the transfer request. */
180 #if defined(UX_DEVICE_CLASS_CDC_ACM_WRITE_AUTO_ZLP)
181 status = _ux_device_stack_transfer_run(transfer_request, requested_length, requested_length + 1);
182 #else
183 status = _ux_device_stack_transfer_run(transfer_request, requested_length, requested_length);
184 #endif
185
186 /* Error case. */
187 if (status < UX_STATE_NEXT)
188 {
189 cdc_acm -> ux_device_class_cdc_acm_write_state = UX_STATE_RESET;
190 cdc_acm -> ux_device_class_cdc_acm_write_status =
191 transfer_request -> ux_slave_transfer_request_completion_code;
192 return(UX_STATE_ERROR);
193 }
194
195 /* Success case. */
196 if (status == UX_STATE_NEXT)
197 {
198
199 /* Last transfer status. */
200 cdc_acm -> ux_device_class_cdc_acm_write_status =
201 transfer_request -> ux_slave_transfer_request_completion_code;
202
203 /* Update actual length. */
204 *actual_length = transfer_request -> ux_slave_transfer_request_actual_length;
205
206 /* It's done. */
207 cdc_acm -> ux_device_class_cdc_acm_write_state = UX_STATE_RESET;
208 }
209
210 return(status);
211 #else
212
213 /* Handle state cases. */
214 switch(cdc_acm -> ux_device_class_cdc_acm_write_state)
215 {
216 case UX_STATE_RESET:
217 cdc_acm -> ux_device_class_cdc_acm_write_state = UX_DEVICE_CLASS_CDC_ACM_WRITE_START;
218 cdc_acm -> ux_device_class_cdc_acm_write_status = UX_TRANSFER_NO_ANSWER;
219 cdc_acm -> ux_device_class_cdc_acm_write_buffer = buffer;
220 cdc_acm -> ux_device_class_cdc_acm_write_requested_length = requested_length;
221 cdc_acm -> ux_device_class_cdc_acm_write_actual_length = 0;
222 cdc_acm -> ux_device_class_cdc_acm_write_host_length = UX_DEVICE_CLASS_CDC_ACM_WRITE_BUFFER_SIZE;
223 if (requested_length == 0)
224 zlp = UX_TRUE;
225
226 /* Fall through. */
227 case UX_DEVICE_CLASS_CDC_ACM_WRITE_START:
228
229 /* Get remaining requested length. */
230 requested_length = cdc_acm -> ux_device_class_cdc_acm_write_requested_length -
231 cdc_acm -> ux_device_class_cdc_acm_write_actual_length;
232
233 /* There is no remaining, we are done. */
234 if (requested_length == 0 && !zlp)
235 {
236 *actual_length = cdc_acm -> ux_device_class_cdc_acm_write_actual_length;
237 cdc_acm -> ux_device_class_cdc_acm_write_state = UX_STATE_RESET;
238 cdc_acm -> ux_device_class_cdc_acm_write_status = UX_SUCCESS;
239 return(UX_STATE_NEXT);
240 }
241
242 /* Check if we have enough in the local buffer. */
243 if (requested_length > UX_DEVICE_CLASS_CDC_ACM_WRITE_BUFFER_SIZE)
244
245 /* We have too much to transfer. */
246 cdc_acm -> ux_device_class_cdc_acm_write_transfer_length =
247 UX_DEVICE_CLASS_CDC_ACM_WRITE_BUFFER_SIZE;
248
249 else
250 {
251
252 /* We can proceed with the demanded length. */
253 cdc_acm -> ux_device_class_cdc_acm_write_transfer_length = requested_length;
254
255 #if !defined(UX_DEVICE_CLASS_CDC_ACM_WRITE_AUTO_ZLP)
256
257 /* Assume expected length and transfer length match. */
258 cdc_acm -> ux_device_class_cdc_acm_write_host_length = requested_length;
259 #else
260
261 /* Assume expected more than transfer to let stack append ZLP if needed. */
262 cdc_acm -> ux_device_class_cdc_acm_write_host_length = UX_DEVICE_CLASS_CDC_ACM_WRITE_BUFFER_SIZE + 1;
263 #endif
264 }
265
266
267 /* On a out, we copy the buffer to the caller. Not very efficient but it makes the API
268 easier. */
269 _ux_utility_memory_copy(transfer_request -> ux_slave_transfer_request_data_pointer,
270 cdc_acm -> ux_device_class_cdc_acm_write_buffer,
271 cdc_acm -> ux_device_class_cdc_acm_write_transfer_length); /* Use case of memcpy is verified. */
272
273 /* Next state. */
274 cdc_acm -> ux_device_class_cdc_acm_write_state = UX_DEVICE_CLASS_CDC_ACM_WRITE_WAIT;
275 UX_SLAVE_TRANSFER_STATE_RESET(transfer_request);
276
277 /* Fall through. */
278 case UX_DEVICE_CLASS_CDC_ACM_WRITE_WAIT:
279
280 /* Send the request to the device controller. */
281 status = _ux_device_stack_transfer_run(transfer_request,
282 cdc_acm -> ux_device_class_cdc_acm_write_transfer_length,
283 cdc_acm -> ux_device_class_cdc_acm_write_host_length);
284
285 /* Error case. */
286 if (status < UX_STATE_NEXT)
287 {
288
289 cdc_acm -> ux_device_class_cdc_acm_write_state = UX_STATE_RESET;
290 cdc_acm -> ux_device_class_cdc_acm_write_status =
291 transfer_request -> ux_slave_transfer_request_completion_code;
292 return(UX_STATE_ERROR);
293 }
294
295 /* Success case. */
296 if (status == UX_STATE_NEXT)
297 {
298
299 /* Next buffer address. */
300 cdc_acm -> ux_device_class_cdc_acm_write_buffer +=
301 transfer_request -> ux_slave_transfer_request_actual_length;
302
303 /* Set the length actually received. */
304 cdc_acm -> ux_device_class_cdc_acm_write_actual_length +=
305 transfer_request -> ux_slave_transfer_request_actual_length;
306
307 /* Last transfer status. */
308 cdc_acm -> ux_device_class_cdc_acm_write_status =
309 transfer_request -> ux_slave_transfer_request_completion_code;
310
311 /* Update actual done length. */
312 *actual_length = cdc_acm -> ux_device_class_cdc_acm_write_actual_length;
313
314 /* Check ZLP case. */
315 if (cdc_acm -> ux_device_class_cdc_acm_write_requested_length == 0)
316 {
317 cdc_acm -> ux_device_class_cdc_acm_write_state = UX_STATE_RESET;
318 return(UX_STATE_NEXT);
319 }
320
321 /* Next state. */
322 cdc_acm -> ux_device_class_cdc_acm_write_state = UX_DEVICE_CLASS_CDC_ACM_WRITE_START;
323 }
324
325 /* Keep waiting. */
326 return(UX_STATE_WAIT);
327
328 default: /* Error. */
329 cdc_acm -> ux_device_class_cdc_acm_write_state = UX_STATE_RESET;
330 break;
331 }
332
333 /* Error case. */
334 return(UX_STATE_EXIT);
335 #endif
336 }
337
338 /**************************************************************************/
339 /* */
340 /* FUNCTION RELEASE */
341 /* */
342 /* _uxe_device_class_cdc_acm_write_run PORTABLE C */
343 /* 6.3.0 */
344 /* AUTHOR */
345 /* */
346 /* Yajun Xia, Microsoft Corporation */
347 /* */
348 /* DESCRIPTION */
349 /* */
350 /* This function checks errors in CDC ACM class write process. */
351 /* */
352 /* It's for standalone mode. */
353 /* */
354 /* INPUT */
355 /* */
356 /* cdc_acm Address of cdc_acm class */
357 /* instance */
358 /* buffer Pointer to data to write */
359 /* requested_length Length of bytes to write */
360 /* actual_length Pointer to save number of */
361 /* bytes written */
362 /* */
363 /* OUTPUT */
364 /* */
365 /* State machine Status to check */
366 /* UX_STATE_NEXT Transfer done, to next state */
367 /* UX_STATE_EXIT Abnormal, to reset state */
368 /* UX_STATE_ERROR Error occurred */
369 /* (others) Keep running, waiting */
370 /* */
371 /* CALLS */
372 /* */
373 /* _ux_device_class_cdc_acm_write_run CDC ACM class write process */
374 /* */
375 /* CALLED BY */
376 /* */
377 /* Application */
378 /* */
379 /* RELEASE HISTORY */
380 /* */
381 /* DATE NAME DESCRIPTION */
382 /* */
383 /* 10-31-2023 Yajun Xia Initial Version 6.3.0 */
384 /* */
385 /**************************************************************************/
_uxe_device_class_cdc_acm_write_run(UX_SLAVE_CLASS_CDC_ACM * cdc_acm,UCHAR * buffer,ULONG requested_length,ULONG * actual_length)386 UINT _uxe_device_class_cdc_acm_write_run(UX_SLAVE_CLASS_CDC_ACM *cdc_acm,
387 UCHAR *buffer, ULONG requested_length, ULONG *actual_length)
388 {
389
390 /* Sanity checks. */
391 if ((cdc_acm == UX_NULL) || ((buffer == UX_NULL) && (requested_length > 0)) || (actual_length == UX_NULL))
392 {
393 return(UX_STATE_ERROR);
394 }
395
396 return (_ux_device_class_cdc_acm_write_run(cdc_acm, buffer, requested_length, actual_length));
397 }
398
399 #endif
400