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

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

usbhost: refactoring

This commit moves interrupt, status and schedule to bus
operations. Then the purpose of hcd_t is better defined, and split into
hc_driver_t and hc_device_t. hc_driver_t is used to wrap driver
implementation by the library (similar to how usb_driver_t is used to
wrap usb device drivers). hc_device_t is used as a parent for hc_t
inside drivers, and is allocated inside the DDF device node.

To support these changes, some local identifiers were renamed, some
functions were moved and/or renamed and their arguments changed. The
most notable one being hcd_send_batch → bus_device_send_batch.

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