#include <exec/types.h>
#include "defs.h"

/* Handles all communication with DOS or the paging device */

static char rcsid [] = "$Id: pageio.c,v 3.6 95/12/16 18:36:42 Martin_Apel Exp $";

/* Params if PageDev == PD_FILE or PD_PSEUDO_PART */
PRIVATE BPTR PageFile;
PRIVATE LONG PageFileArg1;              /* Often used by packets */
PRIVATE struct MsgPort *ReplyPort;
PRIVATE struct MsgPort *PageFilePort;
PRIVATE struct FileInfoBlock *PagingInfo;
PRIVATE LONG BuffersAdded;

/* Data used for multi-page writing */
PRIVATE struct IOStdReq *MultiPageReq;
PRIVATE struct DosPacket *MultiPagePacket;
PRIVATE struct MsgPort *MultiPagePort;

#define CHUNKSIZE (512L * 1024L)        /* write 512 K in one go */

#define MIN(a,b) (((a)<(b))?(a):(b))

/*****************************************************************/

PRIVATE void AddBuffersForFile (void)

{
int rc;
LONG CurrentBuffers;
LONG DesiredBuffers;
ULONG DiskType;

if (GetDiskType (PartWithColon, &DiskType) != SUCCESS)
  return;

if (DiskType != ID_FFS_DISK && DiskType != ID_INTER_FFS_DISK)
  return;                     /* no additional buffers for other filesystems */

rc = AddBuffers (PartWithColon, 0L);

if (rc == FALSE)
  {
  PRINT_DEB ("AddBuffersForFile failed", 0L);
  return;
  }

if (rc == DOSTRUE)                 /* see doc for AddBuffers */
  CurrentBuffers = IoErr ();
else
  CurrentBuffers = rc;

DesiredBuffers = PartSize / 40 / 1024;    /* empirically determined */

PRINT_DEB ("CurrentBuffers = %ld", CurrentBuffers);
PRINT_DEB ("DesiredBuffers = %ld", DesiredBuffers);

if (DesiredBuffers > CurrentBuffers)
  {
  rc = AddBuffers (PartWithColon, DesiredBuffers - CurrentBuffers);
  if (rc == FALSE)
    return;

  if (rc == DOSTRUE)
    BuffersAdded = IoErr () - CurrentBuffers;
  else 
    BuffersAdded = rc - CurrentBuffers;
  
  PRINT_DEB ("Added %ld buffers", BuffersAdded);
  }
}

/*****************************************************************/

PRIVATE int OpenPageFileAsFile (void)

