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 /**   Host Simulator Controller Driver                                    */
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_hcd_sim_host.h"
29 #include "ux_host_stack.h"
30 
31 
32 /**************************************************************************/
33 /*                                                                        */
34 /*  FUNCTION                                               RELEASE        */
35 /*                                                                        */
36 /*    _ux_hcd_sim_host_request_control_transfer           PORTABLE C      */
37 /*                                                           6.1.10       */
38 /*  AUTHOR                                                                */
39 /*                                                                        */
40 /*    Chaoqiong Xiao, Microsoft Corporation                               */
41 /*                                                                        */
42 /*  DESCRIPTION                                                           */
43 /*                                                                        */
44 /*     This function performs a control transfer from a transfer request. */
45 /*     The USB control transfer is in 3 phases (setup, data, status).     */
46 /*     This function will chain all phases of the control sequence before */
47 /*     setting the sim_host endpoint as a candidate for transfer.         */
48 /*                                                                        */
49 /*  INPUT                                                                 */
50 /*                                                                        */
51 /*    hcd_sim_host                          Pointer to host controller    */
52 /*    transfer_request                      Pointer to transfer request   */
53 /*                                                                        */
54 /*  OUTPUT                                                                */
55 /*                                                                        */
56 /*    Completion Status                                                   */
57 /*                                                                        */
58 /*  CALLS                                                                 */
59 /*                                                                        */
60 /*    _ux_hcd_sim_host_regular_td_obtain    Obtain regular TD             */
61 /*    _ux_host_stack_transfer_request_abort Abort transfer request        */
62 /*    _ux_utility_memory_allocate           Allocate memory block         */
63 /*    _ux_utility_memory_free               Release memory block          */
64 /*    _ux_utility_semaphore_get             Get semaphore                 */
65 /*    _ux_utility_short_put                 Write 16-bit value            */
66 /*                                                                        */
67 /*  CALLED BY                                                             */
68 /*                                                                        */
69 /*    Host Simulator Controller Driver                                    */
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 /*                                            prefixed UX to MS_TO_TICK,  */
78 /*                                            resulting in version 6.1    */
79 /*  01-31-2022     Chaoqiong Xiao           Modified comment(s),          */
80 /*                                            added standalone support,   */
81 /*                                            resulting in version 6.1.10 */
82 /*                                                                        */
83 /**************************************************************************/
_ux_hcd_sim_host_request_control_transfer(UX_HCD_SIM_HOST * hcd_sim_host,UX_TRANSFER * transfer_request)84 UINT  _ux_hcd_sim_host_request_control_transfer(UX_HCD_SIM_HOST *hcd_sim_host, UX_TRANSFER *transfer_request)
85 {
86 
87 UX_ENDPOINT             *endpoint;
88 UCHAR                   *setup_request;
89 UX_HCD_SIM_HOST_ED      *ed;
90 UX_HCD_SIM_HOST_TD      *setup_td;
91 UX_HCD_SIM_HOST_TD      *chain_td;
92 UX_HCD_SIM_HOST_TD      *data_td;
93 UX_HCD_SIM_HOST_TD      *tail_td;
94 UX_HCD_SIM_HOST_TD      *status_td;
95 UX_HCD_SIM_HOST_TD      *start_data_td;
96 UX_HCD_SIM_HOST_TD      *next_data_td;
97 ULONG                   transfer_request_payload_length;
98 ULONG                   control_packet_payload_length;
99 UCHAR                   *data_pointer;
100 #if !defined(UX_HOST_STANDALONE)
101 UINT                    status;
102 #endif
103 
104 
105     /* Get the pointer to the endpoint.  */
106     endpoint =  (UX_ENDPOINT *) transfer_request -> ux_transfer_request_endpoint;
107 
108     /* Now get the physical ED attached to this endpoint.  */
109     ed =  endpoint -> ux_endpoint_ed;
110 
111     /* Build the SETUP packet (phase 1 of the control transfer).  */
112     setup_request =  _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, UX_SETUP_SIZE);
113     if (setup_request == UX_NULL)
114         return(UX_MEMORY_INSUFFICIENT);
115 
116     *setup_request =                            (UCHAR)transfer_request -> ux_transfer_request_function;
117     *(setup_request + UX_SETUP_REQUEST_TYPE) =  (UCHAR)transfer_request -> ux_transfer_request_type;
118     *(setup_request + UX_SETUP_REQUEST) =       (UCHAR)transfer_request -> ux_transfer_request_function;
119     _ux_utility_short_put(setup_request + UX_SETUP_VALUE, (USHORT)transfer_request -> ux_transfer_request_value);
120     _ux_utility_short_put(setup_request + UX_SETUP_INDEX, (USHORT)transfer_request -> ux_transfer_request_index);
121     _ux_utility_short_put(setup_request + UX_SETUP_LENGTH, (USHORT) transfer_request -> ux_transfer_request_requested_length);
122 
123     /* Use the TD pointer by ed -> tail for our setup TD and chain from this one on.  */
124     setup_td =  ed -> ux_sim_host_ed_tail_td;
125     setup_td -> ux_sim_host_td_buffer =  setup_request;
126     setup_td -> ux_sim_host_td_length =  UX_SETUP_SIZE;
127     chain_td =  setup_td;
128 
129     /* Attach the endpoint and transfer_request to the TD.  */
130     setup_td -> ux_sim_host_td_transfer_request =  transfer_request;
131     setup_td -> ux_sim_host_td_ed =  ed;
132 
133     /* Setup is OUT.  */
134     setup_td -> ux_sim_host_td_direction =  UX_HCD_SIM_HOST_TD_OUT;
135 
136     /* Mark TD toggle as being DATA0.  */
137     setup_td -> ux_sim_host_td_toggle =  0;
138 
139     /* Mark the TD with the SETUP phase.  */
140     setup_td -> ux_sim_host_td_status |=  UX_HCD_SIM_HOST_TD_SETUP_PHASE;
141 
142     /* Check if there is a data phase, if not jump to status phase.  */
143     data_td =  UX_NULL;
144     start_data_td =  UX_NULL;
145 
146     /* Use local variables to manipulate data pointer and length.  */
147     transfer_request_payload_length =  transfer_request -> ux_transfer_request_requested_length;
148     data_pointer =  transfer_request -> ux_transfer_request_data_pointer;
149 
150     /* Data starts with DATA1. For the data phase, we use the ED to contain the toggle.  */
151     ed -> ux_sim_host_ed_toggle =  1;
152 
153     /* The Control data payload may be split into several smaller blocks.  */
154     while (transfer_request_payload_length != 0)
155     {
156 
157         /* Get a new TD to hook this payload.  */
158         data_td =  _ux_hcd_sim_host_regular_td_obtain(hcd_sim_host);
159         if (data_td == UX_NULL)
160         {
161 
162             /* Free the Setup packet resources.  */
163             _ux_utility_memory_free(setup_request);
164 
165             /* If there was already a TD chain in progress, free it.  */
166             if (start_data_td != UX_NULL)
167             {
168 
169                 data_td =  start_data_td;
170                 while (data_td)
171                 {
172 
173                     next_data_td =  data_td -> ux_sim_host_td_next_td;
174                     data_td -> ux_sim_host_td_status =  UX_UNUSED;
175                     data_td =  next_data_td;
176                 }
177             }
178 
179             return(UX_NO_TD_AVAILABLE);
180         }
181 
182         /* Check the current payload requirement for the max size.  */
183         if (transfer_request_payload_length > UX_HCD_SIM_HOST_MAX_PAYLOAD)
184 
185             control_packet_payload_length =  UX_HCD_SIM_HOST_MAX_PAYLOAD;
186         else
187 
188             control_packet_payload_length =  transfer_request_payload_length;
189 
190         /* Store the beginning of the buffer address in the TD.  */
191         data_td -> ux_sim_host_td_buffer =  data_pointer;
192 
193         /* Update the length of the transfer for this TD.  */
194         data_td -> ux_sim_host_td_length =  control_packet_payload_length;
195 
196         /* Attach the endpoint and transfer request to the TD.  */
197         data_td -> ux_sim_host_td_transfer_request =  transfer_request;
198         data_td -> ux_sim_host_td_ed =  ed;
199 
200         /* Adjust the data payload length and the data payload pointer.  */
201         transfer_request_payload_length -=  control_packet_payload_length;
202         data_pointer +=  control_packet_payload_length;
203 
204         /* The direction of the transaction is set in the TD.  */
205         if ((transfer_request -> ux_transfer_request_type & UX_REQUEST_DIRECTION) == UX_REQUEST_IN)
206 
207             data_td -> ux_sim_host_td_direction =  UX_HCD_SIM_HOST_TD_IN;
208         else
209 
210             data_td -> ux_sim_host_td_direction =  UX_HCD_SIM_HOST_TD_OUT;
211 
212         /* Mark the TD with the DATA phase.  */
213         data_td -> ux_sim_host_td_status |=  UX_HCD_SIM_HOST_TD_DATA_PHASE;
214 
215         /* The Toggle value is in the ED.  */
216         data_td -> ux_sim_host_td_toggle =  UX_HCD_SIM_HOST_TD_TOGGLE_FROM_ED;
217 
218         /* The first obtained TD in the chain has to be remembered.  */
219         if (start_data_td == UX_NULL)
220             start_data_td =  data_td;
221 
222         /* Attach this new TD to the previous one.  */
223         chain_td -> ux_sim_host_td_next_td =  data_td;
224         chain_td -> ux_sim_host_td_next_td_transfer_request =  data_td;
225         chain_td =  data_td;
226     }
227 
228     /* Now, program the status phase.  */
229     status_td =  _ux_hcd_sim_host_regular_td_obtain(hcd_sim_host);
230 
231     if (status_td == UX_NULL)
232     {
233 
234         _ux_utility_memory_free(setup_request);
235         if (data_td != UX_NULL)
236         {
237 
238             data_td =  start_data_td;
239             while (data_td)
240             {
241 
242                 next_data_td =  data_td -> ux_sim_host_td_next_td;
243                 data_td -> ux_sim_host_td_status =  UX_UNUSED;
244                 data_td =  next_data_td;
245             }
246         }
247 
248         return(UX_NO_TD_AVAILABLE);
249     }
250 
251     /* Attach the endpoint and transfer request to the TD.  */
252     status_td -> ux_sim_host_td_transfer_request =  transfer_request;
253     status_td -> ux_sim_host_td_ed =  ed;
254 
255     /* Mark the TD with the STATUS phase.  */
256     status_td -> ux_sim_host_td_status |=  UX_HCD_SIM_HOST_TD_STATUS_PHASE;
257 
258     /* The direction of the status phase is IN if data phase is OUT and
259        vice versa.  */
260     if ((transfer_request -> ux_transfer_request_type&UX_REQUEST_DIRECTION) == UX_REQUEST_IN)
261 
262         status_td -> ux_sim_host_td_direction =  UX_HCD_SIM_HOST_TD_OUT;
263     else
264 
265         status_td -> ux_sim_host_td_direction =  UX_HCD_SIM_HOST_TD_IN;
266 
267     /* No data payload for the status phase.  */
268     status_td -> ux_sim_host_td_buffer =  0;
269     status_td -> ux_sim_host_td_length =  0;
270 
271     /* Status Phase toggle is ALWAYS 1.  */
272     status_td -> ux_sim_host_td_toggle =  1;
273 
274     /* Hook the status phase to the previous TD.  */
275     chain_td -> ux_sim_host_td_next_td =  status_td;
276 
277     /* Since we have consumed out tail TD for the setup packet, we must get another one
278        and hook it to the ED's tail.  */
279     tail_td =  _ux_hcd_sim_host_regular_td_obtain(hcd_sim_host);
280     if (tail_td == UX_NULL)
281     {
282 
283         _ux_utility_memory_free(setup_request);
284         if (data_td != UX_NULL)
285             data_td -> ux_sim_host_td_status =  UX_UNUSED;
286         status_td -> ux_sim_host_td_status =  UX_UNUSED;
287         return(UX_NO_TD_AVAILABLE);
288     }
289 
290     /* Hook the new TD to the status TD.  */
291     status_td -> ux_sim_host_td_next_td =  tail_td;
292 
293     /* At this stage, the Head and Tail in the ED are still the same and
294        the host simulator controller will skip this ED until we have hooked the new
295        tail TD.  */
296     ed -> ux_sim_host_ed_tail_td =  tail_td;
297 
298     /* Now we can tell the scheduler to wake up.  */
299     hcd_sim_host -> ux_hcd_sim_host_queue_empty =  UX_FALSE;
300 
301 #if defined(UX_HOST_STANDALONE)
302     /* Transfer started in background, fine.  */
303     return(UX_SUCCESS);
304 #else
305     /* Wait for the completion of the transfer request.  */
306     status =  _ux_host_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, UX_MS_TO_TICK(UX_CONTROL_TRANSFER_TIMEOUT));
307 
308     /* If the semaphore did not succeed we probably have a time out.  */
309     if (status != UX_SUCCESS)
310     {
311 
312         /* All transfers pending need to abort. There may have been a partial transfer.  */
313         _ux_host_stack_transfer_request_abort(transfer_request);
314 
315         /* There was an error, return to the caller.  */
316         transfer_request -> ux_transfer_request_completion_code =  UX_TRANSFER_TIMEOUT;
317 
318         /* Error trap. */
319         _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_TRANSFER_TIMEOUT);
320 
321         /* If trace is enabled, insert this event into the trace buffer.  */
322         UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_TIMEOUT, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0)
323 
324     }
325 
326     /* Free the resources.  */
327     _ux_utility_memory_free(setup_request);
328 
329     /* Return completion to caller.  */
330     return(transfer_request -> ux_transfer_request_completion_code);
331 #endif
332 }
333