source: mainline/uspace/drv/bus/usb/ehci/ehci_batch.c@ e0a5d4c

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since e0a5d4c was e0a5d4c, checked in by Ondřej Hlavatý <aearsis@…>, 7 years ago

usb: update copyrights

The data was generated by a script, guided manually. If you feel your
name is missing somewhere, please add it!

The semi-automated process was roughly:

1) Changes per file and author (limited to our team) were counted
2) Trivial numbers were thrown away
3) Authors were sorted by lines added to file
4) All previous copyrights were replaced by the newly generated one
5) Hunks changing only year were discarded

It seems that a lot of my copyrights were added. It is due to me being
both sticking my nose everywhere and lazy to update the copyright right
away :)

  • Property mode set to 100644
File size: 12.4 KB
Line 
1/*
2 * Copyright (c) 2014 Jan Vesely
3 * Copyright (c) 2018 Ondrej Hlavaty
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29/** @addtogroup drvusbehci
30 * @{
31 */
32/** @file
33 * @brief EHCI driver USB transaction structure
34 */
35
36#include <assert.h>
37#include <errno.h>
38#include <macros.h>
39#include <mem.h>
40#include <stdbool.h>
41#include <str_error.h>
42
43#include <usb/usb.h>
44#include <usb/debug.h>
45
46#include "ehci_batch.h"
47#include "ehci_bus.h"
48
49/* The buffer pointer list in the qTD is long enough to support a maximum
50 * transfer size of 20K bytes. This case occurs when all five buffer pointers
51 * are used and the first offset is zero. A qTD handles a 16Kbyte buffer
52 * with any starting buffer alignment. EHCI specs p. 87 (pdf p. 97) */
53#define EHCI_TD_MAX_TRANSFER (16 * 1024)
54
55static void (*const batch_setup[])(ehci_transfer_batch_t*);
56
57/** Safely destructs ehci_transfer_batch_t structure
58 *
59 * @param[in] ehci_batch Instance to destroy.
60 */
61void ehci_transfer_batch_destroy(ehci_transfer_batch_t *ehci_batch)
62{
63 assert(ehci_batch);
64 dma_buffer_free(&ehci_batch->ehci_dma_buffer);
65 usb_log_debug2("Batch(%p): disposed", ehci_batch);
66 free(ehci_batch);
67}
68
69/** Allocate memory and initialize internal data structure.
70 *
71 * @param[in] usb_batch Pointer to generic USB batch structure.
72 * @return Valid pointer if all structures were successfully created,
73 * NULL otherwise.
74 *
75 */
76ehci_transfer_batch_t * ehci_transfer_batch_create(endpoint_t *ep)
77{
78 assert(ep);
79
80 ehci_transfer_batch_t *ehci_batch = calloc(1, sizeof(ehci_transfer_batch_t));
81 if (!ehci_batch) {
82 usb_log_error("Failed to allocate EHCI batch data.");
83 return NULL;
84 }
85
86 usb_transfer_batch_init(&ehci_batch->base, ep);
87
88 return ehci_batch;
89}
90
91/** Prepares a batch to be sent.
92 *
93 * Determines the number of needed transfer descriptors (TDs).
94 * Prepares a transport buffer (that is accessible by the hardware).
95 * Initializes parameters needed for the transfer and callback.
96 */
97int ehci_transfer_batch_prepare(ehci_transfer_batch_t *ehci_batch)
98{
99 assert(ehci_batch);
100
101 const size_t setup_size = (ehci_batch->base.ep->transfer_type == USB_TRANSFER_CONTROL)
102 ? USB_SETUP_PACKET_SIZE
103 : 0;
104
105 const size_t size = ehci_batch->base.size;
106
107 /* Add TD left over by the previous transfer */
108 ehci_batch->qh = ehci_endpoint_get(ehci_batch->base.ep)->qh;
109
110 /* Determine number of TDs needed */
111 ehci_batch->td_count = (size + EHCI_TD_MAX_TRANSFER - 1)
112 / EHCI_TD_MAX_TRANSFER;
113
114 /* Control transfer need Setup and Status stage */
115 if (ehci_batch->base.ep->transfer_type == USB_TRANSFER_CONTROL) {
116 ehci_batch->td_count += 2;
117 }
118
119 assert(ehci_batch->td_count > 0);
120
121 const size_t tds_size = ehci_batch->td_count * sizeof(td_t);
122
123 /* Mix setup stage and TDs together, we have enough space */
124 if (dma_buffer_alloc(&ehci_batch->ehci_dma_buffer, tds_size + setup_size)) {
125 usb_log_error("Batch %p: Failed to allocate device buffer",
126 ehci_batch);
127 return ENOMEM;
128 }
129
130 /* Clean TDs */
131 ehci_batch->tds = ehci_batch->ehci_dma_buffer.virt;
132 memset(ehci_batch->tds, 0, tds_size);
133
134 /* Copy setup data */
135 ehci_batch->setup_buffer = ehci_batch->ehci_dma_buffer.virt + tds_size;
136 memcpy(ehci_batch->setup_buffer, ehci_batch->base.setup.buffer, setup_size);
137
138 /* Generic data already prepared*/
139 ehci_batch->data_buffer = ehci_batch->base.dma_buffer.virt;
140
141 if (!batch_setup[ehci_batch->base.ep->transfer_type])
142 return ENOTSUP;
143
144 batch_setup[ehci_batch->base.ep->transfer_type](ehci_batch);
145
146 usb_log_debug("Batch %p %s " USB_TRANSFER_BATCH_FMT " initialized.",
147 ehci_batch, usb_str_direction(ehci_batch->base.dir),
148 USB_TRANSFER_BATCH_ARGS(ehci_batch->base));
149
150 return EOK;
151}
152
153/** Check batch TDs' status.
154 *
155 * @param[in] ehci_batch Batch structure to use.
156 * @return False, if there is an active TD, true otherwise.
157 *
158 * Walk all TDs (usually there is just one). Stop with false if there is an
159 * active TD. Stop with true if an error is found. Return true if the walk
160 * completes with the last TD.
161 */
162bool ehci_transfer_batch_check_completed(ehci_transfer_batch_t *ehci_batch)
163{
164 assert(ehci_batch);
165
166 usb_log_debug("Batch %p: checking %zu td(s) for completion.",
167 ehci_batch, ehci_batch->td_count);
168
169 usb_log_debug2("Batch %p: QH: %08x:%08x:%08x:%08x:%08x:%08x.",
170 ehci_batch,
171 ehci_batch->qh->ep_char, ehci_batch->qh->ep_cap,
172 ehci_batch->qh->status, ehci_batch->qh->current,
173 ehci_batch->qh->next, ehci_batch->qh->alternate);
174
175 if (!qh_halted(ehci_batch->qh) && (qh_transfer_pending(ehci_batch->qh)
176 || qh_transfer_active(ehci_batch->qh)))
177 return false;
178
179 /* Now we may be sure that either the ED is inactive because of errors
180 * or all transfer descriptors completed successfully */
181
182 /* Assume all data got through */
183 ehci_batch->base.transferred_size = ehci_batch->base.size;
184
185 /* Check all TDs */
186 for (size_t i = 0; i < ehci_batch->td_count; ++i) {
187 usb_log_debug("Batch %p: TD %zu: %08x:%08x:%08x.",
188 ehci_batch, i,
189 ehci_batch->tds[i].status, ehci_batch->tds[i].next,
190 ehci_batch->tds[i].alternate);
191
192 ehci_batch->base.error = td_error(&ehci_batch->tds[i]);
193 if (ehci_batch->base.error == EOK) {
194 /* If the TD got all its data through, it will report
195 * 0 bytes remain, the sole exception is INPUT with
196 * data rounding flag (short), i.e. every INPUT.
197 * Nice thing is that short packets will correctly
198 * report remaining data, thus making this computation
199 * correct (short packets need to be produced by the
200 * last TD)
201 * NOTE: This also works for CONTROL transfer as
202 * the first TD will return 0 remain.
203 * NOTE: Short packets don't break the assumption that
204 * we leave the very last(unused) TD behind.
205 */
206 ehci_batch->base.transferred_size
207 -= td_remain_size(&ehci_batch->tds[i]);
208 } else {
209 usb_log_debug("Batch %p found error TD(%zu):%08x: %s.",
210 ehci_batch, i,
211 ehci_batch->tds[i].status,
212 str_error_name(ehci_batch->base.error));
213 /* Clear possible ED HALT */
214 qh_clear_halt(ehci_batch->qh);
215 break;
216 }
217 }
218
219 assert(ehci_batch->base.transferred_size <= ehci_batch->base.size);
220
221 /* Clear TD pointers */
222 ehci_batch->qh->next = LINK_POINTER_TERM;
223 ehci_batch->qh->current = LINK_POINTER_TERM;
224 usb_log_debug("Batch %p complete: %s", ehci_batch,
225 str_error(ehci_batch->base.error));
226
227 return true;
228}
229
230/** Starts execution of the TD list
231 *
232 * @param[in] ehci_batch Batch structure to use
233 */
234void ehci_transfer_batch_commit(const ehci_transfer_batch_t *ehci_batch)
235{
236 assert(ehci_batch);
237 qh_set_next_td(ehci_batch->qh,
238 dma_buffer_phys(&ehci_batch->ehci_dma_buffer, &ehci_batch->tds[0]));
239}
240
241/** Prepare generic control transfer
242 *
243 * @param[in] ehci_batch Batch structure to use.
244 *
245 * Setup stage with toggle 0 and direction BOTH(SETUP_PID)
246 * Data stage with alternating toggle and direction
247 * Status stage with toggle 1 and direction
248 */
249static void batch_control(ehci_transfer_batch_t *ehci_batch)
250{
251 assert(ehci_batch);
252
253 usb_direction_t dir = ehci_batch->base.dir;
254 assert(dir == USB_DIRECTION_IN || dir == USB_DIRECTION_OUT);
255
256 usb_log_debug2("Batch %p: Control QH(%p): "
257 "%08x:%08x:%08x:%08x:%08x:%08x", ehci_batch,
258 ehci_batch->qh,
259 ehci_batch->qh->ep_char, ehci_batch->qh->ep_cap,
260 ehci_batch->qh->status, ehci_batch->qh->current,
261 ehci_batch->qh->next, ehci_batch->qh->alternate);
262 static const usb_direction_t reverse_dir[] = {
263 [USB_DIRECTION_IN] = USB_DIRECTION_OUT,
264 [USB_DIRECTION_OUT] = USB_DIRECTION_IN,
265 };
266
267 int toggle = 0;
268 const usb_direction_t data_dir = dir;
269 const usb_direction_t status_dir = reverse_dir[dir];
270
271 /* Setup stage */
272 td_init(&ehci_batch->tds[0],
273 dma_buffer_phys(&ehci_batch->ehci_dma_buffer, &ehci_batch->tds[1]),
274 dma_buffer_phys(&ehci_batch->ehci_dma_buffer, ehci_batch->setup_buffer),
275 USB_DIRECTION_BOTH, USB_SETUP_PACKET_SIZE, toggle, false);
276 usb_log_debug2("Batch %p: Created CONTROL SETUP TD(%"PRIxn"): "
277 "%08x:%08x:%08x", ehci_batch,
278 dma_buffer_phys(&ehci_batch->ehci_dma_buffer, &ehci_batch->tds[0]),
279 ehci_batch->tds[0].status, ehci_batch->tds[0].next,
280 ehci_batch->tds[0].alternate);
281
282 /* Data stage */
283 unsigned td_current = 1;
284 size_t remain_size = ehci_batch->base.size;
285 uintptr_t buffer = dma_buffer_phys(&ehci_batch->base.dma_buffer,
286 ehci_batch->data_buffer);
287 while (remain_size > 0) {
288 const size_t transfer_size = min(remain_size, EHCI_TD_MAX_TRANSFER);
289 toggle = 1 - toggle;
290
291 td_init(&ehci_batch->tds[td_current],
292 dma_buffer_phys(&ehci_batch->ehci_dma_buffer, &ehci_batch->tds[td_current + 1]),
293 buffer, data_dir, transfer_size, toggle, false);
294 usb_log_debug2("Batch %p: Created CONTROL DATA TD(%"PRIxn"): "
295 "%08x:%08x:%08x", ehci_batch,
296 dma_buffer_phys(&ehci_batch->ehci_dma_buffer, &ehci_batch->tds[td_current]),
297 ehci_batch->tds[td_current].status,
298 ehci_batch->tds[td_current].next,
299 ehci_batch->tds[td_current].alternate);
300
301 buffer += transfer_size;
302 remain_size -= transfer_size;
303 assert(td_current < ehci_batch->td_count - 1);
304 ++td_current;
305 }
306
307 /* Status stage */
308 assert(td_current == ehci_batch->td_count - 1);
309 td_init(&ehci_batch->tds[td_current], 0, 0, status_dir, 0, 1, true);
310 usb_log_debug2("Batch %p: Created CONTROL STATUS TD %d(%"PRIxn"): "
311 "%08x:%08x:%08x", ehci_batch, td_current,
312 dma_buffer_phys(&ehci_batch->ehci_dma_buffer, &ehci_batch->tds[td_current]),
313 ehci_batch->tds[td_current].status,
314 ehci_batch->tds[td_current].next,
315 ehci_batch->tds[td_current].alternate);
316}
317
318/** Prepare generic data transfer
319 *
320 * @param[in] ehci_batch Batch structure to use.
321 * @paramp[in] dir Communication direction.
322 *
323 * Direction is supplied by the associated ep and toggle is maintained by the
324 * EHCI hw in ED.
325 */
326static void batch_data(ehci_transfer_batch_t *ehci_batch)
327{
328 assert(ehci_batch);
329
330 usb_log_debug2("Batch %p: Data QH(%p): "
331 "%08x:%08x:%08x:%08x:%08x:%08x", ehci_batch,
332 ehci_batch->qh,
333 ehci_batch->qh->ep_char, ehci_batch->qh->ep_cap,
334 ehci_batch->qh->status, ehci_batch->qh->current,
335 ehci_batch->qh->next, ehci_batch->qh->alternate);
336
337 size_t td_current = 0;
338 size_t remain_size = ehci_batch->base.size;
339 uintptr_t buffer = dma_buffer_phys(&ehci_batch->base.dma_buffer,
340 ehci_batch->data_buffer);
341 while (remain_size > 0) {
342 const size_t transfer_size = remain_size > EHCI_TD_MAX_TRANSFER
343 ? EHCI_TD_MAX_TRANSFER : remain_size;
344
345 const bool last = (remain_size == transfer_size);
346 td_init(&ehci_batch->tds[td_current],
347 last ? 0 : dma_buffer_phys(&ehci_batch->ehci_dma_buffer,
348 &ehci_batch->tds[td_current + 1]),
349 buffer, ehci_batch->base.dir, transfer_size, -1, last);
350
351 usb_log_debug2("Batch %p: DATA TD(%"PRIxn": %08x:%08x:%08x",
352 ehci_batch,
353 dma_buffer_phys(&ehci_batch->ehci_dma_buffer,
354 &ehci_batch->tds[td_current]),
355 ehci_batch->tds[td_current].status,
356 ehci_batch->tds[td_current].next,
357 ehci_batch->tds[td_current].alternate);
358
359 buffer += transfer_size;
360 remain_size -= transfer_size;
361 assert(td_current < ehci_batch->td_count);
362 ++td_current;
363 }
364}
365
366/** Transfer setup table. */
367static void (*const batch_setup[])(ehci_transfer_batch_t*) =
368{
369 [USB_TRANSFER_CONTROL] = batch_control,
370 [USB_TRANSFER_BULK] = batch_data,
371 [USB_TRANSFER_INTERRUPT] = batch_data,
372 [USB_TRANSFER_ISOCHRONOUS] = NULL,
373};
374/**
375 * @}
376 */
377
Note: See TracBrowser for help on using the repository browser.