VirtualBox

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

Last change on this file since 91968 was 91905, checked in by vboxsync, 3 years ago

Drv*,PDMDrvHlp: Added driver helpers for all queues operations, switching to PDMQUEUEHANDLE instead of PPDMQUEUE just like already done for the devices. bugref:10074

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