xref: /openbmc/qemu/hw/usb/dev-uas.c (revision 125ee0ed)
10f58f68bSGerd Hoffmann /*
20f58f68bSGerd Hoffmann  * UAS (USB Attached SCSI) emulation
30f58f68bSGerd Hoffmann  *
40f58f68bSGerd Hoffmann  * Copyright Red Hat, Inc. 2012
50f58f68bSGerd Hoffmann  *
60f58f68bSGerd Hoffmann  * Author: Gerd Hoffmann <kraxel@redhat.com>
70f58f68bSGerd Hoffmann  *
80f58f68bSGerd Hoffmann  * This work is licensed under the terms of the GNU GPL, version 2 or later.
90f58f68bSGerd Hoffmann  * See the COPYING file in the top-level directory.
100f58f68bSGerd Hoffmann  */
110f58f68bSGerd Hoffmann 
120f58f68bSGerd Hoffmann #include "qemu-common.h"
131de7afc9SPaolo Bonzini #include "qemu/option.h"
141de7afc9SPaolo Bonzini #include "qemu/config-file.h"
150f58f68bSGerd Hoffmann #include "trace.h"
160f58f68bSGerd Hoffmann 
170f58f68bSGerd Hoffmann #include "hw/usb.h"
180f58f68bSGerd Hoffmann #include "hw/usb/desc.h"
190d09e41aSPaolo Bonzini #include "hw/scsi/scsi.h"
200d09e41aSPaolo Bonzini #include "block/scsi.h"
210f58f68bSGerd Hoffmann 
220f58f68bSGerd Hoffmann /* --------------------------------------------------------------------- */
230f58f68bSGerd Hoffmann 
240f58f68bSGerd Hoffmann #define UAS_UI_COMMAND              0x01
250f58f68bSGerd Hoffmann #define UAS_UI_SENSE                0x03
260f58f68bSGerd Hoffmann #define UAS_UI_RESPONSE             0x04
270f58f68bSGerd Hoffmann #define UAS_UI_TASK_MGMT            0x05
280f58f68bSGerd Hoffmann #define UAS_UI_READ_READY           0x06
290f58f68bSGerd Hoffmann #define UAS_UI_WRITE_READY          0x07
300f58f68bSGerd Hoffmann 
310f58f68bSGerd Hoffmann #define UAS_RC_TMF_COMPLETE         0x00
320f58f68bSGerd Hoffmann #define UAS_RC_INVALID_INFO_UNIT    0x02
330f58f68bSGerd Hoffmann #define UAS_RC_TMF_NOT_SUPPORTED    0x04
340f58f68bSGerd Hoffmann #define UAS_RC_TMF_FAILED           0x05
350f58f68bSGerd Hoffmann #define UAS_RC_TMF_SUCCEEDED        0x08
360f58f68bSGerd Hoffmann #define UAS_RC_INCORRECT_LUN        0x09
370f58f68bSGerd Hoffmann #define UAS_RC_OVERLAPPED_TAG       0x0a
380f58f68bSGerd Hoffmann 
390f58f68bSGerd Hoffmann #define UAS_TMF_ABORT_TASK          0x01
400f58f68bSGerd Hoffmann #define UAS_TMF_ABORT_TASK_SET      0x02
410f58f68bSGerd Hoffmann #define UAS_TMF_CLEAR_TASK_SET      0x04
420f58f68bSGerd Hoffmann #define UAS_TMF_LOGICAL_UNIT_RESET  0x08
430f58f68bSGerd Hoffmann #define UAS_TMF_I_T_NEXUS_RESET     0x10
440f58f68bSGerd Hoffmann #define UAS_TMF_CLEAR_ACA           0x40
450f58f68bSGerd Hoffmann #define UAS_TMF_QUERY_TASK          0x80
460f58f68bSGerd Hoffmann #define UAS_TMF_QUERY_TASK_SET      0x81
470f58f68bSGerd Hoffmann #define UAS_TMF_QUERY_ASYNC_EVENT   0x82
480f58f68bSGerd Hoffmann 
490f58f68bSGerd Hoffmann #define UAS_PIPE_ID_COMMAND         0x01
500f58f68bSGerd Hoffmann #define UAS_PIPE_ID_STATUS          0x02
510f58f68bSGerd Hoffmann #define UAS_PIPE_ID_DATA_IN         0x03
520f58f68bSGerd Hoffmann #define UAS_PIPE_ID_DATA_OUT        0x04
530f58f68bSGerd Hoffmann 
540f58f68bSGerd Hoffmann typedef struct {
550f58f68bSGerd Hoffmann     uint8_t    id;
560f58f68bSGerd Hoffmann     uint8_t    reserved;
570f58f68bSGerd Hoffmann     uint16_t   tag;
580f58f68bSGerd Hoffmann } QEMU_PACKED  uas_ui_header;
590f58f68bSGerd Hoffmann 
600f58f68bSGerd Hoffmann typedef struct {
610f58f68bSGerd Hoffmann     uint8_t    prio_taskattr;   /* 6:3 priority, 2:0 task attribute   */
620f58f68bSGerd Hoffmann     uint8_t    reserved_1;
630f58f68bSGerd Hoffmann     uint8_t    add_cdb_length;  /* 7:2 additional adb length (dwords) */
640f58f68bSGerd Hoffmann     uint8_t    reserved_2;
650f58f68bSGerd Hoffmann     uint64_t   lun;
660f58f68bSGerd Hoffmann     uint8_t    cdb[16];
670f58f68bSGerd Hoffmann     uint8_t    add_cdb[];
680f58f68bSGerd Hoffmann } QEMU_PACKED  uas_ui_command;
690f58f68bSGerd Hoffmann 
700f58f68bSGerd Hoffmann typedef struct {
710f58f68bSGerd Hoffmann     uint16_t   status_qualifier;
720f58f68bSGerd Hoffmann     uint8_t    status;
730f58f68bSGerd Hoffmann     uint8_t    reserved[7];
740f58f68bSGerd Hoffmann     uint16_t   sense_length;
750f58f68bSGerd Hoffmann     uint8_t    sense_data[18];
760f58f68bSGerd Hoffmann } QEMU_PACKED  uas_ui_sense;
770f58f68bSGerd Hoffmann 
780f58f68bSGerd Hoffmann typedef struct {
790f58f68bSGerd Hoffmann     uint16_t   add_response_info;
800f58f68bSGerd Hoffmann     uint8_t    response_code;
810f58f68bSGerd Hoffmann } QEMU_PACKED  uas_ui_response;
820f58f68bSGerd Hoffmann 
830f58f68bSGerd Hoffmann typedef struct {
840f58f68bSGerd Hoffmann     uint8_t    function;
850f58f68bSGerd Hoffmann     uint8_t    reserved;
860f58f68bSGerd Hoffmann     uint16_t   task_tag;
870f58f68bSGerd Hoffmann     uint64_t   lun;
880f58f68bSGerd Hoffmann } QEMU_PACKED  uas_ui_task_mgmt;
890f58f68bSGerd Hoffmann 
900f58f68bSGerd Hoffmann typedef struct {
910f58f68bSGerd Hoffmann     uas_ui_header  hdr;
920f58f68bSGerd Hoffmann     union {
930f58f68bSGerd Hoffmann         uas_ui_command   command;
940f58f68bSGerd Hoffmann         uas_ui_sense     sense;
950f58f68bSGerd Hoffmann         uas_ui_task_mgmt task;
960f58f68bSGerd Hoffmann         uas_ui_response  response;
970f58f68bSGerd Hoffmann     };
980f58f68bSGerd Hoffmann } QEMU_PACKED  uas_ui;
990f58f68bSGerd Hoffmann 
1000f58f68bSGerd Hoffmann /* --------------------------------------------------------------------- */
1010f58f68bSGerd Hoffmann 
10289a453d4SGerd Hoffmann #define UAS_STREAM_BM_ATTR  4
10389a453d4SGerd Hoffmann #define UAS_MAX_STREAMS     (1 << UAS_STREAM_BM_ATTR)
10489a453d4SGerd Hoffmann 
1050f58f68bSGerd Hoffmann typedef struct UASDevice UASDevice;
1060f58f68bSGerd Hoffmann typedef struct UASRequest UASRequest;
1070f58f68bSGerd Hoffmann typedef struct UASStatus UASStatus;
1080f58f68bSGerd Hoffmann 
1090f58f68bSGerd Hoffmann struct UASDevice {
1100f58f68bSGerd Hoffmann     USBDevice                 dev;
1110f58f68bSGerd Hoffmann     SCSIBus                   bus;
1120f58f68bSGerd Hoffmann     QEMUBH                    *status_bh;
1130f58f68bSGerd Hoffmann     QTAILQ_HEAD(, UASStatus)  results;
1140f58f68bSGerd Hoffmann     QTAILQ_HEAD(, UASRequest) requests;
11589a453d4SGerd Hoffmann 
11689a453d4SGerd Hoffmann     /* usb 2.0 only */
11789a453d4SGerd Hoffmann     USBPacket                 *status2;
11889a453d4SGerd Hoffmann     UASRequest                *datain2;
11989a453d4SGerd Hoffmann     UASRequest                *dataout2;
12089a453d4SGerd Hoffmann 
12189a453d4SGerd Hoffmann     /* usb 3.0 only */
12289a453d4SGerd Hoffmann     USBPacket                 *data3[UAS_MAX_STREAMS];
12389a453d4SGerd Hoffmann     USBPacket                 *status3[UAS_MAX_STREAMS];
1240f58f68bSGerd Hoffmann };
1250f58f68bSGerd Hoffmann 
1260f58f68bSGerd Hoffmann struct UASRequest {
1270f58f68bSGerd Hoffmann     uint16_t     tag;
1280f58f68bSGerd Hoffmann     uint64_t     lun;
1290f58f68bSGerd Hoffmann     UASDevice    *uas;
1300f58f68bSGerd Hoffmann     SCSIDevice   *dev;
1310f58f68bSGerd Hoffmann     SCSIRequest  *req;
1320f58f68bSGerd Hoffmann     USBPacket    *data;
1330f58f68bSGerd Hoffmann     bool         data_async;
1340f58f68bSGerd Hoffmann     bool         active;
1350f58f68bSGerd Hoffmann     bool         complete;
1360f58f68bSGerd Hoffmann     uint32_t     buf_off;
1370f58f68bSGerd Hoffmann     uint32_t     buf_size;
1380f58f68bSGerd Hoffmann     uint32_t     data_off;
1390f58f68bSGerd Hoffmann     uint32_t     data_size;
1400f58f68bSGerd Hoffmann     QTAILQ_ENTRY(UASRequest)  next;
1410f58f68bSGerd Hoffmann };
1420f58f68bSGerd Hoffmann 
1430f58f68bSGerd Hoffmann struct UASStatus {
14489a453d4SGerd Hoffmann     uint32_t                  stream;
1450f58f68bSGerd Hoffmann     uas_ui                    status;
1460f58f68bSGerd Hoffmann     uint32_t                  length;
1470f58f68bSGerd Hoffmann     QTAILQ_ENTRY(UASStatus)   next;
1480f58f68bSGerd Hoffmann };
1490f58f68bSGerd Hoffmann 
1500f58f68bSGerd Hoffmann /* --------------------------------------------------------------------- */
1510f58f68bSGerd Hoffmann 
1520f58f68bSGerd Hoffmann enum {
1530f58f68bSGerd Hoffmann     STR_MANUFACTURER = 1,
1540f58f68bSGerd Hoffmann     STR_PRODUCT,
1550f58f68bSGerd Hoffmann     STR_SERIALNUMBER,
1560f58f68bSGerd Hoffmann     STR_CONFIG_HIGH,
15789a453d4SGerd Hoffmann     STR_CONFIG_SUPER,
1580f58f68bSGerd Hoffmann };
1590f58f68bSGerd Hoffmann 
1600f58f68bSGerd Hoffmann static const USBDescStrings desc_strings = {
1610f58f68bSGerd Hoffmann     [STR_MANUFACTURER] = "QEMU",
1620f58f68bSGerd Hoffmann     [STR_PRODUCT]      = "USB Attached SCSI HBA",
1630f58f68bSGerd Hoffmann     [STR_SERIALNUMBER] = "27842",
1640f58f68bSGerd Hoffmann     [STR_CONFIG_HIGH]  = "High speed config (usb 2.0)",
16589a453d4SGerd Hoffmann     [STR_CONFIG_SUPER] = "Super speed config (usb 3.0)",
1660f58f68bSGerd Hoffmann };
1670f58f68bSGerd Hoffmann 
1680f58f68bSGerd Hoffmann static const USBDescIface desc_iface_high = {
1690f58f68bSGerd Hoffmann     .bInterfaceNumber              = 0,
1700f58f68bSGerd Hoffmann     .bNumEndpoints                 = 4,
1710f58f68bSGerd Hoffmann     .bInterfaceClass               = USB_CLASS_MASS_STORAGE,
1720f58f68bSGerd Hoffmann     .bInterfaceSubClass            = 0x06, /* SCSI */
1730f58f68bSGerd Hoffmann     .bInterfaceProtocol            = 0x62, /* UAS  */
1740f58f68bSGerd Hoffmann     .eps = (USBDescEndpoint[]) {
1750f58f68bSGerd Hoffmann         {
1760f58f68bSGerd Hoffmann             .bEndpointAddress      = USB_DIR_OUT | UAS_PIPE_ID_COMMAND,
1770f58f68bSGerd Hoffmann             .bmAttributes          = USB_ENDPOINT_XFER_BULK,
1780f58f68bSGerd Hoffmann             .wMaxPacketSize        = 512,
1790f58f68bSGerd Hoffmann             .extra = (uint8_t[]) {
1800f58f68bSGerd Hoffmann                 0x04,  /*  u8  bLength */
1810f58f68bSGerd Hoffmann                 0x24,  /*  u8  bDescriptorType */
1820f58f68bSGerd Hoffmann                 UAS_PIPE_ID_COMMAND,
1830f58f68bSGerd Hoffmann                 0x00,  /*  u8  bReserved */
1840f58f68bSGerd Hoffmann             },
1850f58f68bSGerd Hoffmann         },{
1860f58f68bSGerd Hoffmann             .bEndpointAddress      = USB_DIR_IN | UAS_PIPE_ID_STATUS,
1870f58f68bSGerd Hoffmann             .bmAttributes          = USB_ENDPOINT_XFER_BULK,
1880f58f68bSGerd Hoffmann             .wMaxPacketSize        = 512,
1890f58f68bSGerd Hoffmann             .extra = (uint8_t[]) {
1900f58f68bSGerd Hoffmann                 0x04,  /*  u8  bLength */
1910f58f68bSGerd Hoffmann                 0x24,  /*  u8  bDescriptorType */
1920f58f68bSGerd Hoffmann                 UAS_PIPE_ID_STATUS,
1930f58f68bSGerd Hoffmann                 0x00,  /*  u8  bReserved */
1940f58f68bSGerd Hoffmann             },
1950f58f68bSGerd Hoffmann         },{
1960f58f68bSGerd Hoffmann             .bEndpointAddress      = USB_DIR_IN | UAS_PIPE_ID_DATA_IN,
1970f58f68bSGerd Hoffmann             .bmAttributes          = USB_ENDPOINT_XFER_BULK,
1980f58f68bSGerd Hoffmann             .wMaxPacketSize        = 512,
1990f58f68bSGerd Hoffmann             .extra = (uint8_t[]) {
2000f58f68bSGerd Hoffmann                 0x04,  /*  u8  bLength */
2010f58f68bSGerd Hoffmann                 0x24,  /*  u8  bDescriptorType */
2020f58f68bSGerd Hoffmann                 UAS_PIPE_ID_DATA_IN,
2030f58f68bSGerd Hoffmann                 0x00,  /*  u8  bReserved */
2040f58f68bSGerd Hoffmann             },
2050f58f68bSGerd Hoffmann         },{
2060f58f68bSGerd Hoffmann             .bEndpointAddress      = USB_DIR_OUT | UAS_PIPE_ID_DATA_OUT,
2070f58f68bSGerd Hoffmann             .bmAttributes          = USB_ENDPOINT_XFER_BULK,
2080f58f68bSGerd Hoffmann             .wMaxPacketSize        = 512,
2090f58f68bSGerd Hoffmann             .extra = (uint8_t[]) {
2100f58f68bSGerd Hoffmann                 0x04,  /*  u8  bLength */
2110f58f68bSGerd Hoffmann                 0x24,  /*  u8  bDescriptorType */
2120f58f68bSGerd Hoffmann                 UAS_PIPE_ID_DATA_OUT,
2130f58f68bSGerd Hoffmann                 0x00,  /*  u8  bReserved */
2140f58f68bSGerd Hoffmann             },
2150f58f68bSGerd Hoffmann         },
2160f58f68bSGerd Hoffmann     }
2170f58f68bSGerd Hoffmann };
2180f58f68bSGerd Hoffmann 
21989a453d4SGerd Hoffmann static const USBDescIface desc_iface_super = {
22089a453d4SGerd Hoffmann     .bInterfaceNumber              = 0,
22189a453d4SGerd Hoffmann     .bNumEndpoints                 = 4,
22289a453d4SGerd Hoffmann     .bInterfaceClass               = USB_CLASS_MASS_STORAGE,
22389a453d4SGerd Hoffmann     .bInterfaceSubClass            = 0x06, /* SCSI */
22489a453d4SGerd Hoffmann     .bInterfaceProtocol            = 0x62, /* UAS  */
22589a453d4SGerd Hoffmann     .eps = (USBDescEndpoint[]) {
22689a453d4SGerd Hoffmann         {
22789a453d4SGerd Hoffmann             .bEndpointAddress      = USB_DIR_OUT | UAS_PIPE_ID_COMMAND,
22889a453d4SGerd Hoffmann             .bmAttributes          = USB_ENDPOINT_XFER_BULK,
22989a453d4SGerd Hoffmann             .wMaxPacketSize        = 1024,
23089a453d4SGerd Hoffmann             .bMaxBurst             = 15,
23189a453d4SGerd Hoffmann             .extra = (uint8_t[]) {
23289a453d4SGerd Hoffmann                 0x04,  /*  u8  bLength */
23389a453d4SGerd Hoffmann                 0x24,  /*  u8  bDescriptorType */
23489a453d4SGerd Hoffmann                 UAS_PIPE_ID_COMMAND,
23589a453d4SGerd Hoffmann                 0x00,  /*  u8  bReserved */
23689a453d4SGerd Hoffmann             },
23789a453d4SGerd Hoffmann         },{
23889a453d4SGerd Hoffmann             .bEndpointAddress      = USB_DIR_IN | UAS_PIPE_ID_STATUS,
23989a453d4SGerd Hoffmann             .bmAttributes          = USB_ENDPOINT_XFER_BULK,
24089a453d4SGerd Hoffmann             .wMaxPacketSize        = 1024,
24189a453d4SGerd Hoffmann             .bMaxBurst             = 15,
24289a453d4SGerd Hoffmann             .bmAttributes_super    = UAS_STREAM_BM_ATTR,
24389a453d4SGerd Hoffmann             .extra = (uint8_t[]) {
24489a453d4SGerd Hoffmann                 0x04,  /*  u8  bLength */
24589a453d4SGerd Hoffmann                 0x24,  /*  u8  bDescriptorType */
24689a453d4SGerd Hoffmann                 UAS_PIPE_ID_STATUS,
24789a453d4SGerd Hoffmann                 0x00,  /*  u8  bReserved */
24889a453d4SGerd Hoffmann             },
24989a453d4SGerd Hoffmann         },{
25089a453d4SGerd Hoffmann             .bEndpointAddress      = USB_DIR_IN | UAS_PIPE_ID_DATA_IN,
25189a453d4SGerd Hoffmann             .bmAttributes          = USB_ENDPOINT_XFER_BULK,
25289a453d4SGerd Hoffmann             .wMaxPacketSize        = 1024,
25389a453d4SGerd Hoffmann             .bMaxBurst             = 15,
25489a453d4SGerd Hoffmann             .bmAttributes_super    = UAS_STREAM_BM_ATTR,
25589a453d4SGerd Hoffmann             .extra = (uint8_t[]) {
25689a453d4SGerd Hoffmann                 0x04,  /*  u8  bLength */
25789a453d4SGerd Hoffmann                 0x24,  /*  u8  bDescriptorType */
25889a453d4SGerd Hoffmann                 UAS_PIPE_ID_DATA_IN,
25989a453d4SGerd Hoffmann                 0x00,  /*  u8  bReserved */
26089a453d4SGerd Hoffmann             },
26189a453d4SGerd Hoffmann         },{
26289a453d4SGerd Hoffmann             .bEndpointAddress      = USB_DIR_OUT | UAS_PIPE_ID_DATA_OUT,
26389a453d4SGerd Hoffmann             .bmAttributes          = USB_ENDPOINT_XFER_BULK,
26489a453d4SGerd Hoffmann             .wMaxPacketSize        = 1024,
26589a453d4SGerd Hoffmann             .bMaxBurst             = 15,
26689a453d4SGerd Hoffmann             .bmAttributes_super    = UAS_STREAM_BM_ATTR,
26789a453d4SGerd Hoffmann             .extra = (uint8_t[]) {
26889a453d4SGerd Hoffmann                 0x04,  /*  u8  bLength */
26989a453d4SGerd Hoffmann                 0x24,  /*  u8  bDescriptorType */
27089a453d4SGerd Hoffmann                 UAS_PIPE_ID_DATA_OUT,
27189a453d4SGerd Hoffmann                 0x00,  /*  u8  bReserved */
27289a453d4SGerd Hoffmann             },
27389a453d4SGerd Hoffmann         },
27489a453d4SGerd Hoffmann     }
27589a453d4SGerd Hoffmann };
27689a453d4SGerd Hoffmann 
2770f58f68bSGerd Hoffmann static const USBDescDevice desc_device_high = {
2780f58f68bSGerd Hoffmann     .bcdUSB                        = 0x0200,
2790f58f68bSGerd Hoffmann     .bMaxPacketSize0               = 64,
2800f58f68bSGerd Hoffmann     .bNumConfigurations            = 1,
2810f58f68bSGerd Hoffmann     .confs = (USBDescConfig[]) {
2820f58f68bSGerd Hoffmann         {
2830f58f68bSGerd Hoffmann             .bNumInterfaces        = 1,
2840f58f68bSGerd Hoffmann             .bConfigurationValue   = 1,
2850f58f68bSGerd Hoffmann             .iConfiguration        = STR_CONFIG_HIGH,
2860f58f68bSGerd Hoffmann             .bmAttributes          = 0xc0,
2870f58f68bSGerd Hoffmann             .nif = 1,
2880f58f68bSGerd Hoffmann             .ifs = &desc_iface_high,
2890f58f68bSGerd Hoffmann         },
2900f58f68bSGerd Hoffmann     },
2910f58f68bSGerd Hoffmann };
2920f58f68bSGerd Hoffmann 
29389a453d4SGerd Hoffmann static const USBDescDevice desc_device_super = {
29489a453d4SGerd Hoffmann     .bcdUSB                        = 0x0300,
29589a453d4SGerd Hoffmann     .bMaxPacketSize0               = 64,
29689a453d4SGerd Hoffmann     .bNumConfigurations            = 1,
29789a453d4SGerd Hoffmann     .confs = (USBDescConfig[]) {
29889a453d4SGerd Hoffmann         {
29989a453d4SGerd Hoffmann             .bNumInterfaces        = 1,
30089a453d4SGerd Hoffmann             .bConfigurationValue   = 1,
30189a453d4SGerd Hoffmann             .iConfiguration        = STR_CONFIG_SUPER,
30289a453d4SGerd Hoffmann             .bmAttributes          = 0xc0,
30389a453d4SGerd Hoffmann             .nif = 1,
30489a453d4SGerd Hoffmann             .ifs = &desc_iface_super,
30589a453d4SGerd Hoffmann         },
30689a453d4SGerd Hoffmann     },
30789a453d4SGerd Hoffmann };
30889a453d4SGerd Hoffmann 
3090f58f68bSGerd Hoffmann static const USBDesc desc = {
3100f58f68bSGerd Hoffmann     .id = {
3110f58f68bSGerd Hoffmann         .idVendor          = 0x46f4, /* CRC16() of "QEMU" */
3120daf5304SGerd Hoffmann         .idProduct         = 0x0003,
3130f58f68bSGerd Hoffmann         .bcdDevice         = 0,
3140f58f68bSGerd Hoffmann         .iManufacturer     = STR_MANUFACTURER,
3150f58f68bSGerd Hoffmann         .iProduct          = STR_PRODUCT,
3160f58f68bSGerd Hoffmann         .iSerialNumber     = STR_SERIALNUMBER,
3170f58f68bSGerd Hoffmann     },
3180f58f68bSGerd Hoffmann     .high  = &desc_device_high,
31989a453d4SGerd Hoffmann     .super = &desc_device_super,
3200f58f68bSGerd Hoffmann     .str   = desc_strings,
3210f58f68bSGerd Hoffmann };
3220f58f68bSGerd Hoffmann 
3230f58f68bSGerd Hoffmann /* --------------------------------------------------------------------- */
3240f58f68bSGerd Hoffmann 
32589a453d4SGerd Hoffmann static bool uas_using_streams(UASDevice *uas)
32689a453d4SGerd Hoffmann {
32789a453d4SGerd Hoffmann     return uas->dev.speed == USB_SPEED_SUPER;
32889a453d4SGerd Hoffmann }
32989a453d4SGerd Hoffmann 
33089a453d4SGerd Hoffmann /* --------------------------------------------------------------------- */
33189a453d4SGerd Hoffmann 
33289a453d4SGerd Hoffmann static UASStatus *usb_uas_alloc_status(UASDevice *uas, uint8_t id, uint16_t tag)
3330f58f68bSGerd Hoffmann {
3340f58f68bSGerd Hoffmann     UASStatus *st = g_new0(UASStatus, 1);
3350f58f68bSGerd Hoffmann 
3360f58f68bSGerd Hoffmann     st->status.hdr.id = id;
3370f58f68bSGerd Hoffmann     st->status.hdr.tag = cpu_to_be16(tag);
3380f58f68bSGerd Hoffmann     st->length = sizeof(uas_ui_header);
33989a453d4SGerd Hoffmann     if (uas_using_streams(uas)) {
34089a453d4SGerd Hoffmann         st->stream = tag;
34189a453d4SGerd Hoffmann     }
3420f58f68bSGerd Hoffmann     return st;
3430f58f68bSGerd Hoffmann }
3440f58f68bSGerd Hoffmann 
3450f58f68bSGerd Hoffmann static void usb_uas_send_status_bh(void *opaque)
3460f58f68bSGerd Hoffmann {
3470f58f68bSGerd Hoffmann     UASDevice *uas = opaque;
34889a453d4SGerd Hoffmann     UASStatus *st;
34989a453d4SGerd Hoffmann     USBPacket *p;
3500f58f68bSGerd Hoffmann 
35189a453d4SGerd Hoffmann     while ((st = QTAILQ_FIRST(&uas->results)) != NULL) {
35289a453d4SGerd Hoffmann         if (uas_using_streams(uas)) {
35389a453d4SGerd Hoffmann             p = uas->status3[st->stream];
35489a453d4SGerd Hoffmann             uas->status3[st->stream] = NULL;
35589a453d4SGerd Hoffmann         } else {
35689a453d4SGerd Hoffmann             p = uas->status2;
35789a453d4SGerd Hoffmann             uas->status2 = NULL;
35889a453d4SGerd Hoffmann         }
35989a453d4SGerd Hoffmann         if (p == NULL) {
36089a453d4SGerd Hoffmann             break;
36189a453d4SGerd Hoffmann         }
3620f58f68bSGerd Hoffmann 
3630f58f68bSGerd Hoffmann         usb_packet_copy(p, &st->status, st->length);
3640f58f68bSGerd Hoffmann         QTAILQ_REMOVE(&uas->results, st, next);
3650f58f68bSGerd Hoffmann         g_free(st);
3660f58f68bSGerd Hoffmann 
3679a77a0f5SHans de Goede         p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */
3680f58f68bSGerd Hoffmann         usb_packet_complete(&uas->dev, p);
3690f58f68bSGerd Hoffmann     }
37089a453d4SGerd Hoffmann }
3710f58f68bSGerd Hoffmann 
3720f58f68bSGerd Hoffmann static void usb_uas_queue_status(UASDevice *uas, UASStatus *st, int length)
3730f58f68bSGerd Hoffmann {
37489a453d4SGerd Hoffmann     USBPacket *p = uas_using_streams(uas) ?
37589a453d4SGerd Hoffmann         uas->status3[st->stream] : uas->status2;
37689a453d4SGerd Hoffmann 
3770f58f68bSGerd Hoffmann     st->length += length;
3780f58f68bSGerd Hoffmann     QTAILQ_INSERT_TAIL(&uas->results, st, next);
37989a453d4SGerd Hoffmann     if (p) {
3800f58f68bSGerd Hoffmann         /*
3810f58f68bSGerd Hoffmann          * Just schedule bh make sure any in-flight data transaction
3820f58f68bSGerd Hoffmann          * is finished before completing (sending) the status packet.
3830f58f68bSGerd Hoffmann          */
3840f58f68bSGerd Hoffmann         qemu_bh_schedule(uas->status_bh);
3850f58f68bSGerd Hoffmann     } else {
3860f58f68bSGerd Hoffmann         USBEndpoint *ep = usb_ep_get(&uas->dev, USB_TOKEN_IN,
3870f58f68bSGerd Hoffmann                                      UAS_PIPE_ID_STATUS);
38889a453d4SGerd Hoffmann         usb_wakeup(ep, st->stream);
3890f58f68bSGerd Hoffmann     }
3900f58f68bSGerd Hoffmann }
3910f58f68bSGerd Hoffmann 
3920f58f68bSGerd Hoffmann static void usb_uas_queue_response(UASDevice *uas, uint16_t tag,
3930f58f68bSGerd Hoffmann                                    uint8_t code, uint16_t add_info)
3940f58f68bSGerd Hoffmann {
39589a453d4SGerd Hoffmann     UASStatus *st = usb_uas_alloc_status(uas, UAS_UI_RESPONSE, tag);
3960f58f68bSGerd Hoffmann 
3970f58f68bSGerd Hoffmann     trace_usb_uas_response(uas->dev.addr, tag, code);
3980f58f68bSGerd Hoffmann     st->status.response.response_code = code;
3990f58f68bSGerd Hoffmann     st->status.response.add_response_info = cpu_to_be16(add_info);
4000f58f68bSGerd Hoffmann     usb_uas_queue_status(uas, st, sizeof(uas_ui_response));
4010f58f68bSGerd Hoffmann }
4020f58f68bSGerd Hoffmann 
4030f58f68bSGerd Hoffmann static void usb_uas_queue_sense(UASRequest *req, uint8_t status)
4040f58f68bSGerd Hoffmann {
40589a453d4SGerd Hoffmann     UASStatus *st = usb_uas_alloc_status(req->uas, UAS_UI_SENSE, req->tag);
4060f58f68bSGerd Hoffmann     int len, slen = 0;
4070f58f68bSGerd Hoffmann 
4080f58f68bSGerd Hoffmann     trace_usb_uas_sense(req->uas->dev.addr, req->tag, status);
4090f58f68bSGerd Hoffmann     st->status.sense.status = status;
4100f58f68bSGerd Hoffmann     st->status.sense.status_qualifier = cpu_to_be16(0);
4110f58f68bSGerd Hoffmann     if (status != GOOD) {
4120f58f68bSGerd Hoffmann         slen = scsi_req_get_sense(req->req, st->status.sense.sense_data,
4130f58f68bSGerd Hoffmann                                   sizeof(st->status.sense.sense_data));
4140f58f68bSGerd Hoffmann         st->status.sense.sense_length = cpu_to_be16(slen);
4150f58f68bSGerd Hoffmann     }
4160f58f68bSGerd Hoffmann     len = sizeof(uas_ui_sense) - sizeof(st->status.sense.sense_data) + slen;
4170f58f68bSGerd Hoffmann     usb_uas_queue_status(req->uas, st, len);
4180f58f68bSGerd Hoffmann }
4190f58f68bSGerd Hoffmann 
4200f58f68bSGerd Hoffmann static void usb_uas_queue_read_ready(UASRequest *req)
4210f58f68bSGerd Hoffmann {
42289a453d4SGerd Hoffmann     UASStatus *st = usb_uas_alloc_status(req->uas, UAS_UI_READ_READY,
42389a453d4SGerd Hoffmann                                          req->tag);
4240f58f68bSGerd Hoffmann 
4250f58f68bSGerd Hoffmann     trace_usb_uas_read_ready(req->uas->dev.addr, req->tag);
4260f58f68bSGerd Hoffmann     usb_uas_queue_status(req->uas, st, 0);
4270f58f68bSGerd Hoffmann }
4280f58f68bSGerd Hoffmann 
4290f58f68bSGerd Hoffmann static void usb_uas_queue_write_ready(UASRequest *req)
4300f58f68bSGerd Hoffmann {
43189a453d4SGerd Hoffmann     UASStatus *st = usb_uas_alloc_status(req->uas, UAS_UI_WRITE_READY,
43289a453d4SGerd Hoffmann                                          req->tag);
4330f58f68bSGerd Hoffmann 
4340f58f68bSGerd Hoffmann     trace_usb_uas_write_ready(req->uas->dev.addr, req->tag);
4350f58f68bSGerd Hoffmann     usb_uas_queue_status(req->uas, st, 0);
4360f58f68bSGerd Hoffmann }
4370f58f68bSGerd Hoffmann 
4380f58f68bSGerd Hoffmann /* --------------------------------------------------------------------- */
4390f58f68bSGerd Hoffmann 
4400f58f68bSGerd Hoffmann static int usb_uas_get_lun(uint64_t lun64)
4410f58f68bSGerd Hoffmann {
4420f58f68bSGerd Hoffmann     return (lun64 >> 48) & 0xff;
4430f58f68bSGerd Hoffmann }
4440f58f68bSGerd Hoffmann 
4450f58f68bSGerd Hoffmann static SCSIDevice *usb_uas_get_dev(UASDevice *uas, uint64_t lun64)
4460f58f68bSGerd Hoffmann {
4470f58f68bSGerd Hoffmann     if ((lun64 >> 56) != 0x00) {
4480f58f68bSGerd Hoffmann         return NULL;
4490f58f68bSGerd Hoffmann     }
4500f58f68bSGerd Hoffmann     return scsi_device_find(&uas->bus, 0, 0, usb_uas_get_lun(lun64));
4510f58f68bSGerd Hoffmann }
4520f58f68bSGerd Hoffmann 
4530f58f68bSGerd Hoffmann static void usb_uas_complete_data_packet(UASRequest *req)
4540f58f68bSGerd Hoffmann {
4550f58f68bSGerd Hoffmann     USBPacket *p;
4560f58f68bSGerd Hoffmann 
4570f58f68bSGerd Hoffmann     if (!req->data_async) {
4580f58f68bSGerd Hoffmann         return;
4590f58f68bSGerd Hoffmann     }
4600f58f68bSGerd Hoffmann     p = req->data;
4610f58f68bSGerd Hoffmann     req->data = NULL;
4620f58f68bSGerd Hoffmann     req->data_async = false;
4639a77a0f5SHans de Goede     p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */
4640f58f68bSGerd Hoffmann     usb_packet_complete(&req->uas->dev, p);
4650f58f68bSGerd Hoffmann }
4660f58f68bSGerd Hoffmann 
4670f58f68bSGerd Hoffmann static void usb_uas_copy_data(UASRequest *req)
4680f58f68bSGerd Hoffmann {
4690f58f68bSGerd Hoffmann     uint32_t length;
4700f58f68bSGerd Hoffmann 
4710f58f68bSGerd Hoffmann     length = MIN(req->buf_size - req->buf_off,
4729a77a0f5SHans de Goede                  req->data->iov.size - req->data->actual_length);
4730f58f68bSGerd Hoffmann     trace_usb_uas_xfer_data(req->uas->dev.addr, req->tag, length,
4749a77a0f5SHans de Goede                             req->data->actual_length, req->data->iov.size,
4750f58f68bSGerd Hoffmann                             req->buf_off, req->buf_size);
4760f58f68bSGerd Hoffmann     usb_packet_copy(req->data, scsi_req_get_buf(req->req) + req->buf_off,
4770f58f68bSGerd Hoffmann                     length);
4780f58f68bSGerd Hoffmann     req->buf_off += length;
4790f58f68bSGerd Hoffmann     req->data_off += length;
4800f58f68bSGerd Hoffmann 
4819a77a0f5SHans de Goede     if (req->data->actual_length == req->data->iov.size) {
4820f58f68bSGerd Hoffmann         usb_uas_complete_data_packet(req);
4830f58f68bSGerd Hoffmann     }
4840f58f68bSGerd Hoffmann     if (req->buf_size && req->buf_off == req->buf_size) {
4850f58f68bSGerd Hoffmann         req->buf_off = 0;
4860f58f68bSGerd Hoffmann         req->buf_size = 0;
4870f58f68bSGerd Hoffmann         scsi_req_continue(req->req);
4880f58f68bSGerd Hoffmann     }
4890f58f68bSGerd Hoffmann }
4900f58f68bSGerd Hoffmann 
4910f58f68bSGerd Hoffmann static void usb_uas_start_next_transfer(UASDevice *uas)
4920f58f68bSGerd Hoffmann {
4930f58f68bSGerd Hoffmann     UASRequest *req;
4940f58f68bSGerd Hoffmann 
49589a453d4SGerd Hoffmann     if (uas_using_streams(uas)) {
49689a453d4SGerd Hoffmann         return;
49789a453d4SGerd Hoffmann     }
49889a453d4SGerd Hoffmann 
4990f58f68bSGerd Hoffmann     QTAILQ_FOREACH(req, &uas->requests, next) {
5000f58f68bSGerd Hoffmann         if (req->active || req->complete) {
5010f58f68bSGerd Hoffmann             continue;
5020f58f68bSGerd Hoffmann         }
50389a453d4SGerd Hoffmann         if (req->req->cmd.mode == SCSI_XFER_FROM_DEV && uas->datain2 == NULL) {
50489a453d4SGerd Hoffmann             uas->datain2 = req;
5050f58f68bSGerd Hoffmann             usb_uas_queue_read_ready(req);
5060f58f68bSGerd Hoffmann             req->active = true;
5070f58f68bSGerd Hoffmann             return;
5080f58f68bSGerd Hoffmann         }
50989a453d4SGerd Hoffmann         if (req->req->cmd.mode == SCSI_XFER_TO_DEV && uas->dataout2 == NULL) {
51089a453d4SGerd Hoffmann             uas->dataout2 = req;
5110f58f68bSGerd Hoffmann             usb_uas_queue_write_ready(req);
5120f58f68bSGerd Hoffmann             req->active = true;
5130f58f68bSGerd Hoffmann             return;
5140f58f68bSGerd Hoffmann         }
5150f58f68bSGerd Hoffmann     }
5160f58f68bSGerd Hoffmann }
5170f58f68bSGerd Hoffmann 
5180f58f68bSGerd Hoffmann static UASRequest *usb_uas_alloc_request(UASDevice *uas, uas_ui *ui)
5190f58f68bSGerd Hoffmann {
5200f58f68bSGerd Hoffmann     UASRequest *req;
5210f58f68bSGerd Hoffmann 
5220f58f68bSGerd Hoffmann     req = g_new0(UASRequest, 1);
5230f58f68bSGerd Hoffmann     req->uas = uas;
5240f58f68bSGerd Hoffmann     req->tag = be16_to_cpu(ui->hdr.tag);
5250f58f68bSGerd Hoffmann     req->lun = be64_to_cpu(ui->command.lun);
5260f58f68bSGerd Hoffmann     req->dev = usb_uas_get_dev(req->uas, req->lun);
5270f58f68bSGerd Hoffmann     return req;
5280f58f68bSGerd Hoffmann }
5290f58f68bSGerd Hoffmann 
5300f58f68bSGerd Hoffmann static void usb_uas_scsi_free_request(SCSIBus *bus, void *priv)
5310f58f68bSGerd Hoffmann {
5320f58f68bSGerd Hoffmann     UASRequest *req = priv;
5330f58f68bSGerd Hoffmann     UASDevice *uas = req->uas;
5340f58f68bSGerd Hoffmann 
53589a453d4SGerd Hoffmann     if (req == uas->datain2) {
53689a453d4SGerd Hoffmann         uas->datain2 = NULL;
5370f58f68bSGerd Hoffmann     }
53889a453d4SGerd Hoffmann     if (req == uas->dataout2) {
53989a453d4SGerd Hoffmann         uas->dataout2 = NULL;
5400f58f68bSGerd Hoffmann     }
5410f58f68bSGerd Hoffmann     QTAILQ_REMOVE(&uas->requests, req, next);
5420f58f68bSGerd Hoffmann     g_free(req);
543347e40ffSGerd Hoffmann     usb_uas_start_next_transfer(uas);
5440f58f68bSGerd Hoffmann }
5450f58f68bSGerd Hoffmann 
5460f58f68bSGerd Hoffmann static UASRequest *usb_uas_find_request(UASDevice *uas, uint16_t tag)
5470f58f68bSGerd Hoffmann {
5480f58f68bSGerd Hoffmann     UASRequest *req;
5490f58f68bSGerd Hoffmann 
5500f58f68bSGerd Hoffmann     QTAILQ_FOREACH(req, &uas->requests, next) {
5510f58f68bSGerd Hoffmann         if (req->tag == tag) {
5520f58f68bSGerd Hoffmann             return req;
5530f58f68bSGerd Hoffmann         }
5540f58f68bSGerd Hoffmann     }
5550f58f68bSGerd Hoffmann     return NULL;
5560f58f68bSGerd Hoffmann }
5570f58f68bSGerd Hoffmann 
5580f58f68bSGerd Hoffmann static void usb_uas_scsi_transfer_data(SCSIRequest *r, uint32_t len)
5590f58f68bSGerd Hoffmann {
5600f58f68bSGerd Hoffmann     UASRequest *req = r->hba_private;
5610f58f68bSGerd Hoffmann 
5620f58f68bSGerd Hoffmann     trace_usb_uas_scsi_data(req->uas->dev.addr, req->tag, len);
5630f58f68bSGerd Hoffmann     req->buf_off = 0;
5640f58f68bSGerd Hoffmann     req->buf_size = len;
5650f58f68bSGerd Hoffmann     if (req->data) {
5660f58f68bSGerd Hoffmann         usb_uas_copy_data(req);
5670f58f68bSGerd Hoffmann     } else {
5680f58f68bSGerd Hoffmann         usb_uas_start_next_transfer(req->uas);
5690f58f68bSGerd Hoffmann     }
5700f58f68bSGerd Hoffmann }
5710f58f68bSGerd Hoffmann 
5720f58f68bSGerd Hoffmann static void usb_uas_scsi_command_complete(SCSIRequest *r,
5730f58f68bSGerd Hoffmann                                           uint32_t status, size_t resid)
5740f58f68bSGerd Hoffmann {
5750f58f68bSGerd Hoffmann     UASRequest *req = r->hba_private;
5760f58f68bSGerd Hoffmann 
5770f58f68bSGerd Hoffmann     trace_usb_uas_scsi_complete(req->uas->dev.addr, req->tag, status, resid);
5780f58f68bSGerd Hoffmann     req->complete = true;
5790f58f68bSGerd Hoffmann     if (req->data) {
5800f58f68bSGerd Hoffmann         usb_uas_complete_data_packet(req);
5810f58f68bSGerd Hoffmann     }
5820f58f68bSGerd Hoffmann     usb_uas_queue_sense(req, status);
5830f58f68bSGerd Hoffmann     scsi_req_unref(req->req);
5840f58f68bSGerd Hoffmann }
5850f58f68bSGerd Hoffmann 
5860f58f68bSGerd Hoffmann static void usb_uas_scsi_request_cancelled(SCSIRequest *r)
5870f58f68bSGerd Hoffmann {
5880f58f68bSGerd Hoffmann     UASRequest *req = r->hba_private;
5890f58f68bSGerd Hoffmann 
5900f58f68bSGerd Hoffmann     /* FIXME: queue notification to status pipe? */
5910f58f68bSGerd Hoffmann     scsi_req_unref(req->req);
5920f58f68bSGerd Hoffmann }
5930f58f68bSGerd Hoffmann 
5940f58f68bSGerd Hoffmann static const struct SCSIBusInfo usb_uas_scsi_info = {
5950f58f68bSGerd Hoffmann     .tcq = true,
5960f58f68bSGerd Hoffmann     .max_target = 0,
5970f58f68bSGerd Hoffmann     .max_lun = 255,
5980f58f68bSGerd Hoffmann 
5990f58f68bSGerd Hoffmann     .transfer_data = usb_uas_scsi_transfer_data,
6000f58f68bSGerd Hoffmann     .complete = usb_uas_scsi_command_complete,
6010f58f68bSGerd Hoffmann     .cancel = usb_uas_scsi_request_cancelled,
6020f58f68bSGerd Hoffmann     .free_request = usb_uas_scsi_free_request,
6030f58f68bSGerd Hoffmann };
6040f58f68bSGerd Hoffmann 
6050f58f68bSGerd Hoffmann /* --------------------------------------------------------------------- */
6060f58f68bSGerd Hoffmann 
6070f58f68bSGerd Hoffmann static void usb_uas_handle_reset(USBDevice *dev)
6080f58f68bSGerd Hoffmann {
6090f58f68bSGerd Hoffmann     UASDevice *uas = DO_UPCAST(UASDevice, dev, dev);
6100f58f68bSGerd Hoffmann     UASRequest *req, *nreq;
6110f58f68bSGerd Hoffmann     UASStatus *st, *nst;
6120f58f68bSGerd Hoffmann 
6130f58f68bSGerd Hoffmann     trace_usb_uas_reset(dev->addr);
6140f58f68bSGerd Hoffmann     QTAILQ_FOREACH_SAFE(req, &uas->requests, next, nreq) {
6150f58f68bSGerd Hoffmann         scsi_req_cancel(req->req);
6160f58f68bSGerd Hoffmann     }
6170f58f68bSGerd Hoffmann     QTAILQ_FOREACH_SAFE(st, &uas->results, next, nst) {
6180f58f68bSGerd Hoffmann         QTAILQ_REMOVE(&uas->results, st, next);
6190f58f68bSGerd Hoffmann         g_free(st);
6200f58f68bSGerd Hoffmann     }
6210f58f68bSGerd Hoffmann }
6220f58f68bSGerd Hoffmann 
6239a77a0f5SHans de Goede static void usb_uas_handle_control(USBDevice *dev, USBPacket *p,
6240f58f68bSGerd Hoffmann                int request, int value, int index, int length, uint8_t *data)
6250f58f68bSGerd Hoffmann {
6260f58f68bSGerd Hoffmann     int ret;
6270f58f68bSGerd Hoffmann 
6280f58f68bSGerd Hoffmann     ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
6290f58f68bSGerd Hoffmann     if (ret >= 0) {
6309a77a0f5SHans de Goede         return;
6310f58f68bSGerd Hoffmann     }
6320f58f68bSGerd Hoffmann     fprintf(stderr, "%s: unhandled control request\n", __func__);
6339a77a0f5SHans de Goede     p->status = USB_RET_STALL;
6340f58f68bSGerd Hoffmann }
6350f58f68bSGerd Hoffmann 
6360f58f68bSGerd Hoffmann static void usb_uas_cancel_io(USBDevice *dev, USBPacket *p)
6370f58f68bSGerd Hoffmann {
6380f58f68bSGerd Hoffmann     UASDevice *uas = DO_UPCAST(UASDevice, dev, dev);
6390f58f68bSGerd Hoffmann     UASRequest *req, *nreq;
64089a453d4SGerd Hoffmann     int i;
6410f58f68bSGerd Hoffmann 
64289a453d4SGerd Hoffmann     if (uas->status2 == p) {
64389a453d4SGerd Hoffmann         uas->status2 = NULL;
6440f58f68bSGerd Hoffmann         qemu_bh_cancel(uas->status_bh);
6450f58f68bSGerd Hoffmann         return;
6460f58f68bSGerd Hoffmann     }
64789a453d4SGerd Hoffmann     if (uas_using_streams(uas)) {
64889a453d4SGerd Hoffmann         for (i = 0; i < UAS_MAX_STREAMS; i++) {
64989a453d4SGerd Hoffmann             if (uas->status3[i] == p) {
65089a453d4SGerd Hoffmann                 uas->status3[i] = NULL;
65189a453d4SGerd Hoffmann                 return;
65289a453d4SGerd Hoffmann             }
65389a453d4SGerd Hoffmann             if (uas->data3[i] == p) {
65489a453d4SGerd Hoffmann                 uas->data3[i] = NULL;
65589a453d4SGerd Hoffmann                 return;
65689a453d4SGerd Hoffmann             }
65789a453d4SGerd Hoffmann         }
65889a453d4SGerd Hoffmann     }
6590f58f68bSGerd Hoffmann     QTAILQ_FOREACH_SAFE(req, &uas->requests, next, nreq) {
6600f58f68bSGerd Hoffmann         if (req->data == p) {
6610f58f68bSGerd Hoffmann             req->data = NULL;
6620f58f68bSGerd Hoffmann             return;
6630f58f68bSGerd Hoffmann         }
6640f58f68bSGerd Hoffmann     }
6650f58f68bSGerd Hoffmann     assert(!"canceled usb packet not found");
6660f58f68bSGerd Hoffmann }
6670f58f68bSGerd Hoffmann 
6680f58f68bSGerd Hoffmann static void usb_uas_command(UASDevice *uas, uas_ui *ui)
6690f58f68bSGerd Hoffmann {
6700f58f68bSGerd Hoffmann     UASRequest *req;
6710f58f68bSGerd Hoffmann     uint32_t len;
6720f58f68bSGerd Hoffmann 
6730f58f68bSGerd Hoffmann     req = usb_uas_find_request(uas, be16_to_cpu(ui->hdr.tag));
6740f58f68bSGerd Hoffmann     if (req) {
6750f58f68bSGerd Hoffmann         goto overlapped_tag;
6760f58f68bSGerd Hoffmann     }
6770f58f68bSGerd Hoffmann     req = usb_uas_alloc_request(uas, ui);
6780f58f68bSGerd Hoffmann     if (req->dev == NULL) {
6790f58f68bSGerd Hoffmann         goto bad_target;
6800f58f68bSGerd Hoffmann     }
6810f58f68bSGerd Hoffmann 
6820f58f68bSGerd Hoffmann     trace_usb_uas_command(uas->dev.addr, req->tag,
6830f58f68bSGerd Hoffmann                           usb_uas_get_lun(req->lun),
6840f58f68bSGerd Hoffmann                           req->lun >> 32, req->lun & 0xffffffff);
6850f58f68bSGerd Hoffmann     QTAILQ_INSERT_TAIL(&uas->requests, req, next);
68689a453d4SGerd Hoffmann     if (uas_using_streams(uas) && uas->data3[req->tag] != NULL) {
68789a453d4SGerd Hoffmann         req->data = uas->data3[req->tag];
68889a453d4SGerd Hoffmann         req->data_async = true;
68989a453d4SGerd Hoffmann         uas->data3[req->tag] = NULL;
69089a453d4SGerd Hoffmann     }
69189a453d4SGerd Hoffmann 
6920f58f68bSGerd Hoffmann     req->req = scsi_req_new(req->dev, req->tag,
6930f58f68bSGerd Hoffmann                             usb_uas_get_lun(req->lun),
6940f58f68bSGerd Hoffmann                             ui->command.cdb, req);
69589a453d4SGerd Hoffmann #if 1
69689a453d4SGerd Hoffmann     scsi_req_print(req->req);
69789a453d4SGerd Hoffmann #endif
6980f58f68bSGerd Hoffmann     len = scsi_req_enqueue(req->req);
6990f58f68bSGerd Hoffmann     if (len) {
7000f58f68bSGerd Hoffmann         req->data_size = len;
7010f58f68bSGerd Hoffmann         scsi_req_continue(req->req);
7020f58f68bSGerd Hoffmann     }
7030f58f68bSGerd Hoffmann     return;
7040f58f68bSGerd Hoffmann 
7050f58f68bSGerd Hoffmann overlapped_tag:
7060f58f68bSGerd Hoffmann     usb_uas_queue_response(uas, req->tag, UAS_RC_OVERLAPPED_TAG, 0);
7070f58f68bSGerd Hoffmann     return;
7080f58f68bSGerd Hoffmann 
7090f58f68bSGerd Hoffmann bad_target:
7100f58f68bSGerd Hoffmann     /*
7110f58f68bSGerd Hoffmann      * FIXME: Seems to upset linux, is this wrong?
7120f58f68bSGerd Hoffmann      * NOTE: Happens only with no scsi devices at the bus, not sure
7130f58f68bSGerd Hoffmann      *       this is a valid UAS setup in the first place.
7140f58f68bSGerd Hoffmann      */
7150f58f68bSGerd Hoffmann     usb_uas_queue_response(uas, req->tag, UAS_RC_INVALID_INFO_UNIT, 0);
7160f58f68bSGerd Hoffmann     g_free(req);
7170f58f68bSGerd Hoffmann }
7180f58f68bSGerd Hoffmann 
7190f58f68bSGerd Hoffmann static void usb_uas_task(UASDevice *uas, uas_ui *ui)
7200f58f68bSGerd Hoffmann {
7210f58f68bSGerd Hoffmann     uint16_t tag = be16_to_cpu(ui->hdr.tag);
7220f58f68bSGerd Hoffmann     uint64_t lun64 = be64_to_cpu(ui->task.lun);
7230f58f68bSGerd Hoffmann     SCSIDevice *dev = usb_uas_get_dev(uas, lun64);
7240f58f68bSGerd Hoffmann     int lun = usb_uas_get_lun(lun64);
7250f58f68bSGerd Hoffmann     UASRequest *req;
7260f58f68bSGerd Hoffmann     uint16_t task_tag;
7270f58f68bSGerd Hoffmann 
7280f58f68bSGerd Hoffmann     req = usb_uas_find_request(uas, be16_to_cpu(ui->hdr.tag));
7290f58f68bSGerd Hoffmann     if (req) {
7300f58f68bSGerd Hoffmann         goto overlapped_tag;
7310f58f68bSGerd Hoffmann     }
7320f58f68bSGerd Hoffmann 
7330f58f68bSGerd Hoffmann     switch (ui->task.function) {
7340f58f68bSGerd Hoffmann     case UAS_TMF_ABORT_TASK:
7350f58f68bSGerd Hoffmann         task_tag = be16_to_cpu(ui->task.task_tag);
7360f58f68bSGerd Hoffmann         trace_usb_uas_tmf_abort_task(uas->dev.addr, tag, task_tag);
7370f58f68bSGerd Hoffmann         if (dev == NULL) {
7380f58f68bSGerd Hoffmann             goto bad_target;
7390f58f68bSGerd Hoffmann         }
7400f58f68bSGerd Hoffmann         if (dev->lun != lun) {
7410f58f68bSGerd Hoffmann             goto incorrect_lun;
7420f58f68bSGerd Hoffmann         }
7430f58f68bSGerd Hoffmann         req = usb_uas_find_request(uas, task_tag);
7440f58f68bSGerd Hoffmann         if (req && req->dev == dev) {
7450f58f68bSGerd Hoffmann             scsi_req_cancel(req->req);
7460f58f68bSGerd Hoffmann         }
7470f58f68bSGerd Hoffmann         usb_uas_queue_response(uas, tag, UAS_RC_TMF_COMPLETE, 0);
7480f58f68bSGerd Hoffmann         break;
7490f58f68bSGerd Hoffmann 
7500f58f68bSGerd Hoffmann     case UAS_TMF_LOGICAL_UNIT_RESET:
7510f58f68bSGerd Hoffmann         trace_usb_uas_tmf_logical_unit_reset(uas->dev.addr, tag, lun);
7520f58f68bSGerd Hoffmann         if (dev == NULL) {
7530f58f68bSGerd Hoffmann             goto bad_target;
7540f58f68bSGerd Hoffmann         }
7550f58f68bSGerd Hoffmann         if (dev->lun != lun) {
7560f58f68bSGerd Hoffmann             goto incorrect_lun;
7570f58f68bSGerd Hoffmann         }
7580f58f68bSGerd Hoffmann         qdev_reset_all(&dev->qdev);
7590f58f68bSGerd Hoffmann         usb_uas_queue_response(uas, tag, UAS_RC_TMF_COMPLETE, 0);
7600f58f68bSGerd Hoffmann         break;
7610f58f68bSGerd Hoffmann 
7620f58f68bSGerd Hoffmann     default:
7630f58f68bSGerd Hoffmann         trace_usb_uas_tmf_unsupported(uas->dev.addr, tag, ui->task.function);
7640f58f68bSGerd Hoffmann         usb_uas_queue_response(uas, tag, UAS_RC_TMF_NOT_SUPPORTED, 0);
7650f58f68bSGerd Hoffmann         break;
7660f58f68bSGerd Hoffmann     }
7670f58f68bSGerd Hoffmann     return;
7680f58f68bSGerd Hoffmann 
7690f58f68bSGerd Hoffmann overlapped_tag:
7700f58f68bSGerd Hoffmann     usb_uas_queue_response(uas, req->tag, UAS_RC_OVERLAPPED_TAG, 0);
7710f58f68bSGerd Hoffmann     return;
7720f58f68bSGerd Hoffmann 
7730f58f68bSGerd Hoffmann bad_target:
7740f58f68bSGerd Hoffmann     /* FIXME: correct?  [see long comment in usb_uas_command()] */
7750f58f68bSGerd Hoffmann     usb_uas_queue_response(uas, tag, UAS_RC_INVALID_INFO_UNIT, 0);
7760f58f68bSGerd Hoffmann     return;
7770f58f68bSGerd Hoffmann 
7780f58f68bSGerd Hoffmann incorrect_lun:
7790f58f68bSGerd Hoffmann     usb_uas_queue_response(uas, tag, UAS_RC_INCORRECT_LUN, 0);
7800f58f68bSGerd Hoffmann }
7810f58f68bSGerd Hoffmann 
7829a77a0f5SHans de Goede static void usb_uas_handle_data(USBDevice *dev, USBPacket *p)
7830f58f68bSGerd Hoffmann {
7840f58f68bSGerd Hoffmann     UASDevice *uas = DO_UPCAST(UASDevice, dev, dev);
7850f58f68bSGerd Hoffmann     uas_ui ui;
7860f58f68bSGerd Hoffmann     UASStatus *st;
7870f58f68bSGerd Hoffmann     UASRequest *req;
7889a77a0f5SHans de Goede     int length;
7890f58f68bSGerd Hoffmann 
7900f58f68bSGerd Hoffmann     switch (p->ep->nr) {
7910f58f68bSGerd Hoffmann     case UAS_PIPE_ID_COMMAND:
7920f58f68bSGerd Hoffmann         length = MIN(sizeof(ui), p->iov.size);
7930f58f68bSGerd Hoffmann         usb_packet_copy(p, &ui, length);
7940f58f68bSGerd Hoffmann         switch (ui.hdr.id) {
7950f58f68bSGerd Hoffmann         case UAS_UI_COMMAND:
7960f58f68bSGerd Hoffmann             usb_uas_command(uas, &ui);
7970f58f68bSGerd Hoffmann             break;
7980f58f68bSGerd Hoffmann         case UAS_UI_TASK_MGMT:
7990f58f68bSGerd Hoffmann             usb_uas_task(uas, &ui);
8000f58f68bSGerd Hoffmann             break;
8010f58f68bSGerd Hoffmann         default:
8020f58f68bSGerd Hoffmann             fprintf(stderr, "%s: unknown command ui: id 0x%x\n",
8030f58f68bSGerd Hoffmann                     __func__, ui.hdr.id);
8049a77a0f5SHans de Goede             p->status = USB_RET_STALL;
8050f58f68bSGerd Hoffmann             break;
8060f58f68bSGerd Hoffmann         }
8070f58f68bSGerd Hoffmann         break;
8080f58f68bSGerd Hoffmann     case UAS_PIPE_ID_STATUS:
80989a453d4SGerd Hoffmann         if (p->stream) {
81089a453d4SGerd Hoffmann             QTAILQ_FOREACH(st, &uas->results, next) {
81189a453d4SGerd Hoffmann                 if (st->stream == p->stream) {
81289a453d4SGerd Hoffmann                     break;
81389a453d4SGerd Hoffmann                 }
81489a453d4SGerd Hoffmann             }
8150f58f68bSGerd Hoffmann             if (st == NULL) {
81689a453d4SGerd Hoffmann                 assert(uas->status3[p->stream] == NULL);
81789a453d4SGerd Hoffmann                 uas->status3[p->stream] = p;
8189a77a0f5SHans de Goede                 p->status = USB_RET_ASYNC;
8190f58f68bSGerd Hoffmann                 break;
8200f58f68bSGerd Hoffmann             }
82189a453d4SGerd Hoffmann         } else {
82289a453d4SGerd Hoffmann             st = QTAILQ_FIRST(&uas->results);
82389a453d4SGerd Hoffmann             if (st == NULL) {
82489a453d4SGerd Hoffmann                 assert(uas->status2 == NULL);
82589a453d4SGerd Hoffmann                 uas->status2 = p;
82689a453d4SGerd Hoffmann                 p->status = USB_RET_ASYNC;
82789a453d4SGerd Hoffmann                 break;
82889a453d4SGerd Hoffmann             }
82989a453d4SGerd Hoffmann         }
8300f58f68bSGerd Hoffmann         usb_packet_copy(p, &st->status, st->length);
8310f58f68bSGerd Hoffmann         QTAILQ_REMOVE(&uas->results, st, next);
8320f58f68bSGerd Hoffmann         g_free(st);
8330f58f68bSGerd Hoffmann         break;
8340f58f68bSGerd Hoffmann     case UAS_PIPE_ID_DATA_IN:
8350f58f68bSGerd Hoffmann     case UAS_PIPE_ID_DATA_OUT:
83689a453d4SGerd Hoffmann         if (p->stream) {
83789a453d4SGerd Hoffmann             req = usb_uas_find_request(uas, p->stream);
83889a453d4SGerd Hoffmann         } else {
83989a453d4SGerd Hoffmann             req = (p->ep->nr == UAS_PIPE_ID_DATA_IN)
84089a453d4SGerd Hoffmann                 ? uas->datain2 : uas->dataout2;
84189a453d4SGerd Hoffmann         }
8420f58f68bSGerd Hoffmann         if (req == NULL) {
84389a453d4SGerd Hoffmann             if (p->stream) {
84489a453d4SGerd Hoffmann                 assert(uas->data3[p->stream] == NULL);
84589a453d4SGerd Hoffmann                 uas->data3[p->stream] = p;
84689a453d4SGerd Hoffmann                 p->status = USB_RET_ASYNC;
84789a453d4SGerd Hoffmann                 break;
84889a453d4SGerd Hoffmann             } else {
8490f58f68bSGerd Hoffmann                 fprintf(stderr, "%s: no inflight request\n", __func__);
8509a77a0f5SHans de Goede                 p->status = USB_RET_STALL;
8510f58f68bSGerd Hoffmann                 break;
8520f58f68bSGerd Hoffmann             }
85389a453d4SGerd Hoffmann         }
8540f58f68bSGerd Hoffmann         scsi_req_ref(req->req);
8550f58f68bSGerd Hoffmann         req->data = p;
8560f58f68bSGerd Hoffmann         usb_uas_copy_data(req);
8579a77a0f5SHans de Goede         if (p->actual_length == p->iov.size || req->complete) {
8580f58f68bSGerd Hoffmann             req->data = NULL;
8590f58f68bSGerd Hoffmann         } else {
8600f58f68bSGerd Hoffmann             req->data_async = true;
8619a77a0f5SHans de Goede             p->status = USB_RET_ASYNC;
8620f58f68bSGerd Hoffmann         }
8630f58f68bSGerd Hoffmann         scsi_req_unref(req->req);
8640f58f68bSGerd Hoffmann         usb_uas_start_next_transfer(uas);
8650f58f68bSGerd Hoffmann         break;
8660f58f68bSGerd Hoffmann     default:
8670f58f68bSGerd Hoffmann         fprintf(stderr, "%s: invalid endpoint %d\n", __func__, p->ep->nr);
8689a77a0f5SHans de Goede         p->status = USB_RET_STALL;
8690f58f68bSGerd Hoffmann         break;
8700f58f68bSGerd Hoffmann     }
8710f58f68bSGerd Hoffmann }
8720f58f68bSGerd Hoffmann 
8730f58f68bSGerd Hoffmann static void usb_uas_handle_destroy(USBDevice *dev)
8740f58f68bSGerd Hoffmann {
8750f58f68bSGerd Hoffmann     UASDevice *uas = DO_UPCAST(UASDevice, dev, dev);
8760f58f68bSGerd Hoffmann 
8770f58f68bSGerd Hoffmann     qemu_bh_delete(uas->status_bh);
8780f58f68bSGerd Hoffmann }
8790f58f68bSGerd Hoffmann 
8800f58f68bSGerd Hoffmann static int usb_uas_init(USBDevice *dev)
8810f58f68bSGerd Hoffmann {
8820f58f68bSGerd Hoffmann     UASDevice *uas = DO_UPCAST(UASDevice, dev, dev);
8830f58f68bSGerd Hoffmann 
8840f58f68bSGerd Hoffmann     usb_desc_create_serial(dev);
8850f58f68bSGerd Hoffmann     usb_desc_init(dev);
8860f58f68bSGerd Hoffmann 
8870f58f68bSGerd Hoffmann     QTAILQ_INIT(&uas->results);
8880f58f68bSGerd Hoffmann     QTAILQ_INIT(&uas->requests);
8890f58f68bSGerd Hoffmann     uas->status_bh = qemu_bh_new(usb_uas_send_status_bh, uas);
8900f58f68bSGerd Hoffmann 
89111fc853cSKONRAD Frederic     scsi_bus_new(&uas->bus, &uas->dev.qdev, &usb_uas_scsi_info, NULL);
8920f58f68bSGerd Hoffmann 
8930f58f68bSGerd Hoffmann     return 0;
8940f58f68bSGerd Hoffmann }
8950f58f68bSGerd Hoffmann 
8960f58f68bSGerd Hoffmann static const VMStateDescription vmstate_usb_uas = {
8970f58f68bSGerd Hoffmann     .name = "usb-uas",
8980f58f68bSGerd Hoffmann     .unmigratable = 1,
8990f58f68bSGerd Hoffmann     .fields = (VMStateField[]) {
9000f58f68bSGerd Hoffmann         VMSTATE_USB_DEVICE(dev, UASDevice),
9010f58f68bSGerd Hoffmann         VMSTATE_END_OF_LIST()
9020f58f68bSGerd Hoffmann     }
9030f58f68bSGerd Hoffmann };
9040f58f68bSGerd Hoffmann 
9050f58f68bSGerd Hoffmann static void usb_uas_class_initfn(ObjectClass *klass, void *data)
9060f58f68bSGerd Hoffmann {
9070f58f68bSGerd Hoffmann     DeviceClass *dc = DEVICE_CLASS(klass);
9080f58f68bSGerd Hoffmann     USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
9090f58f68bSGerd Hoffmann 
9100f58f68bSGerd Hoffmann     uc->init           = usb_uas_init;
9110f58f68bSGerd Hoffmann     uc->product_desc   = desc_strings[STR_PRODUCT];
9120f58f68bSGerd Hoffmann     uc->usb_desc       = &desc;
9130f58f68bSGerd Hoffmann     uc->cancel_packet  = usb_uas_cancel_io;
9140f58f68bSGerd Hoffmann     uc->handle_attach  = usb_desc_attach;
9150f58f68bSGerd Hoffmann     uc->handle_reset   = usb_uas_handle_reset;
9160f58f68bSGerd Hoffmann     uc->handle_control = usb_uas_handle_control;
9170f58f68bSGerd Hoffmann     uc->handle_data    = usb_uas_handle_data;
9180f58f68bSGerd Hoffmann     uc->handle_destroy = usb_uas_handle_destroy;
919*125ee0edSMarcel Apfelbaum     set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
9200f58f68bSGerd Hoffmann     dc->fw_name = "storage";
9210f58f68bSGerd Hoffmann     dc->vmsd = &vmstate_usb_uas;
9220f58f68bSGerd Hoffmann }
9230f58f68bSGerd Hoffmann 
9248c43a6f0SAndreas Färber static const TypeInfo uas_info = {
9250f58f68bSGerd Hoffmann     .name          = "usb-uas",
9260f58f68bSGerd Hoffmann     .parent        = TYPE_USB_DEVICE,
9270f58f68bSGerd Hoffmann     .instance_size = sizeof(UASDevice),
9280f58f68bSGerd Hoffmann     .class_init    = usb_uas_class_initfn,
9290f58f68bSGerd Hoffmann };
9300f58f68bSGerd Hoffmann 
9310f58f68bSGerd Hoffmann static void usb_uas_register_types(void)
9320f58f68bSGerd Hoffmann {
9330f58f68bSGerd Hoffmann     type_register_static(&uas_info);
9340f58f68bSGerd Hoffmann }
9350f58f68bSGerd Hoffmann 
9360f58f68bSGerd Hoffmann type_init(usb_uas_register_types)
937