/*
 * $Log:   V:/Flite/archives/TrueFFS5/Src/BLOCKDEV.C_V  $
 * 
 *    Rev 1.43   Apr 15 2002 20:14:20   oris
 * Prevent any change to the flPolicy environment variable. It must be set to FL_DEFAULT_POLICY.
 * 
 *    Rev 1.42   Apr 15 2002 07:33:56   oris
 * Moved doc2exb functions declaration to blockdev.c
 * Moved bdkCall function declaration to docbdk.h
 * Bug fix - Bad arguments sanity check for FL_MTD_BUS_ACCESS_TYPE.
 * Bug fix - Missing initialization of global variables:
 *  - bus access in flSetDocBusRoutine() and flGetDocBusRoutine() and when ENVIRONMENT_VARS compilation flag is not defined.
 *  - verify write when ENVIRONMENT_VARS  compilation flag is not defined.
 *  - All global (for all sockets and volumes) environment variables where not reinitialized after flExit call.
 *  - Renamed initEnvVarsDone to initGlobalVarsDone.
 *  - Renamed flInitEnvVars() to flInitGlobalVars().
 * Changed flBusConfig environment array to dword variables instead of single byte.
 * Added support for VERIFY_ERASED_SECTOR compilation flag.
 * Placed multi-doc environment variable under the MULTI_DOC compilation flag.
 * VolumeInfo routine - remove warning by placing eraseCyclesPerUnit variable under FL_LOW_LEVEL compilation flag.
 * 
 *    Rev 1.41   Feb 19 2002 20:58:08   oris
 * Moved include directives to blockdev.h file.
 * Bug fix - findFreeVolume routine did not initialize flfash records of the volume. It caused exception when formatting a DiskOnChip with more then 1 partition.
 * Compilation error missing ifdef EXIT.
 * Convert TL_LEAVE_BINARY_AREA to FL_LEAVE_BINARY_AREA before sending it to the TL.
 * Bug fix - volumeInfo routine might cause exception if osak version or driver version is larger the designated field.
 * use intermediate variable before sending irFlags to otpSize routine.
 * 
 *    Rev 1.40   Jan 29 2002 20:07:42   oris
 * Added NAMING_CONVENTION prefix and extern "C" for cpp files to all public routines:
 * flSetEnvVolume, flSetEnvSocket , flSetEnvAll , flSetDocBusRoutine , flGetDocBusRoutine, flBuildGeometry , bdCall and flExit
 * Changed LOW_LEVEL compilation flag with FL_LOW_LEVEL to prevent definition clashes.
 * Removed warnings.
 * Moved writeIPL sanity check to MTD level.
 * Removed download operation after writeIPL call (it is now one of the routines parameters).
 * Added sanity check (null pointer passed) to flSetEnvVolume, flSetDocBusRoutine and flGetDocBusRoutine.
 * 
 *    Rev 1.39   Jan 28 2002 21:23:32   oris
 * Bug fix - initialization of flPolicy variable caused a memory lick (set max unit chain to 0).
 * Changed FL_NFTL_CACHE_ENABLED to FL_TL_CACHE_ENABLED.
 * Changed flSetDocBusRoutine interface and added flGetDocBusRoutine. 
 * Added FL_IPL_DOWNLOAD flag to writeIPL routine in order to control whether the IPL will be reloaded after the update.
 * 
 *    Rev 1.38   Jan 23 2002 23:30:46   oris
 * Added a call to flExit() in case flSuspend was restored to FL_OFF.
 * Moved Alon based DiskOnChip write IPL routine from blockdev, to diskonc.
 * Added sanity check to flCheckVolume() - Make sure irData is NULL and irLength is set to 0.
 * 
 *    Rev 1.37   Jan 21 2002 20:43:36   oris
 * Bug fix - Missing\bad support for FL_SECTORS_VERIFIED_PER_FOLDING and FL_VERIFY_WRITE_BDTL environment variables.
 * 
 *    Rev 1.36   Jan 20 2002 20:40:20   oris
 * Improved documentation of bdFormatPhisycalDrive - Added TL_NORMAL_FORMAT flag was added.
 * Removed ifdef NO_PHYSICAL_IO from flGetBPB routine.
 * 
 *    Rev 1.35   Jan 17 2002 22:56:22   oris
 * Placed docbdk.h under BDK_ACCESS ifdef
 * Added include directive to docsys.h   
 * Removed function prototypes from header.
 * Changed FLFlash record in the volume structure into a pointer
 * Added flBusConfig variable and flSetDocBusRoutine() routine for runtime control over memory access routines - under the FL_NO_USE_FUNC definition.
 * Added flSectorsVerfiedPerFolding environment variable was added
 * Added   flSuspendMode environment variable was added.
 * Changed flVerifyWrite environment variable : 4 for Disk partitions 3 for Binary and 1 for the rest.
 * Changed flPolicy to be partition specific.
 * Changed flSetEnv() routine was changed into 3 different routines: flSetEnvVolume / flSetEnvSocket / flSetEnvAll
 * Variables types for environment variables were changed to the minimal size needed.
 * Added flInitEnvVars() routine for setting default values to environment variables.
 * Use single FLFlash record for each volume - (change vol.flash to a pointer instead of the record itself).
 * Added \r to all DEBUG_PRINT routines.
 * Removed SINGLE_BUFFER ifdef.
 * Added volume verification after format for faster write performance with FL_OFF.
 * Removed FLFlash parameter to all TL calls (flMount / flFormat / flPreMount )  
 * Added support for firmware other then the 3 defaults (getExbInfo additional parameter)
 * Added support for M+ 16MB
 * Added partition parameter to setBusy - Stores current partition in the socket record for the use of lower TrueFFS layers .
 * Changed tffsmin to TFFSMIN
 * Added FL_VERIFY_VOLUME functionNo.
 * Made sure flInit() initializes all sockets and flashes volumes
 * Added flClearQuickMountInfo() routine (FL_CLEAR_QUICK_MOUNT_INFO)
 * readIPL routine now supports all DiskOnChip 
 * 
 *    Rev 1.34   Nov 29 2001 20:53:44   oris
 * Bug fix - flInquireCapabilities() returned bad status for SUPPORT_WRITE_IPL_ROUTINE.
 * 
 *    Rev 1.33   Nov 21 2001 11:40:44   oris
 * Changed FL_MULTI_DOC_NOT_ACTIVE to FL_OFF, FL_VERIFY_WRITE_MODE to FL_MTD_VERIFY_WRITE , FL_WITHOUT_VERIFY_WRITE to FL_OFF , FL_MARK_DELETE to FL_ON.
 * 
 *    Rev 1.32   Nov 08 2001 10:44:04   oris
 * Added flVerifyWrite environment variable that controls the verify write mode at run-time. 
 * Added support for large DiskOnChip in flBuildGeometry.
 * Remove s/w protection from binary partition.
 * Placed flAbsMount under ABS_READ_WRITE compilation flag.
 *
 *    Rev 1.31   Sep 24 2001 18:23:04   oris
 * Removed warnings.
 * 
 *    Rev 1.30   Sep 15 2001 23:44:22   oris
 * Changed BIG_ENDIAN to FL_BIG_ENDIAN
 * 
 *    Rev 1.29   Aug 02 2001 20:04:04   oris
 * Added support for 1k IPL for DiskOnChip 2000 TSOP - writeIPL() 
 * 
 *    Rev 1.28   Jul 29 2001 19:14:24   oris
 * Bug fix for TrueFFS internal mutex when using multi-doc and environment variables (when no multi-doc feature is disbaled)
 * 
 *    Rev 1.27   Jul 13 2001 00:59:28   oris
 * Changed flash lifetime calculation according to specific flash.
 * 
 *    Rev 1.26   Jun 17 2001 08:16:50   oris
 * Added NO_PHISICAL_IO compilation flag to reduce code size.
 * Added NO_READ_BBT_CODE compilation flag to reduce code size.
 * Changed placeExbByBuffer exbflags argument to word instead of byte to  support /empty flag.
 * Deleted redundent #ifdef MULTI_DOC in flInit().
 * 
 *    Rev 1.25   May 31 2001 18:11:52   oris
 * Removed readBBT routine from under the #ifndef FL_READ_ONLY.
 * 
 *    Rev 1.24   May 16 2001 21:16:08   oris
 * Changed the Binary state (0,1) of the environment variables to meaningful definitions.
 * Added flMtlDefragMode environment variable.
 * Added the FL_ prefix to the following defines: ON, OFF
 * Bug fix - One of the "ifndef" statement of NO_IPL_CODE was coded as "ifdef"
 * Change "data" named variables to flData to avoid name clashes.
 * 
 *    Rev 1.23   May 09 2001 00:31:00   oris
 * Added NO_PHYSICAL_IO , NO_IPL_CODE and NO_INQUIRE_CAPABILITY compilation flags to reduce code size.
 *
 *    Rev 1.22   May 06 2001 22:41:04   oris
 * Bug fix - flInquireCapabilities routine did not return the correct value when capability was not supported.
 * Added SUPPORT_WRITE_IPL_ROUTIN capability.
 * Removed warnings.
 * 
 *    Rev 1.21   May 01 2001 17:05:10   oris
 * Bug fix - bad argument check in flSetEnv routine.
 * 
 *    Rev 1.20   Apr 30 2001 17:57:00   oris
 * Added new environment variable flMarkDeleteOnFlash.
 * 
 *    Rev 1.19   Apr 24 2001 17:05:38   oris
 * Bug fix flMoutVolume routine return the correct hidden sectors even if high level mount fails.
 * Bug fix Write IPL routine supports Doc2000 TSOP as well as Millennium Plus.
 * Support readBBT new interface.
 * 
 *    Rev 1.18   Apr 18 2001 20:43:00   oris
 * Added force download call after writing IPL.
 * 
 *    Rev 1.17   Apr 18 2001 19:14:24   oris
 * Bug fix - binary partition insert and remove key routine no longer stop the place exb proccess.
 * 
 *    Rev 1.16   Apr 18 2001 09:26:22   oris
 * noOfDrives variable was changed to unsigned. This is a bug fix for vxWorks boot code.
 * Make sure blockdev does not try to mount more volumes then the VOLUMES definition.
 * 
 *    Rev 1.15   Apr 16 2001 13:02:38   oris
 * Removed warrnings.
 * 
 *    Rev 1.14   Apr 12 2001 06:48:22   oris
 * Added call to download routine after physical format in order 
 * to load new IPL and to initialize new protection information.
 * Removed warnings.
 *
 *    Rev 1.13   Apr 03 2001 16:33:10   oris
 * Removed unused variables.
 * Added proper casting in otpSize call.
 * 
 *    Rev 1.12   Apr 03 2001 14:33:16   oris
 * Bug fix in absRead sectors when reading multiple sectors.
 *
 *    Rev 1.11   Apr 01 2001 14:57:58   oris
 * Bug fix in read multiple sectors.
 *
 *    Rev 1.10   Mar 28 2001 06:19:12   oris
 * Removed flChangeEnvVar prototype.
 * Left alligned all # directives.
 * Bug fix in flSetEnv ((value !=0)||(value!=1)) => ((value !=0)&&(value!=1)) + downcast for given argument + add return status code
 * Removed unused variables.
 * Remove casting warnnings.
 * Add arg check in bdFormatPhysicalDrive for BDTLPartitionInfo != NULL.
 * Bug fix for absWrite - multi-secotr write should be initialized with zeros to prevent sector found return code.
 * Replaced the numbers in writeProtect routine to defines (moved from ioctl.h to blockdev.h).
 * Add break for mdocp identification in getPhysicalInfo.
 * Added readIPL routine.
 * Added LOG_FILE compilation flag for EDC errors for mdocp.
 * Added initialization of tl table in flInit.
 * flExit mutex bug fix.
 *
 *    Rev 1.9   Mar 05 2001 21:00:34   oris
 * Bug fix - initialize exbLen argument in bdFormatVolume.
 * Bug fix - initialize bdtlFp flags fiels in bdFormatVolume.
 * Restored the flExit pragma under __BORLANDC__ compilation flag
 *
 *    Rev 1.8   Feb 22 2001 20:21:34   oris
 * Bug fix in flExit with multi-doc release uncreated mutxe.
 *
 *    Rev 1.7   Feb 20 2001 15:44:58   oris
 * Bug fix for mutex initialization in flInit.
 *
 *    Rev 1.6   Feb 18 2001 23:29:28   oris
 * Bug fix - Added findFreeVolume call in flFormatPhysicalDrive.
 * bug fix - Added partition sanity check in bdcall entrence.
 * bug fix - Increamented pVol before entering loop in flExit.
 *
 *    Rev 1.5   Feb 18 2001 14:13:22   oris
 * Restored FL_BACKGROUND.
 * Place bdkCall extern prototype under BDK_ACCESS compilation flag.
 * Changed multiple volume mechanism - Replaced removeVolumeHandles and
 * flConvertDriveHandle routines with flInit + findFreeVolume and changed
 * dismountPhysicalDrive + flEacit to comply.
 * Changed bdcall volume validity check.
 * Added new volume flag VOLUME_ACCUPIED.
 * Moved all the new environment variable (flPolicy,flUseMultiDoc and flMaxUnitChain)
 * from flcustom.c in order to allow backwards competability.
 * Removed globalMutex (for now).
 * Allocate only 1 mutex when multi-doc is registered therfore setBusy takes only 1 mutex .
 * Bug fix - absRead and absWrite add bootSectors to the given sectors.
 * Bug fix - INVALID_VOLUME_HANDLE changed to INVALID_VOLUME_NUMBER.
 * Bug fix - Added (byte FAR1 *) casting in readBBT call.
 * Bug fix - Added (FLCapability FAR2 *) casting in inquire capability call.
 *
 *    Rev 1.4   Feb 14 2001 01:51:32   oris
 * Completly revised flInquireCapabilities routine.
 * Added oldFormat flag to flBuildGeometry routine.
 * Remove FL_BACKGROUND compilation flag.
 * Added arg check for flSetEnv + new FL_SET_MAX_CHAIN.
 * Changed FL_COMPLETE_ASAP_POLICY to FL_SET_POLICY.
 * Moved argument check in flFormatPhysicalDrive to fltl.c.
 * Moved readBBT routine inside bdcall.
 * Placed volumeInfo C\H\S data under ABS_READ_WRITE.
 *
 *    Rev 1.3   Feb 13 2001 02:10:48   oris
 * Changed flChangeEnvVar routine name to flSetEnv
 *
 *    Rev 1.2   Feb 12 2001 11:51:12   oris
 * Added function prototype in begining of the file.
 * Change routine order in the file.
 * Added mutex support for TrueFFS 5.0 partitions while changing bdcall order.
 * flMountVolume returns the number of boot sectors.
 * Moved the writeBBT routine to FLTL.C.
 * Added static before changePassword.
 * Added static before writeProtect.
 * Added static before dismountLowLevel.
 * Added static before writeIPL.
 * Added static before inquireCapabilities.
 * window base is returned by getPhysicalInfo in irLength.
 * Added MULTI_DOC compliation flag.
 *
 *    Rev 1.1   Feb 07 2001 17:55:06   oris
 * Buf fix for writeBBT of no bad blocks
 * Moved readBBT routine from under the fl_read_only define
 * Added casting in calls to readBBT and writeBBT
 * Added checkStatus check for flAbsWrite and flAbsRead routines
 * Added initialization of noOfMTDs and noOfSockets in flInit
 *
 *    Rev 1.0   Feb 04 2001 18:53:04   oris
 * Initial revision.
 *
 */


/*************************************************************************/
/*                    M-Systems Confidential                             */
/*       Copyright (C) M-Systems Flash Disk Pioneers Ltd. 1995-2001      */
/*                     All Rights Reserved                               */
/*************************************************************************/
/*                        NOTICE OF M-SYSTEMS OEM                        */
/*                      SOFTWARE LICENSE AGREEMENT                       */
/*                                                                       */
/*  THE USE OF THIS SOFTWARE IS GOVERNED BY A SEPARATE LICENSE           */
/*  AGREEMENT BETWEEN THE OEM AND M-SYSTEMS. REFER TO THAT AGREEMENT     */
/*  FOR THE SPECIFIC TERMS AND CONDITIONS OF USE,                        */
/*  OR CONTACT M-SYSTEMS FOR LICENSE ASSISTANCE:                         */
/*  E-MAIL = info@m-sys.com                                              */
/*************************************************************************/

#include "bddefs.h"
#include "blockdev.h"

#ifdef FL_BACKGROUND
#include "backgrnd.h"
#endif


/********************* Extern Function Prototype Start *******************/
#ifdef WRITE_EXB_IMAGE
extern FLStatus getExbInfo(Volume vol, void FAR1 * buf, dword bufLen, word exbFlags);
extern FLStatus placeExbByBuffer(Volume vol, byte FAR1 * buf, dword bufLen,
                 word windowBase,word exbFlags);
#endif /* WRITE_EXB_IMAGE */

#if (defined(FORMAT_VOLUME) && defined(COMPRESSION))
extern FLStatus flFormatZIP(unsigned volNo, TL *baseTL , FLFlash * flash);
#endif
#if defined(FILES) && FILES > 0
extern File     fileTable[FILES];       /* the file table */
extern FLStatus flushBuffer(Volume vol);
extern FLStatus openFile(Volume vol, IOreq FAR2 *ioreq);
extern FLStatus closeFile(File *file);
extern FLStatus joinFile (File *file, IOreq FAR2 *ioreq);
extern FLStatus splitFile (File *file, IOreq FAR2 *ioreq);
extern FLStatus readFile(File *file,IOreq FAR2 *ioreq);
extern FLStatus writeFile(File *file, IOreq FAR2 *ioreq);
extern FLStatus seekFile(File *file, IOreq FAR2 *ioreq);
extern FLStatus findFile(Volume vol, File *file, IOreq FAR2 *ioreq);
extern FLStatus findFirstFile(Volume vol, IOreq FAR2 *ioreq);
extern FLStatus findNextFile(File *file, IOreq FAR2 *ioreq);
extern FLStatus getDiskInfo(Volume vol, IOreq FAR2 *ioreq);
extern FLStatus deleteFile(Volume vol, IOreq FAR2 *ioreq, 
                           FLBoolean isDirectory);
