1 | /* |
---|
2 | * USB Mass Storage Device emulation |
---|
3 | * |
---|
4 | * Copyright (c) 2006 CodeSourcery. |
---|
5 | * Written by Paul Brook |
---|
6 | * |
---|
7 | * This code is licenced under the LGPL. |
---|
8 | */ |
---|
9 | |
---|
10 | #include "vl.h" |
---|
11 | |
---|
12 | //#define DEBUG_MSD |
---|
13 | |
---|
14 | #ifdef DEBUG_MSD |
---|
15 | #define DPRINTF(fmt, args...) \ |
---|
16 | do { printf("usb-msd: " fmt , ##args); } while (0) |
---|
17 | #else |
---|
18 | #define DPRINTF(fmt, args...) do {} while(0) |
---|
19 | #endif |
---|
20 | |
---|
21 | /* USB requests. */ |
---|
22 | #define MassStorageReset 0xff |
---|
23 | #define GetMaxLun 0xfe |
---|
24 | |
---|
25 | enum USBMSDMode { |
---|
26 | USB_MSDM_CBW, /* Command Block. */ |
---|
27 | USB_MSDM_DATAOUT, /* Tranfer data to device. */ |
---|
28 | USB_MSDM_DATAIN, /* Transfer data from device. */ |
---|
29 | USB_MSDM_CSW /* Command Status. */ |
---|
30 | }; |
---|
31 | |
---|
32 | typedef struct { |
---|
33 | USBDevice dev; |
---|
34 | enum USBMSDMode mode; |
---|
35 | uint32_t data_len; |
---|
36 | uint32_t tag; |
---|
37 | SCSIDevice *scsi_dev; |
---|
38 | int result; |
---|
39 | } MSDState; |
---|
40 | |
---|
41 | static const uint8_t qemu_msd_dev_descriptor[] = { |
---|
42 | 0x12, /* u8 bLength; */ |
---|
43 | 0x01, /* u8 bDescriptorType; Device */ |
---|
44 | 0x10, 0x00, /* u16 bcdUSB; v1.0 */ |
---|
45 | |
---|
46 | 0x00, /* u8 bDeviceClass; */ |
---|
47 | 0x00, /* u8 bDeviceSubClass; */ |
---|
48 | 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */ |
---|
49 | 0x08, /* u8 bMaxPacketSize0; 8 Bytes */ |
---|
50 | |
---|
51 | /* Vendor and product id are arbitrary. */ |
---|
52 | 0x00, 0x00, /* u16 idVendor; */ |
---|
53 | 0x00, 0x00, /* u16 idProduct; */ |
---|
54 | 0x00, 0x00, /* u16 bcdDevice */ |
---|
55 | |
---|
56 | 0x01, /* u8 iManufacturer; */ |
---|
57 | 0x02, /* u8 iProduct; */ |
---|
58 | 0x03, /* u8 iSerialNumber; */ |
---|
59 | 0x01 /* u8 bNumConfigurations; */ |
---|
60 | }; |
---|
61 | |
---|
62 | static const uint8_t qemu_msd_config_descriptor[] = { |
---|
63 | |
---|
64 | /* one configuration */ |
---|
65 | 0x09, /* u8 bLength; */ |
---|
66 | 0x02, /* u8 bDescriptorType; Configuration */ |
---|
67 | 0x20, 0x00, /* u16 wTotalLength; */ |
---|
68 | 0x01, /* u8 bNumInterfaces; (1) */ |
---|
69 | 0x01, /* u8 bConfigurationValue; */ |
---|
70 | 0x00, /* u8 iConfiguration; */ |
---|
71 | 0xc0, /* u8 bmAttributes; |
---|
72 | Bit 7: must be set, |
---|
73 | 6: Self-powered, |
---|
74 | 5: Remote wakeup, |
---|
75 | 4..0: resvd */ |
---|
76 | 0x00, /* u8 MaxPower; */ |
---|
77 | |
---|
78 | /* one interface */ |
---|
79 | 0x09, /* u8 if_bLength; */ |
---|
80 | 0x04, /* u8 if_bDescriptorType; Interface */ |
---|
81 | 0x00, /* u8 if_bInterfaceNumber; */ |
---|
82 | 0x00, /* u8 if_bAlternateSetting; */ |
---|
83 | 0x02, /* u8 if_bNumEndpoints; */ |
---|
84 | 0x08, /* u8 if_bInterfaceClass; MASS STORAGE */ |
---|
85 | 0x06, /* u8 if_bInterfaceSubClass; SCSI */ |
---|
86 | 0x50, /* u8 if_bInterfaceProtocol; Bulk Only */ |
---|
87 | 0x00, /* u8 if_iInterface; */ |
---|
88 | |
---|
89 | /* Bulk-In endpoint */ |
---|
90 | 0x07, /* u8 ep_bLength; */ |
---|
91 | 0x05, /* u8 ep_bDescriptorType; Endpoint */ |
---|
92 | 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ |
---|
93 | 0x02, /* u8 ep_bmAttributes; Bulk */ |
---|
94 | 0x40, 0x00, /* u16 ep_wMaxPacketSize; */ |
---|
95 | 0x00, /* u8 ep_bInterval; */ |
---|
96 | |
---|
97 | /* Bulk-Out endpoint */ |
---|
98 | 0x07, /* u8 ep_bLength; */ |
---|
99 | 0x05, /* u8 ep_bDescriptorType; Endpoint */ |
---|
100 | 0x02, /* u8 ep_bEndpointAddress; OUT Endpoint 2 */ |
---|
101 | 0x02, /* u8 ep_bmAttributes; Bulk */ |
---|
102 | 0x40, 0x00, /* u16 ep_wMaxPacketSize; */ |
---|
103 | 0x00 /* u8 ep_bInterval; */ |
---|
104 | }; |
---|
105 | |
---|
106 | static void usb_msd_command_complete(void *opaque, uint32_t tag, int fail) |
---|
107 | { |
---|
108 | MSDState *s = (MSDState *)opaque; |
---|
109 | |
---|
110 | DPRINTF("Command complete\n"); |
---|
111 | s->result = fail; |
---|
112 | s->mode = USB_MSDM_CSW; |
---|
113 | } |
---|
114 | |
---|
115 | static void usb_msd_handle_reset(USBDevice *dev) |
---|
116 | { |
---|
117 | MSDState *s = (MSDState *)dev; |
---|
118 | |
---|
119 | DPRINTF("Reset\n"); |
---|
120 | s->mode = USB_MSDM_CBW; |
---|
121 | } |
---|
122 | |
---|
123 | static int usb_msd_handle_control(USBDevice *dev, int request, int value, |
---|
124 | int index, int length, uint8_t *data) |
---|
125 | { |
---|
126 | MSDState *s = (MSDState *)dev; |
---|
127 | int ret = 0; |
---|
128 | |
---|
129 | switch (request) { |
---|
130 | case DeviceRequest | USB_REQ_GET_STATUS: |
---|
131 | data[0] = (1 << USB_DEVICE_SELF_POWERED) | |
---|
132 | (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); |
---|
133 | data[1] = 0x00; |
---|
134 | ret = 2; |
---|
135 | break; |
---|
136 | case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: |
---|
137 | if (value == USB_DEVICE_REMOTE_WAKEUP) { |
---|
138 | dev->remote_wakeup = 0; |
---|
139 | } else { |
---|
140 | goto fail; |
---|
141 | } |
---|
142 | ret = 0; |
---|
143 | break; |
---|
144 | case DeviceOutRequest | USB_REQ_SET_FEATURE: |
---|
145 | if (value == USB_DEVICE_REMOTE_WAKEUP) { |
---|
146 | dev->remote_wakeup = 1; |
---|
147 | } else { |
---|
148 | goto fail; |
---|
149 | } |
---|
150 | ret = 0; |
---|
151 | break; |
---|
152 | case DeviceOutRequest | USB_REQ_SET_ADDRESS: |
---|
153 | dev->addr = value; |
---|
154 | ret = 0; |
---|
155 | break; |
---|
156 | case DeviceRequest | USB_REQ_GET_DESCRIPTOR: |
---|
157 | switch(value >> 8) { |
---|
158 | case USB_DT_DEVICE: |
---|
159 | memcpy(data, qemu_msd_dev_descriptor, |
---|
160 | sizeof(qemu_msd_dev_descriptor)); |
---|
161 | ret = sizeof(qemu_msd_dev_descriptor); |
---|
162 | break; |
---|
163 | case USB_DT_CONFIG: |
---|
164 | memcpy(data, qemu_msd_config_descriptor, |
---|
165 | sizeof(qemu_msd_config_descriptor)); |
---|
166 | ret = sizeof(qemu_msd_config_descriptor); |
---|
167 | break; |
---|
168 | case USB_DT_STRING: |
---|
169 | switch(value & 0xff) { |
---|
170 | case 0: |
---|
171 | /* language ids */ |
---|
172 | data[0] = 4; |
---|
173 | data[1] = 3; |
---|
174 | data[2] = 0x09; |
---|
175 | data[3] = 0x04; |
---|
176 | ret = 4; |
---|
177 | break; |
---|
178 | case 1: |
---|
179 | /* vendor description */ |
---|
180 | ret = set_usb_string(data, "QEMU " QEMU_VERSION); |
---|
181 | break; |
---|
182 | case 2: |
---|
183 | /* product description */ |
---|
184 | ret = set_usb_string(data, "QEMU USB HARDDRIVE"); |
---|
185 | break; |
---|
186 | case 3: |
---|
187 | /* serial number */ |
---|
188 | ret = set_usb_string(data, "1"); |
---|
189 | break; |
---|
190 | default: |
---|
191 | goto fail; |
---|
192 | } |
---|
193 | break; |
---|
194 | default: |
---|
195 | goto fail; |
---|
196 | } |
---|
197 | break; |
---|
198 | case DeviceRequest | USB_REQ_GET_CONFIGURATION: |
---|
199 | data[0] = 1; |
---|
200 | ret = 1; |
---|
201 | break; |
---|
202 | case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: |
---|
203 | ret = 0; |
---|
204 | break; |
---|
205 | case DeviceRequest | USB_REQ_GET_INTERFACE: |
---|
206 | data[0] = 0; |
---|
207 | ret = 1; |
---|
208 | break; |
---|
209 | case DeviceOutRequest | USB_REQ_SET_INTERFACE: |
---|
210 | ret = 0; |
---|
211 | break; |
---|
212 | case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: |
---|
213 | if (value == 0 && index != 0x81) { /* clear ep halt */ |
---|
214 | goto fail; |
---|
215 | } |
---|
216 | ret = 0; |
---|
217 | break; |
---|
218 | /* Class specific requests. */ |
---|
219 | case MassStorageReset: |
---|
220 | /* Reset state ready for the next CBW. */ |
---|
221 | s->mode = USB_MSDM_CBW; |
---|
222 | ret = 0; |
---|
223 | break; |
---|
224 | case GetMaxLun: |
---|
225 | data[0] = 0; |
---|
226 | ret = 1; |
---|
227 | break; |
---|
228 | default: |
---|
229 | fail: |
---|
230 | ret = USB_RET_STALL; |
---|
231 | break; |
---|
232 | } |
---|
233 | return ret; |
---|
234 | } |
---|
235 | |
---|
236 | struct usb_msd_cbw { |
---|
237 | uint32_t sig; |
---|
238 | uint32_t tag; |
---|
239 | uint32_t data_len; |
---|
240 | uint8_t flags; |
---|
241 | uint8_t lun; |
---|
242 | uint8_t cmd_len; |
---|
243 | uint8_t cmd[16]; |
---|
244 | }; |
---|
245 | |
---|
246 | struct usb_msd_csw { |
---|
247 | uint32_t sig; |
---|
248 | uint32_t tag; |
---|
249 | uint32_t residue; |
---|
250 | uint8_t status; |
---|
251 | }; |
---|
252 | |
---|
253 | static int usb_msd_handle_data(USBDevice *dev, int pid, uint8_t devep, |
---|
254 | uint8_t *data, int len) |
---|
255 | { |
---|
256 | MSDState *s = (MSDState *)dev; |
---|
257 | int ret = 0; |
---|
258 | struct usb_msd_cbw cbw; |
---|
259 | struct usb_msd_csw csw; |
---|
260 | |
---|
261 | switch (pid) { |
---|
262 | case USB_TOKEN_OUT: |
---|
263 | if (devep != 2) |
---|
264 | goto fail; |
---|
265 | |
---|
266 | switch (s->mode) { |
---|
267 | case USB_MSDM_CBW: |
---|
268 | if (len != 31) { |
---|
269 | fprintf(stderr, "usb-msd: Bad CBW size"); |
---|
270 | goto fail; |
---|
271 | } |
---|
272 | memcpy(&cbw, data, 31); |
---|
273 | if (le32_to_cpu(cbw.sig) != 0x43425355) { |
---|
274 | fprintf(stderr, "usb-msd: Bad signature %08x\n", |
---|
275 | le32_to_cpu(cbw.sig)); |
---|
276 | goto fail; |
---|
277 | } |
---|
278 | DPRINTF("Command on LUN %d\n", cbw.lun); |
---|
279 | if (cbw.lun != 0) { |
---|
280 | fprintf(stderr, "usb-msd: Bad LUN %d\n", cbw.lun); |
---|
281 | goto fail; |
---|
282 | } |
---|
283 | s->tag = le32_to_cpu(cbw.tag); |
---|
284 | s->data_len = le32_to_cpu(cbw.data_len); |
---|
285 | if (s->data_len == 0) { |
---|
286 | s->mode = USB_MSDM_CSW; |
---|
287 | } else if (cbw.flags & 0x80) { |
---|
288 | s->mode = USB_MSDM_DATAIN; |
---|
289 | } else { |
---|
290 | s->mode = USB_MSDM_DATAOUT; |
---|
291 | } |
---|
292 | DPRINTF("Command tag 0x%x flags %08x len %d data %d\n", |
---|
293 | s->tag, cbw.flags, cbw.cmd_len, s->data_len); |
---|
294 | scsi_send_command(s->scsi_dev, s->tag, cbw.cmd, 0); |
---|
295 | ret = len; |
---|
296 | break; |
---|
297 | |
---|
298 | case USB_MSDM_DATAOUT: |
---|
299 | DPRINTF("Data out %d/%d\n", len, s->data_len); |
---|
300 | if (len > s->data_len) |
---|
301 | goto fail; |
---|
302 | |
---|
303 | if (scsi_write_data(s->scsi_dev, data, len)) |
---|
304 | goto fail; |
---|
305 | |
---|
306 | s->data_len -= len; |
---|
307 | if (s->data_len == 0) |
---|
308 | s->mode = USB_MSDM_CSW; |
---|
309 | ret = len; |
---|
310 | break; |
---|
311 | |
---|
312 | default: |
---|
313 | DPRINTF("Unexpected write (len %d)\n", len); |
---|
314 | goto fail; |
---|
315 | } |
---|
316 | break; |
---|
317 | |
---|
318 | case USB_TOKEN_IN: |
---|
319 | if (devep != 1) |
---|
320 | goto fail; |
---|
321 | |
---|
322 | switch (s->mode) { |
---|
323 | case USB_MSDM_CSW: |
---|
324 | DPRINTF("Command status %d tag 0x%x, len %d\n", |
---|
325 | s->result, s->tag, len); |
---|
326 | if (len < 13) |
---|
327 | goto fail; |
---|
328 | |
---|
329 | csw.sig = cpu_to_le32(0x53425355); |
---|
330 | csw.tag = cpu_to_le32(s->tag); |
---|
331 | csw.residue = 0; |
---|
332 | csw.status = s->result; |
---|
333 | memcpy(data, &csw, 13); |
---|
334 | ret = 13; |
---|
335 | s->mode = USB_MSDM_CBW; |
---|
336 | break; |
---|
337 | |
---|
338 | case USB_MSDM_DATAIN: |
---|
339 | DPRINTF("Data in %d/%d\n", len, s->data_len); |
---|
340 | if (len > s->data_len) |
---|
341 | len = s->data_len; |
---|
342 | |
---|
343 | if (scsi_read_data(s->scsi_dev, data, len)) |
---|
344 | goto fail; |
---|
345 | |
---|
346 | s->data_len -= len; |
---|
347 | if (s->data_len == 0) |
---|
348 | s->mode = USB_MSDM_CSW; |
---|
349 | ret = len; |
---|
350 | break; |
---|
351 | |
---|
352 | default: |
---|
353 | DPRINTF("Unexpected read (len %d)\n", len); |
---|
354 | goto fail; |
---|
355 | } |
---|
356 | break; |
---|
357 | |
---|
358 | default: |
---|
359 | DPRINTF("Bad token\n"); |
---|
360 | fail: |
---|
361 | ret = USB_RET_STALL; |
---|
362 | break; |
---|
363 | } |
---|
364 | |
---|
365 | return ret; |
---|
366 | } |
---|
367 | |
---|
368 | static void usb_msd_handle_destroy(USBDevice *dev) |
---|
369 | { |
---|
370 | MSDState *s = (MSDState *)dev; |
---|
371 | |
---|
372 | scsi_disk_destroy(s->scsi_dev); |
---|
373 | qemu_free(s); |
---|
374 | } |
---|
375 | |
---|
376 | USBDevice *usb_msd_init(const char *filename) |
---|
377 | { |
---|
378 | MSDState *s; |
---|
379 | BlockDriverState *bdrv; |
---|
380 | |
---|
381 | s = qemu_mallocz(sizeof(MSDState)); |
---|
382 | if (!s) |
---|
383 | return NULL; |
---|
384 | |
---|
385 | bdrv = bdrv_new("usb"); |
---|
386 | bdrv_open(bdrv, filename, 0); |
---|
387 | |
---|
388 | s->dev.speed = USB_SPEED_FULL; |
---|
389 | s->dev.handle_packet = usb_generic_handle_packet; |
---|
390 | |
---|
391 | s->dev.handle_reset = usb_msd_handle_reset; |
---|
392 | s->dev.handle_control = usb_msd_handle_control; |
---|
393 | s->dev.handle_data = usb_msd_handle_data; |
---|
394 | s->dev.handle_destroy = usb_msd_handle_destroy; |
---|
395 | |
---|
396 | snprintf(s->dev.devname, sizeof(s->dev.devname), "QEMU USB MSD(%.16s)", |
---|
397 | filename); |
---|
398 | |
---|
399 | s->scsi_dev = scsi_disk_init(bdrv, usb_msd_command_complete, s); |
---|
400 | usb_msd_handle_reset((USBDevice *)s); |
---|
401 | return (USBDevice *)s; |
---|
402 | } |
---|