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

#include <os2.h>
#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <sas.h>
#include "devcmd.h"

#include "iorb.h"
#include "reqpkt.h"
#include "addcalls.h"
#include "dskinit.h"

#include <scsi.h>
#include <cdbscsi.h>
#include <cmd.h>
#include <devhelp.h>
#include <cdb.h>
#include "proto.h"
 #include <string.h>
 #include <memory.h>


 extern PGINFOSEG near PGinfo;
 extern ULONG near DevHlp;
 extern PUCHAR near Read_IOBuffer;
 extern USHORT near IrqNum;
 extern USHORT near DrvBlockSize;
 UCHAR PreviousOpCode,PreviousStatus;
 extern UCHAR near Mode;
 extern UCHAR  near wDrvVer[4];


      TOCINFO TOCInfoArea[101] = { {0,},};
      UCHAR  bPlayFlag = FALSE ;
      UCHAR  bPauseFlag = FALSE ;
      UCHAR  bChangeFlag ;
      UCHAR  bInfoFlag = 0 ;
      UCHAR  bMedChgFlag = CHANGED ;
      UCHAR  bMedChkFlag = FALSE ;
      UCHAR  bMedChkCalled = FALSE ;
      UCHAR  bStatus =0;
      USHORT  bStatusFlag=0;
      extern UCHAR near bCFlag, near last_data_byte;
      UCHAR TOC_Cache=0;
      UCHAR  bMinTno=0, bMaxTno =0,bChanged=0;
      ULONG  ulVSSize =0;
      ULONG  ulLeadOut =0;
      UCHAR  CD_ID=0;
      USHORT wBlkLen =0;
      ULONG  Specialbuffer=NULL;

