Statistics
| Branch: | Revision:

root / hw / usb / dev-uas.c @ f487b677

History | View | Annotate | Download (26.9 kB)

1 0f58f68b Gerd Hoffmann
/*
2 0f58f68b Gerd Hoffmann
 * UAS (USB Attached SCSI) emulation
3 0f58f68b Gerd Hoffmann
 *
4 0f58f68b Gerd Hoffmann
 * Copyright Red Hat, Inc. 2012
5 0f58f68b Gerd Hoffmann
 *
6 0f58f68b Gerd Hoffmann
 * Author: Gerd Hoffmann <kraxel@redhat.com>
7 0f58f68b Gerd Hoffmann
 *
8 0f58f68b Gerd Hoffmann
 * This work is licensed under the terms of the GNU GPL, version 2 or later.
9 0f58f68b Gerd Hoffmann
 * See the COPYING file in the top-level directory.
10 0f58f68b Gerd Hoffmann
 */
11 0f58f68b Gerd Hoffmann
12 0f58f68b Gerd Hoffmann
#include "qemu-common.h"
13 1de7afc9 Paolo Bonzini
#include "qemu/option.h"
14 1de7afc9 Paolo Bonzini
#include "qemu/config-file.h"
15 0f58f68b Gerd Hoffmann
#include "trace.h"
16 0f58f68b Gerd Hoffmann
17 0f58f68b Gerd Hoffmann
#include "hw/usb.h"
18 0f58f68b Gerd Hoffmann
#include "hw/usb/desc.h"
19 0d09e41a Paolo Bonzini
#include "hw/scsi/scsi.h"
20 0d09e41a Paolo Bonzini
#include "block/scsi.h"
21 0f58f68b Gerd Hoffmann
22 0f58f68b Gerd Hoffmann
/* --------------------------------------------------------------------- */
23 0f58f68b Gerd Hoffmann
24 0f58f68b Gerd Hoffmann
#define UAS_UI_COMMAND              0x01
25 0f58f68b Gerd Hoffmann
#define UAS_UI_SENSE                0x03
26 0f58f68b Gerd Hoffmann
#define UAS_UI_RESPONSE             0x04
27 0f58f68b Gerd Hoffmann
#define UAS_UI_TASK_MGMT            0x05
28 0f58f68b Gerd Hoffmann
#define UAS_UI_READ_READY           0x06
29 0f58f68b Gerd Hoffmann
#define UAS_UI_WRITE_READY          0x07
30 0f58f68b Gerd Hoffmann
31 0f58f68b Gerd Hoffmann
#define UAS_RC_TMF_COMPLETE         0x00
32 0f58f68b Gerd Hoffmann
#define UAS_RC_INVALID_INFO_UNIT    0x02
33 0f58f68b Gerd Hoffmann
#define UAS_RC_TMF_NOT_SUPPORTED    0x04
34 0f58f68b Gerd Hoffmann
#define UAS_RC_TMF_FAILED           0x05
35 0f58f68b Gerd Hoffmann
#define UAS_RC_TMF_SUCCEEDED        0x08
36 0f58f68b Gerd Hoffmann
#define UAS_RC_INCORRECT_LUN        0x09
37 0f58f68b Gerd Hoffmann
#define UAS_RC_OVERLAPPED_TAG       0x0a
38 0f58f68b Gerd Hoffmann
39 0f58f68b Gerd Hoffmann
#define UAS_TMF_ABORT_TASK          0x01
40 0f58f68b Gerd Hoffmann
#define UAS_TMF_ABORT_TASK_SET      0x02
41 0f58f68b Gerd Hoffmann
#define UAS_TMF_CLEAR_TASK_SET      0x04
42 0f58f68b Gerd Hoffmann
#define UAS_TMF_LOGICAL_UNIT_RESET  0x08
43 0f58f68b Gerd Hoffmann
#define UAS_TMF_I_T_NEXUS_RESET     0x10
44 0f58f68b Gerd Hoffmann
#define UAS_TMF_CLEAR_ACA           0x40
45 0f58f68b Gerd Hoffmann
#define UAS_TMF_QUERY_TASK          0x80
46 0f58f68b Gerd Hoffmann
#define UAS_TMF_QUERY_TASK_SET      0x81
47 0f58f68b Gerd Hoffmann
#define UAS_TMF_QUERY_ASYNC_EVENT   0x82
48 0f58f68b Gerd Hoffmann
49 0f58f68b Gerd Hoffmann
#define UAS_PIPE_ID_COMMAND         0x01
50 0f58f68b Gerd Hoffmann
#define UAS_PIPE_ID_STATUS          0x02
51 0f58f68b Gerd Hoffmann
#define UAS_PIPE_ID_DATA_IN         0x03
52 0f58f68b Gerd Hoffmann
#define UAS_PIPE_ID_DATA_OUT        0x04
53 0f58f68b Gerd Hoffmann
54 0f58f68b Gerd Hoffmann
typedef struct {
55 0f58f68b Gerd Hoffmann
    uint8_t    id;
56 0f58f68b Gerd Hoffmann
    uint8_t    reserved;
57 0f58f68b Gerd Hoffmann
    uint16_t   tag;
58 0f58f68b Gerd Hoffmann
} QEMU_PACKED  uas_ui_header;
59 0f58f68b Gerd Hoffmann
60 0f58f68b Gerd Hoffmann
typedef struct {
61 0f58f68b Gerd Hoffmann
    uint8_t    prio_taskattr;   /* 6:3 priority, 2:0 task attribute   */
62 0f58f68b Gerd Hoffmann
    uint8_t    reserved_1;
63 0f58f68b Gerd Hoffmann
    uint8_t    add_cdb_length;  /* 7:2 additional adb length (dwords) */
64 0f58f68b Gerd Hoffmann
    uint8_t    reserved_2;
65 0f58f68b Gerd Hoffmann
    uint64_t   lun;
66 0f58f68b Gerd Hoffmann
    uint8_t    cdb[16];
67 0f58f68b Gerd Hoffmann
    uint8_t    add_cdb[];
68 0f58f68b Gerd Hoffmann
} QEMU_PACKED  uas_ui_command;
69 0f58f68b Gerd Hoffmann
70 0f58f68b Gerd Hoffmann
typedef struct {
71 0f58f68b Gerd Hoffmann
    uint16_t   status_qualifier;
72 0f58f68b Gerd Hoffmann
    uint8_t    status;
73 0f58f68b Gerd Hoffmann
    uint8_t    reserved[7];
74 0f58f68b Gerd Hoffmann
    uint16_t   sense_length;
75 0f58f68b Gerd Hoffmann
    uint8_t    sense_data[18];
76 0f58f68b Gerd Hoffmann
} QEMU_PACKED  uas_ui_sense;
77 0f58f68b Gerd Hoffmann
78 0f58f68b Gerd Hoffmann
typedef struct {
79 0f58f68b Gerd Hoffmann
    uint16_t   add_response_info;
80 0f58f68b Gerd Hoffmann
    uint8_t    response_code;
81 0f58f68b Gerd Hoffmann
} QEMU_PACKED  uas_ui_response;
82 0f58f68b Gerd Hoffmann
83 0f58f68b Gerd Hoffmann
typedef struct {
84 0f58f68b Gerd Hoffmann
    uint8_t    function;
85 0f58f68b Gerd Hoffmann
    uint8_t    reserved;
86 0f58f68b Gerd Hoffmann
    uint16_t   task_tag;
87 0f58f68b Gerd Hoffmann
    uint64_t   lun;
88 0f58f68b Gerd Hoffmann
} QEMU_PACKED  uas_ui_task_mgmt;
89 0f58f68b Gerd Hoffmann
90 0f58f68b Gerd Hoffmann
typedef struct {
91 0f58f68b Gerd Hoffmann
    uas_ui_header  hdr;
92 0f58f68b Gerd Hoffmann
    union {
93 0f58f68b Gerd Hoffmann
        uas_ui_command   command;
94 0f58f68b Gerd Hoffmann
        uas_ui_sense     sense;
95 0f58f68b Gerd Hoffmann
        uas_ui_task_mgmt task;
96 0f58f68b Gerd Hoffmann
        uas_ui_response  response;
97 0f58f68b Gerd Hoffmann
    };
98 0f58f68b Gerd Hoffmann
} QEMU_PACKED  uas_ui;
99 0f58f68b Gerd Hoffmann
100 0f58f68b Gerd Hoffmann
/* --------------------------------------------------------------------- */
101 0f58f68b Gerd Hoffmann
102 89a453d4 Gerd Hoffmann
#define UAS_STREAM_BM_ATTR  4
103 89a453d4 Gerd Hoffmann
#define UAS_MAX_STREAMS     (1 << UAS_STREAM_BM_ATTR)
104 89a453d4 Gerd Hoffmann
105 0f58f68b Gerd Hoffmann
typedef struct UASDevice UASDevice;
106 0f58f68b Gerd Hoffmann
typedef struct UASRequest UASRequest;
107 0f58f68b Gerd Hoffmann
typedef struct UASStatus UASStatus;
108 0f58f68b Gerd Hoffmann
109 0f58f68b Gerd Hoffmann
struct UASDevice {
110 0f58f68b Gerd Hoffmann
    USBDevice                 dev;
111 0f58f68b Gerd Hoffmann
    SCSIBus                   bus;
112 0f58f68b Gerd Hoffmann
    QEMUBH                    *status_bh;
113 0f58f68b Gerd Hoffmann
    QTAILQ_HEAD(, UASStatus)  results;
114 0f58f68b Gerd Hoffmann
    QTAILQ_HEAD(, UASRequest) requests;
115 89a453d4 Gerd Hoffmann
116 89a453d4 Gerd Hoffmann
    /* usb 2.0 only */
117 89a453d4 Gerd Hoffmann
    USBPacket                 *status2;
118 89a453d4 Gerd Hoffmann
    UASRequest                *datain2;
119 89a453d4 Gerd Hoffmann
    UASRequest                *dataout2;
120 89a453d4 Gerd Hoffmann
121 89a453d4 Gerd Hoffmann
    /* usb 3.0 only */
122 89a453d4 Gerd Hoffmann
    USBPacket                 *data3[UAS_MAX_STREAMS];
123 89a453d4 Gerd Hoffmann
    USBPacket                 *status3[UAS_MAX_STREAMS];
124 0f58f68b Gerd Hoffmann
};
125 0f58f68b Gerd Hoffmann
126 0f58f68b Gerd Hoffmann
struct UASRequest {
127 0f58f68b Gerd Hoffmann
    uint16_t     tag;
128 0f58f68b Gerd Hoffmann
    uint64_t     lun;
129 0f58f68b Gerd Hoffmann
    UASDevice    *uas;
130 0f58f68b Gerd Hoffmann
    SCSIDevice   *dev;
131 0f58f68b Gerd Hoffmann
    SCSIRequest  *req;
132 0f58f68b Gerd Hoffmann
    USBPacket    *data;
133 0f58f68b Gerd Hoffmann
    bool         data_async;
134 0f58f68b Gerd Hoffmann
    bool         active;
135 0f58f68b Gerd Hoffmann
    bool         complete;
136 0f58f68b Gerd Hoffmann
    uint32_t     buf_off;
137 0f58f68b Gerd Hoffmann
    uint32_t     buf_size;
138 0f58f68b Gerd Hoffmann
    uint32_t     data_off;
139 0f58f68b Gerd Hoffmann
    uint32_t     data_size;
140 0f58f68b Gerd Hoffmann
    QTAILQ_ENTRY(UASRequest)  next;
141 0f58f68b Gerd Hoffmann
};
142 0f58f68b Gerd Hoffmann
143 0f58f68b Gerd Hoffmann
struct UASStatus {
144 89a453d4 Gerd Hoffmann
    uint32_t                  stream;
145 0f58f68b Gerd Hoffmann
    uas_ui                    status;
146 0f58f68b Gerd Hoffmann
    uint32_t                  length;
147 0f58f68b Gerd Hoffmann
    QTAILQ_ENTRY(UASStatus)   next;
148 0f58f68b Gerd Hoffmann
};
149 0f58f68b Gerd Hoffmann
150 0f58f68b Gerd Hoffmann
/* --------------------------------------------------------------------- */
151 0f58f68b Gerd Hoffmann
152 0f58f68b Gerd Hoffmann
enum {
153 0f58f68b Gerd Hoffmann
    STR_MANUFACTURER = 1,
154 0f58f68b Gerd Hoffmann
    STR_PRODUCT,
155 0f58f68b Gerd Hoffmann
    STR_SERIALNUMBER,
156 0f58f68b Gerd Hoffmann
    STR_CONFIG_HIGH,
157 89a453d4 Gerd Hoffmann
    STR_CONFIG_SUPER,
158 0f58f68b Gerd Hoffmann
};
159 0f58f68b Gerd Hoffmann
160 0f58f68b Gerd Hoffmann
static const USBDescStrings desc_strings = {
161 0f58f68b Gerd Hoffmann
    [STR_MANUFACTURER] = "QEMU",
162 0f58f68b Gerd Hoffmann
    [STR_PRODUCT]      = "USB Attached SCSI HBA",
163 0f58f68b Gerd Hoffmann
    [STR_SERIALNUMBER] = "27842",
164 0f58f68b Gerd Hoffmann
    [STR_CONFIG_HIGH]  = "High speed config (usb 2.0)",
165 89a453d4 Gerd Hoffmann
    [STR_CONFIG_SUPER] = "Super speed config (usb 3.0)",
166 0f58f68b Gerd Hoffmann
};
167 0f58f68b Gerd Hoffmann
168 0f58f68b Gerd Hoffmann
static const USBDescIface desc_iface_high = {
169 0f58f68b Gerd Hoffmann
    .bInterfaceNumber              = 0,
170 0f58f68b Gerd Hoffmann
    .bNumEndpoints                 = 4,
171 0f58f68b Gerd Hoffmann
    .bInterfaceClass               = USB_CLASS_MASS_STORAGE,
172 0f58f68b Gerd Hoffmann
    .bInterfaceSubClass            = 0x06, /* SCSI */
173 0f58f68b Gerd Hoffmann
    .bInterfaceProtocol            = 0x62, /* UAS  */
174 0f58f68b Gerd Hoffmann
    .eps = (USBDescEndpoint[]) {
175 0f58f68b Gerd Hoffmann
        {
176 0f58f68b Gerd Hoffmann
            .bEndpointAddress      = USB_DIR_OUT | UAS_PIPE_ID_COMMAND,
177 0f58f68b Gerd Hoffmann
            .bmAttributes          = USB_ENDPOINT_XFER_BULK,
178 0f58f68b Gerd Hoffmann
            .wMaxPacketSize        = 512,
179 0f58f68b Gerd Hoffmann
            .extra = (uint8_t[]) {
180 0f58f68b Gerd Hoffmann
                0x04,  /*  u8  bLength */
181 0f58f68b Gerd Hoffmann
                0x24,  /*  u8  bDescriptorType */
182 0f58f68b Gerd Hoffmann
                UAS_PIPE_ID_COMMAND,
183 0f58f68b Gerd Hoffmann
                0x00,  /*  u8  bReserved */
184 0f58f68b Gerd Hoffmann
            },
185 0f58f68b Gerd Hoffmann
        },{
186 0f58f68b Gerd Hoffmann
            .bEndpointAddress      = USB_DIR_IN | UAS_PIPE_ID_STATUS,
187 0f58f68b Gerd Hoffmann
            .bmAttributes          = USB_ENDPOINT_XFER_BULK,
188 0f58f68b Gerd Hoffmann
            .wMaxPacketSize        = 512,
189 0f58f68b Gerd Hoffmann
            .extra = (uint8_t[]) {
190 0f58f68b Gerd Hoffmann
                0x04,  /*  u8  bLength */
191 0f58f68b Gerd Hoffmann
                0x24,  /*  u8  bDescriptorType */
192 0f58f68b Gerd Hoffmann
                UAS_PIPE_ID_STATUS,
193 0f58f68b Gerd Hoffmann
                0x00,  /*  u8  bReserved */
194 0f58f68b Gerd Hoffmann
            },
195 0f58f68b Gerd Hoffmann
        },{
196 0f58f68b Gerd Hoffmann
            .bEndpointAddress      = USB_DIR_IN | UAS_PIPE_ID_DATA_IN,
197 0f58f68b Gerd Hoffmann
            .bmAttributes          = USB_ENDPOINT_XFER_BULK,
198 0f58f68b Gerd Hoffmann
            .wMaxPacketSize        = 512,
199 0f58f68b Gerd Hoffmann
            .extra = (uint8_t[]) {
200 0f58f68b Gerd Hoffmann
                0x04,  /*  u8  bLength */
201 0f58f68b Gerd Hoffmann
                0x24,  /*  u8  bDescriptorType */
202 0f58f68b Gerd Hoffmann
                UAS_PIPE_ID_DATA_IN,
203 0f58f68b Gerd Hoffmann
                0x00,  /*  u8  bReserved */
204 0f58f68b Gerd Hoffmann
            },
205 0f58f68b Gerd Hoffmann
        },{
206 0f58f68b Gerd Hoffmann
            .bEndpointAddress      = USB_DIR_OUT | UAS_PIPE_ID_DATA_OUT,
207 0f58f68b Gerd Hoffmann
            .bmAttributes          = USB_ENDPOINT_XFER_BULK,
208 0f58f68b Gerd Hoffmann
            .wMaxPacketSize        = 512,
209 0f58f68b Gerd Hoffmann
            .extra = (uint8_t[]) {
210 0f58f68b Gerd Hoffmann
                0x04,  /*  u8  bLength */
211 0f58f68b Gerd Hoffmann
                0x24,  /*  u8  bDescriptorType */
212 0f58f68b Gerd Hoffmann
                UAS_PIPE_ID_DATA_OUT,
213 0f58f68b Gerd Hoffmann
                0x00,  /*  u8  bReserved */
214 0f58f68b Gerd Hoffmann
            },
215 0f58f68b Gerd Hoffmann
        },
216 0f58f68b Gerd Hoffmann
    }
217 0f58f68b Gerd Hoffmann
};
218 0f58f68b Gerd Hoffmann
219 89a453d4 Gerd Hoffmann
static const USBDescIface desc_iface_super = {
220 89a453d4 Gerd Hoffmann
    .bInterfaceNumber              = 0,
221 89a453d4 Gerd Hoffmann
    .bNumEndpoints                 = 4,
222 89a453d4 Gerd Hoffmann
    .bInterfaceClass               = USB_CLASS_MASS_STORAGE,
223 89a453d4 Gerd Hoffmann
    .bInterfaceSubClass            = 0x06, /* SCSI */
224 89a453d4 Gerd Hoffmann
    .bInterfaceProtocol            = 0x62, /* UAS  */
225 89a453d4 Gerd Hoffmann
    .eps = (USBDescEndpoint[]) {
226 89a453d4 Gerd Hoffmann
        {
227 89a453d4 Gerd Hoffmann
            .bEndpointAddress      = USB_DIR_OUT | UAS_PIPE_ID_COMMAND,
228 89a453d4 Gerd Hoffmann
            .bmAttributes          = USB_ENDPOINT_XFER_BULK,
229 89a453d4 Gerd Hoffmann
            .wMaxPacketSize        = 1024,
230 89a453d4 Gerd Hoffmann
            .bMaxBurst             = 15,
231 89a453d4 Gerd Hoffmann
            .extra = (uint8_t[]) {
232 89a453d4 Gerd Hoffmann
                0x04,  /*  u8  bLength */
233 89a453d4 Gerd Hoffmann
                0x24,  /*  u8  bDescriptorType */
234 89a453d4 Gerd Hoffmann
                UAS_PIPE_ID_COMMAND,
235 89a453d4 Gerd Hoffmann
                0x00,  /*  u8  bReserved */
236 89a453d4 Gerd Hoffmann
            },
237 89a453d4 Gerd Hoffmann
        },{
238 89a453d4 Gerd Hoffmann
            .bEndpointAddress      = USB_DIR_IN | UAS_PIPE_ID_STATUS,
239 89a453d4 Gerd Hoffmann
            .bmAttributes          = USB_ENDPOINT_XFER_BULK,
240 89a453d4 Gerd Hoffmann
            .wMaxPacketSize        = 1024,
241 89a453d4 Gerd Hoffmann
            .bMaxBurst             = 15,
242 89a453d4 Gerd Hoffmann
            .bmAttributes_super    = UAS_STREAM_BM_ATTR,
243 89a453d4 Gerd Hoffmann
            .extra = (uint8_t[]) {
244 89a453d4 Gerd Hoffmann
                0x04,  /*  u8  bLength */
245 89a453d4 Gerd Hoffmann
                0x24,  /*  u8  bDescriptorType */
246 89a453d4 Gerd Hoffmann
                UAS_PIPE_ID_STATUS,
247 89a453d4 Gerd Hoffmann
                0x00,  /*  u8  bReserved */
248 89a453d4 Gerd Hoffmann
            },
249 89a453d4 Gerd Hoffmann
        },{
250 89a453d4 Gerd Hoffmann
            .bEndpointAddress      = USB_DIR_IN | UAS_PIPE_ID_DATA_IN,
251 89a453d4 Gerd Hoffmann
            .bmAttributes          = USB_ENDPOINT_XFER_BULK,
252 89a453d4 Gerd Hoffmann
            .wMaxPacketSize        = 1024,
253 89a453d4 Gerd Hoffmann
            .bMaxBurst             = 15,
254 89a453d4 Gerd Hoffmann
            .bmAttributes_super    = UAS_STREAM_BM_ATTR,
255 89a453d4 Gerd Hoffmann
            .extra = (uint8_t[]) {
256 89a453d4 Gerd Hoffmann
                0x04,  /*  u8  bLength */
257 89a453d4 Gerd Hoffmann
                0x24,  /*  u8  bDescriptorType */
258 89a453d4 Gerd Hoffmann
                UAS_PIPE_ID_DATA_IN,
259 89a453d4 Gerd Hoffmann
                0x00,  /*  u8  bReserved */
260 89a453d4 Gerd Hoffmann
            },
261 89a453d4 Gerd Hoffmann
        },{
262 89a453d4 Gerd Hoffmann
            .bEndpointAddress      = USB_DIR_OUT | UAS_PIPE_ID_DATA_OUT,
263 89a453d4 Gerd Hoffmann
            .bmAttributes          = USB_ENDPOINT_XFER_BULK,
264 89a453d4 Gerd Hoffmann
            .wMaxPacketSize        = 1024,
265 89a453d4 Gerd Hoffmann
            .bMaxBurst             = 15,
266 89a453d4 Gerd Hoffmann
            .bmAttributes_super    = UAS_STREAM_BM_ATTR,
267 89a453d4 Gerd Hoffmann
            .extra = (uint8_t[]) {
268 89a453d4 Gerd Hoffmann
                0x04,  /*  u8  bLength */
269 89a453d4 Gerd Hoffmann
                0x24,  /*  u8  bDescriptorType */
270 89a453d4 Gerd Hoffmann
                UAS_PIPE_ID_DATA_OUT,
271 89a453d4 Gerd Hoffmann
                0x00,  /*  u8  bReserved */
272 89a453d4 Gerd Hoffmann
            },
273 89a453d4 Gerd Hoffmann
        },
274 89a453d4 Gerd Hoffmann
    }
275 89a453d4 Gerd Hoffmann
};
276 89a453d4 Gerd Hoffmann
277 0f58f68b Gerd Hoffmann
static const USBDescDevice desc_device_high = {
278 0f58f68b Gerd Hoffmann
    .bcdUSB                        = 0x0200,
279 0f58f68b Gerd Hoffmann
    .bMaxPacketSize0               = 64,
280 0f58f68b Gerd Hoffmann
    .bNumConfigurations            = 1,
281 0f58f68b Gerd Hoffmann
    .confs = (USBDescConfig[]) {
282 0f58f68b Gerd Hoffmann
        {
283 0f58f68b Gerd Hoffmann
            .bNumInterfaces        = 1,
284 0f58f68b Gerd Hoffmann
            .bConfigurationValue   = 1,
285 0f58f68b Gerd Hoffmann
            .iConfiguration        = STR_CONFIG_HIGH,
286 0f58f68b Gerd Hoffmann
            .bmAttributes          = 0xc0,
287 0f58f68b Gerd Hoffmann
            .nif = 1,
288 0f58f68b Gerd Hoffmann
            .ifs = &desc_iface_high,
289 0f58f68b Gerd Hoffmann
        },
290 0f58f68b Gerd Hoffmann
    },
291 0f58f68b Gerd Hoffmann
};
292 0f58f68b Gerd Hoffmann
293 89a453d4 Gerd Hoffmann
static const USBDescDevice desc_device_super = {
294 89a453d4 Gerd Hoffmann
    .bcdUSB                        = 0x0300,
295 89a453d4 Gerd Hoffmann
    .bMaxPacketSize0               = 64,
296 89a453d4 Gerd Hoffmann
    .bNumConfigurations            = 1,
297 89a453d4 Gerd Hoffmann
    .confs = (USBDescConfig[]) {
298 89a453d4 Gerd Hoffmann
        {
299 89a453d4 Gerd Hoffmann
            .bNumInterfaces        = 1,
300 89a453d4 Gerd Hoffmann
            .bConfigurationValue   = 1,
301 89a453d4 Gerd Hoffmann
            .iConfiguration        = STR_CONFIG_SUPER,
302 89a453d4 Gerd Hoffmann
            .bmAttributes          = 0xc0,
303 89a453d4 Gerd Hoffmann
            .nif = 1,
304 89a453d4 Gerd Hoffmann
            .ifs = &desc_iface_super,
305 89a453d4 Gerd Hoffmann
        },
306 89a453d4 Gerd Hoffmann
    },
307 89a453d4 Gerd Hoffmann
};
308 89a453d4 Gerd Hoffmann
309 0f58f68b Gerd Hoffmann
static const USBDesc desc = {
310 0f58f68b Gerd Hoffmann
    .id = {
311 0f58f68b Gerd Hoffmann
        .idVendor          = 0x46f4, /* CRC16() of "QEMU" */
312 0daf5304 Gerd Hoffmann
        .idProduct         = 0x0003,
313 0f58f68b Gerd Hoffmann
        .bcdDevice         = 0,
314 0f58f68b Gerd Hoffmann
        .iManufacturer     = STR_MANUFACTURER,
315 0f58f68b Gerd Hoffmann
        .iProduct          = STR_PRODUCT,
316 0f58f68b Gerd Hoffmann
        .iSerialNumber     = STR_SERIALNUMBER,
317 0f58f68b Gerd Hoffmann
    },
318 89a453d4 Gerd Hoffmann
    .high  = &desc_device_high,
319 89a453d4 Gerd Hoffmann
    .super = &desc_device_super,
320 89a453d4 Gerd Hoffmann
    .str   = desc_strings,
321 0f58f68b Gerd Hoffmann
};
322 0f58f68b Gerd Hoffmann
323 0f58f68b Gerd Hoffmann
/* --------------------------------------------------------------------- */
324 0f58f68b Gerd Hoffmann
325 89a453d4 Gerd Hoffmann
static bool uas_using_streams(UASDevice *uas)
326 89a453d4 Gerd Hoffmann
{
327 89a453d4 Gerd Hoffmann
    return uas->dev.speed == USB_SPEED_SUPER;
328 89a453d4 Gerd Hoffmann
}
329 89a453d4 Gerd Hoffmann
330 89a453d4 Gerd Hoffmann
/* --------------------------------------------------------------------- */
331 89a453d4 Gerd Hoffmann
332 89a453d4 Gerd Hoffmann
static UASStatus *usb_uas_alloc_status(UASDevice *uas, uint8_t id, uint16_t tag)
333 0f58f68b Gerd Hoffmann
{
334 0f58f68b Gerd Hoffmann
    UASStatus *st = g_new0(UASStatus, 1);
335 0f58f68b Gerd Hoffmann
336 0f58f68b Gerd Hoffmann
    st->status.hdr.id = id;
337 0f58f68b Gerd Hoffmann
    st->status.hdr.tag = cpu_to_be16(tag);
338 0f58f68b Gerd Hoffmann
    st->length = sizeof(uas_ui_header);
339 89a453d4 Gerd Hoffmann
    if (uas_using_streams(uas)) {
340 89a453d4 Gerd Hoffmann
        st->stream = tag;
341 89a453d4 Gerd Hoffmann
    }
342 0f58f68b Gerd Hoffmann
    return st;
343 0f58f68b Gerd Hoffmann
}
344 0f58f68b Gerd Hoffmann
345 0f58f68b Gerd Hoffmann
static void usb_uas_send_status_bh(void *opaque)
346 0f58f68b Gerd Hoffmann
{
347 0f58f68b Gerd Hoffmann
    UASDevice *uas = opaque;
348 89a453d4 Gerd Hoffmann
    UASStatus *st;
349 89a453d4 Gerd Hoffmann
    USBPacket *p;
350 0f58f68b Gerd Hoffmann
351 89a453d4 Gerd Hoffmann
    while ((st = QTAILQ_FIRST(&uas->results)) != NULL) {
352 89a453d4 Gerd Hoffmann
        if (uas_using_streams(uas)) {
353 89a453d4 Gerd Hoffmann
            p = uas->status3[st->stream];
354 89a453d4 Gerd Hoffmann
            uas->status3[st->stream] = NULL;
355 89a453d4 Gerd Hoffmann
        } else {
356 89a453d4 Gerd Hoffmann
            p = uas->status2;
357 89a453d4 Gerd Hoffmann
            uas->status2 = NULL;
358 89a453d4 Gerd Hoffmann
        }
359 89a453d4 Gerd Hoffmann
        if (p == NULL) {
360 89a453d4 Gerd Hoffmann
            break;
361 89a453d4 Gerd Hoffmann
        }
362 0f58f68b Gerd Hoffmann
363 89a453d4 Gerd Hoffmann
        usb_packet_copy(p, &st->status, st->length);
364 89a453d4 Gerd Hoffmann
        QTAILQ_REMOVE(&uas->results, st, next);
365 89a453d4 Gerd Hoffmann
        g_free(st);
366 0f58f68b Gerd Hoffmann
367 89a453d4 Gerd Hoffmann
        p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */
368 89a453d4 Gerd Hoffmann
        usb_packet_complete(&uas->dev, p);
369 89a453d4 Gerd Hoffmann
    }
370 0f58f68b Gerd Hoffmann
}
371 0f58f68b Gerd Hoffmann
372 0f58f68b Gerd Hoffmann
static void usb_uas_queue_status(UASDevice *uas, UASStatus *st, int length)
373 0f58f68b Gerd Hoffmann
{
374 89a453d4 Gerd Hoffmann
    USBPacket *p = uas_using_streams(uas) ?
375 89a453d4 Gerd Hoffmann
        uas->status3[st->stream] : uas->status2;
376 89a453d4 Gerd Hoffmann
377 0f58f68b Gerd Hoffmann
    st->length += length;
378 0f58f68b Gerd Hoffmann
    QTAILQ_INSERT_TAIL(&uas->results, st, next);
379 89a453d4 Gerd Hoffmann
    if (p) {
380 0f58f68b Gerd Hoffmann
        /*
381 0f58f68b Gerd Hoffmann
         * Just schedule bh make sure any in-flight data transaction
382 0f58f68b Gerd Hoffmann
         * is finished before completing (sending) the status packet.
383 0f58f68b Gerd Hoffmann
         */
384 0f58f68b Gerd Hoffmann
        qemu_bh_schedule(uas->status_bh);
385 0f58f68b Gerd Hoffmann
    } else {
386 0f58f68b Gerd Hoffmann
        USBEndpoint *ep = usb_ep_get(&uas->dev, USB_TOKEN_IN,
387 0f58f68b Gerd Hoffmann
                                     UAS_PIPE_ID_STATUS);
388 89a453d4 Gerd Hoffmann
        usb_wakeup(ep, st->stream);
389 0f58f68b Gerd Hoffmann
    }
390 0f58f68b Gerd Hoffmann
}
391 0f58f68b Gerd Hoffmann
392 0f58f68b Gerd Hoffmann
static void usb_uas_queue_response(UASDevice *uas, uint16_t tag,
393 0f58f68b Gerd Hoffmann
                                   uint8_t code, uint16_t add_info)
