/*
 * 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 <lock.h>
#include <device.h>
#include <except_i386.h>
#include <errno.h>
#include <lock.h>
#include <time.h>
#include <signal.h>
#include <sys/param.h>
#include <sys/conf.h>
#include <sys/systm.h>
#include <test.h>
#include <debug.h>


//#define DEBUG_DEVICE 1
#ifdef DEBUG_DEVICE
	#define STATIC
	#define INLINE
#else
	#define STATIC static
	#define INLINE	inline
#endif


/******************************************************************************************************
 *
 * デバイスオペレーション
 *
 *******************************************************************************************************/


STATIC INLINE int readDev(dev_t dev, void *buf, size_t len, size_t begin, int ioflag)
{
	struct iovec iov;
	struct uio uio;
	
	iov.iov_base = buf;
	iov.iov_len = len;
	uio.uio_iov = &iov;
	uio.uio_iovcnt = 1;
	uio.uio_offset = begin;
	uio.uio_resid = len;
	uio.uio_segflg = UIO_USERSPACE;
	uio.uio_rw = UIO_READ;
	uio.uio_procp = NULL;

	return dev->si_devsw->d_read(dev, &uio, ioflag);
}


STATIC INLINE int writeDev(dev_t dev, void *buf, size_t len, size_t begin, int ioflag)
{
	struct iovec iov;
	struct uio uio;
	
	iov.iov_base = buf;
	iov.iov_len = len;
	uio.uio_iov = &iov;
	uio.uio_iovcnt = 1;
	uio.uio_offset = begin;
	uio.uio_resid = len;
	uio.uio_segflg = UIO_USERSPACE;
	uio.uio_rw = UIO_WRITE;
	uio.uio_procp = NULL;

	return dev->si_devsw->d_write(dev, &uio, ioflag);

}


/******************************************************************************************************
 *
 * デバイス割り込み待ち
 *
 *******************************************************************************************************/


//================================== PRIVATE ============================================


// 割り込み待ちプロセスを設定する前に割り込みが発生したときに設定される値。
#define WAIT_INTR_ACCESS ((PROC*)-1)


//================================== PUBLIC =============================================


void wait_intr(WAIT_INTR *wait, uint time, int procStat)
{
	int eflag;

	enterCli(&eflag);
	enter_spinlock(&wait->lock);
	if (wait->proc == NULL){
		wait->proc = get_current_task();

		exit_spinlock(&wait->lock);
		exitCli(&eflag);

		setWake(time);
		del_from_schedule(procStat, wait->isDel);
		wait_task();
		delWakeTimer(wait->proc->timer);
	}
	else{
		exit_spinlock(&wait->lock);
		exitCli(&eflag);
	}

	wait->proc = NULL;
	wait->isDel = PROC_SCHEDULE_DEL;
}


int wake_intr(WAIT_INTR *wait, int procStat)
{
	enter_spinlock(&wait->lock);
	if (wait->proc != NULL){
		exit_spinlock(&wait->lock);
		add_to_schedule(wait->proc, procStat, &wait->isDel);
		return 1;
	}
	else{
		wait->proc = WAIT_INTR_ACCESS;
		exit_spinlock(&wait->lock);
		return 0;
	}
}


/***********************************************************************************
 *
 * ブロックデバイス
 *
 ***********************************************************************************/


enum{
	/* Partition table */
	PARTITION_TBL_MAX		= 4,		/* 1セクター内の最大パーティションテーブル数 */
	PARTITION_BEGIN			= 0x1be,	/* パーティションテーブル開始オフセット */
	PARTITION_MAGIC_ADDR	= 0x1fe,	/* パーティションテーブルマジックナンバーオフセット */
	PARTITION_MAGIC_NUMBER	= 0xaa55,	/* パーティションテーブルマジックナンバー */

	/* Partition type */
	PARTITION_TYPE_NULL		= 0,	/* NULL partition */
	PARTITION_TYPE_EXT		= 0x5,	/* 拡張タイプ */
};


/* Partition table */
typedef struct{
	uint8_t		active;		/* Boot active flag=0x80 */
	uint8_t		chsBeg[3];	/* CHS begin block */
	uint8_t		type;		/* Partition type */
	uint8_t		chsEnd[3];	/* CHS last block */
	uint32_t	lbaBeg;		/* LBA begin block */
	uint32_t	size;		/* Partition size by sector */
}PARTITON_TBL;


//================================== PROTECTED ==========================================


// パーティションテーブルを検索する
// return : NOERR
//          ENODEV=削除済み or 拡張パーティション 
//          ENOENT=拡張パーティションリンクの最後に到達
static INLINE dev_t getDevInfo(int inodeNum);
STATIC int readPartition(const int inodeNum, const int partNum, PARTITON_TBL *o_partTbl)
{
	char *sectBuf;
	dev_t dev;
	PARTITON_TBL *partTbl;
	uint extBeg, extNext;
	int c_part;
	int i, rest;

	// デバイスオープン
	dev = getDevInfo(inodeNum);
	if (dev == NULL){
		return -ENOENT;
	}
	if (dev->si_devsw->d_open(dev, O_RDONLY, 0, NULL) < 0){
		return -EIO;
	}

	/* MBRを読む */
	sectBuf = kmalloc(dev->si_devsw->sector_size);
	if (sectBuf == NULL){
		return -ENOMEM;
	}
	rest = readDev(dev, sectBuf, 1, 0, 0);
	if (rest < 0){
		goto END;
	}

	/* パーティションテーブルを確認 */
	if (*(uint16_t*)(sectBuf + PARTITION_MAGIC_ADDR) != PARTITION_MAGIC_NUMBER){
		rest = -ENODEV;
		goto END;
	}
	partTbl = (PARTITON_TBL*)(sectBuf + PARTITION_BEGIN);

	if (partNum <= PARTITION_TBL_MAX){
		/* 基本パーティションを検索 */
		c_part = partNum - 1;

		switch (partTbl[c_part].type){
			/* 基本領域の拡張パーティションは開けない */
			case PARTITION_TYPE_EXT:
				// 下へ
			case PARTITION_TYPE_NULL:
				rest = -ENODEV;
				goto END;
			default:
				/* 出力用パーティションテーブルを設定 */
				o_partTbl->type		= partTbl[c_part].type;
				o_partTbl->lbaBeg	= partTbl[c_part].lbaBeg;
				o_partTbl->size		= partTbl[c_part].size;
		}
	}
	else{
		/* 拡張パーティションを検索 */
		/*
		 * MBR内の拡張パーティションを検索
		 * out : c_part = 拡張パーティション配列番号
		 */
		for (i = 0;; ++i){
			if (PARTITION_TBL_MAX <= i){
				rest = -ENOENT;
				goto END;
			}
			if (partTbl[i].type == PARTITION_TYPE_EXT){
				c_part = i;
				break;
			}
		}

		/*
		 * 拡張パーティションリンクをたどる
		 * out : extNext = 拡張領域始まりからのブロック数 sectBuf = 拡張論理領域のパーティションセクター
		 */
		extBeg = extNext = partTbl[c_part].lbaBeg;
		for (i = PARTITION_TBL_MAX; ;){
			rest = readDev(dev, sectBuf, 1, extNext, 0);
			if (rest < 0){
				goto END;
			}

			/* 該当パーティション発見 */
			if (++i == partNum){
				break;
			}

			/* 拡張パーティションリンクの終了 */
			if (partTbl[1].type != PARTITION_TYPE_EXT){
				rest = -ENOENT;
				goto END;
			}

			extNext = extBeg + partTbl[1].lbaBeg;
		}

		/* 削除済みパーティション */
		if (partTbl[0].type == PARTITION_TYPE_NULL){
			rest = -ENODEV;
			goto END;
		}
		else{
			/* 出力用パーティションテーブルを設定 */
			o_partTbl->type		= partTbl[0].type;
			o_partTbl->lbaBeg	= partTbl[0].lbaBeg + extNext;
			o_partTbl->size		= partTbl[0].size;
		}
	}

	rest = NOERR;
END:
	kfree(sectBuf);

	return rest;
}


