dev-storage.c 18.8 KB
Newer Older
1
/*
P
pbrook 已提交
2 3 4 5 6
 * USB Mass Storage Device emulation
 *
 * Copyright (c) 2006 CodeSourcery.
 * Written by Paul Brook
 *
M
Matthew Fernandez 已提交
7
 * This code is licensed under the LGPL.
P
pbrook 已提交
8 9
 */

P
pbrook 已提交
10
#include "qemu-common.h"
11 12
#include "qemu-option.h"
#include "qemu-config.h"
G
Gerd Hoffmann 已提交
13 14 15
#include "hw/usb.h"
#include "hw/usb/desc.h"
#include "hw/scsi.h"
16
#include "console.h"
17
#include "monitor.h"
18
#include "sysemu.h"
B
Blue Swirl 已提交
19
#include "blockdev.h"
P
pbrook 已提交
20 21 22 23

//#define DEBUG_MSD

#ifdef DEBUG_MSD
24 25
#define DPRINTF(fmt, ...) \
do { printf("usb-msd: " fmt , ## __VA_ARGS__); } while (0)
P
pbrook 已提交
26
#else
27
#define DPRINTF(fmt, ...) do {} while(0)
P
pbrook 已提交
28 29 30 31 32 33 34 35
#endif

/* USB requests.  */
#define MassStorageReset  0xff
#define GetMaxLun         0xfe

enum USBMSDMode {
    USB_MSDM_CBW, /* Command Block.  */
B
Brad Hards 已提交
36
    USB_MSDM_DATAOUT, /* Transfer data to device.  */
P
pbrook 已提交
37 38 39 40
    USB_MSDM_DATAIN, /* Transfer data from device.  */
    USB_MSDM_CSW /* Command Status.  */
};

41 42 43 44 45 46 47
struct usb_msd_csw {
    uint32_t sig;
    uint32_t tag;
    uint32_t residue;
    uint8_t status;
};

P
pbrook 已提交
48 49 50
typedef struct {
    USBDevice dev;
    enum USBMSDMode mode;
51
    uint32_t scsi_off;
P
pbrook 已提交
52
    uint32_t scsi_len;
P
pbrook 已提交
53
    uint32_t data_len;
54
    struct usb_msd_csw csw;
55
    SCSIRequest *req;
56
    SCSIBus bus;
57
    BlockConf conf;
58
    char *serial;
P
pbrook 已提交
59
    SCSIDevice *scsi_dev;
60
    uint32_t removable;
P
pbrook 已提交
61 62
    /* For async completion.  */
    USBPacket *packet;
P
pbrook 已提交
63 64
} MSDState;

P
pbrook 已提交
65 66 67 68 69 70 71 72 73 74
struct usb_msd_cbw {
    uint32_t sig;
    uint32_t tag;
    uint32_t data_len;
    uint8_t flags;
    uint8_t lun;
    uint8_t cmd_len;
    uint8_t cmd[16];
};

75 76 77 78
enum {
    STR_MANUFACTURER = 1,
    STR_PRODUCT,
    STR_SERIALNUMBER,
G
Gerd Hoffmann 已提交
79 80
    STR_CONFIG_FULL,
    STR_CONFIG_HIGH,
P
pbrook 已提交
81 82
};

83
static const USBDescStrings desc_strings = {
84
    [STR_MANUFACTURER] = "QEMU",
85 86
    [STR_PRODUCT]      = "QEMU USB HARDDRIVE",
    [STR_SERIALNUMBER] = "1",
G
Gerd Hoffmann 已提交
87 88
    [STR_CONFIG_FULL]  = "Full speed config (usb 1.1)",
    [STR_CONFIG_HIGH]  = "High speed config (usb 2.0)",
89 90
};

G
Gerd Hoffmann 已提交
91
static const USBDescIface desc_iface_full = {
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
    .bInterfaceNumber              = 0,
    .bNumEndpoints                 = 2,
    .bInterfaceClass               = USB_CLASS_MASS_STORAGE,
    .bInterfaceSubClass            = 0x06, /* SCSI */
    .bInterfaceProtocol            = 0x50, /* Bulk */
    .eps = (USBDescEndpoint[]) {
        {
            .bEndpointAddress      = USB_DIR_IN | 0x01,
            .bmAttributes          = USB_ENDPOINT_XFER_BULK,
            .wMaxPacketSize        = 64,
        },{
            .bEndpointAddress      = USB_DIR_OUT | 0x02,
            .bmAttributes          = USB_ENDPOINT_XFER_BULK,
            .wMaxPacketSize        = 64,
        },
    }
};

G
Gerd Hoffmann 已提交
110 111
static const USBDescDevice desc_device_full = {
    .bcdUSB                        = 0x0200,
112 113 114 115 116 117
    .bMaxPacketSize0               = 8,
    .bNumConfigurations            = 1,
    .confs = (USBDescConfig[]) {
        {
            .bNumInterfaces        = 1,
            .bConfigurationValue   = 1,
G
Gerd Hoffmann 已提交
118
            .iConfiguration        = STR_CONFIG_FULL,
119
            .bmAttributes          = 0xc0,
120
            .nif = 1,
G
Gerd Hoffmann 已提交
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
            .ifs = &desc_iface_full,
        },
    },
};

static const USBDescIface desc_iface_high = {
    .bInterfaceNumber              = 0,
    .bNumEndpoints                 = 2,
    .bInterfaceClass               = USB_CLASS_MASS_STORAGE,
    .bInterfaceSubClass            = 0x06, /* SCSI */
    .bInterfaceProtocol            = 0x50, /* Bulk */
    .eps = (USBDescEndpoint[]) {
        {
            .bEndpointAddress      = USB_DIR_IN | 0x01,
            .bmAttributes          = USB_ENDPOINT_XFER_BULK,
            .wMaxPacketSize        = 512,
        },{
            .bEndpointAddress      = USB_DIR_OUT | 0x02,
            .bmAttributes          = USB_ENDPOINT_XFER_BULK,
            .wMaxPacketSize        = 512,
        },
    }
};

static const USBDescDevice desc_device_high = {
    .bcdUSB                        = 0x0200,
    .bMaxPacketSize0               = 64,
    .bNumConfigurations            = 1,
    .confs = (USBDescConfig[]) {
        {
            .bNumInterfaces        = 1,
            .bConfigurationValue   = 1,
            .iConfiguration        = STR_CONFIG_HIGH,
            .bmAttributes          = 0xc0,
155
            .nif = 1,
G
Gerd Hoffmann 已提交
156
            .ifs = &desc_iface_high,
157 158 159 160 161 162
        },
    },
};

static const USBDesc desc = {
    .id = {
163 164
        .idVendor          = 0x46f4, /* CRC16() of "QEMU" */
        .idProduct         = 0x0001,
165 166 167 168 169
        .bcdDevice         = 0,
        .iManufacturer     = STR_MANUFACTURER,
        .iProduct          = STR_PRODUCT,
        .iSerialNumber     = STR_SERIALNUMBER,
    },
G
Gerd Hoffmann 已提交
170 171
    .full = &desc_device_full,
    .high = &desc_device_high,
172
    .str  = desc_strings,
P
pbrook 已提交
173 174
};

G
Gerd Hoffmann 已提交
175
static void usb_msd_copy_data(MSDState *s, USBPacket *p)
P
pbrook 已提交
176 177
{
    uint32_t len;
G
Gerd Hoffmann 已提交
178
    len = p->iov.size - p->result;
P
pbrook 已提交
179 180
    if (len > s->scsi_len)
        len = s->scsi_len;
181
    usb_packet_copy(p, scsi_req_get_buf(s->req) + s->scsi_off, len);
P
pbrook 已提交
182
    s->scsi_len -= len;
183
    s->scsi_off += len;
P
pbrook 已提交
184
    s->data_len -= len;
185
    if (s->scsi_len == 0 || s->data_len == 0) {
186
        scsi_req_continue(s->req);
P
pbrook 已提交
187 188 189
    }
}

190
static void usb_msd_send_status(MSDState *s, USBPacket *p)
P
pbrook 已提交
191
{
192
    int len;
P
pbrook 已提交
193

194
    DPRINTF("Command status %d tag 0x%x, len %zd\n",
195
            s->csw.status, le32_to_cpu(s->csw.tag), p->iov.size);
196

197
    assert(s->csw.sig == cpu_to_le32(0x53425355));
198 199 200
    len = MIN(sizeof(s->csw), p->iov.size);
    usb_packet_copy(p, &s->csw, len);
    memset(&s->csw, 0, sizeof(s->csw));
P
pbrook 已提交
201 202
}

203 204 205 206 207 208 209 210 211 212 213 214
static void usb_msd_packet_complete(MSDState *s)
{
    USBPacket *p = s->packet;

    /* Set s->packet to NULL before calling usb_packet_complete
       because another request may be issued before
       usb_packet_complete returns.  */
    DPRINTF("Packet complete %p\n", p);
    s->packet = NULL;
    usb_packet_complete(&s->dev, p);
}

215
static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len)
P
pbrook 已提交
216
{
217
    MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
P
pbrook 已提交
218
    USBPacket *p = s->packet;
P
pbrook 已提交
219

220
    assert((s->mode == USB_MSDM_DATAOUT) == (req->cmd.mode == SCSI_XFER_TO_DEV));
221
    s->scsi_len = len;
222
    s->scsi_off = 0;
P
pbrook 已提交
223
    if (p) {
G
Gerd Hoffmann 已提交
224 225 226
        usb_msd_copy_data(s, p);
        p = s->packet;
        if (p && p->result == p->iov.size) {
227
            usb_msd_packet_complete(s);
P
pbrook 已提交
228
        }
P
pbrook 已提交
229
    }
P
pbrook 已提交
230 231
}

232
static void usb_msd_command_complete(SCSIRequest *req, uint32_t status, size_t resid)
233 234 235 236
{
    MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
    USBPacket *p = s->packet;

237
    DPRINTF("Command complete %d tag 0x%x\n", status, req->tag);
238 239

    s->csw.sig = cpu_to_le32(0x53425355);
240
    s->csw.tag = cpu_to_le32(req->tag);
241
    s->csw.residue = cpu_to_le32(s->data_len);
242
    s->csw.status = status != 0;
243

244 245 246 247 248 249
    if (s->packet) {
        if (s->data_len == 0 && s->mode == USB_MSDM_DATAOUT) {
            /* A deferred packet with no write data remaining must be
               the status read packet.  */
            usb_msd_send_status(s, p);
            s->mode = USB_MSDM_CBW;
250 251 252
        } else if (s->mode == USB_MSDM_CSW) {
            usb_msd_send_status(s, p);
            s->mode = USB_MSDM_CBW;
253 254
        } else {
            if (s->data_len) {
G
Gerd Hoffmann 已提交
255 256 257
                int len = (p->iov.size - p->result);
                usb_packet_skip(p, len);
                s->data_len -= len;
258 259 260 261 262
            }
            if (s->data_len == 0) {
                s->mode = USB_MSDM_CSW;
            }
        }
263
        usb_msd_packet_complete(s);
264 265 266 267 268 269 270
    } else if (s->data_len == 0) {
        s->mode = USB_MSDM_CSW;
    }
    scsi_req_unref(req);
    s->req = NULL;
}

P
Paolo Bonzini 已提交
271 272 273 274 275 276 277 278 279 280 281
static void usb_msd_request_cancelled(SCSIRequest *req)
{
    MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);

    if (req == s->req) {
        scsi_req_unref(s->req);
        s->req = NULL;
        s->scsi_len = 0;
    }
}

