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