/*----------------------------------------------------------------------*/
/* usblFatFsSmp.c - FatFs Driver I/F library for MatrixQuestUSB/lite.   */
/*                                                                      */
/* Copyright 2011 TEPCO UQUEST, LTD.                                    */
/* Copyright (C) 2011, ChaN, all right reserved.                        */
/*----------------------------------------------------------------------*/

/* $Id: usblFatFsSmp.c,v 1.11 2011/06/03 09:30:32 sasaki Exp $: */

/*
HISTORY
-------
1.1.0a 2011/03/25,ssa create.
*/

/*----- include -----*/

#include "integer.h"
#include "diskio.h"

#include "usblFatFsSmp.h"


/* fatFs デバイス管理構造体 */
USBL_FATFS_INFO usblFatFsInfo = {
	NULL,
	(STA_NOINIT|STA_NODISK)
};


/************************************************************************
 disk_initialize --- ディスク・ドライブを初期化します。

 引数
 	Drive
 		初期化する物理ドライブ番号を指定します。
 		このバージョンでは0のみ有効です。
 戻り値
 		この関数は戻り値としてディスク・ステータスを返します。
 解説
		ディスク・ドライブを初期化する関数です。USBの初期化はあらかじめ
		別途行う事としますので、ここではフラグ STA_NOINIT をクリアします。
*/

DSTATUS disk_initialize (
  BYTE Drive      /* 物理ドライブ番号 */
) {

	if (Drive >= USBL_FF_MAX_DEV) {
		printf("ERROR disk_initialize unknown drive.(Drive=%d)\n",(int)Drive);
		return( (STA_NOINIT|STA_NODISK) ); /* not support */
	}

    usblFatFsInfo.diskStatus &= ~(STA_NOINIT); /* STA_NOINITをクリア */

	return (usblFatFsInfo.diskStatus);		/* Return result */
}


/************************************************************************

 disk_status --- ディスク・ドライブの状態を取得します。

 引数
 	Drive
 		ステータスを取得する物理ドライブ番号を指定します。
 		このバージョンでは0のみ有効です。
 
 戻り値
	物理ドライブの状態が次のフラグの組み合わせの値で返されます。

	STA_NOINIT
		ドライブが初期化されていないことを示すフラグ。
		システム・リセットやメディアの取り外し等でセットされ、
		disk_initialize() の正常終了でクリア、失敗でセットされます。

	STA_NODISK
		メディアがセットされていないことを示すフラグ。
		このバージョンではUSBメモリの有無を示します。
		USBカードリーダなどでメディアが無い場合でもこのフラグはクリア
		されますので、使用できません。
		
	STA_PROTECTED
		メディアがライト・プロテクトされていることを示すフラグ。
		常にクリアされています。

*/

DSTATUS disk_status (
  BYTE Drive           /* 物理ドライブ番号 */
) {

	if (Drive >= USBL_FF_MAX_DEV) {
		printf("ERROR disk_initialize unknown drive.(Drive=%d)\n",(int)Drive);
		return( (STA_NOINIT|STA_NODISK) ); /* not support */
	}

	return (usblFatFsInfo.diskStatus);		/* Return result */
}