B
bellard 已提交
282
static void usb_msd_handle_reset(USBDevice *dev)
P
pbrook 已提交
283 284 285 286
{
    MSDState *s = (MSDState *)dev;

    DPRINTF("Reset\n");
G
Gerd Hoffmann 已提交
287 288 289 290 291 292
    if (s->req) {
        scsi_req_cancel(s->req);
    }
    assert(s->req == NULL);

    if (s->packet) {
293 294
        s->packet->result = USB_RET_STALL;
        usb_msd_packet_complete(s);
G
Gerd Hoffmann 已提交
295 296
    }

P
pbrook 已提交
297 298 299
    s->mode = USB_MSDM_CBW;
}

300 301
static int usb_msd_handle_control(USBDevice *dev, USBPacket *p,
               int request, int value, int index, int length, uint8_t *data)
P
pbrook 已提交
302 303
{
    MSDState *s = (MSDState *)dev;
304
    int ret;
P
pbrook 已提交
305

306
    ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
307 308 309 310 311
    if (ret >= 0) {
        return ret;
    }

    ret = 0;
P
pbrook 已提交
312 313
    switch (request) {
    case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
314 315
        ret = 0;
        break;
P
pbrook 已提交
316
        /* Class specific requests.  */
317
    case ClassInterfaceOutRequest | MassStorageReset:
P
pbrook 已提交
318 319 320 321
        /* Reset state ready for the next CBW.  */
        s->mode = USB_MSDM_CBW;
        ret = 0;
        break;
322
    case ClassInterfaceRequest | GetMaxLun:
P
pbrook 已提交
323 324 325 326 327 328 329 330 331 332
        data[0] = 0;
        ret = 1;
        break;
    default:
        ret = USB_RET_STALL;
        break;
    }
    return ret;
}

