IOSCSIDVDDrive.cpp [plain text]
#include <IOKit/IOLib.h>
#include <IOKit/IOReturn.h>
#include <IOKit/IOMemoryDescriptor.h>
#include <IOKit/scsi/IOSCSIDeviceInterface.h>
#include <IOKit/storage/scsi/IOSCSIDVDDrive.h>
#include <IOKit/storage/scsi/IOSCSIDVDDriveNub.h>
#include <IOKit/storage/IODVDTypes.h>
#include <libkern/OSByteOrder.h>
#define super IOSCSICDDrive
OSDefineMetaClassAndStructors(IOSCSIDVDDrive,IOSCSICDDrive)
const int kFeatureProfileList = 0x0000;
const int kFeatureCore = 0x0001;
const int kFeatureMorphing = 0x0002;
const int kFeatureRemovableMedium = 0x0003;
const int kFeatureRandomReadable = 0x0010;
const int kFeatureMultiRead = 0x001d;
const int kFeatureCDRead = 0x001e;
const int kFeatureDVDRead = 0x001f;
const int kFeatureRandomWrite = 0x0020;
const int kFeatureIncrStreamWrite = 0x0021;
const int kFeatureSectorErasable = 0x0022;
const int kFeatureFormattable = 0x0023;
const int kFeatureDefectManagement = 0x0024;
const int kFeatureWriteOnce = 0x0025;
const int kFeatureRestrictedOverwrite = 0x0026;
const int kFeatureDVDRWRestrictedOverwrite = 0x002c;
const int kFeatureCDTrackAtOnce = 0x002d;
const int kFeatureCDMastering = 0x002e;
const int kFeatureDVDR_RWWrite = 0x002f;
const int kFeaturePowerManagement = 0x0100;
const int kFeatureSMART = 0x0101;
const int kFeatureEmbeddedChanger = 0x0102;
const int kFeatureCDAudioAnalogPlay = 0x0103;
const int kFeatureMicrocodeUpgrade = 0x0104;
const int kFeatureTimeout = 0x0105;
const int kFeatureDVDCSS = 0x0106;
const int kFeatureRealTimeStreaming = 0x0107;
const int kFeatureLUNSerialNumber = 0x0108;
const int kFeatureDiskControlBlocks = 0x010a;
const int kFeatureDVDCPRM = 0x010b;
void
IOSCSIDVDDrive::checkConfig(UInt8 *buf,UInt32 actual)
{
struct featureHdr {
UInt32 totalLen;
UInt8 reserved1[2];
UInt16 currentProfile;
};
struct featureDescriptor {
UInt16 featureCode;
UInt8 versionPC;
UInt8 additionalLength;
};
int len;
struct featureHdr *fh;
struct featureDescriptor *fdp;
fh = (struct featureHdr *)buf;
len = OSSwapBigToHostInt32(fh->totalLen);
fdp = (struct featureDescriptor *)(&buf[8]);
do {
switch (OSSwapBigToHostInt16(fdp->featureCode)) {
case kFeatureDVDRead :
_isDVDDrive = true;
break;
case kFeatureDVDCSS :
_canDoCSS = true;
break;
}
fdp = (struct featureDescriptor *)((char *)fdp +
sizeof(struct featureDescriptor) +
fdp->additionalLength);
} while ((UInt8 *)fdp < &buf[len]);
}
IOReturn
IOSCSIDVDDrive::determineMediaType(void)
{
struct featureHdr {
UInt32 totalLen;
UInt8 reserved1[2];
UInt16 currentProfile;
};
struct featureDescriptor {
UInt16 featureCode;
UInt8 versionPC;
UInt8 additionalLength;
};
int len;
struct featureHdr *fh;
struct featureDescriptor *fdp;
IOReturn result;
UInt32 configSize;
UInt8 configBuf[kMaxConfigLength];
result = getConfiguration(configBuf,kMaxConfigLength,&configSize,true);
if (result != kIOReturnSuccess) {
IOLog("%s[IOSCSIDVDDrive]::determineMediaType; result = '%s'\n",
getName(),stringFromReturn(result));
return(result);
}
fh = (struct featureHdr *)configBuf;
len = OSSwapBigToHostInt32(fh->totalLen);
fdp = (struct featureDescriptor *)(&configBuf[8]);
_mediaType = kDVDMediaTypeUnknown;
do {
switch (OSSwapBigToHostInt16(fdp->featureCode)) {
case kFeatureCDRead :
_mediaType = kCDMediaTypeROM;
IOLog("%s[IOSCSIDVDDrive]::determineMediaType; media is %s.\n",getName(),"CD");
break;
case kFeatureDVDRead :
_mediaType = kDVDMediaTypeROM;
IOLog("%s[IOSCSIDVDDrive]::determineMediaType; media is %s.\n",getName(),"DVDROM");
break;
case kFeatureFormattable :
_mediaType = kDVDMediaTypeRAM;
IOLog("%s[IOSCSIDVDDrive]::determineMediaType; media is %s.\n",getName(),"DVDRam");
break;
case kFeatureRandomWrite :
_isWriteProtected = false;
IOLog("%s[IOSCSIDVDDrive]::determineMediaType; write-enabled.\n",getName());
break;
}
fdp = (struct featureDescriptor *)((char *)fdp +
sizeof(struct featureDescriptor) +
fdp->additionalLength);
} while ((UInt8 *)fdp < &configBuf[len]);
if (_mediaType == kDVDMediaTypeUnknown) {
IOLog("%s[IOSCSIDVDDrive]::determineMediaType; drive is empty.\n",getName());
}
return(kIOReturnSuccess);
}
bool
IOSCSIDVDDrive::deviceTypeMatches(UInt8 inqBuf[],UInt32 inqLen,SInt32 *score)
{
IOReturn result;
UInt8 type;
type = inqBuf[0] & 0x1f;
if (type == kIOSCSIDeviceTypeCDROM) {
result = getConfiguration(_configBuf,kMaxConfigLength,&_configSize,false);
if (result == kIOReturnSuccess) {
checkConfig(_configBuf,_configSize);
if (_isDVDDrive) {
*score = 16;
return(true);
} else {
return(false);
}
} else {
return(false);
}
} else {
return(false);
}
}
IOReturn
IOSCSIDVDDrive::doAsyncReadWrite(IOMemoryDescriptor *buffer,
UInt32 block,UInt32 nblks,
IOStorageCompletion completion)
{
return(standardAsyncReadWrite(buffer,block,nblks,completion));
}
IOReturn
IOSCSIDVDDrive::doFormatMedia(UInt64 byteCapacity)
{
return(standardFormatMedia(byteCapacity));
}
UInt32
IOSCSIDVDDrive::doGetFormatCapacities(UInt64 *capacities,UInt32 capacitiesMaxCount) const
{
if (capacitiesMaxCount > 0) {
*capacities = (UInt64)((UInt64)2600 * (UInt64)1048576);
return(1);
} else {
return(0);
}
}
IOReturn
IOSCSIDVDDrive::doSynchronizeCache(void)
{
return(standardSynchronizeCache());
}
IOReturn
IOSCSIDVDDrive::doSyncReadWrite(IOMemoryDescriptor *buffer,UInt32 block,UInt32 nblks)
{
return(standardSyncReadWrite(buffer,block,nblks));
}
IOReturn
IOSCSIDVDDrive::getConfiguration(UInt8 *buffer,UInt32 length,UInt32 *actualLength,bool current)
{
struct context *cx;
struct IOGCCdb *c;
IOSCSICommand *req;
SCSICDBInfo scsiCDB;
SCSIResults scsiResults;
IOReturn result;
cx = allocateContext();
if (cx == NULL) {
return(kIOReturnNoMemory);
}
req = cx->scsireq;
bzero( &scsiCDB, sizeof(scsiCDB) );
bzero(buffer,length);
c = (struct IOGCCdb *)(scsiCDB.cdb);
c->opcode = kIOSCSICommandGetConfiguration;
c->lunRT = 0;
if (current) {
c->lunRT |= 0x01;
}
c->startFeature_lo = 0;
c->startFeature_hi = 0;
c->len_hi = length >> 8;
c->len_lo = length & 0xff;
c->ctlbyte = 0;
scsiCDB.cdbLength = 10;
req->setCDB( &scsiCDB );
cx->memory = IOMemoryDescriptor::withAddress((void *)buffer,
length,
kIODirectionIn);
req->setPointers( cx->memory, length, false );
req->setPointers( cx->senseDataDesc, 255, false, true );
req->setTimeout( 5000 );
queueCommand(cx,kSync,getGetConfigurationPowerState());
result = simpleSynchIO(cx);
req->getResults(&scsiResults);
if (result == kIOReturnUnderrun) {
result = kIOReturnSuccess;
}
*actualLength = scsiResults.bytesTransferred;
deleteContext(cx);
return(result);
}
const char *
IOSCSIDVDDrive::getDeviceTypeName(void)
{
return(kIOBlockStorageDeviceTypeDVD);
}
UInt32
IOSCSIDVDDrive::getGetConfigurationPowerState(void)
{
return(kElectronicsOn);
}
UInt32
IOSCSIDVDDrive::getReportKeyPowerState(void)
{
return(kElectronicsOn);
}
UInt32
IOSCSIDVDDrive::getSendKeyPowerState(void)
{
return(kElectronicsOn);
}
UInt32
IOSCSIDVDDrive::getMediaType(void)
{
return(_mediaType);
}
bool
IOSCSIDVDDrive::init(OSDictionary * properties)
{
_isDVDDrive = false;
_canDoCSS = false;
_configSize = 0;
_mediaType = kDVDMediaTypeUnknown;
_isWriteProtected = true;
return(super::init(properties));
}
IOService *
IOSCSIDVDDrive::instantiateNub(void)
{
IOService *nub;
nub = new IOSCSIDVDDriveNub;
return(nub);
}
IOReturn
IOSCSIDVDDrive::reportKey(IOMemoryDescriptor *buffer,const DVDKeyClass keyClass,
const UInt32 lba,const UInt8 agid,const DVDKeyFormat keyFormat)
{
struct context *cx;
struct IORKCdb *c;
IOSCSICommand *req;
SCSICDBInfo scsiCDB;
IOReturn result;
cx = allocateContext();
if (cx == NULL) {
return(kIOReturnNoMemory);
}
req = cx->scsireq;
bzero( &scsiCDB, sizeof(scsiCDB) );
c = (struct IORKCdb *)(scsiCDB.cdb);
c->opcode = kIOSCSICommandReportKey;
if (keyFormat == kTitleKey) {
c->lba_0 = lba >> 24;
c->lba_1 = lba >> 16;
c->lba_2 = lba >> 8;
c->lba_3 = lba & 0xff;
}
c->keyClass = keyClass;
c->len_hi = buffer->getLength() >> 8;
c->len_lo = buffer->getLength() & 0xff;
c->agidKeyFormat = agid << 6 | keyFormat;
c->ctlbyte = 0;
scsiCDB.cdbLength = 10;
req->setCDB( &scsiCDB );
cx->memory = buffer;
req->setPointers( cx->memory, cx->memory->getLength(), false );
req->setPointers( cx->senseDataDesc, 255, false, true );
req->setTimeout( 5000 );
queueCommand(cx,kSync,getReportKeyPowerState());
result = simpleSynchIO(cx);
deleteContext(cx);
return(result);
}
IOReturn
IOSCSIDVDDrive::reportMediaState(bool *mediaPresent,bool *changed)
{
IOReturn result;
result = super::reportMediaState(mediaPresent,changed);
if (result != kIOReturnSuccess) {
IOLog("%s[IOSCSIDVDDrive]:: reportMediaState; result = '%s' from super\n",
getName(),stringFromReturn(result));
return(result);
}
if (*mediaPresent && *changed) {
result = determineMediaType();
}
return(result);
}
IOReturn
IOSCSIDVDDrive::reportWriteProtection(bool *isWriteProtected)
{
*isWriteProtected = _isWriteProtected;
return(kIOReturnSuccess);
}
IOReturn
IOSCSIDVDDrive::sendKey(IOMemoryDescriptor *buffer,const DVDKeyClass keyClass,
const UInt8 agid,const DVDKeyFormat keyFormat)
{
struct context *cx;
struct IOSKCdb *c;
IOSCSICommand *req;
SCSICDBInfo scsiCDB;
IOReturn result;
cx = allocateContext();
if (cx == NULL) {
return(kIOReturnNoMemory);
}
req = cx->scsireq;
bzero( &scsiCDB, sizeof(scsiCDB) );
c = (struct IOSKCdb *)(scsiCDB.cdb);
c->opcode = kIOSCSICommandSendKey;
c->keyClass = keyClass;
c->len_hi = buffer->getLength() >> 8;
c->len_lo = buffer->getLength() & 0xff;
c->agidKeyFormat = agid << 6 | keyFormat;
c->ctlbyte = 0;
scsiCDB.cdbLength = 10;
req->setCDB( &scsiCDB );
cx->memory = buffer;
req->setPointers( cx->memory, cx->memory->getLength(), false );
req->setPointers( cx->senseDataDesc, 255, false, true );
req->setTimeout( 5000 );
queueCommand(cx,kSync,getSendKeyPowerState());
result = simpleSynchIO(cx);
deleteContext(cx);
return(result);
}