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