/***************************************************************************/
//   Example of using the external disk interface.
//
//   Tested with Borland C/C++ 5.02 on a SC12 with Bios version 1.01 Beta
//
//   Copyright Beck IPC GmbH
//
//   Info: http://www.bcl-online.de
//
//   $Header: ide.c, 4, 05.09.2001 11:53:54, Andre Pribil$
//
//   $Log:
//    2    @Chip-DOS 1.1         09.01.01 14:31:30    Andre Pribil    First example
//         on customer board (ALE, PCS5, PCS6)
//    1    @Chip-DOS 1.0         20.12.00 12:15:10    Andre Pribil
//   $
/***************************************************************************/

/***************************************************************************/
// Includes
/***************************************************************************/
#include "define.h"
#include <dos.h>
#include <stdio.h>
#include "ide.h"
#include "rtxapi.h"
#include "rtos.h"
#include "hwapi.h"

/***************************************************************************/
// Defines
/***************************************************************************/
#define IDE_IDENTIFY      (uchar)0xEC
#define IDE_READ_SECTOR   (uchar)0x20
#define IDE_WRITE_SECTOR  (uchar)0x30
#define IDE_VERIFY_SECTOR (uchar)0x40
#define IDE_SEEK          (uchar)0x70
#define IDE_SET_FEATURES  (uchar)0xEF
#define IDE_STANDBY       (uchar)0xE2
#define IDE_IDLE_MODE     (uchar)0xE3
#define IDE_SET_DRV_PARAM (uchar)0x91
#define IDE_RECALIBRATE   (uchar)0x10

#define IDE_DATA       (IDE_PORT0 + 0)  // Data (8/16 Bit)  r/w
#define IDE_FEATURE    (IDE_PORT0 + 1)  // Features         w
#define IDE_ERROR      (IDE_PORT0 + 1)  // Error            r
#define IDE_COUNT      (IDE_PORT0 + 2)  // Sector Count     r/w
#define IDE_SECTOR     (IDE_PORT0 + 3)  // Sector Number    r/w
#define IDE_CYL_LO     (IDE_PORT0 + 4)  // Cylinder low     r/w
#define IDE_CYL_HI     (IDE_PORT0 + 5)  // Cylinder high    r/w
#define IDE_HEAD       (IDE_PORT0 + 6)  // Drive/Head       r/w
#define IDE_COMMAND    (IDE_PORT0 + 7)  // Command          w
#define IDE_STATUS     (IDE_PORT0 + 7)  // Status           r

#define IDE_CONTROL    (IDE_PORT1 + 6)  // Device Control   w
#define IDE_ALT_STS    (IDE_PORT1 + 6)  // Alternate Status r
#define IDE_ADDRESS    (IDE_PORT1 + 7)  // Drive Address    r

////////////////////////////////////////////////////////
// IDE State-Bits
////////////////////////////////////////////////////////

#define IDE_STS_BSY    (uchar)0x80
#define IDE_STS_DRDY   (uchar)0x40
#define IDE_STS_DWF    (uchar)0x20
#define IDE_STS_DSC    (uchar)0x10
#define IDE_STS_DRQ    (uchar)0x08
#define IDE_STS_CORR   (uchar)0x04
#define IDE_STS_ERR    (uchar)0x01

////////////////////////////////////////////////////////
// IDE Error-Bits
////////////////////////////////////////////////////////

#define IDE_ERR_BBK    (uchar)0x80
#define IDE_ERR_UNC    (uchar)0x40
#define IDE_ERR_TMOUT  (uchar)0x20
#define IDE_ERR_IDNF   (uchar)0x10
#define IDE_ERR_ABRT   (uchar)0x04
#define IDE_ERR_TK0NF  (uchar)0x02
#define IDE_ERR_AMNF   (uchar)0x01

/***************************************************************************/
// Typedefs
/***************************************************************************/

typedef unsigned char uchar;
typedef unsigned int  uint;

/***************************************************************************/
// Variables
/***************************************************************************/

#if DATABUS == 16
	uchar FEATURE = 0x81; // disable 8 Bit Mode
#elif DATABUS == 8
	uchar FEATURE = 0x01; // enable 8 Bit Mode
#endif

uchar err,sts;
T_DRIVE_PARAM drive = {0};   // filled by IDE_identify

/***************************************************************************/
// Prototypes
/***************************************************************************/

