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