/************************************************************************
 disk_read - ディスクからセクタを読み出します。

 引数
	Drive
		物理ドライブ番号を指定します。
		このバージョンでは0のみ有効です。
	Buffer
		ディスクから読み出したデータを格納するバイト配列で、読み出される
		バイト数分のサイズが必要です。指定されるアドレスはワード・アライ
		メントされているとは限りません。
	SectorNumber
		読み出しを開始するセクタ番号。LBAで指定します。
	SectorCount
		読み出すセクタ数。 1〜128の範囲で指定します。一般的に、複数セクタ
		の転送要求はデバイスに対して可能な限りマルチ・セクタ転送しなければ
		なりません。
		複数のシングル・セクタ転送に分解した場合、読み出し速度が低下します。
 戻り値
	RES_OK (0)
		正常終了。
	RES_ERROR
		読み込み中にエラーが発生した。
	RES_PARERR
		パラメータが不正。
	RES_NOTRDY
		ドライブが動作可能状態ではないまたはMSCデバイスが接続されていない。

 解説
	ディスクからセクタを読み出す関数です。若干の型変換が必要なことを除けば
	そのままMSCの関数に渡すことができます。

*/
DRESULT disk_read (
  BYTE Drive,          /* 物理ドライブ番号 */
  BYTE* Buffer,        /* 読み出しバッファへのポインタ */
  DWORD SectorNumber,  /* 読み出し開始セクタ番号 */
  BYTE SectorCount     /* 読み出しセクタ数 */
) {
	INT ret;

	if ( Drive >= USBL_FF_MAX_DEV ) {
		printf("ERROR disk_initialize unknown drive.(Drive=%d)\n",(int)Drive);
		return( RES_PARERR ); /* not support */
	}

	if ( (usblFatFsInfo.diskStatus & (STA_NOINIT|STA_NODISK)) != 0 ){
		return( RES_NOTRDY ); /* not ready */
	}

	ret = usblBlkRd(usblFatFsInfo.pUsblBlk,(UINT)SectorNumber,(UINT)SectorCount,Buffer);

	if (ret == K_ERROR){
		printf("ERROR usblBlkRd() failed.(ret=%d)\n",(int)ret);
		return( RES_ERROR );
	}
	return RES_OK;	/* Return result */
}

/************************************************************************
 DRESULT disk_write - ディスクにデータを書き込みます。

 引数
	Drive
		物理ドライブ番号を指定します。
		このバージョンでは0のみ有効です。
	Buffer
		ディスクに書き込むバイト配列を指定します。
	SectorNumber
		書き込みを開始するセクタ番号。LBAで指定します。
	SectorCount
		書き込むセクタ数。 1〜128で指定します。

 戻り値
	RES_OK (0)
		正常終了。
	RES_ERROR
		書き込み中にエラーが発生し、その回復にも失敗した。
	RES_PARERR
		パラメータが不正。
	RES_NOTRDY
		ドライブが動作可能状態ではないまたはMSCデバイスが接続されていない。
 解説
	ディスクにセクタを書き込む関数です。若干の型変換が必要なことを除けば
	そのままMSCの関数に渡すことができます。

*/
DRESULT disk_write (
  BYTE Drive,          /* 物理ドライブ番号 */
  const BYTE* Buffer,  /* 書き込むデータへのポインタ */
  DWORD SectorNumber,  /* 書き込み開始セクタ番号 */
  BYTE SectorCount     /* 書き込みセクタ数 */
) {
	INT ret;

	if ( Drive >= USBL_FF_MAX_DEV ) {
		printf("ERROR disk_initialize unknown drive.(Drive=%d)\n",(int)Drive);
		return( RES_PARERR ); /* not support */
	}

	if ( (usblFatFsInfo.diskStatus & (STA_NOINIT|STA_NODISK)) != 0 ){
		return( RES_NOTRDY ); /* not ready */
	}

	ret = usblBlkWrt(usblFatFsInfo.pUsblBlk,(UINT)SectorNumber,(UINT)SectorCount,Buffer);

	if (ret == K_ERROR){
		printf("ERROR usblBlkRd() failed.(ret=%d)\n",(int)ret);
		return( RES_ERROR );
	}
	return RES_OK;	/* Return result */
}