static uchar near IDE_waitirq( void );
static uchar near IDE_waitdrq( void );
static uchar near IDE_datarequest( void );
static uchar near IDE_error( void );
static uchar near IDE_status( void );

/***************************************************************************/
// Functions
/***************************************************************************/

////////////////////////////////////////////////////////
// wait for DRQ Bit: Ready for data
////////////////////////////////////////////////////////
static uchar near IDE_waitdrq( void ) // ret 0 if not ready
{
  if ( IDE_waitirq() ) return IDE_datarequest();
  return 0;
} // IDE_waitdrq


////////////////////////////////////////////////////////
// Test DRQ-Bit
////////////////////////////////////////////////////////
static uchar near IDE_datarequest( void )
{
  return IDE_status() & IDE_STS_DRQ;
} // IDE_datarequest


////////////////////////////////////////////////////////
// Wait until BUSY-Bit is cleared
////////////////////////////////////////////////////////
static uchar near IDE_waitirq( void )  // ret 0 on error
{
  unsigned long t0, t1;

  RTX_Get_System_Ticks(&t0);

  while((( IDE_status()) & ( IDE_STS_BSY|IDE_STS_DRDY|IDE_STS_DSC )) !=
        ( IDE_STS_DRDY|IDE_STS_DSC ))
  {
    RTX_Get_System_Ticks(&t1);
    if( (t1-t0) > 2000)
    {
      hal_outportb(IDE_FEATURE,IDE_ERR_TMOUT);
      printf("\r\nIDE: Drive IRQ not ready\r\n");
      return 0;
    } // if
  } // while
  return 1;
}


////////////////////////////////////////////////////////
// Send command to disk
////////////////////////////////////////////////////////
static uchar near IDE_command( uchar feat, uint  cyl, uchar head, uchar sect,
                               uchar cnt,  uchar cmd )
{
  IDE_waitdrq();
  hal_outportb(IDE_CONTROL, 8);
  hal_outportb(IDE_FEATURE, feat);
  hal_outportb(IDE_COUNT,   cnt);
  hal_outportb(IDE_SECTOR,  sect);
  hal_outportb(IDE_CYL_LO,  cyl&0xff);
  hal_outportb(IDE_CYL_HI,  cyl>>8);
  hal_outportb(IDE_HEAD,    (head &0x0f)|0xE0);
  hal_outportb(IDE_COMMAND, cmd);
  return cnt;
}


////////////////////////////////////////////////////////
// Send LBA command to disk
////////////////////////////////////////////////////////
static uchar near IDE_LBA_command( uchar feat, unsigned long sector,
                                   uchar cnt,  uchar cmd )
{
  uchar tmp;

  IDE_waitdrq();
  hal_outportb(IDE_CONTROL, 8);
  hal_outportb(IDE_FEATURE, feat);
  hal_outportb(IDE_COUNT,   cnt);
  tmp=(unsigned char)(sector&0xff); hal_outportb(IDE_SECTOR,  tmp);
  sector=sector>>8;
  tmp=(unsigned char)(sector&0xff); hal_outportb(IDE_CYL_LO,  tmp);
  sector=sector>>8;
  tmp=(unsigned char)(sector&0xff); hal_outportb(IDE_CYL_HI,  tmp);
  sector=sector>>8;
  tmp=(unsigned char)(sector&0xff); hal_outportb(IDE_HEAD,  (tmp &0x0f)|0xE0); // LBA bit set, drive 0
  hal_outportb(IDE_COMMAND, cmd);
  return cnt;
}


////////////////////////////////////////////////////////
// Read state register
////////////////////////////////////////////////////////
static uchar near IDE_status( void )
{
	sts = hal_inportb(IDE_STATUS);
	return(sts);
}


////////////////////////////////////////////////////////
// Read buffer
////////////////////////////////////////////////////////
#if DATABUS == 8
 static void near IDE_readbuf(int cnt, unsigned char *buf)
 {
    hal_inportb_block(IDE_DATA,IDE_DATA,buf,cnt);
 }
#elif DATABUS == 16
 static void near IDE_readbuf(int cnt, unsigned char *buf)
 {
    cnt=cnt/2;

    asm les bx, buf
    asm mov cx, cnt

l1:
    asm mov dx, IDE_DATA
    asm in  al, dx
    asm mov es:[bx], al
    asm inc bx
    asm mov dx, IDE_DATA+8
    asm in  al, dx
    asm mov es:[bx], al
    asm inc bx
    asm loop l1
 }
