DldIOShadowFile* DldIOShadowFile::withFileName( __in char* name, __in size_t nameLength )
{
    assert( preemption_enabled() );
    
    //
    // check for the correct file format, the name must be a full path
    //
    if( 0x0 == nameLength || L'\0' != name[ nameLength - 0x1 ] || L'/' != name[0] ){
        
        DBG_PRINT_ERROR(("an invalid file name format\n"));
        return NULL;
    }
    
    
    DldIOShadowFile*  newFile = new DldIOShadowFile();
    assert( newFile );
    if( !newFile )
        return NULL;
    
    
    if( !newFile->initWithFileName( name, nameLength ) ){
        
        newFile->release();
        return NULL;
    }
    
    return newFile;
}
IOReturn DldIPCUserClient::ProcessResponse( __in DldDriverEventData* response )
{
    UInt32 waitBlockIndex = response->Header.waitBlockIndex;
    
    if( DLD_EVENT_RESPONSE != response->Header.type ){
        
        DBG_PRINT_ERROR(("DLD_EVENT_RESPONSE != response->Header.type\n"));
        return kIOReturnBadArgument;
    }
    
    if( waitBlockIndex >= DLD_STATIC_ARRAY_SIZE(DldIPCUserClient::WaitBlocks) ){
        
        DBG_PRINT_ERROR(("response->Head.waitBlockIndex is out of range\n"));
        return kIOReturnBadArgument;
    }
    
    if( DldIPCUserClient::WaitBlocks[waitBlockIndex].eventID != response->Header.id ){
        
        DBG_PRINT_ERROR(("DldIPCUserClient::WaitBlocks[ %d ].eventID != response->Header.id\n", waitBlockIndex));
        return kIOReturnBadArgument;
    }
    
    bool wakeUp = false;
    
    IOSimpleLockLock( DldIPCUserClient::WaitLock );
    { // start of the locked region
        
        if( DldIPCUserClient::WaitBlocks[waitBlockIndex].eventID == response->Header.id ){
            
            assert( 0x0 != DldIPCUserClient::WaitBlocks[waitBlockIndex].inUse );
            DldIPCUserClient::WaitBlocks[waitBlockIndex].completed = true;
            DldIPCUserClient::WaitBlocks[waitBlockIndex].operationCompletionStatus = response->Tail.Response.operationCompletionStatus;
            wakeUp = true;
        }
        
    } // end of the locked region
    IOSimpleLockUnlock( DldIPCUserClient::WaitLock );
    
    if( ! wakeUp )
        return kIOReturnBadArgument;
    
    //
    // there is a slim chance to wake up a just acquired block that was released by timeout, we neglect this case
    //
    wakeup( (event_t)&DldIPCUserClient::WaitBlocks[waitBlockIndex] );
    return kIOReturnSuccess;
}
DldMacPolicy*  DldMacPolicy::createPolicyObject( __in struct mac_policy_conf *mpc, __in void* xd )
{
    assert( preemption_enabled() );
    
    DldMacPolicy* newObject = new DldMacPolicy();
    assert( newObject );
    if( !newObject ){
        DBG_PRINT_ERROR(("new DldMacPolicy() failed\n"));
        return NULL;
    }
    
    if( !newObject->initWithPolicyConf( mpc, xd ) ){
        
        DBG_PRINT_ERROR(("newObject->initWithPolicyConf( mpc, xd ) failed\n"));
        newObject->release();
        return NULL;
    }
    
    return newObject;
}
IOReturn com_VFSFilter0::unregisterUserClient( __in VFSFilter0UserClient* client )
{
    bool   unregistered;
    VFSFilter0UserClient*  currentClient;
    
    currentClient = (VFSFilter0UserClient*)this->userClient;
    assert( currentClient == client );
    if( currentClient != client ){
        
        DBG_PRINT_ERROR(("currentClient != client\n"));
        return kIOReturnError;
    }
    
    this->pendingUnregistration = true;
    
    unregistered = OSCompareAndSwapPtr( (void*)currentClient, NULL, &this->userClient );
    assert( unregistered && NULL == this->userClient );
    if( !unregistered ){
        
        DBG_PRINT_ERROR(("!unregistered\n"));
        
        this->pendingUnregistration = false;
        return kIOReturnError;
    }
    
    do { // wait for any existing client invocations to return
        
        struct timespec ts = { 1, 0 }; // one second
        (void)msleep( &this->clientInvocations,      // wait channel
                      NULL,                          // mutex
                      PUSER,                         // priority
                      "com_VFSFilter0::unregisterUserClient()", // wait message
                      &ts );                         // sleep interval
        
    } while( this->clientInvocations != 0 );
    
    currentClient->release();
    this->pendingUnregistration = false;
    
    return unregistered? kIOReturnSuccess: kIOReturnError;
}
bool DldMacPolicy::initWithPolicyConf( __in struct mac_policy_conf *mpc, __in void* xd )
{
    if( !this->init() ){
        
        DBG_PRINT_ERROR(("this->init() failed\n"));
        return false;
    }
    
    this->mpc = mpc;
    this->xd  = xd;
    
    return true;
}
IOReturn com_VFSFilter0::registerUserClient( __in VFSFilter0UserClient* client )
{
    bool registered;
    
    if( this->pendingUnregistration ){
        
        DBG_PRINT_ERROR(("com_VFSFilter0 : pendingUnregistration\n"));
        return kIOReturnError;
    }
    
    registered = OSCompareAndSwapPtr( NULL, (void*)client, &this->userClient );
    assert( registered );
    if( !registered ){
        
        DBG_PRINT_ERROR(("com_VFSFilter0 : a client was not registered\n"));
        return kIOReturnError;
    }
    
    client->retain();
    
    return registered? kIOReturnSuccess: kIOReturnError;
}
DldAclWithProcsObject* DldAclWithProcsObject::withAcl( __in_opt DldAclWithProcs* procsAcl )
{
    
    DldAclWithProcsObject*  fileProcsAclObj = new DldAclWithProcsObject();
    assert( fileProcsAclObj );
    if( !fileProcsAclObj ){
        
        DBG_PRINT_ERROR(("new DldAclWithProcsObject() failed\n"));
        return NULL;
    }
    
    if( !fileProcsAclObj->init() ){
        
        DBG_PRINT_ERROR(("fileProcsAclObj->init() failed\n"));
        fileProcsAclObj->release();
        return NULL;
    }
    
    if( procsAcl ){
        
        //
        // allocate a memory and copy ACL, a caller must free an ACL provided as a parameter
        //
        
        fileProcsAclObj->acl = (DldAclWithProcs*)IOMalloc( DldAclWithProcsSize( procsAcl ) );
        assert( fileProcsAclObj->acl );
        if( !fileProcsAclObj->acl ){
            
            DBG_PRINT_ERROR(("IOMalloc( DldAclWithProcsSize( procsAcl ) ) failed\n"));
            fileProcsAclObj->release();
            return NULL;
        }
        
        bcopy( procsAcl, fileProcsAclObj->acl, DldAclWithProcsSize( procsAcl ) );
    } // end if( procsAcl )
    
    return fileProcsAclObj;
}
bool
DldIOKitHookDictionaryEntry::init()
{
    if( !super::init() )
        return false;
    
    this->NotifiersDictionary = OSDictionary::withCapacity(2);
    assert( this->NotifiersDictionary );
    if( !this->NotifiersDictionary ) {
        
        DBG_PRINT_ERROR( ( "DldIOKitHookDictionaryEntry(%s)::init() OSDictionary::withCapacity(2) failed\n", this->IOKitClassName->getCStringNoCopy() ) );
        return false;
    }
    
    this->NotifiersDictionaryIterator = OSCollectionIterator::withCollection( this->NotifiersDictionary );
    assert( this->NotifiersDictionaryIterator );
    if( !this->NotifiersDictionaryIterator ){
        
        DBG_PRINT_ERROR( ( "DldIOKitHookDictionaryEntry(%s)::init() OSDictionary::withCapacity(2) failed\n", this->IOKitClassName->getCStringNoCopy() ) );
        return false;
    }
    
    return true;
}
bool
DldIOKitHookDictionaryEntry::setNotifier(
    __in const OSSymbol * type,
    __in IONotifier*    ObjectNotifier
    )
{
    assert( ObjectNotifier );
    assert( !this->NotifiersDictionary->getObject( type ) );
    
    if( !this->NotifiersDictionary->setObject( type, (OSMetaClassBase*)ObjectNotifier ) ){
        
        assert( !"DldIOKitHookDictionaryEntry::setNotifier failed" );
        DBG_PRINT_ERROR( ( "DldIOKitHookDictionaryEntry(%s)::setPublisNotifier( %s ) failed\n",
                            this->IOKitClassName->getCStringNoCopy(), type->getCStringNoCopy() ) );
        
        return false;
    }

    return true;
}
kern_return_t DldMacPolicy::unRegisterMacPolicy()
{
    kern_return_t  err = ENOENT;
    
    assert( preemption_enabled() );
    
    if( this->registered ){
        
        err = mac_policy_unregister( this->handlep );
        this->registered = !( KERN_SUCCESS == err );
        
        assert( KERN_SUCCESS == err );
        if( KERN_SUCCESS != err ){
            
            DBG_PRINT_ERROR(("unRegisterMacPolicy failed with err = %d\n", err));
        }
    }
    
    return err;
}
kern_return_t DldMacPolicy::registerMacPolicy()
{
    kern_return_t  err;
    
    assert( preemption_enabled() );
    assert( !this->registered );
    
    if( this->registered )
        return KERN_SUCCESS;
    
    err = mac_policy_register( this->mpc, &this->handlep, this->xd );
    this->registered = ( KERN_SUCCESS == err );
    
    assert( KERN_SUCCESS == err );
    if( KERN_SUCCESS != err ){
        
        DBG_PRINT_ERROR(("registerMacPolicy failed with err = %d\n", err));
    }
    
    return err;
}
DldIOKitHookDictionaryEntry*
DldIOKitHookDictionaryEntry::withIOKitClassName(
    __in OSString*                    IOKitClassName,
    __in DldHookDictionaryEntryData*  HookData
    )
{
    assert( NULL != IOKitClassName );
    assert( NULL != HookData );
    assert( NULL != HookData->HookFunction );
    assert( NULL != HookData->UnHookFunction );
    
    DldIOKitHookDictionaryEntry*   pEntry = new DldIOKitHookDictionaryEntry();
    assert( NULL != pEntry );
    if( NULL == pEntry )
        return NULL;
    
    //
    // init the IOKit base classes
    //
    if( !pEntry->init() ){
        
        assert( !"DldIOKitHookDictionaryEntry: class init failed" );
        DBG_PRINT_ERROR( ( "DldIOKitHookDictionaryEntry::withIOKitClassName( %s ) pEntry->init() failed\n",
                            IOKitClassName->getCStringNoCopy() ) );
        
        pEntry->release();
        return NULL;
    }
    
    IOKitClassName->retain();
    pEntry->IOKitClassName = IOKitClassName;
    
    pEntry->HookData = *HookData;
    
    return pEntry;
    
}
IOReturn DldIOShadowFile::write( __in void* data, __in off_t length, __in_opt off_t offsetForShadowFile )
{
    /*!
     @function vn_rdwr
     @abstract Read from or write to a file.
     @discussion vn_rdwr() abstracts the details of constructing a uio and picking a vnode operation to allow
     simple in-kernel file I/O.
     @param rw UIO_READ for a read, UIO_WRITE for a write.
     @param vp The vnode on which to perform I/O.
     @param base Start of buffer into which to read or from which to write data.
     @param len Length of buffer.
     @param offset Offset within the file at which to start I/O.
     @param segflg What kind of address "base" is.   See uio_seg definition in sys/uio.h.  UIO_SYSSPACE for kernelspace, UIO_USERSPACE for userspace.
     UIO_USERSPACE32 and UIO_USERSPACE64 are in general preferred, but vn_rdwr will make sure that has the correct address sizes.
     @param ioflg Defined in vnode.h, e.g. IO_NOAUTH, IO_NOCACHE.
     @param cred Credential to pass down to filesystem for authentication.
     @param aresid Destination for amount of requested I/O which was not completed, as with uio_resid().
     @param p Process requesting I/O.
     @return 0 for success; errors from filesystem, and EIO if did not perform all requested I/O and the "aresid" parameter is NULL.
     */
    
    assert( preemption_enabled() );
    assert( length < 0x0FFFFFFF00000000ll );
    
    off_t  offset = offsetForShadowFile;
    
    if( DLD_IGNR_FSIZE == offsetForShadowFile && !this->reserveOffset( length, &offset ) ){
        
        DBG_PRINT_ERROR(("A quota has been exceeded for the %s file, the quota is %llu \n", this->path, this->maxSize ));
        return kIOReturnNoResources;
    }
    
#if defined( DBG )
    static UInt32 gWriteCount = 0x0;
    UInt32 writeCount = OSIncrementAtomic( &gWriteCount ) + 0x1;
#endif//DBG
    
    int   error = 0x0;
    off_t bytesWritten = 0x0;
    
    while( !error && length != bytesWritten ){
        
        unsigned int bytesToWrite;
        
#if defined( DBG )
        //
        // write by 32МВ chunks for each 2nd function invocation, and 1GB for others
        //
        if( (length - bytesWritten) > ((writeCount%2)? 0x40000000ll : 0x2000000ll) )
            bytesToWrite = (writeCount%2)? 0x40000000ll : 0x2000000ll;
        else
            bytesToWrite = (int)(length - bytesWritten);
#else
        //
        // write by 1GB chunks
        //
        if( (length - bytesWritten) > 0x40000000ll )
            bytesToWrite = 0x40000000;
        else
            bytesToWrite = (int)(length - bytesWritten);
#endif//!DBG
        
        
        error = vn_rdwr( UIO_WRITE,
                         this->vnode,
                         (char*)data,
                         bytesToWrite,
                         offset + bytesWritten,
                         UIO_SYSSPACE,
                         IO_NOAUTH | IO_SYNC,//IO_UNIT,
                         kauth_cred_get(),
                         NULL,
                         current_proc() );
        
        if( !error )
            bytesWritten = bytesWritten + bytesToWrite;
        else {
            DBG_PRINT_ERROR(("vn_rdwr( %s, %u ) failed with the 0x%X error\n", this->path, bytesToWrite, error));
        }

        
    }
    
    return error;
}
bool DldIOShadowFile::initWithFileName( __in char* name, __in size_t nameLength )
{
    assert( preemption_enabled() );
    
    
    if( nameLength > (MAXPATHLEN + sizeof( L'\0' )) ){
        
        DBG_PRINT_ERROR(("length > (MAXPATHLEN + sizeof( L'\\0' ))\n"));
        return false;
    }
    
    
    if( !super::init() ){
        
        DBG_PRINT_ERROR(("super::init() failed\n"));
        return false;
    }
    
    this->rwLock = IORWLockAlloc();
    assert( this->rwLock );
    if( !this->rwLock ){
        
        DBG_PRINT_ERROR(("this->rwLock = IORWLockAlloc() failed\n" ));
        return false;
    }
    
    //
    // set a maximum file size to infinity
    //
    this->maxSize = DLD_IGNR_FSIZE;
    
    //
    // set switch size to 512 MB
    //
    this->switchSize = 0x20000000ll;
    
    this->path = (char*)IOMalloc( nameLength );
    assert( this->path );
    if( !this->path ){
        
        DBG_PRINT_ERROR(("this->path = IOMalloc( %u ) failed\n", (int)nameLength ));
        return false;
    }
    
    this->pathLength = nameLength;
    
    memcpy( this->path, name, nameLength );
    assert( L'\0' == this->path[ this->pathLength - 0x1 ] );
    
    //
    // open or create the file
    //
    errno_t  error;
    vfs_context_t   vfs_context;
    
    vfs_context = vfs_context_create( NULL );
    assert( vfs_context );
    if( !vfs_context )
        return false;
    
    //
    // open or create a file, truncate if the file exists
    //
    error = vnode_open( this->path,
                        O_EXLOCK | O_RDWR | O_CREAT | O_TRUNC | O_SYNC, // fmode
                        0644,// cmode
                        0x0,// flags
                        &this->vnode,
                        vfs_context );
    vfs_context_rele( vfs_context );
    if( error ){
        
        DBG_PRINT_ERROR(("vnode_open() failed with the %u error\n", error));
        return false;
    }
    
    //
    // vn_open returns with both a use_count
    // and an io_count on the found vnode
    //
    
    //
    // mark as an internal non shadowed vnode, use the CreateAndAddIOVnodByBSDVnode
    // as the kauth callback might have not been registered so the vnode open
    // was not seen by the DL driver
    //
    DldIOVnode*  dldVnode;
    dldVnode = DldVnodeHashTable::sVnodesHashTable->CreateAndAddIOVnodByBSDVnode( this->vnode );
    assert( dldVnode );
    if( !dldVnode ){
        
        //
        // we can't use this vnode as will be unable to distinguish
        // the DL internal writes from the user writes this will result
        // in an endless loop
        //
        DBG_PRINT_ERROR(("RetrieveReferencedIOVnodByBSDVnode() failed\n"));
        
        this->release();
        return NULL;
    }
    
    //
    // mark as skipped for log, shadowing and any other operations
    //
    dldVnode->flags.internal = 0x1;
    
    dldVnode->release();
    DLD_DBG_MAKE_POINTER_INVALID( dldVnode );
    
    this->expandFile();
    
    //
    // mark as noncached vnode, writes must be sector aligned!
    //
    //vnode_setnocache( this->vnode );
    
    return true;
}
//
// if fase is returned the request has been completed as the access is denied
//
bool
DldIOSCSIProtocolInterface::ExecuteCommand(
    __in IOSCSIProtocolInterface* service,
    __in SCSITaskIdentifier request
    )
{
    //SCSICommandDescriptorBlock   cdb;
    
    //assert( sizeof( cdb ) == kSCSICDBSize_Maximum );
    
    //DldSCSITaskGetCommandDescriptorBlock( request, &cdb );
    
    ///////
    /// start of the test
    /*
      DldSCSITaskCompleteAccessDenied( request );
      return false;
     */
    /// end of the test
    ////////
    /*
    dld_classic_rights_t   access;
    access = DldSCSITaskGetCdbRequestedAccess( request );
    if( 0x0 != ( DEVICE_WRITE & access ) ){
        
        DldSCSITaskCompleteAccessDenied( request );
        return false;
    }*/
    
    
    assert( preemption_enabled() );
    
    DldIOService*             dldSCSIDev;
    DldObjectPropertyEntry*   property;
    dld_classic_rights_t      requestedAccess;
    DldAccessCheckParam       param;
    bool                      disable        = false;
    bool                      isShadowedUser = false;
    
    requestedAccess = DldSCSITaskGetCdbRequestedAccess( request );
    if( 0x0 == requestedAccess )
        return true;
    
    dldSCSIDev = DldIOService::RetrieveDldIOServiceForIOService( service );
    assert( dldSCSIDev );
    if( !dldSCSIDev ){
        
        DBG_PRINT_ERROR(("DldIOService::RetrieveDldIOServiceForIOService( 0x%p ) returned NULL\n", service ));
        return true;
    }
    
    //
    // do not check read operations if there is a CD or DVD object in the stack,
    // as read requests are generated by every client including a mounted FSD( i.e. BSD susbsystem ),
    // but if there is no CD or DVD object then a user mode client dismantled them and
    // attached directly to the SCSI object, in that case the read requests must be 
    // checked and audited
    //
    if( DEVICE_READ == requestedAccess ){
        
        DldObjectPropertyEntry*   media;
        
        //
        // look for actual CD/DVD properties
        //

        media = dldSCSIDev->retrievePropertyByTypeRef( DldObjectPopertyType_IODVDMedia );
        if( NULL == media )
            media = dldSCSIDev->retrievePropertyByTypeRef( DldObjectPopertyType_IOCDMedia );
        
        if( media ){
            
            //
            // a request from the BSD subssytem, the request has been checked by the BSD KAUTH callbacks
            //
            media->release();
            goto __exit;
        }
        
    } // end if( DEVICE_READ == requestedAccess )
    
    property = dldSCSIDev->getObjectProperty();
    if( !property ){
        
        //
        // we are not interested in this device, this means that
        // there is another IOSCSIProtocolInterface device attached
        // to this one and this last is visible for user apps
        //
        goto __exit;
    }
    
    if( DldObjectPopertyType_SCSIPeripheralDeviceType05 != property->dataU.property->typeDsc.type ){
     
        DBG_PRINT_ERROR(("It seems you added a new SCSI type %i but forgot to add its processing\n", property->dataU.property->typeDsc.type ));
        assert( !"It seems you added a new SCSI type but forgot to add its processing" );
        goto __exit;
    }
        
    bzero( &param, sizeof( param ) );
    
    param.userSelectionFlavor = kDefaultUserSelectionFlavor;
    param.aclType             = kDldAclTypeSecurity;
    param.checkParentType     = true;
    param.dldRequestedAccess.winRequestedAccess   = requestedAccess;
    //param.dldRequestedAccess.kauthRequestedAccess = requestedAccess; // TO DO remove when the daemon will be able to proide ACL
    param.credential          = NULL;// current thread's user
    param.dldIOService        = dldSCSIDev;
#if defined(LOG_ACCESS)
    param.sourceFunction      = __PRETTY_FUNCTION__;
    param.sourceFile          = __FILE__;
    param.sourceLine          = __LINE__;
#endif//#if defined(LOG_ACCESS)
    
    /*if( dldIOService->getObjectProperty() &&
     DLD_DEVICE_TYPE_REMOVABLE == dldIOService->getObjectProperty()->dataU.property->deviceType.type.major ){}*/
    
    ::DldAcquireResources( &param );
    {
        ::isAccessAllowed( &param );
        
        disable = ( param.output.access.result[ DldFullTypeFlavor ].disable || 
                    param.output.access.result[ DldMajorTypeFlavor ].disable || 
                    param.output.access.result[ DldParentTypeFlavor ].disable );
        
#if defined( DBG )
        /*if( disable ){
         __asm__ volatile( "int $0x3" );
         }*/
#endif
        
        //
        // should we log?
        //
        if( param.output.access.result[ DldFullTypeFlavor ].log ||
            param.output.access.result[ DldMajorTypeFlavor ].log ){
            
            DldDriverDataLogInt intData;
            DldDriverDataLog    data;
            bool                logDataValid;
            kauth_cred_t        credentials;
            
            intData.logDataSize = sizeof( data );
            intData.logData = &data;
            
            credentials = kauth_cred_proc_ref( current_proc() ); // just like in isAccessAllowed()
            assert( credentials );
            if( credentials ){
                
                logDataValid = dldSCSIDev->initDeviceLogData( &param.dldRequestedAccess,
                                                              (ALL_WRITE & requestedAccess) ? DldFileOperationWrite : DldFileOperationRead,
                                                              proc_pid(current_proc()),
                                                              credentials,
                                                              ( param.output.access.result[ DldFullTypeFlavor ].disable || 
                                                                param.output.access.result[ DldMajorTypeFlavor ].disable ),
                                                              &intData );
                
                assert( logDataValid );
                if( logDataValid ){
                    
                    gLog->logData( &intData );
                    
                } else {
                    
                    DBG_PRINT_ERROR(("device log data is invalid\n"));
                }
                
                kauth_cred_unref( &credentials );
            } else {
                
                DBG_PRINT_ERROR(("kauth_cred_proc_ref failed\n"));
            }
            
        }// end log
    }
    ::DldReleaseResources( &param );
    
    if( !disable ){
        
        //
        // check for ejection
        //
        if( DldSCSITaskIsMediaEjectionRequest( request ) ){
            
            /*
             a media is ejected by calling IOSCSIMultimediaCommandsDevice::EjectTheMedia which in turn
             calls IOSCSIProtocolInterface::ExecuteCommand with two commands - the first unlocks the tray
             and the second is kSCSICmd_START_STOP_UNIT, the stack is as follows
             
             #0  0x015eae43 in IOSCSIMultimediaCommandsDevice::EjectTheMedia (this=0x57c2d00) at /SourceCache/IOSCSIArchitectureModelFamily/IOSCSIArchitectureModelFamily-265.0.3/IOSCSIMultimediaCommands/IOSCSIMultimediaCommandsDevice.cpp:722
             #1  0x015f0cb3 in IODVDServices::doEjectMedia (this=0x57c6380) at /SourceCache/IOSCSIArchitectureModelFamily/IOSCSIArchitectureModelFamily-265.0.3/IOSCSIMultimediaCommands/IODVDServices.cpp:594
             #2  0x011edfd8 in IOBlockStorageDriver::ejectMedia (this=0x57c2200) at /SourceCache/IOStorageFamily/IOStorageFamily-116.1/IOBlockStorageDriver.cpp:1191
             #3  0x011f70ef in dkioctl (dev=234881028, cmd=536896533, data=0x31d93e94 "", flags=1, proc=0x5bf4d20) at /SourceCache/IOStorageFamily/IOStorageFamily-116.1/IOMediaBSDClient.cpp:1382
             #4  0x003662a1 in spec_ioctl (ap=0x31d93d9c) at /work/Mac_OS_X_kernel/10_6_4/xnu-1504.7.4/bsd/miscfs/specfs/spec_vnops.c:529
             #5  0x00357488 in VNOP_IOCTL (vp=0x8f612e4, command=536896533, data=0x31d93e94 "", fflag=1, ctx=0x31d93e8c) at /work/Mac_OS_X_kernel/10_6_4/xnu-1504.7.4/bsd/vfs/kpi_vfs.c:3606
             #6  0x0034ca2f in vn_ioctl (fp=0x63504a0, com=536896533, data=0x31d93e94 "", ctx=0x31d93e8c) at /work/Mac_OS_X_kernel/10_6_4/xnu-1504.7.4/bsd/vfs/vfs_vnops.c:1147
             #7  0x00515c35 in fo_ioctl (fp=0x63504a0, com=536896533, data=0x31d93e94 "", ctx=0x31d93e8c) at /work/Mac_OS_X_kernel/10_6_4/xnu-1504.7.4/bsd/kern/kern_descrip.c:4826
             #8  0x005458ff in ioctl (p=0x5bf4d20, uap=0x54bea28, retval=0x5680324) at /work/Mac_OS_X_kernel/10_6_4/xnu-1504.7.4/bsd/kern/sys_generic.c:914
             #9  0x005b19b5 in unix_syscall64 (state=0x54bea24) at /work/Mac_OS_X_kernel/10_6_4/xnu-1504.7.4/bsd/dev/i386/systemcalls.c:365
             
             a description for the START_STOP_UNIT command can be found here
             http://en.wikipedia.org/wiki/SCSI_Start_Stop_Unit_Command
             we are interested in the following field
             LoEj (load/eject) and Start - these two bits are used together:
             00 - Stop motor
             01 - Start motor
             10 - Eject media
             11 - Load media
             */
            
            //
            // the media is being removed, invalidate the CD/DVD white list state,
            // the FSD has been already unmounted as the tray is locked when FSD
            // is mounted, if the white list results will be removed prematurely
            // when they will be restored on the next request
            //
            
            DldAclObject*  wlACLToRemove;
            
            property->dataU.property->LockExclusive();
            { // start of the lock
                
                wlACLToRemove = property->dataU.property->whiteListState.acl;
                property->dataU.property->whiteListState.acl              = NULL;
                property->dataU.property->whiteListState.inWhiteList      = false;
                property->dataU.property->whiteListState.currentWLApplied = false;// this value is a main driver to start reinitialization
                
                //
                // meaningless for DVD/CD
                //
                assert( false == property->dataU.property->whiteListState.propagateUp );
                
                if( DldObjectPopertyType_SCSIPeripheralDeviceType05 == property->dataU.property->typeDsc.type ){
                    
                    //
                    // the media is being removed
                    //
                    
                    property->dataU.ioSCSIPeripheralType05Property->uidValid = false;
                    
                    bzero( &property->dataU.ioSCSIPeripheralType05Property->mediaUID,
                           sizeof( property->dataU.ioSCSIPeripheralType05Property->mediaUID ) );
                }
                
            }// end of the lock
            property->dataU.property->UnLockExclusive();
            
            if( wlACLToRemove )
                wlACLToRemove->release();
            
            property->setUIDProperty();
            
        }// end if( DldSCSITaskIsMediaEjectionRequest( request )
        
        
        //
        // check for shadowing
        //
        DldAccessCheckParam param;
        
        bzero( &param, sizeof( param ) );
        
        param.userSelectionFlavor = kDefaultUserSelectionFlavor;
        param.aclType             = kDldAclTypeShadow;
        param.checkParentType     = true;
        param.dldRequestedAccess.kauthRequestedAccess = requestedAccess;// actually the value is ignored
        param.credential          = NULL;// current thread's user
        param.service             = NULL;
        param.dldIOService        = dldSCSIDev;
        
        ::DldAcquireResources( &param );
        ::DldIsShadowedUser( &param );
        ::DldReleaseResources( &param );
        
        isShadowedUser = ( param.output.shadow.result[ DldFullTypeFlavor ].shadow || 
                           param.output.shadow.result[ DldMajorTypeFlavor ].shadow || 
                           param.output.shadow.result[ DldParentTypeFlavor ].shadow );
        
    }// end if( !disable )
    
    
    //
    // shadow the write request
    //
    if( isShadowedUser && 0x0 != ( DEVICE_WRITE & requestedAccess ) ){
        
        assert( !disable );
        
        DldCommonShadowParams commonParams;
        UInt32       completionEvent    = DLD_INVALID_EVENT_VALUE;
        IOReturn     shadowCompletionRC = kIOReturnInvalid;
        bool         synchronousShadow  = false;
        bool         shadowed           = false;
        
        if( gSecuritySettings.securitySettings.disableOnShadowErrors ){
            
            //
            // the shadow operation must be synchronous
            //
            synchronousShadow = true;
        }
        
        bzero( &commonParams, sizeof( commonParams ) );
        
        commonParams.operationID = gShadow->generateUniqueID();
        if( synchronousShadow ){
            
            //
            // for synchronous processing we need an event to wait for shadow completion
            //
            DldInitNotificationEvent( &completionEvent );
            commonParams.completionEvent    = &completionEvent;
            commonParams.shadowCompletionRC = &shadowCompletionRC;
        }
        
        //
        // shadow the request
        //
        shadowed = gShadow->shadowSCSIOperation( dldSCSIDev, request, &commonParams );        
        if( shadowed && synchronousShadow ){
            
            //
            // wait for shadow completion
            //
            DldWaitForNotificationEvent( &completionEvent );
            
            //
            // the status code must be valid
            //
            assert( kIOReturnInvalid != completionEvent );
            
        } else if( shadowed ){
            
            //
            // an asynchronous shadowing, the first phase was successful
            //
            assert( !synchronousShadow );
            assert( DLD_INVALID_EVENT_VALUE == completionEvent );
            
            shadowCompletionRC = KERN_SUCCESS;
            
        } else {
            
            //
            // failed on the first phase
            //
            DBG_PRINT_ERROR(("the first phase of the shadowing failed\n"));
            shadowCompletionRC = KERN_FAILURE;
        }
        
        if( KERN_SUCCESS != shadowCompletionRC && gSecuritySettings.securitySettings.disableOnShadowErrors ){
            
            //
            // the shadowing failed so must the request
            //
            DBG_PRINT_ERROR(("shadowing failed\n"));
            disable = true;
        }
        
        //
        // the SCSI operation completion status is reported by a completion callback hook, see shadowSCSIOperation()
        //
        
    }// end if( isShadowedUser )
    
    
__exit:
    assert( dldSCSIDev );
    dldSCSIDev->release();
    
    if( disable )
        DldSCSITaskCompleteAccessDenied( request );
    
    return (!disable);
}
bool
com_VFSFilter0::start(
    __in IOService *provider
    )
{
    
    //__asm__ volatile( "int $0x3" );
    
    VNodeMap::Init();
    
    if( kIOReturnSuccess != VFSHookInit() ){
        
        DBG_PRINT_ERROR( ( "VFSHookInit() failed\n" ) );
        goto __exit_on_error;
    }
    
    if( ! QvrVnodeHooksHashTable::CreateStaticTableWithSize( 8, true ) ){
        
        DBG_PRINT_ERROR( ( "QvrVnodeHooksHashTable::CreateStaticTableWithSize() failed\n" ) );
        goto __exit_on_error;
    }

    
    //
    // gSuperUserContext must have a valid thread and process pointer
    // TO DO redesign this! Indefinit holding a thread or task object is a bad behaviour.
    //
    thread_reference( current_thread() );
    task_reference( current_task() );
    
    gSuperUserContext = vfs_context_create(NULL); // vfs_context_kernel()
    
    //
    // create an object for the vnodes KAuth callback and register the callback,
    // the callback might be called immediatelly just after registration!
    //
    gVnodeGate = QvrIOKitKAuthVnodeGate::withCallbackRegistration( this );
    assert( NULL != gVnodeGate );
    if( NULL == gVnodeGate ){
        
        DBG_PRINT_ERROR( ( "QvrIOKitKAuthVnodeGate::withDefaultSettings() failed\n" ) );
        goto __exit_on_error;
    }
    
    Instance = this;
    
    //
    // register with IOKit to allow the class matching
    //
    registerService();

    return true;
    
__exit_on_error:
    
    //
    // all cleanup will be done in stop() and free()
    //
    this->release();
    return false;
}
IOReturn
DldIPCUserClient::ipcRequest(
                        __in  void *vInBuffer,
                        __out void *vOutBuffer,
                        __in  void *vInSize,
                        __in  void *vOutSizeP,
                        void *, void *)
{
    IOReturn        RC = kIOReturnSuccess;
    DldIpcRequest*  ipcRequest = (DldIpcRequest*)vInBuffer;
    vm_size_t       inSize = (vm_size_t)vInSize;
    
    if( inSize < sizeof( *ipcRequest ) ||
        ipcRequest->size < sizeof( *ipcRequest ) ||
        inSize < ipcRequest->size )
        return kIOReturnBadArgument;
    

    //
    // get a service's client to send a notification to
    //
    DldIOUserClient* serviceUserClient = gServiceUserClient.getUserClient();
    if( ! serviceUserClient )
        return kIOReturnOffline;
    
    SInt32 eventID = gKerneToUserEvents.getNextEventNumber();
    
    UInt32 waitBlockIndex;
    if( ! this->acquireWaitBlock( &waitBlockIndex, eventID ) ){
        
        gServiceUserClient.releaseUserClient();
        return kIOReturnNoResources;
    }
    
    DldDriverEventData  eventTemplate;
    bzero( &eventTemplate, sizeof( eventTemplate ) );
    eventTemplate.Header.id = eventID;
    eventTemplate.Header.size = sizeof( eventTemplate );
    eventTemplate.Header.trustedClient = this->trustedClient;
    eventTemplate.Header.waitBlockIndex = waitBlockIndex;
    
    //
    // send a notification to the service
    //
    switch( ipcRequest->operation ){
            
        case DLD_IPCOP_STOP_SERVICE:
        {
                
            DldDriverEventData  event;
            
            event = eventTemplate;
            event.Header.type = DLD_EVENT_FORCED_STOP;
            
            RC = serviceUserClient->eventNotification( &event );
            
            break;
        }
            
        case DLD_IPCOP_IMPORT_DLS:
        {
            //
            // check the request consistency
            //
            if( ipcRequest->Tail.ImportDLS.filePathLength + sizeof( *ipcRequest ) > ipcRequest->size ){
                
                DBG_PRINT_ERROR(("ipcRequest->Tail.ImportDLS.filePathLength + sizeof( *ipcRequest ) > ipcRequest->size,"
                                  "ipcRequest->Tail.ImportDLS.filePathLength is %d,"
                                  "sizeof( *ipcRequest ) is %d,"
                                  "ipcRequest->size is %d\n",
                                  ipcRequest->Tail.ImportDLS.filePathLength,
                                  sizeof( *ipcRequest ),
                                  ipcRequest->size ));
                RC = kIOReturnBadArgument;
                break;
            }
            
            if( ipcRequest->Tail.ImportDLS.filePathLength < sizeof("/X") ){
                
                
                DBG_PRINT_ERROR(("the DLS file path is too short to be valid\n"));
                RC = kIOReturnBadArgument;
                break;
            }
            
            //
            // check that the zero terminator is present
            //
            if( '\0' != ((const char*)( ipcRequest+1 ))[ ipcRequest->Tail.ImportDLS.filePathLength - 1 ] ){
                
                DBG_PRINT_ERROR(("the DLS file path is not zero terminated\n"));
                RC = kIOReturnBadArgument;
                break;
            }
            
            //
            // check that this is a fully qualified path
            //
            if( '/' != ((const char*)( ipcRequest+1 ))[ 0 ] ){
                
                DBG_PRINT_ERROR(("the DLS file path is not a fully qualified one\n"));
                RC = kIOReturnBadArgument;
                break;
            }
            
            vm_size_t sizeOfEvent = sizeof( DldDriverEventData ) + ipcRequest->Tail.ImportDLS.filePathLength;
            
            DldDriverEventData*  event = (DldDriverEventData*)IOMalloc( sizeOfEvent );
            assert( event );
            if( !event ){
                
                DBG_PRINT_ERROR(("allocating memory for an event failed\n"));
                RC = kIOReturnNoMemory;
                break;
            }
            
            bcopy( &eventTemplate, event, min( sizeof(eventTemplate), sizeOfEvent ) );

            event->Header.size = sizeOfEvent;
            event->Header.type = DLD_EVENT_IMPORT_DLS;
            
            event->Tail.ImportDLS.adminRequest   = ipcRequest->Tail.ImportDLS.adminRequest;
            event->Tail.ImportDLS.filePathLength = ipcRequest->Tail.ImportDLS.filePathLength;
            event->Tail.ImportDLS.senderUid = fClientUID;
            bcopy( (const void*)(ipcRequest+1), (void*)(event+1), ipcRequest->Tail.ImportDLS.filePathLength );
            
            RC = serviceUserClient->eventNotification( event );
            
            IOFree( event, sizeOfEvent );
            event = NULL;
            break;
        }
            
        case DLD_IPCOP_GPUPDATE:
        {
            
            DldDriverEventData  event;
            
            event = eventTemplate;
            event.Header.type = DLD_EVENT_GPUPDATE;
            
            RC = serviceUserClient->eventNotification( &event );
            
            break;
        }
            
        default:
            RC = kIOReturnBadArgument;
            break;
    } // end switch
    
    gServiceUserClient.releaseUserClient();
    serviceUserClient = NULL;
    
    if( kIOReturnSuccess == RC ){
        
        RC = this->waitForCompletion( waitBlockIndex );
        
    } else {
        
        this->releaseWaitBlock( waitBlockIndex );
    }
    
    return RC;
}
Exemple #18
0
/*---------------------------------------------------------------------------
 *            HfRfCallback()
 *---------------------------------------------------------------------------
 *
 * Synopsis:  RFCOMM callback for the HF state machine.
 *
 * Return:    void
 */
