bus.c 17.4 KB
Newer Older
G
Gerd Hoffmann 已提交
1 2 3
#include "hw/hw.h"
#include "hw/usb.h"
#include "hw/qdev.h"
4
#include "sysemu/sysemu.h"
5
#include "monitor/monitor.h"
6
#include "trace.h"
7 8

static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent);
9 10

static char *usb_get_dev_path(DeviceState *dev);
11
static char *usb_get_fw_dev_path(DeviceState *qdev);
12
static int usb_qdev_exit(DeviceState *qdev);
13

14 15
static Property usb_props[] = {
    DEFINE_PROP_STRING("port", USBDevice, port_path),
G
Gerd Hoffmann 已提交
16
    DEFINE_PROP_STRING("serial", USBDevice, serial),
17 18 19 20 21
    DEFINE_PROP_BIT("full-path", USBDevice, flags,
                    USB_DEV_FLAG_FULL_PATH, true),
    DEFINE_PROP_END_OF_LIST()
};

22 23 24 25 26 27 28 29 30 31 32 33 34 35
static void usb_bus_class_init(ObjectClass *klass, void *data)
{
    BusClass *k = BUS_CLASS(klass);

    k->print_dev = usb_bus_dev_print;
    k->get_dev_path = usb_get_dev_path;
    k->get_fw_dev_path = usb_get_fw_dev_path;
}

static const TypeInfo usb_bus_info = {
    .name = TYPE_USB_BUS,
    .parent = TYPE_BUS,
    .instance_size = sizeof(USBBus),
    .class_init = usb_bus_class_init,
36
};
37

38
static int next_usb_bus = 0;
B
Blue Swirl 已提交
39
static QTAILQ_HEAD(, USBBus) busses = QTAILQ_HEAD_INITIALIZER(busses);
40

41 42 43 44 45 46 47 48 49 50 51 52
static int usb_device_post_load(void *opaque, int version_id)
{
    USBDevice *dev = opaque;

    if (dev->state == USB_STATE_NOTATTACHED) {
        dev->attached = 0;
    } else {
        dev->attached = 1;
    }
    return 0;
}

G
Gerd Hoffmann 已提交
53 54 55 56
const VMStateDescription vmstate_usb_device = {
    .name = "USBDevice",
    .version_id = 1,
    .minimum_version_id = 1,
57
    .post_load = usb_device_post_load,
G
Gerd Hoffmann 已提交
58 59 60 61 62 63 64 65 66 67 68 69
    .fields = (VMStateField []) {
        VMSTATE_UINT8(addr, USBDevice),
        VMSTATE_INT32(state, USBDevice),
        VMSTATE_INT32(remote_wakeup, USBDevice),
        VMSTATE_INT32(setup_state, USBDevice),
        VMSTATE_INT32(setup_len, USBDevice),
        VMSTATE_INT32(setup_index, USBDevice),
        VMSTATE_UINT8_ARRAY(setup_buf, USBDevice, 8),
        VMSTATE_END_OF_LIST(),
    }
};

70
void usb_bus_new(USBBus *bus, USBBusOps *ops, DeviceState *host)
71
{
72
    qbus_create_inplace(&bus->qbus, TYPE_USB_BUS, host, NULL);
73
    bus->ops = ops;
74
    bus->busnr = next_usb_bus++;
G
Gerd Hoffmann 已提交
75
    bus->qbus.allow_hotplug = 1; /* Yes, we can */
B
Blue Swirl 已提交
76 77 78
    QTAILQ_INIT(&bus->free);
    QTAILQ_INIT(&bus->used);
    QTAILQ_INSERT_TAIL(&busses, bus, next);
79 80 81 82 83 84 85
}

USBBus *usb_bus_find(int busnr)
{
    USBBus *bus;

    if (-1 == busnr)
B
Blue Swirl 已提交
86 87
        return QTAILQ_FIRST(&busses);
    QTAILQ_FOREACH(bus, &busses, next) {
88 89 90 91 92 93
        if (bus->busnr == busnr)
            return bus;
    }
    return NULL;
}

