Source to iokit/Families/IOATAPICDDrive/IOATAPICDDrive.cpp


Enter a symbol's name here to quickly find it.

/*
 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * The contents of this file constitute Original Code as defined in and
 * are subject to the Apple Public Source License Version 1.1 (the
 * "License").  You may not use this file except in compliance with the
 * License.  Please obtain a copy of the License at
 * http://www.apple.com/publicsource and read it before using this file.
 * 
 * This Original Code and all software distributed under the License are
 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
/*
 * Copyright (c) 1999 Apple Computer, Inc.  All rights reserved. 
 *
 * IOATAPICDDrive.h - Generic ATAPI CD-ROM driver.
 *
 * HISTORY
 * Sep 2, 1999	jliu - Ported from AppleATAPIDrive.
 */

#include <IOKit/assert.h>
#include <IOKit/storage/ata/IOATAPICDDrive.h>
#include <IOKit/storage/ata/IOATAPICDDriveNub.h>

#define	super IOATAPIHDDrive
OSDefineMetaClassAndStructors( IOATAPICDDrive, IOATAPIHDDrive )

// --------------------------------------------------------------------------
// Looks for an ATAPI device which is a CD-ROM device.

bool
IOATAPICDDrive::matchATAPIDeviceType(UInt8 type)
{
	if (type == kIOATAPIDeviceTypeCDROM)
		return true;
	return false;
}

// --------------------------------------------------------------------------
// Instantiate an ATAPI specific subclass of IOCDDriveNub.

IOService *
IOATAPICDDrive::instantiateNub()
{
    IOService * nub = new IOATAPICDDriveNub;
    return nub;
}

// --------------------------------------------------------------------------
// Report whether media is write-protected.

IOReturn
IOATAPICDDrive::reportWriteProtection(bool * isWriteProtected)
{
	*isWriteProtected = true;
	return kIOReturnSuccess;
}

// --------------------------------------------------------------------------
// Returns the device type.

const char *
IOATAPICDDrive::getDeviceTypeName()
{
	return kDeviceTypeCDROM;
}

// --------------------------------------------------------------------------
// Read the Table of Contents.

IOReturn
IOATAPICDDrive::readTOC(struct rawToc * buffer,
                        UInt32          length,
                        tocFormat       format)
{
	IOReturn             ret;
	IOATACommand *       cmd;
	IOMemoryDescriptor * tocDesc;
	
	assert(buffer);
	
    tocDesc = IOMemoryDescriptor::withAddress(buffer,
	                                    length,
	                                    kIODirectionIn);
	if (!tocDesc)
		return kIOReturnNoMemory;
	
	cmd = atapiCommandReadTOC(tocDesc, format, 0);
	if (!cmd)
		return kIOReturnNoMemory;

	// Execute the Read TOC command.
	//
	ret = getIOReturn(syncExecute(cmd));

	// Release the memory descriptor.
	//
	tocDesc->release();
	
	cmd->release();

	return ret;
}

// --------------------------------------------------------------------------
// Start analog audio play

IOReturn
IOATAPICDDrive::setAudioStopAddress (positioningType addressType,
                                     cdAddress address)
{
    if (addressType == kAbsoluteTime)
        fMSFStopAddress = (UInt32)address;
    if (addressType == kBlockAddress)
        fLBAStopAddress = (UInt32)address;
    return 0;
}

IOReturn
IOATAPICDDrive::audioPlay(positioningType addressType,
                          cdAddress address,
                          audioPlayMode mode)
{
    if (addressType == kBlockAddress) {
        return playAudio((UInt32)address,0xfff);
    }
    if (addressType == kAbsoluteTime) {
        return playAudioMSF((UInt32)address,(UInt32)fMSFStopAddress);
    }
    return 0;
}

