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 /** EHCI Controller Driver */
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_hcd_ehci.h"
29 #include "ux_host_stack.h"
30
31
32 /**************************************************************************/
33 /* */
34 /* FUNCTION RELEASE */
35 /* */
36 /* _ux_hcd_ehci_request_control_transfer PORTABLE C */
37 /* 6.3.0 */
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 EHCI endpoint as a candidate for transfer. */
48 /* */
49 /* The max aggregated size of a data payload in EHCI is 16K. We are */
50 /* assuming that this size will be sufficient to contain the control */
51 /* packet. */
52 /* */
53 /* INPUT */
54 /* */
55 /* hcd_ehci Pointer to EHCI controller */
56 /* transfer_request Pointer to transfer request */
57 /* */
58 /* OUTPUT */
59 /* */
60 /* Completion Status */
61 /* */
62 /* CALLS */
63 /* */
64 /* _ux_hcd_ehci_ed_clean Clean TDs */
65 /* _ux_hcd_ehci_request_transfer_add Add transfer to ED */
66 /* _ux_host_stack_transfer_request_abort Abort transfer request */
67 /* _ux_utility_memory_allocate Allocate memory block */
68 /* _ux_utility_memory_free Release memory block */
69 /* _ux_host_semaphore_get Get semaphore */
70 /* _ux_utility_short_put Write a 16-bit value */
71 /* */
72 /* CALLED BY */
73 /* */
74 /* EHCI Controller Driver */
75 /* */
76 /* RELEASE HISTORY */
77 /* */
78 /* DATE NAME DESCRIPTION */
79 /* */
80 /* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */
81 /* 09-30-2020 Chaoqiong Xiao Modified comment(s), */
82 /* prefixed UX to MS_TO_TICK, */
83 /* resulting in version 6.1 */
84 /* 11-09-2020 Chaoqiong Xiao Modified comment(s), */
85 /* refined macros names, */
86 /* fixed compile warnings, */
87 /* resulting in version 6.1.2 */
88 /* 10-31-2023 Chaoqiong Xiao Modified comment(s), */
89 /* fixed compile warnings, */
90 /* resulting in version 6.3.0 */
91 /* */
92 /**************************************************************************/
_ux_hcd_ehci_request_control_transfer(UX_HCD_EHCI * hcd_ehci,UX_TRANSFER * transfer_request)93 UINT _ux_hcd_ehci_request_control_transfer(UX_HCD_EHCI *hcd_ehci, UX_TRANSFER *transfer_request)
94 {
95
96 UX_DEVICE *device;
97 UX_ENDPOINT *endpoint;
98 UCHAR * setup_request;
99 UX_EHCI_ED *ed;
100 ULONG td_component;
101 UINT status;
102 UINT pid;
103
104
105 /* Get the pointer to the Endpoint and to the device. */
106 endpoint = (UX_ENDPOINT *) transfer_request -> ux_transfer_request_endpoint;
107 device = endpoint -> ux_endpoint_device;
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_CACHE_SAFE_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 /* Reset the last TD pointer since it is the first time we hook a transaction. */
125 ed -> ux_ehci_ed_last_td = UX_NULL;
126
127 /* Set the ED address and MPS values since they may have changed from 0 to x
128 The ED direction will be set from the TD. */
129 ed -> ux_ehci_ed_cap0 |= (endpoint -> ux_endpoint_descriptor.bEndpointAddress & ~UX_ENDPOINT_DIRECTION) << UX_EHCI_QH_ED_AD_LOC;
130
131 /* Set the endpoint address (this should have changed after address setting). */
132 ed -> ux_ehci_ed_cap0 |= device -> ux_device_address;
133
134 /* Set the default MPS Capability info in the ED. */
135 ed -> ux_ehci_ed_cap0 &= ~UX_EHCI_QH_MPS_MASK;
136 ed -> ux_ehci_ed_cap0 |= (ULONG)endpoint -> ux_endpoint_descriptor.wMaxPacketSize << UX_EHCI_QH_MPS_LOC;
137
138 /* On Control transfers, the toggle is set in the TD, not the QH. */
139 ed -> ux_ehci_ed_cap0 |= UX_EHCI_QH_DTC;
140
141 /* The overlay parameters should be reset now. */
142 ed -> ux_ehci_ed_current_td = UX_NULL;
143 ed -> ux_ehci_ed_queue_element = (UX_EHCI_TD *) UX_EHCI_TD_T;
144 ed -> ux_ehci_ed_alternate_td = (UX_EHCI_TD *) UX_EHCI_TD_T;
145 ed -> ux_ehci_ed_state &= UX_EHCI_QH_TOGGLE;
146 ed -> ux_ehci_ed_bp0 = UX_NULL;
147 ed -> ux_ehci_ed_bp1 = UX_NULL;
148 ed -> ux_ehci_ed_bp2 = UX_NULL;
149 ed -> ux_ehci_ed_bp3 = UX_NULL;
150 ed -> ux_ehci_ed_bp4 = UX_NULL;
151
152 /* Build and hook the setup phase to the ED. */
153 status = _ux_hcd_ehci_request_transfer_add(hcd_ehci, ed, UX_EHCI_TD_SETUP_PHASE, UX_EHCI_PID_SETUP, UX_EHCI_TOGGLE_0,
154 setup_request, UX_SETUP_SIZE, transfer_request);
155 if (status != UX_SUCCESS)
156 {
157
158 /* We need to clean the tds attached if any. */
159 _ux_hcd_ehci_ed_clean(ed);
160 return(status);
161 }
162
163 /* Test if data phase required, if so decide the PID to use and build/hook it to the ED. */
164 if (transfer_request -> ux_transfer_request_requested_length != 0)
165 {
166
167 if ((transfer_request -> ux_transfer_request_type & UX_REQUEST_DIRECTION) == UX_REQUEST_IN)
168
169 pid = UX_EHCI_PID_IN;
170 else
171
172 pid = UX_EHCI_PID_OUT;
173
174 status = _ux_hcd_ehci_request_transfer_add(hcd_ehci, ed, UX_EHCI_TD_DATA_PHASE, pid, UX_EHCI_TOGGLE_1,
175 transfer_request -> ux_transfer_request_data_pointer,
176 transfer_request -> ux_transfer_request_requested_length,
177 transfer_request);
178 if (status != UX_SUCCESS)
179 {
180
181 /* We need to clean the tds attached if any. */
182 _ux_hcd_ehci_ed_clean(ed);
183 return(status);
184 }
185 }
186
187 /* Program the status phase. the PID is the opposite of the data phase. */
188 if ((transfer_request -> ux_transfer_request_type & UX_REQUEST_DIRECTION) == UX_REQUEST_IN)
189 pid = UX_EHCI_PID_OUT;
190 else
191 pid = UX_EHCI_PID_IN;
192
193 status = _ux_hcd_ehci_request_transfer_add(hcd_ehci, ed, UX_EHCI_TD_STATUS_PHASE, pid,
194 UX_EHCI_TOGGLE_1, UX_NULL, 0, transfer_request);
195
196 if (status != UX_SUCCESS)
197 {
198
199 /* We need to clean the tds attached if any. */
200 _ux_hcd_ehci_ed_clean(ed);
201 return(status);
202 }
203
204 /* Set the IOC bit in the last TD. */
205 ed -> ux_ehci_ed_last_td -> ux_ehci_td_control |= UX_EHCI_TD_IOC;
206
207 /* Ensure the IOC bit is set before activating the TD. This is necessary
208 for some processors that perform writes out of order as an optimization. */
209 UX_DATA_MEMORY_BARRIER
210
211 /* Activate the first TD linked to the ED. */
212 td_component = (ULONG) ed -> ux_ehci_ed_queue_element;
213 td_component &= ~UX_EHCI_TD_T;
214 ed -> ux_ehci_ed_queue_element = (UX_EHCI_TD *) td_component;
215
216 /* Wait for the completion of the transfer request. */
217 status = _ux_host_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, UX_MS_TO_TICK(UX_CONTROL_TRANSFER_TIMEOUT));
218
219 /* If the semaphore did not succeed we probably have a time out. */
220 if (status != UX_SUCCESS)
221 {
222
223 /* All transfers pending need to abort. There may have been a partial transfer. */
224 _ux_host_stack_transfer_request_abort(transfer_request);
225
226 /* There was an error, return to the caller. */
227 transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_TIMEOUT;
228
229 /* Error trap. */
230 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_TRANSFER_TIMEOUT);
231
232 /* If trace is enabled, insert this event into the trace buffer. */
233 UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_TIMEOUT, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0)
234
235 }
236
237 /* Free the resources. */
238 _ux_utility_memory_free(setup_request);
239
240 /* Return completion status. */
241 return(transfer_request -> ux_transfer_request_completion_code);
242 }
243
244