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

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

usb: fix wrong design of transfer aborting

Apparently, we didn't do a good job in thinking through the problem.
In older HCs, it was done just wrong - the UHCI implementation commited
a batch that could have been already aborted, and EHCI+OHCI might miss
an interrupt because they commited the batch sooner than they added it
to their checked list.

This commit takes everything from the other end, which is probably the
only right one. Instead of an endpoint having an extra mutex, it
inherits a mutex from the outside. It never locks it though, it just
checks if the mutex is locked and uses it for waiting on condition
variables.

This mutex is supposed to be the one which the HC driver uses for
locking its structures in scheduling. This way, we avoid the ABBA
deadlock completely, while preserving the synchronization on an
endpoint.

The good thing is that this implementation is much easier to extend with
multiple active batches per endpoint.

  • Property mode set to 100644
File size: 12.4 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#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 int 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 int xhci_endpoint_init(xhci_endpoint_t *xhci_ep, device_t *dev,
61 const usb_endpoint_descriptors_t *desc)
62{
63 int 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 ep->max_transfer_size = ep->max_packet_size * ep->packets_per_uframe;
91 }
92
93 xhci_ep->interval = desc->endpoint.poll_interval;
94
95 /*
96 * Only Low/Full speed interrupt endpoints have interval as a linear field,
97 * others have 2-based log of it.
98 */
99 if (dev->speed >= USB_SPEED_HIGH
100 || ep->transfer_type != USB_TRANSFER_INTERRUPT) {
101 xhci_ep->interval = 1 << (xhci_ep->interval - 1);
102 }
103
104 /* Full speed devices have interval in frames */
105 if (dev->speed <= USB_SPEED_FULL) {
106 xhci_ep->interval *= 8;
107 }
108
109 if (xhci_ep->base.transfer_type == USB_TRANSFER_ISOCHRONOUS)
110 isoch_init(xhci_ep, desc);
111
112 if ((rc = alloc_transfer_ds(xhci_ep)))
113 goto err;
114
115 return EOK;
116
117err:
118 return rc;
119}
120
121/**
122 * Create a new xHCI endpoint structure.
123 *
124 * Bus callback.
125 */
126endpoint_t *xhci_endpoint_create(device_t *dev,
127 const usb_endpoint_descriptors_t *desc)
128{
129 const usb_transfer_type_t type = USB_ED_GET_TRANSFER_TYPE(desc->endpoint);
130
131 xhci_endpoint_t *ep = calloc(1, sizeof(xhci_endpoint_t)
132 + (type == USB_TRANSFER_ISOCHRONOUS) * sizeof(*ep->isoch));
133 if (!ep)
134 return NULL;
135
136 if (xhci_endpoint_init(ep, dev, desc)) {
137 free(ep);
138 return NULL;
139 }
140
141 return &ep->base;
142}
143
144/**
145 * Finalize XHCI endpoint.
146 * @param[in] xhci_ep XHCI endpoint to finalize.
147 */
148static void xhci_endpoint_fini(xhci_endpoint_t *xhci_ep)
149{
150 assert(xhci_ep);
151
152 xhci_endpoint_free_transfer_ds(xhci_ep);
153
154 // TODO: Something missed?
155}
156
157/**
158 * Destroy given xHCI endpoint structure.
159 *
160 * Bus callback.
161 */
162void xhci_endpoint_destroy(endpoint_t *ep)
163{
164 xhci_endpoint_t *xhci_ep = xhci_endpoint_get(ep);
165
166 xhci_endpoint_fini(xhci_ep);
167 free(xhci_ep);
168}
169
170
171/**
172 * Register an andpoint to the xHC.
173 *
174 * Bus callback.
175 */
176int xhci_endpoint_register(endpoint_t *ep_base)
177{
178 int err;
179 xhci_endpoint_t *ep = xhci_endpoint_get(ep_base);
180
181 if (ep_base->endpoint != 0 && (err = hc_add_endpoint(ep)))
182 return err;
183
184 endpoint_set_online(ep_base, &ep->guard);
185 return EOK;
186}
187
188/**
189 * Abort a transfer on an endpoint.
190 */
191static void endpoint_abort(endpoint_t *ep)
192{
193 xhci_device_t *dev = xhci_device_get(ep->device);
194 xhci_endpoint_t *xhci_ep = xhci_endpoint_get(ep);
195
196 /* This function can only abort endpoints without streams. */
197 assert(xhci_ep->primary_stream_data_array == NULL);
198
199 fibril_mutex_lock(&xhci_ep->guard);
200
201 endpoint_set_offline_locked(ep);
202
203 if (!ep->active_batch) {
204 fibril_mutex_unlock(&xhci_ep->guard);
205 return;
206 }
207
208 /* First, offer the batch a short chance to be finished. */
209 endpoint_wait_timeout_locked(ep, 10000);
210
211 if (!ep->active_batch) {
212 fibril_mutex_unlock(&xhci_ep->guard);
213 return;
214 }
215
216 usb_transfer_batch_t * const batch = ep->active_batch;
217
218 const int err = hc_stop_endpoint(xhci_ep);
219 if (err) {
220 usb_log_error("Failed to stop endpoint %u of device "
221 XHCI_DEV_FMT ": %s", ep->endpoint, XHCI_DEV_ARGS(*dev),
222 str_error(err));
223 }
224
225 fibril_mutex_unlock(&xhci_ep->guard);
226
227 batch->error = EINTR;
228 batch->transferred_size = 0;
229 usb_transfer_batch_finish(batch);
230 return;
231}
232
233/**
234 * Unregister an endpoint. If the device is still available, inform the xHC
235 * about it.
236 *
237 * Bus callback.
238 */
239void xhci_endpoint_unregister(endpoint_t *ep_base)
240{
241 int err;
242 xhci_endpoint_t *ep = xhci_endpoint_get(ep_base);
243 xhci_device_t *dev = xhci_device_get(ep_base->device);
244
245 endpoint_abort(ep_base);
246
247 /* If device slot is still available, drop the endpoint. */
248 if (ep_base->endpoint != 0 && dev->slot_id) {
249
250 if ((err = hc_drop_endpoint(ep))) {
251 usb_log_error("Failed to drop endpoint " XHCI_EP_FMT ": %s",
252 XHCI_EP_ARGS(*ep), str_error(err));
253 }
254 } else {
255 usb_log_debug("Not going to drop endpoint " XHCI_EP_FMT " because"
256 " the slot has already been disabled.", XHCI_EP_ARGS(*ep));
257 }
258}
259
260/**
261 * Determine the type of a XHCI endpoint.
262 * @param[in] ep XHCI endpoint to query.
263 *
264 * @return EP_TYPE_[CONTROL|ISOCH|BULK|INTERRUPT]_[IN|OUT]
265 */
266int xhci_endpoint_type(xhci_endpoint_t *ep)
267{
268 const bool in = ep->base.direction == USB_DIRECTION_IN;
269
270 switch (ep->base.transfer_type) {
271 case USB_TRANSFER_CONTROL:
272 return EP_TYPE_CONTROL;
273
274 case USB_TRANSFER_ISOCHRONOUS:
275 return in ? EP_TYPE_ISOCH_IN
276 : EP_TYPE_ISOCH_OUT;
277
278 case USB_TRANSFER_BULK:
279 return in ? EP_TYPE_BULK_IN
280 : EP_TYPE_BULK_OUT;
281
282 case USB_TRANSFER_INTERRUPT:
283 return in ? EP_TYPE_INTERRUPT_IN
284 : EP_TYPE_INTERRUPT_OUT;
285 }
286
287 return EP_TYPE_INVALID;
288}
289
290/**
291 * Allocate transfer data structures for XHCI endpoint not using streams.
292 * @param[in] xhci_ep XHCI endpoint to allocate data structures for.
293 *
294 * @return Error code.
295 */
296static int alloc_transfer_ds(xhci_endpoint_t *xhci_ep)
297{
298 /* Can't use XHCI_EP_FMT because the endpoint may not have device. */
299 usb_log_debug("Allocating main transfer ring for endpoint " XHCI_EP_FMT,
300 XHCI_EP_ARGS(*xhci_ep));
301
302 xhci_ep->primary_stream_data_array = NULL;
303 xhci_ep->primary_stream_data_size = 0;
304
305 int err;
306 if ((err = xhci_trb_ring_init(&xhci_ep->ring, 0))) {
307 return err;
308 }
309
310 if (xhci_ep->base.transfer_type == USB_TRANSFER_ISOCHRONOUS) {
311 if ((err = isoch_alloc_transfers(xhci_ep))) {
312 xhci_trb_ring_fini(&xhci_ep->ring);
313 return err;
314 }
315 }
316
317 return EOK;
318}
319
320/**
321 * Free transfer data structures for XHCI endpoint.
322 * @param[in] xhci_ep XHCI endpoint to free data structures for.
323 */
324void xhci_endpoint_free_transfer_ds(xhci_endpoint_t *xhci_ep)
325{
326 if (xhci_ep->primary_stream_data_size) {
327 xhci_stream_free_ds(xhci_ep);
328 } else {
329 usb_log_debug("Freeing main transfer ring of endpoint " XHCI_EP_FMT,
330 XHCI_EP_ARGS(*xhci_ep));
331 xhci_trb_ring_fini(&xhci_ep->ring);
332 }
333
334 if (xhci_ep->base.transfer_type == USB_TRANSFER_ISOCHRONOUS)
335 isoch_fini(xhci_ep);
336}
337
338xhci_trb_ring_t *xhci_endpoint_get_ring(xhci_endpoint_t *ep, uint32_t stream_id)
339{
340 if (ep->primary_stream_data_size == 0)
341 return stream_id == 0 ? &ep->ring : NULL;
342
343 xhci_stream_data_t *stream_data = xhci_get_stream_ctx_data(ep, stream_id);
344 if (stream_data == NULL) {
345 usb_log_warning("No transfer ring was found for stream %u.", stream_id);
346 return NULL;
347 }
348
349 return &stream_data->ring;
350}
351
352/**
353 * Configure endpoint context of a control endpoint.
354 * @param[in] ep XHCI control endpoint.
355 * @param[in] ctx Endpoint context to configure.
356 */
357static void setup_control_ep_ctx(xhci_endpoint_t *ep, xhci_ep_ctx_t *ctx)
358{
359 XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(ep));
360 XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, ep->base.max_packet_size);
361 XHCI_EP_MAX_BURST_SIZE_SET(*ctx, ep->max_burst - 1);
362 XHCI_EP_MULT_SET(*ctx, ep->mult - 1);
363 XHCI_EP_ERROR_COUNT_SET(*ctx, 3);
364 XHCI_EP_TR_DPTR_SET(*ctx, ep->ring.dequeue);
365 XHCI_EP_DCS_SET(*ctx, 1);
366}
367
368/**
369 * Configure endpoint context of a bulk endpoint.
370 * @param[in] ep XHCI bulk endpoint.
371 * @param[in] ctx Endpoint context to configure.
372 */
373static void setup_bulk_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_ERROR_COUNT_SET(*ctx, 3);
379
380 XHCI_EP_MAX_P_STREAMS_SET(*ctx, 0);
381 XHCI_EP_TR_DPTR_SET(*ctx, ep->ring.dequeue);
382 XHCI_EP_DCS_SET(*ctx, 1);
383}
384
385/**
386 * Configure endpoint context of a isochronous endpoint.
387 * @param[in] ep XHCI isochronous endpoint.
388 * @param[in] ctx Endpoint context to configure.
389 */
390static void setup_isoch_ep_ctx(xhci_endpoint_t *ep, xhci_ep_ctx_t *ctx)
391{
392 XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(ep));
393 XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, ep->base.max_packet_size & 0x07FF);
394 XHCI_EP_MAX_BURST_SIZE_SET(*ctx, ep->max_burst - 1);
395 XHCI_EP_MULT_SET(*ctx, ep->mult - 1);
396 XHCI_EP_ERROR_COUNT_SET(*ctx, 0);
397 XHCI_EP_TR_DPTR_SET(*ctx, ep->ring.dequeue);
398 XHCI_EP_DCS_SET(*ctx, 1);
399 XHCI_EP_INTERVAL_SET(*ctx, fnzb32(ep->interval) % 32);
400
401 XHCI_EP_MAX_ESIT_PAYLOAD_LO_SET(*ctx, ep->isoch->max_size & 0xFFFF);
402 XHCI_EP_MAX_ESIT_PAYLOAD_HI_SET(*ctx, (ep->isoch->max_size >> 16) & 0xFF);
403}
404
405/**
406 * Configure endpoint context of a interrupt endpoint.
407 * @param[in] ep XHCI interrupt endpoint.
408 * @param[in] ctx Endpoint context to configure.
409 */
410static void setup_interrupt_ep_ctx(xhci_endpoint_t *ep, xhci_ep_ctx_t *ctx)
411{
412 XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(ep));
413 XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, ep->base.max_packet_size & 0x07FF);
414 XHCI_EP_MAX_BURST_SIZE_SET(*ctx, ep->max_burst - 1);
415 XHCI_EP_MULT_SET(*ctx, 0);
416 XHCI_EP_ERROR_COUNT_SET(*ctx, 3);
417 XHCI_EP_TR_DPTR_SET(*ctx, ep->ring.dequeue);
418 XHCI_EP_DCS_SET(*ctx, 1);
419 XHCI_EP_INTERVAL_SET(*ctx, fnzb32(ep->interval) % 32);
420 // TODO: max ESIT payload
421}
422
423/** Type of endpoint context configuration function. */
424typedef void (*setup_ep_ctx_helper)(xhci_endpoint_t *, xhci_ep_ctx_t *);
425
426/**
427 * Static array, which maps USB endpoint types to their respective endpoint
428 * context configuration functions.
429 */
430static const setup_ep_ctx_helper setup_ep_ctx_helpers[] = {
431 [USB_TRANSFER_CONTROL] = setup_control_ep_ctx,
432 [USB_TRANSFER_ISOCHRONOUS] = setup_isoch_ep_ctx,
433 [USB_TRANSFER_BULK] = setup_bulk_ep_ctx,
434 [USB_TRANSFER_INTERRUPT] = setup_interrupt_ep_ctx,
435};
436
437/** Configure endpoint context of XHCI endpoint.
438 * @param[in] ep Associated XHCI endpoint.
439 * @param[in] ep_ctx Endpoint context to configure.
440 */
441void xhci_setup_endpoint_context(xhci_endpoint_t *ep, xhci_ep_ctx_t *ep_ctx)
442{
443 assert(ep);
444 assert(ep_ctx);
445
446 usb_transfer_type_t tt = ep->base.transfer_type;
447
448 memset(ep_ctx, 0, sizeof(*ep_ctx));
449 setup_ep_ctx_helpers[tt](ep, ep_ctx);
450}
451
452/**
453 * Clear endpoint halt condition by resetting the endpoint and skipping the
454 * offending transfer.
455 */
456int xhci_endpoint_clear_halt(xhci_endpoint_t *ep, uint32_t stream_id)
457{
458 int err;
459
460 if ((err = hc_reset_endpoint(ep)))
461 return err;
462
463 if ((err = hc_reset_ring(ep, stream_id)))
464 return err;
465
466 return EOK;
467}
468
469/**
470 * @}
471 */
Note: See TracBrowser for help on using the repository browser.