/*
 * ata.c
 *
 * Copyright 2002, Minoru Murashima. All rights reserved.
 * Distributed under the terms of the BSD License.
 *
 * ATA device driver
 */


#include <config.h>
#include <types.h>
#include <lib.h>
#include <interrupt.h>
#include <proc.h>
#include <time.h>
#include <errno.h>
#include <mm.h>
#include <mp.h>
#include <sp.h>
#include <pci.h>
#include <fs.h>
#include <device.h>
#include <console.h>
#include <sys/poll.h>
#include <debug.h>
#include <test.h>


enum{
	IDENTIFY_SIZE=512,			/* ATA ATAPI identify buffer size */
	ATA_SECTOR_SIZE=512,		/* ATA disk sector size */

	/* IO register */
	PRIM_BASE=0x1F0,
	SECN_BASE=0x170,

	/* status bit */
	BSY_BIT=0x80,
	DF_BIT=0x20,
	SRV_BIT=0x10,
	DRQ_BIT=0x8,
	ERR_BIT=0x1,
	CHK_BIT=0x1,

	/* IRQ number */
	PRIM_IRQ	= 14,
	SECOND_IRQ	= 15,

	/* Transfer mode bit in identify infomation */
	PIO4=0x2,		/* PIO mode4 */
	PIO3=0x1,		/* PIO mode3 */
	M_DMA2=0x4,		/* Multi DMA mode2 */
	M_DMA1=0x2,		/* Multi DMA mode1 */
	M_DMA0=0x1,		/* Multi DMA mode0 */
	U_DMA6=0x40,	/* Ultra DMA mode6 */
	U_DMA5=0x20,	/* Ultra DMA mode5 */
	U_DMA4=0x10,	/* Ultra DMA mode4 */
	U_DMA3=0x8,		/* Ultra DMA mode3 */
	U_DMA2=0x4,		/* Ultra DMA mode2 */
	U_DMA1=0x2,		/* Ultra DMA mode1 */
	U_DMA0=0x1,		/* Ultra DMA mode0 */

	/* デバイスタイプ */
	NODEV = 0,
	ATA,
	ATAPI,

	/* 転送タイプ */
	PIO = 1,	/* PIO transfer mode */
	M_DMA,		/* Multi DMA transfer mode */
	U_DMA,		/* Ultra DMA transfer mode */

	/* Interrupt mode */
	INTR_DISABLE=0,
	INTR_ENABLE=1,

	/* Trasfer mode */
	READ=0,
	WRITE=1,
};


/* ATA IO register. */
typedef struct{
	int dtr;	/* ATA_DTR=0 */
	int err;	/* ATA_ERR=1 */
	int ftr;	/* ATA_FTR=1 */
    int scr;	/* ATA_SCR=2 */
    int irr;	/* ATA_IRR=2 */
    int snr;	/* ATA_SNR=3 */
    int clr;	/* ATA_CLR=4 */
    int blr;	/* ATA_BLR=4 */
    int chr;	/* ATA_CHR=5 */
    int bhr;	/* ATA_BHR=5 */
    int dhr;	/* ATA_DHR=6 */
    int str;	/* ATA_STR=7 */
    int cmr;	/* ATA_CMR=7 */
    int ctr;	/* ATA_CTR=0x206 */
    int astr;	/* ATA_ASTR=0x206 */
}ATA_REG;

/* ATA ATAPI identify infomation(ATA-5仕様) */
typedef struct{
	ushort	config;				/* 0 定義違いあり */
	ushort	n_cyl;				/* 1 ATA only */
	ushort	sub_command;		/* 2 */
	ushort	n_head;				/* 3 ATA only */
	ushort	vender_def1[2];		/* 4 */
	ushort	n_sect;				/* 6 ATA only */
	ushort	reserv1[3];			/* 7 */
	uchar	serial_number[20];	/* 10 */
	ushort	nodef1[3];			/* 20 */
	uchar	firm_ware[8];		/* 23 */
	uchar	model[40];			/* 27 */
	ushort	multi_intr;			/* 47 ATA only */
	ushort	reserv2;			/* 48 */
	ushort	iordy;				/* 49 定義違いあり */
	ushort	stby_timer;			/* 50 ATA only */
	ushort	nodef2[2];			/* 51 */
	ushort	word_enable;		/* 53 */
	ushort	current_n_cyl;		/* 54 ATA only */
	ushort	current_n_head;		/* 55 ATA only */
	ushort	current_n_sect;		/* 56 ATA only */
	ushort	all_sect[2];		/* 57 ATA only */
	ushort	multiple;			/* 59 ATA only */
	ushort	lba_all_sect[2];	/* 60 ATA only */
	ushort	nodef3;				/* 62 */
	ushort	multi_dma;			/* 63 */
	ushort	pio;				/* 64 */
	ushort	min_mdma_cycl;		/* 65 */
	ushort	rcm_mdma_cycl;		/* 66 */
	ushort	min_flw_pio_cycl;	/* 67 */
	ushort	min_nflw_pio_cycl;	/* 68 */
	ushort	reserv3[2];			/* 69 */
	ushort	pck_bus_release;	/* 71 ATAPI only */
	ushort	bsy_clear_time;		/* 72 ATAPI only */
	ushort	reserv4[2];			/* 73 */
	ushort	max_cue_size;		/* 75 */
	ushort	reserv5[4];			/* 76 */
	ushort	major_num;			/* 80 */
	ushort	minor_num;			/* 81 */
	ushort	cmd1;				/* 82 */
	ushort	cmd2;				/* 83 */
	ushort	cmd_ext;			/* 84 */
	ushort	cmd1_enable;		/* 85 */
	ushort	cmd2_enable;		/* 86 */
	ushort	cmd_ext_enable;		/* 87 */
	ushort	ultra_dma;			/* 88 */
	ushort	secu_erase_time;	/* 89 ATA only */
	ushort	esecu_erase_time;	/* 90 ATA only */
	ushort	pmm_value;			/* 91 ATA only */
	ushort	pass_rev_coad;		/* 92 ATA only */
	ushort	hard_reset_info;	/* 93 */
	ushort	reserv6[32];		/* 94 */
	ushort	atapi_byte_count;	/* 126 ATAPI only */
	ushort	remov_set;			/* 127 */
	ushort	secu_stat;			/* 128 */
	ushort	vender_def2[31];	/* 129 */
	ushort	cfa_pm;				/* 160 */
}ID_INFO;

/* Conect device. */
typedef struct{
	int type;				/* ATA or ATAPI */
	int mode;				/* PIO=0,Multi DMA=1,Ultra DMA=2 */
	int sectorSize;			/* Secter size */
	uint allSectors;		/* LBA all sectors */
	int flag;				/* Function flag */
	int (*transfer)(int,int,int,void*,int,uint); /* Tranfer function */
}CONECT_DEV;


