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


#include"config.h"
#include"types.h"
#include"lib.h"
#include"mm.h"
#include"interrupt.h"
#include"proc.h"
#include"fs.h"
#include"errno.h"
#include"lock.h"
#include"device.h"


enum{
	MAX_NAME=16,			/* Device name size */
	HASH_NUM=CACHE_NUM,		/* Number of cache hash table */
	SECTER_SIZE=512
};


/* 論理デバイスごとに定義する情報。 */
typedef struct{
	uchar pt_num;		/* Partition number */
	uchar pt_pres;		/* Partition presence flag */
	uchar pt_type;		/* Partition type */
	char ref_count;		/* Reference count */
	DEV_INFO *dev;		/* Device infomation pointer */
	uint begin_blk;		/* Begin block number */
	uint blocks;		/* Number of blocks */
}DEV_INODE;

/* Partition table */
typedef struct{
	uchar active;			/* Boot active flag=0x80 */
	uchar chs_begin[3];		/* CHS begin block */
	uchar type;				/* Partition type */
	uchar chs_last[3];		/* CHS last block */
	uint lba_begin;			/* LBA begin block */
	uint size;				/* Partition size by secter */
}PARTITON_TBL;

typedef struct CACHE_TABLE{
	struct CACHE_TABLE *hnext;		/* Hash next */
	struct CACHE_TABLE *hprev;		/* Hash previous */
	struct CACHE_TABLE *next;		/* Cache chain next */
	struct CACHE_TABLE *prev;		/* Cache chain previous */
	struct CACHE_TABLE *write_next;	/* デバイスへ書き込み更新中リスト */
	struct CACHE_TABLE *write_prev; /* デバイスへ書き込み更新中リスト */
	void *addr;						/* Cache address */
	uint block;						/* Device block index */
	uchar din;						/* Device inode number */
	uchar ref_count;				/* 参照カウンタ */
	uchar update;					/* 更新中フラグ */
	uchar writing;					/* デバイスへ書き込み更新中フラグ */
	int gate;						/* スピンロックゲート */
}CACHE_TABLE;


static DEV_INFO *device[MAX_DEVICES];
static DEV_INODE dinode[MAX_DEVICE_OPEN];

/* Cache */
static CACHE_TABLE cache_tbl[CACHE_NUM];			/* Cache table */
static CACHE_TABLE *cache_hash[HASH_NUM];			/* Hash table for searching cache table */
static CACHE_TABLE *cache_tbl_old=&cache_tbl[0];	/* Cache table old point */
static int cache_gate=0;							/* キャッシュテーブル用スピンロックフラグ */
static int cache_ref_count=0;						/* キャッシュテーブル参照カウント */
static int cache_update=0;							/* キャッシュテーブル更新フラグ */

static PROC *write_task=NULL;						/* 遅延書き込み用タスク */
static CACHE_TABLE delay_list=						/* 遅延書き込みリストの先頭 */
	{NULL,NULL,NULL,NULL,&delay_list,&delay_list,NULL,0,0,0,0,0,0};


static int init_cache();
static void start_delay_write_task();
static void add_delay_list(CACHE_TABLE*);
static void delay_write();
static int open(const char*,int,uint);
static int close(int,uint);
static int read(int,uint,void*,size_t,size_t);
static int write(int,uint,void*,size_t,size_t);
static int ioctl(int,uint,int,void*);


static FS dev_fs={
	"dev_fs",NULL,NULL,open,close,read,write,ioctl,NULL,NULL,NULL,NULL
};


 /*
  * デバイスパス名を比較する。
  * parameters : destination path,sorce path
  * return : 前方一致 一致した次のポインタ,不一致 NULL
  */
extern inline const char *cmp_devpath(const char *s1,const char *s2)
{
	while(*s1!='\0')
		if(*s1++!=*s2++)return NULL;

	return s2;
}


/************************************************************************************************
 *
 * 初期化関数
 *
 ************************************************************************************************/


