All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PULL 0/6] usb patch queue
@ 2012-07-12 13:08 Gerd Hoffmann
  2012-07-12 13:08 ` [Qemu-devel] [PATCH 1/6] scsi: add free_request callback Gerd Hoffmann
                   ` (5 more replies)
  0 siblings, 6 replies; 10+ messages in thread
From: Gerd Hoffmann @ 2012-07-12 13:08 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

  Hi,

Brings UAS (usb attached scsi) support, fixes and improvements for the
ehci interrupt handling and a little uhci loadvm compatibility bugfix.

please pull,
  Gerd

The following changes since commit 92336855975805d88c7979f53bc05c2d47abab04:

  megasas: disable due to build breakage (2012-07-09 18:16:16 -0500)

are available in the git repository at:
  git://git.kraxel.org/qemu usb.57

Gerd Hoffmann (5):
      usb: add usb attached scsi emulation
      uhci: initialize expire_time when loading v1 vmstate
      ehci: raise irq in the frame timer
      ehci: implement Interrupt Threshold Control support
      ehci: improve expire time calculation

Paolo Bonzini (1):
      scsi: add free_request callback

 docs/usb-storage.txt |   38 +++
 hw/scsi-bus.c        |    5 +
 hw/scsi.h            |    1 +
 hw/usb/Makefile.objs |    1 +
 hw/usb/dev-uas.c     |  779 ++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/usb/hcd-ehci.c    |   91 ++++---
 hw/usb/hcd-uhci.c    |   12 +
 trace-events         |   16 +-
 8 files changed, 909 insertions(+), 34 deletions(-)
 create mode 100644 docs/usb-storage.txt
 create mode 100644 hw/usb/dev-uas.c

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH 1/6] scsi: add free_request callback
  2012-07-12 13:08 [Qemu-devel] [PULL 0/6] usb patch queue Gerd Hoffmann
@ 2012-07-12 13:08 ` Gerd Hoffmann
  2012-07-12 13:08 ` [Qemu-devel] [PATCH 2/6] usb: add usb attached scsi emulation Gerd Hoffmann
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 10+ messages in thread
From: Gerd Hoffmann @ 2012-07-12 13:08 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini, Gerd Hoffmann

From: Paolo Bonzini <pbonzini@redhat.com>

Most device models have a simple lifecycle for the hba_private field
and they can free it when a request is completed or cancelled.
However, in some cases it may be simpler to tie the lifetime
of hba_private to that of the included SCSIRequest.  This patch
adds a free_request callback to SCSIBusInfo that lets an HBA
device model do exactly that.

Normally, device models use req->hba_private == NULL to flag requests
that have been completed already.  Device models that use free_request
will still need to track this using a flag.  This is the reason why
"converting" existing HBAs to use free_request adds complexity and
makes little sense.  It is simply an additional convenience that is
provided by the SCSI layer.  USB-attached storage will be the first
user.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 hw/scsi-bus.c |    5 +++++
 hw/scsi.h     |    1 +
 2 files changed, 6 insertions(+), 0 deletions(-)

diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index 5ad1013..dc74063 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -1354,6 +1354,7 @@ static const char *scsi_command_name(uint8_t cmd)
 
 SCSIRequest *scsi_req_ref(SCSIRequest *req)
 {
+    assert(req->refcount > 0);
     req->refcount++;
     return req;
 }