333
static void usb_msd_cancel_io(USBDevice *dev, USBPacket *p)
P
pbrook 已提交
334
{
335
    MSDState *s = DO_UPCAST(MSDState, dev, dev);
336

337 338 339
    assert(s->packet == p);
    s->packet = NULL;

340 341 342
    if (s->req) {
        scsi_req_cancel(s->req);
    }
P
pbrook 已提交
343 344 345
}

static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
P
pbrook 已提交
346 347
{
    MSDState *s = (MSDState *)dev;
348
    uint32_t tag;
P
pbrook 已提交
349 350
    int ret = 0;
    struct usb_msd_cbw cbw;
351
    uint8_t devep = p->ep->nr;
P
pbrook 已提交
352

P
pbrook 已提交
353
    switch (p->pid) {
P
pbrook 已提交
354 355 356 357 358 359
    case USB_TOKEN_OUT:
        if (devep != 2)
            goto fail;

        switch (s->mode) {
        case USB_MSDM_CBW:
G
Gerd Hoffmann 已提交
360
            if (p->iov.size != 31) {
P
pbrook 已提交
361 362 363
                fprintf(stderr, "usb-msd: Bad CBW size");
                goto fail;
            }
G
Gerd Hoffmann 已提交
364
            usb_packet_copy(p, &cbw, 31);
P
pbrook 已提交
365 366 367 368 369 370 371 372 373 374
            if (le32_to_cpu(cbw.sig) != 0x43425355) {
                fprintf(stderr, "usb-msd: Bad signature %08x\n",
                        le32_to_cpu(cbw.sig));
                goto fail;
            }
            DPRINTF("Command on LUN %d\n", cbw.lun);
            if (cbw.lun != 0) {
                fprintf(stderr, "usb-msd: Bad LUN %d\n", cbw.lun);
                goto fail;
            }
375
            tag = le32_to_cpu(cbw.tag);
P
pbrook 已提交
376 377 378 379 380 381 382 383 384
            s->data_len = le32_to_cpu(cbw.data_len);
            if (s->data_len == 0) {
                s->mode = USB_MSDM_CSW;
            } else if (cbw.flags & 0x80) {
                s->mode = USB_MSDM_DATAIN;
            } else {
                s->mode = USB_MSDM_DATAOUT;
            }
            DPRINTF("Command tag 0x%x flags %08x len %d data %d\n",
385
                    tag, cbw.flags, cbw.cmd_len, s->data_len);
386
            assert(le32_to_cpu(s->csw.residue) == 0);
G
Gerd Hoffmann 已提交
387
            s->scsi_len = 0;
388
            s->req = scsi_req_new(s->scsi_dev, tag, 0, cbw.cmd, NULL);
389 390 391
#ifdef DEBUG_MSD
            scsi_req_print(s->req);
#endif
392
            scsi_req_enqueue(s->req);
393
            if (s->req && s->req->cmd.xfer != SCSI_XFER_NONE) {
394
                scsi_req_continue(s->req);
P
pbrook 已提交
395
            }
G
Gerd Hoffmann 已提交
396
            ret = p->result;
P
pbrook 已提交
397 398 399
            break;

        case USB_MSDM_DATAOUT:
G
Gerd Hoffmann 已提交
400 401
            DPRINTF("Data out %zd/%d\n", p->iov.size, s->data_len);
            if (p->iov.size > s->data_len) {
P
pbrook 已提交
402
                goto fail;
G
Gerd Hoffmann 已提交
403
            }
P
pbrook 已提交
404

P
pbrook 已提交
405
            if (s->scsi_len) {
G
Gerd Hoffmann 已提交
406
                usb_msd_copy_data(s, p);
P
pbrook 已提交
407
            }
408
            if (le32_to_cpu(s->csw.residue)) {
G
Gerd Hoffmann 已提交
409 410 411 412 413 414 415 416
                int len = p->iov.size - p->result;
                if (len) {
                    usb_packet_skip(p, len);
                    s->data_len -= len;
                    if (s->data_len == 0) {
                        s->mode = USB_MSDM_CSW;
                    }
                }
P
pbrook 已提交
417
            }
G
Gerd Hoffmann 已提交
418
            if (p->result < p->iov.size) {
419
                DPRINTF("Deferring packet %p [wait data-out]\n", p);
P
pbrook 已提交
420 421
                s->packet = p;
                ret = USB_RET_ASYNC;
P
pbrook 已提交
422
            } else {
G
Gerd Hoffmann 已提交
423
                ret = p->result;
P
pbrook 已提交
424
            }
P
pbrook 已提交
425 426 427
            break;

        default:
G
Gerd Hoffmann 已提交
428
            DPRINTF("Unexpected write (len %zd)\n", p->iov.size);
P
pbrook 已提交
429 430 431 432 433 434 435 436 437
            goto fail;
        }
        break;

    case USB_TOKEN_IN:
        if (devep != 1)
            goto fail;

        switch (s->mode) {
P
pbrook 已提交
438
        case USB_MSDM_DATAOUT:
G
Gerd Hoffmann 已提交
439
            if (s->data_len != 0 || p->iov.size < 13) {
P
pbrook 已提交
440
                goto fail;
G
Gerd Hoffmann 已提交
441
            }
P
pbrook 已提交
442 443 444 445 446
            /* Waiting for SCSI write to complete.  */
            s->packet = p;
            ret = USB_RET_ASYNC;
            break;

P
pbrook 已提交
447
        case USB_MSDM_CSW:
G
Gerd Hoffmann 已提交
448
            if (p->iov.size < 13) {
P
pbrook 已提交
449
                goto fail;
G
Gerd Hoffmann 已提交
450
            }
P
pbrook 已提交
451

452 453
            if (s->req) {
                /* still in flight */
454
                DPRINTF("Deferring packet %p [wait status]\n", p);
455 456 457 458 459 460 461
                s->packet = p;
                ret = USB_RET_ASYNC;
            } else {
                usb_msd_send_status(s, p);
                s->mode = USB_MSDM_CBW;
                ret = 13;
            }
P
pbrook 已提交
462 463 464
            break;

        case USB_MSDM_DATAIN:
G
Gerd Hoffmann 已提交
465 466
            DPRINTF("Data in %zd/%d, scsi_len %d\n",
                    p->iov.size, s->data_len, s->scsi_len);
P
pbrook 已提交
467
            if (s->scsi_len) {
G
Gerd Hoffmann 已提交
468
                usb_msd_copy_data(s, p);
P
pbrook 已提交
469
            }
470
            if (le32_to_cpu(s->csw.residue)) {
G
Gerd Hoffmann 已提交
471 472 473 474 475 476 477 478
                int len = p->iov.size - p->result;
                if (len) {
                    usb_packet_skip(p, len);
                    s->data_len -= len;
                    if (s->data_len == 0) {
                        s->mode = USB_MSDM_CSW;
                    }
                }
P
pbrook 已提交
479
            }
G
Gerd Hoffmann 已提交
480
            if (p->result < p->iov.size) {
481
                DPRINTF("Deferring packet %p [wait data-in]\n", p);
P
pbrook 已提交
482 483
                s->packet = p;
                ret = USB_RET_ASYNC;
P
pbrook 已提交
484
            } else {
G
Gerd Hoffmann 已提交
485
                ret = p->result;
P
pbrook 已提交
486
            }
P
pbrook 已提交
487 488 489
            break;

        default:
G
Gerd Hoffmann 已提交
490
            DPRINTF("Unexpected read (len %zd)\n", p->iov.size);
P
pbrook 已提交
491 492 493 494 495 496 497 498 499 500 501 502 503 504
            goto fail;
        }
        break;

    default:
        DPRINTF("Bad token\n");
    fail:
        ret = USB_RET_STALL;
        break;
    }

    return ret;
}

