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 Stack */
18 /** */
19 /**************************************************************************/
20 /**************************************************************************/
21
22
23 /* Include necessary system files. */
24
25 #define UX_SOURCE_CODE
26
27 #include "ux_api.h"
28 #include "ux_host_stack.h"
29
30
31 #if defined(UX_HOST_STANDALONE)
32
33 #define UX_HOST_STACK_TRANSFER_LIST_IS_NULL 0
34 #define UX_HOST_STACK_TRANSFER_NOT_IN_LIST 1
35 #define UX_HOST_STACK_TRANSFER_AT_LIST_HEAD 2
36 #define UX_HOST_STACK_TRANSFER_IN_LIST 3
37 static inline UINT _ux_host_stack_transfer_locate(UX_TRANSFER *transfer, UX_TRANSFER **previous);
38 static inline void _ux_host_stack_transfer_retire(UX_TRANSFER *transfer);
39
40
41 /**************************************************************************/
42 /* */
43 /* FUNCTION RELEASE */
44 /* */
45 /* _ux_host_stack_transfer_run PORTABLE C */
46 /* 6.1.10 */
47 /* AUTHOR */
48 /* */
49 /* Chaoqiong Xiao, Microsoft Corporation */
50 /* */
51 /* DESCRIPTION */
52 /* */
53 /* This function performs a USB transaction. On entry the transfer */
54 /* request gives the endpoint pipe selected for this transaction and */
55 /* the parameters associated with the transfer (data payload, length */
56 /* of transaction) */
57 /* */
58 /* Note after transfer is done, it's in error or idle state. To ensure */
59 /* transfer is ready for next round, use UX_TRANSFER_STATE_RESET(). */
60 /* */
61 /* It's for standalone mode. */
62 /* */
63 /* INPUT */
64 /* */
65 /* transfer_request Transfer request structure */
66 /* */
67 /* OUTPUT */
68 /* */
69 /* State machine Status to check */
70 /* UX_STATE_NEXT Transfer done, to next state */
71 /* UX_STATE_EXIT Abnormal, to reset state */
72 /* (others) Keep running, waiting */
73 /* */
74 /* CALLS */
75 /* */
76 /* HCD Entry Function */
77 /* */
78 /* CALLED BY */
79 /* */
80 /* Application */
81 /* USBX Components */
82 /* */
83 /* RELEASE HISTORY */
84 /* */
85 /* DATE NAME DESCRIPTION */
86 /* */
87 /* 01-31-2022 Chaoqiong Xiao Initial Version 6.1.10 */
88 /* */
89 /**************************************************************************/
_ux_host_stack_transfer_run(UX_TRANSFER * transfer_request)90 UINT _ux_host_stack_transfer_run(UX_TRANSFER *transfer_request)
91 {
92
93 UX_INTERRUPT_SAVE_AREA
94
95 UX_ENDPOINT *endpoint;
96 UX_DEVICE *device;
97 UX_HCD *hcd;
98 ULONG endpoint_type;
99 UINT status;
100
101
102 /* Get the endpoint container from the transfer_request */
103 endpoint = transfer_request -> ux_transfer_request_endpoint;
104
105 /* Sanity check. */
106 if (endpoint == UX_NULL)
107 return(UX_STATE_EXIT);
108
109 /* Get the endpoint type. */
110 endpoint_type = endpoint -> ux_endpoint_descriptor.bmAttributes;
111 endpoint_type &= UX_MASK_ENDPOINT_TYPE;
112
113 /* Get the device container from the endpoint. */
114 device = endpoint -> ux_endpoint_device;
115
116 /* Sanity check. */
117 if (device == UX_NULL)
118 return(UX_STATE_EXIT);
119
120 /* Ensure we are not preempted by the enum thread while we check the device
121 state and set the transfer status. */
122 UX_DISABLE
123
124 /* Check if it's default endpoint 0. */
125 if (endpoint -> ux_endpoint_descriptor.bEndpointAddress & ~UX_ENDPOINT_DIRECTION)
126 {
127
128 /* It's not endpoint 0, check device state. */
129 if (device -> ux_device_state != UX_DEVICE_ATTACHED &&
130 device -> ux_device_state != UX_DEVICE_ADDRESSED &&
131 device -> ux_device_state != UX_DEVICE_CONFIGURED)
132 {
133 transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_NOT_READY;
134 transfer_request -> ux_transfer_request_state = UX_STATE_EXIT;
135 _ux_host_stack_transfer_retire(transfer_request);
136
137 /* Restore interrupts. */
138 UX_RESTORE
139 return(UX_STATE_EXIT);
140 }
141 }
142
143 /* With the device we have the pointer to the HCD. */
144 hcd = UX_DEVICE_HCD_GET(device);
145
146 /* Process states. */
147 switch(transfer_request -> ux_transfer_request_state)
148 {
149 case UX_STATE_RESET:
150
151 /* Initialize request fields. */
152 transfer_request -> ux_transfer_request_actual_length = 0;
153 transfer_request -> ux_transfer_request_status = UX_TRANSFER_STATUS_NOT_PENDING;
154 transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_STATUS_PENDING;
155 transfer_request -> ux_transfer_request_state = UX_STATE_WAIT;
156 transfer_request -> ux_transfer_request_time_start = _ux_utility_time_get();
157
158 /* Add request to system pending request list. Note request may be kept
159 if transfer callback is used. */
160 if (_ux_host_stack_transfer_locate(transfer_request, UX_NULL) <
161 UX_HOST_STACK_TRANSFER_AT_LIST_HEAD)
162 {
163 transfer_request -> ux_transfer_request_next_pending =
164 _ux_system_host -> ux_system_host_pending_transfers;
165 _ux_system_host -> ux_system_host_pending_transfers = transfer_request;
166 }
167
168 /* Do immediate HCD call to start transfer in background. */
169 /* Fall through. */
170 case UX_STATE_WAIT:
171 UX_RESTORE
172
173 {
174
175 /* Send the command to the controller. */
176 status = hcd -> ux_hcd_entry_function(hcd, UX_HCD_TRANSFER_RUN, transfer_request);
177 }
178
179 /* Any error or end: simplify to idle. */
180 if (status < UX_STATE_WAIT)
181 {
182 UX_DISABLE
183 UX_TRANSFER_STATE_IDLE(transfer_request);
184 _ux_host_stack_transfer_retire(transfer_request);
185 UX_RESTORE
186 return(status);
187 }
188
189 /* Timeout check. */
190 if (transfer_request -> ux_transfer_request_timeout_value != UX_WAIT_FOREVER)
191 {
192 if (transfer_request -> ux_transfer_request_timeout_value <
193 _ux_utility_time_elapsed(transfer_request -> ux_transfer_request_time_start,
194 _ux_utility_time_get()))
195 {
196
197 /* All transfers pending need to abort. There may have been a partial transfer. */
198 _ux_host_stack_transfer_request_abort(transfer_request);
199
200 /* Set the completion code. */
201 transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_TIMEOUT;
202
203 /* There was an error: simplify to idle. */
204 UX_DISABLE
205 UX_TRANSFER_STATE_IDLE(transfer_request);
206 _ux_host_stack_transfer_retire(transfer_request);
207 UX_RESTORE
208 return(UX_STATE_ERROR);
209 }
210 }
211
212 /* And return the status. */
213 return(status);
214
215 case UX_STATE_EXIT:
216 UX_RESTORE
217 return(UX_STATE_EXIT);
218 case UX_STATE_IDLE:
219 UX_RESTORE
220 return(UX_STATE_IDLE);
221
222 default: /* Error case, return EXIT. */
223 break;
224 }
225
226 /* Error case, return EXIT. */
227 transfer_request -> ux_transfer_request_state = UX_STATE_RESET;
228 _ux_host_stack_transfer_retire(transfer_request);
229 UX_RESTORE
230 return(UX_STATE_EXIT);
231 }
_ux_host_stack_transfer_locate(UX_TRANSFER * transfer,UX_TRANSFER ** previous)232 static inline UINT _ux_host_stack_transfer_locate(UX_TRANSFER *transfer, UX_TRANSFER **previous)
233 {
234 UX_TRANSFER *prev;
235
236 /* Case 0: pending list is NULL. */
237 if (_ux_system_host -> ux_system_host_pending_transfers == UX_NULL)
238 return(UX_HOST_STACK_TRANSFER_LIST_IS_NULL);
239
240 /* Case 1: it's list head. */
241 if (_ux_system_host -> ux_system_host_pending_transfers == transfer)
242 return(UX_HOST_STACK_TRANSFER_AT_LIST_HEAD);
243
244 /* Case 2: scan list. */
245 prev = _ux_system_host -> ux_system_host_pending_transfers;
246 do
247 {
248 if (prev -> ux_transfer_request_next_pending == transfer)
249 {
250 if (previous)
251 *previous = prev;
252 return(UX_HOST_STACK_TRANSFER_IN_LIST);
253 }
254 prev = prev -> ux_transfer_request_next_pending;
255 } while (prev);
256
257 /* Case 3: not found. */
258 return(UX_HOST_STACK_TRANSFER_NOT_IN_LIST);
259 }
_ux_host_stack_transfer_retire(UX_TRANSFER * transfer)260 static inline void _ux_host_stack_transfer_retire(UX_TRANSFER *transfer)
261 {
262 ULONG flags;
263 UX_TRANSFER *previous;
264
265 /* Locate the request in pending list. */
266 switch(_ux_host_stack_transfer_locate(transfer, &previous))
267 {
268 case UX_HOST_STACK_TRANSFER_AT_LIST_HEAD:
269
270 /* Unlink from pending transfer list head. */
271 _ux_system_host -> ux_system_host_pending_transfers =
272 transfer -> ux_transfer_request_next_pending;
273 break;
274 case UX_HOST_STACK_TRANSFER_IN_LIST:
275
276 /* Unlink from pending transfer list. */
277 previous -> ux_transfer_request_next_pending =
278 transfer -> ux_transfer_request_next_pending;
279 break;
280
281 default:
282 break;
283 }
284
285 /* Process transfer flags. */
286 flags = transfer -> ux_transfer_request_flags;
287 transfer -> ux_transfer_request_flags &=
288 ~(UX_TRANSFER_FLAG_LOCK | UX_TRANSFER_FLAG_AUTO_DEVICE_UNLOCK);
289 if (flags & UX_TRANSFER_FLAG_AUTO_DEVICE_UNLOCK)
290 {
291 transfer -> ux_transfer_request_endpoint ->
292 ux_endpoint_device -> ux_device_flags &= ~UX_DEVICE_FLAG_LOCK;
293 }
294 }
295
296
297 /**************************************************************************/
298 /* */
299 /* FUNCTION RELEASE */
300 /* */
301 /* _uxe_host_stack_transfer_run PORTABLE C */
302 /* 6.3.0 */
303 /* AUTHOR */
304 /* */
305 /* Chaoqiong Xiao, Microsoft Corporation */
306 /* */
307 /* DESCRIPTION */
308 /* */
309 /* This function checks errors in host stack transfer function call. */
310 /* */
311 /* INPUT */
312 /* */
313 /* transfer_request Pointer to transfer */
314 /* */
315 /* OUTPUT */
316 /* */
317 /* None */
318 /* */
319 /* CALLS */
320 /* */
321 /* _ux_host_stack_transfer_run Run a transfer request */
322 /* */
323 /* CALLED BY */
324 /* */
325 /* Application */
326 /* */
327 /* RELEASE HISTORY */
328 /* */
329 /* DATE NAME DESCRIPTION */
330 /* */
331 /* 10-31-2023 Chaoqiong Xiao Initial Version 6.3.0 */
332 /* */
333 /**************************************************************************/
_uxe_host_stack_transfer_run(UX_TRANSFER * transfer_request)334 UINT _uxe_host_stack_transfer_run(UX_TRANSFER *transfer_request)
335 {
336
337 /* Sanity checks. */
338 if (transfer_request == UX_NULL)
339 return(UX_INVALID_PARAMETER);
340 if (transfer_request -> ux_transfer_request_endpoint == UX_NULL)
341 return(UX_ENDPOINT_HANDLE_UNKNOWN);
342 if (transfer_request -> ux_transfer_request_endpoint -> ux_endpoint_device == UX_NULL)
343 return(UX_DEVICE_HANDLE_UNKNOWN);
344 if (UX_DEVICE_HCD_GET(transfer_request -> ux_transfer_request_endpoint -> ux_endpoint_device) == UX_NULL)
345 return(UX_INVALID_PARAMETER);
346
347 /* Invoke transfer request function. */
348 return(_ux_host_stack_transfer_run(transfer_request));
349 }
350 #endif
351