/*
 * linux/drivers/scsi/iteraid.c
 *
 * (C) Copyright 2002-2004 ITE Tech, inc.
 *
 * Nov 11, 2002	Mark Lu	file created.
 *
 * ITE IT8212 RAID controller device driver for Linux.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2, or (at your option) any
 * later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * Revision 0.0  2002/12/05	15:12:12  root
 * Empty function bodies; detect() works.
 *
 * Revision 0.1 2002/12/17	19:21:37  root
 * First "dma thing doesn't work" version.
 *
 * Revision 0.3 2002/12/23	17:12:09  root
 * Rewrite the dma routines. Reference the ide-dma.c.
 *
 * Revision 0.4 2002/12/26	10:19:29  root
 * The dma read/write works, using some ways to prove it. But there is a
 * problem about the "unknown partition table". The fdisk is ok, but
 * after writing the created partitions, it still shows the "unknown
 * partition table" and i can't access the created partitions.
 *
 * Revision 0.5 2003/01/07	21:49:30 root
 * The problem of "unknown partition table" has been solved.
 * We must "ENABLE CLUSTERING". There is still a another problem about
 * the SCATTER/GATHER.
 *
 * Revision 0.6 2003/01/10	17:45:32 root
 * The SCATTER/GATHER problem has been solved. Now verify the read/write
 * function and make sure each RAID configurations are workable. If testing
 * is OK, then it will be a version 1.0.....
 *
 * Revision 1.0 2003/01/16	17:45:32 root
 * First release version.
 *
 * FixME 1:
 * In RedHat 7.3, if using SG_ALL, the SCSI will timeout. It looks like
 * an command is requested but the interrupt is not been asserted. So
 * try to add a watchdog timer to monitor the interrupts. But this kind
 * of situration will not happen in Mandrake 9.0 and also when using
 * SG_NONE in RedHat 7.3.
 *
 * FixME 2:
 * Module load problem in RedHat 7.3.
 *
 * Fixed: Compile in the graphic mode (GNOME or KDE) will fix the
 *	  module load problem.
 *
 * Revision 1.1 2003/02/10	10:32:21 root
 * Compile in the graphic mode (GNOME or KDE) will fix the
 * module load problem.
 *
 * Revision 1.2 2003/02/18	14:10:35 root
 * Fix the interrupt service routine for share irq problem.
 *
 * ATAPI support ---> schedule is three weeks. (2003/02/28)
 *
 * Revision 1.3 2003/02/27
 * First relase ATAPI version. But there will be an error if no disc in the
 * CD-ROM. Because the commands like TEST_UNIT_READY and READ_CAPACITY will
 * get the error response. This situration in WINDOWS will be then send the
 * REQUEST SENSE command to the device but in Linux, it will never get
 * REQUEST SENSE command. So can we send by ourself???
 *
 * 2003/03/05	root
 *
 * Note 1:
 * According to "The Linux SCSI Generic (sg) HOWTO", the device will respond
 * with a single byte value call the 'scsi_status'. GOOD is the scsi status
 * indicating everything has gone well. The most common other status is
 * CHECK CONDITION. In this latter case, the SCSI mid layer issues a REQUEST
 * SENSE SCSI command. The response of the REQUEST SENSE is 18 bytes or more
 * in length and is called the "sense buffer". It will indicate why the original
 * command may not have been executed. It is important to realize that a CHECK
 * CONDITION may very in severity from informative (e.g. command needed to be
 * retried before succeeding) to fatal (e.g. 'medium error' which often indicates
 * it is time to replace the disk).
 *
 * Note 2:
 * When using the ATAPI BIOS, we also do not need to set up the timimg in linux
 * driver. But it is necessary to write the timing routine in win system, cause it
 * has a s1, s2, s3 mode and devices wake up from these modes need to be initialized
 * again and do not pass through the BIOS.
 *
 * Note 3:
 * The 48-bit support and AP for RAID in linux will the next job.
 *
 * Revision 1.31 2003/03/14	09:40:35 root
 * Fix the error when no disc is on the CD-ROM and the audio cd is ready to play.
 *
 * 2003/04/08	root
 * The ioctl code sklection is finished. But there is a problem about
 * "Bad address" when copy_from_user() is called.
 *
 * Fixed: Use kmalloc() and kfree() to allocate the buffer instead of automatic
 * variables (use stack). The stack size is limited in kernel space.
 *
 * Revision 1.32 2003/04/14	18:20:23 root
 * Complete the IOCTLs code.
 *
 * The IOCTLs are listed below
 * ===========================
 *
 * (1) ITE_IOC_GET_IDENTIFY_DATA
 *
 *     Return virtual drive 512 bytes identification data.
 *
 * (2) ITE_IOC_GET_PHY_DISK_STATUS
 *
 *     Developer can decide to return 4 physical disk information in
 *     512 bytes (data structure should be defined) or 512 bytes
 *     identification data of the physical disk specified by AP.
 *
 * (3) ITE_IOC_CREATE_DISK_ARRAY
 *
 *     Create a new array and erase (or keep) boot sector.
 *
 * (4) ITE_IOC_REBUILD_START
 *
 *     AP nees to specify target/source drive, starting LBA and length.
 *
 * (5) ITE_IOC_GET_REBUILD_STATUS
 *
 *     Return rebuild percentage or last LBA No.
 *
 * (6) ITE_IOC_RESET_ADAPTER
 *
 *     Reset the controller.
 *
 * Revision 1.33 2003/04/15	11:10:08 root
 * The 48-bit support.
 *
 * Revision 1.34 2003/04/20	13:20:38 root
 * Change some values in iteraid.h, so it will not hang in Red Hat Linux
 * and improve the performance.
 *
 * can_queue: 1 --------------------> can_queue: 124
 * sg_tablesize: SG_NONE -----------> sg_tablesize: 16
 * cmd_per_lun: 128 ----------------> cmd_per_lun: 1
 * use_clustering: ENABLE_CLUSTER --> use_clustering: DISABLE_CLUSTER
 *
 * 2003/04/25	root
 * The code will hang on Gigabyte's motherboard when the sourth bridge is
 * sis 962L and 963.
 *
 * Revision 1.35 2003/04/28	10:06:20 root
 * Fixed: Do not enable interrupt again when send each command in
 * IdeSendCommand() routine.
 *
 * 2003/05/20	root
 * Linux SCSI error handling code should be changed to new one.
 *
 * The shortcomings of the existing code.
 *
 * 1. The old error handling code is an imperfect state machine. It
 *    would occasionally get stuck in loops whereby the bus would be reset
 *    over and over again, where the problem would never be resolved and
 *    control of the machine would never return to the user.
 *
 * Reference the http://www.andante.org/scsi.html
 *
 * The kernel after 2.5 or 2.6 will not use the old error handling codes.
 *
 * In iteraid.h
 *
 * #define ITERAID					\
 *	{						\
 *	 proc_name: "it8212",				\
 *	 proc_info: iteraid_proc_info,			\
 *	 .						\
 *	 .						\
 *	 eh_about_handler: iteraid_about_eh,		\  --> New added
 *	 eh_device_reset_handler: NULL			\  --> New added
 *	 eh_bus_reset_handler: NULL			\  --> New added
 *	 eh_host_reset_handler: iteraid_reset_eh	\  --> New added
 *	 use_new_eh_code: 0 --> 1			\
 *	}
 *
 * 2003/06/23	root	17:30:41
 * TODO: Error code still use the old method.
 *
 * Revision 1.36 2003/06/23	19:52:31 root
 * Fixed: Use the new error handling code.
 *
 * Revision 1.40 2003/07/25	10:00:00 root
 * Released version 1.40 by Mark Lu.
 *
 * Revision 1.41 2003/08/06	13:55:17 root
 * Added support for clean shutdown notification/feature table.
 *
 * Revision 1.42 2003/08/21	11:38:57 root
 * Problem: When linux was installed onto IT8212 controller with two disks,
 *	    configured as RAID 1 (P0S0), the hot swap will hang the system.
 * Solve: Use the AtapiResetController() instead of only IT8212ResetAdapter().
 *
 * Revision 1.43 2003/12/24	23:19:07 root
 * Fixed: Fixed a compile error at line 5815. Just move up the variable
 *	  rebuild_info of type PRAID_REBUILD_INFO with other variables.
 *
 * Revision 1.44 2004/03/16	13:12:35 root
 * Fixed: (1) The crash problem when using "rmmod" to remove the iteraid module.
 *        (2) Support two IT8212 cards or chips.
 *        (3) A bug when accessing the slave disk more than 137G.
 *            Thanks for Martine Purschke kindly help to find this bug and fix it.
 *	  (4) can_queue: 12 --------------------> can_queue: 1
 *	  (5) Change the Transparent(Bypass) mode initial PCI registers setting.
 *	  (6) Change IDE I/O, control and dma base address from USHORT to ULONG,
 *	      so that the non x86 platform, like MIPS, will load the correct address.
 */

#include <linux/module.h>

MODULE_AUTHOR ("ITE Tech,Inc.");
MODULE_DESCRIPTION ("ITE IT8212 RAID Controller Linux Driver");
//MODULE_DESCRIPTION ("ITE IT8211 ATA/ATAPI 133 Controller Linux Driver");

#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/time.h>
#include <linux/proc_fs.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/blk.h>
#include <linux/hdreg.h>
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/reboot.h>
#include <linux/spinlock.h>
#include <linux/fs.h>

#include <asm/errno.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>

#include "sd.h"
#include "scsi.h"
#include "hosts.h"

#include "iteraid.h"

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,10)
MODULE_LICENSE("GPL");
#endif

#define MARK_DEBUG_DUMP_MEM	0	/* 1=Enable dump memory	content	*/
#define	MARK_DEBUG_BYPASS_MODE	0	/* 1=Enable use bypass mode	*/
#define MARK_DUMP_CREATE_INFO	0	/* 1=Dump raid create info	*/
#define MARK_SET_BEST_TRANSFER	0	/* 0=BIOS set best trans mode	*/

#define	PRD_BYTES		8	/* PRD table size		*/
#define PRD_ENTRIES		(PAGE_SIZE / (2 * PRD_BYTES))

struct Scsi_Host * ite_vhost = 0;	/* SCSI virtual host		*/
Scsi_Cmnd *	   it8212_req_last = 0;	/* SRB request list		*/

unsigned int  NumAdapters = 0;		/* Adapters number		*/
PITE_ADAPTER ite_adapters[2];		/* How many adapters support	*/

/************************************************************************
 * Notifier blockto get a notify on system shutdown/halt/reboot.
 ************************************************************************/
static int ite_halt(struct notifier_block *nb, ulong event, void *buf);
struct notifier_block ite_notifier =
{
 ite_halt,
 NULL,
 0
};

static struct semaphore	mimd_entry_mtx;

static spinlock_t queue_request_lock	= SPIN_LOCK_UNLOCKED;
static spinlock_t io_request_lock	= SPIN_LOCK_UNLOCKED;

static int driver_ver = 144;		/* Current driver version	*/
static int ite_major = 0;		/* itedev chardev major number	*/

#define ITE_DEBUG

/************************************************************************
 * The File Operations structure for the ioctl interface of the driver.
 ************************************************************************/
static struct file_operations itedev_fops =
{
 ioctl:itedev_ioctl_entry,
 open:itedev_open,
 release:itedev_close,
};

#if (MARK_DEBUG_DUMP_MEM)
/************************************************************************
 * Dump buffer.
 ************************************************************************/
static void
HexDump(unsigned char *buf, int length)
{

 unsigned int		i = 0;
 unsigned int		j = 0;

 printk("\n");
 for (i = 0; i < length; i += 16)
    {
     printk("%04X    ", i);
     for (j = i; (j < i + 8) && (j < length); j++)
	{
	 printk(" %02X", buf[j]);
	}
     if (j == i + 8) printk("-");
     for (j = i + 8; (j < i + 16) && (j < length); j++)
	{
	 printk("%02X ", buf[j]);
	}
     printk("\n");
    }

} /* end HexDump */
#endif

/************************************************************************
 * This routine maps ATAPI and IDE errors to specific SRB statuses.
 ************************************************************************/
UCHAR
MapError(PChannel pChan, PSCSI_REQUEST_BLOCK Srb)
{

 UCHAR			errorByte;
 UCHAR			srbStatus;
 UCHAR			scsiStatus;

 /*
  * Read the error register.
  */
 errorByte = inb(pChan->io_ports[IDE_ERROR_OFFSET]);
 printk("MapError: error register is %x\n", errorByte);

 /*
  * If this is ATAPI error.
  */
 if (pChan->DeviceFlags[Srb->TargetId & 1] & DFLAGS_ATAPI_DEVICE)
    {
     switch (errorByte >> 4)
	{
	 case SCSI_SENSE_NO_SENSE:
	     printk("ATAPI: no sense information\n");
	     scsiStatus = SCSISTAT_CHECK_CONDITION;
	     srbStatus  = SRB_STATUS_ERROR;
	     break;

	 case SCSI_SENSE_RECOVERED_ERROR:
	     printk("ATAPI: recovered error\n");
	     scsiStatus = 0;
	     srbStatus  = SRB_STATUS_SUCCESS;
	     break;

	 case SCSI_SENSE_NOT_READY:
	     printk("ATAPI: device not ready\n");
	     scsiStatus = SCSISTAT_CHECK_CONDITION;
	     srbStatus  = SRB_STATUS_ERROR;
	     break;

	 case SCSI_SENSE_MEDIUM_ERROR:
	     printk("ATAPI: media error\n");
	     scsiStatus = SCSISTAT_CHECK_CONDITION;
	     srbStatus  = SRB_STATUS_ERROR;
	     break;

	 case SCSI_SENSE_HARDWARE_ERROR:
	     printk("ATAPI: hardware error\n");
	     scsiStatus = SCSISTAT_CHECK_CONDITION;
	     srbStatus  = SRB_STATUS_ERROR;
	     break;

	 case SCSI_SENSE_ILLEGAL_REQUEST:
	     printk("ATAPI: illegal request\n");
	     scsiStatus = SCSISTAT_CHECK_CONDITION;
	     srbStatus  = SRB_STATUS_ERROR;
	     break;

	 case SCSI_SENSE_UNIT_ATTENTION:
	     printk("ATAPI: unit attention\n");
	     scsiStatus = SCSISTAT_CHECK_CONDITION;
	     srbStatus  = SRB_STATUS_ERROR;
	     break;

	 case SCSI_SENSE_DATA_PROTECT:
	     printk("ATAPI: data protect\n");
	     scsiStatus = SCSISTAT_CHECK_CONDITION;
	     srbStatus  = SRB_STATUS_ERROR;
	     break;

	 case SCSI_SENSE_BLANK_CHECK:
	     printk("ATAPI: blank check\n");
	     scsiStatus = SCSISTAT_CHECK_CONDITION;
	     srbStatus  = SRB_STATUS_ERROR;
	     break;

	 case SCSI_SENSE_ABORTED_COMMAND:
	     printk("ATAPI: command Aborted\n");
	     scsiStatus = SCSISTAT_CHECK_CONDITION;
	     srbStatus  = SRB_STATUS_ERROR;
	     break;

	 default:
	     printk("ATAPI: invalid sense information\n");
	     scsiStatus = 0;
	     srbStatus  = SRB_STATUS_ERROR;
	     break;
	}
    }
 /*
  * If this is IDE error.
  */
 else
    {
     scsiStatus = 0;
     srbStatus  = SRB_STATUS_ERROR;

     /*
      * Save errorByte, to be used by SCSIOP_REQUEST_SENSE.
      */
     pChan->ReturningMediaStatus = errorByte;

     if (errorByte & IDE_ERROR_MEDIA_CHANGE_REQ)
	{
	 printk("IDE: media change\n");
	 scsiStatus = SCSISTAT_CHECK_CONDITION;
	 srbStatus  = SRB_STATUS_ERROR;
	}
     else if (errorByte & IDE_ERROR_COMMAND_ABORTED)
	{
	 printk("IDE: command abort\n");
	 srbStatus  = SRB_STATUS_ABORTED;
	 scsiStatus = SCSISTAT_CHECK_CONDITION;

	 if (Srb->SenseInfoBuffer)
	    {
	     PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer;

	     senseBuffer->ErrorCode = 0x70;
	     senseBuffer->Valid = 1;
	     senseBuffer->AdditionalSenseLength = 0xB;
	     senseBuffer->SenseKey = SCSI_SENSE_ABORTED_COMMAND;
	     senseBuffer->AdditionalSenseCode = 0;
	     senseBuffer->AdditionalSenseCodeQualifier = 0;

	     srbStatus |= SRB_STATUS_AUTOSENSE_VALID;
	    }
	 /*
	  * pChan->ErrorCount++;
	  */
	}
     else if (errorByte & IDE_ERROR_END_OF_MEDIA)
	{
	 printk("IDE: end of media\n");

	 scsiStatus = SCSISTAT_CHECK_CONDITION;
	 srbStatus = SRB_STATUS_ERROR;

	 if (!(pChan->DeviceFlags[Srb->TargetId & 1] & DFLAGS_MEDIA_STATUS_ENABLED))
	    {
	     /*
	      * pChan->ErrorCount++;
	      */
	    }
	}
     else if (errorByte & IDE_ERROR_ILLEGAL_LENGTH)
	{
	 printk("IDE: illegal length\n");
	 srbStatus = SRB_STATUS_INVALID_REQUEST;
	}
     else if (errorByte & IDE_ERROR_BAD_BLOCK)
	{
	 printk("IDE: bad block\n");

	 srbStatus = SRB_STATUS_ERROR;
	 scsiStatus = SCSISTAT_CHECK_CONDITION;

	 if (Srb->SenseInfoBuffer)
	    {
	     PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer;

	     senseBuffer->ErrorCode = 0x70;
	     senseBuffer->Valid = 1;
	     senseBuffer->AdditionalSenseLength = 0xB;
	     senseBuffer->SenseKey = SCSI_SENSE_MEDIUM_ERROR;
	     senseBuffer->AdditionalSenseCode = 0;
	     senseBuffer->AdditionalSenseCodeQualifier = 0;

	     srbStatus |= SRB_STATUS_AUTOSENSE_VALID;
	    }
	}
     else if (errorByte & IDE_ERROR_ID_NOT_FOUND)
	{
	 printk("IDE: id not found\n");

	 srbStatus = SRB_STATUS_ERROR;
	 scsiStatus = SCSISTAT_CHECK_CONDITION;

	 if (Srb->SenseInfoBuffer)
	    {
	     PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer;

	     senseBuffer->ErrorCode = 0x70;
	     senseBuffer->Valid = 1;
	     senseBuffer->AdditionalSenseLength = 0xb;
	     senseBuffer->SenseKey = SCSI_SENSE_MEDIUM_ERROR;
	     senseBuffer->AdditionalSenseCode = 0;
	     senseBuffer->AdditionalSenseCodeQualifier = 0;

	     srbStatus |= SRB_STATUS_AUTOSENSE_VALID;
	    }
	 /*
	  * pChan->ErrorCount++;
	  */
	}
     else if (errorByte & IDE_ERROR_MEDIA_CHANGE)
	{
	 printk("IDE: media change\n");

	 scsiStatus = SCSISTAT_CHECK_CONDITION;
	 srbStatus = SRB_STATUS_ERROR;

	 if (Srb->SenseInfoBuffer)
	    {
	     PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer;

	     senseBuffer->ErrorCode = 0x70;
	     senseBuffer->Valid = 1;
	     senseBuffer->AdditionalSenseLength = 0xb;
	     senseBuffer->SenseKey = SCSI_SENSE_UNIT_ATTENTION;
	     senseBuffer->AdditionalSenseCode = SCSI_ADSENSE_MEDIUM_CHANGED;
	     senseBuffer->AdditionalSenseCodeQualifier = 0;

	     srbStatus |= SRB_STATUS_AUTOSENSE_VALID;
	    }
	}
     else if (errorByte & IDE_ERROR_DATA_ERROR)
	{
	 printk("IDE: data error\n");

	 scsiStatus = SCSISTAT_CHECK_CONDITION;
	 srbStatus = SRB_STATUS_ERROR;

	 if (!(pChan->DeviceFlags[Srb->TargetId & 1] & DFLAGS_MEDIA_STATUS_ENABLED))
	    {
	     /*
	      * pChan->ErrorCount++;
	      */
	    }

	 /*
	  * Build sense buffer.
	  */
	 if (Srb->SenseInfoBuffer)
	    {
	     PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer;

	     senseBuffer->ErrorCode = 0x70;
	     senseBuffer->Valid = 1;
	     senseBuffer->AdditionalSenseLength = 0xB;
	     senseBuffer->SenseKey = SCSI_SENSE_MEDIUM_ERROR;
	     senseBuffer->AdditionalSenseCode = 0;
	     senseBuffer->AdditionalSenseCodeQualifier = 0;

	     srbStatus |= SRB_STATUS_AUTOSENSE_VALID;
	    }
	}
    }

 /*
  * Set SCSI status to indicate a check condition.
  */
 Srb->ScsiStatus = scsiStatus;

 return srbStatus;

} /* end MapError */

/************************************************************************
 * Just get the higest bit value.
 ************************************************************************/
UCHAR
RaidGetHighestBit(UCHAR Number)
{

 char			bit;

 for (bit = 7; bit >= 0; bit--)
    {
     if (Number & (1 << bit)) return bit;
    }

 return 0xFF;

} /* end RaidGetHighestBit */

/************************************************************************
 * Reset IDE controller or ATAPI device.
 ************************************************************************/
BOOLEAN
AtapiResetController(PITE_ADAPTER pAdap, PChannel pChan)
{

 ULONG			i;
 UCHAR			status;
 ULONG			dma_base;
 BOOLEAN		resetResult;
 SCSI_REQUEST_BLOCK	srb;
 printk("AtapiResetController enter\n");

 dma_base = pChan->dma_base;
 resetResult = FALSE;

 /*
  * Check and see if we are processing an internal srb.
  */
 if (pChan->OriginalSrb)
    {
     pChan->CurrentSrb = pChan->OriginalSrb;
     pChan->OriginalSrb = NULL;
    }

 /*
  * To avoid unexpected interrupts occurs during reset procedure.
  *
  * 1. Stop bus master operation.
  */
 outb(0, dma_base);

 for (i = 0; i < 2; i++)
    {
     outb((UCHAR)((i << 4) | 0xA0), pChan->io_ports[ATAPI_SELECT_OFFSET]);

     /*
      * 2. Clear interrupts if there is any.
      */
     GetBaseStatus(pChan, status);

     /*
      * 3. Disable interrupts.
      */
     outb(IDE_DC_DISABLE_INTERRUPTS, pChan->io_ports[ATAPI_CONTROL_OFFSET]);

     /*
      * 4. Clear interrupts again.
      */
     GetBaseStatus(pChan, status);
    }

 /*
  * Check if request is in progress.
  */
 if (pChan->CurrentSrb)
    {
     /*
      * Complete outstanding request with SRB_STATUS_BUS_RESET.
      */
     srb.SrbStatus = SRB_STATUS_BUS_RESET;

     /*
      * Clear request tracking fields.
      */
     pChan->CurrentSrb = NULL;
     pChan->WordsLeft  = 0;
     pChan->DataBuffer = NULL;

     /*
      * Indicate ready for next request.
      */
     TaskDone(pChan, &srb);
    }

 /*
  * Clear expecting interrupt flag.
  */
 pChan->ExpectingInterrupt = FALSE;
 pChan->RDP = FALSE;

 resetResult = IT8212ResetAdapter(pAdap);

 /*
  * Set transfer modes after resetting the adapter.
  */

 /*
  * Reenable interrupts.
  */
 for (i = 0; i < 4; i++)
    {
     outb((UCHAR)(((i & 1) << 4) | 0xA0), pChan->io_ports[ATAPI_SELECT_OFFSET]);
     outb(IDE_DC_REENABLE_CONTROLLER, pChan->io_ports[ATAPI_CONTROL_OFFSET]);
    }

 printk("AtapiResetController exit\n");

 return resetResult;

} /* end AtapiResetController */

