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