94 95 96 97 98 99 100 101 102
static int usb_device_init(USBDevice *dev)
{
    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
    if (klass->init) {
        return klass->init(dev);
    }
    return 0;
}

G
Gerd Hoffmann 已提交
103
USBDevice *usb_device_find_device(USBDevice *dev, uint8_t addr)
104 105
{
    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
G
Gerd Hoffmann 已提交
106 107
    if (klass->find_device) {
        return klass->find_device(dev, addr);
108
    }
G
Gerd Hoffmann 已提交
109
    return NULL;
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
}

static void usb_device_handle_destroy(USBDevice *dev)
{
    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
    if (klass->handle_destroy) {
        klass->handle_destroy(dev);
    }
}

void usb_device_cancel_packet(USBDevice *dev, USBPacket *p)
{
    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
    if (klass->cancel_packet) {
        klass->cancel_packet(dev, p);
    }
}

void usb_device_handle_attach(USBDevice *dev)
{
    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
    if (klass->handle_attach) {
        klass->handle_attach(dev);
    }
}

void usb_device_handle_reset(USBDevice *dev)
{
    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
    if (klass->handle_reset) {
        klass->handle_reset(dev);
    }
}

144 145
void usb_device_handle_control(USBDevice *dev, USBPacket *p, int request,
                               int value, int index, int length, uint8_t *data)
146 147 148
{
    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
    if (klass->handle_control) {
149
        klass->handle_control(dev, p, request, value, index, length, data);
150 151 152
    }
}

153
void usb_device_handle_data(USBDevice *dev, USBPacket *p)
154 155 156
{
    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
    if (klass->handle_data) {
157
        klass->handle_data(dev, p);
158 159 160 161 162 163 164 165 166 167 168 169
    }
}

const char *usb_device_get_product_desc(USBDevice *dev)
{
    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
    return klass->product_desc;
}

const USBDesc *usb_device_get_usb_desc(USBDevice *dev)
{
    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
170 171 172
    if (dev->usb_desc) {
        return dev->usb_desc;
    }
173 174 175 176 177 178 179 180 181 182 183 184
    return klass->usb_desc;
}

void usb_device_set_interface(USBDevice *dev, int interface,
                              int alt_old, int alt_new)
{
    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
    if (klass->set_interface) {
        klass->set_interface(dev, interface, alt_old, alt_new);
    }
}

185 186 187 188 189 190 191 192
void usb_device_flush_ep_queue(USBDevice *dev, USBEndpoint *ep)
{
    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
    if (klass->flush_ep_queue) {
        klass->flush_ep_queue(dev, ep);
    }
}

193 194 195 196 197 198 199 200
void usb_device_ep_stopped(USBDevice *dev, USBEndpoint *ep)
{
    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
    if (klass->ep_stopped) {
        klass->ep_stopped(dev, ep);
    }
}

A
Anthony Liguori 已提交
201
static int usb_qdev_init(DeviceState *qdev)
202
{
203
    USBDevice *dev = USB_DEVICE(qdev);
204 205
    int rc;

206 207
    pstrcpy(dev->product_desc, sizeof(dev->product_desc),
            usb_device_get_product_desc(dev));
G
Gerd Hoffmann 已提交
208
    dev->auto_attach = 1;
209
    QLIST_INIT(&dev->strings);
G
Gerd Hoffmann 已提交
210
    usb_ep_init(dev);
211
    rc = usb_claim_port(dev);
212
    if (rc != 0) {
213
        return rc;
214
    }
215
    rc = usb_device_init(dev);
216
    if (rc != 0) {
217 218
        usb_release_port(dev);
        return rc;
219 220
    }
    if (dev->auto_attach) {
221
        rc = usb_device_attach(dev);
222
        if (rc != 0) {
223 224
            usb_qdev_exit(qdev);
            return rc;
225
        }
226
    }
227
    return 0;
228 229
}