@@ -1362,6 +1363,10 @@ void scsi_req_unref(SCSIRequest *req)
 {
     assert(req->refcount > 0);
     if (--req->refcount == 0) {
+        SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, req->dev->qdev.parent_bus);
+        if (bus->info->free_request && req->hba_private) {
+            bus->info->free_request(bus, req->hba_private);
+        }
         if (req->ops->free_req) {
             req->ops->free_req(req);
         }
diff --git a/hw/scsi.h b/hw/scsi.h
index 76f06d4..367a346 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -134,6 +134,7 @@ struct SCSIBusInfo {
 
     void (*save_request)(QEMUFile *f, SCSIRequest *req);
     void *(*load_request)(QEMUFile *f, SCSIRequest *req);
+    void (*free_request)(SCSIBus *bus, void *priv);
 };
 
 #define TYPE_SCSI_BUS "SCSI"
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH 2/6] usb: add usb attached scsi emulation
  2012-07-12 13:08 [Qemu-devel] [PULL 0/6] usb patch queue Gerd Hoffmann
  2012-07-12 13:08 ` [Qemu-devel] [PATCH 1/6] scsi: add free_request callback Gerd Hoffmann
@ 2012-07-12 13:08 ` Gerd Hoffmann
  2012-07-15  6:55   ` Deep Debroy
  2012-07-12 13:08 ` [Qemu-devel] [PATCH 3/6] uhci: initialize expire_time when loading v1 vmstate Gerd Hoffmann
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 10+ messages in thread
From: Gerd Hoffmann @ 2012-07-12 13:08 UTC (permalink / raw)
  To: qemu-devel; +Cc: 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     |  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

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..9b02ff4
--- /dev/null
+++ b/hw/usb/dev-uas.c
@@ -0,0 +1,779 @@
+/*
+ * 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;
+    bool         complete;
+    uint32_t     buf_off;
+    uint32_t     buf_size;
+    uint32_t     data_off;
+    uint32_t     data_size;
+    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 || req->complete) {
+            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);
+    return req;
+}
+
+static void usb_uas_scsi_free_request(SCSIBus *bus, void *priv)
+{
+    UASRequest *req = priv;
+    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_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);
+    req->complete = true;
+    if (req->data) {
+        usb_uas_complete_data_packet(req);
+    }
+    usb_uas_queue_sense(req, status);
+    scsi_req_unref(req->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? */
+    scsi_req_unref(req->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,
+    .free_request = usb_uas_scsi_free_request,
+};
+
+/* --------------------------------------------------------------------- */
+
+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;
+        }
+        scsi_req_ref(req->req);
+        req->data = p;
+        usb_uas_copy_data(req);
+        if (p->result == p->iov.size || req->complete) {
+            req->data = NULL;
+            ret = p->result;
+        } else {
+            req->data_async = true;
+            ret = USB_RET_ASYNC;
+        }
+        scsi_req_unref(req->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 1f9fc98..04b3d93 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] 10+ messages in thread

* [Qemu-devel] [PATCH 3/6] uhci: initialize expire_time when loading v1 vmstate
  2012-07-12 13:08 [Qemu-devel] [PULL 0/6] usb patch queue Gerd Hoffmann
  2012-07-12 13:08 ` [Qemu-devel] [PATCH 1/6] scsi: add free_request callback Gerd Hoffmann
  2012-07-12 13:08 ` [Qemu-devel] [PATCH 2/6] usb: add usb attached scsi emulation Gerd Hoffmann