void ChnRfCallback(RfChannel *rfChannel, RfCallbackParms *Parms)
{
    ChnChannel  *Channel;
    /*AtCommands  atParms;*/
    U8          event = 0;
    U16         offset = 0;
    BtStatus status = BT_STATUS_SUCCESS;

    Report(("[CHN][ChnRfCallback] +rfChannel==x%X, RfEvent==0x%X", (U32)rfChannel, Parms->event) );

    Channel = ChnFindRegisteredChannel(rfChannel);
    Assert(Channel != 0);

    switch (Parms->event) 
    {
    case RFEVENT_OPEN_IND:
        DBG_PRINT_EVENT(("[CHN][EVENT] RFCOMM Event : RFEVENT_OPEN_IND."));
        if(Channel)
        {
            Channel->remDev = Parms->ptrs.remDev;
            if(ChnGetSubstate(Channel) == CHN_SUBSTATE_CLOSED1)
            {
                ChnChangeSubstate(Channel, CHN_SUBSTATE_CONN_IN1);
            }
            else
            {
                Report(("ChnRfCallback: Only in C1, the server channel is registered."));
                /* Only in C1, the server channel is registered, so there shall no open ind received in other 
                substates other than C1. */
                (void)RF_RejectChannel(rfChannel);
            }
        }
        else
        {
            Report(("ChnRfCallback: RFEVENT_OPEN_IND: HFP shall never get into this case."));
            /* HFP shall never get into this case */
            DBG_PRINT_ERROR( ("[CHN][ERR] Unexpected substate==0x%X - (%s,%d)", ChnGetSubstate(Channel), __FILE__, __LINE__) );
            (void)RF_RejectChannel(rfChannel);
        }
        break;

    case RFEVENT_OPEN:
        DBG_PRINT_EVENT(("[CHN][EVENT] RFCOMM Event : RFEVENT_OPEN."));
        /* Register a SCO handler */
        if (Channel) 
        {
            Channel->remDev = Parms->ptrs.remDev;
            //CMGR_SetDeviceAsMaster(&(Channel->cmgrHandler));
            //HfgEnableSniffTimerMode(&(Channel->cmgrHandler));
            Channel->linkFlags |= CHN_LINK_HANDSFREE;
            Report(("ChnRfCallback: RFCOMM HF connection established."));

            switch(ChnGetSubstate(Channel))
            {
            case CHN_SUBSTATE_CONN_OUT3:
            case CHN_SUBSTATE_CONN_IN2:
		  // Direct change state to SLC1
                ChnChangeSubstate(Channel, CHN_SUBSTATE_SLC1);
                break;
            case CHN_SUBSTATE_DISC1:
                ChnDisconnecting(Channel);
                break;
            default:
                Report(("ChnRfCallback: RFEVENT_OPEN:CHN shall not get into this case."));
                /* CHN shall not get into this case. */
                DBG_PRINT_ERROR( ("[CHN][ERR] Unexpected substate==0x%X - (%s,%d)", ChnGetSubstate(Channel), __FILE__, __LINE__) );
                Assert(0);
                RF_CloseChannel(rfChannel);
                break;
            }
        }
        else
        {
            RF_CloseChannel(rfChannel);
        }
        break;
    case RFEVENT_DATA_IND:
        DBG_PRINT_EVENT(("[CHN][EVENT] RFCOMM Event : RFEVENT_DATA_IND."));
        if (Channel) 
        {
        	RfBuffer	rfBuf;
        	rfBuf.len = Parms->dataLen;
        	rfBuf.buf = Parms->ptrs.data;
            	Report(("ChnRfCallback: RFCOMM HF data received."));
            	RF_AdvanceCredit(rfChannel, 1);

		ChnAppCallback(Channel, CHN_EVENT_RX_DATA, Parms->status, (U32)&rfBuf);
        }
        break;

    case RFEVENT_PACKET_HANDLED:
        DBG_PRINT_EVENT(("[CHN][EVENT] RFCOMM Event : RFEVENT_PACKET_HANDLED, Status=%d", Parms->status));
        if (Channel) 
        {
        	Channel->bTxInProgress = FALSE;
            status = Parms->status;
            if(status != BT_STATUS_SUCCESS)
            {
                /* Only when RFCOMM channel is closed, it does not return success */
                DBG_PRINT_AT(("[CHN][AT] RFEVENT_PACKET_HANDLED : Fail"));
            }
            else
            {
	            	if(ChnRemainTxData(Channel) > 0)
			{
				DBG_PRINT_AT(("[CHN][AT] Bytestosend=%d", ChnRemainTxData(Channel) ));
				status = ChnSendTxBuffer(Channel);
				if (status == BT_STATUS_PENDING) 
				{
				    DBG_PRINT_AT(("[CHN] ChnSendTxBuffer return Pending"));
				}
            		}
            }
        } 
        else 
        {
            Report(("ChnRfCallback: RFEVENT_PACKET_HANDLED: No CHN channel."));
            return;
        }
        break;

    case RFEVENT_CLOSED:
        DBG_PRINT_EVENT(("[CHN][EVENT] RFCOMM Event : RFEVENT_CLOSED"));
        if (Channel) 
        {
            //HfgDisableSniffTimerMode(&(Channel->cmgrHandler));
            Channel->linkFlags &= ~CHN_LINK_HANDSFREE;

            switch(ChnGetSubstate(Channel))
            {
            case CHN_SUBSTATE_CONN_OUT3:
            case CHN_SUBSTATE_CONN_IN1:
            case CHN_SUBSTATE_CONN_IN2:
            case CHN_SUBSTATE_OPEN1:
                ChnChangeSubstate(Channel, CHN_SUBSTATE_CLOSED1);
                break;
            case CHN_SUBSTATE_OPEN2:
            case CHN_SUBSTATE_SLC1:
            case CHN_SUBSTATE_SLC2:
            case CHN_SUBSTATE_DISC1:
                ChnDisconnecting(Channel);
                break;
            default:
                DBG_PRINT_ERROR( ("[CHN][ERR] Unexpected substate==0x%X - (%s,%d)", ChnGetSubstate(Channel), __FILE__, __LINE__) );
                Assert(0);
                break;
            }
        }
        break;

    default:
        DBG_PRINT_EVENT(("[CHN][EVENT] RFCOMM ignore other events : %d", Parms->event));
        /* Ignore other events */
        break;
    }
    Report(("- ChnRfCallback."));
}
Exemple #19
0
/*---------------------------------------------------------------------------
 *            ChnServiceConnectionCallback()
 *---------------------------------------------------------------------------
 *
 * Synopsis:  Called by device manager with link state events.
 *
 * Return:    (See header file)
 *
 */
