1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 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 143
|
/*
* USB Mass Storage Device emulation
*
* Copyright (c) 2006 CodeSourcery.
* Written by Paul Brook
*
* This code is licensed under the LGPL.
*/
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qapi/visitor.h"
#include "hw/usb.h"
#include "hw/usb/desc.h"
#include "hw/usb/msd.h"
#include "system/system.h"
#include "system/block-backend.h"
static const struct SCSIBusInfo usb_msd_scsi_info_storage = {
.tcq = false,
.max_target = 0,
.max_lun = 0,
.transfer_data = usb_msd_transfer_data,
.complete = usb_msd_command_complete,
.cancel = usb_msd_request_cancelled,
.load_request = usb_msd_load_request,
};
static void usb_msd_storage_realize(USBDevice *dev, Error **errp)
{
MSDState *s = USB_STORAGE_DEV(dev);
BlockBackend *blk = s->conf.blk;
SCSIDevice *scsi_dev;
if (!blk) {
error_setg(errp, "drive property not set");
return;
}
/*
* 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
* creates automatically. But first it needs to detach from its
* blockdev, or else scsi_bus_legacy_add_drive() dies when it
* attaches again. We also need to take another reference so that
* blk_detach_dev() doesn't free blk while we still need it.
*
* The hack is probably a bad idea.
*/
blk_ref(blk);
blk_detach_dev(blk, DEVICE(s));
s->conf.blk = NULL;
usb_desc_create_serial(dev);
usb_desc_init(dev);
dev->flags |= (1 << USB_DEV_FLAG_IS_SCSI_STORAGE);
scsi_bus_init(&s->bus, sizeof(s->bus), DEVICE(dev),
&usb_msd_scsi_info_storage);
scsi_dev = scsi_bus_legacy_add_drive(&s->bus, blk, 0, !!s->removable,
&s->conf, dev->serial, errp);
blk_unref(blk);
if (!scsi_dev) {
return;
}
usb_msd_handle_reset(dev);
s->scsi_dev = scsi_dev;
}
static const Property msd_properties[] = {
DEFINE_BLOCK_PROPERTIES(MSDState, conf),
DEFINE_BLOCK_ERROR_PROPERTIES(MSDState, conf),
DEFINE_PROP_BOOL("removable", MSDState, removable, false),
DEFINE_PROP_BOOL("commandlog", MSDState, commandlog, false),
};
static void usb_msd_class_storage_initfn(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
uc->realize = usb_msd_storage_realize;
device_class_set_props(dc, msd_properties);
}
static void usb_msd_get_bootindex(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
USBDevice *dev = USB_DEVICE(obj);
MSDState *s = USB_STORAGE_DEV(dev);
visit_type_int32(v, name, &s->conf.bootindex, errp);
}
static void usb_msd_set_bootindex(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
USBDevice *dev = USB_DEVICE(obj);
MSDState *s = USB_STORAGE_DEV(dev);
int32_t boot_index;
Error *local_err = NULL;
if (!visit_type_int32(v, name, &boot_index, errp)) {
return;
}
/* check whether bootindex is present in fw_boot_order list */
check_boot_index(boot_index, &local_err);
if (local_err) {
goto out;
}
/* change bootindex to a new one */
s->conf.bootindex = boot_index;
if (s->scsi_dev) {
object_property_set_int(OBJECT(s->scsi_dev), "bootindex", boot_index,
&error_abort);
}
out:
error_propagate(errp, local_err);
}
static void usb_msd_instance_init(Object *obj)
{
object_property_add(obj, "bootindex", "int32",
usb_msd_get_bootindex,
usb_msd_set_bootindex, NULL, NULL);
object_property_set_int(obj, "bootindex", -1, NULL);
}
static const TypeInfo msd_info = {
.name = "usb-storage",
.parent = TYPE_USB_STORAGE,
.class_init = usb_msd_class_storage_initfn,
.instance_init = usb_msd_instance_init,
};
static void register_types(void)
{
type_register_static(&msd_info);
}
type_init(register_types)
|