These changes are the raw update to qemu-2.6.
[kvmfornfv.git] / qemu / roms / seabios / src / hw / usb-uas.c
1 // Code for handling usb attached scsi devices.
2 //
3 // only usb 2.0 for now.
4 //
5 // once we have xhci driver with usb 3.0 support this must
6 // be updated to use usb3 streams so booting from usb3
7 // devices actually works.
8 //
9 // Authors:
10 //  Gerd Hoffmann <kraxel@redhat.com>
11 //
12 // based on usb-msc.c which is written by:
13 //  Kevin O'Connor <kevin@koconnor.net>
14 //
15 // This file may be distributed under the terms of the GNU LGPLv3 license.
16
17 #include "biosvar.h" // GET_GLOBALFLAT
18 #include "block.h" // DTYPE_USB
19 #include "blockcmd.h" // cdb_read
20 #include "config.h" // CONFIG_USB_UAS
21 #include "malloc.h" // free
22 #include "output.h" // dprintf
23 #include "std/disk.h" // DISK_RET_SUCCESS
24 #include "string.h" // memset
25 #include "usb.h" // struct usb_s
26 #include "usb-uas.h" // usb_uas_init
27 #include "util.h" // bootprio_find_usb
28
29 #define UAS_UI_COMMAND              0x01
30 #define UAS_UI_SENSE                0x03
31 #define UAS_UI_RESPONSE             0x04
32 #define UAS_UI_TASK_MGMT            0x05
33 #define UAS_UI_READ_READY           0x06
34 #define UAS_UI_WRITE_READY          0x07
35
36 #define UAS_PIPE_ID_COMMAND         0x01
37 #define UAS_PIPE_ID_STATUS          0x02
38 #define UAS_PIPE_ID_DATA_IN         0x03
39 #define UAS_PIPE_ID_DATA_OUT        0x04
40
41 typedef struct {
42     u8    id;
43     u8    reserved;
44     u16   tag;
45 } PACKED  uas_ui_header;
46
47 typedef struct {
48     u8    prio_taskattr;   /* 6:3 priority, 2:0 task attribute   */
49     u8    reserved_1;
50     u8    add_cdb_length;  /* 7:2 additional adb length (dwords) */
51     u8    reserved_2;
52     u8    lun[8];
53     u8    cdb[16];
54     u8    add_cdb[];
55 } PACKED  uas_ui_command;
56
57 typedef struct {
58     u16   status_qualifier;
59     u8    status;
60     u8    reserved[7];
61     u16   sense_length;
62     u8    sense_data[18];
63 } PACKED  uas_ui_sense;
64
65 typedef struct {
66     u16   add_response_info;
67     u8    response_code;
68 } PACKED  uas_ui_response;
69
70 typedef struct {
71     u8    function;
72     u8    reserved;
73     u16   task_tag;
74     u8    lun[8];
75 } PACKED  uas_ui_task_mgmt;
76
77 typedef struct {
78     uas_ui_header  hdr;
79     union {
80         uas_ui_command   command;
81         uas_ui_sense     sense;
82         uas_ui_task_mgmt task;
83         uas_ui_response  response;
84     };
85 } PACKED  uas_ui;
86
87 struct uasdrive_s {
88     struct drive_s drive;
89     struct usb_pipe *command, *status, *data_in, *data_out;
90     int lun;
91 };
92
93 int
94 uas_process_op(struct disk_op_s *op)
95 {
96     if (!CONFIG_USB_UAS)
97         return DISK_RET_EBADTRACK;
98
99     struct uasdrive_s *drive_gf = container_of(
100         op->drive_gf, struct uasdrive_s, drive);
101
102     uas_ui ui;
103     memset(&ui, 0, sizeof(ui));
104     ui.hdr.id = UAS_UI_COMMAND;
105     ui.hdr.tag = 0xdead;
106     ui.command.lun[1] = GET_GLOBALFLAT(drive_gf->lun);
107     int blocksize = scsi_fill_cmd(op, ui.command.cdb, sizeof(ui.command.cdb));
108     if (blocksize < 0)
109         return default_process_op(op);
110     int ret = usb_send_bulk(GET_GLOBALFLAT(drive_gf->command),
111                             USB_DIR_OUT, MAKE_FLATPTR(GET_SEG(SS), &ui),
112                             sizeof(ui.hdr) + sizeof(ui.command));
113     if (ret) {
114         dprintf(1, "uas: command send fail");
115         goto fail;
116     }
117
118     memset(&ui, 0xff, sizeof(ui));
119     ret = usb_send_bulk(GET_GLOBALFLAT(drive_gf->status),
120                         USB_DIR_IN, MAKE_FLATPTR(GET_SEG(SS), &ui), sizeof(ui));
121     if (ret) {
122         dprintf(1, "uas: status recv fail");
123         goto fail;
124     }
125
126     switch (ui.hdr.id) {
127     case UAS_UI_SENSE:
128         goto have_sense;
129     case UAS_UI_READ_READY:
130         ret = usb_send_bulk(GET_GLOBALFLAT(drive_gf->data_in),
131                             USB_DIR_IN, op->buf_fl, op->count * blocksize);
132         if (ret) {
133             dprintf(1, "uas: data read fail");
134             goto fail;
135         }
136         break;
137     case UAS_UI_WRITE_READY:
138         ret = usb_send_bulk(GET_GLOBALFLAT(drive_gf->data_out),
139                             USB_DIR_OUT, op->buf_fl, op->count * blocksize);
140         if (ret) {
141             dprintf(1, "uas: data write fail");
142             goto fail;
143         }
144         break;
145     default:
146         dprintf(1, "uas: unknown status ui id %d", ui.hdr.id);
147         goto fail;
148     }
149
150     memset(&ui, 0xff, sizeof(ui));
151     ret = usb_send_bulk(GET_GLOBALFLAT(drive_gf->status),
152                         USB_DIR_IN, MAKE_FLATPTR(GET_SEG(SS), &ui), sizeof(ui));
153     if (ret) {
154         dprintf(1, "uas: status recv fail");
155         goto fail;
156     }
157     if (ui.hdr.id != UAS_UI_SENSE) {
158         dprintf(1, "uas: expected sense ui, got ui id %d", ui.hdr.id);
159         goto fail;
160     }
161
162 have_sense:
163     if (ui.sense.status == 0) {
164         return DISK_RET_SUCCESS;
165     }
166
167 fail:
168     return DISK_RET_EBADTRACK;
169 }
170
171 static int
172 uas_lun_setup(struct usbdevice_s *usbdev,
173               struct usb_pipe *command, struct usb_pipe *status,
174               struct usb_pipe *data_in, struct usb_pipe *data_out,
175               int lun)
176 {
177     // Allocate drive structure.
178     struct uasdrive_s *drive = malloc_fseg(sizeof(*drive));
179     if (!drive) {
180         warn_noalloc();
181         return -1;
182     }
183     memset(drive, 0, sizeof(*drive));
184     if (usb_32bit_pipe(data_in))
185         drive->drive.type = DTYPE_UAS_32;
186     else
187         drive->drive.type = DTYPE_UAS;
188     drive->command = command;
189     drive->status = status;
190     drive->data_in = data_in;
191     drive->data_out = data_out;
192     drive->lun = lun;
193
194     int prio = bootprio_find_usb(usbdev, lun);
195     int ret = scsi_drive_setup(&drive->drive, "USB UAS", prio);
196     if (ret) {
197         free(drive);
198         return -1;
199     }
200     return 0;
201 }
202
203 int
204 usb_uas_setup(struct usbdevice_s *usbdev)
205 {
206     if (!CONFIG_USB_UAS)
207         return -1;
208
209     // Verify right kind of device
210     struct usb_interface_descriptor *iface = usbdev->iface;
211     if (iface->bInterfaceSubClass != US_SC_SCSI ||
212         iface->bInterfaceProtocol != US_PR_UAS) {
213         dprintf(1, "Unsupported UAS device (subclass=%02x proto=%02x)\n"
214                 , iface->bInterfaceSubClass, iface->bInterfaceProtocol);
215         return -1;
216     }
217
218     /* find & allocate pipes */
219     struct usb_endpoint_descriptor *ep = NULL;
220     struct usb_pipe *command = NULL;
221     struct usb_pipe *status = NULL;
222     struct usb_pipe *data_in = NULL;
223     struct usb_pipe *data_out = NULL;
224     u8 *desc = (u8*)iface;
225     while (desc) {
226         desc += desc[0];
227         switch (desc[1]) {
228         case USB_DT_ENDPOINT:
229             ep = (void*)desc;
230             break;
231         case USB_DT_ENDPOINT_COMPANION:
232             /* No support (yet) for usb3 streams */
233             dprintf(1, "Superspeed UAS devices not supported (yet)\n");
234             goto fail;
235         case 0x24:
236             switch (desc[2]) {
237             case UAS_PIPE_ID_COMMAND:
238                 command = usb_alloc_pipe(usbdev, ep);
239                 break;
240             case UAS_PIPE_ID_STATUS:
241                 status = usb_alloc_pipe(usbdev, ep);
242                 break;
243             case UAS_PIPE_ID_DATA_IN:
244                 data_in = usb_alloc_pipe(usbdev, ep);
245                 break;
246             case UAS_PIPE_ID_DATA_OUT:
247                 data_out = usb_alloc_pipe(usbdev, ep);
248                 break;
249             default:
250                 goto fail;
251             }
252             break;
253         default:
254             desc = NULL;
255             break;
256         }
257     }
258     if (!command || !status || !data_in || !data_out)
259         goto fail;
260
261     /* TODO: send REPORT LUNS.  For now, only LUN 0 is recognized.  */
262     int ret = uas_lun_setup(usbdev, command, status, data_in, data_out, 0);
263     if (ret < 0) {
264         dprintf(1, "Unable to configure UAS drive.\n");
265         goto fail;
266     }
267
268     return 0;
269
270 fail:
271     usb_free_pipe(usbdev, command);
272     usb_free_pipe(usbdev, status);
273     usb_free_pipe(usbdev, data_in);
274     usb_free_pipe(usbdev, data_out);
275     return -1;
276 }