VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/DrvSCSI.cpp@ 64028

Last change on this file since 64028 was 64028, checked in by vboxsync, 8 years ago

SCSI: Same workaround as for AHCI

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.4 KB
Line 
1/* $Id: DrvSCSI.cpp 64028 2016-09-26 20:04:07Z vboxsync $ */
2/** @file
3 * VBox storage drivers: Generic SCSI command parser and execution driver
4 */
5
6/*
7 * Copyright (C) 2006-2016 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 DEBUG
23#define LOG_GROUP LOG_GROUP_DRV_SCSI
24#include <VBox/vmm/pdmdrv.h>
25#include <VBox/vmm/pdmifs.h>
26#include <VBox/vmm/pdmstorageifs.h>
27#include <VBox/vmm/pdmthread.h>
28#include <VBox/vscsi.h>
29#include <VBox/scsi.h>
30#include <iprt/asm.h>
31#include <iprt/assert.h>
32#include <iprt/mem.h>
33#include <iprt/req.h>
34#include <iprt/semaphore.h>
35#include <iprt/string.h>
36#include <iprt/uuid.h>
37
38#include "VBoxDD.h"
39
40/** The maximum number of release log entries per device. */
41#define MAX_LOG_REL_ERRORS 1024
42
43/**
44 * SCSI driver instance data.
45 *
46 * @implements PDMISCSICONNECTOR
47 * @implements PDMIMEDIAEXPORT
48 * @implements PDMIMOUNTNOTIFY
49 */
50typedef struct DRVSCSI
51{
52 /** Pointer driver instance. */
53 PPDMDRVINS pDrvIns;
54
55 /** Pointer to the attached driver's base interface. */
56 PPDMIBASE pDrvBase;
57 /** Pointer to the attached driver's block interface. */
58 PPDMIMEDIA pDrvMedia;
59 /** Pointer to the attached driver's extended media interface. */
60 PPDMIMEDIAEX pDrvMediaEx;
61 /** Pointer to the attached driver's mount interface. */
62 PPDMIMOUNT pDrvMount;
63 /** Pointer to the SCSI port interface of the device above. */
64 PPDMISCSIPORT pDevScsiPort;
65 /** pointer to the Led port interface of the dveice above. */
66 PPDMILEDPORTS pLedPort;
67 /** The scsi connector interface .*/
68 PDMISCSICONNECTOR ISCSIConnector;
69 /** The media port interface. */
70 PDMIMEDIAPORT IPort;
71 /** The optional extended media port interface. */
72 PDMIMEDIAEXPORT IPortEx;
73 /** The mount notify interface. */
74 PDMIMOUNTNOTIFY IMountNotify;
75 /** Fallback status LED state for this drive.
76 * This is used in case the device doesn't has a LED interface. */
77 PDMLED Led;
78 /** Pointer to the status LED for this drive. */
79 PPDMLED pLed;
80
81 /** VSCSI device handle. */
82 VSCSIDEVICE hVScsiDevice;
83 /** VSCSI LUN handle. */
84 VSCSILUN hVScsiLun;
85 /** I/O callbacks. */
86 VSCSILUNIOCALLBACKS VScsiIoCallbacks;
87
88 /** Indicates whether PDMDrvHlpAsyncNotificationCompleted should be called by
89 * any of the dummy functions. */
90 bool volatile fDummySignal;
91 /** Release statistics: number of bytes written. */
92 STAMCOUNTER StatBytesWritten;
93 /** Release statistics: number of bytes read. */
94 STAMCOUNTER StatBytesRead;
95 /** Release statistics: Current I/O depth. */
96 volatile uint32_t StatIoDepth;
97 /** Errors printed in the release log. */
98 unsigned cErrors;
99 /** Mark the drive as having a non-rotational medium (i.e. as a SSD). */
100 bool fNonRotational;
101 /** Medium is readonly */
102 bool fReadonly;
103} DRVSCSI, *PDRVSCSI;
104
105/** Convert a VSCSI I/O request handle to the associated PDMIMEDIAEX I/O request. */
106#define DRVSCSI_VSCSIIOREQ_2_PDMMEDIAEXIOREQ(a_hVScsiIoReq) (*(PPDMMEDIAEXIOREQ)((uint8_t *)(a_hVScsiIoReq) - sizeof(PDMMEDIAEXIOREQ)))
107/** Convert a PDMIMEDIAEX I/O additional request memory to a VSCSI I/O request. */
108#define DRVSCSI_PDMMEDIAEXIOREQ_2_VSCSIIOREQ(a_pvIoReqAlloc) ((VSCSIIOREQ)((uint8_t *)(a_pvIoReqAlloc) + sizeof(PDMMEDIAEXIOREQ)))
109
110/**
111 * Returns whether the given status code indicates a non fatal error.
112 *
113 * @returns True if the error can be fixed by the user after the VM was suspended.
114 * False if not and the error should be reported to the guest.
115 * @param rc The status code to check.
116 */
117DECLINLINE(bool) drvscsiIsRedoPossible(int rc)
118{
119 if ( rc == VERR_DISK_FULL
120 || rc == VERR_FILE_TOO_BIG
121 || rc == VERR_BROKEN_PIPE
122 || rc == VERR_NET_CONNECTION_REFUSED
123 || rc == VERR_VD_DEK_MISSING)
124 return true;
125
126 return false;
127}
128
129/* -=-=-=-=- VScsiIoCallbacks -=-=-=-=- */
130
131/**
132 * @interface_method_impl{VSCSILUNIOCALLBACKS,pfnVScsiLunReqAllocSizeSet}
133 */
134static DECLCALLBACK(int) drvscsiReqAllocSizeSet(VSCSILUN hVScsiLun, void *pvScsiLunUser, size_t cbVScsiIoReqAlloc)
135{
136 RT_NOREF(hVScsiLun);
137 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
138
139 /* We need to store the I/O request handle so we can get it when VSCSI queues an I/O request. */
140 return pThis->pDrvMediaEx->pfnIoReqAllocSizeSet(pThis->pDrvMediaEx, cbVScsiIoReqAlloc + sizeof(PDMMEDIAEXIOREQ));
141}
142
143/**
144 * @interface_method_impl{VSCSILUNIOCALLBACKS,pfnVScsiLunReqAlloc}
145 */
146static DECLCALLBACK(int) drvscsiReqAlloc(VSCSILUN hVScsiLun, void *pvScsiLunUser,
147 uint64_t u64Tag, PVSCSIIOREQ phVScsiIoReq)
148{
149 RT_NOREF(hVScsiLun);
150 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
151 PDMMEDIAEXIOREQ hIoReq;
152 void *pvIoReqAlloc;
153 int rc = pThis->pDrvMediaEx->pfnIoReqAlloc(pThis->pDrvMediaEx, &hIoReq, &pvIoReqAlloc, u64Tag,
154 PDMIMEDIAEX_F_SYNC);
155 if (RT_SUCCESS(rc))
156 {
157 PPDMMEDIAEXIOREQ phIoReq = (PPDMMEDIAEXIOREQ)pvIoReqAlloc;
158
159 *phIoReq = hIoReq;
160 *phVScsiIoReq = (VSCSIIOREQ)(phIoReq + 1);
161 }
162
163 return rc;
164}
165
166/**
167 * @interface_method_impl{VSCSILUNIOCALLBACKS,pfnVScsiLunReqFree}
168 */
169static DECLCALLBACK(int) drvscsiReqFree(VSCSILUN hVScsiLun, void *pvScsiLunUser, VSCSIIOREQ hVScsiIoReq)
170{
171 RT_NOREF(hVScsiLun);
172 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
173 PDMMEDIAEXIOREQ hIoReq = DRVSCSI_VSCSIIOREQ_2_PDMMEDIAEXIOREQ(hVScsiIoReq);
174
175 return pThis->pDrvMediaEx->pfnIoReqFree(pThis->pDrvMediaEx, hIoReq);
176}
177
178/**
179 * @interface_method_impl{VSCSILUNIOCALLBACKS,pfnVScsiLunMediumGetSize}
180 */
181static DECLCALLBACK(int) drvscsiGetSize(VSCSILUN hVScsiLun, void *pvScsiLunUser, uint64_t *pcbSize)
182{
183 RT_NOREF(hVScsiLun);
184 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
185
186 *pcbSize = pThis->pDrvMedia->pfnGetSize(pThis->pDrvMedia);
187
188 return VINF_SUCCESS;
189}
190
191/**
192 * @interface_method_impl{VSCSILUNIOCALLBACKS,pfnVScsiLunMediumGetSectorSize}
193 */
194static DECLCALLBACK(int) drvscsiGetSectorSize(VSCSILUN hVScsiLun, void *pvScsiLunUser, uint32_t *pcbSectorSize)
195{
196 RT_NOREF(hVScsiLun);
197 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
198
199 *pcbSectorSize = pThis->pDrvMedia->pfnGetSectorSize(pThis->pDrvMedia);
200
201 return VINF_SUCCESS;
202}
203
204/**
205 * @interface_method_impl{VSCSILUNIOCALLBACKS,pfnVScsiLunMediumSetLock}
206 */
207static DECLCALLBACK(int) drvscsiSetLock(VSCSILUN hVScsiLun, void *pvScsiLunUser, bool fLocked)
208{
209 RT_NOREF(hVScsiLun);
210 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
211
212 if (fLocked)
213 pThis->pDrvMount->pfnLock(pThis->pDrvMount);
214 else
215 pThis->pDrvMount->pfnUnlock(pThis->pDrvMount);
216
217 return VINF_SUCCESS;
218}
219
220/**
221 * @interface_method_impl{VSCSILUNIOCALLBACKS,pfnVScsiLunReqTransferEnqueue}
222 */
223static DECLCALLBACK(int) drvscsiReqTransferEnqueue(VSCSILUN hVScsiLun, void *pvScsiLunUser, VSCSIIOREQ hVScsiIoReq)
224{
225 RT_NOREF(hVScsiLun);
226 int rc = VINF_SUCCESS;
227 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
228 PDMMEDIAEXIOREQ hIoReq = DRVSCSI_VSCSIIOREQ_2_PDMMEDIAEXIOREQ(hVScsiIoReq);
229
230 LogFlowFunc(("Enqueuing hVScsiIoReq=%#p\n", hVScsiIoReq));
231
232 VSCSIIOREQTXDIR enmTxDir = VSCSIIoReqTxDirGet(hVScsiIoReq);
233 switch (enmTxDir)
234 {
235 case VSCSIIOREQTXDIR_FLUSH:
236 {
237 rc = pThis->pDrvMediaEx->pfnIoReqFlush(pThis->pDrvMediaEx, hIoReq);
238 if ( RT_FAILURE(rc)
239 && pThis->cErrors++ < MAX_LOG_REL_ERRORS)
240 LogRel(("SCSI#%u: Flush returned rc=%Rrc\n",
241 pThis->pDrvIns->iInstance, rc));
242 break;
243 }
244 case VSCSIIOREQTXDIR_UNMAP:
245 {
246 PCRTRANGE paRanges;
247 unsigned cRanges;
248
249 rc = VSCSIIoReqUnmapParamsGet(hVScsiIoReq, &paRanges, &cRanges);
250 AssertRC(rc);
251
252 pThis->pLed->Asserted.s.fWriting = pThis->pLed->Actual.s.fWriting = 1;
253 rc = pThis->pDrvMediaEx->pfnIoReqDiscard(pThis->pDrvMediaEx, hIoReq, cRanges);
254 if ( RT_FAILURE(rc)
255 && pThis->cErrors++ < MAX_LOG_REL_ERRORS)
256 LogRel(("SCSI#%u: Discard returned rc=%Rrc\n",
257 pThis->pDrvIns->iInstance, rc));
258 break;
259 }
260 case VSCSIIOREQTXDIR_READ:
261 case VSCSIIOREQTXDIR_WRITE:
262 {
263 uint64_t uOffset = 0;
264 size_t cbTransfer = 0;
265 size_t cbSeg = 0;
266 PCRTSGSEG paSeg = NULL;
267 unsigned cSeg = 0;
268
269 rc = VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer,
270 &cSeg, &cbSeg, &paSeg);
271 AssertRC(rc);
272
273 if (enmTxDir == VSCSIIOREQTXDIR_READ)
274 {
275 pThis->pLed->Asserted.s.fReading = pThis->pLed->Actual.s.fReading = 1;
276 rc = pThis->pDrvMediaEx->pfnIoReqRead(pThis->pDrvMediaEx, hIoReq, uOffset, cbTransfer);
277 STAM_REL_COUNTER_ADD(&pThis->StatBytesRead, cbTransfer);
278 }
279 else
280 {
281 pThis->pLed->Asserted.s.fWriting = pThis->pLed->Actual.s.fWriting = 1;
282 rc = pThis->pDrvMediaEx->pfnIoReqWrite(pThis->pDrvMediaEx, hIoReq, uOffset, cbTransfer);
283 STAM_REL_COUNTER_ADD(&pThis->StatBytesWritten, cbTransfer);
284 }
285
286 if ( RT_FAILURE(rc)
287 && pThis->cErrors++ < MAX_LOG_REL_ERRORS)
288 LogRel(("SCSI#%u: %s at offset %llu (%u bytes left) returned rc=%Rrc\n",
289 pThis->pDrvIns->iInstance,
290 enmTxDir == VSCSIIOREQTXDIR_READ
291 ? "Read"
292 : "Write",
293 uOffset,
294 cbTransfer, rc));
295 break;
296 }
297 default:
298 AssertMsgFailed(("Invalid transfer direction %u\n", enmTxDir));
299 }
300
301 if (rc == VINF_SUCCESS)
302 {
303 if (enmTxDir == VSCSIIOREQTXDIR_READ)
304 pThis->pLed->Actual.s.fReading = 0;
305 else if (enmTxDir == VSCSIIOREQTXDIR_WRITE)
306 pThis->pLed->Actual.s.fWriting = 0;
307 else
308 AssertMsg(enmTxDir == VSCSIIOREQTXDIR_FLUSH, ("Invalid transfer direction %u\n", enmTxDir));
309
310 VSCSIIoReqCompleted(hVScsiIoReq, VINF_SUCCESS, false);
311 rc = VINF_SUCCESS;
312 }
313 else if (rc == VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS)
314 rc = VINF_SUCCESS;
315 else if (RT_FAILURE(rc))
316 {
317 if (enmTxDir == VSCSIIOREQTXDIR_READ)
318 pThis->pLed->Actual.s.fReading = 0;
319 else if (enmTxDir == VSCSIIOREQTXDIR_WRITE)
320 pThis->pLed->Actual.s.fWriting = 0;
321 else
322 AssertMsg(enmTxDir == VSCSIIOREQTXDIR_FLUSH, ("Invalid transfer direction %u\n", enmTxDir));
323
324 VSCSIIoReqCompleted(hVScsiIoReq, rc, drvscsiIsRedoPossible(rc));
325 rc = VINF_SUCCESS;
326 }
327 else
328 AssertMsgFailed(("Invalid return code rc=%Rrc\n", rc));
329
330 return rc;
331}
332
333/**
334 * @interface_method_impl{VSCSILUNIOCALLBACKS,pfnVScsiLunGetFeatureFlags}
335 */
336static DECLCALLBACK(int) drvscsiGetFeatureFlags(VSCSILUN hVScsiLun, void *pvScsiLunUser, uint64_t *pfFeatures)
337{
338 RT_NOREF(hVScsiLun);
339 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
340
341 *pfFeatures = 0;
342
343 uint32_t fFeatures = 0;
344 int rc = pThis->pDrvMediaEx->pfnQueryFeatures(pThis->pDrvMediaEx, &fFeatures);
345 if (RT_SUCCESS(rc) && (fFeatures & PDMIMEDIAEX_FEATURE_F_DISCARD))
346 *pfFeatures |= VSCSI_LUN_FEATURE_UNMAP;
347
348 if (pThis->fNonRotational)
349 *pfFeatures |= VSCSI_LUN_FEATURE_NON_ROTATIONAL;
350
351 if (pThis->fReadonly)
352 *pfFeatures |= VSCSI_LUN_FEATURE_READONLY;
353
354 return VINF_SUCCESS;
355}
356
357
358/* -=-=-=-=- IPortEx -=-=-=-=- */
359
360/**
361 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCompleteNotify}
362 */
363static DECLCALLBACK(int) drvscsiIoReqCompleteNotify(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
364 void *pvIoReqAlloc, int rcReq)
365{
366 RT_NOREF1(hIoReq);
367
368 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IPortEx);
369 VSCSIIOREQ hVScsiIoReq = (VSCSIIOREQ)((uint8_t *)pvIoReqAlloc + sizeof(PDMMEDIAEXIOREQ));
370 VSCSIIOREQTXDIR enmTxDir = VSCSIIoReqTxDirGet(hVScsiIoReq);
371
372 LogFlowFunc(("Request hVScsiIoReq=%#p completed\n", hVScsiIoReq));
373
374 if (enmTxDir == VSCSIIOREQTXDIR_READ)
375 pThis->pLed->Actual.s.fReading = 0;
376 else if ( enmTxDir == VSCSIIOREQTXDIR_WRITE
377 || enmTxDir == VSCSIIOREQTXDIR_UNMAP)
378 pThis->pLed->Actual.s.fWriting = 0;
379 else
380 AssertMsg(enmTxDir == VSCSIIOREQTXDIR_FLUSH, ("Invalid transfer direction %u\n", enmTxDir));
381
382 if (RT_SUCCESS(rcReq))
383 VSCSIIoReqCompleted(hVScsiIoReq, rcReq, false /* fRedoPossible */);
384 else
385 {
386 pThis->cErrors++;
387 if (pThis->cErrors < MAX_LOG_REL_ERRORS)
388 {
389 if (enmTxDir == VSCSIIOREQTXDIR_FLUSH)
390 LogRel(("SCSI#%u: Flush returned rc=%Rrc\n",
391 pThis->pDrvIns->iInstance, rcReq));
392 else if (enmTxDir == VSCSIIOREQTXDIR_UNMAP)
393 LogRel(("SCSI#%u: Unmap returned rc=%Rrc\n",
394 pThis->pDrvIns->iInstance, rcReq));
395 else
396 {
397 uint64_t uOffset = 0;
398 size_t cbTransfer = 0;
399 size_t cbSeg = 0;
400 PCRTSGSEG paSeg = NULL;
401 unsigned cSeg = 0;
402
403 VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer,
404 &cSeg, &cbSeg, &paSeg);
405
406 LogRel(("SCSI#%u: %s at offset %llu (%u bytes left) returned rc=%Rrc\n",
407 pThis->pDrvIns->iInstance,
408 enmTxDir == VSCSIIOREQTXDIR_READ
409 ? "Read"
410 : "Write",
411 uOffset,
412 cbTransfer, rcReq));
413 }
414 }
415
416 VSCSIIoReqCompleted(hVScsiIoReq, rcReq, drvscsiIsRedoPossible(rcReq));
417 }
418
419 return VINF_SUCCESS;
420}
421
422/**
423 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCopyFromBuf}
424 */
425static DECLCALLBACK(int) drvscsiIoReqCopyFromBuf(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
426 void *pvIoReqAlloc, uint32_t offDst, PRTSGBUF pSgBuf,
427 size_t cbCopy)
428{
429 RT_NOREF2(pInterface, hIoReq);
430
431 VSCSIIOREQ hVScsiIoReq = DRVSCSI_PDMMEDIAEXIOREQ_2_VSCSIIOREQ(pvIoReqAlloc);
432 uint64_t uOffset = 0;
433 size_t cbTransfer = 0;
434 size_t cbSeg = 0;
435 PCRTSGSEG paSeg = NULL;
436 unsigned cSeg = 0;
437 size_t cbCopied = 0;
438
439 int rc = VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer, &cSeg, &cbSeg, &paSeg);
440 if (RT_SUCCESS(rc))
441 {
442 RTSGBUF SgBuf;
443 RTSgBufInit(&SgBuf, paSeg, cSeg);
444
445 RTSgBufAdvance(&SgBuf, offDst);
446 cbCopied = RTSgBufCopy(&SgBuf, pSgBuf, cbCopy);
447 }
448
449 return cbCopied == cbCopy ? VINF_SUCCESS : VERR_PDM_MEDIAEX_IOBUF_OVERFLOW;
450}
451
452/**
453 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCopyToBuf}
454 */
455static DECLCALLBACK(int) drvscsiIoReqCopyToBuf(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
456 void *pvIoReqAlloc, uint32_t offSrc, PRTSGBUF pSgBuf,
457 size_t cbCopy)
458{
459 RT_NOREF2(pInterface, hIoReq);
460
461 VSCSIIOREQ hVScsiIoReq = DRVSCSI_PDMMEDIAEXIOREQ_2_VSCSIIOREQ(pvIoReqAlloc);
462 uint64_t uOffset = 0;
463 size_t cbTransfer = 0;
464 size_t cbSeg = 0;
465 PCRTSGSEG paSeg = NULL;
466 unsigned cSeg = 0;
467 size_t cbCopied = 0;
468
469 int rc = VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer, &cSeg, &cbSeg, &paSeg);
470 if (RT_SUCCESS(rc))
471 {
472 RTSGBUF SgBuf;
473 RTSgBufInit(&SgBuf, paSeg, cSeg);
474
475 RTSgBufAdvance(&SgBuf, offSrc);
476 cbCopied = RTSgBufCopy(pSgBuf, &SgBuf, cbCopy);
477 }
478
479 return cbCopied == cbCopy ? VINF_SUCCESS : VERR_PDM_MEDIAEX_IOBUF_UNDERRUN;
480}
481
482/**
483 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqQueryDiscardRanges}
484 */
485static DECLCALLBACK(int) drvscsiIoReqQueryDiscardRanges(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
486 void *pvIoReqAlloc, uint32_t idxRangeStart,
487 uint32_t cRanges, PRTRANGE paRanges,
488 uint32_t *pcRanges)
489{
490 RT_NOREF2(pInterface, hIoReq);
491
492 VSCSIIOREQ hVScsiIoReq = DRVSCSI_PDMMEDIAEXIOREQ_2_VSCSIIOREQ(pvIoReqAlloc);
493 PCRTRANGE paRangesVScsi;
494 unsigned cRangesVScsi;
495
496 int rc = VSCSIIoReqUnmapParamsGet(hVScsiIoReq, &paRangesVScsi, &cRangesVScsi);
497 if (RT_SUCCESS(rc))
498 {
499 uint32_t cRangesCopy = RT_MIN(cRangesVScsi - idxRangeStart, cRanges);
500 Assert( idxRangeStart < cRangesVScsi
501 && (idxRangeStart + cRanges) <= cRangesVScsi);
502
503 memcpy(paRanges, &paRangesVScsi[idxRangeStart], cRangesCopy * sizeof(RTRANGE));
504 *pcRanges = cRangesCopy;
505 }
506 return rc;
507}
508
509/**
510 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqStateChanged}
511 */
512static DECLCALLBACK(void) drvscsiIoReqStateChanged(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
513 void *pvIoReqAlloc, PDMMEDIAEXIOREQSTATE enmState)
514{
515 RT_NOREF4(pInterface, hIoReq, pvIoReqAlloc, enmState);
516 AssertLogRelMsgFailed(("This should not be hit because I/O requests should not be suspended\n"));
517}
518
519static DECLCALLBACK(void) drvscsiVScsiReqCompleted(VSCSIDEVICE hVScsiDevice, void *pVScsiDeviceUser,
520 void *pVScsiReqUser, int rcScsiCode, bool fRedoPossible, int rcReq)
521{
522 RT_NOREF(hVScsiDevice);
523 PDRVSCSI pThis = (PDRVSCSI)pVScsiDeviceUser;
524
525 ASMAtomicDecU32(&pThis->StatIoDepth);
526
527 pThis->pDevScsiPort->pfnSCSIRequestCompleted(pThis->pDevScsiPort, (PPDMSCSIREQUEST)pVScsiReqUser,
528 rcScsiCode, fRedoPossible, rcReq);
529
530 if (RT_UNLIKELY(pThis->fDummySignal) && !pThis->StatIoDepth)
531 PDMDrvHlpAsyncNotificationCompleted(pThis->pDrvIns);
532}
533
534/* -=-=-=-=- ISCSIConnector -=-=-=-=- */
535
536#ifdef DEBUG
537/**
538 * Dumps a SCSI request structure for debugging purposes.
539 *
540 * @returns nothing.
541 * @param pRequest Pointer to the request to dump.
542 */
543static void drvscsiDumpScsiRequest(PPDMSCSIREQUEST pRequest)
544{
545 Log(("Dump for pRequest=%#p Command: %s\n", pRequest, SCSICmdText(pRequest->pbCDB[0])));
546 Log(("cbCDB=%u\n", pRequest->cbCDB));
547 for (uint32_t i = 0; i < pRequest->cbCDB; i++)
548 Log(("pbCDB[%u]=%#x\n", i, pRequest->pbCDB[i]));
549 Log(("cbScatterGather=%u\n", pRequest->cbScatterGather));
550 Log(("cScatterGatherEntries=%u\n", pRequest->cScatterGatherEntries));
551 /* Print all scatter gather entries. */
552 for (uint32_t i = 0; i < pRequest->cScatterGatherEntries; i++)
553 {
554 Log(("ScatterGatherEntry[%u].cbSeg=%u\n", i, pRequest->paScatterGatherHead[i].cbSeg));
555 Log(("ScatterGatherEntry[%u].pvSeg=%#p\n", i, pRequest->paScatterGatherHead[i].pvSeg));
556 }
557 Log(("pvUser=%#p\n", pRequest->pvUser));
558}
559#endif
560
561/** @interface_method_impl{PDMISCSICONNECTOR,pfnSCSIRequestSend} */
562static DECLCALLBACK(int) drvscsiRequestSend(PPDMISCSICONNECTOR pInterface, PPDMSCSIREQUEST pSCSIRequest)
563{
564 int rc;
565 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, ISCSIConnector);
566 VSCSIREQ hVScsiReq;
567
568#ifdef DEBUG
569 drvscsiDumpScsiRequest(pSCSIRequest);
570#endif
571
572 rc = VSCSIDeviceReqCreate(pThis->hVScsiDevice, &hVScsiReq,
573 pSCSIRequest->uLogicalUnit,
574 pSCSIRequest->pbCDB,
575 pSCSIRequest->cbCDB,
576 pSCSIRequest->cbScatterGather,
577 pSCSIRequest->cScatterGatherEntries,
578 pSCSIRequest->paScatterGatherHead,
579 pSCSIRequest->pbSenseBuffer,
580 pSCSIRequest->cbSenseBuffer,
581 pSCSIRequest);
582 if (RT_FAILURE(rc))
583 return rc;
584
585 ASMAtomicIncU32(&pThis->StatIoDepth);
586 rc = VSCSIDeviceReqEnqueue(pThis->hVScsiDevice, hVScsiReq);
587
588 return rc;
589}
590
591/** @interface_method_impl{PDMISCSICONNECTOR,pfnQueryLUNType} */
592static DECLCALLBACK(int) drvscsiQueryLUNType(PPDMISCSICONNECTOR pInterface, uint32_t iLun, PPDMSCSILUNTYPE pLunType)
593{
594 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, ISCSIConnector);
595 VSCSILUNTYPE enmLunType;
596
597 int rc = VSCSIDeviceLunQueryType(pThis->hVScsiDevice, iLun, &enmLunType);
598 if (RT_FAILURE(rc))
599 return rc;
600
601 switch (enmLunType)
602 {
603 case VSCSILUNTYPE_SBC: *pLunType = PDMSCSILUNTYPE_SBC; break;
604 case VSCSILUNTYPE_MMC: *pLunType = PDMSCSILUNTYPE_MMC; break;
605 case VSCSILUNTYPE_SSC: *pLunType = PDMSCSILUNTYPE_SSC; break;
606 default: *pLunType = PDMSCSILUNTYPE_INVALID;
607 }
608
609 return rc;
610}
611
612/* -=-=-=-=- IBase -=-=-=-=- */
613
614/**
615 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
616 */
617static DECLCALLBACK(void *) drvscsiQueryInterface(PPDMIBASE pInterface, const char *pszIID)
618{
619 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
620 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
621
622 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNT, pThis->pDrvMount);
623 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
624 PDMIBASE_RETURN_INTERFACE(pszIID, PDMISCSICONNECTOR, &pThis->ISCSIConnector);
625 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAPORT, &pThis->IPort);
626 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNTNOTIFY, &pThis->IMountNotify);
627 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAEXPORT, &pThis->IPortEx);
628 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIA, pThis->pDrvMedia);
629 return NULL;
630}
631
632static DECLCALLBACK(int) drvscsiQueryDeviceLocation(PPDMIMEDIAPORT pInterface, const char **ppcszController,
633 uint32_t *piInstance, uint32_t *piLUN)
634{
635 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IPort);
636
637 return pThis->pDevScsiPort->pfnQueryDeviceLocation(pThis->pDevScsiPort, ppcszController,
638 piInstance, piLUN);
639}
640
641/**
642 * Called when media is mounted.
643 *
644 * @param pInterface Pointer to the interface structure containing the called function pointer.
645 */
646static DECLCALLBACK(void) drvscsiMountNotify(PPDMIMOUNTNOTIFY pInterface)
647{
648 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMountNotify);
649 LogFlowFunc(("mounting LUN#%p\n", pThis->hVScsiLun));
650
651 /* Ignore the call if we're called while being attached. */
652 if (!pThis->pDrvMedia)
653 return;
654
655 /* Let the LUN know that a medium was mounted. */
656 VSCSILunMountNotify(pThis->hVScsiLun);
657}
658
659/**
660 * Called when media is unmounted
661 *
662 * @param pInterface Pointer to the interface structure containing the called function pointer.
663 */
664static DECLCALLBACK(void) drvscsiUnmountNotify(PPDMIMOUNTNOTIFY pInterface)
665{
666 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMountNotify);
667 LogFlowFunc(("unmounting LUN#%p\n", pThis->hVScsiLun));
668
669 /* Let the LUN know that the medium was unmounted. */
670 VSCSILunUnmountNotify(pThis->hVScsiLun);
671}
672
673/**
674 * Worker for drvscsiReset, drvscsiSuspend and drvscsiPowerOff.
675 *
676 * @param pDrvIns The driver instance.
677 * @param pfnAsyncNotify The async callback.
678 */
679static void drvscsiR3ResetOrSuspendOrPowerOff(PPDMDRVINS pDrvIns, PFNPDMDRVASYNCNOTIFY pfnAsyncNotify)
680{
681 RT_NOREF1(pfnAsyncNotify);
682
683 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
684
685 if (pThis->StatIoDepth > 0)
686 ASMAtomicWriteBool(&pThis->fDummySignal, true);
687}
688
689/**
690 * Callback employed by drvscsiSuspend and drvscsiPowerOff.
691 *
692 * @returns true if we've quiesced, false if we're still working.
693 * @param pDrvIns The driver instance.
694 */
695static DECLCALLBACK(bool) drvscsiIsAsyncSuspendOrPowerOffDone(PPDMDRVINS pDrvIns)
696{
697 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
698
699 if (pThis->StatIoDepth > 0)
700 return false;
701 else
702 return true;
703}
704
705/**
706 * @copydoc FNPDMDRVPOWEROFF
707 */
708static DECLCALLBACK(void) drvscsiPowerOff(PPDMDRVINS pDrvIns)
709{
710 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncSuspendOrPowerOffDone);
711}
712
713/**
714 * @copydoc FNPDMDRVSUSPEND
715 */
716static DECLCALLBACK(void) drvscsiSuspend(PPDMDRVINS pDrvIns)
717{
718 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncSuspendOrPowerOffDone);
719}
720
721/**
722 * Callback employed by drvscsiReset.
723 *
724 * @returns true if we've quiesced, false if we're still working.
725 * @param pDrvIns The driver instance.
726 */
727static DECLCALLBACK(bool) drvscsiIsAsyncResetDone(PPDMDRVINS pDrvIns)
728{
729 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
730
731 if (pThis->StatIoDepth > 0)
732 return false;
733 else
734 return true;
735}
736
737/**
738 * @copydoc FNPDMDRVRESET
739 */
740static DECLCALLBACK(void) drvscsiReset(PPDMDRVINS pDrvIns)
741{
742 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncResetDone);
743}
744
745/**
746 * Destruct a driver instance.
747 *
748 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
749 * resources can be freed correctly.
750 *
751 * @param pDrvIns The driver instance data.
752 */
753static DECLCALLBACK(void) drvscsiDestruct(PPDMDRVINS pDrvIns)
754{
755 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
756 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
757
758 /* Free the VSCSI device and LUN handle. */
759 if (pThis->hVScsiDevice)
760 {
761 VSCSILUN hVScsiLun;
762 int rc = VSCSIDeviceLunDetach(pThis->hVScsiDevice, 0, &hVScsiLun);
763 AssertRC(rc);
764
765 Assert(hVScsiLun == pThis->hVScsiLun);
766 rc = VSCSILunDestroy(hVScsiLun);
767 AssertRC(rc);
768 rc = VSCSIDeviceDestroy(pThis->hVScsiDevice);
769 AssertRC(rc);
770
771 pThis->hVScsiDevice = NULL;
772 pThis->hVScsiLun = NULL;
773 }
774
775 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatBytesRead);
776 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatBytesWritten);
777 PDMDrvHlpSTAMDeregister(pDrvIns, (void *)&pThis->StatIoDepth);
778}
779
780/**
781 * Construct a block driver instance.
782 *
783 * @copydoc FNPDMDRVCONSTRUCT
784 */
785static DECLCALLBACK(int) drvscsiConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
786{
787 int rc = VINF_SUCCESS;
788 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
789 LogFlowFunc(("pDrvIns=%#p pCfg=%#p\n", pDrvIns, pCfg));
790 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
791
792 /*
793 * Initialize the instance data.
794 */
795 pThis->pDrvIns = pDrvIns;
796 pThis->ISCSIConnector.pfnSCSIRequestSend = drvscsiRequestSend;
797 pThis->ISCSIConnector.pfnQueryLUNType = drvscsiQueryLUNType;
798
799 pDrvIns->IBase.pfnQueryInterface = drvscsiQueryInterface;
800
801 pThis->IMountNotify.pfnMountNotify = drvscsiMountNotify;
802 pThis->IMountNotify.pfnUnmountNotify = drvscsiUnmountNotify;
803 pThis->IPort.pfnQueryDeviceLocation = drvscsiQueryDeviceLocation;
804 pThis->IPortEx.pfnIoReqCompleteNotify = drvscsiIoReqCompleteNotify;
805 pThis->IPortEx.pfnIoReqCopyFromBuf = drvscsiIoReqCopyFromBuf;
806 pThis->IPortEx.pfnIoReqCopyToBuf = drvscsiIoReqCopyToBuf;
807 pThis->IPortEx.pfnIoReqQueryDiscardRanges = drvscsiIoReqQueryDiscardRanges;
808 pThis->IPortEx.pfnIoReqStateChanged = drvscsiIoReqStateChanged;
809
810 /* Query the SCSI port interface above. */
811 pThis->pDevScsiPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMISCSIPORT);
812 AssertMsgReturn(pThis->pDevScsiPort, ("Missing SCSI port interface above\n"), VERR_PDM_MISSING_INTERFACE);
813
814 /* Query the optional LED interface above. */
815 pThis->pLedPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
816 if (pThis->pLedPort != NULL)
817 {
818 /* Get The Led. */
819 rc = pThis->pLedPort->pfnQueryStatusLed(pThis->pLedPort, 0, &pThis->pLed);
820 if (RT_FAILURE(rc))
821 pThis->pLed = &pThis->Led;
822 }
823 else
824 pThis->pLed = &pThis->Led;
825
826 /*
827 * Validate and read configuration.
828 */
829 if (!CFGMR3AreValuesValid(pCfg, "NonRotationalMedium\0Readonly\0"))
830 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
831 N_("SCSI configuration error: unknown option specified"));
832
833 rc = CFGMR3QueryBoolDef(pCfg, "NonRotationalMedium", &pThis->fNonRotational, false);
834 if (RT_FAILURE(rc))
835 return PDMDRV_SET_ERROR(pDrvIns, rc,
836 N_("SCSI configuration error: failed to read \"NonRotationalMedium\" as boolean"));
837
838 rc = CFGMR3QueryBoolDef(pCfg, "Readonly", &pThis->fReadonly, false);
839 if (RT_FAILURE(rc))
840 return PDMDRV_SET_ERROR(pDrvIns, rc,
841 N_("SCSI configuration error: failed to read \"Readonly\" as boolean"));
842
843 /*
844 * Try attach driver below and query it's block interface.
845 */
846 rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pThis->pDrvBase);
847 AssertMsgReturn(RT_SUCCESS(rc), ("Attaching driver below failed rc=%Rrc\n", rc), rc);
848
849 /*
850 * Query the block and blockbios interfaces.
851 */
852 pThis->pDrvMedia = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMEDIA);
853 AssertMsgReturn(VALID_PTR(pThis->pDrvMedia), ("VSCSI configuration error: No media interface!\n"),
854 VERR_PDM_MISSING_INTERFACE);
855
856 /* Query the extended media interface. */
857 pThis->pDrvMediaEx = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMEDIAEX);
858 AssertMsgReturn(VALID_PTR(pThis->pDrvMediaEx), ("VSCSI configuration error: No extended media interface!\n"),
859 VERR_PDM_MISSING_INTERFACE);
860
861 pThis->pDrvMount = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMOUNT);
862
863 PDMMEDIATYPE enmType = pThis->pDrvMedia->pfnGetType(pThis->pDrvMedia);
864 VSCSILUNTYPE enmLunType;
865 switch (enmType)
866 {
867 case PDMMEDIATYPE_HARD_DISK:
868 enmLunType = VSCSILUNTYPE_SBC;
869 break;
870 case PDMMEDIATYPE_CDROM:
871 case PDMMEDIATYPE_DVD:
872 enmLunType = VSCSILUNTYPE_MMC;
873 break;
874 default:
875 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_UNSUPPORTED_BLOCK_TYPE, RT_SRC_POS,
876 N_("Only hard disks and CD/DVD-ROMs are currently supported as SCSI devices (enmType=%d)"),
877 enmType);
878 }
879 if ( ( enmType == PDMMEDIATYPE_DVD
880 || enmType == PDMMEDIATYPE_CDROM)
881 && !pThis->pDrvMount)
882 {
883 AssertMsgFailed(("Internal error: cdrom without a mountable interface\n"));
884 return VERR_INTERNAL_ERROR;
885 }
886
887 /* Create VSCSI device and LUN. */
888 pThis->VScsiIoCallbacks.pfnVScsiLunReqAllocSizeSet = drvscsiReqAllocSizeSet;
889 pThis->VScsiIoCallbacks.pfnVScsiLunReqAlloc = drvscsiReqAlloc;
890 pThis->VScsiIoCallbacks.pfnVScsiLunReqFree = drvscsiReqFree;
891 pThis->VScsiIoCallbacks.pfnVScsiLunMediumGetSize = drvscsiGetSize;
892 pThis->VScsiIoCallbacks.pfnVScsiLunMediumGetSectorSize = drvscsiGetSectorSize;
893 pThis->VScsiIoCallbacks.pfnVScsiLunReqTransferEnqueue = drvscsiReqTransferEnqueue;
894 pThis->VScsiIoCallbacks.pfnVScsiLunGetFeatureFlags = drvscsiGetFeatureFlags;
895 pThis->VScsiIoCallbacks.pfnVScsiLunMediumSetLock = drvscsiSetLock;
896
897 rc = VSCSIDeviceCreate(&pThis->hVScsiDevice, drvscsiVScsiReqCompleted, pThis);
898 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI device rc=%Rrc\n", rc), rc);
899 rc = VSCSILunCreate(&pThis->hVScsiLun, enmLunType, &pThis->VScsiIoCallbacks,
900 pThis);
901 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI LUN rc=%Rrc\n", rc), rc);
902 rc = VSCSIDeviceLunAttach(pThis->hVScsiDevice, pThis->hVScsiLun, 0);
903 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to attached the LUN to the SCSI device\n"), rc);
904
905 /// @todo This is a very hacky way of telling the LUN whether a medium was mounted.
906 // The mount/unmount interface doesn't work in a very sensible manner!
907 if (pThis->pDrvMount)
908 {
909 if (pThis->pDrvMedia->pfnGetSize(pThis->pDrvMedia))
910 {
911 rc = VINF_SUCCESS; VSCSILunMountNotify(pThis->hVScsiLun);
912 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to notify the LUN of media being mounted\n"), rc);
913 }
914 else
915 {
916 rc = VINF_SUCCESS; VSCSILunUnmountNotify(pThis->hVScsiLun);
917 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to notify the LUN of media being unmounted\n"), rc);
918 }
919 }
920
921 const char *pszCtrl = NULL;
922 uint32_t iCtrlInstance = 0;
923 uint32_t iCtrlLun = 0;
924
925 rc = pThis->pDevScsiPort->pfnQueryDeviceLocation(pThis->pDevScsiPort, &pszCtrl, &iCtrlInstance, &iCtrlLun);
926 if (RT_SUCCESS(rc))
927 {
928 const char *pszCtrlId = strcmp(pszCtrl, "Msd") == 0 ? "USB"
929 : strcmp(pszCtrl, "lsilogicsas") == 0 ? "SAS"
930 : "SCSI";
931 /* Register statistics counter. */
932 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
933 "Amount of data read.", "/Devices/%s%u/%u/ReadBytes", pszCtrlId, iCtrlInstance, iCtrlLun);
934 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
935 "Amount of data written.", "/Devices/%s%u/%u/WrittenBytes", pszCtrlId, iCtrlInstance, iCtrlLun);
936 PDMDrvHlpSTAMRegisterF(pDrvIns, (void *)&pThis->StatIoDepth, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
937 "Number of active tasks.", "/Devices/%s%u/%u/IoDepth", pszCtrlId, iCtrlInstance, iCtrlLun);
938 }
939
940 pThis->StatIoDepth = 0;
941
942 uint32_t fFeatures = 0;
943 rc = pThis->pDrvMediaEx->pfnQueryFeatures(pThis->pDrvMediaEx, &fFeatures);
944 if (RT_FAILURE(rc))
945 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
946 N_("VSCSI configuration error: Failed to query features of device"));
947 if (fFeatures & PDMIMEDIAEX_FEATURE_F_DISCARD)
948 LogRel(("SCSI#%d: Enabled UNMAP support\n", pDrvIns->iInstance));
949
950 return VINF_SUCCESS;
951}
952
953/**
954 * SCSI driver registration record.
955 */
956const PDMDRVREG g_DrvSCSI =
957{
958 /* u32Version */
959 PDM_DRVREG_VERSION,
960 /* szName */
961 "SCSI",
962 /* szRCMod */
963 "",
964 /* szR0Mod */
965 "",
966 /* pszDescription */
967 "Generic SCSI driver.",
968 /* fFlags */
969 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
970 /* fClass. */
971 PDM_DRVREG_CLASS_SCSI,
972 /* cMaxInstances */
973 ~0U,
974 /* cbInstance */
975 sizeof(DRVSCSI),
976 /* pfnConstruct */
977 drvscsiConstruct,
978 /* pfnDestruct */
979 drvscsiDestruct,
980 /* pfnRelocate */
981 NULL,
982 /* pfnIOCtl */
983 NULL,
984 /* pfnPowerOn */
985 NULL,
986 /* pfnReset */
987 drvscsiReset,
988 /* pfnSuspend */
989 drvscsiSuspend,
990 /* pfnResume */
991 NULL,
992 /* pfnAttach */
993 NULL,
994 /* pfnDetach */
995 NULL,
996 /* pfnPowerOff */
997 drvscsiPowerOff,
998 /* pfnSoftReset */
999 NULL,
1000 /* u32EndVersion */
1001 PDM_DRVREG_VERSION
1002};
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