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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 889146e was 6b433a8, checked in by Salmelu <salmelu@…>, 8 years ago

Isochronous transfers - endpoint initialization

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