{
char *tmp;
int i;
ULONG size;

/* First try to open existing file, if none exists, open a new one.
 * C evaluation rules used.
 */
if (((PageFile = Open (CurrentConfig.PartOrFileName, MODE_OLDFILE)) == NULL) &&
    ((PageFile = Open (CurrentConfig.PartOrFileName, MODE_NEWFILE)) == NULL))
  {
  PRINT_DEB ("OpenPageFileAsFile: Couldn't open paging file", 0L);
  return (ERR_NO_PAGING_FILE);
  }

PRINT_DEB ("OpenPageFileAsFile: File opened successfully", 0L);

PageFilePort = DeviceProc (CurrentConfig.PartOrFileName);

PageFileArg1 = ((struct FileHandle*)BADDR (PageFile))->fh_Arg1;
PartSize = SetFileSize (PageFile, CurrentConfig.FileSize * 1024L * 1024L, 
                        OFFSET_BEGINNING);

PRINT_DEB ("OpenPageFileAsFile: After SetFileSize", 0L);

/* Not all handlers support SetFileSize */
if (PartSize != CurrentConfig.FileSize * 1024L * 1024L)
  {
  PRINT_DEB ("Couldn't make page file large enough", 0L);
  PRINT_DEB ("Trying to write desired file size", 0L);

  if (IoErr () == ERROR_DISK_FULL)
    return (ERR_NO_SPACE);

  size = Seek (PageFile, 0L, OFFSET_END);
  PRINT_DEB ("Current file size is %ld bytes", size);
  tmp = ((struct MemHeader*)(SysBase->MemList.lh_Head))->mh_Lower;

  /* Do it the slow, but secure way */
  while (size < CurrentConfig.FileSize * 1024L * 1024L)
    {
    if (Write (PageFile, tmp, CHUNKSIZE) != CHUNKSIZE)
      {
      PRINT_DEB ("Not enough space on volume", 0L);
      return (ERR_NO_SPACE);
      }
    size += CHUNKSIZE;
    }

  PRINT_DEB ("Managed to create page file", 0L);
  PartSize = CurrentConfig.FileSize * 1024L * 1024L;
  }

if ((ReplyPort = CreateMsgPort ()) == NULL)
  return (ERR_NOT_ENOUGH_MEM);

ReplySignal = ReplyPort->mp_SigBit;

if ((MultiPagePort = CreateMsgPort ()) == NULL)
  return (ERR_NOT_ENOUGH_MEM);

PRINT_DEB ("OpenPageFileAsFile: Created Ports", 0L);

for (i = 0; i < MAX_FAULTS; i++)
  {
  /* dp_Arg7 is used as a pointer from the packet to the corresponding
   * TrapStruct.
   */
  if ((TrapInfo[i].IOPacket = AllocDosObject (DOS_STDPKT, NULL)) == NULL)
    return (ERR_NOT_ENOUGH_MEM);

  ((struct DosPacket*)TrapInfo[i].IOPacket)->dp_Arg7 = (LONG)&(TrapInfo [i]);

  /* Another packet is needed for the seek operation, which has to precede
   * each read or write operation.
   */
  if ((TrapInfo[i].SeekPacket = AllocDosObject (DOS_STDPKT, NULL)) == NULL)
    return (ERR_NOT_ENOUGH_MEM);

  ((struct DosPacket*)TrapInfo[i].SeekPacket)->dp_Arg7 = (LONG)&(TrapInfo [i]);
  }

if ((MultiPagePacket = AllocDosObject (DOS_STDPKT, NULL)) == NULL)
  return (ERR_NOT_ENOUGH_MEM);

PRINT_DEB ("OpenPageFileAsFile: Initialized Packets", 0L);

AddBuffersForFile ();

PRINT_DEB ("OpenPageFileAsFile: Added buffers", 0L);

return (SUCCESS);
}

/*****************************************************************/

PRIVATE int OpenPageFileAsPartition (BOOL WritePermit)

