/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1992 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 source code is provided to you solely for       */
/*    the purpose of assisting you in your development of OS/2 device        */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Developer Connection Device Driver       */
/*    Source Kit for OS/2. This Copyright statement may not be removed.      */
/*                                                                           */
/*****************************************************************************/

/*static char *SCCSID = "%w% %e%";*/
/**************************************************************************
 *
 * SOURCE FILE NAME = FLP1INI2.C
 *
 * DESCRIPTIVE NAME = ISA Diskette Driver - ReInitialization
 *
 *
 *
 * VERSION = V2.1
 *
 * DATE =  96/03/26
 *
 * DESCRIPTION :
 *
 * Purpose:       Contains reinitialization code for driver.
 *
 *
 *
 *
 *
*/                                                                    //@V149969
#define INCL_NOBASEAPI                                              //VVVVVVVV
#define INCL_NOPMAPI
#define INCL_DOSERRORS
#include "os2.h"
#include "dos.h"
#include "bseerr.h"

#include "dskinit.h"

#include "iorb.h"
#include "addcalls.h"
#include "dhcalls.h"
#include "apmcalls.h"
#include "doccalls.h"

#define INCL_INITRP_ONLY
#include "reqpkt.h"

#include "flp1cons.h"
#include "flp1regs.h"
#include "flp1misc.h"
#include "flp1type.h"
#include "flp1extn.h"
#include "flp1pro.h"
#include "flp1mif.h"

/*------------------------------------------------------------------------*/
/*                                                                        */
/* Reinitialization                                                       */
/*                                                                        */
/*------------------------------------------------------------------------*/

USHORT NEAR ReConfigureController( NPATBL npAT )
{
  NPUTBL         npUT;

  NPACB          npACB;
  NPUCB          npUCB;
  NPUCB          npFirstUCB;

  USHORT         rc = 0;

  USHORT         Port;
  USHORT         Value;

  USHORT         i;

  npACB = npAT->npACB;
  if ( !npACB )
    return ERROR_INVALID_HANDLE;                //HCT

  npACB->Flags &= ~ACBF_UNITSWAP;

  npUCB = npACB->npUCB;
  npFirstUCB = npACB->npFirstUCB;

  npACB->npUCB = 0;
  npACB->npFirstUCB = 0;

  /*---------------------------------------------*/
  /* Activate the ACB                            */
  /*                                             */
  /* This locks the code in memory and allocates */
  /* the requested IRQ level                     */
  /*---------------------------------------------*/

  if ( ActivateACB( npACB ) )
  {
    npAT->Status = ATS_SET_IRQ_FAILED;
    goto ReConfigureControllerFail;                //HCT
  }

  /*---------------------------------------------*/
  /* If the part was initially reset, remove the */
  /* reset condition.                            */
  /*---------------------------------------------*/
  DISABLE
  Port = npACB->IOPorts[FCR_DOR];
  Value = npACB->IORegs[FCR_DOR] = (FCR_DOR_RESET | FCR_DOR_DMA_GATE);
  outp( Port, Value );
  ENABLE
  IODelay();

  /*----------------------------------------------*/
  /* Perform an interrupt driven reset to         */
  /* verify the controller is functioning         */
  /*----------------------------------------------*/

  if ( InitControllerReset( npACB ) )
  {
    npACB->Flags |= ACBF_NOTPRESENT;
    DeactivateACB( npACB );
    goto ReConfigureControllerFail;                //HCT
  }

  /*--------------------------------------------*/
  /* Check if the part supports FIFO operation. */
  /* Configure the FIFO if supported            */
  /*--------------------------------------------*/
  CheckFIFOSupport( npAT );                                          /*@V95460*/

  /*-Japan---------------------------------------*/                 
  /* Butterfly has its machine unique FDC access */                 
  /* method, we have to determine if H/W is BUT. */                 
  /* If it is we have to turn on flag to change  */                 
  /* some FDC access logic. Butterfly = TP701C   */                 
  /*---------------------------------------------*/                 
  if( npAT->npACB->SeekFlags & ACBK_NATIONAL_8477)                  
      CheckIfButterfly( npAT);                                      
                                                                    
  /*---------------------------------------------*/
  /* Check for Media Sense support and determine */
  /* the polarity of the Changeline              */
  /*---------------------------------------------*/
  npAT->Flags |= CheckMediaIDSupport( npAT );

  npUT = npAT->Unit;

  for ( i=0; i < MAX_DRIVES; i++, npUT++ )
  {
    if ( !(npUT->Flags & UTBF_USER_MEDIASENSE) )                     /*@V85053*/
    {
      if ( npAT->Flags & ATBF_NO_MEDIASENSE )
      {
        npUT->Flags |= UTBF_NO_MEDIASENSE;
      }
    }

    if ( !(npUT->Flags & UTBF_USER_CHANGELINE) )
    {
      if ( npAT->Flags & ATBF_CHANGELINE_INVERT )
      {
        npUT->Flags |= UTBF_CHANGELINE_INVERT;
      }
    }
  }
ReConfigureControllerExit:
  npACB->npUCB = npUCB;
  npACB->npFirstUCB = npFirstUCB;
  return NO_ERROR;

ReConfigureControllerFail:                         //HCT
  npACB->npUCB = npUCB;
  npACB->npFirstUCB = npFirstUCB;
  return ERROR_GEN_FAILURE;


}

