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

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

usbhost endpoint: endpoint→active replaced by tracking active batch

The mechanism is optional, synchronization over endpoint is now not forced. It will be used by xhci to utilize streams.

  • Property mode set to 100644
File size: 14.7 KB
RevLine 
[a5976973]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
[2b61945]35#include <usb/host/utils/malloc32.h>
[20eaa82]36#include <usb/host/ddf_helpers.h>
[a5976973]37#include <usb/host/endpoint.h>
[20eaa82]38#include <usb/host/hcd.h>
[a5976973]39#include <usb/debug.h>
40
41#include <assert.h>
42#include <errno.h>
[20eaa82]43#include <str_error.h>
[a5976973]44#include <macros.h>
45#include <stdbool.h>
46
[2cf28b9]47#include "hc.h"
[a5976973]48#include "bus.h"
49#include "endpoint.h"
[5fd9c30]50#include "transfers.h"
[a5976973]51
[0206d35]52
53/* FIXME Are these really static? Older HCs fetch it from descriptor. */
54/* FIXME Add USB3 options, if applicable. */
55static const usb_endpoint_desc_t ep0_desc = {
56 .endpoint_no = 0,
57 .direction = USB_DIRECTION_BOTH,
58 .transfer_type = USB_TRANSFER_CONTROL,
59 .max_packet_size = CTRL_PIPE_MIN_PACKET_SIZE,
60 .packets = 1,
61};
62
63static int prepare_endpoint(xhci_endpoint_t *ep, const usb_endpoint_desc_t *desc)
64{
65 /* Extract information from endpoint_desc */
[a5b3de6]66 ep->base.endpoint = desc->endpoint_no;
[0206d35]67 ep->base.direction = desc->direction;
68 ep->base.transfer_type = desc->transfer_type;
69 ep->base.max_packet_size = desc->max_packet_size;
70 ep->base.packets = desc->packets;
71 ep->max_streams = desc->usb3.max_streams;
72 ep->max_burst = desc->usb3.max_burst;
73 // TODO add this property to usb_endpoint_desc_t and fetch it from ss companion desc
74 ep->mult = 0;
75
76 return xhci_endpoint_alloc_transfer_ds(ep);
77}
78
79static endpoint_t *create_endpoint(bus_t *base);
80
81static int address_device(xhci_hc_t *hc, xhci_device_t *dev)
82{
83 int err;
84
85 /* Enable new slot. */
86 if ((err = hc_enable_slot(hc, &dev->slot_id)) != EOK)
87 return err;
88 usb_log_debug2("Obtained slot ID: %u.\n", dev->slot_id);
89
90 /* Create and configure control endpoint. */
91 endpoint_t *ep0_base = create_endpoint(&hc->bus.base);
92 if (!ep0_base)
93 goto err_slot;
94
95 /* Temporary reference */
96 endpoint_add_ref(ep0_base);
97
98 xhci_endpoint_t *ep0 = xhci_endpoint_get(ep0_base);
99
100 if ((err = prepare_endpoint(ep0, &ep0_desc)))
101 goto err_ep;
102
[8b8c164]103 /* Register EP0 */
104 if ((err = xhci_device_add_endpoint(dev, ep0)))
105 goto err_prepared;
106
[0206d35]107 /* Address device */
108 if ((err = hc_address_device(hc, dev, ep0)))
[8b8c164]109 goto err_added;
[0206d35]110
[8b8c164]111 /* Temporary reference */
112 endpoint_del_ref(ep0_base);
[0206d35]113 return EOK;
114
[8b8c164]115err_added:
116 xhci_device_remove_endpoint(ep0);
117err_prepared:
[0206d35]118 xhci_endpoint_free_transfer_ds(ep0);
119err_ep:
[8b8c164]120 /* Temporary reference */
[0206d35]121 endpoint_del_ref(ep0_base);
122err_slot:
[9620a54]123 hc_disable_slot(hc, dev);
[0206d35]124 return err;
125}
126
[20eaa82]127int xhci_bus_enumerate_device(xhci_bus_t *bus, xhci_hc_t *hc, device_t *dev)
128{
129 int err;
[2b61945]130 xhci_device_t *xhci_dev = xhci_device_get(dev);
[20eaa82]131
132 /* Manage TT */
133 if (dev->hub->speed == USB_SPEED_HIGH && usb_speed_is_11(dev->speed)) {
134 /* For LS devices under HS hub */
135 /* TODO: How about SS hubs? */
136 dev->tt.address = dev->hub->address;
137 dev->tt.port = dev->port;
138 }
139 else {
140 /* Inherit hub's TT */
141 dev->tt = dev->hub->tt;
142 }
143
[2cf28b9]144 /* Calculate route string */
145 xhci_device_t *xhci_hub = xhci_device_get(dev->hub);
146 xhci_dev->tier = xhci_hub->tier + 1;
147 xhci_dev->route_str = xhci_hub->route_str;
148
149 /* Roothub port is not part of the route string */
150 if (xhci_dev->tier >= 2) {
151 const unsigned offset = 4 * (xhci_dev->tier - 2);
152 xhci_dev->route_str |= (dev->port & 0xf) << offset;
[62558202]153 xhci_dev->rh_port = xhci_hub->rh_port;
[2cf28b9]154 }
155
156 fibril_mutex_lock(&bus->base.guard);
[20eaa82]157 /* Assign an address to the device */
[0206d35]158 if ((err = address_device(hc, xhci_dev))) {
[20eaa82]159 usb_log_error("Failed to setup address of the new device: %s", str_error(err));
160 return err;
161 }
162
[0206d35]163 // TODO: Fetch descriptor of EP0 and reconfigure it accordingly
164 assert(xhci_dev->endpoints[0]);
165
[2b61945]166 assert(bus->devices_by_slot[xhci_dev->slot_id] == NULL);
167 bus->devices_by_slot[xhci_dev->slot_id] = xhci_dev;
[2cf28b9]168 fibril_mutex_unlock(&bus->base.guard);
[2b61945]169
[20eaa82]170 /* Read the device descriptor, derive the match ids */
171 if ((err = hcd_ddf_device_explore(hc->hcd, dev))) {
172 usb_log_error("Device(%d): Failed to explore device: %s", dev->address, str_error(err));
[327f147]173 goto err_address;
[20eaa82]174 }
175
176 return EOK;
[2b61945]177
178err_address:
179 bus_release_address(&bus->base, dev->address);
180 return err;
[20eaa82]181}
182
[0206d35]183static int unregister_endpoint(bus_t *, endpoint_t *);
184
[8ea7459]185int xhci_bus_remove_device(xhci_bus_t *bus, xhci_hc_t *hc, device_t *dev)
[20eaa82]186{
[40a3bfa]187 int err;
[2b61945]188 xhci_device_t *xhci_dev = xhci_device_get(dev);
189
[40a3bfa]190 /* Block creation of new endpoints and transfers. */
[9620a54]191 usb_log_debug2("Device " XHCI_DEV_FMT " going offline.", XHCI_DEV_ARGS(*xhci_dev));
[40a3bfa]192 fibril_mutex_lock(&dev->guard);
193 xhci_dev->online = false;
194 fibril_mutex_unlock(&dev->guard);
195
196 /* Abort running transfers. */
[9620a54]197 usb_log_debug2("Aborting all active transfers to device " XHCI_DEV_FMT ".", XHCI_DEV_ARGS(*xhci_dev));
[40a3bfa]198 for (size_t i = 0; i < ARRAY_SIZE(xhci_dev->endpoints); ++i) {
199 xhci_endpoint_t *ep = xhci_dev->endpoints[i];
[17873ac7]200 if (!ep)
[40a3bfa]201 continue;
202
[17873ac7]203 endpoint_abort(&ep->base);
[40a3bfa]204 }
205
206 /* TODO: Figure out how to handle errors here. So far, they are reported and skipped. */
207
208 /* Make DDF (and all drivers) forget about the device. */
209 if ((err = ddf_fun_unbind(dev->fun))) {
[9620a54]210 usb_log_warning("Failed to unbind DDF function of device " XHCI_DEV_FMT ": %s",
211 XHCI_DEV_ARGS(*xhci_dev), str_error(err));
[40a3bfa]212 }
213
[9620a54]214 /* Disable the slot, dropping all endpoints. */
215 const uint32_t slot_id = xhci_dev->slot_id;
216 if ((err = hc_disable_slot(hc, xhci_dev))) {
217 usb_log_warning("Failed to disable slot of device " XHCI_DEV_FMT ": %s",
218 XHCI_DEV_ARGS(*xhci_dev), str_error(err));
219 }
220
221 bus->devices_by_slot[slot_id] = NULL;
222
223 /* Unregister remaining endpoints, freeing memory. */
[0206d35]224 for (unsigned i = 0; i < ARRAY_SIZE(xhci_dev->endpoints); ++i) {
[a4e26882]225 if (!xhci_dev->endpoints[i])
226 continue;
227
[40a3bfa]228 if ((err = unregister_endpoint(&bus->base, &xhci_dev->endpoints[i]->base))) {
[9620a54]229 usb_log_warning("Failed to unregister endpoint " XHCI_EP_FMT ": %s",
230 XHCI_EP_ARGS(*xhci_dev->endpoints[i]), str_error(err));
[40a3bfa]231 }
[a4e26882]232 }
[2b61945]233
[6b2930b]234 /* Destroy DDF device. */
235 /* XXX: Not a good idea, this method should not destroy devices. */
236 hcd_ddf_device_destroy(dev);
237
[31cca4f3]238 return EOK;
[20eaa82]239}
240
[a5976973]241/** Ops receive generic bus_t pointer. */
242static inline xhci_bus_t *bus_to_xhci_bus(bus_t *bus_base)
243{
244 assert(bus_base);
245 return (xhci_bus_t *) bus_base;
246}
247
[8ea7459]248static int enumerate_device(bus_t *bus_base, hcd_t *hcd, device_t *dev)
249{
250 xhci_hc_t *hc = hcd_get_driver_data(hcd);
251 assert(hc);
252
253 xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
254 assert(bus);
255
256 return xhci_bus_enumerate_device(bus, hc, dev);
257}
258
259static int remove_device(bus_t *bus_base, hcd_t *hcd, device_t *dev)
260{
261 xhci_hc_t *hc = hcd_get_driver_data(hcd);
262 assert(hc);
263
264 xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
265 assert(bus);
266
267 return xhci_bus_remove_device(bus, hc, dev);
268}
269
[d37514e]270static int online_device(bus_t *bus_base, hcd_t *hcd, device_t *dev_base)
271{
272 int err;
273
274 xhci_hc_t *hc = hcd_get_driver_data(hcd);
275 assert(hc);
276
277 xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
278 assert(bus);
279
280 xhci_device_t *dev = xhci_device_get(dev_base);
281 assert(dev);
282
[6b2930b]283 /* Transition the device from the Addressed to the Configured state. */
284 if ((err = hc_configure_device(hc, dev->slot_id))) {
[9620a54]285 usb_log_warning("Failed to configure device " XHCI_DEV_FMT ".", XHCI_DEV_ARGS(*dev));
[6b2930b]286 }
287
288 /* Block creation of new endpoints and transfers. */
[9620a54]289 usb_log_debug2("Device " XHCI_DEV_FMT " going online.", XHCI_DEV_ARGS(*dev));
[6b2930b]290 fibril_mutex_lock(&dev_base->guard);
291 dev->online = true;
292 fibril_mutex_unlock(&dev_base->guard);
[d37514e]293
294 if ((err = ddf_fun_online(dev_base->fun))) {
295 return err;
296 }
297
298 return EOK;
299}
300
301static int offline_device(bus_t *bus_base, hcd_t *hcd, device_t *dev_base)
302{
303 int err;
304
305 xhci_hc_t *hc = hcd_get_driver_data(hcd);
306 assert(hc);
307
308 xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
309 assert(bus);
310
311 xhci_device_t *dev = xhci_device_get(dev_base);
312 assert(dev);
313
314 /* Tear down all drivers working with the device. */
315 if ((err = ddf_fun_offline(dev_base->fun))) {
316 return err;
317 }
318
[6b2930b]319 /* Block creation of new endpoints and transfers. */
[9620a54]320 usb_log_debug2("Device " XHCI_DEV_FMT " going offline.", XHCI_DEV_ARGS(*dev));
[6b2930b]321 fibril_mutex_lock(&dev_base->guard);
322 dev->online = false;
323 fibril_mutex_unlock(&dev_base->guard);
324
325 /* We will need the endpoint array later for DS deallocation. */
326 xhci_endpoint_t *endpoints[ARRAY_SIZE(dev->endpoints)];
327 memcpy(endpoints, dev->endpoints, sizeof(dev->endpoints));
328
329 /* Remove all endpoints except zero. */
330 for (unsigned i = 1; i < ARRAY_SIZE(endpoints); ++i) {
331 if (!endpoints[i])
332 continue;
333
334 /* FIXME: Asserting here that the endpoint is not active. If not, EBUSY? */
335
336 xhci_device_remove_endpoint(endpoints[i]);
337 }
338
339 /* Issue one HC command to simultaneously drop all endpoints except zero. */
340 if ((err = hc_deconfigure_device(hc, dev->slot_id))) {
[9620a54]341 usb_log_warning("Failed to deconfigure device " XHCI_DEV_FMT ".",
342 XHCI_DEV_ARGS(*dev));
[6b2930b]343 }
344
345 /* Tear down TRB ring / PSA. */
346 for (unsigned i = 1; i < ARRAY_SIZE(endpoints); ++i) {
347 if (!endpoints[i])
348 continue;
349
[9620a54]350 xhci_endpoint_free_transfer_ds(endpoints[i]);
[6b2930b]351 }
352
353 /* FIXME: What happens to unregistered endpoints now? Destroy them? */
[d37514e]354
355 return EOK;
356}
357
[a5976973]358static endpoint_t *create_endpoint(bus_t *base)
359{
360 xhci_bus_t *bus = bus_to_xhci_bus(base);
361
[2b61945]362 xhci_endpoint_t *ep = calloc(1, sizeof(xhci_endpoint_t));
[a5976973]363 if (!ep)
364 return NULL;
365
366 if (xhci_endpoint_init(ep, bus)) {
367 free(ep);
368 return NULL;
369 }
370
371 return &ep->base;
372}
373
374static void destroy_endpoint(endpoint_t *ep)
375{
376 xhci_endpoint_t *xhci_ep = xhci_endpoint_get(ep);
377
378 xhci_endpoint_fini(xhci_ep);
379 free(xhci_ep);
380}
381
[8b8c164]382static int register_endpoint(bus_t *bus_base, device_t *device, endpoint_t *ep_base, const usb_endpoint_desc_t *desc)
[add878aa]383{
[0206d35]384 int err;
[a8435eb5]385 xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
[8b8c164]386 xhci_endpoint_t *ep = xhci_endpoint_get(ep_base);
[a8435eb5]387
[8b8c164]388 xhci_device_t *dev = xhci_device_get(device);
[56db65d]389
[8b8c164]390 if ((err = prepare_endpoint(ep, desc)))
[0206d35]391 return err;
[56db65d]392
[8b8c164]393 if ((err = xhci_device_add_endpoint(dev, ep)))
394 goto err_prepared;
395
[9620a54]396 usb_log_info("Endpoint " XHCI_EP_FMT " registered to XHCI bus.", XHCI_EP_ARGS(*ep));
[8b8c164]397
398 xhci_ep_ctx_t ep_ctx;
399 xhci_setup_endpoint_context(ep, &ep_ctx);
400
401 if ((err = hc_add_endpoint(bus->hc, dev->slot_id, xhci_endpoint_index(ep), &ep_ctx)))
402 goto err_added;
403
404 return EOK;
405
406err_added:
407 xhci_device_remove_endpoint(ep);
408err_prepared:
409 xhci_endpoint_free_transfer_ds(ep);
410 return err;
[add878aa]411}
412
[8b8c164]413static int unregister_endpoint(bus_t *bus_base, endpoint_t *ep_base)
[add878aa]414{
[8b8c164]415 int err;
[a8435eb5]416 xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
[8b8c164]417 xhci_endpoint_t *ep = xhci_endpoint_get(ep_base);
418 xhci_device_t *dev = xhci_device_get(ep_base->device);
[a8435eb5]419
[9620a54]420 usb_log_info("Endpoint " XHCI_EP_FMT " unregistered from XHCI bus.", XHCI_EP_ARGS(*ep));
[9b2f69e]421
[8b8c164]422 xhci_device_remove_endpoint(ep);
423
[9620a54]424 /* If device slot is still available, drop the endpoint. */
425 if (dev->slot_id) {
426 if ((err = hc_drop_endpoint(bus->hc, dev->slot_id, xhci_endpoint_index(ep)))) {
427 usb_log_error("Failed to drop endpoint " XHCI_EP_FMT ": %s", XHCI_EP_ARGS(*ep), str_error(err));
428 }
429 } else {
430 usb_log_debug("Not going to drop endpoint " XHCI_EP_FMT " because"
431 " the slot has already been disabled.", XHCI_EP_ARGS(*ep));
[8b8c164]432 }
433
434 /* Tear down TRB ring / PSA. */
[9620a54]435 xhci_endpoint_free_transfer_ds(ep);
[c10daa8]436
[a8435eb5]437 return EOK;
[add878aa]438}
439
[327f147]440static endpoint_t* find_endpoint(bus_t *bus_base, device_t *dev_base, usb_target_t target, usb_direction_t direction)
[add878aa]441{
[327f147]442 xhci_device_t *dev = xhci_device_get(dev_base);
[a4e26882]443
[327f147]444 xhci_endpoint_t *ep = xhci_device_get_endpoint(dev, target.endpoint);
445 if (!ep)
[a8435eb5]446 return NULL;
447
[c10daa8]448 return &ep->base;
[add878aa]449}
450
[17873ac7]451static int reset_toggle(bus_t *bus_base, usb_target_t target, toggle_reset_mode_t mode)
[add878aa]452{
453 // TODO: Implement me!
454 return ENOTSUP;
455}
456
457static size_t count_bw(endpoint_t *ep, size_t size)
458{
459 // TODO: Implement me!
460 return 0;
461}
462
463/* Endpoint ops, optional (have generic fallback) */
[e6b9182]464static bool endpoint_get_toggle(endpoint_t *ep)
[add878aa]465{
466 // TODO: Implement me!
467 return ENOTSUP;
468}
469
[e6b9182]470static void endpoint_set_toggle(endpoint_t *ep, bool toggle)
[add878aa]471{
472 // TODO: Implement me!
473}
474
[2cf28b9]475static int request_address(bus_t *bus_base, usb_address_t *addr, bool strict, usb_speed_t speed)
476{
477 assert(addr);
478
479 if (*addr != USB_ADDRESS_DEFAULT)
480 /* xHCI does not allow software to assign addresses. */
481 return ENOTSUP;
482
483 assert(strict);
484
485 xhci_bus_t *xhci_bus = bus_to_xhci_bus(bus_base);
486
487 if (xhci_bus->default_address_speed != USB_SPEED_MAX)
488 /* Already allocated */
489 return ENOENT;
490
491 xhci_bus->default_address_speed = speed;
492 return EOK;
493}
494
495static int release_address(bus_t *bus_base, usb_address_t addr)
496{
497 if (addr != USB_ADDRESS_DEFAULT)
498 return ENOTSUP;
499
500 xhci_bus_t *xhci_bus = bus_to_xhci_bus(bus_base);
501
502 xhci_bus->default_address_speed = USB_SPEED_MAX;
503 return EOK;
504}
505
[5fd9c30]506static usb_transfer_batch_t *create_batch(bus_t *bus, endpoint_t *ep)
507{
508 xhci_transfer_t *transfer = xhci_transfer_create(ep);
509 return &transfer->batch;
510}
511
512static void destroy_batch(usb_transfer_batch_t *batch)
513{
514 xhci_transfer_destroy(xhci_transfer_from_batch(batch));
515}
516
[a5976973]517static const bus_ops_t xhci_bus_ops = {
[9868982]518#define BIND_OP(op) .op = op,
519 BIND_OP(enumerate_device)
520 BIND_OP(remove_device)
[20eaa82]521
[9868982]522 BIND_OP(online_device)
523 BIND_OP(offline_device)
[d37514e]524
[9868982]525 BIND_OP(create_endpoint)
526 BIND_OP(destroy_endpoint)
[a5976973]527
[9868982]528 BIND_OP(register_endpoint)
529 BIND_OP(unregister_endpoint)
530 BIND_OP(find_endpoint)
[a5976973]531
[9868982]532 BIND_OP(request_address)
533 BIND_OP(release_address)
534 BIND_OP(reset_toggle)
[a5976973]535
[9868982]536 BIND_OP(count_bw)
[a5976973]537
[9868982]538 BIND_OP(endpoint_get_toggle)
539 BIND_OP(endpoint_set_toggle)
[5fd9c30]540
[9868982]541 BIND_OP(create_batch)
542 BIND_OP(destroy_batch)
543#undef BIND_OP
[a5976973]544};
545
[2b61945]546int xhci_bus_init(xhci_bus_t *bus, xhci_hc_t *hc)
[a5976973]547{
548 assert(bus);
549
[2b61945]550 bus_init(&bus->base, sizeof(xhci_device_t));
551
552 bus->devices_by_slot = calloc(hc->max_slots, sizeof(xhci_device_t *));
553 if (!bus->devices_by_slot)
554 return ENOMEM;
[a5976973]555
[25251bb]556 bus->hc = hc;
[a5976973]557 bus->base.ops = xhci_bus_ops;
[2cf28b9]558 bus->default_address_speed = USB_SPEED_MAX;
[a5976973]559 return EOK;
560}
[a8435eb5]561
562void xhci_bus_fini(xhci_bus_t *bus)
563{
[c10daa8]564
[a8435eb5]565}
[20eaa82]566
[a5976973]567/**
568 * @}
569 */
Note: See TracBrowser for help on using the repository browser.