IOReturn
IOATAPICDDrive::playAudioMSF (UInt32 start_msf,
                              UInt32 end_msf)
{
	IOATACommand *       cmd;
	IOReturn             ret;

        // IOLog("IOATAPICDDrive::playAudioMSF %x %x\n",start_msf,end_msf);
	cmd = atapiCommandPlayAudioMSF(start_msf, end_msf);
	if (!cmd)
		return kIOReturnNoMemory;

	// Execute the audio play command.
	//
	ret = getIOReturn(syncExecute(cmd));

	cmd->release();

	return ret;
}

IOReturn
IOATAPICDDrive::playAudio (UInt32 start_lba,
                           UInt32 len_lba)
{
	IOATACommand *       cmd;
	IOReturn             ret;

        // IOLog("IOATAPICDDrive::playAudio\n");
	cmd = atapiCommandPlayAudio(start_lba, len_lba);
	if (!cmd)
		return kIOReturnNoMemory;

	// Execute the audio play command.
	//
	ret = getIOReturn(syncExecute(cmd));

	cmd->release();

	return ret;
}

IOReturn
IOATAPICDDrive::audioPause(bool pause)
{
	IOATACommand *       cmd;
	IOReturn             ret;

        // IOLog("IOATAPICDDrive::audioPause\n");
	cmd = atapiCommandPauseResume(!pause);
	if (!cmd)
		return kIOReturnNoMemory;

	// Execute the audio pause/resume command.
	//
	ret = getIOReturn(syncExecute(cmd));

	cmd->release();

	return ret;
}

IOReturn
IOATAPICDDrive::readAudioVolume(UInt8 * leftVolume, UInt8 * rightVolume)
{
    UInt8 *audio_control;
    IOReturn status = -1;

    audio_control = (UInt8 *)IOMalloc(144);
    if (!audio_control) return kIOReturnNoMemory;

    status = readModeSense(audio_control,(UInt32)144,(UInt32)0xe);
    *leftVolume = audio_control[21] & 0xff;
    *rightVolume = audio_control[17] & 0xff;

    // IOLog("IOATAPICDDrive::readAudioVolume left0=%d right0=%d left1=%d right1=%d\n", *leftVolume,*rightVolume, audio_control[23],audio_control[19]);

    // IOLog("IOATAPICDDrive::readAudioVolume p0=%d p1=%d p2=%d p3=%d\n", audio_control[8],audio_control[10], audio_control[12],audio_control[14]);

    IOFree(audio_control,144);
    return status;
}

IOReturn
IOATAPICDDrive::setVolume(UInt8 leftVolume, UInt8 rightVolume)
{
    UInt8 *audio_control;
    IOReturn status = -1;
    UInt32 len;

    // IOLog("IOATAPICDDrive::setVolume %d %d\n",leftVolume,rightVolume);

    // init
    audio_control = (UInt8 *)IOMalloc(144);
    if (!audio_control) return kIOReturnNoMemory;

    // get current values
    status = readModeSense(audio_control,(UInt32)144,(UInt32)0xe);
    len = (UInt32)audio_control[1] + 2;

    // set new values
    // audio_control[ 9] = audio_control[11] = 0xff;
    // audio_control[23] = audio_control[21] = leftVolume & 0xff;
    audio_control[19] = audio_control[17] = rightVolume & 0xff;

    // get current values
    status = writeModeSelect(audio_control,(UInt32)len,(UInt32)0xe);

    // cleanup and exit
    IOFree(audio_control,144);
    return status;
}

IOReturn
IOATAPICDDrive::readModeSense(UInt8 * buffer, UInt32 length, UInt32 pageCode)
{
    IOReturn             ret;
    IOATACommand *       cmd;
    IOMemoryDescriptor * senseDesc;
	
    assert(buffer);

    // IOLog("IOATAPICDDrive::readModeSense len=%d page=%d\n",length,pageCode);

    senseDesc = IOMemoryDescriptor::withAddress(buffer,
                                     length,
                                     kIODirectionIn);
    if (!senseDesc)
        return kIOReturnNoMemory;

    cmd = atapiCommandModeSense(senseDesc, pageCode);
    if (!cmd)
        return kIOReturnNoMemory;

    // Execute the Mode Sense command.
    //
    ret = getIOReturn(syncExecute(cmd));

    // Release the memory descriptor.
    //
    senseDesc->release();

    cmd->release();

    return ret;
}