USHORT NEAR ReConfigureUnit( NPATBL npAT, USHORT UnitId )
{
  NPACB         npACB;
  NPUCB         npUCB;
  NPUTBL        npUT;

  USHORT        n;
  USHORT        i;

  USHORT        Port;
  USHORT        Value;

  npUT         = &npAT->Unit[UnitId];
  npUT->Status = 0;

  npACB = npAT->npACB;
  npUCB = npUT->npUCB;


  if ( !npACB || !npUCB )
     return ERROR_INVALID_HANDLE;


RetryReconf:
  /* Reinitialize the Unit Id for this channel */
  npUCB->UnitId = UnitId;
  if ( npACB->Flags & ACBF_UNITSWAP )
  {
    npUCB->UnitId ^= 1;
  }

  /*------------------------------------------*/
  /* RECAL the drive to see if it is really   */
  /* installed.                               */
  /*                                          */
  /* Non-installed drives will fail the RECAL */
  /* since the TRK0 indication will not occur */
  /* within 80 steps.                         */
  /*------------------------------------------*/

  npUCB->Flags &=
            ~(UCBF_NOTPRESENT|UCBF_MOTOR_ON|UCBF_MOTOR_ON_PREV|UCBF_MOTOR_OK);
  npUCB->Flags |= UCBF_MEDIA_UNCERTAIN;

  if ( CheckUnitInstalled( npUCB ) )
  {
    /*------------------------------------------*/
    /* Drive forced, but not present            */
    /*------------------------------------------*/
    npUCB->Flags |= UCBF_NOTPRESENT;
  }
  /*------------------------------------------*/
  /* Turn off the drive motor                 */
  /*------------------------------------------*/
  DISABLE

  npUCB->Flags &= ~(UCBF_MOTOR_ON|UCBF_MOTOR_ON_PREV|UCBF_MOTOR_OK);
  npACB->IORegs[FCR_DOR] &= ~(FCR_DOR_MOTOR_0 << npUCB->UnitId);
  Value = npACB->IORegs[FCR_DOR];
  Port  = npACB->IOPorts[FCR_DOR];
  outp( Port, Value );
  ENABLE
  IODelay();

  /*------------------------------------------*/
  /* If only one unit configured, check to    */
  /* see if the unit id's have been swapped.  */
  /*------------------------------------------*/
  if ( (npUCB->Flags & UCBF_NOTPRESENT) &&
       (npUCB->UnitId == 0) &&
       !(npACB->Flags & ACBF_UNITSWAP) &&
                !(npAT->Unit[1].Flags &= UTBF_FORCE) )
  {
     npACB->Flags |= ACBF_UNITSWAP;
      goto RetryReconf;
  }
ReConfigureUnit_Exit:
  return ( npUT->Status );                                          //@AAAAAAA
}                                                                   //@V149969

/*------------------------------------------------------------------------*/
/*                                                                        */
/* Optional Hardware Feature Detection                                    */
/*                                                                        */
/*------------------------------------------------------------------------*/