BOOL GetTocInfo()
{
USHORT i=0,state,First_Track_in_Session,Last_Track_in_Session,count,Status,q,Delta=0;
CDROMSTAT Data ;
BOOL Flag=FALSE,SeekRunning,ModeSetState;
ULONG Clock=0L,Addr;
UCHAR m,s,f;
#define NoneFound  0
#define FirstFound 1
#define LastFound  2
#define AllFound   3
#define MaxFound   7
#define LowTrack  0xa0
#define HighTrack 0xa1
#define BridgeTrk 0xb0
#define MaxTrack 0x99

        CD_ID=0;
        Addr=MinFrame;

        newloop:;

        SeekRunning=FALSE;
        ModeSetState=FALSE;

        for(i=0; i<5; i++)
          {
          DPRINTF(("Seeking to %08lX\n",Addr));
          bStatusFlag=DriveCommand(Seek,Addr,NULL,NULL,(USHORT)NULL);
          DPRINTF(("Seeking to %08lX status=%04X\n",Addr,bStatusFlag));
          if( !COMMANDCHECK(bStatusFlag) && !TIMEOUT(bStatusFlag))
            {
            SeekRunning=TRUE;
            break;
            } /* end if */
          else
            {
            DUMMYWAIT;
            if(i==3)
              {
              bStatusFlag=DriveCommand(Stop,NULL,NULL,NULL,(USHORT)NULL);
              bStatusFlag=DriveCommand(SetDriveMode,(ULONG)MuteData,NULL,NULL,(USHORT)NULL);
              goto newloop;
              } /* end if */
            }
          } /* end for */

        if(SeekRunning)
          {
          for(i=0; i<8; i++)
            {
            DPRINTF(("Setting Drive mode\n"));
            bStatusFlag=DriveCommand(SetDriveMode,(ULONG)(MuteData|TocData),NULL,NULL,(USHORT)NULL);
            if(!COMMANDCHECK(bStatusFlag))
              {
              ModeSetState=TRUE;
              break;
              } /* end if */
            } /* end for */
          }

        DUMMYWAIT;

        if(SeekRunning)
          {
          DPRINTF(("Starting Track search for tracks %d thru %d\n", bMinTno+Delta,bMaxTno));
          for(state=NoneFound,count=400,i=bMinTno+Delta;i<=bMaxTno && count ; count--)
            {
            DUMMYWAIT;
            while(TIMEOUT(bStatusFlag=(UCHAR)(Status=DriveCommand(ReadSubQ,NULL,NULL,&Data,sizeof(Data.TrackInfo)))))
              DUMMYWAIT;
            DPRINTF(("SubqLoop status=%04X\n",bStatusFlag));
            if(!TIMEOUT(Status))
              {
              if(!COMMANDCHECK(bStatusFlag) && !DISCCHANGE(bStatusFlag) && DISCIN(bStatusFlag) && !READERROR(bStatusFlag))
                {
                DPRINTF((" Q Status = %04X\n", bStatusFlag));
                DPRINTF(("Track Q info TNO = %02X, AMin= %02X, Index = %02X ADR=%02X\n",
                                Data.TrackInfo.TNO,
                                BCD2Bin(Data.TrackInfo.AMin),
                                Data.TrackInfo.Index,
                                Data.TrackInfo.S.ADR));
                if(Data.TrackInfo.TNO==0)
                  {
                  if(state!=MaxFound)
                    {
                    if(state!=AllFound)
                      {
                      if(Data.TrackInfo.Index==LowTrack)
                        {
                        if(!(state & FirstFound))
                          {
                          if(BCD2Bin(Data.TrackInfo.AMin)==i)
                            {
                            DPRINTF(("firsttrack index found = %d\n",BCD2Bin(Data.TrackInfo.AMin)));
                            First_Track_in_Session=BCD2Bin(Data.TrackInfo.AMin);
                            DPRINTF(("First=%d\n",First_Track_in_Session));
                            state |= FirstFound;
                            } /* end if */
                          } /* end if */
                        } /* end if */
                      else if(Data.TrackInfo.Index==HighTrack)
                        {
                        if(!(state & LastFound))
                          {
                          DPRINTF(("lasttrack index found = %d\n",BCD2Bin(Data.TrackInfo.AMin)));
                          Last_Track_in_Session=BCD2Bin(Data.TrackInfo.AMin);
                          DPRINTF(("Last=%d\n",Last_Track_in_Session));
                          state |= LastFound;
                          } /* end if */
                        } /* end else */
                      } /* end if */
                    else
                      {
                      if(Data.TrackInfo.Index<=MaxTrack)
                        {
                        if(i==BCD2Bin(Data.TrackInfo.Index))
                          {
                          DPRINTF(("Saving track info for Track %d\n",BCD2Bin(Data.TrackInfo.Index)));
                          TOCInfoArea[BCD2Bin(Data.TrackInfo.Index)].Control=Data.TrackInfo.S.Control;
                          TOCInfoArea[BCD2Bin(Data.TrackInfo.Index)].ADR=Data.TrackInfo.S.ADR;
                          TOCInfoArea[BCD2Bin(Data.TrackInfo.Index)].min=BCD2Bin(Data.TrackInfo.AMin);
                          TOCInfoArea[BCD2Bin(Data.TrackInfo.Index)].sec=BCD2Bin(Data.TrackInfo.ASec);
                          TOCInfoArea[BCD2Bin(Data.TrackInfo.Index)].frame=BCD2Bin(Data.TrackInfo.AFrame);
                          TOCInfoArea[BCD2Bin(Data.TrackInfo.Index)].start= MAKEULONG( MAKEUSHORT( BCD2Bin(Data.TrackInfo.AFrame),
                                                                                             BCD2Bin(Data.TrackInfo.ASec ) ),
                                                                                       MAKEUSHORT( BCD2Bin(Data.TrackInfo.AMin),
                                                                                                   0 ) ) ;
                          DevHelp_RAS( 184 ,i , sizeof(TOCINFO), &TOCInfoArea[i]);
                          if(i==Last_Track_in_Session)                   // if last track in this session
                            {                           // go for more
                            state=MaxFound;
                            DPRINTF(("Max Track in session found\n"));
                            Delta=i;
                            } /* end if */
                          i++;
                          } /* end if */
                        } /* end if */
                      } /* end else */
                    } /* end if */
                  else if(Data.TrackInfo.Index==BridgeTrk)
                    {
                    f =BCD2Bin(Data.TrackInfo.Frame);
                    s =BCD2Bin(Data.TrackInfo.Sec);
                    m =BCD2Bin(Data.TrackInfo.Min);
                    s +=2;          // go two seconds into new area
                    if(s>=60)       // if new seconds is >one minute would allow
                      {
                      m++;          // adjust minutes
                      s-=60;        // adjust downward seconds
                      } /* end if */
                    Addr=MAKEULONG( MAKEUSHORT(f,s ),
                                    MAKEUSHORT(m,0));
                    DPRINTF(("Bridge track found = %08lX\n",Addr));
                    while(TIMEOUT(DriveCommand(SetDriveMode,(ULONG)MuteData,NULL,NULL,(USHORT)NULL)));
                    DUMMYWAIT;
                    DUMMYWAIT;
                    DUMMYWAIT;
                    DUMMYWAIT;
                    DUMMYWAIT;
                    DUMMYWAIT;
                    DUMMYWAIT;
                    DUMMYWAIT;
                    DPRINTF(("Seeking to last session now for tracks %d thru %d status=%04X\n",i,bMaxTno,bStatusFlag));
                    goto newloop;
                    } /* end if */
                  } /* end if */
                } /* end if */
              else
                {
                DevHelp_RAS( 183 ,0xaa , sizeof(PGinfo->msecs), &(PGinfo->msecs));
                DPRINTF(("Aborting track search Status=%04X\n",bStatusFlag));
                break;
                } /* end else */
              } /* end if */
            else
              {
              DevHelp_RAS( 183 ,0xab , sizeof(PGinfo->msecs), &(PGinfo->msecs));
              DPRINTF(("Aborting track search timeout Status=%04X\n",bStatusFlag));
              break;
              }
            } /* end for */
          }
        bStatusFlag=DriveCommand(SetDriveMode,(ULONG)MuteData,NULL,NULL,(USHORT)NULL);
//      bStatusFlag=DriveCommand(Stop,0,NULL,NULL,NULL);
        if(count==0 && i<bMaxTno)
          {
          goto newloop;
          } /* end if */
        if(state==MaxFound)
          {
          DevHelp_RAS( 184 ,0xaa , sizeof(PGinfo->msecs), &(PGinfo->msecs));
          Flag=TRUE;
          } /* end if */
        return Flag;
}