extern FLStatus renameFile(Volume vol, IOreq FAR2 *ioreq);
extern FLStatus makeDir(Volume vol, IOreq FAR2 *ioreq);
#endif /* FILES > 0 */

/********************* Extern Function Prototype End *******************/

/********************* Internal Function Prototype Start ***************/

void flInitGlobalVars(void);

/********************* Internal Function Prototype End *****************/

/********************* Global variables Start **************************/

Volume    vols[VOLUMES];
FLMutex   flMutex[SOCKETS];
byte      handleConversionTable[SOCKETS][MAX_TL_PARTITIONS];
FLBoolean initDone = FALSE;           /* Initialization not done yet   */
FLBoolean initGlobalVarsDone = FALSE; /* Initialization of environment */
                                      /* and access type variables not */
                                      /* done yet.                     */
unsigned  noOfDrives;

dword flMsecCounter = 0;

/* bus configuration
 *
 * DiskOnChip minimal bus width
 */
#ifndef FL_NO_USE_FUNC
dword   flBusConfig[SOCKETS];
#endif /* FL_NO_USE_FUNC */

/* Verify write state
 *
 * BDTL   partitions : 0-(MAX_TL_PARTITIONS-1)
 * Binary partitions : MAX_TL_PARTITIONS - (2*MAX_TL_PARTITIONS-2)
 * Last              : 2*MAX_TL_PARTITIONS-1
 */
#if (defined(VERIFY_WRITE) || defined(VERIFY_ERASED_SECTOR))
byte   flVerifyWrite[SOCKETS][MAX_TL_PARTITIONS<<1];
#endif /* VERIFY_WRITE || VERIFY_ERASED_SECTOR */

#ifdef ENVIRONMENT_VARS
cpyBuffer tffscpy; /* Pointer to memory copy routine    */
cmpBuffer tffscmp; /* Pointer to memory compare routine */
setBuffer tffsset; /* Pointer to memory set routine     */

/********************************************/
/* default values for environment variables */
/********************************************/

#if (defined(VERIFY_WRITE) || defined(VERIFY_ERASED_SECTOR))
/* Max sectors verified per write operation (must be even number) */
dword  flSectorsVerifiedPerFolding = 64;
#endif /* VERIFY_WRITE || VERIFY_ERASED_SECTOR */
#ifdef MULTI_DOC
/* No multi-doc (MTL)                        */
byte   flUseMultiDoc;
/* MTL defragmentaion mode (0 - standard)    */
byte   flMTLdefragMode;
#endif /* MULTI_DOC */
/* Set the TL operation policy               */
byte   flPolicy[SOCKETS][MAX_TL_PARTITIONS];
/* Maximum chain length                      */
byte   flMaxUnitChain;
/* Mark the delete sector on the flash       */
byte   flMarkDeleteOnFlash;
/* Read Only mode                            */
byte   flSuspendMode;

/********************* Global variables End *****************************/

/*----------------------------------------------------------------------*/
/*                   f l S e t E n v V o l u m e                        */
/*                                                                      */
/* Change one of TrueFFS environment variables for a specific partition */
/*                                                                      */
/* Note : This routine is used by all other flSetEnv routines.          */
/*        In order to effect variables that are common to several       */
/*        sockets or volumes use INVALID_VOLUME_NUMBER                  */
/*                                                                      */
/* Parameters:                                                          */
/*      variableType    : variable type to cahnge                       */
/*      socket          : Associated socket                             */
/*      volume          : Associated volume (partition)                 */
/*      value           : varaible value                                */
/*                                                                      */
/* Note: Variables common to al sockets must be addressed using socket  */
/*       0 and volume 0.                                                */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, otherwise failed                */
/*      prevValue       : The previous value of the variable            */
/*----------------------------------------------------------------------*/

FLStatus NAMING_CONVENTION flSetEnvVolume(FLEnvVars variableType ,
                  byte socket,byte volume ,
                  dword value, dword FAR2 *prevValue)
{
   /* Arg sanity check */
   if(prevValue == NULL)
   {
      DEBUG_PRINT(("ERROR - prevValue argument is NULL.\r\n"));
      return flBadParameter;
   }
   switch (variableType) /* Check value argument is a valid mode */
   {
      case FL_SET_MAX_CHAIN:
         if ((value > 31) || (value < 1))
         {
            DEBUG_PRINT(("Debug: Error - Chains length must between 0 and 32.\r\n"));
            return flBadParameter;
         }
         break;
#if (defined(VERIFY_WRITE) || defined(VERIFY_ERASED_SECTOR))
      case FL_SECTORS_VERIFIED_PER_FOLDING:
         if (value & 1) /* odd number */
         {
            DEBUG_PRINT(("Debug: Error - sector verification numbr must be even.\r\n"));
            return flBadParameter;
         }
         break;
      case FL_VERIFY_WRITE_BDTL:
         if ((value != FL_UPS) && (value != FL_OFF) && (value != FL_ON))
         {
            DEBUG_PRINT(("Debug: Error - verify write of BDTL can not accept this value.\r\n"));
            return flBadParameter;
         }
         break;
#endif /* VERIFY_WRITE || VERIFY_ERASED_SECTOR */
      case FL_SUSPEND_MODE:
         if((value != FL_OFF)           && 
            (value != FL_SUSPEND_WRITE) && 
            (value != FL_SUSPEND_IO)      )
         {
            DEBUG_PRINT(("Debug: Error - verify write of BDTL can not accept this value.\r\n"));
            return flBadParameter;
         }
         break;
#ifndef FL_NO_USE_FUNC
      case FL_MTD_BUS_ACCESS_TYPE:
         break;
#endif /* FL_NO_USE_FUNC */
      default:
         if ((value != FL_ON)&&(value!=FL_OFF))
         {
            DEBUG_PRINT(("Debug: Error - Value must be either FL_ON (1) or FL_OFF(0).\r\n"));
            return flBadParameter;
         }
   }

   switch (variableType) /* Check volume and socket sanity */
   {
      /* Volume specfic variables */

      case FL_SET_POLICY:
#if (defined(VERIFY_WRITE) || defined(VERIFY_ERASED_SECTOR))
      case FL_VERIFY_WRITE_BDTL:
#endif /* VERIFY_WRITE || VERIFY_ERASED_SECTOR */
#ifdef VERIFY_WRITE
      case FL_VERIFY_WRITE_BINARY:
#endif /* VERIFY_WRITE */
         if ((       volume >= MAX_TL_PARTITIONS          ) ||
             ((variableType == FL_VERIFY_WRITE_BINARY) &&
              (volume       == MAX_TL_PARTITIONS - 1 )    )   )
         {
            DEBUG_PRINT(("Debug: Error - No such volume, therefore can not change environment variable.\r\n"));
            return flBadParameter;
         }
         if (socket>=SOCKETS)
         {
            DEBUG_PRINT(("Debug: Error - No such socket, therefore can not change environment variable.\r\n"));
            return flBadParameter;
         }
         break;

      /* Socket specfic variables */

#ifdef VERIFY_WRITE
      case FL_VERIFY_WRITE_OTHER:
#endif /* VERIFY_WRITE */
      case FL_MTD_BUS_ACCESS_TYPE:
         if (socket>=SOCKETS)
         {
            DEBUG_PRINT(("Debug: Error - No such socket, therefore can not change environment variable.\r\n"));
            return flBadParameter;
         }
         if (volume!=INVALID_VOLUME_NUMBER)
         {
            DEBUG_PRINT(("Debug: Error - This global variable is common to all volumes.\r\n"));
            return flBadParameter;
         }
         break;

      /* Global variables for all sockets and volumes */
#if (defined(VERIFY_WRITE) || defined(VERIFY_ERASED_SECTOR))
      case FL_SECTORS_VERIFIED_PER_FOLDING:
#endif /* VERIFY_WRITE || VERIFY_ERASED_SECTOR */
      case FL_IS_RAM_CHECK_ENABLED:
      case FL_TL_CACHE_ENABLED:
      case FL_DOC_8BIT_ACCESS:
      case FL_MULTI_DOC_ENABLED:
      case FL_SET_MAX_CHAIN:
      case FL_MARK_DELETE_ON_FLASH:
      case FL_SUSPEND_MODE:
      case FL_MTL_POLICY:
         if ((socket!=INVALID_VOLUME_NUMBER) || (volume!=INVALID_VOLUME_NUMBER))
         {
            DEBUG_PRINT(("Debug: Error - This global variable is common to all sockets and volumes.\r\n"));
            return flBadParameter;
         }
         break;

      default:

         DEBUG_PRINT(("Debug: Unknown variable type.\r\n"));
         return flFeatureNotSupported;
   }

   /* Make sure environement variables are */
   /* initialized to their default values  */
   flInitGlobalVars();


   /***************************************************/
   /* Arguments have been checked now change variable */
   /* and report the previous value.                  */
   /***************************************************/

   switch (variableType)
   {
      case FL_IS_RAM_CHECK_ENABLED:
         *prevValue                  = (dword)flUseisRAM;
         flUseisRAM                  = (byte)value;
         break;
      case FL_TL_CACHE_ENABLED:
         *prevValue                  = (dword)flUseNFTLCache;
         flUseNFTLCache              = (byte)value;
         break;
      case FL_DOC_8BIT_ACCESS:
         *prevValue                  = (dword)flUse8Bit;
         flUse8Bit                   = (byte)value;
         break;
      case FL_SET_MAX_CHAIN:
         *prevValue                  = (dword)flMaxUnitChain;
         flMaxUnitChain              = (byte)value;
         break;
      case FL_MARK_DELETE_ON_FLASH:
         *prevValue                  = (dword)flMarkDeleteOnFlash;
         flMarkDeleteOnFlash         = (byte)value;
         break;
#ifdef MULTI_DOC
      case FL_MULTI_DOC_ENABLED:
         *prevValue                  = (dword)flUseMultiDoc;
         flUseMultiDoc               = (byte)value;
         break;
      case FL_MTL_POLICY:
         *prevValue                  = (dword)flMTLdefragMode;
         flMTLdefragMode             = (byte)value;
         break;
#endif /* MULTI_DOC */
      case FL_SUSPEND_MODE:
         if((value == FL_OFF) && (flSuspendMode != FL_OFF))
#ifdef EXIT
            flExit();
#endif /* EXIT */
         *prevValue                  = (dword)flSuspendMode;
         flSuspendMode               = (byte)value;
         break;
      case FL_SET_POLICY:
         *prevValue                  = (dword)flPolicy[socket][volume];
/*       flPolicy[socket][volume]    = (byte)value; */
         break;
#if (defined(VERIFY_WRITE) || defined(VERIFY_ERASED_SECTOR))
      case FL_SECTORS_VERIFIED_PER_FOLDING:
         *prevValue                  = flSectorsVerifiedPerFolding;
         flSectorsVerifiedPerFolding = value;
         break;
      case FL_VERIFY_WRITE_BDTL:
         *prevValue                    = (dword)flVerifyWrite[socket][volume];
         flVerifyWrite[socket][volume] = (byte)value;
         break;
#endif /* VERIFY_WRITE || VERIFY_ERASED_SECTOR */
#ifdef VERIFY_WRITE
      case FL_VERIFY_WRITE_BINARY:
         *prevValue      = (dword)flVerifyWrite[socket][volume+MAX_TL_PARTITIONS];
         flVerifyWrite[socket][volume+MAX_TL_PARTITIONS] = (byte)value;
         break;
      case FL_VERIFY_WRITE_OTHER:
         *prevValue      = (dword)flVerifyWrite[socket][(MAX_TL_PARTITIONS<<1)-1];
         flVerifyWrite[socket][(MAX_TL_PARTITIONS<<1)-1] = (byte)value;
         break;
#endif /* VERIFY_WRITE */

      default: /* FL_MTD_BUS_ACCESS_TYPE */
#ifndef FL_NO_USE_FUNC
         *prevValue          = flBusConfig[socket];
         flBusConfig[socket] = (dword)value;
#endif /* FL_NO_USE_FUNC */
         break;
   }
   return flOK;
}

/*----------------------------------------------------------------------*/
/*                       f l S e t E n v S o c k e t                    */
/*                                                                      */
/* Change one of TrueFFS environment variables for a specific sockets.  */
/*                                                                      */
/* Parameters:                                                          */
/*      variableType    : variable type to cahnge                       */
/*      socket          : socket number                                 */
/*      value           : varaible value                                */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, otherwise failed                */
/*      prevValue       : The previous value of the variable            */
/*                        if there are more then 1 partition in that    */
/*                        socket , the first partition value is returned*/
/*----------------------------------------------------------------------*/

FLStatus NAMING_CONVENTION flSetEnvSocket(FLEnvVars variableType , byte socket , dword value, dword FAR2 *prevValue)
{
   FLStatus status = flOK;
   byte     volume = 0;

   switch (variableType) /* Check volume and socket sanity */
   {
      /* Volume specific variables */

      case FL_SET_POLICY:
#if (defined(VERIFY_WRITE) || defined(VERIFY_ERASED_SECTOR))
      case FL_VERIFY_WRITE_BDTL:
#endif /* VERIFY_WRITE || VERIFY_ERASED_SECTOR */
         status = flSetEnvVolume(variableType,socket,MAX_TL_PARTITIONS-1,value,prevValue);
#ifdef VERIFY_WRITE
      case FL_VERIFY_WRITE_BINARY:
#endif /* VERIFY_WRITE */
         for (;(volume<MAX_TL_PARTITIONS-1)&&(status == flOK);volume++)
            status = flSetEnvVolume(variableType,socket,volume,value,prevValue);
         break;

      /* Socket specific variables */

#ifdef VERIFY_WRITE
      case FL_VERIFY_WRITE_OTHER:
#endif /* VERIFY_WRITE */
#ifndef FL_NO_USE_FUNC
      case FL_MTD_BUS_ACCESS_TYPE:
#endif /* FL_NO_USE_FUNC */
#if (defined(VERIFY_WRITE) || !defined(FL_NO_USE_FUNC))
         status = flSetEnvVolume(variableType,socket,INVALID_VOLUME_NUMBER,value,prevValue);
         break; 
#endif /* not FL_NO_USE_FUNC || VERIFY_WRITE */

      /* Either global variables , or unknown */

      default:
         if(socket != INVALID_VOLUME_NUMBER) /* Was not called from flSetEnv */
         {
            DEBUG_PRINT(("Debug: Variable type is either unknown or not socket related.\r\n"));
            return flBadParameter;
         }
         status = flSetEnvVolume(variableType,INVALID_VOLUME_NUMBER,INVALID_VOLUME_NUMBER,value,prevValue);
         break; 
   }
   return status;
}

/*----------------------------------------------------------------------*/
/*                       f l S e t E n v All                            */
/*                                                                      */
/* Change one of TrueFFS environment variables for all systems, sockets */
/* and partitions.                                                      */
/*                                                                      */
/* Parameters:                                                          */
/*      variableType    : variable type to cahnge                       */
/*      value           : varaible value                                */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, otherwise failed                */
/*      prevValue       : The previous value of the variable            */
/*----------------------------------------------------------------------*/

FLStatus NAMING_CONVENTION flSetEnvAll(FLEnvVars variableType , dword value, dword FAR2 *prevValue)
{
   FLStatus status = flOK;
   byte     socket;

   switch (variableType) /* Check volume and socket sanity */
   {
      case FL_SET_POLICY:           /* Per volume */
      case FL_VERIFY_WRITE_BDTL:    /* Per volume */
      case FL_VERIFY_WRITE_BINARY:  /* Per volume */
      case FL_VERIFY_WRITE_OTHER:   /* Per socket */
      case FL_MTD_BUS_ACCESS_TYPE:  /* Per socket */
         for (socket=0;(socket<SOCKETS)&&(status == flOK);socket++)
            status = flSetEnvSocket(variableType,socket,value,prevValue);
         return status;
      default:
         return flSetEnvVolume(variableType,INVALID_VOLUME_NUMBER,INVALID_VOLUME_NUMBER,value,prevValue);
   }
}

#endif /* ENVIRONMENT_VARS */

#ifndef FL_NO_USE_FUNC
/*----------------------------------------------------------------------*/
/*                  f l S e t D o c B u s R o u t i n e                 */
/*                                                                      */
/* Set user defined memory acces routines for DiskOnChip.               */
/*                                                                      */
/* Parameters:                                                          */
/*      socket      : Socket number to install routine for.             */
/*      structPtr   : Pointer to function structure.                    */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