/***********************************************************************************
 *
 * デバイスファイルシステム
 *
 * 構造
 *	inode構造体を配列で確保
 *		root inodeは１個独立させる
 *
 * ========== PRIVATE
 * void setRootInode()
 * void partNumToStr(int value, char *m_str)
 * int getMajorNumber()
 * dev_t allocSpecinfo()
 * void freeSpecinfo(dev_t dev)
 * int getInodeNum(DEV_INODE *devInode)
 * void linkInode(int inodeNum)
 * int getFreeInode()
 * int setInode(struct cdevsw *cdevsw, uint blockBeg, uint size, int partType, int major, int partNum, int fflag)
 * ========== PROTECTED
 * void unlinkInode(int inodeNum)
 * int lookupDevInode(const char *path)
 * int getNextInode(int inodeNum)
 * int getStat(int inodeNum, struct stat *m_stat)
 * dev_t getDevInfo(int inodeNum)
 * uint getBlocks(int inodeNum)
 * uint getBegBlock(int inodeNum)
 * uint getMode(int inodeNum)
 * const char *getName(int inodeNum)
 * void initDevFs()
 * ========== PUBLIC
 * dev_t make_dev(struct cdevsw *devsw, int minor, uid_t uid, gid_t gid, int perms, const char *fmt, ...)
 * int get_devstat(int inodeNum, DEV_STAT *m_devStat)
 * int delete_device(const char *name)
 * int registBlockPartition(const char *name)
 ***********************************************************************************/


enum{
	DEVICE_INODE_NUM	= MAX_DEVICE_OPEN,	// デバイスinode総数。
	MAX_DEVICE_NAME		= 8,				// デバイス名最大文字数
	MAX_PART_NUM_DIGIT	= 4,				// ブロックパーティション番号最大桁
};


/* デバイスinode */
typedef struct DEV_INODE{
	dev_t				dev;					// struct specinfo*
	ushort				partType;				// パーティションタイプ
	ushort				major;					// メジャー番号
	ushort				partNum;				// パーティション番号＝マイナー番号
	short				mode;					/* ファイルモード */
	int 				refCount;				/* 参照カウンタ */
	uint				blockBeg;				/* ディスク中でのパーティションの始めのブロック */
	uint				blocks;					/* パーティションの全ブロック数 */
	uint				creatTime;				/* 作成時間 */
	char				name[MAX_DEVICE_NAME];	/* デバイス名 */
}DEV_INODE;


//================================== PRIVATE ============================================


/* デバイスファイルシステムinodeテーブル */
static DEV_INODE devInodeTbl[DEVICE_INODE_NUM];	/* inodeテーブル */

// dev_tの静的領域
struct devData{
	struct specinfo specinfo[DEVICE_INODE_NUM];
	char busy[DEVICE_INODE_NUM];		// 使用中なら１
}devData;


// root inodeの設定
STATIC void setRootInode()
{
	/* inodeの設定 */
	devInodeTbl[DEVICE_ROOT_INODE].partType = 0;
	devInodeTbl[DEVICE_ROOT_INODE].partNum = 0;
	devInodeTbl[DEVICE_ROOT_INODE].refCount = 2;
	devInodeTbl[DEVICE_ROOT_INODE].dev = NULL;
	devInodeTbl[DEVICE_ROOT_INODE].blockBeg = 0;
	devInodeTbl[DEVICE_ROOT_INODE].blocks = 0;
	devInodeTbl[DEVICE_ROOT_INODE].creatTime = sys_time(NULL);
	devInodeTbl[DEVICE_ROOT_INODE].mode = S_IFDIR | S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
}


// パーティション番号を文字列に変換する。
// 文字数は"MAX_PART_NUM_DIGIT"で切る。
STATIC void partNumToStr(int value, char *m_str)
{
	char str[MAX_PART_NUM_DIGIT + 1];
	int i;

	for (i = 0; i < MAX_PART_NUM_DIGIT; ++i){
		str[i] = value % 10 + '0';
		if ((value /= 10) == 0){
			break;
		}
	}
	m_str[i + 1] = '\0';
	while (i >= 0){
		*m_str++ = str[i--];
	}
}


// メジャー番号を取得する。
STATIC int getMajorNumber()
{
	static int majorNum = 0;
	
	return majorNum++;
}


// dev_tを割り当てる
// return : dev_t ro NULL
STATIC dev_t allocSpecinfo()
{
	dev_t dev = NULL;
	int i;
	
	for (i = 0; i < DEVICE_INODE_NUM; ++i){
		if (devData.busy[i] == NO){
			devData.busy[i] = YES;
			dev = &devData.specinfo[i];
			break;
		}
	}
	
	return dev;
}


// dev_tを開放する
STATIC void freeSpecinfo(dev_t dev)
{
	int i;

	for (i = 0; i < DEVICE_INODE_NUM; ++i){
		if (dev == &devData.specinfo[i]){
			devData.busy[i] = NO;
			break;
		}
	}
}


// デバイスinodeからinode番号を取得する
STATIC INLINE int getInodeNum(DEV_INODE *devInode)
{
	return ((uint)devInode - (uint)devInodeTbl) / sizeof(*devInode);
}


// dev_tからinode番号を検索する。
STATIC int searchInodeNum(dev_t dev)
{
	int i;
	
	for (i = 0; i < DEVICE_INODE_NUM; ++i){
		if (0 < devInodeTbl[i].refCount){
			if (devInodeTbl[i].dev == dev){
				return i;
			}
		}
	}
	
	return -ENOENT;
}


// inodeの参照カウンタを１増やす
STATIC void linkInode(int inodeNum)
{
	ASSERT(0 <= devInodeTbl[inodeNum].refCount);
	devInodeTbl[inodeNum].refCount += 1;
}


// 空きinodeを取得する
// return : inode number or error number
STATIC int getFreeInode()
{
	int i;
	
	for (i = 0; i < DEVICE_INODE_NUM; ++i){
		if (devInodeTbl[i].refCount == 0){
			return i;
		}
	}

	return -EMFILE;
}