230 231
static int usb_qdev_exit(DeviceState *qdev)
{
232
    USBDevice *dev = USB_DEVICE(qdev);
233

234 235 236
    if (dev->attached) {
        usb_device_detach(dev);
    }
237
    usb_device_handle_destroy(dev);
238 239 240
    if (dev->port) {
        usb_release_port(dev);
    }
241 242 243
    return 0;
}

244
typedef struct LegacyUSBFactory
245
{
246 247
    const char *name;
    const char *usbdevice_name;
248
    USBDevice *(*usbdevice_init)(USBBus *bus, const char *params);
249
} LegacyUSBFactory;
250

251 252
static GSList *legacy_usb_factory;

253
void usb_legacy_register(const char *typename, const char *usbdevice_name,
254 255
                         USBDevice *(*usbdevice_init)(USBBus *bus,
                                                      const char *params))
256
{
257 258
    if (usbdevice_name) {
        LegacyUSBFactory *f = g_malloc0(sizeof(*f));
259
        f->name = typename;
260 261 262
        f->usbdevice_name = usbdevice_name;
        f->usbdevice_init = usbdevice_init;
        legacy_usb_factory = g_slist_append(legacy_usb_factory, f);
263 264 265
    }
}

266
USBDevice *usb_create(USBBus *bus, const char *name)
267 268 269 270
{
    DeviceState *dev;

    dev = qdev_create(&bus->qbus, name);
271
    return USB_DEVICE(dev);
272
}
273 274 275 276

USBDevice *usb_create_simple(USBBus *bus, const char *name)
{
    USBDevice *dev = usb_create(bus, name);
277 278
    int rc;

P
Paul Brook 已提交
279
    if (!dev) {
280
        error_report("Failed to create USB device '%s'", name);
281 282 283 284
        return NULL;
    }
    rc = qdev_init(&dev->qdev);
    if (rc < 0) {
285
        error_report("Failed to initialize USB device '%s'", name);
286
        return NULL;
P
Paul Brook 已提交
287
    }
288 289 290
    return dev;
}

291 292
static void usb_fill_port(USBPort *port, void *opaque, int index,
                          USBPortOps *ops, int speedmask)
293
{
294 295 296
    port->opaque = opaque;
    port->index = index;
    port->ops = ops;
G
Gerd Hoffmann 已提交
297
    port->speedmask = speedmask;
298
    usb_port_location(port, NULL, index + 1);
299 300 301 302 303 304
}

void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index,
                       USBPortOps *ops, int speedmask)
{
    usb_fill_port(port, opaque, index, ops, speedmask);
B
Blue Swirl 已提交
305
    QTAILQ_INSERT_TAIL(&bus->free, port, next);
306 307 308
    bus->nfree++;
}

309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
int usb_register_companion(const char *masterbus, USBPort *ports[],
                           uint32_t portcount, uint32_t firstport,
                           void *opaque, USBPortOps *ops, int speedmask)
{
    USBBus *bus;
    int i;

    QTAILQ_FOREACH(bus, &busses, next) {
        if (strcmp(bus->qbus.name, masterbus) == 0) {
            break;
        }
    }

    if (!bus || !bus->ops->register_companion) {
        qerror_report(QERR_INVALID_PARAMETER_VALUE, "masterbus",
                      "an USB masterbus");
        if (bus) {
            error_printf_unless_qmp(
                "USB bus '%s' does not allow companion controllers\n",
                masterbus);
        }
        return -1;
    }

    for (i = 0; i < portcount; i++) {
        usb_fill_port(ports[i], opaque, i, ops, speedmask);
    }

    return bus->ops->register_companion(bus, ports, portcount, firstport);
}

340 341 342 343 344
void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr)
{
    if (upstream) {
        snprintf(downstream->path, sizeof(downstream->path), "%s.%d",
                 upstream->path, portnr);
G
Gerd Hoffmann 已提交
345
        downstream->hubcount = upstream->hubcount + 1;
346 347
    } else {
        snprintf(downstream->path, sizeof(downstream->path), "%d", portnr);
G
Gerd Hoffmann 已提交
348
        downstream->hubcount = 0;
349 350 351
    }
}

