source: mainline/uspace/drv/bus/usb/xhci/endpoint.c@ 708d8fcd

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

xhci: rewritten isochronous transfers

There was a fundamental problem with relying on hardware to send
RING_OVERRUN/UNDERRUN events, which QEMU (and possibly others) do not
send. That resulted in not knowing if the transfer is still on schedule,
and having to ring the doorbell every time. That is not feasible,
because then the transfer can be more frequent than it should be.
Furthermore, it ignored the fact that isochronous TRBs are to be
scheduled not too late, but also not too soon (see 4.11.2.5 of the xHCI
spec).

Now, scheduling the TRBs to hardware is called feeding, and can be
delayed by setting a timer. Ring overruns/underruns are detected also at
the end of handling an event.

  • Property mode set to 100644
File size: 14.6 KB
Line 
1/*
2 * Copyright (c) 2017 Petr Manek
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup drvusbxhci
30 * @{
31 */
32/** @file
33 * @brief The host controller endpoint management.
34 */
35
36#include <usb/host/endpoint.h>
37#include <usb/descriptor.h>
38
39#include <errno.h>
40#include <macros.h>
41
42#include "hc.h"
43#include "bus.h"
44#include "commands.h"
45#include "endpoint.h"
46
47static int alloc_transfer_ds(xhci_endpoint_t *);
48static void free_transfer_ds(xhci_endpoint_t *);
49
50/**
51 * Initialize new XHCI endpoint.
52 * @param[in] xhci_ep Allocated XHCI endpoint to initialize.
53 * @param[in] dev Device, to which the endpoint belongs.
54 * @param[in] desc USB endpoint descriptor carrying configuration data.
55 *
56 * @return Error code.
57 */
58int xhci_endpoint_init(xhci_endpoint_t *xhci_ep, device_t *dev, const usb_endpoint_descriptors_t *desc)
59{
60 int rc;
61 assert(xhci_ep);
62
63 endpoint_t *ep = &xhci_ep->base;
64
65 endpoint_init(ep, dev, desc);
66
67 xhci_ep->max_streams = 1 << (USB_SSC_MAX_STREAMS(desc->companion));
68 xhci_ep->max_burst = desc->companion.max_burst + 1;
69 xhci_ep->mult = USB_SSC_MULT(desc->companion) + 1;
70
71 /* In USB 3, the semantics of wMaxPacketSize changed. Now the number of
72 * packets per service interval is determined from max_burst and mult.
73 */
74 if (dev->speed >= USB_SPEED_SUPER) {
75 ep->packets_per_uframe = xhci_ep->max_burst * xhci_ep->mult;
76 ep->max_transfer_size = ep->max_packet_size * ep->packets_per_uframe;
77 }
78
79 xhci_ep->interval = desc->endpoint.poll_interval;
80
81 /*
82 * Only Low/Full speed interrupt endpoints have interval as a linear field,
83 * others have 2-based log of it.
84 */
85 if (dev->speed >= USB_SPEED_HIGH || ep->transfer_type != USB_TRANSFER_INTERRUPT) {
86 xhci_ep->interval = 1 << (xhci_ep->interval - 1);
87 }
88
89 /* Full speed devices have interval in frames */
90 if (dev->speed <= USB_SPEED_FULL) {
91 xhci_ep->interval *= 8;
92 }
93
94 if (xhci_ep->base.transfer_type == USB_TRANSFER_ISOCHRONOUS)
95 isoch_init(xhci_ep, desc);
96
97 if ((rc = alloc_transfer_ds(xhci_ep)))
98 goto err;
99
100 return EOK;
101
102err:
103 return rc;
104}
105
106/**
107 * Finalize XHCI endpoint.
108 * @param[in] xhci_ep XHCI endpoint to finalize.
109 */
110void xhci_endpoint_fini(xhci_endpoint_t *xhci_ep)
111{
112 assert(xhci_ep);
113
114 free_transfer_ds(xhci_ep);
115
116 // TODO: Something missed?
117}
118
119/**
120 * Determine the type of a XHCI endpoint.
121 * @param[in] ep XHCI endpoint to query.
122 *
123 * @return EP_TYPE_[CONTROL|ISOCH|BULK|INTERRUPT]_[IN|OUT]
124 */
125static int xhci_endpoint_type(xhci_endpoint_t *ep)
126{
127 const bool in = ep->base.direction == USB_DIRECTION_IN;
128
129 switch (ep->base.transfer_type) {
130 case USB_TRANSFER_CONTROL:
131 return EP_TYPE_CONTROL;
132
133 case USB_TRANSFER_ISOCHRONOUS:
134 return in ? EP_TYPE_ISOCH_IN
135 : EP_TYPE_ISOCH_OUT;
136
137 case USB_TRANSFER_BULK:
138 return in ? EP_TYPE_BULK_IN
139 : EP_TYPE_BULK_OUT;
140
141 case USB_TRANSFER_INTERRUPT:
142 return in ? EP_TYPE_INTERRUPT_IN
143 : EP_TYPE_INTERRUPT_OUT;
144 }
145
146 return EP_TYPE_INVALID;
147}
148
149/**
150 * Test whether an XHCI endpoint uses streams.
151 * @param[in] xhci_ep XHCI endpoint to query.
152 *
153 * @return True if the endpoint uses streams.
154 */
155static bool endpoint_using_streams(xhci_endpoint_t *xhci_ep)
156{
157 return xhci_ep->primary_stream_ctx_array != NULL;
158}
159
160// static bool primary_stream_ctx_has_secondary_array(xhci_stream_ctx_t *primary_ctx) {
161// /* Section 6.2.4.1, SCT values */
162// return XHCI_STREAM_SCT(*primary_ctx) >= 2;
163// }
164//
165// static size_t secondary_stream_ctx_array_size(xhci_stream_ctx_t *primary_ctx) {
166// if (XHCI_STREAM_SCT(*primary_ctx) < 2) return 0;
167// return 2 << XHCI_STREAM_SCT(*primary_ctx);
168// }
169
170/** Initialize primary streams of XHCI bulk endpoint.
171 * @param[in] hc Host controller of the endpoint.
172 * @param[in] xhci_epi XHCI bulk endpoint to use.
173 * @param[in] count Number of primary streams to initialize.
174 */
175static void initialize_primary_streams(xhci_hc_t *hc, xhci_endpoint_t *xhci_ep, unsigned count) {
176 for (size_t index = 0; index < count; ++index) {
177 xhci_stream_ctx_t *ctx = &xhci_ep->primary_stream_ctx_array[index];
178 xhci_trb_ring_t *ring = &xhci_ep->primary_stream_rings[index];
179
180 /* Init and register TRB ring for every primary stream */
181 xhci_trb_ring_init(ring); // FIXME: Not checking error code?
182 XHCI_STREAM_DEQ_PTR_SET(*ctx, ring->dequeue);
183
184 /* Set to linear stream array */
185 XHCI_STREAM_SCT_SET(*ctx, 1);
186 }
187}
188
189/** Configure XHCI bulk endpoint's stream context.
190 * @param[in] xhci_ep Associated XHCI bulk endpoint.
191 * @param[in] ctx Endpoint context to configure.
192 * @param[in] pstreams The value of MaxPStreams.
193 */
194static void setup_stream_context(xhci_endpoint_t *xhci_ep, xhci_ep_ctx_t *ctx, unsigned pstreams) {
195 XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(xhci_ep));
196 XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, xhci_ep->base.max_packet_size);
197 XHCI_EP_MAX_BURST_SIZE_SET(*ctx, xhci_ep->max_burst - 1);
198 XHCI_EP_ERROR_COUNT_SET(*ctx, 3);
199
200 XHCI_EP_MAX_P_STREAMS_SET(*ctx, pstreams);
201 XHCI_EP_TR_DPTR_SET(*ctx, xhci_ep->primary_stream_ctx_dma.phys);
202 // TODO: set HID?
203 XHCI_EP_LSA_SET(*ctx, 1);
204}
205
206/** TODO document this
207 */
208int xhci_endpoint_request_streams(xhci_hc_t *hc, xhci_device_t *dev, xhci_endpoint_t *xhci_ep, unsigned count) {
209 if (xhci_ep->base.transfer_type != USB_TRANSFER_BULK
210 || dev->base.speed != USB_SPEED_SUPER) {
211 usb_log_error("Streams are only supported by superspeed bulk endpoints.");
212 return EINVAL;
213 }
214
215 if (xhci_ep->max_streams == 1) {
216 usb_log_error("Streams are not supported by endpoint " XHCI_EP_FMT, XHCI_EP_ARGS(*xhci_ep));
217 return EINVAL;
218 }
219
220 uint8_t max_psa_size = 2 << XHCI_REG_RD(hc->cap_regs, XHCI_CAP_MAX_PSA_SIZE);
221 if (count > max_psa_size) {
222 // FIXME: We don't support secondary stream arrays yet, so we just give up for this
223 return ENOTSUP;
224 }
225
226 if (count > xhci_ep->max_streams) {
227 usb_log_error("Endpoint " XHCI_EP_FMT " supports only %" PRIu32 " streams.",
228 XHCI_EP_ARGS(*xhci_ep), xhci_ep->max_streams);
229 return EINVAL;
230 }
231
232 if (count <= 1024) {
233 usb_log_debug2("Allocating primary stream context array of size %u for endpoint " XHCI_EP_FMT,
234 count, XHCI_EP_ARGS(*xhci_ep));
235 if ((dma_buffer_alloc(&xhci_ep->primary_stream_ctx_dma, count * sizeof(xhci_stream_ctx_t))))
236 return ENOMEM;
237 xhci_ep->primary_stream_ctx_array = xhci_ep->primary_stream_ctx_dma.virt;
238
239 xhci_ep->primary_stream_rings = calloc(count, sizeof(xhci_trb_ring_t));
240 if (!xhci_ep->primary_stream_rings) {
241 dma_buffer_free(&xhci_ep->primary_stream_ctx_dma);
242 return ENOMEM;
243 }
244
245 // FIXME: count should be rounded to nearest power of 2 for xHC, workaround for now
246 count = 1024;
247 // FIXME: pstreams are "log2(count) - 1"
248 const size_t pstreams = 9;
249 xhci_ep->primary_stream_ctx_array_size = count;
250
251 memset(xhci_ep->primary_stream_ctx_array, 0, count * sizeof(xhci_stream_ctx_t));
252 initialize_primary_streams(hc, xhci_ep, count);
253
254 xhci_ep_ctx_t ep_ctx;
255 setup_stream_context(xhci_ep, &ep_ctx, pstreams);
256 return hc_add_endpoint(hc, dev->slot_id, xhci_endpoint_index(xhci_ep), &ep_ctx);
257 }
258 // FIXME: Complex stuff not supported yet
259 return ENOTSUP;
260}
261
262/** Allocate transfer data structures for XHCI endpoint.
263 * @param[in] xhci_ep XHCI endpoint to allocate data structures for.
264 *
265 * @return Error code.
266 */
267static int alloc_transfer_ds(xhci_endpoint_t *xhci_ep)
268{
269 /* Can't use XHCI_EP_FMT because the endpoint may not have device. */
270 usb_log_debug2("Allocating main transfer ring for endpoint " XHCI_EP_FMT, XHCI_EP_ARGS(*xhci_ep));
271
272 xhci_ep->primary_stream_ctx_array = NULL;
273
274 int err;
275 if ((err = xhci_trb_ring_init(&xhci_ep->ring))) {
276 return err;
277 }
278
279 if (xhci_ep->base.transfer_type == USB_TRANSFER_ISOCHRONOUS) {
280 if ((err = isoch_alloc_transfers(xhci_ep))) {
281 xhci_trb_ring_fini(&xhci_ep->ring);
282 return err;
283 }
284 }
285
286 return EOK;
287}
288
289/** Free transfer data structures for XHCI endpoint.
290 * @param[in] xhci_ep XHCI endpoint to free data structures for.
291 */
292static void free_transfer_ds(xhci_endpoint_t *xhci_ep)
293{
294 if (endpoint_using_streams(xhci_ep)) {
295 usb_log_debug2("Freeing primary stream context array of endpoint " XHCI_EP_FMT, XHCI_EP_ARGS(*xhci_ep));
296
297 // maybe check if LSA, then skip?
298 // for (size_t index = 0; index < primary_stream_ctx_array_size(xhci_ep); ++index) {
299 // xhci_stream_ctx_t *primary_ctx = xhci_ep->primary_stream_ctx_array + index;
300 // if (primary_stream_ctx_has_secondary_array(primary_ctx)) {
301 // // uintptr_t phys = XHCI_STREAM_DEQ_PTR(*primary_ctx);
302 // /* size_t size = */ secondary_stream_ctx_array_size(primary_ctx);
303 // // TODO: somehow map the address to virtual and free the secondary array
304 // }
305 // }
306 for (size_t index = 0; index < xhci_ep->primary_stream_ctx_array_size; ++index) {
307 // FIXME: Get the trb ring associated with stream [index] and fini it
308 }
309 dma_buffer_free(&xhci_ep->primary_stream_ctx_dma);
310 } else {
311 usb_log_debug2("Freeing main transfer ring of endpoint " XHCI_EP_FMT, XHCI_EP_ARGS(*xhci_ep));
312
313 xhci_trb_ring_fini(&xhci_ep->ring);
314 }
315
316 if (xhci_ep->base.transfer_type == USB_TRANSFER_ISOCHRONOUS) {
317 for (size_t i = 0; i < XHCI_ISOCH_BUFFER_COUNT; ++i) {
318 dma_buffer_free(&xhci_ep->isoch->transfers[i].data);
319 }
320 }
321}
322
323/** See section 4.5.1 of the xHCI spec.
324 */
325uint8_t xhci_endpoint_dci(xhci_endpoint_t *ep)
326{
327 return (2 * ep->base.endpoint) +
328 (ep->base.transfer_type == USB_TRANSFER_CONTROL
329 || ep->base.direction == USB_DIRECTION_IN);
330}
331
332/** Return an index to the endpoint array. The indices are assigned as follows:
333 * 0 EP0 BOTH
334 * 1 EP1 OUT
335 * 2 EP1 IN
336 *
337 * For control endpoints >0, the IN endpoint index is used.
338 *
339 * The index returned must be usually offset by a number of contexts preceding
340 * the endpoint contexts themselves.
341 */
342uint8_t xhci_endpoint_index(xhci_endpoint_t *ep)
343{
344 return xhci_endpoint_dci(ep) - 1;
345}
346
347/** Configure endpoint context of a control endpoint.
348 * @param[in] ep XHCI control endpoint.
349 * @param[in] ctx Endpoint context to configure.
350 */
351static void setup_control_ep_ctx(xhci_endpoint_t *ep, xhci_ep_ctx_t *ctx)
352{
353 XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(ep));
354 XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, ep->base.max_packet_size);
355 XHCI_EP_MAX_BURST_SIZE_SET(*ctx, ep->max_burst - 1);
356 XHCI_EP_MULT_SET(*ctx, ep->mult - 1);
357 XHCI_EP_ERROR_COUNT_SET(*ctx, 3);
358 XHCI_EP_TR_DPTR_SET(*ctx, ep->ring.dequeue);
359 XHCI_EP_DCS_SET(*ctx, 1);
360}
361
362/** Configure endpoint context of a bulk endpoint.
363 * @param[in] ep XHCI bulk endpoint.
364 * @param[in] ctx Endpoint context to configure.
365 */
366static void setup_bulk_ep_ctx(xhci_endpoint_t *ep, xhci_ep_ctx_t *ctx)
367{
368 XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(ep));
369 XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, ep->base.max_packet_size);
370 XHCI_EP_MAX_BURST_SIZE_SET(*ctx, ep->max_burst - 1);
371 XHCI_EP_ERROR_COUNT_SET(*ctx, 3);
372
373 XHCI_EP_MAX_P_STREAMS_SET(*ctx, 0);
374 XHCI_EP_TR_DPTR_SET(*ctx, ep->ring.dequeue);
375 XHCI_EP_DCS_SET(*ctx, 1);
376}
377
378/** Configure endpoint context of a isochronous endpoint.
379 * @param[in] ep XHCI isochronous endpoint.
380 * @param[in] ctx Endpoint context to configure.
381 */
382static void setup_isoch_ep_ctx(xhci_endpoint_t *ep, xhci_ep_ctx_t *ctx)
383{
384 XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(ep));
385 XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, ep->base.max_packet_size & 0x07FF);
386 XHCI_EP_MAX_BURST_SIZE_SET(*ctx, ep->max_burst - 1);
387 XHCI_EP_MULT_SET(*ctx, ep->mult - 1);
388 XHCI_EP_ERROR_COUNT_SET(*ctx, 0);
389 XHCI_EP_TR_DPTR_SET(*ctx, ep->ring.dequeue);
390 XHCI_EP_DCS_SET(*ctx, 1);
391 XHCI_EP_INTERVAL_SET(*ctx, fnzb32(ep->interval) % 32);
392
393 XHCI_EP_MAX_ESIT_PAYLOAD_LO_SET(*ctx, ep->isoch->max_size & 0xFFFF);
394 XHCI_EP_MAX_ESIT_PAYLOAD_HI_SET(*ctx, (ep->isoch->max_size >> 16) & 0xFF);
395}
396
397/** Configure endpoint context of a interrupt endpoint.
398 * @param[in] ep XHCI interrupt endpoint.
399 * @param[in] ctx Endpoint context to configure.
400 */
401static void setup_interrupt_ep_ctx(xhci_endpoint_t *ep, xhci_ep_ctx_t *ctx)
402{
403 XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(ep));
404 XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, ep->base.max_packet_size & 0x07FF);
405 XHCI_EP_MAX_BURST_SIZE_SET(*ctx, ep->max_burst - 1);
406 XHCI_EP_MULT_SET(*ctx, 0);
407 XHCI_EP_ERROR_COUNT_SET(*ctx, 3);
408 XHCI_EP_TR_DPTR_SET(*ctx, ep->ring.dequeue);
409 XHCI_EP_DCS_SET(*ctx, 1);
410 XHCI_EP_INTERVAL_SET(*ctx, fnzb32(ep->interval) % 32);
411 // TODO: max ESIT payload
412}
413
414/** Type of endpoint context configuration function. */
415typedef void (*setup_ep_ctx_helper)(xhci_endpoint_t *, xhci_ep_ctx_t *);
416
417/** Static array, which maps USB endpoint types to their respective endpoint context configuration functions. */
418static const setup_ep_ctx_helper setup_ep_ctx_helpers[] = {
419 [USB_TRANSFER_CONTROL] = setup_control_ep_ctx,
420 [USB_TRANSFER_ISOCHRONOUS] = setup_isoch_ep_ctx,
421 [USB_TRANSFER_BULK] = setup_bulk_ep_ctx,
422 [USB_TRANSFER_INTERRUPT] = setup_interrupt_ep_ctx,
423};
424
425/** Configure endpoint context of XHCI endpoint.
426 * @param[in] ep Associated XHCI endpoint.
427 * @param[in] ep_ctx Endpoint context to configure.
428 */
429void xhci_setup_endpoint_context(xhci_endpoint_t *ep, xhci_ep_ctx_t *ep_ctx)
430{
431 assert(ep);
432 assert(ep_ctx);
433
434 usb_transfer_type_t tt = ep->base.transfer_type;
435 assert(tt < ARRAY_SIZE(setup_ep_ctx_helpers));
436
437 memset(ep_ctx, 0, sizeof(*ep_ctx));
438 setup_ep_ctx_helpers[tt](ep, ep_ctx);
439}
440
441/** Retrieve XHCI endpoint from a device by the endpoint number.
442 * @param[in] dev XHCI device to query.
443 * @param[in] ep Endpoint number identifying the endpoint to retrieve.
444 *
445 * @return XHCI endpoint with the specified number or NULL if no such endpoint exists.
446 */
447xhci_endpoint_t *xhci_device_get_endpoint(xhci_device_t *dev, usb_endpoint_t ep)
448{
449 endpoint_t *ep_base = dev->base.endpoints[ep];
450 if (!ep_base)
451 return NULL;
452
453 return xhci_endpoint_get(ep_base);
454}
455
456/**
457 * @}
458 */
Note: See TracBrowser for help on using the repository browser.