[Back to DRIVES SWAG index] [Back to Main SWAG index] [Original]
{==========================================================================}
{ CDDrive - an interface to the CD-ROM device driver. }
{--------------------------------------------------------------------------}
{ Copyright (c) 1996 C.J.Rankin CJRankin@VossNet.Co.UK }
{ }
{ This unit provides an elementary interface for controlling a CD-ROM }
{ in TP6+. It has been left open-ended so that, if you wish, it can be }
{ extended to provide a more comprehensive range of CD-ROM functions. }
{ }
{ Note that in its current form, it will *only* recognise the first CD-ROM }
{ in the system- not a problem for most of us. }
{ }
{--------------------------------------------------------------------------}
{ }
{ Note: Windows 95 uses a protected mode CD-ROM driver and interface }
{ (MSCDEX v2.95). As a result, the standard way of requesting IOCTL }
{ functions of opening a file handle to the driver using Assign() }
{ and Reset() and then calling DOS services AX=$4402/$4403 DOES NOT }
{ WORK: DOS cannot open the CD-ROM driver in the DOS driver-list and }
{ so opens a new file on the hard disc instead. }
{ }
{==========================================================================}
{$A-,B-,D+,F-,G+,I-,L+,O+,R-,S-,V-,X+}
unit CDDrive;
interface
const
ex_CDROMUnknownUnit = 1;
ex_CDROMUnready = 2;
ex_CDROMUnknownCommand = 3;
ex_CDROMCRCError = 4;
ex_CDROMBadReqStrucLen = 5;
ex_CDROMBadSeek = 6;
ex_CDROMUnknownMedia = 7;
ex_CDROMUnknownSector = 8;
ex_CDROMReadError = 11;
ex_CDROMGeneralFailure = 12;
ex_CDROMMediaUnavailable = 14;
ex_CDROMInvalidDiscChange = 15;
const
MaxCDROM = 26;
type
TCDROMIndex = 1..MaxCDROM;
TCDROMLetter = 0..MaxCDROM-1;
TCDROMNumber = 0..MaxCDROM;
{ }
{ These are the explicit CD-ROM services provided by the unit }
{ }
procedure Eject; (* Eject CD-ROM *)
procedure Close; (* Close CD-ROM tray *)
procedure Lock; (* Lock CD-ROM drive *)
procedure Unlock; (* Unlock CD-ROM drive *)
procedure Reset; (* Reinitialise CD-ROM: i.e. as if disc was changed. *)
function GetNumberOfCDROMDrives: TCDROMNumber;
function GetCDROMVersion: word;
{ }
{ Templates for CD-ROM service requests. To implement another device }
{ or IOCTL request, create a descendant object with the requisite extra }
{ fields and pass it to the appropriate requestor function. }
{ }
type
PDeviceRequest = ^TDeviceRequest;
TDeviceRequest = object
HeaderLength: byte;
SubUnit: byte;
CommandCode: byte;
Status: word;
Reserved: array[1..8] of byte;
end;
PIOCTLRequest = ^TIOCTLRequest;
TIOCTLRequest = object
SubFn: byte;
end;
TRequestFunc = function(var Request: TDeviceRequest): word;
{ }
{ This constitutes the interface to the driver, enabling further functions }
{ to be added if desired. The return value is the driver's Status word: }
{ Status Word: Bit 15: Error flag. If set then Bits 0-7 are error code }
{ Bit 8: Request done flag. }
{ Bit 9: Device busy flag. }
{ }
function DriverRequest_v210(var Request: TDeviceRequest): word;
function DriverBasicRequest(var Request: TDeviceRequest): word;
function IOCTLInput(var Request: TIOCTLRequest; ReqLen: word): word;
function IOCTLOutput(var Request: TIOCTLRequest; ReqLen: word): word;
{ }
{ Errors are returned in this variable: the values are explained above ... }
{ }
var CDROMError: byte;
{ }
{ DriverRequest_v210 enables CD-ROM driver requests for MSCDEX v2.10+. For }
{ earlier versions of MSCDEX, DriverBasicRequest is used instead. The }
{ appropriate function is assigned to the following procedural variable. }
{ }
var DriverRequest: TRequestFunc;
implementation
uses DOS;
type
TDrvName = array[1..8] of char;
PCDROMDriver = ^TCDROMDriver;
TCDROMDriver = record
NextDriver: PCDROMDriver;
DeviceAttr: word;
StrategyEntryPoint: word;
INTEntryPoint: word;
DeviceName: TDrvName;
Reserved: word;
DriveLetter: TCDROMNumber;
Units: byte
end;
TCDROMDriveEntry = record
SubUnit: byte;
CDROMDriver: PCDROMDriver
end;
type
PDeviceIOCTLRequest = ^TDeviceIOCTLRequest;
TDeviceIOCTLRequest = object(TDeviceRequest)
Media: byte;
BufPtr: pointer;
BufLen: byte;
end;
type
TCDROMLock = (CDROM_Unlocked, CDROM_Locked);
PCDROMLockRequest = ^TCDROMLockRequest;
TCDROMLockRequest = object(TIOCTLRequest)
LockStatus: TCDROMLock;
end;
var Regs: Registers;
var NumberOfCDROMDrives: TCDROMNumber;
var CDROMDriveLetter: array[TCDROMIndex] of TCDROMLetter;
var CDROMDriverStrategyEntryPoint: procedure;
var CDROMDriverINTEntryPoint: procedure;
{ }
{ This is the interface to the CD-ROM driver. The assumption here is that }
{ we are only dealing with the first CD-ROM drive in the system- not a }
{ problem to us mere mortals who only HAVE one CD-ROM... }
{ }
function DriverRequest_v210(var Request: TDeviceRequest): word;
begin
with Regs do
begin
ax := $1510;
es := Seg(Request);
bx := Ofs(Request);
cx := CDROMDriveLetter[1]; (* Letter of CD-ROM drive: A=0,B=1,C=2... *)
Intr($2f,Regs)
end;
with Request do
begin
if Status and (1 shl 15) <> 0 then (* Check the error flag...*)
CDROMError := lo(Status) (* ... return the error *)
else
CDROMError := 0; (* ... return `no error' *)
DriverRequest_v210 := Status
end
end;
{ }
{ This method of calling CD-ROM driver functions should work for all }
{ versions of MSCDEX. Again, assume that there is only 1 CD-ROM ... }
{ }
function DriverBasicRequest(var Request: TDeviceRequest): word;
begin
with Request do
begin
SubUnit := 0; (* Only 1 CD-ROM, so it must be driver sub-unit 0 *)
asm
LES BX, [BP+OFFSET Request]
end;
CDROMDriverStrategyEntryPoint;
CDROMDriverINTEntryPoint;
if Status and (1 shl 15) <> 0 then (* Check the error flag...*)
CDROMError := lo(Status) (* ... return the error *)
else
CDROMError := 0; (* ... return `no error' *)
DriverBasicRequest := Status
end
end;
{ }
{ The CDROM driver can be asked to do LOTS of things; the IOCTL requests }
{ are only a very small part. In theory you could descend other buffers }
{ from TDeviceRequest, fill in the HeaderLength, CommandCode and any new }
{ fields and send them off to DriverRequest for execution... }
{ }
function IOCTLOutput(var Request: TIOCTLRequest; ReqLen: word): word;
var
DeviceRequestHeader: TDeviceIOCTLRequest;
begin (* Descendant of TDeviceRequest *)
with DeviceRequestHeader do
begin
HeaderLength := SizeOf(DeviceRequestHeader);
CommandCode := $0C;
BufPtr := @Request.SubFn; (* These fields added to TDeviceRequest *)
BufLen := ReqLen (* for IOCTL commands... *)
end;
IOCTLOutput := DriverRequest(DeviceRequestHeader)
end;
function IOCTLInput(var Request: TIOCTLRequest; ReqLen: word): word;
var
DeviceRequestHeader: TDeviceIOCTLRequest;
begin (* Descendant of TDeviceRequest *)
with DeviceRequestHeader do
begin
HeaderLength := SizeOf(DeviceRequestHeader);
CommandCode := $03;
BufPtr := @Request.SubFn; (* These fields added to TDeviceRequest *)
BufLen := ReqLen (* for IOCTL commands... *)
end;
IOCTLInput := DriverRequest(DeviceRequestHeader)
end;
{ }
{ Yes, I COULD have just put NumberOfCDROMDrives in the interface section, }
{ except that this number is important and I don't want users `fiddling' }
{ with it. :-) }
{ }
function GetNumberOfCDROMDrives: TCDROMNumber;
begin
GetNumberOfCDROMDrives := NumberOfCDROMDrives
end;
{ }
{ The mechanism used to perform device driver requests depends on the }
{ version of MSCDEX, so we need a method of finding this out. }
{ }
function GetCDROMVersion: word;
begin
with Regs do
begin
bx := 0;
ax := $150c;
Intr($2f,Regs);
GetCDROMVersion := bx (* Hi byte = Major version number *)
end (* Lo byte = Minor version number *)
end;
procedure Eject;
var
Request: TIOCTLRequest;
begin
if NumberOfCDROMDrives > 0 then
with Request do
begin
SubFn := 00; (* IOCTL command code for Eject... *)
IOCTLOutput(Request,SizeOf(Request))
end
end;
procedure Reset;
var
Request: TIOCTLRequest;
begin
if NumberOfCDROMDrives > 0 then
with Request do
begin
SubFn := 02; (* IOCTL command code to reset the CD-ROM drive *)
IOCTLOutput(Request,SizeOf(Request))
end
end;
procedure Close;
var
Request: TIOCTLRequest;
begin
if NumberOfCDROMDrives > 0 then
with Request do
begin
SubFn := 05; (* IOCTL command code to close the CD-ROM drive *)
IOCTLOutput(Request,SizeOf(Request))
end
end;
{
{ This routine seems to require a CD in the drive. Otherwise it returns }
{ CDROMError = 2 (on my machine anyway...) }
{ }
procedure Lock;
var
Request: TCDROMLockRequest;
begin
if NumberOfCDROMDrives > 0 then
with Request do
begin
SubFn := 01; (* ... locking the CDROM... *)
LockStatus := CDROM_Locked;
IOCTLOutput(Request,SizeOf(Request))
end
end;
procedure Unlock;
var
Request: TCDROMLockRequest;
begin
if NumberOfCDROMDrives > 0 then
with Request do
begin
SubFn := 01; (* ... and unlocking ... *)
LockStatus := CDROM_Unlocked;
IOCTLOutput(Request,SizeOf(Request))
end
end;
{ }
{ Store the Strategy and Interrupt Entry Points for the CD-ROM device }
{ driver... Again, assume that there is only 1 CD-ROM drive, and so }
{ SubUnit will always = 0 }
{ }
procedure SetUpEntryPoints;
var
CDDriveList: array[TCDROMIndex] of TCDROMDriveEntry;
begin
with Regs do
begin
ax := $1501;
es := Seg(CDDriveList);
bx := Ofs(CDDriveList);
Intr($2f,Regs)
end;
with CDDriveList[1] do
begin
@CDROMDriverStrategyEntryPoint :=
Ptr(Seg(CDROMDriver^),CDROMDriver^.StrategyEntryPoint);
@CDROMDriverINTEntryPoint :=
Ptr(Seg(CDROMDriver^),CDROMDriver^.INTEntryPoint)
end
end;
{ }
{ Initialisation code: neither the number of CD-ROM drives nor their drive }
{ letters will change during execution, so we shall identify the drives }
{ NOW and not do it again. }
{ }
begin
{ }
{ Get number of CD-ROMs in the system ... }
{ }
with Regs do
begin
ax := $1500;
bx := 0;
Intr($2f,Regs);
NumberOfCDROMDrives := bl;
{ }
{ Get the drive-letters for CD-ROMSs... There is an MSCDEX function to do }
{ this for v2.00+, viz AX=$150D INT $2F. However we are assuming only one }
{ CD-ROM and so the drive letter has already been determined. }
{ }
if NumberOfCDROMDrives > 0 then
begin
CDROMDriveLetter[1] := cl;
{ }
{ Determine the entry points for direct device-driver requests... }
{ }
SetUpEntryPoints;
{ }
{ Determine which mechanism to use for CD-ROM driver requests ... }
{ }
if GetCDROMVersion < 2*256 + 10 then
DriverRequest := DriverBasicRequest
else
DriverRequest := DriverRequest_v210
end
end
end.
{ ---------------------- DEMO PROGRAM ----------------------- }
{$A+,B-,D+,E-,F-,G+,I+,L+,N-,O-,R-,S-,V-,X+}
{$M 16384,0,655360}
program CDDemo;
uses CDDrive;
var
Version: word;
begin
if GetNumberOfCDROMDrives = 0 then
writeln( 'This machine has no CD-ROM drive.' )
else
begin
Version := GetCDROMVersion;
writeln( 'MSCDEX v', hi(Version), '.', lo(Version) );
Lock;
writeln( 'Lock: Error = ', CDROMError );
Unlock;
writeln( 'Unlock: Error = ', CDROMError );
Eject;
writeln( 'Eject: Error = ', CDROMError );
Close;
writeln( 'Close: Error = ', CDROMError );
Reset;
writeln( 'Reset: Error = ', CDROMError )
end
end.
[Back to DRIVES SWAG index] [Back to Main SWAG index] [Original]