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 /**                                                                       */
15 /** USBX Component                                                        */
16 /**                                                                       */
17 /**   Device Stack                                                        */
18 /**                                                                       */
19 /**************************************************************************/
20 /**************************************************************************/
21 
22 #define UX_SOURCE_CODE
23 
24 
25 /* Include necessary system files.  */
26 
27 #include "ux_api.h"
28 #include "ux_device_stack.h"
29 
30 
31 /**************************************************************************/
32 /*                                                                        */
33 /*  FUNCTION                                               RELEASE        */
34 /*                                                                        */
35 /*    _ux_device_stack_alternate_setting_set              PORTABLE C      */
36 /*                                                           6.1.12       */
37 /*  AUTHOR                                                                */
38 /*                                                                        */
39 /*    Chaoqiong Xiao, Microsoft Corporation                               */
40 /*                                                                        */
41 /*  DESCRIPTION                                                           */
42 /*                                                                        */
43 /*    This function sets the alternate setting for a specific interface.  */
44 /*    The previous interface is unmounted and all the endpoints           */
45 /*    associated with the alternate setting are mounted.                  */
46 /*                                                                        */
47 /*  INPUT                                                                 */
48 /*                                                                        */
49 /*    endpoint                              Pointer to endpoint           */
50 /*    interface_value                       Interface value               */
51 /*    alternate_setting_value               Alternate setting value       */
52 /*                                                                        */
53 /*  OUTPUT                                                                */
54 /*                                                                        */
55 /*    Completion Status                                                   */
56 /*                                                                        */
57 /*  CALLS                                                                 */
58 /*                                                                        */
59 /*    (ux_slave_dcd_function)               DCD dispatch function         */
60 /*    _ux_utility_descriptor_parse          Parse descriptor              */
61 /*    _ux_device_stack_transfer_all_request_abort                         */
62 /*                                          Abort transfer                */
63 /*    _ux_utility_memory_copy               Copy memory                   */
64 /*                                                                        */
65 /*  CALLED BY                                                             */
66 /*                                                                        */
67 /*    Application                                                         */
68 /*    Device Stack                                                        */
69 /*                                                                        */
70 /*  RELEASE HISTORY                                                       */
71 /*                                                                        */
72 /*    DATE              NAME                      DESCRIPTION             */
73 /*                                                                        */
74 /*  05-19-2020     Chaoqiong Xiao           Initial Version 6.0           */
75 /*  09-30-2020     Chaoqiong Xiao           Modified comment(s),          */
76 /*                                            optimized based on compile  */
77 /*                                            definitions, verified       */
78 /*                                            memset and memcpy cases,    */
79 /*                                            resulting in version 6.1    */
80 /*  10-15-2021     Chaoqiong Xiao           Modified comment(s),          */
81 /*                                            calculated payload size,    */
82 /*                                            resulting in version 6.1.9  */
83 /*  07-29-2022     Chaoqiong Xiao           Modified comment(s),          */
84 /*                                            fixed parameter/variable    */
85 /*                                            names conflict C++ keyword, */
86 /*                                            resulting in version 6.1.12 */
87 /*                                                                        */
88 /**************************************************************************/
_ux_device_stack_alternate_setting_set(ULONG interface_value,ULONG alternate_setting_value)89 UINT  _ux_device_stack_alternate_setting_set(ULONG interface_value, ULONG alternate_setting_value)
90 {
91 
92 UX_SLAVE_DEVICE                 *device;
93 UX_SLAVE_INTERFACE              *interface_ptr;
94 #if !defined(UX_DEVICE_ALTERNATE_SETTING_SUPPORT_DISABLE)
95 UX_SLAVE_DCD                    *dcd;
96 UX_SLAVE_TRANSFER               *transfer_request;
97 UCHAR                           *device_framework;
98 ULONG                           device_framework_length;
99 ULONG                           descriptor_length;
100 UCHAR                           descriptor_type;
101 UX_CONFIGURATION_DESCRIPTOR     configuration_descriptor;
102 UX_INTERFACE_DESCRIPTOR         interface_descriptor;
103 UX_SLAVE_ENDPOINT               *endpoint;
104 UX_SLAVE_ENDPOINT               *next_endpoint;
105 UX_SLAVE_ENDPOINT               *endpoint_link;
106 ULONG                            endpoints_pool_number;
107 UX_SLAVE_CLASS_COMMAND          class_command;
108 UX_SLAVE_CLASS                  *class_ptr;
109 UINT                            status;
110 ULONG                           max_transfer_length, n_trans;
111 #endif
112 
113     /* If trace is enabled, insert this event into the trace buffer.  */
114     UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_STACK_ALTERNATE_SETTING_SET, interface_value, alternate_setting_value, 0, 0, UX_TRACE_DEVICE_STACK_EVENTS, 0, 0)
115 
116     /* Get the pointer to the device. */
117     device =  &_ux_system_slave -> ux_system_slave_device;
118 
119     /* Protocol error must be reported when it's unconfigured */
120     if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED)
121         return(UX_FUNCTION_NOT_SUPPORTED);
122 
123     /* Find the current interface.  */
124     interface_ptr =  device -> ux_slave_device_first_interface;
125 
126 #if !defined(UX_DEVICE_INITIALIZE_FRAMEWORK_SCAN_DISABLE) || UX_MAX_DEVICE_INTERFACES > 1
127     /* Scan all interfaces if any. */
128     while (interface_ptr != UX_NULL)
129     {
130 
131         if (interface_ptr -> ux_slave_interface_descriptor.bInterfaceNumber == interface_value)
132             break;
133         else
134             interface_ptr =  interface_ptr -> ux_slave_interface_next_interface;
135     }
136 #else
137     if (interface_ptr -> ux_slave_interface_descriptor.bInterfaceNumber != interface_value)
138         interface_ptr = UX_NULL;
139 #endif
140 
141     /* We must have found the interface pointer for the interface value
142        requested by the caller.  */
143     if (interface_ptr == UX_NULL)
144     {
145 
146         /* Error trap. */
147         _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_INTERFACE_HANDLE_UNKNOWN);
148 
149         /* If trace is enabled, insert this event into the trace buffer.  */
150         UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_INTERFACE_HANDLE_UNKNOWN, interface_ptr, 0, 0, UX_TRACE_ERRORS, 0, 0)
151 
152         return(UX_INTERFACE_HANDLE_UNKNOWN);
153     }
154 
155     /* If the host is requesting a change of alternate setting to the current one,
156        we do not need to do any work.  */
157     if (interface_ptr -> ux_slave_interface_descriptor.bAlternateSetting == alternate_setting_value)
158         return(UX_SUCCESS);
159 
160 #if defined(UX_DEVICE_ALTERNATE_SETTING_SUPPORT_DISABLE)
161 
162     /* If alternate setting is disabled, do error trap.  */
163     _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_FUNCTION_NOT_SUPPORTED);
164 
165     /* If trace is enabled, insert this event into the trace buffer.  */
166     UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, interface_ptr, 0, 0, UX_TRACE_ERRORS, 0, 0)
167 
168     return(UX_FUNCTION_NOT_SUPPORTED);
169 #else
170 
171     /* Get the pointer to the DCD. */
172     dcd =  &_ux_system_slave->ux_system_slave_dcd;
173 
174     /* We may have multiple configurations!  */
175     device_framework =  _ux_system_slave -> ux_system_slave_device_framework;
176     device_framework_length =  _ux_system_slave -> ux_system_slave_device_framework_length;
177 
178     /* Parse the device framework and locate a configuration descriptor. */
179     while (device_framework_length != 0)
180     {
181 
182         /* Get the length of the current descriptor.  */
183         descriptor_length =  (ULONG) *device_framework;
184 
185         /* And its length.  */
186         descriptor_type =*  (device_framework + 1);
187 
188         /* Check if this is a configuration descriptor. */
189         if (descriptor_type == UX_CONFIGURATION_DESCRIPTOR_ITEM)
190         {
191 
192             /* Parse the descriptor in something more readable. */
193             _ux_utility_descriptor_parse(device_framework,
194                         _ux_system_configuration_descriptor_structure,
195                         UX_CONFIGURATION_DESCRIPTOR_ENTRIES,
196                         (UCHAR *) &configuration_descriptor);
197 
198             /* Now we need to check the configuration value.  */
199             if (configuration_descriptor.bConfigurationValue == device -> ux_slave_device_configuration_selected)
200             {
201 
202                 /* Limit the search in current configuration descriptor. */
203                 device_framework_length = configuration_descriptor.wTotalLength;
204 
205                 /* We have found the configuration value that was selected by the host
206                    We need to scan all the interface descriptors following this
207                    configuration descriptor and locate the interface for which the alternate
208                    setting must be changed. */
209                 while (device_framework_length != 0)
210                 {
211 
212                     /* Get the length of the current descriptor.  */
213                     descriptor_length =  (ULONG) *device_framework;
214 
215                     /* And its type.  */
216                     descriptor_type = *(device_framework + 1);
217 
218                     /* Check if this is an interface descriptor. */
219                     if (descriptor_type == UX_INTERFACE_DESCRIPTOR_ITEM)
220                     {
221 
222                         /* Parse the descriptor in something more readable. */
223                         _ux_utility_descriptor_parse(device_framework,
224                                     _ux_system_interface_descriptor_structure,
225                                     UX_INTERFACE_DESCRIPTOR_ENTRIES,
226                                     (UCHAR *) &interface_descriptor);
227 
228                         /* Check if this is the interface we are searching. */
229                         if (interface_descriptor.bInterfaceNumber == interface_value &&
230                             interface_descriptor.bAlternateSetting == alternate_setting_value)
231                         {
232 
233                             /* We have found the right interface and alternate setting. Before
234                                we mount all the endpoints for this interface, we need to
235                                unmount the endpoints associated with the previous alternate setting.  */
236                             endpoint =  interface_ptr -> ux_slave_interface_first_endpoint;
237                             while (endpoint != UX_NULL)
238                             {
239 
240                                 /* Abort any pending transfer.  */
241                                 _ux_device_stack_transfer_all_request_abort(endpoint, UX_TRANSFER_BUS_RESET);
242 
243                                 /* The device controller must be called to destroy the endpoint.  */
244                                 dcd -> ux_slave_dcd_function(dcd, UX_DCD_DESTROY_ENDPOINT, (VOID *) endpoint);
245 
246                                 /* Get the next endpoint.  */
247                                 next_endpoint =  endpoint -> ux_slave_endpoint_next_endpoint;
248 
249                                 /* Free the endpoint.  */
250                                 endpoint -> ux_slave_endpoint_status =  UX_UNUSED;
251 
252                                 /* Make sure the endpoint instance is now cleaned up.  */
253                                 endpoint -> ux_slave_endpoint_state =  0;
254                                 endpoint -> ux_slave_endpoint_next_endpoint =  UX_NULL;
255                                 endpoint -> ux_slave_endpoint_interface =  UX_NULL;
256                                 endpoint -> ux_slave_endpoint_device =  UX_NULL;
257 
258                                 /* Now we refresh the endpoint pointer.  */
259                                 endpoint =  next_endpoint;
260                             }
261 
262                             /* Now clear the interface endpoint entry.  */
263                             interface_ptr -> ux_slave_interface_first_endpoint = UX_NULL;
264 
265                             /* Point beyond the interface descriptor.  */
266                             device_framework_length -=  (ULONG) *device_framework;
267                             device_framework +=  (ULONG) *device_framework;
268 
269                             /* Parse the device framework and locate endpoint descriptor(s).  */
270                             while (device_framework_length != 0)
271                             {
272 
273                                 /* Get the length of the current descriptor.  */
274                                 descriptor_length =  (ULONG) *device_framework;
275 
276                                 /* And its type.  */
277                                 descriptor_type =  *(device_framework + 1);
278 
279                                 /* Check if this is an endpoint descriptor.  */
280                                 switch(descriptor_type)
281                                 {
282 
283                                 case UX_ENDPOINT_DESCRIPTOR_ITEM:
284 
285                                     /* Find a free endpoint in the pool and hook it to the
286                                        existing interface after it's created by DCD.  */
287                                     endpoint = device -> ux_slave_device_endpoints_pool;
288                                     endpoints_pool_number = device -> ux_slave_device_endpoints_pool_number;
289                                     while (endpoints_pool_number != 0)
290                                     {
291                                         /* Check if this endpoint is free.  */
292                                         if (endpoint ->    ux_slave_endpoint_status == UX_UNUSED)
293                                         {
294                                             /* Mark this endpoint as used now.  */
295                                             endpoint ->    ux_slave_endpoint_status = UX_USED;
296                                             break;
297                                         }
298 
299                                         /* Try the next endpoint.  */
300                                         endpoint++;
301 
302                                         /* Decrement the number of endpoints to scan from the pool.  */
303                                        endpoints_pool_number--;
304                                     }
305 
306                                     /* Did we find a free endpoint ?  */
307                                     if (endpoints_pool_number == 0)
308                                         return(UX_MEMORY_INSUFFICIENT);
309 
310                                     /* Parse the descriptor in something more readable.  */
311                                     _ux_utility_descriptor_parse(device_framework,
312                                                     _ux_system_endpoint_descriptor_structure,
313                                                     UX_ENDPOINT_DESCRIPTOR_ENTRIES,
314                                                     (UCHAR *) &endpoint -> ux_slave_endpoint_descriptor);
315 
316                                     /* Now we create a transfer request to accept transfer on this endpoint.  */
317                                     transfer_request =  &endpoint -> ux_slave_endpoint_transfer_request;
318 
319                                     /* Validate descriptor wMaxPacketSize.  */
320                                     UX_ASSERT(endpoint -> ux_slave_endpoint_descriptor.wMaxPacketSize != 0);
321 
322                                     /* Calculate endpoint transfer payload max size.  */
323                                     max_transfer_length =
324                                             endpoint -> ux_slave_endpoint_descriptor.wMaxPacketSize &
325                                                                                 UX_MAX_PACKET_SIZE_MASK;
326                                     if ((_ux_system_slave -> ux_system_slave_speed == UX_HIGH_SPEED_DEVICE) &&
327                                         (endpoint -> ux_slave_endpoint_descriptor.bmAttributes & 0x1u))
328                                     {
329                                         n_trans = endpoint -> ux_slave_endpoint_descriptor.wMaxPacketSize &
330                                                                     UX_MAX_NUMBER_OF_TRANSACTIONS_MASK;
331                                         if (n_trans)
332                                         {
333                                             n_trans >>= UX_MAX_NUMBER_OF_TRANSACTIONS_SHIFT;
334                                             n_trans ++;
335                                             max_transfer_length *= n_trans;
336                                         }
337                                     }
338 
339                                     /* Validate max transfer size and save it.  */
340                                     UX_ASSERT(max_transfer_length <= UX_SLAVE_REQUEST_DATA_MAX_LENGTH);
341                                     transfer_request -> ux_slave_transfer_request_transfer_length = max_transfer_length;
342 
343                                     /* We store the endpoint in the transfer request as well.  */
344                                     transfer_request -> ux_slave_transfer_request_endpoint =  endpoint;
345 
346                                     /* By default the timeout is infinite on request.  */
347                                     transfer_request -> ux_slave_transfer_request_timeout = UX_WAIT_FOREVER;
348 
349                                     /* Attach the interface to the endpoint.  */
350                                     endpoint -> ux_slave_endpoint_interface =  interface_ptr;
351 
352                                     /* Attach the device to the endpoint.  */
353                                     endpoint -> ux_slave_endpoint_device =  device;
354 
355                                     /* Create the endpoint at the DCD level.  */
356                                     status =  dcd -> ux_slave_dcd_function(dcd, UX_DCD_CREATE_ENDPOINT, (VOID *) endpoint);
357 
358                                     /* Do a sanity check on endpoint creation.  */
359                                     if (status != UX_SUCCESS)
360                                     {
361 
362                                         /* Error was returned, endpoint cannot be created.  */
363                                         endpoint -> ux_slave_endpoint_status = UX_UNUSED;
364                                         return(status);
365                                     }
366 
367                                     /* Attach this endpoint to the end of the endpoint chain.  */
368                                     if (interface_ptr -> ux_slave_interface_first_endpoint == UX_NULL)
369                                     {
370 
371                                         interface_ptr -> ux_slave_interface_first_endpoint =  endpoint;
372                                     }
373                                     else
374                                     {
375                                         /* Multiple endpoints exist, so find the end of the chain.  */
376                                         endpoint_link =  interface_ptr -> ux_slave_interface_first_endpoint;
377                                         while (endpoint_link -> ux_slave_endpoint_next_endpoint != UX_NULL)
378                                             endpoint_link =  endpoint_link -> ux_slave_endpoint_next_endpoint;
379                                         endpoint_link -> ux_slave_endpoint_next_endpoint =  endpoint;
380                                     }
381 
382                                     break;
383 
384                                 case UX_CONFIGURATION_DESCRIPTOR_ITEM:
385                                 case UX_INTERFACE_DESCRIPTOR_ITEM:
386 
387                                     /* We have found a new configuration or interface descriptor, this is the end of the current
388                                        interface. The search for the endpoints must be terminated as if it was the end of the
389                                        entire descriptor.  */
390                                     device_framework_length =  descriptor_length;
391 
392                                     break;
393 
394 
395                                 default:
396 
397                                     /* We have found another descriptor embedded in the interface. Ignore it.  */
398                                     break;
399                                 }
400 
401                                 /* Adjust what is left of the device framework.  */
402                                 device_framework_length -=  descriptor_length;
403 
404                                 /* Point to the next descriptor.  */
405                                 device_framework +=  descriptor_length;
406                             }
407 
408                             /* The interface descriptor in the current class must be changed to the new alternate setting.  */
409                             _ux_utility_memory_copy(&interface_ptr -> ux_slave_interface_descriptor, &interface_descriptor, sizeof(UX_INTERFACE_DESCRIPTOR)); /* Use case of memcpy is verified. */
410 
411                             /* Get the class for the interface.  */
412                             class_ptr =  _ux_system_slave -> ux_system_slave_interface_class_array[interface_ptr -> ux_slave_interface_descriptor.bInterfaceNumber];
413 
414                             /* Check if class driver is available. */
415                             if (class_ptr == UX_NULL || class_ptr -> ux_slave_class_status == UX_UNUSED)
416                             {
417 
418                                 return (UX_NO_CLASS_MATCH);
419                             }
420 
421                             /* The interface attached to this configuration must be changed at the class
422                                level.  */
423                             class_command.ux_slave_class_command_request   =    UX_SLAVE_CLASS_COMMAND_CHANGE;
424                             class_command.ux_slave_class_command_interface =   (VOID *) interface_ptr;
425 
426                             /* And store it.  */
427                             class_command.ux_slave_class_command_class_ptr =  class_ptr;
428 
429                             /* We can now memorize the interface pointer associated with this class.  */
430                             class_ptr -> ux_slave_class_interface = interface_ptr;
431 
432                             /* We have found a potential candidate. Call this registered class entry function to change the alternate setting.  */
433                             status = class_ptr -> ux_slave_class_entry_function(&class_command);
434 
435                             /* We are done here.  */
436                             return(status);
437                         }
438                     }
439 
440                     /* Adjust what is left of the device framework.  */
441                     device_framework_length -=  descriptor_length;
442 
443                     /* Point to the next descriptor.  */
444                     device_framework +=  descriptor_length;
445                 }
446 
447                 /* In case alter setting not found, report protocol error. */
448                 break;
449             }
450         }
451 
452         /* Adjust what is left of the device framework.  */
453         device_framework_length -=  descriptor_length;
454 
455         /* Point to the next descriptor.  */
456         device_framework +=  descriptor_length;
457     }
458 
459     /* Return error completion.  */
460     return(UX_ERROR);
461 #endif
462 }
463 
464