394 0f58f68b Gerd Hoffmann
{
395 89a453d4 Gerd Hoffmann
    UASStatus *st = usb_uas_alloc_status(uas, UAS_UI_RESPONSE, tag);
396 0f58f68b Gerd Hoffmann
397 0f58f68b Gerd Hoffmann
    trace_usb_uas_response(uas->dev.addr, tag, code);
398 0f58f68b Gerd Hoffmann
    st->status.response.response_code = code;
399 0f58f68b Gerd Hoffmann
    st->status.response.add_response_info = cpu_to_be16(add_info);
400 0f58f68b Gerd Hoffmann
    usb_uas_queue_status(uas, st, sizeof(uas_ui_response));
401 0f58f68b Gerd Hoffmann
}
402 0f58f68b Gerd Hoffmann
403 0f58f68b Gerd Hoffmann
static void usb_uas_queue_sense(UASRequest *req, uint8_t status)
404 0f58f68b Gerd Hoffmann
{
405 89a453d4 Gerd Hoffmann
    UASStatus *st = usb_uas_alloc_status(req->uas, UAS_UI_SENSE, req->tag);
406 0f58f68b Gerd Hoffmann
    int len, slen = 0;
407 0f58f68b Gerd Hoffmann
408 0f58f68b Gerd Hoffmann
    trace_usb_uas_sense(req->uas->dev.addr, req->tag, status);
409 0f58f68b Gerd Hoffmann
    st->status.sense.status = status;
410 0f58f68b Gerd Hoffmann
    st->status.sense.status_qualifier = cpu_to_be16(0);
411 0f58f68b Gerd Hoffmann
    if (status != GOOD) {
412 0f58f68b Gerd Hoffmann
        slen = scsi_req_get_sense(req->req, st->status.sense.sense_data,
413 0f58f68b Gerd Hoffmann
                                  sizeof(st->status.sense.sense_data));
414 0f58f68b Gerd Hoffmann
        st->status.sense.sense_length = cpu_to_be16(slen);
415 0f58f68b Gerd Hoffmann
    }
416 0f58f68b Gerd Hoffmann
    len = sizeof(uas_ui_sense) - sizeof(st->status.sense.sense_data) + slen;
417 0f58f68b Gerd Hoffmann
    usb_uas_queue_status(req->uas, st, len);
418 0f58f68b Gerd Hoffmann
}
419 0f58f68b Gerd Hoffmann
420 0f58f68b Gerd Hoffmann
static void usb_uas_queue_read_ready(UASRequest *req)
421 0f58f68b Gerd Hoffmann
{
422 89a453d4 Gerd Hoffmann
    UASStatus *st = usb_uas_alloc_status(req->uas, UAS_UI_READ_READY,
423 89a453d4 Gerd Hoffmann
                                         req->tag);
424 0f58f68b Gerd Hoffmann
425 0f58f68b Gerd Hoffmann
    trace_usb_uas_read_ready(req->uas->dev.addr, req->tag);
426 0f58f68b Gerd Hoffmann
    usb_uas_queue_status(req->uas, st, 0);
427 0f58f68b Gerd Hoffmann
}
428 0f58f68b Gerd Hoffmann
429 0f58f68b Gerd Hoffmann
static void usb_uas_queue_write_ready(UASRequest *req)
430 0f58f68b Gerd Hoffmann
{
431 89a453d4 Gerd Hoffmann
    UASStatus *st = usb_uas_alloc_status(req->uas, UAS_UI_WRITE_READY,
432 89a453d4 Gerd Hoffmann
                                         req->tag);
433 0f58f68b Gerd Hoffmann
434 0f58f68b Gerd Hoffmann
    trace_usb_uas_write_ready(req->uas->dev.addr, req->tag);
435 0f58f68b Gerd Hoffmann
    usb_uas_queue_status(req->uas, st, 0);
436 0f58f68b Gerd Hoffmann
}
437 0f58f68b Gerd Hoffmann
438 0f58f68b Gerd Hoffmann
/* --------------------------------------------------------------------- */
439 0f58f68b Gerd Hoffmann
440 0f58f68b Gerd Hoffmann
static int usb_uas_get_lun(uint64_t lun64)
441 0f58f68b Gerd Hoffmann
{
442 0f58f68b Gerd Hoffmann
    return (lun64 >> 48) & 0xff;
443 0f58f68b Gerd Hoffmann
}
444 0f58f68b Gerd Hoffmann
445 0f58f68b Gerd Hoffmann
static SCSIDevice *usb_uas_get_dev(UASDevice *uas, uint64_t lun64)
446 0f58f68b Gerd Hoffmann
{
447 0f58f68b Gerd Hoffmann
    if ((lun64 >> 56) != 0x00) {
448 0f58f68b Gerd Hoffmann
        return NULL;
449 0f58f68b Gerd Hoffmann
    }
450 0f58f68b Gerd Hoffmann
    return scsi_device_find(&uas->bus, 0, 0, usb_uas_get_lun(lun64));
451 0f58f68b Gerd Hoffmann
}
452 0f58f68b Gerd Hoffmann
453 0f58f68b Gerd Hoffmann
static void usb_uas_complete_data_packet(UASRequest *req)
454 0f58f68b Gerd Hoffmann
{
455 0f58f68b Gerd Hoffmann
    USBPacket *p;
456 0f58f68b Gerd Hoffmann
457 0f58f68b Gerd Hoffmann
    if (!req->data_async) {
458 0f58f68b Gerd Hoffmann
        return;
459 0f58f68b Gerd Hoffmann
    }
460 0f58f68b Gerd Hoffmann
    p = req->data;
461 0f58f68b Gerd Hoffmann
    req->data = NULL;
462 0f58f68b Gerd Hoffmann
    req->data_async = false;
463 9a77a0f5 Hans de Goede
    p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */
464 0f58f68b Gerd Hoffmann
    usb_packet_complete(&req->uas->dev, p);
465 0f58f68b Gerd Hoffmann
}
466 0f58f68b Gerd Hoffmann
467 0f58f68b Gerd Hoffmann
static void usb_uas_copy_data(UASRequest *req)
468 0f58f68b Gerd Hoffmann
{
469 0f58f68b Gerd Hoffmann
    uint32_t length;
470 0f58f68b Gerd Hoffmann
471 0f58f68b Gerd Hoffmann
    length = MIN(req->buf_size - req->buf_off,
472 9a77a0f5 Hans de Goede
                 req->data->iov.size - req->data->actual_length);
473 0f58f68b Gerd Hoffmann
    trace_usb_uas_xfer_data(req->uas->dev.addr, req->tag, length,
474 9a77a0f5 Hans de Goede
                            req->data->actual_length, req->data->iov.size,
475 0f58f68b Gerd Hoffmann
                            req->buf_off, req->buf_size);
476 0f58f68b Gerd Hoffmann
    usb_packet_copy(req->data, scsi_req_get_buf(req->req) + req->buf_off,
477 0f58f68b Gerd Hoffmann
                    length);
478 0f58f68b Gerd Hoffmann
    req->buf_off += length;
479 0f58f68b Gerd Hoffmann
    req->data_off += length;
480 0f58f68b Gerd Hoffmann
481 9a77a0f5 Hans de Goede
    if (req->data->actual_length == req->data->iov.size) {
482 0f58f68b Gerd Hoffmann
        usb_uas_complete_data_packet(req);
483 0f58f68b Gerd Hoffmann
    }
484 0f58f68b Gerd Hoffmann
    if (req->buf_size && req->buf_off == req->buf_size) {
485 0f58f68b Gerd Hoffmann
        req->buf_off = 0;
486 0f58f68b Gerd Hoffmann
        req->buf_size = 0;
487 0f58f68b Gerd Hoffmann
        scsi_req_continue(req->req);
488 0f58f68b Gerd Hoffmann
    }
489 0f58f68b Gerd Hoffmann
}
490 0f58f68b Gerd Hoffmann
491 0f58f68b Gerd Hoffmann
static void usb_uas_start_next_transfer(UASDevice *uas)
492 0f58f68b Gerd Hoffmann
{
493 0f58f68b Gerd Hoffmann
    UASRequest *req;
494 0f58f68b Gerd Hoffmann
495 89a453d4 Gerd Hoffmann
    if (uas_using_streams(uas)) {
496 89a453d4 Gerd Hoffmann
        return;
497 89a453d4 Gerd Hoffmann
    }
498 89a453d4 Gerd Hoffmann
499 0f58f68b Gerd Hoffmann
    QTAILQ_FOREACH(req, &uas->requests, next) {
500 0f58f68b Gerd Hoffmann
        if (req->active || req->complete) {
501 0f58f68b Gerd Hoffmann
            continue;
502 0f58f68b Gerd Hoffmann
        }
503 89a453d4 Gerd Hoffmann
        if (req->req->cmd.mode == SCSI_XFER_FROM_DEV && uas->datain2 == NULL) {
504 89a453d4 Gerd Hoffmann
            uas->datain2 = req;
505 0f58f68b Gerd Hoffmann
            usb_uas_queue_read_ready(req);
506 0f58f68b Gerd Hoffmann
            req->active = true;
507 0f58f68b Gerd Hoffmann
            return;
508 0f58f68b Gerd Hoffmann
        }
509 89a453d4 Gerd Hoffmann
        if (req->req->cmd.mode == SCSI_XFER_TO_DEV && uas->dataout2 == NULL) {
510 89a453d4 Gerd Hoffmann
            uas->dataout2 = req;
511 0f58f68b Gerd Hoffmann
            usb_uas_queue_write_ready(req);
512 0f58f68b Gerd Hoffmann
            req->active = true;
513 0f58f68b Gerd Hoffmann
            return;
514 0f58f68b Gerd Hoffmann
        }
515 0f58f68b Gerd Hoffmann
    }
516 0f58f68b Gerd Hoffmann
}
517 0f58f68b Gerd Hoffmann
518 0f58f68b Gerd Hoffmann
static UASRequest *usb_uas_alloc_request(UASDevice *uas, uas_ui *ui)
519 0f58f68b Gerd Hoffmann
{
520 0f58f68b Gerd Hoffmann
    UASRequest *req;
521 0f58f68b Gerd Hoffmann
522 0f58f68b Gerd Hoffmann
    req = g_new0(UASRequest, 1);
523 0f58f68b Gerd Hoffmann
    req->uas = uas;
524 0f58f68b Gerd Hoffmann
    req->tag = be16_to_cpu(ui->hdr.tag);
525 0f58f68b Gerd Hoffmann
    req->lun = be64_to_cpu(ui->command.lun);
526 0f58f68b Gerd Hoffmann
    req->dev = usb_uas_get_dev(req->uas, req->lun);
527 0f58f68b Gerd Hoffmann
    return req;
528 0f58f68b Gerd Hoffmann
}
529 0f58f68b Gerd Hoffmann
530 0f58f68b Gerd Hoffmann
static void usb_uas_scsi_free_request(SCSIBus *bus, void *priv)
531 0f58f68b Gerd Hoffmann
{
532 0f58f68b Gerd Hoffmann
    UASRequest *req = priv;
533 0f58f68b Gerd Hoffmann
    UASDevice *uas = req->uas;
534 0f58f68b Gerd Hoffmann
535 89a453d4 Gerd Hoffmann
    if (req == uas->datain2) {
536 89a453d4 Gerd Hoffmann
        uas->datain2 = NULL;
537 0f58f68b Gerd Hoffmann
    }
538 89a453d4 Gerd Hoffmann
    if (req == uas->dataout2) {
539 89a453d4 Gerd Hoffmann
        uas->dataout2 = NULL;
540 0f58f68b Gerd Hoffmann
    }
541 0f58f68b Gerd Hoffmann
    QTAILQ_REMOVE(&uas->requests, req, next);
542 0f58f68b Gerd Hoffmann
    g_free(req);
543 347e40ff Gerd Hoffmann
    usb_uas_start_next_transfer(uas);
544 0f58f68b Gerd Hoffmann
}
545 0f58f68b Gerd Hoffmann
546 0f58f68b Gerd Hoffmann
static UASRequest *usb_uas_find_request(UASDevice *uas, uint16_t tag)
547 0f58f68b Gerd Hoffmann
{
548 0f58f68b Gerd Hoffmann
    UASRequest *req;
549 0f58f68b Gerd Hoffmann
550 0f58f68b Gerd Hoffmann
    QTAILQ_FOREACH(req, &uas->requests, next) {
551 0f58f68b Gerd Hoffmann
        if (req->tag == tag) {
552 0f58f68b Gerd Hoffmann
            return req;
553 0f58f68b Gerd Hoffmann
        }
554 0f58f68b Gerd Hoffmann
    }
555 0f58f68b Gerd Hoffmann
    return NULL;
556 0f58f68b Gerd Hoffmann
}
557 0f58f68b Gerd Hoffmann
558 0f58f68b Gerd Hoffmann
static void usb_uas_scsi_transfer_data(SCSIRequest *r, uint32_t len)
559 0f58f68b Gerd Hoffmann
{
560 0f58f68b Gerd Hoffmann
    UASRequest *req = r->hba_private;
561 0f58f68b Gerd Hoffmann
562 0f58f68b Gerd Hoffmann
    trace_usb_uas_scsi_data(req->uas->dev.addr, req->tag, len);
563 0f58f68b Gerd Hoffmann
    req->buf_off = 0;
564 0f58f68b Gerd Hoffmann
    req->buf_size = len;
565 0f58f68b Gerd Hoffmann
    if (req->data) {
566 0f58f68b Gerd Hoffmann
        usb_uas_copy_data(req);
567 0f58f68b Gerd Hoffmann
    } else {
568 0f58f68b Gerd Hoffmann
        usb_uas_start_next_transfer(req->uas);
569 0f58f68b Gerd Hoffmann
    }
570 0f58f68b Gerd Hoffmann
}
571 0f58f68b Gerd Hoffmann
572 0f58f68b Gerd Hoffmann
static void usb_uas_scsi_command_complete(SCSIRequest *r,
573 0f58f68b Gerd Hoffmann
                                          uint32_t status, size_t resid)