@ 2012-07-12 13:08 ` Gerd Hoffmann
  2012-07-12 13:15   ` Michael Tokarev
  2012-07-12 13:08 ` [Qemu-devel] [PATCH 4/6] ehci: raise irq in the frame timer Gerd Hoffmann
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 10+ messages in thread
From: Gerd Hoffmann @ 2012-07-12 13:08 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

$subject says all: when loading old (v1) vmstate which doesn't contain
expire_time initialize it with a reasonable default (current time).

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 hw/usb/hcd-uhci.c |   12 ++++++++++++
 1 files changed, 12 insertions(+), 0 deletions(-)

diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c
index 8f652d2..2aac8a2 100644
--- a/hw/usb/hcd-uhci.c
+++ b/hw/usb/hcd-uhci.c
@@ -388,11 +388,23 @@ static const VMStateDescription vmstate_uhci_port = {
     }
 };
 
+static int uhci_post_load(void *opaque, int version_id)
+{
+    UHCIState *s = opaque;
+
+    if (version_id < 2) {
+        s->expire_time = qemu_get_clock_ns(vm_clock) +
+            (get_ticks_per_sec() / FRAME_TIMER_FREQ);
+    }
+    return 0;
+}
+
 static const VMStateDescription vmstate_uhci = {
     .name = "uhci",
     .version_id = 2,
     .minimum_version_id = 1,
     .minimum_version_id_old = 1,
+    .post_load = uhci_post_load,
     .fields      = (VMStateField []) {
         VMSTATE_PCI_DEVICE(dev, UHCIState),
         VMSTATE_UINT8_EQUAL(num_ports_vmstate, UHCIState),
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH 4/6] ehci: raise irq in the frame timer
  2012-07-12 13:08 [Qemu-devel] [PULL 0/6] usb patch queue Gerd Hoffmann
                   ` (2 preceding siblings ...)
  2012-07-12 13:08 ` [Qemu-devel] [PATCH 3/6] uhci: initialize expire_time when loading v1 vmstate Gerd Hoffmann
@ 2012-07-12 13:08 ` Gerd Hoffmann
  2012-07-12 13:08 ` [Qemu-devel] [PATCH 5/6] ehci: implement Interrupt Threshold Control support Gerd Hoffmann
  2012-07-12 13:08 ` [Qemu-devel] [PATCH 6/6] ehci: improve expire time calculation Gerd Hoffmann
  5 siblings, 0 replies; 10+ messages in thread
From: Gerd Hoffmann @ 2012-07-12 13:08 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

With the async schedule being kicked from other places than the frame
timer (commit 0f588df8b3688b00e77aabaa32e26ece5f19bd39) it may happen
that we call ehci_commit_interrupt() more than once per frame.

Move the call from the async schedule handler to the frame timer to
restore old irq behavior, which is more correct.  Fixes regressions
with some linux kernel versions.

TODO: implement full Interrupt Threshold Control support.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 hw/usb/hcd-ehci.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
index 080f62c..7c5f398 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -2208,8 +2208,6 @@ static void ehci_advance_state(EHCIState *ehci, int async)
         }
     }
     while (again);
-
-    ehci_commit_interrupt(ehci);
 }
 
 static void ehci_advance_async_state(EHCIState *ehci)
@@ -2389,6 +2387,8 @@ static void ehci_frame_timer(void *opaque)
     if (schedules) {
         qemu_mod_timer(ehci->frame_timer, expire_time);
     }
+
+    ehci_commit_interrupt(ehci);
 }
 
 static void ehci_async_bh(void *opaque)
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH 5/6] ehci: implement Interrupt Threshold Control support
  2012-07-12 13:08 [Qemu-devel] [PULL 0/6] usb patch queue Gerd Hoffmann
                   ` (3 preceding siblings ...)
  2012-07-12 13:08 ` [Qemu-devel] [PATCH 4/6] ehci: raise irq in the frame timer Gerd Hoffmann
@ 2012-07-12 13:08 ` Gerd Hoffmann
  2012-07-12 13:08 ` [Qemu-devel] [PATCH 6/6] ehci: improve expire time calculation Gerd Hoffmann
  5 siblings, 0 replies; 10+ messages in thread
From: Gerd Hoffmann @ 2012-07-12 13:08 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Also reorganize and comment the irq functions while being at it.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 hw/usb/hcd-ehci.c |   83 ++++++++++++++++++++++++++++++++++------------------
 trace-events      |    2 +-
 2 files changed, 55 insertions(+), 30 deletions(-)

diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
index 7c5f398..6040e86 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -420,6 +420,7 @@ struct EHCIState {
     USBPort ports[NB_PORTS];
     USBPort *companion_ports[NB_PORTS];
     uint32_t usbsts_pending;
+    uint32_t usbsts_frindex;
     EHCIQueueHead aqueues;
     EHCIQueueHead pqueues;
 
@@ -558,34 +559,45 @@ static inline void ehci_clear_usbsts(EHCIState *s, int mask)
     s->usbsts &= ~mask;
 }
 
