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_bulk_transfer PORTABLE C */
37 /* 6.1 */
38 /* AUTHOR */
39 /* */
40 /* Chaoqiong Xiao, Microsoft Corporation */
41 /* */
42 /* DESCRIPTION */
43 /* */
44 /* This function performs a bulk transfer request. A bulk transfer */
45 /* can be larger than the size of the OHCI buffer so it may be */
46 /* required to chain multiple tds to accommodate this transfer */
47 /* request. A bulk transfer is non blocking, so we return before the */
48 /* transfer request is completed. */
49 /* */
50 /* INPUT */
51 /* */
52 /* hcd_ohci Pointer to OHCI controller */
53 /* transfer_request Pointer to transfer request */
54 /* */
55 /* OUTPUT */
56 /* */
57 /* Completion Status */
58 /* */
59 /* CALLS */
60 /* */
61 /* _ux_hcd_ohci_register_read Read OHCI register */
62 /* _ux_hcd_ohci_register_write Write OHCI register */
63 /* _ux_hcd_ohci_regular_td_obtain Get regular TD */
64 /* _ux_utility_physical_address Get physical address */
65 /* _ux_utility_virtual_address Get virtual address */
66 /* */
67 /* CALLED BY */
68 /* */
69 /* OHCI Controller Driver */
70 /* */
71 /* RELEASE HISTORY */
72 /* */
73 /* DATE NAME DESCRIPTION */
74 /* */
75 /* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */
76 /* 09-30-2020 Chaoqiong Xiao Modified comment(s), */
77 /* resulting in version 6.1 */
78 /* */
79 /**************************************************************************/
_ux_hcd_ohci_request_bulk_transfer(UX_HCD_OHCI * hcd_ohci,UX_TRANSFER * transfer_request)80 UINT _ux_hcd_ohci_request_bulk_transfer(UX_HCD_OHCI *hcd_ohci, UX_TRANSFER *transfer_request)
81 {
82
83 UX_ENDPOINT *endpoint;
84 UX_OHCI_TD *data_td;
85 UX_OHCI_TD *start_data_td;
86 UX_OHCI_TD *next_data_td;
87 UX_OHCI_TD *previous_td;
88 UX_OHCI_TD *tail_td;
89 UX_OHCI_ED *ed;
90 ULONG transfer_request_payload_length;
91 ULONG bulk_packet_payload_length;
92 UCHAR * data_pointer;
93 ULONG ohci_register;
94 ULONG zlp_flag;
95
96
97 /* Get the pointer to the Endpoint. */
98 endpoint = (UX_ENDPOINT *) transfer_request -> ux_transfer_request_endpoint;
99
100 /* Now get the physical ED attached to this endpoint. */
101 ed = endpoint -> ux_endpoint_ed;
102
103 /* Use the TD pointer by ed -> tail for the first TD of this transfer
104 and chain from this one on. */
105 data_td = _ux_utility_virtual_address(ed -> ux_ohci_ed_tail_td);
106 previous_td = data_td;
107
108 /* Reset the first obtained data TD in case there is a TD shortage while building the list of tds. */
109 start_data_td = 0;
110
111 /* It may take more than one TD if the transfer_request length is more than the
112 maximum length for a OHCI TD (this is irrelevant of the MaxPacketSize value in
113 the endpoint descriptor). OHCI data payload has a maximum size of 4K. */
114 transfer_request_payload_length = transfer_request -> ux_transfer_request_requested_length;
115 data_pointer = transfer_request -> ux_transfer_request_data_pointer;
116
117 /* Check for ZLP condition. */
118 if (transfer_request_payload_length == 0)
119
120 /* We have a zlp condition. */
121 zlp_flag = UX_TRUE;
122 else
123
124 /* We do not have a zlp. */
125 zlp_flag = UX_FALSE;
126
127 /* Build all necessary TDs. */
128 while ((transfer_request_payload_length != 0) || zlp_flag == UX_TRUE)
129 {
130
131 /* Reset ZLP now. */
132 zlp_flag = UX_FALSE;
133
134 /* Check if we are exceeding the max payload. */
135 if (transfer_request_payload_length > UX_OHCI_MAX_PAYLOAD)
136
137 bulk_packet_payload_length = UX_OHCI_MAX_PAYLOAD;
138 else
139
140 bulk_packet_payload_length = transfer_request_payload_length;
141
142 /* IN transfer ? */
143 if ((transfer_request -> ux_transfer_request_type&UX_REQUEST_DIRECTION) == UX_REQUEST_IN)
144
145 data_td -> ux_ohci_td_dw0 = UX_OHCI_TD_IN | UX_OHCI_TD_DEFAULT_DW0;
146 else
147
148 data_td -> ux_ohci_td_dw0 = UX_OHCI_TD_OUT | UX_OHCI_TD_DEFAULT_DW0;
149
150 /* Store the beginning of the buffer address in the TD. */
151 data_td -> ux_ohci_td_cbp = _ux_utility_physical_address(data_pointer);
152
153 /* Store the end buffer address in the TD. */
154 data_td -> ux_ohci_td_be = data_td -> ux_ohci_td_cbp + bulk_packet_payload_length - 1;
155
156 /* Update the length of the transfer for this TD. */
157 data_td -> ux_ohci_td_length = bulk_packet_payload_length;
158
159 /* Attach the endpoint and transfer request to the TD. */
160 data_td -> ux_ohci_td_transfer_request = transfer_request;
161 data_td -> ux_ohci_td_ed = ed;
162
163 /* Adjust the data payload length and the data payload pointer. */
164 transfer_request_payload_length -= bulk_packet_payload_length;
165 data_pointer += bulk_packet_payload_length;
166
167 /* Check if there will be another transaction. */
168 if (transfer_request_payload_length != 0)
169 {
170
171 /* Get a new TD to hook this payload. */
172 data_td = _ux_hcd_ohci_regular_td_obtain(hcd_ohci);
173
174 if (data_td == UX_NULL)
175 {
176
177 /* If there was already a TD chain in progress, free it. */
178 if (start_data_td != UX_NULL)
179 {
180
181 data_td = start_data_td;
182 while(data_td)
183 {
184
185 next_data_td = _ux_utility_virtual_address(data_td -> ux_ohci_td_next_td);
186 data_td -> ux_ohci_td_status = UX_UNUSED;
187 data_td = next_data_td;
188 }
189 }
190
191 return(UX_NO_TD_AVAILABLE);
192 }
193
194 /* the first obtained TD in the chain has to be remembered. */
195 if (start_data_td == UX_NULL)
196 start_data_td = data_td;
197
198 /* Attach this new TD to the previous one. */
199 previous_td -> ux_ohci_td_next_td = _ux_utility_physical_address(data_td);
200 previous_td -> ux_ohci_td_next_td_transfer_request = data_td;
201 previous_td = data_td;
202 }
203 }
204
205 /* At this stage, the Head and Tail in the ED are still the same and the OHCI controller
206 will skip this ED until we have hooked the new tail TD. */
207 tail_td = _ux_hcd_ohci_regular_td_obtain(hcd_ohci);
208 if (tail_td == UX_NULL)
209 {
210
211 /* If there was already a TD chain in progress, free it. */
212 if (start_data_td != UX_NULL)
213 {
214
215 data_td = start_data_td;
216 while(data_td)
217 {
218
219 next_data_td = _ux_utility_virtual_address(data_td -> ux_ohci_td_next_td);
220 data_td -> ux_ohci_td_status = UX_UNUSED;
221 data_td = next_data_td;
222 }
223 }
224
225 return(UX_NO_TD_AVAILABLE);
226 }
227
228 /* Attach the tail TD to the last data TD. */
229 data_td -> ux_ohci_td_next_td = _ux_utility_physical_address(tail_td);
230
231 /* Store the new tail TD. */
232 ed -> ux_ohci_ed_tail_td = _ux_utility_physical_address(tail_td);
233
234 /* Now, we must tell the OHCI controller that there is something in the
235 bulk queue. */
236 ohci_register = _ux_hcd_ohci_register_read(hcd_ohci, OHCI_HC_COMMAND_STATUS);
237 ohci_register |= OHCI_HC_CS_BLF;
238 _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_COMMAND_STATUS, ohci_register);
239
240 /* Return successful completion. */
241 return(UX_SUCCESS);
242 }
243
244