505 506 507 508 509
static void usb_msd_password_cb(void *opaque, int err)
{
    MSDState *s = opaque;

    if (!err)
510 511 512
        err = usb_device_attach(&s->dev);

    if (err)
513
        qdev_unplug(&s->dev.qdev, NULL);
514 515
}

G
Gerd Hoffmann 已提交
516 517 518 519 520 521 522 523 524 525 526
static void *usb_msd_load_request(QEMUFile *f, SCSIRequest *req)
{
    MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);

    /* nothing to load, just store req in our state struct */
    assert(s->req == NULL);
    scsi_req_ref(req);
    s->req = req;
    return NULL;
}

527 528
static const struct SCSIBusInfo usb_msd_scsi_info = {
    .tcq = false,
P
Paolo Bonzini 已提交
529 530
    .max_target = 0,
    .max_lun = 0,
531

532
    .transfer_data = usb_msd_transfer_data,
P
Paolo Bonzini 已提交
533
    .complete = usb_msd_command_complete,
G
Gerd Hoffmann 已提交
534 535
    .cancel = usb_msd_request_cancelled,
    .load_request = usb_msd_load_request,
P
Paolo Bonzini 已提交
536 537
};

538 539 540
static int usb_msd_initfn(USBDevice *dev)
{
    MSDState *s = DO_UPCAST(MSDState, dev, dev);
541
    BlockDriverState *bs = s->conf.bs;
542

543
    if (!bs) {
544
        error_report("drive property not set");
545 546 547
        return -1;
    }

548 549
    blkconf_serial(&s->conf, &s->serial);

550 551 552
    /*
     * Hack alert: this pretends to be a block device, but it's really
     * a SCSI bus that can serve only a single device, which it
553 554 555
     * creates automatically.  But first it needs to detach from its
     * blockdev, or else scsi_bus_legacy_add_drive() dies when it
     * attaches again.
556 557 558
     *
     * The hack is probably a bad idea.
     */
559
    bdrv_detach_dev(bs, &s->dev.qdev);
560
    s->conf.bs = NULL;
561

562 563
    if (s->serial) {
        usb_desc_set_string(dev, STR_SERIALNUMBER, s->serial);
G
Gerd Hoffmann 已提交
564 565
    } else {
        usb_desc_create_serial(dev);
566 567
    }

568
    usb_desc_init(dev);
569
    scsi_bus_new(&s->bus, &s->dev.qdev, &usb_msd_scsi_info);
570 571
    s->scsi_dev = scsi_bus_legacy_add_drive(&s->bus, bs, 0, !!s->removable,
                                            s->conf.bootindex);
572 573 574
    if (!s->scsi_dev) {
        return -1;
    }
G
Gerd Hoffmann 已提交
575
    s->bus.qbus.allow_hotplug = 0;
576
    usb_msd_handle_reset(dev);
577

578
    if (bdrv_key_required(bs)) {
579
        if (cur_mon) {
580
            monitor_read_bdrv_key_start(cur_mon, bs, usb_msd_password_cb, s);
581 582 583 584 585 586
            s->dev.auto_attach = 0;
        } else {
            autostart = 0;
        }
    }

587 588 589
    return 0;
}

