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