{
int i;
char *buffer,
     *tmp;
struct EasyStruct MyRequest =
  {
  sizeof (struct EasyStruct),
  0,
  PROGNAME,
  NULL,
  NULL
  };

/* Reserve first 512 bytes for ID */
PartStart = PagingDevParams.low_cyl * PagingDevParams.heads *
            PagingDevParams.secs_per_track * 512L + 512;
PartSize = (PagingDevParams.high_cyl - PagingDevParams.low_cyl + 1) *
            PagingDevParams.heads * PagingDevParams.secs_per_track * 512L - 512;

if (((ReplyPort = CreateMsgPort ()) == NULL) || 
    ((MultiPagePort = CreateMsgPort ()) == NULL))
  return (ERR_NOT_ENOUGH_MEM);

for (i = 0; i < MAX_FAULTS; i++)
  {
  if ((TrapInfo[i].IOPacket = CreateIORequest (ReplyPort,
               sizeof (struct ExtIOReq))) == NULL)
    return (ERR_NOT_ENOUGH_MEM);

  /* Install back pointer from IORequest to TrapInfo */
  ((struct ExtIOReq*)TrapInfo[i].IOPacket)->TrapInfo = &(TrapInfo[i]);
  }

if ((MultiPageReq = CreateIORequest (MultiPagePort, sizeof (struct IOStdReq))) == NULL)
  return (ERR_NOT_ENOUGH_MEM);

if (OpenDevice (PagingDevParams.device->dd_Library.lib_Node.ln_Name,
                PagingDevParams.unit,
                (struct IORequest*)TrapInfo[0].IOPacket,
                PagingDevParams.flags))
  {
  char ErrorBuffer [80];
  sprintf (ErrorBuffer, GetVMMString (msgNoDevice),
           PagingDevParams.device->dd_Library.lib_Node.ln_Name);
  ReportError (ErrorBuffer, ERR_CONTINUE);
  return (ERR_MSG_POSTED);
  }

for (i = 1; i < MAX_FAULTS; i++)
  {
  ((struct ExtIOReq*)TrapInfo [i].IOPacket)->ioreq.io_Device =
                ((struct ExtIOReq*)TrapInfo [0].IOPacket)->ioreq.io_Device;
  ((struct ExtIOReq*)TrapInfo [i].IOPacket)->ioreq.io_Unit =
                    ((struct ExtIOReq*)TrapInfo [0].IOPacket)->ioreq.io_Unit;
  }

MultiPageReq->io_Device = ((struct ExtIOReq*)TrapInfo [0].IOPacket)->ioreq.io_Device;
MultiPageReq->io_Unit   = ((struct ExtIOReq*)TrapInfo [0].IOPacket)->ioreq.io_Unit;

if (WritePermit)
  {
  /* Allocate enough memory for a LINUX page. LINUX marks its swap-space
   * by a signature in the last bytes of the first page.
   */
  if ((buffer = DoOrigAllocMem (4096L, MEMF_PUBLIC)) == NULL)
    return (ERR_NOT_ENOUGH_MEM);

  ((struct IOStdReq*)TrapInfo[0].IOPacket)->io_Command = CMD_READ;
  ((struct IOStdReq*)TrapInfo[0].IOPacket)->io_Flags = 0;
  ((struct IOStdReq*)TrapInfo[0].IOPacket)->io_Length = 4096;
  ((struct IOStdReq*)TrapInfo[0].IOPacket)->io_Data = buffer;
  ((struct IOStdReq*)TrapInfo[0].IOPacket)->io_Offset = PartStart - 512;
  SendIO ((struct IORequest*)TrapInfo[0].IOPacket);
  WaitIO ((struct IORequest*)TrapInfo[0].IOPacket);
  if (((struct IOStdReq*)TrapInfo[0].IOPacket)->io_Error != 0)
    {
    FreeMem (buffer, 4096L);
    return (ERR_FAILED_IO);
    }

  if (strcmp (PROGNAME, buffer) != 0)
    {
    tmp = PartWithColon;
    MyRequest.es_TextFormat = GetVMMString (msgUnusedPartition);
    MyRequest.es_GadgetFormat = GetVMMString (msgUseCancel);
    if (strncmp (buffer + 4096 - 10, "SWAP-SPACE", 10) != 0 &&
        !CxParams->ForceOverwrite && 
        !EasyRequestArgs (NULL, &MyRequest, NULL, &tmp))
      {
      FreeMem (buffer, 4096L);
      return (ERR_MSG_POSTED);
      }
    strcpy (buffer, PROGNAME);
    ((struct IOStdReq*)TrapInfo[0].IOPacket)->io_Command = CMD_WRITE;
    ((struct IOStdReq*)TrapInfo[0].IOPacket)->io_Flags = 0;
    ((struct IOStdReq*)TrapInfo[0].IOPacket)->io_Length = 512;
    ((struct IOStdReq*)TrapInfo[0].IOPacket)->io_Data = buffer;
    ((struct IOStdReq*)TrapInfo[0].IOPacket)->io_Offset = PartStart - 512;
    SendIO ((struct IORequest*)TrapInfo[0].IOPacket);
    WaitIO ((struct IORequest*)TrapInfo[0].IOPacket);
    if (((struct IOStdReq*)TrapInfo[0].IOPacket)->io_Error != 0)
      {
      FreeMem (buffer, 4096L);
      return (ERR_FAILED_IO);
      }
    }

  FreeMem (buffer, 4096L);
  }
ReplySignal = ReplyPort->mp_SigBit;
return (SUCCESS);
}

/*****************************************************************/

PRIVATE int OpenPageFileAsPseudoPart (void)

