VirtualBox

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

Last change on this file since 91942 was 91882, checked in by vboxsync, 3 years ago

Devices/Storage: Change the USB mass storage device emulations to access the CFGM and SSM API through the USB helper callback table only, bugref:10074

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