IOReturn
IOATAPICDDrive::writeModeSelect(UInt8 * buffer, UInt32 length, UInt32 pageCode)
{
    IOReturn             ret;
    IOATACommand *       cmd;
    IOMemoryDescriptor * selectDesc;
	
    // IOLog("IOATAPICDDrive::writeModeSelect %d %d\n",length,pageCode);
    assert(buffer);

    selectDesc = IOMemoryDescriptor::withAddress(buffer,
                                     length,
                                     kIODirectionOut);
    if (!selectDesc)
        return kIOReturnNoMemory;

    cmd = atapiCommandModeSelect(selectDesc, pageCode);
    if (!cmd)
        return kIOReturnNoMemory;

    // Execute the Mode Select command.
    //
    ret = getIOReturn(syncExecute(cmd));

    // Release the memory descriptor.
    //
    selectDesc->release();

    cmd->release();

    return ret;
}

IOReturn
IOATAPICDDrive::readTheQSubcode(struct qSubcode *buffer)
{
    UInt8 * channel_data;
    UInt32 address;
    IOReturn ret;

    // init
    channel_data = (UInt8 *)IOMalloc(16);
    if (!channel_data) return kIOReturnNoMemory;

    // get audio status
    ret = readSubChannel(channel_data,16,0x01,0x00,true);

    // get current absolute address
    address = ((channel_data[ 8] & 0xff) << 24) |
              ((channel_data[ 9] & 0xff) << 16) |
              ((channel_data[10] & 0xff) <<  8) |
              ((channel_data[11] & 0xff));
    buffer->absAddress = (cdAddress)address;

    // get current track relative address
    address = ((channel_data[12] & 0xff) << 24) |
              ((channel_data[13] & 0xff) << 16) |
              ((channel_data[14] & 0xff) <<  8) |
              ((channel_data[15] & 0xff));
    buffer->relAddress = (cdAddress)address;

    // get type, track, index
    buffer->type  = channel_data[5] & 0x0f;
    buffer->track = channel_data[6] & 0x0f;
    buffer->index = channel_data[7] & 0x0f;

    // IOLog("IOATAPICDDrive::readTheQSubcode absAddr=0x%x relAddr=0x%xx\n", buffer->absAddress, buffer->relAddress);

    // cleanup
    IOFree(channel_data,16);
    return ret;
}

IOReturn
IOATAPICDDrive::getAudioStatus(struct audioStatus *status)
{
    UInt8 * channel_data;
    UInt32 address;
    IOReturn ret;

    // init
    channel_data = (UInt8 *)IOMalloc(16);
    if (!channel_data) return kIOReturnNoMemory;

    // get audio status
    ret = readSubChannel(channel_data,16,0x01,0x00,true);

    // get current absolute address
    address = ((channel_data[ 8] & 0xff) << 24) |
              ((channel_data[ 9] & 0xff) << 16) |
              ((channel_data[10] & 0xff) <<  8) |
              ((channel_data[11] & 0xff));
    status->address = (cdAddress)address;

    // get current status
    if ((channel_data[1] & 0xff) == 0x00) status->status = kUnknown;
    if ((channel_data[1] & 0xff) == 0x11) status->status = kAudioPlayInProgress;
    if ((channel_data[1] & 0xff) == 0x12) status->status = kHoldTrackMode;
    if ((channel_data[1] & 0xff) == 0x13) status->status = kAudioPlayCompleted;
    if ((channel_data[1] & 0xff) == 0x14) status->status = kError;
    if ((channel_data[1] & 0xff) == 0x15) status->status = fStatus;
    fStatus = status->status;

    // get current track type
    status->type = channel_data[5] & 0x0f;

    // IOLog("IOATAPICDDrive::getAudioStatus addr=0x%x status=0x%x type=0x%x\n", status->address, status->status, status->type);

    // cleanup
    IOFree(channel_data,16);
    return ret;
}