/************************************************************************
 * IDE start read/write transfer.
 ************************************************************************/
void
IdeStartTransfer
(
 PChannel		pChan,
 PSCSI_REQUEST_BLOCK	Srb,
 ULONG			startingSector,
 ULONG			SectorNumber
)
{

 UCHAR			DiskId;
 UCHAR			drvSelect;
 UCHAR			bmClearStat;
 ULONG			dma_base;
 dprintk("IdeStartTransfer enter\n");

 DiskId = (UCHAR) Srb->TargetId;
 dma_base = pChan->dma_base;

 /*
  * 48-bit support.
  */
 if ((startingSector + SectorNumber) > 0x0FFFFFFF)
    {
     /*
      * Select drive and set LBA mode.
      */
     outb((UCHAR) (((DiskId & 0x1) << 4) | 0xA0 | 0x40),
	     pChan->io_ports[IDE_SELECT_OFFSET]);

     /*
      * Sector count register.
      */
     outb((UCHAR) (SectorNumber >> 8), pChan->io_ports[IDE_NSECTOR_OFFSET]);
     outb((UCHAR) SectorNumber,	       pChan->io_ports[IDE_NSECTOR_OFFSET]);

     /*
      * LBA low register.
      */
     outb((UCHAR) (startingSector >> 24), pChan->io_ports[IDE_LOCYL_OFFSET]);
     outb((UCHAR) startingSector,	  pChan->io_ports[IDE_LOCYL_OFFSET]);

     /*
      * LBA mid register.
      */
     outb((UCHAR) 0,			 pChan->io_ports[IDE_MIDCYL_OFFSET]);
     outb((UCHAR) (startingSector >> 8), pChan->io_ports[IDE_MIDCYL_OFFSET]);

     /*
      * LBA high register.
      */
     outb((UCHAR) 0,			  pChan->io_ports[IDE_HCYL_OFFSET]);
     outb((UCHAR) (startingSector >> 16), pChan->io_ports[IDE_HCYL_OFFSET]);

     /*
      * Start the IDE read/write DMA command.
      */
     if (Srb->SrbFlags & SRB_FLAGS_DATA_IN)
	{
	 outb(IDE_COMMAND_READ_DMA_EXT, pChan->io_ports[IDE_COMMAND_OFFSET]);
	}
     else if (Srb->SrbFlags & SRB_FLAGS_DATA_OUT)
	{
	 outb(IDE_COMMAND_WRITE_DMA_EXT, pChan->io_ports[IDE_COMMAND_OFFSET]);
	}
    }
 /*
  * 28-bit addressing.
  */
 else
    {
     /*
      * Select drive and set LBA mode.
      */
     drvSelect = (UCHAR) (startingSector >> 24);
     drvSelect = drvSelect | (((UCHAR) DiskId & 0x1) << 4) | 0x40 | 0xA0;

     outb(drvSelect,			 pChan->io_ports[IDE_SELECT_OFFSET]);
     outb((UCHAR) SectorNumber,		 pChan->io_ports[IDE_NSECTOR_OFFSET]);
     outb((UCHAR) startingSector,	 pChan->io_ports[IDE_LOCYL_OFFSET]);
     outb((UCHAR)(startingSector >> 8),  pChan->io_ports[IDE_MIDCYL_OFFSET]);
     outb((UCHAR)(startingSector >> 16), pChan->io_ports[IDE_HCYL_OFFSET]);

     /*
      * Start the IDE read/write DMA command.
      */
     if (Srb->SrbFlags & SRB_FLAGS_DATA_IN)
	{
	 outb(IDE_COMMAND_READ_DMA, pChan->io_ports[IDE_COMMAND_OFFSET]);
	}
     else if (Srb->SrbFlags & SRB_FLAGS_DATA_OUT)
	{
	 outb(IDE_COMMAND_WRITE_DMA, pChan->io_ports[IDE_COMMAND_OFFSET]);
	}
    }

 /*
  * Indicate expecting an interrupt.
  */
 pChan->ExpectingInterrupt = TRUE;

 /*
  * Setup PRD table physical address.
  */
 outl(pChan->dmatable_dma, dma_base + 4);

 /*
  * Read Bus Master status.
  */
 bmClearStat = inb(dma_base + 2);
 if (Srb->TargetId & 1)
    {
     bmClearStat = bmClearStat	   | BM_DRV1_DMA_CAPABLE |
		   BM_STAT_FLG_INT | BM_STAT_FLG_ERR;
    }
 else
    {
     bmClearStat = bmClearStat	   | BM_DRV0_DMA_CAPABLE |
     		   BM_STAT_FLG_INT | BM_STAT_FLG_ERR;
    }

 outb(0, dma_base);

 /*
  * Clear INTR and ERROR flags.
  */
 outb(bmClearStat, dma_base + 2);

 /*
  * Start DMA read/write.
  */
 if (Srb->SrbFlags & SRB_FLAGS_DATA_IN)
    {
     outb(BM_CMD_FLG_START | BM_CMD_FLG_WRTTOMEM, dma_base);
    }
 else if (Srb->SrbFlags & SRB_FLAGS_DATA_OUT)
    {
     outb(BM_CMD_FLG_START | BM_CMD_FLG_WRTTODSK, dma_base);
    }

 dprintk("IdeStartTransfer exit\n");

} /* end IdeStartTransfer */

/************************************************************************
 * Setup the PRD table.
 ************************************************************************/
static int
IdeBuildSglist(PChannel pChan, PSCSI_REQUEST_BLOCK Srb)
{

 int		nents		= 0;
 ULONG		bytesRemaining	= Srb->DataTransferLength;
 unsigned char * virt_addr	= Srb->DataBuffer;
 struct scatterlist * sg	= pChan->sg_table;

 if (Srb->SrbFlags & SRB_FLAGS_DATA_IN)
    {
     pChan->sg_dma_direction = PCI_DMA_FROMDEVICE;
    }
 else
    {
     pChan->sg_dma_direction = PCI_DMA_TODEVICE;
    }

 /*
  * The upper layer will never give the memory more than 64K bytes.
  */
 memset(&sg[nents], 0, sizeof(*sg));
 sg[nents].address = virt_addr;
 sg[nents].length  = bytesRemaining;
 nents++;

 return pci_map_sg(pChan->pPciDev, sg, nents, pChan->sg_dma_direction);

} /* end IdeBuildSglist */

/************************************************************************
 * Prepares a dma request.
 ************************************************************************/
static int
IdeBuildDmaTable(PChannel pChan, PSCSI_REQUEST_BLOCK Srb)
{

 unsigned int *		table = pChan->dmatable_cpu;
 unsigned int		count = 0;
 int			i;
 struct scatterlist *	sg;

 pChan->sg_nents = i = IdeBuildSglist(pChan, Srb);

 sg = pChan->sg_table;
 while (i && sg_dma_len(sg))
    {
     u32	cur_len;
     u32	cur_addr;

     cur_addr = sg_dma_address(sg);
     cur_len  = sg_dma_len(sg);

     /*
      * Fill in the dma table, without crossing any 64kB boundaries.
      */
     while (cur_len)
	{
	 if (count++ >= PRD_ENTRIES)
	    {
	     printk(KERN_WARNING "@@DMA table too small\n");
	    }
	 else
	    {
	     u32 xcount, bcount = 0x10000 - (cur_addr & 0xFFFF);

	     if (bcount > cur_len) bcount = cur_len;
	     *table++ = cpu_to_le32(cur_addr);
	     xcount = bcount & 0xFFFF;
	     if (xcount == 0x0000)
		{
		 /*
		  * Most chipsets correctly interpret a length
		  * of 0x0000 as 64KB, but at least one
		  * (e.g. CS5530) misinterprets it as zero (!).
		  * So here we break the 64KB entry into two
		  * 32KB entries instead.
		  */
		 if (count++ >= PRD_ENTRIES)
			printk(KERN_WARNING "##DMA table too small\n");

		 *table++ = cpu_to_le32(0x8000);
		 *table++ = cpu_to_le32(cur_addr + 0x8000);
		 xcount = 0x8000;
		}
	     *table++ = cpu_to_le32(xcount);
	     cur_addr += bcount;
	     cur_len -= bcount;
	    }
	}
     sg++;
     i--;
    }

 if (count)
    {
     *--table |= cpu_to_le32(0x80000000);
     return count;
    }
 else
   {
    printk(KERN_WARNING "Empty DMA table?\n");
   }

 return count;

} /* end IdeBuildDmaTable */

/************************************************************************
 * Prepares a dma scatter/gather request.
 ************************************************************************/
static void
IdeBuildDmaSgTable(PChannel pChan, PSCSI_REQUEST_BLOCK Srb)
{

 int			use_sg = 0;
 int			i;
 PPRD_TABLE_ENTRY	pSG = (PPRD_TABLE_ENTRY)pChan->dmatable_cpu;
 struct scatterlist *	sg = (struct scatterlist *)Srb->DataBuffer;

 if (Srb->SrbFlags & SRB_FLAGS_DATA_IN)
    {
     pChan->sg_dma_direction = PCI_DMA_FROMDEVICE;
    }
 else
    {
     pChan->sg_dma_direction = PCI_DMA_TODEVICE;
    }

 use_sg = pci_map_sg(pChan->pPciDev, Srb->DataBuffer, Srb->UseSg, pChan->sg_dma_direction);

 for (i = 0; i < use_sg; i++)
    {
     pSG[i].PhysicalBaseAddress	= sg_dma_address(&sg[i]);
     pSG[i].ByteCount		= sg_dma_len(&sg[i]);
     pSG[i].EndOfTable		= (i == use_sg - 1) ? SG_FLAG_EOT : 0;
    }

} /* end IdeBuildDmaSgTable */

/************************************************************************
 * Setup DMA table for channel.
 ************************************************************************/
static void
IdeSetupDma(PChannel pChan, ULONG dma_base, USHORT num_ports)
{

 printk("Channel[%d] BM-DMA at 0x%04lX-0x%04lX\n", pChan->channel, dma_base,
	dma_base + num_ports - 1);

 /*
  * Allocate IDE DMA buffer.
  */
 pChan->dmatable_cpu = pci_alloc_consistent(pChan->pPciDev,
	 PRD_ENTRIES * PRD_BYTES, &pChan->dmatable_dma);

 if (pChan->dmatable_cpu == NULL)
    {
     printk("IdeSetupDma: allocate prd table failed.\n");
     return;
    }

 memset(pChan->dmatable_cpu, 0, PRD_ENTRIES * PRD_BYTES);

 /*
  * Allocate SCATTER/GATHER table buffer.
  */
 pChan->sg_table = kmalloc(sizeof(struct scatterlist) * PRD_ENTRIES,
				 GFP_KERNEL);

 if (pChan->sg_table == NULL)
    {
     printk("IdeSetupDma: allocate sg_table failed.\n");
     pci_free_consistent(pChan->pPciDev, PRD_ENTRIES * PRD_BYTES,
		pChan->dmatable_cpu, pChan->dmatable_dma);
     return;
    }

 return;

} /* end IdeSetupDma */

/************************************************************************
 * This will be only used in RAID mode.
 ************************************************************************/
void
IT8212ReconfigChannel(PChannel pChan, UCHAR ArrayId, UCHAR Operation)
{

 UCHAR			enableVirtualChannel;
 struct pci_dev *	pPciDev = pChan->pPciDev;

 pci_read_config_byte(pPciDev, 0x43, &enableVirtualChannel);

 if (Operation == DisableChannel)
    {
     enableVirtualChannel &= ~(1 << ArrayId);
     printk("IT8212ReconfigChannel: disable channel %X\n", ArrayId);
    }
 else
    {
     enableVirtualChannel |= ~(1 << ArrayId);
     printk("IT8212ReconfigChannel: enable channel %X\n", ArrayId);
    }

 printk("IT8212ReconfigChannel: channel enabled after set 0x%X\n",
		enableVirtualChannel);

 /*
  * Set enabled virtual channels.
  */
 pci_write_config_byte(pPciDev, 0x43, enableVirtualChannel);

} /* end IT8212ReconfigChannel */

/************************************************************************
 * Get the chip status. This is a vendor specific command. According to
 * all of the device configurations, the BIOS then can consider the
 * existing RAID configuration reasonable. If the existing RAID configur-
 * ation is not reasonable, or if there is NO existing RAID configuration
 * , the BIOS can ask the user to setup the RAID configuration. Finally,
 * the BIOS or AP should send the SET CHIP STATUS to every virtual device.
 * Only after receiving SET CHIP STATUS command, the corresponding virtual
 * device will be active.
 ************************************************************************/
UCHAR
IT8212GetChipStatus(uioctl_t *ioc)
{

 ULONG			PriMasterIsNull = FALSE;
 UCHAR			statusByte;
 UCHAR			srbStatus;
 PITE_ADAPTER		pAdap;
 PChannel		pChan;
 PHYSICAL_DISK_STATUS *	pPhyDiskInfo;
 dprintk("IT8212GetChipStatus enter\n");

 /*
  * Only support one controller now! In the future, we can pass the
  * argument (user ioctl structure) to know which controller need to be
  * identified.
  */
 pAdap = ite_adapters[0];
 pChan = &pAdap->IDEChannel[0];

 /*
  * Allocate space for PHYSICAL_DISK_STATUS.
  */
 if ((pPhyDiskInfo = kmalloc(sizeof(PHYSICAL_DISK_STATUS) * 4, GFP_KERNEL)) == NULL)
    {
     printk("IT8212GetChipStatus: error kmalloc for PHYSCIAL_DISK_STATUS.\n");
     return -ENOMEM;
    }

 memset(pPhyDiskInfo, 0, sizeof(PHYSICAL_DISK_STATUS));

 /*
  * Always send GET CHIP STATUS command to primary channel master device.
  * Select device.
  */
 outb((UCHAR) 0xA0, pChan->io_ports[IDE_SELECT_OFFSET]);

 /*
  * If primary master channel is not enabled, enable it.
  */
 statusByte = inb(pChan->io_ports[IDE_ALTERNATE_OFFSET]);
 if (statusByte == 0)
    {
     PriMasterIsNull = TRUE;
     IT8212ReconfigChannel(pChan, 0, EnableChannel);
    }

 /*
  * Wait for device ready (Not BUSY and not DRQ)
  */
 WaitForDeviceReady(pChan, statusByte);
 if ((statusByte & IDE_STATUS_BUSY) || (statusByte & IDE_STATUS_DRQ) ||
     (statusByte == 0))
    {
     printk("IT8212GetChipStatus: disk[0] not ready. status=0x%X\n",
		statusByte);
     srbStatus = SRB_STATUS_BUSY;
     goto exit;
    }

 /*
  * Disable interrupt to avoid the unexpected interrupt.
  */
 outb(IDE_DC_DISABLE_INTERRUPTS, pChan->io_ports[IDE_CONTROL_OFFSET]);

 /*
  * Issue the command.
  */
 outb(IDE_COMMAND_GET_CHIP_STATUS, pChan->io_ports[IDE_COMMAND_OFFSET]);

 /*
  * Wait for BUSY = 0, DRQ = 1.
  */
 CheckBusyDrq(pChan, statusByte)
 if (statusByte != 0x58)
    {
     printk("IT8212GetChipStatus: disk[0] return unexpected status after");
     printk("issue command. status=0x%X\n", statusByte);
     goto exit_error;
    }

 /*
  * Read the physical disk info.
  */
 ReadBuffer(pChan, (PUSHORT)pPhyDiskInfo, 256);

#if (MARK_DEBUG_DUMP_MEM)
 HexDump((unsigned char *)pPhyDiskInfo, 512);
#endif

 /*
  * Copy physical disk info to user area.
  */
 copy_to_user((PUSHORT)ioc->data, (PUSHORT)pPhyDiskInfo, 512);

 /*
  * Check error.
  */
 WaitForCommandComplete(pChan, statusByte);

 if (statusByte != IDE_STATUS_IDLE)
    {
     printk("IT8212GetChipStatus: disk[0] return unexpected status after");
     printk("read data. status=0x%X\n", statusByte);
     goto exit_error;
    }

 srbStatus = SRB_STATUS_SUCCESS;
 goto exit;

exit_error:

 /*
  * If fail, hard reset to avoid the DRQ status pending.
  */
 srbStatus = SRB_STATUS_ERROR;
 IdeHardReset(pChan, statusByte);

exit:

 /*
  * Reenable interrupt after command complete.
  */
 outb(IDE_DC_REENABLE_CONTROLLER, pChan->io_ports[IDE_CONTROL_OFFSET]);

 /*
  * If primary master is null, disable primary master channel before we leave.
  */
 if (PriMasterIsNull)
    {
     IT8212ReconfigChannel(pChan, 0, DisableChannel);
    }


 dprintk("IT8212GetChipStatus exit\n");

 return srbStatus;

} /* end IT8212GetChipStatus */

/************************************************************************
 * Erase the partition table.
 ************************************************************************/
UCHAR
IT8212ErasePartition(uioctl_t *pioc)
{

 UCHAR			drvSelect;
 UCHAR			statusByte = 0;
 UCHAR			srbStatus;
 UCHAR *		buffer;
 PRAID_CREATE_INFO	createInfo = (PRAID_CREATE_INFO) pioc->data;
 PITE_ADAPTER		pAdap;
 PChannel		pChan;

 printk("IT8212ErasePartition enter\n");

 printk("createInfo->DiskArrayId = %d\n", createInfo->DiskArrayId);

 if (createInfo->ErasePartition == 0 || (createInfo->RaidType == RAID_LEVEL_NODISK))
	 return SRB_STATUS_SUCCESS;

 pAdap = ite_adapters[0];

 if (createInfo->DiskArrayId < 2)
    {
     pChan = &pAdap->IDEChannel[0];
    }
 else
    {
     pChan = &pAdap->IDEChannel[1];
    }

 /*
  * Allocate 512 bytes for buffer.
  */
 if ((buffer = kmalloc(512, GFP_KERNEL)) == NULL)
    {
     printk("IT8212ErasePartition: error kmalloc.\n");
     return -ENOMEM;
    }

 memset(buffer, 0, 512);

 /*
  * Select device.
  */
 drvSelect = (((UCHAR) createInfo->DiskArrayId & 0x1) << 4) | 0xA0 | 0x40;
 outb(drvSelect, pChan->io_ports[IDE_SELECT_OFFSET]);

 /*
  * Wait for device ready (not BUSY and not DRQ).
  */
 WaitForDeviceReady(pChan, statusByte);
 if ((statusByte & IDE_STATUS_BUSY) || (statusByte & IDE_STATUS_DRQ) ||
     (statusByte == 0))
    {
     printk("IT8212ErasePartition: disk[%d] not ready. status=0x%X\n",
		     createInfo->DiskArrayId, statusByte);
     return SRB_STATUS_BUSY;
    }

 /*
  * Disable interrupt to avoid the unexpected interrupt.
  */
 outb(IDE_DC_DISABLE_INTERRUPTS, pChan->io_ports[IDE_CONTROL_OFFSET]);

 /*
  * Write LBA 0 (1 sector).
  */
 outb(1, pChan->io_ports[IDE_NSECTOR_OFFSET]);
 outb(0, pChan->io_ports[IDE_LOCYL_OFFSET]);
 outb(0, pChan->io_ports[IDE_MIDCYL_OFFSET]);
 outb(0, pChan->io_ports[IDE_HCYL_OFFSET]);
 outb(drvSelect, pChan->io_ports[IDE_SELECT_OFFSET]);
 outb(IDE_COMMAND_WRITE_SECTOR, pChan->io_ports[IDE_COMMAND_OFFSET]);

 /*
  * Wait for BUSY = 0, DRQ = 1.
  */
 CheckBusyDrq(pChan, statusByte);
 if (statusByte != 0x58)
    {
     printk("IT8212ErasePartition: disk[%d] error status. status=0x%X\n",
		     createInfo->DiskArrayId, statusByte);
     goto exit_error;
    }

 /*
  * Start erase partition table.
  */
 WriteBuffer(pChan, (PUSHORT)buffer, 256);

 /*
  * Check error.
  */
 WaitForCommandComplete(pChan, statusByte);

 if (statusByte != IDE_STATUS_IDLE)
    {
     printk("IT8212ErasePartition: disk[%d] error status. status=0x%X\n",
		     createInfo->DiskArrayId, statusByte);
     goto exit_error;
    }

 srbStatus = SRB_STATUS_SUCCESS;
 goto exit;

exit_error:

 /*
  * If failed, hard reset to avoid the DRQ status pending.
  */
 IdeHardReset(pChan, statusByte);
 srbStatus = SRB_STATUS_ERROR;

exit:

 /*
  * Reenable interrupt after command complete.
  */
 outb(IDE_DC_REENABLE_CONTROLLER, pChan->io_ports[IDE_CONTROL_OFFSET]);

 printk("IT8212ErasePartition exit\n");

 return srbStatus;

} /* end IT8212ErasePartition */

/************************************************************************
 *
 ************************************************************************/
ULONG
IT8212TruncateReduentSectors
(
 ULONG			OriginalSectorCount,
 USHORT			StripeSizeInKBytes
)
{

 USHORT			stripeSizeInSector;

 /*
  * 0 means using default value (32 sectors).
  */
 if (StripeSizeInKBytes == 0)
    {
     stripeSizeInSector = 64 * 2;
    }
 else
    {
     stripeSizeInSector = StripeSizeInKBytes * 2;
    }

 return ((OriginalSectorCount / stripeSizeInSector) * stripeSizeInSector);


} /* end IT8212TruncateReduentSectors */

/************************************************************************
 * Calculate the addressable sector for this RAID.
 ************************************************************************/