/*
 * Init device system
 * return : 0 or error=-1
 */
int init_device()
{
	int fs;
	int i;


	for(i=0;i<MAX_DEVICES;++i)device[i]=NULL;
	for(i=0;i<MAX_DEVICES;++i)dinode[i].ref_count=0;

	/* IOキャッシュを初期化する */
	init_cache();

	/* 遅延書き込み用タスクを起動する。 */
	start_delay_write_task();

	/* ファイルシステムに登録する。 */
	if((fs=regist_fs(&dev_fs))==-1)return -1;
	if(mount_dev_fs(fs)==-1)return -1;

	return 0;
}


/*
 * Register device
 * parameters : Device infomation struct address
 * return : 0 or Error number
 */
int regist_device(DEV_INFO *info)
{
	int i;


	for(i=0;i<MAX_DEVICES;++i)
		if(device[i]==NULL)
		{
			device[i]=info;
			return 0;
		}

	return -EMDEV;
}


/*
 * Delete from regist table
 * parameters : name
 * return : 0,error -1
 */
int delete_device(const char *name)
{
	return 0;
}


 /***********************************************************************************
 *
 * その他の関数
 *
 ***********************************************************************************/

/*
 * デバイス情報の取得。
 * parameters : device inode,stat store buffer
 * return : 0 or error=-1
 */
int get_dev_stat(int din,DEV_STAT *buf)
{
	if(dinode[din].ref_count==0)return -1;

	buf->prt_type=dinode[din].pt_type;
	buf->sect_size=dinode[din].dev->secter_size;
	buf->all_sect=dinode[din].blocks;

	return 0;
}


/************************************************************************************************
 *
 * 直接転送関数
 *
 ************************************************************************************************/

/*
 * parameters : device inode number,buffer address,transfer secters,begin secter
 * return : transfer secters or error=-1
 */
int read_direct(int din,void *buf,size_t secters,size_t begin)
{
	/* 論理ディスク制限のチェック。 */
	if(begin>dinode[din].blocks)return -1;
	if((secters>dinode[din].blocks)||(begin+secters>dinode[din].blocks))
		secters=dinode[din].blocks-begin;

	return dinode[din].dev->read(buf,secters,begin+dinode[din].begin_blk);
}


/*
 * parameters : device inode number,buffer address,transfer secters,begin secter
 * return : transfer secters or error=-1
 */
int write_direct(int din,void *buf,size_t secters,size_t begin)
{
	/* 論理ディスク制限のチェック。 */
	if(begin>dinode[din].blocks)return -1;
	if((secters>dinode[din].blocks)||(begin+secters>dinode[din].blocks))
		secters=dinode[din].blocks-begin;

	return dinode[din].dev->write(buf,secters,begin+dinode[din].begin_blk);
}


/***********************************************************************************
 *
 * IOキャッシュ関数
 *  キャッシュブロック単位で読み書きする。
 *
 ***********************************************************************************/

/*
 * ハッシュ値を計算する。
 * return : hash value
 */
extern inline int calc_hash(int value)
{
	return (value/8)%HASH_NUM;
}


extern inline int cache_rounddown(int value)
{
	return ~(CACHE_BLOCK_SIZE-1)/value;
}


/*
 * 読み取り共有ロック、更新が短期の場合用。
 * parameters : 読み取り参照カウンタ,更新フラグポインタ
 */
extern inline void enter_read_lock(uchar *refcnt,uchar *update,int *gate)
{
	enter_spinlock(gate);
	{
		while(*update==1);
		++*refcnt;
	}
	exit_spinlock(gate);
}


/*
 * 読み取り共有ロックから出る。
 * parameters : 読み取り参照カウンタ
 */
extern inline void exit_read_lock(uchar *refcnt)
{
	--*refcnt;
}

