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
12e532b2e0SPeter Maydell #include "qemu/osdep.h"
131de7afc9SPaolo Bonzini #include "qemu/option.h"
141de7afc9SPaolo Bonzini #include "qemu/config-file.h"
150f58f68bSGerd Hoffmann #include "trace.h"
166b7afb7fSGonglei #include "qemu/error-report.h"
17db725815SMarkus Armbruster #include "qemu/main-loop.h"
180b8fa32fSMarkus Armbruster #include "qemu/module.h"
19d755cb96SPhilippe Mathieu-Daudé #include "qemu/log.h"
200f58f68bSGerd Hoffmann
210f58f68bSGerd Hoffmann #include "hw/usb.h"
22d6454270SMarkus Armbruster #include "migration/vmstate.h"
23463581a8SMichael S. Tsirkin #include "desc.h"
24a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
250d09e41aSPaolo Bonzini #include "hw/scsi/scsi.h"
2608e2c9f1SPaolo Bonzini #include "scsi/constants.h"
27db1015e9SEduardo Habkost #include "qom/object.h"
280f58f68bSGerd Hoffmann
290f58f68bSGerd Hoffmann /* --------------------------------------------------------------------- */
300f58f68bSGerd Hoffmann
310f58f68bSGerd Hoffmann #define UAS_UI_COMMAND 0x01
320f58f68bSGerd Hoffmann #define UAS_UI_SENSE 0x03
330f58f68bSGerd Hoffmann #define UAS_UI_RESPONSE 0x04
340f58f68bSGerd Hoffmann #define UAS_UI_TASK_MGMT 0x05
350f58f68bSGerd Hoffmann #define UAS_UI_READ_READY 0x06
360f58f68bSGerd Hoffmann #define UAS_UI_WRITE_READY 0x07
370f58f68bSGerd Hoffmann
380f58f68bSGerd Hoffmann #define UAS_RC_TMF_COMPLETE 0x00
390f58f68bSGerd Hoffmann #define UAS_RC_INVALID_INFO_UNIT 0x02
400f58f68bSGerd Hoffmann #define UAS_RC_TMF_NOT_SUPPORTED 0x04
410f58f68bSGerd Hoffmann #define UAS_RC_TMF_FAILED 0x05
420f58f68bSGerd Hoffmann #define UAS_RC_TMF_SUCCEEDED 0x08
430f58f68bSGerd Hoffmann #define UAS_RC_INCORRECT_LUN 0x09
440f58f68bSGerd Hoffmann #define UAS_RC_OVERLAPPED_TAG 0x0a
450f58f68bSGerd Hoffmann
460f58f68bSGerd Hoffmann #define UAS_TMF_ABORT_TASK 0x01
470f58f68bSGerd Hoffmann #define UAS_TMF_ABORT_TASK_SET 0x02
480f58f68bSGerd Hoffmann #define UAS_TMF_CLEAR_TASK_SET 0x04
490f58f68bSGerd Hoffmann #define UAS_TMF_LOGICAL_UNIT_RESET 0x08
500f58f68bSGerd Hoffmann #define UAS_TMF_I_T_NEXUS_RESET 0x10
510f58f68bSGerd Hoffmann #define UAS_TMF_CLEAR_ACA 0x40
520f58f68bSGerd Hoffmann #define UAS_TMF_QUERY_TASK 0x80
530f58f68bSGerd Hoffmann #define UAS_TMF_QUERY_TASK_SET 0x81
540f58f68bSGerd Hoffmann #define UAS_TMF_QUERY_ASYNC_EVENT 0x82
550f58f68bSGerd Hoffmann
560f58f68bSGerd Hoffmann #define UAS_PIPE_ID_COMMAND 0x01
570f58f68bSGerd Hoffmann #define UAS_PIPE_ID_STATUS 0x02
580f58f68bSGerd Hoffmann #define UAS_PIPE_ID_DATA_IN 0x03
590f58f68bSGerd Hoffmann #define UAS_PIPE_ID_DATA_OUT 0x04
600f58f68bSGerd Hoffmann
610f58f68bSGerd Hoffmann typedef struct {
620f58f68bSGerd Hoffmann uint8_t id;
630f58f68bSGerd Hoffmann uint8_t reserved;
640f58f68bSGerd Hoffmann uint16_t tag;
655007c940SHans de Goede } QEMU_PACKED uas_iu_header;
660f58f68bSGerd Hoffmann
670f58f68bSGerd Hoffmann typedef struct {
680f58f68bSGerd Hoffmann uint8_t prio_taskattr; /* 6:3 priority, 2:0 task attribute */
690f58f68bSGerd Hoffmann uint8_t reserved_1;
700f58f68bSGerd Hoffmann uint8_t add_cdb_length; /* 7:2 additional adb length (dwords) */
710f58f68bSGerd Hoffmann uint8_t reserved_2;
720f58f68bSGerd Hoffmann uint64_t lun;
730f58f68bSGerd Hoffmann uint8_t cdb[16];
74fe9d8927SJohn Millikin uint8_t add_cdb[1];
755007c940SHans de Goede } QEMU_PACKED uas_iu_command;
760f58f68bSGerd Hoffmann
770f58f68bSGerd Hoffmann typedef struct {
780f58f68bSGerd Hoffmann uint16_t status_qualifier;
790f58f68bSGerd Hoffmann uint8_t status;
800f58f68bSGerd Hoffmann uint8_t reserved[7];
810f58f68bSGerd Hoffmann uint16_t sense_length;
820f58f68bSGerd Hoffmann uint8_t sense_data[18];
835007c940SHans de Goede } QEMU_PACKED uas_iu_sense;
840f58f68bSGerd Hoffmann
850f58f68bSGerd Hoffmann typedef struct {
8649cfa2fdSHans de Goede uint8_t add_response_info[3];
870f58f68bSGerd Hoffmann uint8_t response_code;
885007c940SHans de Goede } QEMU_PACKED uas_iu_response;
890f58f68bSGerd Hoffmann
900f58f68bSGerd Hoffmann typedef struct {
910f58f68bSGerd Hoffmann uint8_t function;
920f58f68bSGerd Hoffmann uint8_t reserved;
930f58f68bSGerd Hoffmann uint16_t task_tag;
940f58f68bSGerd Hoffmann uint64_t lun;
955007c940SHans de Goede } QEMU_PACKED uas_iu_task_mgmt;
960f58f68bSGerd Hoffmann
970f58f68bSGerd Hoffmann typedef struct {
985007c940SHans de Goede uas_iu_header hdr;
990f58f68bSGerd Hoffmann union {
1005007c940SHans de Goede uas_iu_command command;
1015007c940SHans de Goede uas_iu_sense sense;
1025007c940SHans de Goede uas_iu_task_mgmt task;
1035007c940SHans de Goede uas_iu_response response;
1040f58f68bSGerd Hoffmann };
1055007c940SHans de Goede } QEMU_PACKED uas_iu;
1060f58f68bSGerd Hoffmann
1070f58f68bSGerd Hoffmann /* --------------------------------------------------------------------- */
1080f58f68bSGerd Hoffmann
10989a453d4SGerd Hoffmann #define UAS_STREAM_BM_ATTR 4
11089a453d4SGerd Hoffmann #define UAS_MAX_STREAMS (1 << UAS_STREAM_BM_ATTR)
11189a453d4SGerd Hoffmann
1120f58f68bSGerd Hoffmann typedef struct UASDevice UASDevice;
1130f58f68bSGerd Hoffmann typedef struct UASRequest UASRequest;
1140f58f68bSGerd Hoffmann typedef struct UASStatus UASStatus;
1150f58f68bSGerd Hoffmann
1160f58f68bSGerd Hoffmann struct UASDevice {
1170f58f68bSGerd Hoffmann USBDevice dev;
1180f58f68bSGerd Hoffmann SCSIBus bus;
1190f58f68bSGerd Hoffmann QEMUBH *status_bh;
1200f58f68bSGerd Hoffmann QTAILQ_HEAD(, UASStatus) results;
1210f58f68bSGerd Hoffmann QTAILQ_HEAD(, UASRequest) requests;
12289a453d4SGerd Hoffmann
1231556a8fcSGerd Hoffmann /* properties */
1241556a8fcSGerd Hoffmann uint32_t requestlog;
1251556a8fcSGerd Hoffmann
12689a453d4SGerd Hoffmann /* usb 2.0 only */
12789a453d4SGerd Hoffmann USBPacket *status2;
12889a453d4SGerd Hoffmann UASRequest *datain2;
12989a453d4SGerd Hoffmann UASRequest *dataout2;
13089a453d4SGerd Hoffmann
13189a453d4SGerd Hoffmann /* usb 3.0 only */
1320478661eSHans de Goede USBPacket *data3[UAS_MAX_STREAMS + 1];
1330478661eSHans de Goede USBPacket *status3[UAS_MAX_STREAMS + 1];
1340f58f68bSGerd Hoffmann };
1350f58f68bSGerd Hoffmann
1360b06d099SGonglei #define TYPE_USB_UAS "usb-uas"
1378063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(UASDevice, USB_UAS)
1380b06d099SGonglei
1390f58f68bSGerd Hoffmann struct UASRequest {
1400f58f68bSGerd Hoffmann uint16_t tag;
1410f58f68bSGerd Hoffmann uint64_t lun;
1420f58f68bSGerd Hoffmann UASDevice *uas;
1430f58f68bSGerd Hoffmann SCSIDevice *dev;
1440f58f68bSGerd Hoffmann SCSIRequest *req;
1450f58f68bSGerd Hoffmann USBPacket *data;
1460f58f68bSGerd Hoffmann bool data_async;
1470f58f68bSGerd Hoffmann bool active;
1480f58f68bSGerd Hoffmann bool complete;
1490f58f68bSGerd Hoffmann uint32_t buf_off;
1500f58f68bSGerd Hoffmann uint32_t buf_size;
1510f58f68bSGerd Hoffmann uint32_t data_off;
1520f58f68bSGerd Hoffmann uint32_t data_size;
1530f58f68bSGerd Hoffmann QTAILQ_ENTRY(UASRequest) next;
1540f58f68bSGerd Hoffmann };
1550f58f68bSGerd Hoffmann
1560f58f68bSGerd Hoffmann struct UASStatus {
15789a453d4SGerd Hoffmann uint32_t stream;
1585007c940SHans de Goede uas_iu status;
1590f58f68bSGerd Hoffmann uint32_t length;
1600f58f68bSGerd Hoffmann QTAILQ_ENTRY(UASStatus) next;
1610f58f68bSGerd Hoffmann };
1620f58f68bSGerd Hoffmann
1630f58f68bSGerd Hoffmann /* --------------------------------------------------------------------- */
1640f58f68bSGerd Hoffmann
1650f58f68bSGerd Hoffmann enum {
1660f58f68bSGerd Hoffmann STR_MANUFACTURER = 1,
1670f58f68bSGerd Hoffmann STR_PRODUCT,
1680f58f68bSGerd Hoffmann STR_SERIALNUMBER,
1690f58f68bSGerd Hoffmann STR_CONFIG_HIGH,
17089a453d4SGerd Hoffmann STR_CONFIG_SUPER,
1710f58f68bSGerd Hoffmann };
1720f58f68bSGerd Hoffmann
1730f58f68bSGerd Hoffmann static const USBDescStrings desc_strings = {
1740f58f68bSGerd Hoffmann [STR_MANUFACTURER] = "QEMU",
1750f58f68bSGerd Hoffmann [STR_PRODUCT] = "USB Attached SCSI HBA",
1760f58f68bSGerd Hoffmann [STR_SERIALNUMBER] = "27842",
1770f58f68bSGerd Hoffmann [STR_CONFIG_HIGH] = "High speed config (usb 2.0)",
17889a453d4SGerd Hoffmann [STR_CONFIG_SUPER] = "Super speed config (usb 3.0)",
1790f58f68bSGerd Hoffmann };
1800f58f68bSGerd Hoffmann
1810f58f68bSGerd Hoffmann static const USBDescIface desc_iface_high = {
1820f58f68bSGerd Hoffmann .bInterfaceNumber = 0,
1830f58f68bSGerd Hoffmann .bNumEndpoints = 4,
1840f58f68bSGerd Hoffmann .bInterfaceClass = USB_CLASS_MASS_STORAGE,
1850f58f68bSGerd Hoffmann .bInterfaceSubClass = 0x06, /* SCSI */
1860f58f68bSGerd Hoffmann .bInterfaceProtocol = 0x62, /* UAS */
1870f58f68bSGerd Hoffmann .eps = (USBDescEndpoint[]) {
1880f58f68bSGerd Hoffmann {
1890f58f68bSGerd Hoffmann .bEndpointAddress = USB_DIR_OUT | UAS_PIPE_ID_COMMAND,
1900f58f68bSGerd Hoffmann .bmAttributes = USB_ENDPOINT_XFER_BULK,
1910f58f68bSGerd Hoffmann .wMaxPacketSize = 512,
1920f58f68bSGerd Hoffmann .extra = (uint8_t[]) {
1930f58f68bSGerd Hoffmann 0x04, /* u8 bLength */
1940f58f68bSGerd Hoffmann 0x24, /* u8 bDescriptorType */
1950f58f68bSGerd Hoffmann UAS_PIPE_ID_COMMAND,
1960f58f68bSGerd Hoffmann 0x00, /* u8 bReserved */
1970f58f68bSGerd Hoffmann },
1980f58f68bSGerd Hoffmann },{
1990f58f68bSGerd Hoffmann .bEndpointAddress = USB_DIR_IN | UAS_PIPE_ID_STATUS,
2000f58f68bSGerd Hoffmann .bmAttributes = USB_ENDPOINT_XFER_BULK,
2010f58f68bSGerd Hoffmann .wMaxPacketSize = 512,
2020f58f68bSGerd Hoffmann .extra = (uint8_t[]) {
2030f58f68bSGerd Hoffmann 0x04, /* u8 bLength */
2040f58f68bSGerd Hoffmann 0x24, /* u8 bDescriptorType */
2050f58f68bSGerd Hoffmann UAS_PIPE_ID_STATUS,
2060f58f68bSGerd Hoffmann 0x00, /* u8 bReserved */
2070f58f68bSGerd Hoffmann },
2080f58f68bSGerd Hoffmann },{
2090f58f68bSGerd Hoffmann .bEndpointAddress = USB_DIR_IN | UAS_PIPE_ID_DATA_IN,
2100f58f68bSGerd Hoffmann .bmAttributes = USB_ENDPOINT_XFER_BULK,
2110f58f68bSGerd Hoffmann .wMaxPacketSize = 512,
2120f58f68bSGerd Hoffmann .extra = (uint8_t[]) {
2130f58f68bSGerd Hoffmann 0x04, /* u8 bLength */
2140f58f68bSGerd Hoffmann 0x24, /* u8 bDescriptorType */
2150f58f68bSGerd Hoffmann UAS_PIPE_ID_DATA_IN,
2160f58f68bSGerd Hoffmann 0x00, /* u8 bReserved */
2170f58f68bSGerd Hoffmann },
2180f58f68bSGerd Hoffmann },{
2190f58f68bSGerd Hoffmann .bEndpointAddress = USB_DIR_OUT | UAS_PIPE_ID_DATA_OUT,
2200f58f68bSGerd Hoffmann .bmAttributes = USB_ENDPOINT_XFER_BULK,
2210f58f68bSGerd Hoffmann .wMaxPacketSize = 512,
2220f58f68bSGerd Hoffmann .extra = (uint8_t[]) {
2230f58f68bSGerd Hoffmann 0x04, /* u8 bLength */
2240f58f68bSGerd Hoffmann 0x24, /* u8 bDescriptorType */
2250f58f68bSGerd Hoffmann UAS_PIPE_ID_DATA_OUT,
2260f58f68bSGerd Hoffmann 0x00, /* u8 bReserved */
2270f58f68bSGerd Hoffmann },
2280f58f68bSGerd Hoffmann },
2290f58f68bSGerd Hoffmann }
2300f58f68bSGerd Hoffmann };
2310f58f68bSGerd Hoffmann
23289a453d4SGerd Hoffmann static const USBDescIface desc_iface_super = {
23389a453d4SGerd Hoffmann .bInterfaceNumber = 0,
23489a453d4SGerd Hoffmann .bNumEndpoints = 4,
23589a453d4SGerd Hoffmann .bInterfaceClass = USB_CLASS_MASS_STORAGE,
23689a453d4SGerd Hoffmann .bInterfaceSubClass = 0x06, /* SCSI */
23789a453d4SGerd Hoffmann .bInterfaceProtocol = 0x62, /* UAS */
23889a453d4SGerd Hoffmann .eps = (USBDescEndpoint[]) {
23989a453d4SGerd Hoffmann {
24089a453d4SGerd Hoffmann .bEndpointAddress = USB_DIR_OUT | UAS_PIPE_ID_COMMAND,
24189a453d4SGerd Hoffmann .bmAttributes = USB_ENDPOINT_XFER_BULK,
24289a453d4SGerd Hoffmann .wMaxPacketSize = 1024,
24389a453d4SGerd Hoffmann .bMaxBurst = 15,
24489a453d4SGerd Hoffmann .extra = (uint8_t[]) {
24589a453d4SGerd Hoffmann 0x04, /* u8 bLength */
24689a453d4SGerd Hoffmann 0x24, /* u8 bDescriptorType */
24789a453d4SGerd Hoffmann UAS_PIPE_ID_COMMAND,
24889a453d4SGerd Hoffmann 0x00, /* u8 bReserved */
24989a453d4SGerd Hoffmann },
25089a453d4SGerd Hoffmann },{
25189a453d4SGerd Hoffmann .bEndpointAddress = USB_DIR_IN | UAS_PIPE_ID_STATUS,
25289a453d4SGerd Hoffmann .bmAttributes = USB_ENDPOINT_XFER_BULK,
25389a453d4SGerd Hoffmann .wMaxPacketSize = 1024,
25489a453d4SGerd Hoffmann .bMaxBurst = 15,
25589a453d4SGerd Hoffmann .bmAttributes_super = UAS_STREAM_BM_ATTR,
25689a453d4SGerd Hoffmann .extra = (uint8_t[]) {
25789a453d4SGerd Hoffmann 0x04, /* u8 bLength */
25889a453d4SGerd Hoffmann 0x24, /* u8 bDescriptorType */
25989a453d4SGerd Hoffmann UAS_PIPE_ID_STATUS,
26089a453d4SGerd Hoffmann 0x00, /* u8 bReserved */
26189a453d4SGerd Hoffmann },
26289a453d4SGerd Hoffmann },{
26389a453d4SGerd Hoffmann .bEndpointAddress = USB_DIR_IN | UAS_PIPE_ID_DATA_IN,
26489a453d4SGerd Hoffmann .bmAttributes = USB_ENDPOINT_XFER_BULK,
26589a453d4SGerd Hoffmann .wMaxPacketSize = 1024,
26689a453d4SGerd Hoffmann .bMaxBurst = 15,
26789a453d4SGerd Hoffmann .bmAttributes_super = UAS_STREAM_BM_ATTR,
26889a453d4SGerd Hoffmann .extra = (uint8_t[]) {
26989a453d4SGerd Hoffmann 0x04, /* u8 bLength */
27089a453d4SGerd Hoffmann 0x24, /* u8 bDescriptorType */
27189a453d4SGerd Hoffmann UAS_PIPE_ID_DATA_IN,
27289a453d4SGerd Hoffmann 0x00, /* u8 bReserved */
27389a453d4SGerd Hoffmann },
27489a453d4SGerd Hoffmann },{
27589a453d4SGerd Hoffmann .bEndpointAddress = USB_DIR_OUT | UAS_PIPE_ID_DATA_OUT,
27689a453d4SGerd Hoffmann .bmAttributes = USB_ENDPOINT_XFER_BULK,
27789a453d4SGerd Hoffmann .wMaxPacketSize = 1024,
27889a453d4SGerd Hoffmann .bMaxBurst = 15,
27989a453d4SGerd Hoffmann .bmAttributes_super = UAS_STREAM_BM_ATTR,
28089a453d4SGerd Hoffmann .extra = (uint8_t[]) {
28189a453d4SGerd Hoffmann 0x04, /* u8 bLength */
28289a453d4SGerd Hoffmann 0x24, /* u8 bDescriptorType */
28389a453d4SGerd Hoffmann UAS_PIPE_ID_DATA_OUT,
28489a453d4SGerd Hoffmann 0x00, /* u8 bReserved */
28589a453d4SGerd Hoffmann },
28689a453d4SGerd Hoffmann },
28789a453d4SGerd Hoffmann }
28889a453d4SGerd Hoffmann };
28989a453d4SGerd Hoffmann
2900f58f68bSGerd Hoffmann static const USBDescDevice desc_device_high = {
2910f58f68bSGerd Hoffmann .bcdUSB = 0x0200,
2920f58f68bSGerd Hoffmann .bMaxPacketSize0 = 64,
2930f58f68bSGerd Hoffmann .bNumConfigurations = 1,
2940f58f68bSGerd Hoffmann .confs = (USBDescConfig[]) {
2950f58f68bSGerd Hoffmann {
2960f58f68bSGerd Hoffmann .bNumInterfaces = 1,
2970f58f68bSGerd Hoffmann .bConfigurationValue = 1,
2980f58f68bSGerd Hoffmann .iConfiguration = STR_CONFIG_HIGH,
299bd93976aSPantelis Koukousoulas .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER,
3000f58f68bSGerd Hoffmann .nif = 1,
3010f58f68bSGerd Hoffmann .ifs = &desc_iface_high,
3020f58f68bSGerd Hoffmann },
3030f58f68bSGerd Hoffmann },
3040f58f68bSGerd Hoffmann };
3050f58f68bSGerd Hoffmann
30689a453d4SGerd Hoffmann static const USBDescDevice desc_device_super = {
30789a453d4SGerd Hoffmann .bcdUSB = 0x0300,
3088ddcc435SGerd Hoffmann .bMaxPacketSize0 = 9,
30989a453d4SGerd Hoffmann .bNumConfigurations = 1,
31089a453d4SGerd Hoffmann .confs = (USBDescConfig[]) {
31189a453d4SGerd Hoffmann {
31289a453d4SGerd Hoffmann .bNumInterfaces = 1,
31389a453d4SGerd Hoffmann .bConfigurationValue = 1,
31489a453d4SGerd Hoffmann .iConfiguration = STR_CONFIG_SUPER,
315bd93976aSPantelis Koukousoulas .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER,
31689a453d4SGerd Hoffmann .nif = 1,
31789a453d4SGerd Hoffmann .ifs = &desc_iface_super,
31889a453d4SGerd Hoffmann },
31989a453d4SGerd Hoffmann },
32089a453d4SGerd Hoffmann };
32189a453d4SGerd Hoffmann
3220f58f68bSGerd Hoffmann static const USBDesc desc = {
3230f58f68bSGerd Hoffmann .id = {
3240f58f68bSGerd Hoffmann .idVendor = 0x46f4, /* CRC16() of "QEMU" */
3250daf5304SGerd Hoffmann .idProduct = 0x0003,
3260f58f68bSGerd Hoffmann .bcdDevice = 0,
3270f58f68bSGerd Hoffmann .iManufacturer = STR_MANUFACTURER,
3280f58f68bSGerd Hoffmann .iProduct = STR_PRODUCT,
3290f58f68bSGerd Hoffmann .iSerialNumber = STR_SERIALNUMBER,
3300f58f68bSGerd Hoffmann },
3310f58f68bSGerd Hoffmann .high = &desc_device_high,
33289a453d4SGerd Hoffmann .super = &desc_device_super,
3330f58f68bSGerd Hoffmann .str = desc_strings,
3340f58f68bSGerd Hoffmann };
3350f58f68bSGerd Hoffmann
3360f58f68bSGerd Hoffmann /* --------------------------------------------------------------------- */
3370f58f68bSGerd Hoffmann
uas_using_streams(UASDevice * uas)33889a453d4SGerd Hoffmann static bool uas_using_streams(UASDevice *uas)
33989a453d4SGerd Hoffmann {
34089a453d4SGerd Hoffmann return uas->dev.speed == USB_SPEED_SUPER;
34189a453d4SGerd Hoffmann }
34289a453d4SGerd Hoffmann
34389a453d4SGerd Hoffmann /* --------------------------------------------------------------------- */
34489a453d4SGerd Hoffmann
usb_uas_alloc_status(UASDevice * uas,uint8_t id,uint16_t tag)34589a453d4SGerd Hoffmann static UASStatus *usb_uas_alloc_status(UASDevice *uas, uint8_t id, uint16_t tag)
3460f58f68bSGerd Hoffmann {
3470f58f68bSGerd Hoffmann UASStatus *st = g_new0(UASStatus, 1);
3480f58f68bSGerd Hoffmann
3490f58f68bSGerd Hoffmann st->status.hdr.id = id;
3500f58f68bSGerd Hoffmann st->status.hdr.tag = cpu_to_be16(tag);
3515007c940SHans de Goede st->length = sizeof(uas_iu_header);
35289a453d4SGerd Hoffmann if (uas_using_streams(uas)) {
35389a453d4SGerd Hoffmann st->stream = tag;
35489a453d4SGerd Hoffmann }
3550f58f68bSGerd Hoffmann return st;
3560f58f68bSGerd Hoffmann }
3570f58f68bSGerd Hoffmann
usb_uas_send_status_bh(void * opaque)3580f58f68bSGerd Hoffmann static void usb_uas_send_status_bh(void *opaque)
3590f58f68bSGerd Hoffmann {
3600f58f68bSGerd Hoffmann UASDevice *uas = opaque;
36189a453d4SGerd Hoffmann UASStatus *st;
36289a453d4SGerd Hoffmann USBPacket *p;
3630f58f68bSGerd Hoffmann
36489a453d4SGerd Hoffmann while ((st = QTAILQ_FIRST(&uas->results)) != NULL) {
36589a453d4SGerd Hoffmann if (uas_using_streams(uas)) {
36689a453d4SGerd Hoffmann p = uas->status3[st->stream];
36789a453d4SGerd Hoffmann uas->status3[st->stream] = NULL;
36889a453d4SGerd Hoffmann } else {
36989a453d4SGerd Hoffmann p = uas->status2;
37089a453d4SGerd Hoffmann uas->status2 = NULL;
37189a453d4SGerd Hoffmann }
37289a453d4SGerd Hoffmann if (p == NULL) {
37389a453d4SGerd Hoffmann break;
37489a453d4SGerd Hoffmann }
3750f58f68bSGerd Hoffmann
3760f58f68bSGerd Hoffmann usb_packet_copy(p, &st->status, st->length);
3770f58f68bSGerd Hoffmann QTAILQ_REMOVE(&uas->results, st, next);
3780f58f68bSGerd Hoffmann g_free(st);
3790f58f68bSGerd Hoffmann
3809a77a0f5SHans de Goede p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */
3810f58f68bSGerd Hoffmann usb_packet_complete(&uas->dev, p);
3820f58f68bSGerd Hoffmann }
38389a453d4SGerd Hoffmann }
3840f58f68bSGerd Hoffmann
usb_uas_queue_status(UASDevice * uas,UASStatus * st,int length)3850f58f68bSGerd Hoffmann static void usb_uas_queue_status(UASDevice *uas, UASStatus *st, int length)
3860f58f68bSGerd Hoffmann {
38789a453d4SGerd Hoffmann USBPacket *p = uas_using_streams(uas) ?
38889a453d4SGerd Hoffmann uas->status3[st->stream] : uas->status2;
38989a453d4SGerd Hoffmann
3900f58f68bSGerd Hoffmann st->length += length;
3910f58f68bSGerd Hoffmann QTAILQ_INSERT_TAIL(&uas->results, st, next);
39289a453d4SGerd Hoffmann if (p) {
3930f58f68bSGerd Hoffmann /*
3940f58f68bSGerd Hoffmann * Just schedule bh make sure any in-flight data transaction
3950f58f68bSGerd Hoffmann * is finished before completing (sending) the status packet.
3960f58f68bSGerd Hoffmann */
3970f58f68bSGerd Hoffmann qemu_bh_schedule(uas->status_bh);
3980f58f68bSGerd Hoffmann } else {
3990f58f68bSGerd Hoffmann USBEndpoint *ep = usb_ep_get(&uas->dev, USB_TOKEN_IN,
4000f58f68bSGerd Hoffmann UAS_PIPE_ID_STATUS);
40189a453d4SGerd Hoffmann usb_wakeup(ep, st->stream);
4020f58f68bSGerd Hoffmann }
4030f58f68bSGerd Hoffmann }
4040f58f68bSGerd Hoffmann
usb_uas_queue_response(UASDevice * uas,uint16_t tag,uint8_t code)40549cfa2fdSHans de Goede static void usb_uas_queue_response(UASDevice *uas, uint16_t tag, uint8_t code)
4060f58f68bSGerd Hoffmann {
40789a453d4SGerd Hoffmann UASStatus *st = usb_uas_alloc_status(uas, UAS_UI_RESPONSE, tag);
4080f58f68bSGerd Hoffmann
4090f58f68bSGerd Hoffmann trace_usb_uas_response(uas->dev.addr, tag, code);
4100f58f68bSGerd Hoffmann st->status.response.response_code = code;
4115007c940SHans de Goede usb_uas_queue_status(uas, st, sizeof(uas_iu_response));
4120f58f68bSGerd Hoffmann }
4130f58f68bSGerd Hoffmann
usb_uas_queue_sense(UASRequest * req,uint8_t status)4140f58f68bSGerd Hoffmann static void usb_uas_queue_sense(UASRequest *req, uint8_t status)
4150f58f68bSGerd Hoffmann {
41689a453d4SGerd Hoffmann UASStatus *st = usb_uas_alloc_status(req->uas, UAS_UI_SENSE, req->tag);
4170f58f68bSGerd Hoffmann int len, slen = 0;
4180f58f68bSGerd Hoffmann
4190f58f68bSGerd Hoffmann trace_usb_uas_sense(req->uas->dev.addr, req->tag, status);
4200f58f68bSGerd Hoffmann st->status.sense.status = status;
4210f58f68bSGerd Hoffmann st->status.sense.status_qualifier = cpu_to_be16(0);
4220f58f68bSGerd Hoffmann if (status != GOOD) {
4230f58f68bSGerd Hoffmann slen = scsi_req_get_sense(req->req, st->status.sense.sense_data,
4240f58f68bSGerd Hoffmann sizeof(st->status.sense.sense_data));
4250f58f68bSGerd Hoffmann st->status.sense.sense_length = cpu_to_be16(slen);
4260f58f68bSGerd Hoffmann }
4275007c940SHans de Goede len = sizeof(uas_iu_sense) - sizeof(st->status.sense.sense_data) + slen;
4280f58f68bSGerd Hoffmann usb_uas_queue_status(req->uas, st, len);
4290f58f68bSGerd Hoffmann }
4300f58f68bSGerd Hoffmann
usb_uas_queue_fake_sense(UASDevice * uas,uint16_t tag,struct SCSISense sense)431d4bfc7b9SHans de Goede static void usb_uas_queue_fake_sense(UASDevice *uas, uint16_t tag,
432d4bfc7b9SHans de Goede struct SCSISense sense)
433d4bfc7b9SHans de Goede {
434d4bfc7b9SHans de Goede UASStatus *st = usb_uas_alloc_status(uas, UAS_UI_SENSE, tag);
435d4bfc7b9SHans de Goede int len, slen = 0;
436d4bfc7b9SHans de Goede
437d4bfc7b9SHans de Goede st->status.sense.status = CHECK_CONDITION;
438d4bfc7b9SHans de Goede st->status.sense.status_qualifier = cpu_to_be16(0);
439d4bfc7b9SHans de Goede st->status.sense.sense_data[0] = 0x70;
440d4bfc7b9SHans de Goede st->status.sense.sense_data[2] = sense.key;
441d4bfc7b9SHans de Goede st->status.sense.sense_data[7] = 10;
442d4bfc7b9SHans de Goede st->status.sense.sense_data[12] = sense.asc;
443d4bfc7b9SHans de Goede st->status.sense.sense_data[13] = sense.ascq;
444d4bfc7b9SHans de Goede slen = 18;
4455007c940SHans de Goede len = sizeof(uas_iu_sense) - sizeof(st->status.sense.sense_data) + slen;
446d4bfc7b9SHans de Goede usb_uas_queue_status(uas, st, len);
447d4bfc7b9SHans de Goede }
448d4bfc7b9SHans de Goede
usb_uas_queue_read_ready(UASRequest * req)4490f58f68bSGerd Hoffmann static void usb_uas_queue_read_ready(UASRequest *req)
4500f58f68bSGerd Hoffmann {
45189a453d4SGerd Hoffmann UASStatus *st = usb_uas_alloc_status(req->uas, UAS_UI_READ_READY,
45289a453d4SGerd Hoffmann req->tag);
4530f58f68bSGerd Hoffmann
4540f58f68bSGerd Hoffmann trace_usb_uas_read_ready(req->uas->dev.addr, req->tag);
4550f58f68bSGerd Hoffmann usb_uas_queue_status(req->uas, st, 0);
4560f58f68bSGerd Hoffmann }
4570f58f68bSGerd Hoffmann
usb_uas_queue_write_ready(UASRequest * req)4580f58f68bSGerd Hoffmann static void usb_uas_queue_write_ready(UASRequest *req)
4590f58f68bSGerd Hoffmann {
46089a453d4SGerd Hoffmann UASStatus *st = usb_uas_alloc_status(req->uas, UAS_UI_WRITE_READY,
46189a453d4SGerd Hoffmann req->tag);
4620f58f68bSGerd Hoffmann
4630f58f68bSGerd Hoffmann trace_usb_uas_write_ready(req->uas->dev.addr, req->tag);
4640f58f68bSGerd Hoffmann usb_uas_queue_status(req->uas, st, 0);
4650f58f68bSGerd Hoffmann }
4660f58f68bSGerd Hoffmann
4670f58f68bSGerd Hoffmann /* --------------------------------------------------------------------- */
4680f58f68bSGerd Hoffmann
usb_uas_get_lun(uint64_t lun64)4690f58f68bSGerd Hoffmann static int usb_uas_get_lun(uint64_t lun64)
4700f58f68bSGerd Hoffmann {
4710f58f68bSGerd Hoffmann return (lun64 >> 48) & 0xff;
4720f58f68bSGerd Hoffmann }
4730f58f68bSGerd Hoffmann
usb_uas_get_dev(UASDevice * uas,uint64_t lun64)4740f58f68bSGerd Hoffmann static SCSIDevice *usb_uas_get_dev(UASDevice *uas, uint64_t lun64)
4750f58f68bSGerd Hoffmann {
4760f58f68bSGerd Hoffmann if ((lun64 >> 56) != 0x00) {
4770f58f68bSGerd Hoffmann return NULL;
4780f58f68bSGerd Hoffmann }
4790f58f68bSGerd Hoffmann return scsi_device_find(&uas->bus, 0, 0, usb_uas_get_lun(lun64));
4800f58f68bSGerd Hoffmann }
4810f58f68bSGerd Hoffmann
usb_uas_complete_data_packet(UASRequest * req)4820f58f68bSGerd Hoffmann static void usb_uas_complete_data_packet(UASRequest *req)
4830f58f68bSGerd Hoffmann {
4840f58f68bSGerd Hoffmann USBPacket *p;
4850f58f68bSGerd Hoffmann
4860f58f68bSGerd Hoffmann if (!req->data_async) {
4870f58f68bSGerd Hoffmann return;
4880f58f68bSGerd Hoffmann }
4890f58f68bSGerd Hoffmann p = req->data;
4900f58f68bSGerd Hoffmann req->data = NULL;
4910f58f68bSGerd Hoffmann req->data_async = false;
4929a77a0f5SHans de Goede p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */
4930f58f68bSGerd Hoffmann usb_packet_complete(&req->uas->dev, p);
4940f58f68bSGerd Hoffmann }
4950f58f68bSGerd Hoffmann
usb_uas_copy_data(UASRequest * req)4960f58f68bSGerd Hoffmann static void usb_uas_copy_data(UASRequest *req)
4970f58f68bSGerd Hoffmann {
4980f58f68bSGerd Hoffmann uint32_t length;
4990f58f68bSGerd Hoffmann
5000f58f68bSGerd Hoffmann length = MIN(req->buf_size - req->buf_off,
5019a77a0f5SHans de Goede req->data->iov.size - req->data->actual_length);
5020f58f68bSGerd Hoffmann trace_usb_uas_xfer_data(req->uas->dev.addr, req->tag, length,
5039a77a0f5SHans de Goede req->data->actual_length, req->data->iov.size,
5040f58f68bSGerd Hoffmann req->buf_off, req->buf_size);
5050f58f68bSGerd Hoffmann usb_packet_copy(req->data, scsi_req_get_buf(req->req) + req->buf_off,
5060f58f68bSGerd Hoffmann length);
5070f58f68bSGerd Hoffmann req->buf_off += length;
5080f58f68bSGerd Hoffmann req->data_off += length;
5090f58f68bSGerd Hoffmann
5109a77a0f5SHans de Goede if (req->data->actual_length == req->data->iov.size) {
5110f58f68bSGerd Hoffmann usb_uas_complete_data_packet(req);
5120f58f68bSGerd Hoffmann }
5130f58f68bSGerd Hoffmann if (req->buf_size && req->buf_off == req->buf_size) {
5140f58f68bSGerd Hoffmann req->buf_off = 0;
5150f58f68bSGerd Hoffmann req->buf_size = 0;
5160f58f68bSGerd Hoffmann scsi_req_continue(req->req);
5170f58f68bSGerd Hoffmann }
5180f58f68bSGerd Hoffmann }
5190f58f68bSGerd Hoffmann
usb_uas_start_next_transfer(UASDevice * uas)5200f58f68bSGerd Hoffmann static void usb_uas_start_next_transfer(UASDevice *uas)
5210f58f68bSGerd Hoffmann {
5220f58f68bSGerd Hoffmann UASRequest *req;
5230f58f68bSGerd Hoffmann
52489a453d4SGerd Hoffmann if (uas_using_streams(uas)) {
52589a453d4SGerd Hoffmann return;
52689a453d4SGerd Hoffmann }
52789a453d4SGerd Hoffmann
5280f58f68bSGerd Hoffmann QTAILQ_FOREACH(req, &uas->requests, next) {
5290f58f68bSGerd Hoffmann if (req->active || req->complete) {
5300f58f68bSGerd Hoffmann continue;
5310f58f68bSGerd Hoffmann }
53289a453d4SGerd Hoffmann if (req->req->cmd.mode == SCSI_XFER_FROM_DEV && uas->datain2 == NULL) {
53389a453d4SGerd Hoffmann uas->datain2 = req;
5340f58f68bSGerd Hoffmann usb_uas_queue_read_ready(req);
5350f58f68bSGerd Hoffmann req->active = true;
5360f58f68bSGerd Hoffmann return;
5370f58f68bSGerd Hoffmann }
53889a453d4SGerd Hoffmann if (req->req->cmd.mode == SCSI_XFER_TO_DEV && uas->dataout2 == NULL) {
53989a453d4SGerd Hoffmann uas->dataout2 = req;
5400f58f68bSGerd Hoffmann usb_uas_queue_write_ready(req);
5410f58f68bSGerd Hoffmann req->active = true;
5420f58f68bSGerd Hoffmann return;
5430f58f68bSGerd Hoffmann }
5440f58f68bSGerd Hoffmann }
5450f58f68bSGerd Hoffmann }
5460f58f68bSGerd Hoffmann
usb_uas_alloc_request(UASDevice * uas,uas_iu * iu)5475007c940SHans de Goede static UASRequest *usb_uas_alloc_request(UASDevice *uas, uas_iu *iu)
5480f58f68bSGerd Hoffmann {
5490f58f68bSGerd Hoffmann UASRequest *req;
5500f58f68bSGerd Hoffmann
5510f58f68bSGerd Hoffmann req = g_new0(UASRequest, 1);
5520f58f68bSGerd Hoffmann req->uas = uas;
5535007c940SHans de Goede req->tag = be16_to_cpu(iu->hdr.tag);
5545007c940SHans de Goede req->lun = be64_to_cpu(iu->command.lun);
5550f58f68bSGerd Hoffmann req->dev = usb_uas_get_dev(req->uas, req->lun);
5560f58f68bSGerd Hoffmann return req;
5570f58f68bSGerd Hoffmann }
5580f58f68bSGerd Hoffmann
usb_uas_scsi_free_request(SCSIBus * bus,void * priv)5590f58f68bSGerd Hoffmann static void usb_uas_scsi_free_request(SCSIBus *bus, void *priv)
5600f58f68bSGerd Hoffmann {
5610f58f68bSGerd Hoffmann UASRequest *req = priv;
5620f58f68bSGerd Hoffmann UASDevice *uas = req->uas;
5630f58f68bSGerd Hoffmann
56489a453d4SGerd Hoffmann if (req == uas->datain2) {
56589a453d4SGerd Hoffmann uas->datain2 = NULL;
5660f58f68bSGerd Hoffmann }
56789a453d4SGerd Hoffmann if (req == uas->dataout2) {
56889a453d4SGerd Hoffmann uas->dataout2 = NULL;
5690f58f68bSGerd Hoffmann }
5700f58f68bSGerd Hoffmann QTAILQ_REMOVE(&uas->requests, req, next);
5710f58f68bSGerd Hoffmann g_free(req);
572347e40ffSGerd Hoffmann usb_uas_start_next_transfer(uas);
5730f58f68bSGerd Hoffmann }
5740f58f68bSGerd Hoffmann
usb_uas_find_request(UASDevice * uas,uint16_t tag)5750f58f68bSGerd Hoffmann static UASRequest *usb_uas_find_request(UASDevice *uas, uint16_t tag)
5760f58f68bSGerd Hoffmann {
5770f58f68bSGerd Hoffmann UASRequest *req;
5780f58f68bSGerd Hoffmann
5790f58f68bSGerd Hoffmann QTAILQ_FOREACH(req, &uas->requests, next) {
5800f58f68bSGerd Hoffmann if (req->tag == tag) {
5810f58f68bSGerd Hoffmann return req;
5820f58f68bSGerd Hoffmann }
5830f58f68bSGerd Hoffmann }
5840f58f68bSGerd Hoffmann return NULL;
5850f58f68bSGerd Hoffmann }
5860f58f68bSGerd Hoffmann
usb_uas_scsi_transfer_data(SCSIRequest * r,uint32_t len)5870f58f68bSGerd Hoffmann static void usb_uas_scsi_transfer_data(SCSIRequest *r, uint32_t len)
5880f58f68bSGerd Hoffmann {
5890f58f68bSGerd Hoffmann UASRequest *req = r->hba_private;
5900f58f68bSGerd Hoffmann
5910f58f68bSGerd Hoffmann trace_usb_uas_scsi_data(req->uas->dev.addr, req->tag, len);
5920f58f68bSGerd Hoffmann req->buf_off = 0;
5930f58f68bSGerd Hoffmann req->buf_size = len;
5940f58f68bSGerd Hoffmann if (req->data) {
5950f58f68bSGerd Hoffmann usb_uas_copy_data(req);
5960f58f68bSGerd Hoffmann } else {
5970f58f68bSGerd Hoffmann usb_uas_start_next_transfer(req->uas);
5980f58f68bSGerd Hoffmann }
5990f58f68bSGerd Hoffmann }
6000f58f68bSGerd Hoffmann
usb_uas_scsi_command_complete(SCSIRequest * r,size_t resid)60117ea26c2SHannes Reinecke static void usb_uas_scsi_command_complete(SCSIRequest *r, size_t resid)
6020f58f68bSGerd Hoffmann {
6030f58f68bSGerd Hoffmann UASRequest *req = r->hba_private;
6040f58f68bSGerd Hoffmann
60517ea26c2SHannes Reinecke trace_usb_uas_scsi_complete(req->uas->dev.addr, req->tag, r->status, resid);
6060f58f68bSGerd Hoffmann req->complete = true;
6070f58f68bSGerd Hoffmann if (req->data) {
6080f58f68bSGerd Hoffmann usb_uas_complete_data_packet(req);
6090f58f68bSGerd Hoffmann }
61017ea26c2SHannes Reinecke usb_uas_queue_sense(req, r->status);
6110f58f68bSGerd Hoffmann scsi_req_unref(req->req);
6120f58f68bSGerd Hoffmann }
6130f58f68bSGerd Hoffmann
usb_uas_scsi_request_cancelled(SCSIRequest * r)6140f58f68bSGerd Hoffmann static void usb_uas_scsi_request_cancelled(SCSIRequest *r)
6150f58f68bSGerd Hoffmann {
6160f58f68bSGerd Hoffmann UASRequest *req = r->hba_private;
6170f58f68bSGerd Hoffmann
6180f58f68bSGerd Hoffmann /* FIXME: queue notification to status pipe? */
6190f58f68bSGerd Hoffmann scsi_req_unref(req->req);
6200f58f68bSGerd Hoffmann }
6210f58f68bSGerd Hoffmann
6220f58f68bSGerd Hoffmann static const struct SCSIBusInfo usb_uas_scsi_info = {
6230f58f68bSGerd Hoffmann .tcq = true,
6240f58f68bSGerd Hoffmann .max_target = 0,
6250f58f68bSGerd Hoffmann .max_lun = 255,
6260f58f68bSGerd Hoffmann
6270f58f68bSGerd Hoffmann .transfer_data = usb_uas_scsi_transfer_data,
6280f58f68bSGerd Hoffmann .complete = usb_uas_scsi_command_complete,
6290f58f68bSGerd Hoffmann .cancel = usb_uas_scsi_request_cancelled,
6300f58f68bSGerd Hoffmann .free_request = usb_uas_scsi_free_request,
6310f58f68bSGerd Hoffmann };
6320f58f68bSGerd Hoffmann
6330f58f68bSGerd Hoffmann /* --------------------------------------------------------------------- */
6340f58f68bSGerd Hoffmann
usb_uas_handle_reset(USBDevice * dev)6350f58f68bSGerd Hoffmann static void usb_uas_handle_reset(USBDevice *dev)
6360f58f68bSGerd Hoffmann {
6370b06d099SGonglei UASDevice *uas = USB_UAS(dev);
6380f58f68bSGerd Hoffmann UASRequest *req, *nreq;
6390f58f68bSGerd Hoffmann UASStatus *st, *nst;
6400f58f68bSGerd Hoffmann
6410f58f68bSGerd Hoffmann trace_usb_uas_reset(dev->addr);
6420f58f68bSGerd Hoffmann QTAILQ_FOREACH_SAFE(req, &uas->requests, next, nreq) {
6430f58f68bSGerd Hoffmann scsi_req_cancel(req->req);
6440f58f68bSGerd Hoffmann }
6450f58f68bSGerd Hoffmann QTAILQ_FOREACH_SAFE(st, &uas->results, next, nst) {
6460f58f68bSGerd Hoffmann QTAILQ_REMOVE(&uas->results, st, next);
6470f58f68bSGerd Hoffmann g_free(st);
6480f58f68bSGerd Hoffmann }
6490f58f68bSGerd Hoffmann }
6500f58f68bSGerd Hoffmann
usb_uas_handle_control(USBDevice * dev,USBPacket * p,int request,int value,int index,int length,uint8_t * data)6519a77a0f5SHans de Goede static void usb_uas_handle_control(USBDevice *dev, USBPacket *p,
6520f58f68bSGerd Hoffmann int request, int value, int index, int length, uint8_t *data)
6530f58f68bSGerd Hoffmann {
6540f58f68bSGerd Hoffmann int ret;
6550f58f68bSGerd Hoffmann
6560f58f68bSGerd Hoffmann ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
6570f58f68bSGerd Hoffmann if (ret >= 0) {
6589a77a0f5SHans de Goede return;
6590f58f68bSGerd Hoffmann }
660e306b2fdSGerd Hoffmann error_report("%s: unhandled control request (req 0x%x, val 0x%x, idx 0x%x",
661e306b2fdSGerd Hoffmann __func__, request, value, index);
6629a77a0f5SHans de Goede p->status = USB_RET_STALL;
6630f58f68bSGerd Hoffmann }
6640f58f68bSGerd Hoffmann
usb_uas_cancel_io(USBDevice * dev,USBPacket * p)6650f58f68bSGerd Hoffmann static void usb_uas_cancel_io(USBDevice *dev, USBPacket *p)
6660f58f68bSGerd Hoffmann {
6670b06d099SGonglei UASDevice *uas = USB_UAS(dev);
6680f58f68bSGerd Hoffmann UASRequest *req, *nreq;
66989a453d4SGerd Hoffmann int i;
6700f58f68bSGerd Hoffmann
67189a453d4SGerd Hoffmann if (uas->status2 == p) {
67289a453d4SGerd Hoffmann uas->status2 = NULL;
6730f58f68bSGerd Hoffmann qemu_bh_cancel(uas->status_bh);
6740f58f68bSGerd Hoffmann return;
6750f58f68bSGerd Hoffmann }
67689a453d4SGerd Hoffmann if (uas_using_streams(uas)) {
6770478661eSHans de Goede for (i = 0; i <= UAS_MAX_STREAMS; i++) {
67889a453d4SGerd Hoffmann if (uas->status3[i] == p) {
67989a453d4SGerd Hoffmann uas->status3[i] = NULL;
68089a453d4SGerd Hoffmann return;
68189a453d4SGerd Hoffmann }
68289a453d4SGerd Hoffmann if (uas->data3[i] == p) {
68389a453d4SGerd Hoffmann uas->data3[i] = NULL;
68489a453d4SGerd Hoffmann return;
68589a453d4SGerd Hoffmann }
68689a453d4SGerd Hoffmann }
68789a453d4SGerd Hoffmann }
6880f58f68bSGerd Hoffmann QTAILQ_FOREACH_SAFE(req, &uas->requests, next, nreq) {
6890f58f68bSGerd Hoffmann if (req->data == p) {
6900f58f68bSGerd Hoffmann req->data = NULL;
6910f58f68bSGerd Hoffmann return;
6920f58f68bSGerd Hoffmann }
6930f58f68bSGerd Hoffmann }
6940f58f68bSGerd Hoffmann assert(!"canceled usb packet not found");
6950f58f68bSGerd Hoffmann }
6960f58f68bSGerd Hoffmann
usb_uas_command(UASDevice * uas,uas_iu * iu)6975007c940SHans de Goede static void usb_uas_command(UASDevice *uas, uas_iu *iu)
6980f58f68bSGerd Hoffmann {
6990f58f68bSGerd Hoffmann UASRequest *req;
7000f58f68bSGerd Hoffmann uint32_t len;
7015007c940SHans de Goede uint16_t tag = be16_to_cpu(iu->hdr.tag);
702fe9d8927SJohn Millikin size_t cdb_len = sizeof(iu->command.cdb) + iu->command.add_cdb_length;
7030f58f68bSGerd Hoffmann
704d755cb96SPhilippe Mathieu-Daudé if (iu->command.add_cdb_length > 0) {
705d755cb96SPhilippe Mathieu-Daudé qemu_log_mask(LOG_UNIMP, "additional adb length not yet supported\n");
706d755cb96SPhilippe Mathieu-Daudé goto unsupported_len;
707d755cb96SPhilippe Mathieu-Daudé }
708d755cb96SPhilippe Mathieu-Daudé
7093453f9a0SHans de Goede if (uas_using_streams(uas) && tag > UAS_MAX_STREAMS) {
7103453f9a0SHans de Goede goto invalid_tag;
7113453f9a0SHans de Goede }
712d4bfc7b9SHans de Goede req = usb_uas_find_request(uas, tag);
7130f58f68bSGerd Hoffmann if (req) {
7140f58f68bSGerd Hoffmann goto overlapped_tag;
7150f58f68bSGerd Hoffmann }
7165007c940SHans de Goede req = usb_uas_alloc_request(uas, iu);
7170f58f68bSGerd Hoffmann if (req->dev == NULL) {
7180f58f68bSGerd Hoffmann goto bad_target;
7190f58f68bSGerd Hoffmann }
7200f58f68bSGerd Hoffmann
7210f58f68bSGerd Hoffmann trace_usb_uas_command(uas->dev.addr, req->tag,
7220f58f68bSGerd Hoffmann usb_uas_get_lun(req->lun),
7230f58f68bSGerd Hoffmann req->lun >> 32, req->lun & 0xffffffff);
7240f58f68bSGerd Hoffmann QTAILQ_INSERT_TAIL(&uas->requests, req, next);
72589a453d4SGerd Hoffmann if (uas_using_streams(uas) && uas->data3[req->tag] != NULL) {
72689a453d4SGerd Hoffmann req->data = uas->data3[req->tag];
72789a453d4SGerd Hoffmann req->data_async = true;
72889a453d4SGerd Hoffmann uas->data3[req->tag] = NULL;
72989a453d4SGerd Hoffmann }
73089a453d4SGerd Hoffmann
7310f58f68bSGerd Hoffmann req->req = scsi_req_new(req->dev, req->tag,
7320f58f68bSGerd Hoffmann usb_uas_get_lun(req->lun),
733fe9d8927SJohn Millikin iu->command.cdb, cdb_len, req);
7341556a8fcSGerd Hoffmann if (uas->requestlog) {
73589a453d4SGerd Hoffmann scsi_req_print(req->req);
7361556a8fcSGerd Hoffmann }
7370f58f68bSGerd Hoffmann len = scsi_req_enqueue(req->req);
7380f58f68bSGerd Hoffmann if (len) {
7390f58f68bSGerd Hoffmann req->data_size = len;
7400f58f68bSGerd Hoffmann scsi_req_continue(req->req);
7410f58f68bSGerd Hoffmann }
7420f58f68bSGerd Hoffmann return;
7430f58f68bSGerd Hoffmann
744d755cb96SPhilippe Mathieu-Daudé unsupported_len:
745d755cb96SPhilippe Mathieu-Daudé usb_uas_queue_fake_sense(uas, tag, sense_code_INVALID_PARAM_VALUE);
746d755cb96SPhilippe Mathieu-Daudé return;
747d755cb96SPhilippe Mathieu-Daudé
7483453f9a0SHans de Goede invalid_tag:
7493453f9a0SHans de Goede usb_uas_queue_fake_sense(uas, tag, sense_code_INVALID_TAG);
7503453f9a0SHans de Goede return;
7513453f9a0SHans de Goede
7520f58f68bSGerd Hoffmann overlapped_tag:
753d4bfc7b9SHans de Goede usb_uas_queue_fake_sense(uas, tag, sense_code_OVERLAPPED_COMMANDS);
7540f58f68bSGerd Hoffmann return;
7550f58f68bSGerd Hoffmann
7560f58f68bSGerd Hoffmann bad_target:
757d4bfc7b9SHans de Goede usb_uas_queue_fake_sense(uas, tag, sense_code_LUN_NOT_SUPPORTED);
7580f58f68bSGerd Hoffmann g_free(req);
7590f58f68bSGerd Hoffmann }
7600f58f68bSGerd Hoffmann
usb_uas_task(UASDevice * uas,uas_iu * iu)7615007c940SHans de Goede static void usb_uas_task(UASDevice *uas, uas_iu *iu)
7620f58f68bSGerd Hoffmann {
7635007c940SHans de Goede uint16_t tag = be16_to_cpu(iu->hdr.tag);
7645007c940SHans de Goede uint64_t lun64 = be64_to_cpu(iu->task.lun);
7650f58f68bSGerd Hoffmann SCSIDevice *dev = usb_uas_get_dev(uas, lun64);
7660f58f68bSGerd Hoffmann int lun = usb_uas_get_lun(lun64);
7670f58f68bSGerd Hoffmann UASRequest *req;
7680f58f68bSGerd Hoffmann uint16_t task_tag;
7690f58f68bSGerd Hoffmann
7703453f9a0SHans de Goede if (uas_using_streams(uas) && tag > UAS_MAX_STREAMS) {
7713453f9a0SHans de Goede goto invalid_tag;
7723453f9a0SHans de Goede }
7735007c940SHans de Goede req = usb_uas_find_request(uas, be16_to_cpu(iu->hdr.tag));
7740f58f68bSGerd Hoffmann if (req) {
7750f58f68bSGerd Hoffmann goto overlapped_tag;
7760f58f68bSGerd Hoffmann }
7775eb6d9e3SHans de Goede if (dev == NULL) {
7785eb6d9e3SHans de Goede goto incorrect_lun;
7795eb6d9e3SHans de Goede }
7800f58f68bSGerd Hoffmann
7815007c940SHans de Goede switch (iu->task.function) {
7820f58f68bSGerd Hoffmann case UAS_TMF_ABORT_TASK:
7835007c940SHans de Goede task_tag = be16_to_cpu(iu->task.task_tag);
7840f58f68bSGerd Hoffmann trace_usb_uas_tmf_abort_task(uas->dev.addr, tag, task_tag);
7850f58f68bSGerd Hoffmann req = usb_uas_find_request(uas, task_tag);
7860f58f68bSGerd Hoffmann if (req && req->dev == dev) {
7870f58f68bSGerd Hoffmann scsi_req_cancel(req->req);
7880f58f68bSGerd Hoffmann }
78949cfa2fdSHans de Goede usb_uas_queue_response(uas, tag, UAS_RC_TMF_COMPLETE);
7900f58f68bSGerd Hoffmann break;
7910f58f68bSGerd Hoffmann
7920f58f68bSGerd Hoffmann case UAS_TMF_LOGICAL_UNIT_RESET:
7930f58f68bSGerd Hoffmann trace_usb_uas_tmf_logical_unit_reset(uas->dev.addr, tag, lun);
794dfa6ba6bSPeter Maydell device_cold_reset(&dev->qdev);
79549cfa2fdSHans de Goede usb_uas_queue_response(uas, tag, UAS_RC_TMF_COMPLETE);
7960f58f68bSGerd Hoffmann break;
7970f58f68bSGerd Hoffmann
7980f58f68bSGerd Hoffmann default:
7995007c940SHans de Goede trace_usb_uas_tmf_unsupported(uas->dev.addr, tag, iu->task.function);
80049cfa2fdSHans de Goede usb_uas_queue_response(uas, tag, UAS_RC_TMF_NOT_SUPPORTED);
8010f58f68bSGerd Hoffmann break;
8020f58f68bSGerd Hoffmann }
8030f58f68bSGerd Hoffmann return;
8040f58f68bSGerd Hoffmann
8053453f9a0SHans de Goede invalid_tag:
80649cfa2fdSHans de Goede usb_uas_queue_response(uas, tag, UAS_RC_INVALID_INFO_UNIT);
8073453f9a0SHans de Goede return;
8083453f9a0SHans de Goede
8090f58f68bSGerd Hoffmann overlapped_tag:
81049cfa2fdSHans de Goede usb_uas_queue_response(uas, req->tag, UAS_RC_OVERLAPPED_TAG);
8110f58f68bSGerd Hoffmann return;
8120f58f68bSGerd Hoffmann
8130f58f68bSGerd Hoffmann incorrect_lun:
81449cfa2fdSHans de Goede usb_uas_queue_response(uas, tag, UAS_RC_INCORRECT_LUN);
8150f58f68bSGerd Hoffmann }
8160f58f68bSGerd Hoffmann
usb_uas_handle_data(USBDevice * dev,USBPacket * p)8179a77a0f5SHans de Goede static void usb_uas_handle_data(USBDevice *dev, USBPacket *p)
8180f58f68bSGerd Hoffmann {
8190b06d099SGonglei UASDevice *uas = USB_UAS(dev);
8205007c940SHans de Goede uas_iu iu;
8210f58f68bSGerd Hoffmann UASStatus *st;
8220f58f68bSGerd Hoffmann UASRequest *req;
8239a77a0f5SHans de Goede int length;
8240f58f68bSGerd Hoffmann
8250f58f68bSGerd Hoffmann switch (p->ep->nr) {
8260f58f68bSGerd Hoffmann case UAS_PIPE_ID_COMMAND:
8275007c940SHans de Goede length = MIN(sizeof(iu), p->iov.size);
8285007c940SHans de Goede usb_packet_copy(p, &iu, length);
8295007c940SHans de Goede switch (iu.hdr.id) {
8300f58f68bSGerd Hoffmann case UAS_UI_COMMAND:
8315007c940SHans de Goede usb_uas_command(uas, &iu);
8320f58f68bSGerd Hoffmann break;
8330f58f68bSGerd Hoffmann case UAS_UI_TASK_MGMT:
8345007c940SHans de Goede usb_uas_task(uas, &iu);
8350f58f68bSGerd Hoffmann break;
8360f58f68bSGerd Hoffmann default:
8376b7afb7fSGonglei error_report("%s: unknown command iu: id 0x%x",
8385007c940SHans de Goede __func__, iu.hdr.id);
8399a77a0f5SHans de Goede p->status = USB_RET_STALL;
8400f58f68bSGerd Hoffmann break;
8410f58f68bSGerd Hoffmann }
8420f58f68bSGerd Hoffmann break;
8430f58f68bSGerd Hoffmann case UAS_PIPE_ID_STATUS:
84413b250b1SGerd Hoffmann if (p->stream > UAS_MAX_STREAMS) {
84513b250b1SGerd Hoffmann goto err_stream;
84613b250b1SGerd Hoffmann }
84789a453d4SGerd Hoffmann if (p->stream) {
84889a453d4SGerd Hoffmann QTAILQ_FOREACH(st, &uas->results, next) {
84989a453d4SGerd Hoffmann if (st->stream == p->stream) {
85089a453d4SGerd Hoffmann break;
85189a453d4SGerd Hoffmann }
85289a453d4SGerd Hoffmann }
8530f58f68bSGerd Hoffmann if (st == NULL) {
85489a453d4SGerd Hoffmann assert(uas->status3[p->stream] == NULL);
85589a453d4SGerd Hoffmann uas->status3[p->stream] = p;
8569a77a0f5SHans de Goede p->status = USB_RET_ASYNC;
8570f58f68bSGerd Hoffmann break;
8580f58f68bSGerd Hoffmann }
85989a453d4SGerd Hoffmann } else {
86089a453d4SGerd Hoffmann st = QTAILQ_FIRST(&uas->results);
86189a453d4SGerd Hoffmann if (st == NULL) {
86289a453d4SGerd Hoffmann assert(uas->status2 == NULL);
86389a453d4SGerd Hoffmann uas->status2 = p;
86489a453d4SGerd Hoffmann p->status = USB_RET_ASYNC;
86589a453d4SGerd Hoffmann break;
86689a453d4SGerd Hoffmann }
86789a453d4SGerd Hoffmann }
8680f58f68bSGerd Hoffmann usb_packet_copy(p, &st->status, st->length);
8690f58f68bSGerd Hoffmann QTAILQ_REMOVE(&uas->results, st, next);
8700f58f68bSGerd Hoffmann g_free(st);
8710f58f68bSGerd Hoffmann break;
8720f58f68bSGerd Hoffmann case UAS_PIPE_ID_DATA_IN:
8730f58f68bSGerd Hoffmann case UAS_PIPE_ID_DATA_OUT:
87413b250b1SGerd Hoffmann if (p->stream > UAS_MAX_STREAMS) {
87513b250b1SGerd Hoffmann goto err_stream;
87613b250b1SGerd Hoffmann }
87789a453d4SGerd Hoffmann if (p->stream) {
87889a453d4SGerd Hoffmann req = usb_uas_find_request(uas, p->stream);
87989a453d4SGerd Hoffmann } else {
88089a453d4SGerd Hoffmann req = (p->ep->nr == UAS_PIPE_ID_DATA_IN)
88189a453d4SGerd Hoffmann ? uas->datain2 : uas->dataout2;
88289a453d4SGerd Hoffmann }
8830f58f68bSGerd Hoffmann if (req == NULL) {
88489a453d4SGerd Hoffmann if (p->stream) {
88589a453d4SGerd Hoffmann assert(uas->data3[p->stream] == NULL);
88689a453d4SGerd Hoffmann uas->data3[p->stream] = p;
88789a453d4SGerd Hoffmann p->status = USB_RET_ASYNC;
88889a453d4SGerd Hoffmann break;
88989a453d4SGerd Hoffmann } else {
8906b7afb7fSGonglei error_report("%s: no inflight request", __func__);
8919a77a0f5SHans de Goede p->status = USB_RET_STALL;
8920f58f68bSGerd Hoffmann break;
8930f58f68bSGerd Hoffmann }
89489a453d4SGerd Hoffmann }
8950f58f68bSGerd Hoffmann scsi_req_ref(req->req);
8960f58f68bSGerd Hoffmann req->data = p;
8970f58f68bSGerd Hoffmann usb_uas_copy_data(req);
8989a77a0f5SHans de Goede if (p->actual_length == p->iov.size || req->complete) {
8990f58f68bSGerd Hoffmann req->data = NULL;
9000f58f68bSGerd Hoffmann } else {
9010f58f68bSGerd Hoffmann req->data_async = true;
9029a77a0f5SHans de Goede p->status = USB_RET_ASYNC;
9030f58f68bSGerd Hoffmann }
9040f58f68bSGerd Hoffmann scsi_req_unref(req->req);
9050f58f68bSGerd Hoffmann usb_uas_start_next_transfer(uas);
9060f58f68bSGerd Hoffmann break;
9070f58f68bSGerd Hoffmann default:
9086b7afb7fSGonglei error_report("%s: invalid endpoint %d", __func__, p->ep->nr);
9099a77a0f5SHans de Goede p->status = USB_RET_STALL;
9100f58f68bSGerd Hoffmann break;
9110f58f68bSGerd Hoffmann }
9121c6c0b9eSGerd Hoffmann return;
91313b250b1SGerd Hoffmann
91413b250b1SGerd Hoffmann err_stream:
91513b250b1SGerd Hoffmann error_report("%s: invalid stream %d", __func__, p->stream);
91613b250b1SGerd Hoffmann p->status = USB_RET_STALL;
91713b250b1SGerd Hoffmann return;
9180f58f68bSGerd Hoffmann }
9190f58f68bSGerd Hoffmann
usb_uas_unrealize(USBDevice * dev)920b69c3c21SMarkus Armbruster static void usb_uas_unrealize(USBDevice *dev)
9210f58f68bSGerd Hoffmann {
9220b06d099SGonglei UASDevice *uas = USB_UAS(dev);
9230f58f68bSGerd Hoffmann
9240f58f68bSGerd Hoffmann qemu_bh_delete(uas->status_bh);
9250f58f68bSGerd Hoffmann }
9260f58f68bSGerd Hoffmann
usb_uas_realize(USBDevice * dev,Error ** errp)927b89dc7e3SGonglei static void usb_uas_realize(USBDevice *dev, Error **errp)
9280f58f68bSGerd Hoffmann {
9290b06d099SGonglei UASDevice *uas = USB_UAS(dev);
9300d4cf3e7SGerd Hoffmann DeviceState *d = DEVICE(dev);
9310f58f68bSGerd Hoffmann
9320f58f68bSGerd Hoffmann usb_desc_create_serial(dev);
9330f58f68bSGerd Hoffmann usb_desc_init(dev);
9340d4cf3e7SGerd Hoffmann if (d->hotplugged) {
9350d4cf3e7SGerd Hoffmann uas->dev.auto_attach = 0;
9360d4cf3e7SGerd Hoffmann }
9370f58f68bSGerd Hoffmann
9380f58f68bSGerd Hoffmann QTAILQ_INIT(&uas->results);
9390f58f68bSGerd Hoffmann QTAILQ_INIT(&uas->requests);
940f63192b0SAlexander Bulekov uas->status_bh = qemu_bh_new_guarded(usb_uas_send_status_bh, uas,
941f63192b0SAlexander Bulekov &d->mem_reentrancy_guard);
9420f58f68bSGerd Hoffmann
943b7b2a60bSGerd Hoffmann dev->flags |= (1 << USB_DEV_FLAG_IS_SCSI_STORAGE);
944739e95f5SPeter Maydell scsi_bus_init(&uas->bus, sizeof(uas->bus), DEVICE(dev), &usb_uas_scsi_info);
9450f58f68bSGerd Hoffmann }
9460f58f68bSGerd Hoffmann
9470f58f68bSGerd Hoffmann static const VMStateDescription vmstate_usb_uas = {
9480f58f68bSGerd Hoffmann .name = "usb-uas",
9490f58f68bSGerd Hoffmann .unmigratable = 1,
950*3abedf29SRichard Henderson .fields = (const VMStateField[]) {
9510f58f68bSGerd Hoffmann VMSTATE_USB_DEVICE(dev, UASDevice),
9520f58f68bSGerd Hoffmann VMSTATE_END_OF_LIST()
9530f58f68bSGerd Hoffmann }
9540f58f68bSGerd Hoffmann };
9550f58f68bSGerd Hoffmann
9561556a8fcSGerd Hoffmann static Property uas_properties[] = {
9571556a8fcSGerd Hoffmann DEFINE_PROP_UINT32("log-scsi-req", UASDevice, requestlog, 0),
9581556a8fcSGerd Hoffmann DEFINE_PROP_END_OF_LIST(),
9591556a8fcSGerd Hoffmann };
9601556a8fcSGerd Hoffmann
usb_uas_class_initfn(ObjectClass * klass,void * data)9610f58f68bSGerd Hoffmann static void usb_uas_class_initfn(ObjectClass *klass, void *data)
9620f58f68bSGerd Hoffmann {
9630f58f68bSGerd Hoffmann DeviceClass *dc = DEVICE_CLASS(klass);
9640f58f68bSGerd Hoffmann USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
9650f58f68bSGerd Hoffmann
966b89dc7e3SGonglei uc->realize = usb_uas_realize;
9670f58f68bSGerd Hoffmann uc->product_desc = desc_strings[STR_PRODUCT];
9680f58f68bSGerd Hoffmann uc->usb_desc = &desc;
9690f58f68bSGerd Hoffmann uc->cancel_packet = usb_uas_cancel_io;
9700f58f68bSGerd Hoffmann uc->handle_attach = usb_desc_attach;
9710f58f68bSGerd Hoffmann uc->handle_reset = usb_uas_handle_reset;
9720f58f68bSGerd Hoffmann uc->handle_control = usb_uas_handle_control;
9730f58f68bSGerd Hoffmann uc->handle_data = usb_uas_handle_data;
974c4fe9700SMarc-André Lureau uc->unrealize = usb_uas_unrealize;
9750d4cf3e7SGerd Hoffmann uc->attached_settable = true;
976125ee0edSMarcel Apfelbaum set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
9770f58f68bSGerd Hoffmann dc->fw_name = "storage";
9780f58f68bSGerd Hoffmann dc->vmsd = &vmstate_usb_uas;
9794f67d30bSMarc-André Lureau device_class_set_props(dc, uas_properties);
9800f58f68bSGerd Hoffmann }
9810f58f68bSGerd Hoffmann
9828c43a6f0SAndreas Färber static const TypeInfo uas_info = {
9830b06d099SGonglei .name = TYPE_USB_UAS,
9840f58f68bSGerd Hoffmann .parent = TYPE_USB_DEVICE,
9850f58f68bSGerd Hoffmann .instance_size = sizeof(UASDevice),
9860f58f68bSGerd Hoffmann .class_init = usb_uas_class_initfn,
9870f58f68bSGerd Hoffmann };
9880f58f68bSGerd Hoffmann
usb_uas_register_types(void)9890f58f68bSGerd Hoffmann static void usb_uas_register_types(void)
9900f58f68bSGerd Hoffmann {
9910f58f68bSGerd Hoffmann type_register_static(&uas_info);
9920f58f68bSGerd Hoffmann }
9930f58f68bSGerd Hoffmann
9940f58f68bSGerd Hoffmann type_init(usb_uas_register_types)
995