// デバイスinodeを設定する。
// return : device inode or error number
static int lookupDevInode(const char *);
STATIC int setInode(
	struct cdevsw *cdevsw,
	uint blockBeg,
	uint size,
	int partType,
	int major,
	int partNum,
	int fflag)
{
	char numStr[MAX_PART_NUM_DIGIT + 1] = {'\0'};
	char name[MAX_DEVICE_NAME + MAX_PART_NUM_DIGIT];
	int inodeNum;

	/* パーティション番号を結合したデバイスネームの文字数の確認 */
	if (0 < partNum){
		partNumToStr(partNum, numStr);
	}
	strcpy(name, cdevsw->d_name);
	strcat(name, numStr);
	if (MAX_DEVICE_NAME <= strlen(name)){
		return -ENAMETOOLONG;
	}

	/* すでに登録があるか？ */
	inodeNum = lookupDevInode(name);
	if (inodeNum < 0){
		// 登録なし
		/* 空きinodeを取得する */
		inodeNum = getFreeInode();
		if (inodeNum < 0){
			return inodeNum;
		}

		/* inodeの設定 */
		devInodeTbl[inodeNum].dev = allocSpecinfo();
		if (devInodeTbl[inodeNum].dev == NULL){
			return -ENFILE;
		}
		devInodeTbl[inodeNum].partType = partType;
		devInodeTbl[inodeNum].major = major;
		devInodeTbl[inodeNum].partNum = partNum;
		devInodeTbl[inodeNum].blockBeg = blockBeg;
		devInodeTbl[inodeNum].blocks = size;
		devInodeTbl[inodeNum].creatTime = sys_time(NULL);
		if (cdevsw->d_flags & (D_TAPE | D_TTY | D_MEM)){
			// キャラクタデバイス
			devInodeTbl[inodeNum].mode = S_IFCHR | fflag;
		}
		else if (cdevsw->d_flags & D_DISK){
			// ブロックデバイス
			devInodeTbl[inodeNum].mode = S_IFBLK | fflag;
		}
		else{
			// とりあえずキャラクタデバイスにしておく
			devInodeTbl[inodeNum].mode = S_IFCHR | fflag;
		}
		/* デバイス名 */
		strcpy(devInodeTbl[inodeNum].name, name);
		linkInode(inodeNum);

		// devの設定
		devInodeTbl[inodeNum].dev->si_udev = makeudev(major, partNum);
		devInodeTbl[inodeNum].dev->si_devsw = cdevsw;
	}
	else{
		return -EEXIST;
	}

	return inodeNum;
}


// cdevsw構造体のNULL関数を変換する。
STATIC void compile_devsw(struct cdevsw *devsw)
{
	if (devsw->d_open == NULL)
		devsw->d_open = noopen;
	if (devsw->d_close == NULL)
		devsw->d_close = noclose;
	if (devsw->d_read == NULL)
		devsw->d_read = noread;
	if (devsw->d_write == NULL)
		devsw->d_write = nowrite;
	if (devsw->d_ioctl == NULL)
		devsw->d_ioctl = noioctl;
	if (devsw->d_poll == NULL)
		devsw->d_poll = nopoll;
	if (devsw->d_mmap == NULL)
		devsw->d_mmap = nommap;
	if (devsw->d_strategy == NULL)
		devsw->d_strategy = nostrategy;
	if (devsw->d_dump == NULL)
		devsw->d_dump = nodump;
	if (devsw->d_psize == NULL)
		devsw->d_psize = nopsize;
	if (devsw->d_kqfilter == NULL)
		devsw->d_kqfilter = nokqfilter;
}


//================================== PROTECTED ==========================================


// inodeの参照カウンタを１減らす。
// return : 参照数
STATIC void unlinkInode(int inodeNum)
{
	ASSERT(0 <= devInodeTbl[inodeNum].refCount);
	// 参照カウントを減らす
	devInodeTbl[inodeNum].refCount -= 1;
	if (devInodeTbl[inodeNum].refCount == 0){
		// dev_tを開放する
		freeSpecinfo(devInodeTbl[inodeNum].dev);
	}
}


// デバイスの検索
// return : inode number or error number
STATIC int lookupDevInode(const char *path)
{
	const char *devName;
	int i;
	
	// パスの検索
	// out : devName = "path"から抽出したデバイス名
	for (devName = path;;){
		/* "."と".."の処理 */
		if (cmpPath(".",devName,1) == 0){
			if (path[1] == '/'){
				devName = &path[2];
			}
			else{
				devName = &path[1];
			}
		}
		/* ".."は未対応 */
		else if (cmpPath("..",devName,2) == 0){
			return -ENOENT;
		}
		/* root inode? */
		else if (cmpPath("\0",devName,0) == 0){
			if (path[1] == '/'){
				devName = &path[1];
			}
			else{
				return DEVICE_ROOT_INODE;
			}
		}
		else{
			break;
		}
	}

	// デバイスinodeテーブルを検索
	for (i = 0; i < DEVICE_INODE_NUM; ++i){
		if (strcmp(devInodeTbl[i].name, devName) == 0){
			if (0 < devInodeTbl[i].refCount){
				return i;
			}
		}
	}

	return -ENOENT;
}


// 与えられたinodeの次のリンクのinodeを返す。
// 与えられたinodeが-1ならトップリンクのinodeを返す。
// return : next device inode or ERR
STATIC int getNextInode(int inodeNum)
{
	int i;
	
	if (inodeNum == -1){
		i = DEVICE_ROOT_INODE;
	}
	else{
		i = inodeNum;
	}
	for (++i; i < DEVICE_INODE_NUM; ++i){
		if (0 < devInodeTbl[i].refCount){
			return i;
		}
	}
	
	return ERR;
}


// stat情報を設定する
STATIC int getStat(int inodeNum, struct stat *m_stat)
{
	ASSERT((0 <= inodeNum) && (inodeNum < DEVICE_INODE_NUM));
	if (devInodeTbl[inodeNum].refCount == 0){
		return -ENOENT;
	}

	m_stat->st_dev			= devInodeTbl[inodeNum].dev->si_udev;
	m_stat->st_ino			= inodeNum;
	m_stat->st_mode			= devInodeTbl[inodeNum].mode;
	m_stat->st_nlink		= devInodeTbl[inodeNum].refCount;
	m_stat->st_uid			= 0;
	m_stat->st_gid			= 0;
	m_stat->st_size			= 0;
	m_stat->st_atime		= devInodeTbl[inodeNum].creatTime;
	m_stat->st_mtime		= devInodeTbl[inodeNum].creatTime;
	m_stat->st_ctime		= devInodeTbl[inodeNum].creatTime;
	m_stat->st_crtpos		= 0;
	if (devInodeTbl[inodeNum].dev == NULL){
		m_stat->st_blksize	= 0;
	}
	else{
		m_stat->st_blksize	= devInodeTbl[inodeNum].dev->si_devsw->sector_size;
	}
	m_stat->st_rdev			= devInodeTbl[inodeNum].dev->si_udev;
	m_stat->st_flags		= 0;
	m_stat->st_blocks		= devInodeTbl[inodeNum].blocks;

	return NOERR;
}


// dev_tを返す
STATIC INLINE dev_t getDevInfo(int inodeNum)
{
	ASSERT((0 <= inodeNum) && (inodeNum < DEVICE_INODE_NUM));
	return devInodeTbl[inodeNum].dev;
}