BOOL CacheTOC(PCDROMSTAT Data,UCHAR checksum)
{
BOOL Flag=TRUE;
        DPRINTF(("Caching TOC\n"));
        bInfoFlag &= CLR_VS;                    // clear the flag just to make sure
        TOC_Cache=0;                            // clear toc indicator, just in case
        ulVSSize = AdrRed2Hsg(MAKEULONG( MAKEUSHORT( BCD2Bin(Data->TOC.LeadoutFrame),
                                         BCD2Bin(Data->TOC.LeadoutSec ) ),
                             MAKEUSHORT( BCD2Bin(Data->TOC.LeadoutMin),
                                         0 ) ) )-1;
        bMinTno   =BCD2Bin(Data->TOC.FirstTrack);
        bMaxTno   =BCD2Bin(Data->TOC.LastTrack);
        DPRINTF(("FirstTrack=%d LastTrack=%d\n",bMinTno,bMaxTno));
        TOCInfoArea[0].min=BCD2Bin(Data->TOC.LeadoutMin);
        TOCInfoArea[0].sec=BCD2Bin(Data->TOC.LeadoutSec );
        TOCInfoArea[0].frame=BCD2Bin(Data->TOC.LeadoutFrame);
        TOCInfoArea[0].ADR =0;
        TOCInfoArea[0].Control=AUDIODISC(bStatusFlag) ? TCI_AUDIO : TCI_DATA;
        DPRINTF((" Low & high track = %d %d status=%02X\n",bMinTno,bMaxTno,bStatusFlag));
        ulLeadOut = MAKEULONG( MAKEUSHORT( BCD2Bin(Data->TOC.LeadoutFrame),
                                         BCD2Bin(Data->TOC.LeadoutSec ) ),
                             MAKEUSHORT( BCD2Bin(Data->TOC.LeadoutMin),
                                         0 ) ) ;

        TOCInfoArea[bMaxTno+1].start=ulLeadOut;     // start after last track, used in read SRD
        TOCInfoArea[bMaxTno+1].Control=TCI_DATA;    // start after last track, used in read SRD
        DPRINTF(("Getting TrackInfo\n"));
        SetDataMode(SuspendData);
        if(GetTocInfo())
          {
          DPRINTF(("TrackInfo completed ok\n"));
          bInfoFlag |= SET_VS ;
          TOC_Cache=checksum;
          Flag=FALSE;
          if(TOCInfoArea[bMinTno].Control & TCI_DATA)
            {
            while(TIMEOUT(DriveCommand( DriveConfig,MODEFLAG ,0, 0, 0)))
              DUMMYWAIT;
            while(TIMEOUT(DriveCommand( DriveConfig,IRQFLAG ,IrqNum?(PreIrq|ErrIrq):0, 0, 0)))
              DUMMYWAIT;
            if (wDrvVer[0]=='D')
              {
              while(TIMEOUT(DriveCommand( DriveConfig,RETRYCOUNT ,3, 0, 0)))
                DUMMYWAIT;
              }
            while(TIMEOUT(DriveCommand( SetDriveMode,MuteData ,0, 0, 0)))
              DUMMYWAIT;
            #ifndef TEST
            #ifndef DEBUG
            TryIrq(FALSE);
            SpecialRead();
            #endif
            #endif
            } /* end if */
          }
        else
          {
          DPRINTF(("TrackInfo failed\n"));
          }
     return Flag;
}