void ChnServiceConnectionCallback(CmgrHandler *Handler, CmgrEvent Event,
                                  BtStatus Status) 
{
    ChnChannel *Channel = ContainingRecord(Handler, ChnChannel, cmgrHandler);
    BtStatus return_code;

    Report(("+ ChnServiceConnectionCallback: Event=%d, Status=%d.", Event, Status));

    switch (Event) 
    {
    case CMEVENT_DATA_LINK_CON_CNF:
        DBG_PRINT_EVENT(("[CHN][EVENT] CMGR Event : CMEVENT_DATA_LINK_CON_CNF, Status=%d.", Status));
        if(ChnGetSubstate(Channel) == CHN_SUBSTATE_CONN_OUT1)
        {
            if (Status == BT_STATUS_SUCCESS) 
            {
                Channel->linkFlags |= CHN_LINK_ACL;
                /* FLOW : Start SDP query */
                if ((return_code = ChnStartServiceQuery(Channel, BSQM_FIRST)) == BT_STATUS_PENDING)
                {
                    ChnChangeSubstate(Channel, CHN_SUBSTATE_CONN_OUT2);
                }
                else
                {
                    ChnChangeSubstate(Channel, CHN_SUBSTATE_CLOSED1);
                }
            }
            else
            {
                /* This event shall not be received in other substates other than C1 */
                ChnChangeSubstate(Channel, CHN_SUBSTATE_CLOSED1);
            }
        }
        else if(ChnGetSubstate(Channel) == CHN_SUBSTATE_DISC1)
        {
            ChnDisconnecting(Channel);
        }
        else
        {
            DBG_PRINT_ERROR( ("[CHN][ERR] Unexpected substate==0x%X - (%s,%d)", ChnGetSubstate(Channel), __FILE__, __LINE__) );
            ChnChangeSubstate(Channel, CHN_SUBSTATE_CLOSED1);
        }
        break;

        case CMEVENT_DATA_LINK_DIS:
            /* We shall never run into this case */
            DBG_PRINT_EVENT(("[CHN][EVENT] CMGR Event : CMEVENT_DATA_LINK_DIS, Status=%d.", Status));
            DBG_PRINT_ERROR( ("[CHN][ERR] Unexpected substate==0x%X - (%s,%d)", ChnGetSubstate(Channel), __FILE__, __LINE__) );
            ChnCloseChannel(Channel);
            break;

        case CMEVENT_AUDIO_LINK_CON:
            DBG_PRINT_EVENT(("[CHN][EVENT] CMGR Event : CMEVENT_AUDIO_LINK_CON, Status=%d.", Status));
            /* Clear SCO connecting flag */
            Channel->linkFlags &= ~CHN_LINK_SCO_CONNECTING;
            ChnAppCallback(Channel, CHN_EVENT_AUDIO_CONNECTED, Status, 
            (U32)&Channel->cmgrHandler.bdc->link->bdAddr);
            if(ChnGetSubstate(Channel) == CHN_SUBSTATE_SLC1)
            {
                /* Remote connect SCO link */
                /* It could happen in SLC1 but no action needed */
            }
            else if(ChnGetSubstate(Channel) == CHN_SUBSTATE_SLC2)
            {
                ChnChangeSubstate(Channel, CHN_SUBSTATE_SLC1);
            }
            else if(ChnGetSubstate(Channel) == CHN_SUBSTATE_DISC1)
            {
                ChnDisconnecting(Channel);
            }
            else
            {
                DBG_PRINT_ERROR( ("[CHN][ERR] Unexpected substate==0x%X - (%s,%d)", ChnGetSubstate(Channel), __FILE__, __LINE__) );
            }
            break;

        case CMEVENT_AUDIO_LINK_DIS:
            DBG_PRINT_EVENT(("[CHN][EVENT] CMGR Event : CMEVENT_AUDIO_LINK_DIS, Status=%d.", Status));
            /* Clear SCO disconnecting flag */
            Channel->linkFlags &= ~CHN_LINK_SCO_DISCONNECTING;
            ChnAppCallback(Channel, CHN_EVENT_AUDIO_DISCONNECTED, Status, 
            (U32)&Channel->cmgrHandler.bdc->link->bdAddr);

            if(ChnGetSubstate(Channel) == CHN_SUBSTATE_SLC1)
            {
                /* Remote disconnect SCO link */
                /* It could happen in SLC1 but no action needed */
            }
            else if(ChnGetSubstate(Channel) == CHN_SUBSTATE_SLC2)
            {
                ChnChangeSubstate(Channel, CHN_SUBSTATE_SLC1);
            }
            else if(ChnGetSubstate(Channel) == CHN_SUBSTATE_DISC1)
            {
                ChnDisconnecting(Channel);
            }
            else
            {
                DBG_PRINT_ERROR( ("[CHN][ERR] Unexpected substate==0x%X - (%s,%d)", ChnGetSubstate(Channel), __FILE__, __LINE__) );
            }
            break;

        #if BT_SCO_HCI_DATA == XA_ENABLED
        case CMEVENT_AUDIO_DATA:
            DBG_PRINT_EVENT(("[CHN][EVENT] CMGR Event : CMEVENT_AUDIO_DATA, Status=%d.", Status));
            ChnAppCallback(Channel, CHN_EVENT_AUDIO_DATA, Status, (U32)&Handler->audioData);
            break;

        case CMEVENT_AUDIO_DATA_SENT:
            DBG_PRINT_EVENT(("[CHN][EVENT] CMGR Event : CMEVENT_AUDIO_DATA_SENT, Status=%d.", Status));
            ChnAppCallback(Channel, CHN_EVENT_AUDIO_DATA_SENT, Status, (U32)&Handler->audioPacket);
            break;
        #endif
    }
    Report(("- ChnServiceConnectionCallback."));
}