IOATAPIDVDDrive.cpp [plain text]
#include <IOKit/assert.h>
#include <IOKit/IOBufferMemoryDescriptor.h>
#include <IOKit/storage/ata/IOATAPIDVDDrive.h>
#include <IOKit/storage/ata/IOATAPIDVDDriveNub.h>
#include <IOKit/storage/IODVDTypes.h>
#include <libkern/OSByteOrder.h>
#ifdef LOG_DVD_MESSAGES
#define DEBUG_LOG(fmt, args...) IOLog(fmt, ## args)
#else
#define DEBUG_LOG(fmt, args...)
#endif
#define super IOATAPICDDrive
OSDefineMetaClassAndStructors( IOATAPIDVDDrive, IOATAPICDDrive )
struct featureHdr {
UInt32 totalLen;
UInt8 reserved1[2];
UInt16 currentProfile;
};
struct featureDescriptor {
UInt16 featureCode;
UInt8 versionPC;
UInt8 additionalLength;
};
#define kConfigBufferSize 2048
#define kConfigFeatureHeaderBytes sizeof(struct featureHdr)
#define kConfigDataLengthBytes sizeof(UInt32)
#define kConfigMinDataLength (sizeof(struct featureHdr) - \
kConfigDataLengthBytes + \
sizeof(struct featureDescriptor))
IOReturn IOATAPIDVDDrive::classifyDrive(bool * isDVDDrive)
{
IOATACommand * cmd = 0;
IOBufferMemoryDescriptor * copyrightDesc;
IOBufferMemoryDescriptor * senseDesc;
IOReturn ret = kIOReturnSuccess;
do {
copyrightDesc = IOBufferMemoryDescriptor::withCapacity(8,
kIODirectionIn);
senseDesc = IOBufferMemoryDescriptor::withCapacity(
sizeof(ATASenseData),
kIODirectionIn);
if ( (copyrightDesc == 0) || (senseDesc == 0) )
{
ret = kIOReturnNoMemory;
break;
}
bzero(senseDesc->getBytesNoCopy(), senseDesc->getCapacity());
cmd = atapiCommandReadDVDStructure(copyrightDesc,
kIODVDReadStructureCopyright);
if (cmd == 0)
{
ret = kIOReturnNoMemory;
break;
}
ret = syncExecute(cmd,
kATADefaultTimeout,
kATADefaultRetries,
senseDesc);
*isDVDDrive = true;
if (ret != kIOReturnSuccess)
{
ATASenseData * senseData;
senseData = (ATASenseData *) senseDesc->getBytesNoCopy();
if ((senseData->errorCode == kATAPISenseCurrentErr) ||
(senseData->errorCode == kATAPISenseDeferredErr))
{
if ((senseData->senseKey == kATAPISenseIllegalReq) &&
(senseData->additionalSenseCode == 0x20) &&
(senseData->additionalSenseQualifier == 0x0))
{
*isDVDDrive = false;
}
}
ret = kIOReturnSuccess;
}
}
while (false);
if (senseDesc) senseDesc->release();
if (copyrightDesc) copyrightDesc->release();
if (cmd) cmd->release();
return ret;
}
IOReturn
IOATAPIDVDDrive::determineMediaType(UInt32 * mediaType)
{
IOATACommand * cmd = 0;
IOBufferMemoryDescriptor * dataDesc;
IODVDStructurePhysical * data;
IOReturn ret = kIOReturnSuccess;
*mediaType = kDVDMediaTypeUnknown;
do {
dataDesc = IOBufferMemoryDescriptor::withCapacity(sizeof(*data),
kIODirectionIn);
if ( dataDesc == 0 )
{
ret = kIOReturnNoMemory;
break;
}
data = (IODVDStructurePhysical *) dataDesc->getBytesNoCopy();
bzero(data, sizeof(data->length));
cmd = atapiCommandReadDVDStructure(dataDesc,
kIODVDReadStructurePhysical);
if ( cmd == 0 )
{
ret = kIOReturnNoMemory;
break;
}
if ( syncExecute(cmd) != kIOReturnSuccess )
{
*mediaType = kCDMediaTypeROM; }
else if ( IODVDGetDataLength16(data->length) <
(sizeof(*data) - sizeof(data->length)) )
{
ret = kIOReturnUnderrun;
}
else
{
DEBUG_LOG("%s: DVD Book Type: %x Part Version: %x\n",
getName(), data->bookType, data->partVersion);
switch (data->bookType)
{
default:
case kIODVDBookTypeDVDROM:
case kIODVDBookTypeDVDR:
case kIODVDBookTypeDVDRW:
case kIODVDBookTypeDVDPlusRW:
*mediaType = kDVDMediaTypeROM;
break;
case kIODVDBookTypeDVDRAM:
*mediaType = kDVDMediaTypeRAM;
break;
}
}
}
while (false);
if (dataDesc) dataDesc->release();
if (cmd) cmd->release();
return ret;
}
bool
IOATAPIDVDDrive::matchATAPIDeviceType(UInt8 type, SInt32 * score)
{
bool isDVDDrive;
bool match = false;
do {
if ( type != kIOATAPIDeviceTypeCDROM )
break;
if ( selectTimingProtocol() == false )
break;
if ( classifyDrive(&isDVDDrive) != kIOReturnSuccess )
break;
if ( isDVDDrive )
{
DEBUG_LOG("%s::%s DVD drive detected\n", getName(), __FUNCTION__);
*score = 20;
match = true;
}
else
{
DEBUG_LOG("%s::%s Not a DVD drive\n", getName(), __FUNCTION__);
}
}
while (false);
return match;
}
IOReturn
IOATAPIDVDDrive::getConfiguration(UInt8 * buf,
UInt32 length,
UInt32 * actualLength,
bool current)
{
IOMemoryDescriptor * bufDesc = 0;
IOATACommand * cmd = 0;
IOReturn ret = kIOReturnNoMemory;
ATAResults results;
do {
bufDesc = IOMemoryDescriptor::withAddress(buf, length, kIODirectionIn);
if (bufDesc == 0)
break;
cmd = atapiCommandGetConfiguration(bufDesc, 0x01);
if (cmd == 0)
break;
ret = syncExecute(cmd);
cmd->getResults(&results);
*actualLength = results.bytesTransferred;
}
while (0);
if (cmd) cmd->release();
if (bufDesc) bufDesc->release();
return ret;
}
const char *
IOATAPIDVDDrive::getDeviceTypeName()
{
return kIOBlockStorageDeviceTypeDVD;
}
UInt32
IOATAPIDVDDrive::getMediaType()
{
UInt32 mediaType;
determineMediaType(&mediaType);
return mediaType;
}
bool
IOATAPIDVDDrive::init(OSDictionary * properties)
{
return super::init(properties);
}
IOService *
IOATAPIDVDDrive::instantiateNub(void)
{
IOService * nub = new IOATAPIDVDDriveNub;
return nub;
}
IOReturn
IOATAPIDVDDrive::reportMediaState(bool * mediaPresent, bool * changed)
{
IOReturn result;
result = super::reportMediaState(mediaPresent, changed);
#if 0 // For testing only
if (result != kIOReturnSuccess)
{
return result;
}
if (*mediaPresent && *changed)
{
getMediaType();
}
#endif
return result;
}
IOReturn
IOATAPIDVDDrive::reportWriteProtection(bool * isWriteProtected)
{
UInt32 len;
struct featureHdr * fh;
struct featureDescriptor * fdp;
IOReturn result;
UInt8 * configBuf;
UInt32 configBufSize;
*isWriteProtected = true;
configBuf = (UInt8 *) IOMalloc(kConfigBufferSize);
if ( configBuf == 0 )
return kIOReturnNoMemory;
bzero((void *) configBuf, kConfigBufferSize);
do {
result = getConfiguration(configBuf,
kConfigBufferSize,
&configBufSize,
true);
if (result == kIOReturnUnderrun)
{
result = kIOReturnSuccess;
}
if (result != kIOReturnSuccess)
{
DEBUG_LOG("%s::%s getConfiguration() error = %s\n",
getName(), __FUNCTION__, stringFromReturn(result));
result = kIOReturnSuccess;
break;
}
fh = (struct featureHdr *) configBuf;
len = OSSwapBigToHostInt32(fh->totalLen);
if (len < kConfigMinDataLength)
{
result = kIOReturnUnderrun;
break;
}
len += kConfigDataLengthBytes;
len = min(len, kConfigBufferSize);
DEBUG_LOG("%s::%s config length = %ld\n", getName(), __FUNCTION__, len);
fdp = (struct featureDescriptor *)
&configBuf[kConfigFeatureHeaderBytes];
do {
if (OSSwapBigToHostInt16(fdp->featureCode) ==
kIOATAPIFeatureRandomWrite)
{
*isWriteProtected = false;
break;
}
fdp = (struct featureDescriptor *)((char *)fdp +
sizeof(struct featureDescriptor) +
fdp->additionalLength);
}
while ( ((UInt8 *)fdp + sizeof(*fdp)) <= &configBuf[len] );
}
while (false);
IOFree((void *) configBuf, kConfigBufferSize);
return result;
}
IOReturn
IOATAPIDVDDrive::sendKey(IOMemoryDescriptor * buffer,
const DVDKeyClass keyClass,
const UInt8 agid,
const DVDKeyFormat keyFormat)
{
IOATACommand * cmd = 0;
IOReturn ret = kIOReturnNoMemory;
do {
assert(buffer);
cmd = atapiCommandSendKey(buffer, keyClass, agid, keyFormat);
if (cmd == 0)
break;
ret = syncExecute(cmd);
}
while (0);
if (cmd)
cmd->release();
return ret;
}
IOReturn
IOATAPIDVDDrive::reportKey(IOMemoryDescriptor * buffer,
const DVDKeyClass keyClass,
const UInt32 lba,
const UInt8 agid,
const DVDKeyFormat keyFormat)
{
IOATACommand * cmd = 0;
IOReturn ret = kIOReturnNoMemory;
do {
assert(buffer);
cmd = atapiCommandReportKey(buffer, keyClass, lba, agid, keyFormat);
if (cmd == 0)
break;
ret = syncExecute(cmd);
}
while (0);
if (cmd)
cmd->release();
return ret;
}