ULONG
IT8212DiskArrayAddressableSector(PUCHAR DiskArrayCreateInfo)
{

 PRAID_CREATE_INFO	createInfo = (PRAID_CREATE_INFO) DiskArrayCreateInfo;
 ULONG			MinDiskCapacity;
 ULONG			ArrayCapacity;
 UCHAR			DiskNo;
 UCHAR			NumOfDisks;

 MinDiskCapacity = ArrayCapacity = NumOfDisks = 0;

 printk("createInfo->AddressableSectors[0] = 0x%lX\n", createInfo->AddressableSectors[0]);
 printk("createInfo->AddressableSectors[1] = 0x%lX\n", createInfo->AddressableSectors[1]);
 printk("createInfo->AddressableSectors[2] = 0x%lX\n", createInfo->AddressableSectors[2]);
 printk("createInfo->AddressableSectors[3] = 0x%lX\n", createInfo->AddressableSectors[3]);

 for (DiskNo = 0; DiskNo < 4; DiskNo++)
    {
     /*
      * If disk exist.
      */
     if ((createInfo->ContainingDisks >> DiskNo) & 0x1)
	{
	 NumOfDisks += 1;
	 if (!MinDiskCapacity || (createInfo->AddressableSectors[DiskNo] <
	      MinDiskCapacity))
	    {
	     MinDiskCapacity = createInfo->AddressableSectors[DiskNo];
	    }
	}
    }

 switch (createInfo->RaidType)
    {
     /*
      * Containing 2 or 3 or 4 disks.
      */
     case RAID_LEVEL_0:
	 MinDiskCapacity = IT8212TruncateReduentSectors(MinDiskCapacity - 2,
			createInfo->StripeSize);
	 ArrayCapacity = MinDiskCapacity * NumOfDisks;
	 break;

     /*
      * Containing 2 disks.
      */
     case RAID_LEVEL_1:
	 ArrayCapacity = MinDiskCapacity - 2;
	 break;

     /*
      * Containing 4 disks.
      */
     case RAID_LEVEL_10:
	 MinDiskCapacity = IT8212TruncateReduentSectors(MinDiskCapacity - 2,
			createInfo->StripeSize);
	 ArrayCapacity = MinDiskCapacity * 2;
	 break;

     /*
      * Containing 2, 3, or 4 disks.
      */
     case RAID_LEVEL_JBOD:
	 for (DiskNo = 0; DiskNo < 4; DiskNo++)
	    {
	     if ((createInfo->ContainingDisks >> DiskNo) & 0x1)
		{
		 ArrayCapacity = ArrayCapacity + (createInfo->AddressableSectors[DiskNo] - 2);
		}
	    }
	 break;

     /*
      * Containing only 1 disk.
      */
     case RAID_LEVEL_NORMAL:
	ArrayCapacity = MinDiskCapacity;
	break;

    }

 return ArrayCapacity;

} /* end IT8212DiskArrayAddressableSector */

/************************************************************************
 * Create a new array.
 ************************************************************************/
UCHAR
IT8212CreateDiskArray(uioctl_t *pioc)
{

 PVOID				buffer;
 UCHAR				i;
 UCHAR				subCommand = 0xFF;
 UCHAR				statusByte;
 UCHAR				dmaSupported;
 UCHAR				udmaSupported;
 UCHAR				srbStatus;
 ULONG				UserAddressableSectors;
 ULONG				PriMasterIsNull = FALSE;
 PChannel			pChan;
 PITE_ADAPTER			pAdap;
 PRAID_CREATE_INFO		createInfo = (PRAID_CREATE_INFO)pioc->data;
 PIDENTIFY_DATA2		identifyData;
 PIT8212_SET_CHIP_STATUS_INFO	setChipStatus;

 USHORT	IT8212TimingTable[7] =
 {
  0x3133,			/* UDMA timimg register 01		*/
  0x2121,			/* UDMA timimg register 23		*/
  0x9111,			/* UDMA timimg register 45		*/
  0x0091,			/* UDMA timimg register 6		*/
  0x3266,			/* DMA  timimg register 01		*/
  0x0021,			/* DMA  timimg register 2		*/
  0x0021			/* PIO  timimg register			*/
 };

 USHORT	IT8212ClockTable[7] =
 {
  0x0505,			/* UDMA clock register 01		*/
  0x0005,			/* UDMA clock register 23		*/
  0x0500,			/* UDMA clock register 45		*/
  0x0000,			/* UDMA clock register 6		*/
  0x0005,			/* DMA  clock register 01		*/
  0x0005,			/* DMA  clock register 2		*/
  0x0005			/* PIO  clock register			*/
 };

 printk("IT8212CreateDiskArray enter\n");

#if (MARK_DUMP_CREATE_INFO)
 printk("createInfo->DiskArrayId           = %d\n", createInfo->DiskArrayId);
 printk("createInfo->ModelNumber           = %s\n", createInfo->ModelNumber);
 printk("createInfo->RaidType              = %d\n", createInfo->RaidType);
 printk("createInfo->ContainingDisks       = %d\n", createInfo->ContainingDisks);
 printk("createInfo->AutoRebuildEnable     = %d\n", createInfo->AutoRebuildEnable);
 printk("createInfo->StripeSize            = %d\n", createInfo->StripeSize);
 printk("createInfo->BootableDisk          = %d\n", createInfo->BootableDisk);
 printk("createInfo->NewlyCreated          = %d\n", createInfo->NewlyCreated);
 printk("createInfo->ErasePartition        = %d\n", createInfo->ErasePartition);
 printk("createInfo->DMASupported[0]       = 0x%x\n", createInfo->DMASupported[0]);
 printk("createInfo->DMASupported[1]       = 0x%x\n", createInfo->DMASupported[1]);
 printk("createInfo->DMASupported[2]       = 0x%x\n", createInfo->DMASupported[2]);
 printk("createInfo->DMASupported[3]       = 0x%x\n", createInfo->DMASupported[3]);
 printk("createInfo->UDMASupported[0]      = 0x%x\n", createInfo->UDMASupported[0]);
 printk("createInfo->UDMASupported[1]      = 0x%x\n", createInfo->UDMASupported[1]);
 printk("createInfo->UDMASupported[2]      = 0x%x\n", createInfo->UDMASupported[2]);
 printk("createInfo->UDMASupported[3]      = 0x%x\n", createInfo->UDMASupported[3]);
 printk("createInfo->AddressableSectors[0] = 0x%lX\n", createInfo->AddressableSectors[0]);
 printk("createInfo->AddressableSectors[1] = 0x%lX\n", createInfo->AddressableSectors[1]);
 printk("createInfo->AddressableSectors[2] = 0x%lX\n", createInfo->AddressableSectors[2]);
 printk("createInfo->AddressableSectors[3] = 0x%lX\n", createInfo->AddressableSectors[3]);
#endif

 switch (createInfo->RaidType)
    {
     case RAID_LEVEL_0:
     case RAID_LEVEL_1:
     case RAID_LEVEL_10:
     case RAID_LEVEL_JBOD:
     case RAID_LEVEL_NORMAL:
	 subCommand = 0x50;
	 break;

     case RAID_LEVEL_NODISK:
	 subCommand = 0x48;
	 break;
    }

 /*
  * The command should be sent to virtual primary master.
  */
 pAdap = ite_adapters[0];
 pChan = &pAdap->IDEChannel[0];

 /*
  * Allocate 512-bytes buffer.
  */
 if ((buffer = kmalloc(512, GFP_KERNEL)) == NULL)
    {
     printk("IT8212CreateDiskArray: error kmalloc.\n");
     return -ENOMEM;
    }

 identifyData  = (PIDENTIFY_DATA2) buffer;

 /*
  * 2003/05/08
  * Remember the vendor specific parameters starts from word 129 not 128.
  */
 setChipStatus = (PIT8212_SET_CHIP_STATUS_INFO) (buffer + 258);

 /*
  * Configure to RAID or NORMAL.
  */
 if (subCommand == 0x50)
    {
     /*
      * Zero identify data structure.
      */
     memset((PUCHAR) identifyData, 0, sizeof(IDENTIFY_DATA));

     /*
      * Fill up identify data.
      */
     memmove(identifyData->ModelNumber, createInfo->ModelNumber, 40);
     memmove(identifyData->SerialNumber, &createInfo->SerialNumber, sizeof(RAID_SERIAL_NUMBER));

     /*
      * Set disk array virtual capacity.
      */
     UserAddressableSectors = IT8212DiskArrayAddressableSector(pioc->data);

     printk("IT8212CreateDiskArray: array capacity = %lX\n", UserAddressableSectors);

     identifyData->Capacity_48bit_LOW  = UserAddressableSectors;
     identifyData->Capacity_48bit_HIGH = 0;

     if (UserAddressableSectors > 0x0FFFFFFF)
	{
	 identifyData->UserAddressableSectors = 0x0FFFFFFF;
	}
     else
	{
	 identifyData->UserAddressableSectors = UserAddressableSectors;
	}

     /*
      * Get DMA supported mode and UDMA supported mode.
      */
     dmaSupported = udmaSupported = 0xFF;
     for (i = 0; i < 4; i++)
	{
	 if ((createInfo->ContainingDisks >> i) & 1)
	    {
	     dmaSupported  &= (UCHAR) createInfo->DMASupported[i];
	     udmaSupported &= (UCHAR) createInfo->UDMASupported[i];
	    }
	}

     identifyData->MultiWordDMASupport	= dmaSupported;
     identifyData->UltraDMASupport	= udmaSupported;

     /*
      * Fill up SET CHIP STATUS data (word 129 - 153)
      */
     setChipStatus->RaidType		 = createInfo->RaidType;
     setChipStatus->ContainingDisks	 = createInfo->ContainingDisks;
     setChipStatus->UltraDmaTiming01	 = IT8212TimingTable[0];
     setChipStatus->UltraDmaTiming23	 = IT8212TimingTable[1];
     setChipStatus->UltraDmaTiming45	 = IT8212TimingTable[2];
     setChipStatus->UltraDmaTiming6	 = IT8212TimingTable[3];
     setChipStatus->MultiWordDmaTiming01 = IT8212TimingTable[4];
     setChipStatus->UltraDmaTiming2	 = IT8212TimingTable[5];
     setChipStatus->PioTiming4		 = IT8212TimingTable[6];
     setChipStatus->AutoRebuildEnable	 = createInfo->AutoRebuildEnable;
     setChipStatus->IdeClkUDma01	 = IT8212ClockTable[0];
     setChipStatus->IdeClkUDma23	 = IT8212ClockTable[1];
     setChipStatus->IdeClkUDma45	 = IT8212ClockTable[2];
     setChipStatus->IdeClkUDma6		 = IT8212ClockTable[3];
     setChipStatus->IdeClkMDma01	 = IT8212ClockTable[4];
     setChipStatus->IdeClkMDma2		 = IT8212ClockTable[5];
     setChipStatus->IdeClkPio4		 = IT8212ClockTable[6];
     setChipStatus->StripeSize		 = createInfo->StripeSize;
     setChipStatus->BootableDisk	 = createInfo->BootableDisk;
     setChipStatus->CheckHotSwapInterval = 0;
     setChipStatus->TargetSourceDisk	 = createInfo->TargetSourceDisk;
     setChipStatus->RebuildBlockSize	 = 0;
     setChipStatus->ResetInterval1	 = 0;
     setChipStatus->ResetInterval2	 = 0;
     setChipStatus->RebuildRetryTimes	 = 0;
     setChipStatus->NewlyCreated	 = createInfo->NewlyCreated;
    }

#if (MARK_DEBUG_DUMP_MEM)
 HexDump(buffer, 512);
#endif

 /*
  * There are some contrains of disk placement. AP will take care of it.
  */

 /*
  * Select device.
  */
 outb((UCHAR) 0xA0, pChan->io_ports[IDE_SELECT_OFFSET]);

 /*
  * If primary master channel is not enabled, enable it.
  */
 statusByte = inb(pChan->io_ports[IDE_CONTROL_OFFSET]);
 if (statusByte == 0)
    {
     PriMasterIsNull = TRUE;
     IT8212ReconfigChannel(pChan, 0, EnableChannel);
    }

 /*
  * Wait for device ready (not BUSY and not DRQ)
  */
 WaitForDeviceReady(pChan, statusByte);
 if ((statusByte & IDE_STATUS_BUSY) || (statusByte & IDE_STATUS_DRQ) ||
     (statusByte == 0))
    {
     printk("IT8212CreateDiskArray: disk[0] not ready. status=0x%X\n",
		statusByte);
     srbStatus = SRB_STATUS_BUSY;
     goto exit;
    }

 /*
  * Disable interrupt to avoid the unexpected interrupt.
  */
 outb(IDE_DC_DISABLE_INTERRUPTS, pChan->io_ports[IDE_CONTROL_OFFSET]);

 outb(subCommand, pChan->io_ports[IDE_FEATURE_OFFSET]);
 outb(createInfo->DiskArrayId, pChan->io_ports[IDE_SELECT_OFFSET]);
 outb(IDE_COMMAND_SET_CHIP_STATUS, pChan->io_ports[IDE_COMMAND_OFFSET]);

 /*
  * No disk (no data command protocol)
  */
 if (subCommand == 0x48)
    {
     WaitForCommandComplete(pChan, statusByte);
     if (statusByte != IDE_STATUS_IDLE)
	{
	 printk("IT8212CreateDiskArray: disk[0] return unexpected status after issue command.\n");
	 goto exit_error;
	}

     IT8212ReconfigChannel(pChan, createInfo->DiskArrayId, DisableChannel);
     srbStatus = SRB_STATUS_SUCCESS;
     goto exit;
    }

 /*
  * Create RAID (PIO data out command protocol).
  */
 if (subCommand == 0x50)
    {
     /*
      * Wait for BUSY=0, DRQ=1.
      */
     CheckBusyDrq(pChan, statusByte);
     if (statusByte != 0x58)
	{
	 printk("IT8212CreateDiskArray: disk[0] return unexpected status after issue command.\n");
	 goto exit_error;
	}

     WriteBuffer(pChan, buffer, 256);

     /*
      * Check error.
      */
     WaitForCommandComplete(pChan, statusByte);

     if (statusByte != IDE_STATUS_IDLE)
	{
	 printk("IT8212CreateDiskArray: disk[0] return unexpected status after issue command.\n");
	 goto exit_error;
	}

     IT8212ReconfigChannel(pChan, createInfo->DiskArrayId, EnableChannel);
     srbStatus = SRB_STATUS_SUCCESS;
     goto exit;
    }

exit_error:

 /*
  * If fail, hard reset to avoid the DRQ pending.
  */
 IdeHardReset(pChan, statusByte);
 srbStatus = SRB_STATUS_ERROR;

exit:

 /*
  * If primary master is null, and we are not configuring array 0. Disable
  * primary master channel again.
  */
 if (PriMasterIsNull && createInfo->DiskArrayId)
    {
     IT8212ReconfigChannel(pChan, 0 , DisableChannel);
    }

 /*
  * Reenable interrupt after command complete.
  */
 outb(IDE_DC_REENABLE_CONTROLLER, pChan->io_ports[IDE_CONTROL_OFFSET]);

 printk("IT8212CreateDiskArray exit\n");

 return srbStatus;


} /* end IT8212CreateDiskArray */

/************************************************************************
 * Return "virtual" drive 512 bytes identification data.
 ************************************************************************/
UCHAR
IT8212IssueIdentify(uioctl_t *pioc)
{

 UCHAR			channum;
 UCHAR			devnum;
 UCHAR			statusByte;
 UCHAR			srbStatus;
 PITE_ADAPTER		pAdap;
 PChannel		pChan;

 /*
  * Only support one adapter now! In the future, we can pass the argument
  * to know which adapter need to be identified.
  */
 pAdap = ite_adapters[0];

 memset(pioc->data, 0, 512 * 4);

 /*
  * Two channels per controller.
  */
 for (channum = 0; channum < pAdap->num_channels; channum++)
    {
     pChan = &pAdap->IDEChannel[channum];

     /*
      * Two devices per channel.
      */
     for (devnum = 0; devnum < 2; devnum++)
	{
	 /*
	  * Select device.
	  */
	 outb((UCHAR)((devnum << 4) | 0xA0), pChan->io_ports[IDE_SELECT_OFFSET]);

	 /*
	  * Check if disk online?
	  */
	 statusByte = inb(pChan->io_ports[IDE_ALTERNATE_OFFSET]);
	 if ((statusByte & 0x40) != 0x40)
	    {
	     printk("IT8212IssueIdentify: disk[%d] is offline\n", devnum + channum * 2);
	     continue;
	    }

	 /*
	  * Wait for device ready (Not busy and not DRQ)
	  */
	 WaitForDeviceReady(pChan, statusByte);

	 if ((statusByte & IDE_STATUS_BUSY) || (statusByte & IDE_STATUS_DRQ) ||
	     (statusByte == 0))
	    {
	     printk("IT8212IssueIdentify: disk[%d] not ready. status=0x%X\n",
			devnum + channum * 2, statusByte);
	     srbStatus = SRB_STATUS_BUSY;
	     goto exit;
	    }

	 /*
	  * Disable interrupt to avoid the unexpected interrupt.
	  */
	 outb(IDE_DC_DISABLE_INTERRUPTS, pChan->io_ports[IDE_CONTROL_OFFSET]);

	 /*
	  * Issue command.
	  */
	 outb(IDE_COMMAND_IDENTIFY, pChan->io_ports[IDE_COMMAND_OFFSET]);

	 /*
	  * Wait for BUSY = 0 and DRQ = 1.
	  */
	 CheckBusyDrq(pChan, statusByte);
	 if (statusByte != 0x58)
	    {
	     printk("IT8212IssueIndetify: disk[%d] returns unexpedted status after issue command. status=0x%X\n",
			devnum + channum * 2, statusByte);
	     goto error;
	    }

	 /*
	  * Read the identify data.
	  */
	 ReadBuffer(pChan, (PUSHORT)&pChan->FullIdentifyData, 256);

	 /*
	  * Then copy to user area.
	  * unsigned long copy_to_user(void *to, const void *from, unsigned long count).
	  */
	 copy_to_user((PUSHORT) (pioc->data + ((devnum + channum * 2) * 512)),
	 		 (PUSHORT)&pChan->FullIdentifyData, 256);

	 /*
	  * Check error after reading data.
	  */
	 WaitForCommandComplete(pChan, statusByte);
	 if (statusByte != IDE_STATUS_IDLE)
	    {
	     printk("IT8212IssueIdentify: disk[%d] returns unexpected status after read data. status=0x%X\n",
			devnum + channum * 2, statusByte);
	     goto error;
	    }

	} /* end for each device */

    } /* end for each channel */

 srbStatus = SRB_STATUS_SUCCESS;
 goto exit;

error:
 /*
  * If failed, hard reset to avoid the IRQ pending.
  */
 IdeHardReset(pChan, statusByte);
 srbStatus = SRB_STATUS_ERROR;

exit:
 /*
  * Reenable interrupt after command complete.
  */
 for (channum = 0; channum < pAdap->num_channels; channum++)
    {
     pChan = &pAdap->IDEChannel[channum];
     outb(IDE_DC_REENABLE_CONTROLLER, pChan->io_ports[IDE_CONTROL_OFFSET]);
    }

 return srbStatus;

} /* end IT8212IssueIdentify */

/************************************************************************
 * Reset the controller.
 ************************************************************************/
BOOLEAN
IT8212ResetAdapter(PITE_ADAPTER pAdap)
{

 BOOLEAN		resetChannel[2];
 UCHAR			channel;
 UCHAR			device;
 ULONG			i;
 UCHAR			status[4];
 PChannel		pChan;

 /*
  * First, perform ATAPI soft reset if ATAPI devices are attached.
  */
 for (channel = 0; channel < 2; channel++)
    {
     pChan = &pAdap->IDEChannel[channel];
     resetChannel[channel] = FALSE;
     for (device = 0; device < 2; device++)
	{
	 if (pChan->DeviceFlags[device] & DFLAGS_DEVICE_PRESENT)
	    {
	     if (pChan->DeviceFlags[device] & DFLAGS_ATAPI_DEVICE)
		{
		 printk("IT8212ResetAdapter: perform ATAPI soft reset (%d, %d)\n",
			channel, device);
		 AtapiSoftReset(pChan, device);
		}
	     else
		{
		 resetChannel[channel] = TRUE;
		}
	    }
	}
    }

 /*
  * If ATA device is present on this channel, perform channel reset.
  */
 for (channel = 0; channel < 2; channel++)
    {
     pChan = &pAdap->IDEChannel[channel];
     if (resetChannel[channel])
	{
	 printk("IT8212ResetAdapter: reset channel %d\n", channel);
	 outb(IDE_DC_RESET_CONTROLLER, pChan->io_ports[IDE_CONTROL_OFFSET]);
	 mdelay(50);
	 outb(IDE_DC_REENABLE_CONTROLLER, pChan->io_ports[IDE_CONTROL_OFFSET]);
	}
    }

 /*
  * Check device status after reset.
  */
 for (i = 0; i < 1000 * 1000; i++)
    {
     for (channel = 0; channel < 2; channel++)
	{
	 pChan = &pAdap->IDEChannel[channel];
	 for (device = 0; device < 2; device++)
	    {
	     if (pChan->DeviceFlags[device] & DFLAGS_DEVICE_PRESENT)
		{
		 outb((UCHAR)((device << 4) | 0xA0), pChan->io_ports[IDE_SELECT_OFFSET]);
		 status[(channel * 2) + device] = inb(pChan->io_ports[IDE_COMMAND_OFFSET]);
		}
	     else
		{
		 status[(channel * 2) + device] = 0;
		}
	    }
	}

     /*
      * ATA device should present status 0x50 after reset.
      * ATAPI device should present status 0 after reset.
      */
     if ((status[0] != IDE_STATUS_IDLE && status[0] != 0x0) ||
	 (status[1] != IDE_STATUS_IDLE && status[1] != 0x0) ||
	 (status[2] != IDE_STATUS_IDLE && status[2] != 0x0) ||
	 (status[3] != IDE_STATUS_IDLE && status[3] != 0x0))
	{
	 udelay(30);
	}
     else
	{
	 break;
	}
    }

 if (i == 1000 * 1000)
    {
     printk("IT8212ResetAdapter Fail!\n");
     printk("Device status after reset = [0x%x, 0x%x, 0x%x, 0x%x]\n",
	status[0], status[1], status[2], status[3]);
     return FALSE;
    }
 else
    {
     printk("IT8212ResetAdapter Success!\n");
     return TRUE;
    }

} /* end IT8212ResetAdapter */

/************************************************************************
 * Rebuild disk array.
 ************************************************************************/
