// ----------------------------------------------------------------------------
//
//      Copyright (c) 1992  Olivetti
//
//      File:           omf.c
//
//      Description:    this code implements the omf protocol as defined
//                      in the ARC-EISA addendum.
//
// ----------------------------------------------------------------------------

#include "fwp.h"
#include "oli2msft.h"
#include "arceisa.h"
#include "inc.h"
#include "string.h"
#include "debug.h"

extern  BL_FILE_TABLE BlFileTable [BL_FILE_TABLE_SIZE];

#define DEVICE_DEVICE 0xDEAD


// ----------------------------------------------------------------------------
//  Declare Function Prototypes
// ----------------------------------------------------------------------------

ARC_STATUS
OmfOpen
    (
    IN PCHAR OpenPath,
    IN OPEN_MODE OpenMode,
    IN OUT PULONG FileId
    );

ARC_STATUS
OmfClose
    (
    IN ULONG FileId
    );

ARC_STATUS
OmfMount
    (
    IN PCHAR            MountPath,
    IN MOUNT_OPERATION  Operation
    );

ARC_STATUS
OmfRead
    (
    IN  ULONG   FileId,
    IN  PVOID   Buffer,
    IN  ULONG   Length,
    OUT PULONG  Count
    );

ARC_STATUS
OmfWrite
    (
    IN  ULONG  FileId,
    IN  PVOID  Buffer,
    IN  ULONG  Length,
    OUT PULONG Count
    );

ARC_STATUS
OmfGetReadStatus
    (
    IN ULONG FileId
    );

ARC_STATUS
OmfSeek
    (
    IN ULONG            FileId,
    IN PLARGE_INTEGER   Offset,
    IN SEEK_MODE        SeekMode
    );

ARC_STATUS
OmfGetFileInformation
    (
    IN  ULONG FileId,
    OUT PFILE_INFORMATION Buffer
    );

ARC_STATUS
OmfSetFileInformation
    (
    IN ULONG FileId,
    IN ULONG AttributeFlags,
    IN ULONG AttributeMask
    );

ARC_STATUS
OmfGetDirectoryEntry
    (
    IN  ULONG           FileId,
    IN  DIRECTORY_ENTRY *DirEntry,
    IN  ULONG           NumberDir,
    OUT PULONG          CountDir
    );

ARC_STATUS
OmfFileOpen
    (
    IN PCHAR            OpenPath,
    IN OPEN_MODE        OpenMode,
    IN OUT PULONG       FileId
    );

ARC_STATUS
OmfFileClose
    (
    IN ULONG FileId
    );

ARC_STATUS
OmfFileMount
    (
    IN PCHAR            MountPath,
    IN MOUNT_OPERATION  Operation
    );

ARC_STATUS
OmfFileRead
    (
    IN  ULONG  FileId,
    IN  PVOID  Buffer,
    IN  ULONG  Length,
    OUT PULONG Count
    );

ARC_STATUS
OmfFileWrite
    (
    IN  ULONG  FileId,
    IN  PVOID  Buffer,
    IN  ULONG  Length,
    OUT PULONG Count
    );

ARC_STATUS
OmfFileGetReadStatus
    (
    IN ULONG FileId
    );

ARC_STATUS
OmfFileSeek
    (
    IN ULONG            FileId,
    IN PLARGE_INTEGER   Offset,
    IN SEEK_MODE        SeekMode
    );

ARC_STATUS
OmfFileGetFileInformation
    (
    IN  ULONG FileId,
    OUT PFILE_INFORMATION Buffer
    );

ARC_STATUS
OmfFileSetFileInformation
    (
    IN ULONG FileId,
    IN ULONG AttributeFlags,
    IN ULONG AttributeMask
    );

ARC_STATUS
OmfFileGetDirectoryEntry
    (
    IN  ULONG           FileId,
    IN  DIRECTORY_ENTRY *DirEntry,
    IN  ULONG           NumberDir,
    OUT PULONG          CountDir
    );

BOOLEAN
MaxOmfFatFiles
    (
    IN OUT PCHAR MasterName,
    IN PCHAR     CheckedName
    );

BOOLEAN
OmfFileNameValidate
    (
    IN  PCHAR OpenPath,
    OUT PCHAR OmfFileName,
    OUT PCHAR OmfType
    );

BOOLEAN
CmpOmfFiles
    (
    IN PCHAR        OmfFileName,
    IN CHAR         OmfType,
    IN POMF_DIR_ENT POmfDirEnt
    );

BOOLEAN
OmfHeaderValidate
    (
    IN ULONG FileId
    );

BOOLEAN
OmfFileValidate
    (
    IN ULONG FileId
    );

VOID
ConvertOmfDirToArcDir
    (
    IN POMF_DIR_ENT     POmfDirEnt,
    OUT DIRECTORY_ENTRY *PArcDirEnt
    );

BOOLEAN
FwGetEisaId
    (
    IN  PCHAR  PathName,
    OUT PCHAR  EisaId,
    OUT PUCHAR IdInfo
    );

VOID
FwUncompressEisaId
    (
    IN  PUCHAR CompEisaId,
    OUT PUCHAR UncompEisaId
    );

BOOLEAN
FwGetEisaBusIoCpuAddress
    (
    IN  PCHAR  EisaPath,
    OUT PVOID *IoBusAddress
    );

PCHAR
FwGetEnvironmentVariable
    (
    IN PCHAR Variable
    );

PCONFIGURATION_COMPONENT
FwGetComponent
    (
    IN PCHAR Pathname
    );

LARGE_INTEGER
RtlConvertUlongToLargeInteger
    (
    IN ULONG UnsignedInteger
    );

LARGE_INTEGER
RtlLargeIntegerAdd
    (
    IN LARGE_INTEGER Addend1,
    IN LARGE_INTEGER Addend2
    );

ARC_STATUS
FwFileIdChecksum
    (
    IN ULONG FileId,
    IN LARGE_INTEGER StartingOffset,
    IN ULONG Length,
    IN OUT PUCHAR Checksum
    );

BOOLEAN
FwGetMnemonicKey
    (
    IN PCHAR  Path,
    IN PCHAR  Mnemonic,
    IN PULONG Key
    );

BOOLEAN
FwGetNextMnemonic
    (
    IN  PCHAR Path,
    IN  PCHAR Mnemonic,
    OUT PCHAR NextMnemonic
    );

BOOLEAN
FwGetMnemonicPath
    (
    IN  PCHAR Path,
    IN  PCHAR Mnemonic,
    OUT PCHAR MnemonicPath
    );

BOOLEAN
GetNextPath
    (
    IN OUT PCHAR *PPathList,
    OUT PCHAR     PathTarget
    );


// ----------------------------------------------------------------------------
//      Global Data
// ----------------------------------------------------------------------------

//
// Omf entry table.
// This is a structure that provides entry to the OMF system procedures.
// It is exported when a OMF is opened as a device.
//

BL_DEVICE_ENTRY_TABLE OmfEntryTable =
    {
        OmfClose,
        OmfMount,
        OmfOpen,
        OmfRead,
        OmfGetReadStatus,
        OmfSeek,
        OmfWrite,
        OmfGetFileInformation,
        OmfSetFileInformation,
        OmfGetDirectoryEntry
    };


//
// Omf file entry table.
// This is a structure that provides entry to the OMF file system procedures.
// It is exported when a OMF file is opened.
//

BL_DEVICE_ENTRY_TABLE OmfFileEntryTable =
    {
        OmfFileClose,
        OmfFileMount,
        OmfFileOpen,
        OmfFileRead,
        OmfFileGetReadStatus,
        OmfFileSeek,
        OmfFileWrite,
        OmfFileGetFileInformation,
        OmfFileSetFileInformation,
        OmfFileGetDirectoryEntry
    };







// ----------------------------------------------------------------------------
// PROCEDURE:   OmfOpen:
//
// DESCRIPTION: The routine opens the newest version/revision of OMF for
//              the specified Product ID.
//
//              a) It reads the EISAROM bit of the <device> component of
//                 the Path argument on the EISA bus spcified by the key of
//                 the <adapter> component of Path argument.
//              b) If this bit is one, then a byte checksum is performed on
//                 the ROM-based OMF header and OMF directory.
//                 The Product ID, Firmware Version and Revision fields
//                 values are retained for the next operations.
//              c) Next, the partitions, specified by the FWSearchPath
//                 environment variable are searched for files of the
//                 following form in the \eisa\omf  subdirectory:
//
//                        <device>"V".LF"R"
//
//                        <device> = EISA Product ID (7 chars)
//                             "V" = version  ([0-9],[A-Z])
//                             "R" = revision ([0-9],[A-Z])
//
//              d) The OMF FAT file (or ROM) with a greater value of version-
//                 revision and with a correct checksum is used.
//              f) Initialize some data structures
//
//              When Open is used to access an OMF as a raw device, all input
//              functions are permitted.  These functions include Read, Seek
//              and Close.  Read and Seek are bounded by the current OMF size.
//              For example, to read the OMF of a serial options board that was
//              in slot 3 of EISA bus 0 as raw device, create and Open a path
//              as follows:     eisa(0)serial(3)omf() .
//
// ARGUMENTS:   OpenPath        Supplies a pointer to a zero terminated device
//                              path name.
//              OpenMode        Supplies the mode of the open.
//              FileId          Supplies a pointer to a variable that specifies
//                              the file table entry that is to be filled in
//                              if the open is successful.
//
// RETURN:      ESUCCESS        is returned if the open operation is successful.
//              ENODEV          the device portion of the pathname is invalid
//
//
// ASSUMPTIONS:
//
// CALLS:
//
// GLOBALS:
//
// NOTES:
// ----------------------------------------------------------------------------
//

