Add qemu 2.4.0
[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_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize)
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     memcpy(ui.command.cdb, cdbcmd, sizeof(ui.command.cdb));
108     int ret = usb_send_bulk(GET_GLOBALFLAT(drive_gf->command),
109                             USB_DIR_OUT, MAKE_FLATPTR(GET_SEG(SS), &ui),
110                             sizeof(ui.hdr) + sizeof(ui.command));
111     if (ret) {
112         dprintf(1, "uas: command send fail");
113         goto fail;
114     }
115
116     memset(&ui, 0xff, sizeof(ui));
117     ret = usb_send_bulk(GET_GLOBALFLAT(drive_gf->status),
118                         USB_DIR_IN, MAKE_FLATPTR(GET_SEG(SS), &ui), sizeof(ui));
119     if (ret) {
120         dprintf(1, "uas: status recv fail");
121         goto fail;
122     }
123
124     switch (ui.hdr.id) {
125     case UAS_UI_SENSE:
126         goto have_sense;
127     case UAS_UI_READ_READY:
128         ret = usb_send_bulk(GET_GLOBALFLAT(drive_gf->data_in),
129                             USB_DIR_IN, op->buf_fl, op->count * blocksize);
130         if (ret) {
131             dprintf(1, "uas: data read fail");
132             goto fail;
133         }
134         break;
135     case UAS_UI_WRITE_READY:
136         ret = usb_send_bulk(GET_GLOBALFLAT(drive_gf->data_out),
137                             USB_DIR_OUT, op->buf_fl, op->count * blocksize);
138         if (ret) {
139             dprintf(1, "uas: data write fail");
140             goto fail;
141         }
142         break;
143     default:
144         dprintf(1, "uas: unknown status ui id %d", ui.hdr.id);
145         goto fail;
146     }
147
148     memset(&ui, 0xff, sizeof(ui));
149     ret = usb_send_bulk(GET_GLOBALFLAT(drive_gf->status),
150                         USB_DIR_IN, MAKE_FLATPTR(GET_SEG(SS), &ui), sizeof(ui));
151     if (ret) {
152         dprintf(1, "uas: status recv fail");
153         goto fail;
154     }
155     if (ui.hdr.id != UAS_UI_SENSE) {
156         dprintf(1, "uas: expected sense ui, got ui id %d", ui.hdr.id);
157         goto fail;
158     }
159
160 have_sense:
161     if (ui.sense.status == 0) {
162         return DISK_RET_SUCCESS;
163     }
164
165 fail:
166     return DISK_RET_EBADTRACK;
167 }
168
169 static int
170 uas_lun_setup(struct usbdevice_s *usbdev,
171               struct usb_pipe *command, struct usb_pipe *status,
172               struct usb_pipe *data_in, struct usb_pipe *data_out,
173               int lun)
174 {
175     // Allocate drive structure.
176     struct uasdrive_s *drive = malloc_fseg(sizeof(*drive));
177     if (!drive) {
178         warn_noalloc();
179         return -1;
180     }
181     memset(drive, 0, sizeof(*drive));
182     if (usb_32bit_pipe(data_in))
183         drive->drive.type = DTYPE_UAS_32;
184     else
185         drive->drive.type = DTYPE_UAS;
186     drive->command = command;
187     drive->status = status;
188     drive->data_in = data_in;
189     drive->data_out = data_out;
190     drive->lun = lun;
191
192     int prio = bootprio_find_usb(usbdev, lun);
193     int ret = scsi_drive_setup(&drive->drive, "USB UAS", prio);
194     if (ret) {
195         free(drive);
196         return -1;
197     }
198     return 0;
199 }
200
201 int
202 usb_uas_setup(struct usbdevice_s *usbdev)
203 {
204     if (!CONFIG_USB_UAS)
205         return -1;
206
207     // Verify right kind of device
208     struct usb_interface_descriptor *iface = usbdev->iface;
209     if (iface->bInterfaceSubClass != US_SC_SCSI ||
210         iface->bInterfaceProtocol != US_PR_UAS) {
211         dprintf(1, "Unsupported UAS device (subclass=%02x proto=%02x)\n"
212                 , iface->bInterfaceSubClass, iface->bInterfaceProtocol);
213         return -1;
214     }
215
216     /* find & allocate pipes */
217     struct usb_endpoint_descriptor *ep = NULL;
218     struct usb_pipe *command = NULL;
219     struct usb_pipe *status = NULL;
220     struct usb_pipe *data_in = NULL;
221     struct usb_pipe *data_out = NULL;
222     u8 *desc = (u8*)iface;
223     while (desc) {
224         desc += desc[0];
225         switch (desc[1]) {
226         case USB_DT_ENDPOINT:
227             ep = (void*)desc;
228             break;
229         case USB_DT_ENDPOINT_COMPANION:
230             /* No support (yet) for usb3 streams */
231             dprintf(1, "Superspeed UAS devices not supported (yet)\n");
232             goto fail;
233         case 0x24:
234             switch (desc[2]) {
235             case UAS_PIPE_ID_COMMAND:
236                 command = usb_alloc_pipe(usbdev, ep);
237                 break;
238             case UAS_PIPE_ID_STATUS:
239                 status = usb_alloc_pipe(usbdev, ep);
240                 break;
241             case UAS_PIPE_ID_DATA_IN:
242                 data_in = usb_alloc_pipe(usbdev, ep);
243                 break;
244             case UAS_PIPE_ID_DATA_OUT:
245                 data_out = usb_alloc_pipe(usbdev, ep);
246                 break;
247             default:
248                 goto fail;
249             }
250             break;
251         default:
252             desc = NULL;
253             break;
254         }
255     }
256     if (!command || !status || !data_in || !data_out)
257         goto fail;
258
259     /* TODO: send REPORT LUNS.  For now, only LUN 0 is recognized.  */
260     int ret = uas_lun_setup(usbdev, command, status, data_in, data_out, 0);
261     if (ret < 0) {
262         dprintf(1, "Unable to configure UAS drive.\n");
263         goto fail;
264     }
265
266     return 0;
267
268 fail:
269     usb_free_pipe(usbdev, command);
270     usb_free_pipe(usbdev, status);
271     usb_free_pipe(usbdev, data_in);
272     usb_free_pipe(usbdev, data_out);
273     return -1;
274 }