GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usbx_host_classes/src/ux_host_class_cdc_ecm_mac_address_get.c Lines: 80 80 100.0 %
Date: 2026-03-06 18:57:10 Branches: 38 38 100.0 %

Line Branch Exec Source
1
/***************************************************************************
2
 * Copyright (c) 2024 Microsoft Corporation
3
 * Copyright (c) 2026-present Eclipse ThreadX contributors
4
 *
5
 * This program and the accompanying materials are made available under the
6
 * terms of the MIT License which is available at
7
 * https://opensource.org/licenses/MIT.
8
 *
9
 * SPDX-License-Identifier: MIT
10
 **************************************************************************/
11
12
13
/**************************************************************************/
14
/**************************************************************************/
15
/**                                                                       */
16
/** USBX Component                                                        */
17
/**                                                                       */
18
/**   CDC ECM Class                                                       */
19
/**                                                                       */
20
/**************************************************************************/
21
/**************************************************************************/
22
23
24
/* Include necessary system files.  */
25
26
#define UX_SOURCE_CODE
27
28
#include "ux_api.h"
29
#include "ux_host_class_cdc_ecm.h"
30
#include "ux_host_stack.h"
31
32
33
/**************************************************************************/
34
/*                                                                        */
35
/*  FUNCTION                                               RELEASE        */
36
/*                                                                        */
37
/*    _ux_host_class_cdc_ecm_mac_address_get              PORTABLE C      */
38
/*                                                           6.2.0        */
39
/*  AUTHOR                                                                */
40
/*                                                                        */
41
/*    Chaoqiong Xiao, Microsoft Corporation                               */
42
/*                                                                        */
43
/*  DESCRIPTION                                                           */
44
/*                                                                        */
45
/*    This function calls the USBX stack to retrieve the MAC address from */
46
/*    the configuration descriptor.                                       */
47
/*                                                                        */
48
/*  INPUT                                                                 */
49
/*                                                                        */
50
/*    cdc_ecm                                Pointer to cdc_ecm class     */
51
/*                                                                        */
52
/*  OUTPUT                                                                */
53
/*                                                                        */
54
/*    Completion Status                                                   */
55
/*                                                                        */
56
/*  CALLS                                                                 */
57
/*                                                                        */
58
/*    _ux_host_stack_transfer_request        Transfer request             */
59
/*    _ux_utility_memory_allocate            Allocate memory              */
60
/*    _ux_utility_memory_free                Free memory                  */
61
/*    _ux_utility_descriptor_parse           Parse descriptors            */
62
/*                                                                        */
63
/*  CALLED BY                                                             */
64
/*                                                                        */
65
/*    _ux_host_class_cdc_ecm_activate            CDC ECM class activate   */
66
/*                                                                        */
67
/**************************************************************************/
68
78
UINT  _ux_host_class_cdc_ecm_mac_address_get(UX_HOST_CLASS_CDC_ECM *cdc_ecm)
69
{
70
71
UINT                                        status;
72
UX_ENDPOINT                                 *control_endpoint;
73
UX_TRANSFER                                 *transfer_request;
74
UX_CONFIGURATION_DESCRIPTOR                 configuration_descriptor;
75
UCHAR                                       *descriptor;
76
78
UCHAR                                       *start_descriptor = UX_NULL;
77
ULONG                                       configuration_index;
78
ULONG                                       total_configuration_length;
79
UINT                                        descriptor_length;
80
UINT                                        descriptor_type;
81
UINT                                        descriptor_subtype;
82
UX_HOST_CLASS_ECM_INTERFACE_DESCRIPTOR      ecm_interface_descriptor;
83
UCHAR                                       *mac_address_string;
84
ULONG                                       string_index;
85
ULONG                                       string_length;
86
UCHAR                                       element_content;
87
UCHAR                                       element_hexa_upper;
88
UCHAR                                       element_hexa_lower;
89
90
    /* We now need to retrieve the MAC address of the node which is embedded in the ECM descriptor.
91
       We will parse the entire configuration descriptor of the device and look for the ECM Ethernet Networking Functional Descriptor.  */
92
78
    configuration_index = (ULONG)cdc_ecm -> ux_host_class_cdc_ecm_interface_data -> ux_interface_configuration -> ux_configuration_descriptor.bConfigurationValue -1;
93
94
    /* We need to get the default control endpoint transfer request pointer.  */
95
78
    control_endpoint =  &cdc_ecm -> ux_host_class_cdc_ecm_device -> ux_device_control_endpoint;
96
78
    transfer_request =  &control_endpoint -> ux_endpoint_transfer_request;
97
98
    /* Need to allocate memory for the descriptor. Since we do not know the size of the
99
       descriptor, we first read the first bytes.  */
100
78
    descriptor =  _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_CONFIGURATION_DESCRIPTOR_LENGTH);
