bus.c 17.3 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 16 17 18 19 20
static Property usb_props[] = {
    DEFINE_PROP_STRING("port", USBDevice, port_path),
    DEFINE_PROP_BIT("full-path", USBDevice, flags,
                    USB_DEV_FLAG_FULL_PATH, true),
    DEFINE_PROP_END_OF_LIST()
};

21 22 23 24 25 26 27 28 29 30 31 32 33 34
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,
35
};
36

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

40 41 42 43 44 45 46 47 48 49 50 51
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 已提交
52 53 54 55
const VMStateDescription vmstate_usb_device = {
    .name = "USBDevice",
    .version_id = 1,
    .minimum_version_id = 1,
56
    .post_load = usb_device_post_load,
G
Gerd Hoffmann 已提交
57 58 59 60 61 62 63 64 65 66 67 68
    .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(),
    }
};

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

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

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

93 94 95 96 97 98 99 100 101
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 已提交
102
USBDevice *usb_device_find_device(USBDevice *dev, uint8_t addr)
103 104
{
    USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
G
Gerd Hoffmann 已提交
105 106
    if (klass->find_device) {
        return klass->find_device(dev, addr);
107
    }
G
Gerd Hoffmann 已提交
108
    return NULL;
109 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
}

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);
    }
}

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

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

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);
169 170 171
    if (dev->usb_desc) {
        return dev->usb_desc;
    }
172 173 174 175 176 177 178 179 180 181 182 183
    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);
    }
}

184 185 186 187 188 189 190 191
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);
    }
}

192 193 194 195 196 197 198 199
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 已提交
200
static int usb_qdev_init(DeviceState *qdev)
201
{
202
    USBDevice *dev = USB_DEVICE(qdev);
203 204
    int rc;

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

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

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

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

250 251
static GSList *legacy_usb_factory;

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

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

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

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

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

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

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 已提交
304
    QTAILQ_INSERT_TAIL(&bus->free, port, next);
305 306 307
    bus->nfree++;
}

308 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
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);
}

339 340 341 342 343
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 已提交
344
        downstream->hubcount = upstream->hubcount + 1;
345 346
    } else {
        snprintf(downstream->path, sizeof(downstream->path), "%d", portnr);
G
Gerd Hoffmann 已提交
347
        downstream->hubcount = 0;
348 349 350
    }
}

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

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

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

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

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

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

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

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

407 408 409 410 411 412 413 414 415 416 417
    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++;
418 419
}

420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441
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);
        }
    }
}

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

448 449
    assert(port != NULL);
    assert(!dev->attached);
450 451 452 453
    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);
454 455

    if (!(port->speedmask & dev->speedmask)) {
456 457 458 459 460
        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);
461 462 463
        return -1;
    }

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

467 468 469 470 471 472 473
    return 0;
}

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

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

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

484 485 486 487 488 489 490 491 492 493
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 已提交
494
    QTAILQ_FOREACH(port, &bus->used, next) {
495 496 497 498 499 500 501
        if (port->dev->addr == addr)
            break;
    }
    if (!port)
        return -1;
    dev = port->dev;

502
    qdev_free(&dev->qdev);
503 504 505 506 507 508 509 510 511
    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",
512
        [ USB_SPEED_SUPER ] = "5000",
513 514 515 516 517 518 519 520
    };
    if (speed >= ARRAY_SIZE(txt))
        return "?";
    return txt[speed];
}

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

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

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

537 538
    if (dev->flags & (1 << USB_DEV_FLAG_FULL_PATH)) {
        id = qdev_get_dev_path(hcd);
G
Gerd Hoffmann 已提交
539 540 541 542 543 544 545 546
    }
    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);
    }
547 548
}

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

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

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

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

B
Blue Swirl 已提交
586 587
    QTAILQ_FOREACH(bus, &busses, next) {
        QTAILQ_FOREACH(port, &bus->used, next) {
588 589 590
            dev = port->dev;
            if (!dev)
                continue;
591 592
            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),
593
                           dev->product_desc);
594 595 596 597
        }
    }
}

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

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

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

634 635 636 637 638 639 640
    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;
    }

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

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

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

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

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