source: mainline/uspace/drv/bus/usb/xhci/device.c@ 92a7b296

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 92a7b296 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: 10.0 KB
Line 
1/*
2 * Copyright (c) 2017 Ondrej Hlavaty <aearsis@eideo.cz>
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/** @addtogroup libusbhost
29 * @{
30 */
31/** @file
32 * HC Endpoint management.
33 */
34
35#include <usb/host/utility.h>
36#include <usb/host/ddf_helpers.h>
37#include <usb/host/endpoint.h>
38#include <usb/host/hcd.h>
39#include <usb/host/utility.h>
40#include <usb/classes/classes.h>
41#include <usb/classes/hub.h>
42#include <usb/descriptor.h>
43#include <usb/debug.h>
44
45#include <assert.h>
46#include <errno.h>
47#include <str_error.h>
48#include <macros.h>
49#include <stdbool.h>
50
51#include "hc.h"
52#include "bus.h"
53#include "endpoint.h"
54#include "hw_struct/context.h"
55
56#include "device.h"
57
58/**
59 * Initial descriptor used for control endpoint 0,
60 * before more configuration is retrieved.
61 */
62static const usb_endpoint_descriptors_t ep0_initial_desc = {
63 .endpoint.max_packet_size = CTRL_PIPE_MIN_PACKET_SIZE,
64};
65
66/**
67 * Assign address and control endpoint to a new XHCI device. Once this function
68 * successfully returns, the device is online.
69 *
70 * @param[in] bus XHCI bus, in which the address is assigned.
71 * @param[in] dev New device to address and configure./e
72 * @return Error code.
73 */
74static int address_device(xhci_device_t *dev)
75{
76 int err;
77
78 /* Enable new slot. */
79 if ((err = hc_enable_slot(dev)) != EOK)
80 return err;
81 usb_log_debug("Obtained slot ID: %u.", dev->slot_id);
82
83 endpoint_t *ep0_base;
84 if ((err = bus_endpoint_add(&dev->base, &ep0_initial_desc, &ep0_base)))
85 goto err_slot;
86
87 usb_log_debug("Looking up new device initial MPS: %s",
88 usb_str_speed(dev->base.speed));
89 ep0_base->max_packet_size = hc_get_ep0_initial_mps(dev->base.speed);
90
91 /* Address device */
92 if ((err = hc_address_device(dev)))
93 goto err_added;
94
95 return EOK;
96
97err_added:
98 /* Bus reference */
99 endpoint_del_ref(ep0_base);
100 dev->base.endpoints[0] = NULL;
101err_slot:
102 hc_disable_slot(dev);
103 return err;
104}
105
106/**
107 * Retrieve and set maximum packet size for endpoint zero of a XHCI device.
108 *
109 * @param[in] hc Host controller, which manages the device.
110 * @param[in] dev Device with operational endpoint zero.
111 * @return Error code.
112 */
113static int setup_ep0_packet_size(xhci_hc_t *hc, xhci_device_t *dev)
114{
115 int err;
116
117 uint16_t max_packet_size;
118 if ((err = hc_get_ep0_max_packet_size(&max_packet_size,
119 (bus_t *) &hc->bus, &dev->base)))
120 return err;
121
122 xhci_endpoint_t *ep0 = xhci_endpoint_get(dev->base.endpoints[0]);
123 assert(ep0);
124
125 if (ep0->base.max_packet_size == max_packet_size)
126 return EOK;
127
128 ep0->base.max_packet_size = max_packet_size;
129 ep0->base.max_transfer_size = max_packet_size * ep0->base.packets_per_uframe;
130
131 if ((err = hc_update_endpoint(ep0)))
132 return err;
133
134 return EOK;
135}
136
137/**
138 * Check whether the device is a hub and if so, fill its characterstics.
139 *
140 * If this fails, it does not necessarily mean the device is unusable.
141 * Just the TT will not work correctly.
142 */
143static int setup_hub(xhci_device_t *dev, usb_standard_device_descriptor_t *desc)
144{
145 if (desc->device_class != USB_CLASS_HUB)
146 return EOK;
147
148 usb_hub_descriptor_header_t hub_desc = { 0 };
149 const int err = hc_get_hub_desc(&dev->base, &hub_desc);
150 if (err)
151 return err;
152
153 dev->is_hub = 1;
154 dev->num_ports = hub_desc.port_count;
155
156 if (dev->base.speed == USB_SPEED_HIGH) {
157 dev->tt_think_time = 8 +
158 8 * !!(hub_desc.characteristics & HUB_CHAR_TT_THINK_8) +
159 16 * !!(hub_desc.characteristics & HUB_CHAR_TT_THINK_16);
160 }
161
162 usb_log_debug("Device(%u): recognised USB hub with %u ports",
163 dev->base.address, dev->num_ports);
164 return EOK;
165}
166
167/**
168 * Respond to a new device on the XHCI bus. Address it, negotiate packet size
169 * and retrieve USB descriptors.
170 *
171 * @param[in] bus XHCI bus, where the new device emerged.
172 * @param[in] dev XHCI device, which has appeared on the bus.
173 *
174 * @return Error code.
175 */
176int xhci_device_enumerate(device_t *dev)
177{
178 int err;
179 xhci_bus_t *bus = bus_to_xhci_bus(dev->bus);
180 xhci_device_t *xhci_dev = xhci_device_get(dev);
181
182 /* Calculate route string */
183 xhci_device_t *xhci_hub = xhci_device_get(dev->hub);
184 xhci_dev->route_str = xhci_hub->route_str;
185
186 /* Roothub port is not part of the route string */
187 if (dev->tier >= 2) {
188 const unsigned offset = 4 * (dev->tier - 2);
189 xhci_dev->route_str |= (dev->port & 0xf) << offset;
190 xhci_dev->rh_port = xhci_hub->rh_port;
191 }
192
193 int retries = 3;
194 do {
195 /* Assign an address to the device */
196 err = address_device(xhci_dev);
197 } while (err == ESTALL && --retries > 0);
198
199 if (err) {
200 usb_log_error("Failed to setup address of the new device: %s",
201 str_error(err));
202 return err;
203 }
204
205 /* Setup EP0 might already need to issue a transfer. */
206 fibril_mutex_lock(&bus->base.guard);
207 assert(bus->devices_by_slot[xhci_dev->slot_id] == NULL);
208 bus->devices_by_slot[xhci_dev->slot_id] = xhci_dev;
209 fibril_mutex_unlock(&bus->base.guard);
210
211 if ((err = setup_ep0_packet_size(bus->hc, xhci_dev))) {
212 usb_log_error("Failed to setup control endpoint "
213 "of the new device: %s", str_error(err));
214 goto err_address;
215 }
216
217 usb_standard_device_descriptor_t desc = { 0 };
218
219 if ((err = hc_get_device_desc(dev, &desc))) {
220 usb_log_error("Device(%d): failed to get device "
221 "descriptor: %s", dev->address, str_error(err));
222 goto err_address;
223 }
224
225 if ((err = setup_hub(xhci_dev, &desc)))
226 usb_log_warning("Device(%d): failed to setup hub "
227 "characteristics: %s. Continuing anyway.",
228 dev->address, str_error(err));
229
230 if ((err = hcd_ddf_setup_match_ids(dev, &desc))) {
231 usb_log_error("Device(%d): failed to setup match IDs: %s",
232 dev->address, str_error(err));
233 goto err_address;
234 }
235
236 return EOK;
237
238err_address:
239 return err;
240}
241
242/**
243 * Remove device from XHCI bus. Transition it to the offline state, abort all
244 * ongoing transfers and unregister all of its endpoints.
245 *
246 * Bus callback.
247 *
248 * @param[in] bus XHCI bus, from which the device is removed.
249 * @param[in] dev XHCI device, which is removed from the bus.
250 * @return Error code.
251 */
252void xhci_device_gone(device_t *dev)
253{
254 int err;
255 xhci_bus_t *bus = bus_to_xhci_bus(dev->bus);
256 xhci_device_t *xhci_dev = xhci_device_get(dev);
257
258 /* Disable the slot, dropping all endpoints. */
259 const uint32_t slot_id = xhci_dev->slot_id;
260 if ((err = hc_disable_slot(xhci_dev))) {
261 usb_log_warning("Failed to disable slot of device " XHCI_DEV_FMT
262 ": %s", XHCI_DEV_ARGS(*xhci_dev), str_error(err));
263 }
264
265 bus->devices_by_slot[slot_id] = NULL;
266}
267
268/**
269 * Reverts things device_offline did, getting the device back up.
270 *
271 * Bus callback.
272 */
273int xhci_device_online(device_t *dev_base)
274{
275 int err;
276
277 xhci_bus_t *bus = bus_to_xhci_bus(dev_base->bus);
278 assert(bus);
279
280 xhci_device_t *dev = xhci_device_get(dev_base);
281 assert(dev);
282
283 /* Transition the device from the Addressed to the Configured state. */
284 if ((err = hc_configure_device(dev))) {
285 usb_log_warning("Failed to configure device " XHCI_DEV_FMT ".",
286 XHCI_DEV_ARGS(*dev));
287 return err;
288 }
289
290 return EOK;
291}
292
293/**
294 * Make given device offline. Offline the DDF function, tear down all
295 * endpoints, issue Deconfigure Device command to xHC.
296 *
297 * Bus callback.
298 */
299void xhci_device_offline(device_t *dev_base)
300{
301 int err;
302
303 xhci_bus_t *bus = bus_to_xhci_bus(dev_base->bus);
304 assert(bus);
305
306 xhci_device_t *dev = xhci_device_get(dev_base);
307 assert(dev);
308
309 /* Issue one HC command to simultaneously drop all endpoints except zero. */
310 if ((err = hc_deconfigure_device(dev))) {
311 usb_log_warning("Failed to deconfigure device "
312 XHCI_DEV_FMT ".", XHCI_DEV_ARGS(*dev));
313 }
314}
315
316/**
317 * Fill a slot context that is part of an Input Context with appropriate
318 * values.
319 *
320 * @param ctx Slot context, zeroed out.
321 */
322void xhci_setup_slot_context(xhci_device_t *dev, xhci_slot_ctx_t *ctx)
323{
324 /* Initialize slot_ctx according to section 4.3.3 point 3. */
325 XHCI_SLOT_ROOT_HUB_PORT_SET(*ctx, dev->rh_port);
326 XHCI_SLOT_ROUTE_STRING_SET(*ctx, dev->route_str);
327 XHCI_SLOT_SPEED_SET(*ctx, hc_speed_to_psiv(dev->base.speed));
328
329 /*
330 * Note: This function is used even before this flag can be set, to
331 * issue the address device command. It is OK, because these
332 * flags are not required to be valid for that command.
333 */
334 if (dev->is_hub) {
335 XHCI_SLOT_HUB_SET(*ctx, 1);
336 XHCI_SLOT_NUM_PORTS_SET(*ctx, dev->num_ports);
337 XHCI_SLOT_TT_THINK_TIME_SET(*ctx, dev->tt_think_time);
338 XHCI_SLOT_MTT_SET(*ctx, 0); // MTT not supported yet
339 }
340
341 /* Setup Transaction Translation. TODO: Test this with HS hub. */
342 if (dev->base.tt.dev != NULL) {
343 xhci_device_t *hub = xhci_device_get(dev->base.tt.dev);
344 XHCI_SLOT_TT_HUB_SLOT_ID_SET(*ctx, hub->slot_id);
345 XHCI_SLOT_TT_HUB_PORT_SET(*ctx, dev->base.tt.port);
346 }
347
348 /*
349 * As we always allocate space for whole input context, we can set this
350 * to maximum. The only exception being Address Device command, which
351 * explicitly requires this to be se to 1.
352 */
353 XHCI_SLOT_CTX_ENTRIES_SET(*ctx, 31);
354}
355
356
357/**
358 * @}
359 */
Note: See TracBrowser for help on using the repository browser.