590
static USBDevice *usb_msd_init(USBBus *bus, const char *filename)
P
pbrook 已提交
591
{
592 593 594 595
    static int nr=0;
    char id[8];
    QemuOpts *opts;
    DriveInfo *dinfo;
596
    USBDevice *dev;
597 598 599
    const char *p1;
    char fmt[32];

600 601
    /* parse -usbdevice disk: syntax into drive opts */
    snprintf(id, sizeof(id), "usb%d", nr++);
602
    opts = qemu_opts_create(qemu_find_opts("drive"), id, 0, NULL);
603

604 605 606 607 608 609 610
    p1 = strchr(filename, ':');
    if (p1++) {
        const char *p2;

        if (strstart(filename, "format=", &p2)) {
            int len = MIN(p1 - p2, sizeof(fmt));
            pstrcpy(fmt, len, p2);
611
            qemu_opt_set(opts, "format", fmt);
612 613 614 615 616 617 618 619 620 621
        } else if (*filename != ':') {
            printf("unrecognized USB mass-storage option %s\n", filename);
            return NULL;
        }
        filename = p1;
    }
    if (!*filename) {
        printf("block device specification needed\n");
        return NULL;
    }
622 623
    qemu_opt_set(opts, "file", filename);
    qemu_opt_set(opts, "if", "none");
P
pbrook 已提交
624

625
    /* create host drive */
626
    dinfo = drive_init(opts, 0);
627 628
    if (!dinfo) {
        qemu_opts_del(opts);
629
        return NULL;
630
    }
P
pbrook 已提交
631

632
    /* create guest device */
633
    dev = usb_create(bus, "usb-storage");
P
Paul Brook 已提交
634 635 636
    if (!dev) {
        return NULL;
    }
637 638 639 640
    if (qdev_prop_set_drive(&dev->qdev, "drive", dinfo->bdrv) < 0) {
        qdev_free(&dev->qdev);
        return NULL;
    }
641 642
    if (qdev_init(&dev->qdev) < 0)
        return NULL;
643

644
    return dev;
P
pbrook 已提交
645
}
646