ARC_STATUS
OmfOpen
    (
    IN PCHAR OpenPath,
    IN OPEN_MODE OpenMode,
    IN OUT PULONG FileId
    )
{
    //
    // initialize local variables
    //

    LARGE_INTEGER Offset;               // large interger for seek operations
    ULONG SlotNumber;                   // eisa slot number
    CHAR EisaCtrlMnemonic[ MAX_MNEMONIC_LEN +1 ]; // eisa ctrl mnemonic name
    UCHAR IdInfo;                       // info id byte
    PVOID IoBusAddress;                 // eisa I/O base address (virtual)
    PUCHAR SlotAddress;                 // I/O address
    BOOLEAN OmfRomOk=FALSE;             // OMF ROM initialization flag
    BOOLEAN OmfFatFileOk=FALSE;         // OMF FAT file initialization flag
    ULONG Count;                        // # of bytes transferred
    PCHAR PFwSearchPath;                // system partitions list pointer
    CHAR OmfPathName[MAX_PATH_LEN +1];  // directory+subdirectory+file name
    CHAR OmfPathBuffer[MAX_PATH_LEN +1] = {'\0'}; // OMF path name buffer
    CHAR OmfFatFileName[]="        .LF "; // omf name (FAT format)

    ULONG OmfSubdirId;                  // file Id of \eisa\omf subdirectory
    POMF_HEADER_CONTEXT POmfHeaderContext; // OMF header context pointer
    POMF_HDR POmfHeader;                // pointer to OMF ROM/FAT file header
    DIRECTORY_ENTRY DirEntBuffer;        // ARC directory entry

    PRINTDBG("OmfOpen\n\r");            // debug support

    //
    // this is a read only device
    //

    if  ( OpenMode != ArcOpenReadOnly )
    {
        return EROFS;
    }

    //
    // The path must have the following form :  eisa(x)"mnemonic"(y)omf()
    // Get EISA ID, slot number and its I/O address space.
    //

    if  ( !FwGetNextMnemonic( OpenPath, "eisa", EisaCtrlMnemonic )
          ||
          !FwGetMnemonicKey( OpenPath, EisaCtrlMnemonic, &SlotNumber )
          ||
          !FwGetMnemonicPath( OpenPath, EisaCtrlMnemonic, OmfPathName )
          ||
          !FwGetEisaId( OmfPathName, OmfFatFileName, &IdInfo )
          ||
          !FwGetMnemonicPath( OpenPath,"eisa",OmfPathName )
          ||
          !FwGetEisaBusIoCpuAddress( OmfPathName, &IoBusAddress ) )
    {
        return ENODEV;
    }

    //
    // Initialize some variables
    //

    POmfHeaderContext   = &BlFileTable[ *FileId ].u.OmfHeaderContext;
    POmfHeader          = &POmfHeaderContext->OmfHeader;
    SlotAddress         = (PUCHAR)IoBusAddress + SlotNumber * 0x1000;
    Offset.LowPart      = 0;                    // seek operation
    Offset.HighPart     = 0;                    // seek operation

    //
    // Check EISAROM bit of the Expansion Board Control Bits field.
    // If the EISA ID is readable, the controller supports the EISA extensions
    //

    // DEBUG-DEBUG :    for now skip the OMF ROM check because
    //                  at the moment there isn't any board available
    //                  with an OMF ROM to test the routine.

    if (0)
    //if ( !(IdInfo & CFG_UNREADABLE_ID)  &&
    //       READ_REGISTER_UCHAR( SlotAddress + EXPANSION_BOARD_CTRL_BITS ) &
    //                                  EISAROMBIT )
    {
        //
        // initialize some fields to direct the OmfRead to the ROM
        //

        POmfHeaderContext->RomIndex = (PULONG)(SlotAddress+ROMINDEX);
        POmfHeaderContext->RomRead  = (PULONG)(SlotAddress+ROMREAD);
        POmfHeaderContext->FileId   = DEVICE_DEVICE;
        POmfHeader->FwSize          = 0;        // max length simulation

        //
        // check OMF ROM header, copy it in the OmfHeaderContext structure
        //

        if  ( !OmfSeek( *FileId, &Offset, SeekAbsolute)
              &&
              !OmfRead( *FileId,
                        (PVOID)POmfHeader,
                        (ULONG)sizeof(OMF_HDR),
                        &Count )
              &&
              OmfHeaderValidate( *FileId ) )
        {
            //
            // copy version and revision
            //

            ((POMF_FAT_FILE_NAME)OmfFatFileName)->Version  =
                                                        POmfHeader->FwVersion;
            ((POMF_FAT_FILE_NAME)OmfFatFileName)->Revision =
                                                        POmfHeader->FwRevision;
            //
            // OMF ROM initialization has been done
            //

            OmfRomOk=TRUE;
        }
    }

    //
    // Set the current FileId to "open" so that calls to FwOpen won't use
    // this FileId.  This will get reset later.
    //

    BlFileTable[ *FileId ].Flags.Open = 1;

    //
    // check now if there is any file that upgrate the OFM ROM firmware.
    // the partitions, specified by the FWSearchPath environment variable,
    // are searched for files of the following form in the \eisa\omf
    // subdirectory:
    //                    <device>"V".LF"R"
    //
    //                    <device> = EISA Product ID (7 chars)
    //                         "V" = version  ([0-9],[A-Z])
    //                         "R" = revision ([0-9],[A-Z])
    //

    if  ( (PFwSearchPath = FwGetEnvironmentVariable("FWSearchPath")) != NULL )
    {
        //
        // check all the directories specified by FWSearchPath
        //

        while ( GetNextPath( &PFwSearchPath, OmfPathName ))
        {
            //
            // open the directory
            //

            strcat(OmfPathName, "\\eisa\\omf" );

            if ( FwOpen( OmfPathName, ArcOpenDirectory, &OmfSubdirId ))
            {
                //
                // Go check next path
                //

                continue;
            }

            //
            // Check all files within the directory
            //

            while (!FwGetDirectoryEntry ( OmfSubdirId, &DirEntBuffer,
                                      (ULONG) 1, &Count) )
            {
                //
                // check directory flag, length of file name and file name
                //

                if  ( !( DirEntBuffer.FileAttribute & ArcDirectoryFile )
                        &&
                      DirEntBuffer.FileNameLength
                        &&
                      MaxOmfFatFiles( OmfFatFileName, DirEntBuffer.FileName ))
                {
                    //
                    // correct name, save file path
                    //

                    strcat( strcpy(OmfPathBuffer, OmfPathName), "\\" );
                    strcat(OmfPathBuffer, OmfFatFileName);
                }
            }

            //
            // close directory before opening the next
            //

            FwClose( OmfSubdirId );
        }
    }

    //
    // check for a valid OMF FAT file name
    //

    if ( OmfPathBuffer[0] )
    {
        //
        // open file
        //

        if (FwOpen(OmfPathBuffer, ArcOpenReadOnly, &POmfHeaderContext->FileId))
        {
            //
            // return with error
            //

            BlFileTable[ *FileId ].Flags.Open = 0;
            return ENODEV;
        }

        POmfHeader->FwSize=0;           // max length simulation

        //
        // validate header and copy data in the OmfHeaderContext structure
        //

        if  ( OmfSeek(  *FileId, &Offset, SeekAbsolute )
              ||
              OmfRead(  *FileId,
                        (PVOID)POmfHeader,
                        (ULONG)sizeof(OMF_HDR),
                        &Count)
              ||
              !OmfHeaderValidate( *FileId ) )
        {

            PRINTDBG("No OK\n\r");

            //
            // close file and return with error
            //

            FwClose(POmfHeaderContext->FileId);
            BlFileTable[ *FileId ].Flags.Open = 0;
            return ENODEV;
        }

        //
        // OMF FAT file initialization is done.
        //

        PRINTDBG("It's OK\n\r");

        OmfFatFileOk=TRUE;
    }

    //
    // Reset the open flag.
    //

    BlFileTable[ *FileId ].Flags.Open = 0;

    //
    // if OMF initialization not done, return error
    //

//    if ( !OmfRomOk && !OmfFatFileOk )
    if ( !OmfFatFileOk )
    {
        return ENOENT;
    }
    else
    {
        BlFileTable[ *FileId ].Position.LowPart=0;
        BlFileTable[ *FileId ].Position.HighPart=0;
        return ESUCCESS;
    }
}



// ----------------------------------------------------------------------------
// PROCEDURE:   OmfClose:
//
// DESCRIPTION: The routine closes the file table entry specified by file id.
//              If a OMF FAT file has been used, the routine calls the FwClose
//              function using its file table entry (file id).
//              the routine uses the following logic :
//
//                              if (OMF FAT file)
//                              {
//                                  close file;
//                                  if (error closing file)
//                                      return error;
//                              }
//                              reset open flag;
//                              return ok;
//
// ARGUMENTS:   FileId          Supplies a pointer to a variable that specifies
//                              the file table entry.
//
// RETURN:      ESUCCESS        is returned if the open operation is successful.
//              EBADF           invalid file descriptor
//
//
// ASSUMPTIONS:
//
// CALLS:
//
// GLOBALS:
//
// NOTES:
// ----------------------------------------------------------------------------
//

ARC_STATUS
OmfClose
    (
    IN ULONG FileId
    )
{
    //
    // initialize local variables
    //

    ARC_STATUS Status;                  // store return status from FwClose

    //
    // if OMF FAT file is used, call FwClose using its file table index
    //

    PRINTDBG("OmfClose\n\r");

    if ( BlFileTable[ FileId ].DeviceId != DEVICE_DEVICE)
    {
        return EBADF;
    }

    if  ( BlFileTable[ FileId ].u.OmfHeaderContext.FileId != DEVICE_DEVICE
          &&
          (Status = FwClose( BlFileTable[ FileId ].u.OmfHeaderContext.FileId)))
    {
        return Status;
    }

    //
    // all done, return ok
    //

    BlFileTable[ FileId ].Flags.Open = 0;
    return ESUCCESS;
}



// ----------------------------------------------------------------------------
// PROCEDURE:   OmfMount:
//
// DESCRIPTION: The routine does nothing and return EBADF.
//
// ARGUMENTS:   MountPath       device path specifier.
//              Operation       mount operation (load/unload).
//
// RETURN:      EBADF           invalid operation
//
// ASSUMPTIONS:
//
// CALLS:
//
// GLOBALS:
//
// NOTES:
// ----------------------------------------------------------------------------
//

ARC_STATUS
OmfMount
    (
    IN PCHAR            MountPath,
    IN MOUNT_OPERATION  Operation
    )
{
    //
    // return error :  invalid operation.
    //

    return EBADF;
}




// ----------------------------------------------------------------------------
// PROCEDURE:   OmfRead:
//
// DESCRIPTION: This routine implements the read operation for the ARC-EISA
//              firmware "OMF" driver.
//
// ARGUMENTS:   FileId          Supplies a pointer to a variable that specifies
//                              the file table entry.
//              Buffer          Supplies a pointer to a buffer where the
//                              characters read will be stored.
//              Length          Supplies the length of Buffer.
//              Count           Return the count of the characters that
//                              were read.
//
// RETURN:      ESUCCESS        is returned if the open operation is successful.
//
// ASSUMPTIONS:
//
// CALLS:
//
// GLOBALS:
//
// NOTES:
// ----------------------------------------------------------------------------
//

ARC_STATUS
OmfRead
    (
    IN  ULONG  FileId,
    IN  PVOID  Buffer,
    IN  ULONG  Length,
    OUT PULONG Count
    )
{

    //
    // initialize local variables
    //

    ARC_STATUS  Status;                 // ARC status
    POMF_HEADER_CONTEXT POmfHeaderContext; // OMF header context pointer
    ULONG       OmfSize;                // OMF firmware size (bytes)
    PLARGE_INTEGER PPosition;           // file/device position field pointer
    USHORT      ByteIndex;              // byte index within the word
    ULONG       WordBuffer;             // 1 word buffer


    PRINTDBG("OmfRead\n\r");

    //
    // Initialize some variables
    //

    POmfHeaderContext = &BlFileTable[ FileId ].u.OmfHeaderContext;

    //
    // compute OMF length in bytes
    //

    OmfSize = (ULONG)POmfHeaderContext->OmfHeader.FwSize * OMF_BLOCK_SIZE;

    if ( !OmfSize )
    {
        OmfSize = OMF_MAX_SIZE;
    }

    //
    // initialize byte count read to zero and position variable
    //

    *Count = 0;
    PPosition = &BlFileTable[ FileId ].Position;

    //
    // if length is zero or end of "file", return ESUCCESS.
    //

    if ( !Length  ||  PPosition->LowPart >= OmfSize )
    {
        return ESUCCESS;
    }

    //
    // adjust length if it is too big.
    //

    Length = MIN( Length, OmfSize );    // to prevent the add to overflow

    if ( PPosition->LowPart + Length > OmfSize )
    {
        Length = OmfSize - PPosition->LowPart;
    }

    //
    // if OMF FAT file, read from file.
    //

    if ( POmfHeaderContext->FileId != DEVICE_DEVICE )
    {
        if  ( !(Status = FwSeek( POmfHeaderContext->FileId,
                                  PPosition,
                                  SeekAbsolute ))
              &&
              !(Status = FwRead( POmfHeaderContext->FileId,
                                  Buffer,
                                  Length,
                                  Count )) )
        {
            PPosition->LowPart += *Count;
        }
        return Status;
    }

    //
    //  write word index and compute starting byte index (within a word)
    //

    WRITE_REGISTER_ULONG( POmfHeaderContext->RomIndex,
                          PPosition->LowPart >> WORD_2P2 );

    //
    // read OMF ROM
    // one loop per word
    //

    while ( Length )
    {
        ByteIndex = (USHORT) (PPosition->LowPart & (1<<WORD_2P2)-1);

        //
        // read the whole word if the buffer and OMF pointers are
        // word aligned.
        //                                                      //       |
        if  ( !ByteIndex                                        // DEBUG V
              &&
              !((USHORT)((PUCHAR)Buffer + *Count) & (1 << WORD_2P2) -1 ) )
        {
            while ( Length >= (1 << WORD_2P2) )
            {
                *((PULONG)( (PUCHAR)Buffer + *Count ))=
                    READ_REGISTER_ULONG( POmfHeaderContext->RomRead );
                *Count += 1<<WORD_2P2;
                Length -= 1<<WORD_2P2;
            }
        }

        //
        // read the word in bytes units if the buffer or the OMF pointers
        // are not word aligned or if just part of the word is needed.
        //

        if ( Length )
        {
            WordBuffer = READ_REGISTER_ULONG( POmfHeaderContext->RomRead );

            //
            // read just the selected bytes
            //

            while ( ByteIndex < (1 << WORD_2P2)  &&  Length-- )
            {
                *( (PUCHAR)Buffer + *Count++ )=
                  (UCHAR)( WordBuffer >> BITSXBYTE * ByteIndex++ );
            }
        }

        //
        // update OMF pointer
        //

        PPosition->LowPart += *Count;
    }

    //
    // return all done
    //

    return ESUCCESS;
}



// ----------------------------------------------------------------------------
// PROCEDURE:   OmfWrite:
//
// DESCRIPTION: This routine implements the write operation for the ARC-EISA
//              firmware "OMF" driver.
//
// ARGUMENTS:   FileId          Supplies a pointer to a variable that specifies
//                              the file table entry.
//              Buffer          Supplies a pointer to a buffer where the
//                              characters are read.
//              Length          Supplies the length of Buffer.
//              Count           Return the count of the characters that
//                              were written.
//
// RETURN:      EBADF           invalid operation
//
// ASSUMPTIONS:
//
// CALLS:
//
// GLOBALS:
//
// NOTES:
// ----------------------------------------------------------------------------
//

ARC_STATUS
OmfWrite
    (
    IN  ULONG  FileId,
    IN  PVOID  Buffer,
    IN  ULONG  Length,
    OUT PULONG Count
    )
{
    //
    // return error : invalid operation
    //

    return EBADF;
}



// ----------------------------------------------------------------------------
// PROCEDURE:   OmfGetReadStatus:
//
// DESCRIPTION: This routine implements the GetReadStatus operation for the
//              ARC-EISA firmware "OMF" driver.
//
// ARGUMENTS:   FileId          Supplies a pointer to a variable that specifies
//                              the file table entry.
//
// RETURN:      ESUCCESS        return always success.
//
// ASSUMPTIONS:
//
// CALLS:
//
// GLOBALS:
//
// NOTES:
// ----------------------------------------------------------------------------
//

ARC_STATUS
OmfGetReadStatus
    (
    IN ULONG FileId
    )
{
    //
    // return ok
    //

    return ESUCCESS;
}



// ----------------------------------------------------------------------------
// PROCEDURE:   OmfSeek:
//
// DESCRIPTION: This routine implements the Seek operation for the
//              ARC-EISA firmware "OMF" driver.
//
// ARGUMENTS:   FileId          Supplies a pointer to a variable that specifies
//                              the file table entry.
//              Offset          Supplies the offset in the file to position to.
//              SeekMode        Supplies the mode of the seek operation
//
// RETURN:      ESUCCESS        returned if the operation is successful.
//              EINVAL          returned otherwise.
//
// ASSUMPTIONS:
//
// CALLS:
//
// GLOBALS:
//
// NOTES:
// ----------------------------------------------------------------------------
//

ARC_STATUS
OmfSeek
    (
    IN ULONG            FileId,
    IN PLARGE_INTEGER   Offset,
    IN SEEK_MODE        SeekMode
    )
{
    //
    // local variables
    //

    LARGE_INTEGER       Point;                  // New position
    ULONG               OmfSize;                // OMF size in bytes

    PRINTDBG("OmfSeek\n\r");

    //
    // initialize some variables
    //

    OmfSize = (ULONG)BlFileTable[ FileId ].u.OmfHeaderContext.OmfHeader.FwSize *
                   OMF_BLOCK_SIZE;

    if ( !OmfSize )
    {
        OmfSize=OMF_MAX_SIZE;
    }

    //
    // compute new offset value
    //

    switch( SeekMode )
    {
        case SeekAbsolute:
            Point = *Offset;
            break;
        case SeekRelative:
            Point = RtlLargeIntegerAdd( BlFileTable[FileId].Position, *Offset );
            break;
        default:
            return EINVAL;
    }

    //
    // if the high 32 bits are not zero, return an error.
    // if new offset is valid, update position field.
    // if the position value 0-based is equal to the OMF size (1-based),
    // the EOF has been reached.  no error is returned in this case and
    // the count field is set to zero with ESUCCESS error code for any
    // successive read operation.  note that this combination (ESUCCESS and
    // count=0) is used to rappresent EOF (end of file).
    //

    if  ( Point.HighPart  ||  Point.LowPart > OmfSize )
    {
        return EINVAL;
    }
    else
    {
        BlFileTable[FileId].Position = Point;
        return ESUCCESS;
    }
}





// ----------------------------------------------------------------------------
// PROCEDURE:   OmfGetFileInformation:
//
// DESCRIPTION: This routine implements the GetFileInformation for the
//              ARC-EISA firmware "OMF" driver.
//
// ARGUMENTS:   FileId          Supplies the file table index.
//              Buffer          Supplies the buffer to receive the file
//                              information.  Note that it must be large
//                              enough to hold the full file name.
//
// RETURN:      EBADF           invalid operation
//
// ASSUMPTIONS:
//
// CALLS:
//
// GLOBALS:
//
// NOTES:
// ----------------------------------------------------------------------------
//

ARC_STATUS
OmfGetFileInformation
    (
    IN  ULONG FileId,
    OUT PFILE_INFORMATION Buffer
    )
{
    //
    // return error :  invalid operation
    //

    return EBADF;
}





// ----------------------------------------------------------------------------
// PROCEDURE:   OmfSetFileInformation:
//
// DESCRIPTION: This routine implements the SetFileInformation for the
//              ARC-EISA firmware "OMF" driver.
//
// ARGUMENTS:   FileId          Supplies the file table index.
//              AttributeFlags  Supplies the value (on or off) for each
//                              attribute being modified.
//              AttributeMask   Supplies a mask of the attributes being altered.
//                              All other file attributes are left alone.
//
// RETURN:      EBADF           invalid operation
//
// ASSUMPTIONS:
//
// CALLS:
//
// GLOBALS:
//
// NOTES:
// ----------------------------------------------------------------------------
//

ARC_STATUS
OmfSetFileInformation
    (
    IN ULONG FileId,
    IN ULONG AttributeFlags,
    IN ULONG AttributeMask
    )
{
    //
    // return error :  invalid operation
    //

    return EBADF;
}





// ----------------------------------------------------------------------------
// PROCEDURE:   OmfGetDirectoryEntry:
//
// DESCRIPTION: This routine implements the GetDirectoryEntry for the
//              ARC-EISA firmware "OMF" driver.
//
// ARGUMENTS:   FileId          Supplies the file table index.
//              DirEntry        Pointer to a directory entry structure
//              NumberDir       # of entries to read
//              Count           # of entries read
//
// RETURN:      EBADF           directory operations are not supported
//
// ASSUMPTIONS:
//
// CALLS:
//
// GLOBALS:
//
// NOTES:
// ----------------------------------------------------------------------------
//

ARC_STATUS
OmfGetDirectoryEntry
    (
    IN  ULONG           FileId,
    IN  DIRECTORY_ENTRY *DirEntry,
    IN  ULONG           NumberDir,
    OUT PULONG          CountDir
    )
{
    //
    // return error :  directory operations are not supported
    //

    return EBADF;
}




// ----------------------------------------------------------------------------
// PROCEDURE:   OmfFileOpen:
//
// DESCRIPTION: The routine opens the OMF file.
//              The <extension> of the <filename> is optional. The caller can
//              make the following assumptions if the <extension> is not
//              included in the <filename> :
//              . the firmware searches for folders whose type is MIPS first.
//                This is equivalent to using '.A' as the <extension>. If
//                the requested folder of this type is found in the most recent
//                OMF, then this folder is used.
//              . If the modst recent OMF does not contain the requested folder
//                whose type is MIPS, then the SMF searches for the requested
//                folder whose type is BINARY.  This is equivalent to using '.B'
//                as the <extension>.  If the requested folder of this type is
//                found in the most recent OMF then this folder is used.
//              . If there are no folders with either type in the most recent
//                OMF, the operation fails.
//
//              When omf() is used as <protocol spec> and the filename is the
//              string "\", the path specification represents the OMF directory.
//              The only valid operation to perform on the OMF directory is to
//              Open it, then use FwGetDirectoryEntry to enumerate the folders
//              within the OMF, and then Close it.  For example, to
//              enumerate all folders that are containded in the OMF of a
//              serail options board that was in slot 3 of EISA bus0, create
//              and Open a path as follows :    eisa(0)serial(3)omf()\ .
//              To open the diagnostic folder for the above EISA option board
//              the system utilty would create and Open the path specification :
//              eisa(0)serial(3)omf()\T-SERIAL.
//
//
// ARGUMENTS:   OpenPath        Supplies a pointer to a zero terminated OMF
//                              file name.
//              OpenMode        Supplies the mode of the open.
//              FileId          Supplies the file table entry index
//
// RETURN:      ESUCCESS        is returned if the open operation is successful.
//              ENOENT          the named device or file does not exist.
//
//
// ASSUMPTIONS:
//
// CALLS:
//
// GLOBALS:
//
// NOTES:
// ----------------------------------------------------------------------------
//

ARC_STATUS
OmfFileOpen
    (
    IN PCHAR      OpenPath,
    IN OPEN_MODE  OpenMode,
    IN OUT PULONG FileId
    )
{
    //
    // initialize local variables
    //

    ARC_STATUS      Status;                     // ARC status
    ULONG           DeviceId;                   // device table entry index
    POMF_DIR_ENT    POmfDirEnt;                 // OMF directory entry pointer
    POMF_HDR        POmfHeader;                 // OMF header pointer
    LARGE_INTEGER   AbsolutePosition;           // position
    ULONG           TypeIndex,DirIndex;         // general indexes
    ULONG           Count;                      // # of bytes transferred
    CHAR            OmfFileName[OMF_FILE_NAME_LEN]; // folder name
    BOOLEAN         SearchSucceeded = FALSE;    // match flag
    BOOLEAN         IsDirectory     = FALSE;    // directory flag
    CHAR OmfType[] = {'A','B','\0'};            // default sequance of search:
                                                // relocatable MIPS I object
                                                // format binary data format
                                                // end of chain

    PRINTDBG("OmfFileOpen\n\r");                // debug support

    //
    // Initialize some variables
    //

    DeviceId    = BlFileTable[ *FileId ].DeviceId;
    POmfDirEnt  = &BlFileTable[ *FileId ].u.OmfFileContext.OmfDirEnt;
    POmfHeader  = &BlFileTable[ DeviceId ].u.OmfHeaderContext.OmfHeader;
    AbsolutePosition.HighPart=0;                // for seek operations

    //
    // initialize the file/directory pointer to zero
    //

    BlFileTable[ *FileId ].Position.LowPart=0;
    BlFileTable[ *FileId ].Position.HighPart=0;

    //
    // the first char must be the directory char, otherwise it's an error
    //

    if ( *OpenPath++ != '\\' );

    //
    // else check if the open is for the root directory
    //

    else if ( *OpenPath == '\0' )
    {
        POmfDirEnt->FolderName[0]='\0';
        SearchSucceeded = TRUE;
        IsDirectory     = TRUE;
    }

    //
    // else validate OMF file name
    //

    else if ( !OmfFileNameValidate( OpenPath, OmfFileName, OmfType ));

    //
    // then search the specified file name
    //

    else
    {
        //
        // save the omf file name
        //

        strcpy( BlFileTable[ *FileId ].FileName, OpenPath );
        BlFileTable[ *FileId ].FileNameLength = strlen( OpenPath );

        //
        // one loop per file
        //

        for (TypeIndex = 0; !SearchSucceeded && OmfType[TypeIndex]; TypeIndex++)
        {
            //
            // search the directory for the specified OMF file name
            //

            for ( DirIndex = 0, AbsolutePosition.LowPart =
                                POmfHeader->FolderDirectoryLink << WORD_2P2;

                  DirIndex < POmfHeader->FolderCount && !SearchSucceeded;

                  DirIndex++, AbsolutePosition.LowPart += sizeof(OMF_DIR_ENT) )
            {
                //
                // exit if error during seek or read.
                //

                if  ((Status = FwSeek(DeviceId, &AbsolutePosition, SeekAbsolute))
                     ||
                     (Status = FwRead(DeviceId,
                                      (PVOID)POmfDirEnt,
                                      (ULONG)sizeof(OMF_DIR_ENT),
                                      &Count )))
                {
                    return Status;
                }

                //
                // check OMF file name
                //

                SearchSucceeded = CmpOmfFiles(  OmfFileName,
                                                OmfType[TypeIndex],
                                                (PVOID)POmfDirEnt );
            }
        }

        //
        // if the search has been successful, validate file's header
        //

        if (SearchSucceeded && !OmfFileValidate( *FileId ))
        {
            SearchSucceeded = FALSE;
        }
    }

    //
    //  At this point we've cracked the name up to (an maybe including the last
    //  component).  We located the last component if the SearchSucceeded flag
    //  is true, otherwise the last component does not exist.  If we located
    //  the last component then this is like an open or a supersede, but not a
    //  create.
    //

    if (SearchSucceeded)
    {
        //
        //  Check if the last component is a directory
        //

        if (IsDirectory)
        {
            //
            //  For an existing directory the only valid open mode is
            //  OpenDirectory all other modes return an error
            //

            switch (OpenMode)
            {

                case ArcOpenReadOnly:
                case ArcOpenWriteOnly:
                case ArcOpenReadWrite:
                case ArcCreateWriteOnly:
                case ArcCreateReadWrite:
                case ArcSupersedeWriteOnly:
                case ArcSupersedeReadWrite:

                    //
                    // if we reach here then the caller got a directory but
                    // didn't want to open a directory
                    //

                    return EISDIR;

                case ArcOpenDirectory:

                    //
                    // if we reach here then the caller got a directory and
                    // wanted to open a directory.
                    //

                    BlFileTable[ *FileId ].Flags.Open = 1;
                    BlFileTable[ *FileId ].Flags.Read = 1;

                    return ESUCCESS;

                case ArcCreateDirectory:

                    //
                    // if we reach here then the caller got a directory and
                    // wanted to create a new directory
                    //

                    return EACCES;

                default:

                    //
                    // invalid open mode
                    //

                    return EINVAL;
            }
        }

        //
        //  If we get there then we have an existing file that is being opened.
        //  We can open existing files only read only.
        //

        switch (OpenMode)
        {
            case ArcOpenReadOnly:

                //
                // if we reach here then the user got a file and wanted to
                // open the file read only
                //

                BlFileTable[ *FileId ].Flags.Open = 1;
                BlFileTable[ *FileId ].Flags.Read = 1;

                return ESUCCESS;

            case ArcOpenWriteOnly:
            case ArcOpenReadWrite:
            case ArcCreateWriteOnly:
            case ArcCreateReadWrite:
            case ArcSupersedeWriteOnly:
            case ArcSupersedeReadWrite:

                //
                // if we reach here then we are trying to open a read only
                // device for write.
                //

                return EROFS;

            case ArcOpenDirectory:
            case ArcCreateDirectory:

                //
                // if we reach here then the user got a file and wanted a
                // directory
                //

                return ENOTDIR;

            default:

                //
                // invalid open mode
                //

                return EINVAL;
        }
    }

    //
    //  If we get here the last component does not exist so we are trying to
    //  create either a new file or a directory.
    //

    switch (OpenMode)
    {
        case ArcOpenReadOnly:
        case ArcOpenWriteOnly:
        case ArcOpenReadWrite:
        case ArcOpenDirectory:

            //
            // if we reach here then the user did not get a file but wanted
            // a file
            //

            return ENOENT;

        case ArcCreateWriteOnly:
        case ArcSupersedeWriteOnly:
        case ArcCreateReadWrite:
        case ArcSupersedeReadWrite:
        case ArcCreateDirectory:

            //
            // if we get hre the user wants to create something.
            //

            return EROFS;

        default:

            //
            // invalid open mode
            //

            return EINVAL;
    }
}





// ----------------------------------------------------------------------------
// PROCEDURE:   OmfFileClose:
//
// DESCRIPTION: The routine closes the file table entry specified by file id.
//
// ARGUMENTS:   FileId          Supplies the file table index.
//
// RETURN:      ESUCCESS        is returned if the open operation is successful.
//
//
// ASSUMPTIONS:
//
// CALLS:
//
// GLOBALS:
//
// NOTES:
// ----------------------------------------------------------------------------
//

ARC_STATUS
OmfFileClose
    (
    IN ULONG FileId
    )
{
    PRINTDBG("OmfFileClose\n\r");

    //
    // clear flag and exit
    //

    BlFileTable[FileId].Flags.Open = 0;
    return ESUCCESS;
}





// ----------------------------------------------------------------------------
// PROCEDURE:   OmfFileMount:
//
// DESCRIPTION: The routine does nothing and return EBADF.
//
// ARGUMENTS:   MountPath       device path specifier.
//              Operation       mount operation (load/unload).
//
// RETURN:      EBADF           invalid operation
//
// ASSUMPTIONS:
//
// CALLS:
//
// GLOBALS:
//
// NOTES:
// ----------------------------------------------------------------------------
//

ARC_STATUS
OmfFileMount
    (
    IN PCHAR            MountPath,
    IN MOUNT_OPERATION  Operation
    )
{
    //
    // return error :  invalid operation.
    //

    return EBADF;
}






// ----------------------------------------------------------------------------
// PROCEDURE:   OmfFileGetReadStatus
//
// DESCRIPTION: This routine implements the GetReadStatus operation for the
//              ARC-EISA firmware OMF file.
//
// ARGUMENTS:   FileId          Supplies a pointer to a variable that specifies
//                              the file table entry.
//
// RETURN:      ESUCCESS        return always success.
//
// ASSUMPTIONS:
//
// CALLS:
//
// GLOBALS:
//
// NOTES:
// ----------------------------------------------------------------------------
//

ARC_STATUS
OmfFileGetReadStatus
    (
    IN ULONG FileId
    )
{
    //
    // return ok
    //

    return ESUCCESS;
}






// ----------------------------------------------------------------------------
// PROCEDURE:   OmfFileRead:
//
// DESCRIPTION: This routine implements the read operation for the ARC-EISA
//              firmware "OMF" driver.
//
// ARGUMENTS:   FileId          Supplies the file table index.
//              Buffer          Supplies a pointer to a buffer where the
//                              characters read will be stored.
//              Length          Supplies the length of Buffer.
//              Count           Return the count of the characters that
//                              were read.
//
// RETURN:      ESUCCESS        is returned if the open operation is successful.
//
// ASSUMPTIONS:
//
// CALLS:
//
// GLOBALS:
//
// NOTES:
// ----------------------------------------------------------------------------
//

ARC_STATUS
OmfFileRead
    (
    IN  ULONG  FileId,
    IN  PVOID  Buffer,
    IN  ULONG  Length,
    OUT PULONG Count
    )
{

    //
    // initialize local variables
    //
    ARC_STATUS    Status;                       // ARC status
    POMF_DIR_ENT  POmfDirEnt;                   // OMF directory entry pointer
    LARGE_INTEGER AbsolutePosition;             // OMF file, logical pointer
    ULONG         OmfFileSize;                  // OMF file size in bytes

    PRINTDBG("OmfFileRead\n\r");

    //
    // initialize some variables
    //

    POmfDirEnt = &BlFileTable[ FileId ].u.OmfFileContext.OmfDirEnt;

    //
    // if directory entry, exit with error
    //

    if ( !POmfDirEnt->FolderName[0] )
    {
        return EBADF;                   // check this error code "EGI_Q"
    }

    //
    // initialize byte count read to zero
    //

    *Count=0;
    OmfFileSize = POmfDirEnt->FolderSize << WORD_2P2;

    //
    // if length is zero or EOF (end of file), return ESUCCESS.
    //

    if (!Length || BlFileTable[FileId].Position.LowPart >= OmfFileSize)
    {
        return ESUCCESS;
    }

    //
    // adjust length if it is too big.
    //

    Length = MIN( Length, OmfFileSize ); // to prevent the add to overflow

    if ( BlFileTable[ FileId ].Position.LowPart + Length > OmfFileSize )
    {
        Length = OmfFileSize - BlFileTable[ FileId ].Position.LowPart;
    }

    //
    // find OMF absolute offset from OMF file absolute offset
    //

    AbsolutePosition.HighPart = 0;
    AbsolutePosition.LowPart = BlFileTable[ FileId ].Position.LowPart +
                                ( POmfDirEnt->FolderLink << WORD_2P2 );

    //
    // seek + read command
    //

    if  ( (Status = FwSeek( BlFileTable[ FileId ].DeviceId,
                             &AbsolutePosition,
                             SeekAbsolute ))
          ||
          (Status = FwRead( BlFileTable[FileId].DeviceId,
                             Buffer,
                             Length,
                             Count )) )
    {
        return Status;
    }

    //
    // update file pointer and exit
    //

    BlFileTable[FileId].Position.LowPart += *Count;
    return ESUCCESS;
}





// ----------------------------------------------------------------------------
// PROCEDURE:   OmfFileWrite:
//
// DESCRIPTION: This routine implements the write operation for the ARC-EISA
//              firmware "OMF" driver.
//
// ARGUMENTS:   FileId          Supplies the file table index.
//              Buffer          Supplies a pointer to a buffer where the
//                              characters are read.
//              Length          Supplies the length of Buffer.
//              Count           Return the count of the characters that
//                              were written.
//
// RETURN:      EBADF           invalid operation
//
// ASSUMPTIONS:
//
// CALLS:
//
// GLOBALS:
//
// NOTES:
// ----------------------------------------------------------------------------
//

ARC_STATUS
OmfFileWrite
    (
    IN ULONG FileId,
    IN PVOID Buffer,
    IN ULONG Length,
    OUT PULONG Count
    )
{
    //
    // return error :  invalid operation
    //

    return EBADF;
}





// ----------------------------------------------------------------------------
// PROCEDURE:   OmfFileSeek:
//
// DESCRIPTION: This routine implements the Seek operation for the
//              ARC-EISA firmware "OMF" driver.
//
// ARGUMENTS:   FileId          Supplies the file table index.
//              Offset          Supplies the offset in the file to position to.
//              SeekMode        Supplies the mode of the seek operation
//
// RETURN:      ESUCCESS        returned if the operation is successful.
//              EINVAL          returned otherwise.
//
// ASSUMPTIONS:
//
// CALLS:
//
// GLOBALS:
//
// NOTES:
// ----------------------------------------------------------------------------
//

ARC_STATUS
OmfFileSeek
    (
    IN ULONG FileId,
    IN PLARGE_INTEGER Offset,
    IN SEEK_MODE SeekMode
    )
{
    //
    // local variables
    //

    LARGE_INTEGER       Point;                  // New position
    POMF_DIR_ENT        POmfDirEnt;             // OMF directory entry pointer

    PRINTDBG("OmfFileSeek\n\r");

    //
    // initialize some variables
    //

    POmfDirEnt = &BlFileTable[ FileId ].u.OmfFileContext.OmfDirEnt;

    //
    // The offset parameter must be zero, if directory entry.
    //

    if  ( !POmfDirEnt->FolderName[0] && ( Offset->LowPart || Offset->HighPart ))
    {
        return EINVAL;
    }

    //
    // compute new offset value
    //

    switch(SeekMode)
    {
        case SeekAbsolute:
            Point = *Offset;
            break;
        case SeekRelative:
            Point = RtlLargeIntegerAdd( BlFileTable[FileId].Position, *Offset );
            break;
        default:
            return EINVAL;
    }

    //
    // if new offset is valid, update position field.
    // if the position value 0-based is equal to the OMF size (1-based),
    // the EOF has been reached.  no error is returned in this case and
    // the count field is set to zero with ESUCCESS error code for any
    // successive read operation.  note that this combination (ESUCCESS and
    // count=0) is used to rappresent EOF (end of file).
    //

    if ( POmfDirEnt->FolderName[0] &&
        (Point.HighPart || Point.LowPart > POmfDirEnt->FolderSize << WORD_2P2))
    {
        return EINVAL;
    }
    else
    {
        BlFileTable[ FileId ].Position = Point;
        return ESUCCESS;
    }
}





// ----------------------------------------------------------------------------
// PROCEDURE:   OmfFileGetFileInformation:
//
// DESCRIPTION: This routine implements the GetFileInformation for the
//              ARC-EISA firmware "OMF" driver.
//
// ARGUMENTS:   FileId          Supplies the file table index.
//              Buffer          Supplies the buffer to receive the file
//                              information.  Note that it must be large
//                              enough to hold the full file name.
//
// RETURN:      EBADF           invalid operation
//
// ASSUMPTIONS:
//
// CALLS:
//
// GLOBALS:
//
// NOTES:
// ----------------------------------------------------------------------------
//

ARC_STATUS
OmfFileGetFileInformation
    (
    IN  ULONG FileId,
    OUT PFILE_INFORMATION Buffer
    )
{
    //
    // define local variable
    //

    PBL_FILE_TABLE FileTableEntry = &BlFileTable[ FileId ];

    PRINTDBG("OmfFileGetFileInformation\n\r");

    //
    // if root, exit with error
    //

    if ( !FileTableEntry->u.OmfFileContext.OmfDirEnt.FolderName[0] )
    {
        return EBADF;
    }

    //
    // set output fields
    //

    RtlZeroMemory(Buffer, sizeof(FILE_INFORMATION));

    //
    // ending address (high part is already zero) and current address
    //

    Buffer->EndingAddress.LowPart    =
        FileTableEntry->u.OmfFileContext.OmfDirEnt.FolderSize << WORD_2P2;
    Buffer->CurrentPosition.LowPart  = FileTableEntry->Position.LowPart;
    Buffer->CurrentPosition.HighPart = FileTableEntry->Position.HighPart;

    //
    // device type
    //

    Buffer->Type = OtherPeripheral;

    //
    // file name length, attributes and file name string
    //

    Buffer->FileNameLength = FileTableEntry->FileNameLength;
    Buffer->Attributes = ArcReadOnlyFile;
    strcpy( Buffer->FileName, FileTableEntry->FileName );

    //
    // all done
    //

    return ESUCCESS;
}





// ----------------------------------------------------------------------------
// PROCEDURE:   OmfFileSetFileInformation:
//
// DESCRIPTION: This routine implements the SetFileInformation for the
//              ARC-EISA firmware "OMF" driver.
//
// ARGUMENTS:   FileId          Supplies the file table index.
//              AttributeFlags  Supplies the value (on or off) for each
//                              attribute being modified.
//              AttributeMask   Supplies a mask of the attributes being altered.
//                              All other file attributes are left alone.
//
// RETURN:      EBADF           invalid operation
//
// ASSUMPTIONS:
//
// CALLS:
//
// GLOBALS:
//
// NOTES:
// ----------------------------------------------------------------------------
//

ARC_STATUS
OmfFileSetFileInformation
    (
    IN ULONG FileId,
    IN ULONG AttributeFlags,
    IN ULONG AttributeMask
    )
{
    //
    // return error :  invalid operation
    //

    return EROFS;
}





// ----------------------------------------------------------------------------
// PROCEDURE:   OmfFileGetDirectoryEntry:
//
// DESCRIPTION: This routine implements the GetDirectoryEntry for the
//              ARC-EISA firmware "OMF" driver.
//
// ARGUMENTS:   FileId          Supplies the file table index.
//              DirEntry        Pointer to a directory entry structure
//              NumberDir       # of entries to read
//              Count           # of entries read
//
// RETURN:      ESUCCESS        returned if the operation is successful.
//              EINVAL          returned otherwise.
//
// ASSUMPTIONS:
//
// CALLS:
//
// GLOBALS:
//
// NOTES:
// ----------------------------------------------------------------------------
//

ARC_STATUS
OmfFileGetDirectoryEntry
    (
    IN  ULONG           FileId,
    IN  DIRECTORY_ENTRY *DirEntry,
    IN  ULONG           NumberDir,
    OUT PULONG          CountDir
    )
{
    //
    // initialize local variables
    //

    ARC_STATUS  Status;                 // ARC status
    ULONG       DeviceId;               // file table entry for the device
    POMF_HDR    POmfHeader;             // pointer to OMF ROM/FAT file header
    ULONG       CurrentDir;             // current directory entry
    LARGE_INTEGER AbsolutePosition;     // OMF file, pointer of logical pointer
    OMF_DIR_ENT OmfFileBuffer;          // buffer for directory entries
    ULONG       Count;                  // # of bytes read

    PRINTDBG("OmfFileGetDirectoryEntry\n\r");

    //
    // if not directory entry, exit with error
    //

    if ( BlFileTable[ FileId ].u.OmfFileContext.OmfDirEnt.FolderName[0] )
    {
        return EBADF;
    }

    //
    // Initialize the output count to zero
    //

    *CountDir=0;

    //
    // if NumberDir is zero, return ESUCCESS.
    //

    if ( !NumberDir )
    {
        return ESUCCESS;
    }

    //
    // initialize variables
    //

    Count=0;
    DeviceId   = BlFileTable[ FileId ].DeviceId;
    POmfHeader = &BlFileTable[ DeviceId ].u.OmfHeaderContext.OmfHeader;
    CurrentDir = BlFileTable[ FileId ].Position.LowPart/sizeof(OMF_DIR_ENT);

    //
    // if no more entries, return ENOTDIR
    //

    if ( CurrentDir >= POmfHeader->FolderCount )
    {
        return ENOTDIR;
    }

    //
    // adjust count if it is too big.
    //

    NumberDir = MIN( NumberDir, POmfHeader->FolderCount );  //avoid add overflow

    if ( CurrentDir + NumberDir > POmfHeader->FolderCount )
    {
        NumberDir = POmfHeader->FolderCount - CurrentDir;
    }

    //
    // find OMF absolute offset from OMF file absolute offset
    //

    AbsolutePosition.HighPart = 0;
    AbsolutePosition.LowPart = BlFileTable[FileId].Position.LowPart +
                                (POmfHeader->FolderDirectoryLink<<WORD_2P2);

    //
    // seek command
    //

    if ( Status = FwSeek( DeviceId, &AbsolutePosition, SeekAbsolute ))
    {
        return Status;
    }

    //
    // read command
    //

    while( NumberDir-- )
    {
        //
        // read one directory entry
        //

        if  ( Status = FwRead( DeviceId,
                                &OmfFileBuffer,
                                (ULONG)sizeof(OMF_DIR_ENT),
                                &Count ))
        {
            return Status;
        }

        //
        // if bytes read are not sizeof(OMF_DIR_ENT), return error
        //

        if ( Count != sizeof(OMF_DIR_ENT) )
        {
            return EBADF;
        }

        //
        // convert OMF directory entry in ARC directory entry
        //

        ConvertOmfDirToArcDir( &OmfFileBuffer, DirEntry++ );
        ++*CountDir;
    }

    //
    // update file pointer and exit
    //

    BlFileTable[FileId].Position.LowPart += *CountDir * sizeof(OMF_DIR_ENT);
    return ESUCCESS;
}





