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 /* FUNCTION RELEASE */
33 /* */
34 /* _ux_device_stack_control_request_process PORTABLE C */
35 /* 6.3.0 */
36 /* AUTHOR */
37 /* */
38 /* Chaoqiong Xiao, Microsoft Corporation */
39 /* */
40 /* DESCRIPTION */
41 /* */
42 /* This function is called by the DCD when the device has received a */
43 /* SETUP packet. */
44 /* */
45 /* INPUT */
46 /* */
47 /* transfer_request Pointer to transfer request */
48 /* */
49 /* OUTPUT */
50 /* */
51 /* Completion Status */
52 /* */
53 /* CALLS */
54 /* */
55 /* (ux_slave_class_entry_function) Device class entry function */
56 /* (ux_slave_dcd_function) DCD dispatch function */
57 /* _ux_device_stack_transfer_request Transfer request */
58 /* _ux_device_stack_endpoint_stall Stall endpoint */
59 /* _ux_device_stack_alternate_setting_get */
60 /* Get alternate settings */
61 /* _ux_device_stack_alternate_setting_set */
62 /* Set alternate settings */
63 /* _ux_device_stack_clear_feature Clear feature */
64 /* _ux_device_stack_configuration_get Get configuration */
65 /* _ux_device_stack_configuration_set Set configuration */
66 /* _ux_device_stack_descriptor_send Send descriptor */
67 /* _ux_device_stack_get_status Get status */
68 /* _ux_device_stack_set_feature Set feature */
69 /* _ux_utility_short_get Get short value */
70 /* */
71 /* CALLED BY */
72 /* */
73 /* Device Stack */
74 /* */
75 /* RELEASE HISTORY */
76 /* */
77 /* DATE NAME DESCRIPTION */
78 /* */
79 /* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */
80 /* 09-30-2020 Chaoqiong Xiao Modified comment(s), */
81 /* resulting in version 6.1 */
82 /* 10-15-2021 Chaoqiong Xiao Modified comment(s), */
83 /* fixed possible buffer issue */
84 /* for control vendor request, */
85 /* resulting in version 6.1.9 */
86 /* 01-31-2022 Chaoqiong Xiao Modified comment(s), */
87 /* added printer support, */
88 /* resulting in version 6.1.10 */
89 /* 07-29-2022 Chaoqiong Xiao Modified comment(s), */
90 /* fixed parameter/variable */
91 /* names conflict C++ keyword, */
92 /* resulting in version 6.1.12 */
93 /* 03-08-2023 Chaoqiong Xiao Modified comment(s), */
94 /* fixed vendor request issue, */
95 /* resulting in version 6.2.1 */
96 /* 10-31-2023 Chaoqiong Xiao Modified comment(s), */
97 /* improved interface request */
98 /* process with print class, */
99 /* resulting in version 6.3.0 */
100 /* */
101 /**************************************************************************/
_ux_device_stack_control_request_process(UX_SLAVE_TRANSFER * transfer_request)102 UINT _ux_device_stack_control_request_process(UX_SLAVE_TRANSFER *transfer_request)
103 {
104
105 UX_SLAVE_DCD *dcd;
106 UX_SLAVE_DEVICE *device;
107 UX_SLAVE_CLASS *class_ptr;
108 UX_SLAVE_CLASS_COMMAND class_command;
109 ULONG request_type;
110 ULONG request;
111 ULONG request_value;
112 ULONG request_index;
113 ULONG request_length;
114 ULONG class_index;
115 UINT status = UX_ERROR;
116 UX_SLAVE_ENDPOINT *endpoint;
117 ULONG application_data_length;
118
119 /* Get the pointer to the DCD. */
120 dcd = &_ux_system_slave -> ux_system_slave_dcd;
121
122 /* Get the pointer to the device. */
123 device = &_ux_system_slave -> ux_system_slave_device;
124
125 /* Ensure that the Setup request has been received correctly. */
126 if (transfer_request -> ux_slave_transfer_request_completion_code == UX_SUCCESS)
127 {
128
129 /* Seems so far, the Setup request is valid. Extract all fields of
130 the request. */
131 request_type = *transfer_request -> ux_slave_transfer_request_setup;
132 request = *(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_REQUEST);
133 request_value = _ux_utility_short_get(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_VALUE);
134 request_index = _ux_utility_short_get(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_INDEX);
135 request_length = _ux_utility_short_get(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_LENGTH);
136
137 /* Filter for GET_DESCRIPTOR/SET_DESCRIPTOR commands. If the descriptor to be returned is not a standard descriptor,
138 treat the command as a CLASS command. */
139 if ((request == UX_GET_DESCRIPTOR || request == UX_SET_DESCRIPTOR) && (((request_value >> 8) & UX_REQUEST_TYPE) != UX_REQUEST_TYPE_STANDARD))
140 {
141
142 /* This request is to be handled by the class layer. */
143 request_type &= (UINT)~UX_REQUEST_TYPE;
144 request_type |= UX_REQUEST_TYPE_CLASS;
145 }
146
147 /* Check if there is a vendor registered function at the application layer. If the request
148 is VENDOR and the request match, pass the request to the application. */
149 if ((request_type & UX_REQUEST_TYPE) == UX_REQUEST_TYPE_VENDOR)
150 {
151
152 /* Check the request demanded and compare it to the application registered one. */
153 if (_ux_system_slave -> ux_system_slave_device_vendor_request_function != UX_NULL &&
154 request == _ux_system_slave -> ux_system_slave_device_vendor_request)
155 {
156
157 /* This is a Microsoft extended function. It happens before the device is configured.
158 The request is passed to the application directly. */
159 application_data_length = UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH;
160 status = _ux_system_slave -> ux_system_slave_device_vendor_request_function(request, request_value,
161 request_index, request_length,
162 transfer_request -> ux_slave_transfer_request_data_pointer,
163 &application_data_length);
164
165 /* Check the status from the application. */
166 if (status == UX_SUCCESS)
167 {
168
169 /* Get the control endpoint associated with the device. */
170 endpoint = &device -> ux_slave_device_control_endpoint;
171
172 /* Get the pointer to the transfer request associated with the control endpoint. */
173 transfer_request = &endpoint -> ux_slave_endpoint_transfer_request;
174
175 /* Set the direction to OUT. */
176 transfer_request -> ux_slave_transfer_request_phase = UX_TRANSFER_PHASE_DATA_OUT;
177
178 /* Perform the data transfer. */
179 _ux_device_stack_transfer_request(transfer_request, application_data_length, request_length);
180
181 /* We are done here. */
182 return(UX_SUCCESS);
183 }
184 else
185 {
186
187 /* The application did not like the vendor command format, stall the control endpoint. */
188 _ux_device_stack_endpoint_stall(&device -> ux_slave_device_control_endpoint);
189
190 /* We are done here. */
191 return(UX_SUCCESS);
192 }
193 }
194 }
195
196 /* Check the destination of the request. If the request is of type CLASS or VENDOR_SPECIFIC,
197 the function has to be passed to the class layer. */
198 if (((request_type & UX_REQUEST_TYPE) == UX_REQUEST_TYPE_CLASS) ||
199 ((request_type & UX_REQUEST_TYPE) == UX_REQUEST_TYPE_VENDOR))
200 {
201
202 /* Build all the fields of the Class Command. */
203 class_command.ux_slave_class_command_request = UX_SLAVE_CLASS_COMMAND_REQUEST;
204
205 /* We need to find which class this request is for. */
206 for (class_index = 0; class_index < UX_MAX_SLAVE_INTERFACES; class_index ++)
207 {
208
209 /* Get the class for the interface. */
210 class_ptr = _ux_system_slave -> ux_system_slave_interface_class_array[class_index];
211
212 /* If class is not ready, try next. */
213 if (class_ptr == UX_NULL)
214 continue;
215
216 /* Is the request target to an interface? */
217 if ((request_type & UX_REQUEST_TARGET) == UX_REQUEST_TARGET_INTERFACE)
218 {
219
220 /* Yes, so the request index contains the index of the interface
221 the request is for. So if the current index does not match
222 the request index, we should go to the next one. */
223 /* For printer class (0x07) GET_DEVICE_ID (0x00) the high byte of
224 wIndex is interface index (for recommended index sequence the interface
225 number is same as interface index inside configuration).
226 */
227 if ((request_type == 0xA1) && (request == 0x00) &&
228 (class_ptr -> ux_slave_class_interface -> ux_slave_interface_descriptor.bInterfaceClass == 0x07))
229 {
230
231 /* Check wIndex high byte. */
232 if(*(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_INDEX + 1) != class_index)
233 continue;
234 }
235 else
236 {
237
238 /* Check wIndex low. */
239 if ((request_index & 0xFF) != class_index)
240 continue;
241 }
242 }
243
244 /* Memorize the class in the command. */
245 class_command.ux_slave_class_command_class_ptr = class_ptr;
246
247 /* We have found a potential candidate. Call this registered class entry function. */
248 status = class_ptr -> ux_slave_class_entry_function(&class_command);
249
250 /* The status simply tells us if the registered class handled the
251 command - if there was an issue processing the command, it would've
252 stalled the control endpoint, notifying the host (and not us). */
253 if (status == UX_SUCCESS)
254
255 /* We are done, break the loop! */
256 break;
257
258 /* Not handled, try next. */
259 }
260
261 /* If no class handled the command, then we have an error here. */
262 if (status != UX_SUCCESS)
263
264 /* We stall the command (request not supported). */
265 _ux_device_stack_endpoint_stall(&device -> ux_slave_device_control_endpoint);
266
267 /* We are done for class/vendor request. */
268 return(status);
269 }
270
271 /* At this point, the request must be a standard request that the device stack should handle. */
272 switch (request)
273 {
274
275 case UX_GET_STATUS:
276
277 status = _ux_device_stack_get_status(request_type, request_index, request_length);
278 break;
279
280 case UX_CLEAR_FEATURE:
281
282 status = _ux_device_stack_clear_feature(request_type, request_value, request_index);
283 break;
284
285 case UX_SET_FEATURE:
286
287 status = _ux_device_stack_set_feature(request_type, request_value, request_index);
288 break;
289
290 case UX_SET_ADDRESS:
291
292 /* Memorize the address. Some controllers memorize the address here. Some don't. */
293 dcd -> ux_slave_dcd_device_address = request_value;
294
295 /* Force the new address. */
296 status = dcd -> ux_slave_dcd_function(dcd, UX_DCD_SET_DEVICE_ADDRESS, (VOID *) (ALIGN_TYPE) request_value);
297 break;
298
299 case UX_GET_DESCRIPTOR:
300
301 status = _ux_device_stack_descriptor_send(request_value, request_index, request_length);
302 break;
303
304 case UX_SET_DESCRIPTOR:
305
306 status = UX_FUNCTION_NOT_SUPPORTED;
307 break;
308
309 case UX_GET_CONFIGURATION:
310
311 status = _ux_device_stack_configuration_get();
312 break;
313
314 case UX_SET_CONFIGURATION:
315
316 status = _ux_device_stack_configuration_set(request_value);
317 break;
318
319 case UX_GET_INTERFACE:
320
321 status = _ux_device_stack_alternate_setting_get(request_index);
322 break;
323
324 case UX_SET_INTERFACE:
325
326 status = _ux_device_stack_alternate_setting_set(request_index,request_value);
327 break;
328
329
330 case UX_SYNCH_FRAME:
331
332 status = UX_SUCCESS;
333 break;
334
335 default :
336
337 status = UX_FUNCTION_NOT_SUPPORTED;
338 break;
339 }
340
341 if (status != UX_SUCCESS)
342
343 /* Stall the control endpoint to issue protocol error. */
344 _ux_device_stack_endpoint_stall(&device -> ux_slave_device_control_endpoint);
345 }
346
347 /* Return the function status. */
348 return(status);
349 }
350
351