{
ULONG first_block,
      last_block;
char *tmp;
int rc;

struct EasyStruct DeleteRequest =
  {
  sizeof (struct EasyStruct),
  0,
  PROGNAME,
  NULL,
  NULL
  };

if ((rc = IsValidFFSPartition ()) != SUCCESS)
  return (rc);

if ((PagingInfo = AllocDosObject (DOS_FIB, NULL)) == NULL)
  {
  PRINT_DEB ("Not enough memory for FileInfoBlock", 0L);
  return (ERR_NOT_ENOUGH_MEM);
  }

/* First try to open existing file, if none exists, open a new one.
 */
if ((PageFile = Lock (CurrentConfig.PartOrFileName, EXCLUSIVE_LOCK)) != NULL)
  {
  /* File exists. Check if its a valid pseudo-part */
  if (!Examine (PageFile, PagingInfo))
    {
    PRINT_DEB ("Couldn't find information about paging file", 0L);
    return (ERR_NOT_ENOUGH_MEM);   /* Error to complicated to tell the user */
    }

  if (PagingInfo->fib_DirEntryType > 0)
    {
    PRINT_DEB ("OpenPageFileAsPseudoPart: PageFile is a directory", 0L);
    return (ERR_FILE_IS_DIR);
    }

  if (PagingInfo->fib_Size == CurrentConfig.FileSize * 1024L * 1024L)
    {
    if (IsPseudoPart (PagingInfo->fib_DiskKey, &first_block, &last_block))
      {
      PRINT_DEB ("First block = %ld", first_block);
      PRINT_DEB ("Last block  = %ld", last_block);

      PartStart = (PagingDevParams.low_cyl * PagingDevParams.heads *
                   PagingDevParams.secs_per_track + first_block) * 512L;
      PartSize = (last_block - first_block + 1) * 512L;
      PRINT_DEB ("Found existing pseudopart. Start at %ld", PartStart);
      PRINT_DEB ("                           Size  is %ld", PartSize);
      return (SUCCESS);
      }
    }

  PRINT_DEB ("Found mismatching file. Deleting it", 0L);
  UnLock (PageFile);
  PageFile = NULL;       /* In case of an error not to be unlocked twice */

  tmp = CurrentConfig.PartOrFileName;
  DeleteRequest.es_TextFormat = GetVMMString (msgDeleteFile);
  DeleteRequest.es_GadgetFormat = GetVMMString (msgDeleteCancel);
  if (!CxParams->ForceOverwrite && !EasyRequestArgs (NULL, &DeleteRequest, NULL, &tmp))
    return (ERR_MSG_POSTED);

  /* No need to actually delete the file. It will be deleted as part
   * of the opening with MODE_NEWFILE.
   */
  }


/* Pagefile did not exist. Create a new one */
if ((PageFile = Open (CurrentConfig.PartOrFileName, MODE_NEWFILE)) == NULL)
  {
  PRINT_DEB ("OpenPageFileAsPseudoPart: Couldn't open paging file", 0L);
  return (ERR_NO_PAGING_FILE);
  }

Close (PageFile);
if ((PageFile = Lock (CurrentConfig.PartOrFileName, EXCLUSIVE_LOCK)) == NULL)
  {
  PRINT_DEB ("OpenPageFileAsPseudoPart: Internal error", 0L);
  return (ERR_INTERNAL);
  }

if (!Examine (PageFile, PagingInfo))
  {
  PRINT_DEB ("OpenPageFileAsPseudoPart: Can't examine new file", 0L);
  return (ERR_NOT_ENOUGH_MEM);     /* Error too complicated to tell the user */
  }

rc = CreatePseudoPart (PagingInfo->fib_DiskKey, &first_block, &last_block);
if (rc != SUCCESS)
  {
  PRINT_DEB ("OpenPageFileAsPseudoPart: Couldn't create pseudo partition", 0L);
  return (rc);
  }

PRINT_DEB ("First block = %ld", first_block);
PRINT_DEB ("Last block  = %ld", last_block);

PartStart = (PagingDevParams.low_cyl * PagingDevParams.heads *
             PagingDevParams.secs_per_track + first_block) * 512L;
PartSize = (last_block - first_block + 1) * 512L;
PRINT_DEB ("Created new pseudopart. Start at %ld", PartStart);
PRINT_DEB ("                        Size  is %ld", PartSize);
return (SUCCESS);
}

/*****************************************************************/

int OpenPageFile (void)

