* [Qemu-devel] [PATCH 0/2] usb/scsi: usb attached scsi emulation
@ 2012-07-09 10:33 Gerd Hoffmann
2012-07-09 10:33 ` [Qemu-devel] [PATCH 1/2] usb: add " Gerd Hoffmann
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Gerd Hoffmann @ 2012-07-09 10:33 UTC (permalink / raw)
To: qemu-devel; +Cc: pbonzini, Gerd Hoffmann
Hi,
v2 of the usb attached scsi emulation patches. Patch #1 is almost
unmodified compared to v1. Patch #2 is new and makes UAS emulation
use the new free_request callback (patch just posted by paolo) and
obviously depends on that patch to compile.
cheers,
Gerd
Gerd Hoffmann (2):
usb: add usb attached scsi emulation
uas: use scsi req refcounting + free_request callback
docs/usb-storage.txt | 38 +++
hw/usb/Makefile.objs | 1 +
hw/usb/dev-uas.c | 779 ++++++++++++++++++++++++++++++++++++++++++++++++++
trace-events | 14 +
4 files changed, 832 insertions(+), 0 deletions(-)
create mode 100644 docs/usb-storage.txt
create mode 100644 hw/usb/dev-uas.c
^ permalink raw reply [flat|nested] 6+ messages in thread
* [Qemu-devel] [PATCH 1/2] usb: add usb attached scsi emulation
2012-07-09 10:33 [Qemu-devel] [PATCH 0/2] usb/scsi: usb attached scsi emulation Gerd Hoffmann
@ 2012-07-09 10:33 ` Gerd Hoffmann
2012-07-09 10:33 ` [Qemu-devel] [PATCH 2/2] uas: use scsi req refcounting + free_request callback Gerd Hoffmann
2012-07-09 10:39 ` [Qemu-devel] [PATCH 0/2] usb/scsi: usb attached scsi emulation Gerd Hoffmann
2 siblings, 0 replies; 6+ messages in thread
From: Gerd Hoffmann @ 2012-07-09 10:33 UTC (permalink / raw)
To: qemu-devel; +Cc: pbonzini, Gerd Hoffmann
$subject says all. First cut.
It's a pure UAS (usb attached scsi) emulation, without BOT (bulk-only
transport) compatibility. If your guest can't handle it use usb-storage
instead.
The emulation works like any other scsi hba emulation (eps, lsi, virtio,
megasas, ...). It provides just the HBA where you can attach scsi
devices as you like using '-device'. A single scsi target with up to
256 luns is supported.
For now only usb 2.0 transport is supported. This will change in the
future though as I plan to use this as playground when codeing up &
testing usb 3.0 transport and streams support in the qemu usb core and
the xhci emulation.
No migration support yet. I'm planning to add usb 3.0 support first as
this probably requires saving additional state.
Special thanks go to Paolo for bringing the qemu scsi emulation into
shape, so this can be added nicely without having to touch a single line
of scsi code.
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
docs/usb-storage.txt | 38 +++
hw/usb/Makefile.objs | 1 +
hw/usb/dev-uas.c | 792 ++++++++++++++++++++++++++++++++++++++++++++++++++
trace-events | 14 +
4 files changed, 845 insertions(+), 0 deletions(-)
create mode 100644 docs/usb-storage.txt
create mode 100644 hw/usb/dev-uas.c
diff --git a/docs/usb-storage.txt b/docs/usb-storage.txt
new file mode 100644
index 0000000..ff97559
--- /dev/null
+++ b/docs/usb-storage.txt
@@ -0,0 +1,38 @@
+
+qemu usb storage emulation
+--------------------------
+
+Qemu has two emulations for usb storage devices.
+
+Number one emulates the classic bulk-only transport protocol which is
+used by 99% of the usb sticks on the marked today and is called
+"usb-storage". Usage (hooking up to xhci, other host controllers work
+too):
+
+ qemu ${other_vm_args} \
+ -drive if=none,id=stick,file=/path/to/file.img \
+ -device nec-usb-xhci,id=xhci \
+ -device usb-storage,bus=xhci.0,drive=stick
+
+
+Number two is the newer usb attached scsi transport. This one doesn't
+automagically create a scsi disk, so you have to explicitly attach one
+manually. Multiple logical units are supported. Here is an example
+with tree logical units:
+
+ qemu ${other_vm_args} \
+ -drive if=none,id=uas-disk1,file=/path/to/file1.img \
+ -drive if=none,id=uas-disk2,file=/path/to/file2.img \
+ -drive if=none,id=uas-cdrom,media=cdrom,file=/path/to/image.iso \
+ -device nec-usb-xhci,id=xhci \
+ -device usb-uas,id=uas,bus=xhci.0 \
+ -device scsi-hd,bus=uas.0,scsi-id=0,lun=0,drive=uas-disk1 \
+ -device scsi-hd,bus=uas.0,scsi-id=0,lun=1,drive=uas-disk2 \
+ -device scsi-cd,bus=uas.0,scsi-id=0,lun=5,drive=uas-cdrom
+
+
+enjoy,
+ Gerd
+
+--
+Gerd Hoffmann <kraxel@redhat.com>
diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs
index 9c7ddf5..4225136 100644
--- a/hw/usb/Makefile.objs
+++ b/hw/usb/Makefile.objs
@@ -11,3 +11,4 @@ common-obj-y += core.o bus.o desc.o dev-hub.o
common-obj-y += host-$(HOST_USB).o dev-bluetooth.o
common-obj-y += dev-hid.o dev-storage.o dev-wacom.o
common-obj-y += dev-serial.o dev-network.o dev-audio.o
+common-obj-y += dev-uas.o
diff --git a/hw/usb/dev-uas.c b/hw/usb/dev-uas.c
new file mode 100644
index 0000000..21fc80e
--- /dev/null
+++ b/hw/usb/dev-uas.c
@@ -0,0 +1,792 @@
+/*
+ * UAS (USB Attached SCSI) emulation
+ *
+ * Copyright Red Hat, Inc. 2012
+ *
+ * Author: Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu-common.h"
+#include "qemu-option.h"
+#include "qemu-config.h"
+#include "trace.h"
+
+#include "hw/usb.h"
+#include "hw/usb/desc.h"
+#include "hw/scsi.h"
+#include "hw/scsi-defs.h"
+
+/* --------------------------------------------------------------------- */
+
+#define UAS_UI_COMMAND 0x01
+#define UAS_UI_SENSE 0x03
+#define UAS_UI_RESPONSE 0x04
+#define UAS_UI_TASK_MGMT 0x05
+#define UAS_UI_READ_READY 0x06
+#define UAS_UI_WRITE_READY 0x07
+
+#define UAS_RC_TMF_COMPLETE 0x00
+#define UAS_RC_INVALID_INFO_UNIT 0x02
+#define UAS_RC_TMF_NOT_SUPPORTED 0x04
+#define UAS_RC_TMF_FAILED 0x05
+#define UAS_RC_TMF_SUCCEEDED 0x08
+#define UAS_RC_INCORRECT_LUN 0x09
+#define UAS_RC_OVERLAPPED_TAG 0x0a
+
+#define UAS_TMF_ABORT_TASK 0x01
+#define UAS_TMF_ABORT_TASK_SET 0x02
+#define UAS_TMF_CLEAR_TASK_SET 0x04
+#define UAS_TMF_LOGICAL_UNIT_RESET 0x08
+#define UAS_TMF_I_T_NEXUS_RESET 0x10
+#define UAS_TMF_CLEAR_ACA 0x40
+#define UAS_TMF_QUERY_TASK 0x80
+#define UAS_TMF_QUERY_TASK_SET 0x81
+#define UAS_TMF_QUERY_ASYNC_EVENT 0x82
+
+#define UAS_PIPE_ID_COMMAND 0x01
+#define UAS_PIPE_ID_STATUS 0x02
+#define UAS_PIPE_ID_DATA_IN 0x03
+#define UAS_PIPE_ID_DATA_OUT 0x04
+
+typedef struct {
+ uint8_t id;
+ uint8_t reserved;
+ uint16_t tag;
+} QEMU_PACKED uas_ui_header;
+
+typedef struct {
+ uint8_t prio_taskattr; /* 6:3 priority, 2:0 task attribute */
+ uint8_t reserved_1;
+ uint8_t add_cdb_length; /* 7:2 additional adb length (dwords) */
+ uint8_t reserved_2;
+ uint64_t lun;
+ uint8_t cdb[16];
+ uint8_t add_cdb[];
+} QEMU_PACKED uas_ui_command;
+
+typedef struct {
+ uint16_t status_qualifier;
+ uint8_t status;
+ uint8_t reserved[7];
+ uint16_t sense_length;
+ uint8_t sense_data[18];
+} QEMU_PACKED uas_ui_sense;
+
+typedef struct {
+ uint16_t add_response_info;
+ uint8_t response_code;
+} QEMU_PACKED uas_ui_response;
+
+typedef struct {
+ uint8_t function;
+ uint8_t reserved;
+ uint16_t task_tag;
+ uint64_t lun;
+} QEMU_PACKED uas_ui_task_mgmt;
+
+typedef struct {
+ uas_ui_header hdr;
+ union {
+ uas_ui_command command;
+ uas_ui_sense sense;
+ uas_ui_task_mgmt task;
+ uas_ui_response response;
+ };
+} QEMU_PACKED uas_ui;
+
+/* --------------------------------------------------------------------- */
+
+typedef struct UASDevice UASDevice;
+typedef struct UASRequest UASRequest;
+typedef struct UASStatus UASStatus;
+
+struct UASDevice {
+ USBDevice dev;
+ SCSIBus bus;
+ UASRequest *datain;
+ UASRequest *dataout;
+ USBPacket *status;
+ QEMUBH *status_bh;
+ QTAILQ_HEAD(, UASStatus) results;
+ QTAILQ_HEAD(, UASRequest) requests;
+};
+
+struct UASRequest {
+ uint16_t tag;
+ uint64_t lun;
+ UASDevice *uas;
+ SCSIDevice *dev;
+ SCSIRequest *req;
+ USBPacket *data;
+ bool data_async;
+ bool active;
+ uint32_t buf_off;
+ uint32_t buf_size;
+ uint32_t data_off;
+ uint32_t data_size;
+ uint32_t refcount;
+ QTAILQ_ENTRY(UASRequest) next;
+};
+
+struct UASStatus {
+ uas_ui status;
+ uint32_t length;
+ QTAILQ_ENTRY(UASStatus) next;
+};
+
+/* --------------------------------------------------------------------- */
+
+enum {
+ STR_MANUFACTURER = 1,
+ STR_PRODUCT,
+ STR_SERIALNUMBER,
+ STR_CONFIG_HIGH,
+};
+
+static const USBDescStrings desc_strings = {
+ [STR_MANUFACTURER] = "QEMU",
+ [STR_PRODUCT] = "USB Attached SCSI HBA",
+ [STR_SERIALNUMBER] = "27842",
+ [STR_CONFIG_HIGH] = "High speed config (usb 2.0)",
+};
+
+static const USBDescIface desc_iface_high = {
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 4,
+ .bInterfaceClass = USB_CLASS_MASS_STORAGE,
+ .bInterfaceSubClass = 0x06, /* SCSI */
+ .bInterfaceProtocol = 0x62, /* UAS */
+ .eps = (USBDescEndpoint[]) {
+ {
+ .bEndpointAddress = USB_DIR_OUT | UAS_PIPE_ID_COMMAND,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 512,
+ .extra = (uint8_t[]) {
+ 0x04, /* u8 bLength */
+ 0x24, /* u8 bDescriptorType */
+ UAS_PIPE_ID_COMMAND,
+ 0x00, /* u8 bReserved */
+ },
+ },{
+ .bEndpointAddress = USB_DIR_IN | UAS_PIPE_ID_STATUS,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 512,
+ .extra = (uint8_t[]) {
+ 0x04, /* u8 bLength */
+ 0x24, /* u8 bDescriptorType */
+ UAS_PIPE_ID_STATUS,
+ 0x00, /* u8 bReserved */
+ },
+ },{
+ .bEndpointAddress = USB_DIR_IN | UAS_PIPE_ID_DATA_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 512,
+ .extra = (uint8_t[]) {
+ 0x04, /* u8 bLength */
+ 0x24, /* u8 bDescriptorType */
+ UAS_PIPE_ID_DATA_IN,
+ 0x00, /* u8 bReserved */
+ },
+ },{
+ .bEndpointAddress = USB_DIR_OUT | UAS_PIPE_ID_DATA_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 512,
+ .extra = (uint8_t[]) {
+ 0x04, /* u8 bLength */
+ 0x24, /* u8 bDescriptorType */
+ UAS_PIPE_ID_DATA_OUT,
+ 0x00, /* u8 bReserved */
+ },
+ },
+ }
+};
+
+static const USBDescDevice desc_device_high = {
+ .bcdUSB = 0x0200,
+ .bMaxPacketSize0 = 64,
+ .bNumConfigurations = 1,
+ .confs = (USBDescConfig[]) {
+ {
+ .bNumInterfaces = 1,
+ .bConfigurationValue = 1,
+ .iConfiguration = STR_CONFIG_HIGH,
+ .bmAttributes = 0xc0,
+ .nif = 1,
+ .ifs = &desc_iface_high,
+ },
+ },
+};
+
+static const USBDesc desc = {
+ .id = {
+ .idVendor = 0x46f4, /* CRC16() of "QEMU" */
+ .idProduct = 0x0002,
+ .bcdDevice = 0,
+ .iManufacturer = STR_MANUFACTURER,
+ .iProduct = STR_PRODUCT,
+ .iSerialNumber = STR_SERIALNUMBER,
+ },
+ .high = &desc_device_high,
+ .str = desc_strings,
+};
+
+/* --------------------------------------------------------------------- */
+
+static UASStatus *usb_uas_alloc_status(uint8_t id, uint16_t tag)
+{
+ UASStatus *st = g_new0(UASStatus, 1);
+
+ st->status.hdr.id = id;
+ st->status.hdr.tag = cpu_to_be16(tag);
+ st->length = sizeof(uas_ui_header);
+ return st;
+}
+
+static void usb_uas_send_status_bh(void *opaque)
+{
+ UASDevice *uas = opaque;
+ UASStatus *st = QTAILQ_FIRST(&uas->results);
+ USBPacket *p = uas->status;
+
+ assert(p != NULL);
+ assert(st != NULL);
+
+ uas->status = NULL;
+ usb_packet_copy(p, &st->status, st->length);
+ p->result = st->length;
+ QTAILQ_REMOVE(&uas->results, st, next);
+ g_free(st);
+
+ usb_packet_complete(&uas->dev, p);
+}
+
+static void usb_uas_queue_status(UASDevice *uas, UASStatus *st, int length)
+{
+ st->length += length;
+ QTAILQ_INSERT_TAIL(&uas->results, st, next);
+ if (uas->status) {
+ /*
+ * Just schedule bh make sure any in-flight data transaction
+ * is finished before completing (sending) the status packet.
+ */
+ qemu_bh_schedule(uas->status_bh);
+ } else {
+ USBEndpoint *ep = usb_ep_get(&uas->dev, USB_TOKEN_IN,
+ UAS_PIPE_ID_STATUS);
+ usb_wakeup(ep);
+ }
+}
+
+static void usb_uas_queue_response(UASDevice *uas, uint16_t tag,
+ uint8_t code, uint16_t add_info)
+{
+ UASStatus *st = usb_uas_alloc_status(UAS_UI_RESPONSE, tag);
+
+ trace_usb_uas_response(uas->dev.addr, tag, code);
+ st->status.response.response_code = code;
+ st->status.response.add_response_info = cpu_to_be16(add_info);
+ usb_uas_queue_status(uas, st, sizeof(uas_ui_response));
+}
+
+static void usb_uas_queue_sense(UASRequest *req, uint8_t status)
+{
+ UASStatus *st = usb_uas_alloc_status(UAS_UI_SENSE, req->tag);
+ int len, slen = 0;
+
+ trace_usb_uas_sense(req->uas->dev.addr, req->tag, status);
+ st->status.sense.status = status;
+ st->status.sense.status_qualifier = cpu_to_be16(0);
+ if (status != GOOD) {
+ slen = scsi_req_get_sense(req->req, st->status.sense.sense_data,
+ sizeof(st->status.sense.sense_data));
+ st->status.sense.sense_length = cpu_to_be16(slen);
+ }
+ len = sizeof(uas_ui_sense) - sizeof(st->status.sense.sense_data) + slen;
+ usb_uas_queue_status(req->uas, st, len);
+}
+
+static void usb_uas_queue_read_ready(UASRequest *req)
+{
+ UASStatus *st = usb_uas_alloc_status(UAS_UI_READ_READY, req->tag);
+
+ trace_usb_uas_read_ready(req->uas->dev.addr, req->tag);
+ usb_uas_queue_status(req->uas, st, 0);
+}
+
+static void usb_uas_queue_write_ready(UASRequest *req)
+{
+ UASStatus *st = usb_uas_alloc_status(UAS_UI_WRITE_READY, req->tag);
+
+ trace_usb_uas_write_ready(req->uas->dev.addr, req->tag);
+ usb_uas_queue_status(req->uas, st, 0);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int usb_uas_get_lun(uint64_t lun64)
+{
+ return (lun64 >> 48) & 0xff;
+}
+
+static SCSIDevice *usb_uas_get_dev(UASDevice *uas, uint64_t lun64)
+{
+ if ((lun64 >> 56) != 0x00) {
+ return NULL;
+ }
+ return scsi_device_find(&uas->bus, 0, 0, usb_uas_get_lun(lun64));
+}
+
+static void usb_uas_complete_data_packet(UASRequest *req)
+{
+ USBPacket *p;
+
+ if (!req->data_async) {
+ return;
+ }
+ p = req->data;
+ req->data = NULL;
+ req->data_async = false;
+ usb_packet_complete(&req->uas->dev, p);
+}
+
+static void usb_uas_copy_data(UASRequest *req)
+{
+ uint32_t length;
+
+ length = MIN(req->buf_size - req->buf_off,
+ req->data->iov.size - req->data->result);
+ trace_usb_uas_xfer_data(req->uas->dev.addr, req->tag, length,
+ req->data->result, req->data->iov.size,
+ req->buf_off, req->buf_size);
+ usb_packet_copy(req->data, scsi_req_get_buf(req->req) + req->buf_off,
+ length);
+ req->buf_off += length;
+ req->data_off += length;
+
+ if (req->data->result == req->data->iov.size) {
+ usb_uas_complete_data_packet(req);
+ }
+ if (req->buf_size && req->buf_off == req->buf_size) {
+ req->buf_off = 0;
+ req->buf_size = 0;
+ scsi_req_continue(req->req);
+ }
+}
+
+static void usb_uas_start_next_transfer(UASDevice *uas)
+{
+ UASRequest *req;
+
+ QTAILQ_FOREACH(req, &uas->requests, next) {
+ if (req->active) {
+ continue;
+ }
+ if (req->req->cmd.mode == SCSI_XFER_FROM_DEV && uas->datain == NULL) {
+ uas->datain = req;
+ usb_uas_queue_read_ready(req);
+ req->active = true;
+ return;
+ }
+ if (req->req->cmd.mode == SCSI_XFER_TO_DEV && uas->dataout == NULL) {
+ uas->dataout = req;
+ usb_uas_queue_write_ready(req);
+ req->active = true;
+ return;
+ }
+ }
+}
+
+static UASRequest *usb_uas_alloc_request(UASDevice *uas, uas_ui *ui)
+{
+ UASRequest *req;
+
+ req = g_new0(UASRequest, 1);
+ req->uas = uas;
+ req->tag = be16_to_cpu(ui->hdr.tag);
+ req->lun = be64_to_cpu(ui->command.lun);
+ req->dev = usb_uas_get_dev(req->uas, req->lun);
+ req->refcount = 1;
+ return req;
+}
+
+static void usb_uas_release_request(UASRequest *req)
+{
+ UASDevice *uas = req->uas;
+
+ if (req == uas->datain) {
+ uas->datain = NULL;
+ }
+ if (req == uas->dataout) {
+ uas->dataout = NULL;
+ }
+ QTAILQ_REMOVE(&uas->requests, req, next);
+ g_free(req);
+}
+
+static UASRequest *usb_uas_find_request(UASDevice *uas, uint16_t tag)
+{
+ UASRequest *req;
+
+ QTAILQ_FOREACH(req, &uas->requests, next) {
+ if (req->tag == tag) {
+ return req;
+ }
+ }
+ return NULL;
+}
+
+static void usb_uas_ref_request(UASRequest *req)
+{
+ req->refcount++;
+}
+
+static void usb_uas_unref_request(UASRequest *req)
+{
+ req->refcount--;
+ if (req->refcount == 0) {
+ usb_uas_release_request(req);
+ }
+}
+
+static void usb_uas_scsi_transfer_data(SCSIRequest *r, uint32_t len)
+{
+ UASRequest *req = r->hba_private;
+
+ trace_usb_uas_scsi_data(req->uas->dev.addr, req->tag, len);
+ req->buf_off = 0;
+ req->buf_size = len;
+ if (req->data) {
+ usb_uas_copy_data(req);
+ } else {
+ usb_uas_start_next_transfer(req->uas);
+ }
+}
+
+static void usb_uas_scsi_command_complete(SCSIRequest *r,
+ uint32_t status, size_t resid)
+{
+ UASRequest *req = r->hba_private;
+ UASDevice *uas = req->uas;
+
+ trace_usb_uas_scsi_complete(req->uas->dev.addr, req->tag, status, resid);
+ if (req->data) {
+ usb_uas_complete_data_packet(req);
+ }
+ usb_uas_queue_sense(req, status);
+ scsi_req_unref(req->req);
+ req->req = NULL;
+ usb_uas_unref_request(req);
+ usb_uas_start_next_transfer(uas);
+}
+
+static void usb_uas_scsi_request_cancelled(SCSIRequest *r)
+{
+ UASRequest *req = r->hba_private;
+
+ /* FIXME: queue notification to status pipe? */
+ usb_uas_unref_request(req);
+}
+
+static const struct SCSIBusInfo usb_uas_scsi_info = {
+ .tcq = true,
+ .max_target = 0,
+ .max_lun = 255,
+
+ .transfer_data = usb_uas_scsi_transfer_data,
+ .complete = usb_uas_scsi_command_complete,
+ .cancel = usb_uas_scsi_request_cancelled,
+};
+
+/* --------------------------------------------------------------------- */
+
+static void usb_uas_handle_reset(USBDevice *dev)
+{
+ UASDevice *uas = DO_UPCAST(UASDevice, dev, dev);
+ UASRequest *req, *nreq;
+ UASStatus *st, *nst;
+
+ trace_usb_uas_reset(dev->addr);
+ QTAILQ_FOREACH_SAFE(req, &uas->requests, next, nreq) {
+ scsi_req_cancel(req->req);
+ }
+ QTAILQ_FOREACH_SAFE(st, &uas->results, next, nst) {
+ QTAILQ_REMOVE(&uas->results, st, next);
+ g_free(st);
+ }
+}
+
+static int usb_uas_handle_control(USBDevice *dev, USBPacket *p,
+ int request, int value, int index, int length, uint8_t *data)
+{
+ int ret;
+
+ ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
+ if (ret >= 0) {
+ return ret;
+ }
+ fprintf(stderr, "%s: unhandled control request\n", __func__);
+ return USB_RET_STALL;
+}
+
+static void usb_uas_cancel_io(USBDevice *dev, USBPacket *p)
+{
+ UASDevice *uas = DO_UPCAST(UASDevice, dev, dev);
+ UASRequest *req, *nreq;
+
+ if (uas->status == p) {
+ uas->status = NULL;
+ qemu_bh_cancel(uas->status_bh);
+ return;
+ }
+ QTAILQ_FOREACH_SAFE(req, &uas->requests, next, nreq) {
+ if (req->data == p) {
+ req->data = NULL;
+ return;
+ }
+ }
+ assert(!"canceled usb packet not found");
+}
+
+static void usb_uas_command(UASDevice *uas, uas_ui *ui)
+{
+ UASRequest *req;
+ uint32_t len;
+
+ req = usb_uas_find_request(uas, be16_to_cpu(ui->hdr.tag));
+ if (req) {
+ goto overlapped_tag;
+ }
+ req = usb_uas_alloc_request(uas, ui);
+ if (req->dev == NULL) {
+ goto bad_target;
+ }
+
+ trace_usb_uas_command(uas->dev.addr, req->tag,
+ usb_uas_get_lun(req->lun),
+ req->lun >> 32, req->lun & 0xffffffff);
+ QTAILQ_INSERT_TAIL(&uas->requests, req, next);
+ req->req = scsi_req_new(req->dev, req->tag,
+ usb_uas_get_lun(req->lun),
+ ui->command.cdb, req);
+ len = scsi_req_enqueue(req->req);
+ if (len) {
+ req->data_size = len;
+ scsi_req_continue(req->req);
+ }
+ return;
+
+overlapped_tag:
+ usb_uas_queue_response(uas, req->tag, UAS_RC_OVERLAPPED_TAG, 0);
+ return;
+
+bad_target:
+ /*
+ * FIXME: Seems to upset linux, is this wrong?
+ * NOTE: Happens only with no scsi devices at the bus, not sure
+ * this is a valid UAS setup in the first place.
+ */
+ usb_uas_queue_response(uas, req->tag, UAS_RC_INVALID_INFO_UNIT, 0);
+ g_free(req);
+ return;
+}
+
+static void usb_uas_task(UASDevice *uas, uas_ui *ui)
+{
+ uint16_t tag = be16_to_cpu(ui->hdr.tag);
+ uint64_t lun64 = be64_to_cpu(ui->task.lun);
+ SCSIDevice *dev = usb_uas_get_dev(uas, lun64);
+ int lun = usb_uas_get_lun(lun64);
+ UASRequest *req;
+ uint16_t task_tag;
+
+ req = usb_uas_find_request(uas, be16_to_cpu(ui->hdr.tag));
+ if (req) {
+ goto overlapped_tag;
+ }
+
+ switch (ui->task.function) {
+ case UAS_TMF_ABORT_TASK:
+ task_tag = be16_to_cpu(ui->task.task_tag);
+ trace_usb_uas_tmf_abort_task(uas->dev.addr, tag, task_tag);
+ if (dev == NULL) {
+ goto bad_target;
+ }
+ if (dev->lun != lun) {
+ goto incorrect_lun;
+ }
+ req = usb_uas_find_request(uas, task_tag);
+ if (req && req->dev == dev) {
+ scsi_req_cancel(req->req);
+ }
+ usb_uas_queue_response(uas, tag, UAS_RC_TMF_COMPLETE, 0);
+ break;
+
+ case UAS_TMF_LOGICAL_UNIT_RESET:
+ trace_usb_uas_tmf_logical_unit_reset(uas->dev.addr, tag, lun);
+ if (dev == NULL) {
+ goto bad_target;
+ }
+ if (dev->lun != lun) {
+ goto incorrect_lun;
+ }
+ qdev_reset_all(&dev->qdev);
+ usb_uas_queue_response(uas, tag, UAS_RC_TMF_COMPLETE, 0);
+ break;
+
+ default:
+ trace_usb_uas_tmf_unsupported(uas->dev.addr, tag, ui->task.function);
+ usb_uas_queue_response(uas, tag, UAS_RC_TMF_NOT_SUPPORTED, 0);
+ break;
+ }
+ return;
+
+overlapped_tag:
+ usb_uas_queue_response(uas, req->tag, UAS_RC_OVERLAPPED_TAG, 0);
+ return;
+
+bad_target:
+ /* FIXME: correct? [see long comment in usb_uas_command()] */
+ usb_uas_queue_response(uas, tag, UAS_RC_INVALID_INFO_UNIT, 0);
+ return;
+
+incorrect_lun:
+ usb_uas_queue_response(uas, tag, UAS_RC_INCORRECT_LUN, 0);
+ return;
+}
+
+static int usb_uas_handle_data(USBDevice *dev, USBPacket *p)
+{
+ UASDevice *uas = DO_UPCAST(UASDevice, dev, dev);
+ uas_ui ui;
+ UASStatus *st;
+ UASRequest *req;
+ int length, ret = 0;
+
+ switch (p->ep->nr) {
+ case UAS_PIPE_ID_COMMAND:
+ length = MIN(sizeof(ui), p->iov.size);
+ usb_packet_copy(p, &ui, length);
+ switch (ui.hdr.id) {
+ case UAS_UI_COMMAND:
+ usb_uas_command(uas, &ui);
+ ret = length;
+ break;
+ case UAS_UI_TASK_MGMT:
+ usb_uas_task(uas, &ui);
+ ret = length;
+ break;
+ default:
+ fprintf(stderr, "%s: unknown command ui: id 0x%x\n",
+ __func__, ui.hdr.id);
+ ret = USB_RET_STALL;
+ break;
+ }
+ break;
+ case UAS_PIPE_ID_STATUS:
+ st = QTAILQ_FIRST(&uas->results);
+ if (st == NULL) {
+ assert(uas->status == NULL);
+ uas->status = p;
+ ret = USB_RET_ASYNC;
+ break;
+ }
+ usb_packet_copy(p, &st->status, st->length);
+ ret = st->length;
+ QTAILQ_REMOVE(&uas->results, st, next);
+ g_free(st);
+ break;
+ case UAS_PIPE_ID_DATA_IN:
+ case UAS_PIPE_ID_DATA_OUT:
+ req = (p->ep->nr == UAS_PIPE_ID_DATA_IN) ? uas->datain : uas->dataout;
+ if (req == NULL) {
+ fprintf(stderr, "%s: no inflight request\n", __func__);
+ ret = USB_RET_STALL;
+ break;
+ }
+ usb_uas_ref_request(req);
+ req->data = p;
+ usb_uas_copy_data(req);
+ if (p->result == p->iov.size || req->req == NULL) {
+ req->data = NULL;
+ ret = p->result;
+ } else {
+ req->data_async = true;
+ ret = USB_RET_ASYNC;
+ }
+ usb_uas_unref_request(req);
+ usb_uas_start_next_transfer(uas);
+ break;
+ default:
+ fprintf(stderr, "%s: invalid endpoint %d\n", __func__, p->ep->nr);
+ ret = USB_RET_STALL;
+ break;
+ }
+ return ret;
+}
+
+static void usb_uas_handle_destroy(USBDevice *dev)
+{
+ UASDevice *uas = DO_UPCAST(UASDevice, dev, dev);
+
+ qemu_bh_delete(uas->status_bh);
+}
+
+static int usb_uas_init(USBDevice *dev)
+{
+ UASDevice *uas = DO_UPCAST(UASDevice, dev, dev);
+
+ usb_desc_create_serial(dev);
+ usb_desc_init(dev);
+
+ QTAILQ_INIT(&uas->results);
+ QTAILQ_INIT(&uas->requests);
+ uas->status_bh = qemu_bh_new(usb_uas_send_status_bh, uas);
+
+ scsi_bus_new(&uas->bus, &uas->dev.qdev, &usb_uas_scsi_info);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_usb_uas = {
+ .name = "usb-uas",
+ .unmigratable = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_USB_DEVICE(dev, UASDevice),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void usb_uas_class_initfn(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
+
+ uc->init = usb_uas_init;
+ uc->product_desc = desc_strings[STR_PRODUCT];
+ uc->usb_desc = &desc;
+ uc->cancel_packet = usb_uas_cancel_io;
+ uc->handle_attach = usb_desc_attach;
+ uc->handle_reset = usb_uas_handle_reset;
+ uc->handle_control = usb_uas_handle_control;
+ uc->handle_data = usb_uas_handle_data;
+ uc->handle_destroy = usb_uas_handle_destroy;
+ dc->fw_name = "storage";
+ dc->vmsd = &vmstate_usb_uas;
+}
+
+static TypeInfo uas_info = {
+ .name = "usb-uas",
+ .parent = TYPE_USB_DEVICE,
+ .instance_size = sizeof(UASDevice),
+ .class_init = usb_uas_class_initfn,
+};
+
+static void usb_uas_register_types(void)
+{
+ type_register_static(&uas_info);
+}
+
+type_init(usb_uas_register_types)
diff --git a/trace-events b/trace-events
index c33d58c..6cc910e 100644
--- a/trace-events
+++ b/trace-events
@@ -347,6 +347,20 @@ usb_hub_clear_port_feature(int addr, int nr, const char *f) "dev %d, port %d, fe
usb_hub_attach(int addr, int nr) "dev %d, port %d"
usb_hub_detach(int addr, int nr) "dev %d, port %d"
+# hw/usb/dev-uas.c
+usb_uas_reset(int addr) "dev %d"
+usb_uas_command(int addr, uint16_t tag, int lun, uint32_t lun64_1, uint32_t lun64_2) "dev %d, tag 0x%x, lun %d, lun64 %08x-%08x"
+usb_uas_response(int addr, uint16_t tag, uint8_t code) "dev %d, tag 0x%x, code 0x%x"
+usb_uas_sense(int addr, uint16_t tag, uint8_t status) "dev %d, tag 0x%x, status 0x%x"
+usb_uas_read_ready(int addr, uint16_t tag) "dev %d, tag 0x%x"
+usb_uas_write_ready(int addr, uint16_t tag) "dev %d, tag 0x%x"
+usb_uas_xfer_data(int addr, uint16_t tag, uint32_t copy, uint32_t uoff, uint32_t usize, uint32_t soff, uint32_t ssize) "dev %d, tag 0x%x, copy %d, usb-pkt %d/%d, scsi-buf %d/%d"
+usb_uas_scsi_data(int addr, uint16_t tag, uint32_t bytes) "dev %d, tag 0x%x, bytes %d"
+usb_uas_scsi_complete(int addr, uint16_t tag, uint32_t status, uint32_t resid) "dev %d, tag 0x%x, status 0x%x, residue %d"
+usb_uas_tmf_abort_task(int addr, uint16_t tag, uint16_t task_tag) "dev %d, tag 0x%x, task-tag 0x%x"
+usb_uas_tmf_logical_unit_reset(int addr, uint16_t tag, int lun) "dev %d, tag 0x%x, lun %d"
+usb_uas_tmf_unsupported(int addr, uint16_t tag, uint32_t function) "dev %d, tag 0x%x, function 0x%x"
+
# hw/usb/host-linux.c
usb_host_open_started(int bus, int addr) "dev %d:%d"
usb_host_open_success(int bus, int addr) "dev %d:%d"
--
1.7.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [Qemu-devel] [PATCH 2/2] uas: use scsi req refcounting + free_request callback
2012-07-09 10:33 [Qemu-devel] [PATCH 0/2] usb/scsi: usb attached scsi emulation Gerd Hoffmann
2012-07-09 10:33 ` [Qemu-devel] [PATCH 1/2] usb: add " Gerd Hoffmann
@ 2012-07-09 10:33 ` Gerd Hoffmann
2012-07-09 10:39 ` [Qemu-devel] [PATCH 0/2] usb/scsi: usb attached scsi emulation Gerd Hoffmann
2 siblings, 0 replies; 6+ messages in thread
From: Gerd Hoffmann @ 2012-07-09 10:33 UTC (permalink / raw)
To: qemu-devel; +Cc: pbonzini, Gerd Hoffmann
With the new free_request callback for SCSIBusInfo we can use scsi
request refcounting instead of our own for the uas request lifecycle.
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
hw/usb/dev-uas.c | 33 ++++++++++-----------------------
1 files changed, 10 insertions(+), 23 deletions(-)
diff --git a/hw/usb/dev-uas.c b/hw/usb/dev-uas.c
index 21fc80e..9b02ff4 100644
--- a/hw/usb/dev-uas.c
+++ b/hw/usb/dev-uas.c
@@ -123,11 +123,11 @@ struct UASRequest {
USBPacket *data;
bool data_async;
bool active;
+ bool complete;
uint32_t buf_off;
uint32_t buf_size;
uint32_t data_off;
uint32_t data_size;
- uint32_t refcount;
QTAILQ_ENTRY(UASRequest) next;
};
@@ -381,7 +381,7 @@ static void usb_uas_start_next_transfer(UASDevice *uas)
UASRequest *req;
QTAILQ_FOREACH(req, &uas->requests, next) {
- if (req->active) {
+ if (req->active || req->complete) {
continue;
}
if (req->req->cmd.mode == SCSI_XFER_FROM_DEV && uas->datain == NULL) {
@@ -408,12 +408,12 @@ static UASRequest *usb_uas_alloc_request(UASDevice *uas, uas_ui *ui)
req->tag = be16_to_cpu(ui->hdr.tag);
req->lun = be64_to_cpu(ui->command.lun);
req->dev = usb_uas_get_dev(req->uas, req->lun);
- req->refcount = 1;
return req;
}
-static void usb_uas_release_request(UASRequest *req)
+static void usb_uas_scsi_free_request(SCSIBus *bus, void *priv)
{
+ UASRequest *req = priv;
UASDevice *uas = req->uas;
if (req == uas->datain) {
@@ -438,19 +438,6 @@ static UASRequest *usb_uas_find_request(UASDevice *uas, uint16_t tag)
return NULL;
}
-static void usb_uas_ref_request(UASRequest *req)
-{
- req->refcount++;
-}
-
-static void usb_uas_unref_request(UASRequest *req)
-{
- req->refcount--;
- if (req->refcount == 0) {
- usb_uas_release_request(req);
- }
-}
-
static void usb_uas_scsi_transfer_data(SCSIRequest *r, uint32_t len)
{
UASRequest *req = r->hba_private;
@@ -472,13 +459,12 @@ static void usb_uas_scsi_command_complete(SCSIRequest *r,
UASDevice *uas = req->uas;
trace_usb_uas_scsi_complete(req->uas->dev.addr, req->tag, status, resid);
+ req->complete = true;
if (req->data) {
usb_uas_complete_data_packet(req);
}
usb_uas_queue_sense(req, status);
scsi_req_unref(req->req);
- req->req = NULL;
- usb_uas_unref_request(req);
usb_uas_start_next_transfer(uas);
}
@@ -487,7 +473,7 @@ static void usb_uas_scsi_request_cancelled(SCSIRequest *r)
UASRequest *req = r->hba_private;
/* FIXME: queue notification to status pipe? */
- usb_uas_unref_request(req);
+ scsi_req_unref(req->req);
}
static const struct SCSIBusInfo usb_uas_scsi_info = {
@@ -498,6 +484,7 @@ static const struct SCSIBusInfo usb_uas_scsi_info = {
.transfer_data = usb_uas_scsi_transfer_data,
.complete = usb_uas_scsi_command_complete,
.cancel = usb_uas_scsi_request_cancelled,
+ .free_request = usb_uas_scsi_free_request,
};
/* --------------------------------------------------------------------- */
@@ -706,17 +693,17 @@ static int usb_uas_handle_data(USBDevice *dev, USBPacket *p)
ret = USB_RET_STALL;
break;
}
- usb_uas_ref_request(req);
+ scsi_req_ref(req->req);
req->data = p;
usb_uas_copy_data(req);
- if (p->result == p->iov.size || req->req == NULL) {
+ if (p->result == p->iov.size || req->complete) {
req->data = NULL;
ret = p->result;
} else {
req->data_async = true;
ret = USB_RET_ASYNC;
}
- usb_uas_unref_request(req);
+ scsi_req_unref(req->req);
usb_uas_start_next_transfer(uas);
break;
default:
--
1.7.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [Qemu-devel] [PATCH 0/2] usb/scsi: usb attached scsi emulation
2012-07-09 10:33 [Qemu-devel] [PATCH 0/2] usb/scsi: usb attached scsi emulation Gerd Hoffmann
2012-07-09 10:33 ` [Qemu-devel] [PATCH 1/2] usb: add " Gerd Hoffmann
2012-07-09 10:33 ` [Qemu-devel] [PATCH 2/2] uas: use scsi req refcounting + free_request callback Gerd Hoffmann
@ 2012-07-09 10:39 ` Gerd Hoffmann
2012-07-09 10:45 ` Paolo Bonzini
2 siblings, 1 reply; 6+ messages in thread
From: Gerd Hoffmann @ 2012-07-09 10:39 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: pbonzini, qemu-devel
On 07/09/12 12:33, Gerd Hoffmann wrote:
> Hi,
>
> v2 of the usb attached scsi emulation patches. Patch #1 is almost
> unmodified compared to v1. Patch #2 is new and makes UAS emulation
> use the new free_request callback (patch just posted by paolo) and
> obviously depends on that patch to compile.
Pushed with paolos patch and all pending usb bugfixes to
"git://git.kraxel.org/qemu usb.56" for those who want to play with it.
cheers,
Gerd
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [Qemu-devel] [PATCH 0/2] usb/scsi: usb attached scsi emulation
2012-07-09 10:39 ` [Qemu-devel] [PATCH 0/2] usb/scsi: usb attached scsi emulation Gerd Hoffmann
@ 2012-07-09 10:45 ` Paolo Bonzini
2012-07-09 12:22 ` Gerd Hoffmann
0 siblings, 1 reply; 6+ messages in thread
From: Paolo Bonzini @ 2012-07-09 10:45 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: qemu-devel
Il 09/07/2012 12:39, Gerd Hoffmann ha scritto:
> On 07/09/12 12:33, Gerd Hoffmann wrote:
>> Hi,
>>
>> v2 of the usb attached scsi emulation patches. Patch #1 is almost
>> unmodified compared to v1. Patch #2 is new and makes UAS emulation
>> use the new free_request callback (patch just posted by paolo) and
>> obviously depends on that patch to compile.
>
> Pushed with paolos patch and all pending usb bugfixes to
> "git://git.kraxel.org/qemu usb.56" for those who want to play with it.
Feel free to include my patch in a pull request, of course.
Paolo
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [Qemu-devel] [PATCH 0/2] usb/scsi: usb attached scsi emulation
2012-07-09 10:45 ` Paolo Bonzini
@ 2012-07-09 12:22 ` Gerd Hoffmann
0 siblings, 0 replies; 6+ messages in thread
From: Gerd Hoffmann @ 2012-07-09 12:22 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel
On 07/09/12 12:45, Paolo Bonzini wrote:
> Il 09/07/2012 12:39, Gerd Hoffmann ha scritto:
>> On 07/09/12 12:33, Gerd Hoffmann wrote:
>>> Hi,
>>>
>>> v2 of the usb attached scsi emulation patches. Patch #1 is almost
>>> unmodified compared to v1. Patch #2 is new and makes UAS emulation
>>> use the new free_request callback (patch just posted by paolo) and
>>> obviously depends on that patch to compile.
>>
>> Pushed with paolos patch and all pending usb bugfixes to
>> "git://git.kraxel.org/qemu usb.56" for those who want to play with it.
>
> Feel free to include my patch in a pull request, of course.
Ok, will do when I send out the uas pull request, just waiting a bit so
people have some time to review, also anthony is on vacation anyway atm.
cheers,
Gerd
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2012-07-09 12:22 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-07-09 10:33 [Qemu-devel] [PATCH 0/2] usb/scsi: usb attached scsi emulation Gerd Hoffmann
2012-07-09 10:33 ` [Qemu-devel] [PATCH 1/2] usb: add " Gerd Hoffmann
2012-07-09 10:33 ` [Qemu-devel] [PATCH 2/2] uas: use scsi req refcounting + free_request callback Gerd Hoffmann
2012-07-09 10:39 ` [Qemu-devel] [PATCH 0/2] usb/scsi: usb attached scsi emulation Gerd Hoffmann
2012-07-09 10:45 ` Paolo Bonzini
2012-07-09 12:22 ` Gerd Hoffmann
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).