GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usbx_device_classes/src/ux_device_class_cdc_ecm_bulkin_thread.c Lines: 39 39 100.0 %
Date: 2024-12-12 17:16:36 Branches: 18 18 100.0 %

Line Branch Exec Source
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_ECM 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_ecm.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_ecm_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_ecm 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
/*  INPUT                                                                 */
49
/*                                                                        */
50
/*    cdc_ecm_class                             Address of cdc_ecm class  */
51
/*                                                container               */
52
/*                                                                        */
53
/*  OUTPUT                                                                */
54
/*                                                                        */
55
/*    None                                                                */
56
/*                                                                        */
57
/*  CALLS                                                                 */
58
/*                                                                        */
59
/*    _ux_device_stack_transfer_request     Request transfer              */
60
/*    _ux_utility_event_flags_get           Get event flags               */
61
/*    _ux_device_mutex_on                   Take mutex                    */
62
/*    _ux_device_mutex_off                  Free mutex                    */
63
/*                                                                        */
64
/*  CALLED BY                                                             */
65
/*                                                                        */
66
/*    ThreadX                                                             */
67
/*                                                                        */
68
/*  RELEASE HISTORY                                                       */
69
/*                                                                        */
70
/*    DATE              NAME                      DESCRIPTION             */
71
/*                                                                        */
72
/*  05-19-2020     Chaoqiong Xiao           Initial Version 6.0           */
73
/*  09-30-2020     Chaoqiong Xiao           Modified comment(s),          */
74
/*                                            verified memset and memcpy  */
75
/*                                            cases, used UX prefix to    */
76
/*                                            refer to TX symbols instead */
77
/*                                            of using them directly,     */
78
/*                                            resulting in version 6.1    */
79
/*  01-31-2022     Chaoqiong Xiao           Modified comment(s),          */
80
/*                                            refined macros names,       */
81
/*                                            resulting in version 6.1.10 */
82
/*  04-25-2022     Chaoqiong Xiao           Modified comment(s),          */
83
/*                                            fixed standalone compile,   */
84
/*                                            resulting in version 6.1.11 */
85
/*  10-31-2022     Chaoqiong Xiao           Modified comment(s),          */
86
/*                                            used NX API to copy data,   */
87
/*                                            resulting in version 6.2.0  */
88
/*  10-31-2023     Chaoqiong Xiao           Modified comment(s),          */
89
/*                                            added zero copy support,    */
90
/*                                            added a new mode to manage  */
91
/*                                            endpoint buffer in classes, */
92
/*                                            resulting in version 6.3.0  */
93
/*                                                                        */
94
/**************************************************************************/
95
47
VOID  _ux_device_class_cdc_ecm_bulkin_thread(ULONG cdc_ecm_class)
96
{
97
98
UX_SLAVE_CLASS                  *class_ptr;
99
UX_SLAVE_CLASS_CDC_ECM          *cdc_ecm;
100
UX_SLAVE_DEVICE                 *device;
101
UX_SLAVE_TRANSFER               *transfer_request;
102
UINT                            status;
103
ULONG                           actual_flags;
104
NX_PACKET                       *current_packet;
105
ULONG                           transfer_length;
106
ULONG                           copied;
107
#if (UX_DEVICE_ENDPOINT_BUFFER_OWNER == 1) && defined(UX_DEVICE_CLASS_CDC_ECM_ZERO_COPY) && !defined(NX_DISABLE_PACKET_CHAIN)
108
NX_PACKET                       *packet;
109
#endif
110
111
    /* Cast properly the cdc_ecm instance.  */
112
47
    UX_THREAD_EXTENSION_PTR_GET(class_ptr, UX_SLAVE_CLASS, cdc_ecm_class)
113
114
    /* Get the cdc_ecm instance from this class container.  */
115
47
    cdc_ecm =  (UX_SLAVE_CLASS_CDC_ECM *) class_ptr -> ux_slave_class_instance;
116
117
    /* Get the pointer to the device.  */
118
47
    device =  &_ux_system_slave -> ux_system_slave_device;
119
120
    /* This thread runs forever but can be suspended or resumed.  */
121
48
    while (1)
122
    {
123
124
        /* For as long we are configured.  */
125
        while (1)
126
        {
127
128
            /* Wait until either a new packet has been added to the xmit queue,
129
               or until there has been a change in the device state (i.e. disconnection).  */
130
241
            _ux_utility_event_flags_get(&cdc_ecm -> ux_slave_class_cdc_ecm_event_flags_group, (UX_DEVICE_CLASS_CDC_ECM_NEW_BULKIN_EVENT |
131
                                                                                               UX_DEVICE_CLASS_CDC_ECM_NEW_DEVICE_STATE_CHANGE_EVENT),
132
                                                                                              UX_OR_CLEAR, &actual_flags, UX_WAIT_FOREVER);
133
134
            /* Check the completion code and the actual flags returned.  */
135
232
            if ((actual_flags & UX_DEVICE_CLASS_CDC_ECM_NEW_DEVICE_STATE_CHANGE_EVENT) == 0)
136
            {
137
138
                /* Get the transfer request for the bulk IN pipe.  */
139
139
                transfer_request =  &cdc_ecm -> ux_slave_class_cdc_ecm_bulkin_endpoint -> ux_slave_endpoint_transfer_request;
140
141
                /* Parse all packets.  */
142
1220
                while (cdc_ecm -> ux_slave_class_cdc_ecm_xmit_queue != UX_NULL)
143
                {
144
145
                    /* Ensure no other threads are modifying the xmit queue.  */
146
1081
                    _ux_device_mutex_on(&cdc_ecm -> ux_slave_class_cdc_ecm_mutex);
147
148
                    /* Get the current packet in the list.  */
149
1081
                    current_packet =  cdc_ecm -> ux_slave_class_cdc_ecm_xmit_queue;
150
151
                    /* Set the next packet (or a NULL value) as the head of the xmit queue. */
152
1081
                    cdc_ecm -> ux_slave_class_cdc_ecm_xmit_queue =  current_packet -> nx_packet_queue_next;
153
154
                    /* Free Mutex resource.  */
155
1081
                    _ux_device_mutex_off(&cdc_ecm -> ux_slave_class_cdc_ecm_mutex);
156
157
                    /* If the link is down no need to rearm a packet. */
158
1081
                    if (cdc_ecm -> ux_slave_class_cdc_ecm_link_state == UX_DEVICE_CLASS_CDC_ECM_LINK_STATE_UP)
159
                    {
160
#if (UX_DEVICE_ENDPOINT_BUFFER_OWNER == 1) && defined(UX_DEVICE_CLASS_CDC_ECM_ZERO_COPY)
161
162
                        /* Default to success.  */
163
                        status = UX_SUCCESS;
164
165
#ifndef NX_DISABLE_PACKET_CHAIN
166
167
                        /* Check if packet is chained, allocate a new packet to fill the total data.  */
168
                        if (current_packet -> nx_packet_next)
169
                        {
170
171
                            /* Check if collection of chained data can fit in one packet.  */
172
                            if (current_packet -> nx_packet_length >
173
                                current_packet -> nx_packet_pool_owner -> nx_packet_pool_payload_size)
174
                                status = UX_TRANSFER_BUFFER_OVERFLOW;
175
                            else
176
                            {
177
178
                                /* Allocate a new packet for chain data collection.  */
179
                                status = nx_packet_allocate(cdc_ecm -> ux_slave_class_cdc_ecm_packet_pool, &packet,
180
                                            NX_RECEIVE_PACKET, UX_MS_TO_TICK(UX_DEVICE_CLASS_CDC_ECM_PACKET_POOL_WAIT));
181
                                if (status == UX_SUCCESS)
182
                                {
183
184
                                    /* Copy the packet to the buffer.  */
185
                                    status = nx_packet_data_extract_offset(current_packet, 0,
186
                                            packet -> nx_packet_prepend_ptr,
187
                                            current_packet -> nx_packet_length, &copied);
188
                                    if (status == NX_SUCCESS)
189
                                    {
190
                                        packet -> nx_packet_length = current_packet -> nx_packet_length;
191
192
                                        /* Release the chained packet.  */
193
                                        nx_packet_transmit_release(current_packet);
194
195
                                        /* Use copied packet instead.  */
196
                                        current_packet = packet;
197
                                    }
198
                                }
199
                            }
200
201
202
                            /* Can not copy/buffer issue.  */
203
                            if (status != UX_SUCCESS)
204
                                status = UX_TRANSFER_BUFFER_OVERFLOW;
205
206
                        }
207
#endif
208
                        if (status == UX_SUCCESS)
209
                        {
210
211
                            /* Set the transfer request data pointer to the packet buffer.  */
212
                            transfer_request -> ux_slave_transfer_request_data_pointer = current_packet -> nx_packet_prepend_ptr;
213
214
                            /* Calculate the transfer length.  */
215
                            transfer_length =  current_packet -> nx_packet_length;
216
217
                            /* If trace is enabled, insert this event into the trace buffer.  */
218
                            UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_CDC_ECM_PACKET_TRANSMIT, cdc_ecm, 0, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0)
219
220
                            /* Send the request to the device controller.  */
221
                            status =  _ux_device_stack_transfer_request(transfer_request, transfer_length, transfer_length + 1);
222
                        }
223
224
                        /* Check error code. */
225
                        if (status != UX_SUCCESS)
226
                        {
227
228
                            /* Is this not a transfer abort? (this is expected to happen)  */
229
                            if (status != UX_TRANSFER_BUS_RESET)
230
                            {
231
232
                                /* Error trap. */
233
                                _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, status);
234
                            }
235
                        }
236
#else
237
238
                        /* Can the packet fit in the transfer requests data buffer?  */
239
1080
                        if (current_packet -> nx_packet_length <= UX_DEVICE_CLASS_CDC_ECM_BULKIN_BUFFER_SIZE)
240
                        {
241
242
                            /* Copy the packet in the transfer descriptor buffer.  */
243
1078
                            status = nx_packet_data_extract_offset(current_packet, 0,
244
1078
                                    transfer_request -> ux_slave_transfer_request_data_pointer,
245
1078
                                    current_packet -> nx_packet_length, &copied);
246
1078
                            if (status == UX_SUCCESS)
247
                            {
248
249
                                /* Calculate the transfer length.  */
250
1076
                                transfer_length =  current_packet -> nx_packet_length;
251
252
                                /* If trace is enabled, insert this event into the trace buffer.  */
253
                                UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_CDC_ECM_PACKET_TRANSMIT, cdc_ecm, 0, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0)
254
255
                                /* Send the request to the device controller.  */
256
1076
                                status =  _ux_device_stack_transfer_request(transfer_request, transfer_length, UX_DEVICE_CLASS_CDC_ECM_BULKIN_BUFFER_SIZE + 1);
257
                            }
258
259
                            /* Check error code. */
260
1078
                            if (status != UX_SUCCESS)
261
                            {
262
263
                                /* Is this not a transfer abort? (this is expected to happen)  */
264
7
                                if (status != UX_TRANSFER_BUS_RESET)
265
                                {
266
267
                                    /* Error trap. */
268
4
                                    _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, status);
269
                                }
270
                            }
271
                        }
272
                        else
273
                        {
274
275
                            /* Packet is too large.  */
276
277
                            /* Report error to application.  */
278
2
                            _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_BUFFER_OVERFLOW);
279
                        }
280
#endif
281
                    }
