VirtualBox

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

Last change on this file since 64137 was 64136, checked in by vboxsync, 9 years ago

build fix

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

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