extern inline void set_update(uchar *refcnt,uchar *update,int *gate)
{
	/* ここに複数到達しても良いように、参照カウントを一旦減らす。 */
	--*refcnt;
	enter_spinlock(gate);
	{
		while(*refcnt!=0);
		*update=1;
		++*refcnt;
	}
	exit_spinlock(gate);
}

extern inline void reset_update(uchar *update)
{
	*update=0;
}


/*
 * Init cache
 * return : 0 or Error number
 */
int init_cache()
{
	int i;


	/* Init cache address */
	for(i=0;i<CACHE_NUM;++i)
	{
		memset(&cache_tbl[i],0,sizeof(CACHE_TABLE));
		if((cache_tbl[i].addr=kmalloc(CACHE_BLOCK_SIZE))==NULL)return -ENOMEM;
		cache_tbl[i].next=&cache_tbl[i+1];
		cache_tbl[i].prev=&cache_tbl[i-1];
		cache_tbl[i].hprev=&cache_tbl[i];
	}
	cache_tbl[CACHE_NUM-1].next=&cache_tbl[0];
	cache_tbl[0].prev=&cache_tbl[CACHE_NUM-1];

	/* Init hash table */
	for(i=0;i<HASH_NUM;++i)cache_hash[i]=NULL;

	return 0;
}


/*
 * キャッシュテーブルから該当デバイスブロックを探す、
 * なければキャッシュテーブルに追加する。
 * parameters : cache hash,device inode,device block
 * return : cache table addres or error=NULL
 */
CACHE_TABLE *search_cache(int hash,int din,size_t begin)
{
	int sect_num;		/* キャッシュブロックサイズにはいるセクター数。 */
	CACHE_TABLE *p;


	sect_num=CACHE_BLOCK_SIZE/dinode[din].dev->secter_size;

	enter_read_lock((uchar*)&cache_ref_count,(uchar*)&cache_update,&cache_gate);

	/* キャッシュを検索する。 */
	for(p=cache_hash[hash];;p=p->hnext)
	{
		/* キャッシュになし */
		if(p==NULL)
		{
			set_update((uchar*)&cache_ref_count,(uchar*)&cache_update,&cache_gate);

			/* 一番古いキャッシュを横取り */
			p=cache_tbl_old;
			cache_tbl_old=p->next;

			/* Delete old hash link */
			p->hprev->hnext=p->hnext;
			if(p->hnext!=NULL)p->hnext->hprev=p->hprev;

			/* Add new hash link */
			p->hnext=cache_hash[hash];
			p->hprev=(CACHE_TABLE*)&cache_hash[hash];
			if(cache_hash[hash]!=NULL)cache_hash[hash]->hprev=p;
			cache_hash[hash]=p;

			/* updateロック */
			enter_spinlock(&p->gate);
			{
				/* 遅延書き込み待ち。 */
				while(p->writing!=0);

				reset_update((uchar*)&cache_update);
				--cache_ref_count;
				while(cache_ref_count!=0);
				p->update=1;
				++cache_ref_count;
			}
			exit_spinlock(&p->gate);

			p->din=din;
			p->block=begin;

			/* デバイスからブロックを読み取る。*/
			if(dinode[din].dev->read(p->addr,sect_num,begin+dinode[din].begin_blk)<sect_num)
			{
				exit_read_lock((uchar*)&cache_ref_count);
				return NULL;
			}

			reset_update(&p->update);

			break;
		}

		/* キャッシュにあり */
		if((p->block==begin)&&(p->din==din))break;
	}

	exit_read_lock((uchar*)&cache_ref_count);

	return p;
}


/*
 * Read from cache
 * parameters : device inode number,buffer address,transfer secters,begin secter
 * return : transfer secters or error=-1
 */