BOOL ReadyHardware( PIORBH pIORB, USHORT OpCode )
{
      USHORT CmdCode = pIORB->CommandCode ;
      USHORT SubCmd  = pIORB->CommandModifier ;
      CDROMSTAT Data ;
      UCHAR i,temp ;
      PUCHAR t;

      for(i=1; i<15; i++)
        {
        bStatusFlag=DriveStatus(Spincount);       // get drives status

        if(COMMANDCHECK(bStatusFlag) || TIMEOUT(bStatusFlag) ||
          ((PreviousOpCode==SCSI_MODE_SELECT) && (LOBYTE(bStatusFlag)==0) && (PreviousStatus!=0))
          )
          {
          DevHelp_RAS( 160 ,OpCode<<8 | bStatusFlag , sizeof(PGinfo->msecs), &(PGinfo->msecs));
          DUMMYWAIT;
          } /* end if */
        else
          {
          break;
          } /* end else */
        } /* end for */
      PreviousOpCode=OpCode;
      PreviousStatus=LOBYTE(bStatusFlag);
      DevHelp_RAS( 160 ,OpCode<<8 | bStatusFlag , sizeof(PGinfo->msecs), &(PGinfo->msecs));
      if(!COMMANDCHECK(bStatusFlag))            // no command error
        {
        if(DISCIN(bStatusFlag))                 // is there a disc in the drive?
          {                                     // calculate checksum of toc data, should be only one match possible
                                                // get TOC
          if(!(bInfoFlag & SET_VS))
            {
            bPlayFlag   = bPauseFlag = FALSE ;
            bStatusFlag=DriveCommand(ReadToc,NULL,NULL,&Data,sizeof(Data.TOC));
            if(!COMMANDCHECK(bStatusFlag) && !READERROR(bStatusFlag) && (Data.TOC.LastTrack<MaxTrack))
              {
              DevHelp_RAS( 184 ,0 , sizeof(Data.TOC), &Data);
              DPRINTF(("Disk in drive\n"));
              for(i=1,t=(PUCHAR)&Data,temp=*t,t++;i<sizeof(Data.TOC) ; i++,t++)
                {
                temp^=*t;
                } /* end for */
              DPRINTF(("Checksum=%02X==Saved=%02X\n",temp,TOC_Cache));
//            if(temp==TOC_Cache)                   // does the checksum matched the saved one?
//              {
//              DPRINTF(("Checksums match\n"));
//              bInfoFlag |= SET_VS ;               // same disk as last time
//              } /* end if */
//            else
//              {
                DPRINTF(("Checksums do NOT match\n"));
                IORB_CmdErr(IOERR_MEDIA_CHANGED, pIORB,SCSI_SK_UNITATTN,ASC_MEDIUM_CHANGED);
                if(CacheTOC(&Data,temp))            // did caching go ok?
                  {
                  DPRINTF(("Caching failed?\n"));
                  switch (OpCode)                   // no failures for some non-disk dependant opcodes
                    {
                    case SCSI_READ_CAPACITY:
                    case SCSI_MODE_SELECT:
                    case SCSI_MODE_SENSE:
                       break;
                    default:
                       IORB_CmdErr(IOERR_UNIT_NOT_READY, pIORB,SCSI_SK_NOTRDY,0);
                       DevHelp_RAS( 162 ,OpCode<<8 | bStatusFlag , sizeof(PGinfo->msecs), &(PGinfo->msecs));
                       return TRUE;
                       break;
                    } /* endswitch */
                  } /* end if */
//              } /* end else */
              } /* end if */
            else
              {
              DPRINTF(("TOC failed status=%02X\n",bStatusFlag));
              DevHelp_RAS( 183 ,0xaa , sizeof(PGinfo->msecs), &(PGinfo->msecs));
              IORB_CmdErr(IOERR_UNIT_NOT_READY, pIORB,SCSI_SK_NOTRDY,0);
              DevHelp_RAS( 162 ,OpCode<<8 | bStatusFlag , sizeof(PGinfo->msecs), &(PGinfo->msecs));
              return TRUE;
              } /* end else */
            } /* end if */
          } /* end if */
        else
          {
          DPRINTF(("NO Disk in drive\n"));
          bPlayFlag   = bPauseFlag = FALSE ;
          switch (OpCode)                       // no failures for some non-disk dependant opcodes
            {
            case SCSI_MODE_SELECT:
            case SCSI_MODE_SENSE:
            case SCSI_LOCK_UNLOCK:
            case SCSI_START_STOP_UNIT:
            case ADD_READ_DISK_INFO:           //rme - fix for photo CD
               break;
            default:
               IORB_CmdErr(IOERR_MEDIA_NOT_PRESENT, pIORB,SCSI_SK_NOTRDY,0);
               DevHelp_RAS( 162 ,OpCode<<8 | bStatusFlag ,sizeof(PGinfo->msecs), &(PGinfo->msecs) );
               return TRUE;
               break;
            } /* endswitch */
          } /* end else */
        } /* end if */
      else                                      // no disc present
        {
        DPRINTF(("Drive Status Command failed %02X\n",bStatusFlag));
        switch (OpCode)                         // no failures for some non-disk dependant opcodes
          {
          case SCSI_READ_CAPACITY:
          case SCSI_MODE_SELECT:
          case SCSI_MODE_SENSE:
             break;
          default:
             IORB_CmdErr(IOERR_UNIT_NOT_READY, pIORB,SCSI_SK_NOTRDY,0);
             DevHelp_RAS( 162 ,OpCode<<8 | bStatusFlag , sizeof(PGinfo->msecs), &(PGinfo->msecs));
             return TRUE;
             break;
          } /* endswitch */
        } /* end else */

      return FALSE;
}