IOReturn
IOATAPICDDrive::readMCN(UInt8 * buffer, bool * found)
{
    UInt8 * channel_data;
    UInt32 address;
    IOReturn ret;

    // init
    channel_data = (UInt8 *)IOMalloc(24);
    if (!channel_data) return kIOReturnNoMemory;

    // get audio status
    ret = readSubChannel(channel_data,24,0x02,0x00,true);

    // check if found
    *found = (channel_data[8] & 0x80 == 0x80);

    // copy the data
    if (*found) {
        bcopy(&channel_data[9],buffer,15);
    }

    // IOLog("IOATAPICDDrive::readMCN found=%d\n",*found);

    // cleanup
    IOFree(channel_data,24);
    return ret;
}

IOReturn
IOATAPICDDrive::readISRC(UInt32 track, UInt8 * buffer, bool * found)
{
    UInt8 * channel_data;
    UInt32 address;
    IOReturn ret;

    // init
    channel_data = (UInt8 *)IOMalloc(24);
    if (!channel_data) return kIOReturnNoMemory;

    // get audio status
    ret = readSubChannel(channel_data,24,0x03,track,true);

    // check if found
    *found = (channel_data[8] & 0x80 == 0x80);

    // copy the data
    if (*found) {
        bcopy(&channel_data[9],buffer,15);
    }

    // IOLog("IOATAPICDDrive::readISRC found=%d\n",*found);

    // cleanup
    IOFree(channel_data,24);
    return ret;
}

IOReturn
IOATAPICDDrive::readHeader(UInt32 blockAddress,struct headerInfo *buffer)
{
    UInt8 * header_data;
    IOReturn             ret;
    IOATACommand *       cmd;
    IOMemoryDescriptor * readDesc;
	
    // init
    header_data = (UInt8 *)IOMalloc(8);
    if (!header_data) return kIOReturnNoMemory;

    // IOLog("IOATAPICDDrive::readHeader addr=0x%x\n",blockAddress);

    readDesc = IOMemoryDescriptor::withAddress(header_data,
                                     8,
                                     kIODirectionIn);
    if (!readDesc)
        return kIOReturnNoMemory;

    cmd = atapiCommandReadHeader(readDesc, blockAddress);
    if (!cmd)
        return kIOReturnNoMemory;

    // Execute the Read Header command.
    //
    ret = getIOReturn(syncExecute(cmd));

    // Release the memory descriptor.
    //
    readDesc->release();
    cmd->release();

    // save the data
    buffer->address = ((header_data[4] & 0xff) << 24) |
                      ((header_data[5] & 0xff) << 16) |
                      ((header_data[6] & 0xff) <<  8) |
                      ((header_data[7] & 0xff));
    buffer->mode = header_data[0] & 0xff;

    IOFree(header_data,8);

    return ret;
}

IOReturn
IOATAPICDDrive::readSubChannel(UInt8 * buffer,
                          UInt32 length,
                          UInt8 dataFormat,
                          UInt8 trackNumber,
                          bool subQ)
{
    IOReturn             ret;
    IOATACommand *       cmd;
    IOMemoryDescriptor * readDesc;
	
    assert(buffer);

    // IOLog("IOATAPICDDrive::readSubChannel len=%d\n",length);

    readDesc = IOMemoryDescriptor::withAddress(buffer,
                                     length,
                                     kIODirectionIn);
    if (!readDesc)
        return kIOReturnNoMemory;

    cmd = atapiCommandReadSubChannel(readDesc, dataFormat, trackNumber, subQ);
    if (!cmd)
        return kIOReturnNoMemory;

    // Execute the Mode Sense command.
    //
    ret = getIOReturn(syncExecute(cmd));

    // Release the memory descriptor.
    //
    readDesc->release();

    cmd->release();

    return ret;
}