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 /** OHCI 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_ohci.h"
29 #include "ux_host_stack.h"
30
31
32 /**************************************************************************/
33 /* */
34 /* FUNCTION RELEASE */
35 /* */
36 /* _ux_hcd_ohci_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 OHCI endpoint as a candidate for transfer. */
48 /* */
49 /* The maximum aggregated size of a data payload in OHCI is 4K. We */
50 /* are assuming that this size will be sufficient to contain the */
51 /* control packet. */
52 /* */
53 /* INPUT */
54 /* */
55 /* hcd_ohci Pointer to OHCI controller */
56 /* transfer_request Pointer to transfer request */
57 /* */
58 /* OUTPUT */
59 /* */
60 /* Completion Status */
61 /* */
62 /* CALLS */
63 /* */
64 /* _ux_hcd_ohci_register_read Read OHCI register */
65 /* _ux_hcd_ohci_register_write Write OHCI register */
66 /* _ux_hcd_ohci_regular_td_obtain Get regular TD */
67 /* _ux_host_stack_transfer_request_abort Abort transfer request */
68 /* _ux_utility_memory_allocate Allocate memory block */
69 /* _ux_utility_memory_free Release memory block */
70 /* _ux_utility_physical_address Get physical address */
71 /* _ux_host_semaphore_get Get semaphore */
72 /* _ux_utility_short_put Write 16-bit value */
73 /* _ux_utility_virtual_address Get virtual address */
74 /* */
75 /* CALLED BY */
76 /* */
77 /* OHCI Controller Driver */
78 /* */
79 /* RELEASE HISTORY */
80 /* */
81 /* DATE NAME DESCRIPTION */
82 /* */
83 /* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */
84 /* 09-30-2020 Chaoqiong Xiao Modified comment(s), */
85 /* prefixed UX to MS_TO_TICK, */
86 /* resulting in version 6.1 */
87 /* 11-09-2020 Chaoqiong Xiao Modified comment(s), */
88 /* fixed compile warnings, */
89 /* resulting in version 6.1.2 */
90 /* 01-31-2022 Chaoqiong Xiao Modified comment(s), */
91 /* refined macros names, */
92 /* resulting in version 6.1.10 */
93 /* */
94 /**************************************************************************/
_ux_hcd_ohci_request_control_transfer(UX_HCD_OHCI * hcd_ohci,UX_TRANSFER * transfer_request)95 UINT _ux_hcd_ohci_request_control_transfer(UX_HCD_OHCI *hcd_ohci, UX_TRANSFER *transfer_request)
96 {
97
98 UX_DEVICE *device;
99 UX_ENDPOINT *endpoint;
100 UCHAR * setup_request;
101 UX_OHCI_ED *ed;
102 UX_OHCI_TD *setup_td;
103 UX_OHCI_TD *chain_td;
104 UX_OHCI_TD *data_td;
105 UX_OHCI_TD *tail_td;
106 UX_OHCI_TD *status_td;
107 ULONG ohci_register;
108 UINT status;
109
110
111 /* Get the pointer to the Endpoint and the Device. */
112 endpoint = (UX_ENDPOINT *) transfer_request -> ux_transfer_request_endpoint;
113 device = endpoint -> ux_endpoint_device;
114
115 /* Now get the physical ED attached to this endpoint. */
116 ed = endpoint -> ux_endpoint_ed;
117
118 /* Build the SETUP packet (phase 1 of the control transfer). */
119 setup_request = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_CACHE_SAFE_MEMORY, UX_SETUP_SIZE);
120 if (setup_request == UX_NULL)
121 return(UX_MEMORY_INSUFFICIENT);
122
123 *setup_request = (UCHAR)transfer_request -> ux_transfer_request_function;
124 *(setup_request + UX_SETUP_REQUEST_TYPE) = (UCHAR)transfer_request -> ux_transfer_request_type;
125 *(setup_request + UX_SETUP_REQUEST) = (UCHAR)transfer_request -> ux_transfer_request_function;
126 _ux_utility_short_put(setup_request + UX_SETUP_VALUE, (USHORT)transfer_request -> ux_transfer_request_value);
127 _ux_utility_short_put(setup_request + UX_SETUP_INDEX, (USHORT)transfer_request -> ux_transfer_request_index);
128 _ux_utility_short_put(setup_request + UX_SETUP_LENGTH, (USHORT) transfer_request -> ux_transfer_request_requested_length);
129
130 /* Set the ED address and MPS values since they may have changed.
131 The ED direction will be set from the TD. */
132 ed -> ux_ohci_ed_dw0 = device -> ux_device_address | ((ULONG) endpoint -> ux_endpoint_descriptor.bEndpointAddress << 7) |
133 ((ULONG) endpoint -> ux_endpoint_descriptor.wMaxPacketSize << 16);
134
135 /* Refresh the speed. */
136 if (device -> ux_device_speed == UX_LOW_SPEED_DEVICE)
137 ed -> ux_ohci_ed_dw0 |= UX_OHCI_ED_LOW_SPEED;
138
139 /* Use the TD pointer by ed -> tail for our setup TD and chain from this one on. */
140 setup_td = _ux_utility_virtual_address(ed -> ux_ohci_ed_tail_td);
141 setup_td -> ux_ohci_td_dw0 = UX_OHCI_TD_DEFAULT_DW0 | UX_OHCI_TD_DATA0 | UX_OHCI_TD_R;
142 setup_td -> ux_ohci_td_cbp = _ux_utility_physical_address(setup_request);
143 setup_td -> ux_ohci_td_be = setup_td -> ux_ohci_td_cbp + UX_SETUP_SIZE - 1;
144 chain_td = setup_td;
145
146 /* Attach the endpoint and transfer request to the TD. */
147 setup_td -> ux_ohci_td_transfer_request = transfer_request;
148 setup_td -> ux_ohci_td_ed = ed;
149
150 /* Mark the TD with the SETUP phase. */
151 setup_td -> ux_ohci_td_status |= UX_OHCI_TD_SETUP_PHASE;
152
153 /* Check if there is a data phase, if not jump to status phase. */
154 data_td = UX_NULL;
155 if (transfer_request -> ux_transfer_request_requested_length != 0)
156 {
157
158 data_td = _ux_hcd_ohci_regular_td_obtain(hcd_ohci);
159 if (data_td == UX_NULL)
160 {
161
162 _ux_utility_memory_free(setup_request);
163 return(UX_NO_TD_AVAILABLE);
164 }
165
166 /* Attach the endpoint and transfer request to the TD. */
167 data_td -> ux_ohci_td_transfer_request = transfer_request;
168 data_td -> ux_ohci_td_ed = ed;
169
170 /* Mark the TD with the DATA phase. */
171 data_td -> ux_ohci_td_status |= UX_OHCI_TD_DATA_PHASE;
172
173 /* Program the control bits of the TD. */
174 if ((transfer_request -> ux_transfer_request_type & UX_REQUEST_DIRECTION) == UX_REQUEST_IN)
175
176 data_td -> ux_ohci_td_dw0 = UX_OHCI_TD_DEFAULT_DW0 | UX_OHCI_TD_IN | UX_OHCI_TD_DATA1 | UX_OHCI_TD_R;
177 else
178
179 data_td -> ux_ohci_td_dw0 = UX_OHCI_TD_DEFAULT_DW0 | UX_OHCI_TD_OUT | UX_OHCI_TD_DATA1 | UX_OHCI_TD_R;
180
181 /* Attach the CBP and BE values to the TD. */
182 data_td -> ux_ohci_td_cbp = _ux_utility_physical_address(transfer_request -> ux_transfer_request_data_pointer);
183 data_td -> ux_ohci_td_be = data_td -> ux_ohci_td_cbp + transfer_request -> ux_transfer_request_requested_length - 1;
184
185 /* Update the length of the transfer for this TD. */
186 data_td -> ux_ohci_td_length = transfer_request -> ux_transfer_request_requested_length;
187
188 /* Chain the TD. */
189 chain_td -> ux_ohci_td_next_td = _ux_utility_physical_address(data_td);
190 chain_td = data_td;
191 }
192
193 /* Now, program the status phase. */
194 status_td = _ux_hcd_ohci_regular_td_obtain(hcd_ohci);
195 if (status_td == UX_NULL)
196 {
197
198 _ux_utility_memory_free(setup_request);
199 if (data_td != UX_NULL)
200 data_td -> ux_ohci_td_status = UX_UNUSED;
201 return(UX_NO_TD_AVAILABLE);
202 }
203
204 /* Attach the endpoint and transfer request to the TD. */
205 status_td -> ux_ohci_td_transfer_request = transfer_request;
206 status_td -> ux_ohci_td_ed = ed;
207
208 /* Mark the TD with the STATUS phase. */
209 status_td -> ux_ohci_td_status |= UX_OHCI_TD_STATUS_PHASE;
210
211 /* The direction of the status phase is IN if data phase is OUT and
212 vice versa. */
213 if ((transfer_request -> ux_transfer_request_type & UX_REQUEST_DIRECTION) == UX_REQUEST_IN)
214
215 status_td -> ux_ohci_td_dw0 = UX_OHCI_TD_DEFAULT_DW0 | UX_OHCI_TD_OUT | UX_OHCI_TD_DATA1;
216 else
217
218 status_td -> ux_ohci_td_dw0 = UX_OHCI_TD_DEFAULT_DW0 | UX_OHCI_TD_IN | UX_OHCI_TD_DATA1;
219
220 /* No data payload for the status phase. */
221 status_td -> ux_ohci_td_cbp = 0;
222 status_td -> ux_ohci_td_be = 0;
223
224 /* Hook the status phase to the previous TD. */
225 chain_td -> ux_ohci_td_next_td = _ux_utility_physical_address(status_td);
226
227 /* Since we have consumed out tail TD for the setup packet, we must get another
228 one and hook it to the ED's tail. */
229 tail_td = _ux_hcd_ohci_regular_td_obtain(hcd_ohci);
230 if (tail_td == UX_NULL)
231 {
232
233 _ux_utility_memory_free(setup_request);
234 if (data_td != UX_NULL)
235 data_td -> ux_ohci_td_status = UX_UNUSED;
236 status_td -> ux_ohci_td_status = UX_UNUSED;
237 return(UX_NO_TD_AVAILABLE);
238 }
239
240 /* Hook the new TD to the status TD. */
241 status_td -> ux_ohci_td_next_td = _ux_utility_physical_address(tail_td);
242
243 /* At this stage, the Head and Tail in the ED are still the same and
244 the OHCI controller will skip this ED until we have hooked the new
245 tail TD. */
246 ed -> ux_ohci_ed_tail_td = _ux_utility_physical_address(tail_td);
247
248 /* Now, we must tell the OHCI controller that there is something in the
249 control queue. */
250 ohci_register = _ux_hcd_ohci_register_read(hcd_ohci, OHCI_HC_COMMAND_STATUS);
251 ohci_register |= OHCI_HC_CS_CLF;
252 _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_COMMAND_STATUS, ohci_register);
253
254 /* Wait for the completion of the transfer request. */
255 status = _ux_host_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, UX_MS_TO_TICK(UX_CONTROL_TRANSFER_TIMEOUT));
256
257 /* If the semaphore did not succeed we probably have a time out. */
258 if (status != UX_SUCCESS)
259 {
260
261 /* All transfers pending need to abort. There may have been a partial transfer. */
262 _ux_host_stack_transfer_request_abort(transfer_request);
263
264 /* There was an error, return to the caller. */
265 transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_TIMEOUT;
266
267 /* Error trap. */
268 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_TRANSFER_TIMEOUT);
269
270 /* If trace is enabled, insert this event into the trace buffer. */
271 UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_TIMEOUT, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0)
272
273 }
274
275 /* Free the resources. */
276 _ux_utility_memory_free(setup_request);
277
278 /* Return the completion status. */
279 return(transfer_request -> ux_transfer_request_completion_code);
280 }
281
282