FLStatus NAMING_CONVENTION flSetDocBusRoutine(byte socket, FLAccessStruct FAR1 * structPtr)
{
   FLFlash* flash;

   /* Arg sanity check */
   if (socket >= SOCKETS)
   {
      DEBUG_PRINT(("Error : change SOCKETS definition in flcustom.h to support that many sockets.\r\n"));
      return flFeatureNotSupported;
   }
   if(structPtr == NULL)
   {
      DEBUG_PRINT(("ERROR - structPtr argument is NULL.\r\n"));
      return flBadParameter;
   }

   /* Make sure global variables are initialized to their default values */
   flInitGlobalVars();

   flash = flFlashOf(socket);

   flash->memWindowSize = structPtr->memWindowSize;
   flash->memRead       = structPtr->memRead;
   flash->memWrite      = structPtr->memWrite;
   flash->memSet        = structPtr->memSet;
   flash->memRead8bit   = structPtr->memRead8bit;
   flash->memRead16bit  = structPtr->memRead16bit;
   flash->memWrite8bit  = structPtr->memWrite8bit;
   flash->memWrite16bit = structPtr->memWrite16bit;

   flBusConfig[socket]  = FL_ACCESS_USER_DEFINED;
   return flOK;
}

/*----------------------------------------------------------------------*/
/*                  f l G e t D o c B u s R o u t i n e                 */
/*                                                                      */
/* Get currently installed memory access routines for DiskOnChip.       */
/*                                                                      */
/* Parameters:                                                          */
/*      socket      : Socket number to install routine for.             */
/*      structPtr   : Pointer to function structure.                    */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

FLStatus NAMING_CONVENTION flGetDocBusRoutine(byte socket, FLAccessStruct FAR1 * structPtr)
{
   FLFlash* flash;

   /* Arg sanity check */
   if (socket >= SOCKETS)
   {
      DEBUG_PRINT(("Error : change SOCKETS definition in flcustom.h to support that many sockets.\r\n"));
      return flFeatureNotSupported;
   }
   if(structPtr == NULL)
   {
      DEBUG_PRINT(("ERROR - structPtr argument is NULL.\r\n"));
      return flBadParameter;
   }

   /* Make sure global variables are initialized to their default values */
   flInitGlobalVars();

   flash = flFlashOf(socket);

   structPtr->memWindowSize = flash->memWindowSize;
   structPtr->memRead       = flash->memRead;
   structPtr->memWrite      = flash->memWrite;
   structPtr->memSet        = flash->memSet;
   structPtr->memRead8bit   = flash->memRead8bit;
   structPtr->memRead16bit  = flash->memRead16bit;
   structPtr->memWrite8bit  = flash->memWrite8bit;
   structPtr->memWrite16bit = flash->memWrite16bit;
   structPtr->access        = flBusConfig[socket];

   return flOK;
}

#endif /* FL_NO_USE_FUNC */

/*----------------------------------------------------------------------*/
/*                     f l I n i t G l o b a l V a r s                  */
/*                                                                      */
/* Initializes the FLite system, environment and access type variables. */
/*                                                                      */
/* Parameters:                                                          */
/*      None                                                            */
/*                                                                      */
/* Returns:                                                             */
/*      None                                                            */
/*----------------------------------------------------------------------*/

void flInitGlobalVars(void)
{
   int i;
#ifdef ENVIRONMENT_VARS
   int j;
#endif /* ENVIRONMENT_VARS */

   if(initGlobalVarsDone == TRUE)
     return;

   /* Do not initialize variables on next call */
   initGlobalVarsDone     = TRUE;

   /*
    * Set default values to per socket/volume variables
    */

   for(i=0;i<SOCKETS;i++)
   {
#ifndef FL_NO_USE_FUNC
      flBusConfig[i] = FL_NO_ADDR_SHIFT        |
                       FL_BUS_HAS_8BIT_ACCESS  |
                       FL_BUS_HAS_16BIT_ACCESS |
                       FL_BUS_HAS_32BIT_ACCESS;
#endif /* FL_NO_USE_FUNC */
#ifdef ENVIRONMENT_VARS
      for (j=0;j<MAX_TL_PARTITIONS;j++)
      {
         flPolicy[i][j]   = FL_DEFAULT_POLICY; /* FL_COMPLETE_ASAP */
      }
#if (defined(VERIFY_WRITE) || defined(VERIFY_ERASED_SECTOR))
      for (j=0;j<MAX_TL_PARTITIONS<<1;j++)
      {
         flVerifyWrite[i][j] = FL_OFF; /* FL_ON , FL_UPS */
      }
#endif /* VERIFY_WRITE || VERIFY_ERASED_SECTOR */
#endif /* ENVIRONMENT_VARS */
   }

   /*
    * Set default values to per platform variables
    */

#ifdef ENVIRONMENT_VARS
#if (defined(VERIFY_WRITE) || defined(VERIFY_ERASED_SECTOR))
/* Max sectors verified per write operation (must be even number) */
   flSectorsVerifiedPerFolding = 64;
#endif /* VERIFY_WRITE || VERIFY_ERASED_SECTOR */
#ifdef MULTI_DOC
/* No multi-doc (MTL)                        */
   flUseMultiDoc               = FL_OFF;
/* MTL defragmentaion mode (0 - standard)    */
   flMTLdefragMode             = FL_MTL_DEFRAGMENT_ALL_DEVICES;
#endif /* MULTI_DOC */
/* Maximum chain length                      */
   flMaxUnitChain              = 20;
/* Mark the delete sector on the flash       */
   flMarkDeleteOnFlash         = FL_ON;
/* Read Only mode                            */
   flSuspendMode               = FL_OFF;
#endif /* ENVIRONMENT_VARS */
}

/*----------------------------------------------------------------------*/
/*                           m o u n t L o w L e v e l                  */
/*                                                                      */
/* Mount a volume for low level operations. If a low level routine is   */
/* called and the volume is not mounted for low level operations, this  */
/* routine is called atomatically.                                      */
/*                                                                      */
/* Parameters:                                                          */
/*      vol             : Pointer identifying drive                     */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

static FLStatus mountLowLevel(Volume vol)
{
  checkStatus(flIdentifyFlash(vol.socket,vol.flash));
  vol.flash->setPowerOnCallback(vol.flash);
  vol.flags |= VOLUME_LOW_LVL_MOUNTED;

  return flOK;
}


/*----------------------------------------------------------------------*/
/*                           d i s m o u n t L o w L e v e l            */
/*                                                                      */
/* Dismount the volume for low level operations.                        */
/*                                                                      */
/* Parameters:                                                          */
/*      vol             : Pointer identifying drive                     */
/*                                                                      */
/*----------------------------------------------------------------------*/

static void dismountLowLevel(Volume vol)
{
  /* mark the volume as unmounted for low level operations.
     And does not change any of the other flags */
  vol.flags &= ~VOLUME_LOW_LVL_MOUNTED;
}


#ifdef FORMAT_VOLUME

/*----------------------------------------------------------------------*/
/*                  f i n d F r e e V o l u m e                         */
/*                                                                      */
/* Search the vols array for an empty cell to hold the new volume  .    */
/*                                                                      */
/* Parameters:                                                          */
/*      socket        : Socket number for the new volume.               */
/*      partition     : Partition number of the new volume.             */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus      : 0 on success, flGeneralFailure if no more       */
/*                      volumes left.                                   */
/*----------------------------------------------------------------------*/

static FLStatus findFreeVolume(byte socket, byte partition)
{
   byte volNo;

   for (volNo = noOfSockets;volNo < VOLUMES;volNo++)
   {
     if ((vols[volNo].flags & VOLUME_ACCUPIED) == 0)
       break;
   }
   if (volNo == VOLUMES)
     return flGeneralFailure;

   handleConversionTable[socket][partition] = volNo;
   vols[volNo].volExecInProgress = &flMutex[socket];
   vols[volNo].socket            = vols[socket].socket;
   vols[volNo].flash             = vols[socket].flash;
   vols[volNo].tl.socketNo       = socket;
   vols[volNo].tl.partitionNo    = partition;
   vols[volNo].flags             = VOLUME_ACCUPIED;
   return flOK;
}

/*----------------------------------------------------------------------*/
/*             d i s m o u n t P h y s i c a l D r i v e                */
/*                                                                      */
/* Dismounts all the volumes on a specfic socket, closing all files.    */
/* This call is not normally necessary, unless it is known the volume   */
/* will soon be removed. The routine also clears the volumes entries in */
/* the volume convertion table, except for partition 0                  */
/*                                                                      */
/* Parameters:                                                          */
/*      socketNo        : Socket number to dismount.                    */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

static FLStatus dismountPhysicalDrive(byte socketNo)
{
  byte volNo;
  byte partition;

  /* Dismount all physical drive volumes  */

  checkStatus(dismountVolume(&vols[socketNo]));
  for(partition = 1;partition < MAX_TL_PARTITIONS; partition++)
  {
      volNo = handleConversionTable[socketNo][partition];
      if (volNo != INVALID_VOLUME_NUMBER)
      {
         checkStatus(dismountVolume(&vols[volNo]));
         handleConversionTable[socketNo][partition]=INVALID_VOLUME_NUMBER;
         vols[volNo].flags = 0;
      }
  }
  return flOK;
}
#endif /* FORMAT_VOLUME */

/*----------------------------------------------------------------------*/
/*                    d i s m o u n t V o l u m e                       */
/*                                                                      */
/* Dismounts the volume, closing all files.                             */
/* This call is not normally necessary, unless it is known the volume   */
/* will soon be removed.                                                */
/*                                                                      */
/* Parameters:                                                          */
/*      vol             : Pointer identifying drive                     */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

FLStatus dismountVolume(Volume vol)
{
  if (vol.flags & VOLUME_ABS_MOUNTED)
  {
    FLStatus status = flOK;
#ifndef FIXED_MEDIA
    status = flMediaCheck(vol.socket);
#endif
    if (status != flOK)
      vol.flags = 0;
#if FILES>0
    status = dismountFS(&vol,status);
#endif
    vol.tl.dismount(vol.tl.rec);
  }
  vol.flags = VOLUME_ACCUPIED;        /* mark volume unmounted */

  return flOK;
}

/*----------------------------------------------------------------------*/
/*                         s e t B u s y                                */
/*                                                                      */
/* Notifies the start and end of a file-system operation.               */
/*                                                                      */
/* Parameters:                                                          */
/*      vol             : Pointer identifying drive                     */
/*      state           : FL_ON (1) = operation entry                   */
/*                        FL_OFF(0) = operation exit                    */
/*      partition       : Partition number of the drive                 */
/*                                                                      */
/*----------------------------------------------------------------------*/

FLStatus setBusy(Volume vol, FLBoolean state, byte partition)
{
  FLStatus status = flOK;

  if (state == FL_ON) {

    if (!flTakeMutex(execInProgress))
       return flDriveNotAvailable;
    
    /* Mark current partition for MTD verify write */
    vol.socket->curPartition = partition; 
    flSocketSetBusy(vol.socket,FL_ON);
    flNeedVcc(vol.socket);
    if (vol.flags & VOLUME_ABS_MOUNTED)
      status = vol.tl.tlSetBusy(vol.tl.rec,FL_ON);
  }
  else {
    if (vol.flags & VOLUME_ABS_MOUNTED)
      status = vol.tl.tlSetBusy(vol.tl.rec,FL_OFF);

    flDontNeedVcc(vol.socket);
    flSocketSetBusy(vol.socket,FL_OFF);
    flFreeMutex(execInProgress);
  }

  return status;
}

/*----------------------------------------------------------------------*/
/*                        f i n d S e c t o r                           */
/*                                                                      */
/* Locates a sector in the buffer or maps it                            */
/*                                                                      */
/* Parameters:                                                          */
/*      vol             : Pointer identifying drive                     */
/*      sectorNo        : Sector no. to locate                          */
/*                                                                      */
/*----------------------------------------------------------------------*/


const void FAR0 *findSector(Volume vol, SectorNo sectorNo)
{
  return
#if FILES > 0
  (sectorNo == vol.volBuffer.sectorNo && &vol == vol.volBuffer.owner) ?
    vol.volBuffer.flData :
#endif
    vol.tl.mapSector(vol.tl.rec,sectorNo,NULL);
}

/*----------------------------------------------------------------------*/
/*                a b s M o u n t V o l u m e                           */
/*                                                                      */
/* Mounts the Flash volume and assume that volume has no FAT            */
/*                                                                      */
/* In case the inserted volume has changed, or on the first access to   */
/* the file system, it should be mounted before file operations can be  */
/* done on it.                                                          */
/* Mounting a volume has the effect of discarding all open files (the   */
/* files cannot be properly closed since the original volume is gone),  */
/* and turning off the media-change indication to allow file processing */
/* calls.                                                               */
/*                                                                      */
/* The volume automatically becomes unmounted if it is removed or       */
/* changed.                                                             */
/*                                                                      */
/* Parameters:                                                          */
/*      vol             : Pointer identifying drive                     */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

FLStatus absMountVolume(Volume vol)
{
  unsigned volNo     = (unsigned)(&vol - vols);
#ifdef WRITE_PROTECTION
  PartitionTable FAR0 *partitionTable;
#endif

  checkStatus(dismountVolume(&vol));

  /* Try to mount translation layer */

  checkStatus(flMount(volNo,vol.tl.socketNo,&vol.tl,TRUE,vol.flash));

  vol.bootSectorNo = 0; /*  assume sector 0 is DOS boot block */
#ifdef WRITE_PROTECTION
  partitionTable = (PartitionTable FAR0 *) findSector(&vol,0);
  if((partitionTable == NULL)||
     (partitionTable==dataErrorToken)||
     (LE2(partitionTable->signature) != PARTITION_SIGNATURE))
      vol.password[0] = vol.password[1] = 0;
  else
  {
     vol.password[0] = vol.password[1] = 0;
     if (UNAL4(partitionTable->passwordInfo[0]) == 0 &&
        (UNAL4(partitionTable->passwordInfo[1]) != 0 ||
        UNAL4(partitionTable->passwordInfo[2]) != 0)) {
        vol.password[0] = UNAL4(partitionTable->passwordInfo[1]);
        vol.password[1] = UNAL4(partitionTable->passwordInfo[2]);
        vol.flags |= VOLUME_WRITE_PROTECTED;
     }
  }
#endif   /* WRITE_PROTECTION */
  /* Disable FAT monitoring */
  vol.firstFATSectorNo = vol.secondFATSectorNo = 0; 
  vol.flags |= VOLUME_ABS_MOUNTED;  /* Enough to do abs operations */

  return flOK;
}


/*----------------------------------------------------------------------*/
/*                     m o u n t V o l u m e                            */
/*                                                                      */
/* Mounts the Flash volume.                                             */
/*                                                                      */
/* In case the inserted volume has changed, or on the first access to   */
/* the file system, it should be mounted before file operations can be  */
/* done on it.                                                          */
/* Mounting a volume has the effect of discarding all open files (the   */
/* files cannot be properly closed since the original volume is gone),  */
/* and turning off the media-change indication to allow file processing */
/* calls.                                                               */
/*                                                                      */
/* The volume automatically becomes unmounted if it is removed or       */
/* changed.                                                             */
/*                                                                      */
/* Parameters:                                                          */
/*      vol             : Pointer identifying drive                     */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, otherwise failed                */
/*      bootSectors     : Returns the number of sectors, flMountVolume  */
/*                        skipps.                                       */
/*----------------------------------------------------------------------*/

static FLStatus mountVolume(Volume vol,unsigned FAR2* bootSectors)
{
  SectorNo noOfSectors;
  PartitionTable FAR0 *partitionTable;
  Partition ptEntry;
  DOSBootSector FAR0 *bootSector;
  unsigned ptCount,extended_depth,ptSector;
  FLBoolean primaryPtFound = FALSE, extendedPtFound = TRUE;

  *bootSectors=0;
  checkStatus(absMountVolume(&vol));

  for(extended_depth = 0,ptSector = 0;
      (extended_depth<MAX_PARTITION_DEPTH) &&
      (primaryPtFound==FALSE) &&
      (extendedPtFound==TRUE);
      extended_depth++) {

    extendedPtFound=FALSE;
    /* Read in paritition table */

    partitionTable = (PartitionTable FAR0 *) findSector(&vol,ptSector);

    if(partitionTable == NULL) {
      vol.tl.dismount(vol.tl.rec);
      return flSectorNotFound;
    }
    if(partitionTable==dataErrorToken) {
      vol.tl.dismount(vol.tl.rec);
      return flDataError;
    }

    if (LE2(partitionTable->signature) != PARTITION_SIGNATURE)
      break;
    for(ptCount=0;
    (ptCount<4) && (primaryPtFound==FALSE) && (extendedPtFound==FALSE);
    ptCount++) {

      ptEntry = partitionTable->ptEntry[ptCount];

      switch (ptEntry.type) {
    case FAT12_PARTIT:
    case FAT16_PARTIT:
    case DOS4_PARTIT:
      primaryPtFound = TRUE;
      vol.bootSectorNo =
          (unsigned) UNAL4(ptEntry.startingSectorOfPartition);
      *bootSectors=vol.bootSectorNo;
      break;
    case EX_PARTIT:
      extendedPtFound = TRUE;
      ptSector = (unsigned)UNAL4(ptEntry.startingSectorOfPartition);
      break;
    default:
      break;
      }
    }
  }

  bootSector = (DOSBootSector FAR0 *) findSector(&vol,vol.bootSectorNo);
  if(bootSector == NULL)
    return flSectorNotFound;

  if(bootSector==dataErrorToken)
    return flDataError;

  /* Do the customary sanity checks */
  if (!(bootSector->bpb.jumpInstruction[0] == 0xe9 ||
    (bootSector->bpb.jumpInstruction[0] == 0xeb &&
     bootSector->bpb.jumpInstruction[2] == 0x90))) {
    DEBUG_PRINT(("Debug: did not recognize format.\r\n"));
    return flNonFATformat;
  }

  /* See if we handle this sector size */
  if (UNAL2(bootSector->bpb.bytesPerSector) != SECTOR_SIZE)
    return flFormatNotSupported;

  vol.sectorsPerCluster = bootSector->bpb.sectorsPerCluster;
  vol.numberOfFATS = bootSector->bpb.noOfFATS;
  vol.sectorsPerFAT = LE2(bootSector->bpb.sectorsPerFAT);
  vol.firstFATSectorNo = vol.bootSectorNo +
                LE2(bootSector->bpb.reservedSectors);
  vol.secondFATSectorNo = vol.firstFATSectorNo +
                 LE2(bootSector->bpb.sectorsPerFAT);
  vol.rootDirectorySectorNo = vol.firstFATSectorNo +
           bootSector->bpb.noOfFATS * LE2(bootSector->bpb.sectorsPerFAT);
  vol.sectorsInRootDirectory =
    (UNAL2(bootSector->bpb.rootDirectoryEntries) * DIRECTORY_ENTRY_SIZE - 1) /
        SECTOR_SIZE + 1;
  vol.firstDataSectorNo = vol.rootDirectorySectorNo +
                 vol.sectorsInRootDirectory;

  noOfSectors = UNAL2(bootSector->bpb.totalSectorsInVolumeDOS3);
  if (noOfSectors == 0)
    noOfSectors = (SectorNo) LE4(bootSector->bpb.totalSectorsInVolume);


  vol.maxCluster = (unsigned) ((noOfSectors + vol.bootSectorNo - vol.firstDataSectorNo) /
                vol.sectorsPerCluster) + 1;

  if (vol.maxCluster < 4085) {
#ifdef FAT_12BIT
    vol.flags |= VOLUME_12BIT_FAT;      /* 12-bit FAT */
#else
    DEBUG_PRINT(("Debug: FAT_12BIT must be defined.\r\n"));
    return flFormatNotSupported;
#endif
  }
  vol.bytesPerCluster = vol.sectorsPerCluster * SECTOR_SIZE;
  vol.allocationRover = 2;      /* Set rover at first cluster */
  vol.flags |= VOLUME_MOUNTED;  /* That's it */
  return flOK;
}

