VirtualBox

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

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

pdmstorageifs.h,AHCI,NVMe,LsiLogic,BusLogic,DrvSCSI: Prepare for a new callback in PDMIMEDIAEXPORT which can reduce the overhead of copying data between buffers under certain circumstances

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

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