// ブロック数を返す
STATIC INLINE uint getBlocks(int inodeNum)
{
	ASSERT((0 <= inodeNum) && (inodeNum < DEVICE_INODE_NUM));
	return devInodeTbl[inodeNum].blocks;
}


// ブロックの始まりを返す
STATIC INLINE uint getBegBlock(int inodeNum)
{
	ASSERT((0 <= inodeNum) && (inodeNum < DEVICE_INODE_NUM));
	return devInodeTbl[inodeNum].blockBeg;
}


// ファイルモードを返す
STATIC INLINE uint getMode(int inodeNum)
{
	ASSERT((0 <= inodeNum) && (inodeNum < DEVICE_INODE_NUM));
	return devInodeTbl[inodeNum].mode;
}


// デバイス名を取得する
STATIC const char *getName(int inodeNum)
{
	ASSERT((0 <= inodeNum) && (inodeNum < DEVICE_INODE_NUM));
	return devInodeTbl[inodeNum].name;
}


// Init device filesystem.
STATIC void initDevFs()
{
	// root inodeの設定
	setRootInode();
}


//================================== PUBLIC =============================================


int major(dev_t x)
{
	if (x == NODEV){
		return NOUDEV;
	}

	return((x->si_udev >> 8) & 0xff);
}


int minor(dev_t x)
{
	if (x == NODEV){
		return NOUDEV;
	}

	return(x->si_udev & 0xffff00ff);
}


udev_t makeudev(int x, int y)
{
	return ((x << 8) | y);
}


// Regist device.
// parameters : cdevsw構造体, マイナー番号, uid, gid, ファイルモード, 登録名
// return : dev_t or NODEV
dev_t make_dev(struct cdevsw *devsw, int minor, uid_t uid, gid_t gid, int perms, const char *fmt, ...)
{
	int inodeNum, nextInodeNum;
	int i;
	dev_t dev;
	va_list ap;

	/* Name sizeの確認。  */
	if (MAX_DEVICE_NAME <= strlen(devsw->d_name)){
		return NODEV;
	}

	// 同じデバイス名がないか？
	for (nextInodeNum = -1; (nextInodeNum = getNextInode(nextInodeNum)) != ERR;){
		if (strcmp(devInodeTbl[nextInodeNum].dev->si_name, devsw->d_name) == 0){
			return NODEV;
		}
	}

	// cdevsw構造体のNULL関数の設定
	compile_devsw(devsw);
	
	// デバイスファイルシステムに登録する。
	inodeNum = setInode(
		devsw,
		devsw->begin_blk,
		devsw->last_blk - devsw->begin_blk + 1,
		0,
		getMajorNumber(),
		minor,
		perms
	);
	if (inodeNum < 0){
		return NODEV;
	}
	dev = devInodeTbl[inodeNum].dev;
	
	va_start(ap, fmt);
	i = kvprintf(fmt, NULL, dev->si_name, 32, ap);
	dev->si_name[i] = '\0';
	va_end(ap);

	return dev;
}


// Delete from regist table
// parameters : name
// return : error number
void destroy_dev(dev_t dev)
{
	int inodeNum;

	inodeNum = searchInodeNum(dev);
	if (inodeNum < 0){
		return;
	}

	unlinkInode(inodeNum);
}


// デバイスinode情報の取得。
// return : error number
int get_devstat(int inodeNum, DEV_STAT *m_devStat)
{
	if (devInodeTbl[inodeNum].refCount == 0){
		return -ENODEV;
	}

	m_devStat->prt_type = devInodeTbl[inodeNum].partType;
	m_devStat->major = devInodeTbl[inodeNum].major;
	m_devStat->prt_num = devInodeTbl[inodeNum].partNum;
	m_devStat->sect_size = devInodeTbl[inodeNum].dev->si_devsw->sector_size;
	m_devStat->all_sect = devInodeTbl[inodeNum].blocks;
	m_devStat->name = devInodeTbl[inodeNum].dev->si_name;

	return NOERR;
}


// ブロックデバイスパーティションをデバイスファイルシステムへ登録
// parmeters : デバイス名（番号無し）
int registBlockPartition(const char *name)
{
	int inodeNum;
	dev_t dev;
	PARTITON_TBL partTbl;
	int partNum;
	
	/* inodeを取得 */
	inodeNum = lookupDevInode(name);
	if (inodeNum < 0){
		return inodeNum;
	}
	
	/* struct cdevsw構造体を取得 */
	dev = getDevInfo(inodeNum);
	if (dev == NULL){
		return -ENODEV;
	}
	
	/* 基本領域の検索 */
	for (partNum = 1; partNum <= PARTITION_TBL_MAX; ++partNum){
		if (readPartition(inodeNum, partNum, &partTbl) == NOERR){
			setInode(
				dev->si_devsw,
				partTbl.lbaBeg,
				partTbl.size,
				partTbl.type,
				devInodeTbl[inodeNum].major,
				partNum,
				S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP
			);
		}
	}

	/* 拡張領域の検索 */
	for (partNum = PARTITION_TBL_MAX + 1;; ++partNum){
		int rest;

		switch (rest = readPartition(inodeNum, partNum, &partTbl)){
			case NOERR:
				setInode(
					dev->si_devsw,
					partTbl.lbaBeg,
					partTbl.size,
					partTbl.type,
					devInodeTbl[inodeNum].major,
					partNum,
					S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP
				);
				break;
			case -ENODEV:
				continue;
			case -ENOENT:
				goto END;
			default:
				printk("%s registBlockPartition() unkown return value!:%d", __FILE__, rest);
		}
	}
END:
	return NOERR;
}


int noopen(dev_t dev, int flags, int fmt, struct proc *p)
{
	return (-ENODEV);
}

int noclose(dev_t dev, int flags, int fmt, struct proc *p)
{
	return (-ENODEV);
}

int noread(dev_t dev, struct uio *uio, int ioflag)
{
	return (-ENODEV);
}

int nowrite(dev_t dev, struct uio *uio, int ioflag)
{
	return (-ENODEV);
}

int noioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
{
	return (-ENODEV);
}

int nokqfilter(dev_t dev, struct knote *kn)
{
	return (-ENODEV);
}

int nommap(dev_t dev, vm_offset_t offset, int nprot)
{
	/* Don't return ENODEV.  That would allow mapping address ENODEV! */
	return (-1);
}

int nodump(dev_t dev)
{
	return (-ENODEV);
}

// nopoll
int seltrue(dev_t dev, int events, struct proc *p)
{
	return (events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM));
}


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


//================================== PUBLIC =============================================


// return : transfer sectors or error number
int read_direct(int inodeNum, void *buf, size_t sectors, size_t begin)
{
	/* 論理ディスク制限のチェック。 */
	if (getBlocks(inodeNum) < begin){
		return -1;
	}
	if (getBlocks(inodeNum) < begin + sectors){
		sectors = getBlocks(inodeNum) - begin;
	}

	return readDev(getDevInfo(inodeNum), buf, sectors, begin + getBegBlock(inodeNum), 0);
}