-static inline void ehci_set_interrupt(EHCIState *s, int intr)
+/* update irq line */
+static inline void ehci_update_irq(EHCIState *s)
 {
     int level = 0;
 
-    // TODO honour interrupt threshold requests
-
-    ehci_set_usbsts(s, intr);
-
     if ((s->usbsts & USBINTR_MASK) & s->usbintr) {
         level = 1;
     }
 
-    trace_usb_ehci_interrupt(level, s->usbsts, s->usbintr);
+    trace_usb_ehci_irq(level, s->frindex, s->usbsts, s->usbintr);
     qemu_set_irq(s->irq, level);
 }
 
-static inline void ehci_record_interrupt(EHCIState *s, int intr)
+/* flag interrupt condition */
+static inline void ehci_raise_irq(EHCIState *s, int intr)
 {
     s->usbsts_pending |= intr;
 }
 
-static inline void ehci_commit_interrupt(EHCIState *s)
+/*
+ * Commit pending interrupts (added via ehci_raise_irq),
+ * at the rate allowed by "Interrupt Threshold Control".
+ */
+static inline void ehci_commit_irq(EHCIState *s)
 {
+    uint32_t itc;
+
     if (!s->usbsts_pending) {
         return;
     }
-    ehci_set_interrupt(s, s->usbsts_pending);
+    if (s->usbsts_frindex > s->frindex) {
+        return;
+    }
+
+    itc = (s->usbcmd >> 16) & 0xff;
+    s->usbsts |= s->usbsts_pending;
     s->usbsts_pending = 0;
+    s->usbsts_frindex = s->frindex + itc;
+    ehci_update_irq(s);
 }
 
 static void ehci_update_halt(EHCIState *s)
@@ -849,7 +861,8 @@ static void ehci_attach(USBPort *port)
     *portsc |= PORTSC_CONNECT;
     *portsc |= PORTSC_CSC;
 
-    ehci_set_interrupt(s, USBSTS_PCD);
+    ehci_raise_irq(s, USBSTS_PCD);
+    ehci_commit_irq(s);
 }
 
 static void ehci_detach(USBPort *port)
@@ -878,7 +891,8 @@ static void ehci_detach(USBPort *port)
     *portsc &= ~(PORTSC_CONNECT|PORTSC_PED);
     *portsc |= PORTSC_CSC;
 
-    ehci_set_interrupt(s, USBSTS_PCD);
+    ehci_raise_irq(s, USBSTS_PCD);
+    ehci_commit_irq(s);
 }
 
 static void ehci_child_detach(USBPort *port, USBDevice *child)
@@ -997,6 +1011,8 @@ static void ehci_reset(void *opaque)
 
     s->usbcmd = NB_MAXINTRATE << USBCMD_ITC_SH;
     s->usbsts = USBSTS_HALT;
+    s->usbsts_pending = 0;
+    s->usbsts_frindex = 0;
 
     s->astate = EST_INACTIVE;
     s->pstate = EST_INACTIVE;
@@ -1188,7 +1204,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
         val &= USBSTS_RO_MASK;              // bits 6 through 31 are RO
         ehci_clear_usbsts(s, val);          // bits 0 through 5 are R/WC
         val = s->usbsts;
-        ehci_set_interrupt(s, 0);
+        ehci_update_irq(s);
         break;
 
     case USBINTR:
@@ -1419,18 +1435,18 @@ static void ehci_execute_complete(EHCIQueue *q)
         case USB_RET_NODEV:
             q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_XACTERR);
             set_field(&q->qh.token, 0, QTD_TOKEN_CERR);
-            ehci_record_interrupt(q->ehci, USBSTS_ERRINT);
+            ehci_raise_irq(q->ehci, USBSTS_ERRINT);
             break;
         case USB_RET_STALL:
             q->qh.token |= QTD_TOKEN_HALT;
-            ehci_record_interrupt(q->ehci, USBSTS_ERRINT);
+            ehci_raise_irq(q->ehci, USBSTS_ERRINT);
             break;
         case USB_RET_NAK:
             set_field(&q->qh.altnext_qtd, 0, QH_ALTNEXT_NAKCNT);
             return; /* We're not done yet with this transaction */
         case USB_RET_BABBLE:
             q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_BABBLE);