UCHAR
IT8212Rebuild(uioctl_t *pioc)
{

 UCHAR			rebuildDirection;
 UCHAR			statusByte = 0;
 PRAID_REBUILD_INFO	apRebuildInfo = (PRAID_REBUILD_INFO) pioc->data;
 PITE_ADAPTER		pAdap;
 PChannel		pChan;
 dprintk("IT8212Rebuild enter\n");

 rebuildDirection = (apRebuildInfo->Resume << 4) | (apRebuildInfo->DestDisk << 2) |
	 	     apRebuildInfo->SrcDisk;

 apRebuildInfo->Status = 0xFF;
 pAdap = ite_adapters[0];

 printk("IT8212Rebuild: diskArrayId=%d\n", apRebuildInfo->DiskArrayId);

 if (apRebuildInfo->DiskArrayId < 2)
    {
     pChan = &pAdap->IDEChannel[0];
    }
 else
    {
     pChan = &pAdap->IDEChannel[1];
    }

 /*
  * Selcet device.
  */
 outb((UCHAR)((apRebuildInfo->DiskArrayId & 0x1) << 4 | 0xA0),
		 pChan->io_ports[IDE_SELECT_OFFSET]);

 /*
  * Wait for device ready (not BUSY and not DRQ).
  */
 WaitForDeviceReady(pChan, statusByte);
 if ((statusByte & IDE_STATUS_BUSY) || (statusByte & IDE_STATUS_DRQ) ||
     (statusByte == 0))
    {
     apRebuildInfo->Status = REBUILD_ERR_DISK_BUSY;
     printk("IT8212Rebuild: disk[%d] not ready. status=0x%X\n",
		     apRebuildInfo->DiskArrayId, statusByte);
     return SRB_STATUS_BUSY;
    }

 /*
  * Disable interrupt to avoid the unexpected interrupt.
  */
 outb(IDE_DC_DISABLE_INTERRUPTS, pChan->io_ports[IDE_CONTROL_OFFSET]);

 /*
  * Give a direction.
  */
 outb(rebuildDirection, pChan->io_ports[IDE_FEATURE_OFFSET]);

 /*
  * Issue a REBUILD commmand.
  */
 outb(IDE_COMMAND_REBUILD, pChan->io_ports[IDE_COMMAND_OFFSET]);

 /*
  * Check for errors.
  */
 WaitForCommandComplete(pChan, statusByte);

 /*
  * Reenable interrupt after command complete.
  */
 outb(IDE_DC_REENABLE_CONTROLLER, pChan->io_ports[IDE_CONTROL_OFFSET]);

 if (statusByte != IDE_STATUS_IDLE)
    {
     if (statusByte & IDE_STATUS_ERROR)
	{
	 apRebuildInfo->Status = inb(pChan->io_ports[IDE_NSECTOR_OFFSET]);
	 printk("IT8212Rebuild: rebuild error. reason=0x%X\n", apRebuildInfo->Status);
	}
     return apRebuildInfo->Status;
    }

 dprintk("IT8212Rebuild exit\n");

 return SRB_STATUS_PENDING;

} /* end IT8212Rebuild */

/************************************************************************
 * Switch to DMA mode if necessary.
 *
 * offset 0x50 = PCI Mode Control Register
 *
 * Bit 0 = PCI Mode Select (1=firmware mode, 0=transparent mode)
 * Bit 1 = Primary Channel IDE Clock Frequency Select (1=50, 0=66)
 * Bit 2 = Secondary Channel IDE Clock Freq Select (1=50, 0=66)
 * Bit 3 = Primary   Channel Dev 0 Transfer Mode (1=MultiWord DMA, 0=Ultra DMA)
 * Bit 4 = Primary   Channel Dev 1 Transfer Mode (1=MultiWord DMA, 0=Ultra DMA)
 * Bit 5 = Secondary Channel Dev 0 Transfer Mode (1=MultiWord DMA, 0=Ultra DMA)
 * Bit 6 = Secondary Channel Dev 1 Transfer Mode (1=MultiWord DMA, 0=Ultra DMA)
 * Bit 7 = PCI Mode Reset
 ************************************************************************/
VOID
IT8212SwitchDmaMode(PChannel pChan, UCHAR DeviceId)
{

 UCHAR			pciControl;
 UCHAR			channel;
 UCHAR			device;
 UCHAR			configByte = 0;
 UCHAR			RevisionID;
 struct pci_dev *	pPciDev = pChan->pPciDev;

 /*
  * These tables are for performance issue. Better formance than lots
  * of "Shifts".
  */
 UCHAR		dmaModeV10[4]  = {0x18, 0x18, 0x60, 0x60};
 UCHAR		udmaModeV10[4] = {0xE7, 0xE7, 0x9F, 0x9F};
 UCHAR		ideClock[4]    = {0xFD, 0xFD, 0xFB, 0xFB};

#if (0)
 UCHAR		dmaMode[4]     = {0x08, 0x10, 0x20, 0x40};
 UCHAR		udmaMode[4]    = {0xF7, 0xEF, 0xDF, 0xBF};
#endif

 /*
  * channel --> 0-1; device --> 0-1; DeviceId --> 0-3;
  */
 channel = DeviceId >> 1;
 device  = DeviceId & 1;

 /*
  * Do nothing if the mode switch is unnecessary.
  */
 if (!pChan->DoSwitch || pChan->ActiveDevice == DeviceId)
    {
     dprintk("IT8212SwitchDmaMode: do not need to switch mode!\n");
     return;
    }

 printk("IT8212SwitchDmaMode: switch DMA mode for dev (%x)\n", DeviceId);

 pci_read_config_byte(pPciDev, 0x50, &pciControl);
 pci_read_config_byte(pPciDev, 0x08, &RevisionID);

 /*
  * Running on MULTIWORD_DMA mode.
  */
 if (pChan->DmaType[device] == USE_MULTIWORD_DMA)
    {
     /*
      * Switch to DMA mode.
      */
     if (RevisionID == 0x10)
	{
	 configByte = pciControl | dmaModeV10[DeviceId];
	}
     pci_write_config_byte(pPciDev, 0x50, configByte);
    }
 /*
  * Running on ULTRA DMA mode.
  */
 else
    {
     /*
      * Select UDMA mode.
      */
     configByte = pciControl;
     if (RevisionID == 0x10)
	{
	 configByte &= udmaModeV10[DeviceId];
	}

     /*
      * Select IDE clock.
      */
     configByte = (configByte & ideClock[DeviceId]) |
		  (pChan->IdeClock[device] << (channel + 1));

     pci_write_config_byte(pPciDev, 0x50, configByte);

     /*
      * Set UDMA timing.
      *
      * offset 0x56 = PCI Mode Primary Device 0 Ultra DMA Timing Registers
      * offset 0x57 = PCI Mode Primary Device 1 Ultra DMA Timing Registers
      * offset 0x5A = PCI Mode Secondary Device 0 Ultra DMA Timing Registers
      * offset 0x5B = PCI Mode Secondary Device 1 Ultra DMA Timing Registers
      */
     if (RevisionID == 0x10)
	{
	 configByte = pChan->UdmaTiming[device];
	 pci_write_config_byte(pPciDev, (UCHAR) (0x56 + (channel * 4)), configByte);
	 pci_write_config_byte(pPciDev, (UCHAR) (0x56 + (channel * 4) + 1), configByte);
	}

     /*
      * Set PIO/DMA timing (Becasuse maybe the IDE clock is changed.)
      */
     configByte = pChan->PioDmaTiming[pChan->IdeClock[device]];
     pci_write_config_byte(pPciDev, (UCHAR) (0x54 + (channel * 4)), configByte);
    }

 /*
  * Record the Active device on this channel
  */
 pChan->ActiveDevice = device;

} /* end IT8212SwitchDmaMode */

/************************************************************************
 * IT8212 read/write routine.
 ************************************************************************/
ULONG
IT8212ReadWrite(PChannel pChan, PSCSI_REQUEST_BLOCK Srb)
{

 UCHAR			statusByte = 0;
 ULONG			startingSector;
 ULONG			sectorNumber;
 ULONG			capacity;
 PITE_ADAPTER		pAdap;

 if (Srb->TargetId >= 4)
    {
     pAdap = ite_adapters[1];
     if (Srb->TargetId < 6) pChan = &pAdap->IDEChannel[0];
     else		    pChan = &pAdap->IDEChannel[1];
    }
 else
    {
     pAdap = ite_adapters[0];
     if (Srb->TargetId < 2) pChan = &pAdap->IDEChannel[0];
     else		    pChan = &pAdap->IDEChannel[1];
    }

 /*
  * Return error if overrun.
  */
 startingSector = ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte3	   |
		  ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte2 << 8   |
		  ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte1 << 16  |
		  ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte0 << 24;

 sectorNumber = (USHORT) ((Srb->DataTransferLength + 0x1FF) / 0x200);

 capacity = pChan->IdentifyData[Srb->TargetId & 0x1].UserAddressableSectors;
 if (capacity == 0x0FFFFFFF)
    {
     capacity = pChan->IdentifyData[Srb->TargetId & 0x1].Capacity_48bit_LOW;
    }

 if ((startingSector + sectorNumber - 1) > capacity)
    {
     printk("IT8212ReadWrite: disk[%d] over disk size.\n", Srb->TargetId);
     printk("capacity: %ld. starting sector: %ld. sector number: %ld\n",
		capacity, startingSector, sectorNumber);
     return SRB_STATUS_ERROR;
    }

 /*
  * Select device.
  */
 outb((UCHAR)((Srb->TargetId & 0x1) << 4 | 0xA0), pChan->io_ports[IDE_SELECT_OFFSET]);

 /*
  * Wait for device ready (Not Busy and Not DRQ).
  */
 WaitForDeviceReady(pChan, statusByte);
 if ((statusByte & IDE_STATUS_BUSY) || (statusByte & IDE_STATUS_DRQ) ||
     (statusByte == 0))
    {
     printk("IT8212ReadWrite: disk[%d] not ready. status=0x%x\n",
		Srb->TargetId, statusByte);
     return SRB_STATUS_BUSY;
    }

 /*
  * First, switch to DMA or UDMA mode if running on bypass mode.
  */
 if (pAdap->bypass_mode)
    {
     IT8212SwitchDmaMode(pChan,	Srb->TargetId);
    }

 /*
  * Check the SCATTER/GATHER count. The upper will give the different
  * memory address depend on whether use_sg is used or not.
  */
 if (Srb->UseSg == 0)
    {
     IdeBuildDmaTable(pChan, Srb);
    }
 else
    {
     IdeBuildDmaSgTable(pChan, Srb);
    }

 /*
  * Start transfer the data.
  */
 IdeStartTransfer(pChan, Srb, startingSector, sectorNumber);

 /*
  * Wait for interrupt.
  */
 return SRB_STATUS_PENDING;

} /* end IT8212ReadWrite */

/************************************************************************
 * Setup the transfer mode.
 ************************************************************************/
VOID
IT8212SetTransferMode
(
 PChannel		pChan,
 ULONG			DiskId,
 UCHAR			TransferMode,
 UCHAR	 		ModeNumber
)
{

 UCHAR			statusByte = 0;

 /*
  * Select device.
  */
 outb((UCHAR) ((DiskId & 0x1) << 4 | 0xA0), pChan->io_ports[IDE_SELECT_OFFSET]);

 /*
  * Wait for device ready (Not Busy and Not DRQ).
  */
 WaitForDeviceReady(pChan, statusByte);
 if ((statusByte & IDE_STATUS_BUSY) || (statusByte & IDE_STATUS_DRQ))
    {
     printk("IT8212SetTransferMode: disk[%ld] not ready. status=0x%x\n",
     DiskId, statusByte);
     return;
    }

 /*
  * Feature number ==> 03
  *
  * Mode contained in Sector Count Register.
  *
  * Bits(7:3)	Bits(2:0)	Mode
  *
  * 00000	000		PIO default mode
  * 00000	001		PIO default mode, disable IORDY
  * 00001	mode		PIO flow control transfer mode
  * 00010	mode		Single Word DMA mode
  * 00100	mode		Multi-word DMA mode
  * 01000	mode		Ultra DMA mode
  */
 TransferMode |= ModeNumber;

 outb(0x03,			pChan->io_ports[IDE_FEATURE_OFFSET]);
 outb(TransferMode,		pChan->io_ports[IDE_NSECTOR_OFFSET]);
 outb(0,			pChan->io_ports[IDE_HCYL_OFFSET]);
 outb(0,			pChan->io_ports[IDE_MIDCYL_OFFSET]);
 outb(IDE_COMMAND_SET_FEATURE,	pChan->io_ports[IDE_COMMAND_OFFSET]);

 /*
  * Check error.
  */
 WaitForBaseCommandComplete(pChan, statusByte);

 if ((statusByte != IDE_STATUS_IDLE) && (statusByte != 0))
    {
     printk("IT8212SetTransferMode: disk[%ld]", DiskId);
     printk("return unexpected status after issue command. 0x%x\n",
	statusByte);
    }

} /* end IT8212SetTransferMode */

/************************************************************************
 * Set the best transfer mode for device.
 ************************************************************************/
VOID
IT8212SetBestTransferMode(PITE_ADAPTER pAdap, PChannel pChan, UCHAR channel)
{

 UCHAR			i;
 UCHAR			k;
 UCHAR			transferMode;
 UCHAR			modeNumber;
 UCHAR			pciControl;
 UCHAR			device;
 UCHAR			configByte;
 UCHAR			cableStatus[2] = {CABLE_40_PIN, CABLE_40_PIN};
 UCHAR			RevisionID;
 struct pci_dev *	pPciDev = pChan->pPciDev;
 PIDENTIFY_DATA2	ideIdentifyData;

 /*
  * UDMA timing table for 66MHz clock.
  * UDMA timing table for 50MHz clock.
  * Best of IDE clock in this mode.
  */
 UCHAR	udmaTiming[3][7] =
 {
  {0x44, 0x42, 0x31, 0x21, 0x11, 0x22, 0x11},
  {0x33, 0x31, 0x21, 0x21, 0x11, 0x11, 0x11},
  {IDE_CLOCK_66, IDE_CLOCK_50, IDE_CLOCK_66, IDE_CLOCK_66, IDE_CLOCK_66, IDE_CLOCK_50, IDE_CLOCK_66}
 };

 /*
  * DMA timing table for 66 MHz clock.
  * DMA timing table for 50 MHz clock.
  */
 UCHAR	dmaTiming[2][3] =
 {
  {0x88, 0x32, 0x31},
  {0x66, 0x22, 0x21}
 };

 /*
  * PIO timing table for 66 MHz clock.
  * PIO timing table for 50 MHz clock.
  */
 UCHAR	pioTiming[2][5] =
 {
  {0xAA, 0xA3, 0xA1, 0x33, 0x31},
  {0x88, 0x82, 0x81, 0x32, 0x21}
 };

 UCHAR	pio_dma_timing[2][2][4] =
 {
  {{0, 0, 0, 0}, {0, 0, 0, 0}},
  {{0, 0, 0, 0}, {0, 0, 0, 0}}
 };

 /*
  * These tables are for performance issue. Better formance than lots
  * of "Shifts".
  */
 //UCHAR dmaModeV10[4] = {0x18, 0x18, 0x60, 0x60};
 UCHAR	udmaModeV10[4] = {0xE7, 0xE7, 0x9F, 0x9F};
 UCHAR	dmaMode[4]     = {0x08, 0x10, 0x20, 0x40};
 UCHAR	udmaMode[4]    = {0xF7, 0xEF, 0xDF, 0xBF};
 UCHAR	ideClock[4]    = {0xFD, 0xFD, 0xFB, 0xFB};

 /*
  * 2003/07/24
  * If running on Firmware mode, get cable status from it.
  */

 for (i = 0; i < 2; i++)
    {
     /*
      * The dafault of cable status is in PCI configuration 0x40.
      */
     cableStatus[i] = pChan->Cable80[i];

     /*
      * channel -->0 to 1.
      * device  -->0 or 1.
      */
     pChan->UseDma[i] = TRUE;
     device	      = i & 1;

     if (!(pChan->DeviceFlags[i] & DFLAGS_DEVICE_PRESENT) ||
	  (pChan->DeviceFlags[i] & DFLAGS_CONFIG_CHANGED))
	{
	 pio_dma_timing[0][channel][device] = pio_dma_timing[0][channel][device + 2] = 0;
	 pio_dma_timing[1][channel][device] = pio_dma_timing[1][channel][device + 2] = 0;
	 continue;
	}

     /*
      * Set PIO Mode.
      */
     ideIdentifyData = &pChan->IdentifyData[i];
     if ((!(ideIdentifyData->ValidFieldIndicator & 0x02)) || (ideIdentifyData->AdvancedPIOModes == 0))
	{
	 transferMode = PIO_FLOW_CONTROL;
	 modeNumber = 2;
	}
     else
	{
	 transferMode = PIO_FLOW_CONTROL;
	 modeNumber = RaidGetHighestBit((UCHAR) ideIdentifyData->AdvancedPIOModes) + 3;
	}

     IT8212SetTransferMode(pChan, i, transferMode, modeNumber);

     /*
      * Record the PIO timing for later use.(0 to 4)
      */
     pio_dma_timing[0][channel][device] = pioTiming[0][modeNumber];
     pio_dma_timing[1][channel][device] = pioTiming[1][modeNumber];

     /*
      * Get the best transfer mode (maybe Ultra DMA or Multi-Word DMA).
      */
     ideIdentifyData = &pChan->IdentifyData[i];
     if ((!(ideIdentifyData->ValidFieldIndicator & 0x04)) || (ideIdentifyData->UltraDMASupport == 0))
	{
	 /*
	  * UltraDMA is not valid.
	  */
	 transferMode = MULTIWORD_DMA;
	 modeNumber = RaidGetHighestBit(ideIdentifyData->MultiWordDMASupport);
	 printk("The best transfer mode of Device[%d] is DMA-%d\n", i, modeNumber);
	}
     else
	{
	 transferMode = ULTRA_DMA;
	 modeNumber = RaidGetHighestBit(ideIdentifyData->UltraDMASupport);
	 printk("The best transfer mode of Device[%d] is Ultra-%d\n", i, modeNumber);

	 /*
	  * If this is 40-pin cable. Limit to Ultra DMA mode 2.
	  */
#	 if (0)
	 if ((cableStatus[i] == CABLE_40_PIN) && (modeNumber > 2))
	    {
	     printk("Reduce trans mode of Device[%d] to Ultra-2 for cable issue.\n", i);
	     modeNumber = 0x02;
	    }
#	 endif
	}

     IT8212SetTransferMode(pChan, i, transferMode, modeNumber);

     /*
      * If running on ByPass mode, driver must take the responsibility to
      * set the PIO/DMA/UDMA timing.
      */
     if (pAdap->bypass_mode)
	{
	 pci_read_config_byte(pPciDev, 0x50, &pciControl);
	 pci_read_config_byte(pPciDev, 0x08, &RevisionID);

	 if (transferMode == ULTRA_DMA)
	    {
	     /*
	      * Set this channel to UDMA mode (not only the device).
	      */
	     if (RevisionID == 0x10)
		{
		 configByte = pciControl & udmaModeV10[i + channel * 2];
		}
	     else
		{
		 configByte = pciControl & udmaMode[i + channel * 2];
		}

	     /*
	      * Select IDE clock (50MHz or 66MHz).
	      */
	     configByte &= ideClock[i + channel * 2];
	     configByte |= (udmaTiming[2][modeNumber] << (channel + 1));

	     pci_write_config_byte(pPciDev, 0x50, configByte);

	     /*
	      * Set UDMA timing.
	      */
	     configByte = udmaTiming[udmaTiming[2][modeNumber]][modeNumber];
	     if (modeNumber == 5 || modeNumber == 6)
		{
		 /*
		  * Enable UDMA mode 5/6
		  */
		 configByte |= UDMA_MODE_5_6;
		}

	     /*
	      * Bug Bug. Fill these two fields into the same value.
	      */
	     if (RevisionID == 0x10)
		{
		 pci_write_config_byte(pPciDev, (UCHAR) (0x56 + (channel * 4)), configByte);
		 pci_write_config_byte(pPciDev, (UCHAR) (0x56 + (channel * 4) + 1), configByte);
		}
	     else
		{
		 pci_write_config_byte(pPciDev, (UCHAR) (0x56 + (channel * 4) + device), configByte);
		}

	     /*
	      * Record the best UDMA mode for this device.
	      */
	     pChan->DmaType[i]	  = ULTRA_DMA;
	     pChan->IdeClock[i]	  = udmaTiming[2][modeNumber];
	     pChan->UdmaTiming[i] = configByte;
	    }
	else if (transferMode == MULTIWORD_DMA)
	    {
	     /*
	      * If an ATAPI device with DMA mode, force it to run in PIO mode.
	      */
	     if (RevisionID == 0x10 && pChan->DeviceFlags[i] & DFLAGS_ATAPI_DEVICE)
		{
		 pChan->UseDma[i] = FALSE;
		}
	     else
		{
		 /*
		  * Set this device to DMA mode.
		  */
		 configByte = pciControl | dmaMode[i + channel * 2];
		 pci_write_config_byte(pPciDev, 0x50, configByte);

		 /*
		  * Record DMA timing (for later use).
		  */
		 pio_dma_timing[0][channel][device + 2] = dmaTiming[0][modeNumber];
		 pio_dma_timing[1][channel][device + 2] = dmaTiming[1][modeNumber];
		}
	     pChan->DmaType[i] = USE_MULTIWORD_DMA;
	    }
	 pChan->ActiveDevice = device;
	}
    }

 /*
  * Because each channel owns one PIO/DMA timimg register only, so we
  * must set the timing to the slowest one to fit all. Really stupid H/W! :(
  */
 if (pAdap->bypass_mode)
    {
     /*
      * Loop for the two IDE clocks (50 MHz and 66 MHz).
      */
     for (i = 0; i < 2; i++)
	{
	 configByte = 0;
	 for (k = 0; k < 4; k++)
	    {
	     /*
	      * High part.
	      */
	     if ((pio_dma_timing[i][channel][k] & 0xF0) > (configByte & 0xF0))
		{
		 configByte = (configByte & 0xF) | (pio_dma_timing[i][channel][k] & 0xF0);
		}

	     /*
	      * Low part.
	      */
	     if ((pio_dma_timing[i][channel][k] & 0xF) > (configByte & 0xF))
		{
		 configByte = (configByte & 0xF0) | (pio_dma_timing[i][channel][k] & 0xF);
		}
	    }

	 /*
	  * Record the PIO/DMA timing for this channel.
	  */
	 pChan->PioDmaTiming[i] = configByte;
	}

     /*
      * Set PIO/DMA timing register for each channel.
      */
     configByte = pChan->PioDmaTiming[(pciControl >> (channel + 1)) & 1];
     if (configByte != 0)
	{
	 pci_write_config_byte(pPciDev, (UCHAR) (0x54 + (channel * 4)), configByte);
	}

     /*
      * Check shall we do switch between the two devices
      */
     for (i = 0; i < 2; i++)
 	{
	 pChan->DoSwitch = TRUE;

	 /*
	  * Master is not present
	  */
	 if (!(pChan->DeviceFlags[i] & DFLAGS_DEVICE_PRESENT) ||
	      (pChan->DeviceFlags[i] & DFLAGS_CONFIG_CHANGED))
	    {
	     printk("Channel %x: master is not present. No switch mode.\n", channel);
	     pChan->DoSwitch = FALSE;
	     continue;
	    }

	 /*
	  * Slave is not present
	  */
	 if (!(pChan->DeviceFlags[i + 1] & DFLAGS_DEVICE_PRESENT) ||
	      (pChan->DeviceFlags[i + 1] & DFLAGS_CONFIG_CHANGED))
	    {
	     printk("Channel %x: slave is not present. No switch mode.\n", channel);
	     pChan->DoSwitch = FALSE;
	     continue;
	    }

	 /*
	  * If both devices are running on DMA mode, no switch.
	  */
	 if (pChan->DmaType[i] == USE_MULTIWORD_DMA && pChan->DmaType[i + 1] == USE_MULTIWORD_DMA)
	    {
	     printk("Channel %x: run on DMA mode only. No switch mode.\n", channel);
	     pChan->DoSwitch = FALSE;
	     continue;
	    }

	 /*
	  * No switch if the two devices are running on the same mode.
	  */
	 if ((pChan->DmaType[i]    == pChan->DmaType[i + 1]   ) &&
	     (pChan->UdmaTiming[i] == pChan->UdmaTiming[i + 1]) &&
	     (pChan->IdeClock[i]   == pChan->IdeClock[i + 1]  ))
	    {
	     printk("Channel %x: two dev run on the same mode. No switch mode.\n", channel);
	     pChan->DoSwitch = FALSE;
	     continue;
	    }

	 printk("Channel %x: switch mode if needed.\n", channel);
	}
    }

} /* end IT8212SetBestTransferMode */