// return : transfer sectors or error number
int write_direct(int inodeNum, void *buf, size_t sectors, size_t begin)
{
	/* 論理ディスク制限のチェック。 */
	if (getBlocks(inodeNum) < begin){
		return -1;
	}
	if (getBlocks(inodeNum) < begin + sectors){
		sectors = getBlocks(inodeNum) - begin;
	}

	return writeDev(getDevInfo(inodeNum), buf, sectors, begin + getBegBlock(inodeNum), 0);
}


/************************************************************************************************
 *
 * デバイスキャッシュ
 *
 * 注意！
 *  デバイスはセクターサイズ512バイトを前提にしているので、その他のセクターサイズの
 *  デバイスを対象にするには、修正が必要。
 *
 ************************************************************************************************/


enum{
	CACHE_NUM = 8,				/* IOキャッシュブロック数 */
	CACHE_BLOCK_SIZE = 0x1000,	/* IOキャッシュブロックサイズ */
	HASH_NUM = CACHE_NUM,		/* Number of cache hash table */
	CACHE_UNIT_SIZE = 512,		/* キャッシュブロックのビットマップ単位バイトサイズ。 */
};

typedef struct CACHE_TABLE{
	struct CACHE_TABLE			*hashNext;	// Hash next
	struct CACHE_TABLE			*hashPrev;
	struct CACHE_TABLE			*cacheNext;	// Cache chain next
	struct CACHE_TABLE			*cachePrev;
	struct CACHE_TABLE volatile	*writeNext;	// 遅延書き込みリスト
	struct CACHE_TABLE		 	*writePrev;
	char						*cache;		// Cache address
	uint						block;		// Device block index
	uchar						inodeNum;	// Device inode number
	uchar volatile				bitmap;		// データ存在ビットマップ
	uchar volatile				writing;	// 遅延書込待ちフラグ
	uchar volatile				busy;		// 処理中フラグ
	int							gate;		// スピンロックゲート
}CACHE_TABLE;


//================================== PRIVATE ============================================


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 *cacheOldest = &cache_tbl[0];	// old chache block.（nextが一番新しいブロック）
static int cacheLock;								// キャッシュリンク更新用ロックゲート
static PROC *writeTask;								// 遅延書き込み用タスク
static CACHE_TABLE delayTop;						// 遅延書き込みリストの先頭
static int delayLock;								// キャッシュリンク更新用ロックゲート
static uchar bitSects[] = {							// セクター数ごとのビットマップ値
	0,0x1,0x3,0x7,0xf,0x1f,0x3f,0x7f,0xff
};


// デバイスからキャッシュに読み込む。
// return : 0 or error number
STATIC int readDevice(CACHE_TABLE *cacheTbl, uchar bitmap)
{
	//読み込み必要だが読み込まれていないビットのみ０にする
	uchar map = ~bitmap | cacheTbl->bitmap;
	dev_t dev = getDevInfo(cacheTbl->inodeNum);
	int start, last, allLast;
	int rest;

	for (start = 0, allLast = CACHE_BLOCK_SIZE / dev->si_devsw->sector_size; start < allLast; ++start){
		if ((map & (1 << start)) == 0){
			for (last = start + 1; last < allLast; ++last){
				if (map & (1 << last)){
					break;
				}
			}
			rest = readDev(dev, cacheTbl->cache + start * CACHE_UNIT_SIZE,
				last - start, start + cacheTbl->block + getBegBlock(cacheTbl->inodeNum), 0);
			if (rest < 0){
				return rest;
			}
			start = last;
		}
	}

	cacheTbl->bitmap |= bitmap;
	
	return 0;
}


// キャッシュからデバイスに書き戻す。
// return : 0 or error number
STATIC int writeDevice(CACHE_TABLE *cacheTbl)
{
	dev_t dev = getDevInfo(cacheTbl->inodeNum);
	int start, last;
	int rest;

	if (cacheTbl->bitmap == 0xff){
		rest = writeDev(dev, cacheTbl->cache, CACHE_BLOCK_SIZE / CACHE_UNIT_SIZE, cacheTbl->block + getBegBlock(cacheTbl->inodeNum), 0);
		if (rest < 0){
			return rest;
		}
	}
	else{
		// 更新箇所をデバイスに書き込む。
		for (start = 0; start < CACHE_BLOCK_SIZE / CACHE_UNIT_SIZE; ++start){
			if (cacheTbl->bitmap & (1 << start)){
				for (last = start + 1; last < CACHE_BLOCK_SIZE / CACHE_UNIT_SIZE; ++last){
					if ((cacheTbl->bitmap & (1 << last)) == 0){
						break;
					}
				}
				rest = writeDev(
					dev, cacheTbl->cache + start * CACHE_UNIT_SIZE, last - start, cacheTbl->block + start + getBegBlock(cacheTbl->inodeNum), 0);
				if (rest < 0){
					return rest;
				}
				start = last;
			}
		}
	}

	return 0;
}


// デバイスへの遅延書き込みタスク。
STATIC void delayWriteTask()
{
	PROC **q;
	CACHE_TABLE *cacheTbl = NULL;

	// 親プロセスから切り離す
	for (q = &writeTask->parent->child; *q != writeTask; q = &(*q)->brother){;
	}
	*q = writeTask->brother;
	
	// 終了させるシグナルをすべてマスクする
	setSigAllMask();

	// ロック
	enter_spinlock(&delayLock);
	for (;;){
		// 書き込み待ちを取り出す
		if (delayTop.writeNext == &delayTop){
			if (cacheTbl != NULL){
				cacheTbl->writing = NO;
			}
			del_from_schedule(TASK_WAIT, PROC_SCHEDULE_DEL);

			// ロック開放
			exit_spinlock(&delayLock);

			wait_task();

			// ロック
			enter_spinlock(&delayLock);
		}
		else{
			cacheTbl = delayTop.writePrev;
			cacheTbl->writeNext->writePrev = cacheTbl->writePrev;
			cacheTbl->writePrev->writeNext = cacheTbl->writeNext;
			cacheTbl->writeNext = cacheTbl->writePrev = cacheTbl;

			// ロック開放
			exit_spinlock(&delayLock);

			if (writeDevice(cacheTbl) < 0){
				printk("Device error in delay write function!\n");
			}

			// ロック
			enter_spinlock(&delayLock);
			
			if (cacheTbl->writeNext == cacheTbl){
				cacheTbl->writing = NO;
			}
		}
	}
}


//《注意》この関数は"cacheLock"で競合しない関数から呼び出されること
// 遅延書き込みリストに加える。
STATIC void addWriteList(CACHE_TABLE *cacheTbl)
{
	enter_spinlock(&delayLock);
	{
		// すでにリンクに接続されている場合もあるので一旦切断処理をする
		cacheTbl->writeNext->writePrev = cacheTbl->writePrev;
		cacheTbl->writePrev->writeNext = cacheTbl->writeNext;
		
		cacheTbl->writeNext = delayTop.writeNext;
		cacheTbl->writePrev = &delayTop;
		delayTop.writeNext->writePrev = cacheTbl;
		delayTop.writeNext = cacheTbl;
		cacheTbl->writing = YES;

		// 遅延書き込みタスクを起こす
		add_to_schedule(writeTask, TASK_WAIT, NULL);
	}
	exit_spinlock(&delayLock);

}