/* ATA IO register */
static ATA_REG reg[2]={
	{PRIM_BASE+0,PRIM_BASE+1,PRIM_BASE+1,PRIM_BASE+2,PRIM_BASE+2,PRIM_BASE+3,PRIM_BASE+4,PRIM_BASE+4,
	 PRIM_BASE+5,PRIM_BASE+5,PRIM_BASE+6,PRIM_BASE+7,PRIM_BASE+7,PRIM_BASE+0x206,PRIM_BASE+0x206},
	{SECN_BASE+0,SECN_BASE+1,SECN_BASE+1,SECN_BASE+2,SECN_BASE+2,SECN_BASE+3,SECN_BASE+4,SECN_BASE+4,
	 SECN_BASE+5,SECN_BASE+5,SECN_BASE+6,SECN_BASE+7,SECN_BASE+7,SECN_BASE+0x206,SECN_BASE+0x206}
};
static CONECT_DEV conectDev[2][2]={			/* Conect device infomation */
	{{0,0,0,0,0,NULL},{0,0,0,0,0,NULL}},
	{{0,0,0,0,0,NULL},{0,0,0,0,0,NULL}}
};
static WAIT_INTR waitIntr[2];						// 割り込み待ち用構造体
static uchar irqNum[2] = {PRIM_IRQ, SECOND_IRQ};	// IRQ番号


/************************************************************************************************
 *
 * < ATA common class >
 *
 ************************************************************************************************/

enum{
	TIME_OUT=2000,			/* Time out ms */
};


static uint64 timeOut;			/* PRIVATE,Time out counts */
static int currentIntr[2]		/* PRIVATE,Current host interrupt mode,enable=1 or diable=0 */
	={0,0};

/*
 * PUBLIC
 * Check busy flag in status register
 * parameters : Stat register,Mask,Compare rest value
 * return : Status value
 */
static int checkBusy(int str)
{
	uchar in;
	uint64 count;


	count=rdtsc();
	while((in=inb(str))&BSY_BIT)
		if(rdtsc()-count>timeOut)return in;

	return in;
}


/*
 * PUBLIC
 * ソフトウェアリセット
 * parameters : コントロールレジスタ
 * return : 0 or Error number
 */
static int softReset(int host)
{
	outb(reg[host].ctr,0x4);	/* ソフトリセット */
	mili_timer(5);				/* 5ms wait */
	outb(reg[host].ctr,0x2);	/* リセット解除|割り込み禁止 */
/*	mili_timer(20);*/				/* 20ms wait */
	mili_timer(5);				/* 5ms wait */
	if((checkBusy(reg[host].astr)&BSY_BIT)!=0)
		return -EDBUSY;

	return 0;
}


/*
 * PUBLIC
 * Set interrupt mode
 * parameters : Host,Interrupt mode
 */
static void setIntr(int host,int flag)
{
	if (flag == currentIntr[host])
		return;

	if (flag == INTR_DISABLE)
	{
		set_irq_mask(irqNum[host]);
		outb(reg[host].ctr,0x2);			/* Disable interrupt */
		mili_timer(5);						/* wait */
		currentIntr[host]=INTR_DISABLE;
	}
	else
	{
		outb(reg[host].ctr,0);				/* Enable interrupt */
		release_irq_mask(irqNum[host]);
		mili_timer(5);						/* wait */
		currentIntr[host]=INTR_ENABLE;
	}
}


/*
 * PUBLIC
 * initialize ATA common.
 */
static void initCommon()
{
	timeOut=clock_1m*TIME_OUT;
}


/************************************************************************************************
 *
 * < ATA command protocol class >
 *
 ************************************************************************************************/

enum{
	/* IDE Bus Master IO register */
	PCI_CONF_BMBASE=0x20,	/* IDE Bus Master IO base address register in PCI Configration */
	IDE_BMIC=0x0,			/* Bus Master IDE Command register */
	IDE_BMIS=0x2,			/* Bus Master IDE Status register */
	IDE_BMIDTP=0x4,			/* Bus Master IDE Descriptor Table Pointer register(4byte) */
	IDE_BMIO_SECOND=0x8,	/* セカンダリーホストの場合にレジスター値にプラスする値 */
	PRD_EOT=0x1<<31,		/* PRD EOT bit */
	IDE_BM_TIMEOUT=4000,	/* Bus Master time out ms. */
};


/* Physical Region Descriptor for IDE Busmaster */
typedef struct{
	void *physAddr;		/* Physical address */
	uint count;			/* EOT bit(31)|Transfer count by bytes,Max 64kbyte */
}PRD;


static int ide_base[2];		/* PRIVATE,IDE Bus Master IO base address */
static PRD prd[2];			/* PRIVATE,Physical Region Descriptor. */


/*
 * PUBLIC
 * デバイスセレクション
 * parameters : Device number,Host number,LBA 24bit-27bit
 * return : 0 or Error number
 */
static int deviceSelect(int host,int dev,int lba24_27)
{
	enum{
		LBA_BIT=0x40,		/* LBA bit in Device_head register */
	};

	int error;


	if(((error=checkBusy(reg[host].astr))&(DRQ_BIT|BSY_BIT))!=0)
	{
		if(error&DRQ_BIT)
			return -EDERRE;
		if(error&BSY_BIT)
			return -EDBUSY;
	}

	/* Device select */
	outb(reg[host].dhr,(uchar)(LBA_BIT|(dev<<4)|lba24_27));
	micro_timer(1);						/* 400ns wait */

	if(((error=checkBusy(reg[host].astr))&(DRQ_BIT|BSY_BIT))!=0)
	{
		if(error&DRQ_BIT)
			return -EDERRE;
		if(error&BSY_BIT)
			return -EDBUSY;
	}
	return 0;
}


/*
 * PUBLIC
 * PIO read data
 * parameters : Host number,Device number,buffer,Block size,Block count
 * return : Status coad
 */
static int readPio(int host,int dev,void *buf,int block,int count)
{
	int error;
	int i,j,last;
	union{
		void *pv;
		uint ui;
	}value;


	last=block/2;
	for(i=0;i<count;++i)
	{
		if(((error=checkBusy(reg[host].str))&(BSY_BIT|DRQ_BIT))!=DRQ_BIT)return error;
		for(j=0;j<last;++j)((short*)buf)[j]=inw(reg[host].dtr);
		value.pv = buf;
		value.ui += block;
		buf = value.pv;
	}

	return checkBusy(reg[host].str);
}


/*
 * PUBLIC
 * PIO write data
 * parameters : Host number,Device number,buffer,Block size,Block count
 * return : Status coad
 */
static int writePio(int host,int dev,void *buf,int block,int count)
{
	int error;
	int i,j,last;
	union{
		void *pv;
		uint ui;
	}value;


	last=block/2;
	for(i=0;i<count;++i)
	{
		if(((error=checkBusy(reg[host].str))&(BSY_BIT|DRQ_BIT))!=DRQ_BIT)return error;
		for(j=0;j<last;++j)outw(reg[host].dtr,((short*)buf)[j]);
		value.pv = buf;
		value.ui += block;
		buf = value.pv;
	}

	return checkBusy(reg[host].str);
}


/*
 * PUBLIC
 * DMA read data
 * parameters : Host number,Device number,buffer,sector size,Block count
 * return : Status coad
 */