#ifndef FL_READ_ONLY
#ifdef DEFRAGMENT_VOLUME

/*----------------------------------------------------------------------*/
/*                       d e f r a g m e n t V o l u m e                */
/*                                                                      */
/* Performs a general defragmentation and recycling of non-writable     */
/* Flash areas, to achieve optimal write speed.                         */
/*                                                                      */
/* NOTE: The required number of sectors (in irLength) may be changed    */
/* (from another execution thread) while defragmentation is active. In  */
/* particular, the defragmentation may be cut short after it began by   */
/* modifying the irLength field to 0.                                   */
/*                                                                      */
/* Parameters:                                                          */
/*      vol             : Pointer identifying drive                     */
/*      ioreq->irLength : Minimum number of sectors to make available   */
/*                        for writes.                                   */
/*                                                                      */
/* Returns:                                                             */
/*      ioreq->irLength : Actual number of sectors available for writes */
/*      FLStatus        : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

static FLStatus defragmentVolume(Volume vol, IOreq FAR2 *ioreq)
{
  return vol.tl.defragment(vol.tl.rec,&ioreq->irLength);
}

#endif /* DEFRAGMENT_VOLUME */

#ifdef FORMAT_VOLUME

/*-----------------------------------------------------------------------*/
/*                    f l F o r m a t V o l u m e                        */
/*                                                                       */
/* Formats a volume, writing a new and empty file-system. All existing   */
/* data is destroyed. Optionally, a low-level FTL formatting is also     */
/* done.                                                                 */
/* Formatting leaves the volume in the dismounted state, so that a       */
/* flMountVolume call is necessary after it.                             */
/*                                                                       */
/* Note: This routine was left for backwards compatibility with OSAK 4.2 */
/*       and down therfore it is strongly recommended to use the         */
/*       flFormatPhysicalDrive routine instead.                          */
/*                                                                       */
/* Parameters:                                                           */
/*      vol             : Pointer identifying drive                      */
/*      irHandle        : Drive number (0, 1, ...)                       */
/*      irFlags         : FAT_ONLY_FORMAT : Do FAT formatting only       */
/*                        TL_FORMAT_ONLY  : Do TL format only            */
/*                        TL_FORMAT       : Translation layer + FAT      */
/*                        TL_FORMAT_IF_NEEDED: Do TL formatting only if  */
/*                                             current format is invalid */
/*                                             but perform FAT anyway    */
/*      irData          : Address of FormatParams structure to use       */
/*                        (defined in flformat.h)                        */
/*                                                                       */
/* Returns:                                                              */
/*      FLStatus        : 0 on success, otherwise failed                 */
/*-----------------------------------------------------------------------*/

static FLStatus bdFormatVolume(Volume vol, IOreq FAR2 *ioreq)
{
  FormatParams FAR2 *userFp = (FormatParams FAR2 *) ioreq->irData;
  BDTLPartitionFormatParams bdtlFp;
  TLFormatParams tlFp;
  FLBoolean mountOK = FALSE;
  FLStatus status;
  byte socket = FL_GET_SOCKET_FROM_HANDLE(ioreq);

  /* Convert argument to TLFormatParmas */

  tlFp.noOfBinaryPartitions = 0;
  tlFp.noOfBDTLPartitions   = 1;
  tlFp.BDTLPartitionInfo    = NULL;
  tlFp.binaryPartitionInfo  = NULL;
  tlFp.bootImageLen         = userFp->bootImageLen;
  tlFp.percentUse           = (byte)userFp->percentUse;
  tlFp.noOfSpareUnits       = (byte)userFp->noOfSpareUnits;
  tlFp.noOfCascadedDevices  = 0;
  tlFp.progressCallback     = userFp->progressCallback;
  tlFp.vmAddressingLimit    = userFp->vmAddressingLimit;
  tlFp.embeddedCISlength    = (word)userFp->embeddedCISlength;
  tlFp.embeddedCIS          = (byte FAR1 *)userFp->embeddedCIS;
  tlFp.flags                = FL_LEAVE_BINARY_AREA;
#ifdef WRITE_EXB_IMAGE
  tlFp.exbLen               = 0;
#endif /* WRITE_EXB_IMAGE */
#ifdef HW_PROTECTION
  /* protectionKey[8]; */
  tlFp.protectionType       = 0;
#endif /* HW_PROTECTION */

  /* Dismount all physical drive volumes and set handle to the first */

  checkStatus(dismountPhysicalDrive(socket));
  pVol = &vols[socket];

  /* Format according to the irFlags argument */

  if ((ioreq->irFlags & TL_FORMAT)||(ioreq->irFlags & TL_FORMAT_ONLY))
  {
     checkStatus(flFormat(socket,&tlFp,vol.flash));
  }
  else
  {
     status = flMount(socket,socket,&vol.tl,FALSE,vol.flash); /* Try to mount translation layer */
     mountOK = TRUE;
     if ((status == flUnknownMedia || status == flBadFormat) &&
     (ioreq->irFlags & TL_FORMAT_IF_NEEDED))
     {
        status = flFormat(socket,&tlFp,vol.flash);
        mountOK = FALSE;
     }
     else
     {
        /*  assume sector 0 is DOS boot block */
        vol.bootSectorNo     = 0;     
        /* Disable FAT monitoring */
        vol.firstFATSectorNo = vol.secondFATSectorNo = 0; 
        /* Enough to do abs operations */
        vol.flags |= VOLUME_ABS_MOUNTED; 
     }
     if (status != flOK)
        return status;
  }

  if (!mountOK)
    checkStatus(absMountVolume(&vol)); /* Mount the first partition */

#if (defined(VERIFY_WRITE) || defined(VERIFY_VOLUME) || defined(VERIFY_ERASED_SECTOR))
  if(vol.tl.checkVolume != NULL)
    checkStatus(vol.tl.checkVolume(vol.tl.rec));
#endif /* VERIFY_WRITE || VERIFY_VOLUME || VERIFY_ERASED_SECTOR */

  if(!(ioreq->irFlags & TL_FORMAT_ONLY))
  {
    /* build BDTL record for dos format routine */

    tffscpy(bdtlFp.volumeId,userFp->volumeId,4);
    bdtlFp.volumeLabel              = (byte FAR1 *)userFp->volumeLabel;
    bdtlFp.noOfFATcopies            = (byte)userFp->noOfFATcopies;
    bdtlFp.flags                    = TL_FORMAT_FAT;
    checkStatus(flDosFormat(&vol.tl,&bdtlFp));
  }
  return flOK;
}

/*----------------------------------------------------------------------*/
/*            f l F o r m a t P h y s i c a l D r i v e                 */
/*                                                                      */
/* Low Level formats the media while partitioning it.                   */
/* optionaly the followng additional formats are placed                 */
/* 1) writing a new and empty file-system                               */
/* 2) Compressed media format                                           */
/* 3) Quick Mount format                                                */
/*                                                                      */
/* 4) Place M-systems EXB file on the media                             */
/* All existing data is destroyed.                                      */
/* Formatting leaves the volume in the dismounted state, so that a      */
/* flMountVolume call is necessary after it.                            */
/*                                                                      */
/* Parameters:                                                          */
/*      vol      : Pointer identifying drive                            */
/*      irHandle : Drive number (0, 1, ...)                             */
/*      irFlags  :                                                      */
/*       TL_NORMAL_FORMAT          : Normal format                      */
/*       TL_LEAVE_BINARY_AREA      : Leave binary area unchanged        */
/*                                                                      */
/*      irData   : Address of FormatParams2 structure to use            */
/*                            (defined in flformat.h)                   */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

static FLStatus bdFormatPhysicalDrive(Volume vol, IOreq FAR2 *ioreq)
{
  FormatParams2  FAR2* userFp = (FormatParams2 FAR2 *) ioreq->irData;
  BDTLPartitionFormatParams FAR2* bdtl;
  TLFormatParams                  tlFp;
  byte     socket = FL_GET_SOCKET_FROM_HANDLE(ioreq);
  byte     partition;
  byte     volNo;
#ifdef COMPRESSION
  FLStatus status;
#endif /* COMPRESSION */

  /***********************************************/
  /* Convert format parameters to TLFormatParams */
  /***********************************************/

  if (userFp->BDTLPartitionInfo == NULL)
     return flBadParameter;

/* Note that the the BDTL partition are shiftet so that the second
   becomes the first ... . This is for backwards compatibility with
   FTP \ NFTL where the first partition is not given as an array    */

  tlFp.bootImageLen         = -1; /* either all or nothing */
  tlFp.percentUse           = userFp->percentUse;
  tlFp.noOfBDTLPartitions   = userFp->noOfBDTLPartitions;
  tlFp.noOfBinaryPartitions = userFp->noOfBinaryPartitions;
  tlFp.BDTLPartitionInfo    = userFp->BDTLPartitionInfo;
  tlFp.binaryPartitionInfo  = userFp->binaryPartitionInfo;
  tlFp.progressCallback     = userFp->progressCallback;
  tlFp.vmAddressingLimit    = userFp->vmAddressingLimit;
  tlFp.embeddedCISlength    = userFp->embeddedCISlength;
  tlFp.embeddedCIS          = userFp->embeddedCIS;
  tlFp.cascadedDeviceNo     = userFp->cascadedDeviceNo;
  tlFp.noOfCascadedDevices  = userFp->noOfCascadedDevices;
  tlFp.flags                = (byte)ioreq->irFlags;

/* Convert last partition arguments from array to dedicated fields */

  bdtl                      = userFp->BDTLPartitionInfo;
  bdtl                     += (userFp->noOfBDTLPartitions - 1);
  tffscpy(tlFp.volumeId,bdtl->volumeId,4);
  tlFp.noOfSpareUnits       = (byte)bdtl->noOfSpareUnits;
  tlFp.volumeLabel          = bdtl->volumeLabel;
  tlFp.noOfFATcopies        = bdtl->noOfFATcopies;
#ifdef HW_PROTECTION
  tffscpy(tlFp.protectionKey,bdtl->protectionKey,PROTECTION_KEY_LENGTH);
  tlFp.protectionType   = bdtl->protectionType;
#endif /* HW_PROTECTION */

  /* Dismount all physical drive volumes  */

  checkStatus(dismountPhysicalDrive(socket));

  /**********************/
  /* Analize EXB buffer */
  /**********************/

  pVol = &vols[socket];
#ifdef WRITE_EXB_IMAGE
  if ((ioreq->irFlags & TL_LEAVE_BINARY_AREA) ||
      ((userFp->exbBufferLen <= 0) && (userFp->exbLen == 0)))
  {
     tlFp.exbLen = 0;
  }
  else
  {
     if (userFp->exbLen <= 0)
     {
        checkStatus(getExbInfo(&vol, userFp->exbBuffer, 
                               userFp->exbBufferLen,
                               userFp->exbFlags));
        tlFp.exbLen = vol.binaryLength;
     }
     else
     {
        tlFp.exbLen = userFp->exbLen;
     }
  }
#endif /* WRITE_EXB_IMAGE */

  /*************************************************/
  /* Perform low level format and write EXB buffer */
  /*************************************************/

  checkStatus(flFormat(socket,&tlFp,vol.flash));

  /****************************************/
  /* perform FAT and ZIP format if needed */
  /****************************************/

  for(partition=0, bdtl = userFp->BDTLPartitionInfo;
      partition<userFp->noOfBDTLPartitions;
      partition++,bdtl++)
  {
     if ( partition > 0)
        checkStatus(findFreeVolume(socket,partition));
     volNo = handleConversionTable[socket][partition];
     pVol = &vols[volNo];
#ifdef COMPRESSION
     if(bdtl->flags & TL_FORMAT_COMPRESSED)
     {
        checkStatus(flMount(volNo,socket,&(vol.tl),FALSE,vol.flash));
        status = flFormatZIP(volNo,&vol.tl);
        vol.tl.dismount(vol.tl.rec);
        if(status!=flOK)
           return status;
     }
#endif /* COMPRESSION */

     checkStatus(absMountVolume(&vol));
#if (defined(VERIFY_WRITE) || defined(VERIFY_VOLUME) || defined(VERIFY_ERASED_SECTOR))
     if(vol.tl.checkVolume != NULL)
        checkStatus(vol.tl.checkVolume(vol.tl.rec));
#endif /* VERIFY_WRITE || VERIFY_VOLUME || VERIFY_ERASED_SECTOR */
     if(bdtl->flags & TL_FORMAT_FAT) /* perform FAT format as well */
        checkStatus(flDosFormat(&vol.tl,bdtl));
     checkStatus(dismountVolume(&vol));
  }

#ifdef WRITE_EXB_IMAGE
  if (userFp->exbBufferLen > 0 )
  {
     pVol = &vols[socket];
     checkStatus(placeExbByBuffer(&vol,(byte FAR1 *)userFp->exbBuffer,
     userFp->exbBufferLen,userFp->exbWindow,userFp->exbFlags));
  }
#endif /* WRITE_EXB_IMAGE */

  checkStatus(mountLowLevel(&vol));
  if (vol.flash->download!=NULL)
  {
     return vol.flash->download(vol.flash); /* download IPL */
  }
  return flOK;
}

/*-----------------------------------------------------------------------*/
/*            f l F o r m a t L o g i c a l D r i v e                    */
/*                                                                       */
/* Formats a logical drive, optionaly writing a new and empty            */
/* file-system and or a compressed format. All existing                  */
/* data is destroyed.                                                    */
/* Formatting leaves the volume in the dismounted state, so that a       */
/* flMountVolume call is necessary after it.                             */
/*                                                                       */
/* Parameters:                                                           */
/*      vol             : Pointer identifying drive                      */
/*      irHandle        : Drive number (0, 1, ...)                       */
/*      irData          : Address of BDTLPartitionFormatParams to use    */
/*                        (defined in flformat.h)                        */
/*                                                                       */
/* Returns:                                                              */
/*      FLStatus        : 0 on success, otherwise failed                 */
/*-----------------------------------------------------------------------*/

static FLStatus bdFormatLogicalDrive(Volume vol, IOreq FAR2 *ioreq)
{
   BDTLPartitionFormatParams FAR2 *userFp =
        (BDTLPartitionFormatParams FAR2 *) ioreq->irData;
   byte       volNo = (byte)(&vol-vols);
#ifdef COMPRESSION
   FLStatus   status;
#endif /* COMPRESSION */

   checkStatus(flMount(volNo,vol.tl.socketNo,&(vol.tl),FALSE,vol.flash));
   /*  assume sector 0 is DOS boot block */
   vol.bootSectorNo     = 0;
   /* Disable FAT monitoring */
   vol.firstFATSectorNo = vol.secondFATSectorNo = 0; 
   /* Enough to do abs operations */
   vol.flags |= VOLUME_ABS_MOUNTED;  

#ifdef COMPRESSION
   if(userFp->flags & TL_FORMAT_COMPRESSED)
   {
      status = flFormatZIP(volNo,&vol.tl,vol.flash);
      vol.tl.dismount(vol.tl.rec);
      if(status!=flOK)
        return status;
   }
#endif /* COMPRESSION */

   if(userFp->flags & TL_FORMAT_FAT) /* perform FAT format as well */
   {
      checkStatus(absMountVolume(&vol));
      checkStatus(flDosFormat(&vol.tl,userFp));
      checkStatus(dismountVolume(&vol));
   }
   return flOK;
}
#endif /* FORMAT_VOLUME */

#endif  /* FL_READ_ONLY */