#endif

////////////////////////////////////////////////////////
// Write buffer
////////////////////////////////////////////////////////
#if DATABUS == 8
 static void near IDE_writebuf(int cnt, unsigned char *buf)
 {
    hal_outportb_block(IDE_DATA,IDE_DATA,buf,cnt);
 } // IDE_writebuf
#elif DATABUS == 16
 static void near IDE_writebuf(int cnt, unsigned char *buf)
 {
    cnt=cnt/2;

    asm les bx, buf
    asm mov cx, cnt

 l2:
    asm mov dx, IDE_DATA+8
    asm mov al, es:[bx+1]
    asm out dx, al
    asm mov dx, IDE_DATA
    asm mov al, es:[bx]
    asm out dx, al
    asm inc bx
    asm inc bx
    asm loop l2
 }
#endif

////////////////////////////////////////////////////////
// Test Error-Bit
////////////////////////////////////////////////////////
static uchar near IDE_error( void )
{
  sts = IDE_status();
  if( sts  & IDE_STS_ERR )
  {
    err=hal_inportb(IDE_ERROR);
    printf(" Error %02Xh ",err);
  }
  else err= 0;
  return(( sts & ( IDE_STS_ERR|IDE_STS_DWF )));
} // IDE_error


////////////////////////////////////////////////////////
// Switch to 8-Bit mode
////////////////////////////////////////////////////////
static void IDE_bit_mode( void )
{
  IDE_command( FEATURE,-1,0,0,0,IDE_SET_FEATURES );
  IDE_waitirq();
} // IDE_bit_mode


////////////////////////////////////////////////////////
// Reset Kittyhawk IDE
////////////////////////////////////////////////////////
void IDE_reset( void )
{
  int retries = 1;
  int error;

  do
  {
    #ifdef TRACE
    printf("\r\nIDE software Reset\r\n");
    #endif

    hal_outportb(IDE_CONTROL,0x0E);   // activate software reset
    RTX_Sleep_Time(1);
    hal_outportb(IDE_CONTROL,0x0A);  // remove reset, no IRQ

    error = IDE_status();
    if( error < IDE_STS_BSY )
    {
      printf("\r\nDrive not found, error %02Xh\r\n", error);
      return;
    } // if
    RTX_Sleep_Time(100);

    // Busy abwarten
    if( IDE_waitirq())
    {
      // 8-Bit-Mode restaurieren
      IDE_bit_mode();
      return;
    } // if

    printf("\r\nIDE: Reset failed\r\n");

  } while( retries-- );
} // IDE_reset


////////////////////////////////////////////////////////
// Read sectors in LBA mode / ret 1 if o.k.,  0 if error
////////////////////////////////////////////////////////
int IDE_LBA_read( unsigned long sector, int cnt, unsigned char *buf)
{
  unsigned char huge *b = buf;

  while (cnt)
  {
    #ifdef TRACE
    printf("\r\nreading sector %lu  cnt=%d SEG:OFF=%p,%p",sector,cnt,b,buf);
    #endif

    IDE_LBA_command( 0, sector,1,IDE_READ_SECTOR );
    if( IDE_waitdrq() )
    {
      if( IDE_error())
      {
        printf("\r\nIDE: Error on READ_SECTOR command");
        return 0;      // Lesefehler
      } // if
      IDE_readbuf( 512, (unsigned char *) b );
    }
    else
    {
      printf("FAILED: DRQ TIMEOUT on LBA read\r\n");
      return 0;      // Lesefehler
    } // if
    b+=512;
    cnt--;
    sector++;
  }

  return 1;
}


////////////////////////////////////////////////////////
// Write sectors in LBA mode / ret 1 if o.k., 0 if error
////////////////////////////////////////////////////////
int IDE_LBA_write( unsigned long sector, int cnt, unsigned char *buf )
{
  unsigned char huge *b = buf;

  while (cnt)
  {
    #ifdef TRACE
    printf("\r\nwriting sector %lu  cnt=%d SEG:OFF=%p,%p",sector,cnt,b,buf);
    #endif

    IDE_LBA_command( 0, sector,1,IDE_WRITE_SECTOR );
    if (IDE_error())
    {
      printf("\r\nError=0x%X\r\n",IDE_error());
      return 0;
    }
    if( IDE_waitdrq() )
    {
      IDE_writebuf( 512, (unsigned char *) b );
      if(( IDE_waitirq() == 0 ) || IDE_error())
      {
        printf("FAILED (IRQ TIMEOUT)\r\n");
        return 0;      // Schreibfehler
      } // if
    }
    else
    {
      printf("FAILED (DRQ TIMEOUT)\r\n");
      return 0;      // Schreibfehler
    } // if
    b+=512;
    cnt--;
    sector++;
  }

  return 1;
} // IDE_write