static int readDma(int host,int dev,void *buf,int sector_size,int count)
{
	// Set PRD
	prd[host].physAddr = buf;
	prd[host].count = sector_size * count | PRD_EOT;
	outdw(ide_base[host] + IDE_BMIDTP,(uint)&prd[host]);

	// Mask interrupt.
//	set_irq_mask(irqNum[host]);

	/*
	 * バスマスターステータスレジスタの割り込みフラグをクリアーしないと
	 * 割り込みが発生しないようだ
	 */
	outb(ide_base[host] + IDE_BMIS,0x6);							// Clear interrupt bit and error bit.
	outb(ide_base[host] + IDE_BMIC,0x9);							// Start read Bus Master.
	wait_intr(&waitIntr[host], IDE_BM_TIMEOUT, TASK_DEVICE_WAIT);	// Wait interrupt.
	outb(ide_base[host] + IDE_BMIC,0);								// Stop Bus Master.

	return inb(reg[host].str);
}


/*
 * PUBLIC
 * DMA write data
 * parameters : Host number,Device number,buffer,sector size,Block count
 * return : Status coad
 */
static int writeDma(int host,int dev,void *buf,int sector_size,int count)
{
	// Set PRD
	prd[host].physAddr = buf;
	prd[host].count = sector_size * count | PRD_EOT;
	outdw(ide_base[host] + IDE_BMIDTP, (uint)&prd[host]);

	/*
	 * バスマスターステータスレジスタの割り込みフラグをクリアーしないと
	 * 割り込みが発生しないようだ
	 */
	outb(ide_base[host] + IDE_BMIS, 0x6);							// Clear interrupt bit and error bit.
	outb(ide_base[host] + IDE_BMIC, 0x1);							// Start write Bus Master.
	wait_intr(&waitIntr[host], IDE_BM_TIMEOUT, TASK_DEVICE_WAIT);	// Wait interrupt.
	outb(ide_base[host] + IDE_BMIC, 0);								// Stop Bus Master.

	return inb(reg[host].str);
}


/*
 * PUBLIC
 * ATA data transfer protocol
 * parameters : Host number,Device number,Mode=READ or WRITE,buffer,sector count,begin sector
 * return : 0 or Error number
 */
static int transferAta(int host,int dev,int trans_mode,void *buf,int count,uint begin)
{
	int error;


	if (conectDev[host][dev].mode == PIO)
		setIntr(host,INTR_DISABLE);
	else
		setIntr(host,INTR_ENABLE);

	if ((error = deviceSelect(host,dev,begin>>24)) != 0)
		return error;

	outb(reg[host].scr,(uchar)count);
	outb(reg[host].snr,(uchar)begin);
	outb(reg[host].clr,(uchar)(begin>>8));
	outb(reg[host].chr,(uchar)(begin>>16));

	/* PIO transfer */
	if (conectDev[host][dev].mode == PIO)
	{
		if (trans_mode == READ)
		{
			outb(reg[host].cmr,0x20);
			error = readPio(host,dev,buf,ATA_SECTOR_SIZE,count);
		}
		else
		{
			outb(reg[host].cmr,0x30);
			error = writePio(host,dev,buf,ATA_SECTOR_SIZE,count);
		}
	}
	/* DMA transfer */
	else
	{
		if (trans_mode == READ)
		{
			outb(reg[host].cmr,0xc8);
			error = readDma(host,dev,buf,ATA_SECTOR_SIZE,count);
		}
		else
		{
			outb(reg[host].cmr,0xca);
			error = writeDma(host,dev,buf,ATA_SECTOR_SIZE,count);
		}
	}

	if ((error & (BSY_BIT | DRQ_BIT | ERR_BIT)) != 0)
	{
		if (error & (DRQ_BIT | ERR_BIT))
			return -EDERRE;
		if (error & BSY_BIT)
			return -EDBUSY;
	}

	return 0;
}


/*
 * PUBLIC
 * initialize IDE Bus Master
 * parameters : Host number,PCI_INFO buffer
 * return : 0 or Error number
 */
static int initIdeBusmaster(int host,PCI_INFO *ide)
{
	ushort com;


	/* Serch IDE */
	ide->bus = ide->dev = ide->func = 0;
	if (search_pci_class(PCI_CLASS_IDE_BSMST,ide) < 0)
		return -ENODEV;

	/* Test Bus Master enable bit on */
	com = read_pci_config(ide,PCI_CONF_COMMAND);
	writew_pci_config(ide,PCI_CONF_COMMAND,com | PCI_COMMAND_BSMST);
	if ((read_pci_config(ide,PCI_CONF_COMMAND) & PCI_COMMAND_BSMST) == 0)
		return -EDNOFUNC;

	/* Get IO base address */
	if ((ide_base[0] = read_pci_config(ide,PCI_CONF_BMBASE)) == 0)
		return -EDNOFUNC;
	ide_base[0] &= 0xfff0;
	ide_base[1] = ide_base[0] + IDE_BMIO_SECOND;

	/* Reset Bus Master */
	outb(ide_base[host] + IDE_BMIC,0);

	return 0;
}


/************************************************************************************************
 *
 * < ATA command >
 *
 ************************************************************************************************/

enum{
	/* Set features subcommand code */
	SET_TRANSFER=0x3,	/* Set transfer mode */

	/* Transfer mode of set features subcommand */
	SUB_PIO_DEF=0x0,
	SUB_PIO_FLO=0x8,
	SUB_M_DMA=0x20,
	SUB_U_DMA=0x40,
};


/*
 * PUBLIC
 * Reset device
 * parameters : Host number,Device number
 * return : 0 or Error number
 */
static int resetDevice(int host,int dev)
{
	int error;


	setIntr(host,INTR_DISABLE);

	/* Device select */
	outb(reg[host].dhr,(uchar)(dev<<4));
	micro_timer(1);					/* 400ns wait */

	outb(reg[host].cmr,0x8);
	micro_timer(1);			/* 400ns wait */

	if(((error=checkBusy(reg[host].astr))&(BSY_BIT|ERR_BIT))!=0)
	{
		if(error&ERR_BIT)
			return -EDERRE;
		if(error&BSY_BIT)
			return -EDBUSY;
	}
	return 0;
}


/*
 * PUBLIC
 * Identif device
 * parameters : Host number,Device number,ATA or ATAPI,buffer
 * return : 0 or Error number
 */
static int identifyDevice(int host,int dev,int drv,void *buf)
{
	int error;


	setIntr(host,INTR_DISABLE);

	if((error=deviceSelect(host,dev,0))!=0)return error;

	outb(reg[host].cmr,(drv==ATA)?0xec:0xa1);
	micro_timer(1);					/* 400ns wait */

	/* Read data */
	if(((error=readPio(host,dev,buf,IDENTIFY_SIZE,1))&(BSY_BIT|DRQ_BIT|ERR_BIT))!=0)
	{
		if(error&(DRQ_BIT|ERR_BIT))
			return -EDERRE;
		if(error&BSY_BIT)
			return -EDBUSY;
	}
	return 0;
}


/*
 * PUBLIC
 * Idle device
 * parameters : Host number,Device number
 * return : 0 or Error number
 */