/*------------------------------------*/
/* CheckFIFOSupport                   */
/*                                    */
/* This routine checks if a newer     */
/* floppy controller is present which */
/* has a speed matching FIFO that     */
/* makes DMA underruns less likely.   */
/*                                    */
/*------------------------------------*/

VOID NEAR CheckFIFOSupport( NPATBL npAT )
{
  USHORT        Is82077   = 0;
  USHORT        SendError = 0;
  NPACB         npACB;

  npACB = npAT->npACB;

  npACB->CurResultsLen = FR_LEN_VERSION;
  npACB->CurCmdLen     = FC_LEN_VERSION;

  npACB->CurCmd[0] = FC_VERSION;

  if ( !(SendError=f_SendCommand( npACB, 0 )) )
  {
    if ( !f_ReadResults( npACB ) )
    {
      if ( npACB->CurResults[0] == FR_VERSION_I82077 )
      {
        Is82077 = 1;
      }
    }
  }

  if ( Is82077 )
  {
    npACB->CurCmdLen     = FC_LEN_CONFIGURE;

    npACB->CurCmd[0]     = FC_CONFIGURE;
    npACB->CurCmd[1]     = 0;
    npACB->CurCmd[2]     = FIFO_THRESHOLD_DEFAULT;
    npACB->CurCmd[3]     = 0;

    if ( !(SendError=f_SendCommand( npACB, 0 )) )
    {
      npACB->SeekFlags     = ACBK_ISEEK_CAPABLE;

      npACB->CurCmdLen     = FC_LEN_LOCK;
      npACB->CurResultsLen = FR_LEN_LOCK;
      npACB->CurCmd[0]     = FC_LOCK;

      if ( !(SendError=f_SendCommand( npACB, 0 )) )
      {
        if ( !f_ReadResults( npACB ) )
        {
          if ( npACB->CurResults[0] == FR_LOCK )
          {
            npAT->Flags |= ATBF_FIFO_SUPPORT;

            /*---------------------------------------------*/
            /* Check for National Semiconductor controller */
            /* series 8477, 87311...                       */
            /*---------------------------------------------*/
            npACB->CurCmdLen     = FC_LEN_NSC;
            npACB->CurCmd[0]     = FC_NSC;

            npACB->CurResultsLen = FR_LEN_NSC;

            if ( !(SendError=f_SendCommand( npACB, 0 )) )
            {
              if ( !f_ReadResults( npACB ) )
              {
                if ( (npACB->CurResults[0] == FR_VERSION_N8477A)
                      || (npACB->CurResults[0] == FR_VERSION_N8477B) )
                {
                  npACB->SeekFlags |= ACBK_NATIONAL_8477;
                }
              }
            }
          }
        }
      }
    }
  }

  /*------------------------------------------------*/
  /* If there was a timeout sending a command       */
  /* to the controller, it is likely the controller */
  /* rejected this command or the previous command  */
  /*                                                */
  /* Result byte ST0 is read to clear pending       */
  /* results for the rejected command.              */
  /*------------------------------------------------*/

  if ( SendError )
  {
    npACB->CurResultsLen = FR_LEN_INVALID_CMD;
    f_ReadResults( npACB );
  }
}

/*------------------------------------*/
/* CheckMediaIDSupport                */
/*                                    */
/* This routine determines whether    */
/* the controller can detect the      */
/* type of media (1MB, 2MB, 4MB)      */
/* by sensing the position of the     */
/* hole on the lower left side of     */
/* the diskette.                      */
/*                                    */
/*------------------------------------*/

