cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

dev-hub.c (20782B)


      1/*
      2 * QEMU USB HUB emulation
      3 *
      4 * Copyright (c) 2005 Fabrice Bellard
      5 *
      6 * Permission is hereby granted, free of charge, to any person obtaining a copy
      7 * of this software and associated documentation files (the "Software"), to deal
      8 * in the Software without restriction, including without limitation the rights
      9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     10 * copies of the Software, and to permit persons to whom the Software is
     11 * furnished to do so, subject to the following conditions:
     12 *
     13 * The above copyright notice and this permission notice shall be included in
     14 * all copies or substantial portions of the Software.
     15 *
     16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
     19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     22 * THE SOFTWARE.
     23 */
     24
     25#include "qemu/osdep.h"
     26#include "qapi/error.h"
     27#include "qemu/timer.h"
     28#include "trace.h"
     29#include "hw/qdev-properties.h"
     30#include "hw/usb.h"
     31#include "migration/vmstate.h"
     32#include "desc.h"
     33#include "qemu/error-report.h"
     34#include "qemu/module.h"
     35#include "qom/object.h"
     36
     37#define MAX_PORTS 8
     38
     39typedef struct USBHubPort {
     40    USBPort port;
     41    uint16_t wPortStatus;
     42    uint16_t wPortChange;
     43} USBHubPort;
     44
     45struct USBHubState {
     46    USBDevice dev;
     47    USBEndpoint *intr;
     48    uint32_t num_ports;
     49    bool port_power;
     50    QEMUTimer *port_timer;
     51    USBHubPort ports[MAX_PORTS];
     52};
     53
     54#define TYPE_USB_HUB "usb-hub"
     55OBJECT_DECLARE_SIMPLE_TYPE(USBHubState, USB_HUB)
     56
     57#define ClearHubFeature		(0x2000 | USB_REQ_CLEAR_FEATURE)
     58#define ClearPortFeature	(0x2300 | USB_REQ_CLEAR_FEATURE)
     59#define GetHubDescriptor	(0xa000 | USB_REQ_GET_DESCRIPTOR)
     60#define GetHubStatus		(0xa000 | USB_REQ_GET_STATUS)
     61#define GetPortStatus		(0xa300 | USB_REQ_GET_STATUS)
     62#define SetHubFeature		(0x2000 | USB_REQ_SET_FEATURE)
     63#define SetPortFeature		(0x2300 | USB_REQ_SET_FEATURE)
     64
     65#define PORT_STAT_CONNECTION	0x0001
     66#define PORT_STAT_ENABLE	0x0002
     67#define PORT_STAT_SUSPEND	0x0004
     68#define PORT_STAT_OVERCURRENT	0x0008
     69#define PORT_STAT_RESET		0x0010
     70#define PORT_STAT_POWER		0x0100
     71#define PORT_STAT_LOW_SPEED	0x0200
     72#define PORT_STAT_HIGH_SPEED    0x0400
     73#define PORT_STAT_TEST          0x0800
     74#define PORT_STAT_INDICATOR     0x1000
     75
     76#define PORT_STAT_C_CONNECTION	0x0001
     77#define PORT_STAT_C_ENABLE	0x0002
     78#define PORT_STAT_C_SUSPEND	0x0004
     79#define PORT_STAT_C_OVERCURRENT	0x0008
     80#define PORT_STAT_C_RESET	0x0010
     81
     82#define PORT_CONNECTION	        0
     83#define PORT_ENABLE		1
     84#define PORT_SUSPEND		2
     85#define PORT_OVERCURRENT	3
     86#define PORT_RESET		4
     87#define PORT_POWER		8
     88#define PORT_LOWSPEED		9
     89#define PORT_HIGHSPEED		10
     90#define PORT_C_CONNECTION	16
     91#define PORT_C_ENABLE		17
     92#define PORT_C_SUSPEND		18
     93#define PORT_C_OVERCURRENT	19
     94#define PORT_C_RESET		20
     95#define PORT_TEST               21
     96#define PORT_INDICATOR          22
     97
     98/* same as Linux kernel root hubs */
     99
    100enum {
    101    STR_MANUFACTURER = 1,
    102    STR_PRODUCT,
    103    STR_SERIALNUMBER,
    104};
    105
    106static const USBDescStrings desc_strings = {
    107    [STR_MANUFACTURER] = "QEMU",
    108    [STR_PRODUCT]      = "QEMU USB Hub",
    109    [STR_SERIALNUMBER] = "314159",
    110};
    111
    112static const USBDescIface desc_iface_hub = {
    113    .bInterfaceNumber              = 0,
    114    .bNumEndpoints                 = 1,
    115    .bInterfaceClass               = USB_CLASS_HUB,
    116    .eps = (USBDescEndpoint[]) {
    117        {
    118            .bEndpointAddress      = USB_DIR_IN | 0x01,
    119            .bmAttributes          = USB_ENDPOINT_XFER_INT,
    120            .wMaxPacketSize        = 1 + DIV_ROUND_UP(MAX_PORTS, 8),
    121            .bInterval             = 0xff,
    122        },
    123    }
    124};
    125
    126static const USBDescDevice desc_device_hub = {
    127    .bcdUSB                        = 0x0110,
    128    .bDeviceClass                  = USB_CLASS_HUB,
    129    .bMaxPacketSize0               = 8,
    130    .bNumConfigurations            = 1,
    131    .confs = (USBDescConfig[]) {
    132        {
    133            .bNumInterfaces        = 1,
    134            .bConfigurationValue   = 1,
    135            .bmAttributes          = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER |
    136                                     USB_CFG_ATT_WAKEUP,
    137            .nif = 1,
    138            .ifs = &desc_iface_hub,
    139        },
    140    },
    141};
    142
    143static const USBDesc desc_hub = {
    144    .id = {
    145        .idVendor          = 0x0409,
    146        .idProduct         = 0x55aa,
    147        .bcdDevice         = 0x0101,
    148        .iManufacturer     = STR_MANUFACTURER,
    149        .iProduct          = STR_PRODUCT,
    150        .iSerialNumber     = STR_SERIALNUMBER,
    151    },
    152    .full = &desc_device_hub,
    153    .str  = desc_strings,
    154};
    155
    156static const uint8_t qemu_hub_hub_descriptor[] =
    157{
    158        0x00,			/*  u8  bLength; patched in later */
    159        0x29,			/*  u8  bDescriptorType; Hub-descriptor */
    160        0x00,			/*  u8  bNbrPorts; (patched later) */
    161        0x0a,			/* u16  wHubCharacteristics; */
    162        0x00,			/*   (per-port OC, no power switching) */
    163        0x01,			/*  u8  bPwrOn2pwrGood; 2ms */
    164        0x00			/*  u8  bHubContrCurrent; 0 mA */
    165
    166        /* DeviceRemovable and PortPwrCtrlMask patched in later */
    167};
    168
    169static bool usb_hub_port_change(USBHubPort *port, uint16_t status)
    170{
    171    bool notify = false;
    172
    173    if (status & 0x1f) {
    174        port->wPortChange |= status;
    175        notify = true;
    176    }
    177    return notify;
    178}
    179
    180static bool usb_hub_port_set(USBHubPort *port, uint16_t status)
    181{
    182    if (port->wPortStatus & status) {
    183        return false;
    184    }
    185    port->wPortStatus |= status;
    186    return usb_hub_port_change(port, status);
    187}
    188
    189static bool usb_hub_port_clear(USBHubPort *port, uint16_t status)
    190{
    191    if (!(port->wPortStatus & status)) {
    192        return false;
    193    }
    194    port->wPortStatus &= ~status;
    195    return usb_hub_port_change(port, status);
    196}
    197
    198static bool usb_hub_port_update(USBHubPort *port)
    199{
    200    bool notify = false;
    201
    202    if (port->port.dev && port->port.dev->attached) {
    203        notify = usb_hub_port_set(port, PORT_STAT_CONNECTION);
    204        if (port->port.dev->speed == USB_SPEED_LOW) {
    205            usb_hub_port_set(port, PORT_STAT_LOW_SPEED);
    206        } else {
    207            usb_hub_port_clear(port, PORT_STAT_LOW_SPEED);
    208        }
    209    }
    210    return notify;
    211}
    212
    213static void usb_hub_port_update_timer(void *opaque)
    214{
    215    USBHubState *s = opaque;
    216    bool notify = false;
    217    int i;
    218
    219    for (i = 0; i < s->num_ports; i++) {
    220        notify |= usb_hub_port_update(&s->ports[i]);
    221    }
    222    if (notify) {
    223        usb_wakeup(s->intr, 0);
    224    }
    225}
    226
    227static void usb_hub_attach(USBPort *port1)
    228{
    229    USBHubState *s = port1->opaque;
    230    USBHubPort *port = &s->ports[port1->index];
    231
    232    trace_usb_hub_attach(s->dev.addr, port1->index + 1);
    233    usb_hub_port_update(port);
    234    usb_wakeup(s->intr, 0);
    235}
    236
    237static void usb_hub_detach(USBPort *port1)
    238{
    239    USBHubState *s = port1->opaque;
    240    USBHubPort *port = &s->ports[port1->index];
    241
    242    trace_usb_hub_detach(s->dev.addr, port1->index + 1);
    243    usb_wakeup(s->intr, 0);
    244
    245    /* Let upstream know the device on this port is gone */
    246    s->dev.port->ops->child_detach(s->dev.port, port1->dev);
    247
    248    usb_hub_port_clear(port, PORT_STAT_CONNECTION);
    249    usb_hub_port_clear(port, PORT_STAT_ENABLE);
    250    usb_hub_port_clear(port, PORT_STAT_SUSPEND);
    251    usb_wakeup(s->intr, 0);
    252}
    253
    254static void usb_hub_child_detach(USBPort *port1, USBDevice *child)
    255{
    256    USBHubState *s = port1->opaque;
    257
    258    /* Pass along upstream */
    259    s->dev.port->ops->child_detach(s->dev.port, child);
    260}
    261
    262static void usb_hub_wakeup(USBPort *port1)
    263{
    264    USBHubState *s = port1->opaque;
    265    USBHubPort *port = &s->ports[port1->index];
    266
    267    if (usb_hub_port_clear(port, PORT_STAT_SUSPEND)) {
    268        usb_wakeup(s->intr, 0);
    269    }
    270}
    271
    272static void usb_hub_complete(USBPort *port, USBPacket *packet)
    273{
    274    USBHubState *s = port->opaque;
    275
    276    /*
    277     * Just pass it along upstream for now.
    278     *
    279     * If we ever implement usb 2.0 split transactions this will
    280     * become a little more complicated ...
    281     *
    282     * Can't use usb_packet_complete() here because packet->owner is
    283     * cleared already, go call the ->complete() callback directly
    284     * instead.
    285     */
    286    s->dev.port->ops->complete(s->dev.port, packet);
    287}
    288
    289static USBDevice *usb_hub_find_device(USBDevice *dev, uint8_t addr)
    290{
    291    USBHubState *s = USB_HUB(dev);
    292    USBHubPort *port;
    293    USBDevice *downstream;
    294    int i;
    295
    296    for (i = 0; i < s->num_ports; i++) {
    297        port = &s->ports[i];
    298        if (!(port->wPortStatus & PORT_STAT_ENABLE)) {
    299            continue;
    300        }
    301        downstream = usb_find_device(&port->port, addr);
    302        if (downstream != NULL) {
    303            return downstream;
    304        }
    305    }
    306    return NULL;
    307}
    308
    309static void usb_hub_handle_reset(USBDevice *dev)
    310{
    311    USBHubState *s = USB_HUB(dev);
    312    USBHubPort *port;
    313    int i;
    314
    315    trace_usb_hub_reset(s->dev.addr);
    316    for (i = 0; i < s->num_ports; i++) {
    317        port = s->ports + i;
    318        port->wPortStatus = 0;
    319        port->wPortChange = 0;
    320        usb_hub_port_set(port, PORT_STAT_POWER);
    321        usb_hub_port_update(port);
    322    }
    323}
    324
    325static const char *feature_name(int feature)
    326{
    327    static const char *name[] = {
    328        [PORT_CONNECTION]    = "connection",
    329        [PORT_ENABLE]        = "enable",
    330        [PORT_SUSPEND]       = "suspend",
    331        [PORT_OVERCURRENT]   = "overcurrent",
    332        [PORT_RESET]         = "reset",
    333        [PORT_POWER]         = "power",
    334        [PORT_LOWSPEED]      = "lowspeed",
    335        [PORT_HIGHSPEED]     = "highspeed",
    336        [PORT_C_CONNECTION]  = "change-connection",
    337        [PORT_C_ENABLE]      = "change-enable",
    338        [PORT_C_SUSPEND]     = "change-suspend",
    339        [PORT_C_OVERCURRENT] = "change-overcurrent",
    340        [PORT_C_RESET]       = "change-reset",
    341        [PORT_TEST]          = "test",
    342        [PORT_INDICATOR]     = "indicator",
    343    };
    344    if (feature < 0 || feature >= ARRAY_SIZE(name)) {
    345        return "?";
    346    }
    347    return name[feature] ?: "?";
    348}
    349
    350static void usb_hub_handle_control(USBDevice *dev, USBPacket *p,
    351               int request, int value, int index, int length, uint8_t *data)
    352{
    353    USBHubState *s = (USBHubState *)dev;
    354    int ret;
    355
    356    trace_usb_hub_control(s->dev.addr, request, value, index, length);
    357
    358    ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
    359    if (ret >= 0) {
    360        return;
    361    }
    362
    363    switch(request) {
    364    case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
    365        if (value == 0 && index != 0x81) { /* clear ep halt */
    366            goto fail;
    367        }
    368        break;
    369        /* usb specific requests */
    370    case GetHubStatus:
    371        data[0] = 0;
    372        data[1] = 0;
    373        data[2] = 0;
    374        data[3] = 0;
    375        p->actual_length = 4;
    376        break;
    377    case GetPortStatus:
    378        {
    379            unsigned int n = index - 1;
    380            USBHubPort *port;
    381            if (n >= s->num_ports) {
    382                goto fail;
    383            }
    384            port = &s->ports[n];
    385            trace_usb_hub_get_port_status(s->dev.addr, index,
    386                                          port->wPortStatus,
    387                                          port->wPortChange);
    388            data[0] = port->wPortStatus;
    389            data[1] = port->wPortStatus >> 8;
    390            data[2] = port->wPortChange;
    391            data[3] = port->wPortChange >> 8;
    392            p->actual_length = 4;
    393        }
    394        break;
    395    case SetHubFeature:
    396    case ClearHubFeature:
    397        if (value != 0 && value != 1) {
    398            goto fail;
    399        }
    400        break;
    401    case SetPortFeature:
    402        {
    403            unsigned int n = index - 1;
    404            USBHubPort *port;
    405            USBDevice *dev;
    406
    407            trace_usb_hub_set_port_feature(s->dev.addr, index,
    408                                           feature_name(value));
    409
    410            if (n >= s->num_ports) {
    411                goto fail;
    412            }
    413            port = &s->ports[n];
    414            dev = port->port.dev;
    415            switch(value) {
    416            case PORT_SUSPEND:
    417                port->wPortStatus |= PORT_STAT_SUSPEND;
    418                break;
    419            case PORT_RESET:
    420                usb_hub_port_set(port, PORT_STAT_RESET);
    421                usb_hub_port_clear(port, PORT_STAT_RESET);
    422                if (dev && dev->attached) {
    423                    usb_device_reset(dev);
    424                    usb_hub_port_set(port, PORT_STAT_ENABLE);
    425                }
    426                usb_wakeup(s->intr, 0);
    427                break;
    428            case PORT_POWER:
    429                if (s->port_power) {
    430                    int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
    431                    usb_hub_port_set(port, PORT_STAT_POWER);
    432                    timer_mod(s->port_timer, now + 5000000); /* 5 ms */
    433                }
    434                break;
    435            default:
    436                goto fail;
    437            }
    438        }
    439        break;
    440    case ClearPortFeature:
    441        {
    442            unsigned int n = index - 1;
    443            USBHubPort *port;
    444
    445            trace_usb_hub_clear_port_feature(s->dev.addr, index,
    446                                             feature_name(value));
    447
    448            if (n >= s->num_ports) {
    449                goto fail;
    450            }
    451            port = &s->ports[n];
    452            switch(value) {
    453            case PORT_ENABLE:
    454                port->wPortStatus &= ~PORT_STAT_ENABLE;
    455                break;
    456            case PORT_C_ENABLE:
    457                port->wPortChange &= ~PORT_STAT_C_ENABLE;
    458                break;
    459            case PORT_SUSPEND:
    460                usb_hub_port_clear(port, PORT_STAT_SUSPEND);
    461                break;
    462            case PORT_C_SUSPEND:
    463                port->wPortChange &= ~PORT_STAT_C_SUSPEND;
    464                break;
    465            case PORT_C_CONNECTION:
    466                port->wPortChange &= ~PORT_STAT_C_CONNECTION;
    467                break;
    468            case PORT_C_OVERCURRENT:
    469                port->wPortChange &= ~PORT_STAT_C_OVERCURRENT;
    470                break;
    471            case PORT_C_RESET:
    472                port->wPortChange &= ~PORT_STAT_C_RESET;
    473                break;
    474            case PORT_POWER:
    475                if (s->port_power) {
    476                    usb_hub_port_clear(port, PORT_STAT_POWER);
    477                    usb_hub_port_clear(port, PORT_STAT_CONNECTION);
    478                    usb_hub_port_clear(port, PORT_STAT_ENABLE);
    479                    usb_hub_port_clear(port, PORT_STAT_SUSPEND);
    480                    port->wPortChange = 0;
    481                }
    482            default:
    483                goto fail;
    484            }
    485        }
    486        break;
    487    case GetHubDescriptor:
    488        {
    489            unsigned int n, limit, var_hub_size = 0;
    490            memcpy(data, qemu_hub_hub_descriptor,
    491                   sizeof(qemu_hub_hub_descriptor));
    492            data[2] = s->num_ports;
    493
    494            if (s->port_power) {
    495                data[3] &= ~0x03;
    496                data[3] |= 0x01;
    497            }
    498
    499            /* fill DeviceRemovable bits */
    500            limit = DIV_ROUND_UP(s->num_ports + 1, 8) + 7;
    501            for (n = 7; n < limit; n++) {
    502                data[n] = 0x00;
    503                var_hub_size++;
    504            }
    505
    506            /* fill PortPwrCtrlMask bits */
    507            limit = limit + DIV_ROUND_UP(s->num_ports, 8);
    508            for (;n < limit; n++) {
    509                data[n] = 0xff;
    510                var_hub_size++;
    511            }
    512
    513            p->actual_length = sizeof(qemu_hub_hub_descriptor) + var_hub_size;
    514            data[0] = p->actual_length;
    515            break;
    516        }
    517    default:
    518    fail:
    519        p->status = USB_RET_STALL;
    520        break;
    521    }
    522}
    523
    524static void usb_hub_handle_data(USBDevice *dev, USBPacket *p)
    525{
    526    USBHubState *s = (USBHubState *)dev;
    527
    528    switch(p->pid) {
    529    case USB_TOKEN_IN:
    530        if (p->ep->nr == 1) {
    531            USBHubPort *port;
    532            unsigned int status;
    533            uint8_t buf[4];
    534            int i, n;
    535            n = DIV_ROUND_UP(s->num_ports + 1, 8);
    536            if (p->iov.size == 1) { /* FreeBSD workaround */
    537                n = 1;
    538            } else if (n > p->iov.size) {
    539                p->status = USB_RET_BABBLE;
    540                return;
    541            }
    542            status = 0;
    543            for (i = 0; i < s->num_ports; i++) {
    544                port = &s->ports[i];
    545                if (port->wPortChange)
    546                    status |= (1 << (i + 1));
    547            }
    548            if (status != 0) {
    549                trace_usb_hub_status_report(s->dev.addr, status);
    550                for(i = 0; i < n; i++) {
    551                    buf[i] = status >> (8 * i);
    552                }
    553                usb_packet_copy(p, buf, n);
    554            } else {
    555                p->status = USB_RET_NAK; /* usb11 11.13.1 */
    556            }
    557        } else {
    558            goto fail;
    559        }
    560        break;
    561    case USB_TOKEN_OUT:
    562    default:
    563    fail:
    564        p->status = USB_RET_STALL;
    565        break;
    566    }
    567}
    568
    569static void usb_hub_unrealize(USBDevice *dev)
    570{
    571    USBHubState *s = (USBHubState *)dev;
    572    int i;
    573
    574    for (i = 0; i < s->num_ports; i++) {
    575        usb_unregister_port(usb_bus_from_device(dev),
    576                            &s->ports[i].port);
    577    }
    578
    579    timer_free(s->port_timer);
    580}
    581
    582static USBPortOps usb_hub_port_ops = {
    583    .attach = usb_hub_attach,
    584    .detach = usb_hub_detach,
    585    .child_detach = usb_hub_child_detach,
    586    .wakeup = usb_hub_wakeup,
    587    .complete = usb_hub_complete,
    588};
    589
    590static void usb_hub_realize(USBDevice *dev, Error **errp)
    591{
    592    USBHubState *s = USB_HUB(dev);
    593    USBHubPort *port;
    594    int i;
    595
    596    if (s->num_ports < 1 || s->num_ports > MAX_PORTS) {
    597        error_setg(errp, "num_ports (%d) out of range (1..%d)",
    598                   s->num_ports, MAX_PORTS);
    599        return;
    600    }
    601
    602    if (dev->port->hubcount == 5) {
    603        error_setg(errp, "usb hub chain too deep");
    604        return;
    605    }
    606
    607    usb_desc_create_serial(dev);
    608    usb_desc_init(dev);
    609    s->port_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
    610                                 usb_hub_port_update_timer, s);
    611    s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1);
    612    for (i = 0; i < s->num_ports; i++) {
    613        port = &s->ports[i];
    614        usb_register_port(usb_bus_from_device(dev),
    615                          &port->port, s, i, &usb_hub_port_ops,
    616                          USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
    617        usb_port_location(&port->port, dev->port, i+1);
    618    }
    619    usb_hub_handle_reset(dev);
    620}
    621
    622static const VMStateDescription vmstate_usb_hub_port = {
    623    .name = "usb-hub-port",
    624    .version_id = 1,
    625    .minimum_version_id = 1,
    626    .fields = (VMStateField[]) {
    627        VMSTATE_UINT16(wPortStatus, USBHubPort),
    628        VMSTATE_UINT16(wPortChange, USBHubPort),
    629        VMSTATE_END_OF_LIST()
    630    }
    631};
    632
    633static bool usb_hub_port_timer_needed(void *opaque)
    634{
    635    USBHubState *s = opaque;
    636
    637    return s->port_power;
    638}
    639
    640static const VMStateDescription vmstate_usb_hub_port_timer = {
    641    .name = "usb-hub/port-timer",
    642    .version_id = 1,
    643    .minimum_version_id = 1,
    644    .needed = usb_hub_port_timer_needed,
    645    .fields = (VMStateField[]) {
    646        VMSTATE_TIMER_PTR(port_timer, USBHubState),
    647        VMSTATE_END_OF_LIST()
    648    },
    649};
    650
    651static const VMStateDescription vmstate_usb_hub = {
    652    .name = "usb-hub",
    653    .version_id = 1,
    654    .minimum_version_id = 1,
    655    .fields = (VMStateField[]) {
    656        VMSTATE_USB_DEVICE(dev, USBHubState),
    657        VMSTATE_STRUCT_ARRAY(ports, USBHubState, MAX_PORTS, 0,
    658                             vmstate_usb_hub_port, USBHubPort),
    659        VMSTATE_END_OF_LIST()
    660    },
    661    .subsections = (const VMStateDescription * []) {
    662        &vmstate_usb_hub_port_timer,
    663        NULL
    664    }
    665};
    666
    667static Property usb_hub_properties[] = {
    668    DEFINE_PROP_UINT32("ports", USBHubState, num_ports, 8),
    669    DEFINE_PROP_BOOL("port-power", USBHubState, port_power, false),
    670    DEFINE_PROP_END_OF_LIST(),
    671};
    672
    673static void usb_hub_class_initfn(ObjectClass *klass, void *data)
    674{
    675    DeviceClass *dc = DEVICE_CLASS(klass);
    676    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
    677
    678    uc->realize        = usb_hub_realize;
    679    uc->product_desc   = "QEMU USB Hub";
    680    uc->usb_desc       = &desc_hub;
    681    uc->find_device    = usb_hub_find_device;
    682    uc->handle_reset   = usb_hub_handle_reset;
    683    uc->handle_control = usb_hub_handle_control;
    684    uc->handle_data    = usb_hub_handle_data;
    685    uc->unrealize      = usb_hub_unrealize;
    686    set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
    687    dc->fw_name = "hub";
    688    dc->vmsd = &vmstate_usb_hub;
    689    device_class_set_props(dc, usb_hub_properties);
    690}
    691
    692static const TypeInfo hub_info = {
    693    .name          = TYPE_USB_HUB,
    694    .parent        = TYPE_USB_DEVICE,
    695    .instance_size = sizeof(USBHubState),
    696    .class_init    = usb_hub_class_initfn,
    697};
    698
    699static void usb_hub_register_types(void)
    700{
    701    type_register_static(&hub_info);
    702}
    703
    704type_init(usb_hub_register_types)