{
int rc;

switch (CurrentConfig.PageDev)
  {
  case PD_FILE: PRINT_DEB ("Opening pagefile as file", 0L);
                return (OpenPageFileAsFile ());

  case PD_PART: PRINT_DEB ("Opening pagefile as partition", 0L);
                return (OpenPageFileAsPartition (TRUE));

  case PD_PSEUDOPART: PRINT_DEB ("Opening pagefile as pseudopart", 0L);
                      if ((rc = OpenPageFileAsPartition (FALSE)) != SUCCESS)
                        return (rc );
                      return (OpenPageFileAsPseudoPart ());
#ifdef DEBUG
  default:      PRINT_DEB ("OpenPageFile: Unknown paging device", 0L);
                ColdReboot ();
                return (SUCCESS);       /* dummy */
#endif
  }
}

/*****************************************************************/

void ClosePageFile ()

{
int i;

switch (CurrentConfig.PageDev)
  {
  case PD_FILE: AddBuffers (PartWithColon, -BuffersAdded);
                for (i = 0; i < MAX_FAULTS; i++)
                  {
                  if (TrapInfo[i].IOPacket != NULL)
                    FreeDosObject (DOS_STDPKT, TrapInfo[i].IOPacket);
                  if (TrapInfo[i].SeekPacket != NULL)
                    FreeDosObject (DOS_STDPKT, TrapInfo[i].SeekPacket);
                  }

                if (MultiPagePacket != NULL)
                  FreeDosObject (DOS_STDPKT, MultiPagePacket);
              
                if (ReplyPort != NULL)
                  DeleteMsgPort (ReplyPort);

                if (MultiPagePort != NULL)
                  DeleteMsgPort (MultiPagePort);

                if (PageFile != NULL)
                  Close (PageFile);
                break;

  case PD_PSEUDOPART:
                if (PageFile != NULL)
                  UnLock (PageFile);

                if (PagingInfo != NULL)
                  FreeDosObject (DOS_FIB, PagingInfo);
                  
                /* fall through to device closing */

  case PD_PART: CloseDevice ((struct IORequest*)TrapInfo [0].IOPacket);
                for (i = 0; i < MAX_FAULTS; i++)
                  DeleteIORequest ((struct IORequest*)TrapInfo [i].IOPacket);

                DeleteIORequest ((struct IORequest*)MultiPageReq);

                DeleteMsgPort (ReplyPort);
                DeleteMsgPort (MultiPagePort);
                break;
#ifdef DEBUG
  default:      PRINT_DEB ("ClosePageFile: Unknown paging device", 0L);
                ColdReboot ();
#endif
  }
}

/*****************************************************************/

void WriteSinglePage (ULONG slot_num, struct TrapStruct *ThisTrap)

{
struct DosPacket *SeekPacket,
                 *WritePacket;

/* PRINT_DEB ("Writing page %ld", slot_num); */

PagesWritten++;

switch (CurrentConfig.PageDev)
  {
  case PD_FILE:
    WritePacket = (struct DosPacket*)ThisTrap->IOPacket;
    SeekPacket  = (struct DosPacket*)ThisTrap->SeekPacket;

    SeekPacket->dp_Port = ReplyPort;
    SeekPacket->dp_Action = ACTION_SEEK;
    SeekPacket->dp_Arg1 = PageFileArg1;
    SeekPacket->dp_Arg2 = slot_num << PAGESIZESHIFT;
    SeekPacket->dp_Arg3 = OFFSET_BEGINNING;
    SendPkt (SeekPacket, PageFilePort, ReplyPort);

    WritePacket->dp_Port = ReplyPort;
    WritePacket->dp_Action = ACTION_WRITE;
    WritePacket->dp_Arg1 = PageFileArg1;
    WritePacket->dp_Arg2 = ThisTrap->PhysAddr;
    WritePacket->dp_Arg3 = PAGESIZE;
    SendPkt (WritePacket, PageFilePort, ReplyPort);
    break;

  case PD_PART:
  case PD_PSEUDOPART:
    ((struct ExtIOReq*)ThisTrap->IOPacket)->ioreq.io_Command = CMD_WRITE;
    ((struct ExtIOReq*)ThisTrap->IOPacket)->ioreq.io_Flags = 0;
    ((struct ExtIOReq*)ThisTrap->IOPacket)->ioreq.io_Length = PAGESIZE;
    ((struct ExtIOReq*)ThisTrap->IOPacket)->ioreq.io_Data = (APTR)ThisTrap->PhysAddr;
    ((struct ExtIOReq*)ThisTrap->IOPacket)->ioreq.io_Offset = slot_num * PAGESIZE + PartStart;

    if (((struct ExtIOReq*)ThisTrap->IOPacket)->ioreq.io_Offset < PartStart ||
        ((struct ExtIOReq*)ThisTrap->IOPacket)->ioreq.io_Offset > PartStart + PartSize)
      FatalError (ERR_OUT_OF_BOUNDS);

    SendIO ((struct IORequest*)ThisTrap->IOPacket);
    break;

#ifdef DEBUG
  default:
    PRINT_DEB ("WritePage: Unknown paging device", 0L);
    ColdReboot ();
#endif
  }
}

