source: mainline/uspace/drv/bus/usb/ehci/ehci_bus.c@ 4172db4a

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 4172db4a 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: 5.0 KB
Line 
1/*
2 * Copyright (c) 2011 Jan Vesely
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 drvusbehci
30 * @{
31 */
32/** @file
33 * @brief EHCI driver
34 */
35
36#include <assert.h>
37#include <stdlib.h>
38#include <usb/host/bandwidth.h>
39#include <usb/debug.h>
40
41#include "ehci_bus.h"
42#include "ehci_batch.h"
43#include "hc.h"
44
45/**
46 * Callback to set toggle on ED.
47 *
48 * @param[in] hcd_ep hcd endpoint structure
49 * @param[in] toggle new value of toggle bit
50 */
51void ehci_ep_toggle_reset(endpoint_t *ep)
52{
53 ehci_endpoint_t *instance = ehci_endpoint_get(ep);
54 if (qh_toggle_from_td(instance->qh))
55 usb_log_warning("EP(%p): Resetting toggle bit for transfer directed EP", instance);
56 qh_toggle_set(instance->qh, 0);
57}
58
59
60/** Creates new hcd endpoint representation.
61 */
62static endpoint_t *ehci_endpoint_create(device_t *dev, const usb_endpoint_descriptors_t *desc)
63{
64 assert(dev);
65
66 ehci_endpoint_t *ehci_ep = malloc(sizeof(ehci_endpoint_t));
67 if (ehci_ep == NULL)
68 return NULL;
69
70 endpoint_init(&ehci_ep->base, dev, desc);
71
72 if (dma_buffer_alloc(&ehci_ep->dma_buffer, sizeof(qh_t)))
73 return NULL;
74
75 ehci_ep->qh = ehci_ep->dma_buffer.virt;
76
77 link_initialize(&ehci_ep->eplist_link);
78 link_initialize(&ehci_ep->pending_link);
79 return &ehci_ep->base;
80}
81
82/** Disposes hcd endpoint structure
83 *
84 * @param[in] hcd driver using this instance.
85 * @param[in] ep endpoint structure.
86 */
87static void ehci_endpoint_destroy(endpoint_t *ep)
88{
89 assert(ep);
90 ehci_endpoint_t *instance = ehci_endpoint_get(ep);
91
92 dma_buffer_free(&instance->dma_buffer);
93 free(instance);
94}
95
96
97static int ehci_register_ep(endpoint_t *ep)
98{
99 bus_t *bus_base = endpoint_get_bus(ep);
100 ehci_bus_t *bus = (ehci_bus_t *) bus_base;
101 ehci_endpoint_t *ehci_ep = ehci_endpoint_get(ep);
102
103 const int err = usb2_bus_ops.endpoint_register(ep);
104 if (err)
105 return err;
106
107 qh_init(ehci_ep->qh, ep);
108 hc_enqueue_endpoint(bus->hc, ep);
109 endpoint_set_online(ep, &bus->hc->guard);
110 return EOK;
111}
112
113static void ehci_unregister_ep(endpoint_t *ep)
114{
115 bus_t *bus_base = endpoint_get_bus(ep);
116 ehci_bus_t *bus = (ehci_bus_t *) bus_base;
117 hc_t *hc = bus->hc;
118 assert(bus);
119 assert(ep);
120
121 usb2_bus_ops.endpoint_unregister(ep);
122 hc_dequeue_endpoint(hc, ep);
123 /*
124 * Now we can be sure the active transfer will not be completed,
125 * as it's out of the schedule, and HC acknowledged it.
126 */
127
128 ehci_endpoint_t *ehci_ep = ehci_endpoint_get(ep);
129
130 fibril_mutex_lock(&hc->guard);
131 endpoint_set_offline_locked(ep);
132 list_remove(&ehci_ep->pending_link);
133 usb_transfer_batch_t * const batch = ep->active_batch;
134 endpoint_deactivate_locked(ep);
135 fibril_mutex_unlock(&hc->guard);
136
137 if (batch) {
138 batch->error = EINTR;
139 batch->transferred_size = 0;
140 usb_transfer_batch_finish(batch);
141 }
142}
143
144static usb_transfer_batch_t *ehci_create_batch(endpoint_t *ep)
145{
146 ehci_transfer_batch_t *batch = ehci_transfer_batch_create(ep);
147 return &batch->base;
148}
149
150static void ehci_destroy_batch(usb_transfer_batch_t *batch)
151{
152 ehci_transfer_batch_destroy(ehci_transfer_batch_get(batch));
153}
154
155static const bus_ops_t ehci_bus_ops = {
156 .parent = &usb2_bus_ops,
157
158 .interrupt = ehci_hc_interrupt,
159 .status = ehci_hc_status,
160
161 .endpoint_destroy = ehci_endpoint_destroy,
162 .endpoint_create = ehci_endpoint_create,
163 .endpoint_register = ehci_register_ep,
164 .endpoint_unregister = ehci_unregister_ep,
165 .endpoint_count_bw = bandwidth_count_usb20,
166
167 .batch_create = ehci_create_batch,
168 .batch_destroy = ehci_destroy_batch,
169 .batch_schedule = ehci_hc_schedule,
170};
171
172int ehci_bus_init(ehci_bus_t *bus, hc_t *hc)
173{
174 assert(hc);
175 assert(bus);
176
177 usb2_bus_t *usb2_bus = (usb2_bus_t *) bus;
178 bus_t *bus_base = (bus_t *) bus;
179
180 usb2_bus_init(usb2_bus, BANDWIDTH_AVAILABLE_USB20);
181 bus_base->ops = &ehci_bus_ops;
182
183 bus->hc = hc;
184
185 return EOK;
186}
187
188/**
189 * @}
190 */
Note: See TracBrowser for help on using the repository browser.