USHORT NEAR CheckMediaIDSupport( NPATBL npAT )
{
  NPACB         npACB;                                               /*@V95460*/

  USHORT        DORPort;
  USHORT        TDRPort;
  USHORT        DIRPort;
  USHORT        DSRPort;

  USHORT        Flags = 0;

  USHORT        MediaIDSupported = 0;
  USHORT        i;
  UCHAR         Value;

  npACB   = npAT->npACB;                                             /*@V95460*/
  DORPort = npACB->IOPorts[FCR_DOR];                                 /*@V95460*/
  TDRPort = npACB->IOPorts[FCR_TDR];                                 /*@V95460*/
  DIRPort = npACB->IOPorts[FCR_DIR];                                 /*@V95460*/
  DSRPort = npACB->IOPorts[FCR_DSR];                                 /*@V95460*/

  /*------------------------------------------------------*/
  /* Read the Tape Drive Register                         */
  /*                                                      */
  /* The value in this register is read after selecting   */
  /* each drive to determine whether the register is      */
  /* actually implemented in the Floppy controller chip.  */
  /*                                                      */
  /*------------------------------------------------------*/

  /*--------------------------------------------*/
  /* Select drives 0-3 and turn on motor enable */
  /*--------------------------------------------*/

  npACB->IORegs[FCR_DOR] &= ~(FCR_DOR_MOTOR_ENABLE                   /*@V95460*/
                                          | FCR_DOR_DRIVE_SEL);      /*@V95460*/

  for ( i = 0; i < MAX_DRIVES; i++ )
  {
    Value = npACB->IORegs[FCR_DOR] | (FCR_DOR_MOTOR_0 << i) | i;     /*@V95460*/
    outp( DORPort, Value);
    IODelay();

    inp( TDRPort, Value );
    IODelay();

    /*--------------------------------------------------*/
    /* If the register always reads FC then it is not   */
    /* implemented at all!                              */
    /*                                                  */
    /* Otherwise verify Boot Drive ID bits 2-3 = 00     */
    /*--------------------------------------------------*/
    if ( (Value & ~FCR_TDR_TAPE_SEL) != (UCHAR) ~FCR_TDR_TAPE_SEL )
    {
      if ( !(Value & FCR_TDR_BOOT_ID) )                              /*@V85053*/
      {
        MediaIDSupported = 1;
      }
    }
  }

  /*----------------------------------------------------------*/     /*@V95460*/
  /* National requested that we check TDR Bit 5 prior to      */     /*@VVVVVV*/
  /* enabling media sense on their controllers.               */
  /*                                                          */
  /* On N87340 series parts, the media sense pins can be      */
  /* reconfigured as density select outputs. Bit 5 indicates  */
  /* indicates whether the media sense function is disabled.  */
  /*----------------------------------------------------------*/

  if ( npACB->SeekFlags & ACBK_NATIONAL_8477 )
  {
    if ( Value & FCR_TDR_MEDIA_ID_DISABLE )
    {
      MediaIDSupported = 0;
    }                                                                /*@VAAAAA*/
  }                                                                  /*@V95460*/

  if ( uCMachine & BUSINFO_OEM_ABIOS )                       // @V92217
  {                                                          // @V92217
    MediaIDSupported = 0;                                    // @V92217
  }                                                          // @V92217

  /*---------------------------------------------*/
  /* Deselect all drives and read ChangeLine     */
  /*                                             */
  /* If ChangeLine bit is non-zero then we have  */
  /* an inverted change indication.              */
  /*---------------------------------------------*/

  Value =  npACB->IORegs[FCR_DOR];                                   /*@V95460*/

  outp( DORPort, Value );
  IODelay();

  inp( DIRPort, Value );

  if ( Value & FCR_DIR_DSK_CHG )
  {
    Flags |= ATBF_CHANGELINE_INVERT;
  }

  if ( !MediaIDSupported )
  {
    Flags |= ATBF_NO_MEDIASENSE;
  }

  /*----------------------------------------------*/
  /* Check for quirky IBM Notebook models         */
  /* whose designers cant seem to make a diskette */
  /* controller that conforms to the rest of      */
  /* the industry.                                */
  /*----------------------------------------------*/

  if ( IBMModel == IBM_MODEL_386 )
  {
    /*-----------------------------------------*/
    /* Turn off diskette power management for  */
    /* IBM Model L40SX.                        */
    /*                                         */
    /* We need to do this since the genius     */
    /* who designed the dskt controller set    */
    /* auto powerdown interval to 500ms so the */
    /* controller powers down during the motor */
    /* spin-up interval!                       */
    /*                                         */
    /* This is done by setting the PRECOMP     */
    /* field of DSR (3F4) to 001               */
    /*-----------------------------------------*/
    if ( IBMSubModel == IBM_SUBMODEL_L40SX )
    {
      outp( DSRPort, IBM_L40SX_PWR_DISABLE )
    }

    /*-----------------------------------------*/
    /* The Yamato lab seems to be able to      */
    /* design dskt controllers that fool the   */
    /* automatic changeline polarity detection */
    /* in the driver.                          */
    /*-----------------------------------------*/
    if ( IBMSubModel == IBM_SUBMODEL_N1 ||
               IBMSubModel == IBM_SUBMODEL_N2 )
    {
      Flags &= ~ATBF_CHANGELINE_INVERT;
    }
  }

  return ( Flags );
}

