
 PrintDoc v1.1 - John Baldwin "PFSDRIVE.PAS"                           Page 1

 {Driver API definition for PFS}
 
 {$A+,B-,D+,E-,F-,G+,I+,L+,N-,O-,P-,Q-,R-,S+,T-,V-,X+,Y+}
 Unit PFSDrivers;
 
 Interface
 
 Uses
    PFSTypes;
 
 {Some info here:
 
  Each drive is associated with a driver that is responsible for implementing
  the API for that drive. The drives are stored in an internal linked-list
  that is ordered on drive number.  Keep in mind that drives could potentially
  be removed from the list.  (If a driver unloads for instance)  Thus the
  drive numbers are stored with the drive information and never change.  As
  a result, they may not always correspond to the numbered position of the
  drive in the list, i.e. drive 5 may actually be the 2nd or 3rd drive in
  the list.
 
  When a driver is loaded, it is initialized and then instructed to detect
  hardware so that it can return the number of drives it will be responsible
  for.  It is then given that many drives in the global linked-list of drives.}

 
 
 (**************************************************************************)
 
 {Error Codes}
 
 Const
    PFS_ecOk           =$0000;
    PFS_ecFail         =$0001;
    PFS_ecBusy         =$0002;
    PFS_ecInvalidDriver=$0003;
    PFS_ecOutOfMemory  =$0004;
    PFS_ecProbeFail    =$0005;
    PFS_ecNoDrives     =$0006;
    PFS_ecDriveNotFound=$0007;
    PFS_ecInvalidDrive =$0008;
 
 (**************************************************************************)
 
 {A Drive}
 
 Const
    {Drive status}
    PFS_dsPresent    =$0001;
    PFS_dsDiskInDrive=$0002;
    PFS_dsReadable   =$0004;
    PFS_dsWritable   =$0008;
    PFS_dsBusy       =$0010;
 
 Type
    {Forward needed here}
    PPFS_Driver=^TPFS_Driver;
    {Drive Records}
    PPFS_Drive=^TPFS_Drive;
    TPFS_Drive=Record


 PrintDoc v1.1 - John Baldwin "PFSDRIVE.PAS"                           Page 2

            {00}  DriveID:TPFS_DriveID;  {only valid if Status and 3 = 3}
            {14}  TotalSectors:QWord;
            {1C}  DriveNumber:Word;      {global number}
            {1E}  DriverDriveNumber:Word; {number used by driver}
            {20}  Status:Word;
            {22}  Driver:PPFS_Driver;    {driver responsible for device}
            {26}  DriverInfo:Pointer;    {pointer to driver-specific stuff}
            {2A}  NextDrive:PPFS_Drive;
            {2E}
               End;
 
 (**************************************************************************)
 
 {Driver API}
 
    {Individual function types}
    TPFS_RWSectorFunction=Function (var Drive:TPFS_Drive; Sector:PPFS_Sector;
                                    SectorNumber:TPFS_LogicalSectorNumber):Word;
 
    TPFS_InitDriver=Function :Word;
    TPFS_DoneDriver=Function :Word;
    TPFS_DriveFunction=Function (var Drive:TPFS_Drive) :Word;
    TPFS_DetectHardware=Function (var NumDrives:Word) :Word;
    TPFS_Format=Function (var Drive:TPFS_Drive; StartSector,EndSector:
                          TPFS_LogicalSectorNumber):Word;
 
    {Actual API}
    TPFS_DriverFunctions=Record
                      {00}  InitDriver:TPFS_InitDriver;
                      {04}  DoneDriver:TPFS_DoneDriver;
                      {08}  InitDrive:TPFS_DriveFunction;
                      {0C}  DoneDrive:TPFS_DriveFunction;
                      {10}  DetectHardware:TPFS_DetectHardware;
                      {14}  ReadSector:TPFS_RWSectorFunction;
                      {18}  WriteSector:TPFS_RWSectorFunction;
                      {1C}  Format:TPFS_Format;
                      {20}
                         End;
    PPFS_DriverFunctions=^TPFS_DriverFunctions;
 
    {Driver Record}
    TPFS_Driver=Record
             {00}  Name:String[15];
             {10}  Version:Record
                              Major,Minor:Byte;
                           End;
             {12}  API:PPFS_DriverFunctions;
             {16}  NumDrives:Word;
             {18}
                End;
 
 (**************************************************************************)
 
 {Add a device driver to the system}
 Function PFS_RegisterDriver(Driver:PPFS_Driver):Word;
 {Remove a device driver and its drives}
 Function PFS_ShutdownDriver(Driver:PPFS_Driver):Word;
 {Wipe out the whole works}
 Function PFS_ShutdownSystem:Word;


 PrintDoc v1.1 - John Baldwin "PFSDRIVE.PAS"                           Page 3

 {Get the number of drives}
 Function PFS_NumSystemDrives:Word;
 {Check to see if Drive is a valid drive}
 Function PFS_ValidDrive(Drive:Word):Boolean;
 {Get the number of the last drive}
 Function PFS_MaxDriveNumber(var MaxDrive:Word):Word;
 {Get the drive record for drive DriveNumber}
 Function PFS_GetDriveRecord(DriveNumber:Word; var Drive:PPFS_Drive):Word;
 {Get the first drive that belongs to the device driver Driver}
 Function PFS_GetFirstDrive(Driver:PPFS_Driver; var FirstDrive:PPFS_Drive):Word;
 
 {Like the FindNext() function, uses the driver field from the previous
  Drive passed in to the find the next drive belonging to the same driver}
 Function PFS_GetNextDrive(var Drive:PPFS_Drive):Word;
 {Remove a drive from the system}
 Function PFS_RemoveDrive(Drive:Word):Word;
 
 Implementation
 
 (**************************************************************************)
 
 {Internal List of Drivers}
 
 Type
    PPFS_DriverList=^TPFS_DriverList;
    TPFS_DriverList=Record
                 {00}  Driver:PPFS_Driver;
                 {04}  NextDriver:PPFS_DriverList;
                 {08}
                    End;
 
 Const
    PFS_FirstDriver:PPFS_DriverList = Nil;
    PFS_FirstDrive:PPFS_Drive = Nil;
 
 (**************************************************************************)
 
 Function PFS_AddNewDrives(NumDrivesToAdd:Word; Driver:PPFS_Driver):Word;
 
 Var
    Loop,MaxDriveNumber,MaxDriverDriveNumber:Word;
    NewDrives,CurrentDrive:PPFS_Drive;
 
 Begin
    {make sure there are drives to add}
    If (NumDrivesToAdd=0) Then
       Begin
          PFS_AddNewDrives:=PFS_ecNoDrives;
          Exit;
       End;
 
    {Check for enough total memory - could still not have enough if too much
     fragmentation, but can't check that till later, this at least avoids
     some wasted time}
    If (MemAvail<NumDrivesToAdd*SizeOf(TPFS_Drive)) Then
       Begin
          PFS_AddNewDrives:=PFS_ecOutOfMemory;
          Exit;
       End;


 PrintDoc v1.1 - John Baldwin "PFSDRIVE.PAS"                           Page 4

 
    {Set up the MaxDriveNumber to set the drive numbers of the new drive
     from.  If there are no drives, then the Max is set to -1 so that the
     first drive will be drive 0}
    If (PFS_FirstDrive=Nil) Then
       MaxDriveNumber:=Word(Integer(-1))
    Else If (PFS_MaxDriveNumber(MaxDriveNumber)<>PFS_ecOk) Then
            Begin
               PFS_AddNewDrives:=PFS_ecFail;
               Exit;
            End;
 
    {Now start building temporary list of drives by building first drive
     in the list.}
    New(NewDrives);
    With NewDrives^ Do
       Begin
          Status:=0;
          NextDrive:=Nil;
          DriveNumber:=MaxDriveNumber+1;
       End;
    NewDrives^.Driver:=Driver;
    CurrentDrive:=NewDrives;
 
    {Now use a loop to add the rest of the drives to the temporary list}
    For Loop:=2 to NumDrivesToAdd Do
 
       {If we don't have enough memory, abort by disposing the temporary
        drives and retuurning an error}
       If (MaxAvail<SizeOf(TPFS_Drive)) Then
          Begin
             While (NewDrives<>Nil) Do
                Begin
                   CurrentDrive:=NewDrives;
                   NewDrives:=NewDrives^.NextDrive;
                   Dispose(CurrentDrive);
                End;
             PFS_AddNewDrives:=PFS_ecOutOfMemory;
             Exit;
          End
 
       {otherwise, go ahead and tack the new drive to the end of the list}
       Else
          Begin
             New(CurrentDrive^.NextDrive);
             CurrentDrive:=CurrentDrive^.NextDrive;
             With CurrentDrive^ Do
                Begin
                   Status:=0;
                   NextDrive:=Nil;
                   DriveNumber:=MaxDriveNumber+Loop;
                   DriverInfo:=Nil;
                End;
             CurrentDrive^.Driver:=Driver;
          End;
 
    {now tack the temporary list to the end of the global list}
    If (PFS_FirstDrive=Nil) Then
       PFS_FirstDrive:=NewDrives


 PrintDoc v1.1 - John Baldwin "PFSDRIVE.PAS"                           Page 5

    Else
       Begin
          CurrentDrive:=PFS_FirstDrive;
          While (CurrentDrive^.NextDrive<>Nil) Do
             CurrentDrive:=CurrentDrive^.NextDrive;
          CurrentDrive^.NextDrive:=NewDrives;
       End;
 
    {next, initialize all the drives}
    CurrentDrive:=NewDrives;
    While (CurrentDrive<>Nil) Do
       Begin
          Driver^.API^.InitDrive(CurrentDrive^);
          CurrentDrive:=CurrentDrive^.NextDrive;
       End;
 
    {Exit w/ no error}
    PFS_AddNewDrives:=PFS_ecOk;
 End;
 
 (**************************************************************************)
 
 Function PFS_RegisterDriver(Driver:PPFS_Driver):Word;
 
 Var
    TempDriver,NewDriver:PPFS_DriverList;
 
 Begin
    {Check for valid driver by making sure it has a valid API}
    If (Driver^.API=Nil) Then
       Begin
          PFS_RegisterDriver:=PFS_ecInvalidDriver;
          Exit;
       End;
 
    {Making sure we have enough memory}
    If (MaxAvail<SizeOf(TPFS_DriverList)) Then
       Begin
          PFS_RegisterDriver:=PFS_ecOutOfMemory;
          Exit;
       End;
 
    {Build the driver's DriverList entry; I did originally do this earlier,
     but there was the off chance that there would just be enough free
     memory for the drives, and thus, it would pass all the memory tests,
     yet be unable to allocate memory for the TPFS_DriveList record.  So, I
     moved it up here instead. }
    New(NewDriver);
    NewDriver^.Driver:=Driver;
    With NewDriver^ Do
       NextDriver:=Nil;
 
    {Initialize the Driver}
    Driver^.API^.InitDriver;
 
    {Now detect its hardware, if it fails return an error code}
    If (Driver^.API^.DetectHardware(NewDriver^.Driver^.NumDrives)<>PFS_ecOk) The
 n
       Begin


 PrintDoc v1.1 - John Baldwin "PFSDRIVE.PAS"                           Page 6

          PFS_RegisterDriver:=PFS_ecProbeFail;
          Dispose(NewDriver);
          Exit;
       End;
 
    {Make sure it has drives, otherwise don't do anything}
    If (Driver^.NumDrives=0) Then
       Begin
          PFS_RegisterDriver:=PFS_ecNoDrives;
          Dispose(NewDriver);
          Exit;
       End;
 
    {Now try to add the new drives}
    If (PFS_AddNewDrives(Driver^.NumDrives,Driver)<>PFS_ecOk) Then
       Begin
          PFS_RegisterDriver:=PFS_ecFail;
          Dispose(NewDriver);
          Exit;
       End;
 
    {Add the driver to the DriverList}
    If (PFS_FirstDriver=Nil) Then
       PFS_FirstDriver:=NewDriver
    Else
       Begin
          TempDriver:=PFS_FirstDriver;
          While (TempDriver^.NextDriver<>Nil) Do
             TempDriver:=TempDriver^.NextDriver;
          TempDriver^.NextDriver:=NewDriver;
       End;
 
    {Exit w/ no error}
    PFS_RegisterDriver:=PFS_ecOk;
 End;
 
 (**************************************************************************)
 
 Function PFS_ShutdownDriver(Driver:PPFS_Driver):Word;
 
 Var
    TempDrive:PPFS_Drive;
    Save:Word;
    TempDriver,SaveDriver:PPFS_DriverList;
 
 Begin
    If (Driver=Nil) or (PFS_FirstDriver=Nil) Then
       Begin
          PFS_ShutdownDriver:=PFS_ecFail;
          Exit;
       End;
    If (PFS_FirstDriver^.Driver=Driver) Then
       Begin
          SaveDriver:=PFS_FirstDriver;
          PFS_FirstDriver:=PFS_FirstDriver^.NextDriver;
       End
    Else
       Begin
          TempDriver:=PFS_FirstDriver;


 PrintDoc v1.1 - John Baldwin "PFSDRIVE.PAS"                           Page 7

          While ((TempDriver^.NextDriver<>Nil) and
             (TempDriver^.NextDriver^.Driver<>Driver)) Do
                TempDriver:=TempDriver^.NextDriver;
          If (TempDriver^.NextDriver=Nil) Then
             Begin
                PFS_ShutdownDriver:=PFS_ecFail;
                Exit;
             End;
          SaveDriver:=TempDriver^.NextDriver;
          TempDriver^.NextDriver:=SaveDriver^.NextDriver;
       End;
    PFS_GetFirstDrive(Driver,TempDrive);
    While (TempDrive<>Nil) Do
       Begin
          Save:=TempDrive^.DriveNumber;
          PFS_GetNextDrive(TempDrive);
          PFS_RemoveDrive(Save);
       End;
    Driver^.API^.DoneDriver;
    Dispose(SaveDriver);
 End;
 
 (**************************************************************************)
 
 Function PFS_ShutdownSystem:Word;
 
 Var
    Save:PPFS_DriverList;
 
 Begin
    While (PFS_FirstDriver<>Nil) Do
       {Keep shutting down the first driver until no more drivers left}
       PFS_ShutdownDriver(PFS_FirstDriver^.Driver);
    {Exit w/ no error}
    PFS_ShutdownSystem:=PFS_ecOk;
 End;
 
 (**************************************************************************)
 
 Function PFS_NumSystemDrives:Word;
 
 Var
    NumTotalDrives:Word;
    TempDrive:PPFS_Drive;
 
 Begin
    {Start at beginning of list with no drives counted}
    NumTotalDrives:=0;
    TempDrive:=PFS_FirstDrive;
    While (TempDrive<>Nil) Do
       Begin
          {For each drive do the following:}
 
          {Update the number of total drives}
          Inc(NumTotalDrives);
          {Move on to the next driver}
          TempDrive:=TempDrive^.NextDrive;
       End;
    {Return the number of drives}


 PrintDoc v1.1 - John Baldwin "PFSDRIVE.PAS"                           Page 8

    PFS_NumSystemDrives:=NumTotalDrives;
 End;
 
 (**************************************************************************)
 
 Function PFS_ValidDrive(Drive:Word):Boolean;
 
 Var
    TempDrive:PPFS_Drive;
 
 Begin
    {Start at the beginning of the list}
    TempDrive:=PFS_FirstDrive;
    While (TempDrive<>Nil) Do
       Begin
          {For each drive:}
 
          {If the DriverNumber is beyond the drive number then we
           haven't found a valid drive but we have moved past it, so
           return false}
          If (TempDrive^.DriveNumber>Drive) Then
             Begin
                PFS_ValidDrive:=False;
                Exit;
             End;
          {Otherwise, if the DriverNumber equals Drive then return true.}
          If (TempDrive^.DriveNumber=Drive) Then
                 Begin
                    PFS_ValidDrive:=True;
                    Exit;
                 End;
          {Move on to the next driver}
          TempDrive:=TempDrive^.NextDrive;
       End;
    {If we get here, we didn't find the drive to be valid, so return false}
    PFS_ValidDrive:=False;
 End;
 
 (**************************************************************************)
 
 {You should call PFS_NumSystemDrives() first before to make sure there is
  at least 1 drive.  If there are no drives, then the result of this
  function is undefined.}
 
 Function PFS_MaxDriveNumber(var MaxDrive:Word):Word;
 
 Var
    TempDrive:PPFS_Drive;
 
 Begin
    MaxDrive:=0;
    If (PFS_FirstDrive=Nil) Then
       Begin
          PFS_MaxDriveNumber:=PFS_ecNoDrives;
          Exit;
       End;
    TempDrive:=PFS_FirstDrive;
    While (TempDrive^.NextDrive<>Nil) Do
       TempDrive:=TempDrive^.NextDrive;


 PrintDoc v1.1 - John Baldwin "PFSDRIVE.PAS"                           Page 9

    MaxDrive:=TempDrive^.DriveNumber;
    PFS_MaxDriveNumber:=PFS_ecOk;
 End;
 
 (**************************************************************************)
 
 Function PFS_GetDriveRecord(DriveNumber:Word; var Drive:PPFS_Drive):Word;
 
 Begin
    Drive:=PFS_FirstDrive;
    While ((Drive<>Nil) and (Drive^.DriveNumber<DriveNumber)) Do
       Drive:=Drive^.NextDrive;
    If ((Drive=Nil) or (Drive^.DriveNumber<>DriveNumber)) Then
       PFS_GetDriveRecord:=PFS_ecDriveNotFound
    Else
       PFS_GetDriveRecord:=PFS_ecOk;
 End;
 
 (**************************************************************************)
 
 Function PFS_GetFirstDrive(Driver:PPFS_Driver; var FirstDrive:PPFS_Drive):Word;
 
 
 Begin
    FirstDrive:=PFS_FirstDrive;
    If (Driver=Nil) Then {not really needed, still works w/o}
       Begin
          PFS_GetFirstDrive:=PFS_ecFail;
          Exit;
       End;
    While ((FirstDrive<>Nil) and (FirstDrive^.Driver<>Driver)) Do
       FirstDrive:=FirstDrive^.NextDrive;
    PFS_GetFirstDrive:=PFS_ecOk;
 End;
 
 (**************************************************************************)
 
 Function PFS_GetNextDrive(var Drive:PPFS_Drive):Word;
 
 Var
    SaveDriver:PPFS_Driver;
 
 Begin
    If (Drive=Nil) Then
       Begin
          PFS_GetNextDrive:=PFS_ecFail;
          Exit;
       End;
    SaveDriver:=Drive^.Driver;
    Drive:=Drive^.NextDrive; {start looking at the next drive}
    While ((Drive<>Nil) and (Drive^.Driver<>SaveDriver)) Do
       Drive:=Drive^.NextDrive;
    PFS_GetNextDrive:=PFS_ecOk;
 End;
 
 (**************************************************************************)
 
 Function PFS_RemoveDrive(Drive:Word):Word;
 


 PrintDoc v1.1 - John Baldwin "PFSDRIVE.PAS"                           Page 10

 Var
    SaveDrive,TempDrive:PPFS_Drive;
 
 Begin
    If (PFS_FirstDrive=Nil) Then
       Begin
          PFS_RemoveDrive:=PFS_ecNoDrives;
          Exit;
       End;
    If (PFS_FirstDrive^.DriveNumber=Drive) Then
       Begin
          SaveDrive:=PFS_FirstDrive;
          PFS_FirstDrive:=PFS_FirstDrive^.NextDrive;
       End
    Else
       Begin
          TempDrive:=PFS_FirstDrive;
          While ((TempDrive^.NextDrive<>Nil) and
             (TempDrive^.NextDrive^.DriveNumber<>Drive)) Do
                TempDrive:=TempDrive^.NextDrive;
          If (TempDrive^.NextDrive=Nil) Then
             Begin
                PFS_RemoveDrive:=PFS_ecDriveNotFound;
                Exit;
             End;
          SaveDrive:=TempDrive^.NextDrive;
          TempDrive^.NextDrive:=SaveDrive^.NextDrive;
       End;
    SaveDrive^.Driver^.API^.DoneDrive(SaveDrive^);
    Dispose(SaveDrive);
    PFS_RemoveDrive:=PFS_ecOk;
 End;
 
 End.
 
 
 