static int idleImmediateDevice(int host,int dev)
{
	int error;


	setIntr(host,INTR_DISABLE);

	if((error=deviceSelect(host,dev,0))!=0)return error;

	outb(reg[host].cmr,0xe1);
	micro_timer(1);			/* 400ns wait */

	if(((error=checkBusy(reg[host].astr))&(BSY_BIT|ERR_BIT))!=0)
	{
		if(error&ERR_BIT)
			return -EDERRE;
		if(error&BSY_BIT)
			return -EDBUSY;
	}
	return 0;
}


/*
 * PUBLIC
 * Initialize device parameters
 * parameters : Host number,Device number,Number of heads,Number of sectors
 * return : 0 or Error number
 */
static int initDeviceParam(int host,int dev,uchar head,uchar sectors)
{
	int error;


	if(--head>0xf)return EINVAL;	/* headはヘッド数-1を値とする。 */

	setIntr(host,INTR_DISABLE);

	if((error=deviceSelect(host,dev,head))!=0)return error;

	outb(reg[host].scr,sectors);
	outb(reg[host].cmr,0x91);
	micro_timer(1);			/* 400ns wait */

	if(((error=checkBusy(reg[host].astr))&(BSY_BIT|ERR_BIT))!=0)
	{
		if(error&ERR_BIT)
			return -EDERRE;
		if(error&BSY_BIT)
			return -EDBUSY;
	}
	return 0;
}


/*
 * PUBLIC
 * デバイスの動作設定
 * parameters : Host number,Device number,Subcommand,Transfer mode or 0
 * return : 0 or Error number
 */
static int setFeatures(int host,int dev,uchar subcommand,uchar trans_mode)
{
	int error;


	setIntr(host,INTR_DISABLE);

	if((error=deviceSelect(host,dev,0))!=0)return error;

	outb(reg[host].ftr,subcommand);
	outb(reg[host].scr,trans_mode);
	outb(reg[host].cmr,0xef);
	micro_timer(1);			/* 400ns wait */

	if(((error=checkBusy(reg[host].astr))&(BSY_BIT|ERR_BIT))!=0)
	{
		if(error&ERR_BIT)
			return -EDERRE;
		if(error&BSY_BIT)
			return -EDBUSY;
	}
	return 0;
}


/************************************************************************************************
 *
 * Packet command
 *
 ************************************************************************************************/

enum{
	/* Interrupt reason bit. */
	CD_BIT=0x1,
	IO_BIT=0x2,
	REL_BIT=0x4,

	/* ATAPI function flag. */
	PACK_OVL=0x2,			/* Packet feature overlappe flag */
	PACK_DMA=0x1,			/* Packet feature DMA flag */
	ATAPI_OVL=0x2000,		/* Overlappe flag in ATAPI function */

	/* Packet start stop unit flag */
	/*SSU_STOP=0,*/				/* Disk stop */
	/*SSU_START=0x1,*/			/* Disk start */
	/*SSU_EJECT=0x2,*/			/* Disk eject */
	/*SSU_STANBY=0x30,*/		/* Stanby */

	/* Packet command features flag */
	/*PACKET_DMA=0x1,*/		/* DMA transfer */
	/*PACKET_OVERLAP=0x2,*/ /* Over lapped */
};


/* Packet command parameters */
typedef struct{
	void *buf;			/* Block read write buffer */
	int size;			/* Transfer bytes or Secter size */
	uchar feutures;		/* Packet command flag */
	uchar packet[14];	/* Packet command parameters and return packet */
}PACKET_PARAM;


/*
 * PRIVATE
 * Issue packet command
 * parameters : host,device,Packet parameters
 * return : 0 or Error number
 */
static int issuePacketCommand(int host,int dev,PACKET_PARAM *param)
{
	int dtr;
	int error;
	int i;


	dtr=reg[host].dtr;

	/* デバイスセレクション */
	if((error=deviceSelect(host,dev,0))!=0)return error;

	/* Issue packet command  */
	outb(reg[host].ftr,param->feutures);
	outb(reg[host].scr,0);
	outb(reg[host].clr,0xff);
	outb(reg[host].chr,0xff);
	outb(reg[host].cmr,0xa0);
	if((checkBusy(reg[host].str)&(DRQ_BIT|CHK_BIT))!=DRQ_BIT)
		return -EDERRE;
	if((inb(reg[host].irr)&(CD_BIT|IO_BIT))!=CD_BIT)
		return -EDERRE;

	/* DMA transfer */
	if(param->feutures&PACK_DMA)
	{
		setIntr(host,INTR_ENABLE);		/* enable interrupt */

		/* Send packet */
		for(i=0;i<6;++i)outw(dtr,((short*)param->packet)[i]);

		/* Overrapped */
		if (param->feutures & PACK_OVL)
		{
			wait_intr(&waitIntr[host],2000,TASK_DEVICE_WAIT);	/* Wait interrupt */
			error = (int)inb(reg[host].str);

			if(error&ERR_BIT)
				return -EDERRE;		/* Packet command Error */
			if((error&DRQ_BIT)==0)
				return 0;			/* Non data transfer */

			if((error=deviceSelect(host,dev,0))!=0)
				return error;
			wait_intr(&waitIntr[host],2000,TASK_DEVICE_WAIT);	/* Wait interrupt */

			outb(reg[host].cmr,0xa2);		/* Issue service command */
			if((checkBusy(reg[host].str)&(BSY_BIT|DRQ_BIT))!=DRQ_BIT)
				return -EDBUSY;
		}

		/* Data transfer */
		if(param->packet[0]==0x28)
			error=readDma(host,dev,param->buf,param->size,(uint)param->packet[7]<<8|(uint)param->packet[8]);
		else if(param->packet[0]==0x2a)
			error=writeDma(host,dev,param->buf,param->size,(uint)param->packet[7]<<8|(uint)param->packet[8]);
		else
			error=readDma(host,dev,param->buf,param->size,1);
	}

	/* PIO transfer */
	else
	{
		setIntr(host,INTR_DISABLE);

		/* Send packet */
		for(i=0;i<6;++i)outw(dtr,((short*)param->packet)[i]);

		error=checkBusy(reg[host].str);
		if(error&ERR_BIT)
			return -EDERRE;		/* Packet command Error */
		if((error&DRQ_BIT)==0)
			return 0;			/* Non data transfer */

		/* Overrapped */
		if(param->feutures&PACK_OVL)
		{
			if((error = deviceSelect(host,dev,0)) != 0)
				return error;
			wait_intr(&waitIntr[host],2000,TASK_DEVICE_WAIT);			/* Wait interrupt */
			
			outb(reg[host].cmr,0xa2);						/* Issue service command */
			if((checkBusy(reg[host].str)&(BSY_BIT|DRQ_BIT))!=DRQ_BIT)
				return -EDBUSY;
		}

		/* Data transfer */
		if(param->packet[0]==0x28)
			error=readPio(host,dev,param->buf,param->size,(uint)param->packet[7]<<8|(uint)param->packet[8]);
		else if(param->packet[0]==0x2a)
			error=writePio(host,dev,param->buf,param->size,(uint)param->packet[7]<<8|(uint)param->packet[8]);
		else
			error=readPio(host,dev,param->buf,param->size,1);
	}

	/* Last check */
	if((error&(BSY_BIT|DRQ_BIT|ERR_BIT))!=0)
	{
		if(error&(DRQ_BIT|ERR_BIT))
			return -EDERRE;
		if(error&BSY_BIT)
			return -EDBUSY;
	}
	return 0;
}


