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