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