VirtualBox

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

Last change on this file since 5184 was 5184, checked in by vboxsync, 17 years ago

Solaris.

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