blob: be153ed590696b0eb17a8903cff2876563befd14 [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
17#include "util.h" // dprintf
18#include "config.h" // CONFIG_USB_UAS
19#include "usb.h" // struct usb_s
20#include "biosvar.h" // GET_GLOBAL
21#include "blockcmd.h" // cdb_read
22#include "disk.h" // DTYPE_UAS
23#include "boot.h" // bootprio_find_usb
24#include "usb-uas.h" // usb_uas_init
25
26#define UAS_UI_COMMAND 0x01
27#define UAS_UI_SENSE 0x03
28#define UAS_UI_RESPONSE 0x04
29#define UAS_UI_TASK_MGMT 0x05
30#define UAS_UI_READ_READY 0x06
31#define UAS_UI_WRITE_READY 0x07
32
33#define UAS_PIPE_ID_COMMAND 0x01
34#define UAS_PIPE_ID_STATUS 0x02
35#define UAS_PIPE_ID_DATA_IN 0x03
36#define UAS_PIPE_ID_DATA_OUT 0x04
37
38typedef struct {
39 u8 id;
40 u8 reserved;
41 u16 tag;
42} PACKED uas_ui_header;
43
44typedef struct {
45 u8 prio_taskattr; /* 6:3 priority, 2:0 task attribute */
46 u8 reserved_1;
47 u8 add_cdb_length; /* 7:2 additional adb length (dwords) */
48 u8 reserved_2;
49 u8 lun[8];
50 u8 cdb[16];
51 u8 add_cdb[];
52} PACKED uas_ui_command;
53
54typedef struct {
55 u16 status_qualifier;
56 u8 status;
57 u8 reserved[7];
58 u16 sense_length;
59 u8 sense_data[18];
60} PACKED uas_ui_sense;
61
62typedef struct {
63 u16 add_response_info;
64 u8 response_code;
65} PACKED uas_ui_response;
66
67typedef struct {
68 u8 function;
69 u8 reserved;
70 u16 task_tag;
71 u8 lun[8];
72} PACKED uas_ui_task_mgmt;
73
74typedef struct {
75 uas_ui_header hdr;
76 union {
77 uas_ui_command command;
78 uas_ui_sense sense;
79 uas_ui_task_mgmt task;
80 uas_ui_response response;
81 };
82} PACKED uas_ui;
83
84struct uasdrive_s {
85 struct drive_s drive;
86 struct usb_pipe *command, *status, *data_in, *data_out;
87 int lun;
88};
89
90int
91uas_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize)
92{
93 if (!CONFIG_USB_UAS)
94 return DISK_RET_EBADTRACK;
95
96 struct uasdrive_s *drive = container_of(
97 op->drive_g, struct uasdrive_s, drive);
98
99 uas_ui ui;
100 memset(&ui, 0, sizeof(ui));
101 ui.hdr.id = UAS_UI_COMMAND;
102 ui.hdr.tag = 0xdead;
103 ui.command.lun[1] = drive->lun;
104 memcpy(ui.command.cdb, cdbcmd, sizeof(ui.command.cdb));
105 int ret = usb_send_bulk(GET_GLOBAL(drive->command),
106 USB_DIR_OUT, MAKE_FLATPTR(GET_SEG(SS), &ui),
107 sizeof(ui.hdr) + sizeof(ui.command));
108 if (ret) {
109 dprintf(1, "uas: command send fail");
110 goto fail;
111 }
112
113 memset(&ui, 0xff, sizeof(ui));
114 ret = usb_send_bulk(GET_GLOBAL(drive->status),
115 USB_DIR_IN, MAKE_FLATPTR(GET_SEG(SS), &ui), sizeof(ui));
116 if (ret) {
117 dprintf(1, "uas: status recv fail");
118 goto fail;
119 }
120
121 switch (ui.hdr.id) {
122 case UAS_UI_SENSE:
123 goto have_sense;
124 case UAS_UI_READ_READY:
125 ret = usb_send_bulk(GET_GLOBAL(drive->data_in),
126 USB_DIR_IN, op->buf_fl, op->count * blocksize);
127 if (ret) {
128 dprintf(1, "uas: data read fail");
129 goto fail;
130 }
131 break;
132 case UAS_UI_WRITE_READY:
133 ret = usb_send_bulk(GET_GLOBAL(drive->data_out),
134 USB_DIR_OUT, op->buf_fl, op->count * blocksize);
135 if (ret) {
136 dprintf(1, "uas: data write fail");
137 goto fail;
138 }
139 break;
140 default:
141 dprintf(1, "uas: unknown status ui id %d", ui.hdr.id);
142 goto fail;
143 }
144
145 memset(&ui, 0xff, sizeof(ui));
146 ret = usb_send_bulk(GET_GLOBAL(drive->status),
147 USB_DIR_IN, MAKE_FLATPTR(GET_SEG(SS), &ui), sizeof(ui));
148 if (ret) {
149 dprintf(1, "uas: status recv fail");
150 goto fail;
151 }
152 if (ui.hdr.id != UAS_UI_SENSE) {
153 dprintf(1, "uas: expected sense ui, got ui id %d", ui.hdr.id);
154 goto fail;
155 }
156
157have_sense:
158 if (ui.sense.status == 0) {
159 return DISK_RET_SUCCESS;
160 }
161
162fail:
163 return DISK_RET_EBADTRACK;
164}
165
166static int
167uas_init_lun(struct usbdevice_s *usbdev,
168 struct usb_pipe *command, struct usb_pipe *status,
169 struct usb_pipe *data_in, struct usb_pipe *data_out,
170 int lun)
171{
172 // Allocate drive structure.
173 struct uasdrive_s *drive = malloc_fseg(sizeof(*drive));
174 if (!drive) {
175 warn_noalloc();
176 return -1;
177 }
178 memset(drive, 0, sizeof(*drive));
179 drive->drive.type = DTYPE_UAS;
180 drive->command = command;
181 drive->status = status;
182 drive->data_in = data_in;
183 drive->data_out = data_out;
184 drive->lun = lun;
185
186 int prio = bootprio_find_usb(usbdev, lun);
187 int ret = scsi_init_drive(&drive->drive, "USB UAS", prio);
188 if (ret) {
189 free(drive);
190 return -1;
191 }
192 return 0;
193}
194
195int
196usb_uas_init(struct usbdevice_s *usbdev)
197{
198 if (!CONFIG_USB_UAS)
199 return -1;
200
201 // Verify right kind of device
202 struct usb_interface_descriptor *iface = usbdev->iface;
203 if (iface->bInterfaceSubClass != US_SC_SCSI ||
204 iface->bInterfaceProtocol != US_PR_UAS) {
205 dprintf(1, "Unsupported UAS device (subclass=%02x proto=%02x)\n"
206 , iface->bInterfaceSubClass, iface->bInterfaceProtocol);
207 return -1;
208 }
209
210 /* find & allocate pipes */
211 struct usb_endpoint_descriptor *ep = NULL;
212 struct usb_pipe *command = NULL;
213 struct usb_pipe *status = NULL;
214 struct usb_pipe *data_in = NULL;
215 struct usb_pipe *data_out = NULL;
216 u8 *desc = (u8*)iface;
217 while (desc) {
218 desc += desc[0];
219 switch (desc[1]) {
220 case USB_DT_ENDPOINT:
221 ep = (void*)desc;
222 break;
223 case 0x24:
224 switch (desc[2]) {
225 case UAS_PIPE_ID_COMMAND:
226 command = usb_alloc_pipe(usbdev, ep);
227 break;
228 case UAS_PIPE_ID_STATUS:
229 status = usb_alloc_pipe(usbdev, ep);
230 break;
231 case UAS_PIPE_ID_DATA_IN:
232 data_in = usb_alloc_pipe(usbdev, ep);
233 break;
234 case UAS_PIPE_ID_DATA_OUT:
235 data_out = usb_alloc_pipe(usbdev, ep);
236 break;
237 default:
238 goto fail;
239 }
240 break;
241 default:
242 desc = NULL;
243 break;
244 }
245 }
246 if (!command || !status || !data_in || !data_out)
247 goto fail;
248
249 /* TODO: send REPORT LUNS. For now, only LUN 0 is recognized. */
250 int ret = uas_init_lun(usbdev, command, status, data_in, data_out, 0);
251 if (ret < 0) {
252 dprintf(1, "Unable to configure UAS drive.\n");
253 goto fail;
254 }
255
256 return 0;
257
258fail:
259 free_pipe(command);
260 free_pipe(status);
261 free_pipe(data_in);
262 free_pipe(data_out);
263 return -1;
264}