/*
 * PUBLIC
 * Test unit ready
 * parameters : Host number,Device number
 * return : 0 or Error number
 */
static int testUnitReady(int host,int dev)
{
	PACKET_PARAM param;


	/* Set packet parameters */
	param.feutures=0;
	param.size=0;
	memset(param.packet,0,12);
	param.packet[0]=0;

	return issuePacketCommand(host,dev,&param);
}


/*
 * PUBLIC
 * Request sense
 * parameters : Host number,Device number
 * return : Ready=0 or Retry=1 or Error number
 */
static int requestSense(int host,int dev)
{
	static char buf[14];
	PACKET_PARAM param;


	/* Set packet parameters */
	param.feutures=conectDev[host][dev].mode>>1;
	param.size=14;
	param.buf=buf;
	memset(param.packet,0,12);
	param.packet[0]=0x3;
	param.packet[4]=14;

	if(issuePacketCommand(host,dev,&param)!=0)return -1;
	switch((((uint)buf[2]&0xf)<<16)|((uint)buf[12]<<8)|((uint)buf[13]))
	{
		case 0x62800:return 0;			/* Not ready to ready change */
		case 0x62900:					/* Power on,Reset */
		case 0x20401:return 1;			/* Becoming ready */
		case 0x63a00:return -ENOMEDIUM;	/* Medium not present */
		default:     return -EDERRE;
	}
}


/*
 * PUBLIC
 * Start stop unit
 * parameters : Host number,Device number,Operation flag
 * return : 0 or Error number
 */
static int startStopUnit(int host,int dev,uchar ope)
{
	PACKET_PARAM param;


	/* Set packet parameters */
	param.feutures=(conectDev[host][dev].flag&ATAPI_OVL)>>12;
	memset(param.packet,0,12);
	param.packet[0]=0x1b;
	param.packet[4]=ope;

	return issuePacketCommand(host,dev,&param);
}


/*
 * Read capacity
 * parameters : Host number,Device number
 * return : 0 or Error number
 */
static int readCapacity(int host,int dev)
{
	PACKET_PARAM param;
	char tmp,*buf;
	int error;
	int i,j;


	buf=(char*)&conectDev[host][dev].sectorSize;

	/* Set packet parameters */
	param.feutures=conectDev[host][dev].mode>>1;
	param.size=8;
	param.buf=buf;
	memset(param.packet,0,12);
	param.packet[0]=0x25;

	if((error=issuePacketCommand(host,dev,&param))!=0)return error;

	for(i=0,j=7;i<4;++i,--j)
	{
		tmp=buf[i];
		buf[i]=buf[j];
		buf[j]=tmp;
	}

	/*
	 * 512byte単位で切捨て
	 * ドライブによっては物理セクターサイズの場合がある
	 */
	conectDev[host][dev].sectorSize=ROUNDDOWN(conectDev[host][dev].sectorSize,512);

	return 0;
}


/*
 * Read data
 * parameters : Host number,Device number,Transfer mode,Buffer,Begin sector
 * rturn : 0 or Error number
 */
static int transferAtapi(int host,int dev,int trans_mode,void *buf,int count,uint begin)
{
	PACKET_PARAM param;


	/* Set packet parameters */
	param.feutures=((conectDev[host][dev].flag&ATAPI_OVL)>>12)|(conectDev[host][dev].mode>>1);
	param.size=conectDev[host][dev].sectorSize;
	param.buf=buf;
	memset(param.packet,0,12);
	param.packet[0]=(trans_mode==READ)?0x28:0x2a;
	param.packet[2]=(uchar)begin>>24;
	param.packet[3]=(uchar)begin>>16;
	param.packet[4]=(uchar)begin>>8;
	param.packet[5]=(uchar)begin;
	param.packet[7]=(uchar)count>>8;
	param.packet[8]=(uchar)count;

	return issuePacketCommand(host,dev,&param);
}


/************************************************************************************************
 *
 * < ATA functions calss >
 *
 ************************************************************************************************/

/*
 * PUBLIC
 * Change transfer mode
 * parameters : Host number,Device number,Transfer mode(PIO=1 or Multi DMA=2 or Ultra DMA=3)
 * return : 0 or Error number
 */
