VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/UsbMsd.cpp@ 98110

Last change on this file since 98110 was 98103, checked in by vboxsync, 23 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 81.6 KB
Line 
1/* $Id: UsbMsd.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * UsbMSD - USB Mass Storage Device Emulation.
4 */
5
6/*
7 * Copyright (C) 2007-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_USB_MSD
33#include <VBox/vmm/pdmusb.h>
34#include <VBox/vmm/pdmstorageifs.h>
35#include <VBox/log.h>
36#include <VBox/err.h>
37#include <VBox/scsi.h>
38#include <iprt/assert.h>
39#include <iprt/critsect.h>
40#include <iprt/mem.h>
41#include <iprt/semaphore.h>
42#include <iprt/string.h>
43#include <iprt/uuid.h>
44#include "VBoxDD.h"
45
46
47/*********************************************************************************************************************************
48* Defined Constants And Macros *
49*********************************************************************************************************************************/
50/** @name USB MSD string IDs
51 * @{ */
52#define USBMSD_STR_ID_MANUFACTURER 1
53#define USBMSD_STR_ID_PRODUCT_HD 2
54#define USBMSD_STR_ID_PRODUCT_CDROM 3
55/** @} */
56
57/** @name USB MSD vendor and product IDs
58 * @{ */
59#define VBOX_USB_VENDOR 0x80EE
60#define USBMSD_PID_HD 0x0030
61#define USBMSD_PID_CD 0x0031
62/** @} */
63
64/** Saved state version. */
65#define USB_MSD_SAVED_STATE_VERSION 2
66/** Saved state vesion before the cleanup. */
67#define USB_MSD_SAVED_STATE_VERSION_PRE_CLEANUP 1
68
69
70/*********************************************************************************************************************************
71* Structures and Typedefs *
72*********************************************************************************************************************************/
73
74/**
75 * USB MSD Command Block Wrapper or CBW. The command block
76 * itself (CBWCB) contains protocol-specific data (here SCSI).
77 */
78#pragma pack(1)
79typedef struct USBCBW
80{
81 uint32_t dCBWSignature;
82#define USBCBW_SIGNATURE UINT32_C(0x43425355)
83 uint32_t dCBWTag;
84 uint32_t dCBWDataTransferLength;
85 uint8_t bmCBWFlags;
86#define USBCBW_DIR_MASK RT_BIT(7)
87#define USBCBW_DIR_OUT 0
88#define USBCBW_DIR_IN RT_BIT(7)
89 uint8_t bCBWLun;
90 uint8_t bCBWCBLength;
91 uint8_t CBWCB[16];
92} USBCBW;
93#pragma pack()
94AssertCompileSize(USBCBW, 31);
95/** Pointer to a Command Block Wrapper. */
96typedef USBCBW *PUSBCBW;
97/** Pointer to a const Command Block Wrapper. */
98typedef const USBCBW *PCUSBCBW;
99
100/**
101 * USB MSD Command Status Wrapper or CSW.
102 */
103#pragma pack(1)
104typedef struct USBCSW
105{
106 uint32_t dCSWSignature;
107#define USBCSW_SIGNATURE UINT32_C(0x53425355)
108 uint32_t dCSWTag;
109 uint32_t dCSWDataResidue;
110#define USBCSW_STATUS_OK UINT8_C(0)
111#define USBCSW_STATUS_FAILED UINT8_C(1)
112#define USBCSW_STATUS_PHASE_ERROR UINT8_C(2)
113 uint8_t bCSWStatus;
114} USBCSW;
115#pragma pack()
116AssertCompileSize(USBCSW, 13);
117/** Pointer to a Command Status Wrapper. */
118typedef USBCSW *PUSBCSW;
119/** Pointer to a const Command Status Wrapper. */
120typedef const USBCSW *PCUSBCSW;
121
122
123/**
124 * The USB MSD request state.
125 */
126typedef enum USBMSDREQSTATE
127{
128 /** Invalid status. */
129 USBMSDREQSTATE_INVALID = 0,
130 /** Ready to receive a new SCSI command. */
131 USBMSDREQSTATE_READY,
132 /** Waiting for the host to supply data. */
133 USBMSDREQSTATE_DATA_FROM_HOST,
134 /** The SCSI request is being executed by the driver. */
135 USBMSDREQSTATE_EXECUTING,
136 /** Have (more) data for the host. */
137 USBMSDREQSTATE_DATA_TO_HOST,
138 /** Waiting to supply status information to the host. */
139 USBMSDREQSTATE_STATUS,
140 /** Destroy the request upon completion.
141 * This is set when the SCSI request doesn't complete before for the device or
142 * mass storage reset operation times out. USBMSD::pReq will be set to NULL
143 * and the only reference to this request will be with DrvSCSI. */
144 USBMSDREQSTATE_DESTROY_ON_COMPLETION,
145 /** The end of the valid states. */
146 USBMSDREQSTATE_END,
147 /** 32bit blow up hack. */
148 USBMSDREQSTATE_32BIT_HACK = 0x7fffffff
149} USBMSDREQSTATE;
150
151
152/**
153 * A pending USB MSD request.
154 */
155typedef struct USBMSDREQ
156{
157 /** The state of the request. */
158 USBMSDREQSTATE enmState;
159 /** The I/O requesthandle .*/
160 PDMMEDIAEXIOREQ hIoReq;
161 /** The size of the data buffer. */
162 uint32_t cbBuf;
163 /** Pointer to the data buffer. */
164 uint8_t *pbBuf;
165 /** Current buffer offset. */
166 uint32_t offBuf;
167 /** The current Cbw when we're in the pending state. */
168 USBCBW Cbw;
169 /** The status of a completed SCSI request. */
170 uint8_t iScsiReqStatus;
171} USBMSDREQ;
172/** Pointer to a USB MSD request. */
173typedef USBMSDREQ *PUSBMSDREQ;
174
175
176/**
177 * Endpoint status data.
178 */
179typedef struct USBMSDEP
180{
181 bool fHalted;
182} USBMSDEP;
183/** Pointer to the endpoint status. */
184typedef USBMSDEP *PUSBMSDEP;
185
186
187/**
188 * A URB queue.
189 */
190typedef struct USBMSDURBQUEUE
191{
192 /** The head pointer. */
193 PVUSBURB pHead;
194 /** Where to insert the next entry. */
195 PVUSBURB *ppTail;
196} USBMSDURBQUEUE;
197/** Pointer to a URB queue. */
198typedef USBMSDURBQUEUE *PUSBMSDURBQUEUE;
199/** Pointer to a const URB queue. */
200typedef USBMSDURBQUEUE const *PCUSBMSDURBQUEUE;
201
202
203/**
204 * The USB MSD instance data.
205 */
206typedef struct USBMSD
207{
208 /** Pointer back to the PDM USB Device instance structure. */
209 PPDMUSBINS pUsbIns;
210 /** Critical section protecting the device state. */
211 RTCRITSECT CritSect;
212
213 /** The current configuration.
214 * (0 - default, 1 - the only, i.e configured.) */
215 uint8_t bConfigurationValue;
216 /** Endpoint 0 is the default control pipe, 1 is the host->dev bulk pipe and 2
217 * is the dev->host one. */
218 USBMSDEP aEps[3];
219 /** The current request. */
220 PUSBMSDREQ pReq;
221
222 /** Pending to-host queue.
223 * The URBs waiting here are pending the completion of the current request and
224 * data or status to become available.
225 */
226 USBMSDURBQUEUE ToHostQueue;
227
228 /** Done queue
229 * The URBs stashed here are waiting to be reaped. */
230 USBMSDURBQUEUE DoneQueue;
231 /** Signalled when adding an URB to the done queue and fHaveDoneQueueWaiter
232 * is set. */
233 RTSEMEVENT hEvtDoneQueue;
234 /** Someone is waiting on the done queue. */
235 bool fHaveDoneQueueWaiter;
236
237 /** Whether to signal the reset semaphore when the current request completes. */
238 bool fSignalResetSem;
239 /** Semaphore usbMsdUsbReset waits on when a request is executing at reset
240 * time. Only signalled when fSignalResetSem is set. */
241 RTSEMEVENTMULTI hEvtReset;
242 /** The reset URB.
243 * This is waiting for SCSI request completion before finishing the reset. */
244 PVUSBURB pResetUrb;
245 /** Indicates that PDMUsbHlpAsyncNotificationCompleted should be called when
246 * the MSD is entering the idle state. */
247 volatile bool fSignalIdle;
248
249 /** Indicates that this device is a CD-ROM. */
250 bool fIsCdrom;
251
252 /**
253 * LUN\#0 data.
254 */
255 struct
256 {
257 /** The base interface for LUN\#0. */
258 PDMIBASE IBase;
259 /** The media port interface fo LUN\#0. */
260 PDMIMEDIAPORT IMediaPort;
261 /** The extended media port interface for LUN\#0 */
262 PDMIMEDIAEXPORT IMediaExPort;
263
264 /** The base interface for the SCSI driver connected to LUN\#0. */
265 PPDMIBASE pIBase;
266 /** The media interface for th SCSI drver conected to LUN\#0. */
267 PPDMIMEDIA pIMedia;
268 /** The extended media inerface for the SCSI driver connected to LUN\#0. */
269 PPDMIMEDIAEX pIMediaEx;
270 } Lun0;
271
272} USBMSD;
273/** Pointer to the USB MSD instance data. */
274typedef USBMSD *PUSBMSD;
275
276
277/*********************************************************************************************************************************
278* Global Variables *
279*********************************************************************************************************************************/
280static const PDMUSBDESCCACHESTRING g_aUsbMsdStrings_en_US[] =
281{
282 { USBMSD_STR_ID_MANUFACTURER, "VirtualBox" },
283 { USBMSD_STR_ID_PRODUCT_HD, "USB Harddisk" },
284 { USBMSD_STR_ID_PRODUCT_CDROM, "USB CD-ROM" }
285};
286
287static const PDMUSBDESCCACHELANG g_aUsbMsdLanguages[] =
288{
289 { 0x0409, RT_ELEMENTS(g_aUsbMsdStrings_en_US), g_aUsbMsdStrings_en_US }
290};
291
292static const VUSBDESCENDPOINTEX g_aUsbMsdEndpointDescsFS[2] =
293{
294 {
295 {
296 /* .bLength = */ sizeof(VUSBDESCENDPOINT),
297 /* .bDescriptorType = */ VUSB_DT_ENDPOINT,
298 /* .bEndpointAddress = */ 0x81 /* ep=1, in */,
299 /* .bmAttributes = */ 2 /* bulk */,
300 /* .wMaxPacketSize = */ 64 /* maximum possible */,
301 /* .bInterval = */ 0 /* not applicable for bulk EP */
302 },
303 /* .pvMore = */ NULL,
304 /* .pvClass = */ NULL,
305 /* .cbClass = */ 0,
306 /* .pvSsepc = */ NULL,
307 /* .cbSsepc = */ 0
308 },
309 {
310 {
311 /* .bLength = */ sizeof(VUSBDESCENDPOINT),
312 /* .bDescriptorType = */ VUSB_DT_ENDPOINT,
313 /* .bEndpointAddress = */ 0x02 /* ep=2, out */,
314 /* .bmAttributes = */ 2 /* bulk */,
315 /* .wMaxPacketSize = */ 64 /* maximum possible */,
316 /* .bInterval = */ 0 /* not applicable for bulk EP */
317 },
318 /* .pvMore = */ NULL,
319 /* .pvClass = */ NULL,
320 /* .cbClass = */ 0,
321 /* .pvSsepc = */ NULL,
322 /* .cbSsepc = */ 0
323 }
324};
325
326static const VUSBDESCENDPOINTEX g_aUsbMsdEndpointDescsHS[2] =
327{
328 {
329 {
330 /* .bLength = */ sizeof(VUSBDESCENDPOINT),
331 /* .bDescriptorType = */ VUSB_DT_ENDPOINT,
332 /* .bEndpointAddress = */ 0x81 /* ep=1, in */,
333 /* .bmAttributes = */ 2 /* bulk */,
334 /* .wMaxPacketSize = */ 512 /* HS bulk packet size */,
335 /* .bInterval = */ 0 /* no NAKs */
336 },
337 /* .pvMore = */ NULL,
338 /* .pvClass = */ NULL,
339 /* .cbClass = */ 0,
340 /* .pvSsepc = */ NULL,
341 /* .cbSsepc = */ 0
342 },
343 {
344 {
345 /* .bLength = */ sizeof(VUSBDESCENDPOINT),
346 /* .bDescriptorType = */ VUSB_DT_ENDPOINT,
347 /* .bEndpointAddress = */ 0x02 /* ep=2, out */,
348 /* .bmAttributes = */ 2 /* bulk */,
349 /* .wMaxPacketSize = */ 512 /* HS bulk packet size */,
350 /* .bInterval = */ 0 /* no NAKs */
351 },
352 /* .pvMore = */ NULL,
353 /* .pvClass = */ NULL,
354 /* .cbClass = */ 0,
355 /* .pvSsepc = */ NULL,
356 /* .cbSsepc = */ 0
357 }
358};
359
360static const VUSBDESCSSEPCOMPANION g_aUsbMsdEpCompanionSS =
361{
362 /* .bLength = */ sizeof(VUSBDESCSSEPCOMPANION),
363 /* .bDescriptorType = */ VUSB_DT_SS_ENDPOINT_COMPANION,
364 /* .bMaxBurst = */ 15 /* we can burst all the way */,
365 /* .bmAttributes = */ 0 /* no streams */,
366 /* .wBytesPerInterval = */ 0 /* not a periodic endpoint */
367};
368
369static const VUSBDESCENDPOINTEX g_aUsbMsdEndpointDescsSS[2] =
370{
371 {
372 {
373 /* .bLength = */ sizeof(VUSBDESCENDPOINT),
374 /* .bDescriptorType = */ VUSB_DT_ENDPOINT,
375 /* .bEndpointAddress = */ 0x81 /* ep=1, in */,
376 /* .bmAttributes = */ 2 /* bulk */,
377 /* .wMaxPacketSize = */ 1024 /* SS bulk packet size */,
378 /* .bInterval = */ 0 /* no NAKs */
379 },
380 /* .pvMore = */ NULL,
381 /* .pvClass = */ NULL,
382 /* .cbClass = */ 0,
383 /* .pvSsepc = */ &g_aUsbMsdEpCompanionSS,
384 /* .cbSsepc = */ sizeof(g_aUsbMsdEpCompanionSS)
385 },
386 {
387 {
388 /* .bLength = */ sizeof(VUSBDESCENDPOINT),
389 /* .bDescriptorType = */ VUSB_DT_ENDPOINT,
390 /* .bEndpointAddress = */ 0x02 /* ep=2, out */,
391 /* .bmAttributes = */ 2 /* bulk */,
392 /* .wMaxPacketSize = */ 1024 /* SS bulk packet size */,
393 /* .bInterval = */ 0 /* no NAKs */
394 },
395 /* .pvMore = */ NULL,
396 /* .pvClass = */ NULL,
397 /* .cbClass = */ 0,
398 /* .pvSsepc = */ &g_aUsbMsdEpCompanionSS,
399 /* .cbSsepc = */ sizeof(g_aUsbMsdEpCompanionSS)
400 }
401};
402
403static const VUSBDESCINTERFACEEX g_UsbMsdInterfaceDescFS =
404{
405 {
406 /* .bLength = */ sizeof(VUSBDESCINTERFACE),
407 /* .bDescriptorType = */ VUSB_DT_INTERFACE,
408 /* .bInterfaceNumber = */ 0,
409 /* .bAlternateSetting = */ 0,
410 /* .bNumEndpoints = */ 2,
411 /* .bInterfaceClass = */ 8 /* Mass Storage */,
412 /* .bInterfaceSubClass = */ 6 /* SCSI transparent command set */,
413 /* .bInterfaceProtocol = */ 0x50 /* Bulk-Only Transport */,
414 /* .iInterface = */ 0
415 },
416 /* .pvMore = */ NULL,
417 /* .pvClass = */ NULL,
418 /* .cbClass = */ 0,
419 &g_aUsbMsdEndpointDescsFS[0],
420 /* .pIAD = */ NULL,
421 /* .cbIAD = */ 0
422};
423
424static const VUSBDESCINTERFACEEX g_UsbMsdInterfaceDescHS =
425{
426 {
427 /* .bLength = */ sizeof(VUSBDESCINTERFACE),
428 /* .bDescriptorType = */ VUSB_DT_INTERFACE,
429 /* .bInterfaceNumber = */ 0,
430 /* .bAlternateSetting = */ 0,
431 /* .bNumEndpoints = */ 2,
432 /* .bInterfaceClass = */ 8 /* Mass Storage */,
433 /* .bInterfaceSubClass = */ 6 /* SCSI transparent command set */,
434 /* .bInterfaceProtocol = */ 0x50 /* Bulk-Only Transport */,
435 /* .iInterface = */ 0
436 },
437 /* .pvMore = */ NULL,
438 /* .pvClass = */ NULL,
439 /* .cbClass = */ 0,
440 &g_aUsbMsdEndpointDescsHS[0],
441 /* .pIAD = */ NULL,
442 /* .cbIAD = */ 0
443};
444
445static const VUSBDESCINTERFACEEX g_UsbMsdInterfaceDescSS =
446{
447 {
448 /* .bLength = */ sizeof(VUSBDESCINTERFACE),
449 /* .bDescriptorType = */ VUSB_DT_INTERFACE,
450 /* .bInterfaceNumber = */ 0,
451 /* .bAlternateSetting = */ 0,
452 /* .bNumEndpoints = */ 2,
453 /* .bInterfaceClass = */ 8 /* Mass Storage */,
454 /* .bInterfaceSubClass = */ 6 /* SCSI transparent command set */,
455 /* .bInterfaceProtocol = */ 0x50 /* Bulk-Only Transport */,
456 /* .iInterface = */ 0
457 },
458 /* .pvMore = */ NULL,
459 /* .pvClass = */ NULL,
460 /* .cbClass = */ 0,
461 &g_aUsbMsdEndpointDescsSS[0],
462 /* .pIAD = */ NULL,
463 /* .cbIAD = */ 0
464};
465
466static const VUSBINTERFACE g_aUsbMsdInterfacesFS[] =
467{
468 { &g_UsbMsdInterfaceDescFS, /* .cSettings = */ 1 },
469};
470
471static const VUSBINTERFACE g_aUsbMsdInterfacesHS[] =
472{
473 { &g_UsbMsdInterfaceDescHS, /* .cSettings = */ 1 },
474};
475
476static const VUSBINTERFACE g_aUsbMsdInterfacesSS[] =
477{
478 { &g_UsbMsdInterfaceDescSS, /* .cSettings = */ 1 },
479};
480
481static const VUSBDESCCONFIGEX g_UsbMsdConfigDescFS =
482{
483 {
484 /* .bLength = */ sizeof(VUSBDESCCONFIG),
485 /* .bDescriptorType = */ VUSB_DT_CONFIG,
486 /* .wTotalLength = */ 0 /* recalculated on read */,
487 /* .bNumInterfaces = */ RT_ELEMENTS(g_aUsbMsdInterfacesFS),
488 /* .bConfigurationValue =*/ 1,
489 /* .iConfiguration = */ 0,
490 /* .bmAttributes = */ RT_BIT(7),
491 /* .MaxPower = */ 50 /* 100mA */
492 },
493 NULL, /* pvMore */
494 NULL, /* pvClass */
495 0, /* cbClass */
496 &g_aUsbMsdInterfacesFS[0],
497 NULL /* pvOriginal */
498};
499
500static const VUSBDESCCONFIGEX g_UsbMsdConfigDescHS =
501{
502 {
503 /* .bLength = */ sizeof(VUSBDESCCONFIG),
504 /* .bDescriptorType = */ VUSB_DT_CONFIG,
505 /* .wTotalLength = */ 0 /* recalculated on read */,
506 /* .bNumInterfaces = */ RT_ELEMENTS(g_aUsbMsdInterfacesHS),
507 /* .bConfigurationValue =*/ 1,
508 /* .iConfiguration = */ 0,
509 /* .bmAttributes = */ RT_BIT(7),
510 /* .MaxPower = */ 50 /* 100mA */
511 },
512 NULL, /* pvMore */
513 NULL, /* pvClass */
514 0, /* cbClass */
515 &g_aUsbMsdInterfacesHS[0],
516 NULL /* pvOriginal */
517};
518
519static const VUSBDESCCONFIGEX g_UsbMsdConfigDescSS =
520{
521 {
522 /* .bLength = */ sizeof(VUSBDESCCONFIG),
523 /* .bDescriptorType = */ VUSB_DT_CONFIG,
524 /* .wTotalLength = */ 0 /* recalculated on read */,
525 /* .bNumInterfaces = */ RT_ELEMENTS(g_aUsbMsdInterfacesSS),
526 /* .bConfigurationValue =*/ 1,
527 /* .iConfiguration = */ 0,
528 /* .bmAttributes = */ RT_BIT(7),
529 /* .MaxPower = */ 50 /* 100mA */
530 },
531 NULL, /* pvMore */
532 NULL, /* pvClass */
533 0, /* cbClass */
534 &g_aUsbMsdInterfacesSS[0],
535 NULL /* pvOriginal */
536};
537
538static const VUSBDESCDEVICE g_UsbMsdDeviceDesc20 =
539{
540 /* .bLength = */ sizeof(g_UsbMsdDeviceDesc20),
541 /* .bDescriptorType = */ VUSB_DT_DEVICE,
542 /* .bcdUsb = */ 0x200, /* USB 2.0 */
543 /* .bDeviceClass = */ 0 /* Class specified in the interface desc. */,
544 /* .bDeviceSubClass = */ 0 /* Subclass specified in the interface desc. */,
545 /* .bDeviceProtocol = */ 0 /* Protocol specified in the interface desc. */,
546 /* .bMaxPacketSize0 = */ 64,
547 /* .idVendor = */ VBOX_USB_VENDOR,
548 /* .idProduct = */ USBMSD_PID_HD,
549 /* .bcdDevice = */ 0x0100, /* 1.0 */
550 /* .iManufacturer = */ USBMSD_STR_ID_MANUFACTURER,
551 /* .iProduct = */ USBMSD_STR_ID_PRODUCT_HD,
552 /* .iSerialNumber = */ 0,
553 /* .bNumConfigurations = */ 1
554};
555
556static const VUSBDESCDEVICE g_UsbCdDeviceDesc20 =
557{
558 /* .bLength = */ sizeof(g_UsbCdDeviceDesc20),
559 /* .bDescriptorType = */ VUSB_DT_DEVICE,
560 /* .bcdUsb = */ 0x200, /* USB 2.0 */
561 /* .bDeviceClass = */ 0 /* Class specified in the interface desc. */,
562 /* .bDeviceSubClass = */ 0 /* Subclass specified in the interface desc. */,
563 /* .bDeviceProtocol = */ 0 /* Protocol specified in the interface desc. */,
564 /* .bMaxPacketSize0 = */ 64,
565 /* .idVendor = */ VBOX_USB_VENDOR,
566 /* .idProduct = */ USBMSD_PID_CD,
567 /* .bcdDevice = */ 0x0100, /* 1.0 */
568 /* .iManufacturer = */ USBMSD_STR_ID_MANUFACTURER,
569 /* .iProduct = */ USBMSD_STR_ID_PRODUCT_CDROM,
570 /* .iSerialNumber = */ 0,
571 /* .bNumConfigurations = */ 1
572};
573
574static const VUSBDESCDEVICE g_UsbMsdDeviceDesc30 =
575{
576 /* .bLength = */ sizeof(g_UsbMsdDeviceDesc30),
577 /* .bDescriptorType = */ VUSB_DT_DEVICE,
578 /* .bcdUsb = */ 0x300, /* USB 2.0 */
579 /* .bDeviceClass = */ 0 /* Class specified in the interface desc. */,
580 /* .bDeviceSubClass = */ 0 /* Subclass specified in the interface desc. */,
581 /* .bDeviceProtocol = */ 0 /* Protocol specified in the interface desc. */,
582 /* .bMaxPacketSize0 = */ 9 /* 512, the only option for USB3. */,
583 /* .idVendor = */ VBOX_USB_VENDOR,
584 /* .idProduct = */ USBMSD_PID_HD,
585 /* .bcdDevice = */ 0x0110, /* 1.10 */
586 /* .iManufacturer = */ USBMSD_STR_ID_MANUFACTURER,
587 /* .iProduct = */ USBMSD_STR_ID_PRODUCT_HD,
588 /* .iSerialNumber = */ 0,
589 /* .bNumConfigurations = */ 1
590};
591
592static const VUSBDESCDEVICE g_UsbCdDeviceDesc30 =
593{
594 /* .bLength = */ sizeof(g_UsbCdDeviceDesc30),
595 /* .bDescriptorType = */ VUSB_DT_DEVICE,
596 /* .bcdUsb = */ 0x300, /* USB 2.0 */
597 /* .bDeviceClass = */ 0 /* Class specified in the interface desc. */,
598 /* .bDeviceSubClass = */ 0 /* Subclass specified in the interface desc. */,
599 /* .bDeviceProtocol = */ 0 /* Protocol specified in the interface desc. */,
600 /* .bMaxPacketSize0 = */ 9 /* 512, the only option for USB3. */,
601 /* .idVendor = */ VBOX_USB_VENDOR,
602 /* .idProduct = */ USBMSD_PID_CD,
603 /* .bcdDevice = */ 0x0110, /* 1.10 */
604 /* .iManufacturer = */ USBMSD_STR_ID_MANUFACTURER,
605 /* .iProduct = */ USBMSD_STR_ID_PRODUCT_CDROM,
606 /* .iSerialNumber = */ 0,
607 /* .bNumConfigurations = */ 1
608};
609
610static const VUSBDEVICEQUALIFIER g_UsbMsdDeviceQualifier =
611{
612 /* .bLength = */ sizeof(g_UsbMsdDeviceQualifier),
613 /* .bDescriptorType = */ VUSB_DT_DEVICE_QUALIFIER,
614 /* .bcdUsb = */ 0x200, /* USB 2.0 */
615 /* .bDeviceClass = */ 0 /* Class specified in the interface desc. */,
616 /* .bDeviceSubClass = */ 0 /* Subclass specified in the interface desc. */,
617 /* .bDeviceProtocol = */ 0 /* Protocol specified in the interface desc. */,
618 /* .bMaxPacketSize0 = */ 64,
619 /* .bNumConfigurations = */ 1,
620 /* .bReserved = */ 0
621};
622
623static const struct {
624 VUSBDESCBOS bos;
625 VUSBDESCSSDEVCAP sscap;
626} g_UsbMsdBOS =
627{
628 {
629 /* .bLength = */ sizeof(g_UsbMsdBOS.bos),
630 /* .bDescriptorType = */ VUSB_DT_BOS,
631 /* .wTotalLength = */ sizeof(g_UsbMsdBOS),
632 /* .bNumDeviceCaps = */ 1
633 },
634 {
635 /* .bLength = */ sizeof(VUSBDESCSSDEVCAP),
636 /* .bDescriptorType = */ VUSB_DT_DEVICE_CAPABILITY,
637 /* .bDevCapabilityType = */ VUSB_DCT_SUPERSPEED_USB,
638 /* .bmAttributes = */ 0 /* No LTM. */,
639 /* .wSpeedsSupported = */ 0xe /* Any speed is good. */,
640 /* .bFunctionalitySupport = */ 2 /* Want HS at least. */,
641 /* .bU1DevExitLat = */ 0, /* We are blazingly fast. */
642 /* .wU2DevExitLat = */ 0
643 }
644};
645
646static const PDMUSBDESCCACHE g_UsbMsdDescCacheFS =
647{
648 /* .pDevice = */ &g_UsbMsdDeviceDesc20,
649 /* .paConfigs = */ &g_UsbMsdConfigDescFS,
650 /* .paLanguages = */ g_aUsbMsdLanguages,
651 /* .cLanguages = */ RT_ELEMENTS(g_aUsbMsdLanguages),
652 /* .fUseCachedDescriptors = */ true,
653 /* .fUseCachedStringsDescriptors = */ true
654};
655
656static const PDMUSBDESCCACHE g_UsbCdDescCacheFS =
657{
658 /* .pDevice = */ &g_UsbCdDeviceDesc20,
659 /* .paConfigs = */ &g_UsbMsdConfigDescFS,
660 /* .paLanguages = */ g_aUsbMsdLanguages,
661 /* .cLanguages = */ RT_ELEMENTS(g_aUsbMsdLanguages),
662 /* .fUseCachedDescriptors = */ true,
663 /* .fUseCachedStringsDescriptors = */ true
664};
665
666static const PDMUSBDESCCACHE g_UsbMsdDescCacheHS =
667{
668 /* .pDevice = */ &g_UsbMsdDeviceDesc20,
669 /* .paConfigs = */ &g_UsbMsdConfigDescHS,
670 /* .paLanguages = */ g_aUsbMsdLanguages,
671 /* .cLanguages = */ RT_ELEMENTS(g_aUsbMsdLanguages),
672 /* .fUseCachedDescriptors = */ true,
673 /* .fUseCachedStringsDescriptors = */ true
674};
675
676static const PDMUSBDESCCACHE g_UsbCdDescCacheHS =
677{
678 /* .pDevice = */ &g_UsbCdDeviceDesc20,
679 /* .paConfigs = */ &g_UsbMsdConfigDescHS,
680 /* .paLanguages = */ g_aUsbMsdLanguages,
681 /* .cLanguages = */ RT_ELEMENTS(g_aUsbMsdLanguages),
682 /* .fUseCachedDescriptors = */ true,
683 /* .fUseCachedStringsDescriptors = */ true
684};
685
686static const PDMUSBDESCCACHE g_UsbMsdDescCacheSS =
687{
688 /* .pDevice = */ &g_UsbMsdDeviceDesc30,
689 /* .paConfigs = */ &g_UsbMsdConfigDescSS,
690 /* .paLanguages = */ g_aUsbMsdLanguages,
691 /* .cLanguages = */ RT_ELEMENTS(g_aUsbMsdLanguages),
692 /* .fUseCachedDescriptors = */ true,
693 /* .fUseCachedStringsDescriptors = */ true
694};
695
696static const PDMUSBDESCCACHE g_UsbCdDescCacheSS =
697{
698 /* .pDevice = */ &g_UsbCdDeviceDesc30,
699 /* .paConfigs = */ &g_UsbMsdConfigDescSS,
700 /* .paLanguages = */ g_aUsbMsdLanguages,
701 /* .cLanguages = */ RT_ELEMENTS(g_aUsbMsdLanguages),
702 /* .fUseCachedDescriptors = */ true,
703 /* .fUseCachedStringsDescriptors = */ true
704};
705
706
707/*********************************************************************************************************************************
708* Internal Functions *
709*********************************************************************************************************************************/
710static int usbMsdHandleBulkDevToHost(PUSBMSD pThis, PUSBMSDEP pEp, PVUSBURB pUrb);
711
712
713/**
714 * Initializes an URB queue.
715 *
716 * @param pQueue The URB queue.
717 */
718static void usbMsdQueueInit(PUSBMSDURBQUEUE pQueue)
719{
720 pQueue->pHead = NULL;
721 pQueue->ppTail = &pQueue->pHead;
722}
723
724
725
726/**
727 * Inserts an URB at the end of the queue.
728 *
729 * @param pQueue The URB queue.
730 * @param pUrb The URB to insert.
731 */
732DECLINLINE(void) usbMsdQueueAddTail(PUSBMSDURBQUEUE pQueue, PVUSBURB pUrb)
733{
734 pUrb->Dev.pNext = NULL;
735 *pQueue->ppTail = pUrb;
736 pQueue->ppTail = &pUrb->Dev.pNext;
737}
738
739
740/**
741 * Unlinks the head of the queue and returns it.
742 *
743 * @returns The head entry.
744 * @param pQueue The URB queue.
745 */
746DECLINLINE(PVUSBURB) usbMsdQueueRemoveHead(PUSBMSDURBQUEUE pQueue)
747{
748 PVUSBURB pUrb = pQueue->pHead;
749 if (pUrb)
750 {
751 PVUSBURB pNext = pUrb->Dev.pNext;
752 pQueue->pHead = pNext;
753 if (!pNext)
754 pQueue->ppTail = &pQueue->pHead;
755 else
756 pUrb->Dev.pNext = NULL;
757 }
758 return pUrb;
759}
760
761
762/**
763 * Removes an URB from anywhere in the queue.
764 *
765 * @returns true if found, false if not.
766 * @param pQueue The URB queue.
767 * @param pUrb The URB to remove.
768 */
769DECLINLINE(bool) usbMsdQueueRemove(PUSBMSDURBQUEUE pQueue, PVUSBURB pUrb)
770{
771 PVUSBURB pCur = pQueue->pHead;
772 if (pCur == pUrb)
773 pQueue->pHead = pUrb->Dev.pNext;
774 else
775 {
776 while (pCur)
777 {
778 if (pCur->Dev.pNext == pUrb)
779 {
780 pCur->Dev.pNext = pUrb->Dev.pNext;
781 break;
782 }
783 pCur = pCur->Dev.pNext;
784 }
785 if (!pCur)
786 return false;
787 }
788 if (!pUrb->Dev.pNext)
789 pQueue->ppTail = &pQueue->pHead;
790 return true;
791}
792
793
794#ifdef VBOX_STRICT
795/**
796 * Checks if the queue is empty or not.
797 *
798 * @returns true if it is, false if it isn't.
799 * @param pQueue The URB queue.
800 */
801DECLINLINE(bool) usbMsdQueueIsEmpty(PCUSBMSDURBQUEUE pQueue)
802{
803 return pQueue->pHead == NULL;
804}
805#endif /* VBOX_STRICT */
806
807
808/**
809 * Links an URB into the done queue.
810 *
811 * @param pThis The MSD instance.
812 * @param pUrb The URB.
813 */
814static void usbMsdLinkDone(PUSBMSD pThis, PVUSBURB pUrb)
815{
816 usbMsdQueueAddTail(&pThis->DoneQueue, pUrb);
817
818 if (pThis->fHaveDoneQueueWaiter)
819 {
820 int rc = RTSemEventSignal(pThis->hEvtDoneQueue);
821 AssertRC(rc);
822 }
823}
824
825
826
827
828/**
829 * Allocates a new request and does basic init.
830 *
831 * @returns Pointer to the new request. NULL if we're out of memory.
832 * @param pThis The MSD instance.
833 */
834static PUSBMSDREQ usbMsdReqAlloc(PUSBMSD pThis)
835{
836 PUSBMSDREQ pReq = NULL;
837 PDMMEDIAEXIOREQ hIoReq = NULL;
838
839 int rc = pThis->Lun0.pIMediaEx->pfnIoReqAlloc(pThis->Lun0.pIMediaEx, &hIoReq, (void **)&pReq,
840 0 /* uTag */, PDMIMEDIAEX_F_DEFAULT);
841 if (RT_SUCCESS(rc))
842 {
843 pReq->hIoReq = hIoReq;
844 pReq->enmState = USBMSDREQSTATE_READY;
845 pReq->iScsiReqStatus = 0xff;
846 }
847 else
848 LogRel(("usbMsdReqAlloc: Out of memory (%Rrc)\n", rc));
849
850 return pReq;
851}
852
853
854/**
855 * Frees a request.
856 *
857 * @param pThis The MSD instance.
858 * @param pReq The request.
859 */
860static void usbMsdReqFree(PUSBMSD pThis, PUSBMSDREQ pReq)
861{
862 /*
863 * Check the input.
864 */
865 AssertReturnVoid( pReq->enmState > USBMSDREQSTATE_INVALID
866 && pReq->enmState != USBMSDREQSTATE_EXECUTING
867 && pReq->enmState < USBMSDREQSTATE_END);
868 PPDMUSBINS pUsbIns = pThis->pUsbIns;
869 AssertPtrReturnVoid(pUsbIns);
870 AssertReturnVoid(PDM_VERSION_ARE_COMPATIBLE(pUsbIns->u32Version, PDM_USBINS_VERSION));
871
872 /*
873 * Invalidate it and free the associated resources.
874 */
875 pReq->enmState = USBMSDREQSTATE_INVALID;
876 pReq->cbBuf = 0;
877 pReq->offBuf = 0;
878
879 if (pReq->pbBuf)
880 {
881 PDMUsbHlpMMHeapFree(pUsbIns, pReq->pbBuf);
882 pReq->pbBuf = NULL;
883 }
884
885 int rc = pThis->Lun0.pIMediaEx->pfnIoReqFree(pThis->Lun0.pIMediaEx, pReq->hIoReq);
886 AssertRC(rc);
887}
888
889
890/**
891 * Prepares a request for execution or data buffering.
892 *
893 * @param pReq The request.
894 * @param pCbw The SCSI command block wrapper.
895 */
896static void usbMsdReqPrepare(PUSBMSDREQ pReq, PCUSBCBW pCbw)
897{
898 /* Copy the CBW */
899 uint8_t bCBWLen = RT_MIN(pCbw->bCBWCBLength, sizeof(pCbw->CBWCB));
900 size_t cbCopy = RT_UOFFSETOF_DYN(USBCBW, CBWCB[bCBWLen]);
901 memcpy(&pReq->Cbw, pCbw, cbCopy);
902 memset((uint8_t *)&pReq->Cbw + cbCopy, 0, sizeof(pReq->Cbw) - cbCopy);
903
904 /* Setup the SCSI request. */
905 pReq->offBuf = 0;
906 pReq->iScsiReqStatus = 0xff;
907}
908
909
910/**
911 * Makes sure that there is sufficient buffer space available.
912 *
913 * @returns Success indicator (true/false)
914 * @param pThis The MSD instance.
915 * @param pReq The request.
916 * @param cbBuf The required buffer space.
917 */
918static int usbMsdReqEnsureBuffer(PUSBMSD pThis, PUSBMSDREQ pReq, uint32_t cbBuf)
919{
920 if (RT_LIKELY(pReq->cbBuf >= cbBuf))
921 RT_BZERO(pReq->pbBuf, cbBuf);
922 else
923 {
924 PDMUsbHlpMMHeapFree(pThis->pUsbIns, pReq->pbBuf);
925 pReq->cbBuf = 0;
926
927 cbBuf = RT_ALIGN_Z(cbBuf, 0x1000);
928 pReq->pbBuf = (uint8_t *)PDMUsbHlpMMHeapAllocZ(pThis->pUsbIns, cbBuf);
929 if (!pReq->pbBuf)
930 return false;
931
932 pReq->cbBuf = cbBuf;
933 }
934 return true;
935}
936
937
938/**
939 * Completes the URB with a stalled state, halting the pipe.
940 */
941static int usbMsdCompleteStall(PUSBMSD pThis, PUSBMSDEP pEp, PVUSBURB pUrb, const char *pszWhy)
942{
943 RT_NOREF(pszWhy);
944 Log(("usbMsdCompleteStall/#%u: pUrb=%p:%s: %s\n", pThis->pUsbIns->iInstance, pUrb, pUrb->pszDesc, pszWhy));
945
946 pUrb->enmStatus = VUSBSTATUS_STALL;
947
948 /** @todo figure out if the stall is global or pipe-specific or both. */
949 if (pEp)
950 pEp->fHalted = true;
951 else
952 {
953 pThis->aEps[1].fHalted = true;
954 pThis->aEps[2].fHalted = true;
955 }
956
957 usbMsdLinkDone(pThis, pUrb);
958 return VINF_SUCCESS;
959}
960
961
962/**
963 * Completes the URB with a OK state.
964 */
965static int usbMsdCompleteOk(PUSBMSD pThis, PVUSBURB pUrb, size_t cbData)
966{
967 Log(("usbMsdCompleteOk/#%u: pUrb=%p:%s cbData=%#zx\n", pThis->pUsbIns->iInstance, pUrb, pUrb->pszDesc, cbData));
968
969 pUrb->enmStatus = VUSBSTATUS_OK;
970 pUrb->cbData = (uint32_t)cbData;
971
972 usbMsdLinkDone(pThis, pUrb);
973 return VINF_SUCCESS;
974}
975
976
977/**
978 * Reset worker for usbMsdUsbReset, usbMsdUsbSetConfiguration and
979 * usbMsdUrbHandleDefaultPipe.
980 *
981 * @returns VBox status code.
982 * @param pThis The MSD instance.
983 * @param pUrb Set when usbMsdUrbHandleDefaultPipe is the
984 * caller.
985 * @param fSetConfig Set when usbMsdUsbSetConfiguration is the
986 * caller.
987 */
988static int usbMsdResetWorker(PUSBMSD pThis, PVUSBURB pUrb, bool fSetConfig)
989{
990 /*
991 * Wait for the any command currently executing to complete before
992 * resetting. (We cannot cancel its execution.) How we do this depends
993 * on the reset method.
994 */
995 PUSBMSDREQ pReq = pThis->pReq;
996 if ( pReq
997 && pReq->enmState == USBMSDREQSTATE_EXECUTING)
998 {
999 /* Don't try to deal with the set config variant nor multiple build-only
1000 mass storage resets. */
1001 if (pThis->pResetUrb && (pUrb || fSetConfig))
1002 {
1003 Log(("usbMsdResetWorker: pResetUrb is already %p:%s - stalling\n", pThis->pResetUrb, pThis->pResetUrb->pszDesc));
1004 return usbMsdCompleteStall(pThis, NULL, pUrb, "pResetUrb");
1005 }
1006
1007 /* Bulk-Only Mass Storage Reset: Complete the reset on request completion. */
1008 if (pUrb)
1009 {
1010 pThis->pResetUrb = pUrb;
1011 Log(("usbMsdResetWorker: Setting pResetUrb to %p:%s\n", pThis->pResetUrb, pThis->pResetUrb->pszDesc));
1012 return VINF_SUCCESS;
1013 }
1014
1015 /* Device reset: Wait for up to 10 ms. If it doesn't work, ditch
1016 whole the request structure. We'll allocate a new one when needed. */
1017 Log(("usbMsdResetWorker: Waiting for completion...\n"));
1018 Assert(!pThis->fSignalResetSem);
1019 pThis->fSignalResetSem = true;
1020 RTSemEventMultiReset(pThis->hEvtReset);
1021 RTCritSectLeave(&pThis->CritSect);
1022
1023 int rc = RTSemEventMultiWait(pThis->hEvtReset, 10 /*ms*/);
1024
1025 RTCritSectEnter(&pThis->CritSect);
1026 pThis->fSignalResetSem = false;
1027 if ( RT_FAILURE(rc)
1028 || pReq->enmState == USBMSDREQSTATE_EXECUTING)
1029 {
1030 Log(("usbMsdResetWorker: Didn't complete, ditching the current request (%p)!\n", pReq));
1031 Assert(pReq == pThis->pReq);
1032 pReq->enmState = USBMSDREQSTATE_DESTROY_ON_COMPLETION;
1033 pThis->pReq = NULL;
1034 pReq = NULL;
1035 }
1036 }
1037
1038 /*
1039 * Reset the request and device state.
1040 */
1041 if (pReq)
1042 {
1043 pReq->enmState = USBMSDREQSTATE_READY;
1044 pReq->iScsiReqStatus = 0xff;
1045 }
1046
1047 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aEps); i++)
1048 pThis->aEps[i].fHalted = false;
1049
1050 if (!pUrb && !fSetConfig) /* (only device reset) */
1051 pThis->bConfigurationValue = 0; /* default */
1052
1053 /*
1054 * Ditch all pending URBs.
1055 */
1056 PVUSBURB pCurUrb;
1057 while ((pCurUrb = usbMsdQueueRemoveHead(&pThis->ToHostQueue)) != NULL)
1058 {
1059 pCurUrb->enmStatus = VUSBSTATUS_CRC;
1060 usbMsdLinkDone(pThis, pCurUrb);
1061 }
1062
1063 pCurUrb = pThis->pResetUrb;
1064 if (pCurUrb)
1065 {
1066 pThis->pResetUrb = NULL;
1067 pCurUrb->enmStatus = VUSBSTATUS_CRC;
1068 usbMsdLinkDone(pThis, pCurUrb);
1069 }
1070
1071 if (pUrb)
1072 return usbMsdCompleteOk(pThis, pUrb, 0);
1073 return VINF_SUCCESS;
1074}
1075
1076
1077/**
1078 * Process a completed request.
1079 *
1080 * @returns nothing.
1081 * @param pThis The MSD instance.
1082 * @param pReq The request.
1083 * @param rcReq The completion status.
1084 */
1085static void usbMsdReqComplete(PUSBMSD pThis, PUSBMSDREQ pReq, int rcReq)
1086{
1087 RT_NOREF1(rcReq);
1088
1089 Log(("usbMsdLun0IoReqCompleteNotify: pReq=%p dCBWTag=%#x iScsiReqStatus=%u \n", pReq, pReq->Cbw.dCBWTag, pReq->iScsiReqStatus));
1090 RTCritSectEnter(&pThis->CritSect);
1091
1092 if (pReq->enmState != USBMSDREQSTATE_DESTROY_ON_COMPLETION)
1093 {
1094 Assert(pReq->enmState == USBMSDREQSTATE_EXECUTING);
1095 Assert(pThis->pReq == pReq);
1096
1097 /*
1098 * Advance the state machine. The state machine is not affected by
1099 * SCSI errors.
1100 */
1101 if ((pReq->Cbw.bmCBWFlags & USBCBW_DIR_MASK) == USBCBW_DIR_OUT)
1102 {
1103 pReq->enmState = USBMSDREQSTATE_STATUS;
1104 Log(("usbMsdLun0IoReqCompleteNotify: Entering STATUS\n"));
1105 }
1106 else
1107 {
1108 pReq->enmState = USBMSDREQSTATE_DATA_TO_HOST;
1109 Log(("usbMsdLun0IoReqCompleteNotify: Entering DATA_TO_HOST\n"));
1110 }
1111
1112 /*
1113 * Deal with pending to-host URBs.
1114 */
1115 for (;;)
1116 {
1117 PVUSBURB pUrb = usbMsdQueueRemoveHead(&pThis->ToHostQueue);
1118 if (!pUrb)
1119 break;
1120
1121 /* Process it the normal way. */
1122 usbMsdHandleBulkDevToHost(pThis, &pThis->aEps[1], pUrb);
1123 }
1124 }
1125 else
1126 {
1127 Log(("usbMsdLun0IoReqCompleteNotify: freeing %p\n", pReq));
1128 usbMsdReqFree(pThis, pReq);
1129 }
1130
1131 if (pThis->fSignalResetSem)
1132 RTSemEventMultiSignal(pThis->hEvtReset);
1133
1134 if (pThis->pResetUrb)
1135 {
1136 pThis->pResetUrb = NULL;
1137 usbMsdResetWorker(pThis, pThis->pResetUrb, false /*fSetConfig*/);
1138 }
1139
1140 RTCritSectLeave(&pThis->CritSect);
1141}
1142
1143
1144/**
1145 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCopyFromBuf}
1146 */
1147static DECLCALLBACK(int) usbMsdLun0IoReqCopyFromBuf(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
1148 void *pvIoReqAlloc, uint32_t offDst, PRTSGBUF pSgBuf,
1149 size_t cbCopy)
1150{
1151 RT_NOREF2(pInterface, hIoReq);
1152 int rc = VINF_SUCCESS;
1153 PUSBMSDREQ pReq = (PUSBMSDREQ)pvIoReqAlloc;
1154
1155 if (RT_UNLIKELY(offDst + cbCopy > pReq->cbBuf))
1156 rc = VERR_PDM_MEDIAEX_IOBUF_OVERFLOW;
1157 else
1158 {
1159 size_t cbCopied = RTSgBufCopyToBuf(pSgBuf, pReq->pbBuf + offDst, cbCopy);
1160 Assert(cbCopied == cbCopy); RT_NOREF(cbCopied);
1161 }
1162
1163 return rc;
1164}
1165
1166
1167/**
1168 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCopyToBuf}
1169 */
1170static DECLCALLBACK(int) usbMsdLun0IoReqCopyToBuf(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
1171 void *pvIoReqAlloc, uint32_t offSrc, PRTSGBUF pSgBuf,
1172 size_t cbCopy)
1173{
1174 RT_NOREF2(pInterface, hIoReq);
1175 int rc = VINF_SUCCESS;
1176 PUSBMSDREQ pReq = (PUSBMSDREQ)pvIoReqAlloc;
1177
1178 if (RT_UNLIKELY(offSrc + cbCopy > pReq->cbBuf))
1179 rc = VERR_PDM_MEDIAEX_IOBUF_UNDERRUN;
1180 else
1181 {
1182 size_t cbCopied = RTSgBufCopyFromBuf(pSgBuf, pReq->pbBuf + offSrc, cbCopy);
1183 Assert(cbCopied == cbCopy); RT_NOREF(cbCopied);
1184 }
1185
1186 return rc;
1187}
1188
1189
1190/**
1191 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCompleteNotify}
1192 */
1193static DECLCALLBACK(int) usbMsdLun0IoReqCompleteNotify(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
1194 void *pvIoReqAlloc, int rcReq)
1195{
1196 RT_NOREF1(hIoReq);
1197 PUSBMSD pThis = RT_FROM_MEMBER(pInterface, USBMSD, Lun0.IMediaExPort);
1198 PUSBMSDREQ pReq = (PUSBMSDREQ)pvIoReqAlloc;
1199
1200 usbMsdReqComplete(pThis, pReq, rcReq);
1201 return VINF_SUCCESS;
1202}
1203
1204
1205/**
1206 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqStateChanged}
1207 */
1208static DECLCALLBACK(void) usbMsdLun0IoReqStateChanged(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
1209 void *pvIoReqAlloc, PDMMEDIAEXIOREQSTATE enmState)
1210{
1211 RT_NOREF4(pInterface, hIoReq, pvIoReqAlloc, enmState);
1212 AssertLogRelMsgFailed(("This should not be hit because I/O requests should not be suspended\n"));
1213}
1214
1215
1216/**
1217 * @interface_method_impl{PDMIMEDIAEXPORT,pfnMediumEjected}
1218 */
1219static DECLCALLBACK(void) usbMsdLun0MediumEjected(PPDMIMEDIAEXPORT pInterface)
1220{
1221 RT_NOREF1(pInterface); /** @todo */
1222}
1223
1224
1225/**
1226 * @interface_method_impl{PDMIMEDIAPORT,pfnQueryDeviceLocation}
1227 */
1228static DECLCALLBACK(int) usbMsdLun0QueryDeviceLocation(PPDMIMEDIAPORT pInterface, const char **ppcszController,
1229 uint32_t *piInstance, uint32_t *piLUN)
1230{
1231 PUSBMSD pThis = RT_FROM_MEMBER(pInterface, USBMSD, Lun0.IMediaPort);
1232 PPDMUSBINS pUsbIns = pThis->pUsbIns;
1233
1234 AssertPtrReturn(ppcszController, VERR_INVALID_POINTER);
1235 AssertPtrReturn(piInstance, VERR_INVALID_POINTER);
1236 AssertPtrReturn(piLUN, VERR_INVALID_POINTER);
1237
1238 *ppcszController = pUsbIns->pReg->szName;
1239 *piInstance = pUsbIns->iInstance;
1240 *piLUN = 0;
1241
1242 return VINF_SUCCESS;
1243}
1244
1245
1246/**
1247 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1248 */
1249static DECLCALLBACK(void *) usbMsdLun0QueryInterface(PPDMIBASE pInterface, const char *pszIID)
1250{
1251 PUSBMSD pThis = RT_FROM_MEMBER(pInterface, USBMSD, Lun0.IBase);
1252 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->Lun0.IBase);
1253 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAPORT, &pThis->Lun0.IMediaPort);
1254 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAEXPORT, &pThis->Lun0.IMediaExPort);
1255 return NULL;
1256}
1257
1258
1259/**
1260 * Checks if all asynchronous I/O is finished.
1261 *
1262 * Used by usbMsdVMReset, usbMsdVMSuspend and usbMsdVMPowerOff.
1263 *
1264 * @returns true if quiesced, false if busy.
1265 * @param pUsbIns The USB device instance.
1266 */
1267static bool usbMsdAllAsyncIOIsFinished(PPDMUSBINS pUsbIns)
1268{
1269 PUSBMSD pThis = PDMINS_2_DATA(pUsbIns, PUSBMSD);
1270
1271 if ( RT_VALID_PTR(pThis->pReq)
1272 && pThis->pReq->enmState == USBMSDREQSTATE_EXECUTING)
1273 return false;
1274
1275 return true;
1276}
1277
1278/**
1279 * @callback_method_impl{FNPDMDEVASYNCNOTIFY,
1280 * Callback employed by usbMsdVMSuspend and usbMsdVMPowerOff.}
1281 */
1282static DECLCALLBACK(bool) usbMsdIsAsyncSuspendOrPowerOffDone(PPDMUSBINS pUsbIns)
1283{
1284 if (!usbMsdAllAsyncIOIsFinished(pUsbIns))
1285 return false;
1286
1287 PUSBMSD pThis = PDMINS_2_DATA(pUsbIns, PUSBMSD);
1288 ASMAtomicWriteBool(&pThis->fSignalIdle, false);
1289 return true;
1290}
1291
1292/**
1293 * Common worker for usbMsdVMSuspend and usbMsdVMPowerOff.
1294 */
1295static void usbMsdSuspendOrPowerOff(PPDMUSBINS pUsbIns)
1296{
1297 PUSBMSD pThis = PDMINS_2_DATA(pUsbIns, PUSBMSD);
1298
1299 ASMAtomicWriteBool(&pThis->fSignalIdle, true);
1300 if (!usbMsdAllAsyncIOIsFinished(pUsbIns))
1301 PDMUsbHlpSetAsyncNotification(pUsbIns, usbMsdIsAsyncSuspendOrPowerOffDone);
1302 else
1303 {
1304 ASMAtomicWriteBool(&pThis->fSignalIdle, false);
1305
1306 if (pThis->pReq)
1307 {
1308 usbMsdReqFree(pThis, pThis->pReq);
1309 pThis->pReq = NULL;
1310 }
1311 }
1312
1313 if (pThis->Lun0.pIMediaEx)
1314 pThis->Lun0.pIMediaEx->pfnNotifySuspend(pThis->Lun0.pIMediaEx);
1315}
1316
1317
1318/* -=-=-=-=- Saved State -=-=-=-=- */
1319
1320/**
1321 * @callback_method_impl{FNSSMUSBSAVEPREP}
1322 */
1323static DECLCALLBACK(int) usbMsdSavePrep(PPDMUSBINS pUsbIns, PSSMHANDLE pSSM)
1324{
1325 RT_NOREF(pSSM);
1326#ifdef VBOX_STRICT
1327 PUSBMSD pThis = PDMINS_2_DATA(pUsbIns, PUSBMSD);
1328 Assert(usbMsdAllAsyncIOIsFinished(pUsbIns));
1329 Assert(usbMsdQueueIsEmpty(&pThis->ToHostQueue));
1330 Assert(usbMsdQueueIsEmpty(&pThis->DoneQueue));
1331#else
1332 RT_NOREF(pUsbIns);
1333#endif
1334 return VINF_SUCCESS;
1335}
1336
1337/**
1338 * @callback_method_impl{FNSSMUSBLOADPREP}
1339 */
1340static DECLCALLBACK(int) usbMsdLoadPrep(PPDMUSBINS pUsbIns, PSSMHANDLE pSSM)
1341{
1342 RT_NOREF(pSSM);
1343#ifdef VBOX_STRICT
1344 PUSBMSD pThis = PDMINS_2_DATA(pUsbIns, PUSBMSD);
1345 Assert(usbMsdAllAsyncIOIsFinished(pUsbIns));
1346 Assert(usbMsdQueueIsEmpty(&pThis->ToHostQueue));
1347 Assert(usbMsdQueueIsEmpty(&pThis->DoneQueue));
1348#else
1349 RT_NOREF(pUsbIns);
1350#endif
1351 return VINF_SUCCESS;
1352}
1353
1354/**
1355 * @callback_method_impl{FNSSMUSBLIVEEXEC}
1356 */
1357static DECLCALLBACK(int) usbMsdLiveExec(PPDMUSBINS pUsbIns, PSSMHANDLE pSSM, uint32_t uPass)
1358{
1359 RT_NOREF(uPass);
1360 PUSBMSD pThis = PDMINS_2_DATA(pUsbIns, PUSBMSD);
1361 PCPDMUSBHLP pHlp = pUsbIns->pHlpR3;
1362
1363 /* config. */
1364 pHlp->pfnSSMPutBool(pSSM, pThis->Lun0.pIBase != NULL);
1365 return VINF_SSM_DONT_CALL_AGAIN;
1366}
1367
1368/**
1369 * @callback_method_impl{FNSSMUSBSAVEEXEC}
1370 */
1371static DECLCALLBACK(int) usbMsdSaveExec(PPDMUSBINS pUsbIns, PSSMHANDLE pSSM)
1372{
1373 PUSBMSD pThis = PDMINS_2_DATA(pUsbIns, PUSBMSD);
1374 PCPDMUSBHLP pHlp = pUsbIns->pHlpR3;
1375
1376 /* The config */
1377 int rc = usbMsdLiveExec(pUsbIns, pSSM, SSM_PASS_FINAL);
1378 AssertRCReturn(rc, rc);
1379
1380 pHlp->pfnSSMPutU8(pSSM, pThis->bConfigurationValue);
1381 pHlp->pfnSSMPutBool(pSSM, pThis->aEps[0].fHalted);
1382 pHlp->pfnSSMPutBool(pSSM, pThis->aEps[1].fHalted);
1383 pHlp->pfnSSMPutBool(pSSM, pThis->aEps[2].fHalted);
1384 pHlp->pfnSSMPutBool(pSSM, pThis->pReq != NULL);
1385
1386 if (pThis->pReq)
1387 {
1388 PUSBMSDREQ pReq = pThis->pReq;
1389
1390 pHlp->pfnSSMPutU32(pSSM, pReq->enmState);
1391 pHlp->pfnSSMPutU32(pSSM, pReq->cbBuf);
1392 if (pReq->cbBuf)
1393 {
1394 AssertPtr(pReq->pbBuf);
1395 pHlp->pfnSSMPutMem(pSSM, pReq->pbBuf, pReq->cbBuf);
1396 }
1397
1398 pHlp->pfnSSMPutU32(pSSM, pReq->offBuf);
1399 pHlp->pfnSSMPutMem(pSSM, &pReq->Cbw, sizeof(pReq->Cbw));
1400 pHlp->pfnSSMPutU8(pSSM, pReq->iScsiReqStatus);
1401 }
1402
1403 return pHlp->pfnSSMPutU32(pSSM, UINT32_MAX); /* sanity/terminator */
1404}
1405
1406/**
1407 * @callback_method_impl{FNSSMUSBLOADEXEC}
1408 */
1409static DECLCALLBACK(int) usbMsdLoadExec(PPDMUSBINS pUsbIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
1410{
1411 PUSBMSD pThis = PDMINS_2_DATA(pUsbIns, PUSBMSD);
1412 PCPDMUSBHLP pHlp = pUsbIns->pHlpR3;
1413
1414 if (uVersion > USB_MSD_SAVED_STATE_VERSION)
1415 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
1416
1417 /* Verify config. */
1418 bool fInUse;
1419 int rc = pHlp->pfnSSMGetBool(pSSM, &fInUse);
1420 AssertRCReturn(rc, rc);
1421 if (fInUse != (pThis->Lun0.pIBase != NULL))
1422 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS,
1423 N_("The %s VM is missing a USB mass storage device. Please make sure the source and target VMs have compatible storage configurations"),
1424 fInUse ? "target" : "source");
1425
1426 if (uPass == SSM_PASS_FINAL)
1427 {
1428 /* Restore data. */
1429 Assert(!pThis->pReq);
1430
1431 pHlp->pfnSSMGetU8(pSSM, &pThis->bConfigurationValue);
1432 pHlp->pfnSSMGetBool(pSSM, &pThis->aEps[0].fHalted);
1433 pHlp->pfnSSMGetBool(pSSM, &pThis->aEps[1].fHalted);
1434 pHlp->pfnSSMGetBool(pSSM, &pThis->aEps[2].fHalted);
1435 bool fReqAlloc = false;
1436 rc = pHlp->pfnSSMGetBool(pSSM, &fReqAlloc);
1437 AssertRCReturn(rc, rc);
1438 if (fReqAlloc)
1439 {
1440 PUSBMSDREQ pReq = usbMsdReqAlloc(pThis);
1441 AssertReturn(pReq, VERR_NO_MEMORY);
1442 pThis->pReq = pReq;
1443
1444 AssertCompile(sizeof(pReq->enmState) == sizeof(uint32_t));
1445 pHlp->pfnSSMGetU32(pSSM, (uint32_t *)&pReq->enmState);
1446
1447 uint32_t cbBuf = 0;
1448 rc = pHlp->pfnSSMGetU32(pSSM, &cbBuf);
1449 AssertRCReturn(rc, rc);
1450 if (cbBuf)
1451 {
1452 if (usbMsdReqEnsureBuffer(pThis, pReq, cbBuf))
1453 {
1454 AssertPtr(pReq->pbBuf);
1455 Assert(cbBuf == pReq->cbBuf);
1456 pHlp->pfnSSMGetMem(pSSM, pReq->pbBuf, pReq->cbBuf);
1457 }
1458 else
1459 return VERR_NO_MEMORY;
1460 }
1461
1462 pHlp->pfnSSMGetU32(pSSM, &pReq->offBuf);
1463 pHlp->pfnSSMGetMem(pSSM, &pReq->Cbw, sizeof(pReq->Cbw));
1464
1465 if (uVersion > USB_MSD_SAVED_STATE_VERSION_PRE_CLEANUP)
1466 rc = pHlp->pfnSSMGetU8(pSSM, &pReq->iScsiReqStatus);
1467 else
1468 {
1469 int32_t iScsiReqStatus;
1470
1471 /* Skip old fields which are unused now or can be determined from the CBW. */
1472 pHlp->pfnSSMSkip(pSSM, 4 * 4 + 64);
1473 rc = pHlp->pfnSSMGetS32(pSSM, &iScsiReqStatus);
1474 pReq->iScsiReqStatus = (uint8_t)iScsiReqStatus;
1475 }
1476 AssertRCReturn(rc, rc);
1477 }
1478
1479 uint32_t u32;
1480 rc = pHlp->pfnSSMGetU32(pSSM, &u32);
1481 AssertRCReturn(rc, rc);
1482 AssertMsgReturn(u32 == UINT32_MAX, ("%#x\n", u32), VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
1483 }
1484
1485 return VINF_SUCCESS;
1486}
1487
1488
1489/**
1490 * @interface_method_impl{PDMUSBREG,pfnUrbReap}
1491 */
1492static DECLCALLBACK(PVUSBURB) usbMsdUrbReap(PPDMUSBINS pUsbIns, RTMSINTERVAL cMillies)
1493{
1494 PUSBMSD pThis = PDMINS_2_DATA(pUsbIns, PUSBMSD);
1495 LogFlow(("usbMsdUrbReap/#%u: cMillies=%u\n", pUsbIns->iInstance, cMillies));
1496
1497 RTCritSectEnter(&pThis->CritSect);
1498
1499 PVUSBURB pUrb = usbMsdQueueRemoveHead(&pThis->DoneQueue);
1500 if (!pUrb && cMillies)
1501 {
1502 /* Wait */
1503 pThis->fHaveDoneQueueWaiter = true;
1504 RTCritSectLeave(&pThis->CritSect);
1505
1506 RTSemEventWait(pThis->hEvtDoneQueue, cMillies);
1507
1508 RTCritSectEnter(&pThis->CritSect);
1509 pThis->fHaveDoneQueueWaiter = false;
1510
1511 pUrb = usbMsdQueueRemoveHead(&pThis->DoneQueue);
1512 }
1513
1514 RTCritSectLeave(&pThis->CritSect);
1515
1516 if (pUrb)
1517 Log(("usbMsdUrbReap/#%u: pUrb=%p:%s\n", pUsbIns->iInstance, pUrb, pUrb->pszDesc));
1518 return pUrb;
1519}
1520
1521
1522/**
1523 * @interface_method_impl{PDMUSBREG,pfnWakeup}
1524 */
1525static DECLCALLBACK(int) usbMsdWakeup(PPDMUSBINS pUsbIns)
1526{
1527 PUSBMSD pThis = PDMINS_2_DATA(pUsbIns, PUSBMSD);
1528 LogFlow(("usbMsdUrbReap/#%u:\n", pUsbIns->iInstance));
1529
1530 return RTSemEventSignal(pThis->hEvtDoneQueue);
1531}
1532
1533
1534/**
1535 * @interface_method_impl{PDMUSBREG,pfnUrbCancel}
1536 */
1537static DECLCALLBACK(int) usbMsdUrbCancel(PPDMUSBINS pUsbIns, PVUSBURB pUrb)
1538{
1539 PUSBMSD pThis = PDMINS_2_DATA(pUsbIns, PUSBMSD);
1540 LogFlow(("usbMsdUrbCancel/#%u: pUrb=%p:%s\n", pUsbIns->iInstance, pUrb, pUrb->pszDesc));
1541 RTCritSectEnter(&pThis->CritSect);
1542
1543 /*
1544 * Remove the URB from the to-host queue and move it onto the done queue.
1545 */
1546 if (usbMsdQueueRemove(&pThis->ToHostQueue, pUrb))
1547 usbMsdLinkDone(pThis, pUrb);
1548
1549 RTCritSectLeave(&pThis->CritSect);
1550 return VINF_SUCCESS;
1551}
1552
1553
1554/**
1555 * Wrapper around PDMISCSICONNECTOR::pfnSCSIRequestSend that deals with
1556 * SCSI_REQUEST_SENSE.
1557 *
1558 * @returns VBox status code.
1559 * @param pThis The MSD instance data.
1560 * @param pReq The MSD request.
1561 * @param pszCaller Where we're called from.
1562 */
1563static int usbMsdSubmitScsiCommand(PUSBMSD pThis, PUSBMSDREQ pReq, const char *pszCaller)
1564{
1565 RT_NOREF(pszCaller);
1566 Log(("%s: Entering EXECUTING (dCBWTag=%#x).\n", pszCaller, pReq->Cbw.dCBWTag));
1567 Assert(pReq == pThis->pReq);
1568 pReq->enmState = USBMSDREQSTATE_EXECUTING;
1569
1570 PDMMEDIAEXIOREQSCSITXDIR enmTxDir = pReq->Cbw.dCBWDataTransferLength == 0
1571 ? PDMMEDIAEXIOREQSCSITXDIR_NONE
1572 : (pReq->Cbw.bmCBWFlags & USBCBW_DIR_MASK) == USBCBW_DIR_OUT
1573 ? PDMMEDIAEXIOREQSCSITXDIR_TO_DEVICE
1574 : PDMMEDIAEXIOREQSCSITXDIR_FROM_DEVICE;
1575
1576 return pThis->Lun0.pIMediaEx->pfnIoReqSendScsiCmd(pThis->Lun0.pIMediaEx, pReq->hIoReq, pReq->Cbw.bCBWLun,
1577 &pReq->Cbw.CBWCB[0], pReq->Cbw.bCBWCBLength, enmTxDir, NULL,
1578 pReq->Cbw.dCBWDataTransferLength, NULL, 0, NULL,
1579 &pReq->iScsiReqStatus, 20 * RT_MS_1SEC);
1580}
1581
1582
1583/**
1584 * Handle requests sent to the outbound (to device) bulk pipe.
1585 */
1586static int usbMsdHandleBulkHostToDev(PUSBMSD pThis, PUSBMSDEP pEp, PVUSBURB pUrb)
1587{
1588 /*
1589 * Stall the request if the pipe is halted.
1590 */
1591 if (RT_UNLIKELY(pEp->fHalted))
1592 return usbMsdCompleteStall(pThis, NULL, pUrb, "Halted pipe");
1593
1594 /*
1595 * Deal with the URB according to the current state.
1596 */
1597 PUSBMSDREQ pReq = pThis->pReq;
1598 USBMSDREQSTATE enmState = pReq ? pReq->enmState : USBMSDREQSTATE_READY;
1599 switch (enmState)
1600 {
1601 case USBMSDREQSTATE_STATUS:
1602 LogFlow(("usbMsdHandleBulkHostToDev: Skipping pending status.\n"));
1603 pReq->enmState = USBMSDREQSTATE_READY;
1604 RT_FALL_THRU();
1605
1606 /*
1607 * We're ready to receive a command. Start off by validating the
1608 * incoming request.
1609 */
1610 case USBMSDREQSTATE_READY:
1611 {
1612 PCUSBCBW pCbw = (PUSBCBW)&pUrb->abData[0];
1613 if (pUrb->cbData < RT_UOFFSETOF(USBCBW, CBWCB[1]))
1614 {
1615 Log(("usbMsd: Bad CBW: cbData=%#x < min=%#x\n", pUrb->cbData, RT_UOFFSETOF(USBCBW, CBWCB[1]) ));
1616 return usbMsdCompleteStall(pThis, NULL, pUrb, "BAD CBW");
1617 }
1618 if (pCbw->dCBWSignature != USBCBW_SIGNATURE)
1619 {
1620 Log(("usbMsd: CBW: Invalid dCBWSignature value: %#x\n", pCbw->dCBWSignature));
1621 return usbMsdCompleteStall(pThis, NULL, pUrb, "Bad CBW");
1622 }
1623 Log(("usbMsd: CBW: dCBWTag=%#x dCBWDataTransferLength=%#x bmCBWFlags=%#x bCBWLun=%#x bCBWCBLength=%#x cbData=%#x fShortNotOk=%RTbool\n",
1624 pCbw->dCBWTag, pCbw->dCBWDataTransferLength, pCbw->bmCBWFlags, pCbw->bCBWLun, pCbw->bCBWCBLength, pUrb->cbData, pUrb->fShortNotOk));
1625 if (pCbw->bmCBWFlags & ~USBCBW_DIR_MASK)
1626 {
1627 Log(("usbMsd: CBW: Bad bmCBWFlags value: %#x\n", pCbw->bmCBWFlags));
1628 return usbMsdCompleteStall(pThis, NULL, pUrb, "Bad CBW");
1629
1630 }
1631 if (pCbw->bCBWLun != 0)
1632 {
1633 Log(("usbMsd: CBW: Bad bCBWLun value: %#x\n", pCbw->bCBWLun));
1634 return usbMsdCompleteStall(pThis, NULL, pUrb, "Bad CBW");
1635 }
1636 if ((pCbw->bCBWCBLength == 0) || (pCbw->bCBWCBLength > sizeof(pCbw->CBWCB)))
1637 {
1638 Log(("usbMsd: CBW: Bad bCBWCBLength value: %#x\n", pCbw->bCBWCBLength));
1639 return usbMsdCompleteStall(pThis, NULL, pUrb, "Bad CBW");
1640 }
1641 if (pUrb->cbData < RT_UOFFSETOF_DYN(USBCBW, CBWCB[pCbw->bCBWCBLength]))
1642 {
1643 Log(("usbMsd: CBW: Mismatching cbData and bCBWCBLength values: %#x vs. %#x (%#x)\n",
1644 pUrb->cbData, RT_UOFFSETOF_DYN(USBCBW, CBWCB[pCbw->bCBWCBLength]), pCbw->bCBWCBLength));
1645 return usbMsdCompleteStall(pThis, NULL, pUrb, "Bad CBW");
1646 }
1647 if (pCbw->dCBWDataTransferLength > _1M)
1648 {
1649 Log(("usbMsd: CBW: dCBWDataTransferLength is too large: %#x (%u)\n",
1650 pCbw->dCBWDataTransferLength, pCbw->dCBWDataTransferLength));
1651 return usbMsdCompleteStall(pThis, NULL, pUrb, "Too big transfer");
1652 }
1653
1654 /*
1655 * Make sure we've got a request and a sufficient buffer space.
1656 *
1657 * Note! This will make sure the buffer is ZERO as well, thus
1658 * saving us the trouble of clearing the output buffer on
1659 * failure later.
1660 */
1661 if (!pReq)
1662 {
1663 pReq = usbMsdReqAlloc(pThis);
1664 if (!pReq)
1665 return usbMsdCompleteStall(pThis, NULL, pUrb, "Request allocation failure");
1666 pThis->pReq = pReq;
1667 }
1668 if (!usbMsdReqEnsureBuffer(pThis, pReq, pCbw->dCBWDataTransferLength))
1669 return usbMsdCompleteStall(pThis, NULL, pUrb, "Buffer allocation failure");
1670
1671 /*
1672 * Prepare the request. Kick it off right away if possible.
1673 */
1674 usbMsdReqPrepare(pReq, pCbw);
1675
1676 if ( pReq->Cbw.dCBWDataTransferLength == 0
1677 || (pReq->Cbw.bmCBWFlags & USBCBW_DIR_MASK) == USBCBW_DIR_IN)
1678 {
1679 int rc = usbMsdSubmitScsiCommand(pThis, pReq, "usbMsdHandleBulkHostToDev");
1680 if (RT_SUCCESS(rc) && rc != VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS)
1681 usbMsdReqComplete(pThis, pReq, rc);
1682 else if (RT_FAILURE(rc))
1683 {
1684 Log(("usbMsd: Failed sending SCSI request to driver: %Rrc\n", rc));
1685 return usbMsdCompleteStall(pThis, NULL, pUrb, "SCSI Submit #1");
1686 }
1687 }
1688 else
1689 {
1690 Log(("usbMsdHandleBulkHostToDev: Entering DATA_FROM_HOST.\n"));
1691 pReq->enmState = USBMSDREQSTATE_DATA_FROM_HOST;
1692 }
1693
1694 return usbMsdCompleteOk(pThis, pUrb, pUrb->cbData);
1695 }
1696
1697 /*
1698 * Stuff the data into the buffer.
1699 */
1700 case USBMSDREQSTATE_DATA_FROM_HOST:
1701 {
1702 uint32_t cbData = pUrb->cbData;
1703 uint32_t cbLeft = pReq->Cbw.dCBWDataTransferLength - pReq->offBuf;
1704 if (cbData > cbLeft)
1705 {
1706 Log(("usbMsd: Too much data: cbData=%#x offBuf=%#x dCBWDataTransferLength=%#x cbLeft=%#x\n",
1707 cbData, pReq->offBuf, pReq->Cbw.dCBWDataTransferLength, cbLeft));
1708 return usbMsdCompleteStall(pThis, NULL, pUrb, "Too much data");
1709 }
1710 memcpy(&pReq->pbBuf[pReq->offBuf], &pUrb->abData[0], cbData);
1711 pReq->offBuf += cbData;
1712
1713 if (pReq->offBuf == pReq->Cbw.dCBWDataTransferLength)
1714 {
1715 int rc = usbMsdSubmitScsiCommand(pThis, pReq, "usbMsdHandleBulkHostToDev");
1716 if (RT_SUCCESS(rc) && rc != VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS)
1717 usbMsdReqComplete(pThis, pReq, rc);
1718 else if (RT_FAILURE(rc))
1719 {
1720 Log(("usbMsd: Failed sending SCSI request to driver: %Rrc\n", rc));
1721 return usbMsdCompleteStall(pThis, NULL, pUrb, "SCSI Submit #2");
1722 }
1723 }
1724 return usbMsdCompleteOk(pThis, pUrb, cbData);
1725 }
1726
1727 /*
1728 * Bad state, stall.
1729 */
1730 case USBMSDREQSTATE_DATA_TO_HOST:
1731 return usbMsdCompleteStall(pThis, NULL, pUrb, "Bad state H2D: DATA_TO_HOST");
1732
1733 case USBMSDREQSTATE_EXECUTING:
1734 return usbMsdCompleteStall(pThis, NULL, pUrb, "Bad state H2D: EXECUTING");
1735
1736 default:
1737 AssertMsgFailed(("enmState=%d\n", enmState));
1738 return usbMsdCompleteStall(pThis, NULL, pUrb, "Bad state (H2D)");
1739 }
1740}
1741
1742
1743/**
1744 * Handle requests sent to the inbound (to host) bulk pipe.
1745 */
1746static int usbMsdHandleBulkDevToHost(PUSBMSD pThis, PUSBMSDEP pEp, PVUSBURB pUrb)
1747{
1748 /*
1749 * Stall the request if the pipe is halted OR if there is no
1750 * pending request yet.
1751 */
1752 PUSBMSDREQ pReq = pThis->pReq;
1753 if (RT_UNLIKELY(pEp->fHalted || !pReq))
1754 return usbMsdCompleteStall(pThis, NULL, pUrb, pEp->fHalted ? "Halted pipe" : "No request");
1755
1756 /*
1757 * Deal with the URB according to the state.
1758 */
1759 switch (pReq->enmState)
1760 {
1761 /*
1762 * We've data left to transfer to the host.
1763 */
1764 case USBMSDREQSTATE_DATA_TO_HOST:
1765 {
1766 uint32_t cbData = pUrb->cbData;
1767 uint32_t cbCopy = pReq->Cbw.dCBWDataTransferLength - pReq->offBuf;
1768 if (cbData <= cbCopy)
1769 cbCopy = cbData;
1770 else if (pUrb->fShortNotOk)
1771 {
1772 Log(("usbMsd: Requested more data that we've got; cbData=%#x offBuf=%#x dCBWDataTransferLength=%#x cbLeft=%#x\n",
1773 cbData, pReq->offBuf, pReq->Cbw.dCBWDataTransferLength, cbCopy));
1774 return usbMsdCompleteStall(pThis, NULL, pUrb, "Data underrun");
1775 }
1776 memcpy(&pUrb->abData[0], &pReq->pbBuf[pReq->offBuf], cbCopy);
1777 pReq->offBuf += cbCopy;
1778
1779 if (pReq->offBuf == pReq->Cbw.dCBWDataTransferLength)
1780 {
1781 Log(("usbMsdHandleBulkDevToHost: Entering STATUS\n"));
1782 pReq->enmState = USBMSDREQSTATE_STATUS;
1783 }
1784 return usbMsdCompleteOk(pThis, pUrb, cbCopy);
1785 }
1786
1787 /*
1788 * Status transfer.
1789 */
1790 case USBMSDREQSTATE_STATUS:
1791 {
1792 if ((pUrb->cbData < sizeof(USBCSW)) || (pUrb->cbData > sizeof(USBCSW) && pUrb->fShortNotOk))
1793 {
1794 Log(("usbMsd: Unexpected status request size: %#x (expected %#x), fShortNotOK=%RTbool\n", pUrb->cbData, sizeof(USBCSW), pUrb->fShortNotOk));
1795 return usbMsdCompleteStall(pThis, NULL, pUrb, "Invalid CSW size");
1796 }
1797
1798 /* Enter a CSW into the URB data buffer. */
1799 PUSBCSW pCsw = (PUSBCSW)&pUrb->abData[0];
1800 pCsw->dCSWSignature = USBCSW_SIGNATURE;
1801 pCsw->dCSWTag = pReq->Cbw.dCBWTag;
1802 pCsw->bCSWStatus = pReq->iScsiReqStatus == SCSI_STATUS_OK
1803 ? USBCSW_STATUS_OK
1804 : pReq->iScsiReqStatus < 0xff
1805 ? USBCSW_STATUS_FAILED
1806 : USBCSW_STATUS_PHASE_ERROR;
1807 /** @todo the following is not always accurate; VSCSI needs
1808 * to implement residual counts properly! */
1809 if ((pReq->Cbw.bmCBWFlags & USBCBW_DIR_MASK) == USBCBW_DIR_OUT)
1810 pCsw->dCSWDataResidue = pCsw->bCSWStatus == USBCSW_STATUS_OK
1811 ? 0
1812 : pReq->Cbw.dCBWDataTransferLength;
1813 else
1814 pCsw->dCSWDataResidue = pCsw->bCSWStatus == USBCSW_STATUS_OK
1815 ? 0
1816 : pReq->Cbw.dCBWDataTransferLength;
1817 Log(("usbMsd: CSW: dCSWTag=%#x bCSWStatus=%d dCSWDataResidue=%#x\n",
1818 pCsw->dCSWTag, pCsw->bCSWStatus, pCsw->dCSWDataResidue));
1819
1820 Log(("usbMsdHandleBulkDevToHost: Entering READY\n"));
1821 pReq->enmState = USBMSDREQSTATE_READY;
1822 return usbMsdCompleteOk(pThis, pUrb, sizeof(*pCsw));
1823 }
1824
1825 /*
1826 * Status request before we've received all (or even any) data.
1827 * Linux 2.4.31 does this sometimes. The recommended behavior is to
1828 * to accept the current data amount and execute the request. (The
1829 * alternative behavior is to stall.)
1830 */
1831 case USBMSDREQSTATE_DATA_FROM_HOST:
1832 {
1833 if (pUrb->cbData != sizeof(USBCSW))
1834 {
1835 Log(("usbMsdHandleBulkDevToHost: DATA_FROM_HOST; cbData=%#x -> stall\n", pUrb->cbData));
1836 return usbMsdCompleteStall(pThis, NULL, pUrb, "Invalid CSW size");
1837 }
1838
1839 int rc = usbMsdSubmitScsiCommand(pThis, pReq, "usbMsdHandleBulkDevToHost");
1840 if (RT_SUCCESS(rc) && rc != VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS)
1841 usbMsdReqComplete(pThis, pReq, rc);
1842 else if (RT_FAILURE(rc))
1843 {
1844 Log(("usbMsd: Failed sending SCSI request to driver: %Rrc\n", rc));
1845 return usbMsdCompleteStall(pThis, NULL, pUrb, "SCSI Submit #3");
1846 }
1847 }
1848 RT_FALL_THRU();
1849
1850 /*
1851 * The SCSI command is still pending, queue the URB awaiting its
1852 * completion.
1853 */
1854 case USBMSDREQSTATE_EXECUTING:
1855 usbMsdQueueAddTail(&pThis->ToHostQueue, pUrb);
1856 LogFlow(("usbMsdHandleBulkDevToHost: Added %p:%s to the to-host queue\n", pUrb, pUrb->pszDesc));
1857 return VINF_SUCCESS;
1858
1859 /*
1860 * Bad states, stall.
1861 */
1862 case USBMSDREQSTATE_READY:
1863 Log(("usbMsdHandleBulkDevToHost: enmState=READ(%d) (cbData=%#x)\n", pReq->enmState, pUrb->cbData));
1864 return usbMsdCompleteStall(pThis, NULL, pUrb, "Bad state D2H: READY");
1865
1866 default:
1867 Log(("usbMsdHandleBulkDevToHost: enmState=%d cbData=%#x\n", pReq->enmState, pUrb->cbData));
1868 return usbMsdCompleteStall(pThis, NULL, pUrb, "Really bad state (D2H)!");
1869 }
1870}
1871
1872
1873/**
1874 * Handles request send to the default control pipe.
1875 */
1876static int usbMsdHandleDefaultPipe(PUSBMSD pThis, PUSBMSDEP pEp, PVUSBURB pUrb)
1877{
1878 PVUSBSETUP pSetup = (PVUSBSETUP)&pUrb->abData[0];
1879 AssertReturn(pUrb->cbData >= sizeof(*pSetup), VERR_VUSB_FAILED_TO_QUEUE_URB);
1880
1881 if ((pSetup->bmRequestType & VUSB_REQ_MASK) == VUSB_REQ_STANDARD)
1882 {
1883 switch (pSetup->bRequest)
1884 {
1885 case VUSB_REQ_GET_DESCRIPTOR:
1886 {
1887 if (pSetup->bmRequestType != (VUSB_TO_DEVICE | VUSB_REQ_STANDARD | VUSB_DIR_TO_HOST))
1888 {
1889 Log(("usbMsd: Bad GET_DESCRIPTOR req: bmRequestType=%#x\n", pSetup->bmRequestType));
1890 return usbMsdCompleteStall(pThis, pEp, pUrb, "Bad GET_DESCRIPTOR");
1891 }
1892
1893 switch (pSetup->wValue >> 8)
1894 {
1895 uint32_t cbCopy;
1896
1897 case VUSB_DT_STRING:
1898 Log(("usbMsd: GET_DESCRIPTOR DT_STRING wValue=%#x wIndex=%#x\n", pSetup->wValue, pSetup->wIndex));
1899 break;
1900 case VUSB_DT_DEVICE_QUALIFIER:
1901 Log(("usbMsd: GET_DESCRIPTOR DT_DEVICE_QUALIFIER wValue=%#x wIndex=%#x\n", pSetup->wValue, pSetup->wIndex));
1902 /* Returned data is written after the setup message. */
1903 cbCopy = pUrb->cbData - sizeof(*pSetup);
1904 cbCopy = RT_MIN(cbCopy, sizeof(g_UsbMsdDeviceQualifier));
1905 memcpy(&pUrb->abData[sizeof(*pSetup)], &g_UsbMsdDeviceQualifier, cbCopy);
1906 return usbMsdCompleteOk(pThis, pUrb, cbCopy + sizeof(*pSetup));
1907 case VUSB_DT_BOS:
1908 Log(("usbMsd: GET_DESCRIPTOR DT_BOS wValue=%#x wIndex=%#x\n", pSetup->wValue, pSetup->wIndex));
1909 /* Returned data is written after the setup message. */
1910 cbCopy = pUrb->cbData - sizeof(*pSetup);
1911 cbCopy = RT_MIN(cbCopy, sizeof(g_UsbMsdBOS));
1912 memcpy(&pUrb->abData[sizeof(*pSetup)], &g_UsbMsdBOS, cbCopy);
1913 return usbMsdCompleteOk(pThis, pUrb, cbCopy + sizeof(*pSetup));
1914 default:
1915 Log(("usbMsd: GET_DESCRIPTOR, huh? wValue=%#x wIndex=%#x\n", pSetup->wValue, pSetup->wIndex));
1916 break;
1917 }
1918 break;
1919 }
1920
1921 case VUSB_REQ_CLEAR_FEATURE:
1922 break;
1923 }
1924
1925 /** @todo implement this. */
1926 Log(("usbMsd: Implement standard request: bmRequestType=%#x bRequest=%#x wValue=%#x wIndex=%#x wLength=%#x\n",
1927 pSetup->bmRequestType, pSetup->bRequest, pSetup->wValue, pSetup->wIndex, pSetup->wLength));
1928
1929 usbMsdCompleteStall(pThis, pEp, pUrb, "TODO: standard request stuff");
1930 }
1931 /* 3.1 Bulk-Only Mass Storage Reset */
1932 else if ( pSetup->bmRequestType == (VUSB_REQ_CLASS | VUSB_TO_INTERFACE)
1933 && pSetup->bRequest == 0xff
1934 && !pSetup->wValue
1935 && !pSetup->wLength
1936 && pSetup->wIndex == 0)
1937 {
1938 Log(("usbMsdHandleDefaultPipe: Bulk-Only Mass Storage Reset\n"));
1939 return usbMsdResetWorker(pThis, pUrb, false /*fSetConfig*/);
1940 }
1941 /* 3.2 Get Max LUN, may stall if we like (but we don't). */
1942 else if ( pSetup->bmRequestType == (VUSB_REQ_CLASS | VUSB_TO_INTERFACE | VUSB_DIR_TO_HOST)
1943 && pSetup->bRequest == 0xfe
1944 && !pSetup->wValue
1945 && pSetup->wLength == 1
1946 && pSetup->wIndex == 0)
1947 {
1948 *(uint8_t *)(pSetup + 1) = 0; /* max lun is 0 */
1949 usbMsdCompleteOk(pThis, pUrb, 1);
1950 }
1951 else
1952 {
1953 Log(("usbMsd: Unknown control msg: bmRequestType=%#x bRequest=%#x wValue=%#x wIndex=%#x wLength=%#x\n",
1954 pSetup->bmRequestType, pSetup->bRequest, pSetup->wValue, pSetup->wIndex, pSetup->wLength));
1955 return usbMsdCompleteStall(pThis, pEp, pUrb, "Unknown control msg");
1956 }
1957
1958 return VINF_SUCCESS;
1959}
1960
1961
1962/**
1963 * @interface_method_impl{PDMUSBREG,pfnUrbQueue}
1964 */
1965static DECLCALLBACK(int) usbMsdQueue(PPDMUSBINS pUsbIns, PVUSBURB pUrb)
1966{
1967 PUSBMSD pThis = PDMINS_2_DATA(pUsbIns, PUSBMSD);
1968 LogFlow(("usbMsdQueue/#%u: pUrb=%p:%s EndPt=%#x\n", pUsbIns->iInstance, pUrb, pUrb->pszDesc, pUrb->EndPt));
1969 RTCritSectEnter(&pThis->CritSect);
1970
1971 /*
1972 * Parse on a per end-point basis.
1973 */
1974 int rc;
1975 switch (pUrb->EndPt)
1976 {
1977 case 0:
1978 rc = usbMsdHandleDefaultPipe(pThis, &pThis->aEps[0], pUrb);
1979 break;
1980
1981 case 0x81:
1982 AssertFailed();
1983 RT_FALL_THRU();
1984 case 0x01:
1985 rc = usbMsdHandleBulkDevToHost(pThis, &pThis->aEps[1], pUrb);
1986 break;
1987
1988 case 0x02:
1989 rc = usbMsdHandleBulkHostToDev(pThis, &pThis->aEps[2], pUrb);
1990 break;
1991
1992 default:
1993 AssertMsgFailed(("EndPt=%d\n", pUrb->EndPt));
1994 rc = VERR_VUSB_FAILED_TO_QUEUE_URB;
1995 break;
1996 }
1997
1998 RTCritSectLeave(&pThis->CritSect);
1999 return rc;
2000}
2001
2002
2003/**
2004 * @interface_method_impl{PDMUSBREG,pfnUsbClearHaltedEndpoint}
2005 */
2006static DECLCALLBACK(int) usbMsdUsbClearHaltedEndpoint(PPDMUSBINS pUsbIns, unsigned uEndpoint)
2007{
2008 PUSBMSD pThis = PDMINS_2_DATA(pUsbIns, PUSBMSD);
2009 LogFlow(("usbMsdUsbClearHaltedEndpoint/#%u: uEndpoint=%#x\n", pUsbIns->iInstance, uEndpoint));
2010
2011 if ((uEndpoint & ~0x80) < RT_ELEMENTS(pThis->aEps))
2012 {
2013 RTCritSectEnter(&pThis->CritSect);
2014 pThis->aEps[(uEndpoint & ~0x80)].fHalted = false;
2015 RTCritSectLeave(&pThis->CritSect);
2016 }
2017
2018 return VINF_SUCCESS;
2019}
2020
2021
2022/**
2023 * @interface_method_impl{PDMUSBREG,pfnUsbSetInterface}
2024 */
2025static DECLCALLBACK(int) usbMsdUsbSetInterface(PPDMUSBINS pUsbIns, uint8_t bInterfaceNumber, uint8_t bAlternateSetting)
2026{
2027 RT_NOREF(pUsbIns, bInterfaceNumber, bAlternateSetting);
2028 LogFlow(("usbMsdUsbSetInterface/#%u: bInterfaceNumber=%u bAlternateSetting=%u\n", pUsbIns->iInstance, bInterfaceNumber, bAlternateSetting));
2029 Assert(bAlternateSetting == 0);
2030 return VINF_SUCCESS;
2031}
2032
2033
2034/**
2035 * @interface_method_impl{PDMUSBREG,pfnUsbSetConfiguration}
2036 */
2037static DECLCALLBACK(int) usbMsdUsbSetConfiguration(PPDMUSBINS pUsbIns, uint8_t bConfigurationValue,
2038 const void *pvOldCfgDesc, const void *pvOldIfState, const void *pvNewCfgDesc)
2039{
2040 RT_NOREF(pvOldCfgDesc, pvOldIfState, pvNewCfgDesc);
2041 PUSBMSD pThis = PDMINS_2_DATA(pUsbIns, PUSBMSD);
2042 LogFlow(("usbMsdUsbSetConfiguration/#%u: bConfigurationValue=%u\n", pUsbIns->iInstance, bConfigurationValue));
2043 Assert(bConfigurationValue == 1);
2044 RTCritSectEnter(&pThis->CritSect);
2045
2046 /*
2047 * If the same config is applied more than once, it's a kind of reset.
2048 */
2049 if (pThis->bConfigurationValue == bConfigurationValue)
2050 usbMsdResetWorker(pThis, NULL, true /*fSetConfig*/); /** @todo figure out the exact difference */
2051 pThis->bConfigurationValue = bConfigurationValue;
2052
2053 RTCritSectLeave(&pThis->CritSect);
2054 return VINF_SUCCESS;
2055}
2056
2057
2058/**
2059 * @interface_method_impl{PDMUSBREG,pfnUsbGetDescriptorCache}
2060 */
2061static DECLCALLBACK(PCPDMUSBDESCCACHE) usbMsdUsbGetDescriptorCache(PPDMUSBINS pUsbIns)
2062{
2063 PUSBMSD pThis = PDMINS_2_DATA(pUsbIns, PUSBMSD);
2064 LogFlow(("usbMsdUsbGetDescriptorCache/#%u:\n", pUsbIns->iInstance));
2065 if (pThis->pUsbIns->enmSpeed == VUSB_SPEED_SUPER)
2066 return pThis->fIsCdrom ? &g_UsbCdDescCacheSS : &g_UsbMsdDescCacheSS;
2067 else if (pThis->pUsbIns->enmSpeed == VUSB_SPEED_HIGH)
2068 return pThis->fIsCdrom ? &g_UsbCdDescCacheHS : &g_UsbMsdDescCacheHS;
2069 else
2070 return pThis->fIsCdrom ? &g_UsbCdDescCacheFS : &g_UsbMsdDescCacheFS;
2071}
2072
2073
2074/**
2075 * @interface_method_impl{PDMUSBREG,pfnUsbReset}
2076 */
2077static DECLCALLBACK(int) usbMsdUsbReset(PPDMUSBINS pUsbIns, bool fResetOnLinux)
2078{
2079 RT_NOREF(fResetOnLinux);
2080 PUSBMSD pThis = PDMINS_2_DATA(pUsbIns, PUSBMSD);
2081 LogFlow(("usbMsdUsbReset/#%u:\n", pUsbIns->iInstance));
2082 RTCritSectEnter(&pThis->CritSect);
2083
2084 int rc = usbMsdResetWorker(pThis, NULL, false /*fSetConfig*/);
2085
2086 RTCritSectLeave(&pThis->CritSect);
2087 return rc;
2088}
2089
2090
2091/**
2092 * @interface_method_impl{PDMUSBREG,pfnVMSuspend}
2093 */
2094static DECLCALLBACK(void) usbMsdVMSuspend(PPDMUSBINS pUsbIns)
2095{
2096 LogFlow(("usbMsdVMSuspend/#%u:\n", pUsbIns->iInstance));
2097 usbMsdSuspendOrPowerOff(pUsbIns);
2098}
2099
2100
2101/**
2102 * @interface_method_impl{PDMUSBREG,pfnVMSuspend}
2103 */
2104static DECLCALLBACK(void) usbMsdVMPowerOff(PPDMUSBINS pUsbIns)
2105{
2106 LogFlow(("usbMsdVMPowerOff/#%u:\n", pUsbIns->iInstance));
2107 usbMsdSuspendOrPowerOff(pUsbIns);
2108}
2109
2110
2111/**
2112 * @interface_method_impl{PDMUSBREG,pfnDriverAttach}
2113 */
2114static DECLCALLBACK(int) usbMsdDriverAttach(PPDMUSBINS pUsbIns, unsigned iLUN, uint32_t fFlags)
2115{
2116 RT_NOREF(fFlags);
2117 PUSBMSD pThis = PDMINS_2_DATA(pUsbIns, PUSBMSD);
2118
2119 LogFlow(("usbMsdDriverAttach/#%u:\n", pUsbIns->iInstance));
2120
2121 AssertMsg(iLUN == 0, ("UsbMsd: No other LUN than 0 is supported\n"));
2122 AssertMsg(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
2123 ("UsbMsd: Device does not support hotplugging\n"));
2124
2125 /* the usual paranoia */
2126 AssertRelease(!pThis->Lun0.pIBase);
2127 AssertRelease(!pThis->Lun0.pIMedia);
2128 AssertRelease(!pThis->Lun0.pIMediaEx);
2129
2130 /*
2131 * Try attach the block device and get the interfaces,
2132 * required as well as optional.
2133 */
2134 int rc = PDMUsbHlpDriverAttach(pUsbIns, iLUN, &pThis->Lun0.IBase, &pThis->Lun0.pIBase, NULL);
2135 if (RT_SUCCESS(rc))
2136 {
2137 /* Get media and extended media interface. */
2138 pThis->Lun0.pIMedia = PDMIBASE_QUERY_INTERFACE(pThis->Lun0.pIBase, PDMIMEDIA);
2139 AssertMsgReturn(pThis->Lun0.pIMedia, ("Missing media interface below\n"), VERR_PDM_MISSING_INTERFACE);
2140 pThis->Lun0.pIMediaEx = PDMIBASE_QUERY_INTERFACE(pThis->Lun0.pIBase, PDMIMEDIAEX);
2141 AssertMsgReturn(pThis->Lun0.pIMediaEx, ("Missing extended media interface below\n"), VERR_PDM_MISSING_INTERFACE);
2142
2143 rc = pThis->Lun0.pIMediaEx->pfnIoReqAllocSizeSet(pThis->Lun0.pIMediaEx, sizeof(USBMSDREQ));
2144 AssertMsgRCReturn(rc, ("MSD failed to set I/O request size!\n"), VERR_PDM_MISSING_INTERFACE);
2145 }
2146 else
2147 AssertMsgFailed(("Failed to attach LUN#%d. rc=%Rrc\n", iLUN, rc));
2148
2149 if (RT_FAILURE(rc))
2150 {
2151 pThis->Lun0.pIBase = NULL;
2152 pThis->Lun0.pIMedia = NULL;
2153 pThis->Lun0.pIMediaEx = NULL;
2154 }
2155
2156 pThis->fIsCdrom = false;
2157 PDMMEDIATYPE enmType = pThis->Lun0.pIMedia->pfnGetType(pThis->Lun0.pIMedia);
2158 /* Anything else will be reported as a hard disk. */
2159 if (enmType == PDMMEDIATYPE_CDROM || enmType == PDMMEDIATYPE_DVD)
2160 pThis->fIsCdrom = true;
2161
2162 return rc;
2163}
2164
2165
2166/**
2167 * @interface_method_impl{PDMUSBREG,pfnDriverDetach}
2168 */
2169static DECLCALLBACK(void) usbMsdDriverDetach(PPDMUSBINS pUsbIns, unsigned iLUN, uint32_t fFlags)
2170{
2171 RT_NOREF(iLUN, fFlags);
2172 PUSBMSD pThis = PDMINS_2_DATA(pUsbIns, PUSBMSD);
2173
2174 LogFlow(("usbMsdDriverDetach/#%u:\n", pUsbIns->iInstance));
2175
2176 AssertMsg(iLUN == 0, ("UsbMsd: No other LUN than 0 is supported\n"));
2177 AssertMsg(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
2178 ("UsbMsd: Device does not support hotplugging\n"));
2179
2180 if (pThis->pReq)
2181 {
2182 usbMsdReqFree(pThis, pThis->pReq);
2183 pThis->pReq = NULL;
2184 }
2185
2186 /*
2187 * Zero some important members.
2188 */
2189 pThis->Lun0.pIBase = NULL;
2190 pThis->Lun0.pIMedia = NULL;
2191 pThis->Lun0.pIMediaEx = NULL;
2192}
2193
2194
2195/**
2196 * @callback_method_impl{FNPDMDEVASYNCNOTIFY,
2197 * Callback employed by usbMsdVMReset.}
2198 */
2199static DECLCALLBACK(bool) usbMsdIsAsyncResetDone(PPDMUSBINS pUsbIns)
2200{
2201 PUSBMSD pThis = PDMINS_2_DATA(pUsbIns, PUSBMSD);
2202
2203 if (!usbMsdAllAsyncIOIsFinished(pUsbIns))
2204 return false;
2205 ASMAtomicWriteBool(&pThis->fSignalIdle, false);
2206
2207 int rc = usbMsdResetWorker(pThis, NULL, false /*fSetConfig*/);
2208 AssertRC(rc);
2209 return true;
2210}
2211
2212/**
2213 * @interface_method_impl{PDMUSBREG,pfnVMReset}
2214 */
2215static DECLCALLBACK(void) usbMsdVMReset(PPDMUSBINS pUsbIns)
2216{
2217 PUSBMSD pThis = PDMINS_2_DATA(pUsbIns, PUSBMSD);
2218
2219 ASMAtomicWriteBool(&pThis->fSignalIdle, true);
2220 if (!usbMsdAllAsyncIOIsFinished(pUsbIns))
2221 PDMUsbHlpSetAsyncNotification(pUsbIns, usbMsdIsAsyncResetDone);
2222 else
2223 {
2224 ASMAtomicWriteBool(&pThis->fSignalIdle, false);
2225 int rc = usbMsdResetWorker(pThis, NULL, false /*fSetConfig*/);
2226 AssertRC(rc);
2227 }
2228}
2229
2230
2231/**
2232 * @interface_method_impl{PDMUSBREG,pfnDestruct}
2233 */
2234static DECLCALLBACK(void) usbMsdDestruct(PPDMUSBINS pUsbIns)
2235{
2236 PDMUSB_CHECK_VERSIONS_RETURN_VOID(pUsbIns);
2237 PUSBMSD pThis = PDMINS_2_DATA(pUsbIns, PUSBMSD);
2238 LogFlow(("usbMsdDestruct/#%u:\n", pUsbIns->iInstance));
2239
2240 if (RTCritSectIsInitialized(&pThis->CritSect))
2241 {
2242 RTCritSectEnter(&pThis->CritSect);
2243 RTCritSectLeave(&pThis->CritSect);
2244 RTCritSectDelete(&pThis->CritSect);
2245 }
2246
2247 if (pThis->hEvtDoneQueue != NIL_RTSEMEVENT)
2248 {
2249 RTSemEventDestroy(pThis->hEvtDoneQueue);
2250 pThis->hEvtDoneQueue = NIL_RTSEMEVENT;
2251 }
2252
2253 if (pThis->hEvtReset != NIL_RTSEMEVENTMULTI)
2254 {
2255 RTSemEventMultiDestroy(pThis->hEvtReset);
2256 pThis->hEvtReset = NIL_RTSEMEVENTMULTI;
2257 }
2258}
2259
2260
2261/**
2262 * @interface_method_impl{PDMUSBREG,pfnConstruct}
2263 */
2264static DECLCALLBACK(int) usbMsdConstruct(PPDMUSBINS pUsbIns, int iInstance, PCFGMNODE pCfg, PCFGMNODE pCfgGlobal)
2265{
2266 RT_NOREF(pCfgGlobal);
2267 PDMUSB_CHECK_VERSIONS_RETURN(pUsbIns);
2268 PUSBMSD pThis = PDMINS_2_DATA(pUsbIns, PUSBMSD);
2269 PCPDMUSBHLP pHlp = pUsbIns->pHlpR3;
2270
2271 Log(("usbMsdConstruct/#%u:\n", iInstance));
2272
2273 /*
2274 * Perform the basic structure initialization first so the destructor
2275 * will not misbehave.
2276 */
2277 pThis->pUsbIns = pUsbIns;
2278 pThis->hEvtDoneQueue = NIL_RTSEMEVENT;
2279 pThis->hEvtReset = NIL_RTSEMEVENTMULTI;
2280 pThis->Lun0.IBase.pfnQueryInterface = usbMsdLun0QueryInterface;
2281 pThis->Lun0.IMediaPort.pfnQueryDeviceLocation = usbMsdLun0QueryDeviceLocation;
2282 pThis->Lun0.IMediaExPort.pfnIoReqCompleteNotify = usbMsdLun0IoReqCompleteNotify;
2283 pThis->Lun0.IMediaExPort.pfnIoReqCopyFromBuf = usbMsdLun0IoReqCopyFromBuf;
2284 pThis->Lun0.IMediaExPort.pfnIoReqCopyToBuf = usbMsdLun0IoReqCopyToBuf;
2285 pThis->Lun0.IMediaExPort.pfnIoReqQueryDiscardRanges = NULL;
2286 pThis->Lun0.IMediaExPort.pfnIoReqStateChanged = usbMsdLun0IoReqStateChanged;
2287 pThis->Lun0.IMediaExPort.pfnMediumEjected = usbMsdLun0MediumEjected;
2288 usbMsdQueueInit(&pThis->ToHostQueue);
2289 usbMsdQueueInit(&pThis->DoneQueue);
2290
2291 int rc = RTCritSectInit(&pThis->CritSect);
2292 AssertRCReturn(rc, rc);
2293
2294 rc = RTSemEventCreate(&pThis->hEvtDoneQueue);
2295 AssertRCReturn(rc, rc);
2296
2297 rc = RTSemEventMultiCreate(&pThis->hEvtReset);
2298 AssertRCReturn(rc, rc);
2299
2300 /*
2301 * Validate and read the configuration.
2302 */
2303 rc = pHlp->pfnCFGMValidateConfig(pCfg, "/", "", "", "UsbMsd", iInstance);
2304 if (RT_FAILURE(rc))
2305 return rc;
2306
2307 /*
2308 * Attach the SCSI driver.
2309 */
2310 rc = PDMUsbHlpDriverAttach(pUsbIns, 0 /*iLun*/, &pThis->Lun0.IBase, &pThis->Lun0.pIBase, "SCSI Port");
2311 if (RT_FAILURE(rc))
2312 return PDMUsbHlpVMSetError(pUsbIns, rc, RT_SRC_POS, N_("MSD failed to attach SCSI driver"));
2313 pThis->Lun0.pIMedia = PDMIBASE_QUERY_INTERFACE(pThis->Lun0.pIBase, PDMIMEDIA);
2314 if (!pThis->Lun0.pIMedia)
2315 return PDMUsbHlpVMSetError(pUsbIns, VERR_PDM_MISSING_INTERFACE_BELOW, RT_SRC_POS,
2316 N_("MSD failed to query the PDMIMEDIA from the driver below it"));
2317 pThis->Lun0.pIMediaEx = PDMIBASE_QUERY_INTERFACE(pThis->Lun0.pIBase, PDMIMEDIAEX);
2318 if (!pThis->Lun0.pIMediaEx)
2319 return PDMUsbHlpVMSetError(pUsbIns, VERR_PDM_MISSING_INTERFACE_BELOW, RT_SRC_POS,
2320 N_("MSD failed to query the PDMIMEDIAEX from the driver below it"));
2321
2322 /*
2323 * Find out what kind of device we are.
2324 */
2325 pThis->fIsCdrom = false;
2326 PDMMEDIATYPE enmType = pThis->Lun0.pIMedia->pfnGetType(pThis->Lun0.pIMedia);
2327 /* Anything else will be reported as a hard disk. */
2328 if (enmType == PDMMEDIATYPE_CDROM || enmType == PDMMEDIATYPE_DVD)
2329 pThis->fIsCdrom = true;
2330
2331 rc = pThis->Lun0.pIMediaEx->pfnIoReqAllocSizeSet(pThis->Lun0.pIMediaEx, sizeof(USBMSDREQ));
2332 if (RT_FAILURE(rc))
2333 return PDMUsbHlpVMSetError(pUsbIns, rc, RT_SRC_POS, N_("MSD failed to set I/O request size!"));
2334
2335 /*
2336 * Register the saved state data unit.
2337 */
2338 rc = PDMUsbHlpSSMRegister(pUsbIns, USB_MSD_SAVED_STATE_VERSION, sizeof(*pThis),
2339 NULL, usbMsdLiveExec, NULL,
2340 usbMsdSavePrep, usbMsdSaveExec, NULL,
2341 usbMsdLoadPrep, usbMsdLoadExec, NULL);
2342 if (RT_FAILURE(rc))
2343 return PDMUsbHlpVMSetError(pUsbIns, rc, RT_SRC_POS,
2344 N_("MSD failed to register SSM save state handlers"));
2345
2346 return VINF_SUCCESS;
2347}
2348
2349
2350/**
2351 * The USB Mass Storage Device (MSD) registration record.
2352 */
2353const PDMUSBREG g_UsbMsd =
2354{
2355 /* u32Version */
2356 PDM_USBREG_VERSION,
2357 /* szName */
2358 "Msd",
2359 /* pszDescription */
2360 "USB Mass Storage Device, one LUN.",
2361 /* fFlags */
2362 PDM_USBREG_HIGHSPEED_CAPABLE | PDM_USBREG_SUPERSPEED_CAPABLE
2363 | PDM_USBREG_SAVED_STATE_SUPPORTED,
2364 /* cMaxInstances */
2365 ~0U,
2366 /* cbInstance */
2367 sizeof(USBMSD),
2368 /* pfnConstruct */
2369 usbMsdConstruct,
2370 /* pfnDestruct */
2371 usbMsdDestruct,
2372 /* pfnVMInitComplete */
2373 NULL,
2374 /* pfnVMPowerOn */
2375 NULL,
2376 /* pfnVMReset */
2377 usbMsdVMReset,
2378 /* pfnVMSuspend */
2379 usbMsdVMSuspend,
2380 /* pfnVMResume */
2381 NULL,
2382 /* pfnVMPowerOff */
2383 usbMsdVMPowerOff,
2384 /* pfnHotPlugged */
2385 NULL,
2386 /* pfnHotUnplugged */
2387 NULL,
2388 /* pfnDriverAttach */
2389 usbMsdDriverAttach,
2390 /* pfnDriverDetach */
2391 usbMsdDriverDetach,
2392 /* pfnQueryInterface */
2393 NULL,
2394 /* pfnUsbReset */
2395 usbMsdUsbReset,
2396 /* pfnUsbGetCachedDescriptors */
2397 usbMsdUsbGetDescriptorCache,
2398 /* pfnUsbSetConfiguration */
2399 usbMsdUsbSetConfiguration,
2400 /* pfnUsbSetInterface */
2401 usbMsdUsbSetInterface,
2402 /* pfnUsbClearHaltedEndpoint */
2403 usbMsdUsbClearHaltedEndpoint,
2404 /* pfnUrbNew */
2405 NULL/*usbMsdUrbNew*/,
2406 /* pfnQueue */
2407 usbMsdQueue,
2408 /* pfnUrbCancel */
2409 usbMsdUrbCancel,
2410 /* pfnUrbReap */
2411 usbMsdUrbReap,
2412 /* pfnWakeup */
2413 usbMsdWakeup,
2414 /* u32TheEnd */
2415 PDM_USBREG_VERSION
2416};
2417
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette