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 /** Host Simulator Controller Driver */
18 /** */
19 /**************************************************************************/
20 /**************************************************************************/
21
22 #define UX_SOURCE_CODE
23
24
25 /* Include necessary system files. */
26
27 #include "ux_api.h"
28 #include "ux_hcd_sim_host.h"
29
30
31 /**************************************************************************/
32 /* */
33 /* FUNCTION RELEASE */
34 /* */
35 /* _ux_hcd_sim_host_request_bulk_transfer PORTABLE C */
36 /* 6.1.3 */
37 /* AUTHOR */
38 /* */
39 /* Chaoqiong Xiao, Microsoft Corporation */
40 /* */
41 /* DESCRIPTION */
42 /* */
43 /* This function performs a bulk transfer request. A bulk transfer */
44 /* can be larger than the size of the sim_host buffer so it may be */
45 /* required to chain multiple TDs to accommodate this transfer */
46 /* request. A bulk transfer is non blocking, so we return before the */
47 /* transfer request is completed. */
48 /* */
49 /* INPUT */
50 /* */
51 /* hcd_sim_host Pointer to host controller */
52 /* transfer_request Pointer to transfer request */
53 /* */
54 /* OUTPUT */
55 /* */
56 /* Completion Status */
57 /* */
58 /* CALLS */
59 /* */
60 /* _ux_hcd_sim_host_regular_td_obtain Obtain regular TD */
61 /* */
62 /* CALLED BY */
63 /* */
64 /* Host Simulator Controller Driver */
65 /* */
66 /* RELEASE HISTORY */
67 /* */
68 /* DATE NAME DESCRIPTION */
69 /* */
70 /* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */
71 /* 09-30-2020 Chaoqiong Xiao Modified comment(s), */
72 /* resulting in version 6.1 */
73 /* 12-31-2020 Chaoqiong Xiao Modified comment(s), */
74 /* fixed ZLP sending, */
75 /* resulting in version 6.1.3 */
76 /* */
77 /**************************************************************************/
_ux_hcd_sim_host_request_bulk_transfer(UX_HCD_SIM_HOST * hcd_sim_host,UX_TRANSFER * transfer_request)78 UINT _ux_hcd_sim_host_request_bulk_transfer(UX_HCD_SIM_HOST *hcd_sim_host, UX_TRANSFER *transfer_request)
79 {
80
81 UX_ENDPOINT *endpoint;
82 UX_HCD_SIM_HOST_TD *data_td;
83 UX_HCD_SIM_HOST_TD *start_data_td;
84 UX_HCD_SIM_HOST_TD *next_data_td;
85 UX_HCD_SIM_HOST_TD *previous_td;
86 UX_HCD_SIM_HOST_TD *tail_td;
87 UX_HCD_SIM_HOST_ED *ed;
88 ULONG transfer_request_payload_length;
89 ULONG bulk_packet_payload_length;
90 UCHAR * data_pointer;
91
92
93 /* Get the pointer to the Endpoint. */
94 endpoint = (UX_ENDPOINT *) transfer_request -> ux_transfer_request_endpoint;
95
96 /* Now get the physical ED attached to this endpoint. */
97 ed = endpoint -> ux_endpoint_ed;
98
99 /* Use the TD pointer by ed -> tail for the first TD of this transfer
100 and chain from this one on. */
101 data_td = ed -> ux_sim_host_ed_tail_td;
102 previous_td = data_td;
103
104 /* Reset the first obtained data TD in case there is a TD shortage while building
105 the list of TDs. */
106 start_data_td = 0;
107
108 /* It may take more than one TD if the transfer_request length is more than the
109 maximum length for a host simulator TD (this is irrelevant of the MaxPacketSize value
110 in the endpoint descriptor). Host simulator data payload has a maximum size of 4K. */
111 transfer_request_payload_length = transfer_request -> ux_transfer_request_requested_length;
112 data_pointer = transfer_request -> ux_transfer_request_data_pointer;
113
114 do
115 {
116
117 if (transfer_request_payload_length > UX_HCD_SIM_HOST_MAX_PAYLOAD)
118
119 bulk_packet_payload_length = UX_HCD_SIM_HOST_MAX_PAYLOAD;
120 else
121
122 bulk_packet_payload_length = transfer_request_payload_length;
123
124 /* Store the beginning of the buffer address in the TD. */
125 data_td -> ux_sim_host_td_buffer = data_pointer;
126
127 /* Update the length of the transfer for this TD. */
128 data_td -> ux_sim_host_td_length = bulk_packet_payload_length;
129
130 /* Attach the endpoint and transfer_request to the TD. */
131 data_td -> ux_sim_host_td_transfer_request = transfer_request;
132 data_td -> ux_sim_host_td_ed = ed;
133
134 /* Adjust the data payload length and the data payload pointer. */
135 transfer_request_payload_length -= bulk_packet_payload_length;
136 data_pointer += bulk_packet_payload_length;
137
138 /* The direction of the transaction is set in the TD. */
139 if ((transfer_request -> ux_transfer_request_type & UX_REQUEST_DIRECTION) == UX_REQUEST_IN)
140
141 data_td -> ux_sim_host_td_direction = UX_HCD_SIM_HOST_TD_IN;
142 else
143 data_td -> ux_sim_host_td_direction = UX_HCD_SIM_HOST_TD_OUT;
144
145 /* Mark the TD with the DATA phase. */
146 data_td -> ux_sim_host_td_status |= UX_HCD_SIM_HOST_TD_DATA_PHASE;
147
148 /* The Toggle value is in the ED. */
149 data_td -> ux_sim_host_td_toggle = UX_HCD_SIM_HOST_TD_TOGGLE_FROM_ED;
150
151 /* Check if there will be another transaction. */
152 if (transfer_request_payload_length != 0)
153 {
154
155 /* Get a new TD to hook this payload. */
156 data_td = _ux_hcd_sim_host_regular_td_obtain(hcd_sim_host);
157 if (data_td == UX_NULL)
158 {
159
160 /* If there was already a TD chain in progress, free it. */
161 if (start_data_td != UX_NULL)
162 {
163
164 data_td = start_data_td;
165 while (data_td)
166 {
167
168 next_data_td = data_td -> ux_sim_host_td_next_td;
169 data_td -> ux_sim_host_td_status = UX_UNUSED;
170 data_td = next_data_td;
171 }
172 }
173
174 return(UX_NO_TD_AVAILABLE);
175 }
176
177 /* the first obtained TD in the chain has to be remembered. */
178 if (start_data_td == UX_NULL)
179 start_data_td = data_td;
180
181 /* Attach this new TD to the previous one. */
182 previous_td -> ux_sim_host_td_next_td = data_td;
183 previous_td -> ux_sim_host_td_next_td_transfer_request = data_td;
184 previous_td = data_td;
185 }
186 } while (transfer_request_payload_length != 0);
187
188 /* At this stage, the Head and Tail in the ED are still the same and the host simulator
189 controller will skip this ED until we have hooked the new tail TD. */
190 tail_td = _ux_hcd_sim_host_regular_td_obtain(hcd_sim_host);
191 if (tail_td == UX_NULL)
192 {
193
194 /* If there was already a TD chain in progress, free it. */
195 if (start_data_td != UX_NULL)
196 {
197
198 data_td = start_data_td;
199 while (data_td)
200 {
201
202 next_data_td = data_td -> ux_sim_host_td_next_td;
203 data_td -> ux_sim_host_td_status = UX_UNUSED;
204 data_td = next_data_td;
205 }
206 }
207
208 return(UX_NO_TD_AVAILABLE);
209 }
210
211 /* Attach the tail TD to the last data TD. */
212 data_td -> ux_sim_host_td_next_td = tail_td;
213
214 /* Store the new tail TD. */
215 ed -> ux_sim_host_ed_tail_td = tail_td;
216
217 /* Now we can tell the scheduler to wake up. */
218 hcd_sim_host -> ux_hcd_sim_host_queue_empty = UX_FALSE;
219
220 /* Return successful completion. */
221 return(UX_SUCCESS);
222 }
223
224