/************************************************************************
 * Initialize bypass(transparent) mode if BIOS is not ready.
 ************************************************************************/
BOOLEAN
IT8212InitBypassMode(struct pci_dev *pPciDev)
{

 /*
  * Reset local CPU, and set BIOS not ready.
  */
 pci_write_config_byte(pPciDev, 0x5E, 0x01);

 /*
  * Set to bypass mode, and reset PCI bus.
  */
 pci_write_config_byte(pPciDev, 0x50, 0x00);

 pci_write_config_word(pPciDev, 0x4, 0x0047);
 pci_write_config_word(pPciDev, 0x40, 0xA0F3);

 pci_write_config_dword(pPciDev,0x4C, 0x02040204);
 pci_write_config_byte(pPciDev, 0x42, 0x36);
 pci_write_config_byte(pPciDev, 0x0D, 0x00);

 return TRUE;

} /* end IT8212InitBypassMode */

/************************************************************************
 * This is the interrupt service routine for ATAPI IDE miniport driver.
 * TURE if expecting an interrupt.
 ************************************************************************/
BOOLEAN
IT8212Interrupt(PChannel pChan, BOOLEAN bypass_mode)
{

 UCHAR			statusByte;
 UCHAR			bmstatus;
 ULONG			i;
 ULONG			bmbase;
 PSCSI_REQUEST_BLOCK	Srb;

 bmstatus = 0;
 bmbase	  = pChan->dma_base;
 Srb	  = pChan->CurrentSrb;

 if (Srb == 0 || pChan->ExpectingInterrupt == 0)
    {
     dprintk("IT8212Interrupt: suspicious interrupt!\n");

     /*
      * Clear interrupt by reading status register.
      */
     outb((UCHAR) 0xA0, pChan->io_ports[IDE_SELECT_OFFSET]);
     GetBaseStatus(pChan, statusByte);
     outb((UCHAR) 0xB0, pChan->io_ports[IDE_SELECT_OFFSET]);
     GetBaseStatus(pChan, statusByte);
     outb(bmbase + 2, (UCHAR) (inb(bmbase + 2) | BM_STAT_FLG_INT));
     return FALSE;
    }

 /*
  * To handle share IRQ condition. If the interrupt is not ours, just
  * return FALSE.
  */
 bmstatus = inb(bmbase + 2);
 if ((bmstatus & BM_STAT_FLG_INT) == 0)
    {
     dprintk("IT8212Interrupt: suspicious interrupt (int bit is not on)\n");
     return FALSE;
    }

 /*
  * Bug Fixed: All PIO access are blocked during bus master operation, so
  * stop bus master operation before we try to access IDE registers.
  */
 if (bypass_mode)
    {
     outb(bmbase, 0);
    }

 /*
  * Clear interrupt by reading status register.
  */
 GetBaseStatus(pChan, statusByte);
 outb(bmbase + 2, (UCHAR) (bmstatus | BM_STAT_FLG_INT));

 /*
  * Handle ATAPI interrupt.
  */
 if (pChan->DeviceFlags[Srb->TargetId & 1] & DFLAGS_ATAPI_DEVICE)
    {
     return AtapiInterrupt(pChan);
    }

 pChan->ExpectingInterrupt = FALSE;

 if ((statusByte & IDE_STATUS_BUSY) || (statusByte & IDE_STATUS_DRQ))
    {
     /*
      * Ensure BUSY and DRQ is non-asserted.
      */
     for (i = 0; i < 100; i++)
	{
	 GetBaseStatus(pChan, statusByte);
	 if (!(statusByte & IDE_STATUS_BUSY) && !(statusByte & IDE_STATUS_DRQ))
	    {
	     break;
	    }
	 mdelay(5);
	}

     if (i == 100)
	{
	 printk("IT8212Interrupt: disk[%x] return busy or drq status. status = 0x%x\n",
		Srb->TargetId, statusByte);
	 return FALSE;
	}
     }

 if (statusByte & IDE_STATUS_ERROR)
    {
     /*
      * Stop bus master operation.
      */
     outb(bmbase, 0);

     printk("IT8212Interrupt: error!\n");

     /*
      * Map error to specific SRB status and handle request sense.
      */
     Srb->SrbStatus = MapError(pChan, Srb);
    }
 else
    {
     Srb->SrbStatus = SRB_STATUS_SUCCESS;
    }

 pChan->CurrentSrb = NULL;

 TaskDone(pChan, Srb);

 return TRUE;

} /* end IT8212Interrupt */

/************************************************************************
 * This is the interrupt service routine for ATAPI IDE miniport driver.
 * TRUE if expecting an interrupt. Remember the ATAPI io registers are
 * different from IDE io registers and this is for each channel not for
 * entire controller.
 ************************************************************************/
BOOLEAN
AtapiInterrupt(PChannel pChan)
{

 ULONG			wordCount;
 ULONG			wordsThisInterrupt;
 ULONG			status;
 ULONG			i;
 UCHAR			statusByte;
 UCHAR			interruptReason;
 UCHAR			target_id;
 PSCSI_REQUEST_BLOCK	srb;
 PITE_ADAPTER		pAdap;

 wordCount	    = 0;
 wordsThisInterrupt = 256;
 srb		    = pChan->CurrentSrb;
 target_id	    = srb->TargetId;

 if (target_id >= 4)
    {
     pAdap = ite_adapters[1];
     if (target_id < 6) pChan = &pAdap->IDEChannel[0];
     else		pChan = &pAdap->IDEChannel[1];
    }
 else
    {
     pAdap = ite_adapters[0];
     if (target_id < 2) pChan = &pAdap->IDEChannel[0];
     else		pChan = &pAdap->IDEChannel[1];
    }

 /*
  * Clear interrupt by reading status.
  */
 GetBaseStatus(pChan, statusByte);

 dprintk("AtapiInterrupt: entered with status (%x)\n", statusByte);

 if (statusByte & IDE_STATUS_BUSY)
    {
     /*
      * Ensure BUSY is non-asserted.
      */
     for (i = 0; i < 10; i++)
	{
	 GetBaseStatus(pChan, statusByte);
	 if (!(statusByte & IDE_STATUS_BUSY))
	    {
	     break;
	    }
	 mdelay(5);
	}

     if (i == 10)
	{
	 printk("AtapiInterrupt: BUSY on entry. Status %x\n", statusByte);
	 return FALSE;
	}
    }

 /*
  * Check for error conditions.
  */
 if (statusByte & IDE_STATUS_ERROR)
    {
     if (srb->Cdb[0] != SCSIOP_REQUEST_SENSE)
	{
	 /*
	  * Fail this request.
	  */
	 status = SRB_STATUS_ERROR;
	 goto CompleteRequest;
	}
    }

 /*
  * Check reason for this interrupt.
  */
 interruptReason = (inb(pChan->io_ports[ATAPI_INTREASON_OFFSET]) & 0x3);
 wordsThisInterrupt = 256;

 if (interruptReason == 0x1 && (statusByte & IDE_STATUS_DRQ))
    {
     /*
      * Write the packet.
      */
     printk("AtapiInterrupt: writing Atapi packet.\n");

     /*
      * Send CDB to device.
      */
     WriteBuffer(pChan, (PUSHORT)srb->Cdb, 6);

     return TRUE;
    }
 else if (interruptReason == 0x0 && (statusByte & IDE_STATUS_DRQ))
    {
     /*
      * Write the data.
      */

     /*
      * Pick up bytes to transfer and convert to words.
      */
     wordCount = inb(pChan->io_ports[ATAPI_LCYL_OFFSET]);
     wordCount |= inb(pChan->io_ports[ATAPI_HCYL_OFFSET]) << 8;

     /*
      * Covert bytes to words.
      */
     wordCount >>= 1;

     if (wordCount != pChan->WordsLeft)
	{
	 printk( "AtapiInterrupt: %ld words requested; %ld words xferred\n",
		pChan->WordsLeft, wordCount);
	}

     /*
      * Verify this makes sense.
      */
     if (wordCount > pChan->WordsLeft)
	{
	 wordCount = pChan->WordsLeft;
	}

     /*
      * Ensure that this is a write command.
      */
     if (srb->SrbFlags & SRB_FLAGS_DATA_OUT)
	{
	 dprintk("AtapiInterrupt: write interrupt\n");
	 WaitOnBusy(pChan, statusByte);
	 WriteBuffer(pChan, pChan->DataBuffer, wordCount);

#	 if (0)
	 /*
	  * Translate ATAPI data back to SCSI data if needed (don't
	  * convert if the original command is SCSIOP_MODE_SELECT10)
	  */
	 if (srb->Cdb[0] == ATAPI_MODE_SELECT && pchan->ConvertCdb)
	    {
	     Atapi2Scsi(pChan, srb, (char *)pChan->DataBuffer, wordCount << 1);
	    }
#	 endif
	}
     else
	{
	 printk("AtapiInterrupt: int reason %x, but srb is for a write %p.\n",
		interruptReason, srb);

	 /*
	  * Fail this request.
	  */
	 status = SRB_STATUS_ERROR;
	 goto CompleteRequest;
	}

     /*
      * Advance data buffer pointer and bytes left.
      */
     pChan->DataBuffer += wordCount;
     pChan->WordsLeft -= wordCount;

     return TRUE;
    }
 else if (interruptReason == 0x2 && (statusByte & IDE_STATUS_DRQ))
    {
     /*
      * Pick up bytes to transfer and convert to words.
      */
     wordCount = inb(pChan->io_ports[ATAPI_LCYL_OFFSET]);
     wordCount |= inb(pChan->io_ports[ATAPI_HCYL_OFFSET]) << 8;

     /*
      * Covert bytes to words.
      */
     wordCount >>= 1;
     if (wordCount != pChan->WordsLeft)
	{
	 printk("AtapiInterrupt: %ld words requested; %ld words xferred\n",
		pChan->WordsLeft, wordCount);
	}

     /*
      * Verify this makes sense.
      */
     if (wordCount > pChan->WordsLeft)
	{
	 wordCount = pChan->WordsLeft;
	}

     /*
      * Ensure that this is a read command.
      */
     if (srb->SrbFlags & SRB_FLAGS_DATA_IN)
	{
	 dprintk("AtapiInterrupt: read interrupt\n");
	 WaitOnBusy(pChan, statusByte);
	 ReadBuffer(pChan, pChan->DataBuffer, wordCount);

	 /*
	  * From Windows DDK
	  * You should typically set the ANSI-approved Version field,
	  * in the INQUIRY response, to at least 2.
	  */
	 if (srb->Cdb[0] == SCSIOP_INQUIRY)
	    {
	     /*
	      * Maybe it's not necessary in Linux driver.
	      */
	     *((PUCHAR)pChan->DataBuffer + 2) = 2;
	    }
	}
     else
	{
	 printk("AtapiInterrupt: int reason %x, but srb is for a read %p.\n",
		interruptReason, srb);

	 /*
	  * Fail this request.
	  */
	 status = SRB_STATUS_ERROR;
	 goto CompleteRequest;
	}

     /*
      * Advance data buffer pointer and bytes left.
      */
     pChan->DataBuffer += wordCount;
     pChan->WordsLeft -= wordCount;

     /*
      * Check for read command complete.
      */
     if (pChan->WordsLeft == 0)
	{
	 /*
	  * Work around to make many atapi devices return correct sector size
	  * of 2048. Also certain devices will have sector count == 0x00, check
	  * for that also.
	  */
	 if ((srb->Cdb[0] == 0x25) &&
	    ((pChan->IdentifyData[srb->TargetId & 1].GeneralConfiguration >> 8) & 0x1F) == 0x05)
	    {
	     pChan->DataBuffer -= wordCount;
	     if (pChan->DataBuffer[0] == 0x00)
		{
		 *((ULONG *) &(pChan->DataBuffer[0])) = 0xFFFFFF7F;
		}

	     *((ULONG *) &(pChan->DataBuffer[2])) = 0x00080000;
	     pChan->DataBuffer += wordCount;
	    }
	}
     return TRUE;
    }
 else if (interruptReason == 0x3 && !(statusByte & IDE_STATUS_DRQ))
    {
     dprintk("AtapiInterrupt: command complete!\n");
     /*
      * Command complete.
      */
     if (pChan->WordsLeft)
	{
	 status = SRB_STATUS_DATA_OVERRUN;
	}
     else
	{
	 status = SRB_STATUS_SUCCESS;
	}

CompleteRequest:

     if (status == SRB_STATUS_ERROR)
	{
	 /*
	  * Map error to specific SRB status and handle request sense.
	  */
	 printk("AtapiInterrupt error\n");

	 status = MapError(pChan, srb);

	 /*
	  * Try to recover it.... 2003/02/27
	  */


	 pChan->RDP = FALSE;
	}
     else
	{
	 /*
	  * Wait for busy to drop.
	  */
	 for (i = 0; i < 30; i++)
	    {
	     GetStatus(pChan, statusByte);
	     if (!(statusByte & IDE_STATUS_BUSY))
		{
		 break;
		}
	     udelay(500);
	    }

	 if (i == 30)
	    {
	     /*
	      * Reset the controller.
	      */
	     printk("AtapiInterrupt: resetting due to BSY still up - %x.\n", statusByte);
	     AtapiResetController(pAdap, pChan);
	     return TRUE;
	    }

	 /*
	  * Check to see if DRQ is still up.
	  */
	 if (statusByte & IDE_STATUS_DRQ)
	    {
	     for (i = 0; i < 500; i++)
		{
		 GetStatus(pChan, statusByte);
		 if (!(statusByte & IDE_STATUS_DRQ))
		    {
		     break;
		    }
		 udelay(100);
		}

	     if (i == 500)
		{
		 /*
		  * Reset the controller.
		  */
		 printk("AtapiInterrupt: resetting due to DRQ still up - %x\n",
			statusByte);
		 AtapiResetController(pAdap, pChan);
		 return TRUE;
		}
	    }
	}

     /*
      * Clear interrupt expecting flag.
      */
     pChan->ExpectingInterrupt = FALSE;

     /*
      * Sanity check that there is a current request.
      */
     if (srb != NULL)
	{
	 /*
	  * Set status in SRB.
	  */
	 srb->SrbStatus = (UCHAR)status;

	 /*
	  * Check for underflow.
	  */
	 if (pChan->WordsLeft)
	    {
	     /*
	      * Subtract out residual words and update if filemark hit,
	      * setmark hit , end of data, end of media...
	      */
	     if (!(pChan->DeviceFlags[srb->TargetId & 1] & DFLAGS_TAPE_DEVICE))
		{
		 if (status == SRB_STATUS_DATA_OVERRUN)
		    {
		     srb->DataTransferLength -= pChan->WordsLeft * 2;
		    }
		 else
		    {
		     srb->DataTransferLength = 0;
		    }
		}
	     else
		{
		 srb->DataTransferLength -= pChan->WordsLeft * 2;
		}
	    }

	 GetBaseStatus(pChan, statusByte);
	 if (pChan->RDP && !(statusByte & IDE_STATUS_DSC))
	    {
	     printk("-@@-\n");
	    }
	 else
	    {
	     /*
	      * Clear current SRB. Indicate ready for next request.
	      */
	     pChan->CurrentSrb = NULL;
	     TaskDone(pChan, srb);
	    }
	}
     else
	{
	 printk("AtapiInterrupt: no SRB!\n");
	}

     return TRUE;
    }
 else
    {
     /*
      * Unexpected int.
      */
     printk("AtapiInterrupt: unexpected interrupt. interruptReason %x. status %x.\n",
     interruptReason, statusByte);
     return FALSE;
    }

 return TRUE;

} /* end AtapiInterrupt */

/************************************************************************
 * IRQ handler.
 ************************************************************************/
static void
Irq_Handler(int irq, void *dev_id, struct pt_regs *regs)
{

 UCHAR			i;
 UCHAR			j;
 ULONG			flags;
 PITE_ADAPTER		pAdap;
 dprintk("Irq_Handler enter\n");

 spin_lock_irqsave(&io_request_lock, flags);

 /*
  * Scan for interrupt to process.
  */
 for (i = 0; i < NumAdapters; i++)
    {
     pAdap = ite_adapters[i];
     if (pAdap->irq != irq) continue;

     for (j = 0; j < pAdap->num_channels; j++)
	{
	 IT8212Interrupt(&pAdap->IDEChannel[j], pAdap->bypass_mode);
	}
    }

 spin_unlock_irqrestore(&io_request_lock, flags);

 dprintk("Irq_Handler exit\n");

} /* end Irq_Handler */

/************************************************************************
 * This routine handles IDE Verify.
 ************************************************************************/
UCHAR
IdeVerify(PChannel pChan, PSCSI_REQUEST_BLOCK Srb)
{

 ULONG		startingSector;
 ULONG		sectors;
 ULONG		endSector;
 ULONG		sectorCount;
 UCHAR		drvSelect;
 UCHAR		statusByte = 0;

 /*
  * Select device
  */
 outb((UCHAR)((Srb->TargetId & 0x1) << 4 | 0xA0), pChan->io_ports[IDE_SELECT_OFFSET]);

 /*
  * Wait for device ready (Not BUSY and Not DRQ)
  */
 WaitForDeviceReady(pChan, statusByte);
 if ((statusByte & IDE_STATUS_BUSY) || (statusByte & IDE_STATUS_DRQ) ||
     (statusByte == 0))
    {
     printk("IdeVerify: disk[%d] not ready. status=0x%x\n",
		Srb->TargetId, statusByte);
     return SRB_STATUS_BUSY;
    }

 /*
  * Get the starting sector number from CDB.
  */
 startingSector = ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte3 |
		  ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte2 << 8 |
		  ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte1 << 16 |
		  ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte0 << 24;

 sectorCount = (USHORT)(((PCDB)Srb->Cdb)->CDB10.TransferBlocksMsb << 8 |
			((PCDB)Srb->Cdb)->CDB10.TransferBlocksLsb );

 endSector = startingSector + sectorCount;

 /*
  * Drive has these number sectors.
  *
  * 48-bit addressing.
  */
 if (endSector > 0x0FFFFFFF)
    {
     sectors = pChan->IdentifyData[Srb->TargetId & 0x01].Capacity_48bit_LOW;

     printk("IdeVerify (48-bit): starting sector %ld, Ending sector %ld\n",
		startingSector, endSector);

     if (endSector > sectors)
	{
	 /*
	  * Too big, round down.
	  */
	 printk("IdeVerify: truncating request to %lx blocks\n", sectors - startingSector - 1);

	 outb((UCHAR)((sectors - startingSector - 1) >> 8), pChan->io_ports[IDE_NSECTOR_OFFSET]);
	 outb((UCHAR)(sectors - startingSector - 1), pChan->io_ports[IDE_NSECTOR_OFFSET]);
	}
     else
	{
	 /*
	  * Set up sector count register. Round up to next block.
	  */
	 if (sectorCount > 0xFFFF)
	    {
	     sectorCount = (USHORT)0xFFFF;
	    }

	 outb((UCHAR)(sectorCount >> 8), pChan->io_ports[IDE_NSECTOR_OFFSET]);
	 outb((UCHAR) sectorCount, pChan->io_ports[IDE_NSECTOR_OFFSET]);
	}

     /*
      * Indicate expecting an interrupt.
      */
     pChan->ExpectingInterrupt = TRUE;

     /*
      * Set up LBA address
      */
     outb((UCHAR) (startingSector >> 24), pChan->io_ports[IDE_LOCYL_OFFSET]);
     outb((UCHAR) startingSector,	  pChan->io_ports[IDE_LOCYL_OFFSET]);
     outb((UCHAR) 0,			  pChan->io_ports[IDE_MIDCYL_OFFSET]);
     outb((UCHAR) (startingSector >> 8),  pChan->io_ports[IDE_MIDCYL_OFFSET]);
     outb((UCHAR) 0,			  pChan->io_ports[IDE_HCYL_OFFSET]);
     outb((UCHAR) (startingSector >> 16), pChan->io_ports[IDE_HCYL_OFFSET]);

     /*
      * Send verify command.
      */
     outb(IDE_COMMAND_READ_VERIFY_EXT, pChan->io_ports[IDE_COMMAND_OFFSET]);
    }
 /*
  * 28-bit addressing
  */
 else
    {
     sectors = pChan->IdentifyData[Srb->TargetId & 0x01].UserAddressableSectors;

     printk("IdeVerify: starting sector %ld, ending sector %ld\n",
		startingSector, endSector);

     if (endSector > sectors)
	{
	 /*
	  * Too big, round down.
	  */
	 printk("IdeVerify: truncating request to %ld blocks\n", sectors - startingSector - 1);
	 outb((UCHAR)(sectors - startingSector - 1), pChan->io_ports[IDE_NSECTOR_OFFSET]);
	}
     else
	{
	 /*
	  * Set up sector count register. Round up to next block.
	  */
	 if (sectorCount > 0xFF)
	    {
	     sectorCount = (USHORT)0xFF;
	    }
	 outb((UCHAR)sectorCount, pChan->io_ports[IDE_NSECTOR_OFFSET]);
	}

     /*
      * Indicate expecting an interrupt.
      */
     pChan->ExpectingInterrupt = TRUE;

     /*
      * Set up LBA address
      */
     outb((UCHAR) startingSector,	 pChan->io_ports[IDE_LOCYL_OFFSET]);
     outb((UCHAR) (startingSector >> 8), pChan->io_ports[IDE_MIDCYL_OFFSET]);
     outb((UCHAR) (startingSector >> 16),pChan->io_ports[IDE_HCYL_OFFSET]);

     /*
      * Select driver, set LBA mode, set LBA (27:27)
      */
     drvSelect = (UCHAR) (startingSector >> 24);
     drvSelect = drvSelect | (((UCHAR) Srb->TargetId & 0x1) << 4) | 0xA0 | 0x40;
     outb(drvSelect, pChan->io_ports[IDE_SELECT_OFFSET]);

     /*
      * Send verify command.
      */
     outb(IDE_COMMAND_READ_VERIFY, pChan->io_ports[IDE_COMMAND_OFFSET]);
    }

 /*
  * Wait for interrupt.
  */
 return SRB_STATUS_PENDING;

} /* end IdeVerify */

/************************************************************************
 * This function is used to copy memory with overlapped destination and
 * source. I guess ScsiPortMoveMemory cannot handle this well. Can it?
 ************************************************************************/
