VirtualBox

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

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

Review of PDM driver destructors making sure that variables they use are correctly initialized in the constructor. Found several RTFileClose(0) cases.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.9 KB
Line 
1/* $Id: DrvSCSI.cpp 45061 2013-03-18 14:09:03Z vboxsync $ */
2/** @file
3 * VBox storage drivers: Generic SCSI command parser and execution driver
4 */
5
6/*
7 * Copyright (C) 2006-2012 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 DEBUG
22#define LOG_GROUP LOG_GROUP_DRV_SCSI
23#include <VBox/vmm/pdmdrv.h>
24#include <VBox/vmm/pdmifs.h>
25#include <VBox/vmm/pdmthread.h>
26#include <VBox/vscsi.h>
27#include <VBox/scsi.h>
28#include <iprt/asm.h>
29#include <iprt/assert.h>
30#include <iprt/mem.h>
31#include <iprt/req.h>
32#include <iprt/semaphore.h>
33#include <iprt/string.h>
34#include <iprt/uuid.h>
35
36#include "VBoxDD.h"
37
38/** The maximum number of release log entries per device. */
39#define MAX_LOG_REL_ERRORS 1024
40
41/**
42 * SCSI driver instance data.
43 *
44 * @implements PDMISCSICONNECTOR
45 * @implements PDMIBLOCKASYNCPORT
46 * @implements PDMIMOUNTNOTIFY
47 */
48typedef struct DRVSCSI
49{
50 /** Pointer driver instance. */
51 PPDMDRVINS pDrvIns;
52
53 /** Pointer to the attached driver's base interface. */
54 PPDMIBASE pDrvBase;
55 /** Pointer to the attached driver's block interface. */
56 PPDMIBLOCK pDrvBlock;
57 /** Pointer to the attached driver's async block interface. */
58 PPDMIBLOCKASYNC pDrvBlockAsync;
59 /** Pointer to the attached driver's block bios interface. */
60 PPDMIBLOCKBIOS pDrvBlockBios;
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 block port interface. */
70 PDMIBLOCKPORT IPort;
71 /** The optional block async port interface. */
72 PDMIBLOCKASYNCPORT IPortAsync;
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 /** The dedicated I/O thread for the non async approach. */
89 PPDMTHREAD pAsyncIOThread;
90 /** Queue for passing the requests to the thread. */
91 RTREQQUEUE hQueueRequests;
92 /** Request that we've left pending on wakeup or reset. */
93 PRTREQ pPendingDummyReq;
94 /** Indicates whether PDMDrvHlpAsyncNotificationCompleted should be called by
95 * any of the dummy functions. */
96 bool volatile fDummySignal;
97 /** Release statistics: number of bytes written. */
98 STAMCOUNTER StatBytesWritten;
99 /** Release statistics: number of bytes read. */
100 STAMCOUNTER StatBytesRead;
101 /** Release statistics: Current I/O depth. */
102 volatile uint32_t StatIoDepth;
103 /** Errors printed in the release log. */
104 unsigned cErrors;
105 /** Mark the drive as having a non-rotational medium (i.e. as a SSD). */
106 bool fNonRotational;
107 /** Medium is readonly */
108 bool fReadonly;
109} DRVSCSI, *PDRVSCSI;
110
111/** Converts a pointer to DRVSCSI::ISCSIConnector to a PDRVSCSI. */
112#define PDMISCSICONNECTOR_2_DRVSCSI(pInterface) ( (PDRVSCSI)((uintptr_t)pInterface - RT_OFFSETOF(DRVSCSI, ISCSIConnector)) )
113/** Converts a pointer to DRVSCSI::IPortAsync to a PDRVSCSI. */
114#define PDMIBLOCKASYNCPORT_2_DRVSCSI(pInterface) ( (PDRVSCSI)((uintptr_t)pInterface - RT_OFFSETOF(DRVSCSI, IPortAsync)) )
115/** Converts a pointer to DRVSCSI::IMountNotify to PDRVSCSI. */
116#define PDMIMOUNTNOTIFY_2_DRVSCSI(pInterface) ( (PDRVSCSI)((uintptr_t)pInterface - RT_OFFSETOF(DRVSCSI, IMountNotify)) )
117/** Converts a pointer to DRVSCSI::IPort to a PDRVSCSI. */
118#define PDMIBLOCKPORT_2_DRVSCSI(pInterface) ( (PDRVSCSI)((uintptr_t)pInterface - RT_OFFSETOF(DRVSCSI, IPort)) )
119
120static bool drvscsiIsRedoPossible(int rc)
121{
122 if ( rc == VERR_DISK_FULL
123 || rc == VERR_FILE_TOO_BIG
124 || rc == VERR_BROKEN_PIPE
125 || rc == VERR_NET_CONNECTION_REFUSED)
126 return true;
127
128 return false;
129}
130
131static int drvscsiProcessRequestOne(PDRVSCSI pThis, VSCSIIOREQ hVScsiIoReq)
132{
133 int rc = VINF_SUCCESS;
134 VSCSIIOREQTXDIR enmTxDir;
135
136 enmTxDir = VSCSIIoReqTxDirGet(hVScsiIoReq);
137
138 switch (enmTxDir)
139 {
140 case VSCSIIOREQTXDIR_FLUSH:
141 {
142 rc = pThis->pDrvBlock->pfnFlush(pThis->pDrvBlock);
143 if ( RT_FAILURE(rc)
144 && pThis->cErrors++ < MAX_LOG_REL_ERRORS)
145 LogRel(("SCSI#%u: Flush returned rc=%Rrc\n",
146 pThis->pDrvIns->iInstance, rc));
147 break;
148 }
149 case VSCSIIOREQTXDIR_READ:
150 case VSCSIIOREQTXDIR_WRITE:
151 {
152 uint64_t uOffset = 0;
153 size_t cbTransfer = 0;
154 size_t cbSeg = 0;
155 PCRTSGSEG paSeg = NULL;
156 unsigned cSeg = 0;
157
158 rc = VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer, &cSeg, &cbSeg,
159 &paSeg);
160 AssertRC(rc);
161
162 while (cbTransfer && cSeg)
163 {
164 size_t cbProcess = (cbTransfer < paSeg->cbSeg) ? cbTransfer : paSeg->cbSeg;
165
166 Log(("%s: uOffset=%llu cbProcess=%u\n", __FUNCTION__, uOffset, cbProcess));
167
168 if (enmTxDir == VSCSIIOREQTXDIR_READ)
169 {
170 pThis->pLed->Asserted.s.fReading = pThis->pLed->Actual.s.fReading = 1;
171 rc = pThis->pDrvBlock->pfnRead(pThis->pDrvBlock, uOffset,
172 paSeg->pvSeg, cbProcess);
173 pThis->pLed->Actual.s.fReading = 0;
174 if (RT_FAILURE(rc))
175 break;
176 STAM_REL_COUNTER_ADD(&pThis->StatBytesRead, cbProcess);
177 }
178 else
179 {
180 pThis->pLed->Asserted.s.fWriting = pThis->pLed->Actual.s.fWriting = 1;
181 rc = pThis->pDrvBlock->pfnWrite(pThis->pDrvBlock, uOffset,
182 paSeg->pvSeg, cbProcess);
183 pThis->pLed->Actual.s.fWriting = 0;
184 if (RT_FAILURE(rc))
185 break;
186 STAM_REL_COUNTER_ADD(&pThis->StatBytesWritten, cbProcess);
187 }
188
189 /* Go to the next entry. */
190 uOffset += cbProcess;
191 cbTransfer -= cbProcess;
192 paSeg++;
193 cSeg--;
194 }
195
196 if ( RT_FAILURE(rc)
197 && pThis->cErrors++ < MAX_LOG_REL_ERRORS)
198 LogRel(("SCSI#%u: %s at offset %llu (%u bytes left) returned rc=%Rrc\n",
199 pThis->pDrvIns->iInstance,
200 enmTxDir == VSCSIIOREQTXDIR_READ
201 ? "Read"
202 : "Write",
203 uOffset,
204 cbTransfer, rc));
205
206 break;
207 }
208 case VSCSIIOREQTXDIR_UNMAP:
209 {
210 PCRTRANGE paRanges;
211 unsigned cRanges;
212
213 rc = VSCSIIoReqUnmapParamsGet(hVScsiIoReq, &paRanges, &cRanges);
214 AssertRC(rc);
215
216 pThis->pLed->Asserted.s.fWriting = pThis->pLed->Actual.s.fWriting = 1;
217 rc = pThis->pDrvBlock->pfnDiscard(pThis->pDrvBlock, paRanges, cRanges);
218 pThis->pLed->Actual.s.fWriting = 0;
219
220 if ( RT_FAILURE(rc)
221 && pThis->cErrors++ < MAX_LOG_REL_ERRORS)
222 LogRel(("SCSI#%u: Unmap returned rc=%Rrc\n",
223 pThis->pDrvIns->iInstance, rc));
224
225 break;
226 }
227 default:
228 AssertMsgFailed(("Invalid transfer direction %d\n", enmTxDir));
229 }
230
231 if (RT_SUCCESS(rc))
232 VSCSIIoReqCompleted(hVScsiIoReq, rc, false /* fRedoPossible */);
233 else
234 VSCSIIoReqCompleted(hVScsiIoReq, rc, drvscsiIsRedoPossible(rc));
235
236 return VINF_SUCCESS;
237}
238
239static DECLCALLBACK(int) drvscsiGetSize(VSCSILUN hVScsiLun, void *pvScsiLunUser, uint64_t *pcbSize)
240{
241 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
242
243 *pcbSize = pThis->pDrvBlock->pfnGetSize(pThis->pDrvBlock);
244
245 return VINF_SUCCESS;
246}
247
248static DECLCALLBACK(int) drvscsiSetLock(VSCSILUN hVScsiLun, void *pvScsiLunUser, bool fLocked)
249{
250 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
251
252 if (fLocked)
253 pThis->pDrvMount->pfnLock(pThis->pDrvMount);
254 else
255 pThis->pDrvMount->pfnUnlock(pThis->pDrvMount);
256
257 return VINF_SUCCESS;
258}
259
260static int drvscsiTransferCompleteNotify(PPDMIBLOCKASYNCPORT pInterface, void *pvUser, int rc)
261{
262 PDRVSCSI pThis = PDMIBLOCKASYNCPORT_2_DRVSCSI(pInterface);
263 VSCSIIOREQ hVScsiIoReq = (VSCSIIOREQ)pvUser;
264 VSCSIIOREQTXDIR enmTxDir = VSCSIIoReqTxDirGet(hVScsiIoReq);
265
266 LogFlowFunc(("Request hVScsiIoReq=%#p completed\n", hVScsiIoReq));
267
268 if (enmTxDir == VSCSIIOREQTXDIR_READ)
269 pThis->pLed->Actual.s.fReading = 0;
270 else if ( enmTxDir == VSCSIIOREQTXDIR_WRITE
271 || enmTxDir == VSCSIIOREQTXDIR_UNMAP)
272 pThis->pLed->Actual.s.fWriting = 0;
273 else
274 AssertMsg(enmTxDir == VSCSIIOREQTXDIR_FLUSH, ("Invalid transfer direction %u\n", enmTxDir));
275
276 if (RT_SUCCESS(rc))
277 VSCSIIoReqCompleted(hVScsiIoReq, rc, false /* fRedoPossible */);
278 else
279 {
280 pThis->cErrors++;
281 if (pThis->cErrors < MAX_LOG_REL_ERRORS)
282 {
283 if (enmTxDir == VSCSIIOREQTXDIR_FLUSH)
284 LogRel(("SCSI#%u: Flush returned rc=%Rrc\n",
285 pThis->pDrvIns->iInstance, rc));
286 else if (enmTxDir == VSCSIIOREQTXDIR_UNMAP)
287 LogRel(("SCSI#%u: Unmap returned rc=%Rrc\n",
288 pThis->pDrvIns->iInstance, rc));
289 else
290 {
291 uint64_t uOffset = 0;
292 size_t cbTransfer = 0;
293 size_t cbSeg = 0;
294 PCRTSGSEG paSeg = NULL;
295 unsigned cSeg = 0;
296
297 VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer,
298 &cSeg, &cbSeg, &paSeg);
299
300 LogRel(("SCSI#%u: %s at offset %llu (%u bytes left) returned rc=%Rrc\n",
301 pThis->pDrvIns->iInstance,
302 enmTxDir == VSCSIIOREQTXDIR_READ
303 ? "Read"
304 : "Write",
305 uOffset,
306 cbTransfer, rc));
307 }
308 }
309
310 VSCSIIoReqCompleted(hVScsiIoReq, rc, drvscsiIsRedoPossible(rc));
311 }
312
313 return VINF_SUCCESS;
314}
315
316static DECLCALLBACK(int) drvscsiReqTransferEnqueue(VSCSILUN hVScsiLun,
317 void *pvScsiLunUser,
318 VSCSIIOREQ hVScsiIoReq)
319{
320 int rc = VINF_SUCCESS;
321 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
322
323 if (pThis->pDrvBlockAsync)
324 {
325 /* async I/O path. */
326 VSCSIIOREQTXDIR enmTxDir;
327
328 LogFlowFunc(("Enqueuing hVScsiIoReq=%#p\n", hVScsiIoReq));
329
330 enmTxDir = VSCSIIoReqTxDirGet(hVScsiIoReq);
331
332 switch (enmTxDir)
333 {
334 case VSCSIIOREQTXDIR_FLUSH:
335 {
336 rc = pThis->pDrvBlockAsync->pfnStartFlush(pThis->pDrvBlockAsync, hVScsiIoReq);
337 if ( RT_FAILURE(rc)
338 && rc != VERR_VD_ASYNC_IO_IN_PROGRESS
339 && pThis->cErrors++ < MAX_LOG_REL_ERRORS)
340 LogRel(("SCSI#%u: Flush returned rc=%Rrc\n",
341 pThis->pDrvIns->iInstance, rc));
342 break;
343 }
344 case VSCSIIOREQTXDIR_UNMAP:
345 {
346 PCRTRANGE paRanges;
347 unsigned cRanges;
348
349 rc = VSCSIIoReqUnmapParamsGet(hVScsiIoReq, &paRanges, &cRanges);
350 AssertRC(rc);
351
352 pThis->pLed->Asserted.s.fWriting = pThis->pLed->Actual.s.fWriting = 1;
353 rc = pThis->pDrvBlockAsync->pfnStartDiscard(pThis->pDrvBlockAsync, paRanges, cRanges, hVScsiIoReq);
354 if ( RT_FAILURE(rc)
355 && rc != VERR_VD_ASYNC_IO_IN_PROGRESS
356 && pThis->cErrors++ < MAX_LOG_REL_ERRORS)
357 LogRel(("SCSI#%u: Discard returned rc=%Rrc\n",
358 pThis->pDrvIns->iInstance, rc));
359 break;
360 }
361 case VSCSIIOREQTXDIR_READ:
362 case VSCSIIOREQTXDIR_WRITE:
363 {
364 uint64_t uOffset = 0;
365 size_t cbTransfer = 0;
366 size_t cbSeg = 0;
367 PCRTSGSEG paSeg = NULL;
368 unsigned cSeg = 0;
369
370 rc = VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer,
371 &cSeg, &cbSeg, &paSeg);
372 AssertRC(rc);
373
374 if (enmTxDir == VSCSIIOREQTXDIR_READ)
375 {
376 pThis->pLed->Asserted.s.fReading = pThis->pLed->Actual.s.fReading = 1;
377 rc = pThis->pDrvBlockAsync->pfnStartRead(pThis->pDrvBlockAsync, uOffset,
378 paSeg, cSeg, cbTransfer,
379 hVScsiIoReq);
380 STAM_REL_COUNTER_ADD(&pThis->StatBytesRead, cbTransfer);
381 }
382 else
383 {
384 pThis->pLed->Asserted.s.fWriting = pThis->pLed->Actual.s.fWriting = 1;
385 rc = pThis->pDrvBlockAsync->pfnStartWrite(pThis->pDrvBlockAsync, uOffset,
386 paSeg, cSeg, cbTransfer,
387 hVScsiIoReq);
388 STAM_REL_COUNTER_ADD(&pThis->StatBytesWritten, cbTransfer);
389 }
390
391 if ( RT_FAILURE(rc)
392 && rc != VERR_VD_ASYNC_IO_IN_PROGRESS
393 && pThis->cErrors++ < MAX_LOG_REL_ERRORS)
394 LogRel(("SCSI#%u: %s at offset %llu (%u bytes left) returned rc=%Rrc\n",
395 pThis->pDrvIns->iInstance,
396 enmTxDir == VSCSIIOREQTXDIR_READ
397 ? "Read"
398 : "Write",
399 uOffset,
400 cbTransfer, rc));
401 break;
402 }
403 default:
404 AssertMsgFailed(("Invalid transfer direction %u\n", enmTxDir));
405 }
406
407 if (rc == VINF_VD_ASYNC_IO_FINISHED)
408 {
409 if (enmTxDir == VSCSIIOREQTXDIR_READ)
410 pThis->pLed->Actual.s.fReading = 0;
411 else if (enmTxDir == VSCSIIOREQTXDIR_WRITE)
412 pThis->pLed->Actual.s.fWriting = 0;
413 else
414 AssertMsg(enmTxDir == VSCSIIOREQTXDIR_FLUSH, ("Invalid transfer direction %u\n", enmTxDir));
415
416 VSCSIIoReqCompleted(hVScsiIoReq, VINF_SUCCESS, false);
417 rc = VINF_SUCCESS;
418 }
419 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
420 rc = VINF_SUCCESS;
421 else if (RT_FAILURE(rc))
422 {
423 if (enmTxDir == VSCSIIOREQTXDIR_READ)
424 pThis->pLed->Actual.s.fReading = 0;
425 else if (enmTxDir == VSCSIIOREQTXDIR_WRITE)
426 pThis->pLed->Actual.s.fWriting = 0;
427 else
428 AssertMsg(enmTxDir == VSCSIIOREQTXDIR_FLUSH, ("Invalid transfer direction %u\n", enmTxDir));
429
430 VSCSIIoReqCompleted(hVScsiIoReq, rc, drvscsiIsRedoPossible(rc));
431 rc = VINF_SUCCESS;
432 }
433 else
434 AssertMsgFailed(("Invalid return code rc=%Rrc\n", rc));
435 }
436 else
437 {
438 /* I/O thread. */
439 rc = RTReqQueueCallEx(pThis->hQueueRequests, NULL, 0, RTREQFLAGS_NO_WAIT,
440 (PFNRT)drvscsiProcessRequestOne, 2, pThis, hVScsiIoReq);
441 }
442
443 return rc;
444}
445
446static DECLCALLBACK(int) drvscsiGetFeatureFlags(VSCSILUN hVScsiLun,
447 void *pvScsiLunUser,
448 uint64_t *pfFeatures)
449{
450 int rc = VINF_SUCCESS;
451 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
452
453 *pfFeatures = 0;
454
455 if ( pThis->pDrvBlock->pfnDiscard
456 || ( pThis->pDrvBlockAsync
457 && pThis->pDrvBlockAsync->pfnStartDiscard))
458 *pfFeatures |= VSCSI_LUN_FEATURE_UNMAP;
459
460 if (pThis->fNonRotational)
461 *pfFeatures |= VSCSI_LUN_FEATURE_NON_ROTATIONAL;
462
463 if (pThis->fReadonly)
464 *pfFeatures |= VSCSI_LUN_FEATURE_READONLY;
465
466 return VINF_SUCCESS;
467}
468
469static void drvscsiVScsiReqCompleted(VSCSIDEVICE hVScsiDevice, void *pVScsiDeviceUser,
470 void *pVScsiReqUser, int rcScsiCode, bool fRedoPossible,
471 int rcReq)
472{
473 PDRVSCSI pThis = (PDRVSCSI)pVScsiDeviceUser;
474
475 ASMAtomicDecU32(&pThis->StatIoDepth);
476
477 pThis->pDevScsiPort->pfnSCSIRequestCompleted(pThis->pDevScsiPort, (PPDMSCSIREQUEST)pVScsiReqUser,
478 rcScsiCode, fRedoPossible, rcReq);
479
480 if (RT_UNLIKELY(pThis->fDummySignal) && !pThis->StatIoDepth)
481 PDMDrvHlpAsyncNotificationCompleted(pThis->pDrvIns);
482}
483
484/**
485 * Dummy request function used by drvscsiReset to wait for all pending requests
486 * to complete prior to the device reset.
487 *
488 * @param pThis Pointer to the instance data.
489 * @returns VINF_SUCCESS.
490 */
491static int drvscsiAsyncIOLoopSyncCallback(PDRVSCSI pThis)
492{
493 if (pThis->fDummySignal)
494 PDMDrvHlpAsyncNotificationCompleted(pThis->pDrvIns);
495 return VINF_SUCCESS;
496}
497
498/**
499 * Request function to wakeup the thread.
500 *
501 * @param pThis Pointer to the instance data.
502 * @returns VWRN_STATE_CHANGED.
503 */
504static int drvscsiAsyncIOLoopWakeupFunc(PDRVSCSI pThis)
505{
506 if (pThis->fDummySignal)
507 PDMDrvHlpAsyncNotificationCompleted(pThis->pDrvIns);
508 return VWRN_STATE_CHANGED;
509}
510
511/**
512 * The thread function which processes the requests asynchronously.
513 *
514 * @returns VBox status code.
515 * @param pDrvIns Pointer to the driver instance data.
516 * @param pThread Pointer to the thread instance data.
517 */
518static int drvscsiAsyncIOLoop(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
519{
520 int rc = VINF_SUCCESS;
521 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
522
523 LogFlowFunc(("Entering async IO loop.\n"));
524
525 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
526 return VINF_SUCCESS;
527
528 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
529 {
530 rc = RTReqQueueProcess(pThis->hQueueRequests, RT_INDEFINITE_WAIT);
531 AssertMsg(rc == VWRN_STATE_CHANGED, ("Left RTReqProcess and error code is not VWRN_STATE_CHANGED rc=%Rrc\n", rc));
532 }
533
534 return VINF_SUCCESS;
535}
536
537/**
538 * Deals with any pending dummy request
539 *
540 * @returns true if no pending dummy request, false if still pending.
541 * @param pThis The instance data.
542 * @param cMillies The number of milliseconds to wait for any
543 * pending request to finish.
544 */
545static bool drvscsiAsyncIOLoopNoPendingDummy(PDRVSCSI pThis, uint32_t cMillies)
546{
547 if (!pThis->pPendingDummyReq)
548 return true;
549 int rc = RTReqWait(pThis->pPendingDummyReq, cMillies);
550 if (RT_FAILURE(rc))
551 return false;
552 RTReqRelease(pThis->pPendingDummyReq);
553 pThis->pPendingDummyReq = NULL;
554 return true;
555}
556
557static int drvscsiAsyncIOLoopWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
558{
559 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
560 PRTREQ pReq;
561 int rc;
562
563 AssertMsgReturn(pThis->hQueueRequests != NIL_RTREQQUEUE, ("hQueueRequests is NULL\n"), VERR_INVALID_STATE);
564
565 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 10000 /* 10 sec */))
566 {
567 LogRel(("drvscsiAsyncIOLoopWakeup#%u: previous dummy request is still pending\n", pDrvIns->iInstance));
568 return VERR_TIMEOUT;
569 }
570
571 rc = RTReqQueueCall(pThis->hQueueRequests, &pReq, 10000 /* 10 sec. */, (PFNRT)drvscsiAsyncIOLoopWakeupFunc, 1, pThis);
572 if (RT_SUCCESS(rc))
573 RTReqRelease(pReq);
574 else
575 {
576 pThis->pPendingDummyReq = pReq;
577 LogRel(("drvscsiAsyncIOLoopWakeup#%u: %Rrc pReq=%p\n", pDrvIns->iInstance, rc, pReq));
578 }
579
580 return rc;
581}
582
583/* -=-=-=-=- ISCSIConnector -=-=-=-=- */
584
585#ifdef DEBUG
586/**
587 * Dumps a SCSI request structure for debugging purposes.
588 *
589 * @returns nothing.
590 * @param pRequest Pointer to the request to dump.
591 */
592static void drvscsiDumpScsiRequest(PPDMSCSIREQUEST pRequest)
593{
594 Log(("Dump for pRequest=%#p Command: %s\n", pRequest, SCSICmdText(pRequest->pbCDB[0])));
595 Log(("cbCDB=%u\n", pRequest->cbCDB));
596 for (uint32_t i = 0; i < pRequest->cbCDB; i++)
597 Log(("pbCDB[%u]=%#x\n", i, pRequest->pbCDB[i]));
598 Log(("cbScatterGather=%u\n", pRequest->cbScatterGather));
599 Log(("cScatterGatherEntries=%u\n", pRequest->cScatterGatherEntries));
600 /* Print all scatter gather entries. */
601 for (uint32_t i = 0; i < pRequest->cScatterGatherEntries; i++)
602 {
603 Log(("ScatterGatherEntry[%u].cbSeg=%u\n", i, pRequest->paScatterGatherHead[i].cbSeg));
604 Log(("ScatterGatherEntry[%u].pvSeg=%#p\n", i, pRequest->paScatterGatherHead[i].pvSeg));
605 }
606 Log(("pvUser=%#p\n", pRequest->pvUser));
607}
608#endif
609
610/** @copydoc PDMISCSICONNECTOR::pfnSCSIRequestSend. */
611static DECLCALLBACK(int) drvscsiRequestSend(PPDMISCSICONNECTOR pInterface, PPDMSCSIREQUEST pSCSIRequest)
612{
613 int rc;
614 PDRVSCSI pThis = PDMISCSICONNECTOR_2_DRVSCSI(pInterface);
615 VSCSIREQ hVScsiReq;
616
617#ifdef DEBUG
618 drvscsiDumpScsiRequest(pSCSIRequest);
619#endif
620
621 rc = VSCSIDeviceReqCreate(pThis->hVScsiDevice, &hVScsiReq,
622 pSCSIRequest->uLogicalUnit,
623 pSCSIRequest->pbCDB,
624 pSCSIRequest->cbCDB,
625 pSCSIRequest->cbScatterGather,
626 pSCSIRequest->cScatterGatherEntries,
627 pSCSIRequest->paScatterGatherHead,
628 pSCSIRequest->pbSenseBuffer,
629 pSCSIRequest->cbSenseBuffer,
630 pSCSIRequest);
631 if (RT_FAILURE(rc))
632 return rc;
633
634 ASMAtomicIncU32(&pThis->StatIoDepth);
635 rc = VSCSIDeviceReqEnqueue(pThis->hVScsiDevice, hVScsiReq);
636
637 return rc;
638}
639
640/* -=-=-=-=- IBase -=-=-=-=- */
641
642/**
643 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
644 */
645static DECLCALLBACK(void *) drvscsiQueryInterface(PPDMIBASE pInterface, const char *pszIID)
646{
647 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
648 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
649
650 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNT, pThis->pDrvMount);
651 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
652 PDMIBASE_RETURN_INTERFACE(pszIID, PDMISCSICONNECTOR, &pThis->ISCSIConnector);
653 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCKPORT, &pThis->IPort);
654 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNTNOTIFY, &pThis->IMountNotify);
655 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCKASYNCPORT, &pThis->IPortAsync);
656 return NULL;
657}
658
659static DECLCALLBACK(int) drvscsiQueryDeviceLocation(PPDMIBLOCKPORT pInterface, const char **ppcszController,
660 uint32_t *piInstance, uint32_t *piLUN)
661{
662 PDRVSCSI pThis = PDMIBLOCKPORT_2_DRVSCSI(pInterface);
663
664 return pThis->pDevScsiPort->pfnQueryDeviceLocation(pThis->pDevScsiPort, ppcszController,
665 piInstance, piLUN);
666}
667
668/**
669 * Called when media is mounted.
670 *
671 * @param pInterface Pointer to the interface structure containing the called function pointer.
672 */
673static DECLCALLBACK(void) drvscsiMountNotify(PPDMIMOUNTNOTIFY pInterface)
674{
675 PDRVSCSI pThis = PDMIMOUNTNOTIFY_2_DRVSCSI(pInterface);
676 LogFlowFunc(("mounting LUN#%p\n", pThis->hVScsiLun));
677
678 /* Ignore the call if we're called while being attached. */
679 if (!pThis->pDrvBlock)
680 return;
681
682 /* Let the LUN know that a medium was mounted. */
683 VSCSILunMountNotify(pThis->hVScsiLun);
684}
685
686/**
687 * Called when media is unmounted
688 *
689 * @param pInterface Pointer to the interface structure containing the called function pointer.
690 */
691static DECLCALLBACK(void) drvscsiUnmountNotify(PPDMIMOUNTNOTIFY pInterface)
692{
693 PDRVSCSI pThis = PDMIMOUNTNOTIFY_2_DRVSCSI(pInterface);
694 LogFlowFunc(("unmounting LUN#%p\n", pThis->hVScsiLun));
695
696 /* Let the LUN know that the medium was unmounted. */
697 VSCSILunUnmountNotify(pThis->hVScsiLun);
698}
699
700/**
701 * Worker for drvscsiReset, drvscsiSuspend and drvscsiPowerOff.
702 *
703 * @param pDrvIns The driver instance.
704 * @param pfnAsyncNotify The async callback.
705 */
706static void drvscsiR3ResetOrSuspendOrPowerOff(PPDMDRVINS pDrvIns, PFNPDMDRVASYNCNOTIFY pfnAsyncNotify)
707{
708 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
709
710 if (!pThis->pDrvBlockAsync)
711 {
712 if (pThis->hQueueRequests != NIL_RTREQQUEUE)
713 return;
714
715 ASMAtomicWriteBool(&pThis->fDummySignal, true);
716 if (drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/))
717 {
718 if (!RTReqQueueIsBusy(pThis->hQueueRequests))
719 {
720 ASMAtomicWriteBool(&pThis->fDummySignal, false);
721 return;
722 }
723
724 PRTREQ pReq;
725 int rc = RTReqQueueCall(pThis->hQueueRequests, &pReq, 0 /*ms*/, (PFNRT)drvscsiAsyncIOLoopSyncCallback, 1, pThis);
726 if (RT_SUCCESS(rc))
727 {
728 ASMAtomicWriteBool(&pThis->fDummySignal, false);
729 RTReqRelease(pReq);
730 return;
731 }
732
733 pThis->pPendingDummyReq = pReq;
734 }
735 }
736 else
737 {
738 if (pThis->StatIoDepth > 0)
739 {
740 ASMAtomicWriteBool(&pThis->fDummySignal, true);
741 }
742 return;
743 }
744
745 PDMDrvHlpSetAsyncNotification(pDrvIns, pfnAsyncNotify);
746}
747
748/**
749 * Callback employed by drvscsiSuspend and drvscsiPowerOff.
750 *
751 * @returns true if we've quiesced, false if we're still working.
752 * @param pDrvIns The driver instance.
753 */
754static DECLCALLBACK(bool) drvscsiIsAsyncSuspendOrPowerOffDone(PPDMDRVINS pDrvIns)
755{
756 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
757
758 if (pThis->pDrvBlockAsync)
759 {
760 if (pThis->StatIoDepth > 0)
761 return false;
762 else
763 return true;
764 }
765 else
766 {
767 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/))
768 return false;
769 ASMAtomicWriteBool(&pThis->fDummySignal, false);
770 PDMR3ThreadSuspend(pThis->pAsyncIOThread);
771 return true;
772 }
773}
774
775/**
776 * @copydoc FNPDMDRVPOWEROFF
777 */
778static DECLCALLBACK(void) drvscsiPowerOff(PPDMDRVINS pDrvIns)
779{
780 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncSuspendOrPowerOffDone);
781}
782
783/**
784 * @copydoc FNPDMDRVSUSPEND
785 */
786static DECLCALLBACK(void) drvscsiSuspend(PPDMDRVINS pDrvIns)
787{
788 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncSuspendOrPowerOffDone);
789}
790
791/**
792 * Callback employed by drvscsiReset.
793 *
794 * @returns true if we've quiesced, false if we're still working.
795 * @param pDrvIns The driver instance.
796 */
797static DECLCALLBACK(bool) drvscsiIsAsyncResetDone(PPDMDRVINS pDrvIns)
798{
799 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
800
801 if (pThis->pDrvBlockAsync)
802 {
803 if (pThis->StatIoDepth > 0)
804 return false;
805 else
806 return true;
807 }
808 else
809 {
810 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/))
811 return false;
812 ASMAtomicWriteBool(&pThis->fDummySignal, false);
813 return true;
814 }
815}
816
817/**
818 * @copydoc FNPDMDRVRESET
819 */
820static DECLCALLBACK(void) drvscsiReset(PPDMDRVINS pDrvIns)
821{
822 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncResetDone);
823}
824
825/**
826 * Destruct a driver instance.
827 *
828 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
829 * resources can be freed correctly.
830 *
831 * @param pDrvIns The driver instance data.
832 */
833static DECLCALLBACK(void) drvscsiDestruct(PPDMDRVINS pDrvIns)
834{
835 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
836 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
837
838 if (pThis->hQueueRequests != NIL_RTREQQUEUE)
839 {
840 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 100 /*ms*/))
841 LogRel(("drvscsiDestruct#%u: previous dummy request is still pending\n", pDrvIns->iInstance));
842
843 int rc = RTReqQueueDestroy(pThis->hQueueRequests);
844 AssertMsgRC(rc, ("Failed to destroy queue rc=%Rrc\n", rc));
845 pThis->hQueueRequests = NIL_RTREQQUEUE;
846 }
847
848 /* Free the VSCSI device and LUN handle. */
849 if (pThis->hVScsiDevice)
850 {
851 VSCSILUN hVScsiLun;
852 int rc = VSCSIDeviceLunDetach(pThis->hVScsiDevice, 0, &hVScsiLun);
853 AssertRC(rc);
854
855 Assert(hVScsiLun == pThis->hVScsiLun);
856 rc = VSCSILunDestroy(hVScsiLun);
857 AssertRC(rc);
858 rc = VSCSIDeviceDestroy(pThis->hVScsiDevice);
859 AssertRC(rc);
860
861 pThis->hVScsiDevice = NULL;
862 pThis->hVScsiLun = NULL;
863 }
864}
865
866/**
867 * Construct a block driver instance.
868 *
869 * @copydoc FNPDMDRVCONSTRUCT
870 */
871static DECLCALLBACK(int) drvscsiConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
872{
873 int rc = VINF_SUCCESS;
874 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
875 LogFlowFunc(("pDrvIns=%#p pCfg=%#p\n", pDrvIns, pCfg));
876 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
877
878 /*
879 * Initialize the instance data.
880 */
881 pThis->pDrvIns = pDrvIns;
882 pThis->ISCSIConnector.pfnSCSIRequestSend = drvscsiRequestSend;
883
884 pDrvIns->IBase.pfnQueryInterface = drvscsiQueryInterface;
885
886 pThis->IMountNotify.pfnMountNotify = drvscsiMountNotify;
887 pThis->IMountNotify.pfnUnmountNotify = drvscsiUnmountNotify;
888 pThis->IPort.pfnQueryDeviceLocation = drvscsiQueryDeviceLocation;
889 pThis->IPortAsync.pfnTransferCompleteNotify = drvscsiTransferCompleteNotify;
890 pThis->hQueueRequests = NIL_RTREQQUEUE;
891
892 /* Query the SCSI port interface above. */
893 pThis->pDevScsiPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMISCSIPORT);
894 AssertMsgReturn(pThis->pDevScsiPort, ("Missing SCSI port interface above\n"), VERR_PDM_MISSING_INTERFACE);
895
896 /* Query the optional LED interface above. */
897 pThis->pLedPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
898 if (pThis->pLedPort != NULL)
899 {
900 /* Get The Led. */
901 rc = pThis->pLedPort->pfnQueryStatusLed(pThis->pLedPort, 0, &pThis->pLed);
902 if (RT_FAILURE(rc))
903 pThis->pLed = &pThis->Led;
904 }
905 else
906 pThis->pLed = &pThis->Led;
907
908 /*
909 * Validate and read configuration.
910 */
911 if (!CFGMR3AreValuesValid(pCfg, "NonRotationalMedium\0Readonly\0"))
912 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
913 N_("SCSI configuration error: unknown option specified"));
914
915 rc = CFGMR3QueryBoolDef(pCfg, "NonRotationalMedium", &pThis->fNonRotational, false);
916 if (RT_FAILURE(rc))
917 return PDMDRV_SET_ERROR(pDrvIns, rc,
918 N_("SCSI configuration error: failed to read \"NonRotationalMedium\" as boolean"));
919
920 rc = CFGMR3QueryBoolDef(pCfg, "Readonly", &pThis->fReadonly, false);
921 if (RT_FAILURE(rc))
922 return PDMDRV_SET_ERROR(pDrvIns, rc,
923 N_("SCSI configuration error: failed to read \"Readonly\" as boolean"));
924
925 /*
926 * Try attach driver below and query it's block interface.
927 */
928 rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pThis->pDrvBase);
929 AssertMsgReturn(RT_SUCCESS(rc), ("Attaching driver below failed rc=%Rrc\n", rc), rc);
930
931 /*
932 * Query the block and blockbios interfaces.
933 */
934 pThis->pDrvBlock = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBLOCK);
935 if (!pThis->pDrvBlock)
936 {
937 AssertMsgFailed(("Configuration error: No block interface!\n"));
938 return VERR_PDM_MISSING_INTERFACE;
939 }
940 pThis->pDrvBlockBios = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBLOCKBIOS);
941 if (!pThis->pDrvBlockBios)
942 {
943 AssertMsgFailed(("Configuration error: No block BIOS interface!\n"));
944 return VERR_PDM_MISSING_INTERFACE;
945 }
946
947 pThis->pDrvMount = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMOUNT);
948
949 /* Try to get the optional async block interface. */
950 pThis->pDrvBlockAsync = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBLOCKASYNC);
951
952 PDMBLOCKTYPE enmType = pThis->pDrvBlock->pfnGetType(pThis->pDrvBlock);
953 VSCSILUNTYPE enmLunType;
954 switch (enmType)
955 {
956 case PDMBLOCKTYPE_HARD_DISK:
957 enmLunType = VSCSILUNTYPE_SBC;
958 break;
959 case PDMBLOCKTYPE_CDROM:
960 case PDMBLOCKTYPE_DVD:
961 enmLunType = VSCSILUNTYPE_MMC;
962 break;
963 default:
964 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_UNSUPPORTED_BLOCK_TYPE, RT_SRC_POS,
965 N_("Only hard disks and CD/DVD-ROMs are currently supported as SCSI devices (enmType=%d)"),
966 enmType);
967 }
968 if ( ( enmType == PDMBLOCKTYPE_DVD
969 || enmType == PDMBLOCKTYPE_CDROM)
970 && !pThis->pDrvMount)
971 {
972 AssertMsgFailed(("Internal error: cdrom without a mountable interface\n"));
973 return VERR_INTERNAL_ERROR;
974 }
975
976 /* Create VSCSI device and LUN. */
977 pThis->VScsiIoCallbacks.pfnVScsiLunMediumGetSize = drvscsiGetSize;
978 pThis->VScsiIoCallbacks.pfnVScsiLunReqTransferEnqueue = drvscsiReqTransferEnqueue;
979 pThis->VScsiIoCallbacks.pfnVScsiLunGetFeatureFlags = drvscsiGetFeatureFlags;
980 pThis->VScsiIoCallbacks.pfnVScsiLunMediumSetLock = drvscsiSetLock;
981
982 rc = VSCSIDeviceCreate(&pThis->hVScsiDevice, drvscsiVScsiReqCompleted, pThis);
983 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI device rc=%Rrc\n"), rc);
984 rc = VSCSILunCreate(&pThis->hVScsiLun, enmLunType, &pThis->VScsiIoCallbacks,
985 pThis);
986 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI LUN rc=%Rrc\n"), rc);
987 rc = VSCSIDeviceLunAttach(pThis->hVScsiDevice, pThis->hVScsiLun, 0);
988 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to attached the LUN to the SCSI device\n"), rc);
989
990 //@todo: This is a very hacky way of telling the LUN whether a medium was mounted.
991 // The mount/unmount interface doesn't work in a very sensible manner!
992 if (pThis->pDrvMount)
993 {
994 if (pThis->pDrvBlock->pfnGetSize(pThis->pDrvBlock))
995 {
996 rc = VINF_SUCCESS; VSCSILunMountNotify(pThis->hVScsiLun);
997 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to notify the LUN of media being mounted\n"), rc);
998 }
999 else
1000 {
1001 rc = VINF_SUCCESS; VSCSILunUnmountNotify(pThis->hVScsiLun);
1002 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to notify the LUN of media being unmounted\n"), rc);
1003 }
1004 }
1005
1006 /* Register statistics counter. */
1007 /** @todo aeichner: Find a way to put the instance number of the attached
1008 * controller device when we support more than one controller of the same type.
1009 * At the moment we have the 0 hardcoded. */
1010 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
1011 "Amount of data read.", "/Devices/SCSI0/%d/ReadBytes", pDrvIns->iInstance);
1012 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
1013 "Amount of data written.", "/Devices/SCSI0/%d/WrittenBytes", pDrvIns->iInstance);
1014
1015 pThis->StatIoDepth = 0;
1016
1017 PDMDrvHlpSTAMRegisterF(pDrvIns, (void *)&pThis->StatIoDepth, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
1018 "Number of active tasks.", "/Devices/SCSI0/%d/IoDepth", pDrvIns->iInstance);
1019
1020 if (!pThis->pDrvBlockAsync)
1021 {
1022 /* Create request queue. */
1023 rc = RTReqQueueCreate(&pThis->hQueueRequests);
1024 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create request queue rc=%Rrc\n"), rc);
1025 /* Create I/O thread. */
1026 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pAsyncIOThread, pThis, drvscsiAsyncIOLoop,
1027 drvscsiAsyncIOLoopWakeup, 0, RTTHREADTYPE_IO, "SCSI async IO");
1028 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create async I/O thread rc=%Rrc\n"), rc);
1029
1030 LogRel(("SCSI#%d: using normal I/O\n", pDrvIns->iInstance));
1031 }
1032 else
1033 LogRel(("SCSI#%d: using async I/O\n", pDrvIns->iInstance));
1034
1035 if ( pThis->pDrvBlock->pfnDiscard
1036 || ( pThis->pDrvBlockAsync
1037 && pThis->pDrvBlockAsync->pfnStartDiscard))
1038 LogRel(("SCSI#%d: Enabled UNMAP support\n"));
1039
1040 return VINF_SUCCESS;
1041}
1042
1043/**
1044 * SCSI driver registration record.
1045 */
1046const PDMDRVREG g_DrvSCSI =
1047{
1048 /* u32Version */
1049 PDM_DRVREG_VERSION,
1050 /* szName */
1051 "SCSI",
1052 /* szRCMod */
1053 "",
1054 /* szR0Mod */
1055 "",
1056 /* pszDescription */
1057 "Generic SCSI driver.",
1058 /* fFlags */
1059 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1060 /* fClass. */
1061 PDM_DRVREG_CLASS_SCSI,
1062 /* cMaxInstances */
1063 ~0U,
1064 /* cbInstance */
1065 sizeof(DRVSCSI),
1066 /* pfnConstruct */
1067 drvscsiConstruct,
1068 /* pfnDestruct */
1069 drvscsiDestruct,
1070 /* pfnRelocate */
1071 NULL,
1072 /* pfnIOCtl */
1073 NULL,
1074 /* pfnPowerOn */
1075 NULL,
1076 /* pfnReset */
1077 drvscsiReset,
1078 /* pfnSuspend */
1079 drvscsiSuspend,
1080 /* pfnResume */
1081 NULL,
1082 /* pfnAttach */
1083 NULL,
1084 /* pfnDetach */
1085 NULL,
1086 /* pfnPowerOff */
1087 drvscsiPowerOff,
1088 /* pfnSoftReset */
1089 NULL,
1090 /* u32EndVersion */
1091 PDM_DRVREG_VERSION
1092};
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