// ハッシュ値を計算する。
STATIC INLINE int calcHash(int value)
{
	return (value / 8) % HASH_NUM;
}


// キャッシュテーブルから該当デバイスブロックを探す。
// return : block addres or NULL
STATIC CACHE_TABLE *searchCache(int inodeNum, size_t begin)
{
	int hash = calcHash(begin);
	CACHE_TABLE *cacheFind;

	for (cacheFind = cache_hash[hash]; cacheFind != NULL; cacheFind = cacheFind->hashNext){
		// キャッシュにあり
		if ((cacheFind->block == begin) && (cacheFind->inodeNum == inodeNum)){
			// リンクを一番新しい位置にする
			if (cacheFind == cacheOldest){
				cacheOldest = cacheOldest->cachePrev;
			}
			else{
				cacheFind->cacheNext->cachePrev = cacheFind->cachePrev;
				cacheFind->cachePrev->cacheNext = cacheFind->cacheNext;
				cacheFind->cacheNext = cacheOldest->cacheNext;
				cacheFind->cachePrev = cacheOldest;
				cacheOldest->cacheNext->cachePrev = cacheFind;
				cacheOldest->cacheNext = cacheFind;
			}

			return cacheFind;
		}
	}

	return NULL;
}


// 新規にキャッシュブロックを割り当てる。
// return : cache table
STATIC CACHE_TABLE *getNewCache(int inodeNum, size_t begin)
{
	int hash = calcHash(begin);
	CACHE_TABLE *newCache;

	// 一番古いキャッシュを横取り
	newCache = cacheOldest;
	cacheOldest = cacheOldest->cachePrev;

	// ハッシュリンクから削除
	newCache->hashPrev->hashNext = newCache->hashNext;
	if (newCache->hashNext != NULL){
		newCache->hashNext->hashPrev = newCache->hashPrev;
	}

	// 新しいハッシュリンクに加える
	newCache->hashNext = cache_hash[hash];
	newCache->hashPrev = (CACHE_TABLE*)&cache_hash[hash];
	if (cache_hash[hash] != NULL){
		cache_hash[hash]->hashPrev = newCache;
	}
	cache_hash[hash] = newCache;

	return newCache;
}


// 遅延書き込みタスクの起動。
STATIC void startDelayWriteTask()
{
	switch (sys_fork()){
		case -1:
			printk("Faild startDelayWriteTask\n");
			break;
		case 0:
			writeTask = get_current_task();
			delayWriteTask();
			// 戻ってこない
	}
}


//================================== PROTECTED ==========================================


// 書込みありキャッシュブロックをすべて遅延書込みに送る。
STATIC void writeBackAllCache()
{
	// 遅延書き込み待ち
	for (;;){
		enter_spinlock(&delayLock);
		if (delayTop.writeNext != &delayTop){
			exit_spinlock(&delayLock);
			wait_task();
		}
		else{
			exit_spinlock(&delayLock);
			break;
		}
	}
}


// return : 0 or Error number
STATIC int initCache()
{
	int i;

	// キャッシュテーブルの初期化
	for (i = 0; i < CACHE_NUM; ++i){
		memset(&cache_tbl[i], 0, sizeof(CACHE_TABLE));
		cache_tbl[i].cache = kmalloc(CACHE_BLOCK_SIZE);
		if (cache_tbl[i].cache == NULL){
			return -ENOMEM;
		}
		cache_tbl[i].cacheNext = &cache_tbl[i + 1];
		cache_tbl[i].cachePrev = &cache_tbl[i - 1];
		cache_tbl[i].hashPrev = &cache_tbl[i];
		cache_tbl[i].writeNext = cache_tbl[i].writePrev = &cache_tbl[i];
	}
	cache_tbl[CACHE_NUM - 1].cacheNext = &cache_tbl[0];
	cache_tbl[0].cachePrev = &cache_tbl[CACHE_NUM - 1];

	// ハッシュリンクの初期化
	for (i = 0; i < HASH_NUM; ++i){
		cache_hash[i] = NULL;
	}
	
	// 遅延書き込みリンクの初期化
	delayTop.writeNext = delayTop.writePrev = &delayTop;
	
	return 0;
}


//================================== PUBLIC =============================================


//《注意》割り込みハンドラからの呼び出し不可
// return : transfer sectors or error number
int read_cache(int inodeNum, void *buf, size_t sector_num, size_t begin)
{
	char *readBuf = (char*)buf;
	int sectSize = getDevInfo(inodeNum)->si_devsw->sector_size;	/* デバイスセクターサイズ。 */
	int cacheSects = CACHE_BLOCK_SIZE / sectSize;				/* キャッシュブロックサイズにはいるセクター数。 */
	int restSects = sector_num;									/* 残りの転送セクター数。 */
	int start, cacheStart, copySects;
	CACHE_TABLE *cacheTbl;

	ASSERT(0 <= restSects);

	/* 論理ディスク制限のチェック。 */
	if (getBlocks(inodeNum) < begin){
		return -EIO;
	}
	if (getBlocks(inodeNum) < begin + cacheSects){
		return -EIO;
	}
	start = begin % cacheSects;
	cacheStart = ROUNDDOWN(begin, cacheSects);
	copySects = (restSects <= cacheSects - start)? restSects : cacheSects - start;
	for(;;){
		enter_spinlock(&cacheLock);
		{
			// キャッシュを検索
			if ((cacheTbl = searchCache(inodeNum, cacheStart)) != NULL){
				// キャッシュ処理待ち
				while (cacheTbl->busy == YES){;
				}
			}
			else{
				cacheTbl = getNewCache(inodeNum, cacheStart);

				// キャッシュ処理待ち
				while (cacheTbl->busy == YES){;
				}
				// 遅延書き込み待ち。
				while (cacheTbl->writing == YES){;
				}

				cacheTbl->inodeNum = inodeNum;
				cacheTbl->block = cacheStart;
				cacheTbl->bitmap = 0;
			}

			// キャッシュ処理開始
			cacheTbl->busy = YES;
		}
		exit_spinlock(&cacheLock);

		// キャッシュに要求セクターがなければデバイスから読み込む
		if ((cacheTbl->bitmap & (bitSects[copySects] << start)) != (bitSects[copySects] << start)){
			if (readDevice(cacheTbl, bitSects[copySects] << start) < 0){
				// ---> キャッシュ処理終了
				cacheTbl->busy = NO;
				return -EIO;
			}
		}

		// バッファにコピーする
		memcpy(readBuf, cacheTbl->cache + start * sectSize, copySects * sectSize);

		// キャッシュ処理終了
		cacheTbl->busy = NO;

		if ((restSects -= copySects) == 0){
			break;
		}
		readBuf += copySects * sectSize;
		cacheStart += cacheSects;
		copySects = (restSects < cacheSects)? restSects : cacheSects;
		start = 0;
	}

	return sector_num;
}


/*
 * ！ 割り込みハンドラからの呼び出し不可
 * return : transfer sectors or error number
 */