void
IT8212MoveMemory(PUCHAR DestAddr, PUCHAR SrcAddr, LONG ByteCount)
{

 long		i;

 dprintk("IT8212MoveMemory: DestAddr=0x%p, SrcAddr=0x%p, ByteCount=0x%lx\n",
	DestAddr, SrcAddr, ByteCount);

 if (DestAddr > SrcAddr)
    {
     /*
      * If Destination Area is in the back of the Source Area, copy from
      * the end of the requested area.
      */
     for (i = (ByteCount - 1); i >= 0; i--)
	{
	 *(DestAddr + i) = *(SrcAddr + i);
	}
    }
 else if (DestAddr < SrcAddr)
    {
     /*
      * If Destination Area is in the front of the Source Area, copy from
      * the begin of the requested area.
      */
     for (i = 0; i < ByteCount; i++)
	{
	 *(DestAddr + i) = *(SrcAddr + i);
	}
    }

} /* end IT8212MoveMemory */

/************************************************************************
 * Convert SCSI packet command to Atapi packet command.
 ************************************************************************/
void
Scsi2Atapi(PChannel pChan, PSCSI_REQUEST_BLOCK Srb)
{

 dprintk("++++++++++ Scsi2Atapi (0x%X) ++++++++++\n", Srb->Cdb[0]);
 dprintk("Srb->DataBuffer=0x%x\n", Srb->DataBuffer);
 dprintk("Srb->DataTransferLength=0x%lx\n", Srb->DataTransferLength);
 dprintk("CDB=0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
	Srb->Cdb[0], Srb->Cdb[1], Srb->Cdb[2], Srb->Cdb[3], Srb->Cdb[4], Srb->Cdb[5],
	Srb->Cdb[6], Srb->Cdb[7], Srb->Cdb[8], Srb->Cdb[9], Srb->Cdb[10], Srb->Cdb[11]);

 /*
  * Change the cdb length.
  */
 Srb->CdbLength = 12;

 /*
  * Because the block descripter and the header translation, we must
  * adjust the requested length.
  */
 Srb->DataTransferLength -= 4;

 /*
  * Record the original CDB for later restore.
  */
 memcpy(pChan->TempCdb, Srb->Cdb, MAXIMUM_CDB_SIZE);

 /*
  * Indicate that we have performed Scsi2Atapi function. And we must
  * restore the CDB back once the command complete.
  */
 pChan->ConvertCdb = TRUE;

 switch (Srb->Cdb[0])
    {
     /*
      * Convert the command from SCSIOP_MODE_SENSE (0x1A) to
      * SCSIOP_MODE_SENSE10 (0x5A).
      */
     case SCSIOP_MODE_SENSE:
	{
	 PSCSI_MODE_SENSE10 modeSense10 = (PSCSI_MODE_SENSE10) Srb->Cdb;
	 PSCSI_MODE_SENSE6  modeSense6 = (PSCSI_MODE_SENSE6) pChan->TempCdb;

	 /*
	  * 1. Zero out the whole CDB.
	  */
	 memset((PUCHAR)modeSense10, 0, MAXIMUM_CDB_SIZE);

	 /*
	  * 2. Fill in command code (SCSI_MODE_SENSE10).
	  */
	 modeSense10->OperationCode = ATAPI_MODE_SENSE;
	 modeSense10->Dbd = modeSense6->Dbd;
	 modeSense10->PageCode = modeSense6->PageCode;
	 modeSense10->Pc = modeSense6->Pc;
	 modeSense10->SubpageCode = modeSense6->SubpageCode;
	 modeSense10->AllocationLengthLsb = modeSense6->AllocationLength;
	 modeSense10->Control = modeSense6->Control;

	 /*
	  * 3. Becasuse we will fake a block descripter (-8), and
	  * translate the header (+4), so the requested length
	  * should be modified. That is, -8+4=-4 bytes.
	  */
	 modeSense10->AllocationLengthLsb -= 4;

	 break;
	}

     /*
      * Convert the command from SCSIOP_MODE_SELECT (0x15) to
      * SCSIOP_MODE_SELECT10 (0x5A).
      */
     case SCSIOP_MODE_SELECT:
	{
	 UCHAR		tempHeader[sizeof(PSCSI_MODE_PARAMETER_HEADER6)];
	 USHORT		byteCount;
	 PSCSI_MODE_PARAMETER_HEADER10 header10 = (PSCSI_MODE_PARAMETER_HEADER10)Srb->DataBuffer;
	 PSCSI_MODE_PARAMETER_HEADER6 header6 = (PSCSI_MODE_PARAMETER_HEADER6)tempHeader;
	 PSCSI_MODE_SELECT10 modeSelect10 = (PSCSI_MODE_SELECT10)Srb->Cdb;
	 PSCSI_MODE_SELECT6 modeSelect6 = (PSCSI_MODE_SELECT6)pChan->TempCdb;

	 /*
	  * First, convert the command block.
	  */

	 /*
	  * 1. Zero out the whole CDB.
	  */
	 memset((PUCHAR)modeSelect10, 0, MAXIMUM_CDB_SIZE);

	 /*
	  * 2. Fill in command code (SCSI_MODE_SENSE10).
	  */
	 modeSelect10->OperationCode = ATAPI_MODE_SELECT;
	 modeSelect10->SPBit = modeSelect6->SPBit;
	 modeSelect10->PFBit = modeSelect6->PFBit;
	 modeSelect10->ParameterListLengthLsb = modeSelect6->ParameterListLength;
	 modeSelect10->Control = modeSelect6->Control;

	 /*
	  * 3. Becasuse we will remove the block descripter (-8), and
	  * translate the header (+4), so the requested length should
	  * be modified. That is, -8+4=-4 bytes.
	  */
	 modeSelect10->ParameterListLengthLsb -= 4;

	 /*
	  * Second, convert the parameter page format from SCSI to ATAPI.
	  */

	 /*
	  * Remove the mode parameter data (except the header and the
	  * block descripter).
	  */
	 byteCount = modeSelect6->ParameterListLength - sizeof(SCSI_MODE_PARAMETER_HEADER6) - sizeof(SCSI_MODE_PARAMTER_BLOCK_DESCRIPTER);
	 if (byteCount > 0)
	    {
	     IT8212MoveMemory(
		(PUCHAR) header10 + sizeof(SCSI_MODE_PARAMETER_HEADER10),
		(PUCHAR) header10 + sizeof(SCSI_MODE_PARAMETER_HEADER6) + sizeof(SCSI_MODE_PARAMTER_BLOCK_DESCRIPTER),
		byteCount
			);
	    }

	 /*
	  * Keep the original header6 (4 bytes) in tempHeader for later use
	  */
	 memcpy(tempHeader, header10, sizeof(SCSI_MODE_PARAMETER_HEADER6));

	 /*
	  * Change the "mode parameter header(6)" to "mode parameter header(10)"
	  * Notice: Remove the block descripter in SCSI-2 command out. It
	  * won't be used in MMC.
	  */
	 memset((PUCHAR)header10, 0, sizeof(SCSI_MODE_PARAMETER_HEADER10));
	 header10->ModeDataLengthLsb = header6->ModeDataLength;
	 header10->MediumType = header6->MediumType;
	 header10->DeviceSpecificParameter = header6->DeviceSpecificParameter;
	 header10->BlockDescriptorLengthLsb = header6->BlockDescriptorLength;

	 /*
	  * ATAPI doesn't support block descripter, so remove it from the
	  * mode paramter.
	  */
	 header10->BlockDescriptorLengthLsb = 0;

	 break;
	}
    }

 dprintk("Srb->DataTransferLength=0x%lx\n", Srb->DataTransferLength);
 dprintk("Final CDB=0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
	Srb->Cdb[0], Srb->Cdb[1], Srb->Cdb[2], Srb->Cdb[3], Srb->Cdb[4], Srb->Cdb[5],
	Srb->Cdb[6], Srb->Cdb[7], Srb->Cdb[8], Srb->Cdb[9], Srb->Cdb[10], Srb->Cdb[11]);
 dprintk("---------- Scsi2Atapi (0x%X) ----------\n", Srb->Cdb[0]);

} /* end Scsi2Atapi */

/************************************************************************
 * Send ATAPI packet command to device.
 ************************************************************************/
UCHAR
AtapiSendCommand(PChannel pChan, PSCSI_REQUEST_BLOCK Srb)
{

 ULONG			bmAddress = pChan->dma_base;
 ULONG			i;
 ULONG			flags;
 UCHAR			statusByte;
 UCHAR			byteCountLow;
 UCHAR			byteCountHigh;
 UCHAR			useDMA;
 UCHAR			RevisionID = 0;
 UCHAR			bmClearStat;
 PITE_ADAPTER		pAdap = ite_adapters[0];

 dprintk("AtapiSendCommand: command 0x%X to device %d\n",
	Srb->Cdb[0], Srb->TargetId);

 /*
  * Default use PIO mode.
  */
 useDMA	= 0;
 pChan->ConvertCdb = FALSE;

 /*
  * Make sure command is to ATAPI device.
  */
 flags = pChan->DeviceFlags[Srb->TargetId & 1];
 if (flags & (DFLAGS_SANYO_ATAPI_CHANGER | DFLAGS_ATAPI_CHANGER))
    {
     if ((Srb->Lun) > (pChan->DiscsPresent[Srb->TargetId & 1] - 1))
	{
	 /*
	  * Indicate no device found at this address.
	  */
	 return SRB_STATUS_SELECTION_TIMEOUT;
	}
    }
 else if (Srb->Lun > 0)
    {
     return SRB_STATUS_SELECTION_TIMEOUT;
    }

 if (!(flags & DFLAGS_ATAPI_DEVICE))
    {
     return SRB_STATUS_SELECTION_TIMEOUT;
    }

 /*
  * Select device 0 or 1.
  */
 outb((UCHAR)(((Srb->TargetId & 0x1) << 4) | 0xA0),
	pChan->io_ports[ATAPI_SELECT_OFFSET]);

 /*
  * Try to enable interrupt again. (2003/02/25)
  */
#if (0)
 outb(0x00, pChan->io_ports[ATAPI_CONTROL_OFFSET]);
#endif

 /*
  * Verify that controller is ready for next command.
  */
 GetStatus(pChan, statusByte);

 dprintk("AtapiSendCommand: entered with status %x\n", statusByte);

 if (statusByte & IDE_STATUS_BUSY)
    {
     printk("AtapiSendCommand: device busy (%x)\n", statusByte);
     return SRB_STATUS_BUSY;
    }

 if (statusByte & IDE_STATUS_ERROR)
    {
     if (Srb->Cdb[0] != SCSIOP_REQUEST_SENSE)
	{
	 printk("AtapiSendCommand: error on entry: (%x)\n", statusByte);

	 /*
	  * Read the error reg. to clear it and fail this request.
	  */
	 return MapError(pChan, Srb);
	}
    }

 /*
  * If a tape drive doesn't have DSC set and the last command is
  * restrictive, don't send the next command. See discussion of
  * Restrictive Delayed Process commands in QIC-157.
  */
 if ((!(statusByte & IDE_STATUS_DSC)) && (flags & DFLAGS_TAPE_DEVICE) && pChan->RDP)
    {
     mdelay(1);
     printk("AtapiSendCommand: DSC not set. %x\n", statusByte);
     return SRB_STATUS_BUSY;
    }

 if (statusByte & IDE_STATUS_DRQ)
    {
     printk("AtapiSendCommand: enter with status (%x). Attempt to recover.\n",
	statusByte);

     /*
      * Try to drain the data that one preliminary device thinks that it has
      * to transfer. Hopefully this random assertion of DRQ will not be present
      * in production devices.
      */
     for (i = 0; i < 0x10000; i++)
	{
	 GetStatus(pChan, statusByte);

	 if (statusByte & IDE_STATUS_DRQ)
	    {
	     /*
	      * Note: The data register is always referenced as a 16-bit word.
	      */
	     inw(pChan->io_ports[ATAPI_DATA_OFFSET]);
	    }
	 else
	    {
	     break;
	    }
	}

     if (i == 0x10000)
	{
	 printk("AtapiSendCommand: DRQ still asserted.Status (%x)\n", statusByte);
	 printk("AtapiSendCommand: issued soft reset to Atapi device. \n");

	 AtapiSoftReset(pChan, Srb->TargetId);

	 /*
	  * Re-initialize Atapi device.
	  */
	 IssueIdentify(pChan, (Srb->TargetId & 1), IDE_COMMAND_ATAPI_IDENTIFY);

	 /*
	  * Inform the port driver that the bus has been reset.
	  */

	 /*
	  * Clean up device extension fields that AtapiStartIo won't.
	  */
	 pChan->ExpectingInterrupt = FALSE;
	 pChan->RDP = FALSE;

	 return SRB_STATUS_BUS_RESET;
	}
    }

 if (flags & (DFLAGS_SANYO_ATAPI_CHANGER | DFLAGS_ATAPI_CHANGER))
    {
     /*
      * As the cdrom driver sets the LUN field in the cdb, it must be removed.
      */
     Srb->Cdb[1] &= ~0xE0;

     if ((Srb->Cdb[0] == SCSIOP_TEST_UNIT_READY) && (flags & DFLAGS_SANYO_ATAPI_CHANGER))
	{
	 /*
	  * Torisan changer. TUR's are overloaded to be platter switches.
	  */
	 Srb->Cdb[7] = Srb->Lun;
	}
    }

 /*
  * Convert SCSI to ATAPI commands if needed
  */
 switch (Srb->Cdb[0])
    {
     case SCSIOP_MODE_SENSE:
     case SCSIOP_MODE_SELECT:
	 if (flags & DFLAGS_ATAPI_DEVICE)
	    {
	     Scsi2Atapi(pChan, Srb);
	    }
	 break;
    }

 if (pChan->UseDma[Srb->TargetId & 1])
    {
     switch (Srb->Cdb[0])
	{
	 case SCSIOP_READ:			/* (0x28)		*/
	 case 0xA8:				/* READ(12)		*/
	 case SCSIOP_READ_CD:

	     if (Srb->DataTransferLength == 0)
		{
		 break;
		}

	     /*
	      * First, switch to DMA or UDMA mode if running on Bypass mode.
	      */
	     if (pAdap->bypass_mode)
		{
		 IT8212SwitchDmaMode(pChan, Srb->TargetId);
		}

	     /*
	      * Check the SCATTER/GATHER count. The upper will give the
	      * different memory address depend on whether use_sg is used
	      * or not.
	      */
	     if (Srb->UseSg == 0)
		{
		 IdeBuildDmaTable(pChan, Srb);
		}
	     else
		{
		 IdeBuildDmaSgTable(pChan, Srb);
		}

	     bmClearStat = inb(bmAddress + 2);
	     if (Srb->TargetId & 0x01)
		{
		 bmClearStat = bmClearStat | BM_DRV1_DMA_CAPABLE |
			   BM_STAT_FLG_INT | BM_STAT_FLG_ERR;
		}
	     else
		{
		 bmClearStat = bmClearStat | BM_DRV0_DMA_CAPABLE |
			   BM_STAT_FLG_INT | BM_STAT_FLG_ERR;
		}

	     useDMA = 1;

	     outb(0, bmAddress);

	     /*
	      * Setup PRD table physical address.
	      */
	     outl(pChan->dmatable_dma, bmAddress + 4);

	     /*
	      * Clear the status.
	      */
	     outb(bmClearStat, bmAddress + 2);

	     break;
	} /* end switch (Srb->Cdb[0]) */
    }

 /*
  * Set data buffer pointer and words left.
  */
 pChan->DataBuffer = (PUSHORT)Srb->DataBuffer;

 if (useDMA)
    {
     pChan->WordsLeft = 0;
    }
 else
    {
     pChan->WordsLeft = Srb->DataTransferLength / 2;
    }

 outb((UCHAR)(((Srb->TargetId & 0x1) << 4) | 0xA0),
	pChan->io_ports[ATAPI_SELECT_OFFSET]);

 WaitOnBusy(pChan, statusByte);

 /*
  * Write transfer byte count to registers.
  */
 byteCountLow  = (UCHAR)(Srb->DataTransferLength & 0xFF);
 byteCountHigh = (UCHAR)(Srb->DataTransferLength >> 8);

 if (Srb->DataTransferLength >= 0x10000)
    {
     byteCountLow = byteCountHigh = 0xFF;
    }

 outb(byteCountLow,  pChan->io_ports[ATAPI_LCYL_OFFSET]);
 outb(byteCountHigh, pChan->io_ports[ATAPI_HCYL_OFFSET]);
 outb(0,	     pChan->io_ports[ATAPI_INTREASON_OFFSET]);
 outb(0,	     pChan->io_ports[ATAPI_UNUSED1_OFFSET]);
 outb(useDMA,	     pChan->io_ports[ATAPI_FEATURE_OFFSET]);

 WaitOnBusy(pChan, statusByte);

 if (flags & DFLAGS_INT_DRQ)
    {
     /*
      * This device interrupts when ready to receive the packet.
      *
      * Write ATAPI packet command.
      */
     outb(IDE_COMMAND_ATAPI_PACKET, pChan->io_ports[IDE_COMMAND_OFFSET]);

     printk("AtapiSendCommand: wait for int. to send packet. status (%x)\n",
	statusByte);

     pChan->ExpectingInterrupt = TRUE;

     return SRB_STATUS_PENDING;
    }
 else
    {
     /*
      * Write ATAPI packet command.
      */
     outb(IDE_COMMAND_ATAPI_PACKET, pChan->io_ports[IDE_COMMAND_OFFSET]);

     /*
      * Wait for DRQ.
      */
     WaitOnBusy(pChan, statusByte);
     WaitForDrq(pChan, statusByte);

     if (!(statusByte & IDE_STATUS_DRQ))
	{
	 printk("AtapiSendCommand: DRQ never asserted (%x)\n", statusByte);
	 return SRB_STATUS_ERROR;
	}
    }

 /*
  * Need to read status register.
  */
 GetBaseStatus(pChan, statusByte);

 /*
  * Send CDB to device.
  * After detecting DRQ, the host writes the 12 bytes(6 words) of Command
  * to the Data Register.
  */
 WaitOnBusy(pChan, statusByte);
 WriteBuffer(pChan, (PUSHORT)Srb->Cdb, 6);

 /*
  * If running on DMA mode, start BUS MASTER operation.
  */
 if (useDMA)
    {
     /*
      * If SCSIOP_READ command is sent to an Audio CD, error will be
      * returned. But the error will be blocked by our controller if bus
      * master operation started. So wait for a short period to check if
      * error occurs. If error occurs, don't start bus master operation.
      */
     if (RevisionID == 0x10)
	{
	 for (i = 0; i < 500; i++)
	    {
	     udelay(1);
	     statusByte = inb(bmAddress + 2);
	     if (statusByte & BM_STAT_FLG_INT)
		{
		 /*
		  * If error occurs, give up this round.
		  */
		 printk("AtapiSendCommand: command failed. Don't start bus master.");
		 printk("status=%x, i=%ld\n", statusByte, i);

		 pChan->ExpectingInterrupt = TRUE;
		 return SRB_STATUS_PENDING;
		}
	    }
	}

     if (Srb->SrbFlags & SRB_FLAGS_DATA_IN)
	{
	 outb(BM_CMD_FLG_START | BM_CMD_FLG_WRTTOMEM, bmAddress);
	}
     else if (Srb->SrbFlags & SRB_FLAGS_DATA_OUT)
	{
	 outb(BM_CMD_FLG_START | BM_CMD_FLG_WRTTODSK, bmAddress);
	}
    } /* end if (useDMA) */

 /*
  * Indicate expecting an interrupt and wait for it.
  */
 pChan->ExpectingInterrupt = TRUE;

 return SRB_STATUS_PENDING;

} /* end AtapiSendCommand */

/************************************************************************
 * Program ATA registers for IDE disk transfer.
 ************************************************************************/
ULONG
IdeSendCommand(PChannel pChan, PSCSI_REQUEST_BLOCK Srb)
{

 UCHAR			statusByte;
 ULONG			status;
 ULONG			i;
 Scsi_Cmnd *		pREQ;
 unsigned char *	request_buffer;
 PINQUIRYDATA		inquiryData;

 pREQ	    = Srb->pREQ;
 status	    = SRB_STATUS_SUCCESS;
 statusByte = 0;

 switch (Srb->Cdb[0])
    {
     case SCSIOP_INQUIRY:
	 dprintk("SCSIOP_INQUIRY\n");

	 /*
	  * Filter out all TIDs but 0 and 1 since this is an IDE interface
	  * which support up to two devices.
	  */
	 if ((pREQ->lun != 0) ||
            (!pChan->DeviceFlags[pREQ->target & 1] & DFLAGS_DEVICE_PRESENT))
	    {
	     /*
	      * Indicate no device found at this address.
	      */
	     status = SRB_STATUS_INVALID_TARGET_ID;
	     break;
	    }
	 else
	    {
	     request_buffer = Srb->DataBuffer;
	     inquiryData    = Srb->DataBuffer;

	     /*
	      * Zero INQUIRY data structure.
	      */
	     memset(request_buffer, 0, Srb->DataTransferLength);

	     /*
	      * Standard IDE interface only supports disks.
	      */
	     inquiryData->DeviceType = DIRECT_ACCESS_DEVICE;

	     /*
	      * Device type modifer.
	      */
	     request_buffer[1] = 0;

	     /*
	      * No ANSI/ISO compliance.
	      */
	     request_buffer[2] = 0;

	     /*
	      * Additional length.
	      */
	     request_buffer[4] = 31;
	     memcpy(&request_buffer[8], "ITE     ", 8);
	     memcpy(&request_buffer[16], "IT8212F         ", 16);
	     memcpy(&request_buffer[32], "1.3", 3);

	     /*
 	      * Set the removable bit, if applicable.
	      */
	     if (pChan->DeviceFlags[pREQ->target & 1] & DFLAGS_REMOVABLE_DRIVE)
		{
		 inquiryData->RemovableMedia = 1;
		}

	     status = SRB_STATUS_SUCCESS;
	    }

	 break;

     case SCSIOP_MODE_SENSE:
	 status = SRB_STATUS_INVALID_REQUEST;
	 break;

     case SCSIOP_TEST_UNIT_READY:
	 status = SRB_STATUS_SUCCESS;
	 break;

     case SCSIOP_READ_CAPACITY:
	 /*
	  * Claim 512 byte blocks (big-endian).
	  */
	 ((PREAD_CAPACITY_DATA)Srb->DataBuffer)->BytesPerBlock = 0x20000;

	 /*
	  * Calculate last sector.
	  */
	 if (pChan->IdentifyData[pREQ->target & 0x01].UserAddressableSectors == 0x0FFFFFFF)
	    {
	     i = pChan->IdentifyData[pREQ->target & 0x01].Capacity_48bit_LOW - 1;
	    }
	 else
	    {
	     i = pChan->IdentifyData[pREQ->target & 0x01].UserAddressableSectors - 1;
	    }

	 ((PREAD_CAPACITY_DATA)Srb->DataBuffer)->LogicalBlockAddress =
		(((PUCHAR)&i)[0] << 24) | (((PUCHAR)&i)[1] << 16) |
		(((PUCHAR)&i)[2] << 8)  | ((PUCHAR)&i)[3];

	 status = SRB_STATUS_SUCCESS;
	 break;

     case SCSIOP_VERIFY:
	 status = IdeVerify(pChan, Srb);
	 break;

     case SCSIOP_READ:
     case SCSIOP_WRITE:
	 status = IT8212ReadWrite(pChan, Srb);
	 break;

     case SCSIOP_START_STOP_UNIT:
	 /*
	  * Determine what type of operation we should perform
	  */
	 status = SRB_STATUS_SUCCESS;
	 break;

     case SCSIOP_REQUEST_SENSE:
	 /*
	  * This function makes sense buffers to report the results
	  * of the original GET_MEDIA_STATUS command
	  */
	 status = SRB_STATUS_INVALID_REQUEST;
	 break;

     default:
	 printk("IdeSendCommand: unsupported command %x\n", Srb->Cdb[0]);
	 status = SRB_STATUS_INVALID_REQUEST;
    } /* end switch */

 return status;

} /* end IdeSendCommand */