// ----------------------------------------------------------------------------
// PROCEDURE:   MaxOmfFatFiles:
//
// DESCRIPTION: The routine returns the most updated file name for the
//              specified product Id.
//              A FALSE is returned if the OMF FAT file name has not been
//              modified.
//
// ARGUMENTS:   MasterName      name used as base
//              CheckedName     name checked
//
// RETURN:      TRUE            is returned if the OMF FAT file name has been
//                              modified.
//              FALSE           is returned in all the other cases.
//
// ASSUMPTIONS:
//
// CALLS:
//
// GLOBALS:
//
// NOTES:
// ----------------------------------------------------------------------------
//

BOOLEAN
MaxOmfFatFiles
    (
    IN OUT PCHAR MasterName,
    IN PCHAR CheckedName
    )
{
    //
    // Define variables
    //

    CHAR MasterVersion, MasterRevision;         // master name
    CHAR CheckedVersion, CheckedRevision;       // checked name
    POMF_FAT_FILE_NAME PointerMaster;           // master name pointer
    POMF_FAT_FILE_NAME PointerChecked;          // checked name pointer

    PRINTDBG("MaxOmfFatFiles\n\r");

    //
    // save version and revision master values
    //

    PointerMaster   = (POMF_FAT_FILE_NAME)MasterName;
    MasterVersion   = PointerMaster->Version;
    MasterRevision  = PointerMaster->Revision;

    //
    // save version and revision checked values
    //

    PointerChecked  = (POMF_FAT_FILE_NAME)CheckedName;
    CheckedVersion  = PointerChecked->Version;
    CheckedRevision = PointerChecked->Revision;
    PointerChecked->Version  = MasterVersion;
    PointerChecked->Revision = MasterRevision;

    //
    // update name if new version for the same product Id
    //

    if  (strcmp(MasterName, CheckedName)
         ||
         CheckedVersion<MasterVersion
         ||
         (CheckedVersion == MasterVersion && CheckedRevision <= MasterRevision))
    {
        return FALSE;
    }
    else
    {
        PointerMaster->Version  = CheckedVersion;
        PointerMaster->Revision = CheckedRevision;
        return TRUE;
    }
}





// ----------------------------------------------------------------------------
// PROCEDURE:   OmfFileNameValidate:
//
// DESCRIPTION: The routine validates the requested OMF file name.
//              The name is decomposed in Folder Name and Folder Type.
//
// ARGUMENTS:   OpenPath        Supplies a pointer to a zero terminated OMF
//                              file name.
//              OmfFileName     Supplies a pointer to an array used to store
//                              the Folder Name.
//              OmfType         Supplies a pointer to an array used to store
//                              the Folder Types.
//
// RETURN:      TRUE            is returned if the operation succeed.
//              FALSE           is returned if the OpenPath is incorrect.
//
// ASSUMPTIONS:
//
// CALLS:
//
// GLOBALS:
//
// NOTES:
// ----------------------------------------------------------------------------
//

BOOLEAN
OmfFileNameValidate
    (
    IN  PCHAR OpenPath,
    OUT PCHAR OmfFileName,
    OUT PCHAR OmfType
    )

{
    //
    // Define variables
    //

    ULONG NameIndex;                    // general index

    PRINTDBG("OmfFileNameValidate\n\r");

    //
    // name validation
    //

    for ( NameIndex = 0;
          NameIndex < OMF_FILE_NAME_LEN  &&  *OpenPath  &&  *OpenPath != '.';
          NameIndex++ )
    {
        if  (
            isupper( *OpenPath )                // upper case letter
            ||
            isdigit( *OpenPath )                // digit
            ||
            *OpenPath=='-'                      // '-' minus
            ||
            (*OpenPath=='$' && NameIndex==0)    // $ char in 1st position
            )
        {
            *OmfFileName++=*OpenPath++;
        }
        else
        {
            return FALSE;
        }
    }

    //
    // return error if name length > of OMF_FILE_NAME_LEN
    //

    if ( NameIndex == OMF_FILE_NAME_LEN  &&  *OpenPath  &&  *OpenPath != '.')
    {
        return FALSE;
    }

    //
    // fill right with null chars ('\0') if Folder Name is less than 12 chars
    //

    while( NameIndex++ < OMF_FILE_NAME_LEN )
    {
        *OmfFileName++='\0';
    }

    //
    // extension validation
    //

    if ( *OpenPath++ && *OpenPath )             // skip "null", ".null"
    {
        switch( *OpenPath )                     // check type field
        {
            case 'A':
            case 'B':
                *OmfType++ = *OpenPath++;
                *OmfType   = '\0';              // means : end of types
                break;
            default:
                return FALSE;
        }
        if( *OpenPath )                         // error if more chars
        {
            return FALSE;
        }
    }

    //
    // all done, return ok
    //

    return TRUE;
}




// ----------------------------------------------------------------------------
// PROCEDURE:   CmpOmfFiles:
//
// DESCRIPTION: The routine checks if the selected directory entry has the
//              requested OMF file name.
//
// ARGUMENTS:   OmfFileName     Requested OMF file name pointer.
//              OmfType         Requested OMF file type pointer.
//              POmfDirEnt      Directory entry to check.
//
// RETURN:      TRUE            is returned if the operation succeed.
//              FALSE           is returned if the name or type doesn't match.
//
// ASSUMPTIONS:
//
// CALLS:
//
// GLOBALS:
//
// NOTES:
// ----------------------------------------------------------------------------
//

BOOLEAN
CmpOmfFiles
    (
    IN PCHAR OmfFileName,
    IN CHAR OmfType,
    IN POMF_DIR_ENT POmfDirEnt
    )
{
    //
    // Define variables
    //

    ULONG NameIndex;                    // general index

    PRINTDBG("CmpOmfFiles\n\r");

    //
    // check name
    //

    for ( NameIndex=0; NameIndex < OMF_FILE_NAME_LEN; NameIndex++ )
    {
        if ( OmfFileName[ NameIndex ] != POmfDirEnt->FolderName[ NameIndex ] )
        {
            return FALSE;
        }
    }

    //
    // check extension
    //

    if ( OmfType != POmfDirEnt->FolderType )
    {
        return FALSE;
    }

    //
    // all done, return ok
    //

    return TRUE;
}





// ----------------------------------------------------------------------------
// PROCEDURE:   OmfHeaderValidate:
//
// DESCRIPTION: The routine checks if the specified OMF header data is valid.
//
// ARGUMENTS:   FileId          file table entry index
//
// RETURN:      TRUE            is returned if file is ok.
//              FALSE           is returned if file is not correct.
//
// ASSUMPTIONS: The header data has already been copied in the
//              context data of the file table entry.
//              The size of all the OMF structures is a multiple of a word.
//
// CALLS:
//
// GLOBALS:
//
// NOTES:
// ----------------------------------------------------------------------------
//

BOOLEAN
OmfHeaderValidate
    (
    IN ULONG FileId
    )
{
    //
    // local variables
    //

    POMF_HDR POmfHeader;                // pointer to the header data
    ULONG OmfDirLink;                   // directory link in words
    ULONG OmfDirLength;                 // directory size in words
    ULONG OmfSize;                      // OMF size in bytes
    UCHAR Checksum=0;                   // used to compute the checksum

    PRINTDBG("OmfHeaderValidate\n\r");

    //
    // initialize variables
    //

    POmfHeader   = &BlFileTable[FileId].u.OmfHeaderContext.OmfHeader;
    OmfDirLink   = POmfHeader->FolderDirectoryLink;

    OmfDirLength =(POmfHeader->FolderCount * sizeof(OMF_DIR_ENT))/(1<<WORD_2P2);
    OmfSize      = (ULONG)POmfHeader->FwSize * OMF_BLOCK_SIZE;
    if (!OmfSize)
    {
        OmfSize = OMF_MAX_SIZE;
    }

    //
    // check header values
    //

    if  ( POmfHeader->ID[0] != OMF_ID_1ST
          ||
          POmfHeader->ID[1] != OMF_ID_2ND
          ||
          POmfHeader->ID[2] != OMF_ID_3RD
          ||
          POmfHeader->ID[3] != OMF_ID_4TH
          ||
          !isalnum(POmfHeader->FwVersion)
          ||
          !isalnum(POmfHeader->FwRevision)
          ||
          sizeof(OMF_HDR) > OmfSize
          ||
          OmfDirLink + OmfDirLength > OmfSize/(1<<WORD_2P2)
          ||
          FwFileIdChecksum( FileId,
                            RtlConvertUlongToLargeInteger(0l),
                            (ULONG)(sizeof(OMF_HDR)),
                            &Checksum )
          ||
          FwFileIdChecksum( FileId,
                            RtlConvertUlongToLargeInteger(OmfDirLink<<WORD_2P2),
                            OmfDirLength << WORD_2P2,
                            &Checksum )
// >>> FIXME <<<
//        ||
//        Checksum
        )
    {
        return FALSE;
    }
    else
    {
        return TRUE;
    }
}





// ----------------------------------------------------------------------------
// PROCEDURE:   OmfFileValidate:
//
// DESCRIPTION: The routine checks if the selected file is valid.
//
// ARGUMENTS:   FileId          file table entry index
//
// RETURN:      TRUE            is returned if file is ok.
//              FALSE           is returned if file is not correct.
//
// ASSUMPTIONS: the folder name and its extension (type) have already
//              been validated.
//
// CALLS:
//
// GLOBALS:
//
// NOTES:
// ----------------------------------------------------------------------------
//

BOOLEAN
OmfFileValidate
    (
    IN ULONG FileId
    )
{
    //
    // local variables
    //

    POMF_DIR_ENT POmfDirEnt;            // OMF directory entry pointer
    ULONG OmfFileLength;                // file length, word count
    ULONG OmfFileLink;                  // file link, word count
    ULONG DeviceId;                     // device id file table entry
    ULONG OmfLength;                    // OMF rom length, word count
    UCHAR Checksum=0;                   // used to compute the checksum

    PRINTDBG("OmfFileValidate\n\r");

    //
    // initialize variables
    //

    POmfDirEnt    = &BlFileTable[FileId].u.OmfFileContext.OmfDirEnt;
    OmfFileLink   = POmfDirEnt->FolderLink;
    OmfFileLength = POmfDirEnt->FolderSize;
    DeviceId      = BlFileTable[FileId].DeviceId;
    OmfLength=(ULONG)BlFileTable[DeviceId].u.OmfHeaderContext.OmfHeader.FwSize *
                 OMF_BLOCK_SIZE/(1<<WORD_2P2);
    if (!OmfLength)
    {
        OmfLength = OMF_MAX_SIZE/(1<<WORD_2P2);
    }

    //
    // validate file
    //

    if  ( OmfFileLink > OMF_MAX_FILE_LINK
          ||
          OmfFileLength > OMF_MAX_FILE_LEN
          ||
          OmfFileLink + OmfFileLength > OmfLength
          ||
          FwFileIdChecksum( DeviceId,
                            RtlConvertUlongToLargeInteger(OmfFileLink<<WORD_2P2),
                            OmfFileLength << WORD_2P2,
                            &Checksum )
// >>> FIXME <<<
//        ||
//        (Checksum += POmfDirEnt->FolderChecksumByte)
        )
    {
        return FALSE;
    }
    else
    {
        return TRUE;
    }
}