static int changeMode(int host,int dev,int mode)
{
	uchar subcm;
	uint value;
	int error;
	ID_INFO *id_info;
	PCI_INFO ide;


	if(conectDev[host][dev].mode==mode)return 0;

	if((id_info=(ID_INFO*)kmalloc(IDENTIFY_SIZE))==NULL)
		return -EDNOMEM;

	/* Read identify infomation */
	if((error=identifyDevice(host,dev,conectDev[host][dev].type,id_info))!=0)return error;

	if(mode==PIO)
	{
		if(id_info->pio&PIO4)subcm=SUB_PIO_FLO|4;
		else if(id_info->pio&PIO3)subcm=SUB_PIO_FLO|3;
		else subcm=SUB_PIO_DEF;
	}
	else if(mode==M_DMA)
	{
		/* Set ATA transfer mode */
		if(id_info->multi_dma&M_DMA2)subcm=SUB_M_DMA|2;
		else if(id_info->multi_dma&M_DMA1)subcm=SUB_M_DMA|1;
		else if(id_info->multi_dma&M_DMA0)subcm=SUB_M_DMA|0;
		else 
			return -EDNOFUNC;

		/*
		 * ULTRA DMA対応のドライブについては、BIOSでIDEがULTRA DMAに
		 * 設定されているので、その設定を取り消す必要がある
		 */
		if((error=initIdeBusmaster(host,&ide))!=0)return error;

		switch(ide.vidDid)
		{
			case 0x24CB8086:		/* Intel ICH4 */
			case 0x248a8086:		/* Intel ICH3 mobile */
    		case 0x248b8086:		/* Intel ICH3 */
    		case 0x244a8086:		/* Intel ICH2 mobile */
    		case 0x244b8086:		/* Intel ICH2 */
    		case 0x24118086:		/* Intel ICH */
    		case 0x76018086:		/* Intel ICH */
    		case 0x24218086:		/* Intel ICH0 */
			case 0x71118086:		/* Intel PIIX4 */
    		case 0x84CA8086:		/* Intel PIIX4 */
    		case 0x71998086:		/* Intel PIIX4e */
    			value=read_pci_config(&ide,0x48);
    			writedw_pci_config(&ide,0x48,value&~(1<<(host*2+dev)));
    			break;
    		case 0x74411022:		/* AMD 768 */
    		case 0x74111022:		/* AMD 766 */
    		case 0x74091022:		/* AMD 756 */
    		case 0x05711106:		/* VIA 82C571, 82C586, 82C596, 82C686 , 8231, 8233 */
    		case 0x31471106:		/* VIA 8233a */
    		case 0x82311106:		/* VIA 8231 */
    		case 0x30741106:		/* VIA 8233 */
    		case 0x31091106:		/* VIA 8233c */
    		case 0x06861106:		/* VIA 82C686 82C686a 82C686b */
    		case 0x05961106:		/* VIA 82C596a 82C596b */
    		case 0x05861106:		/* VIA 82C586b */
    			value=read_pci_config(&ide,0x50);
    			writedw_pci_config(&ide,0x50,value&~(0x40000000>>(host*16+dev*8)));
    			/*writeb_pci_config(&ide,0x4b-host*2-dev,0x31);*/
    			break;
    		case 0x55131039:		/* SiS 5591 */
    		case 0x06301039:		/* SiS 630 */
    		case 0x06331039:		/* SiS 633 */
    		case 0x06351039:		/* SiS 635 */
    		case 0x06401039:		/* SiS 640 */
    		case 0x06451039:		/* SiS 645 */
    		case 0x06501039:		/* SiS 650 */
    		case 0x07301039:		/* SiS 730 */
    		case 0x07331039:		/* SiS 733 */
    		case 0x07351039:		/* SiS 735 */
    		case 0x07401039:		/* SiS 740 */
    		case 0x07451039:		/* SiS 745 */
    		case 0x07501039:		/* SiS 750 */
    		case 0x05301039:		/* SiS 530 */
    		case 0x05401039:		/* SiS 540 */
    		case 0x06201039:		/* SiS 620 */
    			value=read_pci_config(&ide,0x40+host*4);
    			writedw_pci_config(&ide,0x40+host*4,value&~(0xf000<<dev*16));
    			break;
    	}
	}
	else if(mode==U_DMA)
	{
		if((id_info->ultra_dma&(U_DMA5|U_DMA4|U_DMA3|U_DMA2|U_DMA1|U_DMA0))==0)
			return -EDNOFUNC;

		/*
		 * ULTRA DMA対応のドライブについては、BIOSでIDEがULTRA DMAに
		 * 設定されているものとみなす
		 */
		if((error=initIdeBusmaster(host,&ide))!=0)return error;

		switch(ide.vidDid)
		{
			case 0x24CB8086:		/* Intel ICH4 */
			case 0x248a8086:		/* Intel ICH3 mobile */
    		case 0x248b8086:		/* Intel ICH3 */
    		case 0x244a8086:		/* Intel ICH2 mobile */
    		case 0x244b8086:		/* Intel ICH2 */
    			if(id_info->ultra_dma&U_DMA5)
    			{
					subcm=SUB_U_DMA|5;
					goto INTEL;
				}
			case 0x24118086:		/* Intel ICH */
    		case 0x76018086:		/* Intel ICH */
    			if(id_info->ultra_dma&U_DMA4)
    			{
					subcm=SUB_U_DMA|4;
					goto INTEL;
				}
				if(id_info->ultra_dma&U_DMA3)
				{
					subcm=SUB_U_DMA|3;
					goto INTEL;
				}
			case 0x24218086:		/* Intel ICH0 */
			case 0x71118086:		/* Intel PIIX4 */
    		case 0x84CA8086:		/* Intel PIIX4 */
    		case 0x71998086:		/* Intel PIIX4e */
    			if(id_info->ultra_dma&U_DMA2)
    			{
					subcm=SUB_U_DMA|2;
					goto INTEL;
				}
				if(id_info->ultra_dma&U_DMA1)
				{
					subcm=SUB_U_DMA|1;
					goto INTEL;
				}
				subcm=SUB_U_DMA|0;
INTEL:			value=read_pci_config(&ide,0x48);
    			writedw_pci_config(&ide,0x48,value|(1<<(host*2+dev)));
				break;
    		case 0x74411022:		/* AMD 768 */
    		case 0x74111022:		/* AMD 766 */
    			if(id_info->ultra_dma&U_DMA5)
    			{
					subcm=SUB_U_DMA|5;
					goto VIA;
				}
    		case 0x74091022:		/* AMD 756 */
    			if(id_info->ultra_dma&U_DMA4)
    			{
					subcm=SUB_U_DMA|4;
					goto VIA;
				}
				if(id_info->ultra_dma&U_DMA3)
				{
					subcm=SUB_U_DMA|3;
					goto VIA;
				}
				if(id_info->ultra_dma&U_DMA2)
    			{
					subcm=SUB_U_DMA|2;
					goto VIA;
				}
				if(id_info->ultra_dma&U_DMA1)
				{
					subcm=SUB_U_DMA|1;
					goto VIA;
				}
				if(id_info->ultra_dma&U_DMA0)
				{
					subcm=SUB_U_DMA|0;
					goto VIA;
				}
    		/*case 0x05711106:*/	/* VIA 82C571, 82C586, 82C596, 82C686 , 8231, 8233 */
    		case 0x31471106:		/* VIA 8233a */
    			if(id_info->ultra_dma&U_DMA6)
				{
					subcm=SUB_U_DMA|6;
					goto VIA;
				}
    		case 0x06861106:		/* VIA 82C686b */
    		case 0x82311106:		/* VIA 8231 */
    		case 0x30741106:		/* VIA 8233 */
    		case 0x31091106:		/* VIA 8233c */
    			if(id_info->ultra_dma&U_DMA5)
				{
					subcm=SUB_U_DMA|5;
					goto VIA;
				}
    		/*case 0x06861106:*/	/* VIA 82C686a */
    		case 0x05961106:		/* VIA 82C596b */
    			if(id_info->ultra_dma&U_DMA4)
				{
					subcm=SUB_U_DMA|4;
					goto VIA;
				}
				if(id_info->ultra_dma&U_DMA3)
				{
					subcm=SUB_U_DMA|3;
					goto VIA;
				}
    		/*case 0x06861106:*/	/* VIA 82C686 */
    		/*case 0x05961106:*/	/* VIA 82C596a */
    		case 0x05861106:		/* VIA 82C586b */
    			if(id_info->ultra_dma&U_DMA2)
				{
					subcm=SUB_U_DMA|2;
					goto VIA;
				}
				if(id_info->ultra_dma&U_DMA1)
				{
					subcm=SUB_U_DMA|1;
					goto VIA;
				}
				subcm=SUB_U_DMA|0;
VIA:   			value=read_pci_config(&ide,0x50);
    			writedw_pci_config(&ide,0x50,value|(0x40000000>>(host*16+dev*8)));
    			break;
    		case 0x55131039:		/* SiS 5591 */
    		case 0x06301039:		/* SiS 630 */
    		case 0x06331039:		/* SiS 633 */
    		case 0x06351039:		/* SiS 635 */
    		case 0x06401039:		/* SiS 640 */
    		case 0x06451039:		/* SiS 645 */
    		case 0x06501039:		/* SiS 650 */
    		case 0x07301039:		/* SiS 730 */
    		case 0x07331039:		/* SiS 733 */
    		case 0x07351039:		/* SiS 735 */
    		case 0x07401039:		/* SiS 740 */
    		case 0x07451039:		/* SiS 745 */
    		case 0x07501039:		/* SiS 750 */
    			if(id_info->ultra_dma&U_DMA5)
				{
					subcm=SUB_U_DMA|5;
					value=read_pci_config(&ide,0x40+host*4);
    				writedw_pci_config(&ide,0x40+host*4,value|(0x8000<<dev*16));
    				break;
				}
    			if(id_info->ultra_dma&U_DMA4)
				{
					subcm=SUB_U_DMA|4;
					value=read_pci_config(&ide,0x40+host*4);
    				writedw_pci_config(&ide,0x40+host*4,value|(0x9000<<dev*16));
    				break;
				}
				if(id_info->ultra_dma&U_DMA2)
				{
					subcm=SUB_U_DMA|2;
					value=read_pci_config(&ide,0x40+host*4);
    				writedw_pci_config(&ide,0x40+host*4,value|(0xb000<<dev*16));
    				break;
				}
    		case 0x05301039:		/* SiS 530 */
    		case 0x05401039:		/* SiS 540 */
    		case 0x06201039:		/* SiS 620 */
    			if(id_info->ultra_dma&U_DMA4)
				{
					subcm=SUB_U_DMA|4;
					value=read_pci_config(&ide,0x40+host*4);
    				writedw_pci_config(&ide,0x40+host*4,value|(0x9000<<dev*16));
    				break;
				}
				if(id_info->ultra_dma&U_DMA2)
				{
					subcm=SUB_U_DMA|2;
					value=read_pci_config(&ide,0x40+host*4);
    				writedw_pci_config(&ide,0x40+host*4,value|(0xa000<<dev*16));
    				break;
				}
			default:
				return -EDNOFUNC;
		}
	}
	else 
		return -EDINVAL;

	conectDev[host][dev].mode=mode;

	kfree(id_info);

	return setFeatures(host,dev,SET_TRANSFER,subcm);
}