int write_cache(int inodeNum, void *wbuf, size_t sector_num, size_t begin)
{
	char *writeBuf = (char*)wbuf;
	int sectSize = getDevInfo(inodeNum)->si_devsw->sector_size;	/* デバイスセクターサイズ。 */
	int cacheSects = CACHE_BLOCK_SIZE / sectSize;				/* キャッシュブロックサイズにはいるセクター数。 */
	int restSects = sector_num;									/* 残りの転送セクター数。 */
	int start, cacheStart, copySects;
	CACHE_TABLE *cacheTbl;

	ASSERT(0 <= restSects);

	/* 論理ディスク制限のチェック。 */
	if (getBlocks(inodeNum) < begin){
		return -EIO;
	}
	if (getBlocks(inodeNum) < begin + cacheSects){
		return -EIO;
	}

	start = begin % cacheSects;
	cacheStart = ROUNDDOWN(begin, cacheSects);
	copySects = (restSects <= cacheSects - start)? restSects : cacheSects - start;
	for(;;){
		enter_spinlock(&cacheLock);
		{
			// キャッシュを検索
			if ((cacheTbl = searchCache(inodeNum, cacheStart)) != NULL){
				// キャッシュ処理待ち
				while (cacheTbl->busy == YES){;
				}
			}
			else{
				cacheTbl = getNewCache(inodeNum, cacheStart);

				// キャッシュ処理待ち
				while (cacheTbl->busy == YES){;
				}
				// 遅延書き込み待ち
				while (cacheTbl->writing == YES){;
				}

				cacheTbl->inodeNum = inodeNum;
				cacheTbl->block = cacheStart;
				cacheTbl->bitmap = 0;
			}

			// キャッシュ処理開始
			cacheTbl->busy = YES;
		}
		exit_spinlock(&cacheLock);

		// キャッシュにコピーする
		memcpy(cacheTbl->cache + start * sectSize, writeBuf, copySects * sectSize);
		writeBuf += copySects * sectSize;
		cacheTbl->bitmap |= bitSects[copySects] << start;

		// 遅延書き込み
		addWriteList(cacheTbl);

		// キャッシュ処理終了
		cacheTbl->busy = NO;

		if ((restSects -= copySects) == 0){
			break;
		}
		cacheStart += cacheSects;
		copySects = (restSects < cacheSects)? restSects : cacheSects;
		start = 0;
	}

	return sector_num;
}


int initBlockCache()
{
	/* IOキャッシュを初期化する */
	initCache();

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

	return 0;
}


/************************************************************************************************
 *
 * ファイルサービス関数
 *
 ************************************************************************************************/


STATIC int lookup(const char *path, const int inodeNum, void *inodeObj, uint *o_inodeNum)
{
	int rest;
	
	rest = lookupDevInode(path);
	if (rest < 0){
		return rest;
	}
	*o_inodeNum = rest;

	return NOERR;
}


STATIC int creat(const char *path, const int inodeNum, void *inodeObj, const int mode, uint *o_inodeNum)
{
	return -EOPNOTSUPP;
}


STATIC int open(const int din, const uint inodeNum, int oflag, void **o_inodeObj, int *o_type)
{
	dev_t dev = getDevInfo(inodeNum);
	
	// デバイスをopenする
	if (inodeNum != DEVICE_ROOT_INODE){
		if (dev->si_devsw->d_open(dev, oflag, 0, NULL) < 0){
			return -EIO;
		}
	}

	*o_inodeObj = (void*)inodeNum;
	*o_type = getMode(inodeNum) & S_IFMT;

	return NOERR;
}


STATIC int close(const int din, void *inodeObj)
{
	int inodeNum = (int)inodeObj;
	dev_t dev = getDevInfo(inodeNum);

	/* デバイスをcloseする */
	if (inodeNum != DEVICE_ROOT_INODE){
		if (dev->si_devsw->d_close(dev, 0, 0, NULL) < 0){
			return -EIO;
		}
	}

	return NOERR;
}


STATIC int read(const int din, void *inodeObj, void *buf, size_t size, size_t begin)
{
	char *readBuf = buf;
	int inodeNum = (int)inodeObj;
	dev_t dev = getDevInfo(inodeNum);
	int rest;

	switch (getMode(inodeNum) & S_IFMT){
		/* キャラクターデバイス */
		case S_IFCHR:
			return readDev(dev, readBuf, size, 0, 0);
		/* ブロックデバイス */
		case S_IFBLK:{
			char *sectBuf = NULL;
			int sectSize = getDevInfo(inodeNum)->si_devsw->sector_size;	/* セクターサイズ */
			int sectBeg = begin / sectSize;								/* 転送始めのセクター番号 */
			int restSize = size;										/* 残りの転送バイト数 */
			int sects;													/* セクター数 */
			int bytes, restBytes;										/* バイト数 */

			/* 最初のセクターサイズ未満の転送。 */
			if ((bytes = begin % sectSize) != 0){
				if ((sectBuf = kmalloc(sectSize)) == NULL){
					return -ENOMEM;
				}
				rest = read_cache(inodeNum, sectBuf, 1, sectBeg);
				if (rest != 1){
					return rest;
				}
				restBytes = sectSize - bytes;
				if (size < restBytes){
					memcpy(readBuf, sectBuf + bytes, size);
					kfree(sectBuf);
					return size;
				}

				memcpy(readBuf, sectBuf + bytes, restBytes);
				readBuf += restBytes;
				sectBeg += 1;
				restSize -= restBytes;
			}

			sects = restSize / sectSize;
			rest = read_cache(inodeNum, readBuf, sects, sectBeg);
			if (rest < sects){
				kfree(sectBuf);
				return rest;
			}

			/* 残りがセクターサイズ未満の転送。 */
			if (sects * sectSize < restSize){
				bytes = sects * sectSize;
				if ((sectBuf == NULL) && ((sectBuf = kmalloc(sectSize)) == NULL)){
					return -ENOMEM;
				}
				if (read_cache(inodeNum, sectBuf, 1, sectBeg + sects) != 1){
					kfree(sectBuf);
					return size - restSize + bytes;
				}
				memcpy(readBuf + bytes, sectBuf, restSize - bytes);
			}

			kfree(sectBuf);
			
			return size;
		}
		/* root directory */
		case S_IFDIR:
			return 0;
		default:
			ASSERT(0);
			return -ENOENT;
	}
}