-            ehci_record_interrupt(q->ehci, USBSTS_ERRINT);
+            ehci_raise_irq(q->ehci, USBSTS_ERRINT);
             break;
         default:
             /* should not be triggerable */
@@ -1441,7 +1457,7 @@ static void ehci_execute_complete(EHCIQueue *q)
     } else if ((p->usb_status > p->tbytes) && (p->pid == USB_TOKEN_IN)) {
         p->usb_status = USB_RET_BABBLE;
         q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_BABBLE);
-        ehci_record_interrupt(q->ehci, USBSTS_ERRINT);
+        ehci_raise_irq(q->ehci, USBSTS_ERRINT);
     } else {
         // TODO check 4.12 for splits
 
@@ -1462,7 +1478,7 @@ static void ehci_execute_complete(EHCIQueue *q)
     q->qh.token &= ~QTD_TOKEN_ACTIVE;
 
     if (q->qh.token & QTD_TOKEN_IOC) {
-        ehci_record_interrupt(q->ehci, USBSTS_INT);
+        ehci_raise_irq(q->ehci, USBSTS_INT);
     }
 }
 
@@ -1597,12 +1613,12 @@ static int ehci_process_itd(EHCIState *ehci,
                     /* 3.3.2: XACTERR is only allowed on IN transactions */
                     if (dir) {
                         itd->transact[i] |= ITD_XACT_XACTERR;
-                        ehci_record_interrupt(ehci, USBSTS_ERRINT);
+                        ehci_raise_irq(ehci, USBSTS_ERRINT);
                     }
                     break;
                 case USB_RET_BABBLE:
                     itd->transact[i] |= ITD_XACT_BABBLE;
-                    ehci_record_interrupt(ehci, USBSTS_ERRINT);
+                    ehci_raise_irq(ehci, USBSTS_ERRINT);
                     break;
                 case USB_RET_NAK:
                     /* no data for us, so do a zero-length transfer */
@@ -1620,7 +1636,7 @@ static int ehci_process_itd(EHCIState *ehci,
                 }
             }
             if (itd->transact[i] & ITD_XACT_IOC) {
-                ehci_record_interrupt(ehci, USBSTS_INT);
+                ehci_raise_irq(ehci, USBSTS_INT);
             }
             itd->transact[i] &= ~ITD_XACT_ACTIVE;
         }
@@ -2253,7 +2269,7 @@ static void ehci_advance_async_state(EHCIState *ehci)
             ehci_queues_tag_unused_async(ehci);
             DPRINTF("ASYNC: doorbell request acknowledged\n");
             ehci->usbcmd &= ~USBCMD_IAAD;
-            ehci_set_interrupt(ehci, USBSTS_IAA);
+            ehci_raise_irq(ehci, USBSTS_IAA);
         }
         break;
 
@@ -2326,12 +2342,17 @@ static void ehci_update_frindex(EHCIState *ehci, int frames)
         ehci->frindex += 8;
 
         if (ehci->frindex == 0x00002000) {
-            ehci_set_interrupt(ehci, USBSTS_FLR);
+            ehci_raise_irq(ehci, USBSTS_FLR);
         }
 
         if (ehci->frindex == 0x00004000) {
-            ehci_set_interrupt(ehci, USBSTS_FLR);
+            ehci_raise_irq(ehci, USBSTS_FLR);
             ehci->frindex = 0;
+            if (ehci->usbsts_frindex > 0x00004000) {
+                ehci->usbsts_frindex -= 0x00004000;
+            } else {
+                ehci->usbsts_frindex = 0;
+            }
         }
     }
 }
