VirtualBox

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

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

Devices/Storage: Add callback to query the actual transfer size of an I/O request if PDMIMEDIAEX is used, useful to calculate the amount of data transferred for requests where the transfer size can be different from the guest buffer size (e.g. SCSI)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 54.2 KB
Line 
1/* $Id: DrvSCSI.cpp 64407 2016-10-25 11:53:00Z 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_NOREF2(pInterface, hIoReq);
786
787 *pcbResidual = 0; /** @todo: Implement. */
788 return VINF_SUCCESS;
789}
790
791/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqQueryXferSize} */
792static DECLCALLBACK(int) drvscsiIoReqQueryXferSize(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, size_t *pcbXfer)
793{
794 RT_NOREF1(pInterface);
795 PDRVSCSIREQ pReq = (PDRVSCSIREQ)hIoReq;
796
797 *pcbXfer = pReq->cbBuf; /** @todo: Implement properly. */
798 return VINF_SUCCESS;
799}
800
801/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqCancelAll} */
802static DECLCALLBACK(int) drvscsiIoReqCancelAll(PPDMIMEDIAEX pInterface)
803{
804 RT_NOREF1(pInterface);
805 return VINF_SUCCESS;
806}
807
808/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqCancel} */
809static DECLCALLBACK(int) drvscsiIoReqCancel(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQID uIoReqId)
810{
811 RT_NOREF2(pInterface, uIoReqId);
812 return VERR_PDM_MEDIAEX_IOREQID_NOT_FOUND;
813}
814
815/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqRead} */
816static DECLCALLBACK(int) drvscsiIoReqRead(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, uint64_t off, size_t cbRead)
817{
818 RT_NOREF4(pInterface, hIoReq, off, cbRead);
819 return VERR_NOT_SUPPORTED;
820}
821
822/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqWrite} */
823static DECLCALLBACK(int) drvscsiIoReqWrite(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, uint64_t off, size_t cbWrite)
824{
825 RT_NOREF4(pInterface, hIoReq, off, cbWrite);
826 return VERR_NOT_SUPPORTED;
827}
828
829/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqFlush} */
830static DECLCALLBACK(int) drvscsiIoReqFlush(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq)
831{
832 RT_NOREF2(pInterface, hIoReq);
833 return VERR_NOT_SUPPORTED;
834}
835
836/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqDiscard} */
837static DECLCALLBACK(int) drvscsiIoReqDiscard(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, unsigned cRangesMax)
838{
839 RT_NOREF3(pInterface, hIoReq, cRangesMax);
840 return VERR_NOT_SUPPORTED;
841}
842
843/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqSendScsiCmd} */
844static DECLCALLBACK(int) drvscsiIoReqSendScsiCmd(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, uint32_t uLun,
845 const uint8_t *pbCdb, size_t cbCdb, PDMMEDIAEXIOREQSCSITXDIR enmTxDir,
846 size_t cbBuf, uint8_t *pabSense, size_t cbSense, uint8_t *pu8ScsiSts,
847 uint32_t cTimeoutMillies)
848{
849 RT_NOREF1(cTimeoutMillies);
850
851 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMediaEx);
852 PDRVSCSIREQ pReq = (PDRVSCSIREQ)hIoReq;
853 int rc = VINF_SUCCESS;
854
855 Log(("Dump for pReq=%#p Command: %s\n", pReq, SCSICmdText(pbCdb[0])));
856 Log(("cbCdb=%u\n", cbCdb));
857 for (uint32_t i = 0; i < cbCdb; i++)
858 Log(("pbCdb[%u]=%#x\n", i, pbCdb[i]));
859 Log(("cbBuf=%zu\n", cbBuf));
860
861 pReq->enmXferDir = enmTxDir;
862 pReq->cbBuf = cbBuf;
863 pReq->pu8ScsiSts = pu8ScsiSts;
864
865 /* Allocate and sync buffers if a data transfer is indicated. */
866 if (cbBuf)
867 {
868 pReq->pvBuf = RTMemAlloc(cbBuf);
869 if (RT_UNLIKELY(!pReq->pvBuf))
870 rc = VERR_NO_MEMORY;
871 }
872
873 if (RT_SUCCESS(rc))
874 {
875 pReq->Seg.pvSeg = pReq->pvBuf;
876 pReq->Seg.cbSeg = cbBuf;
877
878 if ( cbBuf
879 && ( enmTxDir == PDMMEDIAEXIOREQSCSITXDIR_UNKNOWN
880 || enmTxDir == PDMMEDIAEXIOREQSCSITXDIR_TO_DEVICE))
881 {
882 RTSGBUF SgBuf;
883 RTSgBufInit(&SgBuf, &pReq->Seg, 1);
884 rc = pThis->pDevMediaExPort->pfnIoReqCopyToBuf(pThis->pDevMediaExPort, hIoReq, &pReq->abAlloc[0],
885 0, &SgBuf, cbBuf);
886 }
887
888 if (RT_SUCCESS(rc))
889 {
890 rc = VSCSIDeviceReqCreate(pThis->hVScsiDevice, &pReq->hVScsiReq,
891 uLun, (uint8_t *)pbCdb, cbCdb, cbBuf, 1, &pReq->Seg,
892 pabSense, cbSense, pReq);
893 if (RT_SUCCESS(rc))
894 {
895 ASMAtomicIncU32(&pThis->StatIoDepth);
896 rc = VSCSIDeviceReqEnqueue(pThis->hVScsiDevice, pReq->hVScsiReq);
897 if (RT_SUCCESS(rc))
898 rc = VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS;
899 }
900 }
901 }
902
903 return rc;
904}
905
906/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqGetActiveCount} */
907static DECLCALLBACK(uint32_t) drvscsiIoReqGetActiveCount(PPDMIMEDIAEX pInterface)
908{
909 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMediaEx);
910 return pThis->StatIoDepth;
911}
912
913/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqGetSuspendedCount} */
914static DECLCALLBACK(uint32_t) drvscsiIoReqGetSuspendedCount(PPDMIMEDIAEX pInterface)
915{
916 RT_NOREF1(pInterface);
917 return 0;
918}
919
920/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqQuerySuspendedStart} */
921static DECLCALLBACK(int) drvscsiIoReqQuerySuspendedStart(PPDMIMEDIAEX pInterface, PPDMMEDIAEXIOREQ phIoReq, void **ppvIoReqAlloc)
922{
923 RT_NOREF3(pInterface, phIoReq, ppvIoReqAlloc);
924 return VERR_NOT_IMPLEMENTED;
925}
926
927/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqQuerySuspendedNext} */
928static DECLCALLBACK(int) drvscsiIoReqQuerySuspendedNext(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq,
929 PPDMMEDIAEXIOREQ phIoReqNext, void **ppvIoReqAllocNext)
930{
931 RT_NOREF4(pInterface, hIoReq, phIoReqNext, ppvIoReqAllocNext);
932 return VERR_NOT_IMPLEMENTED;
933}
934
935/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqSuspendedSave} */
936static DECLCALLBACK(int) drvscsiIoReqSuspendedSave(PPDMIMEDIAEX pInterface, PSSMHANDLE pSSM, PDMMEDIAEXIOREQ hIoReq)
937{
938 RT_NOREF3(pInterface, pSSM, hIoReq);
939 return VERR_NOT_IMPLEMENTED;
940}
941
942/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqSuspendedLoad} */
943static DECLCALLBACK(int) drvscsiIoReqSuspendedLoad(PPDMIMEDIAEX pInterface, PSSMHANDLE pSSM, PDMMEDIAEXIOREQ hIoReq)
944{
945 RT_NOREF3(pInterface, pSSM, hIoReq);
946 return VERR_NOT_IMPLEMENTED;
947}
948
949
950static DECLCALLBACK(void) drvscsiIoReqVScsiReqCompleted(VSCSIDEVICE hVScsiDevice, void *pVScsiDeviceUser,
951 void *pVScsiReqUser, int rcScsiCode, bool fRedoPossible, int rcReq)
952{
953 RT_NOREF2(hVScsiDevice, fRedoPossible);
954 PDRVSCSI pThis = (PDRVSCSI)pVScsiDeviceUser;
955 PDRVSCSIREQ pReq = (PDRVSCSIREQ)pVScsiReqUser;
956
957 ASMAtomicDecU32(&pThis->StatIoDepth);
958
959 /* Sync buffers. */
960 if ( RT_SUCCESS(rcReq)
961 && pReq->cbBuf
962 && ( pReq->enmXferDir == PDMMEDIAEXIOREQSCSITXDIR_UNKNOWN
963 || pReq->enmXferDir == PDMMEDIAEXIOREQSCSITXDIR_FROM_DEVICE))
964 {
965 RTSGBUF SgBuf;
966 RTSgBufInit(&SgBuf, &pReq->Seg, 1);
967 int rcCopy = pThis->pDevMediaExPort->pfnIoReqCopyFromBuf(pThis->pDevMediaExPort, (PDMMEDIAEXIOREQ)pReq,
968 &pReq->abAlloc[0], 0, &SgBuf, pReq->cbBuf);
969 if (RT_FAILURE(rcCopy))
970 rcReq = rcCopy;
971 }
972
973 if (pReq->pvBuf)
974 {
975 RTMemFree(pReq->pvBuf);
976 pReq->pvBuf = NULL;
977 }
978
979 *pReq->pu8ScsiSts = (uint8_t)rcScsiCode;
980 int rc = pThis->pDevMediaExPort->pfnIoReqCompleteNotify(pThis->pDevMediaExPort, (PDMMEDIAEXIOREQ)pReq,
981 &pReq->abAlloc[0], rcReq);
982 AssertRC(rc); RT_NOREF(rc);
983
984 if (RT_UNLIKELY(pThis->fDummySignal) && !pThis->StatIoDepth)
985 PDMDrvHlpAsyncNotificationCompleted(pThis->pDrvIns);
986}
987
988/**
989 * Consumer for the queue
990 *
991 * @returns Success indicator.
992 * If false the item will not be removed and the flushing will stop.
993 * @param pDrvIns The driver instance.
994 * @param pItem The item to consume. Upon return this item will be freed.
995 */
996static DECLCALLBACK(bool) drvscsiR3NotifyQueueConsumer(PPDMDRVINS pDrvIns, PPDMQUEUEITEMCORE pItem)
997{
998 PDRVSCSIEJECTSTATE pEjectState = (PDRVSCSIEJECTSTATE)pItem;
999 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
1000
1001 int rc = pThis->pDrvMount->pfnUnmount(pThis->pDrvMount, false/*=fForce*/, true/*=fEject*/);
1002 Assert(RT_SUCCESS(rc) || rc == VERR_PDM_MEDIA_LOCKED || rc == VERR_PDM_MEDIA_NOT_MOUNTED);
1003 if (RT_SUCCESS(rc))
1004 pThis->pDevMediaExPort->pfnMediumEjected(pThis->pDevMediaExPort);
1005
1006 pEjectState->rcReq = rc;
1007 RTSemEventSignal(pEjectState->hSemEvt);
1008 return true;
1009}
1010
1011/* -=-=-=-=- IBase -=-=-=-=- */
1012
1013/**
1014 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1015 */
1016static DECLCALLBACK(void *) drvscsiQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1017{
1018 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1019 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
1020
1021 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNT, pThis->pDrvMount);
1022 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1023 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAEX, pThis->pDevMediaExPort ? &pThis->IMediaEx : NULL);
1024 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIA, pThis->pDrvMedia ? &pThis->IMedia : NULL);
1025 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAPORT, &pThis->IPort);
1026 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNTNOTIFY, &pThis->IMountNotify);
1027 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAEXPORT, &pThis->IPortEx);
1028 return NULL;
1029}
1030
1031static DECLCALLBACK(int) drvscsiQueryDeviceLocation(PPDMIMEDIAPORT pInterface, const char **ppcszController,
1032 uint32_t *piInstance, uint32_t *piLUN)
1033{
1034 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IPort);
1035
1036 return pThis->pDevMediaPort->pfnQueryDeviceLocation(pThis->pDevMediaPort, ppcszController,
1037 piInstance, piLUN);
1038}
1039
1040/**
1041 * Called when media is mounted.
1042 *
1043 * @param pInterface Pointer to the interface structure containing the called function pointer.
1044 */
1045static DECLCALLBACK(void) drvscsiMountNotify(PPDMIMOUNTNOTIFY pInterface)
1046{
1047 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMountNotify);
1048 LogFlowFunc(("mounting LUN#%p\n", pThis->hVScsiLun));
1049
1050 /* Ignore the call if we're called while being attached. */
1051 if (!pThis->pDrvMedia)
1052 return;
1053
1054 /* Let the LUN know that a medium was mounted. */
1055 VSCSILunMountNotify(pThis->hVScsiLun);
1056}
1057
1058/**
1059 * Called when media is unmounted
1060 *
1061 * @param pInterface Pointer to the interface structure containing the called function pointer.
1062 */
1063static DECLCALLBACK(void) drvscsiUnmountNotify(PPDMIMOUNTNOTIFY pInterface)
1064{
1065 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMountNotify);
1066 LogFlowFunc(("unmounting LUN#%p\n", pThis->hVScsiLun));
1067
1068 /* Let the LUN know that the medium was unmounted. */
1069 VSCSILunUnmountNotify(pThis->hVScsiLun);
1070}
1071
1072/**
1073 * Worker for drvscsiReset, drvscsiSuspend and drvscsiPowerOff.
1074 *
1075 * @param pDrvIns The driver instance.
1076 * @param pfnAsyncNotify The async callback.
1077 */
1078static void drvscsiR3ResetOrSuspendOrPowerOff(PPDMDRVINS pDrvIns, PFNPDMDRVASYNCNOTIFY pfnAsyncNotify)
1079{
1080 RT_NOREF1(pfnAsyncNotify);
1081
1082 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
1083
1084 if (pThis->StatIoDepth > 0)
1085 ASMAtomicWriteBool(&pThis->fDummySignal, true);
1086}
1087
1088/**
1089 * Callback employed by drvscsiSuspend and drvscsiPowerOff.
1090 *
1091 * @returns true if we've quiesced, false if we're still working.
1092 * @param pDrvIns The driver instance.
1093 */
1094static DECLCALLBACK(bool) drvscsiIsAsyncSuspendOrPowerOffDone(PPDMDRVINS pDrvIns)
1095{
1096 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
1097
1098 if (pThis->StatIoDepth > 0)
1099 return false;
1100 else
1101 return true;
1102}
1103
1104/**
1105 * @copydoc FNPDMDRVPOWEROFF
1106 */
1107static DECLCALLBACK(void) drvscsiPowerOff(PPDMDRVINS pDrvIns)
1108{
1109 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncSuspendOrPowerOffDone);
1110}
1111
1112/**
1113 * @copydoc FNPDMDRVSUSPEND
1114 */
1115static DECLCALLBACK(void) drvscsiSuspend(PPDMDRVINS pDrvIns)
1116{
1117 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncSuspendOrPowerOffDone);
1118}
1119
1120/**
1121 * Callback employed by drvscsiReset.
1122 *
1123 * @returns true if we've quiesced, false if we're still working.
1124 * @param pDrvIns The driver instance.
1125 */
1126static DECLCALLBACK(bool) drvscsiIsAsyncResetDone(PPDMDRVINS pDrvIns)
1127{
1128 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
1129
1130 if (pThis->StatIoDepth > 0)
1131 return false;
1132 else
1133 return true;
1134}
1135
1136/** @copydoc FNPDMDRVATTACH */
1137static DECLCALLBACK(int) drvscsiAttach(PPDMDRVINS pDrvIns, uint32_t fFlags)
1138{
1139 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
1140
1141 LogFlowFunc(("pDrvIns=%#p fFlags=%#x\n", pDrvIns, fFlags));
1142
1143 AssertMsgReturn((fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG),
1144 ("SCSI: Hotplugging is not supported\n"),
1145 VERR_INVALID_PARAMETER);
1146
1147 /*
1148 * Try attach driver below and query it's media interface.
1149 */
1150 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pThis->pDrvBase);
1151 AssertMsgReturn(RT_SUCCESS(rc), ("Attaching driver below failed rc=%Rrc\n", rc), rc);
1152
1153 /*
1154 * Query the media interface.
1155 */
1156 pThis->pDrvMedia = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMEDIA);
1157 AssertMsgReturn(VALID_PTR(pThis->pDrvMedia), ("VSCSI configuration error: No media interface!\n"),
1158 VERR_PDM_MISSING_INTERFACE);
1159
1160 /* Query the extended media interface. */
1161 pThis->pDrvMediaEx = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMEDIAEX);
1162 AssertMsgReturn(VALID_PTR(pThis->pDrvMediaEx), ("VSCSI configuration error: No extended media interface!\n"),
1163 VERR_PDM_MISSING_INTERFACE);
1164
1165 pThis->pDrvMount = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMOUNT);
1166
1167 if (pThis->cbVScsiIoReqAlloc)
1168 {
1169 rc = pThis->pDrvMediaEx->pfnIoReqAllocSizeSet(pThis->pDrvMediaEx, pThis->cbVScsiIoReqAlloc);
1170 AssertMsgReturn(RT_SUCCESS(rc), ("Setting the I/O request allocation size failed with rc=%Rrc\n", rc), rc);
1171 }
1172
1173 if (pThis->pDrvMount)
1174 {
1175 if (pThis->pDrvMount->pfnIsMounted(pThis->pDrvMount))
1176 {
1177 rc = VINF_SUCCESS; VSCSILunMountNotify(pThis->hVScsiLun);
1178 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to notify the LUN of media being mounted\n"), rc);
1179 }
1180 else
1181 {
1182 rc = VINF_SUCCESS; VSCSILunUnmountNotify(pThis->hVScsiLun);
1183 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to notify the LUN of media being unmounted\n"), rc);
1184 }
1185 }
1186
1187 return rc;
1188}
1189
1190/** @copydoc FNPDMDRVDETACH */
1191static DECLCALLBACK(void) drvscsiDetach(PPDMDRVINS pDrvIns, uint32_t fFlags)
1192{
1193 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
1194
1195 LogFlowFunc(("pDrvIns=%#p fFlags=%#x\n", pDrvIns, fFlags));
1196
1197 AssertMsgReturnVoid((fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG),
1198 ("SCSI: Hotplugging is not supported\n"));
1199
1200 /*
1201 * Zero some important members.
1202 */
1203 pThis->pDrvBase = NULL;
1204 pThis->pDrvMedia = NULL;
1205 pThis->pDrvMediaEx = NULL;
1206 pThis->pDrvMount = NULL;
1207
1208 VSCSILunUnmountNotify(pThis->hVScsiLun);
1209}
1210
1211/**
1212 * @copydoc FNPDMDRVRESET
1213 */
1214static DECLCALLBACK(void) drvscsiReset(PPDMDRVINS pDrvIns)
1215{
1216 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncResetDone);
1217}
1218
1219/**
1220 * Destruct a driver instance.
1221 *
1222 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
1223 * resources can be freed correctly.
1224 *
1225 * @param pDrvIns The driver instance data.
1226 */
1227static DECLCALLBACK(void) drvscsiDestruct(PPDMDRVINS pDrvIns)
1228{
1229 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
1230 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1231
1232 /* Free the VSCSI device and LUN handle. */
1233 if (pThis->hVScsiDevice)
1234 {
1235 VSCSILUN hVScsiLun;
1236 int rc = VSCSIDeviceLunDetach(pThis->hVScsiDevice, 0, &hVScsiLun);
1237 AssertRC(rc);
1238
1239 Assert(hVScsiLun == pThis->hVScsiLun);
1240 rc = VSCSILunDestroy(hVScsiLun);
1241 AssertRC(rc);
1242 rc = VSCSIDeviceDestroy(pThis->hVScsiDevice);
1243 AssertRC(rc);
1244
1245 pThis->hVScsiDevice = NULL;
1246 pThis->hVScsiLun = NULL;
1247 }
1248
1249 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatBytesRead);
1250 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatBytesWritten);
1251 PDMDrvHlpSTAMDeregister(pDrvIns, (void *)&pThis->StatIoDepth);
1252}
1253
1254/**
1255 * Construct a block driver instance.
1256 *
1257 * @copydoc FNPDMDRVCONSTRUCT
1258 */
1259static DECLCALLBACK(int) drvscsiConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1260{
1261 int rc = VINF_SUCCESS;
1262 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
1263 LogFlowFunc(("pDrvIns=%#p pCfg=%#p\n", pDrvIns, pCfg));
1264 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1265
1266 /*
1267 * Initialize the instance data.
1268 */
1269 pThis->pDrvIns = pDrvIns;
1270
1271 pDrvIns->IBase.pfnQueryInterface = drvscsiQueryInterface;
1272
1273 /* IMedia */
1274 pThis->IMedia.pfnRead = NULL;
1275 pThis->IMedia.pfnReadPcBios = NULL;
1276 pThis->IMedia.pfnWrite = NULL;
1277 pThis->IMedia.pfnFlush = NULL;
1278 pThis->IMedia.pfnSendCmd = NULL;
1279 pThis->IMedia.pfnMerge = NULL;
1280 pThis->IMedia.pfnSetSecKeyIf = NULL;
1281 pThis->IMedia.pfnGetSize = drvscsiGetSize;
1282 pThis->IMedia.pfnGetSectorSize = drvscsiGetSectorSize;
1283 pThis->IMedia.pfnIsReadOnly = drvscsiIsReadOnly;
1284 pThis->IMedia.pfnIsNonRotational = drvscsiIsNonRotational;
1285 pThis->IMedia.pfnBiosGetPCHSGeometry = drvscsiBiosGetPCHSGeometry;
1286 pThis->IMedia.pfnBiosSetPCHSGeometry = drvscsiBiosSetPCHSGeometry;
1287 pThis->IMedia.pfnBiosGetLCHSGeometry = drvscsiBiosGetLCHSGeometry;
1288 pThis->IMedia.pfnBiosSetLCHSGeometry = drvscsiBiosSetLCHSGeometry;
1289 pThis->IMedia.pfnBiosIsVisible = drvscsiBiosIsVisible;
1290 pThis->IMedia.pfnGetType = drvscsiGetType;
1291 pThis->IMedia.pfnGetUuid = drvscsiGetUuid;
1292 pThis->IMedia.pfnDiscard = NULL;
1293
1294 /* IMediaEx */
1295 pThis->IMediaEx.pfnQueryFeatures = drvscsiQueryFeatures;
1296 pThis->IMediaEx.pfnIoReqAllocSizeSet = drvscsiIoReqAllocSizeSet;
1297 pThis->IMediaEx.pfnIoReqAlloc = drvscsiIoReqAlloc;
1298 pThis->IMediaEx.pfnIoReqFree = drvscsiIoReqFree;
1299 pThis->IMediaEx.pfnIoReqQueryResidual = drvscsiIoReqQueryResidual;
1300 pThis->IMediaEx.pfnIoReqQueryXferSize = drvscsiIoReqQueryXferSize;
1301 pThis->IMediaEx.pfnIoReqCancelAll = drvscsiIoReqCancelAll;
1302 pThis->IMediaEx.pfnIoReqCancel = drvscsiIoReqCancel;
1303 pThis->IMediaEx.pfnIoReqRead = drvscsiIoReqRead;
1304 pThis->IMediaEx.pfnIoReqWrite = drvscsiIoReqWrite;
1305 pThis->IMediaEx.pfnIoReqFlush = drvscsiIoReqFlush;
1306 pThis->IMediaEx.pfnIoReqDiscard = drvscsiIoReqDiscard;
1307 pThis->IMediaEx.pfnIoReqSendScsiCmd = drvscsiIoReqSendScsiCmd;
1308 pThis->IMediaEx.pfnIoReqGetActiveCount = drvscsiIoReqGetActiveCount;
1309 pThis->IMediaEx.pfnIoReqGetSuspendedCount = drvscsiIoReqGetSuspendedCount;
1310 pThis->IMediaEx.pfnIoReqQuerySuspendedStart = drvscsiIoReqQuerySuspendedStart;
1311 pThis->IMediaEx.pfnIoReqQuerySuspendedNext = drvscsiIoReqQuerySuspendedNext;
1312 pThis->IMediaEx.pfnIoReqSuspendedSave = drvscsiIoReqSuspendedSave;
1313 pThis->IMediaEx.pfnIoReqSuspendedLoad = drvscsiIoReqSuspendedLoad;
1314
1315 pThis->IMountNotify.pfnMountNotify = drvscsiMountNotify;
1316 pThis->IMountNotify.pfnUnmountNotify = drvscsiUnmountNotify;
1317 pThis->IPort.pfnQueryDeviceLocation = drvscsiQueryDeviceLocation;
1318 pThis->IPortEx.pfnIoReqCompleteNotify = drvscsiIoReqCompleteNotify;
1319 pThis->IPortEx.pfnIoReqCopyFromBuf = drvscsiIoReqCopyFromBuf;
1320 pThis->IPortEx.pfnIoReqCopyToBuf = drvscsiIoReqCopyToBuf;
1321 pThis->IPortEx.pfnIoReqQueryDiscardRanges = drvscsiIoReqQueryDiscardRanges;
1322 pThis->IPortEx.pfnIoReqStateChanged = drvscsiIoReqStateChanged;
1323
1324 /* Query the optional media port interface above. */
1325 pThis->pDevMediaPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMEDIAPORT);
1326
1327 /* Query the optional extended media port interface above. */
1328 pThis->pDevMediaExPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMEDIAEXPORT);
1329
1330 AssertMsgReturn(pThis->pDevMediaExPort,
1331 ("Missing extended media port interface above\n"), VERR_PDM_MISSING_INTERFACE);
1332
1333 /* Query the optional LED interface above. */
1334 pThis->pLedPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
1335 if (pThis->pLedPort != NULL)
1336 {
1337 /* Get The Led. */
1338 rc = pThis->pLedPort->pfnQueryStatusLed(pThis->pLedPort, 0, &pThis->pLed);
1339 if (RT_FAILURE(rc))
1340 pThis->pLed = &pThis->Led;
1341 }
1342 else
1343 pThis->pLed = &pThis->Led;
1344
1345 /*
1346 * Validate and read configuration.
1347 */
1348 if (!CFGMR3AreValuesValid(pCfg, ""))
1349 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
1350 N_("SCSI configuration error: unknown option specified"));
1351
1352 /*
1353 * Try attach driver below and query it's media interface.
1354 */
1355 rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pThis->pDrvBase);
1356 AssertMsgReturn(RT_SUCCESS(rc), ("Attaching driver below failed rc=%Rrc\n", rc), rc);
1357
1358 /*
1359 * Query the media interface.
1360 */
1361 pThis->pDrvMedia = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMEDIA);
1362 AssertMsgReturn(VALID_PTR(pThis->pDrvMedia), ("VSCSI configuration error: No media interface!\n"),
1363 VERR_PDM_MISSING_INTERFACE);
1364
1365 /* Query the extended media interface. */
1366 pThis->pDrvMediaEx = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMEDIAEX);
1367 AssertMsgReturn(VALID_PTR(pThis->pDrvMediaEx), ("VSCSI configuration error: No extended media interface!\n"),
1368 VERR_PDM_MISSING_INTERFACE);
1369
1370 pThis->pDrvMount = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMOUNT);
1371
1372 PDMMEDIATYPE enmType = pThis->pDrvMedia->pfnGetType(pThis->pDrvMedia);
1373 VSCSILUNTYPE enmLunType;
1374 switch (enmType)
1375 {
1376 case PDMMEDIATYPE_HARD_DISK:
1377 enmLunType = VSCSILUNTYPE_SBC;
1378 break;
1379 case PDMMEDIATYPE_CDROM:
1380 case PDMMEDIATYPE_DVD:
1381 enmLunType = VSCSILUNTYPE_MMC;
1382 break;
1383 default:
1384 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_UNSUPPORTED_BLOCK_TYPE, RT_SRC_POS,
1385 N_("Only hard disks and CD/DVD-ROMs are currently supported as SCSI devices (enmType=%d)"),
1386 enmType);
1387 }
1388 if ( ( enmType == PDMMEDIATYPE_DVD
1389 || enmType == PDMMEDIATYPE_CDROM)
1390 && !pThis->pDrvMount)
1391 {
1392 AssertMsgFailed(("Internal error: cdrom without a mountable interface\n"));
1393 return VERR_INTERNAL_ERROR;
1394 }
1395
1396 /* Create VSCSI device and LUN. */
1397 pThis->VScsiIoCallbacks.pfnVScsiLunReqAllocSizeSet = drvscsiReqAllocSizeSet;
1398 pThis->VScsiIoCallbacks.pfnVScsiLunReqAlloc = drvscsiReqAlloc;
1399 pThis->VScsiIoCallbacks.pfnVScsiLunReqFree = drvscsiReqFree;
1400 pThis->VScsiIoCallbacks.pfnVScsiLunMediumGetSize = drvscsiGetSize;
1401 pThis->VScsiIoCallbacks.pfnVScsiLunMediumGetSectorSize = drvscsiGetSectorSize;
1402 pThis->VScsiIoCallbacks.pfnVScsiLunMediumEject = drvscsiEject;
1403 pThis->VScsiIoCallbacks.pfnVScsiLunReqTransferEnqueue = drvscsiReqTransferEnqueue;
1404 pThis->VScsiIoCallbacks.pfnVScsiLunGetFeatureFlags = drvscsiGetFeatureFlags;
1405 pThis->VScsiIoCallbacks.pfnVScsiLunMediumSetLock = drvscsiSetLock;
1406
1407 rc = VSCSIDeviceCreate(&pThis->hVScsiDevice, drvscsiIoReqVScsiReqCompleted, pThis);
1408 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI device rc=%Rrc\n", rc), rc);
1409 rc = VSCSILunCreate(&pThis->hVScsiLun, enmLunType, &pThis->VScsiIoCallbacks,
1410 pThis);
1411 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI LUN rc=%Rrc\n", rc), rc);
1412 rc = VSCSIDeviceLunAttach(pThis->hVScsiDevice, pThis->hVScsiLun, 0);
1413 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to attached the LUN to the SCSI device\n"), rc);
1414
1415 /// @todo This is a very hacky way of telling the LUN whether a medium was mounted.
1416 // The mount/unmount interface doesn't work in a very sensible manner!
1417 if (pThis->pDrvMount)
1418 {
1419 if (pThis->pDrvMount->pfnIsMounted(pThis->pDrvMount))
1420 {
1421 rc = VINF_SUCCESS; VSCSILunMountNotify(pThis->hVScsiLun);
1422 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to notify the LUN of media being mounted\n"), rc);
1423 }
1424 else
1425 {
1426 rc = VINF_SUCCESS; VSCSILunUnmountNotify(pThis->hVScsiLun);
1427 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to notify the LUN of media being unmounted\n"), rc);
1428 }
1429 }
1430
1431 const char *pszCtrl = NULL;
1432 uint32_t iCtrlInstance = 0;
1433 uint32_t iCtrlLun = 0;
1434
1435 rc = pThis->pDevMediaPort->pfnQueryDeviceLocation(pThis->pDevMediaPort, &pszCtrl, &iCtrlInstance, &iCtrlLun);
1436 if (RT_SUCCESS(rc))
1437 {
1438 const char *pszCtrlId = strcmp(pszCtrl, "Msd") == 0 ? "USB"
1439 : strcmp(pszCtrl, "lsilogicsas") == 0 ? "SAS"
1440 : "SCSI";
1441 /* Register statistics counter. */
1442 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
1443 "Amount of data read.", "/Devices/%s%u/%u/ReadBytes", pszCtrlId, iCtrlInstance, iCtrlLun);
1444 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
1445 "Amount of data written.", "/Devices/%s%u/%u/WrittenBytes", pszCtrlId, iCtrlInstance, iCtrlLun);
1446 PDMDrvHlpSTAMRegisterF(pDrvIns, (void *)&pThis->StatIoDepth, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
1447 "Number of active tasks.", "/Devices/%s%u/%u/IoDepth", pszCtrlId, iCtrlInstance, iCtrlLun);
1448 }
1449
1450 pThis->StatIoDepth = 0;
1451
1452 uint32_t fFeatures = 0;
1453 rc = pThis->pDrvMediaEx->pfnQueryFeatures(pThis->pDrvMediaEx, &fFeatures);
1454 if (RT_FAILURE(rc))
1455 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1456 N_("VSCSI configuration error: Failed to query features of device"));
1457 if (fFeatures & PDMIMEDIAEX_FEATURE_F_DISCARD)
1458 LogRel(("SCSI#%d: Enabled UNMAP support\n", pDrvIns->iInstance));
1459
1460 rc = PDMDrvHlpQueueCreate(pDrvIns, sizeof(DRVSCSIEJECTSTATE), 1, 0, drvscsiR3NotifyQueueConsumer,
1461 "SCSI-Eject", &pThis->pQueue);
1462 if (RT_FAILURE(rc))
1463 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1464 N_("VSCSI configuration error: Failed to create notification queue"));
1465
1466 return VINF_SUCCESS;
1467}
1468
1469/**
1470 * SCSI driver registration record.
1471 */
1472const PDMDRVREG g_DrvSCSI =
1473{
1474 /* u32Version */
1475 PDM_DRVREG_VERSION,
1476 /* szName */
1477 "SCSI",
1478 /* szRCMod */
1479 "",
1480 /* szR0Mod */
1481 "",
1482 /* pszDescription */
1483 "Generic SCSI driver.",
1484 /* fFlags */
1485 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1486 /* fClass. */
1487 PDM_DRVREG_CLASS_SCSI,
1488 /* cMaxInstances */
1489 ~0U,
1490 /* cbInstance */
1491 sizeof(DRVSCSI),
1492 /* pfnConstruct */
1493 drvscsiConstruct,
1494 /* pfnDestruct */
1495 drvscsiDestruct,
1496 /* pfnRelocate */
1497 NULL,
1498 /* pfnIOCtl */
1499 NULL,
1500 /* pfnPowerOn */
1501 NULL,
1502 /* pfnReset */
1503 drvscsiReset,
1504 /* pfnSuspend */
1505 drvscsiSuspend,
1506 /* pfnResume */
1507 NULL,
1508 /* pfnAttach */
1509 drvscsiAttach,
1510 /* pfnDetach */
1511 drvscsiDetach,
1512 /* pfnPowerOff */
1513 drvscsiPowerOff,
1514 /* pfnSoftReset */
1515 NULL,
1516 /* u32EndVersion */
1517 PDM_DRVREG_VERSION
1518};
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