STATIC int write(const int din, void *inodeObj, void *buf, size_t size, size_t begin)
{
	char *writeBuf = buf;
	int inodeNum = (int)inodeObj;
	dev_t dev = getDevInfo(inodeNum);
	int rest;

	switch (getMode(inodeNum) & S_IFMT){
		/* キャラクターデバイス */
		case S_IFCHR:
			return writeDev(dev, writeBuf, size, 0, 0);
		/* ブロックデバイス */
		case S_IFBLK:{
			char *sectBuf = NULL;
			int sectSize = getDevInfo(inodeNum)->si_devsw->sector_size;	/* セクターサイズ */
			int sectBeg = begin / sectSize;								/* 転送始めのセクター番号 */
			int restSize = size;										/* 残りの転送バイト数 */
			int sects;													/* セクター数 */
			int bytes, restBytes;										/* バイト数 */

			/* 最初のセクターサイズ未満の転送。 */
			if ((bytes = begin % sectSize) != 0){
				if ((sectBuf = kmalloc(sectSize)) == NULL){
					return -ENOMEM;
				}
				rest = read_cache(inodeNum, sectBuf, 1, sectBeg);
				if (rest != 1){
					return rest;
				}
				if (size < (restBytes = sectSize - bytes)){
					memcpy(sectBuf + bytes, writeBuf, size);
					if (write_cache(inodeNum, sectBuf, 1, sectBeg) != 1){
						return 0;
					}
					kfree(sectBuf);
					return size;
				}
				memcpy(sectBuf + bytes, writeBuf, restBytes);
				if (write_cache(inodeNum, sectBuf, 1, sectBeg) != 1){
					return 0;
				}
				writeBuf += restBytes;
				sectBeg += 1;
				restSize -= restBytes;
			}

			sects = restSize / sectSize;
			rest = write_cache(inodeNum, writeBuf, sects, sectBeg);
			if (rest < sects){
				kfree(sectBuf);
				return rest;
			}

			/* 残りがセクターサイズ未満の転送。 */
			if (sects * sectSize < restSize){
				bytes = sects * sectSize;
				if ((sectBuf == NULL) && ((sectBuf = kmalloc(sectSize)) == NULL)){
					return -ENOMEM;
				}
				if (read_cache(inodeNum, sectBuf, 1, sectBeg + sects) != 1){
					kfree(sectBuf);
					return size - restSize + bytes;
				}
				memcpy(sectBuf, writeBuf + bytes, restSize - bytes);
				rest = write_cache(inodeNum, sectBuf, 1, sectBeg + sects);
				if (rest != 1){
					kfree(sectBuf);
					return rest;
				}
			}

			kfree(sectBuf);

			return size;
		}
		/* root directory */
		case S_IFDIR:
			return 0;
		default:
			ASSERT(0);
			return -ENOENT;
	}
}


STATIC int ioctl(const int din, void *inodeObj, int cmd, caddr_t param, int fflag)
{
	int inodeNum = (int)inodeObj;
	dev_t dev = getDevInfo(inodeNum);

	if (inodeNum == DEVICE_ROOT_INODE){
		return -ENOENT;
	}

	return dev->si_devsw->d_ioctl(dev, cmd, param, fflag, NULL);
}


STATIC int poll(void *inodeObj, int events)
{
	int inodeNum = (int)inodeObj;
	dev_t dev = getDevInfo(inodeNum);

	if (inodeNum == DEVICE_ROOT_INODE){
		return -ENOENT;
	}

	return dev->si_devsw->d_poll(dev, events, NULL);
}


STATIC int opendir(const char *path, const int din, void *dirInodeObj, void **o_inodeObj, uint *o_block, int *o_index)
{
	int inodeNum;

	/* パスの検索 */
	inodeNum = lookupDevInode(path);
	if (inodeNum < 0){
		return inodeNum;
	}
	
	*o_inodeObj = (void*)inodeNum;
	*o_block = 0;
	*o_index = -1;

	return NOERR;
}


STATIC int readdir(int din, void *inodeObj, uint *m_block, int *m_index, char *o_name)
{
	const char *name;
	int nodeNum = (int)inodeObj;
	int nextInode;

	if (nodeNum != DEVICE_ROOT_INODE){
		return -ENOENT;
	}

	/* 次のエントリーを検索 */
	nextInode = getNextInode(*m_index);
	if (nextInode == ERR){
		return -ENOENT;
	}
	
	*m_index = nextInode;

	/* 名前を取得 */
	name = getName(nextInode);
	strcpy(o_name, name);
	o_name[strlen(name)] = '\0';

	return 0;
}


STATIC int stat(int din, void *inodeObj, struct stat *m_stat)
{
	int inodeNum = (int)inodeObj;

	return getStat(inodeNum, m_stat);
}


STATIC int mount(const int din, void **o_rootInodeObj)
{
	return -EOPNOTSUPP;
}


STATIC int umount(const int din, void *rootInodeObj)
{
	return -EOPNOTSUPP;
}


STATIC int rename(int din, void *OldDirInodeObj, const char *oldPath, void *newDirInodeObj, const char *newPath)
{
	return -EOPNOTSUPP;
}


STATIC int mkdir(const char *path, const int din, void *dirInodeObj, int mode)
{
	return -EOPNOTSUPP;
}

STATIC int delete(const char *path, const int din, void *dirInodeObj, int type)
{
	return -EOPNOTSUPP;
}

STATIC int chattr(const int din, void *inodeObj, uint mode, uint uid, uint gid, uint atime, uint mtime)
{
	return -EOPNOTSUPP;
}

STATIC int link(const int din, void *dstInodeObj, void *srcInodeObj, const char *srcPath)
{
	return -EOPNOTSUPP;
}

STATIC int symlink(const char *filePath, const int din, void *dirInodeObj, char *linkPath)
{
	return -EOPNOTSUPP;
}

STATIC int statfs(const int din, struct statfs *m_statfs)
{
	return -EOPNOTSUPP;
}


static FS devFsFunc = {
	DEV_FS_NAME,
	mount,
	umount,
	lookup,
	creat,
	open,
	close,
	read,
	write,
	ioctl,
	poll,
	opendir,
	readdir,
	rename,
	mkdir,
	stat,
	delete,
	chattr,
	link,
	symlink,
	statfs,
};


/*
 * キャッシュからデバイスにデータを書き戻す。
 */
int sys_sync()
{
	writeBackAllCache();

	return 0;
}


/*
 * 該当ファイルディスクリプタから端末名を得る。
 */
int sys_ttyname(int fdnum, char *buf)
{
	F_DSC *fd;
	dev_t dev;
	int inodeNum;
	const char *tty;

	if ((fd = getFd(fdnum)) == NULL){
		return -EBADF;
	}

	// デバイスファイルか？
	if (fd->vent->din != 0){
		return -ENOTTY;
	}

	// struct cdevswのラストブロックナンバーが0のキャラクターデバイスかを確認する。
	inodeNum = (int)fd->vent->inodeObj;
	dev = getDevInfo(inodeNum);
	if (dev->si_devsw->sector_size != 0){
		return -ENOTTY;
	}

	tty = getName(inodeNum);
	strcpy(buf, tty);

	return 0;
}


/************************************************************************************************
 *
 * 初期化
 *
 ************************************************************************************************/


//================================== PUBLIC =============================================


int initDeviceFs()
{
	/* デバイスファイルシステムの初期化。 */
	initDevFs();
	
	return 0;
}


int mountDeviceFs()
{
	int rest;


	/* ファイルシステムに登録する。 */
	rest = regist_fs(&devFsFunc);
	if (rest < 0)
		return rest;

	/* デバイスファイルシステムをマウントする。 */
	rest = mount_dev_fs(&devFsFunc);
	if (rest < 0)
		return rest;

	return 0;
}


/****************************************************************************/
void test_device()
{
	printk("buf=%x\n",*(uint*)0x158f58);
}
/*****************************************************************************/
