* This code is licenced under the LGPL.
*/
-#include "vl.h"
+#include "qemu-common.h"
+#include "qemu-option.h"
+#include "qemu-config.h"
+#include "usb.h"
+#include "block.h"
+#include "scsi-disk.h"
+#include "console.h"
//#define DEBUG_MSD
#ifdef DEBUG_MSD
-#define DPRINTF(fmt, args...) \
-do { printf("usb-msd: " fmt , ##args); } while (0)
+#define DPRINTF(fmt, ...) \
+do { printf("usb-msd: " fmt , ## __VA_ARGS__); } while (0)
#else
-#define DPRINTF(fmt, args...) do {} while(0)
+#define DPRINTF(fmt, ...) do {} while(0)
#endif
/* USB requests. */
uint32_t data_len;
uint32_t residue;
uint32_t tag;
- BlockDriverState *bs;
+ SCSIBus bus;
+ DriveInfo *dinfo;
SCSIDevice *scsi_dev;
int result;
/* For async completion. */
5: Remote wakeup,
4..0: resvd */
0x00, /* u8 MaxPower; */
-
+
/* one interface */
0x09, /* u8 if_bLength; */
0x04, /* u8 if_bDescriptorType; Interface */
0x06, /* u8 if_bInterfaceSubClass; SCSI */
0x50, /* u8 if_bInterfaceProtocol; Bulk Only */
0x00, /* u8 if_iInterface; */
-
+
/* Bulk-In endpoint */
0x07, /* u8 ep_bLength; */
0x05, /* u8 ep_bDescriptorType; Endpoint */
s->data_len -= len;
if (s->scsi_len == 0) {
if (s->mode == USB_MSDM_DATAIN) {
- scsi_read_data(s->scsi_dev, s->tag);
+ s->scsi_dev->info->read_data(s->scsi_dev, s->tag);
} else if (s->mode == USB_MSDM_DATAOUT) {
- scsi_write_data(s->scsi_dev, s->tag);
+ s->scsi_dev->info->write_data(s->scsi_dev, s->tag);
}
}
}
memcpy(s->usb_buf, &csw, 13);
}
-static void usb_msd_command_complete(void *opaque, int reason, uint32_t tag,
+static void usb_msd_command_complete(SCSIBus *bus, int reason, uint32_t tag,
uint32_t arg)
{
- MSDState *s = (MSDState *)opaque;
+ MSDState *s = DO_UPCAST(MSDState, dev.qdev, bus->qbus.parent);
USBPacket *p = s->packet;
if (tag != s->tag) {
return;
}
s->scsi_len = arg;
- s->scsi_buf = scsi_get_buf(s->scsi_dev, tag);
+ s->scsi_buf = s->scsi_dev->info->get_buf(s->scsi_dev, tag);
if (p) {
usb_msd_copy_data(s);
if (s->usb_len == 0) {
static void usb_msd_cancel_io(USBPacket *p, void *opaque)
{
MSDState *s = opaque;
- scsi_cancel_io(s->scsi_dev, s->tag);
+ s->scsi_dev->info->cancel_io(s->scsi_dev, s->tag);
s->packet = NULL;
s->scsi_len = 0;
}
DPRINTF("Command tag 0x%x flags %08x len %d data %d\n",
s->tag, cbw.flags, cbw.cmd_len, s->data_len);
s->residue = 0;
- scsi_send_command(s->scsi_dev, s->tag, cbw.cmd, 0);
+ s->scsi_dev->info->send_command(s->scsi_dev, s->tag, cbw.cmd, 0);
/* ??? Should check that USB and SCSI data transfer
directions match. */
if (s->residue == 0) {
if (s->mode == USB_MSDM_DATAIN) {
- scsi_read_data(s->scsi_dev, s->tag);
+ s->scsi_dev->info->read_data(s->scsi_dev, s->tag);
} else if (s->mode == USB_MSDM_DATAOUT) {
- scsi_write_data(s->scsi_dev, s->tag);
+ s->scsi_dev->info->write_data(s->scsi_dev, s->tag);
}
}
ret = len;
{
MSDState *s = (MSDState *)dev;
- scsi_disk_destroy(s->scsi_dev);
- bdrv_delete(s->bs);
- qemu_free(s);
+ drive_uninit(s->dinfo->bdrv);
+}
+
+static int usb_msd_initfn(USBDevice *dev)
+{
+ MSDState *s = DO_UPCAST(MSDState, dev, dev);
+
+ if (!s->dinfo || !s->dinfo->bdrv) {
+ qemu_error("usb-msd: drive property not set\n");
+ return -1;
+ }
+
+ s->dev.speed = USB_SPEED_FULL;
+ scsi_bus_new(&s->bus, &s->dev.qdev, 0, 1, usb_msd_command_complete);
+ s->scsi_dev = scsi_bus_legacy_add_drive(&s->bus, s->dinfo, 0);
+ usb_msd_handle_reset(dev);
+ return 0;
}
USBDevice *usb_msd_init(const char *filename)
{
- MSDState *s;
- BlockDriverState *bdrv;
+ static int nr=0;
+ char id[8];
+ QemuOpts *opts;
+ DriveInfo *dinfo;
+ USBDevice *dev;
+ int fatal_error;
+ const char *p1;
+ char fmt[32];
+
+ /* parse -usbdevice disk: syntax into drive opts */
+ snprintf(id, sizeof(id), "usb%d", nr++);
+ opts = qemu_opts_create(&qemu_drive_opts, id, 0);
+
+ p1 = strchr(filename, ':');
+ if (p1++) {
+ const char *p2;
+
+ if (strstart(filename, "format=", &p2)) {
+ int len = MIN(p1 - p2, sizeof(fmt));
+ pstrcpy(fmt, len, p2);
+ qemu_opt_set(opts, "format", fmt);
+ } 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;
+ }
+ qemu_opt_set(opts, "file", filename);
+ qemu_opt_set(opts, "if", "none");
- s = qemu_mallocz(sizeof(MSDState));
- if (!s)
+ /* create host drive */
+ dinfo = drive_init(opts, NULL, &fatal_error);
+ if (!dinfo) {
+ qemu_opts_del(opts);
return NULL;
+ }
- bdrv = bdrv_new("usb");
- if (bdrv_open(bdrv, filename, 0) < 0)
- goto fail;
- if (qemu_key_check(bdrv, filename))
- goto fail;
- s->bs = bdrv;
+ /* create guest device */
+ dev = usb_create(NULL /* FIXME */, "QEMU USB MSD");
+ qdev_prop_set_drive(&dev->qdev, "drive", dinfo);
+ qdev_init(&dev->qdev);
- s->dev.speed = USB_SPEED_FULL;
- s->dev.handle_packet = usb_generic_handle_packet;
-
- s->dev.handle_reset = usb_msd_handle_reset;
- s->dev.handle_control = usb_msd_handle_control;
- s->dev.handle_data = usb_msd_handle_data;
- s->dev.handle_destroy = usb_msd_handle_destroy;
-
- snprintf(s->dev.devname, sizeof(s->dev.devname), "QEMU USB MSD(%.16s)",
- filename);
-
- s->scsi_dev = scsi_disk_init(bdrv, 0, usb_msd_command_complete, s);
- usb_msd_handle_reset((USBDevice *)s);
- return (USBDevice *)s;
- fail:
- qemu_free(s);
- return NULL;
+ return dev;
+}
+
+BlockDriverState *usb_msd_get_bdrv(USBDevice *dev)
+{
+ MSDState *s = (MSDState *)dev;
+
+ return s->dinfo->bdrv;
+}
+
+static struct USBDeviceInfo msd_info = {
+ .qdev.name = "QEMU USB MSD",
+ .qdev.alias = "usb-storage",
+ .qdev.size = sizeof(MSDState),
+ .init = usb_msd_initfn,
+ .handle_packet = usb_generic_handle_packet,
+ .handle_reset = usb_msd_handle_reset,
+ .handle_control = usb_msd_handle_control,
+ .handle_data = usb_msd_handle_data,
+ .handle_destroy = usb_msd_handle_destroy,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_DRIVE("drive", MSDState, dinfo),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
+static void usb_msd_register_devices(void)
+{
+ usb_qdev_register(&msd_info);
}
+device_init(usb_msd_register_devices)