352 353 354 355 356 357 358 359
void usb_unregister_port(USBBus *bus, USBPort *port)
{
    if (port->dev)
        qdev_free(&port->dev->qdev);
    QTAILQ_REMOVE(&bus->free, port, next);
    bus->nfree--;
}

360
int usb_claim_port(USBDevice *dev)
361 362 363 364
{
    USBBus *bus = usb_bus_from_device(dev);
    USBPort *port;

365 366
    assert(dev->port == NULL);

G
Gerd Hoffmann 已提交
367 368 369 370 371 372 373
    if (dev->port_path) {
        QTAILQ_FOREACH(port, &bus->free, next) {
            if (strcmp(port->path, dev->port_path) == 0) {
                break;
            }
        }
        if (port == NULL) {
374
            error_report("Error: usb port %s (bus %s) not found (in use?)",
375
                         dev->port_path, bus->qbus.name);
376
            return -1;
G
Gerd Hoffmann 已提交
377 378
        }
    } else {
379
        if (bus->nfree == 1 && strcmp(object_get_typename(OBJECT(dev)), "usb-hub") != 0) {
380 381 382 383 384
            /* Create a new hub and chain it on */
            usb_create_simple(bus, "usb-hub");
        }
        if (bus->nfree == 0) {
            error_report("Error: tried to attach usb device %s to a bus "
385
                         "with no free ports", dev->product_desc);
386 387
            return -1;
        }
G
Gerd Hoffmann 已提交
388 389
        port = QTAILQ_FIRST(&bus->free);
    }
390
    trace_usb_port_claim(bus->busnr, port->path);
391

B
Blue Swirl 已提交
392
    QTAILQ_REMOVE(&bus->free, port, next);
393 394
    bus->nfree--;

395 396
    dev->port = port;
    port->dev = dev;
397

B
Blue Swirl 已提交
398
    QTAILQ_INSERT_TAIL(&bus->used, port, next);
399
    bus->nused++;
400
    return 0;
401 402
}

403
void usb_release_port(USBDevice *dev)
404 405
{
    USBBus *bus = usb_bus_from_device(dev);
406
    USBPort *port = dev->port;
407

408 409 410 411 412 413 414 415 416 417 418
    assert(port != NULL);
    trace_usb_port_release(bus->busnr, port->path);

    QTAILQ_REMOVE(&bus->used, port, next);
    bus->nused--;

    dev->port = NULL;
    port->dev = NULL;

    QTAILQ_INSERT_TAIL(&bus->free, port, next);
    bus->nfree++;
419 420
}

421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442
static void usb_mask_to_str(char *dest, size_t size,
                            unsigned int speedmask)
{
    static const struct {
        unsigned int mask;
        const char *name;
    } speeds[] = {
        { .mask = USB_SPEED_MASK_FULL,  .name = "full"  },
        { .mask = USB_SPEED_MASK_HIGH,  .name = "high"  },
        { .mask = USB_SPEED_MASK_SUPER, .name = "super" },
    };
    int i, pos = 0;

    for (i = 0; i < ARRAY_SIZE(speeds); i++) {
        if (speeds[i].mask & speedmask) {
            pos += snprintf(dest + pos, size - pos, "%s%s",
                            pos ? "+" : "",
                            speeds[i].name);
        }
    }
}

443
int usb_device_attach(USBDevice *dev)
444 445
{
    USBBus *bus = usb_bus_from_device(dev);
446
    USBPort *port = dev->port;
447
    char devspeed[32], portspeed[32];
448

449 450
    assert(port != NULL);
    assert(!dev->attached);
451 452 453 454
    usb_mask_to_str(devspeed, sizeof(devspeed), dev->speedmask);
    usb_mask_to_str(portspeed, sizeof(portspeed), port->speedmask);
    trace_usb_port_attach(bus->busnr, port->path,
                          devspeed, portspeed);
455 456

    if (!(port->speedmask & dev->speedmask)) {
457 458 459 460 461
        error_report("Warning: speed mismatch trying to attach"
                     " usb device \"%s\" (%s speed)"
                     " to bus \"%s\", port \"%s\" (%s speed)",
                     dev->product_desc, devspeed,
                     bus->qbus.name, port->path, portspeed);
462 463 464
        return -1;
    }

465 466
    dev->attached++;
    usb_attach(port);
467

468 469 470 471 472 473 474
    return 0;
}