////////////////////////////////////////////////////////
// Read n sectors
////////////////////////////////////////////////////////
int IDE_read( int cyl, int head, int sect, int cnt, unsigned char *buf )
{
  unsigned char huge *b = buf;

  cnt = IDE_command( 0, cyl,head,sect,cnt,IDE_READ_SECTOR );
  do
  {
    if( IDE_waitdrq() && ( IDE_error() == 0 ))
    {
      IDE_readbuf( 512, (unsigned char *)b );
      if( IDE_error())
      {
        return 0;      // Lesefehler
      } // if
    }
    else
    {
      printf("FAILED (TIMEOUT)\r\n");
      return 0;      // Lesefehler
    } // if
    b+=512;
    cnt--;
  } while( cnt );

  return 1;
} // IDE_read


////////////////////////////////////////////////////////
// Write n sectors
////////////////////////////////////////////////////////
int IDE_write( int cyl, int head, int sect, int cnt, unsigned char *buf )
{
  unsigned char huge *b = buf;

  cnt = IDE_command( 0, cyl,head,sect,cnt,IDE_WRITE_SECTOR );
  // RTX_Sleep_Time(1);
  do
  {
    if( IDE_waitdrq() && ( IDE_error() == 0 ))
    {
      IDE_writebuf( 512, (unsigned char *) b);
      if(( IDE_waitirq() == 0 ) || IDE_error())
      {
        printf("FAILED (TIMEOUT)\r\n");
        return 0;      // Schreibfehler
      } // if
    }
    else
    {
      printf("FAILED (ERROR)\r\n");
      return 0;      // Schreibfehler
    } // if
    b+=512;
    cnt--;
  } while( cnt );

  return 1;
} // IDE_write


////////////////////////////////////////////////////////
// Read SRAM
////////////////////////////////////////////////////////
int IDE_ReadSRAM(unsigned char *bfr)
{
  hal_outportb(IDE_HEAD,  0xE0);  // set drive 0 and LBA mode
  hal_outportb(IDE_COMMAND, 0xE4);
  if (IDE_waitdrq())
  {
    IDE_readbuf( 512, bfr );
  }
  else
  {
    return 0;
  }
  return 1;
}


////////////////////////////////////////////////////////
// Write SRAM
////////////////////////////////////////////////////////
int IDE_WriteSRAM(unsigned char *bfr)
{
  hal_outportb(IDE_HEAD,  0xE0);  // set drive 0 and LBA mode
  hal_outportb(IDE_COMMAND, 0xE8);
  if (IDE_waitdrq())
  {
    IDE_writebuf( 512, bfr );
  }
  else
  {
    return 0;
  }
  return 1;
}


// int positions
#define ID_CYL    1
#define ID_HEADS  3
#define ID_SECT   6  // Anz. Sektoren/Spur


////////////////////////////////////////////////////////
// Identify disk
////////////////////////////////////////////////////////
int IDE_identify(void)   // ret 1 if o.k.
{
  uint IDbuf[256];

  IDE_reset();
  IDE_bit_mode();

  hal_outportb(IDE_HEAD, 0xE0);  // set drive 0 and LBA mode
  hal_outportb(IDE_COMMAND, IDE_IDENTIFY);

  if( IDE_waitdrq() == 0 )  // wait for data transfer
  {
    return 0;
  }

  if( IDE_datarequest()) // o.k. if 8 bit
  {
    if( IDE_error())
    {
      return 0;
    }
    IDE_readbuf( 256*2 , (unsigned char *)IDbuf );

    drive.id          = IDbuf[0];
    drive.cylinders   = IDbuf[ID_CYL];
    drive.heads       = IDbuf[ID_HEADS];
    drive.sectors     = IDbuf[ID_SECT];
    drive.sector_size = 512;
  }
  else
  {
    // Not 8 Bit
    return 0;
  }

  return 1;
}

// End of file

