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