int usb_device_detach(USBDevice *dev)
{
    USBBus *bus = usb_bus_from_device(dev);
    USBPort *port = dev->port;
475

476 477 478
    assert(port != NULL);
    assert(dev->attached);
    trace_usb_port_detach(bus->busnr, port->path);
479

480 481
    usb_detach(port);
    dev->attached--;
482 483 484
    return 0;
}

485 486 487 488 489 490 491 492 493 494
int usb_device_delete_addr(int busnr, int addr)
{
    USBBus *bus;
    USBPort *port;
    USBDevice *dev;

    bus = usb_bus_find(busnr);
    if (!bus)
        return -1;

B
Blue Swirl 已提交
495
    QTAILQ_FOREACH(port, &bus->used, next) {
496 497 498 499 500 501 502
        if (port->dev->addr == addr)
            break;
    }
    if (!port)
        return -1;
    dev = port->dev;

503
    qdev_free(&dev->qdev);
504 505 506 507 508 509 510 511 512
    return 0;
}

static const char *usb_speed(unsigned int speed)
{
    static const char *txt[] = {
        [ USB_SPEED_LOW  ] = "1.5",
        [ USB_SPEED_FULL ] = "12",
        [ USB_SPEED_HIGH ] = "480",
513
        [ USB_SPEED_SUPER ] = "5000",
514 515 516 517 518 519 520 521
    };
    if (speed >= ARRAY_SIZE(txt))
        return "?";
    return txt[speed];
}

static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent)
{
522
    USBDevice *dev = USB_DEVICE(qdev);
523 524
    USBBus *bus = usb_bus_from_device(dev);

525
    monitor_printf(mon, "%*saddr %d.%d, port %s, speed %s, name %s%s\n",
526
                   indent, "", bus->busnr, dev->addr,
527
                   dev->port ? dev->port->path : "-",
528
                   usb_speed(dev->speed), dev->product_desc,
529
                   dev->attached ? ", attached" : "");
530 531
}

532 533
static char *usb_get_dev_path(DeviceState *qdev)
{
534
    USBDevice *dev = USB_DEVICE(qdev);
G
Gerd Hoffmann 已提交
535 536 537
    DeviceState *hcd = qdev->parent_bus->parent;
    char *id = NULL;

538 539
    if (dev->flags & (1 << USB_DEV_FLAG_FULL_PATH)) {
        id = qdev_get_dev_path(hcd);
G
Gerd Hoffmann 已提交
540 541 542 543 544 545 546 547
    }
    if (id) {
        char *ret = g_strdup_printf("%s/%s", id, dev->port->path);
        g_free(id);
        return ret;
    } else {
        return g_strdup(dev->port->path);
    }
548 549
}

550 551
static char *usb_get_fw_dev_path(DeviceState *qdev)
{
552
    USBDevice *dev = USB_DEVICE(qdev);
553
    char *fw_path, *in;
B
Blue Swirl 已提交
554
    ssize_t pos = 0, fw_len;
555 556
    long nr;

B
Blue Swirl 已提交
557
    fw_len = 32 + strlen(dev->port->path) * 6;
558
    fw_path = g_malloc(fw_len);
559
    in = dev->port->path;
B
Blue Swirl 已提交
560
    while (fw_len - pos > 0) {
561 562 563
        nr = strtol(in, &in, 10);
        if (in[0] == '.') {
            /* some hub between root port and device */
B
Blue Swirl 已提交
564
            pos += snprintf(fw_path + pos, fw_len - pos, "hub@%ld/", nr);
565 566 567
            in++;
        } else {
            /* the device itself */
B
Blue Swirl 已提交
568 569
            pos += snprintf(fw_path + pos, fw_len - pos, "%s@%ld",
                            qdev_fw_name(qdev), nr);
570 571 572 573 574 575
            break;
        }
    }
    return fw_path;
}