574 0f58f68b Gerd Hoffmann
{
575 0f58f68b Gerd Hoffmann
    UASRequest *req = r->hba_private;
576 0f58f68b Gerd Hoffmann
577 0f58f68b Gerd Hoffmann
    trace_usb_uas_scsi_complete(req->uas->dev.addr, req->tag, status, resid);
578 0f58f68b Gerd Hoffmann
    req->complete = true;
579 0f58f68b Gerd Hoffmann
    if (req->data) {
580 0f58f68b Gerd Hoffmann
        usb_uas_complete_data_packet(req);
581 0f58f68b Gerd Hoffmann
    }
582 0f58f68b Gerd Hoffmann
    usb_uas_queue_sense(req, status);
583 0f58f68b Gerd Hoffmann
    scsi_req_unref(req->req);
584 0f58f68b Gerd Hoffmann
}
585 0f58f68b Gerd Hoffmann
586 0f58f68b Gerd Hoffmann
static void usb_uas_scsi_request_cancelled(SCSIRequest *r)
587 0f58f68b Gerd Hoffmann
{
588 0f58f68b Gerd Hoffmann
    UASRequest *req = r->hba_private;
589 0f58f68b Gerd Hoffmann
590 0f58f68b Gerd Hoffmann
    /* FIXME: queue notification to status pipe? */
591 0f58f68b Gerd Hoffmann
    scsi_req_unref(req->req);
592 0f58f68b Gerd Hoffmann
}
593 0f58f68b Gerd Hoffmann
594 0f58f68b Gerd Hoffmann
static const struct SCSIBusInfo usb_uas_scsi_info = {
595 0f58f68b Gerd Hoffmann
    .tcq = true,
596 0f58f68b Gerd Hoffmann
    .max_target = 0,
597 0f58f68b Gerd Hoffmann
    .max_lun = 255,
598 0f58f68b Gerd Hoffmann
599 0f58f68b Gerd Hoffmann
    .transfer_data = usb_uas_scsi_transfer_data,
600 0f58f68b Gerd Hoffmann
    .complete = usb_uas_scsi_command_complete,
601 0f58f68b Gerd Hoffmann
    .cancel = usb_uas_scsi_request_cancelled,
602 0f58f68b Gerd Hoffmann
    .free_request = usb_uas_scsi_free_request,
603 0f58f68b Gerd Hoffmann
};
604 0f58f68b Gerd Hoffmann
605 0f58f68b Gerd Hoffmann
/* --------------------------------------------------------------------- */
606 0f58f68b Gerd Hoffmann
607 0f58f68b Gerd Hoffmann
static void usb_uas_handle_reset(USBDevice *dev)
608 0f58f68b Gerd Hoffmann
{
609 0f58f68b Gerd Hoffmann
    UASDevice *uas = DO_UPCAST(UASDevice, dev, dev);
610 0f58f68b Gerd Hoffmann
    UASRequest *req, *nreq;
611 0f58f68b Gerd Hoffmann
    UASStatus *st, *nst;
612 0f58f68b Gerd Hoffmann
613 0f58f68b Gerd Hoffmann
    trace_usb_uas_reset(dev->addr);
614 0f58f68b Gerd Hoffmann
    QTAILQ_FOREACH_SAFE(req, &uas->requests, next, nreq) {
615 0f58f68b Gerd Hoffmann
        scsi_req_cancel(req->req);
616 0f58f68b Gerd Hoffmann
    }
617 0f58f68b Gerd Hoffmann
    QTAILQ_FOREACH_SAFE(st, &uas->results, next, nst) {
618 0f58f68b Gerd Hoffmann
        QTAILQ_REMOVE(&uas->results, st, next);
619 0f58f68b Gerd Hoffmann
        g_free(st);
620 0f58f68b Gerd Hoffmann
    }
621 0f58f68b Gerd Hoffmann
}
622 0f58f68b Gerd Hoffmann
623 9a77a0f5 Hans de Goede
static void usb_uas_handle_control(USBDevice *dev, USBPacket *p,
624 0f58f68b Gerd Hoffmann
               int request, int value, int index, int length, uint8_t *data)