/*****************************************************************/

void ReadSinglePage (ULONG slot_num, struct TrapStruct *ThisTrap)

{
struct DosPacket *SeekPacket,
                 *ReadPacket;

/* PRINT_DEB ("Reading page %ld", slot_num); */
PagesRead++;

switch (CurrentConfig.PageDev)
  {
  case PD_FILE:
    ReadPacket = (struct DosPacket*)ThisTrap->IOPacket;
    SeekPacket = (struct DosPacket*)ThisTrap->SeekPacket;

    SeekPacket->dp_Port = ReplyPort;
    SeekPacket->dp_Action = ACTION_SEEK;
    SeekPacket->dp_Arg1 = PageFileArg1;
    SeekPacket->dp_Arg2 = slot_num << PAGESIZESHIFT;
    SeekPacket->dp_Arg3 = OFFSET_BEGINNING;
    SendPkt (SeekPacket, PageFilePort, ReplyPort);

    ReadPacket->dp_Port = ReplyPort;
    ReadPacket->dp_Action = ACTION_READ;
    ReadPacket->dp_Arg1 = PageFileArg1;
    ReadPacket->dp_Arg2 = ThisTrap->PhysAddr;
    ReadPacket->dp_Arg3 = PAGESIZE;
    SendPkt (ReadPacket, PageFilePort, ReplyPort);
    break;

  case PD_PART:
  case PD_PSEUDOPART:
    ((struct ExtIOReq*)ThisTrap->IOPacket)->ioreq.io_Command = CMD_READ;
    ((struct ExtIOReq*)ThisTrap->IOPacket)->ioreq.io_Flags = 0;
    ((struct ExtIOReq*)ThisTrap->IOPacket)->ioreq.io_Length = PAGESIZE;
    ((struct ExtIOReq*)ThisTrap->IOPacket)->ioreq.io_Data = (APTR)ThisTrap->PhysAddr;
    ((struct ExtIOReq*)ThisTrap->IOPacket)->ioreq.io_Offset = slot_num * PAGESIZE + PartStart;

    if (((struct ExtIOReq*)ThisTrap->IOPacket)->ioreq.io_Offset < PartStart ||
        ((struct ExtIOReq*)ThisTrap->IOPacket)->ioreq.io_Offset > PartStart + PartSize)
      FatalError (ERR_OUT_OF_BOUNDS);

    SendIO ((struct IORequest*)ThisTrap->IOPacket);
    break;

#ifdef DEBUG
  default:
    PRINT_DEB ("ReadPage: Unknown paging device", 0L);
    ColdReboot ();
#endif
  }
}

/*****************************************************************/

void WriteMultiplePages (ULONG slot_num, void *buffer, ULONG num_pages)

