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