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