/************************************************************************************************
 *
 * < interrupt handler class >
 *
 ************************************************************************************************/

/*
 * GLOBAL
 * Primary ATA interrupt handler
 * return : Task switch on
 */
static int primIntrHandler()
{
/***************************************
	printk("IRQ14\n");
***************************************/
	return wake_intr(&waitIntr[0],TASK_DEVICE_WAIT);

//	return 1;
}


/*
 * GLOBAL
 * Secondary ATA interrupt handler
 * return : Task switch on
 */
static int secondIntrHandler()
{
/***************************************
	printk("IRQ15\n");
***************************************/
	return wake_intr(&waitIntr[1],TASK_DEVICE_WAIT);
}


/************************************************************************************************
 *
 * < System call interface class >
 *
 ************************************************************************************************/

/* 処理待ち用Wait queue */
static WAIT_QUEUE wait_queue[2];


static int testAtapi(int,int);


/*
 * PRIVATE
 * Data transfer
 * parameters : Host number,Device number,Mode=READ or WRITE,buffer,Transfer blocks,begin block
 * return : Transfer size or Error number
 */
static inline int transfer(int host,int dev,int mode,void *buf,size_t blocks,size_t begin)
{
	int rest;


	if (blocks == 0)
		return 0;
	if (begin + blocks > conectDev[host][dev].allSectors)
		return -EDINVAL;

	waitSemaphore(&wait_queue[host]);
	{
		/* SMPなら割り込みが同じcpuに発生するようにする */
		if(MFPS_addres)
			set_intr_cpu(irqNum[host],get_current_cpu());

		/* 転送開始 */
		rest = conectDev[host][dev].transfer(host,dev,mode,buf,blocks,begin);
		if (rest == 0)
			rest = blocks;
	}
	wakeSemaphore(&wait_queue[host]);

	return rest;
}


/*
 * File operation interface
 */
static int openHda(dev_t dev, int oflags, int devtype, d_thread_t *proc)
{
	switch(conectDev[0][0].type)
	{
		case ATA:  return 0;
		case ATAPI:return testAtapi(0,0);
		default:   return -EDNODEV;
	}
}

static int openHdb(dev_t dev, int oflags, int devtype, d_thread_t *proc)
{
	switch(conectDev[0][1].type)
	{
		case ATA:  return 0;
		case ATAPI:return testAtapi(0,1);
		default:   return -EDNODEV;
	}
}

static int openHdc(dev_t dev, int oflags, int devtype, d_thread_t *proc)
{
	switch(conectDev[1][0].type)
	{
		case ATA:  return 0;
		case ATAPI:return testAtapi(1,0);
		default:   return -EDNODEV;
	}
}

static int openHdd(dev_t dev, int oflags, int devtype, d_thread_t *proc)
{
	switch(conectDev[1][1].type)
	{
		case ATA:  return 0;
		case ATAPI:return testAtapi(1,1);
		default:   return -EDNODEV;
	}
}

static int close(dev_t dev, int fflag, int devtype, d_thread_t *proc)
{
	return 0;
}

static int readHda(dev_t dev, struct uio *uio, int ioflag)
{
	return transfer(0, 0, READ, uio->uio_iov->iov_base, uio->uio_iov->iov_len, uio->uio_offset);
}

static int readHdb(dev_t dev, struct uio *uio, int ioflag)
{
	return transfer(0, 1, READ, uio->uio_iov->iov_base, uio->uio_iov->iov_len, uio->uio_offset);
}

static int readHdc(dev_t dev, struct uio *uio, int ioflag)
{
	return transfer(1, 0, READ, uio->uio_iov->iov_base, uio->uio_iov->iov_len, uio->uio_offset);
}

static int readHdd(dev_t dev, struct uio *uio, int ioflag)
{
	return transfer(1, 1, READ, uio->uio_iov->iov_base, uio->uio_iov->iov_len, uio->uio_offset);
}

static int writeHda(dev_t dev, struct uio *uio, int ioflag)
{
	return transfer(0, 0, WRITE, uio->uio_iov->iov_base, uio->uio_iov->iov_len, uio->uio_offset);
}

static int writeHdb(dev_t dev, struct uio *uio, int ioflag)
{
	return transfer(0, 1, WRITE, uio->uio_iov->iov_base, uio->uio_iov->iov_len, uio->uio_offset);
}

static int writeHdc(dev_t dev, struct uio *uio, int ioflag)
{
	return transfer(1, 0, WRITE, uio->uio_iov->iov_base, uio->uio_iov->iov_len, uio->uio_offset);
}

static int writeHdd(dev_t dev, struct uio *uio, int ioflag)
{
	return transfer(1, 1, WRITE, uio->uio_iov->iov_base, uio->uio_iov->iov_len, uio->uio_offset);
}

static int ioctlHda(dev_t dev, u_long cmd, caddr_t data, int fflag, d_thread_t *p)
{
	return 0;
}

static int ioctlHdb(dev_t dev, u_long cmd, caddr_t data, int fflag, d_thread_t *p)
{
	return 0;
}

static int ioctlHdc(dev_t dev, u_long cmd, caddr_t data, int fflag, d_thread_t *p)
{
	return 0;
}

static int ioctlHdd(dev_t dev, u_long cmd, caddr_t data, int fflag, d_thread_t *p)
{
	return 0;
}