/*----------------------------------------------------------------------*/
/*                  s e c t o r s I n V o l u m e                       */
/*                                                                      */
/* Defines actual number of virtual sectors according to the low-level  */
/* format of the media.                                                 */
/*                                                                      */
/* Returns:                                                             */
/*      vol             : Pointer identifying drive                     */
/*      ioreq->irLength : Actual number of virtual sectors in volume    */
/*      FLStatus        : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/
static FLStatus sectorsInVolume(Volume vol, IOreq FAR2 *ioreq)
{
  dword sectorsInVol = vol.tl.sectorsInVolume(vol.tl.rec);
  if(sectorsInVol<=vol.bootSectorNo) {
    ioreq->irLength = 0;
    return flGeneralFailure;
  }

  ioreq->irLength = sectorsInVol-vol.bootSectorNo;
  return flOK;
}


#ifdef ABS_READ_WRITE

/*----------------------------------------------------------------------*/
/*                           a b s R e a d                              */
/*                                                                      */
/* Reads absolute sectors by sector no.                                 */
/*                                                                      */
/* Note that if readSecots is not implemented irSectoCount will not     */
/* return the actual number of sectors written in case the operation    */
/* failed in the middle.                                                */
/*                                                                      */
/* Parameters:                                                          */
/*      vol             : Pointer identifying drive                     */
/*      irHandle        : Drive number (0, 1, ...)                      */
/*      irData          : Address of user buffer to read into           */
/*      irSectorNo      : First sector no. to read (sector 0 is the     */
/*                        DOS boot sector).                             */
/*      irSectorCount   : Number of consectutive sectors to read        */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, otherwise failed                */
/*      irSectorCount   : Number of sectors actually read               */
/*----------------------------------------------------------------------*/

static FLStatus absRead(Volume vol, IOreq FAR2 *ioreq)
{
  char FAR1 *userBuffer = (char FAR1 *) ioreq->irData;
  SectorNo  currSector   = vol.bootSectorNo + ioreq->irSectorNo;
  void FAR0 *mappedSector;
  FLStatus  status;

  if (vol.tl.readSectors != NULL)
  {
     status = vol.tl.readSectors(vol.tl.rec , currSector,
              (byte FAR1* )ioreq->irData , ioreq->irSectorCount);

     if (status == flSectorNotFound)
     {
        /* Do not report unassigned sectors. Simply report all 0's */
        if(vol.tl.sectorsInVolume(vol.tl.rec) >=
           (currSector+ioreq->irSectorCount))
           return flOK;
     }
     return status;
  }
  else
  {
     SectorNo sectorCount  = (SectorNo)ioreq->irSectorCount;
     for (ioreq->irSectorCount = 0;
         (SectorNo)(ioreq->irSectorCount) < sectorCount;
          ioreq->irSectorCount++, currSector++, userBuffer += SECTOR_SIZE)
     {
#ifdef SCATTER_GATHER
        userBuffer = *((char FAR1 **)(ioreq->irData) + 
                    (int)(ioreq->irSectorCount));
#endif
        mappedSector = (void FAR0 *)findSector(&vol,currSector);
        if (mappedSector)
        {
           if(mappedSector==dataErrorToken)
             return flDataError;

           tffscpy(userBuffer,mappedSector,SECTOR_SIZE);
        }
        else
        {
           if(vol.tl.sectorsInVolume(vol.tl.rec)<=(currSector))
              return flSectorNotFound;
           tffsset(userBuffer,0,SECTOR_SIZE);
        }
     }
  }
  return flOK;
}


#ifndef FL_READ_ONLY
/*----------------------------------------------------------------------*/
/*                    r e p l a c e F A T s e c t o r                   */
/*                                                                      */
/* Monitors sector deletions in the FAT.                                */
/*                                                                      */
/* When a FAT block is about to be written by an absolute write, this   */
/* routine will first scan whether any sectors are being logically      */
/* deleted by this FAT update, and if so, it will delete-sector them    */
/* before the actual FAT update takes place.                            */
/*                                                                      */
/* Parameters:                                                          */
/*      vol             : Pointer identifying drive                     */
/*      sectorNo        : FAT Sector no. about to be written            */
/*      newFATsector    : Address of FAT sector about to be written     */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