/*------------------------------------------------------------------------*/
/*                                                                        */
/* Initialization I/O Routines                                            */
/*                                                                        */
/* These routines interface to the Inner State Machine to provide         */
/* init-time I/O services. These routines are also used by the APM Resume */
/*                                                                        */
/*------------------------------------------------------------------------*/


/*------------------------------------*/
/* CheckUnitInstalled                 */
/*                                    */
/* This routine issues a RECALIBRATE  */
/* request to the drive and returns   */
/* the result to the caller.          */
/*                                    */
/*------------------------------------*/

USHORT NEAR CheckUnitInstalled( NPUCB npUCB )
{
  NPACB         npACB;
  USHORT        rc;

  npACB           = npUCB->npACB;
  npACB->ReqFlags = (ACBR_FORCE_SELECT | ACBR_SPECIFY | ACBR_MOTOR_WAIT |
                     ACBR_MOTOR_ON     | ACBR_RECAL);

  rc = InitIORequest( npUCB );

  return( rc );
}

/*---------------------------------------*/
/* InitControllerReset                   */
/*                                       */
/* This routine issues an a CONTROLLER   */
/* RESET to verify that the controller   */
/* is installed and its IRQ level is     */
/* known.                                */
/*                                       */
/*---------------------------------------*/

USHORT NEAR InitControllerReset( NPACB npACB )
{
  USHORT        IRQTimer;                                            /*@V88786*/

  FLP1IOWait = 1;

  IRQTimer          = npACB->IRQTimer;                               /*@V88786*/
  npACB->IRQTimer   = INIT_RESET_TIMEOUT;                            /*@V88786*/

  npACB->ReqFlags   = ACBR_RESET;
  npACB->ISMDoneRtn = InitIODone;
  npACB->State      = ACBS_NEXT_REQUEST;
  npACB->ISMRecal   = 0;
  npACB->CurUnitId  = -1;

  AllocateHWResources( npACB );

  StartISM( npACB );

  InitIOWait();

  FreeHWResources( npACB );

  npACB->IRQTimer   = IRQTimer;                                      /*@V88786*/

  return ( (npACB->CurStatus == REQS_NO_ERROR) ? 0 : 1 );
}

/*------------------------------------*/
/* InitIORequest                      */
/*                                    */
/* This routine interfaces with the   */
/* Inner State Machine code to start  */
/* I/O requests at init-time          */
/*                                    */
/*------------------------------------*/

USHORT NEAR InitIORequest( NPUCB npUCB )
{
  NPACB         npACB;

  FLP1IOWait = 1;

  npACB             = npUCB->npACB;

  npACB->ISMDoneRtn = InitIODone;

  npACB->State      = ACBS_NEXT_REQUEST;

  npACB->npUCB      = npUCB;
  npACB->UnitId     = npUCB->UnitId;
  npACB->ISMRecal   = 0;

  AllocateHWResources( npACB );

  StartISM( npACB );

  InitIOWait();

  FreeHWResources( npACB );

  return ( (npACB->CurStatus == REQS_NO_ERROR) ? 0 : 1 );
}

/*------------------------------------*/
/* InitIOWait                         */
/*                                    */
/* This routine waits for requests    */
/* issued by the init-time I/O        */
/* routines to complete.              */
/*------------------------------------*/
VOID  NEAR InitIOWait()
{
  DISABLE
  while ( FLP1IOWait )
  {
    DevHelp_ProcBlock( (ULONG) &FLP1IOWait,
                       (ULONG) -1L,
                       (USHORT) WAIT_IS_NOT_INTERRUPTABLE );
    DISABLE
  }
}