647 648 649 650 651 652
static const VMStateDescription vmstate_usb_msd = {
    .name = "usb-storage",
    .version_id = 1,
    .minimum_version_id = 1,
    .fields = (VMStateField []) {
        VMSTATE_USB_DEVICE(dev, MSDState),
G
Gerd Hoffmann 已提交
653 654 655 656 657 658 659 660
        VMSTATE_UINT32(mode, MSDState),
        VMSTATE_UINT32(scsi_len, MSDState),
        VMSTATE_UINT32(scsi_off, MSDState),
        VMSTATE_UINT32(data_len, MSDState),
        VMSTATE_UINT32(csw.sig, MSDState),
        VMSTATE_UINT32(csw.tag, MSDState),
        VMSTATE_UINT32(csw.residue, MSDState),
        VMSTATE_UINT8(csw.status, MSDState),
661 662 663 664
        VMSTATE_END_OF_LIST()
    }
};

665 666 667 668 669 670 671
static Property msd_properties[] = {
    DEFINE_BLOCK_PROPERTIES(MSDState, conf),
    DEFINE_PROP_STRING("serial", MSDState, serial),
    DEFINE_PROP_BIT("removable", MSDState, removable, 0, false),
    DEFINE_PROP_END_OF_LIST(),
};

672 673
static void usb_msd_class_initfn(ObjectClass *klass, void *data)
{
674
    DeviceClass *dc = DEVICE_CLASS(klass);
675 676 677 678 679 680 681 682 683 684
    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);

    uc->init           = usb_msd_initfn;
    uc->product_desc   = "QEMU USB MSD";
    uc->usb_desc       = &desc;
    uc->cancel_packet  = usb_msd_cancel_io;
    uc->handle_attach  = usb_desc_attach;
    uc->handle_reset   = usb_msd_handle_reset;
    uc->handle_control = usb_msd_handle_control;
    uc->handle_data    = usb_msd_handle_data;
685 686 687
    dc->fw_name = "storage";
    dc->vmsd = &vmstate_usb_msd;
    dc->props = msd_properties;
688 689
}

690 691 692 693 694
static TypeInfo msd_info = {
    .name          = "usb-storage",
    .parent        = TYPE_USB_DEVICE,
    .instance_size = sizeof(MSDState),
    .class_init    = usb_msd_class_initfn,
695 696
};

A
Andreas Färber 已提交
697
static void usb_msd_register_types(void)
698
{
699
    type_register_static(&msd_info);
700
    usb_legacy_register("usb-storage", "disk", usb_msd_init);
701
}
A
Andreas Färber 已提交
702 703

type_init(usb_msd_register_types)