VirtualBox

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

Last change on this file since 43788 was 43788, checked in by vboxsync, 12 years ago

Scraped off some of the bit rot.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 56.0 KB
Line 
1/* $Id: UsbMsd.cpp 43788 2012-10-31 18:30:35Z vboxsync $ */
2/** @file
3 * UsbMSD - USB Mass Storage Device Emulation.
4 */
5
6/*
7 * Copyright (C) 2007-2010 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* Header Files *
20*******************************************************************************/
21#define LOG_GROUP LOG_GROUP_USB_MSD
22#include <VBox/vmm/pdmusb.h>
23#include <VBox/log.h>
24#include <VBox/err.h>
25#include <VBox/scsi.h>
26#include <iprt/assert.h>
27#include <iprt/critsect.h>
28#include <iprt/mem.h>
29#include <iprt/semaphore.h>
30#include <iprt/string.h>
31#include <iprt/uuid.h>
32#include "VBoxDD.h"
33
34
35/*******************************************************************************
36* Defined Constants And Macros *
37*******************************************************************************/
38/** @name USB MSD string IDs
39 * @{ */
40#define USBMSD_STR_ID_MANUFACTURER 1
41#define USBMSD_STR_ID_PRODUCT_HD 2
42/** @} */
43
44/** @name USB MSD vendor and product IDs
45 * @{ */
46#define VBOX_USB_VENDOR 0x80EE
47#define USBMSD_PID_HD 0x0030
48#define USBMSD_PID_CD 0x0031
49/** @} */
50
51/*******************************************************************************
52* Structures and Typedefs *
53*******************************************************************************/
54
55/**
56 * USB MSD Command Block Wrapper or CBW. The command block
57 * itself (CBWCB) contains protocol-specific data (here SCSI).
58 */
59#pragma pack(1)
60typedef struct USBCBW
61{
62 uint32_t dCBWSignature;
63#define USBCBW_SIGNATURE UINT32_C(0x43425355)
64 uint32_t dCBWTag;
65 uint32_t dCBWDataTransferLength;
66 uint8_t bmCBWFlags;
67#define USBCBW_DIR_MASK RT_BIT(7)
68#define USBCBW_DIR_OUT 0
69#define USBCBW_DIR_IN RT_BIT(7)
70 uint8_t bCBWLun;
71 uint8_t bCBWCBLength;
72 uint8_t CBWCB[16];
73} USBCBW;
74#pragma pack()
75AssertCompileSize(USBCBW, 31);
76/** Pointer to a Command Block Wrapper. */
77typedef USBCBW *PUSBCBW;
78/** Pointer to a const Command Block Wrapper. */
79typedef const USBCBW *PCUSBCBW;
80
81/**
82 * USB MSD Command Status Wrapper or CSW.
83 */
84#pragma pack(1)
85typedef struct USBCSW
86{
87 uint32_t dCSWSignature;
88#define USBCSW_SIGNATURE UINT32_C(0x53425355)
89 uint32_t dCSWTag;
90 uint32_t dCSWDataResidue;
91#define USBCSW_STATUS_OK UINT8_C(0)
92#define USBCSW_STATUS_FAILED UINT8_C(1)
93#define USBCSW_STATUS_PHASE_ERROR UINT8_C(2)
94 uint8_t bCSWStatus;
95} USBCSW;
96#pragma pack()
97AssertCompileSize(USBCSW, 13);
98/** Pointer to a Command Status Wrapper. */
99typedef USBCSW *PUSBCSW;
100/** Pointer to a const Command Status Wrapper. */
101typedef const USBCSW *PCUSBCSW;
102
103
104/**
105 * The USB MSD request state.
106 */
107typedef enum USBMSDREQSTATE
108{
109 /** Invalid status. */
110 USBMSDREQSTATE_INVALID = 0,
111 /** Ready to receive a new SCSI command. */
112 USBMSDREQSTATE_READY,
113 /** Waiting for the host to supply data. */
114 USBMSDREQSTATE_DATA_FROM_HOST,
115 /** The SCSI request is being executed by the driver. */
116 USBMSDREQSTATE_EXECUTING,
117 /** Have (more) data for the host. */
118 USBMSDREQSTATE_DATA_TO_HOST,
119 /** Waiting to supply status information to the host. */
120 USBMSDREQSTATE_STATUS,
121 /** Destroy the request upon completion.
122 * This is set when the SCSI request doesn't complete before for the device or
123 * mass storage reset operation times out. USBMSD::pReq will be set to NULL
124 * and the only reference to this request will be with DrvSCSI. */
125 USBMSDREQSTATE_DESTROY_ON_COMPLETION,
126 /** The end of the valid states. */
127 USBMSDREQSTATE_END
128} USBMSDREQSTATE;
129
130
131/**
132 * A pending USB MSD request.
133 */
134typedef struct USBMSDREQ
135{
136 /** The state of the request. */
137 USBMSDREQSTATE enmState;
138 /** The size of the data buffer. */
139 size_t cbBuf;
140 /** Pointer to the data buffer. */
141 uint8_t *pbBuf;
142 /** Current buffer offset. */
143 uint32_t offBuf;
144 /** The current Cbw when we're in the pending state. */
145 USBCBW Cbw;
146 /** The current SCSI request. */
147 PDMSCSIREQUEST ScsiReq;
148 /** The scatter-gather segment used by ScsiReq for describing pbBuf. */
149 RTSGSEG ScsiReqSeg;
150 /** The sense buffer for the current SCSI request. */
151 uint8_t ScsiReqSense[64];
152 /** The status of a completed SCSI request. */
153 int iScsiReqStatus;
154 /** Set if the request structure must be destroyed when the SCSI driver
155 * completes it. This is used to deal with requests that runs while the
156 * device is being reset. */
157 bool fDestoryOnCompletion;
158 /** Pointer to the USB device instance owning it. */
159 PPDMUSBINS pUsbIns;
160} USBMSDREQ;
161/** Pointer to a USB MSD request. */
162typedef USBMSDREQ *PUSBMSDREQ;
163
164
165/**
166 * Endpoint status data.
167 */
168typedef struct USBMSDEP
169{
170 bool fHalted;
171} USBMSDEP;
172/** Pointer to the endpoint status. */
173typedef USBMSDEP *PUSBMSDEP;
174
175
176/**
177 * A URB queue.
178 */
179typedef struct USBMSDURBQUEUE
180{
181 /** The head pointer. */
182 PVUSBURB pHead;
183 /** Where to insert the next entry. */
184 PVUSBURB *ppTail;
185} USBMSDURBQUEUE;
186/** Pointer to a URB queue. */
187typedef USBMSDURBQUEUE *PUSBMSDURBQUEUE;
188/** Pointer to a const URB queue. */
189typedef USBMSDURBQUEUE const *PCUSBMSDURBQUEUE;
190
191
192/**
193 * The USB MSD instance data.
194 */
195typedef struct USBMSD
196{
197 /** Pointer back to the PDM USB Device instance structure. */
198 PPDMUSBINS pUsbIns;
199 /** Critical section protecting the device state. */
200 RTCRITSECT CritSect;
201
202 /** The current configuration.
203 * (0 - default, 1 - the only, i.e configured.) */
204 uint8_t bConfigurationValue;
205#if 0
206 /** The state of the MSD (state machine).*/
207 USBMSDSTATE enmState;
208#endif
209 /** Endpoint 0 is the default control pipe, 1 is the host->dev bulk pipe and 2
210 * is the dev->host one. */
211 USBMSDEP aEps[3];
212 /** The current request. */
213 PUSBMSDREQ pReq;
214
215 /** Pending to-host queue.
216 * The URBs waiting here are pending the completion of the current request and
217 * data or status to become available.
218 */
219 USBMSDURBQUEUE ToHostQueue;
220
221 /** Done queue
222 * The URBs stashed here are waiting to be reaped. */
223 USBMSDURBQUEUE DoneQueue;
224 /** Signalled when adding an URB to the done queue and fHaveDoneQueueWaiter
225 * is set. */
226 RTSEMEVENT hEvtDoneQueue;
227 /** Someone is waiting on the done queue. */
228 bool fHaveDoneQueueWaiter;
229
230 /** Whether to signal the reset semaphore when the current request completes. */
231 bool fSignalResetSem;
232 /** Semaphore usbMsdUsbReset waits on when a request is executing at reset
233 * time. Only signalled when fSignalResetSem is set. */
234 RTSEMEVENTMULTI hEvtReset;
235 /** The reset URB.
236 * This is waiting for SCSI request completion before finishing the reset. */
237 PVUSBURB pResetUrb;
238
239 /**
240 * LUN\#0 data.
241 */
242 struct
243 {
244 /** The base interface for LUN\#0. */
245 PDMIBASE IBase;
246 /** The SCSI port interface for LUN\#0 */
247 PDMISCSIPORT IScsiPort;
248
249 /** The base interface for the SCSI driver connected to LUN\#0. */
250 PPDMIBASE pIBase;
251 /** The SCSI connector interface for the SCSI driver connected to LUN\#0. */
252 PPDMISCSICONNECTOR pIScsiConnector;
253 } Lun0;
254
255} USBMSD;
256/** Pointer to the USB MSD instance data. */
257typedef USBMSD *PUSBMSD;
258
259
260/*******************************************************************************
261* Global Variables *
262*******************************************************************************/
263static const PDMUSBDESCCACHESTRING g_aUsbMsdStrings_en_US[] =
264{
265 { USBMSD_STR_ID_MANUFACTURER, "VirtualBox" },
266 { USBMSD_STR_ID_PRODUCT_HD, "USB Harddisk" },
267};
268
269static const PDMUSBDESCCACHELANG g_aUsbMsdLanguages[] =
270{
271 { 0x0409, RT_ELEMENTS(g_aUsbMsdStrings_en_US), g_aUsbMsdStrings_en_US }
272};
273
274static const VUSBDESCENDPOINTEX g_aUsbMsdEndpointDescs[2] =
275{
276 {
277 {
278 /* .bLength = */ sizeof(VUSBDESCENDPOINT),
279 /* .bDescriptorType = */ VUSB_DT_ENDPOINT,
280 /* .bEndpointAddress = */ 0x81 /* ep=1, in */,
281 /* .bmAttributes = */ 2 /* bulk */,
282 /* .wMaxPacketSize = */ 64,//512 /* or 64? */, <-- depends on device speed!
283 /* .bInterval = */ 0,
284 },
285 /* .pvMore = */ NULL,
286 /* .pvClass = */ NULL,
287 /* .cbClass = */ 0
288 },
289 {
290 {
291 /* .bLength = */ sizeof(VUSBDESCENDPOINT),
292 /* .bDescriptorType = */ VUSB_DT_ENDPOINT,
293 /* .bEndpointAddress = */ 0x02 /* ep=2, out */,
294 /* .bmAttributes = */ 2 /* bulk */,
295 /* .wMaxPacketSize = */ 64,//512 /* or 64? */, <-- depends on device speed!
296 /* .bInterval = */ 0,
297 },
298 /* .pvMore = */ NULL,
299 /* .pvClass = */ NULL,
300 /* .cbClass = */ 0
301 }
302};
303
304static const VUSBDESCINTERFACEEX g_UsbMsdInterfaceDesc =
305{
306 {
307 /* .bLength = */ sizeof(VUSBDESCINTERFACE),
308 /* .bDescriptorType = */ VUSB_DT_INTERFACE,
309 /* .bInterfaceNumber = */ 0,
310 /* .bAlternateSetting = */ 0,
311 /* .bNumEndpoints = */ 2,
312 /* .bInterfaceClass = */ 8 /* Mass Storage */,
313 /* .bInterfaceSubClass = */ 6 /* SCSI transparent command set */,
314 /* .bInterfaceProtocol = */ 0x50 /* Bulk-Only Transport */,
315 /* .iInterface = */ 0
316 },
317 /* .pvMore = */ NULL,
318 /* .pvClass = */ NULL,
319 /* .cbClass = */ 0,
320 &g_aUsbMsdEndpointDescs[0]
321};
322
323static const VUSBINTERFACE g_aUsbMsdInterfaces[] =
324{
325 { &g_UsbMsdInterfaceDesc, /* .cSettings = */ 1 },
326};
327
328static const VUSBDESCCONFIGEX g_UsbMsdConfigDesc =
329{
330 {
331 /* .bLength = */ sizeof(VUSBDESCCONFIG),
332 /* .bDescriptorType = */ VUSB_DT_CONFIG,
333 /* .wTotalLength = */ 0 /* recalculated on read */,
334 /* .bNumInterfaces = */ RT_ELEMENTS(g_aUsbMsdInterfaces),
335 /* .bConfigurationValue =*/ 1,
336 /* .iConfiguration = */ 0,
337 /* .bmAttributes = */ RT_BIT(7),
338 /* .MaxPower = */ 50 /* 100mA */
339 },
340 NULL, /* pvMore */
341 &g_aUsbMsdInterfaces[0],
342 NULL /* pvOriginal */
343};
344
345static const VUSBDESCDEVICE g_UsbMsdDeviceDesc =
346{
347 /* .bLength = */ sizeof(g_UsbMsdDeviceDesc),
348 /* .bDescriptorType = */ VUSB_DT_DEVICE,
349 /* .bcdUsb = */ 0x200, /* USB 2.0 */
350 /* .bDeviceClass = */ 0 /* Class specified in the interface desc. */,
351 /* .bDeviceSubClass = */ 0 /* Subclass specified in the interface desc. */,
352 /* .bDeviceProtocol = */ 0 /* Protocol specified in the interface desc. */,
353 /* .bMaxPacketSize0 = */ 64,
354 /* .idVendor = */ VBOX_USB_VENDOR,
355 /* .idProduct = */ USBMSD_PID_HD,
356 /* .bcdDevice = */ 0x0100, /* 1.0 */
357 /* .iManufacturer = */ USBMSD_STR_ID_MANUFACTURER,
358 /* .iProduct = */ USBMSD_STR_ID_PRODUCT_HD,
359 /* .iSerialNumber = */ 0,
360 /* .bNumConfigurations = */ 1
361};
362
363static const VUSBDEVICEQUALIFIER g_UsbMsdDeviceQualifier =
364{
365 /* .bLength = */ sizeof(g_UsbMsdDeviceQualifier),
366 /* .bDescriptorType = */ VUSB_DT_DEVICE_QUALIFIER,
367 /* .bcdUsb = */ 0x200, /* USB 2.0 */
368 /* .bDeviceClass = */ 0 /* Class specified in the interface desc. */,
369 /* .bDeviceSubClass = */ 0 /* Subclass specified in the interface desc. */,
370 /* .bDeviceProtocol = */ 0 /* Protocol specified in the interface desc. */,
371 /* .bMaxPacketSize0 = */ 64,
372 /* .bNumConfigurations = */ 1,
373 /* .bReserved = */ 0
374};
375
376static const PDMUSBDESCCACHE g_UsbMsdDescCache =
377{
378 /* .pDevice = */ &g_UsbMsdDeviceDesc,
379 /* .paConfigs = */ &g_UsbMsdConfigDesc,
380 /* .paLanguages = */ g_aUsbMsdLanguages,
381 /* .cLanguages = */ RT_ELEMENTS(g_aUsbMsdLanguages),
382 /* .fUseCachedDescriptors = */ true,
383 /* .fUseCachedStringsDescriptors = */ true
384};
385
386
387/*******************************************************************************
388* Internal Functions *
389*******************************************************************************/
390static int usbMsdHandleBulkDevToHost(PUSBMSD pThis, PUSBMSDEP pEp, PVUSBURB pUrb);
391
392
393/**
394 * Initializes an URB queue.
395 *
396 * @param pQueue The URB queue.
397 */
398static void usbMsdQueueInit(PUSBMSDURBQUEUE pQueue)
399{
400 pQueue->pHead = NULL;
401 pQueue->ppTail = &pQueue->pHead;
402}
403
404
405
406/**
407 * Inserts an URB at the end of the queue.
408 *
409 * @param pQueue The URB queue.
410 * @param pUrb The URB to insert.
411 */
412DECLINLINE(void) usbMsdQueueAddTail(PUSBMSDURBQUEUE pQueue, PVUSBURB pUrb)
413{
414 pUrb->Dev.pNext = NULL;
415 *pQueue->ppTail = pUrb;
416 pQueue->ppTail = &pUrb->Dev.pNext;
417}
418
419
420/**
421 * Unlinks the head of the queue and returns it.
422 *
423 * @returns The head entry.
424 * @param pQueue The URB queue.
425 */
426DECLINLINE(PVUSBURB) usbMsdQueueRemoveHead(PUSBMSDURBQUEUE pQueue)
427{
428 PVUSBURB pUrb = pQueue->pHead;
429 if (pUrb)
430 {
431 PVUSBURB pNext = pUrb->Dev.pNext;
432 pQueue->pHead = pNext;
433 if (!pNext)
434 pQueue->ppTail = &pQueue->pHead;
435 else
436 pUrb->Dev.pNext = NULL;
437 }
438 return pUrb;
439}
440
441
442/**
443 * Removes an URB from anywhere in the queue.
444 *
445 * @returns true if found, false if not.
446 * @param pQueue The URB queue.
447 * @param pUrb The URB to remove.
448 */
449DECLINLINE(bool) usbMsdQueueRemove(PUSBMSDURBQUEUE pQueue, PVUSBURB pUrb)
450{
451 PVUSBURB pCur = pQueue->pHead;
452 if (pCur == pUrb)
453 pQueue->pHead = pUrb->Dev.pNext;
454 else
455 {
456 while (pCur)
457 {
458 if (pCur->Dev.pNext == pUrb)
459 {
460 pCur->Dev.pNext = pUrb->Dev.pNext;
461 break;
462 }
463 pCur = pCur->Dev.pNext;
464 }
465 if (!pCur)
466 return false;
467 }
468 if (!pUrb->Dev.pNext)
469 pQueue->ppTail = &pQueue->pHead;
470 return true;
471}
472
473
474/**
475 * Checks if the queue is empty or not.
476 *
477 * @returns true if it is, false if it isn't.
478 * @param pQueue The URB queue.
479 */
480DECLINLINE(bool) usbMsdQueueIsEmpty(PCUSBMSDURBQUEUE pQueue)
481{
482 return pQueue->pHead == NULL;
483}
484
485
486/**
487 * Links an URB into the done queue.
488 *
489 * @param pThis The MSD instance.
490 * @param pUrb The URB.
491 */
492static void usbMsdLinkDone(PUSBMSD pThis, PVUSBURB pUrb)
493{
494 usbMsdQueueAddTail(&pThis->DoneQueue, pUrb);
495
496 if (pThis->fHaveDoneQueueWaiter)
497 {
498 int rc = RTSemEventSignal(pThis->hEvtDoneQueue);
499 AssertRC(rc);
500 }
501}
502
503
504
505
506/**
507 * Allocates a new request and does basic init.
508 *
509 * @returns Pointer to the new request. NULL if we're out of memory.
510 * @param pUsbIns The instance allocating it.
511 */
512static PUSBMSDREQ usbMsdReqAlloc(PPDMUSBINS pUsbIns)
513{
514 PUSBMSDREQ pReq = (PUSBMSDREQ)PDMUsbHlpMMHeapAllocZ(pUsbIns, sizeof(*pReq));
515 if (pReq)
516 {
517 pReq->enmState = USBMSDREQSTATE_READY;
518 pReq->iScsiReqStatus = -1;
519 pReq->pUsbIns = pUsbIns;
520 }
521 else
522 LogRel(("usbMsdReqAlloc: Out of memory\n"));
523 return pReq;
524}
525
526
527/**
528 * Frees a request.
529 *
530 * @param pReq The request.
531 */
532static void usbMsdReqFree(PUSBMSDREQ pReq)
533{
534 /*
535 * Check the input.
536 */
537 AssertReturnVoid( pReq->enmState > USBMSDREQSTATE_INVALID
538 && pReq->enmState != USBMSDREQSTATE_EXECUTING
539 && pReq->enmState < USBMSDREQSTATE_END);
540 PPDMUSBINS pUsbIns = pReq->pUsbIns;
541 AssertPtrReturnVoid(pUsbIns);
542 AssertReturnVoid(PDM_VERSION_ARE_COMPATIBLE(pUsbIns->u32Version, PDM_USBINS_VERSION));
543
544 /*
545 * Invalidate it and free the associated resources.
546 */
547 pReq->enmState = USBMSDREQSTATE_INVALID;
548 pReq->cbBuf = 0;
549 pReq->offBuf = 0;
550 pReq->ScsiReq.pbCDB = NULL;
551 pReq->ScsiReq.paScatterGatherHead = NULL;
552 pReq->ScsiReq.pbSenseBuffer = NULL;
553 pReq->ScsiReq.pvUser = NULL;
554 pReq->ScsiReqSeg.cbSeg = 0;
555 pReq->ScsiReqSeg.pvSeg = NULL;
556
557 if (pReq->pbBuf)
558 {
559 PDMUsbHlpMMHeapFree(pUsbIns, pReq->pbBuf);
560 pReq->pbBuf = NULL;
561 }
562
563 PDMUsbHlpMMHeapFree(pUsbIns, pReq);
564}
565
566
567/**
568 * Prepares a request for execution or data buffering.
569 *
570 * @param pReq The request.
571 * @param pCbw The SCSI command block wrapper.
572 */
573static void usbMsdReqPrepare(PUSBMSDREQ pReq, PCUSBCBW pCbw)
574{
575 /* Copy the CBW */
576 size_t cbCopy = RT_OFFSETOF(USBCBW, CBWCB[pCbw->bCBWCBLength]);
577 memcpy(&pReq->Cbw, pCbw, cbCopy);
578 memset((uint8_t *)&pReq->Cbw + cbCopy, 0, sizeof(pReq->Cbw) - cbCopy);
579
580 /* Setup the SCSI request. */
581 pReq->ScsiReq.uLogicalUnit = pReq->Cbw.bCBWLun;
582 pReq->ScsiReq.uDataDirection = (pReq->Cbw.bmCBWFlags & USBCBW_DIR_MASK) == USBCBW_DIR_OUT
583 ? PDMSCSIREQUESTTXDIR_TO_DEVICE
584 : PDMSCSIREQUESTTXDIR_FROM_DEVICE;
585 pReq->ScsiReq.cbCDB = pReq->Cbw.bCBWCBLength;
586
587 pReq->ScsiReq.pbCDB = &pReq->Cbw.CBWCB[0];
588 pReq->offBuf = 0;
589 pReq->ScsiReqSeg.pvSeg = pReq->pbBuf;
590 pReq->ScsiReqSeg.cbSeg = pReq->Cbw.dCBWDataTransferLength;
591 pReq->ScsiReq.cbScatterGather = pReq->Cbw.dCBWDataTransferLength;
592 pReq->ScsiReq.cScatterGatherEntries = 1;
593 pReq->ScsiReq.paScatterGatherHead = &pReq->ScsiReqSeg;
594 pReq->ScsiReq.cbSenseBuffer = sizeof(pReq->ScsiReqSense);
595 pReq->ScsiReq.pbSenseBuffer = &pReq->ScsiReqSense[0];
596 pReq->ScsiReq.pvUser = NULL;
597 RT_ZERO(pReq->ScsiReqSense);
598 pReq->iScsiReqStatus = -1;
599}
600
601
602/**
603 * Makes sure that there is sufficient buffer space available.
604 *
605 * @returns Success indicator (true/false)
606 * @param pReq
607 * @param cbBuf The required buffer space.
608 */
609static int usbMsdReqEnsureBuffer(PUSBMSDREQ pReq, size_t cbBuf)
610{
611 if (RT_LIKELY(pReq->cbBuf >= cbBuf))
612 RT_BZERO(pReq->pbBuf, cbBuf);
613 else
614 {
615 PDMUsbHlpMMHeapFree(pReq->pUsbIns, pReq->pbBuf);
616 pReq->cbBuf = 0;
617
618 cbBuf = RT_ALIGN_Z(cbBuf, 0x1000);
619 pReq->pbBuf = (uint8_t *)PDMUsbHlpMMHeapAllocZ(pReq->pUsbIns, cbBuf);
620 if (!pReq->pbBuf)
621 return false;
622
623 pReq->cbBuf = cbBuf;
624 }
625 return true;
626}
627
628
629/**
630 * Completes the URB with a stalled state, halting the pipe.
631 */
632static int usbMsdCompleteStall(PUSBMSD pThis, PUSBMSDEP pEp, PVUSBURB pUrb, const char *pszWhy)
633{
634 Log(("usbMsdCompleteStall/#%u: pUrb=%p:%s: %s\n", pThis->pUsbIns->iInstance, pUrb, pUrb->pszDesc, pszWhy));
635
636 pUrb->enmStatus = VUSBSTATUS_STALL;
637
638 /** @todo figure out if the stall is global or pipe-specific or both. */
639 if (pEp)
640 pEp->fHalted = true;
641 else
642 {
643 pThis->aEps[1].fHalted = true;
644 pThis->aEps[2].fHalted = true;
645 }
646
647 usbMsdLinkDone(pThis, pUrb);
648 return VINF_SUCCESS;
649}
650
651
652/**
653 * Completes the URB with a OK state.
654 */
655static int usbMsdCompleteOk(PUSBMSD pThis, PVUSBURB pUrb, size_t cbData)
656{
657 Log(("usbMsdCompleteOk/#%u: pUrb=%p:%s cbData=%#zx\n", pThis->pUsbIns->iInstance, pUrb, pUrb->pszDesc, cbData));
658
659 pUrb->enmStatus = VUSBSTATUS_OK;
660 pUrb->cbData = (uint32_t)cbData;
661
662 usbMsdLinkDone(pThis, pUrb);
663 return VINF_SUCCESS;
664}
665
666
667/**
668 * Reset worker for usbMsdUsbReset, usbMsdUsbSetConfiguration and
669 * usbMsdUrbHandleDefaultPipe.
670 *
671 * @returns VBox status code.
672 * @param pThis The MSD instance.
673 * @param pUrb Set when usbMsdUrbHandleDefaultPipe is the
674 * caller.
675 * @param fSetConfig Set when usbMsdUsbSetConfiguration is the
676 * caller.
677 */
678static int usbMsdResetWorker(PUSBMSD pThis, PVUSBURB pUrb, bool fSetConfig)
679{
680 /*
681 * Wait for the any command currently executing to complete before
682 * resetting. (We cannot cancel its execution.) How we do this depends
683 * on the reset method.
684 */
685 PUSBMSDREQ pReq = pThis->pReq;
686 if ( pReq
687 && pReq->enmState == USBMSDREQSTATE_EXECUTING)
688 {
689 /* Don't try to deal with the set config variant nor multiple build-only
690 mass storage resets. */
691 if (pThis->pResetUrb && (pUrb || fSetConfig))
692 {
693 Log(("usbMsdResetWorker: pResetUrb is already %p:%s - stalling\n", pThis->pResetUrb, pThis->pResetUrb->pszDesc));
694 return usbMsdCompleteStall(pThis, NULL, pUrb, "pResetUrb");
695 }
696
697 /* Bulk-Only Mass Storage Reset: Complete the reset on request completion. */
698 if (pUrb)
699 {
700 pThis->pResetUrb = pUrb;
701 Log(("usbMsdResetWorker: Setting pResetUrb to %p:%s\n", pThis->pResetUrb, pThis->pResetUrb->pszDesc));
702 return VINF_SUCCESS;
703 }
704
705 /* Device reset: Wait for up to 10 ms. If it doesn't work, ditch
706 whoe the request structure. We'll allocate a new one when needed. */
707 Log(("usbMsdResetWorker: Waiting for completion...\n"));
708 Assert(!pThis->fSignalResetSem);
709 pThis->fSignalResetSem = true;
710 RTSemEventMultiReset(pThis->hEvtReset);
711 RTCritSectLeave(&pThis->CritSect);
712
713 int rc = RTSemEventMultiWait(pThis->hEvtReset, 10 /*ms*/);
714
715 RTCritSectEnter(&pThis->CritSect);
716 pThis->fSignalResetSem = false;
717 if ( RT_FAILURE(rc)
718 || pReq->enmState == USBMSDREQSTATE_EXECUTING)
719 {
720 Log(("usbMsdResetWorker: Didn't complete, ditching the current request (%p)!\n", pReq));
721 Assert(pReq == pThis->pReq);
722 pReq->enmState = USBMSDREQSTATE_DESTROY_ON_COMPLETION;
723 pThis->pReq = NULL;
724 pReq = NULL;
725 }
726 }
727
728 /*
729 * Reset the request and device state.
730 */
731 if (pReq)
732 {
733 pReq->enmState = USBMSDREQSTATE_READY;
734 pReq->iScsiReqStatus = -1;
735 }
736
737 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aEps); i++)
738 pThis->aEps[i].fHalted = false;
739
740 if (!pUrb && !fSetConfig) /* (only device reset) */
741 pThis->bConfigurationValue = 0; /* default */
742
743 /*
744 * Ditch all pending URBs.
745 */
746 PVUSBURB pCurUrb;
747 while ((pCurUrb = usbMsdQueueRemoveHead(&pThis->ToHostQueue)) != NULL)
748 {
749 pCurUrb->enmStatus = VUSBSTATUS_CRC;
750 usbMsdLinkDone(pThis, pCurUrb);
751 }
752
753 pCurUrb = pThis->pResetUrb;
754 if (pCurUrb)
755 {
756 pThis->pResetUrb = NULL;
757 pCurUrb->enmStatus = VUSBSTATUS_CRC;
758 usbMsdLinkDone(pThis, pCurUrb);
759 }
760
761 if (pUrb)
762 return usbMsdCompleteOk(pThis, pUrb, 0);
763 return VINF_SUCCESS;
764}
765
766
767/**
768 * @interface_method_impl{PDMISCSIPORT,pfnSCSIRequestCompleted}
769 */
770static DECLCALLBACK(int) usbMsdLun0ScsiRequestCompleted(PPDMISCSIPORT pInterface, PPDMSCSIREQUEST pSCSIRequest,
771 int rcCompletion, bool fRedo, int rcReq)
772{
773 PUSBMSD pThis = RT_FROM_MEMBER(pInterface, USBMSD, Lun0.IScsiPort);
774 PUSBMSDREQ pReq = RT_FROM_MEMBER(pSCSIRequest, USBMSDREQ, ScsiReq);
775
776 Log(("usbMsdLun0ScsiRequestCompleted: pReq=%p dCBWTag=%#x iScsiReqStatus=%u \n", pReq, pReq->Cbw.dCBWTag, rcCompletion));
777 RTCritSectEnter(&pThis->CritSect);
778
779 if (pReq->enmState != USBMSDREQSTATE_DESTROY_ON_COMPLETION)
780 {
781 Assert(pReq->enmState == USBMSDREQSTATE_EXECUTING);
782 Assert(pThis->pReq == pReq);
783 pReq->iScsiReqStatus = rcCompletion;
784
785 /*
786 * Advance the state machine. The state machine is not affected by
787 * SCSI errors.
788 */
789 if ((pReq->Cbw.bmCBWFlags & USBCBW_DIR_MASK) == USBCBW_DIR_OUT)
790 {
791 pReq->enmState = USBMSDREQSTATE_STATUS;
792 Log(("usbMsdLun0ScsiRequestCompleted: Entering STATUS\n"));
793 }
794 else
795 {
796 pReq->enmState = USBMSDREQSTATE_DATA_TO_HOST;
797 Log(("usbMsdLun0ScsiRequestCompleted: Entering DATA_TO_HOST\n"));
798 }
799
800 /*
801 * Deal with pending to-host URBs.
802 */
803 for (;;)
804 {
805 PVUSBURB pUrb = usbMsdQueueRemoveHead(&pThis->ToHostQueue);
806 if (!pUrb)
807 break;
808
809 /* Process it the normal way. */
810 usbMsdHandleBulkDevToHost(pThis, &pThis->aEps[1], pUrb);
811 }
812 }
813 else
814 {
815 Log(("usbMsdLun0ScsiRequestCompleted: freeing %p\n", pReq));
816 usbMsdReqFree(pReq);
817 }
818
819 if (pThis->fSignalResetSem)
820 RTSemEventMultiSignal(pThis->hEvtReset);
821
822 if (pThis->pResetUrb)
823 {
824 pThis->pResetUrb = NULL;
825 usbMsdResetWorker(pThis, pThis->pResetUrb, false /*fSetConfig*/);
826 }
827
828 RTCritSectLeave(&pThis->CritSect);
829 return VINF_SUCCESS;
830}
831
832
833/**
834 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
835 */
836static DECLCALLBACK(void *) usbMsdLun0QueryInterface(PPDMIBASE pInterface, const char *pszIID)
837{
838 PUSBMSD pThis = RT_FROM_MEMBER(pInterface, USBMSD, Lun0.IBase);
839 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->Lun0.IBase);
840 PDMIBASE_RETURN_INTERFACE(pszIID, PDMISCSIPORT, &pThis->Lun0.IScsiPort);
841 return NULL;
842}
843
844
845/**
846 * @copydoc PDMUSBREG::pfnUrbReap
847 */
848static DECLCALLBACK(PVUSBURB) usbMsdUrbReap(PPDMUSBINS pUsbIns, RTMSINTERVAL cMillies)
849{
850 PUSBMSD pThis = PDMINS_2_DATA(pUsbIns, PUSBMSD);
851 LogFlow(("usbMsdUrbReap/#%u: cMillies=%u\n", pUsbIns->iInstance, cMillies));
852
853 RTCritSectEnter(&pThis->CritSect);
854
855 PVUSBURB pUrb = usbMsdQueueRemoveHead(&pThis->DoneQueue);
856 if (!pUrb && cMillies)
857 {
858 /* Wait */
859 pThis->fHaveDoneQueueWaiter = true;
860 RTCritSectLeave(&pThis->CritSect);
861
862 RTSemEventWait(pThis->hEvtDoneQueue, cMillies);
863
864 RTCritSectEnter(&pThis->CritSect);
865 pThis->fHaveDoneQueueWaiter = false;
866
867 pUrb = usbMsdQueueRemoveHead(&pThis->DoneQueue);
868 }
869
870 RTCritSectLeave(&pThis->CritSect);
871
872 if (pUrb)
873 Log(("usbMsdUrbReap/#%u: pUrb=%p:%s\n", pUsbIns->iInstance, pUrb, pUrb->pszDesc));
874 return pUrb;
875}
876
877
878/**
879 * @copydoc PDMUSBREG::pfnUrbCancel
880 */
881static DECLCALLBACK(int) usbMsdUrbCancel(PPDMUSBINS pUsbIns, PVUSBURB pUrb)
882{
883 PUSBMSD pThis = PDMINS_2_DATA(pUsbIns, PUSBMSD);
884 LogFlow(("usbMsdUrbCancel/#%u: pUrb=%p:%s\n", pUsbIns->iInstance, pUrb, pUrb->pszDesc));
885 RTCritSectEnter(&pThis->CritSect);
886
887 /*
888 * Remove the URB from the to-host queue and move it onto the done queue.
889 */
890 if (usbMsdQueueRemove(&pThis->ToHostQueue, pUrb))
891 usbMsdLinkDone(pThis, pUrb);
892
893 RTCritSectLeave(&pThis->CritSect);
894 return VINF_SUCCESS;
895}
896
897
898/**
899 * Fails an illegal SCSI request.
900 *
901 * @returns VBox status code.
902 * @param pThis The MSD instance data.
903 * @param pReq The MSD request.
904 * @param bAsc The ASC for the SCSI_SENSE_ILLEGAL_REQUEST.
905 * @param bAscq The ASC qualifier.
906 * @param pszWhy For logging why.
907 */
908static int usbMsdScsiIllegalRequest(PUSBMSD pThis, PUSBMSDREQ pReq, uint8_t bAsc, uint8_t bAscq, const char *pszWhy)
909{
910 Log(("usbMsdScsiIllegalRequest: bAsc=%#x bAscq=%#x %s\n", bAsc, bAscq, pszWhy));
911
912 RT_ZERO(pReq->ScsiReqSense);
913 pReq->ScsiReqSense[0] = 0x80 | SCSI_SENSE_RESPONSE_CODE_CURR_FIXED;
914 pReq->ScsiReqSense[2] = SCSI_SENSE_ILLEGAL_REQUEST;
915 pReq->ScsiReqSense[7] = 10;
916 pReq->ScsiReqSense[12] = SCSI_ASC_INVALID_MESSAGE;
917 pReq->ScsiReqSense[13] = 0; /* Should be ASCQ but it has the same value for success. */
918
919 usbMsdLun0ScsiRequestCompleted(&pThis->Lun0.IScsiPort, &pReq->ScsiReq, SCSI_STATUS_CHECK_CONDITION, false, VINF_SUCCESS);
920 return VINF_SUCCESS;
921}
922
923
924/**
925 * The SCSI driver doesn't handle SCSI_REQUEST_SENSE but instead
926 * returns the sense info with the request.
927 *
928 */
929static int usbMsdHandleScsiReqestSense(PUSBMSD pThis, PUSBMSDREQ pReq, PCUSBCBW pCbw)
930{
931 Log(("usbMsdHandleScsiReqestSense: Entering EXECUTING (dCBWTag=%#x).\n", pReq->Cbw.dCBWTag));
932 Assert(pReq == pThis->pReq);
933 pReq->enmState = USBMSDREQSTATE_EXECUTING;
934
935 /* validation */
936 if ((pCbw->bmCBWFlags & USBCBW_DIR_MASK) != USBCBW_DIR_IN)
937 return usbMsdScsiIllegalRequest(pThis, pReq, SCSI_ASC_INVALID_MESSAGE, 0, "direction");
938 if (pCbw->bCBWCBLength < 6)
939 return usbMsdScsiIllegalRequest(pThis, pReq, SCSI_ASC_INVALID_MESSAGE, 0, "length");
940 if ((pCbw->CBWCB[1] >> 5) != pCbw->bCBWLun)
941 return usbMsdScsiIllegalRequest(pThis, pReq, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0, "lun");
942 if (pCbw->bCBWLun != 0)
943 return usbMsdScsiIllegalRequest(pThis, pReq, SCSI_ASC_INVALID_MESSAGE, 0, "lun0");
944 if (pCbw->CBWCB[4] < 6)
945 return usbMsdScsiIllegalRequest(pThis, pReq, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0, "out length");
946
947 /* If the previous command succeeded successfully, whip up some sense data. */
948 if ( pReq->iScsiReqStatus == SCSI_STATUS_OK
949 && pReq->ScsiReqSense[0] == 0)
950 {
951 RT_ZERO(pReq->ScsiReqSense);
952#if 0 /** @todo something upsets linux about this stuff. Needs investigation. */
953 pReq->ScsiReqSense[0] = 0x80 | SCSI_SENSE_RESPONSE_CODE_CURR_FIXED;
954 pReq->ScsiReqSense[0] = SCSI_SENSE_RESPONSE_CODE_CURR_FIXED;
955 pReq->ScsiReqSense[2] = SCSI_SENSE_NONE;
956 pReq->ScsiReqSense[7] = 10;
957 pReq->ScsiReqSense[12] = SCSI_ASC_NONE;
958 pReq->ScsiReqSense[13] = SCSI_ASC_NONE; /* Should be ASCQ but it has the same value for success. */
959#endif
960 }
961
962 /* Copy the data into the result buffer. */
963 size_t cbCopy = RT_MIN(pCbw->dCBWDataTransferLength, sizeof(pReq->ScsiReqSense));
964 Log(("usbMsd: SCSI_REQUEST_SENSE - CBWCB[4]=%#x iOldState=%d, %u bytes, raw: %.*Rhxs\n",
965 pCbw->CBWCB[4], pReq->iScsiReqStatus, pCbw->dCBWDataTransferLength, RT_MAX(1, cbCopy), pReq->ScsiReqSense));
966 memcpy(pReq->pbBuf, &pReq->ScsiReqSense[0], cbCopy);
967
968 usbMsdReqPrepare(pReq, pCbw);
969
970 /* Do normal completion. */
971 usbMsdLun0ScsiRequestCompleted(&pThis->Lun0.IScsiPort, &pReq->ScsiReq, SCSI_STATUS_OK, false, VINF_SUCCESS);
972 return VINF_SUCCESS;
973}
974
975
976/**
977 * Wrapper around PDMISCSICONNECTOR::pfnSCSIRequestSend that deals with
978 * SCSI_REQUEST_SENSE.
979 *
980 * @returns VBox status code.
981 * @param pThis The MSD instance data.
982 * @param pReq The MSD request.
983 * @param pszCaller Where we're called from.
984 */
985static int usbMsdSubmitScsiCommand(PUSBMSD pThis, PUSBMSDREQ pReq, const char *pszCaller)
986{
987 Log(("%s: Entering EXECUTING (dCBWTag=%#x).\n", pszCaller, pReq->Cbw.dCBWTag));
988 Assert(pReq == pThis->pReq);
989 pReq->enmState = USBMSDREQSTATE_EXECUTING;
990
991 switch (pReq->ScsiReq.pbCDB[0])
992 {
993 case SCSI_REQUEST_SENSE:
994 {
995 }
996
997 default:
998 return pThis->Lun0.pIScsiConnector->pfnSCSIRequestSend(pThis->Lun0.pIScsiConnector, &pReq->ScsiReq);
999 }
1000}
1001
1002/**
1003 * Validates a SCSI request before passing it down to the SCSI driver.
1004 *
1005 * @returns true / false. The request will be completed on failure.
1006 * @param pThis The MSD instance data.
1007 * @param pCbw The USB command block wrapper.
1008 * @param pUrb The URB.
1009 */
1010static bool usbMsdIsValidCommand(PUSBMSD pThis, PCUSBCBW pCbw, PVUSBURB pUrb)
1011{
1012 switch (pCbw->CBWCB[0])
1013 {
1014 case SCSI_REQUEST_SENSE:
1015 /** @todo validate this. */
1016 return true;
1017
1018 default:
1019 return true;
1020 }
1021}
1022
1023
1024/**
1025 * Handle requests sent to the outbound (to device) bulk pipe.
1026 */
1027static int usbMsdHandleBulkHostToDev(PUSBMSD pThis, PUSBMSDEP pEp, PVUSBURB pUrb)
1028{
1029 /*
1030 * Stall the request if the pipe is halted.
1031 */
1032 if (RT_UNLIKELY(pEp->fHalted))
1033 return usbMsdCompleteStall(pThis, NULL, pUrb, "Halted pipe");
1034
1035 /*
1036 * Deal with the URB according to the current state.
1037 */
1038 PUSBMSDREQ pReq = pThis->pReq;
1039 USBMSDREQSTATE enmState = pReq ? pReq->enmState : USBMSDREQSTATE_READY;
1040 switch (enmState)
1041 {
1042 case USBMSDREQSTATE_STATUS:
1043 LogFlow(("usbMsdHandleBulkHostToDev: Skipping pending status.\n"));
1044 pReq->enmState = USBMSDREQSTATE_READY;
1045 /* fall thru */
1046
1047 /*
1048 * We're ready to receive a command. Start off by validating the
1049 * incoming request.
1050 */
1051 case USBMSDREQSTATE_READY:
1052 {
1053 PCUSBCBW pCbw = (PUSBCBW)&pUrb->abData[0];
1054 if (pUrb->cbData < RT_UOFFSETOF(USBCBW, CBWCB[1]))
1055 {
1056 Log(("usbMsd: Bad CBW: cbData=%#x < min=%#x\n", pUrb->cbData, RT_UOFFSETOF(USBCBW, CBWCB[1]) ));
1057 return usbMsdCompleteStall(pThis, NULL, pUrb, "BAD CBW");
1058 }
1059 if (pCbw->dCBWSignature != USBCBW_SIGNATURE)
1060 {
1061 Log(("usbMsd: CBW: Invalid dCBWSignature value: %#x\n", pCbw->dCBWSignature));
1062 return usbMsdCompleteStall(pThis, NULL, pUrb, "Bad CBW");
1063 }
1064 Log(("usbMsd: CBW: dCBWTag=%#x dCBWDataTransferLength=%#x bmCBWFlags=%#x bCBWLun=%#x bCBWCBLength=%#x cbData=%#x fShortNotOk=%RTbool\n",
1065 pCbw->dCBWTag, pCbw->dCBWDataTransferLength, pCbw->bmCBWFlags, pCbw->bCBWLun, pCbw->bCBWCBLength, pUrb->cbData, pUrb->fShortNotOk));
1066 if (pCbw->bmCBWFlags & ~USBCBW_DIR_MASK)
1067 {
1068 Log(("usbMsd: CBW: Bad bmCBWFlags value: %#x\n", pCbw->bmCBWFlags));
1069 return usbMsdCompleteStall(pThis, NULL, pUrb, "Bad CBW");
1070
1071 }
1072 if (pCbw->bCBWLun != 0)
1073 {
1074 Log(("usbMsd: CBW: Bad bCBWLun value: %#x\n", pCbw->bCBWLun));
1075 return usbMsdCompleteStall(pThis, NULL, pUrb, "Bad CBW");
1076 }
1077 if (pCbw->bCBWCBLength == 0)
1078 {
1079 Log(("usbMsd: CBW: Bad bCBWCBLength value: %#x\n", pCbw->bCBWCBLength));
1080 return usbMsdCompleteStall(pThis, NULL, pUrb, "Bad CBW");
1081 }
1082 if (pUrb->cbData < RT_UOFFSETOF(USBCBW, CBWCB[pCbw->bCBWCBLength]))
1083 {
1084 Log(("usbMsd: CBW: Mismatching cbData and bCBWCBLength values: %#x vs. %#x (%#x)\n",
1085 pUrb->cbData, RT_UOFFSETOF(USBCBW, CBWCB[pCbw->bCBWCBLength]), pCbw->bCBWCBLength));
1086 return usbMsdCompleteStall(pThis, NULL, pUrb, "Bad CBW");
1087 }
1088 if (pCbw->dCBWDataTransferLength > _1M)
1089 {
1090 Log(("usbMsd: CBW: dCBWDataTransferLength is too large: %#x (%u)\n",
1091 pCbw->dCBWDataTransferLength, pCbw->dCBWDataTransferLength));
1092 return usbMsdCompleteStall(pThis, NULL, pUrb, "Too big transfer");
1093 }
1094
1095 if (!usbMsdIsValidCommand(pThis, pCbw, pUrb))
1096 return VINF_SUCCESS;
1097
1098 /*
1099 * Make sure we've got a request and a sufficient buffer space.
1100 *
1101 * Note! This will make sure the buffer is ZERO as well, thus
1102 * saving us the trouble of clearing the output buffer on
1103 * failure later.
1104 */
1105 if (!pReq)
1106 {
1107 pReq = usbMsdReqAlloc(pThis->pUsbIns);
1108 if (!pReq)
1109 return usbMsdCompleteStall(pThis, NULL, pUrb, "Request allocation failure");
1110 pThis->pReq = pReq;
1111 }
1112 if (!usbMsdReqEnsureBuffer(pReq, pCbw->dCBWDataTransferLength))
1113 return usbMsdCompleteStall(pThis, NULL, pUrb, "Buffer allocation failure");
1114
1115 /*
1116 * Special case REQUEST SENSE requests, usbMsdReqPrepare will
1117 * trash the sense data otherwise.
1118 */
1119 if (pCbw->CBWCB[0] == SCSI_REQUEST_SENSE)
1120 usbMsdHandleScsiReqestSense(pThis, pReq, pCbw);
1121 else
1122 {
1123 /*
1124 * Prepare the request. Kick it off right away if possible.
1125 */
1126 usbMsdReqPrepare(pReq, pCbw);
1127
1128 if ( pReq->Cbw.dCBWDataTransferLength == 0
1129 || (pReq->Cbw.bmCBWFlags & USBCBW_DIR_MASK) == USBCBW_DIR_IN)
1130 {
1131 int rc = usbMsdSubmitScsiCommand(pThis, pReq, "usbMsdHandleBulkHostToDev");
1132 if (RT_FAILURE(rc))
1133 {
1134 Log(("usbMsd: Failed sending SCSI request to driver: %Rrc\n", rc));
1135 return usbMsdCompleteStall(pThis, NULL, pUrb, "SCSI Submit #1");
1136 }
1137 }
1138 else
1139 {
1140 Log(("usbMsdHandleBulkHostToDev: Entering DATA_FROM_HOST.\n"));
1141 pReq->enmState = USBMSDREQSTATE_DATA_FROM_HOST;
1142 }
1143 }
1144
1145 return usbMsdCompleteOk(pThis, pUrb, 0);
1146 }
1147
1148 /*
1149 * Stuff the data into the buffer.
1150 */
1151 case USBMSDREQSTATE_DATA_FROM_HOST:
1152 {
1153 uint32_t cbData = pUrb->cbData;
1154 uint32_t cbLeft = pReq->Cbw.dCBWDataTransferLength - pReq->offBuf;
1155 if (cbData > cbLeft)
1156 {
1157 Log(("usbMsd: Too much data: cbData=%#x offBuf=%#x dCBWDataTransferLength=%#x cbLeft=%#x\n",
1158 cbData, pReq->offBuf, pReq->Cbw.dCBWDataTransferLength, cbLeft));
1159 return usbMsdCompleteStall(pThis, NULL, pUrb, "Too much data");
1160 }
1161 memcpy(&pReq->pbBuf[pReq->offBuf], &pUrb->abData[0], cbData);
1162 pReq->offBuf += cbData;
1163
1164 if (pReq->offBuf == pReq->Cbw.dCBWDataTransferLength)
1165 {
1166 int rc = usbMsdSubmitScsiCommand(pThis, pReq, "usbMsdHandleBulkHostToDev");
1167 if (RT_FAILURE(rc))
1168 {
1169 Log(("usbMsd: Failed sending SCSI request to driver: %Rrc\n", rc));
1170 return usbMsdCompleteStall(pThis, NULL, pUrb, "SCSI Submit #2");
1171 }
1172 }
1173 return usbMsdCompleteOk(pThis, pUrb, cbData);
1174 }
1175
1176 /*
1177 * Bad state, stall.
1178 */
1179 case USBMSDREQSTATE_DATA_TO_HOST:
1180 return usbMsdCompleteStall(pThis, NULL, pUrb, "Bad state H2D: DATA_TO_HOST");
1181
1182 case USBMSDREQSTATE_EXECUTING:
1183 return usbMsdCompleteStall(pThis, NULL, pUrb, "Bad state H2D: EXECUTING");
1184
1185 default:
1186 AssertMsgFailed(("enmState=%d\n", enmState));
1187 return usbMsdCompleteStall(pThis, NULL, pUrb, "Bad state (H2D)");
1188 }
1189}
1190
1191
1192/**
1193 * Handle requests sent to the inbound (to host) bulk pipe.
1194 */
1195static int usbMsdHandleBulkDevToHost(PUSBMSD pThis, PUSBMSDEP pEp, PVUSBURB pUrb)
1196{
1197 /*
1198 * Stall the request if the pipe is halted OR if there is no
1199 * pending request yet.
1200 */
1201 PUSBMSDREQ pReq = pThis->pReq;
1202 if (RT_UNLIKELY(pEp->fHalted || !pReq))
1203 return usbMsdCompleteStall(pThis, NULL, pUrb, pEp->fHalted ? "Halted pipe" : "No request");
1204
1205 /*
1206 * Deal with the URB according to the state.
1207 */
1208 switch (pReq->enmState)
1209 {
1210 /*
1211 * We've data left to transfer to the host.
1212 */
1213 case USBMSDREQSTATE_DATA_TO_HOST:
1214 {
1215 uint32_t cbData = pUrb->cbData;
1216 uint32_t cbCopy = pReq->Cbw.dCBWDataTransferLength - pReq->offBuf;
1217 if (cbData <= cbCopy)
1218 cbCopy = cbData;
1219 else if (pUrb->fShortNotOk)
1220 {
1221 Log(("usbMsd: Requested more data that we've got; cbData=%#x offBuf=%#x dCBWDataTransferLength=%#x cbLeft=%#x\n",
1222 cbData, pReq->offBuf, pReq->Cbw.dCBWDataTransferLength, cbCopy));
1223 return usbMsdCompleteStall(pThis, NULL, pUrb, "Data underrun");
1224 }
1225 memcpy(&pUrb->abData[0], &pReq->pbBuf[pReq->offBuf], cbCopy);
1226 pReq->offBuf += cbCopy;
1227
1228 if (pReq->offBuf == pReq->Cbw.dCBWDataTransferLength)
1229 {
1230 Log(("usbMsdHandleBulkDevToHost: Entering STATUS\n"));
1231 pReq->enmState = USBMSDREQSTATE_STATUS;
1232 }
1233 return usbMsdCompleteOk(pThis, pUrb, cbCopy);
1234 }
1235
1236 /*
1237 * Status transfer.
1238 */
1239 case USBMSDREQSTATE_STATUS:
1240 {
1241 if ((pUrb->cbData < sizeof(USBCSW)) || (pUrb->cbData > sizeof(USBCSW) && pUrb->fShortNotOk))
1242 {
1243 Log(("usbMsd: Unexpected status request size: %#x (expected %#x), fShortNotOK=%RTbool\n", pUrb->cbData, sizeof(USBCSW), pUrb->fShortNotOk));
1244 return usbMsdCompleteStall(pThis, NULL, pUrb, "Invalid CSW size");
1245 }
1246
1247 /* Enter a CSW into the URB data buffer. */
1248 PUSBCSW pCsw = (PUSBCSW)&pUrb->abData[0];
1249 pCsw->dCSWSignature = USBCSW_SIGNATURE;
1250 pCsw->dCSWTag = pReq->Cbw.dCBWTag;
1251 pCsw->bCSWStatus = pReq->iScsiReqStatus == SCSI_STATUS_OK
1252 ? USBCSW_STATUS_OK
1253 : pReq->iScsiReqStatus >= 0
1254 ? USBCSW_STATUS_FAILED
1255 : USBCSW_STATUS_PHASE_ERROR;
1256 /** @todo the following is not always accurate; VSCSI needs
1257 * to implement residual counts properly! */
1258 if ((pReq->Cbw.bmCBWFlags & USBCBW_DIR_MASK) == USBCBW_DIR_OUT)
1259 pCsw->dCSWDataResidue = pCsw->bCSWStatus == USBCSW_STATUS_OK
1260 ? pReq->Cbw.dCBWDataTransferLength - pReq->ScsiReq.cbScatterGather
1261 : pReq->Cbw.dCBWDataTransferLength;
1262 else
1263 pCsw->dCSWDataResidue = pCsw->bCSWStatus == USBCSW_STATUS_OK
1264 ? 0
1265 : pReq->ScsiReq.cbScatterGather;
1266 Log(("usbMsd: CSW: dCSWTag=%#x bCSWStatus=%d dCSWDataResidue=%#x\n",
1267 pCsw->dCSWTag, pCsw->bCSWStatus, pCsw->dCSWDataResidue));
1268
1269 Log(("usbMsdHandleBulkDevToHost: Entering READY\n"));
1270 pReq->enmState = USBMSDREQSTATE_READY;
1271 return usbMsdCompleteOk(pThis, pUrb, sizeof(*pCsw));
1272 }
1273
1274 /*
1275 * Status request before we've received all (or even any) data.
1276 * Linux 2.4.31 does this sometimes. The recommended behavior is to
1277 * to accept the current data amount and execute the request. (The
1278 * alternative behavior is to stall.)
1279 */
1280 case USBMSDREQSTATE_DATA_FROM_HOST:
1281 {
1282 if (pUrb->cbData != sizeof(USBCSW))
1283 {
1284 Log(("usbMsdHandleBulkDevToHost: DATA_FROM_HOST; cbData=%#x -> stall\n", pUrb->cbData));
1285 return usbMsdCompleteStall(pThis, NULL, pUrb, "Invalid CSW size");
1286 }
1287
1288 /* Adjust the request and kick it off. Special case the no-data
1289 case since the SCSI driver doesn't like that. */
1290 pReq->ScsiReq.cbScatterGather = pReq->offBuf;
1291 pReq->ScsiReqSeg.cbSeg = pReq->offBuf;
1292 if (!pReq->offBuf)
1293 {
1294 Log(("usbMsdHandleBulkDevToHost: Entering EXECUTING (offBuf=0x0).\n"));
1295 pReq->enmState = USBMSDREQSTATE_EXECUTING;
1296
1297 usbMsdQueueAddTail(&pThis->ToHostQueue, pUrb);
1298 LogFlow(("usbMsdHandleBulkDevToHost: Added %p:%s to the to-host queue\n", pUrb, pUrb->pszDesc));
1299
1300 usbMsdLun0ScsiRequestCompleted(&pThis->Lun0.IScsiPort, &pReq->ScsiReq, SCSI_STATUS_OK, false, VINF_SUCCESS);
1301 return VINF_SUCCESS;
1302 }
1303
1304 int rc = usbMsdSubmitScsiCommand(pThis, pReq, "usbMsdHandleBulkDevToHost");
1305 if (RT_FAILURE(rc))
1306 {
1307 Log(("usbMsd: Failed sending SCSI request to driver: %Rrc\n", rc));
1308 return usbMsdCompleteStall(pThis, NULL, pUrb, "SCSI Submit #3");
1309 }
1310
1311 /* fall thru */
1312 }
1313
1314 /*
1315 * The SCSI command is still pending, queue the URB awaiting its
1316 * completion.
1317 */
1318 case USBMSDREQSTATE_EXECUTING:
1319 usbMsdQueueAddTail(&pThis->ToHostQueue, pUrb);
1320 LogFlow(("usbMsdHandleBulkDevToHost: Added %p:%s to the to-host queue\n", pUrb, pUrb->pszDesc));
1321 return VINF_SUCCESS;
1322
1323 /*
1324 * Bad states, stall.
1325 */
1326 case USBMSDREQSTATE_READY:
1327 Log(("usbMsdHandleBulkDevToHost: enmState=READ (cbData=%#x)\n", pReq->enmState, pUrb->cbData));
1328 return usbMsdCompleteStall(pThis, NULL, pUrb, "Bad state D2H: READY");
1329
1330 default:
1331 Log(("usbMsdHandleBulkDevToHost: enmState=%d cbData=%#x\n", pReq->enmState, pUrb->cbData));
1332 return usbMsdCompleteStall(pThis, NULL, pUrb, "Really bad state (D2H)!");
1333 }
1334}
1335
1336
1337/**
1338 * Handles request send to the default control pipe.
1339 */
1340static int usbMsdHandleDefaultPipe(PUSBMSD pThis, PUSBMSDEP pEp, PVUSBURB pUrb)
1341{
1342 PVUSBSETUP pSetup = (PVUSBSETUP)&pUrb->abData[0];
1343 AssertReturn(pUrb->cbData >= sizeof(*pSetup), VERR_VUSB_FAILED_TO_QUEUE_URB);
1344
1345 if ((pSetup->bmRequestType & VUSB_REQ_MASK) == VUSB_REQ_STANDARD)
1346 {
1347 switch (pSetup->bRequest)
1348 {
1349 case VUSB_REQ_GET_DESCRIPTOR:
1350 {
1351 if (pSetup->bmRequestType != (VUSB_TO_DEVICE | VUSB_REQ_STANDARD | VUSB_DIR_TO_HOST))
1352 {
1353 Log(("usbMsd: Bad GET_DESCRIPTOR req: bmRequestType=%#x\n", pSetup->bmRequestType));
1354 return usbMsdCompleteStall(pThis, pEp, pUrb, "Bad GET_DESCRIPTOR");
1355 }
1356
1357 switch (pSetup->wValue >> 8)
1358 {
1359 uint32_t cbCopy;
1360
1361 case VUSB_DT_STRING:
1362 Log(("usbMsd: GET_DESCRIPTOR DT_STRING wValue=%#x wIndex=%#x\n", pSetup->wValue, pSetup->wIndex));
1363 break;
1364 case VUSB_DT_DEVICE_QUALIFIER:
1365 Log(("usbMsd: GET_DESCRIPTOR DT_DEVICE_QUALIFIER wValue=%#x wIndex=%#x\n", pSetup->wValue, pSetup->wIndex));
1366 /* Returned data is written after the setup message. */
1367 cbCopy = pUrb->cbData - sizeof(*pSetup);
1368 cbCopy = RT_MIN(cbCopy, sizeof(g_UsbMsdDeviceQualifier));
1369 memcpy(&pUrb->abData[sizeof(*pSetup)], &g_UsbMsdDeviceQualifier, cbCopy);
1370 return usbMsdCompleteOk(pThis, pUrb, cbCopy + sizeof(*pSetup));
1371 default:
1372 Log(("usbMsd: GET_DESCRIPTOR, huh? wValue=%#x wIndex=%#x\n", pSetup->wValue, pSetup->wIndex));
1373 break;
1374 }
1375 break;
1376 }
1377
1378 case VUSB_REQ_CLEAR_FEATURE:
1379 break;
1380 }
1381
1382 /** @todo implement this. */
1383 Log(("usbMsd: Implement standard request: bmRequestType=%#x bRequest=%#x wValue=%#x wIndex=%#x wLength=%#x\n",
1384 pSetup->bmRequestType, pSetup->bRequest, pSetup->wValue, pSetup->wIndex, pSetup->wLength));
1385
1386 usbMsdCompleteStall(pThis, pEp, pUrb, "TODO: standard request stuff");
1387 }
1388 /* 3.1 Bulk-Only Mass Storage Reset */
1389 else if ( pSetup->bmRequestType == (VUSB_REQ_CLASS | VUSB_TO_INTERFACE)
1390 && pSetup->bRequest == 0xff
1391 && !pSetup->wValue
1392 && !pSetup->wLength
1393 && pSetup->wIndex == 0)
1394 {
1395 Log(("usbMsdHandleDefaultPipe: Bulk-Only Mass Storage Reset\n"));
1396 return usbMsdResetWorker(pThis, pUrb, false /*fSetConfig*/);
1397 }
1398 /* 3.2 Get Max LUN, may stall if we like (but we don't). */
1399 else if ( pSetup->bmRequestType == (VUSB_REQ_CLASS | VUSB_TO_INTERFACE | VUSB_DIR_TO_HOST)
1400 && pSetup->bRequest == 0xfe
1401 && !pSetup->wValue
1402 && pSetup->wLength == 1
1403 && pSetup->wIndex == 0)
1404 {
1405 *(uint8_t *)(pSetup + 1) = 0; /* max lun is 0 */
1406 usbMsdCompleteOk(pThis, pUrb, 1);
1407 }
1408 else
1409 {
1410 Log(("usbMsd: Unknown control msg: bmRequestType=%#x bRequest=%#x wValue=%#x wIndex=%#x wLength=%#x\n",
1411 pSetup->bmRequestType, pSetup->bRequest, pSetup->wValue, pSetup->wIndex, pSetup->wLength));
1412 return usbMsdCompleteStall(pThis, pEp, pUrb, "Unknown control msg");
1413 }
1414
1415 return VINF_SUCCESS;
1416}
1417
1418
1419/**
1420 * @copydoc PDMUSBREG::pfnQueue
1421 */
1422static DECLCALLBACK(int) usbMsdQueue(PPDMUSBINS pUsbIns, PVUSBURB pUrb)
1423{
1424 PUSBMSD pThis = PDMINS_2_DATA(pUsbIns, PUSBMSD);
1425 LogFlow(("usbMsdQueue/#%u: pUrb=%p:%s EndPt=%#x\n", pUsbIns->iInstance, pUrb, pUrb->pszDesc, pUrb->EndPt));
1426 RTCritSectEnter(&pThis->CritSect);
1427
1428 /*
1429 * Parse on a per end-point basis.
1430 */
1431 int rc;
1432 switch (pUrb->EndPt)
1433 {
1434 case 0:
1435 rc = usbMsdHandleDefaultPipe(pThis, &pThis->aEps[0], pUrb);
1436 break;
1437
1438 case 0x81:
1439 AssertFailed();
1440 case 0x01:
1441 rc = usbMsdHandleBulkDevToHost(pThis, &pThis->aEps[1], pUrb);
1442 break;
1443
1444 case 0x02:
1445 rc = usbMsdHandleBulkHostToDev(pThis, &pThis->aEps[2], pUrb);
1446 break;
1447
1448 default:
1449 AssertMsgFailed(("EndPt=%d\n", pUrb->EndPt));
1450 rc = VERR_VUSB_FAILED_TO_QUEUE_URB;
1451 break;
1452 }
1453
1454 RTCritSectLeave(&pThis->CritSect);
1455 return rc;
1456}
1457
1458
1459/**
1460 * @copydoc PDMUSBREG::pfnUsbClearHaltedEndpoint
1461 */
1462static DECLCALLBACK(int) usbMsdUsbClearHaltedEndpoint(PPDMUSBINS pUsbIns, unsigned uEndpoint)
1463{
1464 PUSBMSD pThis = PDMINS_2_DATA(pUsbIns, PUSBMSD);
1465 LogFlow(("usbMsdUsbClearHaltedEndpoint/#%u: uEndpoint=%#x\n", pUsbIns->iInstance, uEndpoint));
1466
1467 if ((uEndpoint & ~0x80) < RT_ELEMENTS(pThis->aEps))
1468 {
1469 RTCritSectEnter(&pThis->CritSect);
1470 pThis->aEps[(uEndpoint & ~0x80)].fHalted = false;
1471 RTCritSectLeave(&pThis->CritSect);
1472 }
1473
1474 return VINF_SUCCESS;
1475}
1476
1477
1478/**
1479 * @copydoc PDMUSBREG::pfnUsbSetInterface
1480 */
1481static DECLCALLBACK(int) usbMsdUsbSetInterface(PPDMUSBINS pUsbIns, uint8_t bInterfaceNumber, uint8_t bAlternateSetting)
1482{
1483 LogFlow(("usbMsdUsbSetInterface/#%u: bInterfaceNumber=%u bAlternateSetting=%u\n", pUsbIns->iInstance, bInterfaceNumber, bAlternateSetting));
1484 Assert(bAlternateSetting == 0);
1485 return VINF_SUCCESS;
1486}
1487
1488
1489/**
1490 * @copydoc PDMUSBREG::pfnUsbSetConfiguration
1491 */
1492static DECLCALLBACK(int) usbMsdUsbSetConfiguration(PPDMUSBINS pUsbIns, uint8_t bConfigurationValue,
1493 const void *pvOldCfgDesc, const void *pvOldIfState, const void *pvNewCfgDesc)
1494{
1495 PUSBMSD pThis = PDMINS_2_DATA(pUsbIns, PUSBMSD);
1496 LogFlow(("usbMsdUsbSetConfiguration/#%u: bConfigurationValue=%u\n", pUsbIns->iInstance, bConfigurationValue));
1497 Assert(bConfigurationValue == 1);
1498 RTCritSectEnter(&pThis->CritSect);
1499
1500 /*
1501 * If the same config is applied more than once, it's a kind of reset.
1502 */
1503 if (pThis->bConfigurationValue == bConfigurationValue)
1504 usbMsdResetWorker(pThis, NULL, true /*fSetConfig*/); /** @todo figure out the exact difference */
1505 pThis->bConfigurationValue = bConfigurationValue;
1506
1507 RTCritSectLeave(&pThis->CritSect);
1508 return VINF_SUCCESS;
1509}
1510
1511
1512/**
1513 * @copydoc PDMUSBREG::pfnUsbGetDescriptorCache
1514 */
1515static DECLCALLBACK(PCPDMUSBDESCCACHE) usbMsdUsbGetDescriptorCache(PPDMUSBINS pUsbIns)
1516{
1517 PUSBMSD pThis = PDMINS_2_DATA(pUsbIns, PUSBMSD);
1518 LogFlow(("usbMsdUsbGetDescriptorCache/#%u:\n", pUsbIns->iInstance));
1519 return &g_UsbMsdDescCache;
1520}
1521
1522
1523/**
1524 * @copydoc PDMUSBREG::pfnUsbReset
1525 */
1526static DECLCALLBACK(int) usbMsdUsbReset(PPDMUSBINS pUsbIns, bool fResetOnLinux)
1527{
1528 PUSBMSD pThis = PDMINS_2_DATA(pUsbIns, PUSBMSD);
1529 LogFlow(("usbMsdUsbReset/#%u:\n", pUsbIns->iInstance));
1530 RTCritSectEnter(&pThis->CritSect);
1531
1532 int rc = usbMsdResetWorker(pThis, NULL, false /*fSetConfig*/);
1533
1534 RTCritSectLeave(&pThis->CritSect);
1535 return rc;
1536}
1537
1538
1539/**
1540 * @copydoc PDMUSBREG::pfnDestruct
1541 */
1542static void usbMsdDestruct(PPDMUSBINS pUsbIns)
1543{
1544 PUSBMSD pThis = PDMINS_2_DATA(pUsbIns, PUSBMSD);
1545 LogFlow(("usbMsdDestruct/#%u:\n", pUsbIns->iInstance));
1546
1547 if (RTCritSectIsInitialized(&pThis->CritSect))
1548 {
1549 RTCritSectEnter(&pThis->CritSect);
1550 RTCritSectLeave(&pThis->CritSect);
1551 RTCritSectDelete(&pThis->CritSect);
1552 }
1553
1554 if (pThis->pReq)
1555 {
1556 usbMsdReqFree(pThis->pReq);
1557 pThis->pReq = NULL;
1558 }
1559
1560 if (pThis->hEvtDoneQueue != NIL_RTSEMEVENT)
1561 {
1562 RTSemEventDestroy(pThis->hEvtDoneQueue);
1563 pThis->hEvtDoneQueue = NIL_RTSEMEVENT;
1564 }
1565
1566 if (pThis->hEvtReset != NIL_RTSEMEVENTMULTI)
1567 {
1568 RTSemEventMultiDestroy(pThis->hEvtReset);
1569 pThis->hEvtReset = NIL_RTSEMEVENTMULTI;
1570 }
1571}
1572
1573
1574/**
1575 * @copydoc PDMUSBREG::pfnConstruct
1576 */
1577static DECLCALLBACK(int) usbMsdConstruct(PPDMUSBINS pUsbIns, int iInstance, PCFGMNODE pCfg, PCFGMNODE pCfgGlobal)
1578{
1579 PUSBMSD pThis = PDMINS_2_DATA(pUsbIns, PUSBMSD);
1580 Log(("usbMsdConstruct/#%u:\n", iInstance));
1581
1582 /*
1583 * Perform the basic structure initialization first so the destructor
1584 * will not misbehave.
1585 */
1586 pThis->pUsbIns = pUsbIns;
1587 pThis->hEvtDoneQueue = NIL_RTSEMEVENT;
1588 pThis->hEvtReset = NIL_RTSEMEVENTMULTI;
1589 pThis->Lun0.IBase.pfnQueryInterface = usbMsdLun0QueryInterface;
1590 pThis->Lun0.IScsiPort.pfnSCSIRequestCompleted = usbMsdLun0ScsiRequestCompleted;
1591 usbMsdQueueInit(&pThis->ToHostQueue);
1592 usbMsdQueueInit(&pThis->DoneQueue);
1593
1594 int rc = RTCritSectInit(&pThis->CritSect);
1595 AssertRCReturn(rc, rc);
1596
1597 rc = RTSemEventCreate(&pThis->hEvtDoneQueue);
1598 AssertRCReturn(rc, rc);
1599
1600 rc = RTSemEventMultiCreate(&pThis->hEvtReset);
1601 AssertRCReturn(rc, rc);
1602
1603 /*
1604 * Validate and read the configuration.
1605 */
1606 rc = CFGMR3ValidateConfig(pCfg, "/", "", "", "UsbMsd", iInstance);
1607 if (RT_FAILURE(rc))
1608 return rc;
1609
1610 /*
1611 * Attach the SCSI driver.
1612 */
1613 rc = PDMUsbHlpDriverAttach(pUsbIns, 0 /*iLun*/, &pThis->Lun0.IBase, &pThis->Lun0.pIBase, "SCSI Port");
1614 if (RT_FAILURE(rc))
1615 return PDMUsbHlpVMSetError(pUsbIns, rc, RT_SRC_POS, N_("MSD failed to attach SCSI driver"));
1616 pThis->Lun0.pIScsiConnector = PDMIBASE_QUERY_INTERFACE(pThis->Lun0.pIBase, PDMISCSICONNECTOR);
1617 if (!pThis->Lun0.pIScsiConnector)
1618 return PDMUsbHlpVMSetError(pUsbIns, VERR_PDM_MISSING_INTERFACE_BELOW, RT_SRC_POS,
1619 N_("MSD failed to query the PDMISCSICONNECTOR from the driver below it"));
1620
1621 return VINF_SUCCESS;
1622}
1623
1624
1625/**
1626 * The USB Mass Storage Device (MSD) registration record.
1627 */
1628const PDMUSBREG g_UsbMsd =
1629{
1630 /* u32Version */
1631 PDM_USBREG_VERSION,
1632 /* szName */
1633 "Msd",
1634 /* pszDescription */
1635 "USB Mass Storage Device, one LUN.",
1636 /* fFlags */
1637 0,
1638 /* cMaxInstances */
1639 ~0U,
1640 /* cbInstance */
1641 sizeof(USBMSD),
1642 /* pfnConstruct */
1643 usbMsdConstruct,
1644 /* pfnDestruct */
1645 usbMsdDestruct,
1646 /* pfnVMInitComplete */
1647 NULL,
1648 /* pfnVMPowerOn */
1649 NULL,
1650 /* pfnVMReset */
1651 NULL,
1652 /* pfnVMSuspend */
1653 NULL,
1654 /* pfnVMResume */
1655 NULL,
1656 /* pfnVMPowerOff */
1657 NULL,
1658 /* pfnHotPlugged */
1659 NULL,
1660 /* pfnHotUnplugged */
1661 NULL,
1662 /* pfnDriverAttach */
1663 NULL,
1664 /* pfnDriverDetach */
1665 NULL,
1666 /* pfnQueryInterface */
1667 NULL,
1668 /* pfnUsbReset */
1669 usbMsdUsbReset,
1670 /* pfnUsbGetCachedDescriptors */
1671 usbMsdUsbGetDescriptorCache,
1672 /* pfnUsbSetConfiguration */
1673 usbMsdUsbSetConfiguration,
1674 /* pfnUsbSetInterface */
1675 usbMsdUsbSetInterface,
1676 /* pfnUsbClearHaltedEndpoint */
1677 usbMsdUsbClearHaltedEndpoint,
1678 /* pfnUrbNew */
1679 NULL/*usbMsdUrbNew*/,
1680 /* pfnQueue */
1681 usbMsdQueue,
1682 /* pfnUrbCancel */
1683 usbMsdUrbCancel,
1684 /* pfnUrbReap */
1685 usbMsdUrbReap,
1686 /* u32TheEnd */
1687 PDM_USBREG_VERSION
1688};
1689
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