101
78
    if (descriptor == UX_NULL)
102
1
        return(UX_MEMORY_INSUFFICIENT);
103
104
    /* Memorize the descriptor start address.  */
105
77
    start_descriptor =  descriptor;
106
107
    /* Create a transfer request for the GET_DESCRIPTOR request.  */
108
77
    transfer_request -> ux_transfer_request_data_pointer =      descriptor;
109
77
    transfer_request -> ux_transfer_request_requested_length =  UX_CONFIGURATION_DESCRIPTOR_LENGTH;
110
77
    transfer_request -> ux_transfer_request_function =          UX_GET_DESCRIPTOR;
111
77
    transfer_request -> ux_transfer_request_type =              UX_REQUEST_IN | UX_REQUEST_TYPE_STANDARD | UX_REQUEST_TARGET_DEVICE;
112
77
    transfer_request -> ux_transfer_request_value =             (UX_CONFIGURATION_DESCRIPTOR_ITEM << 8) | configuration_index;
113
77
    transfer_request -> ux_transfer_request_index =             0;
114
115
    /* Send request to HCD layer.  */
116
77
    status =  _ux_host_stack_transfer_request(transfer_request);
117
118
    /* Check for correct transfer and entire descriptor returned.  */
119

77
    if ((status == UX_SUCCESS) && (transfer_request -> ux_transfer_request_actual_length == UX_CONFIGURATION_DESCRIPTOR_LENGTH))
120
    {
121
122
        /* Parse the descriptor so that we can read the total length.  */
123
73
        _ux_utility_descriptor_parse(descriptor, _ux_system_configuration_descriptor_structure,
124
                                                                UX_CONFIGURATION_DESCRIPTOR_ENTRIES, (UCHAR *) &configuration_descriptor);
125
126
        /* We don't need this descriptor now.  */
127
73
        _ux_utility_memory_free(descriptor);
128
129
        /* Reallocate the memory necessary for the reading the entire descriptor.  */
130
73
        total_configuration_length =  configuration_descriptor.wTotalLength;
131
73
        descriptor =  _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, total_configuration_length);
132
73
        if (descriptor == UX_NULL)
133
1
            return(UX_MEMORY_INSUFFICIENT);
134
135
        /* Save this descriptor address.  */
136
72
        start_descriptor =  descriptor;
137
138
        /* Read the descriptor again with the correct length this time.  */
139
72
        transfer_request -> ux_transfer_request_requested_length =  total_configuration_length;
140
141
        /* Since the address of the descriptor may have changed, reprogram it.  */
142
72
        transfer_request -> ux_transfer_request_data_pointer =  descriptor;
143
144
        /* Send request to HCD layer.  */
145
72
        status =  _ux_host_stack_transfer_request(transfer_request);
146
147
        /* Check for correct transfer and entire descriptor returned.  */
148

72
        if ((status == UX_SUCCESS) && (transfer_request -> ux_transfer_request_actual_length == configuration_descriptor.wTotalLength))