void SpecialRead( void)
{
USHORT Status,i;
CDROMSTAT c;
UCHAR CurrentMode=Mode1;
#define SpecialAddr 0x00000210L
PRawSector SpecialBuffer;

        do
          {
          DUMMYWAIT;
          Status=DriveCommand( ModeSet,Mode1 ,0, 0, 0);          // set mode 1 first
          Mode = Mode1;
          } while (TIMEOUT(Status)); /* end do */
        DriveCommand( SetDriveMode,DataLength|EccBit|MuteData ,0, 0, 0);
        if(!Specialbuffer)
          {
#if defined(DEBUG) | defined(TEST)
          Specialbuffer=malloc(sizeof(RawSector));
#else
          DevHelp_AllocPhys(sizeof(RawSector),0, (PULONG)&Specialbuffer);
#endif
          } /* end if */
        if(Specialbuffer)
          {
#if defined(DEBUG) | defined(TEST)
          SpecialBuffer=(PRawSector)Specialbuffer;
#else
          DevHelp_PhysToGDT((ULONG)Specialbuffer, sizeof(RawSector), FP_SEG(Read_IOBuffer));
          SpecialBuffer=(PRawSector)Read_IOBuffer;
#endif
          for(i=0; i<2; i++)
            {
            Status=DriveCommand( Read, SpecialAddr, 1,(PCDROMSTAT)SpecialBuffer ,sizeof(RawSector));   // read
            if(!READERROR(Status) && !COMMANDCHECK(Status) && !TIMEOUT(Status))
              {
              if(Mode==Mode2)
                {
                DriveCommand( SetDriveMode,TestMode|MuteData ,0, 0, 0);
                while(TIMEOUT(DriveCommand( DriveConfig,BLOCKSIZE ,DrvBlockSize, 0, 0)))
                  DUMMYWAIT;
                break;
                } /* end if */
              else
                {
                Status=DriveCommand( ModeSet,Mode1 ,0, 0, 0);  // try mode 2 now
                DriveCommand( SetDriveMode,MuteData ,0, 0, 0);
                break;
                } /* end else */
              } /* end if */
            else
              {
              Mode=Mode2;
              Status=DriveCommand( ModeSet,Mode2 ,0, 0, 0);  // try mode 2 now
              } /* end else */
            } /* end for */
          } /* end if */
}