int read_cache(int din,void *buf,size_t secters,size_t begin)
{
	int cache_mask;		/* キャッシュの先頭セクターを算出するためのビットマスク。 */
	int sect_num;		/* キャッシュブロックサイズにはいるセクター数。 */
	int sect_size;		/* デバイスセクターサイズ。 */
	int rest;			/* 残りの転送セクター数。 */
	CACHE_TABLE *p;
	int i,s,t;


	/* 論理ディスク制限のチェック。 */
	if(begin>dinode[din].blocks)return 0;
	if((secters>dinode[din].blocks)||(begin+secters>dinode[din].blocks))
		secters=dinode[din].blocks-begin;

	sect_size=dinode[din].dev->secter_size;
	sect_num=CACHE_BLOCK_SIZE/sect_size;
	cache_mask=~(sect_num-1);
	rest=secters;

	/* 転送がキャッシュブロックの途中から始まる場合。 */
	if((begin&~cache_mask)!=0)
	{
		s=begin&~cache_mask;

		/* キャッシュを検索 */
		if((p=search_cache(calc_hash(begin&cache_mask),din,begin&cache_mask))==NULL)return -1;

		if((t=sect_num-s)>secters)
		{
			memcpy(buf,(char*)p->addr+s*sect_size,secters*sect_size);
			return secters;
		}

		memcpy(buf,(char*)p->addr+s*sect_size,t*sect_size);
		(char*)buf+=t*sect_size;
		rest-=t;
		begin+=t;
	}

	for(i=0,s=rest-sect_num;i<=s;i+=sect_num)
	{
		/* キャッシュを検索 */
		if((p=search_cache(calc_hash(begin+i),din,begin+i))==NULL)return -1;

		memcpy(buf,p->addr,CACHE_BLOCK_SIZE);
		(char*)buf+=CACHE_BLOCK_SIZE;
	}

	/* 転送がキャッシュブロックの途中で終わる場合。 */
	if(i!=rest)
	{
		/* キャッシュを検索 */
		if((p=search_cache(calc_hash(begin+i),din,begin+i))==NULL)return -1;

		memcpy(buf,p->addr,sect_size*(rest-i));
	}

	return secters;
}


/*
 * Write to cache
 * parameters : device inode number,buffer address,transfer secters,begin secter
 * return : transfer secters or error=-1
 */
int write_cache(int din,void *buf,size_t secters,size_t begin)
{
	int cache_mask;		/* キャッシュの先頭セクターを算出するためのビットマスク。 */
	int sect_num;		/* キャッシュブロックサイズにはいるセクター数。 */
	int sect_size;		/* デバイスセクターサイズ。 */
	int rest;			/* 残りの転送セクター数。 */
	CACHE_TABLE *p;
	int i,s,t;


	/* 論理ディスク制限のチェック。 */
	if(begin>dinode[din].blocks)return 0;
	if((secters>dinode[din].blocks)||(begin+secters>dinode[din].blocks))
		secters=dinode[din].blocks-begin;

	sect_size=dinode[din].dev->secter_size;
	sect_num=CACHE_BLOCK_SIZE/sect_size;
	cache_mask=~(sect_num-1);
	rest=secters;

	/* 転送がキャッシュブロックの途中から始まる場合。 */
	if((begin&~cache_mask)!=0)
	{
		s=begin&~cache_mask;

		/* キャッシュを検索 */
		if((p=search_cache(calc_hash(begin&cache_mask),din,begin&cache_mask))==NULL)return -1;

		if((t=sect_num-s)>secters)
		{
			memcpy((char*)p->addr+s*sect_size,buf,secters*sect_size);
			add_delay_list(p);		/* 遅延書き込みリストに加える。 */
			return secters;
		}

		memcpy((char*)p->addr+s*sect_size,buf,t*sect_size);
		(char*)buf+=t*sect_size;
		rest-=t;
		begin+=t;

		/* 遅延書き込みリストに加える。 */
		add_delay_list(p);
	}

	for(i=0,s=rest-sect_num;i<=s;i+=sect_num)
	{
		/* キャッシュを検索 */
		if((p=search_cache(calc_hash(begin+i),din,begin+i))==NULL)return -1;

		memcpy(p->addr,buf,CACHE_BLOCK_SIZE);
		(char*)buf+=CACHE_BLOCK_SIZE;

		/* 遅延書き込みリストに加える。 */
		add_delay_list(p);
	}

	/* 転送がキャッシュブロックの途中で終わる場合。 */
	if(i!=rest)
	{
		/* キャッシュを検索 */
		if((p=search_cache(calc_hash(begin+i),din,begin+i))==NULL)return -1;

		memcpy(p->addr,buf,sect_size*(rest-i));

		/* 遅延書き込みリストに加える。 */
		add_delay_list(p);
	}

	return secters;
}


