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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since b2dca8de was e0a5d4c, checked in by Ondřej Hlavatý <aearsis@…>, 8 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: 13.0 KB
Line 
1/*
2 * Copyright (c) 2018 Petr Manek, Ondrej Hlavaty, Michal Staruch, Jan Hrach
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#include <str_error.h>
42
43#include "hc.h"
44#include "bus.h"
45#include "commands.h"
46#include "device.h"
47#include "endpoint.h"
48#include "streams.h"
49
50static errno_t alloc_transfer_ds(xhci_endpoint_t *);
51
52/**
53 * Initialize new XHCI endpoint.
54 * @param[in] xhci_ep Allocated XHCI endpoint to initialize.
55 * @param[in] dev Device, to which the endpoint belongs.
56 * @param[in] desc USB endpoint descriptor carrying configuration data.
57 *
58 * @return Error code.
59 */
60static errno_t xhci_endpoint_init(xhci_endpoint_t *xhci_ep, device_t *dev,
61 const usb_endpoint_descriptors_t *desc)
62{
63 errno_t rc;
64 assert(xhci_ep);
65
66 endpoint_t *ep = &xhci_ep->base;
67
68 endpoint_init(ep, dev, desc);
69
70 fibril_mutex_initialize(&xhci_ep->guard);
71
72 xhci_ep->max_burst = desc->companion.max_burst + 1;
73
74 if (ep->transfer_type == USB_TRANSFER_BULK)
75 xhci_ep->max_streams = 1 << (USB_SSC_MAX_STREAMS(desc->companion));
76 else
77 xhci_ep->max_streams = 1;
78
79 if (ep->transfer_type == USB_TRANSFER_ISOCHRONOUS)
80 xhci_ep->mult = USB_SSC_MULT(desc->companion) + 1;
81 else
82 xhci_ep->mult = 1;
83
84 /*
85 * In USB 3, the semantics of wMaxPacketSize changed. Now the number of
86 * packets per service interval is determined from max_burst and mult.
87 */
88 if (dev->speed >= USB_SPEED_SUPER) {
89 ep->packets_per_uframe = xhci_ep->max_burst * xhci_ep->mult;
90 if (ep->transfer_type == USB_TRANSFER_ISOCHRONOUS
91 || ep->transfer_type == USB_TRANSFER_INTERRUPT) {
92 ep->max_transfer_size = ep->max_packet_size * ep->packets_per_uframe;
93 }
94 }
95
96 xhci_ep->interval = desc->endpoint.poll_interval;
97
98 /*
99 * Only Low/Full speed interrupt endpoints have interval as a linear field,
100 * others have 2-based log of it.
101 */
102 if (dev->speed >= USB_SPEED_HIGH
103 || ep->transfer_type != USB_TRANSFER_INTERRUPT) {
104 xhci_ep->interval = 1 << (xhci_ep->interval - 1);
105 }
106
107 /* Full speed devices have interval in frames */
108 if (dev->speed <= USB_SPEED_FULL) {
109 xhci_ep->interval *= 8;
110 }
111
112 if (xhci_ep->base.transfer_type == USB_TRANSFER_ISOCHRONOUS)
113 isoch_init(xhci_ep, desc);
114
115 if ((rc = alloc_transfer_ds(xhci_ep)))
116 goto err;
117
118 unsigned flags = -1U;
119
120 /* Some xHCs can handle 64-bit addresses */
121 xhci_bus_t *bus = bus_to_xhci_bus(ep->device->bus);
122 if (bus->hc->ac64)
123 flags &= ~DMA_POLICY_4GiB;
124
125 /* xHCI works best if it can fit 65k transfers in one TRB */
126 ep->transfer_buffer_policy = dma_policy_create(flags, 1 << 16);
127
128 /* But actualy can do full scatter-gather. */
129 ep->required_transfer_buffer_policy = dma_policy_create(flags, PAGE_SIZE);
130
131 return EOK;
132
133err:
134 return rc;
135}
136
137/**
138 * Create a new xHCI endpoint structure.
139 *
140 * Bus callback.
141 */
142endpoint_t *xhci_endpoint_create(device_t *dev,
143 const usb_endpoint_descriptors_t *desc)
144{
145 const usb_transfer_type_t type = USB_ED_GET_TRANSFER_TYPE(desc->endpoint);
146
147 xhci_endpoint_t *ep = calloc(1, sizeof(xhci_endpoint_t)
148 + (type == USB_TRANSFER_ISOCHRONOUS) * sizeof(*ep->isoch));
149 if (!ep)
150 return NULL;
151
152 if (xhci_endpoint_init(ep, dev, desc)) {
153 free(ep);
154 return NULL;
155 }
156
157 return &ep->base;
158}
159
160/**
161 * Finalize XHCI endpoint.
162 * @param[in] xhci_ep XHCI endpoint to finalize.
163 */
164static void xhci_endpoint_fini(xhci_endpoint_t *xhci_ep)
165{
166 assert(xhci_ep);
167
168 xhci_endpoint_free_transfer_ds(xhci_ep);
169
170 // TODO: Something missed?
171}
172
173/**
174 * Destroy given xHCI endpoint structure.
175 *
176 * Bus callback.
177 */
178void xhci_endpoint_destroy(endpoint_t *ep)
179{
180 xhci_endpoint_t *xhci_ep = xhci_endpoint_get(ep);
181
182 xhci_endpoint_fini(xhci_ep);
183 free(xhci_ep);
184}
185
186
187/**
188 * Register an andpoint to the xHC.
189 *
190 * Bus callback.
191 */
192errno_t xhci_endpoint_register(endpoint_t *ep_base)
193{
194 errno_t err;
195 xhci_endpoint_t *ep = xhci_endpoint_get(ep_base);
196
197 if (ep_base->endpoint != 0 && (err = hc_add_endpoint(ep)))
198 return err;
199
200 endpoint_set_online(ep_base, &ep->guard);
201 return EOK;
202}
203
204/**
205 * Abort a transfer on an endpoint.
206 */
207static void endpoint_abort(endpoint_t *ep)
208{
209 xhci_device_t *dev = xhci_device_get(ep->device);
210 xhci_endpoint_t *xhci_ep = xhci_endpoint_get(ep);
211
212 /* This function can only abort endpoints without streams. */
213 assert(xhci_ep->primary_stream_data_array == NULL);
214
215 fibril_mutex_lock(&xhci_ep->guard);
216
217 endpoint_set_offline_locked(ep);
218
219 if (!ep->active_batch) {
220 fibril_mutex_unlock(&xhci_ep->guard);
221 return;
222 }
223
224 /* First, offer the batch a short chance to be finished. */
225 endpoint_wait_timeout_locked(ep, 10000);
226
227 if (!ep->active_batch) {
228 fibril_mutex_unlock(&xhci_ep->guard);
229 return;
230 }
231
232 usb_transfer_batch_t * const batch = ep->active_batch;
233
234 const errno_t err = hc_stop_endpoint(xhci_ep);
235 if (err) {
236 usb_log_error("Failed to stop endpoint %u of device "
237 XHCI_DEV_FMT ": %s", ep->endpoint, XHCI_DEV_ARGS(*dev),
238 str_error(err));
239 }
240
241 fibril_mutex_unlock(&xhci_ep->guard);
242
243 batch->error = EINTR;
244 batch->transferred_size = 0;
245 usb_transfer_batch_finish(batch);
246 return;
247}
248
249/**
250 * Unregister an endpoint. If the device is still available, inform the xHC
251 * about it.
252 *
253 * Bus callback.
254 */
255void xhci_endpoint_unregister(endpoint_t *ep_base)
256{
257 errno_t err;
258 xhci_endpoint_t *ep = xhci_endpoint_get(ep_base);
259 xhci_device_t *dev = xhci_device_get(ep_base->device);
260
261 endpoint_abort(ep_base);
262
263 /* If device slot is still available, drop the endpoint. */
264 if (ep_base->endpoint != 0 && dev->slot_id) {
265
266 if ((err = hc_drop_endpoint(ep))) {
267 usb_log_error("Failed to drop endpoint " XHCI_EP_FMT ": %s",
268 XHCI_EP_ARGS(*ep), str_error(err));
269 }
270 } else {
271 usb_log_debug("Not going to drop endpoint " XHCI_EP_FMT " because"
272 " the slot has already been disabled.", XHCI_EP_ARGS(*ep));
273 }
274}
275
276/**
277 * Determine the type of a XHCI endpoint.
278 * @param[in] ep XHCI endpoint to query.
279 *
280 * @return EP_TYPE_[CONTROL|ISOCH|BULK|INTERRUPT]_[IN|OUT]
281 */
282int xhci_endpoint_type(xhci_endpoint_t *ep)
283{
284 const bool in = ep->base.direction == USB_DIRECTION_IN;
285
286 switch (ep->base.transfer_type) {
287 case USB_TRANSFER_CONTROL:
288 return EP_TYPE_CONTROL;
289
290 case USB_TRANSFER_ISOCHRONOUS:
291 return in ? EP_TYPE_ISOCH_IN
292 : EP_TYPE_ISOCH_OUT;
293
294 case USB_TRANSFER_BULK:
295 return in ? EP_TYPE_BULK_IN
296 : EP_TYPE_BULK_OUT;
297
298 case USB_TRANSFER_INTERRUPT:
299 return in ? EP_TYPE_INTERRUPT_IN
300 : EP_TYPE_INTERRUPT_OUT;
301 }
302
303 return EP_TYPE_INVALID;
304}
305
306/**
307 * Allocate transfer data structures for XHCI endpoint not using streams.
308 * @param[in] xhci_ep XHCI endpoint to allocate data structures for.
309 *
310 * @return Error code.
311 */
312static errno_t alloc_transfer_ds(xhci_endpoint_t *xhci_ep)
313{
314 /* Can't use XHCI_EP_FMT because the endpoint may not have device. */
315 usb_log_debug("Allocating main transfer ring for endpoint " XHCI_EP_FMT,
316 XHCI_EP_ARGS(*xhci_ep));
317
318 xhci_ep->primary_stream_data_array = NULL;
319 xhci_ep->primary_stream_data_size = 0;
320
321 errno_t err;
322 if ((err = xhci_trb_ring_init(&xhci_ep->ring, 0))) {
323 return err;
324 }
325
326 if (xhci_ep->base.transfer_type == USB_TRANSFER_ISOCHRONOUS) {
327 if ((err = isoch_alloc_transfers(xhci_ep))) {
328 xhci_trb_ring_fini(&xhci_ep->ring);
329 return err;
330 }
331 }
332
333 return EOK;
334}
335
336/**
337 * Free transfer data structures for XHCI endpoint.
338 * @param[in] xhci_ep XHCI endpoint to free data structures for.
339 */
340void xhci_endpoint_free_transfer_ds(xhci_endpoint_t *xhci_ep)
341{
342 if (xhci_ep->primary_stream_data_size) {
343 xhci_stream_free_ds(xhci_ep);
344 } else {
345 usb_log_debug("Freeing main transfer ring of endpoint " XHCI_EP_FMT,
346 XHCI_EP_ARGS(*xhci_ep));
347 xhci_trb_ring_fini(&xhci_ep->ring);
348 }
349
350 if (xhci_ep->base.transfer_type == USB_TRANSFER_ISOCHRONOUS)
351 isoch_fini(xhci_ep);
352}
353
354xhci_trb_ring_t *xhci_endpoint_get_ring(xhci_endpoint_t *ep, uint32_t stream_id)
355{
356 if (ep->primary_stream_data_size == 0)
357 return stream_id == 0 ? &ep->ring : NULL;
358
359 xhci_stream_data_t *stream_data = xhci_get_stream_ctx_data(ep, stream_id);
360 if (stream_data == NULL) {
361 usb_log_warning("No transfer ring was found for stream %u.", stream_id);
362 return NULL;
363 }
364
365 return &stream_data->ring;
366}
367
368/**
369 * Configure endpoint context of a control endpoint.
370 * @param[in] ep XHCI control endpoint.
371 * @param[in] ctx Endpoint context to configure.
372 */
373static void setup_control_ep_ctx(xhci_endpoint_t *ep, xhci_ep_ctx_t *ctx)
374{
375 XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(ep));
376 XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, ep->base.max_packet_size);
377 XHCI_EP_MAX_BURST_SIZE_SET(*ctx, ep->max_burst - 1);
378 XHCI_EP_MULT_SET(*ctx, ep->mult - 1);
379 XHCI_EP_ERROR_COUNT_SET(*ctx, 3);
380 XHCI_EP_TR_DPTR_SET(*ctx, ep->ring.dequeue);
381 XHCI_EP_DCS_SET(*ctx, 1);
382}
383
384/**
385 * Configure endpoint context of a bulk endpoint.
386 * @param[in] ep XHCI bulk endpoint.
387 * @param[in] ctx Endpoint context to configure.
388 */
389static void setup_bulk_ep_ctx(xhci_endpoint_t *ep, xhci_ep_ctx_t *ctx)
390{
391 XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(ep));
392 XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, ep->base.max_packet_size);
393 XHCI_EP_MAX_BURST_SIZE_SET(*ctx, ep->max_burst - 1);
394 XHCI_EP_ERROR_COUNT_SET(*ctx, 3);
395
396 XHCI_EP_MAX_P_STREAMS_SET(*ctx, 0);
397 XHCI_EP_TR_DPTR_SET(*ctx, ep->ring.dequeue);
398 XHCI_EP_DCS_SET(*ctx, 1);
399}
400
401/**
402 * Configure endpoint context of a isochronous endpoint.
403 * @param[in] ep XHCI isochronous endpoint.
404 * @param[in] ctx Endpoint context to configure.
405 */
406static void setup_isoch_ep_ctx(xhci_endpoint_t *ep, xhci_ep_ctx_t *ctx)
407{
408 XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(ep));
409 XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, ep->base.max_packet_size & 0x07FF);
410 XHCI_EP_MAX_BURST_SIZE_SET(*ctx, ep->max_burst - 1);
411 XHCI_EP_MULT_SET(*ctx, ep->mult - 1);
412 XHCI_EP_ERROR_COUNT_SET(*ctx, 0);
413 XHCI_EP_TR_DPTR_SET(*ctx, ep->ring.dequeue);
414 XHCI_EP_DCS_SET(*ctx, 1);
415 XHCI_EP_INTERVAL_SET(*ctx, fnzb32(ep->interval) % 32);
416
417 XHCI_EP_MAX_ESIT_PAYLOAD_LO_SET(*ctx, ep->isoch->max_size & 0xFFFF);
418 XHCI_EP_MAX_ESIT_PAYLOAD_HI_SET(*ctx, (ep->isoch->max_size >> 16) & 0xFF);
419}
420
421/**
422 * Configure endpoint context of a interrupt endpoint.
423 * @param[in] ep XHCI interrupt endpoint.
424 * @param[in] ctx Endpoint context to configure.
425 */
426static void setup_interrupt_ep_ctx(xhci_endpoint_t *ep, xhci_ep_ctx_t *ctx)
427{
428 XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(ep));
429 XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, ep->base.max_packet_size & 0x07FF);
430 XHCI_EP_MAX_BURST_SIZE_SET(*ctx, ep->max_burst - 1);
431 XHCI_EP_MULT_SET(*ctx, 0);
432 XHCI_EP_ERROR_COUNT_SET(*ctx, 3);
433 XHCI_EP_TR_DPTR_SET(*ctx, ep->ring.dequeue);
434 XHCI_EP_DCS_SET(*ctx, 1);
435 XHCI_EP_INTERVAL_SET(*ctx, fnzb32(ep->interval) % 32);
436 // TODO: max ESIT payload
437}
438
439/** Type of endpoint context configuration function. */
440typedef void (*setup_ep_ctx_helper)(xhci_endpoint_t *, xhci_ep_ctx_t *);
441
442/**
443 * Static array, which maps USB endpoint types to their respective endpoint
444 * context configuration functions.
445 */
446static const setup_ep_ctx_helper setup_ep_ctx_helpers[] = {
447 [USB_TRANSFER_CONTROL] = setup_control_ep_ctx,
448 [USB_TRANSFER_ISOCHRONOUS] = setup_isoch_ep_ctx,
449 [USB_TRANSFER_BULK] = setup_bulk_ep_ctx,
450 [USB_TRANSFER_INTERRUPT] = setup_interrupt_ep_ctx,
451};
452
453/** Configure endpoint context of XHCI endpoint.
454 * @param[in] ep Associated XHCI endpoint.
455 * @param[in] ep_ctx Endpoint context to configure.
456 */
457void xhci_setup_endpoint_context(xhci_endpoint_t *ep, xhci_ep_ctx_t *ep_ctx)
458{
459 assert(ep);
460 assert(ep_ctx);
461
462 usb_transfer_type_t tt = ep->base.transfer_type;
463
464 memset(ep_ctx, 0, sizeof(*ep_ctx));
465 setup_ep_ctx_helpers[tt](ep, ep_ctx);
466}
467
468/**
469 * Clear endpoint halt condition by resetting the endpoint and skipping the
470 * offending transfer.
471 */
472errno_t xhci_endpoint_clear_halt(xhci_endpoint_t *ep, uint32_t stream_id)
473{
474 errno_t err;
475
476 if ((err = hc_reset_endpoint(ep)))
477 return err;
478
479 if ((err = hc_reset_ring(ep, stream_id)))
480 return err;
481
482 return EOK;
483}
484
485/**
486 * @}
487 */
Note: See TracBrowser for help on using the repository browser.