// ----------------------------------------------------------------------------
// PROCEDURE:   ConvertOmfDirToArcDir:
//
// DESCRIPTION: The routine converts the OMF directory entry in the ARC
//              directory entry format.
//
// ARGUMENTS:   OmfFileBuffer   pointer to OMF directory entry
//              DirEntry        pointer to ARC directory entry to be filled
//
// RETURN:      NONE
//
// ASSUMPTIONS: FileNameLengthMax is at least 3 chars.
//
// CALLS:
//
// GLOBALS:
//
// NOTES:
// ----------------------------------------------------------------------------
//

VOID
ConvertOmfDirToArcDir
    (
    IN  POMF_DIR_ENT    POmfDirEnt,
    OUT DIRECTORY_ENTRY *PArcDirEnt
    )
{
    //
    // local variables
    //

    ULONG CharIndex;                            // name+extension length
    ULONG MaxFileName;                          // max name length

    PRINTDBG("ConvertOmfDirToArcDir\n\r");

    //
    // set attribute bit (read only file)
    //

    PArcDirEnt->FileAttribute = ArcReadOnlyFile;

    //
    // set file name (note that 3 means: '.' + extension + '\0' )
    //

    MaxFileName = MIN( 32 - 3, OMF_FILE_NAME_LEN );

    for ( CharIndex = 0;
          CharIndex < MaxFileName && POmfDirEnt->FolderName[ CharIndex ];
          CharIndex++ )
    {
         PArcDirEnt->FileName[ CharIndex ] = POmfDirEnt->FolderName[ CharIndex];
    }

    //
    // dot '.'
    //

    PArcDirEnt->FileName[ CharIndex++ ] = '.';

    //
    // extension
    //

    PArcDirEnt->FileName[ CharIndex++ ] = POmfDirEnt->FolderType;

    //
    // null char
    //

    PArcDirEnt->FileName[ CharIndex ]='\0';

    //
    // set length (note: CharIndex is 0-based)
    //

    PArcDirEnt->FileNameLength = CharIndex;

    //
    // all done, exit.
    //

    return;
}





// ----------------------------------------------------------------------------
// PROCEDURE:   FwGetEisaId:
//
// DESCRIPTION: The routine returns the requested board and info id.
//
// ARGUMENTS:   PathName        Option board path name pointer
//              EisaId          Pointer to 7 bytes space for the id
//              IdInfo          Pointer to 1 byte space for the info id
//
// RETURN:      TRUE            all done.
//              FALSE           for any error.
//
// ASSUMPTIONS:
//
// CALLS:
//
// GLOBALS:
//
// NOTES:
// ----------------------------------------------------------------------------
//

BOOLEAN
FwGetEisaId
    (
    IN  PCHAR  PathName,
    OUT PCHAR  EisaId,
    OUT PUCHAR IdInfo
    )
{
    //
    // define local variables
    //

    PCONFIGURATION_COMPONENT pComp;             // Pointer to a component struc
    EISA_SLOT_INFO SlotInfo;                    // pointer to first eisa info
    BOOLEAN GetIdStatus = FALSE;                // be pessimistic

    PRINTDBG("FwGetEisaId\n\r");                // DEBUG SUPPORT

    //
    // Check to see if the motherboard component is being accessed.  Since
    // Jazz doesn't have a motherboard id, substitute values.
    //

    if (strstr(PathName, "eisa(0)other(0)") != NULL) {
        EisaId[0] = 'J';
        EisaId[1] = 'A';
        EisaId[2] = 'Z';
        EisaId[3] = '0';
        EisaId[4] = '0';
        EisaId[5] = '0';
        EisaId[6] = '0';
        *IdInfo = 0;
        GetIdStatus = TRUE;

    //
    // get the requested component
    //

    } else if  ( (pComp = FwGetComponent(PathName)) == NULL );

    //
    // check the configuration data length
    //

    else if ( pComp->ConfigurationDataLength < EISA_SLOT_MIN_INFO );

    //
    // get the slot info
    //

    else if ( FwGetConfigurationDataIndex( (PVOID)&SlotInfo,
                                           pComp,
                                           CONFIGDATAHEADER_SIZE,
                                           EISA_SLOT_INFO_SIZE ));

    //
    // get the requested info
    //

    else
    {
        FwUncompressEisaId( &SlotInfo.Id1stChar, EisaId );
        *IdInfo = SlotInfo.IdInfo;
        GetIdStatus = TRUE;
    }

    //
    // return status
    //

    return GetIdStatus;
}






// ----------------------------------------------------------------------------
// PROCEDURE:   FwUncompressEisaId:
//
// DESCRIPTION: The routine converts the specified compressed ID in 7 ASCII
//              chars.
//
// ARGUMENTS:   CompEisaId      Pointer to the compressed ID (4 bytes)
//              UncompEisaId    Pointer to the uncompressed ID (7 bytes)
//
// RETURN:      none
//
// ASSUMPTIONS: Compressed ID :  1st byte  bit  7   - Reserved
//                                         bits 6-2 - 1st char name comp
//                                         bits 1-0 - 2nd char name comp
//                               2nd byte  bits 7-5 - 2nd char name
//                                         bits 4-0 - 3rd char name comp
//                               3rd byte  bits 7-4 - 1st decimal number comp
//                                         bits 3-0 - 2nd decimal number comp
//                               4th byte  bits 7-4 - 3rd decimal number comp
//                                         bits 3-0 - 4th decimal number comp
//
//              Uncompressed ID: 1st byte  1st char name
//                               2nd byte  2nd char name
//                               3rd byte  3rd char name
//                               4th byte  1st decimal number
//                               5th byte  2nd decimal number
//                               6th byte  3rd decimal number
//                               7th byte  4th decimal number
//
//              Compressed -> Uncompressed :
//
//                              char comp + 'A' - 1 = char uncompressed
//                              decimal comp + '0'  = decimal uncompressed
//
//
// CALLS:
//
// GLOBALS:
//
// NOTES:
// ----------------------------------------------------------------------------
//

VOID
FwUncompressEisaId
    (
    IN PUCHAR CompEisaId,
    OUT PUCHAR UncompEisaId
    )
{
    //
    // local variables
    //

    ULONG       Id;                             // used to hold the 4 bytes ID
    SHORT       i;                              // general index
    UCHAR       d;                              // hexadecimal digit

    PRINTDBG("FwUncompressEisaId\n\r");

    //
    // transform the 4 chars ID in a ULONG
    //

    Id = Fw4UcharToUlongMSB( CompEisaId );

    //
    // uncompress 4 digits, starting from the last one
    //

    for (i=3; i>=0; i--)
    {
        d = Id & ID_DIGIT_MASK;
        UncompEisaId[3+i] = (d <= 9) ? (d + '0') : (d - 0xA + 'A');
        Id >>= ID_DIGIT_SIZE;
    }

    //
    // uncompress 3 chars
    //

    for (i=2; i>=0; i--)
    {
        UncompEisaId[i] = (Id & ID_CHAR_MASK) + 'A' - 1;
        Id >>= ID_CHAR_SIZE;
    }

    //
    // exit
    //

    return;
}






// ----------------------------------------------------------------------------
// PROCEDURE:   FwGetEisaBusIoCpuAddress:
//
// DESCRIPTION: The routine returns the virtual (CPU) address for the specified
//              EISA bus.
//
// ARGUMENTS:   EisaPath        pointer to the path string
//              IoBusAddress    pointer to a PVOID variable. It used to store
//                              the computed address.
//
// RETURN:      TRUE            returned if all correct.
//              FALSE           returned for any error.
//
// ASSUMPTIONS:
//
// CALLS:
//
// GLOBALS:
//
// NOTES:
// ----------------------------------------------------------------------------
//

BOOLEAN
FwGetEisaBusIoCpuAddress
    (
    IN  PCHAR EisaPath,
    OUT PVOID *IoBusAddress
    )
{
    //
    // define local variables
    //

    PCONFIGURATION_COMPONENT PComponent;
    EISA_ADAPTER_DETAILS EisaInfo;

    PRINTDBG("FwGetEisaBusIoCpuAddress\n\r");

    //
    // get the requested component and configuration data
    //

    if  ( (PComponent = FwGetComponent( EisaPath )) == NULL  ||
          FwGetConfigurationData( (PVOID)&EisaInfo, PComponent ))
    {
        return FALSE;
    }

    //
    // Return the requested virtual (CPU) address
    //

    *IoBusAddress = EisaInfo.IoStart;
    return TRUE;
}

// ----------------------------------------------------------------------------
// PROCEDURE:   FwFileChecksum:
//
// DESCRIPTION: The routine performs a byte checksum on the specified file.
//
// ARGUMENTS:   Path            File path
//              Checksum        File checksum
//
// RETURN:      ESUCCESS        Operation completed successfully.
//              ...other        Error code.
//
// ASSUMPTIONS: none
//
// CALLS:       FwFileIdChecksum
//
// GLOBALS:     none
//
// NOTES:       The file length must be contained in a ULONG (4G -1).
// ----------------------------------------------------------------------------
//

ARC_STATUS
FwFileChecksum
    (
    IN  PCHAR  Path,
    OUT PUCHAR Checksum
    )
{
    ARC_STATUS  Status;
    ULONG       FileId;
    FILE_INFORMATION FileInfo;

    //
    // initialize checksum to zero
    //

    *Checksum = 0;

    //
    // open file
    //

    if (Status = FwOpen( Path, ArcOpenReadOnly, &FileId ))
    {
        return Status;
    }

    //
    // get file information
    //

    if (Status = FwGetFileInformation( FileId, &FileInfo ));

    //
    // return an error if file is greater than 4G - 1
    //

    else if (FileInfo.EndingAddress.HighPart != 0)
    {
        Status = EINVAL;
    }

    //
    // perform the checksum and return the status
    //

    else
    {
        Status = FwFileIdChecksum( FileId,
                                   FileInfo.StartingAddress,
                                   FileInfo.EndingAddress.LowPart,
                                   Checksum );
    }

    //
    // clase file and return status
    //

    FwClose( FileId );
    return Status;
}






// ----------------------------------------------------------------------------
// PROCEDURE:   FwChecksumByte:
//
// DESCRIPTION: The routine performs a byte checksum on the specified buffer.
//
// ARGUMENTS:   Buffer          Pointer to area to checksum.
//              Length          Length of area to checksum (bytes).
//              ChecksumByte    The pointed byte is used in input as a
//                              starting checksum value and in output as
//                              the final checksum value.
//
// RETURN:      ESUCCESS        operation completed successfully
//
// ASSUMPTIONS: none
//
// CALLS:       none
//
// GLOBALS:     none
//
// NOTES:       none
//
// ----------------------------------------------------------------------------
//

ARC_STATUS
FwChecksumByte (
    IN PUCHAR Buffer,
    IN ULONG Length,
    IN OUT PUCHAR ChecksumByte
    )
{
    //
    // checksum the buffer and exit
    //

    while (Length--)
    {
        *ChecksumByte += *Buffer++;
    }

    return ESUCCESS;
}



