VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/DrvHostBase.cpp@ 37608

Last change on this file since 37608 was 37601, checked in by vboxsync, 14 years ago

build fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 75.3 KB
Line 
1/* $Id: DrvHostBase.cpp 37601 2011-06-22 22:11:50Z vboxsync $ */
2/** @file
3 * DrvHostBase - Host base drive access driver.
4 */
5
6/*
7 * Copyright (C) 2006-2007 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 LOG_GROUP LOG_GROUP_DRV_HOST_BASE
23#ifdef RT_OS_DARWIN
24# include <mach/mach.h>
25# include <Carbon/Carbon.h>
26# include <IOKit/IOKitLib.h>
27# include <IOKit/storage/IOStorageDeviceCharacteristics.h>
28# include <IOKit/scsi/SCSITaskLib.h>
29# include <IOKit/scsi/SCSICommandOperationCodes.h>
30# include <IOKit/IOBSD.h>
31# include <DiskArbitration/DiskArbitration.h>
32# include <mach/mach_error.h>
33# include <VBox/scsi.h>
34
35#elif defined(RT_OS_L4)
36 /* Nothing special requires... yeah, right. */
37
38#elif defined(RT_OS_LINUX)
39# include <sys/ioctl.h>
40# include <sys/fcntl.h>
41# include <errno.h>
42
43#elif defined(RT_OS_SOLARIS)
44# include <fcntl.h>
45# include <errno.h>
46# include <stropts.h>
47# include <malloc.h>
48# include <sys/dkio.h>
49extern "C" char *getfullblkname(char *);
50
51#elif defined(RT_OS_WINDOWS)
52# define WIN32_NO_STATUS
53# include <Windows.h>
54# include <dbt.h>
55# undef WIN32_NO_STATUS
56# include <ntstatus.h>
57
58/* from ntdef.h */
59typedef LONG NTSTATUS;
60
61/* from ntddk.h */
62typedef struct _IO_STATUS_BLOCK {
63 union {
64 NTSTATUS Status;
65 PVOID Pointer;
66 };
67 ULONG_PTR Information;
68} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
69
70
71/* from ntinternals.com */
72typedef enum _FS_INFORMATION_CLASS {
73 FileFsVolumeInformation=1,
74 FileFsLabelInformation,
75 FileFsSizeInformation,
76 FileFsDeviceInformation,
77 FileFsAttributeInformation,
78 FileFsControlInformation,
79 FileFsFullSizeInformation,
80 FileFsObjectIdInformation,
81 FileFsMaximumInformation
82} FS_INFORMATION_CLASS, *PFS_INFORMATION_CLASS;
83
84typedef struct _FILE_FS_SIZE_INFORMATION {
85 LARGE_INTEGER TotalAllocationUnits;
86 LARGE_INTEGER AvailableAllocationUnits;
87 ULONG SectorsPerAllocationUnit;
88 ULONG BytesPerSector;
89} FILE_FS_SIZE_INFORMATION, *PFILE_FS_SIZE_INFORMATION;
90
91extern "C"
92NTSTATUS __stdcall NtQueryVolumeInformationFile(
93 /*IN*/ HANDLE FileHandle,
94 /*OUT*/ PIO_STATUS_BLOCK IoStatusBlock,
95 /*OUT*/ PVOID FileSystemInformation,
96 /*IN*/ ULONG Length,
97 /*IN*/ FS_INFORMATION_CLASS FileSystemInformationClass );
98
99#elif defined(RT_OS_FREEBSD)
100# include <sys/cdefs.h>
101# include <sys/param.h>
102# include <errno.h>
103# include <stdio.h>
104# include <cam/cam.h>
105# include <cam/cam_ccb.h>
106# include <cam/scsi/scsi_message.h>
107# include <cam/scsi/scsi_pass.h>
108# include <VBox/scsi.h>
109# include <iprt/log.h>
110#else
111# error "Unsupported Platform."
112#endif
113
114#include <VBox/vmm/pdmdrv.h>
115#include <iprt/assert.h>
116#include <iprt/file.h>
117#include <iprt/path.h>
118#include <iprt/string.h>
119#include <iprt/thread.h>
120#include <iprt/semaphore.h>
121#include <iprt/uuid.h>
122#include <iprt/asm.h>
123#include <iprt/critsect.h>
124#include <iprt/ctype.h>
125
126#include "DrvHostBase.h"
127
128
129
130
131/* -=-=-=-=- IBlock -=-=-=-=- */
132
133/** @copydoc PDMIBLOCK::pfnRead */
134static DECLCALLBACK(int) drvHostBaseRead(PPDMIBLOCK pInterface, uint64_t off, void *pvBuf, size_t cbRead)
135{
136 PDRVHOSTBASE pThis = PDMIBLOCK_2_DRVHOSTBASE(pInterface);
137 LogFlow(("%s-%d: drvHostBaseRead: off=%#llx pvBuf=%p cbRead=%#x (%s)\n",
138 pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, off, pvBuf, cbRead, pThis->pszDevice));
139 RTCritSectEnter(&pThis->CritSect);
140
141 /*
142 * Check the state.
143 */
144 int rc;
145#ifdef RT_OS_DARWIN
146 if ( pThis->fMediaPresent
147 && pThis->ppScsiTaskDI
148 && pThis->cbBlock)
149#elif RT_OS_FREEBSD
150 if ( pThis->fMediaPresent
151 && pThis->cbBlock)
152#else
153 if (pThis->fMediaPresent)
154#endif
155 {
156#if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)
157 /*
158 * Issue a READ(12) request.
159 */
160 do
161 {
162 const uint32_t LBA = off / pThis->cbBlock;
163 AssertReturn(!(off % pThis->cbBlock), VERR_INVALID_PARAMETER);
164 uint32_t cbRead32 = cbRead > SCSI_MAX_BUFFER_SIZE
165 ? SCSI_MAX_BUFFER_SIZE
166 : (uint32_t)cbRead;
167 const uint32_t cBlocks = cbRead32 / pThis->cbBlock;
168 AssertReturn(!(cbRead % pThis->cbBlock), VERR_INVALID_PARAMETER);
169 uint8_t abCmd[16] =
170 {
171 SCSI_READ_12, 0,
172 RT_BYTE4(LBA), RT_BYTE3(LBA), RT_BYTE2(LBA), RT_BYTE1(LBA),
173 RT_BYTE4(cBlocks), RT_BYTE3(cBlocks), RT_BYTE2(cBlocks), RT_BYTE1(cBlocks),
174 0, 0, 0, 0, 0
175 };
176 rc = DRVHostBaseScsiCmd(pThis, abCmd, 12, PDMBLOCKTXDIR_FROM_DEVICE, pvBuf, &cbRead32, NULL, 0, 0);
177
178 off += cbRead32;
179 cbRead -= cbRead32;
180 pvBuf = (uint8_t *)pvBuf + cbRead32;
181 } while ((cbRead > 0) && RT_SUCCESS(rc));
182
183#else
184 /*
185 * Seek and read.
186 */
187 rc = RTFileReadAt(pThis->hFileDevice, off, pvBuf, cbRead, NULL);
188 if (RT_SUCCESS(rc))
189 {
190 Log2(("%s-%d: drvHostBaseRead: off=%#llx cbRead=%#x\n"
191 "%16.*Rhxd\n",
192 pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, off, cbRead, cbRead, pvBuf));
193 }
194 else
195 Log(("%s-%d: drvHostBaseRead: RTFileReadAt(%RTfile, %#llx, %p, %#x) -> %Rrc ('%s')\n",
196 pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, pThis->hFileDevice,
197 off, pvBuf, cbRead, rc, pThis->pszDevice));
198#endif
199 }
200 else
201 rc = VERR_MEDIA_NOT_PRESENT;
202
203 RTCritSectLeave(&pThis->CritSect);
204 LogFlow(("%s-%d: drvHostBaseRead: returns %Rrc\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, rc));
205 return rc;
206}
207
208
209/** @copydoc PDMIBLOCK::pfnWrite */
210static DECLCALLBACK(int) drvHostBaseWrite(PPDMIBLOCK pInterface, uint64_t off, const void *pvBuf, size_t cbWrite)
211{
212 PDRVHOSTBASE pThis = PDMIBLOCK_2_DRVHOSTBASE(pInterface);
213 LogFlow(("%s-%d: drvHostBaseWrite: off=%#llx pvBuf=%p cbWrite=%#x (%s)\n",
214 pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, off, pvBuf, cbWrite, pThis->pszDevice));
215 Log2(("%s-%d: drvHostBaseWrite: off=%#llx cbWrite=%#x\n"
216 "%16.*Rhxd\n",
217 pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, off, cbWrite, cbWrite, pvBuf));
218 RTCritSectEnter(&pThis->CritSect);
219
220 /*
221 * Check the state.
222 */
223 int rc;
224 if (!pThis->fReadOnly)
225 {
226 if (pThis->fMediaPresent)
227 {
228#if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)
229 /** @todo write support... */
230 rc = VERR_WRITE_PROTECT;
231
232#else
233 /*
234 * Seek and write.
235 */
236 rc = RTFileWriteAt(pThis->hFileDevice, off, pvBuf, cbWrite, NULL);
237 if (RT_FAILURE(rc))
238 Log(("%s-%d: drvHostBaseWrite: RTFileWriteAt(%RTfile, %#llx, %p, %#x) -> %Rrc ('%s')\n",
239 pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, pThis->hFileDevice,
240 off, pvBuf, cbWrite, rc, pThis->pszDevice));
241#endif
242 }
243 else
244 rc = VERR_MEDIA_NOT_PRESENT;
245 }
246 else
247 rc = VERR_WRITE_PROTECT;
248
249 RTCritSectLeave(&pThis->CritSect);
250 LogFlow(("%s-%d: drvHostBaseWrite: returns %Rrc\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, rc));
251 return rc;
252}
253
254
255/** @copydoc PDMIBLOCK::pfnFlush */
256static DECLCALLBACK(int) drvHostBaseFlush(PPDMIBLOCK pInterface)
257{
258 int rc;
259 PDRVHOSTBASE pThis = PDMIBLOCK_2_DRVHOSTBASE(pInterface);
260 LogFlow(("%s-%d: drvHostBaseFlush: (%s)\n",
261 pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, pThis->pszDevice));
262 RTCritSectEnter(&pThis->CritSect);
263
264 if (pThis->fMediaPresent)
265 {
266#if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)
267 rc = VINF_SUCCESS;
268 /** @todo scsi device buffer flush... */
269#else
270 rc = RTFileFlush(pThis->hFileDevice);
271#endif
272 }
273 else
274 rc = VERR_MEDIA_NOT_PRESENT;
275
276 RTCritSectLeave(&pThis->CritSect);
277 LogFlow(("%s-%d: drvHostBaseFlush: returns %Rrc\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, rc));
278 return rc;
279}
280
281
282/** @copydoc PDMIBLOCK::pfnIsReadOnly */
283static DECLCALLBACK(bool) drvHostBaseIsReadOnly(PPDMIBLOCK pInterface)
284{
285 PDRVHOSTBASE pThis = PDMIBLOCK_2_DRVHOSTBASE(pInterface);
286 return pThis->fReadOnly;
287}
288
289
290/** @copydoc PDMIBLOCK::pfnGetSize */
291static DECLCALLBACK(uint64_t) drvHostBaseGetSize(PPDMIBLOCK pInterface)
292{
293 PDRVHOSTBASE pThis = PDMIBLOCK_2_DRVHOSTBASE(pInterface);
294 RTCritSectEnter(&pThis->CritSect);
295
296 uint64_t cb = 0;
297 if (pThis->fMediaPresent)
298 cb = pThis->cbSize;
299
300 RTCritSectLeave(&pThis->CritSect);
301 LogFlow(("%s-%d: drvHostBaseGetSize: returns %llu\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, cb));
302 return cb;
303}
304
305
306/** @copydoc PDMIBLOCK::pfnGetType */
307static DECLCALLBACK(PDMBLOCKTYPE) drvHostBaseGetType(PPDMIBLOCK pInterface)
308{
309 PDRVHOSTBASE pThis = PDMIBLOCK_2_DRVHOSTBASE(pInterface);
310 LogFlow(("%s-%d: drvHostBaseGetType: returns %d\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, pThis->enmType));
311 return pThis->enmType;
312}
313
314
315/** @copydoc PDMIBLOCK::pfnGetUuid */
316static DECLCALLBACK(int) drvHostBaseGetUuid(PPDMIBLOCK pInterface, PRTUUID pUuid)
317{
318 PDRVHOSTBASE pThis = PDMIBLOCK_2_DRVHOSTBASE(pInterface);
319
320 *pUuid = pThis->Uuid;
321
322 LogFlow(("%s-%d: drvHostBaseGetUuid: returns VINF_SUCCESS *pUuid=%RTuuid\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, pUuid));
323 return VINF_SUCCESS;
324}
325
326
327/* -=-=-=-=- IBlockBios -=-=-=-=- */
328
329/** Makes a PDRVHOSTBASE out of a PPDMIBLOCKBIOS. */
330#define PDMIBLOCKBIOS_2_DRVHOSTBASE(pInterface) ( (PDRVHOSTBASE((uintptr_t)pInterface - RT_OFFSETOF(DRVHOSTBASE, IBlockBios))) )
331
332
333/** @copydoc PDMIBLOCKBIOS::pfnGetPCHSGeometry */
334static DECLCALLBACK(int) drvHostBaseGetPCHSGeometry(PPDMIBLOCKBIOS pInterface, PPDMMEDIAGEOMETRY pPCHSGeometry)
335{
336 PDRVHOSTBASE pThis = PDMIBLOCKBIOS_2_DRVHOSTBASE(pInterface);
337 RTCritSectEnter(&pThis->CritSect);
338
339 int rc = VINF_SUCCESS;
340 if (pThis->fMediaPresent)
341 {
342 if ( pThis->PCHSGeometry.cCylinders > 0
343 && pThis->PCHSGeometry.cHeads > 0
344 && pThis->PCHSGeometry.cSectors > 0)
345 {
346 *pPCHSGeometry = pThis->PCHSGeometry;
347 }
348 else
349 rc = VERR_PDM_GEOMETRY_NOT_SET;
350 }
351 else
352 rc = VERR_PDM_MEDIA_NOT_MOUNTED;
353
354 RTCritSectLeave(&pThis->CritSect);
355 LogFlow(("%s-%d: %s: returns %Rrc CHS={%d,%d,%d}\n",
356 pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, __FUNCTION__, rc, pThis->PCHSGeometry.cCylinders, pThis->PCHSGeometry.cHeads, pThis->PCHSGeometry.cSectors));
357 return rc;
358}
359
360
361/** @copydoc PDMIBLOCKBIOS::pfnSetPCHSGeometry */
362static DECLCALLBACK(int) drvHostBaseSetPCHSGeometry(PPDMIBLOCKBIOS pInterface, PCPDMMEDIAGEOMETRY pPCHSGeometry)
363{
364 PDRVHOSTBASE pThis = PDMIBLOCKBIOS_2_DRVHOSTBASE(pInterface);
365 LogFlow(("%s-%d: %s: cCylinders=%d cHeads=%d cSectors=%d\n",
366 pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, __FUNCTION__, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
367 RTCritSectEnter(&pThis->CritSect);
368
369 int rc = VINF_SUCCESS;
370 if (pThis->fMediaPresent)
371 {
372 pThis->PCHSGeometry = *pPCHSGeometry;
373 }
374 else
375 {
376 AssertMsgFailed(("Invalid state! Not mounted!\n"));
377 rc = VERR_PDM_MEDIA_NOT_MOUNTED;
378 }
379
380 RTCritSectLeave(&pThis->CritSect);
381 return rc;
382}
383
384
385/** @copydoc PDMIBLOCKBIOS::pfnGetLCHSGeometry */
386static DECLCALLBACK(int) drvHostBaseGetLCHSGeometry(PPDMIBLOCKBIOS pInterface, PPDMMEDIAGEOMETRY pLCHSGeometry)
387{
388 PDRVHOSTBASE pThis = PDMIBLOCKBIOS_2_DRVHOSTBASE(pInterface);
389 RTCritSectEnter(&pThis->CritSect);
390
391 int rc = VINF_SUCCESS;
392 if (pThis->fMediaPresent)
393 {
394 if ( pThis->LCHSGeometry.cCylinders > 0
395 && pThis->LCHSGeometry.cHeads > 0
396 && pThis->LCHSGeometry.cSectors > 0)
397 {
398 *pLCHSGeometry = pThis->LCHSGeometry;
399 }
400 else
401 rc = VERR_PDM_GEOMETRY_NOT_SET;
402 }
403 else
404 rc = VERR_PDM_MEDIA_NOT_MOUNTED;
405
406 RTCritSectLeave(&pThis->CritSect);
407 LogFlow(("%s-%d: %s: returns %Rrc CHS={%d,%d,%d}\n",
408 pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, __FUNCTION__, rc, pThis->LCHSGeometry.cCylinders, pThis->LCHSGeometry.cHeads, pThis->LCHSGeometry.cSectors));
409 return rc;
410}
411
412
413/** @copydoc PDMIBLOCKBIOS::pfnSetLCHSGeometry */
414static DECLCALLBACK(int) drvHostBaseSetLCHSGeometry(PPDMIBLOCKBIOS pInterface, PCPDMMEDIAGEOMETRY pLCHSGeometry)
415{
416 PDRVHOSTBASE pThis = PDMIBLOCKBIOS_2_DRVHOSTBASE(pInterface);
417 LogFlow(("%s-%d: %s: cCylinders=%d cHeads=%d cSectors=%d\n",
418 pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, __FUNCTION__, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
419 RTCritSectEnter(&pThis->CritSect);
420
421 int rc = VINF_SUCCESS;
422 if (pThis->fMediaPresent)
423 {
424 pThis->LCHSGeometry = *pLCHSGeometry;
425 }
426 else
427 {
428 AssertMsgFailed(("Invalid state! Not mounted!\n"));
429 rc = VERR_PDM_MEDIA_NOT_MOUNTED;
430 }
431
432 RTCritSectLeave(&pThis->CritSect);
433 return rc;
434}
435
436
437/** @copydoc PDMIBLOCKBIOS::pfnIsVisible */
438static DECLCALLBACK(bool) drvHostBaseIsVisible(PPDMIBLOCKBIOS pInterface)
439{
440 PDRVHOSTBASE pThis = PDMIBLOCKBIOS_2_DRVHOSTBASE(pInterface);
441 return pThis->fBiosVisible;
442}
443
444
445/** @copydoc PDMIBLOCKBIOS::pfnGetType */
446static DECLCALLBACK(PDMBLOCKTYPE) drvHostBaseBiosGetType(PPDMIBLOCKBIOS pInterface)
447{
448 PDRVHOSTBASE pThis = PDMIBLOCKBIOS_2_DRVHOSTBASE(pInterface);
449 return pThis->enmType;
450}
451
452
453
454/* -=-=-=-=- IMount -=-=-=-=- */
455
456/** @copydoc PDMIMOUNT::pfnMount */
457static DECLCALLBACK(int) drvHostBaseMount(PPDMIMOUNT pInterface, const char *pszFilename, const char *pszCoreDriver)
458{
459 /* We're not mountable. */
460 AssertMsgFailed(("drvHostBaseMount: This shouldn't be called!\n"));
461 return VERR_PDM_MEDIA_MOUNTED;
462}
463
464
465/** @copydoc PDMIMOUNT::pfnUnmount */
466static DECLCALLBACK(int) drvHostBaseUnmount(PPDMIMOUNT pInterface, bool fForce, bool fEject)
467{
468 LogFlow(("drvHostBaseUnmount: returns VERR_NOT_SUPPORTED\n"));
469 return VERR_NOT_SUPPORTED;
470}
471
472
473/** @copydoc PDMIMOUNT::pfnIsMounted */
474static DECLCALLBACK(bool) drvHostBaseIsMounted(PPDMIMOUNT pInterface)
475{
476 PDRVHOSTBASE pThis = PDMIMOUNT_2_DRVHOSTBASE(pInterface);
477 RTCritSectEnter(&pThis->CritSect);
478
479 bool fRc = pThis->fMediaPresent;
480
481 RTCritSectLeave(&pThis->CritSect);
482 return fRc;
483}
484
485
486/** @copydoc PDMIMOUNT::pfnIsLocked */
487static DECLCALLBACK(int) drvHostBaseLock(PPDMIMOUNT pInterface)
488{
489 PDRVHOSTBASE pThis = PDMIMOUNT_2_DRVHOSTBASE(pInterface);
490 RTCritSectEnter(&pThis->CritSect);
491
492 int rc = VINF_SUCCESS;
493 if (!pThis->fLocked)
494 {
495 if (pThis->pfnDoLock)
496 rc = pThis->pfnDoLock(pThis, true);
497 if (RT_SUCCESS(rc))
498 pThis->fLocked = true;
499 }
500 else
501 LogFlow(("%s-%d: drvHostBaseLock: already locked\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance));
502
503 RTCritSectLeave(&pThis->CritSect);
504 LogFlow(("%s-%d: drvHostBaseLock: returns %Rrc\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, rc));
505 return rc;
506}
507
508
509/** @copydoc PDMIMOUNT::pfnIsLocked */
510static DECLCALLBACK(int) drvHostBaseUnlock(PPDMIMOUNT pInterface)
511{
512 PDRVHOSTBASE pThis = PDMIMOUNT_2_DRVHOSTBASE(pInterface);
513 RTCritSectEnter(&pThis->CritSect);
514
515 int rc = VINF_SUCCESS;
516 if (pThis->fLocked)
517 {
518 if (pThis->pfnDoLock)
519 rc = pThis->pfnDoLock(pThis, false);
520 if (RT_SUCCESS(rc))
521 pThis->fLocked = false;
522 }
523 else
524 LogFlow(("%s-%d: drvHostBaseUnlock: not locked\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance));
525
526 RTCritSectLeave(&pThis->CritSect);
527 LogFlow(("%s-%d: drvHostBaseUnlock: returns %Rrc\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, rc));
528 return rc;
529}
530
531
532/** @copydoc PDMIMOUNT::pfnIsLocked */
533static DECLCALLBACK(bool) drvHostBaseIsLocked(PPDMIMOUNT pInterface)
534{
535 PDRVHOSTBASE pThis = PDMIMOUNT_2_DRVHOSTBASE(pInterface);
536 RTCritSectEnter(&pThis->CritSect);
537
538 bool fRc = pThis->fLocked;
539
540 RTCritSectLeave(&pThis->CritSect);
541 return fRc;
542}
543
544
545/* -=-=-=-=- IBase -=-=-=-=- */
546
547/**
548 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
549 */
550static DECLCALLBACK(void *) drvHostBaseQueryInterface(PPDMIBASE pInterface, const char *pszIID)
551{
552 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
553 PDRVHOSTBASE pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTBASE);
554
555 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
556 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCK, &pThis->IBlock);
557 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCKBIOS, pThis->fBiosVisible ? &pThis->IBlockBios : NULL);
558 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNT, &pThis->IMount);
559 return NULL;
560}
561
562
563/* -=-=-=-=- poller thread -=-=-=-=- */
564
565#ifdef RT_OS_DARWIN
566/** The runloop input source name for the disk arbitration events. */
567# define MY_RUN_LOOP_MODE CFSTR("drvHostBaseDA") /** @todo r=bird: Check if this will cause trouble in the same way that the one in the USB code did. */
568
569/**
570 * Gets the BSD Name (/dev/disc[0-9]+) for the service.
571 *
572 * This is done by recursing down the I/O registry until we hit upon an entry
573 * with a BSD Name. Usually we find it two levels down. (Further down under
574 * the IOCDPartitionScheme, the volume (slices) BSD Name is found. We don't
575 * seem to have to go this far fortunately.)
576 *
577 * @return VINF_SUCCESS if found, VERR_FILE_NOT_FOUND otherwise.
578 * @param Entry The current I/O registry entry reference.
579 * @param pszName Where to store the name. 128 bytes.
580 * @param cRecursions Number of recursions. This is used as an precaution
581 * just to limit the depth and avoid blowing the stack
582 * should we hit a bug or something.
583 */
584static int drvHostBaseGetBSDName(io_registry_entry_t Entry, char *pszName, unsigned cRecursions)
585{
586 int rc = VERR_FILE_NOT_FOUND;
587 io_iterator_t Children = 0;
588 kern_return_t krc = IORegistryEntryGetChildIterator(Entry, kIOServicePlane, &Children);
589 if (krc == KERN_SUCCESS)
590 {
591 io_object_t Child;
592 while ( rc == VERR_FILE_NOT_FOUND
593 && (Child = IOIteratorNext(Children)) != 0)
594 {
595 CFStringRef BSDNameStrRef = (CFStringRef)IORegistryEntryCreateCFProperty(Child, CFSTR(kIOBSDNameKey), kCFAllocatorDefault, 0);
596 if (BSDNameStrRef)
597 {
598 if (CFStringGetCString(BSDNameStrRef, pszName, 128, kCFStringEncodingUTF8))
599 rc = VINF_SUCCESS;
600 else
601 AssertFailed();
602 CFRelease(BSDNameStrRef);
603 }
604 if (rc == VERR_FILE_NOT_FOUND && cRecursions < 10)
605 rc = drvHostBaseGetBSDName(Child, pszName, cRecursions + 1);
606 IOObjectRelease(Child);
607 }
608 IOObjectRelease(Children);
609 }
610 return rc;
611}
612
613
614/**
615 * Callback notifying us that the async DADiskClaim()/DADiskUnmount call has completed.
616 *
617 * @param DiskRef The disk that was attempted claimed / unmounted.
618 * @param DissenterRef NULL on success, contains details on failure.
619 * @param pvContext Pointer to the return code variable.
620 */
621static void drvHostBaseDADoneCallback(DADiskRef DiskRef, DADissenterRef DissenterRef, void *pvContext)
622{
623 int *prc = (int *)pvContext;
624 if (!DissenterRef)
625 *prc = 0;
626 else
627 *prc = DADissenterGetStatus(DissenterRef) ? DADissenterGetStatus(DissenterRef) : -1;
628 CFRunLoopStop(CFRunLoopGetCurrent());
629}
630
631
632/**
633 * Obtain exclusive access to the DVD device, umount it if necessary.
634 *
635 * @return VBox status code.
636 * @param pThis The driver instance.
637 * @param DVDService The DVD service object.
638 */
639static int drvHostBaseObtainExclusiveAccess(PDRVHOSTBASE pThis, io_object_t DVDService)
640{
641 PPDMDRVINS pDrvIns = pThis->pDrvIns; NOREF(pDrvIns);
642
643 for (unsigned iTry = 0;; iTry++)
644 {
645 IOReturn irc = (*pThis->ppScsiTaskDI)->ObtainExclusiveAccess(pThis->ppScsiTaskDI);
646 if (irc == kIOReturnSuccess)
647 {
648 /*
649 * This is a bit weird, but if we unmounted the DVD drive we also need to
650 * unlock it afterwards or the guest won't be able to eject it later on.
651 */
652 if (pThis->pDADisk)
653 {
654 uint8_t abCmd[16] =
655 {
656 SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL, 0, 0, 0, false, 0,
657 0,0,0,0,0,0,0,0,0,0
658 };
659 DRVHostBaseScsiCmd(pThis, abCmd, 6, PDMBLOCKTXDIR_NONE, NULL, NULL, NULL, 0, 0);
660 }
661 return VINF_SUCCESS;
662 }
663 if (irc == kIOReturnExclusiveAccess)
664 return VERR_SHARING_VIOLATION; /* already used exclusivly. */
665 if (irc != kIOReturnBusy)
666 return VERR_GENERAL_FAILURE; /* not mounted */
667
668 /*
669 * Attempt to the unmount all volumes of the device.
670 * It seems we can can do this all in one go without having to enumerate the
671 * volumes (sessions) and deal with them one by one. This is very fortuitous
672 * as the disk arbitration API is a bit cumbersome to deal with.
673 */
674 if (iTry > 2)
675 return VERR_DRIVE_LOCKED;
676 char szName[128];
677 int rc = drvHostBaseGetBSDName(DVDService, &szName[0], 0);
678 if (RT_SUCCESS(rc))
679 {
680 pThis->pDASession = DASessionCreate(kCFAllocatorDefault);
681 if (pThis->pDASession)
682 {
683 DASessionScheduleWithRunLoop(pThis->pDASession, CFRunLoopGetCurrent(), MY_RUN_LOOP_MODE);
684 pThis->pDADisk = DADiskCreateFromBSDName(kCFAllocatorDefault, pThis->pDASession, szName);
685 if (pThis->pDADisk)
686 {
687 /*
688 * Try claim the device.
689 */
690 Log(("%s-%d: calling DADiskClaim on '%s'.\n", pDrvIns->pReg->szName, pDrvIns->iInstance, szName));
691 int rcDA = -2;
692 DADiskClaim(pThis->pDADisk, kDADiskClaimOptionDefault, NULL, NULL, drvHostBaseDADoneCallback, &rcDA);
693 SInt32 rc32 = CFRunLoopRunInMode(MY_RUN_LOOP_MODE, 120.0, FALSE);
694 AssertMsg(rc32 == kCFRunLoopRunStopped, ("rc32=%RI32 (%RX32)\n", rc32, rc32));
695 if ( rc32 == kCFRunLoopRunStopped
696 && !rcDA)
697 {
698 /*
699 * Try unmount the device.
700 */
701 Log(("%s-%d: calling DADiskUnmount on '%s'.\n", pDrvIns->pReg->szName, pDrvIns->iInstance, szName));
702 rcDA = -2;
703 DADiskUnmount(pThis->pDADisk, kDADiskUnmountOptionWhole, drvHostBaseDADoneCallback, &rcDA);
704 rc32 = CFRunLoopRunInMode(MY_RUN_LOOP_MODE, 120.0, FALSE);
705 AssertMsg(rc32 == kCFRunLoopRunStopped, ("rc32=%RI32 (%RX32)\n", rc32, rc32));
706 if ( rc32 == kCFRunLoopRunStopped
707 && !rcDA)
708 {
709 iTry = 99;
710 DASessionUnscheduleFromRunLoop(pThis->pDASession, CFRunLoopGetCurrent(), MY_RUN_LOOP_MODE);
711 Log(("%s-%d: unmount succeed - retrying.\n", pDrvIns->pReg->szName, pDrvIns->iInstance));
712 continue;
713 }
714 Log(("%s-%d: umount => rc32=%d & rcDA=%#x\n", pDrvIns->pReg->szName, pDrvIns->iInstance, rc32, rcDA));
715
716 /* failed - cleanup */
717 DADiskUnclaim(pThis->pDADisk);
718 }
719 else
720 Log(("%s-%d: claim => rc32=%d & rcDA=%#x\n", pDrvIns->pReg->szName, pDrvIns->iInstance, rc32, rcDA));
721
722 CFRelease(pThis->pDADisk);
723 pThis->pDADisk = NULL;
724 }
725 else
726 Log(("%s-%d: failed to open disk '%s'!\n", pDrvIns->pReg->szName, pDrvIns->iInstance, szName));
727
728 DASessionUnscheduleFromRunLoop(pThis->pDASession, CFRunLoopGetCurrent(), MY_RUN_LOOP_MODE);
729 CFRelease(pThis->pDASession);
730 pThis->pDASession = NULL;
731 }
732 else
733 Log(("%s-%d: failed to create DA session!\n", pDrvIns->pReg->szName, pDrvIns->iInstance));
734 }
735 RTThreadSleep(10);
736 }
737}
738#endif /* RT_OS_DARWIN */
739
740
741#ifndef RT_OS_SOLARIS
742/**
743 * Wrapper for open / RTFileOpen / IOKit.
744 *
745 * @remark The Darwin code must correspond exactly to the enumeration
746 * done in Main/darwin/iokit.c.
747 */
748static int drvHostBaseOpen(PDRVHOSTBASE pThis, PRTFILE pFileDevice, bool fReadOnly)
749{
750# ifdef RT_OS_DARWIN
751 /* Darwin is kind of special... */
752 Assert(!pFileDevice); NOREF(pFileDevice);
753 Assert(!pThis->cbBlock);
754 Assert(!pThis->MasterPort);
755 Assert(!pThis->ppMMCDI);
756 Assert(!pThis->ppScsiTaskDI);
757
758 /*
759 * Open the master port on the first invocation.
760 */
761 kern_return_t krc = IOMasterPort(MACH_PORT_NULL, &pThis->MasterPort);
762 AssertReturn(krc == KERN_SUCCESS, VERR_GENERAL_FAILURE);
763
764 /*
765 * Create a matching dictionary for searching for DVD services in the IOKit.
766 *
767 * [If I understand this correctly, plain CDROMs doesn't show up as
768 * IODVDServices. Too keep things simple, we will only support DVDs
769 * until somebody complains about it and we get hardware to test it on.
770 * (Unless I'm much mistaken, there aren't any (orignal) intel macs with
771 * plain cdroms.)]
772 */
773 CFMutableDictionaryRef RefMatchingDict = IOServiceMatching("IODVDServices");
774 AssertReturn(RefMatchingDict, NULL);
775
776 /*
777 * do the search and get a collection of keyboards.
778 */
779 io_iterator_t DVDServices = NULL;
780 IOReturn irc = IOServiceGetMatchingServices(pThis->MasterPort, RefMatchingDict, &DVDServices);
781 AssertMsgReturn(irc == kIOReturnSuccess, ("irc=%d\n", irc), NULL);
782 RefMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
783
784 /*
785 * Enumerate the DVD drives (services).
786 * (This enumeration must be identical to the one performed in DrvHostBase.cpp.)
787 */
788 int rc = VERR_FILE_NOT_FOUND;
789 unsigned i = 0;
790 io_object_t DVDService;
791 while ((DVDService = IOIteratorNext(DVDServices)) != 0)
792 {
793 /*
794 * Get the properties we use to identify the DVD drive.
795 *
796 * While there is a (weird 12 byte) GUID, it isn't persistent
797 * across boots. So, we have to use a combination of the
798 * vendor name and product name properties with an optional
799 * sequence number for identification.
800 */
801 CFMutableDictionaryRef PropsRef = 0;
802 krc = IORegistryEntryCreateCFProperties(DVDService, &PropsRef, kCFAllocatorDefault, kNilOptions);
803 if (krc == KERN_SUCCESS)
804 {
805 /* Get the Device Characteristics dictionary. */
806 CFDictionaryRef DevCharRef = (CFDictionaryRef)CFDictionaryGetValue(PropsRef, CFSTR(kIOPropertyDeviceCharacteristicsKey));
807 if (DevCharRef)
808 {
809 /* The vendor name. */
810 char szVendor[128];
811 char *pszVendor = &szVendor[0];
812 CFTypeRef ValueRef = CFDictionaryGetValue(DevCharRef, CFSTR(kIOPropertyVendorNameKey));
813 if ( ValueRef
814 && CFGetTypeID(ValueRef) == CFStringGetTypeID()
815 && CFStringGetCString((CFStringRef)ValueRef, szVendor, sizeof(szVendor), kCFStringEncodingUTF8))
816 pszVendor = RTStrStrip(szVendor);
817 else
818 *pszVendor = '\0';
819
820 /* The product name. */
821 char szProduct[128];
822 char *pszProduct = &szProduct[0];
823 ValueRef = CFDictionaryGetValue(DevCharRef, CFSTR(kIOPropertyProductNameKey));
824 if ( ValueRef
825 && CFGetTypeID(ValueRef) == CFStringGetTypeID()
826 && CFStringGetCString((CFStringRef)ValueRef, szProduct, sizeof(szProduct), kCFStringEncodingUTF8))
827 pszProduct = RTStrStrip(szProduct);
828 else
829 *pszProduct = '\0';
830
831 /* Construct the two names and compare thwm with the one we're searching for. */
832 char szName1[256 + 32];
833 char szName2[256 + 32];
834 if (*pszVendor || *pszProduct)
835 {
836 if (*pszVendor && *pszProduct)
837 {
838 RTStrPrintf(szName1, sizeof(szName1), "%s %s", pszVendor, pszProduct);
839 RTStrPrintf(szName2, sizeof(szName2), "%s %s (#%u)", pszVendor, pszProduct, i);
840 }
841 else
842 {
843 strcpy(szName1, *pszVendor ? pszVendor : pszProduct);
844 RTStrPrintf(szName2, sizeof(szName2), "%s %s (#%u)", *pszVendor ? pszVendor : pszProduct, i);
845 }
846 }
847 else
848 {
849 RTStrPrintf(szName1, sizeof(szName1), "(#%u)", i);
850 strcpy(szName2, szName1);
851 }
852
853 if ( !strcmp(szName1, pThis->pszDeviceOpen)
854 || !strcmp(szName2, pThis->pszDeviceOpen))
855 {
856 /*
857 * Found it! Now, get the client interface and stuff.
858 * Note that we could also query kIOSCSITaskDeviceUserClientTypeID here if the
859 * MMC client plugin is missing. For now we assume this won't be necessary.
860 */
861 SInt32 Score = 0;
862 IOCFPlugInInterface **ppPlugInInterface = NULL;
863 krc = IOCreatePlugInInterfaceForService(DVDService, kIOMMCDeviceUserClientTypeID, kIOCFPlugInInterfaceID,
864 &ppPlugInInterface, &Score);
865 if (krc == KERN_SUCCESS)
866 {
867 HRESULT hrc = (*ppPlugInInterface)->QueryInterface(ppPlugInInterface,
868 CFUUIDGetUUIDBytes(kIOMMCDeviceInterfaceID),
869 (LPVOID *)&pThis->ppMMCDI);
870 (*ppPlugInInterface)->Release(ppPlugInInterface);
871 ppPlugInInterface = NULL;
872 if (hrc == S_OK)
873 {
874 pThis->ppScsiTaskDI = (*pThis->ppMMCDI)->GetSCSITaskDeviceInterface(pThis->ppMMCDI);
875 if (pThis->ppScsiTaskDI)
876 rc = VINF_SUCCESS;
877 else
878 {
879 LogRel(("GetSCSITaskDeviceInterface failed on '%s'\n", pThis->pszDeviceOpen));
880 rc = VERR_NOT_SUPPORTED;
881 (*pThis->ppMMCDI)->Release(pThis->ppMMCDI);
882 }
883 }
884 else
885 {
886 rc = VERR_GENERAL_FAILURE;//RTErrConvertFromDarwinCOM(krc);
887 pThis->ppMMCDI = NULL;
888 }
889 }
890 else /* Check for kIOSCSITaskDeviceUserClientTypeID? */
891 rc = VERR_GENERAL_FAILURE;//RTErrConvertFromDarwinKern(krc);
892
893 /* Obtain exclusive access to the device so we can send SCSI commands. */
894 if (RT_SUCCESS(rc))
895 rc = drvHostBaseObtainExclusiveAccess(pThis, DVDService);
896
897 /* Cleanup on failure. */
898 if (RT_FAILURE(rc))
899 {
900 if (pThis->ppScsiTaskDI)
901 {
902 (*pThis->ppScsiTaskDI)->Release(pThis->ppScsiTaskDI);
903 pThis->ppScsiTaskDI = NULL;
904 }
905 if (pThis->ppMMCDI)
906 {
907 (*pThis->ppMMCDI)->Release(pThis->ppMMCDI);
908 pThis->ppMMCDI = NULL;
909 }
910 }
911
912 IOObjectRelease(DVDService);
913 break;
914 }
915 }
916 CFRelease(PropsRef);
917 }
918 else
919 AssertMsgFailed(("krc=%#x\n", krc));
920
921 IOObjectRelease(DVDService);
922 i++;
923 }
924
925 IOObjectRelease(DVDServices);
926 return rc;
927
928#elif defined(RT_OS_FREEBSD)
929 RTFILE hFileDevice;
930 int rc = RTFileOpen(&hFileDevice, pThis->pszDeviceOpen, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
931 if (RT_FAILURE(rc))
932 return rc;
933
934 /*
935 * The current device handle can't passthrough SCSI commands.
936 * We have to get he passthrough device path and open this.
937 */
938 union ccb DeviceCCB;
939 memset(&DeviceCCB, 0, sizeof(DeviceCCB));
940
941 DeviceCCB.ccb_h.func_code = XPT_GDEVLIST;
942 int rcBSD = ioctl(RTFileToNative(hFileDevice), CAMGETPASSTHRU, &DeviceCCB);
943 if (!rcBSD)
944 {
945 char *pszPassthroughDevice = NULL;
946 rc = RTStrAPrintf(&pszPassthroughDevice, "/dev/%s%u",
947 DeviceCCB.cgdl.periph_name, DeviceCCB.cgdl.unit_number);
948 if (rc >= 0)
949 {
950 RTFILE hPassthroughDevice;
951 rc = RTFileOpen(&hPassthroughDevice, pszPassthroughDevice, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
952 RTStrFree(pszPassthroughDevice);
953 if (RT_SUCCESS(rc))
954 {
955 /* Get needed device parameters. */
956
957 /*
958 * The device path, target id and lun id. Those are
959 * needed for the SCSI passthrough ioctl.
960 */
961 memset(&DeviceCCB, 0, sizeof(DeviceCCB));
962 DeviceCCB.ccb_h.func_code = XPT_GDEVLIST;
963
964 rcBSD = ioctl(RTFileToNative(hPassthroughDevice), CAMGETPASSTHRU, &DeviceCCB);
965 if (!rcBSD)
966 {
967 if (DeviceCCB.cgdl.status != CAM_GDEVLIST_ERROR)
968 {
969 pThis->ScsiBus = DeviceCCB.ccb_h.path_id;
970 pThis->ScsiTargetID = DeviceCCB.ccb_h.target_id;
971 pThis->ScsiLunID = DeviceCCB.ccb_h.target_lun;
972 *pFileDevice = hPassthroughDevice;
973 }
974 else
975 {
976 /* The passthrough device wasn't found. */
977 rc = VERR_NOT_FOUND;
978 }
979 }
980 else
981 rc = RTErrConvertFromErrno(errno);
982
983 if (RT_FAILURE(rc))
984 RTFileClose(hPassthroughDevice);
985 }
986 }
987 else
988 rc = VERR_NO_STR_MEMORY;
989 }
990 else
991 rc = RTErrConvertFromErrno(errno);
992
993 RTFileClose(hFileDevice);
994 return rc;
995
996#else
997 uint32_t fFlags = (fReadOnly ? RTFILE_O_READ : RTFILE_O_READWRITE) | RTFILE_O_OPEN | RTFILE_O_DENY_NONE;
998# ifdef RT_OS_LINUX
999 fFlags |= RTFILE_O_NON_BLOCK;
1000# endif
1001 return RTFileOpen(pFileDevice, pThis->pszDeviceOpen, fFlags);
1002#endif
1003}
1004
1005#else /* RT_OS_SOLARIS */
1006
1007/**
1008 * Solaris wrapper for RTFileOpen.
1009 *
1010 * Solaris has to deal with two filehandles, a block and a raw one. Rather than messing
1011 * with drvHostBaseOpen's function signature & body, having a separate one is better.
1012 *
1013 * @returns VBox status code.
1014 */
1015static int drvHostBaseOpen(PDRVHOSTBASE pThis, PRTFILE pFileBlockDevice, PRTFILE pFileRawDevice, bool fReadOnly)
1016{
1017 unsigned fFlags = (fReadOnly ? RTFILE_O_READ : RTFILE_O_READWRITE)
1018 | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_NON_BLOCK;
1019 int rc = RTFileOpen(pFileBlockDevice, pThis->pszDeviceOpen, fFlags);
1020 if (RT_SUCCESS(rc))
1021 {
1022 rc = RTFileOpen(pFileRawDevice, pThis->pszRawDeviceOpen, fFlags);
1023 if (RT_SUCCESS(rc))
1024 return rc;
1025
1026 LogRel(("DVD: failed to open device %s rc=%Rrc\n", pThis->pszRawDeviceOpen, rc));
1027 RTFileClose(*pFileBlockDevice);
1028 }
1029 else
1030 LogRel(("DVD: failed to open device %s rc=%Rrc\n", pThis->pszDeviceOpen, rc));
1031 return rc;
1032}
1033#endif /* RT_OS_SOLARIS */
1034
1035
1036/**
1037 * (Re)opens the device.
1038 *
1039 * This is used to open the device during construction, but it's also used to re-open
1040 * the device when a media is inserted. This re-open will kill off any cached data
1041 * that Linux for some peculiar reason thinks should survive a media change...
1042 *
1043 * @returns VBOX status code.
1044 * @param pThis Instance data.
1045 */
1046static int drvHostBaseReopen(PDRVHOSTBASE pThis)
1047{
1048#ifndef RT_OS_DARWIN /* Only *one* open for darwin. */
1049 LogFlow(("%s-%d: drvHostBaseReopen: '%s'\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, pThis->pszDeviceOpen));
1050
1051 RTFILE hFileDevice;
1052#ifdef RT_OS_SOLARIS
1053 if (pThis->hFileRawDevice != NIL_RTFILE)
1054 {
1055 RTFileClose(pThis->hFileRawDevice);
1056 pThis->hFileRawDevice = NIL_RTFILE;
1057 }
1058 if (pThis->hFileDevice != NIL_RTFILE)
1059 {
1060 RTFileClose(pThis->hFileDevice);
1061 pThis->hFileDevice = NIL_RTFILE;
1062 }
1063 RTFILE hFileRawDevice;
1064 int rc = drvHostBaseOpen(pThis, &hFileDevice, &hFileRawDevice, pThis->fReadOnlyConfig);
1065#else
1066 int rc = drvHostBaseOpen(pThis, &hFileDevice, pThis->fReadOnlyConfig);
1067#endif
1068 if (RT_FAILURE(rc))
1069 {
1070 if (!pThis->fReadOnlyConfig)
1071 {
1072 LogFlow(("%s-%d: drvHostBaseReopen: '%s' - retry readonly (%Rrc)\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, pThis->pszDeviceOpen, rc));
1073#ifdef RT_OS_SOLARIS
1074 rc = drvHostBaseOpen(pThis, &hFileDevice, &hFileRawDevice, false);
1075#else
1076 rc = drvHostBaseOpen(pThis, &hFileDevice, false);
1077#endif
1078 }
1079 if (RT_FAILURE(rc))
1080 {
1081 LogFlow(("%s-%d: failed to open device '%s', rc=%Rrc\n",
1082 pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, pThis->pszDevice, rc));
1083 return rc;
1084 }
1085 pThis->fReadOnly = true;
1086 }
1087 else
1088 pThis->fReadOnly = pThis->fReadOnlyConfig;
1089
1090#ifdef RT_OS_SOLARIS
1091 if (pThis->hFileRawDevice != NIL_RTFILE)
1092 RTFileClose(pThis->hFileRawDevice);
1093 pThis->hFileRawDevice = hFileRawDevice;
1094#endif
1095
1096 if (pThis->hFileDevice != NIL_RTFILE)
1097 RTFileClose(pThis->hFileDevice);
1098 pThis->hFileDevice = hFileDevice;
1099#endif /* !RT_OS_DARWIN */
1100 return VINF_SUCCESS;
1101}
1102
1103
1104/**
1105 * Queries the media size.
1106 *
1107 * @returns VBox status code.
1108 * @param pThis Pointer to the instance data.
1109 * @param pcb Where to store the media size in bytes.
1110 */
1111static int drvHostBaseGetMediaSize(PDRVHOSTBASE pThis, uint64_t *pcb)
1112{
1113#if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)
1114 /*
1115 * Try a READ_CAPACITY command...
1116 */
1117 struct
1118 {
1119 uint32_t cBlocks;
1120 uint32_t cbBlock;
1121 } Buf = {0, 0};
1122 uint32_t cbBuf = sizeof(Buf);
1123 uint8_t abCmd[16] =
1124 {
1125 SCSI_READ_CAPACITY, 0, 0, 0, 0, 0, 0,
1126 0,0,0,0,0,0,0,0,0
1127 };
1128 int rc = DRVHostBaseScsiCmd(pThis, abCmd, 6, PDMBLOCKTXDIR_FROM_DEVICE, &Buf, &cbBuf, NULL, 0, 0);
1129 if (RT_SUCCESS(rc))
1130 {
1131 Assert(cbBuf == sizeof(Buf));
1132 Buf.cBlocks = RT_BE2H_U32(Buf.cBlocks);
1133 Buf.cbBlock = RT_BE2H_U32(Buf.cbBlock);
1134 //if (Buf.cbBlock > 2048) /* everyone else is doing this... check if it needed/right.*/
1135 // Buf.cbBlock = 2048;
1136 pThis->cbBlock = Buf.cbBlock;
1137
1138 *pcb = (uint64_t)Buf.cBlocks * Buf.cbBlock;
1139 }
1140 return rc;
1141
1142#elif defined(RT_OS_SOLARIS)
1143 /*
1144 * Sun docs suggests using DKIOCGGEOM instead of DKIOCGMEDIAINFO, but
1145 * Sun themselves use DKIOCGMEDIAINFO for DVDs/CDs, and use DKIOCGGEOM
1146 * for secondary storage devices.
1147 */
1148 struct dk_minfo MediaInfo;
1149 if (ioctl(RTFileToNative(pThis->hFileRawDevice), DKIOCGMEDIAINFO, &MediaInfo) == 0)
1150 {
1151 *pcb = MediaInfo.dki_capacity * (uint64_t)MediaInfo.dki_lbsize;
1152 return VINF_SUCCESS;
1153 }
1154 return RTFileSeek(pThis->hFileDevice, 0, RTFILE_SEEK_END, pcb);
1155
1156#elif defined(RT_OS_WINDOWS)
1157 /* use NT api, retry a few times if the media is being verified. */
1158 IO_STATUS_BLOCK IoStatusBlock = {0};
1159 FILE_FS_SIZE_INFORMATION FsSize= {0};
1160 NTSTATUS rcNt = NtQueryVolumeInformationFile((HANDLE)RTFileToNative(pThis->hFileDevice), &IoStatusBlock,
1161 &FsSize, sizeof(FsSize), FileFsSizeInformation);
1162 int cRetries = 5;
1163 while (rcNt == STATUS_VERIFY_REQUIRED && cRetries-- > 0)
1164 {
1165 RTThreadSleep(10);
1166 rcNt = NtQueryVolumeInformationFile((HANDLE)RTFileToNative(pThis->hFileDevice), &IoStatusBlock,
1167 &FsSize, sizeof(FsSize), FileFsSizeInformation);
1168 }
1169 if (rcNt >= 0)
1170 {
1171 *pcb = FsSize.TotalAllocationUnits.QuadPart * FsSize.BytesPerSector;
1172 return VINF_SUCCESS;
1173 }
1174
1175 /* convert nt status code to VBox status code. */
1176 /** @todo Make conversion function!. */
1177 int rc = VERR_GENERAL_FAILURE;
1178 switch (rcNt)
1179 {
1180 case STATUS_NO_MEDIA_IN_DEVICE: rc = VERR_MEDIA_NOT_PRESENT; break;
1181 case STATUS_VERIFY_REQUIRED: rc = VERR_TRY_AGAIN; break;
1182 }
1183 LogFlow(("drvHostBaseGetMediaSize: NtQueryVolumeInformationFile -> %#lx\n", rcNt, rc));
1184 return rc;
1185#else
1186 return RTFileSeek(pThis->hFileDevice, 0, RTFILE_SEEK_END, pcb);
1187#endif
1188}
1189
1190
1191#if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)
1192/**
1193 * Execute a SCSI command.
1194 *
1195 * @param pThis The instance data.
1196 * @param pbCmd Pointer to the SCSI command.
1197 * @param cbCmd The size of the SCSI command.
1198 * @param enmTxDir The transfer direction.
1199 * @param pvBuf The buffer. Can be NULL if enmTxDir is PDMBLOCKTXDIR_NONE.
1200 * @param pcbBuf Where to get the buffer size from and put the actual transfer size. Can be NULL.
1201 * @param pbSense Where to put the sense data. Can be NULL.
1202 * @param cbSense Size of the sense data buffer.
1203 * @param cTimeoutMillies The timeout. 0 mean the default timeout.
1204 *
1205 * @returns VINF_SUCCESS on success (no sense code).
1206 * @returns VERR_UNRESOLVED_ERROR if sense code is present.
1207 * @returns Some other VBox status code on failures without sense code.
1208 *
1209 * @todo Fix VERR_UNRESOLVED_ERROR abuse.
1210 */
1211DECLCALLBACK(int) DRVHostBaseScsiCmd(PDRVHOSTBASE pThis, const uint8_t *pbCmd, size_t cbCmd, PDMBLOCKTXDIR enmTxDir,
1212 void *pvBuf, uint32_t *pcbBuf, uint8_t *pbSense, size_t cbSense, uint32_t cTimeoutMillies)
1213{
1214 /*
1215 * Minimal input validation.
1216 */
1217 Assert(enmTxDir == PDMBLOCKTXDIR_NONE || enmTxDir == PDMBLOCKTXDIR_FROM_DEVICE || enmTxDir == PDMBLOCKTXDIR_TO_DEVICE);
1218 Assert(!pvBuf || pcbBuf);
1219 Assert(pvBuf || enmTxDir == PDMBLOCKTXDIR_NONE);
1220 Assert(pbSense || !cbSense);
1221 AssertPtr(pbCmd);
1222 Assert(cbCmd <= 16 && cbCmd >= 1);
1223 const uint32_t cbBuf = pcbBuf ? *pcbBuf : 0;
1224 if (pcbBuf)
1225 *pcbBuf = 0;
1226
1227# ifdef RT_OS_DARWIN
1228 Assert(pThis->ppScsiTaskDI);
1229
1230 int rc = VERR_GENERAL_FAILURE;
1231 SCSITaskInterface **ppScsiTaskI = (*pThis->ppScsiTaskDI)->CreateSCSITask(pThis->ppScsiTaskDI);
1232 if (!ppScsiTaskI)
1233 return VERR_NO_MEMORY;
1234 do
1235 {
1236 /* Setup the scsi command. */
1237 SCSICommandDescriptorBlock cdb = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
1238 memcpy(&cdb[0], pbCmd, cbCmd);
1239 IOReturn irc = (*ppScsiTaskI)->SetCommandDescriptorBlock(ppScsiTaskI, cdb, cbCmd);
1240 AssertBreak(irc == kIOReturnSuccess);
1241
1242 /* Setup the buffer. */
1243 if (enmTxDir == PDMBLOCKTXDIR_NONE)
1244 irc = (*ppScsiTaskI)->SetScatterGatherEntries(ppScsiTaskI, NULL, 0, 0, kSCSIDataTransfer_NoDataTransfer);
1245 else
1246 {
1247 IOVirtualRange Range = { (IOVirtualAddress)pvBuf, cbBuf };
1248 irc = (*ppScsiTaskI)->SetScatterGatherEntries(ppScsiTaskI, &Range, 1, cbBuf,
1249 enmTxDir == PDMBLOCKTXDIR_FROM_DEVICE
1250 ? kSCSIDataTransfer_FromTargetToInitiator
1251 : kSCSIDataTransfer_FromInitiatorToTarget);
1252 }
1253 AssertBreak(irc == kIOReturnSuccess);
1254
1255 /* Set the timeout. */
1256 irc = (*ppScsiTaskI)->SetTimeoutDuration(ppScsiTaskI, cTimeoutMillies ? cTimeoutMillies : 30000 /*ms*/);
1257 AssertBreak(irc == kIOReturnSuccess);
1258
1259 /* Execute the command and get the response. */
1260 SCSI_Sense_Data SenseData = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
1261 SCSIServiceResponse ServiceResponse = kSCSIServiceResponse_Request_In_Process;
1262 SCSITaskStatus TaskStatus = kSCSITaskStatus_GOOD;
1263 UInt64 cbReturned = 0;
1264 irc = (*ppScsiTaskI)->ExecuteTaskSync(ppScsiTaskI, &SenseData, &TaskStatus, &cbReturned);
1265 AssertBreak(irc == kIOReturnSuccess);
1266 if (pcbBuf)
1267 *pcbBuf = (int32_t)cbReturned;
1268
1269 irc = (*ppScsiTaskI)->GetSCSIServiceResponse(ppScsiTaskI, &ServiceResponse);
1270 AssertBreak(irc == kIOReturnSuccess);
1271 AssertBreak(ServiceResponse == kSCSIServiceResponse_TASK_COMPLETE);
1272
1273 if (TaskStatus == kSCSITaskStatus_GOOD)
1274 rc = VINF_SUCCESS;
1275 else if ( TaskStatus == kSCSITaskStatus_CHECK_CONDITION
1276 && pbSense)
1277 {
1278 memset(pbSense, 0, cbSense); /* lazy */
1279 memcpy(pbSense, &SenseData, RT_MIN(sizeof(SenseData), cbSense));
1280 rc = VERR_UNRESOLVED_ERROR;
1281 }
1282 /** @todo convert sense codes when caller doesn't wish to do this himself. */
1283 /*else if ( TaskStatus == kSCSITaskStatus_CHECK_CONDITION
1284 && SenseData.ADDITIONAL_SENSE_CODE == 0x3A)
1285 rc = VERR_MEDIA_NOT_PRESENT; */
1286 else
1287 {
1288 rc = enmTxDir == PDMBLOCKTXDIR_NONE
1289 ? VERR_DEV_IO_ERROR
1290 : enmTxDir == PDMBLOCKTXDIR_FROM_DEVICE
1291 ? VERR_READ_ERROR
1292 : VERR_WRITE_ERROR;
1293 if (pThis->cLogRelErrors++ < 10)
1294 LogRel(("DVD scsi error: cmd={%.*Rhxs} TaskStatus=%#x key=%#x ASC=%#x ASCQ=%#x (%Rrc)\n",
1295 cbCmd, pbCmd, TaskStatus, SenseData.SENSE_KEY, SenseData.ADDITIONAL_SENSE_CODE,
1296 SenseData.ADDITIONAL_SENSE_CODE_QUALIFIER, rc));
1297 }
1298 } while (0);
1299
1300 (*ppScsiTaskI)->Release(ppScsiTaskI);
1301
1302# elif defined(RT_OS_FREEBSD)
1303 int rc = VINF_SUCCESS;
1304 int rcBSD = 0;
1305 union ccb DeviceCCB;
1306 union ccb *pDeviceCCB = &DeviceCCB;
1307 u_int32_t fFlags;
1308
1309 memset(pDeviceCCB, 0, sizeof(DeviceCCB));
1310 pDeviceCCB->ccb_h.path_id = pThis->ScsiBus;
1311 pDeviceCCB->ccb_h.target_id = pThis->ScsiTargetID;
1312 pDeviceCCB->ccb_h.target_lun = pThis->ScsiLunID;
1313
1314 /* The SCSI INQUIRY command can't be passed through directly. */
1315 if (pbCmd[0] == SCSI_INQUIRY)
1316 {
1317 pDeviceCCB->ccb_h.func_code = XPT_GDEV_TYPE;
1318
1319 rcBSD = ioctl(RTFileToNative(pThis->hFileDevice), CAMIOCOMMAND, pDeviceCCB);
1320 if (!rcBSD)
1321 {
1322 uint32_t cbCopy = cbBuf < sizeof(struct scsi_inquiry_data)
1323 ? cbBuf
1324 : sizeof(struct scsi_inquiry_data);;
1325 memcpy(pvBuf, &pDeviceCCB->cgd.inq_data, cbCopy);
1326 memset(pbSense, 0, cbSense);
1327
1328 if (pcbBuf)
1329 *pcbBuf = cbCopy;
1330 }
1331 else
1332 rc = RTErrConvertFromErrno(errno);
1333 }
1334 else
1335 {
1336 /* Copy the CDB. */
1337 memcpy(&pDeviceCCB->csio.cdb_io.cdb_bytes, pbCmd, cbCmd);
1338
1339 /* Set direction. */
1340 if (enmTxDir == PDMBLOCKTXDIR_NONE)
1341 fFlags = CAM_DIR_NONE;
1342 else if (enmTxDir == PDMBLOCKTXDIR_FROM_DEVICE)
1343 fFlags = CAM_DIR_IN;
1344 else
1345 fFlags = CAM_DIR_OUT;
1346
1347 fFlags |= CAM_DEV_QFRZDIS;
1348
1349 cam_fill_csio(&pDeviceCCB->csio, 1, NULL, fFlags, MSG_SIMPLE_Q_TAG,
1350 (u_int8_t *)pvBuf, cbBuf, cbSense, cbCmd,
1351 cTimeoutMillies ? cTimeoutMillies : 30000/* timeout */);
1352
1353 /* Send command */
1354 rcBSD = ioctl(RTFileToNative(pThis->hFileDevice), CAMIOCOMMAND, pDeviceCCB);
1355 if (!rcBSD)
1356 {
1357 switch (pDeviceCCB->ccb_h.status & CAM_STATUS_MASK)
1358 {
1359 case CAM_REQ_CMP:
1360 rc = VINF_SUCCESS;
1361 break;
1362 case CAM_SEL_TIMEOUT:
1363 rc = VERR_DEV_IO_ERROR;
1364 break;
1365 case CAM_CMD_TIMEOUT:
1366 rc = VERR_TIMEOUT;
1367 break;
1368 default:
1369 rc = VERR_DEV_IO_ERROR;
1370 }
1371
1372 if (pcbBuf)
1373 *pcbBuf = cbBuf - pDeviceCCB->csio.resid;
1374
1375 if (pbSense)
1376 memcpy(pbSense, &pDeviceCCB->csio.sense_data,
1377 cbSense - pDeviceCCB->csio.sense_resid);
1378 }
1379 else
1380 rc = RTErrConvertFromErrno(errno);
1381 }
1382# endif
1383
1384 return rc;
1385}
1386#endif
1387
1388
1389/**
1390 * Media present.
1391 * Query the size and notify the above driver / device.
1392 *
1393 * @param pThis The instance data.
1394 */
1395int DRVHostBaseMediaPresent(PDRVHOSTBASE pThis)
1396{
1397 /*
1398 * Open the drive.
1399 */
1400 int rc = drvHostBaseReopen(pThis);
1401 if (RT_FAILURE(rc))
1402 return rc;
1403
1404 /*
1405 * Determine the size.
1406 */
1407 uint64_t cb;
1408 rc = pThis->pfnGetMediaSize(pThis, &cb);
1409 if (RT_FAILURE(rc))
1410 {
1411 LogFlow(("%s-%d: failed to figure media size of %s, rc=%Rrc\n",
1412 pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, pThis->pszDevice, rc));
1413 return rc;
1414 }
1415
1416 /*
1417 * Update the data and inform the unit.
1418 */
1419 pThis->cbSize = cb;
1420 pThis->fMediaPresent = true;
1421 if (pThis->pDrvMountNotify)
1422 pThis->pDrvMountNotify->pfnMountNotify(pThis->pDrvMountNotify);
1423 LogFlow(("%s-%d: drvHostBaseMediaPresent: cbSize=%lld (%#llx)\n",
1424 pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, pThis->cbSize, pThis->cbSize));
1425 return VINF_SUCCESS;
1426}
1427
1428
1429/**
1430 * Media no longer present.
1431 * @param pThis The instance data.
1432 */
1433void DRVHostBaseMediaNotPresent(PDRVHOSTBASE pThis)
1434{
1435 pThis->fMediaPresent = false;
1436 pThis->fLocked = false;
1437 pThis->PCHSGeometry.cCylinders = 0;
1438 pThis->PCHSGeometry.cHeads = 0;
1439 pThis->PCHSGeometry.cSectors = 0;
1440 pThis->LCHSGeometry.cCylinders = 0;
1441 pThis->LCHSGeometry.cHeads = 0;
1442 pThis->LCHSGeometry.cSectors = 0;
1443 if (pThis->pDrvMountNotify)
1444 pThis->pDrvMountNotify->pfnUnmountNotify(pThis->pDrvMountNotify);
1445}
1446
1447
1448#ifdef RT_OS_WINDOWS
1449
1450/**
1451 * Window procedure for the invisible window used to catch the WM_DEVICECHANGE broadcasts.
1452 */
1453static LRESULT CALLBACK DeviceChangeWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1454{
1455 Log2(("DeviceChangeWindowProc: hwnd=%08x uMsg=%08x\n", hwnd, uMsg));
1456 if (uMsg == WM_DESTROY)
1457 {
1458 PDRVHOSTBASE pThis = (PDRVHOSTBASE)GetWindowLong(hwnd, GWLP_USERDATA);
1459 if (pThis)
1460 ASMAtomicXchgSize(&pThis->hwndDeviceChange, NULL);
1461 PostQuitMessage(0);
1462 }
1463
1464 if (uMsg != WM_DEVICECHANGE)
1465 return DefWindowProc(hwnd, uMsg, wParam, lParam);
1466
1467 PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)lParam;
1468 PDRVHOSTBASE pThis = (PDRVHOSTBASE)GetWindowLongPtr(hwnd, GWLP_USERDATA);
1469 Assert(pThis);
1470 if (pThis == NULL)
1471 return 0;
1472
1473 switch (wParam)
1474 {
1475 case DBT_DEVICEARRIVAL:
1476 case DBT_DEVICEREMOVECOMPLETE:
1477 // Check whether a CD or DVD was inserted into or removed from a drive.
1478 if (lpdb->dbch_devicetype == DBT_DEVTYP_VOLUME)
1479 {
1480 PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb;
1481 if ( (lpdbv->dbcv_flags & DBTF_MEDIA)
1482 && (pThis->fUnitMask & lpdbv->dbcv_unitmask))
1483 {
1484 RTCritSectEnter(&pThis->CritSect);
1485 if (wParam == DBT_DEVICEARRIVAL)
1486 {
1487 int cRetries = 10;
1488 int rc = DRVHostBaseMediaPresent(pThis);
1489 while (RT_FAILURE(rc) && cRetries-- > 0)
1490 {
1491 RTThreadSleep(50);
1492 rc = DRVHostBaseMediaPresent(pThis);
1493 }
1494 }
1495 else
1496 DRVHostBaseMediaNotPresent(pThis);
1497 RTCritSectLeave(&pThis->CritSect);
1498 }
1499 }
1500 break;
1501 }
1502 return TRUE;
1503}
1504
1505#endif /* RT_OS_WINDOWS */
1506
1507
1508/**
1509 * This thread will periodically poll the device for media presence.
1510 *
1511 * @returns Ignored.
1512 * @param ThreadSelf Handle of this thread. Ignored.
1513 * @param pvUser Pointer to the driver instance structure.
1514 */
1515static DECLCALLBACK(int) drvHostBaseMediaThread(RTTHREAD ThreadSelf, void *pvUser)
1516{
1517 PDRVHOSTBASE pThis = (PDRVHOSTBASE)pvUser;
1518 LogFlow(("%s-%d: drvHostBaseMediaThread: ThreadSelf=%p pvUser=%p\n",
1519 pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, ThreadSelf, pvUser));
1520#ifdef RT_OS_WINDOWS
1521 static WNDCLASS s_classDeviceChange = {0};
1522 static ATOM s_hAtomDeviceChange = 0;
1523
1524 /*
1525 * Register custom window class.
1526 */
1527 if (s_hAtomDeviceChange == 0)
1528 {
1529 memset(&s_classDeviceChange, 0, sizeof(s_classDeviceChange));
1530 s_classDeviceChange.lpfnWndProc = DeviceChangeWindowProc;
1531 s_classDeviceChange.lpszClassName = "VBOX_DeviceChangeClass";
1532 s_classDeviceChange.hInstance = GetModuleHandle("VBOXDD.DLL");
1533 Assert(s_classDeviceChange.hInstance);
1534 s_hAtomDeviceChange = RegisterClassA(&s_classDeviceChange);
1535 Assert(s_hAtomDeviceChange);
1536 }
1537
1538 /*
1539 * Create Window w/ the pThis as user data.
1540 */
1541 HWND hwnd = CreateWindow((LPCTSTR)s_hAtomDeviceChange, "", WS_POPUP, 0, 0, 0, 0, 0, 0, s_classDeviceChange.hInstance, 0);
1542 AssertMsg(hwnd, ("CreateWindow failed with %d\n", GetLastError()));
1543 SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pThis);
1544
1545 /*
1546 * Signal the waiting EMT thread that everything went fine.
1547 */
1548 ASMAtomicXchgSize(&pThis->hwndDeviceChange, hwnd);
1549 RTThreadUserSignal(ThreadSelf);
1550 if (!hwnd)
1551 {
1552 LogFlow(("%s-%d: drvHostBaseMediaThread: returns VERR_GENERAL_FAILURE\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance));
1553 return VERR_GENERAL_FAILURE;
1554 }
1555 LogFlow(("%s-%d: drvHostBaseMediaThread: Created hwndDeviceChange=%p\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, hwnd));
1556
1557 /*
1558 * Message pump.
1559 */
1560 MSG Msg;
1561 BOOL fRet;
1562 while ((fRet = GetMessage(&Msg, NULL, 0, 0)) != FALSE)
1563 {
1564 if (fRet != -1)
1565 {
1566 TranslateMessage(&Msg);
1567 DispatchMessage(&Msg);
1568 }
1569 //else: handle the error and possibly exit
1570 }
1571 Assert(!pThis->hwndDeviceChange);
1572
1573#else /* !RT_OS_WINDOWS */
1574 bool fFirst = true;
1575 int cRetries = 10;
1576 while (!pThis->fShutdownPoller)
1577 {
1578 /*
1579 * Perform the polling (unless we've run out of 50ms retries).
1580 */
1581 if ( pThis->pfnPoll
1582 && cRetries-- > 0)
1583 {
1584
1585 int rc = pThis->pfnPoll(pThis);
1586 if (RT_FAILURE(rc))
1587 {
1588 RTSemEventWait(pThis->EventPoller, 50);
1589 continue;
1590 }
1591 }
1592
1593 /*
1594 * Signal EMT after the first go.
1595 */
1596 if (fFirst)
1597 {
1598 RTThreadUserSignal(ThreadSelf);
1599 fFirst = false;
1600 }
1601
1602 /*
1603 * Sleep.
1604 */
1605 int rc = RTSemEventWait(pThis->EventPoller, pThis->cMilliesPoller);
1606 if ( RT_FAILURE(rc)
1607 && rc != VERR_TIMEOUT)
1608 {
1609 AssertMsgFailed(("rc=%Rrc\n", rc));
1610 pThis->ThreadPoller = NIL_RTTHREAD;
1611 LogFlow(("drvHostBaseMediaThread: returns %Rrc\n", rc));
1612 return rc;
1613 }
1614 cRetries = 10;
1615 }
1616
1617#endif /* !RT_OS_WINDOWS */
1618
1619 /* (Don't clear the thread handle here, the destructor thread is using it to wait.) */
1620 LogFlow(("%s-%d: drvHostBaseMediaThread: returns VINF_SUCCESS\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance));
1621 return VINF_SUCCESS;
1622}
1623
1624/* -=-=-=-=- driver interface -=-=-=-=- */
1625
1626
1627/**
1628 * Done state load operation.
1629 *
1630 * @returns VBox load code.
1631 * @param pDrvIns Driver instance of the driver which registered the data unit.
1632 * @param pSSM SSM operation handle.
1633 */
1634static DECLCALLBACK(int) drvHostBaseLoadDone(PPDMDRVINS pDrvIns, PSSMHANDLE pSSM)
1635{
1636 PDRVHOSTBASE pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTBASE);
1637 LogFlow(("%s-%d: drvHostBaseMediaThread:\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance));
1638 RTCritSectEnter(&pThis->CritSect);
1639
1640 /*
1641 * Tell the device/driver above us that the media status is uncertain.
1642 */
1643 if (pThis->pDrvMountNotify)
1644 {
1645 pThis->pDrvMountNotify->pfnUnmountNotify(pThis->pDrvMountNotify);
1646 if (pThis->fMediaPresent)
1647 pThis->pDrvMountNotify->pfnMountNotify(pThis->pDrvMountNotify);
1648 }
1649
1650 RTCritSectLeave(&pThis->CritSect);
1651 return VINF_SUCCESS;
1652}
1653
1654
1655/** @copydoc FNPDMDRVDESTRUCT */
1656DECLCALLBACK(void) DRVHostBaseDestruct(PPDMDRVINS pDrvIns)
1657{
1658 PDRVHOSTBASE pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTBASE);
1659 LogFlow(("%s-%d: drvHostBaseDestruct: iInstance=%d\n", pDrvIns->pReg->szName, pDrvIns->iInstance, pDrvIns->iInstance));
1660
1661 /*
1662 * Terminate the thread.
1663 */
1664 if (pThis->ThreadPoller != NIL_RTTHREAD)
1665 {
1666 pThis->fShutdownPoller = true;
1667 int rc;
1668 int cTimes = 50;
1669 do
1670 {
1671#ifdef RT_OS_WINDOWS
1672 if (pThis->hwndDeviceChange)
1673 PostMessage(pThis->hwndDeviceChange, WM_CLOSE, 0, 0); /* default win proc will destroy the window */
1674#else
1675 RTSemEventSignal(pThis->EventPoller);
1676#endif
1677 rc = RTThreadWait(pThis->ThreadPoller, 100, NULL);
1678 } while (cTimes-- > 0 && rc == VERR_TIMEOUT);
1679
1680 if (!rc)
1681 pThis->ThreadPoller = NIL_RTTHREAD;
1682 }
1683
1684 /*
1685 * Unlock the drive if we've locked it or we're in passthru mode.
1686 */
1687#ifdef RT_OS_DARWIN
1688 if ( ( pThis->fLocked
1689 || pThis->IBlock.pfnSendCmd)
1690 && pThis->ppScsiTaskDI
1691#else /** @todo Check if the other guys can mix pfnDoLock with scsi passthru.
1692 * (We're currently not unlocking the device after use. See todo in DevATA.cpp.) */
1693 if ( pThis->fLocked
1694 && pThis->hFileDevice != NIL_RTFILE
1695#endif
1696 && pThis->pfnDoLock)
1697 {
1698 int rc = pThis->pfnDoLock(pThis, false);
1699 if (RT_SUCCESS(rc))
1700 pThis->fLocked = false;
1701 }
1702
1703 /*
1704 * Cleanup the other resources.
1705 */
1706#ifdef RT_OS_WINDOWS
1707 if (pThis->hwndDeviceChange)
1708 {
1709 if (SetWindowLongPtr(pThis->hwndDeviceChange, GWLP_USERDATA, 0) == (LONG_PTR)pThis)
1710 PostMessage(pThis->hwndDeviceChange, WM_CLOSE, 0, 0); /* default win proc will destroy the window */
1711 pThis->hwndDeviceChange = NULL;
1712 }
1713#else
1714 if (pThis->EventPoller != NULL)
1715 {
1716 RTSemEventDestroy(pThis->EventPoller);
1717 pThis->EventPoller = NULL;
1718 }
1719#endif
1720
1721#ifdef RT_OS_DARWIN
1722 /*
1723 * The unclaiming doesn't seem to mean much, the DVD is actually
1724 * remounted when we release exclusive access. I'm not quite sure
1725 * if I should put the unclaim first or not...
1726 *
1727 * Anyway, that it's automatically remounted very good news for us,
1728 * because that means we don't have to mess with that ourselves. Of
1729 * course there is the unlikely scenario that we've succeeded in claiming
1730 * and umount the DVD but somehow failed to gain exclusive scsi access...
1731 */
1732 if (pThis->ppScsiTaskDI)
1733 {
1734 LogFlow(("%s-%d: releasing exclusive scsi access!\n", pDrvIns->pReg->szName, pDrvIns->iInstance));
1735 (*pThis->ppScsiTaskDI)->ReleaseExclusiveAccess(pThis->ppScsiTaskDI);
1736 (*pThis->ppScsiTaskDI)->Release(pThis->ppScsiTaskDI);
1737 pThis->ppScsiTaskDI = NULL;
1738 }
1739 if (pThis->pDADisk)
1740 {
1741 LogFlow(("%s-%d: unclaiming the disk!\n", pDrvIns->pReg->szName, pDrvIns->iInstance));
1742 DADiskUnclaim(pThis->pDADisk);
1743 CFRelease(pThis->pDADisk);
1744 pThis->pDADisk = NULL;
1745 }
1746 if (pThis->ppMMCDI)
1747 {
1748 LogFlow(("%s-%d: releasing the MMC object!\n", pDrvIns->pReg->szName, pDrvIns->iInstance));
1749 (*pThis->ppMMCDI)->Release(pThis->ppMMCDI);
1750 pThis->ppMMCDI = NULL;
1751 }
1752 if (pThis->MasterPort)
1753 {
1754 mach_port_deallocate(mach_task_self(), pThis->MasterPort);
1755 pThis->MasterPort = NULL;
1756 }
1757 if (pThis->pDASession)
1758 {
1759 LogFlow(("%s-%d: releasing the DA session!\n", pDrvIns->pReg->szName, pDrvIns->iInstance));
1760 CFRelease(pThis->pDASession);
1761 pThis->pDASession = NULL;
1762 }
1763#else
1764 if (pThis->hFileDevice != NIL_RTFILE)
1765 {
1766 int rc = RTFileClose(pThis->hFileDevice);
1767 AssertRC(rc);
1768 pThis->hFileDevice = NIL_RTFILE;
1769 }
1770#endif
1771
1772#ifdef RT_OS_SOLARIS
1773 if (pThis->hFileRawDevice != NIL_RTFILE)
1774 {
1775 int rc = RTFileClose(pThis->hFileRawDevice);
1776 AssertRC(rc);
1777 pThis->hFileRawDevice = NIL_RTFILE;
1778 }
1779
1780 if (pThis->pszRawDeviceOpen)
1781 {
1782 RTStrFree(pThis->pszRawDeviceOpen);
1783 pThis->pszRawDeviceOpen = NULL;
1784 }
1785#endif
1786
1787 if (pThis->pszDevice)
1788 {
1789 MMR3HeapFree(pThis->pszDevice);
1790 pThis->pszDevice = NULL;
1791 }
1792
1793 if (pThis->pszDeviceOpen)
1794 {
1795 RTStrFree(pThis->pszDeviceOpen);
1796 pThis->pszDeviceOpen = NULL;
1797 }
1798
1799 /* Forget about the notifications. */
1800 pThis->pDrvMountNotify = NULL;
1801
1802 /* Leave the instance operational if this is just a cleanup of the state
1803 * after an attach error happened. So don't destroy the critsect then. */
1804 if (!pThis->fKeepInstance && RTCritSectIsInitialized(&pThis->CritSect))
1805 RTCritSectDelete(&pThis->CritSect);
1806 LogFlow(("%s-%d: drvHostBaseDestruct completed\n", pDrvIns->pReg->szName, pDrvIns->iInstance));
1807}
1808
1809
1810/**
1811 * Initializes the instance data (init part 1).
1812 *
1813 * The driver which derives from this base driver will override function pointers after
1814 * calling this method, and complete the construction by calling DRVHostBaseInitFinish().
1815 *
1816 * On failure call DRVHostBaseDestruct().
1817 *
1818 * @returns VBox status code.
1819 * @param pDrvIns Driver instance.
1820 * @param pCfg Configuration handle.
1821 * @param enmType Device type.
1822 */
1823int DRVHostBaseInitData(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, PDMBLOCKTYPE enmType)
1824{
1825 PDRVHOSTBASE pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTBASE);
1826 LogFlow(("%s-%d: DRVHostBaseInitData: iInstance=%d\n", pDrvIns->pReg->szName, pDrvIns->iInstance, pDrvIns->iInstance));
1827
1828 /*
1829 * Initialize most of the data members.
1830 */
1831 pThis->pDrvIns = pDrvIns;
1832 pThis->fKeepInstance = false;
1833 pThis->ThreadPoller = NIL_RTTHREAD;
1834#ifdef RT_OS_DARWIN
1835 pThis->MasterPort = NULL;
1836 pThis->ppMMCDI = NULL;
1837 pThis->ppScsiTaskDI = NULL;
1838 pThis->cbBlock = 0;
1839 pThis->pDADisk = NULL;
1840 pThis->pDASession = NULL;
1841#else
1842 pThis->hFileDevice = NIL_RTFILE;
1843#endif
1844#ifdef RT_OS_SOLARIS
1845 pThis->hFileRawDevice = NIL_RTFILE;
1846#endif
1847 pThis->enmType = enmType;
1848 //pThis->cErrors = 0;
1849
1850 pThis->pfnGetMediaSize = drvHostBaseGetMediaSize;
1851
1852 /* IBase. */
1853 pDrvIns->IBase.pfnQueryInterface = drvHostBaseQueryInterface;
1854
1855 /* IBlock. */
1856 pThis->IBlock.pfnRead = drvHostBaseRead;
1857 pThis->IBlock.pfnWrite = drvHostBaseWrite;
1858 pThis->IBlock.pfnFlush = drvHostBaseFlush;
1859 pThis->IBlock.pfnIsReadOnly = drvHostBaseIsReadOnly;
1860 pThis->IBlock.pfnGetSize = drvHostBaseGetSize;
1861 pThis->IBlock.pfnGetType = drvHostBaseGetType;
1862 pThis->IBlock.pfnGetUuid = drvHostBaseGetUuid;
1863
1864 /* IBlockBios. */
1865 pThis->IBlockBios.pfnGetPCHSGeometry = drvHostBaseGetPCHSGeometry;
1866 pThis->IBlockBios.pfnSetPCHSGeometry = drvHostBaseSetPCHSGeometry;
1867 pThis->IBlockBios.pfnGetLCHSGeometry = drvHostBaseGetLCHSGeometry;
1868 pThis->IBlockBios.pfnSetLCHSGeometry = drvHostBaseSetLCHSGeometry;
1869 pThis->IBlockBios.pfnIsVisible = drvHostBaseIsVisible;
1870 pThis->IBlockBios.pfnGetType = drvHostBaseBiosGetType;
1871
1872 /* IMount. */
1873 pThis->IMount.pfnMount = drvHostBaseMount;
1874 pThis->IMount.pfnUnmount = drvHostBaseUnmount;
1875 pThis->IMount.pfnIsMounted = drvHostBaseIsMounted;
1876 pThis->IMount.pfnLock = drvHostBaseLock;
1877 pThis->IMount.pfnUnlock = drvHostBaseUnlock;
1878 pThis->IMount.pfnIsLocked = drvHostBaseIsLocked;
1879
1880 /*
1881 * Get the IBlockPort & IMountNotify interfaces of the above driver/device.
1882 */
1883 pThis->pDrvBlockPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIBLOCKPORT);
1884 if (!pThis->pDrvBlockPort)
1885 {
1886 AssertMsgFailed(("Configuration error: No block port interface above!\n"));
1887 return VERR_PDM_MISSING_INTERFACE_ABOVE;
1888 }
1889 pThis->pDrvMountNotify = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMOUNTNOTIFY);
1890
1891 /*
1892 * Query configuration.
1893 */
1894 /* Device */
1895 int rc = CFGMR3QueryStringAlloc(pCfg, "Path", &pThis->pszDevice);
1896 if (RT_FAILURE(rc))
1897 {
1898 AssertMsgFailed(("Configuration error: query for \"Path\" string returned %Rra.\n", rc));
1899 return rc;
1900 }
1901
1902 /* Mountable */
1903 uint32_t u32;
1904 rc = CFGMR3QueryU32(pCfg, "Interval", &u32);
1905 if (RT_SUCCESS(rc))
1906 pThis->cMilliesPoller = u32;
1907 else if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1908 pThis->cMilliesPoller = 1000;
1909 else if (RT_FAILURE(rc))
1910 {
1911 AssertMsgFailed(("Configuration error: Query \"Mountable\" resulted in %Rrc.\n", rc));
1912 return rc;
1913 }
1914
1915 /* ReadOnly */
1916 rc = CFGMR3QueryBool(pCfg, "ReadOnly", &pThis->fReadOnlyConfig);
1917 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1918 pThis->fReadOnlyConfig = enmType == PDMBLOCKTYPE_DVD || enmType == PDMBLOCKTYPE_CDROM ? true : false;
1919 else if (RT_FAILURE(rc))
1920 {
1921 AssertMsgFailed(("Configuration error: Query \"ReadOnly\" resulted in %Rrc.\n", rc));
1922 return rc;
1923 }
1924
1925 /* Locked */
1926 rc = CFGMR3QueryBool(pCfg, "Locked", &pThis->fLocked);
1927 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1928 pThis->fLocked = false;
1929 else if (RT_FAILURE(rc))
1930 {
1931 AssertMsgFailed(("Configuration error: Query \"Locked\" resulted in %Rrc.\n", rc));
1932 return rc;
1933 }
1934
1935 /* BIOS visible */
1936 rc = CFGMR3QueryBool(pCfg, "BIOSVisible", &pThis->fBiosVisible);
1937 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1938 pThis->fBiosVisible = true;
1939 else if (RT_FAILURE(rc))
1940 {
1941 AssertMsgFailed(("Configuration error: Query \"BIOSVisible\" resulted in %Rrc.\n", rc));
1942 return rc;
1943 }
1944
1945 /* Uuid */
1946 char *psz;
1947 rc = CFGMR3QueryStringAlloc(pCfg, "Uuid", &psz);
1948 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1949 RTUuidClear(&pThis->Uuid);
1950 else if (RT_SUCCESS(rc))
1951 {
1952 rc = RTUuidFromStr(&pThis->Uuid, psz);
1953 if (RT_FAILURE(rc))
1954 {
1955 AssertMsgFailed(("Configuration error: Uuid from string failed on \"%s\", rc=%Rrc.\n", psz, rc));
1956 MMR3HeapFree(psz);
1957 return rc;
1958 }
1959 MMR3HeapFree(psz);
1960 }
1961 else
1962 {
1963 AssertMsgFailed(("Configuration error: Failed to obtain the uuid, rc=%Rrc.\n", rc));
1964 return rc;
1965 }
1966
1967 /* Define whether attach failure is an error (default) or not. */
1968 bool fAttachFailError;
1969 rc = CFGMR3QueryBool(pCfg, "AttachFailError", &fAttachFailError);
1970 if (RT_FAILURE(rc))
1971 fAttachFailError = true;
1972 pThis->fAttachFailError = fAttachFailError;
1973
1974 /* name to open & watch for */
1975#ifdef RT_OS_WINDOWS
1976 int iBit = RT_C_TO_UPPER(pThis->pszDevice[0]) - 'A';
1977 if ( iBit > 'Z' - 'A'
1978 || pThis->pszDevice[1] != ':'
1979 || pThis->pszDevice[2])
1980 {
1981 AssertMsgFailed(("Configuration error: Invalid drive specification: '%s'\n", pThis->pszDevice));
1982 return VERR_INVALID_PARAMETER;
1983 }
1984 pThis->fUnitMask = 1 << iBit;
1985 RTStrAPrintf(&pThis->pszDeviceOpen, "\\\\.\\%s", pThis->pszDevice);
1986
1987#elif defined(RT_OS_SOLARIS)
1988 char *pszBlockDevName = getfullblkname(pThis->pszDevice);
1989 if (!pszBlockDevName)
1990 return VERR_NO_MEMORY;
1991 pThis->pszDeviceOpen = RTStrDup(pszBlockDevName); /* for RTStrFree() */
1992 free(pszBlockDevName);
1993 pThis->pszRawDeviceOpen = RTStrDup(pThis->pszDevice);
1994
1995#else
1996 pThis->pszDeviceOpen = RTStrDup(pThis->pszDevice);
1997#endif
1998
1999 if (!pThis->pszDeviceOpen)
2000 return VERR_NO_MEMORY;
2001
2002 return VINF_SUCCESS;
2003}
2004
2005
2006/**
2007 * Do the 2nd part of the init after the derived driver has overridden the defaults.
2008 *
2009 * On failure call DRVHostBaseDestruct().
2010 *
2011 * @returns VBox status code.
2012 * @param pThis Pointer to the instance data.
2013 */
2014int DRVHostBaseInitFinish(PDRVHOSTBASE pThis)
2015{
2016 int src = VINF_SUCCESS;
2017 PPDMDRVINS pDrvIns = pThis->pDrvIns;
2018
2019 /* log config summary */
2020 Log(("%s-%d: pszDevice='%s' (%s) cMilliesPoller=%d fReadOnlyConfig=%d fLocked=%d fBIOSVisible=%d Uuid=%RTuuid\n",
2021 pDrvIns->pReg->szName, pDrvIns->iInstance, pThis->pszDevice, pThis->pszDeviceOpen, pThis->cMilliesPoller,
2022 pThis->fReadOnlyConfig, pThis->fLocked, pThis->fBiosVisible, &pThis->Uuid));
2023
2024 /*
2025 * Check that there are no drivers below us.
2026 */
2027 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
2028 ("Configuration error: Not possible to attach anything to this driver!\n"),
2029 VERR_PDM_DRVINS_NO_ATTACH);
2030
2031 /*
2032 * Register saved state.
2033 */
2034 int rc = PDMDrvHlpSSMRegisterLoadDone(pDrvIns, drvHostBaseLoadDone);
2035 if (RT_FAILURE(rc))
2036 return rc;
2037
2038 /*
2039 * Verify type.
2040 */
2041#ifdef RT_OS_WINDOWS
2042 UINT uDriveType = GetDriveType(pThis->pszDevice);
2043 switch (pThis->enmType)
2044 {
2045 case PDMBLOCKTYPE_FLOPPY_360:
2046 case PDMBLOCKTYPE_FLOPPY_720:
2047 case PDMBLOCKTYPE_FLOPPY_1_20:
2048 case PDMBLOCKTYPE_FLOPPY_1_44:
2049 case PDMBLOCKTYPE_FLOPPY_2_88:
2050 if (uDriveType != DRIVE_REMOVABLE)
2051 {
2052 AssertMsgFailed(("Configuration error: '%s' is not a floppy (type=%d)\n",
2053 pThis->pszDevice, uDriveType));
2054 return VERR_INVALID_PARAMETER;
2055 }
2056 break;
2057 case PDMBLOCKTYPE_CDROM:
2058 case PDMBLOCKTYPE_DVD:
2059 if (uDriveType != DRIVE_CDROM)
2060 {
2061 AssertMsgFailed(("Configuration error: '%s' is not a cdrom (type=%d)\n",
2062 pThis->pszDevice, uDriveType));
2063 return VERR_INVALID_PARAMETER;
2064 }
2065 break;
2066 case PDMBLOCKTYPE_HARD_DISK:
2067 default:
2068 AssertMsgFailed(("enmType=%d\n", pThis->enmType));
2069 return VERR_INVALID_PARAMETER;
2070 }
2071#endif
2072
2073 /*
2074 * Open the device.
2075 */
2076#if defined(RT_OS_DARWIN)
2077 rc = drvHostBaseOpen(pThis, NULL, pThis->fReadOnlyConfig);
2078#else
2079 rc = drvHostBaseReopen(pThis);
2080#endif
2081 if (RT_FAILURE(rc))
2082 {
2083 char *pszDevice = pThis->pszDevice;
2084#ifndef RT_OS_DARWIN
2085 char szPathReal[256];
2086 if ( RTPathExists(pszDevice)
2087 && RT_SUCCESS(RTPathReal(pszDevice, szPathReal, sizeof(szPathReal))))
2088 pszDevice = szPathReal;
2089 pThis->hFileDevice = NIL_RTFILE;
2090#endif
2091#ifdef RT_OS_SOLARIS
2092 pThis->hFileRawDevice = NIL_RTFILE;
2093#endif
2094
2095 /*
2096 * Disable CD/DVD passthrough in case it was enabled. Would cause
2097 * weird failures later when the guest issues commands. These would
2098 * all fail because of the invalid file handle. So use the normal
2099 * virtual CD/DVD code, which deals more gracefully with unavailable
2100 * "media" - actually a complete drive in this case.
2101 */
2102 pThis->IBlock.pfnSendCmd = NULL;
2103 AssertMsgFailed(("Could not open host device %s, rc=%Rrc\n", pszDevice, rc));
2104 switch (rc)
2105 {
2106 case VERR_ACCESS_DENIED:
2107 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
2108#ifdef RT_OS_LINUX
2109 N_("Cannot open host device '%s' for %s access. Check the permissions "
2110 "of that device ('/bin/ls -l %s'): Most probably you need to be member "
2111 "of the device group. Make sure that you logout/login after changing "
2112 "the group settings of the current user"),
2113#else
2114 N_("Cannot open host device '%s' for %s access. Check the permissions "
2115 "of that device"),
2116#endif
2117 pszDevice, pThis->fReadOnlyConfig ? "readonly" : "read/write",
2118 pszDevice);
2119 default:
2120 {
2121 if (pThis->fAttachFailError)
2122 return rc;
2123 int erc = PDMDrvHlpVMSetRuntimeError(pDrvIns, 0 /*fFlags*/,
2124 "DrvHost_MOUNTFAIL",
2125 N_("Cannot attach to host device '%s'"), pszDevice);
2126 AssertRC(erc);
2127 src = rc;
2128 }
2129 }
2130 }
2131#ifdef RT_OS_WINDOWS
2132 if (RT_SUCCESS(src))
2133 DRVHostBaseMediaPresent(pThis);
2134#endif
2135
2136 /*
2137 * Lock the drive if that's required by the configuration.
2138 */
2139 if (pThis->fLocked)
2140 {
2141 if (pThis->pfnDoLock)
2142 rc = pThis->pfnDoLock(pThis, true);
2143 if (RT_FAILURE(rc))
2144 {
2145 AssertMsgFailed(("Failed to lock the dvd drive. rc=%Rrc\n", rc));
2146 return rc;
2147 }
2148 }
2149
2150#ifndef RT_OS_WINDOWS
2151 if (RT_SUCCESS(src))
2152 {
2153 /*
2154 * Create the event semaphore which the poller thread will wait on.
2155 */
2156 rc = RTSemEventCreate(&pThis->EventPoller);
2157 if (RT_FAILURE(rc))
2158 return rc;
2159 }
2160#endif
2161
2162 /*
2163 * Initialize the critical section used for serializing the access to the media.
2164 */
2165 rc = RTCritSectInit(&pThis->CritSect);
2166 if (RT_FAILURE(rc))
2167 return rc;
2168
2169 if (RT_SUCCESS(src))
2170 {
2171 /*
2172 * Start the thread which will poll for the media.
2173 */
2174 rc = RTThreadCreate(&pThis->ThreadPoller, drvHostBaseMediaThread, pThis, 0,
2175 RTTHREADTYPE_INFREQUENT_POLLER, RTTHREADFLAGS_WAITABLE, "DVDMEDIA");
2176 if (RT_FAILURE(rc))
2177 {
2178 AssertMsgFailed(("Failed to create poller thread. rc=%Rrc\n", rc));
2179 return rc;
2180 }
2181
2182 /*
2183 * Wait for the thread to start up (!w32:) and do one detection loop.
2184 */
2185 rc = RTThreadUserWait(pThis->ThreadPoller, 10000);
2186 AssertRC(rc);
2187#ifdef RT_OS_WINDOWS
2188 if (!pThis->hwndDeviceChange)
2189 return VERR_GENERAL_FAILURE;
2190#endif
2191 }
2192
2193 if (RT_FAILURE(src))
2194 return src;
2195 return rc;
2196}
2197
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