149
        {
150
151
            /* The ECM descriptor is embedded within the configuration descriptor. We parse the
152
               entire descriptor to locate the ECM functional descriptor portion.  */
153
354
            while (total_configuration_length)
154
            {
155
156
                /* Gather the length and type of the descriptor.   */
157
351
                descriptor_length  =  *descriptor;
158
351
                descriptor_type    =  *(descriptor + 1);
159
351
                descriptor_subtype =  *(descriptor + 2);
160
161
                /* Descriptor length validation.  */
162

351
                if (descriptor_length < 3 || descriptor_length > total_configuration_length)
163
                {
164
165
                    /* Error trap.  */
166
2
                    _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED);
167
168
                    /* Free descriptor memory.  */
169
2
                    _ux_utility_memory_free(start_descriptor);
170
171
                    /* Return error.  */
172
2
                    return(UX_DESCRIPTOR_CORRUPTED);
173
                }
174
175
                /* Check the type for an interface descriptor and the subtype for a ECM functional descriptor.  */
176

349
                if ((descriptor_type == UX_HOST_CLASS_CDC_ECM_CS_INTERFACE) && (descriptor_subtype == UX_HOST_CLASS_CDC_ECM_FUNCTIONAL_DESCRIPTOR))
177
                {
178
179
                    /* Parse the interface descriptor and make it machine independent.  */
180
63
                    _ux_utility_descriptor_parse(descriptor,
181
                                _ux_system_ecm_interface_descriptor_structure,
182
                                UX_HOST_CLASS_CDC_ECM_INTERFACE_DESCRIPTOR_ENTRIES,
183
                                (UCHAR *) &ecm_interface_descriptor);
184
185
186
                    /* Release the memory.  */
187
63
                    _ux_utility_memory_free(start_descriptor);
188
189
                    /* We now have the ECM functional descriptor in memory. We can retrieve the index of the iMACAddress
190
                       which we need for NetX.  */
191
192
                    /* Allocate memory for the MAC address.  */
193
63
                    mac_address_string =  _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_HOST_CLASS_CDC_ECM_MAC_ADDRESS_STRING_LENGTH);
194
195
                    /* Check memory allocation.  */
196
63
                    if (mac_address_string == UX_NULL)
197
1
                        return(UX_MEMORY_INSUFFICIENT);
198
199
                    /* Create a transfer request for the GET_DESCRIPTOR request.  */
200
62
                    transfer_request -> ux_transfer_request_data_pointer =      mac_address_string;
201
62
                    transfer_request -> ux_transfer_request_requested_length =  UX_HOST_CLASS_CDC_ECM_MAC_ADDRESS_STRING_LENGTH;
202
62
                    transfer_request -> ux_transfer_request_function =          UX_GET_DESCRIPTOR;
203
62
                    transfer_request -> ux_transfer_request_type =              UX_REQUEST_IN | UX_REQUEST_TYPE_STANDARD | UX_REQUEST_TARGET_DEVICE;
204
62
                    transfer_request -> ux_transfer_request_value =             (UX_STRING_DESCRIPTOR_ITEM << 8) | ecm_interface_descriptor.iMACAddress;
205
62
                    transfer_request -> ux_transfer_request_index =             0x0409;
206
207
                    /* Send request to HCD layer.  */
208
62
                    status =  _ux_host_stack_transfer_request(transfer_request);
209
210
                    /* Check for correct transfer. */
211
62
                    if (status == UX_SUCCESS)
212
                    {
213
214
                        /* Translate from Unicode to string. Length is in the first byte followed type.
215
                           We must take away 2 from it and divide by 2 to find the right ascii length. */
216
61
                        string_length = (ULONG) *mac_address_string;
217
218
                        /* Check the length of the MAC address Unicode string
219
                           (length or 1B + type of 1B + string or 12*2B).  */
220
61
                        if (string_length != 26)
221
                        {
222
223
                            /* Error trap. */
224
3
                            _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED);
225
226
                            /* Return error.  */
227
3
                            status =  UX_DESCRIPTOR_CORRUPTED;
228
                        }
229
                        else
230
                        {
231
232
                            /* No error in length, decode the string.  */
233
58
                            string_length -=2;
234
58
                            string_length = string_length / 2;
235
236
                            /* Now we have a string of 12 hex ASCII digits to be translated into 6 hex digit bytes.
237
                               and copy into the node ID.  */
238
406
                            for (string_index = 0; string_index < string_length; string_index++)
239
                            {
240
241
                                /* Get the upper element from the ASCII string.  */
242
348
                                element_content = *(mac_address_string + (string_index * 2) + 2);
243
244
                                /* We have a valid element content.  Turn it into a hex decimal value.  Note
245
                                   that only hex digits are allowed.  */
246
348
                                if (element_content <= '9')
247
248
                                    /* We have a digit.  */
249
286
                                    element_hexa_upper = (UCHAR)(element_content - '0');
250
251
                                else
252
                                {
253
                                    /* We have a 'A' to 'F' or 'a' to 'f' value.  */
254
62
                                    if (element_content >= 'a')
255
256
                                        /* We have a 'a' to 'f' char.  */
257
4
                                        element_hexa_upper = (UCHAR)(element_content - 'a' + 10);
258
259
                                    else
260
261
                                        /* We have a 'A' to 'F' char.  */
262
58
                                        element_hexa_upper = (UCHAR)(element_content - 'A' + 10);
263
264
                                }
265
266
                                /* Get the lower element from the ASCII string.  */
267
348
                                element_content = *(mac_address_string + ((string_index + 1) * 2) + 2);
268
269
                                /* We have a valid element content.  Turn it into a hexa decimal value.  Note
270
                                   that only hex digits are allowed.  */
271
348
                                if (element_content <= '9')
272
273
                                    /* We have a digit.  */
274
284
                                    element_hexa_lower = (UCHAR)(element_content - '0');
275
276
                                else
277
                                {
278
                                    /* We have a 'A' to 'F' or 'a' to 'f' value.  */
279
64
                                    if (element_content >= 'a')
280
281
                                        /* We have a 'a' to 'f' char.  */
282
4
                                        element_hexa_lower = (UCHAR)(element_content - 'a' + 10);
283
284
                                    else
285
286
                                        /* We have a 'A' to 'F' char.  */
287
60
                                        element_hexa_lower = (UCHAR)(element_content - 'A' + 10);
288
289
                                }
290
291
                                /* Assemble the byte from the 2 nibbles and store it into the node_id. */
292
348
                                *(cdc_ecm -> ux_host_class_cdc_ecm_node_id + string_index / 2) = (UCHAR)(element_hexa_upper << 4 | element_hexa_lower);
293
294
                                /* Skip the lower nibble. */
295
348
                                string_index ++;
296
297
                            }
298
299
                            /* Operation was successful ! */
300
58
                            status = UX_SUCCESS;
301
                        }
302
                    }
303
                    else
304
                    {
305
306
                        /* We have a bad MAC address string.  Do not proceed.  */
307
1
                        status = UX_ERROR;
308
                    }
309
310
                    /* Free the MAC address string.  */
311
62
                    _ux_utility_memory_free(mac_address_string);
312
313
                    /* Return completion status.  */
314
62
                    return(status);
315
                }
316
                else
317
                {
318
319
                    /* Jump to the next descriptor if we have not reached the end.  */
320
286
                    descriptor +=  descriptor_length;
321
322
                    /* And adjust the length left to parse in the descriptor.  */
323
286
                    total_configuration_length -=  descriptor_length;
324
                }
325
            }
326
        }
327
    }
328
329
    /* Error trap. */
330
11
    _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED);
331
332
    /* If trace is enabled, insert this event into the trace buffer.  */
333
    UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, &configuration_descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0)
334
335
    /* Release the memory.  */
336
11
    _ux_utility_memory_free(start_descriptor);
337
338
    /* Return an error.  */
339
11
    return(UX_DESCRIPTOR_CORRUPTED);
340
341
}