summaryrefslogtreecommitdiff
path: root/common/core/src/ux_device_stack_descriptor_send.c
blob: 0d731da41d64ffb3cf02251184d3e7356d9b48ce (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
/***************************************************************************
 * Copyright (c) 2024 Microsoft Corporation
 * Copyright (c) 2026-present Eclipse ThreadX contributors
 *
 * This program and the accompanying materials are made available under the
 * terms of the MIT License which is available at
 * https://opensource.org/licenses/MIT.
 *
 * SPDX-License-Identifier: MIT
 **************************************************************************/


/**************************************************************************/
/**************************************************************************/
/**                                                                       */
/** USBX Component                                                        */
/**                                                                       */
/**   Device Stack                                                        */
/**                                                                       */
/**************************************************************************/
/**************************************************************************/

#define UX_SOURCE_CODE


/* Include necessary system files.  */

#include "ux_api.h"
#include "ux_device_stack.h"


#if (UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH < UX_DEVICE_DESCRIPTOR_LENGTH) || \
    (UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH < UX_DEVICE_QUALIFIER_DESCRIPTOR_LENGTH) || \
    (UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH < UX_OTG_DESCRIPTOR_LENGTH)
/* #error UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH too small, please check  */
/* Build option checked runtime by UX_ASSERT  */
#endif

/**************************************************************************/
/*                                                                        */
/*  FUNCTION                                               RELEASE        */
/*                                                                        */
/*    _ux_device_stack_descriptor_send                    PORTABLE C      */
/*                                                           6.3.0        */
/*  AUTHOR                                                                */
/*                                                                        */
/*    Chaoqiong Xiao, Microsoft Corporation                               */
/*                                                                        */
/*  DESCRIPTION                                                           */
/*                                                                        */
/*    This function sends back the device descriptor required by the host.*/
/*                                                                        */
/*  INPUT                                                                 */
/*                                                                        */
/*    descriptor_type                       Descriptor type               */
/*    descriptor_index                      Index of descriptor           */
/*    host_length                           Length requested by host      */
/*                                                                        */
/*  OUTPUT                                                                */
/*                                                                        */
/*    Completion Status                                                   */
/*                                                                        */
/*  CALLS                                                                 */
/*                                                                        */
/*    (ux_slave_dcd_function)               DCD dispatch function         */
/*    _ux_device_stack_transfer_request     Process transfer request      */
/*    _ux_utility_descriptor_parse          Parse descriptor              */
/*    _ux_utility_memory_copy               Memory copy                   */
/*    _ux_utility_short_get                 Get short value               */
/*                                                                        */
/*  CALLED BY                                                             */
/*                                                                        */
/*    Application                                                         */
/*    Device Stack                                                        */
/*                                                                        */
/**************************************************************************/
UINT  _ux_device_stack_descriptor_send(ULONG descriptor_type, ULONG request_index, ULONG host_length)
{

UX_SLAVE_DCD                    *dcd;
UX_SLAVE_DEVICE                 *device;
ULONG                           descriptor_index;
ULONG                           parsed_descriptor_index;
UX_SLAVE_TRANSFER               *transfer_request;
UX_CONFIGURATION_DESCRIPTOR     configuration_descriptor;
#ifndef UX_BOS_SUPPORT_DISABLE
UX_BOS_DESCRIPTOR               bos_descriptor;
#endif
UX_SLAVE_ENDPOINT               *endpoint;
UCHAR                           *device_framework;
UCHAR                           *device_framework_end;
ULONG                           device_framework_length;
ULONG                           descriptor_length;
ULONG                           target_descriptor_length = 0;
UINT                            status =  UX_ERROR;
ULONG                           length;
UCHAR                           *string_memory;
UCHAR                           *string_framework;
ULONG                           string_framework_length;
ULONG                           string_length;


    /* Build option check.  */
    UX_ASSERT((UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH >= UX_DEVICE_DESCRIPTOR_LENGTH) &&
              (UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH >= UX_DEVICE_QUALIFIER_DESCRIPTOR_LENGTH) &&
              (UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH >= UX_OTG_DESCRIPTOR_LENGTH));

    /* If trace is enabled, insert this event into the trace buffer.  */
    UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_STACK_DESCRIPTOR_SEND, descriptor_type, request_index, 0, 0, UX_TRACE_DEVICE_STACK_EVENTS, 0, 0)

    /* Get the pointer to the DCD.  */
    dcd =  &_ux_system_slave -> ux_system_slave_dcd;

    /* Get the pointer to the device.  */
    device =  &_ux_system_slave -> ux_system_slave_device;

    /* Get the control endpoint associated with the device.  */
    endpoint =  &device -> ux_slave_device_control_endpoint;

    /* Get the pointer to the transfer request associated with the endpoint.  */
    transfer_request =  &endpoint -> ux_slave_endpoint_transfer_request;

    /* Set the direction to OUT.  */
    transfer_request -> ux_slave_transfer_request_phase =  UX_TRANSFER_PHASE_DATA_OUT;

    /* Isolate the descriptor index.  */
    descriptor_index =  descriptor_type & 0xff;

    /* Reset the parsed index.  */
    parsed_descriptor_index =  0;

    /* Shift the descriptor type in the low byte field.  */
    descriptor_type =  (UCHAR) ((descriptor_type >> 8) & 0xff);

    /* Default descriptor length is host length.  */
    length =  host_length;

    /* What type of descriptor do we need to return?  */
    switch (descriptor_type)
    {

    case UX_DEVICE_DESCRIPTOR_ITEM:

		/* Setup device descriptor length.  */
        if (host_length > UX_DEVICE_DESCRIPTOR_LENGTH)
            length =  UX_DEVICE_DESCRIPTOR_LENGTH;

        /* Fall through.  */
    case UX_DEVICE_QUALIFIER_DESCRIPTOR_ITEM:

        /* Setup qualifier descriptor length.  */
        if (descriptor_type == UX_DEVICE_QUALIFIER_DESCRIPTOR_ITEM &&
            host_length > UX_DEVICE_QUALIFIER_DESCRIPTOR_LENGTH)
            length =  UX_DEVICE_QUALIFIER_DESCRIPTOR_LENGTH;

        /* Fall through.  */
    case UX_OTG_DESCRIPTOR_ITEM:

        /* Setup OTG descriptor length.  */
        if (descriptor_type == UX_OTG_DESCRIPTOR_ITEM &&
            host_length > UX_OTG_DESCRIPTOR_LENGTH)
            length =  UX_OTG_DESCRIPTOR_LENGTH;

        /* We may or may not have a device qualifier descriptor.  */
        device_framework =  _ux_system_slave -> ux_system_slave_device_framework;
        device_framework_length =  _ux_system_slave -> ux_system_slave_device_framework_length;
        device_framework_end = device_framework + device_framework_length;

        /* Parse the device framework and locate a device qualifier descriptor.  */
        while (device_framework < device_framework_end)
        {

            /* Get descriptor length.  */
            descriptor_length =  (ULONG) *device_framework;

            /* Check if this is a descriptor expected.  */
            if (*(device_framework + 1) == descriptor_type)
            {

                /* Copy the device descriptor into the transfer request memory.  */
                _ux_utility_memory_copy(transfer_request -> ux_slave_transfer_request_data_pointer,
                                                device_framework, length); /* Use case of memcpy is verified. */

                /* Perform the data transfer.  */
                status =  _ux_device_stack_transfer_request(transfer_request, length, host_length);
                break;
            }

            /* Adjust what is left of the device framework.  */
            device_framework_length -=  descriptor_length;

            /* Point to the next descriptor.  */
            device_framework +=  descriptor_length;
        }
        break;

#ifndef UX_BOS_SUPPORT_DISABLE
    case UX_BOS_DESCRIPTOR_ITEM:
        /* Fall through.  */
#endif
    case UX_OTHER_SPEED_DESCRIPTOR_ITEM:
        /* Fall through.  */
    case UX_CONFIGURATION_DESCRIPTOR_ITEM:

        if (descriptor_type == UX_OTHER_SPEED_DESCRIPTOR_ITEM)
        {

            /* This request is used by the host to find out the capability of this device
            if it was running at full speed. The behavior is the same as in a GET_CONFIGURATIOn descriptor
            but we do not use the current device framework but rather the full speed framework. */
            device_framework =  _ux_system_slave -> ux_system_slave_device_framework_full_speed;
            device_framework_length =  _ux_system_slave -> ux_system_slave_device_framework_length_full_speed;
            device_framework_end = device_framework + device_framework_length;
        }
        else
        {

            /* We may have multiple configurations !, the index will tell us what
            configuration descriptor we need to return.  */
            device_framework =  _ux_system_slave -> ux_system_slave_device_framework;
            device_framework_length =  _ux_system_slave -> ux_system_slave_device_framework_length;
            device_framework_end = device_framework + device_framework_length;
        }

        /* Parse the device framework and locate a configuration descriptor.  */
        while (device_framework < device_framework_end)
        {

            /* Get descriptor length. */
            descriptor_length =  (ULONG) *device_framework;

#ifndef UX_BOS_SUPPORT_DISABLE

            /* Check if we are finding BOS descriptor.  */
            if (descriptor_type == UX_BOS_DESCRIPTOR_ITEM)
            {
                if (*(device_framework + 1) == UX_BOS_DESCRIPTOR_ITEM)
                {

                    /* Parse the BOS descriptor.  */
                    _ux_utility_descriptor_parse(device_framework,
                                _ux_system_bos_descriptor_structure,
                                UX_BOS_DESCRIPTOR_ENTRIES,
                                (UCHAR *) &bos_descriptor);

                    /* Get the length of entire BOS descriptor.  */
                    target_descriptor_length = bos_descriptor.wTotalLength;

                    /* Descriptor is found.  */
                    status = UX_SUCCESS;
                    break;
                }
            }
            else
#endif

            {

                /* Check if this is a configuration descriptor.  We are cheating here. Instead of creating
                a OTHER SPEED descriptor, we simply scan the configuration descriptor for the Full Speed
                framework and return this configuration after we manually changed the configuration descriptor
                item into a Other Speed Descriptor. */
                if (*(device_framework + 1) == UX_CONFIGURATION_DESCRIPTOR_ITEM)
                {

                    /* Check the index. It must be the same as the one requested.  */
                    if (parsed_descriptor_index == descriptor_index)
                    {

                        /* Parse the configuration descriptor. */
                        _ux_utility_descriptor_parse(device_framework,
                                    _ux_system_configuration_descriptor_structure,
                                    UX_CONFIGURATION_DESCRIPTOR_ENTRIES,
                                    (UCHAR *) &configuration_descriptor);

                        /* Get the length of entire configuration descriptor.  */
                        target_descriptor_length = configuration_descriptor.wTotalLength;

                        /* Descriptor is found.  */
                        status = UX_SUCCESS;
                        break;
                    }
                    else
                    {

                        /* There may be more configuration descriptors in this framework.  */
                        parsed_descriptor_index++;
                    }
                }
            }

            /* Adjust what is left of the device framework.  */
            device_framework_length -=  descriptor_length;

            /* Point to the next descriptor.  */
            device_framework +=  descriptor_length;
        }

        /* Send the descriptor.  */
        if (status == UX_SUCCESS)
        {

            /* Ensure the host does not demand a length beyond our descriptor (Windows does that)
                and do not return more than what is allowed.  */
            if (target_descriptor_length < host_length)
                length =  target_descriptor_length;
            else
                length =  host_length;

            /* Check buffer length, since total descriptors length may exceed buffer...  */
            if (length > UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH)
            {
                /* Error trap. */
                _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_DEVICE_STACK, UX_MEMORY_INSUFFICIENT);

                /* If trace is enabled, insert this event into the trace buffer.  */
                UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_MEMORY_INSUFFICIENT, device, 0, 0, UX_TRACE_ERRORS, 0, 0)

                /* Stall the endpoint.  */
                status =  dcd -> ux_slave_dcd_function(dcd, UX_DCD_STALL_ENDPOINT, endpoint);
                break;
            }

            /* Copy the device descriptor into the transfer request memory.  */
            _ux_utility_memory_copy(transfer_request -> ux_slave_transfer_request_data_pointer,
                                device_framework, length); /* Use case of memcpy is verified. */

            /* Now we need to hack the found descriptor because this request expect a requested
                descriptor type instead of the regular descriptor.  */
            *(transfer_request -> ux_slave_transfer_request_data_pointer + 1) = (UCHAR)descriptor_type;

            /* We can return the configuration descriptor.  */
            status =  _ux_device_stack_transfer_request(transfer_request, length, host_length);
        }
        break;

    case UX_STRING_DESCRIPTOR_ITEM:

        /* We need to filter for the index 0 which is the language ID string.  */
        if (descriptor_index == 0)
        {

            /* We need to check request buffer size in case it's possible exceed. */
            if (_ux_system_slave -> ux_system_slave_language_id_framework_length + 2 > UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH)
            {

                /* Error trap. */
                _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_DEVICE_STACK, UX_MEMORY_INSUFFICIENT);

                /* If trace is enabled, insert this event into the trace buffer.  */
                UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_MEMORY_INSUFFICIENT, device, 0, 0, UX_TRACE_ERRORS, 0, 0)

                /* Stall the endpoint.  */
                status =  dcd -> ux_slave_dcd_function(dcd, UX_DCD_STALL_ENDPOINT, endpoint);
                break;
            }

            /* We have a request to send back the language ID list. Use the transfer request buffer.  */
            string_memory =  transfer_request -> ux_slave_transfer_request_data_pointer;

            /* Store the total length of the response.  */
            *string_memory =  (UCHAR)(_ux_system_slave -> ux_system_slave_language_id_framework_length + 2);

            /* Store the descriptor type.  */
            *(string_memory +1) =  UX_STRING_DESCRIPTOR_ITEM;

            /* Store the language ID into the buffer.  */
            _ux_utility_memory_copy(string_memory+2, _ux_system_slave -> ux_system_slave_language_id_framework,
                                                        _ux_system_slave -> ux_system_slave_language_id_framework_length); /* Use case of memcpy is verified. */

            /* Filter the length asked/required.  */
            if (host_length > _ux_system_slave -> ux_system_slave_language_id_framework_length + 2)
                length =  _ux_system_slave -> ux_system_slave_language_id_framework_length + 2;
            else
                length =  host_length;

            /* We can return the string language ID descriptor.  */
            status =  _ux_device_stack_transfer_request(transfer_request, length, host_length);
        }
        else
        {
#ifdef UX_DEVICE_ENABLE_GET_STRING_WITH_ZERO_LANGUAGE_ID

            /* Check if the language ID is zero.  */
            if (request_index == 0)
            {

                /* Get the first language ID in the language ID framework.  */
                request_index =  _ux_utility_short_get(_ux_system_slave -> ux_system_slave_language_id_framework);
            }
#endif

            /* The host wants a specific string index returned. Get the string framework pointer
               and length.  */
            string_framework =  _ux_system_slave -> ux_system_slave_string_framework;
            string_framework_length =  _ux_system_slave -> ux_system_slave_string_framework_length;

            /* We search through the string framework until we find the right index.
               The index is in the lower byte of the descriptor type. */
            while (string_framework_length != 0)
            {

                /* Ensure we have the correct language page.  */
                if (_ux_utility_short_get(string_framework) == request_index)
                {

                    /* Check the index.  */
                    if (*(string_framework + 2) == descriptor_index)
                    {

                        /* We need to check request buffer size in case it's possible exceed. */
                        if (((*(string_framework + 3)*2) + 2) > UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH)
                        {

                            /* Error trap. */
                            _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_DEVICE_STACK, UX_MEMORY_INSUFFICIENT);

                            /* If trace is enabled, insert this event into the trace buffer.  */
                            UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_MEMORY_INSUFFICIENT, device, 0, 0, UX_TRACE_ERRORS, 0, 0)

                            /* Stall the endpoint.  */
                            status =  dcd -> ux_slave_dcd_function(dcd, UX_DCD_STALL_ENDPOINT, endpoint);
                            break;
                        }

                        /* We have a request to send back a string. Use the transfer request buffer.  */
                        string_memory =  transfer_request -> ux_slave_transfer_request_data_pointer;

                        /* Store the length in the string buffer. The length
                           of the string descriptor is stored in the third byte,
                           hence the ' + 3'. The encoding must be in 16-bit
                           unicode, hence the '*2'. The length includes the size
                           of the length itself as well as the descriptor type,
                           hence the ' + 2'.  */
                        *string_memory =  (UCHAR)((*(string_framework + 3)*2) + 2);

                        /* Store the Descriptor type. */
                        *(string_memory + 1) =  UX_STRING_DESCRIPTOR_ITEM;

                        /* Create the Unicode string.  */
                        for (string_length = 0; string_length <  *(string_framework + 3) ; string_length ++)
                        {

                            /* Insert a Unicode byte.  */
                            *(string_memory + 2 + (string_length * 2)) =  *(string_framework + 4 + string_length);

                            /* Insert a zero after the Unicode byte.  */
                            *(string_memory + 2 + (string_length * 2) + 1) =  0;
                        }

                        /* Filter the length asked/required.  */
                        if (host_length > (UINT)((*(string_framework + 3)*2) + 2))
                            length =  (ULONG)((*(string_framework + 3)*2) + 2);
                        else
                            length =  host_length;

                        /* We can return the string descriptor.  */
                        status =  _ux_device_stack_transfer_request(transfer_request, length, host_length);
                        break;
                    }
                }

                /* This is the wrong string descriptor, jump to the next.  */
                string_framework_length -=  (ULONG) *(string_framework + 3) + 4;
                string_framework +=  (ULONG) *(string_framework + 3) + 4;
            }

            /* Have we exhausted all the string descriptors?  */
            if (string_framework_length == 0)
            {

                /* Could not find the required string index. Stall the endpoint.  */
                dcd -> ux_slave_dcd_function(dcd, UX_DCD_STALL_ENDPOINT, endpoint);
                return(UX_ERROR);
            }
        }
        break;

    default:

        /* Stall the endpoint.  */
        dcd -> ux_slave_dcd_function(dcd, UX_DCD_STALL_ENDPOINT, endpoint);
        return(UX_ERROR);
    }

    /* Return the status to the caller.  */
    return(status);
}