/************************************************************************************************
 *
 * デバイス遅延書き込み
 *
 ************************************************************************************************/

/*
 * 遅延書き込みタスクの起動。
 */
void start_delay_write_task()
{
	int proc;


	switch(proc=sys_fork())
    {
		case -1:
			printk("Faild start_delay_write_task\n");
			break;
		case 0:
			delay_write();
		default:
			write_task=(PROC*)proc;
	}
}


/*
 * 遅延書き込みリストに加える。
 * parameters : device inode number,書き込みブロック数,ブロックの始まり
 */
void add_delay_list(CACHE_TABLE *p)
{
	enter_spinlock(&p->gate);
	{
		p->writing=1;
	}
	exit_spinlock(&p->gate);

	enter_spinlock(&delay_list.gate);
	{
		p->write_next=delay_list.write_next;
		p->write_prev=&delay_list;
		p->write_next->write_prev=p;
		delay_list.write_next=p;
	}
	exit_spinlock(&delay_list.gate);

	/* 遅延書き込みタスクを起こす。 */
	if(p->write_next==&delay_list)add_to_schedule(write_task);
}


/*
 * デバイスへの遅延書き込みタスク用関数。
 */
void delay_write()
{
	CACHE_TABLE *p;


	while(write_task==NULL);
	del_from_schedule(write_task);
	wait_task();

	for(;;)
	{
		enter_spinlock(&delay_list.gate);

		if(delay_list.write_next!=&delay_list)
		{
			p=delay_list.write_prev;
			p->write_prev->write_next=&delay_list;
			delay_list.write_prev=p->write_prev;

			exit_spinlock(&delay_list.gate);

			/* デバイスに書き込む。 */
			dinode[p->din].dev->write(
				p->addr,CACHE_BLOCK_SIZE/dinode[p->din].dev->secter_size,p->block+dinode[p->din].begin_blk);

			p->writing=0;
		}
		else
		{
			exit_spinlock(&delay_list.gate);

			/* スリープする。 */
			del_from_schedule(write_task);
			wait_task();
		}
	}
}


/************************************************************************************************
 *
 * System call interface
 *
 ************************************************************************************************/

/*
 * Open device
 * device inodeの0はすでにデバイスファイルシステムに使われているので、1から開始する。
 * parameters : Device name,device inode=0,directory block=0
 * return : device inode number or error=-1
 */