/************************************************************************
 * This routine is called from the SCSI port driver synchronized with
 * the kernel to start an IO request. If the current SRB is busy, return
 * FALSE, else return TURE.
 ************************************************************************/
BOOLEAN
AtapiStartIo(PChannel pChan, PSCSI_REQUEST_BLOCK Srb)
{

 ULONG			status = 0;

 /*
  * Determine which function.
  */
 switch (Srb->Function)
    {
     case SRB_FUNCTION_EXECUTE_SCSI:
	 /*
	  * Sanity check. Only one request can be outstanding on a
	  * controller.
 	  */
 	 if (pChan->CurrentSrb)
	    {
	     printk("AtapiStartIo: already have a request!\n");
	     status = SRB_STATUS_BUSY;
	     Srb->SrbStatus = SRB_STATUS_BUSY;
	     goto busy;
	    }

	 /*
	  * Indicate that a request is active on the controller.
	  */
	 pChan->CurrentSrb = Srb;
	 Srb->SrbStatus	   = SRB_STATUS_PENDING;

	 /*
 	  * Send command to device.
	  */
	 if (pChan->DeviceFlags[Srb->TargetId & 1] & DFLAGS_ATAPI_DEVICE)
	    {
	     /*
	      * If this is ATAPI device.
	      */
	     status = AtapiSendCommand(pChan, Srb);
	    }
	 else if (pChan->DeviceFlags[Srb->TargetId & 1] & DFLAGS_DEVICE_PRESENT)
	    {
	     /*
	      * If this is IDE device.
	      */
	     status = IdeSendCommand(pChan, Srb);
	    }
	 else
	    {
	     /*
	      * Nothing else.
	      */
	     status = SRB_STATUS_SELECTION_TIMEOUT;
	    }

	 break;

     case SRB_FUNCTION_IO_CONTROL:
	 /*
	  * IO control function.
	  */
	 printk("AtapiStartIo: IO control\n");
	 break;

    default:
	 /*
	  * Indicate unsupported command.
	  */
	 status = SRB_STATUS_INVALID_REQUEST;
	 break;

    } /* end switch */

 busy:
 if (status != SRB_STATUS_PENDING)
    {
     /*
      * Set status in SRB.
      */
     Srb->SrbStatus = (UCHAR)status;
     dprintk("AtapiStartIo: status=%lx\n", status);
     TaskDone(pChan, Srb);
    }

 return TRUE;

} /* end AtapiStartIo */

/************************************************************************
 * Convert Scsi_Cmnd structure to SCSI_REQUEST_BLOCK.
 ************************************************************************/
void
MapRequest(Scsi_Cmnd * pREQ, PSCSI_REQUEST_BLOCK Srb)
{

 Srb->Length 	= sizeof(SCSI_REQUEST_BLOCK);
 Srb->CdbLength	= pREQ->cmd_len;
 Srb->TargetId	= pREQ->target;
 Srb->Lun	= pREQ->lun;
 Srb->UseSg	= pREQ->use_sg;

 /*
  * Copy the actual command from Scsi_Cmnd to CDB.
  */
 memcpy(Srb->Cdb, pREQ->cmnd, Srb->CdbLength);

 /*
  * Always the SCSI_FUNCTION_EXECUTE_SCSI now.
  */
 Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;

 Srb->SrbStatus		    = 0;
 Srb->ScsiStatus	    = 0;
 Srb->SenseInfoBufferLength = 16;

 /*
  * The CDB's first byte is operation code.
  */
 if ((Srb->Cdb[0] == SCSIOP_WRITE6) || (Srb->Cdb[0] == SCSIOP_WRITE) ||
     (Srb->Cdb[0] == SCSIOP_MODE_SELECT10))
    {
     Srb->SrbFlags = SRB_FLAGS_DATA_OUT;
    }
 else
    {
     Srb->SrbFlags = SRB_FLAGS_DATA_IN;
    }

 Srb->TimeOutValue    = 0;
 Srb->SenseInfoBuffer = pREQ->sense_buffer;

 if (Srb->Cdb[0] == SCSIOP_REQUEST_SENSE)
    {
     Srb->DataTransferLength = 0x40;
     Srb->DataBuffer	     = pREQ->sense_buffer;
    }
 else
    {
     Srb->DataTransferLength = pREQ->request_bufflen;
     Srb->DataBuffer	     = pREQ->request_buffer;
    }

 if (pREQ->use_sg)
    {
     Srb->WorkingFlags |= SRB_WFLAGS_USE_SG;
    }

 Srb->pREQ = pREQ;

} /* end MapRequest */

/************************************************************************
 * A task execution has been done. For OS request, we need to Notify OS
 * and invoke next take which wait at queue.
 ************************************************************************/
void
TaskDone(PChannel pChan, PSCSI_REQUEST_BLOCK Srb)
{

 Scsi_Cmnd * pREQ = Srb->pREQ;

 pChan->CurrentSrb = NULL;
 pChan->RetryCount = 0;

 switch (SRB_STATUS(Srb->SrbStatus))
    {
     case SRB_STATUS_SUCCESS:
	 pREQ->result = (DID_OK << 16);
	 break;

     case SRB_STATUS_SELECTION_TIMEOUT:
	 pREQ->result = (DID_NO_CONNECT << 16);
	 break;

     case SRB_STATUS_BUSY:
	 pREQ->result =	(DID_BUS_BUSY << 16);
	 break;

     case SRB_STATUS_BUS_RESET:
	 pREQ->result =	(DID_RESET << 16);
	 break;

     case SRB_STATUS_INVALID_TARGET_ID:
     case SRB_STATUS_INVALID_PATH_ID:
     case SRB_STATUS_INVALID_LUN:
     case SRB_STATUS_NO_HBA:
	 pREQ->result = (DID_BAD_TARGET << 16);
	 break;

     case SRB_STATUS_NO_DEVICE:
	 pREQ->result = (DID_BAD_TARGET << 16);
	 break;

     case SRB_STATUS_ERROR:
	 pREQ->result = (DRIVER_SENSE << 24) | (DID_OK << 16) |
		 	(CHECK_CONDITION << 1);
	 break;
    }

 dprintk("TaskDone(pChan=%p, pREQ=%p, result=%x)\n",
	pChan, pREQ, pREQ->result);

 /*
  * Notify OS that this OS request has been done.
  */
 pREQ->scsi_done(pREQ);

 /*
  * Check the queue again.
  */
 TaskQueue();

} /* end TaskDone */

/************************************************************************
 * Start a command, doing convert first.
 ************************************************************************/
void
TaskStart(PChannel pChan, Scsi_Cmnd *pREQ)
{

 PSCSI_REQUEST_BLOCK	Srb;

 dprintk("TaskStart(pChan=%p, pREQ=%p)\n", pChan, pREQ);

 Srb = &pChan->_Srb;

 /*
  * Clear the SRB structure.
  */
 memset(Srb, 0, sizeof(SCSI_REQUEST_BLOCK));

 /*
  * Convert Scsi_Cmnd structure to SCSI_REQUEST_BLOCK.
  */
 MapRequest(pREQ, Srb);

 /*
  * Start IDE I/O command.
  */
 AtapiStartIo(pChan, Srb);

} /* end TaskStart */

/************************************************************************
 * Check if queue is empty. If there are request in queue, transfer the
 * request to HIM's request and execute the request.
 ************************************************************************/
void
TaskQueue(void)
{

 unsigned long		flags;
 Scsi_Cmnd *		SCpnt;
 PChannel		pChan;
 PITE_ADAPTER		pAdap;

 check_next:

 if (it8212_req_last != NULL)
    {
     spin_lock_irqsave(&queue_request_lock, flags);
     SCpnt = (Scsi_Cmnd *)it8212_req_last->SCp.ptr;

     if (it8212_req_last == SCpnt)
	{
	 it8212_req_last = NULL;
	}
     else
	{
	 it8212_req_last->SCp.ptr = (char *)SCpnt->SCp.ptr;
	}
     spin_unlock_irqrestore(&queue_request_lock, flags);

     /*
      * Check the command.
      */
     if (SCpnt->host)
	{
	 if ((SCpnt->channel != 0) || (SCpnt->target >= (4 * NumAdapters)))
	    {
	     /*
	      * Returns that we have a bad target.
	      */
	     printk("BAD TARGET\n");
	     SCpnt->result = (DID_BAD_TARGET << 16);
	     SCpnt->scsi_done(SCpnt);
	     goto check_next;
	    }
	}

     if (SCpnt->target >= 4)
	{
	 pAdap = ite_adapters[1];
	 if (SCpnt->target < 6)	 pChan = &pAdap->IDEChannel[0];
	 else			 pChan = &pAdap->IDEChannel[1];
	}
     else
	{
	 pAdap = ite_adapters[0];
	 if (SCpnt->target < 2)	 pChan = &pAdap->IDEChannel[0];
	 else			 pChan = &pAdap->IDEChannel[1];
	}

     TaskStart(pChan, SCpnt);
     return;
    }

} /* end TaskQueue */

/****************************************************************
 * Name:	iteraid_queuecommand
 * Description:	Process a queued command from the SCSI manager.
 * Parameters:	SCpnt - Pointer to SCSI command structure.
 *		done  - Pointer to done function to call.
 * Returns:	Status code.
 ****************************************************************/
int
iteraid_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
{

 unsigned long		flags;
 dprintk("##Queuecommand enter##\n");

 /*
  * Hooks the done routine.
  */
 SCpnt->scsi_done = (void *)done;

 spin_lock_irqsave(&queue_request_lock, flags);
 if (it8212_req_last == NULL)
    {
     SCpnt->SCp.ptr = (char *)SCpnt;
    }
 else
    {
     SCpnt->SCp.ptr = it8212_req_last->SCp.ptr;
     it8212_req_last->SCp.ptr = (char *)SCpnt;
    }
 it8212_req_last = SCpnt;
 spin_unlock_irqrestore(&queue_request_lock, flags);

 TaskQueue();

 dprintk("@@Queuecommand exit@@\n");
 return 0;

} /* end iteraid_queuecommand */

/****************************************************************
 * Name:	internal_done :LOCAL
 * Description:	Done handler for non-queued commands
 * Parameters:	SCpnt - Pointer to SCSI command structure.
 * Returns:	Nothing.
 ****************************************************************/
static void
internal_done(Scsi_Cmnd * SCpnt)
{

 SCpnt->SCp.Status++;

} /* end internal_done */

/****************************************************************
 * Name:	iteraid_command
 * Description:	Process a command from the SCSI manager.
 * Parameters:	SCpnt - Pointer to SCSI command structure.
 * Returns:	Status code.
 ****************************************************************/
int
iteraid_command(Scsi_Cmnd *SCpnt)
{

 DWORD		timeout;

 SCpnt->SCp.Status = 0;
 iteraid_queuecommand(SCpnt, internal_done);

 /*
  * Should be longer than hard-reset time.
  */
 timeout = jiffies + 60 * HZ;
 while (!SCpnt->SCp.Status && time_before(jiffies, timeout))
    {
     barrier();
    }

 if (!SCpnt->SCp.Status) SCpnt->result = (DID_ERROR << 16);

 return SCpnt->result;

} /* end iteraid_command */

/************************************************************************
 * Enables/disables media status notification.
 ************************************************************************/
void
IdeMediaStatus(BOOLEAN	EnableMSN, PChannel pChan, ULONG Device)
{

 UCHAR			statusByte;
 UCHAR			errorByte;

 statusByte = 0;

 if (EnableMSN == TRUE)
    {
     /*
      * If supported enable Media Status Notification support.
      */
     if ((pChan->DeviceFlags[Device] & DFLAGS_REMOVABLE_DRIVE))
	{
	 outb((UCHAR) (0x95), pChan->io_ports[IDE_FEATURE_OFFSET]);
	 outb(IDE_COMMAND_ENABLE_MEDIA_STATUS, pChan->io_ports[IDE_COMMAND_OFFSET]);

	 WaitOnBaseBusy(pChan, statusByte);

	 if (statusByte & IDE_STATUS_ERROR)
	    {
	     /*
	      * Read the error register.
	      */
	     errorByte = inb(pChan->io_ports[IDE_ERROR_OFFSET]);

	     printk("IdeMediaStatus: error enabling media status. status %u, error byte %u\n",
		statusByte, errorByte);
	    }
	 else
	    {
	     pChan->DeviceFlags[Device] |= DFLAGS_MEDIA_STATUS_ENABLED;
	     printk("IdeMediaStatus: media status notification supported!\n");
	     pChan->ReturningMediaStatus = 0;
	    }
	}
    }
 else /* end if EnableMSN == TRUE */
    {
     /*
      * Disable if previously enabled.
      */
     if ((pChan->DeviceFlags[Device] & DFLAGS_MEDIA_STATUS_ENABLED))
	{
	 outb((UCHAR)(0x31), pChan->io_ports[IDE_FEATURE_OFFSET]);
	 outb(IDE_COMMAND_ENABLE_MEDIA_STATUS,
			pChan->io_ports[IDE_COMMAND_OFFSET]);

	 WaitOnBaseBusy(pChan, statusByte);
	 pChan->DeviceFlags[Device] &= ~DFLAGS_MEDIA_STATUS_ENABLED;
	}
    }

} /* end IdeMediaStatus */

/************************************************************************
 * Issue IDENTIFY command to a device.
 * Either the standard (EC) or the ATAPI packet (A1) IDENTIFY.
 ************************************************************************/
BOOLEAN
IssueIdentify(PChannel pChan, UCHAR DeviceNumber, UCHAR Command)
{

 ULONG			i;
 ULONG			j;
 UCHAR			statusByte = 0;

 /*
  * Check that the status register makes sense.
  */
 GetBaseStatus(pChan, statusByte);

 if (Command == IDE_COMMAND_IDENTIFY)
    {
     /*
      * Mask status byte ERROR bits.
      */
     statusByte &= ~(IDE_STATUS_ERROR | IDE_STATUS_INDEX);

     dprintk("IssueIdentify: checking for IDE. status (%x)\n", statusByte);

     /*
      * Check if register value is reasonable.
      */
     if (statusByte != IDE_STATUS_IDLE)
	{
	 /*
	  * Reset the channel.
	  */
	 printk("IssueIdentify: resetting channel.\n");
	 IdeHardReset(pChan, statusByte);

	 outb((UCHAR)((DeviceNumber << 4) | 0xA0), pChan->io_ports[IDE_SELECT_OFFSET]);
	 GetBaseStatus(pChan, statusByte);
	 statusByte &= ~IDE_STATUS_INDEX;

	 if (statusByte != IDE_STATUS_IDLE)
	    {
	     /*
	      * Give up on this.
	      */
	     printk("IssueIdentify(IDE): disk[%d] not ready. status=0x%x\n",
		     DeviceNumber, statusByte);
	     return FALSE;
	    }
	}
    }
 else
    {
     dprintk("IssueIdentify: checking for ATAPI. status (%x)\n", statusByte);
     if ((statusByte & IDE_STATUS_BUSY) || (statusByte & IDE_STATUS_DRQ))
	{
	 /*
	  * Reset the device.
	  */
	 dprintk("IssueIdentify: resetting device.\n");
	 AtapiSoftReset(pChan, DeviceNumber);

	 outb((UCHAR)((DeviceNumber << 4) | 0xA0), pChan->io_ports[IDE_SELECT_OFFSET]);
	 GetBaseStatus(pChan, statusByte);
	 if (statusByte != 0)
	    {
	     /*
	      * Give up on this.
	      */
	     printk("IssueIdentify(ATAPI): disk[%d] not ready. status=0x%x\n", DeviceNumber, statusByte);
	     return FALSE;
	    }
	}
    }

 for (j = 0; j < 2; j++)
    {
     /*
      * Wait for device ready (Not Busy and Not DRQ).
      */
     outb((UCHAR)((DeviceNumber << 4) | 0xA0), pChan->io_ports[IDE_SELECT_OFFSET]);
     WaitForDeviceReady(pChan, statusByte);
     if ((statusByte & IDE_STATUS_BUSY) || (statusByte & IDE_STATUS_DRQ))
	{
	 printk("IssueIdentify: disk[%d] not ready. status=0x%x\n", DeviceNumber, statusByte);
	 continue;
	}

     /*
      * Send IDENTIFY command.
      */
     outb(Command, pChan->io_ports[IDE_COMMAND_OFFSET]);

     /*
      * Wait for DRQ.
      */
     WaitForBaseDrq(pChan, statusByte);
     if (!(statusByte & IDE_STATUS_DRQ))
	{
	 printk("IssueIdentify: disk[%d] DRQ never asserted. status=%x\n", DeviceNumber, statusByte);

	 /*
	  * Give one more chance.
	  */
	 if (Command == IDE_COMMAND_IDENTIFY)
	    {
	     IdeHardReset(pChan, statusByte);
	    }
	 else
	    {
	     AtapiSoftReset(pChan, DeviceNumber);
	    }
	}
     else
	{
	 break;
	}
    }

 /*
  * Check for error on really stupid master devices that assert random
  * patterns of bits in the status register at the slave address.
  */
 outb((UCHAR)((DeviceNumber << 4) | 0xA0), pChan->io_ports[IDE_SELECT_OFFSET]);
 GetBaseStatus(pChan, statusByte);
 if (statusByte & IDE_STATUS_ERROR)
    {
     printk("IssueIdentify: disk[%d] returns error status\n", DeviceNumber);
     return FALSE;
    }

 dprintk("IssueIdentify: status before read words %x\n", statusByte);

 /*
  * Suck out 256 words. After waiting for one model that asserts busy
  * after receiving the Packet Identify command.
  */
 WaitOnBusy(pChan, statusByte);

 if (!(statusByte & IDE_STATUS_DRQ)) { return FALSE; }

 ReadBuffer(pChan, (PUSHORT)&pChan->FullIdentifyData, 256);

 /*
  * Check out a few capabilities / limitations of the device.
  * 01/29/2003
  */
 if (pChan->FullIdentifyData.SpecialFunctionsEnabled & 1)
    {
     /*
      * Determine if this drive supports the MSN functions.
      */
     printk("Marking drive %x as removable. SFE = %x\n",
	DeviceNumber, pChan->FullIdentifyData.SpecialFunctionsEnabled);
     pChan->DeviceFlags[DeviceNumber] |= DFLAGS_REMOVABLE_DRIVE;
    }

 memcpy(&pChan->IdentifyData[DeviceNumber], &pChan->FullIdentifyData,
		sizeof(IDENTIFY_DATA2));

 if (pChan->IdentifyData[DeviceNumber].GeneralConfiguration & 0x20 &&
        Command != IDE_COMMAND_IDENTIFY)
    {
     /*
      * This device interrupts with the assertion of DRQ after receiving
      * Atapi Packet Command.
      */
     pChan->DeviceFlags[DeviceNumber] |= DFLAGS_INT_DRQ;

     dprintk(KERN_NOTICE "Device interrupts on assertion of DRQ.\n");
    }
 else
    {
     dprintk(KERN_NOTICE "Device does't interrupt on assertion of DRQ.\n");
    }

 if (((pChan->IdentifyData[DeviceNumber].GeneralConfiguration & 0xF00)
	== 0x100) && Command != IDE_COMMAND_IDENTIFY)
    {
     /*
      * This is a tape.
      */
     pChan->DeviceFlags[DeviceNumber] |= DFLAGS_TAPE_DEVICE;
     printk(KERN_NOTICE "IssueIdentify: device is a tape drive.\n");
    }
 else
    {
     dprintk(KERN_NOTICE "IssueIdentify: device is not a tape drive.\n");
    }

 /*
  * Work around for some IDE and one model Atapi that will present more
  * then 256 bytes for the Identify data.
  */
 WaitOnBaseBusy(pChan, statusByte);

 for (i = 0; i < 0x10000; i++)
    {
     GetStatus(pChan, statusByte);

     if (statusByte & IDE_STATUS_DRQ)
	{
         /*
          * Suck out any remaining bytes and throw away.
          */
	 inw(pChan->io_ports[IDE_DATA_OFFSET]);
	}
     else
	{
	 break;
	}
    }

 return TRUE;

} /* end IssueIdentify() */

/************************************************************************
 * Check this is the IDE or ATAPI disk then identify it.
 ************************************************************************/