/************************************************************************
 DRESULT disk_ioctl - セクタの読み書き以外のディスク・ドライブ自体に対する
                      様々な制御をします。

 引数
	Drive
		物理ドライブ番号を指定します。
		このバージョンでは0のみ有効です。
	Command
		制御コマンド・コードを指定します。
	Buffer
		制御コマンドに依存したパラメータを授受するバッファを指すポインタを
		指定します。データの授受のないコマンドの場合は、NULLを指定します。
 戻り値
	RES_OK (0)
		正常終了。
	RES_ERROR
		何らかのエラーが発生した。
	RES_PARERR
		コマンドが不正。
	RES_NOTRDY
		ドライブが動作可能状態ではない、または初期化されていない。
 解説
	次の汎用コマンドが使用できます。
		CTRL_SYNC 
			常にRES_OKを返します。特に処理は行いません。
		GET_SECTOR_COUNT
		GET_BLOCK_SIZE
			それぞれの情報はMSCのクラスドライバで取得して保持しています。
			この情報が必要となるのは(_USE_MKFS == 1)の場合のみですから、
			今回は非対応としています。
		GET_SECTOR_SIZE
			Buffer内に常に512を入れてRES_OKを返します。
		CTRL_ERASE_SECTOR フラッシュ・メモリの一部の領域を消去します。
			USBホストドライバは無関係ですので、常にOを返します。
*/
DRESULT disk_ioctl (
  BYTE Drive,      /* 物理ドライブ番号 */
  BYTE Command,    /* 制御コマンド */
  void* Buffer     /* データ受け渡しバッファ */
) {
	if ( Drive >= USBL_FF_MAX_DEV ) {
		printf("ERROR disk_initialize unknown drive.(Drive=%d)\n",(int)Drive);
		return( RES_PARERR ); /* not support */
	}

	printf("#### disk_ioctl Command =0x%x \n",Command);

	if ( (usblFatFsInfo.diskStatus & (STA_NOINIT|STA_NODISK)) != 0 ){
		printf("ERROR Drive not ready.(Drive=%d)\n",(int)Drive);
		return( RES_NOTRDY ); /* not support */
	}

    switch ( Command ) {
	    case GET_SECTOR_SIZE:
			if ( Buffer == NULL ) {
				printf("ERROR Buffer is NULL.(%d)\n",(int)Buffer);
				return( RES_ERROR ); /* not support */
			}
    		*(INT *)Buffer = FIXED_SECTOR_SIZE;
			break;

	    case CTRL_ERASE_SECTOR:
	    case CTRL_SYNC:
			break;

	    case GET_SECTOR_COUNT:
	    case GET_BLOCK_SIZE:
	    default:
			printf("Unsupported Command (0x%x) \n",Command);
			return RES_PARERR;

    }
	return RES_OK;	/* Return result */

}


/********************************************************************************
 INT usblFfInsert - MSCのUSBデバイスを接続した時に実行するコールバック関数です。
  引数
	pDevice
		USBメモリの管理情報を格納したポインタがUSBドライバによって渡されます。
 戻り値
 	K_OK(0) 	接続処理成功
 	K_ERROR(-1) 接続処理失敗
 解説
	pDeviceを引数としてMSCクラスの接続処理(usblBlkDevCreate)を実行します。
	結果をusblFatFsInfo.pUsblBlkに保存します。
	usblFatFsInfo.diskStatusのNODISKのフラグを落とします。
*/
INT usblFfInsert( void *pDevice )
{
	usblFatFsInfo.pUsblBlk = usblBlkDevCreate( pDevice,0,0,0);
	if (usblFatFsInfo.pUsblBlk == NULL ) {
		printf("create blkDev error\n");
		return K_ERROR;
	}
	usblFatFsInfo.diskStatus &= ~(STA_NODISK); /* clear STA_DISK flag */
	printf("Insert\n");
	
	return K_OK;
}

/********************************************************************************
 INT usblFfRemove - MSCのUSBデバイスを切断した時に実行するコールバック関数です。
  引数
	pDevice
		USBメモリの管理情報を格納したポインタがUSBドライバによって渡されます。
		今回は特に使用しません。
 戻り値
 	K_OK(0) 	切断処理成功
 	K_ERROR(-1) 切断処理失敗
 解説
	usblFatFsInfo.pUsblBlkを引数として切断処理(usblBlkDevDelete)を実行します。
	複数デバイスに対応する場合はpDeviceから該当するデバイスを特定する必要があります。
	usblFatFsInfo.diskStatusのNODISKのフラグを立てます。
*/
INT usblFfRemove( void *pDevice )
{
	INT rc;

	printf("Remove\n");
	if ( usblFatFsInfo.pUsblBlk != NULL ) {
		rc = usblBlkDevDelete( usblFatFsInfo.pUsblBlk );
		if ( rc == K_ERROR ) {
			printf("delete blkDev error\n");
			return K_ERROR;
		}
		usblFatFsInfo.pUsblBlk = NULL;
		usblFatFsInfo.diskStatus |= (STA_NODISK); /* set STA_DISK flag */
	}
	return K_OK;
}

