File: | hw/usb/dev-storage.c |
Location: | line 308, column 5 |
Description: | Value stored to 'ret' is never read |
1 | /* |
2 | * USB Mass Storage Device emulation |
3 | * |
4 | * Copyright (c) 2006 CodeSourcery. |
5 | * Written by Paul Brook |
6 | * |
7 | * This code is licensed under the LGPL. |
8 | */ |
9 | |
10 | #include "qemu-common.h" |
11 | #include "qemu-option.h" |
12 | #include "qemu-config.h" |
13 | #include "hw/usb.h" |
14 | #include "hw/usb/desc.h" |
15 | #include "hw/scsi.h" |
16 | #include "console.h" |
17 | #include "monitor.h" |
18 | #include "sysemu.h" |
19 | #include "blockdev.h" |
20 | |
21 | //#define DEBUG_MSD |
22 | |
23 | #ifdef DEBUG_MSD |
24 | #define DPRINTF(fmt, ...)do {} while(0) \ |
25 | do { printf("usb-msd: " fmt , ## __VA_ARGS__); } while (0) |
26 | #else |
27 | #define DPRINTF(fmt, ...)do {} while(0) do {} while(0) |
28 | #endif |
29 | |
30 | /* USB requests. */ |
31 | #define MassStorageReset0xff 0xff |
32 | #define GetMaxLun0xfe 0xfe |
33 | |
34 | enum USBMSDMode { |
35 | USB_MSDM_CBW, /* Command Block. */ |
36 | USB_MSDM_DATAOUT, /* Transfer data to device. */ |
37 | USB_MSDM_DATAIN, /* Transfer data from device. */ |
38 | USB_MSDM_CSW /* Command Status. */ |
39 | }; |
40 | |
41 | struct usb_msd_csw { |
42 | uint32_t sig; |
43 | uint32_t tag; |
44 | uint32_t residue; |
45 | uint8_t status; |
46 | }; |
47 | |
48 | typedef struct { |
49 | USBDevice dev; |
50 | enum USBMSDMode mode; |
51 | uint32_t scsi_off; |
52 | uint32_t scsi_len; |
53 | uint32_t data_len; |
54 | struct usb_msd_csw csw; |
55 | SCSIRequest *req; |
56 | SCSIBus bus; |
57 | BlockConf conf; |
58 | char *serial; |
59 | SCSIDevice *scsi_dev; |
60 | uint32_t removable; |
61 | /* For async completion. */ |
62 | USBPacket *packet; |
63 | } MSDState; |
64 | |
65 | struct usb_msd_cbw { |
66 | uint32_t sig; |
67 | uint32_t tag; |
68 | uint32_t data_len; |
69 | uint8_t flags; |
70 | uint8_t lun; |
71 | uint8_t cmd_len; |
72 | uint8_t cmd[16]; |
73 | }; |
74 | |
75 | enum { |
76 | STR_MANUFACTURER = 1, |
77 | STR_PRODUCT, |
78 | STR_SERIALNUMBER, |
79 | STR_CONFIG_FULL, |
80 | STR_CONFIG_HIGH, |
81 | }; |
82 | |
83 | static const USBDescStrings desc_strings = { |
84 | [STR_MANUFACTURER] = "QEMU", |
85 | [STR_PRODUCT] = "QEMU USB HARDDRIVE", |
86 | [STR_SERIALNUMBER] = "1", |
87 | [STR_CONFIG_FULL] = "Full speed config (usb 1.1)", |
88 | [STR_CONFIG_HIGH] = "High speed config (usb 2.0)", |
89 | }; |
90 | |
91 | static const USBDescIface desc_iface_full = { |
92 | .bInterfaceNumber = 0, |
93 | .bNumEndpoints = 2, |
94 | .bInterfaceClass = USB_CLASS_MASS_STORAGE8, |
95 | .bInterfaceSubClass = 0x06, /* SCSI */ |
96 | .bInterfaceProtocol = 0x50, /* Bulk */ |
97 | .eps = (USBDescEndpoint[]) { |
98 | { |
99 | .bEndpointAddress = USB_DIR_IN0x80 | 0x01, |
100 | .bmAttributes = USB_ENDPOINT_XFER_BULK2, |
101 | .wMaxPacketSize = 64, |
102 | },{ |
103 | .bEndpointAddress = USB_DIR_OUT0 | 0x02, |
104 | .bmAttributes = USB_ENDPOINT_XFER_BULK2, |
105 | .wMaxPacketSize = 64, |
106 | }, |
107 | } |
108 | }; |
109 | |
110 | static const USBDescDevice desc_device_full = { |
111 | .bcdUSB = 0x0200, |
112 | .bMaxPacketSize0 = 8, |
113 | .bNumConfigurations = 1, |
114 | .confs = (USBDescConfig[]) { |
115 | { |
116 | .bNumInterfaces = 1, |
117 | .bConfigurationValue = 1, |
118 | .iConfiguration = STR_CONFIG_FULL, |
119 | .bmAttributes = 0xc0, |
120 | .nif = 1, |
121 | .ifs = &desc_iface_full, |
122 | }, |
123 | }, |
124 | }; |
125 | |
126 | static const USBDescIface desc_iface_high = { |
127 | .bInterfaceNumber = 0, |
128 | .bNumEndpoints = 2, |
129 | .bInterfaceClass = USB_CLASS_MASS_STORAGE8, |
130 | .bInterfaceSubClass = 0x06, /* SCSI */ |
131 | .bInterfaceProtocol = 0x50, /* Bulk */ |
132 | .eps = (USBDescEndpoint[]) { |
133 | { |
134 | .bEndpointAddress = USB_DIR_IN0x80 | 0x01, |
135 | .bmAttributes = USB_ENDPOINT_XFER_BULK2, |
136 | .wMaxPacketSize = 512, |
137 | },{ |
138 | .bEndpointAddress = USB_DIR_OUT0 | 0x02, |
139 | .bmAttributes = USB_ENDPOINT_XFER_BULK2, |
140 | .wMaxPacketSize = 512, |
141 | }, |
142 | } |
143 | }; |
144 | |
145 | static const USBDescDevice desc_device_high = { |
146 | .bcdUSB = 0x0200, |
147 | .bMaxPacketSize0 = 64, |
148 | .bNumConfigurations = 1, |
149 | .confs = (USBDescConfig[]) { |
150 | { |
151 | .bNumInterfaces = 1, |
152 | .bConfigurationValue = 1, |
153 | .iConfiguration = STR_CONFIG_HIGH, |
154 | .bmAttributes = 0xc0, |
155 | .nif = 1, |
156 | .ifs = &desc_iface_high, |
157 | }, |
158 | }, |
159 | }; |
160 | |
161 | static const USBDesc desc = { |
162 | .id = { |
163 | .idVendor = 0x46f4, /* CRC16() of "QEMU" */ |
164 | .idProduct = 0x0001, |
165 | .bcdDevice = 0, |
166 | .iManufacturer = STR_MANUFACTURER, |
167 | .iProduct = STR_PRODUCT, |
168 | .iSerialNumber = STR_SERIALNUMBER, |
169 | }, |
170 | .full = &desc_device_full, |
171 | .high = &desc_device_high, |
172 | .str = desc_strings, |
173 | }; |
174 | |
175 | static void usb_msd_copy_data(MSDState *s, USBPacket *p) |
176 | { |
177 | uint32_t len; |
178 | len = p->iov.size - p->result; |
179 | if (len > s->scsi_len) |
180 | len = s->scsi_len; |
181 | usb_packet_copy(p, scsi_req_get_buf(s->req) + s->scsi_off, len); |
182 | s->scsi_len -= len; |
183 | s->scsi_off += len; |
184 | s->data_len -= len; |
185 | if (s->scsi_len == 0 || s->data_len == 0) { |
186 | scsi_req_continue(s->req); |
187 | } |
188 | } |
189 | |
190 | static void usb_msd_send_status(MSDState *s, USBPacket *p) |
191 | { |
192 | int len; |
193 | |
194 | DPRINTF("Command status %d tag 0x%x, len %zd\n",do {} while(0) |
195 | s->csw.status, le32_to_cpu(s->csw.tag), p->iov.size)do {} while(0); |
196 | |
197 | assert(s->csw.sig == cpu_to_le32(0x53425355))((s->csw.sig == cpu_to_le32(0x53425355)) ? (void) (0) : __assert_fail ("s->csw.sig == cpu_to_le32(0x53425355)", "/home/stefan/src/qemu/qemu.org/qemu/hw/usb/dev-storage.c" , 197, __PRETTY_FUNCTION__)); |
198 | len = MIN(sizeof(s->csw), p->iov.size)(((sizeof(s->csw)) < (p->iov.size)) ? (sizeof(s-> csw)) : (p->iov.size)); |
199 | usb_packet_copy(p, &s->csw, len); |
200 | memset(&s->csw, 0, sizeof(s->csw)); |
201 | } |
202 | |
203 | static void usb_msd_packet_complete(MSDState *s) |
204 | { |
205 | USBPacket *p = s->packet; |
206 | |
207 | /* Set s->packet to NULL before calling usb_packet_complete |
208 | because another request may be issued before |
209 | usb_packet_complete returns. */ |
210 | DPRINTF("Packet complete %p\n", p)do {} while(0); |
211 | s->packet = NULL((void*)0); |
212 | usb_packet_complete(&s->dev, p); |
213 | } |
214 | |
215 | static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len) |
216 | { |
217 | MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent)( __extension__ ( { char __attribute__((unused)) offset_must_be_zero [ -__builtin_offsetof(MSDState, dev.qdev)]; ({ const typeof(( (MSDState *) 0)->dev.qdev) *__mptr = (req->bus->qbus .parent); (MSDState *) ((char *) __mptr - __builtin_offsetof( MSDState, dev.qdev));});})); |
218 | USBPacket *p = s->packet; |
219 | |
220 | assert((s->mode == USB_MSDM_DATAOUT) == (req->cmd.mode == SCSI_XFER_TO_DEV))(((s->mode == USB_MSDM_DATAOUT) == (req->cmd.mode == SCSI_XFER_TO_DEV )) ? (void) (0) : __assert_fail ("(s->mode == USB_MSDM_DATAOUT) == (req->cmd.mode == SCSI_XFER_TO_DEV)" , "/home/stefan/src/qemu/qemu.org/qemu/hw/usb/dev-storage.c", 220, __PRETTY_FUNCTION__)); |
221 | s->scsi_len = len; |
222 | s->scsi_off = 0; |
223 | if (p) { |
224 | usb_msd_copy_data(s, p); |
225 | p = s->packet; |
226 | if (p && p->result == p->iov.size) { |
227 | usb_msd_packet_complete(s); |
228 | } |
229 | } |
230 | } |
231 | |
232 | static void usb_msd_command_complete(SCSIRequest *req, uint32_t status, size_t resid) |
233 | { |
234 | MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent)( __extension__ ( { char __attribute__((unused)) offset_must_be_zero [ -__builtin_offsetof(MSDState, dev.qdev)]; ({ const typeof(( (MSDState *) 0)->dev.qdev) *__mptr = (req->bus->qbus .parent); (MSDState *) ((char *) __mptr - __builtin_offsetof( MSDState, dev.qdev));});})); |
235 | USBPacket *p = s->packet; |
236 | |
237 | DPRINTF("Command complete %d tag 0x%x\n", status, req->tag)do {} while(0); |
238 | |
239 | s->csw.sig = cpu_to_le32(0x53425355); |
240 | s->csw.tag = cpu_to_le32(req->tag); |
241 | s->csw.residue = cpu_to_le32(s->data_len); |
242 | s->csw.status = status != 0; |
243 | |
244 | if (s->packet) { |
245 | if (s->data_len == 0 && s->mode == USB_MSDM_DATAOUT) { |
246 | /* A deferred packet with no write data remaining must be |
247 | the status read packet. */ |
248 | usb_msd_send_status(s, p); |
249 | s->mode = USB_MSDM_CBW; |
250 | } else { |
251 | if (s->data_len) { |
252 | int len = (p->iov.size - p->result); |
253 | usb_packet_skip(p, len); |
254 | s->data_len -= len; |
255 | } |
256 | if (s->data_len == 0) { |
257 | s->mode = USB_MSDM_CSW; |
258 | } |
259 | } |
260 | usb_msd_packet_complete(s); |
261 | } else if (s->data_len == 0) { |
262 | s->mode = USB_MSDM_CSW; |
263 | } |
264 | scsi_req_unref(req); |
265 | s->req = NULL((void*)0); |
266 | } |
267 | |
268 | static void usb_msd_request_cancelled(SCSIRequest *req) |
269 | { |
270 | MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent)( __extension__ ( { char __attribute__((unused)) offset_must_be_zero [ -__builtin_offsetof(MSDState, dev.qdev)]; ({ const typeof(( (MSDState *) 0)->dev.qdev) *__mptr = (req->bus->qbus .parent); (MSDState *) ((char *) __mptr - __builtin_offsetof( MSDState, dev.qdev));});})); |
271 | |
272 | if (req == s->req) { |
273 | scsi_req_unref(s->req); |
274 | s->req = NULL((void*)0); |
275 | s->scsi_len = 0; |
276 | } |
277 | } |
278 | |
279 | static void usb_msd_handle_reset(USBDevice *dev) |
280 | { |
281 | MSDState *s = (MSDState *)dev; |
282 | |
283 | DPRINTF("Reset\n")do {} while(0); |
284 | if (s->req) { |
285 | scsi_req_cancel(s->req); |
286 | } |
287 | assert(s->req == NULL)((s->req == ((void*)0)) ? (void) (0) : __assert_fail ("s->req == ((void*)0)" , "/home/stefan/src/qemu/qemu.org/qemu/hw/usb/dev-storage.c", 287, __PRETTY_FUNCTION__)); |
288 | |
289 | if (s->packet) { |
290 | s->packet->result = USB_RET_STALL(-3); |
291 | usb_msd_packet_complete(s); |
292 | } |
293 | |
294 | s->mode = USB_MSDM_CBW; |
295 | } |
296 | |
297 | static int usb_msd_handle_control(USBDevice *dev, USBPacket *p, |
298 | int request, int value, int index, int length, uint8_t *data) |
299 | { |
300 | MSDState *s = (MSDState *)dev; |
301 | int ret; |
302 | |
303 | ret = usb_desc_handle_control(dev, p, request, value, index, length, data); |
304 | if (ret >= 0) { |
305 | return ret; |
306 | } |
307 | |
308 | ret = 0; |
Value stored to 'ret' is never read | |
309 | switch (request) { |
310 | case EndpointOutRequest((0|(0x00 << 5)|0x02)<<8) | USB_REQ_CLEAR_FEATURE0x01: |
311 | ret = 0; |
312 | break; |
313 | /* Class specific requests. */ |
314 | case ClassInterfaceOutRequest((0|(0x01 << 5)|0x01)<<8) | MassStorageReset0xff: |
315 | /* Reset state ready for the next CBW. */ |
316 | s->mode = USB_MSDM_CBW; |
317 | ret = 0; |
318 | break; |
319 | case ClassInterfaceRequest((0x80|(0x01 << 5)|0x01)<<8) | GetMaxLun0xfe: |
320 | data[0] = 0; |
321 | ret = 1; |
322 | break; |
323 | default: |
324 | ret = USB_RET_STALL(-3); |
325 | break; |
326 | } |
327 | return ret; |
328 | } |
329 | |
330 | static void usb_msd_cancel_io(USBDevice *dev, USBPacket *p) |
331 | { |
332 | MSDState *s = DO_UPCAST(MSDState, dev, dev)( __extension__ ( { char __attribute__((unused)) offset_must_be_zero [ -__builtin_offsetof(MSDState, dev)]; ({ const typeof(((MSDState *) 0)->dev) *__mptr = (dev); (MSDState *) ((char *) __mptr - __builtin_offsetof(MSDState, dev));});})); |
333 | |
334 | assert(s->packet == p)((s->packet == p) ? (void) (0) : __assert_fail ("s->packet == p" , "/home/stefan/src/qemu/qemu.org/qemu/hw/usb/dev-storage.c", 334, __PRETTY_FUNCTION__)); |
335 | s->packet = NULL((void*)0); |
336 | |
337 | if (s->req) { |
338 | scsi_req_cancel(s->req); |
339 | } |
340 | } |
341 | |
342 | static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) |
343 | { |
344 | MSDState *s = (MSDState *)dev; |
345 | uint32_t tag; |
346 | int ret = 0; |
347 | struct usb_msd_cbw cbw; |
348 | uint8_t devep = p->ep->nr; |
349 | |
350 | switch (p->pid) { |
351 | case USB_TOKEN_OUT0xe1: |
352 | if (devep != 2) |
353 | goto fail; |
354 | |
355 | switch (s->mode) { |
356 | case USB_MSDM_CBW: |
357 | if (p->iov.size != 31) { |
358 | fprintf(stderrstderr, "usb-msd: Bad CBW size"); |
359 | goto fail; |
360 | } |
361 | usb_packet_copy(p, &cbw, 31); |
362 | if (le32_to_cpu(cbw.sig) != 0x43425355) { |
363 | fprintf(stderrstderr, "usb-msd: Bad signature %08x\n", |
364 | le32_to_cpu(cbw.sig)); |
365 | goto fail; |
366 | } |
367 | DPRINTF("Command on LUN %d\n", cbw.lun)do {} while(0); |
368 | if (cbw.lun != 0) { |
369 | fprintf(stderrstderr, "usb-msd: Bad LUN %d\n", cbw.lun); |
370 | goto fail; |
371 | } |
372 | tag = le32_to_cpu(cbw.tag); |
373 | s->data_len = le32_to_cpu(cbw.data_len); |
374 | if (s->data_len == 0) { |
375 | s->mode = USB_MSDM_CSW; |
376 | } else if (cbw.flags & 0x80) { |
377 | s->mode = USB_MSDM_DATAIN; |
378 | } else { |
379 | s->mode = USB_MSDM_DATAOUT; |
380 | } |
381 | DPRINTF("Command tag 0x%x flags %08x len %d data %d\n",do {} while(0) |
382 | tag, cbw.flags, cbw.cmd_len, s->data_len)do {} while(0); |
383 | assert(le32_to_cpu(s->csw.residue) == 0)((le32_to_cpu(s->csw.residue) == 0) ? (void) (0) : __assert_fail ("le32_to_cpu(s->csw.residue) == 0", "/home/stefan/src/qemu/qemu.org/qemu/hw/usb/dev-storage.c" , 383, __PRETTY_FUNCTION__)); |
384 | s->scsi_len = 0; |
385 | s->req = scsi_req_new(s->scsi_dev, tag, 0, cbw.cmd, NULL((void*)0)); |
386 | scsi_req_enqueue(s->req); |
387 | if (s->req && s->req->cmd.xfer != SCSI_XFER_NONE) { |
388 | scsi_req_continue(s->req); |
389 | } |
390 | ret = p->result; |
391 | break; |
392 | |
393 | case USB_MSDM_DATAOUT: |
394 | DPRINTF("Data out %zd/%d\n", p->iov.size, s->data_len)do {} while(0); |
395 | if (p->iov.size > s->data_len) { |
396 | goto fail; |
397 | } |
398 | |
399 | if (s->scsi_len) { |
400 | usb_msd_copy_data(s, p); |
401 | } |
402 | if (le32_to_cpu(s->csw.residue)) { |
403 | int len = p->iov.size - p->result; |
404 | if (len) { |
405 | usb_packet_skip(p, len); |
406 | s->data_len -= len; |
407 | if (s->data_len == 0) { |
408 | s->mode = USB_MSDM_CSW; |
409 | } |
410 | } |
411 | } |
412 | if (p->result < p->iov.size) { |
413 | DPRINTF("Deferring packet %p\n", p)do {} while(0); |
414 | s->packet = p; |
415 | ret = USB_RET_ASYNC(-6); |
416 | } else { |
417 | ret = p->result; |
418 | } |
419 | break; |
420 | |
421 | default: |
422 | DPRINTF("Unexpected write (len %zd)\n", p->iov.size)do {} while(0); |
423 | goto fail; |
424 | } |
425 | break; |
426 | |
427 | case USB_TOKEN_IN0x69: |
428 | if (devep != 1) |
429 | goto fail; |
430 | |
431 | switch (s->mode) { |
432 | case USB_MSDM_DATAOUT: |
433 | if (s->data_len != 0 || p->iov.size < 13) { |
434 | goto fail; |
435 | } |
436 | /* Waiting for SCSI write to complete. */ |
437 | s->packet = p; |
438 | ret = USB_RET_ASYNC(-6); |
439 | break; |
440 | |
441 | case USB_MSDM_CSW: |
442 | if (p->iov.size < 13) { |
443 | goto fail; |
444 | } |
445 | |
446 | if (s->req) { |
447 | /* still in flight */ |
448 | s->packet = p; |
449 | ret = USB_RET_ASYNC(-6); |
450 | } else { |
451 | usb_msd_send_status(s, p); |
452 | s->mode = USB_MSDM_CBW; |
453 | ret = 13; |
454 | } |
455 | break; |
456 | |
457 | case USB_MSDM_DATAIN: |
458 | DPRINTF("Data in %zd/%d, scsi_len %d\n",do {} while(0) |
459 | p->iov.size, s->data_len, s->scsi_len)do {} while(0); |
460 | if (s->scsi_len) { |
461 | usb_msd_copy_data(s, p); |
462 | } |
463 | if (le32_to_cpu(s->csw.residue)) { |
464 | int len = p->iov.size - p->result; |
465 | if (len) { |
466 | usb_packet_skip(p, len); |
467 | s->data_len -= len; |
468 | if (s->data_len == 0) { |
469 | s->mode = USB_MSDM_CSW; |
470 | } |
471 | } |
472 | } |
473 | if (p->result < p->iov.size) { |
474 | DPRINTF("Deferring packet %p\n", p)do {} while(0); |
475 | s->packet = p; |
476 | ret = USB_RET_ASYNC(-6); |
477 | } else { |
478 | ret = p->result; |
479 | } |
480 | break; |
481 | |
482 | default: |
483 | DPRINTF("Unexpected read (len %zd)\n", p->iov.size)do {} while(0); |
484 | goto fail; |
485 | } |
486 | break; |
487 | |
488 | default: |
489 | DPRINTF("Bad token\n")do {} while(0); |
490 | fail: |
491 | ret = USB_RET_STALL(-3); |
492 | break; |
493 | } |
494 | |
495 | return ret; |
496 | } |
497 | |
498 | static void usb_msd_password_cb(void *opaque, int err) |
499 | { |
500 | MSDState *s = opaque; |
501 | |
502 | if (!err) |
503 | err = usb_device_attach(&s->dev); |
504 | |
505 | if (err) |
506 | qdev_unplug(&s->dev.qdev, NULL((void*)0)); |
507 | } |
508 | |
509 | static void *usb_msd_load_request(QEMUFile *f, SCSIRequest *req) |
510 | { |
511 | MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent)( __extension__ ( { char __attribute__((unused)) offset_must_be_zero [ -__builtin_offsetof(MSDState, dev.qdev)]; ({ const typeof(( (MSDState *) 0)->dev.qdev) *__mptr = (req->bus->qbus .parent); (MSDState *) ((char *) __mptr - __builtin_offsetof( MSDState, dev.qdev));});})); |
512 | |
513 | /* nothing to load, just store req in our state struct */ |
514 | assert(s->req == NULL)((s->req == ((void*)0)) ? (void) (0) : __assert_fail ("s->req == ((void*)0)" , "/home/stefan/src/qemu/qemu.org/qemu/hw/usb/dev-storage.c", 514, __PRETTY_FUNCTION__)); |
515 | scsi_req_ref(req); |
516 | s->req = req; |
517 | return NULL((void*)0); |
518 | } |
519 | |
520 | static const struct SCSIBusInfo usb_msd_scsi_info = { |
521 | .tcq = false0, |
522 | .max_target = 0, |
523 | .max_lun = 0, |
524 | |
525 | .transfer_data = usb_msd_transfer_data, |
526 | .complete = usb_msd_command_complete, |
527 | .cancel = usb_msd_request_cancelled, |
528 | .load_request = usb_msd_load_request, |
529 | }; |
530 | |
531 | static int usb_msd_initfn(USBDevice *dev) |
532 | { |
533 | MSDState *s = DO_UPCAST(MSDState, dev, dev)( __extension__ ( { char __attribute__((unused)) offset_must_be_zero [ -__builtin_offsetof(MSDState, dev)]; ({ const typeof(((MSDState *) 0)->dev) *__mptr = (dev); (MSDState *) ((char *) __mptr - __builtin_offsetof(MSDState, dev));});})); |
534 | BlockDriverState *bs = s->conf.bs; |
535 | DriveInfo *dinfo; |
536 | |
537 | if (!bs) { |
538 | error_report("drive property not set"); |
539 | return -1; |
540 | } |
541 | |
542 | /* |
543 | * Hack alert: this pretends to be a block device, but it's really |
544 | * a SCSI bus that can serve only a single device, which it |
545 | * creates automatically. But first it needs to detach from its |
546 | * blockdev, or else scsi_bus_legacy_add_drive() dies when it |
547 | * attaches again. |
548 | * |
549 | * The hack is probably a bad idea. |
550 | */ |
551 | bdrv_detach_dev(bs, &s->dev.qdev); |
552 | s->conf.bs = NULL((void*)0); |
553 | |
554 | if (!s->serial) { |
555 | /* try to fall back to value set with legacy -drive serial=... */ |
556 | dinfo = drive_get_by_blockdev(bs); |
557 | if (*dinfo->serial) { |
558 | s->serial = strdup(dinfo->serial); |
559 | } |
560 | } |
561 | if (s->serial) { |
562 | usb_desc_set_string(dev, STR_SERIALNUMBER, s->serial); |
563 | } else { |
564 | usb_desc_create_serial(dev); |
565 | } |
566 | |
567 | usb_desc_init(dev); |
568 | scsi_bus_new(&s->bus, &s->dev.qdev, &usb_msd_scsi_info); |
569 | s->scsi_dev = scsi_bus_legacy_add_drive(&s->bus, bs, 0, !!s->removable, |
570 | s->conf.bootindex); |
571 | if (!s->scsi_dev) { |
572 | return -1; |
573 | } |
574 | s->bus.qbus.allow_hotplug = 0; |
575 | usb_msd_handle_reset(dev); |
576 | |
577 | if (bdrv_key_required(bs)) { |
578 | if (cur_mon) { |
579 | monitor_read_bdrv_key_start(cur_mon, bs, usb_msd_password_cb, s); |
580 | s->dev.auto_attach = 0; |
581 | } else { |
582 | autostart = 0; |
583 | } |
584 | } |
585 | |
586 | return 0; |
587 | } |
588 | |
589 | static USBDevice *usb_msd_init(USBBus *bus, const char *filename) |
590 | { |
591 | static int nr=0; |
592 | char id[8]; |
593 | QemuOpts *opts; |
594 | DriveInfo *dinfo; |
595 | USBDevice *dev; |
596 | const char *p1; |
597 | char fmt[32]; |
598 | |
599 | /* parse -usbdevice disk: syntax into drive opts */ |
600 | snprintf(id, sizeof(id), "usb%d", nr++); |
601 | opts = qemu_opts_create(qemu_find_opts("drive"), id, 0, NULL((void*)0)); |
602 | |
603 | p1 = strchr(filename, ':'); |
604 | if (p1++) { |
605 | const char *p2; |
606 | |
607 | if (strstart(filename, "format=", &p2)) { |
608 | int len = MIN(p1 - p2, sizeof(fmt))(((p1 - p2) < (sizeof(fmt))) ? (p1 - p2) : (sizeof(fmt))); |
609 | pstrcpy(fmt, len, p2); |
610 | qemu_opt_set(opts, "format", fmt); |
611 | } else if (*filename != ':') { |
612 | printf("unrecognized USB mass-storage option %s\n", filename); |
613 | return NULL((void*)0); |
614 | } |
615 | filename = p1; |
616 | } |
617 | if (!*filename) { |
618 | printf("block device specification needed\n"); |
619 | return NULL((void*)0); |
620 | } |
621 | qemu_opt_set(opts, "file", filename); |
622 | qemu_opt_set(opts, "if", "none"); |
623 | |
624 | /* create host drive */ |
625 | dinfo = drive_init(opts, 0); |
626 | if (!dinfo) { |
627 | qemu_opts_del(opts); |
628 | return NULL((void*)0); |
629 | } |
630 | |
631 | /* create guest device */ |
632 | dev = usb_create(bus, "usb-storage"); |
633 | if (!dev) { |
634 | return NULL((void*)0); |
635 | } |
636 | if (qdev_prop_set_drive(&dev->qdev, "drive", dinfo->bdrv) < 0) { |
637 | qdev_free(&dev->qdev); |
638 | return NULL((void*)0); |
639 | } |
640 | if (qdev_init(&dev->qdev) < 0) |
641 | return NULL((void*)0); |
642 | |
643 | return dev; |
644 | } |
645 | |
646 | static const VMStateDescription vmstate_usb_msd = { |
647 | .name = "usb-storage", |
648 | .version_id = 1, |
649 | .minimum_version_id = 1, |
650 | .fields = (VMStateField []) { |
651 | VMSTATE_USB_DEVICE(dev, MSDState){ .name = ("dev"), .size = sizeof(USBDevice), .vmsd = &vmstate_usb_device , .flags = VMS_STRUCT, .offset = (__builtin_offsetof(MSDState , dev) + ((USBDevice*)0 - (typeof(((MSDState *)0)->dev)*)0 )), }, |
652 | VMSTATE_UINT32(mode, MSDState){ .name = ("mode"), .version_id = (0), .field_exists = (((void *)0)), .size = sizeof(uint32_t), .info = &(vmstate_info_uint32 ), .flags = VMS_SINGLE, .offset = (__builtin_offsetof(MSDState , mode) + ((uint32_t*)0 - (typeof(((MSDState *)0)->mode)*) 0)), }, |
653 | VMSTATE_UINT32(scsi_len, MSDState){ .name = ("scsi_len"), .version_id = (0), .field_exists = (( (void*)0)), .size = sizeof(uint32_t), .info = &(vmstate_info_uint32 ), .flags = VMS_SINGLE, .offset = (__builtin_offsetof(MSDState , scsi_len) + ((uint32_t*)0 - (typeof(((MSDState *)0)->scsi_len )*)0)), }, |
654 | VMSTATE_UINT32(scsi_off, MSDState){ .name = ("scsi_off"), .version_id = (0), .field_exists = (( (void*)0)), .size = sizeof(uint32_t), .info = &(vmstate_info_uint32 ), .flags = VMS_SINGLE, .offset = (__builtin_offsetof(MSDState , scsi_off) + ((uint32_t*)0 - (typeof(((MSDState *)0)->scsi_off )*)0)), }, |
655 | VMSTATE_UINT32(data_len, MSDState){ .name = ("data_len"), .version_id = (0), .field_exists = (( (void*)0)), .size = sizeof(uint32_t), .info = &(vmstate_info_uint32 ), .flags = VMS_SINGLE, .offset = (__builtin_offsetof(MSDState , data_len) + ((uint32_t*)0 - (typeof(((MSDState *)0)->data_len )*)0)), }, |
656 | VMSTATE_UINT32(csw.sig, MSDState){ .name = ("csw.sig"), .version_id = (0), .field_exists = ((( void*)0)), .size = sizeof(uint32_t), .info = &(vmstate_info_uint32 ), .flags = VMS_SINGLE, .offset = (__builtin_offsetof(MSDState , csw.sig) + ((uint32_t*)0 - (typeof(((MSDState *)0)->csw. sig)*)0)), }, |
657 | VMSTATE_UINT32(csw.tag, MSDState){ .name = ("csw.tag"), .version_id = (0), .field_exists = ((( void*)0)), .size = sizeof(uint32_t), .info = &(vmstate_info_uint32 ), .flags = VMS_SINGLE, .offset = (__builtin_offsetof(MSDState , csw.tag) + ((uint32_t*)0 - (typeof(((MSDState *)0)->csw. tag)*)0)), }, |
658 | VMSTATE_UINT32(csw.residue, MSDState){ .name = ("csw.residue"), .version_id = (0), .field_exists = (((void*)0)), .size = sizeof(uint32_t), .info = &(vmstate_info_uint32 ), .flags = VMS_SINGLE, .offset = (__builtin_offsetof(MSDState , csw.residue) + ((uint32_t*)0 - (typeof(((MSDState *)0)-> csw.residue)*)0)), }, |
659 | VMSTATE_UINT8(csw.status, MSDState){ .name = ("csw.status"), .version_id = (0), .field_exists = ( ((void*)0)), .size = sizeof(uint8_t), .info = &(vmstate_info_uint8 ), .flags = VMS_SINGLE, .offset = (__builtin_offsetof(MSDState , csw.status) + ((uint8_t*)0 - (typeof(((MSDState *)0)->csw .status)*)0)), }, |
660 | VMSTATE_END_OF_LIST(){} |
661 | } |
662 | }; |
663 | |
664 | static Property msd_properties[] = { |
665 | DEFINE_BLOCK_PROPERTIES(MSDState, conf){ .name = ("drive"), .info = &(qdev_prop_drive), .offset = __builtin_offsetof(MSDState, conf.bs) + ((BlockDriverState * *)0 - (typeof(((MSDState *)0)->conf.bs)*)0), }, { .name = ( "logical_block_size"), .info = &(qdev_prop_blocksize), .offset = __builtin_offsetof(MSDState, conf.logical_block_size) + (( uint16_t*)0 - (typeof(((MSDState *)0)->conf.logical_block_size )*)0), .qtype = QTYPE_QINT, .defval = (uint16_t)512, }, { .name = ("physical_block_size"), .info = &(qdev_prop_blocksize ), .offset = __builtin_offsetof(MSDState, conf.physical_block_size ) + ((uint16_t*)0 - (typeof(((MSDState *)0)->conf.physical_block_size )*)0), .qtype = QTYPE_QINT, .defval = (uint16_t)512, }, { .name = ("min_io_size"), .info = &(qdev_prop_uint16), .offset = __builtin_offsetof(MSDState, conf.min_io_size) + ((uint16_t* )0 - (typeof(((MSDState *)0)->conf.min_io_size)*)0), .qtype = QTYPE_QINT, .defval = (uint16_t)0, }, { .name = ("opt_io_size" ), .info = &(qdev_prop_uint32), .offset = __builtin_offsetof (MSDState, conf.opt_io_size) + ((uint32_t*)0 - (typeof(((MSDState *)0)->conf.opt_io_size)*)0), .qtype = QTYPE_QINT, .defval = (uint32_t)0, }, { .name = ("bootindex"), .info = &(qdev_prop_int32 ), .offset = __builtin_offsetof(MSDState, conf.bootindex) + ( (int32_t*)0 - (typeof(((MSDState *)0)->conf.bootindex)*)0) , .qtype = QTYPE_QINT, .defval = (int32_t)-1, }, { .name = ("discard_granularity" ), .info = &(qdev_prop_uint32), .offset = __builtin_offsetof (MSDState, conf.discard_granularity) + ((uint32_t*)0 - (typeof (((MSDState *)0)->conf.discard_granularity)*)0), .qtype = QTYPE_QINT , .defval = (uint32_t)0, }, |
666 | DEFINE_PROP_STRING("serial", MSDState, serial){ .name = ("serial"), .info = &(qdev_prop_string), .offset = __builtin_offsetof(MSDState, serial) + ((char**)0 - (typeof (((MSDState *)0)->serial)*)0), }, |
667 | DEFINE_PROP_BIT("removable", MSDState, removable, 0, false){ .name = ("removable"), .info = &(qdev_prop_bit), .bitnr = (0), .offset = __builtin_offsetof(MSDState, removable) + ( (uint32_t*)0 - (typeof(((MSDState *)0)->removable)*)0), .qtype = QTYPE_QBOOL, .defval = (_Bool)0, }, |
668 | DEFINE_PROP_END_OF_LIST(){}, |
669 | }; |
670 | |
671 | static void usb_msd_class_initfn(ObjectClass *klass, void *data) |
672 | { |
673 | DeviceClass *dc = DEVICE_CLASS(klass)((DeviceClass *)object_class_dynamic_cast_assert(((ObjectClass *)((klass))), ("device"))); |
674 | USBDeviceClass *uc = USB_DEVICE_CLASS(klass)((USBDeviceClass *)object_class_dynamic_cast_assert(((ObjectClass *)((klass))), ("usb-device"))); |
675 | |
676 | uc->init = usb_msd_initfn; |
677 | uc->product_desc = "QEMU USB MSD"; |
678 | uc->usb_desc = &desc; |
679 | uc->cancel_packet = usb_msd_cancel_io; |
680 | uc->handle_attach = usb_desc_attach; |
681 | uc->handle_reset = usb_msd_handle_reset; |
682 | uc->handle_control = usb_msd_handle_control; |
683 | uc->handle_data = usb_msd_handle_data; |
684 | dc->fw_name = "storage"; |
685 | dc->vmsd = &vmstate_usb_msd; |
686 | dc->props = msd_properties; |
687 | } |
688 | |
689 | static TypeInfo msd_info = { |
690 | .name = "usb-storage", |
691 | .parent = TYPE_USB_DEVICE"usb-device", |
692 | .instance_size = sizeof(MSDState), |
693 | .class_init = usb_msd_class_initfn, |
694 | }; |
695 | |
696 | static void usb_msd_register_types(void) |
697 | { |
698 | type_register_static(&msd_info); |
699 | usb_legacy_register("usb-storage", "disk", usb_msd_init); |
700 | } |
701 | |
702 | type_init(usb_msd_register_types)static void __attribute__((constructor)) do_qemu_init_usb_msd_register_types (void) { register_module_init(usb_msd_register_types, MODULE_INIT_QOM ); } |