@@ -2339,7 +2360,7 @@ static void ehci_update_frindex(EHCIState *ehci, int frames)
 static void ehci_frame_timer(void *opaque)
 {
     EHCIState *ehci = opaque;
-    int schedules = 0;
+    int need_timer = 0;
     int64_t expire_time, t_now;
     uint64_t ns_elapsed;
     int frames, skipped_frames;
@@ -2350,7 +2371,7 @@ static void ehci_frame_timer(void *opaque)
     frames = ns_elapsed / FRAME_TIMER_NS;
 
     if (ehci_periodic_enabled(ehci) || ehci->pstate != EST_INACTIVE) {
-        schedules++;
+        need_timer++;
         expire_time = t_now + (get_ticks_per_sec() / FRAME_TIMER_FREQ);
 
         if (frames > ehci->maxframes) {
@@ -2380,15 +2401,19 @@ static void ehci_frame_timer(void *opaque)
      *  called
      */
     if (ehci_async_enabled(ehci) || ehci->astate != EST_INACTIVE) {
-        schedules++;
+        need_timer++;
         qemu_bh_schedule(ehci->async_bh);
     }
 
-    if (schedules) {
-        qemu_mod_timer(ehci->frame_timer, expire_time);
+    ehci_commit_irq(ehci);
+    if (ehci->usbsts_pending) {
+        need_timer++;
+        ehci->async_stepdown = 0;
     }
 
-    ehci_commit_interrupt(ehci);
+    if (need_timer) {
+        qemu_mod_timer(ehci->frame_timer, expire_time);
+    }
 }
 
 static void ehci_async_bh(void *opaque)
diff --git a/trace-events b/trace-events
index 04b3d93..e45c7e5 100644
--- a/trace-events
+++ b/trace-events
@@ -258,7 +258,7 @@ usb_ehci_port_reset(uint32_t port, int enable) "reset port #%d - %d"
 usb_ehci_data(int rw, uint32_t cpage, uint32_t offset, uint32_t addr, uint32_t len, uint32_t bufpos) "write %d, cpage %d, offset 0x%03x, addr 0x%08x, len %d, bufpos %d"
 usb_ehci_queue_action(void *q, const char *action) "q %p: %s"
 usb_ehci_packet_action(void *q, void *p, const char *action) "q %p p %p: %s"
-usb_ehci_interrupt(uint32_t level, uint32_t sts, uint32_t mask) "level %d, sts 0x%x, mask 0x%x"
+usb_ehci_irq(uint32_t level, uint32_t frindex, uint32_t sts, uint32_t mask) "level %d, frindex 0x%04x, sts 0x%x, mask 0x%x"
 
 # hw/usb/hcd-uhci.c
 usb_uhci_reset(void) "=== RESET ==="
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH 6/6] ehci: improve expire time calculation
  2012-07-12 13:08 [Qemu-devel] [PULL 0/6] usb patch queue Gerd Hoffmann
                   ` (4 preceding siblings ...)
  2012-07-12 13:08 ` [Qemu-devel] [PATCH 5/6] ehci: implement Interrupt Threshold Control support Gerd Hoffmann
@ 2012-07-12 13:08 ` Gerd Hoffmann
  5 siblings, 0 replies; 10+ messages in thread
From: Gerd Hoffmann @ 2012-07-12 13:08 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Move down the expire time calculation down in the frame timer, to the
point where the timer is actually reloaded.  This way we'll notice any
async_stepdown changes (especially resetting to 0 due to usb activity).

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 hw/usb/hcd-ehci.c |    8 ++++----
 1 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
index 6040e86..b043e7c 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -2372,7 +2372,7 @@ static void ehci_frame_timer(void *opaque)
 
     if (ehci_periodic_enabled(ehci) || ehci->pstate != EST_INACTIVE) {
         need_timer++;
-        expire_time = t_now + (get_ticks_per_sec() / FRAME_TIMER_FREQ);
+        ehci->async_stepdown = 0;
 
         if (frames > ehci->maxframes) {
             skipped_frames = frames - ehci->maxframes;
@@ -2391,8 +2391,6 @@ static void ehci_frame_timer(void *opaque)
         if (ehci->async_stepdown < ehci->maxframes / 2) {
             ehci->async_stepdown++;
         }
-        expire_time = t_now + (get_ticks_per_sec()
-                               * ehci->async_stepdown / FRAME_TIMER_FREQ);
         ehci_update_frindex(ehci, frames);
         ehci->last_run_ns += FRAME_TIMER_NS * frames;
     }
@@ -2402,7 +2400,7 @@ static void ehci_frame_timer(void *opaque)
      */
     if (ehci_async_enabled(ehci) || ehci->astate != EST_INACTIVE) {
         need_timer++;
-        qemu_bh_schedule(ehci->async_bh);
+        ehci_advance_async_state(ehci);
     }
 
     ehci_commit_irq(ehci);
@@ -2412,6 +2410,8 @@ static void ehci_frame_timer(void *opaque)
     }
 
     if (need_timer) {
+        expire_time = t_now + (get_ticks_per_sec()
+                               * (ehci->async_stepdown+1) / FRAME_TIMER_FREQ);
         qemu_mod_timer(ehci->frame_timer, expire_time);
     }
 }
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH 3/6] uhci: initialize expire_time when loading v1 vmstate
  2012-07-12 13:08 ` [Qemu-devel] [PATCH 3/6] uhci: initialize expire_time when loading v1 vmstate Gerd Hoffmann
@ 2012-07-12 13:15   ` Michael Tokarev
  0 siblings, 0 replies; 10+ messages in thread
From: Michael Tokarev @ 2012-07-12 13:15 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

On 12.07.2012 17:08, Gerd Hoffmann wrote:
> $subject says all: when loading old (v1) vmstate which doesn't contain
> expire_time initialize it with a reasonable default (current time).

-stable (1.1) material?

Thanks,

/mjt

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH 2/6] usb: add usb attached scsi emulation
  2012-07-12 13:08 ` [Qemu-devel] [PATCH 2/6] usb: add usb attached scsi emulation Gerd Hoffmann
@ 2012-07-15  6:55   ` Deep Debroy
  2012-07-16 11:22     ` Stefan Hajnoczi
  0 siblings, 1 reply; 10+ messages in thread
From: Deep Debroy @ 2012-07-15  6:55 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

On Thu, Jul 12, 2012 at 6:08 AM, Gerd Hoffmann <kraxel@redhat.com> wrote:
> $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     |  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
>
> 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..9b02ff4
> --- /dev/null
> +++ b/hw/usb/dev-uas.c
> @@ -0,0 +1,779 @@
> +/*
> + * 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;
> +    bool         complete;
> +    uint32_t     buf_off;
> +    uint32_t     buf_size;
> +    uint32_t     data_off;
> +    uint32_t     data_size;
> +    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 || req->complete) {
> +            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);
> +    return req;
> +}
> +
> +static void usb_uas_scsi_free_request(SCSIBus *bus, void *priv)
> +{
> +    UASRequest *req = priv;
> +    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_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);
> +    req->complete = true;
> +    if (req->data) {
> +        usb_uas_complete_data_packet(req);
> +    }
> +    usb_uas_queue_sense(req, status);
> +    scsi_req_unref(req->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? */
> +    scsi_req_unref(req->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,
> +    .free_request = usb_uas_scsi_free_request,
> +};
> +
> +/* --------------------------------------------------------------------- */
> +
> +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;
> +        }
> +        scsi_req_ref(req->req);
> +        req->data = p;
> +        usb_uas_copy_data(req);
> +        if (p->result == p->iov.size || req->complete) {
> +            req->data = NULL;
> +            ret = p->result;
> +        } else {
> +            req->data_async = true;
> +            ret = USB_RET_ASYNC;
> +        }
> +        scsi_req_unref(req->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 1f9fc98..04b3d93 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"

The above tracepoint seems to require too many parameters for the
simple tracing backend. Trace routines appear to go up to trace6 in
trace/simple.c but no trace7. So please add the trace7 or reduce the
number of parameters above.

Currently with the above situation, trying to build latest scsi-next
with ./configure --enable-trace-backend=simple is resulting in a build
break around trace_usb_uas_xfer_data.

> +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	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH 2/6] usb: add usb attached scsi emulation
  2012-07-15  6:55   ` Deep Debroy
@ 2012-07-16 11:22     ` Stefan Hajnoczi
  0 siblings, 0 replies; 10+ messages in thread
From: Stefan Hajnoczi @ 2012-07-16 11:22 UTC (permalink / raw)
  To: Deep Debroy; +Cc: Gerd Hoffmann, qemu-devel

On Sun, Jul 15, 2012 at 7:55 AM, Deep Debroy <ddebroy@gmail.com> wrote:
> On Thu, Jul 12, 2012 at 6:08 AM, Gerd Hoffmann <kraxel@redhat.com> wrote:
>> $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     |  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
>>
>> 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..9b02ff4
>> --- /dev/null
>> +++ b/hw/usb/dev-uas.c
>> @@ -0,0 +1,779 @@
>> +/*
>> + * 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;
>> +    bool         complete;
>> +    uint32_t     buf_off;
>> +    uint32_t     buf_size;
>> +    uint32_t     data_off;
>> +    uint32_t     data_size;
>> +    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 || req->complete) {
>> +            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);
>> +    return req;
>> +}
>> +
>> +static void usb_uas_scsi_free_request(SCSIBus *bus, void *priv)
>> +{
>> +    UASRequest *req = priv;
>> +    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_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);
>> +    req->complete = true;
>> +    if (req->data) {
>> +        usb_uas_complete_data_packet(req);
>> +    }
>> +    usb_uas_queue_sense(req, status);
>> +    scsi_req_unref(req->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? */
>> +    scsi_req_unref(req->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,
>> +    .free_request = usb_uas_scsi_free_request,
>> +};
>> +
>> +/* --------------------------------------------------------------------- */
>> +
>> +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;
>> +        }
>> +        scsi_req_ref(req->req);
>> +        req->data = p;
>> +        usb_uas_copy_data(req);
>> +        if (p->result == p->iov.size || req->complete) {
>> +            req->data = NULL;
>> +            ret = p->result;
>> +        } else {
>> +            req->data_async = true;
>> +            ret = USB_RET_ASYNC;
>> +        }
>> +        scsi_req_unref(req->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 1f9fc98..04b3d93 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"
>
> The above tracepoint seems to require too many parameters for the
> simple tracing backend. Trace routines appear to go up to trace6 in
> trace/simple.c but no trace7. So please add the trace7 or reduce the
> number of parameters above.
>
> Currently with the above situation, trying to build latest scsi-next
> with ./configure --enable-trace-backend=simple is resulting in a build
> break around trace_usb_uas_xfer_data.

Thanks for pointing this out.  The limitation will be lifted soon,
Harsh Prateek Bora has submitted a simpletrace v2 patch series which
moves to a variable-length record format.  It can support >6 arguments
but has not been applied yet.  I am going to review and hopefully
merge it into the tracing tree this week.

It's still a good idea to shorten the trace event definition in the meantime.

Stefan

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2012-07-16 11:23 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-07-12 13:08 [Qemu-devel] [PULL 0/6] usb patch queue Gerd Hoffmann
2012-07-12 13:08 ` [Qemu-devel] [PATCH 1/6] scsi: add free_request callback Gerd Hoffmann
2012-07-12 13:08 ` [Qemu-devel] [PATCH 2/6] usb: add usb attached scsi emulation Gerd Hoffmann
2012-07-15  6:55   ` Deep Debroy
2012-07-16 11:22     ` Stefan Hajnoczi
2012-07-12 13:08 ` [Qemu-devel] [PATCH 3/6] uhci: initialize expire_time when loading v1 vmstate Gerd Hoffmann
2012-07-12 13:15   ` Michael Tokarev
2012-07-12 13:08 ` [Qemu-devel] [PATCH 4/6] ehci: raise irq in the frame timer Gerd Hoffmann
2012-07-12 13:08 ` [Qemu-devel] [PATCH 5/6] ehci: implement Interrupt Threshold Control support Gerd Hoffmann
2012-07-12 13:08 ` [Qemu-devel] [PATCH 6/6] ehci: improve expire time calculation Gerd Hoffmann

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.