int open(const char *dev_name,int dev,uint dir)
{
	enum{
		/* Partition table */
		PT_NUM=4,				/* 1セクター内のパーティションテーブル数 */
		PT_BEGIN=0x1be,			/* パーティションテーブル開始オフセット */
		PT_MAGIC_ADDR=0x1fe,	/* パーティションテーブルマジックナンバーオフセット */
		PT_MAGIC_NUMBER=0xaa55,	/* パーティションテーブルマジックナンバー */

		/* Partition type */
		PT_TYPE_NULL=0,			/* NULL partition */
		PT_TYPE_EXT=0x5,		/* ext partition */
		PT_TYPE_LEXT=0xf,		/* ext(LBA) partition */
	};

	const char *str;
	char *buf;
	int inode_num;
	int pt_num;
	uint beg_blk,next_blk;
	PARTITON_TBL *pt;
	int i;


	/* 登録デバイスから名前で検索 */
	for(i=0;(str=cmp_devpath(device[i]->name,dev_name))==NULL;++i)
		if(i>=MAX_DEVICES)return -1;
	dev=i;

	/* 名前にパーティション番号があれば、整数に変換する */
	pt_num=0;
	if(*str!='\0')
		if((pt_num=atoi(str))<=0)return -1;		/* パーティション番号は1以上 */

	/* すでにオープンされているか確認 */
	for(i=1;i<MAX_DEVICE_OPEN;++i)
		if((dinode[i].dev==device[dev])&&(dinode[i].pt_num==pt_num))
		{
			++dinode[i].ref_count;
			return i;
		}

	/* 空きデバイスinode検索をする。 */
	for(i=1;dinode[i].ref_count!=0;++i)
		if(i>=MAX_DEVICE_OPEN)return -1;
	inode_num=i;

	/* Open device */
	if(device[dev]->open()<0)return -1;

	/* パーティションテーブルを検索する */
	if((buf=(char*)kmalloc(device[dev]->secter_size))==NULL)return -1;
	if(device[dev]->read(buf,1,0)!=1)goto ERR;
	if(pt_num==0)
	{
		if(*(ushort*)(buf+PT_MAGIC_ADDR)!=PT_MAGIC_NUMBER)dinode[inode_num].pt_pres=1;
		else dinode[inode_num].pt_pres=0;

		dinode[inode_num].pt_pres=0;
		dinode[inode_num].pt_num=0;
		dinode[inode_num].pt_type=0;
		dinode[inode_num].begin_blk=device[dev]->begin_blk;
		dinode[inode_num].blocks=device[dev]->last_blk-device[dev]->begin_blk+1;
	}
	else
	{
		if(*(ushort*)(buf+PT_MAGIC_ADDR)!=PT_MAGIC_NUMBER)goto ERR;

		pt=(PARTITON_TBL*)(buf+PT_BEGIN);
		if(pt_num<=PT_NUM)
		{
			if(pt[pt_num].type==PT_TYPE_EXT)goto ERR;	/* 基本領域の拡張パーティションは開けない。 */
			
			dinode[inode_num].pt_num=pt_num;
			--pt_num;
			dinode[inode_num].pt_type=pt[pt_num].type;
			dinode[inode_num].begin_blk=pt[pt_num].lba_begin;
			dinode[inode_num].blocks=pt[pt_num].size;
		}
		else
		{
			for(i=0;pt[i].type!=PT_TYPE_EXT;++i)
				if(i>=PT_NUM)goto ERR;			/* 拡張パーティションがない */

			/* 拡張パーティションリンクをたどる。 */
			beg_blk=next_blk=pt[i].lba_begin;
			for(i=PT_NUM;;)
			{
				if(device[dev]->read(buf,1,next_blk)!=1)goto ERR;
				if(++i==pt_num)break;
				if(pt[1].type!=PT_TYPE_EXT)goto ERR;
				next_blk=beg_blk+pt[1].lba_begin;
			}

			dinode[inode_num].pt_num=pt_num;
			dinode[inode_num].pt_type=pt[0].type;
			dinode[inode_num].begin_blk=pt[0].lba_begin+next_blk;
			dinode[inode_num].blocks=pt[0].size;
		}

		dinode[inode_num].pt_pres=1;
	}

	kfree(buf);

	dinode[inode_num].dev=device[dev];
	dinode[inode_num].ref_count=1;

	return inode_num;

ERR:
	kfree(buf);

	return -1;
}

/*
 * parameters : device inode,inode block
 */
int close(int din,uint inode)
{
	if(--dinode[inode].ref_count<0)dinode[inode].ref_count=0;

	return 0;
}

/*
 * parameters : device inode,inode,Buffer,Transfer bytes,begin byte
 */
