[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 |
|
---|
[20eaa82] | 35 | #include <usb/host/ddf_helpers.h>
|
---|
[a5976973] | 36 | #include <usb/host/endpoint.h>
|
---|
[20eaa82] | 37 | #include <usb/host/hcd.h>
|
---|
[a5976973] | 38 | #include <usb/debug.h>
|
---|
| 39 |
|
---|
| 40 | #include <assert.h>
|
---|
| 41 | #include <errno.h>
|
---|
[20eaa82] | 42 | #include <str_error.h>
|
---|
[a5976973] | 43 | #include <macros.h>
|
---|
| 44 | #include <stdbool.h>
|
---|
| 45 |
|
---|
[2cf28b9] | 46 | #include "hc.h"
|
---|
[a5976973] | 47 | #include "bus.h"
|
---|
| 48 | #include "endpoint.h"
|
---|
[5fd9c30] | 49 | #include "transfers.h"
|
---|
[a5976973] | 50 |
|
---|
[0206d35] | 51 |
|
---|
[306a36d] | 52 | static const usb_endpoint_desc_t ep0_initial_desc = {
|
---|
[0206d35] | 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 |
|
---|
[6832245] | 60 | static endpoint_t *endpoint_create(device_t *, const usb_endpoint_desc_t *);
|
---|
[0206d35] | 61 |
|
---|
[6832245] | 62 | static int address_device(xhci_bus_t *bus, xhci_device_t *dev)
|
---|
[0206d35] | 63 | {
|
---|
| 64 | int err;
|
---|
| 65 |
|
---|
| 66 | /* Enable new slot. */
|
---|
[6832245] | 67 | if ((err = hc_enable_slot(bus->hc, &dev->slot_id)) != EOK)
|
---|
[0206d35] | 68 | return err;
|
---|
| 69 | usb_log_debug2("Obtained slot ID: %u.\n", dev->slot_id);
|
---|
| 70 |
|
---|
| 71 | /* Create and configure control endpoint. */
|
---|
[6832245] | 72 | endpoint_t *ep0_base = endpoint_create(&dev->base, &ep0_initial_desc);
|
---|
[0206d35] | 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 |
|
---|
[6832245] | 81 | if ((err = xhci_endpoint_alloc_transfer_ds(ep0)))
|
---|
[0206d35] | 82 | goto err_ep;
|
---|
| 83 |
|
---|
[8b8c164] | 84 | /* Register EP0 */
|
---|
| 85 | if ((err = xhci_device_add_endpoint(dev, ep0)))
|
---|
| 86 | goto err_prepared;
|
---|
| 87 |
|
---|
[0206d35] | 88 | /* Address device */
|
---|
[6832245] | 89 | if ((err = hc_address_device(bus->hc, dev, ep0)))
|
---|
[8b8c164] | 90 | goto err_added;
|
---|
[0206d35] | 91 |
|
---|
[8b8c164] | 92 | /* Temporary reference */
|
---|
| 93 | endpoint_del_ref(ep0_base);
|
---|
[0206d35] | 94 | return EOK;
|
---|
| 95 |
|
---|
[8b8c164] | 96 | err_added:
|
---|
| 97 | xhci_device_remove_endpoint(ep0);
|
---|
| 98 | err_prepared:
|
---|
[0206d35] | 99 | xhci_endpoint_free_transfer_ds(ep0);
|
---|
| 100 | err_ep:
|
---|
[8b8c164] | 101 | /* Temporary reference */
|
---|
[0206d35] | 102 | endpoint_del_ref(ep0_base);
|
---|
| 103 | err_slot:
|
---|
[6832245] | 104 | hc_disable_slot(bus->hc, dev);
|
---|
[0206d35] | 105 | return err;
|
---|
| 106 | }
|
---|
| 107 |
|
---|
[306a36d] | 108 | static int setup_ep0_packet_size(xhci_hc_t *hc, xhci_device_t *dev)
|
---|
| 109 | {
|
---|
| 110 | int err;
|
---|
| 111 |
|
---|
| 112 | uint16_t max_packet_size;
|
---|
[32fb6bce] | 113 | if ((err = hcd_get_ep0_max_packet_size(&max_packet_size, (bus_t *) &hc->bus, &dev->base)))
|
---|
[306a36d] | 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 |
|
---|
[6832245] | 133 | int xhci_bus_enumerate_device(xhci_bus_t *bus, device_t *dev)
|
---|
[20eaa82] | 134 | {
|
---|
| 135 | int err;
|
---|
[2b61945] | 136 | xhci_device_t *xhci_dev = xhci_device_get(dev);
|
---|
[20eaa82] | 137 |
|
---|
[ff14aede] | 138 | hcd_setup_device_tt(dev);
|
---|
[20eaa82] | 139 |
|
---|
[2cf28b9] | 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;
|
---|
[62558202] | 149 | xhci_dev->rh_port = xhci_hub->rh_port;
|
---|
[2cf28b9] | 150 | }
|
---|
| 151 |
|
---|
[20eaa82] | 152 | /* Assign an address to the device */
|
---|
[6832245] | 153 | if ((err = address_device(bus, xhci_dev))) {
|
---|
[20eaa82] | 154 | usb_log_error("Failed to setup address of the new device: %s", str_error(err));
|
---|
| 155 | return err;
|
---|
| 156 | }
|
---|
| 157 |
|
---|
[53db806] | 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 |
|
---|
[6832245] | 164 | if ((err = setup_ep0_packet_size(bus->hc, xhci_dev))) {
|
---|
[306a36d] | 165 | usb_log_error("Failed to setup control endpoint of the new device: %s", str_error(err));
|
---|
| 166 | goto err_address;
|
---|
| 167 | }
|
---|
[0206d35] | 168 |
|
---|
[20eaa82] | 169 | /* Read the device descriptor, derive the match ids */
|
---|
[32fb6bce] | 170 | if ((err = hcd_device_explore(dev))) {
|
---|
[20eaa82] | 171 | usb_log_error("Device(%d): Failed to explore device: %s", dev->address, str_error(err));
|
---|
[327f147] | 172 | goto err_address;
|
---|
[20eaa82] | 173 | }
|
---|
| 174 |
|
---|
| 175 | return EOK;
|
---|
[2b61945] | 176 |
|
---|
| 177 | err_address:
|
---|
[10cd715] | 178 | // TODO: deaddress device
|
---|
[2b61945] | 179 | return err;
|
---|
[20eaa82] | 180 | }
|
---|
| 181 |
|
---|
[6832245] | 182 | static int endpoint_unregister(endpoint_t *);
|
---|
[0206d35] | 183 |
|
---|
[6832245] | 184 | int xhci_bus_remove_device(xhci_bus_t *bus, device_t *dev)
|
---|
[20eaa82] | 185 | {
|
---|
[40a3bfa] | 186 | int err;
|
---|
[2b61945] | 187 | xhci_device_t *xhci_dev = xhci_device_get(dev);
|
---|
| 188 |
|
---|
[40a3bfa] | 189 | /* Block creation of new endpoints and transfers. */
|
---|
[9620a54] | 190 | usb_log_debug2("Device " XHCI_DEV_FMT " going offline.", XHCI_DEV_ARGS(*xhci_dev));
|
---|
[40a3bfa] | 191 | fibril_mutex_lock(&dev->guard);
|
---|
| 192 | xhci_dev->online = false;
|
---|
| 193 | fibril_mutex_unlock(&dev->guard);
|
---|
| 194 |
|
---|
| 195 | /* Abort running transfers. */
|
---|
[9620a54] | 196 | usb_log_debug2("Aborting all active transfers to device " XHCI_DEV_FMT ".", XHCI_DEV_ARGS(*xhci_dev));
|
---|
[40a3bfa] | 197 | for (size_t i = 0; i < ARRAY_SIZE(xhci_dev->endpoints); ++i) {
|
---|
| 198 | xhci_endpoint_t *ep = xhci_dev->endpoints[i];
|
---|
[17873ac7] | 199 | if (!ep)
|
---|
[40a3bfa] | 200 | continue;
|
---|
| 201 |
|
---|
[17873ac7] | 202 | endpoint_abort(&ep->base);
|
---|
[40a3bfa] | 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))) {
|
---|
[9620a54] | 209 | usb_log_warning("Failed to unbind DDF function of device " XHCI_DEV_FMT ": %s",
|
---|
| 210 | XHCI_DEV_ARGS(*xhci_dev), str_error(err));
|
---|
[40a3bfa] | 211 | }
|
---|
| 212 |
|
---|
[9620a54] | 213 | /* Disable the slot, dropping all endpoints. */
|
---|
| 214 | const uint32_t slot_id = xhci_dev->slot_id;
|
---|
[6832245] | 215 | if ((err = hc_disable_slot(bus->hc, xhci_dev))) {
|
---|
[9620a54] | 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. */
|
---|
[0206d35] | 223 | for (unsigned i = 0; i < ARRAY_SIZE(xhci_dev->endpoints); ++i) {
|
---|
[a4e26882] | 224 | if (!xhci_dev->endpoints[i])
|
---|
| 225 | continue;
|
---|
| 226 |
|
---|
[6832245] | 227 | if ((err = endpoint_unregister(&xhci_dev->endpoints[i]->base))) {
|
---|
[9620a54] | 228 | usb_log_warning("Failed to unregister endpoint " XHCI_EP_FMT ": %s",
|
---|
| 229 | XHCI_EP_ARGS(*xhci_dev->endpoints[i]), str_error(err));
|
---|
[40a3bfa] | 230 | }
|
---|
[a4e26882] | 231 | }
|
---|
[2b61945] | 232 |
|
---|
[6b2930b] | 233 | /* Destroy DDF device. */
|
---|
| 234 | /* XXX: Not a good idea, this method should not destroy devices. */
|
---|
[32fb6bce] | 235 | hcd_ddf_fun_destroy(dev);
|
---|
[6b2930b] | 236 |
|
---|
[31cca4f3] | 237 | return EOK;
|
---|
[20eaa82] | 238 | }
|
---|
| 239 |
|
---|
[a5976973] | 240 | /** Ops receive generic bus_t pointer. */
|
---|
| 241 | static 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 |
|
---|
[6832245] | 247 | static int device_enumerate(device_t *dev)
|
---|
[8ea7459] | 248 | {
|
---|
[6832245] | 249 | xhci_bus_t *bus = bus_to_xhci_bus(dev->bus);
|
---|
| 250 | return xhci_bus_enumerate_device(bus, dev);
|
---|
[8ea7459] | 251 | }
|
---|
| 252 |
|
---|
[6832245] | 253 | static int device_remove(device_t *dev)
|
---|
[8ea7459] | 254 | {
|
---|
[6832245] | 255 | xhci_bus_t *bus = bus_to_xhci_bus(dev->bus);
|
---|
| 256 | return xhci_bus_remove_device(bus, dev);
|
---|
[8ea7459] | 257 | }
|
---|
| 258 |
|
---|
[6832245] | 259 | static int device_online(device_t *dev_base)
|
---|
[d37514e] | 260 | {
|
---|
| 261 | int err;
|
---|
| 262 |
|
---|
[6832245] | 263 | xhci_bus_t *bus = bus_to_xhci_bus(dev_base->bus);
|
---|
[d37514e] | 264 | assert(bus);
|
---|
| 265 |
|
---|
| 266 | xhci_device_t *dev = xhci_device_get(dev_base);
|
---|
| 267 | assert(dev);
|
---|
| 268 |
|
---|
[6b2930b] | 269 | /* Transition the device from the Addressed to the Configured state. */
|
---|
[6832245] | 270 | if ((err = hc_configure_device(bus->hc, dev->slot_id))) {
|
---|
[9620a54] | 271 | usb_log_warning("Failed to configure device " XHCI_DEV_FMT ".", XHCI_DEV_ARGS(*dev));
|
---|
[6b2930b] | 272 | }
|
---|
| 273 |
|
---|
| 274 | /* Block creation of new endpoints and transfers. */
|
---|
[9620a54] | 275 | usb_log_debug2("Device " XHCI_DEV_FMT " going online.", XHCI_DEV_ARGS(*dev));
|
---|
[6b2930b] | 276 | fibril_mutex_lock(&dev_base->guard);
|
---|
| 277 | dev->online = true;
|
---|
| 278 | fibril_mutex_unlock(&dev_base->guard);
|
---|
[d37514e] | 279 |
|
---|
| 280 | if ((err = ddf_fun_online(dev_base->fun))) {
|
---|
| 281 | return err;
|
---|
| 282 | }
|
---|
| 283 |
|
---|
| 284 | return EOK;
|
---|
| 285 | }
|
---|
| 286 |
|
---|
[6832245] | 287 | static int device_offline(device_t *dev_base)
|
---|
[d37514e] | 288 | {
|
---|
| 289 | int err;
|
---|
| 290 |
|
---|
[6832245] | 291 | xhci_bus_t *bus = bus_to_xhci_bus(dev_base->bus);
|
---|
[d37514e] | 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 |
|
---|
[6b2930b] | 302 | /* Block creation of new endpoints and transfers. */
|
---|
[9620a54] | 303 | usb_log_debug2("Device " XHCI_DEV_FMT " going offline.", XHCI_DEV_ARGS(*dev));
|
---|
[6b2930b] | 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. */
|
---|
[6832245] | 323 | if ((err = hc_deconfigure_device(bus->hc, dev->slot_id))) {
|
---|
[9620a54] | 324 | usb_log_warning("Failed to deconfigure device " XHCI_DEV_FMT ".",
|
---|
| 325 | XHCI_DEV_ARGS(*dev));
|
---|
[6b2930b] | 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 |
|
---|
[9620a54] | 333 | xhci_endpoint_free_transfer_ds(endpoints[i]);
|
---|
[6b2930b] | 334 | }
|
---|
| 335 |
|
---|
| 336 | /* FIXME: What happens to unregistered endpoints now? Destroy them? */
|
---|
[d37514e] | 337 |
|
---|
| 338 | return EOK;
|
---|
| 339 | }
|
---|
| 340 |
|
---|
[6832245] | 341 | static endpoint_t *endpoint_create(device_t *dev, const usb_endpoint_desc_t *desc)
|
---|
[a5976973] | 342 | {
|
---|
[2b61945] | 343 | xhci_endpoint_t *ep = calloc(1, sizeof(xhci_endpoint_t));
|
---|
[a5976973] | 344 | if (!ep)
|
---|
| 345 | return NULL;
|
---|
| 346 |
|
---|
[6832245] | 347 | if (xhci_endpoint_init(ep, dev, desc)) {
|
---|
[a5976973] | 348 | free(ep);
|
---|
| 349 | return NULL;
|
---|
| 350 | }
|
---|
| 351 |
|
---|
| 352 | return &ep->base;
|
---|
| 353 | }
|
---|
| 354 |
|
---|
[6832245] | 355 | static void endpoint_destroy(endpoint_t *ep)
|
---|
[a5976973] | 356 | {
|
---|
| 357 | xhci_endpoint_t *xhci_ep = xhci_endpoint_get(ep);
|
---|
| 358 |
|
---|
| 359 | xhci_endpoint_fini(xhci_ep);
|
---|
| 360 | free(xhci_ep);
|
---|
| 361 | }
|
---|
| 362 |
|
---|
[6832245] | 363 | static int endpoint_register(endpoint_t *ep_base)
|
---|
[add878aa] | 364 | {
|
---|
[0206d35] | 365 | int err;
|
---|
[6832245] | 366 | xhci_bus_t *bus = bus_to_xhci_bus(endpoint_get_bus(ep_base));
|
---|
[8b8c164] | 367 | xhci_endpoint_t *ep = xhci_endpoint_get(ep_base);
|
---|
[a8435eb5] | 368 |
|
---|
[6832245] | 369 | xhci_device_t *dev = xhci_device_get(ep_base->device);
|
---|
[56db65d] | 370 |
|
---|
[6832245] | 371 | if ((err = xhci_endpoint_alloc_transfer_ds(ep)))
|
---|
[0206d35] | 372 | return err;
|
---|
[56db65d] | 373 |
|
---|
[8b8c164] | 374 | if ((err = xhci_device_add_endpoint(dev, ep)))
|
---|
| 375 | goto err_prepared;
|
---|
| 376 |
|
---|
[9620a54] | 377 | usb_log_info("Endpoint " XHCI_EP_FMT " registered to XHCI bus.", XHCI_EP_ARGS(*ep));
|
---|
[8b8c164] | 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 |
|
---|
| 387 | err_added:
|
---|
| 388 | xhci_device_remove_endpoint(ep);
|
---|
| 389 | err_prepared:
|
---|
| 390 | xhci_endpoint_free_transfer_ds(ep);
|
---|
| 391 | return err;
|
---|
[add878aa] | 392 | }
|
---|
| 393 |
|
---|
[6832245] | 394 | static int endpoint_unregister(endpoint_t *ep_base)
|
---|
[add878aa] | 395 | {
|
---|
[8b8c164] | 396 | int err;
|
---|
[6832245] | 397 | xhci_bus_t *bus = bus_to_xhci_bus(endpoint_get_bus(ep_base));
|
---|
[8b8c164] | 398 | xhci_endpoint_t *ep = xhci_endpoint_get(ep_base);
|
---|
| 399 | xhci_device_t *dev = xhci_device_get(ep_base->device);
|
---|
[a8435eb5] | 400 |
|
---|
[9620a54] | 401 | usb_log_info("Endpoint " XHCI_EP_FMT " unregistered from XHCI bus.", XHCI_EP_ARGS(*ep));
|
---|
[9b2f69e] | 402 |
|
---|
[8b8c164] | 403 | xhci_device_remove_endpoint(ep);
|
---|
| 404 |
|
---|
[9620a54] | 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));
|
---|
[8b8c164] | 413 | }
|
---|
| 414 |
|
---|
| 415 | /* Tear down TRB ring / PSA. */
|
---|
[9620a54] | 416 | xhci_endpoint_free_transfer_ds(ep);
|
---|
[c10daa8] | 417 |
|
---|
[a8435eb5] | 418 | return EOK;
|
---|
[add878aa] | 419 | }
|
---|
| 420 |
|
---|
[6832245] | 421 | static endpoint_t* device_find_endpoint(device_t *dev_base, usb_target_t target, usb_direction_t direction)
|
---|
[add878aa] | 422 | {
|
---|
[327f147] | 423 | xhci_device_t *dev = xhci_device_get(dev_base);
|
---|
[a4e26882] | 424 |
|
---|
[327f147] | 425 | xhci_endpoint_t *ep = xhci_device_get_endpoint(dev, target.endpoint);
|
---|
| 426 | if (!ep)
|
---|
[a8435eb5] | 427 | return NULL;
|
---|
| 428 |
|
---|
[c10daa8] | 429 | return &ep->base;
|
---|
[add878aa] | 430 | }
|
---|
| 431 |
|
---|
[17873ac7] | 432 | static int reset_toggle(bus_t *bus_base, usb_target_t target, toggle_reset_mode_t mode)
|
---|
[add878aa] | 433 | {
|
---|
| 434 | // TODO: Implement me!
|
---|
| 435 | return ENOTSUP;
|
---|
| 436 | }
|
---|
| 437 |
|
---|
| 438 | /* Endpoint ops, optional (have generic fallback) */
|
---|
[e6b9182] | 439 | static bool endpoint_get_toggle(endpoint_t *ep)
|
---|
[add878aa] | 440 | {
|
---|
| 441 | // TODO: Implement me!
|
---|
| 442 | return ENOTSUP;
|
---|
| 443 | }
|
---|
| 444 |
|
---|
[e6b9182] | 445 | static void endpoint_set_toggle(endpoint_t *ep, bool toggle)
|
---|
[add878aa] | 446 | {
|
---|
| 447 | // TODO: Implement me!
|
---|
| 448 | }
|
---|
| 449 |
|
---|
[10cd715] | 450 | static int reserve_default_address(bus_t *bus_base, usb_speed_t speed)
|
---|
[2cf28b9] | 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 |
|
---|
[10cd715] | 462 | static int release_default_address(bus_t *bus_base)
|
---|
[2cf28b9] | 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 |
|
---|
[6832245] | 470 | static usb_transfer_batch_t *batch_create(endpoint_t *ep)
|
---|
[5fd9c30] | 471 | {
|
---|
| 472 | xhci_transfer_t *transfer = xhci_transfer_create(ep);
|
---|
| 473 | return &transfer->batch;
|
---|
| 474 | }
|
---|
| 475 |
|
---|
[6832245] | 476 | static void batch_destroy(usb_transfer_batch_t *batch)
|
---|
[5fd9c30] | 477 | {
|
---|
| 478 | xhci_transfer_destroy(xhci_transfer_from_batch(batch));
|
---|
| 479 | }
|
---|
| 480 |
|
---|
[a5976973] | 481 | static const bus_ops_t xhci_bus_ops = {
|
---|
[9868982] | 482 | #define BIND_OP(op) .op = op,
|
---|
[10cd715] | 483 | BIND_OP(reserve_default_address)
|
---|
| 484 | BIND_OP(release_default_address)
|
---|
| 485 | BIND_OP(reset_toggle)
|
---|
[a5976973] | 486 |
|
---|
[6832245] | 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)
|
---|
[9868982] | 497 | BIND_OP(endpoint_get_toggle)
|
---|
| 498 | BIND_OP(endpoint_set_toggle)
|
---|
[5fd9c30] | 499 |
|
---|
[6832245] | 500 | BIND_OP(batch_create)
|
---|
| 501 | BIND_OP(batch_destroy)
|
---|
[9868982] | 502 | #undef BIND_OP
|
---|
[32fb6bce] | 503 |
|
---|
| 504 | .interrupt = hc_interrupt,
|
---|
| 505 | .status = hc_status,
|
---|
| 506 | .batch_schedule = hc_schedule,
|
---|
[a5976973] | 507 | };
|
---|
| 508 |
|
---|
[2b61945] | 509 | int xhci_bus_init(xhci_bus_t *bus, xhci_hc_t *hc)
|
---|
[a5976973] | 510 | {
|
---|
| 511 | assert(bus);
|
---|
| 512 |
|
---|
[32fb6bce] | 513 | bus_init(&bus->base, sizeof(xhci_device_t));
|
---|
[2b61945] | 514 |
|
---|
| 515 | bus->devices_by_slot = calloc(hc->max_slots, sizeof(xhci_device_t *));
|
---|
| 516 | if (!bus->devices_by_slot)
|
---|
| 517 | return ENOMEM;
|
---|
[a5976973] | 518 |
|
---|
[25251bb] | 519 | bus->hc = hc;
|
---|
[6832245] | 520 | bus->base.ops = &xhci_bus_ops;
|
---|
[2cf28b9] | 521 | bus->default_address_speed = USB_SPEED_MAX;
|
---|
[a5976973] | 522 | return EOK;
|
---|
| 523 | }
|
---|
[a8435eb5] | 524 |
|
---|
| 525 | void xhci_bus_fini(xhci_bus_t *bus)
|
---|
| 526 | {
|
---|
[c10daa8] | 527 |
|
---|
[a8435eb5] | 528 | }
|
---|
[20eaa82] | 529 |
|
---|
[a5976973] | 530 | /**
|
---|
| 531 | * @}
|
---|
| 532 | */
|
---|