VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/DrvHostBase-darwin.cpp@ 76396

Last change on this file since 76396 was 76396, checked in by vboxsync, 6 years ago

Make scm happy.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.0 KB
Line 
1/* $Id: DrvHostBase-darwin.cpp 76396 2018-12-23 04:28:07Z vboxsync $ */
2/** @file
3 * DrvHostBase - Host base drive access driver, OS X specifics.
4 */
5
6/*
7 * Copyright (C) 2006-2017 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#include <mach/mach.h>
24#include <Carbon/Carbon.h>
25#include <IOKit/IOKitLib.h>
26#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
27#include <IOKit/scsi/SCSITaskLib.h>
28#include <IOKit/scsi/SCSICommandOperationCodes.h>
29#include <IOKit/IOBSD.h>
30#include <DiskArbitration/DiskArbitration.h>
31#include <mach/mach_error.h>
32#include <VBox/scsi.h>
33#include <iprt/string.h>
34
35
36/**
37 * Host backend specific data.
38 */
39typedef struct DRVHOSTBASEOS
40{
41 /** The master port. */
42 mach_port_t MasterPort;
43 /** The MMC-2 Device Interface. (This is only used to get the scsi task interface.) */
44 MMCDeviceInterface **ppMMCDI;
45 /** The SCSI Task Device Interface. */
46 SCSITaskDeviceInterface **ppScsiTaskDI;
47 /** The block size. Set when querying the media size. */
48 uint32_t cbBlock;
49 /** The disk arbitration session reference. NULL if we didn't have to claim & unmount the device. */
50 DASessionRef pDASession;
51 /** The disk arbitration disk reference. NULL if we didn't have to claim & unmount the device. */
52 DADiskRef pDADisk;
53 /** The number of errors that could go into the release log. (flood gate) */
54 uint32_t cLogRelErrors;
55} DRVHOSTBASEOS;
56/** Pointer to the host backend specific data. */
57typedef DRVHOSTBASEOS *PDRVHOSBASEOS;
58AssertCompile(sizeof(DRVHOSTBASEOS) <= 64);
59
60#define DRVHOSTBASE_OS_INT_DECLARED
61#include "DrvHostBase.h"
62
63
64/*********************************************************************************************************************************
65* Defined Constants And Macros *
66*********************************************************************************************************************************/
67/** Maximum buffer size we support, check whether darwin has some real upper limit. */
68#define DARWIN_SCSI_MAX_BUFFER_SIZE (100 * _1K)
69
70/** The runloop input source name for the disk arbitration events. */
71#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. */
72
73
74
75/**
76 * Gets the BSD Name (/dev/disc[0-9]+) for the service.
77 *
78 * This is done by recursing down the I/O registry until we hit upon an entry
79 * with a BSD Name. Usually we find it two levels down. (Further down under
80 * the IOCDPartitionScheme, the volume (slices) BSD Name is found. We don't
81 * seem to have to go this far fortunately.)
82 *
83 * @return VINF_SUCCESS if found, VERR_FILE_NOT_FOUND otherwise.
84 * @param Entry The current I/O registry entry reference.
85 * @param pszName Where to store the name. 128 bytes.
86 * @param cRecursions Number of recursions. This is used as an precaution
87 * just to limit the depth and avoid blowing the stack
88 * should we hit a bug or something.
89 */
90static int drvHostBaseGetBSDName(io_registry_entry_t Entry, char *pszName, unsigned cRecursions)
91{
92 int rc = VERR_FILE_NOT_FOUND;
93 io_iterator_t Children = 0;
94 kern_return_t krc = IORegistryEntryGetChildIterator(Entry, kIOServicePlane, &Children);
95 if (krc == KERN_SUCCESS)
96 {
97 io_object_t Child;
98 while ( rc == VERR_FILE_NOT_FOUND
99 && (Child = IOIteratorNext(Children)) != 0)
100 {
101 CFStringRef BSDNameStrRef = (CFStringRef)IORegistryEntryCreateCFProperty(Child, CFSTR(kIOBSDNameKey), kCFAllocatorDefault, 0);
102 if (BSDNameStrRef)
103 {
104 if (CFStringGetCString(BSDNameStrRef, pszName, 128, kCFStringEncodingUTF8))
105 rc = VINF_SUCCESS;
106 else
107 AssertFailed();
108 CFRelease(BSDNameStrRef);
109 }
110 if (rc == VERR_FILE_NOT_FOUND && cRecursions < 10)
111 rc = drvHostBaseGetBSDName(Child, pszName, cRecursions + 1);
112 IOObjectRelease(Child);
113 }
114 IOObjectRelease(Children);
115 }
116 return rc;
117}
118
119
120/**
121 * Callback notifying us that the async DADiskClaim()/DADiskUnmount call has completed.
122 *
123 * @param DiskRef The disk that was attempted claimed / unmounted.
124 * @param DissenterRef NULL on success, contains details on failure.
125 * @param pvContext Pointer to the return code variable.
126 */
127static void drvHostBaseDADoneCallback(DADiskRef DiskRef, DADissenterRef DissenterRef, void *pvContext)
128{
129 RT_NOREF(DiskRef);
130 int *prc = (int *)pvContext;
131 if (!DissenterRef)
132 *prc = 0;
133 else
134 *prc = DADissenterGetStatus(DissenterRef) ? DADissenterGetStatus(DissenterRef) : -1;
135 CFRunLoopStop(CFRunLoopGetCurrent());
136}
137
138
139/**
140 * Obtain exclusive access to the DVD device, umount it if necessary.
141 *
142 * @return VBox status code.
143 * @param pThis The driver instance.
144 * @param DVDService The DVD service object.
145 */
146static int drvHostBaseObtainExclusiveAccess(PDRVHOSTBASE pThis, io_object_t DVDService)
147{
148 PPDMDRVINS pDrvIns = pThis->pDrvIns; NOREF(pDrvIns);
149
150 for (unsigned iTry = 0;; iTry++)
151 {
152 IOReturn irc = (*pThis->Os.ppScsiTaskDI)->ObtainExclusiveAccess(pThis->Os.ppScsiTaskDI);
153 if (irc == kIOReturnSuccess)
154 {
155 /*
156 * This is a bit weird, but if we unmounted the DVD drive we also need to
157 * unlock it afterwards or the guest won't be able to eject it later on.
158 */
159 if (pThis->Os.pDADisk)
160 {
161 uint8_t abCmd[16] =
162 {
163 SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL, 0, 0, 0, false, 0,
164 0,0,0,0,0,0,0,0,0,0
165 };
166 drvHostBaseScsiCmdOs(pThis, abCmd, 6, PDMMEDIATXDIR_NONE, NULL, NULL, NULL, 0, 0);
167 }
168 return VINF_SUCCESS;
169 }
170 if (irc == kIOReturnExclusiveAccess)
171 return VERR_SHARING_VIOLATION; /* already used exclusivly. */
172 if (irc != kIOReturnBusy)
173 return VERR_GENERAL_FAILURE; /* not mounted */
174
175 /*
176 * Attempt to the unmount all volumes of the device.
177 * It seems we can can do this all in one go without having to enumerate the
178 * volumes (sessions) and deal with them one by one. This is very fortuitous
179 * as the disk arbitration API is a bit cumbersome to deal with.
180 */
181 if (iTry > 2)
182 return VERR_DRIVE_LOCKED;
183 char szName[128];
184 int rc = drvHostBaseGetBSDName(DVDService, &szName[0], 0);
185 if (RT_SUCCESS(rc))
186 {
187 pThis->Os.pDASession = DASessionCreate(kCFAllocatorDefault);
188 if (pThis->Os.pDASession)
189 {
190 DASessionScheduleWithRunLoop(pThis->Os.pDASession, CFRunLoopGetCurrent(), MY_RUN_LOOP_MODE);
191 pThis->Os.pDADisk = DADiskCreateFromBSDName(kCFAllocatorDefault, pThis->Os.pDASession, szName);
192 if (pThis->Os.pDADisk)
193 {
194 /*
195 * Try claim the device.
196 */
197 Log(("%s-%d: calling DADiskClaim on '%s'.\n", pDrvIns->pReg->szName, pDrvIns->iInstance, szName));
198 int rcDA = -2;
199 DADiskClaim(pThis->Os.pDADisk, kDADiskClaimOptionDefault, NULL, NULL, drvHostBaseDADoneCallback, &rcDA);
200 SInt32 rc32 = CFRunLoopRunInMode(MY_RUN_LOOP_MODE, 120.0, FALSE);
201 AssertMsg(rc32 == kCFRunLoopRunStopped, ("rc32=%RI32 (%RX32)\n", rc32, rc32));
202 if ( rc32 == kCFRunLoopRunStopped
203 && !rcDA)
204 {
205 /*
206 * Try unmount the device.
207 */
208 Log(("%s-%d: calling DADiskUnmount on '%s'.\n", pDrvIns->pReg->szName, pDrvIns->iInstance, szName));
209 rcDA = -2;
210 DADiskUnmount(pThis->Os.pDADisk, kDADiskUnmountOptionWhole, drvHostBaseDADoneCallback, &rcDA);
211 rc32 = CFRunLoopRunInMode(MY_RUN_LOOP_MODE, 120.0, FALSE);
212 AssertMsg(rc32 == kCFRunLoopRunStopped, ("rc32=%RI32 (%RX32)\n", rc32, rc32));
213 if ( rc32 == kCFRunLoopRunStopped
214 && !rcDA)
215 {
216 iTry = 99;
217 DASessionUnscheduleFromRunLoop(pThis->Os.pDASession, CFRunLoopGetCurrent(), MY_RUN_LOOP_MODE);
218 Log(("%s-%d: unmount succeed - retrying.\n", pDrvIns->pReg->szName, pDrvIns->iInstance));
219 continue;
220 }
221 Log(("%s-%d: umount => rc32=%d & rcDA=%#x\n", pDrvIns->pReg->szName, pDrvIns->iInstance, rc32, rcDA));
222
223 /* failed - cleanup */
224 DADiskUnclaim(pThis->Os.pDADisk);
225 }
226 else
227 Log(("%s-%d: claim => rc32=%d & rcDA=%#x\n", pDrvIns->pReg->szName, pDrvIns->iInstance, rc32, rcDA));
228
229 CFRelease(pThis->Os.pDADisk);
230 pThis->Os.pDADisk = NULL;
231 }
232 else
233 Log(("%s-%d: failed to open disk '%s'!\n", pDrvIns->pReg->szName, pDrvIns->iInstance, szName));
234
235 DASessionUnscheduleFromRunLoop(pThis->Os.pDASession, CFRunLoopGetCurrent(), MY_RUN_LOOP_MODE);
236 CFRelease(pThis->Os.pDASession);
237 pThis->Os.pDASession = NULL;
238 }
239 else
240 Log(("%s-%d: failed to create DA session!\n", pDrvIns->pReg->szName, pDrvIns->iInstance));
241 }
242 RTThreadSleep(10);
243 }
244}
245
246DECLHIDDEN(int) drvHostBaseScsiCmdOs(PDRVHOSTBASE pThis, const uint8_t *pbCmd, size_t cbCmd, PDMMEDIATXDIR enmTxDir,
247 void *pvBuf, uint32_t *pcbBuf, uint8_t *pbSense, size_t cbSense, uint32_t cTimeoutMillies)
248{
249 /*
250 * Minimal input validation.
251 */
252 Assert(enmTxDir == PDMMEDIATXDIR_NONE || enmTxDir == PDMMEDIATXDIR_FROM_DEVICE || enmTxDir == PDMMEDIATXDIR_TO_DEVICE);
253 Assert(!pvBuf || pcbBuf);
254 Assert(pvBuf || enmTxDir == PDMMEDIATXDIR_NONE);
255 Assert(pbSense || !cbSense);
256 AssertPtr(pbCmd);
257 Assert(cbCmd <= 16 && cbCmd >= 1);
258 const uint32_t cbBuf = pcbBuf ? *pcbBuf : 0;
259 if (pcbBuf)
260 *pcbBuf = 0;
261
262 Assert(pThis->Os.ppScsiTaskDI);
263
264 int rc = VERR_GENERAL_FAILURE;
265 SCSITaskInterface **ppScsiTaskI = (*pThis->Os.ppScsiTaskDI)->CreateSCSITask(pThis->Os.ppScsiTaskDI);
266 if (!ppScsiTaskI)
267 return VERR_NO_MEMORY;
268 do
269 {
270 /* Setup the scsi command. */
271 SCSICommandDescriptorBlock cdb = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
272 memcpy(&cdb[0], pbCmd, cbCmd);
273 IOReturn irc = (*ppScsiTaskI)->SetCommandDescriptorBlock(ppScsiTaskI, cdb, cbCmd);
274 AssertBreak(irc == kIOReturnSuccess);
275
276 /* Setup the buffer. */
277 if (enmTxDir == PDMMEDIATXDIR_NONE)
278 irc = (*ppScsiTaskI)->SetScatterGatherEntries(ppScsiTaskI, NULL, 0, 0, kSCSIDataTransfer_NoDataTransfer);
279 else
280 {
281 IOVirtualRange Range = { (IOVirtualAddress)pvBuf, cbBuf };
282 irc = (*ppScsiTaskI)->SetScatterGatherEntries(ppScsiTaskI, &Range, 1, cbBuf,
283 enmTxDir == PDMMEDIATXDIR_FROM_DEVICE
284 ? kSCSIDataTransfer_FromTargetToInitiator
285 : kSCSIDataTransfer_FromInitiatorToTarget);
286 }
287 AssertBreak(irc == kIOReturnSuccess);
288
289 /* Set the timeout. */
290 irc = (*ppScsiTaskI)->SetTimeoutDuration(ppScsiTaskI, cTimeoutMillies ? cTimeoutMillies : 30000 /*ms*/);
291 AssertBreak(irc == kIOReturnSuccess);
292
293 /* Execute the command and get the response. */
294 SCSI_Sense_Data SenseData = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
295 SCSIServiceResponse ServiceResponse = kSCSIServiceResponse_Request_In_Process;
296 SCSITaskStatus TaskStatus = kSCSITaskStatus_GOOD;
297 UInt64 cbReturned = 0;
298 irc = (*ppScsiTaskI)->ExecuteTaskSync(ppScsiTaskI, &SenseData, &TaskStatus, &cbReturned);
299 AssertBreak(irc == kIOReturnSuccess);
300 if (pcbBuf)
301 *pcbBuf = (int32_t)cbReturned;
302
303 irc = (*ppScsiTaskI)->GetSCSIServiceResponse(ppScsiTaskI, &ServiceResponse);
304 AssertBreak(irc == kIOReturnSuccess);
305 AssertBreak(ServiceResponse == kSCSIServiceResponse_TASK_COMPLETE);
306
307 if (TaskStatus == kSCSITaskStatus_GOOD)
308 rc = VINF_SUCCESS;
309 else if ( TaskStatus == kSCSITaskStatus_CHECK_CONDITION
310 && pbSense)
311 {
312 memset(pbSense, 0, cbSense); /* lazy */
313 memcpy(pbSense, &SenseData, RT_MIN(sizeof(SenseData), cbSense));
314 rc = VERR_UNRESOLVED_ERROR;
315 }
316 /** @todo convert sense codes when caller doesn't wish to do this himself. */
317 /*else if ( TaskStatus == kSCSITaskStatus_CHECK_CONDITION
318 && SenseData.ADDITIONAL_SENSE_CODE == 0x3A)
319 rc = VERR_MEDIA_NOT_PRESENT; */
320 else
321 {
322 rc = enmTxDir == PDMMEDIATXDIR_NONE
323 ? VERR_DEV_IO_ERROR
324 : enmTxDir == PDMMEDIATXDIR_FROM_DEVICE
325 ? VERR_READ_ERROR
326 : VERR_WRITE_ERROR;
327 if (pThis->Os.cLogRelErrors++ < 10)
328 LogRel(("DVD scsi error: cmd={%.*Rhxs} TaskStatus=%#x key=%#x ASC=%#x ASCQ=%#x (%Rrc)\n",
329 cbCmd, pbCmd, TaskStatus, SenseData.SENSE_KEY, SenseData.ADDITIONAL_SENSE_CODE,
330 SenseData.ADDITIONAL_SENSE_CODE_QUALIFIER, rc));
331 }
332 } while (0);
333
334 (*ppScsiTaskI)->Release(ppScsiTaskI);
335
336 return rc;
337}
338
339
340DECLHIDDEN(size_t) drvHostBaseScsiCmdGetBufLimitOs(PDRVHOSTBASE pThis)
341{
342 RT_NOREF(pThis);
343
344 return DARWIN_SCSI_MAX_BUFFER_SIZE;
345}
346
347
348DECLHIDDEN(int) drvHostBaseGetMediaSizeOs(PDRVHOSTBASE pThis, uint64_t *pcb)
349{
350 /*
351 * Try a READ_CAPACITY command...
352 */
353 struct
354 {
355 uint32_t cBlocks;
356 uint32_t cbBlock;
357 } Buf = {0, 0};
358 uint32_t cbBuf = sizeof(Buf);
359 uint8_t abCmd[16] =
360 {
361 SCSI_READ_CAPACITY, 0, 0, 0, 0, 0, 0,
362 0,0,0,0,0,0,0,0,0
363 };
364 int rc = drvHostBaseScsiCmdOs(pThis, abCmd, 6, PDMMEDIATXDIR_FROM_DEVICE, &Buf, &cbBuf, NULL, 0, 0);
365 if (RT_SUCCESS(rc))
366 {
367 Assert(cbBuf == sizeof(Buf));
368 Buf.cBlocks = RT_BE2H_U32(Buf.cBlocks);
369 Buf.cbBlock = RT_BE2H_U32(Buf.cbBlock);
370 //if (Buf.cbBlock > 2048) /* everyone else is doing this... check if it needed/right.*/
371 // Buf.cbBlock = 2048;
372 pThis->Os.cbBlock = Buf.cbBlock;
373
374 *pcb = (uint64_t)Buf.cBlocks * Buf.cbBlock;
375 }
376 return rc;
377}
378
379
380DECLHIDDEN(int) drvHostBaseReadOs(PDRVHOSTBASE pThis, uint64_t off, void *pvBuf, size_t cbRead)
381{
382 int rc = VINF_SUCCESS;
383
384 if ( pThis->Os.ppScsiTaskDI
385 && pThis->Os.cbBlock)
386 {
387 /*
388 * Issue a READ(12) request.
389 */
390 do
391 {
392 const uint32_t LBA = off / pThis->Os.cbBlock;
393 AssertReturn(!(off % pThis->Os.cbBlock), VERR_INVALID_PARAMETER);
394 uint32_t cbRead32 = cbRead > SCSI_MAX_BUFFER_SIZE
395 ? SCSI_MAX_BUFFER_SIZE
396 : (uint32_t)cbRead;
397 const uint32_t cBlocks = cbRead32 / pThis->Os.cbBlock;
398 AssertReturn(!(cbRead % pThis->Os.cbBlock), VERR_INVALID_PARAMETER);
399 uint8_t abCmd[16] =
400 {
401 SCSI_READ_12, 0,
402 RT_BYTE4(LBA), RT_BYTE3(LBA), RT_BYTE2(LBA), RT_BYTE1(LBA),
403 RT_BYTE4(cBlocks), RT_BYTE3(cBlocks), RT_BYTE2(cBlocks), RT_BYTE1(cBlocks),
404 0, 0, 0, 0, 0
405 };
406 rc = drvHostBaseScsiCmdOs(pThis, abCmd, 12, PDMMEDIATXDIR_FROM_DEVICE, pvBuf, &cbRead32, NULL, 0, 0);
407
408 off += cbRead32;
409 cbRead -= cbRead32;
410 pvBuf = (uint8_t *)pvBuf + cbRead32;
411 } while ((cbRead > 0) && RT_SUCCESS(rc));
412 }
413 else
414 rc = VERR_MEDIA_NOT_PRESENT;
415
416 return rc;
417}
418
419
420DECLHIDDEN(int) drvHostBaseWriteOs(PDRVHOSTBASE pThis, uint64_t off, const void *pvBuf, size_t cbWrite)
421{
422 RT_NOREF4(pThis, off, pvBuf, cbWrite);
423 return VERR_WRITE_PROTECT;
424}
425
426
427DECLHIDDEN(int) drvHostBaseFlushOs(PDRVHOSTBASE pThis)
428{
429 RT_NOREF1(pThis);
430 return VINF_SUCCESS;
431}
432
433
434DECLHIDDEN(int) drvHostBaseDoLockOs(PDRVHOSTBASE pThis, bool fLock)
435{
436 uint8_t abCmd[16] =
437 {
438 SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL, 0, 0, 0, fLock, 0,
439 0,0,0,0,0,0,0,0,0,0
440 };
441 return drvHostBaseScsiCmdOs(pThis, abCmd, 6, PDMMEDIATXDIR_NONE, NULL, NULL, NULL, 0, 0);
442}
443
444
445DECLHIDDEN(int) drvHostBaseEjectOs(PDRVHOSTBASE pThis)
446{
447 uint8_t abCmd[16] =
448 {
449 SCSI_START_STOP_UNIT, 0, 0, 0, 2 /*eject+stop*/, 0,
450 0,0,0,0,0,0,0,0,0,0
451 };
452 return drvHostBaseScsiCmdOs(pThis, abCmd, 6, PDMMEDIATXDIR_NONE, NULL, NULL, NULL, 0, 0);
453}
454
455
456DECLHIDDEN(int) drvHostBaseQueryMediaStatusOs(PDRVHOSTBASE pThis, bool *pfMediaChanged, bool *pfMediaPresent)
457{
458 AssertReturn(pThis->Os.ppScsiTaskDI, VERR_INTERNAL_ERROR);
459
460 /*
461 * Issue a TEST UNIT READY request.
462 */
463 *pfMediaChanged = false;
464 *pfMediaPresent = false;
465 uint8_t abCmd[16] = { SCSI_TEST_UNIT_READY, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
466 uint8_t abSense[32];
467 int rc = drvHostBaseScsiCmdOs(pThis, abCmd, 6, PDMMEDIATXDIR_NONE, NULL, NULL, abSense, sizeof(abSense), 0);
468 if (RT_SUCCESS(rc))
469 *pfMediaPresent = true;
470 else if ( rc == VERR_UNRESOLVED_ERROR
471 && abSense[2] == 6 /* unit attention */
472 && ( (abSense[12] == 0x29 && abSense[13] < 5 /* reset */)
473 || (abSense[12] == 0x2a && abSense[13] == 0 /* parameters changed */) //???
474 || (abSense[12] == 0x3f && abSense[13] == 0 /* target operating conditions have changed */) //???
475 || (abSense[12] == 0x3f && abSense[13] == 2 /* changed operating definition */) //???
476 || (abSense[12] == 0x3f && abSense[13] == 3 /* inquiry parameters changed */)
477 || (abSense[12] == 0x3f && abSense[13] == 5 /* device identifier changed */)
478 )
479 )
480 {
481 *pfMediaPresent = false;
482 *pfMediaChanged = true;
483 rc = VINF_SUCCESS;
484 /** @todo check this media change stuff on Darwin. */
485 }
486
487 return rc;
488}
489
490
491DECLHIDDEN(void) drvHostBaseInitOs(PDRVHOSTBASE pThis)
492{
493 pThis->Os.MasterPort = IO_OBJECT_NULL;
494 pThis->Os.ppMMCDI = NULL;
495 pThis->Os.ppScsiTaskDI = NULL;
496 pThis->Os.cbBlock = 0;
497 pThis->Os.pDADisk = NULL;
498 pThis->Os.pDASession = NULL;
499}
500
501
502DECLHIDDEN(int) drvHostBaseOpenOs(PDRVHOSTBASE pThis, bool fReadOnly)
503{
504 RT_NOREF(fReadOnly);
505
506 /* Darwin is kind of special... */
507 Assert(!pThis->Os.cbBlock);
508 Assert(pThis->Os.MasterPort == IO_OBJECT_NULL);
509 Assert(!pThis->Os.ppMMCDI);
510 Assert(!pThis->Os.ppScsiTaskDI);
511
512 /*
513 * Open the master port on the first invocation.
514 */
515 kern_return_t krc = IOMasterPort(MACH_PORT_NULL, &pThis->Os.MasterPort);
516 AssertReturn(krc == KERN_SUCCESS, VERR_GENERAL_FAILURE);
517
518 /*
519 * Create a matching dictionary for searching for CD, DVD and BlueRay services in the IOKit.
520 *
521 * The idea is to find all the devices which are of class IOCDBlockStorageDevice.
522 * CD devices are represented by IOCDBlockStorageDevice class itself, while DVD and BlueRay ones
523 * have it as a parent class.
524 */
525 CFMutableDictionaryRef RefMatchingDict = IOServiceMatching("IOCDBlockStorageDevice");
526 AssertReturn(RefMatchingDict, VERR_NOT_FOUND);
527
528 /*
529 * do the search and get a collection of keyboards.
530 */
531 io_iterator_t DVDServices = IO_OBJECT_NULL;
532 IOReturn irc = IOServiceGetMatchingServices(pThis->Os.MasterPort, RefMatchingDict, &DVDServices);
533 AssertMsgReturn(irc == kIOReturnSuccess, ("irc=%d\n", irc), VERR_NOT_FOUND);
534 RefMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
535
536 /*
537 * Enumerate the matching drives (services).
538 * (This enumeration must be identical to the one performed in Main/src-server/darwin/iokit.cpp.)
539 */
540 int rc = VERR_FILE_NOT_FOUND;
541 unsigned i = 0;
542 io_object_t DVDService;
543 while ((DVDService = IOIteratorNext(DVDServices)) != 0)
544 {
545 /*
546 * Get the properties we use to identify the DVD drive.
547 *
548 * While there is a (weird 12 byte) GUID, it isn't persistent
549 * across boots. So, we have to use a combination of the
550 * vendor name and product name properties with an optional
551 * sequence number for identification.
552 */
553 CFMutableDictionaryRef PropsRef = 0;
554 krc = IORegistryEntryCreateCFProperties(DVDService, &PropsRef, kCFAllocatorDefault, kNilOptions);
555 if (krc == KERN_SUCCESS)
556 {
557 /* Get the Device Characteristics dictionary. */
558 CFDictionaryRef DevCharRef = (CFDictionaryRef)CFDictionaryGetValue(PropsRef, CFSTR(kIOPropertyDeviceCharacteristicsKey));
559 if (DevCharRef)
560 {
561 /* The vendor name. */
562 char szVendor[128];
563 char *pszVendor = &szVendor[0];
564 CFTypeRef ValueRef = CFDictionaryGetValue(DevCharRef, CFSTR(kIOPropertyVendorNameKey));
565 if ( ValueRef
566 && CFGetTypeID(ValueRef) == CFStringGetTypeID()
567 && CFStringGetCString((CFStringRef)ValueRef, szVendor, sizeof(szVendor), kCFStringEncodingUTF8))
568 pszVendor = RTStrStrip(szVendor);
569 else
570 *pszVendor = '\0';
571
572 /* The product name. */
573 char szProduct[128];
574 char *pszProduct = &szProduct[0];
575 ValueRef = CFDictionaryGetValue(DevCharRef, CFSTR(kIOPropertyProductNameKey));
576 if ( ValueRef
577 && CFGetTypeID(ValueRef) == CFStringGetTypeID()
578 && CFStringGetCString((CFStringRef)ValueRef, szProduct, sizeof(szProduct), kCFStringEncodingUTF8))
579 pszProduct = RTStrStrip(szProduct);
580 else
581 *pszProduct = '\0';
582
583 /* Construct the two names and compare thwm with the one we're searching for. */
584 char szName1[256 + 32];
585 char szName2[256 + 32];
586 if (*pszVendor || *pszProduct)
587 {
588 if (*pszVendor && *pszProduct)
589 {
590 RTStrPrintf(szName1, sizeof(szName1), "%s %s", pszVendor, pszProduct);
591 RTStrPrintf(szName2, sizeof(szName2), "%s %s (#%u)", pszVendor, pszProduct, i);
592 }
593 else
594 {
595 strcpy(szName1, *pszVendor ? pszVendor : pszProduct);
596 RTStrPrintf(szName2, sizeof(szName2), "%s (#%u)", *pszVendor ? pszVendor : pszProduct, i);
597 }
598 }
599 else
600 {
601 RTStrPrintf(szName1, sizeof(szName1), "(#%u)", i);
602 strcpy(szName2, szName1);
603 }
604
605 if ( !strcmp(szName1, pThis->pszDevice)
606 || !strcmp(szName2, pThis->pszDevice))
607 {
608 /*
609 * Found it! Now, get the client interface and stuff.
610 * Note that we could also query kIOSCSITaskDeviceUserClientTypeID here if the
611 * MMC client plugin is missing. For now we assume this won't be necessary.
612 */
613 SInt32 Score = 0;
614 IOCFPlugInInterface **ppPlugInInterface = NULL;
615 krc = IOCreatePlugInInterfaceForService(DVDService, kIOMMCDeviceUserClientTypeID, kIOCFPlugInInterfaceID,
616 &ppPlugInInterface, &Score);
617 if (krc == KERN_SUCCESS)
618 {
619 HRESULT hrc = (*ppPlugInInterface)->QueryInterface(ppPlugInInterface,
620 CFUUIDGetUUIDBytes(kIOMMCDeviceInterfaceID),
621 (LPVOID *)&pThis->Os.ppMMCDI);
622 (*ppPlugInInterface)->Release(ppPlugInInterface);
623 ppPlugInInterface = NULL;
624 if (hrc == S_OK)
625 {
626 pThis->Os.ppScsiTaskDI = (*pThis->Os.ppMMCDI)->GetSCSITaskDeviceInterface(pThis->Os.ppMMCDI);
627 if (pThis->Os.ppScsiTaskDI)
628 rc = VINF_SUCCESS;
629 else
630 {
631 LogRel(("GetSCSITaskDeviceInterface failed on '%s'\n", pThis->pszDevice));
632 rc = VERR_NOT_SUPPORTED;
633 (*pThis->Os.ppMMCDI)->Release(pThis->Os.ppMMCDI);
634 }
635 }
636 else
637 {
638 rc = VERR_GENERAL_FAILURE;//RTErrConvertFromDarwinCOM(krc);
639 pThis->Os.ppMMCDI = NULL;
640 }
641 }
642 else /* Check for kIOSCSITaskDeviceUserClientTypeID? */
643 rc = VERR_GENERAL_FAILURE;//RTErrConvertFromDarwinKern(krc);
644
645 /* Obtain exclusive access to the device so we can send SCSI commands. */
646 if (RT_SUCCESS(rc))
647 rc = drvHostBaseObtainExclusiveAccess(pThis, DVDService);
648
649 /* Cleanup on failure. */
650 if (RT_FAILURE(rc))
651 {
652 if (pThis->Os.ppScsiTaskDI)
653 {
654 (*pThis->Os.ppScsiTaskDI)->Release(pThis->Os.ppScsiTaskDI);
655 pThis->Os.ppScsiTaskDI = NULL;
656 }
657 if (pThis->Os.ppMMCDI)
658 {
659 (*pThis->Os.ppMMCDI)->Release(pThis->Os.ppMMCDI);
660 pThis->Os.ppMMCDI = NULL;
661 }
662 }
663
664 IOObjectRelease(DVDService);
665 break;
666 }
667 }
668 CFRelease(PropsRef);
669 }
670 else
671 AssertMsgFailed(("krc=%#x\n", krc));
672
673 IOObjectRelease(DVDService);
674 i++;
675 }
676
677 IOObjectRelease(DVDServices);
678 return rc;
679
680}
681
682
683DECLHIDDEN(int) drvHostBaseMediaRefreshOs(PDRVHOSTBASE pThis)
684{
685 RT_NOREF(pThis);
686 return VINF_SUCCESS;
687}
688
689
690DECLHIDDEN(bool) drvHostBaseIsMediaPollingRequiredOs(PDRVHOSTBASE pThis)
691{
692 if (pThis->enmType == PDMMEDIATYPE_CDROM || pThis->enmType == PDMMEDIATYPE_DVD)
693 return true;
694
695 AssertMsgFailed(("Darwin supports only CD/DVD host drive access\n"));
696 return false;
697}
698
699
700DECLHIDDEN(void) drvHostBaseDestructOs(PDRVHOSTBASE pThis)
701{
702 /*
703 * Unlock the drive if we've locked it or we're in passthru mode.
704 */
705 if ( ( pThis->fLocked
706 || pThis->IMedia.pfnSendCmd)
707 && pThis->Os.ppScsiTaskDI
708 && pThis->pfnDoLock)
709 {
710 int rc = pThis->pfnDoLock(pThis, false);
711 if (RT_SUCCESS(rc))
712 pThis->fLocked = false;
713 }
714
715 /*
716 * The unclaiming doesn't seem to mean much, the DVD is actually
717 * remounted when we release exclusive access. I'm not quite sure
718 * if I should put the unclaim first or not...
719 *
720 * Anyway, that it's automatically remounted very good news for us,
721 * because that means we don't have to mess with that ourselves. Of
722 * course there is the unlikely scenario that we've succeeded in claiming
723 * and umount the DVD but somehow failed to gain exclusive scsi access...
724 */
725 if (pThis->Os.ppScsiTaskDI)
726 {
727 LogFlow(("%s-%d: releasing exclusive scsi access!\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance));
728 (*pThis->Os.ppScsiTaskDI)->ReleaseExclusiveAccess(pThis->Os.ppScsiTaskDI);
729 (*pThis->Os.ppScsiTaskDI)->Release(pThis->Os.ppScsiTaskDI);
730 pThis->Os.ppScsiTaskDI = NULL;
731 }
732 if (pThis->Os.pDADisk)
733 {
734 LogFlow(("%s-%d: unclaiming the disk!\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance));
735 DADiskUnclaim(pThis->Os.pDADisk);
736 CFRelease(pThis->Os.pDADisk);
737 pThis->Os.pDADisk = NULL;
738 }
739 if (pThis->Os.ppMMCDI)
740 {
741 LogFlow(("%s-%d: releasing the MMC object!\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance));
742 (*pThis->Os.ppMMCDI)->Release(pThis->Os.ppMMCDI);
743 pThis->Os.ppMMCDI = NULL;
744 }
745 if (pThis->Os.MasterPort != IO_OBJECT_NULL)
746 {
747 mach_port_deallocate(mach_task_self(), pThis->Os.MasterPort);
748 pThis->Os.MasterPort = IO_OBJECT_NULL;
749 }
750 if (pThis->Os.pDASession)
751 {
752 LogFlow(("%s-%d: releasing the DA session!\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance));
753 CFRelease(pThis->Os.pDASession);
754 pThis->Os.pDASession = NULL;
755 }
756}
757
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