1 // Code for handling usb attached scsi devices.
3 // only usb 2.0 for now.
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.
10 // Gerd Hoffmann <kraxel@redhat.com>
12 // based on usb-msc.c which is written by:
13 // Kevin O'Connor <kevin@koconnor.net>
15 // This file may be distributed under the terms of the GNU LGPLv3 license.
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
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
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
45 } PACKED uas_ui_header;
48 u8 prio_taskattr; /* 6:3 priority, 2:0 task attribute */
50 u8 add_cdb_length; /* 7:2 additional adb length (dwords) */
55 } PACKED uas_ui_command;
63 } PACKED uas_ui_sense;
66 u16 add_response_info;
68 } PACKED uas_ui_response;
75 } PACKED uas_ui_task_mgmt;
80 uas_ui_command command;
82 uas_ui_task_mgmt task;
83 uas_ui_response response;
89 struct usb_pipe *command, *status, *data_in, *data_out;
94 uas_process_op(struct disk_op_s *op)
97 return DISK_RET_EBADTRACK;
99 struct uasdrive_s *drive_gf = container_of(
100 op->drive_gf, struct uasdrive_s, drive);
103 memset(&ui, 0, sizeof(ui));
104 ui.hdr.id = UAS_UI_COMMAND;
106 ui.command.lun[1] = GET_GLOBALFLAT(drive_gf->lun);
107 int blocksize = scsi_fill_cmd(op, ui.command.cdb, sizeof(ui.command.cdb));
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));
114 dprintf(1, "uas: command send fail");
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));
122 dprintf(1, "uas: status recv fail");
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);
133 dprintf(1, "uas: data read fail");
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);
141 dprintf(1, "uas: data write fail");
146 dprintf(1, "uas: unknown status ui id %d", ui.hdr.id);
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));
154 dprintf(1, "uas: status recv fail");
157 if (ui.hdr.id != UAS_UI_SENSE) {
158 dprintf(1, "uas: expected sense ui, got ui id %d", ui.hdr.id);
163 if (ui.sense.status == 0) {
164 return DISK_RET_SUCCESS;
168 return DISK_RET_EBADTRACK;
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,
177 // Allocate drive structure.
178 struct uasdrive_s *drive = malloc_fseg(sizeof(*drive));
183 memset(drive, 0, sizeof(*drive));
184 if (usb_32bit_pipe(data_in))
185 drive->drive.type = DTYPE_UAS_32;
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;
194 int prio = bootprio_find_usb(usbdev, lun);
195 int ret = scsi_drive_setup(&drive->drive, "USB UAS", prio);
204 usb_uas_setup(struct usbdevice_s *usbdev)
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);
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;
228 case USB_DT_ENDPOINT:
231 case USB_DT_ENDPOINT_COMPANION:
232 /* No support (yet) for usb3 streams */
233 dprintf(1, "Superspeed UAS devices not supported (yet)\n");
237 case UAS_PIPE_ID_COMMAND:
238 command = usb_alloc_pipe(usbdev, ep);
240 case UAS_PIPE_ID_STATUS:
241 status = usb_alloc_pipe(usbdev, ep);
243 case UAS_PIPE_ID_DATA_IN:
244 data_in = usb_alloc_pipe(usbdev, ep);
246 case UAS_PIPE_ID_DATA_OUT:
247 data_out = usb_alloc_pipe(usbdev, ep);
258 if (!command || !status || !data_in || !data_out)
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);
264 dprintf(1, "Unable to configure UAS drive.\n");
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);