static int poll(dev_t dev, int events, d_thread_t *p)
{
	return POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM;
}


static struct cdevsw hd_info[2][2]={
	{{openHda, close, readHda, writeHda, ioctlHda, poll, NULL, NULL, "hda", 0, NULL,  NULL, D_DISK,},
	 {openHdb, close, readHdb, writeHdb, ioctlHdb, poll, NULL, NULL, "hdb", 0, NULL,  NULL, D_DISK,}},
	{{openHdc, close, readHdc, writeHdc, ioctlHdc, poll, NULL, NULL, "hdc", 0, NULL,  NULL, D_DISK,},
	 {openHdd, close, readHdd, writeHdd, ioctlHdd, poll, NULL, NULL, "hdd", 0, NULL,  NULL, D_DISK,}}
};


/*
 * PRIVATE
 * Test atapi and read capacity
 * parameters : Host number,Device number
 * return : Redy=0 or Error number
 */
static int testAtapi(int host,int dev)
{
	int error;

	for(; testUnitReady(host, dev) != 0;){
		error = requestSense(host, dev);
		if (error == 0){
			break;
		}
		else if (error < 0){
			return error;
		}
	}

	/* Read sector size and max sector number */
	if ((error = readCapacity(host, dev)) != 0){
		return error;
	}
	hd_info[host][dev].last_blk = conectDev[host][dev].allSectors - 1;
	hd_info[host][dev].sector_size = conectDev[host][dev].sectorSize;

	return 0;
}


/************************************************************************************************
 *
 * < ATA initialize class >
 *
 ************************************************************************************************/


/*
 * PRIVATE
 * ワードのビッグエンディアンをリトルエンディアンに変える
 * parameters : string address,string length
 * return : string address
 */
static char *convertBigToLittle(char *str,int len)
{
	char c;
	int count;
	int i;


	/*
	 * ' 'が２つつづいたら'\0'を入れる
	 */
	count=0;
	for(i=0;i<len;++i)
	{
		c=str[i];
		str[i]=str[i+1];
		str[i+1]=c;

		if(str[i]==' ')++count;
		else count=0;
		if(count==2)break;
		if(str[++i]==' ')++count;
		else count=0;
		if(count==2)break;
	}
	str[--i]='\0';

	return str;
}


/*
 * GLOBAL
 * ATAの初期化
 * return : 0 or Error number
 */
int initAta()
{
	uchar cl,ch;
	int error;
	ID_INFO *id_info;
	int i,j,k;


	/* ATA common クラスの初期化。 */
	initCommon();

	if((id_info=(ID_INFO*)kmalloc(IDENTIFY_SIZE))==NULL)
		return -EDNOMEM;

	/* 接続デバイスを判定する */
	for(i=0;i<2;++i)
	{
		/* ソフトリセット */
		if(softReset(i)!=0)continue;

		for(j=0;j<2;++j)
		{
			/*
			 * ソフトリセット後の状態。
			 *  ATA:ATA_CLR=0 ATA_CHR=0, ATAPI:ATA_CLR=0x14 ATA_CHR=0xeb
			 */
			outb(reg[i].dhr,j<<4);
			mili_timer(5);
			cl=inb(reg[i].clr);
			ch=inb(reg[i].chr);

			if((cl==0)&&(ch==0))	/* ATA disk. */
			{
				id_info->model[0]=0xff;
				if(identifyDevice(i,j,ATA,id_info)!=0)continue;		/* Read identify infomation */
				if(id_info->model[0]==0xff)continue;				/* 読み出しているかをチェック */

				/* Print device infomation */
				convertBigToLittle(id_info->model,40);
				printk("%s : %s, %s\n", hd_info[i][j].d_name, id_info->model, "ATA DISK drive");

				/* LBA all sectors */
				conectDev[i][j].allSectors=(uint)id_info->lba_all_sect[1]<<16|(uint)id_info->lba_all_sect[0];
				if(conectDev[i][j].allSectors==0)
				{
					printk("This device is not support LBA. Stop initialize!");
					continue;
				}
				conectDev[i][j].type=ATA;
				conectDev[i][j].transfer=transferAta;

				/* Init device parameters */
				initDeviceParam(i,j,(uchar)id_info->n_head,id_info->n_sect);

				hd_info[i][j].last_blk=conectDev[i][j].allSectors-1;
				hd_info[i][j].sector_size=ATA_SECTOR_SIZE;
			}
			else if((cl==0x14)&&(ch==0xeb))	/* ATAPI device. */
			{
				char *media;


				if(identifyDevice(i,j,ATAPI,id_info)!=0)continue;	/* Read identify infomation */

				/* Print device infomation */
				convertBigToLittle(id_info->model,40);
				switch((id_info->config>>8)&0x1f)		/* Medium name */
				{
					case 0x5:
						media="ATAPI CDROM drive";
						break;
					default:
						media="ATAPI OTHER drive";
						break;
				}
				printk("%s : %s, %s\n", hd_info[i][j].d_name, id_info->model, media);

				conectDev[i][j].flag=id_info->iordy;
				conectDev[i][j].type=ATAPI;
				conectDev[i][j].transfer=transferAtapi;
			}
			else continue;

			/* Idle device */
			idleImmediateDevice(i,j);

			/* Set DMA or PIO mode. */
			if (changeMode(i, j, M_DMA) != 0){
				if ((error = changeMode(i, j, PIO)) != 0){
					printk("Transfer mode set error : %x\n", error);
				}
			}
		}
	}

	kfree(id_info);

	/*
	 * もう一度確認
	 * デバイスによっては、同一ホストが存在しない場合ビジー状態のままになってしまう。
	 */
	for (i = 0; i < 2; ++i){
		for (j = 0; j < 2; ++j){
			outb(reg[i].dhr, j << 4);
			mili_timer(5);
			if ((inb(reg[i].str) & BSY_BIT) == BSY_BIT){
				if ((error = softReset(i)) != 0){	/* soft reset */
					return error;
				}
				for (k = 0; k < 2; ++k){
					changeMode(i, k, conectDev[i][k].mode);	/* set transfer mode */
				}
			}
		}
	}

	/*
	 * 割り込みの設定
	 * 8259PICの場合マスクしても割り込みは保持されているので、マスク解除の後
	 * タイマーを入れて、ハンドラ設定前に割り込みを発生させる。
	 */
	release_irq_mask(PRIM_IRQ);
	release_irq_mask(SECOND_IRQ);
	mili_timer(2);

	irq_entry[IRQ14] = primIntrHandler;
	irq_entry[IRQ15] = secondIntrHandler;

	/* デバイスファイルシステムに登録 */
	for (i = 0; i < 2; ++i){
		for (j = 0; j < 2; ++j){
			if (conectDev[i][j].type != NODEV){
				make_dev(&hd_info[i][j], 0, 0, 0, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, "ata");
			}
			if (conectDev[i][j].type == ATA){
				/* パーティションディスクの登録 */
				registBlockPartition(hd_info[i][j].d_name);
			}
		}
	}
	
	return 0;
}

/******************************************************************/
void test_hd()
{
}