{
/* Writes multiple pages to disk SYNCHRONOUSLY */
BOOL ErrorOccurred;
ULONG BytesToWrite;
ULONG Offset;
ULONG Location;

/* PRINT_DEB ("WriteMultiplePages called. %ld pages", num_pages); */
PagesWritten += num_pages;

switch (CurrentConfig.PageDev)
  {
  case PD_FILE:
    MultiPagePacket->dp_Port = MultiPagePort;
    MultiPagePacket->dp_Action = ACTION_SEEK;
    MultiPagePacket->dp_Arg1 = PageFileArg1;
    MultiPagePacket->dp_Arg2 = slot_num << PAGESIZESHIFT;
    MultiPagePacket->dp_Arg3 = OFFSET_BEGINNING;
    SendPkt (MultiPagePacket, PageFilePort, MultiPagePort);
    WaitPort (MultiPagePort);
    GetMsg (MultiPagePort);

    MultiPagePacket->dp_Port = MultiPagePort;
    MultiPagePacket->dp_Action = ACTION_WRITE;
    MultiPagePacket->dp_Arg1 = PageFileArg1;
    MultiPagePacket->dp_Arg2 = (long)buffer;
    MultiPagePacket->dp_Arg3 = num_pages * PAGESIZE;
    SendPkt (MultiPagePacket, PageFilePort, MultiPagePort);
    WaitPort (MultiPagePort);
    GetMsg (MultiPagePort);
    ErrorOccurred = (MultiPagePacket->dp_Res1 != num_pages * PAGESIZE);
    break;

  case PD_PART:
  case PD_PSEUDOPART:
    BytesToWrite = num_pages * PAGESIZE;
    Location = (ULONG)buffer;
    Offset = slot_num * PAGESIZE + PartStart;

    while (BytesToWrite > 0)
      {
      ULONG BytesThisTime = MIN (BytesToWrite, 
                    ALIGN_DOWN (PagingDevParams.MaxTransfer + 1, PAGESIZE));

      PRINT_DEB ("Transferring %ld bytes", BytesThisTime);

      MultiPageReq->io_Command = CMD_WRITE;
      MultiPageReq->io_Flags = 0;
      MultiPageReq->io_Length = BytesThisTime;
      MultiPageReq->io_Data = (APTR)Location;
      MultiPageReq->io_Offset = Offset;

      if (MultiPageReq->io_Offset < PartStart ||
          MultiPageReq->io_Offset > PartStart + PartSize)
        FatalError (ERR_OUT_OF_BOUNDS);

      SendIO ((struct IORequest*)MultiPageReq);
      WaitIO ((struct IORequest*)MultiPageReq);
      ErrorOccurred = (MultiPageReq->io_Error != 0) || 
                      (MultiPageReq->io_Actual != BytesThisTime);
      BytesToWrite -= BytesThisTime;
      Location += BytesThisTime;
      Offset += BytesThisTime;
      }

    break;

#ifdef DEBUG
  default:
    PRINT_DEB ("WriteMultiplePages: Unknown paging device", 0L);
    ColdReboot ();
#endif
  }

if (ErrorOccurred)
  {
  PRINT_DEB ("IO to paging device failed", 0L);
#ifdef DEBUG
  ColdReboot ();
#endif
  FatalError (ERR_FAILED_IO);
  }
}

/*****************************************************************/

void HandleReturn (void)

{
struct Message *RetMsg;
struct DosPacket *RetPacket;
struct TrapStruct *ThisFault;
struct ExtIOReq *IO;
BOOL WriteReady;
ULONG ErrorOccurred;

while ((RetMsg = GetMsg (ReplyPort)) != NULL)
  {
  switch (CurrentConfig.PageDev)
    {
    case PD_FILE:
      RetPacket = (struct DosPacket*)RetMsg->mn_Node.ln_Name;
      if (RetPacket->dp_Action == ACTION_SEEK)
        {
        /* Ignore the return of seek packets */
        continue;
        }

      ThisFault = (struct TrapStruct*)RetPacket->dp_Arg7;
      WriteReady = (RetPacket->dp_Action == ACTION_WRITE);
      ErrorOccurred = (RetPacket->dp_Res1 != PAGESIZE);
      break;

    case PD_PART:
    case PD_PSEUDOPART:
      IO = (struct ExtIOReq*)RetMsg;
      ThisFault = IO->TrapInfo;
      WriteReady = (IO->ioreq.io_Command == CMD_WRITE);
      ErrorOccurred = ((IO->ioreq.io_Error != 0) ||
                       (IO->ioreq.io_Actual != PAGESIZE));
      break;

#ifdef DEBUG
    default:
      PRINT_DEB ("HandleReturn: Unknown paging device", 0L);
      ColdReboot ();
#endif
    }

  if (ErrorOccurred)
    {
    PRINT_DEB ("IO to paging device failed", 0L);
#ifdef DEBUG
    ColdReboot ();
#endif
    FatalError (ERR_FAILED_IO);
    }

  if (WriteReady)
    WriteReturned (ThisFault);
  else
    ReadReturned (ThisFault);
  }
}