FLStatus replaceFATsector(Volume vol,
            SectorNo sectorNo,
            const char FAR1 *newFATsector)
{
  const char FAR0 *oldFATsector = (const char FAR0 *)findSector(&vol,sectorNo);
  SectorNo firstSector;
  unsigned firstCluster;
#ifdef FAT_12BIT
  word FAThalfBytes;
  word halfByteOffset;
#else
  word byteOffset;
#endif

  if((oldFATsector==NULL) || oldFATsector==dataErrorToken)
    return flOK;

#ifdef FAT_12BIT
  FAThalfBytes = vol.flags & VOLUME_12BIT_FAT ? 3 : 4;

  firstCluster = (FAThalfBytes == 3) ?
    (((unsigned) (sectorNo - vol.firstFATSectorNo) * (2 * SECTOR_SIZE) + 2) / 3) :
    ((unsigned) (sectorNo - vol.firstFATSectorNo) * (SECTOR_SIZE>>1));
  firstSector    = ((SectorNo) firstCluster - 2) * 
                   vol.sectorsPerCluster + vol.firstDataSectorNo;
  halfByteOffset = (firstCluster * FAThalfBytes) & ((SECTOR_SIZE<<1) - 1);

  /* Find if any clusters were logically deleted, and if so, delete them */
  /* NOTE: We are skipping over 12-bit FAT entries which span more than  */
  /*       one sector. Nobody's perfect anyway.                          */
  for (; halfByteOffset < ((SECTOR_SIZE<<1) - 2);
       firstSector += vol.sectorsPerCluster, 
       halfByteOffset += FAThalfBytes)
  {
    unsigned short oldFATentry, newFATentry;

#ifdef FL_BIG_ENDIAN
    oldFATentry = LE2(*(LEushort FAR0 *)(oldFATsector + (halfByteOffset>>1)));
    newFATentry = LE2(*(LEushort FAR1 *)(newFATsector + (halfByteOffset>>1)));
#else
    oldFATentry = UNAL2(*(Unaligned FAR0 *)(oldFATsector + (halfByteOffset / 2)));
    newFATentry = UNAL2(*(Unaligned FAR1 *)(newFATsector + (halfByteOffset / 2)));
#endif
    if (halfByteOffset & 1) {
      oldFATentry >>= 4;
      newFATentry >>= 4;
    }
    else if (FAThalfBytes == 3) {
      oldFATentry &= 0xfff;
      newFATentry &= 0xfff;
    }
#else
  firstCluster = ((unsigned) (sectorNo - vol.firstFATSectorNo) << (SECTOR_SIZE_BITS-1));
  firstSector = ((SectorNo) firstCluster - 2) * vol.sectorsPerCluster + 
	            vol.firstDataSectorNo;

  /* Find if any clusters were logically deleted, and if so, delete them */
  for (byteOffset = 0; byteOffset < SECTOR_SIZE;
       firstSector += vol.sectorsPerCluster, byteOffset += 2) {
    unsigned short oldFATentry = LE2(*(LEushort FAR0 *)(oldFATsector + byteOffset));
    unsigned short newFATentry = LE2(*(LEushort FAR1 *)(newFATsector + byteOffset));
#endif

    if (oldFATentry != FAT_FREE && newFATentry == FAT_FREE)
      checkStatus(vol.tl.deleteSector(vol.tl.rec,firstSector,vol.sectorsPerCluster));

    /* make sure sector is still there */
    oldFATsector = (const char FAR0 *) findSector(&vol,sectorNo);
  }

  return flOK;
}


/*----------------------------------------------------------------------*/
/*                         a b s W r i t e                              */
/*                                                                      */
/* Writes absolute sectors by sector no.                                */
/*                                                                      */
/* Parameters:                                                          */
/*      vol             : Pointer identifying drive                     */
/*      irHandle        : Drive number (0, 1, ...)                      */
/*      irData          : Address of user buffer to write from          */
/*      irSectorNo      : First sector no. to write (sector 0 is the    */
/*                        DOS boot sector).                             */
/*      irSectorCount   : Number of consectutive sectors to write       */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, otherwise failed                */
/*      irSectorCount   : Number of sectors actually written            */
/*----------------------------------------------------------------------*/

static FLStatus absWrite(Volume vol, IOreq FAR2 *ioreq)
{
  char FAR1 *userBuffer = (char FAR1 *) ioreq->irData;
  SectorNo currSector = vol.bootSectorNo + ioreq->irSectorNo;
  SectorNo sectorCount = (SectorNo)ioreq->irSectorCount;

  if (currSector < vol.secondFATSectorNo &&
      currSector + sectorCount > vol.firstFATSectorNo) {
    SectorNo iSector;

    for (iSector = 0; iSector < sectorCount;
    iSector++, currSector++, userBuffer += SECTOR_SIZE) {

      if (currSector >= vol.firstFATSectorNo &&
      currSector < vol.secondFATSectorNo)
    replaceFATsector(&vol,currSector,userBuffer);
    }

    userBuffer = (char FAR1 *) ioreq->irData;
    currSector = (SectorNo)vol.bootSectorNo + (SectorNo)ioreq->irSectorNo;
  }
  if (vol.tl.writeMultiSector != NULL)
  {
      checkStatus(vol.tl.writeMultiSector(vol.tl.rec, currSector,
                  ioreq->irData,ioreq->irSectorCount));
  }
  else
  {
     for (ioreq->irSectorCount = 0;
          (SectorNo)(ioreq->irSectorCount) < sectorCount;
          ioreq->irSectorCount++, currSector++, userBuffer += SECTOR_SIZE)
     {
#if FILES>0
        if ((currSector == vol.volBuffer.sectorNo) &&
            (&vol == vol.volBuffer.owner))
        {
           vol.volBuffer.sectorNo = UNASSIGNED_SECTOR; /* no longer valid */
           vol.volBuffer.dirty = vol.volBuffer.checkPoint = FALSE;
        }
#endif

#ifdef SCATTER_GATHER
        userBuffer = *((char FAR1 **)(ioreq->irData)+(int)(ioreq->irSectorCount));
#endif
        checkStatus(vol.tl.writeSector(vol.tl.rec,currSector,userBuffer));
     }
  }
  return flOK;
}

/*----------------------------------------------------------------------*/
/*                        a b s D e l e t e                             */
/*                                                                      */
/* Marks absolute sectors by sector no. as deleted.                     */
/*                                                                      */
/* Parameters:                                                          */
/*      vol             : Pointer identifying drive                     */
/*      irHandle        : Drive number (0, 1, ...)                      */
/*      irSectorNo      : First sector no. to write (sector 0 is the    */
/*                        DOS boot sector).                             */
/*      irSectorCount   : Number of consectutive sectors to delete      */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

static FLStatus absDelete(Volume vol, IOreq FAR2 *ioreq)
{
  SectorNo first;
  first = (SectorNo)(vol.bootSectorNo + ioreq->irSectorNo);
  return vol.tl.deleteSector(vol.tl.rec,first,(SectorNo)ioreq->irSectorCount);
}

#endif /* FL_READ_ONLY */

#ifndef NO_PHYSICAL_IO
/*----------------------------------------------------------------------*/
/*                       f l A b s A d d r e s s                        */
/*                                                                      */
/* Returns the current physical media offset of an absolute sector by   */
/* sector no.                                                           */
/*                                                                      */
/* Parameters:                                                          */
/*      vol             : Pointer identifying drive                     */
/*      irHandle        : Drive number (0, 1, ...)                      */
/*      irSectorNo      : Sector no. to address (sector 0 is the DOS    */
/*                        boot sector)                                  */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, otherwise failed                */
/*      irCount         : Offset of the sector on the physical media    */
/*----------------------------------------------------------------------*/

static FLStatus absAddress(Volume vol, IOreq FAR2 *ioreq)
{
  CardAddress cardOffset;
  const void FAR0 * sectorData =
    vol.tl.mapSector(vol.tl.rec,vol.bootSectorNo + ioreq->irSectorNo,&cardOffset);

  if (sectorData) {
    if(sectorData==dataErrorToken)
    return flDataError;

    ioreq->irCount = cardOffset;
    return flOK;
  }
  else
    return flSectorNotFound;
}

#endif /* NO_PHYSICAL_IO */

/*----------------------------------------------------------------------*/
/*                           g e t B P B                                */
/*                                                                      */
/* Reads the BIOS Parameter Block from the boot sector                  */
/*                                                                      */
/* Parameters:                                                          */
/*      vol             : Pointer identifying drive                     */
/*      irHandle        : Drive number (0, 1, ...)                      */
/*      irData          : Address of user buffer to read BPB into       */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

static FLStatus getBPB(Volume vol, IOreq FAR2 *ioreq)
{
  BPB FAR1 *userBPB = (BPB FAR1 *) ioreq->irData;
  DOSBootSector FAR0 *bootSector;

  bootSector = (DOSBootSector FAR0 *) findSector(&vol,vol.bootSectorNo);
  if(bootSector == NULL)
    return flSectorNotFound;
  if(bootSector==dataErrorToken)
    return flDataError;

  *userBPB = bootSector->bpb;
  return flOK;
}

#ifndef FL_READ_ONLY
#ifdef WRITE_PROTECTION
/*----------------------------------------------------------------------*/
/*              c h a n g e P a s s w o r d                             */
/*                                                                      */
/* Change password for write protectipon.                               */
/*                                                                      */
/* Parameters:                                                          */
/*  vol         : Pointer identifying drive                             */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

static FLStatus changePassword(Volume vol)
  {
  PartitionTable partitionTable;
  IOreq ioreq;
  FLStatus status;
#ifdef SCATTER_GATHER
  void FAR1 *iovec[1];
#endif

  ioreq.irHandle=(unsigned)(&vol-vols);
  ioreq.irSectorNo=0-(int)vol.bootSectorNo;
  ioreq.irSectorCount=1;
#ifdef SCATTER_GATHER
  iovec[0]     = (void FAR1 *) &partitionTable;
  ioreq.irData = (void FAR1 *) iovec;
#else
  ioreq.irData=&partitionTable;
#endif
    if((status=absRead(&vol,&ioreq))!=flOK)
     return status;
  toUNAL4(partitionTable.passwordInfo[0], 0);
  toUNAL4(partitionTable.passwordInfo[1],vol.password[0]);
  toUNAL4(partitionTable.passwordInfo[2],vol.password[1]);

  vol.flags &= ~VOLUME_WRITE_PROTECTED;

  return absWrite(&vol,&ioreq);
}

/*----------------------------------------------------------------------*/
/*              w r i t e P r o t e c t                                 */
/*                                                                      */
/* Put and remove write protection from the volume                      */
/*                                                                      */
/* Parameters:                                                          */
/*  vol      : Pointer identifying drive                                */
/*  irHandle : Drive number ( 0,1,2...  )                               */
/*  irFlags  : FL_PROTECT   = place protection                          */
/*             FL_UNPROTECT = remove protection                         */
/*             FL_UNLOCK    = remove protection until next dismount     */
/*  irData   : password (8 bytes)                                       */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

static FLStatus writeProtect(Volume vol,IOreq FAR2*ioreq)
{
  FLStatus status=flWriteProtect;
  dword *flData=(dword *)ioreq->irData;
  dword passCode1 = flData[0] ^ SCRAMBLE_KEY_1;
  dword passCode2 = flData[1] ^ SCRAMBLE_KEY_2;

    switch (ioreq->irFlags) {

      case FL_UNLOCK:     /* unlock volume */
     if((vol.password[0] == passCode1 && vol.password[1] == passCode2)||
        (vol.password[0] == 0 && vol.password[1] == 0))
     {
         vol.flags &= ~VOLUME_WRITE_PROTECTED;
         status=flOK;
     }
     else
        status=flWriteProtect;
     break;

      case FL_UNPROTECT:     /* remove password */
     if(vol.password[0] == passCode1 && vol.password[1] == passCode2)
     {
        vol.password[0] = vol.password[1] = 0;
        status = changePassword(&vol);
     }
     else
        status=flWriteProtect;
     break;

      case FL_PROTECT: /* set password */
    if(vol.password[0] == 0 && vol.password[1] == 0)
       {
       vol.password[0] = passCode1;
       vol.password[1] = passCode2;
       status = changePassword(&vol);
       vol.flags|=VOLUME_WRITE_PROTECTED;
       }
    else
       status=flWriteProtect;
          break;

       default:
    status = flGeneralFailure;
      }
return status;
}
#endif  /* WRITE_PROTECTION */

#endif /* FL_READ_ONLY   */

#endif /* ABS_READ_WRITE */

/*----------------------------------------------------------------------*/
/*                   s o c k e t I n f o                */
/*                                                                      */
/* Get socket Information (window base address)          */
/*                                                                      */
/* Parameters:                                                          */
/*  vol         : Pointer identifying drive         */
/*  baseAddress : pointer to receive window base address   */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

static FLStatus socketInfo(Volume vol, dword FAR2 *baseAddress)
{
  *baseAddress = (long)(vol.socket->window.baseAddress) << 12;
  return flOK;
}

#ifdef FL_LOW_LEVEL

/*----------------------------------------------------------------------*/
/*                           g e t P h y s i c a l I n f o              */
/*                                                                      */
/* Get physical information of the media. The information includes      */
/* JEDEC ID, unit size and media size.                                  */
/*                                                                      */
/* Parameters:                                                          */
/*      vol             : Pointer identifying drive                     */
/*      irData          : Address of user buffer to read physical       */
/*                        information into.                             */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, otherwise failed                */
/*      irLength        : Physical base address of device               */
/*----------------------------------------------------------------------*/

static FLStatus getPhysicalInfo(Volume vol, IOreq FAR2 *ioreq)
{
  PhysicalInfo FAR2 *physicalInfo = (PhysicalInfo FAR2 *)ioreq->irData;

  physicalInfo->type = vol.flash->type;
  physicalInfo->unitSize = vol.flash->erasableBlockSize;
  physicalInfo->mediaSize = vol.flash->chipSize * vol.flash->noOfChips;
  physicalInfo->chipSize = vol.flash->chipSize;
  physicalInfo->interleaving = vol.flash->interleaving;
  switch(vol.flash->mediaType) {
    case NOT_DOC_TYPE:
    physicalInfo->mediaType = FL_NOT_DOC;
    break;
    case DOC_TYPE :
    physicalInfo->mediaType = FL_DOC;
    break;
    case MDOC_TYPE :
    physicalInfo->mediaType = FL_MDOC;
    break;
    case MDOCP_TYPE :
    physicalInfo->mediaType = FL_MDOCP;
    break;
    case DOC2000TSOP_TYPE :
    physicalInfo->mediaType = FL_DOC2000TSOP;
    break;
    case MDOCP_16_TYPE :
    physicalInfo->mediaType = FL_MDOCP_16;
    break;
  }
  socketInfo(&vol,(dword FAR2 *) &(ioreq->irLength));
  return flOK;
}

#ifndef NO_PHYSICAL_IO

/*----------------------------------------------------------------------*/
/*                           p h y s i c a l R e a d                    */
/*                                                                      */
/* Read from a physical address.                                        */
/*                                                                      */
/* Parameters:                                                          */
/*      vol             : Pointer identifying drive                     */
/*      irAddress       : Physical address to read from.                */
/*      irByteCount     : Number of bytes to read.                      */
/*      irData          : Address of user buffer to read into.          */
/*      irFlags         : Mode of the operation.                        */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

static FLStatus physicalRead(Volume vol, IOreq FAR2 *ioreq)
{
  /* check that we are reading whithin the media boundaries */
  if (ioreq->irAddress + (long)ioreq->irByteCount > (long)vol.flash->chipSize *
                vol.flash->noOfChips)
    return flBadParameter;

  /* We don't read accross a unit boundary */
  if ((long)ioreq->irByteCount > (long)(vol.flash->erasableBlockSize -
           (ioreq->irAddress % vol.flash->erasableBlockSize)))
    return flBadParameter;

  checkStatus(vol.flash->read(vol.flash, ioreq->irAddress, ioreq->irData,
     (word)ioreq->irByteCount, (word)ioreq->irFlags));
  return flOK;
}

#ifndef FL_READ_ONLY

/*----------------------------------------------------------------------*/
/*                           p h y s i c a l W r i t e                  */
/*                                                                      */
/* Write to a physical address.                                         */
/*                                                                      */
/* Parameters:                                                          */
/*      vol             : Pointer identifying drive                     */
/*      irAddress       : Physical address to write to.                 */
/*      irByteCount     : Number of bytes to write.                     */
/*      irData          : Address of user buffer to write from.         */
/*      irFlags         : Mode of the operation.                        */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

static FLStatus physicalWrite(Volume vol, IOreq FAR2 *ioreq)
{
  /* check that we are writing whithin the media boundaries */
  if (ioreq->irAddress + (long)ioreq->irByteCount > (long)(vol.flash->chipSize *
      vol.flash->noOfChips))
    return flBadParameter;

  /* We don't write accross a unit boundary */
  if (ioreq->irByteCount > (long)(vol.flash->erasableBlockSize -
      (ioreq->irAddress % vol.flash->erasableBlockSize)))
    return flBadParameter;

  checkStatus(vol.flash->write(vol.flash, ioreq->irAddress, ioreq->irData,
      (dword)ioreq->irByteCount, (word)ioreq->irFlags));
  return flOK;
}

/*----------------------------------------------------------------------*/
/*                           p h y s i c a l E r a s e                  */
/*                                                                      */
/* Erase physical units.                                                */
/*                                                                      */
/* Parameters:                                                          */
/*      vol             : Pointer identifying drive                     */
/*      irUnitNo        : First unit to erase.                          */
/*      irUnitCount     : Number of units to erase.                     */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

static FLStatus physicalErase(Volume vol, IOreq FAR2 *ioreq)
{
  if (ioreq->irUnitNo + (long)ioreq->irUnitCount > (long)
      (vol.flash->chipSize * vol.flash->noOfChips / vol.flash->erasableBlockSize))
    return flBadParameter;

  checkStatus(vol.flash->erase(vol.flash, (word)ioreq->irUnitNo, (word)ioreq->irUnitCount));
  return flOK;
}
#endif /* NO_PHYSICAL_IO */
#endif /* FL_READ_ONLY */


#ifndef NO_IPL_CODE
/*----------------------------------------------------------------------*/
/*                           r e a d I P L                              */
/*                                                                      */
/* Read IPL to user buffer.                                             */
/*                                                                      */
/* Note : Read length must be a multiplication of 512 bytes             */
/* Note : Causes DiskOnChip Millennium Plus to download (i,e protection */
/*        key will be removed from all partitions.                      */
/*                                                                      */
/* Parameters:                                                          */
/*  flash            : Pointer identifying flash medium of the volume   */
/*  irHandle         : Socket number ( 0,1,2...  )                      */
/*  buf              : User buffer to read into                         */
/*  bufLen           : Size of user buffer                              */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

static FLStatus readIPL(FLFlash* flash, byte FAR1 * buf , dword bufLen)
{
   FLStatus status;
   dword    secondCopyOffset = 1024;

   if(bufLen & 512)
   {
      DEBUG_PRINT(("ERROR - Must read a multiplication of 512 bytes.\r\n"));
      return flBadLength;
   }

   switch (flash->mediaType)
   {
      case MDOCP_TYPE:     /* have a special place for the IPL */
      case MDOCP_16_TYPE:  
         if (flash->readIPL != NULL)
         {
            checkStatus(flash->readIPL(flash,buf,(word)bufLen));
            return flOK;
         }
         break;
      case MDOC_TYPE:
         if(bufLen != 512)
         {
            DEBUG_PRINT(("ERROR - DiskOnChip Millennium 8M has only 512 Bytes of XIP.\r\n"));
            return flBadLength;
         }
         secondCopyOffset = 512;

      case DOC2000TSOP_TYPE: /* Have a special place for the IPL */
         if(bufLen > 1024)
         {
            DEBUG_PRINT(("ERROR - DiskOnChip 2000 TSOP has only 1024 Bytes of XIP.\r\n"));
            return flBadLength;
         }
         if(flash->read != NULL)
         {
            status = flash->read(flash,0,buf,bufLen,EDC);
            if(status != flOK)
               return flash->read(flash,secondCopyOffset,buf,bufLen,EDC);
            return status;
         }
         break;
      default :
         if(bufLen != 512)
         {
            DEBUG_PRINT(("ERROR - DiskOnChip 2000 has only 512 Bytes of XIP.\r\n"));
            return flBadLength;
         }
         if(flash->win != NULL)
         {
            tffscpy(buf,(const void FAR1*)flash->win,bufLen);
         }
         break;
   }
   return flGeneralFailure;
}

#ifndef FL_READ_ONLY

/*----------------------------------------------------------------------*/
/*                           w r i t e I P L                            */
/*                                                                      */
/* Write IPL to the proper media location                               */
/*                                                                      */
/* Parameters:                                                          */
/*  flash            : Pointer identifying flash medium of the volume   */
/*  irHandle         : Socket number ( 0,1,2...  )                      */
/*  buf              : User buffer containin data to write to the IPL   */
/*  bufLen           : Size of user buffer                              */
/*  flags            : FL_IPL_MODE_NORMAL : None Strong Arm mode        */
/*                     FL_IPL_DOWNLOAD    : Download new IPL when done  */
/*                     FL_IPL_MODE_SA     : Strong Arm mode             */
/*                     FL_IPL_MODE_XSCALE : X-Scale mode                */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

static FLStatus writeIPL(FLFlash* flash, byte FAR1 * buf , dword bufLen , unsigned flags)
{
   switch (flash->mediaType)
   {
      case DOC2000TSOP_TYPE: /* Have a special place for the IPL */
      case MDOCP_TYPE      :
      case MDOCP_16_TYPE   :
         if (flash->writeIPL != NULL)
            return (flash->writeIPL(flash,buf,(word)bufLen,0,flags));
         DFORMAT_PRINT(("ERROR - MTD does not support write IPL (Please reconfigure MTD).\r\n"));
         break;
      case MDOC_TYPE:
         DFORMAT_PRINT(("ERROR - DiskOnChip Millennium does not support write IPL, use the /BDFK flag.\r\n"));
         break;
      default :
         DFORMAT_PRINT(("ERROR - DiskOnChip 2000 does not support a writiable IPL.\r\n"));
         break;
   }
   return flFeatureNotSupported;
}
#endif /* FL_READ_ONLY */
#endif /* NO_IPL_CODE */

#ifndef NO_INQUIRE_CAPABILITIES

/*----------------------------------------------------------------------*/
/*                  i n q u i r e C a p a b i l i t i e s               */
/*                                                                      */
/* Get the specific device S/W and H/W capabilities                     */
/*                                                                      */
/* Parameters:                                                          */
/*      flash           : Record discribing the media                   */
/*                        4 LSB - Socket number                         */
/*      capability      : Enumarator representing the capability        */
/* Returns:                                                             */
/*      capability      : CAPABILITY_SUPPORTED if the capability is     */
/*                        supported                                     */
/*                                                                      */
/*----------------------------------------------------------------------*/

static void inquireCapabilities(FLFlash* flash , FLCapability FAR2* capability)
{
   FLCapability inquiredCapability = *capability;

   *capability = CAPABILITY_NOT_SUPPORTED;

   switch (inquiredCapability)
   {
#ifdef HW_OTP
      case SUPPORT_UNERASABLE_BBT:
      case SUPPORT_OTP_AREA:
        if(flash->otpSize != NULL)
           *capability = CAPABILITY_SUPPORTED;
        break;
      case SUPPORT_CUSTOMER_ID:
      case SUPPORT_UNIQUE_ID:
        if(flash->getUniqueId != NULL)
           *capability = CAPABILITY_SUPPORTED;
        break;
#endif /* HW_OTP */
      case SUPPORT_MULTIPLE_BDTL_PARTITIONS:
      case SUPPORT_MULTIPLE_BINARY_PARTITIONS:
        if(flash->flags & INFTL_ENABLED)
           *capability = CAPABILITY_SUPPORTED;
        break;

#ifdef HW_PROTECTION
      case SUPPORT_HW_PROTECTION:
      case SUPPORT_HW_LOCK_KEY:
        if(flash->protectionKeyInsert != NULL)
           *capability = CAPABILITY_SUPPORTED;
        break;
#endif /* HW_PROTECTION */
      case SUPPORT_DEEP_POWER_DOWN_MODE:
        if(flash->enterDeepPowerDownMode != NULL)
           *capability = CAPABILITY_SUPPORTED;
    break;
      case SUPPORT_WRITE_IPL_ROUTINE:
    if((flash->mediaType == DOC2000TSOP_TYPE) ||
       (flash->mediaType == MDOC_TYPE))
       *capability = CAPABILITY_SUPPORTED;
    break;
      default:
    break;
   }
}
#endif /* NO_INQUIRE_CAPABILITIES */
#endif /* FL_LOW_LEVEL */

/*----------------------------------------------------------------------*/
/*                   f l B u i l d G e o m e t r y                      */
/*                                                                      */
/* Get C/H/S information of the disk according to number of sectors.    */
/*                                                                      */
/* Parameters:                                                          */
/*  capacity    : Number of Sectors in Volume                           */
/*  cylinders   : Pointer to Number of Cylinders                        */
/*  heads       : Pointer to Number of Heads                            */
/*  sectors     : Pointer to Number of Sectors per Track                */
/*  oldFormat   : True for one sector per culoster                      */
/*                                                                      */
/*----------------------------------------------------------------------*/

void NAMING_CONVENTION flBuildGeometry(dword capacity, dword FAR2 *cylinders,
         dword FAR2 *heads,dword FAR2 *sectors, FLBoolean oldFormat)
{
  dword temp;

  *cylinders = 1024;                 /* Set number of cylinders to max value */

  if (oldFormat == TRUE)
  {
    *sectors = 62L;                     /* Max out number of sectors per track */
    temp = (*cylinders) * (*sectors);   /* Compute divisor for heads           */
    (*heads) = capacity / temp;         /* Compute value for number of heads   */
    if (capacity % temp) {              /* If no remainder, done!              */
      (*heads)++;                       /* Else, increment number of heads     */
      temp = (*cylinders) * (*heads);   /* Compute divisor for sectors         */
      (*sectors) = capacity / temp;     /* Compute value for sectors per track */
      if (capacity % temp) {            /* If no remainder, done!              */
        (*sectors)++;                   /* Else, increment number of sectors   */
        temp = (*heads) * (*sectors);   /* Compute divisor for cylinders       */
        (*cylinders) = capacity / temp; /* Compute number of cylinders         */
      }
    }
  }
  else
  {
     *heads = 16L;                              /* Max out number of heads             */
     temp = (*cylinders) * (*heads);            /* Compute divisor for heads           */
     *sectors = capacity / temp;                /* Compute value for sectors per track */
     while (*sectors > 0x3f ){                  /* While number of sectors too big     */
       *heads *= 2;                             /* use one more head                   */
       temp = (*cylinders) * (*heads);          /* Recompute divisor for heads         */
       *sectors = capacity / temp;              /* Recompute sectors per track         */
     }
     if (capacity % temp) {                     /* If no remainder, done!              */
       (*sectors)++;                            /* Else, increment number of sectors   */
       temp = (*cylinders) * (*sectors);        /* Compute divisor for heads           */
       *heads = capacity / temp;                /* Compute value for heads             */
       if (capacity % temp) {                   /* If no remainder, done!              */
         (*heads)++;                            /* Else, increment number of heads     */
         temp = (*heads) * (*sectors);          /* Compute divisor for cylinders       */
         *cylinders = (dword)(capacity / temp); /* Compute number of cylinders         */
       }
     }
  }
}

/*----------------------------------------------------------------------*/
/*                   v o l u m e I n f o                                */
/*                                                                      */
/* Get general volume Information.                                      */
/*                                                                      */
/* Parameters:                                                          */
/*  vol         : Pointer identifying drive                             */
/*  irHandle    : Drive number (0, 1, ...)                              */
/*  irData      : pointer to VolumeInfoRecord                           */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

static FLStatus volumeInfo(Volume vol, IOreq FAR2 *ioreq)
{
  VolumeInfoRecord FAR2 *info = (VolumeInfoRecord FAR2 *)(ioreq->irData);
#ifdef FL_LOW_LEVEL
  IOreq ioreq2;
  PhysicalInfo physicalInfo;
  char wasLowLevelMounted = 1;
#endif
  TLInfo tlInfo;
#ifdef FL_LOW_LEVEL
  dword eraseCyclesPerUnit;
#endif /* FL_LOW_LEVEL */

#ifdef FL_LOW_LEVEL
  ioreq2.irHandle = ioreq->irHandle;
  if (!(vol.flags & VOLUME_LOW_LVL_MOUNTED)) {
    checkStatus(mountLowLevel(&vol));
    wasLowLevelMounted = 0;
  }
  ioreq2.irData = &physicalInfo;
  checkStatus(getPhysicalInfo(&vol, &ioreq2));
  info->flashType = physicalInfo.type;
  info->physicalUnitSize = (unsigned short)physicalInfo.unitSize;
  info->physicalSize = physicalInfo.mediaSize;
  info->DOCType = physicalInfo.mediaType;
#endif /* FL_LOW_LEVEL */
  tffsset(info->driverVer,0,sizeof(info->driverVer));
  tffsset(info->OSAKVer,0,sizeof(info->OSAKVer));
  tffscpy(info->driverVer,driverVersion,
          TFFSMIN(sizeof(info->driverVer),sizeof(driverVersion)));
  tffscpy(info->OSAKVer,OSAKVersion,
          TFFSMIN(sizeof(info->OSAKVer),sizeof(OSAKVersion)));
  checkStatus(socketInfo(&vol, &(info->baseAddress)));
  checkStatus(vol.tl.getTLInfo(vol.tl.rec,&tlInfo));
  info->logicalSectors = tlInfo.sectorsInVolume;
  info->bootAreaSize = tlInfo.bootAreaSize;

#ifdef ABS_READ_WRITE
  flBuildGeometry( tlInfo.sectorsInVolume,
       (dword FAR2 *)&(info->cylinders),
       (dword FAR2 *)&(info->heads),
       (dword FAR2 *)&(info->sectors),FALSE);
#endif /* ABS_READ_WRITE */

#ifdef FL_LOW_LEVEL

  eraseCyclesPerUnit = vol.flash->maxEraseCycles;
  info->lifeTime = (char)(((tlInfo.eraseCycles /
       (eraseCyclesPerUnit * (physicalInfo.mediaSize / physicalInfo.unitSize)))
       % 10) + 1);
  if(!wasLowLevelMounted)
    dismountLowLevel(&vol);
#endif /* FL_LOW_LEVEL */
  return flOK;
}

/*----------------------------------------------------------------------*/
/*                         b d C a l l                                  */
/*                                                                      */
/* Common entry-point to all file-system functions. Macros are          */
/* to call individual function, which are separately described below.   */
/*                                                                      */
/* Parameters:                                                          */
/*      function        : file-system function code (listed below)      */
/*      ioreq           : IOreq structure                               */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

FLStatus NAMING_CONVENTION bdCall(FLFunctionNo functionNo, IOreq FAR2 *ioreq)
{
  Volume vol = NULL;
  FLStatus status;
  byte volNo;
#if defined(FILES) && FILES>0
  File *file;
#endif
  byte socket    = FL_GET_SOCKET_FROM_HANDLE(ioreq);
  byte partition = FL_GET_PARTITION_FROM_HANDLE(ioreq);
  byte curPartitionForEnvVars;

/***********************************************/
/***                                         ***/
/***    find the volume record to work on    ***/
/***                                         ***/
/***********************************************/

  if (initDone == FALSE)
    checkStatus(flInit());

/** ori - We are not taking mutex befre cheking fileTable */

/**************************************/
/* working with open files operations */
/**************************************/

#if defined(FILES) && FILES>0
  /* Verify file handle if applicable */
  if ((functionNo <  INDEX_OPENFILES_END) &&
      ((functionNo != FL_FIND_FILE)||(ioreq->irFlags & FIND_BY_HANDLE)))
  {
     if ((ioreq->irHandle < FILES) &&
     (fileTable[ioreq->irHandle].flags & FILE_IS_OPEN))
     {
        file = fileTable + ioreq->irHandle;
        pVol = file->fileVol;
        socket = vol.tl.socketNo;
        partition = vol.tl.partitionNo;
     }
     else
     {
        return flBadFileHandle;
     }
  }

#endif  /* FILES>0 */

  /* Set environment variable current partition. */
  curPartitionForEnvVars = partition;


/****************************************************/
/* None files operations are divided into 3 groups: */
/*  1. Uses the specific partition volume record.   */
/*  2. Must be called with partition number 0.      */
/*  3. Must use partition number 0 and ignore       */
/*     the given partition (binary operations).     */
/****************************************************/

  if (pVol == NULL)     /* irHandle is drive no. */
  {

     /* Handle sanity check */

     if ((socket    >= noOfSockets) ||
         (partition >= MAX_TL_PARTITIONS))
        return flBadDriveHandle;

     volNo = handleConversionTable[socket][partition];

     /* Some operation must not be checked ONLY for socket sanity */

     if((functionNo < INDEX_BINARY_END) &&     /* Group 3 */
        (functionNo > INDEX_BINARY_START))
     {
        volNo = socket;

        /* Binary partitions are numbered after BDTL partitions */
        curPartitionForEnvVars = (byte)(MAX_TL_PARTITIONS+partition);
     }

     /* The rest must be checked for socket and volume sanity */

     else
     {
        if (volNo == INVALID_VOLUME_NUMBER)    /* Group 1 + 2 */
          return flBadDriveHandle;

        /* Some operation must be called with partition 0 */

        if ((functionNo > INDEX_NEED_PARTITION_0_START) &&   /* Group 2 */
            (functionNo < INDEX_NEED_PARTITION_0_END))
        {
            if(partition != 0)
            {
               return flBadDriveHandle;
            }
            else /* Use general verify write environement variable */
            {
               curPartitionForEnvVars = (MAX_TL_PARTITIONS<<1)-1;
            }
        }
     }
     pVol = &vols[volNo];
  }

/********************************************************************/
/***                                                              ***/
/***   Volume record has been found. Now verify that the volume   ***/
/***   record is ready for the specific operation.                ***/
/***                                                              ***/
/********************************************************************/

  status = setBusy(&vol,FL_ON,curPartitionForEnvVars); /* Let everyone know we are here */

/*******************************/
/* Verify S/W write protection */
/*******************************/

#if defined(WRITE_PROTECTION)&& !defined(FL_READ_ONLY)
  if(vol.flags&VOLUME_WRITE_PROTECTED)
     if(
#if defined(FILES) && FILES>0
    ((functionNo > INDEX_WRITE_FILE_START) &&
     (functionNo < FL_LAST_FAT_FUNCTION))  ||
#endif /* FILES > 0 */
#ifdef FORMAT_VOLUME
    (functionNo==BD_FORMAT_VOLUME)         ||
    (functionNo==BD_FORMAT_PHYSICAL_DRIVE) ||
    (functionNo==BD_FORMAT_LOGICAL_DRIVE)  ||
#endif /* FORMAT_VOLUME */
#ifdef WRITE_EXB_IMAGE
    (functionNo==FL_PLACE_EXB)             ||
#endif /* WRITE_EXB_IMAGE */
    (functionNo==FL_DEFRAGMENT_VOLUME)     ||
#ifdef ABS_READ_WRITE
    (functionNo==FL_ABS_WRITE)             ||
    (functionNo==FL_ABS_DELETE)            ||
#endif /* ABS_READ_WRITE */
#ifdef FL_LOW_LEVEL
    (functionNo==FL_PHYSICAL_WRITE)        ||
    (functionNo==FL_PHYSICAL_ERASE)        ||
#endif /* FL_LOW_LEVEL */
#ifdef VERIFY_VOLUME
    (functionNo==FL_VERIFY_VOLUME)         ||
#endif /* VERIFY_VOLUME */
    0)
  {
     status = flWriteProtect;
     goto flCallExit;
  }

#endif /* WRITE_PROTECTION  && !FL_READ_ONLY */

/***********************************************/
/* Binary partition operations                 */
/* Low level mount and direct call the routine */
/***********************************************/

#ifdef BDK_ACCESS
  if ((functionNo > INDEX_BINARY_START) &&
      (functionNo < INDEX_BINARY_END  )    )
  {
#ifdef WRITE_EXB_IMAGE
     if ((functionNo != FL_BINARY_PROTECTION_INSERT_KEY) &&
         (functionNo != FL_BINARY_PROTECTION_REMOVE_KEY))
        vol.moduleNo = INVALID_MODULE_NO; /* Stop place EXB operation */
#endif
     /* Mount flash and call binary module to proccess request */

     if (!(pVol->flags & VOLUME_LOW_LVL_MOUNTED))
        status = mountLowLevel(&vol);
     if (status == flOK)
        status = bdkCall(functionNo,ioreq,pVol->flash);
     goto flCallExit;
  }
#endif /* BDK_ACCESS */

/***********************************/
/* None binary routine             */
/* Nag about mounting if necessary */
/***********************************/

  switch (functionNo) {
#if FILES > 0
    case FL_LAST_FAT_FUNCTION:
      status = flBadFunction;
      goto flCallExit;
#endif

/* Pre mount routine - Make sure the volume is low level disMounted */

    case FL_ABS_MOUNT:
    case FL_MOUNT_VOLUME:
#ifndef FL_READ_ONLY
#ifdef FORMAT_VOLUME
    case BD_FORMAT_VOLUME:
    case BD_FORMAT_LOGICAL_DRIVE:
#endif /* FORMAT_VOLUME */
#endif /* FL_READ_ONLY */
#ifdef FL_LOW_LEVEL
      if (vol.flags & VOLUME_LOW_LVL_MOUNTED)
         dismountLowLevel(&vol);  /* mutual exclusive mounting */
#endif /* FL_LOW_LEVEL */
      break;

/* Physical operation must have the device low level (MTD) mounted */

/* Do not need any special operation */

    case FL_UPDATE_SOCKET_PARAMS:
    case FL_COUNT_VOLUMES:
#if (defined(FORMAT_VOLUME) && !defined(FL_READ_ONLY))
    case FL_WRITE_BBT:
#endif
#ifdef HW_PROTECTION
    case FL_PROTECTION_GET_TYPE:
    case FL_PROTECTION_REMOVE_KEY:
    case FL_PROTECTION_INSERT_KEY:
#ifndef FL_READ_ONLY
    case FL_PROTECTION_SET_LOCK:
    case FL_PROTECTION_CHANGE_KEY:
    case FL_PROTECTION_CHANGE_TYPE:
#endif /* FL_READ_ONLY */
#endif /* HW_PROTECTION */
#ifdef QUICK_MOUNT_FEATURE
    case FL_CLEAR_QUICK_MOUNT_INFO:
#endif /* QUICK_MOUNT_FEATURE */ 
       break;

/* MTD must be mounted first */

#ifdef FL_LOW_LEVEL

/* Some physical operation must not be done while abs mounted */
    case FL_GET_PHYSICAL_INFO:
    case FL_PHYSICAL_READ:
#ifndef FL_READ_ONLY
    case FL_PHYSICAL_WRITE:
    case FL_PHYSICAL_ERASE:
#endif /* FL_READ_ONLY */
#ifndef BDK_ACCESS
#ifdef MULTI_DOC
#ifdef ENVIRONMENT_VARS
      if (flUseMultiDoc == FL_ON)
#endif
      {
         volNo = handleConversionTable[0][0];
         if ((volNo != INVALID_VOLUME_NUMBER) &&
             (pVol[volNo].flags & VOLUME_ABS_MOUNTED))
#else
      {
         if(vol.flags & VOLUME_ABS_MOUNTED)
#endif
         {
            status = flGeneralFailure;  /* mutual exclusive mounting */
            goto flCallExit;
         }
      }
#endif /* BDK_ACCESS */
#ifndef NO_INQUIRE_CAPABILITIES
    case FL_INQUIRE_CAPABILITIES:
#endif /* NO_INQUIRE_CAPABILITIES */
    case FL_DEEP_POWER_DOWN_MODE:
#ifdef HW_OTP
    case FL_OTP_SIZE:
    case FL_OTP_READ:
#ifndef FL_READ_ONLY
    case FL_OTP_WRITE:
#endif /* FL_READ_ONLY */
    case FL_UNIQUE_ID:
    case FL_CUSTOMER_ID:
#endif /* HW_OTP */
#ifndef NO_IPL_CODE
    case FL_READ_IPL:
#ifndef FL_READ_ONLY
    case FL_WRITE_IPL:
#endif /* FL_READ_ONLY */
#endif /* NO_IPL_CODE */
#ifndef FL_READ_ONLY
#ifdef WRITE_EXB_IMAGE
    case FL_PLACE_EXB:
#endif /* WRITE_EXB_IMAGE */
#ifdef FORMAT_VOLUME
    case BD_FORMAT_PHYSICAL_DRIVE:
#endif /* FORMAT_VOLUME */
#endif /* FL_READ_ONLY */

      if (!(vol.flags & VOLUME_LOW_LVL_MOUNTED))
      {
         status = mountLowLevel(&vol);  /* automatic low level mounting */
      }
      else
      {
         status = flOK;
#ifndef FIXED_MEDIA
         status = flMediaCheck(vol.socket);
         if (status == flDiskChange)
            status = mountLowLevel(&vol); /* card was changed, remount */
#endif /* FIXED_MEDIA */
      }
      if (status != flOK)
      {
         dismountLowLevel(&vol);
         goto flCallExit;
      }
      break;
#endif /* FL_LOW_LEVEL */

/* Check for abs mount */


	

	
	default:
      if (vol.flags & VOLUME_ABS_MOUNTED)
      {
#ifndef NT5PORT
         FLStatus status = flOK;
#else /*NT5PORT*/
         FLStatus istatus = flOK;
#endif /*NT5PORT*/

#ifndef FIXED_MEDIA
#ifndef NT5PORT
         status = flMediaCheck(vol.socket);
#else /*NT5PORT*/
         istatus = flMediaCheck(vol.socket);
#endif /*NT5PORT*/
#endif /*FIXED_MEDIA*/

#ifndef NT5PORT
         if (status != flOK)
#else /*NT5PORT*/
         if (istatus != flOK)
#endif /*NT5PORT*/
            dismountVolume(&vol);
      }
      if (!(vol.flags & VOLUME_ABS_MOUNTED)
#if defined(FILES) && FILES>0
          && (functionNo > FL_LAST_FAT_FUNCTION)
#endif
      )
      {
         status = flNotMounted;
         goto flCallExit;
      }

/* We know that the tl is abs mounted (except for files )
   now check for high level mounted                       */

      if (!( vol.flags  & VOLUME_MOUNTED        ) &&
           ( functionNo != FL_DISMOUNT_VOLUME   ) &&
           ( functionNo != FL_CHECK_VOLUME      ) &&
#ifndef FL_READ_ONLY
           ( functionNo != FL_DEFRAGMENT_VOLUME ) &&
#endif  /* FL_READ_ONLY */
#ifdef ABS_READ_WRITE
           ( functionNo != FL_ABS_READ          ) &&
           ( functionNo != FL_ABS_ADDRESS       ) &&
#ifndef FL_READ_ONLY
           ( functionNo != FL_ABS_WRITE         ) &&
           ( functionNo != FL_ABS_DELETE        ) &&
#endif  /* FL_READ_ONLY */
#endif /* ABS_READ_WRITE */
#if (defined(WRITE_PROTECTION) && !defined(FL_READ_ONLY))
           ( functionNo != FL_WRITE_PROTECTION  ) &&
#endif /* WRITE_PROTECTION */
#ifdef VERIFY_VOLUME
           ( functionNo != FL_VERIFY_VOLUME     ) &&
#endif /* VERIFY_VOLUME */
           ( functionNo != FL_READ_BBT          ) &&
           ( functionNo != FL_SECTORS_IN_VOLUME ) &&
           ( functionNo != FL_VOLUME_INFO       ))
      {
         status = flNotMounted;
         goto flCallExit;
      }
  }

/*****************************************************/
/***                                               ***/
/***   The Volume record is already initialized.   ***/
/***   Exceute the proper function.                ***/
/***                                               ***/
/*****************************************************/

  switch (functionNo) {

#if defined(FILES) && FILES > 0
#ifndef FL_READ_ONLY
    case FL_FLUSH_BUFFER:
      status = flushBuffer(&vol);
      break;
#endif /* FL_READ_ONLY  */
    case FL_OPEN_FILE:
      status = openFile(&vol,ioreq);
      break;

    case FL_CLOSE_FILE:
      status = closeFile(file);
      break;
#ifndef FL_READ_ONLY
#ifdef SPLIT_JOIN_FILE
    case FL_JOIN_FILE:
      status = joinFile(file, ioreq);
      break;

    case FL_SPLIT_FILE:
      status = splitFile(file, ioreq);
      break;
#endif
#endif  /* FL_READ_ONLY */
    case FL_READ_FILE:
      status = readFile(file,ioreq);
      break;
#ifndef FL_READ_ONLY
    case FL_WRITE_FILE:
      status = writeFile(file,ioreq);
      break;
#endif
    case FL_SEEK_FILE:
      status = seekFile(file,ioreq);
      break;

    case FL_FIND_FILE:
      status = findFile(&vol,file,ioreq);
      break;

    case FL_FIND_FIRST_FILE:
      status = findFirstFile(&vol,ioreq);
      break;

    case FL_FIND_NEXT_FILE:
      status = findNextFile(file,ioreq);
      break;

    case FL_GET_DISK_INFO:
      status = getDiskInfo(&vol,ioreq);
      break;
#ifndef FL_READ_ONLY
    case FL_DELETE_FILE:
      status = deleteFile(&vol,ioreq,FALSE);
      break;

#ifdef RENAME_FILE
    case FL_RENAME_FILE:
      status = renameFile(&vol,ioreq);
      break;
#endif

#ifdef SUB_DIRECTORY
    case FL_MAKE_DIR:
      status = makeDir(&vol,ioreq);
      break;

    case FL_REMOVE_DIR:
      status = deleteFile(&vol,ioreq,TRUE);
      break;
#endif
#endif /* FL_READ_ONLY */
#endif /* FILES > 0 */

    case FL_MOUNT_VOLUME:
      status = mountVolume(&vol,&(ioreq->irFlags));
      break;

    case FL_DISMOUNT_VOLUME:
      status = dismountVolume(&vol);
      break;

    case FL_CHECK_VOLUME:
      status = flOK;            /* If we got this far */
      break;

    case FL_UPDATE_SOCKET_PARAMS:
      status = updateSocketParameters(flSocketOf(ioreq->irHandle), ioreq->irData);
      break;

#ifndef NO_READ_BBT_CODE
    case FL_READ_BBT:
      if(vol.tl.readBBT != NULL)
      {
         status = vol.tl.readBBT(vol.tl.rec,(CardAddress FAR1 *)ioreq->irData,
                 &(ioreq->irLength),&(ioreq->irFlags));
      }
      else
      {
         status = flFeatureNotSupported;
      }
      break;
#endif /* NO_READ_BBT_CODE */

#ifndef FL_READ_ONLY
#ifdef DEFRAGMENT_VOLUME
    case FL_DEFRAGMENT_VOLUME:
      status = defragmentVolume(&vol,ioreq);
      break;
#endif /* DEFRAGMENT_VOLUME */

#if defined (FORMAT_VOLUME) && !defined(FL_READ_ONLY)
    case BD_FORMAT_VOLUME:
      status = bdFormatVolume(&vol,ioreq);
      break;

    case BD_FORMAT_PHYSICAL_DRIVE:
      status = bdFormatPhysicalDrive(&vol,ioreq);
      break;

    case BD_FORMAT_LOGICAL_DRIVE:
      status = bdFormatLogicalDrive(&vol,ioreq);
      break;

#endif /* FORMAT_VOLUME AND NOT FL_READ_ONLY */
#ifdef WRITE_EXB_IMAGE
    case FL_PLACE_EXB:
      status = placeExbByBuffer(&vol,(byte FAR1*)ioreq->irData,
           ioreq->irLength,(word)ioreq->irWindowBase ,(word)ioreq->irFlags);
      break;

#endif /* WRITE_EXB_IMAGE */
#endif  /* FL_READ_ONLY */

    case FL_SECTORS_IN_VOLUME:
      status = sectorsInVolume(&vol,ioreq);
      break;

    case FL_ABS_MOUNT:
      status = absMountVolume(&vol);
      break;

#ifdef ABS_READ_WRITE
    case FL_ABS_READ:
      status = absRead(&vol,ioreq);
      break;

#ifndef NO_PHYSICAL_IO
    case FL_ABS_ADDRESS:
      status = absAddress(&vol,ioreq);
      break;
#endif /* NO_PHYSICAL_IO */

#ifndef FL_READ_ONLY
    case FL_ABS_DELETE:
      status = absDelete(&vol,ioreq);
      break;

    case FL_ABS_WRITE:
      status = absWrite(&vol,ioreq);
      break;

#endif  /* FL_READ_ONLY */

    case FL_GET_BPB:
      status = getBPB(&vol,ioreq);
      break;

#ifndef FL_READ_ONLY
#if (defined(WRITE_PROTECTION) && !defined(FL_READ_ONLY))
    case FL_WRITE_PROTECTION :
     status = writeProtect(&vol,ioreq);
     break;

#endif /* WRITE_PROTECTION */
#endif /* FL_READ_ONLY     */
#endif /* ABS_READ_WRITE   */

#ifdef VERIFY_VOLUME
    case FL_VERIFY_VOLUME :
     if ((vol.tl.checkVolume != NULL) &&
         (ioreq->irData      == NULL) &&
         (ioreq->irLength    == 0   )   )
     {
        status = vol.tl.checkVolume(vol.tl.rec);
     }
     else
     {
        status = flFeatureNotSupported;
     }
     break;
#endif /* VERIFY_VOLUME */

#ifdef FL_LOW_LEVEL
    case FL_GET_PHYSICAL_INFO:
      status = getPhysicalInfo(&vol, ioreq);
      break;

#ifndef NO_PHYSICAL_IO
    case FL_PHYSICAL_READ:
      status = physicalRead(&vol, ioreq);
      break;

#ifndef FL_READ_ONLY
    case FL_PHYSICAL_WRITE:
      status = physicalWrite(&vol, ioreq);
      break;

    case FL_PHYSICAL_ERASE:
      status = physicalErase(&vol, ioreq);
      break;
#endif /* FL_READ_ONLY */
#endif /* NO_PHYSICAL_IO */

      /* direct calls to the MTD */
#ifdef HW_OTP
    case FL_OTP_SIZE:
      if (vol.flash->otpSize != NULL)
      {
        word tmp = (word)ioreq->irFlags;
        status = vol.flash->otpSize(vol.flash, (dword FAR2 *)(&ioreq->irCount ),
                                               (dword FAR2 *)(&ioreq->irLength),
                                               &tmp);
        ioreq->irFlags = (unsigned)tmp;
      }
      else
      {
        status = flFeatureNotSupported;
      }
      break;

    case FL_OTP_READ:
      if (vol.flash->readOTP != NULL)
      {
        status = vol.flash->readOTP(vol.flash, (word)ioreq->irCount,
                       ioreq->irData, (word)ioreq->irLength);
      }
      else
      {
        status = flFeatureNotSupported;
      }
      break;

#ifndef FL_READ_ONLY
    case FL_OTP_WRITE:
      if (vol.flash->writeOTP != NULL)
      {
         status = vol.flash->writeOTP(vol.flash,ioreq->irData,
                         (word)ioreq->irLength);
      }
      else
      {
         status = flFeatureNotSupported;
      }
      break;

#endif /* FL_READ_ONLY */

    case FL_UNIQUE_ID:
      if (vol.flash->getUniqueId !=NULL)
      {
         status = vol.flash->getUniqueId(vol.flash, ioreq->irData);
      }
      else
      {
        status = flFeatureNotSupported;
      }
      break;

    case FL_CUSTOMER_ID:
      if (vol.flash->getUniqueId !=NULL)
      {
        byte buf[UNIQUE_ID_LEN];
        status = vol.flash->getUniqueId (vol.flash, buf);
        tffscpy (ioreq->irData,buf,CUSTOMER_ID_LEN);
      }
      else
      {
        status = flFeatureNotSupported;
      }
      break;
#endif /* HW_OTP */

#ifndef NO_IPL_CODE
#ifndef FL_READ_ONLY
    case FL_WRITE_IPL:
      status = writeIPL(vol.flash,(byte FAR1*)ioreq->irData,ioreq->irLength,ioreq->irFlags);
      break;
#endif /* FL_READ_ONLY */
    case FL_READ_IPL:
      status = readIPL(vol.flash,(byte FAR1*)ioreq->irData,ioreq->irLength);
      break;
#endif /* NO_IPL_CODE */

    case FL_DEEP_POWER_DOWN_MODE:
      if (vol.flash->enterDeepPowerDownMode !=NULL)
      {
        vol.flash->enterDeepPowerDownMode(vol.flash,(word)ioreq->irFlags);
        status = flOK;
      }
      else
      {
        status = flFeatureNotSupported;
      }
      break;

#ifndef NO_INQUIRE_CAPABILITIES
    case FL_INQUIRE_CAPABILITIES:
      inquireCapabilities(vol.flash,(FLCapability FAR2 *)&(ioreq->irLength));
      break;
#endif /* NO_INQUIRE_CAPABILITIES */

#endif /* FL_LOW_LEVEL */

    case FL_VOLUME_INFO:
      status = volumeInfo(&vol, ioreq);
      break;

     /* Pre mount routines */
#if (defined(FORMAT_VOLUME) && !defined(FL_READ_ONLY))
    case FL_WRITE_BBT:
#ifndef NT5PORT
		checkStatus(dismountPhysicalDrive(socket));
#else /*NT5PORT*/
		status = dismountPhysicalDrive(socket);
	    if (status != flOK)
		  goto flCallExit;
#endif /*NT5PORT*/
#endif
    case FL_COUNT_VOLUMES:
#ifdef QUICK_MOUNT_FEATURE
    case FL_CLEAR_QUICK_MOUNT_INFO:
#endif /* QUICK_MOUNT_FEATURE */ 
#ifdef HW_PROTECTION
    case FL_PROTECTION_GET_TYPE:
    case FL_PROTECTION_REMOVE_KEY:
    case FL_PROTECTION_INSERT_KEY:
#ifndef FL_READ_ONLY
    case FL_PROTECTION_SET_LOCK:
    case FL_PROTECTION_CHANGE_KEY:
    case FL_PROTECTION_CHANGE_TYPE:
#endif /* FL_READ_ONLY */
#endif  /* HW_PROTECTION */
      status = flPreMount(functionNo , ioreq , vol.flash);
      break;
    default:
      status = flBadFunction;
  }

#if ((defined(FILES)) && (FILES > 0) && (!defined(FL_READ_ONLY)))
  if (vol.volBuffer.checkPoint)
  {
     FLStatus st = flushBuffer(&vol);
     if (status == flOK)
        status = st;
  }
#endif

/*********************************************/
/* Exit nicely - Release mutex of the socket */
/*********************************************/

flCallExit:
  if(status==flOK)
    status = setBusy(&vol,FL_OFF,curPartitionForEnvVars);
  else
    setBusy(&vol,FL_OFF,curPartitionForEnvVars); /* We're leaving */

  return status;
}

#if POLLING_INTERVAL != 0

/*----------------------------------------------------------------------*/
/*                 s o c k e t I n t e r v a l R o u t i n e            */
/*                                                                      */
/* Routine called by the interval timer to perform periodic socket      */
/* actions and handle the watch-dog timer.                              */
/*                                                                      */
/* Parameters:                                                          */
/*      None                                                            */
/*                                                                      */
/*----------------------------------------------------------------------*/

/* Routine called at time intervals to poll sockets */
static void socketIntervalRoutine(void)
{
  unsigned volNo;
  Volume vol = vols;

  flMsecCounter += POLLING_INTERVAL;

  for (volNo = 0; volNo < noOfSockets; volNo++, pVol++)
    if (flTakeMutex(&flMutex[volNo])) {
#ifdef FL_BACKGROUND
      if (vol.flags & VOLUME_ABS_MOUNTED)
    /* Allow background operation to proceed */
    vol.tl.tlSetBusy(vol.tl.rec,FL_OFF);
#endif
      flIntervalRoutine(vol.socket);
      flFreeMutex(&flMutex[volNo]);
    }
}

#endif /* POLLING_INTERVAL != 0 */

/*----------------------------------------------------------------------*/
/*                          f l I n i t                                 */
/*                                                                      */
/* Initializes the FLite system, sockets and timers.                    */
/*                                                                      */
/* Calling this function is optional. If it is not called,              */
/* initialization will be done automatically on the first FLite call.   */
/* This function is provided for those applications who want to         */
/* explicitly initialize the system and get an initialization status.   */
/*                                                                      */
/* Calling flInit after initialization was done has no effect.          */
/*                                                                      */
/* Parameters:                                                          */
/*      None                                                            */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

FLStatus flInit(void)
{
  if (!initDone) {
    FLStatus status;
    unsigned volNo;
    Volume vol = vols;
    IOreq req;
#ifdef LOG_FILE
    FILE *out;

    out=FL_FOPEN("EDCerr.txt","w");
    FL_FPRINTF(out,"EDC error LOG\n");
    FL_FCLOSE(out);
#endif /* LOG_FILE */

 flInitGlobalVars();

#ifdef ENVIRONMENT_VARS

 /* Call users initialization routine for :
  *  flUse8Bit,flUseNFTLCache,flUseisRAM
  */
 flSetEnvVar();

 if(flUse8Bit==1)
 {
    tffscpy = flmemcpy;
    tffscmp = flmemcmp;
    tffsset = flmemset;
 }
 else
 {
    tffscpy = flcpy;
    tffsset = flset;
    tffscmp = flcmp;
 }

#endif /* ENVIRONMENT_VARS */

   /*
    * 1) Mark all the volumes as not used and free to be allocated.
    * 2) Clear passowrd in order to make it invald.
    */
   
    tffsset(vols,0,sizeof(vols));

    for (volNo = 0,pVol = vols; volNo < VOLUMES; volNo++,pVol++)
    {     
      /* The actual number of sockets is not yet known and will be retreaved by
       * flRegisterComponents routine by the socket componenets. For now supply
       * each of the possible sockets with its buffer and socket number.
       */
       if ( volNo < SOCKETS)
       {
          vol.socket = flSocketOf(volNo);
          vol.flash  = flFlashOf(volNo);
          tffsset(vol.socket,0,sizeof(FLSocket));          
          tffsset(vol.flash,0,sizeof(FLFlash));
          vol.socket->volNo = volNo;         
#ifdef WRITE_EXB_IMAGE
          vol.moduleNo = INVALID_MODULE_NO; /* Ready for exb write operation */
#endif
       }
       else
       {
           vol.flash = NULL;
       }
       vol.volExecInProgress = NULL;
    }

#if FILES > 0
    initFS();
#endif

    flSysfunInit();

#ifdef FL_BACKGROUND
    flCreateBackground();
#endif
    /* Initialize variales */
    for (noOfTLs = 0 ;noOfTLs < TLS;noOfTLs++)
    {
       tlTable[noOfTLs].mountRoutine     = NULL;
       tlTable[noOfTLs].preMountRoutine  = NULL;
       tlTable[noOfTLs].formatRoutine    = NULL;
    }
    initDone    = TRUE;
    noOfTLs     = 0;
    noOfDrives  = 0;
    noOfSockets = 0;
    noOfMTDs    = 0;

    checkStatus(flRegisterComponents());

#ifdef COMPRESSION
    checkStatus(flRegisterZIP());
#endif

#ifdef MULTI_DOC
#ifdef ENVIRONMENT_VARS
    if(flUseMultiDoc==FL_ON)
    {
      checkStatus(flRegisterMTL());
    }
#else
    checkStatus(flRegisterMTL());
#endif /* ENVIRONMENT_VARS */
#endif /* MULT_DOC */

#ifdef BDK_ACCESS
    bdkInit();
#endif

    checkStatus(flInitSockets());

#if POLLING_INTERVAL > 0
    checkStatus(flInstallTimer(socketIntervalRoutine,POLLING_INTERVAL));
#endif

  /*
   * Now that the number of actual sockets is known, create a mutex for
   * each of the systems sockets. Multi-doc uses only 1 mutex.
   */

#ifdef MULTI_DOC
   if ((noOfSockets)
#ifdef ENVIRONMENT_VARS
       &&(flUseMultiDoc == FL_ON)
#endif /* ENVIRONMENT_VARS */
      )
   {   /* All sockets use the same mutex */

      if (flCreateMutex(&flMutex[0]) != flOK)
         return flGeneralFailure;

      for (volNo = 0; volNo < noOfSockets ; volNo++)
      {
         vols[volNo].volExecInProgress = &flMutex[0];
      }
   }
   else
#endif /* MULTI_DOC */
   {   /* Each socket uses a diffren mutex */

      for (volNo = 0; volNo < noOfSockets ; volNo++)
      {
         if (flCreateMutex(&flMutex[volNo]) != flOK)
            return flGeneralFailure;
         vols[volNo].volExecInProgress   = &flMutex[volNo];
      }
   }

    /* Count the number of volumes on all the systems sockets and
     * initialize conversion table. The table is used to convert a socket
     * and a partition numbers to the correct volume index in the vols record.
     * Partition 0 of each of the sockets will recieve the volume record
     * indexed with the socket number in the vols array. The rest of the
     * volumes will be serialy allocated. New volume records can be allocated
     * By the format routine. The format and the write BBT routines clear all
     * volume records taken by the sockets except for the first.
     *
     * The partition and socket are passed to the TL module using 2 dedicated
     * fields in the TL record. Both those values are also initiaized.
     * All volumes on a specific socket will share the same mutex.
     */

    tffsset(handleConversionTable,INVALID_VOLUME_NUMBER, /* Clear table */
      sizeof(handleConversionTable));

    for (pVol = vols , noOfDrives = (unsigned)noOfSockets , req.irHandle = 0;
     req.irHandle < noOfSockets; req.irHandle++, pVol++)
    {
       /* Initialize partition 0 */

       handleConversionTable[req.irHandle][0] = (byte)req.irHandle;
       vol.tl.socketNo         = (byte)req.irHandle;
       vol.tl.partitionNo      = 0;
       vol.flags = VOLUME_ACCUPIED;

       /* The rest of the partitions are initialzed only if multi-doc
    * is not active since Multi-doc supports only the first volume
    * of each socket.
    */

       status =  flPreMount(FL_COUNT_VOLUMES , &req , vol.flash);
       if (status == flOK)
       {
          vol.volExecInProgress = &flMutex[req.irHandle];
          for(volNo = 1;(volNo < req.irFlags) && (noOfDrives < VOLUMES); volNo++,noOfDrives++)
          {
             handleConversionTable[req.irHandle][volNo] = (byte)noOfDrives;
             vols[noOfDrives].socket         = vol.socket;
             vols[noOfDrives].flash          = vol.flash;
             vols[noOfDrives].tl.socketNo    = (byte)req.irHandle;
             vols[noOfDrives].tl.partitionNo = (byte)volNo;
             vols[noOfDrives].volExecInProgress =
             vol.volExecInProgress;
             vol.flags = VOLUME_ACCUPIED;
          }
       }
    }
  }
  return flOK;
}

#ifdef EXIT

/*----------------------------------------------------------------------*/
/*                          f l E x i t                                 */
/*                                                                      */
/* If the application ever exits, flExit should be called before exit.  */
/* flExit flushes all buffers, closes all open files, powers down the   */
/* sockets and removes the interval timer.                              */
/*                                                                      */
/* Parameters:                                                          */
/*      None                                                            */
/*                                                                      */
/* Returns:                                                             */
/*      Nothing                                                         */
/*----------------------------------------------------------------------*/

void NAMING_CONVENTION flExit(void)
{
  unsigned volNo;
  Volume vol = vols;
  FLBoolean mutexTaken = FALSE;

  if(flInit==FALSE)
      return;

  /* Dismount the TL and MTD */

  for (volNo = 0; (volNo < VOLUMES); volNo++,pVol++)
  {
     if ((vol.flags & VOLUME_ACCUPIED) && (setBusy(&vol,FL_ON,vol.tl.partitionNo) == flOK))
     {
        dismountVolume(&vol);

#ifdef FL_LOW_LEVEL
        dismountLowLevel(&vol);
#endif
        setBusy(&vol,FL_OFF,vol.tl.partitionNo);
     }
  }

  /* Dismount SOCKET  */

  pVol = vols;
  for (volNo = 0; volNo < noOfSockets ; volNo++,pVol++)
  {
#ifdef MULTI_DOC
#ifdef ENVIRONMENT_VARS
      if (flUseMultiDoc == FL_ON)     /* multi-doc not active */
#endif /* ENVIRONMENT_VARS */
     if (volNo == 0)
#endif /* MULTI_DOC */
        mutexTaken = (setBusy(&vol,FL_ON,vol.tl.partitionNo) == flOK) ? TRUE:FALSE;

     if (mutexTaken == TRUE)
     {
        flFreeMutex(execInProgress);  /* free the mutex that was taken in setBusy(FL_ON) */
        /* delete mutex protecting FLite volume access */
        flDeleteMutex(execInProgress);
        mutexTaken = FALSE;
     }
     flExitSocket(vol.socket);
  }

#if POLLING_INTERVAL != 0
  flRemoveTimer();
#endif

#ifdef ALLOCTST
  out_data_sz();
#endif
  initDone           = FALSE;
  initGlobalVarsDone = FALSE;
  pVol = NULL;
}

#ifdef __BORLANDC__
#ifdef EXIT
#pragma exit flExit
#endif /* EXIT */
#include <dos.h>

static int cdecl flBreakExit(void)
{
  flExit();

  return 0;
}

static void setCBreak(void)
{
  ctrlbrk(flBreakExit);
}

#pragma startup setCBreak

#endif

#endif