/*------------------------------------*/
/* InitIODone                         */
/*                                    */
/* This routine is called from the    */
/* ISM NotifyRoutine. It wakes        */
/* the I/O requestor waiting in       */
/* InitIOWait.                        */
/*------------------------------------*/

VOID FAR InitIODone( NPACB npACB )
{
  USHORT        AwakeCount;

  FLP1IOWait = 0;
  DevHelp_ProcRun( (ULONG)   &FLP1IOWait,
                   (PUSHORT) &AwakeCount  );

  npACB->Flags |= ACBF_WAITSTATE;
}

/*------------------------------------*/                                
/* CheckIfButterfly                   */                                
/*                                    */                                
/*                                    */                                
/*------------------------------------*/                                
                                                                        
VOID NEAR CheckIfButterfly( NPATBL npAT )                               
{                                                                       
    USHORT Value, HiValue;                                              
    USHORT SMIPort = 0;                                                 
                                                                        
    /************************************/                              
    /* CMOS_CTRL Port:  0x70            */                              
    /* CMOS_DATA Port:  0x71            */                              
    /* CMOS RAM Address 0x7D (Hi)       */                              
    /* CMOS RAM Address 0x7C (Lo)       */                              
    /* To get status word from CMOS     */                              
    /************************************/                              
                                                                        
    DISABLE                                                             
    _asm{                                                               
        mov     dx, 070h                                                
        mov     al, 07dh                                                
        out     dx, al                                                  
        inc     dx                                                      
        in      al, dx                                                  
        shl     ax, 8                                                   
        dec     dx                                                      
        mov     al, 07ch                                                
        out     dx, al                                                  
        inc     dx                                                      
        in      al, dx                                                  
        mov     Value, ax                                               
    };                                                                  
    ENABLE                                                              
    if( Value != 0x5349) return;                                        
                                                                        
    /************************************/                              
    /* CMOS RAM Address 0x7F (Hi)       */                              
    /* CMOS RAM Address 0x7E (Lo)       */                              
    /* To get SMI port from CMOS        */                              
    /************************************/                              
    DISABLE                                                             
    _asm{                                                               
        mov     dx, 070h                                                
        mov     al, 07fh                                                
        out     dx, al                                                  
        inc     dx                                                      
        in      al, dx                                                  
        shl     ax, 8                                                   
        dec     dx                                                      
        mov     al, 07eh                                                
        out     dx, al                                                  
        inc     dx                                                      
        in      al, dx                                                  
        mov     dx, ax                                                  
        mov     ax, 5380h                                               
        mov     bx, 0                                                   
        /********************************/                              
        /* DX: SMI Port Address         */                              
        /* AX: 5380=SMAPI Request       */                              
        /* BX: 0   =SMPAI Get SysInfo   */                              
        /********************************/                              
        /* save CX register             ****************************/   //@V159786
        /* Looks like a hardware error when OUT is made and values */   //@V159786
        /* in DI and SI are changed, so we are saving them prior to*/   //@V159786
        /* the OUT and restoring after.                            */   //@V159786
        /***********************************************************/   //@V159786
        push    cx                                                      //@V159786
        mov     cx, 3                                                   
    Retry:                                                              
        push    cx                                                      
        push    si                      /* save SI */                   //@V159786
        push    di                      /* save DI */                   //@V159786
        out     dx,  al                 /* DI and SI changes */         
        out     4fh, al                                                 
        out     4fh, al                                                 
        cmp     ah, 53h                                                 
        pop     di                      /* restore DI */                //@V159786
        pop     si                      /* restore SI */                //@V159786
        pop     cx                                                      
        jne     Func_Ok                                                 
        loop    Retry                                                   
    Func_Ok:                                                            
        pop     cx                                                      
        mov     Value, bx                                               
    };                                                                  
    ENABLE                                                              
    if( Value == 0x4255)                                                
        npAT->npACB->SeekFlags |= ACBK_BUTTERFLY;                       
}                                                                       
