blob: 9474383133a11e861f14cf42dcc13e76c5ed0bf3 [file] [log] [blame]
Gerd Hoffmanne53e30d2012-07-20 10:59:24 +02001// 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
Kevin O'Connor1902c942013-10-26 11:48:06 -040017#include "biosvar.h" // GET_GLOBALFLAT
Kevin O'Connor135f3f62013-09-14 23:57:26 -040018#include "block.h" // DTYPE_USB
Gerd Hoffmanne53e30d2012-07-20 10:59:24 +020019#include "blockcmd.h" // cdb_read
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040020#include "config.h" // CONFIG_USB_UAS
Kevin O'Connor9dea5902013-09-14 20:23:54 -040021#include "malloc.h" // free
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040022#include "output.h" // dprintf
Kevin O'Connor135f3f62013-09-14 23:57:26 -040023#include "std/disk.h" // DISK_RET_SUCCESS
Kevin O'Connorfa9c66a2013-09-14 19:10:40 -040024#include "string.h" // memset
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040025#include "usb.h" // struct usb_s
26#include "usb-uas.h" // usb_uas_init
Kevin O'Connor07cf73b2013-09-15 00:31:17 -040027#include "util.h" // bootprio_find_usb
Gerd Hoffmanne53e30d2012-07-20 10:59:24 +020028
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
41typedef struct {
42 u8 id;
43 u8 reserved;
44 u16 tag;
45} PACKED uas_ui_header;
46
47typedef 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
57typedef 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
65typedef struct {
66 u16 add_response_info;
67 u8 response_code;
68} PACKED uas_ui_response;
69
70typedef struct {
71 u8 function;
72 u8 reserved;
73 u16 task_tag;
74 u8 lun[8];
75} PACKED uas_ui_task_mgmt;
76
77typedef 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
87struct uasdrive_s {
88 struct drive_s drive;
89 struct usb_pipe *command, *status, *data_in, *data_out;
90 int lun;
91};
92
93int
94uas_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize)
95{
96 if (!CONFIG_USB_UAS)
97 return DISK_RET_EBADTRACK;
98
Kevin O'Connor1902c942013-10-26 11:48:06 -040099 struct uasdrive_s *drive_gf = container_of(
100 op->drive_gf, struct uasdrive_s, drive);
Gerd Hoffmanne53e30d2012-07-20 10:59:24 +0200101
102 uas_ui ui;
103 memset(&ui, 0, sizeof(ui));
104 ui.hdr.id = UAS_UI_COMMAND;
105 ui.hdr.tag = 0xdead;
Kevin O'Connor1902c942013-10-26 11:48:06 -0400106 ui.command.lun[1] = GET_GLOBALFLAT(drive_gf->lun);
Gerd Hoffmanne53e30d2012-07-20 10:59:24 +0200107 memcpy(ui.command.cdb, cdbcmd, sizeof(ui.command.cdb));
Kevin O'Connor1902c942013-10-26 11:48:06 -0400108 int ret = usb_send_bulk(GET_GLOBALFLAT(drive_gf->command),
Gerd Hoffmanne53e30d2012-07-20 10:59:24 +0200109 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));
Kevin O'Connor1902c942013-10-26 11:48:06 -0400117 ret = usb_send_bulk(GET_GLOBALFLAT(drive_gf->status),
Gerd Hoffmanne53e30d2012-07-20 10:59:24 +0200118 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:
Kevin O'Connor1902c942013-10-26 11:48:06 -0400128 ret = usb_send_bulk(GET_GLOBALFLAT(drive_gf->data_in),
Gerd Hoffmanne53e30d2012-07-20 10:59:24 +0200129 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:
Kevin O'Connor1902c942013-10-26 11:48:06 -0400136 ret = usb_send_bulk(GET_GLOBALFLAT(drive_gf->data_out),
Gerd Hoffmanne53e30d2012-07-20 10:59:24 +0200137 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));
Kevin O'Connor1902c942013-10-26 11:48:06 -0400149 ret = usb_send_bulk(GET_GLOBALFLAT(drive_gf->status),
Gerd Hoffmanne53e30d2012-07-20 10:59:24 +0200150 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
160have_sense:
161 if (ui.sense.status == 0) {
162 return DISK_RET_SUCCESS;
163 }
164
165fail:
166 return DISK_RET_EBADTRACK;
167}
168
169static int
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500170uas_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)
Gerd Hoffmanne53e30d2012-07-20 10:59:24 +0200174{
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));
Kevin O'Connorde30dad2013-12-30 22:09:04 -0500182 if (usb_32bit_pipe(data_in))
183 drive->drive.type = DTYPE_UAS_32;
184 else
185 drive->drive.type = DTYPE_UAS;
Gerd Hoffmanne53e30d2012-07-20 10:59:24 +0200186 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);
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500193 int ret = scsi_drive_setup(&drive->drive, "USB UAS", prio);
Gerd Hoffmanne53e30d2012-07-20 10:59:24 +0200194 if (ret) {
195 free(drive);
196 return -1;
197 }
198 return 0;
199}
200
201int
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500202usb_uas_setup(struct usbdevice_s *usbdev)
Gerd Hoffmanne53e30d2012-07-20 10:59:24 +0200203{
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;
Gerd Hoffmannee9b84f2013-06-13 14:23:08 +0200229 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;
Gerd Hoffmanne53e30d2012-07-20 10:59:24 +0200233 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. */
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500260 int ret = uas_lun_setup(usbdev, command, status, data_in, data_out, 0);
Gerd Hoffmanne53e30d2012-07-20 10:59:24 +0200261 if (ret < 0) {
262 dprintf(1, "Unable to configure UAS drive.\n");
263 goto fail;
264 }
265
266 return 0;
267
268fail:
269 free_pipe(command);
270 free_pipe(status);
271 free_pipe(data_in);
272 free_pipe(data_out);
273 return -1;
274}