282
283
                    /* Free the packet that was just sent.  First do some housekeeping.  */
284
1081
                    current_packet -> nx_packet_prepend_ptr =  current_packet -> nx_packet_prepend_ptr + UX_DEVICE_CLASS_CDC_ECM_ETHERNET_SIZE;
285
1081
                    current_packet -> nx_packet_length =  current_packet -> nx_packet_length - UX_DEVICE_CLASS_CDC_ECM_ETHERNET_SIZE;
286
287
                    /* And ask Netx to release it.  */
288
1081
                    nx_packet_transmit_release(current_packet);
289
                }
290
            }
291
            else
292
            {
293
294
                /* We need to ensure nobody is adding to the queue, so get the mutex protection. */
295
93
                _ux_device_mutex_on(&cdc_ecm -> ux_slave_class_cdc_ecm_mutex);
296
297
                /* Since we got the mutex, we know no one is trying to modify the queue; we also know
298
                   no one can start modifying the queue since the link state is down, so we can just
299
                   release the mutex.  */
300
93
                _ux_device_mutex_off(&cdc_ecm -> ux_slave_class_cdc_ecm_mutex);
301
302
                /* We get here when the link is down. All packets pending must be freed.  */
303
95
                while (cdc_ecm -> ux_slave_class_cdc_ecm_xmit_queue != UX_NULL)
304
                {
305
306
                    /* Get the current packet in the list.  */
307
2
                    current_packet =  cdc_ecm -> ux_slave_class_cdc_ecm_xmit_queue;
308
309
                    /* Set the next packet (or a NULL value) as the head of the xmit queue. */
310
2
                    cdc_ecm -> ux_slave_class_cdc_ecm_xmit_queue =  current_packet -> nx_packet_queue_next;
311
312
                    /* Free the packet.  */
313
2
                    current_packet -> nx_packet_prepend_ptr =  current_packet -> nx_packet_prepend_ptr + UX_DEVICE_CLASS_CDC_ECM_ETHERNET_SIZE;
314
2
                    current_packet -> nx_packet_length =  current_packet -> nx_packet_length - UX_DEVICE_CLASS_CDC_ECM_ETHERNET_SIZE;
315
316
                    /* And ask Netx to release it.  */
317
2
                    nx_packet_transmit_release(current_packet);
318
                }
319
320
                /* Was the change in the device state caused by a disconnection?  */
321
93
                if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED)
322
                {
323
324
                    /* Yes. Break out of the loop and suspend ourselves, waiting for the next configuration.  */
325
86
                    break;
326
                }
327
            }
328
        }
329
330
        /* We need to suspend ourselves. We will be resumed by the device enumeration module or when a change of alternate setting happens.  */
331
86
        _ux_device_thread_suspend(&cdc_ecm -> ux_slave_class_cdc_ecm_bulkin_thread);
332
    }
333
}
334
#endif