int read(int din,uint inode,void *buf,size_t size,size_t begin)
{
	char *tmp=NULL;
	int sect_siz;		/* セクターサイズ */
	int rest=size;		/* 残りの転送バイト数 */
	int beg_sect;		/* 転送始めのセクター番号 */
	int s,t;


	sect_siz=dinode[inode].dev->secter_size;
	beg_sect=begin/sect_siz;

	/* 最初のセクターサイズ未満の転送。 */
	if((s=begin%sect_siz)!=0)
	{
		if((tmp=(char*)kmalloc(sect_siz))==NULL)return -1;
		if(read_cache(din,tmp,1,beg_sect)!=1)return 0;
		t=sect_siz-s;
		if(t>size)
		{
			memcpy(buf,tmp+s,size);
			kfree(tmp);
			return size;
		}
		memcpy(buf,tmp+s,t);
		(char*)buf+=t;
		beg_sect+=1;
		rest-=t;
	}

	s=rest/sect_siz;
	if((t=read_cache(din,buf,s,beg_sect))<s)
	{
		kfree(tmp);
		return size-rest+t*sect_siz;
	}

	/* 残りがセクターサイズ未満の転送。 */
	if(s*sect_siz<rest)
	{
		t=s*sect_siz;
		if((tmp==NULL)&&((tmp=(char*)kmalloc(sect_siz))==NULL))return -1;
		if(read_cache(din,tmp,1,beg_sect+s)!=1)
		{
			kfree(tmp);
			return size-rest+t;
		}
		memcpy((char*)buf+t,tmp,rest-t);
	}

	kfree(tmp);

	return size;
}

/*
 * parameters : device inode,inode,Buffer,Transfer bytes,begin byte
 */
int write(int din,uint inode,void *buf,size_t size,size_t begin)
{
	char *tmp=NULL;
	int sect_siz;		/* セクターサイズ */
	int rest=size;		/* 残りの転送バイト数 */
	int beg_sect;		/* 転送始めのセクター番号 */
	int s,t;


	sect_siz=dinode[inode].dev->secter_size;
	beg_sect=begin/sect_siz;

	/* 最初のセクターサイズ未満の転送。 */
	if((s=begin%sect_siz)!=0)
	{
		if((tmp=(char*)kmalloc(sect_siz))==NULL)return -1;
		if(read_cache(din,tmp,1,beg_sect)!=1)return 0;
		if((t=sect_siz-s)>size)
		{
			memcpy(tmp+s,buf,size);
			if(write_cache(din,tmp,1,beg_sect)!=1)return 0;
			kfree(tmp);
			return size;
		}
		memcpy(tmp+s,buf,t);
		if(write_cache(din,tmp,1,beg_sect)!=1)return 0;
		(char*)buf+=t;
		beg_sect+=1;
		rest-=t;
	}

	s=rest/sect_siz;
	if((t=write_cache(din,buf,s,beg_sect))<s)
	{
		kfree(tmp);
		return size-rest+t*sect_siz;
	}

	/* 残りがセクターサイズ未満の転送。 */
	if(s*sect_siz<rest)
	{
		t=s*sect_siz;
		if((tmp==NULL)&&((tmp=(char*)kmalloc(sect_siz))==NULL))return -1;
		if(read_cache(din,tmp,1,beg_sect+s)!=1)
		{
			kfree(tmp);
			return size-rest+t;
		}
		memcpy(tmp,(char*)buf+t,rest-t);
		if(write_cache(din,tmp,1,beg_sect+s)!=1)
		{
			kfree(tmp);
			return size-rest+t;
		}
	}

	kfree(tmp);

	return size;
}

/*
 * parameters : device inode,inode,Command,Parameter struct pointer
 */
int ioctl(int din,uint ionode,int com,void *prm)
{
	return 0;
}
/****************************************************************************/
void test_device()
{
	uint *buf2=(uint*)0x90000;
	int i;

	mili_timer(2000);

	i=open("hdb",0,0);

	read_direct(i,buf2,9,2000);
	printk("buf2[5]=%x,buf2[0x400+4]=%x\n",buf2[5],buf2[0x400+4]);
}
/*****************************************************************************/