static BOOLEAN
iteraid_find_device(PChannel pChan, UCHAR channel)
{

 BOOLEAN		deviceResponded = FALSE;
 UCHAR			deviceNumber;
 UCHAR			signatureLow;
 UCHAR			signatureHigh;
 UCHAR			statusByte = 0;


 /*
  * Clear expecting interrupt flag and current SRB field.
  */
 pChan->ExpectingInterrupt = FALSE;
 pChan->CurrentSrb	   = NULL;

 /*
  * Search for devices in each channel.
  */
 for (deviceNumber = 0; deviceNumber < 2; deviceNumber++)
    {
     /*
      * Select the device.
      */
     outb((UCHAR)((deviceNumber << 4) | 0xA0), pChan->io_ports[IDE_SELECT_OFFSET]);

     /*
      * Disable interrupts during initialization.
      */
     outb(IDE_DC_DISABLE_INTERRUPTS, pChan->io_ports[IDE_CONTROL_OFFSET]);

     /*
      * Check here for some SCSI adapters that incorporate IDE emulation.
      */
     statusByte = inb(pChan->io_ports[IDE_CONTROL_OFFSET]);

     /*
      * Do soft reset on selected device. (AtapiSoftReset)
      */
     AtapiSoftReset(pChan, deviceNumber);
     WaitOnBusy(pChan, statusByte);

     signatureLow  = inb(pChan->io_ports[IDE_MIDCYL_OFFSET]);
     signatureHigh = inb(pChan->io_ports[IDE_HCYL_OFFSET]);

     if (signatureLow == 0x14 && signatureHigh == 0xEB)
	{
	 /*
	  * ATAPI signature found. Issue ATAPI packet identify command.
	  */
	 if (IssueIdentify(pChan, deviceNumber, IDE_COMMAND_ATAPI_IDENTIFY))
	    {
	     /*
	      * Indicate ATAPI device.
	      */
	     printk("iteraid_find_device: channel %x device %x is ATAPI.\n",
		channel, deviceNumber);

	     pChan->DeviceFlags[deviceNumber] |= DFLAGS_ATAPI_DEVICE;
	     pChan->DeviceFlags[deviceNumber] |= DFLAGS_DEVICE_PRESENT;
	     pChan->DeviceFlags[deviceNumber] &= ~DFLAGS_CONFIG_CHANGED;

	     deviceResponded = TRUE;

	     GetStatus(pChan, statusByte);
	     if (statusByte & IDE_STATUS_ERROR)
		{
		 AtapiSoftReset(pChan, deviceNumber);
		}
	    }
	 else
	    {
	     /*
	      * Indicate no working device.
	      */
	     printk("iteraid_find_device: channel %x device %x doesn't respond.\n",
		channel, deviceNumber);
	     pChan->DeviceFlags[deviceNumber] &= ~DFLAGS_DEVICE_PRESENT;
	    }
	}
     else
	{
	 /*
	  * Select the device.
	  */
	 outb((UCHAR)((deviceNumber << 4) | 0xA0), pChan->io_ports[IDE_SELECT_OFFSET]);

	 /*
	  * Check here for some SCSI adapters that incorporate IDE emulation.
	  */
	 GetStatus(pChan, statusByte);

	 /*
	  * No Disk.
	  */
	 if (statusByte == 0xFF || statusByte == 0x7F || statusByte == 0x0)
	    {
	     dprintk("FindDevices: cannot find IDE device. status = %x\n", statusByte);
	     continue;
	    }

	 /*
	  * Issue IDE Identify. If an ATAPI device is actually present,
	  * the signature will be asserted, and the drive will be
	  * recognized as such.
	  */
	 if (IssueIdentify(pChan, deviceNumber, IDE_COMMAND_IDENTIFY))
	    {
	     /*
	      * IDE drive found.
	      */
	     printk(KERN_WARNING "FindDevices: device %u is IDE\n",
		(channel * 2) + deviceNumber);

	     pChan->DeviceFlags[deviceNumber] |= DFLAGS_DEVICE_PRESENT;
	     pChan->DeviceFlags[deviceNumber] &= ~DFLAGS_ATAPI_DEVICE;
	     pChan->DeviceFlags[deviceNumber] &= ~DFLAGS_CONFIG_CHANGED;

	     deviceResponded = TRUE;
	    }
	 else
	    {
	     printk(KERN_WARNING "FindDevices: device %u is not present\n",
		(channel * 2) + deviceNumber);
	     pChan->DeviceFlags[deviceNumber] &= ~DFLAGS_DEVICE_PRESENT;
	    }
	}
    }

 return deviceResponded;

} /* end iteraid_find_device */

/************************************************************************
 * IDE disk hardware initialize.
 ************************************************************************/
BOOLEAN
AtapiHwInitialize(PITE_ADAPTER pAdap, PChannel pChan, UCHAR channel)
{

 UCHAR			i;
 UCHAR			statusByte = 0;

 /*
  * For two devices in this channel.
  */
 for (i = 0; i < 2; i++)
    {
     /*
      * only check in Fireware mode.
      */
     if (pAdap->bypass_mode == FALSE)
	{
	 outb((UCHAR) (0xA0 | ((UCHAR) i << 4)), pChan->io_ports[IDE_SELECT_OFFSET]);

	 /*
	  * Check if card at this address.
	  */
	 outb(0xAA, pChan->io_ports[IDE_MIDCYL_OFFSET]);

	 /*
	  * Check if indentifier can be read back.
	  */
	if ((statusByte = inb(pChan->io_ports[IDE_MIDCYL_OFFSET])) != 0xAA)
	   {
	     printk("AtapiHwInitialize: identifier read back from (%x, %x) = %x\n",
	     channel, i, statusByte);

	     /*
	      * ***** Dont free it....For later use *****
	      * ScsiPortFreeDeviceBase(HwDeviceExtension, ioSpace1);
	      */
	     continue;
	    }

	 printk("AtapiHwInitialize: found ATA device (%x, %x)n", channel, i);
	}

    }

 return TRUE;

} /* end AtapiHwInitialize */

/************************************************************************
 * Initialize a adapter, return 0 means success.
 ************************************************************************/
static int
iteraid_init(PITE_ADAPTER pAdap, struct pci_dev *pPciDev)
{

 unsigned char		z;
 unsigned char		i;
 unsigned char		j;
 unsigned int		set_irq;
 ULONG			control_addr;	/* Control reg base address	*/
 ULONG			base_addr;	/* IDE I/O port base address	*/
 ULONG			bm_base_addr;	/* Bus Master base address	*/
 PChannel		pChan;		/* Use for each channel		*/
 dprintk("iteraid_init enter\n");

 /*
  * Common settings.
  */
 pAdap->pci_bus	 = pPciDev->bus->number;
 pAdap->devfn	 = pPciDev->devfn;
 pAdap->irq	 = pPciDev->irq;
 pAdap->irqOwned = 0;

 printk(KERN_NOTICE "Found Controller: %s\n", pAdap->name);

 /*
  * Allocate buffer for IDE channles (One IT8212 supports two channels)
  */
 pAdap->IDEChannel =
 (PChannel)kmalloc(sizeof(Channel) * pAdap->num_channels, GFP_ATOMIC);

 if (pAdap->IDEChannel == 0)
    {
     printk("iteraid_init: pChan allocate failed.\n");
     return -1;
    }

 memset(pAdap->IDEChannel, 0, sizeof(Channel) * pAdap->num_channels);

 set_irq = 1;
 for (i = 0; i < NumAdapters; i++)
    {
     if (ite_adapters[i]->irqOwned == pAdap->irq) set_irq = 0;
    }

 /*
  * Request the irq (share irq) and hook the interrupt service routine.
  */
 if (set_irq)
    {
     if (request_irq(pAdap->irq, Irq_Handler, SA_SHIRQ, PROC_DIR_NAME, pAdap) < 0)
	{
	 printk("iteraid_init: unable to allocate IRQ for %s\n", pAdap->name);
	 return -1;
	}
     pAdap->irqOwned = pAdap->irq;
    }

 /*
  * Get the IDE port and DMA registers.
  */
 for (i = 0; i < pAdap->num_channels; i++)
    {
     pChan = &pAdap->IDEChannel[i];
     /*
      * Reference the book "LINUX DEVICE DRIVER 2nd", Page 484
      * unsigned long pci_resource_start(struct pci_dev *dev, int bar);
      */
     base_addr	     = pci_resource_start(pPciDev, i * 2);
     control_addr    = pci_resource_start(pPciDev, i * 2 + 1);
     bm_base_addr    = pci_resource_start(pPciDev, 4);
     pChan->dma_base = bm_base_addr + i * 8;
     for (j = 0; j <= IDE_STATUS_OFFSET; j++)
	{
	 pChan->io_ports[j] = base_addr;
	 base_addr += 1;
	}
     pChan->io_ports[IDE_CONTROL_OFFSET] = control_addr + 2;
    }

 /*
  * Initialize channels.
  */
 for (z = 0; z < pAdap->num_channels; z++)
    {
     pChan	    = &pAdap->IDEChannel[z];
     pChan->pPciDev = pPciDev;
     pChan->channel = z;

     /*
      * This section should be masked off if BIOS is ready.
      */
#    if (MARK_DEBUG_BYPASS_MODE)
     /*
      * BIOS is not ready, so I change to ByPass Mode by myself.
      */
     pAdap->bypass_mode = TRUE;

     /*
      * Change to bypass mode.
      */
     IT8212InitBypassMode(pPciDev);
#    endif

     /*
      * Hardware initialize.
      */
#    if (0)
     AtapiHwInitialize(pAdap, pChan, z);
#    endif

     /*
      * Find and identify the IDE or ATAPI device.
      */
     iteraid_find_device(pChan, z);

     /*
      * Set the best transfer mode.
      */
#    if (MARK_SET_BEST_TRANSFER)
     IT8212SetBestTransferMode(pAdap, pChan, z);
#    endif

     /*
      * Set Scatter/Gather List buffer for the channel.
      */
     IdeSetupDma(pChan, pChan->dma_base, 8);
    }

 dprintk("iteraid_init exit\n");
 return 0;

} /* end iteraid_init */

/************************************************************************
 * This function will find and initialize any cards.
 ************************************************************************/
int
iteraid_detect(Scsi_Host_Template *tpnt)
{

 UCHAR			i;
 UCHAR			j;
 UCHAR			mode;
 UCHAR			pci_id;
 PChannel		pChan;
 PITE_ADAPTER		pAdap;
 struct pci_dev *	pPciDev;
 dprintk("iteraid_detect enter\n");

 /*
  * Search ITE IT8212 chip.
  */
 pPciDev = NULL;
 pci_id  = 0;
 while ((pPciDev = pci_find_device(ITE_VENDOR_ID, ITE_DEVICE_ID, pPciDev)))
    {
     if (PCI_FUNC(pPciDev->devfn)) continue;

     /*
      * Allocate memory for Adapter.
      */
     pAdap = (PITE_ADAPTER)kmalloc(sizeof(ITE_ADAPTER), GFP_ATOMIC);
     if (pAdap == NULL)
	{
	 printk("iteraid_detect: pAdap allocate failed.\n");
	 continue;
	}

     memset(pAdap, 0, sizeof(ITE_ADAPTER));
     pAdap->name	 = CONTROLLER_NAME_IT8212;
     pAdap->num_channels = 2;
     pAdap->pci_dev	 = pPciDev;

     /*
      * Check if we are in bypass(transparent) or firmware mode.
      */
     pci_read_config_byte(pPciDev, 0x50, &mode);
     if (mode & 1)
        {
	 dprintk("Firmware mode in PCI#%d\n", pci_id);
	 pAdap->bypass_mode = FALSE;
	}
     else
	{
	 dprintk("Transparent mode in PCI#%d\n", pci_id);
	 pAdap->bypass_mode = TRUE;
	}

     if (iteraid_init(pAdap, pPciDev) == 0)
	{
	 ite_adapters[NumAdapters++] = pAdap;
	}
     pci_id++;
    }

 /*
  * Reenable interrupt after initialization. 2003/04/28
  */
 for (i = 0; i < NumAdapters; i++)
    {
     pAdap = ite_adapters[i];
     for (j = 0; j < pAdap->num_channels; j++)
	{
	 pChan = &pAdap->IDEChannel[j];
	 outb(IDE_DC_REENABLE_CONTROLLER, pChan->io_ports[IDE_CONTROL_OFFSET]);
	}
    }

 if (NumAdapters)
    {
     /*
      * Register a virtual host.
      */
     ite_vhost = scsi_register(tpnt, 0);

     ite_vhost->io_port		= 0;
     ite_vhost->n_io_port	= 0;
     ite_vhost->max_channel	= 0;
     ite_vhost->max_id		= MAX_DEVICES;
     ite_vhost->max_lun		= 1;
     scsi_set_pci_device(ite_vhost, pPciDev);

     /*
      * Register the driver as a character device, for applications to
      * acess it for ioctls. Ideally, this should go in the init_module()
      * routine, but since it is hidden in the file "scsi_module.c" (
      * included in the end), we define it here. First argument (major)
      * to register_chrdev implies a dynamic major number allocation.
      */
     ite_major = register_chrdev(0, "itedev", &itedev_fops);

     /*
      * Register the Shutdown Notification hook in the kernel.
      */
     register_reboot_notifier(&ite_notifier);

     /*
      * Initialize ioctl semphore.
      */
     init_MUTEX(&mimd_entry_mtx);
    }

 dprintk("iteraid_detect exit\n");
 return 1;

} /* end iteraid_detect() */

/************************************************************************
 * Name:	iteraid_release
 * Description:	Release resources allocated for a single each adapter.
 * Parameters:	pshost - Pointer to SCSI command structure.
 * Returns:	zero.
 ************************************************************************/
int
iteraid_release(struct Scsi_Host *pshost)
{

 UCHAR			i;
 PITE_ADAPTER		pAdap;

 /*
  * Unregister the character device.
  */
 if (ite_major > 0)
    {
     unregister_chrdev(ite_major, "itedev");
     ite_major = -1;
    }

 /*
  * Free irq and memory.
  */
 for (i = 0; i < NumAdapters; i++)
    {
     pAdap = ite_adapters[i];

     if (pAdap->irqOwned) free_irq(pAdap->irq, pAdap);
     if (pAdap->IDEChannel != NULL) { kfree(pAdap->IDEChannel); }
     if (pAdap		   != NULL) { kfree(pAdap); }
    }

 /*
  * Unregister the reboot notifier.
  */
 unregister_reboot_notifier(&ite_notifier);

 /*
  * Tell kernel scsi-layer we are gone.
  */
 scsi_unregister(pshost);

 return 0;

} /* end iteraid_Release */

/************************************************************************
 * Name:	iteraid_old_abort
 * Description:	Process the Abort command from the SCSI manager.
 * Parameters:	SCpnt - Pointer to SCSI command structure.
 * Returns:	Always snooze(A short sleep).
 ************************************************************************/
int
iteraid_old_abort(Scsi_Cmnd *SCpnt)
{

#if (0)
 return	SCSI_ABORT_SNOOZE;
#else
 return SCSI_ABORT_ERROR;
#endif

} /* end iteraid_old_abort */

/************************************************************************
 * Name:	iteraid_old_reset
 * Description:	Process the Reset command from the SCSI manager.
 * Parameters:	SCpnt - Pointer to SCSI command structure.
 *		flags - Flags about the reset command
 * Returns:	No active command at this time, so this means
 *		that each time we got some kind of response the
 *		last time through.  Tell the mid-level code to
 *		request sense information in order to decide what
 *		to do next.
 ************************************************************************/
int
iteraid_old_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags)
{

 return SCSI_RESET_SUCCESS;

} /* end iteraid_old_reset */

/************************************************************************
 * This is the new scsi eh reset function.
 ************************************************************************/
int
iteraid_reset_eh(Scsi_Cmnd *SCpnt)
{

 UCHAR			i;
 UCHAR			j;
 PChannel		pChan;
 PITE_ADAPTER		pAdap;

 if (SCpnt == NULL)
    {
     printk("iteraid_reset_eh: invalid Scsi_Cmnd\n");
     return FAILED;
    }

 for (i = 0; i < NumAdapters; i++)
    {
     pAdap = ite_adapters[i];
     for (j = 0; j < pAdap->num_channels; j++)
	{
	 pChan = &pAdap->IDEChannel[j];
	 AtapiResetController(pAdap, pChan);
	}
    }

 return SUCCESS;

} /* end iteraid_reset_eh */

/************************************************************************
 * The new error handling code.
 ************************************************************************/
int
iteraid_abort_eh(Scsi_Cmnd *SCpnt)
{

 if (SCpnt == NULL)
    {
     printk("iteraid_reset_eh: invalid Scsi_Cmnd\n");
     return FAILED;
    }

 return SUCCESS;

} /* end iteraid_abort_eh */

/************************************************************************
 * Name:	iteraid_biosparam
 * Description:	Process the biosparam request from the SCSI manager to
 *		return C/H/S data.
 * Parameters:	disk - Pointer to SCSI disk structure.
 *		dev  - Major/minor number from kernel.
 *		geom - Pointer to integer array to place geometry data.
 * Returns:	zero.
 ************************************************************************/
int
iteraid_biosparam(Scsi_Disk *disk, kdev_t dev, int geom[])
{

 int			heads;
 int			sectors;
 int			cylinders;

 /*
  * Default heads (64) & sectors (32)
  * Handle extended translation size for logical drives > 1Gb
  */
 if (disk->capacity >= 0x200000)
    {
     heads   = 255;
     sectors = 63;
    }
 else
    {
     heads   = 64;
     sectors = 32;
    }

 cylinders = disk->capacity / (heads * sectors);

 /*
  * Return result
  */
 geom[0] = heads;
 geom[1] = sectors;
 geom[2] = cylinders;

 return 0;

} /* end iteraid_biosparam */

/************************************************************************
 * Shutdown routine.
 ************************************************************************/
static int
ite_halt(struct notifier_block *nb, ulong event, void *buf)
{

 if (event != SYS_RESTART && event != SYS_HALT && event != SYS_POWER_OFF)
    {
     return NOTIFY_DONE;
    }

 unregister_reboot_notifier(&ite_notifier);

 return NOTIFY_OK;

} /* end ite_halt */

/************************************************************************
 * PROC information.
 ************************************************************************/
int
iteraid_proc_info
(
char *			buffer,
char **			start,
off_t			offset,
int			length,
int			host_no,
int			inout
)
{

 return 0;

} /* end iteraid_proc_info */

/************************************************************************
 * IOCTL open entry.
 ************************************************************************/
static int
itedev_open(struct inode *inode,  struct file *filep)
{

 MOD_INC_USE_COUNT;

 return 0;

} /* end itedev_open */

/************************************************************************
 * IOCTL code entry.
 ************************************************************************/
static int
itedev_ioctl_entry
(
 struct inode *		inode,
 struct	file *		filep,
 unsigned int		cmd,
 unsigned long		arg
)
{

 int			ret = -1;

 /*
  * We do not allow parallel ioctls to the driver as of now.
  */
 down(&mimd_entry_mtx);
 ret = itedev_ioctl(inode, filep, cmd, arg);
 up(&mimd_entry_mtx);

 return ret;

} /* end itedev_ioctl_entry */

/************************************************************************
 * Real IOCTL code.
 ************************************************************************/
static int
itedev_ioctl
(
 struct inode *		inode,
 struct	file *		filep,
 unsigned int		cmd,
 unsigned long		arg
)
{

 UCHAR			diskArrayId;
 UCHAR			statusByte = 0;
 UCHAR			srbStatus;
 UCHAR			progress = 0;
 UCHAR			status = 0;
 uioctl_t *		pioc;
 PITE_ADAPTER		pAdap;
 PChannel		pChan;
 PRAID_REBUILD_INFO	rebuild_info;
 dprintk("itedev_ioctl enter\n");

 /*
  * Extract the type and number bitfield.
  */
 if (_IOC_TYPE(cmd) != ITE_IOCMAGIC) return -EINVAL;

 /*
  * Allocate space for ioctl data structure.
  */
 if ((pioc = kmalloc(sizeof(uioctl_t), GFP_KERNEL)) == NULL)
    {
     printk("itedev_ioctl: error kmalloc on ioctl\n");
     return -ENOMEM;
    }

 /*
  * Get the user ioctl structure.
  */
 if (copy_from_user(pioc, (uioctl_t *)arg, sizeof(uioctl_t)))
    {
     kfree(pioc);
     return -EFAULT;
    }

 /*
  * Check which command to do.
  */
 switch (cmd)
    {
     case ITE_IOC_GET_PHY_DISK_STATUS:
	 dprintk("ITE_IOC_GET_PHY_DISK_STATUS\n");

	 /*
	  * Get the physical disk status.
	  */
	 status = IT8212GetChipStatus(pioc);
	 return 0;

     case ITE_IOC_CREATE_DISK_ARRAY:
	 dprintk("ITE_IOC_CREATE_DISK_ARRAY\n");

	 /*
	  * Create disk array.
	  */
	 status = IT8212CreateDiskArray(pioc);

	 if (status != SRB_STATUS_SUCCESS) return status;

	 status = IT8212ErasePartition(pioc);
	 return 0;

     case ITE_IOC_REBUILD_START:
	 dprintk("ITE_IOC_REBUILD_START\n");

	 /*
	  * Rebuild array.
	  */
	 status = IT8212Rebuild(pioc);
	 put_user(status, (UCHAR *)pioc->data);
	 return 0;

     case ITE_IOC_GET_REBUILD_STATUS:
	 dprintk("ITE_IOC_GET_REBUILD_STATUS\n");

	 pAdap = ite_adapters[0];

	 /*
	  * Get the rebuild disk ID.
	  */
	 rebuild_info = (PRAID_REBUILD_INFO) pioc->data;
	 diskArrayId = rebuild_info->DiskArrayId;

	 /*
	  * Select channel.
	  */
	 if (diskArrayId < 2)
	    {
	     pChan = &pAdap->IDEChannel[0];
	    }
	 else
	    {
	     pChan = &pAdap->IDEChannel[1];
	    }

	 /*
	  * Select device.
	  */
	 outb(((UCHAR) (diskArrayId << 4) | 0xA0),
		pChan->io_ports[IDE_SELECT_OFFSET]);

	 /*
	  * Wait for device ready (not BUSY and not DRQ).
	  */
	 WaitForDeviceReady(pChan, statusByte);
	 if ((statusByte & IDE_STATUS_BUSY) || (statusByte & IDE_STATUS_DRQ) ||
	     (statusByte == 0))
	    {
	     printk("IT8212GetRebuildStatus: Disk[%d] busy. Status=0x%X\n",
		     diskArrayId, statusByte);
	     srbStatus = SRB_STATUS_BUSY;
	     goto exit;
	    }

	 /*
	  * Disable interrupt to avoid the unexpected interrupt.
	  */
	 outb(IDE_DC_DISABLE_INTERRUPTS, pChan->io_ports[IDE_CONTROL_OFFSET]);

	 /*
	  * Issue command.
	  */
	 outb(IDE_COMMAND_REBUILD_STATUS, pChan->io_ports[IDE_COMMAND_OFFSET]);

	 /*
	  * Check error.
	  */
	 WaitForCommandComplete(pChan, statusByte);

	 /*
	  * Reenable interrupt after command complete.
	  */
	 outb(IDE_DC_REENABLE_CONTROLLER, pChan->io_ports[IDE_CONTROL_OFFSET]);

	 if (statusByte != IDE_STATUS_IDLE)
	    {
	     srbStatus = SRB_STATUS_ERROR;
	     printk("GetRebuildStatus: ERROR\n");
	     goto exit;
	    }

	 progress = inb(pChan->io_ports[IDE_NSECTOR_OFFSET]);
	 srbStatus = SRB_STATUS_SUCCESS;

	 if (progress != 0xFF) progress = 0x64 - progress;

	 /*
	  * Put the rebuild status to user space.
	  */
	 put_user(progress, (UCHAR *)pioc->data);

exit:
	 return 0;

     case ITE_IOC_RESET_ADAPTER:
	 dprintk("ITE_IOC_RESET_ADAPTER\n");

	 /*
	  * Reset the adapter.
	  */
#	 if (0)
	 status = IT8212ResetAdapter();
#	 endif

	 /*
	  * Return TURE or FALSE to user space.
	  */
	 put_user(status, (UCHAR *)arg);
	 return 0;

     case ITE_IOC_GET_DRIVER_VERSION:
	 dprintk("ITE_IOC_GET_DRIVER_VERSION\n");

	 /*
	  * Get the current driver version.
	  */
	 put_user(driver_ver, (int *)arg);
	 return 0;

     default:
	 return -EINVAL;

    } /* end switch */

} /* end itedev_ioctl */

/************************************************************************
 * IOCTL close routine.
 ************************************************************************/
static int
itedev_close(struct inode *inode,  struct file *filep)
{

 MOD_DEC_USE_COUNT;

 return 0;

} /* end itedev_close */

/*
 * To make our driver a SCSI module, we have to include some magic at the
 * end of the file. This generates the init_module and clean_module code
 * nedded for a SCSI device, rather than having the author replicate it
 * each time a new driver is written.
 */
static Scsi_Host_Template driver_template = ITERAID;
#include "scsi_module.c"