576
void usb_info(Monitor *mon, const QDict *qdict)
577 578 579 580 581
{
    USBBus *bus;
    USBDevice *dev;
    USBPort *port;

B
Blue Swirl 已提交
582
    if (QTAILQ_EMPTY(&busses)) {
583 584 585 586
        monitor_printf(mon, "USB support not enabled\n");
        return;
    }

B
Blue Swirl 已提交
587 588
    QTAILQ_FOREACH(bus, &busses, next) {
        QTAILQ_FOREACH(port, &bus->used, next) {
589 590 591
            dev = port->dev;
            if (!dev)
                continue;
592 593
            monitor_printf(mon, "  Device %d.%d, Port %s, Speed %s Mb/s, Product %s\n",
                           bus->busnr, dev->addr, port->path, usb_speed(dev->speed),
594
                           dev->product_desc);
595 596 597 598
        }
    }
}

G
Gerd Hoffmann 已提交
599 600 601 602
/* handle legacy -usbdevice cmd line option */
USBDevice *usbdevice_create(const char *cmdline)
{
    USBBus *bus = usb_bus_find(-1 /* any */);
603 604
    LegacyUSBFactory *f = NULL;
    GSList *i;
605 606
    char driver[32];
    const char *params;
G
Gerd Hoffmann 已提交
607 608 609 610 611 612 613 614 615 616
    int len;

    params = strchr(cmdline,':');
    if (params) {
        params++;
        len = params - cmdline;
        if (len > sizeof(driver))
            len = sizeof(driver);
        pstrcpy(driver, len, cmdline);
    } else {
617
        params = "";
G
Gerd Hoffmann 已提交
618 619 620
        pstrcpy(driver, sizeof(driver), cmdline);
    }

621 622 623 624 625
    for (i = legacy_usb_factory; i; i = i->next) {
        f = i->data;
        if (strcmp(f->usbdevice_name, driver) == 0) {
            break;
        }
G
Gerd Hoffmann 已提交
626
    }
627
    if (i == NULL) {
G
Gerd Hoffmann 已提交
628 629
#if 0
        /* no error because some drivers are not converted (yet) */
630
        error_report("usbdevice %s not found", driver);
G
Gerd Hoffmann 已提交
631 632 633 634
#endif
        return NULL;
    }

635 636 637 638 639 640 641
    if (!bus) {
        error_report("Error: no usb bus to attach usbdevice %s, "
                     "please try -machine usb=on and check that "
                     "the machine model supports USB", driver);
        return NULL;
    }

642
    if (!f->usbdevice_init) {
T
TeLeMan 已提交
643
        if (*params) {
644
            error_report("usbdevice %s accepts no params", driver);
G
Gerd Hoffmann 已提交
645 646
            return NULL;
        }
647
        return usb_create_simple(bus, f->name);
G
Gerd Hoffmann 已提交
648
    }
649
    return f->usbdevice_init(bus, params);
G
Gerd Hoffmann 已提交
650
}
651

652 653 654
static void usb_device_class_init(ObjectClass *klass, void *data)
{
    DeviceClass *k = DEVICE_CLASS(klass);
655
    k->bus_type = TYPE_USB_BUS;
656 657 658
    k->init     = usb_qdev_init;
    k->unplug   = qdev_simple_unplug_cb;
    k->exit     = usb_qdev_exit;
659
    k->props    = usb_props;
660 661
}

662
static const TypeInfo usb_device_type_info = {
663 664 665 666 667
    .name = TYPE_USB_DEVICE,
    .parent = TYPE_DEVICE,
    .instance_size = sizeof(USBDevice),
    .abstract = true,
    .class_size = sizeof(USBDeviceClass),
668
    .class_init = usb_device_class_init,
669 670
};

A
Andreas Färber 已提交
671
static void usb_register_types(void)
672
{
673
    type_register_static(&usb_bus_info);
674 675 676
    type_register_static(&usb_device_type_info);
}

A
Andreas Färber 已提交
677
type_init(usb_register_types)