VirtualBox

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

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

DrvSCSI: Don't report that all data was transfered if the status indicates an error + todo to implement that residual counters properly

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 54.3 KB
Line 
1/* $Id: DrvSCSI.cpp 64441 2016-10-27 14:54:04Z 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/pdmqueue.h>
27#include <VBox/vmm/pdmstorageifs.h>
28#include <VBox/vmm/pdmthread.h>
29#include <VBox/vscsi.h>
30#include <VBox/scsi.h>
31#include <iprt/asm.h>
32#include <iprt/assert.h>
33#include <iprt/mem.h>
34#include <iprt/req.h>
35#include <iprt/semaphore.h>
36#include <iprt/string.h>
37#include <iprt/uuid.h>
38
39#include "VBoxDD.h"
40
41/** The maximum number of release log entries per device. */
42#define MAX_LOG_REL_ERRORS 1024
43
44/**
45 * Eject state.
46 */
47typedef struct DRVSCSIEJECTSTATE
48{
49 /** The item core for the PDM queue. */
50 PDMQUEUEITEMCORE Core;
51 /** Event semaphore to signal when complete. */
52 RTSEMEVENT hSemEvt;
53 /** Status of the eject operation. */
54 int rcReq;
55} DRVSCSIEJECTSTATE;
56typedef DRVSCSIEJECTSTATE *PDRVSCSIEJECTSTATE;
57
58/**
59 * SCSI driver private per request data.
60 */
61typedef struct DRVSCSIREQ
62{
63 /** Size of the guest buffer. */
64 size_t cbBuf;
65 /** Temporary buffer holding the data. */
66 void *pvBuf;
67 /** Data segment. */
68 RTSGSEG Seg;
69 /** Transfer direction. */
70 PDMMEDIAEXIOREQSCSITXDIR enmXferDir;
71 /** The VSCSI request handle. */
72 VSCSIREQ hVScsiReq;
73 /** Where to store the SCSI status code. */
74 uint8_t *pu8ScsiSts;
75 /** Start of the request data for the device above us. */
76 uint8_t abAlloc[1];
77} DRVSCSIREQ;
78/** Pointer to the driver private per request data. */
79typedef DRVSCSIREQ *PDRVSCSIREQ;
80
81/**
82 * SCSI driver instance data.
83 *
84 * @implements PDMIMEDIAEXPORT
85 * @implements PDMIMEDIAEX
86 * @implements PDMIMOUNTNOTIFY
87 */
88typedef struct DRVSCSI
89{
90 /** Pointer driver instance. */
91 PPDMDRVINS pDrvIns;
92
93 /** Pointer to the attached driver's base interface. */
94 PPDMIBASE pDrvBase;
95 /** Pointer to the attached driver's block interface. */
96 PPDMIMEDIA pDrvMedia;
97 /** Pointer to the attached driver's extended media interface. */
98 PPDMIMEDIAEX pDrvMediaEx;
99 /** Pointer to the attached driver's mount interface. */
100 PPDMIMOUNT pDrvMount;
101 /** Pointer to the extended media port interface of the device above. */
102 PPDMIMEDIAEXPORT pDevMediaExPort;
103 /** Pointer to the media port interface of the device above. */
104 PPDMIMEDIAPORT pDevMediaPort;
105 /** pointer to the Led port interface of the dveice above. */
106 PPDMILEDPORTS pLedPort;
107 /** The media interface for the device above. */
108 PDMIMEDIA IMedia;
109 /** The extended media interface for the device above. */
110 PDMIMEDIAEX IMediaEx;
111 /** The media port interface. */
112 PDMIMEDIAPORT IPort;
113 /** The optional extended media port interface. */
114 PDMIMEDIAEXPORT IPortEx;
115 /** The mount notify interface. */
116 PDMIMOUNTNOTIFY IMountNotify;
117 /** Fallback status LED state for this drive.
118 * This is used in case the device doesn't has a LED interface. */
119 PDMLED Led;
120 /** Pointer to the status LED for this drive. */
121 PPDMLED pLed;
122
123 /** VSCSI device handle. */
124 VSCSIDEVICE hVScsiDevice;
125 /** VSCSI LUN handle. */
126 VSCSILUN hVScsiLun;
127 /** I/O callbacks. */
128 VSCSILUNIOCALLBACKS VScsiIoCallbacks;
129
130 /** Indicates whether PDMDrvHlpAsyncNotificationCompleted should be called by
131 * any of the dummy functions. */
132 bool volatile fDummySignal;
133 /** Release statistics: number of bytes written. */
134 STAMCOUNTER StatBytesWritten;
135 /** Release statistics: number of bytes read. */
136 STAMCOUNTER StatBytesRead;
137 /** Release statistics: Current I/O depth. */
138 volatile uint32_t StatIoDepth;
139 /** Errors printed in the release log. */
140 unsigned cErrors;
141
142 /** Size of the I/O request to allocate. */
143 size_t cbIoReqAlloc;
144 /** Size of a VSCSI I/O request. */
145 size_t cbVScsiIoReqAlloc;
146 /** Queue to defer unmounting to EMT. */
147 PPDMQUEUE pQueue;
148} DRVSCSI, *PDRVSCSI;
149
150/** Convert a VSCSI I/O request handle to the associated PDMIMEDIAEX I/O request. */
151#define DRVSCSI_VSCSIIOREQ_2_PDMMEDIAEXIOREQ(a_hVScsiIoReq) (*(PPDMMEDIAEXIOREQ)((uint8_t *)(a_hVScsiIoReq) - sizeof(PDMMEDIAEXIOREQ)))
152/** Convert a PDMIMEDIAEX I/O additional request memory to a VSCSI I/O request. */
153#define DRVSCSI_PDMMEDIAEXIOREQ_2_VSCSIIOREQ(a_pvIoReqAlloc) ((VSCSIIOREQ)((uint8_t *)(a_pvIoReqAlloc) + sizeof(PDMMEDIAEXIOREQ)))
154
155/**
156 * Returns whether the given status code indicates a non fatal error.
157 *
158 * @returns True if the error can be fixed by the user after the VM was suspended.
159 * False if not and the error should be reported to the guest.
160 * @param rc The status code to check.
161 */
162DECLINLINE(bool) drvscsiIsRedoPossible(int rc)
163{
164 if ( rc == VERR_DISK_FULL
165 || rc == VERR_FILE_TOO_BIG
166 || rc == VERR_BROKEN_PIPE
167 || rc == VERR_NET_CONNECTION_REFUSED
168 || rc == VERR_VD_DEK_MISSING)
169 return true;
170
171 return false;
172}
173
174/* -=-=-=-=- VScsiIoCallbacks -=-=-=-=- */
175
176/**
177 * @interface_method_impl{VSCSILUNIOCALLBACKS,pfnVScsiLunReqAllocSizeSet}
178 */
179static DECLCALLBACK(int) drvscsiReqAllocSizeSet(VSCSILUN hVScsiLun, void *pvScsiLunUser, size_t cbVScsiIoReqAlloc)
180{
181 RT_NOREF(hVScsiLun);
182 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
183
184 /* We need to store the I/O request handle so we can get it when VSCSI queues an I/O request. */
185 int rc = pThis->pDrvMediaEx->pfnIoReqAllocSizeSet(pThis->pDrvMediaEx, cbVScsiIoReqAlloc + sizeof(PDMMEDIAEXIOREQ));
186 if (RT_SUCCESS(rc))
187 pThis->cbVScsiIoReqAlloc = cbVScsiIoReqAlloc + sizeof(PDMMEDIAEXIOREQ);
188
189 return rc;
190}
191
192/**
193 * @interface_method_impl{VSCSILUNIOCALLBACKS,pfnVScsiLunReqAlloc}
194 */
195static DECLCALLBACK(int) drvscsiReqAlloc(VSCSILUN hVScsiLun, void *pvScsiLunUser,
196 uint64_t u64Tag, PVSCSIIOREQ phVScsiIoReq)
197{
198 RT_NOREF(hVScsiLun);
199 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
200 PDMMEDIAEXIOREQ hIoReq;
201 void *pvIoReqAlloc;
202 int rc = pThis->pDrvMediaEx->pfnIoReqAlloc(pThis->pDrvMediaEx, &hIoReq, &pvIoReqAlloc, u64Tag,
203 PDMIMEDIAEX_F_SUSPEND_ON_RECOVERABLE_ERR);
204 if (RT_SUCCESS(rc))
205 {
206 PPDMMEDIAEXIOREQ phIoReq = (PPDMMEDIAEXIOREQ)pvIoReqAlloc;
207
208 *phIoReq = hIoReq;
209 *phVScsiIoReq = (VSCSIIOREQ)(phIoReq + 1);
210 }
211
212 return rc;
213}
214
215/**
216 * @interface_method_impl{VSCSILUNIOCALLBACKS,pfnVScsiLunReqFree}
217 */
218static DECLCALLBACK(int) drvscsiReqFree(VSCSILUN hVScsiLun, void *pvScsiLunUser, VSCSIIOREQ hVScsiIoReq)
219{
220 RT_NOREF(hVScsiLun);
221 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
222 PDMMEDIAEXIOREQ hIoReq = DRVSCSI_VSCSIIOREQ_2_PDMMEDIAEXIOREQ(hVScsiIoReq);
223
224 return pThis->pDrvMediaEx->pfnIoReqFree(pThis->pDrvMediaEx, hIoReq);
225}
226
227/**
228 * @interface_method_impl{VSCSILUNIOCALLBACKS,pfnVScsiLunMediumGetSize}
229 */
230static DECLCALLBACK(int) drvscsiGetSize(VSCSILUN hVScsiLun, void *pvScsiLunUser, uint64_t *pcbSize)
231{
232 RT_NOREF(hVScsiLun);
233 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
234
235 *pcbSize = pThis->pDrvMedia->pfnGetSize(pThis->pDrvMedia);
236
237 return VINF_SUCCESS;
238}
239
240/**
241 * @interface_method_impl{VSCSILUNIOCALLBACKS,pfnVScsiLunMediumGetSectorSize}
242 */
243static DECLCALLBACK(int) drvscsiGetSectorSize(VSCSILUN hVScsiLun, void *pvScsiLunUser, uint32_t *pcbSectorSize)
244{
245 RT_NOREF(hVScsiLun);
246 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
247
248 *pcbSectorSize = pThis->pDrvMedia->pfnGetSectorSize(pThis->pDrvMedia);
249
250 return VINF_SUCCESS;
251}
252
253/**
254 * @interface_method_impl{VSCSILUNIOCALLBACKS,pfnVScsiLunMediumSetLock}
255 */
256static DECLCALLBACK(int) drvscsiSetLock(VSCSILUN hVScsiLun, void *pvScsiLunUser, bool fLocked)
257{
258 RT_NOREF(hVScsiLun);
259 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
260
261 if (fLocked)
262 pThis->pDrvMount->pfnLock(pThis->pDrvMount);
263 else
264 pThis->pDrvMount->pfnUnlock(pThis->pDrvMount);
265
266 return VINF_SUCCESS;
267}
268
269/** @interface_method_impl{VSCSILUNIOCALLBACKS,pfnVScsiLunMediumEject} */
270static DECLCALLBACK(int) drvscsiEject(VSCSILUN hVScsiLun, void *pvScsiLunUser)
271{
272 RT_NOREF(hVScsiLun);
273 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
274 int rc = VINF_SUCCESS;
275 RTSEMEVENT hSemEvt = NIL_RTSEMEVENT;
276
277 /* This must be done from EMT. */
278 rc = RTSemEventCreate(&hSemEvt);
279 if (RT_SUCCESS(rc))
280 {
281 PDRVSCSIEJECTSTATE pEjectState = (PDRVSCSIEJECTSTATE)PDMQueueAlloc(pThis->pQueue);
282 if (pEjectState)
283 {
284 pEjectState->hSemEvt = hSemEvt;
285 PDMQueueInsert(pThis->pQueue, &pEjectState->Core);
286
287 /* Wait for completion. */
288 rc = RTSemEventWait(pEjectState->hSemEvt, RT_INDEFINITE_WAIT);
289 if (RT_SUCCESS(rc))
290 rc = pEjectState->rcReq;
291 }
292 else
293 rc = VERR_NO_MEMORY;
294
295 RTSemEventDestroy(pEjectState->hSemEvt);
296 }
297
298 return rc;
299}
300
301/**
302 * @interface_method_impl{VSCSILUNIOCALLBACKS,pfnVScsiLunReqTransferEnqueue}
303 */
304static DECLCALLBACK(int) drvscsiReqTransferEnqueue(VSCSILUN hVScsiLun, void *pvScsiLunUser, VSCSIIOREQ hVScsiIoReq)
305{
306 RT_NOREF(hVScsiLun);
307 int rc = VINF_SUCCESS;
308 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
309 PDMMEDIAEXIOREQ hIoReq = DRVSCSI_VSCSIIOREQ_2_PDMMEDIAEXIOREQ(hVScsiIoReq);
310
311 LogFlowFunc(("Enqueuing hVScsiIoReq=%#p\n", hVScsiIoReq));
312
313 VSCSIIOREQTXDIR enmTxDir = VSCSIIoReqTxDirGet(hVScsiIoReq);
314 switch (enmTxDir)
315 {
316 case VSCSIIOREQTXDIR_FLUSH:
317 {
318 rc = pThis->pDrvMediaEx->pfnIoReqFlush(pThis->pDrvMediaEx, hIoReq);
319 if ( RT_FAILURE(rc)
320 && pThis->cErrors++ < MAX_LOG_REL_ERRORS)
321 LogRel(("SCSI#%u: Flush returned rc=%Rrc\n",
322 pThis->pDrvIns->iInstance, rc));
323 break;
324 }
325 case VSCSIIOREQTXDIR_UNMAP:
326 {
327 PCRTRANGE paRanges;
328 unsigned cRanges;
329
330 rc = VSCSIIoReqUnmapParamsGet(hVScsiIoReq, &paRanges, &cRanges);
331 AssertRC(rc);
332
333 pThis->pLed->Asserted.s.fWriting = pThis->pLed->Actual.s.fWriting = 1;
334 rc = pThis->pDrvMediaEx->pfnIoReqDiscard(pThis->pDrvMediaEx, hIoReq, cRanges);
335 if ( RT_FAILURE(rc)
336 && pThis->cErrors++ < MAX_LOG_REL_ERRORS)
337 LogRel(("SCSI#%u: Discard returned rc=%Rrc\n",
338 pThis->pDrvIns->iInstance, rc));
339 break;
340 }
341 case VSCSIIOREQTXDIR_READ:
342 case VSCSIIOREQTXDIR_WRITE:
343 {
344 uint64_t uOffset = 0;
345 size_t cbTransfer = 0;
346 size_t cbSeg = 0;
347 PCRTSGSEG paSeg = NULL;
348 unsigned cSeg = 0;
349
350 rc = VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer,
351 &cSeg, &cbSeg, &paSeg);
352 AssertRC(rc);
353
354 if (enmTxDir == VSCSIIOREQTXDIR_READ)
355 {
356 pThis->pLed->Asserted.s.fReading = pThis->pLed->Actual.s.fReading = 1;
357 rc = pThis->pDrvMediaEx->pfnIoReqRead(pThis->pDrvMediaEx, hIoReq, uOffset, cbTransfer);
358 STAM_REL_COUNTER_ADD(&pThis->StatBytesRead, cbTransfer);
359 }
360 else
361 {
362 pThis->pLed->Asserted.s.fWriting = pThis->pLed->Actual.s.fWriting = 1;
363 rc = pThis->pDrvMediaEx->pfnIoReqWrite(pThis->pDrvMediaEx, hIoReq, uOffset, cbTransfer);
364 STAM_REL_COUNTER_ADD(&pThis->StatBytesWritten, cbTransfer);
365 }
366
367 if ( RT_FAILURE(rc)
368 && pThis->cErrors++ < MAX_LOG_REL_ERRORS)
369 LogRel(("SCSI#%u: %s at offset %llu (%u bytes left) returned rc=%Rrc\n",
370 pThis->pDrvIns->iInstance,
371 enmTxDir == VSCSIIOREQTXDIR_READ
372 ? "Read"
373 : "Write",
374 uOffset,
375 cbTransfer, rc));
376 break;
377 }
378 default:
379 AssertMsgFailed(("Invalid transfer direction %u\n", enmTxDir));
380 }
381
382 if (rc == VINF_SUCCESS)
383 {
384 if (enmTxDir == VSCSIIOREQTXDIR_READ)
385 pThis->pLed->Actual.s.fReading = 0;
386 else if (enmTxDir == VSCSIIOREQTXDIR_WRITE)
387 pThis->pLed->Actual.s.fWriting = 0;
388 else
389 AssertMsg(enmTxDir == VSCSIIOREQTXDIR_FLUSH, ("Invalid transfer direction %u\n", enmTxDir));
390
391 VSCSIIoReqCompleted(hVScsiIoReq, VINF_SUCCESS, false);
392 rc = VINF_SUCCESS;
393 }
394 else if (rc == VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS)
395 rc = VINF_SUCCESS;
396 else if (RT_FAILURE(rc))
397 {
398 if (enmTxDir == VSCSIIOREQTXDIR_READ)
399 pThis->pLed->Actual.s.fReading = 0;
400 else if (enmTxDir == VSCSIIOREQTXDIR_WRITE)
401 pThis->pLed->Actual.s.fWriting = 0;
402 else
403 AssertMsg(enmTxDir == VSCSIIOREQTXDIR_FLUSH, ("Invalid transfer direction %u\n", enmTxDir));
404
405 VSCSIIoReqCompleted(hVScsiIoReq, rc, drvscsiIsRedoPossible(rc));
406 rc = VINF_SUCCESS;
407 }
408 else
409 AssertMsgFailed(("Invalid return code rc=%Rrc\n", rc));
410
411 return rc;
412}
413
414/**
415 * @interface_method_impl{VSCSILUNIOCALLBACKS,pfnVScsiLunGetFeatureFlags}
416 */
417static DECLCALLBACK(int) drvscsiGetFeatureFlags(VSCSILUN hVScsiLun, void *pvScsiLunUser, uint64_t *pfFeatures)
418{
419 RT_NOREF(hVScsiLun);
420 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
421
422 *pfFeatures = 0;
423
424 uint32_t fFeatures = 0;
425 int rc = pThis->pDrvMediaEx->pfnQueryFeatures(pThis->pDrvMediaEx, &fFeatures);
426 if (RT_SUCCESS(rc) && (fFeatures & PDMIMEDIAEX_FEATURE_F_DISCARD))
427 *pfFeatures |= VSCSI_LUN_FEATURE_UNMAP;
428
429 if ( pThis->pDrvMedia
430 && pThis->pDrvMedia->pfnIsNonRotational(pThis->pDrvMedia))
431 *pfFeatures |= VSCSI_LUN_FEATURE_NON_ROTATIONAL;
432
433 if (pThis->pDrvMedia->pfnIsReadOnly(pThis->pDrvMedia))
434 *pfFeatures |= VSCSI_LUN_FEATURE_READONLY;
435
436 return VINF_SUCCESS;
437}
438
439
440/* -=-=-=-=- IPortEx -=-=-=-=- */
441
442/**
443 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCompleteNotify}
444 */
445static DECLCALLBACK(int) drvscsiIoReqCompleteNotify(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
446 void *pvIoReqAlloc, int rcReq)
447{
448 RT_NOREF1(hIoReq);
449
450 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IPortEx);
451 VSCSIIOREQ hVScsiIoReq = DRVSCSI_PDMMEDIAEXIOREQ_2_VSCSIIOREQ(pvIoReqAlloc);
452 VSCSIIOREQTXDIR enmTxDir = VSCSIIoReqTxDirGet(hVScsiIoReq);
453
454 LogFlowFunc(("Request hVScsiIoReq=%#p completed\n", hVScsiIoReq));
455
456 if (enmTxDir == VSCSIIOREQTXDIR_READ)
457 pThis->pLed->Actual.s.fReading = 0;
458 else if ( enmTxDir == VSCSIIOREQTXDIR_WRITE
459 || enmTxDir == VSCSIIOREQTXDIR_UNMAP)
460 pThis->pLed->Actual.s.fWriting = 0;
461 else
462 AssertMsg(enmTxDir == VSCSIIOREQTXDIR_FLUSH, ("Invalid transfer direction %u\n", enmTxDir));
463
464 if (RT_SUCCESS(rcReq))
465 VSCSIIoReqCompleted(hVScsiIoReq, rcReq, false /* fRedoPossible */);
466 else
467 {
468 pThis->cErrors++;
469 if (pThis->cErrors < MAX_LOG_REL_ERRORS)
470 {
471 if (enmTxDir == VSCSIIOREQTXDIR_FLUSH)
472 LogRel(("SCSI#%u: Flush returned rc=%Rrc\n",
473 pThis->pDrvIns->iInstance, rcReq));
474 else if (enmTxDir == VSCSIIOREQTXDIR_UNMAP)
475 LogRel(("SCSI#%u: Unmap returned rc=%Rrc\n",
476 pThis->pDrvIns->iInstance, rcReq));
477 else
478 {
479 uint64_t uOffset = 0;
480 size_t cbTransfer = 0;
481 size_t cbSeg = 0;
482 PCRTSGSEG paSeg = NULL;
483 unsigned cSeg = 0;
484
485 VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer,
486 &cSeg, &cbSeg, &paSeg);
487
488 LogRel(("SCSI#%u: %s at offset %llu (%u bytes left) returned rc=%Rrc\n",
489 pThis->pDrvIns->iInstance,
490 enmTxDir == VSCSIIOREQTXDIR_READ
491 ? "Read"
492 : "Write",
493 uOffset,
494 cbTransfer, rcReq));
495 }
496 }
497
498 VSCSIIoReqCompleted(hVScsiIoReq, rcReq, drvscsiIsRedoPossible(rcReq));
499 }
500
501 return VINF_SUCCESS;
502}
503
504/**
505 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCopyFromBuf}
506 */
507static DECLCALLBACK(int) drvscsiIoReqCopyFromBuf(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
508 void *pvIoReqAlloc, uint32_t offDst, PRTSGBUF pSgBuf,
509 size_t cbCopy)
510{
511 RT_NOREF2(pInterface, hIoReq);
512
513 VSCSIIOREQ hVScsiIoReq = DRVSCSI_PDMMEDIAEXIOREQ_2_VSCSIIOREQ(pvIoReqAlloc);
514 uint64_t uOffset = 0;
515 size_t cbTransfer = 0;
516 size_t cbSeg = 0;
517 PCRTSGSEG paSeg = NULL;
518 unsigned cSeg = 0;
519 size_t cbCopied = 0;
520
521 int rc = VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer, &cSeg, &cbSeg, &paSeg);
522 if (RT_SUCCESS(rc))
523 {
524 RTSGBUF SgBuf;
525 RTSgBufInit(&SgBuf, paSeg, cSeg);
526
527 RTSgBufAdvance(&SgBuf, offDst);
528 cbCopied = RTSgBufCopy(&SgBuf, pSgBuf, cbCopy);
529 }
530
531 return cbCopied == cbCopy ? VINF_SUCCESS : VERR_PDM_MEDIAEX_IOBUF_OVERFLOW;
532}
533
534/**
535 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCopyToBuf}
536 */
537static DECLCALLBACK(int) drvscsiIoReqCopyToBuf(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
538 void *pvIoReqAlloc, uint32_t offSrc, PRTSGBUF pSgBuf,
539 size_t cbCopy)
540{
541 RT_NOREF2(pInterface, hIoReq);
542
543 VSCSIIOREQ hVScsiIoReq = DRVSCSI_PDMMEDIAEXIOREQ_2_VSCSIIOREQ(pvIoReqAlloc);
544 uint64_t uOffset = 0;
545 size_t cbTransfer = 0;
546 size_t cbSeg = 0;
547 PCRTSGSEG paSeg = NULL;
548 unsigned cSeg = 0;
549 size_t cbCopied = 0;
550
551 int rc = VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer, &cSeg, &cbSeg, &paSeg);
552 if (RT_SUCCESS(rc))
553 {
554 RTSGBUF SgBuf;
555 RTSgBufInit(&SgBuf, paSeg, cSeg);
556
557 RTSgBufAdvance(&SgBuf, offSrc);
558 cbCopied = RTSgBufCopy(pSgBuf, &SgBuf, cbCopy);
559 }
560
561 return cbCopied == cbCopy ? VINF_SUCCESS : VERR_PDM_MEDIAEX_IOBUF_UNDERRUN;
562}
563
564/**
565 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqQueryDiscardRanges}
566 */
567static DECLCALLBACK(int) drvscsiIoReqQueryDiscardRanges(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
568 void *pvIoReqAlloc, uint32_t idxRangeStart,
569 uint32_t cRanges, PRTRANGE paRanges,
570 uint32_t *pcRanges)
571{
572 RT_NOREF2(pInterface, hIoReq);
573
574 VSCSIIOREQ hVScsiIoReq = DRVSCSI_PDMMEDIAEXIOREQ_2_VSCSIIOREQ(pvIoReqAlloc);
575 PCRTRANGE paRangesVScsi;
576 unsigned cRangesVScsi;
577
578 int rc = VSCSIIoReqUnmapParamsGet(hVScsiIoReq, &paRangesVScsi, &cRangesVScsi);
579 if (RT_SUCCESS(rc))
580 {
581 uint32_t cRangesCopy = RT_MIN(cRangesVScsi - idxRangeStart, cRanges);
582 Assert( idxRangeStart < cRangesVScsi
583 && (idxRangeStart + cRanges) <= cRangesVScsi);
584
585 memcpy(paRanges, &paRangesVScsi[idxRangeStart], cRangesCopy * sizeof(RTRANGE));
586 *pcRanges = cRangesCopy;
587 }
588 return rc;
589}
590
591/**
592 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqStateChanged}
593 */
594static DECLCALLBACK(void) drvscsiIoReqStateChanged(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
595 void *pvIoReqAlloc, PDMMEDIAEXIOREQSTATE enmState)
596{
597 RT_NOREF2(hIoReq, pvIoReqAlloc);
598 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IPortEx);
599
600 switch (enmState)
601 {
602 case PDMMEDIAEXIOREQSTATE_SUSPENDED:
603 {
604 /* Make sure the request is not accounted for so the VM can suspend successfully. */
605 uint32_t cTasksActive = ASMAtomicDecU32(&pThis->StatIoDepth);
606 if (!cTasksActive && pThis->fDummySignal)
607 PDMDrvHlpAsyncNotificationCompleted(pThis->pDrvIns);
608 break;
609 }
610 case PDMMEDIAEXIOREQSTATE_ACTIVE:
611 /* Make sure the request is accounted for so the VM suspends only when the request is complete. */
612 ASMAtomicIncU32(&pThis->StatIoDepth);
613 break;
614 default:
615 AssertMsgFailed(("Invalid request state given %u\n", enmState));
616 }
617
618 pThis->pDevMediaExPort->pfnIoReqStateChanged(pThis->pDevMediaExPort, hIoReq, pvIoReqAlloc, enmState);
619}
620
621
622/* -=-=-=-=- IMedia -=-=-=-=- */
623
624/** @interface_method_impl{PDMIMEDIA,pfnGetSize} */
625static DECLCALLBACK(uint64_t) drvscsiGetSize(PPDMIMEDIA pInterface)
626{
627 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMedia);
628 return pThis->pDrvMedia->pfnGetSize(pThis->pDrvMedia);
629}
630
631/** @interface_method_impl{PDMIMEDIA,pfnGetSectorSize} */
632static DECLCALLBACK(uint32_t) drvscsiGetSectorSize(PPDMIMEDIA pInterface)
633{
634 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMedia);
635 return pThis->pDrvMedia->pfnGetSectorSize(pThis->pDrvMedia);
636}
637
638/** @interface_method_impl{PDMIMEDIA,pfnIsReadOnly} */
639static DECLCALLBACK(bool) drvscsiIsReadOnly(PPDMIMEDIA pInterface)
640{
641 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMedia);
642 return pThis->pDrvMedia->pfnIsReadOnly(pThis->pDrvMedia);
643}
644
645/** @interface_method_impl{PDMIMEDIA,pfnIsNonRotational} */
646static DECLCALLBACK(bool) drvscsiIsNonRotational(PPDMIMEDIA pInterface)
647{
648 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMedia);
649 return pThis->pDrvMedia->pfnIsNonRotational(pThis->pDrvMedia);
650}
651
652/** @interface_method_impl{PDMIMEDIA,pfnBiosGetPCHSGeometry} */
653static DECLCALLBACK(int) drvscsiBiosGetPCHSGeometry(PPDMIMEDIA pInterface,
654 PPDMMEDIAGEOMETRY pPCHSGeometry)
655{
656 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMedia);
657 return pThis->pDrvMedia->pfnBiosGetPCHSGeometry(pThis->pDrvMedia, pPCHSGeometry);
658}
659
660/** @interface_method_impl{PDMIMEDIA,pfnBiosSetPCHSGeometry} */
661static DECLCALLBACK(int) drvscsiBiosSetPCHSGeometry(PPDMIMEDIA pInterface,
662 PCPDMMEDIAGEOMETRY pPCHSGeometry)
663{
664 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMedia);
665 return pThis->pDrvMedia->pfnBiosSetPCHSGeometry(pThis->pDrvMedia, pPCHSGeometry);
666}
667
668/** @interface_method_impl{PDMIMEDIA,pfnBiosGetLCHSGeometry} */
669static DECLCALLBACK(int) drvscsiBiosGetLCHSGeometry(PPDMIMEDIA pInterface,
670 PPDMMEDIAGEOMETRY pLCHSGeometry)
671{
672 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMedia);
673 return pThis->pDrvMedia->pfnBiosGetLCHSGeometry(pThis->pDrvMedia, pLCHSGeometry);
674}
675
676/** @interface_method_impl{PDMIMEDIA,pfnBiosSetLCHSGeometry} */
677static DECLCALLBACK(int) drvscsiBiosSetLCHSGeometry(PPDMIMEDIA pInterface,
678 PCPDMMEDIAGEOMETRY pLCHSGeometry)
679{
680 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMedia);
681 return pThis->pDrvMedia->pfnBiosSetLCHSGeometry(pThis->pDrvMedia, pLCHSGeometry);
682}
683
684/** @interface_method_impl{PDMIMEDIA,pfnBiosIsVisible} */
685static DECLCALLBACK(bool) drvscsiBiosIsVisible(PPDMIMEDIA pInterface)
686{
687 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMedia);
688 return pThis->pDrvMedia->pfnBiosIsVisible(pThis->pDrvMedia);
689}
690
691/** @interface_method_impl{PDMIMEDIA,pfnGetType} */
692static DECLCALLBACK(PDMMEDIATYPE) drvscsiGetType(PPDMIMEDIA pInterface)
693{
694 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMedia);
695 VSCSILUNTYPE enmLunType;
696 PDMMEDIATYPE enmMediaType = PDMMEDIATYPE_ERROR;
697
698 int rc = VSCSIDeviceLunQueryType(pThis->hVScsiDevice, 0, &enmLunType);
699 if (RT_SUCCESS(rc))
700 {
701 switch (enmLunType)
702 {
703 case VSCSILUNTYPE_SBC:
704 enmMediaType = PDMMEDIATYPE_HARD_DISK;
705 break;
706 case VSCSILUNTYPE_MMC:
707 enmMediaType = PDMMEDIATYPE_CDROM;
708 break;
709 default:
710 enmMediaType = PDMMEDIATYPE_ERROR;
711 break;
712 }
713 }
714
715 return enmMediaType;
716}
717
718/** @interface_method_impl{PDMIMEDIA,pfnGetUuid} */
719static DECLCALLBACK(int) drvscsiGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
720{
721 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMedia);
722
723 int rc = VINF_SUCCESS;
724 if (pThis->pDrvMedia)
725 rc = pThis->pDrvMedia->pfnGetUuid(pThis->pDrvMedia, pUuid);
726 else
727 RTUuidClear(pUuid);
728
729 return rc;
730}
731
732/* -=-=-=-=- IMediaEx -=-=-=-=- */
733
734/** @interface_method_impl{PDMIMEDIAEX,pfnQueryFeatures} */
735static DECLCALLBACK(int) drvscsiQueryFeatures(PPDMIMEDIAEX pInterface, uint32_t *pfFeatures)
736{
737 RT_NOREF1(pInterface);
738
739 *pfFeatures = PDMIMEDIAEX_FEATURE_F_RAWSCSICMD;
740 return VINF_SUCCESS;
741}
742
743/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqAllocSizeSet} */
744static DECLCALLBACK(int) drvscsiIoReqAllocSizeSet(PPDMIMEDIAEX pInterface, size_t cbIoReqAlloc)
745{
746 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMediaEx);
747
748 pThis->cbIoReqAlloc = RT_OFFSETOF(DRVSCSIREQ, abAlloc[cbIoReqAlloc]);
749 return VINF_SUCCESS;
750}
751
752/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqAlloc} */
753static DECLCALLBACK(int) drvscsiIoReqAlloc(PPDMIMEDIAEX pInterface, PPDMMEDIAEXIOREQ phIoReq, void **ppvIoReqAlloc,
754 PDMMEDIAEXIOREQID uIoReqId, uint32_t fFlags)
755{
756 RT_NOREF2(uIoReqId, fFlags);
757
758 int rc = VINF_SUCCESS;
759 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMediaEx);
760 PDRVSCSIREQ pReq = (PDRVSCSIREQ)RTMemAllocZ(pThis->cbIoReqAlloc);
761 if (RT_LIKELY(pReq))
762 {
763 *phIoReq = (PDMMEDIAEXIOREQ)pReq;
764 *ppvIoReqAlloc = &pReq->abAlloc[0];
765 }
766 else
767 rc = VERR_NO_MEMORY;
768
769 return rc;
770}
771
772/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqFree} */
773static DECLCALLBACK(int) drvscsiIoReqFree(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq)
774{
775 RT_NOREF1(pInterface);
776 PDRVSCSIREQ pReq = (PDRVSCSIREQ)hIoReq;
777
778 RTMemFree(pReq);
779 return VINF_SUCCESS;
780}
781
782/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqQueryResidual} */
783static DECLCALLBACK(int) drvscsiIoReqQueryResidual(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, size_t *pcbResidual)
784{
785 RT_NOREF1(pInterface);
786 PDRVSCSIREQ pReq = (PDRVSCSIREQ)hIoReq;
787
788 *pcbResidual = *pReq->pu8ScsiSts == SCSI_STATUS_OK ? 0 : pReq->cbBuf; /** @todo: Implement properly. */
789 return VINF_SUCCESS;
790}
791
792/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqQueryXferSize} */
793static DECLCALLBACK(int) drvscsiIoReqQueryXferSize(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, size_t *pcbXfer)
794{
795 RT_NOREF1(pInterface);
796 PDRVSCSIREQ pReq = (PDRVSCSIREQ)hIoReq;
797
798 *pcbXfer = pReq->cbBuf; /** @todo: Implement properly. */
799 return VINF_SUCCESS;
800}
801
802/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqCancelAll} */
803static DECLCALLBACK(int) drvscsiIoReqCancelAll(PPDMIMEDIAEX pInterface)
804{
805 RT_NOREF1(pInterface);
806 return VINF_SUCCESS;
807}
808
809/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqCancel} */
810static DECLCALLBACK(int) drvscsiIoReqCancel(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQID uIoReqId)
811{
812 RT_NOREF2(pInterface, uIoReqId);
813 return VERR_PDM_MEDIAEX_IOREQID_NOT_FOUND;
814}
815
816/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqRead} */
817static DECLCALLBACK(int) drvscsiIoReqRead(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, uint64_t off, size_t cbRead)
818{
819 RT_NOREF4(pInterface, hIoReq, off, cbRead);
820 return VERR_NOT_SUPPORTED;
821}
822
823/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqWrite} */
824static DECLCALLBACK(int) drvscsiIoReqWrite(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, uint64_t off, size_t cbWrite)
825{
826 RT_NOREF4(pInterface, hIoReq, off, cbWrite);
827 return VERR_NOT_SUPPORTED;
828}
829
830/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqFlush} */
831static DECLCALLBACK(int) drvscsiIoReqFlush(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq)
832{
833 RT_NOREF2(pInterface, hIoReq);
834 return VERR_NOT_SUPPORTED;
835}
836
837/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqDiscard} */
838static DECLCALLBACK(int) drvscsiIoReqDiscard(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, unsigned cRangesMax)
839{
840 RT_NOREF3(pInterface, hIoReq, cRangesMax);
841 return VERR_NOT_SUPPORTED;
842}
843
844/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqSendScsiCmd} */
845static DECLCALLBACK(int) drvscsiIoReqSendScsiCmd(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, uint32_t uLun,
846 const uint8_t *pbCdb, size_t cbCdb, PDMMEDIAEXIOREQSCSITXDIR enmTxDir,
847 size_t cbBuf, uint8_t *pabSense, size_t cbSense, uint8_t *pu8ScsiSts,
848 uint32_t cTimeoutMillies)
849{
850 RT_NOREF1(cTimeoutMillies);
851
852 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMediaEx);
853 PDRVSCSIREQ pReq = (PDRVSCSIREQ)hIoReq;
854 int rc = VINF_SUCCESS;
855
856 Log(("Dump for pReq=%#p Command: %s\n", pReq, SCSICmdText(pbCdb[0])));
857 Log(("cbCdb=%u\n", cbCdb));
858 for (uint32_t i = 0; i < cbCdb; i++)
859 Log(("pbCdb[%u]=%#x\n", i, pbCdb[i]));
860 Log(("cbBuf=%zu\n", cbBuf));
861
862 pReq->enmXferDir = enmTxDir;
863 pReq->cbBuf = cbBuf;
864 pReq->pu8ScsiSts = pu8ScsiSts;
865
866 /* Allocate and sync buffers if a data transfer is indicated. */
867 if (cbBuf)
868 {
869 pReq->pvBuf = RTMemAlloc(cbBuf);
870 if (RT_UNLIKELY(!pReq->pvBuf))
871 rc = VERR_NO_MEMORY;
872 }
873
874 if (RT_SUCCESS(rc))
875 {
876 pReq->Seg.pvSeg = pReq->pvBuf;
877 pReq->Seg.cbSeg = cbBuf;
878
879 if ( cbBuf
880 && ( enmTxDir == PDMMEDIAEXIOREQSCSITXDIR_UNKNOWN
881 || enmTxDir == PDMMEDIAEXIOREQSCSITXDIR_TO_DEVICE))
882 {
883 RTSGBUF SgBuf;
884 RTSgBufInit(&SgBuf, &pReq->Seg, 1);
885 rc = pThis->pDevMediaExPort->pfnIoReqCopyToBuf(pThis->pDevMediaExPort, hIoReq, &pReq->abAlloc[0],
886 0, &SgBuf, cbBuf);
887 }
888
889 if (RT_SUCCESS(rc))
890 {
891 rc = VSCSIDeviceReqCreate(pThis->hVScsiDevice, &pReq->hVScsiReq,
892 uLun, (uint8_t *)pbCdb, cbCdb, cbBuf, 1, &pReq->Seg,
893 pabSense, cbSense, pReq);
894 if (RT_SUCCESS(rc))
895 {
896 ASMAtomicIncU32(&pThis->StatIoDepth);
897 rc = VSCSIDeviceReqEnqueue(pThis->hVScsiDevice, pReq->hVScsiReq);
898 if (RT_SUCCESS(rc))
899 rc = VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS;
900 }
901 }
902 }
903
904 return rc;
905}
906
907/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqGetActiveCount} */
908static DECLCALLBACK(uint32_t) drvscsiIoReqGetActiveCount(PPDMIMEDIAEX pInterface)
909{
910 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMediaEx);
911 return pThis->StatIoDepth;
912}
913
914/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqGetSuspendedCount} */
915static DECLCALLBACK(uint32_t) drvscsiIoReqGetSuspendedCount(PPDMIMEDIAEX pInterface)
916{
917 RT_NOREF1(pInterface);
918 return 0;
919}
920
921/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqQuerySuspendedStart} */
922static DECLCALLBACK(int) drvscsiIoReqQuerySuspendedStart(PPDMIMEDIAEX pInterface, PPDMMEDIAEXIOREQ phIoReq, void **ppvIoReqAlloc)
923{
924 RT_NOREF3(pInterface, phIoReq, ppvIoReqAlloc);
925 return VERR_NOT_IMPLEMENTED;
926}
927
928/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqQuerySuspendedNext} */
929static DECLCALLBACK(int) drvscsiIoReqQuerySuspendedNext(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq,
930 PPDMMEDIAEXIOREQ phIoReqNext, void **ppvIoReqAllocNext)
931{
932 RT_NOREF4(pInterface, hIoReq, phIoReqNext, ppvIoReqAllocNext);
933 return VERR_NOT_IMPLEMENTED;
934}
935
936/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqSuspendedSave} */
937static DECLCALLBACK(int) drvscsiIoReqSuspendedSave(PPDMIMEDIAEX pInterface, PSSMHANDLE pSSM, PDMMEDIAEXIOREQ hIoReq)
938{
939 RT_NOREF3(pInterface, pSSM, hIoReq);
940 return VERR_NOT_IMPLEMENTED;
941}
942
943/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqSuspendedLoad} */
944static DECLCALLBACK(int) drvscsiIoReqSuspendedLoad(PPDMIMEDIAEX pInterface, PSSMHANDLE pSSM, PDMMEDIAEXIOREQ hIoReq)
945{
946 RT_NOREF3(pInterface, pSSM, hIoReq);
947 return VERR_NOT_IMPLEMENTED;
948}
949
950
951static DECLCALLBACK(void) drvscsiIoReqVScsiReqCompleted(VSCSIDEVICE hVScsiDevice, void *pVScsiDeviceUser,
952 void *pVScsiReqUser, int rcScsiCode, bool fRedoPossible, int rcReq)
953{
954 RT_NOREF2(hVScsiDevice, fRedoPossible);
955 PDRVSCSI pThis = (PDRVSCSI)pVScsiDeviceUser;
956 PDRVSCSIREQ pReq = (PDRVSCSIREQ)pVScsiReqUser;
957
958 ASMAtomicDecU32(&pThis->StatIoDepth);
959
960 /* Sync buffers. */
961 if ( RT_SUCCESS(rcReq)
962 && pReq->cbBuf
963 && ( pReq->enmXferDir == PDMMEDIAEXIOREQSCSITXDIR_UNKNOWN
964 || pReq->enmXferDir == PDMMEDIAEXIOREQSCSITXDIR_FROM_DEVICE))
965 {
966 RTSGBUF SgBuf;
967 RTSgBufInit(&SgBuf, &pReq->Seg, 1);
968 int rcCopy = pThis->pDevMediaExPort->pfnIoReqCopyFromBuf(pThis->pDevMediaExPort, (PDMMEDIAEXIOREQ)pReq,
969 &pReq->abAlloc[0], 0, &SgBuf, pReq->cbBuf);
970 if (RT_FAILURE(rcCopy))
971 rcReq = rcCopy;
972 }
973
974 if (pReq->pvBuf)
975 {
976 RTMemFree(pReq->pvBuf);
977 pReq->pvBuf = NULL;
978 }
979
980 *pReq->pu8ScsiSts = (uint8_t)rcScsiCode;
981 int rc = pThis->pDevMediaExPort->pfnIoReqCompleteNotify(pThis->pDevMediaExPort, (PDMMEDIAEXIOREQ)pReq,
982 &pReq->abAlloc[0], rcReq);
983 AssertRC(rc); RT_NOREF(rc);
984
985 if (RT_UNLIKELY(pThis->fDummySignal) && !pThis->StatIoDepth)
986 PDMDrvHlpAsyncNotificationCompleted(pThis->pDrvIns);
987}
988
989/**
990 * Consumer for the queue
991 *
992 * @returns Success indicator.
993 * If false the item will not be removed and the flushing will stop.
994 * @param pDrvIns The driver instance.
995 * @param pItem The item to consume. Upon return this item will be freed.
996 */
997static DECLCALLBACK(bool) drvscsiR3NotifyQueueConsumer(PPDMDRVINS pDrvIns, PPDMQUEUEITEMCORE pItem)
998{
999 PDRVSCSIEJECTSTATE pEjectState = (PDRVSCSIEJECTSTATE)pItem;
1000 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
1001
1002 int rc = pThis->pDrvMount->pfnUnmount(pThis->pDrvMount, false/*=fForce*/, true/*=fEject*/);
1003 Assert(RT_SUCCESS(rc) || rc == VERR_PDM_MEDIA_LOCKED || rc == VERR_PDM_MEDIA_NOT_MOUNTED);
1004 if (RT_SUCCESS(rc))
1005 pThis->pDevMediaExPort->pfnMediumEjected(pThis->pDevMediaExPort);
1006
1007 pEjectState->rcReq = rc;
1008 RTSemEventSignal(pEjectState->hSemEvt);
1009 return true;
1010}
1011
1012/* -=-=-=-=- IBase -=-=-=-=- */
1013
1014/**
1015 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1016 */
1017static DECLCALLBACK(void *) drvscsiQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1018{
1019 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1020 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
1021
1022 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNT, pThis->pDrvMount);
1023 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1024 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAEX, pThis->pDevMediaExPort ? &pThis->IMediaEx : NULL);
1025 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIA, pThis->pDrvMedia ? &pThis->IMedia : NULL);
1026 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAPORT, &pThis->IPort);
1027 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNTNOTIFY, &pThis->IMountNotify);
1028 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAEXPORT, &pThis->IPortEx);
1029 return NULL;
1030}
1031
1032static DECLCALLBACK(int) drvscsiQueryDeviceLocation(PPDMIMEDIAPORT pInterface, const char **ppcszController,
1033 uint32_t *piInstance, uint32_t *piLUN)
1034{
1035 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IPort);
1036
1037 return pThis->pDevMediaPort->pfnQueryDeviceLocation(pThis->pDevMediaPort, ppcszController,
1038 piInstance, piLUN);
1039}
1040
1041/**
1042 * Called when media is mounted.
1043 *
1044 * @param pInterface Pointer to the interface structure containing the called function pointer.
1045 */
1046static DECLCALLBACK(void) drvscsiMountNotify(PPDMIMOUNTNOTIFY pInterface)
1047{
1048 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMountNotify);
1049 LogFlowFunc(("mounting LUN#%p\n", pThis->hVScsiLun));
1050
1051 /* Ignore the call if we're called while being attached. */
1052 if (!pThis->pDrvMedia)
1053 return;
1054
1055 /* Let the LUN know that a medium was mounted. */
1056 VSCSILunMountNotify(pThis->hVScsiLun);
1057}
1058
1059/**
1060 * Called when media is unmounted
1061 *
1062 * @param pInterface Pointer to the interface structure containing the called function pointer.
1063 */
1064static DECLCALLBACK(void) drvscsiUnmountNotify(PPDMIMOUNTNOTIFY pInterface)
1065{
1066 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMountNotify);
1067 LogFlowFunc(("unmounting LUN#%p\n", pThis->hVScsiLun));
1068
1069 /* Let the LUN know that the medium was unmounted. */
1070 VSCSILunUnmountNotify(pThis->hVScsiLun);
1071}
1072
1073/**
1074 * Worker for drvscsiReset, drvscsiSuspend and drvscsiPowerOff.
1075 *
1076 * @param pDrvIns The driver instance.
1077 * @param pfnAsyncNotify The async callback.
1078 */
1079static void drvscsiR3ResetOrSuspendOrPowerOff(PPDMDRVINS pDrvIns, PFNPDMDRVASYNCNOTIFY pfnAsyncNotify)
1080{
1081 RT_NOREF1(pfnAsyncNotify);
1082
1083 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
1084
1085 if (pThis->StatIoDepth > 0)
1086 ASMAtomicWriteBool(&pThis->fDummySignal, true);
1087}
1088
1089/**
1090 * Callback employed by drvscsiSuspend and drvscsiPowerOff.
1091 *
1092 * @returns true if we've quiesced, false if we're still working.
1093 * @param pDrvIns The driver instance.
1094 */
1095static DECLCALLBACK(bool) drvscsiIsAsyncSuspendOrPowerOffDone(PPDMDRVINS pDrvIns)
1096{
1097 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
1098
1099 if (pThis->StatIoDepth > 0)
1100 return false;
1101 else
1102 return true;
1103}
1104
1105/**
1106 * @copydoc FNPDMDRVPOWEROFF
1107 */
1108static DECLCALLBACK(void) drvscsiPowerOff(PPDMDRVINS pDrvIns)
1109{
1110 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncSuspendOrPowerOffDone);
1111}
1112
1113/**
1114 * @copydoc FNPDMDRVSUSPEND
1115 */
1116static DECLCALLBACK(void) drvscsiSuspend(PPDMDRVINS pDrvIns)
1117{
1118 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncSuspendOrPowerOffDone);
1119}
1120
1121/**
1122 * Callback employed by drvscsiReset.
1123 *
1124 * @returns true if we've quiesced, false if we're still working.
1125 * @param pDrvIns The driver instance.
1126 */
1127static DECLCALLBACK(bool) drvscsiIsAsyncResetDone(PPDMDRVINS pDrvIns)
1128{
1129 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
1130
1131 if (pThis->StatIoDepth > 0)
1132 return false;
1133 else
1134 return true;
1135}
1136
1137/** @copydoc FNPDMDRVATTACH */
1138static DECLCALLBACK(int) drvscsiAttach(PPDMDRVINS pDrvIns, uint32_t fFlags)
1139{
1140 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
1141
1142 LogFlowFunc(("pDrvIns=%#p fFlags=%#x\n", pDrvIns, fFlags));
1143
1144 AssertMsgReturn((fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG),
1145 ("SCSI: Hotplugging is not supported\n"),
1146 VERR_INVALID_PARAMETER);
1147
1148 /*
1149 * Try attach driver below and query it's media interface.
1150 */
1151 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pThis->pDrvBase);
1152 AssertMsgReturn(RT_SUCCESS(rc), ("Attaching driver below failed rc=%Rrc\n", rc), rc);
1153
1154 /*
1155 * Query the media interface.
1156 */
1157 pThis->pDrvMedia = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMEDIA);
1158 AssertMsgReturn(VALID_PTR(pThis->pDrvMedia), ("VSCSI configuration error: No media interface!\n"),
1159 VERR_PDM_MISSING_INTERFACE);
1160
1161 /* Query the extended media interface. */
1162 pThis->pDrvMediaEx = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMEDIAEX);
1163 AssertMsgReturn(VALID_PTR(pThis->pDrvMediaEx), ("VSCSI configuration error: No extended media interface!\n"),
1164 VERR_PDM_MISSING_INTERFACE);
1165
1166 pThis->pDrvMount = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMOUNT);
1167
1168 if (pThis->cbVScsiIoReqAlloc)
1169 {
1170 rc = pThis->pDrvMediaEx->pfnIoReqAllocSizeSet(pThis->pDrvMediaEx, pThis->cbVScsiIoReqAlloc);
1171 AssertMsgReturn(RT_SUCCESS(rc), ("Setting the I/O request allocation size failed with rc=%Rrc\n", rc), rc);
1172 }
1173
1174 if (pThis->pDrvMount)
1175 {
1176 if (pThis->pDrvMount->pfnIsMounted(pThis->pDrvMount))
1177 {
1178 rc = VINF_SUCCESS; VSCSILunMountNotify(pThis->hVScsiLun);
1179 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to notify the LUN of media being mounted\n"), rc);
1180 }
1181 else
1182 {
1183 rc = VINF_SUCCESS; VSCSILunUnmountNotify(pThis->hVScsiLun);
1184 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to notify the LUN of media being unmounted\n"), rc);
1185 }
1186 }
1187
1188 return rc;
1189}
1190
1191/** @copydoc FNPDMDRVDETACH */
1192static DECLCALLBACK(void) drvscsiDetach(PPDMDRVINS pDrvIns, uint32_t fFlags)
1193{
1194 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
1195
1196 LogFlowFunc(("pDrvIns=%#p fFlags=%#x\n", pDrvIns, fFlags));
1197
1198 AssertMsgReturnVoid((fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG),
1199 ("SCSI: Hotplugging is not supported\n"));
1200
1201 /*
1202 * Zero some important members.
1203 */
1204 pThis->pDrvBase = NULL;
1205 pThis->pDrvMedia = NULL;
1206 pThis->pDrvMediaEx = NULL;
1207 pThis->pDrvMount = NULL;
1208
1209 VSCSILunUnmountNotify(pThis->hVScsiLun);
1210}
1211
1212/**
1213 * @copydoc FNPDMDRVRESET
1214 */
1215static DECLCALLBACK(void) drvscsiReset(PPDMDRVINS pDrvIns)
1216{
1217 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncResetDone);
1218}
1219
1220/**
1221 * Destruct a driver instance.
1222 *
1223 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
1224 * resources can be freed correctly.
1225 *
1226 * @param pDrvIns The driver instance data.
1227 */
1228static DECLCALLBACK(void) drvscsiDestruct(PPDMDRVINS pDrvIns)
1229{
1230 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
1231 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1232
1233 /* Free the VSCSI device and LUN handle. */
1234 if (pThis->hVScsiDevice)
1235 {
1236 VSCSILUN hVScsiLun;
1237 int rc = VSCSIDeviceLunDetach(pThis->hVScsiDevice, 0, &hVScsiLun);
1238 AssertRC(rc);
1239
1240 Assert(hVScsiLun == pThis->hVScsiLun);
1241 rc = VSCSILunDestroy(hVScsiLun);
1242 AssertRC(rc);
1243 rc = VSCSIDeviceDestroy(pThis->hVScsiDevice);
1244 AssertRC(rc);
1245
1246 pThis->hVScsiDevice = NULL;
1247 pThis->hVScsiLun = NULL;
1248 }
1249
1250 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatBytesRead);
1251 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatBytesWritten);
1252 PDMDrvHlpSTAMDeregister(pDrvIns, (void *)&pThis->StatIoDepth);
1253}
1254
1255/**
1256 * Construct a block driver instance.
1257 *
1258 * @copydoc FNPDMDRVCONSTRUCT
1259 */
1260static DECLCALLBACK(int) drvscsiConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1261{
1262 int rc = VINF_SUCCESS;
1263 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
1264 LogFlowFunc(("pDrvIns=%#p pCfg=%#p\n", pDrvIns, pCfg));
1265 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1266
1267 /*
1268 * Initialize the instance data.
1269 */
1270 pThis->pDrvIns = pDrvIns;
1271
1272 pDrvIns->IBase.pfnQueryInterface = drvscsiQueryInterface;
1273
1274 /* IMedia */
1275 pThis->IMedia.pfnRead = NULL;
1276 pThis->IMedia.pfnReadPcBios = NULL;
1277 pThis->IMedia.pfnWrite = NULL;
1278 pThis->IMedia.pfnFlush = NULL;
1279 pThis->IMedia.pfnSendCmd = NULL;
1280 pThis->IMedia.pfnMerge = NULL;
1281 pThis->IMedia.pfnSetSecKeyIf = NULL;
1282 pThis->IMedia.pfnGetSize = drvscsiGetSize;
1283 pThis->IMedia.pfnGetSectorSize = drvscsiGetSectorSize;
1284 pThis->IMedia.pfnIsReadOnly = drvscsiIsReadOnly;
1285 pThis->IMedia.pfnIsNonRotational = drvscsiIsNonRotational;
1286 pThis->IMedia.pfnBiosGetPCHSGeometry = drvscsiBiosGetPCHSGeometry;
1287 pThis->IMedia.pfnBiosSetPCHSGeometry = drvscsiBiosSetPCHSGeometry;
1288 pThis->IMedia.pfnBiosGetLCHSGeometry = drvscsiBiosGetLCHSGeometry;
1289 pThis->IMedia.pfnBiosSetLCHSGeometry = drvscsiBiosSetLCHSGeometry;
1290 pThis->IMedia.pfnBiosIsVisible = drvscsiBiosIsVisible;
1291 pThis->IMedia.pfnGetType = drvscsiGetType;
1292 pThis->IMedia.pfnGetUuid = drvscsiGetUuid;
1293 pThis->IMedia.pfnDiscard = NULL;
1294
1295 /* IMediaEx */
1296 pThis->IMediaEx.pfnQueryFeatures = drvscsiQueryFeatures;
1297 pThis->IMediaEx.pfnIoReqAllocSizeSet = drvscsiIoReqAllocSizeSet;
1298 pThis->IMediaEx.pfnIoReqAlloc = drvscsiIoReqAlloc;
1299 pThis->IMediaEx.pfnIoReqFree = drvscsiIoReqFree;
1300 pThis->IMediaEx.pfnIoReqQueryResidual = drvscsiIoReqQueryResidual;
1301 pThis->IMediaEx.pfnIoReqQueryXferSize = drvscsiIoReqQueryXferSize;
1302 pThis->IMediaEx.pfnIoReqCancelAll = drvscsiIoReqCancelAll;
1303 pThis->IMediaEx.pfnIoReqCancel = drvscsiIoReqCancel;
1304 pThis->IMediaEx.pfnIoReqRead = drvscsiIoReqRead;
1305 pThis->IMediaEx.pfnIoReqWrite = drvscsiIoReqWrite;
1306 pThis->IMediaEx.pfnIoReqFlush = drvscsiIoReqFlush;
1307 pThis->IMediaEx.pfnIoReqDiscard = drvscsiIoReqDiscard;
1308 pThis->IMediaEx.pfnIoReqSendScsiCmd = drvscsiIoReqSendScsiCmd;
1309 pThis->IMediaEx.pfnIoReqGetActiveCount = drvscsiIoReqGetActiveCount;
1310 pThis->IMediaEx.pfnIoReqGetSuspendedCount = drvscsiIoReqGetSuspendedCount;
1311 pThis->IMediaEx.pfnIoReqQuerySuspendedStart = drvscsiIoReqQuerySuspendedStart;
1312 pThis->IMediaEx.pfnIoReqQuerySuspendedNext = drvscsiIoReqQuerySuspendedNext;
1313 pThis->IMediaEx.pfnIoReqSuspendedSave = drvscsiIoReqSuspendedSave;
1314 pThis->IMediaEx.pfnIoReqSuspendedLoad = drvscsiIoReqSuspendedLoad;
1315
1316 pThis->IMountNotify.pfnMountNotify = drvscsiMountNotify;
1317 pThis->IMountNotify.pfnUnmountNotify = drvscsiUnmountNotify;
1318 pThis->IPort.pfnQueryDeviceLocation = drvscsiQueryDeviceLocation;
1319 pThis->IPortEx.pfnIoReqCompleteNotify = drvscsiIoReqCompleteNotify;
1320 pThis->IPortEx.pfnIoReqCopyFromBuf = drvscsiIoReqCopyFromBuf;
1321 pThis->IPortEx.pfnIoReqCopyToBuf = drvscsiIoReqCopyToBuf;
1322 pThis->IPortEx.pfnIoReqQueryDiscardRanges = drvscsiIoReqQueryDiscardRanges;
1323 pThis->IPortEx.pfnIoReqStateChanged = drvscsiIoReqStateChanged;
1324
1325 /* Query the optional media port interface above. */
1326 pThis->pDevMediaPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMEDIAPORT);
1327
1328 /* Query the optional extended media port interface above. */
1329 pThis->pDevMediaExPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMEDIAEXPORT);
1330
1331 AssertMsgReturn(pThis->pDevMediaExPort,
1332 ("Missing extended media port interface above\n"), VERR_PDM_MISSING_INTERFACE);
1333
1334 /* Query the optional LED interface above. */
1335 pThis->pLedPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
1336 if (pThis->pLedPort != NULL)
1337 {
1338 /* Get The Led. */
1339 rc = pThis->pLedPort->pfnQueryStatusLed(pThis->pLedPort, 0, &pThis->pLed);
1340 if (RT_FAILURE(rc))
1341 pThis->pLed = &pThis->Led;
1342 }
1343 else
1344 pThis->pLed = &pThis->Led;
1345
1346 /*
1347 * Validate and read configuration.
1348 */
1349 if (!CFGMR3AreValuesValid(pCfg, ""))
1350 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
1351 N_("SCSI configuration error: unknown option specified"));
1352
1353 /*
1354 * Try attach driver below and query it's media interface.
1355 */
1356 rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pThis->pDrvBase);
1357 AssertMsgReturn(RT_SUCCESS(rc), ("Attaching driver below failed rc=%Rrc\n", rc), rc);
1358
1359 /*
1360 * Query the media interface.
1361 */
1362 pThis->pDrvMedia = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMEDIA);
1363 AssertMsgReturn(VALID_PTR(pThis->pDrvMedia), ("VSCSI configuration error: No media interface!\n"),
1364 VERR_PDM_MISSING_INTERFACE);
1365
1366 /* Query the extended media interface. */
1367 pThis->pDrvMediaEx = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMEDIAEX);
1368 AssertMsgReturn(VALID_PTR(pThis->pDrvMediaEx), ("VSCSI configuration error: No extended media interface!\n"),
1369 VERR_PDM_MISSING_INTERFACE);
1370
1371 pThis->pDrvMount = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMOUNT);
1372
1373 PDMMEDIATYPE enmType = pThis->pDrvMedia->pfnGetType(pThis->pDrvMedia);
1374 VSCSILUNTYPE enmLunType;
1375 switch (enmType)
1376 {
1377 case PDMMEDIATYPE_HARD_DISK:
1378 enmLunType = VSCSILUNTYPE_SBC;
1379 break;
1380 case PDMMEDIATYPE_CDROM:
1381 case PDMMEDIATYPE_DVD:
1382 enmLunType = VSCSILUNTYPE_MMC;
1383 break;
1384 default:
1385 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_UNSUPPORTED_BLOCK_TYPE, RT_SRC_POS,
1386 N_("Only hard disks and CD/DVD-ROMs are currently supported as SCSI devices (enmType=%d)"),
1387 enmType);
1388 }
1389 if ( ( enmType == PDMMEDIATYPE_DVD
1390 || enmType == PDMMEDIATYPE_CDROM)
1391 && !pThis->pDrvMount)
1392 {
1393 AssertMsgFailed(("Internal error: cdrom without a mountable interface\n"));
1394 return VERR_INTERNAL_ERROR;
1395 }
1396
1397 /* Create VSCSI device and LUN. */
1398 pThis->VScsiIoCallbacks.pfnVScsiLunReqAllocSizeSet = drvscsiReqAllocSizeSet;
1399 pThis->VScsiIoCallbacks.pfnVScsiLunReqAlloc = drvscsiReqAlloc;
1400 pThis->VScsiIoCallbacks.pfnVScsiLunReqFree = drvscsiReqFree;
1401 pThis->VScsiIoCallbacks.pfnVScsiLunMediumGetSize = drvscsiGetSize;
1402 pThis->VScsiIoCallbacks.pfnVScsiLunMediumGetSectorSize = drvscsiGetSectorSize;
1403 pThis->VScsiIoCallbacks.pfnVScsiLunMediumEject = drvscsiEject;
1404 pThis->VScsiIoCallbacks.pfnVScsiLunReqTransferEnqueue = drvscsiReqTransferEnqueue;
1405 pThis->VScsiIoCallbacks.pfnVScsiLunGetFeatureFlags = drvscsiGetFeatureFlags;
1406 pThis->VScsiIoCallbacks.pfnVScsiLunMediumSetLock = drvscsiSetLock;
1407
1408 rc = VSCSIDeviceCreate(&pThis->hVScsiDevice, drvscsiIoReqVScsiReqCompleted, pThis);
1409 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI device rc=%Rrc\n", rc), rc);
1410 rc = VSCSILunCreate(&pThis->hVScsiLun, enmLunType, &pThis->VScsiIoCallbacks,
1411 pThis);
1412 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI LUN rc=%Rrc\n", rc), rc);
1413 rc = VSCSIDeviceLunAttach(pThis->hVScsiDevice, pThis->hVScsiLun, 0);
1414 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to attached the LUN to the SCSI device\n"), rc);
1415
1416 /// @todo This is a very hacky way of telling the LUN whether a medium was mounted.
1417 // The mount/unmount interface doesn't work in a very sensible manner!
1418 if (pThis->pDrvMount)
1419 {
1420 if (pThis->pDrvMount->pfnIsMounted(pThis->pDrvMount))
1421 {
1422 rc = VINF_SUCCESS; VSCSILunMountNotify(pThis->hVScsiLun);
1423 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to notify the LUN of media being mounted\n"), rc);
1424 }
1425 else
1426 {
1427 rc = VINF_SUCCESS; VSCSILunUnmountNotify(pThis->hVScsiLun);
1428 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to notify the LUN of media being unmounted\n"), rc);
1429 }
1430 }
1431
1432 const char *pszCtrl = NULL;
1433 uint32_t iCtrlInstance = 0;
1434 uint32_t iCtrlLun = 0;
1435
1436 rc = pThis->pDevMediaPort->pfnQueryDeviceLocation(pThis->pDevMediaPort, &pszCtrl, &iCtrlInstance, &iCtrlLun);
1437 if (RT_SUCCESS(rc))
1438 {
1439 const char *pszCtrlId = strcmp(pszCtrl, "Msd") == 0 ? "USB"
1440 : strcmp(pszCtrl, "lsilogicsas") == 0 ? "SAS"
1441 : "SCSI";
1442 /* Register statistics counter. */
1443 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
1444 "Amount of data read.", "/Devices/%s%u/%u/ReadBytes", pszCtrlId, iCtrlInstance, iCtrlLun);
1445 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
1446 "Amount of data written.", "/Devices/%s%u/%u/WrittenBytes", pszCtrlId, iCtrlInstance, iCtrlLun);
1447 PDMDrvHlpSTAMRegisterF(pDrvIns, (void *)&pThis->StatIoDepth, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
1448 "Number of active tasks.", "/Devices/%s%u/%u/IoDepth", pszCtrlId, iCtrlInstance, iCtrlLun);
1449 }
1450
1451 pThis->StatIoDepth = 0;
1452
1453 uint32_t fFeatures = 0;
1454 rc = pThis->pDrvMediaEx->pfnQueryFeatures(pThis->pDrvMediaEx, &fFeatures);
1455 if (RT_FAILURE(rc))
1456 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1457 N_("VSCSI configuration error: Failed to query features of device"));
1458 if (fFeatures & PDMIMEDIAEX_FEATURE_F_DISCARD)
1459 LogRel(("SCSI#%d: Enabled UNMAP support\n", pDrvIns->iInstance));
1460
1461 rc = PDMDrvHlpQueueCreate(pDrvIns, sizeof(DRVSCSIEJECTSTATE), 1, 0, drvscsiR3NotifyQueueConsumer,
1462 "SCSI-Eject", &pThis->pQueue);
1463 if (RT_FAILURE(rc))
1464 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1465 N_("VSCSI configuration error: Failed to create notification queue"));
1466
1467 return VINF_SUCCESS;
1468}
1469
1470/**
1471 * SCSI driver registration record.
1472 */
1473const PDMDRVREG g_DrvSCSI =
1474{
1475 /* u32Version */
1476 PDM_DRVREG_VERSION,
1477 /* szName */
1478 "SCSI",
1479 /* szRCMod */
1480 "",
1481 /* szR0Mod */
1482 "",
1483 /* pszDescription */
1484 "Generic SCSI driver.",
1485 /* fFlags */
1486 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1487 /* fClass. */
1488 PDM_DRVREG_CLASS_SCSI,
1489 /* cMaxInstances */
1490 ~0U,
1491 /* cbInstance */
1492 sizeof(DRVSCSI),
1493 /* pfnConstruct */
1494 drvscsiConstruct,
1495 /* pfnDestruct */
1496 drvscsiDestruct,
1497 /* pfnRelocate */
1498 NULL,
1499 /* pfnIOCtl */
1500 NULL,
1501 /* pfnPowerOn */
1502 NULL,
1503 /* pfnReset */
1504 drvscsiReset,
1505 /* pfnSuspend */
1506 drvscsiSuspend,
1507 /* pfnResume */
1508 NULL,
1509 /* pfnAttach */
1510 drvscsiAttach,
1511 /* pfnDetach */
1512 drvscsiDetach,
1513 /* pfnPowerOff */
1514 drvscsiPowerOff,
1515 /* pfnSoftReset */
1516 NULL,
1517 /* u32EndVersion */
1518 PDM_DRVREG_VERSION
1519};
Note: See TracBrowser for help on using the repository browser.

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