// ----------------------------------------------------------------------------
// PROCEDURE:   FwFileIdChecksum:
//
// DESCRIPTION: The routine performs a byte checksum within the spcified
//              range of the FileId.
//
// ARGUMENTS:   FileId          File table entry index.
//              StartingOffset  Beginning of checksum range (byte offset).
//              Length          Number of bytes to checksum.
//              Checksum        Input : start checksum value
//                              Output: image checksum
//
// RETURN:      ESUCCESS        Operation completed successfully.
//              ...other        Error code.
//
// ASSUMPTIONS: none
//
// CALLS:       FwChecksumByte
//
// GLOBALS:     none
//
// NOTES:       This routine can also be called during the "open" phase
//              (the open bit is not set), because the read and seek functions
//              are called directly using the table within the file id
//              descriptor.
// ----------------------------------------------------------------------------
//

ARC_STATUS
FwFileIdChecksum
    (
    IN ULONG FileId,
    IN LARGE_INTEGER StartingOffset,
    IN ULONG Length,
    IN OUT PUCHAR Checksum
    )
{
    ARC_STATUS Status;
    ULONG  BytesToRead;
    ULONG  Count;
    UCHAR  TempBuffer[MAXIMUM_SECTOR_SIZE + MAX_DCACHE_LINE_SIZE];
    PUCHAR TempPointer = ALIGN_BUFFER( TempBuffer );

    //
    // If buffer length is zero, return ESUCCESS
    //

    if (Length==0)
    {
        return ESUCCESS;
    }

    //
    // position the file pointer
    //

    if (Status = (BlFileTable[FileId].DeviceEntryTable->Seek)
                        (FileId, &StartingOffset, SeekAbsolute))
    {
        return Status;
    }

    //
    // perform the checksum
    //

    do
    {
        BytesToRead = MIN( Length, MAXIMUM_SECTOR_SIZE );

        //
        // fill the buffer
        //

        if (Status = (BlFileTable[FileId].DeviceEntryTable->Read)
                        (FileId, TempPointer, BytesToRead, &Count))
        {
            return Status;
        }

        //
        // make sure that we got the requested number of bytes
        //

        if (Count != BytesToRead)
        {
            return EINVAL;
        }

        //
        // checksum the area
        //

        FwChecksumByte( TempPointer, BytesToRead, Checksum);

    } while ( Length -= BytesToRead );

    //
    // all done
    //

    return Status;
}





// ----------------------------------------------------------------------------
// PROCEDURE:   FwGetMnemonicKey;
//
// DESCRIPTION: The routine returns the specified key.  The specified key
//              within the path string must be in digits, an error is returned
//              otherwise.
//
// ARGUMENTS:   Path            pointer to the pathname string
//              Mnemonic        mnemonic string
//              Key             pointer to buffer used to hold the ouput
//
// RETURN:      TRUE            returned if all correct
//              FALSE           returned invalid input parameter
//
// ASSUMPTIONS:
//
// CALLS:
//
// GLOBALS:
//
// NOTES:
// ----------------------------------------------------------------------------
//

BOOLEAN
FwGetMnemonicKey
    (
    IN PCHAR  Path,
    IN PCHAR  Mnemonic,
    IN PULONG Key
    )
{
    PCHAR Tmp;
    PCHAR Tmp2;
    CHAR  Digits[ KEY_MAX_DIGITS + 1 ];
    ULONG i;
    CHAR  String[ MAX_MNEMONIC_LEN + 3 ];       // mnemonic + ')' + '(' + '\0'

    PRINTDBG("FwGetMnemonicKey\n\r");

    //
    // Construct a string of the form ")mnemonic("
    //

    String[0]=')';
    for(i=1; *Mnemonic; i++)
    {
        String[i] = * Mnemonic++;
    }
    String[i++]='(';
    String[i]='\0';

    //
    // first look for the "mnemonic(" string.
    //

    if ( (Tmp = strstr( Path, &String[1] )) == NULL)
    {
        return FALSE;
    }

    //
    // if not the begin of the path name, look for the ")mnemonic(" string.
    //

    if (Tmp != Path)
    {
        if ( (Tmp = strstr( Path, String )) == NULL)
        {
            return FALSE;
        }
    }
    else
    {
        i--;
    }

    //
    // skip the mnemonic
    //

    Tmp+=i;

    //
    // make sure that the number of digits is within the allowed range
    //

    if ((Tmp2 = strchr(Tmp, ')')) == NULL  ||  Tmp2 - Tmp > KEY_MAX_DIGITS )
    {
        return FALSE;
    }

    //
    // convert the value in between parentesis to integer
    //

    for ( i=0;  *Tmp != ')';  i++ )
    {
        if ( !isdigit( Digits[i] = *Tmp++ ))
        {
            return FALSE;
        }
    }
    Digits[i]='\0';

    //
    // return the converted number
    //

    *Key = i ? atoi(Digits) : 0;
    return TRUE;
}






// ----------------------------------------------------------------------------
// PROCEDURE:   FwGetNextMnemonic:
//
// DESCRIPTION: The routine returns the next mnemonic in the list.
//              If Mnemonic is a null string, the first mnemonic of the list
//              is returned.  The FALSE value is returned if the specified
//              mnemonic is not present or end of list.
//
//
// ARGUMENTS:   Path            pointer to the pathname string.
//              Mnemonic        mnemonic string pointer
//              NextMnemonic    next mnemonic string pointer
//
// RETURN:      TRUE            returned if all correct
//              FALSE           returned if invalid input parameter or end of
//                              list
//
// ASSUMPTIONS: The path is composed by ARC mnemonic names as follows:
//              adapter(.)..adapter(.)controller(.)peripheral(.)'\0'.
//              The NextMnemonic string is big enough to hold the mnemonic
//              name.
//
// CALLS:
//
// GLOBALS:
//
// NOTES:
// ----------------------------------------------------------------------------
//

BOOLEAN
FwGetNextMnemonic
    (
    IN  PCHAR Path,
    IN  PCHAR Mnemonic,
    OUT PCHAR NextMnemonic
    )
{
    //
    // local variables
    //

    PCHAR Tmp = Path;
    PCHAR Tmp2;
    ULONG i;
    CHAR  String[ MAX_MNEMONIC_LEN + 3 ];       // mnemonic + ')' + '(' + '\0'

    PRINTDBG("FwGetNextMnemonic\n\r");

    if ( *Mnemonic )
    {
        //
        // Construct a string of the form ")mnemonic("
        //

        String[0]=')';
        for(i=1; *Mnemonic; i++)
        {
            String[i] = *Mnemonic++;
        }
        String[i++]='(';
        String[i]='\0';

        //
        // first look for the "mnemonic(" string.
        //

        if ( (Tmp = strstr( Path, &String[1] )) == NULL)
        {
            return FALSE;
        }

        //
        // if not the begin of the path name, look for the ")mnemonic(" string.
        //

        if (Tmp != Path)
        {
            if ( (Tmp = strstr( Path, String )) == NULL)
            {
                return FALSE;
            }
            Tmp++;
        }

        //
        // return an error if there is another mnemonic with same name
        //

        if (strstr( Tmp, String ) != NULL)
        {
            return FALSE;
        }

        //
        // find the start of the next mnemonic
        //

        if ( (Tmp = strchr(Tmp,')')) == NULL  )
        {
            return FALSE;
        }
        Tmp++;
    }

    //
    // find the end of the next mnemonic and copy it.
    //

    if ( (Tmp2 = strchr(Tmp,'(')) == NULL  ||  Tmp2 == Tmp  ||
          strchr( Tmp2, ')') == NULL )
    {
        return FALSE;
    }

    //
    // copy the mnemonic
    //

    while( Tmp < Tmp2 )
    {
        *NextMnemonic++ = *Tmp++;
    }
    *NextMnemonic = '\0';

    //
    // return all done
    //

    return TRUE;
}






// ----------------------------------------------------------------------------
// PROCEDURE:   FwGetMnemonicPath:
//
// DESCRIPTION: The routine returns a pointer to the mnemonic path.
//              The routine uses the first mnemoic match to built
//              the mnemonic path.
//
// ARGUMENTS:   Path            pointer to the path string.
//              Mnemonic        pointer to the mnemomic.
//              MnemonicPath    pointer to the mnemonic path string.
//
// RETURN:      TRUE            returned if all correct.
//              FALSE           returned for any error.
//
// ASSUMPTIONS: the string pointed by MnemonicPath must be enough large to
//              contain the mnemoinc path.
//
// CALLS:
//
// GLOBALS:
//
// NOTES:
// ----------------------------------------------------------------------------
//

BOOLEAN
FwGetMnemonicPath
    (
    IN PCHAR  Path,
    IN PCHAR  Mnemonic,
    OUT PCHAR MnemonicPath
    )
{
    //
    // local variables
    //

    PCHAR Tmp;
    ULONG i;
    CHAR  String[ MAX_MNEMONIC_LEN + 3 ];       // mnemonic + ')' + '(' + '\0'

    PRINTDBG("FwGetMnemonicPath\n\r");

    //
    // Construct a string of the form ")mnemonic("
    //

    String[0]=')';
    for(i=1; *Mnemonic; i++)
    {
        String[i] = *Mnemonic++;
    }
    String[i++]='(';
    String[i]='\0';

    //
    // first look for the "mnemonic(" string.
    //

    if ( (Tmp = strstr( Path, &String[1] )) == NULL)
    {
        return FALSE;
    }

    //
    // if not the begin of the path name, look for the ")mnemonic(" string.
    //

    if (Tmp != Path)
    {
        if ( (Tmp = strstr( Path, String )) == NULL)
        {
            return FALSE;
        }
        Tmp++;
    }

    //
    // find the end of this mnemonic
    //

    if ( (Tmp = strchr(Tmp,')')) == NULL  )
    {
        return FALSE;
    }

    //
    // copy the mnemonic path in the output string
    //

    strncpy( MnemonicPath, Path, Tmp-Path+1 );
    MnemonicPath[ Tmp-Path+1 ] = '\0';

    //
    // all done, return
    //

    return TRUE;
}





// ----------------------------------------------------------------------------
// PROCEDURE:   GetNextPath:
//
// DESCRIPTION: The routine isolates the first path string entry form the
//              specified path string list.
//
// ARGUMENTS:   PathList        pointer to the path string list pointer.
//                              it is updated upon return to point the
//                              next path string entry.
//              PathTarget      pointer to the isolated path string.
//
//
// RETURN:      TRUE            returned if operation is successful.
//              FALSE           returned if end of list has been reached.
//
// ASSUMPTIONS: The path string list is ended with a null char ('\0') and
//              the path string entries are separated from each other with
//              the ';' char.
//              The target string has enough space to hold the isolated
//              path string.
//
// CALLS:
//
// GLOBALS:
//
// NOTES:
// ----------------------------------------------------------------------------
//

BOOLEAN
GetNextPath
    (
    IN OUT PCHAR *PPathList,
    OUT PCHAR PathTarget
    )
{
    PRINTDBG("GetNextPath\n\r");

    //
    // if end of list, retrun with error
    //

    if (!**PPathList)
    {
        return FALSE;
    }

    //
    // copy the path string and update the pointer
    //

    for (; *PathTarget=**PPathList; *PathTarget++, *(*PPathList)++)
    {
        if (**PPathList==';')
        {
            *PathTarget='\0';
            (*PPathList)++;
            break;
        }
    }
    return TRUE;
}