625 0f58f68b Gerd Hoffmann
{
626 0f58f68b Gerd Hoffmann
    int ret;
627 0f58f68b Gerd Hoffmann
628 0f58f68b Gerd Hoffmann
    ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
629 0f58f68b Gerd Hoffmann
    if (ret >= 0) {
630 9a77a0f5 Hans de Goede
        return;
631 0f58f68b Gerd Hoffmann
    }
632 0f58f68b Gerd Hoffmann
    fprintf(stderr, "%s: unhandled control request\n", __func__);
633 9a77a0f5 Hans de Goede
    p->status = USB_RET_STALL;
634 0f58f68b Gerd Hoffmann
}
635 0f58f68b Gerd Hoffmann
636 0f58f68b Gerd Hoffmann
static void usb_uas_cancel_io(USBDevice *dev, USBPacket *p)
637 0f58f68b Gerd Hoffmann
{
638 0f58f68b Gerd Hoffmann
    UASDevice *uas = DO_UPCAST(UASDevice, dev, dev);
639 0f58f68b Gerd Hoffmann
    UASRequest *req, *nreq;
640 89a453d4 Gerd Hoffmann
    int i;
641 0f58f68b Gerd Hoffmann
642 89a453d4 Gerd Hoffmann
    if (uas->status2 == p) {
643 89a453d4 Gerd Hoffmann
        uas->status2 = NULL;
644 0f58f68b Gerd Hoffmann
        qemu_bh_cancel(uas->status_bh);
645 0f58f68b Gerd Hoffmann
        return;
646 0f58f68b Gerd Hoffmann
    }
647 89a453d4 Gerd Hoffmann
    if (uas_using_streams(uas)) {
648 89a453d4 Gerd Hoffmann
        for (i = 0; i < UAS_MAX_STREAMS; i++) {
649 89a453d4 Gerd Hoffmann
            if (uas->status3[i] == p) {
650 89a453d4 Gerd Hoffmann
                uas->status3[i] = NULL;
651 89a453d4 Gerd Hoffmann
                return;
652 89a453d4 Gerd Hoffmann
            }
653 89a453d4 Gerd Hoffmann
            if (uas->data3[i] == p) {
654 89a453d4 Gerd Hoffmann
                uas->data3[i] = NULL;
655 89a453d4 Gerd Hoffmann
                return;
656 89a453d4 Gerd Hoffmann
            }
657 89a453d4 Gerd Hoffmann
        }
658 89a453d4 Gerd Hoffmann
    }
659 0f58f68b Gerd Hoffmann
    QTAILQ_FOREACH_SAFE(req, &uas->requests, next, nreq) {
660 0f58f68b Gerd Hoffmann
        if (req->data == p) {
661 0f58f68b Gerd Hoffmann
            req->data = NULL;
662 0f58f68b Gerd Hoffmann
            return;
663 0f58f68b Gerd Hoffmann
        }
664 0f58f68b Gerd Hoffmann
    }
665 0f58f68b Gerd Hoffmann
    assert(!"canceled usb packet not found");
666 0f58f68b Gerd Hoffmann
}
667 0f58f68b Gerd Hoffmann
668 0f58f68b Gerd Hoffmann
static void usb_uas_command(UASDevice *uas, uas_ui *ui)
669 0f58f68b Gerd Hoffmann
{
670 0f58f68b Gerd Hoffmann
    UASRequest *req;
671 0f58f68b Gerd Hoffmann
    uint32_t len;
672 0f58f68b Gerd Hoffmann
673 0f58f68b Gerd Hoffmann
    req = usb_uas_find_request(uas, be16_to_cpu(ui->hdr.tag));
674 0f58f68b Gerd Hoffmann
    if (req) {
675 0f58f68b Gerd Hoffmann
        goto overlapped_tag;
676 0f58f68b Gerd Hoffmann
    }
677 0f58f68b Gerd Hoffmann
    req = usb_uas_alloc_request(uas, ui);
678 0f58f68b Gerd Hoffmann
    if (req->dev == NULL) {
679 0f58f68b Gerd Hoffmann
        goto bad_target;
680 0f58f68b Gerd Hoffmann
    }
681 0f58f68b Gerd Hoffmann
682 0f58f68b Gerd Hoffmann
    trace_usb_uas_command(uas->dev.addr, req->tag,
683 0f58f68b Gerd Hoffmann
                          usb_uas_get_lun(req->lun),
684 0f58f68b Gerd Hoffmann
                          req->lun >> 32, req->lun & 0xffffffff);
685 0f58f68b Gerd Hoffmann
    QTAILQ_INSERT_TAIL(&uas->requests, req, next);
686 89a453d4 Gerd Hoffmann
    if (uas_using_streams(uas) && uas->data3[req->tag] != NULL) {
687 89a453d4 Gerd Hoffmann
        req->data = uas->data3[req->tag];
688 89a453d4 Gerd Hoffmann
        req->data_async = true;
689 89a453d4 Gerd Hoffmann
        uas->data3[req->tag] = NULL;
690 89a453d4 Gerd Hoffmann
    }
691 89a453d4 Gerd Hoffmann
692 0f58f68b Gerd Hoffmann
    req->req = scsi_req_new(req->dev, req->tag,
693 0f58f68b Gerd Hoffmann
                            usb_uas_get_lun(req->lun),
694 0f58f68b Gerd Hoffmann
                            ui->command.cdb, req);
695 89a453d4 Gerd Hoffmann
#if 1
696 89a453d4 Gerd Hoffmann
    scsi_req_print(req->req);
697 89a453d4 Gerd Hoffmann
#endif
698 0f58f68b Gerd Hoffmann
    len = scsi_req_enqueue(req->req);
699 0f58f68b Gerd Hoffmann
    if (len) {
700 0f58f68b Gerd Hoffmann
        req->data_size = len;
701 0f58f68b Gerd Hoffmann
        scsi_req_continue(req->req);
702 0f58f68b Gerd Hoffmann
    }
703 0f58f68b Gerd Hoffmann
    return;
704 0f58f68b Gerd Hoffmann
705 0f58f68b Gerd Hoffmann
overlapped_tag:
706 0f58f68b Gerd Hoffmann
    usb_uas_queue_response(uas, req->tag, UAS_RC_OVERLAPPED_TAG, 0);
707 0f58f68b Gerd Hoffmann
    return;
708 0f58f68b Gerd Hoffmann
709 0f58f68b Gerd Hoffmann
bad_target:
710 0f58f68b Gerd Hoffmann
    /*
711 0f58f68b Gerd Hoffmann
     * FIXME: Seems to upset linux, is this wrong?
712 0f58f68b Gerd Hoffmann
     * NOTE: Happens only with no scsi devices at the bus, not sure
713 0f58f68b Gerd Hoffmann
     *       this is a valid UAS setup in the first place.
714 0f58f68b Gerd Hoffmann
     */
715 0f58f68b Gerd Hoffmann
    usb_uas_queue_response(uas, req->tag, UAS_RC_INVALID_INFO_UNIT, 0);
716 0f58f68b Gerd Hoffmann
    g_free(req);
717 0f58f68b Gerd Hoffmann
}
718 0f58f68b Gerd Hoffmann
719 0f58f68b Gerd Hoffmann
static void usb_uas_task(UASDevice *uas, uas_ui *ui)
720 0f58f68b Gerd Hoffmann
{
721 0f58f68b Gerd Hoffmann
    uint16_t tag = be16_to_cpu(ui->hdr.tag);
722 0f58f68b Gerd Hoffmann
    uint64_t lun64 = be64_to_cpu(ui->task.lun);
723 0f58f68b Gerd Hoffmann
    SCSIDevice *dev = usb_uas_get_dev(uas, lun64);
724 0f58f68b Gerd Hoffmann
    int lun = usb_uas_get_lun(lun64);
725 0f58f68b Gerd Hoffmann
    UASRequest *req;
726 0f58f68b Gerd Hoffmann
    uint16_t task_tag;
727 0f58f68b Gerd Hoffmann
728 0f58f68b Gerd Hoffmann
    req = usb_uas_find_request(uas, be16_to_cpu(ui->hdr.tag));
729 0f58f68b Gerd Hoffmann
    if (req) {
730 0f58f68b Gerd Hoffmann
        goto overlapped_tag;
731 0f58f68b Gerd Hoffmann
    }
732 0f58f68b Gerd Hoffmann
733 0f58f68b Gerd Hoffmann
    switch (ui->task.function) {
734 0f58f68b Gerd Hoffmann
    case UAS_TMF_ABORT_TASK:
735 0f58f68b Gerd Hoffmann
        task_tag = be16_to_cpu(ui->task.task_tag);
736 0f58f68b Gerd Hoffmann
        trace_usb_uas_tmf_abort_task(uas->dev.addr, tag, task_tag);
737 0f58f68b Gerd Hoffmann
        if (dev == NULL) {
738 0f58f68b Gerd Hoffmann
            goto bad_target;
739 0f58f68b Gerd Hoffmann
        }
740 0f58f68b Gerd Hoffmann
        if (dev->lun != lun) {
741 0f58f68b Gerd Hoffmann
            goto incorrect_lun;
742 0f58f68b Gerd Hoffmann
        }
743 0f58f68b Gerd Hoffmann
        req = usb_uas_find_request(uas, task_tag);
744 0f58f68b Gerd Hoffmann
        if (req && req->dev == dev) {
745 0f58f68b Gerd Hoffmann
            scsi_req_cancel(req->req);
746 0f58f68b Gerd Hoffmann
        }
747 0f58f68b Gerd Hoffmann
        usb_uas_queue_response(uas, tag, UAS_RC_TMF_COMPLETE, 0);
748 0f58f68b Gerd Hoffmann
        break;
749 0f58f68b Gerd Hoffmann
750 0f58f68b Gerd Hoffmann
    case UAS_TMF_LOGICAL_UNIT_RESET:
751 0f58f68b Gerd Hoffmann
        trace_usb_uas_tmf_logical_unit_reset(uas->dev.addr, tag, lun);
752 0f58f68b Gerd Hoffmann
        if (dev == NULL) {
753 0f58f68b Gerd Hoffmann
            goto bad_target;
754 0f58f68b Gerd Hoffmann
        }
755 0f58f68b Gerd Hoffmann
        if (dev->lun != lun) {
756 0f58f68b Gerd Hoffmann
            goto incorrect_lun;
757 0f58f68b Gerd Hoffmann
        }
758 0f58f68b Gerd Hoffmann
        qdev_reset_all(&dev->qdev);
759 0f58f68b Gerd Hoffmann
        usb_uas_queue_response(uas, tag, UAS_RC_TMF_COMPLETE, 0);
760 0f58f68b Gerd Hoffmann
        break;
761 0f58f68b Gerd Hoffmann
762 0f58f68b Gerd Hoffmann
    default:
763 0f58f68b Gerd Hoffmann
        trace_usb_uas_tmf_unsupported(uas->dev.addr, tag, ui->task.function);
764 0f58f68b Gerd Hoffmann
        usb_uas_queue_response(uas, tag, UAS_RC_TMF_NOT_SUPPORTED, 0);
765 0f58f68b Gerd Hoffmann
        break;
766 0f58f68b Gerd Hoffmann
    }
767 0f58f68b Gerd Hoffmann
    return;
768 0f58f68b Gerd Hoffmann
769 0f58f68b Gerd Hoffmann
overlapped_tag:
770 0f58f68b Gerd Hoffmann
    usb_uas_queue_response(uas, req->tag, UAS_RC_OVERLAPPED_TAG, 0);
771 0f58f68b Gerd Hoffmann
    return;
772 0f58f68b Gerd Hoffmann
773 0f58f68b Gerd Hoffmann
bad_target:
774 0f58f68b Gerd Hoffmann
    /* FIXME: correct?  [see long comment in usb_uas_command()] */
775 0f58f68b Gerd Hoffmann
    usb_uas_queue_response(uas, tag, UAS_RC_INVALID_INFO_UNIT, 0);
776 0f58f68b Gerd Hoffmann
    return;
777 0f58f68b Gerd Hoffmann
778 0f58f68b Gerd Hoffmann
incorrect_lun:
779 0f58f68b Gerd Hoffmann
    usb_uas_queue_response(uas, tag, UAS_RC_INCORRECT_LUN, 0);
780 0f58f68b Gerd Hoffmann
}
781 0f58f68b Gerd Hoffmann
782 9a77a0f5 Hans de Goede
static void usb_uas_handle_data(USBDevice *dev, USBPacket *p)
783 0f58f68b Gerd Hoffmann
{
784 0f58f68b Gerd Hoffmann
    UASDevice *uas = DO_UPCAST(UASDevice, dev, dev);
785 0f58f68b Gerd Hoffmann
    uas_ui ui;
786 0f58f68b Gerd Hoffmann
    UASStatus *st;
787 0f58f68b Gerd Hoffmann
    UASRequest *req;
788 9a77a0f5 Hans de Goede
    int length;
789 0f58f68b Gerd Hoffmann
790 0f58f68b Gerd Hoffmann
    switch (p->ep->nr) {
791 0f58f68b Gerd Hoffmann
    case UAS_PIPE_ID_COMMAND:
792 0f58f68b Gerd Hoffmann
        length = MIN(sizeof(ui), p->iov.size);
793 0f58f68b Gerd Hoffmann
        usb_packet_copy(p, &ui, length);
794 0f58f68b Gerd Hoffmann
        switch (ui.hdr.id) {
795 0f58f68b Gerd Hoffmann
        case UAS_UI_COMMAND:
796 0f58f68b Gerd Hoffmann
            usb_uas_command(uas, &ui);
797 0f58f68b Gerd Hoffmann
            break;
798 0f58f68b Gerd Hoffmann
        case UAS_UI_TASK_MGMT:
799 0f58f68b Gerd Hoffmann
            usb_uas_task(uas, &ui);
800 0f58f68b Gerd Hoffmann
            break;
801 0f58f68b Gerd Hoffmann
        default:
802 0f58f68b Gerd Hoffmann
            fprintf(stderr, "%s: unknown command ui: id 0x%x\n",
803 0f58f68b Gerd Hoffmann
                    __func__, ui.hdr.id);
804 9a77a0f5 Hans de Goede
            p->status = USB_RET_STALL;
805 0f58f68b Gerd Hoffmann
            break;
806 0f58f68b Gerd Hoffmann
        }
807 0f58f68b Gerd Hoffmann
        break;
808 0f58f68b Gerd Hoffmann
    case UAS_PIPE_ID_STATUS:
809 89a453d4 Gerd Hoffmann
        if (p->stream) {
810 89a453d4 Gerd Hoffmann
            QTAILQ_FOREACH(st, &uas->results, next) {
811 89a453d4 Gerd Hoffmann
                if (st->stream == p->stream) {
812 89a453d4 Gerd Hoffmann
                    break;
813 89a453d4 Gerd Hoffmann
                }
814 89a453d4 Gerd Hoffmann
            }
815 89a453d4 Gerd Hoffmann
            if (st == NULL) {
816 89a453d4 Gerd Hoffmann
                assert(uas->status3[p->stream] == NULL);
817 89a453d4 Gerd Hoffmann
                uas->status3[p->stream] = p;
818 89a453d4 Gerd Hoffmann
                p->status = USB_RET_ASYNC;
819 89a453d4 Gerd Hoffmann
                break;
820 89a453d4 Gerd Hoffmann
            }
821 89a453d4 Gerd Hoffmann
        } else {
822 89a453d4 Gerd Hoffmann
            st = QTAILQ_FIRST(&uas->results);
823 89a453d4 Gerd Hoffmann
            if (st == NULL) {
824 89a453d4 Gerd Hoffmann
                assert(uas->status2 == NULL);
825 89a453d4 Gerd Hoffmann
                uas->status2 = p;
826 89a453d4 Gerd Hoffmann
                p->status = USB_RET_ASYNC;
827 89a453d4 Gerd Hoffmann
                break;
828 89a453d4 Gerd Hoffmann
            }
829 0f58f68b Gerd Hoffmann
        }
830 0f58f68b Gerd Hoffmann
        usb_packet_copy(p, &st->status, st->length);
831 0f58f68b Gerd Hoffmann
        QTAILQ_REMOVE(&uas->results, st, next);
832 0f58f68b Gerd Hoffmann
        g_free(st);
833 0f58f68b Gerd Hoffmann
        break;
834 0f58f68b Gerd Hoffmann
    case UAS_PIPE_ID_DATA_IN:
835 0f58f68b Gerd Hoffmann
    case UAS_PIPE_ID_DATA_OUT:
836 89a453d4 Gerd Hoffmann
        if (p->stream) {
837 89a453d4 Gerd Hoffmann
            req = usb_uas_find_request(uas, p->stream);
838 89a453d4 Gerd Hoffmann
        } else {
839 89a453d4 Gerd Hoffmann
            req = (p->ep->nr == UAS_PIPE_ID_DATA_IN)
840 89a453d4 Gerd Hoffmann
                ? uas->datain2 : uas->dataout2;
841 89a453d4 Gerd Hoffmann
        }
842 0f58f68b Gerd Hoffmann
        if (req == NULL) {
843 89a453d4 Gerd Hoffmann
            if (p->stream) {
844 89a453d4 Gerd Hoffmann
                assert(uas->data3[p->stream] == NULL);
845 89a453d4 Gerd Hoffmann
                uas->data3[p->stream] = p;
846 89a453d4 Gerd Hoffmann
                p->status = USB_RET_ASYNC;
847 89a453d4 Gerd Hoffmann
                break;
848 89a453d4 Gerd Hoffmann
            } else {
849 89a453d4 Gerd Hoffmann
                fprintf(stderr, "%s: no inflight request\n", __func__);
850 89a453d4 Gerd Hoffmann
                p->status = USB_RET_STALL;
851 89a453d4 Gerd Hoffmann
                break;
852 89a453d4 Gerd Hoffmann
            }
853 0f58f68b Gerd Hoffmann
        }
854 0f58f68b Gerd Hoffmann
        scsi_req_ref(req->req);
855 0f58f68b Gerd Hoffmann
        req->data = p;
856 0f58f68b Gerd Hoffmann
        usb_uas_copy_data(req);
857 9a77a0f5 Hans de Goede
        if (p->actual_length == p->iov.size || req->complete) {
858 0f58f68b Gerd Hoffmann
            req->data = NULL;
859 0f58f68b Gerd Hoffmann
        } else {
860 0f58f68b Gerd Hoffmann
            req->data_async = true;
861 9a77a0f5 Hans de Goede
            p->status = USB_RET_ASYNC;
862 0f58f68b Gerd Hoffmann
        }
863 0f58f68b Gerd Hoffmann
        scsi_req_unref(req->req);
864 0f58f68b Gerd Hoffmann
        usb_uas_start_next_transfer(uas);
865 0f58f68b Gerd Hoffmann
        break;
866 0f58f68b Gerd Hoffmann
    default:
867 0f58f68b Gerd Hoffmann
        fprintf(stderr, "%s: invalid endpoint %d\n", __func__, p->ep->nr);
868 9a77a0f5 Hans de Goede
        p->status = USB_RET_STALL;
869 0f58f68b Gerd Hoffmann
        break;
870 0f58f68b Gerd Hoffmann
    }
871 0f58f68b Gerd Hoffmann
}
872 0f58f68b Gerd Hoffmann
873 0f58f68b Gerd Hoffmann
static void usb_uas_handle_destroy(USBDevice *dev)
874 0f58f68b Gerd Hoffmann
{
875 0f58f68b Gerd Hoffmann
    UASDevice *uas = DO_UPCAST(UASDevice, dev, dev);
876 0f58f68b Gerd Hoffmann
877 0f58f68b Gerd Hoffmann
    qemu_bh_delete(uas->status_bh);
878 0f58f68b Gerd Hoffmann
}
879 0f58f68b Gerd Hoffmann
880 0f58f68b Gerd Hoffmann
static int usb_uas_init(USBDevice *dev)
881 0f58f68b Gerd Hoffmann
{
882 0f58f68b Gerd Hoffmann
    UASDevice *uas = DO_UPCAST(UASDevice, dev, dev);
883 0f58f68b Gerd Hoffmann
884 0f58f68b Gerd Hoffmann
    usb_desc_create_serial(dev);
885 0f58f68b Gerd Hoffmann
    usb_desc_init(dev);
886 0f58f68b Gerd Hoffmann
887 0f58f68b Gerd Hoffmann
    QTAILQ_INIT(&uas->results);
888 0f58f68b Gerd Hoffmann
    QTAILQ_INIT(&uas->requests);
889 0f58f68b Gerd Hoffmann
    uas->status_bh = qemu_bh_new(usb_uas_send_status_bh, uas);
890 0f58f68b Gerd Hoffmann
891 11fc853c KONRAD Frederic
    scsi_bus_new(&uas->bus, &uas->dev.qdev, &usb_uas_scsi_info, NULL);
892 0f58f68b Gerd Hoffmann
893 0f58f68b Gerd Hoffmann
    return 0;
894 0f58f68b Gerd Hoffmann
}
895 0f58f68b Gerd Hoffmann
896 0f58f68b Gerd Hoffmann
static const VMStateDescription vmstate_usb_uas = {
897 0f58f68b Gerd Hoffmann
    .name = "usb-uas",
898 0f58f68b Gerd Hoffmann
    .unmigratable = 1,
899 0f58f68b Gerd Hoffmann
    .fields = (VMStateField[]) {
900 0f58f68b Gerd Hoffmann
        VMSTATE_USB_DEVICE(dev, UASDevice),
901 0f58f68b Gerd Hoffmann
        VMSTATE_END_OF_LIST()
902 0f58f68b Gerd Hoffmann
    }
903 0f58f68b Gerd Hoffmann
};
904 0f58f68b Gerd Hoffmann
905 0f58f68b Gerd Hoffmann
static void usb_uas_class_initfn(ObjectClass *klass, void *data)
906 0f58f68b Gerd Hoffmann
{
907 0f58f68b Gerd Hoffmann
    DeviceClass *dc = DEVICE_CLASS(klass);
908 0f58f68b Gerd Hoffmann
    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
909 0f58f68b Gerd Hoffmann
910 0f58f68b Gerd Hoffmann
    uc->init           = usb_uas_init;
911 0f58f68b Gerd Hoffmann
    uc->product_desc   = desc_strings[STR_PRODUCT];
912 0f58f68b Gerd Hoffmann
    uc->usb_desc       = &desc;
913 0f58f68b Gerd Hoffmann
    uc->cancel_packet  = usb_uas_cancel_io;
914 0f58f68b Gerd Hoffmann
    uc->handle_attach  = usb_desc_attach;
915 0f58f68b Gerd Hoffmann
    uc->handle_reset   = usb_uas_handle_reset;
916 0f58f68b Gerd Hoffmann
    uc->handle_control = usb_uas_handle_control;
917 0f58f68b Gerd Hoffmann
    uc->handle_data    = usb_uas_handle_data;
918 0f58f68b Gerd Hoffmann
    uc->handle_destroy = usb_uas_handle_destroy;
919 0f58f68b Gerd Hoffmann
    dc->fw_name = "storage";
920 0f58f68b Gerd Hoffmann
    dc->vmsd = &vmstate_usb_uas;
921 0f58f68b Gerd Hoffmann
}
922 0f58f68b Gerd Hoffmann
923 8c43a6f0 Andreas Färber
static const TypeInfo uas_info = {
924 0f58f68b Gerd Hoffmann
    .name          = "usb-uas",
925 0f58f68b Gerd Hoffmann
    .parent        = TYPE_USB_DEVICE,
926 0f58f68b Gerd Hoffmann
    .instance_size = sizeof(UASDevice),
927 0f58f68b Gerd Hoffmann
    .class_init    = usb_uas_class_initfn,
928 0f58f68b Gerd Hoffmann
};
929 0f58f68b Gerd Hoffmann
930 0f58f68b Gerd Hoffmann
static void usb_uas_register_types(void)
931 0f58f68b Gerd Hoffmann
{
932 0f58f68b Gerd Hoffmann
    type_register_static(&uas_info);
933 0f58f68b Gerd Hoffmann
}
934 0f58f68b Gerd Hoffmann
935 0f58f68b Gerd Hoffmann
type_init(usb_uas_register_types)