VirtualBox

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

最後變更 在這個檔案從27654是 27654,由 vboxsync 提交於 15 年 前

DrvSCSI: Use the new VSCSI component and implement the async interface

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 23.8 KB
 
1/* $Id: DrvSCSI.cpp 27654 2010-03-23 23:29:03Z vboxsync $ */
2/** @file
3 * VBox storage drivers: Generic SCSI command parser and execution driver
4 */
5
6/*
7 * Copyright (C) 2006-2010 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22/*******************************************************************************
23* Header Files *
24*******************************************************************************/
25//#define DEBUG
26#define LOG_GROUP LOG_GROUP_DRV_SCSI
27#include <VBox/pdmdrv.h>
28#include <VBox/pdmifs.h>
29#include <VBox/pdmthread.h>
30#include <VBox/vscsi.h>
31#include <iprt/assert.h>
32#include <iprt/mem.h>
33#include <iprt/req.h>
34#include <iprt/semaphore.h>
35#include <iprt/string.h>
36#include <iprt/uuid.h>
37
38#include "Builtins.h"
39
40/**
41 * SCSI driver instance data.
42 *
43 * @implements PDMISCSICONNECTOR
44 * @implements PDMIBLOCKASYNCPORT
45 * @implements PDMIMOUNTNOTIFY
46 */
47typedef struct DRVSCSI
48{
49 /** Pointer driver instance. */
50 PPDMDRVINS pDrvIns;
51
52 /** Pointer to the attached driver's base interface. */
53 PPDMIBASE pDrvBase;
54 /** Pointer to the attached driver's block interface. */
55 PPDMIBLOCK pDrvBlock;
56 /** Pointer to the attached driver's async block interface. */
57 PPDMIBLOCKASYNC pDrvBlockAsync;
58 /** Pointer to the attached driver's block bios interface. */
59 PPDMIBLOCKBIOS pDrvBlockBios;
60 /** Pointer to the attached driver's mount interface. */
61 PPDMIMOUNT pDrvMount;
62 /** Pointer to the SCSI port interface of the device above. */
63 PPDMISCSIPORT pDevScsiPort;
64 /** pointer to the Led port interface of the dveice above. */
65 PPDMILEDPORTS pLedPort;
66 /** The scsi connector interface .*/
67 PDMISCSICONNECTOR ISCSIConnector;
68 /** The block port interface. */
69 PDMIBLOCKPORT IPort;
70 /** The optional block async port interface. */
71 PDMIBLOCKASYNCPORT IPortAsync;
72#if 0 /* these interfaces aren't implemented */
73 /** The mount notify interface. */
74 PDMIMOUNTNOTIFY IMountNotify;
75#endif
76 /** Fallback status LED state for this drive.
77 * This is used in case the device doesn't has a LED interface. */
78 PDMLED Led;
79 /** Pointer to the status LED for this drive. */
80 PPDMLED pLed;
81
82 /** VSCSI device handle. */
83 VSCSIDEVICE hVScsiDevice;
84 /** VSCSI LUN handle. */
85 VSCSILUN hVScsiLun;
86 /** I/O callbacks. */
87 VSCSILUNIOCALLBACKS VScsiIoCallbacks;
88
89 /** The dedicated I/O thread for the non async approach. */
90 PPDMTHREAD pAsyncIOThread;
91 /** Queue for passing the requests to the thread. */
92 PRTREQQUEUE pQueueRequests;
93 /** Request that we've left pending on wakeup or reset. */
94 PRTREQ pPendingDummyReq;
95 /** Indicates whether PDMDrvHlpAsyncNotificationCompleted should be called by
96 * any of the dummy functions. */
97 bool volatile fDummySignal;
98 /** Release statistics: number of bytes written. */
99 STAMCOUNTER StatBytesWritten;
100 /** Release statistics: number of bytes read. */
101 STAMCOUNTER StatBytesRead;
102} DRVSCSI, *PDRVSCSI;
103
104/** Converts a pointer to DRVSCSI::ISCSIConnector to a PDRVSCSI. */
105#define PDMISCSICONNECTOR_2_DRVSCSI(pInterface) ( (PDRVSCSI)((uintptr_t)pInterface - RT_OFFSETOF(DRVSCSI, ISCSIConnector)) )
106/** Converts a pointer to DRVSCSI::IPortAsync to a PDRVSCSI. */
107#define PDMIBLOCKASYNCPORT_2_DRVSCSI(pInterface) ( (PDRVSCSI)((uintptr_t)pInterface - RT_OFFSETOF(DRVSCSI, IPortAsync)) )
108
109static int drvscsiProcessRequestOne(PDRVSCSI pThis, VSCSIIOREQ hVScsiIoReq)
110{
111 int rc = VINF_SUCCESS;
112 VSCSIIOREQTXDIR enmTxDir;
113
114 enmTxDir = VSCSIIoReqTxDirGet(hVScsiIoReq);
115
116 switch (enmTxDir)
117 {
118 case VSCSIIOREQTXDIR_FLUSH:
119 {
120 rc = pThis->pDrvBlock->pfnFlush(pThis->pDrvBlock);
121 break;
122 }
123 case VSCSIIOREQTXDIR_READ:
124 case VSCSIIOREQTXDIR_WRITE:
125 {
126 uint64_t uOffset = 0;
127 size_t cbTransfer = 0;
128 size_t cbSeg = 0;
129 PCPDMDATASEG paSeg = NULL;
130 unsigned cSeg = 0;
131
132 rc = VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer, &cSeg, &cbSeg,
133 &paSeg);
134 AssertRC(rc);
135
136 while (cbTransfer && cSeg)
137 {
138 size_t cbProcess = (cbTransfer < paSeg->cbSeg) ? cbTransfer : paSeg->cbSeg;
139
140 Log(("%s: uOffset=%llu cbProcess=%u\n", __FUNCTION__, uOffset, cbProcess));
141
142 if (enmTxDir == VSCSIIOREQTXDIR_READ)
143 {
144 pThis->pLed->Asserted.s.fReading = pThis->pLed->Actual.s.fReading = 1;
145 rc = pThis->pDrvBlock->pfnRead(pThis->pDrvBlock, uOffset,
146 paSeg->pvSeg, cbProcess);
147 pThis->pLed->Actual.s.fReading = 0;
148 if (RT_FAILURE(rc))
149 AssertMsgFailed(("%s: Failed to read data %Rrc\n", __FUNCTION__, rc));
150 STAM_REL_COUNTER_ADD(&pThis->StatBytesRead, cbProcess);
151 }
152 else
153 {
154 pThis->pLed->Asserted.s.fWriting = pThis->pLed->Actual.s.fWriting = 1;
155 rc = pThis->pDrvBlock->pfnWrite(pThis->pDrvBlock, uOffset,
156 paSeg->pvSeg, cbProcess);
157 pThis->pLed->Actual.s.fWriting = 0;
158 if (RT_FAILURE(rc))
159 AssertMsgFailed(("%s: Failed to write data %Rrc\n", __FUNCTION__, rc));
160 STAM_REL_COUNTER_ADD(&pThis->StatBytesWritten, cbProcess);
161 }
162
163 /* Go to the next entry. */
164 uOffset += cbProcess;
165 cbTransfer -= cbProcess;
166 paSeg++;
167 cSeg--;
168 }
169
170 break;
171 }
172 default:
173 AssertMsgFailed(("Invalid transfer direction %d\n", enmTxDir));
174 }
175
176 VSCSIIoReqCompleted(hVScsiIoReq, rc);
177
178 return VINF_SUCCESS;
179}
180
181static int drvscsiGetSize(VSCSILUN hVScsiLun, void *pvScsiLunUser, uint64_t *pcbSize)
182{
183 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
184
185 *pcbSize = pThis->pDrvBlock->pfnGetSize(pThis->pDrvBlock);
186
187 return VINF_SUCCESS;
188}
189
190static int drvscsiTransferCompleteNotify(PPDMIBLOCKASYNCPORT pInterface, void *pvUser)
191{
192 PDRVSCSI pThis = PDMIBLOCKASYNCPORT_2_DRVSCSI(pInterface);
193 VSCSIIOREQ hVScsiIoReq = (VSCSIIOREQ)pvUser;
194 VSCSIIOREQTXDIR enmTxDir = VSCSIIoReqTxDirGet(hVScsiIoReq);
195
196 if (enmTxDir == VSCSIIOREQTXDIR_READ)
197 pThis->pLed->Actual.s.fReading = 0;
198 else if (enmTxDir == VSCSIIOREQTXDIR_WRITE)
199 pThis->pLed->Actual.s.fWriting = 0;
200 else
201 AssertMsgFailed(("Invalid transfer direction %u\n", enmTxDir));
202
203 VSCSIIoReqCompleted(hVScsiIoReq, VINF_SUCCESS);
204
205 return VINF_SUCCESS;
206}
207
208static int drvscsiReqTransferEnqueue(VSCSILUN hVScsiLun,
209 void *pvScsiLunUser,
210 VSCSIIOREQ hVScsiIoReq)
211{
212 int rc = VINF_SUCCESS;
213 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
214
215 if (pThis->pDrvBlockAsync)
216 {
217 /* asnyc I/O path. */
218 VSCSIIOREQTXDIR enmTxDir;
219
220 enmTxDir = VSCSIIoReqTxDirGet(hVScsiIoReq);
221
222 switch (enmTxDir)
223 {
224 case VSCSIIOREQTXDIR_FLUSH:
225 {
226 /** @todo Flush callback for the async I/O interface. */
227 VSCSIIoReqCompleted(hVScsiIoReq, VINF_SUCCESS);
228 break;
229 }
230 case VSCSIIOREQTXDIR_READ:
231 case VSCSIIOREQTXDIR_WRITE:
232 {
233 uint64_t uOffset = 0;
234 size_t cbTransfer = 0;
235 size_t cbSeg = 0;
236 PCPDMDATASEG paSeg = NULL;
237 unsigned cSeg = 0;
238
239 rc = VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer,
240 &cSeg, &cbSeg, &paSeg);
241 AssertRC(rc);
242
243 if (enmTxDir == VSCSIIOREQTXDIR_READ)
244 {
245 pThis->pLed->Asserted.s.fReading = pThis->pLed->Actual.s.fReading = 1;
246 rc = pThis->pDrvBlockAsync->pfnStartRead(pThis->pDrvBlockAsync, uOffset,
247 (PPDMDATASEG)paSeg, cSeg, cbTransfer,
248 hVScsiIoReq);
249 if (RT_FAILURE(rc))
250 AssertMsgFailed(("%s: Failed to read data %Rrc\n", __FUNCTION__, rc));
251 STAM_REL_COUNTER_ADD(&pThis->StatBytesRead, cbSeg);
252 }
253 else
254 {
255 pThis->pLed->Asserted.s.fWriting = pThis->pLed->Actual.s.fWriting = 1;
256 rc = pThis->pDrvBlockAsync->pfnStartWrite(pThis->pDrvBlockAsync, uOffset,
257 (PPDMDATASEG)paSeg, cSeg, cbTransfer,
258 hVScsiIoReq);
259 if (RT_FAILURE(rc))
260 AssertMsgFailed(("%s: Failed to write data %Rrc\n", __FUNCTION__, rc));
261 STAM_REL_COUNTER_ADD(&pThis->StatBytesWritten, cbSeg);
262 }
263
264 break;
265 }
266 default:
267 AssertMsgFailed(("Invalid transfer direction %u\n", enmTxDir));
268 }
269 }
270 else
271 {
272 /* I/O thread. */
273 rc = RTReqCallEx(pThis->pQueueRequests, NULL, 0, RTREQFLAGS_NO_WAIT,
274 (PFNRT)drvscsiProcessRequestOne, 2, pThis, hVScsiIoReq);
275 }
276
277 return rc;
278}
279
280static void drvscsiVScsiReqCompleted(VSCSIDEVICE hVScsiDevice, void *pVScsiDeviceUser,
281 void *pVScsiReqUser, int rcReq)
282{
283 PDRVSCSI pThis = (PDRVSCSI)pVScsiDeviceUser;
284
285 pThis->pDevScsiPort->pfnSCSIRequestCompleted(pThis->pDevScsiPort, (PPDMSCSIREQUEST)pVScsiReqUser,
286 rcReq);
287}
288
289/**
290 * Dummy request function used by drvscsiReset to wait for all pending requests
291 * to complete prior to the device reset.
292 *
293 * @param pThis Pointer to the instace data.
294 * @returns VINF_SUCCESS.
295 */
296static int drvscsiAsyncIOLoopSyncCallback(PDRVSCSI pThis)
297{
298 if (pThis->fDummySignal)
299 PDMDrvHlpAsyncNotificationCompleted(pThis->pDrvIns);
300 return VINF_SUCCESS;
301}
302
303/**
304 * Request function to wakeup the thread.
305 *
306 * @param pThis Pointer to the instace data.
307 * @returns VWRN_STATE_CHANGED.
308 */
309static int drvscsiAsyncIOLoopWakeupFunc(PDRVSCSI pThis)
310{
311 if (pThis->fDummySignal)
312 PDMDrvHlpAsyncNotificationCompleted(pThis->pDrvIns);
313 return VWRN_STATE_CHANGED;
314}
315
316/**
317 * The thread function which processes the requests asynchronously.
318 *
319 * @returns VBox status code.
320 * @param pDrvIns Pointer to the driver instance data.
321 * @param pThread Pointer to the thread instance data.
322 */
323static int drvscsiAsyncIOLoop(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
324{
325 int rc = VINF_SUCCESS;
326 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
327
328 LogFlowFunc(("Entering async IO loop.\n"));
329
330 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
331 return VINF_SUCCESS;
332
333 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
334 {
335 rc = RTReqProcess(pThis->pQueueRequests, RT_INDEFINITE_WAIT);
336 AssertMsg(rc == VWRN_STATE_CHANGED, ("Left RTReqProcess and error code is not VWRN_STATE_CHANGED rc=%Rrc\n", rc));
337 }
338
339 return VINF_SUCCESS;
340}
341
342/**
343 * Deals with any pending dummy request
344 *
345 * @returns true if no pending dummy request, false if still pending.
346 * @param pThis The instance data.
347 * @param cMillies The number of milliseconds to wait for any
348 * pending request to finish.
349 */
350static bool drvscsiAsyncIOLoopNoPendingDummy(PDRVSCSI pThis, uint32_t cMillies)
351{
352 if (!pThis->pPendingDummyReq)
353 return true;
354 int rc = RTReqWait(pThis->pPendingDummyReq, cMillies);
355 if (RT_FAILURE(rc))
356 return false;
357 RTReqFree(pThis->pPendingDummyReq);
358 pThis->pPendingDummyReq = NULL;
359 return true;
360}
361
362static int drvscsiAsyncIOLoopWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
363{
364 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
365 PRTREQ pReq;
366 int rc;
367
368 AssertMsgReturn(pThis->pQueueRequests, ("pQueueRequests is NULL\n"), VERR_INVALID_STATE);
369
370 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 10000 /* 10 sec */))
371 {
372 LogRel(("drvscsiAsyncIOLoopWakeup#%u: previous dummy request is still pending\n", pDrvIns->iInstance));
373 return VERR_TIMEOUT;
374 }
375
376 rc = RTReqCall(pThis->pQueueRequests, &pReq, 10000 /* 10 sec. */, (PFNRT)drvscsiAsyncIOLoopWakeupFunc, 1, pThis);
377 if (RT_SUCCESS(rc))
378 RTReqFree(pReq);
379 else
380 {
381 pThis->pPendingDummyReq = pReq;
382 LogRel(("drvscsiAsyncIOLoopWakeup#%u: %Rrc pReq=%p\n", pDrvIns->iInstance, rc, pReq));
383 }
384
385 return rc;
386}
387
388/* -=-=-=-=- ISCSIConnector -=-=-=-=- */
389
390/** @copydoc PDMISCSICONNECTOR::pfnSCSIRequestSend. */
391static DECLCALLBACK(int) drvscsiRequestSend(PPDMISCSICONNECTOR pInterface, PPDMSCSIREQUEST pSCSIRequest)
392{
393 int rc;
394 PDRVSCSI pThis = PDMISCSICONNECTOR_2_DRVSCSI(pInterface);
395 VSCSIREQ hVScsiReq;
396
397 rc = VSCSIDeviceReqCreate(pThis->hVScsiDevice, &hVScsiReq,
398 pSCSIRequest->uLogicalUnit,
399 pSCSIRequest->pbCDB,
400 pSCSIRequest->cbCDB,
401 pSCSIRequest->cbScatterGather,
402 pSCSIRequest->cScatterGatherEntries,
403 pSCSIRequest->paScatterGatherHead,
404 pSCSIRequest->pbSenseBuffer,
405 pSCSIRequest->cbSenseBuffer,
406 pSCSIRequest);
407 if (RT_FAILURE(rc))
408 return rc;
409
410 rc = VSCSIDeviceReqEnqueue(pThis->hVScsiDevice, hVScsiReq);
411
412 return rc;
413}
414
415/* -=-=-=-=- IBase -=-=-=-=- */
416
417/**
418 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
419 */
420static DECLCALLBACK(void *) drvscsiQueryInterface(PPDMIBASE pInterface, const char *pszIID)
421{
422 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
423 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
424
425 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
426 PDMIBASE_RETURN_INTERFACE(pszIID, PDMISCSICONNECTOR, &pThis->ISCSIConnector);
427 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCKPORT, &pThis->IPort);
428 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCKASYNCPORT, &pThis->IPortAsync);
429 return NULL;
430}
431
432/**
433 * Worker for drvscsiReset, drvscsiSuspend and drvscsiPowerOff.
434 *
435 * @param pDrvIns The driver instance.
436 * @param pfnAsyncNotify The async callback.
437 */
438static void drvscsiR3ResetOrSuspendOrPowerOff(PPDMDRVINS pDrvIns, PFNPDMDRVASYNCNOTIFY pfnAsyncNotify)
439{
440 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
441
442 if (!pThis->pQueueRequests)
443 return;
444
445 ASMAtomicWriteBool(&pThis->fDummySignal, true);
446 if (drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/))
447 {
448 if (!RTReqIsBusy(pThis->pQueueRequests))
449 {
450 ASMAtomicWriteBool(&pThis->fDummySignal, false);
451 return;
452 }
453
454 PRTREQ pReq;
455 int rc = RTReqCall(pThis->pQueueRequests, &pReq, 0 /*ms*/, (PFNRT)drvscsiAsyncIOLoopSyncCallback, 1, pThis);
456 if (RT_SUCCESS(rc))
457 {
458 ASMAtomicWriteBool(&pThis->fDummySignal, false);
459 RTReqFree(pReq);
460 return;
461 }
462
463 pThis->pPendingDummyReq = pReq;
464 }
465 PDMDrvHlpSetAsyncNotification(pDrvIns, pfnAsyncNotify);
466}
467
468/**
469 * Callback employed by drvscsiSuspend and drvscsiPowerOff.
470 *
471 * @returns true if we've quiesced, false if we're still working.
472 * @param pDrvIns The driver instance.
473 */
474static DECLCALLBACK(bool) drvscsiIsAsyncSuspendOrPowerOffDone(PPDMDRVINS pDrvIns)
475{
476 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
477
478 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/))
479 return false;
480 ASMAtomicWriteBool(&pThis->fDummySignal, false);
481 PDMR3ThreadSuspend(pThis->pAsyncIOThread);
482 return true;
483}
484
485/**
486 * @copydoc FNPDMDRVPOWEROFF
487 */
488static DECLCALLBACK(void) drvscsiPowerOff(PPDMDRVINS pDrvIns)
489{
490 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncSuspendOrPowerOffDone);
491}
492
493/**
494 * @copydoc FNPDMDRVSUSPEND
495 */
496static DECLCALLBACK(void) drvscsiSuspend(PPDMDRVINS pDrvIns)
497{
498 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncSuspendOrPowerOffDone);
499}
500
501/**
502 * Callback employed by drvscsiReset.
503 *
504 * @returns true if we've quiesced, false if we're still working.
505 * @param pDrvIns The driver instance.
506 */
507static DECLCALLBACK(bool) drvscsiIsAsyncResetDone(PPDMDRVINS pDrvIns)
508{
509 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
510
511 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/))
512 return false;
513 ASMAtomicWriteBool(&pThis->fDummySignal, false);
514 return true;
515}
516
517/**
518 * @copydoc FNPDMDRVRESET
519 */
520static DECLCALLBACK(void) drvscsiReset(PPDMDRVINS pDrvIns)
521{
522 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncResetDone);
523}
524
525/**
526 * Destruct a driver instance.
527 *
528 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
529 * resources can be freed correctly.
530 *
531 * @param pDrvIns The driver instance data.
532 */
533static DECLCALLBACK(void) drvscsiDestruct(PPDMDRVINS pDrvIns)
534{
535 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
536 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
537
538 if (pThis->pQueueRequests)
539 {
540 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 100 /*ms*/))
541 LogRel(("drvscsiDestruct#%u: previous dummy request is still pending\n", pDrvIns->iInstance));
542
543 int rc = RTReqDestroyQueue(pThis->pQueueRequests);
544 AssertMsgRC(rc, ("Failed to destroy queue rc=%Rrc\n", rc));
545 }
546}
547
548/**
549 * Construct a block driver instance.
550 *
551 * @copydoc FNPDMDRVCONSTRUCT
552 */
553static DECLCALLBACK(int) drvscsiConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
554{
555 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
556 LogFlowFunc(("pDrvIns=%#p pCfg=%#p\n", pDrvIns, pCfg));
557 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
558
559 /*
560 * Initialize the instance data.
561 */
562 pThis->pDrvIns = pDrvIns;
563 pThis->ISCSIConnector.pfnSCSIRequestSend = drvscsiRequestSend;
564
565 pDrvIns->IBase.pfnQueryInterface = drvscsiQueryInterface;
566
567 pThis->IPortAsync.pfnTransferCompleteNotify = drvscsiTransferCompleteNotify;
568
569 /*
570 * Try attach driver below and query it's block interface.
571 */
572 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pThis->pDrvBase);
573 AssertMsgReturn(RT_SUCCESS(rc), ("Attaching driver below failed rc=%Rrc\n", rc), rc);
574
575 /*
576 * Query the block and blockbios interfaces.
577 */
578 pThis->pDrvBlock = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBLOCK);
579 if (!pThis->pDrvBlock)
580 {
581 AssertMsgFailed(("Configuration error: No block interface!\n"));
582 return VERR_PDM_MISSING_INTERFACE;
583 }
584 pThis->pDrvBlockBios = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBLOCKBIOS);
585 if (!pThis->pDrvBlockBios)
586 {
587 AssertMsgFailed(("Configuration error: No block BIOS interface!\n"));
588 return VERR_PDM_MISSING_INTERFACE;
589 }
590
591 /* Query the SCSI port interface above. */
592 pThis->pDevScsiPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMISCSIPORT);
593 AssertMsgReturn(pThis->pDevScsiPort, ("Missing SCSI port interface above\n"), VERR_PDM_MISSING_INTERFACE);
594
595 pThis->pDrvMount = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMOUNT);
596
597 /* Query the optional LED interface above. */
598 pThis->pLedPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
599 if (pThis->pLedPort != NULL)
600 {
601 /* Get The Led. */
602 rc = pThis->pLedPort->pfnQueryStatusLed(pThis->pLedPort, 0, &pThis->pLed);
603 if (RT_FAILURE(rc))
604 pThis->pLed = &pThis->Led;
605 }
606 else
607 pThis->pLed = &pThis->Led;
608
609 /* Try to get the optional async block interface. */
610 pThis->pDrvBlockAsync = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBLOCKASYNC);
611
612 PDMBLOCKTYPE enmType = pThis->pDrvBlock->pfnGetType(pThis->pDrvBlock);
613 if (enmType != PDMBLOCKTYPE_HARD_DISK)
614 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_UNSUPPORTED_BLOCK_TYPE, RT_SRC_POS,
615 N_("Only hard disks are currently supported as SCSI devices (enmType=%d)"),
616 enmType);
617
618 /* Create VSCSI device and LUN. */
619 pThis->VScsiIoCallbacks.pfnVScsiLunMediumGetSize = drvscsiGetSize;
620 pThis->VScsiIoCallbacks.pfnVScsiLunReqTransferEnqueue = drvscsiReqTransferEnqueue;
621
622 rc = VSCSIDeviceCreate(&pThis->hVScsiDevice, drvscsiVScsiReqCompleted, pThis);
623 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI device rc=%Rrc\n"), rc);
624 rc = VSCSILunCreate(&pThis->hVScsiLun, VSCSILUNTYPE_SBC, &pThis->VScsiIoCallbacks,
625 pThis);
626 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI LUN rc=%Rrc\n"), rc);
627 rc = VSCSIDeviceLunAttach(pThis->hVScsiDevice, pThis->hVScsiLun, 0);
628 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to attached the LUN to the SCSI device\n"), rc);
629
630 /* Create request queue. */
631 rc = RTReqCreateQueue(&pThis->pQueueRequests);
632 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create request queue rc=%Rrc\n"), rc);
633
634 /* Register statistics counter. */
635 /** @todo aeichner: Find a way to put the instance number of the attached
636 * controller device when we support more than one controller of the same type.
637 * At the moment we have the 0 hardcoded. */
638 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
639 "Amount of data read.", "/Devices/SCSI0/%d/ReadBytes", pDrvIns->iInstance);
640 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
641 "Amount of data written.", "/Devices/SCSI0/%d/WrittenBytes", pDrvIns->iInstance);
642
643 /* Create I/O thread. */
644 rc = PDMDrvHlpPDMThreadCreate(pDrvIns, &pThis->pAsyncIOThread, pThis, drvscsiAsyncIOLoop,
645 drvscsiAsyncIOLoopWakeup, 0, RTTHREADTYPE_IO, "SCSI async IO");
646 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create async I/O thread rc=%Rrc\n"), rc);
647
648 return VINF_SUCCESS;
649}
650
651/**
652 * SCSI driver registration record.
653 */
654const PDMDRVREG g_DrvSCSI =
655{
656 /* u32Version */
657 PDM_DRVREG_VERSION,
658 /* szName */
659 "SCSI",
660 /* szRCMod */
661 "",
662 /* szR0Mod */
663 "",
664 /* pszDescription */
665 "Generic SCSI driver.",
666 /* fFlags */
667 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
668 /* fClass. */
669 PDM_DRVREG_CLASS_SCSI,
670 /* cMaxInstances */
671 ~0,
672 /* cbInstance */
673 sizeof(DRVSCSI),
674 /* pfnConstruct */
675 drvscsiConstruct,
676 /* pfnDestruct */
677 drvscsiDestruct,
678 /* pfnRelocate */
679 NULL,
680 /* pfnIOCtl */
681 NULL,
682 /* pfnPowerOn */
683 NULL,
684 /* pfnReset */
685 drvscsiReset,
686 /* pfnSuspend */
687 drvscsiSuspend,
688 /* pfnResume */
689 NULL,
690 /* pfnAttach */
691 NULL,
692 /* pfnDetach */
693 NULL,
694 /* pfnPowerOff */
695 drvscsiPowerOff,
696 /* pfnSoftReset */
697 NULL,
698 /* u32EndVersion */
699 PDM_DRVREG_VERSION
700};
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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